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