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 = ⊤
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