xref: /original-bsd/sys/tahoe/if/if_ex.c (revision 608e088d)
1 /*
2  * Copyright (c) 1989 The 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 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
19  *
20  *	@(#)if_ex.c	7.3 (Berkeley) 06/20/90
21  */
22 
23 #include "ex.h"
24 
25 #if	NEX > 0
26 
27 /*
28  * Excelan EXOS 202(VME) & 203(QBUS) Link Level Ethernet Interface Drivers
29  */
30 #include "param.h"
31 #include "systm.h"
32 #include "mbuf.h"
33 #include "buf.h"
34 #include "protosw.h"
35 #include "socket.h"
36 #include "vmmac.h"
37 #include "ioctl.h"
38 #include "errno.h"
39 #include "vmparam.h"
40 #include "syslog.h"
41 #include "uio.h"
42 
43 #include "../net/if.h"
44 #include "../net/netisr.h"
45 #include "../net/route.h"
46 
47 #ifdef INET
48 #include "../netinet/in.h"
49 #include "../netinet/in_systm.h"
50 #include "../netinet/in_var.h"
51 #include "../netinet/ip.h"
52 #include "../netinet/if_ether.h"
53 #endif
54 
55 #ifdef NS
56 #include "../netns/ns.h"
57 #include "../netns/ns_if.h"
58 #endif
59 
60 #ifdef ISO
61 #include "../netiso/iso.h"
62 #include "../netiso/iso_var.h"
63 extern char all_es_snpa[], all_is_snpa[];
64 #endif
65 
66 #include "../tahoe/cpu.h"
67 #include "../tahoe/pte.h"
68 #include "../tahoe/mtpr.h"
69 
70 #include "../tahoevba/vbavar.h"
71 #include "if_exreg.h"
72 #include "if_vba.h"
73 
74 
75 #define	NH2X 32			/* Host to eXcelan request buffers */
76 
77 #define	NX2H 16			/* eXcelan to Host reply buffers */
78 #define	NREC	16		/* Number of RECeive buffers */
79 #define	NTRB	4		/* Number of TRansmit Buffers */
80 #define NVBI	(NREC + NTRB)
81 
82 #define EXWATCHINTVL	10	/* call exwatch every x secs */
83 
84 int	exprobe(), exslave(), exattach(), exintr(), exstart();
85 struct	vba_device *exinfo[NEX];
86 
87 long	exstd[] = { 0 };
88 
89 
90 struct	vba_driver exdriver =
91 	{ exprobe, 0, exattach, exstart, exstd, "ex", exinfo };
92 int	exinit(),ether_output(),exioctl(),exreset(),exwatch();
93 struct	ex_msg *exgetcbuf();
94 int	ex_ncall = 0;			/* counts calls to exprobe */
95 u_long	busoff;
96 
97 /*
98  * Ethernet software status per interface.
99  *
100  * Each interface is referenced by a network interface structure, xs_if, which
101  * the routing code uses to locate the interface.  This structure contains the
102  * output queue for the interface, its address, ... NOTE: To configure multiple
103  * controllers, the sizeof this structure must be a multiple of 16 (xs_h2xhdr).
104  */
105 struct	ex_softc {
106 	struct		arpcom xs_ac;	/* Ethernet common part */
107 #define	xs_if		xs_ac.ac_if	/* network-visible interface */
108 #define	xs_addr		xs_ac.ac_enaddr	/* hardware Ethernet address */
109 	int		xs_flags;	/* private flags */
110 #define	EX_XPENDING	1		/* xmit rqst pending on EXOS */
111 #define	EX_STATPENDING	(1<<1)		/* stats rqst pending on EXOS */
112 #define	EX_RUNNING	(1<<2)		/* board is running */
113 #define EX_SETADDR	(1<<3)		/* physaddr has been changed */
114 	int		xs_cvec;	/* probe stores cvec here */
115 	short		xs_enetunit;	/* unit number for enet filtering */
116 	short		xs_enetinit;	/* enet inetrface is initialized */
117 	struct	ex_msg	*xs_h2xnext;	/* host pointer to request queue */
118 	struct	ex_msg 	*xs_x2hnext;	/* host pointer to reply queue */
119 	u_long		xs_qbaddr;	/* map info for structs below */
120 	struct	ex_shm	{
121 	/* the following structures are always mapped in */
122 	u_short		sm_h2xhdr;	/* EXOS's request queue header */
123 	u_short		sm_x2hhdr;	/* EXOS's reply queue header */
124 	struct ex_msg 	sm_h2xent[NH2X];/* request msg buffers */
125 	struct ex_msg 	sm_x2hent[NX2H];/* reply msg buffers */
126 	struct ex_conf	sm_cm;		/* configuration message */
127 	struct ex_stat	sm_xsa;	/* EXOS writes stats here */
128 	/* end mapped area */
129 	} 		*xs_shm;	/* host pointer to shared area */
130 #define	xs_h2xhdr	xs_shm->sm_h2xhdr
131 #define	xs_x2hhdr	xs_shm->sm_x2hhdr
132 #define	xs_h2xent	xs_shm->sm_h2xent
133 #define	xs_x2hent	xs_shm->sm_x2hent
134 #define	xs_cm		xs_shm->sm_cm
135 #define	xs_xsa		xs_shm->sm_xsa
136 #define	BUSADDR(x)	(0x3D000000 | (((u_long)kvtophys(x))&0xFFFFFF))
137 #define	P_BUSADDR(x)	(0x3D000000 | (((u_long)kvtophys(x))&0xFFFFF0))
138 #define	INCORE_BASE(p)	(((u_long)(p)->xs_shm) & 0xFFFFFFF0)
139 /* we will arrange that the shared memory begins on a 16 byte boundary */
140 #define	RVAL_OFF(n)	(((char *)&(((struct ex_shm *)0)->n))-(char *)0)
141 #define	LVAL_OFF(n)	(((char *)(((struct ex_shm *)0)->n))-(char *)0)
142 #define	H2XHDR_OFFSET	RVAL_OFF(sm_h2xhdr)
143 #define	X2HHDR_OFFSET	RVAL_OFF(sm_x2hhdr)
144 #define	H2XENT_OFFSET	LVAL_OFF(sm_h2xent)
145 #define	X2HENT_OFFSET	LVAL_OFF(sm_x2hent)
146 #define	CM_OFFSET	RVAL_OFF(sm_cm)
147 #define	SA_OFFSET	RVAL_OFF(sm_xsa)
148 	struct		ifvba xs_vbinfo[NVBI];/* Bus Resources (low core) */
149 	struct		ifvba *xs_pkblist; /* free list of above */
150 #define GetPkBuf(b, v)  ((v = (b)->mb_pkb = xs->xs_pkblist),\
151 		      (xs->xs_pkblist = (struct ifvba *)(v)->iff_mbuf))
152 #define FreePkBuf(v) (((v)->iff_mbuf = (struct mbuf *)xs->xs_pkblist),\
153 							(xs->xs_pkblist = v))
154 	char		xs_nrec;	/* number of pending receive buffers */
155 	char		xs_ntrb;	/* number of pending transmit buffers */
156 } ex_softc[NEX];
157 
158 int ex_padcheck = sizeof (struct ex_softc);
159 
160 exprobe(reg, vi)
161 	caddr_t reg;
162 	struct vba_device *vi;
163 {
164 	register br, cvec;		/* r12, r11 value-result */
165 	register struct exdevice *exaddr = (struct exdevice *)reg;
166 	int	i;
167 
168 	if (badaddr((caddr_t)exaddr, 2))
169 		return 0;
170 	/*
171 	 * Reset EXOS and run self-test (should complete within 2 seconds).
172 	 */
173 	movow(&exaddr->ex_porta, EX_RESET);
174 	for (i = 1000000; i; i--) {
175 		uncache(&(exaddr->ex_portb));
176 		if (exaddr->ex_portb & EX_TESTOK)
177 			break;
178 	}
179 	if ((exaddr->ex_portb & EX_TESTOK) == 0)
180 		return 0;
181 	br = 0x15;
182 	cvec = --vi->ui_hd->vh_lastiv;
183 	ex_softc[vi->ui_unit].xs_cvec = cvec;
184 	ex_ncall++;
185 	return (sizeof(struct exdevice));
186 }
187 
188 /*
189  * Interface exists: make available by filling in network interface record.
190  * System will initialize the interface when it is ready to accept packets.
191  * A NET_ADDRS command is done to get the ethernet address.
192  */
193 exattach(ui)
194 	register struct vba_device	*ui;
195 {
196 	register struct ex_softc *xs = &ex_softc[ui->ui_unit];
197 	register struct ifnet *ifp = &xs->xs_if;
198 	register struct exdevice *exaddr = (struct exdevice *)ui->ui_addr;
199 	register struct ex_msg *bp;
200 
201 	ifp->if_unit = ui->ui_unit;
202 	ifp->if_name = "ex";
203 	ifp->if_mtu = ETHERMTU;
204 	ifp->if_init = exinit;
205 	ifp->if_ioctl = exioctl;
206 	ifp->if_output = ether_output;
207 	ifp->if_reset = exreset;
208 	ifp->if_start = exstart;
209 	ifp->if_flags = IFF_BROADCAST;
210 
211 	/*
212 	 * Note: extra memory gets returned by if_vbareserve()
213 	 * first, so, being page alligned, it is also 16-byte alligned.
214 	 */
215 	if (if_vbareserve(xs->xs_vbinfo, NVBI, EXMAXRBUF,
216 			(caddr_t *)&xs->xs_shm, sizeof(*xs->xs_shm)) == 0)
217 		return;
218 	/*
219 	 * Temporarily map queues in order to configure EXOS
220 	 */
221 	xs->xs_qbaddr = INCORE_BASE(xs);
222 	exconfig(ui, 0);			/* without interrupts */
223 	if (xs->xs_cm.cm_cc)
224 		return;				/* bad conf */
225 	/*
226 	 * Get Ethernet address.
227 	 */
228 	if ((bp = exgetcbuf(xs, LLNET_ADDRS)) == (struct ex_msg *)0)
229 		panic("exattach");
230 	bp->mb_na.na_mask = READ_OBJ;
231 	bp->mb_na.na_slot = PHYSSLOT;
232 	bp->mb_status |= MH_EXOS;
233 	movow(&exaddr->ex_portb, EX_NTRUPT);
234 	bp = xs->xs_x2hnext;
235 	while ((bp->mb_status & MH_OWNER) == MH_EXOS);/* poll for reply */
236 	printf("ex%d: HW %c.%c NX %c.%c, hardware address %s\n",
237 		ui->ui_unit, xs->xs_cm.cm_vc[2], xs->xs_cm.cm_vc[3],
238 		xs->xs_cm.cm_vc[0], xs->xs_cm.cm_vc[1],
239 		ether_sprintf(bp->mb_na.na_addrs));
240 	bcopy((caddr_t)bp->mb_na.na_addrs, (caddr_t)xs->xs_addr,
241 		sizeof(xs->xs_addr));
242 	if_attach(ifp);
243 }
244 
245 /*
246  * Reset of interface after BUS reset.
247  * If interface is on specified vba, reset its state.
248  */
249 exreset(unit)
250 int unit;
251 {
252 	register struct vba_device *ui;
253 
254 	if (unit >= NEX || (ui = exinfo[unit]) == 0 || ui->ui_alive == 0)
255 		return;
256 	printf(" ex%d", unit);
257 	ex_softc[unit].xs_if.if_flags &= ~IFF_RUNNING;
258 	ex_softc[unit].xs_flags &= ~EX_RUNNING;
259 
260 	exinit(unit);
261 }
262 
263 /*
264  * Initialization of interface; clear recorded pending operations, and
265  * reinitialize BUS usage. Called at boot time, and at ifconfig time via
266  * exioctl, with interrupts disabled.
267  */
268 exinit(unit)
269 int unit;
270 {
271 	register struct ex_softc *xs = &ex_softc[unit];
272 	register struct vba_device *ui = exinfo[unit];
273 	register struct exdevice *exaddr = (struct exdevice *)ui->ui_addr;
274 	register struct ifnet *ifp = &xs->xs_if;
275 	register struct sockaddr_in *sin;
276 	register struct ex_msg 	*bp;
277 	int s;
278 
279 	/* not yet, if address still unknown */
280 	if (ifp->if_addrlist == (struct ifaddr *)0)
281 		return;
282 	if (xs->xs_flags & EX_RUNNING)
283 		return;
284 
285 	xs->xs_qbaddr = INCORE_BASE(xs);
286 	exconfig(ui, 4);		/* with vectored interrupts*/
287 
288 	/*
289 	 * Put EXOS on the Ethernet, using NET_MODE command
290 	 */
291 	if ((bp = exgetcbuf(xs, LLNET_MODE)) == (struct ex_msg *)0)
292 		panic("exinit");
293 	bp->mb_nm.nm_mask = WRITE_OBJ;
294 	bp->mb_nm.nm_optn = 0;
295 	bp->mb_nm.nm_mode = MODE_PERF;
296 	bp->mb_status |= MH_EXOS;
297 	movow(&exaddr->ex_portb, EX_NTRUPT);
298 	bp = xs->xs_x2hnext;
299 	while ((bp->mb_status & MH_OWNER) == MH_EXOS) /* poll for reply */
300 		;
301 	bp->mb_length = MBDATALEN;
302 	bp->mb_status |= MH_EXOS;		/* free up buffer */
303 	movow(&exaddr->ex_portb, EX_NTRUPT);
304 	xs->xs_x2hnext = xs->xs_x2hnext->mb_next;
305 
306 	ifp->if_watchdog = exwatch;
307 	ifp->if_timer = EXWATCHINTVL;
308 	s = splimp();		/* are interrupts disabled here, anyway? */
309 	exhangrcv(unit);
310 	xs->xs_if.if_flags |= IFF_RUNNING;
311 	xs->xs_flags |= EX_RUNNING;
312 	if (xs->xs_flags & EX_SETADDR)
313 		ex_setaddr((u_char *)0, unit);
314 #ifdef ISO
315 	ex_setmulti(all_es_snpa, unit, 1);
316 	ex_setmulti(all_is_snpa, unit, 2);
317 #endif
318 	exstart(&ex_softc[unit].xs_if);		/* start transmits */
319 	splx(s);		/* are interrupts disabled here, anyway? */
320 }
321 
322 /*
323  * Reset, test, and configure EXOS.  It is called by exinit, and exattach.
324  * Returns 0 if successful, 1 if self-test failed.
325  */
326 exconfig(ui, itype)
327 struct	vba_device *ui;
328 int itype;
329 {
330 	register int unit = ui->ui_unit;
331 	register struct ex_softc *xs = &ex_softc[unit];
332 	register struct exdevice *exaddr = (struct exdevice *) ui->ui_addr;
333 	register struct ex_conf *cm = &xs->xs_cm;
334 	register struct ex_msg 	*bp;
335 	register struct ifvba *pkb;
336 	int 	i;
337 	u_long 	shiftreg;
338 	static	u_char	cmaddr[8] = {0xFF, 0xFF, 0, 0};
339 
340 	xs->xs_flags = 0;
341 	/*
342 	 * Reset EXOS, wait for self-test to complete
343 	 */
344 	movow(&exaddr->ex_porta, EX_RESET);
345 	do {
346 		uncache(&exaddr->ex_portb);
347 	} while ((exaddr->ex_portb & EX_TESTOK) == 0) ;
348 	/*
349 	 * Set up configuration message.
350 	 */
351 	cm->cm_1rsrv = 1;
352 	cm->cm_cc = 0xFF;
353 	cm->cm_opmode = 0;		/* link-level controller mode */
354 	cm->cm_dfo = 0x0101;		/* enable host data order conversion */
355 	cm->cm_dcn1 = 1;
356 	cm->cm_2rsrv[0] = cm->cm_2rsrv[1] = 0;
357 	cm->cm_ham = 3;			/* absolute address mode */
358 	cm->cm_3rsrv = 0;
359 	cm->cm_mapsiz = 0;
360 	cm->cm_byteptrn[0] = 0x01;	/* EXOS deduces data order of host */
361 	cm->cm_byteptrn[1] = 0x03;	/*  by looking at this pattern */
362 	cm->cm_byteptrn[2] = 0x07;
363 	cm->cm_byteptrn[3] = 0x0F;
364 	cm->cm_wordptrn[0] = 0x0103;
365 	cm->cm_wordptrn[1] = 0x070F;
366 	cm->cm_lwordptrn = 0x0103070F;
367 	for (i=0; i<20; i++) cm->cm_rsrvd[i] = 0;
368 	cm->cm_mba = 0xFFFFFFFF;
369 	cm->cm_nproc = 0xFF;
370 	cm->cm_nmbox = 0xFF;
371 	cm->cm_nmcast = 0xFF;
372 	cm->cm_nhost = 1;
373 	cm->cm_h2xba = P_BUSADDR(xs->xs_qbaddr);
374 	cm->cm_h2xhdr = H2XHDR_OFFSET;
375 	cm->cm_h2xtyp = 0;		/* should never wait for rqst buffer */
376 	cm->cm_x2hba = cm->cm_h2xba;
377 	cm->cm_x2hhdr = X2HHDR_OFFSET;
378 	cm->cm_x2htyp = itype;		/* 0 for none, 4 for vectored */
379 	cm->cm_x2haddr = xs->xs_cvec;	/* ivec allocated in 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 = xs->xs_h2xent[NH2X-1].mb_link = (u_short)H2XENT_OFFSET;
392 	xs->xs_h2xnext = xs->xs_h2xent[NH2X-1].mb_next = xs->xs_h2xent;
393 
394 	/* Now the reply queue. */
395 	for (bp = &xs->xs_x2hent[0]; bp < &xs->xs_x2hent[NX2H]; bp++) {
396 		bp->mb_link = (u_short)((char *)(bp+1)-INCORE_BASE(xs));
397 		bp->mb_rsrv = 0;
398 		bp->mb_length = MBDATALEN;
399 		bp->mb_status = MH_EXOS;
400 		bp->mb_next = bp+1;
401 	}
402 	xs->xs_x2hhdr = xs->xs_x2hent[NX2H-1].mb_link = (u_short)X2HENT_OFFSET;
403 	xs->xs_x2hnext = xs->xs_x2hent[NX2H-1].mb_next = xs->xs_x2hent;
404 	xs->xs_nrec = 0;
405 	xs->xs_ntrb = 0;
406 	xs->xs_pkblist =  xs->xs_vbinfo + NVBI - 1;
407 	for (pkb = xs->xs_pkblist; pkb > xs->xs_vbinfo; pkb--)
408 		pkb->iff_mbuf = (struct mbuf *)(pkb - 1);
409 	xs->xs_vbinfo[0].iff_mbuf = 0;
410 
411 	/*
412 	 * Write config msg address to EXOS and wait for configuration to
413 	 * complete (guaranteed response within 2 seconds).
414 	 */
415 	shiftreg = P_BUSADDR(xs->xs_qbaddr) + CM_OFFSET;
416 	for (i = 4; i < 8; i++) {
417 		cmaddr[i] = (u_char)(shiftreg & 0xFF);
418 		shiftreg >>= 8;
419 	}
420 	for (i = 0; i < 8; i++) {
421 		do {
422 			uncache(&exaddr->ex_portb);
423 		} while (exaddr->ex_portb & EX_UNREADY) ;
424 		DELAY(500);
425 		movow(&exaddr->ex_portb, cmaddr[i]);
426 	}
427 	for (i = 500000; i; --i) {
428 		DELAY(10);
429 		uncache(&cm->cm_cc);
430 		if (cm->cm_cc != 0xFF)
431 			break;
432 	}
433 	if (cm->cm_cc)
434 		printf("ex%d: configuration failed; cc=%x\n", unit, cm->cm_cc);
435 }
436 
437 /*
438  * Start or re-start output on interface. Get another datagram to send off of
439  * the interface queue, and map it to the interface before starting the output.
440  * This routine is called by exinit(), exoutput(), and excdint().  In all cases,
441  * interrupts by EXOS are disabled.
442  */
443 exstart(ifp)
444 struct ifnet *ifp;
445 {
446 	int unit = ifp->if_unit;
447 	struct vba_device *ui = exinfo[unit];
448 	register struct ex_softc *xs = &ex_softc[unit];
449 	struct exdevice *exaddr = (struct exdevice *)ui->ui_addr;
450 	register struct ex_msg *bp;
451 	register struct mbuf *m;
452         int len;
453 	register struct ifvba *pkb;
454 	struct mbuf *m0 = 0;
455 	register int nb = 0, tlen = 0;
456 	union l_util {
457 		u_long	l;
458 		struct	i86_long i;
459 	} l_util;
460 
461 	if (xs->xs_ntrb >= NTRB)
462 		return;
463 	if (xs->xs_pkblist == 0) {
464 		printf("ex%d: vbinfo exhausted, would panic", unit);
465 		return;
466 	}
467 	IF_DEQUEUE(&xs->xs_if.if_snd, m);
468 	if (m == 0)
469 		return;
470 	/*
471 	 * Get a transmit request.
472 	 */
473 	if ((bp = exgetcbuf(xs, LLRTRANSMIT)) == (struct ex_msg *)0) {
474 		m_freem(m);
475 		printf("exstart: no command buffers\n");
476 		return;
477 	}
478 	xs->xs_ntrb++;
479 	GetPkBuf(bp, pkb);
480 	pkb->iff_mbuf = m;	/* save mbuf pointer to free when done */
481 	/*
482 	 * point directly to the first group of mbufs to be transmitted. The
483 	 * hardware can only support NFRAGMENTS descriptors.
484 	 */
485 	while (m && ((nb < NFRAGMENTS-1) || (m->m_next == 0)) ) {
486 		l_util.l = BUSADDR(mtod(m, caddr_t));
487 		bp->mb_et.et_blks[nb].bb_len = (u_short)m->m_len;
488 		bp->mb_et.et_blks[nb].bb_addr = l_util.i;
489 		if (l_util.l + m->m_len > BUSADDR(VB_MAXADDR24)) {
490 			/* Here, the phys memory for the mbuf is out
491 			   of range for the vmebus to talk to it */
492 			if (m == pkb->iff_mbuf)
493 				pkb->iff_mbuf = 0;
494 			break;
495 		}
496 		tlen += m->m_len;
497 		m0 = m;
498 		m = m->m_next;
499 		nb++;
500 	}
501 
502 	/* 0 end of chain pointed to by iff_mbuf, to be freed when xmit done */
503 	if (m0)
504 		m0->m_next = 0;
505 
506 	/*
507 	 * if not all of the descriptors would fit then merge remaining data
508 	 * into the transmit buffer, and point to it.  Note: the mbufs are freed
509 	 * during the merge, they do not have to be freed when we get the
510 	 * transmit interrupt.
511 	 */
512 	if (m) {
513 		if (m == pkb->iff_mbuf) {
514 			printf("ex%d: exstart insanity\n", unit);
515 			pkb->iff_mbuf = 0;
516 		}
517 		len = if_vbaput(pkb->iff_buffer, m, 0);
518 		l_util.l = BUSADDR(pkb->iff_buffer);
519 		bp->mb_et.et_blks[nb].bb_len = (u_short)len;
520 		bp->mb_et.et_blks[nb].bb_addr = l_util.i;
521 		tlen += len;
522 		nb++;
523 	}
524 
525 	/*
526 	 * If the total length of the packet is too small,
527 	 * pad the last fragment.  (May run into very obscure problems)
528 	 */
529 	if (tlen < sizeof(struct ether_header) + ETHERMIN) {
530 		len = (ETHERMIN + sizeof(struct ether_header)) - tlen;
531 		bp->mb_et.et_blks[nb-1].bb_len += (u_short)len;
532 		tlen += len;
533 #ifdef notdef
534                 if (l_util.l + m->m_len > BUSADDR(VB_MAXADDR24)) {
535 			must copy last frag into private buffer
536 		}
537 #endif
538 	}
539 
540 	/* set number of fragments in descriptor */
541 	bp->mb_et.et_nblock = nb;
542 	bp->mb_status |= MH_EXOS;
543 	movow(&exaddr->ex_portb, EX_NTRUPT);
544 }
545 
546 /*
547  * interrupt service routine.
548  */
549 exintr(unit)
550 	int unit;
551 {
552 	register struct ex_softc *xs = &ex_softc[unit];
553 	register struct ex_msg *bp = xs->xs_x2hnext;
554 	struct vba_device *ui = exinfo[unit];
555 	struct exdevice *exaddr = (struct exdevice *)ui->ui_addr;
556 	struct ex_msg *next_bp;
557 
558 	while ((bp->mb_status & MH_OWNER) == MH_HOST) {
559 		switch (bp->mb_rqst) {
560 		    case LLRECEIVE:
561 			if (--xs->xs_nrec < 0) {
562 				printf("ex%d: internal receive check\n", unit);
563 				xs->xs_nrec = 0;
564 			}
565 			exrecv(unit, bp);
566 			FreePkBuf(bp->mb_pkb);
567 			bp->mb_pkb = (struct ifvba *)0;
568 			exhangrcv(unit);
569 			break;
570 
571 		    case LLTRANSMIT:
572 		    case LLRTRANSMIT:
573 			if (--xs->xs_ntrb < 0) {
574 				printf("ex%d: internal transmit check\n", unit);
575 				xs->xs_ntrb = 0;
576 			}
577 			xs->xs_if.if_opackets++;
578 			if (bp->mb_rply == LL_OK || bp->mb_rply == LLXM_NSQE)
579 				;
580 			else if (bp->mb_rply & LLXM_1RTRY)
581 				xs->xs_if.if_collisions++;
582 			else if (bp->mb_rply & LLXM_RTRYS)
583 				xs->xs_if.if_collisions += 2;	/* guess */
584 			else if (bp->mb_rply & LLXM_ERROR)
585 				if (xs->xs_if.if_oerrors++ % 100 == 0)
586 					printf("ex%d: 100 transmit errors=%b\n",
587 						unit, bp->mb_rply, XMIT_BITS);
588 			if (bp->mb_pkb->iff_mbuf) {
589 				m_freem(bp->mb_pkb->iff_mbuf);
590 				bp->mb_pkb->iff_mbuf = (struct mbuf *)0;
591 			}
592 			FreePkBuf(bp->mb_pkb);
593 			bp->mb_pkb = (struct ifvba *)0;
594 			exstart(&xs->xs_if);
595 			exhangrcv(unit);
596 			break;
597 
598 		    case LLNET_STSTCS:
599 			xs->xs_if.if_ierrors += xs->xs_xsa.sa_crc;
600 			xs->xs_flags &= ~EX_STATPENDING;
601 		    case LLNET_ADDRS:
602 		    case LLNET_RECV:
603 			if (bp->mb_rply == LL_OK || bp->mb_rply == LLXM_NSQE)
604 				;
605 			else
606 				printf("ex%d: %s, request 0x%x, reply 0x%x\n",
607 				  unit, "unsucessful stat or address change",
608 				  bp->mb_rqst, bp->mb_rply);
609 			break;
610 
611 		    default:
612 			printf("ex%d: unknown reply 0x%x", unit, bp->mb_rqst);
613 		}
614 		bp->mb_length = MBDATALEN;
615 		next_bp = bp->mb_next;
616 		bp->mb_status |= MH_EXOS;	/* free up buffer */
617 		bp = next_bp;			/* paranoia about race */
618 		movow(&exaddr->ex_portb, EX_NTRUPT); /* tell EXOS about it */
619 	}
620 	xs->xs_x2hnext = bp;
621 }
622 
623 /*
624  * Get a request buffer, fill in standard values, advance pointer.
625  */
626 struct ex_msg *
627 exgetcbuf(xs, req)
628 struct ex_softc *xs;
629 int req;
630 {
631 	register struct ex_msg *bp;
632 	struct ifvba *pkb;
633 	int s = splimp();
634 
635 	bp = xs->xs_h2xnext;
636 	if ((bp->mb_status & MH_OWNER) == MH_EXOS) {
637 		splx(s);
638 		return (struct ex_msg *)0;
639 	}
640 	xs->xs_h2xnext = bp->mb_next;
641 	bp->mb_1rsrv = 0;
642 	bp->mb_rqst = req;
643 	bp->mb_length = MBDATALEN;
644 	bp->mb_pkb = (struct ifvba *)0;
645 	splx(s);
646 	return bp;
647 }
648 
649 /*
650  * Process Ethernet receive completion:  If input error just drop packet,
651  * otherwise examine packet to determine type.  If can't determine length from
652  * type, then have to drop packet, otherwise decapsulate packet based on type
653  * and pass to type-specific higher-level input routine.
654  */
655 exrecv(unit, bp)
656 int unit;
657 register struct ex_msg *bp;
658 {
659 	register struct ex_softc *xs = &ex_softc[unit];
660 	register struct ether_header *eh;
661     	register struct mbuf *m;
662 	int len, off, resid;
663 	register struct ifqueue *inq;
664 	int s;
665 
666 	xs->xs_if.if_ipackets++;
667 	/*     total length               - header                      - crc */
668 	len = bp->mb_er.er_blks[0].bb_len - sizeof(struct ether_header) - 4;
669 	if (bp->mb_rply != LL_OK) {
670 		if (xs->xs_if.if_ierrors++ % 100 == 0)
671 			printf("ex%d: 100 receive errors=%b\n",
672 				unit, bp->mb_rply, RECV_BITS);
673 		return;
674 	}
675 	eh = (struct ether_header *)(bp->mb_pkb->iff_buffer);
676 
677 	/*
678 	 * Deal with trailer protocol: if type is PUP trailer get true type from
679 	 * first 16-bit word past data.  Remember that type was trailer by
680 	 * setting off.
681 	 */
682 	eh->ether_type = ntohs((u_short)eh->ether_type);
683 #define	exdataaddr(eh, off, type)	((type)(((caddr_t)((eh)+1)+(off))))
684 	if (eh->ether_type >= ETHERTYPE_TRAIL &&
685 	    eh->ether_type < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) {
686 		off = (eh->ether_type - ETHERTYPE_TRAIL) * 512;
687 		if (off >= ETHERMTU)
688 			return;			/* sanity */
689 		eh->ether_type = ntohs(*exdataaddr(eh, off, u_short *));
690 		resid = ntohs(*(exdataaddr(eh, off+2, u_short *)));
691 		if (off + resid > len)
692 			return;			/* sanity */
693 		len = off + resid;
694 	} else
695 		off = 0;
696 	if (len == 0)
697 		return;
698 	/*
699 	 * Pull packet off interface.  Off is nonzero if packet
700 	 * has trailing header; if_vbaget will then force this header
701 	 * information to be at the front, but we still have to drop
702 	 * the type and length which are at the front of any trailer data.
703 	 */
704 	m = if_vbaget(bp->mb_pkb->iff_buffer, len, off, &xs->xs_if, 0);
705 	if (m == 0)
706 		return;
707 	ether_input(&xs->xs_if, eh, m);
708 	return;
709 }
710 
711 /*
712  * Hang a receive request. This routine is called by exinit and excdint,
713  * with interrupts disabled in both cases.
714  */
715 exhangrcv(unit)
716 	int unit;
717 {
718 	register struct ex_softc *xs = &ex_softc[unit];
719 	register struct ex_msg *bp;
720 	register struct ifvba *pkb;
721 	short mustint = 0;
722 	union l_util {
723 		u_long	l;
724 		struct	i86_long i;
725 	} l_util;
726 
727 	while (xs->xs_nrec < NREC) {
728 		if (xs->xs_pkblist == (struct ifvba *)0)
729 			break;
730 		if ((bp = exgetcbuf(xs, LLRECEIVE)) == (struct ex_msg *)0) {
731 			break;
732 		}
733 		GetPkBuf(bp, pkb);
734 		pkb->iff_mbuf = 0;
735 		xs->xs_nrec += 1;
736 		bp->mb_er.er_nblock = 1;
737 		bp->mb_er.er_blks[0].bb_len = EXMAXRBUF;
738 		l_util.l = BUSADDR(pkb->iff_buffer);
739 		bp->mb_er.er_blks[0].bb_addr = l_util.i;
740 		bp->mb_status |= MH_EXOS;
741 		mustint = 1;
742 	}
743 	if (mustint == 0)
744 		return;
745 	movow(&((struct exdevice *)exinfo[unit]->ui_addr)->ex_portb, EX_NTRUPT);
746 }
747 
748 /*
749  * Ethernet output routine is ether_output().
750  */
751 
752 /*
753  * Watchdog routine (currently not used). Might use this to get stats from EXOS.
754  */
755 exwatch(unit)
756 int unit;
757 {
758 	struct exdevice *exaddr = (struct exdevice *)exinfo[unit]->ui_addr;
759 	register struct ex_softc *xs = &ex_softc[unit];
760 	register struct ex_msg *bp;
761 	int s = splimp();
762 
763 	if (xs->xs_flags & EX_STATPENDING)
764 		goto exspnd;
765 	if ((bp = exgetcbuf(xs, LLNET_STSTCS)) == (struct ex_msg *)0) {
766 		splx(s);
767 		return;
768 	}
769 	xs->xs_flags |= EX_STATPENDING;
770 	bp->mb_ns.ns_mask = READ_OBJ;
771 	bp->mb_ns.ns_rsrv = 0;
772 	bp->mb_ns.ns_nobj = 8;
773 	bp->mb_ns.ns_xobj = 0;
774 	bp->mb_ns.ns_bufp = P_BUSADDR(xs->xs_qbaddr) + SA_OFFSET;
775 	bp->mb_status |= MH_EXOS;
776 	movow(&exaddr->ex_portb, EX_NTRUPT);
777 exspnd:	splx(s);
778 	xs->xs_if.if_timer = EXWATCHINTVL;
779 }
780 
781 /*
782  * Process an ioctl request.
783  */
784 exioctl(ifp, cmd, data)
785 	register struct ifnet *ifp;
786 	int cmd;
787 	caddr_t data;
788 {
789 	register struct ifaddr *ifa = (struct ifaddr *)data;
790 	register struct ex_softc *xs = &ex_softc[ifp->if_unit];
791 	int s = splimp(), error = 0;
792 
793 	switch (cmd) {
794 
795 	case SIOCSIFADDR:
796                 ifp->if_flags |= IFF_UP;
797                 exinit(ifp->if_unit);
798 
799                 switch (ifa->ifa_addr->sa_family) {
800 #ifdef INET
801 		case AF_INET:
802 			((struct arpcom *)ifp)->ac_ipaddr =
803 				IA_SIN(ifa)->sin_addr;
804 			arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr);
805 			break;
806 #endif
807 #ifdef NS
808 		case AF_NS:
809 		    {
810 			register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr);
811 
812 			if (ns_nullhost(*ina))
813 				ina->x_host = *(union ns_host *)(xs->xs_addr);
814 			else
815 				ex_setaddr(ina->x_host.c_host,ifp->if_unit);
816 			break;
817 		    }
818 #endif
819 		}
820 		break;
821 
822 	case SIOCSIFFLAGS:
823 		if ((ifp->if_flags & IFF_UP) == 0 &&
824 		    xs->xs_flags & EX_RUNNING) {
825 			movow(&((struct exdevice *)
826 			  (exinfo[ifp->if_unit]->ui_addr))->ex_porta, EX_RESET);
827 			xs->xs_flags &= ~EX_RUNNING;
828 		} else if (ifp->if_flags & IFF_UP &&
829 		    (xs->xs_flags & EX_RUNNING) == 0)
830 			exinit(ifp->if_unit);
831 		break;
832 
833 	default:
834 		error = EINVAL;
835 	}
836 	splx(s);
837 	return (error);
838 }
839 
840 /*
841  * set ethernet address for unit
842  */
843 ex_setaddr(physaddr, unit)
844 	u_char *physaddr;
845 	int unit;
846 {
847 	register struct ex_softc *xs = &ex_softc[unit];
848 
849 	if (physaddr) {
850 		xs->xs_flags |= EX_SETADDR;
851 		bcopy((caddr_t)physaddr, (caddr_t)xs->xs_addr, 6);
852 	}
853 	ex_setmulti((u_char *)xs->xs_addr, unit, PHYSSLOT);
854 }
855 
856 /*
857  * Enable multicast reception for unit.
858  */
859 ex_setmulti(linkaddr, unit, slot)
860 	u_char *linkaddr;
861 	int unit, slot;
862 {
863 	register struct ex_softc *xs = &ex_softc[unit];
864 	struct vba_device *ui = exinfo[unit];
865 	register struct exdevice *addr= (struct exdevice *)ui->ui_addr;
866 	register struct ex_msg *bp;
867 
868 	if (!(xs->xs_flags & EX_RUNNING))
869 		return;
870 	bp = exgetcbuf(xs, LLNET_ADDRS);
871 	bp->mb_na.na_mask = READ_OBJ|WRITE_OBJ;
872 	bp->mb_na.na_slot = slot;
873 	bcopy((caddr_t)linkaddr, (caddr_t)bp->mb_na.na_addrs, 6);
874 	bp->mb_status |= MH_EXOS;
875 	movow(&addr->ex_portb, EX_NTRUPT);
876 	bp = xs->xs_x2hnext;
877 	while ((bp->mb_status & MH_OWNER) == MH_EXOS);/* poll for reply */
878 #ifdef	DEBUG
879 	log(LOG_DEBUG, "ex%d: %s %s (slot %d)\n", unit,
880 		(slot == PHYSSLOT ? "reset addr" : "add multicast"
881 		ether_sprintf(bp->mb_na.na_addrs), slot);
882 #endif
883 	/*
884 	 * Now, re-enable reception on slot.
885 	 */
886 	bp = exgetcbuf(xs, LLNET_RECV);
887 	bp->mb_nr.nr_mask = ENABLE_RCV|READ_OBJ|WRITE_OBJ;
888 	bp->mb_nr.nr_slot = slot;
889 	bp->mb_status |= MH_EXOS;
890 	movow(&addr->ex_portb, EX_NTRUPT);
891 	bp = xs->xs_x2hnext;
892 	while ((bp->mb_status & MH_OWNER) == MH_EXOS);/* poll for reply */
893 		;
894 }
895 #endif
896