xref: /original-bsd/sys/vax/if/if_vv.c (revision 264c46cb)
1 /*	if_vv.c	4.22	83/06/13	*/
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 #include "../h/ioctl.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_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(),vvioctl(),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 
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_init = vvinit;
172 	vs->vs_if.if_ioctl = vvioctl;
173 	vs->vs_if.if_output = vvoutput;
174 	vs->vs_if.if_reset = vvreset;
175 	vs->vs_ifuba.ifu_flags = UBA_CANTWAIT | UBA_NEEDBDP | UBA_NEED16;
176 #if defined(VAX750)
177 	/* don't chew up 750 bdp's */
178 	if (cpu == VAX_750 && ui->ui_unit > 0)
179 		vs->vs_ifuba.ifu_flags &= ~UBA_NEEDBDP;
180 #endif
181 	if_attach(&vs->vs_if);
182 }
183 
184 /*
185  * Reset of interface after UNIBUS reset.
186  * If interface is on specified uba, reset its state.
187  */
188 vvreset(unit, uban)
189 	int unit, uban;
190 {
191 	register struct uba_device *ui;
192 
193 	if (unit >= NVV || (ui = vvinfo[unit]) == 0 || ui->ui_alive == 0 ||
194 	    ui->ui_ubanum != uban)
195 		return;
196 	printf(" vv%d", unit);
197 	vvinit(unit);
198 }
199 
200 /*
201  * Initialization of interface; clear recorded pending
202  * operations, and reinitialize UNIBUS usage.
203  */
204 vvinit(unit)
205 	int unit;
206 {
207 	register struct vv_softc *vs = &vv_softc[unit];
208 	register struct uba_device *ui = vvinfo[unit];
209 	register struct vvreg *addr;
210 	struct sockaddr_in *sin;
211 	int ubainfo, s;
212 	int vvtimeout();
213 
214 	if (vs->vs_if.if_net == 0)
215 		return;
216 	addr = (struct vvreg *)ui->ui_addr;
217 	if (if_ubainit(&vs->vs_ifuba, ui->ui_ubanum,
218 	    sizeof (struct vv_header), (int)btoc(VVMTU)) == 0) {
219 		printf("vv%d: can't initialize\n", unit);
220 		vs->vs_if.if_flags &= ~IFF_UP;
221 		return;
222 	}
223 	if (vv_ticking++ == 0)
224 		timeout(vvtimeout, (caddr_t) 0, VV_FLYWHEEL);
225 	/*
226 	 * Discover our host address and post it
227 	 */
228 	vs->vs_if.if_host[0] = vvidentify(unit);
229 	printf("vv%d: host %d\n", unit, vs->vs_if.if_host[0]);
230 	sin = (struct sockaddr_in *)&vs->vs_if.if_addr;
231 	sin->sin_family = AF_INET;
232 	sin->sin_addr = if_makeaddr(vs->vs_if.if_net, vs->vs_if.if_host[0]);
233 	sin = (struct sockaddr_in *)&vs->vs_if.if_broadaddr;
234 	sin->sin_family = AF_INET;
235 	sin->sin_addr = if_makeaddr(vs->vs_if.if_net, VV_BROADCAST);
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 	DELAY(500000);				/* 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 | IFF_RUNNING;
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 	int unit;
270 {
271 	register struct vv_softc *vs = &vv_softc[unit];
272 	register struct uba_device *ui = vvinfo[unit];
273 	register struct vvreg *addr;
274 	struct mbuf *m;
275 	struct vv_header *v;
276 	int ubainfo, attempts, waitcount;
277 
278 	/*
279 	 * Build a multicast message to identify our address
280 	 */
281 	addr = (struct vvreg *)ui->ui_addr;
282 	attempts = 0;		/* total attempts, including bad msg type */
283 	m = m_get(M_DONTWAIT, MT_HEADER);
284 	if (m == NULL)
285 		return (0);
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 (0);
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 /*ARGSUSED*/
365 vvtimeout(junk)
366 	int junk;
367 {
368 	register struct vv_softc *vs;
369 	register int i;
370 	register struct vvreg *addr;
371 	int ubainfo;
372 
373 	timeout(vvtimeout, (caddr_t) 0, VV_FLYWHEEL);
374 	for (i = 0; i < NVV; i++) {
375 		vs = &vv_softc[i];
376 		addr = (struct vvreg *)vvinfo[i]->ui_addr;
377 		if ((vs->vs_if.if_flags & IFF_UP) == 0)
378 			continue;
379 		switch (vs->vs_major) {
380 
381 		/*
382 		 * MODE0: generally OK, just check error rate
383 		 */
384 		case MODE0:
385 			if (vs->vs_dropped < VV_ERRORTHRESHOLD) {
386 				vs->vs_dropped = 0;
387 				continue;
388 			}
389 			/* suspend reads for a while */
390 			vvtrprintf("vv%d going MODE1 in vvtimeout\n",i);
391 			vs->vs_major = MODE1;
392 			vs->vs_iactive = PAUSE;	/* no new reads */
393 			vs->vs_retry = VV_MODE1ATTEMPTS;
394 			vs->vs_delayclock = VV_MODE1DELAY;
395 			vs->vs_minor = 0;
396 			continue;
397 
398 		/*
399 		 * MODE1: excessive error rate observed
400 		 * Scheme: try simply suspending reads for a
401 		 * short while a small number of times
402 		 */
403 		case MODE1:
404 			if (vs->vs_delayclock > 0) {
405 				vs->vs_delayclock--;
406 				continue;
407 			}
408 			switch (vs->vs_minor) {
409 
410 			case 0:				/* reenable reads */
411 				vvtrprintf("vv%d M1m0\n",i);
412 				vs->vs_dropped = 0;
413 				vs->vs_iactive = ACTIVE;
414 				vs->vs_minor = 1;	/* next state */
415 				ubainfo = vs->vs_ifuba.ifu_r.ifrw_info;
416 				addr->vviba = (u_short) ubainfo;
417 				addr->vviea = (u_short) (ubainfo >> 16);
418 				addr->vviwc =
419 				  -(sizeof (struct vv_header) + VVMTU) >> 1;
420 				addr->vvicsr = VV_RST | VV_CONF;
421 				addr->vvicsr |= VV_IEN | VV_DEN | VV_ENB;
422 				continue;
423 
424 			case 1:				/* see if it worked */
425 				vvtrprintf("vv%d M1m1\n",i);
426 				if (vs->vs_dropped < VV_ERRORTHRESHOLD) {
427 					vs->vs_dropped = 0;
428 					vs->vs_major = MODE0;	/* yeah!! */
429 					continue;
430 				}
431 				if (vs->vs_retry -- > 0) {
432 					vs->vs_dropped = 0;
433 					vs->vs_iactive = PAUSE;
434 					vs->vs_delayclock = VV_MODE1DELAY;
435 					vs->vs_minor = 0; /* recheck */
436 					continue;
437 				}
438 				vs->vs_major = MODE2;
439 				vs->vs_minor = 0;
440 				vs->vs_dropped = 0;
441 				vs->vs_iactive = OPEN;
442 				vs->vs_delayrange = VV_MODE2DELAY;
443 				vs->vs_delayclock = VV_MODE2DELAY;
444 				/* fall thru ... */
445 			}
446 
447 		/*
448 		 * MODE2: simply ignoring traffic didn't relieve condition
449 		 * Scheme: open host relay for intervals linearly
450 		 * increasing up to some maximum of a several minutes.
451 		 * This allows broken networks to return to operation
452 		 * without rebooting.
453 		 */
454 		case MODE2:
455 			if (vs->vs_delayclock > 0) {
456 				vs->vs_delayclock--;
457 				continue;
458 			}
459 			switch (vs->vs_minor) {
460 
461 			case 0:		/* close relay and reenable reads */
462 				vvtrprintf("vv%d M2m0\n",i);
463 				vs->vs_dropped = 0;
464 				vs->vs_iactive = ACTIVE;
465 				vs->vs_minor = 1;	/* next state */
466 				ubainfo = vs->vs_ifuba.ifu_r.ifrw_info;
467 				addr->vviba = (u_short) ubainfo;
468 				addr->vviea = (u_short) (ubainfo >> 16);
469 				addr->vviwc =
470 				  -(sizeof (struct vv_header) + VVMTU) >> 1;
471 				addr->vvicsr = VV_RST | VV_CONF;
472 				addr->vvicsr |= VV_IEN | VV_DEN | VV_ENB;
473 				continue;
474 
475 			case 1:				/* see if it worked */
476 				vvtrprintf("vv%d M2m1\n",i);
477 				if (vs->vs_dropped < VV_ERRORTHRESHOLD) {
478 					vs->vs_dropped = 0;
479 					vs->vs_major = MODE0;	/* yeah!! */
480 					continue;
481 				}
482 				vvtrprintf("vv%d M2m1 ++ delay\n",i);
483 				vs->vs_dropped = 0;
484 				vs->vs_iactive = OPEN;
485 				vs->vs_minor = 0;
486 				if (vs->vs_delayrange < VV_MAXDELAY)
487 					vs->vs_delayrange +=
488 					  (vs->vs_delayrange/2);
489 				vs->vs_delayclock = vs->vs_delayrange;
490 				continue;
491 			}
492 
493 		default:
494 			printf("vv%d: major state screwed\n", i);
495 			vs->vs_if.if_flags &= ~IFF_UP;
496 		}
497 	}
498 }
499 
500 /*
501  * Start or restart output on interface.
502  * If interface is active, this is a retransmit, so just
503  * restuff registers and go.
504  * If interface is not already active, get another datagram
505  * to send off of the interface queue, and map it to the interface
506  * before starting the output.
507  */
508 vvstart(dev)
509 	dev_t dev;
510 {
511         int unit = VVUNIT(dev);
512 	struct uba_device *ui = vvinfo[unit];
513 	register struct vv_softc *vs = &vv_softc[unit];
514 	register struct vvreg *addr;
515 	struct mbuf *m;
516 	int ubainfo;
517 	int dest;
518 
519 	if (vs->vs_oactive)
520 		goto restart;
521 	/*
522 	 * Not already active: dequeue another request
523 	 * and map it to the UNIBUS.  If no more requests,
524 	 * just return.
525 	 */
526 	IF_DEQUEUE(&vs->vs_if.if_snd, m);
527 	if (m == NULL) {
528 		vs->vs_oactive = 0;
529 		return;
530 	}
531 	dest = mtod(m, struct vv_header *)->vh_dhost;
532 	vs->vs_olen = if_wubaput(&vs->vs_ifuba, m);
533 	vs->vs_lastx = dest;
534 restart:
535 	/*
536 	 * Have request mapped to UNIBUS for transmission.
537 	 * Purge any stale data from this BDP, and start the otput.
538 	 */
539 	if (vs->vs_olen > VVMTU + sizeof (struct vv_header)) {
540 		printf("vv%d vs_olen: %d > VVMTU\n", unit, vs->vs_olen);
541 		panic("vvdriver vs_olen botch");
542 	}
543 	if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP)
544 		UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_w.ifrw_bdp);
545 	addr = (struct vvreg *)ui->ui_addr;
546 	ubainfo = vs->vs_ifuba.ifu_w.ifrw_info;
547 	addr->vvoba = (u_short) ubainfo;
548 	addr->vvoea = (u_short) (ubainfo >> 16);
549 	addr->vvowc = -((vs->vs_olen + 1) >> 1);
550 	addr->vvocsr = VV_IEN | VV_CPB | VV_DEN | VV_INR | VV_ENB;
551 	vs->vs_oactive = 1;
552 }
553 
554 /*
555  * VVLNI transmit interrupt
556  * Start another output if more data to send.
557  */
558 vvxint(unit)
559 	int unit;
560 {
561 	register struct uba_device *ui = vvinfo[unit];
562 	register struct vv_softc *vs = &vv_softc[unit];
563 	register struct vvreg *addr;
564 	register int oc;
565 
566 	addr = (struct vvreg *)ui->ui_addr;
567 	oc = 0xffff & (addr->vvocsr);
568 	if (vs->vs_oactive == 0) {
569 		printf("vv%d: stray interrupt vvocsr = %b\n", unit,
570 			oc, VV_OBITS);
571 		return;
572 	}
573 	if (oc &  (VV_OPT | VV_RFS)) {
574 		vs->vs_if.if_collisions++;
575 		if (vs->vs_tries++ < VVRETRY) {
576 			if (oc & VV_OPT)
577 				vs->vs_init++;
578 			if (oc & VV_RFS)
579 				vs->vs_nottaken++;
580 			vvstart(unit);		/* restart this message */
581 			return;
582 		}
583 		if (oc & VV_OPT)
584 			printf("vv%d: output timeout\n");
585 	}
586 	vs->vs_if.if_opackets++;
587 	vs->vs_oactive = 0;
588 	vs->vs_tries = 0;
589 	if (oc & VVXERR) {
590 		vs->vs_if.if_oerrors++;
591 		printf("vv%d: error vvocsr = %b\n", unit, 0xffff & oc,
592 			VV_OBITS);
593 	}
594 	if (vs->vs_ifuba.ifu_xtofree) {
595 		m_freem(vs->vs_ifuba.ifu_xtofree);
596 		vs->vs_ifuba.ifu_xtofree = 0;
597 	}
598 	if (vs->vs_if.if_snd.ifq_head == 0) {
599 		vs->vs_lastx = 256;		/* an invalid address */
600 		return;
601 	}
602 	vvstart(unit);
603 }
604 
605 /*
606  * V2lni interface receiver interrupt.
607  * If input error just drop packet.
608  * Otherwise purge input buffered data path and examine
609  * packet to determine type.  If can't determine length
610  * from type, then have to drop packet.  Othewise decapsulate
611  * packet based on type and pass to type specific higher-level
612  * input routine.
613  */
614 vvrint(unit)
615 	int unit;
616 {
617 	register struct vv_softc *vs = &vv_softc[unit];
618 	struct vvreg *addr = (struct vvreg *)vvinfo[unit]->ui_addr;
619 	register struct vv_header *vv;
620 	register struct ifqueue *inq;
621     	struct mbuf *m;
622 	int ubainfo, len, off;
623 	short resid;
624 
625 	vs->vs_if.if_ipackets++;
626 	/*
627 	 * Purge BDP; drop if input error indicated.
628 	 */
629 	if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP)
630 		UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_r.ifrw_bdp);
631 	if (addr->vvicsr & VVRERR) {
632 		if (vv_logreaderrors)
633 			printf("vv%d: error vvicsr = %b\n", unit,
634 				0xffff&(addr->vvicsr), VV_IBITS);
635 		goto dropit;
636 	}
637 
638 	/*
639 	 * Get packet length from word count residue
640 	 *
641 	 * Compute header offset if trailer protocol
642 	 *
643 	 * Pull packet off interface.  Off is nonzero if packet
644 	 * has trailing header; if_rubaget will then force this header
645 	 * information to be at the front.  The vh_info field
646 	 * carries the offset to the trailer data in trailer
647 	 * format packets.
648 	 */
649 	vv = (struct vv_header *)(vs->vs_ifuba.ifu_r.ifrw_addr);
650 	vvtracehdr("vi", vv);
651 	resid = addr->vviwc;
652 	if (resid)
653 		resid |= 0176000;		/* ugly!!!! */
654 	len = (((sizeof (struct vv_header) + VVMRU) >> 1) + resid) << 1;
655 	len -= sizeof(struct vv_header);
656 	if (len > VVMRU || len <= 0)
657 		goto dropit;
658 #define	vvdataaddr(vv, off, type)	((type)(((caddr_t)((vv)+1)+(off))))
659 	if (vv->vh_type >= RING_IPTrailer &&
660 	     vv->vh_type < RING_IPTrailer+RING_IPNTrailer) {
661 		off = (vv->vh_type - RING_IPTrailer) * 512;
662 		if (off > VVMTU)
663 			goto dropit;
664 		vv->vh_type = *vvdataaddr(vv, off, u_short *);
665 		resid = *(vvdataaddr(vv, off+2, u_short *));
666 		if (off + resid > len)
667 			goto dropit;
668 		len = off + resid;
669 	} else
670 		off = 0;
671 	if (len == 0)
672 		goto dropit;
673 	m = if_rubaget(&vs->vs_ifuba, len, off);
674 	if (m == NULL)
675 		goto dropit;
676 	if (off) {
677 		m->m_off += 2 * sizeof(u_short);
678 		m->m_len -= 2 * sizeof(u_short);
679 	}
680 
681 	/*
682 	 * Demultiplex on packet type
683 	 */
684 	switch (vv->vh_type) {
685 
686 #ifdef INET
687 	case RING_IP:
688 		schednetisr(NETISR_IP);
689 		inq = &ipintrq;
690 		break;
691 #endif
692 	default:
693 		printf("vv%d: unknown pkt type 0x%x\n", unit, vv->vh_type);
694 		m_freem(m);
695 		goto setup;
696 	}
697 	if (IF_QFULL(inq)) {
698 		IF_DROP(inq);
699 		m_freem(m);
700 	} else
701 		IF_ENQUEUE(inq, m);
702 setup:
703 	/*
704 	 * Check the error rate and start recovery if needed
705 	 * this has to go here since the timer flywheel runs at
706 	 * a lower ipl and never gets a chance to change the mode
707 	 */
708 	if (vs->vs_major == MODE0 && vs->vs_dropped > VV_ERRORTHRESHOLD) {
709 		vvtrprintf("vv%d going MODE1 in vvrint\n",unit);
710 		vs->vs_major = MODE1;
711 		vs->vs_iactive = PAUSE;		/* no new reads */
712 		vs->vs_retry = VV_MODE1ATTEMPTS;
713 		vs->vs_delayclock = VV_MODE1DELAY;
714 		vs->vs_minor = 0;
715 		vs->vs_dropped = 0;
716 	}
717 	switch (vs->vs_iactive) {
718 
719 	case ACTIVE:		/* Restart the read for next packet */
720 		ubainfo = vs->vs_ifuba.ifu_r.ifrw_info;
721 		addr->vviba = (u_short) ubainfo;
722 		addr->vviea = (u_short) (ubainfo >> 16);
723 		addr->vviwc = -(sizeof (struct vv_header) + VVMTU) >> 1;
724 		addr->vvicsr = VV_RST | VV_CONF;
725 		addr->vvicsr |= VV_IEN | VV_DEN | VV_ENB;
726 		return;
727 
728 	case PAUSE:		/* requested to not start any new reads */
729 		vs->vs_dropped = 0;
730 		return;
731 
732 	case OPEN:		/* request to open host relay */
733 		vs->vs_dropped = 0;
734 		addr->vvicsr = 0;
735 		return;
736 
737 	default:
738 		printf("vv%d: vs_iactive = %d\n", unit, vs->vs_iactive);
739 		return;
740 	}
741 	/*
742 	 * Drop packet on floor -- count them!!
743 	 */
744 dropit:
745 	vs->vs_if.if_ierrors++;
746 	vs->vs_dropped++;
747 	/*
748 	printf("vv%d: error vvicsr = %b\n", unit,
749 		0xffff&(addr->vvicsr), VV_IBITS);
750 	*/
751 	goto setup;
752 }
753 
754 /*
755  * V2lni output routine.
756  * Encapsulate a packet of type family for the local net.
757  * Use trailer local net encapsulation if enough data in first
758  * packet leaves a multiple of 512 bytes of data in remainder.
759  */
760 vvoutput(ifp, m0, dst)
761 	struct ifnet *ifp;
762 	struct mbuf *m0;
763 	struct sockaddr *dst;
764 {
765 	register struct mbuf *m = m0;
766 	register struct vv_header *vv;
767 	register int off;
768 	int type, dest, s, error;
769 
770 	switch (dst->sa_family) {
771 
772 #ifdef INET
773 	case AF_INET: {
774 		dest = ((struct sockaddr_in *)dst)->sin_addr.s_addr;
775 		if ((dest = in_lnaof(*((struct in_addr *)&dest))) >= 0x100) {
776 			error = EPERM;
777 			goto bad;
778 		}
779 		off = ntohs((u_short)mtod(m, struct ip *)->ip_len) - m->m_len;
780 		if ((ifp->if_flags & IFF_NOTRAILERS) == 0)
781 		if (off > 0 && (off & 0x1ff) == 0 &&
782 		    m->m_off >= MMINOFF + 2 * sizeof (u_short)) {
783 			type = RING_IPTrailer + (off>>9);
784 			m->m_off -= 2 * sizeof (u_short);
785 			m->m_len += 2 * sizeof (u_short);
786 			*mtod(m, u_short *) = RING_IP;
787 			*(mtod(m, u_short *) + 1) = m->m_len;
788 			goto gottrailertype;
789 		}
790 		type = RING_IP;
791 		off = 0;
792 		goto gottype;
793 		}
794 #endif
795 	default:
796 		printf("vv%d: can't handle af%d\n", ifp->if_unit,
797 			dst->sa_family);
798 		error = EAFNOSUPPORT;
799 		goto bad;
800 	}
801 
802 gottrailertype:
803 	/*
804 	 * Packet to be sent as trailer: move first packet
805 	 * (control information) to end of chain.
806 	 */
807 	while (m->m_next)
808 		m = m->m_next;
809 	m->m_next = m0;
810 	m = m0->m_next;
811 	m0->m_next = 0;
812 	m0 = m;
813 gottype:
814 	/*
815 	 * Add local net header.  If no space in first mbuf,
816 	 * allocate another.
817 	 */
818 	if (m->m_off > MMAXOFF ||
819 	    MMINOFF + sizeof (struct vv_header) > m->m_off) {
820 		m = m_get(M_DONTWAIT, MT_HEADER);
821 		if (m == NULL) {
822 			error = ENOBUFS;
823 			goto bad;
824 		}
825 		m->m_next = m0;
826 		m->m_off = MMINOFF;
827 		m->m_len = sizeof (struct vv_header);
828 	} else {
829 		m->m_off -= sizeof (struct vv_header);
830 		m->m_len += sizeof (struct vv_header);
831 	}
832 	vv = mtod(m, struct vv_header *);
833 	vv->vh_shost = ifp->if_host[0];
834 	vv->vh_dhost = dest;
835 	vv->vh_version = RING_VERSION;
836 	vv->vh_type = type;
837 	vv->vh_info = off;
838 	vvtracehdr("vo", vv);
839 
840 	/*
841 	 * Queue message on interface, and start output if interface
842 	 * not yet active.
843 	 */
844 	s = splimp();
845 	if (IF_QFULL(&ifp->if_snd)) {
846 		IF_DROP(&ifp->if_snd);
847 		error = ENOBUFS;
848 		goto qfull;
849 	}
850 	IF_ENQUEUE(&ifp->if_snd, m);
851 	if (vv_softc[ifp->if_unit].vs_oactive == 0)
852 		vvstart(ifp->if_unit);
853 	splx(s);
854 	return (0);
855 qfull:
856 	m0 = m;
857 	splx(s);
858 bad:
859 	m_freem(m0);
860 	return(error);
861 }
862 
863 /*
864  * Process an ioctl request.
865  */
866 vvioctl(ifp, cmd, data)
867 	register struct ifnet *ifp;
868 	int cmd;
869 	caddr_t data;
870 {
871 	struct ifreq *ifr = (struct ifreq *)data;
872 	int s = splimp(), error = 0;
873 
874 	switch (cmd) {
875 
876 	case SIOCSIFADDR:
877 		/* too difficult to change addr while running */
878 		if ((ifp->if_flags & IFF_RUNNING) == 0) {
879 			struct sockaddr_in *sin =
880 			    (struct sockaddr_in *)&ifr->ifr_addr;
881 			ifp->if_net = in_netof(sin->sin_addr);
882 			vvinit(ifp->if_unit);
883 		} else
884 			error = EINVAL;
885 		break;
886 
887 	default:
888 		error = EINVAL;
889 	}
890 	splx(s);
891 	return (error);
892 }
893 
894 /*
895  * vvprt_hdr(s, v) print the local net header in "v"
896  * 	with title is "s"
897  */
898 vvprt_hdr(s, v)
899 	char *s;
900 	register struct vv_header *v;
901 {
902 	printf("%s: dsvti: 0x%x 0x%x 0x%x 0x%x 0x%x\n",
903 		s,
904 		0xff & (int)(v->vh_dhost), 0xff & (int)(v->vh_shost),
905 		0xff & (int)(v->vh_version), 0xff & (int)(v->vh_type),
906 		0xffff & (int)(v->vh_info));
907 }
908 
909 #ifdef notdef
910 /*
911  * print "l" hex bytes starting at "s"
912  */
913 vvprt_hex(s, l)
914 	char *s;
915 	int l;
916 {
917 	register int i;
918 	register int z;
919 
920 	for (i=0 ; i < l; i++) {
921 		z = 0xff & (int)(*(s + i));
922 		printf("%c%c ",
923 		"0123456789abcdef"[(z >> 4) & 0x0f],
924 		"0123456789abcdef"[z & 0x0f]
925 		);
926 	}
927 }
928 #endif
929