1*20997Skarels /* if_vv.c 6.7 85/05/22 */ 27024Ssam 39799Ssam #include "vv.h" 411192Ssam 57024Ssam /* 6*20997Skarels * Proteon proNET-10 and proNET-80 token ring driver. 7*20997Skarels * The name of this device driver derives from the old MIT 8*20997Skarels * name of V2LNI for the proNET hardware, would would abbreviate 9*20997Skarels * to "v2", but this won't work right. Thus the name is "vv". 1011192Ssam * 11*20997Skarels * This driver is compatible with the proNET 10 meagbit and 12*20997Skarels * 80 megabit token ring interfaces (models p1000 and p1080). 13*20997Skarels * 14*20997Skarels * TRAILERS: You must turn off trailers via ifconfig if you want to share 15*20997Skarels * a ring with software using the following protocol types: 16*20997Skarels * 3: Address Resolution Protocol 17*20997Skarels * 4: HDLC (old Proteon drivers) 18*20997Skarels * 5: VAX Debugging Protocol (never used) 19*20997Skarels * This is because the protocol type values chosen for trailers 20*20997Skarels * conflict with these protocols. It's too late to change either now. 21*20997Skarels * 22*20997Skarels * HARDWARE COMPATABILITY: This driver requires that the HSBU (p1001) 23*20997Skarels * have a serial number >= 040, which is about March, 1982. Older 24*20997Skarels * HSBUs do not carry across 64kbyte boundaries. The old warning 25*20997Skarels * about use without Wire Centers applies only to CTL (p1002) cards with 26*20997Skarels * serial <= 057, which have not received ECO 176-743, which was 27*20997Skarels * implemented in March, 1982. Most such CTLs have received this ECO, 28*20997Skarels * but they are only compatible with the old HSBUs (<=039) anyways. 297024Ssam */ 3011209Ssam #include "../machine/pte.h" 319799Ssam 3217117Sbloom #include "param.h" 3317117Sbloom #include "systm.h" 3417117Sbloom #include "mbuf.h" 3517117Sbloom #include "buf.h" 3617117Sbloom #include "protosw.h" 3717117Sbloom #include "socket.h" 3817117Sbloom #include "vmmac.h" 3917117Sbloom #include "errno.h" 4017117Sbloom #include "ioctl.h" 418465Sroot 428465Sroot #include "../net/if.h" 4311209Ssam #include "../net/netisr.h" 448465Sroot #include "../net/route.h" 458421Swnj #include "../netinet/in.h" 468421Swnj #include "../netinet/in_systm.h" 478421Swnj #include "../netinet/ip.h" 488421Swnj #include "../netinet/ip_var.h" 498465Sroot 5015794Sleres #include "../vax/cpu.h" 5111209Ssam #include "../vax/mtpr.h" 5217117Sbloom #include "if_vv.h" 5317117Sbloom #include "if_uba.h" 548465Sroot #include "../vaxuba/ubareg.h" 558465Sroot #include "../vaxuba/ubavar.h" 567024Ssam 577024Ssam /* 58*20997Skarels * 80 megabit configuration 59*20997Skarels * Uncomment the next line if you are using the 80 megabit system. The 60*20997Skarels * only change is the disposition of packets with parity/link_data_error 61*20997Skarels * indication. 627024Ssam */ 63*20997Skarels /* #define PRONET80 */ 647024Ssam 65*20997Skarels /* 66*20997Skarels * maximum transmission unit definition -- 67*20997Skarels * you can set VVMTU at anything from 576 to 2024. 68*20997Skarels * 1536 is a popular "large" value, because it is a multiple 69*20997Skarels * of 512, which the trailer scheme likes. 70*20997Skarels * The absolute maximum size is 2024, which is enforced. 71*20997Skarels */ 72*20997Skarels 73*20997Skarels #define VVMTU (1024) 74*20997Skarels 75*20997Skarels #define VVMRU (VVMTU + 16) 76*20997Skarels #define VVBUFSIZE (VVMRU + sizeof(struct vv_header)) 77*20997Skarels #if VVMTU>2024 78*20997Skarels #undef VVMTU 79*20997Skarels #undef VVMRU 80*20997Skarels #undef VVBUFSIZE 81*20997Skarels #define VVBUFSIZE (2046) 82*20997Skarels #define VVMRU (VVBUFSIZE - sizeof (struct vv_header)) 83*20997Skarels #define VVMTU (VVMRU - 16) 847024Ssam #endif 857024Ssam 86*20997Skarels /* 87*20997Skarels * debugging and tracing stuff 88*20997Skarels */ 8916581Skarels int vv_tracehdr = 0; /* 1 => trace headers (slowly!!) */ 90*20997Skarels #ifndef proteon 9116581Skarels int vv_logreaderrors = 1; /* 1 => log all read errors */ 92*20997Skarels #else proteon 93*20997Skarels int vv_logerrors = 0; /* 1 => log all i/o errors */ 94*20997Skarels #endif proteon 957640Ssam 96*20997Skarels #define vvtracehdr if (vv_tracehdr) vvprt_hdr 97*20997Skarels #define vvprintf if (vv_logerrors && vs->vs_if.if_flags & IFF_DEBUG) printf 9811192Ssam 99*20997Skarels /* 100*20997Skarels * externals, types, etc. 101*20997Skarels */ 10216581Skarels int vvprobe(), vvattach(), vvreset(), vvinit(); 10316581Skarels int vvidentify(), vvstart(), vvxint(), vvwatchdog(); 10416581Skarels int vvrint(), vvoutput(), vvioctl(), vvsetaddr(); 1057024Ssam struct uba_device *vvinfo[NVV]; 1067024Ssam u_short vvstd[] = { 0 }; 1077024Ssam struct uba_driver vvdriver = 1087024Ssam { vvprobe, 0, vvattach, 0, vvstd, "vv", vvinfo }; 1097024Ssam #define VVUNIT(x) minor(x) 1107024Ssam 111*20997Skarels #define LOOPBACK /* use loopback for packets meant for us */ 112*20997Skarels #ifdef LOOPBACK 113*20997Skarels extern struct ifnet loif; 114*20997Skarels #endif 115*20997Skarels 1167024Ssam /* 1177024Ssam * Software status of each interface. 1187024Ssam * 1197024Ssam * Each interface is referenced by a network interface structure, 1207024Ssam * vs_if, which the routing code uses to locate the interface. 1217024Ssam * This structure contains the output queue for the interface, its address, ... 1227024Ssam * We also have, for each interface, a UBA interface structure, which 1237024Ssam * contains information about the UNIBUS resources held by the interface: 1247024Ssam * map registers, buffered data paths, etc. Information is cached in this 1257024Ssam * structure for use by the if_uba.c routines in running the interface 1267024Ssam * efficiently. 1277024Ssam */ 1287024Ssam struct vv_softc { 1297024Ssam struct ifnet vs_if; /* network-visible interface */ 1307024Ssam struct ifuba vs_ifuba; /* UNIBUS resources */ 13111192Ssam short vs_oactive; /* is output active */ 1327024Ssam short vs_olen; /* length of last output */ 13315794Sleres u_short vs_lastx; /* address of last packet sent */ 13415794Sleres u_short vs_lastr; /* address of last packet received */ 13511192Ssam short vs_tries; /* transmit current retry count */ 1367024Ssam short vs_init; /* number of ring inits */ 137*20997Skarels short vs_refused; /* number of packets refused */ 13815794Sleres short vs_timeouts; /* number of transmit timeouts */ 139*20997Skarels short vs_otimeout; /* number of output timeouts */ 140*20997Skarels short vs_ibadf; /* number of input bad formats */ 141*20997Skarels short vs_parity; /* number of parity errors on 10 meg, */ 142*20997Skarels /* link data errors on 80 meg */ 1437024Ssam } vv_softc[NVV]; 1447024Ssam 145*20997Skarels /* 146*20997Skarels * probe the interface to see that the registers exist, and then 147*20997Skarels * cause an interrupt to find its vector 148*20997Skarels */ 1497024Ssam vvprobe(reg) 1507024Ssam caddr_t reg; 1517024Ssam { 1527024Ssam register int br, cvec; 15316581Skarels register struct vvreg *addr; 1547024Ssam 1557024Ssam #ifdef lint 15615764Sleres br = 0; cvec = br; br = cvec; 1577024Ssam #endif 15816581Skarels addr = (struct vvreg *)reg; 159*20997Skarels 1607024Ssam /* reset interface, enable, and wait till dust settles */ 1617024Ssam addr->vvicsr = VV_RST; 1627024Ssam addr->vvocsr = VV_RST; 16315764Sleres DELAY(100000); 164*20997Skarels 1657024Ssam /* generate interrupt by doing 1 word DMA from 0 in uba space!! */ 1667024Ssam addr->vvoba = 0; /* low 16 bits */ 1677024Ssam addr->vvoea = 0; /* extended bits */ 1687024Ssam addr->vvowc = -1; /* for 1 word */ 169*20997Skarels addr->vvocsr = VV_IEN | VV_DEN; /* start the DMA, with interrupt */ 1707024Ssam DELAY(100000); 171*20997Skarels addr->vvocsr = VV_RST; /* clear out the CSR */ 1727024Ssam if (cvec && cvec != 0x200) 17315764Sleres cvec -= 4; /* backup so vector => receive */ 1747024Ssam return(1); 1757024Ssam } 1767024Ssam 1777024Ssam /* 1787024Ssam * Interface exists: make available by filling in network interface 1797024Ssam * record. System will initialize the interface when it is ready 1807024Ssam * to accept packets. 1817024Ssam */ 1827024Ssam vvattach(ui) 1837024Ssam struct uba_device *ui; 1847024Ssam { 18515764Sleres register struct vv_softc *vs; 1867024Ssam 18715764Sleres vs = &vv_softc[ui->ui_unit]; 1887024Ssam vs->vs_if.if_unit = ui->ui_unit; 1897024Ssam vs->vs_if.if_name = "vv"; 1907024Ssam vs->vs_if.if_mtu = VVMTU; 1917024Ssam vs->vs_if.if_init = vvinit; 19213057Ssam vs->vs_if.if_ioctl = vvioctl; 1937024Ssam vs->vs_if.if_output = vvoutput; 19411209Ssam vs->vs_if.if_reset = vvreset; 19515794Sleres vs->vs_if.if_timer = 0; 19615794Sleres vs->vs_if.if_watchdog = vvwatchdog; 1977640Ssam vs->vs_ifuba.ifu_flags = UBA_CANTWAIT | UBA_NEEDBDP | UBA_NEED16; 19812354Smo #if defined(VAX750) 19912354Smo /* don't chew up 750 bdp's */ 20012354Smo if (cpu == VAX_750 && ui->ui_unit > 0) 20112354Smo vs->vs_ifuba.ifu_flags &= ~UBA_NEEDBDP; 20212354Smo #endif 2037024Ssam if_attach(&vs->vs_if); 2047024Ssam } 2057024Ssam 2067024Ssam /* 2077024Ssam * Reset of interface after UNIBUS reset. 2087024Ssam * If interface is on specified uba, reset its state. 2097024Ssam */ 2107024Ssam vvreset(unit, uban) 2117024Ssam int unit, uban; 2127024Ssam { 2137024Ssam register struct uba_device *ui; 2147024Ssam 2157024Ssam if (unit >= NVV || (ui = vvinfo[unit]) == 0 || ui->ui_alive == 0 || 2167024Ssam ui->ui_ubanum != uban) 2177024Ssam return; 2187024Ssam printf(" vv%d", unit); 2197024Ssam vvinit(unit); 2207024Ssam } 2217024Ssam 2227024Ssam /* 2237024Ssam * Initialization of interface; clear recorded pending 2247024Ssam * operations, and reinitialize UNIBUS usage. 2257024Ssam */ 2267024Ssam vvinit(unit) 2277024Ssam int unit; 2287024Ssam { 22915764Sleres register struct vv_softc *vs; 23015764Sleres register struct uba_device *ui; 2317024Ssam register struct vvreg *addr; 23216581Skarels register struct sockaddr_in *sin; 23316581Skarels register int ubainfo, s; 2347024Ssam 23515764Sleres vs = &vv_softc[unit]; 23615764Sleres ui = vvinfo[unit]; 23715764Sleres sin = (struct sockaddr_in *)&vs->vs_if.if_addr; 238*20997Skarels 23915764Sleres /* 24015764Sleres * If the network number is still zero, we've been 24115764Sleres * called too soon. 24215764Sleres */ 24315764Sleres if (in_netof(sin->sin_addr) == 0) 24413057Ssam return; 245*20997Skarels 2467640Ssam addr = (struct vvreg *)ui->ui_addr; 2477024Ssam if (if_ubainit(&vs->vs_ifuba, ui->ui_ubanum, 24815764Sleres sizeof (struct vv_header), (int)btoc(VVMTU)) == 0) { 24915794Sleres printf("vv%d: can't initialize, if_ubainit() failed\n", unit); 2507640Ssam vs->vs_if.if_flags &= ~IFF_UP; 2517024Ssam return; 2527024Ssam } 253*20997Skarels 2547024Ssam /* 25515764Sleres * Now that the uba is set up, figure out our address and 25615764Sleres * update complete our host address. 2577640Ssam */ 258*20997Skarels if ((vs->vs_if.if_host[0] = vvidentify(unit)) == -1) { 25915794Sleres vs->vs_if.if_flags &= ~IFF_UP; 26015794Sleres return; 26115794Sleres } 2627640Ssam printf("vv%d: host %d\n", unit, vs->vs_if.if_host[0]); 26311209Ssam sin->sin_addr = if_makeaddr(vs->vs_if.if_net, vs->vs_if.if_host[0]); 264*20997Skarels 2657640Ssam /* 266*20997Skarels * Reset the interface, and stay in the ring 2677640Ssam */ 268*20997Skarels addr->vvocsr = VV_RST; /* take over output */ 269*20997Skarels addr->vvocsr = VV_CPB; /* clear packet buffer */ 270*20997Skarels addr->vvicsr = VV_RST | VV_HEN; /* take over input, */ 271*20997Skarels /* keep relay closed */ 27212351Smo DELAY(500000); /* let contacts settle */ 273*20997Skarels 274*20997Skarels vs->vs_init = 0; /* clear counters, etc. */ 275*20997Skarels vs->vs_refused = 0; 27615794Sleres vs->vs_timeouts = 0; 277*20997Skarels vs->vs_otimeout = 0; 278*20997Skarels vs->vs_ibadf = 0; 279*20997Skarels vs->vs_parity = 0; 28015794Sleres vs->vs_lastx = 256; /* an invalid address */ 28115794Sleres vs->vs_lastr = 256; /* an invalid address */ 282*20997Skarels 2837640Ssam /* 2847640Ssam * Hang a receive and start any 2857640Ssam * pending writes by faking a transmit complete. 2867640Ssam */ 2877640Ssam s = splimp(); 2887640Ssam ubainfo = vs->vs_ifuba.ifu_r.ifrw_info; 28913057Ssam addr->vviba = (u_short)ubainfo; 29013057Ssam addr->vviea = (u_short)(ubainfo >> 16); 291*20997Skarels addr->vviwc = -(VVBUFSIZE) >> 1; 292*20997Skarels addr->vvicsr = VV_IEN | VV_HEN | VV_DEN | VV_ENB; 2937640Ssam vs->vs_oactive = 1; 29413057Ssam vs->vs_if.if_flags |= IFF_UP | IFF_RUNNING; 2957640Ssam vvxint(unit); 2967640Ssam splx(s); 2977640Ssam if_rtinit(&vs->vs_if, RTF_UP); 2987640Ssam } 2997640Ssam 3007640Ssam /* 301*20997Skarels * Do a moderately thorough self-test in all three modes. Mostly 302*20997Skarels * to keeps defective nodes off the ring, rather than to be especially 303*20997Skarels * thorough. The key issue is to detect any cable breaks before joining 304*20997Skarels * the ring. Return our node address on success, return -1 on failure. 305*20997Skarels * 3067640Ssam */ 307*20997Skarels 308*20997Skarels /* the three self-test modes */ 309*20997Skarels static u_short vv_modes[] = { 310*20997Skarels VV_STE|VV_LPB, /* digital loopback */ 311*20997Skarels VV_STE, /* analog loopback */ 312*20997Skarels VV_HEN /* network mode */ 313*20997Skarels }; 314*20997Skarels 31511209Ssam vvidentify(unit) 31613057Ssam int unit; 31711209Ssam { 31815764Sleres register struct vv_softc *vs; 31915764Sleres register struct uba_device *ui; 3207640Ssam register struct vvreg *addr; 32116581Skarels register struct mbuf *m; 32216581Skarels register struct vv_header *v; 323*20997Skarels register int ubainfo; 324*20997Skarels register int i, successes, failures, waitcount; 325*20997Skarels u_short shost = -1; 3267640Ssam 327*20997Skarels vs = &vv_softc[unit]; 328*20997Skarels ui = vvinfo[unit]; 329*20997Skarels addr = (struct vvreg *)ui->ui_addr; 330*20997Skarels 3317640Ssam /* 3327024Ssam * Build a multicast message to identify our address 333*20997Skarels * We need do this only once, since nobody else is about to use 334*20997Skarels * the intermediate transmit buffer (ifu_w.ifrw_addr) that 335*20997Skarels * if_ubainit() aquired for us. 3367024Ssam */ 33711209Ssam m = m_get(M_DONTWAIT, MT_HEADER); 33815794Sleres if (m == NULL) { 33915794Sleres printf("vv%d: can't initialize, m_get() failed\n", unit); 34013057Ssam return (0); 34115794Sleres } 34211192Ssam m->m_next = 0; 3437024Ssam m->m_off = MMINOFF; 3447024Ssam m->m_len = sizeof(struct vv_header); 3457024Ssam v = mtod(m, struct vv_header *); 34611192Ssam v->vh_dhost = VV_BROADCAST; /* multicast destination address */ 3477024Ssam v->vh_shost = 0; /* will be overwritten with ours */ 3487024Ssam v->vh_version = RING_VERSION; 349*20997Skarels v->vh_type = RING_DIAGNOSTICS; 3507024Ssam v->vh_info = 0; 351*20997Skarels /* map xmit message into uba, copying to intermediate buffer */ 352*20997Skarels vs->vs_olen = if_wubaput(&vs->vs_ifuba, m); 353*20997Skarels 3547024Ssam /* 355*20997Skarels * For each of the modes (digital, analog, network), go through 356*20997Skarels * a self-test that requires me to send VVIDENTSUCC good packets 357*20997Skarels * in VVIDENTRETRY attempts. Use broadcast destination to find out 358*20997Skarels * who I am, then use this as my address to check my address match 359*20997Skarels * logic. Only data checked is the vh_type field. 3607024Ssam */ 3617640Ssam 362*20997Skarels for (i = 0; i < 3; i++) { 363*20997Skarels successes = 0; /* clear successes for this mode */ 364*20997Skarels failures = 0; /* and clear failures, too */ 3657640Ssam 366*20997Skarels /* take over device, and leave ring */ 367*20997Skarels addr->vvicsr = VV_RST; 368*20997Skarels addr->vvocsr = VV_RST; 369*20997Skarels addr->vvicsr = vv_modes[i]; /* test mode */ 370*20997Skarels 371*20997Skarels /* 372*20997Skarels * let the flag and token timers pop so that the init ring bit 373*20997Skarels * will be allowed to work, by waiting about 1 second 374*20997Skarels */ 375*20997Skarels DELAY(1000000L); 376*20997Skarels 377*20997Skarels /* 378*20997Skarels * retry loop 379*20997Skarels */ 380*20997Skarels while ((successes < VVIDENTSUCC) && (failures < VVIDENTRETRY)) 381*20997Skarels { 382*20997Skarels /* start a receive */ 383*20997Skarels ubainfo = vs->vs_ifuba.ifu_r.ifrw_info; 384*20997Skarels addr->vvicsr = VV_RST | vv_modes[i]; /* abort last */ 385*20997Skarels addr->vviba = (u_short) ubainfo; 386*20997Skarels addr->vviea = (u_short) (ubainfo >> 16); 387*20997Skarels addr->vviwc = -(VVBUFSIZE) >> 1; 388*20997Skarels addr->vvicsr = vv_modes[i] | VV_DEN | VV_ENB; 389*20997Skarels 390*20997Skarels /* purge stale data from BDP */ 391*20997Skarels if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP) 392*20997Skarels UBAPURGE(vs->vs_ifuba.ifu_uba, 393*20997Skarels vs->vs_ifuba.ifu_w.ifrw_bdp); 394*20997Skarels 395*20997Skarels /* do a transmit */ 396*20997Skarels ubainfo = vs->vs_ifuba.ifu_w.ifrw_info; 397*20997Skarels addr->vvocsr = VV_RST; /* abort last try */ 398*20997Skarels addr->vvoba = (u_short) ubainfo; 399*20997Skarels addr->vvoea = (u_short) (ubainfo >> 16); 400*20997Skarels addr->vvowc = -((vs->vs_olen + 1) >> 1); 401*20997Skarels addr->vvocsr = VV_CPB | VV_DEN | VV_INR | VV_ENB; 402*20997Skarels 403*20997Skarels /* poll receive side for completion */ 404*20997Skarels DELAY(10000); /* give it a chance */ 405*20997Skarels for (waitcount = 0; waitcount < 10; waitcount++) { 406*20997Skarels if (addr->vvicsr & VV_RDY) 407*20997Skarels goto gotit; 408*20997Skarels DELAY(1000); 409*20997Skarels } 410*20997Skarels failures++; /* no luck */ 41111209Ssam continue; 412*20997Skarels 413*20997Skarels gotit: /* we got something--is it any good? */ 414*20997Skarels if ((addr->vvicsr & (VVRERR|VV_LDE)) || 415*20997Skarels (ADDR->vvocsr & (VVXERR|VV_RFS))) { 416*20997Skarels failures++; 417*20997Skarels continue; 418*20997Skarels } 419*20997Skarels 420*20997Skarels /* Purge BDP before looking at received packet */ 421*20997Skarels if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP) 422*20997Skarels UBAPURGE(vs->vs_ifuba.ifu_uba, 423*20997Skarels vs->vs_ifuba.ifu_r.ifrw_bdp); 424*20997Skarels m = if_rubaget(&vs->vs_ifuba, 425*20997Skarels sizeof(struct vv_header), 0); 426*20997Skarels if (m != NULL) 427*20997Skarels m_freem(m); 428*20997Skarels 429*20997Skarels v = (struct vv_header *)(vs->vs_ifuba.ifu_r.ifrw_addr); 430*20997Skarels 431*20997Skarels /* check message type, catch our node address */ 432*20997Skarels if ((v->vh_type & 0xff) == RING_DIAGNOSTICS) { 433*20997Skarels if (shost == -1) { 434*20997Skarels shost = v->vh_shost & 0xff; 435*20997Skarels /* send to ourself now */ 436*20997Skarels ((struct vv_header *) 437*20997Skarels (vs->vs_ifuba.ifu_r.ifrw_addr)) 438*20997Skarels ->vh_dhost = shost; 439*20997Skarels } 440*20997Skarels successes++; 441*20997Skarels v->vh_type = 0; /* clear to check again */ 442*20997Skarels } 44311192Ssam } 444*20997Skarels 445*20997Skarels if (failures >= VVIDENTRETRY) 446*20997Skarels { 447*20997Skarels printf("vv%d: failed self-test after %d tries \ 448*20997Skarels in %s mode\n", 449*20997Skarels unit, VVIDENTRETRY, i == 0 ? "digital loopback" : 450*20997Skarels (i == 1 ? "analog loopback" : "network")); 451*20997Skarels printf("vv%d: icsr = %b, ocsr = %b\n", 452*20997Skarels unit, 0xffff & addr->vvicsr, VV_IBITS, 453*20997Skarels 0xffff & addr->vvocsr, VV_OBITS); 454*20997Skarels addr->vvicsr = VV_RST; /* kill the sick board */ 455*20997Skarels addr->vvocsr = VV_RST; 456*20997Skarels shost = -1; 457*20997Skarels goto done; 458*20997Skarels } 45911192Ssam } 460*20997Skarels 461*20997Skarels done: 462*20997Skarels /* deallocate mbuf used for send packet (won't be one, anyways) */ 46315794Sleres if (vs->vs_ifuba.ifu_xtofree) { 4647024Ssam m_freem(vs->vs_ifuba.ifu_xtofree); 46515794Sleres vs->vs_ifuba.ifu_xtofree = 0; 46615794Sleres } 467*20997Skarels 468*20997Skarels return(shost); 4697024Ssam } 4707024Ssam 4717024Ssam /* 4727024Ssam * Start or restart output on interface. 47311192Ssam * If interface is active, this is a retransmit, so just 47411192Ssam * restuff registers and go. 4757024Ssam * If interface is not already active, get another datagram 4767024Ssam * to send off of the interface queue, and map it to the interface 4777024Ssam * before starting the output. 4787024Ssam */ 4797024Ssam vvstart(dev) 4807024Ssam dev_t dev; 4817024Ssam { 48216581Skarels register struct uba_device *ui; 48315764Sleres register struct vv_softc *vs; 4847024Ssam register struct vvreg *addr; 48516581Skarels register struct mbuf *m; 48616581Skarels register int unit, ubainfo, dest, s; 4877024Ssam 48816581Skarels unit = VVUNIT(dev); 48915764Sleres ui = vvinfo[unit]; 49015764Sleres vs = &vv_softc[unit]; 4917024Ssam if (vs->vs_oactive) 4927024Ssam goto restart; 4937024Ssam /* 4947024Ssam * Not already active: dequeue another request 4957024Ssam * and map it to the UNIBUS. If no more requests, 4967024Ssam * just return. 4977024Ssam */ 49815794Sleres s = splimp(); 4997024Ssam IF_DEQUEUE(&vs->vs_if.if_snd, m); 50015794Sleres splx(s); 50111209Ssam if (m == NULL) { 5027024Ssam vs->vs_oactive = 0; 5037024Ssam return; 5047024Ssam } 5057024Ssam dest = mtod(m, struct vv_header *)->vh_dhost; 5067024Ssam vs->vs_olen = if_wubaput(&vs->vs_ifuba, m); 5077024Ssam vs->vs_lastx = dest; 5087024Ssam restart: 5097024Ssam /* 5107024Ssam * Have request mapped to UNIBUS for transmission. 51115794Sleres * Purge any stale data from this BDP, and start the output. 51215794Sleres * 51315794Sleres * Make sure this packet will fit in the interface. 5147024Ssam */ 515*20997Skarels if (vs->vs_olen > VVBUFSIZE) { 516*20997Skarels printf("vv%d vs_olen: %d > VVBUFSIZE\n", unit, vs->vs_olen); 51711192Ssam panic("vvdriver vs_olen botch"); 51811192Ssam } 519*20997Skarels 520*20997Skarels vs->vs_if.if_timer = VVTIMEOUT; 521*20997Skarels vs->vs_oactive = 1; 522*20997Skarels 523*20997Skarels /* ship it */ 5247024Ssam if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP) 5257024Ssam UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_w.ifrw_bdp); 5267024Ssam addr = (struct vvreg *)ui->ui_addr; 5277024Ssam ubainfo = vs->vs_ifuba.ifu_w.ifrw_info; 5287024Ssam addr->vvoba = (u_short) ubainfo; 5297024Ssam addr->vvoea = (u_short) (ubainfo >> 16); 5307024Ssam addr->vvowc = -((vs->vs_olen + 1) >> 1); 531*20997Skarels addr->vvowc = -((vs->vs_olen + 1) >> 1); /* extra byte is garbage */ 532*20997Skarels if (addr->vvocsr & VV_NOK) 533*20997Skarels vs->vs_init++; /* count ring inits */ 5347024Ssam addr->vvocsr = VV_IEN | VV_CPB | VV_DEN | VV_INR | VV_ENB; 53515794Sleres vs->vs_if.if_timer = VVTIMEOUT; 5367024Ssam vs->vs_oactive = 1; 5377024Ssam } 5387024Ssam 5397024Ssam /* 540*20997Skarels * proNET transmit interrupt 5417024Ssam * Start another output if more data to send. 5427024Ssam */ 5437024Ssam vvxint(unit) 5447024Ssam int unit; 5457024Ssam { 54615764Sleres register struct uba_device *ui; 54715764Sleres register struct vv_softc *vs; 5487024Ssam register struct vvreg *addr; 5497024Ssam register int oc; 5507024Ssam 55115764Sleres ui = vvinfo[unit]; 55215764Sleres vs = &vv_softc[unit]; 55315794Sleres vs->vs_if.if_timer = 0; 5547024Ssam addr = (struct vvreg *)ui->ui_addr; 5557024Ssam oc = 0xffff & (addr->vvocsr); 5567024Ssam if (vs->vs_oactive == 0) { 557*20997Skarels vvprintf("vv%d: stray interrupt vvocsr = %b\n", unit, 55815764Sleres oc, VV_OBITS); 5597024Ssam return; 5607024Ssam } 561*20997Skarels 562*20997Skarels /* 563*20997Skarels * we retransmit on soft error 564*20997Skarels * TODO: sort retransmits to end of queue if possible! 565*20997Skarels */ 566*20997Skarels if (oc & (VV_OPT | VV_RFS)) { 56711192Ssam if (vs->vs_tries++ < VVRETRY) { 5687024Ssam if (oc & VV_OPT) 569*20997Skarels vs->vs_otimeout++; 570*20997Skarels if (oc & VV_RFS) { 571*20997Skarels vs->vs_if.if_collisions++; 572*20997Skarels vs->vs_refused++; 573*20997Skarels } 57411192Ssam vvstart(unit); /* restart this message */ 5757024Ssam return; 5767024Ssam } 5777024Ssam } 5787024Ssam vs->vs_if.if_opackets++; 5797024Ssam vs->vs_oactive = 0; 5807024Ssam vs->vs_tries = 0; 581*20997Skarels 5827024Ssam if (oc & VVXERR) { 5837024Ssam vs->vs_if.if_oerrors++; 58411192Ssam printf("vv%d: error vvocsr = %b\n", unit, 0xffff & oc, 585*20997Skarels vvprintf("vv%d: error vvocsr = %b\n", unit, 0xffff & oc, 58615764Sleres VV_OBITS); 5877024Ssam } 5887024Ssam if (vs->vs_ifuba.ifu_xtofree) { 5897024Ssam m_freem(vs->vs_ifuba.ifu_xtofree); 5907024Ssam vs->vs_ifuba.ifu_xtofree = 0; 5917024Ssam } 5927024Ssam vvstart(unit); 5937024Ssam } 5947024Ssam 5957024Ssam /* 59615794Sleres * Transmit watchdog timer routine. 59715794Sleres * This routine gets called when we lose a transmit interrupt. 59815794Sleres * The best we can do is try to restart output. 59915794Sleres */ 60015794Sleres vvwatchdog(unit) 60115794Sleres int unit; 60215794Sleres { 60315794Sleres register struct vv_softc *vs; 60415794Sleres register int s; 60515794Sleres 60615794Sleres vs = &vv_softc[unit]; 607*20997Skarels vvprintf("vv%d: lost a transmit interrupt.\n", unit); 60815794Sleres vs->vs_timeouts++; 60915794Sleres s = splimp(); 61015794Sleres vvstart(unit); 61115794Sleres splx(s); 61215794Sleres } 61315794Sleres 61415794Sleres /* 615*20997Skarels * proNET interface receiver interrupt. 6167024Ssam * If input error just drop packet. 61715764Sleres * Otherwise purge input buffered data path and examine 6187024Ssam * packet to determine type. If can't determine length 61915764Sleres * from type, then have to drop packet. Otherwise decapsulate 6207024Ssam * packet based on type and pass to type specific higher-level 6217024Ssam * input routine. 6227024Ssam */ 6237024Ssam vvrint(unit) 6247024Ssam int unit; 6257024Ssam { 62615764Sleres register struct vv_softc *vs; 62716581Skarels register struct vvreg *addr; 6287024Ssam register struct vv_header *vv; 6297024Ssam register struct ifqueue *inq; 63016581Skarels register struct mbuf *m; 63115794Sleres int ubainfo, len, off, s; 6327640Ssam short resid; 6337024Ssam 63415764Sleres vs = &vv_softc[unit]; 63516581Skarels vs->vs_if.if_ipackets++; 63615764Sleres addr = (struct vvreg *)vvinfo[unit]->ui_addr; 637*20997Skarels 6387024Ssam /* 6397024Ssam * Purge BDP; drop if input error indicated. 6407024Ssam */ 6417024Ssam if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP) 6427024Ssam UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_r.ifrw_bdp); 643*20997Skarels 644*20997Skarels /* 645*20997Skarels * receive errors? 646*20997Skarels */ 6477024Ssam if (addr->vvicsr & VVRERR) { 648*20997Skarels vvprintf("vv%d: receive error, vvicsr = %b\n", unit, 649*20997Skarels 0xffff&(addr->vvicsr), VV_IBITS); 650*20997Skarels if (addr->vvicsr & VV_BDF) 651*20997Skarels vs->vs_ibadf++; 6527640Ssam goto dropit; 6537024Ssam } 6547640Ssam 6557024Ssam /* 656*20997Skarels * parity errors? 657*20997Skarels */ 658*20997Skarels if (addr->vvicsr & VV_LDE) { 659*20997Skarels /* we don't have to clear it because the receive command */ 660*20997Skarels /* writes 0 to parity bit */ 661*20997Skarels vs->vs_parity++; 662*20997Skarels #ifndef PRONET80 663*20997Skarels /* 664*20997Skarels * only on 10 megabit proNET is VV_LDE an end-to-end parity 665*20997Skarels * bit. On 80 megabit, it returns to the intended use of 666*20997Skarels * node-to-node parity. End-to-end parity errors on 80 megabit 667*20997Skarels * give VV_BDF. 668*20997Skarels */ 669*20997Skarels goto dropit; 670*20997Skarels #endif 671*20997Skarels } 672*20997Skarels 673*20997Skarels /* 674*20997Skarels * Get packet length from residual word count 6757640Ssam * 6767640Ssam * Compute header offset if trailer protocol 6777640Ssam * 6787640Ssam * Pull packet off interface. Off is nonzero if packet 6797640Ssam * has trailing header; if_rubaget will then force this header 6807640Ssam * information to be at the front. The vh_info field 6817640Ssam * carries the offset to the trailer data in trailer 6827640Ssam * format packets. 6837024Ssam */ 6847640Ssam vv = (struct vv_header *)(vs->vs_ifuba.ifu_r.ifrw_addr); 68511192Ssam vvtracehdr("vi", vv); 686*20997Skarels resid = addr->vviwc & 01777; /* only low 10 bits valid */ 6877640Ssam if (resid) 688*20997Skarels resid |= 0176000; /* high 6 bits are undefined */ 689*20997Skarels len = ((VVBUFSIZE >> 1) + resid) << 1; 6907640Ssam len -= sizeof(struct vv_header); 691*20997Skarels 692*20997Skarels if ((addr->vvicsr & VV_DPR) || len > VVMRU || len <= 0) { 693*20997Skarels vvprintf("vv%d: len too long or short, \ 694*20997Skarels len = %d, vvicsr = %b\n", 69515794Sleres unit, len, 0xffff&(addr->vvicsr), VV_IBITS); 6967640Ssam goto dropit; 69715794Sleres } 698*20997Skarels 699*20997Skarels /* check the protocol header version */ 700*20997Skarels if (vv->vh_version != RING_VERSION) { 701*20997Skarels vvprintf("vv%d: bad protocol header version %d\n", 702*20997Skarels unit, vv->vh_version & 0xff); 703*20997Skarels goto dropit; 704*20997Skarels } 705*20997Skarels 7067640Ssam #define vvdataaddr(vv, off, type) ((type)(((caddr_t)((vv)+1)+(off)))) 70713057Ssam if (vv->vh_type >= RING_IPTrailer && 70813057Ssam vv->vh_type < RING_IPTrailer+RING_IPNTrailer) { 7097640Ssam off = (vv->vh_type - RING_IPTrailer) * 512; 71015794Sleres if (off > VVMTU) { 711*20997Skarels vvprintf("vv%d: off > VVMTU, off = %d, vvicsr = %b\n", 71215794Sleres unit, off, 0xffff&(addr->vvicsr), VV_IBITS); 7137640Ssam goto dropit; 71415794Sleres } 7157640Ssam vv->vh_type = *vvdataaddr(vv, off, u_short *); 7167640Ssam resid = *(vvdataaddr(vv, off+2, u_short *)); 71715794Sleres if (off + resid > len) { 718*20997Skarels vvprintf("vv%d: trailer packet too short\n", unit); 719*20997Skarels vvprintf("vv%d: off = %d, resid = %d, vvicsr = %b\n", 72015794Sleres unit, off, resid, 72115794Sleres 0xffff&(addr->vvicsr), VV_IBITS); 7227640Ssam goto dropit; 72315794Sleres } 7247640Ssam len = off + resid; 72511209Ssam } else 7267640Ssam off = 0; 727*20997Skarels 72815794Sleres if (len == 0) { 729*20997Skarels vvprintf("vv%d: len is zero, vvicsr = %b\n", unit, 73015794Sleres 0xffff&(addr->vvicsr), VV_IBITS); 7317640Ssam goto dropit; 73215794Sleres } 733*20997Skarels 7347640Ssam m = if_rubaget(&vs->vs_ifuba, len, off); 73515794Sleres if (m == NULL) { 736*20997Skarels vvprintf("vv%d: if_rubaget() failed, vvicsr = %b\n", unit, 73715794Sleres 0xffff&(addr->vvicsr), VV_IBITS); 7387640Ssam goto dropit; 73915794Sleres } 7407640Ssam if (off) { 7417640Ssam m->m_off += 2 * sizeof(u_short); 7427640Ssam m->m_len -= 2 * sizeof(u_short); 7437640Ssam } 74411192Ssam 74515794Sleres /* Keep track of source address of this packet */ 74615794Sleres vs->vs_lastr = vv->vh_shost; 747*20997Skarels 74811192Ssam /* 74915764Sleres * Demultiplex on packet type 75011192Ssam */ 7517024Ssam switch (vv->vh_type) { 75211192Ssam 7537024Ssam #ifdef INET 7547024Ssam case RING_IP: 7557024Ssam schednetisr(NETISR_IP); 7567024Ssam inq = &ipintrq; 7577024Ssam break; 7587024Ssam #endif 7597024Ssam default: 760*20997Skarels vvprintf("vv%d: unknown pkt type 0x%x\n", unit, vv->vh_type); 7617640Ssam m_freem(m); 7627024Ssam goto setup; 7637024Ssam } 76415794Sleres s = splimp(); 7657640Ssam if (IF_QFULL(inq)) { 7667640Ssam IF_DROP(inq); 7677640Ssam m_freem(m); 76811209Ssam } else 7697640Ssam IF_ENQUEUE(inq, m); 77015764Sleres 77115794Sleres splx(s); 7727024Ssam /* 77315764Sleres * Reset for the next packet. 7747024Ssam */ 77515764Sleres setup: 77615764Sleres ubainfo = vs->vs_ifuba.ifu_r.ifrw_info; 77715764Sleres addr->vviba = (u_short) ubainfo; 77815764Sleres addr->vviea = (u_short) (ubainfo >> 16); 779*20997Skarels addr->vviwc = -(VVBUFSIZE) >> 1; 780*20997Skarels addr->vvicsr = VV_HEN | VV_IEN | VV_DEN | VV_ENB; 78115764Sleres return; 78211192Ssam 78311192Ssam /* 78411209Ssam * Drop packet on floor -- count them!! 78511192Ssam */ 7867640Ssam dropit: 7877640Ssam vs->vs_if.if_ierrors++; 7887640Ssam goto setup; 7897024Ssam } 7907024Ssam 7917024Ssam /* 792*20997Skarels * proNET output routine. 7937024Ssam * Encapsulate a packet of type family for the local net. 7947024Ssam * Use trailer local net encapsulation if enough data in first 7957024Ssam * packet leaves a multiple of 512 bytes of data in remainder. 7967024Ssam */ 7977024Ssam vvoutput(ifp, m0, dst) 7987024Ssam struct ifnet *ifp; 7997024Ssam struct mbuf *m0; 8007024Ssam struct sockaddr *dst; 8017024Ssam { 80216581Skarels register struct mbuf *m; 8037024Ssam register struct vv_header *vv; 8047640Ssam register int off; 80516581Skarels register int unit; 80616581Skarels register struct vvreg *addr; 80716581Skarels register struct vv_softc *vs; 80816581Skarels register int s; 80916581Skarels int type, dest, error; 8107024Ssam 81116581Skarels m = m0; 81216581Skarels unit = ifp->if_unit; 81316581Skarels addr = (struct vvreg *)vvinfo[unit]->ui_addr; 81416581Skarels vs = &vv_softc[unit]; 815*20997Skarels 81616581Skarels /* 817*20997Skarels * Check to see if the input side has wedged due the UBA 818*20997Skarels * vectoring through 0. 81916581Skarels * 82016581Skarels * We are lower than device ipl when we enter this routine, 82116581Skarels * so if the interface is ready with an input packet then 82216581Skarels * an input interrupt must have slipped through the cracks. 82316581Skarels * 82416581Skarels * Avoid the race with an input interrupt by watching to see 82516581Skarels * if any packets come in. 82616581Skarels */ 82716581Skarels s = vs->vs_if.if_ipackets; 82816581Skarels if (addr->vvicsr & VV_RDY && s == vs->vs_if.if_ipackets) { 829*20997Skarels vvprintf("vv%d: lost a receive interrupt, icsr = %b\n", 83016581Skarels unit, 0xffff&(addr->vvicsr), VV_IBITS); 83116581Skarels s = splimp(); 83216581Skarels vvrint(unit); 83316581Skarels splx(s); 83416581Skarels } 83516581Skarels 8367024Ssam switch (dst->sa_family) { 83711192Ssam 8387024Ssam #ifdef INET 83915764Sleres case AF_INET: 8407640Ssam dest = ((struct sockaddr_in *)dst)->sin_addr.s_addr; 841*20997Skarels #ifdef LOOPBACK 842*20997Skarels if ((dest == ((struct sockaddr_in *)&ifp->if_addr)->sin_addr.s_addr) && 843*20997Skarels ((loif.if_flags & IFF_UP) != 0)) 844*20997Skarels return(looutput(&loif, m0, dst)); 845*20997Skarels #endif LOOPBACK 84611192Ssam if ((dest = in_lnaof(*((struct in_addr *)&dest))) >= 0x100) { 8477640Ssam error = EPERM; 8487640Ssam goto bad; 8497640Ssam } 8507640Ssam off = ntohs((u_short)mtod(m, struct ip *)->ip_len) - m->m_len; 851*20997Skarels /* 852*20997Skarels * Trailerize, if the configuration allows it. 853*20997Skarels * TODO: Need per host negotiation. 854*20997Skarels */ 85513090Ssam if ((ifp->if_flags & IFF_NOTRAILERS) == 0) 85613090Ssam if (off > 0 && (off & 0x1ff) == 0 && 8577640Ssam m->m_off >= MMINOFF + 2 * sizeof (u_short)) { 8587640Ssam type = RING_IPTrailer + (off>>9); 8597640Ssam m->m_off -= 2 * sizeof (u_short); 8607640Ssam m->m_len += 2 * sizeof (u_short); 8617640Ssam *mtod(m, u_short *) = RING_IP; 8627640Ssam *(mtod(m, u_short *) + 1) = m->m_len; 8637640Ssam goto gottrailertype; 8647640Ssam } 8657024Ssam type = RING_IP; 8667024Ssam off = 0; 8677024Ssam goto gottype; 8687024Ssam #endif 8697024Ssam default: 87016581Skarels printf("vv%d: can't handle af%d\n", unit, dst->sa_family); 8717640Ssam error = EAFNOSUPPORT; 8727640Ssam goto bad; 8737024Ssam } 8747024Ssam 8757024Ssam gottrailertype: 8767024Ssam /* 8777024Ssam * Packet to be sent as trailer: move first packet 8787024Ssam * (control information) to end of chain. 8797024Ssam */ 8807024Ssam while (m->m_next) 8817024Ssam m = m->m_next; 8827024Ssam m->m_next = m0; 8837024Ssam m = m0->m_next; 8847024Ssam m0->m_next = 0; 8857024Ssam m0 = m; 8867024Ssam gottype: 8877024Ssam /* 8887024Ssam * Add local net header. If no space in first mbuf, 8897024Ssam * allocate another. 8907024Ssam */ 8917024Ssam if (m->m_off > MMAXOFF || 8927024Ssam MMINOFF + sizeof (struct vv_header) > m->m_off) { 89311209Ssam m = m_get(M_DONTWAIT, MT_HEADER); 89411209Ssam if (m == NULL) { 8957640Ssam error = ENOBUFS; 8967640Ssam goto bad; 8977024Ssam } 8987024Ssam m->m_next = m0; 8997024Ssam m->m_off = MMINOFF; 9007024Ssam m->m_len = sizeof (struct vv_header); 9017024Ssam } else { 9027024Ssam m->m_off -= sizeof (struct vv_header); 9037024Ssam m->m_len += sizeof (struct vv_header); 9047024Ssam } 9057024Ssam vv = mtod(m, struct vv_header *); 9067024Ssam vv->vh_shost = ifp->if_host[0]; 90715764Sleres /* Map the destination address if it's a broadcast */ 90815764Sleres if ((vv->vh_dhost = dest) == INADDR_ANY) 90915764Sleres vv->vh_dhost = VV_BROADCAST; 9107024Ssam vv->vh_version = RING_VERSION; 9117024Ssam vv->vh_type = type; 9127640Ssam vv->vh_info = off; 91311192Ssam vvtracehdr("vo", vv); 9147024Ssam 9157024Ssam /* 9167024Ssam * Queue message on interface, and start output if interface 9177024Ssam * not yet active. 9187024Ssam */ 9197024Ssam s = splimp(); 9207640Ssam if (IF_QFULL(&ifp->if_snd)) { 9217640Ssam IF_DROP(&ifp->if_snd); 9227640Ssam error = ENOBUFS; 9237640Ssam goto qfull; 9247640Ssam } 9257024Ssam IF_ENQUEUE(&ifp->if_snd, m); 92616581Skarels if (vs->vs_oactive == 0) 92716581Skarels vvstart(unit); 9287024Ssam splx(s); 9297640Ssam return (0); 9307640Ssam qfull: 9317640Ssam m0 = m; 9327640Ssam splx(s); 9337640Ssam bad: 9347640Ssam m_freem(m0); 9357640Ssam return(error); 9367024Ssam } 9377024Ssam 9387024Ssam /* 93913057Ssam * Process an ioctl request. 94013057Ssam */ 94113057Ssam vvioctl(ifp, cmd, data) 94213057Ssam register struct ifnet *ifp; 94313057Ssam int cmd; 94413057Ssam caddr_t data; 94513057Ssam { 94616581Skarels register struct ifreq *ifr; 94716581Skarels register int s; 94816581Skarels int error; 94913057Ssam 95015764Sleres ifr = (struct ifreq *)data; 95115764Sleres error = 0; 95215764Sleres s = splimp(); 95313057Ssam switch (cmd) { 95413057Ssam 95513057Ssam case SIOCSIFADDR: 95616581Skarels if (ifp->if_flags & IFF_RUNNING) 95716581Skarels if_rtinit(ifp, -1); /* delete previous route */ 95816581Skarels vvsetaddr(ifp, (struct sockaddr_in *)&ifr->ifr_addr); 95916581Skarels if (ifp->if_flags & IFF_RUNNING) 96016581Skarels if_rtinit(ifp, RTF_UP); 96116581Skarels else 96213057Ssam vvinit(ifp->if_unit); 96313057Ssam break; 96413057Ssam 96513057Ssam default: 96613057Ssam error = EINVAL; 96713057Ssam } 96813057Ssam splx(s); 96916581Skarels return(error); 97013057Ssam } 97113057Ssam 97213057Ssam /* 97315764Sleres * Set up the address for this interface. We use the network number 97415794Sleres * from the passed address and an invalid host number; vvinit() will 97515794Sleres * figure out the host number and insert it later. 97615764Sleres */ 97715764Sleres vvsetaddr(ifp, sin) 97815764Sleres register struct ifnet *ifp; 97915764Sleres register struct sockaddr_in *sin; 98015764Sleres { 98115764Sleres ifp->if_net = in_netof(sin->sin_addr); 98215764Sleres ifp->if_host[0] = 256; /* an invalid host number */ 98315764Sleres sin = (struct sockaddr_in *)&ifp->if_addr; 98415764Sleres sin->sin_family = AF_INET; 98515764Sleres sin->sin_addr = if_makeaddr(ifp->if_net, ifp->if_host[0]); 98615764Sleres sin = (struct sockaddr_in *)&ifp->if_broadaddr; 98715764Sleres sin->sin_family = AF_INET; 98815764Sleres sin->sin_addr = if_makeaddr(ifp->if_net, INADDR_ANY); 98915764Sleres ifp->if_flags |= IFF_BROADCAST; 99015764Sleres } 99115764Sleres 99215764Sleres /* 9937024Ssam * vvprt_hdr(s, v) print the local net header in "v" 99415764Sleres * with title is "s" 9957024Ssam */ 9967024Ssam vvprt_hdr(s, v) 9977024Ssam char *s; 9987024Ssam register struct vv_header *v; 9997024Ssam { 10007024Ssam printf("%s: dsvti: 0x%x 0x%x 0x%x 0x%x 0x%x\n", 10017024Ssam s, 10027024Ssam 0xff & (int)(v->vh_dhost), 0xff & (int)(v->vh_shost), 10037024Ssam 0xff & (int)(v->vh_version), 0xff & (int)(v->vh_type), 10047024Ssam 0xffff & (int)(v->vh_info)); 10057024Ssam } 1006