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