Source to arch/i386/isa/if_ne.c


Enter a symbol's name here to quickly find it.

/*-
 * Copyright (c) 1990, 1991 William F. Jolitz.
 * Copyright (c) 1990 The Regents of the University of California.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by the University of
 *	California, Berkeley and its contributors.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *	from: @(#)if_ne.c	7.4 (Berkeley) 5/21/91
 *	if_ne.c,v 1.9 1993/07/17 16:41:27 mycroft Exp
 */

/*
 * NE2000/NE1000 Ethernet driver
 *
 * Parts inspired from Tim Tucker's if_wd driver for the wd8003,
 * insight on the ne2000 gained from Robert Clements PC/FTP driver.
 *
 * Corrected for NE1000 by Andrew A. Chernov
 */

#include "ne.h"
#if NNE > 0

#include "param.h"
#include "systm.h"
#include "mbuf.h"
#include "buf.h"
#include "protosw.h"
#include "socket.h"
#include "ioctl.h"
#include "errno.h"
#include "syslog.h"

#include "net/if.h"
#include "net/netisr.h"
#include "net/route.h"

#ifdef INET
#include "netinet/in.h"
#include "netinet/in_systm.h"
#include "netinet/in_var.h"
#include "netinet/ip.h"
#include "netinet/if_ether.h"
#endif

#ifdef NS
#include "netns/ns.h"
#include "netns/ns_if.h"
#endif

#include "bpfilter.h"
#if NBPFILTER > 0
#include "sys/select.h"
#include "net/bpf.h"
#include "net/bpfdesc.h"
#endif

#include "i386/isa/isa_device.h"
#include "i386/isa/if_nereg.h"
#include "i386/isa/icu.h"

int	neprobe(), neattach(), neintr();
int	nestart(),neinit(), ether_output(), neioctl();

struct	isa_driver nedriver = {
	neprobe, neattach, "ne",
};

struct	mbuf *neget();

#define ETHER_MIN_LEN 64
#define ETHER_MAX_LEN 1536

/*
 * Ethernet software status per interface.
 *
 * Each interface is referenced by a network interface structure,
 * ns_if, which the routing code uses to locate the interface.
 * This structure contains the output queue for the interface, its address, ...
 */
struct	ne_softc {
	struct	arpcom ns_ac;		/* Ethernet common part */
#define	ns_if	ns_ac.ac_if		/* network-visible interface */
#define	ns_addrp	ns_ac.ac_enaddr		/* hardware Ethernet address */
	int	ns_flags;
#define	DSF_LOCK	1		/* block re-entering enstart */
	int	ns_oactive ;
	int	ns_mask ;
	int	ns_ba;			/* byte addr in buffer ram of inc pkt */
	int	ns_cur;			/* current page being filled */
	struct	prhdr	ns_ph;		/* hardware header of incoming packet*/
	struct	ether_header ns_eh;	/* header of incoming packet */
	u_char	ns_pb[2048 /*ETHERMTU+sizeof(long)*/];
	short	ns_txstart;		/* transmitter buffer start */
	u_short ns_rxend;               /* recevier buffer end */
	short	ns_port;		/* i/o port base */
	short	ns_mode;		/* word/byte mode */
	caddr_t	ns_bpf;			/* BPF frob */
} ne_softc[NNE] ;
#define	ENBUFSIZE	(sizeof(struct ether_header) + ETHERMTU + 2 + ETHER_MIN_LEN)

#define	PAT(n)	(0xa55a + 37*(n))

u_short boarddata[16];
 
neprobe(dvp)
	struct isa_device *dvp;
{
	int val, i, s, sum, bytemode = 1, pat;
	register struct ne_softc *ns = &ne_softc[0];
	register nec;

#ifdef lint
	neintr(0);
#endif

	nec = ns->ns_port = dvp->id_iobase;
	s = splimp();

	if (bytemode) {
		/* Byte Transfers, Burst Mode Select, Fifo at 8 bytes */
		ns->ns_mode = DSDC_BMS|DSDC_FT1;
		ns->ns_txstart = TBUF8;
		ns->ns_rxend = RBUFEND8;
	} else {
word:
		/* Word Transfers, Burst Mode Select, Fifo at 8 bytes */
		ns->ns_mode = DSDC_WTS|DSDC_BMS|DSDC_FT1;
		ns->ns_txstart = TBUF16;
		ns->ns_rxend = RBUFEND16;
		bytemode = 0;
	}

	/* Reset the bastard */
	val = inb(nec + ne_reset);
	DELAY(200);
	outb(nec + ne_reset, val);
	DELAY(200);

	outb(nec + ds_cmd, DSCM_STOP|DSCM_NODMA);
	
	i = 10000;
	while ((inb(nec + ds0_isr) & DSIS_RESET) == 0 && i-- > 0);
	if (i < 0) return (0);

	outb(nec + ds0_isr, 0xff);
	outb(nec + ds0_dcr, ns->ns_mode);
	outb(nec + ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_STOP);
	DELAY(1000);

	/* Check cmd reg and fail if not right */
	if ((i = inb(nec + ds_cmd)) != (DSCM_NODMA|DSCM_PG0|DSCM_STOP))
		return(0);

	outb(nec + ds0_tcr, 0);
	outb(nec + ds0_rcr, DSRC_MON);
	outb(nec + ds0_pstart, (ns->ns_txstart+PKTSZ)/DS_PGSIZE);
	outb(nec + ds0_pstop, ns->ns_rxend/DS_PGSIZE);
	outb(nec + ds0_bnry, ns->ns_rxend/DS_PGSIZE);
	outb(nec + ds0_imr, 0);
	outb(nec + ds0_isr, 0);
	outb(nec + ds_cmd, DSCM_NODMA|DSCM_PG1|DSCM_STOP);
	outb(nec + ds1_curr, (ns->ns_txstart+PKTSZ)/DS_PGSIZE);
	outb(nec + ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_STOP);


#ifdef NEDEBUG
#define	RCON	37
	{	int i, rom;

		rom=1;
		printf("ne ram ");
		
		for (i = 0; i < 0xfff0; i+=4) {
			pat = PAT(i);
			neput(ns, &pat,i,4);
			nefetch(ns, &pat,i,4);
			if (pat == PAT(i)) {
				if (rom) {
					rom=0;
					printf(" %x", i);
				}
			} else {
				if (!rom) {
					rom=1;
					printf("..%x ", i);
				}
			}
			pat=0;
			neput(ns, &pat,i,4);
		}
		printf("\n");
	}
#endif

	/*
	 * <groan> detect difference between units
	 * solely by where the RAM is decoded.
	 */
	pat = PAT(0);
	neput(ns, &pat, ns->ns_txstart, 4);
	nefetch(ns, &pat, ns->ns_txstart, 4);
	if (pat != PAT(0)) {
		if (bytemode)
			goto word;
		else return (0);
	}


	/* Extract board address */
	nefetch (ns, (caddr_t)boarddata, 0, sizeof(boarddata));

	for(i=0; i < 6; i++)
		ns->ns_addrp[i] = boarddata[i];
	splx(s);
	return (16);
}

/*
 * Fetch from onboard ROM/RAM
 */
nefetch (ns, up, ad, len) struct ne_softc *ns; caddr_t up; {
	u_char cmd;
	register nec = ns->ns_port;
	int counter = 100000;

	cmd = inb (nec + ds_cmd);
	outb (nec + ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_START);

	/* Setup remote dma */
	outb (nec + ds0_isr, DSIS_RDC);

	if ((ns->ns_mode & DSDC_WTS) && len&1)
		len++;		/* roundup to words */

	outb (nec+ds0_rbcr0, len);
	outb (nec+ds0_rbcr1, len>>8);
	outb (nec+ds0_rsar0, ad);
	outb (nec+ds0_rsar1, ad>>8);

	/* Execute & extract from card */
	outb (nec+ds_cmd, DSCM_RREAD|DSCM_PG0|DSCM_START);

	if (ns->ns_mode & DSDC_WTS)
		insw (nec+ne_data, up, len/2);
	else
		insb (nec+ne_data, up, len);

	/* Wait till done, then shutdown feature */
	while ((inb (nec+ds0_isr) & DSIS_RDC) == 0 && counter-- > 0)
		;
	outb (nec+ds0_isr, DSIS_RDC);
	outb (nec+ds_cmd, cmd);
}

/*
 * Put to onboard RAM
 */
neput (ns, up, ad, len) struct ne_softc *ns; caddr_t up; {
	u_char cmd;
	register nec = ns->ns_port;
	int counter = 100000;

	cmd = inb(nec+ds_cmd);
	outb (nec+ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_START);

	/* Setup for remote dma */
	outb (nec+ds0_isr, DSIS_RDC);

	if ((ns->ns_mode & DSDC_WTS) && len&1)
		len++;		/* roundup to words */

	outb (nec+ds0_rbcr0, len);
	outb (nec+ds0_rbcr1, len>>8);
	outb (nec+ds0_rsar0, ad);
	outb (nec+ds0_rsar1, ad>>8);

	/* Execute & stuff to card */
	outb (nec+ds_cmd, DSCM_RWRITE|DSCM_PG0|DSCM_START);
	if (ns->ns_mode & DSDC_WTS)
		outsw (nec+ne_data, up, len/2);
	else
		outsb (nec+ne_data, up, len);
	
	/* Wait till done, then shutdown feature */
	while ((inb (nec+ds0_isr) & DSIS_RDC) == 0 && counter-- > 0)
		;
	outb (nec+ds0_isr, DSIS_RDC);
	outb (nec+ds_cmd, cmd);
}

/*
 * Reset of interface.
 */
nereset(unit, uban)
	int unit, uban;
{
	if (unit >= NNE)
		return;
	printf("ne%d: reset\n", unit);
	ne_softc[unit].ns_flags &= ~DSF_LOCK;
	neinit(unit);
}
 
/*
 * 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.
 */
neattach(dvp)
	struct isa_device *dvp;
{
	int unit = dvp->id_unit;
	register struct ne_softc *ns = &ne_softc[unit];
	register struct ifnet *ifp = &ns->ns_if;

	ifp->if_unit = unit;
	ifp->if_name = nedriver.name ;
	ifp->if_mtu = ETHERMTU;
	printf ("ne%d: ne%s ethernet address %s\n", unit,
		(ns->ns_mode & DSDC_WTS) ? "2000" : "1000",
		ether_sprintf(ns->ns_addrp));
	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS;
	ifp->if_init = neinit;
	ifp->if_output = ether_output;
	ifp->if_start = nestart;
	ifp->if_ioctl = neioctl;
	ifp->if_reset = nereset;
	ifp->if_watchdog = 0;
	if_attach(ifp);

#if NBPFILTER > 0
	bpfattach(&ns->ns_bpf, ifp, DLT_EN10MB,
		  sizeof(struct ether_header));
#endif
}

/*
 * Initialization of interface; set up initialization block
 * and transmit/receive descriptor rings.
 */
neinit(unit)
	int unit;
{
	register struct ne_softc *ns = &ne_softc[unit];
	struct ifnet *ifp = &ns->ns_if;
	int s;
	int i; char *cp;
	register nec = ns->ns_port;

 	if (ifp->if_addrlist == (struct ifaddr *)0) return;
	if (ifp->if_flags & IFF_RUNNING) return;

	s = splimp();

	/* set physical address on ethernet */
	outb (nec+ds_cmd, DSCM_NODMA|DSCM_PG1|DSCM_STOP);
	for (i=0 ; i < 6 ; i++) outb(nec+ds1_par0+i,ns->ns_addrp[i]);

	/* clr logical address hash filter for now */
	for (i=0 ; i < 8 ; i++) outb(nec+ds1_mar0+i,0xff);

	/* init regs */
	outb (nec+ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_STOP);
	outb (nec+ds0_rbcr0, 0);
	outb (nec+ds0_rbcr1, 0);
	outb (nec+ds0_imr, 0);
	outb (nec+ds0_isr, 0xff);

	/* Word Transfer select, Burst Mode Select, Fifo at 8 bytes */
	outb(nec+ds0_dcr, ns->ns_mode);

	outb(nec+ds0_tcr, 0);
	outb (nec+ds0_rcr, DSRC_MON);
	outb (nec+ds0_tpsr, 0);
	outb(nec+ds0_pstart, (ns->ns_txstart+PKTSZ)/DS_PGSIZE);
	outb(nec+ds0_pstop, ns->ns_rxend/DS_PGSIZE);
	outb(nec+ds0_bnry, (ns->ns_txstart+PKTSZ)/DS_PGSIZE);
	outb (nec+ds_cmd, DSCM_NODMA|DSCM_PG1|DSCM_STOP);
	outb(nec+ds1_curr, (ns->ns_txstart+PKTSZ)/DS_PGSIZE);
	ns->ns_cur = (ns->ns_txstart+PKTSZ)/DS_PGSIZE;
	outb (nec+ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_START);
#if NBPFILTER > 0
	if (ns->ns_if.if_flags & IFF_PROMISC)
		outb (nec+ds0_rcr, DSRC_AB | DSRC_PRO | DSRC_AR | DSRC_SEP);
	else
#endif
		outb (nec+ds0_rcr, DSRC_AB);
	outb(nec+ds0_dcr, ns->ns_mode);
	outb (nec+ds0_imr, 0xff);

	ns->ns_if.if_flags |= IFF_RUNNING;
	ns->ns_flags &= ~DSF_LOCK;
	ns->ns_oactive = 0; ns->ns_mask = ~0;
	nestart(ifp);
	splx(s);
}

/*
 * Setup output on interface.
 * Get another datagram to send off of the interface queue,
 * and map it to the interface before starting the output.
 * called only at splimp or interrupt level.
 * XXX: Needs BPF hook.
 */
nestart(ifp)
	struct ifnet *ifp;
{
	register struct ne_softc *ns = &ne_softc[ifp->if_unit];
	struct mbuf *m0, *m;
	int len, i, total,t;
	register nec = ns->ns_port;
	u_char cmd;
	u_short word;
	int counter;

	if (ns->ns_flags & DSF_LOCK)
		return;

	if ((ns->ns_if.if_flags & IFF_RUNNING) == 0)
		return;

	/*
	 * The DS8390 has only one transmit buffer, if it is busy we
	 * must wait until the transmit interrupt completes.
	 */
	outb(nec+ds_cmd,DSCM_NODMA|DSCM_START);

	if (inb(nec+ds_cmd) & DSCM_TRANS)
		return;

	IF_DEQUEUE(&ns->ns_if.if_snd, m);
 
	if (m == 0)
		return;

	ns->ns_flags |= DSF_LOCK;	/* prevent entering nestart */

	/*
	 * Copy the mbuf chain into the transmit buffer
	 */

	len = i = 0;
	t = 0;
	for (m0 = m; m != 0; m = m->m_next)
		t += m->m_len;
		
	/* next code derived from neput() */
		
	if ((ns->ns_mode & DSDC_WTS) && t&1)
		t++;          /* roundup to words */
		
	cmd = inb(nec+ds_cmd);
	outb (nec+ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_START);

	/* Setup for remote dma */
	outb (nec+ds0_isr, DSIS_RDC);

	outb (nec+ds0_rbcr0, t);
	outb (nec+ds0_rbcr1, t>>8);
	outb (nec+ds0_rsar0, ns->ns_txstart);
	outb (nec+ds0_rsar1, ns->ns_txstart>>8);

	/* Execute & stuff to card */
	outb (nec+ds_cmd, DSCM_RWRITE|DSCM_PG0|DSCM_START);

			m = m0;
	total = t;
	if (ns->ns_mode & DSDC_WTS) {        /* Word Mode */
		while (m != 0) {
			if (m->m_len > 1)
				outsw(nec+ne_data, m->m_data, m->m_len / 2);
			if (m->m_len & 1) {
				word = (u_char) *(mtod(m, caddr_t) + m->m_len - 1);
				if ((m = m->m_next) != 0) {
					word |= *mtod(m, caddr_t) << 8;
					m->m_len--;
					m->m_data++;
				}
				outsw(nec+ne_data, (caddr_t)&word, 1);
			} else
				m = m->m_next;
		}
	}
	else {                                /* Byte Mode */
		while (m != 0) {
			if (m->m_len > 0)
				outsb(nec+ne_data, mtod(m, caddr_t), m->m_len);
			m = m->m_next;
		}
	}

	m_freem(m0);

	counter = 100000;
	/* Wait till done, then shutdown feature */
	while ((inb (nec+ds0_isr) & DSIS_RDC) == 0 && counter-- > 0)
		;
	outb (nec+ds0_isr, DSIS_RDC);
	outb (nec+ds_cmd, cmd);

	/*
	 * Init transmit length registers, and set transmit start flag.
	 */

	len = total;
	if (len < ETHER_MIN_LEN) len = ETHER_MIN_LEN;
	outb(nec+ds0_tbcr0,len&0xff);
	outb(nec+ds0_tbcr1,(len>>8)&0xff);
	outb(nec+ds0_tpsr, ns->ns_txstart/DS_PGSIZE);
	outb(nec+ds_cmd, DSCM_TRANS|DSCM_NODMA|DSCM_START);
}

/* buffer successor/predecessor in ring? */
#define succ(n) (((n)+1 >= ns->ns_rxend/DS_PGSIZE) ? (ns->ns_txstart+PKTSZ)/DS_PGSIZE : (n)+1)
#define pred(n) (((n)-1 < (ns->ns_txstart+PKTSZ)/DS_PGSIZE) ? ns->ns_rxend/DS_PGSIZE-1 : (n)-1)

/*
 * Controller interrupt.
 */
neintr(unit)
{
	register struct ne_softc *ns = &ne_softc[unit];
	u_char cmd,isr;
	register nec = ns->ns_port;

	/* Save cmd, clear interrupt */
	cmd = inb (nec+ds_cmd);
loop:
	isr = inb (nec+ds0_isr);
	outb(nec+ds_cmd,DSCM_NODMA|DSCM_START);
	outb(nec+ds0_isr, isr);

	/* Receiver error */
	if (isr & DSIS_RXE) {
		/* need to read these registers to clear status */
		(void) inb(nec+ ds0_rsr);
		(void) inb(nec+ 0xD);
		(void) inb(nec + 0xE);
		(void) inb(nec + 0xF);
		ns->ns_if.if_ierrors++;
	}

	/* We received something; rummage thru tiny ring buffer */
	if (isr & (DSIS_RX|DSIS_RXE|DSIS_ROVRN)) {
		u_char pend,lastfree;

		outb(nec+ds_cmd, DSCM_START|DSCM_NODMA|DSCM_PG1);
		pend = inb(nec+ds1_curr);
		outb(nec+ds_cmd, DSCM_START|DSCM_NODMA|DSCM_PG0);
		lastfree = inb(nec+ds0_bnry);

		/* Have we wrapped? */
		if (lastfree >= ns->ns_rxend/DS_PGSIZE)
			lastfree = (ns->ns_txstart+PKTSZ)/DS_PGSIZE;
		if (pend < lastfree && ns->ns_cur < pend)
			lastfree = ns->ns_cur;
		else	if (ns->ns_cur > lastfree)
			lastfree = ns->ns_cur;

		/* Something in the buffer? */
		while (pend != lastfree) {
			u_char nxt;

			/* Extract header from microcephalic board */
			nefetch(ns, &ns->ns_ph,lastfree*DS_PGSIZE,
				sizeof(ns->ns_ph));
			ns->ns_ba = lastfree*DS_PGSIZE+sizeof(ns->ns_ph);

			/* Incipient paranoia */
			if (ns->ns_ph.pr_status == DSRS_RPC ||
				/* for dequna's */
				ns->ns_ph.pr_status == 0x21)
				nerecv (ns);
#ifdef NEDEBUG
			else  {
				printf("cur %x pnd %x lfr %x ",
					ns->ns_cur, pend, lastfree);
				printf("nxt %x len %x ", ns->ns_ph.pr_nxtpg,
					(ns->ns_ph.pr_sz1<<8)+ ns->ns_ph.pr_sz0);
				printf("Bogus Sts %x\n", ns->ns_ph.pr_status);	
			}
#endif

			nxt = ns->ns_ph.pr_nxtpg ;

			/* Sanity check */
			if ( nxt >= (ns->ns_txstart+PKTSZ)/DS_PGSIZE
				&& nxt <= ns->ns_rxend/DS_PGSIZE && nxt <= pend)
				ns->ns_cur = nxt;
			else	ns->ns_cur = nxt = pend;

			/* Set the boundaries */
			lastfree = nxt;
			outb(nec+ds0_bnry, pred(nxt));
			outb(nec+ds_cmd, DSCM_START|DSCM_NODMA|DSCM_PG1);
			pend = inb(nec+ds1_curr);
			outb(nec+ds_cmd, DSCM_START|DSCM_NODMA|DSCM_PG0);
		}
		outb(nec+ds_cmd, DSCM_START|DSCM_NODMA);
	}

	/* Transmit error */
	if (isr & DSIS_TXE) {
		ns->ns_flags &= ~DSF_LOCK;
		/* Need to read these registers to clear status */
		ns->ns_if.if_collisions += inb(nec+ds0_tbcr0);
		ns->ns_if.if_oerrors++;
	}

	/* Packet Transmitted */
	if (isr & DSIS_TX) {
		ns->ns_flags &= ~DSF_LOCK;
		++ns->ns_if.if_opackets;
		ns->ns_if.if_collisions += inb(nec+ds0_tbcr0);
	}

	/* Receiver ovverun? */
	if (isr & DSIS_ROVRN) {
		log(LOG_ERR, "ne%d: error: isr %x\n", ns-ne_softc, isr
			/*, DSIS_BITS*/);
		outb(nec+ds0_rbcr0, 0);
		outb(nec+ds0_rbcr1, 0);
		outb(nec+ds0_tcr, DSTC_LB0);
		outb(nec+ds0_rcr, DSRC_MON);
		outb(nec+ds_cmd, DSCM_START|DSCM_NODMA);
		outb(nec+ds0_rcr, DSRC_AB);
		outb(nec+ds0_tcr, 0);
	}

	/* Any more to send? */
	outb (nec+ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_START);
	nestart(&ns->ns_if);
	outb (nec+ds_cmd, cmd);
	outb (nec+ds0_imr, 0xff);

	/* Still more to do? */
	isr = inb (nec+ds0_isr);
	if(isr) goto loop;
}

/*
 * Ethernet interface receiver interface.
 * If input error just drop packet.
 * Otherwise 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.
 */
nerecv(ns)
	register struct ne_softc *ns;
{
	int len,i;

	ns->ns_if.if_ipackets++;
	len = ns->ns_ph.pr_sz0 + (ns->ns_ph.pr_sz1<<8);
	if(len < ETHER_MIN_LEN || len > ETHER_MAX_LEN)
		return;

	/* this need not be so torturous - one/two bcopys at most into mbufs */
	nefetch(ns, ns->ns_pb, ns->ns_ba, min(len,DS_PGSIZE-sizeof(ns->ns_ph)));
	if (len > DS_PGSIZE-sizeof(ns->ns_ph)) {
		int l = len - (DS_PGSIZE-sizeof(ns->ns_ph)), b ;
		u_char *p = ns->ns_pb + (DS_PGSIZE-sizeof(ns->ns_ph));

		if (++ns->ns_cur >= ns->ns_rxend/DS_PGSIZE)
			ns->ns_cur = (ns->ns_txstart+PKTSZ)/DS_PGSIZE;
		b = ns->ns_cur*DS_PGSIZE;
		
		while (l >= DS_PGSIZE) {
			nefetch(ns, p, b, DS_PGSIZE);
			p += DS_PGSIZE; l -= DS_PGSIZE;
			if (++ns->ns_cur >= ns->ns_rxend/DS_PGSIZE)
				ns->ns_cur = (ns->ns_txstart+PKTSZ)/DS_PGSIZE;
			b = ns->ns_cur*DS_PGSIZE;
		}
		if (l > 0)
			nefetch(ns, p, b, l);
	}
	/* don't forget checksum! */
	len -= sizeof(long) + sizeof(struct ether_header);
			
	neread(ns,(caddr_t)(ns->ns_pb), len);
}

/*
 * Pass a packet to the higher levels.
 * We deal with the trailer protocol here.
 */
neread(ns, buf, len)
	register struct ne_softc *ns;
	char *buf;
	int len;
{
	register struct ether_header *eh;
    	struct mbuf *m;
	int off, resid;
	register struct ifqueue *inq;
	u_short etype;

	/*
	 * Deal with trailer protocol: if type is trailer type
	 * get true type from first 16-bit word past data.
	 * Remember that type was trailer by setting off.
	 */
	eh = (struct ether_header *)buf;
	etype = ntohs((u_short)eh->ether_type);
#define	nedataaddr(eh, off, type)	((type)(((caddr_t)((eh)+1)+(off))))
	if (etype >= ETHERTYPE_TRAIL &&
	    etype < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) {
		off = (etype - ETHERTYPE_TRAIL) * 512;
		if (off >= ETHERMTU) return;		/* sanity */
		eh->ether_type = *nedataaddr(eh, off, u_short *);
		resid = ntohs(*nedataaddr(eh, off+2, u_short *));
		if (off + resid > len) return;		/* sanity */
		len = off + resid;
	} else	off = 0;

	if (len <= 0) return;

	/*
	 * Pull packet off interface.  Off is nonzero if packet
	 * has trailing header; neget 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 = neget(buf, len, off, &ns->ns_if);
	if (m == 0) return;

#if NBPFILTER > 0
	/*
	 * Check if there's a bpf filter listening on this interface.
	 * If so, hand off the raw packet to bpf. 
	 */
	if (ns->ns_bpf) {
		bpf_mtap(ns->ns_bpf, m);
	}

	/*
	 * Note that the interface cannot be in promiscuous mode if
	 * there are no bpf listeners.  And if we are in promiscuous
	 * mode, we have to check if this packet is really ours.
	 *
	 * XXX This test does not support multicasts.
	 */
	if ((ns->ns_if.if_flags & IFF_PROMISC) &&
		bcmp(eh->ether_dhost, ns->ns_addrp,
			sizeof(eh->ether_dhost)) != 0 &&
		bcmp(eh->ether_dhost, etherbroadcastaddr,
			sizeof(eh->ether_dhost)) != 0) {

		m_freem(m);
		return;
	}
#endif

	/*
	 * Drop packet header and save fixed up ether_type.
	 */
#if NBPFILTER > 0
	m_adj(m, sizeof(struct ether_header));
#endif
	eh->ether_type = ntohs(eh->ether_type);

	ether_input(&ns->ns_if, eh, m);
}

/*
 * Supporting routines
 */

/*
 * 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.  When full cluster sized units are present
 * we copy into clusters.
 */
struct mbuf *
neget(buf, totlen, off, ifp)
	caddr_t buf;
	int totlen, off;
	struct ifnet *ifp;
{
	struct mbuf *top, *last, *m;
	int len;
	register caddr_t cp;

	MGETHDR(m, M_DONTWAIT, MT_DATA);
	if (m == 0)
		return (0);
	top = last = m;

#if NBPFILTER > 0
	/*
	 * Copy the ethernet header first.
	 * XXX: This assumes that ether_header fits inside the mbuf.
	 */
	/*
	 * XXX: This line is to avoid `nfs_rcv odd length!' spewage.
	 */
	m->m_data += (MHLEN - sizeof(struct ether_header)) & 3;
	bcopy(buf, mtod(m, caddr_t), sizeof(struct ether_header));
        m->m_len = sizeof(struct ether_header);
	m->m_pkthdr.len = totlen + sizeof(struct ether_header);
#else
	m->m_len = 0;
	m->m_pkthdr.len = totlen;
#endif
	m->m_pkthdr.rcvif = ifp;

	buf += sizeof(struct ether_header);
	cp = buf;

	if (off) {
		cp += off + 2 * sizeof(u_short);
		totlen -= off + 2 * sizeof(u_short);
	}

    copy:
	while (totlen > 0) {
		len = min(totlen, M_TRAILINGSPACE(m));
		if (!len) {
			MGET(m, M_DONTWAIT, MT_DATA);
			if (m == 0) {
				m_freem(top);
				return (0);
			}
			if (totlen >= MINCLSIZE) {
				MCLGET(m, M_DONTWAIT);
				if (m == 0) {
					m_freem(top);
					return (0);
				}
			}
			m->m_len = 0;
			last->m_next = m;
			last = m;
			len = min(totlen, M_TRAILINGSPACE(m));
		}

		bcopy(cp, mtod(m, caddr_t) + m->m_len, len);
		m->m_len += len;
		totlen -= len;
		cp += len;
	}

	if (off) {
		totlen = off;
		cp = buf;
		off = 0;
		goto copy;
	}

	return (top);
}

/*
 * Process an ioctl request.
 */
neioctl(ifp, cmd, data)
	register struct ifnet *ifp;
	int cmd;
	caddr_t data;
{
	register struct ifaddr *ifa = (struct ifaddr *)data;
	struct ne_softc *ns = &ne_softc[ifp->if_unit];
	struct ifreq *ifr = (struct ifreq *)data;
	int s = splimp(), error = 0;


	switch (cmd) {

	case SIOCSIFADDR:
		ifp->if_flags |= IFF_UP;

		switch (ifa->ifa_addr->sa_family) {
#ifdef INET
		case AF_INET:
			neinit(ifp->if_unit);	/* before arpwhohas */
			((struct arpcom *)ifp)->ac_ipaddr =
				IA_SIN(ifa)->sin_addr;
			arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr);
			break;
#endif
#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 *)(ns->ns_addrp);
			else {
				/* 
				 * The manual says we can't change the address 
				 * while the receiver is armed,
				 * so reset everything
				 */
				ifp->if_flags &= ~IFF_RUNNING; 
				bcopy((caddr_t)ina->x_host.c_host,
				    (caddr_t)ns->ns_addrp, sizeof(ns->ns_addrp));
			}
			neinit(ifp->if_unit); /* does ne_setaddr() */
			break;
		    }
#endif
		default:
			neinit(ifp->if_unit);
			break;
		}
		break;

	case SIOCSIFFLAGS:
		if ((ifp->if_flags & IFF_UP) == 0 &&
		    ifp->if_flags & IFF_RUNNING) {
			ifp->if_flags &= ~IFF_RUNNING;
			outb(ns->ns_port + ds_cmd, DSCM_STOP|DSCM_NODMA);
		} else if (ifp->if_flags & IFF_UP &&
		    (ifp->if_flags & IFF_RUNNING) == 0) {
			neinit(ifp->if_unit);
			break;
		}
#if NBPFILTER > 0
		if (ns->ns_if.if_flags & IFF_PROMISC)
			outb (ns->ns_port+ds0_rcr, DSRC_AB | DSRC_PRO |
			      DSRC_AR | DSRC_SEP);
		else
#endif
			outb (ns->ns_port+ds0_rcr, DSRC_AB);
		break;

#ifdef notdef
	case SIOCGHWADDR:
		bcopy((caddr_t)ns->ns_addr, (caddr_t) &ifr->ifr_data,
			sizeof(ns->ns_addr));
		break;
#endif

	default:
		error = EINVAL;
	}
	splx(s);
	return (error);
}
#endif