Commit | Line | Data |
---|---|---|
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 | |
52 | int neprobe(), neattach(), neintr(); | |
d0d290ad | 53 | int nestart(),neinit(), ether_output(), neioctl(); |
93b2b8c5 | 54 | |
c6b0f337 | 55 | struct isa_driver nedriver = { |
93b2b8c5 BJ |
56 | neprobe, neattach, "ne", |
57 | }; | |
58 | ||
93b2b8c5 BJ |
59 | struct 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 | */ | |
71 | struct 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 | |
87 | int nec; | |
88 | ||
89 | u_short boarddata[16]; | |
90 | ||
91 | neprobe(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 | */ | |
178 | nefetch (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 | */ | |
204 | neput (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 | */ | |
231 | nereset(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 | */ | |
246 | neattach(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 | */ | |
271 | neinit(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 |
326 | nestart(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 | 403 | neintr(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 | 410 | loop: |
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 | */ | |
531 | nerecv(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 | */ | |
569 | neread(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 | */ | |
624 | struct mbuf * | |
625 | neget(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 = ⊤ | |
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 | */ | |
695 | neioctl(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 |