xref: /csrg-svn/sys/vax/if/if_vv.c (revision 11209)
1 /*	if_vv.c	4.14	83/02/21	*/
2 
3 #include "vv.h"
4 
5 /*
6  * Proteon 10 Meg Ring Driver.
7  * This device is called "vv" because its "real name",
8  * V2LNI won't work if shortened to the obvious "v2".
9  * Hence the subterfuge.
10  *
11  * UNTESTED WITH 4.1C
12  */
13 #include "../machine/pte.h"
14 
15 #include "../h/param.h"
16 #include "../h/systm.h"
17 #include "../h/mbuf.h"
18 #include "../h/buf.h"
19 #include "../h/protosw.h"
20 #include "../h/socket.h"
21 #include "../h/vmmac.h"
22 #include "../h/errno.h"
23 #include "../h/time.h"
24 #include "../h/kernel.h"
25 
26 #include "../net/if.h"
27 #include "../net/netisr.h"
28 #include "../net/route.h"
29 
30 #include "../netinet/in.h"
31 #include "../netinet/in_systm.h"
32 #include "../netinet/ip.h"
33 #include "../netinet/ip_var.h"
34 
35 #include "../vax/mtpr.h"
36 #include "../vax/cpu.h"
37 
38 #include "../vaxuba/ubareg.h"
39 #include "../vaxuba/ubavar.h"
40 
41 #include "../vaxif/if_vv.h"
42 #include "../vaxif/if_uba.h"
43 
44 /*
45  * N.B. - if WIRECENTER is defined wrong, it can well break
46  * the hardware!!
47  */
48 #define	WIRECENTER
49 
50 #ifdef WIRECENTER
51 #define	VV_CONF	VV_HEN		/* drive wire center relay */
52 #else
53 #define	VV_CONF	VV_STE		/* allow operation without wire center */
54 #endif
55 
56 #define	VVMTU	(1024+512)
57 #define VVMRU	(1024+512+16)	/* space for trailer */
58 
59 int vv_dotrailer = 0,		/* 1 => do trailer protocol */
60     vv_tracehdr = 0,		/* 1 => trace headers (slowly!!) */
61     vv_tracetimeout = 1;	/* 1 => trace input error-rate limiting */
62     vv_logreaderrors = 0;	/* 1 => log all read errors */
63 
64 #define vvtracehdr	if (vv_tracehdr) vvprt_hdr
65 #define	vvtrprintf	if (vv_tracetimeout) printf
66 
67 int vv_ticking = 0;		/* error flywheel is running */
68 
69 /*
70  * Interval in HZ - 50 msec.
71  * N.B. all times below are in units of flywheel ticks
72  */
73 #define VV_FLYWHEEL		3
74 #define	VV_ERRORTHRESHOLD	100	/* errors/flywheel-interval */
75 #define	VV_MODE1ATTEMPTS	10	/* number mode 1 retries */
76 #define	VV_MODE1DELAY		2	/* period interface is PAUSEd - 100ms */
77 #define VV_MODE2DELAY		4	/* base interval host relay is off - 200ms */
78 #define	VV_MAXDELAY		6400	/* max interval host relay is off - 2 minutes */
79 
80 int	vvprobe(), vvattach(), vvrint(), vvxint();
81 struct	uba_device *vvinfo[NVV];
82 u_short vvstd[] = { 0 };
83 struct	uba_driver vvdriver =
84 	{ vvprobe, 0, vvattach, 0, vvstd, "vv", vvinfo };
85 #define	VVUNIT(x)	minor(x)
86 int	vvinit(),vvoutput(),vvreset();
87 
88 /*
89  * Software status of each interface.
90  *
91  * Each interface is referenced by a network interface structure,
92  * vs_if, which the routing code uses to locate the interface.
93  * This structure contains the output queue for the interface, its address, ...
94  * We also have, for each interface, a UBA interface structure, which
95  * contains information about the UNIBUS resources held by the interface:
96  * map registers, buffered data paths, etc.  Information is cached in this
97  * structure for use by the if_uba.c routines in running the interface
98  * efficiently.
99  */
100 struct	vv_softc {
101 	struct	ifnet vs_if;		/* network-visible interface */
102 	struct	ifuba vs_ifuba;		/* UNIBUS resources */
103 	short	vs_oactive;		/* is output active */
104 	short	vs_iactive;		/* is input active */
105 	short	vs_olen;		/* length of last output */
106 	u_short	vs_lastx;		/* last destination address */
107 	short	vs_tries;		/* transmit current retry count */
108 	short	vs_init;		/* number of ring inits */
109 	short	vs_nottaken;		/* number of packets refused */
110 	/* input error rate limiting state */
111 	short	vs_major;		/* recovery major state */
112 	short	vs_minor;		/* recovery minor state */
113 	short	vs_retry;		/* recovery retry count */
114 	short	vs_delayclock;		/* recovery delay clock */
115 	short	vs_delayrange;		/* increasing delay interval */
116 	short	vs_dropped;		/* number of packes tossed in last dt */
117 } vv_softc[NVV];
118 
119 /*
120  * States of vs_iactive.
121  */
122 #define	ACTIVE	1		/* interface should post new receives */
123 #define	PAUSE	0		/* interface should NOT post new receives */
124 #define	OPEN	-1		/* PAUSE and open host relay */
125 
126 /*
127  * Recovery major states.
128  */
129 #define	MODE0	0		/* everything is wonderful */
130 #define	MODE1	1		/* hopefully whatever will go away */
131 #define	MODE2	2		/* drastic measures - open host relay for increasing intervals */
132 
133 vvprobe(reg)
134 	caddr_t reg;
135 {
136 	register int br, cvec;
137 	register struct vvreg *addr = (struct vvreg *)reg;
138 
139 #ifdef lint
140 	br = 0; cvec = br; br = cvec;
141 #endif
142 	/* reset interface, enable, and wait till dust settles */
143 	addr->vvicsr = VV_RST;
144 	addr->vvocsr = VV_RST;
145 	DELAY(100000);
146 	/* generate interrupt by doing 1 word DMA from 0 in uba space!! */
147 	addr->vvocsr = VV_IEN;		/* enable interrupt */
148 	addr->vvoba = 0;		/* low 16 bits */
149 	addr->vvoea = 0;		/* extended bits */
150 	addr->vvowc = -1;		/* for 1 word */
151 	addr->vvocsr |= VV_DEN;		/* start the DMA */
152 	DELAY(100000);
153 	addr->vvocsr = 0;
154 	if (cvec && cvec != 0x200)
155 		cvec -= 4;		/* backup so vector => recieve */
156 	return(1);
157 }
158 
159 /*
160  * Interface exists: make available by filling in network interface
161  * record.  System will initialize the interface when it is ready
162  * to accept packets.
163  */
164 vvattach(ui)
165 	struct uba_device *ui;
166 {
167 	register struct vv_softc *vs = &vv_softc[ui->ui_unit];
168 	register struct sockaddr_in *sin;
169 
170 	vs->vs_if.if_unit = ui->ui_unit;
171 	vs->vs_if.if_name = "vv";
172 	vs->vs_if.if_mtu = VVMTU;
173 	vs->vs_if.if_net = ui->ui_flags;
174 	vs->vs_if.if_host[0] = 0;	/* this will be reset in vvinit() */
175 	sin = (struct sockaddr_in *)&vs->vs_if.if_addr;
176 	sin->sin_family = AF_INET;
177 	sin->sin_addr = if_makeaddr(vs->vs_if.if_net, vs->vs_if.if_host[0]);
178 	sin = (struct sockaddr_in *)&vs->vs_if.if_broadaddr;
179 	sin->sin_family = AF_INET;
180 	sin->sin_addr = if_makeaddr(vs->vs_if.if_net, VV_BROADCAST);
181 	vs->vs_if.if_flags = IFF_BROADCAST;
182 	vs->vs_if.if_init = vvinit;
183 	vs->vs_if.if_output = vvoutput;
184 	vs->vs_if.if_reset = vvreset;
185 	vs->vs_ifuba.ifu_flags = UBA_CANTWAIT | UBA_NEEDBDP | UBA_NEED16;
186 	if_attach(&vs->vs_if);
187 }
188 
189 /*
190  * Reset of interface after UNIBUS reset.
191  * If interface is on specified uba, reset its state.
192  */
193 vvreset(unit, uban)
194 	int unit, uban;
195 {
196 	register struct uba_device *ui;
197 
198 	if (unit >= NVV || (ui = vvinfo[unit]) == 0 || ui->ui_alive == 0 ||
199 	    ui->ui_ubanum != uban)
200 		return;
201 	printf(" vv%d", unit);
202 	vvinit(unit);
203 }
204 
205 /*
206  * Initialization of interface; clear recorded pending
207  * operations, and reinitialize UNIBUS usage.
208  */
209 vvinit(unit)
210 	int unit;
211 {
212 	register struct vv_softc *vs = &vv_softc[unit];
213 	register struct uba_device *ui = vvinfo[unit];
214 	register struct vvreg *addr;
215 	struct sockaddr_in *sin;
216 	int ubainfo, s;
217 	int vvtimeout();
218 
219 	addr = (struct vvreg *)ui->ui_addr;
220 	if (if_ubainit(&vs->vs_ifuba, ui->ui_ubanum,
221 	    sizeof (struct vv_header), (int)btoc(VVMTU)) == 0) {
222 		printf("vv%d: can't initialize\n", unit);
223 		vs->vs_if.if_flags &= ~IFF_UP;
224 		return;
225 	}
226 	if (vv_ticking++ == 0)
227 		timeout(vvtimeout, (caddr_t) 0, VV_FLYWHEEL);
228 	/*
229 	 * Discover our host address and post it
230 	 */
231 	vs->vs_if.if_host[0] = vvidentify(unit);
232 	printf("vv%d: host %d\n", unit, vs->vs_if.if_host[0]);
233 	sin = (struct sockaddr_in *)&vs->vs_if.if_addr;
234 	sin->sin_family = AF_INET;
235 	sin->sin_addr = if_makeaddr(vs->vs_if.if_net, vs->vs_if.if_host[0]);
236 
237 	/*
238 	 * Reset the interface, and join the ring
239 	 */
240 	addr->vvocsr = VV_RST | VV_CPB;		/* clear packet buffer */
241 	addr->vvicsr = VV_RST | VV_CONF;	/* close logical relay */
242 	sleep((caddr_t)&lbolt, PZERO);		/* let contacts settle */
243 	vs->vs_init = 0;
244 	vs->vs_dropped = 0;
245 	vs->vs_nottaken = 0;
246 
247 	/*
248 	 * Hang a receive and start any
249 	 * pending writes by faking a transmit complete.
250 	 */
251 	s = splimp();
252 	ubainfo = vs->vs_ifuba.ifu_r.ifrw_info;
253 	addr->vviba = (u_short) ubainfo;
254 	addr->vviea = (u_short) (ubainfo >> 16);
255 	addr->vviwc = -(sizeof (struct vv_header) + VVMTU) >> 1;
256 	addr->vvicsr = VV_IEN | VV_CONF | VV_DEN | VV_ENB;
257 	vs->vs_iactive = ACTIVE;
258 	vs->vs_oactive = 1;
259 	vs->vs_if.if_flags |= IFF_UP;
260 	vvxint(unit);
261 	splx(s);
262 	if_rtinit(&vs->vs_if, RTF_UP);
263 }
264 
265 /*
266  * vvidentify() - return our host address
267  */
268 vvidentify(unit)
269 {
270 	register struct vv_softc *vs = &vv_softc[unit];
271 	register struct uba_device *ui = vvinfo[unit];
272 	register struct vvreg *addr;
273 	struct mbuf *m;
274 	struct vv_header *v;
275 	int ubainfo, retrying, attempts, waitcount, s;
276 
277 	/*
278 	 * Build a multicast message to identify our address
279 	 */
280 	addr = (struct vvreg *)ui->ui_addr;
281 	attempts = 0;		/* total attempts, including bad msg type */
282 	retrying = 0;		/* first time through */
283 	m = m_get(M_DONTWAIT, MT_HEADER);
284 	if (m == 0)
285 		panic("vvinit: can't get mbuf");
286 	m->m_next = 0;
287 	m->m_off = MMINOFF;
288 	m->m_len = sizeof(struct vv_header);
289 	v = mtod(m, struct vv_header *);
290 	v->vh_dhost = VV_BROADCAST;	/* multicast destination address */
291 	v->vh_shost = 0;		/* will be overwritten with ours */
292 	v->vh_version = RING_VERSION;
293 	v->vh_type = RING_WHOAMI;
294 	v->vh_info = 0;
295 	/* map xmit message into uba */
296 	vs->vs_olen =  if_wubaput(&vs->vs_ifuba, m);
297 	if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP)
298 		UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_w.ifrw_bdp);
299 	/*
300 	 * Reset interface, establish Digital Loopback Mode, and
301 	 * send the multicast (to myself) with Input Copy enabled.
302 	 */
303 retry:
304 	ubainfo = vs->vs_ifuba.ifu_r.ifrw_info;
305 	addr->vvicsr = VV_RST;
306 	addr->vviba = (u_short) ubainfo;
307 	addr->vviea = (u_short) (ubainfo >> 16);
308 	addr->vviwc = -(sizeof (struct vv_header) + VVMTU) >> 1;
309 	addr->vvicsr = VV_STE | VV_DEN | VV_ENB | VV_LPB;
310 
311 	/* let flag timers fire so ring will initialize */
312 	sleep((caddr_t) &lbolt, PZERO);
313 	sleep((caddr_t) &lbolt, PZERO);
314 
315 	addr->vvocsr = VV_RST | VV_CPB;	/* clear packet buffer */
316 	ubainfo = vs->vs_ifuba.ifu_w.ifrw_info;
317 	addr->vvoba = (u_short) ubainfo;
318 	addr->vvoea = (u_short) (ubainfo >> 16);
319 	addr->vvowc = -((vs->vs_olen + 1) >> 1);
320 	addr->vvocsr = VV_CPB | VV_DEN | VV_INR | VV_ENB;
321 	/*
322 	 * Wait for receive side to finish.
323 	 * Extract source address (which will our own),
324 	 * and post to interface structure.
325 	 */
326 	DELAY(1000);
327 	for (waitcount = 0; (addr->vvicsr & VV_RDY) == 0; waitcount++) {
328 		if (waitcount < 10) {
329 			DELAY(1000);
330 			continue;
331 		}
332 		if (attempts++ >= 10) {
333 			printf("vv%d: can't initialize\n", unit);
334 			printf("vvinit loopwait: icsr = %b\n",
335 				0xffff&(addr->vvicsr), VV_IBITS);
336 			vs->vs_if.if_flags &= ~IFF_UP;
337 			return;
338 		}
339 		goto retry;
340 	}
341 	if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP)
342 		UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_w.ifrw_bdp);
343 	if (vs->vs_ifuba.ifu_xtofree)
344 		m_freem(vs->vs_ifuba.ifu_xtofree);
345 	if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP)
346 		UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_r.ifrw_bdp);
347 	m = if_rubaget(&vs->vs_ifuba, sizeof(struct vv_header), 0);
348 	if (m != NULL)
349 		m_freem(m);
350 	/*
351 	 * Check message type before we believe the source host address
352 	 */
353 	v = (struct vv_header *)(vs->vs_ifuba.ifu_r.ifrw_addr);
354 	if (v->vh_type != RING_WHOAMI)
355 		goto retry;
356 	return(v->vh_shost);
357 }
358 
359 /*
360  * vvtimeout() - called by timer flywheel to monitor input packet
361  * discard rate.  Interfaces getting too many errors are shut
362  * down for a while.  If the condition persists, the interface
363  * is marked down.
364  */
365 vvtimeout(junk)
366 	int junk;
367 {
368 	register struct vv_softc *vs;
369 	register int i;
370 	register struct vvreg *addr;
371 	int ubainfo;
372 
373 	timeout(vvtimeout, (caddr_t) 0, VV_FLYWHEEL);
374 	for (i = 0; i < NVV; i++) {
375 		vs = &vv_softc[i];
376 		addr = (struct vvreg *)vvinfo[i]->ui_addr;
377 		if (vs->vs_if.if_flags & IFF_UP == 0) continue;
378 		switch (vs->vs_major) {
379 
380 		/*
381 		 * MODE0: generally OK, just check error rate
382 		 */
383 		case MODE0:
384 			if (vs->vs_dropped < VV_ERRORTHRESHOLD) {
385 				vs->vs_dropped = 0;
386 				continue;
387 			}
388 			/* suspend reads for a while */
389 			vvtrprintf("vv%d going MODE1 in vvtimeout\n",i);
390 			vs->vs_major = MODE1;
391 			vs->vs_iactive = PAUSE;	/* no new reads */
392 			vs->vs_retry = VV_MODE1ATTEMPTS;
393 			vs->vs_delayclock = VV_MODE1DELAY;
394 			vs->vs_minor = 0;
395 			continue;
396 
397 		/*
398 		 * MODE1: excessive error rate observed
399 		 * Scheme: try simply suspending reads for a
400 		 * short while a small number of times
401 		 */
402 		case MODE1:
403 			if (vs->vs_delayclock > 0) {
404 				vs->vs_delayclock--;
405 				continue;
406 			}
407 			switch (vs->vs_minor) {
408 
409 			case 0:				/* reenable reads */
410 				vvtrprintf("vv%d M1m0\n",i);
411 				vs->vs_dropped = 0;
412 				vs->vs_iactive = ACTIVE;
413 				vs->vs_minor = 1;	/* next state */
414 				ubainfo = vs->vs_ifuba.ifu_r.ifrw_info;
415 				addr->vviba = (u_short) ubainfo;
416 				addr->vviea = (u_short) (ubainfo >> 16);
417 				addr->vviwc =
418 				  -(sizeof (struct vv_header) + VVMTU) >> 1;
419 				addr->vvicsr = VV_RST | VV_CONF;
420 				addr->vvicsr |= VV_IEN | VV_DEN | VV_ENB;
421 				continue;
422 
423 			case 1:				/* see if it worked */
424 				vvtrprintf("vv%d M1m1\n",i);
425 				if (vs->vs_dropped < VV_ERRORTHRESHOLD) {
426 					vs->vs_dropped = 0;
427 					vs->vs_major = MODE0;	/* yeah!! */
428 					continue;
429 				}
430 				if (vs->vs_retry -- > 0) {
431 					vs->vs_dropped = 0;
432 					vs->vs_iactive = PAUSE;
433 					vs->vs_delayclock = VV_MODE1DELAY;
434 					vs->vs_minor = 0; /* recheck */
435 					continue;
436 				}
437 				vs->vs_major = MODE2;
438 				vs->vs_minor = 0;
439 				vs->vs_dropped = 0;
440 				vs->vs_iactive = OPEN;
441 				vs->vs_delayrange = VV_MODE2DELAY;
442 				vs->vs_delayclock = VV_MODE2DELAY;
443 				/* fall thru ... */
444 			}
445 
446 		/*
447 		 * MODE2: simply ignoring traffic didn't relieve condition
448 		 * Scheme: open host relay for intervals linearly
449 		 * increasing up to some maximum of a several minutes.
450 		 * This allows broken networks to return to operation
451 		 * without rebooting.
452 		 */
453 		case MODE2:
454 			if (vs->vs_delayclock > 0) {
455 				vs->vs_delayclock--;
456 				continue;
457 			}
458 			switch (vs->vs_minor) {
459 
460 			case 0:		/* close relay and reenable reads */
461 				vvtrprintf("vv%d M2m0\n",i);
462 				vs->vs_dropped = 0;
463 				vs->vs_iactive = ACTIVE;
464 				vs->vs_minor = 1;	/* next state */
465 				ubainfo = vs->vs_ifuba.ifu_r.ifrw_info;
466 				addr->vviba = (u_short) ubainfo;
467 				addr->vviea = (u_short) (ubainfo >> 16);
468 				addr->vviwc =
469 				  -(sizeof (struct vv_header) + VVMTU) >> 1;
470 				addr->vvicsr = VV_RST | VV_CONF;
471 				addr->vvicsr |= VV_IEN | VV_DEN | VV_ENB;
472 				continue;
473 
474 			case 1:				/* see if it worked */
475 				vvtrprintf("vv%d M2m1\n",i);
476 				if (vs->vs_dropped < VV_ERRORTHRESHOLD) {
477 					vs->vs_dropped = 0;
478 					vs->vs_major = MODE0;	/* yeah!! */
479 					continue;
480 				}
481 				vvtrprintf("vv%d M2m1 ++ delay\n",i);
482 				vs->vs_dropped = 0;
483 				vs->vs_iactive = OPEN;
484 				vs->vs_minor = 0;
485 				if (vs->vs_delayrange < VV_MAXDELAY)
486 					vs->vs_delayrange +=
487 					  (vs->vs_delayrange/2);
488 				vs->vs_delayclock = vs->vs_delayrange;
489 				continue;
490 			}
491 
492 		default:
493 			printf("vv%d: major state screwed\n", i);
494 			vs->vs_if.if_flags &= ~IFF_UP;
495 		}
496 	}
497 }
498 
499 /*
500  * Start or restart output on interface.
501  * If interface is active, this is a retransmit, so just
502  * restuff registers and go.
503  * If interface is not already active, get another datagram
504  * to send off of the interface queue, and map it to the interface
505  * before starting the output.
506  */
507 vvstart(dev)
508 	dev_t dev;
509 {
510         int unit = VVUNIT(dev);
511 	struct uba_device *ui = vvinfo[unit];
512 	register struct vv_softc *vs = &vv_softc[unit];
513 	register struct vvreg *addr;
514 	struct mbuf *m;
515 	int ubainfo;
516 	int dest;
517 
518 	if (vs->vs_oactive)
519 		goto restart;
520 	/*
521 	 * Not already active: dequeue another request
522 	 * and map it to the UNIBUS.  If no more requests,
523 	 * just return.
524 	 */
525 	IF_DEQUEUE(&vs->vs_if.if_snd, m);
526 	if (m == NULL) {
527 		vs->vs_oactive = 0;
528 		return;
529 	}
530 	dest = mtod(m, struct vv_header *)->vh_dhost;
531 	vs->vs_olen = if_wubaput(&vs->vs_ifuba, m);
532 	vs->vs_lastx = dest;
533 restart:
534 	/*
535 	 * Have request mapped to UNIBUS for transmission.
536 	 * Purge any stale data from this BDP, and start the otput.
537 	 */
538 	if (vs->vs_olen > VVMTU) {
539 		printf("vv%d vs_olen: %d > VVMTU\n", unit, vs->vs_olen);
540 		panic("vvdriver vs_olen botch");
541 	}
542 	if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP)
543 		UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_w.ifrw_bdp);
544 	addr = (struct vvreg *)ui->ui_addr;
545 	ubainfo = vs->vs_ifuba.ifu_w.ifrw_info;
546 	addr->vvoba = (u_short) ubainfo;
547 	addr->vvoea = (u_short) (ubainfo >> 16);
548 	addr->vvowc = -((vs->vs_olen + 1) >> 1);
549 	addr->vvocsr = VV_IEN | VV_CPB | VV_DEN | VV_INR | VV_ENB;
550 	vs->vs_oactive = 1;
551 }
552 
553 /*
554  * VVLNI transmit interrupt
555  * Start another output if more data to send.
556  */
557 vvxint(unit)
558 	int unit;
559 {
560 	register struct uba_device *ui = vvinfo[unit];
561 	register struct vv_softc *vs = &vv_softc[unit];
562 	register struct vvreg *addr;
563 	register int oc;
564 
565 	addr = (struct vvreg *)ui->ui_addr;
566 	oc = 0xffff & (addr->vvocsr);
567 	if (vs->vs_oactive == 0) {
568 		printf("vv%d: stray interrupt vvocsr = %b\n", unit,
569 			oc, VV_OBITS);
570 		return;
571 	}
572 	if (oc &  (VV_OPT | VV_RFS)) {
573 		vs->vs_if.if_collisions++;
574 		if (vs->vs_tries++ < VVRETRY) {
575 			if (oc & VV_OPT)
576 				vs->vs_init++;
577 			if (oc & VV_RFS)
578 				vs->vs_nottaken++;
579 			vvstart(unit);		/* restart this message */
580 			return;
581 		}
582 		if (oc & VV_OPT)
583 			printf("vv%d: output timeout\n");
584 	}
585 	vs->vs_if.if_opackets++;
586 	vs->vs_oactive = 0;
587 	vs->vs_tries = 0;
588 	if (oc & VVXERR) {
589 		vs->vs_if.if_oerrors++;
590 		printf("vv%d: error vvocsr = %b\n", unit, 0xffff & oc,
591 			VV_OBITS);
592 	}
593 	if (vs->vs_ifuba.ifu_xtofree) {
594 		m_freem(vs->vs_ifuba.ifu_xtofree);
595 		vs->vs_ifuba.ifu_xtofree = 0;
596 	}
597 	if (vs->vs_if.if_snd.ifq_head == 0) {
598 		vs->vs_lastx = 256;		/* an invalid address */
599 		return;
600 	}
601 	vvstart(unit);
602 }
603 
604 /*
605  * V2lni interface receiver interrupt.
606  * If input error just drop packet.
607  * Otherwise purge input buffered data path and examine
608  * packet to determine type.  If can't determine length
609  * from type, then have to drop packet.  Othewise decapsulate
610  * packet based on type and pass to type specific higher-level
611  * input routine.
612  */
613 vvrint(unit)
614 	int unit;
615 {
616 	register struct vv_softc *vs = &vv_softc[unit];
617 	struct vvreg *addr = (struct vvreg *)vvinfo[unit]->ui_addr;
618 	register struct vv_header *vv;
619 	register struct ifqueue *inq;
620     	struct mbuf *m;
621 	int ubainfo, len, off;
622 	short resid;
623 
624 	vs->vs_if.if_ipackets++;
625 	/*
626 	 * Purge BDP; drop if input error indicated.
627 	 */
628 	if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP)
629 		UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_r.ifrw_bdp);
630 	if (addr->vvicsr & VVRERR) {
631 		if (vv_logreaderrors)
632 			printf("vv%d: error vvicsr = %b\n", unit,
633 				0xffff&(addr->vvicsr), VV_IBITS);
634 		goto dropit;
635 	}
636 
637 	/*
638 	 * Get packet length from word count residue
639 	 *
640 	 * Compute header offset if trailer protocol
641 	 *
642 	 * Pull packet off interface.  Off is nonzero if packet
643 	 * has trailing header; if_rubaget will then force this header
644 	 * information to be at the front.  The vh_info field
645 	 * carries the offset to the trailer data in trailer
646 	 * format packets.
647 	 */
648 	vv = (struct vv_header *)(vs->vs_ifuba.ifu_r.ifrw_addr);
649 	vvtracehdr("vi", vv);
650 	resid = addr->vviwc;
651 	if (resid)
652 		resid |= 0176000;		/* ugly!!!! */
653 	len = (((sizeof (struct vv_header) + VVMRU) >> 1) + resid) << 1;
654 	len -= sizeof(struct vv_header);
655 	if (len > VVMRU || len <= 0)
656 		goto dropit;
657 #define	vvdataaddr(vv, off, type)	((type)(((caddr_t)((vv)+1)+(off))))
658 	if (vv_dotrailer && vv->vh_type >= RING_IPTrailer &&
659 	     vv->vh_type < RING_IPTrailer+RING_IPNTrailer){
660 		off = (vv->vh_type - RING_IPTrailer) * 512;
661 		if (off > VVMTU)
662 			goto dropit;
663 		vv->vh_type = *vvdataaddr(vv, off, u_short *);
664 		resid = *(vvdataaddr(vv, off+2, u_short *));
665 		if (off + resid > len)
666 			goto dropit;
667 		len = off + resid;
668 	} else
669 		off = 0;
670 	if (len == 0)
671 		goto dropit;
672 	m = if_rubaget(&vs->vs_ifuba, len, off);
673 	if (m == NULL)
674 		goto dropit;
675 	if (off) {
676 		m->m_off += 2 * sizeof(u_short);
677 		m->m_len -= 2 * sizeof(u_short);
678 	}
679 
680 	/*
681 	 * Demultiplex on packet type
682 	 */
683 	switch (vv->vh_type) {
684 
685 #ifdef INET
686 	case RING_IP:
687 		schednetisr(NETISR_IP);
688 		inq = &ipintrq;
689 		break;
690 #endif
691 	default:
692 		printf("vv%d: unknown pkt type 0x%x\n", unit, vv->vh_type);
693 		m_freem(m);
694 		goto setup;
695 	}
696 	if (IF_QFULL(inq)) {
697 		IF_DROP(inq);
698 		m_freem(m);
699 	} else
700 		IF_ENQUEUE(inq, m);
701 setup:
702 	/*
703 	 * Check the error rate and start recovery if needed
704 	 * this has to go here since the timer flywheel runs at
705 	 * a lower ipl and never gets a chance to change the mode
706 	 */
707 	if (vs->vs_major == MODE0 && vs->vs_dropped > VV_ERRORTHRESHOLD) {
708 		vvtrprintf("vv%d going MODE1 in vvrint\n",unit);
709 		vs->vs_major = MODE1;
710 		vs->vs_iactive = PAUSE;		/* no new reads */
711 		vs->vs_retry = VV_MODE1ATTEMPTS;
712 		vs->vs_delayclock = VV_MODE1DELAY;
713 		vs->vs_minor = 0;
714 		vs->vs_dropped = 0;
715 	}
716 	switch (vs->vs_iactive) {
717 
718 	case ACTIVE:		/* Restart the read for next packet */
719 		ubainfo = vs->vs_ifuba.ifu_r.ifrw_info;
720 		addr->vviba = (u_short) ubainfo;
721 		addr->vviea = (u_short) (ubainfo >> 16);
722 		addr->vviwc = -(sizeof (struct vv_header) + VVMTU) >> 1;
723 		addr->vvicsr = VV_RST | VV_CONF;
724 		addr->vvicsr |= VV_IEN | VV_DEN | VV_ENB;
725 		return;
726 
727 	case PAUSE:		/* requested to not start any new reads */
728 		vs->vs_dropped = 0;
729 		return;
730 
731 	case OPEN:		/* request to open host relay */
732 		vs->vs_dropped = 0;
733 		addr->vvicsr = 0;
734 		return;
735 
736 	default:
737 		printf("vv%d: vs_iactive = %d\n", unit, vs->vs_iactive);
738 		return;
739 	}
740 	/*
741 	 * Drop packet on floor -- count them!!
742 	 */
743 dropit:
744 	vs->vs_if.if_ierrors++;
745 	vs->vs_dropped++;
746 	/*
747 	printf("vv%d: error vvicsr = %b\n", unit,
748 		0xffff&(addr->vvicsr), VV_IBITS);
749 	*/
750 	goto setup;
751 }
752 
753 /*
754  * V2lni output routine.
755  * Encapsulate a packet of type family for the local net.
756  * Use trailer local net encapsulation if enough data in first
757  * packet leaves a multiple of 512 bytes of data in remainder.
758  */
759 vvoutput(ifp, m0, dst)
760 	struct ifnet *ifp;
761 	struct mbuf *m0;
762 	struct sockaddr *dst;
763 {
764 	register struct mbuf *m = m0;
765 	register struct vv_header *vv;
766 	register int off;
767 	int type, dest, s, error;
768 
769 	switch (dst->sa_family) {
770 
771 #ifdef INET
772 	case AF_INET: {
773 		dest = ((struct sockaddr_in *)dst)->sin_addr.s_addr;
774 		if ((dest = in_lnaof(*((struct in_addr *)&dest))) >= 0x100) {
775 			error = EPERM;
776 			goto bad;
777 		}
778 		off = ntohs((u_short)mtod(m, struct ip *)->ip_len) - m->m_len;
779 		if (vv_dotrailer && off > 0 && (off & 0x1ff) == 0 &&
780 		    m->m_off >= MMINOFF + 2 * sizeof (u_short)) {
781 			type = RING_IPTrailer + (off>>9);
782 			m->m_off -= 2 * sizeof (u_short);
783 			m->m_len += 2 * sizeof (u_short);
784 			*mtod(m, u_short *) = RING_IP;
785 			*(mtod(m, u_short *) + 1) = m->m_len;
786 			goto gottrailertype;
787 		}
788 		type = RING_IP;
789 		off = 0;
790 		goto gottype;
791 		}
792 #endif
793 	default:
794 		printf("vv%d: can't handle af%d\n", ifp->if_unit,
795 			dst->sa_family);
796 		error = EAFNOSUPPORT;
797 		goto bad;
798 	}
799 
800 gottrailertype:
801 	/*
802 	 * Packet to be sent as trailer: move first packet
803 	 * (control information) to end of chain.
804 	 */
805 	while (m->m_next)
806 		m = m->m_next;
807 	m->m_next = m0;
808 	m = m0->m_next;
809 	m0->m_next = 0;
810 	m0 = m;
811 gottype:
812 	/*
813 	 * Add local net header.  If no space in first mbuf,
814 	 * allocate another.
815 	 */
816 	if (m->m_off > MMAXOFF ||
817 	    MMINOFF + sizeof (struct vv_header) > m->m_off) {
818 		m = m_get(M_DONTWAIT, MT_HEADER);
819 		if (m == NULL) {
820 			error = ENOBUFS;
821 			goto bad;
822 		}
823 		m->m_next = m0;
824 		m->m_off = MMINOFF;
825 		m->m_len = sizeof (struct vv_header);
826 	} else {
827 		m->m_off -= sizeof (struct vv_header);
828 		m->m_len += sizeof (struct vv_header);
829 	}
830 	vv = mtod(m, struct vv_header *);
831 	vv->vh_shost = ifp->if_host[0];
832 	vv->vh_dhost = dest;
833 	vv->vh_version = RING_VERSION;
834 	vv->vh_type = type;
835 	vv->vh_info = off;
836 	vvtracehdr("vo", vv);
837 
838 	/*
839 	 * Queue message on interface, and start output if interface
840 	 * not yet active.
841 	 */
842 	s = splimp();
843 	if (IF_QFULL(&ifp->if_snd)) {
844 		IF_DROP(&ifp->if_snd);
845 		error = ENOBUFS;
846 		goto qfull;
847 	}
848 	IF_ENQUEUE(&ifp->if_snd, m);
849 	if (vv_softc[ifp->if_unit].vs_oactive == 0)
850 		vvstart(ifp->if_unit);
851 	splx(s);
852 	return (0);
853 qfull:
854 	m0 = m;
855 	splx(s);
856 bad:
857 	m_freem(m0);
858 	return(error);
859 }
860 
861 /*
862  * vvprt_hdr(s, v) print the local net header in "v"
863  * 	with title is "s"
864  */
865 vvprt_hdr(s, v)
866 	char *s;
867 	register struct vv_header *v;
868 {
869 	printf("%s: dsvti: 0x%x 0x%x 0x%x 0x%x 0x%x\n",
870 		s,
871 		0xff & (int)(v->vh_dhost), 0xff & (int)(v->vh_shost),
872 		0xff & (int)(v->vh_version), 0xff & (int)(v->vh_type),
873 		0xffff & (int)(v->vh_info));
874 }
875 
876 /*
877  * print "l" hex bytes starting at "s"
878  */
879 vvprt_hex(s, l)
880 	char *s;
881 	int l;
882 {
883 	register int i;
884 	register int z;
885 
886 	for (i=0 ; i < l; i++) {
887 		z = 0xff & (int)(*(s + i));
888 		printf("%c%c ",
889 		"0123456789abcdef"[(z >> 4) & 0x0f],
890 		"0123456789abcdef"[z & 0x0f]
891 		);
892 	}
893 }
894