xref: /csrg-svn/sys/tahoe/if/if_ex.c (revision 37110)
1 /*
2  * Copyright (c) 1989 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Excelan Inc.
7  *
8  * Redistribution and use in source and binary forms are permitted
9  * provided that the above copyright notice and this paragraph are
10  * duplicated in all such forms and that any documentation,
11  * advertising materials, and other materials related to such
12  * distribution and use acknowledge that the software was developed
13  * by the University of California, Berkeley.  The name of the
14  * University may not be used to endorse or promote products derived
15  * from this software without specific prior written permission.
16  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
19  *
20  *	@(#)if_ex.c	7.1 (Berkeley) 03/09/89
21  */
22 
23 #include "ex.h"
24 
25 #if	NEX > 0
26 
27 /*
28  * Excelan EXOS 202(VME) & 203(QBUS) Link Level Ethernet Interface Drivers
29  */
30 #include "param.h"
31 #include "systm.h"
32 #include "mbuf.h"
33 #include "buf.h"
34 #include "protosw.h"
35 #include "socket.h"
36 #include "vmmac.h"
37 #include "ioctl.h"
38 #include "errno.h"
39 #include "vmparam.h"
40 #include "syslog.h"
41 #include "uio.h"
42 
43 #include "../net/if.h"
44 #include "../net/netisr.h"
45 #include "../net/route.h"
46 
47 #ifdef INET
48 #include "../netinet/in.h"
49 #include "../netinet/in_systm.h"
50 #include "../netinet/in_var.h"
51 #include "../netinet/ip.h"
52 #include "../netinet/if_ether.h"
53 #endif
54 
55 #ifdef NS
56 #include "../netns/ns.h"
57 #include "../netns/ns_if.h"
58 #endif
59 
60 #include "../tahoe/cpu.h"
61 #include "../tahoe/pte.h"
62 #include "../tahoe/mtpr.h"
63 
64 #include "../tahoevba/vbavar.h"
65 #include "if_exreg.h"
66 #include "if_vba.h"
67 
68 
69 #define	NH2X 32			/* Host to eXcelan request buffers */
70 
71 #define	NX2H 16			/* eXcelan to Host reply buffers */
72 #define	NREC	16		/* Number of RECeive buffers */
73 #define	NTRB	4		/* Number of TRansmit Buffers */
74 #define NVBI	(NREC + NTRB)
75 
76 #define EXWATCHINTVL	10	/* call exwatch every x secs */
77 
78 int	exprobe(), exslave(), exattach(), exintr(), exstart();
79 struct	vba_device *exinfo[NEX];
80 
81 long	exstd[] = { 0 };
82 
83 
84 struct	vba_driver exdriver =
85 	{ exprobe, 0, exattach, exstart, exstd, "ex", exinfo };
86 int	exinit(),ether_output(),exioctl(),exreset(),exwatch();
87 struct	ex_msg *exgetcbuf();
88 int	ex_ncall = 0;			/* counts calls to exprobe */
89 u_long	busoff;
90 
91 /*
92  * Ethernet software status per interface.
93  *
94  * Each interface is referenced by a network interface structure, xs_if, which
95  * the routing code uses to locate the interface.  This structure contains the
96  * output queue for the interface, its address, ... NOTE: To configure multiple
97  * controllers, the sizeof this structure must be a multiple of 16 (xs_h2xhdr).
98  */
99 struct	ex_softc {
100 	struct		arpcom xs_ac;	/* Ethernet common part */
101 #define	xs_if		xs_ac.ac_if	/* network-visible interface */
102 #define	xs_addr		xs_ac.ac_enaddr	/* hardware Ethernet address */
103 	int		xs_flags;	/* private flags */
104 #define	EX_XPENDING	1		/* xmit rqst pending on EXOS */
105 #define	EX_STATPENDING	(1<<1)		/* stats rqst pending on EXOS */
106 #define	EX_RUNNING	(1<<2)		/* board is running */
107 #define EX_SETADDR	(1<<3)		/* physaddr has been changed */
108 	int		xs_cvec;	/* probe stores cvec here */
109 	short		xs_enetunit;	/* unit number for enet filtering */
110 	short		xs_enetinit;	/* enet inetrface is initialized */
111 	struct	ex_msg	*xs_h2xnext;	/* host pointer to request queue */
112 	struct	ex_msg 	*xs_x2hnext;	/* host pointer to reply queue */
113 	u_long		xs_qbaddr;	/* map info for structs below */
114 	/* the following structures are always mapped in */
115 	u_short		xs_h2xhdr;	/* EXOS's request queue header */
116 	u_short		xs_x2hhdr;	/* EXOS's reply queue header */
117 	struct ex_msg 	xs_h2xent[NH2X];/* request msg buffers */
118 	struct ex_msg 	xs_x2hent[NX2H];/* reply msg buffers */
119 	struct confmsg	xs_cm;		/* configuration message */
120 	struct stat_array xs_xsa;	/* EXOS writes stats here */
121 	/* end mapped area */
122 #define	BUSADDR(x)	((0x3D000000 | ((u_long)(kvtophys(x))&0xFFFFFF)) + busoff)
123 #define	P_BUSADDR(x)	((0x3D000000 | ((u_long)(kvtophys(x))&0xFFFFF0)) + busoff)
124 #define	INCORE_BASE(p)	(((u_long)(&(p)->xs_h2xhdr)) & 0xFFFFFFF0)
125 #define	RVAL_OFF(n)	((u_long)(&(ex_softc[0].n)) - INCORE_BASE(&ex_softc[0]))
126 #define	LVAL_OFF(n)	((u_long)(ex_softc[0].n) - INCORE_BASE(&ex_softc[0]))
127 #define	H2XHDR_OFFSET	RVAL_OFF(xs_h2xhdr)
128 #define	X2HHDR_OFFSET	RVAL_OFF(xs_x2hhdr)
129 #define	H2XENT_OFFSET	LVAL_OFF(xs_h2xent)
130 #define	X2HENT_OFFSET	LVAL_OFF(xs_x2hent)
131 #define	CM_OFFSET	RVAL_OFF(xs_cm)
132 #define	SA_OFFSET	RVAL_OFF(xs_xsa)
133 #define FreePkBuf(b) (((b)->iff_mbuf = (struct mbuf *)xs->xs_pkblist),\
134 							(xs->xs_pkblist = b))
135 	struct		ifvba xs_vbinfo[NVBI];/* Bus Resources (low core) */
136 	struct		ifvba *xs_pkblist; /* free list of above */
137 	char		xs_nrec;	/* number of pending receive buffers */
138 	char		xs_ntrb;	/* number of pending transmit buffers */
139 	char		pad[6];		/* make BUSADDR macros */
140 } ex_softc[NEX];
141 
142 int ex_padcheck = sizeof (struct ex_softc);
143 
144 exprobe(reg, vi)
145 	caddr_t reg;
146 	struct vba_device *vi;
147 {
148 	register br, cvec;		/* r12, r11 value-result */
149 	register struct exdevice *exaddr = (struct exdevice *)reg;
150 	int	i;
151 
152 	if (badaddr(exaddr, 2))
153 		return 0;
154 	/*
155 	 * Reset EXOS and run self-test (should complete within 2 seconds).
156 	 */
157 	movow(&exaddr->ex_porta, EX_RESET);
158 	for (i = 1000000; i; i--) {
159 		uncache(&(exaddr->ex_portb));
160 		if (exaddr->ex_portb & EX_TESTOK)
161 			break;
162 	}
163 	if ((exaddr->ex_portb & EX_TESTOK) == 0)
164 		return 0;
165 	br = 0x15;
166 	cvec = --vi->ui_hd->vh_lastiv;
167 	ex_softc[vi->ui_unit].xs_cvec = cvec;
168 	ex_ncall++;
169 	return (sizeof(struct exdevice));
170 }
171 
172 /*
173  * Interface exists: make available by filling in network interface record.
174  * System will initialize the interface when it is ready to accept packets.
175  * A NET_ADDRS command is done to get the ethernet address.
176  */
177 exattach(ui)
178 	register struct vba_device	*ui;
179 {
180 	register struct ex_softc *xs = &ex_softc[ui->ui_unit];
181 	register struct ifnet *ifp = &xs->xs_if;
182 	register struct exdevice *exaddr = (struct exdevice *)ui->ui_addr;
183 	register struct ex_msg *bp;
184 
185 	ifp->if_unit = ui->ui_unit;
186 	ifp->if_name = "ex";
187 	ifp->if_mtu = ETHERMTU;
188 	ifp->if_init = exinit;
189 	ifp->if_ioctl = exioctl;
190 	ifp->if_output = ether_output;
191 	ifp->if_reset = exreset;
192 	ifp->if_start = exstart;
193 
194 	if (if_vbareserve(xs->xs_vbinfo, NVBI, EXMAXRBUF) == 0)
195 		return;
196 	/*
197 	 * Temporarily map queues in order to configure EXOS
198 	 */
199 	xs->xs_qbaddr = INCORE_BASE(xs);
200 	exconfig(ui, 0);			/* without interrupts */
201 	if (xs->xs_cm.cm_cc)
202 		return;				/* bad conf */
203 	/*
204 	 * Get Ethernet address.
205 	 */
206 	if ((bp = exgetcbuf(xs, LLNET_ADDRS)) == (struct ex_msg *)0)
207 		panic("exattach");
208 	bp->mb_na.na_mask = READ_OBJ;
209 	bp->mb_na.na_slot = PHYSSLOT;
210 	bp->mb_status |= MH_EXOS;
211 	movow(&exaddr->ex_portb, EX_NTRUPT);
212 	bp = xs->xs_x2hnext;
213 	do {
214 		uncache(&bp->mb_status);
215 	} while ((bp->mb_status & MH_OWNER) == MH_EXOS);/* poll for reply */
216 	printf("ex%d: HW %c.%c NX %c.%c, hardware address %s\n",
217 		ui->ui_unit, xs->xs_cm.cm_vc[2], xs->xs_cm.cm_vc[3],
218 		xs->xs_cm.cm_vc[0], xs->xs_cm.cm_vc[1],
219 		ether_sprintf(bp->mb_na.na_addrs));
220 	bcopy((caddr_t)bp->mb_na.na_addrs, (caddr_t)xs->xs_addr,
221 		sizeof(xs->xs_addr));
222 	if_attach(ifp);
223 }
224 
225 /*
226  * Reset of interface after BUS reset.
227  * If interface is on specified vba, reset its state.
228  */
229 exreset(unit)
230 int unit;
231 {
232 	register struct vba_device *ui;
233 
234 	if (unit >= NEX || (ui = exinfo[unit]) == 0 || ui->ui_alive == 0)
235 		return;
236 	printf(" ex%d", unit);
237 	ex_softc[unit].xs_if.if_flags &= ~IFF_RUNNING;
238 	ex_softc[unit].xs_flags &= ~EX_RUNNING;
239 
240 	exinit(unit);
241 }
242 
243 /*
244  * Initialization of interface; clear recorded pending operations, and
245  * reinitialize BUS usage. Called at boot time, and at ifconfig time via
246  * exioctl, with interrupts disabled.
247  */
248 exinit(unit)
249 int unit;
250 {
251 	register struct ex_softc *xs = &ex_softc[unit];
252 	register struct vba_device *ui = exinfo[unit];
253 	register struct exdevice *exaddr = (struct exdevice *)ui->ui_addr;
254 	register struct ifnet *ifp = &xs->xs_if;
255 	register struct sockaddr_in *sin;
256 	register struct ex_msg 	*bp;
257 	int s;
258 
259 	/* not yet, if address still unknown */
260 	if (ifp->if_addrlist == (struct ifaddr *)0)
261 		return;
262 	if (xs->xs_flags & EX_RUNNING)
263 		return;
264 
265 	xs->xs_qbaddr = INCORE_BASE(xs);
266 	exconfig(ui, 4);		/* with vectored interrupts*/
267 
268 	/*
269 	 * Put EXOS on the Ethernet, using NET_MODE command
270 	 */
271 	if ((bp = exgetcbuf(xs, LLNET_MODE)) == (struct ex_msg *)0)
272 		panic("exinit");
273 	bp->mb_nm.nm_mask = WRITE_OBJ;
274 	bp->mb_nm.nm_optn = 0;
275 	bp->mb_nm.nm_mode = MODE_PERF;
276 	bp->mb_status |= MH_EXOS;
277 	movow(&exaddr->ex_portb, EX_NTRUPT);
278 	bp = xs->xs_x2hnext;
279 	do {
280 		uncache(&bp->mb_status);
281 	} while ((bp->mb_status & MH_OWNER) == MH_EXOS);/* poll for reply */
282 	bp->mb_length = MBDATALEN;
283 	bp->mb_status |= MH_EXOS;		/* free up buffer */
284 	movow(&exaddr->ex_portb, EX_NTRUPT);
285 	xs->xs_x2hnext = xs->xs_x2hnext->mb_next;
286 
287 	ifp->if_watchdog = exwatch;
288 	ifp->if_timer = EXWATCHINTVL;
289 	s = splimp();		/* are interrupts disabled here, anyway? */
290 	exhangrcv(unit);
291 	xs->xs_if.if_flags |= IFF_RUNNING;
292 	xs->xs_flags |= EX_RUNNING;
293 	if (xs->xs_flags & EX_SETADDR)
294 		ex_setaddr((u_char *)0, unit);
295 	exstart(&ex_softc[unit].xs_if);		/* start transmits */
296 	splx(s);		/* are interrupts disabled here, anyway? */
297 }
298 
299 /*
300  * Reset, test, and configure EXOS.  It is called by exinit, and exattach.
301  * Returns 0 if successful, 1 if self-test failed.
302  */
303 exconfig(ui, itype)
304 struct	vba_device *ui;
305 int itype;
306 {
307 	register int unit = ui->ui_unit;
308 	register struct ex_softc *xs = &ex_softc[unit];
309 	register struct exdevice *exaddr = (struct exdevice *) ui->ui_addr;
310 	register struct confmsg *cm = &xs->xs_cm;
311 	register struct ex_msg 	*bp;
312 	register struct ifvba *pkb;
313 	int 	i;
314 	u_long 	shiftreg;
315 	static	u_char	cmaddr[8] = {0xFF, 0xFF, 0, 0};
316 
317 	xs->xs_flags = 0;
318 	/*
319 	 * Reset EXOS, wait for self-test to complete
320 	 */
321 	movow(&exaddr->ex_porta, EX_RESET);
322 	do {
323 		uncache(&exaddr->ex_portb);
324 	} while ((exaddr->ex_portb & EX_TESTOK) == 0) ;
325 	/*
326 	 * Set up configuration message.
327 	 */
328 	cm->cm_1rsrv = 1;
329 	cm->cm_cc = 0xFF;
330 	cm->cm_opmode = 0;		/* link-level controller mode */
331 	cm->cm_dfo = 0x0101;		/* enable host data order conversion */
332 	cm->cm_dcn1 = 1;
333 	cm->cm_2rsrv[0] = cm->cm_2rsrv[1] = 0;
334 	cm->cm_ham = 3;			/* absolute address mode */
335 	cm->cm_3rsrv = 0;
336 	cm->cm_mapsiz = 0;
337 	cm->cm_byteptrn[0] = 0x01;	/* EXOS deduces data order of host */
338 	cm->cm_byteptrn[1] = 0x03;	/*  by looking at this pattern */
339 	cm->cm_byteptrn[2] = 0x07;
340 	cm->cm_byteptrn[3] = 0x0F;
341 	cm->cm_wordptrn[0] = 0x0103;
342 	cm->cm_wordptrn[1] = 0x070F;
343 	cm->cm_lwordptrn = 0x0103070F;
344 	for (i=0; i<20; i++) cm->cm_rsrvd[i] = 0;
345 	cm->cm_mba = 0xFFFFFFFF;
346 	cm->cm_nproc = 0xFF;
347 	cm->cm_nmbox = 0xFF;
348 	cm->cm_nmcast = 0xFF;
349 	cm->cm_nhost = 1;
350 	cm->cm_h2xba = P_BUSADDR(xs->xs_qbaddr);
351 	cm->cm_h2xhdr = H2XHDR_OFFSET;
352 	cm->cm_h2xtyp = 0;		/* should never wait for rqst buffer */
353 	cm->cm_x2hba = cm->cm_h2xba;
354 	cm->cm_x2hhdr = X2HHDR_OFFSET;
355 	cm->cm_x2htyp = itype;		/* 0 for none, 4 for vectored */
356 	cm->cm_x2haddr = xs->xs_cvec;	/* ivec allocated in exprobe */
357 	/*
358 	 * Set up message queues and headers.
359 	 * First the request queue
360 	 */
361 	for (bp = &xs->xs_h2xent[0]; bp < &xs->xs_h2xent[NH2X]; bp++) {
362 		bp->mb_link = (u_short)((char *)(bp+1)-INCORE_BASE(xs));
363 		bp->mb_rsrv = 0;
364 		bp->mb_length = MBDATALEN;
365 		bp->mb_status = MH_HOST;
366 		bp->mb_next = bp+1;
367 	}
368 	xs->xs_h2xhdr = xs->xs_h2xent[NH2X-1].mb_link = (u_short)H2XENT_OFFSET;
369 	xs->xs_h2xnext = xs->xs_h2xent[NH2X-1].mb_next = xs->xs_h2xent;
370 
371 	/* Now the reply queue. */
372 	for (bp = &xs->xs_x2hent[0]; bp < &xs->xs_x2hent[NX2H]; bp++) {
373 		bp->mb_link = (u_short)((char *)(bp+1)-INCORE_BASE(xs));
374 		bp->mb_rsrv = 0;
375 		bp->mb_length = MBDATALEN;
376 		bp->mb_status = MH_EXOS;
377 		bp->mb_next = bp+1;
378 	}
379 	xs->xs_x2hhdr = xs->xs_x2hent[NX2H-1].mb_link = (u_short)X2HENT_OFFSET;
380 	xs->xs_x2hnext = xs->xs_x2hent[NX2H-1].mb_next = xs->xs_x2hent;
381 	xs->xs_nrec = 0;
382 	xs->xs_ntrb = 0;
383 	xs->xs_pkblist =  xs->xs_vbinfo + NVBI - 1;
384 	for (pkb = xs->xs_pkblist; pkb >= xs->xs_vbinfo; pkb--)
385 		pkb->iff_mbuf = (struct mbuf *)(pkb - 1);
386 	xs->xs_vbinfo[0].iff_mbuf = 0;
387 
388 	/*
389 	 * Write config msg address to EXOS and wait for configuration to
390 	 * complete (guaranteed response within 2 seconds).
391 	 */
392 	shiftreg = P_BUSADDR(xs->xs_qbaddr) + CM_OFFSET;
393 	for (i = 4; i < 8; i++) {
394 		cmaddr[i] = (u_char)(shiftreg & 0xFF);
395 		shiftreg >>= 8;
396 	}
397 	for (i = 0; i < 8; i++) {
398 		do {
399 			uncache(&exaddr->ex_portb);
400 		} while (exaddr->ex_portb & EX_UNREADY) ;
401 		DELAY(500);
402 		movow(&exaddr->ex_portb, cmaddr[i]);
403 	}
404 	for (i = 500000; i; --i) {
405 		DELAY(10);
406 		uncache(&cm->cm_cc);
407 		if (cm->cm_cc != 0xFF)
408 			break;
409 	}
410 	if (cm->cm_cc)
411 		printf("ex%d: configuration failed; cc=%x\n", unit, cm->cm_cc);
412 }
413 
414 /*
415  * Start or re-start output on interface. Get another datagram to send off of
416  * the interface queue, and map it to the interface before starting the output.
417  * This routine is called by exinit(), exoutput(), and excdint().  In all cases,
418  * interrupts by EXOS are disabled.
419  */
420 exstart(ifp)
421 struct ifnet *ifp;
422 {
423 	int unit = ifp->if_unit;
424 	struct vba_device *ui = exinfo[unit];
425 	register struct ex_softc *xs = &ex_softc[unit];
426 	struct exdevice *exaddr = (struct exdevice *)ui->ui_addr;
427 	register struct ex_msg *bp;
428 	register struct mbuf *m;
429         int len;
430 	register struct ifvba *pkb;
431 	struct mbuf *m0;
432 	register int nb, tlen;
433 	union l_util {
434 		u_long	l;
435 		struct	i86_long i;
436 	} l_util;
437 
438 	if (xs->xs_ntrb >= NTRB)
439 		return;
440 	if (xs->xs_pkblist == 0) {
441 		printf("ex%d: vbinfo exhausted, would panic", unit);
442 		return;
443 	}
444 	IF_DEQUEUE(&xs->xs_if.if_snd, m);
445 	if (m == 0)
446 		return;
447 	/*
448 	 * Get a transmit request.
449 	 */
450 	if ((bp = exgetcbuf(xs, LLRTRANSMIT)) == (struct ex_msg *)0) {
451 		m_freem(m);
452 		printf("exstart: no command buffers\n");
453 		return;
454 	}
455 	xs->xs_ntrb++;
456 	bp->mb_pkb = pkb = xs->xs_pkblist;
457 	xs->xs_pkblist = (struct ifvba *)pkb->iff_mbuf;
458 	nb = 0; tlen = 0; m0 = 0;
459 	pkb->iff_mbuf = m;	/* save mbuf pointer to free when done */
460 	/*
461 	 * point directly to the first group of mbufs to be transmitted. The
462 	 * hardware can only support NFRAGMENTS descriptors.
463 	 */
464 	while (m && ((nb < NFRAGMENTS-1) || (m->m_next == 0)) ) {
465 		l_util.l = BUSADDR(mtod(m, char *));
466 		bp->mb_et.et_blks[nb].bb_len = (u_short)m->m_len;
467 		bp->mb_et.et_blks[nb].bb_addr = l_util.i;
468 		tlen += m->m_len;
469 		m0 = m;
470 		m = m->m_next;
471 		nb++;
472 	}
473 
474 	/* 0 end of chain pointed to by iff_mbuf, to be freed when xmit done */
475 	if (m0)
476 		m0->m_next = 0;
477 
478 	/*
479 	 * if not all of the descriptors would fit then merge remaining data
480 	 * into the transmit buffer, and point to it.  Note: the mbufs are freed
481 	 * during the merge, they do not have to be freed when we get the
482 	 * transmit interrupt.
483 	 */
484 	if (m) {
485 		len = if_vbaput(pkb->iff_buffer, m);
486 		l_util.l = BUSADDR(pkb->iff_buffer);
487 		bp->mb_et.et_blks[nb].bb_len = (u_short)len;
488 		bp->mb_et.et_blks[nb].bb_addr = l_util.i;
489 		tlen += len;
490 		nb++;
491 	}
492 
493 	/*
494 	 * If the total length of the packet is too small, pad the last frag
495 	 */
496 	if (tlen - sizeof(struct ether_header) < ETHERMIN) {
497 		len = (ETHERMIN + sizeof(struct ether_header)) - tlen;
498 		bp->mb_et.et_blks[nb-1].bb_len += (u_short)len;
499 		tlen += len;
500 	}
501 
502 	/* set number of fragments in descriptor */
503 	bp->mb_et.et_nblock = nb;
504 	bp->mb_status |= MH_EXOS;
505 	movow(&exaddr->ex_portb, EX_NTRUPT);
506 }
507 
508 /*
509  * interrupt service routine.
510  */
511 exintr(unit)
512 	int unit;
513 {
514 	register struct ex_softc *xs = &ex_softc[unit];
515 	register struct ex_msg *bp = xs->xs_x2hnext;
516 	struct vba_device *ui = exinfo[unit];
517 	struct exdevice *exaddr = (struct exdevice *)ui->ui_addr;
518 
519 	uncache(&bp->mb_status);
520 	while ((bp->mb_status & MH_OWNER) == MH_HOST) {
521 		switch (bp->mb_rqst) {
522 		    case LLRECEIVE:
523 			if (--xs->xs_nrec < 0)
524 				xs->xs_nrec = 0;
525 			exrecv(unit, bp);
526 			FreePkBuf(bp->mb_pkb);
527 			bp->mb_pkb = (struct ifvba *)0;
528 			exhangrcv(unit);
529 			break;
530 
531 		    case LLTRANSMIT:
532 		    case LLRTRANSMIT:
533 			if (--xs->xs_ntrb < 0)
534 				xs->xs_ntrb = 0;
535 			xs->xs_if.if_opackets++;
536 			if (bp->mb_rply == LL_OK)
537 				;
538 			else if (bp->mb_rply & LLXM_1RTRY)
539 				xs->xs_if.if_collisions++;
540 			else if (bp->mb_rply & LLXM_RTRYS)
541 				xs->xs_if.if_collisions += 2;	/* guess */
542 			else if (bp->mb_rply & LLXM_ERROR)
543 				if (xs->xs_if.if_oerrors++ % 100 == 0)
544 					printf("ex%d: 100 transmit errors=%b\n",
545 						unit, bp->mb_rply, XMIT_BITS);
546 			if (bp->mb_pkb->iff_mbuf) {
547 				m_freem(bp->mb_pkb->iff_mbuf);
548 				bp->mb_pkb->iff_mbuf = (struct mbuf *)0;
549 			}
550 			FreePkBuf(bp->mb_pkb);
551 			bp->mb_pkb = (struct ifvba *)0;
552 			exstart(&ex_softc[unit].xs_if);
553 			exhangrcv(unit);
554 			break;
555 
556 		    case LLNET_STSTCS:
557 			xs->xs_if.if_ierrors += xs->xs_xsa.sa_crc;
558 			xs->xs_flags &= ~EX_STATPENDING;
559 			break;
560 
561 		    default:
562 			printf("ex%d: unknown reply 0x%x", unit, bp->mb_rqst);
563 		}
564 		bp->mb_length = MBDATALEN;
565 		bp->mb_status |= MH_EXOS;	/* free up buffer */
566 		movow(&exaddr->ex_portb, EX_NTRUPT); /* tell EXOS about it */
567 		bp = bp->mb_next;
568 		uncache(&bp->mb_status);
569 	}
570 	xs->xs_x2hnext = bp;
571 }
572 
573 /*
574  * Get a request buffer, fill in standard values, advance pointer.
575  */
576 struct ex_msg *
577 exgetcbuf(xs, req)
578 struct ex_softc *xs;
579 int req;
580 {
581 	register struct ex_msg *bp;
582 	struct ifvba *pkb;
583 	int s = splimp();
584 
585 	bp = xs->xs_h2xnext;
586 	uncache(&bp->mb_status);
587 	if ((bp->mb_status & MH_OWNER) == MH_EXOS) {
588 		splx(s);
589 		return (struct ex_msg *)0;
590 	}
591 	xs->xs_h2xnext = bp->mb_next;
592 	bp->mb_1rsrv = 0;
593 	bp->mb_rqst = req;
594 	bp->mb_length = MBDATALEN;
595 	bp->mb_pkb = (struct ifvba *)0;
596 	splx(s);
597 	return bp;
598 }
599 
600 /*
601  * Process Ethernet receive completion:  If input error just drop packet,
602  * otherwise examine packet to determine type.  If can't determine length from
603  * type, then have to drop packet, otherwise decapsulate packet based on type
604  * and pass to type-specific higher-level input routine.
605  */
606 exrecv(unit, bp)
607 int unit;
608 register struct ex_msg *bp;
609 {
610 	register struct ex_softc *xs = &ex_softc[unit];
611 	register struct ether_header *eh;
612     	register struct mbuf *m;
613 	int len, off, resid;
614 	register struct ifqueue *inq;
615 	int s;
616 
617 	xs->xs_if.if_ipackets++;
618 	/*     total length               - header                      - crc */
619 	len = bp->mb_er.er_blks[0].bb_len - sizeof(struct ether_header) - 4;
620 	if (bp->mb_rply != LL_OK) {
621 		if (xs->xs_if.if_ierrors++ % 100 == 0)
622 			printf("ex%d: 100 receive errors=%b\n",
623 				unit, bp->mb_rply, RECV_BITS);
624 		return;
625 	}
626 	eh = (struct ether_header *)(bp->mb_pkb->iff_buffer);
627 
628 	/*
629 	 * Deal with trailer protocol: if type is PUP trailer get true type from
630 	 * first 16-bit word past data.  Remember that type was trailer by
631 	 * setting off.
632 	 */
633 	eh->ether_type = ntohs((u_short)eh->ether_type);
634 #define	exdataaddr(eh, off, type)	((type)(((caddr_t)((eh)+1)+(off))))
635 	if (eh->ether_type >= ETHERTYPE_TRAIL &&
636 	    eh->ether_type < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) {
637 		off = (eh->ether_type - ETHERTYPE_TRAIL) * 512;
638 		if (off >= ETHERMTU)
639 			return;			/* sanity */
640 		eh->ether_type = ntohs(*exdataaddr(eh, off, u_short *));
641 		resid = ntohs(*(exdataaddr(eh, off+2, u_short *)));
642 		if (off + resid > len)
643 			return;			/* sanity */
644 		len = off + resid;
645 	} else
646 		off = 0;
647 	if (len == 0)
648 		return;
649 	/*
650 	 * Pull packet off interface.  Off is nonzero if packet
651 	 * has trailing header; if_vbaget will then force this header
652 	 * information to be at the front, but we still have to drop
653 	 * the type and length which are at the front of any trailer data.
654 	 */
655 	m = if_vbaget(bp->mb_pkb->iff_buffer, len, off, &xs->xs_if, 0);
656 	if (m == 0)
657 		return;
658 	ether_input(&xs->xs_if, eh, m);
659 	return;
660 }
661 
662 /*
663  * Hang a receive request. This routine is called by exinit and excdint,
664  * with interrupts disabled in both cases.
665  */
666 exhangrcv(unit)
667 	int unit;
668 {
669 	register struct ex_softc *xs = &ex_softc[unit];
670 	register struct ex_msg *bp;
671 	register struct ifvba *pkb;
672 	short mustint = 0;
673 	union l_util {
674 		u_long	l;
675 		struct	i86_long i;
676 	} l_util;
677 
678 	while (xs->xs_nrec < NREC) {
679 		if (xs->xs_pkblist == (struct ifvba *)0)
680 			break;
681 		if ((bp = exgetcbuf(xs, LLRECEIVE)) == (struct ex_msg *)0) {
682 			break;
683 		}
684 		pkb = bp->mb_pkb = xs->xs_pkblist;
685 		xs->xs_pkblist = (struct ifvba *)bp->mb_pkb->iff_mbuf;
686 
687 		xs->xs_nrec += 1;
688 		bp->mb_er.er_nblock = 1;
689 		bp->mb_er.er_blks[0].bb_len = EXMAXRBUF;
690 		l_util.l = BUSADDR(pkb->iff_buffer);
691 		bp->mb_er.er_blks[0].bb_addr = l_util.i;
692 		bp->mb_status |= MH_EXOS;
693 		mustint = 1;
694 	}
695 	if (mustint == 0)
696 		return;
697 	movow(&((struct exdevice *)exinfo[unit]->ui_addr)->ex_portb, EX_NTRUPT);
698 }
699 
700 /*
701  * Ethernet output routine is ether_output().
702  */
703 
704 /*
705  * Watchdog routine (currently not used). Might use this to get stats from EXOS.
706  */
707 exwatch(unit)
708 int unit;
709 {
710 	struct exdevice *exaddr = (struct exdevice *)exinfo[unit]->ui_addr;
711 	register struct ex_softc *xs = &ex_softc[unit];
712 	register struct ex_msg *bp;
713 	int s = splimp();
714 
715 	if (xs->xs_flags & EX_STATPENDING)
716 		goto exspnd;
717 	if ((bp = exgetcbuf(xs, LLNET_STSTCS)) == (struct ex_msg *)0) {
718 		splx(s);
719 		return;
720 	}
721 	xs->xs_flags |= EX_STATPENDING;
722 	bp->mb_ns.ns_mask = READ_OBJ;
723 	bp->mb_ns.ns_rsrv = 0;
724 	bp->mb_ns.ns_nobj = 8;
725 	bp->mb_ns.ns_xobj = 0;
726 	bp->mb_ns.ns_bufp = P_BUSADDR(xs->xs_qbaddr) + SA_OFFSET;
727 	bp->mb_status |= MH_EXOS;
728 	movow(&exaddr->ex_portb, EX_NTRUPT);
729 exspnd:	splx(s);
730 	xs->xs_if.if_timer = EXWATCHINTVL;
731 }
732 
733 /*
734  * Process an ioctl request.
735  */
736 exioctl(ifp, cmd, data)
737 	register struct ifnet *ifp;
738 	int cmd;
739 	caddr_t data;
740 {
741 	register struct ifaddr *ifa = (struct ifaddr *)data;
742 	register struct ex_softc *xs = &ex_softc[ifp->if_unit];
743 	int s = splimp(), error = 0;
744 
745 	switch (cmd) {
746 
747 	case SIOCSIFADDR:
748                 ifp->if_flags |= IFF_UP;
749                 exinit(ifp->if_unit);
750 
751                 switch (ifa->ifa_addr->sa_family) {
752 #ifdef INET
753 		case AF_INET:
754 			((struct arpcom *)ifp)->ac_ipaddr =
755 				IA_SIN(ifa)->sin_addr;
756 			arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr);
757 			break;
758 #endif
759 #ifdef NS
760 		case AF_NS:
761 		    {
762 			register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr);
763 
764 			if (ns_nullhost(*ina))
765 				ina->x_host = *(union ns_host *)(xs->xs_addr);
766 			else
767 				ex_setaddr(ina->x_host.c_host,ifp->if_unit);
768 			break;
769 		    }
770 #endif
771 		}
772 		break;
773 
774 	case SIOCSIFFLAGS:
775 		if ((ifp->if_flags & IFF_UP) == 0 &&
776 		    xs->xs_flags & EX_RUNNING) {
777 			movow(&((struct exdevice *)
778 			  (exinfo[ifp->if_unit]->ui_addr))->ex_porta, EX_RESET);
779 			xs->xs_flags &= ~EX_RUNNING;
780 		} else if (ifp->if_flags & IFF_UP &&
781 		    (xs->xs_flags & EX_RUNNING) == 0)
782 			exinit(ifp->if_unit);
783 		break;
784 
785 	default:
786 		error = EINVAL;
787 	}
788 	splx(s);
789 	return (error);
790 }
791 
792 /*
793  * set ethernet address for unit
794  */
795 ex_setaddr(physaddr, unit)
796 	u_char *physaddr;
797 	int unit;
798 {
799 	register struct ex_softc *xs = &ex_softc[unit];
800 	struct vba_device *ui = exinfo[unit];
801 	register struct exdevice *addr= (struct exdevice *)ui->ui_addr;
802 	register struct ex_msg *bp;
803 
804 	if (physaddr) {
805 		xs->xs_flags |= EX_SETADDR;
806 		bcopy((caddr_t)physaddr, (caddr_t)xs->xs_addr, 6);
807 	}
808 	if (! (xs->xs_flags & EX_RUNNING))
809 		return;
810 	bp = exgetcbuf(xs);
811 	bp->mb_rqst = LLNET_ADDRS;
812 	bp->mb_na.na_mask = READ_OBJ|WRITE_OBJ;
813 	bp->mb_na.na_slot = PHYSSLOT;
814 	bcopy((caddr_t)xs->xs_addr, (caddr_t)bp->mb_na.na_addrs, 6);
815 	bp->mb_status |= MH_EXOS;
816 	movow(&addr->ex_portb, EX_NTRUPT);
817 	bp = xs->xs_x2hnext;
818 	do {
819 		uncache(&bp->mb_status);
820 	} while ((bp->mb_status & MH_OWNER) == MH_EXOS);/* poll for reply */
821 #ifdef	DEBUG
822 	log(LOG_DEBUG, "ex%d: reset addr %s\n", ui->ui_unit,
823 		ether_sprintf(bp->mb_na.na_addrs));
824 #endif
825 	/*
826 	 * Now, re-enable reception on phys slot.
827 	 */
828 	bp = exgetcbuf(xs);
829 	bp->mb_rqst = LLNET_RECV;
830 	bp->mb_nr.nr_mask = ENABLE_RCV|READ_OBJ|WRITE_OBJ;
831 	bp->mb_nr.nr_slot = PHYSSLOT;
832 	bp->mb_status |= MH_EXOS;
833 	movow(&addr->ex_portb, EX_NTRUPT);
834 	bp = xs->xs_x2hnext;
835 	do {
836 		uncache(&bp->mb_status);
837 	} while ((bp->mb_status & MH_OWNER) == MH_EXOS);/* poll for reply */
838 		;
839 }
840 #endif
841