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