xref: /csrg-svn/sys/i386/isa/if_ne.c (revision 63364)
149596Swilliam /*-
249827Sbostic  * Copyright (c) 1990, 1991 William F. Jolitz.
3*63364Sbostic  * Copyright (c) 1990, 1993
4*63364Sbostic  *	The Regents of the University of California.  All rights reserved.
545537Sbill  *
649596Swilliam  * %sccs.include.redist.c%
749596Swilliam  *
8*63364Sbostic  *	@(#)if_ne.c	8.1 (Berkeley) 06/11/93
949827Sbostic  */
1049827Sbostic 
1149827Sbostic /*
1249827Sbostic  * NE2000 Ethernet driver
1349596Swilliam  *
1445537Sbill  * Parts inspired from Tim Tucker's if_wd driver for the wd8003,
1545537Sbill  * insight on the ne2000 gained from Robert Clements PC/FTP driver.
1645537Sbill  */
1745537Sbill 
1849827Sbostic #include "ne.h"
1949827Sbostic #if NNE > 0
2049827Sbostic 
2156513Sbostic #include <sys/param.h>
2256513Sbostic #include <sys/systm.h>
2356513Sbostic #include <sys/mbuf.h>
2456513Sbostic #include <sys/buf.h>
2556513Sbostic #include <sys/protosw.h>
2656513Sbostic #include <sys/socket.h>
2756513Sbostic #include <sys/ioctl.h>
2856513Sbostic #include <sys/errno.h>
2956513Sbostic #include <sys/syslog.h>
3045537Sbill 
3156513Sbostic #include <net/if.h>
3256513Sbostic #include <net/netisr.h>
3356513Sbostic #include <net/route.h>
3445537Sbill 
3545537Sbill #ifdef INET
3656513Sbostic #include <netinet/in.h>
3756513Sbostic #include <netinet/in_systm.h>
3856513Sbostic #include <netinet/in_var.h>
3956513Sbostic #include <netinet/ip.h>
4056513Sbostic #include <netinet/if_ether.h>
4145537Sbill #endif
4245537Sbill 
4345537Sbill #ifdef NS
4456513Sbostic #include <netns/ns.h>
4556513Sbostic #include <netns/ns_if.h>
4645537Sbill #endif
4745537Sbill 
4856513Sbostic #include <i386/isa/isa_device.h>
4956513Sbostic #include <i386/isa/if_nereg.h>
5056513Sbostic #include <i386/isa/icu.h>
5145537Sbill 
5245537Sbill int	neprobe(), neattach(), neintr();
5349596Swilliam int	nestart(),neinit(), ether_output(), neioctl();
5445537Sbill 
5545538Sbill struct	isa_driver nedriver = {
5645537Sbill 	neprobe, neattach, "ne",
5745537Sbill };
5845537Sbill 
5945537Sbill struct	mbuf *neget();
6045537Sbill 
6149700Swilliam #define ETHER_MIN_LEN 64
6249700Swilliam #define ETHER_MAX_LEN 1536
6349700Swilliam 
6445537Sbill /*
6545537Sbill  * Ethernet software status per interface.
6645537Sbill  *
6745537Sbill  * Each interface is referenced by a network interface structure,
6845537Sbill  * ns_if, which the routing code uses to locate the interface.
6945537Sbill  * This structure contains the output queue for the interface, its address, ...
7045537Sbill  */
7145537Sbill struct	ne_softc {
7245537Sbill 	struct	arpcom ns_ac;		/* Ethernet common part */
7345537Sbill #define	ns_if	ns_ac.ac_if		/* network-visible interface */
7445537Sbill #define	ns_addr	ns_ac.ac_enaddr		/* hardware Ethernet address */
7545537Sbill 	int	ns_flags;
7645537Sbill #define	DSF_LOCK	1		/* block re-entering enstart */
7745537Sbill 	int	ns_oactive ;
7845537Sbill 	int	ns_mask ;
7945537Sbill 	int	ns_ba;			/* byte addr in buffer ram of inc pkt */
8045537Sbill 	int	ns_cur;			/* current page being filled */
8145537Sbill 	struct	prhdr	ns_ph;		/* hardware header of incoming packet*/
8245537Sbill 	struct	ether_header ns_eh;	/* header of incoming packet */
8345537Sbill 	u_char	ns_pb[2048 /*ETHERMTU+sizeof(long)*/];
8445537Sbill } ne_softc[NNE] ;
8549699Swilliam #define	ENBUFSIZE	(sizeof(struct ether_header) + ETHERMTU + 2 + ETHER_MIN_LEN)
8645537Sbill 
8745537Sbill int nec;
8845537Sbill 
8945537Sbill u_short boarddata[16];
9045537Sbill 
9145537Sbill neprobe(dvp)
9245538Sbill 	struct isa_device *dvp;
9345537Sbill {
9445537Sbill 	int val,i,s;
9545537Sbill 	register struct ne_softc *ns = &ne_softc[0];
9645537Sbill 
9745537Sbill #ifdef lint
9845537Sbill 	neintr(0);
9945537Sbill #endif
10045537Sbill 
10145538Sbill 	nec = dvp->id_iobase;
10245537Sbill 	s = splimp();
10345537Sbill 
10449699Swilliam 	/* Reset the bastard */
10545537Sbill 	val = inb(nec+ne_reset);
10645537Sbill 	DELAY(2000000);
10745537Sbill 	outb(nec+ne_reset,val);
10845537Sbill 
10945537Sbill 	outb(nec+ds_cmd, DSCM_STOP|DSCM_NODMA);
11045538Sbill 
11145538Sbill 	i = 1000000;
11249596Swilliam 	while ((inb(nec+ds0_isr)&DSIS_RESET) == 0 && i-- > 0);
11345538Sbill 	if (i < 0) return (0);
11445538Sbill 
11545537Sbill 	outb(nec+ds0_isr, 0xff);
11645537Sbill 
11745537Sbill 	/* Word Transfers, Burst Mode Select, Fifo at 8 bytes */
11845537Sbill 	outb(nec+ds0_dcr, DSDC_WTS|DSDC_BMS|DSDC_FT1);
11945537Sbill 
12045537Sbill 	outb(nec+ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_STOP);
12145538Sbill 	DELAY(10000);
12245538Sbill 
12349699Swilliam 	/* Check cmd reg and fail if not right */
12445538Sbill 	if ((i=inb(nec+ds_cmd)) != (DSCM_NODMA|DSCM_PG0|DSCM_STOP))
12545538Sbill 		return(0);
12645538Sbill 
12745537Sbill 	outb(nec+ds0_tcr, 0);
12845537Sbill 	outb(nec+ds0_rcr, DSRC_MON);
12945537Sbill 	outb(nec+ds0_pstart, RBUF/DS_PGSIZE);
13045537Sbill 	outb(nec+ds0_pstop, RBUFEND/DS_PGSIZE);
13145537Sbill 	outb(nec+ds0_bnry, RBUFEND/DS_PGSIZE);
13245537Sbill 	outb(nec+ds0_imr, 0);
13345537Sbill 	outb(nec+ds0_isr, 0);
13445537Sbill 	outb(nec+ds_cmd, DSCM_NODMA|DSCM_PG1|DSCM_STOP);
13545537Sbill 	outb(nec+ds1_curr, RBUF/DS_PGSIZE);
13645537Sbill 	outb(nec+ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_STOP);
13749699Swilliam 
13845538Sbill #ifdef NEDEBUG
13949699Swilliam #define	PAT(n)	(0xa55a + 37*(n))
14049699Swilliam #define	RCON	37
14149699Swilliam 	{	int i, rom, pat;
14249699Swilliam 
14349699Swilliam 		rom=1;
14449699Swilliam 		printf("ne ram ");
14549699Swilliam 
14649699Swilliam 		for (i = 0; i < 0xfff0; i+=4) {
14749699Swilliam 			pat = PAT(i);
14849699Swilliam 			neput(&pat,i,4);
14949699Swilliam 			nefetch(&pat,i,4);
15049699Swilliam 			if (pat == PAT(i)) {
15149699Swilliam 				if (rom) {
15249699Swilliam 					rom=0;
15349699Swilliam 					printf(" %x", i);
15449699Swilliam 				}
15549699Swilliam 			} else {
15649699Swilliam 				if (!rom) {
15749699Swilliam 					rom=1;
15849699Swilliam 					printf("..%x ", i);
15949699Swilliam 				}
16049699Swilliam 			}
16149699Swilliam 			pat=0;
16249699Swilliam 			neput(&pat,i,4);
16345537Sbill 		}
16449699Swilliam 		printf("\n");
16545537Sbill 	}
16645538Sbill #endif
16749699Swilliam 
16849699Swilliam 	/* Extract board address */
16949699Swilliam 	nefetch ((caddr_t)boarddata, 0, sizeof(boarddata));
17045537Sbill 	for(i=0; i < 6; i++)  ns->ns_addr[i] = boarddata[i];
17145537Sbill 	splx(s);
17245537Sbill 	return (1);
17345537Sbill }
17445537Sbill 
17549699Swilliam /*
17649699Swilliam  * Fetch from onboard ROM/RAM
17749699Swilliam  */
nefetch(up,ad,len)17849699Swilliam nefetch (up, ad, len) caddr_t up; {
17945537Sbill 	u_char cmd;
18045537Sbill 
18145537Sbill 	cmd = inb(nec+ds_cmd);
18249699Swilliam 	outb (nec+ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_START);
18349699Swilliam 
18449699Swilliam 	/* Setup remote dma */
18545537Sbill 	outb (nec+ds0_isr, DSIS_RDC);
18649699Swilliam 	outb (nec+ds0_rbcr0, len);
18749699Swilliam 	outb (nec+ds0_rbcr1, len>>8);
18849699Swilliam 	outb (nec+ds0_rsar0, ad);
18949699Swilliam 	outb (nec+ds0_rsar1, ad>>8);
19049699Swilliam 
19149699Swilliam 	/* Execute & extract from card */
19245537Sbill 	outb (nec+ds_cmd, DSCM_RREAD|DSCM_PG0|DSCM_START);
19345537Sbill 	insw (nec+ne_data, up, len/2);
19449699Swilliam 
19549699Swilliam 	/* Wait till done, then shutdown feature */
19649699Swilliam 	while ((inb (nec+ds0_isr) & DSIS_RDC) == 0) ;
19745537Sbill 	outb (nec+ds0_isr, DSIS_RDC);
19845537Sbill 	outb (nec+ds_cmd, cmd);
19945537Sbill }
20045537Sbill 
20149699Swilliam /*
20249699Swilliam  * Put to onboard RAM
20349699Swilliam  */
neput(up,ad,len)20449699Swilliam neput (up, ad, len) caddr_t up; {
20545537Sbill 	u_char cmd;
20645537Sbill 
20745537Sbill 	cmd = inb(nec+ds_cmd);
20845537Sbill 	outb (nec+ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_START);
20949699Swilliam 
21049699Swilliam 	/* Setup for remote dma */
21145537Sbill 	outb (nec+ds0_isr, DSIS_RDC);
21249699Swilliam 	if(len&1) len++;		/* roundup to words */
21349699Swilliam 	outb (nec+ds0_rbcr0, len);
21449699Swilliam 	outb (nec+ds0_rbcr1, len>>8);
21549699Swilliam 	outb (nec+ds0_rsar0, ad);
21649699Swilliam 	outb (nec+ds0_rsar1, ad>>8);
21745537Sbill 
21849699Swilliam 	/* Execute & stuff to card */
21945537Sbill 	outb (nec+ds_cmd, DSCM_RWRITE|DSCM_PG0|DSCM_START);
22045537Sbill 	outsw (nec+ne_data, up, len/2);
22149699Swilliam 
22249699Swilliam 	/* Wait till done, then shutdown feature */
22349699Swilliam 	while ((inb (nec+ds0_isr) & DSIS_RDC) == 0) ;
22445537Sbill 	outb (nec+ds0_isr, DSIS_RDC);
22545537Sbill 	outb (nec+ds_cmd, cmd);
22645537Sbill }
22745537Sbill 
22845537Sbill /*
22945537Sbill  * Reset of interface.
23045537Sbill  */
nereset(unit,uban)23145537Sbill nereset(unit, uban)
23245537Sbill 	int unit, uban;
23345537Sbill {
23445537Sbill 	if (unit >= NNE)
23545537Sbill 		return;
23645537Sbill 	printf("ne%d: reset\n", unit);
23745537Sbill 	ne_softc[unit].ns_flags &= ~DSF_LOCK;
23845537Sbill 	neinit(unit);
23945537Sbill }
24045537Sbill 
24145537Sbill /*
24245537Sbill  * Interface exists: make available by filling in network interface
24345537Sbill  * record.  System will initialize the interface when it is ready
24445537Sbill  * to accept packets.  We get the ethernet address here.
24545537Sbill  */
24645537Sbill neattach(dvp)
24745538Sbill 	struct isa_device *dvp;
24845537Sbill {
24945538Sbill 	int unit = dvp->id_unit;
25045537Sbill 	register struct ne_softc *ns = &ne_softc[unit];
25145537Sbill 	register struct ifnet *ifp = &ns->ns_if;
25245537Sbill 
25345537Sbill 	ifp->if_unit = unit;
25445537Sbill 	ifp->if_name = nedriver.name ;
25545537Sbill 	ifp->if_mtu = ETHERMTU;
25645619Sbill 	printf (" ethernet address %s", ether_sprintf(ns->ns_addr)) ;
25749596Swilliam 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS;
25845537Sbill 	ifp->if_init = neinit;
25949596Swilliam 	ifp->if_output = ether_output;
26049596Swilliam 	ifp->if_start = nestart;
26145537Sbill 	ifp->if_ioctl = neioctl;
26245537Sbill 	ifp->if_reset = nereset;
26345537Sbill 	ifp->if_watchdog = 0;
26445537Sbill 	if_attach(ifp);
26545537Sbill }
26645537Sbill 
26745537Sbill /*
26845537Sbill  * Initialization of interface; set up initialization block
26945537Sbill  * and transmit/receive descriptor rings.
27045537Sbill  */
neinit(unit)27145537Sbill neinit(unit)
27245537Sbill 	int unit;
27345537Sbill {
27445537Sbill 	register struct ne_softc *ns = &ne_softc[unit];
27545537Sbill 	struct ifnet *ifp = &ns->ns_if;
27645537Sbill 	int s;
27745537Sbill 	register i; char *cp;
27845537Sbill 
27945537Sbill  	if (ifp->if_addrlist == (struct ifaddr *)0) return;
28045537Sbill 	if (ifp->if_flags & IFF_RUNNING) return;
28145537Sbill 
28245537Sbill 	s = splimp();
28345537Sbill 
28445537Sbill 	/* set physical address on ethernet */
28545537Sbill 	outb (nec+ds_cmd, DSCM_NODMA|DSCM_PG1|DSCM_STOP);
28645537Sbill 	for (i=0 ; i < 6 ; i++) outb(nec+ds1_par0+i,ns->ns_addr[i]);
28745537Sbill 
28845537Sbill 	/* clr logical address hash filter for now */
28945537Sbill 	for (i=0 ; i < 8 ; i++) outb(nec+ds1_mar0+i,0xff);
29045537Sbill 
29145537Sbill 	/* init regs */
29245537Sbill 	outb (nec+ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_STOP);
29345537Sbill 	outb (nec+ds0_rbcr0, 0);
29445537Sbill 	outb (nec+ds0_rbcr1, 0);
29545537Sbill 	outb (nec+ds0_imr, 0);
29645537Sbill 	outb (nec+ds0_isr, 0xff);
29749699Swilliam 
29845537Sbill 	/* Word Transfers, Burst Mode Select, Fifo at 8 bytes */
29945537Sbill 	outb(nec+ds0_dcr, DSDC_WTS|DSDC_BMS|DSDC_FT1);
30045537Sbill 	outb(nec+ds0_tcr, 0);
30145537Sbill 	outb (nec+ds0_rcr, DSRC_MON);
30245537Sbill 	outb (nec+ds0_tpsr, 0);
30345537Sbill 	outb(nec+ds0_pstart, RBUF/DS_PGSIZE);
30445537Sbill 	outb(nec+ds0_pstop, RBUFEND/DS_PGSIZE);
30545558Sbill 	outb(nec+ds0_bnry, RBUF/DS_PGSIZE);
30645537Sbill 	outb (nec+ds_cmd, DSCM_NODMA|DSCM_PG1|DSCM_STOP);
30745537Sbill 	outb(nec+ds1_curr, RBUF/DS_PGSIZE);
30845537Sbill 	ns->ns_cur = RBUF/DS_PGSIZE;
30945537Sbill 	outb (nec+ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_START);
31045537Sbill 	outb (nec+ds0_rcr, DSRC_AB);
31145537Sbill 	outb(nec+ds0_dcr, DSDC_WTS|DSDC_BMS|DSDC_FT1);
31245537Sbill 	outb (nec+ds0_imr, 0xff);
31345537Sbill 
31445537Sbill 	ns->ns_if.if_flags |= IFF_RUNNING;
31545537Sbill 	ns->ns_oactive = 0; ns->ns_mask = ~0;
31649596Swilliam 	nestart(ifp);
31745537Sbill 	splx(s);
31845537Sbill }
31945537Sbill 
32045537Sbill /*
32145537Sbill  * Setup output on interface.
32245537Sbill  * Get another datagram to send off of the interface queue,
32345537Sbill  * and map it to the interface before starting the output.
32445537Sbill  * called only at splimp or interrupt level.
32545537Sbill  */
32649596Swilliam nestart(ifp)
32749596Swilliam 	struct ifnet *ifp;
32845537Sbill {
32949596Swilliam 	register struct ne_softc *ns = &ne_softc[ifp->if_unit];
33045537Sbill 	struct mbuf *m0, *m;
33145537Sbill 	int buffer;
33249596Swilliam 	int len = 0, i, total,t;
33345537Sbill 
33445537Sbill 	/*
33545537Sbill 	 * The DS8390 has only one transmit buffer, if it is busy we
33645537Sbill 	 * must wait until the transmit interrupt completes.
33745537Sbill 	 */
33845537Sbill 	outb(nec+ds_cmd,DSCM_NODMA|DSCM_START);
33945537Sbill 
34045538Sbill 	if (ns->ns_flags & DSF_LOCK)
34145538Sbill 		return;
34245538Sbill 
34345537Sbill 	if (inb(nec+ds_cmd) & DSCM_TRANS)
34445537Sbill 		return;
34545537Sbill 
34645537Sbill 	if ((ns->ns_if.if_flags & IFF_RUNNING) == 0)
34745537Sbill 		return;
34845537Sbill 
34945537Sbill 	IF_DEQUEUE(&ns->ns_if.if_snd, m);
35045537Sbill 
35145537Sbill 	if (m == 0)
35245537Sbill 		return;
35345537Sbill 
35445537Sbill 	/*
35545537Sbill 	 * Copy the mbuf chain into the transmit buffer
35645537Sbill 	 */
35745537Sbill 
35845538Sbill 	ns->ns_flags |= DSF_LOCK;	/* prevent entering nestart */
35945537Sbill 	buffer = TBUF; len = i = 0;
36049596Swilliam 	t = 0;
36149699Swilliam 	for (m0 = m; m != 0; m = m->m_next)
36249596Swilliam 		t += m->m_len;
36349596Swilliam 
36449596Swilliam 	m = m0;
36549596Swilliam 	total = t;
36649596Swilliam 	for (m0 = m; m != 0; ) {
36749596Swilliam 
36849596Swilliam 		if (m->m_len&1 && t > m->m_len) {
36949699Swilliam 			neput(mtod(m, caddr_t), buffer, m->m_len - 1);
37049596Swilliam 			t -= m->m_len - 1;
37149596Swilliam 			buffer += m->m_len - 1;
37249596Swilliam 			m->m_data += m->m_len - 1;
37349596Swilliam 			m->m_len = 1;
37449596Swilliam 			m = m_pullup(m, 2);
37549596Swilliam 		} else {
37649699Swilliam 			neput(mtod(m, caddr_t), buffer, m->m_len);
37749596Swilliam 			buffer += m->m_len;
37849596Swilliam 			t -= m->m_len;
37949596Swilliam 			MFREE(m, m0);
38049596Swilliam 			m = m0;
38149596Swilliam 		}
38245537Sbill 	}
38345537Sbill 
38445537Sbill 	/*
38545537Sbill 	 * Init transmit length registers, and set transmit start flag.
38645537Sbill 	 */
38745537Sbill 
38849596Swilliam 	len = total;
38949699Swilliam 	if (len < ETHER_MIN_LEN) len = ETHER_MIN_LEN;
39045537Sbill 	outb(nec+ds0_tbcr0,len&0xff);
39145537Sbill 	outb(nec+ds0_tbcr1,(len>>8)&0xff);
39245537Sbill 	outb(nec+ds0_tpsr, TBUF/DS_PGSIZE);
39345537Sbill 	outb(nec+ds_cmd, DSCM_TRANS|DSCM_NODMA|DSCM_START);
39445537Sbill }
39545537Sbill 
39649699Swilliam /* buffer successor/predecessor in ring? */
39745558Sbill #define succ(n) (((n)+1 >= RBUFEND/DS_PGSIZE) ? RBUF/DS_PGSIZE : (n)+1)
39845558Sbill #define pred(n) (((n)-1 < RBUF/DS_PGSIZE) ? RBUFEND/DS_PGSIZE-1 : (n)-1)
39949699Swilliam 
40045537Sbill /*
40145537Sbill  * Controller interrupt.
40245537Sbill  */
neintr(unit)40349596Swilliam neintr(unit)
40445537Sbill {
40549596Swilliam 	register struct ne_softc *ns = &ne_softc[unit];
40645537Sbill 	u_char cmd,isr;
40745537Sbill 
40849699Swilliam 	/* Save cmd, clear interrupt */
40945537Sbill 	cmd = inb (nec+ds_cmd);
41045538Sbill loop:
41145537Sbill 	isr = inb (nec+ds0_isr);
41245538Sbill 	outb(nec+ds_cmd,DSCM_NODMA|DSCM_START);
41345558Sbill 	outb(nec+ds0_isr, isr);
41445537Sbill 
41549699Swilliam 	/* Receiver error */
41645537Sbill 	if (isr & DSIS_RXE) {
41745537Sbill 		/* need to read these registers to clear status */
41845537Sbill 		(void) inb(nec+ ds0_rsr);
41945537Sbill 		(void) inb(nec+ 0xD);
42045537Sbill 		(void) inb(nec + 0xE);
42145537Sbill 		(void) inb(nec + 0xF);
42245537Sbill 		ns->ns_if.if_ierrors++;
42345537Sbill 	}
42445537Sbill 
42549699Swilliam 	/* We received something; rummage thru tiny ring buffer */
42645558Sbill 	if (isr & (DSIS_RX|DSIS_RXE|DSIS_ROVRN)) {
42745537Sbill 		u_char pend,lastfree;
42845537Sbill 
42945537Sbill 		outb(nec+ds_cmd, DSCM_START|DSCM_NODMA|DSCM_PG1);
43045537Sbill 		pend = inb(nec+ds1_curr);
43145537Sbill 		outb(nec+ds_cmd, DSCM_START|DSCM_NODMA|DSCM_PG0);
43245537Sbill 		lastfree = inb(nec+ds0_bnry);
43349699Swilliam 
43449699Swilliam 		/* Have we wrapped? */
43545558Sbill 		if (lastfree >= RBUFEND/DS_PGSIZE)
43645537Sbill 			lastfree = RBUF/DS_PGSIZE;
43745558Sbill 		if (pend < lastfree && ns->ns_cur < pend)
43845558Sbill 			lastfree = ns->ns_cur;
43945558Sbill 		else	if (ns->ns_cur > lastfree)
44045558Sbill 			lastfree = ns->ns_cur;
44145558Sbill 
44249699Swilliam 		/* Something in the buffer? */
44345558Sbill 		while (pend != lastfree) {
44445537Sbill 			u_char nxt;
44545537Sbill 
44649699Swilliam 			/* Extract header from microcephalic board */
44749699Swilliam 			nefetch(&ns->ns_ph,lastfree*DS_PGSIZE,
44845558Sbill 				sizeof(ns->ns_ph));
44945558Sbill 			ns->ns_ba = lastfree*DS_PGSIZE+sizeof(ns->ns_ph);
45045537Sbill 
45149699Swilliam 			/* Incipient paranoia */
45245538Sbill 			if (ns->ns_ph.pr_status == DSRS_RPC ||
45349699Swilliam 				/* for dequna's */
45445538Sbill 				ns->ns_ph.pr_status == 0x21)
45545537Sbill 				nerecv (ns);
45645538Sbill #ifdef NEDEBUG
45745538Sbill 			else  {
45849699Swilliam 				printf("cur %x pnd %x lfr %x ",
45949699Swilliam 					ns->ns_cur, pend, lastfree);
46049699Swilliam 				printf("nxt %x len %x ", ns->ns_ph.pr_nxtpg,
46149699Swilliam 					(ns->ns_ph.pr_sz1<<8)+ ns->ns_ph.pr_sz0);
46249699Swilliam 				printf("Bogus Sts %x\n", ns->ns_ph.pr_status);
46345538Sbill 			}
46445538Sbill #endif
46545537Sbill 
46645537Sbill 			nxt = ns->ns_ph.pr_nxtpg ;
46749699Swilliam 
46849699Swilliam 			/* Sanity check */
46949699Swilliam 			if ( nxt >= RBUF/DS_PGSIZE && nxt <= RBUFEND/DS_PGSIZE
47049699Swilliam 				&& nxt <= pend)
47145537Sbill 				ns->ns_cur = nxt;
47245537Sbill 			else	ns->ns_cur = nxt = pend;
47349699Swilliam 
47449699Swilliam 			/* Set the boundaries */
47545558Sbill 			lastfree = nxt;
47645558Sbill 			outb(nec+ds0_bnry, pred(nxt));
47745558Sbill 			outb(nec+ds_cmd, DSCM_START|DSCM_NODMA|DSCM_PG1);
47845538Sbill 			pend = inb(nec+ds1_curr);
47945558Sbill 			outb(nec+ds_cmd, DSCM_START|DSCM_NODMA|DSCM_PG0);
48049699Swilliam 		}
48145537Sbill 		outb(nec+ds_cmd, DSCM_START|DSCM_NODMA);
48245537Sbill 	}
48349699Swilliam 
48449699Swilliam 	/* Transmit error */
48545537Sbill 	if (isr & DSIS_TXE) {
48645558Sbill 		ns->ns_flags &= ~DSF_LOCK;
48749699Swilliam 		/* Need to read these registers to clear status */
48845537Sbill 		ns->ns_if.if_collisions += inb(nec+ds0_tbcr0);
48945537Sbill 		ns->ns_if.if_oerrors++;
49045537Sbill 	}
49149699Swilliam 
49249699Swilliam 	/* Packet Transmitted */
49345538Sbill 	if (isr & DSIS_TX) {
49445558Sbill 		ns->ns_flags &= ~DSF_LOCK;
49545537Sbill 		++ns->ns_if.if_opackets;
49645558Sbill 		ns->ns_if.if_collisions += inb(nec+ds0_tbcr0);
49745537Sbill 	}
49845538Sbill 
49949699Swilliam 	/* Receiver ovverun? */
50045558Sbill 	if (isr & DSIS_ROVRN) {
50149699Swilliam 		log(LOG_ERR, "ne%d: error: isr %x\n", ns-ne_softc, isr
50249699Swilliam 			/*, DSIS_BITS*/);
50345558Sbill 		outb(nec+ds0_rbcr0, 0);
50445558Sbill 		outb(nec+ds0_rbcr1, 0);
50545558Sbill 		outb(nec+ds0_tcr, DSTC_LB0);
50645558Sbill 		outb(nec+ds0_rcr, DSRC_MON);
50745558Sbill 		outb(nec+ds_cmd, DSCM_START|DSCM_NODMA);
50845558Sbill 		outb(nec+ds0_rcr, DSRC_AB);
50945558Sbill 		outb(nec+ds0_tcr, 0);
51045558Sbill 	}
51145558Sbill 
51249699Swilliam 	/* Any more to send? */
51345537Sbill 	outb (nec+ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_START);
51449596Swilliam 	nestart(&ns->ns_if);
51545538Sbill 	outb (nec+ds_cmd, cmd);
51645537Sbill 	outb (nec+ds0_imr, 0xff);
51749699Swilliam 
51849699Swilliam 	/* Still more to do? */
51945538Sbill 	isr = inb (nec+ds0_isr);
52045538Sbill 	if(isr) goto loop;
52145537Sbill }
52245537Sbill 
52345537Sbill /*
52445537Sbill  * Ethernet interface receiver interface.
52545537Sbill  * If input error just drop packet.
52645537Sbill  * Otherwise examine packet to determine type.  If can't determine length
52745537Sbill  * from type, then have to drop packet.  Othewise decapsulate
52845537Sbill  * packet based on type and pass to type specific higher-level
52945537Sbill  * input routine.
53045537Sbill  */
nerecv(ns)53145537Sbill nerecv(ns)
53245537Sbill 	register struct ne_softc *ns;
53345537Sbill {
53445537Sbill 	int len,i;
53545537Sbill 
53645537Sbill 	ns->ns_if.if_ipackets++;
53745537Sbill 	len = ns->ns_ph.pr_sz0 + (ns->ns_ph.pr_sz1<<8);
53849699Swilliam 	if(len < ETHER_MIN_LEN || len > ETHER_MAX_LEN)
53949699Swilliam 		return;
54049699Swilliam 
54149699Swilliam 	/* this need not be so torturous - one/two bcopys at most into mbufs */
54249699Swilliam 	nefetch(ns->ns_pb, ns->ns_ba, min(len,DS_PGSIZE-sizeof(ns->ns_ph)));
54349699Swilliam 	if (len > DS_PGSIZE-sizeof(ns->ns_ph)) {
54445537Sbill 		int l = len - (DS_PGSIZE-sizeof(ns->ns_ph)), b ;
54545537Sbill 		u_char *p = ns->ns_pb + (DS_PGSIZE-sizeof(ns->ns_ph));
54645537Sbill 
54745537Sbill 		if(++ns->ns_cur > 0x7f) ns->ns_cur = 0x46;
54845537Sbill 		b = ns->ns_cur*DS_PGSIZE;
54945537Sbill 
55045537Sbill 		while (l >= DS_PGSIZE) {
55149699Swilliam 			nefetch(p, b, DS_PGSIZE);
55245537Sbill 			p += DS_PGSIZE; l -= DS_PGSIZE;
55349699Swilliam 			if(++ns->ns_cur > 0x7f) ns->ns_cur = 0x46;
55449699Swilliam 			b = ns->ns_cur*DS_PGSIZE;
55545537Sbill 		}
55645537Sbill 		if (l > 0)
55749699Swilliam 			nefetch(p, b, l);
55845537Sbill 	}
55949699Swilliam 	/* don't forget checksum! */
56049699Swilliam 	len -= sizeof(struct ether_header) + sizeof(long);
56145537Sbill 
56245537Sbill 	neread(ns,(caddr_t)(ns->ns_pb), len);
56345537Sbill }
56445537Sbill 
56545537Sbill /*
56645537Sbill  * Pass a packet to the higher levels.
56745537Sbill  * We deal with the trailer protocol here.
56845537Sbill  */
neread(ns,buf,len)56945537Sbill neread(ns, buf, len)
57045537Sbill 	register struct ne_softc *ns;
57145537Sbill 	char *buf;
57245537Sbill 	int len;
57345537Sbill {
57445537Sbill 	register struct ether_header *eh;
57545537Sbill     	struct mbuf *m;
57645537Sbill 	int off, resid;
57745537Sbill 	register struct ifqueue *inq;
57845537Sbill 
57945537Sbill 	/*
58045537Sbill 	 * Deal with trailer protocol: if type is trailer type
58145537Sbill 	 * get true type from first 16-bit word past data.
58245537Sbill 	 * Remember that type was trailer by setting off.
58345537Sbill 	 */
58445537Sbill 	eh = (struct ether_header *)buf;
58545537Sbill 	eh->ether_type = ntohs((u_short)eh->ether_type);
58645537Sbill #define	nedataaddr(eh, off, type)	((type)(((caddr_t)((eh)+1)+(off))))
58745537Sbill 	if (eh->ether_type >= ETHERTYPE_TRAIL &&
58845537Sbill 	    eh->ether_type < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) {
58945537Sbill 		off = (eh->ether_type - ETHERTYPE_TRAIL) * 512;
59045537Sbill 		if (off >= ETHERMTU) return;		/* sanity */
59145537Sbill 		eh->ether_type = ntohs(*nedataaddr(eh, off, u_short *));
59245537Sbill 		resid = ntohs(*(nedataaddr(eh, off+2, u_short *)));
59345537Sbill 		if (off + resid > len) return;		/* sanity */
59445537Sbill 		len = off + resid;
59545537Sbill 	} else	off = 0;
59645537Sbill 
59745537Sbill 	if (len == 0) return;
59845537Sbill 
59945537Sbill 	/*
60045537Sbill 	 * Pull packet off interface.  Off is nonzero if packet
60145537Sbill 	 * has trailing header; neget will then force this header
60245537Sbill 	 * information to be at the front, but we still have to drop
60345537Sbill 	 * the type and length which are at the front of any trailer data.
60445537Sbill 	 */
60545537Sbill 	m = neget(buf, len, off, &ns->ns_if);
60645537Sbill 	if (m == 0) return;
60745537Sbill 
60849596Swilliam 	ether_input(&ns->ns_if, eh, m);
60945537Sbill }
61045537Sbill 
61145537Sbill /*
61245537Sbill  * Supporting routines
61345537Sbill  */
61445537Sbill 
61545537Sbill /*
61645537Sbill  * Pull read data off a interface.
61745537Sbill  * Len is length of data, with local net header stripped.
61845537Sbill  * Off is non-zero if a trailer protocol was used, and
61945537Sbill  * gives the offset of the trailer information.
62045537Sbill  * We copy the trailer information and then all the normal
62145537Sbill  * data into mbufs.  When full cluster sized units are present
62245537Sbill  * we copy into clusters.
62345537Sbill  */
62445537Sbill struct mbuf *
neget(buf,totlen,off0,ifp)62545537Sbill neget(buf, totlen, off0, ifp)
62645537Sbill 	caddr_t buf;
62745537Sbill 	int totlen, off0;
62845537Sbill 	struct ifnet *ifp;
62945537Sbill {
63045537Sbill 	struct mbuf *top, **mp, *m, *p;
63145537Sbill 	int off = off0, len;
63245537Sbill 	register caddr_t cp = buf;
63349596Swilliam 	char *epkt;
63445537Sbill 
63549596Swilliam 	buf += sizeof(struct ether_header);
63649596Swilliam 	cp = buf;
63749596Swilliam 	epkt = cp + totlen;
63849596Swilliam 
63949596Swilliam 
64049596Swilliam 	if (off) {
64149596Swilliam 		cp += off + 2 * sizeof(u_short);
64249596Swilliam 		totlen -= 2 * sizeof(u_short);
64349596Swilliam 	}
64449596Swilliam 
64549596Swilliam 	MGETHDR(m, M_DONTWAIT, MT_DATA);
64649596Swilliam 	if (m == 0)
64749596Swilliam 		return (0);
64849596Swilliam 	m->m_pkthdr.rcvif = ifp;
64949596Swilliam 	m->m_pkthdr.len = totlen;
65049596Swilliam 	m->m_len = MHLEN;
65149596Swilliam 
65245537Sbill 	top = 0;
65345537Sbill 	mp = &top;
65445537Sbill 	while (totlen > 0) {
65549596Swilliam 		if (top) {
65649596Swilliam 			MGET(m, M_DONTWAIT, MT_DATA);
65749596Swilliam 			if (m == 0) {
65849596Swilliam 				m_freem(top);
65949596Swilliam 				return (0);
66049596Swilliam 			}
66149596Swilliam 			m->m_len = MLEN;
66249596Swilliam 		}
66349596Swilliam 		len = min(totlen, epkt - cp);
66449596Swilliam 		if (len >= MINCLSIZE) {
66549596Swilliam 			MCLGET(m, M_DONTWAIT);
66649596Swilliam 			if (m->m_flags & M_EXT)
66749596Swilliam 				m->m_len = len = min(len, MCLBYTES);
66845537Sbill 			else
66949596Swilliam 				len = m->m_len;
67045537Sbill 		} else {
67145537Sbill 			/*
67249596Swilliam 			 * Place initial small packet/header at end of mbuf.
67345537Sbill 			 */
67449596Swilliam 			if (len < m->m_len) {
67549596Swilliam 				if (top == 0 && len + max_linkhdr <= m->m_len)
67649596Swilliam 					m->m_data += max_linkhdr;
67749596Swilliam 				m->m_len = len;
67849596Swilliam 			} else
67949596Swilliam 				len = m->m_len;
68045537Sbill 		}
68149596Swilliam 		bcopy(cp, mtod(m, caddr_t), (unsigned)len);
68249596Swilliam 		cp += len;
68345537Sbill 		*mp = m;
68445537Sbill 		mp = &m->m_next;
68549596Swilliam 		totlen -= len;
68649596Swilliam 		if (cp == epkt)
68749596Swilliam 			cp = buf;
68845537Sbill 	}
68945537Sbill 	return (top);
69045537Sbill }
69145537Sbill 
69245537Sbill /*
69345537Sbill  * Process an ioctl request.
69445537Sbill  */
neioctl(ifp,cmd,data)69545537Sbill neioctl(ifp, cmd, data)
69645537Sbill 	register struct ifnet *ifp;
69745537Sbill 	int cmd;
69845537Sbill 	caddr_t data;
69945537Sbill {
70045537Sbill 	register struct ifaddr *ifa = (struct ifaddr *)data;
70145537Sbill 	struct ne_softc *ns = &ne_softc[ifp->if_unit];
70245537Sbill 	struct ifreq *ifr = (struct ifreq *)data;
70345537Sbill 	int s = splimp(), error = 0;
70445537Sbill 
70545537Sbill 
70645537Sbill 	switch (cmd) {
70745537Sbill 
70845537Sbill 	case SIOCSIFADDR:
70945537Sbill 		ifp->if_flags |= IFF_UP;
71045537Sbill 
71149596Swilliam 		switch (ifa->ifa_addr->sa_family) {
71245537Sbill #ifdef INET
71345537Sbill 		case AF_INET:
71445537Sbill 			neinit(ifp->if_unit);	/* before arpwhohas */
71545537Sbill 			((struct arpcom *)ifp)->ac_ipaddr =
71645537Sbill 				IA_SIN(ifa)->sin_addr;
71745537Sbill 			arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr);
71845537Sbill 			break;
71945537Sbill #endif
72045537Sbill #ifdef NS
72145537Sbill 		case AF_NS:
72245537Sbill 		    {
72345537Sbill 			register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr);
72445537Sbill 
72545537Sbill 			if (ns_nullhost(*ina))
72645537Sbill 				ina->x_host = *(union ns_host *)(ns->ns_addr);
72745537Sbill 			else {
72845537Sbill 				/*
72945537Sbill 				 * The manual says we can't change the address
73045537Sbill 				 * while the receiver is armed,
73145537Sbill 				 * so reset everything
73245537Sbill 				 */
73345537Sbill 				ifp->if_flags &= ~IFF_RUNNING;
73445537Sbill 				bcopy((caddr_t)ina->x_host.c_host,
73545537Sbill 				    (caddr_t)ns->ns_addr, sizeof(ns->ns_addr));
73645537Sbill 			}
73745537Sbill 			neinit(ifp->if_unit); /* does ne_setaddr() */
73845537Sbill 			break;
73945537Sbill 		    }
74045537Sbill #endif
74145537Sbill 		default:
74245537Sbill 			neinit(ifp->if_unit);
74345537Sbill 			break;
74445537Sbill 		}
74545537Sbill 		break;
74645537Sbill 
74745537Sbill 	case SIOCSIFFLAGS:
74845537Sbill 		if ((ifp->if_flags & IFF_UP) == 0 &&
74945537Sbill 		    ifp->if_flags & IFF_RUNNING) {
75045537Sbill 			ifp->if_flags &= ~IFF_RUNNING;
75145537Sbill 			outb(nec+ds_cmd,DSCM_STOP|DSCM_NODMA);
75245537Sbill 		} else if (ifp->if_flags & IFF_UP &&
75345537Sbill 		    (ifp->if_flags & IFF_RUNNING) == 0)
75445537Sbill 			neinit(ifp->if_unit);
75545537Sbill 		break;
75645537Sbill 
75745537Sbill #ifdef notdef
75845537Sbill 	case SIOCGHWADDR:
75945537Sbill 		bcopy((caddr_t)ns->ns_addr, (caddr_t) &ifr->ifr_data,
76045537Sbill 			sizeof(ns->ns_addr));
76145537Sbill 		break;
76245537Sbill #endif
76345537Sbill 
76445537Sbill 	default:
76545537Sbill 		error = EINVAL;
76645537Sbill 	}
76745537Sbill 	splx(s);
76845537Sbill 	return (error);
76945537Sbill }
77045537Sbill #endif
771