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