added ISO network software interrupts
[unix-history] / usr / src / sys / i386 / isa / if_ne.c
CommitLineData
d0d290ad 1/*-
d91e594a 2 * Copyright (c) 1990, 1991 William F. Jolitz.
d0d290ad
WN
3 * Copyright (c) 1990 The Regents of the University of California.
4 * All rights reserved.
5 *
6 * %sccs.include.redist.c%
7 *
d91e594a
KB
8 * @(#)if_ne.c 7.4 (Berkeley) %G%
9 */
10
11/*
12 * NE2000 Ethernet driver
93b2b8c5
BJ
13 *
14 * Parts inspired from Tim Tucker's if_wd driver for the wd8003,
15 * insight on the ne2000 gained from Robert Clements PC/FTP driver.
16 */
17
d91e594a
KB
18#include "ne.h"
19#if NNE > 0
20
93b2b8c5
BJ
21#include "param.h"
22#include "systm.h"
23#include "mbuf.h"
24#include "buf.h"
25#include "protosw.h"
26#include "socket.h"
27#include "ioctl.h"
28#include "errno.h"
29#include "syslog.h"
30
d0d290ad
WN
31#include "net/if.h"
32#include "net/netisr.h"
33#include "net/route.h"
93b2b8c5
BJ
34
35#ifdef INET
d0d290ad
WN
36#include "netinet/in.h"
37#include "netinet/in_systm.h"
38#include "netinet/in_var.h"
39#include "netinet/ip.h"
40#include "netinet/if_ether.h"
93b2b8c5
BJ
41#endif
42
43#ifdef NS
d0d290ad
WN
44#include "netns/ns.h"
45#include "netns/ns_if.h"
93b2b8c5
BJ
46#endif
47
d0d290ad
WN
48#include "i386/isa/isa_device.h"
49#include "i386/isa/if_nereg.h"
50#include "i386/isa/icu.h"
93b2b8c5
BJ
51
52int neprobe(), neattach(), neintr();
d0d290ad 53int nestart(),neinit(), ether_output(), neioctl();
93b2b8c5 54
c6b0f337 55struct isa_driver nedriver = {
93b2b8c5
BJ
56 neprobe, neattach, "ne",
57};
58
93b2b8c5
BJ
59struct mbuf *neget();
60
2eaad484
WN
61#define ETHER_MIN_LEN 64
62#define ETHER_MAX_LEN 1536
63
93b2b8c5
BJ
64/*
65 * Ethernet software status per interface.
66 *
67 * Each interface is referenced by a network interface structure,
68 * ns_if, which the routing code uses to locate the interface.
69 * This structure contains the output queue for the interface, its address, ...
70 */
71struct ne_softc {
72 struct arpcom ns_ac; /* Ethernet common part */
73#define ns_if ns_ac.ac_if /* network-visible interface */
74#define ns_addr ns_ac.ac_enaddr /* hardware Ethernet address */
75 int ns_flags;
76#define DSF_LOCK 1 /* block re-entering enstart */
77 int ns_oactive ;
78 int ns_mask ;
79 int ns_ba; /* byte addr in buffer ram of inc pkt */
80 int ns_cur; /* current page being filled */
81 struct prhdr ns_ph; /* hardware header of incoming packet*/
82 struct ether_header ns_eh; /* header of incoming packet */
83 u_char ns_pb[2048 /*ETHERMTU+sizeof(long)*/];
84} ne_softc[NNE] ;
187497c9 85#define ENBUFSIZE (sizeof(struct ether_header) + ETHERMTU + 2 + ETHER_MIN_LEN)
93b2b8c5
BJ
86
87int nec;
88
89u_short boarddata[16];
90
91neprobe(dvp)
c6b0f337 92 struct isa_device *dvp;
93b2b8c5
BJ
93{
94 int val,i,s;
95 register struct ne_softc *ns = &ne_softc[0];
96
97#ifdef lint
98 neintr(0);
99#endif
100
c6b0f337 101 nec = dvp->id_iobase;
93b2b8c5
BJ
102 s = splimp();
103
187497c9 104 /* Reset the bastard */
93b2b8c5
BJ
105 val = inb(nec+ne_reset);
106 DELAY(2000000);
107 outb(nec+ne_reset,val);
108
109 outb(nec+ds_cmd, DSCM_STOP|DSCM_NODMA);
c6b0f337
BJ
110
111 i = 1000000;
d0d290ad 112 while ((inb(nec+ds0_isr)&DSIS_RESET) == 0 && i-- > 0);
c6b0f337
BJ
113 if (i < 0) return (0);
114
93b2b8c5
BJ
115 outb(nec+ds0_isr, 0xff);
116
117 /* Word Transfers, Burst Mode Select, Fifo at 8 bytes */
118 outb(nec+ds0_dcr, DSDC_WTS|DSDC_BMS|DSDC_FT1);
119
120 outb(nec+ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_STOP);
c6b0f337
BJ
121 DELAY(10000);
122
187497c9 123 /* Check cmd reg and fail if not right */
c6b0f337
BJ
124 if ((i=inb(nec+ds_cmd)) != (DSCM_NODMA|DSCM_PG0|DSCM_STOP))
125 return(0);
126
93b2b8c5
BJ
127 outb(nec+ds0_tcr, 0);
128 outb(nec+ds0_rcr, DSRC_MON);
129 outb(nec+ds0_pstart, RBUF/DS_PGSIZE);
130 outb(nec+ds0_pstop, RBUFEND/DS_PGSIZE);
131 outb(nec+ds0_bnry, RBUFEND/DS_PGSIZE);
132 outb(nec+ds0_imr, 0);
133 outb(nec+ds0_isr, 0);
134 outb(nec+ds_cmd, DSCM_NODMA|DSCM_PG1|DSCM_STOP);
135 outb(nec+ds1_curr, RBUF/DS_PGSIZE);
136 outb(nec+ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_STOP);
187497c9 137
c6b0f337 138#ifdef NEDEBUG
187497c9
WN
139#define PAT(n) (0xa55a + 37*(n))
140#define RCON 37
141 { int i, rom, pat;
142
143 rom=1;
144 printf("ne ram ");
145
146 for (i = 0; i < 0xfff0; i+=4) {
147 pat = PAT(i);
148 neput(&pat,i,4);
149 nefetch(&pat,i,4);
150 if (pat == PAT(i)) {
151 if (rom) {
152 rom=0;
153 printf(" %x", i);
154 }
155 } else {
156 if (!rom) {
157 rom=1;
158 printf("..%x ", i);
159 }
160 }
161 pat=0;
162 neput(&pat,i,4);
93b2b8c5 163 }
187497c9 164 printf("\n");
93b2b8c5 165 }
c6b0f337 166#endif
187497c9
WN
167
168 /* Extract board address */
169 nefetch ((caddr_t)boarddata, 0, sizeof(boarddata));
93b2b8c5 170 for(i=0; i < 6; i++) ns->ns_addr[i] = boarddata[i];
93b2b8c5
BJ
171 splx(s);
172 return (1);
173}
174
187497c9
WN
175/*
176 * Fetch from onboard ROM/RAM
177 */
178nefetch (up, ad, len) caddr_t up; {
93b2b8c5
BJ
179 u_char cmd;
180
181 cmd = inb(nec+ds_cmd);
182 outb (nec+ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_START);
187497c9
WN
183
184 /* Setup remote dma */
93b2b8c5 185 outb (nec+ds0_isr, DSIS_RDC);
187497c9
WN
186 outb (nec+ds0_rbcr0, len);
187 outb (nec+ds0_rbcr1, len>>8);
188 outb (nec+ds0_rsar0, ad);
189 outb (nec+ds0_rsar1, ad>>8);
190
191 /* Execute & extract from card */
93b2b8c5
BJ
192 outb (nec+ds_cmd, DSCM_RREAD|DSCM_PG0|DSCM_START);
193 insw (nec+ne_data, up, len/2);
187497c9
WN
194
195 /* Wait till done, then shutdown feature */
196 while ((inb (nec+ds0_isr) & DSIS_RDC) == 0) ;
93b2b8c5
BJ
197 outb (nec+ds0_isr, DSIS_RDC);
198 outb (nec+ds_cmd, cmd);
199}
200
187497c9
WN
201/*
202 * Put to onboard RAM
203 */
204neput (up, ad, len) caddr_t up; {
93b2b8c5
BJ
205 u_char cmd;
206
207 cmd = inb(nec+ds_cmd);
208 outb (nec+ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_START);
187497c9
WN
209
210 /* Setup for remote dma */
93b2b8c5 211 outb (nec+ds0_isr, DSIS_RDC);
187497c9
WN
212 if(len&1) len++; /* roundup to words */
213 outb (nec+ds0_rbcr0, len);
214 outb (nec+ds0_rbcr1, len>>8);
215 outb (nec+ds0_rsar0, ad);
216 outb (nec+ds0_rsar1, ad>>8);
217
218 /* Execute & stuff to card */
93b2b8c5
BJ
219 outb (nec+ds_cmd, DSCM_RWRITE|DSCM_PG0|DSCM_START);
220 outsw (nec+ne_data, up, len/2);
187497c9
WN
221
222 /* Wait till done, then shutdown feature */
223 while ((inb (nec+ds0_isr) & DSIS_RDC) == 0) ;
93b2b8c5
BJ
224 outb (nec+ds0_isr, DSIS_RDC);
225 outb (nec+ds_cmd, cmd);
226}
227
228/*
229 * Reset of interface.
230 */
231nereset(unit, uban)
232 int unit, uban;
233{
234 if (unit >= NNE)
235 return;
236 printf("ne%d: reset\n", unit);
237 ne_softc[unit].ns_flags &= ~DSF_LOCK;
238 neinit(unit);
239}
240
241/*
242 * Interface exists: make available by filling in network interface
243 * record. System will initialize the interface when it is ready
244 * to accept packets. We get the ethernet address here.
245 */
246neattach(dvp)
c6b0f337 247 struct isa_device *dvp;
93b2b8c5 248{
c6b0f337 249 int unit = dvp->id_unit;
93b2b8c5
BJ
250 register struct ne_softc *ns = &ne_softc[unit];
251 register struct ifnet *ifp = &ns->ns_if;
252
253 ifp->if_unit = unit;
254 ifp->if_name = nedriver.name ;
255 ifp->if_mtu = ETHERMTU;
e243739b 256 printf (" ethernet address %s", ether_sprintf(ns->ns_addr)) ;
d0d290ad 257 ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS;
93b2b8c5 258 ifp->if_init = neinit;
d0d290ad
WN
259 ifp->if_output = ether_output;
260 ifp->if_start = nestart;
93b2b8c5
BJ
261 ifp->if_ioctl = neioctl;
262 ifp->if_reset = nereset;
263 ifp->if_watchdog = 0;
264 if_attach(ifp);
265}
266
267/*
268 * Initialization of interface; set up initialization block
269 * and transmit/receive descriptor rings.
270 */
271neinit(unit)
272 int unit;
273{
274 register struct ne_softc *ns = &ne_softc[unit];
275 struct ifnet *ifp = &ns->ns_if;
276 int s;
277 register i; char *cp;
278
279 if (ifp->if_addrlist == (struct ifaddr *)0) return;
280 if (ifp->if_flags & IFF_RUNNING) return;
281
282 s = splimp();
283
284 /* set physical address on ethernet */
285 outb (nec+ds_cmd, DSCM_NODMA|DSCM_PG1|DSCM_STOP);
286 for (i=0 ; i < 6 ; i++) outb(nec+ds1_par0+i,ns->ns_addr[i]);
287
288 /* clr logical address hash filter for now */
289 for (i=0 ; i < 8 ; i++) outb(nec+ds1_mar0+i,0xff);
290
291 /* init regs */
292 outb (nec+ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_STOP);
293 outb (nec+ds0_rbcr0, 0);
294 outb (nec+ds0_rbcr1, 0);
295 outb (nec+ds0_imr, 0);
296 outb (nec+ds0_isr, 0xff);
187497c9 297
93b2b8c5
BJ
298 /* Word Transfers, Burst Mode Select, Fifo at 8 bytes */
299 outb(nec+ds0_dcr, DSDC_WTS|DSDC_BMS|DSDC_FT1);
300 outb(nec+ds0_tcr, 0);
301 outb (nec+ds0_rcr, DSRC_MON);
302 outb (nec+ds0_tpsr, 0);
303 outb(nec+ds0_pstart, RBUF/DS_PGSIZE);
304 outb(nec+ds0_pstop, RBUFEND/DS_PGSIZE);
c67ce870 305 outb(nec+ds0_bnry, RBUF/DS_PGSIZE);
93b2b8c5
BJ
306 outb (nec+ds_cmd, DSCM_NODMA|DSCM_PG1|DSCM_STOP);
307 outb(nec+ds1_curr, RBUF/DS_PGSIZE);
308 ns->ns_cur = RBUF/DS_PGSIZE;
309 outb (nec+ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_START);
310 outb (nec+ds0_rcr, DSRC_AB);
311 outb(nec+ds0_dcr, DSDC_WTS|DSDC_BMS|DSDC_FT1);
312 outb (nec+ds0_imr, 0xff);
313
314 ns->ns_if.if_flags |= IFF_RUNNING;
315 ns->ns_oactive = 0; ns->ns_mask = ~0;
d0d290ad 316 nestart(ifp);
93b2b8c5
BJ
317 splx(s);
318}
319
320/*
321 * Setup output on interface.
322 * Get another datagram to send off of the interface queue,
323 * and map it to the interface before starting the output.
324 * called only at splimp or interrupt level.
325 */
d0d290ad
WN
326nestart(ifp)
327 struct ifnet *ifp;
93b2b8c5 328{
d0d290ad 329 register struct ne_softc *ns = &ne_softc[ifp->if_unit];
93b2b8c5
BJ
330 struct mbuf *m0, *m;
331 int buffer;
d0d290ad 332 int len = 0, i, total,t;
93b2b8c5
BJ
333
334 /*
335 * The DS8390 has only one transmit buffer, if it is busy we
336 * must wait until the transmit interrupt completes.
337 */
338 outb(nec+ds_cmd,DSCM_NODMA|DSCM_START);
339
c6b0f337
BJ
340 if (ns->ns_flags & DSF_LOCK)
341 return;
342
93b2b8c5
BJ
343 if (inb(nec+ds_cmd) & DSCM_TRANS)
344 return;
345
346 if ((ns->ns_if.if_flags & IFF_RUNNING) == 0)
347 return;
348
349 IF_DEQUEUE(&ns->ns_if.if_snd, m);
350
351 if (m == 0)
352 return;
353
354 /*
355 * Copy the mbuf chain into the transmit buffer
356 */
357
c6b0f337 358 ns->ns_flags |= DSF_LOCK; /* prevent entering nestart */
93b2b8c5 359 buffer = TBUF; len = i = 0;
d0d290ad 360 t = 0;
187497c9 361 for (m0 = m; m != 0; m = m->m_next)
d0d290ad 362 t += m->m_len;
d0d290ad
WN
363
364 m = m0;
365 total = t;
d0d290ad
WN
366 for (m0 = m; m != 0; ) {
367
d0d290ad 368 if (m->m_len&1 && t > m->m_len) {
187497c9 369 neput(mtod(m, caddr_t), buffer, m->m_len - 1);
d0d290ad
WN
370 t -= m->m_len - 1;
371 buffer += m->m_len - 1;
372 m->m_data += m->m_len - 1;
373 m->m_len = 1;
374 m = m_pullup(m, 2);
d0d290ad 375 } else {
187497c9 376 neput(mtod(m, caddr_t), buffer, m->m_len);
d0d290ad
WN
377 buffer += m->m_len;
378 t -= m->m_len;
379 MFREE(m, m0);
380 m = m0;
381 }
93b2b8c5 382 }
93b2b8c5
BJ
383
384 /*
385 * Init transmit length registers, and set transmit start flag.
386 */
387
d0d290ad 388 len = total;
187497c9 389 if (len < ETHER_MIN_LEN) len = ETHER_MIN_LEN;
93b2b8c5
BJ
390 outb(nec+ds0_tbcr0,len&0xff);
391 outb(nec+ds0_tbcr1,(len>>8)&0xff);
392 outb(nec+ds0_tpsr, TBUF/DS_PGSIZE);
393 outb(nec+ds_cmd, DSCM_TRANS|DSCM_NODMA|DSCM_START);
394}
395
187497c9 396/* buffer successor/predecessor in ring? */
c67ce870
BJ
397#define succ(n) (((n)+1 >= RBUFEND/DS_PGSIZE) ? RBUF/DS_PGSIZE : (n)+1)
398#define pred(n) (((n)-1 < RBUF/DS_PGSIZE) ? RBUFEND/DS_PGSIZE-1 : (n)-1)
187497c9 399
93b2b8c5
BJ
400/*
401 * Controller interrupt.
402 */
d0d290ad 403neintr(unit)
93b2b8c5 404{
d0d290ad 405 register struct ne_softc *ns = &ne_softc[unit];
93b2b8c5
BJ
406 u_char cmd,isr;
407
187497c9 408 /* Save cmd, clear interrupt */
93b2b8c5 409 cmd = inb (nec+ds_cmd);
c6b0f337 410loop:
93b2b8c5 411 isr = inb (nec+ds0_isr);
c6b0f337 412 outb(nec+ds_cmd,DSCM_NODMA|DSCM_START);
c67ce870 413 outb(nec+ds0_isr, isr);
93b2b8c5 414
187497c9 415 /* Receiver error */
93b2b8c5
BJ
416 if (isr & DSIS_RXE) {
417 /* need to read these registers to clear status */
418 (void) inb(nec+ ds0_rsr);
419 (void) inb(nec+ 0xD);
420 (void) inb(nec + 0xE);
421 (void) inb(nec + 0xF);
422 ns->ns_if.if_ierrors++;
423 }
424
187497c9 425 /* We received something; rummage thru tiny ring buffer */
c67ce870 426 if (isr & (DSIS_RX|DSIS_RXE|DSIS_ROVRN)) {
93b2b8c5
BJ
427 u_char pend,lastfree;
428
429 outb(nec+ds_cmd, DSCM_START|DSCM_NODMA|DSCM_PG1);
430 pend = inb(nec+ds1_curr);
431 outb(nec+ds_cmd, DSCM_START|DSCM_NODMA|DSCM_PG0);
432 lastfree = inb(nec+ds0_bnry);
187497c9
WN
433
434 /* Have we wrapped? */
c67ce870 435 if (lastfree >= RBUFEND/DS_PGSIZE)
93b2b8c5 436 lastfree = RBUF/DS_PGSIZE;
c67ce870
BJ
437 if (pend < lastfree && ns->ns_cur < pend)
438 lastfree = ns->ns_cur;
439 else if (ns->ns_cur > lastfree)
440 lastfree = ns->ns_cur;
441
187497c9 442 /* Something in the buffer? */
c67ce870 443 while (pend != lastfree) {
93b2b8c5
BJ
444 u_char nxt;
445
187497c9
WN
446 /* Extract header from microcephalic board */
447 nefetch(&ns->ns_ph,lastfree*DS_PGSIZE,
c67ce870
BJ
448 sizeof(ns->ns_ph));
449 ns->ns_ba = lastfree*DS_PGSIZE+sizeof(ns->ns_ph);
93b2b8c5 450
187497c9 451 /* Incipient paranoia */
c6b0f337 452 if (ns->ns_ph.pr_status == DSRS_RPC ||
187497c9 453 /* for dequna's */
c6b0f337 454 ns->ns_ph.pr_status == 0x21)
93b2b8c5 455 nerecv (ns);
c6b0f337
BJ
456#ifdef NEDEBUG
457 else {
187497c9
WN
458 printf("cur %x pnd %x lfr %x ",
459 ns->ns_cur, pend, lastfree);
460 printf("nxt %x len %x ", ns->ns_ph.pr_nxtpg,
461 (ns->ns_ph.pr_sz1<<8)+ ns->ns_ph.pr_sz0);
462 printf("Bogus Sts %x\n", ns->ns_ph.pr_status);
c6b0f337
BJ
463 }
464#endif
93b2b8c5
BJ
465
466 nxt = ns->ns_ph.pr_nxtpg ;
187497c9
WN
467
468 /* Sanity check */
469 if ( nxt >= RBUF/DS_PGSIZE && nxt <= RBUFEND/DS_PGSIZE
470 && nxt <= pend)
93b2b8c5
BJ
471 ns->ns_cur = nxt;
472 else ns->ns_cur = nxt = pend;
187497c9
WN
473
474 /* Set the boundaries */
c67ce870
BJ
475 lastfree = nxt;
476 outb(nec+ds0_bnry, pred(nxt));
477 outb(nec+ds_cmd, DSCM_START|DSCM_NODMA|DSCM_PG1);
c6b0f337 478 pend = inb(nec+ds1_curr);
c67ce870 479 outb(nec+ds_cmd, DSCM_START|DSCM_NODMA|DSCM_PG0);
187497c9 480 }
93b2b8c5 481 outb(nec+ds_cmd, DSCM_START|DSCM_NODMA);
93b2b8c5 482 }
187497c9
WN
483
484 /* Transmit error */
93b2b8c5 485 if (isr & DSIS_TXE) {
c67ce870 486 ns->ns_flags &= ~DSF_LOCK;
187497c9 487 /* Need to read these registers to clear status */
93b2b8c5
BJ
488 ns->ns_if.if_collisions += inb(nec+ds0_tbcr0);
489 ns->ns_if.if_oerrors++;
490 }
187497c9
WN
491
492 /* Packet Transmitted */
c6b0f337 493 if (isr & DSIS_TX) {
c67ce870 494 ns->ns_flags &= ~DSF_LOCK;
93b2b8c5 495 ++ns->ns_if.if_opackets;
c67ce870
BJ
496 ns->ns_if.if_collisions += inb(nec+ds0_tbcr0);
497 }
498
187497c9 499 /* Receiver ovverun? */
c67ce870 500 if (isr & DSIS_ROVRN) {
187497c9
WN
501 log(LOG_ERR, "ne%d: error: isr %x\n", ns-ne_softc, isr
502 /*, DSIS_BITS*/);
c67ce870
BJ
503 outb(nec+ds0_rbcr0, 0);
504 outb(nec+ds0_rbcr1, 0);
505 outb(nec+ds0_tcr, DSTC_LB0);
506 outb(nec+ds0_rcr, DSRC_MON);
507 outb(nec+ds_cmd, DSCM_START|DSCM_NODMA);
508 outb(nec+ds0_rcr, DSRC_AB);
509 outb(nec+ds0_tcr, 0);
93b2b8c5 510 }
c6b0f337 511
187497c9 512 /* Any more to send? */
93b2b8c5 513 outb (nec+ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_START);
d0d290ad 514 nestart(&ns->ns_if);
c6b0f337 515 outb (nec+ds_cmd, cmd);
93b2b8c5 516 outb (nec+ds0_imr, 0xff);
187497c9
WN
517
518 /* Still more to do? */
c6b0f337
BJ
519 isr = inb (nec+ds0_isr);
520 if(isr) goto loop;
93b2b8c5
BJ
521}
522
523/*
524 * Ethernet interface receiver interface.
525 * If input error just drop packet.
526 * Otherwise examine packet to determine type. If can't determine length
527 * from type, then have to drop packet. Othewise decapsulate
528 * packet based on type and pass to type specific higher-level
529 * input routine.
530 */
531nerecv(ns)
532 register struct ne_softc *ns;
533{
534 int len,i;
535
536 ns->ns_if.if_ipackets++;
537 len = ns->ns_ph.pr_sz0 + (ns->ns_ph.pr_sz1<<8);
187497c9
WN
538 if(len < ETHER_MIN_LEN || len > ETHER_MAX_LEN)
539 return;
540
541 /* this need not be so torturous - one/two bcopys at most into mbufs */
542 nefetch(ns->ns_pb, ns->ns_ba, min(len,DS_PGSIZE-sizeof(ns->ns_ph)));
543 if (len > DS_PGSIZE-sizeof(ns->ns_ph)) {
93b2b8c5
BJ
544 int l = len - (DS_PGSIZE-sizeof(ns->ns_ph)), b ;
545 u_char *p = ns->ns_pb + (DS_PGSIZE-sizeof(ns->ns_ph));
546
93b2b8c5
BJ
547 if(++ns->ns_cur > 0x7f) ns->ns_cur = 0x46;
548 b = ns->ns_cur*DS_PGSIZE;
549
550 while (l >= DS_PGSIZE) {
187497c9 551 nefetch(p, b, DS_PGSIZE);
93b2b8c5 552 p += DS_PGSIZE; l -= DS_PGSIZE;
187497c9
WN
553 if(++ns->ns_cur > 0x7f) ns->ns_cur = 0x46;
554 b = ns->ns_cur*DS_PGSIZE;
93b2b8c5 555 }
93b2b8c5 556 if (l > 0)
187497c9 557 nefetch(p, b, l);
93b2b8c5 558 }
187497c9
WN
559 /* don't forget checksum! */
560 len -= sizeof(struct ether_header) + sizeof(long);
93b2b8c5 561
93b2b8c5
BJ
562 neread(ns,(caddr_t)(ns->ns_pb), len);
563}
564
565/*
566 * Pass a packet to the higher levels.
567 * We deal with the trailer protocol here.
568 */
569neread(ns, buf, len)
570 register struct ne_softc *ns;
571 char *buf;
572 int len;
573{
574 register struct ether_header *eh;
575 struct mbuf *m;
576 int off, resid;
577 register struct ifqueue *inq;
578
579 /*
580 * Deal with trailer protocol: if type is trailer type
581 * get true type from first 16-bit word past data.
582 * Remember that type was trailer by setting off.
583 */
584 eh = (struct ether_header *)buf;
585 eh->ether_type = ntohs((u_short)eh->ether_type);
586#define nedataaddr(eh, off, type) ((type)(((caddr_t)((eh)+1)+(off))))
587 if (eh->ether_type >= ETHERTYPE_TRAIL &&
588 eh->ether_type < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) {
589 off = (eh->ether_type - ETHERTYPE_TRAIL) * 512;
590 if (off >= ETHERMTU) return; /* sanity */
591 eh->ether_type = ntohs(*nedataaddr(eh, off, u_short *));
592 resid = ntohs(*(nedataaddr(eh, off+2, u_short *)));
593 if (off + resid > len) return; /* sanity */
594 len = off + resid;
595 } else off = 0;
596
597 if (len == 0) return;
598
599 /*
600 * Pull packet off interface. Off is nonzero if packet
601 * has trailing header; neget will then force this header
602 * information to be at the front, but we still have to drop
603 * the type and length which are at the front of any trailer data.
604 */
605 m = neget(buf, len, off, &ns->ns_if);
606 if (m == 0) return;
607
d0d290ad 608 ether_input(&ns->ns_if, eh, m);
93b2b8c5
BJ
609}
610
611/*
612 * Supporting routines
613 */
614
615/*
616 * Pull read data off a interface.
617 * Len is length of data, with local net header stripped.
618 * Off is non-zero if a trailer protocol was used, and
619 * gives the offset of the trailer information.
620 * We copy the trailer information and then all the normal
621 * data into mbufs. When full cluster sized units are present
622 * we copy into clusters.
623 */
624struct mbuf *
625neget(buf, totlen, off0, ifp)
626 caddr_t buf;
627 int totlen, off0;
628 struct ifnet *ifp;
629{
630 struct mbuf *top, **mp, *m, *p;
631 int off = off0, len;
632 register caddr_t cp = buf;
d0d290ad
WN
633 char *epkt;
634
635 buf += sizeof(struct ether_header);
636 cp = buf;
637 epkt = cp + totlen;
638
639
640 if (off) {
641 cp += off + 2 * sizeof(u_short);
642 totlen -= 2 * sizeof(u_short);
643 }
644
645 MGETHDR(m, M_DONTWAIT, MT_DATA);
646 if (m == 0)
647 return (0);
648 m->m_pkthdr.rcvif = ifp;
649 m->m_pkthdr.len = totlen;
650 m->m_len = MHLEN;
93b2b8c5 651
93b2b8c5
BJ
652 top = 0;
653 mp = &top;
654 while (totlen > 0) {
d0d290ad
WN
655 if (top) {
656 MGET(m, M_DONTWAIT, MT_DATA);
657 if (m == 0) {
658 m_freem(top);
659 return (0);
660 }
661 m->m_len = MLEN;
662 }
663 len = min(totlen, epkt - cp);
664 if (len >= MINCLSIZE) {
665 MCLGET(m, M_DONTWAIT);
666 if (m->m_flags & M_EXT)
667 m->m_len = len = min(len, MCLBYTES);
93b2b8c5 668 else
d0d290ad 669 len = m->m_len;
93b2b8c5 670 } else {
93b2b8c5 671 /*
d0d290ad 672 * Place initial small packet/header at end of mbuf.
93b2b8c5 673 */
d0d290ad
WN
674 if (len < m->m_len) {
675 if (top == 0 && len + max_linkhdr <= m->m_len)
676 m->m_data += max_linkhdr;
677 m->m_len = len;
678 } else
679 len = m->m_len;
93b2b8c5 680 }
d0d290ad
WN
681 bcopy(cp, mtod(m, caddr_t), (unsigned)len);
682 cp += len;
93b2b8c5
BJ
683 *mp = m;
684 mp = &m->m_next;
d0d290ad
WN
685 totlen -= len;
686 if (cp == epkt)
687 cp = buf;
93b2b8c5
BJ
688 }
689 return (top);
93b2b8c5
BJ
690}
691
692/*
693 * Process an ioctl request.
694 */
695neioctl(ifp, cmd, data)
696 register struct ifnet *ifp;
697 int cmd;
698 caddr_t data;
699{
700 register struct ifaddr *ifa = (struct ifaddr *)data;
701 struct ne_softc *ns = &ne_softc[ifp->if_unit];
702 struct ifreq *ifr = (struct ifreq *)data;
703 int s = splimp(), error = 0;
704
705
706 switch (cmd) {
707
708 case SIOCSIFADDR:
709 ifp->if_flags |= IFF_UP;
710
d0d290ad 711 switch (ifa->ifa_addr->sa_family) {
93b2b8c5
BJ
712#ifdef INET
713 case AF_INET:
714 neinit(ifp->if_unit); /* before arpwhohas */
715 ((struct arpcom *)ifp)->ac_ipaddr =
716 IA_SIN(ifa)->sin_addr;
717 arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr);
718 break;
719#endif
720#ifdef NS
721 case AF_NS:
722 {
723 register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr);
724
725 if (ns_nullhost(*ina))
726 ina->x_host = *(union ns_host *)(ns->ns_addr);
727 else {
728 /*
729 * The manual says we can't change the address
730 * while the receiver is armed,
731 * so reset everything
732 */
733 ifp->if_flags &= ~IFF_RUNNING;
734 bcopy((caddr_t)ina->x_host.c_host,
735 (caddr_t)ns->ns_addr, sizeof(ns->ns_addr));
736 }
737 neinit(ifp->if_unit); /* does ne_setaddr() */
738 break;
739 }
740#endif
741 default:
742 neinit(ifp->if_unit);
743 break;
744 }
745 break;
746
747 case SIOCSIFFLAGS:
748 if ((ifp->if_flags & IFF_UP) == 0 &&
749 ifp->if_flags & IFF_RUNNING) {
750 ifp->if_flags &= ~IFF_RUNNING;
751 outb(nec+ds_cmd,DSCM_STOP|DSCM_NODMA);
752 } else if (ifp->if_flags & IFF_UP &&
753 (ifp->if_flags & IFF_RUNNING) == 0)
754 neinit(ifp->if_unit);
755 break;
756
757#ifdef notdef
758 case SIOCGHWADDR:
759 bcopy((caddr_t)ns->ns_addr, (caddr_t) &ifr->ifr_data,
760 sizeof(ns->ns_addr));
761 break;
762#endif
763
764 default:
765 error = EINVAL;
766 }
767 splx(s);
768 return (error);
769}
93b2b8c5 770#endif