xref: /csrg-svn/sys/vax/if/if_vv.c (revision 12172)
1 /*	if_vv.c	4.16	83/05/01	*/
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 	DELAY(2000000);
313 
314 	addr->vvocsr = VV_RST | VV_CPB;	/* clear packet buffer */
315 	ubainfo = vs->vs_ifuba.ifu_w.ifrw_info;
316 	addr->vvoba = (u_short) ubainfo;
317 	addr->vvoea = (u_short) (ubainfo >> 16);
318 	addr->vvowc = -((vs->vs_olen + 1) >> 1);
319 	addr->vvocsr = VV_CPB | VV_DEN | VV_INR | VV_ENB;
320 	/*
321 	 * Wait for receive side to finish.
322 	 * Extract source address (which will our own),
323 	 * and post to interface structure.
324 	 */
325 	DELAY(1000);
326 	for (waitcount = 0; (addr->vvicsr & VV_RDY) == 0; waitcount++) {
327 		if (waitcount < 10) {
328 			DELAY(1000);
329 			continue;
330 		}
331 		if (attempts++ >= 10) {
332 			printf("vv%d: can't initialize\n", unit);
333 			printf("vvinit loopwait: icsr = %b\n",
334 				0xffff&(addr->vvicsr), VV_IBITS);
335 			vs->vs_if.if_flags &= ~IFF_UP;
336 			return;
337 		}
338 		goto retry;
339 	}
340 	if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP)
341 		UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_w.ifrw_bdp);
342 	if (vs->vs_ifuba.ifu_xtofree)
343 		m_freem(vs->vs_ifuba.ifu_xtofree);
344 	if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP)
345 		UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_r.ifrw_bdp);
346 	m = if_rubaget(&vs->vs_ifuba, sizeof(struct vv_header), 0);
347 	if (m != NULL)
348 		m_freem(m);
349 	/*
350 	 * Check message type before we believe the source host address
351 	 */
352 	v = (struct vv_header *)(vs->vs_ifuba.ifu_r.ifrw_addr);
353 	if (v->vh_type != RING_WHOAMI)
354 		goto retry;
355 	return(v->vh_shost);
356 }
357 
358 /*
359  * vvtimeout() - called by timer flywheel to monitor input packet
360  * discard rate.  Interfaces getting too many errors are shut
361  * down for a while.  If the condition persists, the interface
362  * is marked down.
363  */
364 vvtimeout(junk)
365 	int junk;
366 {
367 	register struct vv_softc *vs;
368 	register int i;
369 	register struct vvreg *addr;
370 	int ubainfo;
371 
372 	timeout(vvtimeout, (caddr_t) 0, VV_FLYWHEEL);
373 	for (i = 0; i < NVV; i++) {
374 		vs = &vv_softc[i];
375 		addr = (struct vvreg *)vvinfo[i]->ui_addr;
376 		if (vs->vs_if.if_flags & IFF_UP == 0) continue;
377 		switch (vs->vs_major) {
378 
379 		/*
380 		 * MODE0: generally OK, just check error rate
381 		 */
382 		case MODE0:
383 			if (vs->vs_dropped < VV_ERRORTHRESHOLD) {
384 				vs->vs_dropped = 0;
385 				continue;
386 			}
387 			/* suspend reads for a while */
388 			vvtrprintf("vv%d going MODE1 in vvtimeout\n",i);
389 			vs->vs_major = MODE1;
390 			vs->vs_iactive = PAUSE;	/* no new reads */
391 			vs->vs_retry = VV_MODE1ATTEMPTS;
392 			vs->vs_delayclock = VV_MODE1DELAY;
393 			vs->vs_minor = 0;
394 			continue;
395 
396 		/*
397 		 * MODE1: excessive error rate observed
398 		 * Scheme: try simply suspending reads for a
399 		 * short while a small number of times
400 		 */
401 		case MODE1:
402 			if (vs->vs_delayclock > 0) {
403 				vs->vs_delayclock--;
404 				continue;
405 			}
406 			switch (vs->vs_minor) {
407 
408 			case 0:				/* reenable reads */
409 				vvtrprintf("vv%d M1m0\n",i);
410 				vs->vs_dropped = 0;
411 				vs->vs_iactive = ACTIVE;
412 				vs->vs_minor = 1;	/* next state */
413 				ubainfo = vs->vs_ifuba.ifu_r.ifrw_info;
414 				addr->vviba = (u_short) ubainfo;
415 				addr->vviea = (u_short) (ubainfo >> 16);
416 				addr->vviwc =
417 				  -(sizeof (struct vv_header) + VVMTU) >> 1;
418 				addr->vvicsr = VV_RST | VV_CONF;
419 				addr->vvicsr |= VV_IEN | VV_DEN | VV_ENB;
420 				continue;
421 
422 			case 1:				/* see if it worked */
423 				vvtrprintf("vv%d M1m1\n",i);
424 				if (vs->vs_dropped < VV_ERRORTHRESHOLD) {
425 					vs->vs_dropped = 0;
426 					vs->vs_major = MODE0;	/* yeah!! */
427 					continue;
428 				}
429 				if (vs->vs_retry -- > 0) {
430 					vs->vs_dropped = 0;
431 					vs->vs_iactive = PAUSE;
432 					vs->vs_delayclock = VV_MODE1DELAY;
433 					vs->vs_minor = 0; /* recheck */
434 					continue;
435 				}
436 				vs->vs_major = MODE2;
437 				vs->vs_minor = 0;
438 				vs->vs_dropped = 0;
439 				vs->vs_iactive = OPEN;
440 				vs->vs_delayrange = VV_MODE2DELAY;
441 				vs->vs_delayclock = VV_MODE2DELAY;
442 				/* fall thru ... */
443 			}
444 
445 		/*
446 		 * MODE2: simply ignoring traffic didn't relieve condition
447 		 * Scheme: open host relay for intervals linearly
448 		 * increasing up to some maximum of a several minutes.
449 		 * This allows broken networks to return to operation
450 		 * without rebooting.
451 		 */
452 		case MODE2:
453 			if (vs->vs_delayclock > 0) {
454 				vs->vs_delayclock--;
455 				continue;
456 			}
457 			switch (vs->vs_minor) {
458 
459 			case 0:		/* close relay and reenable reads */
460 				vvtrprintf("vv%d M2m0\n",i);
461 				vs->vs_dropped = 0;
462 				vs->vs_iactive = ACTIVE;
463 				vs->vs_minor = 1;	/* next state */
464 				ubainfo = vs->vs_ifuba.ifu_r.ifrw_info;
465 				addr->vviba = (u_short) ubainfo;
466 				addr->vviea = (u_short) (ubainfo >> 16);
467 				addr->vviwc =
468 				  -(sizeof (struct vv_header) + VVMTU) >> 1;
469 				addr->vvicsr = VV_RST | VV_CONF;
470 				addr->vvicsr |= VV_IEN | VV_DEN | VV_ENB;
471 				continue;
472 
473 			case 1:				/* see if it worked */
474 				vvtrprintf("vv%d M2m1\n",i);
475 				if (vs->vs_dropped < VV_ERRORTHRESHOLD) {
476 					vs->vs_dropped = 0;
477 					vs->vs_major = MODE0;	/* yeah!! */
478 					continue;
479 				}
480 				vvtrprintf("vv%d M2m1 ++ delay\n",i);
481 				vs->vs_dropped = 0;
482 				vs->vs_iactive = OPEN;
483 				vs->vs_minor = 0;
484 				if (vs->vs_delayrange < VV_MAXDELAY)
485 					vs->vs_delayrange +=
486 					  (vs->vs_delayrange/2);
487 				vs->vs_delayclock = vs->vs_delayrange;
488 				continue;
489 			}
490 
491 		default:
492 			printf("vv%d: major state screwed\n", i);
493 			vs->vs_if.if_flags &= ~IFF_UP;
494 		}
495 	}
496 }
497 
498 /*
499  * Start or restart output on interface.
500  * If interface is active, this is a retransmit, so just
501  * restuff registers and go.
502  * If interface is not already active, get another datagram
503  * to send off of the interface queue, and map it to the interface
504  * before starting the output.
505  */
506 vvstart(dev)
507 	dev_t dev;
508 {
509         int unit = VVUNIT(dev);
510 	struct uba_device *ui = vvinfo[unit];
511 	register struct vv_softc *vs = &vv_softc[unit];
512 	register struct vvreg *addr;
513 	struct mbuf *m;
514 	int ubainfo;
515 	int dest;
516 
517 	if (vs->vs_oactive)
518 		goto restart;
519 	/*
520 	 * Not already active: dequeue another request
521 	 * and map it to the UNIBUS.  If no more requests,
522 	 * just return.
523 	 */
524 	IF_DEQUEUE(&vs->vs_if.if_snd, m);
525 	if (m == NULL) {
526 		vs->vs_oactive = 0;
527 		return;
528 	}
529 	dest = mtod(m, struct vv_header *)->vh_dhost;
530 	vs->vs_olen = if_wubaput(&vs->vs_ifuba, m);
531 	vs->vs_lastx = dest;
532 restart:
533 	/*
534 	 * Have request mapped to UNIBUS for transmission.
535 	 * Purge any stale data from this BDP, and start the otput.
536 	 */
537 	if (vs->vs_olen > VVMTU + sizeof (struct vvheader)) {
538 		printf("vv%d vs_olen: %d > VVMTU\n", unit, vs->vs_olen);
539 		panic("vvdriver vs_olen botch");
540 	}
541 	if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP)
542 		UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_w.ifrw_bdp);
543 	addr = (struct vvreg *)ui->ui_addr;
544 	ubainfo = vs->vs_ifuba.ifu_w.ifrw_info;
545 	addr->vvoba = (u_short) ubainfo;
546 	addr->vvoea = (u_short) (ubainfo >> 16);
547 	addr->vvowc = -((vs->vs_olen + 1) >> 1);
548 	addr->vvocsr = VV_IEN | VV_CPB | VV_DEN | VV_INR | VV_ENB;
549 	vs->vs_oactive = 1;
550 }
551 
552 /*
553  * VVLNI transmit interrupt
554  * Start another output if more data to send.
555  */
556 vvxint(unit)
557 	int unit;
558 {
559 	register struct uba_device *ui = vvinfo[unit];
560 	register struct vv_softc *vs = &vv_softc[unit];
561 	register struct vvreg *addr;
562 	register int oc;
563 
564 	addr = (struct vvreg *)ui->ui_addr;
565 	oc = 0xffff & (addr->vvocsr);
566 	if (vs->vs_oactive == 0) {
567 		printf("vv%d: stray interrupt vvocsr = %b\n", unit,
568 			oc, VV_OBITS);
569 		return;
570 	}
571 	if (oc &  (VV_OPT | VV_RFS)) {
572 		vs->vs_if.if_collisions++;
573 		if (vs->vs_tries++ < VVRETRY) {
574 			if (oc & VV_OPT)
575 				vs->vs_init++;
576 			if (oc & VV_RFS)
577 				vs->vs_nottaken++;
578 			vvstart(unit);		/* restart this message */
579 			return;
580 		}
581 		if (oc & VV_OPT)
582 			printf("vv%d: output timeout\n");
583 	}
584 	vs->vs_if.if_opackets++;
585 	vs->vs_oactive = 0;
586 	vs->vs_tries = 0;
587 	if (oc & VVXERR) {
588 		vs->vs_if.if_oerrors++;
589 		printf("vv%d: error vvocsr = %b\n", unit, 0xffff & oc,
590 			VV_OBITS);
591 	}
592 	if (vs->vs_ifuba.ifu_xtofree) {
593 		m_freem(vs->vs_ifuba.ifu_xtofree);
594 		vs->vs_ifuba.ifu_xtofree = 0;
595 	}
596 	if (vs->vs_if.if_snd.ifq_head == 0) {
597 		vs->vs_lastx = 256;		/* an invalid address */
598 		return;
599 	}
600 	vvstart(unit);
601 }
602 
603 /*
604  * V2lni interface receiver interrupt.
605  * If input error just drop packet.
606  * Otherwise purge input buffered data path and examine
607  * packet to determine type.  If can't determine length
608  * from type, then have to drop packet.  Othewise decapsulate
609  * packet based on type and pass to type specific higher-level
610  * input routine.
611  */
612 vvrint(unit)
613 	int unit;
614 {
615 	register struct vv_softc *vs = &vv_softc[unit];
616 	struct vvreg *addr = (struct vvreg *)vvinfo[unit]->ui_addr;
617 	register struct vv_header *vv;
618 	register struct ifqueue *inq;
619     	struct mbuf *m;
620 	int ubainfo, len, off;
621 	short resid;
622 
623 	vs->vs_if.if_ipackets++;
624 	/*
625 	 * Purge BDP; drop if input error indicated.
626 	 */
627 	if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP)
628 		UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_r.ifrw_bdp);
629 	if (addr->vvicsr & VVRERR) {
630 		if (vv_logreaderrors)
631 			printf("vv%d: error vvicsr = %b\n", unit,
632 				0xffff&(addr->vvicsr), VV_IBITS);
633 		goto dropit;
634 	}
635 
636 	/*
637 	 * Get packet length from word count residue
638 	 *
639 	 * Compute header offset if trailer protocol
640 	 *
641 	 * Pull packet off interface.  Off is nonzero if packet
642 	 * has trailing header; if_rubaget will then force this header
643 	 * information to be at the front.  The vh_info field
644 	 * carries the offset to the trailer data in trailer
645 	 * format packets.
646 	 */
647 	vv = (struct vv_header *)(vs->vs_ifuba.ifu_r.ifrw_addr);
648 	vvtracehdr("vi", vv);
649 	resid = addr->vviwc;
650 	if (resid)
651 		resid |= 0176000;		/* ugly!!!! */
652 	len = (((sizeof (struct vv_header) + VVMRU) >> 1) + resid) << 1;
653 	len -= sizeof(struct vv_header);
654 	if (len > VVMRU || len <= 0)
655 		goto dropit;
656 #define	vvdataaddr(vv, off, type)	((type)(((caddr_t)((vv)+1)+(off))))
657 	if (vv_dotrailer && vv->vh_type >= RING_IPTrailer &&
658 	     vv->vh_type < RING_IPTrailer+RING_IPNTrailer){
659 		off = (vv->vh_type - RING_IPTrailer) * 512;
660 		if (off > VVMTU)
661 			goto dropit;
662 		vv->vh_type = *vvdataaddr(vv, off, u_short *);
663 		resid = *(vvdataaddr(vv, off+2, u_short *));
664 		if (off + resid > len)
665 			goto dropit;
666 		len = off + resid;
667 	} else
668 		off = 0;
669 	if (len == 0)
670 		goto dropit;
671 	m = if_rubaget(&vs->vs_ifuba, len, off);
672 	if (m == NULL)
673 		goto dropit;
674 	if (off) {
675 		m->m_off += 2 * sizeof(u_short);
676 		m->m_len -= 2 * sizeof(u_short);
677 	}
678 
679 	/*
680 	 * Demultiplex on packet type
681 	 */
682 	switch (vv->vh_type) {
683 
684 #ifdef INET
685 	case RING_IP:
686 		schednetisr(NETISR_IP);
687 		inq = &ipintrq;
688 		break;
689 #endif
690 	default:
691 		printf("vv%d: unknown pkt type 0x%x\n", unit, vv->vh_type);
692 		m_freem(m);
693 		goto setup;
694 	}
695 	if (IF_QFULL(inq)) {
696 		IF_DROP(inq);
697 		m_freem(m);
698 	} else
699 		IF_ENQUEUE(inq, m);
700 setup:
701 	/*
702 	 * Check the error rate and start recovery if needed
703 	 * this has to go here since the timer flywheel runs at
704 	 * a lower ipl and never gets a chance to change the mode
705 	 */
706 	if (vs->vs_major == MODE0 && vs->vs_dropped > VV_ERRORTHRESHOLD) {
707 		vvtrprintf("vv%d going MODE1 in vvrint\n",unit);
708 		vs->vs_major = MODE1;
709 		vs->vs_iactive = PAUSE;		/* no new reads */
710 		vs->vs_retry = VV_MODE1ATTEMPTS;
711 		vs->vs_delayclock = VV_MODE1DELAY;
712 		vs->vs_minor = 0;
713 		vs->vs_dropped = 0;
714 	}
715 	switch (vs->vs_iactive) {
716 
717 	case ACTIVE:		/* Restart the read for next packet */
718 		ubainfo = vs->vs_ifuba.ifu_r.ifrw_info;
719 		addr->vviba = (u_short) ubainfo;
720 		addr->vviea = (u_short) (ubainfo >> 16);
721 		addr->vviwc = -(sizeof (struct vv_header) + VVMTU) >> 1;
722 		addr->vvicsr = VV_RST | VV_CONF;
723 		addr->vvicsr |= VV_IEN | VV_DEN | VV_ENB;
724 		return;
725 
726 	case PAUSE:		/* requested to not start any new reads */
727 		vs->vs_dropped = 0;
728 		return;
729 
730 	case OPEN:		/* request to open host relay */
731 		vs->vs_dropped = 0;
732 		addr->vvicsr = 0;
733 		return;
734 
735 	default:
736 		printf("vv%d: vs_iactive = %d\n", unit, vs->vs_iactive);
737 		return;
738 	}
739 	/*
740 	 * Drop packet on floor -- count them!!
741 	 */
742 dropit:
743 	vs->vs_if.if_ierrors++;
744 	vs->vs_dropped++;
745 	/*
746 	printf("vv%d: error vvicsr = %b\n", unit,
747 		0xffff&(addr->vvicsr), VV_IBITS);
748 	*/
749 	goto setup;
750 }
751 
752 /*
753  * V2lni output routine.
754  * Encapsulate a packet of type family for the local net.
755  * Use trailer local net encapsulation if enough data in first
756  * packet leaves a multiple of 512 bytes of data in remainder.
757  */
758 vvoutput(ifp, m0, dst)
759 	struct ifnet *ifp;
760 	struct mbuf *m0;
761 	struct sockaddr *dst;
762 {
763 	register struct mbuf *m = m0;
764 	register struct vv_header *vv;
765 	register int off;
766 	int type, dest, s, error;
767 
768 	switch (dst->sa_family) {
769 
770 #ifdef INET
771 	case AF_INET: {
772 		dest = ((struct sockaddr_in *)dst)->sin_addr.s_addr;
773 		if ((dest = in_lnaof(*((struct in_addr *)&dest))) >= 0x100) {
774 			error = EPERM;
775 			goto bad;
776 		}
777 		off = ntohs((u_short)mtod(m, struct ip *)->ip_len) - m->m_len;
778 		if (vv_dotrailer && off > 0 && (off & 0x1ff) == 0 &&
779 		    m->m_off >= MMINOFF + 2 * sizeof (u_short)) {
780 			type = RING_IPTrailer + (off>>9);
781 			m->m_off -= 2 * sizeof (u_short);
782 			m->m_len += 2 * sizeof (u_short);
783 			*mtod(m, u_short *) = RING_IP;
784 			*(mtod(m, u_short *) + 1) = m->m_len;
785 			goto gottrailertype;
786 		}
787 		type = RING_IP;
788 		off = 0;
789 		goto gottype;
790 		}
791 #endif
792 	default:
793 		printf("vv%d: can't handle af%d\n", ifp->if_unit,
794 			dst->sa_family);
795 		error = EAFNOSUPPORT;
796 		goto bad;
797 	}
798 
799 gottrailertype:
800 	/*
801 	 * Packet to be sent as trailer: move first packet
802 	 * (control information) to end of chain.
803 	 */
804 	while (m->m_next)
805 		m = m->m_next;
806 	m->m_next = m0;
807 	m = m0->m_next;
808 	m0->m_next = 0;
809 	m0 = m;
810 gottype:
811 	/*
812 	 * Add local net header.  If no space in first mbuf,
813 	 * allocate another.
814 	 */
815 	if (m->m_off > MMAXOFF ||
816 	    MMINOFF + sizeof (struct vv_header) > m->m_off) {
817 		m = m_get(M_DONTWAIT, MT_HEADER);
818 		if (m == NULL) {
819 			error = ENOBUFS;
820 			goto bad;
821 		}
822 		m->m_next = m0;
823 		m->m_off = MMINOFF;
824 		m->m_len = sizeof (struct vv_header);
825 	} else {
826 		m->m_off -= sizeof (struct vv_header);
827 		m->m_len += sizeof (struct vv_header);
828 	}
829 	vv = mtod(m, struct vv_header *);
830 	vv->vh_shost = ifp->if_host[0];
831 	vv->vh_dhost = dest;
832 	vv->vh_version = RING_VERSION;
833 	vv->vh_type = type;
834 	vv->vh_info = off;
835 	vvtracehdr("vo", vv);
836 
837 	/*
838 	 * Queue message on interface, and start output if interface
839 	 * not yet active.
840 	 */
841 	s = splimp();
842 	if (IF_QFULL(&ifp->if_snd)) {
843 		IF_DROP(&ifp->if_snd);
844 		error = ENOBUFS;
845 		goto qfull;
846 	}
847 	IF_ENQUEUE(&ifp->if_snd, m);
848 	if (vv_softc[ifp->if_unit].vs_oactive == 0)
849 		vvstart(ifp->if_unit);
850 	splx(s);
851 	return (0);
852 qfull:
853 	m0 = m;
854 	splx(s);
855 bad:
856 	m_freem(m0);
857 	return(error);
858 }
859 
860 /*
861  * vvprt_hdr(s, v) print the local net header in "v"
862  * 	with title is "s"
863  */
864 vvprt_hdr(s, v)
865 	char *s;
866 	register struct vv_header *v;
867 {
868 	printf("%s: dsvti: 0x%x 0x%x 0x%x 0x%x 0x%x\n",
869 		s,
870 		0xff & (int)(v->vh_dhost), 0xff & (int)(v->vh_shost),
871 		0xff & (int)(v->vh_version), 0xff & (int)(v->vh_type),
872 		0xffff & (int)(v->vh_info));
873 }
874 
875 /*
876  * print "l" hex bytes starting at "s"
877  */
878 vvprt_hex(s, l)
879 	char *s;
880 	int l;
881 {
882 	register int i;
883 	register int z;
884 
885 	for (i=0 ; i < l; i++) {
886 		z = 0xff & (int)(*(s + i));
887 		printf("%c%c ",
888 		"0123456789abcdef"[(z >> 4) & 0x0f],
889 		"0123456789abcdef"[z & 0x0f]
890 		);
891 	}
892 }
893