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