123305Smckusick /* 2*29372Smckusick * Copyright (c) 1982, 1986 Regents of the University of California. 323305Smckusick * All rights reserved. The Berkeley software License Agreement 423305Smckusick * specifies the terms and conditions for redistribution. 523305Smckusick * 6*29372Smckusick * @(#)if_vv.c 7.1 (Berkeley) 06/05/86 723305Smckusick */ 87024Ssam 99799Ssam #include "vv.h" 1025275Sbloom #if NVV > 0 1111192Ssam 127024Ssam /* 1320997Skarels * Proteon proNET-10 and proNET-80 token ring driver. 1420997Skarels * The name of this device driver derives from the old MIT 1520997Skarels * name of V2LNI for the proNET hardware, would would abbreviate 1620997Skarels * to "v2", but this won't work right. Thus the name is "vv". 1711192Ssam * 1820997Skarels * This driver is compatible with the proNET 10 meagbit and 1920997Skarels * 80 megabit token ring interfaces (models p1000 and p1080). 2026903Sjas * A unit may be marked as 80 megabit using "flags 1" in the 2126903Sjas * config file. 2220997Skarels * 2326950Sjas * TRAILERS: This driver has a new implementation of trailers that 2426950Sjas * is at least a tolerable neighbor on the ring. The offset is not 2526950Sjas * stored in the protocol type, but instead only in the vh_info 2626950Sjas * field. Also, the vh_info field, and the two shorts before the 2726950Sjas * trailing header, are in network byte order, not VAX byte order. 2820997Skarels * 2926950Sjas * Of course, nothing but BSD UNIX supports trailers on ProNET. 3026950Sjas * If you need interoperability with anything else, turn off 3126950Sjas * trailers using the -trailers option to /etc/ifconfig! 3226950Sjas * 3326903Sjas * HARDWARE COMPATABILITY: This driver prefers that the HSBU (p1001) 3420997Skarels * have a serial number >= 040, which is about March, 1982. Older 3526903Sjas * HSBUs do not carry across 64kbyte boundaries. They can be supported 3626903Sjas * by adding "| UBA_NEED16" to the vs_ifuba.ifu_flags initialization 3726903Sjas * in vvattach(). 3826903Sjas * 3926903Sjas * The old warning about use without Wire Centers applies only to CTL 4026903Sjas * (p1002) cards with serial <= 057, which have not received ECO 176-743, 4126903Sjas * which was implemented in March, 1982. Most such CTLs have received 4226903Sjas * this ECO. 437024Ssam */ 4411209Ssam #include "../machine/pte.h" 459799Ssam 4617117Sbloom #include "param.h" 4717117Sbloom #include "systm.h" 4817117Sbloom #include "mbuf.h" 4917117Sbloom #include "buf.h" 5017117Sbloom #include "protosw.h" 5117117Sbloom #include "socket.h" 5217117Sbloom #include "vmmac.h" 5317117Sbloom #include "errno.h" 5417117Sbloom #include "ioctl.h" 558465Sroot 568465Sroot #include "../net/if.h" 5711209Ssam #include "../net/netisr.h" 588465Sroot #include "../net/route.h" 5924793Skarels 6024793Skarels #ifdef INET 618421Swnj #include "../netinet/in.h" 628421Swnj #include "../netinet/in_systm.h" 6321779Skarels #include "../netinet/in_var.h" 648421Swnj #include "../netinet/ip.h" 6524793Skarels #endif 668465Sroot 6715794Sleres #include "../vax/cpu.h" 6811209Ssam #include "../vax/mtpr.h" 6917117Sbloom #include "if_vv.h" 7017117Sbloom #include "if_uba.h" 718465Sroot #include "../vaxuba/ubareg.h" 728465Sroot #include "../vaxuba/ubavar.h" 737024Ssam 747024Ssam /* 7520997Skarels * maximum transmission unit definition -- 7620997Skarels * you can set VVMTU at anything from 576 to 2024. 7720997Skarels * 1536 is a popular "large" value, because it is a multiple 7820997Skarels * of 512, which the trailer scheme likes. 7920997Skarels * The absolute maximum size is 2024, which is enforced. 8020997Skarels */ 8120997Skarels 8224793Skarels #define VVMTU (1536) 8320997Skarels 8420997Skarels #define VVMRU (VVMTU + 16) 8520997Skarels #define VVBUFSIZE (VVMRU + sizeof(struct vv_header)) 8620997Skarels #if VVMTU>2024 8720997Skarels #undef VVMTU 8820997Skarels #undef VVMRU 8920997Skarels #undef VVBUFSIZE 9020997Skarels #define VVBUFSIZE (2046) 9120997Skarels #define VVMRU (VVBUFSIZE - sizeof (struct vv_header)) 9220997Skarels #define VVMTU (VVMRU - 16) 937024Ssam #endif 947024Ssam 9520997Skarels /* 9620997Skarels * debugging and tracing stuff 9720997Skarels */ 9816581Skarels int vv_tracehdr = 0; /* 1 => trace headers (slowly!!) */ 997640Ssam 10020997Skarels #define vvtracehdr if (vv_tracehdr) vvprt_hdr 10121779Skarels #define vvprintf if (vs->vs_if.if_flags & IFF_DEBUG) printf 10211192Ssam 10320997Skarels /* 10420997Skarels * externals, types, etc. 10520997Skarels */ 10616581Skarels int vvprobe(), vvattach(), vvreset(), vvinit(); 10716581Skarels int vvidentify(), vvstart(), vvxint(), vvwatchdog(); 10826394Skarels int vvrint(), vvoutput(), vvioctl(); 1097024Ssam struct uba_device *vvinfo[NVV]; 1107024Ssam u_short vvstd[] = { 0 }; 1117024Ssam struct uba_driver vvdriver = 1127024Ssam { vvprobe, 0, vvattach, 0, vvstd, "vv", vvinfo }; 1137024Ssam #define VVUNIT(x) minor(x) 1147024Ssam 11520997Skarels #define LOOPBACK /* use loopback for packets meant for us */ 11620997Skarels #ifdef LOOPBACK 11720997Skarels extern struct ifnet loif; 11820997Skarels #endif 11920997Skarels 1207024Ssam /* 1217024Ssam * Software status of each interface. 1227024Ssam * 1237024Ssam * Each interface is referenced by a network interface structure, 1247024Ssam * vs_if, which the routing code uses to locate the interface. 1257024Ssam * This structure contains the output queue for the interface, its address, ... 1267024Ssam * We also have, for each interface, a UBA interface structure, which 1277024Ssam * contains information about the UNIBUS resources held by the interface: 1287024Ssam * map registers, buffered data paths, etc. Information is cached in this 1297024Ssam * structure for use by the if_uba.c routines in running the interface 1307024Ssam * efficiently. 1317024Ssam */ 1327024Ssam struct vv_softc { 1337024Ssam struct ifnet vs_if; /* network-visible interface */ 1347024Ssam struct ifuba vs_ifuba; /* UNIBUS resources */ 13526298Skarels u_short vs_host; /* this interface address */ 13611192Ssam short vs_oactive; /* is output active */ 13726903Sjas short vs_is80; /* is 80 megabit version */ 1387024Ssam short vs_olen; /* length of last output */ 13915794Sleres u_short vs_lastx; /* address of last packet sent */ 14015794Sleres u_short vs_lastr; /* address of last packet received */ 14111192Ssam short vs_tries; /* transmit current retry count */ 1427024Ssam short vs_init; /* number of ring inits */ 14320997Skarels short vs_refused; /* number of packets refused */ 14415794Sleres short vs_timeouts; /* number of transmit timeouts */ 14520997Skarels short vs_otimeout; /* number of output timeouts */ 14620997Skarels short vs_ibadf; /* number of input bad formats */ 14720997Skarels short vs_parity; /* number of parity errors on 10 meg, */ 14820997Skarels /* link data errors on 80 meg */ 1497024Ssam } vv_softc[NVV]; 1507024Ssam 15126311Skarels #define NOHOST 0xffff /* illegal host number */ 15226311Skarels 15320997Skarels /* 15420997Skarels * probe the interface to see that the registers exist, and then 15520997Skarels * cause an interrupt to find its vector 15620997Skarels */ 1577024Ssam vvprobe(reg) 1587024Ssam caddr_t reg; 1597024Ssam { 1607024Ssam register int br, cvec; 16116581Skarels register struct vvreg *addr; 1627024Ssam 1637024Ssam #ifdef lint 16415764Sleres br = 0; cvec = br; br = cvec; 1657024Ssam #endif 16616581Skarels addr = (struct vvreg *)reg; 16720997Skarels 1687024Ssam /* reset interface, enable, and wait till dust settles */ 1697024Ssam addr->vvicsr = VV_RST; 1707024Ssam addr->vvocsr = VV_RST; 17115764Sleres DELAY(100000); 17220997Skarels 1737024Ssam /* generate interrupt by doing 1 word DMA from 0 in uba space!! */ 1747024Ssam addr->vvoba = 0; /* low 16 bits */ 1757024Ssam addr->vvoea = 0; /* extended bits */ 1767024Ssam addr->vvowc = -1; /* for 1 word */ 17720997Skarels addr->vvocsr = VV_IEN | VV_DEN; /* start the DMA, with interrupt */ 1787024Ssam DELAY(100000); 17920997Skarels addr->vvocsr = VV_RST; /* clear out the CSR */ 1807024Ssam if (cvec && cvec != 0x200) 18115764Sleres cvec -= 4; /* backup so vector => receive */ 1827024Ssam return(1); 1837024Ssam } 1847024Ssam 1857024Ssam /* 1867024Ssam * Interface exists: make available by filling in network interface 1877024Ssam * record. System will initialize the interface when it is ready 1887024Ssam * to accept packets. 1897024Ssam */ 1907024Ssam vvattach(ui) 1917024Ssam struct uba_device *ui; 1927024Ssam { 19315764Sleres register struct vv_softc *vs; 1947024Ssam 19515764Sleres vs = &vv_softc[ui->ui_unit]; 1967024Ssam vs->vs_if.if_unit = ui->ui_unit; 1977024Ssam vs->vs_if.if_name = "vv"; 1987024Ssam vs->vs_if.if_mtu = VVMTU; 19921779Skarels vs->vs_if.if_flags = IFF_BROADCAST; 2007024Ssam vs->vs_if.if_init = vvinit; 20113057Ssam vs->vs_if.if_ioctl = vvioctl; 2027024Ssam vs->vs_if.if_output = vvoutput; 20311209Ssam vs->vs_if.if_reset = vvreset; 20415794Sleres vs->vs_if.if_timer = 0; 20515794Sleres vs->vs_if.if_watchdog = vvwatchdog; 20626201Skarels vs->vs_ifuba.ifu_flags = UBA_CANTWAIT | UBA_NEEDBDP; 20726903Sjas 20826903Sjas /* use flag to determine if this is proNET-80 */ 20926903Sjas vs->vs_is80 = (short)(ui->ui_flags & 01); 21026903Sjas 21112354Smo #if defined(VAX750) 21212354Smo /* don't chew up 750 bdp's */ 21312354Smo if (cpu == VAX_750 && ui->ui_unit > 0) 21412354Smo vs->vs_ifuba.ifu_flags &= ~UBA_NEEDBDP; 21512354Smo #endif 2167024Ssam if_attach(&vs->vs_if); 2177024Ssam } 2187024Ssam 2197024Ssam /* 2207024Ssam * Reset of interface after UNIBUS reset. 2217024Ssam * If interface is on specified uba, reset its state. 2227024Ssam */ 2237024Ssam vvreset(unit, uban) 2247024Ssam int unit, uban; 2257024Ssam { 2267024Ssam register struct uba_device *ui; 2277024Ssam 2287024Ssam if (unit >= NVV || (ui = vvinfo[unit]) == 0 || ui->ui_alive == 0 || 2297024Ssam ui->ui_ubanum != uban) 2307024Ssam return; 2317024Ssam printf(" vv%d", unit); 2327024Ssam vvinit(unit); 2337024Ssam } 2347024Ssam 2357024Ssam /* 2367024Ssam * Initialization of interface; clear recorded pending 2377024Ssam * operations, and reinitialize UNIBUS usage. 2387024Ssam */ 2397024Ssam vvinit(unit) 2407024Ssam int unit; 2417024Ssam { 24215764Sleres register struct vv_softc *vs; 24315764Sleres register struct uba_device *ui; 2447024Ssam register struct vvreg *addr; 24516581Skarels register int ubainfo, s; 2467024Ssam 24715764Sleres vs = &vv_softc[unit]; 24815764Sleres ui = vvinfo[unit]; 24920997Skarels 25021779Skarels if (vs->vs_if.if_addrlist == (struct ifaddr *)0) 25113057Ssam return; 25220997Skarels 2537640Ssam addr = (struct vvreg *)ui->ui_addr; 2547024Ssam if (if_ubainit(&vs->vs_ifuba, ui->ui_ubanum, 25515764Sleres sizeof (struct vv_header), (int)btoc(VVMTU)) == 0) { 25615794Sleres printf("vv%d: can't initialize, if_ubainit() failed\n", unit); 2577640Ssam vs->vs_if.if_flags &= ~IFF_UP; 2587024Ssam return; 2597024Ssam } 26020997Skarels 2617024Ssam /* 26215764Sleres * Now that the uba is set up, figure out our address and 26315764Sleres * update complete our host address. 2647640Ssam */ 26526311Skarels if ((vs->vs_host = vvidentify(unit)) == NOHOST) { 26615794Sleres vs->vs_if.if_flags &= ~IFF_UP; 26715794Sleres return; 26815794Sleres } 26926903Sjas printf("vv%d: host %u\n", unit, vs->vs_host); 27020997Skarels 2717640Ssam /* 27220997Skarels * Reset the interface, and stay in the ring 2737640Ssam */ 27420997Skarels addr->vvocsr = VV_RST; /* take over output */ 27520997Skarels addr->vvocsr = VV_CPB; /* clear packet buffer */ 27620997Skarels addr->vvicsr = VV_RST | VV_HEN; /* take over input, */ 27720997Skarels /* keep relay closed */ 27812351Smo DELAY(500000); /* let contacts settle */ 27920997Skarels 28020997Skarels vs->vs_init = 0; /* clear counters, etc. */ 28120997Skarels vs->vs_refused = 0; 28215794Sleres vs->vs_timeouts = 0; 28320997Skarels vs->vs_otimeout = 0; 28420997Skarels vs->vs_ibadf = 0; 28520997Skarels vs->vs_parity = 0; 28615794Sleres vs->vs_lastx = 256; /* an invalid address */ 28715794Sleres vs->vs_lastr = 256; /* an invalid address */ 28820997Skarels 2897640Ssam /* 2907640Ssam * Hang a receive and start any 2917640Ssam * pending writes by faking a transmit complete. 2927640Ssam */ 2937640Ssam s = splimp(); 2947640Ssam ubainfo = vs->vs_ifuba.ifu_r.ifrw_info; 29513057Ssam addr->vviba = (u_short)ubainfo; 29613057Ssam addr->vviea = (u_short)(ubainfo >> 16); 29720997Skarels addr->vviwc = -(VVBUFSIZE) >> 1; 29820997Skarels addr->vvicsr = VV_IEN | VV_HEN | VV_DEN | VV_ENB; 2997640Ssam vs->vs_oactive = 1; 30021779Skarels vs->vs_if.if_flags |= IFF_RUNNING; 3017640Ssam vvxint(unit); 3027640Ssam splx(s); 3037640Ssam } 3047640Ssam 3057640Ssam /* 30620997Skarels * Do a moderately thorough self-test in all three modes. Mostly 30720997Skarels * to keeps defective nodes off the ring, rather than to be especially 30820997Skarels * thorough. The key issue is to detect any cable breaks before joining 30920997Skarels * the ring. Return our node address on success, return -1 on failure. 31020997Skarels * 3117640Ssam */ 31220997Skarels 31320997Skarels /* the three self-test modes */ 31420997Skarels static u_short vv_modes[] = { 31520997Skarels VV_STE|VV_LPB, /* digital loopback */ 31620997Skarels VV_STE, /* analog loopback */ 31720997Skarels VV_HEN /* network mode */ 31820997Skarels }; 31920997Skarels 32011209Ssam vvidentify(unit) 32113057Ssam int unit; 32211209Ssam { 32315764Sleres register struct vv_softc *vs; 32415764Sleres register struct uba_device *ui; 3257640Ssam register struct vvreg *addr; 32616581Skarels register struct mbuf *m; 32716581Skarels register struct vv_header *v; 32820997Skarels register int ubainfo; 32920997Skarels register int i, successes, failures, waitcount; 33026311Skarels u_short shost = NOHOST; 3317640Ssam 33220997Skarels vs = &vv_softc[unit]; 33320997Skarels ui = vvinfo[unit]; 33420997Skarels addr = (struct vvreg *)ui->ui_addr; 33520997Skarels 3367640Ssam /* 3377024Ssam * Build a multicast message to identify our address 33820997Skarels * We need do this only once, since nobody else is about to use 33920997Skarels * the intermediate transmit buffer (ifu_w.ifrw_addr) that 34020997Skarels * if_ubainit() aquired for us. 3417024Ssam */ 34211209Ssam m = m_get(M_DONTWAIT, MT_HEADER); 34315794Sleres if (m == NULL) { 34415794Sleres printf("vv%d: can't initialize, m_get() failed\n", unit); 34513057Ssam return (0); 34615794Sleres } 34711192Ssam m->m_next = 0; 3487024Ssam m->m_off = MMINOFF; 3497024Ssam m->m_len = sizeof(struct vv_header); 3507024Ssam v = mtod(m, struct vv_header *); 35111192Ssam v->vh_dhost = VV_BROADCAST; /* multicast destination address */ 3527024Ssam v->vh_shost = 0; /* will be overwritten with ours */ 3537024Ssam v->vh_version = RING_VERSION; 35420997Skarels v->vh_type = RING_DIAGNOSTICS; 3557024Ssam v->vh_info = 0; 35620997Skarels /* map xmit message into uba, copying to intermediate buffer */ 35720997Skarels vs->vs_olen = if_wubaput(&vs->vs_ifuba, m); 35820997Skarels 3597024Ssam /* 36020997Skarels * For each of the modes (digital, analog, network), go through 36120997Skarels * a self-test that requires me to send VVIDENTSUCC good packets 36220997Skarels * in VVIDENTRETRY attempts. Use broadcast destination to find out 36320997Skarels * who I am, then use this as my address to check my address match 36420997Skarels * logic. Only data checked is the vh_type field. 3657024Ssam */ 3667640Ssam 36720997Skarels for (i = 0; i < 3; i++) { 36820997Skarels successes = 0; /* clear successes for this mode */ 36920997Skarels failures = 0; /* and clear failures, too */ 3707640Ssam 37120997Skarels /* take over device, and leave ring */ 37220997Skarels addr->vvicsr = VV_RST; 37320997Skarels addr->vvocsr = VV_RST; 37420997Skarels addr->vvicsr = vv_modes[i]; /* test mode */ 37520997Skarels 37620997Skarels /* 37720997Skarels * let the flag and token timers pop so that the init ring bit 37820997Skarels * will be allowed to work, by waiting about 1 second 37920997Skarels */ 38020997Skarels DELAY(1000000L); 38120997Skarels 38220997Skarels /* 38320997Skarels * retry loop 38420997Skarels */ 38520997Skarels while ((successes < VVIDENTSUCC) && (failures < VVIDENTRETRY)) 38620997Skarels { 38720997Skarels /* start a receive */ 38820997Skarels ubainfo = vs->vs_ifuba.ifu_r.ifrw_info; 38920997Skarels addr->vvicsr = VV_RST | vv_modes[i]; /* abort last */ 39020997Skarels addr->vviba = (u_short) ubainfo; 39120997Skarels addr->vviea = (u_short) (ubainfo >> 16); 39220997Skarels addr->vviwc = -(VVBUFSIZE) >> 1; 39320997Skarels addr->vvicsr = vv_modes[i] | VV_DEN | VV_ENB; 39420997Skarels 39520997Skarels /* purge stale data from BDP */ 39620997Skarels if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP) 39720997Skarels UBAPURGE(vs->vs_ifuba.ifu_uba, 39820997Skarels vs->vs_ifuba.ifu_w.ifrw_bdp); 39920997Skarels 40020997Skarels /* do a transmit */ 40120997Skarels ubainfo = vs->vs_ifuba.ifu_w.ifrw_info; 40220997Skarels addr->vvocsr = VV_RST; /* abort last try */ 40320997Skarels addr->vvoba = (u_short) ubainfo; 40420997Skarels addr->vvoea = (u_short) (ubainfo >> 16); 40520997Skarels addr->vvowc = -((vs->vs_olen + 1) >> 1); 40620997Skarels addr->vvocsr = VV_CPB | VV_DEN | VV_INR | VV_ENB; 40720997Skarels 40820997Skarels /* poll receive side for completion */ 40920997Skarels DELAY(10000); /* give it a chance */ 41020997Skarels for (waitcount = 0; waitcount < 10; waitcount++) { 41120997Skarels if (addr->vvicsr & VV_RDY) 41220997Skarels goto gotit; 41320997Skarels DELAY(1000); 41420997Skarels } 41520997Skarels failures++; /* no luck */ 41611209Ssam continue; 41720997Skarels 41820997Skarels gotit: /* we got something--is it any good? */ 41920997Skarels if ((addr->vvicsr & (VVRERR|VV_LDE)) || 42021779Skarels (addr->vvocsr & (VVXERR|VV_RFS))) { 42120997Skarels failures++; 42220997Skarels continue; 42320997Skarels } 42420997Skarels 42520997Skarels /* Purge BDP before looking at received packet */ 42620997Skarels if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP) 42720997Skarels UBAPURGE(vs->vs_ifuba.ifu_uba, 42820997Skarels vs->vs_ifuba.ifu_r.ifrw_bdp); 42924793Skarels m = if_rubaget(&vs->vs_ifuba, sizeof(struct vv_header), 43026394Skarels 0, &vs->vs_if); 43120997Skarels if (m != NULL) 43220997Skarels m_freem(m); 43320997Skarels 43420997Skarels v = (struct vv_header *)(vs->vs_ifuba.ifu_r.ifrw_addr); 43520997Skarels 43620997Skarels /* check message type, catch our node address */ 43720997Skarels if ((v->vh_type & 0xff) == RING_DIAGNOSTICS) { 43826311Skarels if (shost == NOHOST) { 43920997Skarels shost = v->vh_shost & 0xff; 44020997Skarels /* send to ourself now */ 44120997Skarels ((struct vv_header *) 44220997Skarels (vs->vs_ifuba.ifu_r.ifrw_addr)) 44320997Skarels ->vh_dhost = shost; 44420997Skarels } 44520997Skarels successes++; 44626903Sjas } else { 44726903Sjas failures++; 44820997Skarels } 44926903Sjas v->vh_type = 0; /* clear to check again */ 45011192Ssam } 45120997Skarels 45220997Skarels if (failures >= VVIDENTRETRY) 45320997Skarels { 45420997Skarels printf("vv%d: failed self-test after %d tries \ 45520997Skarels in %s mode\n", 45620997Skarels unit, VVIDENTRETRY, i == 0 ? "digital loopback" : 45720997Skarels (i == 1 ? "analog loopback" : "network")); 45820997Skarels printf("vv%d: icsr = %b, ocsr = %b\n", 45920997Skarels unit, 0xffff & addr->vvicsr, VV_IBITS, 46020997Skarels 0xffff & addr->vvocsr, VV_OBITS); 46120997Skarels addr->vvicsr = VV_RST; /* kill the sick board */ 46220997Skarels addr->vvocsr = VV_RST; 46326311Skarels shost = NOHOST; 46420997Skarels goto done; 46520997Skarels } 46611192Ssam } 46720997Skarels 46820997Skarels done: 46920997Skarels /* deallocate mbuf used for send packet (won't be one, anyways) */ 47015794Sleres if (vs->vs_ifuba.ifu_xtofree) { 4717024Ssam m_freem(vs->vs_ifuba.ifu_xtofree); 47215794Sleres vs->vs_ifuba.ifu_xtofree = 0; 47315794Sleres } 47420997Skarels 47520997Skarels return(shost); 4767024Ssam } 4777024Ssam 4787024Ssam /* 4797024Ssam * Start or restart output on interface. 48011192Ssam * If interface is active, this is a retransmit, so just 48111192Ssam * restuff registers and go. 4827024Ssam * If interface is not already active, get another datagram 4837024Ssam * to send off of the interface queue, and map it to the interface 4847024Ssam * before starting the output. 4857024Ssam */ 4867024Ssam vvstart(dev) 4877024Ssam dev_t dev; 4887024Ssam { 48916581Skarels register struct uba_device *ui; 49015764Sleres register struct vv_softc *vs; 4917024Ssam register struct vvreg *addr; 49216581Skarels register struct mbuf *m; 49316581Skarels register int unit, ubainfo, dest, s; 4947024Ssam 49516581Skarels unit = VVUNIT(dev); 49615764Sleres ui = vvinfo[unit]; 49715764Sleres vs = &vv_softc[unit]; 4987024Ssam if (vs->vs_oactive) 4997024Ssam goto restart; 5007024Ssam /* 5017024Ssam * Not already active: dequeue another request 5027024Ssam * and map it to the UNIBUS. If no more requests, 5037024Ssam * just return. 5047024Ssam */ 50515794Sleres s = splimp(); 5067024Ssam IF_DEQUEUE(&vs->vs_if.if_snd, m); 50715794Sleres splx(s); 50811209Ssam if (m == NULL) { 5097024Ssam vs->vs_oactive = 0; 5107024Ssam return; 5117024Ssam } 5127024Ssam dest = mtod(m, struct vv_header *)->vh_dhost; 5137024Ssam vs->vs_olen = if_wubaput(&vs->vs_ifuba, m); 5147024Ssam vs->vs_lastx = dest; 5157024Ssam restart: 5167024Ssam /* 5177024Ssam * Have request mapped to UNIBUS for transmission. 51815794Sleres * Purge any stale data from this BDP, and start the output. 51915794Sleres * 52015794Sleres * Make sure this packet will fit in the interface. 5217024Ssam */ 52220997Skarels if (vs->vs_olen > VVBUFSIZE) { 52320997Skarels printf("vv%d vs_olen: %d > VVBUFSIZE\n", unit, vs->vs_olen); 52411192Ssam panic("vvdriver vs_olen botch"); 52511192Ssam } 52620997Skarels 52720997Skarels vs->vs_if.if_timer = VVTIMEOUT; 52820997Skarels vs->vs_oactive = 1; 52920997Skarels 53020997Skarels /* ship it */ 5317024Ssam if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP) 5327024Ssam UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_w.ifrw_bdp); 5337024Ssam addr = (struct vvreg *)ui->ui_addr; 5347024Ssam ubainfo = vs->vs_ifuba.ifu_w.ifrw_info; 5357024Ssam addr->vvoba = (u_short) ubainfo; 5367024Ssam addr->vvoea = (u_short) (ubainfo >> 16); 5377024Ssam addr->vvowc = -((vs->vs_olen + 1) >> 1); 53820997Skarels addr->vvowc = -((vs->vs_olen + 1) >> 1); /* extra byte is garbage */ 53920997Skarels if (addr->vvocsr & VV_NOK) 54020997Skarels vs->vs_init++; /* count ring inits */ 5417024Ssam addr->vvocsr = VV_IEN | VV_CPB | VV_DEN | VV_INR | VV_ENB; 5427024Ssam } 5437024Ssam 5447024Ssam /* 54520997Skarels * proNET transmit interrupt 5467024Ssam * Start another output if more data to send. 5477024Ssam */ 5487024Ssam vvxint(unit) 5497024Ssam int unit; 5507024Ssam { 55115764Sleres register struct uba_device *ui; 55215764Sleres register struct vv_softc *vs; 5537024Ssam register struct vvreg *addr; 5547024Ssam register int oc; 5557024Ssam 55615764Sleres ui = vvinfo[unit]; 55715764Sleres vs = &vv_softc[unit]; 55815794Sleres vs->vs_if.if_timer = 0; 5597024Ssam addr = (struct vvreg *)ui->ui_addr; 5607024Ssam oc = 0xffff & (addr->vvocsr); 5617024Ssam if (vs->vs_oactive == 0) { 56220997Skarels vvprintf("vv%d: stray interrupt vvocsr = %b\n", unit, 56315764Sleres oc, VV_OBITS); 5647024Ssam return; 5657024Ssam } 56620997Skarels 56720997Skarels /* 56820997Skarels * we retransmit on soft error 56920997Skarels * TODO: sort retransmits to end of queue if possible! 57020997Skarels */ 57120997Skarels if (oc & (VV_OPT | VV_RFS)) { 57211192Ssam if (vs->vs_tries++ < VVRETRY) { 5737024Ssam if (oc & VV_OPT) 57420997Skarels vs->vs_otimeout++; 57520997Skarels if (oc & VV_RFS) { 57620997Skarels vs->vs_if.if_collisions++; 57720997Skarels vs->vs_refused++; 57820997Skarels } 57911192Ssam vvstart(unit); /* restart this message */ 5807024Ssam return; 5817024Ssam } 5827024Ssam } 5837024Ssam vs->vs_if.if_opackets++; 5847024Ssam vs->vs_oactive = 0; 5857024Ssam vs->vs_tries = 0; 58620997Skarels 5877024Ssam if (oc & VVXERR) { 5887024Ssam vs->vs_if.if_oerrors++; 58920997Skarels vvprintf("vv%d: error vvocsr = %b\n", unit, 0xffff & oc, 59015764Sleres VV_OBITS); 5917024Ssam } 5927024Ssam if (vs->vs_ifuba.ifu_xtofree) { 5937024Ssam m_freem(vs->vs_ifuba.ifu_xtofree); 5947024Ssam vs->vs_ifuba.ifu_xtofree = 0; 5957024Ssam } 5967024Ssam vvstart(unit); 5977024Ssam } 5987024Ssam 5997024Ssam /* 60015794Sleres * Transmit watchdog timer routine. 60115794Sleres * This routine gets called when we lose a transmit interrupt. 60215794Sleres * The best we can do is try to restart output. 60315794Sleres */ 60415794Sleres vvwatchdog(unit) 60515794Sleres int unit; 60615794Sleres { 60715794Sleres register struct vv_softc *vs; 60815794Sleres register int s; 60915794Sleres 61015794Sleres vs = &vv_softc[unit]; 61120997Skarels vvprintf("vv%d: lost a transmit interrupt.\n", unit); 61215794Sleres vs->vs_timeouts++; 61315794Sleres s = splimp(); 61415794Sleres vvstart(unit); 61515794Sleres splx(s); 61615794Sleres } 61715794Sleres 61815794Sleres /* 61920997Skarels * proNET interface receiver interrupt. 6207024Ssam * If input error just drop packet. 62115764Sleres * Otherwise purge input buffered data path and examine 6227024Ssam * packet to determine type. If can't determine length 62315764Sleres * from type, then have to drop packet. Otherwise decapsulate 6247024Ssam * packet based on type and pass to type specific higher-level 6257024Ssam * input routine. 6267024Ssam */ 6277024Ssam vvrint(unit) 6287024Ssam int unit; 6297024Ssam { 63015764Sleres register struct vv_softc *vs; 63116581Skarels register struct vvreg *addr; 6327024Ssam register struct vv_header *vv; 6337024Ssam register struct ifqueue *inq; 63416581Skarels register struct mbuf *m; 63515794Sleres int ubainfo, len, off, s; 6367640Ssam short resid; 6377024Ssam 63815764Sleres vs = &vv_softc[unit]; 63916581Skarels vs->vs_if.if_ipackets++; 64015764Sleres addr = (struct vvreg *)vvinfo[unit]->ui_addr; 64120997Skarels 6427024Ssam /* 64326903Sjas * Purge BDP 6447024Ssam */ 6457024Ssam if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP) 6467024Ssam UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_r.ifrw_bdp); 64720997Skarels 64820997Skarels /* 64920997Skarels * receive errors? 65020997Skarels */ 6517024Ssam if (addr->vvicsr & VVRERR) { 65220997Skarels vvprintf("vv%d: receive error, vvicsr = %b\n", unit, 65320997Skarels 0xffff&(addr->vvicsr), VV_IBITS); 65420997Skarels if (addr->vvicsr & VV_BDF) 65520997Skarels vs->vs_ibadf++; 6567640Ssam goto dropit; 6577024Ssam } 6587640Ssam 6597024Ssam /* 66020997Skarels * parity errors? 66120997Skarels */ 66220997Skarels if (addr->vvicsr & VV_LDE) { 66320997Skarels /* we don't have to clear it because the receive command */ 66420997Skarels /* writes 0 to parity bit */ 66520997Skarels vs->vs_parity++; 66626903Sjas 66720997Skarels /* 66820997Skarels * only on 10 megabit proNET is VV_LDE an end-to-end parity 66920997Skarels * bit. On 80 megabit, it returns to the intended use of 67020997Skarels * node-to-node parity. End-to-end parity errors on 80 megabit 67120997Skarels * give VV_BDF. 67220997Skarels */ 67326903Sjas if (vs->vs_is80 == 0) 67426903Sjas goto dropit; 67520997Skarels } 67620997Skarels 67720997Skarels /* 67820997Skarels * Get packet length from residual word count 6797640Ssam * 6807640Ssam * Compute header offset if trailer protocol 6817640Ssam * 6827640Ssam * Pull packet off interface. Off is nonzero if packet 6837640Ssam * has trailing header; if_rubaget will then force this header 6847640Ssam * information to be at the front. The vh_info field 6857640Ssam * carries the offset to the trailer data in trailer 6867640Ssam * format packets. 6877024Ssam */ 6887640Ssam vv = (struct vv_header *)(vs->vs_ifuba.ifu_r.ifrw_addr); 68911192Ssam vvtracehdr("vi", vv); 69020997Skarels resid = addr->vviwc & 01777; /* only low 10 bits valid */ 6917640Ssam if (resid) 69220997Skarels resid |= 0176000; /* high 6 bits are undefined */ 69320997Skarels len = ((VVBUFSIZE >> 1) + resid) << 1; 6947640Ssam len -= sizeof(struct vv_header); 69520997Skarels 69620997Skarels if ((addr->vvicsr & VV_DPR) || len > VVMRU || len <= 0) { 69720997Skarels vvprintf("vv%d: len too long or short, \ 69820997Skarels len = %d, vvicsr = %b\n", 69915794Sleres unit, len, 0xffff&(addr->vvicsr), VV_IBITS); 7007640Ssam goto dropit; 70115794Sleres } 70220997Skarels 70320997Skarels /* check the protocol header version */ 70420997Skarels if (vv->vh_version != RING_VERSION) { 70520997Skarels vvprintf("vv%d: bad protocol header version %d\n", 70620997Skarels unit, vv->vh_version & 0xff); 70720997Skarels goto dropit; 70820997Skarels } 70920997Skarels 7107640Ssam #define vvdataaddr(vv, off, type) ((type)(((caddr_t)((vv)+1)+(off)))) 71126950Sjas if (vv->vh_type == RING_TRAILER ) { 71228954Skarels off = ntohs((u_short)vv->vh_info); 71315794Sleres if (off > VVMTU) { 71420997Skarels vvprintf("vv%d: off > VVMTU, off = %d, vvicsr = %b\n", 71515794Sleres unit, off, 0xffff&(addr->vvicsr), VV_IBITS); 7167640Ssam goto dropit; 71715794Sleres } 71826903Sjas vv->vh_type = ntohs(*vvdataaddr(vv, off, u_short *)); 71926950Sjas resid = ntohs(*(vvdataaddr(vv, off+sizeof(u_short), u_short *))); 72015794Sleres if (off + resid > len) { 72120997Skarels vvprintf("vv%d: trailer packet too short\n", unit); 72220997Skarels vvprintf("vv%d: off = %d, resid = %d, vvicsr = %b\n", 72315794Sleres unit, off, resid, 72415794Sleres 0xffff&(addr->vvicsr), VV_IBITS); 7257640Ssam goto dropit; 72615794Sleres } 7277640Ssam len = off + resid; 72811209Ssam } else 7297640Ssam off = 0; 73020997Skarels 73115794Sleres if (len == 0) { 73220997Skarels vvprintf("vv%d: len is zero, vvicsr = %b\n", unit, 73315794Sleres 0xffff&(addr->vvicsr), VV_IBITS); 7347640Ssam goto dropit; 73515794Sleres } 73620997Skarels 73724793Skarels m = if_rubaget(&vs->vs_ifuba, len, off, &vs->vs_if); 73815794Sleres if (m == NULL) { 73920997Skarels vvprintf("vv%d: if_rubaget() failed, vvicsr = %b\n", unit, 74015794Sleres 0xffff&(addr->vvicsr), VV_IBITS); 7417640Ssam goto dropit; 74215794Sleres } 7437640Ssam if (off) { 74424793Skarels struct ifnet *ifp; 74524793Skarels 74624793Skarels ifp = *(mtod(m, struct ifnet **)); 74724793Skarels m->m_off += 2 * sizeof (u_short); 74824793Skarels m->m_len -= 2 * sizeof (u_short); 74924793Skarels *(mtod(m, struct ifnet **)) = ifp; 7507640Ssam } 75111192Ssam 75215794Sleres /* Keep track of source address of this packet */ 75315794Sleres vs->vs_lastr = vv->vh_shost; 75420997Skarels 75511192Ssam /* 75615764Sleres * Demultiplex on packet type 75711192Ssam */ 7587024Ssam switch (vv->vh_type) { 75911192Ssam 7607024Ssam #ifdef INET 7617024Ssam case RING_IP: 7627024Ssam schednetisr(NETISR_IP); 7637024Ssam inq = &ipintrq; 7647024Ssam break; 7657024Ssam #endif 7667024Ssam default: 76720997Skarels vvprintf("vv%d: unknown pkt type 0x%x\n", unit, vv->vh_type); 7687640Ssam m_freem(m); 7697024Ssam goto setup; 7707024Ssam } 77115794Sleres s = splimp(); 7727640Ssam if (IF_QFULL(inq)) { 7737640Ssam IF_DROP(inq); 7747640Ssam m_freem(m); 77511209Ssam } else 7767640Ssam IF_ENQUEUE(inq, m); 77715764Sleres 77815794Sleres splx(s); 7797024Ssam /* 78015764Sleres * Reset for the next packet. 7817024Ssam */ 78215764Sleres setup: 78315764Sleres ubainfo = vs->vs_ifuba.ifu_r.ifrw_info; 78415764Sleres addr->vviba = (u_short) ubainfo; 78515764Sleres addr->vviea = (u_short) (ubainfo >> 16); 78620997Skarels addr->vviwc = -(VVBUFSIZE) >> 1; 78720997Skarels addr->vvicsr = VV_HEN | VV_IEN | VV_DEN | VV_ENB; 78815764Sleres return; 78911192Ssam 79011192Ssam /* 79111209Ssam * Drop packet on floor -- count them!! 79211192Ssam */ 7937640Ssam dropit: 7947640Ssam vs->vs_if.if_ierrors++; 7957640Ssam goto setup; 7967024Ssam } 7977024Ssam 7987024Ssam /* 79920997Skarels * proNET output routine. 8007024Ssam * Encapsulate a packet of type family for the local net. 8017024Ssam * Use trailer local net encapsulation if enough data in first 8027024Ssam * packet leaves a multiple of 512 bytes of data in remainder. 8037024Ssam */ 8047024Ssam vvoutput(ifp, m0, dst) 8057024Ssam struct ifnet *ifp; 8067024Ssam struct mbuf *m0; 8077024Ssam struct sockaddr *dst; 8087024Ssam { 80916581Skarels register struct mbuf *m; 8107024Ssam register struct vv_header *vv; 8117640Ssam register int off; 81216581Skarels register int unit; 81316581Skarels register struct vvreg *addr; 81416581Skarels register struct vv_softc *vs; 81516581Skarels register int s; 81616581Skarels int type, dest, error; 8177024Ssam 81816581Skarels m = m0; 81916581Skarels unit = ifp->if_unit; 82016581Skarels addr = (struct vvreg *)vvinfo[unit]->ui_addr; 82116581Skarels vs = &vv_softc[unit]; 82220997Skarels 82316581Skarels /* 82420997Skarels * Check to see if the input side has wedged due the UBA 82520997Skarels * vectoring through 0. 82616581Skarels * 82716581Skarels * We are lower than device ipl when we enter this routine, 82816581Skarels * so if the interface is ready with an input packet then 82916581Skarels * an input interrupt must have slipped through the cracks. 83016581Skarels * 83116581Skarels * Avoid the race with an input interrupt by watching to see 83216581Skarels * if any packets come in. 83316581Skarels */ 83416581Skarels s = vs->vs_if.if_ipackets; 83516581Skarels if (addr->vvicsr & VV_RDY && s == vs->vs_if.if_ipackets) { 83620997Skarels vvprintf("vv%d: lost a receive interrupt, icsr = %b\n", 83716581Skarels unit, 0xffff&(addr->vvicsr), VV_IBITS); 83816581Skarels s = splimp(); 83916581Skarels vvrint(unit); 84016581Skarels splx(s); 84116581Skarels } 84216581Skarels 8437024Ssam switch (dst->sa_family) { 84411192Ssam 8457024Ssam #ifdef INET 84615764Sleres case AF_INET: 84721779Skarels if (in_broadcast(((struct sockaddr_in *)dst)->sin_addr)) 84821779Skarels dest = VV_BROADCAST; 84921779Skarels else 85021779Skarels dest = in_lnaof(((struct sockaddr_in *)dst)->sin_addr); 85120997Skarels #ifdef LOOPBACK 85221779Skarels if (dest == vs->vs_host && (loif.if_flags & IFF_UP)) 85321779Skarels return (looutput(&loif, m0, dst)); 85420997Skarels #endif LOOPBACK 85521779Skarels if (dest >= 0x100) { 8567640Ssam error = EPERM; 8577640Ssam goto bad; 8587640Ssam } 8597640Ssam off = ntohs((u_short)mtod(m, struct ip *)->ip_len) - m->m_len; 86020997Skarels /* 86120997Skarels * Trailerize, if the configuration allows it. 86220997Skarels * TODO: Need per host negotiation. 86320997Skarels */ 86413090Ssam if ((ifp->if_flags & IFF_NOTRAILERS) == 0) 86513090Ssam if (off > 0 && (off & 0x1ff) == 0 && 8667640Ssam m->m_off >= MMINOFF + 2 * sizeof (u_short)) { 86726950Sjas type = RING_TRAILER; 8687640Ssam m->m_off -= 2 * sizeof (u_short); 8697640Ssam m->m_len += 2 * sizeof (u_short); 87028954Skarels *mtod(m, u_short *) = htons((short)RING_IP); 87128954Skarels *(mtod(m, u_short *) + 1) = htons((u_short)m->m_len); 8727640Ssam goto gottrailertype; 8737640Ssam } 8747024Ssam type = RING_IP; 8757024Ssam off = 0; 8767024Ssam goto gottype; 8777024Ssam #endif 8787024Ssam default: 87916581Skarels printf("vv%d: can't handle af%d\n", unit, dst->sa_family); 8807640Ssam error = EAFNOSUPPORT; 8817640Ssam goto bad; 8827024Ssam } 8837024Ssam 8847024Ssam gottrailertype: 8857024Ssam /* 8867024Ssam * Packet to be sent as trailer: move first packet 8877024Ssam * (control information) to end of chain. 8887024Ssam */ 8897024Ssam while (m->m_next) 8907024Ssam m = m->m_next; 8917024Ssam m->m_next = m0; 8927024Ssam m = m0->m_next; 8937024Ssam m0->m_next = 0; 8947024Ssam m0 = m; 8957024Ssam gottype: 8967024Ssam /* 8977024Ssam * Add local net header. If no space in first mbuf, 8987024Ssam * allocate another. 8997024Ssam */ 9007024Ssam if (m->m_off > MMAXOFF || 9017024Ssam MMINOFF + sizeof (struct vv_header) > m->m_off) { 90211209Ssam m = m_get(M_DONTWAIT, MT_HEADER); 90311209Ssam if (m == NULL) { 9047640Ssam error = ENOBUFS; 9057640Ssam goto bad; 9067024Ssam } 9077024Ssam m->m_next = m0; 9087024Ssam m->m_off = MMINOFF; 9097024Ssam m->m_len = sizeof (struct vv_header); 9107024Ssam } else { 9117024Ssam m->m_off -= sizeof (struct vv_header); 9127024Ssam m->m_len += sizeof (struct vv_header); 9137024Ssam } 9147024Ssam vv = mtod(m, struct vv_header *); 91521779Skarels vv->vh_shost = vs->vs_host; 91621779Skarels vv->vh_dhost = dest; 9177024Ssam vv->vh_version = RING_VERSION; 9187024Ssam vv->vh_type = type; 91928954Skarels vv->vh_info = htons((u_short)off); 92011192Ssam vvtracehdr("vo", vv); 9217024Ssam 9227024Ssam /* 9237024Ssam * Queue message on interface, and start output if interface 9247024Ssam * not yet active. 9257024Ssam */ 9267024Ssam s = splimp(); 9277640Ssam if (IF_QFULL(&ifp->if_snd)) { 9287640Ssam IF_DROP(&ifp->if_snd); 9297640Ssam error = ENOBUFS; 9307640Ssam goto qfull; 9317640Ssam } 9327024Ssam IF_ENQUEUE(&ifp->if_snd, m); 93316581Skarels if (vs->vs_oactive == 0) 93416581Skarels vvstart(unit); 9357024Ssam splx(s); 9367640Ssam return (0); 9377640Ssam qfull: 9387640Ssam m0 = m; 9397640Ssam splx(s); 9407640Ssam bad: 9417640Ssam m_freem(m0); 9427640Ssam return(error); 9437024Ssam } 9447024Ssam 9457024Ssam /* 94613057Ssam * Process an ioctl request. 94713057Ssam */ 94813057Ssam vvioctl(ifp, cmd, data) 94913057Ssam register struct ifnet *ifp; 95013057Ssam int cmd; 95113057Ssam caddr_t data; 95213057Ssam { 95321779Skarels struct ifaddr *ifa = (struct ifaddr *) data; 95421779Skarels int s = splimp(), error = 0; 95513057Ssam 95613057Ssam switch (cmd) { 95713057Ssam 95813057Ssam case SIOCSIFADDR: 95924793Skarels ifp->if_flags |= IFF_UP; 96021779Skarels if ((ifp->if_flags & IFF_RUNNING) == 0) 96113057Ssam vvinit(ifp->if_unit); 96226903Sjas /* 96326903Sjas * Did self-test succeed? 96426903Sjas */ 96526903Sjas if ((ifp->if_flags & IFF_UP) == 0) 96626903Sjas error = ENETDOWN; 96721779Skarels /* 96821779Skarels * Attempt to check agreement of protocol address 96921779Skarels * and board address. 97021779Skarels */ 97121779Skarels switch (ifa->ifa_addr.sa_family) { 97221779Skarels case AF_INET: 97321779Skarels if (in_lnaof(IA_SIN(ifa)->sin_addr) != 97421779Skarels vv_softc[ifp->if_unit].vs_host) 97525194Skarels error = EADDRNOTAVAIL; 97621779Skarels break; 97721779Skarels } 97813057Ssam break; 97913057Ssam 98013057Ssam default: 98113057Ssam error = EINVAL; 98213057Ssam } 98313057Ssam splx(s); 98421779Skarels return (error); 98513057Ssam } 98625190Skarels 98725190Skarels /* 98825190Skarels * vvprt_hdr(s, v) print the local net header in "v" 98925190Skarels * with title is "s" 99025190Skarels */ 99125190Skarels vvprt_hdr(s, v) 99225190Skarels char *s; 99325190Skarels register struct vv_header *v; 99425190Skarels { 99525190Skarels printf("%s: dsvti: 0x%x 0x%x 0x%x 0x%x 0x%x\n", 99625190Skarels s, 99725190Skarels 0xff & (int)(v->vh_dhost), 0xff & (int)(v->vh_shost), 99825190Skarels 0xff & (int)(v->vh_version), 0xff & (int)(v->vh_type), 99925190Skarels 0xffff & (int)(v->vh_info)); 100025190Skarels } 100126903Sjas #endif NVV 1002