xref: /netbsd-src/sys/dev/ic/am7990.c (revision eb7c1594f145c931049e1fd9eb056a5987e87e59)
1 /*	$NetBSD: am7990.c,v 1.65 2003/08/07 16:30:58 agc 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 __KERNEL_RCSID(0, "$NetBSD: am7990.c,v 1.65 2003/08/07 16:30:58 agc Exp $");
76 
77 #include "bpfilter.h"
78 #include "rnd.h"
79 
80 #include <sys/param.h>
81 #include <sys/systm.h>
82 #include <sys/mbuf.h>
83 #include <sys/syslog.h>
84 #include <sys/socket.h>
85 #include <sys/device.h>
86 #include <sys/malloc.h>
87 #include <sys/ioctl.h>
88 #include <sys/errno.h>
89 #if NRND > 0
90 #include <sys/rnd.h>
91 #endif
92 
93 #include <net/if.h>
94 #include <net/if_dl.h>
95 #include <net/if_ether.h>
96 #include <net/if_media.h>
97 
98 #if NBPFILTER > 0
99 #include <net/bpf.h>
100 #include <net/bpfdesc.h>
101 #endif
102 
103 #include <dev/ic/lancereg.h>
104 #include <dev/ic/lancevar.h>
105 #include <dev/ic/am7990reg.h>
106 #include <dev/ic/am7990var.h>
107 
108 void am7990_meminit __P((struct lance_softc *));
109 void am7990_start __P((struct ifnet *));
110 
111 #if defined(_KERNEL_OPT)
112 #include "opt_ddb.h"
113 #endif
114 
115 #ifdef DDB
116 #define	integrate
117 #define hide
118 #else
119 #define	integrate	static __inline
120 #define hide		static
121 #endif
122 
123 integrate void am7990_rint __P((struct lance_softc *));
124 integrate void am7990_tint __P((struct lance_softc *));
125 
126 #ifdef LEDEBUG
127 void am7990_recv_print __P((struct lance_softc *, int));
128 void am7990_xmit_print __P((struct lance_softc *, int));
129 #endif
130 
131 #define	ifp	(&sc->sc_ethercom.ec_if)
132 
133 void
134 am7990_config(sc)
135 	struct am7990_softc *sc;
136 {
137 	int mem, i;
138 
139 	sc->lsc.sc_meminit = am7990_meminit;
140 	sc->lsc.sc_start = am7990_start;
141 
142 	lance_config(&sc->lsc);
143 
144 	mem = 0;
145 	sc->lsc.sc_initaddr = mem;
146 	mem += sizeof(struct leinit);
147 	sc->lsc.sc_rmdaddr = mem;
148 	mem += sizeof(struct lermd) * sc->lsc.sc_nrbuf;
149 	sc->lsc.sc_tmdaddr = mem;
150 	mem += sizeof(struct letmd) * sc->lsc.sc_ntbuf;
151 	for (i = 0; i < sc->lsc.sc_nrbuf; i++, mem += LEBLEN)
152 		sc->lsc.sc_rbufaddr[i] = mem;
153 	for (i = 0; i < sc->lsc.sc_ntbuf; i++, mem += LEBLEN)
154 		sc->lsc.sc_tbufaddr[i] = mem;
155 #ifdef notyet
156 	if (mem > ...)
157 		panic(...);
158 #endif
159 }
160 
161 /*
162  * Set up the initialization block and the descriptor rings.
163  */
164 void
165 am7990_meminit(sc)
166 	struct lance_softc *sc;
167 {
168 	u_long a;
169 	int bix;
170 	struct leinit init;
171 	struct lermd rmd;
172 	struct letmd tmd;
173 	u_int8_t *myaddr;
174 
175 #if NBPFILTER > 0
176 	if (ifp->if_flags & IFF_PROMISC)
177 		init.init_mode = LE_MODE_NORMAL | LE_MODE_PROM;
178 	else
179 #endif
180 		init.init_mode = LE_MODE_NORMAL;
181 	if (sc->sc_initmodemedia == 1)
182 		init.init_mode |= LE_MODE_PSEL0;
183 
184 	/*
185 	 * Update our private copy of the Ethernet address.
186 	 * We NEED the copy so we can ensure its alignment!
187 	 */
188 	memcpy(sc->sc_enaddr, LLADDR(ifp->if_sadl), ETHER_ADDR_LEN);
189 	myaddr = sc->sc_enaddr;
190 
191 	init.init_padr[0] = (myaddr[1] << 8) | myaddr[0];
192 	init.init_padr[1] = (myaddr[3] << 8) | myaddr[2];
193 	init.init_padr[2] = (myaddr[5] << 8) | myaddr[4];
194 	lance_setladrf(&sc->sc_ethercom, init.init_ladrf);
195 
196 	sc->sc_last_rd = 0;
197 	sc->sc_first_td = sc->sc_last_td = sc->sc_no_td = 0;
198 
199 	a = sc->sc_addr + LE_RMDADDR(sc, 0);
200 	init.init_rdra = a;
201 	init.init_rlen = (a >> 16) | ((ffs(sc->sc_nrbuf) - 1) << 13);
202 
203 	a = sc->sc_addr + LE_TMDADDR(sc, 0);
204 	init.init_tdra = a;
205 	init.init_tlen = (a >> 16) | ((ffs(sc->sc_ntbuf) - 1) << 13);
206 
207 	(*sc->sc_copytodesc)(sc, &init, LE_INITADDR(sc), sizeof(init));
208 
209 	/*
210 	 * Set up receive ring descriptors.
211 	 */
212 	for (bix = 0; bix < sc->sc_nrbuf; bix++) {
213 		a = sc->sc_addr + LE_RBUFADDR(sc, bix);
214 		rmd.rmd0 = a;
215 		rmd.rmd1_hadr = a >> 16;
216 		rmd.rmd1_bits = LE_R1_OWN;
217 		rmd.rmd2 = -LEBLEN | LE_XMD2_ONES;
218 		rmd.rmd3 = 0;
219 		(*sc->sc_copytodesc)(sc, &rmd, LE_RMDADDR(sc, bix),
220 		    sizeof(rmd));
221 	}
222 
223 	/*
224 	 * Set up transmit ring descriptors.
225 	 */
226 	for (bix = 0; bix < sc->sc_ntbuf; bix++) {
227 		a = sc->sc_addr + LE_TBUFADDR(sc, bix);
228 		tmd.tmd0 = a;
229 		tmd.tmd1_hadr = a >> 16;
230 		tmd.tmd1_bits = 0;
231 		tmd.tmd2 = 0 | LE_XMD2_ONES;
232 		tmd.tmd3 = 0;
233 		(*sc->sc_copytodesc)(sc, &tmd, LE_TMDADDR(sc, bix),
234 		    sizeof(tmd));
235 	}
236 }
237 
238 integrate void
239 am7990_rint(sc)
240 	struct lance_softc *sc;
241 {
242 	int bix;
243 	int rp;
244 	struct lermd rmd;
245 
246 	bix = sc->sc_last_rd;
247 
248 	/* Process all buffers with valid data. */
249 	for (;;) {
250 		rp = LE_RMDADDR(sc, bix);
251 		(*sc->sc_copyfromdesc)(sc, &rmd, rp, sizeof(rmd));
252 
253 		if (rmd.rmd1_bits & LE_R1_OWN)
254 			break;
255 
256 		if (rmd.rmd1_bits & LE_R1_ERR) {
257 			if (rmd.rmd1_bits & LE_R1_ENP) {
258 #ifdef LEDEBUG
259 				if ((rmd.rmd1_bits & LE_R1_OFLO) == 0) {
260 					if (rmd.rmd1_bits & LE_R1_FRAM)
261 						printf("%s: framing error\n",
262 						    sc->sc_dev.dv_xname);
263 					if (rmd.rmd1_bits & LE_R1_CRC)
264 						printf("%s: crc mismatch\n",
265 						    sc->sc_dev.dv_xname);
266 				}
267 #endif
268 			} else {
269 				if (rmd.rmd1_bits & LE_R1_OFLO)
270 					printf("%s: overflow\n",
271 					    sc->sc_dev.dv_xname);
272 			}
273 			if (rmd.rmd1_bits & LE_R1_BUFF)
274 				printf("%s: receive buffer error\n",
275 				    sc->sc_dev.dv_xname);
276 			ifp->if_ierrors++;
277 		} else if ((rmd.rmd1_bits & (LE_R1_STP | LE_R1_ENP)) !=
278 		    (LE_R1_STP | LE_R1_ENP)) {
279 			printf("%s: dropping chained buffer\n",
280 			    sc->sc_dev.dv_xname);
281 			ifp->if_ierrors++;
282 		} else {
283 #ifdef LEDEBUG
284 			if (sc->sc_debug > 1)
285 				am7990_recv_print(sc, sc->sc_last_rd);
286 #endif
287 			lance_read(sc, LE_RBUFADDR(sc, bix),
288 				   (int)rmd.rmd3 - 4);
289 		}
290 
291 		rmd.rmd1_bits = LE_R1_OWN;
292 		rmd.rmd2 = -LEBLEN | LE_XMD2_ONES;
293 		rmd.rmd3 = 0;
294 		(*sc->sc_copytodesc)(sc, &rmd, rp, sizeof(rmd));
295 
296 #ifdef LEDEBUG
297 		if (sc->sc_debug)
298 			printf("sc->sc_last_rd = %x, rmd: "
299 			       "ladr %04x, hadr %02x, flags %02x, "
300 			       "bcnt %04x, mcnt %04x\n",
301 				sc->sc_last_rd,
302 				rmd.rmd0, rmd.rmd1_hadr, rmd.rmd1_bits,
303 				rmd.rmd2, rmd.rmd3);
304 #endif
305 
306 		if (++bix == sc->sc_nrbuf)
307 			bix = 0;
308 	}
309 
310 	sc->sc_last_rd = bix;
311 }
312 
313 integrate void
314 am7990_tint(sc)
315 	struct lance_softc *sc;
316 {
317 	int bix;
318 	struct letmd tmd;
319 
320 	bix = sc->sc_first_td;
321 
322 	for (;;) {
323 		if (sc->sc_no_td <= 0)
324 			break;
325 
326 		(*sc->sc_copyfromdesc)(sc, &tmd, LE_TMDADDR(sc, bix),
327 		    sizeof(tmd));
328 
329 #ifdef LEDEBUG
330 		if (sc->sc_debug)
331 			printf("trans tmd: "
332 			    "ladr %04x, hadr %02x, flags %02x, "
333 			    "bcnt %04x, mcnt %04x\n",
334 			    tmd.tmd0, tmd.tmd1_hadr, tmd.tmd1_bits,
335 			    tmd.tmd2, tmd.tmd3);
336 #endif
337 
338 		if (tmd.tmd1_bits & LE_T1_OWN)
339 			break;
340 
341 		ifp->if_flags &= ~IFF_OACTIVE;
342 
343 		if (tmd.tmd1_bits & LE_T1_ERR) {
344 			if (tmd.tmd3 & LE_T3_BUFF)
345 				printf("%s: transmit buffer error\n",
346 				    sc->sc_dev.dv_xname);
347 			else if (tmd.tmd3 & LE_T3_UFLO)
348 				printf("%s: underflow\n", sc->sc_dev.dv_xname);
349 			if (tmd.tmd3 & (LE_T3_BUFF | LE_T3_UFLO)) {
350 				lance_reset(sc);
351 				return;
352 			}
353 			if (tmd.tmd3 & LE_T3_LCAR) {
354 				sc->sc_havecarrier = 0;
355 				if (sc->sc_nocarrier)
356 					(*sc->sc_nocarrier)(sc);
357 				else
358 					printf("%s: lost carrier\n",
359 					    sc->sc_dev.dv_xname);
360 			}
361 			if (tmd.tmd3 & LE_T3_LCOL)
362 				ifp->if_collisions++;
363 			if (tmd.tmd3 & LE_T3_RTRY) {
364 #ifdef LEDEBUG
365 				printf("%s: excessive collisions, tdr %d\n",
366 				    sc->sc_dev.dv_xname,
367 				    tmd.tmd3 & LE_T3_TDR_MASK);
368 #endif
369 				ifp->if_collisions += 16;
370 			}
371 			ifp->if_oerrors++;
372 		} else {
373 			if (tmd.tmd1_bits & LE_T1_ONE)
374 				ifp->if_collisions++;
375 			else if (tmd.tmd1_bits & LE_T1_MORE)
376 				/* Real number is unknown. */
377 				ifp->if_collisions += 2;
378 			ifp->if_opackets++;
379 		}
380 
381 		if (++bix == sc->sc_ntbuf)
382 			bix = 0;
383 
384 		--sc->sc_no_td;
385 	}
386 
387 	sc->sc_first_td = bix;
388 
389 	am7990_start(ifp);
390 
391 	if (sc->sc_no_td == 0)
392 		ifp->if_timer = 0;
393 }
394 
395 /*
396  * Controller interrupt.
397  */
398 int
399 am7990_intr(arg)
400 	void *arg;
401 {
402 	struct lance_softc *sc = arg;
403 	u_int16_t isr;
404 
405 	isr = (*sc->sc_rdcsr)(sc, LE_CSR0) | sc->sc_saved_csr0;
406 	sc->sc_saved_csr0 = 0;
407 #if defined(LEDEBUG) && LEDEBUG > 1
408 	if (sc->sc_debug)
409 		printf("%s: am7990_intr entering with isr=%04x\n",
410 		    sc->sc_dev.dv_xname, isr);
411 #endif
412 	if ((isr & LE_C0_INTR) == 0)
413 		return (0);
414 
415 #ifdef __vax__
416 	/*
417 	 * DEC needs this write order to the registers, don't know
418 	 * the results on other arch's.  Ragge 991029
419 	 */
420 	isr &= ~LE_C0_INEA;
421 	(*sc->sc_wrcsr)(sc, LE_CSR0, isr);
422 	(*sc->sc_wrcsr)(sc, LE_CSR0, LE_C0_INEA);
423 #else
424 	(*sc->sc_wrcsr)(sc, LE_CSR0,
425 	    isr & (LE_C0_INEA | LE_C0_BABL | LE_C0_MISS | LE_C0_MERR |
426 		   LE_C0_RINT | LE_C0_TINT | LE_C0_IDON));
427 #endif
428 	if (isr & LE_C0_ERR) {
429 		if (isr & LE_C0_BABL) {
430 #ifdef LEDEBUG
431 			printf("%s: babble\n", sc->sc_dev.dv_xname);
432 #endif
433 			ifp->if_oerrors++;
434 		}
435 #if 0
436 		if (isr & LE_C0_CERR) {
437 			printf("%s: collision error\n", sc->sc_dev.dv_xname);
438 			ifp->if_collisions++;
439 		}
440 #endif
441 		if (isr & LE_C0_MISS) {
442 #ifdef LEDEBUG
443 			printf("%s: missed packet\n", sc->sc_dev.dv_xname);
444 #endif
445 			ifp->if_ierrors++;
446 		}
447 		if (isr & LE_C0_MERR) {
448 			printf("%s: memory error\n", sc->sc_dev.dv_xname);
449 			lance_reset(sc);
450 			return (1);
451 		}
452 	}
453 
454 	if ((isr & LE_C0_RXON) == 0) {
455 		printf("%s: receiver disabled\n", sc->sc_dev.dv_xname);
456 		ifp->if_ierrors++;
457 		lance_reset(sc);
458 		return (1);
459 	}
460 	if ((isr & LE_C0_TXON) == 0) {
461 		printf("%s: transmitter disabled\n", sc->sc_dev.dv_xname);
462 		ifp->if_oerrors++;
463 		lance_reset(sc);
464 		return (1);
465 	}
466 
467 	/*
468 	 * Pretend we have carrier; if we don't this will be cleared
469 	 * shortly.
470 	 */
471 	sc->sc_havecarrier = 1;
472 
473 	if (isr & LE_C0_RINT)
474 		am7990_rint(sc);
475 	if (isr & LE_C0_TINT)
476 		am7990_tint(sc);
477 
478 #if NRND > 0
479 	rnd_add_uint32(&sc->rnd_source, isr);
480 #endif
481 
482 	return (1);
483 }
484 
485 #undef ifp
486 
487 /*
488  * Setup output on interface.
489  * Get another datagram to send off of the interface queue, and map it to the
490  * interface before starting the output.
491  * Called only at splnet or interrupt level.
492  */
493 void
494 am7990_start(ifp)
495 	struct ifnet *ifp;
496 {
497 	struct lance_softc *sc = ifp->if_softc;
498 	int bix;
499 	struct mbuf *m;
500 	struct letmd tmd;
501 	int rp;
502 	int len;
503 
504 	if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING)
505 		return;
506 
507 	bix = sc->sc_last_td;
508 
509 	for (;;) {
510 		rp = LE_TMDADDR(sc, bix);
511 		(*sc->sc_copyfromdesc)(sc, &tmd, rp, sizeof(tmd));
512 
513 		if (tmd.tmd1_bits & LE_T1_OWN) {
514 			ifp->if_flags |= IFF_OACTIVE;
515 			printf("missing buffer, no_td = %d, last_td = %d\n",
516 			    sc->sc_no_td, sc->sc_last_td);
517 		}
518 
519 		IFQ_DEQUEUE(&ifp->if_snd, m);
520 		if (m == 0)
521 			break;
522 
523 #if NBPFILTER > 0
524 		/*
525 		 * If BPF is listening on this interface, let it see the packet
526 		 * before we commit it to the wire.
527 		 */
528 		if (ifp->if_bpf)
529 			bpf_mtap(ifp->if_bpf, m);
530 #endif
531 
532 		/*
533 		 * Copy the mbuf chain into the transmit buffer.
534 		 */
535 		len = lance_put(sc, LE_TBUFADDR(sc, bix), m);
536 
537 #ifdef LEDEBUG
538 		if (len > ETHERMTU + sizeof(struct ether_header))
539 			printf("packet length %d\n", len);
540 #endif
541 
542 		ifp->if_timer = 5;
543 
544 		/*
545 		 * Init transmit registers, and set transmit start flag.
546 		 */
547 		tmd.tmd1_bits = LE_T1_OWN | LE_T1_STP | LE_T1_ENP;
548 		tmd.tmd2 = -len | LE_XMD2_ONES;
549 		tmd.tmd3 = 0;
550 
551 		(*sc->sc_copytodesc)(sc, &tmd, rp, sizeof(tmd));
552 
553 #ifdef LEDEBUG
554 		if (sc->sc_debug > 1)
555 			am7990_xmit_print(sc, sc->sc_last_td);
556 #endif
557 
558 		(*sc->sc_wrcsr)(sc, LE_CSR0, LE_C0_INEA | LE_C0_TDMD);
559 
560 		if (++bix == sc->sc_ntbuf)
561 			bix = 0;
562 
563 		if (++sc->sc_no_td == sc->sc_ntbuf) {
564 			ifp->if_flags |= IFF_OACTIVE;
565 			break;
566 		}
567 
568 	}
569 
570 	sc->sc_last_td = bix;
571 }
572 
573 #ifdef LEDEBUG
574 void
575 am7990_recv_print(sc, no)
576 	struct lance_softc *sc;
577 	int no;
578 {
579 	struct lermd rmd;
580 	u_int16_t len;
581 	struct ether_header eh;
582 
583 	(*sc->sc_copyfromdesc)(sc, &rmd, LE_RMDADDR(sc, no), sizeof(rmd));
584 	len = rmd.rmd3;
585 	printf("%s: receive buffer %d, len = %d\n", sc->sc_dev.dv_xname, no,
586 	    len);
587 	printf("%s: status %04x\n", sc->sc_dev.dv_xname,
588 	    (*sc->sc_rdcsr)(sc, LE_CSR0));
589 	printf("%s: ladr %04x, hadr %02x, flags %02x, bcnt %04x, mcnt %04x\n",
590 	    sc->sc_dev.dv_xname,
591 	    rmd.rmd0, rmd.rmd1_hadr, rmd.rmd1_bits, rmd.rmd2, rmd.rmd3);
592 	if (len >= sizeof(eh)) {
593 		(*sc->sc_copyfrombuf)(sc, &eh, LE_RBUFADDR(sc, no), sizeof(eh));
594 		printf("%s: dst %s", sc->sc_dev.dv_xname,
595 			ether_sprintf(eh.ether_dhost));
596 		printf(" src %s type %04x\n", ether_sprintf(eh.ether_shost),
597 			ntohs(eh.ether_type));
598 	}
599 }
600 
601 void
602 am7990_xmit_print(sc, no)
603 	struct lance_softc *sc;
604 	int no;
605 {
606 	struct letmd tmd;
607 	u_int16_t len;
608 	struct ether_header eh;
609 
610 	(*sc->sc_copyfromdesc)(sc, &tmd, LE_TMDADDR(sc, no), sizeof(tmd));
611 	len = -tmd.tmd2;
612 	printf("%s: transmit buffer %d, len = %d\n", sc->sc_dev.dv_xname, no,
613 	    len);
614 	printf("%s: status %04x\n", sc->sc_dev.dv_xname,
615 	    (*sc->sc_rdcsr)(sc, LE_CSR0));
616 	printf("%s: ladr %04x, hadr %02x, flags %02x, bcnt %04x, mcnt %04x\n",
617 	    sc->sc_dev.dv_xname,
618 	    tmd.tmd0, tmd.tmd1_hadr, tmd.tmd1_bits, tmd.tmd2, tmd.tmd3);
619 	if (len >= sizeof(eh)) {
620 		(*sc->sc_copyfrombuf)(sc, &eh, LE_TBUFADDR(sc, no), sizeof(eh));
621 		printf("%s: dst %s", sc->sc_dev.dv_xname,
622 			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