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