xref: /csrg-svn/sys/pmax/dev/if_le.c (revision 52130)
1*52130Smckusick /*
2*52130Smckusick  * Copyright (c) 1992 Regents of the University of California.
3*52130Smckusick  * All rights reserved.
4*52130Smckusick  *
5*52130Smckusick  * This code is derived from software contributed to Berkeley by
6*52130Smckusick  * Ralph Campbell.
7*52130Smckusick  *
8*52130Smckusick  * %sccs.include.redist.c%
9*52130Smckusick  *
10*52130Smckusick  *	@(#)if_le.c	7.1 (Berkeley) 01/07/92
11*52130Smckusick  */
12*52130Smckusick 
13*52130Smckusick #include "le.h"
14*52130Smckusick #if NLE > 0
15*52130Smckusick 
16*52130Smckusick #include "bpfilter.h"
17*52130Smckusick 
18*52130Smckusick /*
19*52130Smckusick  * AMD 7990 LANCE
20*52130Smckusick  *
21*52130Smckusick  * This driver will generate and accept trailer encapsulated packets even
22*52130Smckusick  * though it buys us nothing.  The motivation was to avoid incompatibilities
23*52130Smckusick  * with VAXen, SUNs, and others that handle and benefit from them.
24*52130Smckusick  * This reasoning is dubious.
25*52130Smckusick  */
26*52130Smckusick #include "param.h"
27*52130Smckusick #include "systm.h"
28*52130Smckusick #include "mbuf.h"
29*52130Smckusick #include "buf.h"
30*52130Smckusick #include "protosw.h"
31*52130Smckusick #include "socket.h"
32*52130Smckusick #include "syslog.h"
33*52130Smckusick #include "ioctl.h"
34*52130Smckusick #include "errno.h"
35*52130Smckusick 
36*52130Smckusick #include "net/if.h"
37*52130Smckusick #include "net/netisr.h"
38*52130Smckusick #include "net/route.h"
39*52130Smckusick 
40*52130Smckusick #ifdef INET
41*52130Smckusick #include "netinet/in.h"
42*52130Smckusick #include "netinet/in_systm.h"
43*52130Smckusick #include "netinet/in_var.h"
44*52130Smckusick #include "netinet/ip.h"
45*52130Smckusick #include "netinet/if_ether.h"
46*52130Smckusick #endif
47*52130Smckusick 
48*52130Smckusick #ifdef NS
49*52130Smckusick #include "netns/ns.h"
50*52130Smckusick #include "netns/ns_if.h"
51*52130Smckusick #endif
52*52130Smckusick 
53*52130Smckusick #ifdef RMP
54*52130Smckusick #include "netrmp/rmp.h"
55*52130Smckusick #include "netrmp/rmp_var.h"
56*52130Smckusick #endif
57*52130Smckusick 
58*52130Smckusick #include "machine/machConst.h"
59*52130Smckusick #include "device.h"
60*52130Smckusick #include "if_lereg.h"
61*52130Smckusick 
62*52130Smckusick #if NBPFILTER > 0
63*52130Smckusick #include "../net/bpf.h"
64*52130Smckusick #include "../net/bpfdesc.h"
65*52130Smckusick #endif
66*52130Smckusick 
67*52130Smckusick int	leprobe();
68*52130Smckusick struct	driver ledriver = {
69*52130Smckusick 	"le", leprobe,
70*52130Smckusick };
71*52130Smckusick 
72*52130Smckusick int	ledebug = 1;		/* console error messages */
73*52130Smckusick 
74*52130Smckusick int	leintr(), leinit(), leioctl(), lestart(), ether_output();
75*52130Smckusick struct	mbuf *leget();
76*52130Smckusick 
77*52130Smckusick /*
78*52130Smckusick  * Ethernet software status per interface.
79*52130Smckusick  *
80*52130Smckusick  * Each interface is referenced by a network interface structure,
81*52130Smckusick  * le_if, which the routing code uses to locate the interface.
82*52130Smckusick  * This structure contains the output queue for the interface, its address, ...
83*52130Smckusick  */
84*52130Smckusick struct	le_softc {
85*52130Smckusick 	struct	arpcom sc_ac;	/* common Ethernet structures */
86*52130Smckusick #define	sc_if	sc_ac.ac_if	/* network-visible interface */
87*52130Smckusick #define	sc_addr	sc_ac.ac_enaddr	/* hardware Ethernet address */
88*52130Smckusick 	volatile struct	lereg1 *sc_r1;	/* LANCE registers */
89*52130Smckusick 	volatile struct	lereg2 *sc_r2;	/* dual-port RAM */
90*52130Smckusick 	int	sc_rmd;		/* predicted next rmd to process */
91*52130Smckusick 	int	sc_tmd;		/* last tmd processed */
92*52130Smckusick 	int	sc_tmdnext;	/* next tmd to transmit with */
93*52130Smckusick 	int	sc_runt;
94*52130Smckusick 	int	sc_jab;
95*52130Smckusick 	int	sc_merr;
96*52130Smckusick 	int	sc_babl;
97*52130Smckusick 	int	sc_cerr;
98*52130Smckusick 	int	sc_miss;
99*52130Smckusick 	int	sc_xint;
100*52130Smckusick 	int	sc_xown;
101*52130Smckusick 	int	sc_uflo;
102*52130Smckusick 	int	sc_rxlen;
103*52130Smckusick 	int	sc_rxoff;
104*52130Smckusick 	int	sc_txoff;
105*52130Smckusick 	int	sc_busy;
106*52130Smckusick 	short	sc_iflags;
107*52130Smckusick #if NBPFILTER > 0
108*52130Smckusick 	caddr_t sc_bpf;
109*52130Smckusick #endif
110*52130Smckusick } le_softc[NLE];
111*52130Smckusick 
112*52130Smckusick /* access LANCE registers */
113*52130Smckusick #define	LERDWR(cntl, src, dst)	{ (dst) = (src); DELAY(10); }
114*52130Smckusick 
115*52130Smckusick #define CPU_TO_CHIP_ADDR(cpu) \
116*52130Smckusick 	(((unsigned)(&(((struct lereg2 *)0)->cpu))) >> 1)
117*52130Smckusick 
118*52130Smckusick /*
119*52130Smckusick  * Test to see if device is present.
120*52130Smckusick  * Return true if found and initialized ok.
121*52130Smckusick  * If interface exists, make available by filling in network interface
122*52130Smckusick  * record.  System will initialize the interface when it is ready
123*52130Smckusick  * to accept packets.
124*52130Smckusick  */
125*52130Smckusick leprobe(dp)
126*52130Smckusick 	struct pmax_ctlr *dp;
127*52130Smckusick {
128*52130Smckusick 	volatile struct lereg1 *ler1;
129*52130Smckusick 	struct le_softc *le = &le_softc[dp->pmax_unit];
130*52130Smckusick 	struct ifnet *ifp = &le->sc_if;
131*52130Smckusick 	u_char *cp;
132*52130Smckusick 	int i;
133*52130Smckusick 
134*52130Smckusick 	le->sc_r1 = ler1 = (volatile struct lereg1 *)dp->pmax_addr;
135*52130Smckusick 	le->sc_r2 = (volatile struct lereg2 *)MACH_NETWORK_BUFFER_ADDR;
136*52130Smckusick 
137*52130Smckusick 	/*
138*52130Smckusick 	 * Read the ethernet address.
139*52130Smckusick 	 * See "DECstation 3100 Desktop Workstation Functional Specification".
140*52130Smckusick 	 */
141*52130Smckusick 	cp = (u_char *)(MACH_CLOCK_ADDR + 1);
142*52130Smckusick 	for (i = 0; i < sizeof(le->sc_addr); i++) {
143*52130Smckusick 		le->sc_addr[i] = *cp;
144*52130Smckusick 		cp += 4;
145*52130Smckusick 	}
146*52130Smckusick 
147*52130Smckusick 	/* make sure the chip is stopped */
148*52130Smckusick 	LERDWR(ler0, LE_CSR0, ler1->ler1_rap);
149*52130Smckusick 	LERDWR(ler0, LE_STOP, ler1->ler1_rdp);
150*52130Smckusick 
151*52130Smckusick 	ifp->if_unit = dp->pmax_unit;
152*52130Smckusick 	ifp->if_name = "le";
153*52130Smckusick 	ifp->if_mtu = ETHERMTU;
154*52130Smckusick 	ifp->if_init = leinit;
155*52130Smckusick 	ifp->if_ioctl = leioctl;
156*52130Smckusick 	ifp->if_output = ether_output;
157*52130Smckusick 	ifp->if_start = lestart;
158*52130Smckusick 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX;
159*52130Smckusick #if NBPFILTER > 0
160*52130Smckusick 	bpfattach(&le->sc_bpf, ifp, DLT_EN10MB, sizeof(struct ether_header));
161*52130Smckusick #endif
162*52130Smckusick 	if_attach(ifp);
163*52130Smckusick 
164*52130Smckusick 	printf("le%d at nexus0 csr 0x%x ethernet address %s\n", dp->pmax_unit,
165*52130Smckusick 		dp->pmax_addr, ether_sprintf(le->sc_addr));
166*52130Smckusick 	return (1);
167*52130Smckusick }
168*52130Smckusick 
169*52130Smckusick ledrinit(ler2)
170*52130Smckusick 	register volatile struct lereg2 *ler2;
171*52130Smckusick {
172*52130Smckusick 	register int i;
173*52130Smckusick 
174*52130Smckusick 	for (i = 0; i < LERBUF; i++) {
175*52130Smckusick 		ler2->ler2_rmd[i].rmd0 = CPU_TO_CHIP_ADDR(ler2_rbuf[i][0]);
176*52130Smckusick 		ler2->ler2_rmd[i].rmd1 = LE_OWN;
177*52130Smckusick 		ler2->ler2_rmd[i].rmd2 = -LEMTU;
178*52130Smckusick 		ler2->ler2_rmd[i].rmd3 = 0;
179*52130Smckusick 	}
180*52130Smckusick 	for (i = 0; i < LETBUF; i++) {
181*52130Smckusick 		ler2->ler2_tmd[i].tmd0 = CPU_TO_CHIP_ADDR(ler2_tbuf[i][0]);
182*52130Smckusick 		ler2->ler2_tmd[i].tmd1 = 0;
183*52130Smckusick 		ler2->ler2_tmd[i].tmd2 = 0;
184*52130Smckusick 		ler2->ler2_tmd[i].tmd3 = 0;
185*52130Smckusick 	}
186*52130Smckusick }
187*52130Smckusick 
188*52130Smckusick lereset(unit)
189*52130Smckusick 	register int unit;
190*52130Smckusick {
191*52130Smckusick 	register struct le_softc *le = &le_softc[unit];
192*52130Smckusick 	register volatile struct lereg1 *ler1 = le->sc_r1;
193*52130Smckusick 	register volatile struct lereg2 *ler2 = le->sc_r2;
194*52130Smckusick 	register int timo = 100000;
195*52130Smckusick 	register int stat;
196*52130Smckusick 
197*52130Smckusick #ifdef lint
198*52130Smckusick 	stat = unit;
199*52130Smckusick #endif
200*52130Smckusick #if NBPFILTER > 0
201*52130Smckusick 	if (le->sc_if.if_flags & IFF_PROMISC)
202*52130Smckusick 		/* set the promiscuous bit */
203*52130Smckusick 		le->sc_r2->ler2_mode = LE_MODE|0x8000;
204*52130Smckusick 	else
205*52130Smckusick 		le->sc_r2->ler2_mode = LE_MODE;
206*52130Smckusick #endif
207*52130Smckusick 	LERDWR(ler0, LE_CSR0, ler1->ler1_rap);
208*52130Smckusick 	LERDWR(ler0, LE_STOP, ler1->ler1_rdp);
209*52130Smckusick 
210*52130Smckusick 	/*
211*52130Smckusick 	 * Setup for transmit/receive
212*52130Smckusick 	 */
213*52130Smckusick 	ler2->ler2_mode = LE_MODE;
214*52130Smckusick 	ler2->ler2_padr0 = (le->sc_addr[1] << 8) | le->sc_addr[0];
215*52130Smckusick 	ler2->ler2_padr1 = (le->sc_addr[3] << 8) | le->sc_addr[2];
216*52130Smckusick 	ler2->ler2_padr2 = (le->sc_addr[5] << 8) | le->sc_addr[4];
217*52130Smckusick #ifdef RMP
218*52130Smckusick 	/*
219*52130Smckusick 	 * Set up logical addr filter to accept multicast 9:0:9:0:0:4
220*52130Smckusick 	 * This should be an ioctl() to the driver.  (XXX)
221*52130Smckusick 	 */
222*52130Smckusick 	ler2->ler2_ladrf0 = 0x0010;
223*52130Smckusick 	ler2->ler2_ladrf1 = 0x0;
224*52130Smckusick 	ler2->ler2_ladrf2 = 0x0;
225*52130Smckusick 	ler2->ler2_ladrf3 = 0x0;
226*52130Smckusick #else
227*52130Smckusick 	ler2->ler2_ladrf0 = 0;
228*52130Smckusick 	ler2->ler2_ladrf1 = 0;
229*52130Smckusick 	ler2->ler2_ladrf2 = 0;
230*52130Smckusick 	ler2->ler2_ladrf3 = 0;
231*52130Smckusick #endif
232*52130Smckusick 	ler2->ler2_rlen = LE_RLEN;
233*52130Smckusick 	ler2->ler2_rdra = CPU_TO_CHIP_ADDR(ler2_rmd[0]);
234*52130Smckusick 	ler2->ler2_tlen = LE_TLEN;
235*52130Smckusick 	ler2->ler2_tdra = CPU_TO_CHIP_ADDR(ler2_tmd[0]);
236*52130Smckusick 	ledrinit(ler2);
237*52130Smckusick 	le->sc_rmd = 0;
238*52130Smckusick 	le->sc_tmd = LETBUF - 1;
239*52130Smckusick 	le->sc_tmdnext = 0;
240*52130Smckusick 
241*52130Smckusick 	LERDWR(ler0, LE_CSR1, ler1->ler1_rap);
242*52130Smckusick 	LERDWR(ler0, CPU_TO_CHIP_ADDR(ler2_mode), ler1->ler1_rdp);
243*52130Smckusick 	LERDWR(ler0, LE_CSR2, ler1->ler1_rap);
244*52130Smckusick 	LERDWR(ler0, 0, ler1->ler1_rdp);
245*52130Smckusick 	LERDWR(ler0, LE_CSR3, ler1->ler1_rap);
246*52130Smckusick 	LERDWR(ler0, 0, ler1->ler1_rdp);
247*52130Smckusick 	LERDWR(ler0, LE_CSR0, ler1->ler1_rap);
248*52130Smckusick 	LERDWR(ler0, LE_INIT, ler1->ler1_rdp);
249*52130Smckusick 	MachEmptyWriteBuffer();
250*52130Smckusick 	do {
251*52130Smckusick 		if (--timo == 0) {
252*52130Smckusick 			printf("le%d: init timeout, stat = 0x%x\n",
253*52130Smckusick 			       unit, stat);
254*52130Smckusick 			break;
255*52130Smckusick 		}
256*52130Smckusick 		LERDWR(ler0, ler1->ler1_rdp, stat);
257*52130Smckusick 	} while ((stat & LE_IDON) == 0);
258*52130Smckusick 	LERDWR(ler0, LE_IDON, ler1->ler1_rdp);
259*52130Smckusick 	LERDWR(ler0, LE_STRT | LE_INEA, ler1->ler1_rdp);
260*52130Smckusick 	MachEmptyWriteBuffer();
261*52130Smckusick 	le->sc_if.if_flags &= ~IFF_OACTIVE;
262*52130Smckusick }
263*52130Smckusick 
264*52130Smckusick /*
265*52130Smckusick  * Initialization of interface
266*52130Smckusick  */
267*52130Smckusick leinit(unit)
268*52130Smckusick 	int unit;
269*52130Smckusick {
270*52130Smckusick 	struct le_softc *le = &le_softc[unit];
271*52130Smckusick 	register struct ifnet *ifp = &le->sc_if;
272*52130Smckusick 	int s;
273*52130Smckusick 
274*52130Smckusick 	/* not yet, if address still unknown */
275*52130Smckusick 	if (ifp->if_addrlist == (struct ifaddr *)0)
276*52130Smckusick 		return;
277*52130Smckusick 	if ((ifp->if_flags & IFF_RUNNING) == 0) {
278*52130Smckusick 		s = splnet();
279*52130Smckusick 		ifp->if_flags |= IFF_RUNNING;
280*52130Smckusick 		lereset(unit);
281*52130Smckusick 	        (void) lestart(ifp);
282*52130Smckusick 		splx(s);
283*52130Smckusick 	}
284*52130Smckusick }
285*52130Smckusick 
286*52130Smckusick #define	LENEXTTMP \
287*52130Smckusick 	if (++bix == LETBUF) bix = 0, tmd = le->sc_r2->ler2_tmd; else ++tmd
288*52130Smckusick 
289*52130Smckusick /*
290*52130Smckusick  * Start output on interface.  Get another datagram to send
291*52130Smckusick  * off of the interface queue, and copy it to the interface
292*52130Smckusick  * before starting the output.
293*52130Smckusick  */
294*52130Smckusick lestart(ifp)
295*52130Smckusick 	struct ifnet *ifp;
296*52130Smckusick {
297*52130Smckusick 	register struct le_softc *le = &le_softc[ifp->if_unit];
298*52130Smckusick 	register int bix = le->sc_tmdnext;
299*52130Smckusick 	register volatile struct letmd *tmd = &le->sc_r2->ler2_tmd[bix];
300*52130Smckusick 	register struct mbuf *m;
301*52130Smckusick 	int len = 0;
302*52130Smckusick 
303*52130Smckusick 	if ((le->sc_if.if_flags & IFF_RUNNING) == 0)
304*52130Smckusick 		return (0);
305*52130Smckusick 	while (bix != le->sc_tmd) {
306*52130Smckusick 		if (tmd->tmd1 & LE_OWN)
307*52130Smckusick 			panic("lestart");
308*52130Smckusick 		IF_DEQUEUE(&le->sc_if.if_snd, m);
309*52130Smckusick 		if (m == 0)
310*52130Smckusick 			break;
311*52130Smckusick 		len = leput(le->sc_r2->ler2_tbuf[bix], m);
312*52130Smckusick #if NBPFILTER > 0
313*52130Smckusick 		/*
314*52130Smckusick 		 * If bpf is listening on this interface, let it
315*52130Smckusick 		 * see the packet before we commit it to the wire.
316*52130Smckusick 		 */
317*52130Smckusick 		if (le->sc_bpf)
318*52130Smckusick 			bpf_tap(le->sc_bpf, le->sc_r2->ler2_tbuf[bix], len);
319*52130Smckusick #endif
320*52130Smckusick 		tmd->tmd3 = 0;
321*52130Smckusick 		tmd->tmd2 = -len;
322*52130Smckusick 		tmd->tmd1 = LE_OWN | LE_STP | LE_ENP;
323*52130Smckusick 		LENEXTTMP;
324*52130Smckusick 	}
325*52130Smckusick 	if (len != 0) {
326*52130Smckusick 		le->sc_if.if_flags |= IFF_OACTIVE;
327*52130Smckusick 		LERDWR(ler0, LE_TDMD | LE_INEA, le->sc_r1->ler1_rdp);
328*52130Smckusick 		MachEmptyWriteBuffer();
329*52130Smckusick 	}
330*52130Smckusick 	le->sc_tmdnext = bix;
331*52130Smckusick 	return (0);
332*52130Smckusick }
333*52130Smckusick 
334*52130Smckusick /*
335*52130Smckusick  * Process interrupts from the 7990 chip.
336*52130Smckusick  */
337*52130Smckusick leintr()
338*52130Smckusick {
339*52130Smckusick 	register struct le_softc *le;
340*52130Smckusick 	register volatile struct lereg1 *ler1;
341*52130Smckusick 	register int unit, stat;
342*52130Smckusick 
343*52130Smckusick 	/* only one unit right now; should be a loop. */
344*52130Smckusick 	unit = 0;
345*52130Smckusick 	le = &le_softc[unit];
346*52130Smckusick 	ler1 = le->sc_r1;
347*52130Smckusick 	stat = ler1->ler1_rdp;
348*52130Smckusick 	if (!(stat & LE_INTR)) {
349*52130Smckusick 		printf("le?: spurrious interrupt\n");
350*52130Smckusick 		return;
351*52130Smckusick 	}
352*52130Smckusick 	if (stat & LE_SERR) {
353*52130Smckusick 		leerror(unit, stat);
354*52130Smckusick 		if (stat & LE_MERR) {
355*52130Smckusick 			le->sc_merr++;
356*52130Smckusick 			lereset(unit);
357*52130Smckusick 			return;
358*52130Smckusick 		}
359*52130Smckusick 		if (stat & LE_BABL)
360*52130Smckusick 			le->sc_babl++;
361*52130Smckusick 		if (stat & LE_CERR)
362*52130Smckusick 			le->sc_cerr++;
363*52130Smckusick 		if (stat & LE_MISS)
364*52130Smckusick 			le->sc_miss++;
365*52130Smckusick 		LERDWR(ler0, LE_BABL|LE_CERR|LE_MISS|LE_INEA, ler1->ler1_rdp);
366*52130Smckusick 		MachEmptyWriteBuffer();
367*52130Smckusick 	}
368*52130Smckusick 	if ((stat & LE_RXON) == 0) {
369*52130Smckusick 		le->sc_rxoff++;
370*52130Smckusick 		lereset(unit);
371*52130Smckusick 		return;
372*52130Smckusick 	}
373*52130Smckusick 	if ((stat & LE_TXON) == 0) {
374*52130Smckusick 		le->sc_txoff++;
375*52130Smckusick 		lereset(unit);
376*52130Smckusick 		return;
377*52130Smckusick 	}
378*52130Smckusick 	if (stat & LE_RINT) {
379*52130Smckusick 		/* interrupt is cleared in lerint */
380*52130Smckusick 		lerint(unit);
381*52130Smckusick 	}
382*52130Smckusick 	if (stat & LE_TINT) {
383*52130Smckusick 		LERDWR(ler0, LE_TINT|LE_INEA, ler1->ler1_rdp);
384*52130Smckusick 		MachEmptyWriteBuffer();
385*52130Smckusick 		lexint(unit);
386*52130Smckusick 	}
387*52130Smckusick }
388*52130Smckusick 
389*52130Smckusick /*
390*52130Smckusick  * Ethernet interface transmitter interrupt.
391*52130Smckusick  * Start another output if more data to send.
392*52130Smckusick  */
393*52130Smckusick lexint(unit)
394*52130Smckusick 	register int unit;
395*52130Smckusick {
396*52130Smckusick 	register struct le_softc *le = &le_softc[unit];
397*52130Smckusick 	register int bix = le->sc_tmd;
398*52130Smckusick 	register volatile struct letmd *tmd = &le->sc_r2->ler2_tmd[bix];
399*52130Smckusick 
400*52130Smckusick 	if ((le->sc_if.if_flags & IFF_OACTIVE) == 0) {
401*52130Smckusick 		le->sc_xint++;
402*52130Smckusick 		return;
403*52130Smckusick 	}
404*52130Smckusick 	LENEXTTMP;
405*52130Smckusick 	while (bix != le->sc_tmdnext && (tmd->tmd1 & LE_OWN) == 0) {
406*52130Smckusick 		le->sc_tmd = bix;
407*52130Smckusick 		if ((tmd->tmd1 & LE_ERR) || (tmd->tmd3 & LE_TBUFF)) {
408*52130Smckusick 			lexerror(unit);
409*52130Smckusick 			le->sc_if.if_oerrors++;
410*52130Smckusick 			if (tmd->tmd3 & (LE_TBUFF|LE_UFLO)) {
411*52130Smckusick 				le->sc_uflo++;
412*52130Smckusick 				lereset(unit);
413*52130Smckusick 				break;
414*52130Smckusick 			}
415*52130Smckusick 			else if (tmd->tmd3 & LE_LCOL)
416*52130Smckusick 				le->sc_if.if_collisions++;
417*52130Smckusick 			else if (tmd->tmd3 & LE_RTRY)
418*52130Smckusick 				le->sc_if.if_collisions += 16;
419*52130Smckusick 		}
420*52130Smckusick 		else if (tmd->tmd1 & LE_ONE)
421*52130Smckusick 			le->sc_if.if_collisions++;
422*52130Smckusick 		else if (tmd->tmd1 & LE_MORE)
423*52130Smckusick 			/* what is the real number? */
424*52130Smckusick 			le->sc_if.if_collisions += 2;
425*52130Smckusick 		else
426*52130Smckusick 			le->sc_if.if_opackets++;
427*52130Smckusick 		LENEXTTMP;
428*52130Smckusick 	}
429*52130Smckusick 	if (bix == le->sc_tmdnext)
430*52130Smckusick 		le->sc_if.if_flags &= ~IFF_OACTIVE;
431*52130Smckusick 	(void) lestart(&le->sc_if);
432*52130Smckusick }
433*52130Smckusick 
434*52130Smckusick #define	LENEXTRMP \
435*52130Smckusick 	if (++bix == LERBUF) bix = 0, rmd = le->sc_r2->ler2_rmd; else ++rmd
436*52130Smckusick 
437*52130Smckusick /*
438*52130Smckusick  * Ethernet interface receiver interrupt.
439*52130Smckusick  * If input error just drop packet.
440*52130Smckusick  * Decapsulate packet based on type and pass to type specific
441*52130Smckusick  * higher-level input routine.
442*52130Smckusick  */
443*52130Smckusick lerint(unit)
444*52130Smckusick 	int unit;
445*52130Smckusick {
446*52130Smckusick 	register struct le_softc *le = &le_softc[unit];
447*52130Smckusick 	register int bix = le->sc_rmd;
448*52130Smckusick 	register volatile struct lermd *rmd = &le->sc_r2->ler2_rmd[bix];
449*52130Smckusick 
450*52130Smckusick 	/*
451*52130Smckusick 	 * Out of sync with hardware, should never happen?
452*52130Smckusick 	 */
453*52130Smckusick 	if (rmd->rmd1 & LE_OWN) {
454*52130Smckusick 		LERDWR(le->sc_r0, LE_RINT|LE_INEA, le->sc_r1->ler1_rdp);
455*52130Smckusick 		MachEmptyWriteBuffer();
456*52130Smckusick 		return;
457*52130Smckusick 	}
458*52130Smckusick 
459*52130Smckusick 	/*
460*52130Smckusick 	 * Process all buffers with valid data
461*52130Smckusick 	 */
462*52130Smckusick 	while ((rmd->rmd1 & LE_OWN) == 0) {
463*52130Smckusick 		int len = rmd->rmd3;
464*52130Smckusick 
465*52130Smckusick 		/* Clear interrupt to avoid race condition */
466*52130Smckusick 		LERDWR(le->sc_r0, LE_RINT|LE_INEA, le->sc_r1->ler1_rdp);
467*52130Smckusick 		MachEmptyWriteBuffer();
468*52130Smckusick 
469*52130Smckusick 		if (rmd->rmd1 & LE_ERR) {
470*52130Smckusick 			le->sc_rmd = bix;
471*52130Smckusick 			lererror(unit, "bad packet");
472*52130Smckusick 			le->sc_if.if_ierrors++;
473*52130Smckusick 		} else if ((rmd->rmd1 & (LE_STP|LE_ENP)) != (LE_STP|LE_ENP)) {
474*52130Smckusick 			/*
475*52130Smckusick 			 * Find the end of the packet so we can see how long
476*52130Smckusick 			 * it was.  We still throw it away.
477*52130Smckusick 			 */
478*52130Smckusick 			do {
479*52130Smckusick 				LERDWR(le->sc_r0, LE_RINT|LE_INEA,
480*52130Smckusick 				       le->sc_r1->ler1_rdp);
481*52130Smckusick 				MachEmptyWriteBuffer();
482*52130Smckusick 				rmd->rmd3 = 0;
483*52130Smckusick 				rmd->rmd1 = LE_OWN;
484*52130Smckusick 				LENEXTRMP;
485*52130Smckusick 			} while (!(rmd->rmd1 & (LE_OWN|LE_ERR|LE_STP|LE_ENP)));
486*52130Smckusick 			le->sc_rmd = bix;
487*52130Smckusick 			lererror(unit, "chained buffer");
488*52130Smckusick 			le->sc_rxlen++;
489*52130Smckusick 			/*
490*52130Smckusick 			 * If search terminated without successful completion
491*52130Smckusick 			 * we reset the hardware (conservative).
492*52130Smckusick 			 */
493*52130Smckusick 			if ((rmd->rmd1 & (LE_OWN|LE_ERR|LE_STP|LE_ENP)) !=
494*52130Smckusick 			    LE_ENP) {
495*52130Smckusick 				lereset(unit);
496*52130Smckusick 				return;
497*52130Smckusick 			}
498*52130Smckusick 		} else
499*52130Smckusick 			leread(unit, le->sc_r2->ler2_rbuf[bix], len);
500*52130Smckusick 		rmd->rmd3 = 0;
501*52130Smckusick 		rmd->rmd1 = LE_OWN;
502*52130Smckusick 		LENEXTRMP;
503*52130Smckusick 	}
504*52130Smckusick 	MachEmptyWriteBuffer();		/* Paranoia */
505*52130Smckusick 	le->sc_rmd = bix;
506*52130Smckusick }
507*52130Smckusick 
508*52130Smckusick /*
509*52130Smckusick  * Look at the packet in network buffer memory so we can be smart about how
510*52130Smckusick  * we copy the data into mbufs.
511*52130Smckusick  * This needs work since we can't just read network buffer memory like
512*52130Smckusick  * regular memory.
513*52130Smckusick  */
514*52130Smckusick leread(unit, buf, len)
515*52130Smckusick 	int unit;
516*52130Smckusick 	volatile u_short *buf;
517*52130Smckusick 	int len;
518*52130Smckusick {
519*52130Smckusick 	register struct le_softc *le = &le_softc[unit];
520*52130Smckusick 	struct ether_header et;
521*52130Smckusick     	struct mbuf *m;
522*52130Smckusick 	int off, resid;
523*52130Smckusick 	u_short sbuf[2];
524*52130Smckusick 
525*52130Smckusick 	le->sc_if.if_ipackets++;
526*52130Smckusick 	CopyFromBuffer(buf, (char *)&et, sizeof(et));
527*52130Smckusick 	et.ether_type = ntohs(et.ether_type);
528*52130Smckusick 	/* adjust input length to account for header and CRC */
529*52130Smckusick 	len = len - sizeof(struct ether_header) - 4;
530*52130Smckusick 
531*52130Smckusick #ifdef RMP
532*52130Smckusick 	/*  (XXX)
533*52130Smckusick 	 *
534*52130Smckusick 	 *  If Ethernet Type field is < MaxPacketSize, we probably have
535*52130Smckusick 	 *  a IEEE802 packet here.  Make sure that the size is at least
536*52130Smckusick 	 *  that of the HP LLC.  Also do sanity checks on length of LLC
537*52130Smckusick 	 *  (old Ethernet Type field) and packet length.
538*52130Smckusick 	 *
539*52130Smckusick 	 *  Provided the above checks succeed, change `len' to reflect
540*52130Smckusick 	 *  the length of the LLC (i.e. et.ether_type) and change the
541*52130Smckusick 	 *  type field to ETHERTYPE_IEEE so we can switch() on it later.
542*52130Smckusick 	 *  Yes, this is a hack and will eventually be done "right".
543*52130Smckusick 	 */
544*52130Smckusick 	if (et.ether_type <= IEEE802LEN_MAX && len >= sizeof(struct hp_llc) &&
545*52130Smckusick 	    len >= et.ether_type && len >= IEEE802LEN_MIN) {
546*52130Smckusick 		len = et.ether_type;
547*52130Smckusick 		et.ether_type = ETHERTYPE_IEEE;	/* hack! */
548*52130Smckusick 	}
549*52130Smckusick #endif
550*52130Smckusick 
551*52130Smckusick 	if (et.ether_type >= ETHERTYPE_TRAIL &&
552*52130Smckusick 	    et.ether_type < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) {
553*52130Smckusick 		off = (et.ether_type - ETHERTYPE_TRAIL) * 512;
554*52130Smckusick 		if (off >= ETHERMTU)
555*52130Smckusick 			return;		/* sanity */
556*52130Smckusick 		CopyFromBuffer(buf + (sizeof(et) + off),
557*52130Smckusick 			(char *)sbuf, sizeof(sbuf));
558*52130Smckusick 		et.ether_type = ntohs(sbuf[0]);
559*52130Smckusick 		resid = ntohs(sbuf[1]);
560*52130Smckusick 		if (off + resid > len)
561*52130Smckusick 			return;		/* sanity */
562*52130Smckusick 		len = off + resid;
563*52130Smckusick 	} else
564*52130Smckusick 		off = 0;
565*52130Smckusick 
566*52130Smckusick 	if (len <= 0) {
567*52130Smckusick 		if (ledebug)
568*52130Smckusick 			log(LOG_WARNING,
569*52130Smckusick 			    "le%d: ierror(runt packet): from %s: len=%d\n",
570*52130Smckusick 			    unit, ether_sprintf(et.ether_shost), len);
571*52130Smckusick 		le->sc_runt++;
572*52130Smckusick 		le->sc_if.if_ierrors++;
573*52130Smckusick 		return;
574*52130Smckusick 	}
575*52130Smckusick #if NBPFILTER > 0
576*52130Smckusick 	/*
577*52130Smckusick 	 * Check if there's a bpf filter listening on this interface.
578*52130Smckusick 	 * If so, hand off the raw packet to bpf, which must deal with
579*52130Smckusick 	 * trailers in its own way.
580*52130Smckusick 	 */
581*52130Smckusick 	if (le->sc_bpf) {
582*52130Smckusick 		bpf_tap(le->sc_bpf, buf, len + sizeof(struct ether_header));
583*52130Smckusick 
584*52130Smckusick 		/*
585*52130Smckusick 		 * Note that the interface cannot be in promiscuous mode if
586*52130Smckusick 		 * there are no bpf listeners.  And if we are in promiscuous
587*52130Smckusick 		 * mode, we have to check if this packet is really ours.
588*52130Smckusick 		 *
589*52130Smckusick 		 * XXX This test does not support multicasts.
590*52130Smckusick 		 */
591*52130Smckusick 		if ((le->sc_if.if_flags & IFF_PROMISC)
592*52130Smckusick 		    && bcmp(et.ether_dhost, le->sc_addr,
593*52130Smckusick 			    sizeof(et.ether_dhost)) != 0
594*52130Smckusick 		    && bcmp(et.ether_dhost, etherbroadcastaddr,
595*52130Smckusick 			    sizeof(et.ether_dhost)) != 0)
596*52130Smckusick 			return;
597*52130Smckusick 	}
598*52130Smckusick #endif
599*52130Smckusick 	/*
600*52130Smckusick 	 * Pull packet off interface.  Off is nonzero if packet
601*52130Smckusick 	 * has trailing header; leget will then force this header
602*52130Smckusick 	 * information to be at the front, but we still have to drop
603*52130Smckusick 	 * the type and length which are at the front of any trailer data.
604*52130Smckusick 	 */
605*52130Smckusick 	m = leget(buf, len, off, &le->sc_if);
606*52130Smckusick 	if (m == 0)
607*52130Smckusick 		return;
608*52130Smckusick #ifdef RMP
609*52130Smckusick 	/*
610*52130Smckusick 	 * (XXX)
611*52130Smckusick 	 * This needs to be integrated with the ISO stuff in ether_input()
612*52130Smckusick 	 */
613*52130Smckusick 	if (et.ether_type == ETHERTYPE_IEEE) {
614*52130Smckusick 		/*
615*52130Smckusick 		 *  Snag the Logical Link Control header (IEEE 802.2).
616*52130Smckusick 		 */
617*52130Smckusick 		struct hp_llc *llc = &(mtod(m, struct rmp_packet *)->hp_llc);
618*52130Smckusick 
619*52130Smckusick 		/*
620*52130Smckusick 		 *  If the DSAP (and HP's extended DXSAP) indicate this
621*52130Smckusick 		 *  is an RMP packet, hand it to the raw input routine.
622*52130Smckusick 		 */
623*52130Smckusick 		if (llc->dsap == IEEE_DSAP_HP && llc->dxsap == HPEXT_DXSAP) {
624*52130Smckusick 			static struct sockproto rmp_sp = {AF_RMP,RMPPROTO_BOOT};
625*52130Smckusick 			static struct sockaddr rmp_src = {AF_RMP};
626*52130Smckusick 			static struct sockaddr rmp_dst = {AF_RMP};
627*52130Smckusick 
628*52130Smckusick 			bcopy(et.ether_shost, rmp_src.sa_data,
629*52130Smckusick 			      sizeof(et.ether_shost));
630*52130Smckusick 			bcopy(et.ether_dhost, rmp_dst.sa_data,
631*52130Smckusick 			      sizeof(et.ether_dhost));
632*52130Smckusick 
633*52130Smckusick 			raw_input(m, &rmp_sp, &rmp_src, &rmp_dst);
634*52130Smckusick 			return;
635*52130Smckusick 		}
636*52130Smckusick 	}
637*52130Smckusick #endif
638*52130Smckusick 	ether_input(&le->sc_if, &et, m);
639*52130Smckusick }
640*52130Smckusick 
641*52130Smckusick /*
642*52130Smckusick  * Routine to copy from mbuf chain to transmit buffer in
643*52130Smckusick  * network buffer memory.
644*52130Smckusick  * NOTE: network memory can only be written one short at every other address.
645*52130Smckusick  */
646*52130Smckusick leput(lebuf, m)
647*52130Smckusick 	volatile register u_short *lebuf;
648*52130Smckusick 	register struct mbuf *m;
649*52130Smckusick {
650*52130Smckusick 	register struct mbuf *mp;
651*52130Smckusick 	register int len, tlen = 0, xfer;
652*52130Smckusick 	register char *cp;
653*52130Smckusick 	int tmp;
654*52130Smckusick 
655*52130Smckusick 	for (mp = m; mp; mp = mp->m_next) {
656*52130Smckusick 		len = mp->m_len;
657*52130Smckusick 		if (len == 0)
658*52130Smckusick 			continue;
659*52130Smckusick 		/* copy data for this mbuf */
660*52130Smckusick 		cp = mtod(mp, char *);
661*52130Smckusick 		if (tlen & 1) {
662*52130Smckusick 			/* handle odd length from previous mbuf */
663*52130Smckusick 			*lebuf = (cp[0] << 8) | tmp;
664*52130Smckusick 			lebuf += 2;
665*52130Smckusick 			cp++;
666*52130Smckusick 			len--;
667*52130Smckusick 			tlen++;
668*52130Smckusick 		}
669*52130Smckusick 		tlen += len;
670*52130Smckusick 		if ((unsigned)cp & 1) {
671*52130Smckusick 			while (len > 1) {
672*52130Smckusick 				*lebuf = (cp[1] << 8) | cp[0];
673*52130Smckusick 				lebuf += 2;
674*52130Smckusick 				cp += 2;
675*52130Smckusick 				len -= 2;
676*52130Smckusick 			}
677*52130Smckusick 		} else {
678*52130Smckusick 			/* optimize for aligned transfers */
679*52130Smckusick 			xfer = (int)((unsigned)len & ~0x1);
680*52130Smckusick 			CopyToBuffer((u_short *)cp, lebuf, xfer);
681*52130Smckusick 			lebuf += xfer;
682*52130Smckusick 			cp += xfer;
683*52130Smckusick 			len -= xfer;
684*52130Smckusick 		}
685*52130Smckusick 		if (len == 1)
686*52130Smckusick 			tmp = *cp;
687*52130Smckusick 	}
688*52130Smckusick 	m_freem(m);
689*52130Smckusick 	/* handle odd length from previous mbuf */
690*52130Smckusick 	if (tlen & 1)
691*52130Smckusick 		*lebuf = tmp;
692*52130Smckusick 	if (tlen < LEMINSIZE)
693*52130Smckusick 		tlen = LEMINSIZE;
694*52130Smckusick 	return(tlen);
695*52130Smckusick }
696*52130Smckusick 
697*52130Smckusick /*
698*52130Smckusick  * Routine to copy from network buffer memory into mbufs.
699*52130Smckusick  * NOTE: network memory can only be read one short at every other address.
700*52130Smckusick  */
701*52130Smckusick struct mbuf *
702*52130Smckusick leget(lebuf, totlen, off, ifp)
703*52130Smckusick 	volatile u_short *lebuf;
704*52130Smckusick 	int totlen, off;
705*52130Smckusick 	struct ifnet *ifp;
706*52130Smckusick {
707*52130Smckusick 	register struct mbuf *m;
708*52130Smckusick 	struct mbuf *top = 0, **mp = &top;
709*52130Smckusick 	register int len, resid;
710*52130Smckusick 	register volatile u_short *sp;
711*52130Smckusick 
712*52130Smckusick 	/* NOTE: sizeof(struct ether_header) should be even */
713*52130Smckusick 	lebuf += sizeof(struct ether_header);
714*52130Smckusick 	sp = lebuf;
715*52130Smckusick 	if (off) {
716*52130Smckusick 		/* NOTE: off should be even */
717*52130Smckusick 		sp += off + 2 * sizeof(u_short);
718*52130Smckusick 		totlen -= 2 * sizeof(u_short);
719*52130Smckusick 		resid = totlen - off;
720*52130Smckusick 	} else
721*52130Smckusick 		resid = totlen;
722*52130Smckusick 
723*52130Smckusick 	MGETHDR(m, M_DONTWAIT, MT_DATA);
724*52130Smckusick 	if (m == 0)
725*52130Smckusick 		return (0);
726*52130Smckusick 	m->m_pkthdr.rcvif = ifp;
727*52130Smckusick 	m->m_pkthdr.len = totlen;
728*52130Smckusick 	m->m_len = MHLEN;
729*52130Smckusick 
730*52130Smckusick 	while (totlen > 0) {
731*52130Smckusick 		if (top) {
732*52130Smckusick 			MGET(m, M_DONTWAIT, MT_DATA);
733*52130Smckusick 			if (m == 0) {
734*52130Smckusick 				m_freem(top);
735*52130Smckusick 				return (0);
736*52130Smckusick 			}
737*52130Smckusick 			m->m_len = MLEN;
738*52130Smckusick 		}
739*52130Smckusick 
740*52130Smckusick 		if (resid >= MINCLSIZE)
741*52130Smckusick 			MCLGET(m, M_DONTWAIT);
742*52130Smckusick 		if (m->m_flags & M_EXT)
743*52130Smckusick 			m->m_len = MIN(resid, MCLBYTES);
744*52130Smckusick 		else if (resid < m->m_len) {
745*52130Smckusick 			/*
746*52130Smckusick 			 * Place initial small packet/header at end of mbuf.
747*52130Smckusick 			 */
748*52130Smckusick 			if (top == 0 && resid + max_linkhdr <= m->m_len)
749*52130Smckusick 				m->m_data += max_linkhdr;
750*52130Smckusick 			m->m_len = resid;
751*52130Smckusick 		}
752*52130Smckusick 		len = m->m_len;
753*52130Smckusick 		if ((unsigned)sp & 2) {
754*52130Smckusick 			/*
755*52130Smckusick 			 * Previous len was odd. Copy the single byte specially.
756*52130Smckusick 			 * XXX Can this ever happen??
757*52130Smckusick 			 */
758*52130Smckusick 			panic("le odd rcv");
759*52130Smckusick 			*mtod(m, char *) = ((volatile char *)sp)[-1];
760*52130Smckusick 			CopyFromBuffer(sp + 1, mtod(m, char *) + 1, len - 1);
761*52130Smckusick 		} else
762*52130Smckusick 			CopyFromBuffer(sp, mtod(m, char *), len);
763*52130Smckusick 		sp += len;
764*52130Smckusick 		*mp = m;
765*52130Smckusick 		mp = &m->m_next;
766*52130Smckusick 		totlen -= len;
767*52130Smckusick 		resid -= len;
768*52130Smckusick 		if (resid == 0) {
769*52130Smckusick 			sp = lebuf;
770*52130Smckusick 			resid = totlen;
771*52130Smckusick 		}
772*52130Smckusick 	}
773*52130Smckusick 	return (top);
774*52130Smckusick }
775*52130Smckusick 
776*52130Smckusick /*
777*52130Smckusick  * Process an ioctl request.
778*52130Smckusick  */
779*52130Smckusick leioctl(ifp, cmd, data)
780*52130Smckusick 	register struct ifnet *ifp;
781*52130Smckusick 	int cmd;
782*52130Smckusick 	caddr_t data;
783*52130Smckusick {
784*52130Smckusick 	register struct ifaddr *ifa = (struct ifaddr *)data;
785*52130Smckusick 	struct le_softc *le = &le_softc[ifp->if_unit];
786*52130Smckusick 	volatile struct lereg1 *ler1 = le->sc_r1;
787*52130Smckusick 	int s, error = 0;
788*52130Smckusick 
789*52130Smckusick 	s = splnet();
790*52130Smckusick 	switch (cmd) {
791*52130Smckusick 
792*52130Smckusick 	case SIOCSIFADDR:
793*52130Smckusick 		ifp->if_flags |= IFF_UP;
794*52130Smckusick 		switch (ifa->ifa_addr->sa_family) {
795*52130Smckusick #ifdef INET
796*52130Smckusick 		case AF_INET:
797*52130Smckusick 			leinit(ifp->if_unit);	/* before arpwhohas */
798*52130Smckusick 			((struct arpcom *)ifp)->ac_ipaddr =
799*52130Smckusick 				IA_SIN(ifa)->sin_addr;
800*52130Smckusick 			arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr);
801*52130Smckusick 			break;
802*52130Smckusick #endif
803*52130Smckusick #ifdef NS
804*52130Smckusick 		case AF_NS:
805*52130Smckusick 		    {
806*52130Smckusick 			register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr);
807*52130Smckusick 
808*52130Smckusick 			if (ns_nullhost(*ina))
809*52130Smckusick 				ina->x_host = *(union ns_host *)(le->sc_addr);
810*52130Smckusick 			else {
811*52130Smckusick 				/*
812*52130Smckusick 				 * The manual says we can't change the address
813*52130Smckusick 				 * while the receiver is armed,
814*52130Smckusick 				 * so reset everything
815*52130Smckusick 				 */
816*52130Smckusick 				ifp->if_flags &= ~IFF_RUNNING;
817*52130Smckusick 				bcopy((caddr_t)ina->x_host.c_host,
818*52130Smckusick 				    (caddr_t)le->sc_addr, sizeof(le->sc_addr));
819*52130Smckusick 			}
820*52130Smckusick 			leinit(ifp->if_unit); /* does le_setaddr() */
821*52130Smckusick 			break;
822*52130Smckusick 		    }
823*52130Smckusick #endif
824*52130Smckusick 		default:
825*52130Smckusick 			leinit(ifp->if_unit);
826*52130Smckusick 			break;
827*52130Smckusick 		}
828*52130Smckusick 		break;
829*52130Smckusick 
830*52130Smckusick 	case SIOCSIFFLAGS:
831*52130Smckusick 		if ((ifp->if_flags & IFF_UP) == 0 &&
832*52130Smckusick 		    ifp->if_flags & IFF_RUNNING) {
833*52130Smckusick 			LERDWR(le->sc_r0, LE_STOP, ler1->ler1_rdp);
834*52130Smckusick 			MachEmptyWriteBuffer();
835*52130Smckusick 			ifp->if_flags &= ~IFF_RUNNING;
836*52130Smckusick 		} else if (ifp->if_flags & IFF_UP &&
837*52130Smckusick 		    (ifp->if_flags & IFF_RUNNING) == 0)
838*52130Smckusick 			leinit(ifp->if_unit);
839*52130Smckusick 		/*
840*52130Smckusick 		 * If the state of the promiscuous bit changes, the interface
841*52130Smckusick 		 * must be reset to effect the change.
842*52130Smckusick 		 */
843*52130Smckusick 		if (((ifp->if_flags ^ le->sc_iflags) & IFF_PROMISC) &&
844*52130Smckusick 		    (ifp->if_flags & IFF_RUNNING)) {
845*52130Smckusick 			le->sc_iflags = ifp->if_flags;
846*52130Smckusick 			lereset(ifp->if_unit);
847*52130Smckusick 			lestart(ifp);
848*52130Smckusick 		}
849*52130Smckusick 		break;
850*52130Smckusick 
851*52130Smckusick 	default:
852*52130Smckusick 		error = EINVAL;
853*52130Smckusick 	}
854*52130Smckusick 	splx(s);
855*52130Smckusick 	return (error);
856*52130Smckusick }
857*52130Smckusick 
858*52130Smckusick leerror(unit, stat)
859*52130Smckusick 	int unit;
860*52130Smckusick 	int stat;
861*52130Smckusick {
862*52130Smckusick 	if (!ledebug)
863*52130Smckusick 		return;
864*52130Smckusick 
865*52130Smckusick 	/*
866*52130Smckusick 	 * Not all transceivers implement heartbeat
867*52130Smckusick 	 * so we only log CERR once.
868*52130Smckusick 	 */
869*52130Smckusick 	if ((stat & LE_CERR) && le_softc[unit].sc_cerr)
870*52130Smckusick 		return;
871*52130Smckusick 	log(LOG_WARNING,
872*52130Smckusick 	    "le%d: error: stat=%b\n", unit,
873*52130Smckusick 	    stat,
874*52130Smckusick 	    "\20\20ERR\17BABL\16CERR\15MISS\14MERR\13RINT\12TINT\11IDON\10INTR\07INEA\06RXON\05TXON\04TDMD\03STOP\02STRT\01INIT");
875*52130Smckusick }
876*52130Smckusick 
877*52130Smckusick lererror(unit, msg)
878*52130Smckusick 	int unit;
879*52130Smckusick 	char *msg;
880*52130Smckusick {
881*52130Smckusick 	register struct le_softc *le = &le_softc[unit];
882*52130Smckusick 	register volatile struct lermd *rmd;
883*52130Smckusick 	int len;
884*52130Smckusick 
885*52130Smckusick 	if (!ledebug)
886*52130Smckusick 		return;
887*52130Smckusick 
888*52130Smckusick 	rmd = &le->sc_r2->ler2_rmd[le->sc_rmd];
889*52130Smckusick 	len = rmd->rmd3;
890*52130Smckusick 	log(LOG_WARNING,
891*52130Smckusick 	    "le%d: ierror(%s): from %s: buf=%d, len=%d, rmd1=%b\n",
892*52130Smckusick 	    unit, msg,
893*52130Smckusick 	    len > 11 ? ether_sprintf(&le->sc_r2->ler2_rbuf[le->sc_rmd][6]) : "unknown",
894*52130Smckusick 	    le->sc_rmd, len,
895*52130Smckusick 	    rmd->rmd1,
896*52130Smckusick 	    "\20\20OWN\17ERR\16FRAM\15OFLO\14CRC\13RBUF\12STP\11ENP");
897*52130Smckusick }
898*52130Smckusick 
899*52130Smckusick lexerror(unit)
900*52130Smckusick 	int unit;
901*52130Smckusick {
902*52130Smckusick 	register struct le_softc *le = &le_softc[unit];
903*52130Smckusick 	register volatile struct letmd *tmd;
904*52130Smckusick 	int len;
905*52130Smckusick 
906*52130Smckusick 	if (!ledebug)
907*52130Smckusick 		return;
908*52130Smckusick 
909*52130Smckusick 	tmd = le->sc_r2->ler2_tmd;
910*52130Smckusick 	len = -tmd->tmd2;
911*52130Smckusick 	log(LOG_WARNING,
912*52130Smckusick 	    "le%d: oerror: to %s: buf=%d, len=%d, tmd1=%b, tmd3=%b\n",
913*52130Smckusick 	    unit,
914*52130Smckusick 	    len > 5 ? ether_sprintf(&le->sc_r2->ler2_tbuf[0][0]) : "unknown",
915*52130Smckusick 	    0, len,
916*52130Smckusick 	    tmd->tmd1,
917*52130Smckusick 	    "\20\20OWN\17ERR\16RES\15MORE\14ONE\13DEF\12STP\11ENP",
918*52130Smckusick 	    tmd->tmd3,
919*52130Smckusick 	    "\20\20BUFF\17UFLO\16RES\15LCOL\14LCAR\13RTRY");
920*52130Smckusick }
921*52130Smckusick #endif
922