Sebastian Rasmussen | 7e00a5a | 2016-02-23 13:37:28 +0800 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) ST-Ericsson SA 2010-2011 |
| 3 | * Author: Sebastian Rasmussen <sebastian.rasmussen@stericsson.com> |
| 4 | * |
| 5 | * All rights reserved. |
| 6 | * |
| 7 | * Redistribution and use in source and binary forms, with or without |
| 8 | * modification, are permitted provided that the following conditions |
| 9 | * are met: |
| 10 | * |
| 11 | * 1. Redistributions of source code must retain the above copyright |
| 12 | * notice, this list of conditions and the following disclaimer. |
| 13 | * 2. Redistributions in binary form must reproduce the above |
| 14 | * copyright notice, this list of conditions and the following |
| 15 | * disclaimer in the documentation and/or other materials |
| 16 | * provided with the distribution. |
| 17 | * 3. Neither the name of the ST-Ericsson SA nor the names of its |
| 18 | * contributors may be used to endorse or promote products |
| 19 | * derived from this software without specific prior written |
| 20 | * permission. |
| 21 | * |
| 22 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 23 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 24 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
| 25 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
| 26 | * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, |
| 27 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| 28 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
| 29 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| 30 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
| 31 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| 32 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED |
| 33 | * OF THE POSSIBILITY OF SUCH DAMAGE. |
| 34 | */ |
| 35 | |
| 36 | #include <assert.h> |
| 37 | #include <ctype.h> |
| 38 | #include <dirent.h> |
| 39 | #include <errno.h> |
| 40 | #include <limits.h> |
| 41 | #include <stdarg.h> |
| 42 | #include <stdbool.h> |
| 43 | #include <stdint.h> |
| 44 | #include <stdio.h> |
| 45 | #include <stdlib.h> |
| 46 | #include <string.h> |
| 47 | #include <unistd.h> |
| 48 | |
| 49 | #include "mmc.h" |
| 50 | |
| 51 | #define MASKTOBIT0(high) \ |
| 52 | ((high >= 0) ? ((1ull << ((high) + 1ull)) - 1ull) : 0ull) |
| 53 | #define MASK(high, low) (MASKTOBIT0(high) & ~MASKTOBIT0(low - 1)) |
| 54 | #define BITS(value, high, low) (((value) & MASK((high), (low))) >> (low)) |
| 55 | #define IDS_MAX 256 |
| 56 | |
| 57 | struct config { |
| 58 | char *idsfile; |
| 59 | char *dir; |
| 60 | bool verbose; |
| 61 | int interfaces; |
| 62 | char **interface; |
| 63 | char **mmc_ids; |
| 64 | char **sd_ids; |
| 65 | |
| 66 | char *type; |
| 67 | char *cid; |
| 68 | char *csd; |
| 69 | char *scr; |
| 70 | char *ext_csd; |
| 71 | }; |
| 72 | |
| 73 | enum REG_TYPE { |
| 74 | CID = 0, |
| 75 | CSD, |
| 76 | SCR, |
| 77 | EXT_CSD, |
| 78 | }; |
| 79 | |
| 80 | struct ids_database { |
| 81 | char *type; |
| 82 | int id; |
| 83 | char *manufacturer; |
| 84 | }; |
| 85 | |
| 86 | struct ids_database database[] = { |
| 87 | { |
| 88 | .type = "sd", |
| 89 | .id = 0x01, |
| 90 | .manufacturer = "Panasonic", |
| 91 | }, |
| 92 | { |
| 93 | .type = "sd", |
| 94 | .id = 0x02, |
| 95 | .manufacturer = "Toshiba/Kingston/Viking", |
| 96 | }, |
| 97 | { |
| 98 | .type = "sd", |
| 99 | .id = 0x03, |
| 100 | .manufacturer = "SanDisk", |
| 101 | }, |
| 102 | { |
| 103 | .type = "sd", |
| 104 | .id = 0x08, |
| 105 | .manufacturer = "Silicon Power", |
| 106 | }, |
| 107 | { |
| 108 | .type = "sd", |
| 109 | .id = 0x18, |
| 110 | .manufacturer = "Infineon", |
| 111 | }, |
| 112 | { |
| 113 | .type = "sd", |
| 114 | .id = 0x1b, |
| 115 | .manufacturer = "Transcend", |
| 116 | }, |
| 117 | { |
| 118 | .type = "sd", |
| 119 | .id = 0x1c, |
| 120 | .manufacturer = "Transcend", |
| 121 | }, |
| 122 | { |
| 123 | .type = "sd", |
| 124 | .id = 0x1d, |
| 125 | .manufacturer = "Corsair", |
| 126 | }, |
| 127 | { |
| 128 | .type = "sd", |
| 129 | .id = 0x1e, |
| 130 | .manufacturer = "Transcend", |
| 131 | }, |
| 132 | { |
| 133 | .type = "sd", |
| 134 | .id = 0x1f, |
| 135 | .manufacturer = "Kingston", |
| 136 | }, |
| 137 | { |
| 138 | .type = "sd", |
| 139 | .id = 0x28, |
| 140 | .manufacturer = "Lexar", |
| 141 | }, |
| 142 | { |
| 143 | .type = "sd", |
| 144 | .id = 0x30, |
| 145 | .manufacturer = "SanDisk", |
| 146 | }, |
| 147 | { |
| 148 | .type = "sd", |
| 149 | .id = 0x33, |
| 150 | .manufacturer = "STMicroelectronics", |
| 151 | }, |
| 152 | { |
| 153 | .type = "sd", |
| 154 | .id = 0x41, |
| 155 | .manufacturer = "Kingston", |
| 156 | }, |
| 157 | { |
| 158 | .type = "sd", |
| 159 | .id = 0x6f, |
| 160 | .manufacturer = "STMicroelectronics", |
| 161 | }, |
| 162 | { |
| 163 | .type = "sd", |
| 164 | .id = 0x89, |
| 165 | .manufacturer = "Unknown", |
| 166 | }, |
| 167 | { |
| 168 | .type = "mmc", |
| 169 | .id = 0x00, |
| 170 | .manufacturer = "SanDisk", |
| 171 | }, |
| 172 | { |
| 173 | .type = "mmc", |
| 174 | .id = 0x02, |
| 175 | .manufacturer = "Kingston/SanDisk", |
| 176 | }, |
| 177 | { |
| 178 | .type = "mmc", |
| 179 | .id = 0x03, |
| 180 | .manufacturer = "Toshiba", |
| 181 | }, |
| 182 | { |
| 183 | .type = "mmc", |
| 184 | .id = 0x05, |
| 185 | .manufacturer = "Unknown", |
| 186 | }, |
| 187 | { |
| 188 | .type = "mmc", |
| 189 | .id = 0x06, |
| 190 | .manufacturer = "Unknown", |
| 191 | }, |
| 192 | { |
| 193 | .type = "mmc", |
| 194 | .id = 0x11, |
| 195 | .manufacturer = "Toshiba", |
| 196 | }, |
| 197 | { |
| 198 | .type = "mmc", |
| 199 | .id = 0x15, |
| 200 | .manufacturer = "Samsung/SanDisk/LG", |
| 201 | }, |
| 202 | { |
| 203 | .type = "mmc", |
| 204 | .id = 0x37, |
| 205 | .manufacturer = "KingMax", |
| 206 | }, |
| 207 | { |
| 208 | .type = "mmc", |
| 209 | .id = 0x44, |
| 210 | .manufacturer = "SanDisk", |
| 211 | }, |
| 212 | { |
| 213 | .type = "mmc", |
| 214 | .id = 0x2c, |
| 215 | .manufacturer = "Kingston", |
| 216 | }, |
| 217 | { |
| 218 | .type = "mmc", |
| 219 | .id = 0x70, |
| 220 | .manufacturer = "Kingston", |
| 221 | }, |
| 222 | }; |
| 223 | |
| 224 | /* Command line parsing functions */ |
| 225 | void usage(void) |
| 226 | { |
| 227 | printf("Usage: print mmc [-h] [-v] <device path ...>\n"); |
| 228 | printf("\n"); |
| 229 | printf("Options:\n"); |
| 230 | printf("\t-h\tShow this help.\n"); |
| 231 | printf("\t-v\tEnable verbose mode.\n"); |
| 232 | } |
| 233 | |
| 234 | int parse_opts(int argc, char **argv, struct config *config) |
| 235 | { |
| 236 | int c; |
| 237 | |
| 238 | while ((c = getopt(argc, argv, "hv")) != -1) { |
| 239 | switch (c) { |
| 240 | case 'h': |
| 241 | usage(); |
| 242 | return -1; |
| 243 | case 'v': |
| 244 | config->verbose = true; |
| 245 | break; |
| 246 | case '?': |
| 247 | fprintf(stderr, |
| 248 | "Unknown option '%c' encountered.\n\n", c); |
| 249 | usage(); |
| 250 | return -1; |
| 251 | case ':': |
| 252 | fprintf(stderr, |
| 253 | "Argument for option '%c' missing.\n\n", c); |
| 254 | usage(); |
| 255 | return -1; |
| 256 | default: |
| 257 | fprintf(stderr, |
| 258 | "Unimplemented option '%c' encountered.\n", c); |
| 259 | break; |
| 260 | } |
| 261 | } |
| 262 | |
| 263 | if (optind >= argc) { |
| 264 | fprintf(stderr, "Expected mmc directory arguments.\n\n"); |
| 265 | usage(); |
| 266 | return -1; |
| 267 | } |
| 268 | |
| 269 | config->dir = strdup(argv[optind]); |
| 270 | return 0; |
| 271 | } |
| 272 | |
| 273 | int parse_ids(struct config *config) |
| 274 | { |
| 275 | unsigned int ids_cnt = sizeof(database) / sizeof(struct ids_database); |
| 276 | unsigned int value; |
| 277 | char **ids; |
| 278 | char *type; |
| 279 | int i; |
| 280 | |
| 281 | for (i = 0; i < ids_cnt; i++) { |
| 282 | type = database[i].type; |
| 283 | |
| 284 | if (!strcmp(type, "mmc")) { |
| 285 | ids = config->mmc_ids; |
| 286 | } else if (!strcmp(type, "sd")) { |
| 287 | ids = config->sd_ids; |
| 288 | } else { |
| 289 | fprintf(stderr, |
| 290 | "MMC/SD id parse error, unknown type: '%s'.\n", |
| 291 | type); |
| 292 | return -1; |
| 293 | } |
| 294 | |
| 295 | value = database[i].id; |
| 296 | |
| 297 | if (value >= IDS_MAX) { |
| 298 | fprintf(stderr, |
| 299 | "MMC/SD id parse error, id out of range.\n"); |
| 300 | return -1; |
| 301 | } |
| 302 | |
| 303 | if (ids[value]) { |
| 304 | fprintf(stderr, |
| 305 | "Duplicate entries: type='%s', id='0x%1x'.\n", |
| 306 | type, value); |
| 307 | return -1; |
| 308 | } |
| 309 | |
| 310 | ids[value] = database[i].manufacturer; |
| 311 | } |
| 312 | |
| 313 | return 0; |
| 314 | } |
| 315 | |
| 316 | /* MMC/SD file parsing functions */ |
| 317 | char *read_file(char *name) |
| 318 | { |
| 319 | char *preparsed; |
| 320 | char line[4096]; |
| 321 | FILE *f; |
| 322 | |
| 323 | f = fopen(name, "r"); |
| 324 | if (!f) { |
| 325 | fprintf(stderr, "Could not open MMC/SD file '%s'.\n", name); |
| 326 | return NULL; |
| 327 | } |
| 328 | |
| 329 | preparsed = fgets(line, sizeof(line), f); |
| 330 | if (!preparsed) { |
| 331 | if (ferror(f)) |
| 332 | fprintf(stderr, "Could not read MMC/SD file '%s'.\n", |
| 333 | name); |
| 334 | else |
| 335 | fprintf(stderr, |
| 336 | "Could not read data from MMC/SD file '%s'.\n", |
| 337 | name); |
| 338 | |
| 339 | if (fclose(f)) |
| 340 | fprintf(stderr, "Could not close MMC/SD file '%s'.\n", |
| 341 | name); |
| 342 | return NULL; |
| 343 | } |
| 344 | |
| 345 | if (fclose(f)) { |
| 346 | fprintf(stderr, "Could not close MMC/SD file '%s'.\n", name); |
| 347 | return NULL; |
| 348 | } |
| 349 | |
| 350 | line[sizeof(line) - 1] = '\0'; |
| 351 | |
| 352 | while (isspace(line[strlen(line) - 1])) |
| 353 | line[strlen(line) - 1] = '\0'; |
| 354 | |
| 355 | while (isspace(line[0])) |
| 356 | strncpy(&line[0], &line[1], sizeof(line)); |
| 357 | |
| 358 | return strdup(line); |
| 359 | } |
| 360 | |
| 361 | /* Hexadecimal string parsing functions */ |
| 362 | char *to_binstr(char *hexstr) |
| 363 | { |
| 364 | char *bindigits[] = { |
| 365 | "0000", "0001", "0010", "0011", "0100", "0101", "0110", "0111", |
| 366 | "1000", "1001", "1010", "1011", "1100", "1101", "1110", "1111", |
| 367 | }; |
| 368 | char *binstr; |
| 369 | |
| 370 | binstr = calloc(strlen(hexstr) * 4 + 1, sizeof(char)); |
| 371 | |
| 372 | while (hexstr && *hexstr != '\0') { |
| 373 | if (!isxdigit(*hexstr)) |
| 374 | return NULL; |
| 375 | |
| 376 | if (isdigit(*hexstr)) |
| 377 | strcat(binstr, bindigits[*hexstr - '0']); |
| 378 | else if (islower(*hexstr)) |
| 379 | strcat(binstr, bindigits[*hexstr - 'a']); |
| 380 | else |
| 381 | strcat(binstr, bindigits[*hexstr - 'A']); |
| 382 | |
| 383 | hexstr++; |
| 384 | } |
| 385 | |
| 386 | return binstr; |
| 387 | } |
| 388 | |
| 389 | void bin_to_unsigned(unsigned int *u, char *binstr, int width) |
| 390 | { |
| 391 | *u = 0; |
| 392 | assert(width <= 32); |
| 393 | |
| 394 | while (binstr && *binstr != '\0' && width > 0) { |
| 395 | *u <<= 1; |
| 396 | *u |= *binstr == '0' ? 0 : 1; |
| 397 | |
| 398 | binstr++; |
| 399 | width--; |
| 400 | } |
| 401 | } |
| 402 | |
| 403 | void bin_to_ascii(char *a, char *binstr, int width) |
| 404 | { |
| 405 | assert(width % 8 == 0); |
| 406 | *a = '\0'; |
| 407 | |
| 408 | while (binstr && *binstr != '\0' && width > 0) { |
| 409 | unsigned int u; |
| 410 | char c[2] = { '\0', '\0' }; |
| 411 | char *s = &c[0]; |
| 412 | |
| 413 | bin_to_unsigned(&u, binstr, 8); |
| 414 | c[0] = u; |
| 415 | |
| 416 | strcat(a, s); |
| 417 | binstr += 8; |
| 418 | width -= 8; |
| 419 | } |
| 420 | } |
| 421 | |
| 422 | void parse_bin(char *hexstr, char *fmt, ...) |
| 423 | { |
| 424 | va_list args; |
| 425 | char *origstr; |
| 426 | char *binstr; |
| 427 | unsigned long width = 0; |
| 428 | |
| 429 | binstr = to_binstr(hexstr); |
| 430 | origstr = binstr; |
| 431 | |
| 432 | va_start(args, fmt); |
| 433 | |
| 434 | while (binstr && fmt && *fmt != '\0') { |
| 435 | if (isdigit(*fmt)) { |
| 436 | char *rest; |
| 437 | |
| 438 | errno = 0; |
| 439 | width = strtoul(fmt, &rest, 10); |
| 440 | if (width == ULONG_MAX && errno != 0) |
| 441 | fprintf(stderr, "strtoul()"); |
| 442 | fmt = rest; |
| 443 | } else if (*fmt == 'u') { |
| 444 | unsigned int *u = va_arg(args, unsigned int *); |
| 445 | |
| 446 | if (u) |
| 447 | bin_to_unsigned(u, binstr, width); |
| 448 | binstr += width; |
| 449 | width = 0; |
| 450 | fmt++; |
| 451 | } else if (*fmt == 'r') { |
| 452 | binstr += width; |
| 453 | width = 0; |
| 454 | fmt++; |
| 455 | } else if (*fmt == 'a') { |
| 456 | char *c = va_arg(args, char *); |
| 457 | |
| 458 | if (c) |
| 459 | bin_to_ascii(c, binstr, width); |
| 460 | binstr += width; |
| 461 | width = 0; |
| 462 | fmt++; |
| 463 | } else { |
| 464 | fmt++; |
| 465 | } |
| 466 | } |
| 467 | |
| 468 | va_end(args); |
| 469 | free(origstr); |
| 470 | } |
| 471 | |
| 472 | /* MMC/SD information parsing functions */ |
| 473 | void print_sd_cid(struct config *config, char *cid) |
| 474 | { |
| 475 | static const char *months[] = { |
| 476 | "jan", "feb", "mar", "apr", "may", "jun", |
| 477 | "jul", "aug", "sep", "oct", "nov", "dec", |
| 478 | "invalid0", "invalid1", "invalid2", "invalid3", |
| 479 | }; |
| 480 | unsigned int mid; |
| 481 | char oid[3]; |
| 482 | char pnm[6]; |
| 483 | unsigned int prv_major; |
| 484 | unsigned int prv_minor; |
| 485 | unsigned int psn; |
| 486 | unsigned int mdt_month; |
| 487 | unsigned int mdt_year; |
| 488 | unsigned int crc; |
| 489 | |
| 490 | parse_bin(cid, "8u16a40a4u4u32u4r8u4u7u1r", |
| 491 | &mid, &oid[0], &pnm[0], &prv_major, &prv_minor, &psn, |
| 492 | &mdt_year, &mdt_month, &crc); |
| 493 | |
| 494 | oid[2] = '\0'; |
| 495 | pnm[5] = '\0'; |
| 496 | |
| 497 | if (config->verbose) { |
| 498 | printf("======SD/CID======\n"); |
| 499 | |
| 500 | printf("\tMID: 0x%02x (", mid); |
| 501 | if (config->sd_ids[mid]) |
| 502 | printf("%s)\n", config->sd_ids[mid]); |
| 503 | else |
| 504 | printf("Unlisted)\n"); |
| 505 | |
| 506 | printf("\tOID: %s\n", oid); |
| 507 | printf("\tPNM: %s\n", pnm); |
| 508 | printf("\tPRV: 0x%01x%01x ", prv_major, prv_minor); |
| 509 | printf("(%d.%d)\n", prv_major, prv_minor); |
| 510 | printf("\tPSN: 0x%08x\n", psn); |
| 511 | printf("\tMDT: 0x%02x%01x %d %s\n", mdt_year, mdt_month, |
| 512 | 2000 + mdt_year, months[mdt_month]); |
| 513 | printf("\tCRC: 0x%02x\n", crc); |
| 514 | } else { |
| 515 | if (config->sd_ids[mid]) |
| 516 | printf("manufacturer: '%s' '%s'\n", |
| 517 | config->sd_ids[mid], oid); |
| 518 | else |
| 519 | printf("manufacturer: 'Unlisted' '%s'\n", oid); |
| 520 | |
| 521 | printf("product: '%s' %d.%d\n", pnm, prv_major, prv_minor); |
| 522 | printf("serial: 0x%08x\n", psn); |
| 523 | printf("manfacturing date: %d %s\n", 2000 + mdt_year, |
| 524 | months[mdt_month]); |
| 525 | } |
| 526 | } |
| 527 | |
| 528 | void print_mmc_cid(struct config *config, char *cid) |
| 529 | { |
| 530 | static const char *months[] = { |
| 531 | "jan", "feb", "mar", "apr", "may", "jun", |
| 532 | "jul", "aug", "sep", "oct", "nov", "dec", |
| 533 | "invalid0", "invalid1", "invalid2", "invalid3", |
| 534 | }; |
| 535 | unsigned int mid; |
| 536 | unsigned int cbx; |
| 537 | unsigned int oid; |
| 538 | char pnm[7]; |
| 539 | unsigned int prv_major; |
| 540 | unsigned int prv_minor; |
| 541 | unsigned int psn; |
| 542 | unsigned int mdt_month; |
| 543 | unsigned int mdt_year; |
| 544 | unsigned int crc; |
| 545 | |
| 546 | parse_bin(cid, "8u6r2u8u48a4u4u32u4u4u7u1r", |
| 547 | &mid, &cbx, &oid, &pnm[0], &psn, &prv_major, &prv_minor, |
| 548 | &mdt_year, &mdt_month, &crc); |
| 549 | |
| 550 | pnm[6] = '\0'; |
| 551 | |
| 552 | if (config->verbose) { |
| 553 | printf("======MMC/CID======\n"); |
| 554 | |
| 555 | printf("\tMID: 0x%02x (", mid); |
| 556 | if (config->mmc_ids[mid]) |
| 557 | printf("%s)\n", config->mmc_ids[mid]); |
| 558 | else |
| 559 | printf("Unlisted)\n"); |
| 560 | |
| 561 | printf("\tCBX: 0x%01x (", cbx); |
| 562 | switch (cbx) { |
| 563 | case 0: |
| 564 | printf("card)\n"); |
| 565 | break; |
| 566 | case 1: |
| 567 | printf("BGA)\n"); |
| 568 | break; |
| 569 | case 2: |
| 570 | printf("PoP)\n"); |
| 571 | break; |
| 572 | case 3: |
| 573 | printf("reserved)\n"); |
| 574 | break; |
| 575 | } |
| 576 | |
| 577 | printf("\tOID: 0x%01x\n", oid); |
| 578 | printf("\tPNM: %s\n", pnm); |
| 579 | printf("\tPRV: 0x%01x%01x ", prv_major, prv_minor); |
| 580 | printf("(%d.%d)\n", prv_major, prv_minor); |
| 581 | printf("\tPSN: 0x%08x\n", psn); |
| 582 | printf("\tMDT: 0x%01x%01x %d %s\n", mdt_month, mdt_year, |
| 583 | 1997 + mdt_year, months[mdt_month]); |
| 584 | printf("\tCRC: 0x%02x\n", crc); |
| 585 | } else { |
| 586 | if (config->mmc_ids[mid]) |
| 587 | printf("manufacturer: '%s' '%c'\n", |
| 588 | config->mmc_ids[mid], oid); |
| 589 | else |
| 590 | printf("manufacturer: 'Unlisted' '%c'\n", oid); |
| 591 | |
| 592 | printf("product: '%s' %d.%d\n", pnm, prv_major, prv_minor); |
| 593 | printf("serial: 0x%08x\n", psn); |
| 594 | printf("manfacturing date: %d %s\n", 1997 + mdt_year, |
| 595 | months[mdt_month]); |
| 596 | } |
| 597 | } |
| 598 | |
| 599 | void print_sd_csd(struct config *config, char *csd) |
| 600 | { |
| 601 | unsigned int csd_structure; |
| 602 | unsigned int taac_timevalue; |
| 603 | unsigned int taac_timeunit; |
| 604 | unsigned int nsac; |
| 605 | unsigned int tran_speed_timevalue; |
| 606 | unsigned int tran_speed_transferrateunit; |
| 607 | unsigned int ccc; |
| 608 | unsigned int read_bl_len; |
| 609 | unsigned int read_bl_partial; |
| 610 | unsigned int write_blk_misalign; |
| 611 | unsigned int read_blk_misalign; |
| 612 | unsigned int dsr_imp; |
| 613 | unsigned int c_size; |
| 614 | unsigned int vdd_r_curr_min; |
| 615 | unsigned int vdd_r_curr_max; |
| 616 | unsigned int vdd_w_curr_min; |
| 617 | unsigned int vdd_w_curr_max; |
| 618 | unsigned int c_size_mult; |
| 619 | unsigned int erase_blk_en; |
| 620 | unsigned int sector_size; |
| 621 | unsigned int wp_grp_size; |
| 622 | unsigned int wp_grp_enable; |
| 623 | unsigned int r2w_factor; |
| 624 | unsigned int write_bl_len; |
| 625 | unsigned int write_bl_partial; |
| 626 | unsigned int file_format_grp; |
| 627 | unsigned int copy; |
| 628 | unsigned int perm_write_protect; |
| 629 | unsigned int tmp_write_protect; |
| 630 | unsigned int file_format; |
| 631 | unsigned int crc; |
| 632 | unsigned int taac; |
| 633 | unsigned int tran_speed; |
| 634 | |
| 635 | parse_bin(csd, "2u", &csd_structure); |
| 636 | |
| 637 | if (csd_structure == 0) { |
| 638 | parse_bin(csd, "2u6r1r4u3u8u1r4u3u12u4u1u1u1u1u2r12u3u3u3u3u3u" |
| 639 | "1u7u7u1u2r3u4u1u5r1u1u1u1u2u2r7u1r", |
| 640 | NULL, &taac_timevalue, &taac_timeunit, &nsac, |
| 641 | &tran_speed_timevalue, |
| 642 | &tran_speed_transferrateunit, &ccc, |
| 643 | &read_bl_len, &read_bl_partial, |
| 644 | &write_blk_misalign, &read_blk_misalign, |
| 645 | &dsr_imp, &c_size, &vdd_r_curr_min, |
| 646 | &vdd_r_curr_max, &vdd_w_curr_min, |
| 647 | &vdd_w_curr_max, &c_size_mult, &erase_blk_en, |
| 648 | §or_size, &wp_grp_size, &wp_grp_enable, |
| 649 | &r2w_factor, &write_bl_len, &write_bl_partial, |
| 650 | &file_format_grp, ©, &perm_write_protect, |
| 651 | &tmp_write_protect, &file_format, &crc); |
| 652 | } else if (csd_structure == 1) { |
| 653 | parse_bin(csd, "2u6r1r4u3u8u1r4u3u12u4u1u1u1u1u6r22u1r1u7u7u1u" |
| 654 | "2r3u4u1u5r1u1u1u1u2u2r7u1r", |
| 655 | NULL, &taac_timevalue, &taac_timeunit, &nsac, |
| 656 | &tran_speed_timevalue, |
| 657 | &tran_speed_transferrateunit, &ccc, |
| 658 | &read_bl_len, &read_bl_partial, |
| 659 | &write_blk_misalign, &read_blk_misalign, |
| 660 | &dsr_imp, &c_size, &erase_blk_en, §or_size, |
| 661 | &wp_grp_size, &wp_grp_enable, &r2w_factor, |
| 662 | &write_bl_len, &write_bl_partial, |
| 663 | &file_format_grp, ©, &perm_write_protect, |
| 664 | &tmp_write_protect, &file_format, &crc); |
| 665 | |
| 666 | vdd_r_curr_min = 0; |
| 667 | c_size_mult = 0; |
| 668 | } else { |
| 669 | printf("Unknown CSD structure: 0x%1x\n", csd_structure); |
| 670 | return; |
| 671 | } |
| 672 | |
| 673 | taac = taac_timevalue << 3 | taac_timeunit; |
| 674 | tran_speed = tran_speed_timevalue << 3 | tran_speed_transferrateunit; |
| 675 | |
| 676 | if (config->verbose) { |
| 677 | float value; |
| 678 | unsigned long long blocks = 0; |
| 679 | int block_size = 0; |
| 680 | unsigned long long memory_capacity; |
| 681 | |
| 682 | printf("======SD/CSD======\n"); |
| 683 | |
| 684 | printf("\tCSD_STRUCTURE: %d\n", csd_structure); |
| 685 | printf("\tTAAC: 0x%02x (", taac); |
| 686 | |
| 687 | switch (taac_timevalue) { |
| 688 | case 0x0: |
| 689 | value = 0.0f; |
| 690 | break; |
| 691 | case 0x1: |
| 692 | value = 1.0f; |
| 693 | break; |
| 694 | case 0x2: |
| 695 | value = 1.2f; |
| 696 | break; |
| 697 | case 0x3: |
| 698 | value = 1.3f; |
| 699 | break; |
| 700 | case 0x4: |
| 701 | value = 1.5f; |
| 702 | break; |
| 703 | case 0x5: |
| 704 | value = 2.0f; |
| 705 | break; |
| 706 | case 0x6: |
| 707 | value = 2.5f; |
| 708 | break; |
| 709 | case 0x7: |
| 710 | value = 3.0f; |
| 711 | break; |
| 712 | case 0x8: |
| 713 | value = 3.5f; |
| 714 | break; |
| 715 | case 0x9: |
| 716 | value = 4.0f; |
| 717 | break; |
| 718 | case 0xa: |
| 719 | value = 4.5f; |
| 720 | break; |
| 721 | case 0xb: |
| 722 | value = 5.0f; |
| 723 | break; |
| 724 | case 0xc: |
| 725 | value = 5.5f; |
| 726 | break; |
| 727 | case 0xd: |
| 728 | value = 6.0f; |
| 729 | break; |
| 730 | case 0xe: |
| 731 | value = 7.0f; |
| 732 | break; |
| 733 | case 0xf: |
| 734 | value = 8.0f; |
| 735 | break; |
| 736 | default: |
| 737 | value = 0.0f; |
| 738 | break; |
| 739 | } |
| 740 | |
| 741 | switch (taac_timeunit) { |
| 742 | case 0x0: |
| 743 | printf("%.2fns)\n", value * 1.0f); |
| 744 | break; |
| 745 | case 0x1: |
| 746 | printf("%.2fns)\n", value * 10.0f); |
| 747 | break; |
| 748 | case 0x2: |
| 749 | printf("%.2fns)\n", value * 100.0f); |
| 750 | break; |
| 751 | case 0x3: |
| 752 | printf("%.2fus)\n", value * 1.0f); |
| 753 | break; |
| 754 | case 0x4: |
| 755 | printf("%.2fus)\n", value * 10.0f); |
| 756 | break; |
| 757 | case 0x5: |
| 758 | printf("%.2fus)\n", value * 100.0f); |
| 759 | break; |
| 760 | case 0x6: |
| 761 | printf("%.2fms)\n", value * 1.0f); |
| 762 | break; |
| 763 | case 0x7: |
| 764 | printf("%.2fms)\n", value * 10.0f); |
| 765 | break; |
| 766 | } |
| 767 | |
| 768 | if (csd_structure == 1 && taac != 0x0e) |
| 769 | printf("Warn: Invalid TAAC (should be 0x0e)\n"); |
| 770 | |
| 771 | printf("\tNSAC: %d clocks\n", nsac); |
| 772 | if (csd_structure == 1 && nsac != 0x00) |
| 773 | printf("Warn: Invalid NSAC (should be 0x00)\n"); |
| 774 | |
| 775 | printf("\tTRAN_SPEED: 0x%02x (", tran_speed); |
| 776 | switch (tran_speed_timevalue) { |
| 777 | case 0x0: |
| 778 | value = 0.0f; |
| 779 | break; |
| 780 | case 0x1: |
| 781 | value = 1.0f; |
| 782 | break; |
| 783 | case 0x2: |
| 784 | value = 1.2f; |
| 785 | break; |
| 786 | case 0x3: |
| 787 | value = 1.3f; |
| 788 | break; |
| 789 | case 0x4: |
| 790 | value = 1.5f; |
| 791 | break; |
| 792 | case 0x5: |
| 793 | value = 2.0f; |
| 794 | break; |
| 795 | case 0x6: |
| 796 | value = 2.5f; |
| 797 | break; |
| 798 | case 0x7: |
| 799 | value = 3.0f; |
| 800 | break; |
| 801 | case 0x8: |
| 802 | value = 3.5f; |
| 803 | break; |
| 804 | case 0x9: |
| 805 | value = 4.0f; |
| 806 | break; |
| 807 | case 0xa: |
| 808 | value = 4.5f; |
| 809 | break; |
| 810 | case 0xb: |
| 811 | value = 5.0f; |
| 812 | break; |
| 813 | case 0xc: |
| 814 | value = 5.5f; |
| 815 | break; |
| 816 | case 0xd: |
| 817 | value = 6.0f; |
| 818 | break; |
| 819 | case 0xe: |
| 820 | value = 7.0f; |
| 821 | break; |
| 822 | case 0xf: |
| 823 | value = 8.0f; |
| 824 | break; |
| 825 | default: |
| 826 | value = 0.0f; |
| 827 | break; |
| 828 | } |
| 829 | |
| 830 | switch (tran_speed_transferrateunit) { |
| 831 | case 0x0: |
| 832 | printf("%.2fkbit/s)\n", value * 100.0f); |
| 833 | break; |
| 834 | case 0x1: |
| 835 | printf("%.2fMbit/s)\n", value * 1.0f); |
| 836 | break; |
| 837 | case 0x2: |
| 838 | printf("%.2fMbit/s)\n", value * 10.0f); |
| 839 | break; |
| 840 | case 0x3: |
| 841 | printf("%.2fMbit/s)\n", value * 100.0f); |
| 842 | break; |
| 843 | default: |
| 844 | printf("reserved)\n"); |
| 845 | break; |
| 846 | } |
| 847 | if (csd_structure == 0 && |
| 848 | (tran_speed != 0x32 && tran_speed != 0x5a)) |
| 849 | printf("Warn: Invalid TRAN_SPEED " |
| 850 | "(should be 0x32 or 0x5a)\n"); |
| 851 | if (csd_structure == 1 && tran_speed != 0x32 && |
| 852 | tran_speed != 0x5a && tran_speed != 0x0b && |
| 853 | tran_speed != 0x2b) |
| 854 | printf("Warn: Invalid TRAN_SPEED " |
| 855 | "(should be 0x32, 0x5a, 0x0b or 0x2b\n"); |
| 856 | |
| 857 | printf("\tCCC: 0x%03x (class: ", ccc); |
| 858 | if (ccc & 0x800) |
| 859 | printf("11, "); |
| 860 | if (ccc & 0x400) |
| 861 | printf("10, "); |
| 862 | if (ccc & 0x200) |
| 863 | printf("9, "); |
| 864 | if (ccc & 0x100) |
| 865 | printf("8, "); |
| 866 | if (ccc & 0x080) |
| 867 | printf("7, "); |
| 868 | if (ccc & 0x040) |
| 869 | printf("6, "); |
| 870 | if (ccc & 0x020) |
| 871 | printf("5, "); |
| 872 | if (ccc & 0x010) |
| 873 | printf("4, "); |
| 874 | if (ccc & 0x008) |
| 875 | printf("3, "); |
| 876 | if (ccc & 0x004) |
| 877 | printf("2, "); |
| 878 | if (ccc & 0x002) |
| 879 | printf("1, "); |
| 880 | if (ccc & 0x001) |
| 881 | printf("0, "); |
| 882 | printf(" )\n"); |
| 883 | |
| 884 | if (csd_structure == 0 && |
| 885 | (ccc != 0x5b5 && ccc != 0x7b5 && ccc != 0x5f5)) |
| 886 | printf("Warn: Invalid CCC (should be 0x5b5, " |
| 887 | "0x7b5 or 0x5f5)\n"); |
| 888 | else if (csd_structure == 1 && ccc != 0x5b5 && ccc != 0x7b5) |
| 889 | printf("Warn: Invalid CCC (should be 0x5b5 or 0x7b5)\n"); |
| 890 | |
| 891 | printf("\tREAD_BL_LEN: 0x%01x (", read_bl_len); |
| 892 | switch (read_bl_len) { |
| 893 | case 0x9: |
| 894 | printf("512 bytes)\n"); |
| 895 | break; |
| 896 | case 0xa: |
| 897 | printf("1024 bytes)\n"); |
| 898 | break; |
| 899 | case 0xb: |
| 900 | printf("2048 bytes)\n"); |
| 901 | break; |
| 902 | default: |
| 903 | printf("reserved bytes)\n"); |
| 904 | break; |
| 905 | } |
| 906 | |
| 907 | if (csd_structure == 1 && read_bl_len != 0x9) |
| 908 | printf("Warn: Invalid READ_BL_LEN (should be 0x9)\n"); |
| 909 | |
| 910 | printf("\tREAD_BL_PARTIAL: 0x%01x\n", read_bl_partial); |
| 911 | if (csd_structure == 0 && read_bl_partial != 0x01) |
| 912 | printf("Warn: Invalid READ_BL_PARTIAL (should be 0x01)\n"); |
| 913 | else if (csd_structure == 1 && read_bl_partial != 0x00) |
| 914 | printf("Warn: Invalid READ_BL_PARTIAL (should be 0x00)\n"); |
| 915 | |
| 916 | printf("\tWRITE_BLK_MISALIGN: 0x%01x\n", write_blk_misalign); |
| 917 | if (csd_structure == 1 && write_blk_misalign != 0x00) |
| 918 | printf("Warn: Invalid WRITE_BLK_MISALIGN (should be 0x00)\n"); |
| 919 | |
| 920 | printf("\tREAD_BLK_MISALIGN: 0x%01x\n", read_blk_misalign); |
| 921 | if (csd_structure == 1 && read_blk_misalign != 0x00) |
| 922 | printf("Warn: Invalid READ_BLK_MISALIGN (should be 0x00)\n"); |
| 923 | |
| 924 | printf("\tDSR_IMP: 0x%01x\n", dsr_imp); |
| 925 | |
| 926 | if (csd_structure == 0) { |
| 927 | int mult; |
| 928 | int blocknr; |
| 929 | int block_len; |
| 930 | |
| 931 | printf("\tC_SIZE: 0x%03x\n", c_size); |
| 932 | printf("\tVDD_R_CURR_MIN: 0x%01x (", vdd_r_curr_min); |
| 933 | switch (vdd_r_curr_min) { |
| 934 | case 0x0: |
| 935 | printf("0.5mA)\n"); |
| 936 | break; |
| 937 | case 0x1: |
| 938 | printf("1mA)\n"); |
| 939 | break; |
| 940 | case 0x2: |
| 941 | printf("5mA)\n"); |
| 942 | break; |
| 943 | case 0x3: |
| 944 | printf("10mA)\n"); |
| 945 | break; |
| 946 | case 0x4: |
| 947 | printf("25mA)\n"); |
| 948 | break; |
| 949 | case 0x5: |
| 950 | printf("35mA)\n"); |
| 951 | break; |
| 952 | case 0x6: |
| 953 | printf("60mA)\n"); |
| 954 | break; |
| 955 | case 0x7: |
| 956 | printf("100mA)\n"); |
| 957 | break; |
| 958 | } |
| 959 | |
| 960 | printf("\tVDD_R_CURR_MAX: 0x%01x (", vdd_r_curr_max); |
| 961 | switch (vdd_r_curr_max) { |
| 962 | case 0x0: |
| 963 | printf("1mA)\n"); |
| 964 | break; |
| 965 | case 0x1: |
| 966 | printf("5mA)\n"); |
| 967 | break; |
| 968 | case 0x2: |
| 969 | printf("10mA)\n"); |
| 970 | break; |
| 971 | case 0x3: |
| 972 | printf("25mA)\n"); |
| 973 | break; |
| 974 | case 0x4: |
| 975 | printf("35mA)\n"); |
| 976 | break; |
| 977 | case 0x5: |
| 978 | printf("45mA)\n"); |
| 979 | break; |
| 980 | case 0x6: |
| 981 | printf("80mA)\n"); |
| 982 | break; |
| 983 | case 0x7: |
| 984 | printf("200mA)\n"); |
| 985 | break; |
| 986 | } |
| 987 | |
| 988 | printf("\tVDD_W_CURR_MIN: 0x%01x (", vdd_w_curr_min); |
| 989 | switch (vdd_w_curr_min) { |
| 990 | case 0x0: |
| 991 | printf("0.5mA)\n"); |
| 992 | break; |
| 993 | case 0x1: |
| 994 | printf("1mA)\n"); |
| 995 | break; |
| 996 | case 0x2: |
| 997 | printf("5mA)\n"); |
| 998 | break; |
| 999 | case 0x3: |
| 1000 | printf("10mA)\n"); |
| 1001 | break; |
| 1002 | case 0x4: |
| 1003 | printf("25mA)\n"); |
| 1004 | break; |
| 1005 | case 0x5: |
| 1006 | printf("35mA)\n"); |
| 1007 | break; |
| 1008 | case 0x6: |
| 1009 | printf("60mA)\n"); |
| 1010 | break; |
| 1011 | case 0x7: |
| 1012 | printf("100mA)\n"); |
| 1013 | break; |
| 1014 | } |
| 1015 | |
| 1016 | printf("\tVDD_W_CURR_MAX: 0x%01x (", vdd_w_curr_max); |
| 1017 | switch (vdd_w_curr_max) { |
| 1018 | case 0x0: |
| 1019 | printf("1mA)\n"); |
| 1020 | break; |
| 1021 | case 0x1: |
| 1022 | printf("5mA)\n"); |
| 1023 | break; |
| 1024 | case 0x2: |
| 1025 | printf("10mA)\n"); |
| 1026 | break; |
| 1027 | case 0x3: |
| 1028 | printf("25mA)\n"); |
| 1029 | break; |
| 1030 | case 0x4: |
| 1031 | printf("35mA)\n"); |
| 1032 | break; |
| 1033 | case 0x5: |
| 1034 | printf("45mA)\n"); |
| 1035 | break; |
| 1036 | case 0x6: |
| 1037 | printf("80mA)\n"); |
| 1038 | break; |
| 1039 | case 0x7: |
| 1040 | printf("200mA)\n"); |
| 1041 | break; |
| 1042 | } |
| 1043 | |
| 1044 | printf("\tC_SIZE_MULT: 0x%01x\n", c_size_mult); |
| 1045 | |
| 1046 | mult = 1 << (c_size_mult + 2); |
| 1047 | blocknr = (c_size + 1) * mult; |
| 1048 | block_len = 1 << read_bl_len; |
| 1049 | blocks = blocknr; |
| 1050 | block_size = block_len; |
| 1051 | } else if (csd_structure == 1) { |
| 1052 | printf("\tC_SIZE: 0x%06x\n", c_size); |
| 1053 | |
| 1054 | printf("\tERASE_BLK_EN: 0x%01x\n", erase_blk_en); |
| 1055 | if (erase_blk_en != 0x01) |
| 1056 | printf("Warn: Invalid ERASE_BLK_EN (should be 0x01)\n"); |
| 1057 | |
| 1058 | printf("\tSECTOR_SIZE: 0x%02x (Erasable sector: %d blocks)\n", |
| 1059 | sector_size, sector_size + 1); |
| 1060 | if (sector_size != 0x7f) |
| 1061 | printf("Warn: Invalid SECTOR_SIZE (should be 0x7f)\n"); |
| 1062 | |
| 1063 | printf("\tWP_GRP_SIZE: 0x%02x (Write protect group: %d blocks)\n", |
| 1064 | wp_grp_size, wp_grp_size + 1); |
| 1065 | if (wp_grp_size != 0x00) |
| 1066 | printf("Warn: Invalid WP_GRP_SIZE (should be 0x00)\n"); |
| 1067 | |
| 1068 | printf("\tWP_GRP_ENABLE: 0x%01x\n", wp_grp_enable); |
| 1069 | if (wp_grp_enable != 0x00) |
| 1070 | printf("Warn: Invalid WP_GRP_ENABLE (should be 0x00)\n"); |
| 1071 | |
| 1072 | printf("\tR2W_FACTOR: 0x%01x (Write %d times read)\n", |
| 1073 | r2w_factor, r2w_factor); |
| 1074 | if (r2w_factor != 0x02) |
| 1075 | printf("Warn: Invalid R2W_FACTOR (should be 0x02)\n"); |
| 1076 | |
| 1077 | printf("\tWRITE_BL_LEN: 0x%01x (", write_bl_len); |
| 1078 | switch (write_bl_len) { |
| 1079 | case 9: |
| 1080 | printf("512 bytes)\n"); |
| 1081 | break; |
| 1082 | case 10: |
| 1083 | printf("1024 bytes)\n"); |
| 1084 | break; |
| 1085 | case 11: |
| 1086 | printf("2048 bytes)\n"); |
| 1087 | break; |
| 1088 | default: |
| 1089 | printf("reserved)\n"); |
| 1090 | break; |
| 1091 | } |
| 1092 | |
| 1093 | if (write_bl_len != 0x09) |
| 1094 | printf("Warn: Invalid WRITE_BL_LEN (should be 0x09)\n"); |
| 1095 | |
| 1096 | printf("\tWRITE_BL_PARTIAL: 0x%01x\n", write_bl_partial); |
| 1097 | if (write_bl_partial != 0x00) |
| 1098 | printf("Warn: Invalid WRITE_BL_PARTIAL (should be 0x00)\n"); |
| 1099 | |
| 1100 | printf("\tFILE_FORMAT_GRP: 0x%01x\n", file_format_grp); |
| 1101 | if (file_format_grp != 0x00) |
| 1102 | printf("Warn: Invalid FILE_FORMAT_GRP (should be 0x00)\n"); |
| 1103 | |
| 1104 | printf("\tCOPY: 0x%01x\n", copy); |
| 1105 | printf("\tPERM_WRITE_PROTECT: 0x%01x\n", |
| 1106 | perm_write_protect); |
| 1107 | printf("\tTMP_WRITE_PROTECT: 0x%01x\n", |
| 1108 | tmp_write_protect); |
| 1109 | printf("\tFILE_FORMAT: 0x%01x (", |
| 1110 | file_format); |
| 1111 | |
| 1112 | if (file_format_grp == 1) { |
| 1113 | printf("reserved)\n"); |
| 1114 | } else { |
| 1115 | switch (file_format) { |
| 1116 | case 0: |
| 1117 | printf("partition table)\n"); |
| 1118 | break; |
| 1119 | case 1: |
| 1120 | printf("no partition table)\n"); |
| 1121 | break; |
| 1122 | case 2: |
| 1123 | printf("Universal File Format)\n"); |
| 1124 | break; |
| 1125 | case 3: |
| 1126 | printf("Others/unknown)\n"); |
| 1127 | break; |
| 1128 | } |
| 1129 | } |
| 1130 | |
| 1131 | if (file_format != 0x00) |
| 1132 | printf("Warn: Invalid FILE_FORMAT (should be 0x00)\n"); |
| 1133 | |
| 1134 | printf("\tCRC: 0x%01x\n", crc); |
| 1135 | |
| 1136 | memory_capacity = (c_size + 1) * 512ull * 1024ull; |
| 1137 | block_size = 512; |
| 1138 | blocks = memory_capacity / block_size; |
| 1139 | } |
| 1140 | |
| 1141 | memory_capacity = blocks * block_size; |
| 1142 | |
| 1143 | printf("\tCAPACITY: "); |
| 1144 | if (memory_capacity / (1024ull * 1024ull * 1024ull) > 0) |
| 1145 | printf("%.2fGbyte", |
| 1146 | memory_capacity / (1024.0 * 1024.0 * 1024.0)); |
| 1147 | else if (memory_capacity / (1024ull * 1024ull) > 0) |
| 1148 | printf("%.2fMbyte", memory_capacity / (1024.0 * 1024.0)); |
| 1149 | else if (memory_capacity / (1024ull) > 0) |
| 1150 | printf("%.2fKbyte", memory_capacity / (1024.0)); |
| 1151 | else |
| 1152 | printf("%.2fbyte", memory_capacity * 1.0); |
| 1153 | |
| 1154 | printf(" (%lld bytes, %lld sectors, %d bytes each)\n", |
| 1155 | memory_capacity, blocks, block_size); |
| 1156 | } else { |
| 1157 | unsigned long long blocks = 0; |
| 1158 | int block_size = 0; |
| 1159 | unsigned long long memory_capacity; |
| 1160 | |
| 1161 | printf("card classes: "); |
| 1162 | if (ccc & 0x800) |
| 1163 | printf("11 extension, "); |
| 1164 | if (ccc & 0x400) |
| 1165 | printf("10 switch, "); |
| 1166 | if (ccc & 0x200) |
| 1167 | printf("9 I/O mode, "); |
| 1168 | if (ccc & 0x100) |
| 1169 | printf("8 application specific, "); |
| 1170 | if (ccc & 0x080) |
| 1171 | printf("7 lock card, "); |
| 1172 | if (ccc & 0x040) |
| 1173 | printf("6 write protection, "); |
| 1174 | if (ccc & 0x020) |
| 1175 | printf("5 erase, "); |
| 1176 | if (ccc & 0x010) |
| 1177 | printf("4 block write, "); |
| 1178 | if (ccc & 0x008) |
| 1179 | printf("3 reserved, "); |
| 1180 | if (ccc & 0x004) |
| 1181 | printf("2 block read, "); |
| 1182 | if (ccc & 0x002) |
| 1183 | printf("1 reserved, "); |
| 1184 | if (ccc & 0x001) |
| 1185 | printf("0 basic, "); |
| 1186 | printf("\b\b\n"); |
| 1187 | |
| 1188 | if (csd_structure == 0) { |
| 1189 | int mult; |
| 1190 | int blocknr; |
| 1191 | int block_len; |
| 1192 | |
| 1193 | mult = 1 << (c_size_mult + 2); |
| 1194 | blocknr = (c_size + 1) * mult; |
| 1195 | block_len = 1 << read_bl_len; |
| 1196 | blocks = blocknr; |
| 1197 | block_size = block_len; |
| 1198 | } else if (csd_structure == 1) { |
| 1199 | memory_capacity = (c_size + 1) * 512ull * 1024ull; |
| 1200 | block_size = 512; |
| 1201 | blocks = memory_capacity / block_size; |
| 1202 | } |
| 1203 | |
| 1204 | memory_capacity = blocks * block_size; |
| 1205 | |
| 1206 | printf("capacity: "); |
| 1207 | if (memory_capacity / (1024ull * 1024ull * 1024ull) > 0) |
| 1208 | printf("%.2fGbyte", |
| 1209 | memory_capacity / (1024.0 * 1024.0 * 1024.0)); |
| 1210 | else if (memory_capacity / (1024ull * 1024ull) > 0) |
| 1211 | printf("%.2fMbyte", memory_capacity / (1024.0 * 1024.0)); |
| 1212 | else if (memory_capacity / (1024ull) > 0) |
| 1213 | printf("%.2fKbyte", memory_capacity / (1024.0)); |
| 1214 | else |
| 1215 | printf("%.2fbyte", memory_capacity * 1.0); |
| 1216 | |
| 1217 | printf(" (%lld bytes, %lld sectors, %d bytes each)\n", |
| 1218 | memory_capacity, blocks, block_size); |
| 1219 | } |
| 1220 | } |
| 1221 | |
| 1222 | void print_mmc_csd(struct config *config, char *csd) |
| 1223 | { |
| 1224 | unsigned int csd_structure; |
| 1225 | unsigned int spec_vers; |
| 1226 | unsigned int taac_timevalue; |
| 1227 | unsigned int taac_timeunit; |
| 1228 | unsigned int nsac; |
| 1229 | unsigned int tran_speed_timevalue; |
| 1230 | unsigned int tran_speed_transferrateunit; |
| 1231 | unsigned int ccc; |
| 1232 | unsigned int read_bl_len; |
| 1233 | unsigned int read_bl_partial; |
| 1234 | unsigned int write_blk_misalign; |
| 1235 | unsigned int read_blk_misalign; |
| 1236 | unsigned int dsr_imp; |
| 1237 | unsigned int c_size; |
| 1238 | unsigned int vdd_r_curr_min; |
| 1239 | unsigned int vdd_r_curr_max; |
| 1240 | unsigned int vdd_w_curr_min; |
| 1241 | unsigned int vdd_w_curr_max; |
| 1242 | unsigned int c_size_mult; |
| 1243 | unsigned int erase_grp_size; |
| 1244 | unsigned int erase_grp_mult; |
| 1245 | unsigned int wp_grp_size; |
| 1246 | unsigned int wp_grp_enable; |
| 1247 | unsigned int default_ecc; |
| 1248 | unsigned int r2w_factor; |
| 1249 | unsigned int write_bl_len; |
| 1250 | unsigned int write_bl_partial; |
| 1251 | unsigned int content_prot_app; |
| 1252 | unsigned int file_format_grp; |
| 1253 | unsigned int copy; |
| 1254 | unsigned int perm_write_protect; |
| 1255 | unsigned int tmp_write_protect; |
| 1256 | unsigned int file_format; |
| 1257 | unsigned int ecc; |
| 1258 | unsigned int crc; |
| 1259 | unsigned int taac; |
| 1260 | unsigned int tran_speed; |
| 1261 | |
| 1262 | parse_bin(csd, "2u4u2r1r4u3u8u1r4u3u12u4u1u1u1u1u2r12u3u3u3u3u3u" |
| 1263 | "5u5u5u1u2u3u4u1u4r1u1u1u1u1u2u2u7u1r", |
| 1264 | &csd_structure, &spec_vers, &taac_timevalue, |
| 1265 | &taac_timeunit, &nsac, &tran_speed_timevalue, |
| 1266 | &tran_speed_transferrateunit, &ccc, &read_bl_len, |
| 1267 | &read_bl_partial, &write_blk_misalign, |
| 1268 | &read_blk_misalign, &dsr_imp, &c_size, |
| 1269 | &vdd_r_curr_min, &vdd_r_curr_max, |
| 1270 | &vdd_w_curr_min, &vdd_w_curr_max, &c_size_mult, |
| 1271 | &erase_grp_size, &erase_grp_mult, &wp_grp_size, |
| 1272 | &wp_grp_enable, &default_ecc, &r2w_factor, |
| 1273 | &write_bl_len, &write_bl_partial, &content_prot_app, |
| 1274 | &file_format_grp, ©, &perm_write_protect, |
| 1275 | &tmp_write_protect, &file_format, &ecc, &crc); |
| 1276 | |
| 1277 | taac = taac_timevalue << 3 | taac_timeunit; |
| 1278 | tran_speed = tran_speed_timevalue << 3 | tran_speed_transferrateunit; |
| 1279 | |
| 1280 | if (config->verbose) { |
| 1281 | float value; |
| 1282 | int mult; |
| 1283 | int blocknr; |
| 1284 | int block_len; |
| 1285 | unsigned long long blocks = 0; |
| 1286 | int block_size = 0; |
| 1287 | unsigned long long memory_capacity; |
| 1288 | |
| 1289 | printf("======MMC/CSD======\n"); |
| 1290 | |
| 1291 | printf("\tCSD_STRUCTURE: 0x%01x (", csd_structure); |
| 1292 | switch (csd_structure) { |
| 1293 | case 0x0: |
| 1294 | printf("v1.0)\n"); |
| 1295 | break; |
| 1296 | case 0x1: |
| 1297 | printf("v1.1)\n"); |
| 1298 | break; |
| 1299 | case 0x2: |
| 1300 | printf("v1.2)\n"); |
| 1301 | break; |
| 1302 | case 0x3: |
| 1303 | printf("version in ext_csd)\n"); |
| 1304 | break; |
| 1305 | } |
| 1306 | |
| 1307 | printf("\tSPEC_VERS: 0x%01x (", spec_vers); |
| 1308 | switch (spec_vers) { |
| 1309 | case 0x0: |
| 1310 | printf("v1.0-v1.2)\n"); |
| 1311 | break; |
| 1312 | case 0x1: |
| 1313 | printf("v1.4)\n"); |
| 1314 | break; |
| 1315 | case 0x2: |
| 1316 | printf("v2.0-v2.2)\n"); |
| 1317 | break; |
| 1318 | case 0x3: |
| 1319 | printf("v3.1-v3.31)\n"); |
| 1320 | break; |
| 1321 | case 0x4: |
| 1322 | printf("v4.0-v4.3)\n"); |
| 1323 | break; |
| 1324 | default: |
| 1325 | printf("reserved)\n"); |
| 1326 | break; |
| 1327 | } |
| 1328 | |
| 1329 | printf("\tTAAC: 0x%02x (", taac); |
| 1330 | switch (taac_timevalue) { |
| 1331 | case 0x0: |
| 1332 | value = 0.0f; |
| 1333 | break; |
| 1334 | case 0x1: |
| 1335 | value = 1.0f; |
| 1336 | break; |
| 1337 | case 0x2: |
| 1338 | value = 1.2f; |
| 1339 | break; |
| 1340 | case 0x3: |
| 1341 | value = 1.3f; |
| 1342 | break; |
| 1343 | case 0x4: |
| 1344 | value = 1.5f; |
| 1345 | break; |
| 1346 | case 0x5: |
| 1347 | value = 2.0f; |
| 1348 | break; |
| 1349 | case 0x6: |
| 1350 | value = 2.5f; |
| 1351 | break; |
| 1352 | case 0x7: |
| 1353 | value = 3.0f; |
| 1354 | break; |
| 1355 | case 0x8: |
| 1356 | value = 3.5f; |
| 1357 | break; |
| 1358 | case 0x9: |
| 1359 | value = 4.0f; |
| 1360 | break; |
| 1361 | case 0xa: |
| 1362 | value = 4.5f; |
| 1363 | break; |
| 1364 | case 0xb: |
| 1365 | value = 5.0f; |
| 1366 | break; |
| 1367 | case 0xc: |
| 1368 | value = 5.5f; |
| 1369 | break; |
| 1370 | case 0xd: |
| 1371 | value = 6.0f; |
| 1372 | break; |
| 1373 | case 0xe: |
| 1374 | value = 7.0f; |
| 1375 | break; |
| 1376 | case 0xf: |
| 1377 | value = 8.0f; |
| 1378 | break; |
| 1379 | default: |
| 1380 | value = 0.0f; |
| 1381 | break; |
| 1382 | } |
| 1383 | |
| 1384 | switch (taac_timeunit) { |
| 1385 | case 0x0: |
| 1386 | printf("%.2fns)\n", value * 1.0f); |
| 1387 | break; |
| 1388 | case 0x1: |
| 1389 | printf("%.2fns)\n", value * 10.0f); |
| 1390 | break; |
| 1391 | case 0x2: |
| 1392 | printf("%.2fns)\n", value * 100.0f); |
| 1393 | break; |
| 1394 | case 0x3: |
| 1395 | printf("%.2fus)\n", value * 1.0f); |
| 1396 | break; |
| 1397 | case 0x4: |
| 1398 | printf("%.2fus)\n", value * 10.0f); |
| 1399 | break; |
| 1400 | case 0x5: |
| 1401 | printf("%.2fus)\n", value * 100.0f); |
| 1402 | break; |
| 1403 | case 0x6: |
| 1404 | printf("%.2fms)\n", value * 1.0f); |
| 1405 | break; |
| 1406 | case 0x7: |
| 1407 | printf("%.2fms)\n", value * 10.0f); |
| 1408 | break; |
| 1409 | } |
| 1410 | |
| 1411 | printf("\tNSAC: %d clocks\n", nsac); |
| 1412 | printf("\tTRAN_SPEED: 0x%02x (", tran_speed); |
| 1413 | switch (tran_speed_timevalue) { |
| 1414 | case 0x0: |
| 1415 | value = 0.0f; |
| 1416 | break; |
| 1417 | case 0x1: |
| 1418 | value = 1.0f; |
| 1419 | break; |
| 1420 | case 0x2: |
| 1421 | value = 1.2f; |
| 1422 | break; |
| 1423 | case 0x3: |
| 1424 | value = 1.3f; |
| 1425 | break; |
| 1426 | case 0x4: |
| 1427 | value = 1.5f; |
| 1428 | break; |
| 1429 | case 0x5: |
| 1430 | value = 2.0f; |
| 1431 | break; |
| 1432 | case 0x6: |
| 1433 | value = 2.6f; |
| 1434 | break; |
| 1435 | case 0x7: |
| 1436 | value = 3.0f; |
| 1437 | break; |
| 1438 | case 0x8: |
| 1439 | value = 3.5f; |
| 1440 | break; |
| 1441 | case 0x9: |
| 1442 | value = 4.0f; |
| 1443 | break; |
| 1444 | case 0xa: |
| 1445 | value = 4.5f; |
| 1446 | break; |
| 1447 | case 0xb: |
| 1448 | value = 5.2f; |
| 1449 | break; |
| 1450 | case 0xc: |
| 1451 | value = 5.5f; |
| 1452 | break; |
| 1453 | case 0xd: |
| 1454 | value = 6.0f; |
| 1455 | break; |
| 1456 | case 0xe: |
| 1457 | value = 7.0f; |
| 1458 | break; |
| 1459 | case 0xf: |
| 1460 | value = 8.0f; |
| 1461 | break; |
| 1462 | default: |
| 1463 | value = 0.0f; |
| 1464 | break; |
| 1465 | } |
| 1466 | |
| 1467 | switch (tran_speed_transferrateunit) { |
| 1468 | case 0x0: |
| 1469 | printf("%.2fKHz/s)\n", value * 100.0f); |
| 1470 | break; |
| 1471 | case 0x1: |
| 1472 | printf("%.2fMHz/s)\n", value * 1.0f); |
| 1473 | break; |
| 1474 | case 0x2: |
| 1475 | printf("%.2fMHz/s)\n", value * 10.0f); |
| 1476 | break; |
| 1477 | case 0x3: |
| 1478 | printf("%.2fMHz/s)\n", value * 100.0f); |
| 1479 | break; |
| 1480 | default: |
| 1481 | printf("reserved)\n"); |
| 1482 | break; |
| 1483 | } |
| 1484 | |
| 1485 | printf("\tCCC: 0x%03x (class: ", ccc); |
| 1486 | if (ccc & 0x800) |
| 1487 | printf("11, "); |
| 1488 | if (ccc & 0x400) |
| 1489 | printf("10, "); |
| 1490 | if (ccc & 0x200) |
| 1491 | printf("9, "); |
| 1492 | if (ccc & 0x100) |
| 1493 | printf("8, "); |
| 1494 | if (ccc & 0x080) |
| 1495 | printf("7, "); |
| 1496 | if (ccc & 0x040) |
| 1497 | printf("6, "); |
| 1498 | if (ccc & 0x020) |
| 1499 | printf("5, "); |
| 1500 | if (ccc & 0x010) |
| 1501 | printf("4, "); |
| 1502 | if (ccc & 0x008) |
| 1503 | printf("3, "); |
| 1504 | if (ccc & 0x004) |
| 1505 | printf("2, "); |
| 1506 | if (ccc & 0x002) |
| 1507 | printf("1, "); |
| 1508 | if (ccc & 0x001) |
| 1509 | printf("0, "); |
| 1510 | printf(" )\n"); |
| 1511 | |
| 1512 | printf("\tREAD_BL_LEN: 0x%01x (", read_bl_len); |
| 1513 | switch (read_bl_len) { |
| 1514 | case 0x0: |
| 1515 | printf("1 byte)\n"); |
| 1516 | break; |
| 1517 | case 0x1: |
| 1518 | printf("2 byte)\n"); |
| 1519 | break; |
| 1520 | case 0x2: |
| 1521 | printf("4 byte)\n"); |
| 1522 | break; |
| 1523 | case 0x3: |
| 1524 | printf("8 byte)\n"); |
| 1525 | break; |
| 1526 | case 0x4: |
| 1527 | printf("16 byte)\n"); |
| 1528 | break; |
| 1529 | case 0x5: |
| 1530 | printf("32 byte)\n"); |
| 1531 | break; |
| 1532 | case 0x6: |
| 1533 | printf("64 byte)\n"); |
| 1534 | break; |
| 1535 | case 0x7: |
| 1536 | printf("128 byte)\n"); |
| 1537 | break; |
| 1538 | case 0x8: |
| 1539 | printf("256 byte)\n"); |
| 1540 | break; |
| 1541 | case 0x9: |
| 1542 | printf("512 bytes)\n"); |
| 1543 | break; |
| 1544 | case 0xa: |
| 1545 | printf("1024 bytes)\n"); |
| 1546 | break; |
| 1547 | case 0xb: |
| 1548 | printf("2048 bytes)\n"); |
| 1549 | break; |
| 1550 | case 0xc: |
| 1551 | printf("4096 bytes)\n"); |
| 1552 | break; |
| 1553 | case 0xd: |
| 1554 | printf("8192 bytes)\n"); |
| 1555 | break; |
| 1556 | case 0xe: |
| 1557 | printf("16K bytes)\n"); |
| 1558 | break; |
| 1559 | default: |
| 1560 | printf("reserved bytes)\n"); |
| 1561 | break; |
| 1562 | } |
| 1563 | |
| 1564 | printf("\tREAD_BL_PARTIAL: 0x%01x (", read_bl_partial); |
| 1565 | switch (read_bl_partial) { |
| 1566 | case 0x0: |
| 1567 | printf("only 512 byte and READ_BL_LEN block size)\n"); |
| 1568 | break; |
| 1569 | case 0x1: |
| 1570 | printf("less than READ_BL_LEN block size can be used)\n"); |
| 1571 | break; |
| 1572 | } |
| 1573 | |
| 1574 | printf("\tWRITE_BLK_MISALIGN: 0x%01x (", write_blk_misalign); |
| 1575 | switch (write_blk_misalign) { |
| 1576 | case 0x0: |
| 1577 | printf("writes across block boundaries are invalid)\n"); |
| 1578 | break; |
| 1579 | case 0x1: |
| 1580 | printf("writes across block boundaries are allowed)\n"); |
| 1581 | break; |
| 1582 | } |
| 1583 | |
| 1584 | printf("\tREAD_BLK_MISALIGN: 0x%01x (", read_blk_misalign); |
| 1585 | switch (read_blk_misalign) { |
| 1586 | case 0x0: |
| 1587 | printf("reads across block boundaries are invalid)\n"); |
| 1588 | break; |
| 1589 | case 0x1: |
| 1590 | printf("reads across block boundaries are allowed)\n"); |
| 1591 | break; |
| 1592 | } |
| 1593 | |
| 1594 | printf("\tDSR_IMP: 0x%01x (", dsr_imp); |
| 1595 | switch (dsr_imp) { |
| 1596 | case 0x0: |
| 1597 | printf("configurable driver stage not available)\n"); |
| 1598 | break; |
| 1599 | case 0x1: |
| 1600 | printf("configurable driver state available)\n"); |
| 1601 | break; |
| 1602 | } |
| 1603 | |
| 1604 | printf("\tC_SIZE: 0x%03x\n", c_size); |
| 1605 | printf("\tVDD_R_CURR_MIN: 0x%01x (", vdd_r_curr_min); |
| 1606 | switch (vdd_r_curr_min) { |
| 1607 | case 0x0: |
| 1608 | printf("0.5mA)\n"); |
| 1609 | break; |
| 1610 | case 0x1: |
| 1611 | printf("1mA)\n"); |
| 1612 | break; |
| 1613 | case 0x2: |
| 1614 | printf("5mA)\n"); |
| 1615 | break; |
| 1616 | case 0x3: |
| 1617 | printf("10mA)\n"); |
| 1618 | break; |
| 1619 | case 0x4: |
| 1620 | printf("25mA)\n"); |
| 1621 | break; |
| 1622 | case 0x5: |
| 1623 | printf("35mA)\n"); |
| 1624 | break; |
| 1625 | case 0x6: |
| 1626 | printf("60mA)\n"); |
| 1627 | break; |
| 1628 | case 0x7: |
| 1629 | printf("100mA)\n"); |
| 1630 | break; |
| 1631 | } |
| 1632 | |
| 1633 | printf("\tVDD_R_CURR_MAX: 0x%01x (", vdd_r_curr_max); |
| 1634 | switch (vdd_r_curr_max) { |
| 1635 | case 0x0: |
| 1636 | printf("1mA)\n"); |
| 1637 | break; |
| 1638 | case 0x1: |
| 1639 | printf("5mA)\n"); |
| 1640 | break; |
| 1641 | case 0x2: |
| 1642 | printf("10mA)\n"); |
| 1643 | break; |
| 1644 | case 0x3: |
| 1645 | printf("25mA)\n"); |
| 1646 | break; |
| 1647 | case 0x4: |
| 1648 | printf("35mA)\n"); |
| 1649 | break; |
| 1650 | case 0x5: |
| 1651 | printf("45mA)\n"); |
| 1652 | break; |
| 1653 | case 0x6: |
| 1654 | printf("80mA)\n"); |
| 1655 | break; |
| 1656 | case 0x7: |
| 1657 | printf("200mA)\n"); |
| 1658 | break; |
| 1659 | } |
| 1660 | |
| 1661 | printf("\tVDD_W_CURR_MIN: 0x%01x (", vdd_w_curr_min); |
| 1662 | switch (vdd_w_curr_min) { |
| 1663 | case 0x0: |
| 1664 | printf("0.5mA)\n"); |
| 1665 | break; |
| 1666 | case 0x1: |
| 1667 | printf("1mA)\n"); |
| 1668 | break; |
| 1669 | case 0x2: |
| 1670 | printf("5mA)\n"); |
| 1671 | break; |
| 1672 | case 0x3: |
| 1673 | printf("10mA)\n"); |
| 1674 | break; |
| 1675 | case 0x4: |
| 1676 | printf("25mA)\n"); |
| 1677 | break; |
| 1678 | case 0x5: |
| 1679 | printf("35mA)\n"); |
| 1680 | break; |
| 1681 | case 0x6: |
| 1682 | printf("60mA)\n"); |
| 1683 | break; |
| 1684 | case 0x7: |
| 1685 | printf("100mA)\n"); |
| 1686 | break; |
| 1687 | } |
| 1688 | |
| 1689 | printf("\tVDD_W_CURR_MAX: 0x%01x (", vdd_w_curr_max); |
| 1690 | switch (vdd_w_curr_max) { |
| 1691 | case 0x0: |
| 1692 | printf("1mA)\n"); |
| 1693 | break; |
| 1694 | case 0x1: |
| 1695 | printf("5mA)\n"); |
| 1696 | break; |
| 1697 | case 0x2: |
| 1698 | printf("10mA)\n"); |
| 1699 | break; |
| 1700 | case 0x3: |
| 1701 | printf("25mA)\n"); |
| 1702 | break; |
| 1703 | case 0x4: |
| 1704 | printf("35mA)\n"); |
| 1705 | break; |
| 1706 | case 0x5: |
| 1707 | printf("45mA)\n"); |
| 1708 | break; |
| 1709 | case 0x6: |
| 1710 | printf("80mA)\n"); |
| 1711 | break; |
| 1712 | case 0x7: |
| 1713 | printf("200mA)\n"); |
| 1714 | break; |
| 1715 | } |
| 1716 | |
| 1717 | printf("\tC_SIZE_MULT: 0x%01x\n", c_size_mult); |
| 1718 | printf("\tERASE_GRP_SIZE: 0x%02x\n", erase_grp_size); |
| 1719 | printf("\tERASE_GRP_MULT: 0x%02x (%d write blocks/erase group)\n", |
| 1720 | erase_grp_mult, (erase_grp_size + 1) * |
| 1721 | (erase_grp_mult + 1)); |
| 1722 | printf("\tWP_GRP_SIZE: 0x%02x (%d blocks/write protect group)\n", |
| 1723 | wp_grp_size, wp_grp_size + 1); |
| 1724 | printf("\tWP_GRP_ENABLE: 0x%01x\n", wp_grp_enable); |
| 1725 | |
| 1726 | printf("\tDEFAULT_ECC: 0x%01x (", default_ecc); |
| 1727 | switch (default_ecc) { |
| 1728 | case 0: |
| 1729 | printf("none)\n"); |
| 1730 | break; |
| 1731 | case 1: |
| 1732 | printf("BCH)\n"); |
| 1733 | break; |
| 1734 | default: |
| 1735 | printf("reserved)\n"); |
| 1736 | break; |
| 1737 | } |
| 1738 | |
| 1739 | printf("\tR2W_FACTOR: 0x%01x (Write %d times read)\n", |
| 1740 | r2w_factor, r2w_factor); |
| 1741 | |
| 1742 | printf("\tWRITE_BL_LEN: 0x%01x (", write_bl_len); |
| 1743 | switch (write_bl_len) { |
| 1744 | case 0x0: |
| 1745 | printf("1 byte)\n"); |
| 1746 | break; |
| 1747 | case 0x1: |
| 1748 | printf("2 byte)\n"); |
| 1749 | break; |
| 1750 | case 0x2: |
| 1751 | printf("4 byte)\n"); |
| 1752 | break; |
| 1753 | case 0x3: |
| 1754 | printf("8 byte)\n"); |
| 1755 | break; |
| 1756 | case 0x4: |
| 1757 | printf("16 byte)\n"); |
| 1758 | break; |
| 1759 | case 0x5: |
| 1760 | printf("32 byte)\n"); |
| 1761 | break; |
| 1762 | case 0x6: |
| 1763 | printf("64 byte)\n"); |
| 1764 | break; |
| 1765 | case 0x7: |
| 1766 | printf("128 byte)\n"); |
| 1767 | break; |
| 1768 | case 0x8: |
| 1769 | printf("256 byte)\n"); |
| 1770 | break; |
| 1771 | case 0x9: |
| 1772 | printf("512 bytes)\n"); |
| 1773 | break; |
| 1774 | case 0xa: |
| 1775 | printf("1024 bytes)\n"); |
| 1776 | break; |
| 1777 | case 0xb: |
| 1778 | printf("2048 bytes)\n"); |
| 1779 | break; |
| 1780 | case 0xc: |
| 1781 | printf("4096 bytes)\n"); |
| 1782 | break; |
| 1783 | case 0xd: |
| 1784 | printf("8192 bytes)\n"); |
| 1785 | break; |
| 1786 | case 0xe: |
| 1787 | printf("16K bytes)\n"); |
| 1788 | break; |
| 1789 | default: |
| 1790 | printf("reserved bytes)\n"); |
| 1791 | break; |
| 1792 | } |
| 1793 | |
| 1794 | printf("\tWRITE_BL_PARTIAL: 0x%01x (", write_bl_partial); |
| 1795 | switch (write_bl_partial) { |
| 1796 | case 0x0: |
| 1797 | printf("only 512 byte and WRITE_BL_LEN block size)\n"); |
| 1798 | break; |
| 1799 | case 0x1: |
| 1800 | printf("less than WRITE_BL_LEN block size can be used)\n"); |
| 1801 | break; |
| 1802 | } |
| 1803 | |
| 1804 | printf("\tCONTENT_PROT_APP: 0x%01x\n", content_prot_app); |
| 1805 | printf("\tFILE_FORMAT_GRP: 0x%01x\n", file_format_grp); |
| 1806 | if (file_format_grp != 0) |
| 1807 | printf("Warn: Invalid FILE_FORMAT_GRP\n"); |
| 1808 | |
| 1809 | printf("\tCOPY: 0x%01x\n", copy); |
| 1810 | printf("\tPERM_WRITE_PROTECT: 0x%01x\n", perm_write_protect); |
| 1811 | printf("\tTMP_WRITE_PROTECT: 0x%01x\n", tmp_write_protect); |
| 1812 | printf("\tFILE_FORMAT: 0x%01x (", file_format); |
| 1813 | if (file_format != 0) |
| 1814 | printf("Warn: Invalid FILE_FORMAT\n"); |
| 1815 | |
| 1816 | if (file_format_grp == 1) { |
| 1817 | printf("reserved)\n"); |
| 1818 | } else { |
| 1819 | switch (file_format) { |
| 1820 | case 0: |
| 1821 | printf("partition table)\n"); |
| 1822 | break; |
| 1823 | case 1: |
| 1824 | printf("no partition table)\n"); |
| 1825 | break; |
| 1826 | case 2: |
| 1827 | printf("Universal File Format)\n"); |
| 1828 | break; |
| 1829 | case 3: |
| 1830 | printf("Others/unknown)\n"); |
| 1831 | break; |
| 1832 | } |
| 1833 | } |
| 1834 | |
| 1835 | printf("\tECC: 0x%01x (", ecc); |
| 1836 | switch (ecc) { |
| 1837 | case 0: |
| 1838 | printf("none)\n"); |
| 1839 | break; |
| 1840 | case 1: |
| 1841 | printf("BCH(542,512))\n"); |
| 1842 | break; |
| 1843 | default: |
| 1844 | printf("reserved)\n"); |
| 1845 | break; |
| 1846 | } |
| 1847 | |
| 1848 | printf("\tCRC: 0x%01x\n", crc); |
| 1849 | |
| 1850 | mult = 1 << (c_size_mult + 2); |
| 1851 | blocknr = (c_size + 1) * mult; |
| 1852 | block_len = 1 << read_bl_len; |
| 1853 | blocks = blocknr; |
| 1854 | block_size = block_len; |
| 1855 | |
| 1856 | memory_capacity = blocks * block_size; |
| 1857 | |
| 1858 | printf("\tCAPACITY: "); |
| 1859 | if (memory_capacity / (1024ull * 1024ull * 1024ull) > 0) |
| 1860 | printf("%.2fGbyte", |
| 1861 | memory_capacity / (1024.0 * 1024.0 * 1024.0)); |
| 1862 | else if (memory_capacity / (1024ull * 1024ull) > 0) |
| 1863 | printf("%.2fMbyte", memory_capacity / (1024.0 * 1024.0)); |
| 1864 | else if (memory_capacity / (1024ull) > 0) |
| 1865 | printf("%.2fKbyte", memory_capacity / (1024.0)); |
| 1866 | else |
| 1867 | printf("%.2fbyte", memory_capacity * 1.0); |
| 1868 | |
| 1869 | printf(" (%lld bytes, %lld sectors, %d bytes each)\n", |
| 1870 | memory_capacity, blocks, block_size); |
| 1871 | } else { |
| 1872 | int mult; |
| 1873 | int blocknr; |
| 1874 | int block_len; |
| 1875 | unsigned long long blocks = 0; |
| 1876 | int block_size = 0; |
| 1877 | unsigned long long memory_capacity; |
| 1878 | |
| 1879 | printf("version: "); |
| 1880 | switch (spec_vers) { |
| 1881 | case 0x0: |
| 1882 | printf("MMC v1.0-v1.2\n"); |
| 1883 | break; |
| 1884 | case 0x1: |
| 1885 | printf("MMC v1.4\n"); |
| 1886 | break; |
| 1887 | case 0x2: |
| 1888 | printf("MMC v2.0-v2.2\n"); |
| 1889 | break; |
| 1890 | case 0x3: |
| 1891 | printf("MMC v3.1-v3.31\n"); |
| 1892 | break; |
| 1893 | case 0x4: |
| 1894 | printf("MMC v4.0-v4.3\n"); |
| 1895 | break; |
| 1896 | default: |
| 1897 | printf("reserved\n"); |
| 1898 | break; |
| 1899 | } |
| 1900 | |
| 1901 | printf("card classes: "); |
| 1902 | if (ccc & 0x800) |
| 1903 | printf("11, "); |
| 1904 | if (ccc & 0x400) |
| 1905 | printf("10, "); |
| 1906 | if (ccc & 0x200) |
| 1907 | printf("9, "); |
| 1908 | if (ccc & 0x100) |
| 1909 | printf("8, "); |
| 1910 | if (ccc & 0x080) |
| 1911 | printf("7, "); |
| 1912 | if (ccc & 0x040) |
| 1913 | printf("6, "); |
| 1914 | if (ccc & 0x020) |
| 1915 | printf("5, "); |
| 1916 | if (ccc & 0x010) |
| 1917 | printf("4, "); |
| 1918 | if (ccc & 0x008) |
| 1919 | printf("3, "); |
| 1920 | if (ccc & 0x004) |
| 1921 | printf("2, "); |
| 1922 | if (ccc & 0x002) |
| 1923 | printf("1, "); |
| 1924 | if (ccc & 0x001) |
| 1925 | printf("0, "); |
| 1926 | printf("\b\b\n"); |
| 1927 | |
| 1928 | mult = 1 << (c_size_mult + 2); |
| 1929 | blocknr = (c_size + 1) * mult; |
| 1930 | block_len = 1 << read_bl_len; |
| 1931 | blocks = blocknr; |
| 1932 | block_size = block_len; |
| 1933 | |
| 1934 | memory_capacity = blocks * block_size; |
| 1935 | |
| 1936 | printf("capacity: "); |
| 1937 | if (memory_capacity / (1024ull * 1024ull * 1024ull) > 0) |
| 1938 | printf("%.2fGbyte", |
| 1939 | memory_capacity / (1024.0 * 1024.0 * 1024.0)); |
| 1940 | else if (memory_capacity / (1024ull * 1024ull) > 0) |
| 1941 | printf("%.2fMbyte", memory_capacity / (1024.0 * 1024.0)); |
| 1942 | else if (memory_capacity / (1024ull) > 0) |
| 1943 | printf("%.2fKbyte", memory_capacity / (1024.0)); |
| 1944 | else |
| 1945 | printf("%.2fbyte", memory_capacity * 1.0); |
| 1946 | printf(" (%lld bytes, %lld sectors, %d bytes each)\n", |
| 1947 | memory_capacity, blocks, block_size); |
| 1948 | } |
| 1949 | } |
| 1950 | |
| 1951 | char *speed_class_speed(unsigned char id, bool ddr) |
| 1952 | { |
| 1953 | if (ddr) { |
| 1954 | switch (id) { |
| 1955 | case 0x00: return "<4.8MB/s"; |
| 1956 | case 0x08: return " 4.8MB/s"; |
| 1957 | case 0x0a: return " 6.0MB/s"; |
| 1958 | case 0x0f: return " 9.0MB/s"; |
| 1959 | case 0x14: return "12.0MB/s"; |
| 1960 | case 0x1e: return "18.0MB/s"; |
| 1961 | case 0x28: return "24.0MB/s"; |
| 1962 | case 0x32: return "30.0MB/s"; |
| 1963 | case 0x3c: return "36.0MB/s"; |
| 1964 | case 0x46: return "42.0MB/s"; |
| 1965 | case 0x50: return "48.0MB/s"; |
| 1966 | case 0x64: return "60.0MB/s"; |
| 1967 | case 0x78: return "72.0MB/s"; |
| 1968 | case 0x8c: return "84.0MB/s"; |
| 1969 | case 0xa0: return "96.0MB/s"; |
| 1970 | default: return "??.?MB/s"; |
| 1971 | } |
| 1972 | } else { |
| 1973 | switch (id) { |
| 1974 | case 0x00: return "<2.4MB/s"; |
| 1975 | case 0x08: return " 2.4MB/s"; |
| 1976 | case 0x0a: return " 3.0MB/s"; |
| 1977 | case 0x0f: return " 4.5MB/s"; |
| 1978 | case 0x14: return " 6.0MB/s"; |
| 1979 | case 0x1e: return " 9.0MB/s"; |
| 1980 | case 0x28: return "12.0MB/s"; |
| 1981 | case 0x32: return "15.0MB/s"; |
| 1982 | case 0x3c: return "18.0MB/s"; |
| 1983 | case 0x46: return "21.0MB/s"; |
| 1984 | case 0x50: return "24.0MB/s"; |
| 1985 | case 0x64: return "30.0MB/s"; |
| 1986 | case 0x78: return "36.0MB/s"; |
| 1987 | case 0x8c: return "42.0MB/s"; |
| 1988 | case 0xa0: return "48.0MB/s"; |
| 1989 | default: return "??.?MB/s"; |
| 1990 | } |
| 1991 | } |
| 1992 | } |
| 1993 | |
| 1994 | char speed_class_name(unsigned char id) |
| 1995 | { |
| 1996 | switch (id) { |
| 1997 | case 0x00: return '?'; |
| 1998 | case 0x08: return 'A'; |
| 1999 | case 0x0a: return 'B'; |
| 2000 | case 0x0f: return 'C'; |
| 2001 | case 0x14: return 'D'; |
| 2002 | case 0x1e: return 'E'; |
| 2003 | case 0x28: return 'F'; |
| 2004 | case 0x32: return 'G'; |
| 2005 | case 0x3c: return 'H'; |
| 2006 | case 0x46: return 'J'; |
| 2007 | case 0x50: return 'K'; |
| 2008 | case 0x64: return 'M'; |
| 2009 | case 0x78: return 'O'; |
| 2010 | case 0x8c: return 'R'; |
| 2011 | case 0xa0: return 'T'; |
| 2012 | default: return '?'; |
| 2013 | } |
| 2014 | } |
| 2015 | |
| 2016 | char *power_class_consumption(unsigned int id, bool volt360) |
| 2017 | { |
| 2018 | if (volt360) { |
| 2019 | switch (id) { |
| 2020 | case 0x0: return "100-200mA"; |
| 2021 | case 0x1: return "120-220mA"; |
| 2022 | case 0x2: return "150-250mA"; |
| 2023 | case 0x3: return "180-280mA"; |
| 2024 | case 0x4: return "200-300mA"; |
| 2025 | case 0x5: return "220-320mA"; |
| 2026 | case 0x6: return "250-350mA"; |
| 2027 | case 0x7: return "300-400mA"; |
| 2028 | case 0x8: return "350-450mA"; |
| 2029 | case 0x9: return "400-500mA"; |
| 2030 | case 0xa: return "450-550mA"; |
| 2031 | default: return "reserved"; |
| 2032 | } |
| 2033 | } else { |
| 2034 | switch (id) { |
| 2035 | case 0x0: return "65-130mA"; |
| 2036 | case 0x1: return "70-140mA"; |
| 2037 | case 0x2: return "80-160mA"; |
| 2038 | case 0x3: return "90-180mA"; |
| 2039 | case 0x4: return "100-200mA"; |
| 2040 | case 0x5: return "120-220mA"; |
| 2041 | case 0x6: return "140-240mA"; |
| 2042 | case 0x7: return "160-260mA"; |
| 2043 | case 0x8: return "180-280mA"; |
| 2044 | case 0x9: return "200-300mA"; |
| 2045 | case 0xa: return "250-350mA"; |
| 2046 | default: return "reserved"; |
| 2047 | } |
| 2048 | } |
| 2049 | } |
| 2050 | |
| 2051 | char *sleep_consumption(unsigned int id) |
| 2052 | { |
| 2053 | switch (id) { |
| 2054 | case 0x00: return "not defined"; |
| 2055 | case 0x01: return "2uA"; |
| 2056 | case 0x02: return "4uA"; |
| 2057 | case 0x03: return "8uA"; |
| 2058 | case 0x04: return "16uA"; |
| 2059 | case 0x05: return "32uA"; |
| 2060 | case 0x06: return "64uA"; |
| 2061 | case 0x07: return "128uA"; |
| 2062 | case 0x08: return "0.256mA"; |
| 2063 | case 0x09: return "0.512mA"; |
| 2064 | case 0x0a: return "1.024mA"; |
| 2065 | case 0x0b: return "2.048mA"; |
| 2066 | case 0x0c: return "4.096mA"; |
| 2067 | case 0x0d: return "8.192mA"; |
| 2068 | default: return "reserved"; |
| 2069 | } |
| 2070 | } |
| 2071 | |
| 2072 | void print_sd_scr(struct config *config, char *scr) |
| 2073 | { |
| 2074 | unsigned int scr_structure; |
| 2075 | unsigned int sd_spec; |
| 2076 | unsigned int data_stat_after_erase; |
| 2077 | unsigned int sd_security; |
| 2078 | unsigned int sd_bus_widths; |
| 2079 | unsigned int sd_spec3; |
| 2080 | unsigned int ex_security; |
| 2081 | unsigned int cmd_support; |
| 2082 | |
| 2083 | parse_bin(scr, "4u4u1u3u4u1u4u9r2u32r", |
| 2084 | &scr_structure, &sd_spec, &data_stat_after_erase, |
| 2085 | &sd_security, &sd_bus_widths, &sd_spec3, |
| 2086 | &ex_security, &cmd_support); |
| 2087 | |
| 2088 | if (config->verbose) { |
| 2089 | printf("======SD/SCR======\n"); |
| 2090 | |
| 2091 | printf("\tSCR_STRUCTURE: 0x%01x (", scr_structure); |
| 2092 | switch (scr_structure) { |
| 2093 | case 0: |
| 2094 | printf("SCR v1.0)\n"); |
| 2095 | break; |
| 2096 | default: |
| 2097 | printf("reserved)\n"); |
| 2098 | break; |
| 2099 | } |
| 2100 | |
| 2101 | printf("\tSD_SPEC: 0x%01x (", sd_spec); |
| 2102 | switch (sd_spec) { |
| 2103 | case 0: |
| 2104 | printf("SD v1.0/1.01)\n"); |
| 2105 | break; |
| 2106 | case 1: |
| 2107 | printf("SD v1.10)\n"); |
| 2108 | break; |
| 2109 | case 2: |
| 2110 | printf("SD v2.00/v3.0x)\n"); |
| 2111 | break; |
| 2112 | case 3: |
| 2113 | printf("SD v4.00)\n"); |
| 2114 | break; |
| 2115 | default: |
| 2116 | printf("reserved)\n"); |
| 2117 | break; |
| 2118 | } |
| 2119 | |
| 2120 | printf("\tDATA_STAT_AFTER_ERASE: 0x%01x\n", |
| 2121 | data_stat_after_erase); |
| 2122 | |
| 2123 | printf("\tSD_SECURITY: 0x%01x (", sd_security); |
| 2124 | switch (sd_security) { |
| 2125 | case 0: |
| 2126 | printf("no security)\n"); |
| 2127 | break; |
| 2128 | case 1: |
| 2129 | printf("not used)\n"); |
| 2130 | break; |
| 2131 | case 2: |
| 2132 | printf("SDSC card/security v1.01)\n"); |
| 2133 | break; |
| 2134 | case 3: |
| 2135 | printf("SDHC card/security v2.00)\n"); |
| 2136 | break; |
| 2137 | case 4: |
| 2138 | printf("SDXC card/security v3.xx)\n"); |
| 2139 | break; |
| 2140 | default: |
| 2141 | printf("reserved)\n"); |
| 2142 | break; |
| 2143 | } |
| 2144 | |
| 2145 | printf("\tSD_BUS_WIDTHS: 0x%01x (", sd_bus_widths); |
| 2146 | if (BITS(sd_bus_widths, 2, 2)) |
| 2147 | printf("4bit, "); |
| 2148 | if (BITS(sd_bus_widths, 0, 0)) |
| 2149 | printf("1bit, "); |
| 2150 | printf(" bus)\n"); |
| 2151 | |
| 2152 | printf("\tSD_SPEC3: 0x%01x (", sd_spec3); |
| 2153 | if (sd_spec >= 2) { |
| 2154 | switch (sd_spec3) { |
| 2155 | case 0: |
| 2156 | printf("SD v2.00)\n"); |
| 2157 | break; |
| 2158 | case 1: |
| 2159 | printf("SD v3.0x)\n"); |
| 2160 | break; |
| 2161 | } |
| 2162 | } else { |
| 2163 | printf("SD 1.xx)\n"); |
| 2164 | } |
| 2165 | |
| 2166 | printf("\tEX_SECURITY: 0x%01x\n", ex_security); |
| 2167 | |
| 2168 | printf("\tCMD_SUPPORT: 0x%01x (", cmd_support); |
| 2169 | if (BITS(cmd_support, 1, 1)) |
| 2170 | printf("CMD23 "); |
| 2171 | if (BITS(cmd_support, 0, 0)) |
| 2172 | printf("CMD20 "); |
| 2173 | printf(" )\n"); |
| 2174 | } else { |
| 2175 | printf("version: "); |
| 2176 | switch (sd_spec) { |
| 2177 | case 0: |
| 2178 | printf("SD 1.0/1.01\n"); |
| 2179 | break; |
| 2180 | case 1: |
| 2181 | printf("SD 1.10\n"); |
| 2182 | break; |
| 2183 | case 2: |
| 2184 | switch (sd_spec3) { |
| 2185 | case 0: |
| 2186 | printf("SD 2.00\n"); |
| 2187 | break; |
| 2188 | case 1: |
| 2189 | printf("SD 3.0x\n"); |
| 2190 | break; |
| 2191 | default: |
| 2192 | printf("unknown\n"); |
| 2193 | break; |
| 2194 | } |
| 2195 | break; |
| 2196 | case 3: |
| 2197 | printf("SD 4.00\n"); |
| 2198 | break; |
| 2199 | default: |
| 2200 | printf("unknown\n"); |
| 2201 | break; |
| 2202 | } |
| 2203 | |
| 2204 | printf("bus widths: "); |
| 2205 | if (BITS(sd_bus_widths, 2, 2)) |
| 2206 | printf("4bit, "); |
| 2207 | if (BITS(sd_bus_widths, 0, 0)) |
| 2208 | printf("1bit, "); |
| 2209 | printf("\b\b\n"); |
| 2210 | } |
| 2211 | } |
| 2212 | |
| 2213 | /* MMC/SD interface processing functions */ |
| 2214 | void print_info(struct config *config, char *type, |
| 2215 | char *cid, char *csd, char *scr, char *ext_csd) |
| 2216 | { |
| 2217 | printf("type: '%s'\n", type); |
| 2218 | |
| 2219 | if (!strcmp(type, "SD") && cid) |
| 2220 | print_sd_cid(config, cid); |
| 2221 | else if (!strcmp(type, "MMC") && cid) |
| 2222 | print_mmc_cid(config, cid); |
| 2223 | |
| 2224 | if (!strcmp(type, "SD") && scr) |
| 2225 | print_sd_scr(config, scr); |
| 2226 | |
| 2227 | if (!strcmp(type, "MMC") && csd) |
| 2228 | print_mmc_csd(config, csd); |
| 2229 | else if (!strcmp(type, "SD") && csd) |
| 2230 | print_sd_csd(config, csd); |
| 2231 | } |
| 2232 | |
| 2233 | int process_dir(struct config *config, enum REG_TYPE reg) |
| 2234 | { |
| 2235 | char *type = NULL, *cid = NULL, *csd = NULL, *scr = NULL, *ext_csd = NULL; |
| 2236 | int ret = 0; |
| 2237 | |
| 2238 | if (chdir(config->dir) < 0) { |
| 2239 | fprintf(stderr, |
| 2240 | "MMC/SD information directory '%s' does not exist.\n", |
| 2241 | config->dir); |
| 2242 | return -1; |
| 2243 | } |
| 2244 | |
| 2245 | type = read_file("type"); |
| 2246 | if (!type) { |
| 2247 | fprintf(stderr, |
| 2248 | "Could not read card interface type in directory '%s'.\n", |
| 2249 | config->dir); |
| 2250 | return -1; |
| 2251 | } |
| 2252 | |
| 2253 | if (strcmp(type, "MMC") && strcmp(type, "SD")) { |
| 2254 | fprintf(stderr, "Unknown type: '%s'\n", type); |
| 2255 | ret = -1; |
| 2256 | goto err; |
| 2257 | } |
| 2258 | |
| 2259 | switch (reg) { |
| 2260 | case CID: |
| 2261 | cid = read_file("cid"); |
| 2262 | if (!cid) { |
| 2263 | fprintf(stderr, |
| 2264 | "Could not read card identity in directory '%s'.\n", |
| 2265 | config->dir); |
| 2266 | ret = -1; |
| 2267 | goto err; |
| 2268 | } |
| 2269 | break; |
| 2270 | case CSD: |
| 2271 | csd = read_file("csd"); |
| 2272 | if (!csd) { |
| 2273 | fprintf(stderr, |
| 2274 | "Could not read card specific data in " |
| 2275 | "directory '%s'.\n", config->dir); |
| 2276 | ret = -1; |
| 2277 | goto err; |
| 2278 | } |
| 2279 | break; |
| 2280 | case SCR: |
| 2281 | if (!strcmp(type, "SD")) { |
| 2282 | scr = read_file("scr"); |
| 2283 | if (!scr) { |
| 2284 | fprintf(stderr, "Could not read SD card " |
| 2285 | "configuration in directory '%s'.\n", |
| 2286 | config->dir); |
| 2287 | ret = -1; |
| 2288 | goto err; |
| 2289 | } |
| 2290 | } |
| 2291 | break; |
| 2292 | case EXT_CSD: |
| 2293 | if (!strcmp(type, "MMC")) { |
| 2294 | ext_csd = read_file("ext_csd"); |
| 2295 | if (!ext_csd) { |
| 2296 | fprintf(stderr, "Could not read extra specific " |
| 2297 | "data in directory '%s'.\n", |
| 2298 | config->dir); |
| 2299 | ret = -1; |
| 2300 | goto err; |
| 2301 | } |
| 2302 | } |
| 2303 | break; |
| 2304 | default: |
| 2305 | goto err; |
| 2306 | } |
| 2307 | |
| 2308 | print_info(config, type, cid, csd, scr, ext_csd); |
| 2309 | |
| 2310 | err: |
| 2311 | free(ext_csd); |
| 2312 | free(scr); |
| 2313 | free(csd); |
| 2314 | free(cid); |
| 2315 | free(type); |
| 2316 | |
| 2317 | return ret; |
| 2318 | } |
| 2319 | |
| 2320 | int lsmmc_main(struct config *config, int argc, char **argv) |
| 2321 | { |
| 2322 | int ret; |
| 2323 | |
| 2324 | memset(config, 0, sizeof(*config)); |
| 2325 | config->mmc_ids = calloc(IDS_MAX, sizeof(char *)); |
| 2326 | config->sd_ids = calloc(IDS_MAX, sizeof(char *)); |
| 2327 | if (!config->mmc_ids || !config->sd_ids) { |
| 2328 | fprintf(stderr, "Could not allocate memory for lsmmc.\n"); |
| 2329 | return -1; |
| 2330 | } |
| 2331 | |
| 2332 | ret = parse_opts(argc, argv, config); |
| 2333 | if (ret) |
| 2334 | return ret; |
| 2335 | |
| 2336 | return parse_ids(config); |
| 2337 | } |
| 2338 | |
| 2339 | void lsmmc_free(struct config *config) |
| 2340 | { |
| 2341 | free(config->mmc_ids); |
| 2342 | free(config->sd_ids); |
| 2343 | free(config->dir); |
| 2344 | } |
| 2345 | |
| 2346 | int do_read_csd(int argc, char **argv) |
| 2347 | { |
| 2348 | struct config config; |
| 2349 | int ret; |
| 2350 | |
Uwe Kleine-König | cd2fea7 | 2017-12-21 11:00:11 +0100 | [diff] [blame^] | 2351 | if (argc != 2 && argc != 3) { |
| 2352 | fprintf(stderr, "Usage: Print CSD data from <device path>.\n"); |
| 2353 | exit(1); |
| 2354 | } |
Sebastian Rasmussen | 7e00a5a | 2016-02-23 13:37:28 +0800 | [diff] [blame] | 2355 | |
| 2356 | ret = lsmmc_main(&config, argc, argv); |
| 2357 | if (ret) |
| 2358 | goto out; |
| 2359 | |
| 2360 | if (config.dir) |
| 2361 | ret = process_dir(&config, CSD); |
| 2362 | |
| 2363 | out: |
| 2364 | lsmmc_free(&config); |
| 2365 | |
| 2366 | return ret; |
| 2367 | } |
| 2368 | |
| 2369 | int do_read_cid(int argc, char **argv) |
| 2370 | { |
| 2371 | struct config config; |
| 2372 | int ret; |
| 2373 | |
Uwe Kleine-König | cd2fea7 | 2017-12-21 11:00:11 +0100 | [diff] [blame^] | 2374 | if (argc != 2 && argc != 3) { |
| 2375 | fprintf(stderr, "Usage: Print CID data from <device path>.\n"); |
| 2376 | exit(1); |
| 2377 | } |
Sebastian Rasmussen | 7e00a5a | 2016-02-23 13:37:28 +0800 | [diff] [blame] | 2378 | |
| 2379 | ret = lsmmc_main(&config, argc, argv); |
| 2380 | if (ret) |
| 2381 | goto out; |
| 2382 | |
| 2383 | if (config.dir) |
| 2384 | ret = process_dir(&config, CID); |
| 2385 | |
| 2386 | out: |
| 2387 | lsmmc_free(&config); |
| 2388 | |
| 2389 | return ret; |
| 2390 | } |
| 2391 | |
| 2392 | int do_read_scr(int argc, char **argv) |
| 2393 | { |
| 2394 | struct config config; |
| 2395 | int ret; |
| 2396 | |
Uwe Kleine-König | cd2fea7 | 2017-12-21 11:00:11 +0100 | [diff] [blame^] | 2397 | if (argc != 2 && argc != 3) { |
| 2398 | fprintf(stderr, "Usage: Print SCR data from <device path>.\n"); |
| 2399 | exit(1); |
| 2400 | } |
Sebastian Rasmussen | 7e00a5a | 2016-02-23 13:37:28 +0800 | [diff] [blame] | 2401 | |
| 2402 | ret = lsmmc_main(&config, argc, argv); |
| 2403 | if (ret) |
| 2404 | goto out; |
| 2405 | |
| 2406 | if (config.dir) |
| 2407 | ret = process_dir(&config, SCR); |
| 2408 | |
| 2409 | out: |
| 2410 | lsmmc_free(&config); |
| 2411 | |
| 2412 | return ret; |
| 2413 | } |