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