readcis.c revision 61804
1/* 2 * Copyright (c) 1995 Andrew McRae. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 3. The name of the author may not be used to endorse or promote products 13 * derived from this software without specific prior written permission. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#ifndef lint 28static const char rcsid[] = 29 "$FreeBSD: head/usr.sbin/pccard/pccardd/readcis.c 61804 2000-06-18 20:22:11Z roberto $"; 30#endif /* not lint */ 31 32/* 33 * Code cleanup, bug-fix and extension 34 * by Tatsumi Hosokawa <hosokawa@mt.cs.keio.ac.jp> 35 */ 36 37#include <err.h> 38#include <stdio.h> 39#include <stdlib.h> 40#include <string.h> 41#include <unistd.h> 42#include <sys/ioctl.h> 43 44#include <pccard/cardinfo.h> 45#include <pccard/cis.h> 46 47#include "readcis.h" 48 49#ifdef RATOCLAN 50static int rex5588 = 0; 51#endif 52 53static int read_attr(int, char *, int); 54static int ck_linktarget(int, off_t, int); 55static void cis_info(struct cis *, unsigned char *, int); 56static void device_desc(unsigned char *, int, struct dev_mem *); 57static void config_map(struct cis *, unsigned char *, int); 58static void cis_config(struct cis *, unsigned char *, int); 59static void cis_func_id(struct cis *, unsigned char *, int); 60static void cis_network_ext(struct cis *, unsigned char *, int); 61static struct tuple_list *read_one_tuplelist(int, int, off_t); 62static struct tuple_list *read_tuples(int); 63static struct tuple *find_tuple_in_list(struct tuple_list *, unsigned char); 64static struct tuple_info *get_tuple_info(unsigned char); 65 66static struct tuple_info tuple_info[] = { 67 {"Null tuple", 0x00, 0}, 68 {"Common memory descriptor", 0x01, 255}, 69 {"Long link to next chain for CardBus", 0x02, 255}, 70 {"Indirect access", 0x03, 255}, 71 {"Configuration map for CardBus", 0x04, 255}, 72 {"Configuration entry for CardBus", 0x05, 255}, 73 {"Long link to next chain for MFC", 0x06, 255}, 74 {"Base address register for CardBus", 0x07, 6}, 75 {"Checksum", 0x10, 5}, 76 {"Long link to attribute memory", 0x11, 4}, 77 {"Long link to common memory", 0x12, 4}, 78 {"Link target", 0x13, 3}, 79 {"No link", 0x14, 0}, 80 {"Version 1 info", 0x15, 255}, 81 {"Alternate language string", 0x16, 255}, 82 {"Attribute memory descriptor", 0x17, 255}, 83 {"JEDEC descr for common memory", 0x18, 255}, 84 {"JEDEC descr for attribute memory", 0x19, 255}, 85 {"Configuration map", 0x1A, 255}, 86 {"Configuration entry", 0x1B, 255}, 87 {"Other conditions for common memory", 0x1C, 255}, 88 {"Other conditions for attribute memory", 0x1D, 255}, 89 {"Geometry info for common memory", 0x1E, 255}, 90 {"Geometry info for attribute memory", 0x1F, 255}, 91 {"Manufacturer ID", 0x20, 4}, 92 {"Functional ID", 0x21, 2}, 93 {"Functional EXT", 0x22, 255}, 94 {"Software interleave", 0x23, 2}, 95 {"Version 2 Info", 0x40, 255}, 96 {"Data format", 0x41, 255}, 97 {"Geometry", 0x42, 4}, 98 {"Byte order", 0x43, 2}, 99 {"Card init date", 0x44, 4}, 100 {"Battery replacement", 0x45, 4}, 101 {"Organization", 0x46, 255}, 102 {"Terminator", 0xFF, 0}, 103 {0, 0, 0} 104}; 105 106/* 107 * After reading the tuples, decode the relevant ones. 108 */ 109struct cis * 110readcis(int fd) 111{ 112 struct tuple_list *tl; 113 struct tuple *tp; 114 struct cis *cp; 115 116 cp = xmalloc(sizeof(*cp)); 117 cp->tlist = read_tuples(fd); 118 if (cp->tlist == 0) 119 return (NULL); 120 121 for (tl = cp->tlist; tl; tl = tl->next) 122 for (tp = tl->tuples; tp; tp = tp->next) { 123#if 0 124 printf("tuple code = 0x%02x, data is\n", tp->code); 125 dump(tp->data, tp->length); 126#endif 127 switch (tp->code) { 128 case CIS_MEM_COMMON: /* 0x01 */ 129 device_desc(tp->data, tp->length, &cp->common_mem); 130 break; 131 case CIS_INFO_V1: /* 0x15 */ 132 cis_info(cp, tp->data, tp->length); 133 break; 134 case CIS_MEM_ATTR: /* 0x17 */ 135 device_desc(tp->data, tp->length, &cp->attr_mem); 136 break; 137 case CIS_CONF_MAP: /* 0x1A */ 138 config_map(cp, tp->data, tp->length); 139 break; 140 case CIS_CONFIG: /* 0x1B */ 141 cis_config(cp, tp->data, tp->length); 142 break; 143 case CIS_FUNC_ID: /* 0x21 */ 144 cis_func_id(cp, tp->data, tp->length); 145 break; 146 case CIS_FUNC_EXT: /* 0x22 */ 147 if (cp->func_id1 == 6) /* LAN adaptor */ 148 cis_network_ext(cp, tp->data, tp->length); 149 break; 150 } 151 } 152 return (cp); 153} 154 155/* 156 * free_cis - delete cis entry. 157 */ 158void 159freecis(struct cis *cp) 160{ 161 struct cis_ioblk *io; 162 struct cis_memblk *mem; 163 struct cis_config *conf; 164 struct tuple *tp; 165 struct tuple_list *tl; 166 167 while ((tl = cp->tlist) != 0) { 168 cp->tlist = tl->next; 169 while ((tp = tl->tuples) != 0) { 170 tl->tuples = tp->next; 171 if (tp->data) 172 free(tp->data); 173 } 174 } 175 176 while ((conf = cp->conf) != 0) { 177 cp->conf = conf->next; 178 while ((io = conf->io) != 0) { 179 conf->io = io->next; 180 free(io); 181 } 182 while ((mem = conf->mem) != 0) { 183 conf->mem = mem->next; 184 free(mem); 185 } 186 free(conf); 187 } 188 free(cp); 189} 190 191/* 192 * Fills in CIS version data. 193 */ 194static void 195cis_info(struct cis *cp, unsigned char *p, int len) 196{ 197 cp->maj_v = *p++; 198 cp->min_v = *p++; 199 len -= 2; 200 if (cp->manuf) { 201 free(cp->manuf); 202 cp->manuf = NULL; 203 } 204 if (len > 1 && *p != 0xff) { 205 cp->manuf = strdup(p); 206 while (*p++ && --len > 0); 207 } 208 if (cp->vers) { 209 free(cp->vers); 210 cp->vers = NULL; 211 } 212 if (len > 1 && *p != 0xff) { 213 cp->vers = strdup(p); 214 while (*p++ && --len > 0); 215 } else { 216 cp->vers = strdup("?"); 217 } 218 if (cp->add_info1) { 219 free(cp->add_info1); 220 cp->add_info1 = NULL; 221 } 222 if (len > 1 && *p != 0xff) { 223 cp->add_info1 = strdup(p); 224 while (*p++ && --len > 0); 225 } 226 if (cp->add_info2) { 227 free(cp->add_info2); 228 cp->add_info2 = NULL; 229 } 230 if (len > 1 && *p != 0xff) 231 cp->add_info2 = strdup(p); 232} 233 234/* 235 * Fills in CIS function ID. 236 */ 237static void 238cis_func_id(struct cis *cp, unsigned char *p, int len) 239{ 240 cp->func_id1 = *p++; 241 cp->func_id2 = *p++; 242} 243 244static void 245cis_network_ext(struct cis *cp, unsigned char *p, int len) 246{ 247 int i; 248 249 switch (p[0]) { 250 case 4: /* Node ID */ 251 if (len <= 2 || len < p[1] + 2) 252 return; 253 254 if (cp->lan_nid) 255 free(cp->lan_nid); 256 cp->lan_nid = xmalloc(p[1]); 257 258 for (i = 0; i <= p[1]; i++) 259 cp->lan_nid[i] = p[i + 1]; 260 break; 261 } 262} 263 264/* 265 * "FUJITSU LAN Card (FMV-J182)" has broken CIS 266 */ 267static int 268fmvj182_check(unsigned char *p) 269{ 270 char manuf[BUFSIZ], vers[BUFSIZ]; 271 272 p++; /* major version */ 273 p++; /* minor version */ 274 strncpy(manuf, p, sizeof(manuf) - 1); 275 while (*p++); 276 strncpy(vers, p, sizeof(vers) - 1); 277 if (!strcmp(manuf, "FUJITSU") && !strcmp(vers, "LAN Card(FMV-J182)")) 278 return 1; 279 else 280 return 0; 281} 282 283#ifdef RATOCLAN 284/* 285 * "RATOC LAN Card (REX-5588)" has broken CIS 286 */ 287static int 288rex5588_check(unsigned char *p) 289{ 290 char manuf[BUFSIZ], vers[BUFSIZ]; 291 292 p++; /* major version */ 293 p++; /* minor version */ 294 strncpy(manuf, p, sizeof(manuf) - 1); 295 while (*p++); 296 strncpy(vers, p, sizeof(manuf) - 1); 297 if (!strcmp(manuf, "PCMCIA LAN MBH10304 ES")) 298 return 1; 299 else 300 return 0; 301} 302#endif 303 304#ifdef HSSYNTH 305/* 306 * Broken CIS for "HITACHI MICROCOMPUTER SYSTEM LTD." "MSSHVPC02" 307 */ 308static int 309hss_check(unsigned char *p) 310{ 311 char manuf[BUFSIZ], vers[BUFSIZ]; 312 313 p++; /* major version */ 314 p++; /* minor version */ 315 strncpy(manuf, p, sizeof(manuf) - 1); 316 while (*p++); 317 strncpy(vers, p, sizeof(vers) - 1); 318 if (!strcmp(manuf, "HITACHI MICROCOMPUTER SYSTEMS LTD.") 319 && !strcmp(vers, "MSSHVPC02")) 320 return 1; 321 else 322 return 0; 323} 324#endif /* HSSYNTH */ 325 326/* 327 * device_desc - decode device descriptor. 328 */ 329static void 330device_desc(unsigned char *p, int len, struct dev_mem *dp) 331{ 332 while (len > 0 && *p != 0xFF) { 333 dp->valid = 1; 334 dp->type = (*p & 0xF0) >> 4; 335 dp->wps = !!(*p & 0x8); 336 dp->speed = *p & 7; 337 p++; 338 if (*p != 0xFF) { 339 dp->addr = (*p >> 3) & 0xF; 340 dp->units = *p & 7; 341 } 342 p++; 343 len -= 2; 344 } 345} 346 347/* 348 * configuration map of card control register. 349 */ 350static void 351config_map(struct cis *cp, unsigned char *p, int len) 352{ 353 unsigned char *p1; 354 int rlen = (*p & 3) + 1; 355 356 p1 = p + 1; 357 cp->last_config = *p1++ & 0x3F; 358 cp->reg_addr = parse_num(rlen | 0x10, p1, &p1, 0); 359 cp->ccrs = *p1; 360} 361 362/* 363 * Parse variable length value. 364 */ 365u_int 366parse_num(int sz, u_char *p, u_char **q, int ofs) 367{ 368 u_int num = 0; 369 370 switch (sz) { 371 case 0: 372 case 0x10: 373 break; 374 case 1: 375 case 0x11: 376 num = (*p++) + ofs; 377 break; 378 case 2: 379 case 0x12: 380 num = tpl16(p) + ofs; 381 p += 2; 382 break; 383 case 0x13: 384 num = tpl24(p) + ofs; 385 p += 3; 386 break; 387 case 3: 388 case 0x14: 389 num = tpl32(p) + ofs; 390 p += 4; 391 break; 392 } 393 if (q) 394 *q = p; 395 return num; 396} 397 398/* 399 * CIS config entry - Decode and build configuration entry. 400 */ 401static void 402cis_config(struct cis *cp, unsigned char *p, int len) 403{ 404 int x; 405 int i, j; 406 struct cis_config *conf, *last; 407 unsigned char feat; 408 409 conf = xmalloc(sizeof(*conf)); 410 if ((last = cp->conf) != 0) { 411 while (last->next) 412 last = last->next; 413 last->next = conf; 414 } else 415 cp->conf = conf; 416 conf->id = *p & 0x3F; /* Config index */ 417#ifdef RATOCLAN 418 if (rex5588 && conf->id >= 0x08 && conf->id <= 0x1d) 419 conf->id |= 0x20; 420#endif 421 if (*p & 0x40) /* Default flag */ 422 cp->def_config = conf; 423 if (*p++ & 0x80) 424 p++; /* Interface byte skip */ 425 feat = *p++; /* Features byte */ 426 for (i = 0; i < CIS_FEAT_POWER(feat); i++) { 427 unsigned char parms = *p++; 428 429 conf->pwr = 1; 430 for (j = 0; j < 8; j++) 431 if (parms & (1 << j)) 432 while (*p++ & 0x80); 433 } 434 if (feat & CIS_FEAT_TIMING) { 435 conf->timing = 1; 436 i = *p++; 437 if (CIS_WAIT_SCALE(i) != 3) 438 p++; 439 if (CIS_READY_SCALE(i) != 7) 440 p++; 441 if (CIS_RESERVED_SCALE(i) != 7) 442 p++; 443 } 444 if (feat & CIS_FEAT_I_O) { 445 conf->iospace = 1; 446 if (CIS_IO_RANGE & *p) 447 conf->io_blks = CIS_IO_BLKS(p[1]) + 1; 448 conf->io_addr = CIS_IO_ADDR(*p); 449 conf->io_bus = (*p >> 5) & 3; /* CIS_IO_8BIT | CIS_IO_16BIT */ 450 if (*p++ & CIS_IO_RANGE) { 451 struct cis_ioblk *io; 452 struct cis_ioblk *last_io = NULL; 453 454 i = CIS_IO_ADSZ(*p); 455 j = CIS_IO_BLKSZ(*p++); 456 for (x = 0; x < conf->io_blks; x++) { 457 io = xmalloc(sizeof(*io)); 458 if (last_io) 459 last_io->next = io; 460 else 461 conf->io = io; 462 last_io = io; 463 io->addr = parse_num(i, p, &p, 0); 464 io->size = parse_num(j, p, &p, 1); 465 } 466 } 467 } 468 if (feat & CIS_FEAT_IRQ) { 469 conf->irq = 1; 470 conf->irqlevel = *p & 0xF; 471 conf->irq_flags = *p & 0xF0; 472 if (*p++ & CIS_IRQ_MASK) { 473 conf->irq_mask = tpl16(p); 474 p += 2; 475 } 476 } 477 switch (CIS_FEAT_MEMORY(feat)) { 478 case 0: 479 break; 480 case 1: 481 conf->memspace = 1; 482 conf->mem = xmalloc(sizeof(*conf->mem)); 483 conf->mem->length = tpl16(p) << 8; 484 break; 485 case 2: 486 conf->memspace = 1; 487 conf->mem = xmalloc(sizeof(*conf->mem)); 488 conf->mem->length = tpl16(p) << 8; 489 conf->mem->address = tpl16(p + 2) << 8; 490 break; 491 case 3: { 492 struct cis_memblk *mem; 493 struct cis_memblk *last_mem = NULL; 494 495 conf->memspace = 1; 496 x = *p++; 497 conf->memwins = CIS_MEM_WINS(x); 498 for (i = 0; i < conf->memwins; i++) { 499 mem = xmalloc(sizeof(*mem)); 500 if (last_mem) 501 last_mem->next = mem; 502 else 503 conf->mem = mem; 504 last_mem = mem; 505 mem->length = parse_num(CIS_MEM_LENSZ(x) | 0x10, p, &p, 0) << 8; 506 mem->address = parse_num(CIS_MEM_ADDRSZ(x) | 0x10, p, &p, 0) << 8; 507 if (x & CIS_MEM_HOST) { 508 mem->host_address = parse_num(CIS_MEM_ADDRSZ(x) | 0x10, 509 p, &p, 0) << 8; 510 } 511 } 512 break; 513 } 514 } 515 if (feat & CIS_FEAT_MISC) { 516 conf->misc_valid = 1; 517 conf->misc = *p++; 518 } 519} 520 521/* 522 * Read the tuples from the card. 523 * The processing of tuples is as follows: 524 * - Read tuples at attribute memory, offset 0. 525 * - If a CIS_END is the first tuple, look for 526 * a tuple list at common memory offset 0; this list 527 * must start with a LINKTARGET. 528 * - If a long link tuple was encountered, execute the long 529 * link. 530 * - If a no-link tuple was seen, terminate processing. 531 * - If no no-link tuple exists, and no long link tuple 532 * exists while processing the primary tuple list, 533 * then look for a LINKTARGET tuple in common memory. 534 * - If a long link tuple is found in any list, then process 535 * it. Only one link is allowed per list. 536 */ 537static struct tuple_list *tlist; 538 539static struct tuple_list * 540read_tuples(int fd) 541{ 542 struct tuple_list *tl = 0, *last_tl; 543 struct tuple *tp; 544 int flag; 545 off_t offs; 546 547 tlist = 0; 548 last_tl = tlist = read_one_tuplelist(fd, MDF_ATTR, (off_t) 0); 549 550 /* Now start processing the links (if any). */ 551 do { 552 flag = MDF_ATTR; 553 tp = find_tuple_in_list(last_tl, CIS_LONGLINK_A); 554 if (tp == 0) { 555 flag = 0; 556 tp = find_tuple_in_list(last_tl, CIS_LONGLINK_C); 557 } 558 if (tp && tp->length == 4) { 559 offs = tpl32(tp->data); 560#ifdef DEBUG 561 printf("Checking long link at %qd (%s memory)\n", 562 offs, flag ? "Attribute" : "Common"); 563#endif 564 /* If a link was found, read the tuple list from it. */ 565 if (ck_linktarget(fd, offs, flag)) { 566 tl = read_one_tuplelist(fd, flag, offs); 567 last_tl->next = tl; 568 last_tl = tl; 569 } 570 } else 571 tl = 0; 572 } while (tl); 573 574 /* 575 * If the primary list had no NOLINK tuple, and no LINKTARGET, 576 * then try to read a tuple list at common memory (offset 0). 577 */ 578 if (find_tuple_in_list(tlist, CIS_NOLINK) == 0 && tlist->next == 0 && 579 ck_linktarget(fd, (off_t) 0, 0)) { 580#ifdef DEBUG 581 printf("Reading long link at %qd (%s memory)\n", 582 offs, flag ? "Attribute" : "Common"); 583#endif 584 tlist->next = read_one_tuplelist(fd, 0, (off_t) 0); 585 } 586 return (tlist); 587} 588 589/* 590 * Read one tuple list from the card. 591 */ 592static struct tuple_list * 593read_one_tuplelist(int fd, int flags, off_t offs) 594{ 595 struct tuple *tp, *last_tp = 0; 596 struct tuple_list *tl; 597 struct tuple_info *tinfo; 598 int total = 0; 599 unsigned char code, length; 600 int fmvj182 = 0; 601#ifdef HSSYNTH 602 int hss = 0; 603#endif /* HSSYNTH */ 604 605 /* Check to see if this memory has already been scanned. */ 606 for (tl = tlist; tl; tl = tl->next) 607 if (tl->offs == offs && tl->flags == (flags & MDF_ATTR)) 608 return (0); 609 tl = xmalloc(sizeof(*tl)); 610 tl->offs = offs; 611 tl->flags = flags & MDF_ATTR; 612 ioctl(fd, PIOCRWFLAG, &flags); 613 lseek(fd, offs, SEEK_SET); 614 do { 615 if (read_attr(fd, &code, 1) != 1) { 616 warn("CIS code read"); 617 break; 618 } 619 total++; 620 if (code == CIS_NULL) 621 continue; 622 tp = xmalloc(sizeof(*tp)); 623 tp->code = code; 624 if (code == CIS_END) 625 length = 0; 626 else { 627 if (read_attr(fd, &length, 1) != 1) { 628 warn("CIS len read"); 629 break; 630 } 631 total++; 632 if (fmvj182 && (code == 0x1b) && (length == 25)) 633 length = 31; 634 } 635 tp->length = length; 636#ifdef DEBUG 637 printf("Tuple code = 0x%x, len = %d\n", code, length); 638#endif 639 if (length == 0xFF) { 640 length = tp->length = 0; 641 code = CIS_END; 642 } 643 if (length != 0) { 644 total += length; 645 tp->data = xmalloc(length); 646 if (read_attr(fd, tp->data, length) != length) { 647 warn("CIS read"); 648 break; 649 } 650 } 651 652 /* 653 * Check the tuple, and ignore it if it isn't in the table 654 * or the length is illegal. 655 */ 656 tinfo = get_tuple_info(code); 657 if (code == CIS_INFO_V1) { 658 /* Hack for broken CIS of FMV-J182 Ethernet card */ 659 fmvj182 = fmvj182_check(tp->data); 660#ifdef RATOCLAN 661 /* Hack for RATOC LAN card */ 662 rex5588 = rex5588_check(tp->data); 663#endif /* RATOCLAN */ 664#ifdef HSSYNTH 665 /* Hack for Hitachi Speech Synthesis card */ 666 hss = hss_check(tp->data); 667#endif /* HSSYNTH */ 668 } 669 if (tinfo == NULL || (tinfo->length != 255 && tinfo->length > length)) { 670 printf("code %s ignored\n", tuple_name(code)); 671 tp->code = CIS_NULL; 672 } 673 if (tl->tuples == NULL) 674 tl->tuples = tp; 675 else 676 last_tp->next = tp; 677 last_tp = tp; 678 } while (code != CIS_END && total < 1024); 679 return (tl); 680} 681 682/* 683 * return true if the offset points to a LINKTARGET tuple. 684 */ 685static int 686ck_linktarget(int fd, off_t offs, int flag) 687{ 688 char blk[5]; 689 690 ioctl(fd, PIOCRWFLAG, &flag); 691 lseek(fd, offs, SEEK_SET); 692 if (read_attr(fd, blk, 5) != 5) 693 return (0); 694 if (blk[0] == 0x13 && 695 blk[1] == 0x3 && 696 blk[2] == 'C' && 697 blk[3] == 'I' && 698 blk[4] == 'S') 699 return (1); 700 return (0); 701} 702 703/* 704 * find_tuple_in_list - find a tuple within a 705 * single tuple list. 706 */ 707static struct tuple * 708find_tuple_in_list(struct tuple_list *tl, unsigned char code) 709{ 710 struct tuple *tp; 711 712 for (tp = tl->tuples; tp; tp = tp->next) 713 if (tp->code == code) 714 break; 715 return (tp); 716} 717 718static int 719read_attr(int fd, char *bp, int len) 720{ 721 char blk[1024], *p = blk; 722 int i, l; 723 724 if (len > sizeof(blk) / 2) 725 len = sizeof(blk) / 2; 726 l = i = read(fd, blk, len * 2); 727 if (i <= 0) { 728 printf("Read return %d bytes (expected %d)\n", i, len * 2); 729 return (i); 730 } 731 while (i > 0) { 732 *bp++ = *p++; 733 p++; 734 i -= 2; 735 } 736 return (l / 2); 737} 738 739/* 740 * return table entry for code. 741 */ 742static struct tuple_info * 743get_tuple_info(unsigned char code) 744{ 745 struct tuple_info *tp; 746 747 for (tp = tuple_info; tp->name; tp++) 748 if (tp->code == code) 749 return (tp); 750 printf("Code %d not found\n", code); 751 return (0); 752} 753 754char * 755tuple_name(unsigned char code) 756{ 757 struct tuple_info *tp; 758 759 tp = get_tuple_info(code); 760 if (tp) 761 return (tp->name); 762 return ("Unknown"); 763} 764