1 /* if_vv.c 4.11 82/12/17 */ 2 3 #include "vv.h" 4 #if NVV > 0 5 /* 6 * Proteon 10 Meg Ring Driver. 7 * This device is called "vv" because its "real name", 8 * V2LNI won't work if shortened to the obvious "v2". 9 * Hence the subterfuge. 10 */ 11 #include "../machine/pte.h" 12 13 #include "../h/param.h" 14 #include "../h/systm.h" 15 #include "../h/mbuf.h" 16 #include "../h/buf.h" 17 #include "../h/protosw.h" 18 #include "../h/socket.h" 19 #include "../h/vmmac.h" 20 #include <errno.h> 21 22 #include "../net/if.h" 23 #include "../net/route.h" 24 #include "../netinet/in.h" 25 #include "../netinet/in_systm.h" 26 #include "../netinet/ip.h" 27 #include "../netinet/ip_var.h" 28 29 #include "../vax/cpu.h" 30 #include "../vax/mtpr.h" 31 #include "../vaxif/if_vv.h" 32 #include "../vaxif/if_uba.h" 33 #include "../vaxuba/ubareg.h" 34 #include "../vaxuba/ubavar.h" 35 36 #include "vv.h" 37 38 /* 39 * N.B. - if WIRECENTER is defined wrong, it can well break 40 * the hardware!! 41 */ 42 43 #define WIRECENTER 44 45 #ifdef WIRECENTER 46 #define VV_CONF VV_HEN /* drive wire center relay */ 47 #else 48 #define VV_CONF VV_STE /* allow operation without wire center */ 49 #endif 50 51 #define VVMTU (1024+512) 52 #define VVMRU (1024+512+16) /* space for trailer */ 53 54 int vv_dotrailer = 1, /* so can do trailers selectively */ 55 vv_trace = 0; 56 57 int vvprobe(), vvattach(), vvrint(), vvxint(); 58 struct uba_device *vvinfo[NVV]; 59 u_short vvstd[] = { 0 }; 60 struct uba_driver vvdriver = 61 { vvprobe, 0, vvattach, 0, vvstd, "vv", vvinfo }; 62 #define VVUNIT(x) minor(x) 63 int vvinit(),vvoutput(),vvreset(); 64 65 /* 66 * Software status of each interface. 67 * 68 * Each interface is referenced by a network interface structure, 69 * vs_if, which the routing code uses to locate the interface. 70 * This structure contains the output queue for the interface, its address, ... 71 * We also have, for each interface, a UBA interface structure, which 72 * contains information about the UNIBUS resources held by the interface: 73 * map registers, buffered data paths, etc. Information is cached in this 74 * structure for use by the if_uba.c routines in running the interface 75 * efficiently. 76 */ 77 struct vv_softc { 78 struct ifnet vs_if; /* network-visible interface */ 79 struct ifuba vs_ifuba; /* UNIBUS resources */ 80 short vs_oactive; /* is output active? */ 81 short vs_olen; /* length of last output */ 82 u_short vs_lastx; /* last destination address */ 83 short vs_tries; /* current retry count */ 84 short vs_init; /* number of ring inits */ 85 short vs_flush; /* number of flushed packets */ 86 short vs_nottaken; /* number of packets refused */ 87 } vv_softc[NVV]; 88 89 vvprobe(reg) 90 caddr_t reg; 91 { 92 register int br, cvec; 93 register struct vvreg *addr = (struct vvreg *)reg; 94 95 #ifdef lint 96 br = 0; cvec = br; br = cvec; 97 #endif 98 /* reset interface, enable, and wait till dust settles */ 99 addr->vvicsr = VV_RST; 100 addr->vvocsr = VV_RST; 101 DELAY(100000); 102 /* generate interrupt by doing 1 word DMA from 0 in uba space!! */ 103 addr->vvocsr = VV_IEN; /* enable interrupt */ 104 addr->vvoba = 0; /* low 16 bits */ 105 addr->vvoea = 0; /* extended bits */ 106 addr->vvowc = -1; /* for 1 word */ 107 addr->vvocsr |= VV_DEN; /* start the DMA */ 108 DELAY(100000); 109 addr->vvocsr = 0; 110 if (cvec && cvec != 0x200) 111 cvec -= 4; /* backup so vector => recieve */ 112 return(1); 113 } 114 115 /* 116 * Interface exists: make available by filling in network interface 117 * record. System will initialize the interface when it is ready 118 * to accept packets. 119 */ 120 vvattach(ui) 121 struct uba_device *ui; 122 { 123 register struct vv_softc *vs = &vv_softc[ui->ui_unit]; 124 register struct sockaddr_in *sin; 125 126 vs->vs_if.if_unit = ui->ui_unit; 127 vs->vs_if.if_name = "vv"; 128 vs->vs_if.if_mtu = VVMTU; 129 vs->vs_if.if_net = ui->ui_flags; 130 vs->vs_if.if_host[0] = 0; /* this will be reset in vvinit() */ 131 132 sin = (struct sockaddr_in *)&vs->vs_if.if_addr; 133 sin->sin_family = AF_INET; 134 sin->sin_addr = if_makeaddr(vs->vs_if.if_net, vs->vs_if.if_host[0]); 135 136 sin = (struct sockaddr_in *)&vs->vs_if.if_broadaddr; 137 sin->sin_family = AF_INET; 138 sin->sin_addr = if_makeaddr(vs->vs_if.if_net, VV_BROADCAST); 139 vs->vs_if.if_flags = IFF_BROADCAST; 140 141 vs->vs_if.if_init = vvinit; 142 vs->vs_if.if_output = vvoutput; 143 vs->vs_if.if_reset = vvreset; 144 vs->vs_ifuba.ifu_flags = UBA_CANTWAIT | UBA_NEEDBDP | UBA_NEED16; 145 if_attach(&vs->vs_if); 146 } 147 148 /* 149 * Reset of interface after UNIBUS reset. 150 * If interface is on specified uba, reset its state. 151 */ 152 vvreset(unit, uban) 153 int unit, uban; 154 { 155 register struct uba_device *ui; 156 157 if (unit >= NVV || (ui = vvinfo[unit]) == 0 || ui->ui_alive == 0 || 158 ui->ui_ubanum != uban) 159 return; 160 printf(" vv%d", unit); 161 vvinit(unit); 162 } 163 164 /* 165 * Initialization of interface; clear recorded pending 166 * operations, and reinitialize UNIBUS usage. 167 */ 168 vvinit(unit) 169 int unit; 170 { 171 register struct vv_softc *vs = &vv_softc[unit]; 172 register struct uba_device *ui = vvinfo[unit]; 173 register struct vvreg *addr; 174 struct sockaddr_in *sin; 175 int ubainfo, s; 176 177 addr = (struct vvreg *)ui->ui_addr; 178 if (if_ubainit(&vs->vs_ifuba, ui->ui_ubanum, 179 sizeof (struct vv_header), (int)btoc(VVMTU)) == 0) { 180 nogo: 181 printf("vv%d: can't initialize\n", unit); 182 vs->vs_if.if_flags &= ~IFF_UP; 183 return; 184 } 185 186 /* 187 * discover our host address and post it 188 */ 189 190 vs->vs_if.if_host[0] = vvidentify(unit); 191 if (vs->vs_if.if_host[0] == 0) 192 goto nogo; 193 printf("vv%d: host %d\n", unit, vs->vs_if.if_host[0]); 194 sin = (struct sockaddr_in *)&vs->vs_if.if_addr; 195 sin->sin_family = AF_INET; 196 sin->sin_addr = 197 if_makeaddr(vs->vs_if.if_net, vs->vs_if.if_host[0]); 198 199 /* 200 * Reset the interface, and join the ring 201 */ 202 addr->vvocsr = VV_RST | VV_CPB; /* clear packet buffer */ 203 addr->vvicsr = VV_RST | VV_CONF; /* close logical relay */ 204 sleep((caddr_t)&lbolt, PZERO); /* let contacts settle */ 205 vs->vs_init = 0; 206 vs->vs_flush = 0; 207 vs->vs_nottaken = 0; 208 209 /* 210 * Hang a receive and start any 211 * pending writes by faking a transmit complete. 212 */ 213 s = splimp(); 214 ubainfo = vs->vs_ifuba.ifu_r.ifrw_info; 215 addr->vviba = (u_short) ubainfo; 216 addr->vviea = (u_short) (ubainfo >> 16); 217 addr->vviwc = -(sizeof (struct vv_header) + VVMTU) >> 1; 218 addr->vvicsr = VV_IEN | VV_CONF | VV_DEN | VV_ENB; 219 vs->vs_oactive = 1; 220 vs->vs_if.if_flags |= IFF_UP; 221 vvxint(unit); 222 splx(s); 223 if_rtinit(&vs->vs_if, RTF_UP); 224 } 225 226 /* 227 * vvidentify() - return our host address 228 */ 229 vvidentify(unit) 230 { 231 register struct vv_softc *vs = &vv_softc[unit]; 232 register struct uba_device *ui = vvinfo[unit]; 233 register struct vvreg *addr; 234 struct mbuf *m; 235 struct vv_header *v; 236 int ubainfo, retrying, attempts, waitcount, s; 237 238 /* 239 * Build a multicast message to identify our address 240 */ 241 addr = (struct vvreg *)ui->ui_addr; 242 attempts = 0; /* total attempts, including bad msg type */ 243 retrying = 0; /* first time through */ 244 m = m_get(M_DONTWAIT, MT_HEADER); 245 if (m == 0) { 246 printf("vvinit: can't get mbuf"); 247 return (0); 248 } 249 m->m_off = MMINOFF; 250 m->m_len = sizeof(struct vv_header); 251 252 v = mtod(m, struct vv_header *); 253 v->vh_dhost = 0; /* multicast destination address */ 254 v->vh_shost = 0; /* will be overwritten with ours */ 255 v->vh_version = RING_VERSION; 256 v->vh_type = RING_WHOAMI; 257 v->vh_info = 0; 258 vs->vs_olen = if_wubaput(&vs->vs_ifuba, m); 259 if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP) 260 UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_w.ifrw_bdp); 261 262 /* 263 * Reset interface, establish Digital Loopback Mode, and 264 * send the multicast (to myself) with Input Copy enabled. 265 */ 266 retry: 267 ubainfo = vs->vs_ifuba.ifu_r.ifrw_info; 268 addr->vvicsr = VV_RST; 269 addr->vviba = (u_short) ubainfo; 270 addr->vviea = (u_short) (ubainfo >> 16); 271 addr->vviwc = -(sizeof (struct vv_header) + VVMTU) >> 1; 272 addr->vvicsr = VV_STE | VV_DEN | VV_ENB | VV_LPB; 273 274 /* let flag timers fire so ring will initialize */ 275 sleep((caddr_t) &lbolt, PZERO); 276 sleep((caddr_t) &lbolt, PZERO); 277 278 addr->vvocsr = VV_RST | VV_CPB; /* clear packet buffer */ 279 ubainfo = vs->vs_ifuba.ifu_w.ifrw_info; 280 addr->vvoba = (u_short) ubainfo; 281 addr->vvoea = (u_short) (ubainfo >> 16); 282 addr->vvowc = -((vs->vs_olen + 1) >> 1); 283 addr->vvocsr = VV_CPB | VV_DEN | VV_INR | VV_ENB; 284 285 /* 286 * Wait for receive side to finish. 287 * Extract source address (which will be our own), 288 * and post to interface structure. 289 */ 290 DELAY(1000); 291 for (waitcount = 0; (addr->vvicsr & VV_RDY) == 0; waitcount++) 292 if (waitcount < 10) 293 DELAY(1000); 294 else { 295 if (attempts++ < 10) 296 goto retry; 297 else { 298 printf("vv%d: can't initialize\n", unit); 299 printf("vvinit loopwait: icsr = %b\n", 300 0xffff&(addr->vvicsr),VV_IBITS); 301 vs->vs_if.if_flags &= ~IFF_UP; 302 return (0); 303 } 304 } 305 if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP) 306 UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_w.ifrw_bdp); 307 if (vs->vs_ifuba.ifu_xtofree) 308 m_freem(vs->vs_ifuba.ifu_xtofree); 309 if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP) 310 UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_r.ifrw_bdp); 311 m = if_rubaget(&vs->vs_ifuba, sizeof(struct vv_header), 0); 312 if (m) 313 m_freem(m); 314 /* 315 * check message type before we believe the source host address 316 */ 317 v = (struct vv_header *)(vs->vs_ifuba.ifu_r.ifrw_addr); 318 if (v->vh_type != RING_WHOAMI) 319 goto retry; 320 return (v->vh_shost); 321 } 322 323 /* 324 * Start or restart output on interface. 325 * If interface is not already active, get another datagram 326 * to send off of the interface queue, and map it to the interface 327 * before starting the output. 328 */ 329 vvstart(dev) 330 dev_t dev; 331 { 332 int unit = VVUNIT(dev); 333 struct uba_device *ui = vvinfo[unit]; 334 register struct vv_softc *vs = &vv_softc[unit]; 335 register struct vvreg *addr; 336 struct mbuf *m; 337 int ubainfo, dest; 338 339 if (vs->vs_oactive) 340 goto restart; 341 /* 342 * Not already active: dequeue another request 343 * and map it to the UNIBUS. If no more requests, 344 * just return. 345 */ 346 IF_DEQUEUE(&vs->vs_if.if_snd, m); 347 if (m == 0) { 348 vs->vs_oactive = 0; 349 return; 350 } 351 dest = mtod(m, struct vv_header *)->vh_dhost; 352 vs->vs_olen = if_wubaput(&vs->vs_ifuba, m); 353 vs->vs_lastx = dest; 354 355 restart: 356 /* 357 * Have request mapped to UNIBUS for transmission. 358 * Purge any stale data from this BDP, and start the otput. 359 */ 360 if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP) 361 UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_w.ifrw_bdp); 362 addr = (struct vvreg *)ui->ui_addr; 363 ubainfo = vs->vs_ifuba.ifu_w.ifrw_info; 364 addr->vvoba = (u_short) ubainfo; 365 addr->vvoea = (u_short) (ubainfo >> 16); 366 addr->vvowc = -((vs->vs_olen + 1) >> 1); 367 addr->vvocsr = VV_IEN | VV_CPB | VV_DEN | VV_INR | VV_ENB; 368 vs->vs_oactive = 1; 369 } 370 371 /* 372 * VVLNI transmit interrupt 373 * Start another output if more data to send. 374 */ 375 vvxint(unit) 376 int unit; 377 { 378 register struct uba_device *ui = vvinfo[unit]; 379 register struct vv_softc *vs = &vv_softc[unit]; 380 register struct vvreg *addr; 381 register int oc; 382 383 addr = (struct vvreg *)ui->ui_addr; 384 oc = 0xffff & (addr->vvocsr); 385 if (vs->vs_oactive == 0) { 386 printf("vv%d: stray interrupt, vvocsr=%b\n", unit, 387 oc, VV_OBITS); 388 return; 389 } 390 if (oc & (VV_OPT | VV_RFS)) { 391 vs->vs_if.if_collisions++; 392 if (++(vs->vs_tries) < VVRETRY) { 393 if (oc & VV_OPT) 394 vs->vs_init++; 395 if (oc & VV_RFS) 396 vs->vs_nottaken++; 397 addr->vvocsr = VV_IEN | VV_ENB | VV_INR; 398 return; 399 } 400 if (oc & VV_OPT) 401 printf("vv%d: output timeout\n"); 402 } 403 vs->vs_if.if_opackets++; 404 vs->vs_oactive = 0; 405 vs->vs_tries = 0; 406 if (oc & VVXERR) { 407 vs->vs_if.if_oerrors++; 408 printf("vv%d: error, vvocsr=%b\n", unit, 0xffff & oc, 409 VV_OBITS); 410 } 411 if (vs->vs_ifuba.ifu_xtofree) { 412 m_freem(vs->vs_ifuba.ifu_xtofree); 413 vs->vs_ifuba.ifu_xtofree = 0; 414 } 415 if (vs->vs_if.if_snd.ifq_head == 0) { 416 vs->vs_lastx = 256; 417 return; 418 } 419 vvstart(unit); 420 } 421 422 /* 423 * V2lni interface receiver interrupt. 424 * If input error just drop packet. 425 * Otherwise purge input buffered data path and examine 426 * packet to determine type. If can't determine length 427 * from type, then have to drop packet. Othewise decapsulate 428 * packet based on type and pass to type specific higher-level 429 * input routine. 430 */ 431 vvrint(unit) 432 int unit; 433 { 434 register struct vv_softc *vs = &vv_softc[unit]; 435 struct vvreg *addr = (struct vvreg *)vvinfo[unit]->ui_addr; 436 register struct vv_header *vv; 437 register struct ifqueue *inq; 438 struct mbuf *m; 439 int ubainfo, len, off; 440 short resid; 441 442 vs->vs_if.if_ipackets++; 443 /* 444 * Purge BDP; drop if input error indicated. 445 */ 446 if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP) 447 UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_r.ifrw_bdp); 448 if (addr->vvicsr & VVRERR) { 449 /* 450 printf("vv%d: error vvicsr = %b\n", unit, 451 0xffff&(addr->vvicsr), VV_IBITS); 452 */ 453 goto dropit; 454 } 455 456 /* 457 * Get packet length from word count residue 458 * 459 * Compute header offset if trailer protocol 460 * 461 * Pull packet off interface. Off is nonzero if packet 462 * has trailing header; if_rubaget will then force this header 463 * information to be at the front. The vh_info field 464 * carries the offset to the trailer data in trailer 465 * format packets. 466 */ 467 vv = (struct vv_header *)(vs->vs_ifuba.ifu_r.ifrw_addr); 468 if (vv_trace) 469 vvprt_hdr("vi", vv); 470 resid = addr->vviwc; 471 if (resid) 472 resid |= 0176000; /* ugly!!!! */ 473 len = (((sizeof (struct vv_header) + VVMRU) >> 1) + resid) << 1; 474 len -= sizeof(struct vv_header); 475 if (len > VVMRU) 476 goto dropit; 477 #define vvdataaddr(vv, off, type) ((type)(((caddr_t)((vv)+1)+(off)))) 478 if (vv_dotrailer && vv->vh_type >= RING_IPTrailer && 479 vv->vh_type < RING_IPTrailer+RING_IPNTrailer){ 480 off = (vv->vh_type - RING_IPTrailer) * 512; 481 if (off > VVMTU) 482 goto dropit; 483 vv->vh_type = *vvdataaddr(vv, off, u_short *); 484 resid = *(vvdataaddr(vv, off+2, u_short *)); 485 if (off + resid > len) 486 goto dropit; 487 len = off + resid; 488 } else 489 off = 0; 490 if (len == 0) 491 goto dropit; 492 m = if_rubaget(&vs->vs_ifuba, len, off); 493 if (m == 0) 494 goto dropit; 495 if (off) { 496 m->m_off += 2 * sizeof(u_short); 497 m->m_len -= 2 * sizeof(u_short); 498 } 499 switch (vv->vh_type) { 500 #ifdef INET 501 case RING_IP: 502 schednetisr(NETISR_IP); 503 inq = &ipintrq; 504 break; 505 #endif 506 default: 507 printf("vv%d: unknown pkt type 0x%x\n", unit, vv->vh_type); 508 m_freem(m); 509 goto setup; 510 } 511 if (IF_QFULL(inq)) { 512 IF_DROP(inq); 513 m_freem(m); 514 } else 515 IF_ENQUEUE(inq, m); 516 517 setup: 518 /* 519 * Restart the read for next packet. 520 */ 521 ubainfo = vs->vs_ifuba.ifu_r.ifrw_info; 522 addr->vviba = (u_short) ubainfo; 523 addr->vviea = (u_short) (ubainfo >> 16); 524 addr->vviwc = -(sizeof (struct vv_header) + VVMTU) >> 1; 525 addr->vvicsr = VV_RST | VV_CONF; 526 addr->vvicsr |= VV_IEN | VV_DEN | VV_ENB; 527 return; 528 529 dropit: 530 vs->vs_if.if_ierrors++; 531 /* 532 printf("vv%d: error vvicsr = %b\n", unit, 533 0xffff&(addr->vvicsr), VV_IBITS); 534 */ 535 goto setup; 536 } 537 538 /* 539 * V2lni output routine. 540 * Encapsulate a packet of type family for the local net. 541 * Use trailer local net encapsulation if enough data in first 542 * packet leaves a multiple of 512 bytes of data in remainder. 543 */ 544 vvoutput(ifp, m0, dst) 545 struct ifnet *ifp; 546 struct mbuf *m0; 547 struct sockaddr *dst; 548 { 549 register struct mbuf *m = m0; 550 register struct vv_header *vv; 551 register int off; 552 int type, dest, s, error; 553 554 switch (dst->sa_family) { 555 #ifdef INET 556 case AF_INET: { 557 dest = ((struct sockaddr_in *)dst)->sin_addr.s_addr; 558 if (dest & 0x00ffff00) { 559 error = EPERM; 560 goto bad; 561 } 562 dest = (dest >> 24) & 0xff; 563 off = ntohs((u_short)mtod(m, struct ip *)->ip_len) - m->m_len; 564 if (vv_dotrailer && off > 0 && (off & 0x1ff) == 0 && 565 m->m_off >= MMINOFF + 2 * sizeof (u_short)) { 566 type = RING_IPTrailer + (off>>9); 567 m->m_off -= 2 * sizeof (u_short); 568 m->m_len += 2 * sizeof (u_short); 569 *mtod(m, u_short *) = RING_IP; 570 *(mtod(m, u_short *) + 1) = m->m_len; 571 goto gottrailertype; 572 } 573 type = RING_IP; 574 off = 0; 575 goto gottype; 576 } 577 #endif 578 default: 579 printf("vv%d: can't handle af%d\n", ifp->if_unit, 580 dst->sa_family); 581 error = EAFNOSUPPORT; 582 goto bad; 583 } 584 585 gottrailertype: 586 /* 587 * Packet to be sent as trailer: move first packet 588 * (control information) to end of chain. 589 */ 590 while (m->m_next) 591 m = m->m_next; 592 m->m_next = m0; 593 m = m0->m_next; 594 m0->m_next = 0; 595 m0 = m; 596 597 gottype: 598 /* 599 * Add local net header. If no space in first mbuf, 600 * allocate another. 601 */ 602 if (m->m_off > MMAXOFF || 603 MMINOFF + sizeof (struct vv_header) > m->m_off) { 604 m = m_get(M_DONTWAIT, MT_HEADER); 605 if (m == 0) { 606 error = ENOBUFS; 607 goto bad; 608 } 609 m->m_next = m0; 610 m->m_off = MMINOFF; 611 m->m_len = sizeof (struct vv_header); 612 } else { 613 m->m_off -= sizeof (struct vv_header); 614 m->m_len += sizeof (struct vv_header); 615 } 616 vv = mtod(m, struct vv_header *); 617 vv->vh_shost = ifp->if_host[0]; 618 vv->vh_dhost = dest; 619 vv->vh_version = RING_VERSION; 620 vv->vh_type = type; 621 vv->vh_info = off; 622 if (vv_trace) 623 vvprt_hdr("vo", vv); 624 625 /* 626 * Queue message on interface, and start output if interface 627 * not yet active. 628 */ 629 s = splimp(); 630 if (IF_QFULL(&ifp->if_snd)) { 631 IF_DROP(&ifp->if_snd); 632 error = ENOBUFS; 633 goto qfull; 634 } 635 IF_ENQUEUE(&ifp->if_snd, m); 636 if (vv_softc[ifp->if_unit].vs_oactive == 0) 637 vvstart(ifp->if_unit); 638 splx(s); 639 return (0); 640 641 qfull: 642 m0 = m; 643 splx(s); 644 bad: 645 m_freem(m0); 646 return(error); 647 } 648 649 /* 650 * vvprt_hdr(s, v) print the local net header in "v" 651 * with title is "s" 652 */ 653 vvprt_hdr(s, v) 654 char *s; 655 register struct vv_header *v; 656 { 657 printf("%s: dsvti: 0x%x 0x%x 0x%x 0x%x 0x%x\n", 658 s, 659 0xff & (int)(v->vh_dhost), 0xff & (int)(v->vh_shost), 660 0xff & (int)(v->vh_version), 0xff & (int)(v->vh_type), 661 0xffff & (int)(v->vh_info)); 662 } 663 664 /* 665 * print "l" hex bytes starting at "s" 666 */ 667 vvprt_hex(s, l) 668 char *s; 669 int l; 670 { 671 register int i; 672 register int z; 673 674 for (i=0 ; i < l; i++) { 675 z = 0xff & (int)(*(s + i)); 676 printf("%c%c ", 677 "0123456789abcdef"[(z >> 4) & 0x0f], 678 "0123456789abcdef"[z & 0x0f] 679 ); 680 } 681 } 682 #endif 683