123305Smckusick /* 229372Smckusick * Copyright (c) 1982, 1986 Regents of the University of California. 335328Sbostic * All rights reserved. 423305Smckusick * 535328Sbostic * Redistribution and use in source and binary forms are permitted 635328Sbostic * provided that the above copyright notice and this paragraph are 735328Sbostic * duplicated in all such forms and that any documentation, 835328Sbostic * advertising materials, and other materials related to such 935328Sbostic * distribution and use acknowledge that the software was developed 1035328Sbostic * by the University of California, Berkeley. The name of the 1135328Sbostic * University may not be used to endorse or promote products derived 1235328Sbostic * from this software without specific prior written permission. 1335328Sbostic * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 1435328Sbostic * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 1535328Sbostic * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 1635328Sbostic * 17*35805Skarels * @(#)if_vv.c 7.3 (Berkeley) 10/12/88 1823305Smckusick */ 197024Ssam 209799Ssam #include "vv.h" 2125275Sbloom #if NVV > 0 2211192Ssam 237024Ssam /* 24*35805Skarels * Proteon ProNET-10 and ProNET-80 token ring driver. 2520997Skarels * The name of this device driver derives from the old MIT 2620997Skarels * name of V2LNI for the proNET hardware, would would abbreviate 27*35805Skarels * to "v2", but this won't work right in config. Thus the name is "vv". 2811192Ssam * 29*35805Skarels * This driver is compatible with the Unibus ProNET 10 megabit and 3020997Skarels * 80 megabit token ring interfaces (models p1000 and p1080). 3126903Sjas * A unit may be marked as 80 megabit using "flags 1" in the 3226903Sjas * config file. 3320997Skarels * 34*35805Skarels * This driver is also compatible with the Q-bus ProNET 10 megabit and 35*35805Skarels * 80 megabit token ring interfaces (models p1100 and p1180), but 36*35805Skarels * only on a MicroVAX-II or MicroVAX-III. No attempt is made to 37*35805Skarels * support the MicroVAX-I. 38*35805Skarels * 3926950Sjas * TRAILERS: This driver has a new implementation of trailers that 4026950Sjas * is at least a tolerable neighbor on the ring. The offset is not 4126950Sjas * stored in the protocol type, but instead only in the vh_info 4226950Sjas * field. Also, the vh_info field, and the two shorts before the 4326950Sjas * trailing header, are in network byte order, not VAX byte order. 4420997Skarels * 4526950Sjas * Of course, nothing but BSD UNIX supports trailers on ProNET. 46*35805Skarels * If you need interoperability with anything else (like the p4200), 47*35805Skarels * turn off trailers using the -trailers option to /etc/ifconfig! 4826950Sjas * 4926903Sjas * HARDWARE COMPATABILITY: This driver prefers that the HSBU (p1001) 5020997Skarels * have a serial number >= 040, which is about March, 1982. Older 5126903Sjas * HSBUs do not carry across 64kbyte boundaries. They can be supported 5226903Sjas * by adding "| UBA_NEED16" to the vs_ifuba.ifu_flags initialization 5326903Sjas * in vvattach(). 5426903Sjas * 5526903Sjas * The old warning about use without Wire Centers applies only to CTL 5626903Sjas * (p1002) cards with serial <= 057, which have not received ECO 176-743, 5726903Sjas * which was implemented in March, 1982. Most such CTLs have received 5826903Sjas * this ECO. 597024Ssam */ 6011209Ssam #include "../machine/pte.h" 619799Ssam 6217117Sbloom #include "param.h" 6317117Sbloom #include "systm.h" 6417117Sbloom #include "mbuf.h" 6517117Sbloom #include "buf.h" 6617117Sbloom #include "protosw.h" 6717117Sbloom #include "socket.h" 6817117Sbloom #include "vmmac.h" 6917117Sbloom #include "errno.h" 7017117Sbloom #include "ioctl.h" 718465Sroot 728465Sroot #include "../net/if.h" 7311209Ssam #include "../net/netisr.h" 748465Sroot #include "../net/route.h" 7524793Skarels 7624793Skarels #ifdef INET 778421Swnj #include "../netinet/in.h" 788421Swnj #include "../netinet/in_systm.h" 7921779Skarels #include "../netinet/in_var.h" 808421Swnj #include "../netinet/ip.h" 8124793Skarels #endif 828465Sroot 8315794Sleres #include "../vax/cpu.h" 8411209Ssam #include "../vax/mtpr.h" 8517117Sbloom #include "if_vv.h" 8617117Sbloom #include "if_uba.h" 878465Sroot #include "../vaxuba/ubareg.h" 888465Sroot #include "../vaxuba/ubavar.h" 897024Ssam 907024Ssam /* 9120997Skarels * maximum transmission unit definition -- 92*35805Skarels * you can set VVMTU at anything from 576 to 2036. 9320997Skarels * 1536 is a popular "large" value, because it is a multiple 9420997Skarels * of 512, which the trailer scheme likes. 95*35805Skarels * The absolute maximum size is 2036, which is enforced. 9620997Skarels */ 9720997Skarels 98*35805Skarels #define VVMTU (2036) 9920997Skarels 100*35805Skarels #define VVMRU (VVMTU + (2 * sizeof(u_short))) 10120997Skarels #define VVBUFSIZE (VVMRU + sizeof(struct vv_header)) 102*35805Skarels #if VVMTU>2036 10320997Skarels #undef VVMTU 10420997Skarels #undef VVMRU 10520997Skarels #undef VVBUFSIZE 10620997Skarels #define VVBUFSIZE (2046) 10720997Skarels #define VVMRU (VVBUFSIZE - sizeof (struct vv_header)) 108*35805Skarels #define VVMTU (VVMRU - (2 * sizeof(u_short))) 1097024Ssam #endif 1107024Ssam 11120997Skarels /* 11220997Skarels * debugging and tracing stuff 11320997Skarels */ 11416581Skarels int vv_tracehdr = 0; /* 1 => trace headers (slowly!!) */ 1157640Ssam 11620997Skarels #define vvtracehdr if (vv_tracehdr) vvprt_hdr 11721779Skarels #define vvprintf if (vs->vs_if.if_flags & IFF_DEBUG) printf 11811192Ssam 11920997Skarels /* 12020997Skarels * externals, types, etc. 12120997Skarels */ 12216581Skarels int vvprobe(), vvattach(), vvreset(), vvinit(); 12316581Skarels int vvidentify(), vvstart(), vvxint(), vvwatchdog(); 12426394Skarels int vvrint(), vvoutput(), vvioctl(); 1257024Ssam struct uba_device *vvinfo[NVV]; 1267024Ssam u_short vvstd[] = { 0 }; 1277024Ssam struct uba_driver vvdriver = 1287024Ssam { vvprobe, 0, vvattach, 0, vvstd, "vv", vvinfo }; 1297024Ssam #define VVUNIT(x) minor(x) 1307024Ssam 13120997Skarels #define LOOPBACK /* use loopback for packets meant for us */ 13220997Skarels #ifdef LOOPBACK 13320997Skarels extern struct ifnet loif; 13420997Skarels #endif 13520997Skarels 1367024Ssam /* 1377024Ssam * Software status of each interface. 1387024Ssam * 1397024Ssam * Each interface is referenced by a network interface structure, 1407024Ssam * vs_if, which the routing code uses to locate the interface. 1417024Ssam * This structure contains the output queue for the interface, its address, ... 1427024Ssam * We also have, for each interface, a UBA interface structure, which 1437024Ssam * contains information about the UNIBUS resources held by the interface: 1447024Ssam * map registers, buffered data paths, etc. Information is cached in this 1457024Ssam * structure for use by the if_uba.c routines in running the interface 1467024Ssam * efficiently. 1477024Ssam */ 1487024Ssam struct vv_softc { 1497024Ssam struct ifnet vs_if; /* network-visible interface */ 1507024Ssam struct ifuba vs_ifuba; /* UNIBUS resources */ 15126298Skarels u_short vs_host; /* this interface address */ 15211192Ssam short vs_oactive; /* is output active */ 15326903Sjas short vs_is80; /* is 80 megabit version */ 1547024Ssam short vs_olen; /* length of last output */ 15515794Sleres u_short vs_lastx; /* address of last packet sent */ 15615794Sleres u_short vs_lastr; /* address of last packet received */ 15711192Ssam short vs_tries; /* transmit current retry count */ 1587024Ssam short vs_init; /* number of ring inits */ 15920997Skarels short vs_refused; /* number of packets refused */ 16015794Sleres short vs_timeouts; /* number of transmit timeouts */ 16120997Skarels short vs_otimeout; /* number of output timeouts */ 16220997Skarels short vs_ibadf; /* number of input bad formats */ 16320997Skarels short vs_parity; /* number of parity errors on 10 meg, */ 16420997Skarels /* link data errors on 80 meg */ 1657024Ssam } vv_softc[NVV]; 1667024Ssam 16726311Skarels #define NOHOST 0xffff /* illegal host number */ 16826311Skarels 16920997Skarels /* 17020997Skarels * probe the interface to see that the registers exist, and then 17120997Skarels * cause an interrupt to find its vector 17220997Skarels */ 1737024Ssam vvprobe(reg) 1747024Ssam caddr_t reg; 1757024Ssam { 1767024Ssam register int br, cvec; 17716581Skarels register struct vvreg *addr; 1787024Ssam 1797024Ssam #ifdef lint 18015764Sleres br = 0; cvec = br; br = cvec; 1817024Ssam #endif 18216581Skarels addr = (struct vvreg *)reg; 18320997Skarels 1847024Ssam /* reset interface, enable, and wait till dust settles */ 1857024Ssam addr->vvicsr = VV_RST; 1867024Ssam addr->vvocsr = VV_RST; 18715764Sleres DELAY(100000); 18820997Skarels 1897024Ssam /* generate interrupt by doing 1 word DMA from 0 in uba space!! */ 1907024Ssam addr->vvoba = 0; /* low 16 bits */ 1917024Ssam addr->vvoea = 0; /* extended bits */ 1927024Ssam addr->vvowc = -1; /* for 1 word */ 19320997Skarels addr->vvocsr = VV_IEN | VV_DEN; /* start the DMA, with interrupt */ 1947024Ssam DELAY(100000); 19520997Skarels addr->vvocsr = VV_RST; /* clear out the CSR */ 1967024Ssam if (cvec && cvec != 0x200) 19715764Sleres cvec -= 4; /* backup so vector => receive */ 1987024Ssam return(1); 1997024Ssam } 2007024Ssam 2017024Ssam /* 2027024Ssam * Interface exists: make available by filling in network interface 2037024Ssam * record. System will initialize the interface when it is ready 2047024Ssam * to accept packets. 2057024Ssam */ 2067024Ssam vvattach(ui) 2077024Ssam struct uba_device *ui; 2087024Ssam { 20915764Sleres register struct vv_softc *vs; 2107024Ssam 21115764Sleres vs = &vv_softc[ui->ui_unit]; 2127024Ssam vs->vs_if.if_unit = ui->ui_unit; 2137024Ssam vs->vs_if.if_name = "vv"; 2147024Ssam vs->vs_if.if_mtu = VVMTU; 21521779Skarels vs->vs_if.if_flags = IFF_BROADCAST; 2167024Ssam vs->vs_if.if_init = vvinit; 21713057Ssam vs->vs_if.if_ioctl = vvioctl; 2187024Ssam vs->vs_if.if_output = vvoutput; 21911209Ssam vs->vs_if.if_reset = vvreset; 22015794Sleres vs->vs_if.if_timer = 0; 22115794Sleres vs->vs_if.if_watchdog = vvwatchdog; 22226201Skarels vs->vs_ifuba.ifu_flags = UBA_CANTWAIT | UBA_NEEDBDP; 22326903Sjas 22426903Sjas /* use flag to determine if this is proNET-80 */ 22526903Sjas vs->vs_is80 = (short)(ui->ui_flags & 01); 22626903Sjas 22712354Smo #if defined(VAX750) 22812354Smo /* don't chew up 750 bdp's */ 22912354Smo if (cpu == VAX_750 && ui->ui_unit > 0) 23012354Smo vs->vs_ifuba.ifu_flags &= ~UBA_NEEDBDP; 23112354Smo #endif 2327024Ssam if_attach(&vs->vs_if); 2337024Ssam } 2347024Ssam 2357024Ssam /* 2367024Ssam * Reset of interface after UNIBUS reset. 2377024Ssam * If interface is on specified uba, reset its state. 2387024Ssam */ 2397024Ssam vvreset(unit, uban) 2407024Ssam int unit, uban; 2417024Ssam { 2427024Ssam register struct uba_device *ui; 2437024Ssam 2447024Ssam if (unit >= NVV || (ui = vvinfo[unit]) == 0 || ui->ui_alive == 0 || 2457024Ssam ui->ui_ubanum != uban) 2467024Ssam return; 2477024Ssam printf(" vv%d", unit); 2487024Ssam vvinit(unit); 2497024Ssam } 2507024Ssam 2517024Ssam /* 2527024Ssam * Initialization of interface; clear recorded pending 2537024Ssam * operations, and reinitialize UNIBUS usage. 2547024Ssam */ 2557024Ssam vvinit(unit) 2567024Ssam int unit; 2577024Ssam { 25815764Sleres register struct vv_softc *vs; 25915764Sleres register struct uba_device *ui; 2607024Ssam register struct vvreg *addr; 261*35805Skarels register int ubaaddr, s; 2627024Ssam 26315764Sleres vs = &vv_softc[unit]; 26415764Sleres ui = vvinfo[unit]; 26520997Skarels 26621779Skarels if (vs->vs_if.if_addrlist == (struct ifaddr *)0) 26713057Ssam return; 26820997Skarels 2697640Ssam addr = (struct vvreg *)ui->ui_addr; 2707024Ssam if (if_ubainit(&vs->vs_ifuba, ui->ui_ubanum, 271*35805Skarels sizeof (struct vv_header), (int)btoc(VVMRU)) == 0) { 27215794Sleres printf("vv%d: can't initialize, if_ubainit() failed\n", unit); 2737640Ssam vs->vs_if.if_flags &= ~IFF_UP; 2747024Ssam return; 2757024Ssam } 27620997Skarels 2777024Ssam /* 27815764Sleres * Now that the uba is set up, figure out our address and 27915764Sleres * update complete our host address. 2807640Ssam */ 28126311Skarels if ((vs->vs_host = vvidentify(unit)) == NOHOST) { 28215794Sleres vs->vs_if.if_flags &= ~IFF_UP; 28315794Sleres return; 28415794Sleres } 28526903Sjas printf("vv%d: host %u\n", unit, vs->vs_host); 28620997Skarels 2877640Ssam /* 28820997Skarels * Reset the interface, and stay in the ring 2897640Ssam */ 29020997Skarels addr->vvocsr = VV_RST; /* take over output */ 29120997Skarels addr->vvocsr = VV_CPB; /* clear packet buffer */ 29220997Skarels addr->vvicsr = VV_RST | VV_HEN; /* take over input, */ 29320997Skarels /* keep relay closed */ 29412351Smo DELAY(500000); /* let contacts settle */ 29520997Skarels 29620997Skarels vs->vs_init = 0; /* clear counters, etc. */ 29720997Skarels vs->vs_refused = 0; 29815794Sleres vs->vs_timeouts = 0; 29920997Skarels vs->vs_otimeout = 0; 30020997Skarels vs->vs_ibadf = 0; 30120997Skarels vs->vs_parity = 0; 30215794Sleres vs->vs_lastx = 256; /* an invalid address */ 30315794Sleres vs->vs_lastr = 256; /* an invalid address */ 30420997Skarels 3057640Ssam /* 3067640Ssam * Hang a receive and start any 3077640Ssam * pending writes by faking a transmit complete. 3087640Ssam */ 3097640Ssam s = splimp(); 310*35805Skarels ubaaddr = UBAI_ADDR(vs->vs_ifuba.ifu_r.ifrw_info); 311*35805Skarels addr->vviba = (u_short)ubaaddr; 312*35805Skarels addr->vviea = (u_short)(ubaaddr >> 16); 31320997Skarels addr->vviwc = -(VVBUFSIZE) >> 1; 31420997Skarels addr->vvicsr = VV_IEN | VV_HEN | VV_DEN | VV_ENB; 3157640Ssam vs->vs_oactive = 1; 31621779Skarels vs->vs_if.if_flags |= IFF_RUNNING; 3177640Ssam vvxint(unit); 3187640Ssam splx(s); 3197640Ssam } 3207640Ssam 3217640Ssam /* 32220997Skarels * Do a moderately thorough self-test in all three modes. Mostly 32320997Skarels * to keeps defective nodes off the ring, rather than to be especially 32420997Skarels * thorough. The key issue is to detect any cable breaks before joining 32520997Skarels * the ring. Return our node address on success, return -1 on failure. 32620997Skarels * 3277640Ssam */ 32820997Skarels 32920997Skarels /* the three self-test modes */ 33020997Skarels static u_short vv_modes[] = { 33120997Skarels VV_STE|VV_LPB, /* digital loopback */ 33220997Skarels VV_STE, /* analog loopback */ 33320997Skarels VV_HEN /* network mode */ 33420997Skarels }; 33520997Skarels 33611209Ssam vvidentify(unit) 33713057Ssam int unit; 33811209Ssam { 33915764Sleres register struct vv_softc *vs; 34015764Sleres register struct uba_device *ui; 3417640Ssam register struct vvreg *addr; 34216581Skarels register struct mbuf *m; 34316581Skarels register struct vv_header *v; 344*35805Skarels register int ubaaddr; 34520997Skarels register int i, successes, failures, waitcount; 34626311Skarels u_short shost = NOHOST; 3477640Ssam 34820997Skarels vs = &vv_softc[unit]; 34920997Skarels ui = vvinfo[unit]; 35020997Skarels addr = (struct vvreg *)ui->ui_addr; 35120997Skarels 3527640Ssam /* 3537024Ssam * Build a multicast message to identify our address 35420997Skarels * We need do this only once, since nobody else is about to use 35520997Skarels * the intermediate transmit buffer (ifu_w.ifrw_addr) that 35620997Skarels * if_ubainit() aquired for us. 3577024Ssam */ 35811209Ssam m = m_get(M_DONTWAIT, MT_HEADER); 35915794Sleres if (m == NULL) { 36015794Sleres printf("vv%d: can't initialize, m_get() failed\n", unit); 36113057Ssam return (0); 36215794Sleres } 36311192Ssam m->m_next = 0; 3647024Ssam m->m_off = MMINOFF; 3657024Ssam m->m_len = sizeof(struct vv_header); 3667024Ssam v = mtod(m, struct vv_header *); 36711192Ssam v->vh_dhost = VV_BROADCAST; /* multicast destination address */ 3687024Ssam v->vh_shost = 0; /* will be overwritten with ours */ 3697024Ssam v->vh_version = RING_VERSION; 37020997Skarels v->vh_type = RING_DIAGNOSTICS; 3717024Ssam v->vh_info = 0; 37220997Skarels /* map xmit message into uba, copying to intermediate buffer */ 37320997Skarels vs->vs_olen = if_wubaput(&vs->vs_ifuba, m); 37420997Skarels 3757024Ssam /* 37620997Skarels * For each of the modes (digital, analog, network), go through 37720997Skarels * a self-test that requires me to send VVIDENTSUCC good packets 37820997Skarels * in VVIDENTRETRY attempts. Use broadcast destination to find out 37920997Skarels * who I am, then use this as my address to check my address match 38020997Skarels * logic. Only data checked is the vh_type field. 3817024Ssam */ 3827640Ssam 38320997Skarels for (i = 0; i < 3; i++) { 38420997Skarels successes = 0; /* clear successes for this mode */ 38520997Skarels failures = 0; /* and clear failures, too */ 3867640Ssam 38720997Skarels /* take over device, and leave ring */ 38820997Skarels addr->vvicsr = VV_RST; 38920997Skarels addr->vvocsr = VV_RST; 39020997Skarels addr->vvicsr = vv_modes[i]; /* test mode */ 39120997Skarels 39220997Skarels /* 39320997Skarels * let the flag and token timers pop so that the init ring bit 39420997Skarels * will be allowed to work, by waiting about 1 second 39520997Skarels */ 39620997Skarels DELAY(1000000L); 39720997Skarels 39820997Skarels /* 39920997Skarels * retry loop 40020997Skarels */ 40120997Skarels while ((successes < VVIDENTSUCC) && (failures < VVIDENTRETRY)) 40220997Skarels { 40320997Skarels /* start a receive */ 404*35805Skarels ubaaddr = UBAI_ADDR(vs->vs_ifuba.ifu_r.ifrw_info); 40520997Skarels addr->vvicsr = VV_RST | vv_modes[i]; /* abort last */ 406*35805Skarels addr->vviba = (u_short) ubaaddr; 407*35805Skarels addr->vviea = (u_short) (ubaaddr >> 16); 40820997Skarels addr->vviwc = -(VVBUFSIZE) >> 1; 40920997Skarels addr->vvicsr = vv_modes[i] | VV_DEN | VV_ENB; 41020997Skarels 41120997Skarels /* purge stale data from BDP */ 41220997Skarels if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP) 41320997Skarels UBAPURGE(vs->vs_ifuba.ifu_uba, 41420997Skarels vs->vs_ifuba.ifu_w.ifrw_bdp); 41520997Skarels 41620997Skarels /* do a transmit */ 417*35805Skarels ubaaddr = UBAI_ADDR(vs->vs_ifuba.ifu_w.ifrw_info); 41820997Skarels addr->vvocsr = VV_RST; /* abort last try */ 419*35805Skarels addr->vvoba = (u_short) ubaaddr; 420*35805Skarels addr->vvoea = (u_short) (ubaaddr >> 16); 42120997Skarels addr->vvowc = -((vs->vs_olen + 1) >> 1); 42220997Skarels addr->vvocsr = VV_CPB | VV_DEN | VV_INR | VV_ENB; 42320997Skarels 42420997Skarels /* poll receive side for completion */ 42520997Skarels DELAY(10000); /* give it a chance */ 42620997Skarels for (waitcount = 0; waitcount < 10; waitcount++) { 42720997Skarels if (addr->vvicsr & VV_RDY) 42820997Skarels goto gotit; 42920997Skarels DELAY(1000); 43020997Skarels } 43120997Skarels failures++; /* no luck */ 43211209Ssam continue; 43320997Skarels 43420997Skarels gotit: /* we got something--is it any good? */ 43520997Skarels if ((addr->vvicsr & (VVRERR|VV_LDE)) || 43621779Skarels (addr->vvocsr & (VVXERR|VV_RFS))) { 43720997Skarels failures++; 43820997Skarels continue; 43920997Skarels } 44020997Skarels 44120997Skarels /* Purge BDP before looking at received packet */ 44220997Skarels if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP) 44320997Skarels UBAPURGE(vs->vs_ifuba.ifu_uba, 44420997Skarels vs->vs_ifuba.ifu_r.ifrw_bdp); 44524793Skarels m = if_rubaget(&vs->vs_ifuba, sizeof(struct vv_header), 44626394Skarels 0, &vs->vs_if); 44720997Skarels if (m != NULL) 44820997Skarels m_freem(m); 44920997Skarels 45020997Skarels v = (struct vv_header *)(vs->vs_ifuba.ifu_r.ifrw_addr); 45120997Skarels 45220997Skarels /* check message type, catch our node address */ 45320997Skarels if ((v->vh_type & 0xff) == RING_DIAGNOSTICS) { 45426311Skarels if (shost == NOHOST) { 45520997Skarels shost = v->vh_shost & 0xff; 45620997Skarels /* send to ourself now */ 45720997Skarels ((struct vv_header *) 45820997Skarels (vs->vs_ifuba.ifu_r.ifrw_addr)) 45920997Skarels ->vh_dhost = shost; 46020997Skarels } 46120997Skarels successes++; 46226903Sjas } else { 46326903Sjas failures++; 46420997Skarels } 46526903Sjas v->vh_type = 0; /* clear to check again */ 46611192Ssam } 46720997Skarels 46820997Skarels if (failures >= VVIDENTRETRY) 46920997Skarels { 47020997Skarels printf("vv%d: failed self-test after %d tries \ 47120997Skarels in %s mode\n", 47220997Skarels unit, VVIDENTRETRY, i == 0 ? "digital loopback" : 47320997Skarels (i == 1 ? "analog loopback" : "network")); 47420997Skarels printf("vv%d: icsr = %b, ocsr = %b\n", 47520997Skarels unit, 0xffff & addr->vvicsr, VV_IBITS, 47620997Skarels 0xffff & addr->vvocsr, VV_OBITS); 47720997Skarels addr->vvicsr = VV_RST; /* kill the sick board */ 47820997Skarels addr->vvocsr = VV_RST; 47926311Skarels shost = NOHOST; 48020997Skarels goto done; 48120997Skarels } 48211192Ssam } 48320997Skarels 48420997Skarels done: 48520997Skarels /* deallocate mbuf used for send packet (won't be one, anyways) */ 48615794Sleres if (vs->vs_ifuba.ifu_xtofree) { 4877024Ssam m_freem(vs->vs_ifuba.ifu_xtofree); 48815794Sleres vs->vs_ifuba.ifu_xtofree = 0; 48915794Sleres } 49020997Skarels 49120997Skarels return(shost); 4927024Ssam } 4937024Ssam 4947024Ssam /* 4957024Ssam * Start or restart output on interface. 49611192Ssam * If interface is active, this is a retransmit, so just 49711192Ssam * restuff registers and go. 4987024Ssam * If interface is not already active, get another datagram 4997024Ssam * to send off of the interface queue, and map it to the interface 5007024Ssam * before starting the output. 5017024Ssam */ 5027024Ssam vvstart(dev) 5037024Ssam dev_t dev; 5047024Ssam { 50516581Skarels register struct uba_device *ui; 50615764Sleres register struct vv_softc *vs; 5077024Ssam register struct vvreg *addr; 50816581Skarels register struct mbuf *m; 509*35805Skarels register int unit, ubaaddr, dest, s; 5107024Ssam 51116581Skarels unit = VVUNIT(dev); 51215764Sleres ui = vvinfo[unit]; 51315764Sleres vs = &vv_softc[unit]; 5147024Ssam if (vs->vs_oactive) 5157024Ssam goto restart; 5167024Ssam /* 5177024Ssam * Not already active: dequeue another request 5187024Ssam * and map it to the UNIBUS. If no more requests, 5197024Ssam * just return. 5207024Ssam */ 52115794Sleres s = splimp(); 5227024Ssam IF_DEQUEUE(&vs->vs_if.if_snd, m); 52315794Sleres splx(s); 52411209Ssam if (m == NULL) { 5257024Ssam vs->vs_oactive = 0; 5267024Ssam return; 5277024Ssam } 5287024Ssam dest = mtod(m, struct vv_header *)->vh_dhost; 5297024Ssam vs->vs_olen = if_wubaput(&vs->vs_ifuba, m); 5307024Ssam vs->vs_lastx = dest; 5317024Ssam restart: 5327024Ssam /* 5337024Ssam * Have request mapped to UNIBUS for transmission. 53415794Sleres * Purge any stale data from this BDP, and start the output. 53515794Sleres * 53615794Sleres * Make sure this packet will fit in the interface. 5377024Ssam */ 53820997Skarels if (vs->vs_olen > VVBUFSIZE) { 53920997Skarels printf("vv%d vs_olen: %d > VVBUFSIZE\n", unit, vs->vs_olen); 54011192Ssam panic("vvdriver vs_olen botch"); 54111192Ssam } 54220997Skarels 54320997Skarels vs->vs_if.if_timer = VVTIMEOUT; 54420997Skarels vs->vs_oactive = 1; 54520997Skarels 54620997Skarels /* ship it */ 5477024Ssam if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP) 5487024Ssam UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_w.ifrw_bdp); 5497024Ssam addr = (struct vvreg *)ui->ui_addr; 550*35805Skarels ubaaddr = UBAI_ADDR(vs->vs_ifuba.ifu_w.ifrw_info); 551*35805Skarels addr->vvoba = (u_short) ubaaddr; 552*35805Skarels addr->vvoea = (u_short) (ubaaddr >> 16); 5537024Ssam addr->vvowc = -((vs->vs_olen + 1) >> 1); 55420997Skarels addr->vvowc = -((vs->vs_olen + 1) >> 1); /* extra byte is garbage */ 55520997Skarels if (addr->vvocsr & VV_NOK) 55620997Skarels vs->vs_init++; /* count ring inits */ 5577024Ssam addr->vvocsr = VV_IEN | VV_CPB | VV_DEN | VV_INR | VV_ENB; 5587024Ssam } 5597024Ssam 5607024Ssam /* 56120997Skarels * proNET transmit interrupt 5627024Ssam * Start another output if more data to send. 5637024Ssam */ 5647024Ssam vvxint(unit) 5657024Ssam int unit; 5667024Ssam { 56715764Sleres register struct uba_device *ui; 56815764Sleres register struct vv_softc *vs; 5697024Ssam register struct vvreg *addr; 5707024Ssam register int oc; 5717024Ssam 57215764Sleres ui = vvinfo[unit]; 57315764Sleres vs = &vv_softc[unit]; 57415794Sleres vs->vs_if.if_timer = 0; 5757024Ssam addr = (struct vvreg *)ui->ui_addr; 5767024Ssam oc = 0xffff & (addr->vvocsr); 5777024Ssam if (vs->vs_oactive == 0) { 57820997Skarels vvprintf("vv%d: stray interrupt vvocsr = %b\n", unit, 57915764Sleres oc, VV_OBITS); 5807024Ssam return; 5817024Ssam } 58220997Skarels 58320997Skarels /* 58420997Skarels * we retransmit on soft error 58520997Skarels * TODO: sort retransmits to end of queue if possible! 58620997Skarels */ 58720997Skarels if (oc & (VV_OPT | VV_RFS)) { 58811192Ssam if (vs->vs_tries++ < VVRETRY) { 5897024Ssam if (oc & VV_OPT) 59020997Skarels vs->vs_otimeout++; 59120997Skarels if (oc & VV_RFS) { 59220997Skarels vs->vs_if.if_collisions++; 59320997Skarels vs->vs_refused++; 59420997Skarels } 59511192Ssam vvstart(unit); /* restart this message */ 5967024Ssam return; 5977024Ssam } 5987024Ssam } 5997024Ssam vs->vs_if.if_opackets++; 6007024Ssam vs->vs_oactive = 0; 6017024Ssam vs->vs_tries = 0; 60220997Skarels 6037024Ssam if (oc & VVXERR) { 6047024Ssam vs->vs_if.if_oerrors++; 60520997Skarels vvprintf("vv%d: error vvocsr = %b\n", unit, 0xffff & oc, 60615764Sleres VV_OBITS); 6077024Ssam } 6087024Ssam if (vs->vs_ifuba.ifu_xtofree) { 6097024Ssam m_freem(vs->vs_ifuba.ifu_xtofree); 6107024Ssam vs->vs_ifuba.ifu_xtofree = 0; 6117024Ssam } 6127024Ssam vvstart(unit); 6137024Ssam } 6147024Ssam 6157024Ssam /* 61615794Sleres * Transmit watchdog timer routine. 61715794Sleres * This routine gets called when we lose a transmit interrupt. 61815794Sleres * The best we can do is try to restart output. 61915794Sleres */ 62015794Sleres vvwatchdog(unit) 62115794Sleres int unit; 62215794Sleres { 62315794Sleres register struct vv_softc *vs; 62415794Sleres register int s; 62515794Sleres 62615794Sleres vs = &vv_softc[unit]; 62720997Skarels vvprintf("vv%d: lost a transmit interrupt.\n", unit); 62815794Sleres vs->vs_timeouts++; 62915794Sleres s = splimp(); 63015794Sleres vvstart(unit); 63115794Sleres splx(s); 63215794Sleres } 63315794Sleres 63415794Sleres /* 63520997Skarels * proNET interface receiver interrupt. 6367024Ssam * If input error just drop packet. 63715764Sleres * Otherwise purge input buffered data path and examine 6387024Ssam * packet to determine type. If can't determine length 63915764Sleres * from type, then have to drop packet. Otherwise decapsulate 6407024Ssam * packet based on type and pass to type specific higher-level 6417024Ssam * input routine. 6427024Ssam */ 6437024Ssam vvrint(unit) 6447024Ssam int unit; 6457024Ssam { 64615764Sleres register struct vv_softc *vs; 64716581Skarels register struct vvreg *addr; 6487024Ssam register struct vv_header *vv; 6497024Ssam register struct ifqueue *inq; 65016581Skarels register struct mbuf *m; 651*35805Skarels int ubaaddr, len, off, s; 6527640Ssam short resid; 6537024Ssam 65415764Sleres vs = &vv_softc[unit]; 65516581Skarels vs->vs_if.if_ipackets++; 65615764Sleres addr = (struct vvreg *)vvinfo[unit]->ui_addr; 65720997Skarels 6587024Ssam /* 65926903Sjas * Purge BDP 6607024Ssam */ 6617024Ssam if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP) 6627024Ssam UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_r.ifrw_bdp); 66320997Skarels 66420997Skarels /* 66520997Skarels * receive errors? 66620997Skarels */ 6677024Ssam if (addr->vvicsr & VVRERR) { 66820997Skarels vvprintf("vv%d: receive error, vvicsr = %b\n", unit, 66920997Skarels 0xffff&(addr->vvicsr), VV_IBITS); 67020997Skarels if (addr->vvicsr & VV_BDF) 67120997Skarels vs->vs_ibadf++; 6727640Ssam goto dropit; 6737024Ssam } 6747640Ssam 6757024Ssam /* 67620997Skarels * parity errors? 67720997Skarels */ 67820997Skarels if (addr->vvicsr & VV_LDE) { 67920997Skarels /* we don't have to clear it because the receive command */ 68020997Skarels /* writes 0 to parity bit */ 68120997Skarels vs->vs_parity++; 68226903Sjas 68320997Skarels /* 68420997Skarels * only on 10 megabit proNET is VV_LDE an end-to-end parity 68520997Skarels * bit. On 80 megabit, it returns to the intended use of 68620997Skarels * node-to-node parity. End-to-end parity errors on 80 megabit 68720997Skarels * give VV_BDF. 68820997Skarels */ 68926903Sjas if (vs->vs_is80 == 0) 69026903Sjas goto dropit; 69120997Skarels } 69220997Skarels 69320997Skarels /* 69420997Skarels * Get packet length from residual word count 6957640Ssam * 6967640Ssam * Compute header offset if trailer protocol 6977640Ssam * 6987640Ssam * Pull packet off interface. Off is nonzero if packet 6997640Ssam * has trailing header; if_rubaget will then force this header 7007640Ssam * information to be at the front. The vh_info field 7017640Ssam * carries the offset to the trailer data in trailer 7027640Ssam * format packets. 7037024Ssam */ 7047640Ssam vv = (struct vv_header *)(vs->vs_ifuba.ifu_r.ifrw_addr); 70511192Ssam vvtracehdr("vi", vv); 70620997Skarels resid = addr->vviwc & 01777; /* only low 10 bits valid */ 7077640Ssam if (resid) 70820997Skarels resid |= 0176000; /* high 6 bits are undefined */ 70920997Skarels len = ((VVBUFSIZE >> 1) + resid) << 1; 7107640Ssam len -= sizeof(struct vv_header); 71120997Skarels 71220997Skarels if ((addr->vvicsr & VV_DPR) || len > VVMRU || len <= 0) { 71320997Skarels vvprintf("vv%d: len too long or short, \ 71420997Skarels len = %d, vvicsr = %b\n", 71515794Sleres unit, len, 0xffff&(addr->vvicsr), VV_IBITS); 7167640Ssam goto dropit; 71715794Sleres } 71820997Skarels 71920997Skarels /* check the protocol header version */ 72020997Skarels if (vv->vh_version != RING_VERSION) { 72120997Skarels vvprintf("vv%d: bad protocol header version %d\n", 72220997Skarels unit, vv->vh_version & 0xff); 72320997Skarels goto dropit; 72420997Skarels } 72520997Skarels 7267640Ssam #define vvdataaddr(vv, off, type) ((type)(((caddr_t)((vv)+1)+(off)))) 72726950Sjas if (vv->vh_type == RING_TRAILER ) { 72828954Skarels off = ntohs((u_short)vv->vh_info); 72915794Sleres if (off > VVMTU) { 73020997Skarels vvprintf("vv%d: off > VVMTU, off = %d, vvicsr = %b\n", 73115794Sleres unit, off, 0xffff&(addr->vvicsr), VV_IBITS); 7327640Ssam goto dropit; 73315794Sleres } 73426903Sjas vv->vh_type = ntohs(*vvdataaddr(vv, off, u_short *)); 73526950Sjas resid = ntohs(*(vvdataaddr(vv, off+sizeof(u_short), u_short *))); 73615794Sleres if (off + resid > len) { 73720997Skarels vvprintf("vv%d: trailer packet too short\n", unit); 73820997Skarels vvprintf("vv%d: off = %d, resid = %d, vvicsr = %b\n", 73915794Sleres unit, off, resid, 74015794Sleres 0xffff&(addr->vvicsr), VV_IBITS); 7417640Ssam goto dropit; 74215794Sleres } 7437640Ssam len = off + resid; 74411209Ssam } else 7457640Ssam off = 0; 74620997Skarels 74715794Sleres if (len == 0) { 74820997Skarels vvprintf("vv%d: len is zero, vvicsr = %b\n", unit, 74915794Sleres 0xffff&(addr->vvicsr), VV_IBITS); 7507640Ssam goto dropit; 75115794Sleres } 75220997Skarels 75324793Skarels m = if_rubaget(&vs->vs_ifuba, len, off, &vs->vs_if); 75415794Sleres if (m == NULL) { 75520997Skarels vvprintf("vv%d: if_rubaget() failed, vvicsr = %b\n", unit, 75615794Sleres 0xffff&(addr->vvicsr), VV_IBITS); 7577640Ssam goto dropit; 75815794Sleres } 7597640Ssam if (off) { 76024793Skarels struct ifnet *ifp; 76124793Skarels 76224793Skarels ifp = *(mtod(m, struct ifnet **)); 76324793Skarels m->m_off += 2 * sizeof (u_short); 76424793Skarels m->m_len -= 2 * sizeof (u_short); 76524793Skarels *(mtod(m, struct ifnet **)) = ifp; 7667640Ssam } 76711192Ssam 76815794Sleres /* Keep track of source address of this packet */ 76915794Sleres vs->vs_lastr = vv->vh_shost; 77020997Skarels 77111192Ssam /* 77215764Sleres * Demultiplex on packet type 77311192Ssam */ 7747024Ssam switch (vv->vh_type) { 77511192Ssam 7767024Ssam #ifdef INET 7777024Ssam case RING_IP: 7787024Ssam schednetisr(NETISR_IP); 7797024Ssam inq = &ipintrq; 7807024Ssam break; 7817024Ssam #endif 7827024Ssam default: 78320997Skarels vvprintf("vv%d: unknown pkt type 0x%x\n", unit, vv->vh_type); 7847640Ssam m_freem(m); 7857024Ssam goto setup; 7867024Ssam } 78715794Sleres s = splimp(); 7887640Ssam if (IF_QFULL(inq)) { 7897640Ssam IF_DROP(inq); 7907640Ssam m_freem(m); 79111209Ssam } else 7927640Ssam IF_ENQUEUE(inq, m); 79315764Sleres 79415794Sleres splx(s); 7957024Ssam /* 79615764Sleres * Reset for the next packet. 7977024Ssam */ 79815764Sleres setup: 799*35805Skarels ubaaddr = UBAI_ADDR(vs->vs_ifuba.ifu_r.ifrw_info); 800*35805Skarels addr->vviba = (u_short) ubaaddr; 801*35805Skarels addr->vviea = (u_short) (ubaaddr >> 16); 80220997Skarels addr->vviwc = -(VVBUFSIZE) >> 1; 80320997Skarels addr->vvicsr = VV_HEN | VV_IEN | VV_DEN | VV_ENB; 80415764Sleres return; 80511192Ssam 80611192Ssam /* 80711209Ssam * Drop packet on floor -- count them!! 80811192Ssam */ 8097640Ssam dropit: 8107640Ssam vs->vs_if.if_ierrors++; 8117640Ssam goto setup; 8127024Ssam } 8137024Ssam 8147024Ssam /* 81520997Skarels * proNET output routine. 8167024Ssam * Encapsulate a packet of type family for the local net. 8177024Ssam * Use trailer local net encapsulation if enough data in first 8187024Ssam * packet leaves a multiple of 512 bytes of data in remainder. 8197024Ssam */ 8207024Ssam vvoutput(ifp, m0, dst) 8217024Ssam struct ifnet *ifp; 8227024Ssam struct mbuf *m0; 8237024Ssam struct sockaddr *dst; 8247024Ssam { 82516581Skarels register struct mbuf *m; 8267024Ssam register struct vv_header *vv; 8277640Ssam register int off; 82816581Skarels register int unit; 82916581Skarels register struct vvreg *addr; 83016581Skarels register struct vv_softc *vs; 83116581Skarels register int s; 83216581Skarels int type, dest, error; 8337024Ssam 83416581Skarels m = m0; 83516581Skarels unit = ifp->if_unit; 83616581Skarels addr = (struct vvreg *)vvinfo[unit]->ui_addr; 83716581Skarels vs = &vv_softc[unit]; 83820997Skarels 83916581Skarels /* 84020997Skarels * Check to see if the input side has wedged due the UBA 84120997Skarels * vectoring through 0. 84216581Skarels * 84316581Skarels * We are lower than device ipl when we enter this routine, 84416581Skarels * so if the interface is ready with an input packet then 84516581Skarels * an input interrupt must have slipped through the cracks. 84616581Skarels * 84716581Skarels * Avoid the race with an input interrupt by watching to see 84816581Skarels * if any packets come in. 84916581Skarels */ 85016581Skarels s = vs->vs_if.if_ipackets; 85116581Skarels if (addr->vvicsr & VV_RDY && s == vs->vs_if.if_ipackets) { 85220997Skarels vvprintf("vv%d: lost a receive interrupt, icsr = %b\n", 85316581Skarels unit, 0xffff&(addr->vvicsr), VV_IBITS); 85416581Skarels s = splimp(); 85516581Skarels vvrint(unit); 85616581Skarels splx(s); 85716581Skarels } 85816581Skarels 8597024Ssam switch (dst->sa_family) { 86011192Ssam 8617024Ssam #ifdef INET 86215764Sleres case AF_INET: 86321779Skarels if (in_broadcast(((struct sockaddr_in *)dst)->sin_addr)) 86421779Skarels dest = VV_BROADCAST; 86521779Skarels else 86621779Skarels dest = in_lnaof(((struct sockaddr_in *)dst)->sin_addr); 86720997Skarels #ifdef LOOPBACK 86821779Skarels if (dest == vs->vs_host && (loif.if_flags & IFF_UP)) 86921779Skarels return (looutput(&loif, m0, dst)); 87020997Skarels #endif LOOPBACK 87121779Skarels if (dest >= 0x100) { 8727640Ssam error = EPERM; 8737640Ssam goto bad; 8747640Ssam } 8757640Ssam off = ntohs((u_short)mtod(m, struct ip *)->ip_len) - m->m_len; 87620997Skarels /* 87720997Skarels * Trailerize, if the configuration allows it. 87820997Skarels * TODO: Need per host negotiation. 87920997Skarels */ 88013090Ssam if ((ifp->if_flags & IFF_NOTRAILERS) == 0) 88113090Ssam if (off > 0 && (off & 0x1ff) == 0 && 8827640Ssam m->m_off >= MMINOFF + 2 * sizeof (u_short)) { 88326950Sjas type = RING_TRAILER; 8847640Ssam m->m_off -= 2 * sizeof (u_short); 8857640Ssam m->m_len += 2 * sizeof (u_short); 88628954Skarels *mtod(m, u_short *) = htons((short)RING_IP); 88728954Skarels *(mtod(m, u_short *) + 1) = htons((u_short)m->m_len); 8887640Ssam goto gottrailertype; 8897640Ssam } 8907024Ssam type = RING_IP; 8917024Ssam off = 0; 8927024Ssam goto gottype; 8937024Ssam #endif 8947024Ssam default: 89516581Skarels printf("vv%d: can't handle af%d\n", unit, dst->sa_family); 8967640Ssam error = EAFNOSUPPORT; 8977640Ssam goto bad; 8987024Ssam } 8997024Ssam 9007024Ssam gottrailertype: 9017024Ssam /* 9027024Ssam * Packet to be sent as trailer: move first packet 9037024Ssam * (control information) to end of chain. 9047024Ssam */ 9057024Ssam while (m->m_next) 9067024Ssam m = m->m_next; 9077024Ssam m->m_next = m0; 9087024Ssam m = m0->m_next; 9097024Ssam m0->m_next = 0; 9107024Ssam m0 = m; 9117024Ssam gottype: 9127024Ssam /* 9137024Ssam * Add local net header. If no space in first mbuf, 9147024Ssam * allocate another. 9157024Ssam */ 9167024Ssam if (m->m_off > MMAXOFF || 9177024Ssam MMINOFF + sizeof (struct vv_header) > m->m_off) { 91811209Ssam m = m_get(M_DONTWAIT, MT_HEADER); 91911209Ssam if (m == NULL) { 9207640Ssam error = ENOBUFS; 9217640Ssam goto bad; 9227024Ssam } 9237024Ssam m->m_next = m0; 9247024Ssam m->m_off = MMINOFF; 9257024Ssam m->m_len = sizeof (struct vv_header); 9267024Ssam } else { 9277024Ssam m->m_off -= sizeof (struct vv_header); 9287024Ssam m->m_len += sizeof (struct vv_header); 9297024Ssam } 9307024Ssam vv = mtod(m, struct vv_header *); 93121779Skarels vv->vh_shost = vs->vs_host; 93221779Skarels vv->vh_dhost = dest; 9337024Ssam vv->vh_version = RING_VERSION; 9347024Ssam vv->vh_type = type; 93528954Skarels vv->vh_info = htons((u_short)off); 93611192Ssam vvtracehdr("vo", vv); 9377024Ssam 9387024Ssam /* 9397024Ssam * Queue message on interface, and start output if interface 9407024Ssam * not yet active. 9417024Ssam */ 9427024Ssam s = splimp(); 9437640Ssam if (IF_QFULL(&ifp->if_snd)) { 9447640Ssam IF_DROP(&ifp->if_snd); 9457640Ssam error = ENOBUFS; 9467640Ssam goto qfull; 9477640Ssam } 9487024Ssam IF_ENQUEUE(&ifp->if_snd, m); 94916581Skarels if (vs->vs_oactive == 0) 95016581Skarels vvstart(unit); 9517024Ssam splx(s); 9527640Ssam return (0); 9537640Ssam qfull: 9547640Ssam m0 = m; 9557640Ssam splx(s); 9567640Ssam bad: 9577640Ssam m_freem(m0); 9587640Ssam return(error); 9597024Ssam } 9607024Ssam 9617024Ssam /* 96213057Ssam * Process an ioctl request. 96313057Ssam */ 96413057Ssam vvioctl(ifp, cmd, data) 96513057Ssam register struct ifnet *ifp; 96613057Ssam int cmd; 96713057Ssam caddr_t data; 96813057Ssam { 96921779Skarels struct ifaddr *ifa = (struct ifaddr *) data; 97021779Skarels int s = splimp(), error = 0; 97113057Ssam 97213057Ssam switch (cmd) { 97313057Ssam 97413057Ssam case SIOCSIFADDR: 97524793Skarels ifp->if_flags |= IFF_UP; 97621779Skarels if ((ifp->if_flags & IFF_RUNNING) == 0) 97713057Ssam vvinit(ifp->if_unit); 97826903Sjas /* 97926903Sjas * Did self-test succeed? 98026903Sjas */ 98126903Sjas if ((ifp->if_flags & IFF_UP) == 0) 98226903Sjas error = ENETDOWN; 98321779Skarels /* 98421779Skarels * Attempt to check agreement of protocol address 98521779Skarels * and board address. 98621779Skarels */ 98721779Skarels switch (ifa->ifa_addr.sa_family) { 98821779Skarels case AF_INET: 98921779Skarels if (in_lnaof(IA_SIN(ifa)->sin_addr) != 99021779Skarels vv_softc[ifp->if_unit].vs_host) 99125194Skarels error = EADDRNOTAVAIL; 99221779Skarels break; 99321779Skarels } 99413057Ssam break; 99513057Ssam 99613057Ssam default: 99713057Ssam error = EINVAL; 99813057Ssam } 99913057Ssam splx(s); 100021779Skarels return (error); 100113057Ssam } 100225190Skarels 100325190Skarels /* 100425190Skarels * vvprt_hdr(s, v) print the local net header in "v" 100525190Skarels * with title is "s" 100625190Skarels */ 100725190Skarels vvprt_hdr(s, v) 100825190Skarels char *s; 100925190Skarels register struct vv_header *v; 101025190Skarels { 101125190Skarels printf("%s: dsvti: 0x%x 0x%x 0x%x 0x%x 0x%x\n", 101225190Skarels s, 101325190Skarels 0xff & (int)(v->vh_dhost), 0xff & (int)(v->vh_shost), 101425190Skarels 0xff & (int)(v->vh_version), 0xff & (int)(v->vh_type), 101525190Skarels 0xffff & (int)(v->vh_info)); 101625190Skarels } 101726903Sjas #endif NVV 1018