xref: /freebsd/sys/dev/le/am7990.c (revision 60c430f511ec69c2c1e205f60294a6ba395adf53)
1a7ee7a7dSMarius Strobl /*	$NetBSD: am7990.c,v 1.68 2005/12/11 12:21:25 christos Exp $	*/
2a7ee7a7dSMarius Strobl 
3a7ee7a7dSMarius Strobl /*-
4a7ee7a7dSMarius Strobl  * Copyright (c) 1997, 1998 The NetBSD Foundation, Inc.
5a7ee7a7dSMarius Strobl  * All rights reserved.
6a7ee7a7dSMarius Strobl  *
7a7ee7a7dSMarius Strobl  * This code is derived from software contributed to The NetBSD Foundation
8a7ee7a7dSMarius Strobl  * by Charles M. Hannum and by Jason R. Thorpe of the Numerical Aerospace
9a7ee7a7dSMarius Strobl  * Simulation Facility, NASA Ames Research Center.
10a7ee7a7dSMarius Strobl  *
11a7ee7a7dSMarius Strobl  * Redistribution and use in source and binary forms, with or without
12a7ee7a7dSMarius Strobl  * modification, are permitted provided that the following conditions
13a7ee7a7dSMarius Strobl  * are met:
14a7ee7a7dSMarius Strobl  * 1. Redistributions of source code must retain the above copyright
15a7ee7a7dSMarius Strobl  *    notice, this list of conditions and the following disclaimer.
16a7ee7a7dSMarius Strobl  * 2. Redistributions in binary form must reproduce the above copyright
17a7ee7a7dSMarius Strobl  *    notice, this list of conditions and the following disclaimer in the
18a7ee7a7dSMarius Strobl  *    documentation and/or other materials provided with the distribution.
19a7ee7a7dSMarius Strobl  * 3. All advertising materials mentioning features or use of this software
20a7ee7a7dSMarius Strobl  *    must display the following acknowledgement:
21a7ee7a7dSMarius Strobl  *	This product includes software developed by the NetBSD
22a7ee7a7dSMarius Strobl  *	Foundation, Inc. and its contributors.
23a7ee7a7dSMarius Strobl  * 4. Neither the name of The NetBSD Foundation nor the names of its
24a7ee7a7dSMarius Strobl  *    contributors may be used to endorse or promote products derived
25a7ee7a7dSMarius Strobl  *    from this software without specific prior written permission.
26a7ee7a7dSMarius Strobl  *
27a7ee7a7dSMarius Strobl  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
28a7ee7a7dSMarius Strobl  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
29a7ee7a7dSMarius Strobl  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30a7ee7a7dSMarius Strobl  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
31a7ee7a7dSMarius Strobl  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32a7ee7a7dSMarius Strobl  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33a7ee7a7dSMarius Strobl  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34a7ee7a7dSMarius Strobl  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35a7ee7a7dSMarius Strobl  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36a7ee7a7dSMarius Strobl  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37a7ee7a7dSMarius Strobl  * POSSIBILITY OF SUCH DAMAGE.
38a7ee7a7dSMarius Strobl  */
39a7ee7a7dSMarius Strobl 
40a7ee7a7dSMarius Strobl /*-
41a7ee7a7dSMarius Strobl  * Copyright (c) 1992, 1993
42a7ee7a7dSMarius Strobl  *	The Regents of the University of California.  All rights reserved.
43a7ee7a7dSMarius Strobl  *
44a7ee7a7dSMarius Strobl  * This code is derived from software contributed to Berkeley by
45a7ee7a7dSMarius Strobl  * Ralph Campbell and Rick Macklem.
46a7ee7a7dSMarius Strobl  *
47a7ee7a7dSMarius Strobl  * Redistribution and use in source and binary forms, with or without
48a7ee7a7dSMarius Strobl  * modification, are permitted provided that the following conditions
49a7ee7a7dSMarius Strobl  * are met:
50a7ee7a7dSMarius Strobl  * 1. Redistributions of source code must retain the above copyright
51a7ee7a7dSMarius Strobl  *    notice, this list of conditions and the following disclaimer.
52a7ee7a7dSMarius Strobl  * 2. Redistributions in binary form must reproduce the above copyright
53a7ee7a7dSMarius Strobl  *    notice, this list of conditions and the following disclaimer in the
54a7ee7a7dSMarius Strobl  *    documentation and/or other materials provided with the distribution.
55a7ee7a7dSMarius Strobl  * 3. Neither the name of the University nor the names of its contributors
56a7ee7a7dSMarius Strobl  *    may be used to endorse or promote products derived from this software
57a7ee7a7dSMarius Strobl  *    without specific prior written permission.
58a7ee7a7dSMarius Strobl  *
59a7ee7a7dSMarius Strobl  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
60a7ee7a7dSMarius Strobl  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
61a7ee7a7dSMarius Strobl  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
62a7ee7a7dSMarius Strobl  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
63a7ee7a7dSMarius Strobl  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
64a7ee7a7dSMarius Strobl  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
65a7ee7a7dSMarius Strobl  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
66a7ee7a7dSMarius Strobl  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
67a7ee7a7dSMarius Strobl  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
68a7ee7a7dSMarius Strobl  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
69a7ee7a7dSMarius Strobl  * SUCH DAMAGE.
70a7ee7a7dSMarius Strobl  *
71a7ee7a7dSMarius Strobl  *	@(#)if_le.c	8.2 (Berkeley) 11/16/93
72a7ee7a7dSMarius Strobl  */
73a7ee7a7dSMarius Strobl 
74a7ee7a7dSMarius Strobl #include <sys/cdefs.h>
75a7ee7a7dSMarius Strobl __FBSDID("$FreeBSD$");
76a7ee7a7dSMarius Strobl 
77a7ee7a7dSMarius Strobl #include <sys/param.h>
78a7ee7a7dSMarius Strobl #include <sys/bus.h>
79a7ee7a7dSMarius Strobl #include <sys/endian.h>
80a7ee7a7dSMarius Strobl #include <sys/lock.h>
81a7ee7a7dSMarius Strobl #include <sys/mbuf.h>
82a7ee7a7dSMarius Strobl #include <sys/mutex.h>
83a7ee7a7dSMarius Strobl #include <sys/socket.h>
84a7ee7a7dSMarius Strobl 
85a7ee7a7dSMarius Strobl #include <net/bpf.h>
86a7ee7a7dSMarius Strobl #include <net/ethernet.h>
87a7ee7a7dSMarius Strobl #include <net/if.h>
88a7ee7a7dSMarius Strobl #include <net/if_arp.h>
89a7ee7a7dSMarius Strobl #include <net/if_dl.h>
90a7ee7a7dSMarius Strobl #include <net/if_media.h>
91a7ee7a7dSMarius Strobl #include <net/if_var.h>
92a7ee7a7dSMarius Strobl 
9360c430f5SMarius Strobl #include <machine/bus.h>
9460c430f5SMarius Strobl 
95a7ee7a7dSMarius Strobl #include <dev/le/lancereg.h>
96a7ee7a7dSMarius Strobl #include <dev/le/lancevar.h>
97a7ee7a7dSMarius Strobl #include <dev/le/am7990reg.h>
98a7ee7a7dSMarius Strobl #include <dev/le/am7990var.h>
99a7ee7a7dSMarius Strobl 
100a7ee7a7dSMarius Strobl static void	am7990_meminit(struct lance_softc *);
101a7ee7a7dSMarius Strobl static void	am7990_rint(struct lance_softc *);
102a7ee7a7dSMarius Strobl static void	am7990_tint(struct lance_softc *);
103a7ee7a7dSMarius Strobl static void	am7990_start_locked(struct lance_softc *sc);
104a7ee7a7dSMarius Strobl 
105a7ee7a7dSMarius Strobl #ifdef LEDEBUG
106a7ee7a7dSMarius Strobl static void	am7990_recv_print(struct lance_softc *, int);
107a7ee7a7dSMarius Strobl static void	am7990_xmit_print(struct lance_softc *, int);
108a7ee7a7dSMarius Strobl #endif
109a7ee7a7dSMarius Strobl 
110a7ee7a7dSMarius Strobl int
111a7ee7a7dSMarius Strobl am7990_config(struct am7990_softc *sc, const char* name, int unit)
112a7ee7a7dSMarius Strobl {
113a7ee7a7dSMarius Strobl 	int error, mem;
114a7ee7a7dSMarius Strobl 
115a7ee7a7dSMarius Strobl 	sc->lsc.sc_meminit = am7990_meminit;
116a7ee7a7dSMarius Strobl 	sc->lsc.sc_start_locked = am7990_start_locked;
117a7ee7a7dSMarius Strobl 
118a7ee7a7dSMarius Strobl 	error = lance_config(&sc->lsc, name, unit);
119a7ee7a7dSMarius Strobl 	if (error != 0)
120a7ee7a7dSMarius Strobl 		return (error);
121a7ee7a7dSMarius Strobl 
122a7ee7a7dSMarius Strobl 	mem = 0;
123a7ee7a7dSMarius Strobl 	sc->lsc.sc_initaddr = mem;
124a7ee7a7dSMarius Strobl 	mem += sizeof(struct leinit);
125a7ee7a7dSMarius Strobl 	sc->lsc.sc_rmdaddr = mem;
126a7ee7a7dSMarius Strobl 	mem += sizeof(struct lermd) * sc->lsc.sc_nrbuf;
127a7ee7a7dSMarius Strobl 	sc->lsc.sc_tmdaddr = mem;
128a7ee7a7dSMarius Strobl 	mem += sizeof(struct letmd) * sc->lsc.sc_ntbuf;
129a7ee7a7dSMarius Strobl 	sc->lsc.sc_rbufaddr = mem;
130a7ee7a7dSMarius Strobl 	mem += LEBLEN * sc->lsc.sc_nrbuf;
131a7ee7a7dSMarius Strobl 	sc->lsc.sc_tbufaddr = mem;
132a7ee7a7dSMarius Strobl 	mem += LEBLEN * sc->lsc.sc_ntbuf;
133a7ee7a7dSMarius Strobl 
134a7ee7a7dSMarius Strobl 	if (mem > sc->lsc.sc_memsize)
135a7ee7a7dSMarius Strobl 		panic("%s: memsize", __func__);
136a7ee7a7dSMarius Strobl 
137a7ee7a7dSMarius Strobl 	lance_attach(&sc->lsc);
138a7ee7a7dSMarius Strobl 
139a7ee7a7dSMarius Strobl 	return (0);
140a7ee7a7dSMarius Strobl }
141a7ee7a7dSMarius Strobl 
142a7ee7a7dSMarius Strobl void
143a7ee7a7dSMarius Strobl am7990_detach(struct am7990_softc *sc)
144a7ee7a7dSMarius Strobl {
145a7ee7a7dSMarius Strobl 
146a7ee7a7dSMarius Strobl 	lance_detach(&sc->lsc);
147a7ee7a7dSMarius Strobl }
148a7ee7a7dSMarius Strobl 
149a7ee7a7dSMarius Strobl /*
150a7ee7a7dSMarius Strobl  * Set up the initialization block and the descriptor rings.
151a7ee7a7dSMarius Strobl  */
152a7ee7a7dSMarius Strobl static void
153a7ee7a7dSMarius Strobl am7990_meminit(struct lance_softc *sc)
154a7ee7a7dSMarius Strobl {
155a7ee7a7dSMarius Strobl 	struct ifnet *ifp = sc->sc_ifp;
156a7ee7a7dSMarius Strobl 	struct leinit init;
157a7ee7a7dSMarius Strobl 	struct lermd rmd;
158a7ee7a7dSMarius Strobl 	struct letmd tmd;
159a7ee7a7dSMarius Strobl 	u_long a;
160a7ee7a7dSMarius Strobl 	int bix;
161a7ee7a7dSMarius Strobl 
162a7ee7a7dSMarius Strobl 	LE_LOCK_ASSERT(sc, MA_OWNED);
163a7ee7a7dSMarius Strobl 
164a7ee7a7dSMarius Strobl 	if (ifp->if_flags & IFF_PROMISC)
165a7ee7a7dSMarius Strobl 		init.init_mode = LE_MODE_NORMAL | LE_MODE_PROM;
166a7ee7a7dSMarius Strobl 	else
167a7ee7a7dSMarius Strobl 		init.init_mode = LE_MODE_NORMAL;
168a7ee7a7dSMarius Strobl 
169a7ee7a7dSMarius Strobl 	init.init_padr[0] = (sc->sc_enaddr[1] << 8) | sc->sc_enaddr[0];
170a7ee7a7dSMarius Strobl 	init.init_padr[1] = (sc->sc_enaddr[3] << 8) | sc->sc_enaddr[2];
171a7ee7a7dSMarius Strobl 	init.init_padr[2] = (sc->sc_enaddr[5] << 8) | sc->sc_enaddr[4];
172a7ee7a7dSMarius Strobl 	lance_setladrf(sc, init.init_ladrf);
173a7ee7a7dSMarius Strobl 
174a7ee7a7dSMarius Strobl 	sc->sc_last_rd = 0;
175a7ee7a7dSMarius Strobl 	sc->sc_first_td = sc->sc_last_td = sc->sc_no_td = 0;
176a7ee7a7dSMarius Strobl 
177a7ee7a7dSMarius Strobl 	a = sc->sc_addr + LE_RMDADDR(sc, 0);
178a7ee7a7dSMarius Strobl 	init.init_rdra = a;
179a7ee7a7dSMarius Strobl 	init.init_rlen = (a >> 16) | ((ffs(sc->sc_nrbuf) - 1) << 13);
180a7ee7a7dSMarius Strobl 
181a7ee7a7dSMarius Strobl 	a = sc->sc_addr + LE_TMDADDR(sc, 0);
182a7ee7a7dSMarius Strobl 	init.init_tdra = a;
183a7ee7a7dSMarius Strobl 	init.init_tlen = (a >> 16) | ((ffs(sc->sc_ntbuf) - 1) << 13);
184a7ee7a7dSMarius Strobl 
185a7ee7a7dSMarius Strobl 	(*sc->sc_copytodesc)(sc, &init, LE_INITADDR(sc), sizeof(init));
186a7ee7a7dSMarius Strobl 
187a7ee7a7dSMarius Strobl 	/*
188a7ee7a7dSMarius Strobl 	 * Set up receive ring descriptors.
189a7ee7a7dSMarius Strobl 	 */
190a7ee7a7dSMarius Strobl 	for (bix = 0; bix < sc->sc_nrbuf; bix++) {
191a7ee7a7dSMarius Strobl 		a = sc->sc_addr + LE_RBUFADDR(sc, bix);
192a7ee7a7dSMarius Strobl 		rmd.rmd0 = a;
193a7ee7a7dSMarius Strobl 		rmd.rmd1_hadr = a >> 16;
194a7ee7a7dSMarius Strobl 		rmd.rmd1_bits = LE_R1_OWN;
195a7ee7a7dSMarius Strobl 		rmd.rmd2 = -LEBLEN | LE_XMD2_ONES;
196a7ee7a7dSMarius Strobl 		rmd.rmd3 = 0;
197a7ee7a7dSMarius Strobl 		(*sc->sc_copytodesc)(sc, &rmd, LE_RMDADDR(sc, bix),
198a7ee7a7dSMarius Strobl 		    sizeof(rmd));
199a7ee7a7dSMarius Strobl 	}
200a7ee7a7dSMarius Strobl 
201a7ee7a7dSMarius Strobl 	/*
202a7ee7a7dSMarius Strobl 	 * Set up transmit ring descriptors.
203a7ee7a7dSMarius Strobl 	 */
204a7ee7a7dSMarius Strobl 	for (bix = 0; bix < sc->sc_ntbuf; bix++) {
205a7ee7a7dSMarius Strobl 		a = sc->sc_addr + LE_TBUFADDR(sc, bix);
206a7ee7a7dSMarius Strobl 		tmd.tmd0 = a;
207a7ee7a7dSMarius Strobl 		tmd.tmd1_hadr = a >> 16;
208a7ee7a7dSMarius Strobl 		tmd.tmd1_bits = 0;
209a7ee7a7dSMarius Strobl 		tmd.tmd2 = LE_XMD2_ONES;
210a7ee7a7dSMarius Strobl 		tmd.tmd3 = 0;
211a7ee7a7dSMarius Strobl 		(*sc->sc_copytodesc)(sc, &tmd, LE_TMDADDR(sc, bix),
212a7ee7a7dSMarius Strobl 		    sizeof(tmd));
213a7ee7a7dSMarius Strobl 	}
214a7ee7a7dSMarius Strobl }
215a7ee7a7dSMarius Strobl 
216a7ee7a7dSMarius Strobl static void
217a7ee7a7dSMarius Strobl am7990_rint(struct lance_softc *sc)
218a7ee7a7dSMarius Strobl {
219a7ee7a7dSMarius Strobl 	struct ifnet *ifp = sc->sc_ifp;
22060c430f5SMarius Strobl 	struct mbuf *m;
221a7ee7a7dSMarius Strobl 	struct lermd rmd;
222a7ee7a7dSMarius Strobl 	int bix, rp;
22360c430f5SMarius Strobl #if defined(LANCE_REVC_BUG)
22460c430f5SMarius Strobl 	struct ether_header *eh;
22560c430f5SMarius Strobl 	/* Make sure this is short-aligned, for ether_cmp(). */
22660c430f5SMarius Strobl 	static uint16_t bcast_enaddr[3] = { ~0, ~0, ~0 };
22760c430f5SMarius Strobl #endif
228a7ee7a7dSMarius Strobl 
229a7ee7a7dSMarius Strobl 	bix = sc->sc_last_rd;
230a7ee7a7dSMarius Strobl 
231a7ee7a7dSMarius Strobl 	/* Process all buffers with valid data. */
232a7ee7a7dSMarius Strobl 	for (;;) {
233a7ee7a7dSMarius Strobl 		rp = LE_RMDADDR(sc, bix);
234a7ee7a7dSMarius Strobl 		(*sc->sc_copyfromdesc)(sc, &rmd, rp, sizeof(rmd));
235a7ee7a7dSMarius Strobl 
236a7ee7a7dSMarius Strobl 		if (rmd.rmd1_bits & LE_R1_OWN)
237a7ee7a7dSMarius Strobl 			break;
238a7ee7a7dSMarius Strobl 
23960c430f5SMarius Strobl 		m = NULL;
24060c430f5SMarius Strobl 		if ((rmd.rmd1_bits & (LE_R1_ERR | LE_R1_STP | LE_R1_ENP)) !=
24160c430f5SMarius Strobl 		    (LE_R1_STP | LE_R1_ENP)) {
242a7ee7a7dSMarius Strobl 			if (rmd.rmd1_bits & LE_R1_ERR) {
243a7ee7a7dSMarius Strobl #ifdef LEDEBUG
24460c430f5SMarius Strobl 				if (rmd.rmd1_bits & LE_R1_ENP) {
245a7ee7a7dSMarius Strobl 					if ((rmd.rmd1_bits & LE_R1_OFLO) == 0) {
246a7ee7a7dSMarius Strobl 						if (rmd.rmd1_bits & LE_R1_FRAM)
247a7ee7a7dSMarius Strobl 							if_printf(ifp,
248a7ee7a7dSMarius Strobl 							    "framing error\n");
249a7ee7a7dSMarius Strobl 						if (rmd.rmd1_bits & LE_R1_CRC)
250a7ee7a7dSMarius Strobl 							if_printf(ifp,
251a7ee7a7dSMarius Strobl 							    "crc mismatch\n");
252a7ee7a7dSMarius Strobl 					}
25360c430f5SMarius Strobl 				} else
254a7ee7a7dSMarius Strobl 					if (rmd.rmd1_bits & LE_R1_OFLO)
255a7ee7a7dSMarius Strobl 						if_printf(ifp, "overflow\n");
25660c430f5SMarius Strobl #endif
257a7ee7a7dSMarius Strobl 				if (rmd.rmd1_bits & LE_R1_BUFF)
25860c430f5SMarius Strobl 					if_printf(ifp,
25960c430f5SMarius Strobl 					    "receive buffer error\n");
260a7ee7a7dSMarius Strobl 			} else if ((rmd.rmd1_bits & (LE_R1_STP | LE_R1_ENP)) !=
26160c430f5SMarius Strobl 			    (LE_R1_STP | LE_R1_ENP))
262a7ee7a7dSMarius Strobl 				if_printf(ifp, "dropping chained buffer\n");
263a7ee7a7dSMarius Strobl 		} else {
264a7ee7a7dSMarius Strobl #ifdef LEDEBUG
265a7ee7a7dSMarius Strobl 			if (sc->sc_flags & LE_DEBUG)
26660c430f5SMarius Strobl 				am7990_recv_print(sc, bix);
267a7ee7a7dSMarius Strobl #endif
26860c430f5SMarius Strobl 			/* Pull the packet off the interface. */
26960c430f5SMarius Strobl 			m = lance_get(sc, LE_RBUFADDR(sc, bix),
270a7ee7a7dSMarius Strobl 			    (int)rmd.rmd3 - ETHER_CRC_LEN);
271a7ee7a7dSMarius Strobl 		}
272a7ee7a7dSMarius Strobl 
273a7ee7a7dSMarius Strobl 		rmd.rmd1_bits = LE_R1_OWN;
274a7ee7a7dSMarius Strobl 		rmd.rmd2 = -LEBLEN | LE_XMD2_ONES;
275a7ee7a7dSMarius Strobl 		rmd.rmd3 = 0;
276a7ee7a7dSMarius Strobl 		(*sc->sc_copytodesc)(sc, &rmd, rp, sizeof(rmd));
277a7ee7a7dSMarius Strobl 
278a7ee7a7dSMarius Strobl 		if (++bix == sc->sc_nrbuf)
279a7ee7a7dSMarius Strobl 			bix = 0;
28060c430f5SMarius Strobl 
28160c430f5SMarius Strobl 		if (m != NULL) {
28260c430f5SMarius Strobl 			ifp->if_ipackets++;
28360c430f5SMarius Strobl 
28460c430f5SMarius Strobl #ifdef LANCE_REVC_BUG
28560c430f5SMarius Strobl 			/*
28660c430f5SMarius Strobl 			 * The old LANCE (Rev. C) chips have a bug which
28760c430f5SMarius Strobl 			 * causes garbage to be inserted in front of the
28860c430f5SMarius Strobl 			 * received packet. The workaround is to ignore
28960c430f5SMarius Strobl 			 * packets with an invalid destination address
29060c430f5SMarius Strobl 			 * (garbage will usually not match).
29160c430f5SMarius Strobl 			 * Of course, this precludes multicast support...
29260c430f5SMarius Strobl 			 */
29360c430f5SMarius Strobl 			eh = mtod(m, struct ether_header *);
29460c430f5SMarius Strobl 			if (ether_cmp(eh->ether_dhost, sc->sc_enaddr) &&
29560c430f5SMarius Strobl 			    ether_cmp(eh->ether_dhost, bcast_enaddr)) {
29660c430f5SMarius Strobl 				m_freem(m);
29760c430f5SMarius Strobl 				continue;
29860c430f5SMarius Strobl 			}
29960c430f5SMarius Strobl #endif
30060c430f5SMarius Strobl 
30160c430f5SMarius Strobl 			/* Pass the packet up. */
30260c430f5SMarius Strobl 			LE_UNLOCK(sc);
30360c430f5SMarius Strobl 			(*ifp->if_input)(ifp, m);
30460c430f5SMarius Strobl 			LE_LOCK(sc);
30560c430f5SMarius Strobl 		} else
30660c430f5SMarius Strobl 			ifp->if_ierrors++;
307a7ee7a7dSMarius Strobl 	}
308a7ee7a7dSMarius Strobl 
309a7ee7a7dSMarius Strobl 	sc->sc_last_rd = bix;
310a7ee7a7dSMarius Strobl }
311a7ee7a7dSMarius Strobl 
312a7ee7a7dSMarius Strobl static void
313a7ee7a7dSMarius Strobl am7990_tint(struct lance_softc *sc)
314a7ee7a7dSMarius Strobl {
315a7ee7a7dSMarius Strobl 	struct ifnet *ifp = sc->sc_ifp;
316a7ee7a7dSMarius Strobl 	struct letmd tmd;
317a7ee7a7dSMarius Strobl 	int bix;
318a7ee7a7dSMarius Strobl 
319a7ee7a7dSMarius Strobl 	bix = sc->sc_first_td;
320a7ee7a7dSMarius Strobl 
321a7ee7a7dSMarius Strobl 	for (;;) {
322a7ee7a7dSMarius Strobl 		if (sc->sc_no_td <= 0)
323a7ee7a7dSMarius Strobl 			break;
324a7ee7a7dSMarius Strobl 
325a7ee7a7dSMarius Strobl 		(*sc->sc_copyfromdesc)(sc, &tmd, LE_TMDADDR(sc, bix),
326a7ee7a7dSMarius Strobl 		    sizeof(tmd));
327a7ee7a7dSMarius Strobl 
328a7ee7a7dSMarius Strobl #ifdef LEDEBUG
329a7ee7a7dSMarius Strobl 		if (sc->sc_flags & LE_DEBUG)
330a7ee7a7dSMarius Strobl 			if_printf(ifp, "trans tmd: "
331a7ee7a7dSMarius Strobl 			    "ladr %04x, hadr %02x, flags %02x, "
332a7ee7a7dSMarius Strobl 			    "bcnt %04x, mcnt %04x\n",
333a7ee7a7dSMarius Strobl 			    tmd.tmd0, tmd.tmd1_hadr, tmd.tmd1_bits,
334a7ee7a7dSMarius Strobl 			    tmd.tmd2, tmd.tmd3);
335a7ee7a7dSMarius Strobl #endif
336a7ee7a7dSMarius Strobl 
337a7ee7a7dSMarius Strobl 		if (tmd.tmd1_bits & LE_T1_OWN)
338a7ee7a7dSMarius Strobl 			break;
339a7ee7a7dSMarius Strobl 
340a7ee7a7dSMarius Strobl 		ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
341a7ee7a7dSMarius Strobl 
342a7ee7a7dSMarius Strobl 		if (tmd.tmd1_bits & LE_T1_ERR) {
343a7ee7a7dSMarius Strobl 			if (tmd.tmd3 & LE_T3_BUFF)
344a7ee7a7dSMarius Strobl 				if_printf(ifp, "transmit buffer error\n");
345a7ee7a7dSMarius Strobl 			else if (tmd.tmd3 & LE_T3_UFLO)
346a7ee7a7dSMarius Strobl 				if_printf(ifp, "underflow\n");
347a7ee7a7dSMarius Strobl 			if (tmd.tmd3 & (LE_T3_BUFF | LE_T3_UFLO)) {
348a7ee7a7dSMarius Strobl 				lance_init_locked(sc);
349a7ee7a7dSMarius Strobl 				return;
350a7ee7a7dSMarius Strobl 			}
351a7ee7a7dSMarius Strobl 			if (tmd.tmd3 & LE_T3_LCAR) {
352a7ee7a7dSMarius Strobl 				if (sc->sc_flags & LE_CARRIER)
353a7ee7a7dSMarius Strobl 					if_link_state_change(ifp,
354a7ee7a7dSMarius Strobl 					    LINK_STATE_DOWN);
355a7ee7a7dSMarius Strobl 				sc->sc_flags &= ~LE_CARRIER;
356a7ee7a7dSMarius Strobl 				if (sc->sc_nocarrier)
357a7ee7a7dSMarius Strobl 					(*sc->sc_nocarrier)(sc);
358a7ee7a7dSMarius Strobl 				else
359a7ee7a7dSMarius Strobl 					if_printf(ifp, "lost carrier\n");
360a7ee7a7dSMarius Strobl 			}
361a7ee7a7dSMarius Strobl 			if (tmd.tmd3 & LE_T3_LCOL)
362a7ee7a7dSMarius Strobl 				ifp->if_collisions++;
363a7ee7a7dSMarius Strobl 			if (tmd.tmd3 & LE_T3_RTRY) {
364a7ee7a7dSMarius Strobl #ifdef LEDEBUG
365a7ee7a7dSMarius Strobl 				if_printf(ifp, "excessive collisions, tdr %d\n",
366a7ee7a7dSMarius Strobl 				    tmd.tmd3 & LE_T3_TDR_MASK);
367a7ee7a7dSMarius Strobl #endif
368a7ee7a7dSMarius Strobl 				ifp->if_collisions += 16;
369a7ee7a7dSMarius Strobl 			}
370a7ee7a7dSMarius Strobl 			ifp->if_oerrors++;
371a7ee7a7dSMarius Strobl 		} else {
372a7ee7a7dSMarius Strobl 			if (tmd.tmd1_bits & LE_T1_ONE)
373a7ee7a7dSMarius Strobl 				ifp->if_collisions++;
374a7ee7a7dSMarius Strobl 			else if (tmd.tmd1_bits & LE_T1_MORE)
375a7ee7a7dSMarius Strobl 				/* Real number is unknown. */
376a7ee7a7dSMarius Strobl 				ifp->if_collisions += 2;
377a7ee7a7dSMarius Strobl 			ifp->if_opackets++;
378a7ee7a7dSMarius Strobl 		}
379a7ee7a7dSMarius Strobl 
380a7ee7a7dSMarius Strobl 		if (++bix == sc->sc_ntbuf)
381a7ee7a7dSMarius Strobl 			bix = 0;
382a7ee7a7dSMarius Strobl 
383a7ee7a7dSMarius Strobl 		--sc->sc_no_td;
384a7ee7a7dSMarius Strobl 	}
385a7ee7a7dSMarius Strobl 
386a7ee7a7dSMarius Strobl 	sc->sc_first_td = bix;
387a7ee7a7dSMarius Strobl 
3880c05ab41SMarius Strobl 	ifp->if_timer = sc->sc_no_td > 0 ? 5 : 0;
389a7ee7a7dSMarius Strobl }
390a7ee7a7dSMarius Strobl 
391a7ee7a7dSMarius Strobl /*
392a7ee7a7dSMarius Strobl  * Controller interrupt
393a7ee7a7dSMarius Strobl  */
394a7ee7a7dSMarius Strobl void
395a7ee7a7dSMarius Strobl am7990_intr(void *arg)
396a7ee7a7dSMarius Strobl {
397a7ee7a7dSMarius Strobl 	struct lance_softc *sc = arg;
398a7ee7a7dSMarius Strobl 	struct ifnet *ifp = sc->sc_ifp;
399a7ee7a7dSMarius Strobl 	uint16_t isr;
400a7ee7a7dSMarius Strobl 
401a7ee7a7dSMarius Strobl 	LE_LOCK(sc);
402a7ee7a7dSMarius Strobl 
403a7ee7a7dSMarius Strobl 	if (sc->sc_hwintr && (*sc->sc_hwintr)(sc) == -1) {
404a7ee7a7dSMarius Strobl 		ifp->if_ierrors++;
405a7ee7a7dSMarius Strobl 		lance_init_locked(sc);
406a7ee7a7dSMarius Strobl 		LE_UNLOCK(sc);
407a7ee7a7dSMarius Strobl 		return;
408a7ee7a7dSMarius Strobl 	}
409a7ee7a7dSMarius Strobl 
410a7ee7a7dSMarius Strobl 	isr = (*sc->sc_rdcsr)(sc, LE_CSR0);
411a7ee7a7dSMarius Strobl #if defined(LEDEBUG) && LEDEBUG > 1
412a7ee7a7dSMarius Strobl 	if (sc->sc_flags & LE_DEBUG)
413a7ee7a7dSMarius Strobl 		if_printf(ifp, "%s: entering with isr=%04x\n", __func__, isr);
414a7ee7a7dSMarius Strobl #endif
415a7ee7a7dSMarius Strobl 	if ((isr & LE_C0_INTR) == 0) {
416a7ee7a7dSMarius Strobl 		LE_UNLOCK(sc);
417a7ee7a7dSMarius Strobl 		return;
418a7ee7a7dSMarius Strobl 	}
419a7ee7a7dSMarius Strobl 
4200c05ab41SMarius Strobl 	/*
4210c05ab41SMarius Strobl 	 * Clear interrupt source flags and turn off interrupts. If we
4220c05ab41SMarius Strobl 	 * don't clear these flags before processing their sources we
42360c430f5SMarius Strobl 	 * could completely miss some interrupt events as the NIC can
42460c430f5SMarius Strobl 	 * change these flags while we're in this handler. We turn off
42560c430f5SMarius Strobl 	 * interrupts so we don't get another RX interrupt while still
42660c430f5SMarius Strobl 	 * processing the previous one in ifp->if_input() with the
42760c430f5SMarius Strobl 	 * driver lock dropped.
4280c05ab41SMarius Strobl 	 */
4290c05ab41SMarius Strobl 	(*sc->sc_wrcsr)(sc, LE_CSR0, isr & ~(LE_C0_INEA | LE_C0_TDMD |
4300c05ab41SMarius Strobl 	    LE_C0_STOP | LE_C0_STRT | LE_C0_INIT));
4310c05ab41SMarius Strobl 
432a7ee7a7dSMarius Strobl 	if (isr & LE_C0_ERR) {
433a7ee7a7dSMarius Strobl 		if (isr & LE_C0_BABL) {
434a7ee7a7dSMarius Strobl #ifdef LEDEBUG
435a7ee7a7dSMarius Strobl 			if_printf(ifp, "babble\n");
436a7ee7a7dSMarius Strobl #endif
437a7ee7a7dSMarius Strobl 			ifp->if_oerrors++;
438a7ee7a7dSMarius Strobl 		}
439a7ee7a7dSMarius Strobl #if 0
440a7ee7a7dSMarius Strobl 		if (isr & LE_C0_CERR) {
441a7ee7a7dSMarius Strobl 			if_printf(ifp, "collision error\n");
442a7ee7a7dSMarius Strobl 			ifp->if_collisions++;
443a7ee7a7dSMarius Strobl 		}
444a7ee7a7dSMarius Strobl #endif
445a7ee7a7dSMarius Strobl 		if (isr & LE_C0_MISS) {
446a7ee7a7dSMarius Strobl #ifdef LEDEBUG
447a7ee7a7dSMarius Strobl 			if_printf(ifp, "missed packet\n");
448a7ee7a7dSMarius Strobl #endif
449a7ee7a7dSMarius Strobl 			ifp->if_ierrors++;
450a7ee7a7dSMarius Strobl 		}
451a7ee7a7dSMarius Strobl 		if (isr & LE_C0_MERR) {
452a7ee7a7dSMarius Strobl 			if_printf(ifp, "memory error\n");
453a7ee7a7dSMarius Strobl 			lance_init_locked(sc);
454a7ee7a7dSMarius Strobl 			LE_UNLOCK(sc);
455a7ee7a7dSMarius Strobl 			return;
456a7ee7a7dSMarius Strobl 		}
457a7ee7a7dSMarius Strobl 	}
458a7ee7a7dSMarius Strobl 
459a7ee7a7dSMarius Strobl 	if ((isr & LE_C0_RXON) == 0) {
460a7ee7a7dSMarius Strobl 		if_printf(ifp, "receiver disabled\n");
461a7ee7a7dSMarius Strobl 		ifp->if_ierrors++;
462a7ee7a7dSMarius Strobl 		lance_init_locked(sc);
463a7ee7a7dSMarius Strobl 		LE_UNLOCK(sc);
464a7ee7a7dSMarius Strobl 		return;
465a7ee7a7dSMarius Strobl 	}
466a7ee7a7dSMarius Strobl 	if ((isr & LE_C0_TXON) == 0) {
467a7ee7a7dSMarius Strobl 		if_printf(ifp, "transmitter disabled\n");
468a7ee7a7dSMarius Strobl 		ifp->if_oerrors++;
469a7ee7a7dSMarius Strobl 		lance_init_locked(sc);
470a7ee7a7dSMarius Strobl 		LE_UNLOCK(sc);
471a7ee7a7dSMarius Strobl 		return;
472a7ee7a7dSMarius Strobl 	}
473a7ee7a7dSMarius Strobl 
474a7ee7a7dSMarius Strobl 	/*
475a7ee7a7dSMarius Strobl 	 * Pretend we have carrier; if we don't this will be cleared shortly.
476a7ee7a7dSMarius Strobl 	 */
477a7ee7a7dSMarius Strobl 	if (!(sc->sc_flags & LE_CARRIER))
478a7ee7a7dSMarius Strobl 		if_link_state_change(ifp, LINK_STATE_UP);
479a7ee7a7dSMarius Strobl 	sc->sc_flags |= LE_CARRIER;
480a7ee7a7dSMarius Strobl 
481a7ee7a7dSMarius Strobl 	if (isr & LE_C0_RINT)
482a7ee7a7dSMarius Strobl 		am7990_rint(sc);
483a7ee7a7dSMarius Strobl 	if (isr & LE_C0_TINT)
484a7ee7a7dSMarius Strobl 		am7990_tint(sc);
485a7ee7a7dSMarius Strobl 
4860c05ab41SMarius Strobl 	/* Enable interrupts again. */
4870c05ab41SMarius Strobl 	(*sc->sc_wrcsr)(sc, LE_CSR0, LE_C0_INEA);
4880c05ab41SMarius Strobl 
4890c05ab41SMarius Strobl 	if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
4900c05ab41SMarius Strobl 		am7990_start_locked(sc);
491a7ee7a7dSMarius Strobl 
492a7ee7a7dSMarius Strobl 	LE_UNLOCK(sc);
493a7ee7a7dSMarius Strobl }
494a7ee7a7dSMarius Strobl 
495a7ee7a7dSMarius Strobl /*
496a7ee7a7dSMarius Strobl  * Set up output on interface.
497a7ee7a7dSMarius Strobl  * Get another datagram to send off of the interface queue, and map it to the
498a7ee7a7dSMarius Strobl  * interface before starting the output.
499a7ee7a7dSMarius Strobl  */
500a7ee7a7dSMarius Strobl static void
501a7ee7a7dSMarius Strobl am7990_start_locked(struct lance_softc *sc)
502a7ee7a7dSMarius Strobl {
503a7ee7a7dSMarius Strobl 	struct ifnet *ifp = sc->sc_ifp;
504a7ee7a7dSMarius Strobl 	struct letmd tmd;
505a7ee7a7dSMarius Strobl 	struct mbuf *m;
5060c05ab41SMarius Strobl 	int bix, enq, len, rp;
507a7ee7a7dSMarius Strobl 
508a7ee7a7dSMarius Strobl 	LE_LOCK_ASSERT(sc, MA_OWNED);
509a7ee7a7dSMarius Strobl 
510a7ee7a7dSMarius Strobl 	if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) !=
511a7ee7a7dSMarius Strobl 	    IFF_DRV_RUNNING)
512a7ee7a7dSMarius Strobl 		return;
513a7ee7a7dSMarius Strobl 
514a7ee7a7dSMarius Strobl 	bix = sc->sc_last_td;
5150c05ab41SMarius Strobl 	enq = 0;
516a7ee7a7dSMarius Strobl 
517a7ee7a7dSMarius Strobl 	for (; sc->sc_no_td < sc->sc_ntbuf &&
518a7ee7a7dSMarius Strobl 	    !IFQ_DRV_IS_EMPTY(&ifp->if_snd);) {
519a7ee7a7dSMarius Strobl 		rp = LE_TMDADDR(sc, bix);
520a7ee7a7dSMarius Strobl 		(*sc->sc_copyfromdesc)(sc, &tmd, rp, sizeof(tmd));
521a7ee7a7dSMarius Strobl 
522a7ee7a7dSMarius Strobl 		if (tmd.tmd1_bits & LE_T1_OWN) {
523a7ee7a7dSMarius Strobl 			ifp->if_drv_flags |= IFF_DRV_OACTIVE;
524a7ee7a7dSMarius Strobl 			if_printf(ifp,
525a7ee7a7dSMarius Strobl 			    "missing buffer, no_td = %d, last_td = %d\n",
526a7ee7a7dSMarius Strobl 			    sc->sc_no_td, sc->sc_last_td);
527a7ee7a7dSMarius Strobl 		}
528a7ee7a7dSMarius Strobl 
529a7ee7a7dSMarius Strobl 		IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
530a7ee7a7dSMarius Strobl 		if (m == 0)
531a7ee7a7dSMarius Strobl 			break;
532a7ee7a7dSMarius Strobl 
533a7ee7a7dSMarius Strobl 		/*
534a7ee7a7dSMarius Strobl 		 * If BPF is listening on this interface, let it see the packet
535a7ee7a7dSMarius Strobl 		 * before we commit it to the wire.
536a7ee7a7dSMarius Strobl 		 */
537a7ee7a7dSMarius Strobl 		BPF_MTAP(ifp, m);
538a7ee7a7dSMarius Strobl 
539a7ee7a7dSMarius Strobl 		/*
540a7ee7a7dSMarius Strobl 		 * Copy the mbuf chain into the transmit buffer.
541a7ee7a7dSMarius Strobl 		 */
542a7ee7a7dSMarius Strobl 		len = lance_put(sc, LE_TBUFADDR(sc, bix), m);
543a7ee7a7dSMarius Strobl 
544a7ee7a7dSMarius Strobl #ifdef LEDEBUG
545a7ee7a7dSMarius Strobl 		if (len > ETHERMTU + ETHER_HDR_LEN)
546a7ee7a7dSMarius Strobl 			if_printf(ifp, "packet length %d\n", len);
547a7ee7a7dSMarius Strobl #endif
548a7ee7a7dSMarius Strobl 
549a7ee7a7dSMarius Strobl 		/*
550a7ee7a7dSMarius Strobl 		 * Init transmit registers, and set transmit start flag.
551a7ee7a7dSMarius Strobl 		 */
552a7ee7a7dSMarius Strobl 		tmd.tmd1_bits = LE_T1_OWN | LE_T1_STP | LE_T1_ENP;
553a7ee7a7dSMarius Strobl 		tmd.tmd2 = -len | LE_XMD2_ONES;
554a7ee7a7dSMarius Strobl 		tmd.tmd3 = 0;
555a7ee7a7dSMarius Strobl 
556a7ee7a7dSMarius Strobl 		(*sc->sc_copytodesc)(sc, &tmd, rp, sizeof(tmd));
557a7ee7a7dSMarius Strobl 
558a7ee7a7dSMarius Strobl #ifdef LEDEBUG
559a7ee7a7dSMarius Strobl 		if (sc->sc_flags & LE_DEBUG)
56060c430f5SMarius Strobl 			am7990_xmit_print(sc, bix);
561a7ee7a7dSMarius Strobl #endif
562a7ee7a7dSMarius Strobl 
563a7ee7a7dSMarius Strobl 		(*sc->sc_wrcsr)(sc, LE_CSR0, LE_C0_INEA | LE_C0_TDMD);
5640c05ab41SMarius Strobl 		enq++;
565a7ee7a7dSMarius Strobl 
566a7ee7a7dSMarius Strobl 		if (++bix == sc->sc_ntbuf)
567a7ee7a7dSMarius Strobl 			bix = 0;
568a7ee7a7dSMarius Strobl 
569a7ee7a7dSMarius Strobl 		if (++sc->sc_no_td == sc->sc_ntbuf) {
570a7ee7a7dSMarius Strobl 			ifp->if_drv_flags |= IFF_DRV_OACTIVE;
571a7ee7a7dSMarius Strobl 			break;
572a7ee7a7dSMarius Strobl 		}
573a7ee7a7dSMarius Strobl 	}
574a7ee7a7dSMarius Strobl 
575a7ee7a7dSMarius Strobl 	sc->sc_last_td = bix;
5760c05ab41SMarius Strobl 
5770c05ab41SMarius Strobl 	if (enq > 0)
5780c05ab41SMarius Strobl 		ifp->if_timer = 5;
579a7ee7a7dSMarius Strobl }
580a7ee7a7dSMarius Strobl 
581a7ee7a7dSMarius Strobl #ifdef LEDEBUG
582a7ee7a7dSMarius Strobl static void
583a7ee7a7dSMarius Strobl am7990_recv_print(struct lance_softc *sc, int no)
584a7ee7a7dSMarius Strobl {
585a7ee7a7dSMarius Strobl 	struct ifnet *ifp = sc->sc_ifp;
586a7ee7a7dSMarius Strobl 	struct ether_header eh;
587a7ee7a7dSMarius Strobl 	struct lermd rmd;
588a7ee7a7dSMarius Strobl 	uint16_t len;
589a7ee7a7dSMarius Strobl 
590a7ee7a7dSMarius Strobl 	(*sc->sc_copyfromdesc)(sc, &rmd, LE_RMDADDR(sc, no), sizeof(rmd));
591a7ee7a7dSMarius Strobl 	len = rmd.rmd3;
592a7ee7a7dSMarius Strobl 	if_printf(ifp, "receive buffer %d, len = %d\n", no, len);
593a7ee7a7dSMarius Strobl 	if_printf(ifp, "status %04x\n", (*sc->sc_rdcsr)(sc, LE_CSR0));
594a7ee7a7dSMarius Strobl 	if_printf(ifp,
595a7ee7a7dSMarius Strobl 	    "ladr %04x, hadr %02x, flags %02x, bcnt %04x, mcnt %04x\n",
596a7ee7a7dSMarius Strobl 	    rmd.rmd0, rmd.rmd1_hadr, rmd.rmd1_bits, rmd.rmd2, rmd.rmd3);
597a7ee7a7dSMarius Strobl 	if (len - ETHER_CRC_LEN >= sizeof(eh)) {
598a7ee7a7dSMarius Strobl 		(*sc->sc_copyfrombuf)(sc, &eh, LE_RBUFADDR(sc, no), sizeof(eh));
599a7ee7a7dSMarius Strobl 		if_printf(ifp, "dst %s", ether_sprintf(eh.ether_dhost));
600a7ee7a7dSMarius Strobl 		printf(" src %s type %04x\n", ether_sprintf(eh.ether_shost),
601a7ee7a7dSMarius Strobl 		    ntohs(eh.ether_type));
602a7ee7a7dSMarius Strobl 	}
603a7ee7a7dSMarius Strobl }
604a7ee7a7dSMarius Strobl 
605a7ee7a7dSMarius Strobl static void
606a7ee7a7dSMarius Strobl am7990_xmit_print(struct lance_softc *sc, int no)
607a7ee7a7dSMarius Strobl {
608a7ee7a7dSMarius Strobl 	struct ifnet *ifp = sc->sc_ifp;
609a7ee7a7dSMarius Strobl 	struct ether_header eh;
610a7ee7a7dSMarius Strobl 	struct letmd tmd;
611a7ee7a7dSMarius Strobl 	uint16_t len;
612a7ee7a7dSMarius Strobl 
613a7ee7a7dSMarius Strobl 	(*sc->sc_copyfromdesc)(sc, &tmd, LE_TMDADDR(sc, no), sizeof(tmd));
614a7ee7a7dSMarius Strobl 	len = -tmd.tmd2;
615a7ee7a7dSMarius Strobl 	if_printf(ifp, "transmit buffer %d, len = %d\n", no, len);
616a7ee7a7dSMarius Strobl 	if_printf(ifp, "status %04x\n", (*sc->sc_rdcsr)(sc, LE_CSR0));
617a7ee7a7dSMarius Strobl 	if_printf(ifp,
618a7ee7a7dSMarius Strobl 	    "ladr %04x, hadr %02x, flags %02x, bcnt %04x, mcnt %04x\n",
619a7ee7a7dSMarius Strobl 	    tmd.tmd0, tmd.tmd1_hadr, tmd.tmd1_bits, tmd.tmd2, tmd.tmd3);
620a7ee7a7dSMarius Strobl 	if (len >= sizeof(eh)) {
621a7ee7a7dSMarius Strobl 		(*sc->sc_copyfrombuf)(sc, &eh, LE_TBUFADDR(sc, no), sizeof(eh));
622a7ee7a7dSMarius Strobl 		if_printf(ifp, "dst %s", ether_sprintf(eh.ether_dhost));
623a7ee7a7dSMarius Strobl 		printf(" src %s type %04x\n", ether_sprintf(eh.ether_shost),
624a7ee7a7dSMarius Strobl 		    ntohs(eh.ether_type));
625a7ee7a7dSMarius Strobl 	}
626a7ee7a7dSMarius Strobl }
627a7ee7a7dSMarius Strobl #endif /* LEDEBUG */
628