1 | /* $NetBSD: lance.c,v 1.51 2016/10/02 14:16:02 christos Exp $ */ |
2 | |
3 | /*- |
4 | * Copyright (c) 1997, 1998 The NetBSD Foundation, Inc. |
5 | * All rights reserved. |
6 | * |
7 | * This code is derived from software contributed to The NetBSD Foundation |
8 | * by Charles M. Hannum and by Jason R. Thorpe of the Numerical Aerospace |
9 | * Simulation Facility, NASA Ames Research Center. |
10 | * |
11 | * Redistribution and use in source and binary forms, with or without |
12 | * modification, are permitted provided that the following conditions |
13 | * are met: |
14 | * 1. Redistributions of source code must retain the above copyright |
15 | * notice, this list of conditions and the following disclaimer. |
16 | * 2. Redistributions in binary form must reproduce the above copyright |
17 | * notice, this list of conditions and the following disclaimer in the |
18 | * documentation and/or other materials provided with the distribution. |
19 | * |
20 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
21 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
22 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
23 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
24 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
25 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
26 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
27 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
28 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
29 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
30 | * POSSIBILITY OF SUCH DAMAGE. |
31 | */ |
32 | |
33 | /*- |
34 | * Copyright (c) 1992, 1993 |
35 | * The Regents of the University of California. All rights reserved. |
36 | * |
37 | * This code is derived from software contributed to Berkeley by |
38 | * Ralph Campbell and Rick Macklem. |
39 | * |
40 | * Redistribution and use in source and binary forms, with or without |
41 | * modification, are permitted provided that the following conditions |
42 | * are met: |
43 | * 1. Redistributions of source code must retain the above copyright |
44 | * notice, this list of conditions and the following disclaimer. |
45 | * 2. Redistributions in binary form must reproduce the above copyright |
46 | * notice, this list of conditions and the following disclaimer in the |
47 | * documentation and/or other materials provided with the distribution. |
48 | * 3. Neither the name of the University nor the names of its contributors |
49 | * may be used to endorse or promote products derived from this software |
50 | * without specific prior written permission. |
51 | * |
52 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
53 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
54 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
55 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
56 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
57 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
58 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
59 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
60 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
61 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
62 | * SUCH DAMAGE. |
63 | * |
64 | * @(#)if_le.c 8.2 (Berkeley) 11/16/93 |
65 | */ |
66 | |
67 | #include <sys/cdefs.h> |
68 | __KERNEL_RCSID(0, "$NetBSD: lance.c,v 1.51 2016/10/02 14:16:02 christos Exp $" ); |
69 | |
70 | #include <sys/param.h> |
71 | #include <sys/systm.h> |
72 | #include <sys/mbuf.h> |
73 | #include <sys/syslog.h> |
74 | #include <sys/socket.h> |
75 | #include <sys/device.h> |
76 | #include <sys/malloc.h> |
77 | #include <sys/ioctl.h> |
78 | #include <sys/errno.h> |
79 | #include <sys/rndsource.h> |
80 | |
81 | #include <net/if.h> |
82 | #include <net/if_dl.h> |
83 | #include <net/if_ether.h> |
84 | #include <net/if_media.h> |
85 | |
86 | |
87 | #include <net/bpf.h> |
88 | #include <net/bpfdesc.h> |
89 | |
90 | #include <dev/ic/lancereg.h> |
91 | #include <dev/ic/lancevar.h> |
92 | |
93 | #if defined(_KERNEL_OPT) |
94 | #include "opt_ddb.h" |
95 | #endif |
96 | |
97 | #ifdef DDB |
98 | #define integrate |
99 | #define hide |
100 | #else |
101 | #define integrate static inline |
102 | #define hide static |
103 | #endif |
104 | |
105 | integrate struct mbuf *lance_get(struct lance_softc *, int, int); |
106 | |
107 | hide bool lance_shutdown(device_t, int); |
108 | |
109 | int lance_mediachange(struct ifnet *); |
110 | void lance_mediastatus(struct ifnet *, struct ifmediareq *); |
111 | |
112 | static inline u_int16_t ether_cmp(void *, void *); |
113 | |
114 | void lance_stop(struct ifnet *, int); |
115 | int lance_ioctl(struct ifnet *, u_long, void *); |
116 | void lance_watchdog(struct ifnet *); |
117 | |
118 | /* |
119 | * Compare two Ether/802 addresses for equality, inlined and |
120 | * unrolled for speed. Use this like memcmp(). |
121 | * |
122 | * XXX: Add <machine/inlines.h> for stuff like this? |
123 | * XXX: or maybe add it to libkern.h instead? |
124 | * |
125 | * "I'd love to have an inline assembler version of this." |
126 | * XXX: Who wanted that? mycroft? I wrote one, but this |
127 | * version in C is as good as hand-coded assembly. -gwr |
128 | * |
129 | * Please do NOT tweak this without looking at the actual |
130 | * assembly code generated before and after your tweaks! |
131 | */ |
132 | static inline uint16_t |
133 | ether_cmp(void *one, void *two) |
134 | { |
135 | uint16_t *a = (uint16_t *)one; |
136 | uint16_t *b = (uint16_t *)two; |
137 | uint16_t diff; |
138 | |
139 | #ifdef m68k |
140 | /* |
141 | * The post-increment-pointer form produces the best |
142 | * machine code for m68k. This was carefully tuned |
143 | * so it compiles to just 8 short (2-byte) op-codes! |
144 | */ |
145 | diff = *a++ - *b++; |
146 | diff |= *a++ - *b++; |
147 | diff |= *a++ - *b++; |
148 | #else |
149 | /* |
150 | * Most modern CPUs do better with a single expresion. |
151 | * Note that short-cut evaluation is NOT helpful here, |
152 | * because it just makes the code longer, not faster! |
153 | */ |
154 | diff = (a[0] - b[0]) | (a[1] - b[1]) | (a[2] - b[2]); |
155 | #endif |
156 | |
157 | return (diff); |
158 | } |
159 | |
160 | #define ETHER_CMP ether_cmp |
161 | |
162 | #ifdef LANCE_REVC_BUG |
163 | /* Make sure this is short-aligned, for ether_cmp(). */ |
164 | static uint16_t bcast_enaddr[3] = { ~0, ~0, ~0 }; |
165 | #endif |
166 | |
167 | void |
168 | lance_config(struct lance_softc *sc) |
169 | { |
170 | int i, nbuf; |
171 | struct ifnet *ifp = &sc->sc_ethercom.ec_if; |
172 | |
173 | /* Initialize ifnet structure. */ |
174 | strcpy(ifp->if_xname, device_xname(sc->sc_dev)); |
175 | ifp->if_softc = sc; |
176 | ifp->if_start = sc->sc_start; |
177 | ifp->if_ioctl = lance_ioctl; |
178 | ifp->if_watchdog = lance_watchdog; |
179 | ifp->if_init = lance_init; |
180 | ifp->if_stop = lance_stop; |
181 | ifp->if_flags = |
182 | IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS | IFF_MULTICAST; |
183 | #ifdef LANCE_REVC_BUG |
184 | ifp->if_flags &= ~IFF_MULTICAST; |
185 | #endif |
186 | IFQ_SET_READY(&ifp->if_snd); |
187 | |
188 | /* Initialize ifmedia structures. */ |
189 | ifmedia_init(&sc->sc_media, 0, lance_mediachange, lance_mediastatus); |
190 | if (sc->sc_supmedia != NULL) { |
191 | for (i = 0; i < sc->sc_nsupmedia; i++) |
192 | ifmedia_add(&sc->sc_media, sc->sc_supmedia[i], |
193 | 0, NULL); |
194 | ifmedia_set(&sc->sc_media, sc->sc_defaultmedia); |
195 | } else { |
196 | ifmedia_add(&sc->sc_media, IFM_ETHER|IFM_MANUAL, 0, NULL); |
197 | ifmedia_set(&sc->sc_media, IFM_ETHER|IFM_MANUAL); |
198 | } |
199 | |
200 | switch (sc->sc_memsize) { |
201 | case 8192: |
202 | sc->sc_nrbuf = 4; |
203 | sc->sc_ntbuf = 1; |
204 | break; |
205 | case 16384: |
206 | sc->sc_nrbuf = 8; |
207 | sc->sc_ntbuf = 2; |
208 | break; |
209 | case 32768: |
210 | sc->sc_nrbuf = 16; |
211 | sc->sc_ntbuf = 4; |
212 | break; |
213 | case 65536: |
214 | sc->sc_nrbuf = 32; |
215 | sc->sc_ntbuf = 8; |
216 | break; |
217 | case 131072: |
218 | sc->sc_nrbuf = 64; |
219 | sc->sc_ntbuf = 16; |
220 | break; |
221 | case 262144: |
222 | sc->sc_nrbuf = 128; |
223 | sc->sc_ntbuf = 32; |
224 | break; |
225 | default: |
226 | /* weird memory size; cope with it */ |
227 | nbuf = sc->sc_memsize / LEBLEN; |
228 | sc->sc_ntbuf = nbuf / 5; |
229 | sc->sc_nrbuf = nbuf - sc->sc_ntbuf; |
230 | } |
231 | |
232 | aprint_normal(": address %s\n" , ether_sprintf(sc->sc_enaddr)); |
233 | aprint_normal_dev(sc->sc_dev, |
234 | "%d receive buffers, %d transmit buffers\n" , |
235 | sc->sc_nrbuf, sc->sc_ntbuf); |
236 | |
237 | /* Make sure the chip is stopped. */ |
238 | lance_stop(ifp, 0); |
239 | |
240 | /* claim 802.1q capability */ |
241 | sc->sc_ethercom.ec_capabilities |= ETHERCAP_VLAN_MTU; |
242 | /* Attach the interface. */ |
243 | if_attach(ifp); |
244 | ether_ifattach(ifp, sc->sc_enaddr); |
245 | |
246 | if (pmf_device_register1(sc->sc_dev, NULL, NULL, lance_shutdown)) |
247 | pmf_class_network_register(sc->sc_dev, ifp); |
248 | else |
249 | aprint_error_dev(sc->sc_dev, |
250 | "couldn't establish power handler\n" ); |
251 | |
252 | sc->sc_rbufaddr = malloc(sc->sc_nrbuf * sizeof(int), M_DEVBUF, |
253 | M_WAITOK); |
254 | sc->sc_tbufaddr = malloc(sc->sc_ntbuf * sizeof(int), M_DEVBUF, |
255 | M_WAITOK); |
256 | |
257 | rnd_attach_source(&sc->rnd_source, device_xname(sc->sc_dev), |
258 | RND_TYPE_NET, RND_FLAG_DEFAULT); |
259 | } |
260 | |
261 | void |
262 | lance_reset(struct lance_softc *sc) |
263 | { |
264 | int s; |
265 | |
266 | s = splnet(); |
267 | lance_init(&sc->sc_ethercom.ec_if); |
268 | splx(s); |
269 | } |
270 | |
271 | void |
272 | lance_stop(struct ifnet *ifp, int disable) |
273 | { |
274 | struct lance_softc *sc = ifp->if_softc; |
275 | |
276 | (*sc->sc_wrcsr)(sc, LE_CSR0, LE_C0_STOP); |
277 | } |
278 | |
279 | /* |
280 | * Initialization of interface; set up initialization block |
281 | * and transmit/receive descriptor rings. |
282 | */ |
283 | int |
284 | lance_init(struct ifnet *ifp) |
285 | { |
286 | struct lance_softc *sc = ifp->if_softc; |
287 | int timo; |
288 | u_long a; |
289 | |
290 | (*sc->sc_wrcsr)(sc, LE_CSR0, LE_C0_STOP); |
291 | DELAY(100); |
292 | |
293 | /* Newer LANCE chips have a reset register */ |
294 | if (sc->sc_hwreset) |
295 | (*sc->sc_hwreset)(sc); |
296 | |
297 | /* Set the correct byte swapping mode, etc. */ |
298 | (*sc->sc_wrcsr)(sc, LE_CSR3, sc->sc_conf3); |
299 | |
300 | /* Set up LANCE init block. */ |
301 | (*sc->sc_meminit)(sc); |
302 | |
303 | /* Give LANCE the physical address of its init block. */ |
304 | a = sc->sc_addr + LE_INITADDR(sc); |
305 | (*sc->sc_wrcsr)(sc, LE_CSR1, a); |
306 | (*sc->sc_wrcsr)(sc, LE_CSR2, a >> 16); |
307 | |
308 | /* Try to initialize the LANCE. */ |
309 | DELAY(100); |
310 | (*sc->sc_wrcsr)(sc, LE_CSR0, LE_C0_INIT); |
311 | |
312 | /* Wait for initialization to finish. */ |
313 | for (timo = 100000; timo; timo--) |
314 | if ((*sc->sc_rdcsr)(sc, LE_CSR0) & LE_C0_IDON) |
315 | break; |
316 | |
317 | if ((*sc->sc_rdcsr)(sc, LE_CSR0) & LE_C0_IDON) { |
318 | /* Start the LANCE. */ |
319 | (*sc->sc_wrcsr)(sc, LE_CSR0, LE_C0_INEA | LE_C0_STRT); |
320 | ifp->if_flags |= IFF_RUNNING; |
321 | ifp->if_flags &= ~IFF_OACTIVE; |
322 | ifp->if_timer = 0; |
323 | (*sc->sc_start)(ifp); |
324 | } else |
325 | printf("%s: controller failed to initialize\n" , |
326 | device_xname(sc->sc_dev)); |
327 | if (sc->sc_hwinit) |
328 | (*sc->sc_hwinit)(sc); |
329 | |
330 | return (0); |
331 | } |
332 | |
333 | /* |
334 | * Routine to copy from mbuf chain to transmit buffer in |
335 | * network buffer memory. |
336 | */ |
337 | int |
338 | lance_put(struct lance_softc *sc, int boff, struct mbuf *m) |
339 | { |
340 | struct mbuf *n; |
341 | int len, tlen = 0; |
342 | |
343 | for (; m; m = n) { |
344 | len = m->m_len; |
345 | if (len == 0) { |
346 | n = m_free(m); |
347 | continue; |
348 | } |
349 | (*sc->sc_copytobuf)(sc, mtod(m, void *), boff, len); |
350 | boff += len; |
351 | tlen += len; |
352 | n = m_free(m); |
353 | } |
354 | if (tlen < LEMINSIZE) { |
355 | (*sc->sc_zerobuf)(sc, boff, LEMINSIZE - tlen); |
356 | tlen = LEMINSIZE; |
357 | } |
358 | return (tlen); |
359 | } |
360 | |
361 | /* |
362 | * Pull data off an interface. |
363 | * Len is length of data, with local net header stripped. |
364 | * We copy the data into mbufs. When full cluster sized units are present |
365 | * we copy into clusters. |
366 | */ |
367 | integrate struct mbuf * |
368 | lance_get(struct lance_softc *sc, int boff, int totlen) |
369 | { |
370 | struct mbuf *m, *m0, *newm; |
371 | int len; |
372 | |
373 | MGETHDR(m0, M_DONTWAIT, MT_DATA); |
374 | if (m0 == 0) |
375 | return (0); |
376 | m_set_rcvif(m0, &sc->sc_ethercom.ec_if); |
377 | m0->m_pkthdr.len = totlen; |
378 | len = MHLEN; |
379 | m = m0; |
380 | |
381 | while (totlen > 0) { |
382 | if (totlen >= MINCLSIZE) { |
383 | MCLGET(m, M_DONTWAIT); |
384 | if ((m->m_flags & M_EXT) == 0) |
385 | goto bad; |
386 | len = MCLBYTES; |
387 | } |
388 | |
389 | if (m == m0) { |
390 | char *newdata = (char *) |
391 | ALIGN(m->m_data + sizeof(struct ether_header)) - |
392 | sizeof(struct ether_header); |
393 | len -= newdata - m->m_data; |
394 | m->m_data = newdata; |
395 | } |
396 | |
397 | m->m_len = len = min(totlen, len); |
398 | (*sc->sc_copyfrombuf)(sc, mtod(m, void *), boff, len); |
399 | boff += len; |
400 | |
401 | totlen -= len; |
402 | if (totlen > 0) { |
403 | MGET(newm, M_DONTWAIT, MT_DATA); |
404 | if (newm == 0) |
405 | goto bad; |
406 | len = MLEN; |
407 | m = m->m_next = newm; |
408 | } |
409 | } |
410 | |
411 | return (m0); |
412 | |
413 | bad: |
414 | m_freem(m0); |
415 | return (0); |
416 | } |
417 | |
418 | /* |
419 | * Pass a packet to the higher levels. |
420 | */ |
421 | void |
422 | lance_read(struct lance_softc *sc, int boff, int len) |
423 | { |
424 | struct mbuf *m; |
425 | struct ifnet *ifp = &sc->sc_ethercom.ec_if; |
426 | struct ether_header *eh; |
427 | |
428 | if (len <= sizeof(struct ether_header) || |
429 | len > ((sc->sc_ethercom.ec_capenable & ETHERCAP_VLAN_MTU) ? |
430 | ETHER_VLAN_ENCAP_LEN + ETHERMTU + sizeof(struct ether_header) : |
431 | ETHERMTU + sizeof(struct ether_header))) { |
432 | #ifdef LEDEBUG |
433 | printf("%s: invalid packet size %d; dropping\n" , |
434 | device_xname(sc->sc_dev), len); |
435 | #endif |
436 | ifp->if_ierrors++; |
437 | return; |
438 | } |
439 | |
440 | /* Pull packet off interface. */ |
441 | m = lance_get(sc, boff, len); |
442 | if (m == 0) { |
443 | ifp->if_ierrors++; |
444 | return; |
445 | } |
446 | |
447 | ifp->if_ipackets++; |
448 | |
449 | eh = mtod(m, struct ether_header *); |
450 | |
451 | #ifdef LANCE_REVC_BUG |
452 | /* |
453 | * The old LANCE (Rev. C) chips have a bug which causes |
454 | * garbage to be inserted in front of the received packet. |
455 | * The work-around is to ignore packets with an invalid |
456 | * destination address (garbage will usually not match). |
457 | * Of course, this precludes multicast support... |
458 | */ |
459 | if (ETHER_CMP(eh->ether_dhost, sc->sc_enaddr) && |
460 | ETHER_CMP(eh->ether_dhost, bcast_enaddr)) { |
461 | m_freem(m); |
462 | return; |
463 | } |
464 | #endif |
465 | |
466 | /* |
467 | * Some lance device does not present IFF_SIMPLEX behavior on multicast |
468 | * packets. Make sure to drop it if it is from ourselves. |
469 | */ |
470 | if (!ETHER_CMP(eh->ether_shost, sc->sc_enaddr)) { |
471 | m_freem(m); |
472 | return; |
473 | } |
474 | |
475 | /* |
476 | * Check if there's a BPF listener on this interface. |
477 | * If so, hand off the raw packet to BPF. |
478 | */ |
479 | bpf_mtap(ifp, m); |
480 | |
481 | /* Pass the packet up. */ |
482 | if_percpuq_enqueue(ifp->if_percpuq, m); |
483 | } |
484 | |
485 | #undef ifp |
486 | |
487 | void |
488 | lance_watchdog(struct ifnet *ifp) |
489 | { |
490 | struct lance_softc *sc = ifp->if_softc; |
491 | |
492 | log(LOG_ERR, "%s: device timeout\n" , device_xname(sc->sc_dev)); |
493 | ++ifp->if_oerrors; |
494 | |
495 | lance_reset(sc); |
496 | } |
497 | |
498 | int |
499 | lance_mediachange(struct ifnet *ifp) |
500 | { |
501 | struct lance_softc *sc = ifp->if_softc; |
502 | |
503 | if (sc->sc_mediachange) |
504 | return ((*sc->sc_mediachange)(sc)); |
505 | return (0); |
506 | } |
507 | |
508 | void |
509 | lance_mediastatus(struct ifnet *ifp, struct ifmediareq *ifmr) |
510 | { |
511 | struct lance_softc *sc = ifp->if_softc; |
512 | |
513 | if ((ifp->if_flags & IFF_UP) == 0) |
514 | return; |
515 | |
516 | ifmr->ifm_status = IFM_AVALID; |
517 | if (sc->sc_havecarrier) |
518 | ifmr->ifm_status |= IFM_ACTIVE; |
519 | |
520 | if (sc->sc_mediastatus) |
521 | (*sc->sc_mediastatus)(sc, ifmr); |
522 | } |
523 | |
524 | /* |
525 | * Process an ioctl request. |
526 | */ |
527 | int |
528 | lance_ioctl(struct ifnet *ifp, u_long cmd, void *data) |
529 | { |
530 | struct lance_softc *sc = ifp->if_softc; |
531 | struct ifreq *ifr = (struct ifreq *)data; |
532 | int s, error = 0; |
533 | |
534 | s = splnet(); |
535 | |
536 | switch (cmd) { |
537 | case SIOCGIFMEDIA: |
538 | case SIOCSIFMEDIA: |
539 | error = ifmedia_ioctl(ifp, ifr, &sc->sc_media, cmd); |
540 | break; |
541 | default: |
542 | if ((error = ether_ioctl(ifp, cmd, data)) != ENETRESET) |
543 | break; |
544 | error = 0; |
545 | if (cmd != SIOCADDMULTI && cmd != SIOCDELMULTI) |
546 | break; |
547 | if (ifp->if_flags & IFF_RUNNING) { |
548 | /* |
549 | * Multicast list has changed; set the hardware filter |
550 | * accordingly. |
551 | */ |
552 | lance_reset(sc); |
553 | } |
554 | break; |
555 | |
556 | } |
557 | |
558 | splx(s); |
559 | return (error); |
560 | } |
561 | |
562 | hide bool |
563 | lance_shutdown(device_t self, int howto) |
564 | { |
565 | struct lance_softc *sc = device_private(self); |
566 | struct ifnet *ifp = &sc->sc_ethercom.ec_if; |
567 | |
568 | lance_stop(ifp, 0); |
569 | |
570 | return true; |
571 | } |
572 | |
573 | /* |
574 | * Set up the logical address filter. |
575 | */ |
576 | void |
577 | lance_setladrf(struct ethercom *ac, uint16_t *af) |
578 | { |
579 | struct ifnet *ifp = &ac->ec_if; |
580 | struct ether_multi *enm; |
581 | uint32_t crc; |
582 | struct ether_multistep step; |
583 | |
584 | /* |
585 | * Set up multicast address filter by passing all multicast addresses |
586 | * through a crc generator, and then using the high order 6 bits as an |
587 | * index into the 64 bit logical address filter. The high order bit |
588 | * selects the word, while the rest of the bits select the bit within |
589 | * the word. |
590 | */ |
591 | |
592 | if (ifp->if_flags & IFF_PROMISC) |
593 | goto allmulti; |
594 | |
595 | af[0] = af[1] = af[2] = af[3] = 0x0000; |
596 | ETHER_FIRST_MULTI(step, ac, enm); |
597 | while (enm != NULL) { |
598 | if (ETHER_CMP(enm->enm_addrlo, enm->enm_addrhi)) { |
599 | /* |
600 | * We must listen to a range of multicast addresses. |
601 | * For now, just accept all multicasts, rather than |
602 | * trying to set only those filter bits needed to match |
603 | * the range. (At this time, the only use of address |
604 | * ranges is for IP multicast routing, for which the |
605 | * range is big enough to require all bits set.) |
606 | */ |
607 | goto allmulti; |
608 | } |
609 | |
610 | crc = ether_crc32_le(enm->enm_addrlo, ETHER_ADDR_LEN); |
611 | |
612 | /* Just want the 6 most significant bits. */ |
613 | crc >>= 26; |
614 | |
615 | /* Set the corresponding bit in the filter. */ |
616 | af[crc >> 4] |= 1 << (crc & 0xf); |
617 | |
618 | ETHER_NEXT_MULTI(step, enm); |
619 | } |
620 | ifp->if_flags &= ~IFF_ALLMULTI; |
621 | return; |
622 | |
623 | allmulti: |
624 | ifp->if_flags |= IFF_ALLMULTI; |
625 | af[0] = af[1] = af[2] = af[3] = 0xffff; |
626 | } |
627 | |
628 | /* |
629 | * Routines for accessing the transmit and receive buffers. |
630 | * The various CPU and adapter configurations supported by this |
631 | * driver require three different access methods for buffers |
632 | * and descriptors: |
633 | * (1) contig (contiguous data; no padding), |
634 | * (2) gap2 (two bytes of data followed by two bytes of padding), |
635 | * (3) gap16 (16 bytes of data followed by 16 bytes of padding). |
636 | */ |
637 | |
638 | /* |
639 | * contig: contiguous data with no padding. |
640 | * |
641 | * Buffers may have any alignment. |
642 | */ |
643 | |
644 | void |
645 | lance_copytobuf_contig(struct lance_softc *sc, void *from, int boff, int len) |
646 | { |
647 | uint8_t *buf = sc->sc_mem; |
648 | |
649 | /* |
650 | * Just call memcpy() to do the work. |
651 | */ |
652 | memcpy(buf + boff, from, len); |
653 | } |
654 | |
655 | void |
656 | lance_copyfrombuf_contig(struct lance_softc *sc, void *to, int boff, int len) |
657 | { |
658 | uint8_t *buf = sc->sc_mem; |
659 | |
660 | /* |
661 | * Just call memcpy() to do the work. |
662 | */ |
663 | memcpy(to, buf + boff, len); |
664 | } |
665 | |
666 | void |
667 | lance_zerobuf_contig(struct lance_softc *sc, int boff, int len) |
668 | { |
669 | uint8_t *buf = sc->sc_mem; |
670 | |
671 | /* |
672 | * Just let memset() do the work |
673 | */ |
674 | memset(buf + boff, 0, len); |
675 | } |
676 | |
677 | #if 0 |
678 | /* |
679 | * Examples only; duplicate these and tweak (if necessary) in |
680 | * machine-specific front-ends. |
681 | */ |
682 | |
683 | /* |
684 | * gap2: two bytes of data followed by two bytes of pad. |
685 | * |
686 | * Buffers must be 4-byte aligned. The code doesn't worry about |
687 | * doing an extra byte. |
688 | */ |
689 | |
690 | void |
691 | lance_copytobuf_gap2(struct lance_softc *sc, void *fromv, int boff, int len) |
692 | { |
693 | volatile void *buf = sc->sc_mem; |
694 | void *from = fromv; |
695 | volatile uint16_t *bptr; |
696 | |
697 | if (boff & 0x1) { |
698 | /* handle unaligned first byte */ |
699 | bptr = ((volatile uint16_t *)buf) + (boff - 1); |
700 | *bptr = (*from++ << 8) | (*bptr & 0xff); |
701 | bptr += 2; |
702 | len--; |
703 | } else |
704 | bptr = ((volatile uint16_t *)buf) + boff; |
705 | while (len > 1) { |
706 | *bptr = (from[1] << 8) | (from[0] & 0xff); |
707 | bptr += 2; |
708 | from += 2; |
709 | len -= 2; |
710 | } |
711 | if (len == 1) |
712 | *bptr = (uint16_t)*from; |
713 | } |
714 | |
715 | void |
716 | lance_copyfrombuf_gap2(struct lance_softc *sc, void *tov, int boff, int len) |
717 | { |
718 | volatile void *buf = sc->sc_mem; |
719 | void *to = tov; |
720 | volatile uint16_t *bptr; |
721 | uint16_t tmp; |
722 | |
723 | if (boff & 0x1) { |
724 | /* handle unaligned first byte */ |
725 | bptr = ((volatile uint16_t *)buf) + (boff - 1); |
726 | *to++ = (*bptr >> 8) & 0xff; |
727 | bptr += 2; |
728 | len--; |
729 | } else |
730 | bptr = ((volatile uint16_t *)buf) + boff; |
731 | while (len > 1) { |
732 | tmp = *bptr; |
733 | *to++ = tmp & 0xff; |
734 | *to++ = (tmp >> 8) & 0xff; |
735 | bptr += 2; |
736 | len -= 2; |
737 | } |
738 | if (len == 1) |
739 | *to = *bptr & 0xff; |
740 | } |
741 | |
742 | void |
743 | lance_zerobuf_gap2(struct lance_softc *sc, int boff, int len) |
744 | { |
745 | volatile void *buf = sc->sc_mem; |
746 | volatile uint16_t *bptr; |
747 | |
748 | if ((unsigned int)boff & 0x1) { |
749 | bptr = ((volatile uint16_t *)buf) + (boff - 1); |
750 | *bptr &= 0xff; |
751 | bptr += 2; |
752 | len--; |
753 | } else |
754 | bptr = ((volatile uint16_t *)buf) + boff; |
755 | while (len > 0) { |
756 | *bptr = 0; |
757 | bptr += 2; |
758 | len -= 2; |
759 | } |
760 | } |
761 | |
762 | /* |
763 | * gap16: 16 bytes of data followed by 16 bytes of pad. |
764 | * |
765 | * Buffers must be 32-byte aligned. |
766 | */ |
767 | |
768 | void |
769 | lance_copytobuf_gap16(struct lance_softc *sc, void *fromv, int boff, int len) |
770 | { |
771 | volatile uint8_t *buf = sc->sc_mem; |
772 | void *from = fromv; |
773 | uint8_t *bptr; |
774 | int xfer; |
775 | |
776 | bptr = buf + ((boff << 1) & ~0x1f); |
777 | boff &= 0xf; |
778 | xfer = min(len, 16 - boff); |
779 | while (len > 0) { |
780 | memcpy(bptr + boff, from, xfer); |
781 | from += xfer; |
782 | bptr += 32; |
783 | boff = 0; |
784 | len -= xfer; |
785 | xfer = min(len, 16); |
786 | } |
787 | } |
788 | |
789 | void |
790 | lance_copyfrombuf_gap16(struct lance_softc *sc, void *tov, int boff, int len) |
791 | { |
792 | volatile uint8_t *buf = sc->sc_mem; |
793 | void *to = tov; |
794 | uint8_t *bptr; |
795 | int xfer; |
796 | |
797 | bptr = buf + ((boff << 1) & ~0x1f); |
798 | boff &= 0xf; |
799 | xfer = min(len, 16 - boff); |
800 | while (len > 0) { |
801 | memcpy(to, bptr + boff, xfer); |
802 | to += xfer; |
803 | bptr += 32; |
804 | boff = 0; |
805 | len -= xfer; |
806 | xfer = min(len, 16); |
807 | } |
808 | } |
809 | |
810 | void |
811 | lance_zerobuf_gap16(struct lance_softc *sc, int boff, int len) |
812 | { |
813 | volatile uint8_t *buf = sc->sc_mem; |
814 | uint8_t *bptr; |
815 | int xfer; |
816 | |
817 | bptr = buf + ((boff << 1) & ~0x1f); |
818 | boff &= 0xf; |
819 | xfer = min(len, 16 - boff); |
820 | while (len > 0) { |
821 | memset(bptr + boff, 0, xfer); |
822 | bptr += 32; |
823 | boff = 0; |
824 | len -= xfer; |
825 | xfer = min(len, 16); |
826 | } |
827 | } |
828 | #endif /* Example only */ |
829 | |