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