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