Commit | Line | Data |
---|---|---|
21ac5de2 | 1 | /* |
016ac65c | 2 | * Device driver for National Semiconductor DS8390/WD83C690 based ethernet |
21ac5de2 DG |
3 | * adapters. By David Greenman, 29-April-1993 |
4 | * | |
5 | * Copyright (C) 1993, David Greenman. This software may be used, modified, | |
6 | * copied, distributed, and sold, in both source and binary form provided | |
7 | * that the above copyright and these terms are retained. Under no | |
8 | * circumstances is the author responsible for the proper functioning | |
9 | * of this software, nor does the author assume any responsibility | |
10 | * for damages incurred with its use. | |
11 | * | |
016ac65c | 12 | * Currently supports the Western Digital/SMC 8003 and 8013 series, |
362c9b14 DG |
13 | * the SMC Elite Ultra (8216), the 3Com 3c503, the NE1000 and NE2000, |
14 | * and a variety of similar clones. | |
a24ec720 | 15 | * |
21ac5de2 DG |
16 | */ |
17 | ||
b12abaae | 18 | /* |
fcd1510c | 19 | * $Id: if_ed.c,v 1.35 1994/03/02 05:50:01 davidg Exp $ |
b12abaae DG |
20 | */ |
21 | ||
21ac5de2 DG |
22 | #include "ed.h" |
23 | #if NED > 0 | |
b12abaae | 24 | /* bpfilter included here in case it is needed in future net includes */ |
21ac5de2 DG |
25 | #include "bpfilter.h" |
26 | ||
27 | #include "param.h" | |
d6d150e8 | 28 | #include "systm.h" |
21ac5de2 DG |
29 | #include "errno.h" |
30 | #include "ioctl.h" | |
31 | #include "mbuf.h" | |
32 | #include "socket.h" | |
33 | #include "syslog.h" | |
34 | ||
35 | #include "net/if.h" | |
36 | #include "net/if_dl.h" | |
37 | #include "net/if_types.h" | |
21ac5de2 DG |
38 | |
39 | #ifdef INET | |
40 | #include "netinet/in.h" | |
41 | #include "netinet/in_systm.h" | |
42 | #include "netinet/in_var.h" | |
43 | #include "netinet/ip.h" | |
44 | #include "netinet/if_ether.h" | |
45 | #endif | |
46 | ||
47 | #ifdef NS | |
48 | #include "netns/ns.h" | |
49 | #include "netns/ns_if.h" | |
50 | #endif | |
51 | ||
52 | #if NBPFILTER > 0 | |
53 | #include "net/bpf.h" | |
54 | #include "net/bpfdesc.h" | |
55 | #endif | |
56 | ||
57 | #include "i386/isa/isa.h" | |
58 | #include "i386/isa/isa_device.h" | |
59 | #include "i386/isa/icu.h" | |
60 | #include "i386/isa/if_edreg.h" | |
61 | ||
62 | #include "i386/include/pio.h" | |
63 | ||
4d8228f9 DG |
64 | /* For backwards compatibility */ |
65 | #ifndef IFF_ALTPHYS | |
66 | #define IFF_ALTPHYS IFF_LLC0 | |
67 | #endif | |
21ac5de2 DG |
68 | |
69 | /* | |
70 | * ed_softc: per line info and status | |
71 | */ | |
72 | struct ed_softc { | |
73 | struct arpcom arpcom; /* ethernet common */ | |
74 | ||
75 | char *type_str; /* pointer to type string */ | |
76 | u_char vendor; /* interface vendor */ | |
77 | u_char type; /* interface type code */ | |
78 | ||
21ac5de2 DG |
79 | u_short asic_addr; /* ASIC I/O bus address */ |
80 | u_short nic_addr; /* NIC (DS8390) I/O bus address */ | |
81 | ||
b12abaae DG |
82 | /* |
83 | * The following 'proto' variable is part of a work-around for 8013EBT asics | |
84 | * being write-only. It's sort of a prototype/shadow of the real thing. | |
85 | */ | |
86 | u_char wd_laar_proto; | |
016ac65c | 87 | u_char isa16bit; /* width of access to card 0=8 or 1=16 */ |
d2a46cd2 | 88 | int is790; /* set by the probe code if the card is 790 based */ |
21ac5de2 DG |
89 | |
90 | caddr_t bpf; /* BPF "magic cookie" */ | |
016ac65c DG |
91 | caddr_t mem_start; /* NIC memory start address */ |
92 | caddr_t mem_end; /* NIC memory end address */ | |
93 | u_long mem_size; /* total NIC memory size */ | |
94 | caddr_t mem_ring; /* start of RX ring-buffer (in NIC mem) */ | |
21ac5de2 | 95 | |
016ac65c | 96 | u_char mem_shared; /* NIC memory is shared with host */ |
21ac5de2 | 97 | u_char xmit_busy; /* transmitter is busy */ |
016ac65c DG |
98 | u_char txb_cnt; /* number of transmit buffers */ |
99 | u_char txb_inuse; /* number of TX buffers currently in-use*/ | |
21ac5de2 | 100 | |
016ac65c DG |
101 | u_char txb_new; /* pointer to where new buffer will be added */ |
102 | u_char txb_next_tx; /* pointer to next buffer ready to xmit */ | |
103 | u_short txb_len[8]; /* buffered xmit buffer lengths */ | |
104 | u_char tx_page_start; /* first page of TX buffer area */ | |
21ac5de2 DG |
105 | u_char rec_page_start; /* first page of RX ring-buffer */ |
106 | u_char rec_page_stop; /* last page of RX ring-buffer */ | |
107 | u_char next_packet; /* pointer to next unread RX packet */ | |
108 | } ed_softc[NED]; | |
109 | ||
8164a262 DG |
110 | int ed_attach(struct isa_device *); |
111 | void ed_init(int); | |
112 | void edintr(int); | |
113 | int ed_ioctl(struct ifnet *, int, caddr_t); | |
114 | int ed_probe(struct isa_device *); | |
115 | void ed_start(struct ifnet *); | |
116 | void ed_reset(int, int); | |
117 | void ed_watchdog(int); | |
118 | ||
4c45483e GW |
119 | static void ed_get_packet(struct ed_softc *, char *, int /*u_short*/); |
120 | static void ed_stop(int); | |
21ac5de2 DG |
121 | |
122 | static inline void ed_rint(); | |
123 | static inline void ed_xmit(); | |
124 | static inline char *ed_ring_copy(); | |
125 | ||
016ac65c DG |
126 | void ed_pio_readmem(), ed_pio_writemem(); |
127 | u_short ed_pio_write_mbufs(); | |
128 | ||
21ac5de2 DG |
129 | extern int ether_output(); |
130 | ||
016ac65c DG |
131 | struct trailer_header { |
132 | u_short ether_type; | |
133 | u_short ether_residual; | |
134 | }; | |
135 | ||
21ac5de2 DG |
136 | struct isa_driver eddriver = { |
137 | ed_probe, | |
138 | ed_attach, | |
139 | "ed" | |
140 | }; | |
141 | /* | |
142 | * Interrupt conversion table for WD/SMC ASIC | |
143 | * (IRQ* are defined in icu.h) | |
144 | */ | |
145 | static unsigned short ed_intr_mask[] = { | |
146 | IRQ9, | |
147 | IRQ3, | |
148 | IRQ5, | |
149 | IRQ7, | |
150 | IRQ10, | |
151 | IRQ11, | |
152 | IRQ15, | |
153 | IRQ4 | |
154 | }; | |
155 | ||
d2a46cd2 DG |
156 | /* |
157 | * Interrupt conversion table for 585/790 Combo | |
158 | */ | |
159 | static unsigned short ed_790_intr_mask[] = { | |
160 | 0, | |
161 | IRQ9, | |
162 | IRQ3, | |
d2a46cd2 | 163 | IRQ5, |
0f7b02e3 | 164 | IRQ7, |
d2a46cd2 DG |
165 | IRQ10, |
166 | IRQ11, | |
167 | IRQ15 | |
168 | }; | |
21ac5de2 DG |
169 | #define ETHER_MIN_LEN 64 |
170 | #define ETHER_MAX_LEN 1518 | |
171 | #define ETHER_ADDR_LEN 6 | |
172 | #define ETHER_HDR_SIZE 14 | |
173 | ||
174 | /* | |
175 | * Determine if the device is present | |
176 | * | |
177 | * on entry: | |
178 | * a pointer to an isa_device struct | |
179 | * on exit: | |
180 | * NULL if device not found | |
181 | * or # of i/o addresses used (if found) | |
182 | */ | |
183 | int | |
184 | ed_probe(isa_dev) | |
185 | struct isa_device *isa_dev; | |
186 | { | |
187 | struct ed_softc *sc = &ed_softc[isa_dev->id_unit]; | |
b12abaae | 188 | int nports; |
21ac5de2 | 189 | |
b12abaae DG |
190 | if (nports = ed_probe_WD80x3(isa_dev)) |
191 | return (nports); | |
192 | ||
193 | if (nports = ed_probe_3Com(isa_dev)) | |
194 | return (nports); | |
016ac65c DG |
195 | |
196 | if (nports = ed_probe_Novell(isa_dev)) | |
197 | return (nports); | |
95445baa | 198 | |
d2a46cd2 | 199 | return(0); |
4d8228f9 | 200 | } |
21ac5de2 | 201 | |
016ac65c DG |
202 | /* |
203 | * Generic probe routine for testing for the existance of a DS8390. | |
204 | * Must be called after the NIC has just been reset. This routine | |
205 | * works by looking at certain register values that are gauranteed | |
206 | * to be initialized a certain way after power-up or reset. Seems | |
207 | * not to currently work on the 83C690. | |
208 | * | |
209 | * Specifically: | |
210 | * | |
211 | * Register reset bits set bits | |
212 | * Command Register (CR) TXP, STA RD2, STP | |
213 | * Interrupt Status (ISR) RST | |
214 | * Interrupt Mask (IMR) All bits | |
215 | * Data Control (DCR) LAS | |
216 | * Transmit Config. (TCR) LB1, LB0 | |
217 | * | |
218 | * We only look at the CR and ISR registers, however, because looking at | |
219 | * the others would require changing register pages (which would be | |
220 | * intrusive if this isn't an 8390). | |
221 | * | |
222 | * Return 1 if 8390 was found, 0 if not. | |
223 | */ | |
224 | ||
225 | int | |
226 | ed_probe_generic8390(sc) | |
227 | struct ed_softc *sc; | |
228 | { | |
229 | if ((inb(sc->nic_addr + ED_P0_CR) & | |
362c9b14 DG |
230 | (ED_CR_RD2|ED_CR_TXP|ED_CR_STA|ED_CR_STP)) != |
231 | (ED_CR_RD2|ED_CR_STP)) | |
232 | return (0); | |
016ac65c DG |
233 | if ((inb(sc->nic_addr + ED_P0_ISR) & ED_ISR_RST) != ED_ISR_RST) |
234 | return (0); | |
235 | ||
236 | return(1); | |
237 | } | |
238 | ||
4d8228f9 DG |
239 | /* |
240 | * Probe and vendor-specific initialization routine for SMC/WD80x3 boards | |
241 | */ | |
242 | int | |
243 | ed_probe_WD80x3(isa_dev) | |
244 | struct isa_device *isa_dev; | |
245 | { | |
246 | struct ed_softc *sc = &ed_softc[isa_dev->id_unit]; | |
247 | int i; | |
248 | u_int memsize; | |
b12abaae DG |
249 | u_char iptr, isa16bit, sum; |
250 | ||
251 | sc->asic_addr = isa_dev->id_iobase; | |
252 | sc->nic_addr = sc->asic_addr + ED_WD_NIC_OFFSET; | |
d2a46cd2 | 253 | sc->is790 = 0; |
21ac5de2 | 254 | |
e1d36170 | 255 | #ifdef TOSH_ETHER |
788ee259 | 256 | outb(sc->asic_addr + ED_WD_MSR, ED_WD_MSR_POW); |
e1d36170 AS |
257 | DELAY(10000); |
258 | #endif | |
b12abaae DG |
259 | /* |
260 | * Attempt to do a checksum over the station address PROM. | |
261 | * If it fails, it's probably not a SMC/WD board. There | |
262 | * is a problem with this, though: some clone WD boards | |
263 | * don't pass the checksum test. Danpex boards for one. | |
b12abaae DG |
264 | */ |
265 | for (sum = 0, i = 0; i < 8; ++i) | |
266 | sum += inb(sc->asic_addr + ED_WD_PROM + i); | |
55fe4dc1 DG |
267 | |
268 | if (sum != ED_WD_ROM_CHECKSUM_TOTAL) { | |
269 | /* | |
270 | * Checksum is invalid. This often happens with cheap | |
271 | * WD8003E clones. In this case, the checksum byte | |
272 | * (the eighth byte) seems to always be zero. | |
273 | */ | |
274 | if (inb(sc->asic_addr + ED_WD_CARD_ID) != ED_TYPE_WD8003E || | |
362c9b14 DG |
275 | inb(sc->asic_addr + ED_WD_PROM + 7) != 0) |
276 | return(0); | |
55fe4dc1 DG |
277 | } |
278 | ||
21ac5de2 | 279 | /* reset card to force it into a known state. */ |
e1d36170 | 280 | #ifdef TOSH_ETHER |
788ee259 | 281 | outb(sc->asic_addr + ED_WD_MSR, ED_WD_MSR_RST | ED_WD_MSR_POW); |
e1d36170 | 282 | #else |
21ac5de2 | 283 | outb(sc->asic_addr + ED_WD_MSR, ED_WD_MSR_RST); |
e1d36170 | 284 | #endif |
21ac5de2 DG |
285 | DELAY(100); |
286 | outb(sc->asic_addr + ED_WD_MSR, inb(sc->asic_addr + ED_WD_MSR) & ~ED_WD_MSR_RST); | |
287 | /* wait in the case this card is reading it's EEROM */ | |
288 | DELAY(5000); | |
289 | ||
016ac65c DG |
290 | sc->vendor = ED_VENDOR_WD_SMC; |
291 | sc->type = inb(sc->asic_addr + ED_WD_CARD_ID); | |
292 | ||
21ac5de2 DG |
293 | /* |
294 | * Set initial values for width/size. | |
295 | */ | |
5324facd DG |
296 | memsize = 8192; |
297 | isa16bit = 0; | |
21ac5de2 DG |
298 | switch (sc->type) { |
299 | case ED_TYPE_WD8003S: | |
300 | sc->type_str = "WD8003S"; | |
21ac5de2 DG |
301 | break; |
302 | case ED_TYPE_WD8003E: | |
303 | sc->type_str = "WD8003E"; | |
5324facd DG |
304 | break; |
305 | case ED_TYPE_WD8003EB: | |
306 | sc->type_str = "WD8003EB"; | |
21ac5de2 | 307 | break; |
de9b9c67 DG |
308 | case ED_TYPE_WD8003W: |
309 | sc->type_str = "WD8003W"; | |
de9b9c67 | 310 | break; |
21ac5de2 DG |
311 | case ED_TYPE_WD8013EBT: |
312 | sc->type_str = "WD8013EBT"; | |
313 | memsize = 16384; | |
b12abaae | 314 | isa16bit = 1; |
21ac5de2 | 315 | break; |
8164a262 DG |
316 | case ED_TYPE_WD8013W: |
317 | sc->type_str = "WD8013W"; | |
318 | memsize = 16384; | |
319 | isa16bit = 1; | |
320 | break; | |
4d8228f9 | 321 | case ED_TYPE_WD8013EP: /* also WD8003EP */ |
21ac5de2 | 322 | if (inb(sc->asic_addr + ED_WD_ICR) |
362c9b14 | 323 | & ED_WD_ICR_16BIT) { |
b12abaae | 324 | isa16bit = 1; |
21ac5de2 | 325 | memsize = 16384; |
4d8228f9 | 326 | sc->type_str = "WD8013EP"; |
21ac5de2 | 327 | } else { |
b12abaae | 328 | sc->type_str = "WD8003EP"; |
21ac5de2 DG |
329 | } |
330 | break; | |
4d8228f9 DG |
331 | case ED_TYPE_WD8013WC: |
332 | sc->type_str = "WD8013WC"; | |
333 | memsize = 16384; | |
b12abaae | 334 | isa16bit = 1; |
4d8228f9 | 335 | break; |
21ac5de2 DG |
336 | case ED_TYPE_WD8013EBP: |
337 | sc->type_str = "WD8013EBP"; | |
338 | memsize = 16384; | |
b12abaae | 339 | isa16bit = 1; |
21ac5de2 DG |
340 | break; |
341 | case ED_TYPE_WD8013EPC: | |
342 | sc->type_str = "WD8013EPC"; | |
343 | memsize = 16384; | |
b12abaae | 344 | isa16bit = 1; |
21ac5de2 | 345 | break; |
d2a46cd2 DG |
346 | case ED_TYPE_SMC8216C: |
347 | sc->type_str = "SMC8216/SMC8216C"; | |
348 | memsize = 16384; | |
349 | isa16bit = 1; | |
350 | sc->is790 = 1; | |
351 | break; | |
352 | case ED_TYPE_SMC8216T: | |
353 | sc->type_str = "SMC8216T"; | |
354 | memsize = 16384; | |
355 | isa16bit = 1; | |
356 | sc->is790 = 1; | |
357 | break; | |
e1d36170 AS |
358 | #ifdef TOSH_ETHER |
359 | case ED_TYPE_TOSHIBA1: | |
360 | sc->type_str = "Toshiba1"; | |
361 | memsize = 32768; | |
362 | isa16bit = 1; | |
363 | break; | |
788ee259 AS |
364 | case ED_TYPE_TOSHIBA4: |
365 | sc->type_str = "Toshiba4"; | |
e1d36170 AS |
366 | memsize = 32768; |
367 | isa16bit = 1; | |
368 | break; | |
369 | #endif | |
21ac5de2 | 370 | default: |
b12abaae | 371 | sc->type_str = ""; |
21ac5de2 DG |
372 | break; |
373 | } | |
374 | /* | |
375 | * Make some adjustments to initial values depending on what is | |
376 | * found in the ICR. | |
377 | */ | |
b12abaae | 378 | if (isa16bit && (sc->type != ED_TYPE_WD8013EBT) |
e1d36170 | 379 | #ifdef TOSH_ETHER |
788ee259 | 380 | && (sc->type != ED_TYPE_TOSHIBA1) && (sc->type != ED_TYPE_TOSHIBA4) |
e1d36170 | 381 | #endif |
362c9b14 | 382 | && ((inb(sc->asic_addr + ED_WD_ICR) & ED_WD_ICR_16BIT) == 0)) { |
b12abaae | 383 | isa16bit = 0; |
21ac5de2 DG |
384 | memsize = 8192; |
385 | } | |
21ac5de2 DG |
386 | |
387 | #if ED_DEBUG | |
e1d36170 AS |
388 | printf("type = %x type_str=%s isa16bit=%d memsize=%d id_msize=%d\n", |
389 | sc->type,sc->type_str,isa16bit,memsize,isa_dev->id_msize); | |
21ac5de2 DG |
390 | for (i=0; i<8; i++) |
391 | printf("%x -> %x\n", i, inb(sc->asic_addr + i)); | |
392 | #endif | |
14cdbcbb DG |
393 | /* |
394 | * Allow the user to override the autoconfiguration | |
395 | */ | |
396 | if (isa_dev->id_msize) | |
611ca7df | 397 | memsize = isa_dev->id_msize; |
14cdbcbb DG |
398 | /* |
399 | * (note that if the user specifies both of the following flags | |
400 | * that '8bit' mode intentionally has precedence) | |
401 | */ | |
402 | if (isa_dev->id_flags & ED_FLAGS_FORCE_16BIT_MODE) | |
b12abaae | 403 | isa16bit = 1; |
14cdbcbb | 404 | if (isa_dev->id_flags & ED_FLAGS_FORCE_8BIT_MODE) |
b12abaae | 405 | isa16bit = 0; |
611ca7df | 406 | |
5969b7e9 DG |
407 | /* |
408 | * Check 83C584 interrupt configuration register if this board has one | |
409 | * XXX - we could also check the IO address register. But why | |
410 | * bother...if we get past this, it *has* to be correct. | |
411 | */ | |
d2a46cd2 | 412 | if ((sc->type & ED_WD_SOFTCONFIG) && (!sc->is790)) { |
5969b7e9 DG |
413 | /* |
414 | * Assemble together the encoded interrupt number. | |
415 | */ | |
416 | iptr = (inb(isa_dev->id_iobase + ED_WD_ICR) & ED_WD_ICR_IR2) | | |
fcd1510c DG |
417 | ((inb(isa_dev->id_iobase + ED_WD_IRR) & |
418 | (ED_WD_IRR_IR0 | ED_WD_IRR_IR1)) >> 5); | |
5969b7e9 DG |
419 | /* |
420 | * Translate it using translation table, and check for correctness. | |
421 | */ | |
21ac5de2 | 422 | if (ed_intr_mask[iptr] != isa_dev->id_irq) { |
4d8228f9 | 423 | printf("ed%d: kernel configured irq %d doesn't match board configured irq %d\n", |
fcd1510c DG |
424 | isa_dev->id_unit, ffs(isa_dev->id_irq) - 1, |
425 | ffs(ed_intr_mask[iptr]) - 1); | |
21ac5de2 DG |
426 | return(0); |
427 | } | |
5969b7e9 DG |
428 | /* |
429 | * Enable the interrupt. | |
430 | */ | |
431 | outb(isa_dev->id_iobase + ED_WD_IRR, | |
432 | inb(isa_dev->id_iobase + ED_WD_IRR) | ED_WD_IRR_IEN); | |
21ac5de2 | 433 | } |
d2a46cd2 | 434 | if (sc->is790) { |
fcd1510c DG |
435 | outb(isa_dev->id_iobase + ED_WD790_HWR, |
436 | inb(isa_dev->id_iobase + ED_WD790_HWR) | ED_WD790_HWR_SWH); | |
437 | iptr = (((inb(isa_dev->id_iobase + ED_WD790_GCR) & ED_WD790_GCR_IR2) >> 4) | | |
438 | (inb(isa_dev->id_iobase + ED_WD790_GCR) & | |
439 | (ED_WD790_GCR_IR1|ED_WD790_GCR_IR0)) >> 2); | |
440 | outb(isa_dev->id_iobase + ED_WD790_HWR, | |
441 | inb(isa_dev->id_iobase + ED_WD790_HWR) & ~ED_WD790_HWR_SWH); | |
d2a46cd2 DG |
442 | |
443 | if (ed_790_intr_mask[iptr] != isa_dev->id_irq) { | |
444 | printf("ed%d: kernel configured irq %d doesn't match board configured irq %d %d\n", | |
fcd1510c DG |
445 | isa_dev->id_unit, ffs(isa_dev->id_irq) - 1, |
446 | ffs(ed_790_intr_mask[iptr]) - 1, iptr); | |
d2a46cd2 DG |
447 | return 0; |
448 | } | |
fcd1510c DG |
449 | /* |
450 | * Enable interrupts. | |
451 | */ | |
452 | outb(isa_dev->id_iobase + ED_WD790_ICR, | |
453 | inb(isa_dev->id_iobase + ED_WD790_ICR) | ED_WD790_ICR_EIL); | |
d2a46cd2 | 454 | } |
21ac5de2 | 455 | |
b12abaae | 456 | sc->isa16bit = isa16bit; |
016ac65c DG |
457 | |
458 | #ifdef notyet /* XXX - I'm not sure if PIO mode is even possible on WD/SMC boards */ | |
459 | /* | |
460 | * The following allows the WD/SMC boards to be used in Programmed I/O | |
461 | * mode - without mapping the NIC memory shared. ...Not the prefered | |
462 | * way, but it might be the only way. | |
463 | */ | |
464 | if (isa_dev->id_flags & ED_FLAGS_FORCE_PIO) { | |
465 | sc->mem_shared = 0; | |
466 | isa_dev->id_maddr = 0; | |
467 | } else { | |
468 | sc->mem_shared = 1; | |
469 | } | |
470 | #else | |
471 | sc->mem_shared = 1; | |
472 | #endif | |
473 | isa_dev->id_msize = memsize; | |
474 | ||
475 | sc->mem_start = (caddr_t)isa_dev->id_maddr; | |
b12abaae | 476 | |
21ac5de2 DG |
477 | /* |
478 | * allocate one xmit buffer if < 16k, two buffers otherwise | |
479 | */ | |
016ac65c DG |
480 | if ((memsize < 16384) || (isa_dev->id_flags & ED_FLAGS_NO_MULTI_BUFFERING)) { |
481 | sc->mem_ring = sc->mem_start + (ED_PAGE_SIZE * ED_TXBUF_SIZE); | |
21ac5de2 DG |
482 | sc->txb_cnt = 1; |
483 | sc->rec_page_start = ED_TXBUF_SIZE; | |
484 | } else { | |
016ac65c | 485 | sc->mem_ring = sc->mem_start + (ED_PAGE_SIZE * ED_TXBUF_SIZE * 2); |
21ac5de2 DG |
486 | sc->txb_cnt = 2; |
487 | sc->rec_page_start = ED_TXBUF_SIZE * 2; | |
488 | } | |
016ac65c DG |
489 | sc->mem_size = memsize; |
490 | sc->mem_end = sc->mem_start + memsize; | |
21ac5de2 DG |
491 | sc->rec_page_stop = memsize / ED_PAGE_SIZE; |
492 | sc->tx_page_start = ED_WD_PAGE_OFFSET; | |
493 | ||
494 | /* | |
495 | * Get station address from on-board ROM | |
496 | */ | |
497 | for (i = 0; i < ETHER_ADDR_LEN; ++i) | |
498 | sc->arpcom.ac_enaddr[i] = inb(sc->asic_addr + ED_WD_PROM + i); | |
499 | ||
016ac65c DG |
500 | if (sc->mem_shared) { |
501 | /* | |
502 | * Set address and enable interface shared memory. | |
503 | */ | |
d2a46cd2 | 504 | if(!sc->is790) { |
e1d36170 AS |
505 | #ifdef TOSH_ETHER |
506 | outb(sc->asic_addr + ED_WD_MSR + 1, ((kvtop(sc->mem_start) >> 8) & 0xe0) | 4); | |
507 | outb(sc->asic_addr + ED_WD_MSR + 2, ((kvtop(sc->mem_start) >> 16) & 0x0f)); | |
788ee259 | 508 | outb(sc->asic_addr + ED_WD_MSR, ED_WD_MSR_MENB | ED_WD_MSR_POW); |
e1d36170 AS |
509 | |
510 | #else | |
d2a46cd2 DG |
511 | outb(sc->asic_addr + ED_WD_MSR, ((kvtop(sc->mem_start) >> 13) & |
512 | ED_WD_MSR_ADDR) | ED_WD_MSR_MENB); | |
e1d36170 | 513 | #endif |
d2a46cd2 DG |
514 | } else { |
515 | outb(sc->asic_addr + ED_WD_MSR, ED_WD_MSR_MENB); | |
516 | outb(sc->asic_addr + 0x04, (inb(sc->asic_addr + 0x04) | 0x80)); | |
517 | outb(sc->asic_addr + 0x0b, ((kvtop(sc->mem_start) >> 13) & 0x0f) | | |
362c9b14 DG |
518 | ((kvtop(sc->mem_start) >> 11) & 0x40) | |
519 | (inb(sc->asic_addr + 0x0b) & 0xb0)); | |
d2a46cd2 DG |
520 | outb(sc->asic_addr + 0x04, (inb(sc->asic_addr + 0x04) & ~0x80)); |
521 | } | |
21ac5de2 | 522 | |
016ac65c DG |
523 | /* |
524 | * Set upper address bits and 8/16 bit access to shared memory | |
525 | */ | |
526 | if (isa16bit) { | |
d2a46cd2 DG |
527 | if (sc->is790) { |
528 | sc->wd_laar_proto = inb(sc->asic_addr + ED_WD_LAAR); | |
529 | outb(sc->asic_addr + ED_WD_LAAR, ED_WD_LAAR_M16EN); | |
530 | } else { | |
531 | outb(sc->asic_addr + ED_WD_LAAR, (sc->wd_laar_proto = | |
362c9b14 DG |
532 | ED_WD_LAAR_L16EN | ED_WD_LAAR_M16EN | |
533 | ((kvtop(sc->mem_start) >> 19) & ED_WD_LAAR_ADDRHI))); | |
d2a46cd2 | 534 | } |
016ac65c | 535 | } else { |
362c9b14 | 536 | if ((sc->type & ED_WD_SOFTCONFIG) || |
e1d36170 | 537 | #ifdef TOSH_ETHER |
788ee259 | 538 | (sc->type == ED_TYPE_TOSHIBA1) || (sc->type == ED_TYPE_TOSHIBA4) || |
e1d36170 | 539 | #endif |
362c9b14 | 540 | (sc->type == ED_TYPE_WD8013EBT) && (!sc->is790)) { |
016ac65c | 541 | outb(sc->asic_addr + ED_WD_LAAR, (sc->wd_laar_proto = |
362c9b14 | 542 | ((kvtop(sc->mem_start) >> 19) & ED_WD_LAAR_ADDRHI))); |
016ac65c DG |
543 | } |
544 | } | |
21ac5de2 | 545 | |
016ac65c DG |
546 | /* |
547 | * Now zero memory and verify that it is clear | |
548 | */ | |
549 | bzero(sc->mem_start, memsize); | |
21ac5de2 | 550 | |
016ac65c DG |
551 | for (i = 0; i < memsize; ++i) |
552 | if (sc->mem_start[i]) { | |
553 | printf("ed%d: failed to clear shared memory at %x - check configuration\n", | |
554 | isa_dev->id_unit, kvtop(sc->mem_start + i)); | |
21ac5de2 | 555 | |
016ac65c DG |
556 | /* |
557 | * Disable 16 bit access to shared memory | |
558 | */ | |
559 | if (isa16bit) | |
560 | outb(sc->asic_addr + ED_WD_LAAR, (sc->wd_laar_proto &= | |
362c9b14 | 561 | ~ED_WD_LAAR_M16EN)); |
21ac5de2 | 562 | |
016ac65c DG |
563 | return(0); |
564 | } | |
21ac5de2 | 565 | |
016ac65c DG |
566 | /* |
567 | * Disable 16bit access to shared memory - we leave it disabled so | |
568 | * that 1) machines reboot properly when the board is set | |
569 | * 16 bit mode and there are conflicting 8bit devices/ROMS | |
570 | * in the same 128k address space as this boards shared | |
571 | * memory. and 2) so that other 8 bit devices with shared | |
572 | * memory can be used in this 128k region, too. | |
573 | */ | |
574 | if (isa16bit) | |
575 | outb(sc->asic_addr + ED_WD_LAAR, (sc->wd_laar_proto &= | |
362c9b14 | 576 | ~ED_WD_LAAR_M16EN)); |
016ac65c DG |
577 | |
578 | } | |
21ac5de2 | 579 | |
21ac5de2 | 580 | return (ED_WD_IO_PORTS); |
4d8228f9 | 581 | } |
21ac5de2 | 582 | |
4d8228f9 DG |
583 | /* |
584 | * Probe and vendor-specific initialization routine for 3Com 3c503 boards | |
585 | */ | |
016ac65c | 586 | int |
4d8228f9 DG |
587 | ed_probe_3Com(isa_dev) |
588 | struct isa_device *isa_dev; | |
589 | { | |
590 | struct ed_softc *sc = &ed_softc[isa_dev->id_unit]; | |
591 | int i; | |
592 | u_int memsize; | |
b12abaae | 593 | u_char isa16bit, sum; |
21ac5de2 | 594 | |
21ac5de2 DG |
595 | sc->asic_addr = isa_dev->id_iobase + ED_3COM_ASIC_OFFSET; |
596 | sc->nic_addr = isa_dev->id_iobase + ED_3COM_NIC_OFFSET; | |
597 | ||
21ac5de2 DG |
598 | /* |
599 | * Verify that the kernel configured I/O address matches the board | |
600 | * configured address | |
601 | */ | |
286a528a | 602 | switch (inb(sc->asic_addr + ED_3COM_BCFR)) { |
21ac5de2 DG |
603 | case ED_3COM_BCFR_300: |
604 | if (isa_dev->id_iobase != 0x300) | |
605 | return(0); | |
606 | break; | |
607 | case ED_3COM_BCFR_310: | |
608 | if (isa_dev->id_iobase != 0x310) | |
609 | return(0); | |
610 | break; | |
611 | case ED_3COM_BCFR_330: | |
612 | if (isa_dev->id_iobase != 0x330) | |
613 | return(0); | |
614 | break; | |
615 | case ED_3COM_BCFR_350: | |
616 | if (isa_dev->id_iobase != 0x350) | |
617 | return(0); | |
618 | break; | |
619 | case ED_3COM_BCFR_250: | |
620 | if (isa_dev->id_iobase != 0x250) | |
621 | return(0); | |
622 | break; | |
623 | case ED_3COM_BCFR_280: | |
624 | if (isa_dev->id_iobase != 0x280) | |
625 | return(0); | |
626 | break; | |
627 | case ED_3COM_BCFR_2A0: | |
628 | if (isa_dev->id_iobase != 0x2a0) | |
629 | return(0); | |
630 | break; | |
631 | case ED_3COM_BCFR_2E0: | |
632 | if (isa_dev->id_iobase != 0x2e0) | |
633 | return(0); | |
634 | break; | |
14622cd9 DG |
635 | default: |
636 | return(0); | |
21ac5de2 DG |
637 | } |
638 | ||
639 | /* | |
640 | * Verify that the kernel shared memory address matches the | |
641 | * board configured address. | |
642 | */ | |
286a528a | 643 | switch (inb(sc->asic_addr + ED_3COM_PCFR)) { |
21ac5de2 | 644 | case ED_3COM_PCFR_DC000: |
286a528a | 645 | if (kvtop(isa_dev->id_maddr) != 0xdc000) |
21ac5de2 DG |
646 | return(0); |
647 | break; | |
648 | case ED_3COM_PCFR_D8000: | |
286a528a | 649 | if (kvtop(isa_dev->id_maddr) != 0xd8000) |
21ac5de2 DG |
650 | return(0); |
651 | break; | |
652 | case ED_3COM_PCFR_CC000: | |
286a528a | 653 | if (kvtop(isa_dev->id_maddr) != 0xcc000) |
21ac5de2 DG |
654 | return(0); |
655 | break; | |
656 | case ED_3COM_PCFR_C8000: | |
286a528a | 657 | if (kvtop(isa_dev->id_maddr) != 0xc8000) |
21ac5de2 DG |
658 | return(0); |
659 | break; | |
14622cd9 DG |
660 | default: |
661 | return(0); | |
21ac5de2 DG |
662 | } |
663 | ||
b12abaae | 664 | |
21ac5de2 | 665 | /* |
b12abaae DG |
666 | * Reset NIC and ASIC. Enable on-board transceiver throughout reset |
667 | * sequence because it'll lock up if the cable isn't connected | |
668 | * if we don't. | |
21ac5de2 | 669 | */ |
4d8228f9 DG |
670 | outb(sc->asic_addr + ED_3COM_CR, ED_3COM_CR_RST | ED_3COM_CR_XSEL); |
671 | ||
21ac5de2 DG |
672 | /* |
673 | * Wait for a while, then un-reset it | |
674 | */ | |
4d8228f9 DG |
675 | DELAY(50); |
676 | /* | |
677 | * The 3Com ASIC defaults to rather strange settings for the CR after | |
678 | * a reset - it's important to set it again after the following | |
679 | * outb (this is done when we map the PROM below). | |
680 | */ | |
681 | outb(sc->asic_addr + ED_3COM_CR, ED_3COM_CR_XSEL); | |
21ac5de2 DG |
682 | |
683 | /* | |
ac7f64c9 | 684 | * Wait a bit for the NIC to recover from the reset |
21ac5de2 | 685 | */ |
016ac65c DG |
686 | DELAY(5000); |
687 | ||
688 | sc->vendor = ED_VENDOR_3COM; | |
689 | sc->type_str = "3c503"; | |
690 | ||
691 | sc->mem_shared = 1; | |
692 | ||
693 | /* | |
694 | * Hmmm...a 16bit 3Com board has 16k of memory, but only an 8k | |
695 | * window to it. | |
696 | */ | |
697 | memsize = 8192; | |
21ac5de2 DG |
698 | |
699 | /* | |
4d8228f9 DG |
700 | * Get station address from on-board ROM |
701 | */ | |
702 | /* | |
703 | * First, map ethernet address PROM over the top of where the NIC | |
704 | * registers normally appear. | |
705 | */ | |
706 | outb(sc->asic_addr + ED_3COM_CR, ED_3COM_CR_EALO | ED_3COM_CR_XSEL); | |
707 | ||
708 | for (i = 0; i < ETHER_ADDR_LEN; ++i) | |
709 | sc->arpcom.ac_enaddr[i] = inb(sc->nic_addr + i); | |
710 | ||
711 | /* | |
712 | * Unmap PROM - select NIC registers. The proper setting of the | |
713 | * tranceiver is set in ed_init so that the attach code | |
714 | * is given a chance to set the default based on a compile-time | |
715 | * config option | |
21ac5de2 | 716 | */ |
4d8228f9 | 717 | outb(sc->asic_addr + ED_3COM_CR, ED_3COM_CR_XSEL); |
21ac5de2 DG |
718 | |
719 | /* | |
720 | * Determine if this is an 8bit or 16bit board | |
721 | */ | |
ac7f64c9 | 722 | |
21ac5de2 DG |
723 | /* |
724 | * select page 0 registers | |
725 | */ | |
726 | outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STP); | |
ac7f64c9 | 727 | |
21ac5de2 DG |
728 | /* |
729 | * Attempt to clear WTS bit. If it doesn't clear, then this is a | |
730 | * 16bit board. | |
731 | */ | |
732 | outb(sc->nic_addr + ED_P0_DCR, 0); | |
ac7f64c9 | 733 | |
21ac5de2 DG |
734 | /* |
735 | * select page 2 registers | |
736 | */ | |
737 | outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_2|ED_CR_RD2|ED_CR_STP); | |
ac7f64c9 | 738 | |
21ac5de2 DG |
739 | /* |
740 | * The 3c503 forces the WTS bit to a one if this is a 16bit board | |
741 | */ | |
742 | if (inb(sc->nic_addr + ED_P2_DCR) & ED_DCR_WTS) | |
b12abaae | 743 | isa16bit = 1; |
21ac5de2 | 744 | else |
b12abaae | 745 | isa16bit = 0; |
21ac5de2 | 746 | |
21ac5de2 DG |
747 | /* |
748 | * select page 0 registers | |
749 | */ | |
ac7f64c9 | 750 | outb(sc->nic_addr + ED_P2_CR, ED_CR_RD2|ED_CR_STP); |
21ac5de2 | 751 | |
016ac65c DG |
752 | sc->mem_start = (caddr_t)isa_dev->id_maddr; |
753 | sc->mem_size = memsize; | |
754 | sc->mem_end = sc->mem_start + memsize; | |
21ac5de2 | 755 | |
016ac65c DG |
756 | /* |
757 | * We have an entire 8k window to put the transmit buffers on the | |
758 | * 16bit boards. But since the 16bit 3c503's shared memory | |
759 | * is only fast enough to overlap the loading of one full-size | |
760 | * packet, trying to load more than 2 buffers can actually | |
761 | * leave the transmitter idle during the load. So 2 seems | |
762 | * the best value. (Although a mix of variable-sized packets | |
763 | * might change this assumption. Nonetheless, we optimize for | |
764 | * linear transfers of same-size packets.) | |
765 | */ | |
766 | if (isa16bit) { | |
767 | if (isa_dev->id_flags & ED_FLAGS_NO_MULTI_BUFFERING) | |
768 | sc->txb_cnt = 1; | |
769 | else | |
770 | sc->txb_cnt = 2; | |
771 | ||
772 | sc->tx_page_start = ED_3COM_TX_PAGE_OFFSET_16BIT; | |
773 | sc->rec_page_start = ED_3COM_RX_PAGE_OFFSET_16BIT; | |
774 | sc->rec_page_stop = memsize / ED_PAGE_SIZE + | |
775 | ED_3COM_RX_PAGE_OFFSET_16BIT; | |
776 | sc->mem_ring = sc->mem_start; | |
777 | } else { | |
778 | sc->txb_cnt = 1; | |
779 | sc->tx_page_start = ED_3COM_TX_PAGE_OFFSET_8BIT; | |
780 | sc->rec_page_start = ED_TXBUF_SIZE + ED_3COM_TX_PAGE_OFFSET_8BIT; | |
781 | sc->rec_page_stop = memsize / ED_PAGE_SIZE + | |
782 | ED_3COM_TX_PAGE_OFFSET_8BIT; | |
783 | sc->mem_ring = sc->mem_start + (ED_PAGE_SIZE * ED_TXBUF_SIZE); | |
784 | } | |
21ac5de2 | 785 | |
b12abaae | 786 | sc->isa16bit = isa16bit; |
21ac5de2 DG |
787 | |
788 | /* | |
789 | * Initialize GA page start/stop registers. Probably only needed | |
790 | * if doing DMA, but what the hell. | |
791 | */ | |
792 | outb(sc->asic_addr + ED_3COM_PSTR, sc->rec_page_start); | |
793 | outb(sc->asic_addr + ED_3COM_PSPR, sc->rec_page_stop); | |
794 | ||
795 | /* | |
796 | * Set IRQ. 3c503 only allows a choice of irq 2-5. | |
797 | */ | |
798 | switch (isa_dev->id_irq) { | |
799 | case IRQ2: | |
800 | outb(sc->asic_addr + ED_3COM_IDCFR, ED_3COM_IDCFR_IRQ2); | |
801 | break; | |
802 | case IRQ3: | |
803 | outb(sc->asic_addr + ED_3COM_IDCFR, ED_3COM_IDCFR_IRQ3); | |
804 | break; | |
805 | case IRQ4: | |
806 | outb(sc->asic_addr + ED_3COM_IDCFR, ED_3COM_IDCFR_IRQ4); | |
807 | break; | |
808 | case IRQ5: | |
809 | outb(sc->asic_addr + ED_3COM_IDCFR, ED_3COM_IDCFR_IRQ5); | |
810 | break; | |
811 | default: | |
4d8228f9 DG |
812 | printf("ed%d: Invalid irq configuration (%d) must be 2-5 for 3c503\n", |
813 | isa_dev->id_unit, ffs(isa_dev->id_irq) - 1); | |
21ac5de2 DG |
814 | return(0); |
815 | } | |
816 | ||
817 | /* | |
016ac65c | 818 | * Initialize GA configuration register. Set bank and enable shared mem. |
21ac5de2 DG |
819 | */ |
820 | outb(sc->asic_addr + ED_3COM_GACFR, ED_3COM_GACFR_RSEL | | |
821 | ED_3COM_GACFR_MBS0); | |
822 | ||
823 | /* | |
824 | * Initialize "Vector Pointer" registers. These gawd-awful things | |
825 | * are compared to 20 bits of the address on ISA, and if they | |
826 | * match, the shared memory is disabled. We set them to | |
827 | * 0xffff0...allegedly the reset vector. | |
828 | */ | |
829 | outb(sc->asic_addr + ED_3COM_VPTR2, 0xff); | |
830 | outb(sc->asic_addr + ED_3COM_VPTR1, 0xff); | |
831 | outb(sc->asic_addr + ED_3COM_VPTR0, 0x00); | |
832 | ||
ac7f64c9 DG |
833 | /* |
834 | * Zero memory and verify that it is clear | |
835 | */ | |
016ac65c | 836 | bzero(sc->mem_start, memsize); |
21ac5de2 DG |
837 | |
838 | for (i = 0; i < memsize; ++i) | |
016ac65c | 839 | if (sc->mem_start[i]) { |
21ac5de2 | 840 | printf("ed%d: failed to clear shared memory at %x - check configuration\n", |
016ac65c | 841 | isa_dev->id_unit, kvtop(sc->mem_start + i)); |
21ac5de2 DG |
842 | return(0); |
843 | } | |
844 | ||
845 | isa_dev->id_msize = memsize; | |
846 | return(ED_3COM_IO_PORTS); | |
847 | } | |
016ac65c DG |
848 | |
849 | /* | |
850 | * Probe and vendor-specific initialization routine for NE1000/2000 boards | |
851 | */ | |
852 | int | |
853 | ed_probe_Novell(isa_dev) | |
854 | struct isa_device *isa_dev; | |
855 | { | |
856 | struct ed_softc *sc = &ed_softc[isa_dev->id_unit]; | |
857 | u_int memsize, n; | |
858 | u_char romdata[16], isa16bit = 0, tmp; | |
859 | static char test_pattern[32] = "THIS is A memory TEST pattern"; | |
860 | char test_buffer[32]; | |
861 | ||
862 | sc->asic_addr = isa_dev->id_iobase + ED_NOVELL_ASIC_OFFSET; | |
863 | sc->nic_addr = isa_dev->id_iobase + ED_NOVELL_NIC_OFFSET; | |
864 | ||
865 | /* XXX - do Novell-specific probe here */ | |
866 | ||
867 | /* Reset the board */ | |
868 | tmp = inb(sc->asic_addr + ED_NOVELL_RESET); | |
869 | ||
016ac65c DG |
870 | /* |
871 | * I don't know if this is necessary; probably cruft leftover from | |
872 | * Clarkson packet driver code. Doesn't do a thing on the boards | |
9cfebed5 DG |
873 | * I've tested. -DG [note that a outb(0x84, 0) seems to work |
874 | * here, and is non-invasive...but some boards don't seem to reset | |
875 | * and I don't have complete documentation on what the 'right' | |
876 | * thing to do is...so we do the invasive thing for now. Yuck.] | |
016ac65c DG |
877 | */ |
878 | outb(sc->asic_addr + ED_NOVELL_RESET, tmp); | |
879 | DELAY(5000); | |
016ac65c | 880 | |
7fdaa9b3 DG |
881 | /* |
882 | * This is needed because some NE clones apparently don't reset the | |
883 | * NIC properly (or the NIC chip doesn't reset fully on power-up) | |
9cfebed5 DG |
884 | * XXX - this makes the probe invasive! ...Done against my better |
885 | * judgement. -DLG | |
7fdaa9b3 DG |
886 | */ |
887 | outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STP); | |
016ac65c | 888 | |
9cfebed5 DG |
889 | DELAY(5000); |
890 | ||
891 | /* Make sure that we really have an 8390 based board */ | |
892 | if (!ed_probe_generic8390(sc)) | |
893 | return(0); | |
894 | ||
016ac65c DG |
895 | sc->vendor = ED_VENDOR_NOVELL; |
896 | sc->mem_shared = 0; | |
897 | isa_dev->id_maddr = 0; | |
898 | ||
899 | /* | |
900 | * Test the ability to read and write to the NIC memory. This has | |
901 | * the side affect of determining if this is an NE1000 or an NE2000. | |
902 | */ | |
903 | ||
904 | /* | |
905 | * This prevents packets from being stored in the NIC memory when | |
906 | * the readmem routine turns on the start bit in the CR. | |
907 | */ | |
908 | outb(sc->nic_addr + ED_P0_RCR, ED_RCR_MON); | |
909 | ||
910 | /* Temporarily initialize DCR for byte operations */ | |
911 | outb(sc->nic_addr + ED_P0_DCR, ED_DCR_FT1|ED_DCR_LS); | |
912 | ||
913 | outb(sc->nic_addr + ED_P0_PSTART, 8192 / ED_PAGE_SIZE); | |
914 | outb(sc->nic_addr + ED_P0_PSTOP, 16384 / ED_PAGE_SIZE); | |
915 | ||
916 | sc->isa16bit = 0; | |
917 | ||
918 | /* | |
919 | * Write a test pattern in byte mode. If this fails, then there | |
920 | * probably isn't any memory at 8k - which likely means | |
921 | * that the board is an NE2000. | |
922 | */ | |
923 | ed_pio_writemem(sc, test_pattern, 8192, sizeof(test_pattern)); | |
924 | ed_pio_readmem(sc, 8192, test_buffer, sizeof(test_pattern)); | |
925 | ||
926 | if (bcmp(test_pattern, test_buffer, sizeof(test_pattern))) { | |
927 | /* not an NE1000 - try NE2000 */ | |
928 | ||
362c9b14 | 929 | outb(sc->nic_addr + ED_P0_DCR, ED_DCR_WTS|ED_DCR_FT1|ED_DCR_LS); |
016ac65c DG |
930 | outb(sc->nic_addr + ED_P0_PSTART, 16384 / ED_PAGE_SIZE); |
931 | outb(sc->nic_addr + ED_P0_PSTOP, 32768 / ED_PAGE_SIZE); | |
932 | ||
933 | sc->isa16bit = 1; | |
934 | /* | |
935 | * Write a test pattern in word mode. If this also fails, then | |
936 | * we don't know what this board is. | |
937 | */ | |
938 | ed_pio_writemem(sc, test_pattern, 16384, sizeof(test_pattern)); | |
939 | ed_pio_readmem(sc, 16384, test_buffer, sizeof(test_pattern)); | |
940 | ||
941 | if (bcmp(test_pattern, test_buffer, sizeof(test_pattern))) | |
942 | return(0); /* not an NE2000 either */ | |
943 | ||
944 | sc->type = ED_TYPE_NE2000; | |
945 | sc->type_str = "NE2000"; | |
946 | } else { | |
947 | sc->type = ED_TYPE_NE1000; | |
948 | sc->type_str = "NE1000"; | |
949 | } | |
950 | ||
951 | /* 8k of memory plus an additional 8k if 16bit */ | |
952 | memsize = 8192 + sc->isa16bit * 8192; | |
953 | ||
954 | #if 0 /* probably not useful - NE boards only come two ways */ | |
955 | /* allow kernel config file overrides */ | |
956 | if (isa_dev->id_msize) | |
957 | memsize = isa_dev->id_msize; | |
958 | #endif | |
959 | ||
960 | sc->mem_size = memsize; | |
961 | ||
962 | /* NIC memory doesn't start at zero on an NE board */ | |
963 | /* The start address is tied to the bus width */ | |
964 | sc->mem_start = (char *) 8192 + sc->isa16bit * 8192; | |
965 | sc->mem_end = sc->mem_start + memsize; | |
966 | sc->tx_page_start = memsize / ED_PAGE_SIZE; | |
967 | ||
968 | /* | |
969 | * Use one xmit buffer if < 16k, two buffers otherwise (if not told | |
970 | * otherwise). | |
971 | */ | |
972 | if ((memsize < 16384) || (isa_dev->id_flags & ED_FLAGS_NO_MULTI_BUFFERING)) | |
973 | sc->txb_cnt = 1; | |
974 | else | |
975 | sc->txb_cnt = 2; | |
976 | ||
977 | sc->rec_page_start = sc->tx_page_start + sc->txb_cnt * ED_TXBUF_SIZE; | |
978 | sc->rec_page_stop = sc->tx_page_start + memsize / ED_PAGE_SIZE; | |
979 | ||
980 | sc->mem_ring = sc->mem_start + sc->txb_cnt * ED_PAGE_SIZE * ED_TXBUF_SIZE; | |
981 | ||
982 | ed_pio_readmem(sc, 0, romdata, 16); | |
a23f6e67 | 983 | for (n = 0; n < ETHER_ADDR_LEN; n++) |
016ac65c DG |
984 | sc->arpcom.ac_enaddr[n] = romdata[n*(sc->isa16bit+1)]; |
985 | ||
986 | /* clear any pending interrupts that might have occurred above */ | |
987 | outb(sc->nic_addr + ED_P0_ISR, 0xff); | |
988 | ||
989 | return(ED_NOVELL_IO_PORTS); | |
990 | } | |
21ac5de2 DG |
991 | |
992 | /* | |
993 | * Install interface into kernel networking data structures | |
994 | */ | |
995 | int | |
996 | ed_attach(isa_dev) | |
997 | struct isa_device *isa_dev; | |
998 | { | |
999 | struct ed_softc *sc = &ed_softc[isa_dev->id_unit]; | |
1000 | struct ifnet *ifp = &sc->arpcom.ac_if; | |
1001 | struct ifaddr *ifa; | |
1002 | struct sockaddr_dl *sdl; | |
1003 | ||
1004 | /* | |
1005 | * Set interface to stopped condition (reset) | |
1006 | */ | |
1007 | ed_stop(isa_dev->id_unit); | |
1008 | ||
1009 | /* | |
1010 | * Initialize ifnet structure | |
1011 | */ | |
1012 | ifp->if_unit = isa_dev->id_unit; | |
1013 | ifp->if_name = "ed" ; | |
1014 | ifp->if_mtu = ETHERMTU; | |
21ac5de2 DG |
1015 | ifp->if_init = ed_init; |
1016 | ifp->if_output = ether_output; | |
1017 | ifp->if_start = ed_start; | |
1018 | ifp->if_ioctl = ed_ioctl; | |
1019 | ifp->if_reset = ed_reset; | |
1020 | ifp->if_watchdog = ed_watchdog; | |
1021 | ||
ac7f64c9 | 1022 | /* |
4d8228f9 | 1023 | * Set default state for ALTPHYS flag (used to disable the tranceiver |
ac7f64c9 DG |
1024 | * for AUI operation), based on compile-time config option. |
1025 | */ | |
1026 | if (isa_dev->id_flags & ED_FLAGS_DISABLE_TRANCEIVER) | |
362c9b14 DG |
1027 | ifp->if_flags = |
1028 | (IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS | IFF_ALTPHYS); | |
ac7f64c9 DG |
1029 | else |
1030 | ifp->if_flags = (IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS); | |
1031 | ||
5969b7e9 DG |
1032 | /* |
1033 | * Attach the interface | |
1034 | */ | |
21ac5de2 DG |
1035 | if_attach(ifp); |
1036 | ||
21ac5de2 DG |
1037 | /* |
1038 | * Search down the ifa address list looking for the AF_LINK type entry | |
1039 | */ | |
1040 | ifa = ifp->if_addrlist; | |
1041 | while ((ifa != 0) && (ifa->ifa_addr != 0) && | |
1042 | (ifa->ifa_addr->sa_family != AF_LINK)) | |
1043 | ifa = ifa->ifa_next; | |
21ac5de2 | 1044 | /* |
5969b7e9 DG |
1045 | * If we find an AF_LINK type entry we fill in the hardware address. |
1046 | * This is useful for netstat(1) to keep track of which interface | |
1047 | * is which. | |
21ac5de2 DG |
1048 | */ |
1049 | if ((ifa != 0) && (ifa->ifa_addr != 0)) { | |
1050 | /* | |
ac7f64c9 | 1051 | * Fill in the link-level address for this interface |
21ac5de2 DG |
1052 | */ |
1053 | sdl = (struct sockaddr_dl *)ifa->ifa_addr; | |
1054 | sdl->sdl_type = IFT_ETHER; | |
1055 | sdl->sdl_alen = ETHER_ADDR_LEN; | |
1056 | sdl->sdl_slen = 0; | |
1057 | bcopy(sc->arpcom.ac_enaddr, LLADDR(sdl), ETHER_ADDR_LEN); | |
1058 | } | |
1059 | ||
1060 | /* | |
1061 | * Print additional info when attached | |
1062 | */ | |
b12abaae DG |
1063 | printf("ed%d: address %s, ", isa_dev->id_unit, |
1064 | ether_sprintf(sc->arpcom.ac_enaddr)); | |
1065 | ||
1066 | if (sc->type_str && (*sc->type_str != 0)) | |
1067 | printf("type %s ", sc->type_str); | |
1068 | else | |
1069 | printf("type unknown (0x%x) ", sc->type); | |
1070 | ||
1071 | printf("%s ",sc->isa16bit ? "(16 bit)" : "(8 bit)"); | |
1072 | ||
1073 | printf("%s\n", ((sc->vendor == ED_VENDOR_3COM) && | |
788ee259 | 1074 | (ifp->if_flags & IFF_ALTPHYS)) ? " tranceiver disabled" : ""); |
14cdbcbb DG |
1075 | |
1076 | /* | |
1077 | * If BPF is in the kernel, call the attach for it | |
1078 | */ | |
1079 | #if NBPFILTER > 0 | |
1080 | bpfattach(&sc->bpf, ifp, DLT_EN10MB, sizeof(struct ether_header)); | |
1081 | #endif | |
4c45483e | 1082 | return 1; |
21ac5de2 DG |
1083 | } |
1084 | ||
1085 | /* | |
1086 | * Reset interface. | |
1087 | */ | |
4c45483e GW |
1088 | void |
1089 | ed_reset(unit, uban) | |
21ac5de2 | 1090 | int unit; |
4c45483e | 1091 | int uban; /* XXX */ |
21ac5de2 DG |
1092 | { |
1093 | int s; | |
1094 | ||
d2a46cd2 | 1095 | s = splimp(); |
21ac5de2 DG |
1096 | |
1097 | /* | |
1098 | * Stop interface and re-initialize. | |
1099 | */ | |
1100 | ed_stop(unit); | |
1101 | ed_init(unit); | |
1102 | ||
dd18dc33 | 1103 | (void) splx(s); |
21ac5de2 DG |
1104 | } |
1105 | ||
1106 | /* | |
1107 | * Take interface offline. | |
1108 | */ | |
1109 | void | |
1110 | ed_stop(unit) | |
1111 | int unit; | |
1112 | { | |
1113 | struct ed_softc *sc = &ed_softc[unit]; | |
1114 | int n = 5000; | |
1115 | ||
1116 | /* | |
1117 | * Stop everything on the interface, and select page 0 registers. | |
1118 | */ | |
d2a46cd2 DG |
1119 | if (sc->is790) { |
1120 | outb(sc->nic_addr + ED_P0_CR, ED_CR_STP); | |
1121 | } else { | |
1122 | outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STP); | |
1123 | } | |
21ac5de2 DG |
1124 | /* |
1125 | * Wait for interface to enter stopped state, but limit # of checks | |
1126 | * to 'n' (about 5ms). It shouldn't even take 5us on modern | |
1127 | * DS8390's, but just in case it's an old one. | |
1128 | */ | |
d6d150e8 DG |
1129 | while (((inb(sc->nic_addr + ED_P0_ISR) & ED_ISR_RST) == 0) && --n); |
1130 | ||
21ac5de2 DG |
1131 | } |
1132 | ||
5969b7e9 DG |
1133 | /* |
1134 | * Device timeout/watchdog routine. Entered if the device neglects to | |
1135 | * generate an interrupt after a transmit has been started on it. | |
1136 | */ | |
4c45483e | 1137 | void |
21ac5de2 DG |
1138 | ed_watchdog(unit) |
1139 | int unit; | |
1140 | { | |
9cfebed5 DG |
1141 | struct ed_softc *sc = &ed_softc[unit]; |
1142 | ||
21ac5de2 | 1143 | log(LOG_ERR, "ed%d: device timeout\n", unit); |
9cfebed5 | 1144 | ++sc->arpcom.ac_if.if_oerrors; |
21ac5de2 | 1145 | |
4c45483e | 1146 | ed_reset(unit, 0); |
21ac5de2 DG |
1147 | } |
1148 | ||
1149 | /* | |
1150 | * Initialize device. | |
1151 | */ | |
4c45483e | 1152 | void |
21ac5de2 DG |
1153 | ed_init(unit) |
1154 | int unit; | |
1155 | { | |
1156 | struct ed_softc *sc = &ed_softc[unit]; | |
1157 | struct ifnet *ifp = &sc->arpcom.ac_if; | |
1158 | int i, s; | |
1159 | u_char command; | |
1160 | ||
1161 | ||
1162 | /* address not known */ | |
1163 | if (ifp->if_addrlist == (struct ifaddr *)0) return; | |
1164 | ||
1165 | /* | |
1166 | * Initialize the NIC in the exact order outlined in the NS manual. | |
1167 | * This init procedure is "mandatory"...don't change what or when | |
1168 | * things happen. | |
1169 | */ | |
d2a46cd2 | 1170 | s = splimp(); |
21ac5de2 DG |
1171 | |
1172 | /* reset transmitter flags */ | |
21ac5de2 DG |
1173 | sc->xmit_busy = 0; |
1174 | sc->arpcom.ac_if.if_timer = 0; | |
1175 | ||
016ac65c DG |
1176 | sc->txb_inuse = 0; |
1177 | sc->txb_new = 0; | |
1178 | sc->txb_next_tx = 0; | |
21ac5de2 DG |
1179 | |
1180 | /* This variable is used below - don't move this assignment */ | |
1181 | sc->next_packet = sc->rec_page_start + 1; | |
1182 | ||
1183 | /* | |
1184 | * Set interface for page 0, Remote DMA complete, Stopped | |
1185 | */ | |
d2a46cd2 DG |
1186 | if (sc->is790) { |
1187 | outb(sc->nic_addr + ED_P0_CR, ED_CR_STP); | |
1188 | } else { | |
1189 | outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STP); | |
1190 | } | |
b12abaae | 1191 | if (sc->isa16bit) { |
21ac5de2 DG |
1192 | /* |
1193 | * Set FIFO threshold to 8, No auto-init Remote DMA, | |
b6a07cb6 | 1194 | * byte order=80x86, word-wide DMA xfers, |
21ac5de2 | 1195 | */ |
b6a07cb6 | 1196 | outb(sc->nic_addr + ED_P0_DCR, ED_DCR_FT1|ED_DCR_WTS|ED_DCR_LS); |
21ac5de2 DG |
1197 | } else { |
1198 | /* | |
1199 | * Same as above, but byte-wide DMA xfers | |
1200 | */ | |
b6a07cb6 | 1201 | outb(sc->nic_addr + ED_P0_DCR, ED_DCR_FT1|ED_DCR_LS); |
21ac5de2 DG |
1202 | } |
1203 | ||
1204 | /* | |
1205 | * Clear Remote Byte Count Registers | |
1206 | */ | |
1207 | outb(sc->nic_addr + ED_P0_RBCR0, 0); | |
1208 | outb(sc->nic_addr + ED_P0_RBCR1, 0); | |
1209 | ||
1210 | /* | |
1211 | * Enable reception of broadcast packets | |
1212 | */ | |
1213 | outb(sc->nic_addr + ED_P0_RCR, ED_RCR_AB); | |
1214 | ||
1215 | /* | |
1216 | * Place NIC in internal loopback mode | |
1217 | */ | |
1218 | outb(sc->nic_addr + ED_P0_TCR, ED_TCR_LB0); | |
1219 | ||
1220 | /* | |
1221 | * Initialize transmit/receive (ring-buffer) Page Start | |
1222 | */ | |
1223 | outb(sc->nic_addr + ED_P0_TPSR, sc->tx_page_start); | |
1224 | outb(sc->nic_addr + ED_P0_PSTART, sc->rec_page_start); | |
d2a46cd2 DG |
1225 | /* Set lower bits of byte addressable framing to 0 */ |
1226 | if (sc->is790) | |
1227 | outb(sc->nic_addr + 0x09, 0); | |
21ac5de2 DG |
1228 | |
1229 | /* | |
1230 | * Initialize Receiver (ring-buffer) Page Stop and Boundry | |
1231 | */ | |
1232 | outb(sc->nic_addr + ED_P0_PSTOP, sc->rec_page_stop); | |
1233 | outb(sc->nic_addr + ED_P0_BNRY, sc->rec_page_start); | |
1234 | ||
1235 | /* | |
1236 | * Clear all interrupts. A '1' in each bit position clears the | |
1237 | * corresponding flag. | |
1238 | */ | |
1239 | outb(sc->nic_addr + ED_P0_ISR, 0xff); | |
1240 | ||
1241 | /* | |
1242 | * Enable the following interrupts: receive/transmit complete, | |
1243 | * receive/transmit error, and Receiver OverWrite. | |
1244 | * | |
1245 | * Counter overflow and Remote DMA complete are *not* enabled. | |
1246 | */ | |
1247 | outb(sc->nic_addr + ED_P0_IMR, | |
362c9b14 | 1248 | ED_IMR_PRXE|ED_IMR_PTXE|ED_IMR_RXEE|ED_IMR_TXEE|ED_IMR_OVWE); |
21ac5de2 DG |
1249 | |
1250 | /* | |
1251 | * Program Command Register for page 1 | |
1252 | */ | |
d2a46cd2 DG |
1253 | if (sc->is790) { |
1254 | outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_1|ED_CR_STP); | |
1255 | } else { | |
1256 | outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_1|ED_CR_RD2|ED_CR_STP); | |
1257 | } | |
21ac5de2 DG |
1258 | /* |
1259 | * Copy out our station address | |
1260 | */ | |
1261 | for (i = 0; i < ETHER_ADDR_LEN; ++i) | |
1262 | outb(sc->nic_addr + ED_P1_PAR0 + i, sc->arpcom.ac_enaddr[i]); | |
1263 | ||
1264 | #if NBPFILTER > 0 | |
1265 | /* | |
1266 | * Initialize multicast address hashing registers to accept | |
1267 | * all multicasts (only used when in promiscuous mode) | |
1268 | */ | |
1269 | for (i = 0; i < 8; ++i) | |
1270 | outb(sc->nic_addr + ED_P1_MAR0 + i, 0xff); | |
1271 | #endif | |
1272 | ||
1273 | /* | |
1274 | * Set Current Page pointer to next_packet (initialized above) | |
1275 | */ | |
1276 | outb(sc->nic_addr + ED_P1_CURR, sc->next_packet); | |
1277 | ||
1278 | /* | |
1279 | * Set Command Register for page 0, Remote DMA complete, | |
1280 | * and interface Start. | |
1281 | */ | |
d2a46cd2 DG |
1282 | if (sc->is790) { |
1283 | outb(sc->nic_addr + ED_P1_CR, ED_CR_STA); | |
1284 | } else { | |
1285 | outb(sc->nic_addr + ED_P1_CR, ED_CR_RD2|ED_CR_STA); | |
1286 | } | |
21ac5de2 DG |
1287 | /* |
1288 | * Take interface out of loopback | |
1289 | */ | |
1290 | outb(sc->nic_addr + ED_P0_TCR, 0); | |
1291 | ||
ac7f64c9 DG |
1292 | /* |
1293 | * If this is a 3Com board, the tranceiver must be software enabled | |
1294 | * (there is no settable hardware default). | |
1295 | */ | |
9f2aff44 | 1296 | if (sc->vendor == ED_VENDOR_3COM) { |
4d8228f9 | 1297 | if (ifp->if_flags & IFF_ALTPHYS) { |
9f2aff44 DG |
1298 | outb(sc->asic_addr + ED_3COM_CR, 0); |
1299 | } else { | |
1300 | outb(sc->asic_addr + ED_3COM_CR, ED_3COM_CR_XSEL); | |
1301 | } | |
ac7f64c9 DG |
1302 | } |
1303 | ||
21ac5de2 DG |
1304 | /* |
1305 | * Set 'running' flag, and clear output active flag. | |
1306 | */ | |
1307 | ifp->if_flags |= IFF_RUNNING; | |
1308 | ifp->if_flags &= ~IFF_OACTIVE; | |
1309 | ||
1310 | /* | |
1311 | * ...and attempt to start output | |
1312 | */ | |
1313 | ed_start(ifp); | |
1314 | ||
1315 | (void) splx(s); | |
1316 | } | |
1317 | ||
5969b7e9 DG |
1318 | /* |
1319 | * This routine actually starts the transmission on the interface | |
1320 | */ | |
21ac5de2 DG |
1321 | static inline void ed_xmit(ifp) |
1322 | struct ifnet *ifp; | |
1323 | { | |
1324 | struct ed_softc *sc = &ed_softc[ifp->if_unit]; | |
016ac65c DG |
1325 | unsigned short len; |
1326 | ||
1327 | len = sc->txb_len[sc->txb_next_tx]; | |
21ac5de2 DG |
1328 | |
1329 | /* | |
1330 | * Set NIC for page 0 register access | |
1331 | */ | |
d2a46cd2 DG |
1332 | if (sc->is790) { |
1333 | outb(sc->nic_addr + ED_P0_CR, ED_CR_STA); | |
1334 | } else { | |
1335 | outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STA); | |
1336 | } | |
21ac5de2 DG |
1337 | /* |
1338 | * Set TX buffer start page | |
1339 | */ | |
1340 | outb(sc->nic_addr + ED_P0_TPSR, sc->tx_page_start + | |
016ac65c | 1341 | sc->txb_next_tx * ED_TXBUF_SIZE); |
21ac5de2 DG |
1342 | |
1343 | /* | |
1344 | * Set TX length | |
1345 | */ | |
016ac65c | 1346 | outb(sc->nic_addr + ED_P0_TBCR0, len); |
21ac5de2 DG |
1347 | outb(sc->nic_addr + ED_P0_TBCR1, len >> 8); |
1348 | ||
1349 | /* | |
5969b7e9 | 1350 | * Set page 0, Remote DMA complete, Transmit Packet, and *Start* |
21ac5de2 | 1351 | */ |
d2a46cd2 DG |
1352 | if (sc->is790) { |
1353 | outb(sc->nic_addr + ED_P0_CR, ED_CR_TXP | ED_CR_STA); | |
1354 | } else { | |
1355 | outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_TXP|ED_CR_STA); | |
1356 | } | |
21ac5de2 | 1357 | sc->xmit_busy = 1; |
21ac5de2 | 1358 | |
5969b7e9 | 1359 | /* |
016ac65c | 1360 | * Point to next transmit buffer slot and wrap if necessary. |
5969b7e9 | 1361 | */ |
016ac65c DG |
1362 | sc->txb_next_tx++; |
1363 | if (sc->txb_next_tx == sc->txb_cnt) | |
1364 | sc->txb_next_tx = 0; | |
21ac5de2 DG |
1365 | |
1366 | /* | |
1367 | * Set a timer just in case we never hear from the board again | |
1368 | */ | |
1369 | ifp->if_timer = 2; | |
1370 | } | |
1371 | ||
1372 | /* | |
1373 | * Start output on interface. | |
1374 | * We make two assumptions here: | |
d2a46cd2 | 1375 | * 1) that the current priority is set to splimp _before_ this code |
21ac5de2 DG |
1376 | * is called *and* is returned to the appropriate priority after |
1377 | * return | |
1378 | * 2) that the IFF_OACTIVE flag is checked before this code is called | |
1379 | * (i.e. that the output part of the interface is idle) | |
1380 | */ | |
4c45483e | 1381 | void |
21ac5de2 DG |
1382 | ed_start(ifp) |
1383 | struct ifnet *ifp; | |
1384 | { | |
1385 | struct ed_softc *sc = &ed_softc[ifp->if_unit]; | |
1386 | struct mbuf *m0, *m; | |
1387 | caddr_t buffer; | |
1388 | int len; | |
21ac5de2 DG |
1389 | |
1390 | outloop: | |
5969b7e9 | 1391 | /* |
016ac65c DG |
1392 | * First, see if there are buffered packets and an idle |
1393 | * transmitter - should never happen at this point. | |
5969b7e9 | 1394 | */ |
016ac65c DG |
1395 | if (sc->txb_inuse && (sc->xmit_busy == 0)) { |
1396 | printf("ed: packets buffers, but transmitter idle\n"); | |
1397 | ed_xmit(ifp); | |
1398 | } | |
1399 | ||
1400 | /* | |
1401 | * See if there is room to put another packet in the buffer. | |
1402 | */ | |
1403 | if (sc->txb_inuse == sc->txb_cnt) { | |
1404 | /* | |
1405 | * No room. Indicate this to the outside world | |
1406 | * and exit. | |
1407 | */ | |
1408 | ifp->if_flags |= IFF_OACTIVE; | |
1409 | return; | |
1410 | } | |
21ac5de2 DG |
1411 | |
1412 | IF_DEQUEUE(&sc->arpcom.ac_if.if_snd, m); | |
1413 | if (m == 0) { | |
1414 | /* | |
016ac65c DG |
1415 | * We are using the !OACTIVE flag to indicate to the outside |
1416 | * world that we can accept an additional packet rather than | |
1417 | * that the transmitter is _actually_ active. Indeed, the | |
1418 | * transmitter may be active, but if we haven't filled all | |
1419 | * the buffers with data then we still want to accept more. | |
21ac5de2 DG |
1420 | */ |
1421 | ifp->if_flags &= ~IFF_OACTIVE; | |
1422 | return; | |
1423 | } | |
1424 | ||
1425 | /* | |
1426 | * Copy the mbuf chain into the transmit buffer | |
1427 | */ | |
21ac5de2 | 1428 | |
a23f6e67 DG |
1429 | m0 = m; |
1430 | ||
016ac65c DG |
1431 | /* txb_new points to next open buffer slot */ |
1432 | buffer = sc->mem_start + (sc->txb_new * ED_TXBUF_SIZE * ED_PAGE_SIZE); | |
21ac5de2 | 1433 | |
016ac65c DG |
1434 | if (sc->mem_shared) { |
1435 | /* | |
1436 | * Special case setup for 16 bit boards... | |
1437 | */ | |
1438 | if (sc->isa16bit) { | |
1439 | switch (sc->vendor) { | |
1440 | /* | |
1441 | * For 16bit 3Com boards (which have 16k of memory), | |
1442 | * we have the xmit buffers in a different page | |
1443 | * of memory ('page 0') - so change pages. | |
1444 | */ | |
1445 | case ED_VENDOR_3COM: | |
1446 | outb(sc->asic_addr + ED_3COM_GACFR, | |
362c9b14 | 1447 | ED_3COM_GACFR_RSEL); |
016ac65c DG |
1448 | break; |
1449 | /* | |
1450 | * Enable 16bit access to shared memory on WD/SMC boards | |
1451 | * Don't update wd_laar_proto because we want to restore the | |
1452 | * previous state (because an arp reply in the input code | |
1453 | * may cause a call-back to ed_start) | |
1454 | * XXX - the call-back to 'start' is a bug, IMHO. | |
1455 | */ | |
fcd1510c | 1456 | case ED_VENDOR_WD_SMC: { |
016ac65c | 1457 | outb(sc->asic_addr + ED_WD_LAAR, |
362c9b14 | 1458 | (sc->wd_laar_proto | ED_WD_LAAR_M16EN)); |
fcd1510c DG |
1459 | if (sc->is790) |
1460 | outb(sc->asic_addr + ED_WD_MSR, ED_WD_MSR_MENB); | |
1461 | break; | |
1462 | } | |
016ac65c DG |
1463 | } |
1464 | } | |
21ac5de2 | 1465 | |
a23f6e67 | 1466 | for (len = 0; m != 0; m = m->m_next) { |
016ac65c DG |
1467 | bcopy(mtod(m, caddr_t), buffer, m->m_len); |
1468 | buffer += m->m_len; | |
1469 | len += m->m_len; | |
1470 | } | |
21ac5de2 | 1471 | |
21ac5de2 | 1472 | /* |
016ac65c | 1473 | * Restore previous shared memory access |
21ac5de2 | 1474 | */ |
016ac65c DG |
1475 | if (sc->isa16bit) { |
1476 | switch (sc->vendor) { | |
1477 | case ED_VENDOR_3COM: | |
1478 | outb(sc->asic_addr + ED_3COM_GACFR, | |
362c9b14 | 1479 | ED_3COM_GACFR_RSEL | ED_3COM_GACFR_MBS0); |
016ac65c | 1480 | break; |
fcd1510c | 1481 | case ED_VENDOR_WD_SMC: { |
016ac65c | 1482 | outb(sc->asic_addr + ED_WD_LAAR, sc->wd_laar_proto); |
fcd1510c DG |
1483 | if (sc->is790) |
1484 | outb(sc->asic_addr + ED_WD_MSR, 0x00); | |
016ac65c | 1485 | break; |
fcd1510c | 1486 | } |
016ac65c DG |
1487 | } |
1488 | } | |
1489 | } else { | |
a23f6e67 | 1490 | len = ed_pio_write_mbufs(sc, m, buffer); |
016ac65c DG |
1491 | } |
1492 | ||
1493 | sc->txb_len[sc->txb_new] = MAX(len, ETHER_MIN_LEN); | |
1494 | ||
1495 | sc->txb_inuse++; | |
1496 | ||
1497 | /* | |
1498 | * Point to next buffer slot and wrap if necessary. | |
1499 | */ | |
1500 | sc->txb_new++; | |
1501 | if (sc->txb_new == sc->txb_cnt) | |
1502 | sc->txb_new = 0; | |
21ac5de2 DG |
1503 | |
1504 | if (sc->xmit_busy == 0) | |
1505 | ed_xmit(ifp); | |
1506 | /* | |
1507 | * If there is BPF support in the configuration, tap off here. | |
1508 | * The following has support for converting trailer packets | |
1509 | * back to normal. | |
b12abaae DG |
1510 | * XXX - support for trailer packets in BPF should be moved into |
1511 | * the bpf code proper to avoid code duplication in all of | |
1512 | * the drivers. | |
21ac5de2 DG |
1513 | */ |
1514 | #if NBPFILTER > 0 | |
1515 | if (sc->bpf) { | |
1516 | u_short etype; | |
1517 | int off, datasize, resid; | |
1518 | struct ether_header *eh; | |
016ac65c | 1519 | struct trailer_header trailer_header; |
21ac5de2 DG |
1520 | char ether_packet[ETHER_MAX_LEN]; |
1521 | char *ep; | |
1522 | ||
1523 | ep = ether_packet; | |
1524 | ||
1525 | /* | |
1526 | * We handle trailers below: | |
1527 | * Copy ether header first, then residual data, | |
1528 | * then data. Put all this in a temporary buffer | |
1529 | * 'ether_packet' and send off to bpf. Since the | |
1530 | * system has generated this packet, we assume | |
1531 | * that all of the offsets in the packet are | |
1532 | * correct; if they're not, the system will almost | |
1533 | * certainly crash in m_copydata. | |
1534 | * We make no assumptions about how the data is | |
1535 | * arranged in the mbuf chain (i.e. how much | |
1536 | * data is in each mbuf, if mbuf clusters are | |
1537 | * used, etc.), which is why we use m_copydata | |
1538 | * to get the ether header rather than assume | |
1539 | * that this is located in the first mbuf. | |
1540 | */ | |
1541 | /* copy ether header */ | |
1542 | m_copydata(m0, 0, sizeof(struct ether_header), ep); | |
1543 | eh = (struct ether_header *) ep; | |
1544 | ep += sizeof(struct ether_header); | |
1545 | etype = ntohs(eh->ether_type); | |
1546 | if (etype >= ETHERTYPE_TRAIL && | |
1547 | etype < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) { | |
1548 | datasize = ((etype - ETHERTYPE_TRAIL) << 9); | |
1549 | off = datasize + sizeof(struct ether_header); | |
1550 | ||
1551 | /* copy trailer_header into a data structure */ | |
1552 | m_copydata(m0, off, sizeof(struct trailer_header), | |
362c9b14 | 1553 | (caddr_t)&trailer_header.ether_type); |
21ac5de2 DG |
1554 | |
1555 | /* copy residual data */ | |
1556 | m_copydata(m0, off+sizeof(struct trailer_header), | |
362c9b14 DG |
1557 | resid = ntohs(trailer_header.ether_residual) - |
1558 | sizeof(struct trailer_header), ep); | |
21ac5de2 DG |
1559 | ep += resid; |
1560 | ||
1561 | /* copy data */ | |
1562 | m_copydata(m0, sizeof(struct ether_header), | |
362c9b14 | 1563 | datasize, ep); |
21ac5de2 DG |
1564 | ep += datasize; |
1565 | ||
1566 | /* restore original ether packet type */ | |
1567 | eh->ether_type = trailer_header.ether_type; | |
1568 | ||
1569 | bpf_tap(sc->bpf, ether_packet, ep - ether_packet); | |
1570 | } else | |
1571 | bpf_mtap(sc->bpf, m0); | |
1572 | } | |
1573 | #endif | |
1574 | ||
1575 | m_freem(m0); | |
1576 | ||
5969b7e9 | 1577 | /* |
016ac65c | 1578 | * Loop back to the top to possibly buffer more packets |
5969b7e9 | 1579 | */ |
016ac65c | 1580 | goto outloop; |
21ac5de2 DG |
1581 | } |
1582 | ||
1583 | /* | |
1584 | * Ethernet interface receiver interrupt. | |
1585 | */ | |
b12abaae | 1586 | static inline void |
21ac5de2 DG |
1587 | ed_rint(unit) |
1588 | int unit; | |
1589 | { | |
1590 | register struct ed_softc *sc = &ed_softc[unit]; | |
1591 | u_char boundry, current; | |
1592 | u_short len; | |
016ac65c DG |
1593 | struct ed_ring packet_hdr; |
1594 | char *packet_ptr; | |
21ac5de2 DG |
1595 | |
1596 | /* | |
1597 | * Set NIC to page 1 registers to get 'current' pointer | |
1598 | */ | |
d2a46cd2 DG |
1599 | if (sc->is790) { |
1600 | outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_1|ED_CR_STA); | |
1601 | } else { | |
1602 | outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_1|ED_CR_RD2|ED_CR_STA); | |
1603 | } | |
21ac5de2 DG |
1604 | /* |
1605 | * 'sc->next_packet' is the logical beginning of the ring-buffer - i.e. | |
1606 | * it points to where new data has been buffered. The 'CURR' | |
1607 | * (current) register points to the logical end of the ring-buffer | |
1608 | * - i.e. it points to where additional new data will be added. | |
1609 | * We loop here until the logical beginning equals the logical | |
1610 | * end (or in other words, until the ring-buffer is empty). | |
1611 | */ | |
1612 | while (sc->next_packet != inb(sc->nic_addr + ED_P1_CURR)) { | |
1613 | ||
016ac65c DG |
1614 | /* get pointer to this buffer's header structure */ |
1615 | packet_ptr = sc->mem_ring + | |
362c9b14 | 1616 | (sc->next_packet - sc->rec_page_start) * ED_PAGE_SIZE; |
21ac5de2 DG |
1617 | |
1618 | /* | |
1619 | * The byte count includes the FCS - Frame Check Sequence (a | |
1620 | * 32 bit CRC). | |
1621 | */ | |
016ac65c DG |
1622 | if (sc->mem_shared) |
1623 | packet_hdr = *(struct ed_ring *)packet_ptr; | |
1624 | else | |
1625 | ed_pio_readmem(sc, packet_ptr, (char *) &packet_hdr, | |
362c9b14 | 1626 | sizeof(packet_hdr)); |
016ac65c | 1627 | len = packet_hdr.count; |
21ac5de2 DG |
1628 | if ((len >= ETHER_MIN_LEN) && (len <= ETHER_MAX_LEN)) { |
1629 | /* | |
1630 | * Go get packet. len - 4 removes CRC from length. | |
21ac5de2 | 1631 | */ |
016ac65c | 1632 | ed_get_packet(sc, packet_ptr + 4, len - 4); |
21ac5de2 DG |
1633 | ++sc->arpcom.ac_if.if_ipackets; |
1634 | } else { | |
1635 | /* | |
1636 | * Really BAD...probably indicates that the ring pointers | |
1637 | * are corrupted. Also seen on early rev chips under | |
1638 | * high load - the byte order of the length gets switched. | |
1639 | */ | |
1640 | log(LOG_ERR, | |
362c9b14 DG |
1641 | "ed%d: NIC memory corrupt - invalid packet length %d\n", |
1642 | unit, len); | |
9cfebed5 | 1643 | ++sc->arpcom.ac_if.if_ierrors; |
4c45483e | 1644 | ed_reset(unit, 0); |
21ac5de2 DG |
1645 | return; |
1646 | } | |
1647 | ||
1648 | /* | |
1649 | * Update next packet pointer | |
1650 | */ | |
016ac65c | 1651 | sc->next_packet = packet_hdr.next_packet; |
21ac5de2 DG |
1652 | |
1653 | /* | |
1654 | * Update NIC boundry pointer - being careful to keep it | |
1655 | * one buffer behind. (as recommended by NS databook) | |
1656 | */ | |
1657 | boundry = sc->next_packet - 1; | |
1658 | if (boundry < sc->rec_page_start) | |
1659 | boundry = sc->rec_page_stop - 1; | |
1660 | ||
1661 | /* | |
1662 | * Set NIC to page 0 registers to update boundry register | |
1663 | */ | |
d2a46cd2 DG |
1664 | if (sc->is790) { |
1665 | outb(sc->nic_addr + ED_P0_CR, ED_CR_STA); | |
1666 | } else { | |
1667 | outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STA); | |
1668 | } | |
21ac5de2 DG |
1669 | outb(sc->nic_addr + ED_P0_BNRY, boundry); |
1670 | ||
1671 | /* | |
1672 | * Set NIC to page 1 registers before looping to top (prepare to | |
1673 | * get 'CURR' current pointer) | |
1674 | */ | |
d2a46cd2 DG |
1675 | if (sc->is790) { |
1676 | outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_1|ED_CR_STA); | |
1677 | } else { | |
1678 | outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_1|ED_CR_RD2|ED_CR_STA); | |
1679 | } | |
21ac5de2 DG |
1680 | } |
1681 | } | |
1682 | ||
1683 | /* | |
1684 | * Ethernet interface interrupt processor | |
1685 | */ | |
4c45483e | 1686 | void |
21ac5de2 DG |
1687 | edintr(unit) |
1688 | int unit; | |
1689 | { | |
1690 | struct ed_softc *sc = &ed_softc[unit]; | |
1691 | u_char isr; | |
1692 | ||
1693 | /* | |
1694 | * Set NIC to page 0 registers | |
1695 | */ | |
d2a46cd2 DG |
1696 | if (sc->is790) { |
1697 | outb(sc->nic_addr + ED_P0_CR, ED_CR_STA); | |
1698 | } else { | |
1699 | outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STA); | |
1700 | } | |
21ac5de2 DG |
1701 | /* |
1702 | * loop until there are no more new interrupts | |
1703 | */ | |
1704 | while (isr = inb(sc->nic_addr + ED_P0_ISR)) { | |
1705 | ||
1706 | /* | |
b12abaae | 1707 | * reset all the bits that we are 'acknowledging' |
21ac5de2 DG |
1708 | * by writing a '1' to each bit position that was set |
1709 | * (writing a '1' *clears* the bit) | |
1710 | */ | |
1711 | outb(sc->nic_addr + ED_P0_ISR, isr); | |
1712 | ||
1713 | /* | |
b12abaae DG |
1714 | * Handle transmitter interrupts. Handle these first |
1715 | * because the receiver will reset the board under | |
1716 | * some conditions. | |
21ac5de2 | 1717 | */ |
b12abaae | 1718 | if (isr & (ED_ISR_PTX|ED_ISR_TXE)) { |
d2a46cd2 | 1719 | u_char collisions = inb(sc->nic_addr + ED_P0_NCR) & 0x0f; |
21ac5de2 DG |
1720 | |
1721 | /* | |
b12abaae DG |
1722 | * Check for transmit error. If a TX completed with an |
1723 | * error, we end up throwing the packet away. Really | |
1724 | * the only error that is possible is excessive | |
1725 | * collisions, and in this case it is best to allow the | |
1726 | * automatic mechanisms of TCP to backoff the flow. Of | |
1727 | * course, with UDP we're screwed, but this is expected | |
1728 | * when a network is heavily loaded. | |
21ac5de2 | 1729 | */ |
d2a46cd2 | 1730 | (void) inb(sc->nic_addr + ED_P0_TSR); |
b12abaae DG |
1731 | if (isr & ED_ISR_TXE) { |
1732 | ||
21ac5de2 | 1733 | /* |
b12abaae | 1734 | * Excessive collisions (16) |
21ac5de2 | 1735 | */ |
b12abaae | 1736 | if ((inb(sc->nic_addr + ED_P0_TSR) & ED_TSR_ABT) |
362c9b14 | 1737 | && (collisions == 0)) { |
b12abaae DG |
1738 | /* |
1739 | * When collisions total 16, the | |
1740 | * P0_NCR will indicate 0, and the | |
1741 | * TSR_ABT is set. | |
1742 | */ | |
1743 | collisions = 16; | |
1744 | } | |
21ac5de2 | 1745 | |
b12abaae DG |
1746 | /* |
1747 | * update output errors counter | |
1748 | */ | |
1749 | ++sc->arpcom.ac_if.if_oerrors; | |
1750 | } else { | |
1751 | /* | |
1752 | * Update total number of successfully | |
1753 | * transmitted packets. | |
1754 | */ | |
1755 | ++sc->arpcom.ac_if.if_opackets; | |
1756 | } | |
21ac5de2 DG |
1757 | |
1758 | /* | |
1759 | * reset tx busy and output active flags | |
1760 | */ | |
1761 | sc->xmit_busy = 0; | |
1762 | sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE; | |
1763 | ||
1764 | /* | |
5969b7e9 | 1765 | * clear watchdog timer |
21ac5de2 DG |
1766 | */ |
1767 | sc->arpcom.ac_if.if_timer = 0; | |
21ac5de2 | 1768 | |
21ac5de2 | 1769 | /* |
b12abaae DG |
1770 | * Add in total number of collisions on last |
1771 | * transmission. | |
21ac5de2 | 1772 | */ |
b12abaae DG |
1773 | sc->arpcom.ac_if.if_collisions += collisions; |
1774 | ||
1775 | /* | |
016ac65c DG |
1776 | * Decrement buffer in-use count if not zero (can only |
1777 | * be zero if a transmitter interrupt occured while | |
1778 | * not actually transmitting). | |
b12abaae DG |
1779 | * If data is ready to transmit, start it transmitting, |
1780 | * otherwise defer until after handling receiver | |
1781 | */ | |
016ac65c | 1782 | if (sc->txb_inuse && --sc->txb_inuse) |
b12abaae | 1783 | ed_xmit(&sc->arpcom.ac_if); |
21ac5de2 DG |
1784 | } |
1785 | ||
1786 | /* | |
b12abaae | 1787 | * Handle receiver interrupts |
21ac5de2 | 1788 | */ |
b12abaae DG |
1789 | if (isr & (ED_ISR_PRX|ED_ISR_RXE|ED_ISR_OVW)) { |
1790 | /* | |
1791 | * Overwrite warning. In order to make sure that a lockup | |
1792 | * of the local DMA hasn't occurred, we reset and | |
1793 | * re-init the NIC. The NSC manual suggests only a | |
1794 | * partial reset/re-init is necessary - but some | |
1795 | * chips seem to want more. The DMA lockup has been | |
1796 | * seen only with early rev chips - Methinks this | |
1797 | * bug was fixed in later revs. -DG | |
1798 | */ | |
1799 | if (isr & ED_ISR_OVW) { | |
1800 | ++sc->arpcom.ac_if.if_ierrors; | |
016ac65c | 1801 | #ifdef DIAGNOSTIC |
b12abaae DG |
1802 | log(LOG_WARNING, |
1803 | "ed%d: warning - receiver ring buffer overrun\n", | |
1804 | unit); | |
016ac65c | 1805 | #endif |
b12abaae DG |
1806 | /* |
1807 | * Stop/reset/re-init NIC | |
1808 | */ | |
4c45483e | 1809 | ed_reset(unit, 0); |
b12abaae | 1810 | } else { |
21ac5de2 | 1811 | |
b12abaae DG |
1812 | /* |
1813 | * Receiver Error. One or more of: CRC error, frame | |
1814 | * alignment error FIFO overrun, or missed packet. | |
1815 | */ | |
1816 | if (isr & ED_ISR_RXE) { | |
1817 | ++sc->arpcom.ac_if.if_ierrors; | |
1818 | #ifdef ED_DEBUG | |
1819 | printf("ed%d: receive error %x\n", unit, | |
1820 | inb(sc->nic_addr + ED_P0_RSR)); | |
1821 | #endif | |
1822 | } | |
21ac5de2 | 1823 | |
b12abaae DG |
1824 | /* |
1825 | * Go get the packet(s) | |
1826 | * XXX - Doing this on an error is dubious | |
1827 | * because there shouldn't be any data to | |
1828 | * get (we've configured the interface to | |
1829 | * not accept packets with errors). | |
1830 | */ | |
21ac5de2 | 1831 | |
b12abaae DG |
1832 | /* |
1833 | * Enable 16bit access to shared memory first | |
1834 | * on WD/SMC boards. | |
1835 | */ | |
1836 | if (sc->isa16bit && | |
362c9b14 | 1837 | (sc->vendor == ED_VENDOR_WD_SMC)) { |
21ac5de2 | 1838 | |
21ac5de2 | 1839 | outb(sc->asic_addr + ED_WD_LAAR, |
362c9b14 DG |
1840 | (sc->wd_laar_proto |= |
1841 | ED_WD_LAAR_M16EN)); | |
fcd1510c DG |
1842 | if (sc->is790) |
1843 | outb(sc->asic_addr + ED_WD_MSR, | |
1844 | ED_WD_MSR_MENB); | |
21ac5de2 DG |
1845 | } |
1846 | ||
b12abaae DG |
1847 | ed_rint (unit); |
1848 | ||
1849 | /* disable 16bit access */ | |
1850 | if (sc->isa16bit && | |
1851 | (sc->vendor == ED_VENDOR_WD_SMC)) { | |
21ac5de2 | 1852 | |
21ac5de2 | 1853 | outb(sc->asic_addr + ED_WD_LAAR, |
362c9b14 DG |
1854 | (sc->wd_laar_proto &= |
1855 | ~ED_WD_LAAR_M16EN)); | |
fcd1510c DG |
1856 | if (sc->is790) |
1857 | outb(sc->asic_addr + ED_WD_MSR, 0x00); | |
21ac5de2 | 1858 | } |
b12abaae | 1859 | } |
21ac5de2 DG |
1860 | } |
1861 | ||
1862 | /* | |
1863 | * If it looks like the transmitter can take more data, | |
b12abaae DG |
1864 | * attempt to start output on the interface. |
1865 | * This is done after handling the receiver to | |
1866 | * give the receiver priority. | |
21ac5de2 | 1867 | */ |
b12abaae | 1868 | if ((sc->arpcom.ac_if.if_flags & IFF_OACTIVE) == 0) |
21ac5de2 | 1869 | ed_start(&sc->arpcom.ac_if); |
21ac5de2 DG |
1870 | |
1871 | /* | |
d6d150e8 DG |
1872 | * return NIC CR to standard state: page 0, remote DMA complete, |
1873 | * start (toggling the TXP bit off, even if was just set | |
1874 | * in the transmit routine, is *okay* - it is 'edge' | |
1875 | * triggered from low to high) | |
21ac5de2 | 1876 | */ |
d2a46cd2 DG |
1877 | if (sc->is790) { |
1878 | outb(sc->nic_addr + ED_P0_CR, ED_CR_STA); | |
1879 | } else { | |
1880 | outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STA); | |
1881 | } | |
d6d150e8 DG |
1882 | /* |
1883 | * If the Network Talley Counters overflow, read them to | |
1884 | * reset them. It appears that old 8390's won't | |
1885 | * clear the ISR flag otherwise - resulting in an | |
1886 | * infinite loop. | |
1887 | */ | |
1888 | if (isr & ED_ISR_CNT) { | |
1889 | (void) inb(sc->nic_addr + ED_P0_CNTR0); | |
1890 | (void) inb(sc->nic_addr + ED_P0_CNTR1); | |
1891 | (void) inb(sc->nic_addr + ED_P0_CNTR2); | |
1892 | } | |
21ac5de2 DG |
1893 | } |
1894 | } | |
1895 | ||
1896 | /* | |
1897 | * Process an ioctl request. This code needs some work - it looks | |
1898 | * pretty ugly. | |
1899 | */ | |
1900 | int | |
1901 | ed_ioctl(ifp, command, data) | |
1902 | register struct ifnet *ifp; | |
1903 | int command; | |
1904 | caddr_t data; | |
1905 | { | |
1906 | register struct ifaddr *ifa = (struct ifaddr *)data; | |
1907 | struct ed_softc *sc = &ed_softc[ifp->if_unit]; | |
1908 | struct ifreq *ifr = (struct ifreq *)data; | |
1909 | int s, error = 0; | |
1910 | ||
d2a46cd2 | 1911 | s = splimp(); |
21ac5de2 DG |
1912 | |
1913 | switch (command) { | |
1914 | ||
1915 | case SIOCSIFADDR: | |
1916 | ifp->if_flags |= IFF_UP; | |
1917 | ||
1918 | switch (ifa->ifa_addr->sa_family) { | |
1919 | #ifdef INET | |
1920 | case AF_INET: | |
1921 | ed_init(ifp->if_unit); /* before arpwhohas */ | |
1922 | /* | |
1923 | * See if another station has *our* IP address. | |
1924 | * i.e.: There is an address conflict! If a | |
1925 | * conflict exists, a message is sent to the | |
1926 | * console. | |
1927 | */ | |
362c9b14 | 1928 | ((struct arpcom *)ifp)->ac_ipaddr = IA_SIN(ifa)->sin_addr; |
21ac5de2 DG |
1929 | arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr); |
1930 | break; | |
1931 | #endif | |
1932 | #ifdef NS | |
1933 | /* | |
1934 | * XXX - This code is probably wrong | |
1935 | */ | |
1936 | case AF_NS: | |
1937 | { | |
1938 | register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr); | |
1939 | ||
1940 | if (ns_nullhost(*ina)) | |
1941 | ina->x_host = | |
1942 | *(union ns_host *)(sc->arpcom.ac_enaddr); | |
1943 | else { | |
1944 | /* | |
1945 | * | |
1946 | */ | |
1947 | bcopy((caddr_t)ina->x_host.c_host, | |
362c9b14 DG |
1948 | (caddr_t)sc->arpcom.ac_enaddr, |
1949 | sizeof(sc->arpcom.ac_enaddr)); | |
21ac5de2 DG |
1950 | } |
1951 | /* | |
1952 | * Set new address | |
1953 | */ | |
1954 | ed_init(ifp->if_unit); | |
1955 | break; | |
1956 | } | |
1957 | #endif | |
1958 | default: | |
1959 | ed_init(ifp->if_unit); | |
1960 | break; | |
1961 | } | |
1962 | break; | |
1963 | ||
b27f876d DG |
1964 | case SIOCGIFADDR: |
1965 | { | |
1966 | struct sockaddr *sa; | |
1967 | sa = (struct sockaddr *)&ifr->ifr_data; | |
1968 | bcopy((caddr_t)sc->arpcom.ac_enaddr, | |
1969 | (caddr_t) sa->sa_data, ETHER_ADDR_LEN); | |
1970 | } | |
1971 | break; | |
1972 | ||
21ac5de2 DG |
1973 | case SIOCSIFFLAGS: |
1974 | /* | |
1975 | * If interface is marked down and it is running, then stop it | |
1976 | */ | |
1977 | if (((ifp->if_flags & IFF_UP) == 0) && | |
1978 | (ifp->if_flags & IFF_RUNNING)) { | |
1979 | ed_stop(ifp->if_unit); | |
1980 | ifp->if_flags &= ~IFF_RUNNING; | |
1981 | } else { | |
1982 | /* | |
1983 | * If interface is marked up and it is stopped, then start it | |
1984 | */ | |
1985 | if ((ifp->if_flags & IFF_UP) && | |
1986 | ((ifp->if_flags & IFF_RUNNING) == 0)) | |
1987 | ed_init(ifp->if_unit); | |
1988 | } | |
1989 | #if NBPFILTER > 0 | |
1990 | if (ifp->if_flags & IFF_PROMISC) { | |
1991 | /* | |
1992 | * Set promiscuous mode on interface. | |
1993 | * XXX - for multicasts to work, we would need to | |
1994 | * write 1's in all bits of multicast | |
1995 | * hashing array. For now we assume that | |
1996 | * this was done in ed_init(). | |
1997 | */ | |
1998 | outb(sc->nic_addr + ED_P0_RCR, | |
362c9b14 | 1999 | ED_RCR_PRO|ED_RCR_AM|ED_RCR_AB); |
ac7f64c9 | 2000 | } else { |
21ac5de2 DG |
2001 | /* |
2002 | * XXX - for multicasts to work, we would need to | |
2003 | * rewrite the multicast hashing array with the | |
2004 | * proper hash (would have been destroyed above). | |
2005 | */ | |
2006 | outb(sc->nic_addr + ED_P0_RCR, ED_RCR_AB); | |
ac7f64c9 | 2007 | } |
21ac5de2 | 2008 | #endif |
ac7f64c9 DG |
2009 | /* |
2010 | * An unfortunate hack to provide the (required) software control | |
4d8228f9 | 2011 | * of the tranceiver for 3Com boards. The ALTPHYS flag disables |
ac7f64c9 DG |
2012 | * the tranceiver if set. |
2013 | */ | |
9f2aff44 | 2014 | if (sc->vendor == ED_VENDOR_3COM) { |
4d8228f9 | 2015 | if (ifp->if_flags & IFF_ALTPHYS) { |
9f2aff44 DG |
2016 | outb(sc->asic_addr + ED_3COM_CR, 0); |
2017 | } else { | |
2018 | outb(sc->asic_addr + ED_3COM_CR, ED_3COM_CR_XSEL); | |
2019 | } | |
ac7f64c9 DG |
2020 | } |
2021 | ||
21ac5de2 DG |
2022 | break; |
2023 | ||
2024 | default: | |
2025 | error = EINVAL; | |
2026 | } | |
2027 | (void) splx(s); | |
2028 | return (error); | |
2029 | } | |
2030 | ||
2031 | /* | |
2032 | * Macro to calculate a new address within shared memory when given an offset | |
2033 | * from an address, taking into account ring-wrap. | |
2034 | */ | |
2035 | #define ringoffset(sc, start, off, type) \ | |
016ac65c DG |
2036 | ((type)( ((caddr_t)(start)+(off) >= (sc)->mem_end) ? \ |
2037 | (((caddr_t)(start)+(off))) - (sc)->mem_end \ | |
2038 | + (sc)->mem_ring: \ | |
21ac5de2 DG |
2039 | ((caddr_t)(start)+(off)) )) |
2040 | ||
2041 | /* | |
2042 | * Retreive packet from shared memory and send to the next level up via | |
2043 | * ether_input(). If there is a BPF listener, give a copy to BPF, too. | |
2044 | */ | |
4c45483e | 2045 | static void |
21ac5de2 DG |
2046 | ed_get_packet(sc, buf, len) |
2047 | struct ed_softc *sc; | |
2048 | char *buf; | |
2049 | u_short len; | |
2050 | { | |
2051 | struct ether_header *eh; | |
4c45483e | 2052 | struct mbuf *m, *head = 0, *ed_ring_to_mbuf(); |
21ac5de2 DG |
2053 | u_short off; |
2054 | int resid; | |
2055 | u_short etype; | |
016ac65c | 2056 | struct trailer_header trailer_header; |
21ac5de2 DG |
2057 | |
2058 | /* Allocate a header mbuf */ | |
2059 | MGETHDR(m, M_DONTWAIT, MT_DATA); | |
2060 | if (m == 0) | |
2061 | goto bad; | |
2062 | m->m_pkthdr.rcvif = &sc->arpcom.ac_if; | |
2063 | m->m_pkthdr.len = len; | |
2064 | m->m_len = 0; | |
2065 | head = m; | |
2066 | ||
21ac5de2 DG |
2067 | /* The following sillines is to make NFS happy */ |
2068 | #define EROUND ((sizeof(struct ether_header) + 3) & ~3) | |
2069 | #define EOFF (EROUND - sizeof(struct ether_header)) | |
2070 | ||
2071 | /* | |
2072 | * The following assumes there is room for | |
2073 | * the ether header in the header mbuf | |
2074 | */ | |
2075 | head->m_data += EOFF; | |
016ac65c DG |
2076 | eh = mtod(head, struct ether_header *); |
2077 | ||
2078 | if (sc->mem_shared) | |
2079 | bcopy(buf, mtod(head, caddr_t), sizeof(struct ether_header)); | |
2080 | else | |
2081 | ed_pio_readmem(sc, buf, mtod(head, caddr_t), | |
362c9b14 | 2082 | sizeof(struct ether_header)); |
21ac5de2 DG |
2083 | buf += sizeof(struct ether_header); |
2084 | head->m_len += sizeof(struct ether_header); | |
2085 | len -= sizeof(struct ether_header); | |
2086 | ||
2087 | etype = ntohs((u_short)eh->ether_type); | |
2088 | ||
2089 | /* | |
2090 | * Deal with trailer protocol: | |
2091 | * If trailer protocol, calculate the datasize as 'off', | |
2092 | * which is also the offset to the trailer header. | |
2093 | * Set resid to the amount of packet data following the | |
2094 | * trailer header. | |
2095 | * Finally, copy residual data into mbuf chain. | |
2096 | */ | |
2097 | if (etype >= ETHERTYPE_TRAIL && | |
2098 | etype < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) { | |
2099 | ||
2100 | off = (etype - ETHERTYPE_TRAIL) << 9; | |
2101 | if ((off + sizeof(struct trailer_header)) > len) | |
2102 | goto bad; /* insanity */ | |
2103 | ||
016ac65c DG |
2104 | /* |
2105 | * If we have shared memory, we can get info directly from the | |
2106 | * stored packet, otherwise we must get a local copy | |
2107 | * of the trailer header using PIO. | |
2108 | */ | |
2109 | if (sc->mem_shared) { | |
2110 | eh->ether_type = *ringoffset(sc, buf, off, u_short *); | |
2111 | resid = ntohs(*ringoffset(sc, buf, off+2, u_short *)); | |
2112 | } else { | |
2113 | struct trailer_header trailer_header; | |
2114 | ed_pio_readmem(sc, | |
362c9b14 DG |
2115 | ringoffset(sc, buf, off, caddr_t), |
2116 | (char *) &trailer_header, | |
2117 | sizeof(trailer_header)); | |
016ac65c DG |
2118 | eh->ether_type = trailer_header.ether_type; |
2119 | resid = trailer_header.ether_residual; | |
2120 | } | |
21ac5de2 DG |
2121 | |
2122 | if ((off + resid) > len) goto bad; /* insanity */ | |
2123 | ||
2124 | resid -= sizeof(struct trailer_header); | |
2125 | if (resid < 0) goto bad; /* insanity */ | |
2126 | ||
362c9b14 DG |
2127 | m = ed_ring_to_mbuf(sc, ringoffset(sc, buf, off+4, char *), |
2128 | head, resid); | |
21ac5de2 DG |
2129 | if (m == 0) goto bad; |
2130 | ||
2131 | len = off; | |
2132 | head->m_pkthdr.len -= 4; /* subtract trailer header */ | |
2133 | } | |
2134 | ||
2135 | /* | |
2136 | * Pull packet off interface. Or if this was a trailer packet, | |
2137 | * the data portion is appended. | |
2138 | */ | |
2139 | m = ed_ring_to_mbuf(sc, buf, m, len); | |
2140 | if (m == 0) goto bad; | |
2141 | ||
2142 | #if NBPFILTER > 0 | |
2143 | /* | |
2144 | * Check if there's a BPF listener on this interface. | |
2145 | * If so, hand off the raw packet to bpf. | |
2146 | */ | |
2147 | if (sc->bpf) { | |
2148 | bpf_mtap(sc->bpf, head); | |
2149 | ||
2150 | /* | |
2151 | * Note that the interface cannot be in promiscuous mode if | |
2152 | * there are no BPF listeners. And if we are in promiscuous | |
2153 | * mode, we have to check if this packet is really ours. | |
2154 | * | |
2155 | * XXX This test does not support multicasts. | |
2156 | */ | |
2157 | if ((sc->arpcom.ac_if.if_flags & IFF_PROMISC) && | |
362c9b14 DG |
2158 | bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr, |
2159 | sizeof(eh->ether_dhost)) != 0 && | |
2160 | bcmp(eh->ether_dhost, etherbroadcastaddr, | |
2161 | sizeof(eh->ether_dhost)) != 0) { | |
21ac5de2 | 2162 | |
362c9b14 DG |
2163 | m_freem(head); |
2164 | return; | |
21ac5de2 DG |
2165 | } |
2166 | } | |
2167 | #endif | |
2168 | ||
2169 | /* | |
2170 | * Fix up data start offset in mbuf to point past ether header | |
2171 | */ | |
2172 | m_adj(head, sizeof(struct ether_header)); | |
2173 | ||
2174 | /* | |
2175 | * silly ether_input routine needs 'type' in host byte order | |
2176 | */ | |
2177 | eh->ether_type = ntohs(eh->ether_type); | |
2178 | ||
2179 | ether_input(&sc->arpcom.ac_if, eh, head); | |
2180 | return; | |
2181 | ||
2182 | bad: if (head) | |
2183 | m_freem(head); | |
2184 | return; | |
2185 | } | |
2186 | ||
2187 | /* | |
2188 | * Supporting routines | |
2189 | */ | |
2190 | ||
016ac65c DG |
2191 | /* |
2192 | * Given a NIC memory source address and a host memory destination | |
2193 | * address, copy 'amount' from NIC to host using Programmed I/O. | |
2194 | * The 'amount' is rounded up to a word - okay as long as mbufs | |
2195 | * are word sized. | |
2196 | * This routine is currently Novell-specific. | |
2197 | */ | |
2198 | void | |
2199 | ed_pio_readmem(sc,src,dst,amount) | |
2200 | struct ed_softc *sc; | |
2201 | unsigned short src; | |
2202 | unsigned char *dst; | |
2203 | unsigned short amount; | |
2204 | { | |
2205 | unsigned short tmp_amount; | |
2206 | ||
2207 | /* select page 0 registers */ | |
2208 | outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STA); | |
2209 | ||
2210 | /* round up to a word */ | |
2211 | tmp_amount = amount; | |
2212 | if (amount & 1) ++amount; | |
2213 | ||
2214 | /* set up DMA byte count */ | |
2215 | outb(sc->nic_addr + ED_P0_RBCR0, amount); | |
2216 | outb(sc->nic_addr + ED_P0_RBCR1, amount>>8); | |
2217 | ||
2218 | /* set up source address in NIC mem */ | |
2219 | outb(sc->nic_addr + ED_P0_RSAR0, src); | |
2220 | outb(sc->nic_addr + ED_P0_RSAR1, src>>8); | |
2221 | ||
2222 | outb(sc->nic_addr + ED_P0_CR, ED_CR_RD0 | ED_CR_STA); | |
2223 | ||
2224 | if (sc->isa16bit) { | |
2225 | insw(sc->asic_addr + ED_NOVELL_DATA, dst, amount/2); | |
2226 | } else | |
2227 | insb(sc->asic_addr + ED_NOVELL_DATA, dst, amount); | |
2228 | ||
2229 | } | |
2230 | ||
2231 | /* | |
2232 | * Stripped down routine for writing a linear buffer to NIC memory. | |
2233 | * Only used in the probe routine to test the memory. 'len' must | |
2234 | * be even. | |
2235 | */ | |
2236 | void | |
2237 | ed_pio_writemem(sc,src,dst,len) | |
2238 | struct ed_softc *sc; | |
2239 | char *src; | |
2240 | unsigned short dst; | |
2241 | unsigned short len; | |
2242 | { | |
a23f6e67 | 2243 | int maxwait=100; /* about 120us */ |
016ac65c DG |
2244 | |
2245 | /* select page 0 registers */ | |
2246 | outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STA); | |
2247 | ||
2248 | /* reset remote DMA complete flag */ | |
2249 | outb(sc->nic_addr + ED_P0_ISR, ED_ISR_RDC); | |
2250 | ||
2251 | /* set up DMA byte count */ | |
2252 | outb(sc->nic_addr + ED_P0_RBCR0, len); | |
2253 | outb(sc->nic_addr + ED_P0_RBCR1, len>>8); | |
2254 | ||
2255 | /* set up destination address in NIC mem */ | |
2256 | outb(sc->nic_addr + ED_P0_RSAR0, dst); | |
2257 | outb(sc->nic_addr + ED_P0_RSAR1, dst>>8); | |
2258 | ||
2259 | /* set remote DMA write */ | |
2260 | outb(sc->nic_addr + ED_P0_CR, ED_CR_RD1 | ED_CR_STA); | |
2261 | ||
2262 | if (sc->isa16bit) | |
2263 | outsw(sc->asic_addr + ED_NOVELL_DATA, src, len/2); | |
2264 | else | |
2265 | outsb(sc->asic_addr + ED_NOVELL_DATA, src, len); | |
2266 | /* | |
2267 | * Wait for remote DMA complete. This is necessary because on the | |
2268 | * transmit side, data is handled internally by the NIC in bursts | |
2269 | * and we can't start another remote DMA until this one completes. | |
2270 | * Not waiting causes really bad things to happen - like the NIC | |
2271 | * irrecoverably jamming the ISA bus. | |
2272 | */ | |
2273 | while (((inb(sc->nic_addr + ED_P0_ISR) & ED_ISR_RDC) != ED_ISR_RDC) && --maxwait); | |
2274 | } | |
2275 | ||
2276 | /* | |
2277 | * Write an mbuf chain to the destination NIC memory address using | |
2278 | * programmed I/O. | |
2279 | */ | |
2280 | u_short | |
2281 | ed_pio_write_mbufs(sc,m,dst) | |
2282 | struct ed_softc *sc; | |
2283 | struct mbuf *m; | |
2284 | unsigned short dst; | |
2285 | { | |
2286 | unsigned short len, mb_offset; | |
2287 | struct mbuf *mp; | |
2288 | unsigned char residual[2]; | |
a23f6e67 | 2289 | int maxwait=100; /* about 120us */ |
016ac65c DG |
2290 | |
2291 | /* First, count up the total number of bytes to copy */ | |
2292 | for (len = 0, mp = m; mp; mp = mp->m_next) | |
2293 | len += mp->m_len; | |
2294 | ||
2295 | /* select page 0 registers */ | |
2296 | outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STA); | |
2297 | ||
2298 | /* reset remote DMA complete flag */ | |
2299 | outb(sc->nic_addr + ED_P0_ISR, ED_ISR_RDC); | |
2300 | ||
2301 | /* set up DMA byte count */ | |
2302 | outb(sc->nic_addr + ED_P0_RBCR0, len); | |
2303 | outb(sc->nic_addr + ED_P0_RBCR1, len>>8); | |
2304 | ||
2305 | /* set up destination address in NIC mem */ | |
2306 | outb(sc->nic_addr + ED_P0_RSAR0, dst); | |
2307 | outb(sc->nic_addr + ED_P0_RSAR1, dst>>8); | |
2308 | ||
2309 | /* set remote DMA write */ | |
2310 | outb(sc->nic_addr + ED_P0_CR, ED_CR_RD1 | ED_CR_STA); | |
2311 | ||
2312 | mb_offset = 0; | |
2313 | /* | |
2314 | * Transfer the mbuf chain to the NIC memory. | |
2315 | * The following code isn't too pretty. The problem is that we can only | |
2316 | * transfer words to the board, and if an mbuf has an odd number | |
2317 | * of bytes in it, this is a problem. It's not a simple matter of | |
2318 | * just removing a byte from the next mbuf (adjusting data++ and | |
2319 | * len--) because this will hose-over the mbuf chain which might | |
2320 | * be needed later for BPF. Instead, we maintain an offset | |
2321 | * (mb_offset) which let's us skip over the first byte in the | |
2322 | * following mbuf. | |
2323 | */ | |
2324 | while (m) { | |
2325 | if (m->m_len - mb_offset) { | |
2326 | if (sc->isa16bit) { | |
2327 | if ((m->m_len - mb_offset) > 1) | |
2328 | outsw(sc->asic_addr + ED_NOVELL_DATA, | |
362c9b14 DG |
2329 | mtod(m, caddr_t) + mb_offset, |
2330 | (m->m_len - mb_offset) / 2); | |
016ac65c DG |
2331 | |
2332 | /* | |
2333 | * if odd number of bytes, get the odd byte from | |
2334 | * the next mbuf with data | |
2335 | */ | |
2336 | if ((m->m_len - mb_offset) & 1) { | |
2337 | /* first the last byte in current mbuf */ | |
362c9b14 DG |
2338 | residual[0] = *(mtod(m, caddr_t) + |
2339 | m->m_len - 1); | |
016ac65c DG |
2340 | |
2341 | /* advance past any empty mbufs */ | |
2342 | while (m->m_next && (m->m_next->m_len == 0)) | |
2343 | m = m->m_next; | |
2344 | ||
2345 | if (m->m_next) { | |
2346 | /* remove first byte in next mbuf */ | |
2347 | residual[1] = *(mtod(m->m_next, caddr_t)); | |
2348 | mb_offset = 1; | |
2349 | } | |
2350 | ||
2351 | outw(sc->asic_addr + ED_NOVELL_DATA, | |
362c9b14 | 2352 | *((unsigned short *) residual)); |
016ac65c DG |
2353 | } else |
2354 | mb_offset = 0; | |
2355 | } else | |
2356 | outsb(sc->asic_addr + ED_NOVELL_DATA, m->m_data, m->m_len); | |
2357 | ||
2358 | } | |
2359 | m = m->m_next; | |
2360 | } | |
2361 | ||
2362 | /* | |
2363 | * Wait for remote DMA complete. This is necessary because on the | |
2364 | * transmit side, data is handled internally by the NIC in bursts | |
2365 | * and we can't start another remote DMA until this one completes. | |
2366 | * Not waiting causes really bad things to happen - like the NIC | |
2367 | * irrecoverably jamming the ISA bus. | |
2368 | */ | |
2369 | while (((inb(sc->nic_addr + ED_P0_ISR) & ED_ISR_RDC) != ED_ISR_RDC) && --maxwait); | |
2370 | ||
2371 | if (!maxwait) { | |
2372 | log(LOG_WARNING, "ed%d: remote transmit DMA failed to complete\n", | |
362c9b14 | 2373 | sc->arpcom.ac_if.if_unit); |
4c45483e | 2374 | ed_reset(sc->arpcom.ac_if.if_unit, 0); |
016ac65c DG |
2375 | } |
2376 | ||
2377 | return(len); | |
2378 | } | |
2379 | ||
21ac5de2 DG |
2380 | /* |
2381 | * Given a source and destination address, copy 'amount' of a packet from | |
2382 | * the ring buffer into a linear destination buffer. Takes into account | |
2383 | * ring-wrap. | |
2384 | */ | |
2385 | static inline char * | |
2386 | ed_ring_copy(sc,src,dst,amount) | |
2387 | struct ed_softc *sc; | |
2388 | char *src; | |
2389 | char *dst; | |
2390 | u_short amount; | |
2391 | { | |
2392 | u_short tmp_amount; | |
2393 | ||
2394 | /* does copy wrap to lower addr in ring buffer? */ | |
016ac65c DG |
2395 | if (src + amount > sc->mem_end) { |
2396 | tmp_amount = sc->mem_end - src; | |
2397 | ||
2398 | /* copy amount up to end of NIC memory */ | |
2399 | if (sc->mem_shared) | |
2400 | bcopy(src,dst,tmp_amount); | |
2401 | else | |
2402 | ed_pio_readmem(sc,src,dst,tmp_amount); | |
2403 | ||
21ac5de2 | 2404 | amount -= tmp_amount; |
016ac65c | 2405 | src = sc->mem_ring; |
21ac5de2 DG |
2406 | dst += tmp_amount; |
2407 | } | |
2408 | ||
016ac65c DG |
2409 | if (sc->mem_shared) |
2410 | bcopy(src, dst, amount); | |
2411 | else | |
2412 | ed_pio_readmem(sc, src, dst, amount); | |
21ac5de2 DG |
2413 | |
2414 | return(src + amount); | |
2415 | } | |
2416 | ||
2417 | /* | |
2418 | * Copy data from receive buffer to end of mbuf chain | |
2419 | * allocate additional mbufs as needed. return pointer | |
2420 | * to last mbuf in chain. | |
2421 | * sc = ed info (softc) | |
2422 | * src = pointer in ed ring buffer | |
2423 | * dst = pointer to last mbuf in mbuf chain to copy to | |
2424 | * amount = amount of data to copy | |
2425 | */ | |
2426 | struct mbuf * | |
2427 | ed_ring_to_mbuf(sc,src,dst,total_len) | |
2428 | struct ed_softc *sc; | |
2429 | char *src; | |
2430 | struct mbuf *dst; | |
2431 | u_short total_len; | |
2432 | { | |
2433 | register struct mbuf *m = dst; | |
2434 | ||
2435 | while (total_len) { | |
2436 | register u_short amount = min(total_len, M_TRAILINGSPACE(m)); | |
2437 | ||
2438 | if (amount == 0) { /* no more data in this mbuf, alloc another */ | |
2439 | /* | |
5969b7e9 DG |
2440 | * If there is enough data for an mbuf cluster, attempt |
2441 | * to allocate one of those, otherwise, a regular | |
2442 | * mbuf will do. | |
2443 | * Note that a regular mbuf is always required, even if | |
2444 | * we get a cluster - getting a cluster does not | |
2445 | * allocate any mbufs, and one is needed to assign | |
2446 | * the cluster to. The mbuf that has a cluster | |
2447 | * extension can not be used to contain data - only | |
2448 | * the cluster can contain data. | |
21ac5de2 DG |
2449 | */ |
2450 | dst = m; | |
2451 | MGET(m, M_DONTWAIT, MT_DATA); | |
2452 | if (m == 0) | |
2453 | return (0); | |
2454 | ||
2455 | if (total_len >= MINCLSIZE) | |
2456 | MCLGET(m, M_DONTWAIT); | |
2457 | ||
2458 | m->m_len = 0; | |
2459 | dst->m_next = m; | |
2460 | amount = min(total_len, M_TRAILINGSPACE(m)); | |
2461 | } | |
2462 | ||
2463 | src = ed_ring_copy(sc, src, mtod(m, caddr_t) + m->m_len, amount); | |
2464 | ||
2465 | m->m_len += amount; | |
2466 | total_len -= amount; | |
2467 | ||
2468 | } | |
2469 | return (m); | |
2470 | } | |
2471 | #endif |