xref: /original-bsd/sys/vax/if/if_ex.c (revision 79cf7955)
1 /*
2  * Copyright (c) 1982, 1986 Regents of the University of California.
3  * All rights reserved.  The Berkeley software License Agreement
4  * specifies the terms and conditions for redistribution.
5  *
6  *	@(#)if_ex.c	7.3 (Berkeley) 05/27/88
7  */
8 
9 
10 #include "ex.h"
11 #if NEX > 0
12 
13 /*
14  * Excelan EXOS 204 Interface
15  *
16  *	George Powers
17  *	Excelan Inc.
18  */
19 
20 #include "param.h"
21 #include "systm.h"
22 #include "mbuf.h"
23 #include "buf.h"
24 #include "protosw.h"
25 #include "socket.h"
26 #include "vmmac.h"
27 #include "ioctl.h"
28 #include "syslog.h"
29 #include "errno.h"
30 
31 #include "../net/if.h"
32 #include "../net/netisr.h"
33 #include "../net/route.h"
34 
35 #ifdef	INET
36 #include "../netinet/in.h"
37 #include "../netinet/in_systm.h"
38 #include "../netinet/in_var.h"
39 #include "../netinet/ip.h"
40 #include "../netinet/if_ether.h"
41 #endif
42 
43 #ifdef NS
44 #include "../netns/ns.h"
45 #include "../netns/ns_if.h"
46 #endif
47 
48 #include "../vax/pte.h"
49 #include "../vax/cpu.h"
50 #include "../vax/mtpr.h"
51 #include "if_exreg.h"
52 #include "if_uba.h"
53 #include "../vaxuba/ubareg.h"
54 #include "../vaxuba/ubavar.h"
55 
56 /* #define DEBUG			/* check for "impossible" events */
57 
58 #define	NH2X 4			/* a sufficient number is critical */
59 #define	NX2H 4			/* this is pretty arbitrary */
60 #define	EXWATCHINTVL 10		/* call exwatch() every 10 seconds */
61 
62 int	exprobe(), exattach(), excdint();
63 struct	uba_device *exinfo[NEX];
64 u_short exstd[] = { 0 };
65 struct	uba_driver exdriver =
66 	{ exprobe, 0, exattach, 0, exstd, "ex", exinfo };
67 int	exinit(),exoutput(),exioctl(),exreset(),exwatch();
68 struct ex_msg *exgetcbuf();
69 
70 /*
71  * Ethernet software status per interface.
72  *
73  * Each interface is referenced by a network interface structure,
74  * xs_if, which the routing code uses to locate the interface.
75  * This structure contains the output queue for the interface, its address, ...
76  * We also have, for each interface, a UBA interface structure, which
77  * contains information about the UNIBUS resources held by the interface:
78  * map registers, buffered data paths, etc.  Information is cached in this
79  * structure for use by the if_uba.c routines in running the interface
80  * efficiently.
81  */
82 struct	ex_softc {
83 	struct	arpcom xs_ac;		/* Ethernet common part */
84 #define	xs_if	xs_ac.ac_if		/* network-visible interface */
85 #define	xs_addr	xs_ac.ac_enaddr		/* hardware Ethernet address */
86 #ifdef DEBUG
87 	int	xs_wait;
88 #endif
89 	struct	ifuba xs_ifuba;		/* UNIBUS resources */
90 	int	xs_flags;		/* private flags */
91 #define	EX_XPENDING	1		/* xmit rqst pending on EXOS */
92 #define	EX_STATPENDING	(1<<1)		/* stats rqst pending on EXOS */
93 #define	EX_RUNNING	(1<<2)		/* board is running */
94 #define EX_SETADDR	(1<<3)		/* physaddr has been changed */
95 	struct	ex_msg *xs_h2xnext;	/* host pointer to request queue */
96 	struct	ex_msg *xs_x2hnext;	/* host pointer to reply queue */
97 	int	xs_ubaddr;		/* map info for structs below */
98 #define	UNIADDR(x)	((u_long)(x)&0x3FFFF)
99 #define	P_UNIADDR(x)	((u_long)(x)&0x3FFF0)
100 	/* the following structures are always mapped in */
101 	u_short	xs_h2xhdr;		/* EXOS's request queue header */
102 	u_short	xs_x2hhdr;		/* EXOS's reply queue header */
103 	struct	ex_msg xs_h2xent[NH2X];	/* request msg buffers */
104 	struct	ex_msg xs_x2hent[NX2H];	/* reply msg buffers */
105 	struct	confmsg xs_cm;		/* configuration message */
106 	struct	stat_array xs_xsa;	/* EXOS writes stats here */
107 	/* end mapped area */
108 #define	INCORE_BASE(p)	((caddr_t)((u_long)(&(p)->xs_h2xhdr) & 0xFFFFFFF0))
109 #define	RVAL_OFF(unit, n) \
110 	((caddr_t)(&(ex_softc[unit].n)) - INCORE_BASE(&ex_softc[unit]))
111 #define	LVAL_OFF(unit, n) \
112 	((caddr_t)(ex_softc[unit].n) - INCORE_BASE(&ex_softc[unit]))
113 #define	H2XHDR_OFFSET(unit)	RVAL_OFF(unit, xs_h2xhdr)
114 #define	X2HHDR_OFFSET(unit)	RVAL_OFF(unit, xs_x2hhdr)
115 #define	H2XENT_OFFSET(unit)	LVAL_OFF(unit, xs_h2xent)
116 #define	X2HENT_OFFSET(unit)	LVAL_OFF(unit, xs_x2hent)
117 #define	CM_OFFSET(unit)		RVAL_OFF(unit, xs_cm)
118 #define	SA_OFFSET(unit)		RVAL_OFF(unit, xs_xsa)
119 #define	INCORE_SIZE(unit)	RVAL_OFF(unit, xs_end)
120 	int	xs_end;			/* place holder */
121 } ex_softc[NEX];
122 
123 /*
124  * The following structure is a kludge to store a cvec value
125  * between the time exprobe is called, and exconfig.
126  */
127 struct	ex_cvecs {
128 	struct	exdevice *xc_csraddr;
129 	int	xc_cvec;
130 }ex_cvecs[NEX];
131 
132 int	ex_ncall = 0;			/* counts calls to exprobe */
133 
134 exprobe(reg)
135 	caddr_t reg;
136 {
137 	register int br, cvec;		/* r11, r10 value-result */
138 	register struct exdevice *addr = (struct exdevice *)reg;
139 	register i;
140 
141 	/*
142 	 * We program the EXOS interrupt vector, like dmf device.
143 	 */
144 	br = 0x15;
145 	cvec = (uba_hd[numuba].uh_lastiv -= 4);
146 	ex_cvecs[ex_ncall].xc_csraddr = addr;
147 	ex_cvecs[ex_ncall].xc_cvec = cvec;
148 	/*
149 	 * Reset EXOS and run self-test (guaranteed to
150 	 * complete within 2 seconds).
151 	 */
152 	addr->xd_porta = EX_RESET;
153 	i = 2000;
154 	while (((addr->xd_portb & EX_TESTOK) == 0) && --i)
155 		DELAY(1000);
156 	if ((addr->xd_portb & EX_TESTOK) == 0) {
157 		printf("ex: self-test failed\n");
158 		return 0;
159 	}
160 #ifdef lint
161 	br = br;
162 	excdint(0);
163 #endif
164 	ex_ncall++;
165 	return (sizeof(struct exdevice));
166 }
167 
168 /*
169  * Interface exists: make available by filling in network interface
170  * record.  System will initialize the interface when it is ready
171  * to accept packets.  Board is temporarily configured and issues
172  * a NET_ADDRS command, only to get the Ethernet address.
173  */
174 exattach(ui)
175 	struct uba_device *ui;
176 {
177 	register struct ex_softc *xs = &ex_softc[ui->ui_unit];
178 	register struct ifnet *ifp = &xs->xs_if;
179 	register struct exdevice *addr = (struct exdevice *)ui->ui_addr;
180 	register struct ex_msg *bp;
181 	int unit = ui->ui_unit;
182 	ifp->if_unit = ui->ui_unit;
183 	ifp->if_name = "ex";
184 	ifp->if_mtu = ETHERMTU;
185 
186 	/*
187 	 * Temporarily map queues in order to configure EXOS
188 	 */
189 	xs->xs_ubaddr = uballoc(ui->ui_ubanum, INCORE_BASE(xs),
190 		INCORE_SIZE(unit), 0);
191 	exconfig(ui, 0);			/* without interrupts */
192 	if (xs->xs_cm.cm_cc) goto badconf;
193 
194 	bp = exgetcbuf(xs);
195 	bp->mb_rqst = LLNET_ADDRS;
196 	bp->mb_na.na_mask = READ_OBJ;
197 	bp->mb_na.na_slot = PHYSSLOT;
198 	bp->mb_status |= MH_EXOS;
199 	addr->xd_portb = EX_NTRUPT;
200 	bp = xs->xs_x2hnext;
201 	while ((bp->mb_status & MH_OWNER) == MH_EXOS)	/* poll for reply */
202 		;
203 	printf("ex%d: HW %c.%c, NX %c.%c, hardware address %s\n",
204 		ui->ui_unit, xs->xs_cm.cm_vc[2], xs->xs_cm.cm_vc[3],
205 		xs->xs_cm.cm_vc[0], xs->xs_cm.cm_vc[1],
206 		ether_sprintf(bp->mb_na.na_addrs));
207 	bcopy((caddr_t)bp->mb_na.na_addrs, (caddr_t)xs->xs_addr,
208 	    sizeof (xs->xs_addr));
209 
210 	ifp->if_init = exinit;
211 	ifp->if_output = exoutput;
212 	ifp->if_ioctl = exioctl;
213 	ifp->if_reset = exreset;
214 	ifp->if_flags = IFF_BROADCAST;
215 	xs->xs_ifuba.ifu_flags = UBA_CANTWAIT;
216 	if_attach(ifp);
217 badconf:
218 	ubarelse(ui->ui_ubanum, &xs->xs_ubaddr);
219 }
220 
221 /*
222  * Reset of interface after UNIBUS reset.
223  * If interface is on specified uba, reset its state.
224  */
225 exreset(unit, uban)
226 	int unit, uban;
227 {
228 	register struct uba_device *ui;
229 
230 	if (unit >= NEX || (ui = exinfo[unit]) == 0 || ui->ui_alive == 0 ||
231 	    ui->ui_ubanum != uban)
232 		return;
233 	printf(" ex%d", unit);
234 	ex_softc[unit].xs_if.if_flags &= ~IFF_RUNNING;
235 	ex_softc[unit].xs_flags &= ~EX_RUNNING;
236 	exinit(unit);
237 }
238 
239 /*
240  * Initialization of interface; clear recorded pending
241  * operations, and reinitialize UNIBUS usage.
242  * Called at boot time (with interrupts disabled?),
243  * and at ifconfig time via exioctl, with interrupts disabled.
244  */
245 exinit(unit)
246 	int unit;
247 {
248 	register struct ex_softc *xs = &ex_softc[unit];
249 	register struct uba_device *ui = exinfo[unit];
250 	register struct exdevice *addr = (struct exdevice *)ui->ui_addr;
251 	register struct ifnet *ifp = &xs->xs_if;
252 	register struct ex_msg *bp;
253 	int s;
254 
255 	/* not yet, if address still unknown */
256 	if (ifp->if_addrlist == (struct ifaddr *)0)
257 		return;
258 	if (xs->xs_flags & EX_RUNNING)
259 		return;
260 
261 	if ((ifp->if_flags & IFF_RUNNING) == 0) {
262 		if (if_ubainit(&xs->xs_ifuba, ui->ui_ubanum,
263 		    sizeof (struct ether_header),
264 		    (int)btoc(EXMAXRBUF-sizeof(struct ether_header))) == 0) {
265 			printf("ex%d: can't initialize\n", unit);
266 			xs->xs_if.if_flags &= ~IFF_UP;
267 			return;
268 		}
269 		xs->xs_ubaddr = uballoc(ui->ui_ubanum, INCORE_BASE(xs),
270 			INCORE_SIZE(unit), 0);
271 	}
272 	exconfig(ui, 4);		/* with vectored interrupts*/
273 	/*
274 	 * Put EXOS on the Ethernet, using NET_MODE command
275 	 */
276 	bp = exgetcbuf(xs);
277 	bp->mb_rqst = LLNET_MODE;
278 	bp->mb_nm.nm_mask = WRITE_OBJ;
279 	bp->mb_nm.nm_optn = 0;
280 	bp->mb_nm.nm_mode = MODE_PERF;
281 	bp->mb_status |= MH_EXOS;
282 	addr->xd_portb = EX_NTRUPT;
283 	bp = xs->xs_x2hnext;
284 	while ((bp->mb_status & MH_OWNER) == MH_EXOS)	/* poll for reply */
285 		;
286 	bp->mb_length = MBDATALEN;
287 	bp->mb_status |= MH_EXOS;		/* free up buffer */
288 	addr->xd_portb = EX_NTRUPT;		/* tell EXOS about it */
289 	xs->xs_x2hnext = xs->xs_x2hnext->mb_next;
290 
291 	ifp->if_watchdog = exwatch;
292 	ifp->if_timer = EXWATCHINTVL;
293 	s = splimp();	/* are interrupts always disabled here, anyway? */
294 	exhangrcv(unit);			/* hang receive request */
295 	xs->xs_if.if_flags |= IFF_RUNNING;
296 	xs->xs_flags |= EX_RUNNING;
297 	if (xs->xs_flags & EX_SETADDR)
298 		ex_setaddr((u_char *)0, unit);
299 	exstart(unit);				/* start transmits */
300 	splx(s);
301 }
302 
303 /*
304  * Reset, test, and configure EXOS.  This routine assumes
305  * that message queues, etc. have already been mapped into
306  * the UBA.  It is called by exinit, and should also be
307  * callable by exattach.
308  */
309 exconfig(ui, itype)
310 	struct	uba_device *ui;
311 	int itype;
312 {
313 	register int unit = ui->ui_unit;
314 	register struct ex_softc *xs = &ex_softc[unit];
315 	register struct exdevice *addr = (struct exdevice *) ui->ui_addr;
316 	register struct confmsg *cm = &xs->xs_cm;
317 	register struct ex_msg *bp;
318 	int i;
319 	u_long shiftreg;
320 
321 	xs->xs_flags = 0;
322 	/*
323 	 * Reset EXOS, wait for self-test to complete
324 	 */
325 	addr->xd_porta = EX_RESET;
326 	while ((addr->xd_portb & EX_TESTOK) == 0)
327 		;
328 	/*
329 	 * Set up configuration message.
330 	 */
331 	cm->cm_1rsrv = 1;
332 	cm->cm_cc = 0xFF;
333 	cm->cm_opmode = 0;		/* link-level controller mode */
334 	cm->cm_dfo = 0x0101;		/* enable host data order conversion */
335 	cm->cm_dcn1 = 1;
336 	cm->cm_2rsrv[0] =
337 		cm->cm_2rsrv[1] = 0;
338 	cm->cm_ham = 3;			/* absolute address mode */
339 	cm->cm_3rsrv = 0;
340 	cm->cm_mapsiz = 0;
341 	cm->cm_byteptrn[0] = 0x01;	/* EXOS deduces data order of host */
342 	cm->cm_byteptrn[1] = 0x03;	/*  by looking at this pattern */
343 	cm->cm_byteptrn[2] = 0x07;
344 	cm->cm_byteptrn[3] = 0x0F;
345 	cm->cm_wordptrn[0] = 0x0103;
346 	cm->cm_wordptrn[1] = 0x070F;
347 	cm->cm_lwordptrn = 0x0103070F;
348 	for (i=0; i<20; i++) cm->cm_rsrvd[i] = 0;
349 	cm->cm_mba = 0xFFFFFFFF;
350 	cm->cm_nproc = 0xFF;
351 	cm->cm_nmbox = 0xFF;
352 	cm->cm_nmcast = 0xFF;
353 	cm->cm_nhost = 1;
354 	cm->cm_h2xba = P_UNIADDR(xs->xs_ubaddr);
355 	cm->cm_h2xhdr = H2XHDR_OFFSET(unit);
356 	cm->cm_h2xtyp = 0;		/* should never wait for rqst buffer */
357 	cm->cm_x2hba = cm->cm_h2xba;
358 	cm->cm_x2hhdr = X2HHDR_OFFSET(unit);
359 	cm->cm_x2htyp = itype;		/* 0 for none, 4 for vectored */
360 	for (i=0; (addr != ex_cvecs[i].xc_csraddr); i++)
361 #ifdef DEBUG
362 	if (i >= NEX)
363 		panic("ex: matching csr address not found");
364 #endif
365 		;
366 	cm->cm_x2haddr = ex_cvecs[i].xc_cvec;	/* stashed here by exprobe */
367 	/*
368 	 * Set up message queues and headers.
369 	 * First the request queue.
370 	 */
371 	for (bp = &xs->xs_h2xent[0]; bp < &xs->xs_h2xent[NH2X]; bp++) {
372 		bp->mb_link = (u_short)((char *)(bp+1)-INCORE_BASE(xs));
373 		bp->mb_rsrv = 0;
374 		bp->mb_length = MBDATALEN;
375 		bp->mb_status = MH_HOST;
376 		bp->mb_next = bp+1;
377 	}
378 	xs->xs_h2xhdr =
379 		xs->xs_h2xent[NH2X-1].mb_link =
380 		(u_short)H2XENT_OFFSET(unit);
381 	xs->xs_h2xnext =
382 		xs->xs_h2xent[NH2X-1].mb_next =
383 		xs->xs_h2xent;
384 
385 	/* Now the reply queue. */
386 	for (bp = &xs->xs_x2hent[0]; bp < &xs->xs_x2hent[NX2H]; bp++) {
387 		bp->mb_link = (u_short)((char *)(bp+1)-INCORE_BASE(xs));
388 		bp->mb_rsrv = 0;
389 		bp->mb_length = MBDATALEN;
390 		bp->mb_status = MH_EXOS;
391 		bp->mb_next = bp+1;
392 	}
393 	xs->xs_x2hhdr =
394 		xs->xs_x2hent[NX2H-1].mb_link =
395 		(u_short)X2HENT_OFFSET(unit);
396 	xs->xs_x2hnext =
397 		xs->xs_x2hent[NX2H-1].mb_next =
398 		xs->xs_x2hent;
399 
400 	/*
401 	 * Write config msg address to EXOS and wait for
402 	 * configuration to complete (guaranteed response
403 	 * within 2 seconds).
404 	 */
405 	shiftreg = (u_long)0x0000FFFF;
406 	for (i = 0; i < 8; i++) {
407 		if (i == 4)
408 			shiftreg = P_UNIADDR(xs->xs_ubaddr) + CM_OFFSET(unit);
409 		while (addr->xd_portb & EX_UNREADY)
410 			;
411 		addr->xd_portb = (u_char)(shiftreg & 0xFF);
412 		shiftreg >>= 8;
413 	}
414 	for (i = 1000000; (cm->cm_cc == 0xFF) && i; --i);
415 	if (cm->cm_cc)
416 		printf("ex%d: configuration failed; cc = %x\n",
417 			unit, cm->cm_cc);
418 }
419 
420 /*
421  * Start or re-start output on interface.
422  * Get another datagram to send off of the interface queue,
423  * and map it to the interface before starting the output.
424  * This routine is called by exinit(), exoutput(), and excdint().
425  * In all cases, interrupts by EXOS are disabled.
426  */
427 exstart(unit)
428 	int unit;
429 {
430 	struct uba_device *ui = exinfo[unit];
431 	register struct ex_softc *xs = &ex_softc[unit];
432 	register struct exdevice *addr = (struct exdevice *)ui->ui_addr;
433 	register struct ex_msg *bp;
434 	struct mbuf *m;
435         int len;
436 
437 #ifdef DEBUG
438 	if (xs->xs_flags & EX_XPENDING)
439 		panic("exstart(): xmit still pending");
440 #endif
441 	IF_DEQUEUE(&xs->xs_if.if_snd, m);
442 	if (m == 0)
443 		return;
444 	len = if_wubaput(&xs->xs_ifuba, m);
445 	if (len - sizeof(struct ether_header) < ETHERMIN)
446 		len = ETHERMIN + sizeof(struct ether_header);
447 	/*
448 	 * Place a transmit request.
449 	 */
450 	bp = exgetcbuf(xs);
451 	bp->mb_rqst = LLRTRANSMIT;
452 	bp->mb_et.et_nblock = 1;
453 	bp->mb_et.et_blks[0].bb_len = (u_short)len;
454 	*(u_long *)bp->mb_et.et_blks[0].bb_addr =
455 		UNIADDR(xs->xs_ifuba.ifu_w.ifrw_info);
456 	xs->xs_flags |= EX_XPENDING;
457 	bp->mb_status |= MH_EXOS;
458 	addr->xd_portb = EX_NTRUPT;
459 }
460 
461 /*
462  * Command done interrupt.
463  */
464 excdint(unit)
465 	int unit;
466 {
467 	register struct ex_softc *xs = &ex_softc[unit];
468 	register struct ex_msg *bp = xs->xs_x2hnext;
469 	struct uba_device *ui = exinfo[unit];
470 	struct exdevice *addr = (struct exdevice *)ui->ui_addr;
471 
472 	while ((bp->mb_status & MH_OWNER) == MH_HOST) {
473 		switch (bp->mb_rqst) {
474 		case LLRECEIVE:
475 			exrecv(unit, bp);
476 			exhangrcv(unit);
477 			break;
478 		case LLRTRANSMIT:
479 #ifdef DEBUG
480 			if ((xs->xs_flags & EX_XPENDING) == 0)
481 				panic("exxmit: no xmit pending");
482 #endif
483 			xs->xs_flags &= ~EX_XPENDING;
484 			xs->xs_if.if_opackets++;
485 			if (bp->mb_rply == LL_OK) {
486 				;
487 			} else if (bp->mb_rply & LLXM_1RTRY) {
488 				xs->xs_if.if_collisions++;
489 			} else if (bp->mb_rply & LLXM_RTRYS) {
490 				xs->xs_if.if_collisions += 2;	/* guess */
491 			} else if (bp->mb_rply & LLXM_ERROR) {
492 				xs->xs_if.if_oerrors++;
493 				log(LOG_ERR, "ex%d: transmit error=%b\n",
494 					unit, bp->mb_rply, XMIT_BITS);
495 			}
496 			if (xs->xs_ifuba.ifu_xtofree) {
497 				m_freem(xs->xs_ifuba.ifu_xtofree);
498 				xs->xs_ifuba.ifu_xtofree = 0;
499 			}
500 			exstart(unit);
501 			break;
502 		case LLNET_STSTCS:
503 			xs->xs_if.if_ierrors = xs->xs_xsa.sa_crc;
504 			xs->xs_flags &= ~EX_STATPENDING;
505 			break;
506 		case LLNET_ADDRS:
507 		case LLNET_RECV:
508 			break;
509 #ifdef	DEBUG
510 		default:
511 			panic("ex%d: unknown reply");
512 #endif
513 		} /* end of switch */
514 		bp->mb_length = MBDATALEN;
515 		bp->mb_status |= MH_EXOS;		/* free up buffer */
516 		addr->xd_portb = EX_NTRUPT;		/* tell EXOS about it */
517 		bp = xs->xs_x2hnext = xs->xs_x2hnext->mb_next;
518 	}
519 }
520 
521 /*
522  * Get a request buffer, fill in standard values, advance pointer.
523  */
524 struct ex_msg *
525 exgetcbuf(xs)
526 	struct ex_softc *xs;
527 {
528 	register struct ex_msg *bp = xs->xs_h2xnext;
529 
530 #ifdef DEBUG
531 	if ((bp->mb_status & MH_OWNER) == MH_EXOS)
532 		panic("exgetcbuf(): EXOS owns message buffer");
533 #endif
534 	bp->mb_1rsrv = 0;
535 	bp->mb_length = MBDATALEN;
536 	xs->xs_h2xnext = xs->xs_h2xnext->mb_next;
537 	return bp;
538 }
539 
540 /*
541  * Process Ethernet receive completion:
542  *	If input error just drop packet.
543  *	Otherwise purge input buffered data path and examine
544  *	packet to determine type.  If can't determine length
545  *	from type, then have to drop packet.  Otherwise decapsulate
546  *	packet based on type and pass to type-specific higher-level
547  *	input routine.
548  */
549 exrecv(unit, bp)
550 	int unit;
551 	register struct ex_msg *bp;
552 {
553 	register struct ex_softc *xs = &ex_softc[unit];
554 	register struct ether_header *eh;
555     	struct mbuf *m;
556 	register int len, off, resid;
557 	register struct ifqueue *inq;
558 	int s;
559 
560 	xs->xs_if.if_ipackets++;
561 	len = bp->mb_er.er_blks[0].bb_len - sizeof(struct ether_header) - 4;
562 	if (bp->mb_rply != LL_OK) {
563 		xs->xs_if.if_ierrors++;
564 		log(LOG_ERR, "ex%d: receive error=%b\n",
565 			unit, bp->mb_rply, RECV_BITS);
566 		return;
567 	}
568 	eh = (struct ether_header *)(xs->xs_ifuba.ifu_r.ifrw_addr);
569 
570 	/*
571 	 * Deal with trailer protocol: if type is trailer
572 	 * get true type from first 16-bit word past data.
573 	 * Remember that type was trailer by setting off.
574 	 */
575 	eh->ether_type = ntohs((u_short)eh->ether_type);
576 #define	exdataaddr(eh, off, type)	((type)(((caddr_t)((eh)+1)+(off))))
577 	if (eh->ether_type >= ETHERTYPE_TRAIL &&
578 	    eh->ether_type < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) {
579 		off = (eh->ether_type - ETHERTYPE_TRAIL) * 512;
580 		if (off >= ETHERMTU)
581 			return;		/* sanity */
582 		eh->ether_type = ntohs(*exdataaddr(eh, off, u_short *));
583 		resid = ntohs(*(exdataaddr(eh, off+2, u_short *)));
584 		if (off + resid > len)
585 			return;		/* sanity */
586 		len = off + resid;
587 	} else
588 		off = 0;
589 	if (len == 0)
590 		return;
591 
592 	/*
593 	 * Pull packet off interface.  Off is nonzero if packet
594 	 * has trailing header; if_rubaget will then force this header
595 	 * information to be at the front, but we still have to drop
596 	 * the type and length which are at the front of any trailer data.
597 	 */
598 	m = if_rubaget(&xs->xs_ifuba, len, off, &xs->xs_if);
599 	if (m == 0)
600 		return;
601 	if (off) {
602 		struct ifnet *ifp;
603 
604 		ifp = *(mtod(m, struct ifnet **));
605 		m->m_off += 2 * sizeof (u_short);
606 		m->m_len -= 2 * sizeof (u_short);
607 		*(mtod(m, struct ifnet **)) = ifp;
608 	}
609 	switch (eh->ether_type) {
610 
611 #ifdef INET
612 	case ETHERTYPE_IP:
613 		schednetisr(NETISR_IP);	/* is this necessary */
614 		inq = &ipintrq;
615 		break;
616 
617 	case ETHERTYPE_ARP:
618 		arpinput(&xs->xs_ac, m);
619 		return;
620 #endif
621 #ifdef NS
622 	case ETHERTYPE_NS:
623 		schednetisr(NETISR_NS);
624 		inq = &nsintrq;
625 		break;
626 
627 #endif
628 	default:
629 		m_freem(m);
630 		return;
631 	}
632 
633 	s = splimp();
634 	if (IF_QFULL(inq)) {
635 		IF_DROP(inq);
636 		m_freem(m);
637 	} else
638 		IF_ENQUEUE(inq, m);
639 	splx(s);
640 }
641 
642 /*
643  * Send receive request to EXOS.
644  * This routine is called by exinit and excdint,
645  * with interrupts disabled in both cases.
646  */
647 exhangrcv(unit)
648 	int unit;
649 {
650 	register struct ex_softc *xs = &ex_softc[unit];
651 	register struct ex_msg *bp = exgetcbuf(xs);
652 	struct exdevice *addr = (struct exdevice *)exinfo[unit]->ui_addr;
653 
654 	bp->mb_rqst = LLRECEIVE;
655 	bp->mb_er.er_nblock = 1;
656 	bp->mb_er.er_blks[0].bb_len = EXMAXRBUF;
657 	*(u_long *)bp->mb_er.er_blks[0].bb_addr =
658 		UNIADDR(xs->xs_ifuba.ifu_r.ifrw_info);
659 	bp->mb_status |= MH_EXOS;
660 	addr->xd_portb = EX_NTRUPT;
661 }
662 
663 /*
664  * Ethernet output routine.
665  * Encapsulate a packet of type family for the local net.
666  * Use trailer local net encapsulation if enough data in first
667  * packet leaves a multiple of 512 bytes of data in remainder.
668  */
669 exoutput(ifp, m0, dst)
670 	register struct ifnet *ifp;
671 	register struct mbuf *m0;
672 	struct sockaddr *dst;
673 {
674 	int type, s, error;
675 	u_char edst[6];
676 	struct in_addr idst;
677 	register struct ex_softc *xs = &ex_softc[ifp->if_unit];
678 	register struct mbuf *m = m0;
679 	register struct ether_header *eh;
680 	register int off;
681 	int usetrailers;
682 
683 	if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) {
684 		error = ENETDOWN;
685 		goto bad;
686 	}
687 	switch (dst->sa_family) {
688 
689 #ifdef INET
690 	case AF_INET:
691 		idst = ((struct sockaddr_in *)dst)->sin_addr;
692 		if (!arpresolve(&xs->xs_ac, m, &idst, edst, &usetrailers))
693 			return (0);	/* if not yet resolved */
694 		off = ntohs((u_short)mtod(m, struct ip *)->ip_len) - m->m_len;
695 		if (usetrailers && off > 0 && (off & 0x1ff) == 0 &&
696 		    m->m_off >= MMINOFF + 2 * sizeof (u_short)) {
697 			type = ETHERTYPE_TRAIL + (off>>9);
698 			m->m_off -= 2 * sizeof (u_short);
699 			m->m_len += 2 * sizeof (u_short);
700 			*mtod(m, u_short *) = htons((u_short)ETHERTYPE_IP);
701 			*(mtod(m, u_short *) + 1) = htons((u_short)m->m_len);
702 			goto gottrailertype;
703 		}
704 		type = ETHERTYPE_IP;
705 		off = 0;
706 		goto gottype;
707 #endif
708 #ifdef NS
709 	case AF_NS:
710 		type = ETHERTYPE_NS;
711  		bcopy((caddr_t)&(((struct sockaddr_ns *)dst)->sns_addr.x_host),
712 		(caddr_t)edst, sizeof (edst));
713 		off = 0;
714 		goto gottype;
715 #endif
716 
717 	case AF_UNSPEC:
718 		eh = (struct ether_header *)dst->sa_data;
719 		bcopy((caddr_t)eh->ether_dhost, (caddr_t)edst, sizeof (edst));
720 		type = eh->ether_type;
721 		goto gottype;
722 
723 	default:
724 		printf("ex%d: can't handle af%d\n", ifp->if_unit,
725 			dst->sa_family);
726 		error = EAFNOSUPPORT;
727 		goto bad;
728 	}
729 
730 gottrailertype:
731 	/*
732 	 * Packet to be sent as trailer: move first packet
733 	 * (control information) to end of chain.
734 	 */
735 	while (m->m_next)
736 		m = m->m_next;
737 	m->m_next = m0;
738 	m = m0->m_next;
739 	m0->m_next = 0;
740 	m0 = m;
741 
742 gottype:
743 	/*
744 	 * Add local net header.  If no space in first mbuf,
745 	 * allocate another.
746 	 */
747 	if (m->m_off > MMAXOFF ||
748 	    MMINOFF + sizeof (struct ether_header) > m->m_off) {
749 		m = m_get(M_DONTWAIT, MT_HEADER);
750 		if (m == 0) {
751 			error = ENOBUFS;
752 			goto bad;
753 		}
754 		m->m_next = m0;
755 		m->m_off = MMINOFF;
756 		m->m_len = sizeof (struct ether_header);
757 	} else {
758 		m->m_off -= sizeof (struct ether_header);
759 		m->m_len += sizeof (struct ether_header);
760 	}
761 	eh = mtod(m, struct ether_header *);
762 	eh->ether_type = htons((u_short)type);
763 	bcopy((caddr_t)edst, (caddr_t)eh->ether_dhost, sizeof (edst));
764 	bcopy((caddr_t)xs->xs_addr, (caddr_t)eh->ether_shost, 6);
765 
766 	/*
767 	 * Queue message on interface, and start output if interface
768 	 * not yet active.
769 	 */
770 	s = splimp();
771 	if (IF_QFULL(&ifp->if_snd)) {
772 		IF_DROP(&ifp->if_snd);
773 		splx(s);
774 		m_freem(m);
775 		return (ENOBUFS);
776 	}
777 	IF_ENQUEUE(&ifp->if_snd, m);
778 	/*
779 	 * If transmit request not already pending, then
780 	 * kick the back end.
781 	 */
782 	if ((xs->xs_flags & EX_XPENDING) == 0) {
783 		exstart(ifp->if_unit);
784 	}
785 #ifdef DEBUG
786 	else {
787 		xs->xs_wait++;
788 	}
789 #endif
790 	splx(s);
791 	return (0);
792 
793 bad:
794 	m_freem(m0);
795 	return (error);
796 }
797 
798 /*
799  * Watchdog routine - place stats request to EXOS
800  * (This could be dispensed with, if you don't care
801  *  about the if_ierrors count, or are willing to receive
802  *  bad packets in order to derive it.)
803  */
804 exwatch(unit)
805 	int unit;
806 {
807 	struct uba_device *ui = exinfo[unit];
808 	struct exdevice *addr = (struct exdevice *)ui->ui_addr;
809 	register struct ex_softc *xs = &ex_softc[unit];
810 	register struct ex_msg *bp;
811 	int s = splimp();
812 
813 	if (xs->xs_flags & EX_STATPENDING) goto exspnd;
814 	bp = exgetcbuf(xs);
815 	xs->xs_flags |= EX_STATPENDING;
816 	bp->mb_rqst = LLNET_STSTCS;
817 	bp->mb_ns.ns_mask = READ_OBJ;
818 	bp->mb_ns.ns_rsrv = 0;
819 	bp->mb_ns.ns_nobj = 8;		/* read all 8 stats objects */
820 	bp->mb_ns.ns_xobj = 0;		/* starting with the 1st one */
821 	bp->mb_ns.ns_bufp = P_UNIADDR(xs->xs_ubaddr) + SA_OFFSET(unit);
822 	bp->mb_status |= MH_EXOS;
823 	addr->xd_portb = EX_NTRUPT;
824 exspnd:
825 	splx(s);
826 	xs->xs_if.if_timer = EXWATCHINTVL;
827 }
828 
829 /*
830  * Process an ioctl request.
831  */
832 exioctl(ifp, cmd, data)
833 	register struct ifnet *ifp;
834 	int cmd;
835 	caddr_t data;
836 {
837 	register struct ifaddr *ifa = (struct ifaddr *)data;
838 	register struct ex_softc *xs = &ex_softc[ifp->if_unit];
839 	int s = splimp(), error = 0;
840 
841 	switch (cmd) {
842 
843 	case SIOCSIFADDR:
844                 ifp->if_flags |= IFF_UP;
845                 exinit(ifp->if_unit);
846 
847                 switch (ifa->ifa_addr.sa_family) {
848 #ifdef INET
849 		case AF_INET:
850 			((struct arpcom *)ifp)->ac_ipaddr =
851 				IA_SIN(ifa)->sin_addr;
852 			arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr);
853 			break;
854 #endif
855 #ifdef NS
856 		case AF_NS:
857 		    {
858 			register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr);
859 
860 			if (ns_nullhost(*ina))
861 				ina->x_host = *(union ns_host *)(xs->xs_addr);
862 			else
863 				ex_setaddr(ina->x_host.c_host,ifp->if_unit);
864 			break;
865 		    }
866 #endif
867 		}
868 		break;
869 
870 	case SIOCSIFFLAGS:
871 		if ((ifp->if_flags & IFF_UP) == 0 &&
872 		    xs->xs_flags & EX_RUNNING) {
873 			((struct exdevice *)
874 			  (exinfo[ifp->if_unit]->ui_addr))->xd_porta = EX_RESET;
875 			xs->xs_flags &= ~EX_RUNNING;
876 		} else if (ifp->if_flags & IFF_UP &&
877 		    (xs->xs_flags & EX_RUNNING) == 0)
878 			exinit(ifp->if_unit);
879 		break;
880 
881 	default:
882 		error = EINVAL;
883 	}
884 	splx(s);
885 	return (error);
886 }
887 
888 /*
889  * set ethernet address for unit
890  */
891 ex_setaddr(physaddr, unit)
892 	u_char *physaddr;
893 	int unit;
894 {
895 	register struct ex_softc *xs = &ex_softc[unit];
896 	struct uba_device *ui = exinfo[unit];
897 	register struct exdevice *addr= (struct exdevice *)ui->ui_addr;
898 	register struct ex_msg *bp;
899 
900 	if (physaddr) {
901 		xs->xs_flags |= EX_SETADDR;
902 		bcopy((caddr_t)physaddr, (caddr_t)xs->xs_addr, 6);
903 	}
904 	if (! (xs->xs_flags & EX_RUNNING))
905 		return;
906 	bp = exgetcbuf(xs);
907 	bp->mb_rqst = LLNET_ADDRS;
908 	bp->mb_na.na_mask = READ_OBJ|WRITE_OBJ;
909 	bp->mb_na.na_slot = PHYSSLOT;
910 	bcopy((caddr_t)xs->xs_addr, (caddr_t)bp->mb_na.na_addrs, 6);
911 	bp->mb_status |= MH_EXOS;
912 	addr->xd_portb = EX_NTRUPT;
913 	bp = xs->xs_x2hnext;
914 	while ((bp->mb_status & MH_OWNER) == MH_EXOS)	/* poll for reply */
915 		;
916 #ifdef	DEBUG
917 	log(LOG_DEBUG, "ex%d: reset addr %s\n", ui->ui_unit,
918 		ether_sprintf(bp->mb_na.na_addrs));
919 #endif
920 	/*
921 	 * Now, re-enable reception on phys slot.
922 	 */
923 	bp = exgetcbuf(xs);
924 	bp->mb_rqst = LLNET_RECV;
925 	bp->mb_nr.nr_mask = ENABLE_RCV|READ_OBJ|WRITE_OBJ;
926 	bp->mb_nr.nr_slot = PHYSSLOT;
927 	bp->mb_status |= MH_EXOS;
928 	addr->xd_portb = EX_NTRUPT;
929 	bp = xs->xs_x2hnext;
930 	while ((bp->mb_status & MH_OWNER) == MH_EXOS)	/* poll for reply */
931 		;
932 }
933 #endif
934