now routing code might work
[unix-history] / usr / src / sys / vax / if / if_vv.c
CommitLineData
957fb656 1/* if_vv.c 4.2 82/06/12 */
547e5718
SL
2
3/*
4 * Proteon 10 Meg Ring Driver.
5 * This device is called "vv" because its "real name",
6 * V2LNI won't work if shortened to the obvious "v2".
7 * Hence the subterfuge.
8 */
9#include "../h/param.h"
10#include "../h/systm.h"
11#include "../h/mbuf.h"
12#include "../h/pte.h"
13#include "../h/buf.h"
14#include "../h/protosw.h"
15#include "../h/socket.h"
16#include "../h/ubareg.h"
17#include "../h/ubavar.h"
18#include "../h/cpu.h"
19#include "../h/mtpr.h"
20#include "../h/vmmac.h"
21#include "../net/in.h"
22#include "../net/in_systm.h"
23#include "../net/if.h"
24#include "../net/if_vv.h"
25#include "../net/if_uba.h"
26#include "../net/ip.h"
27#include "../net/ip_var.h"
28#include "../net/route.h"
29
30#include "vv.h"
31#include "imp.h"
32
33/*
34 * N.B. - if WIRECENTER is defined wrong, it can well break
35 * the hardware!!
36 */
37#undef AUTOIDENTIFY
38#define WIRECENTER
39
40#ifdef WIRECENTER
41#define VV_CONF VV_HEN /* drive wire center relay */
42#else
43#define VV_CONF VV_STE /* allow operation without wire center */
44#endif
45
46#define VVMTU (1024+512)
47
48int vvprobe(), vvattach(), vvrint(), vvxint();
49struct uba_device *vvinfo[NVV];
50u_short vvstd[] = { 0 };
51struct uba_driver vvdriver =
52 { vvprobe, 0, vvattach, 0, vvstd, "vv", vvinfo };
53#define VVUNIT(x) minor(x)
54int vvinit(),vvoutput(),vvreset();
55
56/*
57 * Software status of each interface.
58 *
59 * Each interface is referenced by a network interface structure,
60 * vs_if, which the routing code uses to locate the interface.
61 * This structure contains the output queue for the interface, its address, ...
62 * We also have, for each interface, a UBA interface structure, which
63 * contains information about the UNIBUS resources held by the interface:
64 * map registers, buffered data paths, etc. Information is cached in this
65 * structure for use by the if_uba.c routines in running the interface
66 * efficiently.
67 */
68struct vv_softc {
69 struct ifnet vs_if; /* network-visible interface */
70 struct ifuba vs_ifuba; /* UNIBUS resources */
71 short vs_oactive; /* is output active? */
72 short vs_olen; /* length of last output */
73 u_short vs_lastx; /* last destination address */
74 short vs_tries; /* current retry count */
75 short vs_init; /* number of ring inits */
76 short vs_flush; /* number of flushed packets */
77 short vs_nottaken; /* number of packets refused */
78} vv_softc[NVV];
79
80vvprobe(reg)
81 caddr_t reg;
82{
83 register int br, cvec;
84 register struct vvreg *addr = (struct vvreg *)reg;
85
86#ifdef lint
87 br = 0; cvec = br; br = cvec;
88#endif
89 /* reset interface, enable, and wait till dust settles */
90 addr->vvicsr = VV_RST;
91 addr->vvocsr = VV_RST;
92 DELAY(100000);
93 /* generate interrupt by doing 1 word DMA from 0 in uba space!! */
94 addr->vvocsr = VV_IEN; /* enable interrupt */
95 addr->vvoba = 0; /* low 16 bits */
96 addr->vvoea = 0; /* extended bits */
97 addr->vvowc = -1; /* for 1 word */
98 addr->vvocsr |= VV_DEN; /* start the DMA */
99 DELAY(100000);
100 addr->vvocsr = 0;
101 if (cvec && cvec != 0x200)
102 cvec -= 4; /* backup so vector => recieve */
103 return(1);
104}
105
106/*
107 * Interface exists: make available by filling in network interface
108 * record. System will initialize the interface when it is ready
109 * to accept packets.
110 */
111vvattach(ui)
112 struct uba_device *ui;
113{
114 register struct vv_softc *vs = &vv_softc[ui->ui_unit];
115 register struct sockaddr_in *sin;
116COUNT(VVATTACH);
117
118 vs->vs_if.if_unit = ui->ui_unit;
119 vs->vs_if.if_name = "vv";
120 vs->vs_if.if_mtu = VVMTU;
121 vs->vs_if.if_net = ui->ui_flags;
122 vs->vs_if.if_host[0] = 0; /* this will be reset in vvinit() */
123
124 sin = (struct sockaddr_in *)&vs->vs_if.if_addr;
125 sin->sin_family = AF_INET;
126 sin->sin_addr = if_makeaddr(vs->vs_if.if_net, vs->vs_if.if_host[0]);
127
128 sin = (struct sockaddr_in *)&vs->vs_if.if_broadaddr;
129 sin->sin_family = AF_INET;
130 sin->sin_addr = if_makeaddr(vs->vs_if.if_net, VV_BROADCAST);
131 vs->vs_if.if_flags = IFF_BROADCAST;
132
133 vs->vs_if.if_init = vvinit;
134 vs->vs_if.if_output = vvoutput;
135 vs->vs_if.if_ubareset = vvreset;
136 vs->vs_ifuba.ifu_flags = UBA_NEEDBDP | UBA_NEED16;
137 if_attach(&vs->vs_if);
138#if NIMP == 0
139 if (ui->ui_flags & ~0xff)
140 vvlhinit((ui->ui_flags &~ 0xff) | 0x0a);
141#endif
142}
143
144/*
145 * Reset of interface after UNIBUS reset.
146 * If interface is on specified uba, reset its state.
147 */
148vvreset(unit, uban)
149 int unit, uban;
150{
151 register struct uba_device *ui;
152COUNT(VVRESET);
153
154 if (unit >= NVV || (ui = vvinfo[unit]) == 0 || ui->ui_alive == 0 ||
155 ui->ui_ubanum != uban)
156 return;
157 printf(" vv%d", unit);
158 vvinit(unit);
159}
160
161/*
162 * Initialization of interface; clear recorded pending
163 * operations, and reinitialize UNIBUS usage.
164 */
165vvinit(unit)
166 int unit;
167{
168 register struct vv_softc *vs = &vv_softc[unit];
169 register struct uba_device *ui = vvinfo[unit];
170 register struct vvreg *addr;
171 struct sockaddr_in *sin;
172 struct mbuf *m;
173 struct vv_header *v;
174 int ubainfo, retrying, attempts, waitcount, s;
175
176 if (if_ubainit(&vs->vs_ifuba, ui->ui_ubanum,
177 sizeof (struct vv_header), (int)btoc(VVMTU)) == 0) {
178 printf("vv%d: can't initialize\n", unit);
179 return;
180 }
181 addr = (struct vvreg *)ui->ui_addr;
182
183#ifdef AUTOIDENTIFY
184 /*
185 * Build a multicast message to identify our address
186 */
187 attempts = 0; /* total attempts, including bad msg type */
188top:
189 retrying = 0; /* first time through */
190 m = m_get(M_DONTWAIT);
191 if (m == 0)
192 panic("vvinit: can't get mbuf");
193 m->m_next = 0;
194 m->m_off = MMINOFF;
195 m->m_len = sizeof(struct vv_header);
196
197 v = mtod(m, struct vv_header *);
198 v->vh_dhost = 0; /* multicast destination address */
199 v->vh_shost = 0; /* will be overwritten with ours */
200 v->vh_version = RING_VERSION;
201 v->vh_type = RING_WHOAMI;
202 v->vh_info = 0;
203
204 /*
205 * Reset interface, establish Digital Loopback Mode, and
206 * send the multicast (to myself) with Input Copy enabled.
207 */
208retry:
209 ubainfo = vs->vs_ifuba.ifu_r.ifrw_info;
210 addr->vvicsr = VV_RST;
211 addr->vviba = (u_short) ubainfo;
212 addr->vviea = (u_short) (ubainfo >> 16);
213 addr->vviwc = -(sizeof (struct vv_header) + VVMTU) >> 1;
214 addr->vvicsr = VV_STE | VV_DEN | VV_ENB | VV_LPB;
215 /* map xmit message into uba if not already there */
216 if (!retrying)
217 vs->vs_olen = if_wubaput(&vs->vs_ifuba, m);
218 if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP)
219 UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_w.ifrw_bdp);
220 addr->vvocsr = VV_RST | VV_CPB; /* clear packet buffer */
221 ubainfo = vs->vs_ifuba.ifu_w.ifrw_info;
222 addr->vvoba = (u_short) ubainfo;
223 addr->vvoea = (u_short) (ubainfo >> 16);
224 addr->vvowc = -((vs->vs_olen + 1) >> 1);
225 addr->vvocsr = VV_CPB | VV_DEN | VV_INR | VV_ENB;
226
227 /*
228 * Wait for receive side to finish.
229 * Extract source address (which will our own),
230 * and post to interface structure.
231 */
232 DELAY(1000);
233 for (waitcount = 0; ((addr->vvicsr) & VV_RDY) == 0; waitcount++) {
234 if (waitcount < 10)
235 DELAY(1000);
236 else {
237 if (attempts++ < 10)s
238 goto retry;
239 else {
240 printf("vv%d: can't initialize\n", unit);
241 printf("vvinit loopwait: icsr = %b\n",
242 0xffff&(addr->vvicsr),VV_IBITS);
243 return;
244 }
245 }
246 }
247
248 if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP)
249 UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_w.ifrw_bdp);
250 if (vs->vs_ifuba.ifu_xtofree)
251 m_freem(vs->vs_ifuba.ifu_xtofree);
252 if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP)
253 UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_r.ifrw_bdp);
254 m = if_rubaget(&vs->vs_ifuba, sizeof(struct vv_header), 0);
255 if (m)
256 m_freem(m);
257 /*
258 * check message type before we believe the source host address
259 */
260 v = (struct vv_header *)(vs->vs_ifuba.ifu_r.ifrw_addr);
261 if (v->vh_type == RING_WHOAMI)
262 vs->vs_if.if_host[0] = v->vh_shost;
263 else
264 goto top;
265#else
266 vs->vs_if.if_host[0] = 24;
267#endif
268
269 printf("vv%d: host %d\n", unit, vs->vs_if.if_host[0]);
270 sin = (struct sockaddr_in *)&vs->vs_if.if_addr;
271 sin->sin_family = AF_INET;
272 sin->sin_addr =
273 if_makeaddr(vs->vs_if.if_net, vs->vs_if.if_host[0]);
274
275 /*
276 * Reset the interface, and join the ring
277 */
278 addr->vvocsr = VV_RST | VV_CPB; /* clear packet buffer */
279 addr->vvicsr = VV_RST | VV_CONF; /* close logical relay */
280 sleep((caddr_t)&lbolt, PZERO); /* let contacts settle */
281 vs->vs_init = 0;
282 vs->vs_flush = 0;
283 vs->vs_nottaken = 0;
284
285 /*
286 * Hang a receive and start any
287 * pending writes by faking a transmit complete.
288 */
289 s = splimp();
290 ubainfo = vs->vs_ifuba.ifu_r.ifrw_info;
291 addr->vviba = (u_short) ubainfo;
292 addr->vviea = (u_short) (ubainfo >> 16);
293 addr->vviwc = -(sizeof (struct vv_header) + VVMTU) >> 1;
294 addr->vvicsr = VV_IEN | VV_CONF | VV_DEN | VV_ENB;
295 vs->vs_oactive = 1;
296 vvxint(unit);
297 splx(s);
957fb656 298 if_rtinit(&vs->vs_if, RTF_UP);
547e5718
SL
299}
300
301/*
302 * Start or restart output on interface.
303 * If interface is not already active, get another datagram
304 * to send off of the interface queue, and map it to the interface
305 * before starting the output.
306 */
307vvstart(dev)
308 dev_t dev;
309{
310 int unit = VVUNIT(dev);
311 struct uba_device *ui = vvinfo[unit];
312 register struct vv_softc *vs = &vv_softc[unit];
313 register struct vvreg *addr;
314 struct mbuf *m;
315 int ubainfo;
316 int dest;
317COUNT(VVSTART);
318
319 if (vs->vs_oactive)
320 goto restart;
321
322 /*
323 * Not already active: dequeue another request
324 * and map it to the UNIBUS. If no more requests,
325 * just return.
326 */
327 IF_DEQUEUE(&vs->vs_if.if_snd, m);
328 if (m == 0) {
329 vs->vs_oactive = 0;
330 return;
331 }
332 dest = mtod(m, struct vv_header *)->vh_dhost;
333 vs->vs_olen = if_wubaput(&vs->vs_ifuba, m);
334 vs->vs_lastx = dest;
335
336restart:
337 /*
338 * Have request mapped to UNIBUS for transmission.
339 * Purge any stale data from this BDP, and start the otput.
340 */
341 if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP)
342 UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_w.ifrw_bdp);
343 addr = (struct vvreg *)ui->ui_addr;
344 ubainfo = vs->vs_ifuba.ifu_w.ifrw_info;
345 addr->vvoba = (u_short) ubainfo;
346 addr->vvoea = (u_short) (ubainfo >> 16);
347 addr->vvowc = -((vs->vs_olen + 1) >> 1);
348 addr->vvocsr = VV_IEN | VV_CPB | VV_DEN | VV_INR | VV_ENB;
349 vs->vs_oactive = 1;
350}
351
352/*
353 * VVLNI transmit interrupt
354 * Start another output if more data to send.
355 */
356vvxint(unit)
357 int unit;
358{
359 register struct uba_device *ui = vvinfo[unit];
360 register struct vv_softc *vs = &vv_softc[unit];
361 register struct vvreg *addr;
362 register int oc;
363COUNT(ENXINT);
364
365 addr = (struct vvreg *)ui->ui_addr;
366 oc = 0xffff & (addr->vvocsr);
367 if (vs->vs_oactive == 0) {
368 printf("vv%d: stray interrupt vvocsr = %b\n", unit,
369 oc, VV_OBITS);
370 return;
371 }
372 if (oc & (VV_OPT | VV_RFS)) {
373 if (++(vs->vs_tries) < VVRETRY) {
374 if (oc & VV_OPT)
375 vs->vs_init++;
376 if (oc & VV_RFS)
377 vs->vs_nottaken++;
378 addr->vvocsr = VV_IEN | VV_ENB | VV_INR;
379 return;
380 }
381 if (oc & VV_OPT)
382 printf("vv%d: output timeout\n");
383 }
384 vs->vs_if.if_opackets++;
385 vs->vs_oactive = 0;
386 vs->vs_tries = 0;
387 if (oc & VVXERR) {
388 vs->vs_if.if_oerrors++;
389 printf("vv%d: error vvocsr = %b\n", unit, 0xffff & oc,
390 VV_OBITS);
391 }
392 if (vs->vs_ifuba.ifu_xtofree) {
393 m_freem(vs->vs_ifuba.ifu_xtofree);
394 vs->vs_ifuba.ifu_xtofree = 0;
395 }
396 if (vs->vs_if.if_snd.ifq_head == 0) {
397 vs->vs_lastx = 0;
398 return;
399 }
400 vvstart(unit);
401}
402
403/*
404 * V2lni interface receiver interrupt.
405 * If input error just drop packet.
406 * Otherwise purge input buffered data path and examine
407 * packet to determine type. If can't determine length
408 * from type, then have to drop packet. Othewise decapsulate
409 * packet based on type and pass to type specific higher-level
410 * input routine.
411 */
412vvrint(unit)
413 int unit;
414{
415 register struct vv_softc *vs = &vv_softc[unit];
416 struct vvreg *addr = (struct vvreg *)vvinfo[unit]->ui_addr;
417 register struct vv_header *vv;
418 register struct ifqueue *inq;
419 struct mbuf *m;
420 int ubainfo, len, off;
421COUNT(VVRINT);
422
423 vs->vs_if.if_ipackets++;
424 /*
425 * Purge BDP; drop if input error indicated.
426 */
427 if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP)
428 UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_r.ifrw_bdp);
429 if (addr->vvicsr & VVRERR) {
430 vs->vs_if.if_ierrors++;
431 printf("vv%d: error vvicsr = %b\n", unit,
432 0xffff&(addr->vvicsr), VV_IBITS);
433 goto setup;
434 }
435 off = 0;
436 len = 0;
437 vv = (struct vv_header *)(vs->vs_ifuba.ifu_r.ifrw_addr);
438 /*
439 * Demultiplex on packet type and deal with oddities of
440 * trailer protocol format
441 */
442 switch (vv->vh_type) {
443
444#ifdef INET
445 case RING_IP:
446 len = htons((u_short)((struct ip *) vv)->ip_len);
447 schednetisr(NETISR_IP);
448 inq = &ipintrq;
449 break;
450#endif
451 default:
452 printf("vv%d: unknown pkt type 0x%x\n", unit, vv->vh_type);
453 goto setup;
454 }
455 if (len == 0)
456 goto setup;
457 /*
458 * Pull packet off interface. Off is nonzero if packet
459 * has trailing header; if_rubaget will then force this header
460 * information to be at the front, but we still have to drop
461 * the two-byte type which is at the front of any trailer data.
462 */
463 m = if_rubaget(&vs->vs_ifuba, len, off);
464 if (m == 0)
465 goto setup;
466 IF_ENQUEUE(inq, m);
467
468setup:
469 /*
470 * Reset for next packet.
471 */
472 ubainfo = vs->vs_ifuba.ifu_r.ifrw_info;
473 addr->vviba = (u_short) ubainfo;
474 addr->vviea = (u_short) (ubainfo >> 16);
475 addr->vviwc = -(sizeof (struct vv_header) + VVMTU) >> 1;
476 addr->vvicsr = VV_RST | VV_CONF;
477 addr->vvicsr |= VV_IEN | VV_DEN | VV_ENB;
478
479}
480
481/*
482 * V2lni output routine.
483 * Encapsulate a packet of type family for the local net.
484 * Use trailer local net encapsulation if enough data in first
485 * packet leaves a multiple of 512 bytes of data in remainder.
486 */
487vvoutput(ifp, m0, dst)
488 struct ifnet *ifp;
489 struct mbuf *m0;
490 struct sockaddr *dst;
491{
492 register struct mbuf *m = m0;
493 register struct vv_header *vv;
494 int type, dest, s;
495
496 switch (dst->sa_family) {
497
498#ifdef INET
499 case AF_INET: {
500 register struct ip *ip = mtod(m0, struct ip *);
501 int off;
502
503 dest = ip->ip_dst.s_addr >> 24;
504 type = RING_IP;
505 off = 0;
506 goto gottype;
507 }
508#endif
509 default:
510 printf("vv%d: can't handle af%d\n", ifp->if_unit,
511 dst->sa_family);
512 m_freem(m0);
513 return (0);
514 }
515
516gottrailertype:
517 /*
518 * Packet to be sent as trailer: move first packet
519 * (control information) to end of chain.
520 */
521 while (m->m_next)
522 m = m->m_next;
523 m->m_next = m0;
524 m = m0->m_next;
525 m0->m_next = 0;
526 m0 = m;
527
528gottype:
529 /*
530 * Add local net header. If no space in first mbuf,
531 * allocate another.
532 */
533 if (m->m_off > MMAXOFF ||
534 MMINOFF + sizeof (struct vv_header) > m->m_off) {
535 m = m_get(M_DONTWAIT);
536 if (m == 0) {
537 m_freem(m0);
538 return (0);
539 }
540 m->m_next = m0;
541 m->m_off = MMINOFF;
542 m->m_len = sizeof (struct vv_header);
543 } else {
544 m->m_off -= sizeof (struct vv_header);
545 m->m_len += sizeof (struct vv_header);
546 }
547 vv = mtod(m, struct vv_header *);
548 vv->vh_shost = ifp->if_host[0];
549 vv->vh_dhost = dest;
550 vv->vh_version = RING_VERSION;
551 vv->vh_type = type;
552 vv->vh_info = m->m_len;
553
554 /*
555 * Queue message on interface, and start output if interface
556 * not yet active.
557 */
558 s = splimp();
559 IF_ENQUEUE(&ifp->if_snd, m);
560 if (vv_softc[ifp->if_unit].vs_oactive == 0)
561 vvstart(ifp->if_unit);
562 splx(s);
563 return (1);
564}
565
566#ifdef notdef
567/*
568 * vvprt_hdr(s, v) print the local net header in "v"
569 * with title is "s"
570 */
571vvprt_hdr(s, v)
572 char *s;
573 register struct vv_header *v;
574{
575 printf("%s: dsvti: 0x%x 0x%x 0x%x 0x%x 0x%x\n",
576 s,
577 0xff & (int)(v->vh_dhost), 0xff & (int)(v->vh_shost),
578 0xff & (int)(v->vh_version), 0xff & (int)(v->vh_type),
579 0xffff & (int)(v->vh_info));
580}
581
582/*
583 * print "l" hex bytes starting at "s"
584 */
585vvprt_hex(s, l)
586 char *s;
587 int l;
588{
589 register int i;
590 register int z;
591
592 for (i=0 ; i < l; i++) {
593 z = 0xff & (int)(*(s + i));
594 printf("%c%c ",
595 "0123456789abcdef"[(z >> 4) & 0x0f],
596 "0123456789abcdef"[z & 0x0f]
597 );
598 }
599}
600#endif
601
602#if NIMP == 0 && NVV > 0
603/*
604 * Logical host interface driver.
605 * Allows host to appear as an ARPAnet
606 * logical host. Must also have routing
607 * table entry set up to forward packets
608 * to appropriate geteway on localnet.
609 */
610struct ifnet vvlhif;
611int looutput();
612
613/*
614 * Called by localnet interface to allow logical
615 * host interface to "attach".
616 */
617vvlhinit(vvifp, addr)
618 struct ifnet *vvifp;
619 int addr;
620{
621 register struct ifnet *ifp = &vvlhif;
622 register struct sockaddr_in *sin;
623
624COUNT(VVLHINIT);
625 ifp->if_name = "lh";
626 ifp->if_mtu = VVMTU;
627 sin = (struct sockaddr_in *)&ifp->if_addr;
628 sin->sin_family = AF_INET;
629 sin->sin_addr.s_addr = addr;
630 ifp->if_net = netpart(sin->sin_addr);
631 ifp->if_flags = IFF_UP;
632 ifp->if_output = looutput;
633 if_attach(ifp);
957fb656 634 rtinit(&ifp->if_addr, &ifp->if_addr, RTF_UP|RTF_HOST);
547e5718
SL
635}
636#endif