1 /* $NetBSD: lance.c,v 1.34 2005/12/24 20:27:30 perry Exp $ */
2 /* $FreeBSD: src/sys/dev/le/lance.c,v 1.2 2006/05/16 21:04:01 marius Exp $ */
3 /* $DragonFly: src/sys/dev/netif/lnc/lance.c,v 1.7 2008/05/14 11:59:20 sephe Exp $ */
7 * Copyright (c) 1997, 1998 The NetBSD Foundation, Inc.
10 * This code is derived from software contributed to The NetBSD Foundation
11 * by Charles M. Hannum and by Jason R. Thorpe of the Numerical Aerospace
12 * Simulation Facility, NASA Ames Research Center.
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions
17 * 1. Redistributions of source code must retain the above copyright
18 * notice, this list of conditions and the following disclaimer.
19 * 2. Redistributions in binary form must reproduce the above copyright
20 * notice, this list of conditions and the following disclaimer in the
21 * documentation and/or other materials provided with the distribution.
22 * 3. All advertising materials mentioning features or use of this software
23 * must display the following acknowledgement:
24 * This product includes software developed by the NetBSD
25 * Foundation, Inc. and its contributors.
26 * 4. Neither the name of The NetBSD Foundation nor the names of its
27 * contributors may be used to endorse or promote products derived
28 * from this software without specific prior written permission.
30 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
31 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
32 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
33 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
34 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
35 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
36 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
37 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
38 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
39 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
40 * POSSIBILITY OF SUCH DAMAGE.
44 * Copyright (c) 1992, 1993
45 * The Regents of the University of California. All rights reserved.
47 * This code is derived from software contributed to Berkeley by
48 * Ralph Campbell and Rick Macklem.
50 * Redistribution and use in source and binary forms, with or without
51 * modification, are permitted provided that the following conditions
53 * 1. Redistributions of source code must retain the above copyright
54 * notice, this list of conditions and the following disclaimer.
55 * 2. Redistributions in binary form must reproduce the above copyright
56 * notice, this list of conditions and the following disclaimer in the
57 * documentation and/or other materials provided with the distribution.
58 * 3. Neither the name of the University nor the names of its contributors
59 * may be used to endorse or promote products derived from this software
60 * without specific prior written permission.
62 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
63 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
64 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
65 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
66 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
67 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
68 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
69 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
70 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
71 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
74 * @(#)if_le.c 8.2 (Berkeley) 11/16/93
77 #include <sys/param.h>
79 #include <sys/endian.h>
82 #include <sys/socket.h>
83 #include <sys/sockio.h>
85 #include <net/ethernet.h>
87 #include <net/ifq_var.h>
88 #include <net/if_arp.h>
89 #include <net/if_dl.h>
90 #include <net/if_media.h>
91 #include <net/if_types.h>
92 #include <net/vlan/if_vlan_var.h>
94 #include <dev/netif/lnc/lancereg.h>
95 #include <dev/netif/lnc/lancevar.h>
97 devclass_t le_devclass
;
99 static void lance_start(struct ifnet
*);
100 static void lance_init(void *);
101 static void lance_watchdog(struct ifnet
*);
102 static int lance_mediachange(struct ifnet
*);
103 static void lance_mediastatus(struct ifnet
*, struct ifmediareq
*);
104 static int lance_ioctl(struct ifnet
*, u_long
, caddr_t
, struct ucred
*);
107 lance_config(struct lance_softc
*sc
, const char* name
, int unit
)
112 ifp
= sc
->ifp
= &sc
->sc_if
;
114 /* Initialize ifnet structure. */
116 if_initname(ifp
, name
, unit
);
117 ifp
->if_start
= lance_start
;
118 ifp
->if_ioctl
= lance_ioctl
;
119 ifp
->if_watchdog
= lance_watchdog
;
120 ifp
->if_init
= lance_init
;
121 ifp
->if_flags
= IFF_BROADCAST
| IFF_SIMPLEX
| IFF_MULTICAST
;
122 #ifdef LANCE_REVC_BUG
123 ifp
->if_flags
&= ~IFF_MULTICAST
;
125 ifp
->if_baudrate
= IF_Mbps(10);
126 ifq_set_maxlen(&ifp
->if_snd
, IFQ_MAXLEN
);
127 ifq_set_ready(&ifp
->if_snd
);
129 /* Initialize ifmedia structures. */
130 ifmedia_init(&sc
->sc_media
, 0, lance_mediachange
, lance_mediastatus
);
131 if (sc
->sc_supmedia
!= NULL
) {
132 for (i
= 0; i
< sc
->sc_nsupmedia
; i
++)
133 ifmedia_add(&sc
->sc_media
, sc
->sc_supmedia
[i
], 0, NULL
);
134 ifmedia_set(&sc
->sc_media
, sc
->sc_defaultmedia
);
136 ifmedia_add(&sc
->sc_media
,
137 IFM_MAKEWORD(IFM_ETHER
, IFM_MANUAL
, 0, 0), 0, NULL
);
138 ifmedia_set(&sc
->sc_media
,
139 IFM_MAKEWORD(IFM_ETHER
, IFM_MANUAL
, 0, 0));
142 switch (sc
->sc_memsize
) {
168 /* weird memory size; cope with it */
169 nbuf
= sc
->sc_memsize
/ LEBLEN
;
170 sc
->sc_ntbuf
= nbuf
/ 5;
171 sc
->sc_nrbuf
= nbuf
- sc
->sc_ntbuf
;
174 if_printf(ifp
, "%d receive buffers, %d transmit buffers\n",
175 sc
->sc_nrbuf
, sc
->sc_ntbuf
);
177 /* Make sure the chip is stopped. */
184 lance_attach(struct lance_softc
*sc
)
186 struct ifnet
*ifp
= sc
->ifp
;
188 /* Attach the interface. */
189 ether_ifattach(ifp
, sc
->sc_enaddr
, NULL
);
191 /* Claim 802.1q capability. */
192 ifp
->if_data
.ifi_hdrlen
= sizeof(struct ether_vlan_header
);
193 ifp
->if_capabilities
|= IFCAP_VLAN_MTU
;
194 ifp
->if_capenable
|= IFCAP_VLAN_MTU
;
198 lance_suspend(struct lance_softc
*sc
)
201 lwkt_serialize_enter(sc
->ifp
->if_serializer
);
203 lwkt_serialize_exit(sc
->ifp
->if_serializer
);
207 lance_resume(struct lance_softc
*sc
)
210 lwkt_serialize_enter(sc
->ifp
->if_serializer
);
211 if (sc
->ifp
->if_flags
& IFF_UP
)
212 lance_init_locked(sc
);
213 lwkt_serialize_exit(sc
->ifp
->if_serializer
);
217 lance_start(struct ifnet
*ifp
)
219 struct lance_softc
*sc
= ifp
->if_softc
;
221 (*sc
->sc_start_locked
)(sc
);
225 lance_stop(struct lance_softc
*sc
)
227 struct ifnet
*ifp
= sc
->ifp
;
230 * Mark the interface down and cancel the watchdog timer.
232 ifp
->if_flags
&= ~(IFF_RUNNING
| IFF_OACTIVE
);
235 (*sc
->sc_wrcsr
)(sc
, LE_CSR0
, LE_C0_STOP
);
239 lance_init(void *xsc
)
241 struct lance_softc
*sc
= (struct lance_softc
*)xsc
;
244 lance_init_locked(sc
);
249 * Initialization of interface; set up initialization block
250 * and transmit/receive descriptor rings.
253 lance_init_locked(struct lance_softc
*sc
)
255 struct ifnet
*ifp
= sc
->ifp
;
259 (*sc
->sc_wrcsr
)(sc
, LE_CSR0
, LE_C0_STOP
);
262 /* Newer LANCE chips have a reset register. */
264 (*sc
->sc_hwreset
)(sc
);
266 /* Set the correct byte swapping mode, etc. */
267 (*sc
->sc_wrcsr
)(sc
, LE_CSR3
, sc
->sc_conf3
);
270 * Update our private copy of the Ethernet address.
271 * We NEED the copy so we can ensure its alignment!
273 memcpy(sc
->sc_enaddr
, IF_LLADDR(ifp
), ETHER_ADDR_LEN
);
275 /* Set up LANCE init block. */
276 (*sc
->sc_meminit
)(sc
);
278 /* Give LANCE the physical address of its init block. */
279 a
= sc
->sc_addr
+ LE_INITADDR(sc
);
280 (*sc
->sc_wrcsr
)(sc
, LE_CSR1
, a
& 0xffff);
281 (*sc
->sc_wrcsr
)(sc
, LE_CSR2
, a
>> 16);
283 /* Try to initialize the LANCE. */
285 (*sc
->sc_wrcsr
)(sc
, LE_CSR0
, LE_C0_INIT
);
287 /* Wait for initialization to finish. */
288 for (timo
= 100000; timo
; timo
--)
289 if ((*sc
->sc_rdcsr
)(sc
, LE_CSR0
) & LE_C0_IDON
)
292 /* Set the current media. */
293 if (sc
->sc_mediachange
)
294 (*sc
->sc_mediachange
)(sc
);
296 if ((*sc
->sc_rdcsr
)(sc
, LE_CSR0
) & LE_C0_IDON
) {
297 /* Start the LANCE. */
298 (*sc
->sc_wrcsr
)(sc
, LE_CSR0
, LE_C0_INEA
| LE_C0_STRT
);
299 ifp
->if_flags
|= IFF_RUNNING
;
300 ifp
->if_flags
&= ~IFF_OACTIVE
;
304 if_printf(ifp
, "controller failed to initialize\n");
307 (*sc
->sc_hwinit
)(sc
);
311 * Routine to copy from mbuf chain to transmit buffer in
312 * network buffer memory.
315 lance_put(struct lance_softc
*sc
, int boff
, struct mbuf
*m
)
319 for (; m
; m
= m_free(m
)) {
322 (*sc
->sc_copytobuf
)(sc
, mtod(m
, caddr_t
), boff
, m
->m_len
);
326 if (tlen
< LEMINSIZE
) {
327 (*sc
->sc_zerobuf
)(sc
, boff
, LEMINSIZE
- tlen
);
334 * Pull data off an interface.
335 * Len is length of data, with local net header stripped.
336 * We copy the data into mbufs. When full cluster sized units are present
337 * we copy into clusters.
340 lance_get(struct lance_softc
*sc
, int boff
, int totlen
)
342 struct ifnet
*ifp
= sc
->ifp
;
343 struct mbuf
*m0
= NULL
, *newm
;
344 struct mbuf
*m
= NULL
;
347 if (totlen
<= ETHER_HDR_LEN
|| totlen
> LEBLEN
- ETHER_CRC_LEN
) {
349 if_printf(ifp
, "invalid packet size %d; dropping\n", totlen
);
355 newm
= m_getl(totlen
, MB_DONTWAIT
, MT_DATA
,
356 m0
== NULL
? M_PKTHDR
: 0, &mlen
);
364 m0
->m_pkthdr
.rcvif
= ifp
;
365 m0
->m_pkthdr
.len
= totlen
;
367 ALIGN(m0
->m_data
+ ETHER_HDR_LEN
) - ETHER_HDR_LEN
;
368 mlen
-= newdata
- m0
->m_data
;
369 m0
->m_data
= newdata
;
375 m
->m_len
= min(totlen
, mlen
);
376 (*sc
->sc_copyfrombuf
)(sc
, mtod(m
, caddr_t
), boff
, m
->m_len
);
379 } while (totlen
> 0);
389 lance_watchdog(struct ifnet
*ifp
)
391 struct lance_softc
*sc
= ifp
->if_softc
;
393 if_printf(ifp
, "device timeout\n");
395 lance_init_locked(sc
);
399 lance_mediachange(struct ifnet
*ifp
)
401 struct lance_softc
*sc
= ifp
->if_softc
;
404 if (sc
->sc_mediachange
) {
405 error
= (*sc
->sc_mediachange
)(sc
);
412 lance_mediastatus(struct ifnet
*ifp
, struct ifmediareq
*ifmr
)
414 struct lance_softc
*sc
= ifp
->if_softc
;
416 if (!(ifp
->if_flags
& IFF_UP
)) {
420 ifmr
->ifm_status
= IFM_AVALID
;
421 if (sc
->sc_flags
& LE_CARRIER
)
422 ifmr
->ifm_status
|= IFM_ACTIVE
;
424 if (sc
->sc_mediastatus
)
425 (*sc
->sc_mediastatus
)(sc
, ifmr
);
429 * Process an ioctl request.
432 lance_ioctl(struct ifnet
*ifp
, u_long cmd
, caddr_t data
, struct ucred
*cr
)
434 struct lance_softc
*sc
= ifp
->if_softc
;
435 struct ifreq
*ifr
= (struct ifreq
*)data
;
442 if (ifp
->if_flags
& IFF_PROMISC
) {
443 if (!(sc
->sc_flags
& LE_PROMISC
)) {
444 sc
->sc_flags
|= LE_PROMISC
;
445 lance_init_locked(sc
);
447 } else if (sc
->sc_flags
& LE_PROMISC
) {
448 sc
->sc_flags
&= ~LE_PROMISC
;
449 lance_init_locked(sc
);
452 if ((ifp
->if_flags
& IFF_ALLMULTI
) &&
453 !(sc
->sc_flags
& LE_ALLMULTI
)) {
454 sc
->sc_flags
|= LE_ALLMULTI
;
455 lance_init_locked(sc
);
456 } else if (!(ifp
->if_flags
& IFF_ALLMULTI
) &&
457 (sc
->sc_flags
& LE_ALLMULTI
)) {
458 sc
->sc_flags
&= ~LE_ALLMULTI
;
459 lance_init_locked(sc
);
462 if (!(ifp
->if_flags
& IFF_UP
) &&
463 ifp
->if_flags
& IFF_RUNNING
) {
465 * If interface is marked down and it is running, then
469 } else if (ifp
->if_flags
& IFF_UP
&&
470 !(ifp
->if_flags
& IFF_RUNNING
)) {
472 * If interface is marked up and it is stopped, then
475 lance_init_locked(sc
);
478 if (ifp
->if_flags
& IFF_DEBUG
)
479 sc
->sc_flags
|= LE_DEBUG
;
481 sc
->sc_flags
&= ~LE_DEBUG
;
488 * Multicast list has changed; set the hardware filter
491 if (ifp
->if_flags
& IFF_RUNNING
)
492 lance_init_locked(sc
);
497 error
= ifmedia_ioctl(ifp
, ifr
, &sc
->sc_media
, cmd
);
501 error
= ether_ioctl(ifp
, cmd
, data
);
511 * Set up the logical address filter.
514 lance_setladrf(struct lance_softc
*sc
, uint16_t *af
)
516 struct ifnet
*ifp
= sc
->ifp
;
517 struct ifmultiaddr
*ifma
;
521 * Set up multicast address filter by passing all multicast addresses
522 * through a crc generator, and then using the high order 6 bits as an
523 * index into the 64 bit logical address filter. The high order bit
524 * selects the word, while the rest of the bits select the bit within
528 if (ifp
->if_flags
& IFF_PROMISC
|| sc
->sc_flags
& LE_ALLMULTI
) {
529 af
[0] = af
[1] = af
[2] = af
[3] = 0xffff;
533 af
[0] = af
[1] = af
[2] = af
[3] = 0x0000;
535 LIST_FOREACH(ifma
, &ifp
->if_multiaddrs
, ifma_link
) {
536 if (ifma
->ifma_addr
->sa_family
!= AF_LINK
)
539 crc
= ether_crc32_le(LLADDR((struct sockaddr_dl
*)
540 ifma
->ifma_addr
), ETHER_ADDR_LEN
);
542 /* Just want the 6 most significant bits. */
545 /* Set the corresponding bit in the filter. */
546 af
[crc
>> 4] |= LE_HTOLE16(1 << (crc
& 0xf));
551 * Routines for accessing the transmit and receive buffers.
552 * The various CPU and adapter configurations supported by this
553 * driver require three different access methods for buffers
555 * (1) contig (contiguous data; no padding),
556 * (2) gap2 (two bytes of data followed by two bytes of padding),
557 * (3) gap16 (16 bytes of data followed by 16 bytes of padding).
561 * contig: contiguous data with no padding.
563 * Buffers may have any alignment.
567 lance_copytobuf_contig(struct lance_softc
*sc
, void *from
, int boff
, int len
)
569 volatile caddr_t buf
= sc
->sc_mem
;
572 * Just call memcpy() to do the work.
574 memcpy(buf
+ boff
, from
, len
);
578 lance_copyfrombuf_contig(struct lance_softc
*sc
, void *to
, int boff
, int len
)
580 volatile caddr_t buf
= sc
->sc_mem
;
583 * Just call memcpy() to do the work.
585 memcpy(to
, buf
+ boff
, len
);
589 lance_zerobuf_contig(struct lance_softc
*sc
, int boff
, int len
)
591 volatile caddr_t buf
= sc
->sc_mem
;
594 * Just let memset() do the work
596 memset(buf
+ boff
, 0, len
);
601 * Examples only; duplicate these and tweak (if necessary) in
602 * machine-specific front-ends.
606 * gap2: two bytes of data followed by two bytes of pad.
608 * Buffers must be 4-byte aligned. The code doesn't worry about
609 * doing an extra byte.
613 lance_copytobuf_gap2(struct lance_softc
*sc
, void *fromv
, int boff
, int len
)
615 volatile caddr_t buf
= sc
->sc_mem
;
616 caddr_t from
= fromv
;
617 volatile uint16_t *bptr
;
620 /* Handle unaligned first byte. */
621 bptr
= ((volatile uint16_t *)buf
) + (boff
- 1);
622 *bptr
= (*from
++ << 8) | (*bptr
& 0xff);
626 bptr
= ((volatile uint16_t *)buf
) + boff
;
628 *bptr
= (from
[1] << 8) | (from
[0] & 0xff);
634 *bptr
= (uint16_t)*from
;
638 lance_copyfrombuf_gap2(struct lance_softc
*sc
, void *tov
, int boff
, int len
)
640 volatile caddr_t buf
= sc
->sc_mem
;
642 volatile uint16_t *bptr
;
646 /* Handle unaligned first byte. */
647 bptr
= ((volatile uint16_t *)buf
) + (boff
- 1);
648 *to
++ = (*bptr
>> 8) & 0xff;
652 bptr
= ((volatile uint16_t *)buf
) + boff
;
656 *to
++ = (tmp
>> 8) & 0xff;
665 lance_zerobuf_gap2(struct lance_softc
*sc
, int boff
, int len
)
667 volatile caddr_t buf
= sc
->sc_mem
;
668 volatile uint16_t *bptr
;
670 if ((unsigned)boff
& 0x1) {
671 bptr
= ((volatile uint16_t *)buf
) + (boff
- 1);
676 bptr
= ((volatile uint16_t *)buf
) + boff
;
685 * gap16: 16 bytes of data followed by 16 bytes of pad.
687 * Buffers must be 32-byte aligned.
691 lance_copytobuf_gap16(struct lance_softc
*sc
, void *fromv
, int boff
, int len
)
693 volatile caddr_t buf
= sc
->sc_mem
;
694 caddr_t bptr
, from
= fromv
;
697 bptr
= buf
+ ((boff
<< 1) & ~0x1f);
699 xfer
= min(len
, 16 - boff
);
701 memcpy(bptr
+ boff
, from
, xfer
);
711 lance_copyfrombuf_gap16(struct lance_softc
*sc
, void *tov
, int boff
, int len
)
713 volatile caddr_t buf
= sc
->sc_mem
;
714 caddr_t bptr
, to
= tov
;
717 bptr
= buf
+ ((boff
<< 1) & ~0x1f);
719 xfer
= min(len
, 16 - boff
);
721 memcpy(to
, bptr
+ boff
, xfer
);
731 lance_zerobuf_gap16(struct lance_softc
*sc
, int boff
, int len
)
733 volatile caddr_t buf
= sc
->sc_mem
;
737 bptr
= buf
+ ((boff
<< 1) & ~0x1f);
739 xfer
= min(len
, 16 - boff
);
741 memset(bptr
+ boff
, 0, xfer
);
748 #endif /* Example only */