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