1155093Smarius/*	$NetBSD: lance.c,v 1.34 2005/12/24 20:27:30 perry Exp $	*/
2155093Smarius
3155093Smarius/*-
4155093Smarius * Copyright (c) 1997, 1998 The NetBSD Foundation, Inc.
5155093Smarius * All rights reserved.
6155093Smarius *
7155093Smarius * This code is derived from software contributed to The NetBSD Foundation
8155093Smarius * by Charles M. Hannum and by Jason R. Thorpe of the Numerical Aerospace
9155093Smarius * Simulation Facility, NASA Ames Research Center.
10155093Smarius *
11155093Smarius * Redistribution and use in source and binary forms, with or without
12155093Smarius * modification, are permitted provided that the following conditions
13155093Smarius * are met:
14155093Smarius * 1. Redistributions of source code must retain the above copyright
15155093Smarius *    notice, this list of conditions and the following disclaimer.
16155093Smarius * 2. Redistributions in binary form must reproduce the above copyright
17155093Smarius *    notice, this list of conditions and the following disclaimer in the
18155093Smarius *    documentation and/or other materials provided with the distribution.
19155093Smarius *
20155093Smarius * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21155093Smarius * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22155093Smarius * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23155093Smarius * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24155093Smarius * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25155093Smarius * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26155093Smarius * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27155093Smarius * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28155093Smarius * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29155093Smarius * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30155093Smarius * POSSIBILITY OF SUCH DAMAGE.
31155093Smarius */
32155093Smarius
33155093Smarius/*-
34155093Smarius * Copyright (c) 1992, 1993
35155093Smarius *	The Regents of the University of California.  All rights reserved.
36155093Smarius *
37155093Smarius * This code is derived from software contributed to Berkeley by
38155093Smarius * Ralph Campbell and Rick Macklem.
39155093Smarius *
40155093Smarius * Redistribution and use in source and binary forms, with or without
41155093Smarius * modification, are permitted provided that the following conditions
42155093Smarius * are met:
43155093Smarius * 1. Redistributions of source code must retain the above copyright
44155093Smarius *    notice, this list of conditions and the following disclaimer.
45155093Smarius * 2. Redistributions in binary form must reproduce the above copyright
46155093Smarius *    notice, this list of conditions and the following disclaimer in the
47155093Smarius *    documentation and/or other materials provided with the distribution.
48155093Smarius * 3. Neither the name of the University nor the names of its contributors
49155093Smarius *    may be used to endorse or promote products derived from this software
50155093Smarius *    without specific prior written permission.
51155093Smarius *
52155093Smarius * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
53155093Smarius * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
54155093Smarius * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
55155093Smarius * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
56155093Smarius * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
57155093Smarius * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
58155093Smarius * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
59155093Smarius * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
60155093Smarius * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
61155093Smarius * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
62155093Smarius * SUCH DAMAGE.
63155093Smarius *
64155093Smarius *	@(#)if_le.c	8.2 (Berkeley) 11/16/93
65155093Smarius */
66155093Smarius
67155093Smarius#include <sys/cdefs.h>
68155093Smarius__FBSDID("$FreeBSD$");
69155093Smarius
70155093Smarius#include <sys/param.h>
71155093Smarius#include <sys/bus.h>
72155093Smarius#include <sys/endian.h>
73155093Smarius#include <sys/lock.h>
74164933Smarius#include <sys/kernel.h>
75155093Smarius#include <sys/mbuf.h>
76155093Smarius#include <sys/mutex.h>
77155093Smarius#include <sys/socket.h>
78155093Smarius#include <sys/sockio.h>
79155093Smarius
80155093Smarius#include <net/ethernet.h>
81155093Smarius#include <net/if.h>
82155093Smarius#include <net/if_arp.h>
83155093Smarius#include <net/if_dl.h>
84155093Smarius#include <net/if_media.h>
85155093Smarius#include <net/if_types.h>
86155093Smarius#include <net/if_vlan_var.h>
87155093Smarius
88158663Smarius#include <machine/bus.h>
89158663Smarius
90155093Smarius#include <dev/le/lancereg.h>
91155093Smarius#include <dev/le/lancevar.h>
92155093Smarius
93155093Smariusdevclass_t le_devclass;
94155093Smarius
95155093Smariusstatic void lance_start(struct ifnet *);
96155093Smariusstatic void lance_stop(struct lance_softc *);
97155093Smariusstatic void lance_init(void *);
98164933Smariusstatic void lance_watchdog(void *s);
99155093Smariusstatic int lance_mediachange(struct ifnet *);
100155093Smariusstatic void lance_mediastatus(struct ifnet *, struct ifmediareq *);
101155093Smariusstatic int lance_ioctl(struct ifnet *, u_long, caddr_t);
102155093Smarius
103155093Smariusint
104155093Smariuslance_config(struct lance_softc *sc, const char* name, int unit)
105155093Smarius{
106155093Smarius	struct ifnet *ifp;
107155093Smarius	int i, nbuf;
108155093Smarius
109155093Smarius	if (LE_LOCK_INITIALIZED(sc) == 0)
110155093Smarius		return (ENXIO);
111155093Smarius
112155093Smarius	ifp = sc->sc_ifp = if_alloc(IFT_ETHER);
113155093Smarius	if (ifp == NULL)
114155093Smarius		return (ENOSPC);
115155093Smarius
116164933Smarius	callout_init_mtx(&sc->sc_wdog_ch, &sc->sc_mtx, 0);
117164933Smarius
118155093Smarius	/* Initialize ifnet structure. */
119155093Smarius	ifp->if_softc = sc;
120155093Smarius	if_initname(ifp, name, unit);
121155093Smarius	ifp->if_start = lance_start;
122155093Smarius	ifp->if_ioctl = lance_ioctl;
123155093Smarius	ifp->if_init = lance_init;
124155093Smarius	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
125155093Smarius#ifdef LANCE_REVC_BUG
126155093Smarius	ifp->if_flags &= ~IFF_MULTICAST;
127155093Smarius#endif
128155093Smarius	ifp->if_baudrate = IF_Mbps(10);
129207554Ssobomax	IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen);
130207554Ssobomax	ifp->if_snd.ifq_drv_maxlen = ifqmaxlen;
131155093Smarius	IFQ_SET_READY(&ifp->if_snd);
132155093Smarius
133155093Smarius	/* Initialize ifmedia structures. */
134155093Smarius	ifmedia_init(&sc->sc_media, 0, lance_mediachange, lance_mediastatus);
135155093Smarius	if (sc->sc_supmedia != NULL) {
136155093Smarius		for (i = 0; i < sc->sc_nsupmedia; i++)
137155093Smarius			ifmedia_add(&sc->sc_media, sc->sc_supmedia[i], 0, NULL);
138155093Smarius		ifmedia_set(&sc->sc_media, sc->sc_defaultmedia);
139155093Smarius	} else {
140155093Smarius		ifmedia_add(&sc->sc_media,
141155093Smarius		    IFM_MAKEWORD(IFM_ETHER, IFM_MANUAL, 0, 0), 0, NULL);
142155093Smarius		ifmedia_set(&sc->sc_media,
143155093Smarius		    IFM_MAKEWORD(IFM_ETHER, IFM_MANUAL, 0, 0));
144155093Smarius	}
145155093Smarius
146155093Smarius	switch (sc->sc_memsize) {
147155093Smarius	case 8192:
148155093Smarius		sc->sc_nrbuf = 4;
149155093Smarius		sc->sc_ntbuf = 1;
150155093Smarius		break;
151155093Smarius	case 16384:
152155093Smarius		sc->sc_nrbuf = 8;
153155093Smarius		sc->sc_ntbuf = 2;
154155093Smarius		break;
155155093Smarius	case 32768:
156155093Smarius		sc->sc_nrbuf = 16;
157155093Smarius		sc->sc_ntbuf = 4;
158155093Smarius		break;
159155093Smarius	case 65536:
160155093Smarius		sc->sc_nrbuf = 32;
161155093Smarius		sc->sc_ntbuf = 8;
162155093Smarius		break;
163155093Smarius	case 131072:
164155093Smarius		sc->sc_nrbuf = 64;
165155093Smarius		sc->sc_ntbuf = 16;
166155093Smarius		break;
167155093Smarius	case 262144:
168155093Smarius		sc->sc_nrbuf = 128;
169155093Smarius		sc->sc_ntbuf = 32;
170155093Smarius		break;
171155093Smarius	default:
172155093Smarius		/* weird memory size; cope with it */
173155093Smarius		nbuf = sc->sc_memsize / LEBLEN;
174155093Smarius		sc->sc_ntbuf = nbuf / 5;
175155093Smarius		sc->sc_nrbuf = nbuf - sc->sc_ntbuf;
176155093Smarius	}
177155093Smarius
178155093Smarius	if_printf(ifp, "%d receive buffers, %d transmit buffers\n",
179155093Smarius	    sc->sc_nrbuf, sc->sc_ntbuf);
180155093Smarius
181155093Smarius	/* Make sure the chip is stopped. */
182155093Smarius	LE_LOCK(sc);
183155093Smarius	lance_stop(sc);
184155093Smarius	LE_UNLOCK(sc);
185155093Smarius
186155093Smarius	return (0);
187155093Smarius}
188155093Smarius
189155093Smariusvoid
190155093Smariuslance_attach(struct lance_softc *sc)
191155093Smarius{
192155093Smarius	struct ifnet *ifp = sc->sc_ifp;
193155093Smarius
194155093Smarius	/* Attach the interface. */
195155093Smarius	ether_ifattach(ifp, sc->sc_enaddr);
196155093Smarius
197155093Smarius	/* Claim 802.1q capability. */
198155093Smarius	ifp->if_data.ifi_hdrlen = sizeof(struct ether_vlan_header);
199155093Smarius	ifp->if_capabilities |= IFCAP_VLAN_MTU;
200155093Smarius	ifp->if_capenable |= IFCAP_VLAN_MTU;
201155093Smarius}
202155093Smarius
203155093Smariusvoid
204155093Smariuslance_detach(struct lance_softc *sc)
205155093Smarius{
206155093Smarius	struct ifnet *ifp = sc->sc_ifp;
207155093Smarius
208155093Smarius	LE_LOCK(sc);
209155093Smarius	lance_stop(sc);
210155093Smarius	LE_UNLOCK(sc);
211164933Smarius	callout_drain(&sc->sc_wdog_ch);
212155093Smarius	ether_ifdetach(ifp);
213155093Smarius	if_free(ifp);
214155093Smarius}
215155093Smarius
216155093Smariusvoid
217155093Smariuslance_suspend(struct lance_softc *sc)
218155093Smarius{
219155093Smarius
220155093Smarius	LE_LOCK(sc);
221155093Smarius	lance_stop(sc);
222155093Smarius	LE_UNLOCK(sc);
223155093Smarius}
224155093Smarius
225155093Smariusvoid
226155093Smariuslance_resume(struct lance_softc *sc)
227155093Smarius{
228155093Smarius
229155093Smarius	LE_LOCK(sc);
230155093Smarius	if (sc->sc_ifp->if_flags & IFF_UP)
231155093Smarius		lance_init_locked(sc);
232155093Smarius	LE_UNLOCK(sc);
233155093Smarius}
234155093Smarius
235155093Smariusstatic void
236155093Smariuslance_start(struct ifnet *ifp)
237155093Smarius{
238155093Smarius	struct lance_softc *sc = ifp->if_softc;
239155093Smarius
240155093Smarius	LE_LOCK(sc);
241155093Smarius	(*sc->sc_start_locked)(sc);
242155093Smarius	LE_UNLOCK(sc);
243155093Smarius}
244155093Smarius
245155093Smariusstatic void
246155093Smariuslance_stop(struct lance_softc *sc)
247155093Smarius{
248155093Smarius	struct ifnet *ifp = sc->sc_ifp;
249155093Smarius
250155093Smarius	LE_LOCK_ASSERT(sc, MA_OWNED);
251155093Smarius
252155093Smarius	/*
253155093Smarius	 * Mark the interface down and cancel the watchdog timer.
254155093Smarius	 */
255155093Smarius	ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
256164933Smarius	callout_stop(&sc->sc_wdog_ch);
257164933Smarius	sc->sc_wdog_timer = 0;
258155093Smarius
259155093Smarius	(*sc->sc_wrcsr)(sc, LE_CSR0, LE_C0_STOP);
260155093Smarius}
261155093Smarius
262155093Smariusstatic void
263155093Smariuslance_init(void *xsc)
264155093Smarius{
265155093Smarius	struct lance_softc *sc = (struct lance_softc *)xsc;
266155093Smarius
267155093Smarius	LE_LOCK(sc);
268155093Smarius	lance_init_locked(sc);
269155093Smarius	LE_UNLOCK(sc);
270155093Smarius}
271155093Smarius
272155093Smarius/*
273155093Smarius * Initialization of interface; set up initialization block
274155093Smarius * and transmit/receive descriptor rings.
275155093Smarius */
276155093Smariusvoid
277155093Smariuslance_init_locked(struct lance_softc *sc)
278155093Smarius{
279155093Smarius	struct ifnet *ifp = sc->sc_ifp;
280155093Smarius	u_long a;
281155093Smarius	int timo;
282155093Smarius
283155093Smarius	LE_LOCK_ASSERT(sc, MA_OWNED);
284155093Smarius
285155093Smarius	(*sc->sc_wrcsr)(sc, LE_CSR0, LE_C0_STOP);
286155093Smarius	DELAY(100);
287155093Smarius
288155093Smarius	/* Newer LANCE chips have a reset register. */
289155093Smarius	if (sc->sc_hwreset)
290155093Smarius		(*sc->sc_hwreset)(sc);
291155093Smarius
292155093Smarius	/* Set the correct byte swapping mode, etc. */
293155093Smarius	(*sc->sc_wrcsr)(sc, LE_CSR3, sc->sc_conf3);
294155093Smarius
295166139Smarius	/* Set the current media. This may require the chip to be stopped. */
296166139Smarius	if (sc->sc_mediachange)
297166139Smarius		(void)(*sc->sc_mediachange)(sc);
298166139Smarius
299155093Smarius	/*
300155093Smarius	 * Update our private copy of the Ethernet address.
301155093Smarius	 * We NEED the copy so we can ensure its alignment!
302155093Smarius	 */
303155093Smarius	memcpy(sc->sc_enaddr, IF_LLADDR(ifp), ETHER_ADDR_LEN);
304155093Smarius
305155093Smarius	/* Set up LANCE init block. */
306155093Smarius	(*sc->sc_meminit)(sc);
307155093Smarius
308155093Smarius	/* Give LANCE the physical address of its init block. */
309155093Smarius	a = sc->sc_addr + LE_INITADDR(sc);
310155093Smarius	(*sc->sc_wrcsr)(sc, LE_CSR1, a & 0xffff);
311155093Smarius	(*sc->sc_wrcsr)(sc, LE_CSR2, a >> 16);
312155093Smarius
313155093Smarius	/* Try to initialize the LANCE. */
314155093Smarius	DELAY(100);
315155093Smarius	(*sc->sc_wrcsr)(sc, LE_CSR0, LE_C0_INIT);
316155093Smarius
317155093Smarius	/* Wait for initialization to finish. */
318155093Smarius	for (timo = 100000; timo; timo--)
319155093Smarius		if ((*sc->sc_rdcsr)(sc, LE_CSR0) & LE_C0_IDON)
320155093Smarius			break;
321155093Smarius
322155093Smarius	if ((*sc->sc_rdcsr)(sc, LE_CSR0) & LE_C0_IDON) {
323155093Smarius		/* Start the LANCE. */
324155093Smarius		(*sc->sc_wrcsr)(sc, LE_CSR0, LE_C0_INEA | LE_C0_STRT);
325155093Smarius		ifp->if_drv_flags |= IFF_DRV_RUNNING;
326155093Smarius		ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
327164933Smarius		sc->sc_wdog_timer = 0;
328164933Smarius		callout_reset(&sc->sc_wdog_ch, hz, lance_watchdog, sc);
329155093Smarius		(*sc->sc_start_locked)(sc);
330155093Smarius	} else
331155093Smarius		if_printf(ifp, "controller failed to initialize\n");
332155093Smarius
333155093Smarius	if (sc->sc_hwinit)
334155093Smarius		(*sc->sc_hwinit)(sc);
335155093Smarius}
336155093Smarius
337155093Smarius/*
338155093Smarius * Routine to copy from mbuf chain to transmit buffer in
339155093Smarius * network buffer memory.
340155093Smarius */
341155093Smariusint
342155093Smariuslance_put(struct lance_softc *sc, int boff, struct mbuf *m)
343155093Smarius{
344155093Smarius	struct mbuf *n;
345155093Smarius	int len, tlen = 0;
346155093Smarius
347155093Smarius	LE_LOCK_ASSERT(sc, MA_OWNED);
348155093Smarius
349155093Smarius	for (; m; m = n) {
350155093Smarius		len = m->m_len;
351155093Smarius		if (len == 0) {
352155093Smarius			n = m_free(m);
353155093Smarius			m = NULL;
354155093Smarius			continue;
355155093Smarius		}
356155093Smarius		(*sc->sc_copytobuf)(sc, mtod(m, caddr_t), boff, len);
357155093Smarius		boff += len;
358155093Smarius		tlen += len;
359155093Smarius		n = m_free(m);
360155093Smarius		m = NULL;
361155093Smarius	}
362155093Smarius	if (tlen < LEMINSIZE) {
363155093Smarius		(*sc->sc_zerobuf)(sc, boff, LEMINSIZE - tlen);
364155093Smarius		tlen = LEMINSIZE;
365155093Smarius	}
366155093Smarius	return (tlen);
367155093Smarius}
368155093Smarius
369155093Smarius/*
370155093Smarius * Pull data off an interface.
371155093Smarius * Len is length of data, with local net header stripped.
372155093Smarius * We copy the data into mbufs.  When full cluster sized units are present
373155093Smarius * we copy into clusters.
374155093Smarius */
375158663Smariusstruct mbuf *
376155093Smariuslance_get(struct lance_softc *sc, int boff, int totlen)
377155093Smarius{
378155093Smarius	struct ifnet *ifp = sc->sc_ifp;
379155093Smarius	struct mbuf *m, *m0, *newm;
380155093Smarius	caddr_t newdata;
381155093Smarius	int len;
382155093Smarius
383158663Smarius	if (totlen <= ETHER_HDR_LEN || totlen > LEBLEN - ETHER_CRC_LEN) {
384158663Smarius#ifdef LEDEBUG
385158663Smarius		if_printf(ifp, "invalid packet size %d; dropping\n", totlen);
386158663Smarius#endif
387158663Smarius		return (NULL);
388158663Smarius	}
389158663Smarius
390248078Smarius	MGETHDR(m0, M_NOWAIT, MT_DATA);
391158663Smarius	if (m0 == NULL)
392158663Smarius		return (NULL);
393155093Smarius	m0->m_pkthdr.rcvif = ifp;
394155093Smarius	m0->m_pkthdr.len = totlen;
395155093Smarius	len = MHLEN;
396155093Smarius	m = m0;
397155093Smarius
398155093Smarius	while (totlen > 0) {
399155093Smarius		if (totlen >= MINCLSIZE) {
400248078Smarius			MCLGET(m, M_NOWAIT);
401155093Smarius			if ((m->m_flags & M_EXT) == 0)
402155093Smarius				goto bad;
403155093Smarius			len = MCLBYTES;
404155093Smarius		}
405155093Smarius
406155093Smarius		if (m == m0) {
407155093Smarius			newdata = (caddr_t)
408155093Smarius			    ALIGN(m->m_data + ETHER_HDR_LEN) - ETHER_HDR_LEN;
409155093Smarius			len -= newdata - m->m_data;
410155093Smarius			m->m_data = newdata;
411155093Smarius		}
412155093Smarius
413155093Smarius		m->m_len = len = min(totlen, len);
414155093Smarius		(*sc->sc_copyfrombuf)(sc, mtod(m, caddr_t), boff, len);
415155093Smarius		boff += len;
416155093Smarius
417155093Smarius		totlen -= len;
418155093Smarius		if (totlen > 0) {
419248078Smarius			MGET(newm, M_NOWAIT, MT_DATA);
420155093Smarius			if (newm == 0)
421155093Smarius				goto bad;
422155093Smarius			len = MLEN;
423155093Smarius			m = m->m_next = newm;
424155093Smarius		}
425155093Smarius	}
426155093Smarius
427155093Smarius	return (m0);
428155093Smarius
429155093Smarius bad:
430155093Smarius	m_freem(m0);
431158663Smarius	return (NULL);
432155093Smarius}
433155093Smarius
434155093Smariusstatic void
435164933Smariuslance_watchdog(void *xsc)
436155093Smarius{
437164933Smarius	struct lance_softc *sc = (struct lance_softc *)xsc;
438164933Smarius	struct ifnet *ifp = sc->sc_ifp;
439155093Smarius
440164933Smarius	LE_LOCK_ASSERT(sc, MA_OWNED);
441164933Smarius
442164933Smarius	if (sc->sc_wdog_timer == 0 || --sc->sc_wdog_timer != 0) {
443164933Smarius		callout_reset(&sc->sc_wdog_ch, hz, lance_watchdog, sc);
444164933Smarius		return;
445164933Smarius	}
446164933Smarius
447155093Smarius	if_printf(ifp, "device timeout\n");
448155093Smarius	++ifp->if_oerrors;
449155093Smarius	lance_init_locked(sc);
450155093Smarius}
451155093Smarius
452155093Smariusstatic int
453155093Smariuslance_mediachange(struct ifnet *ifp)
454155093Smarius{
455155093Smarius	struct lance_softc *sc = ifp->if_softc;
456155093Smarius
457155093Smarius	if (sc->sc_mediachange) {
458166139Smarius		/*
459166139Smarius		 * For setting the port in LE_CSR15 the PCnet chips must
460166139Smarius		 * be powered down or stopped and unlike documented may
461166139Smarius		 * not take effect without an initialization. So don't
462166139Smarius		 * invoke (*sc_mediachange) directly here but go through
463166139Smarius		 * lance_init_locked().
464166139Smarius		 */
465155093Smarius		LE_LOCK(sc);
466166139Smarius		lance_stop(sc);
467166139Smarius		lance_init_locked(sc);
468166139Smarius		if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
469166139Smarius			(*sc->sc_start_locked)(sc);
470155093Smarius		LE_UNLOCK(sc);
471155093Smarius	}
472155093Smarius	return (0);
473155093Smarius}
474155093Smarius
475155093Smariusstatic void
476155093Smariuslance_mediastatus(struct ifnet *ifp, struct ifmediareq *ifmr)
477155093Smarius{
478155093Smarius	struct lance_softc *sc = ifp->if_softc;
479155093Smarius
480155093Smarius	LE_LOCK(sc);
481155093Smarius	if (!(ifp->if_flags & IFF_UP)) {
482155093Smarius		LE_UNLOCK(sc);
483155093Smarius		return;
484155093Smarius	}
485155093Smarius
486155093Smarius	ifmr->ifm_status = IFM_AVALID;
487155093Smarius	if (sc->sc_flags & LE_CARRIER)
488155093Smarius		ifmr->ifm_status |= IFM_ACTIVE;
489155093Smarius
490155093Smarius	if (sc->sc_mediastatus)
491155093Smarius		(*sc->sc_mediastatus)(sc, ifmr);
492155093Smarius	LE_UNLOCK(sc);
493155093Smarius}
494155093Smarius
495155093Smarius/*
496155093Smarius * Process an ioctl request.
497155093Smarius */
498155093Smariusstatic int
499155093Smariuslance_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
500155093Smarius{
501155093Smarius	struct lance_softc *sc = ifp->if_softc;
502155093Smarius	struct ifreq *ifr = (struct ifreq *)data;
503155093Smarius	int error = 0;
504155093Smarius
505155093Smarius	switch (cmd) {
506155093Smarius	case SIOCSIFFLAGS:
507155093Smarius		LE_LOCK(sc);
508155093Smarius		if (ifp->if_flags & IFF_PROMISC) {
509155093Smarius			if (!(sc->sc_flags & LE_PROMISC)) {
510155093Smarius				sc->sc_flags |= LE_PROMISC;
511155093Smarius				lance_init_locked(sc);
512155093Smarius			}
513155093Smarius		} else if (sc->sc_flags & LE_PROMISC) {
514155093Smarius			sc->sc_flags &= ~LE_PROMISC;
515155093Smarius			lance_init_locked(sc);
516155093Smarius		}
517155093Smarius
518155093Smarius		if ((ifp->if_flags & IFF_ALLMULTI) &&
519155093Smarius		    !(sc->sc_flags & LE_ALLMULTI)) {
520155093Smarius			sc->sc_flags |= LE_ALLMULTI;
521155093Smarius			lance_init_locked(sc);
522155093Smarius		} else if (!(ifp->if_flags & IFF_ALLMULTI) &&
523155093Smarius		    (sc->sc_flags & LE_ALLMULTI)) {
524155093Smarius			sc->sc_flags &= ~LE_ALLMULTI;
525155093Smarius			lance_init_locked(sc);
526155093Smarius		}
527155093Smarius
528155093Smarius		if (!(ifp->if_flags & IFF_UP) &&
529155093Smarius		    ifp->if_drv_flags & IFF_DRV_RUNNING) {
530155093Smarius			/*
531155093Smarius			 * If interface is marked down and it is running, then
532155093Smarius			 * stop it.
533155093Smarius			 */
534155093Smarius			lance_stop(sc);
535155093Smarius		} else if (ifp->if_flags & IFF_UP &&
536155093Smarius		    !(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
537155093Smarius			/*
538155093Smarius			 * If interface is marked up and it is stopped, then
539155093Smarius			 * start it.
540155093Smarius			 */
541155093Smarius			lance_init_locked(sc);
542155093Smarius		}
543155093Smarius#ifdef LEDEBUG
544155093Smarius		if (ifp->if_flags & IFF_DEBUG)
545155093Smarius			sc->sc_flags |= LE_DEBUG;
546155093Smarius		else
547155093Smarius			sc->sc_flags &= ~LE_DEBUG;
548155093Smarius#endif
549155093Smarius		LE_UNLOCK(sc);
550155093Smarius		break;
551155093Smarius
552155093Smarius	case SIOCADDMULTI:
553155093Smarius	case SIOCDELMULTI:
554155093Smarius		/*
555155093Smarius		 * Multicast list has changed; set the hardware filter
556155093Smarius		 * accordingly.
557155093Smarius		 */
558155093Smarius		LE_LOCK(sc);
559155093Smarius		if (ifp->if_drv_flags & IFF_DRV_RUNNING)
560155093Smarius			lance_init_locked(sc);
561155093Smarius		LE_UNLOCK(sc);
562155093Smarius		break;
563155093Smarius
564155093Smarius	case SIOCGIFMEDIA:
565155093Smarius	case SIOCSIFMEDIA:
566155093Smarius		error = ifmedia_ioctl(ifp, ifr, &sc->sc_media, cmd);
567155093Smarius		break;
568155093Smarius
569155093Smarius	default:
570155093Smarius		error = ether_ioctl(ifp, cmd, data);
571155093Smarius		break;
572155093Smarius	}
573155093Smarius
574155093Smarius	return (error);
575155093Smarius}
576155093Smarius
577155093Smarius/*
578155093Smarius * Set up the logical address filter.
579155093Smarius */
580155093Smariusvoid
581155093Smariuslance_setladrf(struct lance_softc *sc, uint16_t *af)
582155093Smarius{
583155093Smarius	struct ifnet *ifp = sc->sc_ifp;
584155093Smarius	struct ifmultiaddr *ifma;
585155093Smarius	uint32_t crc;
586155093Smarius
587155093Smarius	/*
588155093Smarius	 * Set up multicast address filter by passing all multicast addresses
589155093Smarius	 * through a crc generator, and then using the high order 6 bits as an
590155093Smarius	 * index into the 64 bit logical address filter.  The high order bit
591155093Smarius	 * selects the word, while the rest of the bits select the bit within
592155093Smarius	 * the word.
593155093Smarius	 */
594155093Smarius
595155093Smarius	if (ifp->if_flags & IFF_PROMISC || sc->sc_flags & LE_ALLMULTI) {
596155093Smarius		af[0] = af[1] = af[2] = af[3] = 0xffff;
597155093Smarius		return;
598155093Smarius	}
599155093Smarius
600155093Smarius	af[0] = af[1] = af[2] = af[3] = 0x0000;
601195049Srwatson	if_maddr_rlock(ifp);
602155093Smarius	TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
603155093Smarius		if (ifma->ifma_addr->sa_family != AF_LINK)
604155093Smarius			continue;
605155093Smarius
606155093Smarius		crc = ether_crc32_le(LLADDR((struct sockaddr_dl *)
607155093Smarius		    ifma->ifma_addr), ETHER_ADDR_LEN);
608155093Smarius
609155093Smarius		/* Just want the 6 most significant bits. */
610155093Smarius		crc >>= 26;
611155093Smarius
612155093Smarius		/* Set the corresponding bit in the filter. */
613158663Smarius		af[crc >> 4] |= LE_HTOLE16(1 << (crc & 0xf));
614155093Smarius	}
615195049Srwatson	if_maddr_runlock(ifp);
616155093Smarius}
617155093Smarius
618155093Smarius/*
619155093Smarius * Routines for accessing the transmit and receive buffers.
620155093Smarius * The various CPU and adapter configurations supported by this
621155093Smarius * driver require three different access methods for buffers
622155093Smarius * and descriptors:
623155093Smarius *	(1) contig (contiguous data; no padding),
624155093Smarius *	(2) gap2 (two bytes of data followed by two bytes of padding),
625155093Smarius *	(3) gap16 (16 bytes of data followed by 16 bytes of padding).
626155093Smarius */
627155093Smarius
628155093Smarius/*
629155093Smarius * contig: contiguous data with no padding.
630155093Smarius *
631155093Smarius * Buffers may have any alignment.
632155093Smarius */
633155093Smarius
634155093Smariusvoid
635155093Smariuslance_copytobuf_contig(struct lance_softc *sc, void *from, int boff, int len)
636155093Smarius{
637155093Smarius	volatile caddr_t buf = sc->sc_mem;
638155093Smarius
639155093Smarius	/*
640155093Smarius	 * Just call memcpy() to do the work.
641155093Smarius	 */
642155093Smarius	memcpy(buf + boff, from, len);
643155093Smarius}
644155093Smarius
645155093Smariusvoid
646155093Smariuslance_copyfrombuf_contig(struct lance_softc *sc, void *to, int boff, int len)
647155093Smarius{
648155093Smarius	volatile caddr_t buf = sc->sc_mem;
649155093Smarius
650155093Smarius	/*
651155093Smarius	 * Just call memcpy() to do the work.
652155093Smarius	 */
653155093Smarius	memcpy(to, buf + boff, len);
654155093Smarius}
655155093Smarius
656155093Smariusvoid
657155093Smariuslance_zerobuf_contig(struct lance_softc *sc, int boff, int len)
658155093Smarius{
659155093Smarius	volatile caddr_t buf = sc->sc_mem;
660155093Smarius
661155093Smarius	/*
662155093Smarius	 * Just let memset() do the work
663155093Smarius	 */
664155093Smarius	memset(buf + boff, 0, len);
665155093Smarius}
666155093Smarius
667155093Smarius#if 0
668155093Smarius/*
669155093Smarius * Examples only; duplicate these and tweak (if necessary) in
670155093Smarius * machine-specific front-ends.
671155093Smarius */
672155093Smarius
673155093Smarius/*
674155093Smarius * gap2: two bytes of data followed by two bytes of pad.
675155093Smarius *
676155093Smarius * Buffers must be 4-byte aligned.  The code doesn't worry about
677155093Smarius * doing an extra byte.
678155093Smarius */
679155093Smarius
680155093Smariusstatic void
681155093Smariuslance_copytobuf_gap2(struct lance_softc *sc, void *fromv, int boff, int len)
682155093Smarius{
683155093Smarius	volatile caddr_t buf = sc->sc_mem;
684155093Smarius	caddr_t from = fromv;
685155093Smarius	volatile uint16_t *bptr;
686155093Smarius
687155093Smarius	if (boff & 0x1) {
688155093Smarius		/* Handle unaligned first byte. */
689155093Smarius		bptr = ((volatile uint16_t *)buf) + (boff - 1);
690155093Smarius		*bptr = (*from++ << 8) | (*bptr & 0xff);
691155093Smarius		bptr += 2;
692155093Smarius		len--;
693155093Smarius	} else
694155093Smarius		bptr = ((volatile uint16_t *)buf) + boff;
695155093Smarius	while (len > 1) {
696155093Smarius		*bptr = (from[1] << 8) | (from[0] & 0xff);
697155093Smarius		bptr += 2;
698155093Smarius		from += 2;
699155093Smarius		len -= 2;
700155093Smarius	}
701155093Smarius	if (len == 1)
702155093Smarius		*bptr = (uint16_t)*from;
703155093Smarius}
704155093Smarius
705155093Smariusstatic void
706155093Smariuslance_copyfrombuf_gap2(struct lance_softc *sc, void *tov, int boff, int len)
707155093Smarius{
708155093Smarius	volatile caddr_t buf = sc->sc_mem;
709155093Smarius	caddr_t to = tov;
710155093Smarius	volatile uint16_t *bptr;
711155093Smarius	uint16_t tmp;
712155093Smarius
713155093Smarius	if (boff & 0x1) {
714155093Smarius		/* Handle unaligned first byte. */
715155093Smarius		bptr = ((volatile uint16_t *)buf) + (boff - 1);
716155093Smarius		*to++ = (*bptr >> 8) & 0xff;
717155093Smarius		bptr += 2;
718155093Smarius		len--;
719155093Smarius	} else
720155093Smarius		bptr = ((volatile uint16_t *)buf) + boff;
721155093Smarius	while (len > 1) {
722155093Smarius		tmp = *bptr;
723155093Smarius		*to++ = tmp & 0xff;
724155093Smarius		*to++ = (tmp >> 8) & 0xff;
725155093Smarius		bptr += 2;
726155093Smarius		len -= 2;
727155093Smarius	}
728155093Smarius	if (len == 1)
729155093Smarius		*to = *bptr & 0xff;
730155093Smarius}
731155093Smarius
732155093Smariusstatic void
733155093Smariuslance_zerobuf_gap2(struct lance_softc *sc, int boff, int len)
734155093Smarius{
735155093Smarius	volatile caddr_t buf = sc->sc_mem;
736155093Smarius	volatile uint16_t *bptr;
737155093Smarius
738155093Smarius	if ((unsigned)boff & 0x1) {
739155093Smarius		bptr = ((volatile uint16_t *)buf) + (boff - 1);
740155093Smarius		*bptr &= 0xff;
741155093Smarius		bptr += 2;
742155093Smarius		len--;
743155093Smarius	} else
744155093Smarius		bptr = ((volatile uint16_t *)buf) + boff;
745155093Smarius	while (len > 0) {
746155093Smarius		*bptr = 0;
747155093Smarius		bptr += 2;
748155093Smarius		len -= 2;
749155093Smarius	}
750155093Smarius}
751155093Smarius
752155093Smarius/*
753155093Smarius * gap16: 16 bytes of data followed by 16 bytes of pad.
754155093Smarius *
755155093Smarius * Buffers must be 32-byte aligned.
756155093Smarius */
757155093Smarius
758155093Smariusstatic void
759155093Smariuslance_copytobuf_gap16(struct lance_softc *sc, void *fromv, int boff, int len)
760155093Smarius{
761155093Smarius	volatile caddr_t buf = sc->sc_mem;
762155093Smarius	caddr_t bptr, from = fromv;
763155093Smarius	int xfer;
764155093Smarius
765155093Smarius	bptr = buf + ((boff << 1) & ~0x1f);
766155093Smarius	boff &= 0xf;
767155093Smarius	xfer = min(len, 16 - boff);
768155093Smarius	while (len > 0) {
769155093Smarius		memcpy(bptr + boff, from, xfer);
770155093Smarius		from += xfer;
771155093Smarius		bptr += 32;
772155093Smarius		boff = 0;
773155093Smarius		len -= xfer;
774155093Smarius		xfer = min(len, 16);
775155093Smarius	}
776155093Smarius}
777155093Smarius
778155093Smariusstatic void
779155093Smariuslance_copyfrombuf_gap16(struct lance_softc *sc, void *tov, int boff, int len)
780155093Smarius{
781155093Smarius	volatile caddr_t buf = sc->sc_mem;
782155093Smarius	caddr_t bptr, to = tov;
783155093Smarius	int xfer;
784155093Smarius
785155093Smarius	bptr = buf + ((boff << 1) & ~0x1f);
786155093Smarius	boff &= 0xf;
787155093Smarius	xfer = min(len, 16 - boff);
788155093Smarius	while (len > 0) {
789155093Smarius		memcpy(to, bptr + boff, xfer);
790155093Smarius		to += xfer;
791155093Smarius		bptr += 32;
792155093Smarius		boff = 0;
793155093Smarius		len -= xfer;
794155093Smarius		xfer = min(len, 16);
795155093Smarius	}
796155093Smarius}
797155093Smarius
798155093Smariusstatic void
799155093Smariuslance_zerobuf_gap16(struct lance_softc *sc, int boff, int len)
800155093Smarius{
801155093Smarius	volatile caddr_t buf = sc->sc_mem;
802155093Smarius	caddr_t bptr;
803155093Smarius	int xfer;
804155093Smarius
805155093Smarius	bptr = buf + ((boff << 1) & ~0x1f);
806155093Smarius	boff &= 0xf;
807155093Smarius	xfer = min(len, 16 - boff);
808155093Smarius	while (len > 0) {
809155093Smarius		memset(bptr + boff, 0, xfer);
810155093Smarius		bptr += 32;
811155093Smarius		boff = 0;
812155093Smarius		len -= xfer;
813155093Smarius		xfer = min(len, 16);
814155093Smarius	}
815155093Smarius}
816155093Smarius#endif /* Example only */
817