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