diff --git a/share/man/man4/le.4 b/share/man/man4/le.4 index 2c7f9cc3e55e..58a6826bb03f 100644 --- a/share/man/man4/le.4 +++ b/share/man/man4/le.4 @@ -1,370 +1,376 @@ .\" $NetBSD: le.4,v 1.22 2004/10/04 19:12:52 rumble Exp $ .\" .\"- .\" Copyright (c) 1992, 1993 .\" The Regents of the University of California. All rights reserved. .\" .\" This software was developed by the Computer Systems Engineering group .\" at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and .\" contributed to Berkeley. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" 3. Neither the name of the University nor the names of its contributors .\" may be used to endorse or promote products derived from this software .\" without specific prior written permission. .\" .\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .\" from: Header: le.4,v 1.2 92/10/13 05:31:33 leres Exp .\" from: @(#)le.4 8.1 (Berkeley) 6/9/93 .\" $FreeBSD$ .\" -.Dd December 26, 2020 +.Dd June 21, 2023 .Dt LE 4 .Os .Sh NAME .Nm le .Nd "AMD Am7900 LANCE and Am79C9xx ILACC/PCnet Ethernet interface driver" .Sh SYNOPSIS To compile this driver into the kernel, place the following line in your kernel configuration file: .Bd -ragged -offset indent .Cd "device le" .Ed .Pp Alternatively, to load the driver as a module at boot time, place the following line in .Xr loader.conf 5 : .Bd -literal -offset indent if_le_load="YES" .Ed .Pp For ISA non-PnP adapters, the port address as well as the IRQ and the DRQ numbers have to be specified in .Pa /boot/device.hints : .Cd hint.le.0.at="isa" .Cd hint.le.0.port="0x280" .Cd hint.le.0.irq="10" .Cd hint.le.0.drq="0" +.Sh DEPRECATION NOTICE +The +.Nm +driver may not be present in +.Fx 15.0 +and later. .Sh DESCRIPTION The .Nm driver provides support for Ethernet adapters based on the .Tn AMD Am7990 and .Tn Am79C90 .Pq CMOS, pin-compatible Local Area Network Controller for Ethernet .Pq Tn LANCE chips. .Pp The .Nm driver also supports Ethernet adapters based on the .Tn AMD Am79C900 Integrated Local Area Communications Controller .Pq Tn ILACC as well as the .Tn Am79C9xx PCnet family of chips, which are single-chip implementations of a .Tn LANCE chip and a DMA engine. The .Nm driver treats all of these .Tn PCI bus Ethernet chips as an .Tn AMD Am79C970 PCnet-PCI and does not support the additional features like the MII bus and burst mode of .Tn AMD Am79C971 PCnet-FAST and greater chips. .Pp Generally, the .Nm driver aims at supporting as many different chips on as many different platforms as possible, partially at the cost of the best performance with some of these. .Pp The .Nm driver supports reception and transmission of extended frames for .Xr vlan 4 . Selective reception of multicast Ethernet frames is provided by a 64-bit mask; multicast destination addresses are hashed to a bit entry using the Ethernet CRC function. .Sh HARDWARE .Ss ISA The .Nm driver supports .Tn ISA bus Ethernet adapters which are based on the following chips: .Pp .Bl -bullet -compact .It .Tn AMD Am7990 and Am79C90 LANCE .It .Tn AMD Am79C960 PCnet-ISA .It .Tn AMD Am79C961 PCnet-ISA+ .It .Tn AMD Am79C961A PCnet-ISA II .El .Pp This includes support for the following Ethernet adapters: .Pp ISA non-PnP: .Pp .Bl -bullet -compact .It .Tn BICC Isolan .\" .It .\" .Tn Digital DEPCA .It .Tn Novell NE2100 .El .Pp ISA PnP: .Pp .Bl -bullet -compact .It .Tn AMD AM1500T/AM2100 .It .Tn AMD PCnet-32 .It .Tn AMD PCnet-ISA .It .Tn Allied Telesyn AT-1500 .It .Tn Boca LANCard Combo .It .Tn Cabletron E2100 Series DNI .It .Tn Cabletron E2200 Single Chip .It .Tn Melco Inc. LGY-IV .It .Tn Novell NE2100 .It .Tn Racal InterLan EtherBlaster .El .Pp The .Nm driver does not support the selection of media types and options via .Xr ifconfig 8 with .Tn ISA bus Ethernet adapters. .Ss PCI The .Tn PCI bus Ethernet chips supported by the .Nm driver are: .Pp .Bl -bullet -compact .It .Tn AMD Am53C974/Am79C970/Am79C974 PCnet-PCI .It .Tn AMD Am79C970A PCnet-PCI II .It .Tn AMD Am79C971 PCnet-FAST .It .Tn AMD Am79C972 PCnet-FAST+ .It .Tn AMD Am79C973/Am79C975 PCnet-FAST III .It .Tn AMD Am79C976 PCnet-PRO .It .Tn AMD Am79C978 PCnet-Home .El .Pp This includes support for the following Ethernet adapters: .Pp .Bl -bullet -compact .It .Tn AcerLAN NIC P20 .It .Tn Allied Telesyn AT-2450 and AT-2700 series .It .Tn VMware emulated AMD Am79C970A PCnet-PCI II interface .El .Pp The .Nm driver supports the selection of the following media types via .Xr ifconfig 8 with .Tn PCI bus Ethernet adapters: .Bl -tag -width ".Cm 10base5/AUI" .It Cm autoselect Enable autoselection of the media type. .It Cm 10baseT/UTP Select UTP media. .It Cm 10base5/AUI Select AUI/BNC media. .El .Pp The following media option is supported with these media types: .Bl -tag -width ".Cm full-duplex" .It Cm full-duplex Select full duplex operation. .El .Pp Note that the .Nm driver does not support selecting 100Mbps (Fast Ethernet) media types. .Sh DIAGNOSTICS .Bl -diag .It "le%d: overflow" More packets came in from the Ethernet than there was space in the .Tn LANCE receive buffers. Packets were missed. .It "le%d: receive buffer error" The .Tn LANCE ran out of buffer space, packet dropped. .It "le%d: lost carrier" The Ethernet carrier disappeared during an attempt to transmit. The .Tn LANCE will finish transmitting the current packet, but will not automatically retry transmission if there is a collision. .It "le%d: excessive collisions, tdr %d" The Ethernet was extremely busy or jammed, outbound packets were dropped after 16 attempts to retransmit. .Pp TDR is the abbreviation of .Qq Time Domain Reflectometry . The optionally reported TDR value is an internal counter of the interval between the start of a transmission and the occurrence of a collision. This value can be used to determine the distance from the Ethernet tap to the point on the Ethernet cable that is shorted or open (unterminated). .It "le%d: dropping chained buffer" A packet did not fit into a single receive buffer and was dropped. Since the .Nm driver allocates buffers large enough to receive maximum sized Ethernet packets, this means some other station on the LAN transmitted a packet larger than allowed by the Ethernet standard. .It "le%d: transmit buffer error" The .Tn LANCE ran out of buffer space before finishing the transmission of a packet. If this error occurs, the driver software has a bug. .It "le%d: underflow" The .Tn LANCE ran out of buffer space before finishing the transmission of a packet. If this error occurs, the driver software has a bug. .It "le%d: controller failed to initialize" Driver failed to start the .Tn LANCE . This is potentially a hardware failure. .It "le%d: memory error" RAM failed to respond within the timeout when the .Tn LANCE wanted to read or write it. This is potentially a hardware failure. .It "le%d: receiver disabled" The receiver of the .Tn LANCE was turned off due to an error. .It "le%d: transmitter disabled" The transmitter of the .Tn LANCE was turned off due to an error. .El .Sh SEE ALSO .Xr altq 4 , .Xr arp 4 , .Xr intro 4 , .Xr netintro 4 , .Xr vlan 4 , .Xr ifconfig 8 .Sh HISTORY The .Nm driver was ported from .Nx and first appeared in .Fx 6.1 . The .Nx version in turn was derived from the .Nm driver which first appeared in .Bx 4.4 . .Sh AUTHORS The .Nm driver was ported by .An Marius Strobl Aq Mt marius@FreeBSD.org . .\" .Sh BUGS .\" The Am7990 Revision C chips have a bug which causes garbage to be inserted .\" in front of the received packet occasionally. .\" The work-around is to ignore packets with an invalid destination address .\" (garbage will usually not match), by double-checking the destination .\" address of every packet in the driver. .\" This work-around can be enabled with the .\" .Dv LANCE_REVC_BUG .\" kernel option. .\" .Pp .\" When .\" .Dv LANCE_REVC_BUG .\" is enabled, the .\" .Nm .\" driver executes one or two calls to an inline Ethernet address comparison .\" function for every received packet. .\" On the .\" .Tn MC68000 .\" it is exactly eight instructions of 16 bits each. .\" There is one comparison for each unicast packet, and two comparisons for .\" each broadcast packet. .\" .Pp .\" In summary, the cost of the LANCE_REVC_BUG option is: .\" .Bl -enum -compact .\" .It .\" loss of multicast support, and .\" .It .\" eight extra .\" .Tn CPU .\" instructions per received packet, sometimes sixteen, depending on both the .\" processor, and the type of packet. .\" .El .\" .Pp .\" All sun3 systems are presumed to have this bad revision of the Am7990, .\" until proven otherwise. .\" Alas, the only way to prove what revision of the chip is in a particular .\" system is inspection of the date code on the chip package, .\" to compare against a list of what chip revisions were fabricated between .\" which dates. .\" .Pp .\" Alas, the Am7990 chip is so old that .\" .Tn AMD .\" has .\" .Qq de-archived .\" the production information about it; pending a search elsewhere, we do not .\" know how to identify the revision C chip from the date codes. diff --git a/sys/dev/le/lance.c b/sys/dev/le/lance.c index c10ebb074e65..9b8a39086371 100644 --- a/sys/dev/le/lance.c +++ b/sys/dev/le/lance.c @@ -1,822 +1,824 @@ /* $NetBSD: lance.c,v 1.34 2005/12/24 20:27:30 perry Exp $ */ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 1997, 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Charles M. Hannum and by Jason R. Thorpe of the Numerical Aerospace * Simulation Facility, NASA Ames Research Center. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Ralph Campbell and Rick Macklem. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)if_le.c 8.2 (Berkeley) 11/16/93 */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void lance_start(if_t); static void lance_stop(struct lance_softc *); static void lance_init(void *); static void lance_watchdog(void *s); static int lance_mediachange(if_t); static void lance_mediastatus(if_t, struct ifmediareq *); static int lance_ioctl(if_t, u_long, caddr_t); int lance_config(struct lance_softc *sc, const char* name, int unit) { if_t ifp; int i, nbuf; if (LE_LOCK_INITIALIZED(sc) == 0) return (ENXIO); ifp = sc->sc_ifp = if_alloc(IFT_ETHER); if (ifp == NULL) return (ENOSPC); callout_init_mtx(&sc->sc_wdog_ch, &sc->sc_mtx, 0); /* Initialize ifnet structure. */ if_setsoftc(ifp, sc); if_initname(ifp, name, unit); if_setstartfn(ifp, lance_start); if_setioctlfn(ifp, lance_ioctl); if_setinitfn(ifp, lance_init); if_setflags(ifp, IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST); #ifdef LANCE_REVC_BUG if_setflagsbit(ifp, 0, IFF_MULTICAST); #endif if_setbaudrate(ifp, IF_Mbps(10)); if_setsendqlen(ifp, ifqmaxlen); if_setsendqready(ifp); /* Initialize ifmedia structures. */ ifmedia_init(&sc->sc_media, 0, lance_mediachange, lance_mediastatus); if (sc->sc_supmedia != NULL) { for (i = 0; i < sc->sc_nsupmedia; i++) ifmedia_add(&sc->sc_media, sc->sc_supmedia[i], 0, NULL); ifmedia_set(&sc->sc_media, sc->sc_defaultmedia); } else { ifmedia_add(&sc->sc_media, IFM_MAKEWORD(IFM_ETHER, IFM_MANUAL, 0, 0), 0, NULL); ifmedia_set(&sc->sc_media, IFM_MAKEWORD(IFM_ETHER, IFM_MANUAL, 0, 0)); } switch (sc->sc_memsize) { case 8192: sc->sc_nrbuf = 4; sc->sc_ntbuf = 1; break; case 16384: sc->sc_nrbuf = 8; sc->sc_ntbuf = 2; break; case 32768: sc->sc_nrbuf = 16; sc->sc_ntbuf = 4; break; case 65536: sc->sc_nrbuf = 32; sc->sc_ntbuf = 8; break; case 131072: sc->sc_nrbuf = 64; sc->sc_ntbuf = 16; break; case 262144: sc->sc_nrbuf = 128; sc->sc_ntbuf = 32; break; default: /* weird memory size; cope with it */ nbuf = sc->sc_memsize / LEBLEN; sc->sc_ntbuf = nbuf / 5; sc->sc_nrbuf = nbuf - sc->sc_ntbuf; } if_printf(ifp, "%d receive buffers, %d transmit buffers\n", sc->sc_nrbuf, sc->sc_ntbuf); /* Make sure the chip is stopped. */ LE_LOCK(sc); lance_stop(sc); LE_UNLOCK(sc); return (0); } void lance_attach(struct lance_softc *sc) { if_t ifp = sc->sc_ifp; /* Attach the interface. */ ether_ifattach(ifp, sc->sc_enaddr); /* Claim 802.1q capability. */ if_setifheaderlen(ifp, sizeof(struct ether_vlan_header)); if_setcapabilitiesbit(ifp, IFCAP_VLAN_MTU, 0); if_setcapenablebit(ifp, IFCAP_VLAN_MTU, 0); + + gone_in(15, "le: 10/100 NIC no longer needed for Qemu/MIPS"); } void lance_detach(struct lance_softc *sc) { if_t ifp = sc->sc_ifp; LE_LOCK(sc); lance_stop(sc); LE_UNLOCK(sc); callout_drain(&sc->sc_wdog_ch); ether_ifdetach(ifp); if_free(ifp); } void lance_suspend(struct lance_softc *sc) { LE_LOCK(sc); lance_stop(sc); LE_UNLOCK(sc); } void lance_resume(struct lance_softc *sc) { LE_LOCK(sc); if (if_getflags(sc->sc_ifp) & IFF_UP) lance_init_locked(sc); LE_UNLOCK(sc); } static void lance_start(if_t ifp) { struct lance_softc *sc = if_getsoftc(ifp); LE_LOCK(sc); (*sc->sc_start_locked)(sc); LE_UNLOCK(sc); } static void lance_stop(struct lance_softc *sc) { if_t ifp = sc->sc_ifp; LE_LOCK_ASSERT(sc, MA_OWNED); /* * Mark the interface down and cancel the watchdog timer. */ if_setdrvflagbits(ifp, 0, (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)); callout_stop(&sc->sc_wdog_ch); sc->sc_wdog_timer = 0; (*sc->sc_wrcsr)(sc, LE_CSR0, LE_C0_STOP); } static void lance_init(void *xsc) { struct lance_softc *sc = (struct lance_softc *)xsc; LE_LOCK(sc); lance_init_locked(sc); LE_UNLOCK(sc); } /* * Initialization of interface; set up initialization block * and transmit/receive descriptor rings. */ void lance_init_locked(struct lance_softc *sc) { if_t ifp = sc->sc_ifp; u_long a; int timo; LE_LOCK_ASSERT(sc, MA_OWNED); (*sc->sc_wrcsr)(sc, LE_CSR0, LE_C0_STOP); DELAY(100); /* Newer LANCE chips have a reset register. */ if (sc->sc_hwreset) (*sc->sc_hwreset)(sc); /* Set the correct byte swapping mode, etc. */ (*sc->sc_wrcsr)(sc, LE_CSR3, sc->sc_conf3); /* Set the current media. This may require the chip to be stopped. */ if (sc->sc_mediachange) (void)(*sc->sc_mediachange)(sc); /* * Update our private copy of the Ethernet address. * We NEED the copy so we can ensure its alignment! */ memcpy(sc->sc_enaddr, if_getlladdr(ifp), ETHER_ADDR_LEN); /* Set up LANCE init block. */ (*sc->sc_meminit)(sc); /* Give LANCE the physical address of its init block. */ a = sc->sc_addr + LE_INITADDR(sc); (*sc->sc_wrcsr)(sc, LE_CSR1, a & 0xffff); (*sc->sc_wrcsr)(sc, LE_CSR2, a >> 16); /* Try to initialize the LANCE. */ DELAY(100); (*sc->sc_wrcsr)(sc, LE_CSR0, LE_C0_INIT); /* Wait for initialization to finish. */ for (timo = 100000; timo; timo--) if ((*sc->sc_rdcsr)(sc, LE_CSR0) & LE_C0_IDON) break; if ((*sc->sc_rdcsr)(sc, LE_CSR0) & LE_C0_IDON) { /* Start the LANCE. */ (*sc->sc_wrcsr)(sc, LE_CSR0, LE_C0_INEA | LE_C0_STRT); if_setdrvflagbits(ifp, IFF_DRV_RUNNING, 0); if_setdrvflagbits(ifp, 0, IFF_DRV_OACTIVE); sc->sc_wdog_timer = 0; callout_reset(&sc->sc_wdog_ch, hz, lance_watchdog, sc); (*sc->sc_start_locked)(sc); } else if_printf(ifp, "controller failed to initialize\n"); if (sc->sc_hwinit) (*sc->sc_hwinit)(sc); } /* * Routine to copy from mbuf chain to transmit buffer in * network buffer memory. */ int lance_put(struct lance_softc *sc, int boff, struct mbuf *m) { struct mbuf *n; int len, tlen = 0; LE_LOCK_ASSERT(sc, MA_OWNED); for (; m; m = n) { len = m->m_len; if (len == 0) { n = m_free(m); m = NULL; continue; } (*sc->sc_copytobuf)(sc, mtod(m, caddr_t), boff, len); boff += len; tlen += len; n = m_free(m); m = NULL; } if (tlen < LEMINSIZE) { (*sc->sc_zerobuf)(sc, boff, LEMINSIZE - tlen); tlen = LEMINSIZE; } return (tlen); } /* * Pull data off an interface. * Len is length of data, with local net header stripped. * We copy the data into mbufs. When full cluster sized units are present * we copy into clusters. */ struct mbuf * lance_get(struct lance_softc *sc, int boff, int totlen) { if_t ifp = sc->sc_ifp; struct mbuf *m, *m0, *newm; caddr_t newdata; int len; if (totlen <= ETHER_HDR_LEN || totlen > LEBLEN - ETHER_CRC_LEN) { #ifdef LEDEBUG if_printf(ifp, "invalid packet size %d; dropping\n", totlen); #endif return (NULL); } MGETHDR(m0, M_NOWAIT, MT_DATA); if (m0 == NULL) return (NULL); m0->m_pkthdr.rcvif = ifp; m0->m_pkthdr.len = totlen; len = MHLEN; m = m0; while (totlen > 0) { if (totlen >= MINCLSIZE) { if (!(MCLGET(m, M_NOWAIT))) goto bad; len = MCLBYTES; } if (m == m0) { newdata = (caddr_t) ALIGN(m->m_data + ETHER_HDR_LEN) - ETHER_HDR_LEN; len -= newdata - m->m_data; m->m_data = newdata; } m->m_len = len = min(totlen, len); (*sc->sc_copyfrombuf)(sc, mtod(m, caddr_t), boff, len); boff += len; totlen -= len; if (totlen > 0) { MGET(newm, M_NOWAIT, MT_DATA); if (newm == NULL) goto bad; len = MLEN; m = m->m_next = newm; } } return (m0); bad: m_freem(m0); return (NULL); } static void lance_watchdog(void *xsc) { struct lance_softc *sc = (struct lance_softc *)xsc; if_t ifp = sc->sc_ifp; LE_LOCK_ASSERT(sc, MA_OWNED); if (sc->sc_wdog_timer == 0 || --sc->sc_wdog_timer != 0) { callout_reset(&sc->sc_wdog_ch, hz, lance_watchdog, sc); return; } if_printf(ifp, "device timeout\n"); if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); lance_init_locked(sc); } static int lance_mediachange(if_t ifp) { struct lance_softc *sc = if_getsoftc(ifp); if (sc->sc_mediachange) { /* * For setting the port in LE_CSR15 the PCnet chips must * be powered down or stopped and unlike documented may * not take effect without an initialization. So don't * invoke (*sc_mediachange) directly here but go through * lance_init_locked(). */ LE_LOCK(sc); lance_stop(sc); lance_init_locked(sc); if (!if_sendq_empty(ifp)) (*sc->sc_start_locked)(sc); LE_UNLOCK(sc); } return (0); } static void lance_mediastatus(if_t ifp, struct ifmediareq *ifmr) { struct lance_softc *sc = if_getsoftc(ifp); LE_LOCK(sc); if (!(if_getflags(ifp) & IFF_UP)) { LE_UNLOCK(sc); return; } ifmr->ifm_status = IFM_AVALID; if (sc->sc_flags & LE_CARRIER) ifmr->ifm_status |= IFM_ACTIVE; if (sc->sc_mediastatus) (*sc->sc_mediastatus)(sc, ifmr); LE_UNLOCK(sc); } /* * Process an ioctl request. */ static int lance_ioctl(if_t ifp, u_long cmd, caddr_t data) { struct lance_softc *sc = if_getsoftc(ifp); struct ifreq *ifr = (struct ifreq *)data; int error = 0; switch (cmd) { case SIOCSIFFLAGS: LE_LOCK(sc); if (if_getflags(ifp) & IFF_PROMISC) { if (!(sc->sc_flags & LE_PROMISC)) { sc->sc_flags |= LE_PROMISC; lance_init_locked(sc); } } else if (sc->sc_flags & LE_PROMISC) { sc->sc_flags &= ~LE_PROMISC; lance_init_locked(sc); } if ((if_getflags(ifp) & IFF_ALLMULTI) && !(sc->sc_flags & LE_ALLMULTI)) { sc->sc_flags |= LE_ALLMULTI; lance_init_locked(sc); } else if (!(if_getflags(ifp) & IFF_ALLMULTI) && (sc->sc_flags & LE_ALLMULTI)) { sc->sc_flags &= ~LE_ALLMULTI; lance_init_locked(sc); } if (!(if_getflags(ifp) & IFF_UP) && if_getdrvflags(ifp) & IFF_DRV_RUNNING) { /* * If interface is marked down and it is running, then * stop it. */ lance_stop(sc); } else if (if_getflags(ifp) & IFF_UP && !(if_getdrvflags(ifp) & IFF_DRV_RUNNING)) { /* * If interface is marked up and it is stopped, then * start it. */ lance_init_locked(sc); } #ifdef LEDEBUG if (if_getflags(ifp) & IFF_DEBUG) sc->sc_flags |= LE_DEBUG; else sc->sc_flags &= ~LE_DEBUG; #endif LE_UNLOCK(sc); break; case SIOCADDMULTI: case SIOCDELMULTI: /* * Multicast list has changed; set the hardware filter * accordingly. */ LE_LOCK(sc); if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) lance_init_locked(sc); LE_UNLOCK(sc); break; case SIOCGIFMEDIA: case SIOCSIFMEDIA: error = ifmedia_ioctl(ifp, ifr, &sc->sc_media, cmd); break; default: error = ether_ioctl(ifp, cmd, data); break; } return (error); } struct lance_hash_maddr_ctx { struct lance_softc *sc; uint16_t *af; }; static u_int lance_hash_maddr(void *arg, struct sockaddr_dl *sdl, u_int cnt) { struct lance_hash_maddr_ctx *ctx = arg; struct lance_softc *sc = ctx->sc; uint32_t crc; crc = ether_crc32_le(LLADDR(sdl), ETHER_ADDR_LEN); /* Just want the 6 most significant bits. */ crc >>= 26; /* Set the corresponding bit in the filter. */ ctx->af[crc >> 4] |= LE_HTOLE16(1 << (crc & 0xf)); return (1); } /* * Set up the logical address filter. */ void lance_setladrf(struct lance_softc *sc, uint16_t *af) { if_t ifp = sc->sc_ifp; struct lance_hash_maddr_ctx ctx = { sc, af }; /* * Set up multicast address filter by passing all multicast addresses * through a crc generator, and then using the high order 6 bits as an * index into the 64 bit logical address filter. The high order bit * selects the word, while the rest of the bits select the bit within * the word. */ if (if_getflags(ifp) & IFF_PROMISC || sc->sc_flags & LE_ALLMULTI) { af[0] = af[1] = af[2] = af[3] = 0xffff; return; } af[0] = af[1] = af[2] = af[3] = 0x0000; if_foreach_llmaddr(ifp, lance_hash_maddr, &ctx); } /* * Routines for accessing the transmit and receive buffers. * The various CPU and adapter configurations supported by this * driver require three different access methods for buffers * and descriptors: * (1) contig (contiguous data; no padding), * (2) gap2 (two bytes of data followed by two bytes of padding), * (3) gap16 (16 bytes of data followed by 16 bytes of padding). */ /* * contig: contiguous data with no padding. * * Buffers may have any alignment. */ void lance_copytobuf_contig(struct lance_softc *sc, void *from, int boff, int len) { volatile caddr_t buf = sc->sc_mem; /* * Just call memcpy() to do the work. */ memcpy(buf + boff, from, len); } void lance_copyfrombuf_contig(struct lance_softc *sc, void *to, int boff, int len) { volatile caddr_t buf = sc->sc_mem; /* * Just call memcpy() to do the work. */ memcpy(to, buf + boff, len); } void lance_zerobuf_contig(struct lance_softc *sc, int boff, int len) { volatile caddr_t buf = sc->sc_mem; /* * Just let memset() do the work */ memset(buf + boff, 0, len); } #if 0 /* * Examples only; duplicate these and tweak (if necessary) in * machine-specific front-ends. */ /* * gap2: two bytes of data followed by two bytes of pad. * * Buffers must be 4-byte aligned. The code doesn't worry about * doing an extra byte. */ static void lance_copytobuf_gap2(struct lance_softc *sc, void *fromv, int boff, int len) { volatile caddr_t buf = sc->sc_mem; caddr_t from = fromv; volatile uint16_t *bptr; if (boff & 0x1) { /* Handle unaligned first byte. */ bptr = ((volatile uint16_t *)buf) + (boff - 1); *bptr = (*from++ << 8) | (*bptr & 0xff); bptr += 2; len--; } else bptr = ((volatile uint16_t *)buf) + boff; while (len > 1) { *bptr = (from[1] << 8) | (from[0] & 0xff); bptr += 2; from += 2; len -= 2; } if (len == 1) *bptr = (uint16_t)*from; } static void lance_copyfrombuf_gap2(struct lance_softc *sc, void *tov, int boff, int len) { volatile caddr_t buf = sc->sc_mem; caddr_t to = tov; volatile uint16_t *bptr; uint16_t tmp; if (boff & 0x1) { /* Handle unaligned first byte. */ bptr = ((volatile uint16_t *)buf) + (boff - 1); *to++ = (*bptr >> 8) & 0xff; bptr += 2; len--; } else bptr = ((volatile uint16_t *)buf) + boff; while (len > 1) { tmp = *bptr; *to++ = tmp & 0xff; *to++ = (tmp >> 8) & 0xff; bptr += 2; len -= 2; } if (len == 1) *to = *bptr & 0xff; } static void lance_zerobuf_gap2(struct lance_softc *sc, int boff, int len) { volatile caddr_t buf = sc->sc_mem; volatile uint16_t *bptr; if ((unsigned)boff & 0x1) { bptr = ((volatile uint16_t *)buf) + (boff - 1); *bptr &= 0xff; bptr += 2; len--; } else bptr = ((volatile uint16_t *)buf) + boff; while (len > 0) { *bptr = 0; bptr += 2; len -= 2; } } /* * gap16: 16 bytes of data followed by 16 bytes of pad. * * Buffers must be 32-byte aligned. */ static void lance_copytobuf_gap16(struct lance_softc *sc, void *fromv, int boff, int len) { volatile caddr_t buf = sc->sc_mem; caddr_t bptr, from = fromv; int xfer; bptr = buf + ((boff << 1) & ~0x1f); boff &= 0xf; xfer = min(len, 16 - boff); while (len > 0) { memcpy(bptr + boff, from, xfer); from += xfer; bptr += 32; boff = 0; len -= xfer; xfer = min(len, 16); } } static void lance_copyfrombuf_gap16(struct lance_softc *sc, void *tov, int boff, int len) { volatile caddr_t buf = sc->sc_mem; caddr_t bptr, to = tov; int xfer; bptr = buf + ((boff << 1) & ~0x1f); boff &= 0xf; xfer = min(len, 16 - boff); while (len > 0) { memcpy(to, bptr + boff, xfer); to += xfer; bptr += 32; boff = 0; len -= xfer; xfer = min(len, 16); } } static void lance_zerobuf_gap16(struct lance_softc *sc, int boff, int len) { volatile caddr_t buf = sc->sc_mem; caddr_t bptr; int xfer; bptr = buf + ((boff << 1) & ~0x1f); boff &= 0xf; xfer = min(len, 16 - boff); while (len > 0) { memset(bptr + boff, 0, xfer); bptr += 32; boff = 0; len -= xfer; xfer = min(len, 16); } } #endif /* Example only */