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