1*13090Ssam /* if_vv.c 4.22 83/06/13 */ 27024Ssam 39799Ssam #include "vv.h" 411192Ssam 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. 1011192Ssam * 117024Ssam */ 1211209Ssam #include "../machine/pte.h" 139799Ssam 147024Ssam #include "../h/param.h" 157024Ssam #include "../h/systm.h" 167024Ssam #include "../h/mbuf.h" 177024Ssam #include "../h/buf.h" 187024Ssam #include "../h/protosw.h" 197024Ssam #include "../h/socket.h" 207024Ssam #include "../h/vmmac.h" 2111192Ssam #include "../h/errno.h" 2211209Ssam #include "../h/time.h" 2311209Ssam #include "../h/kernel.h" 2413057Ssam #include "../h/ioctl.h" 258465Sroot 268465Sroot #include "../net/if.h" 2711209Ssam #include "../net/netisr.h" 288465Sroot #include "../net/route.h" 2911192Ssam 308421Swnj #include "../netinet/in.h" 318421Swnj #include "../netinet/in_systm.h" 328421Swnj #include "../netinet/ip.h" 338421Swnj #include "../netinet/ip_var.h" 348465Sroot 3511209Ssam #include "../vax/mtpr.h" 3611209Ssam #include "../vax/cpu.h" 3711192Ssam 388465Sroot #include "../vaxuba/ubareg.h" 398465Sroot #include "../vaxuba/ubavar.h" 407024Ssam 4111209Ssam #include "../vaxif/if_vv.h" 4211209Ssam #include "../vaxif/if_uba.h" 4311209Ssam 447024Ssam /* 457024Ssam * N.B. - if WIRECENTER is defined wrong, it can well break 467024Ssam * the hardware!! 477024Ssam */ 487024Ssam #define WIRECENTER 497024Ssam 507024Ssam #ifdef WIRECENTER 517024Ssam #define VV_CONF VV_HEN /* drive wire center relay */ 527024Ssam #else 537024Ssam #define VV_CONF VV_STE /* allow operation without wire center */ 547024Ssam #endif 557024Ssam 567024Ssam #define VVMTU (1024+512) 577640Ssam #define VVMRU (1024+512+16) /* space for trailer */ 587024Ssam 5913057Ssam int vv_tracehdr = 0, /* 1 => trace headers (slowly!!) */ 6011192Ssam vv_tracetimeout = 1; /* 1 => trace input error-rate limiting */ 6111192Ssam vv_logreaderrors = 0; /* 1 => log all read errors */ 627640Ssam 6311192Ssam #define vvtracehdr if (vv_tracehdr) vvprt_hdr 6411192Ssam #define vvtrprintf if (vv_tracetimeout) printf 6511192Ssam 6611192Ssam int vv_ticking = 0; /* error flywheel is running */ 6711192Ssam 6811209Ssam /* 6911209Ssam * Interval in HZ - 50 msec. 7011209Ssam * N.B. all times below are in units of flywheel ticks 7111209Ssam */ 7211209Ssam #define VV_FLYWHEEL 3 7311192Ssam #define VV_ERRORTHRESHOLD 100 /* errors/flywheel-interval */ 7411192Ssam #define VV_MODE1ATTEMPTS 10 /* number mode 1 retries */ 7511192Ssam #define VV_MODE1DELAY 2 /* period interface is PAUSEd - 100ms */ 7611192Ssam #define VV_MODE2DELAY 4 /* base interval host relay is off - 200ms */ 7711192Ssam #define VV_MAXDELAY 6400 /* max interval host relay is off - 2 minutes */ 7811192Ssam 797024Ssam int vvprobe(), vvattach(), vvrint(), vvxint(); 807024Ssam struct uba_device *vvinfo[NVV]; 817024Ssam u_short vvstd[] = { 0 }; 827024Ssam struct uba_driver vvdriver = 837024Ssam { vvprobe, 0, vvattach, 0, vvstd, "vv", vvinfo }; 847024Ssam #define VVUNIT(x) minor(x) 8513057Ssam int vvinit(),vvioctl(),vvoutput(),vvreset(); 867024Ssam 877024Ssam /* 887024Ssam * Software status of each interface. 897024Ssam * 907024Ssam * Each interface is referenced by a network interface structure, 917024Ssam * vs_if, which the routing code uses to locate the interface. 927024Ssam * This structure contains the output queue for the interface, its address, ... 937024Ssam * We also have, for each interface, a UBA interface structure, which 947024Ssam * contains information about the UNIBUS resources held by the interface: 957024Ssam * map registers, buffered data paths, etc. Information is cached in this 967024Ssam * structure for use by the if_uba.c routines in running the interface 977024Ssam * efficiently. 987024Ssam */ 997024Ssam struct vv_softc { 1007024Ssam struct ifnet vs_if; /* network-visible interface */ 1017024Ssam struct ifuba vs_ifuba; /* UNIBUS resources */ 10211192Ssam short vs_oactive; /* is output active */ 10311192Ssam short vs_iactive; /* is input active */ 1047024Ssam short vs_olen; /* length of last output */ 1057024Ssam u_short vs_lastx; /* last destination address */ 10611192Ssam short vs_tries; /* transmit current retry count */ 1077024Ssam short vs_init; /* number of ring inits */ 1087024Ssam short vs_nottaken; /* number of packets refused */ 10911192Ssam /* input error rate limiting state */ 11011192Ssam short vs_major; /* recovery major state */ 11111192Ssam short vs_minor; /* recovery minor state */ 11211192Ssam short vs_retry; /* recovery retry count */ 11311192Ssam short vs_delayclock; /* recovery delay clock */ 11411192Ssam short vs_delayrange; /* increasing delay interval */ 11511192Ssam short vs_dropped; /* number of packes tossed in last dt */ 1167024Ssam } vv_softc[NVV]; 1177024Ssam 11811192Ssam /* 11911209Ssam * States of vs_iactive. 12011192Ssam */ 12111192Ssam #define ACTIVE 1 /* interface should post new receives */ 12211192Ssam #define PAUSE 0 /* interface should NOT post new receives */ 12311192Ssam #define OPEN -1 /* PAUSE and open host relay */ 12411192Ssam 12511192Ssam /* 12611209Ssam * Recovery major states. 12711192Ssam */ 12811192Ssam #define MODE0 0 /* everything is wonderful */ 12911192Ssam #define MODE1 1 /* hopefully whatever will go away */ 13011209Ssam #define MODE2 2 /* drastic measures - open host relay for increasing intervals */ 13111192Ssam 1327024Ssam vvprobe(reg) 1337024Ssam caddr_t reg; 1347024Ssam { 1357024Ssam register int br, cvec; 1367024Ssam register struct vvreg *addr = (struct vvreg *)reg; 1377024Ssam 1387024Ssam #ifdef lint 13912774Ssam br = 0; cvec = br; br = cvec; vvrint(0); 1407024Ssam #endif 1417024Ssam /* reset interface, enable, and wait till dust settles */ 1427024Ssam addr->vvicsr = VV_RST; 1437024Ssam addr->vvocsr = VV_RST; 14412351Smo DELAY(10000); 1457024Ssam /* generate interrupt by doing 1 word DMA from 0 in uba space!! */ 1467024Ssam addr->vvocsr = VV_IEN; /* enable interrupt */ 1477024Ssam addr->vvoba = 0; /* low 16 bits */ 1487024Ssam addr->vvoea = 0; /* extended bits */ 1497024Ssam addr->vvowc = -1; /* for 1 word */ 1507024Ssam addr->vvocsr |= VV_DEN; /* start the DMA */ 1517024Ssam DELAY(100000); 1527024Ssam addr->vvocsr = 0; 1537024Ssam if (cvec && cvec != 0x200) 1547024Ssam cvec -= 4; /* backup so vector => recieve */ 1557024Ssam return(1); 1567024Ssam } 1577024Ssam 1587024Ssam /* 1597024Ssam * Interface exists: make available by filling in network interface 1607024Ssam * record. System will initialize the interface when it is ready 1617024Ssam * to accept packets. 1627024Ssam */ 1637024Ssam vvattach(ui) 1647024Ssam struct uba_device *ui; 1657024Ssam { 1667024Ssam register struct vv_softc *vs = &vv_softc[ui->ui_unit]; 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_init = vvinit; 17213057Ssam vs->vs_if.if_ioctl = vvioctl; 1737024Ssam vs->vs_if.if_output = vvoutput; 17411209Ssam vs->vs_if.if_reset = vvreset; 1757640Ssam vs->vs_ifuba.ifu_flags = UBA_CANTWAIT | UBA_NEEDBDP | UBA_NEED16; 17612354Smo #if defined(VAX750) 17712354Smo /* don't chew up 750 bdp's */ 17812354Smo if (cpu == VAX_750 && ui->ui_unit > 0) 17912354Smo vs->vs_ifuba.ifu_flags &= ~UBA_NEEDBDP; 18012354Smo #endif 1817024Ssam if_attach(&vs->vs_if); 1827024Ssam } 1837024Ssam 1847024Ssam /* 1857024Ssam * Reset of interface after UNIBUS reset. 1867024Ssam * If interface is on specified uba, reset its state. 1877024Ssam */ 1887024Ssam vvreset(unit, uban) 1897024Ssam int unit, uban; 1907024Ssam { 1917024Ssam register struct uba_device *ui; 1927024Ssam 1937024Ssam if (unit >= NVV || (ui = vvinfo[unit]) == 0 || ui->ui_alive == 0 || 1947024Ssam ui->ui_ubanum != uban) 1957024Ssam return; 1967024Ssam printf(" vv%d", unit); 1977024Ssam vvinit(unit); 1987024Ssam } 1997024Ssam 2007024Ssam /* 2017024Ssam * Initialization of interface; clear recorded pending 2027024Ssam * operations, and reinitialize UNIBUS usage. 2037024Ssam */ 2047024Ssam vvinit(unit) 2057024Ssam int unit; 2067024Ssam { 2077024Ssam register struct vv_softc *vs = &vv_softc[unit]; 2087024Ssam register struct uba_device *ui = vvinfo[unit]; 2097024Ssam register struct vvreg *addr; 2107024Ssam struct sockaddr_in *sin; 2117640Ssam int ubainfo, s; 21211192Ssam int vvtimeout(); 2137024Ssam 21413057Ssam if (vs->vs_if.if_net == 0) 21513057Ssam return; 2167640Ssam addr = (struct vvreg *)ui->ui_addr; 2177024Ssam if (if_ubainit(&vs->vs_ifuba, ui->ui_ubanum, 2187024Ssam sizeof (struct vv_header), (int)btoc(VVMTU)) == 0) { 2197024Ssam printf("vv%d: can't initialize\n", unit); 2207640Ssam vs->vs_if.if_flags &= ~IFF_UP; 2217024Ssam return; 2227024Ssam } 22311209Ssam if (vv_ticking++ == 0) 22411209Ssam timeout(vvtimeout, (caddr_t) 0, VV_FLYWHEEL); 2257024Ssam /* 22611209Ssam * Discover our host address and post it 2277640Ssam */ 2287640Ssam vs->vs_if.if_host[0] = vvidentify(unit); 2297640Ssam printf("vv%d: host %d\n", unit, vs->vs_if.if_host[0]); 2307640Ssam sin = (struct sockaddr_in *)&vs->vs_if.if_addr; 2317640Ssam sin->sin_family = AF_INET; 23211209Ssam sin->sin_addr = if_makeaddr(vs->vs_if.if_net, vs->vs_if.if_host[0]); 23313057Ssam sin = (struct sockaddr_in *)&vs->vs_if.if_broadaddr; 23413057Ssam sin->sin_family = AF_INET; 23513057Ssam sin->sin_addr = if_makeaddr(vs->vs_if.if_net, VV_BROADCAST); 2367640Ssam 2377640Ssam /* 2387640Ssam * Reset the interface, and join the ring 2397640Ssam */ 2407640Ssam addr->vvocsr = VV_RST | VV_CPB; /* clear packet buffer */ 2417640Ssam addr->vvicsr = VV_RST | VV_CONF; /* close logical relay */ 24212351Smo DELAY(500000); /* let contacts settle */ 2437640Ssam vs->vs_init = 0; 24411192Ssam vs->vs_dropped = 0; 2457640Ssam vs->vs_nottaken = 0; 2467640Ssam 2477640Ssam /* 2487640Ssam * Hang a receive and start any 2497640Ssam * pending writes by faking a transmit complete. 2507640Ssam */ 2517640Ssam s = splimp(); 2527640Ssam ubainfo = vs->vs_ifuba.ifu_r.ifrw_info; 25313057Ssam addr->vviba = (u_short)ubainfo; 25413057Ssam addr->vviea = (u_short)(ubainfo >> 16); 2557640Ssam addr->vviwc = -(sizeof (struct vv_header) + VVMTU) >> 1; 2567640Ssam addr->vvicsr = VV_IEN | VV_CONF | VV_DEN | VV_ENB; 25711192Ssam vs->vs_iactive = ACTIVE; 2587640Ssam vs->vs_oactive = 1; 25913057Ssam vs->vs_if.if_flags |= IFF_UP | IFF_RUNNING; 2607640Ssam vvxint(unit); 2617640Ssam splx(s); 2627640Ssam if_rtinit(&vs->vs_if, RTF_UP); 2637640Ssam } 2647640Ssam 2657640Ssam /* 2667640Ssam * vvidentify() - return our host address 2677640Ssam */ 26811209Ssam vvidentify(unit) 26913057Ssam int unit; 27011209Ssam { 2717640Ssam register struct vv_softc *vs = &vv_softc[unit]; 2727640Ssam register struct uba_device *ui = vvinfo[unit]; 2737640Ssam register struct vvreg *addr; 2747640Ssam struct mbuf *m; 2757640Ssam struct vv_header *v; 27612774Ssam int ubainfo, attempts, waitcount; 2777640Ssam 2787640Ssam /* 2797024Ssam * Build a multicast message to identify our address 2807024Ssam */ 2817640Ssam addr = (struct vvreg *)ui->ui_addr; 2827024Ssam attempts = 0; /* total attempts, including bad msg type */ 28311209Ssam m = m_get(M_DONTWAIT, MT_HEADER); 28412774Ssam if (m == NULL) 28513057Ssam return (0); 28611192Ssam m->m_next = 0; 2877024Ssam m->m_off = MMINOFF; 2887024Ssam m->m_len = sizeof(struct vv_header); 2897024Ssam v = mtod(m, struct vv_header *); 29011192Ssam v->vh_dhost = VV_BROADCAST; /* multicast destination address */ 2917024Ssam v->vh_shost = 0; /* will be overwritten with ours */ 2927024Ssam v->vh_version = RING_VERSION; 2937024Ssam v->vh_type = RING_WHOAMI; 2947024Ssam v->vh_info = 0; 29511192Ssam /* map xmit message into uba */ 2967640Ssam vs->vs_olen = if_wubaput(&vs->vs_ifuba, m); 2977640Ssam if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP) 2987640Ssam UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_w.ifrw_bdp); 2997024Ssam /* 3007024Ssam * Reset interface, establish Digital Loopback Mode, and 3017024Ssam * send the multicast (to myself) with Input Copy enabled. 3027024Ssam */ 3037024Ssam retry: 3047024Ssam ubainfo = vs->vs_ifuba.ifu_r.ifrw_info; 3057024Ssam addr->vvicsr = VV_RST; 3067024Ssam addr->vviba = (u_short) ubainfo; 3077024Ssam addr->vviea = (u_short) (ubainfo >> 16); 3087024Ssam addr->vviwc = -(sizeof (struct vv_header) + VVMTU) >> 1; 3097024Ssam addr->vvicsr = VV_STE | VV_DEN | VV_ENB | VV_LPB; 3107640Ssam 3117640Ssam /* let flag timers fire so ring will initialize */ 31212164Ssam DELAY(2000000); 3137640Ssam 3147024Ssam addr->vvocsr = VV_RST | VV_CPB; /* clear packet buffer */ 3157024Ssam ubainfo = vs->vs_ifuba.ifu_w.ifrw_info; 3167024Ssam addr->vvoba = (u_short) ubainfo; 3177024Ssam addr->vvoea = (u_short) (ubainfo >> 16); 3187024Ssam addr->vvowc = -((vs->vs_olen + 1) >> 1); 3197024Ssam addr->vvocsr = VV_CPB | VV_DEN | VV_INR | VV_ENB; 3207024Ssam /* 3217024Ssam * Wait for receive side to finish. 32211192Ssam * Extract source address (which will our own), 3237024Ssam * and post to interface structure. 3247024Ssam */ 3257024Ssam DELAY(1000); 32611192Ssam for (waitcount = 0; (addr->vvicsr & VV_RDY) == 0; waitcount++) { 3279876Ssam if (waitcount < 10) { 3287024Ssam DELAY(1000); 32911209Ssam continue; 33011192Ssam } 33111209Ssam if (attempts++ >= 10) { 33211209Ssam printf("vv%d: can't initialize\n", unit); 33311209Ssam printf("vvinit loopwait: icsr = %b\n", 33411209Ssam 0xffff&(addr->vvicsr), VV_IBITS); 33511209Ssam vs->vs_if.if_flags &= ~IFF_UP; 33612774Ssam return (0); 3377024Ssam } 33811209Ssam goto retry; 33911192Ssam } 3407024Ssam if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP) 3417024Ssam UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_w.ifrw_bdp); 3427024Ssam if (vs->vs_ifuba.ifu_xtofree) 3437024Ssam m_freem(vs->vs_ifuba.ifu_xtofree); 3447024Ssam if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP) 3457024Ssam UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_r.ifrw_bdp); 3467024Ssam m = if_rubaget(&vs->vs_ifuba, sizeof(struct vv_header), 0); 34711209Ssam if (m != NULL) 3487024Ssam m_freem(m); 3497024Ssam /* 35011209Ssam * Check message type before we believe the source host address 3517024Ssam */ 3527024Ssam v = (struct vv_header *)(vs->vs_ifuba.ifu_r.ifrw_addr); 35311209Ssam if (v->vh_type != RING_WHOAMI) 3547640Ssam goto retry; 35511209Ssam return(v->vh_shost); 3567024Ssam } 3577024Ssam 3587024Ssam /* 35911192Ssam * vvtimeout() - called by timer flywheel to monitor input packet 36011192Ssam * discard rate. Interfaces getting too many errors are shut 36111192Ssam * down for a while. If the condition persists, the interface 36211192Ssam * is marked down. 36311192Ssam */ 36412774Ssam /*ARGSUSED*/ 36511192Ssam vvtimeout(junk) 36611209Ssam int junk; 36711192Ssam { 36811192Ssam register struct vv_softc *vs; 36911192Ssam register int i; 37011192Ssam register struct vvreg *addr; 37111192Ssam int ubainfo; 37211192Ssam 37311192Ssam timeout(vvtimeout, (caddr_t) 0, VV_FLYWHEEL); 37411209Ssam for (i = 0; i < NVV; i++) { 37511192Ssam vs = &vv_softc[i]; 37611192Ssam addr = (struct vvreg *)vvinfo[i]->ui_addr; 37712774Ssam if ((vs->vs_if.if_flags & IFF_UP) == 0) 37812774Ssam continue; 37911192Ssam switch (vs->vs_major) { 38011192Ssam 38111192Ssam /* 38211192Ssam * MODE0: generally OK, just check error rate 38311192Ssam */ 38411192Ssam case MODE0: 38511192Ssam if (vs->vs_dropped < VV_ERRORTHRESHOLD) { 38611192Ssam vs->vs_dropped = 0; 38711192Ssam continue; 38811192Ssam } 38911209Ssam /* suspend reads for a while */ 39011209Ssam vvtrprintf("vv%d going MODE1 in vvtimeout\n",i); 39111209Ssam vs->vs_major = MODE1; 39211209Ssam vs->vs_iactive = PAUSE; /* no new reads */ 39311209Ssam vs->vs_retry = VV_MODE1ATTEMPTS; 39411209Ssam vs->vs_delayclock = VV_MODE1DELAY; 39511209Ssam vs->vs_minor = 0; 39611209Ssam continue; 39711192Ssam 39811192Ssam /* 39911192Ssam * MODE1: excessive error rate observed 40011192Ssam * Scheme: try simply suspending reads for a 40111192Ssam * short while a small number of times 40211192Ssam */ 40311192Ssam case MODE1: 40411192Ssam if (vs->vs_delayclock > 0) { 40511192Ssam vs->vs_delayclock--; 40611192Ssam continue; 40711192Ssam } 40811192Ssam switch (vs->vs_minor) { 40911209Ssam 41011192Ssam case 0: /* reenable reads */ 41111192Ssam vvtrprintf("vv%d M1m0\n",i); 41211192Ssam vs->vs_dropped = 0; 41311192Ssam vs->vs_iactive = ACTIVE; 41411192Ssam vs->vs_minor = 1; /* next state */ 41511192Ssam ubainfo = vs->vs_ifuba.ifu_r.ifrw_info; 41611192Ssam addr->vviba = (u_short) ubainfo; 41711192Ssam addr->vviea = (u_short) (ubainfo >> 16); 41811209Ssam addr->vviwc = 41911209Ssam -(sizeof (struct vv_header) + VVMTU) >> 1; 42011192Ssam addr->vvicsr = VV_RST | VV_CONF; 42111192Ssam addr->vvicsr |= VV_IEN | VV_DEN | VV_ENB; 42211192Ssam continue; 42311209Ssam 42411192Ssam case 1: /* see if it worked */ 42511192Ssam vvtrprintf("vv%d M1m1\n",i); 42611192Ssam if (vs->vs_dropped < VV_ERRORTHRESHOLD) { 42711192Ssam vs->vs_dropped = 0; 42811192Ssam vs->vs_major = MODE0; /* yeah!! */ 42911192Ssam continue; 43011192Ssam } 43111209Ssam if (vs->vs_retry -- > 0) { 43211209Ssam vs->vs_dropped = 0; 43311209Ssam vs->vs_iactive = PAUSE; 43411209Ssam vs->vs_delayclock = VV_MODE1DELAY; 43511209Ssam vs->vs_minor = 0; /* recheck */ 43611209Ssam continue; 43711192Ssam } 43811209Ssam vs->vs_major = MODE2; 43911209Ssam vs->vs_minor = 0; 44011209Ssam vs->vs_dropped = 0; 44111209Ssam vs->vs_iactive = OPEN; 44211209Ssam vs->vs_delayrange = VV_MODE2DELAY; 44311209Ssam vs->vs_delayclock = VV_MODE2DELAY; 44411209Ssam /* fall thru ... */ 44511192Ssam } 44611192Ssam 44711192Ssam /* 44811192Ssam * MODE2: simply ignoring traffic didn't relieve condition 44911192Ssam * Scheme: open host relay for intervals linearly 45011192Ssam * increasing up to some maximum of a several minutes. 45111192Ssam * This allows broken networks to return to operation 45211192Ssam * without rebooting. 45311192Ssam */ 45411192Ssam case MODE2: 45511192Ssam if (vs->vs_delayclock > 0) { 45611192Ssam vs->vs_delayclock--; 45711192Ssam continue; 45811192Ssam } 45911192Ssam switch (vs->vs_minor) { 46011209Ssam 46111192Ssam case 0: /* close relay and reenable reads */ 46211192Ssam vvtrprintf("vv%d M2m0\n",i); 46311192Ssam vs->vs_dropped = 0; 46411192Ssam vs->vs_iactive = ACTIVE; 46511192Ssam vs->vs_minor = 1; /* next state */ 46611192Ssam ubainfo = vs->vs_ifuba.ifu_r.ifrw_info; 46711192Ssam addr->vviba = (u_short) ubainfo; 46811192Ssam addr->vviea = (u_short) (ubainfo >> 16); 46911209Ssam addr->vviwc = 47011209Ssam -(sizeof (struct vv_header) + VVMTU) >> 1; 47111192Ssam addr->vvicsr = VV_RST | VV_CONF; 47211192Ssam addr->vvicsr |= VV_IEN | VV_DEN | VV_ENB; 47311192Ssam continue; 47411209Ssam 47511192Ssam case 1: /* see if it worked */ 47611192Ssam vvtrprintf("vv%d M2m1\n",i); 47711192Ssam if (vs->vs_dropped < VV_ERRORTHRESHOLD) { 47811192Ssam vs->vs_dropped = 0; 47911192Ssam vs->vs_major = MODE0; /* yeah!! */ 48011192Ssam continue; 48111192Ssam } 48211209Ssam vvtrprintf("vv%d M2m1 ++ delay\n",i); 48311209Ssam vs->vs_dropped = 0; 48411209Ssam vs->vs_iactive = OPEN; 48511209Ssam vs->vs_minor = 0; 48611209Ssam if (vs->vs_delayrange < VV_MAXDELAY) 48711209Ssam vs->vs_delayrange += 48811209Ssam (vs->vs_delayrange/2); 48911209Ssam vs->vs_delayclock = vs->vs_delayrange; 49011209Ssam continue; 49111192Ssam } 49211192Ssam 49311192Ssam default: 49411192Ssam printf("vv%d: major state screwed\n", i); 49511192Ssam vs->vs_if.if_flags &= ~IFF_UP; 49611192Ssam } 49711192Ssam } 49811192Ssam } 49911192Ssam 50011192Ssam /* 5017024Ssam * Start or restart output on interface. 50211192Ssam * If interface is active, this is a retransmit, so just 50311192Ssam * restuff registers and go. 5047024Ssam * If interface is not already active, get another datagram 5057024Ssam * to send off of the interface queue, and map it to the interface 5067024Ssam * before starting the output. 5077024Ssam */ 5087024Ssam vvstart(dev) 5097024Ssam dev_t dev; 5107024Ssam { 5117024Ssam int unit = VVUNIT(dev); 5127024Ssam struct uba_device *ui = vvinfo[unit]; 5137024Ssam register struct vv_softc *vs = &vv_softc[unit]; 5147024Ssam register struct vvreg *addr; 5157024Ssam struct mbuf *m; 51611192Ssam int ubainfo; 51711192Ssam int dest; 5187024Ssam 5197024Ssam if (vs->vs_oactive) 5207024Ssam goto restart; 5217024Ssam /* 5227024Ssam * Not already active: dequeue another request 5237024Ssam * and map it to the UNIBUS. If no more requests, 5247024Ssam * just return. 5257024Ssam */ 5267024Ssam IF_DEQUEUE(&vs->vs_if.if_snd, m); 52711209Ssam if (m == NULL) { 5287024Ssam vs->vs_oactive = 0; 5297024Ssam return; 5307024Ssam } 5317024Ssam dest = mtod(m, struct vv_header *)->vh_dhost; 5327024Ssam vs->vs_olen = if_wubaput(&vs->vs_ifuba, m); 5337024Ssam vs->vs_lastx = dest; 5347024Ssam restart: 5357024Ssam /* 5367024Ssam * Have request mapped to UNIBUS for transmission. 5377024Ssam * Purge any stale data from this BDP, and start the otput. 5387024Ssam */ 53912363Ssam if (vs->vs_olen > VVMTU + sizeof (struct vv_header)) { 54011192Ssam printf("vv%d vs_olen: %d > VVMTU\n", unit, vs->vs_olen); 54111192Ssam panic("vvdriver vs_olen botch"); 54211192Ssam } 5437024Ssam if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP) 5447024Ssam UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_w.ifrw_bdp); 5457024Ssam addr = (struct vvreg *)ui->ui_addr; 5467024Ssam ubainfo = vs->vs_ifuba.ifu_w.ifrw_info; 5477024Ssam addr->vvoba = (u_short) ubainfo; 5487024Ssam addr->vvoea = (u_short) (ubainfo >> 16); 5497024Ssam addr->vvowc = -((vs->vs_olen + 1) >> 1); 5507024Ssam addr->vvocsr = VV_IEN | VV_CPB | VV_DEN | VV_INR | VV_ENB; 5517024Ssam vs->vs_oactive = 1; 5527024Ssam } 5537024Ssam 5547024Ssam /* 5557024Ssam * VVLNI transmit interrupt 5567024Ssam * Start another output if more data to send. 5577024Ssam */ 5587024Ssam vvxint(unit) 5597024Ssam int unit; 5607024Ssam { 5617024Ssam register struct uba_device *ui = vvinfo[unit]; 5627024Ssam register struct vv_softc *vs = &vv_softc[unit]; 5637024Ssam register struct vvreg *addr; 5647024Ssam register int oc; 5657024Ssam 5667024Ssam addr = (struct vvreg *)ui->ui_addr; 5677024Ssam oc = 0xffff & (addr->vvocsr); 5687024Ssam if (vs->vs_oactive == 0) { 56911192Ssam printf("vv%d: stray interrupt vvocsr = %b\n", unit, 5707024Ssam oc, VV_OBITS); 5717024Ssam return; 5727024Ssam } 5737024Ssam if (oc & (VV_OPT | VV_RFS)) { 5747640Ssam vs->vs_if.if_collisions++; 57511192Ssam if (vs->vs_tries++ < VVRETRY) { 5767024Ssam if (oc & VV_OPT) 5777024Ssam vs->vs_init++; 5787024Ssam if (oc & VV_RFS) 5797024Ssam vs->vs_nottaken++; 58011192Ssam vvstart(unit); /* restart this message */ 5817024Ssam return; 5827024Ssam } 5837024Ssam if (oc & VV_OPT) 5847024Ssam printf("vv%d: output timeout\n"); 5857024Ssam } 5867024Ssam vs->vs_if.if_opackets++; 5877024Ssam vs->vs_oactive = 0; 5887024Ssam vs->vs_tries = 0; 5897024Ssam if (oc & VVXERR) { 5907024Ssam vs->vs_if.if_oerrors++; 59111192Ssam printf("vv%d: error vvocsr = %b\n", unit, 0xffff & oc, 5927024Ssam VV_OBITS); 5937024Ssam } 5947024Ssam if (vs->vs_ifuba.ifu_xtofree) { 5957024Ssam m_freem(vs->vs_ifuba.ifu_xtofree); 5967024Ssam vs->vs_ifuba.ifu_xtofree = 0; 5977024Ssam } 5987024Ssam if (vs->vs_if.if_snd.ifq_head == 0) { 59911192Ssam vs->vs_lastx = 256; /* an invalid address */ 6007024Ssam return; 6017024Ssam } 6027024Ssam vvstart(unit); 6037024Ssam } 6047024Ssam 6057024Ssam /* 6067024Ssam * V2lni interface receiver interrupt. 6077024Ssam * If input error just drop packet. 6087024Ssam * Otherwise purge input buffered data path and examine 6097024Ssam * packet to determine type. If can't determine length 6107024Ssam * from type, then have to drop packet. Othewise decapsulate 6117024Ssam * packet based on type and pass to type specific higher-level 6127024Ssam * input routine. 6137024Ssam */ 6147024Ssam vvrint(unit) 6157024Ssam int unit; 6167024Ssam { 6177024Ssam register struct vv_softc *vs = &vv_softc[unit]; 6187024Ssam struct vvreg *addr = (struct vvreg *)vvinfo[unit]->ui_addr; 6197024Ssam register struct vv_header *vv; 6207024Ssam register struct ifqueue *inq; 6217024Ssam struct mbuf *m; 6227024Ssam int ubainfo, len, off; 6237640Ssam short resid; 6247024Ssam 6257024Ssam vs->vs_if.if_ipackets++; 6267024Ssam /* 6277024Ssam * Purge BDP; drop if input error indicated. 6287024Ssam */ 6297024Ssam if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP) 6307024Ssam UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_r.ifrw_bdp); 6317024Ssam if (addr->vvicsr & VVRERR) { 63211192Ssam if (vv_logreaderrors) 63311192Ssam printf("vv%d: error vvicsr = %b\n", unit, 63411192Ssam 0xffff&(addr->vvicsr), VV_IBITS); 6357640Ssam goto dropit; 6367024Ssam } 6377640Ssam 6387024Ssam /* 6397640Ssam * Get packet length from word count residue 6407640Ssam * 6417640Ssam * Compute header offset if trailer protocol 6427640Ssam * 6437640Ssam * Pull packet off interface. Off is nonzero if packet 6447640Ssam * has trailing header; if_rubaget will then force this header 6457640Ssam * information to be at the front. The vh_info field 6467640Ssam * carries the offset to the trailer data in trailer 6477640Ssam * format packets. 6487024Ssam */ 6497640Ssam vv = (struct vv_header *)(vs->vs_ifuba.ifu_r.ifrw_addr); 65011192Ssam vvtracehdr("vi", vv); 6517640Ssam resid = addr->vviwc; 6527640Ssam if (resid) 6537640Ssam resid |= 0176000; /* ugly!!!! */ 6547640Ssam len = (((sizeof (struct vv_header) + VVMRU) >> 1) + resid) << 1; 6557640Ssam len -= sizeof(struct vv_header); 65611192Ssam if (len > VVMRU || len <= 0) 6577640Ssam goto dropit; 6587640Ssam #define vvdataaddr(vv, off, type) ((type)(((caddr_t)((vv)+1)+(off)))) 65913057Ssam if (vv->vh_type >= RING_IPTrailer && 66013057Ssam vv->vh_type < RING_IPTrailer+RING_IPNTrailer) { 6617640Ssam off = (vv->vh_type - RING_IPTrailer) * 512; 6627640Ssam if (off > VVMTU) 6637640Ssam goto dropit; 6647640Ssam vv->vh_type = *vvdataaddr(vv, off, u_short *); 6657640Ssam resid = *(vvdataaddr(vv, off+2, u_short *)); 6667640Ssam if (off + resid > len) 6677640Ssam goto dropit; 6687640Ssam len = off + resid; 66911209Ssam } else 6707640Ssam off = 0; 6717640Ssam if (len == 0) 6727640Ssam goto dropit; 6737640Ssam m = if_rubaget(&vs->vs_ifuba, len, off); 67411209Ssam if (m == NULL) 6757640Ssam goto dropit; 6767640Ssam if (off) { 6777640Ssam m->m_off += 2 * sizeof(u_short); 6787640Ssam m->m_len -= 2 * sizeof(u_short); 6797640Ssam } 68011192Ssam 68111192Ssam /* 68211192Ssam * Demultiplex on packet type 68311192Ssam */ 6847024Ssam switch (vv->vh_type) { 68511192Ssam 6867024Ssam #ifdef INET 6877024Ssam case RING_IP: 6887024Ssam schednetisr(NETISR_IP); 6897024Ssam inq = &ipintrq; 6907024Ssam break; 6917024Ssam #endif 6927024Ssam default: 6937024Ssam printf("vv%d: unknown pkt type 0x%x\n", unit, vv->vh_type); 6947640Ssam m_freem(m); 6957024Ssam goto setup; 6967024Ssam } 6977640Ssam if (IF_QFULL(inq)) { 6987640Ssam IF_DROP(inq); 6997640Ssam m_freem(m); 70011209Ssam } else 7017640Ssam IF_ENQUEUE(inq, m); 7027024Ssam setup: 7037024Ssam /* 70411192Ssam * Check the error rate and start recovery if needed 70511192Ssam * this has to go here since the timer flywheel runs at 70611192Ssam * a lower ipl and never gets a chance to change the mode 7077024Ssam */ 70811192Ssam if (vs->vs_major == MODE0 && vs->vs_dropped > VV_ERRORTHRESHOLD) { 70911192Ssam vvtrprintf("vv%d going MODE1 in vvrint\n",unit); 71011192Ssam vs->vs_major = MODE1; 71111192Ssam vs->vs_iactive = PAUSE; /* no new reads */ 71211192Ssam vs->vs_retry = VV_MODE1ATTEMPTS; 71311192Ssam vs->vs_delayclock = VV_MODE1DELAY; 71411192Ssam vs->vs_minor = 0; 71511192Ssam vs->vs_dropped = 0; 71611192Ssam } 71711192Ssam switch (vs->vs_iactive) { 71811192Ssam 71911209Ssam case ACTIVE: /* Restart the read for next packet */ 72011192Ssam ubainfo = vs->vs_ifuba.ifu_r.ifrw_info; 72111192Ssam addr->vviba = (u_short) ubainfo; 72211192Ssam addr->vviea = (u_short) (ubainfo >> 16); 72311192Ssam addr->vviwc = -(sizeof (struct vv_header) + VVMTU) >> 1; 72411192Ssam addr->vvicsr = VV_RST | VV_CONF; 72511192Ssam addr->vvicsr |= VV_IEN | VV_DEN | VV_ENB; 72611192Ssam return; 72711192Ssam 72811209Ssam case PAUSE: /* requested to not start any new reads */ 72911192Ssam vs->vs_dropped = 0; 73011192Ssam return; 73111192Ssam 73211209Ssam case OPEN: /* request to open host relay */ 73311192Ssam vs->vs_dropped = 0; 73411192Ssam addr->vvicsr = 0; 73511192Ssam return; 73611192Ssam 73711192Ssam default: 73811192Ssam printf("vv%d: vs_iactive = %d\n", unit, vs->vs_iactive); 73911192Ssam return; 74011192Ssam } 74111192Ssam /* 74211209Ssam * Drop packet on floor -- count them!! 74311192Ssam */ 7447640Ssam dropit: 7457640Ssam vs->vs_if.if_ierrors++; 74611192Ssam vs->vs_dropped++; 7477640Ssam /* 7487640Ssam printf("vv%d: error vvicsr = %b\n", unit, 7497640Ssam 0xffff&(addr->vvicsr), VV_IBITS); 7507640Ssam */ 7517640Ssam goto setup; 7527024Ssam } 7537024Ssam 7547024Ssam /* 7557024Ssam * V2lni output routine. 7567024Ssam * Encapsulate a packet of type family for the local net. 7577024Ssam * Use trailer local net encapsulation if enough data in first 7587024Ssam * packet leaves a multiple of 512 bytes of data in remainder. 7597024Ssam */ 7607024Ssam vvoutput(ifp, m0, dst) 7617024Ssam struct ifnet *ifp; 7627024Ssam struct mbuf *m0; 7637024Ssam struct sockaddr *dst; 7647024Ssam { 7657024Ssam register struct mbuf *m = m0; 7667024Ssam register struct vv_header *vv; 7677640Ssam register int off; 7687640Ssam int type, dest, s, error; 7697024Ssam 7707024Ssam switch (dst->sa_family) { 77111192Ssam 7727024Ssam #ifdef INET 7737024Ssam case AF_INET: { 7747640Ssam dest = ((struct sockaddr_in *)dst)->sin_addr.s_addr; 77511192Ssam if ((dest = in_lnaof(*((struct in_addr *)&dest))) >= 0x100) { 7767640Ssam error = EPERM; 7777640Ssam goto bad; 7787640Ssam } 7797640Ssam off = ntohs((u_short)mtod(m, struct ip *)->ip_len) - m->m_len; 780*13090Ssam if ((ifp->if_flags & IFF_NOTRAILERS) == 0) 781*13090Ssam if (off > 0 && (off & 0x1ff) == 0 && 7827640Ssam m->m_off >= MMINOFF + 2 * sizeof (u_short)) { 7837640Ssam type = RING_IPTrailer + (off>>9); 7847640Ssam m->m_off -= 2 * sizeof (u_short); 7857640Ssam m->m_len += 2 * sizeof (u_short); 7867640Ssam *mtod(m, u_short *) = RING_IP; 7877640Ssam *(mtod(m, u_short *) + 1) = m->m_len; 7887640Ssam goto gottrailertype; 7897640Ssam } 7907024Ssam type = RING_IP; 7917024Ssam off = 0; 7927024Ssam goto gottype; 7937024Ssam } 7947024Ssam #endif 7957024Ssam default: 7967024Ssam printf("vv%d: can't handle af%d\n", ifp->if_unit, 7977024Ssam dst->sa_family); 7987640Ssam error = EAFNOSUPPORT; 7997640Ssam goto bad; 8007024Ssam } 8017024Ssam 8027024Ssam gottrailertype: 8037024Ssam /* 8047024Ssam * Packet to be sent as trailer: move first packet 8057024Ssam * (control information) to end of chain. 8067024Ssam */ 8077024Ssam while (m->m_next) 8087024Ssam m = m->m_next; 8097024Ssam m->m_next = m0; 8107024Ssam m = m0->m_next; 8117024Ssam m0->m_next = 0; 8127024Ssam m0 = m; 8137024Ssam gottype: 8147024Ssam /* 8157024Ssam * Add local net header. If no space in first mbuf, 8167024Ssam * allocate another. 8177024Ssam */ 8187024Ssam if (m->m_off > MMAXOFF || 8197024Ssam MMINOFF + sizeof (struct vv_header) > m->m_off) { 82011209Ssam m = m_get(M_DONTWAIT, MT_HEADER); 82111209Ssam if (m == NULL) { 8227640Ssam error = ENOBUFS; 8237640Ssam goto bad; 8247024Ssam } 8257024Ssam m->m_next = m0; 8267024Ssam m->m_off = MMINOFF; 8277024Ssam m->m_len = sizeof (struct vv_header); 8287024Ssam } else { 8297024Ssam m->m_off -= sizeof (struct vv_header); 8307024Ssam m->m_len += sizeof (struct vv_header); 8317024Ssam } 8327024Ssam vv = mtod(m, struct vv_header *); 8337024Ssam vv->vh_shost = ifp->if_host[0]; 8347024Ssam vv->vh_dhost = dest; 8357024Ssam vv->vh_version = RING_VERSION; 8367024Ssam vv->vh_type = type; 8377640Ssam vv->vh_info = off; 83811192Ssam vvtracehdr("vo", vv); 8397024Ssam 8407024Ssam /* 8417024Ssam * Queue message on interface, and start output if interface 8427024Ssam * not yet active. 8437024Ssam */ 8447024Ssam s = splimp(); 8457640Ssam if (IF_QFULL(&ifp->if_snd)) { 8467640Ssam IF_DROP(&ifp->if_snd); 8477640Ssam error = ENOBUFS; 8487640Ssam goto qfull; 8497640Ssam } 8507024Ssam IF_ENQUEUE(&ifp->if_snd, m); 8517024Ssam if (vv_softc[ifp->if_unit].vs_oactive == 0) 8527024Ssam vvstart(ifp->if_unit); 8537024Ssam splx(s); 8547640Ssam return (0); 8557640Ssam qfull: 8567640Ssam m0 = m; 8577640Ssam splx(s); 8587640Ssam bad: 8597640Ssam m_freem(m0); 8607640Ssam return(error); 8617024Ssam } 8627024Ssam 8637024Ssam /* 86413057Ssam * Process an ioctl request. 86513057Ssam */ 86613057Ssam vvioctl(ifp, cmd, data) 86713057Ssam register struct ifnet *ifp; 86813057Ssam int cmd; 86913057Ssam caddr_t data; 87013057Ssam { 87113057Ssam struct ifreq *ifr = (struct ifreq *)data; 87213057Ssam int s = splimp(), error = 0; 87313057Ssam 87413057Ssam switch (cmd) { 87513057Ssam 87613057Ssam case SIOCSIFADDR: 87713057Ssam /* too difficult to change addr while running */ 87813057Ssam if ((ifp->if_flags & IFF_RUNNING) == 0) { 879*13090Ssam struct sockaddr_in *sin = 880*13090Ssam (struct sockaddr_in *)&ifr->ifr_addr; 881*13090Ssam ifp->if_net = in_netof(sin->sin_addr); 88213057Ssam vvinit(ifp->if_unit); 88313057Ssam } else 88413057Ssam error = EINVAL; 88513057Ssam break; 88613057Ssam 88713057Ssam default: 88813057Ssam error = EINVAL; 88913057Ssam } 89013057Ssam splx(s); 89113057Ssam return (error); 89213057Ssam } 89313057Ssam 89413057Ssam /* 8957024Ssam * vvprt_hdr(s, v) print the local net header in "v" 8967024Ssam * with title is "s" 8977024Ssam */ 8987024Ssam vvprt_hdr(s, v) 8997024Ssam char *s; 9007024Ssam register struct vv_header *v; 9017024Ssam { 9027024Ssam printf("%s: dsvti: 0x%x 0x%x 0x%x 0x%x 0x%x\n", 9037024Ssam s, 9047024Ssam 0xff & (int)(v->vh_dhost), 0xff & (int)(v->vh_shost), 9057024Ssam 0xff & (int)(v->vh_version), 0xff & (int)(v->vh_type), 9067024Ssam 0xffff & (int)(v->vh_info)); 9077024Ssam } 9087024Ssam 90912774Ssam #ifdef notdef 9107024Ssam /* 9117024Ssam * print "l" hex bytes starting at "s" 9127024Ssam */ 9137024Ssam vvprt_hex(s, l) 9147024Ssam char *s; 9157024Ssam int l; 9167024Ssam { 9177024Ssam register int i; 9187024Ssam register int z; 9197024Ssam 9207024Ssam for (i=0 ; i < l; i++) { 9217024Ssam z = 0xff & (int)(*(s + i)); 9227024Ssam printf("%c%c ", 9237024Ssam "0123456789abcdef"[(z >> 4) & 0x0f], 9247024Ssam "0123456789abcdef"[z & 0x0f] 9257024Ssam ); 9267024Ssam } 9277024Ssam } 92812774Ssam #endif 929