11590Srgrimes/*	$NetBSD: lance.c,v 1.34 2005/12/24 20:27:30 perry Exp $	*/
21590Srgrimes
31590Srgrimes/*-
41590Srgrimes * Copyright (c) 1997, 1998 The NetBSD Foundation, Inc.
51590Srgrimes * All rights reserved.
61590Srgrimes *
71590Srgrimes * This code is derived from software contributed to The NetBSD Foundation
81590Srgrimes * by Charles M. Hannum and by Jason R. Thorpe of the Numerical Aerospace
91590Srgrimes * Simulation Facility, NASA Ames Research Center.
101590Srgrimes *
111590Srgrimes * Redistribution and use in source and binary forms, with or without
121590Srgrimes * modification, are permitted provided that the following conditions
131590Srgrimes * are met:
141590Srgrimes * 1. Redistributions of source code must retain the above copyright
151590Srgrimes *    notice, this list of conditions and the following disclaimer.
161590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
171590Srgrimes *    notice, this list of conditions and the following disclaimer in the
181590Srgrimes *    documentation and/or other materials provided with the distribution.
191590Srgrimes * 3. All advertising materials mentioning features or use of this software
201590Srgrimes *    must display the following acknowledgement:
211590Srgrimes *	This product includes software developed by the NetBSD
221590Srgrimes *	Foundation, Inc. and its contributors.
231590Srgrimes * 4. Neither the name of The NetBSD Foundation nor the names of its
241590Srgrimes *    contributors may be used to endorse or promote products derived
251590Srgrimes *    from this software without specific prior written permission.
261590Srgrimes *
271590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
281590Srgrimes * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
291590Srgrimes * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
3087715Smarkm * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
3187715Smarkm * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
3287715Smarkm * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
3387715Smarkm * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
3487715Smarkm * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
3587715Smarkm * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
3628149Scharnier * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
371590Srgrimes * POSSIBILITY OF SUCH DAMAGE.
381590Srgrimes */
391590Srgrimes
401590Srgrimes/*-
411590Srgrimes * Copyright (c) 1992, 1993
421590Srgrimes *	The Regents of the University of California.  All rights reserved.
431590Srgrimes *
441590Srgrimes * This code is derived from software contributed to Berkeley by
451590Srgrimes * Ralph Campbell and Rick Macklem.
4612811Sbde *
471590Srgrimes * Redistribution and use in source and binary forms, with or without
48111007Sphk * modification, are permitted provided that the following conditions
491590Srgrimes * are met:
5012805Speter * 1. Redistributions of source code must retain the above copyright
511590Srgrimes *    notice, this list of conditions and the following disclaimer.
5212811Sbde * 2. Redistributions in binary form must reproduce the above copyright
5312811Sbde *    notice, this list of conditions and the following disclaimer in the
5428149Scharnier *    documentation and/or other materials provided with the distribution.
5528149Scharnier * 3. Neither the name of the University nor the names of its contributors
5659217Simp *    may be used to endorse or promote products derived from this software
5774595Sache *    without specific prior written permission.
581590Srgrimes *
59200462Sdelphij * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
6028149Scharnier * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
6128149Scharnier * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
621590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
6314953Sache * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
641590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
65202200Sed * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
6639230Sgibbs * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
671590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
681590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
6940060Sobrien * SUCH DAMAGE.
701590Srgrimes *
711590Srgrimes *	@(#)if_le.c	8.2 (Berkeley) 11/16/93
721590Srgrimes */
7374671Stmm
7474671Stmm#include <sys/cdefs.h>
7574671Stmm__FBSDID("$FreeBSD$");
7674671Stmm
7774671Stmm#include <sys/param.h>
7874671Stmm#include <sys/bus.h>
7974671Stmm#include <sys/endian.h>
8074671Stmm#include <sys/lock.h>
8174671Stmm#include <sys/kernel.h>
82246033Szont#include <sys/mbuf.h>
8374671Stmm#include <sys/mutex.h>
8474671Stmm#include <sys/socket.h>
8574671Stmm#include <sys/sockio.h>
8674671Stmm
8774671Stmm#include <net/ethernet.h>
8874671Stmm#include <net/if.h>
8974671Stmm#include <net/if_arp.h>
9074671Stmm#include <net/if_dl.h>
9174671Stmm#include <net/if_media.h>
9274671Stmm#include <net/if_types.h>
9374671Stmm#include <net/if_vlan_var.h>
9474671Stmm
9574671Stmm#include <machine/bus.h>
9674671Stmm
9774671Stmm#include <dev/le/lancereg.h>
9874671Stmm#include <dev/le/lancevar.h>
9974671Stmm
10074671Stmmdevclass_t le_devclass;
10174671Stmm
10274671Stmmstatic void lance_start(struct ifnet *);
10374671Stmmstatic void lance_stop(struct lance_softc *);
10474671Stmmstatic void lance_init(void *);
10574671Stmmstatic void lance_watchdog(void *s);
10674671Stmmstatic int lance_mediachange(struct ifnet *);
10774671Stmmstatic void lance_mediastatus(struct ifnet *, struct ifmediareq *);
10874671Stmmstatic int lance_ioctl(struct ifnet *, u_long, caddr_t);
10974671Stmm
11074671Stmmint
111292817Saraujolance_config(struct lance_softc *sc, const char* name, int unit)
1121590Srgrimes{
1131590Srgrimes	struct ifnet *ifp;
1141590Srgrimes	int i, nbuf;
1151590Srgrimes
116189626Sjhb	if (LE_LOCK_INITIALIZED(sc) == 0)
11729842Speter		return (ENXIO);
11869529Sgallatin
11969529Sgallatin	ifp = sc->sc_ifp = if_alloc(IFT_ETHER);
12074978Sgallatin	if (ifp == NULL)
1211590Srgrimes		return (ENOSPC);
122292817Saraujo
123292817Saraujo	callout_init_mtx(&sc->sc_wdog_ch, &sc->sc_mtx, 0);
1241590Srgrimes
12539230Sgibbs	/* Initialize ifnet structure. */
12639230Sgibbs	ifp->if_softc = sc;
1271590Srgrimes	if_initname(ifp, name, unit);
1281590Srgrimes	ifp->if_start = lance_start;
1291590Srgrimes	ifp->if_ioctl = lance_ioctl;
1301590Srgrimes	ifp->if_init = lance_init;
1311590Srgrimes	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
1321590Srgrimes#ifdef LANCE_REVC_BUG
13392922Simp	ifp->if_flags &= ~IFF_MULTICAST;
13492922Simp#endif
13592922Simp	ifp->if_baudrate = IF_Mbps(10);
13692922Simp	IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen);
13792922Simp	ifp->if_snd.ifq_drv_maxlen = ifqmaxlen;
13892922Simp	IFQ_SET_READY(&ifp->if_snd);
13992922Simp
14092922Simp	/* Initialize ifmedia structures. */
14192922Simp	ifmedia_init(&sc->sc_media, 0, lance_mediachange, lance_mediastatus);
1421590Srgrimes	if (sc->sc_supmedia != NULL) {
14336430Sjhay		for (i = 0; i < sc->sc_nsupmedia; i++)
1441590Srgrimes			ifmedia_add(&sc->sc_media, sc->sc_supmedia[i], 0, NULL);
1451590Srgrimes		ifmedia_set(&sc->sc_media, sc->sc_defaultmedia);
1461590Srgrimes	} else {
1471590Srgrimes		ifmedia_add(&sc->sc_media,
1481590Srgrimes		    IFM_MAKEWORD(IFM_ETHER, IFM_MANUAL, 0, 0), 0, NULL);
1491590Srgrimes		ifmedia_set(&sc->sc_media,
1501590Srgrimes		    IFM_MAKEWORD(IFM_ETHER, IFM_MANUAL, 0, 0));
1511590Srgrimes	}
1521590Srgrimes
153175387Sdelphij	switch (sc->sc_memsize) {
1541590Srgrimes	case 8192:
1551590Srgrimes		sc->sc_nrbuf = 4;
1561590Srgrimes		sc->sc_ntbuf = 1;
1571590Srgrimes		break;
1581590Srgrimes	case 16384:
1591590Srgrimes		sc->sc_nrbuf = 8;
160175387Sdelphij		sc->sc_ntbuf = 2;
1611590Srgrimes		break;
1621590Srgrimes	case 32768:
1631590Srgrimes		sc->sc_nrbuf = 16;
1641590Srgrimes		sc->sc_ntbuf = 4;
1651590Srgrimes		break;
1661590Srgrimes	case 65536:
1671590Srgrimes		sc->sc_nrbuf = 32;
1681590Srgrimes		sc->sc_ntbuf = 8;
1691590Srgrimes		break;
1701590Srgrimes	case 131072:
1711590Srgrimes		sc->sc_nrbuf = 64;
172155663Sbde		sc->sc_ntbuf = 16;
173155677Sbde		break;
174155663Sbde	case 262144:
1751590Srgrimes		sc->sc_nrbuf = 128;
176158198Sbde		sc->sc_ntbuf = 32;
177155677Sbde		break;
178155666Sbde	default:
179155666Sbde		/* weird memory size; cope with it */
180155677Sbde		nbuf = sc->sc_memsize / LEBLEN;
1811590Srgrimes		sc->sc_ntbuf = nbuf / 5;
182155666Sbde		sc->sc_nrbuf = nbuf - sc->sc_ntbuf;
183155666Sbde	}
184158198Sbde
185158198Sbde	if_printf(ifp, "%d receive buffers, %d transmit buffers\n",
186158198Sbde	    sc->sc_nrbuf, sc->sc_ntbuf);
1871590Srgrimes
188158187Sbde	/* Make sure the chip is stopped. */
189158187Sbde	LE_LOCK(sc);
190158187Sbde	lance_stop(sc);
1911590Srgrimes	LE_UNLOCK(sc);
192155663Sbde
1931590Srgrimes	return (0);
1941590Srgrimes}
19539230Sgibbs
1961590Srgrimesvoid
1971590Srgrimeslance_attach(struct lance_softc *sc)
1981590Srgrimes{
1991590Srgrimes	struct ifnet *ifp = sc->sc_ifp;
200175387Sdelphij
2011590Srgrimes	/* Attach the interface. */
202158154Sbde	ether_ifattach(ifp, sc->sc_enaddr);
2031590Srgrimes
20477583Stmm	/* Claim 802.1q capability. */
2051590Srgrimes	ifp->if_data.ifi_hdrlen = sizeof(struct ether_vlan_header);
20683131Sken	ifp->if_capabilities |= IFCAP_VLAN_MTU;
20739230Sgibbs	ifp->if_capenable |= IFCAP_VLAN_MTU;
2081590Srgrimes}
2091590Srgrimes
21039230Sgibbsvoid
211287633Sdelphijlance_detach(struct lance_softc *sc)
212287633Sdelphij{
213287633Sdelphij	struct ifnet *ifp = sc->sc_ifp;
21439230Sgibbs
21539230Sgibbs	LE_LOCK(sc);
21639230Sgibbs	lance_stop(sc);
21739230Sgibbs	LE_UNLOCK(sc);
2181590Srgrimes	callout_drain(&sc->sc_wdog_ch);
21977583Stmm	ether_ifdetach(ifp);
22077583Stmm	if_free(ifp);
22177583Stmm}
22277583Stmm
22377583Stmmvoid
22477583Stmmlance_suspend(struct lance_softc *sc)
2251590Srgrimes{
22677583Stmm
22774671Stmm	LE_LOCK(sc);
228226396Sed	lance_stop(sc);
22974671Stmm	LE_UNLOCK(sc);
23074671Stmm}
2311590Srgrimes
2321590Srgrimesvoid
2331590Srgrimeslance_resume(struct lance_softc *sc)
2341590Srgrimes{
2351590Srgrimes
2361590Srgrimes	LE_LOCK(sc);
2371590Srgrimes	if (sc->sc_ifp->if_flags & IFF_UP)
2381590Srgrimes		lance_init_locked(sc);
2391590Srgrimes	LE_UNLOCK(sc);
2401590Srgrimes}
241158154Sbde
242158154Sbdestatic void
243158154Sbdelance_start(struct ifnet *ifp)
244158154Sbde{
245158154Sbde	struct lance_softc *sc = ifp->if_softc;
246158154Sbde
247158154Sbde	LE_LOCK(sc);
248158154Sbde	(*sc->sc_start_locked)(sc);
249158154Sbde	LE_UNLOCK(sc);
250158154Sbde}
251158154Sbde
252158154Sbdestatic void
253158154Sbdelance_stop(struct lance_softc *sc)
254209312Smav{
255158154Sbde	struct ifnet *ifp = sc->sc_ifp;
256158154Sbde
257158154Sbde	LE_LOCK_ASSERT(sc, MA_OWNED);
258209312Smav
259210228Smav	/*
260209312Smav	 * Mark the interface down and cancel the watchdog timer.
261209312Smav	 */
262209312Smav	ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
263158154Sbde	callout_stop(&sc->sc_wdog_ch);
264158154Sbde	sc->sc_wdog_timer = 0;
265158154Sbde
266210228Smav	(*sc->sc_wrcsr)(sc, LE_CSR0, LE_C0_STOP);
267210228Smav}
268210228Smav
269210228Smavstatic void
270210228Smavlance_init(void *xsc)
271210228Smav{
272210228Smav	struct lance_softc *sc = (struct lance_softc *)xsc;
273210228Smav
274210228Smav	LE_LOCK(sc);
2751590Srgrimes	lance_init_locked(sc);
276158154Sbde	LE_UNLOCK(sc);
2771590Srgrimes}
2781590Srgrimes
2791590Srgrimes/*
2801590Srgrimes * Initialization of interface; set up initialization block
2811590Srgrimes * and transmit/receive descriptor rings.
2821590Srgrimes */
2831590Srgrimesvoid
284292817Saraujolance_init_locked(struct lance_softc *sc)
285292817Saraujo{
28687715Smarkm	struct ifnet *ifp = sc->sc_ifp;
2871590Srgrimes	u_long a;
2881590Srgrimes	int timo;
2891590Srgrimes
2901590Srgrimes	LE_LOCK_ASSERT(sc, MA_OWNED);
2911590Srgrimes
292175387Sdelphij	(*sc->sc_wrcsr)(sc, LE_CSR0, LE_C0_STOP);
2931590Srgrimes	DELAY(100);
2941590Srgrimes
29514953Sache	/* Newer LANCE chips have a reset register. */
29674595Sache	if (sc->sc_hwreset)
2971590Srgrimes		(*sc->sc_hwreset)(sc);
29874595Sache
29974595Sache	/* Set the correct byte swapping mode, etc. */
30074595Sache	(*sc->sc_wrcsr)(sc, LE_CSR3, sc->sc_conf3);
3011590Srgrimes
30214953Sache	/* Set the current media. This may require the chip to be stopped. */
30374595Sache	if (sc->sc_mediachange)
30474595Sache		(void)(*sc->sc_mediachange)(sc);
30587715Smarkm
3061590Srgrimes	/*
3071590Srgrimes	 * Update our private copy of the Ethernet address.
3081590Srgrimes	 * We NEED the copy so we can ensure its alignment!
309175387Sdelphij	 */
3101590Srgrimes	memcpy(sc->sc_enaddr, IF_LLADDR(ifp), ETHER_ADDR_LEN);
31187715Smarkm
3121590Srgrimes	/* Set up LANCE init block. */
3131590Srgrimes	(*sc->sc_meminit)(sc);
314155677Sbde
315292817Saraujo	/* Give LANCE the physical address of its init block. */
316292817Saraujo	a = sc->sc_addr + LE_INITADDR(sc);
31719687Sjkh	(*sc->sc_wrcsr)(sc, LE_CSR1, a & 0xffff);
3181590Srgrimes	(*sc->sc_wrcsr)(sc, LE_CSR2, a >> 16);
3191590Srgrimes
3201590Srgrimes	/* Try to initialize the LANCE. */
32119687Sjkh	DELAY(100);
3221590Srgrimes	(*sc->sc_wrcsr)(sc, LE_CSR0, LE_C0_INIT);
323155677Sbde
324155677Sbde	/* Wait for initialization to finish. */
3251590Srgrimes	for (timo = 100000; timo; timo--)
3261590Srgrimes		if ((*sc->sc_rdcsr)(sc, LE_CSR0) & LE_C0_IDON)
3271590Srgrimes			break;
328155666Sbde
329155666Sbde	if ((*sc->sc_rdcsr)(sc, LE_CSR0) & LE_C0_IDON) {
3301590Srgrimes		/* Start the LANCE. */
331246033Szont		(*sc->sc_wrcsr)(sc, LE_CSR0, LE_C0_INEA | LE_C0_STRT);
332246033Szont		ifp->if_drv_flags |= IFF_DRV_RUNNING;
333246033Szont		ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
334246033Szont		sc->sc_wdog_timer = 0;
335246033Szont		callout_reset(&sc->sc_wdog_ch, hz, lance_watchdog, sc);
336246033Szont		(*sc->sc_start_locked)(sc);
337246033Szont	} else
338246033Szont		if_printf(ifp, "controller failed to initialize\n");
339246033Szont
340246033Szont	if (sc->sc_hwinit)
341246033Szont		(*sc->sc_hwinit)(sc);
342246033Szont}
343246033Szont
344246033Szont/*
345246033Szont * Routine to copy from mbuf chain to transmit buffer in
346246033Szont * network buffer memory.
347246033Szont */
348246033Szontint
349246033Szontlance_put(struct lance_softc *sc, int boff, struct mbuf *m)
3501590Srgrimes{
351155666Sbde	struct mbuf *n;
3521590Srgrimes	int len, tlen = 0;
3531590Srgrimes
3544930Sbde	LE_LOCK_ASSERT(sc, MA_OWNED);
355155677Sbde
356155677Sbde	for (; m; m = n) {
3571590Srgrimes		len = m->m_len;
3581590Srgrimes		if (len == 0) {
3591590Srgrimes			n = m_free(m);
360158187Sbde			m = NULL;
361158187Sbde			continue;
362158187Sbde		}
363158187Sbde		(*sc->sc_copytobuf)(sc, mtod(m, caddr_t), boff, len);
364158187Sbde		boff += len;
365158187Sbde		tlen += len;
3661590Srgrimes		n = m_free(m);
367158187Sbde		m = NULL;
36851421Sgreen	}
36939230Sgibbs	if (tlen < LEMINSIZE) {
37039230Sgibbs		(*sc->sc_zerobuf)(sc, boff, LEMINSIZE - tlen);
37139230Sgibbs		tlen = LEMINSIZE;
372155666Sbde	}
37339230Sgibbs	return (tlen);
37439230Sgibbs}
37539230Sgibbs
37639230Sgibbs/*
37739230Sgibbs * Pull data off an interface.
37839230Sgibbs * Len is length of data, with local net header stripped.
3791590Srgrimes * We copy the data into mbufs.  When full cluster sized units are present
38039230Sgibbs * we copy into clusters.
38139230Sgibbs */
38239230Sgibbsstruct mbuf *
38339230Sgibbslance_get(struct lance_softc *sc, int boff, int totlen)
38439230Sgibbs{
38539230Sgibbs	struct ifnet *ifp = sc->sc_ifp;
38639230Sgibbs	struct mbuf *m, *m0, *newm;
3871590Srgrimes	caddr_t newdata;
3881590Srgrimes	int len;
38943756Sdillon
3901590Srgrimes	if (totlen <= ETHER_HDR_LEN || totlen > LEBLEN - ETHER_CRC_LEN) {
3911590Srgrimes#ifdef LEDEBUG
3921590Srgrimes		if_printf(ifp, "invalid packet size %d; dropping\n", totlen);
393155666Sbde#endif
3941590Srgrimes		return (NULL);
3951590Srgrimes	}
3961590Srgrimes
3971590Srgrimes	MGETHDR(m0, M_DONTWAIT, MT_DATA);
39839230Sgibbs	if (m0 == NULL)
3991590Srgrimes		return (NULL);
4001590Srgrimes	m0->m_pkthdr.rcvif = ifp;
4011590Srgrimes	m0->m_pkthdr.len = totlen;
4021590Srgrimes	len = MHLEN;
403155665Sbde	m = m0;
4041590Srgrimes
405155665Sbde	while (totlen > 0) {
406155665Sbde		if (totlen >= MINCLSIZE) {
4071590Srgrimes			MCLGET(m, M_DONTWAIT);
4081590Srgrimes			if ((m->m_flags & M_EXT) == 0)
4094930Sbde				goto bad;
4104930Sbde			len = MCLBYTES;
4114930Sbde		}
4121590Srgrimes
4131590Srgrimes		if (m == m0) {
414175387Sdelphij			newdata = (caddr_t)
4151590Srgrimes			    ALIGN(m->m_data + ETHER_HDR_LEN) - ETHER_HDR_LEN;
4161590Srgrimes			len -= newdata - m->m_data;
4171590Srgrimes			m->m_data = newdata;
418158152Sbde		}
4191590Srgrimes
4201590Srgrimes		m->m_len = len = min(totlen, len);
4211590Srgrimes		(*sc->sc_copyfrombuf)(sc, mtod(m, caddr_t), boff, len);
4221590Srgrimes		boff += len;
4231590Srgrimes
42439230Sgibbs		totlen -= len;
4251590Srgrimes		if (totlen > 0) {
4261590Srgrimes			MGET(newm, M_DONTWAIT, MT_DATA);
4271590Srgrimes			if (newm == 0)
4281590Srgrimes				goto bad;
4291590Srgrimes			len = MLEN;
4301590Srgrimes			m = m->m_next = newm;
4311590Srgrimes		}
4321590Srgrimes	}
4331590Srgrimes
4341590Srgrimes	return (m0);
4351590Srgrimes
4361590Srgrimes bad:
4371590Srgrimes	m_freem(m0);
4381590Srgrimes	return (NULL);
4391590Srgrimes}
4401590Srgrimes
4411590Srgrimesstatic void
44236430Sjhaylance_watchdog(void *xsc)
4431590Srgrimes{
4441590Srgrimes	struct lance_softc *sc = (struct lance_softc *)xsc;
4451590Srgrimes	struct ifnet *ifp = sc->sc_ifp;
4461590Srgrimes
447212313Smav	LE_LOCK_ASSERT(sc, MA_OWNED);
448212313Smav
449212313Smav	if (sc->sc_wdog_timer == 0 || --sc->sc_wdog_timer != 0) {
4501590Srgrimes		callout_reset(&sc->sc_wdog_ch, hz, lance_watchdog, sc);
4511590Srgrimes		return;
4521590Srgrimes	}
4531590Srgrimes
454155666Sbde	if_printf(ifp, "device timeout\n");
455158152Sbde	++ifp->if_oerrors;
4561590Srgrimes	lance_init_locked(sc);
457155666Sbde}
4581590Srgrimes
459155666Sbdestatic int
4601590Srgrimeslance_mediachange(struct ifnet *ifp)
4617138Sphk{
4621590Srgrimes	struct lance_softc *sc = ifp->if_softc;
4637012Sphk
4641590Srgrimes	if (sc->sc_mediachange) {
4651590Srgrimes		/*
4661590Srgrimes		 * For setting the port in LE_CSR15 the PCnet chips must
4671590Srgrimes		 * be powered down or stopped and unlike documented may
4681590Srgrimes		 * not take effect without an initialization. So don't
46987715Smarkm		 * invoke (*sc_mediachange) directly here but go through
47087715Smarkm		 * lance_init_locked().
4711590Srgrimes		 */
4721590Srgrimes		LE_LOCK(sc);
4731590Srgrimes		lance_stop(sc);
47487715Smarkm		lance_init_locked(sc);
4751590Srgrimes		if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
4761590Srgrimes			(*sc->sc_start_locked)(sc);
4771590Srgrimes		LE_UNLOCK(sc);
47887715Smarkm	}
4791590Srgrimes	return (0);
4801590Srgrimes}
481155677Sbde
482155677Sbdestatic void
483155677Sbdelance_mediastatus(struct ifnet *ifp, struct ifmediareq *ifmr)
484155677Sbde{
485155677Sbde	struct lance_softc *sc = ifp->if_softc;
486142175Sps
487292817Saraujo	LE_LOCK(sc);
488292817Saraujo	if (!(ifp->if_flags & IFF_UP)) {
489292817Saraujo		LE_UNLOCK(sc);
490292817Saraujo		return;
491292817Saraujo	}
492164718Sru
493164718Sru	ifmr->ifm_status = IFM_AVALID;
494164718Sru	if (sc->sc_flags & LE_CARRIER)
495164718Sru		ifmr->ifm_status |= IFM_ACTIVE;
496164718Sru
497164718Sru	if (sc->sc_mediastatus)
498164718Sru		(*sc->sc_mediastatus)(sc, ifmr);
499164718Sru	LE_UNLOCK(sc);
500164718Sru}
501164718Sru
502164718Sru/*
503164718Sru * Process an ioctl request.
504164718Sru */
505164718Srustatic int
506246033Szontlance_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
507246033Szont{
508246033Szont	struct lance_softc *sc = ifp->if_softc;
509246033Szont	struct ifreq *ifr = (struct ifreq *)data;
510158196Sbde	int error = 0;
511246033Szont
512246033Szont	switch (cmd) {
513246033Szont	case SIOCSIFFLAGS:
514246033Szont		LE_LOCK(sc);
515246033Szont		if (ifp->if_flags & IFF_PROMISC) {
516246033Szont			if (!(sc->sc_flags & LE_PROMISC)) {
517246033Szont				sc->sc_flags |= LE_PROMISC;
518246033Szont				lance_init_locked(sc);
519246033Szont			}
520246033Szont		} else if (sc->sc_flags & LE_PROMISC) {
521246033Szont			sc->sc_flags &= ~LE_PROMISC;
522246033Szont			lance_init_locked(sc);
523246033Szont		}
524246033Szont
525246033Szont		if ((ifp->if_flags & IFF_ALLMULTI) &&
526155677Sbde		    !(sc->sc_flags & LE_ALLMULTI)) {
527155677Sbde			sc->sc_flags |= LE_ALLMULTI;
528155677Sbde			lance_init_locked(sc);
529155677Sbde		} else if (!(ifp->if_flags & IFF_ALLMULTI) &&
530155677Sbde		    (sc->sc_flags & LE_ALLMULTI)) {
531155677Sbde			sc->sc_flags &= ~LE_ALLMULTI;
532155677Sbde			lance_init_locked(sc);
533155677Sbde		}
534155666Sbde
535155666Sbde		if (!(ifp->if_flags & IFF_UP) &&
536155666Sbde		    ifp->if_drv_flags & IFF_DRV_RUNNING) {
537155666Sbde			/*
538155666Sbde			 * If interface is marked down and it is running, then
539155666Sbde			 * stop it.
54087715Smarkm			 */
54139230Sgibbs			lance_stop(sc);
54239230Sgibbs		} else if (ifp->if_flags & IFF_UP &&
54339230Sgibbs		    !(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
54487715Smarkm			/*
54539230Sgibbs			 * If interface is marked up and it is stopped, then
54639230Sgibbs			 * start it.
54787715Smarkm			 */
54839230Sgibbs			lance_init_locked(sc);
54939230Sgibbs		}
55087715Smarkm#ifdef LEDEBUG
55139230Sgibbs		if (ifp->if_flags & IFF_DEBUG)
55239230Sgibbs			sc->sc_flags |= LE_DEBUG;
5531590Srgrimes		else
554158187Sbde			sc->sc_flags &= ~LE_DEBUG;
555158187Sbde#endif
556158187Sbde		LE_UNLOCK(sc);
557158187Sbde		break;
558158187Sbde
5597012Sphk	case SIOCADDMULTI:
560158187Sbde	case SIOCDELMULTI:
5611590Srgrimes		/*
5628874Srgrimes		 * Multicast list has changed; set the hardware filter
5637012Sphk		 * accordingly.
564158187Sbde		 */
565158187Sbde		LE_LOCK(sc);
5661590Srgrimes		if (ifp->if_drv_flags & IFF_DRV_RUNNING)
567158187Sbde			lance_init_locked(sc);
5681590Srgrimes		LE_UNLOCK(sc);
5691590Srgrimes		break;
5701590Srgrimes
5711590Srgrimes	case SIOCGIFMEDIA:
572175387Sdelphij	case SIOCSIFMEDIA:
5731590Srgrimes		error = ifmedia_ioctl(ifp, ifr, &sc->sc_media, cmd);
57439230Sgibbs		break;
5751590Srgrimes
5761590Srgrimes	default:
57739230Sgibbs		error = ether_ioctl(ifp, cmd, data);
5781590Srgrimes		break;
57983131Sken	}
58039230Sgibbs
58139230Sgibbs	return (error);
58239230Sgibbs}
58339230Sgibbs
58439230Sgibbs/*
58539230Sgibbs * Set up the logical address filter.
58639230Sgibbs */
58739230Sgibbsvoid
58839230Sgibbslance_setladrf(struct lance_softc *sc, uint16_t *af)
58939230Sgibbs{
59039230Sgibbs	struct ifnet *ifp = sc->sc_ifp;
59139230Sgibbs	struct ifmultiaddr *ifma;
59239230Sgibbs	uint32_t crc;
5931590Srgrimes
59439230Sgibbs	/*
5951590Srgrimes	 * Set up multicast address filter by passing all multicast addresses
5961590Srgrimes	 * through a crc generator, and then using the high order 6 bits as an
5971590Srgrimes	 * index into the 64 bit logical address filter.  The high order bit
5981590Srgrimes	 * selects the word, while the rest of the bits select the bit within
5991590Srgrimes	 * the word.
6001590Srgrimes	 */
6011590Srgrimes
6021590Srgrimes	if (ifp->if_flags & IFF_PROMISC || sc->sc_flags & LE_ALLMULTI) {
6031590Srgrimes		af[0] = af[1] = af[2] = af[3] = 0xffff;
6041590Srgrimes		return;
6051590Srgrimes	}
60639230Sgibbs
60739230Sgibbs	af[0] = af[1] = af[2] = af[3] = 0x0000;
60887715Smarkm	if_maddr_rlock(ifp);
60983131Sken	TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
61039230Sgibbs		if (ifma->ifma_addr->sa_family != AF_LINK)
61139230Sgibbs			continue;
61239230Sgibbs
61339230Sgibbs		crc = ether_crc32_le(LLADDR((struct sockaddr_dl *)
61439230Sgibbs		    ifma->ifma_addr), ETHER_ADDR_LEN);
61539230Sgibbs
61639230Sgibbs		/* Just want the 6 most significant bits. */
61739230Sgibbs		crc >>= 26;
61839230Sgibbs
61939230Sgibbs		/* Set the corresponding bit in the filter. */
62039230Sgibbs		af[crc >> 4] |= LE_HTOLE16(1 << (crc & 0xf));
62139230Sgibbs	}
62239230Sgibbs	if_maddr_runlock(ifp);
62339230Sgibbs}
62439230Sgibbs
6251590Srgrimes/*
62639230Sgibbs * Routines for accessing the transmit and receive buffers.
62739230Sgibbs * The various CPU and adapter configurations supported by this
62839230Sgibbs * driver require three different access methods for buffers
62939230Sgibbs * and descriptors:
63039230Sgibbs *	(1) contig (contiguous data; no padding),
63139230Sgibbs *	(2) gap2 (two bytes of data followed by two bytes of padding),
6321590Srgrimes *	(3) gap16 (16 bytes of data followed by 16 bytes of padding).
6331590Srgrimes */
6341590Srgrimes
6351590Srgrimes/*
636175387Sdelphij * contig: contiguous data with no padding.
6371590Srgrimes *
63887715Smarkm * Buffers may have any alignment.
639200167Sed */
6401590Srgrimes
641200167Sedvoid
642200167Sedlance_copytobuf_contig(struct lance_softc *sc, void *from, int boff, int len)
643200167Sed{
6441590Srgrimes	volatile caddr_t buf = sc->sc_mem;
645200167Sed
6461590Srgrimes	/*
6471590Srgrimes	 * Just call memcpy() to do the work.
6481590Srgrimes	 */
6491590Srgrimes	memcpy(buf + boff, from, len);
6501590Srgrimes}
651175387Sdelphij
6521590Srgrimesvoid
65387715Smarkmlance_copyfrombuf_contig(struct lance_softc *sc, void *to, int boff, int len)
65487715Smarkm{
6551590Srgrimes	volatile caddr_t buf = sc->sc_mem;
65687715Smarkm
6571590Srgrimes	/*
65887715Smarkm	 * Just call memcpy() to do the work.
65987715Smarkm	 */
66087715Smarkm	memcpy(to, buf + boff, len);
66187715Smarkm}
6621590Srgrimes
6631590Srgrimesvoid
6641590Srgrimeslance_zerobuf_contig(struct lance_softc *sc, int boff, int len)
665175387Sdelphij{
6661590Srgrimes	volatile caddr_t buf = sc->sc_mem;
667155668Sbde
6681590Srgrimes	/*
6691590Srgrimes	 * Just let memset() do the work
67087715Smarkm	 */
671158168Sbde	memset(buf + boff, 0, len);
672158168Sbde}
673158168Sbde
674158168Sbde#if 0
675158168Sbde/*
6761590Srgrimes * Examples only; duplicate these and tweak (if necessary) in
6771590Srgrimes * machine-specific front-ends.
6781590Srgrimes */
6791590Srgrimes
6801590Srgrimes/*
681155668Sbde * gap2: two bytes of data followed by two bytes of pad.
682155668Sbde *
683155668Sbde * Buffers must be 4-byte aligned.  The code doesn't worry about
684155668Sbde * doing an extra byte.
685155668Sbde */
686155668Sbde
687155668Sbdestatic void
688155668Sbdelance_copytobuf_gap2(struct lance_softc *sc, void *fromv, int boff, int len)
689155668Sbde{
690155668Sbde	volatile caddr_t buf = sc->sc_mem;
6911590Srgrimes	caddr_t from = fromv;
6921590Srgrimes	volatile uint16_t *bptr;
6931590Srgrimes
6941590Srgrimes	if (boff & 0x1) {
695175387Sdelphij		/* Handle unaligned first byte. */
6961590Srgrimes		bptr = ((volatile uint16_t *)buf) + (boff - 1);
697155668Sbde		*bptr = (*from++ << 8) | (*bptr & 0xff);
6981590Srgrimes		bptr += 2;
6991590Srgrimes		len--;
70087715Smarkm	} else
701158168Sbde		bptr = ((volatile uint16_t *)buf) + boff;
702158168Sbde	while (len > 1) {
703158168Sbde		*bptr = (from[1] << 8) | (from[0] & 0xff);
704158168Sbde		bptr += 2;
705158168Sbde		from += 2;
7061590Srgrimes		len -= 2;
7071590Srgrimes	}
7081590Srgrimes	if (len == 1)
7091590Srgrimes		*bptr = (uint16_t)*from;
7101590Srgrimes}
711155668Sbde
712155668Sbdestatic void
713155668Sbdelance_copyfrombuf_gap2(struct lance_softc *sc, void *tov, int boff, int len)
714248647Smav{
715248647Smav	volatile caddr_t buf = sc->sc_mem;
716248647Smav	caddr_t to = tov;
717248647Smav	volatile uint16_t *bptr;
718155668Sbde	uint16_t tmp;
7191590Srgrimes
7201590Srgrimes	if (boff & 0x1) {
7211590Srgrimes		/* Handle unaligned first byte. */
7221590Srgrimes		bptr = ((volatile uint16_t *)buf) + (boff - 1);
7231590Srgrimes		*to++ = (*bptr >> 8) & 0xff;
7241590Srgrimes		bptr += 2;
7251590Srgrimes		len--;
7261590Srgrimes	} else
727175387Sdelphij		bptr = ((volatile uint16_t *)buf) + boff;
72839230Sgibbs	while (len > 1) {
729155668Sbde		tmp = *bptr;
73039230Sgibbs		*to++ = tmp & 0xff;
73139230Sgibbs		*to++ = (tmp >> 8) & 0xff;
73287715Smarkm		bptr += 2;
733158168Sbde		len -= 2;
734158168Sbde	}
735158168Sbde	if (len == 1)
736158168Sbde		*to = *bptr & 0xff;
737158168Sbde}
73839230Sgibbs
73939230Sgibbsstatic void
74039230Sgibbslance_zerobuf_gap2(struct lance_softc *sc, int boff, int len)
74139230Sgibbs{
74239230Sgibbs	volatile caddr_t buf = sc->sc_mem;
743155668Sbde	volatile uint16_t *bptr;
744155668Sbde
745155668Sbde	if ((unsigned)boff & 0x1) {
746248647Smav		bptr = ((volatile uint16_t *)buf) + (boff - 1);
747248647Smav		*bptr &= 0xff;
748248647Smav		bptr += 2;
749248647Smav		len--;
750155668Sbde	} else
75139230Sgibbs		bptr = ((volatile uint16_t *)buf) + boff;
75239230Sgibbs	while (len > 0) {
75339230Sgibbs		*bptr = 0;
75439230Sgibbs		bptr += 2;
75539230Sgibbs		len -= 2;
75639230Sgibbs	}
75739230Sgibbs}
75839230Sgibbs
759175387Sdelphij/*
7601590Srgrimes * gap16: 16 bytes of data followed by 16 bytes of pad.
76139230Sgibbs *
76274671Stmm * Buffers must be 32-byte aligned.
76374671Stmm */
7641590Srgrimes
76587715Smarkmstatic void
76674671Stmmlance_copytobuf_gap16(struct lance_softc *sc, void *fromv, int boff, int len)
76787715Smarkm{
76887715Smarkm	volatile caddr_t buf = sc->sc_mem;
76987715Smarkm	caddr_t bptr, from = fromv;
77087715Smarkm	int xfer;
77187715Smarkm
77287715Smarkm	bptr = buf + ((boff << 1) & ~0x1f);
773246033Szont	boff &= 0xf;
77487715Smarkm	xfer = min(len, 16 - boff);
77587715Smarkm	while (len > 0) {
77687715Smarkm		memcpy(bptr + boff, from, xfer);
77787715Smarkm		from += xfer;
77887715Smarkm		bptr += 32;
77987715Smarkm		boff = 0;
78087715Smarkm		len -= xfer;
78187715Smarkm		xfer = min(len, 16);
78287715Smarkm	}
78387715Smarkm}
78487715Smarkm
78587715Smarkmstatic void
78687715Smarkmlance_copyfrombuf_gap16(struct lance_softc *sc, void *tov, int boff, int len)
78787715Smarkm{
78887715Smarkm	volatile caddr_t buf = sc->sc_mem;
78987715Smarkm	caddr_t bptr, to = tov;
79087715Smarkm	int xfer;
79187715Smarkm
79287715Smarkm	bptr = buf + ((boff << 1) & ~0x1f);
79387715Smarkm	boff &= 0xf;
79487715Smarkm	xfer = min(len, 16 - boff);
79587715Smarkm	while (len > 0) {
79687715Smarkm		memcpy(to, bptr + boff, xfer);
79787715Smarkm		to += xfer;
79887715Smarkm		bptr += 32;
79987715Smarkm		boff = 0;
80097970Sdes		len -= xfer;
80197970Sdes		xfer = min(len, 16);
80287715Smarkm	}
80387715Smarkm}
804292817Saraujo
80587715Smarkmstatic void
80669143Srwatsonlance_zerobuf_gap16(struct lance_softc *sc, int boff, int len)
80787715Smarkm{
8081590Srgrimes	volatile caddr_t buf = sc->sc_mem;
809109097Sdillon	caddr_t bptr;
81087715Smarkm	int xfer;
8111590Srgrimes
81287715Smarkm	bptr = buf + ((boff << 1) & ~0x1f);
8131590Srgrimes	boff &= 0xf;
81436430Sjhay	xfer = min(len, 16 - boff);
81569143Srwatson	while (len > 0) {
81669143Srwatson		memset(bptr + boff, 0, xfer);
81736430Sjhay		bptr += 32;
81839230Sgibbs		boff = 0;
81939230Sgibbs		len -= xfer;
82039230Sgibbs		xfer = min(len, 16);
82139230Sgibbs	}
82239230Sgibbs}
823112288Sphk#endif /* Example only */
82483131Sken