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