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