2.11BSD/sys/pdpif/if_de.c

Compare this file to the similar file:
Show the results in this format:

/*
 * SCCSID: @(#)if_de.c	1.1	(2.11BSD GTE)	12/31/93
 *	2.11BSD - Remove dereset since 1) it was never called, and 2)
 *		  wouldn't work if it were called. Also uballoc and
 *		  ubmalloc calling convention changed. - sms
 */
#include "de.h"
#if NDE > 0
/*
 * DEC DEUNA interface
 *
 *	Lou Salkind
 *	New York University
 *
 * TODO:
 *	timeout routine (get statistics)
 */

#include "param.h"
#include "../machine/seg.h"
#include "systm.h"
#include "mbuf.h"
#include "domain.h"
#include "protosw.h"
#include "ioctl.h"
#include "errno.h"
#include "time.h"

/*
 * Define DE_DO_BCTRS to get the DEUNA/DELUA to add code to allow 
 * ioctl() for clearing/getting I/O stats from the board.
#define DE_DO_BCTRS
 */

/*
 * Define DE_DO_PHYSADDR to get the DEUNA/DELUA to add code to allow 
 * ioctl() for setting/getting physical hardware address.
#define DE_DO_PHYSADDR
 */

/*
 * Define DE_DO_MULTI to get the DEUNA/DELUA to handle multi-cast addresses
#define DE_DO_MULTI
 */

/*
 * Define DE_INT_LOOPBACK to get the DEUNA/DELUA internal loopback code turned on
#define DE_INT_LOOPBACK
 */

/*
 * Define DE_DEBUG to get the DEUNA/DELUA debug code turned on
#define DE_DEBUG
 */

#include "../pdpif/if_de.h"

#ifndef YES
#define YES	1
#else		/* YES */
#undef	YES
#define	YES	1
#endif		/* YES */
#ifndef NO
#define NO	0
#else		/* NO */
#undef	NO
#define	NO	0
#endif		/* NO */

#define	MAPBUFDESC	(((btoc(ETHERMTU+sizeof(struct ether_header)) - 1) << 8 ) | RW)
#define	CRC_LEN		4	/* Length of CRC added to packet by board */

struct de_softc de_softc[NDE];  

#ifdef	DE_DEBUG
/*
 * Setting dedebug turns on the level of debugging, iff DE_DEBUG
 * was defined at compile time.  The current levels are:
 * 1 - added informative messages and error messages
 * 2 - more added messages
 * 3 - still more messages, like one for every loop packet seen, etc.
 */
#define	DE_DEBUG_LEVEL 1
int dedebug = DE_DEBUG_LEVEL;
#endif		/* DE_DEBUG */

#ifdef DE_DO_MULTI
u_char unused_multi[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
#endif		/* DE_DO_MULTI */
#ifdef not_on_pdp
extern struct protosw *iftype_to_proto(), *iffamily_to_proto();
#endif		/* not_on_pdp */

int	deprobe(), deattach(), deintr(), deinit(), deoutput(), deioctl();

struct	mbuf *deget();

u_short destd[] = { 0174510 };

struct	uba_device *deinfo[NDE];

struct	uba_driver dedriver = 
	{ deprobe, 0, deattach, 0, destd, "de", deinfo };

#ifdef	DE_INT_LOOPBACK
#define	DELUA_LOOP_LEN	(32 - sizeof(struct ether_header))
#endif	/* DE_INT_LOOPBACK */

#ifdef	DE_DO_BCTRS
extern	struct timeval time;
struct	timeval atv;
#endif		/* DE_DO_BCTRS */

deprobe(reg)
	caddr_t reg;
{
#ifdef not_on_pdp
	register int br, cvec;		/* r11, r10 value-result */
	register struct dedevice *addr = (struct dedevice *)reg;
	register i;

#ifdef lint
	br = 0; cvec = br; br = cvec;
	i = 0; derint(i); deintr(i);
#endif		/* lint */

       /*
        * Make sure self-test is finished 
        * Self-test on a DELUA can take 15 seconds.
        */
        for (i = 0; i < 160 &&
             (addr->pcsr0 & PCSR0_FATI) == 0 &&
             (addr->pcsr1 & PCSR1_STMASK) == STAT_RESET;
             ++i)
                DELAY(100000);
        if ((addr->pcsr0 & PCSR0_FATI) != 0 ||
            (addr->pcsr1 & PCSR1_STMASK) != STAT_READY)
                return(0);

        addr->pcsr0 = 0;


	addr->pcsr0 = PCSR0_RSET;

	/*
	 * just in case this is not a deuna or delua 
	 * dont wait for more than 30 secs
         */
        for (i = 0; i < 300 && (addr->pcsr0 & PCSR0_INTR) == 0; i++)
                DELAY(100000);
        if ((addr->pcsr0 & PCSR0_INTR) == 0) 
                return(0);

	/* make board interrupt by executing a GETPCBB command */
	addr->pcsr0 = PCSR0_INTE;
	addr->pcsr2 = 0;
	addr->pcsr3 = 0;
	addr->pcsr0 = PCSR0_INTE|CMD_GETPCBB;
	DELAY(100000);
#endif		/* not_on_pdp */
	return(1);
}

/*
 * Interface exists: make available by filling in network interface
 * record.  System will initialize the interface when it is ready
 * to accept packets.  We get the ethernet address here.
 */
deattach(ui)
struct	uba_device *ui;
{
	register struct de_softc *ds = &de_softc[ui->ui_unit];
	register struct ifnet *ifp = &ds->ds_if;
	register struct dedevice *addr = (struct dedevice *)ui->ui_addr;
	int i;
	u_short csr1;

	/*
	 * Is it a DEUNA or a DELULA? Save the device id.
	 */
	csr1 = addr->pcsr1;
	ds->ds_devid = (csr1 & PCSR1_DEVID) >> 4;

#ifdef DE_DEBUG
	if (dedebug >= 1)
		printf("de%d: Device Type: %s\n", ui->ui_unit,
			(ds->ds_devid == DEUNA) ? "DEUNA" : "DELUA");
#endif		/* DE_DEBUG */
	/*
	 * Board Status Check
	 */
	if (csr1 & 0xff80) {
		if (ds->ds_devid == DEUNA)
			printf("de%d: hardware error, pcsr1=%b\n", 
				ui->ui_unit, csr1, PCSR1_BITS);
		else
			printf("de%d: hardware error, pcsr1=%b\n", 
				ui->ui_unit, csr1, PCSR1_BITS_DELUA);
	}

	ifp->if_unit = ui->ui_unit;
	ifp->if_name = "de";
	ifp->if_mtu = ETHERMTU;
	ifp->if_flags |= IFF_BROADCAST;
#ifdef	DE_DEBUG
	ifp->if_flags |= IFF_DEBUG;
	printf("de%d: DEBUG enabled level=%d\n",ifp->if_unit,dedebug);
#endif		/* DE_DEBUG */

#ifdef DE_DO_MULTI
	/*
	 * Fill the multicast address table with unused entries (broadcast
	 * address) so that we can always give the full table to the device
	 * and we don't have to worry about gaps.
	 */
	for (i=0; i < NMULTI; i++)
		bcopy(unused_multi,(u_char *)&ds->ds_multicast[i],MULTISIZE);

#endif		/* DE_DO_MULTI */
	/*
	 * Reset the board and map the pcbb buffer onto the Unibus.
	 */
	addr->pcsr0 = PCSR0_RSET;
	(void) dewait(ui, "board reset", YES, NO);

	ds->ds_ubaddr = uballoc(INCORE_BASE(ds), INCORE_SIZE);
	addr->pcsr2 = ds->ds_ubaddr & 0xffff;
	addr->pcsr3 = (ds->ds_ubaddr >> 16) & 0x3;
	addr->pclow = CMD_GETPCBB;
	(void) dewait(ui, "get pcbb", YES, NO);

	ds->ds_pcbb.pcbb0 = FC_RDPHYAD;
	addr->pclow = CMD_GETCMD;
	(void) dewait(ui, "read addr", YES, NO);

	bcopy((caddr_t)&ds->ds_pcbb.pcbb2,(caddr_t)ds->ds_addr,
	    sizeof (ds->ds_addr));
#ifdef	DE_DEBUG
	if (dedebug >= 1)
		printf("de%d: hardware address %s\n",
			ui->ui_unit, ether_sprintf(ds->ds_addr));
#endif		/* DE_DEBUG */
	ifp->if_init = deinit;
	ifp->if_output = deoutput;
	ifp->if_ioctl = deioctl;
	ifp->if_reset = 0;
	ds->ds_deuba.difu_flags = UBA_CANTWAIT;

	if_attach(ifp);
}

/*
 * Initialization of interface; clear recorded pending
 * operations, and reinitialize UNIBUS usage.
 */
deinit(unit)
	int unit;
{
	register struct de_softc *ds = &de_softc[unit];
	register struct uba_device *ui = deinfo[unit];
	register struct dedevice *addr;
	register struct ifrw *ifrw;
	struct ifnet *ifp = &ds->ds_if;
	int s;
	struct de_ring *rp;
	ubadr_t incaddr;

	/* not yet, if address still unknown */
	/* DECnet must set this somewhere to make device happy */
	if (ifp->if_addrlist == (struct ifaddr *)0)
		return;

	if (ds->ds_flags & DSF_RUNNING)
		return;

	if ((ifp->if_flags & IFF_RUNNING) == 0) {	/* First time */
		/*
		 * Only allocate the resources ONCE.
		 * ~IFF_RUNNING && ~DSF_RUNNING
		 */
		if (de_ubainit(&ds->ds_deuba, ui->ui_ubanum, 
		    sizeof (struct ether_header), 
		    (int) btoc(ETHERMTU)) == 0) { 
			printf("de%d: can't initialize\n", unit);
			ds->ds_if.if_flags &= ~IFF_UP;
			return;
		}
	}
	addr = (struct dedevice *)ui->ui_addr;

	/*
	 * Set up the PCBB - this is done in deattach() also, but
	 * has to be redone here in case the board is reset (PCSR0_RSET)
	 * and this routine is called.	Note that ds->ds_ubaddr is set
	 * in deattach() and all we do here is tell the
	 * DEUNA/DELUA where it can find its PCBB.
	 */
	addr->pcsr2 = ds->ds_ubaddr & 0xffff;
	addr->pcsr3 = (ds->ds_ubaddr >> 16) & 0x3;
	addr->pclow = CMD_GETPCBB;
	(void) dewait(ui, "get pcbb", YES, NO);

	/*
	 * Set the transmit and receive ring header addresses
	 */
	incaddr = ds->ds_ubaddr + UDBBUF_OFFSET;
	ds->ds_pcbb.pcbb0 = FC_WTRING;
	ds->ds_pcbb.pcbb2 = incaddr & 0xffff;
	ds->ds_pcbb.pcbb4 = (incaddr >> 16) & 0x3;

	incaddr = ds->ds_ubaddr + XRENT_OFFSET;
	ds->ds_udbbuf.b_tdrbl = incaddr & 0xffff;
	ds->ds_udbbuf.b_tdrbh = (incaddr >> 16) & 0x3;
	ds->ds_udbbuf.b_telen = sizeof (struct de_ring) / sizeof (short);
	ds->ds_udbbuf.b_trlen = NXMT;
	incaddr = ds->ds_ubaddr + RRENT_OFFSET;
	ds->ds_udbbuf.b_rdrbl = incaddr & 0xffff;
	ds->ds_udbbuf.b_rdrbh = (incaddr >> 16) & 0x3;
	ds->ds_udbbuf.b_relen = sizeof (struct de_ring) / sizeof (short);
	ds->ds_udbbuf.b_rrlen = NRCV;

	addr->pclow = CMD_GETCMD;
	(void) dewait(ui, "wtring", NO, NO);

	/*
	 * Initialize the board's mode
	 */
	ds->ds_pcbb.pcbb0 = FC_WTMODE;
	/*
	 * Let hardware do padding & set MTCH bit on broadcast
 	 */
	ds->ds_pcbb.pcbb2 = MOD_TPAD|MOD_HDX;
	addr->pclow = CMD_GETCMD;
	(void) dewait(ui, "wtmode", NO, NO);

	/*
	 * Set up the receive and transmit ring entries
	 */
	ifrw = &ds->ds_deuba.difu_w[0];
	for (rp = &ds->ds_xrent[0]; rp < &ds->ds_xrent[NXMT]; rp++) {
		rp->r_segbl = ifrw->ifrw_info & 0xffff;
		rp->r_segbh = (ifrw->ifrw_info >> 16) & 0x3;
		rp->r_flags = 0;
		ifrw++;
	}
	ifrw = &ds->ds_deuba.difu_r[0];
	for (rp = &ds->ds_rrent[0]; rp < &ds->ds_rrent[NRCV]; rp++) {
		rp->r_slen = sizeof (struct de_buf);
		rp->r_segbl = ifrw->ifrw_info & 0xffff;
		rp->r_segbh = (ifrw->ifrw_info >> 16) & 0x3;
		rp->r_flags = RFLG_OWN;		/* hang receive */
		ifrw++;
	}

	/*
	 * Start up the board (rah rah)
	 */
	s = splimp();
	ds->ds_nxmit = ds->ds_rindex = ds->ds_xindex = ds->ds_xfree = 0;
	ds->ds_if.if_flags |= IFF_RUNNING;
	destart(unit);				/* queue output packets */
	addr->pclow = PCSR0_INTE;		/* avoid interlock */
	ds->ds_flags |= DSF_RUNNING;
#ifdef	NS
	if (ds->ds_flags & DSF_SETADDR)
		de_setaddr(ds->ds_addr, unit);
#endif		/* NS */
	addr->pclow = CMD_START | PCSR0_INTE;
	splx(s);
#ifdef	DE_DO_BCTRS
	cpfromkern(&time, &atv, sizeof(struct timeval));
	ds->ds_ztime = atv.tv_sec;
#endif		/* DE_DO_BCTRS */
}

/*
 * Setup output on interface.
 * Get another datagram to send off of the interface queue,
 * and map it to the interface before starting the output.
 */
static
destart(unit)
	int unit;
{
        int len;
	register struct uba_device *ui = deinfo[unit];
	struct dedevice *addr = (struct dedevice *)ui->ui_addr;
	register struct de_softc *ds = &de_softc[unit];
	register struct de_ring *rp;
	struct mbuf *m;
	register int nxmit;

	if (ds->ds_flags & DSF_LOCK)
		return;

	for (nxmit = ds->ds_nxmit; nxmit < NXMT; nxmit++) {
		IF_DEQUEUE(&ds->ds_if.if_snd, m);
		if (m == 0)
			break;
		rp = &ds->ds_xrent[ds->ds_xfree];
		if (rp->r_flags & XFLG_OWN)
			panic("deuna xmit in progress");
		len = deput(&ds->ds_deuba, ds->ds_xfree, m);
		rp->r_slen = len;
		rp->r_tdrerr = 0;
		rp->r_flags = XFLG_STP|XFLG_ENP|XFLG_OWN;

		ds->ds_xfree++;
		if (ds->ds_xfree == NXMT)
			ds->ds_xfree = 0;
	}
	if (ds->ds_nxmit != nxmit) {
		ds->ds_nxmit = nxmit;
		if (ds->ds_flags & DSF_RUNNING)
			addr->pclow = PCSR0_INTE|CMD_PDMD;
	} else if (ds->ds_nxmit == NXMT) {
		/*
		 * poke device if we have something to send and 
		 * transmit ring is full. 
		 */
#ifdef	DE_DEBUG
		if (dedebug >= 1) { 
			rp = &ds->ds_xrent[0];
			printf("de%d: did not transmit: %d, %d, %d, flag0=%x, flag1=%x\n",
				unit, ds->ds_xindex, ds->ds_nxmit, ds->ds_xfree,
				rp->r_flags, (rp+1)->r_flags);
		}
#endif		/* DE_DEBUG */
		if (ds->ds_flags & DSF_RUNNING)
			addr->pclow = PCSR0_INTE|CMD_PDMD;
	}
}

/*
 * Command done interrupt.
 */
deintr(unit)
	int unit;
{
	struct uba_device *ui = deinfo[unit];
	register struct dedevice *addr = (struct dedevice *)ui->ui_addr;
	register struct de_softc *ds = &de_softc[unit];
	register struct de_ring *rp;
	register struct ifrw *ifrw;
	short csr0;

	/* save flags right away - clear out interrupt bits */
	csr0 = addr->pcsr0;
	addr->pchigh = csr0 >> 8;

	if (csr0 & PCSR0_FATI) {
		if (ds->ds_devid == DEUNA)
			printf(
		          "de%d: unsolicited state change, csr0=%b, csr1=%b\n",
			   unit, csr0, PCSR0_BITS,
			   addr->pcsr1, PCSR1_BITS);
		else
			printf(
		          "de%d: unsolicited state change, csr0=%b, csr1=%b\n",
			   unit, csr0, PCSR0_BITS,
			   addr->pcsr1, PCSR1_BITS_DELUA);
	}

	if (csr0 & PCSR0_SERI) {
		if (ds->ds_devid == DEUNA)
			printf("de%d: status error, csr0=%b, csr1=%b\n",
			   unit, csr0, PCSR0_BITS,
			   addr->pcsr1, PCSR1_BITS);
		else
			printf("de%d: status error, csr0=%b, csr1=%b\n",
			   unit, csr0, PCSR0_BITS,
			   addr->pcsr1, PCSR1_BITS_DELUA);
	}

	ds->ds_flags |= DSF_LOCK;

	/*
	 * if receive, put receive buffer on mbuf
	 * and hang the request again
	 */
	rp = &ds->ds_rrent[ds->ds_rindex];
	if ((rp->r_flags & RFLG_OWN) == 0)
		derecv(unit);

	/*
	 * Poll transmit ring and check status.
	 * Be careful about loopback requests.
	 * Then free buffer space and check for
	 * more transmit requests.
	 */
	for ( ; ds->ds_nxmit > 0; ds->ds_nxmit--) {
		rp = &ds->ds_xrent[ds->ds_xindex];
		if (rp->r_flags & XFLG_OWN)
			break;
		ds->ds_if.if_opackets++;
		ifrw = &ds->ds_deuba.difu_w[ds->ds_xindex];
		/* check for unusual conditions */
		if (rp->r_flags & (XFLG_ERRS|XFLG_MTCH|XFLG_ONE|XFLG_MORE)) {
			if (rp->r_flags & XFLG_ERRS) {
				/* output error */
				ds->ds_if.if_oerrors++;
#ifdef	DE_DEBUG
				if (dedebug >= 1)
				    printf("de%d: oerror, flags=%b tdrerr=%b (len=%d)\n",
					unit, rp->r_flags, XFLG_BITS,
					rp->r_tdrerr, XERR_BITS,
					rp->r_slen);
#endif		/* DE_DEBUG */
			} else  {
				if (rp->r_flags & XFLG_ONE) {
					/* one collision */
					ds->ds_if.if_collisions++;
				} else if (rp->r_flags & XFLG_MORE) {
					/* more than one collision */
					ds->ds_if.if_collisions += 2; /*guess*/
				}
				if ((rp->r_flags & XFLG_MTCH) &&
					!(ds->ds_if.if_flags & IFF_LOOPBACK)) {
					/* received our own packet */
					ds->ds_if.if_ipackets++;
#ifdef DE_DEBUG
					if (dedebug >= 3)
						printf("de%d: loopback packet\n",
							unit);
#endif		/* DE_DEBUG */
					deread(ds, ifrw,
					    rp->r_slen - sizeof (struct ether_header));
				}
			}
		}
		/* check if next transmit buffer also finished */
		ds->ds_xindex++;
		if (ds->ds_xindex >= NXMT)
			ds->ds_xindex = 0;
	}
	ds->ds_flags &= ~DSF_LOCK;
	destart(unit);

	if (csr0 & PCSR0_RCBI) {
		ds->ds_if.if_ierrors++;
#ifdef DE_DEBUG
		if (dedebug >= 1)
			printf("de%d: buffer unavailable\n", ui->ui_unit);
#endif		/* DE_DEBUG */
		addr->pclow = PCSR0_INTE|CMD_PDMD;
	}
}

/*
 * Ethernet interface receiver interface.
 * 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.
 */
static
derecv(unit)
	int unit;
{
	register struct de_softc *ds = &de_softc[unit];
	register struct de_ring *rp;
	register int len;
	struct ether_header *eh;

	rp = &ds->ds_rrent[ds->ds_rindex];
	while ((rp->r_flags & RFLG_OWN) == 0) {
		ds->ds_if.if_ipackets++;
		len = (rp->r_lenerr&RERR_MLEN) - sizeof (struct ether_header)
			- CRC_LEN;	/* don't forget checksum! */
                if( ! (ds->ds_if.if_flags & IFF_LOOPBACK) ) {
		/* check for errors */
		    if ((rp->r_flags & (RFLG_ERRS|RFLG_FRAM|RFLG_OFLO|RFLG_CRC)) ||
			(rp->r_flags&(RFLG_STP|RFLG_ENP)) != (RFLG_STP|RFLG_ENP) ||
			(rp->r_lenerr & (RERR_BUFL|RERR_UBTO|RERR_NCHN)) ||
		        len < ETHERMIN || len > ETHERMTU) {
		  	    ds->ds_if.if_ierrors++;
#ifdef	DE_DEBUG
			    if (dedebug >= 1)
			     printf("de%d: ierror, flags=%b lenerr=%b (len=%d)\n",
				unit, rp->r_flags, RFLG_BITS,
				rp->r_lenerr, RERR_BITS, len);
#endif		/* DE_DEBUG */
		    } else
			deread(ds, &ds->ds_deuba.difu_r[ds->ds_rindex], len);
                } else {
			int	ret;
			segm	sav5;

			saveseg5(sav5);
			mapseg5(ds->ds_deuba.difu_r[ds->ds_rindex].ifrw_click, MAPBUFDESC);
			eh = (struct ether_header *) SEG5;
                        ret = bcmp(eh->ether_dhost, ds->ds_addr, 6);
			restorseg5(sav5);
			if (ret == NULL)
                                deread(ds, &ds->ds_deuba.difu_r[ds->ds_rindex], len);
                }

		/* hang the receive buffer again */
		rp->r_lenerr = 0;
		rp->r_flags = RFLG_OWN;

		/* check next receive buffer */
		ds->ds_rindex++;
		if (ds->ds_rindex >= NRCV)
			ds->ds_rindex = 0;
		rp = &ds->ds_rrent[ds->ds_rindex];
	}
}

/*
 * Pass a packet to the higher levels.
 * We deal with the trailer protocol here.
 */
static
deread(ds, ifrw, len)
	register struct de_softc *ds;
	struct ifrw *ifrw;
	int len;
{
	struct ether_header *eh;
    	struct mbuf *m;
	struct protosw *pr;
	int off, resid, s;
	struct ifqueue *inq;
	segm	sav5;
	int	type;

	/*
	 * Deal with trailer protocol: if type is trailer
	 * get true type from first 16-bit word past data.
	 * Remember that type was trailer by setting off.
	 */
	saveseg5(sav5);
	mapseg5(ifrw->ifrw_click, MAPBUFDESC);
	eh = (struct ether_header *) SEG5;

	type = eh->ether_type = ntohs((u_short)eh->ether_type);

#define	dedataaddr(eh, off, type)	((type)(((caddr_t)((eh)+1)+(off))))
	if (type >= ETHERTYPE_TRAIL &&
		type < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) {
		off = (type - ETHERTYPE_TRAIL) * 512;
		if (off >= ETHERMTU) {
			restorseg5(sav5);
			return;		/* sanity */
		}
		type = ntohs(*dedataaddr(eh, off, u_short *));
		resid = ntohs(*(dedataaddr(eh, off+2, u_short *)));

		if (off + resid > len) {
			restorseg5(sav5);
			return;		/* sanity */
		}
		len = off + resid;
	} else
		off = 0;

	if (len == 0) {
		restorseg5(sav5);
		return;
	}

	restorseg5(sav5);

	/*
	 * Pull packet off interface.  Off is nonzero if packet
	 * has trailing header; deget 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 = deget(&ds->ds_deuba, ifrw, len, off, &ds->ds_if);

	if (m == 0)
		return;
	if (off) {
		struct ifnet *ifp;

		ifp = *(mtod(m, struct ifnet **));
		m->m_off += 2 * sizeof (u_short);
		m->m_len -= 2 * sizeof (u_short);
		*(mtod(m, struct ifnet **)) = ifp;
	}

	switch (type) {

#ifdef INET
	case ETHERTYPE_IP:
		schednetisr(NETISR_IP);
		inq = &ipintrq;
		break;

	case ETHERTYPE_ARP:
		arpinput(&ds->ds_ac, m);
		return;
#endif		/* INET */
	default:
#ifdef not_on_pdp
		/*
		 * see if other protocol families defined
		 * and call protocol specific routines.
		 * If no other protocols defined then dump message.
		 */

		if (pr=iftype_to_proto(type))  {
			if ((m = (struct mbuf *)(*pr->pr_ifinput)(m, &ds->ds_if, &inq)) == 0)
				return;
		} else {
#endif		/* not_on_pdp */
#ifdef DE_DEBUG
		if(dedebug >= 1)
			printf("de%d: Unknow Packet Type 0%o(%d)\n",
				ds->ds_if.if_unit, type, type);
#endif		/* DE_DEBUG */
#ifdef	DE_DO_BCTRS
			if (ds->ds_unrecog != 0xffff)
				ds->ds_unrecog++;
#endif		/* DE_DO_BCTRS */
			m_freem(m);
			return;
#ifdef not_on_pdp
		}
#endif		/* not_on_pdp */
	}

	s = splimp();
	if (IF_QFULL(inq)) {
		IF_DROP(inq);
		splx(s);
#ifdef DE_DEBUG
		if (dedebug >= 1)
			printf("de%d: Packet Dropped(deread)\n",
				ds->ds_if.if_unit);
#endif		/* DE_DEBUG */
		m_freem(m);
		return;
	}
	IF_ENQUEUE(inq, m);
	splx(s);
}

/*
 * 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.
 */
deoutput(ifp, m0, dst)
	struct ifnet *ifp;
	struct mbuf *m0;
	struct sockaddr *dst;
{
	int type, s, error;
	u_char edst[6];
	struct in_addr idst;
	struct protosw *pr;
	register struct de_softc *ds = &de_softc[ifp->if_unit];
	register struct mbuf *m = m0;
	register struct ether_header *eh;
	register int off;
	int usetrailers;

	if((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) {
		error = ENETDOWN;
		goto bad;
	}

	switch (dst->sa_family) {

#ifdef INET
	case AF_INET:
		idst = ((struct sockaddr_in *)dst)->sin_addr;
		if (!arpresolve(&ds->ds_ac, m, &idst, edst, &usetrailers))
			return (0);	/* if not yet resolved */

		off = ntohs((u_short)mtod(m, struct ip *)->ip_len) - m->m_len;

		/* need per host negotiation */
		if (usetrailers && off > 0 && (off & 0x1ff) == 0 &&
		    m->m_off >= MMINOFF + 2 * sizeof (u_short)) {
			type = ETHERTYPE_TRAIL + (off>>9);
			m->m_off -= 2 * sizeof (u_short);
			m->m_len += 2 * sizeof (u_short);
			*mtod(m, u_short *) = htons((u_short)ETHERTYPE_IP);
			*(mtod(m, u_short *) + 1) = htons((u_short)m->m_len);
			goto gottrailertype;
		}
		type = ETHERTYPE_IP;
		off = 0;
		goto gottype;
#endif		/* INET */
#ifdef	NS
	case AF_NS:
		type = ETHERTYPE_NS;
		bcopy((caddr_t)&(((struct sockaddr_ns *)dst)->sns_addr.x_host),
		(caddr_t)edst, sizeof (edst));
		off = 0;
		goto gottype;
#endif		/* NS */

	case AF_UNSPEC:
		eh = (struct ether_header *)dst->sa_data;
		bcopy((caddr_t)eh->ether_dhost, (caddr_t)edst, sizeof (edst));
		type = eh->ether_type;
		goto gottype;

	default:
#ifdef	DE_INT_LOOPBACK
		/*
		 * If we are in loopback mode check the length and
		 * device type.  DELUA can only loopback frames of 36 bytes
		 * or less including crc.
		 */
		if ((ifp->if_flags & IFF_LOOPBACK) &&
		    (m->m_len > DELUA_LOOP_LEN) && (ds->ds_devid == DELUA))
			return(EINVAL);
#endif		/* DE_INT_LOOPBACK */
#ifdef not_on_pdp
		/*
		 * try to find other address families and call protocol
		 * specific output routine.
		 */
		if (pr=iffamily_to_proto(dst->sa_family)) {
			(*pr->pr_ifoutput)(ifp, m0, dst, &type, (char *)edst);
			goto gottype;
		} else {
#endif		/* not_on_pdp */
			printf("de%d: can't handle af%d", ifp->if_unit, dst->sa_family);
			error = EAFNOSUPPORT;
			goto bad;
#ifdef not_on_pdp
		}
#endif		/* not_on_pdp */
	}

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);
	}
	eh = mtod(m, struct ether_header *);
	eh->ether_type = htons((u_short)type);
	bcopy((caddr_t)edst, (caddr_t)eh->ether_dhost, sizeof (edst));
	/* DEUNA fills in source address */
	bcopy((caddr_t)ds->ds_addr,
	      (caddr_t)eh->ether_shost, sizeof(ds->ds_addr));

	/*
	 * 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);
#ifdef DE_DEBUG
		if (dedebug >= 1)
			printf("de%d: Packet Dropped(deoutput)\n",
				ifp->if_unit);
#endif		/* DE_DEBUG */
		m_freem(m);
		return (ENOBUFS);
	}
	IF_ENQUEUE(&ifp->if_snd, m);
	destart(ifp->if_unit);
	splx(s);
	return (0);

bad:
	m_freem(m0);
	return (error);
}

/*
 * Routines supporting UNIBUS network interfaces.
 */
/*
 * Init UNIBUS for interface.  We map the i/o area
 * onto the UNIBUS. We then split up the i/o area among
 * all the receive and transmit buffers.
 */

static
de_ubainit(ifu, uban, hlen, nmr)
	register struct deuba *ifu;
	int uban, hlen, nmr;
{
	register caddr_t cp, dp;
	register struct ifrw *ifrw;
	int i, ncl;

	/*
	 * If the ring already has core allocated, quit now.
	 */
	if (ifu->difu_r[0].ifrw_click)
		return(1);

	nmr = ctob(nmr);
	nmr += hlen;
	for (i = 0; i < NRCV; i++) {
		ifu->difu_r[i].ifrw_click = m_ioget(nmr);
		if (ifu->difu_r[i].ifrw_click == 0) {
			ifu->difu_r[0].ifrw_click = 0;
			if (i)
				printf("de: lost some space\n");
			return(0);
		}
	}
	for (i = 0; i < NXMT; i++) {
		ifu->difu_w[i].ifrw_click = m_ioget(nmr);
		if (ifu->difu_w[i].ifrw_click == 0) {
			ifu->difu_w[0].ifrw_click = 0;
			ifu->difu_r[0].ifrw_click = 0;
			if (i)
				printf("de: lost some space\n");
			return(0);
		}
	}
	for (i = 0; i < NRCV; i++)
		ifu->difu_r[i].ifrw_info = 
			ubmalloc(ifu->difu_r[i].ifrw_click);
	for (i = 0; i < NXMT; i++)
		ifu->difu_w[i].ifrw_info = 
			ubmalloc(ifu->difu_w[i].ifrw_click);
	ifu->ifu_hlen = hlen;
	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.
 */
static struct mbuf *
deget(ifu, ifrw, totlen, off0, ifp)
	register struct deuba *ifu;
	register struct ifrw *ifrw;
	int totlen, off0;
	struct ifnet *ifp;
{
	struct mbuf *top, **mp, *m;
	int off = off0, len;
	register caddr_t cp = (caddr_t) ifu->ifu_hlen;
	u_int click;

	top = 0;
	mp = &top;
	click = ifrw->ifrw_click;
	while (totlen > 0) {
		MGET(m, M_DONTWAIT, MT_DATA);
		if (m == 0)
			goto bad;
		if (off) {
			len = totlen - off;
			cp = (caddr_t)(ifu->ifu_hlen + off);
		} else
			len = totlen;
		m->m_off = MMINOFF;
		if (ifp) {
			/*
			 * Leave Room for the ifp
			 */
			m->m_len = MIN(MLEN - sizeof(ifp), len);
			m->m_off += sizeof(ifp);
		} else
			m->m_len = MIN(MLEN, len);

		mbcopyin(click, cp, mtod(m, char *), (u_int) m->m_len);
		cp += m->m_len;
		*mp = m;
		mp = &m->m_next;
		if (off) {
			/* sort of an ALGOL-W style for statement... */
			off += m->m_len;
			if (off == totlen) {
				cp = (caddr_t) ifu->ifu_hlen;
				off = 0;
				totlen = off0;
			}
		} else
			totlen -= m->m_len;
		if (ifp) {
			/*
			 * Prepend interface pointer to first mbuf
			 */
			m->m_len += sizeof(ifp);
			m->m_off -= sizeof(ifp);
			*(mtod(m, struct ifnet **)) = ifp;
			ifp = (struct ifnet *) 0;
		}
	}
	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.
 */
static
deput(ifu, n, m)
	struct deuba *ifu;
	int n;
	register struct mbuf *m;
{
	register struct mbuf *mp;
	register u_short off, click;

	click = ifu->difu_w[n].ifrw_click;
	off = 0;
	while (m) {
		mbcopyout(mtod(m, char *), click, off, (u_int) m->m_len);
		off += m->m_len;
		MFREE(m, mp);
		m = mp;
	}

	return (off);
}

/*
 * Process an ioctl request.
 */
deioctl(ifp, cmd, data)
	register struct ifnet *ifp;
	int cmd;
	caddr_t data;
{
	register struct de_softc *ds = &de_softc[ifp->if_unit];
	register struct uba_device *ui = deinfo[ifp->if_unit];
	register struct dedevice *addr = (struct dedevice *)ui->ui_addr;
	struct protosw *pr;
	struct sockaddr *sa;
	struct ifreq *ifr = (struct ifreq *)data;
	struct ifdevea *ifd = (struct ifdevea *)data;
	register struct ifaddr *ifa = (struct ifaddr *)data;
	int s = splimp(), error = 0;

	switch (cmd) {

#ifdef DE_INT_LOOPBACK
        case SIOCENABLBACK:
                printf("de%d: internal loopback enable requested\n", ifp->if_unit);
                if ( (error = deloopback(ifp, ds, addr, YES)) != NULL )
                        break;
                ifp->if_flags |= IFF_LOOPBACK;
                break;
 
        case SIOCDISABLBACK:
                printf("de%d: internal loopback disable requested\n", ifp->if_unit);
                if ( (error = deloopback(ifp, ds, addr, NO)) != NULL )
                        break;
                ifp->if_flags &= ~IFF_LOOPBACK;
                deinit(ifp->if_unit);
                break;
#endif		/* DE_INT_LOOPBACK */
 
#ifdef	DE_DO_PHYSADDR
        case SIOCRPHYSADDR: 
                /*
                 * read default hardware address.
                 */
                ds->ds_pcbb.pcbb0 = FC_RDDEFAULT;
                addr->pclow = CMD_GETCMD|((ds->ds_flags & DSF_RUNNING) ? PCSR0_INTE : 0);
		if (dewait(ui, "read default hardware address", NO, YES)) {
                        error = EIO;
                        break;
                }
                /*
                 * copy current physical address and default hardware address
                 * for requestor.
                 */
                bcopy(&ds->ds_pcbb.pcbb2, ifd->default_pa, 6);
                bcopy(ds->ds_addr, ifd->current_pa, 6);
                break;
 

	case SIOCSPHYSADDR: 
		/* Set the DNA address as the de's physical address */
		ds->ds_pcbb.pcbb0 = FC_WTPHYAD;
		bcopy (ifr->ifr_addr.sa_data, &ds->ds_pcbb.pcbb2, 6);
		addr->pclow = CMD_GETCMD|((ds->ds_flags & DSF_RUNNING) ? PCSR0_INTE : 0);
		(void) dewait(ui, "write physical address", NO, YES);
		bcopy((caddr_t)&ds->ds_pcbb.pcbb2,(caddr_t)ds->ds_addr,
	    		sizeof (ds->ds_addr));
		deinit(ifp->if_unit);
		break;
#endif		/* DE_DO_PHYSADDR */

#ifdef DE_DO_MULTI
	case SIOCDELMULTI: 
	case SIOCADDMULTI: 
		{
		int i,j = -1,incaddr = ds->ds_ubaddr + MULTI_OFFSET;

		if (cmd==SIOCDELMULTI) {
		   for (i = 0; i < NMULTI; i++)
		       if (bcmp(&ds->ds_multicast[i],ifr->ifr_addr.sa_data,MULTISIZE) == 0) {
			    if (--ds->ds_muse[i] == 0)
				bcopy(unused_multi,&ds->ds_multicast[i],MULTISIZE);
		       }
		} else {
		    for (i = 0; i < NMULTI; i++) {
			if (bcmp(&ds->ds_multicast[i],ifr->ifr_addr.sa_data,MULTISIZE) == 0) {
			    ds->ds_muse[i]++;
			    goto done;
			}
		        if (bcmp(&ds->ds_multicast[i],unused_multi,MULTISIZE) == 0)
			    j = i;
		    }
		    if (j == -1) {
			printf("de%d: mtmulti failed, multicast list full: %d\n",
				ui->ui_unit, NMULTI);
			error = ENOBUFS;
			goto done;
		    }
		    bcopy(ifr->ifr_addr.sa_data, &ds->ds_multicast[j], MULTISIZE);
		    ds->ds_muse[j]++;
		}

		ds->ds_pcbb.pcbb0 = FC_WTMULTI;
		ds->ds_pcbb.pcbb2 = incaddr & 0xffff;
		ds->ds_pcbb.pcbb4 = (NMULTI << 8) | ((incaddr >> 16) & 03);
		addr->pclow = CMD_GETCMD|((ds->ds_flags & DSF_RUNNING) ? PCSR0_INTE : 0);
		(void) dewait(ui, "set multicast address", NO, YES);
		break;

		}

#endif		/* DE_DO_MULTI */

#ifdef	DE_DO_BCTRS
	case SIOCRDCTRS:
	case SIOCRDZCTRS:
		{
		register struct ctrreq *ctr = (struct ctrreq *)data;
		ubadr_t incaddr;

		ds->ds_pcbb.pcbb0 = cmd == SIOCRDCTRS ? FC_RDCNTS : FC_RCCNTS;
		incaddr = ds->ds_ubaddr + COUNTER_OFFSET;
		ds->ds_pcbb.pcbb2 = incaddr & 0xffff;
		ds->ds_pcbb.pcbb4 = (incaddr >> 16) & 0x3;
		ds->ds_pcbb.pcbb6 = sizeof(struct de_counters) / sizeof (short);
		addr->pclow = CMD_GETCMD|((ds->ds_flags & DSF_RUNNING) ? PCSR0_INTE : 0);
		if (dewait(ui, "read counts", NO, YES)) {
			error = ENOBUFS;
			break;
		}
		bzero(&ctr->ctr_ether, sizeof(struct estat));
		ctr->ctr_type = CTR_ETHER;
		cpfromkern(&time, &atv, sizeof(struct timeval));
		ctr->ctr_ether.est_seconds = (atv.tv_sec - ds->ds_ztime) > 0xfffe ? 0xffff : (atv.tv_sec - ds->ds_ztime);
		ctr->ctr_ether.est_byrcvd = *(long *)ds->ds_counters.c_brcvd;
		ctr->ctr_ether.est_bysent = *(long *)ds->ds_counters.c_bsent;
		ctr->ctr_ether.est_mbyrcvd = *(long *)ds->ds_counters.c_mbrcvd;
		ctr->ctr_ether.est_blrcvd = *(long *)ds->ds_counters.c_prcvd;
		ctr->ctr_ether.est_blsent = *(long *)ds->ds_counters.c_psent;
		ctr->ctr_ether.est_mblrcvd = *(long *)ds->ds_counters.c_mprcvd;
		ctr->ctr_ether.est_deferred = *(long *)ds->ds_counters.c_defer;
		ctr->ctr_ether.est_single = *(long *)ds->ds_counters.c_single;
		ctr->ctr_ether.est_multiple = *(long *)ds->ds_counters.c_multiple;
		ctr->ctr_ether.est_sf = ds->ds_counters.c_snderr;
		ctr->ctr_ether.est_sfbm = ds->ds_counters.c_sbm & 0xff;
		ctr->ctr_ether.est_collis = ds->ds_counters.c_collis;
		ctr->ctr_ether.est_rf = ds->ds_counters.c_rcverr;
		ctr->ctr_ether.est_rfbm = ds->ds_counters.c_rbm & 0xff;
		ctr->ctr_ether.est_unrecog = ds->ds_unrecog;
		ctr->ctr_ether.est_sysbuf = ds->ds_counters.c_ibuferr;
		ctr->ctr_ether.est_userbuf = ds->ds_counters.c_lbuferr;
		if (cmd == SIOCRDZCTRS) {
			cpfromkern(&time, &atv, sizeof(struct timeval));
			ds->ds_ztime = atv.tv_sec;
			ds->ds_unrecog = 0;
		}
		break;
		}
#endif		/* DE_DO_BCTRS */

	case SIOCSIFADDR:
		ifp->if_flags |= IFF_UP;
		deinit(ifp->if_unit);
		switch (ifa->ifa_addr.sa_family) {
#ifdef	INET
		case AF_INET:
			((struct arpcom *)ifp)->ac_ipaddr =
				IA_SIN(ifa)->sin_addr;
			arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr);
			break;
#endif		/* INET */
#ifdef	NS
		case AF_NS:
		    {
			register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr);

			if (ns_nullhost(*ina)) {
				ina->x_host = * (union ns_host *)
					(de_softc[ifp->if_unit].ds_addr);
			} else {
				de_setaddr(ina->x_host.c_host,ifp->if_unit);
			}
			break;
		    }
#endif		/* NS */
		default:
#ifdef not_on_pdp
			if (pr=iffamily_to_proto(ifa->ifa_addr.sa_family)) {
				error = (*pr->pr_ifioctl)(ifp, cmd, data);
			}
#endif		/* not_on_pdp */
#ifdef DE_DEBUG
			if (dedebug >= 1)
				printf("de%d: SIOCSIFADDR Unknown address family\n", ifp->if_unit);
#endif		/* DE_DEBUG */
			error = EAFNOSUPPORT;
			break;
		}
		break;

	case SIOCSIFFLAGS:
		if ((ifp->if_flags & IFF_UP) == 0 &&
		    ds->ds_flags & DSF_RUNNING) {
			((struct dedevice *)
			   (deinfo[ifp->if_unit]->ui_addr))->pclow = PCSR0_RSET;
			(void) dewait(deinfo[ifp->if_unit], "board reset(deioctl)", YES, NO);
			ds->ds_flags &= ~(DSF_LOCK | DSF_RUNNING);
#ifdef	DE_DEBUG
			if (dedebug >= 1)
				printf("de%d: reset and marked down\n",ifp->if_unit);
#endif		/* DE_DEBUG */
		} else if (ifp->if_flags & IFF_UP &&
		    (ds->ds_flags & DSF_RUNNING) == 0) {
			deinit(ifp->if_unit);
#ifdef	DE_DEBUG
			if (dedebug >= 1)
				printf("de%d: reinitialized and marked up\n",ifp->if_unit);
#endif		/* DE_DEBUG */
		}
#ifdef	DE_DEBUG
		if ((ifp->if_flags & IFF_DEBUG) == 0) {
			if (dedebug != 0) {
				dedebug = 0;
				printf("de%d: DEBUG disabled\n",ifp->if_unit);
			}
		} else {
			if (dedebug == 0) {
				dedebug = DE_DEBUG_LEVEL;
				printf("de%d: DEBUG enabled level=%d\n",ifp->if_unit,dedebug);
			}
		}
#endif		/* DE_DEBUG */
		break;

	default:
#ifdef	not_on_pdp
		if (pr=iffamily_to_proto(ifa->ifa_addr.sa_family))
			error = (*pr->pr_ifioctl)(ifp, cmd, data);
		else
#endif		/* not_on_pdp */
		error = EINVAL;
	}
done:	splx(s);
	return (error);
}

#ifdef DE_INT_LOOPBACK
/*
 * enable or disable internal loopback
 */
static
deloopback(ifp, ds, addr, lb_ctl )
register struct ifnet *ifp;
register struct de_softc *ds;
register struct dedevice *addr;
u_char lb_ctl;
{
	register struct uba_device *ui = deinfo[ifp->if_unit];
        /*
         * read current mode register.
         */
        ds->ds_pcbb.pcbb0 = FC_RDMODE;
        addr->pclow = CMD_GETCMD|((ds->ds_flags & DSF_RUNNING) ? PCSR0_INTE : 0);
	if (dewait(ui, "read mode register", NO, YES))
                return(EIO);

        /*
         * set or clear the loopback bit as a function of lb_ctl and
         * return mode register to driver.
         */
        if ( lb_ctl == YES ) {
                ds->ds_pcbb.pcbb2 |= MOD_LOOP;
		if (ds->ds_devid == DELUA)
			ds->ds_pcbb.pcbb2 |= MOD_INTL;
		else
                	ds->ds_pcbb.pcbb2 &= ~MOD_HDX;
        } else {
                ds->ds_pcbb.pcbb2 &= ~MOD_LOOP;
		if (ds->ds_devid == DELUA)
			ds->ds_pcbb.pcbb2 &= ~MOD_INTL;
		else
			ds->ds_pcbb.pcbb2 |= MOD_HDX;
        }
        ds->ds_pcbb.pcbb0 = FC_WTMODE;
        addr->pclow = CMD_GETCMD|((ds->ds_flags & DSF_RUNNING) ? PCSR0_INTE : 0);
	if(dewait(ui, "write mode register", NO, YES))
                return(EIO);

        return(NULL);
}
#endif		/* DE_INT_LOOPBACK */

dewait(ui, fn, no_port, only_dni)
register struct uba_device *ui;
char *fn;
int no_port;
int only_dni;
{
	register struct de_softc *ds = &de_softc[ui->ui_unit];
	register struct dedevice *addr = (struct dedevice *)ui->ui_addr;
	register csr0;
	
	if (only_dni)
	    while ((addr->pcsr0 & PCSR0_DNI) == 0)
		    ;
	else
	    while ((addr->pcsr0 & PCSR0_INTR) == 0)
		    ;
	csr0 = addr->pcsr0;
	addr->pchigh = csr0 >> 8;
	if ((csr0 & PCSR0_PCEI) || (no_port && (csr0 & PCSR0_DNI)==0)) {
		if (ds->ds_devid == DEUNA)
			printf("de%d: %s failed, csr0=%b, csr1=%b\n",
				ui->ui_unit, fn, csr0, PCSR0_BITS,
				addr->pcsr1, PCSR1_BITS);
		else
			printf("de%d: %s failed, csr0=%b, csr1=%b\n",
				ui->ui_unit, fn, csr0, PCSR0_BITS,
				addr->pcsr1, PCSR1_BITS_DELUA);
	}
	return(csr0 & PCSR0_PCEI);
}

#ifdef	NS
/*
 * Set the ethernet address for the unit, this sets the board's
 * hardware address to physaddr.  This is only used if you have
 * NS defined.
 */
de_setaddr(physaddr, unit)
u_char *physaddr;
int unit;
{
	register struct de_softc *ds = &de_softc[unit];
	struct uba_device *ui = deinfo[unit];
	register struct dedevice *addr = (struct dedevice *)ui->ui_addr;

	if (! (ds->ds_flags & DSF_RUNNING))
		return;
	bcopy(physaddr, &ds->ds_pcbb.pcbb2, 6);
	ds->ds_pcbb.pcbb0 = FC_WTPHYAD;
	addr->pclow = PCSR0_INTE | CMD_GETCMD;
	if (dewait(ui, "address changed", NO, NO) == 0) {
		ds->ds_flags |= DSF_SETADDR;
		bcopy(physaddr, ds->ds_addr, 6);
	}
}
#endif		/* NS */
#endif		/* NDE > 0 */