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