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