xref: /original-bsd/sys/i386/isa/if_ne.c (revision 2dc74e6c)
1ede20247Swilliam /*-
252079767Sbostic  * Copyright (c) 1990, 1991 William F. Jolitz.
3*2dc74e6cSbostic  * Copyright (c) 1990, 1993
4*2dc74e6cSbostic  *	The Regents of the University of California.  All rights reserved.
5ede20247Swilliam  *
6ede20247Swilliam  * %sccs.include.redist.c%
7ede20247Swilliam  *
8*2dc74e6cSbostic  *	@(#)if_ne.c	8.1 (Berkeley) 06/11/93
952079767Sbostic  */
1052079767Sbostic 
1152079767Sbostic /*
1252079767Sbostic  * NE2000 Ethernet driver
13011b0098Sbill  *
14011b0098Sbill  * Parts inspired from Tim Tucker's if_wd driver for the wd8003,
15011b0098Sbill  * insight on the ne2000 gained from Robert Clements PC/FTP driver.
16011b0098Sbill  */
17011b0098Sbill 
1852079767Sbostic #include "ne.h"
1952079767Sbostic #if NNE > 0
2052079767Sbostic 
21fb81e5fdSbostic #include <sys/param.h>
22fb81e5fdSbostic #include <sys/systm.h>
23fb81e5fdSbostic #include <sys/mbuf.h>
24fb81e5fdSbostic #include <sys/buf.h>
25fb81e5fdSbostic #include <sys/protosw.h>
26fb81e5fdSbostic #include <sys/socket.h>
27fb81e5fdSbostic #include <sys/ioctl.h>
28fb81e5fdSbostic #include <sys/errno.h>
29fb81e5fdSbostic #include <sys/syslog.h>
30011b0098Sbill 
31fb81e5fdSbostic #include <net/if.h>
32fb81e5fdSbostic #include <net/netisr.h>
33fb81e5fdSbostic #include <net/route.h>
34011b0098Sbill 
35011b0098Sbill #ifdef INET
36fb81e5fdSbostic #include <netinet/in.h>
37fb81e5fdSbostic #include <netinet/in_systm.h>
38fb81e5fdSbostic #include <netinet/in_var.h>
39fb81e5fdSbostic #include <netinet/ip.h>
40fb81e5fdSbostic #include <netinet/if_ether.h>
41011b0098Sbill #endif
42011b0098Sbill 
43011b0098Sbill #ifdef NS
44fb81e5fdSbostic #include <netns/ns.h>
45fb81e5fdSbostic #include <netns/ns_if.h>
46011b0098Sbill #endif
47011b0098Sbill 
48fb81e5fdSbostic #include <i386/isa/isa_device.h>
49fb81e5fdSbostic #include <i386/isa/if_nereg.h>
50fb81e5fdSbostic #include <i386/isa/icu.h>
51011b0098Sbill 
52011b0098Sbill int	neprobe(), neattach(), neintr();
53ede20247Swilliam int	nestart(),neinit(), ether_output(), neioctl();
54011b0098Sbill 
552f641111Sbill struct	isa_driver nedriver = {
56011b0098Sbill 	neprobe, neattach, "ne",
57011b0098Sbill };
58011b0098Sbill 
59011b0098Sbill struct	mbuf *neget();
60011b0098Sbill 
61c632612cSwilliam #define ETHER_MIN_LEN 64
62c632612cSwilliam #define ETHER_MAX_LEN 1536
63c632612cSwilliam 
64011b0098Sbill /*
65011b0098Sbill  * Ethernet software status per interface.
66011b0098Sbill  *
67011b0098Sbill  * Each interface is referenced by a network interface structure,
68011b0098Sbill  * ns_if, which the routing code uses to locate the interface.
69011b0098Sbill  * This structure contains the output queue for the interface, its address, ...
70011b0098Sbill  */
71011b0098Sbill struct	ne_softc {
72011b0098Sbill 	struct	arpcom ns_ac;		/* Ethernet common part */
73011b0098Sbill #define	ns_if	ns_ac.ac_if		/* network-visible interface */
74011b0098Sbill #define	ns_addr	ns_ac.ac_enaddr		/* hardware Ethernet address */
75011b0098Sbill 	int	ns_flags;
76011b0098Sbill #define	DSF_LOCK	1		/* block re-entering enstart */
77011b0098Sbill 	int	ns_oactive ;
78011b0098Sbill 	int	ns_mask ;
79011b0098Sbill 	int	ns_ba;			/* byte addr in buffer ram of inc pkt */
80011b0098Sbill 	int	ns_cur;			/* current page being filled */
81011b0098Sbill 	struct	prhdr	ns_ph;		/* hardware header of incoming packet*/
82011b0098Sbill 	struct	ether_header ns_eh;	/* header of incoming packet */
83011b0098Sbill 	u_char	ns_pb[2048 /*ETHERMTU+sizeof(long)*/];
84011b0098Sbill } ne_softc[NNE] ;
856ae1586dSwilliam #define	ENBUFSIZE	(sizeof(struct ether_header) + ETHERMTU + 2 + ETHER_MIN_LEN)
86011b0098Sbill 
87011b0098Sbill int nec;
88011b0098Sbill 
89011b0098Sbill u_short boarddata[16];
90011b0098Sbill 
91011b0098Sbill neprobe(dvp)
922f641111Sbill 	struct isa_device *dvp;
93011b0098Sbill {
94011b0098Sbill 	int val,i,s;
95011b0098Sbill 	register struct ne_softc *ns = &ne_softc[0];
96011b0098Sbill 
97011b0098Sbill #ifdef lint
98011b0098Sbill 	neintr(0);
99011b0098Sbill #endif
100011b0098Sbill 
1012f641111Sbill 	nec = dvp->id_iobase;
102011b0098Sbill 	s = splimp();
103011b0098Sbill 
1046ae1586dSwilliam 	/* Reset the bastard */
105011b0098Sbill 	val = inb(nec+ne_reset);
106011b0098Sbill 	DELAY(2000000);
107011b0098Sbill 	outb(nec+ne_reset,val);
108011b0098Sbill 
109011b0098Sbill 	outb(nec+ds_cmd, DSCM_STOP|DSCM_NODMA);
1102f641111Sbill 
1112f641111Sbill 	i = 1000000;
112ede20247Swilliam 	while ((inb(nec+ds0_isr)&DSIS_RESET) == 0 && i-- > 0);
1132f641111Sbill 	if (i < 0) return (0);
1142f641111Sbill 
115011b0098Sbill 	outb(nec+ds0_isr, 0xff);
116011b0098Sbill 
117011b0098Sbill 	/* Word Transfers, Burst Mode Select, Fifo at 8 bytes */
118011b0098Sbill 	outb(nec+ds0_dcr, DSDC_WTS|DSDC_BMS|DSDC_FT1);
119011b0098Sbill 
120011b0098Sbill 	outb(nec+ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_STOP);
1212f641111Sbill 	DELAY(10000);
1222f641111Sbill 
1236ae1586dSwilliam 	/* Check cmd reg and fail if not right */
1242f641111Sbill 	if ((i=inb(nec+ds_cmd)) != (DSCM_NODMA|DSCM_PG0|DSCM_STOP))
1252f641111Sbill 		return(0);
1262f641111Sbill 
127011b0098Sbill 	outb(nec+ds0_tcr, 0);
128011b0098Sbill 	outb(nec+ds0_rcr, DSRC_MON);
129011b0098Sbill 	outb(nec+ds0_pstart, RBUF/DS_PGSIZE);
130011b0098Sbill 	outb(nec+ds0_pstop, RBUFEND/DS_PGSIZE);
131011b0098Sbill 	outb(nec+ds0_bnry, RBUFEND/DS_PGSIZE);
132011b0098Sbill 	outb(nec+ds0_imr, 0);
133011b0098Sbill 	outb(nec+ds0_isr, 0);
134011b0098Sbill 	outb(nec+ds_cmd, DSCM_NODMA|DSCM_PG1|DSCM_STOP);
135011b0098Sbill 	outb(nec+ds1_curr, RBUF/DS_PGSIZE);
136011b0098Sbill 	outb(nec+ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_STOP);
1376ae1586dSwilliam 
1382f641111Sbill #ifdef NEDEBUG
1396ae1586dSwilliam #define	PAT(n)	(0xa55a + 37*(n))
1406ae1586dSwilliam #define	RCON	37
1416ae1586dSwilliam 	{	int i, rom, pat;
1426ae1586dSwilliam 
143011b0098Sbill 		rom=1;
144011b0098Sbill 		printf("ne ram ");
1456ae1586dSwilliam 
146011b0098Sbill 		for (i = 0; i < 0xfff0; i+=4) {
1476ae1586dSwilliam 			pat = PAT(i);
1486ae1586dSwilliam 			neput(&pat,i,4);
1496ae1586dSwilliam 			nefetch(&pat,i,4);
1506ae1586dSwilliam 			if (pat == PAT(i)) {
1516ae1586dSwilliam 				if (rom) {
1526ae1586dSwilliam 					rom=0;
1536ae1586dSwilliam 					printf(" %x", i);
1546ae1586dSwilliam 				}
155011b0098Sbill 			} else {
1566ae1586dSwilliam 				if (!rom) {
1576ae1586dSwilliam 					rom=1;
1586ae1586dSwilliam 					printf("..%x ", i);
1596ae1586dSwilliam 				}
160011b0098Sbill 			}
161011b0098Sbill 			pat=0;
1626ae1586dSwilliam 			neput(&pat,i,4);
163011b0098Sbill 		}
164011b0098Sbill 		printf("\n");
1656ae1586dSwilliam 	}
1662f641111Sbill #endif
1676ae1586dSwilliam 
1686ae1586dSwilliam 	/* Extract board address */
1696ae1586dSwilliam 	nefetch ((caddr_t)boarddata, 0, sizeof(boarddata));
170011b0098Sbill 	for(i=0; i < 6; i++)  ns->ns_addr[i] = boarddata[i];
171011b0098Sbill 	splx(s);
172011b0098Sbill 	return (1);
173011b0098Sbill }
174011b0098Sbill 
1756ae1586dSwilliam /*
1766ae1586dSwilliam  * Fetch from onboard ROM/RAM
1776ae1586dSwilliam  */
nefetch(up,ad,len)1786ae1586dSwilliam nefetch (up, ad, len) caddr_t up; {
179011b0098Sbill 	u_char cmd;
180011b0098Sbill 
181011b0098Sbill 	cmd = inb(nec+ds_cmd);
1826ae1586dSwilliam 	outb (nec+ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_START);
1836ae1586dSwilliam 
1846ae1586dSwilliam 	/* Setup remote dma */
185011b0098Sbill 	outb (nec+ds0_isr, DSIS_RDC);
1866ae1586dSwilliam 	outb (nec+ds0_rbcr0, len);
1876ae1586dSwilliam 	outb (nec+ds0_rbcr1, len>>8);
1886ae1586dSwilliam 	outb (nec+ds0_rsar0, ad);
1896ae1586dSwilliam 	outb (nec+ds0_rsar1, ad>>8);
1906ae1586dSwilliam 
1916ae1586dSwilliam 	/* Execute & extract from card */
192011b0098Sbill 	outb (nec+ds_cmd, DSCM_RREAD|DSCM_PG0|DSCM_START);
193011b0098Sbill 	insw (nec+ne_data, up, len/2);
1946ae1586dSwilliam 
1956ae1586dSwilliam 	/* Wait till done, then shutdown feature */
1966ae1586dSwilliam 	while ((inb (nec+ds0_isr) & DSIS_RDC) == 0) ;
197011b0098Sbill 	outb (nec+ds0_isr, DSIS_RDC);
198011b0098Sbill 	outb (nec+ds_cmd, cmd);
199011b0098Sbill }
200011b0098Sbill 
2016ae1586dSwilliam /*
2026ae1586dSwilliam  * Put to onboard RAM
2036ae1586dSwilliam  */
neput(up,ad,len)2046ae1586dSwilliam neput (up, ad, len) caddr_t up; {
205011b0098Sbill 	u_char cmd;
206011b0098Sbill 
207011b0098Sbill 	cmd = inb(nec+ds_cmd);
208011b0098Sbill 	outb (nec+ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_START);
209011b0098Sbill 
2106ae1586dSwilliam 	/* Setup for remote dma */
211011b0098Sbill 	outb (nec+ds0_isr, DSIS_RDC);
2126ae1586dSwilliam 	if(len&1) len++;		/* roundup to words */
2136ae1586dSwilliam 	outb (nec+ds0_rbcr0, len);
2146ae1586dSwilliam 	outb (nec+ds0_rbcr1, len>>8);
2156ae1586dSwilliam 	outb (nec+ds0_rsar0, ad);
2166ae1586dSwilliam 	outb (nec+ds0_rsar1, ad>>8);
2176ae1586dSwilliam 
2186ae1586dSwilliam 	/* Execute & stuff to card */
219011b0098Sbill 	outb (nec+ds_cmd, DSCM_RWRITE|DSCM_PG0|DSCM_START);
220011b0098Sbill 	outsw (nec+ne_data, up, len/2);
2216ae1586dSwilliam 
2226ae1586dSwilliam 	/* Wait till done, then shutdown feature */
2236ae1586dSwilliam 	while ((inb (nec+ds0_isr) & DSIS_RDC) == 0) ;
224011b0098Sbill 	outb (nec+ds0_isr, DSIS_RDC);
225011b0098Sbill 	outb (nec+ds_cmd, cmd);
226011b0098Sbill }
227011b0098Sbill 
228011b0098Sbill /*
229011b0098Sbill  * Reset of interface.
230011b0098Sbill  */
nereset(unit,uban)231011b0098Sbill nereset(unit, uban)
232011b0098Sbill 	int unit, uban;
233011b0098Sbill {
234011b0098Sbill 	if (unit >= NNE)
235011b0098Sbill 		return;
236011b0098Sbill 	printf("ne%d: reset\n", unit);
237011b0098Sbill 	ne_softc[unit].ns_flags &= ~DSF_LOCK;
238011b0098Sbill 	neinit(unit);
239011b0098Sbill }
240011b0098Sbill 
241011b0098Sbill /*
242011b0098Sbill  * Interface exists: make available by filling in network interface
243011b0098Sbill  * record.  System will initialize the interface when it is ready
244011b0098Sbill  * to accept packets.  We get the ethernet address here.
245011b0098Sbill  */
246011b0098Sbill neattach(dvp)
2472f641111Sbill 	struct isa_device *dvp;
248011b0098Sbill {
2492f641111Sbill 	int unit = dvp->id_unit;
250011b0098Sbill 	register struct ne_softc *ns = &ne_softc[unit];
251011b0098Sbill 	register struct ifnet *ifp = &ns->ns_if;
252011b0098Sbill 
253011b0098Sbill 	ifp->if_unit = unit;
254011b0098Sbill 	ifp->if_name = nedriver.name ;
255011b0098Sbill 	ifp->if_mtu = ETHERMTU;
2561cb440e4Sbill 	printf (" ethernet address %s", ether_sprintf(ns->ns_addr)) ;
257ede20247Swilliam 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS;
258011b0098Sbill 	ifp->if_init = neinit;
259ede20247Swilliam 	ifp->if_output = ether_output;
260ede20247Swilliam 	ifp->if_start = nestart;
261011b0098Sbill 	ifp->if_ioctl = neioctl;
262011b0098Sbill 	ifp->if_reset = nereset;
263011b0098Sbill 	ifp->if_watchdog = 0;
264011b0098Sbill 	if_attach(ifp);
265011b0098Sbill }
266011b0098Sbill 
267011b0098Sbill /*
268011b0098Sbill  * Initialization of interface; set up initialization block
269011b0098Sbill  * and transmit/receive descriptor rings.
270011b0098Sbill  */
neinit(unit)271011b0098Sbill neinit(unit)
272011b0098Sbill 	int unit;
273011b0098Sbill {
274011b0098Sbill 	register struct ne_softc *ns = &ne_softc[unit];
275011b0098Sbill 	struct ifnet *ifp = &ns->ns_if;
276011b0098Sbill 	int s;
277011b0098Sbill 	register i; char *cp;
278011b0098Sbill 
279011b0098Sbill  	if (ifp->if_addrlist == (struct ifaddr *)0) return;
280011b0098Sbill 	if (ifp->if_flags & IFF_RUNNING) return;
281011b0098Sbill 
282011b0098Sbill 	s = splimp();
283011b0098Sbill 
284011b0098Sbill 	/* set physical address on ethernet */
285011b0098Sbill 	outb (nec+ds_cmd, DSCM_NODMA|DSCM_PG1|DSCM_STOP);
286011b0098Sbill 	for (i=0 ; i < 6 ; i++) outb(nec+ds1_par0+i,ns->ns_addr[i]);
287011b0098Sbill 
288011b0098Sbill 	/* clr logical address hash filter for now */
289011b0098Sbill 	for (i=0 ; i < 8 ; i++) outb(nec+ds1_mar0+i,0xff);
290011b0098Sbill 
291011b0098Sbill 	/* init regs */
292011b0098Sbill 	outb (nec+ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_STOP);
293011b0098Sbill 	outb (nec+ds0_rbcr0, 0);
294011b0098Sbill 	outb (nec+ds0_rbcr1, 0);
295011b0098Sbill 	outb (nec+ds0_imr, 0);
296011b0098Sbill 	outb (nec+ds0_isr, 0xff);
2976ae1586dSwilliam 
298011b0098Sbill 	/* Word Transfers, Burst Mode Select, Fifo at 8 bytes */
299011b0098Sbill 	outb(nec+ds0_dcr, DSDC_WTS|DSDC_BMS|DSDC_FT1);
300011b0098Sbill 	outb(nec+ds0_tcr, 0);
301011b0098Sbill 	outb (nec+ds0_rcr, DSRC_MON);
302011b0098Sbill 	outb (nec+ds0_tpsr, 0);
303011b0098Sbill 	outb(nec+ds0_pstart, RBUF/DS_PGSIZE);
304011b0098Sbill 	outb(nec+ds0_pstop, RBUFEND/DS_PGSIZE);
305452cbf0bSbill 	outb(nec+ds0_bnry, RBUF/DS_PGSIZE);
306011b0098Sbill 	outb (nec+ds_cmd, DSCM_NODMA|DSCM_PG1|DSCM_STOP);
307011b0098Sbill 	outb(nec+ds1_curr, RBUF/DS_PGSIZE);
308011b0098Sbill 	ns->ns_cur = RBUF/DS_PGSIZE;
309011b0098Sbill 	outb (nec+ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_START);
310011b0098Sbill 	outb (nec+ds0_rcr, DSRC_AB);
311011b0098Sbill 	outb(nec+ds0_dcr, DSDC_WTS|DSDC_BMS|DSDC_FT1);
312011b0098Sbill 	outb (nec+ds0_imr, 0xff);
313011b0098Sbill 
314011b0098Sbill 	ns->ns_if.if_flags |= IFF_RUNNING;
315011b0098Sbill 	ns->ns_oactive = 0; ns->ns_mask = ~0;
316ede20247Swilliam 	nestart(ifp);
317011b0098Sbill 	splx(s);
318011b0098Sbill }
319011b0098Sbill 
320011b0098Sbill /*
321011b0098Sbill  * Setup output on interface.
322011b0098Sbill  * Get another datagram to send off of the interface queue,
323011b0098Sbill  * and map it to the interface before starting the output.
324011b0098Sbill  * called only at splimp or interrupt level.
325011b0098Sbill  */
326ede20247Swilliam nestart(ifp)
327ede20247Swilliam 	struct ifnet *ifp;
328011b0098Sbill {
329ede20247Swilliam 	register struct ne_softc *ns = &ne_softc[ifp->if_unit];
330011b0098Sbill 	struct mbuf *m0, *m;
331011b0098Sbill 	int buffer;
332ede20247Swilliam 	int len = 0, i, total,t;
333011b0098Sbill 
334011b0098Sbill 	/*
335011b0098Sbill 	 * The DS8390 has only one transmit buffer, if it is busy we
336011b0098Sbill 	 * must wait until the transmit interrupt completes.
337011b0098Sbill 	 */
338011b0098Sbill 	outb(nec+ds_cmd,DSCM_NODMA|DSCM_START);
339011b0098Sbill 
3402f641111Sbill 	if (ns->ns_flags & DSF_LOCK)
3412f641111Sbill 		return;
3422f641111Sbill 
343011b0098Sbill 	if (inb(nec+ds_cmd) & DSCM_TRANS)
344011b0098Sbill 		return;
345011b0098Sbill 
346011b0098Sbill 	if ((ns->ns_if.if_flags & IFF_RUNNING) == 0)
347011b0098Sbill 		return;
348011b0098Sbill 
349011b0098Sbill 	IF_DEQUEUE(&ns->ns_if.if_snd, m);
350011b0098Sbill 
351011b0098Sbill 	if (m == 0)
352011b0098Sbill 		return;
353011b0098Sbill 
354011b0098Sbill 	/*
355011b0098Sbill 	 * Copy the mbuf chain into the transmit buffer
356011b0098Sbill 	 */
357011b0098Sbill 
3582f641111Sbill 	ns->ns_flags |= DSF_LOCK;	/* prevent entering nestart */
359011b0098Sbill 	buffer = TBUF; len = i = 0;
360ede20247Swilliam 	t = 0;
3616ae1586dSwilliam 	for (m0 = m; m != 0; m = m->m_next)
362ede20247Swilliam 		t += m->m_len;
363ede20247Swilliam 
364ede20247Swilliam 	m = m0;
365ede20247Swilliam 	total = t;
366ede20247Swilliam 	for (m0 = m; m != 0; ) {
367ede20247Swilliam 
368ede20247Swilliam 		if (m->m_len&1 && t > m->m_len) {
3696ae1586dSwilliam 			neput(mtod(m, caddr_t), buffer, m->m_len - 1);
370ede20247Swilliam 			t -= m->m_len - 1;
371ede20247Swilliam 			buffer += m->m_len - 1;
372ede20247Swilliam 			m->m_data += m->m_len - 1;
373ede20247Swilliam 			m->m_len = 1;
374ede20247Swilliam 			m = m_pullup(m, 2);
375ede20247Swilliam 		} else {
3766ae1586dSwilliam 			neput(mtod(m, caddr_t), buffer, m->m_len);
377011b0098Sbill 			buffer += m->m_len;
378ede20247Swilliam 			t -= m->m_len;
379ede20247Swilliam 			MFREE(m, m0);
380ede20247Swilliam 			m = m0;
381ede20247Swilliam 		}
382011b0098Sbill 	}
383011b0098Sbill 
384011b0098Sbill 	/*
385011b0098Sbill 	 * Init transmit length registers, and set transmit start flag.
386011b0098Sbill 	 */
387011b0098Sbill 
388ede20247Swilliam 	len = total;
3896ae1586dSwilliam 	if (len < ETHER_MIN_LEN) len = ETHER_MIN_LEN;
390011b0098Sbill 	outb(nec+ds0_tbcr0,len&0xff);
391011b0098Sbill 	outb(nec+ds0_tbcr1,(len>>8)&0xff);
392011b0098Sbill 	outb(nec+ds0_tpsr, TBUF/DS_PGSIZE);
393011b0098Sbill 	outb(nec+ds_cmd, DSCM_TRANS|DSCM_NODMA|DSCM_START);
394011b0098Sbill }
395011b0098Sbill 
3966ae1586dSwilliam /* buffer successor/predecessor in ring? */
397452cbf0bSbill #define succ(n) (((n)+1 >= RBUFEND/DS_PGSIZE) ? RBUF/DS_PGSIZE : (n)+1)
398452cbf0bSbill #define pred(n) (((n)-1 < RBUF/DS_PGSIZE) ? RBUFEND/DS_PGSIZE-1 : (n)-1)
3996ae1586dSwilliam 
400011b0098Sbill /*
401011b0098Sbill  * Controller interrupt.
402011b0098Sbill  */
neintr(unit)403ede20247Swilliam neintr(unit)
404011b0098Sbill {
405ede20247Swilliam 	register struct ne_softc *ns = &ne_softc[unit];
406011b0098Sbill 	u_char cmd,isr;
407011b0098Sbill 
4086ae1586dSwilliam 	/* Save cmd, clear interrupt */
409011b0098Sbill 	cmd = inb (nec+ds_cmd);
4102f641111Sbill loop:
411011b0098Sbill 	isr = inb (nec+ds0_isr);
4122f641111Sbill 	outb(nec+ds_cmd,DSCM_NODMA|DSCM_START);
413011b0098Sbill 	outb(nec+ds0_isr, isr);
414011b0098Sbill 
4156ae1586dSwilliam 	/* Receiver error */
416011b0098Sbill 	if (isr & DSIS_RXE) {
417011b0098Sbill 		/* need to read these registers to clear status */
418011b0098Sbill 		(void) inb(nec+ ds0_rsr);
419011b0098Sbill 		(void) inb(nec+ 0xD);
420011b0098Sbill 		(void) inb(nec + 0xE);
421011b0098Sbill 		(void) inb(nec + 0xF);
422011b0098Sbill 		ns->ns_if.if_ierrors++;
423011b0098Sbill 	}
424011b0098Sbill 
4256ae1586dSwilliam 	/* We received something; rummage thru tiny ring buffer */
426452cbf0bSbill 	if (isr & (DSIS_RX|DSIS_RXE|DSIS_ROVRN)) {
427011b0098Sbill 		u_char pend,lastfree;
428011b0098Sbill 
429011b0098Sbill 		outb(nec+ds_cmd, DSCM_START|DSCM_NODMA|DSCM_PG1);
430011b0098Sbill 		pend = inb(nec+ds1_curr);
431011b0098Sbill 		outb(nec+ds_cmd, DSCM_START|DSCM_NODMA|DSCM_PG0);
432011b0098Sbill 		lastfree = inb(nec+ds0_bnry);
4336ae1586dSwilliam 
4346ae1586dSwilliam 		/* Have we wrapped? */
435452cbf0bSbill 		if (lastfree >= RBUFEND/DS_PGSIZE)
436011b0098Sbill 			lastfree = RBUF/DS_PGSIZE;
437452cbf0bSbill 		if (pend < lastfree && ns->ns_cur < pend)
438452cbf0bSbill 			lastfree = ns->ns_cur;
439452cbf0bSbill 		else	if (ns->ns_cur > lastfree)
440452cbf0bSbill 			lastfree = ns->ns_cur;
441452cbf0bSbill 
4426ae1586dSwilliam 		/* Something in the buffer? */
443452cbf0bSbill 		while (pend != lastfree) {
444011b0098Sbill 			u_char nxt;
445011b0098Sbill 
4466ae1586dSwilliam 			/* Extract header from microcephalic board */
4476ae1586dSwilliam 			nefetch(&ns->ns_ph,lastfree*DS_PGSIZE,
448452cbf0bSbill 				sizeof(ns->ns_ph));
449452cbf0bSbill 			ns->ns_ba = lastfree*DS_PGSIZE+sizeof(ns->ns_ph);
450011b0098Sbill 
4516ae1586dSwilliam 			/* Incipient paranoia */
4522f641111Sbill 			if (ns->ns_ph.pr_status == DSRS_RPC ||
4536ae1586dSwilliam 				/* for dequna's */
4542f641111Sbill 				ns->ns_ph.pr_status == 0x21)
455011b0098Sbill 				nerecv (ns);
4562f641111Sbill #ifdef NEDEBUG
4572f641111Sbill 			else  {
4586ae1586dSwilliam 				printf("cur %x pnd %x lfr %x ",
4596ae1586dSwilliam 					ns->ns_cur, pend, lastfree);
4606ae1586dSwilliam 				printf("nxt %x len %x ", ns->ns_ph.pr_nxtpg,
4616ae1586dSwilliam 					(ns->ns_ph.pr_sz1<<8)+ ns->ns_ph.pr_sz0);
4626ae1586dSwilliam 				printf("Bogus Sts %x\n", ns->ns_ph.pr_status);
4632f641111Sbill 			}
4642f641111Sbill #endif
465011b0098Sbill 
466011b0098Sbill 			nxt = ns->ns_ph.pr_nxtpg ;
4676ae1586dSwilliam 
4686ae1586dSwilliam 			/* Sanity check */
4696ae1586dSwilliam 			if ( nxt >= RBUF/DS_PGSIZE && nxt <= RBUFEND/DS_PGSIZE
4706ae1586dSwilliam 				&& nxt <= pend)
471011b0098Sbill 				ns->ns_cur = nxt;
472011b0098Sbill 			else	ns->ns_cur = nxt = pend;
4736ae1586dSwilliam 
4746ae1586dSwilliam 			/* Set the boundaries */
475452cbf0bSbill 			lastfree = nxt;
476452cbf0bSbill 			outb(nec+ds0_bnry, pred(nxt));
477452cbf0bSbill 			outb(nec+ds_cmd, DSCM_START|DSCM_NODMA|DSCM_PG1);
4782f641111Sbill 			pend = inb(nec+ds1_curr);
479452cbf0bSbill 			outb(nec+ds_cmd, DSCM_START|DSCM_NODMA|DSCM_PG0);
4806ae1586dSwilliam 		}
481011b0098Sbill 		outb(nec+ds_cmd, DSCM_START|DSCM_NODMA);
482011b0098Sbill 	}
4836ae1586dSwilliam 
4846ae1586dSwilliam 	/* Transmit error */
485011b0098Sbill 	if (isr & DSIS_TXE) {
4862f641111Sbill 		ns->ns_flags &= ~DSF_LOCK;
4876ae1586dSwilliam 		/* Need to read these registers to clear status */
488011b0098Sbill 		ns->ns_if.if_collisions += inb(nec+ds0_tbcr0);
489011b0098Sbill 		ns->ns_if.if_oerrors++;
490011b0098Sbill 	}
4916ae1586dSwilliam 
4926ae1586dSwilliam 	/* Packet Transmitted */
4932f641111Sbill 	if (isr & DSIS_TX) {
4942f641111Sbill 		ns->ns_flags &= ~DSF_LOCK;
495011b0098Sbill 		++ns->ns_if.if_opackets;
496452cbf0bSbill 		ns->ns_if.if_collisions += inb(nec+ds0_tbcr0);
497452cbf0bSbill 	}
498452cbf0bSbill 
4996ae1586dSwilliam 	/* Receiver ovverun? */
500452cbf0bSbill 	if (isr & DSIS_ROVRN) {
5016ae1586dSwilliam 		log(LOG_ERR, "ne%d: error: isr %x\n", ns-ne_softc, isr
5026ae1586dSwilliam 			/*, DSIS_BITS*/);
503452cbf0bSbill 		outb(nec+ds0_rbcr0, 0);
504452cbf0bSbill 		outb(nec+ds0_rbcr1, 0);
505452cbf0bSbill 		outb(nec+ds0_tcr, DSTC_LB0);
506452cbf0bSbill 		outb(nec+ds0_rcr, DSRC_MON);
507452cbf0bSbill 		outb(nec+ds_cmd, DSCM_START|DSCM_NODMA);
508452cbf0bSbill 		outb(nec+ds0_rcr, DSRC_AB);
509452cbf0bSbill 		outb(nec+ds0_tcr, 0);
510011b0098Sbill 	}
5112f641111Sbill 
5126ae1586dSwilliam 	/* Any more to send? */
513011b0098Sbill 	outb (nec+ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_START);
514ede20247Swilliam 	nestart(&ns->ns_if);
5152f641111Sbill 	outb (nec+ds_cmd, cmd);
516011b0098Sbill 	outb (nec+ds0_imr, 0xff);
5176ae1586dSwilliam 
5186ae1586dSwilliam 	/* Still more to do? */
5192f641111Sbill 	isr = inb (nec+ds0_isr);
5202f641111Sbill 	if(isr) goto loop;
521011b0098Sbill }
522011b0098Sbill 
523011b0098Sbill /*
524011b0098Sbill  * Ethernet interface receiver interface.
525011b0098Sbill  * If input error just drop packet.
526011b0098Sbill  * Otherwise examine packet to determine type.  If can't determine length
527011b0098Sbill  * from type, then have to drop packet.  Othewise decapsulate
528011b0098Sbill  * packet based on type and pass to type specific higher-level
529011b0098Sbill  * input routine.
530011b0098Sbill  */
nerecv(ns)531011b0098Sbill nerecv(ns)
532011b0098Sbill 	register struct ne_softc *ns;
533011b0098Sbill {
534011b0098Sbill 	int len,i;
535011b0098Sbill 
536011b0098Sbill 	ns->ns_if.if_ipackets++;
537011b0098Sbill 	len = ns->ns_ph.pr_sz0 + (ns->ns_ph.pr_sz1<<8);
5386ae1586dSwilliam 	if(len < ETHER_MIN_LEN || len > ETHER_MAX_LEN)
5392f641111Sbill 		return;
5406ae1586dSwilliam 
5416ae1586dSwilliam 	/* this need not be so torturous - one/two bcopys at most into mbufs */
5426ae1586dSwilliam 	nefetch(ns->ns_pb, ns->ns_ba, min(len,DS_PGSIZE-sizeof(ns->ns_ph)));
543011b0098Sbill 	if (len > DS_PGSIZE-sizeof(ns->ns_ph)) {
544011b0098Sbill 		int l = len - (DS_PGSIZE-sizeof(ns->ns_ph)), b ;
545011b0098Sbill 		u_char *p = ns->ns_pb + (DS_PGSIZE-sizeof(ns->ns_ph));
546011b0098Sbill 
547011b0098Sbill 		if(++ns->ns_cur > 0x7f) ns->ns_cur = 0x46;
548011b0098Sbill 		b = ns->ns_cur*DS_PGSIZE;
549011b0098Sbill 
550011b0098Sbill 		while (l >= DS_PGSIZE) {
5516ae1586dSwilliam 			nefetch(p, b, DS_PGSIZE);
552011b0098Sbill 			p += DS_PGSIZE; l -= DS_PGSIZE;
553011b0098Sbill 			if(++ns->ns_cur > 0x7f) ns->ns_cur = 0x46;
554011b0098Sbill 			b = ns->ns_cur*DS_PGSIZE;
555011b0098Sbill 		}
556011b0098Sbill 		if (l > 0)
5576ae1586dSwilliam 			nefetch(p, b, l);
558011b0098Sbill 	}
5596ae1586dSwilliam 	/* don't forget checksum! */
5606ae1586dSwilliam 	len -= sizeof(struct ether_header) + sizeof(long);
561011b0098Sbill 
562011b0098Sbill 	neread(ns,(caddr_t)(ns->ns_pb), len);
563011b0098Sbill }
564011b0098Sbill 
565011b0098Sbill /*
566011b0098Sbill  * Pass a packet to the higher levels.
567011b0098Sbill  * We deal with the trailer protocol here.
568011b0098Sbill  */
neread(ns,buf,len)569011b0098Sbill neread(ns, buf, len)
570011b0098Sbill 	register struct ne_softc *ns;
571011b0098Sbill 	char *buf;
572011b0098Sbill 	int len;
573011b0098Sbill {
574011b0098Sbill 	register struct ether_header *eh;
575011b0098Sbill     	struct mbuf *m;
576011b0098Sbill 	int off, resid;
577011b0098Sbill 	register struct ifqueue *inq;
578011b0098Sbill 
579011b0098Sbill 	/*
580011b0098Sbill 	 * Deal with trailer protocol: if type is trailer type
581011b0098Sbill 	 * get true type from first 16-bit word past data.
582011b0098Sbill 	 * Remember that type was trailer by setting off.
583011b0098Sbill 	 */
584011b0098Sbill 	eh = (struct ether_header *)buf;
585011b0098Sbill 	eh->ether_type = ntohs((u_short)eh->ether_type);
586011b0098Sbill #define	nedataaddr(eh, off, type)	((type)(((caddr_t)((eh)+1)+(off))))
587011b0098Sbill 	if (eh->ether_type >= ETHERTYPE_TRAIL &&
588011b0098Sbill 	    eh->ether_type < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) {
589011b0098Sbill 		off = (eh->ether_type - ETHERTYPE_TRAIL) * 512;
590011b0098Sbill 		if (off >= ETHERMTU) return;		/* sanity */
591011b0098Sbill 		eh->ether_type = ntohs(*nedataaddr(eh, off, u_short *));
592011b0098Sbill 		resid = ntohs(*(nedataaddr(eh, off+2, u_short *)));
593011b0098Sbill 		if (off + resid > len) return;		/* sanity */
594011b0098Sbill 		len = off + resid;
595011b0098Sbill 	} else	off = 0;
596011b0098Sbill 
597011b0098Sbill 	if (len == 0) return;
598011b0098Sbill 
599011b0098Sbill 	/*
600011b0098Sbill 	 * Pull packet off interface.  Off is nonzero if packet
601011b0098Sbill 	 * has trailing header; neget will then force this header
602011b0098Sbill 	 * information to be at the front, but we still have to drop
603011b0098Sbill 	 * the type and length which are at the front of any trailer data.
604011b0098Sbill 	 */
605011b0098Sbill 	m = neget(buf, len, off, &ns->ns_if);
606011b0098Sbill 	if (m == 0) return;
607011b0098Sbill 
608ede20247Swilliam 	ether_input(&ns->ns_if, eh, m);
609011b0098Sbill }
610011b0098Sbill 
611011b0098Sbill /*
612011b0098Sbill  * Supporting routines
613011b0098Sbill  */
614011b0098Sbill 
615011b0098Sbill /*
616011b0098Sbill  * Pull read data off a interface.
617011b0098Sbill  * Len is length of data, with local net header stripped.
618011b0098Sbill  * Off is non-zero if a trailer protocol was used, and
619011b0098Sbill  * gives the offset of the trailer information.
620011b0098Sbill  * We copy the trailer information and then all the normal
621011b0098Sbill  * data into mbufs.  When full cluster sized units are present
622011b0098Sbill  * we copy into clusters.
623011b0098Sbill  */
624011b0098Sbill struct mbuf *
neget(buf,totlen,off0,ifp)625011b0098Sbill neget(buf, totlen, off0, ifp)
626011b0098Sbill 	caddr_t buf;
627011b0098Sbill 	int totlen, off0;
628011b0098Sbill 	struct ifnet *ifp;
629011b0098Sbill {
630011b0098Sbill 	struct mbuf *top, **mp, *m, *p;
631011b0098Sbill 	int off = off0, len;
632011b0098Sbill 	register caddr_t cp = buf;
633ede20247Swilliam 	char *epkt;
634011b0098Sbill 
635ede20247Swilliam 	buf += sizeof(struct ether_header);
636ede20247Swilliam 	cp = buf;
637ede20247Swilliam 	epkt = cp + totlen;
638ede20247Swilliam 
639ede20247Swilliam 
640ede20247Swilliam 	if (off) {
641ede20247Swilliam 		cp += off + 2 * sizeof(u_short);
642ede20247Swilliam 		totlen -= 2 * sizeof(u_short);
643ede20247Swilliam 	}
644ede20247Swilliam 
645ede20247Swilliam 	MGETHDR(m, M_DONTWAIT, MT_DATA);
646ede20247Swilliam 	if (m == 0)
647ede20247Swilliam 		return (0);
648ede20247Swilliam 	m->m_pkthdr.rcvif = ifp;
649ede20247Swilliam 	m->m_pkthdr.len = totlen;
650ede20247Swilliam 	m->m_len = MHLEN;
651ede20247Swilliam 
652011b0098Sbill 	top = 0;
653011b0098Sbill 	mp = &top;
654011b0098Sbill 	while (totlen > 0) {
655ede20247Swilliam 		if (top) {
656011b0098Sbill 			MGET(m, M_DONTWAIT, MT_DATA);
657ede20247Swilliam 			if (m == 0) {
658011b0098Sbill 				m_freem(top);
659011b0098Sbill 				return (0);
660011b0098Sbill 			}
661ede20247Swilliam 			m->m_len = MLEN;
662011b0098Sbill 		}
663ede20247Swilliam 		len = min(totlen, epkt - cp);
664ede20247Swilliam 		if (len >= MINCLSIZE) {
665ede20247Swilliam 			MCLGET(m, M_DONTWAIT);
666ede20247Swilliam 			if (m->m_flags & M_EXT)
667ede20247Swilliam 				m->m_len = len = min(len, MCLBYTES);
668ede20247Swilliam 			else
669ede20247Swilliam 				len = m->m_len;
670ede20247Swilliam 		} else {
671ede20247Swilliam 			/*
672ede20247Swilliam 			 * Place initial small packet/header at end of mbuf.
673ede20247Swilliam 			 */
674ede20247Swilliam 			if (len < m->m_len) {
675ede20247Swilliam 				if (top == 0 && len + max_linkhdr <= m->m_len)
676ede20247Swilliam 					m->m_data += max_linkhdr;
677ede20247Swilliam 				m->m_len = len;
678ede20247Swilliam 			} else
679ede20247Swilliam 				len = m->m_len;
680ede20247Swilliam 		}
681ede20247Swilliam 		bcopy(cp, mtod(m, caddr_t), (unsigned)len);
682ede20247Swilliam 		cp += len;
683ede20247Swilliam 		*mp = m;
684ede20247Swilliam 		mp = &m->m_next;
685ede20247Swilliam 		totlen -= len;
686ede20247Swilliam 		if (cp == epkt)
687ede20247Swilliam 			cp = buf;
688ede20247Swilliam 	}
689ede20247Swilliam 	return (top);
690011b0098Sbill }
691011b0098Sbill 
692011b0098Sbill /*
693011b0098Sbill  * Process an ioctl request.
694011b0098Sbill  */
neioctl(ifp,cmd,data)695011b0098Sbill neioctl(ifp, cmd, data)
696011b0098Sbill 	register struct ifnet *ifp;
697011b0098Sbill 	int cmd;
698011b0098Sbill 	caddr_t data;
699011b0098Sbill {
700011b0098Sbill 	register struct ifaddr *ifa = (struct ifaddr *)data;
701011b0098Sbill 	struct ne_softc *ns = &ne_softc[ifp->if_unit];
702011b0098Sbill 	struct ifreq *ifr = (struct ifreq *)data;
703011b0098Sbill 	int s = splimp(), error = 0;
704011b0098Sbill 
705011b0098Sbill 
706011b0098Sbill 	switch (cmd) {
707011b0098Sbill 
708011b0098Sbill 	case SIOCSIFADDR:
709011b0098Sbill 		ifp->if_flags |= IFF_UP;
710011b0098Sbill 
711ede20247Swilliam 		switch (ifa->ifa_addr->sa_family) {
712011b0098Sbill #ifdef INET
713011b0098Sbill 		case AF_INET:
714011b0098Sbill 			neinit(ifp->if_unit);	/* before arpwhohas */
715011b0098Sbill 			((struct arpcom *)ifp)->ac_ipaddr =
716011b0098Sbill 				IA_SIN(ifa)->sin_addr;
717011b0098Sbill 			arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr);
718011b0098Sbill 			break;
719011b0098Sbill #endif
720011b0098Sbill #ifdef NS
721011b0098Sbill 		case AF_NS:
722011b0098Sbill 		    {
723011b0098Sbill 			register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr);
724011b0098Sbill 
725011b0098Sbill 			if (ns_nullhost(*ina))
726011b0098Sbill 				ina->x_host = *(union ns_host *)(ns->ns_addr);
727011b0098Sbill 			else {
728011b0098Sbill 				/*
729011b0098Sbill 				 * The manual says we can't change the address
730011b0098Sbill 				 * while the receiver is armed,
731011b0098Sbill 				 * so reset everything
732011b0098Sbill 				 */
733011b0098Sbill 				ifp->if_flags &= ~IFF_RUNNING;
734011b0098Sbill 				bcopy((caddr_t)ina->x_host.c_host,
735011b0098Sbill 				    (caddr_t)ns->ns_addr, sizeof(ns->ns_addr));
736011b0098Sbill 			}
737011b0098Sbill 			neinit(ifp->if_unit); /* does ne_setaddr() */
738011b0098Sbill 			break;
739011b0098Sbill 		    }
740011b0098Sbill #endif
741011b0098Sbill 		default:
742011b0098Sbill 			neinit(ifp->if_unit);
743011b0098Sbill 			break;
744011b0098Sbill 		}
745011b0098Sbill 		break;
746011b0098Sbill 
747011b0098Sbill 	case SIOCSIFFLAGS:
748011b0098Sbill 		if ((ifp->if_flags & IFF_UP) == 0 &&
749011b0098Sbill 		    ifp->if_flags & IFF_RUNNING) {
750011b0098Sbill 			ifp->if_flags &= ~IFF_RUNNING;
751011b0098Sbill 			outb(nec+ds_cmd,DSCM_STOP|DSCM_NODMA);
752011b0098Sbill 		} else if (ifp->if_flags & IFF_UP &&
753011b0098Sbill 		    (ifp->if_flags & IFF_RUNNING) == 0)
754011b0098Sbill 			neinit(ifp->if_unit);
755011b0098Sbill 		break;
756011b0098Sbill 
757011b0098Sbill #ifdef notdef
758011b0098Sbill 	case SIOCGHWADDR:
759011b0098Sbill 		bcopy((caddr_t)ns->ns_addr, (caddr_t) &ifr->ifr_data,
760011b0098Sbill 			sizeof(ns->ns_addr));
761011b0098Sbill 		break;
762011b0098Sbill #endif
763011b0098Sbill 
764011b0098Sbill 	default:
765011b0098Sbill 		error = EINVAL;
766011b0098Sbill 	}
767011b0098Sbill 	splx(s);
768011b0098Sbill 	return (error);
769011b0098Sbill }
770011b0098Sbill #endif
771