xref: /csrg-svn/sys/sparc/sbus/if_le.c (revision 59215)
155139Storek /*-
255139Storek  * Copyright (c) 1982, 1992 The Regents of the University of California.
355139Storek  * All rights reserved.
455139Storek  *
555139Storek  * %sccs.include.redist.c%
655139Storek  *
7*59215Storek  *	@(#)if_le.c	7.3 (Berkeley) 04/20/93
855139Storek  *
9*59215Storek  * from: $Header: if_le.c,v 1.23 93/04/21 02:39:38 torek Exp $
1055139Storek  */
1155139Storek 
1255139Storek #include "bpfilter.h"
1355139Storek 
1455139Storek /*
1555139Storek  * AMD 7990 LANCE
1655139Storek  */
1756540Sbostic #include <sys/param.h>
1856540Sbostic #include <sys/device.h>
1956540Sbostic #include <sys/systm.h>
2056540Sbostic #include <sys/kernel.h>
2156540Sbostic #include <sys/mbuf.h>
2256540Sbostic #include <sys/buf.h>
2356540Sbostic #include <sys/socket.h>
2456540Sbostic #include <sys/syslog.h>
2556540Sbostic #include <sys/ioctl.h>
2656540Sbostic #include <sys/malloc.h>
2756540Sbostic #include <sys/errno.h>
2855139Storek 
2956540Sbostic #include <net/if.h>
3056540Sbostic #include <net/netisr.h>
3156540Sbostic #include <net/route.h>
3255139Storek #if NBPFILTER > 0
3356540Sbostic #include <sys/select.h>
3456540Sbostic #include <net/bpf.h>
3556540Sbostic #include <net/bpfdesc.h>
3655139Storek #endif
3755139Storek 
3855139Storek #ifdef INET
3956540Sbostic #include <netinet/in.h>
4056540Sbostic #include <netinet/in_systm.h>
4156540Sbostic #include <netinet/in_var.h>
4256540Sbostic #include <netinet/ip.h>
4356540Sbostic #include <netinet/if_ether.h>
4455139Storek #endif
4555139Storek 
4655139Storek #ifdef NS
4756540Sbostic #include <netns/ns.h>
4856540Sbostic #include <netns/ns_if.h>
4955139Storek #endif
5055139Storek 
5155139Storek #ifdef APPLETALK
5256540Sbostic #include <netddp/atalk.h>
5355139Storek #endif
5455139Storek 
5556540Sbostic #include <machine/autoconf.h>
5656540Sbostic #include <machine/cpu.h>
5756540Sbostic #include <machine/pmap.h>
5855139Storek 
5956540Sbostic #include <sparc/sbus/if_lereg.h>
6056540Sbostic #include <sparc/sbus/sbusvar.h>
6155139Storek 
6255139Storek /* DVMA address to LANCE address -- the Sbus/MMU will resupply the 0xff */
6355139Storek #define	LANCE_ADDR(x)	((int)(x) & ~0xff000000)
6455139Storek 
6555139Storek int	ledebug = 0;		/* console error messages */
6655139Storek 
6755139Storek #ifdef PACKETSTATS
6855139Storek long	lexpacketsizes[LEMTU+1];
6955139Storek long	lerpacketsizes[LEMTU+1];
7055139Storek #endif
7155139Storek 
7255139Storek /* Per interface statistics */
7355139Storek /* XXX this should go in something like if_levar.h */
7455139Storek struct	lestats {
7555139Storek 	long	lexints;	/* transmitter interrupts */
7655139Storek 	long	lerints;	/* receiver interrupts */
7755139Storek 	long	lerbufs;	/* total buffers received during interrupts */
7855139Storek 	long	lerhits;	/* times current rbuf was full */
7955139Storek 	long	lerscans;	/* rbufs scanned before finding first full */
8055139Storek };
8155139Storek 
8255139Storek /*
8355139Storek  * Ethernet software status per interface.
8455139Storek  *
8555139Storek  * Each interface is referenced by a network interface structure,
8655139Storek  * le_if, which the routing code uses to locate the interface.
8755139Storek  * This structure contains the output queue for the interface, its address, ...
8855139Storek  */
8955139Storek struct le_softc {
9055139Storek 	struct	device sc_dev;		/* base device */
9155139Storek 	struct	sbusdev sc_sd;		/* sbus device */
9255139Storek 	struct	intrhand sc_ih;		/* interrupt vectoring */
93*59215Storek 	struct	evcnt sc_intrcnt;	/* # of interrupts, per le */
94*59215Storek 	struct	evcnt sc_errcnt;	/* # of errors, per le */
9555139Storek 
9655139Storek 	struct	arpcom sc_ac;		/* common Ethernet structures */
9755139Storek #define	sc_if	sc_ac.ac_if		/* network-visible interface */
9855139Storek #define	sc_addr	sc_ac.ac_enaddr		/* hardware Ethernet address */
9955139Storek 	volatile struct	lereg1 *sc_r1;	/* LANCE registers */
10055139Storek 	volatile struct	lereg2 *sc_r2;	/* dual-port RAM */
10155139Storek 	int	sc_rmd;			/* predicted next rmd to process */
10255139Storek 	int	sc_runt;
10355139Storek 	int	sc_jab;
10455139Storek 	int	sc_merr;
10555139Storek 	int	sc_babl;
10655139Storek 	int	sc_cerr;
10755139Storek 	int	sc_miss;
10855139Storek 	int	sc_xint;
10955139Storek 	int	sc_xown;
11055139Storek 	int	sc_uflo;
11155139Storek 	int	sc_rxlen;
11255139Storek 	int	sc_rxoff;
11355139Storek 	int	sc_txoff;
11455139Storek 	int	sc_busy;
11555139Storek 	short	sc_iflags;
11655139Storek 	struct	lestats sc_lestats;	/* per interface statistics */
11755139Storek #if NBPFILTER > 0
11855139Storek 	caddr_t	sc_bpf;
11955139Storek #endif
12055139Storek };
12155139Storek 
12255139Storek 
12355139Storek /* autoconfiguration driver */
12455139Storek void	leattach(struct device *, struct device *, void *);
12555139Storek struct	cfdriver lecd =
12655139Storek     { NULL, "le", matchbyname, leattach, DV_IFNET, sizeof(struct le_softc) };
12755139Storek 
12855139Storek /* Forwards */
12955139Storek void	leattach(struct device *, struct device *, void *);
13055139Storek void	lesetladrf(struct le_softc *);
13155139Storek void	lereset(struct device *);
13255139Storek int	leinit(int);
13355139Storek int	lestart(struct ifnet *);
13455139Storek int	leintr(void *);
13555139Storek void	lexint(struct le_softc *);
13655139Storek void	lerint(struct le_softc *);
13755139Storek void	leread(struct le_softc *, char *, int);
13855139Storek int	leput(char *, struct mbuf *);
13955139Storek struct mbuf *leget(char *, int, int, struct ifnet *);
14055139Storek int	leioctl(struct ifnet *, int, caddr_t);
14155139Storek void	leerror(struct le_softc *, int);
14255139Storek void	lererror(struct le_softc *, char *);
14355139Storek void	lexerror(struct le_softc *);
14455139Storek 
14555139Storek /*
14655139Storek  * Interface exists: make available by filling in network interface
14755139Storek  * record.  System will initialize the interface when it is ready
14855139Storek  * to accept packets.
14955139Storek  */
15055139Storek void
15155139Storek leattach(parent, self, args)
15255139Storek 	struct device *parent;
15355139Storek 	struct device *self;
15455139Storek 	void *args;
15555139Storek {
15655139Storek 	register struct le_softc *sc = (struct le_softc *)self;
15755139Storek 	register struct sbus_attach_args *sa = args;
15855139Storek 	register volatile struct lereg2 *ler2;
15955139Storek 	struct ifnet *ifp = &sc->sc_if;
160*59215Storek 	register struct bootpath *bp;
16155139Storek 	register int a, pri;
16255139Storek #define	ISQUADALIGN(a) ((((long) a) & 0x3) == 0)
16355139Storek 
16455139Storek 	/* XXX the following declarations should be elsewhere */
16555139Storek 	extern void myetheraddr(u_char *);
16655139Storek 	extern caddr_t dvma_malloc(size_t);
16755139Storek 
16855139Storek 	if (sa->sa_ra.ra_nintr != 1) {
16955139Storek 		printf(": expected 1 interrupt, got %d\n", sa->sa_ra.ra_nintr);
17055139Storek 		return;
17155139Storek 	}
17255139Storek 	pri = sa->sa_ra.ra_intr[0].int_pri;
17355139Storek 	printf(" pri %d", pri);
17455139Storek 	sc->sc_r1 = (volatile struct lereg1 *)
17555139Storek 	    mapiodev(sa->sa_ra.ra_paddr, sizeof(struct lereg1));
17655139Storek 	ler2 = sc->sc_r2 = (volatile struct lereg2 *)
17755139Storek 	    dvma_malloc(sizeof(struct lereg2));
17855139Storek if (!ISQUADALIGN(ler2))
17955139Storek 	printf("? not quad aligned (0x%x)\n", ler2);
18055139Storek 
18155139Storek 	myetheraddr(sc->sc_addr);
18255139Storek 	printf(": hardware address %s\n", ether_sprintf(sc->sc_addr));
18355139Storek 
18455139Storek 	/*
18555139Storek 	 * Setup for transmit/receive
18655139Storek 	 *
18755139Storek 	 * According to Van, some versions of the Lance only use this
18855139Storek 	 * address to receive packets; it doesn't put them in
18955139Storek 	 * output packets. We'll want to make sure that lestart()
19055139Storek 	 * installs the address.
19155139Storek 	 */
19255139Storek 	ler2->ler2_padr[0] = sc->sc_addr[1];
19355139Storek 	ler2->ler2_padr[1] = sc->sc_addr[0];
19455139Storek 	ler2->ler2_padr[2] = sc->sc_addr[3];
19555139Storek 	ler2->ler2_padr[3] = sc->sc_addr[2];
19655139Storek 	ler2->ler2_padr[4] = sc->sc_addr[5];
19755139Storek 	ler2->ler2_padr[5] = sc->sc_addr[4];
19855139Storek 	a = LANCE_ADDR(&ler2->ler2_rmd);
19955139Storek if (!ISQUADALIGN(a))
20055139Storek 	printf("rdra not quad aligned (0x%x)\n", a);
20155139Storek 	ler2->ler2_rlen = LE_RLEN | (a >> 16);
20255139Storek 	ler2->ler2_rdra = a;
20355139Storek 	a = LANCE_ADDR(&ler2->ler2_tmd);
20455139Storek if (!ISQUADALIGN(a))
20555139Storek 	printf("tdra not quad aligned (0x%x)\n", a);
20655139Storek 	ler2->ler2_tlen = LE_TLEN | (a >> 16);
20755139Storek 	ler2->ler2_tdra = a;
20855139Storek 
20955139Storek 	/*
21055139Storek 	 * Link into sbus, and establish interrupt handler.
21155139Storek 	 */
21255139Storek 	sc->sc_sd.sd_reset = lereset;
21355139Storek 	sbus_establish(&sc->sc_sd, &sc->sc_dev);
21455139Storek 	sc->sc_ih.ih_fun = leintr;
21555139Storek 	sc->sc_ih.ih_arg = sc;
21655139Storek 	intr_establish(pri, &sc->sc_ih);
21755139Storek 
218*59215Storek 	/*
219*59215Storek 	 * Set up event counters.
220*59215Storek 	 */
221*59215Storek 	evcnt_attach(&sc->sc_dev, "intr", &sc->sc_intrcnt);
222*59215Storek 	evcnt_attach(&sc->sc_dev, "errs", &sc->sc_errcnt);
223*59215Storek 
22455139Storek 	ifp->if_unit = sc->sc_dev.dv_unit;
22555139Storek 	ifp->if_name = "le";
22655139Storek 	ifp->if_mtu = ETHERMTU;
22755139Storek 	ifp->if_init = leinit;
22855139Storek 	ifp->if_ioctl = leioctl;
22955139Storek 	ifp->if_output = ether_output;
23055139Storek 	ifp->if_start = lestart;
23155139Storek 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
23255139Storek #ifdef IFF_NOTRAILERS
23355139Storek 	/* XXX still compile when the blasted things are gone... */
23455139Storek 	ifp->if_flags |= IFF_NOTRAILERS;
23555139Storek #endif
23655139Storek #if NBPFILTER > 0
23755139Storek 	bpfattach(&sc->sc_bpf, ifp, DLT_EN10MB, sizeof(struct ether_header));
23855139Storek #endif
23955139Storek 	if_attach(ifp);
240*59215Storek 
241*59215Storek #define SAME_LANCE(bp, sa) \
242*59215Storek 	((bp->val[0] == sa->sa_slot && bp->val[1] == sa->sa_offset) || \
243*59215Storek 	 (bp->val[0] == -1 && bp->val[1] == sc->sc_dev.dv_unit))
244*59215Storek 
245*59215Storek 	bp = sa->sa_ra.ra_bp;
246*59215Storek 	if (bp != NULL && strcmp(bp->name, "le") == 0 && SAME_LANCE(bp, sa))
247*59215Storek 		bootdv = &sc->sc_dev;
24855139Storek }
24955139Storek 
25055139Storek /*
25155139Storek  * Setup the logical address filter
25255139Storek  */
25355139Storek void
25455139Storek lesetladrf(sc)
25555139Storek 	register struct le_softc *sc;
25655139Storek {
25755139Storek 	register volatile struct lereg2 *ler2 = sc->sc_r2;
25855139Storek 	register struct ifnet *ifp = &sc->sc_if;
25955139Storek 	register struct ether_multi *enm;
260*59215Storek 	register u_char *cp, c;
26155139Storek 	register u_long crc;
26255139Storek 	register int i, len;
26355139Storek 	struct ether_multistep step;
26455139Storek 
26555139Storek 	/*
26655139Storek 	 * Set up multicast address filter by passing all multicast
26755139Storek 	 * addresses through a crc generator, and then using the high
26855139Storek 	 * order 6 bits as a index into the 64 bit logical address
26955139Storek 	 * filter. The high order two bits select the word, while the
27055139Storek 	 * rest of the bits select the bit within the word.
27155139Storek 	 */
27255139Storek 
27355139Storek 	ler2->ler2_ladrf[0] = 0;
27455139Storek 	ler2->ler2_ladrf[1] = 0;
27555139Storek 	ifp->if_flags &= ~IFF_ALLMULTI;
27655139Storek 	ETHER_FIRST_MULTI(step, &sc->sc_ac, enm);
27755139Storek 	while (enm != NULL) {
27855139Storek 		if (bcmp((caddr_t)&enm->enm_addrlo,
279*59215Storek 		    (caddr_t)&enm->enm_addrhi, sizeof(enm->enm_addrlo)) != 0) {
28055139Storek 			/*
28155139Storek 			 * We must listen to a range of multicast
28255139Storek 			 * addresses. For now, just accept all
28355139Storek 			 * multicasts, rather than trying to set only
28455139Storek 			 * those filter bits needed to match the range.
28555139Storek 			 * (At this time, the only use of address
28655139Storek 			 * ranges is for IP multicast routing, for
28755139Storek 			 * which the range is big enough to require all
28855139Storek 			 * bits set.)
28955139Storek 			 */
29055139Storek 			ler2->ler2_ladrf[0] = 0xffffffff;
29155139Storek 			ler2->ler2_ladrf[1] = 0xffffffff;
29255139Storek 			ifp->if_flags |= IFF_ALLMULTI;
29355139Storek 			return;
29455139Storek 		}
29555139Storek 
296*59215Storek 		/*
297*59215Storek 		 * One would think, given the AM7990 document's polynomial
298*59215Storek 		 * of 0x04c11db6, that this should be 0x6db88320 (the bit
299*59215Storek 		 * reversal of the AMD value), but that is not right.  See
300*59215Storek 		 * the BASIC listing: bit 0 (our bit 31) must then be set.
301*59215Storek 		 */
30255139Storek 		cp = (unsigned char *)&enm->enm_addrlo;
30355139Storek 		crc = 0xffffffff;
304*59215Storek 		for (len = 6; --len >= 0;) {
305*59215Storek 			c = *cp++;
30655139Storek 			for (i = 0; i < 8; i++) {
30755139Storek 				if ((c & 0x01) ^ (crc & 0x01)) {
30855139Storek 					crc >>= 1;
30955139Storek 					crc = crc ^ 0xedb88320;
310*59215Storek 				} else
31155139Storek 					crc >>= 1;
31255139Storek 				c >>= 1;
31355139Storek 			}
31455139Storek 		}
31555139Storek 		/* Just want the 6 most significant bits. */
31655139Storek 		crc = crc >> 26;
31755139Storek 
31855139Storek 		/* Turn on the corresponding bit in the filter. */
31955139Storek 		ler2->ler2_ladrf[crc >> 5] |= 1 << (crc & 0x1f);
32055139Storek 
32155139Storek 		ETHER_NEXT_MULTI(step, enm);
32255139Storek 	}
32355139Storek }
32455139Storek 
32555139Storek void
32655139Storek lereset(dev)
32755139Storek 	struct device *dev;
32855139Storek {
32955139Storek 	register struct le_softc *sc = (struct le_softc *)dev;
33055139Storek 	register volatile struct lereg1 *ler1 = sc->sc_r1;
33155139Storek 	register volatile struct lereg2 *ler2 = sc->sc_r2;
33255139Storek 	register int i, a, timo, stat;
33355139Storek 
33455139Storek #if NBPFILTER > 0
33555139Storek 	if (sc->sc_if.if_flags & IFF_PROMISC)
33655139Storek 		ler2->ler2_mode = LE_MODE_NORMAL | LE_MODE_PROM;
33755139Storek 	else
33855139Storek #endif
33955139Storek 		ler2->ler2_mode = LE_MODE_NORMAL;
34055139Storek 	ler1->ler1_rap = LE_CSR0;
34155139Storek 	ler1->ler1_rdp = LE_C0_STOP;
34255139Storek 
34355139Storek 	/* Setup the logical address filter */
34455139Storek 	lesetladrf(sc);
34555139Storek 
34655139Storek 	/* init receive and transmit rings */
34755139Storek a = LANCE_ADDR(&ler2->ler2_rbuf[0][0]);
34855139Storek if (!ISQUADALIGN(a))
34955139Storek 	printf("rbuf not quad aligned (0x%x)\n", a);
35055139Storek 	for (i = 0; i < LERBUF; i++) {
35155139Storek 		a = LANCE_ADDR(&ler2->ler2_rbuf[i][0]);
35255139Storek 		ler2->ler2_rmd[i].rmd0 = a;
35355139Storek 		ler2->ler2_rmd[i].rmd1_hadr = a >> 16;
35455139Storek 		ler2->ler2_rmd[i].rmd1_bits = LE_R1_OWN;
35555139Storek 		ler2->ler2_rmd[i].rmd2 = -LEMTU;
35655139Storek 		ler2->ler2_rmd[i].rmd3 = 0;
35755139Storek 	}
35855139Storek a = LANCE_ADDR(&ler2->ler2_tbuf[0][0]);
35955139Storek if (!ISQUADALIGN(a))
36055139Storek 	printf("tbuf not quad aligned (0x%x)\n", a);
36155139Storek 	for (i = 0; i < LETBUF; i++) {
36255139Storek 		a = LANCE_ADDR(&ler2->ler2_tbuf[i][0]);
36355139Storek 		ler2->ler2_tmd[i].tmd0 = a;
36455139Storek 		ler2->ler2_tmd[i].tmd1_hadr = a >> 16;
36555139Storek 		ler2->ler2_tmd[i].tmd1_bits = 0;
36655139Storek 		ler2->ler2_tmd[i].tmd2 = 0;
36755139Storek 		ler2->ler2_tmd[i].tmd3 = 0;
36855139Storek 	}
36955139Storek 
37055139Storek bzero(&ler2->ler2_rbuf[0][0], (LERBUF + LETBUF) * LEMTU);
37155139Storek 	/* lance will stuff packet into receive buffer 0 next */
37255139Storek 	sc->sc_rmd = 0;
37355139Storek 
37455139Storek 	/* tell the chip where to find the initialization block */
37555139Storek 	a = LANCE_ADDR(&ler2->ler2_mode);
37655139Storek 	ler1->ler1_rap = LE_CSR1;
37755139Storek 	ler1->ler1_rdp = a;
37855139Storek 	ler1->ler1_rap = LE_CSR2;
37955139Storek 	ler1->ler1_rdp = a >> 16;
38055139Storek 	ler1->ler1_rap = LE_CSR3;
38155139Storek 	ler1->ler1_rdp = LE_C3_BSWP | LE_C3_ACON | LE_C3_BCON;
38255139Storek 	ler1->ler1_rap = LE_CSR0;
38355139Storek 	ler1->ler1_rdp = LE_C0_INIT;
38455139Storek 	timo = 100000;
38555139Storek 	while (((stat = ler1->ler1_rdp) & (LE_C0_ERR | LE_C0_IDON)) == 0) {
38655139Storek 		if (--timo == 0) {
38755139Storek 			printf("%s: init timeout, stat=%b\n",
38855139Storek 			    sc->sc_dev.dv_xname, stat, LE_C0_BITS);
38955139Storek 			break;
39055139Storek 		}
39155139Storek 	}
39255139Storek 	if (stat & LE_C0_ERR)
39355139Storek 		printf("%s: init failed, stat=%b\n",
39455139Storek 		    sc->sc_dev.dv_xname, stat, LE_C0_BITS);
39555139Storek 	else
39655139Storek 		ler1->ler1_rdp = LE_C0_IDON;	/* clear IDON */
39755139Storek 	ler1->ler1_rdp = LE_C0_STRT | LE_C0_INEA;
39855139Storek 	sc->sc_if.if_flags &= ~IFF_OACTIVE;
39955139Storek }
40055139Storek 
40155139Storek /*
40255139Storek  * Initialization of interface
40355139Storek  */
40455139Storek int
40555139Storek leinit(unit)
40655139Storek 	int unit;
40755139Storek {
40855139Storek 	register struct le_softc *sc = lecd.cd_devs[unit];
40955139Storek 	register struct ifnet *ifp = &sc->sc_if;
41055139Storek 	register int s;
41155139Storek 
41255139Storek 	/* not yet, if address still unknown */
41355139Storek 	if (ifp->if_addrlist == (struct ifaddr *)0)
41455139Storek 		return (0);
41555139Storek 	if ((ifp->if_flags & IFF_RUNNING) == 0) {
41655139Storek 		s = splimp();
41755139Storek 		ifp->if_flags |= IFF_RUNNING;
41855139Storek 		lereset((struct device *)sc);
41955139Storek 	        lestart(ifp);
42055139Storek 		splx(s);
42155139Storek 	}
42255139Storek 	return (0);
42355139Storek }
42455139Storek 
42555139Storek /*
42655139Storek  * Start output on interface.  Get another datagram to send
42755139Storek  * off of the interface queue, and copy it to the interface
42855139Storek  * before starting the output.
42955139Storek  */
43055139Storek int
43155139Storek lestart(ifp)
43255139Storek 	register struct ifnet *ifp;
43355139Storek {
43455139Storek 	register struct le_softc *sc = lecd.cd_devs[ifp->if_unit];
43555139Storek 	register volatile struct letmd *tmd;
43655139Storek 	register struct mbuf *m;
43755139Storek 	register int len;
43855139Storek 
43955139Storek 	if ((sc->sc_if.if_flags & IFF_RUNNING) == 0)
44055139Storek 		return (0);
44155139Storek 	IF_DEQUEUE(&sc->sc_if.if_snd, m);
44255139Storek 	if (m == 0)
44355139Storek 		return (0);
44455139Storek 	len = leput(sc->sc_r2->ler2_tbuf[0], m);
44555139Storek #if NBPFILTER > 0
44655139Storek 	/*
44755139Storek 	 * If bpf is listening on this interface, let it
44855139Storek 	 * see the packet before we commit it to the wire.
44955139Storek 	 */
45055139Storek 	if (sc->sc_bpf)
45155139Storek 		bpf_tap(sc->sc_bpf, sc->sc_r2->ler2_tbuf[0], len);
45255139Storek #endif
45355139Storek 
45455139Storek #ifdef PACKETSTATS
45555139Storek 	if (len <= LEMTU)
45655139Storek 		lexpacketsizes[len]++;
45755139Storek #endif
45855139Storek 	tmd = sc->sc_r2->ler2_tmd;
45955139Storek 	tmd->tmd3 = 0;
46055139Storek 	tmd->tmd2 = -len;
46155139Storek 	tmd->tmd1_bits = LE_T1_OWN | LE_T1_STP | LE_T1_ENP;
46255139Storek 	sc->sc_if.if_flags |= IFF_OACTIVE;
46355139Storek 	return (0);
46455139Storek }
46555139Storek 
46655139Storek int
46755139Storek leintr(dev)
46855139Storek 	register void *dev;
46955139Storek {
47055139Storek 	register struct le_softc *sc = dev;
47155139Storek 	register volatile struct lereg1 *ler1 = sc->sc_r1;
47255139Storek 	register int csr0;
47355139Storek 
47455139Storek 	csr0 = ler1->ler1_rdp;
47555139Storek 	if ((csr0 & LE_C0_INTR) == 0)
47655139Storek 		return (0);
477*59215Storek 	sc->sc_intrcnt.ev_count++;
47855139Storek 
47955139Storek 	if (csr0 & LE_C0_ERR) {
480*59215Storek 		sc->sc_errcnt.ev_count++;
48155139Storek 		leerror(sc, csr0);
48255139Storek 		if (csr0 & LE_C0_MERR) {
48355139Storek 			sc->sc_merr++;
48455139Storek 			lereset((struct device *)sc);
48555139Storek 			return (1);
48655139Storek 		}
48755139Storek 		if (csr0 & LE_C0_BABL)
48855139Storek 			sc->sc_babl++;
48955139Storek 		if (csr0 & LE_C0_CERR)
49055139Storek 			sc->sc_cerr++;
49155139Storek 		if (csr0 & LE_C0_MISS)
49255139Storek 			sc->sc_miss++;
49355139Storek 		ler1->ler1_rdp = LE_C0_BABL|LE_C0_CERR|LE_C0_MISS|LE_C0_INEA;
49455139Storek 	}
49555139Storek 	if ((csr0 & LE_C0_RXON) == 0) {
49655139Storek 		sc->sc_rxoff++;
49755139Storek 		lereset((struct device *)sc);
49855139Storek 		return (1);
49955139Storek 	}
50055139Storek 	if ((csr0 & LE_C0_TXON) == 0) {
50155139Storek 		sc->sc_txoff++;
50255139Storek 		lereset((struct device *)sc);
50355139Storek 		return (1);
50455139Storek 	}
50555139Storek 	if (csr0 & LE_C0_RINT) {
50655139Storek 		/* interrupt is cleared in lerint */
50755139Storek 		lerint(sc);
50855139Storek 	}
50955139Storek 	if (csr0 & LE_C0_TINT) {
51055139Storek 		ler1->ler1_rdp = LE_C0_TINT|LE_C0_INEA;
51155139Storek 		lexint(sc);
51255139Storek 	}
51355139Storek 	return (1);
51455139Storek }
51555139Storek 
51655139Storek /*
51755139Storek  * Ethernet interface transmitter interrupt.
51855139Storek  * Start another output if more data to send.
51955139Storek  */
52055139Storek void
52155139Storek lexint(sc)
52255139Storek 	register struct le_softc *sc;
52355139Storek {
52455139Storek 	register volatile struct letmd *tmd = sc->sc_r2->ler2_tmd;
52555139Storek 
52655139Storek 	sc->sc_lestats.lexints++;
52755139Storek 	if ((sc->sc_if.if_flags & IFF_OACTIVE) == 0) {
52855139Storek 		sc->sc_xint++;
52955139Storek 		return;
53055139Storek 	}
53155139Storek 	if (tmd->tmd1_bits & LE_T1_OWN) {
53255139Storek 		sc->sc_xown++;
53355139Storek 		return;
53455139Storek 	}
53555139Storek 	if (tmd->tmd1_bits & LE_T1_ERR) {
53655139Storek err:
53755139Storek 		lexerror(sc);
53855139Storek 		sc->sc_if.if_oerrors++;
53955139Storek 		if (tmd->tmd3 & (LE_T3_BUFF|LE_T3_UFLO)) {
54055139Storek 			sc->sc_uflo++;
54155139Storek 			lereset((struct device *)sc);
54255139Storek 		} else if (tmd->tmd3 & LE_T3_LCOL)
54355139Storek 			sc->sc_if.if_collisions++;
54455139Storek 		else if (tmd->tmd3 & LE_T3_RTRY)
54555139Storek 			sc->sc_if.if_collisions += 16;
54655139Storek 	}
54755139Storek 	else if (tmd->tmd3 & LE_T3_BUFF)
54855139Storek 		/* XXX documentation says BUFF not included in ERR */
54955139Storek 		goto err;
55055139Storek 	else if (tmd->tmd1_bits & LE_T1_ONE)
55155139Storek 		sc->sc_if.if_collisions++;
55255139Storek 	else if (tmd->tmd1_bits & LE_T1_MORE)
55355139Storek 		/* what is the real number? */
55455139Storek 		sc->sc_if.if_collisions += 2;
55555139Storek 	else
55655139Storek 		sc->sc_if.if_opackets++;
55755139Storek 	sc->sc_if.if_flags &= ~IFF_OACTIVE;
55855139Storek 	lestart(&sc->sc_if);
55955139Storek }
56055139Storek 
56155139Storek #define	LENEXTRMP \
56255139Storek 	if (++bix == LERBUF) bix = 0, rmd = sc->sc_r2->ler2_rmd; else ++rmd
56355139Storek 
56455139Storek /*
56555139Storek  * Ethernet interface receiver interrupt.
56655139Storek  * If input error just drop packet.
56755139Storek  * Decapsulate packet based on type and pass to type specific
56855139Storek  * higher-level input routine.
56955139Storek  */
57055139Storek void
57155139Storek lerint(sc)
57255139Storek 	register struct le_softc *sc;
57355139Storek {
57455139Storek 	register int bix = sc->sc_rmd;
57555139Storek 	register volatile struct lermd *rmd = &sc->sc_r2->ler2_rmd[bix];
57655139Storek 
57755139Storek 	sc->sc_lestats.lerints++;
57855139Storek 	/*
57955139Storek 	 * Out of sync with hardware, should never happen?
58055139Storek 	 */
58155139Storek 	if (rmd->rmd1_bits & LE_R1_OWN) {
58255139Storek 		do {
58355139Storek 			sc->sc_lestats.lerscans++;
58455139Storek 			LENEXTRMP;
58555139Storek 		} while ((rmd->rmd1_bits & LE_R1_OWN) && bix != sc->sc_rmd);
58655139Storek 		if (bix == sc->sc_rmd)
58755139Storek 			printf("%s: RINT with no buffer\n",
58855139Storek 			    sc->sc_dev.dv_xname);
58955139Storek 	} else
59055139Storek 		sc->sc_lestats.lerhits++;
59155139Storek 
59255139Storek 	/*
59355139Storek 	 * Process all buffers with valid data
59455139Storek 	 */
59555139Storek 	while ((rmd->rmd1_bits & LE_R1_OWN) == 0) {
59655139Storek 		int len = rmd->rmd3;
59755139Storek 
59855139Storek 		/* Clear interrupt to avoid race condition */
59955139Storek 		sc->sc_r1->ler1_rdp = LE_C0_RINT|LE_C0_INEA;
60055139Storek 
60155139Storek 		if (rmd->rmd1_bits & LE_R1_ERR) {
60255139Storek 			sc->sc_rmd = bix;
60355139Storek 			lererror(sc, "bad packet");
60455139Storek 			sc->sc_if.if_ierrors++;
60555139Storek 		} else if ((rmd->rmd1_bits & (LE_R1_STP|LE_R1_ENP)) !=
60655139Storek 		    (LE_R1_STP|LE_R1_ENP)) {
60755139Storek 			/* XXX make a define for LE_R1_STP|LE_R1_ENP? */
60855139Storek 			/*
60955139Storek 			 * Find the end of the packet so we can see how long
61055139Storek 			 * it was.  We still throw it away.
61155139Storek 			 */
61255139Storek 			do {
61355139Storek 				sc->sc_r1->ler1_rdp = LE_C0_RINT|LE_C0_INEA;
61455139Storek 				rmd->rmd3 = 0;
61555139Storek 				rmd->rmd1_bits = LE_R1_OWN;
61655139Storek 				LENEXTRMP;
61755139Storek 			} while (!(rmd->rmd1_bits &
61855139Storek 			    (LE_R1_OWN|LE_R1_ERR|LE_R1_STP|LE_R1_ENP)));
61955139Storek 			sc->sc_rmd = bix;
62055139Storek 			lererror(sc, "chained buffer");
62155139Storek 			sc->sc_rxlen++;
62255139Storek 			/*
62355139Storek 			 * If search terminated without successful completion
62455139Storek 			 * we reset the hardware (conservative).
62555139Storek 			 */
62655139Storek 			if ((rmd->rmd1_bits &
62755139Storek 			    (LE_R1_OWN|LE_R1_ERR|LE_R1_STP|LE_R1_ENP)) !=
62855139Storek 			    LE_R1_ENP) {
62955139Storek 				lereset((struct device *)sc);
63055139Storek 				return;
63155139Storek 			}
63255139Storek 		} else {
63355139Storek 			leread(sc, sc->sc_r2->ler2_rbuf[bix], len);
63455139Storek #ifdef PACKETSTATS
63555139Storek 			lerpacketsizes[len]++;
63655139Storek #endif
63755139Storek 			sc->sc_lestats.lerbufs++;
63855139Storek 		}
63955139Storek 		rmd->rmd3 = 0;
64055139Storek 		rmd->rmd1_bits = LE_R1_OWN;
64155139Storek 		LENEXTRMP;
64255139Storek 	}
64355139Storek 	sc->sc_rmd = bix;
64455139Storek }
64555139Storek 
64655139Storek void
64755139Storek leread(sc, pkt, len)
64855139Storek 	register struct le_softc *sc;
64955139Storek 	char *pkt;
65055139Storek 	int len;
65155139Storek {
65255139Storek 	register struct ether_header *et;
65355139Storek 	register struct ifnet *ifp = &sc->sc_if;
65455139Storek 	struct mbuf *m;
65555139Storek 	struct ifqueue *inq;
65655139Storek 	int flags;
65755139Storek 
65855139Storek 	ifp->if_ipackets++;
65955139Storek 	et = (struct ether_header *)pkt;
66055139Storek 	et->ether_type = ntohs((u_short)et->ether_type);
66155139Storek 	/* adjust input length to account for header and CRC */
66255139Storek 	len -= sizeof(struct ether_header) + 4;
66355139Storek 
66455139Storek 	if (len <= 0) {
66555139Storek 		if (ledebug)
66655139Storek 			log(LOG_WARNING,
66755139Storek 			    "%s: ierror(runt packet): from %s: len=%d\n",
66855139Storek 			    sc->sc_dev.dv_xname,
66955139Storek 			    ether_sprintf(et->ether_shost), len);
67055139Storek 		sc->sc_runt++;
67155139Storek 		ifp->if_ierrors++;
67255139Storek 		return;
67355139Storek 	}
67455139Storek 
67555139Storek 	/* Setup mbuf flags we'll need later */
67655139Storek 	flags = 0;
67755139Storek 	if (bcmp((caddr_t)etherbroadcastaddr,
67855139Storek 	    (caddr_t)et->ether_dhost, sizeof(etherbroadcastaddr)) == 0)
67955139Storek 		flags |= M_BCAST;
68055139Storek 	if (et->ether_dhost[0] & 1)
68155139Storek 		flags |= M_MCAST;
68255139Storek 
68355139Storek #if NBPFILTER > 0
68455139Storek 	/*
68555139Storek 	 * Check if there's a bpf filter listening on this interface.
686*59215Storek 	 * If so, hand off the raw packet to enet, then discard things
687*59215Storek 	 * not destined for us (but be sure to keep broadcast/multicast).
68855139Storek 	 */
68955139Storek 	if (sc->sc_bpf) {
69055139Storek 		bpf_tap(sc->sc_bpf, pkt, len + sizeof(struct ether_header));
691*59215Storek 		if ((flags & (M_BCAST | M_MCAST)) == 0 &&
69255139Storek 		    bcmp(et->ether_dhost, sc->sc_addr,
693*59215Storek 			    sizeof(et->ether_dhost)) != 0)
69455139Storek 			return;
69555139Storek 	}
69655139Storek #endif
69755139Storek 	m = leget(pkt, len, 0, ifp);
69855139Storek 	if (m == 0)
69955139Storek 		return;
70055139Storek 
70155139Storek 	/* XXX this code comes from ether_input() */
70255139Storek 	ifp->if_lastchange = time;
70355139Storek 	ifp->if_ibytes += m->m_pkthdr.len + sizeof (*et);
70455139Storek 	if (flags) {
70555139Storek 		m->m_flags |= flags;
70655139Storek 		ifp->if_imcasts++;
70755139Storek 	}
70855139Storek 	/* XXX end of code from ether_input() */
70955139Storek 
71055139Storek 	switch (et->ether_type) {
71155139Storek 
71255139Storek #ifdef INET
71355139Storek 	case ETHERTYPE_IP:
71455139Storek 		schednetisr(NETISR_IP);
71555139Storek 		inq = &ipintrq;
71655139Storek 		break;
71755139Storek 
71855139Storek 	case ETHERTYPE_ARP:
71955139Storek 		schednetisr(NETISR_ARP);
72055139Storek 		inq = &arpintrq;
72155139Storek 		break;
72255139Storek #endif
72355139Storek #ifdef NS
72455139Storek 	case ETHERTYPE_NS:
72555139Storek 		schednetisr(NETISR_NS);
72655139Storek 		inq = &nsintrq;
72755139Storek 		break;
72855139Storek #endif
72955139Storek 
73055139Storek #ifdef UTAHONLY
73155139Storek #ifdef APPLETALK
73255139Storek 	case ETHERTYPE_APPLETALK:
73355139Storek 		schednetisr(NETISR_DDP);
73455139Storek 		inq = &ddpintq;
73555139Storek 		break;
73655139Storek 
73755139Storek 	case ETHERTYPE_AARP:
73855139Storek 		aarpinput(&sc->sc_ac, m);
73955139Storek 		return;
74055139Storek #endif
74155139Storek #endif
74255139Storek 	default:
74355139Storek 		m_freem(m);
74455139Storek 		return;
74555139Storek 	}
74655139Storek 
74755139Storek 	if (IF_QFULL(inq)) {
74855139Storek 		IF_DROP(inq);
74955139Storek 		m_freem(m);
75055139Storek 		return;
75155139Storek 	}
75255139Storek 	IF_ENQUEUE(inq, m);
75355139Storek }
75455139Storek 
75555139Storek /*
75655139Storek  * Routine to copy from mbuf chain to transmit
75755139Storek  * buffer in board local memory.
75855139Storek  *
75955139Storek  * ### this can be done by remapping in some cases
76055139Storek  */
76155139Storek int
76255139Storek leput(lebuf, m)
76355139Storek 	register char *lebuf;
76455139Storek 	register struct mbuf *m;
76555139Storek {
76655139Storek 	register struct mbuf *mp;
76755139Storek 	register int len, tlen = 0;
76855139Storek 
76955139Storek 	for (mp = m; mp; mp = mp->m_next) {
77055139Storek 		len = mp->m_len;
77155139Storek 		if (len == 0)
77255139Storek 			continue;
77355139Storek 		tlen += len;
77455139Storek 		bcopy(mtod(mp, char *), lebuf, len);
77555139Storek 		lebuf += len;
77655139Storek 	}
77755139Storek 	m_freem(m);
77855139Storek 	if (tlen < LEMINSIZE) {
77955139Storek 		bzero(lebuf, LEMINSIZE - tlen);
78055139Storek 		tlen = LEMINSIZE;
78155139Storek 	}
78255139Storek 	return (tlen);
78355139Storek }
78455139Storek 
78555139Storek /*
78655139Storek  * Routine to copy from board local memory into mbufs.
78755139Storek  */
78855139Storek struct mbuf *
78955139Storek leget(lebuf, totlen, off0, ifp)
79055139Storek 	char *lebuf;
79155139Storek 	int totlen, off0;
79255139Storek 	struct ifnet *ifp;
79355139Storek {
79455139Storek 	register struct mbuf *m;
79555139Storek 	struct mbuf *top = 0, **mp = &top;
79655139Storek 	register int off = off0, len;
79755139Storek 	register char *cp;
79855139Storek 	char *epkt;
79955139Storek 
80055139Storek 	lebuf += sizeof(struct ether_header);
80155139Storek 	cp = lebuf;
80255139Storek 	epkt = cp + totlen;
80355139Storek 	if (off) {
80455139Storek 		cp += off + 2 * sizeof(u_short);
80555139Storek 		totlen -= 2 * sizeof(u_short);
80655139Storek 	}
80755139Storek 
80855139Storek 	MGETHDR(m, M_DONTWAIT, MT_DATA);
80955139Storek 	if (m == 0)
81055139Storek 		return (0);
81155139Storek 	m->m_pkthdr.rcvif = ifp;
81255139Storek 	m->m_pkthdr.len = totlen;
81355139Storek 	m->m_len = MHLEN;
81455139Storek 
81555139Storek 	while (totlen > 0) {
81655139Storek 		if (top) {
81755139Storek 			MGET(m, M_DONTWAIT, MT_DATA);
81855139Storek 			if (m == 0) {
81955139Storek 				m_freem(top);
82055139Storek 				return (0);
82155139Storek 			}
82255139Storek 			m->m_len = MLEN;
82355139Storek 		}
82455139Storek 		len = min(totlen, epkt - cp);
82555139Storek 		if (len >= MINCLSIZE) {
82655139Storek 			MCLGET(m, M_DONTWAIT);
82755139Storek 			if (m->m_flags & M_EXT)
82855139Storek 				m->m_len = len = min(len, MCLBYTES);
82955139Storek 			else
83055139Storek 				len = m->m_len;
83155139Storek 		} else {
83255139Storek 			/*
83355139Storek 			 * Place initial small packet/header at end of mbuf.
83455139Storek 			 */
83555139Storek 			if (len < m->m_len) {
83655139Storek 				if (top == 0 && len + max_linkhdr <= m->m_len)
83755139Storek 					m->m_data += max_linkhdr;
83855139Storek 				m->m_len = len;
83955139Storek 			} else
84055139Storek 				len = m->m_len;
84155139Storek 		}
84255139Storek 		bcopy(cp, mtod(m, caddr_t), (unsigned)len);
84355139Storek 		cp += len;
84455139Storek 		*mp = m;
84555139Storek 		mp = &m->m_next;
84655139Storek 		totlen -= len;
84755139Storek 		if (cp == epkt)
84855139Storek 			cp = lebuf;
84955139Storek 	}
85055139Storek 	return (top);
85155139Storek }
85255139Storek 
85355139Storek /*
85455139Storek  * Process an ioctl request.
85555139Storek  */
85655139Storek int
85755139Storek leioctl(ifp, cmd, data)
85855139Storek 	register struct ifnet *ifp;
85955139Storek 	int cmd;
86055139Storek 	caddr_t data;
86155139Storek {
86255139Storek 	register struct ifaddr *ifa;
86355139Storek 	register struct le_softc *sc = lecd.cd_devs[ifp->if_unit];
86455139Storek 	register volatile struct lereg1 *ler1;
86555139Storek 	int s = splimp(), error = 0;
86655139Storek 
86755139Storek 	switch (cmd) {
86855139Storek 
86955139Storek 	case SIOCSIFADDR:
87055139Storek 		ifa = (struct ifaddr *)data;
87155139Storek 		ifp->if_flags |= IFF_UP;
87255139Storek 		switch (ifa->ifa_addr->sa_family) {
87355139Storek #ifdef INET
87455139Storek 		case AF_INET:
87555139Storek 			(void)leinit(ifp->if_unit);	/* before arpwhohas */
87655139Storek 			((struct arpcom *)ifp)->ac_ipaddr =
87755139Storek 				IA_SIN(ifa)->sin_addr;
87855139Storek 			arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr);
87955139Storek 			break;
88055139Storek #endif
88155139Storek #ifdef NS
88255139Storek 		case AF_NS:
88355139Storek 		    {
88455139Storek 			register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr);
88555139Storek 
88655139Storek 			if (ns_nullhost(*ina))
88755139Storek 				ina->x_host = *(union ns_host *)(sc->sc_addr);
88855139Storek 			else {
88955139Storek 				/*
89055139Storek 				 * The manual says we can't change the address
89155139Storek 				 * while the receiver is armed,
89255139Storek 				 * so reset everything
89355139Storek 				 */
89455139Storek 				ifp->if_flags &= ~IFF_RUNNING;
89555139Storek 				bcopy((caddr_t)ina->x_host.c_host,
89655139Storek 				    (caddr_t)sc->sc_addr, sizeof(sc->sc_addr));
89755139Storek 			}
89855139Storek 			(void)leinit(ifp->if_unit);	/* does le_setaddr() */
89955139Storek 			break;
90055139Storek 		    }
90155139Storek #endif
90255139Storek 		default:
90355139Storek 			(void)leinit(ifp->if_unit);
90455139Storek 			break;
90555139Storek 		}
90655139Storek 		break;
90755139Storek 
90855139Storek 	case SIOCSIFFLAGS:
90955139Storek 		ler1 = sc->sc_r1;
91055139Storek 		if ((ifp->if_flags & IFF_UP) == 0 &&
91155139Storek 		    ifp->if_flags & IFF_RUNNING) {
91255139Storek 			ler1->ler1_rdp = LE_C0_STOP;
91355139Storek 			ifp->if_flags &= ~IFF_RUNNING;
91455139Storek 		} else if (ifp->if_flags & IFF_UP &&
91555139Storek 		    (ifp->if_flags & IFF_RUNNING) == 0)
91655139Storek 			(void)leinit(ifp->if_unit);
91755139Storek 		/*
91855139Storek 		 * If the state of the promiscuous bit changes, the interface
91955139Storek 		 * must be reset to effect the change.
92055139Storek 		 */
92155139Storek 		if (((ifp->if_flags ^ sc->sc_iflags) & IFF_PROMISC) &&
92255139Storek 		    (ifp->if_flags & IFF_RUNNING)) {
92355139Storek 			sc->sc_iflags = ifp->if_flags;
92455139Storek 			lereset((struct device *)sc);
92555139Storek 			lestart(ifp);
92655139Storek 		}
92755139Storek 		break;
92855139Storek 
92955139Storek 	case SIOCADDMULTI:
930*59215Storek 		error = ether_addmulti((struct ifreq *)data, &sc->sc_ac);
931*59215Storek 		goto update_multicast;
932*59215Storek 
93355139Storek 	case SIOCDELMULTI:
934*59215Storek 		error = ether_delmulti((struct ifreq *)data, &sc->sc_ac);
935*59215Storek 	update_multicast:
93655139Storek 		if (error == ENETRESET) {
93755139Storek 			/*
93855139Storek 			 * Multicast list has changed; set the hardware
93955139Storek 			 * filter accordingly.
94055139Storek 			 */
94155139Storek 			lereset((struct device *)sc);
94255139Storek 			error = 0;
94355139Storek 		}
94455139Storek 		break;
94555139Storek 
94655139Storek 	default:
94755139Storek 		error = EINVAL;
94855139Storek 	}
94955139Storek 	splx(s);
95055139Storek 	return (error);
95155139Storek }
95255139Storek 
95355139Storek void
95455139Storek leerror(sc, stat)
95555139Storek 	register struct le_softc *sc;
95655139Storek 	int stat;
95755139Storek {
95855139Storek 	if (!ledebug)
95955139Storek 		return;
96055139Storek 
96155139Storek 	/*
96255139Storek 	 * Not all transceivers implement heartbeat
96355139Storek 	 * so we only log CERR once.
96455139Storek 	 */
96555139Storek 	if ((stat & LE_C0_CERR) && sc->sc_cerr)
96655139Storek 		return;
96755139Storek 	log(LOG_WARNING, "%s: error: stat=%b\n",
96855139Storek 	    sc->sc_dev.dv_xname, stat, LE_C0_BITS);
96955139Storek }
97055139Storek 
97155139Storek void
97255139Storek lererror(sc, msg)
97355139Storek 	register struct le_softc *sc;
97455139Storek 	char *msg;
97555139Storek {
97655139Storek 	register volatile struct lermd *rmd;
97755139Storek 	int len;
97855139Storek 
97955139Storek 	if (!ledebug)
98055139Storek 		return;
98155139Storek 
98255139Storek 	rmd = &sc->sc_r2->ler2_rmd[sc->sc_rmd];
98355139Storek 	len = rmd->rmd3;
98455139Storek 	log(LOG_WARNING, "%s: ierror(%s): from %s: buf=%d, len=%d, rmd1=%b\n",
98555139Storek 	    sc->sc_dev.dv_xname, msg, len > 11 ?
98655139Storek 	    ether_sprintf((u_char *)&sc->sc_r2->ler2_rbuf[sc->sc_rmd][6]) :
98755139Storek 	    "unknown",
98855139Storek 	    sc->sc_rmd, len, rmd->rmd1_bits, LE_R1_BITS);
98955139Storek }
99055139Storek 
99155139Storek void
99255139Storek lexerror(sc)
99355139Storek 	register struct le_softc *sc;
99455139Storek {
99555139Storek 	register volatile struct letmd *tmd;
99655139Storek 	register int len, tmd3, tdr;
99755139Storek 
99855139Storek 	if (!ledebug)
99955139Storek 		return;
100055139Storek 
100155139Storek 	tmd = sc->sc_r2->ler2_tmd;
100255139Storek 	tmd3 = tmd->tmd3;
100355139Storek 	tdr = tmd3 & LE_T3_TDR_MASK;
100455139Storek 	len = -tmd->tmd2;
100555139Storek 	log(LOG_WARNING,
100655139Storek     "%s: oerror: to %s: buf=%d, len=%d, tmd1=%b, tmd3=%b, tdr=%d (%d nsecs)\n",
100755139Storek 	    sc->sc_dev.dv_xname, len > 5 ?
100855139Storek 	    ether_sprintf((u_char *)&sc->sc_r2->ler2_tbuf[0][0]) : "unknown",
100955139Storek 	    0, len,
101055139Storek 	    tmd->tmd1_bits, LE_T1_BITS,
101155139Storek 	    tmd3, LE_T3_BITS, tdr, tdr * 100);
101255139Storek }
1013