1*11192Ssam /* if_vv.c 4.13 83/02/20 */ 27024Ssam 39799Ssam #include "vv.h" 4*11192Ssam 57024Ssam /* 67024Ssam * Proteon 10 Meg Ring Driver. 77024Ssam * This device is called "vv" because its "real name", 87024Ssam * V2LNI won't work if shortened to the obvious "v2". 97024Ssam * Hence the subterfuge. 10*11192Ssam * 11*11192Ssam * MUST BE UPDATE FOR 4.1C 127024Ssam */ 13*11192Ssam #include "../h/pte.h" 149799Ssam 157024Ssam #include "../h/param.h" 167024Ssam #include "../h/systm.h" 177024Ssam #include "../h/mbuf.h" 187024Ssam #include "../h/buf.h" 197024Ssam #include "../h/protosw.h" 207024Ssam #include "../h/socket.h" 21*11192Ssam #include "../h/cpu.h" 22*11192Ssam #include "../h/mtpr.h" 237024Ssam #include "../h/vmmac.h" 24*11192Ssam #include "../h/errno.h" 258465Sroot 268465Sroot #include "../net/if.h" 278465Sroot #include "../net/route.h" 28*11192Ssam 298421Swnj #include "../netinet/in.h" 308421Swnj #include "../netinet/in_systm.h" 318421Swnj #include "../netinet/ip.h" 328421Swnj #include "../netinet/ip_var.h" 338465Sroot 348421Swnj #include "../vaxif/if_vv.h" 358421Swnj #include "../vaxif/if_uba.h" 36*11192Ssam 378465Sroot #include "../vaxuba/ubareg.h" 388465Sroot #include "../vaxuba/ubavar.h" 397024Ssam 407024Ssam /* 417024Ssam * N.B. - if WIRECENTER is defined wrong, it can well break 427024Ssam * the hardware!! 437024Ssam */ 447640Ssam 457024Ssam #define WIRECENTER 467024Ssam 477024Ssam #ifdef WIRECENTER 487024Ssam #define VV_CONF VV_HEN /* drive wire center relay */ 497024Ssam #else 507024Ssam #define VV_CONF VV_STE /* allow operation without wire center */ 517024Ssam #endif 527024Ssam 537024Ssam #define VVMTU (1024+512) 547640Ssam #define VVMRU (1024+512+16) /* space for trailer */ 557024Ssam 56*11192Ssam int vv_dotrailer = 0, /* 1 => do trailer protocol */ 57*11192Ssam vv_tracehdr = 0, /* 1 => trace headers (slowly!!) */ 58*11192Ssam vv_tracetimeout = 1; /* 1 => trace input error-rate limiting */ 59*11192Ssam vv_logreaderrors = 0; /* 1 => log all read errors */ 607640Ssam 61*11192Ssam #define vvtracehdr if (vv_tracehdr) vvprt_hdr 62*11192Ssam #define vvtrprintf if (vv_tracetimeout) printf 63*11192Ssam 64*11192Ssam int vv_ticking = 0; /* error flywheel is running */ 65*11192Ssam 66*11192Ssam #define VV_FLYWHEEL 3 /* interval in HZ - 50 msec. 67*11192Ssam N.B. all times below are 68*11192Ssam in units of flywheel ticks */ 69*11192Ssam #define VV_ERRORTHRESHOLD 100 /* errors/flywheel-interval */ 70*11192Ssam #define VV_MODE1ATTEMPTS 10 /* number mode 1 retries */ 71*11192Ssam #define VV_MODE1DELAY 2 /* period interface is PAUSEd - 100ms */ 72*11192Ssam #define VV_MODE2DELAY 4 /* base interval host relay is off - 200ms */ 73*11192Ssam #define VV_MAXDELAY 6400 /* max interval host relay is off - 2 minutes */ 74*11192Ssam 757024Ssam int vvprobe(), vvattach(), vvrint(), vvxint(); 767024Ssam struct uba_device *vvinfo[NVV]; 777024Ssam u_short vvstd[] = { 0 }; 787024Ssam struct uba_driver vvdriver = 797024Ssam { vvprobe, 0, vvattach, 0, vvstd, "vv", vvinfo }; 807024Ssam #define VVUNIT(x) minor(x) 817024Ssam int vvinit(),vvoutput(),vvreset(); 827024Ssam 837024Ssam /* 847024Ssam * Software status of each interface. 857024Ssam * 867024Ssam * Each interface is referenced by a network interface structure, 877024Ssam * vs_if, which the routing code uses to locate the interface. 887024Ssam * This structure contains the output queue for the interface, its address, ... 897024Ssam * We also have, for each interface, a UBA interface structure, which 907024Ssam * contains information about the UNIBUS resources held by the interface: 917024Ssam * map registers, buffered data paths, etc. Information is cached in this 927024Ssam * structure for use by the if_uba.c routines in running the interface 937024Ssam * efficiently. 947024Ssam */ 957024Ssam struct vv_softc { 967024Ssam struct ifnet vs_if; /* network-visible interface */ 977024Ssam struct ifuba vs_ifuba; /* UNIBUS resources */ 98*11192Ssam short vs_oactive; /* is output active */ 99*11192Ssam short vs_iactive; /* is input active */ 1007024Ssam short vs_olen; /* length of last output */ 1017024Ssam u_short vs_lastx; /* last destination address */ 102*11192Ssam short vs_tries; /* transmit current retry count */ 1037024Ssam short vs_init; /* number of ring inits */ 1047024Ssam short vs_nottaken; /* number of packets refused */ 105*11192Ssam /* input error rate limiting state */ 106*11192Ssam short vs_major; /* recovery major state */ 107*11192Ssam short vs_minor; /* recovery minor state */ 108*11192Ssam short vs_retry; /* recovery retry count */ 109*11192Ssam short vs_delayclock; /* recovery delay clock */ 110*11192Ssam short vs_delayrange; /* increasing delay interval */ 111*11192Ssam short vs_dropped; /* number of packes tossed in last dt */ 1127024Ssam } vv_softc[NVV]; 1137024Ssam 114*11192Ssam /* 115*11192Ssam * states of vs_iactive 116*11192Ssam */ 117*11192Ssam 118*11192Ssam #define ACTIVE 1 /* interface should post new receives */ 119*11192Ssam #define PAUSE 0 /* interface should NOT post new receives */ 120*11192Ssam #define OPEN -1 /* PAUSE and open host relay */ 121*11192Ssam 122*11192Ssam /* 123*11192Ssam * recovery major states 124*11192Ssam */ 125*11192Ssam 126*11192Ssam #define MODE0 0 /* everything is wonderful */ 127*11192Ssam #define MODE1 1 /* hopefully whatever will go away */ 128*11192Ssam #define MODE2 2 /* drastic measures - open host relay 129*11192Ssam for increasing intervals */ 130*11192Ssam 1317024Ssam vvprobe(reg) 1327024Ssam caddr_t reg; 1337024Ssam { 1347024Ssam register int br, cvec; 1357024Ssam register struct vvreg *addr = (struct vvreg *)reg; 1367024Ssam 1377024Ssam #ifdef lint 1387024Ssam br = 0; cvec = br; br = cvec; 1397024Ssam #endif 1407024Ssam /* reset interface, enable, and wait till dust settles */ 1417024Ssam addr->vvicsr = VV_RST; 1427024Ssam addr->vvocsr = VV_RST; 1437024Ssam DELAY(100000); 1447024Ssam /* generate interrupt by doing 1 word DMA from 0 in uba space!! */ 1457024Ssam addr->vvocsr = VV_IEN; /* enable interrupt */ 1467024Ssam addr->vvoba = 0; /* low 16 bits */ 1477024Ssam addr->vvoea = 0; /* extended bits */ 1487024Ssam addr->vvowc = -1; /* for 1 word */ 1497024Ssam addr->vvocsr |= VV_DEN; /* start the DMA */ 1507024Ssam DELAY(100000); 1517024Ssam addr->vvocsr = 0; 1527024Ssam if (cvec && cvec != 0x200) 1537024Ssam cvec -= 4; /* backup so vector => recieve */ 1547024Ssam return(1); 1557024Ssam } 1567024Ssam 1577024Ssam /* 1587024Ssam * Interface exists: make available by filling in network interface 1597024Ssam * record. System will initialize the interface when it is ready 1607024Ssam * to accept packets. 1617024Ssam */ 1627024Ssam vvattach(ui) 1637024Ssam struct uba_device *ui; 1647024Ssam { 1657024Ssam register struct vv_softc *vs = &vv_softc[ui->ui_unit]; 1667024Ssam register struct sockaddr_in *sin; 1677024Ssam 1687024Ssam vs->vs_if.if_unit = ui->ui_unit; 1697024Ssam vs->vs_if.if_name = "vv"; 1707024Ssam vs->vs_if.if_mtu = VVMTU; 1717024Ssam vs->vs_if.if_net = ui->ui_flags; 1727024Ssam vs->vs_if.if_host[0] = 0; /* this will be reset in vvinit() */ 1737024Ssam 1747024Ssam sin = (struct sockaddr_in *)&vs->vs_if.if_addr; 1757024Ssam sin->sin_family = AF_INET; 1767024Ssam sin->sin_addr = if_makeaddr(vs->vs_if.if_net, vs->vs_if.if_host[0]); 1777024Ssam 1787024Ssam sin = (struct sockaddr_in *)&vs->vs_if.if_broadaddr; 1797024Ssam sin->sin_family = AF_INET; 1807024Ssam sin->sin_addr = if_makeaddr(vs->vs_if.if_net, VV_BROADCAST); 1817024Ssam vs->vs_if.if_flags = IFF_BROADCAST; 1827024Ssam 1837024Ssam vs->vs_if.if_init = vvinit; 1847024Ssam vs->vs_if.if_output = vvoutput; 185*11192Ssam vs->vs_if.if_ubareset = vvreset; 1867640Ssam vs->vs_ifuba.ifu_flags = UBA_CANTWAIT | UBA_NEEDBDP | UBA_NEED16; 1877024Ssam if_attach(&vs->vs_if); 1887024Ssam } 1897024Ssam 1907024Ssam /* 1917024Ssam * Reset of interface after UNIBUS reset. 1927024Ssam * If interface is on specified uba, reset its state. 1937024Ssam */ 1947024Ssam vvreset(unit, uban) 1957024Ssam int unit, uban; 1967024Ssam { 1977024Ssam register struct uba_device *ui; 1987024Ssam 1997024Ssam if (unit >= NVV || (ui = vvinfo[unit]) == 0 || ui->ui_alive == 0 || 2007024Ssam ui->ui_ubanum != uban) 2017024Ssam return; 2027024Ssam printf(" vv%d", unit); 2037024Ssam vvinit(unit); 2047024Ssam } 2057024Ssam 2067024Ssam /* 2077024Ssam * Initialization of interface; clear recorded pending 2087024Ssam * operations, and reinitialize UNIBUS usage. 2097024Ssam */ 2107024Ssam vvinit(unit) 2117024Ssam int unit; 2127024Ssam { 2137024Ssam register struct vv_softc *vs = &vv_softc[unit]; 2147024Ssam register struct uba_device *ui = vvinfo[unit]; 2157024Ssam register struct vvreg *addr; 2167024Ssam struct sockaddr_in *sin; 2177640Ssam int ubainfo, s; 218*11192Ssam int vvtimeout(); 2197024Ssam 2207640Ssam addr = (struct vvreg *)ui->ui_addr; 2217024Ssam if (if_ubainit(&vs->vs_ifuba, ui->ui_ubanum, 2227024Ssam sizeof (struct vv_header), (int)btoc(VVMTU)) == 0) { 2237024Ssam printf("vv%d: can't initialize\n", unit); 2247640Ssam vs->vs_if.if_flags &= ~IFF_UP; 2257024Ssam return; 2267024Ssam } 2277024Ssam 228*11192Ssam if (vv_ticking++ == 0) timeout(vvtimeout, (caddr_t) 0, VV_FLYWHEEL); 229*11192Ssam 2307024Ssam /* 2317640Ssam * discover our host address and post it 2327640Ssam */ 2337640Ssam 2347640Ssam vs->vs_if.if_host[0] = vvidentify(unit); 2357640Ssam printf("vv%d: host %d\n", unit, vs->vs_if.if_host[0]); 2367640Ssam sin = (struct sockaddr_in *)&vs->vs_if.if_addr; 2377640Ssam sin->sin_family = AF_INET; 2387640Ssam sin->sin_addr = 2397640Ssam if_makeaddr(vs->vs_if.if_net, vs->vs_if.if_host[0]); 2407640Ssam 2417640Ssam /* 2427640Ssam * Reset the interface, and join the ring 2437640Ssam */ 2447640Ssam addr->vvocsr = VV_RST | VV_CPB; /* clear packet buffer */ 2457640Ssam addr->vvicsr = VV_RST | VV_CONF; /* close logical relay */ 2467640Ssam sleep((caddr_t)&lbolt, PZERO); /* let contacts settle */ 2477640Ssam vs->vs_init = 0; 248*11192Ssam vs->vs_dropped = 0; 2497640Ssam vs->vs_nottaken = 0; 2507640Ssam 2517640Ssam /* 2527640Ssam * Hang a receive and start any 2537640Ssam * pending writes by faking a transmit complete. 2547640Ssam */ 2557640Ssam s = splimp(); 2567640Ssam ubainfo = vs->vs_ifuba.ifu_r.ifrw_info; 2577640Ssam addr->vviba = (u_short) ubainfo; 2587640Ssam addr->vviea = (u_short) (ubainfo >> 16); 2597640Ssam addr->vviwc = -(sizeof (struct vv_header) + VVMTU) >> 1; 2607640Ssam addr->vvicsr = VV_IEN | VV_CONF | VV_DEN | VV_ENB; 261*11192Ssam vs->vs_iactive = ACTIVE; 2627640Ssam vs->vs_oactive = 1; 2637640Ssam vs->vs_if.if_flags |= IFF_UP; 2647640Ssam vvxint(unit); 2657640Ssam splx(s); 2667640Ssam if_rtinit(&vs->vs_if, RTF_UP); 2677640Ssam } 2687640Ssam 2697640Ssam /* 2707640Ssam * vvidentify() - return our host address 2717640Ssam */ 272*11192Ssam 273*11192Ssam vvidentify(unit) { 274*11192Ssam 2757640Ssam register struct vv_softc *vs = &vv_softc[unit]; 2767640Ssam register struct uba_device *ui = vvinfo[unit]; 2777640Ssam register struct vvreg *addr; 2787640Ssam struct mbuf *m; 2797640Ssam struct vv_header *v; 2807640Ssam int ubainfo, retrying, attempts, waitcount, s; 2817640Ssam 2827640Ssam /* 2837024Ssam * Build a multicast message to identify our address 2847024Ssam */ 285*11192Ssam 2867640Ssam addr = (struct vvreg *)ui->ui_addr; 287*11192Ssam 2887024Ssam attempts = 0; /* total attempts, including bad msg type */ 2897024Ssam retrying = 0; /* first time through */ 290*11192Ssam m = m_get(M_DONTWAIT); 291*11192Ssam if (m == 0) 292*11192Ssam panic("vvinit: can't get mbuf"); 293*11192Ssam m->m_next = 0; 2947024Ssam m->m_off = MMINOFF; 2957024Ssam m->m_len = sizeof(struct vv_header); 2967024Ssam 2977024Ssam v = mtod(m, struct vv_header *); 298*11192Ssam v->vh_dhost = VV_BROADCAST; /* multicast destination address */ 2997024Ssam v->vh_shost = 0; /* will be overwritten with ours */ 3007024Ssam v->vh_version = RING_VERSION; 3017024Ssam v->vh_type = RING_WHOAMI; 3027024Ssam v->vh_info = 0; 303*11192Ssam 304*11192Ssam /* map xmit message into uba */ 305*11192Ssam 3067640Ssam vs->vs_olen = if_wubaput(&vs->vs_ifuba, m); 3077640Ssam if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP) 3087640Ssam UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_w.ifrw_bdp); 3097024Ssam 3107024Ssam /* 3117024Ssam * Reset interface, establish Digital Loopback Mode, and 3127024Ssam * send the multicast (to myself) with Input Copy enabled. 3137024Ssam */ 3147024Ssam retry: 3157024Ssam ubainfo = vs->vs_ifuba.ifu_r.ifrw_info; 3167024Ssam addr->vvicsr = VV_RST; 3177024Ssam addr->vviba = (u_short) ubainfo; 3187024Ssam addr->vviea = (u_short) (ubainfo >> 16); 3197024Ssam addr->vviwc = -(sizeof (struct vv_header) + VVMTU) >> 1; 3207024Ssam addr->vvicsr = VV_STE | VV_DEN | VV_ENB | VV_LPB; 3217640Ssam 3227640Ssam /* let flag timers fire so ring will initialize */ 323*11192Ssam 3247640Ssam sleep((caddr_t) &lbolt, PZERO); 3257640Ssam sleep((caddr_t) &lbolt, PZERO); 3267640Ssam 3277024Ssam addr->vvocsr = VV_RST | VV_CPB; /* clear packet buffer */ 3287024Ssam ubainfo = vs->vs_ifuba.ifu_w.ifrw_info; 3297024Ssam addr->vvoba = (u_short) ubainfo; 3307024Ssam addr->vvoea = (u_short) (ubainfo >> 16); 3317024Ssam addr->vvowc = -((vs->vs_olen + 1) >> 1); 3327024Ssam addr->vvocsr = VV_CPB | VV_DEN | VV_INR | VV_ENB; 3337024Ssam 3347024Ssam /* 3357024Ssam * Wait for receive side to finish. 336*11192Ssam * Extract source address (which will our own), 3377024Ssam * and post to interface structure. 3387024Ssam */ 3397024Ssam DELAY(1000); 340*11192Ssam for (waitcount = 0; (addr->vvicsr & VV_RDY) == 0; waitcount++) { 3419876Ssam if (waitcount < 10) { 3427024Ssam DELAY(1000); 343*11192Ssam } 344*11192Ssam else { 345*11192Ssam if (attempts++ < 10){ 3467024Ssam goto retry; 347*11192Ssam } 3487024Ssam else { 3497024Ssam printf("vv%d: can't initialize\n", unit); 3507024Ssam printf("vvinit loopwait: icsr = %b\n", 3517024Ssam 0xffff&(addr->vvicsr),VV_IBITS); 3527640Ssam vs->vs_if.if_flags &= ~IFF_UP; 353*11192Ssam return; 3547024Ssam } 3557024Ssam } 356*11192Ssam } 357*11192Ssam 3587024Ssam if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP) 3597024Ssam UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_w.ifrw_bdp); 3607024Ssam if (vs->vs_ifuba.ifu_xtofree) 3617024Ssam m_freem(vs->vs_ifuba.ifu_xtofree); 3627024Ssam if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP) 3637024Ssam UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_r.ifrw_bdp); 3647024Ssam m = if_rubaget(&vs->vs_ifuba, sizeof(struct vv_header), 0); 3657024Ssam if (m) 3667024Ssam m_freem(m); 3677024Ssam /* 3687024Ssam * check message type before we believe the source host address 3697024Ssam */ 3707024Ssam v = (struct vv_header *)(vs->vs_ifuba.ifu_r.ifrw_addr); 371*11192Ssam if (v->vh_type == RING_WHOAMI) 372*11192Ssam return(v->vh_shost); 373*11192Ssam else 3747640Ssam goto retry; 3757024Ssam } 3767024Ssam 3777024Ssam /* 378*11192Ssam * vvtimeout() - called by timer flywheel to monitor input packet 379*11192Ssam * discard rate. Interfaces getting too many errors are shut 380*11192Ssam * down for a while. If the condition persists, the interface 381*11192Ssam * is marked down. 382*11192Ssam */ 383*11192Ssam 384*11192Ssam vvtimeout(junk) 385*11192Ssam int junk; 386*11192Ssam { 387*11192Ssam register struct vv_softc *vs; 388*11192Ssam register int i; 389*11192Ssam register struct vvreg *addr; 390*11192Ssam int ubainfo; 391*11192Ssam 392*11192Ssam timeout(vvtimeout, (caddr_t) 0, VV_FLYWHEEL); 393*11192Ssam 394*11192Ssam for (i=0; i<NVV; i++) { 395*11192Ssam vs = &vv_softc[i]; 396*11192Ssam addr = (struct vvreg *)vvinfo[i]->ui_addr; 397*11192Ssam if (vs->vs_if.if_flags & IFF_UP == 0) continue; 398*11192Ssam switch (vs->vs_major) { 399*11192Ssam 400*11192Ssam /* 401*11192Ssam * MODE0: generally OK, just check error rate 402*11192Ssam */ 403*11192Ssam 404*11192Ssam case MODE0: 405*11192Ssam if (vs->vs_dropped < VV_ERRORTHRESHOLD) { 406*11192Ssam vs->vs_dropped = 0; 407*11192Ssam continue; 408*11192Ssam } 409*11192Ssam else { 410*11192Ssam /* suspend reads for a while */ 411*11192Ssam vvtrprintf("vv%d going MODE1 in vvtimeout\n",i); 412*11192Ssam vs->vs_major = MODE1; 413*11192Ssam vs->vs_iactive = PAUSE; /* no new reads */ 414*11192Ssam vs->vs_retry = VV_MODE1ATTEMPTS; 415*11192Ssam vs->vs_delayclock = VV_MODE1DELAY; 416*11192Ssam vs->vs_minor = 0; 417*11192Ssam continue; 418*11192Ssam } 419*11192Ssam 420*11192Ssam /* 421*11192Ssam * MODE1: excessive error rate observed 422*11192Ssam * Scheme: try simply suspending reads for a 423*11192Ssam * short while a small number of times 424*11192Ssam */ 425*11192Ssam 426*11192Ssam case MODE1: 427*11192Ssam if (vs->vs_delayclock > 0) { 428*11192Ssam vs->vs_delayclock--; 429*11192Ssam continue; 430*11192Ssam } 431*11192Ssam switch (vs->vs_minor) { 432*11192Ssam case 0: /* reenable reads */ 433*11192Ssam vvtrprintf("vv%d M1m0\n",i); 434*11192Ssam vs->vs_dropped = 0; 435*11192Ssam vs->vs_iactive = ACTIVE; 436*11192Ssam vs->vs_minor = 1; /* next state */ 437*11192Ssam ubainfo = vs->vs_ifuba.ifu_r.ifrw_info; 438*11192Ssam addr->vviba = (u_short) ubainfo; 439*11192Ssam addr->vviea = (u_short) (ubainfo >> 16); 440*11192Ssam addr->vviwc = -(sizeof (struct vv_header) + VVMTU) >> 1; 441*11192Ssam addr->vvicsr = VV_RST | VV_CONF; 442*11192Ssam addr->vvicsr |= VV_IEN | VV_DEN | VV_ENB; 443*11192Ssam continue; 444*11192Ssam case 1: /* see if it worked */ 445*11192Ssam vvtrprintf("vv%d M1m1\n",i); 446*11192Ssam if (vs->vs_dropped < VV_ERRORTHRESHOLD) { 447*11192Ssam vs->vs_dropped = 0; 448*11192Ssam vs->vs_major = MODE0; /* yeah!! */ 449*11192Ssam continue; 450*11192Ssam } 451*11192Ssam else { 452*11192Ssam if (vs->vs_retry -- > 0) { 453*11192Ssam vs->vs_dropped = 0; 454*11192Ssam vs->vs_iactive = PAUSE; 455*11192Ssam vs->vs_delayclock = VV_MODE1DELAY; 456*11192Ssam vs->vs_minor = 0; /* recheck */ 457*11192Ssam continue; 458*11192Ssam } 459*11192Ssam else { 460*11192Ssam vs->vs_major = MODE2; 461*11192Ssam vs->vs_minor = 0; 462*11192Ssam vs->vs_dropped = 0; 463*11192Ssam vs->vs_iactive = OPEN; 464*11192Ssam vs->vs_delayrange = VV_MODE2DELAY; 465*11192Ssam vs->vs_delayclock = VV_MODE2DELAY; 466*11192Ssam } 467*11192Ssam } 468*11192Ssam } 469*11192Ssam 470*11192Ssam /* 471*11192Ssam * MODE2: simply ignoring traffic didn't relieve condition 472*11192Ssam * Scheme: open host relay for intervals linearly 473*11192Ssam * increasing up to some maximum of a several minutes. 474*11192Ssam * This allows broken networks to return to operation 475*11192Ssam * without rebooting. 476*11192Ssam */ 477*11192Ssam 478*11192Ssam case MODE2: 479*11192Ssam if (vs->vs_delayclock > 0) { 480*11192Ssam vs->vs_delayclock--; 481*11192Ssam continue; 482*11192Ssam } 483*11192Ssam switch (vs->vs_minor) { 484*11192Ssam case 0: /* close relay and reenable reads */ 485*11192Ssam vvtrprintf("vv%d M2m0\n",i); 486*11192Ssam vs->vs_dropped = 0; 487*11192Ssam vs->vs_iactive = ACTIVE; 488*11192Ssam vs->vs_minor = 1; /* next state */ 489*11192Ssam ubainfo = vs->vs_ifuba.ifu_r.ifrw_info; 490*11192Ssam addr->vviba = (u_short) ubainfo; 491*11192Ssam addr->vviea = (u_short) (ubainfo >> 16); 492*11192Ssam addr->vviwc = -(sizeof (struct vv_header) + VVMTU) >> 1; 493*11192Ssam addr->vvicsr = VV_RST | VV_CONF; 494*11192Ssam addr->vvicsr |= VV_IEN | VV_DEN | VV_ENB; 495*11192Ssam continue; 496*11192Ssam case 1: /* see if it worked */ 497*11192Ssam vvtrprintf("vv%d M2m1\n",i); 498*11192Ssam if (vs->vs_dropped < VV_ERRORTHRESHOLD) { 499*11192Ssam vs->vs_dropped = 0; 500*11192Ssam vs->vs_major = MODE0; /* yeah!! */ 501*11192Ssam continue; 502*11192Ssam } 503*11192Ssam else { 504*11192Ssam vvtrprintf("vv%d M2m1 ++ delay\n",i); 505*11192Ssam vs->vs_dropped = 0; 506*11192Ssam vs->vs_iactive = OPEN; 507*11192Ssam vs->vs_minor = 0; 508*11192Ssam if (vs->vs_delayrange < VV_MAXDELAY) 509*11192Ssam vs->vs_delayrange += (vs->vs_delayrange/2); 510*11192Ssam vs->vs_delayclock = vs->vs_delayrange; 511*11192Ssam continue; 512*11192Ssam } 513*11192Ssam } 514*11192Ssam 515*11192Ssam 516*11192Ssam default: 517*11192Ssam printf("vv%d: major state screwed\n", i); 518*11192Ssam vs->vs_if.if_flags &= ~IFF_UP; 519*11192Ssam } 520*11192Ssam } 521*11192Ssam } 522*11192Ssam 523*11192Ssam /* 5247024Ssam * Start or restart output on interface. 525*11192Ssam * If interface is active, this is a retransmit, so just 526*11192Ssam * restuff registers and go. 5277024Ssam * If interface is not already active, get another datagram 5287024Ssam * to send off of the interface queue, and map it to the interface 5297024Ssam * before starting the output. 5307024Ssam */ 5317024Ssam vvstart(dev) 5327024Ssam dev_t dev; 5337024Ssam { 5347024Ssam int unit = VVUNIT(dev); 5357024Ssam struct uba_device *ui = vvinfo[unit]; 5367024Ssam register struct vv_softc *vs = &vv_softc[unit]; 5377024Ssam register struct vvreg *addr; 5387024Ssam struct mbuf *m; 539*11192Ssam int ubainfo; 540*11192Ssam int dest; 5417024Ssam 5427024Ssam if (vs->vs_oactive) 5437024Ssam goto restart; 544*11192Ssam 5457024Ssam /* 5467024Ssam * Not already active: dequeue another request 5477024Ssam * and map it to the UNIBUS. If no more requests, 5487024Ssam * just return. 5497024Ssam */ 5507024Ssam IF_DEQUEUE(&vs->vs_if.if_snd, m); 5517024Ssam if (m == 0) { 5527024Ssam vs->vs_oactive = 0; 5537024Ssam return; 5547024Ssam } 5557024Ssam dest = mtod(m, struct vv_header *)->vh_dhost; 5567024Ssam vs->vs_olen = if_wubaput(&vs->vs_ifuba, m); 5577024Ssam vs->vs_lastx = dest; 5587024Ssam 5597024Ssam restart: 560*11192Ssam 5617024Ssam /* 5627024Ssam * Have request mapped to UNIBUS for transmission. 5637024Ssam * Purge any stale data from this BDP, and start the otput. 5647024Ssam */ 565*11192Ssam 566*11192Ssam if (vs->vs_olen > VVMTU) { 567*11192Ssam printf("vv%d vs_olen: %d > VVMTU\n", unit, vs->vs_olen); 568*11192Ssam panic("vvdriver vs_olen botch"); 569*11192Ssam } 5707024Ssam if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP) 5717024Ssam UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_w.ifrw_bdp); 5727024Ssam addr = (struct vvreg *)ui->ui_addr; 5737024Ssam ubainfo = vs->vs_ifuba.ifu_w.ifrw_info; 5747024Ssam addr->vvoba = (u_short) ubainfo; 5757024Ssam addr->vvoea = (u_short) (ubainfo >> 16); 5767024Ssam addr->vvowc = -((vs->vs_olen + 1) >> 1); 5777024Ssam addr->vvocsr = VV_IEN | VV_CPB | VV_DEN | VV_INR | VV_ENB; 5787024Ssam vs->vs_oactive = 1; 5797024Ssam } 5807024Ssam 5817024Ssam /* 5827024Ssam * VVLNI transmit interrupt 5837024Ssam * Start another output if more data to send. 5847024Ssam */ 5857024Ssam vvxint(unit) 5867024Ssam int unit; 5877024Ssam { 5887024Ssam register struct uba_device *ui = vvinfo[unit]; 5897024Ssam register struct vv_softc *vs = &vv_softc[unit]; 5907024Ssam register struct vvreg *addr; 5917024Ssam register int oc; 5927024Ssam 5937024Ssam addr = (struct vvreg *)ui->ui_addr; 5947024Ssam oc = 0xffff & (addr->vvocsr); 5957024Ssam if (vs->vs_oactive == 0) { 596*11192Ssam printf("vv%d: stray interrupt vvocsr = %b\n", unit, 5977024Ssam oc, VV_OBITS); 5987024Ssam return; 5997024Ssam } 6007024Ssam if (oc & (VV_OPT | VV_RFS)) { 6017640Ssam vs->vs_if.if_collisions++; 602*11192Ssam if (vs->vs_tries++ < VVRETRY) { 6037024Ssam if (oc & VV_OPT) 6047024Ssam vs->vs_init++; 6057024Ssam if (oc & VV_RFS) 6067024Ssam vs->vs_nottaken++; 607*11192Ssam vvstart(unit); /* restart this message */ 6087024Ssam return; 6097024Ssam } 6107024Ssam if (oc & VV_OPT) 6117024Ssam printf("vv%d: output timeout\n"); 6127024Ssam } 6137024Ssam vs->vs_if.if_opackets++; 6147024Ssam vs->vs_oactive = 0; 6157024Ssam vs->vs_tries = 0; 6167024Ssam if (oc & VVXERR) { 6177024Ssam vs->vs_if.if_oerrors++; 618*11192Ssam printf("vv%d: error vvocsr = %b\n", unit, 0xffff & oc, 6197024Ssam VV_OBITS); 6207024Ssam } 6217024Ssam if (vs->vs_ifuba.ifu_xtofree) { 6227024Ssam m_freem(vs->vs_ifuba.ifu_xtofree); 6237024Ssam vs->vs_ifuba.ifu_xtofree = 0; 6247024Ssam } 6257024Ssam if (vs->vs_if.if_snd.ifq_head == 0) { 626*11192Ssam vs->vs_lastx = 256; /* an invalid address */ 6277024Ssam return; 6287024Ssam } 6297024Ssam vvstart(unit); 6307024Ssam } 6317024Ssam 6327024Ssam /* 6337024Ssam * V2lni interface receiver interrupt. 6347024Ssam * If input error just drop packet. 6357024Ssam * Otherwise purge input buffered data path and examine 6367024Ssam * packet to determine type. If can't determine length 6377024Ssam * from type, then have to drop packet. Othewise decapsulate 6387024Ssam * packet based on type and pass to type specific higher-level 6397024Ssam * input routine. 6407024Ssam */ 6417024Ssam vvrint(unit) 6427024Ssam int unit; 6437024Ssam { 6447024Ssam register struct vv_softc *vs = &vv_softc[unit]; 6457024Ssam struct vvreg *addr = (struct vvreg *)vvinfo[unit]->ui_addr; 6467024Ssam register struct vv_header *vv; 6477024Ssam register struct ifqueue *inq; 6487024Ssam struct mbuf *m; 6497024Ssam int ubainfo, len, off; 6507640Ssam short resid; 6517024Ssam 6527024Ssam vs->vs_if.if_ipackets++; 653*11192Ssam 6547024Ssam /* 6557024Ssam * Purge BDP; drop if input error indicated. 6567024Ssam */ 657*11192Ssam 6587024Ssam if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP) 6597024Ssam UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_r.ifrw_bdp); 6607024Ssam if (addr->vvicsr & VVRERR) { 661*11192Ssam if (vv_logreaderrors) 662*11192Ssam printf("vv%d: error vvicsr = %b\n", unit, 663*11192Ssam 0xffff&(addr->vvicsr), VV_IBITS); 6647640Ssam goto dropit; 6657024Ssam } 6667640Ssam 6677024Ssam /* 6687640Ssam * Get packet length from word count residue 6697640Ssam * 6707640Ssam * Compute header offset if trailer protocol 6717640Ssam * 6727640Ssam * Pull packet off interface. Off is nonzero if packet 6737640Ssam * has trailing header; if_rubaget will then force this header 6747640Ssam * information to be at the front. The vh_info field 6757640Ssam * carries the offset to the trailer data in trailer 6767640Ssam * format packets. 6777024Ssam */ 678*11192Ssam 6797640Ssam vv = (struct vv_header *)(vs->vs_ifuba.ifu_r.ifrw_addr); 680*11192Ssam 681*11192Ssam vvtracehdr("vi", vv); 682*11192Ssam 6837640Ssam resid = addr->vviwc; 6847640Ssam if (resid) 6857640Ssam resid |= 0176000; /* ugly!!!! */ 6867640Ssam len = (((sizeof (struct vv_header) + VVMRU) >> 1) + resid) << 1; 6877640Ssam len -= sizeof(struct vv_header); 688*11192Ssam if (len > VVMRU || len <= 0) 6897640Ssam goto dropit; 690*11192Ssam 6917640Ssam #define vvdataaddr(vv, off, type) ((type)(((caddr_t)((vv)+1)+(off)))) 692*11192Ssam 6937640Ssam if (vv_dotrailer && vv->vh_type >= RING_IPTrailer && 6947640Ssam vv->vh_type < RING_IPTrailer+RING_IPNTrailer){ 6957640Ssam off = (vv->vh_type - RING_IPTrailer) * 512; 6967640Ssam if (off > VVMTU) 6977640Ssam goto dropit; 6987640Ssam vv->vh_type = *vvdataaddr(vv, off, u_short *); 6997640Ssam resid = *(vvdataaddr(vv, off+2, u_short *)); 7007640Ssam if (off + resid > len) 7017640Ssam goto dropit; 7027640Ssam len = off + resid; 703*11192Ssam } else { 7047640Ssam off = 0; 705*11192Ssam } 7067640Ssam if (len == 0) 7077640Ssam goto dropit; 708*11192Ssam 7097640Ssam m = if_rubaget(&vs->vs_ifuba, len, off); 7107640Ssam if (m == 0) 7117640Ssam goto dropit; 712*11192Ssam 7137640Ssam if (off) { 7147640Ssam m->m_off += 2 * sizeof(u_short); 7157640Ssam m->m_len -= 2 * sizeof(u_short); 7167640Ssam } 717*11192Ssam 718*11192Ssam /* 719*11192Ssam * Demultiplex on packet type 720*11192Ssam */ 721*11192Ssam 7227024Ssam switch (vv->vh_type) { 723*11192Ssam 7247024Ssam #ifdef INET 7257024Ssam case RING_IP: 7267024Ssam schednetisr(NETISR_IP); 7277024Ssam inq = &ipintrq; 7287024Ssam break; 7297024Ssam #endif 7307024Ssam default: 7317024Ssam printf("vv%d: unknown pkt type 0x%x\n", unit, vv->vh_type); 7327640Ssam m_freem(m); 7337024Ssam goto setup; 7347024Ssam } 7357640Ssam if (IF_QFULL(inq)) { 7367640Ssam IF_DROP(inq); 7377640Ssam m_freem(m); 738*11192Ssam } else { 7397640Ssam IF_ENQUEUE(inq, m); 740*11192Ssam } 7417024Ssam 7427024Ssam setup: 7437024Ssam /* 744*11192Ssam * Check the error rate and start recovery if needed 745*11192Ssam * this has to go here since the timer flywheel runs at 746*11192Ssam * a lower ipl and never gets a chance to change the mode 7477024Ssam */ 748*11192Ssam if (vs->vs_major == MODE0 && vs->vs_dropped > VV_ERRORTHRESHOLD) { 7497024Ssam 750*11192Ssam vvtrprintf("vv%d going MODE1 in vvrint\n",unit); 751*11192Ssam vs->vs_major = MODE1; 752*11192Ssam vs->vs_iactive = PAUSE; /* no new reads */ 753*11192Ssam vs->vs_retry = VV_MODE1ATTEMPTS; 754*11192Ssam vs->vs_delayclock = VV_MODE1DELAY; 755*11192Ssam vs->vs_minor = 0; 756*11192Ssam vs->vs_dropped = 0; 757*11192Ssam } 758*11192Ssam 759*11192Ssam switch (vs->vs_iactive) { 760*11192Ssam 761*11192Ssam case ACTIVE: 762*11192Ssam 763*11192Ssam /* Restart the read for next packet */ 764*11192Ssam 765*11192Ssam ubainfo = vs->vs_ifuba.ifu_r.ifrw_info; 766*11192Ssam addr->vviba = (u_short) ubainfo; 767*11192Ssam addr->vviea = (u_short) (ubainfo >> 16); 768*11192Ssam addr->vviwc = -(sizeof (struct vv_header) + VVMTU) >> 1; 769*11192Ssam addr->vvicsr = VV_RST | VV_CONF; 770*11192Ssam addr->vvicsr |= VV_IEN | VV_DEN | VV_ENB; 771*11192Ssam return; 772*11192Ssam 773*11192Ssam case PAUSE: 774*11192Ssam 775*11192Ssam /* requested to not start any new reads */ 776*11192Ssam vs->vs_dropped = 0; 777*11192Ssam return; 778*11192Ssam 779*11192Ssam case OPEN: 780*11192Ssam 781*11192Ssam /* request to open host relay */ 782*11192Ssam vs->vs_dropped = 0; 783*11192Ssam addr->vvicsr = 0; 784*11192Ssam return; 785*11192Ssam 786*11192Ssam default: 787*11192Ssam printf("vv%d: vs_iactive = %d\n", unit, vs->vs_iactive); 788*11192Ssam return; 789*11192Ssam } 790*11192Ssam 791*11192Ssam /* 792*11192Ssam * drop packet on floor -- count them!! 793*11192Ssam */ 794*11192Ssam 7957640Ssam dropit: 7967640Ssam vs->vs_if.if_ierrors++; 797*11192Ssam vs->vs_dropped++; 7987640Ssam /* 7997640Ssam printf("vv%d: error vvicsr = %b\n", unit, 8007640Ssam 0xffff&(addr->vvicsr), VV_IBITS); 8017640Ssam */ 8027640Ssam goto setup; 803*11192Ssam 8047024Ssam } 8057024Ssam 8067024Ssam /* 8077024Ssam * V2lni output routine. 8087024Ssam * Encapsulate a packet of type family for the local net. 8097024Ssam * Use trailer local net encapsulation if enough data in first 8107024Ssam * packet leaves a multiple of 512 bytes of data in remainder. 8117024Ssam */ 8127024Ssam vvoutput(ifp, m0, dst) 8137024Ssam struct ifnet *ifp; 8147024Ssam struct mbuf *m0; 8157024Ssam struct sockaddr *dst; 8167024Ssam { 8177024Ssam register struct mbuf *m = m0; 8187024Ssam register struct vv_header *vv; 8197640Ssam register int off; 8207640Ssam int type, dest, s, error; 8217024Ssam 8227024Ssam switch (dst->sa_family) { 823*11192Ssam 8247024Ssam #ifdef INET 8257024Ssam case AF_INET: { 8267640Ssam dest = ((struct sockaddr_in *)dst)->sin_addr.s_addr; 827*11192Ssam 828*11192Ssam /* check address range */ 829*11192Ssam 830*11192Ssam if ((dest = in_lnaof(*((struct in_addr *)&dest))) >= 0x100) { 8317640Ssam error = EPERM; 8327640Ssam goto bad; 8337640Ssam } 8347640Ssam off = ntohs((u_short)mtod(m, struct ip *)->ip_len) - m->m_len; 8357640Ssam if (vv_dotrailer && off > 0 && (off & 0x1ff) == 0 && 8367640Ssam m->m_off >= MMINOFF + 2 * sizeof (u_short)) { 8377640Ssam type = RING_IPTrailer + (off>>9); 8387640Ssam m->m_off -= 2 * sizeof (u_short); 8397640Ssam m->m_len += 2 * sizeof (u_short); 8407640Ssam *mtod(m, u_short *) = RING_IP; 8417640Ssam *(mtod(m, u_short *) + 1) = m->m_len; 8427640Ssam goto gottrailertype; 8437640Ssam } 8447024Ssam type = RING_IP; 8457024Ssam off = 0; 8467024Ssam goto gottype; 8477024Ssam } 8487024Ssam #endif 8497024Ssam default: 8507024Ssam printf("vv%d: can't handle af%d\n", ifp->if_unit, 8517024Ssam dst->sa_family); 8527640Ssam error = EAFNOSUPPORT; 8537640Ssam goto bad; 8547024Ssam } 8557024Ssam 8567024Ssam gottrailertype: 8577024Ssam /* 8587024Ssam * Packet to be sent as trailer: move first packet 8597024Ssam * (control information) to end of chain. 8607024Ssam */ 8617024Ssam while (m->m_next) 8627024Ssam m = m->m_next; 8637024Ssam m->m_next = m0; 8647024Ssam m = m0->m_next; 8657024Ssam m0->m_next = 0; 8667024Ssam m0 = m; 8677024Ssam 8687024Ssam gottype: 8697024Ssam /* 8707024Ssam * Add local net header. If no space in first mbuf, 8717024Ssam * allocate another. 8727024Ssam */ 8737024Ssam if (m->m_off > MMAXOFF || 8747024Ssam MMINOFF + sizeof (struct vv_header) > m->m_off) { 875*11192Ssam m = m_get(M_DONTWAIT); 8767024Ssam if (m == 0) { 8777640Ssam error = ENOBUFS; 8787640Ssam goto bad; 8797024Ssam } 8807024Ssam m->m_next = m0; 8817024Ssam m->m_off = MMINOFF; 8827024Ssam m->m_len = sizeof (struct vv_header); 8837024Ssam } else { 8847024Ssam m->m_off -= sizeof (struct vv_header); 8857024Ssam m->m_len += sizeof (struct vv_header); 8867024Ssam } 8877024Ssam vv = mtod(m, struct vv_header *); 8887024Ssam vv->vh_shost = ifp->if_host[0]; 8897024Ssam vv->vh_dhost = dest; 8907024Ssam vv->vh_version = RING_VERSION; 8917024Ssam vv->vh_type = type; 8927640Ssam vv->vh_info = off; 893*11192Ssam vvtracehdr("vo", vv); 8947024Ssam 8957024Ssam /* 8967024Ssam * Queue message on interface, and start output if interface 8977024Ssam * not yet active. 8987024Ssam */ 8997024Ssam s = splimp(); 9007640Ssam if (IF_QFULL(&ifp->if_snd)) { 9017640Ssam IF_DROP(&ifp->if_snd); 9027640Ssam error = ENOBUFS; 9037640Ssam goto qfull; 9047640Ssam } 9057024Ssam IF_ENQUEUE(&ifp->if_snd, m); 9067024Ssam if (vv_softc[ifp->if_unit].vs_oactive == 0) 9077024Ssam vvstart(ifp->if_unit); 9087024Ssam splx(s); 9097640Ssam return (0); 9107640Ssam 9117640Ssam qfull: 9127640Ssam m0 = m; 9137640Ssam splx(s); 9147640Ssam bad: 9157640Ssam m_freem(m0); 9167640Ssam return(error); 9177024Ssam } 9187024Ssam 9197024Ssam /* 9207024Ssam * vvprt_hdr(s, v) print the local net header in "v" 9217024Ssam * with title is "s" 9227024Ssam */ 9237024Ssam vvprt_hdr(s, v) 9247024Ssam char *s; 9257024Ssam register struct vv_header *v; 9267024Ssam { 9277024Ssam printf("%s: dsvti: 0x%x 0x%x 0x%x 0x%x 0x%x\n", 9287024Ssam s, 9297024Ssam 0xff & (int)(v->vh_dhost), 0xff & (int)(v->vh_shost), 9307024Ssam 0xff & (int)(v->vh_version), 0xff & (int)(v->vh_type), 9317024Ssam 0xffff & (int)(v->vh_info)); 9327024Ssam } 9337024Ssam 9347024Ssam /* 9357024Ssam * print "l" hex bytes starting at "s" 9367024Ssam */ 9377024Ssam vvprt_hex(s, l) 9387024Ssam char *s; 9397024Ssam int l; 9407024Ssam { 9417024Ssam register int i; 9427024Ssam register int z; 9437024Ssam 9447024Ssam for (i=0 ; i < l; i++) { 9457024Ssam z = 0xff & (int)(*(s + i)); 9467024Ssam printf("%c%c ", 9477024Ssam "0123456789abcdef"[(z >> 4) & 0x0f], 9487024Ssam "0123456789abcdef"[z & 0x0f] 9497024Ssam ); 9507024Ssam } 9517024Ssam } 952