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