readcis.c revision 54267
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 54267 1999-12-07 19:23:56Z imp $"; 30#endif /* not lint */ 31 32#include <err.h> 33#include <stdio.h> 34#include <stdlib.h> 35#include <string.h> 36#include <unistd.h> 37#include <sys/ioctl.h> 38 39#include <pccard/cardinfo.h> 40#include <pccard/cis.h> 41 42#include "readcis.h" 43 44static int read_attr(int, char *, int); 45static int ck_linktarget(int, off_t, int); 46static void cis_info(struct cis *, unsigned char *, int); 47static void device_desc(unsigned char *, int, struct dev_mem *); 48static void config_map(struct cis *, unsigned char *, int); 49static void cis_config(struct cis *, unsigned char *, int); 50static struct tuple_list *read_one_tuplelist(int, int, off_t); 51static struct tuple_list *read_tuples(int); 52static struct tuple *find_tuple_in_list(struct tuple_list *, unsigned char); 53static struct tuple_info *get_tuple_info(unsigned char); 54 55static struct tuple_info tuple_info[] = { 56 {"Null tuple", 0x00, 0}, 57 {"Common memory descriptor", 0x01, 255}, 58 {"Checksum", 0x10, 5}, 59 {"Long link to attribute memory", 0x11, 4}, 60 {"Long link to common memory", 0x12, 4}, 61 {"Link target", 0x13, 3}, 62 {"No link", 0x14, 0}, 63 {"Version 1 info", 0x15, 255}, 64 {"Alternate language string", 0x16, 255}, 65 {"Attribute memory descriptor", 0x17, 255}, 66 {"JEDEC descr for common memory", 0x18, 255}, 67 {"JEDEC descr for attribute memory", 0x19, 255}, 68 {"Configuration map", 0x1A, 255}, 69 {"Configuration entry", 0x1B, 255}, 70 {"Other conditions for common memory", 0x1C, 255}, 71 {"Other conditions for attribute memory", 0x1D, 255}, 72 {"Geometry info for common memory", 0x1E, 255}, 73 {"Geometry info for attribute memory", 0x1F, 255}, 74 {"Manufacturer ID", 0x20, 4}, 75 {"Functional ID", 0x21, 255}, 76 {"Functional EXT", 0x22, 255}, 77 {"Software interleave", 0x23, 2}, 78 {"Version 2 Info", 0x40, 255}, 79 {"Data format", 0x41, 255}, 80 {"Geometry", 0x42, 4}, 81 {"Byte order", 0x43, 2}, 82 {"Card init date", 0x44, 4}, 83 {"Battery replacement", 0x45, 4}, 84 {"Organisation", 0x46, 255}, 85 {"Terminator", 0xFF, 255}, 86 {0, 0, 0} 87}; 88 89/* 90 * After reading the tuples, decode the relevant ones. 91 */ 92struct cis * 93readcis(int fd) 94{ 95 struct tuple_list *tl; 96 struct tuple *tp; 97 struct cis *cp; 98 99 cp = xmalloc(sizeof(*cp)); 100 cp->tlist = read_tuples(fd); 101 if (cp->tlist == 0) 102 return (NULL); 103 104 for (tl = cp->tlist; tl; tl = tl->next) 105 for (tp = tl->tuples; tp; tp = tp->next) { 106#if 0 107 printf("tuple code = 0x%02x, data is\n", tp->code); 108 dump(tp->data, tp->length); 109#endif 110 switch (tp->code) { 111 case CIS_MEM_COMMON: /* 0x01 */ 112 device_desc(tp->data, tp->length, &cp->common_mem); 113 break; 114 case CIS_INFO_V1: /* 0x15 */ 115 cis_info(cp, tp->data, tp->length); 116 break; 117 case CIS_MEM_ATTR: /* 0x17 */ 118 device_desc(tp->data, tp->length, &cp->attr_mem); 119 break; 120 case CIS_CONF_MAP: /* 0x1A */ 121 config_map(cp, tp->data, tp->length); 122 break; 123 case CIS_CONFIG: /* 0x1B */ 124 cis_config(cp, tp->data, tp->length); 125 break; 126 } 127 } 128 return (cp); 129} 130 131/* 132 * free_cis - delete cis entry. 133 */ 134void 135freecis(struct cis *cp) 136{ 137 struct cis_ioblk *io; 138 struct cis_memblk *mem; 139 struct cis_config *conf; 140 struct tuple *tp; 141 struct tuple_list *tl; 142 143 while ((tl = cp->tlist) != 0) { 144 cp->tlist = tl->next; 145 while ((tp = tl->tuples) != 0) { 146 tl->tuples = tp->next; 147 if (tp->data) 148 free(tp->data); 149 } 150 } 151 152 while ((conf = cp->conf) != 0) { 153 cp->conf = conf->next; 154 while ((io = conf->io) != 0) { 155 conf->io = io->next; 156 free(io); 157 } 158 while ((mem = conf->mem) != 0) { 159 conf->mem = mem->next; 160 free(mem); 161 } 162 free(conf); 163 } 164 free(cp); 165} 166 167/* 168 * Fills in CIS version data. 169 */ 170static void 171cis_info(struct cis *cp, unsigned char *p, int len) 172{ 173 unsigned char *end = p + len; 174 *cp->manuf = *cp->vers = *cp->add_info1 = *cp->add_info2 = '\0'; 175 cp->maj_v = *p++; 176 cp->min_v = *p++; 177 if (p >= end) 178 return; 179 strncpy(cp->manuf, p, CIS_MAXSTR - 1); 180 cp->manuf[CIS_MAXSTR - 1] = '\0'; 181 p += strlen(p); 182 if (p >= end) 183 return; 184 strncpy(cp->vers, p, CIS_MAXSTR - 1); 185 cp->vers[CIS_MAXSTR - 1] = '\0'; 186 p += strlen(p); 187 if (p >= end) 188 return; 189 strncpy(cp->add_info1, p, CIS_MAXSTR - 1); 190 cp->add_info1[CIS_MAXSTR - 1] = '\0'; 191 p += strlen(p); 192 if (p >= end) 193 return; 194 strncpy(cp->add_info2, p, CIS_MAXSTR - 1); 195 cp->add_info2[CIS_MAXSTR - 1] = '\0'; 196} 197 198/* 199 * device_desc - decode device descriptor. 200 */ 201static void 202device_desc(unsigned char *p, int len, struct dev_mem *dp) 203{ 204 while (len > 0 && *p != 0xFF) { 205 dp->valid = 1; 206 dp->type = (*p & 0xF0) >> 4; 207 dp->wps = !!(*p & 0x8); 208 dp->speed = *p & 7; 209 p++; 210 if (*p != 0xFF) { 211 dp->addr = *p >> 3; 212 dp->units = *p & 7; 213 } 214 p++; 215 len -= 2; 216 } 217} 218 219/* 220 * configuration map of card control register. 221 */ 222static void 223config_map(struct cis *cp, unsigned char *p, int len) 224{ 225 unsigned char *p1; 226 int i; 227 union { 228 unsigned long l; 229 unsigned char b[4]; 230 } u; 231 232 p1 = p + 1; 233 cp->last_config = *p1++ & 0x3F; 234 u.l = 0; 235 for (i = 0; i <= (*p & 3); i++) 236 u.b[i] = *p1++; 237 cp->reg_addr = u.l; 238 cp->ccrs = *p1; 239} 240 241/* 242 * CIS config entry - Decode and build configuration entry. 243 */ 244static void 245cis_config(struct cis *cp, unsigned char *p, int len) 246{ 247 int x; 248 int i, j; 249 union { 250 unsigned long l; 251 unsigned char b[4]; 252 } u; 253 struct cis_config *conf, *last; 254 struct cis_memblk *mem; 255 unsigned char feat; 256 struct cis_memblk *lastmem = 0; 257 258 conf = xmalloc(sizeof(*conf)); 259 if ((last = cp->conf) != 0) { 260 while (last->next) 261 last = last->next; 262 last->next = conf; 263 } else 264 cp->conf = conf; 265 conf->id = *p & 0x3F; 266 if (*p & 0x40) 267 cp->def_config = conf; 268 if (*p++ & 0x80) 269 p++; 270 feat = *p++; 271 for (i = 0; i < CIS_FEAT_POWER(feat); i++) { 272 unsigned char parms = *p++; 273 274 conf->pwr = 1; 275 for (j = 0; j < 8; j++) 276 if (parms & (1 << j)) 277 while (*p++ & 0x80); 278 } 279 if (feat & CIS_FEAT_TIMING) { 280 conf->timing = 1; 281 i = *p++; 282 if (CIS_WAIT_SCALE(i) != 3) 283 p++; 284 if (CIS_READY_SCALE(i) != 7) 285 p++; 286 if (CIS_RESERVED_SCALE(i) != 7) 287 p++; 288 } 289 if (feat & CIS_FEAT_I_O) { 290 conf->iospace = 1; 291 if (CIS_IO_RANGE & *p) 292 conf->io_blks = CIS_IO_BLKS(p[1]) + 1; 293 conf->io_addr = CIS_IO_ADDR(*p); 294 conf->io_bus = (*p >> 5) & 3; 295 if (*p++ & CIS_IO_RANGE) { 296 struct cis_ioblk *io, *last = 0; 297 i = CIS_IO_ADSZ(*p); 298 j = CIS_IO_BLKSZ(*p++); 299 for (x = 0; x < conf->io_blks; x++) { 300 io = xmalloc(sizeof(*io)); 301 if (last) 302 last->next = io; 303 else 304 conf->io = io; 305 last = io; 306 u.l = 0; 307 switch (i) { 308 case 0: 309 break; 310 case 1: 311 u.b[0] = *p++; 312 break; 313 case 2: 314 u.b[0] = *p++; 315 u.b[1] = *p++; 316 break; 317 case 3: 318 u.b[0] = *p++; 319 u.b[1] = *p++; 320 u.b[2] = *p++; 321 u.b[3] = *p++; 322 break; 323 } 324 io->addr = u.l; 325 u.l = 0; 326 switch (j) { 327 case 0: 328 break; 329 case 1: 330 u.b[0] = *p++; 331 u.l++; 332 break; 333 case 2: 334 u.b[0] = *p++; 335 u.b[1] = *p++; 336 u.l++; 337 break; 338 case 3: 339 u.b[0] = *p++; 340 u.b[1] = *p++; 341 u.b[2] = *p++; 342 u.b[3] = *p++; 343 u.l++; 344 break; 345 } 346 io->size = u.l; 347 } 348 } 349 } 350 if (feat & CIS_FEAT_IRQ) { 351 conf->irq = 1; 352 conf->irqlevel = *p & 0xF; 353 conf->irq_flags = *p & 0xF0; 354 if (*p++ & CIS_IRQ_MASK) { 355 conf->irq_mask = (p[1] << 8) | p[0]; 356 p += 2; 357 } 358 } 359 switch (CIS_FEAT_MEMORY(feat)) { 360 case 0: 361 break; 362 case 1: 363 conf->memspace = 1; 364 conf->mem = xmalloc(sizeof(*conf->mem)); 365 conf->mem->length = ((p[1] << 8) | p[0]) << 8; 366 break; 367 case 2: 368 conf->memspace = 1; 369 conf->mem = xmalloc(sizeof(*conf->mem)); 370 conf->mem->length = ((p[1] << 8) | p[0]) << 8; 371 conf->mem->address = ((p[3] << 8) | p[2]) << 8; 372 break; 373 case 3: 374 conf->memspace = 1; 375 x = *p++; 376 conf->memwins = CIS_MEM_WINS(x); 377 for (i = 0; i < conf->memwins; i++) { 378 mem = xmalloc(sizeof(*mem)); 379 if (i == 0) 380 conf->mem = mem; 381 else 382 lastmem->next = mem; 383 lastmem = mem; 384 u.l = 0; 385 for (j = 0; j < CIS_MEM_LENSZ(x); j++) 386 u.b[j] = *p++; 387 mem->length = u.l << 8; 388 u.l = 0; 389 for (j = 0; j < CIS_MEM_ADDRSZ(x); j++) 390 u.b[j] = *p++; 391 mem->address = u.l << 8; 392 if (x & CIS_MEM_HOST) { 393 u.l = 0; 394 for (j = 0; j < CIS_MEM_ADDRSZ(x); j++) 395 u.b[j] = *p++; 396 mem->host_address = u.l << 8; 397 } 398 } 399 break; 400 } 401 if (feat & 0x80) { 402 conf->misc_valid = 1; 403 conf->misc = *p++; 404 } 405} 406 407/* 408 * Read the tuples from the card. 409 * The processing of tuples is as follows: 410 * - Read tuples at attribute memory, offset 0. 411 * - If a CIS_END is the first tuple, look for 412 * a tuple list at common memory offset 0; this list 413 * must start with a LINKTARGET. 414 * - If a long link tuple was encountered, execute the long 415 * link. 416 * - If a no-link tuple was seen, terminate processing. 417 * - If no no-link tuple exists, and no long link tuple 418 * exists while processing the primary tuple list, 419 * then look for a LINKTARGET tuple in common memory. 420 * - If a long link tuple is found in any list, then process 421 * it. Only one link is allowed per list. 422 */ 423static struct tuple_list *tlist; 424 425static struct tuple_list * 426read_tuples(int fd) 427{ 428 struct tuple_list *tl = 0, *last_tl; 429 struct tuple *tp; 430 int flag; 431 off_t offs; 432 433 tlist = 0; 434 last_tl = tlist = read_one_tuplelist(fd, MDF_ATTR, (off_t) 0); 435 436 /* Now start processing the links (if any). */ 437 do { 438 flag = MDF_ATTR; 439 tp = find_tuple_in_list(last_tl, CIS_LONGLINK_A); 440 if (tp == 0) { 441 flag = 0; 442 tp = find_tuple_in_list(last_tl, CIS_LONGLINK_C); 443 } 444 if (tp && tp->length == 4) { 445 offs = tp->data[0] | 446 (tp->data[1] << 8) | 447 (tp->data[2] << 16) | 448 (tp->data[3] << 24); 449#ifdef DEBUG 450 printf("Checking long link at %ld (%s memory)\n", 451 offs, flag ? "Attribute" : "Common"); 452#endif 453 /* If a link was found, read the tuple list from it. */ 454 if (ck_linktarget(fd, offs, flag)) { 455 tl = read_one_tuplelist(fd, flag, offs); 456 last_tl->next = tl; 457 last_tl = tl; 458 } 459 } else 460 tl = 0; 461 } while (tl); 462 463 /* 464 * If the primary list had no NOLINK tuple, and no LINKTARGET, 465 * then try to read a tuple list at common memory (offset 0). 466 */ 467 if (find_tuple_in_list(tlist, CIS_NOLINK) == 0 && tlist->next == 0 && 468 ck_linktarget(fd, (off_t) 0, 0)) { 469#ifdef DEBUG 470 printf("Reading long link at %ld (%s memory)\n", 471 offs, flag ? "Attribute" : "Common"); 472#endif 473 tlist->next = read_one_tuplelist(fd, 0, (off_t) 0); 474 } 475 return (tlist); 476} 477 478/* 479 * Read one tuple list from the card. 480 */ 481static struct tuple_list * 482read_one_tuplelist(int fd, int flags, off_t offs) 483{ 484 struct tuple *tp, *last_tp = 0; 485 struct tuple_list *tl; 486 struct tuple_info *tinfo; 487 int total = 0; 488 unsigned char code, length; 489 490 /* Check to see if this memory has already been scanned. */ 491 for (tl = tlist; tl; tl = tl->next) 492 if (tl->offs == offs && tl->flags == (flags & MDF_ATTR)) 493 return (0); 494 tl = xmalloc(sizeof(*tl)); 495 tl->offs = offs; 496 tl->flags = flags & MDF_ATTR; 497 ioctl(fd, PIOCRWFLAG, &flags); 498 lseek(fd, offs, SEEK_SET); 499 do { 500 if (read_attr(fd, &code, 1) != 1) { 501 warn("CIS code read"); 502 break; 503 } 504 total++; 505 if (code == CIS_NULL) 506 continue; 507 tp = xmalloc(sizeof(*tp)); 508 tp->code = code; 509 if (read_attr(fd, &length, 1) != 1) { 510 warn("CIS len read"); 511 break; 512 } 513 total++; 514 tp->length = length; 515#ifdef DEBUG 516 printf("Tuple code = 0x%x, len = %d\n", code, length); 517#endif 518 if (length == 0xFF) { 519 length = tp->length = 0; 520 code = CIS_END; 521 } 522 if (length != 0) { 523 total += length; 524 tp->data = xmalloc(length); 525 if (read_attr(fd, tp->data, length) != length) { 526 warn("CIS read"); 527 break; 528 } 529 } 530 531 /* 532 * Check the tuple, and ignore it if it isn't in the table 533 * or the length is illegal. 534 */ 535 tinfo = get_tuple_info(code); 536 if (tinfo == 0 || (tinfo->length != 255 && tinfo->length != length)) { 537 printf("code %s ignored\n", tuple_name(code)); 538 tp->code = CIS_NULL; 539 } 540 if (tl->tuples == 0) 541 tl->tuples = tp; 542 else 543 last_tp->next = tp; 544 last_tp = tp; 545 } while (code != CIS_END && total < 1024); 546 return (tl); 547} 548 549/* 550 * return true if the offset points to a LINKTARGET tuple. 551 */ 552static int 553ck_linktarget(int fd, off_t offs, int flag) 554{ 555 char blk[5]; 556 557 ioctl(fd, PIOCRWFLAG, &flag); 558 lseek(fd, offs, SEEK_SET); 559 if (read_attr(fd, blk, 5) != 5) 560 return (0); 561 if (blk[0] == 0x13 && 562 blk[1] == 0x3 && 563 blk[2] == 'C' && 564 blk[3] == 'I' && 565 blk[4] == 'S') 566 return (1); 567 return (0); 568} 569 570/* 571 * find_tuple_in_list - find a tuple within a 572 * single tuple list. 573 */ 574static struct tuple * 575find_tuple_in_list(struct tuple_list *tl, unsigned char code) 576{ 577 struct tuple *tp; 578 579 for (tp = tl->tuples; tp; tp = tp->next) 580 if (tp->code == code) 581 break; 582 return (tp); 583} 584 585static int 586read_attr(int fd, char *bp, int len) 587{ 588 char blk[1024], *p = blk; 589 int i, l; 590 591 if (len > sizeof(blk) / 2) 592 len = sizeof(blk) / 2; 593 l = i = read(fd, blk, len * 2); 594 if (i <= 0) { 595 printf("Read return %d bytes (expected %d)\n", i, len * 2); 596 return (i); 597 } 598 while (i > 0) { 599 *bp++ = *p++; 600 p++; 601 i -= 2; 602 } 603 return (l / 2); 604} 605 606/* 607 * return table entry for code. 608 */ 609static struct tuple_info * 610get_tuple_info(unsigned char code) 611{ 612 struct tuple_info *tp; 613 614 for (tp = tuple_info; tp->name; tp++) 615 if (tp->code == code) 616 return (tp); 617 printf("Code %d not found\n", code); 618 return (0); 619} 620 621char * 622tuple_name(unsigned char code) 623{ 624 struct tuple_info *tp; 625 626 tp = get_tuple_info(code); 627 if (tp) 628 return (tp->name); 629 return ("Unknown"); 630} 631