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