From patchwork Tue Feb 2 08:21:08 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "(Exiting) Baolin Wang" X-Patchwork-Id: 8187451 Return-Path: X-Original-To: patchwork-linux-mmc@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id B5CE79F4DD for ; Tue, 2 Feb 2016 08:21:39 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 910FB2026D for ; Tue, 2 Feb 2016 08:21:34 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 2562D2020F for ; Tue, 2 Feb 2016 08:21:29 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753784AbcBBIV2 (ORCPT ); Tue, 2 Feb 2016 03:21:28 -0500 Received: from mail-pf0-f179.google.com ([209.85.192.179]:33406 "EHLO mail-pf0-f179.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753678AbcBBIVZ (ORCPT ); Tue, 2 Feb 2016 03:21:25 -0500 Received: by mail-pf0-f179.google.com with SMTP id w123so6529937pfb.0 for ; Tue, 02 Feb 2016 00:21:24 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id; bh=J/Y2AjdsyNeDsE5GW0l+r/48a/vPsUiGJZZ1ksxOsiw=; b=iskWOimKumRMulTpQoFtHQd+7e7puzDiZWKEYVsA3a281XntZZwtahExHNk9D8j7Ln hi+6Ih5OSG+7F++RZEIEve1Ub/Aaq/GyLK52uBDsWhKfXlNLmfjONud279JiWqRzuKi9 dVgnb2dtLOe8vrCr5d/6OXS0I1nlB2GROfXZ0= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=J/Y2AjdsyNeDsE5GW0l+r/48a/vPsUiGJZZ1ksxOsiw=; b=TE+yJ2nnApqc0cuC7mj+T5FUssuBm82SKWM7/bjtbf03ooeG6RKM1YvNbGehxF/Jd/ U7nHLcM9gxrmJjkH5gK+4HYeAl3EQcztUtxPTyaaxvYr4SmA2TaU2DXLdKhvxFeqjI0c bxLKpztXEt+tvhlXLVbBZnjwFmtl8tJPpu5qHOio95xnhhl8U+9GdzpEe7Jq8gOAXqb9 wilYLnNYjR1ZR54Ao2NjLszuPgwxwSc2IY/usOObVJzoctvk+LKiERiBaStGEvJ3NEd6 MovFolFfrgOYZb846Z1IYJn8dzU8WMEJx9G3aR8Tgwxt6RoW+KYkPlJ+jx0KOdUch5BS jDPQ== X-Gm-Message-State: AG10YORF7kdxokL4GpslMROhOo87Xuw7yTUmZVkj2ZoWw22Q+fMA1L6GojULHzkceJtQrqgy X-Received: by 10.98.12.21 with SMTP id u21mr44707209pfi.36.1454401284504; Tue, 02 Feb 2016 00:21:24 -0800 (PST) Received: from baolinwangubtpc.spreadtrum.com ([175.111.195.49]) by smtp.gmail.com with ESMTPSA id u24sm465515pfa.94.2016.02.02.00.21.20 (version=TLS1 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Tue, 02 Feb 2016 00:21:23 -0800 (PST) From: Baolin Wang To: chris@printf.net Cc: linux-mmc@vger.kernel.org, ulf.hansson@linaro.org, broonie@kernel.org, linus.walleij@linaro.org, sebras@gmail.com, baolin.wang@linaro.org Subject: [PATCH v2] mmc-utils: Merge the lsmmc tool into mmc-utils Date: Tue, 2 Feb 2016 16:21:08 +0800 Message-Id: X-Mailer: git-send-email 1.7.9.5 Sender: linux-mmc-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-mmc@vger.kernel.org X-Spam-Status: No, score=-7.2 required=5.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_HI,RP_MATCHES_RCVD,T_DKIM_INVALID,UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP The lsmmc tools contains an extensive parser of the CID, CSD, SCR, EXT_CSD registers from userspace. The utility works as-is and uses sysfs to read the register values. The original code is created by Sebastian Rasmussen and still lives in private git. It need to be merged into mmc-utils repository, which is convenient for testing MMC device from userspace. Signed-off-by: Baolin Wang Signed-off-by: Sebastian Rasmussen Signed-off-by: Chris Ball Signed-off-by: Baolin Wang --- Makefile | 1 + lsmmc.c | 4138 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ lsmmc.ids | 27 + mmc.c | 18 + mmc_cmds.h | 3 + 5 files changed, 4187 insertions(+) create mode 100644 lsmmc.c create mode 100644 lsmmc.ids diff --git a/Makefile b/Makefile index 0533be3..5e4eb1c 100644 --- a/Makefile +++ b/Makefile @@ -4,6 +4,7 @@ CFLAGS ?= -g -O2 objects = \ mmc.o \ mmc_cmds.o \ + lsmmc.o \ 3rdparty/hmac_sha/hmac_sha2.o \ 3rdparty/hmac_sha/sha2.o diff --git a/lsmmc.c b/lsmmc.c new file mode 100644 index 0000000..3161222 --- /dev/null +++ b/lsmmc.c @@ -0,0 +1,4138 @@ +/* + * Copyright (C) ST-Ericsson SA 2010-2011 + * Author: Sebastian Rasmussen + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * 3. Neither the name of the ST-Ericsson SA nor the names of its + * contributors may be used to endorse or promote products + * derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MASKTOBIT0(high) \ + ((high >= 0) ? ((1ull << ((high) + 1ull)) - 1ull) : 0ull) +#define MASK(high, low) (MASKTOBIT0(high) & ~MASKTOBIT0(low - 1)) +#define BITS(value, high, low) (((value) & MASK((high), (low))) >> (low)) + +struct config { + char *idsfile; + char *dir; + bool verbose; + int interfaces; + char **interface; + char **mmc_ids; + char **sd_ids; + + char *type; + char *cid; + char *csd; + char *scr; + char *ext_csd; +}; + +enum REG_TYPE { + CID = 0, + CSD, + SCR, + EXT_CSD, +}; + +/* Command line parsing functions */ +void usage(void) +{ + printf("Usage: print mmc [-f idsfile] [-h] [-v] \n"); + printf("\n"); + printf("Options:\n"); + printf("\t-h\tShow this help.\n"); + printf("\t-v\tEnable verbose mode.\n"); + printf("\t-f\tPath to manufacturer ID database.\n"); +} + +int parse_opts(int argc, char **argv, struct config *config) +{ + int c; + + while ((c = getopt(argc, argv, "f:hv")) != -1) { + switch (c) { + case 'f': + config->idsfile = strdup(optarg); + break; + case 'h': + usage(); + return -1; + case 'v': + config->verbose = true; + break; + case '?': + fprintf(stderr, + "Unknown option '%c' encountered.\n\n", c); + usage(); + return -1; + case ':': + fprintf(stderr, + "Argument for option '%c' missing.\n\n", c); + usage(); + return -1; + default: + fprintf(stderr, + "Unimplemented option '%c' encountered.\n", c); + break; + } + } + + if (optind >= argc) { + fprintf(stderr, "Expected mmc interface arguments.\n\n"); + usage(); + return -1; + } + + config->dir = strdup(argv[optind]); + return 0; +} + +int parse_id(struct config *config, char *line) +{ + char *type, *id, *manufacturer, *colon, *remaining; + unsigned long value; + char **ids; + + if (!line) { + fprintf(stderr, "MMC/SD id parse error, empty line"); + return -1; + } + + type = line; + colon = strchr(type, ':'); + if (!colon) { + fprintf(stderr, "MMC/SD id parse error, no delimiter found"); + return -1; + } + *colon = '\0'; + + if (!strcmp(type, "mmc")) { + ids = config->mmc_ids; + } else if (!strcmp(type, "sd")) { + ids = config->sd_ids; + } else { + fprintf(stderr, "MMC/SD id parse error, unknown type: '%s'.\n", + type); + return -1; + } + + id = colon + 1; + colon = strchr(id, ':'); + if (colon) + *colon = '\0'; + + value = strtoul(id, &remaining, 16); + if (strlen(remaining) > 0) { + fprintf(stderr, + "MMC/SD id parse error, unintelligible id: '%s'.\n", + remaining); + return -1; + } + + if (value == ULONG_MAX) { + fprintf(stderr, + "MMC/SD id parse error, id out of range: '%s'.\n", id); + return -1; + } + + manufacturer = colon + 1; + + if (ids[value]) { + fprintf(stderr, "Duplicate entries: type='%s', id='0x%1x'.\n", + type, *id); + return -1; + } + + ids[value] = strdup(manufacturer); + + return 0; +} + +int parse_ids(struct config *config) +{ + char *line = NULL; + FILE *f; + int c; + + if (!config->idsfile) + return 0; + + f = fopen(config->idsfile, "r"); + if (!f) { + fprintf(stderr, "Unable to open MMC/SD id file '%s'.\n", + config->idsfile); + return -1; + } + + do { + c = fgetc(f); + + if (c == '\n' || c == '\r') { + if (line) { + parse_id(config, line); + free(line); + line = NULL; + } + } else if (c != EOF) { + char nc[2] = { c, '\0' }; + int len = line ? strlen(line) : 0; + + line = (char *)realloc(line, len + sizeof(char) * 2); + if (!line) + goto realloc_fail; + + memcpy(&line[len], nc, 2); + } + } while (c != EOF); + +realloc_fail: + if (fclose(f)) { + fprintf(stderr, "Unable to close MMC/SD id file '%s'.\n", + config->idsfile); + return -1; + } + + if (line) + free(line); + + return 0; +} + +/* MMC/SD file parsing functions */ +char *read_file(char *name) +{ + char *preparsed; + char line[4096]; + FILE *f; + + f = fopen(name, "r"); + if (!f) { + fprintf(stderr, "Could not open MMC/SD file '%s'.\n", name); + return NULL; + } + + preparsed = fgets(line, sizeof(line), f); + if (!preparsed) { + if (ferror(f)) + fprintf(stderr, "Could not read MMC/SD file '%s'.\n", + name); + else + fprintf(stderr, + "Could not read data from MMC/SD file '%s'.\n", + name); + + if (fclose(f)) + fprintf(stderr, "Could not close MMC/SD file '%s'.\n", + name); + return NULL; + } + + if (fclose(f)) { + fprintf(stderr, "Could not close MMC/SD file '%s'.\n", name); + return NULL; + } + + line[sizeof(line) - 1] = '\0'; + + while (isspace(line[strlen(line) - 1])) + line[strlen(line) - 1] = '\0'; + + while (isspace(line[0])) + strncpy(&line[0], &line[1], sizeof(line)); + + return strdup(line); +} + +/* Hexadecimal string parsing functions */ +char *to_binstr(char *hexstr) +{ + char *bindigits[] = { + "0000", "0001", "0010", "0011", "0100", "0101", "0110", "0111", + "1000", "1001", "1010", "1011", "1100", "1101", "1110", "1111", + }; + char *binstr; + + binstr = calloc(strlen(hexstr) * 4 + 1, sizeof(char)); + + while (hexstr && *hexstr != '\0') { + if (!isxdigit(*hexstr)) + return NULL; + + if (isdigit(*hexstr)) + strcat(binstr, bindigits[*hexstr - '0']); + else if (islower(*hexstr)) + strcat(binstr, bindigits[*hexstr - 'a']); + else + strcat(binstr, bindigits[*hexstr - 'A']); + + hexstr++; + } + + return binstr; +} + +void bin_to_unsigned(unsigned int *u, char *binstr, int width) +{ + *u = 0; + assert(width <= 32); + + while (binstr && *binstr != '\0' && width > 0) { + *u <<= 1; + *u |= *binstr == '0' ? 0 : 1; + + binstr++; + width--; + } +} + +void bin_to_ascii(char *a, char *binstr, int width) +{ + assert(width % 8 == 0); + *a = '\0'; + + while (binstr && *binstr != '\0' && width > 0) { + unsigned int u; + char c[2] = { '\0', '\0' }; + char *s = &c[0]; + + bin_to_unsigned(&u, binstr, 8); + c[0] = u; + + strcat(a, s); + binstr += 8; + width -= 8; + } +} + +void parse_bin(char *hexstr, char *fmt, ...) +{ + va_list args; + char *origstr; + char *binstr; + unsigned long width = 0; + + binstr = to_binstr(hexstr); + origstr = binstr; + + va_start(args, fmt); + + while (binstr && fmt && *fmt != '\0') { + if (isdigit(*fmt)) { + char *rest; + + errno = 0; + width = strtoul(fmt, &rest, 10); + if (width == ULONG_MAX && errno != 0) + fprintf(stderr, "strtoul()"); + fmt = rest; + } else if (*fmt == 'u') { + unsigned int *u = va_arg(args, unsigned int *); + + if (u) + bin_to_unsigned(u, binstr, width); + binstr += width; + width = 0; + fmt++; + } else if (*fmt == 'r') { + binstr += width; + width = 0; + fmt++; + } else if (*fmt == 'a') { + char *c = va_arg(args, char *); + + if (c) + bin_to_ascii(c, binstr, width); + binstr += width; + width = 0; + fmt++; + } else { + fmt++; + } + } + + va_end(args); + free(origstr); +} + +/* MMC/SD information parsing functions */ +void print_sd_cid(struct config *config, char *cid) +{ + static const char *months[] = { + "jan", "feb", "mar", "apr", "may", "jun", + "jul", "aug", "sep", "oct", "nov", "dec", + "invalid0", "invalid1", "invalid2", "invalid3", + }; + unsigned int mid; + char oid[3]; + char pnm[6]; + unsigned int prv_major; + unsigned int prv_minor; + unsigned int psn; + unsigned int mdt_month; + unsigned int mdt_year; + unsigned int crc; + + parse_bin(cid, "8u16a40a4u4u32u4r8u4u7u1r", + &mid, &oid[0], &pnm[0], &prv_major, &prv_minor, &psn, + &mdt_year, &mdt_month, &crc); + + oid[2] = '\0'; + pnm[5] = '\0'; + + if (config->verbose) { + printf("======SD/CID======\n"); + + printf("\tMID: 0x%02x (", mid); + if (config->sd_ids[mid]) + printf("%s)\n", config->sd_ids[mid]); + else + printf("Unlisted)\n"); + + printf("\tOID: %s\n", oid); + printf("\tPNM: %s\n", pnm); + printf("\tPRV: 0x%01x%01x ", prv_major, prv_minor); + printf("(%d.%d)\n", prv_major, prv_minor); + printf("\tPSN: 0x%08x\n", psn); + printf("\tMDT: 0x%02x%01x %d %s\n", mdt_year, mdt_month, + 2000 + mdt_year, months[mdt_month]); + printf("\tCRC: 0x%02x\n", crc); + } else { + if (config->sd_ids[mid]) + printf("manufacturer: '%s' '%s'\n", + config->sd_ids[mid], oid); + else + printf("manufacturer: 'Unlisted' '%s'\n", oid); + + printf("product: '%s' %d.%d\n", pnm, prv_major, prv_minor); + printf("serial: 0x%08x\n", psn); + printf("manfacturing date: %d %s\n", 2000 + mdt_year, + months[mdt_month]); + } +} + +void print_mmc_cid(struct config *config, char *cid) +{ + static const char *months[] = { + "jan", "feb", "mar", "apr", "may", "jun", + "jul", "aug", "sep", "oct", "nov", "dec", + "invalid0", "invalid1", "invalid2", "invalid3", + }; + unsigned int mid; + unsigned int cbx; + unsigned int oid; + char pnm[7]; + unsigned int prv_major; + unsigned int prv_minor; + unsigned int psn; + unsigned int mdt_month; + unsigned int mdt_year; + unsigned int crc; + + parse_bin(cid, "8u6r2u8u48a4u4u32u4u4u7u1r", + &mid, &cbx, &oid, &pnm[0], &psn, &prv_major, &prv_minor, + &mdt_year, &mdt_month, &crc); + + pnm[6] = '\0'; + + if (config->verbose) { + printf("======MMC/CID======\n"); + + printf("\tMID: 0x%02x (", mid); + if (config->mmc_ids[mid]) + printf("%s)\n", config->mmc_ids[mid]); + else + printf("Unlisted)\n"); + + printf("\tCBX: 0x%01x (", cbx); + switch (cbx) { + case 0: + printf("card)\n"); + break; + case 1: + printf("BGA)\n"); + break; + case 2: + printf("PoP)\n"); + break; + case 3: + printf("reserved)\n"); + break; + } + + printf("\tOID: 0x%01x\n", oid); + printf("\tPNM: %s\n", pnm); + printf("\tPRV: 0x%01x%01x ", prv_major, prv_minor); + printf("(%d.%d)\n", prv_major, prv_minor); + printf("\tPSN: 0x%08x\n", psn); + printf("\tMDT: 0x%01x%01x %d %s\n", mdt_month, mdt_year, + 1997 + mdt_year, months[mdt_month]); + printf("\tCRC: 0x%02x\n", crc); + } else { + if (config->mmc_ids[mid]) + printf("manufacturer: '%s' '%c'\n", + config->mmc_ids[mid], oid); + else + printf("manufacturer: 'Unlisted' '%c'\n", oid); + + printf("product: '%s' %d.%d\n", pnm, prv_major, prv_minor); + printf("serial: 0x%08x\n", psn); + printf("manfacturing date: %d %s\n", 1997 + mdt_year, + months[mdt_month]); + } +} + +void print_sd_csd(struct config *config, char *csd) +{ + unsigned int csd_structure; + unsigned int taac_timevalue; + unsigned int taac_timeunit; + unsigned int nsac; + unsigned int tran_speed_timevalue; + unsigned int tran_speed_transferrateunit; + unsigned int ccc; + unsigned int read_bl_len; + unsigned int read_bl_partial; + unsigned int write_blk_misalign; + unsigned int read_blk_misalign; + unsigned int dsr_imp; + unsigned int c_size; + unsigned int vdd_r_curr_min; + unsigned int vdd_r_curr_max; + unsigned int vdd_w_curr_min; + unsigned int vdd_w_curr_max; + unsigned int c_size_mult; + unsigned int erase_blk_en; + unsigned int sector_size; + unsigned int wp_grp_size; + unsigned int wp_grp_enable; + unsigned int r2w_factor; + unsigned int write_bl_len; + unsigned int write_bl_partial; + unsigned int file_format_grp; + unsigned int copy; + unsigned int perm_write_protect; + unsigned int tmp_write_protect; + unsigned int file_format; + unsigned int crc; + unsigned int taac; + unsigned int tran_speed; + + parse_bin(csd, "2u", &csd_structure); + + if (csd_structure == 0) { + parse_bin(csd, "2u6r1r4u3u8u1r4u3u12u4u1u1u1u1u2r12u3u3u3u3u3u" + "1u7u7u1u2r3u4u1u5r1u1u1u1u2u2r7u1r", + NULL, &taac_timevalue, &taac_timeunit, &nsac, + &tran_speed_timevalue, + &tran_speed_transferrateunit, &ccc, + &read_bl_len, &read_bl_partial, + &write_blk_misalign, &read_blk_misalign, + &dsr_imp, &c_size, &vdd_r_curr_min, + &vdd_r_curr_max, &vdd_w_curr_min, + &vdd_w_curr_max, &c_size_mult, &erase_blk_en, + §or_size, &wp_grp_size, &wp_grp_enable, + &r2w_factor, &write_bl_len, &write_bl_partial, + &file_format_grp, ©, &perm_write_protect, + &tmp_write_protect, &file_format, &crc); + } else if (csd_structure == 1) { + parse_bin(csd, "2u6r1r4u3u8u1r4u3u12u4u1u1u1u1u6r22u1r1u7u7u1u" + "2r3u4u1u5r1u1u1u1u2u2r7u1r", + NULL, &taac_timevalue, &taac_timeunit, &nsac, + &tran_speed_timevalue, + &tran_speed_transferrateunit, &ccc, + &read_bl_len, &read_bl_partial, + &write_blk_misalign, &read_blk_misalign, + &dsr_imp, &c_size, &erase_blk_en, §or_size, + &wp_grp_size, &wp_grp_enable, &r2w_factor, + &write_bl_len, &write_bl_partial, + &file_format_grp, ©, &perm_write_protect, + &tmp_write_protect, &file_format, &crc); + + vdd_r_curr_min = 0; + c_size_mult = 0; + } else { + printf("Unknown CSD structure: 0x%1x\n", csd_structure); + return; + } + + taac = taac_timevalue << 3 | taac_timeunit; + tran_speed = tran_speed_timevalue << 3 | tran_speed_transferrateunit; + + if (config->verbose) { + float value; + unsigned long long blocks = 0; + int block_size = 0; + unsigned long long memory_capacity; + + printf("======SD/CSD======\n"); + + printf("\tCSD_STRUCTURE: %d\n", csd_structure); + printf("\tTAAC: 0x%02x (", taac); + + switch (taac_timevalue) { + case 0x0: + value = 0.0f; + break; + case 0x1: + value = 1.0f; + break; + case 0x2: + value = 1.2f; + break; + case 0x3: + value = 1.3f; + break; + case 0x4: + value = 1.5f; + break; + case 0x5: + value = 2.0f; + break; + case 0x6: + value = 2.5f; + break; + case 0x7: + value = 3.0f; + break; + case 0x8: + value = 3.5f; + break; + case 0x9: + value = 4.0f; + break; + case 0xa: + value = 4.5f; + break; + case 0xb: + value = 5.0f; + break; + case 0xc: + value = 5.5f; + break; + case 0xd: + value = 6.0f; + break; + case 0xe: + value = 7.0f; + break; + case 0xf: + value = 8.0f; + break; + default: + value = 0.0f; + break; + } + + switch (taac_timeunit) { + case 0x0: + printf("%.2fns)\n", value * 1.0f); + break; + case 0x1: + printf("%.2fns)\n", value * 10.0f); + break; + case 0x2: + printf("%.2fns)\n", value * 100.0f); + break; + case 0x3: + printf("%.2fus)\n", value * 1.0f); + break; + case 0x4: + printf("%.2fus)\n", value * 10.0f); + break; + case 0x5: + printf("%.2fus)\n", value * 100.0f); + break; + case 0x6: + printf("%.2fms)\n", value * 1.0f); + break; + case 0x7: + printf("%.2fms)\n", value * 10.0f); + break; + } + + if (csd_structure == 1 && taac != 0x0e) + printf("Warn: Invalid TAAC (should be 0x0e)\n"); + + printf("\tNSAC: %d clocks\n", nsac); + if (csd_structure == 1 && nsac != 0x00) + printf("Warn: Invalid NSAC (should be 0x00)\n"); + + printf("\tTRAN_SPEED: 0x%02x (", tran_speed); + switch (tran_speed_timevalue) { + case 0x0: + value = 0.0f; + break; + case 0x1: + value = 1.0f; + break; + case 0x2: + value = 1.2f; + break; + case 0x3: + value = 1.3f; + break; + case 0x4: + value = 1.5f; + break; + case 0x5: + value = 2.0f; + break; + case 0x6: + value = 2.5f; + break; + case 0x7: + value = 3.0f; + break; + case 0x8: + value = 3.5f; + break; + case 0x9: + value = 4.0f; + break; + case 0xa: + value = 4.5f; + break; + case 0xb: + value = 5.0f; + break; + case 0xc: + value = 5.5f; + break; + case 0xd: + value = 6.0f; + break; + case 0xe: + value = 7.0f; + break; + case 0xf: + value = 8.0f; + break; + default: + value = 0.0f; + break; + } + + switch (tran_speed_transferrateunit) { + case 0x0: + printf("%.2fkbit/s)\n", value * 100.0f); + break; + case 0x1: + printf("%.2fMbit/s)\n", value * 1.0f); + break; + case 0x2: + printf("%.2fMbit/s)\n", value * 10.0f); + break; + case 0x3: + printf("%.2fMbit/s)\n", value * 100.0f); + break; + default: + printf("reserved)\n"); + break; + } + if (csd_structure == 0 && + (tran_speed != 0x32 && tran_speed != 0x5a)) + printf("Warn: Invalid TRAN_SPEED " + "(should be 0x32 or 0x5a)\n"); + if (csd_structure == 1 && tran_speed != 0x32 && + tran_speed != 0x5a && tran_speed != 0x0b && + tran_speed != 0x2b) + printf("Warn: Invalid TRAN_SPEED " + "(should be 0x32, 0x5a, 0x0b or 0x2b\n"); + + printf("\tCCC: 0x%03x (class: ", ccc); + if (ccc & 0x800) + printf("11, "); + if (ccc & 0x400) + printf("10, "); + if (ccc & 0x200) + printf("9, "); + if (ccc & 0x100) + printf("8, "); + if (ccc & 0x080) + printf("7, "); + if (ccc & 0x040) + printf("6, "); + if (ccc & 0x020) + printf("5, "); + if (ccc & 0x010) + printf("4, "); + if (ccc & 0x008) + printf("3, "); + if (ccc & 0x004) + printf("2, "); + if (ccc & 0x002) + printf("1, "); + if (ccc & 0x001) + printf("0, "); + printf(" )\n"); + + if (csd_structure == 0 && + (ccc != 0x5b5 && ccc != 0x7b5 && ccc != 0x5f5)) + printf("Warn: Invalid CCC (should be 0x5b5, " + "0x7b5 or 0x5f5)\n"); + else if (csd_structure == 1 && ccc != 0x5b5 && ccc != 0x7b5) + printf("Warn: Invalid CCC (should be 0x5b5 or 0x7b5)\n"); + + printf("\tREAD_BL_LEN: 0x%01x (", read_bl_len); + switch (read_bl_len) { + case 0x9: + printf("512 bytes)\n"); + break; + case 0xa: + printf("1024 bytes)\n"); + break; + case 0xb: + printf("2048 bytes)\n"); + break; + default: + printf("reserved bytes)\n"); + break; + } + + if (csd_structure == 1 && read_bl_len != 0x9) + printf("Warn: Invalid READ_BL_LEN (should be 0x9)\n"); + + printf("\tREAD_BL_PARTIAL: 0x%01x\n", read_bl_partial); + if (csd_structure == 0 && read_bl_partial != 0x01) + printf("Warn: Invalid READ_BL_PARTIAL (should be 0x01)\n"); + else if (csd_structure == 1 && read_bl_partial != 0x00) + printf("Warn: Invalid READ_BL_PARTIAL (should be 0x00)\n"); + + printf("\tWRITE_BLK_MISALIGN: 0x%01x\n", write_blk_misalign); + if (csd_structure == 1 && write_blk_misalign != 0x00) + printf("Warn: Invalid WRITE_BLK_MISALIGN (should be 0x00)\n"); + + printf("\tREAD_BLK_MISALIGN: 0x%01x\n", read_blk_misalign); + if (csd_structure == 1 && read_blk_misalign != 0x00) + printf("Warn: Invalid READ_BLK_MISALIGN (should be 0x00)\n"); + + printf("\tDSR_IMP: 0x%01x\n", dsr_imp); + + if (csd_structure == 0) { + int mult; + int blocknr; + int block_len; + + printf("\tC_SIZE: 0x%03x\n", c_size); + printf("\tVDD_R_CURR_MIN: 0x%01x (", vdd_r_curr_min); + switch (vdd_r_curr_min) { + case 0x0: + printf("0.5mA)\n"); + break; + case 0x1: + printf("1mA)\n"); + break; + case 0x2: + printf("5mA)\n"); + break; + case 0x3: + printf("10mA)\n"); + break; + case 0x4: + printf("25mA)\n"); + break; + case 0x5: + printf("35mA)\n"); + break; + case 0x6: + printf("60mA)\n"); + break; + case 0x7: + printf("100mA)\n"); + break; + } + + printf("\tVDD_R_CURR_MAX: 0x%01x (", vdd_r_curr_max); + switch (vdd_r_curr_max) { + case 0x0: + printf("1mA)\n"); + break; + case 0x1: + printf("5mA)\n"); + break; + case 0x2: + printf("10mA)\n"); + break; + case 0x3: + printf("25mA)\n"); + break; + case 0x4: + printf("35mA)\n"); + break; + case 0x5: + printf("45mA)\n"); + break; + case 0x6: + printf("80mA)\n"); + break; + case 0x7: + printf("200mA)\n"); + break; + } + + printf("\tVDD_W_CURR_MIN: 0x%01x (", vdd_w_curr_min); + switch (vdd_w_curr_min) { + case 0x0: + printf("0.5mA)\n"); + break; + case 0x1: + printf("1mA)\n"); + break; + case 0x2: + printf("5mA)\n"); + break; + case 0x3: + printf("10mA)\n"); + break; + case 0x4: + printf("25mA)\n"); + break; + case 0x5: + printf("35mA)\n"); + break; + case 0x6: + printf("60mA)\n"); + break; + case 0x7: + printf("100mA)\n"); + break; + } + + printf("\tVDD_W_CURR_MAX: 0x%01x (", vdd_w_curr_max); + switch (vdd_w_curr_max) { + case 0x0: + printf("1mA)\n"); + break; + case 0x1: + printf("5mA)\n"); + break; + case 0x2: + printf("10mA)\n"); + break; + case 0x3: + printf("25mA)\n"); + break; + case 0x4: + printf("35mA)\n"); + break; + case 0x5: + printf("45mA)\n"); + break; + case 0x6: + printf("80mA)\n"); + break; + case 0x7: + printf("200mA)\n"); + break; + } + + printf("\tC_SIZE_MULT: 0x%01x\n", c_size_mult); + + mult = 1 << (c_size_mult + 2); + blocknr = (c_size + 1) * mult; + block_len = 1 << read_bl_len; + blocks = blocknr; + block_size = block_len; + } else if (csd_structure == 1) { + printf("\tC_SIZE: 0x%06x\n", c_size); + + printf("\tERASE_BLK_EN: 0x%01x\n", erase_blk_en); + if (erase_blk_en != 0x01) + printf("Warn: Invalid ERASE_BLK_EN (should be 0x01)\n"); + + printf("\tSECTOR_SIZE: 0x%02x (Erasable sector: %d blocks)\n", + sector_size, sector_size + 1); + if (sector_size != 0x7f) + printf("Warn: Invalid SECTOR_SIZE (should be 0x7f)\n"); + + printf("\tWP_GRP_SIZE: 0x%02x (Write protect group: %d blocks)\n", + wp_grp_size, wp_grp_size + 1); + if (wp_grp_size != 0x00) + printf("Warn: Invalid WP_GRP_SIZE (should be 0x00)\n"); + + printf("\tWP_GRP_ENABLE: 0x%01x\n", wp_grp_enable); + if (wp_grp_enable != 0x00) + printf("Warn: Invalid WP_GRP_ENABLE (should be 0x00)\n"); + + printf("\tR2W_FACTOR: 0x%01x (Write %d times read)\n", + r2w_factor, r2w_factor); + if (r2w_factor != 0x02) + printf("Warn: Invalid R2W_FACTOR (should be 0x02)\n"); + + printf("\tWRITE_BL_LEN: 0x%01x (", write_bl_len); + switch (write_bl_len) { + case 9: + printf("512 bytes)\n"); + break; + case 10: + printf("1024 bytes)\n"); + break; + case 11: + printf("2048 bytes)\n"); + break; + default: + printf("reserved)\n"); + break; + } + + if (write_bl_len != 0x09) + printf("Warn: Invalid WRITE_BL_LEN (should be 0x09)\n"); + + printf("\tWRITE_BL_PARTIAL: 0x%01x\n", write_bl_partial); + if (write_bl_partial != 0x00) + printf("Warn: Invalid WRITE_BL_PARTIAL (should be 0x00)\n"); + + printf("\tFILE_FORMAT_GRP: 0x%01x\n", file_format_grp); + if (file_format_grp != 0x00) + printf("Warn: Invalid FILE_FORMAT_GRP (should be 0x00)\n"); + + printf("\tCOPY: 0x%01x\n", copy); + printf("\tPERM_WRITE_PROTECT: 0x%01x\n", + perm_write_protect); + printf("\tTMP_WRITE_PROTECT: 0x%01x\n", + tmp_write_protect); + printf("\tFILE_FORMAT: 0x%01x (", + file_format); + + if (file_format_grp == 1) { + printf("reserved)\n"); + } else { + switch (file_format) { + case 0: + printf("partition table)\n"); + break; + case 1: + printf("no partition table)\n"); + break; + case 2: + printf("Universal File Format)\n"); + break; + case 3: + printf("Others/unknown)\n"); + break; + } + } + + if (file_format != 0x00) + printf("Warn: Invalid FILE_FORMAT (should be 0x00)\n"); + + printf("\tCRC: 0x%01x\n", crc); + + memory_capacity = (c_size + 1) * 512ull * 1024ull; + block_size = 512; + blocks = memory_capacity / block_size; + } + + memory_capacity = blocks * block_size; + + printf("\tCAPACITY: "); + if (memory_capacity / (1024ull * 1024ull * 1024ull) > 0) + printf("%.2fGbyte", + memory_capacity / (1024.0 * 1024.0 * 1024.0)); + else if (memory_capacity / (1024ull * 1024ull) > 0) + printf("%.2fMbyte", memory_capacity / (1024.0 * 1024.0)); + else if (memory_capacity / (1024ull) > 0) + printf("%.2fKbyte", memory_capacity / (1024.0)); + else + printf("%.2fbyte", memory_capacity * 1.0); + + printf(" (%lld bytes, %lld sectors, %d bytes each)\n", + memory_capacity, blocks, block_size); + } else { + unsigned long long blocks = 0; + int block_size = 0; + unsigned long long memory_capacity; + + printf("card classes: "); + if (ccc & 0x800) + printf("11 extension, "); + if (ccc & 0x400) + printf("10 switch, "); + if (ccc & 0x200) + printf("9 I/O mode, "); + if (ccc & 0x100) + printf("8 application specific, "); + if (ccc & 0x080) + printf("7 lock card, "); + if (ccc & 0x040) + printf("6 write protection, "); + if (ccc & 0x020) + printf("5 erase, "); + if (ccc & 0x010) + printf("4 block write, "); + if (ccc & 0x008) + printf("3 reserved, "); + if (ccc & 0x004) + printf("2 block read, "); + if (ccc & 0x002) + printf("1 reserved, "); + if (ccc & 0x001) + printf("0 basic, "); + printf("\b\b\n"); + + if (csd_structure == 0) { + int mult; + int blocknr; + int block_len; + + mult = 1 << (c_size_mult + 2); + blocknr = (c_size + 1) * mult; + block_len = 1 << read_bl_len; + blocks = blocknr; + block_size = block_len; + } else if (csd_structure == 1) { + memory_capacity = (c_size + 1) * 512ull * 1024ull; + block_size = 512; + blocks = memory_capacity / block_size; + } + + memory_capacity = blocks * block_size; + + printf("capacity: "); + if (memory_capacity / (1024ull * 1024ull * 1024ull) > 0) + printf("%.2fGbyte", + memory_capacity / (1024.0 * 1024.0 * 1024.0)); + else if (memory_capacity / (1024ull * 1024ull) > 0) + printf("%.2fMbyte", memory_capacity / (1024.0 * 1024.0)); + else if (memory_capacity / (1024ull) > 0) + printf("%.2fKbyte", memory_capacity / (1024.0)); + else + printf("%.2fbyte", memory_capacity * 1.0); + + printf(" (%lld bytes, %lld sectors, %d bytes each)\n", + memory_capacity, blocks, block_size); + } +} + +void print_mmc_csd(struct config *config, char *csd) +{ + unsigned int csd_structure; + unsigned int spec_vers; + unsigned int taac_timevalue; + unsigned int taac_timeunit; + unsigned int nsac; + unsigned int tran_speed_timevalue; + unsigned int tran_speed_transferrateunit; + unsigned int ccc; + unsigned int read_bl_len; + unsigned int read_bl_partial; + unsigned int write_blk_misalign; + unsigned int read_blk_misalign; + unsigned int dsr_imp; + unsigned int c_size; + unsigned int vdd_r_curr_min; + unsigned int vdd_r_curr_max; + unsigned int vdd_w_curr_min; + unsigned int vdd_w_curr_max; + unsigned int c_size_mult; + unsigned int erase_grp_size; + unsigned int erase_grp_mult; + unsigned int wp_grp_size; + unsigned int wp_grp_enable; + unsigned int default_ecc; + unsigned int r2w_factor; + unsigned int write_bl_len; + unsigned int write_bl_partial; + unsigned int content_prot_app; + unsigned int file_format_grp; + unsigned int copy; + unsigned int perm_write_protect; + unsigned int tmp_write_protect; + unsigned int file_format; + unsigned int ecc; + unsigned int crc; + unsigned int taac; + unsigned int tran_speed; + + parse_bin(csd, "2u4u2r1r4u3u8u1r4u3u12u4u1u1u1u1u2r12u3u3u3u3u3u" + "5u5u5u1u2u3u4u1u4r1u1u1u1u1u2u2u7u1r", + &csd_structure, &spec_vers, &taac_timevalue, + &taac_timeunit, &nsac, &tran_speed_timevalue, + &tran_speed_transferrateunit, &ccc, &read_bl_len, + &read_bl_partial, &write_blk_misalign, + &read_blk_misalign, &dsr_imp, &c_size, + &vdd_r_curr_min, &vdd_r_curr_max, + &vdd_w_curr_min, &vdd_w_curr_max, &c_size_mult, + &erase_grp_size, &erase_grp_mult, &wp_grp_size, + &wp_grp_enable, &default_ecc, &r2w_factor, + &write_bl_len, &write_bl_partial, &content_prot_app, + &file_format_grp, ©, &perm_write_protect, + &tmp_write_protect, &file_format, &ecc, &crc); + + taac = taac_timevalue << 3 | taac_timeunit; + tran_speed = tran_speed_timevalue << 3 | tran_speed_transferrateunit; + + if (config->verbose) { + float value; + int mult; + int blocknr; + int block_len; + unsigned long long blocks = 0; + int block_size = 0; + unsigned long long memory_capacity; + + printf("======MMC/CSD======\n"); + + printf("\tCSD_STRUCTURE: 0x%01x (", csd_structure); + switch (csd_structure) { + case 0x0: + printf("v1.0)\n"); + break; + case 0x1: + printf("v1.1)\n"); + break; + case 0x2: + printf("v1.2)\n"); + break; + case 0x3: + printf("version in ext_csd)\n"); + break; + } + + printf("\tSPEC_VERS: 0x%01x (", spec_vers); + switch (spec_vers) { + case 0x0: + printf("v1.0-v1.2)\n"); + break; + case 0x1: + printf("v1.4)\n"); + break; + case 0x2: + printf("v2.0-v2.2)\n"); + break; + case 0x3: + printf("v3.1-v3.31)\n"); + break; + case 0x4: + printf("v4.0-v4.3)\n"); + break; + default: + printf("reserved)\n"); + break; + } + + printf("\tTAAC: 0x%02x (", taac); + switch (taac_timevalue) { + case 0x0: + value = 0.0f; + break; + case 0x1: + value = 1.0f; + break; + case 0x2: + value = 1.2f; + break; + case 0x3: + value = 1.3f; + break; + case 0x4: + value = 1.5f; + break; + case 0x5: + value = 2.0f; + break; + case 0x6: + value = 2.5f; + break; + case 0x7: + value = 3.0f; + break; + case 0x8: + value = 3.5f; + break; + case 0x9: + value = 4.0f; + break; + case 0xa: + value = 4.5f; + break; + case 0xb: + value = 5.0f; + break; + case 0xc: + value = 5.5f; + break; + case 0xd: + value = 6.0f; + break; + case 0xe: + value = 7.0f; + break; + case 0xf: + value = 8.0f; + break; + default: + value = 0.0f; + break; + } + + switch (taac_timeunit) { + case 0x0: + printf("%.2fns)\n", value * 1.0f); + break; + case 0x1: + printf("%.2fns)\n", value * 10.0f); + break; + case 0x2: + printf("%.2fns)\n", value * 100.0f); + break; + case 0x3: + printf("%.2fus)\n", value * 1.0f); + break; + case 0x4: + printf("%.2fus)\n", value * 10.0f); + break; + case 0x5: + printf("%.2fus)\n", value * 100.0f); + break; + case 0x6: + printf("%.2fms)\n", value * 1.0f); + break; + case 0x7: + printf("%.2fms)\n", value * 10.0f); + break; + } + + printf("\tNSAC: %d clocks\n", nsac); + printf("\tTRAN_SPEED: 0x%02x (", tran_speed); + switch (tran_speed_timevalue) { + case 0x0: + value = 0.0f; + break; + case 0x1: + value = 1.0f; + break; + case 0x2: + value = 1.2f; + break; + case 0x3: + value = 1.3f; + break; + case 0x4: + value = 1.5f; + break; + case 0x5: + value = 2.0f; + break; + case 0x6: + value = 2.6f; + break; + case 0x7: + value = 3.0f; + break; + case 0x8: + value = 3.5f; + break; + case 0x9: + value = 4.0f; + break; + case 0xa: + value = 4.5f; + break; + case 0xb: + value = 5.2f; + break; + case 0xc: + value = 5.5f; + break; + case 0xd: + value = 6.0f; + break; + case 0xe: + value = 7.0f; + break; + case 0xf: + value = 8.0f; + break; + default: + value = 0.0f; + break; + } + + switch (tran_speed_transferrateunit) { + case 0x0: + printf("%.2fKHz/s)\n", value * 100.0f); + break; + case 0x1: + printf("%.2fMHz/s)\n", value * 1.0f); + break; + case 0x2: + printf("%.2fMHz/s)\n", value * 10.0f); + break; + case 0x3: + printf("%.2fMHz/s)\n", value * 100.0f); + break; + default: + printf("reserved)\n"); + break; + } + + printf("\tCCC: 0x%03x (class: ", ccc); + if (ccc & 0x800) + printf("11, "); + if (ccc & 0x400) + printf("10, "); + if (ccc & 0x200) + printf("9, "); + if (ccc & 0x100) + printf("8, "); + if (ccc & 0x080) + printf("7, "); + if (ccc & 0x040) + printf("6, "); + if (ccc & 0x020) + printf("5, "); + if (ccc & 0x010) + printf("4, "); + if (ccc & 0x008) + printf("3, "); + if (ccc & 0x004) + printf("2, "); + if (ccc & 0x002) + printf("1, "); + if (ccc & 0x001) + printf("0, "); + printf(" )\n"); + + printf("\tREAD_BL_LEN: 0x%01x (", read_bl_len); + switch (read_bl_len) { + case 0x0: + printf("1 byte)\n"); + break; + case 0x1: + printf("2 byte)\n"); + break; + case 0x2: + printf("4 byte)\n"); + break; + case 0x3: + printf("8 byte)\n"); + break; + case 0x4: + printf("16 byte)\n"); + break; + case 0x5: + printf("32 byte)\n"); + break; + case 0x6: + printf("64 byte)\n"); + break; + case 0x7: + printf("128 byte)\n"); + break; + case 0x8: + printf("256 byte)\n"); + break; + case 0x9: + printf("512 bytes)\n"); + break; + case 0xa: + printf("1024 bytes)\n"); + break; + case 0xb: + printf("2048 bytes)\n"); + break; + case 0xc: + printf("4096 bytes)\n"); + break; + case 0xd: + printf("8192 bytes)\n"); + break; + case 0xe: + printf("16K bytes)\n"); + break; + default: + printf("reserved bytes)\n"); + break; + } + + printf("\tREAD_BL_PARTIAL: 0x%01x (", read_bl_partial); + switch (read_bl_partial) { + case 0x0: + printf("only 512 byte and READ_BL_LEN block size)\n"); + break; + case 0x1: + printf("less than READ_BL_LEN block size can be used)\n"); + break; + } + + printf("\tWRITE_BLK_MISALIGN: 0x%01x (", write_blk_misalign); + switch (write_blk_misalign) { + case 0x0: + printf("writes across block boundaries are invalid)\n"); + break; + case 0x1: + printf("writes across block boundaries are allowed)\n"); + break; + } + + printf("\tREAD_BLK_MISALIGN: 0x%01x (", read_blk_misalign); + switch (read_blk_misalign) { + case 0x0: + printf("reads across block boundaries are invalid)\n"); + break; + case 0x1: + printf("reads across block boundaries are allowed)\n"); + break; + } + + printf("\tDSR_IMP: 0x%01x (", dsr_imp); + switch (dsr_imp) { + case 0x0: + printf("configurable driver stage not available)\n"); + break; + case 0x1: + printf("configurable driver state available)\n"); + break; + } + + printf("\tC_SIZE: 0x%03x\n", c_size); + printf("\tVDD_R_CURR_MIN: 0x%01x (", vdd_r_curr_min); + switch (vdd_r_curr_min) { + case 0x0: + printf("0.5mA)\n"); + break; + case 0x1: + printf("1mA)\n"); + break; + case 0x2: + printf("5mA)\n"); + break; + case 0x3: + printf("10mA)\n"); + break; + case 0x4: + printf("25mA)\n"); + break; + case 0x5: + printf("35mA)\n"); + break; + case 0x6: + printf("60mA)\n"); + break; + case 0x7: + printf("100mA)\n"); + break; + } + + printf("\tVDD_R_CURR_MAX: 0x%01x (", vdd_r_curr_max); + switch (vdd_r_curr_max) { + case 0x0: + printf("1mA)\n"); + break; + case 0x1: + printf("5mA)\n"); + break; + case 0x2: + printf("10mA)\n"); + break; + case 0x3: + printf("25mA)\n"); + break; + case 0x4: + printf("35mA)\n"); + break; + case 0x5: + printf("45mA)\n"); + break; + case 0x6: + printf("80mA)\n"); + break; + case 0x7: + printf("200mA)\n"); + break; + } + + printf("\tVDD_W_CURR_MIN: 0x%01x (", vdd_w_curr_min); + switch (vdd_w_curr_min) { + case 0x0: + printf("0.5mA)\n"); + break; + case 0x1: + printf("1mA)\n"); + break; + case 0x2: + printf("5mA)\n"); + break; + case 0x3: + printf("10mA)\n"); + break; + case 0x4: + printf("25mA)\n"); + break; + case 0x5: + printf("35mA)\n"); + break; + case 0x6: + printf("60mA)\n"); + break; + case 0x7: + printf("100mA)\n"); + break; + } + + printf("\tVDD_W_CURR_MAX: 0x%01x (", vdd_w_curr_max); + switch (vdd_w_curr_max) { + case 0x0: + printf("1mA)\n"); + break; + case 0x1: + printf("5mA)\n"); + break; + case 0x2: + printf("10mA)\n"); + break; + case 0x3: + printf("25mA)\n"); + break; + case 0x4: + printf("35mA)\n"); + break; + case 0x5: + printf("45mA)\n"); + break; + case 0x6: + printf("80mA)\n"); + break; + case 0x7: + printf("200mA)\n"); + break; + } + + printf("\tC_SIZE_MULT: 0x%01x\n", c_size_mult); + printf("\tERASE_GRP_SIZE: 0x%02x\n", erase_grp_size); + printf("\tERASE_GRP_MULT: 0x%02x (%d write blocks/erase group)\n", + erase_grp_mult, (erase_grp_size + 1) * + (erase_grp_mult + 1)); + printf("\tWP_GRP_SIZE: 0x%02x (%d blocks/write protect group)\n", + wp_grp_size, wp_grp_size + 1); + printf("\tWP_GRP_ENABLE: 0x%01x\n", wp_grp_enable); + + printf("\tDEFAULT_ECC: 0x%01x (", default_ecc); + switch (default_ecc) { + case 0: + printf("none)\n"); + break; + case 1: + printf("BCH)\n"); + break; + default: + printf("reserved)\n"); + break; + } + + printf("\tR2W_FACTOR: 0x%01x (Write %d times read)\n", + r2w_factor, r2w_factor); + + printf("\tWRITE_BL_LEN: 0x%01x (", write_bl_len); + switch (write_bl_len) { + case 0x0: + printf("1 byte)\n"); + break; + case 0x1: + printf("2 byte)\n"); + break; + case 0x2: + printf("4 byte)\n"); + break; + case 0x3: + printf("8 byte)\n"); + break; + case 0x4: + printf("16 byte)\n"); + break; + case 0x5: + printf("32 byte)\n"); + break; + case 0x6: + printf("64 byte)\n"); + break; + case 0x7: + printf("128 byte)\n"); + break; + case 0x8: + printf("256 byte)\n"); + break; + case 0x9: + printf("512 bytes)\n"); + break; + case 0xa: + printf("1024 bytes)\n"); + break; + case 0xb: + printf("2048 bytes)\n"); + break; + case 0xc: + printf("4096 bytes)\n"); + break; + case 0xd: + printf("8192 bytes)\n"); + break; + case 0xe: + printf("16K bytes)\n"); + break; + default: + printf("reserved bytes)\n"); + break; + } + + printf("\tWRITE_BL_PARTIAL: 0x%01x (", write_bl_partial); + switch (write_bl_partial) { + case 0x0: + printf("only 512 byte and WRITE_BL_LEN block size)\n"); + break; + case 0x1: + printf("less than WRITE_BL_LEN block size can be used)\n"); + break; + } + + printf("\tCONTENT_PROT_APP: 0x%01x\n", content_prot_app); + printf("\tFILE_FORMAT_GRP: 0x%01x\n", file_format_grp); + if (file_format_grp != 0) + printf("Warn: Invalid FILE_FORMAT_GRP\n"); + + printf("\tCOPY: 0x%01x\n", copy); + printf("\tPERM_WRITE_PROTECT: 0x%01x\n", perm_write_protect); + printf("\tTMP_WRITE_PROTECT: 0x%01x\n", tmp_write_protect); + printf("\tFILE_FORMAT: 0x%01x (", file_format); + if (file_format != 0) + printf("Warn: Invalid FILE_FORMAT\n"); + + if (file_format_grp == 1) { + printf("reserved)\n"); + } else { + switch (file_format) { + case 0: + printf("partition table)\n"); + break; + case 1: + printf("no partition table)\n"); + break; + case 2: + printf("Universal File Format)\n"); + break; + case 3: + printf("Others/unknown)\n"); + break; + } + } + + printf("\tECC: 0x%01x (", ecc); + switch (ecc) { + case 0: + printf("none)\n"); + break; + case 1: + printf("BCH(542,512))\n"); + break; + default: + printf("reserved)\n"); + break; + } + + printf("\tCRC: 0x%01x\n", crc); + + mult = 1 << (c_size_mult + 2); + blocknr = (c_size + 1) * mult; + block_len = 1 << read_bl_len; + blocks = blocknr; + block_size = block_len; + + memory_capacity = blocks * block_size; + + printf("\tCAPACITY: "); + if (memory_capacity / (1024ull * 1024ull * 1024ull) > 0) + printf("%.2fGbyte", + memory_capacity / (1024.0 * 1024.0 * 1024.0)); + else if (memory_capacity / (1024ull * 1024ull) > 0) + printf("%.2fMbyte", memory_capacity / (1024.0 * 1024.0)); + else if (memory_capacity / (1024ull) > 0) + printf("%.2fKbyte", memory_capacity / (1024.0)); + else + printf("%.2fbyte", memory_capacity * 1.0); + + printf(" (%lld bytes, %lld sectors, %d bytes each)\n", + memory_capacity, blocks, block_size); + } else { + int mult; + int blocknr; + int block_len; + unsigned long long blocks = 0; + int block_size = 0; + unsigned long long memory_capacity; + + printf("version: "); + switch (spec_vers) { + case 0x0: + printf("MMC v1.0-v1.2\n"); + break; + case 0x1: + printf("MMC v1.4\n"); + break; + case 0x2: + printf("MMC v2.0-v2.2\n"); + break; + case 0x3: + printf("MMC v3.1-v3.31\n"); + break; + case 0x4: + printf("MMC v4.0-v4.3\n"); + break; + default: + printf("reserved\n"); + break; + } + + printf("card classes: "); + if (ccc & 0x800) + printf("11, "); + if (ccc & 0x400) + printf("10, "); + if (ccc & 0x200) + printf("9, "); + if (ccc & 0x100) + printf("8, "); + if (ccc & 0x080) + printf("7, "); + if (ccc & 0x040) + printf("6, "); + if (ccc & 0x020) + printf("5, "); + if (ccc & 0x010) + printf("4, "); + if (ccc & 0x008) + printf("3, "); + if (ccc & 0x004) + printf("2, "); + if (ccc & 0x002) + printf("1, "); + if (ccc & 0x001) + printf("0, "); + printf("\b\b\n"); + + mult = 1 << (c_size_mult + 2); + blocknr = (c_size + 1) * mult; + block_len = 1 << read_bl_len; + blocks = blocknr; + block_size = block_len; + + memory_capacity = blocks * block_size; + + printf("capacity: "); + if (memory_capacity / (1024ull * 1024ull * 1024ull) > 0) + printf("%.2fGbyte", + memory_capacity / (1024.0 * 1024.0 * 1024.0)); + else if (memory_capacity / (1024ull * 1024ull) > 0) + printf("%.2fMbyte", memory_capacity / (1024.0 * 1024.0)); + else if (memory_capacity / (1024ull) > 0) + printf("%.2fKbyte", memory_capacity / (1024.0)); + else + printf("%.2fbyte", memory_capacity * 1.0); + printf(" (%lld bytes, %lld sectors, %d bytes each)\n", + memory_capacity, blocks, block_size); + } +} + +char *speed_class_speed(unsigned char id, bool ddr) +{ + if (ddr) { + switch (id) { + case 0x00: return "<4.8MB/s"; + case 0x08: return " 4.8MB/s"; + case 0x0a: return " 6.0MB/s"; + case 0x0f: return " 9.0MB/s"; + case 0x14: return "12.0MB/s"; + case 0x1e: return "18.0MB/s"; + case 0x28: return "24.0MB/s"; + case 0x32: return "30.0MB/s"; + case 0x3c: return "36.0MB/s"; + case 0x46: return "42.0MB/s"; + case 0x50: return "48.0MB/s"; + case 0x64: return "60.0MB/s"; + case 0x78: return "72.0MB/s"; + case 0x8c: return "84.0MB/s"; + case 0xa0: return "96.0MB/s"; + default: return "??.?MB/s"; + } + } else { + switch (id) { + case 0x00: return "<2.4MB/s"; + case 0x08: return " 2.4MB/s"; + case 0x0a: return " 3.0MB/s"; + case 0x0f: return " 4.5MB/s"; + case 0x14: return " 6.0MB/s"; + case 0x1e: return " 9.0MB/s"; + case 0x28: return "12.0MB/s"; + case 0x32: return "15.0MB/s"; + case 0x3c: return "18.0MB/s"; + case 0x46: return "21.0MB/s"; + case 0x50: return "24.0MB/s"; + case 0x64: return "30.0MB/s"; + case 0x78: return "36.0MB/s"; + case 0x8c: return "42.0MB/s"; + case 0xa0: return "48.0MB/s"; + default: return "??.?MB/s"; + } + } +} + +char speed_class_name(unsigned char id) +{ + switch (id) { + case 0x00: return '?'; + case 0x08: return 'A'; + case 0x0a: return 'B'; + case 0x0f: return 'C'; + case 0x14: return 'D'; + case 0x1e: return 'E'; + case 0x28: return 'F'; + case 0x32: return 'G'; + case 0x3c: return 'H'; + case 0x46: return 'J'; + case 0x50: return 'K'; + case 0x64: return 'M'; + case 0x78: return 'O'; + case 0x8c: return 'R'; + case 0xa0: return 'T'; + default: return '?'; + } +} + +char *power_class_consumption(unsigned int id, bool volt360) +{ + if (volt360) { + switch (id) { + case 0x0: return "100-200mA"; + case 0x1: return "120-220mA"; + case 0x2: return "150-250mA"; + case 0x3: return "180-280mA"; + case 0x4: return "200-300mA"; + case 0x5: return "220-320mA"; + case 0x6: return "250-350mA"; + case 0x7: return "300-400mA"; + case 0x8: return "350-450mA"; + case 0x9: return "400-500mA"; + case 0xa: return "450-550mA"; + default: return "reserved"; + } + } else { + switch (id) { + case 0x0: return "65-130mA"; + case 0x1: return "70-140mA"; + case 0x2: return "80-160mA"; + case 0x3: return "90-180mA"; + case 0x4: return "100-200mA"; + case 0x5: return "120-220mA"; + case 0x6: return "140-240mA"; + case 0x7: return "160-260mA"; + case 0x8: return "180-280mA"; + case 0x9: return "200-300mA"; + case 0xa: return "250-350mA"; + default: return "reserved"; + } + } +} + +char *sleep_consumption(unsigned int id) +{ + switch (id) { + case 0x00: return "not defined"; + case 0x01: return "2uA"; + case 0x02: return "4uA"; + case 0x03: return "8uA"; + case 0x04: return "16uA"; + case 0x05: return "32uA"; + case 0x06: return "64uA"; + case 0x07: return "128uA"; + case 0x08: return "0.256mA"; + case 0x09: return "0.512mA"; + case 0x0a: return "1.024mA"; + case 0x0b: return "2.048mA"; + case 0x0c: return "4.096mA"; + case 0x0d: return "8.192mA"; + default: return "reserved"; + } +} + +void print_mmc_ext_csd(struct config *config, char *ext_csd) +{ + unsigned int s_cmd_set; + unsigned int hpi_features; + unsigned int bkops_support; + unsigned int bkops_status; + unsigned int correctly_prg_sectors_num; + unsigned int ini_timeout_ap; + unsigned int pwr_cl_ddr_52_360; + unsigned int pwr_cl_ddr_52_195; + unsigned int min_perf_ddr_w_8_52; + unsigned int min_perf_ddr_r_8_52; + unsigned int trim_mult; + unsigned int sec_feature_support; + unsigned int sec_erase_mult; + unsigned int sec_trim_mult; + unsigned int boot_info; + unsigned int boot_size_mult; + unsigned int acc_size; + unsigned int hc_erase_grp_size; + unsigned int erase_timeout_mult; + unsigned int rel_wr_sec_c; + unsigned int hc_wp_grp_size; + unsigned int s_c_vcc; + unsigned int s_c_vccq; + unsigned int s_a_timeout; + unsigned int sec_count; + unsigned int min_perf_w_8_52; + unsigned int min_perf_r_8_52; + unsigned int min_perf_w_8_26_4_52; + unsigned int min_perf_r_8_26_4_52; + unsigned int min_perf_w_4_26; + unsigned int min_perf_r_4_26; + unsigned int pwr_cl_26_360; + unsigned int pwr_cl_52_360; + unsigned int pwr_cl_26_195; + unsigned int pwr_cl_52_195; + unsigned int partition_switch_time; + unsigned int out_of_interrupt_time; + unsigned int card_type; + unsigned int csd_structure; + unsigned int ext_csd_rev; + unsigned int cmd_set; + unsigned int cmd_set_rev; + unsigned int power_class; + unsigned int hs_timing; + unsigned int bus_width; + unsigned int erased_mem_cont; + unsigned int partition_config; + unsigned int boot_config_prot; + unsigned int boot_bus_width; + unsigned int erase_group_def; + unsigned int boot_wp; + unsigned int user_wp; + unsigned int fw_config; + unsigned int rpmb_size_mult; + unsigned int wr_rel_set; + unsigned int wr_rel_param; + unsigned int bkops_start; + unsigned int bkops_en; + unsigned int rst_n_function; + unsigned int hpi_mgmt; + unsigned int partitioning_support; + unsigned int max_enh_size_mult; + unsigned int partitions_attribute; + unsigned int partition_setting_completed; + unsigned int gp_size_mult_gp0; + unsigned int gp_size_mult_gp1; + unsigned int gp_size_mult_gp2; + unsigned int gp_size_mult_gp3; + unsigned int enh_size_mult; + unsigned int enh_start_addr; + unsigned int sec_bad_blk_mgmnt; + + parse_bin(ext_csd, "56r8u8u8u2040r8u32u8u8r8u8u16r8u8u8r8u8u8u8u8u8r" + "8u8u8u8u8u8u8u8u8r8u8r32u8r8u8u8u8u8u8u8r8u8u8u8u8u8u8r8u8r" + "8u8r8u8u8r8u8r8u8r8u8r8u8r8u8r8u8u8u8r8u8r8u8r8u8r8u8u8u8u8r" + "8u8u8u8u8u24u8u8u12u12u12u12u24u32u8r8u1072r", + &s_cmd_set, &hpi_features, &bkops_support, + &bkops_status, &correctly_prg_sectors_num, + &ini_timeout_ap, &pwr_cl_ddr_52_360, &pwr_cl_ddr_52_195, + &min_perf_ddr_w_8_52, &min_perf_ddr_r_8_52, + &trim_mult, &sec_feature_support, &sec_erase_mult, + &sec_trim_mult, &boot_info, &boot_size_mult, &acc_size, + &hc_erase_grp_size, &erase_timeout_mult, &rel_wr_sec_c, + &hc_wp_grp_size, &s_c_vcc, &s_c_vccq, &s_a_timeout, &sec_count, + &min_perf_w_8_52, &min_perf_r_8_52, &min_perf_w_8_26_4_52, + &min_perf_r_8_26_4_52, &min_perf_w_4_26, &min_perf_r_4_26, + &pwr_cl_26_360, &pwr_cl_52_360, &pwr_cl_26_195, &pwr_cl_52_195, + &partition_switch_time, &out_of_interrupt_time, &card_type, + &csd_structure, &ext_csd_rev, &cmd_set, &cmd_set_rev, + &power_class, &hs_timing, &bus_width, &erased_mem_cont, + &partition_config, &boot_config_prot, &boot_bus_width, + &erase_group_def, &boot_wp, &user_wp, &fw_config, + &rpmb_size_mult, &wr_rel_set, &wr_rel_param, &bkops_start, + &bkops_en, &rst_n_function, &hpi_mgmt, &partitioning_support, + &max_enh_size_mult, &partitions_attribute, + &partition_setting_completed, &gp_size_mult_gp0, + &gp_size_mult_gp1, &gp_size_mult_gp2, &gp_size_mult_gp3, + &enh_size_mult, &enh_start_addr, &sec_bad_blk_mgmnt); + + if (config->verbose) { + printf("======MMC/EXT_CSD======\n"); + + printf("\tS_CMD_SET: 0x%02x (", s_cmd_set); + if (s_cmd_set & 0x4) + printf("Content Protection SecureMMC, "); + if (s_cmd_set & 0x2) + printf("SecureMMC, "); + if (s_cmd_set & 0x1) + printf("Standard MMC, "); + printf(")\n"); + + printf("\tHPI_FEATURES: 0x%02x (", hpi_features); + if (hpi_features & 0x1) + printf("HPI based on CMD%d", + hpi_features & 0x2 ? 12 : 13); + printf(")\n"); + + printf("\tBKOPS_SUPPORT: 0x%02x (", bkops_support); + printf("background operations%s supported)\n", + bkops_support & 0x1 ? "" : "not "); + + printf("\tBKOPS_STATUS: 0x%02x (", bkops_status); + switch (bkops_status & 0x3) { + case 0x0: + printf("no ops required)\n"); + break; + case 0x1: + printf("ops outstanding (non-critical))\n"); + break; + case 0x2: + printf("ops outstanding (performance impacted))\n"); + break; + case 0x3: + printf("ops outstanding (critical))\n"); + break; + } + + printf("\tCORRECTLY_PRG_SECTORS_NUM: 0x%02x %d\n", + correctly_prg_sectors_num, correctly_prg_sectors_num); + printf("\tINI_TIMEOUT_PA: 0x%02x %dms\n", + ini_timeout_ap, 100 * ini_timeout_ap); + printf("\tTRIM_MULT: 0x%02x %dms\n", + trim_mult, 300 * trim_mult); + + printf("\tSEC_FEATURE_SUPPORT: 0x%02x (", sec_feature_support); + if (sec_feature_support & 0x10) + printf("secure/insecure trim supported, "); + if (sec_feature_support & 0x4) + printf("secure purge on defective portions supported, "); + if (sec_feature_support & 0x1) + printf("secure purge supported, "); + printf(")\n"); + + printf("\tSEC_ERASE_MULT: 0x%02x %dms\n", sec_erase_mult, + 300 * erase_timeout_mult * sec_erase_mult); + printf("\tSEC_TRIM_MULT: 0x%02x %dms\n", sec_trim_mult, + 300 * erase_timeout_mult * sec_trim_mult); + + printf("\tBOOT_INFO: 0x%02x (", boot_info); + if (boot_info & 0x4) + printf("high speed timing during boot supported, "); + if (boot_info & 0x2) + printf("alternate dual data rate during boot supported, "); + if (boot_info & 0x1) + printf("alternate boot supported, "); + printf(")\n"); + + printf("\tBOOT_SIZE_MULT: 0x%02x %dKbytes\n", boot_size_mult, + 128 * boot_size_mult); + + printf("\tACC_SIZE_MULT: 0x%02x (", acc_size); + switch (acc_size & 0xf) { + case 0x0: + printf("not defined)\n"); + break; + case 0x1: + printf("512 bytes)\n"); + break; + case 0x2: + printf("1Kbytes)\n"); + break; + case 0x3: + printf("2Kbytes)\n"); + break; + case 0x4: + printf("4Kbytes)\n"); + break; + case 0x5: + printf("8Kbytes)\n"); + break; + case 0x6: + printf("16Kbytes)\n"); + break; + case 0x7: + printf("32Kbytes)\n"); + break; + case 0x8: + printf("64Kbytes)\n"); + break; + default: + printf("reserved)\n"); + break; + } + + printf("\tHC_ERASE_GRP_SIZE: 0x%02x ", hc_erase_grp_size); + if (hc_erase_grp_size == 0x00) + printf("(no high-capacity erase-unit size support)\n"); + else + printf("(%0.1fMbyte high-capcity erase-unit size)\n", + (512 * 1024 * hc_erase_grp_size) / 1048576.0f); + + printf("\tERASE_TIMEOUT_MULT: 0x%02x %dms\n", + erase_timeout_mult, 300 * erase_timeout_mult); + printf("\tREL_WR_SEC_C: 0x%02x %dsectors\n", + rel_wr_sec_c, rel_wr_sec_c); + printf("\tHC_WP_GRP_SIZE: 0x%02x %dKbyte\n", hc_wp_grp_size, + 512 * hc_erase_grp_size * hc_wp_grp_size); + + printf("\tS_C_VCC: 0x%02x (", s_c_vcc); + switch (s_c_vcc) { + case 0x00: + printf("not defined)\n"); + break; + case 0x01: + printf("2uA)\n"); + break; + case 0x02: + printf("4uA)\n"); + break; + case 0x03: + printf("8uA)\n"); + break; + case 0x04: + printf("16uA)\n"); + break; + case 0x05: + printf("32uA)\n"); + break; + case 0x06: + printf("64uA)\n"); + break; + case 0x07: + printf("128uA)\n"); + break; + case 0x08: + printf("0.256mA)\n"); + break; + case 0x09: + printf("0.512mA)\n"); + break; + case 0x0a: + printf("1.024mA)\n"); + break; + case 0x0b: + printf("2.048mA)\n"); + break; + case 0x0c: + printf("4.096mA)\n"); + break; + case 0x0d: + printf("8.192mA)\n"); + break; + default: + printf("reserved)\n"); + break; + } + + printf("\tS_C_VCCQ: 0x%02x (", s_c_vccq); + switch (s_c_vccq) { + case 0x00: + printf("not defined)\n"); + break; + case 0x01: + printf("2uA)\n"); + break; + case 0x02: + printf("4uA)\n"); + break; + case 0x03: + printf("8uA)\n"); + break; + case 0x04: + printf("16uA)\n"); + break; + case 0x05: + printf("32uA)\n"); + break; + case 0x06: + printf("64uA)\n"); + break; + case 0x07: + printf("128uA)\n"); + break; + case 0x08: + printf("0.256mA)\n"); + break; + case 0x09: + printf("0.512mA)\n"); + break; + case 0x0a: + printf("1.024mA)\n"); + break; + case 0x0b: + printf("2.048mA)\n"); + break; + case 0x0c: + printf("4.096mA)\n"); + break; + case 0x0d: + printf("8.192mA)\n"); + break; + default: + printf("reserved)\n"); + break; + } + + printf("\tS_A_TIMEOUT: 0x%02x %dns", s_a_timeout, + 100 * 1 << s_a_timeout); + printf("\tSEC_COUNT: 0x%02x (%u sectors, 512bytes each, " + "%lubytes in total)\n", sec_count, sec_count, + sec_count * 512ul); + + printf("\tMIN_PERF_DDR_W_8_52: 0x%02x (", min_perf_ddr_w_8_52); + switch (min_perf_ddr_w_8_52) { + case 0x00: + printf("card doesn't reach 4.8MB/s)\n"); + break; + case 0x08: + printf("class A: 4.8MB/s)\n"); + break; + case 0x0a: + printf("class B: 6.0MB/s)\n"); + break; + case 0x0f: + printf("class C: 9.0MB/s)\n"); + break; + case 0x14: + printf("class D: 12.0MB/s)\n"); + break; + case 0x1e: + printf("class E: 18.0MB/s)\n"); + break; + case 0x28: + printf("class F: 24.0MB/s)\n"); + break; + case 0x32: + printf("class G: 30.0MB/s)\n"); + break; + case 0x3c: + printf("class H: 36.0MB/s)\n"); + break; + case 0x46: + printf("class J: 42.0MB/s)\n"); + break; + case 0x50: + printf("class K: 48.0MB/s)\n"); + break; + case 0x64: + printf("class M: 60.0MB/s)\n"); + break; + case 0x78: + printf("class O: 72.0MB/s)\n"); + break; + case 0x8c: + printf("class R: 84.0MB/s)\n"); + break; + case 0xa0: + printf("class T: 96.0MB/s)\n"); + break; + } + + printf("\tMIN_PERF_DDR_R_8_52: 0x%02x (", min_perf_ddr_r_8_52); + switch (min_perf_ddr_r_8_52) { + case 0x00: + printf("card doesn't reach 4.8MB/s)\n"); + break; + case 0x08: + printf("class A: 4.8MB/s)\n"); + break; + case 0x0a: + printf("class B: 6.0MB/s)\n"); + break; + case 0x0f: + printf("class C: 9.0MB/s)\n"); + break; + case 0x14: + printf("class D: 12.0MB/s)\n"); + break; + case 0x1e: + printf("class E: 18.0MB/s)\n"); + break; + case 0x28: + printf("class F: 24.0MB/s)\n"); + break; + case 0x32: + printf("class G: 30.0MB/s)\n"); + break; + case 0x3c: + printf("class H: 36.0MB/s)\n"); + break; + case 0x46: + printf("class J: 42.0MB/s)\n"); + break; + case 0x50: + printf("class K: 48.0MB/s)\n"); + break; + case 0x64: + printf("class M: 60.0MB/s)\n"); + break; + case 0x78: + printf("class O: 72.0MB/s)\n"); + break; + case 0x8c: + printf("class R: 84.0MB/s)\n"); + break; + case 0xa0: + printf("class T: 96.0MB/s)\n"); + break; + } + + printf("\tMIN_PERF_W_8_52: 0x%02x (", min_perf_w_8_52); + switch (min_perf_w_8_52) { + case 0x00: + printf("card doesn't reach 2.4MB/s)\n"); + break; + case 0x08: + printf("class A: 2.4MB/s)\n"); + break; + case 0x0a: + printf("class B: 3.0MB/s)\n"); + break; + case 0x0f: + printf("class C: 4.5MB/s)\n"); + break; + case 0x14: + printf("class D: 6.0MB/s)\n"); + break; + case 0x1e: + printf("class E: 9.0MB/s)\n"); + break; + case 0x28: + printf("class F: 12.0MB/s)\n"); + break; + case 0x32: + printf("class G: 15.0MB/s)\n"); + break; + case 0x3c: + printf("class H: 18.0MB/s)\n"); + break; + case 0x46: + printf("class J: 21.0MB/s)\n"); + break; + case 0x50: + printf("class K: 24.0MB/s)\n"); + break; + case 0x64: + printf("class M: 30.0MB/s)\n"); + break; + case 0x78: + printf("class O: 36.0MB/s)\n"); + break; + case 0x8c: + printf("class R: 42.0MB/s)\n"); + break; + case 0xa0: + printf("class T: 48.0MB/s)\n"); + break; + } + + printf("\tMIN_PERF_R_8_52: 0x%02x (", min_perf_r_8_52); + switch (min_perf_r_8_52) { + case 0x00: + printf("card doesn't reach 2.4MB/s)\n"); + break; + case 0x08: + printf("class A: 2.4MB/s)\n"); + break; + case 0x0a: + printf("class B: 3.0MB/s)\n"); + break; + case 0x0f: + printf("class C: 4.5MB/s)\n"); + break; + case 0x14: + printf("class D: 6.0MB/s)\n"); + break; + case 0x1e: + printf("class E: 9.0MB/s)\n"); + break; + case 0x28: + printf("class F: 12.0MB/s)\n"); + break; + case 0x32: + printf("class G: 15.0MB/s)\n"); + break; + case 0x3c: + printf("class H: 18.0MB/s)\n"); + break; + case 0x46: + printf("class J: 21.0MB/s)\n"); + break; + case 0x50: + printf("class K: 24.0MB/s)\n"); + break; + case 0x64: + printf("class M: 30.0MB/s)\n"); + break; + case 0x78: + printf("class O: 36.0MB/s)\n"); + break; + case 0x8c: + printf("class R: 42.0MB/s)\n"); + break; + case 0xa0: + printf("class T: 48.0MB/s)\n"); + break; + } + + printf("\tMIN_PERF_W_8_26_4_52: 0x%02x (", + min_perf_w_8_26_4_52); + switch (min_perf_w_8_26_4_52) { + case 0x00: + printf("card doesn't reach 2.4MB/s)\n"); + break; + case 0x08: + printf("class A: 2.4MB/s)\n"); + break; + case 0x0a: + printf("class B: 3.0MB/s)\n"); + break; + case 0x0f: + printf("class C: 4.5MB/s)\n"); + break; + case 0x14: + printf("class D: 6.0MB/s)\n"); + break; + case 0x1e: + printf("class E: 9.0MB/s)\n"); + break; + case 0x28: + printf("class F: 12.0MB/s)\n"); + break; + case 0x32: + printf("class G: 15.0MB/s)\n"); + break; + case 0x3c: + printf("class H: 18.0MB/s)\n"); + break; + case 0x46: + printf("class J: 21.0MB/s)\n"); + break; + case 0x50: + printf("class K: 24.0MB/s)\n"); + break; + case 0x64: + printf("class M: 30.0MB/s)\n"); + break; + case 0x78: + printf("class O: 36.0MB/s)\n"); + break; + case 0x8c: + printf("class R: 42.0MB/s)\n"); + break; + case 0xa0: + printf("class T: 48.0MB/s)\n"); + break; + } + + printf("\tMIN_PERF_R_8_26_4_52: 0x%02x (", + min_perf_r_8_26_4_52); + switch (min_perf_r_8_26_4_52) { + case 0x00: + printf("card doesn't reach 2.4MB/s)\n"); + break; + case 0x08: + printf("class A: 2.4MB/s)\n"); + break; + case 0x0a: + printf("class B: 3.0MB/s)\n"); + break; + case 0x0f: + printf("class C: 4.5MB/s)\n"); + break; + case 0x14: + printf("class D: 6.0MB/s)\n"); + break; + case 0x1e: + printf("class E: 9.0MB/s)\n"); + break; + case 0x28: + printf("class F: 12.0MB/s)\n"); + break; + case 0x32: + printf("class G: 15.0MB/s)\n"); + break; + case 0x3c: + printf("class H: 18.0MB/s)\n"); + break; + case 0x46: + printf("class J: 21.0MB/s)\n"); + break; + case 0x50: + printf("class K: 24.0MB/s)\n"); + break; + case 0x64: + printf("class M: 30.0MB/s)\n"); + break; + case 0x78: + printf("class O: 36.0MB/s)\n"); + break; + case 0x8c: + printf("class R: 42.0MB/s)\n"); + break; + case 0xa0: + printf("class T: 48.0MB/s)\n"); + break; + } + + printf("\tMIN_PERF_W_4_26: 0x%02x (", min_perf_w_4_26); + switch (min_perf_w_4_26) { + case 0x00: + printf("card doesn't reach 2.4MB/s)\n"); + break; + case 0x08: + printf("class A: 2.4MB/s)\n"); + break; + case 0x0a: + printf("class B: 3.0MB/s)\n"); + break; + case 0x0f: + printf("class C: 4.5MB/s)\n"); + break; + case 0x14: + printf("class D: 6.0MB/s)\n"); + break; + case 0x1e: + printf("class E: 9.0MB/s)\n"); + break; + case 0x28: + printf("class F: 12.0MB/s)\n"); + break; + case 0x32: + printf("class G: 15.0MB/s)\n"); + break; + case 0x3c: + printf("class H: 18.0MB/s)\n"); + break; + case 0x46: + printf("class J: 21.0MB/s)\n"); + break; + case 0x50: + printf("class K: 24.0MB/s)\n"); + break; + case 0x64: + printf("class M: 30.0MB/s)\n"); + break; + case 0x78: + printf("class O: 36.0MB/s)\n"); + break; + case 0x8c: + printf("class R: 42.0MB/s)\n"); + break; + case 0xa0: + printf("class T: 48.0MB/s)\n"); + break; + } + + printf("\tMIN_PERF_R_4_26: 0x%02x (", min_perf_r_4_26); + switch (min_perf_r_4_26) { + case 0x00: + printf("card doesn't reach 2.4MB/s)\n"); + break; + case 0x08: + printf("class A: 2.4MB/s)\n"); + break; + case 0x0a: + printf("class B: 3.0MB/s)\n"); + break; + case 0x0f: + printf("class C: 4.5MB/s)\n"); + break; + case 0x14: + printf("class D: 6.0MB/s)\n"); + break; + case 0x1e: + printf("class E: 9.0MB/s)\n"); + break; + case 0x28: + printf("class F: 12.0MB/s)\n"); + break; + case 0x32: + printf("class G: 15.0MB/s)\n"); + break; + case 0x3c: + printf("class H: 18.0MB/s)\n"); + break; + case 0x46: + printf("class J: 21.0MB/s)\n"); + break; + case 0x50: + printf("class K: 24.0MB/s)\n"); + break; + case 0x64: + printf("class M: 30.0MB/s)\n"); + break; + case 0x78: + printf("class O: 36.0MB/s)\n"); + break; + case 0x8c: + printf("class R: 42.0MB/s)\n"); + break; + case 0xa0: + printf("class T: 48.0MB/s)\n"); + break; + } + + printf("\tPWR_CL_DDR_52_360: 0x%02x (8bit -> ", + pwr_cl_ddr_52_360); + switch ((pwr_cl_ddr_52_360 & 0xf0) >> 0) { + case 0x0: + printf("100-200mA, "); + break; + case 0x1: + printf("120-220mA, "); + break; + case 0x2: + printf("150-250mA, "); + break; + case 0x3: + printf("180-280mA, "); + break; + case 0x4: + printf("200-300mA, "); + break; + case 0x5: + printf("220-320mA, "); + break; + case 0x6: + printf("250-350mA, "); + break; + case 0x7: + printf("300-400mA, "); + break; + case 0x8: + printf("350-450mA, "); + break; + case 0x9: + printf("400-500mA, "); + break; + case 0xa: + printf("450-550mA, "); + break; + default: + printf("reserved, "); + break; + } + + printf("4bit -> "); + switch (pwr_cl_ddr_52_360 & 0x0f) { + case 0x0: + printf("100-200mA)\n"); + break; + case 0x1: + printf("120-220mA)\n"); + break; + case 0x2: + printf("150-250mA)\n"); + break; + case 0x3: + printf("180-280mA)\n"); + break; + case 0x4: + printf("200-300mA)\n"); + break; + case 0x5: + printf("220-320mA)\n"); + break; + case 0x6: + printf("250-350mA)\n"); + break; + case 0x7: + printf("300-400mA)\n"); + break; + case 0x8: + printf("350-450mA)\n"); + break; + case 0x9: + printf("400-500mA)\n"); + break; + case 0xa: + printf("450-550mA)\n"); + break; + default: + printf("reserved)\n"); + break; + } + + printf("\tPWR_CL_DDR_52_195: 0x%02x (8bit -> ", + pwr_cl_ddr_52_195); + switch ((pwr_cl_ddr_52_195 & 0xf0) >> 0) { + case 0x0: + printf("65-130mA, "); + break; + case 0x1: + printf("70-140mA, "); + break; + case 0x2: + printf("80-160mA, "); + break; + case 0x3: + printf("90-180mA, "); + break; + case 0x4: + printf("100-200mA, "); + break; + case 0x5: + printf("120-220mA, "); + break; + case 0x6: + printf("140-240mA, "); + break; + case 0x7: + printf("160-260mA, "); + break; + case 0x8: + printf("180-280mA, "); + break; + case 0x9: + printf("200-300mA, "); + break; + case 0xa: + printf("250-350mA, "); + break; + default: + printf("reserved, "); + break; + } + printf("4bit -> "); + switch (pwr_cl_ddr_52_195 & 0x0f) { + case 0x0: + printf("65-130mA)\n"); + break; + case 0x1: + printf("70-140mA)\n"); + break; + case 0x2: + printf("80-160mA)\n"); + break; + case 0x3: + printf("90-180mA)\n"); + break; + case 0x4: + printf("100-200mA)\n"); + break; + case 0x5: + printf("120-220mA)\n"); + break; + case 0x6: + printf("140-240mA)\n"); + break; + case 0x7: + printf("160-260mA)\n"); + break; + case 0x8: + printf("180-280mA)\n"); + break; + case 0x9: + printf("200-300mA)\n"); + break; + case 0xa: + printf("250-350mA)\n"); + break; + default: + printf("reserved)\n"); + break; + } + + printf("\tPWR_CL_26_360: 0x%02x (8bit -> ", pwr_cl_26_360); + switch ((pwr_cl_26_360 & 0xf0) >> 0) { + case 0x0: + printf("100-200mA, "); + break; + case 0x1: + printf("120-220mA, "); + break; + case 0x2: + printf("150-250mA, "); + break; + case 0x3: + printf("180-280mA, "); + break; + case 0x4: + printf("200-300mA, "); + break; + case 0x5: + printf("220-320mA, "); + break; + case 0x6: + printf("250-350mA, "); + break; + case 0x7: + printf("300-400mA, "); + break; + case 0x8: + printf("350-450mA, "); + break; + case 0x9: + printf("400-500mA, "); + break; + case 0xa: + printf("450-550mA, "); + break; + default: + printf("reserved, "); + break; + } + printf("4bit -> "); + switch (pwr_cl_26_360 & 0x0f) { + case 0x0: + printf("100-200mA)\n"); + break; + case 0x1: + printf("120-220mA)\n"); + break; + case 0x2: + printf("150-250mA)\n"); + break; + case 0x3: + printf("180-280mA)\n"); + break; + case 0x4: + printf("200-300mA)\n"); + break; + case 0x5: + printf("220-320mA)\n"); + break; + case 0x6: + printf("250-350mA)\n"); + break; + case 0x7: + printf("300-400mA)\n"); + break; + case 0x8: + printf("350-450mA)\n"); + break; + case 0x9: + printf("400-500mA)\n"); + break; + case 0xa: + printf("450-550mA)\n"); + break; + default: + printf("reserved)\n"); + break; + } + + printf("\tPWR_CL_52_360: 0x%02x (8bit -> ", pwr_cl_52_360); + switch ((pwr_cl_52_360 & 0xf0) >> 0) { + case 0x0: + printf("100-200mA, "); + break; + case 0x1: + printf("120-220mA, "); + break; + case 0x2: + printf("150-250mA, "); + break; + case 0x3: + printf("180-280mA, "); + break; + case 0x4: + printf("200-300mA, "); + break; + case 0x5: + printf("220-320mA, "); + break; + case 0x6: + printf("250-350mA, "); + break; + case 0x7: + printf("300-400mA, "); + break; + case 0x8: + printf("350-450mA, "); + break; + case 0x9: + printf("400-500mA, "); + break; + case 0xa: + printf("450-550mA, "); + break; + default: + printf("reserved, "); + break; + } + printf("4bit -> "); + switch (pwr_cl_52_360 & 0x0f) { + case 0x0: + printf("100-200mA)\n"); + break; + case 0x1: + printf("120-220mA)\n"); + break; + case 0x2: + printf("150-250mA)\n"); + break; + case 0x3: + printf("180-280mA)\n"); + break; + case 0x4: + printf("200-300mA)\n"); + break; + case 0x5: + printf("220-320mA)\n"); + break; + case 0x6: + printf("250-350mA)\n"); + break; + case 0x7: + printf("300-400mA)\n"); + break; + case 0x8: + printf("350-450mA)\n"); + break; + case 0x9: + printf("400-500mA)\n"); + break; + case 0xa: + printf("450-550mA)\n"); + break; + default: + printf("reserved)\n"); + break; + } + + printf("\tPWR_CL_26_195: 0x%02x (8bit -> ", pwr_cl_26_195); + switch ((pwr_cl_26_195 & 0xf0) >> 0) { + case 0x0: + printf("65-130mA, "); + break; + case 0x1: + printf("70-140mA, "); + break; + case 0x2: + printf("80-160mA, "); + break; + case 0x3: + printf("90-180mA, "); + break; + case 0x4: + printf("100-200mA, "); + break; + case 0x5: + printf("120-220mA, "); + break; + case 0x6: + printf("140-240mA, "); + break; + case 0x7: + printf("160-260mA, "); + break; + case 0x8: + printf("180-280mA, "); + break; + case 0x9: + printf("200-300mA, "); + break; + case 0xa: + printf("250-350mA, "); + break; + default: + printf("reserved, "); + break; + } + printf("4bit -> "); + switch (pwr_cl_26_195 & 0x0f) { + case 0x0: + printf("65-130mA)\n"); + break; + case 0x1: + printf("70-140mA)\n"); + break; + case 0x2: + printf("80-160mA)\n"); + break; + case 0x3: + printf("90-180mA)\n"); + break; + case 0x4: + printf("100-200mA)\n"); + break; + case 0x5: + printf("120-220mA)\n"); + break; + case 0x6: + printf("140-240mA)\n"); + break; + case 0x7: + printf("160-260mA)\n"); + break; + case 0x8: + printf("180-280mA)\n"); + break; + case 0x9: + printf("200-300mA)\n"); + break; + case 0xa: + printf("250-350mA)\n"); + break; + default: + printf("reserved)\n"); + break; + } + + printf("\tPWR_CL_52_195: 0x%02x (8bit -> ", pwr_cl_52_195); + switch ((pwr_cl_52_195 & 0xf0) >> 0) { + case 0x0: + printf("65-130mA, "); + break; + case 0x1: + printf("70-140mA, "); + break; + case 0x2: + printf("80-160mA, "); + break; + case 0x3: + printf("90-180mA, "); + break; + case 0x4: + printf("100-200mA, "); + break; + case 0x5: + printf("120-220mA, "); + break; + case 0x6: + printf("140-240mA, "); + break; + case 0x7: + printf("160-260mA, "); + break; + case 0x8: + printf("180-280mA, "); + break; + case 0x9: + printf("200-300mA, "); + break; + case 0xa: + printf("250-350mA, "); + break; + default: + printf("reserved, "); + break; + } + printf("4bit -> "); + switch (pwr_cl_52_195 & 0x0f) { + case 0x0: + printf("65-130mA)\n"); + break; + case 0x1: + printf("70-140mA)\n"); + break; + case 0x2: + printf("80-160mA)\n"); + break; + case 0x3: + printf("90-180mA)\n"); + break; + case 0x4: + printf("100-200mA)\n"); + break; + case 0x5: + printf("120-220mA)\n"); + break; + case 0x6: + printf("140-240mA)\n"); + break; + case 0x7: + printf("160-260mA)\n"); + break; + case 0x8: + printf("180-280mA)\n"); + break; + case 0x9: + printf("200-300mA)\n"); + break; + case 0xa: + printf("250-350mA)\n"); + break; + default: + printf("reserved)\n"); + break; + } + + printf("\tPARTITION_SWITCH_TIME: 0x%02x %dms\n", + partition_switch_time, 10 * partition_switch_time); + + printf("\tOUT_OF_INTERRUPT_TIME: 0x%02x %dms\n", + out_of_interrupt_time, 10 * out_of_interrupt_time); + + printf("\tCARD_TYPE: 0x%02x (", card_type); + if (card_type & 0x8) + printf("High-speed DDR MMC @ 52Mhz 1.2V I/O, "); + if (card_type & 0x4) + printf("High-speed DDR MMC @ 52Mhz 1.8V/3V I/O, "); + if (card_type & 0x2) + printf("High-speed MMC @ 52Mhz, "); + if (card_type & 0x1) + printf("High-speed MMC @ 26Mhz, "); + printf(")\n"); + + printf("\tCSD_STRUCTURE: 0x%02x (", csd_structure); + switch (csd_structure) { + case 0x0: + printf("v1.0)\n"); + break; + case 0x1: + printf("v1.1)\n"); + break; + case 0x2: + printf("v1.2)\n"); + break; + case 0x3: + printf("v1.3)\n"); + break; + default: + printf("reserved)\n"); + break; + } + + printf("\tEXT_CSD_REV: 0x%02x (", ext_csd_rev); + switch (ext_csd_rev) { + case 0x0: + printf("rev 1.0 for MMC v4.0)\n"); + break; + case 0x1: + printf("rev 1.1 for MMC v4.1)\n"); + break; + case 0x2: + printf("rev 1.2 for MMC v4.2)\n"); + break; + case 0x3: + printf("rev 1.3 for MMC v4.3)\n"); + break; + case 0x4: + printf("rev 1.4 for MMC v4.4)\n"); + break; + case 0x5: + printf("rev 1.5 for MMC v4.41)\n"); + break; + default: + printf("reserved)\n"); + break; + } + + printf("\tCMD_SET: 0x%02x\n", cmd_set); + printf("\tCMD_SET_REV: 0x%02x (", cmd_set_rev); + if (cmd_set_rev == 0x00) + printf("MMC v4.0 command set)\n"); + else + printf("reserved)\n"); + + printf("\tPOWER_CLASS: 0x%02x (", power_class); + switch (power_class & 0xf) { + case 0x0: + printf("65-130mA or "); + break; + case 0x1: + printf("70-140mA or "); + break; + case 0x2: + printf("80-160mA or "); + break; + case 0x3: + printf("90-180mA or "); + break; + case 0x4: + printf("100-200mA or "); + break; + case 0x5: + printf("120-220mA or "); + break; + case 0x6: + printf("140-240mA or "); + break; + case 0x7: + printf("160-260mA or "); + break; + case 0x8: + printf("180-280mA or "); + break; + case 0x9: + printf("200-300mA or "); + break; + case 0xa: + printf("250-350mA or "); + break; + default: + printf("reserved or "); + break; + } + + switch (power_class & 0xf) { + case 0x0: + printf("100-200mA)\n"); + break; + case 0x1: + printf("120-220mA)\n"); + break; + case 0x2: + printf("150-250mA)\n"); + break; + case 0x3: + printf("180-280mA)\n"); + break; + case 0x4: + printf("200-300mA)\n"); + break; + case 0x5: + printf("220-320mA)\n"); + break; + case 0x6: + printf("250-350mA)\n"); + break; + case 0x7: + printf("300-400mA)\n"); + break; + case 0x8: + printf("350-450mA)\n"); + break; + case 0x9: + printf("400-500mA)\n"); + break; + case 0xa: + printf("450-550mA)\n"); + break; + default: + printf("reserved)\n"); + break; + } + + printf("\tHS_TIMING: 0x%02x\n", hs_timing); + + printf("\tBUS_WIDTH: 0x%02x (", bus_width); + switch (bus_width) { + case 0x0: + printf("8bit data bus DDR)\n"); + break; + case 0x1: + printf("4bit data bus DDR)\n"); + break; + case 0x2: + printf("8bit data bus)\n"); + break; + case 0x5: + printf("4bit data bus)\n"); + break; + case 0x6: + printf("1bit data bus)\n"); + break; + default: + printf("reserved)\n"); + break; + } + + printf("\tERASED_MEM_CONT: 0x%02x (", erased_mem_cont); + if (erased_mem_cont & 0x01) + printf("erased is 1)\n"); + else + printf("erased is 0)\n"); + + + printf("\tPARTITION_CONFIG: 0x%02x (", partition_config); + if (partition_config & 0x40) + printf("boot acknowledge during boot, "); + switch ((partition_config & 0x38) >> 3) { + case 0x0: + printf("device boot not enabled, "); + break; + case 0x1: + printf("boot partition 1 enabled for boot, "); + break; + case 0x2: + printf("boot partition 2 enabled for boot, "); + break; + case 0x7: + printf("user area enabled for boot, "); + break; + default: + printf("reserved, "); + break; + } + + switch (partition_config & 0x3) { + case 0x0: + printf("no access to boot partition, "); + break; + case 0x1: + printf("R/W boot partition 1, "); + break; + case 0x2: + printf("R/W boot partition 2, "); + break; + case 0x3: + printf("R/W RPMB, "); + break; + case 0x4: + printf("general partition 1 access, "); + break; + case 0x5: + printf("general partition 2 access, "); + break; + case 0x6: + printf("general partition 3 access, "); + break; + case 0x7: + printf("general partition 4 access, "); + break; + } + + printf("\tBOOT_CONFIG_PROT: 0x%02x (", boot_config_prot); + if (boot_config_prot & 0x10) + printf("boot configuration register bits locked, "); + if (boot_config_prot & 0x01) + printf("boot configuration regsiter bits temporarily locked, "); + printf(")\n"); + + printf("\tBOOT_BUS_WIDTH: 0x%02x (", boot_bus_width); + switch ((boot_bus_width & 0x18) >> 3) { + case 0x0: + printf("SDR + normal timing in boot, "); + break; + case 0x1: + printf("SDR + high-speed timing in boot, "); + break; + case 0x2: + printf("DDR in boot, "); + break; + default: + printf("reserved, "); + break; + } + + if (boot_bus_width & 0x4) + printf("retain bus width/boot mode after boot, "); + else + printf("reset to 1bit SDR normal timing after boot, "); + switch (boot_bus_width & 0x3) { + case 0x0: + printf("1bit SDR or 4bit DDR during boot)\n"); + break; + case 0x1: + printf("4bit SDR/DDR during boot)\n"); + break; + case 0x2: + printf("8bit SDR/DDR during boot)\n"); + break; + case 0x3: + printf("reserved)\n"); + break; + } + + printf("\tERASE_GROUP_DEF: 0x%02x (", erase_group_def); + if (erase_group_def & 0x1) + printf("Use high-capacity erase unit/erase " + "timeout/write protect group)\n"); + else + printf("Use normal erase unit/erase timeout/write " + "protect group)\n"); + + printf("\tBOOT_WP: 0x%02x (", boot_wp); + if (boot_wp & 0x40) + printf("allow power-on protection in boot area, "); + if (boot_wp & 0x10) + printf("allow boot area write protection, "); + if (boot_wp & 0x4) + printf("boot area permanently write protected, "); + if (boot_wp & 0x1) + printf("power-on boot area write protection, "); + printf(")\n"); + + printf("\tUSER_WP: 0x%02x (", user_wp); + if (user_wp & 0x80) + printf("password protection features disabled, "); + if (user_wp & 0x40) + printf("disable permanent write protection, "); + if (user_wp & 0x10) + printf("write protection group write protection disabled, "); + if (user_wp & 0x8) + printf("power-on period write protection group write " + "proection disabled, "); + if (user_wp & 0x4) + printf("apply permanent write protection to protection " + "group, "); + if (user_wp & 0x1) + printf("apply power-on period write protection to " + "protection group, "); + printf(")\n"); + + printf("\tFW_CONFIG: 0x%02x (", fw_config); + if (fw_config & 0x1) + printf("FW upgrade disabled"); + printf(")\n"); + + printf("\tRPMB_SIZE_MULT: 0x%02x %dKbyte\n", + rpmb_size_mult, 128 * rpmb_size_mult); + + printf("\tWR_REL_SET: 0x%02x (reliable write enabled for: ", + wr_rel_set); + if (wr_rel_set & 0x10) + printf("general partition 4, "); + if (wr_rel_set & 0x8) + printf("general partition 3, "); + if (wr_rel_set & 0x4) + printf("general partition 2, "); + if (wr_rel_set & 0x2) + printf("general partition 1, "); + if (wr_rel_set & 0x1) + printf("user data area, "); + printf(")\n"); + + printf("\tWR_REL_PARAM: 0x%02x (", wr_rel_param); + if (wr_rel_param & 0x1) + printf("host may alter reliable write settings, "); + if (wr_rel_param & 0x4) + printf("enhanced reliable write support)\n"); + else + printf("legacy reliable write support)\n"); + + printf("\tBKOPS_START: 0x%02x\n", bkops_start); + + printf("\tBKOPS_EN: 0x%02x (", bkops_en); + if (bkops_en & 0x1) + printf("host will use background operations"); + else + printf("device will have to handle background " + "operations itself"); + printf(")\n"); + + printf("\tRST_n_FUNCTION: 0x%02x (", rst_n_function); + switch (rst_n_function & 0x3) { + case 0x0: + printf("RST_n is temporarily disabled)\n"); + break; + case 0x1: + printf("RST_n is permanently enabled)\n"); + break; + case 0x2: + printf("RST_n is permanently disabled)\n"); + break; + case 0x3: + printf("reserved)\n"); + break; + } + + printf("\tHPI_MGMT: 0x%02x (", hpi_mgmt); + if (hpi_mgmt & 0x1) + printf("HPI is enabled"); + printf(")\n"); + + printf("\tPARTITIONING_SUPPORT: 0x%02x (", + partitioning_support); + if (partitioning_support & 0x02) + printf("device supports enhanced technology features, "); + if (partitioning_support & 0x1) + printf("device supports partitioning, "); + printf(")\n"); + + printf("\tMAX_ENH_SIZE_MULT: 0x%06x %luKbyte\n", + max_enh_size_mult, max_enh_size_mult * hc_wp_grp_size * + hc_erase_grp_size * 512ul); + + printf("\tPARTITIONS_ATTRIBUTE: 0x%02x (enhanced areas: ", + partitions_attribute); + if (partitions_attribute & 0x10) + printf("general partition 4, "); + if (partitions_attribute & 0x08) + printf("general partition 3, "); + if (partitions_attribute & 0x04) + printf("general partition 2, "); + if (partitions_attribute & 0x02) + printf("general partition 1, "); + if (partitions_attribute & 0x01) + printf("user data area, "); + printf(")\n"); + + printf("\tPARTITION_SETTING_COMPLETED: 0x%02x\n", + partition_setting_completed); + printf("\tGP_SIZE_MULT_GP0: 0x%06x %luKbytes\n", + gp_size_mult_gp0, + gp_size_mult_gp0 * hc_wp_grp_size * + hc_erase_grp_size * 512ul); + printf("\tGP_SIZE_MULT_GP1: 0x%06x %luKbytes\n", + gp_size_mult_gp1, + gp_size_mult_gp1 * hc_wp_grp_size * + hc_erase_grp_size * 512ul); + printf("\tGP_SIZE_MULT_GP2: 0x%06x %luKbytes\n", + gp_size_mult_gp2, + gp_size_mult_gp2 * hc_wp_grp_size * + hc_erase_grp_size * 512ul); + printf("\tGP_SIZE_MULT_GP3: 0x%06x %luKbytes\n", + gp_size_mult_gp3, + gp_size_mult_gp3 * hc_wp_grp_size * + hc_erase_grp_size * 512ul); + printf("\tENH_SIZE_MULT: 0x%06x %luKbytes\n", + enh_size_mult, + enh_size_mult * hc_wp_grp_size * + hc_erase_grp_size * 512ul); + printf("\tENH_START_ADDR: 0x%08x\n", enh_start_addr); + + printf("\tSEC_BAD_BLK_MGMNT: 0x%02x (", sec_bad_blk_mgmnt); + if (sec_bad_blk_mgmnt & 0x1) + printf("retired memory regions are purged"); + printf(")\n"); + } else { + unsigned long long blocks = 0; + int block_size = 0; + unsigned long long memory_capacity; + + printf("bus width: "); + switch (bus_width) { + case 0x0: + printf("8bit data bus ddr\n"); + break; + case 0x1: + printf("4bit data bus ddr\n"); + break; + case 0x2: + printf("8bit data bus\n"); + break; + case 0x5: + printf("4bit data bus\n"); + break; + case 0x6: + printf("1bit data bus\n"); + break; + default: + printf("reserved\n"); + break; + } + + block_size = 512; + blocks = sec_count; + memory_capacity = blocks * block_size; + + printf("high-cacpacity size: "); + if (memory_capacity / (1024ull * 1024ull * 1024ull) > 0) + printf("%.2fGbyte", + memory_capacity / (1024.0 * 1024.0 * 1024.0)); + else if (memory_capacity / (1024ull * 1024ull) > 0) + printf("%.2fMbyte", + memory_capacity / (1024.0 * 1024.0)); + else if (memory_capacity / (1024ull) > 0) + printf("%.2fKbyte", memory_capacity / (1024.0)); + else + printf("%.2fbyte", memory_capacity * 1.0); + printf(" (%lld bytes, %lld sectors, %d bytes each)\n", + memory_capacity, blocks, block_size); + + printf("speed class:\n"); + printf("\t%20s: %s (%c) / %s (%c)\n", + "8bit@52mhz ddr r/w", + speed_class_speed(min_perf_ddr_r_8_52, true), + speed_class_name(min_perf_ddr_r_8_52), + speed_class_speed(min_perf_ddr_w_8_52, true), + speed_class_name(min_perf_ddr_w_8_52)); + printf("\t%20s: %s (%c) / %s (%c)\n", + "8bit@52mhz r/w", + speed_class_speed(min_perf_r_8_52, false), + speed_class_name(min_perf_r_8_52), + speed_class_speed(min_perf_w_8_52, false), + speed_class_name(min_perf_w_8_52)); + printf("\t%20s: %s (%c) / %s (%c)\n", + "8bit@26mhz r/w", + speed_class_speed(min_perf_r_8_26_4_52, false), + speed_class_name(min_perf_r_8_26_4_52), + speed_class_speed(min_perf_w_8_26_4_52, false), + speed_class_name(min_perf_w_8_26_4_52)); + printf("\t%20s: %s (%c) / %s (%c)\n", + "4bit@26mhz r/w", + speed_class_speed(min_perf_r_4_26, false), + speed_class_name(min_perf_r_4_26), + speed_class_speed(min_perf_w_4_26, false), + speed_class_name(min_perf_w_4_26)); + + printf("power class:\n"); + printf("\t%25s: %s / %s\n", + "8bit@52mhz 3.6v/1.95v", + power_class_consumption((pwr_cl_52_360 >> 4) & 0xf, + true), + power_class_consumption((pwr_cl_52_195 >> 4) & 0xf, + false)); + printf("\t%25s: %s / %s\n", + "8bit@26mhz 3.6v/1.95v", + power_class_consumption((pwr_cl_26_360 >> 4) & 0xf, + true), + power_class_consumption((pwr_cl_26_195 >> 4) & 0xf, + false)); + printf("\t%25s: %s / %s\n", + "4bit@52mhz 3.6v/1.95v", + power_class_consumption(pwr_cl_52_360 & 0xf, true), + power_class_consumption(pwr_cl_52_195 & 0xf, false)); + printf("\t%25s: %s / %s\n", + "4bit@26mhz 3.6v/1.95v", + power_class_consumption(pwr_cl_26_360 & 0xf, true), + power_class_consumption(pwr_cl_26_195 & 0xf, false)); + printf("\t%25s: %s / %s\n", + "sleep core/io", + sleep_consumption(s_c_vcc), + sleep_consumption(s_c_vccq)); + + printf("erase group size: %dKbyte\n", 512 * hc_erase_grp_size); + printf("write protect group size: %dKbyte\n", + 512 * hc_erase_grp_size * hc_wp_grp_size); + + printf("access size: "); + switch (acc_size & 0xf) { + case 0x0: + printf("not defined\n"); + break; + case 0x1: + printf("512 bytes\n"); + break; + case 0x2: + printf("1Kbytes\n"); + break; + case 0x3: + printf("2Kbytes\n"); + break; + case 0x4: + printf("4Kbytes\n"); + break; + case 0x5: + printf("8Kbytes\n"); + break; + case 0x6: + printf("16Kbytes\n"); + break; + case 0x7: + printf("32Kbytes\n"); + break; + case 0x8: + printf("64Kbytes\n"); + break; + default: + printf("reserved\n"); + break; + } + + printf("features: "); + if (hpi_features & 0x1) + printf("hpi, "); + if (bkops_support & 0x1) + printf("bkops, "); + printf("\n"); + + printf("background ops status: "); + switch (bkops_status & 0x3) { + case 0x0: + printf("bkops not required\n"); + break; + case 0x1: + printf("bkops required (non-critical)\n"); + break; + case 0x2: + printf("bkops required (performance impacted)\n"); + break; + case 0x3: + printf("bkops required (critical)\n"); + break; + } + } +} + +void print_sd_scr(struct config *config, char *scr) +{ + unsigned int scr_structure; + unsigned int sd_spec; + unsigned int data_stat_after_erase; + unsigned int sd_security; + unsigned int sd_bus_widths; + unsigned int sd_spec3; + unsigned int ex_security; + unsigned int cmd_support; + + parse_bin(scr, "4u4u1u3u4u1u4u9r2u32r", + &scr_structure, &sd_spec, &data_stat_after_erase, + &sd_security, &sd_bus_widths, &sd_spec3, + &ex_security, &cmd_support); + + if (config->verbose) { + printf("======SD/SCR======\n"); + + printf("\tSCR_STRUCTURE: 0x%01x (", scr_structure); + switch (scr_structure) { + case 0: + printf("SCR v1.0)\n"); + break; + default: + printf("reserved)\n"); + break; + } + + printf("\tSD_SPEC: 0x%01x (", sd_spec); + switch (sd_spec) { + case 0: + printf("SD v1.0/1.01)\n"); + break; + case 1: + printf("SD v1.10)\n"); + break; + case 2: + printf("SD v2.00/v3.0x)\n"); + break; + case 3: + printf("SD v4.00)\n"); + break; + default: + printf("reserved)\n"); + break; + } + + printf("\tDATA_STAT_AFTER_ERASE: 0x%01x\n", + data_stat_after_erase); + + printf("\tSD_SECURITY: 0x%01x (", sd_security); + switch (sd_security) { + case 0: + printf("no security)\n"); + break; + case 1: + printf("not used)\n"); + break; + case 2: + printf("SDSC card/security v1.01)\n"); + break; + case 3: + printf("SDHC card/security v2.00)\n"); + break; + case 4: + printf("SDXC card/security v3.xx)\n"); + break; + default: + printf("reserved)\n"); + break; + } + + printf("\tSD_BUS_WIDTHS: 0x%01x (", sd_bus_widths); + if (BITS(sd_bus_widths, 2, 2)) + printf("4bit, "); + if (BITS(sd_bus_widths, 0, 0)) + printf("1bit, "); + printf(" bus)\n"); + + printf("\tSD_SPEC3: 0x%01x (", sd_spec3); + if (sd_spec >= 2) { + switch (sd_spec3) { + case 0: + printf("SD v2.00)\n"); + break; + case 1: + printf("SD v3.0x)\n"); + break; + } + } else { + printf("SD 1.xx)\n"); + } + + printf("\tEX_SECURITY: 0x%01x\n", ex_security); + + printf("\tCMD_SUPPORT: 0x%01x (", cmd_support); + if (BITS(cmd_support, 1, 1)) + printf("CMD23 "); + if (BITS(cmd_support, 0, 0)) + printf("CMD20 "); + printf(" )\n"); + } else { + printf("version: "); + switch (sd_spec) { + case 0: + printf("SD 1.0/1.01\n"); + break; + case 1: + printf("SD 1.10\n"); + break; + case 2: + switch (sd_spec3) { + case 0: + printf("SD 2.00\n"); + break; + case 1: + printf("SD 3.0x\n"); + break; + default: + printf("unknown\n"); + break; + } + break; + case 3: + printf("SD 4.00\n"); + break; + default: + printf("unknown\n"); + break; + } + + printf("bus widths: "); + if (BITS(sd_bus_widths, 2, 2)) + printf("4bit, "); + if (BITS(sd_bus_widths, 0, 0)) + printf("1bit, "); + printf("\b\b\n"); + } +} + +/* MMC/SD interface processing functions */ +void print_info(struct config *config, char *type, + char *cid, char *csd, char *scr, char *ext_csd) +{ + printf("type: '%s'\n", type); + + if (!strcmp(type, "SD") && cid) + print_sd_cid(config, cid); + else if (!strcmp(type, "MMC") && cid) + print_mmc_cid(config, cid); + + if (!strcmp(type, "SD") && scr) + print_sd_scr(config, scr); + + if (!strcmp(type, "MMC") && csd) + print_mmc_csd(config, csd); + else if (!strcmp(type, "SD") && csd) + print_sd_csd(config, csd); + + if (!strcmp(type, "MMC") && ext_csd) + print_mmc_ext_csd(config, ext_csd); +} + +int process_dir(struct config *config, enum REG_TYPE reg) +{ + char *type = NULL, *cid = NULL, *csd = NULL, *scr = NULL, *ext_csd = NULL; + int ret = 0; + + if (chdir(config->dir) < 0) { + fprintf(stderr, + "MMC/SD information directory '%s' does not exist.\n", + config->dir); + return -1; + } + + type = read_file("type"); + if (!type) { + fprintf(stderr, + "Could not read card interface type in directory '%s'.\n", + config->dir); + return -1; + } + + if (strcmp(type, "MMC") && strcmp(type, "SD")) { + fprintf(stderr, "Unknown type: '%s'\n", type); + ret = -1; + goto err; + } + + switch (reg) { + case CID: + cid = read_file("cid"); + if (!cid) { + fprintf(stderr, + "Could not read card identity in directory '%s'.\n", + config->dir); + ret = -1; + goto err; + } + break; + case CSD: + csd = read_file("csd"); + if (!csd) { + fprintf(stderr, + "Could not read card specific data in " + "directory '%s'.\n", config->dir); + ret = -1; + goto err; + } + break; + case SCR: + if (!strcmp(type, "SD")) { + scr = read_file("scr"); + if (!scr) { + fprintf(stderr, "Could not read SD card " + "configuration in directory '%s'.\n", + config->dir); + ret = -1; + goto err; + } + } + break; + case EXT_CSD: + if (!strcmp(type, "MMC")) { + ext_csd = read_file("ext_csd"); + if (!ext_csd) { + fprintf(stderr, "Could not read extra specific " + "data in directory '%s'.\n", + config->dir); + ret = -1; + goto err; + } + } + break; + default: + goto err; + } + + print_info(config, type, cid, csd, scr, ext_csd); + +err: + free(ext_csd); + free(scr); + free(csd); + free(cid); + free(type); + + return ret; +} + +int lsmmc_main(struct config *config, int argc, char **argv) +{ + int ret; + + ret = parse_opts(argc, argv, config); + if (ret) + return ret; + + if (!config->idsfile) { + int i = 0; + char *ids_path[] = { + "/etc/lsmmc.ids", + "/system/etc/lsmmc.ids", + "lsmmc.ids", + NULL, + }; + + while (!config->idsfile && ids_path[i]) { + if (!access(ids_path[i], F_OK)) + config->idsfile = strdup(ids_path[i]); + i++; + } + } + + return parse_ids(config); +} + +int do_read_csd(int argc, char **argv) +{ + struct config config; + int ret, i; + + memset(&config, 0, sizeof(config)); + config.mmc_ids = calloc(256, sizeof(char *)); + config.sd_ids = calloc(256, sizeof(char *)); + + ret = lsmmc_main(&config, argc, argv); + if (ret) + goto out; + + if (config.dir) + ret = process_dir(&config, CSD); + +out: + for (i = 0; i < 256; i++) { + free(config.mmc_ids[i]); + free(config.sd_ids[i]); + } + + free(config.mmc_ids); + free(config.sd_ids); + free(config.dir); + free(config.idsfile); + + return ret; +} + +int do_read_cid(int argc, char **argv) +{ + struct config config; + int ret, i; + + memset(&config, 0, sizeof(config)); + config.mmc_ids = calloc(256, sizeof(char *)); + config.sd_ids = calloc(256, sizeof(char *)); + + ret = lsmmc_main(&config, argc, argv); + if (ret) + goto out; + + if (config.dir) + ret = process_dir(&config, CID); + +out: + for (i = 0; i < 256; i++) { + free(config.mmc_ids[i]); + free(config.sd_ids[i]); + } + + free(config.mmc_ids); + free(config.sd_ids); + free(config.dir); + free(config.idsfile); + + return ret; +} + +int do_read_scr(int argc, char **argv) +{ + struct config config; + int ret, i; + + memset(&config, 0, sizeof(config)); + config.mmc_ids = calloc(256, sizeof(char *)); + config.sd_ids = calloc(256, sizeof(char *)); + + ret = lsmmc_main(&config, argc, argv); + if (ret) + goto out; + + if (config.dir) + ret = process_dir(&config, SCR); + +out: + for (i = 0; i < 256; i++) { + free(config.mmc_ids[i]); + free(config.sd_ids[i]); + } + + free(config.mmc_ids); + free(config.sd_ids); + free(config.dir); + free(config.idsfile); + + return ret; +} diff --git a/lsmmc.ids b/lsmmc.ids new file mode 100644 index 0000000..e79932d --- /dev/null +++ b/lsmmc.ids @@ -0,0 +1,27 @@ +sd:0x01:Panasonic +sd:0x02:Toshiba/Kingston/Viking +sd:0x03:SanDisk +sd:0x08:Silicon Power +sd:0x18:Infineon +sd:0x1b:Transcend +sd:0x1c:Transcend +sd:0x1d:Corsair +sd:0x1e:Transcend +sd:0x1f:Kingston +sd:0x28:Lexar +sd:0x30:SanDisk +sd:0x33:STMicroelectronics +sd:0x41:Kingston +sd:0x6f:STMicroelectronics +sd:0x89:Unknown +mmc:0x00:SanDisk +mmc:0x02:Kingston/SanDisk +mmc:0x03:Toshiba +mmc:0x05:Unknown +mmc:0x06:Unknown +mmc:0x11:Toshiba +mmc:0x15:Samsung/SanDisk/LG +mmc:0x37:KingMax +mmc:0x44:SanDisk +mmc:0x2c:Kingston +mmc:0x70:Kingston diff --git a/mmc.c b/mmc.c index a13d9ae..ed5bbf5 100644 --- a/mmc.c +++ b/mmc.c @@ -175,6 +175,24 @@ static struct Command commands[] = { "NOTE! The cache is an optional feature on devices >= eMMC4.5.", NULL }, + { do_read_csd, -1, + "csd read", "\n" + "Print CSD data from .\n" + "The device path should specify the csd file directory.", + NULL + }, + { do_read_cid, -1, + "cid read", "\n" + "Print CID data from .\n" + "The device path should specify the cid file directory.", + NULL + }, + { do_read_scr, -1, + "scr read", "\n" + "Print SCR data from .\n" + "The device path should specify the scr file directory.", + NULL + }, { 0, 0, 0, 0 } }; diff --git a/mmc_cmds.h b/mmc_cmds.h index 75d8f8c..32a4001 100644 --- a/mmc_cmds.h +++ b/mmc_cmds.h @@ -36,3 +36,6 @@ int do_rpmb_read_block(int nargs, char **argv); int do_rpmb_write_block(int nargs, char **argv); int do_cache_en(int nargs, char **argv); int do_cache_dis(int nargs, char **argv); +int do_read_scr(int argc, char **argv); +int do_read_cid(int argc, char **argv); +int do_read_csd(int argc, char **argv);