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