├── .gitignore ├── Android.mk ├── Makefile ├── man └── Makefile ├── mmc.c ├── mmc.h ├── mmc_cmds.c ├── mmc_cmds.h └── mmc_utils /.gitignore: -------------------------------------------------------------------------------- 1 | .mmc.o.d 2 | .mmc_cmds.o.d 3 | mmc 4 | mmc.o 5 | mmc_cmds.o 6 | -------------------------------------------------------------------------------- /Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH:= $(call my-dir) 2 | 3 | include $(CLEAR_VARS) 4 | LOCAL_MODULE_TAGS := optional 5 | LOCAL_SRC_FILES:= mmc.c mmc_cmds.c 6 | LOCAL_MODULE := mmc_utils 7 | LOCAL_SHARED_LIBRARIES := libcutils libc 8 | LOCAL_C_INCLUDES+= $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr/include 9 | LOCAL_ADDITIONAL_DEPENDENCIES += $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr 10 | LOCAL_MODULE_PATH := $(TARGET_OUT_DATA)/mmc-utils 11 | include $(BUILD_EXECUTABLE) 12 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC ?= gcc 2 | AM_CFLAGS = -D_FILE_OFFSET_BITS=64 -D_FORTIFY_SOURCE=2 3 | CFLAGS ?= -g -O2 4 | objects = mmc.o mmc_cmds.o 5 | 6 | CHECKFLAGS = -Wall -Werror -Wuninitialized -Wundef 7 | 8 | DEPFLAGS = -Wp,-MMD,$(@D)/.$(@F).d,-MT,$@ 9 | 10 | override CFLAGS := $(CHECKFLAGS) $(AM_CFLAGS) $(CFLAGS) 11 | 12 | INSTALL = install 13 | prefix ?= /usr/local 14 | bindir = $(prefix)/bin 15 | LIBS= 16 | RESTORE_LIBS= 17 | 18 | progs = mmc 19 | 20 | # make C=1 to enable sparse 21 | ifdef C 22 | check = sparse $(CHECKFLAGS) 23 | endif 24 | 25 | all: $(progs) manpages 26 | 27 | .c.o: 28 | ifdef C 29 | $(check) $< 30 | endif 31 | $(CC) $(CPPFLAGS) $(CFLAGS) $(DEPFLAGS) -c $< 32 | 33 | mmc: $(objects) 34 | $(CC) $(CFLAGS) -o $@ $(objects) $(LDFLAGS) $(LIBS) 35 | 36 | manpages: 37 | cd man && make 38 | 39 | install-man: 40 | cd man && make install 41 | 42 | clean: 43 | rm -f $(progs) $(objects) 44 | cd man && make clean 45 | 46 | install: $(progs) install-man 47 | $(INSTALL) -m755 -d $(DESTDIR)$(bindir) 48 | $(INSTALL) $(progs) $(DESTDIR)$(bindir) 49 | -------------------------------------------------------------------------------- /man/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | clean: 3 | -------------------------------------------------------------------------------- /mmc.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This program is free software; you can redistribute it and/or 3 | * modify it under the terms of the GNU General Public 4 | * License v2 as published by the Free Software Foundation. 5 | * 6 | * This program is distributed in the hope that it will be useful, 7 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 9 | * General Public License for more details. 10 | * 11 | * You should have received a copy of the GNU General Public 12 | * License along with this program; if not, write to the 13 | * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 14 | * Boston, MA 021110-1307, USA. 15 | * 16 | * (This code is based on btrfs-progs/btrfs.c.) 17 | */ 18 | 19 | #define _GNU_SOURCE 20 | #include 21 | #include 22 | #include 23 | 24 | #include "mmc_cmds.h" 25 | 26 | #define MMC_VERSION "0.1" 27 | 28 | #define BASIC_HELP 0 29 | #define ADVANCED_HELP 1 30 | 31 | typedef int (*CommandFunction)(int argc, char **argv); 32 | 33 | struct Command { 34 | CommandFunction func; /* function which implements the command */ 35 | int nargs; /* if == 999, any number of arguments 36 | if >= 0, number of arguments, 37 | if < 0, _minimum_ number of arguments */ 38 | char *verb; /* verb */ 39 | char *help; /* help lines; from the 2nd line onward they 40 | are automatically indented */ 41 | char *adv_help; /* advanced help message; from the 2nd line 42 | onward they are automatically indented */ 43 | 44 | /* the following fields are run-time filled by the program */ 45 | char **cmds; /* array of subcommands */ 46 | int ncmds; /* number of subcommand */ 47 | }; 48 | 49 | static struct Command commands[] = { 50 | /* 51 | * avoid short commands different for the case only 52 | */ 53 | { do_read_extcsd, -1, 54 | "extcsd read", "\n" 55 | "Print extcsd data from .", 56 | NULL 57 | }, 58 | { do_writeprotect_get, -1, 59 | "writeprotect get", "\n" 60 | "Determine the eMMC writeprotect status of .", 61 | NULL 62 | }, 63 | { do_writeprotect_set, -1, 64 | "writeprotect set", "\n" 65 | "Set the eMMC writeprotect status of .\nThis sets the eMMC to be write-protected until next boot.", 66 | NULL 67 | }, 68 | { do_disable_512B_emulation, -1, 69 | "disable 512B emulation", "\n" 70 | "Set the eMMC data sector size to 4KB by disabling emulation on\n.", 71 | NULL 72 | }, 73 | { do_enh_area_set, -4, 74 | "enh_area set", "<-y|-n> " " " " " "\n" 75 | "Enable the enhanced user area for the .\nDry-run only unless -y is passed.\nNOTE! This is a one-time programmable (unreversible) change.", 76 | NULL 77 | }, 78 | { do_status_get, -1, 79 | "status get", "\n" 80 | "Print the response to STATUS_SEND (CMD13).", 81 | NULL 82 | }, 83 | { do_write_boot_en, -3, 84 | "bootpart enable", " " " " "\n" 85 | "Enable the boot partition for the .\nTo receive acknowledgment of boot from the card set \nto 1, else set it to 0.", 86 | NULL 87 | }, 88 | { do_write_bkops_en, -1, 89 | "bkops enable", "\n" 90 | "Enable the eMMC BKOPS feature on .\nNOTE! This is a one-time programmable (unreversible) change.", 91 | NULL 92 | }, 93 | { do_hwreset_en, -1, 94 | "hwreset enable", "\n" 95 | "Permanently enable the eMMC H/W Reset feature on .\nNOTE! This is a one-time programmable (unreversible) change.", 96 | NULL 97 | }, 98 | { do_hwreset_dis, -1, 99 | "hwreset disable", "\n" 100 | "Permanently disable the eMMC H/W Reset feature on .\nNOTE! This is a one-time programmable (unreversible) change.", 101 | NULL 102 | }, 103 | { do_sanitize, -1, 104 | "sanitize", "\n" 105 | "Send Sanitize command to the .\nThis will delete the unmapped memory region of the device.", 106 | NULL 107 | }, 108 | { do_vendor_cmd, -2, 109 | "vendor", " " "\n" 110 | "Send CMD62 bootsize command to the .\nUsed for various Samsung moviNAND cmds.", 111 | NULL 112 | }, 113 | { 0, 0, 0, 0 } 114 | }; 115 | 116 | static char *get_prgname(char *programname) 117 | { 118 | char *np; 119 | np = strrchr(programname,'/'); 120 | if(!np) 121 | np = programname; 122 | else 123 | np++; 124 | 125 | return np; 126 | } 127 | 128 | static void print_help(char *programname, struct Command *cmd, int helptype) 129 | { 130 | char *pc; 131 | 132 | printf("\t%s %s ", programname, cmd->verb ); 133 | 134 | if (helptype == ADVANCED_HELP && cmd->adv_help) 135 | for(pc = cmd->adv_help; *pc; pc++){ 136 | putchar(*pc); 137 | if(*pc == '\n') 138 | printf("\t\t"); 139 | } 140 | else 141 | for(pc = cmd->help; *pc; pc++){ 142 | putchar(*pc); 143 | if(*pc == '\n') 144 | printf("\t\t"); 145 | } 146 | 147 | putchar('\n'); 148 | } 149 | 150 | static void help(char *np) 151 | { 152 | struct Command *cp; 153 | 154 | printf("Usage:\n"); 155 | for( cp = commands; cp->verb; cp++ ) 156 | print_help(np, cp, BASIC_HELP); 157 | 158 | printf("\n\t%s help|--help|-h\n\t\tShow the help.\n",np); 159 | printf("\n\t%s --help\n\t\tShow detailed help for a command or subset of commands.\n",np); 160 | printf("\n%s\n", MMC_VERSION); 161 | } 162 | 163 | static int split_command(char *cmd, char ***commands) 164 | { 165 | int c, l; 166 | char *p, *s; 167 | 168 | for( *commands = 0, l = c = 0, p = s = cmd ; ; p++, l++ ){ 169 | if ( *p && *p != ' ' ) 170 | continue; 171 | 172 | /* c + 2 so that we have room for the null */ 173 | (*commands) = realloc( (*commands), sizeof(char *)*(c + 2)); 174 | (*commands)[c] = strndup(s, l); 175 | c++; 176 | l = 0; 177 | s = p+1; 178 | if( !*p ) break; 179 | } 180 | 181 | (*commands)[c] = 0; 182 | return c; 183 | } 184 | 185 | /* 186 | This function checks if the passed command is ambiguous 187 | */ 188 | static int check_ambiguity(struct Command *cmd, char **argv){ 189 | int i; 190 | struct Command *cp; 191 | /* check for ambiguity */ 192 | for( i = 0 ; i < cmd->ncmds ; i++ ){ 193 | int match; 194 | for( match = 0, cp = commands; cp->verb; cp++ ){ 195 | int j, skip; 196 | char *s1, *s2; 197 | 198 | if( cp->ncmds < i ) 199 | continue; 200 | 201 | for( skip = 0, j = 0 ; j < i ; j++ ) 202 | if( strcmp(cmd->cmds[j], cp->cmds[j])){ 203 | skip=1; 204 | break; 205 | } 206 | if(skip) 207 | continue; 208 | 209 | if( !strcmp(cmd->cmds[i], cp->cmds[i])) 210 | continue; 211 | for(s2 = cp->cmds[i], s1 = argv[i+1]; 212 | *s1 == *s2 && *s1; s1++, s2++ ) ; 213 | if( !*s1 ) 214 | match++; 215 | } 216 | if(match){ 217 | int j; 218 | fprintf(stderr, "ERROR: in command '"); 219 | for( j = 0 ; j <= i ; j++ ) 220 | fprintf(stderr, "%s%s",j?" ":"", argv[j+1]); 221 | fprintf(stderr, "', '%s' is ambiguous\n",argv[j]); 222 | return -2; 223 | } 224 | } 225 | return 0; 226 | } 227 | 228 | /* 229 | * This function, compacts the program name and the command in the first 230 | * element of the '*av' array 231 | */ 232 | static int prepare_args(int *ac, char ***av, char *prgname, struct Command *cmd ){ 233 | 234 | char **ret; 235 | int i; 236 | char *newname; 237 | 238 | ret = (char **)malloc(sizeof(char*)*(*ac+1)); 239 | newname = (char*)malloc(strlen(prgname)+strlen(cmd->verb)+2); 240 | if( !ret || !newname ){ 241 | free(ret); 242 | free(newname); 243 | return -1; 244 | } 245 | 246 | ret[0] = newname; 247 | for(i=0; i < *ac ; i++ ) 248 | ret[i+1] = (*av)[i]; 249 | 250 | strcpy(newname, prgname); 251 | strcat(newname, " "); 252 | strcat(newname, cmd->verb); 253 | 254 | (*ac)++; 255 | *av = ret; 256 | 257 | return 0; 258 | 259 | } 260 | 261 | /* 262 | This function performs the following jobs: 263 | - show the help if '--help' or 'help' or '-h' are passed 264 | - verify that a command is not ambiguous, otherwise show which 265 | part of the command is ambiguous 266 | - if after a (even partial) command there is '--help' show detailed help 267 | for all the matching commands 268 | - if the command doesn't match show an error 269 | - finally, if a command matches, they return which command matched and 270 | the arguments 271 | 272 | The function return 0 in case of help is requested; <0 in case 273 | of uncorrect command; >0 in case of matching commands 274 | argc, argv are the arg-counter and arg-vector (input) 275 | *nargs_ is the number of the arguments after the command (output) 276 | **cmd_ is the invoked command (output) 277 | ***args_ are the arguments after the command 278 | 279 | */ 280 | static int parse_args(int argc, char **argv, 281 | CommandFunction *func_, 282 | int *nargs_, char **cmd_, char ***args_ ) 283 | { 284 | struct Command *cp; 285 | struct Command *matchcmd=0; 286 | char *prgname = get_prgname(argv[0]); 287 | int i=0, helprequested=0; 288 | 289 | if( argc < 2 || !strcmp(argv[1], "help") || 290 | !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")){ 291 | help(prgname); 292 | return 0; 293 | } 294 | 295 | for( cp = commands; cp->verb; cp++ ) 296 | if( !cp->ncmds) 297 | cp->ncmds = split_command(cp->verb, &(cp->cmds)); 298 | 299 | for( cp = commands; cp->verb; cp++ ){ 300 | int match; 301 | 302 | if( argc-1 < cp->ncmds ) 303 | continue; 304 | for( match = 1, i = 0 ; i < cp->ncmds ; i++ ){ 305 | char *s1, *s2; 306 | s1 = cp->cmds[i]; 307 | s2 = argv[i+1]; 308 | 309 | for(s2 = cp->cmds[i], s1 = argv[i+1]; 310 | *s1 == *s2 && *s1; 311 | s1++, s2++ ) ; 312 | if( *s1 ){ 313 | match=0; 314 | break; 315 | } 316 | } 317 | 318 | /* If you understand why this code works ... 319 | you are a genious !! */ 320 | if(argc>i+1 && !strcmp(argv[i+1],"--help")){ 321 | if(!helprequested) 322 | printf("Usage:\n"); 323 | print_help(prgname, cp, ADVANCED_HELP); 324 | helprequested=1; 325 | continue; 326 | } 327 | 328 | if(!match) 329 | continue; 330 | 331 | matchcmd = cp; 332 | *nargs_ = argc-matchcmd->ncmds-1; 333 | *cmd_ = matchcmd->verb; 334 | *args_ = argv+matchcmd->ncmds+1; 335 | *func_ = cp->func; 336 | 337 | break; 338 | } 339 | 340 | if(helprequested){ 341 | printf("\n%s\n", MMC_VERSION); 342 | return 0; 343 | } 344 | 345 | if(!matchcmd){ 346 | fprintf( stderr, "ERROR: unknown command '%s'\n",argv[1]); 347 | help(prgname); 348 | return -1; 349 | } 350 | 351 | if(check_ambiguity(matchcmd, argv)) 352 | return -2; 353 | 354 | /* check the number of argument */ 355 | if (matchcmd->nargs < 0 && matchcmd->nargs < -*nargs_ ){ 356 | fprintf(stderr, "ERROR: '%s' requires minimum %d arg(s)\n", 357 | matchcmd->verb, -matchcmd->nargs); 358 | return -2; 359 | } 360 | if(matchcmd->nargs >= 0 && matchcmd->nargs != *nargs_ && matchcmd->nargs != 999){ 361 | fprintf(stderr, "ERROR: '%s' requires %d arg(s)\n", 362 | matchcmd->verb, matchcmd->nargs); 363 | return -2; 364 | } 365 | 366 | if (prepare_args( nargs_, args_, prgname, matchcmd )){ 367 | fprintf(stderr, "ERROR: not enough memory\\n"); 368 | return -20; 369 | } 370 | 371 | 372 | return 1; 373 | } 374 | int main(int ac, char **av ) 375 | { 376 | char *cmd=0, **args=0; 377 | int nargs=0, r; 378 | CommandFunction func=0; 379 | 380 | r = parse_args(ac, av, &func, &nargs, &cmd, &args); 381 | if( r <= 0 ){ 382 | /* error or no command to parse*/ 383 | exit(-r); 384 | } 385 | 386 | exit(func(nargs, args)); 387 | } 388 | 389 | -------------------------------------------------------------------------------- /mmc.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This program is free software; you can redistribute it and/or 3 | * modify it under the terms of the GNU General Public 4 | * License v2 as published by the Free Software Foundation. 5 | * 6 | * This program is distributed in the hope that it will be useful, 7 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 9 | * General Public License for more details. 10 | * 11 | * You should have received a copy of the GNU General Public 12 | * License along with this program; if not, write to the 13 | * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 14 | * Boston, MA 021110-1307, USA. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | #define CHECK(expr, msg, err_stmt) { if (expr) { fprintf(stderr, msg); err_stmt; } } 22 | 23 | /* From kernel linux/major.h */ 24 | #define MMC_BLOCK_MAJOR 179 25 | 26 | /* From kernel linux/mmc/mmc.h */ 27 | #define MMC_SWITCH 6 /* ac [31:0] See below R1b */ 28 | #define MMC_SEND_EXT_CSD 8 /* adtc R1 */ 29 | #define MMC_SEND_STATUS 13 /* ac [31:16] RCA R1 */ 30 | #define R1_SWITCH_ERROR (1 << 7) /* sx, c */ 31 | #define MMC_SWITCH_MODE_WRITE_BYTE 0x03 /* Set target to value */ 32 | 33 | #define MMC_CMD62 62 34 | #define MMC_CMD62_ARG1 0xEFAC62EC 35 | #define MMC_CMD62_ARG2 0x00CBAEA7 36 | 37 | /* 38 | * EXT_CSD fields 39 | */ 40 | #define EXT_CSD_S_CMD_SET 504 41 | #define EXT_CSD_HPI_FEATURE 503 42 | #define EXT_CSD_BKOPS_SUPPORT 502 /* RO */ 43 | #define EXT_CSD_BOOT_INFO 228 /* R/W */ 44 | #define EXT_CSD_SEC_COUNT_3 215 45 | #define EXT_CSD_SEC_COUNT_2 214 46 | #define EXT_CSD_SEC_COUNT_1 213 47 | #define EXT_CSD_SEC_COUNT_0 212 48 | #define EXT_CSD_PART_SWITCH_TIME 199 49 | #define EXT_CSD_BOOT_CFG 179 50 | #define EXT_CSD_PART_CONFIG 179 51 | #define EXT_CSD_ERASE_GROUP_DEF 175 52 | #define EXT_CSD_BOOT_WP 173 53 | #define EXT_CSD_WR_REL_PARAM 166 54 | #define EXT_CSD_SANITIZE_START 165 55 | #define EXT_CSD_BKOPS_EN 163 /* R/W */ 56 | #define EXT_CSD_RST_N_FUNCTION 162 /* R/W */ 57 | #define EXT_CSD_PARTITIONING_SUPPORT 160 /* RO */ 58 | #define EXT_CSD_PARTITIONS_ATTRIBUTE 156 /* R/W */ 59 | #define EXT_CSD_PARTITION_SETTING_COMPLETED 155 /* R/W */ 60 | #define EXT_CSD_ENH_SIZE_MULT_2 142 61 | #define EXT_CSD_ENH_SIZE_MULT_1 141 62 | #define EXT_CSD_ENH_SIZE_MULT_0 140 63 | #define EXT_CSD_ENH_START_ADDR_3 139 64 | #define EXT_CSD_ENH_START_ADDR_2 138 65 | #define EXT_CSD_ENH_START_ADDR_1 137 66 | #define EXT_CSD_ENH_START_ADDR_0 136 67 | #define EXT_CSD_NATIVE_SECTOR_SIZE 63 /* R */ 68 | #define EXT_CSD_USE_NATIVE_SECTOR 62 /* R/W */ 69 | #define EXT_CSD_DATA_SECTOR_SIZE 61 /* R */ 70 | 71 | /* 72 | * WR_REL_PARAM field definitions 73 | */ 74 | #define HS_CTRL_REL (1<<0) 75 | #define EN_REL_WR (1<<2) 76 | 77 | /* 78 | * BKOPS_EN field definition 79 | */ 80 | #define BKOPS_ENABLE (1<<0) 81 | 82 | /* 83 | * EXT_CSD field definitions 84 | */ 85 | #define EXT_CSD_HPI_SUPP (1<<0) 86 | #define EXT_CSD_HPI_IMPL (1<<1) 87 | #define EXT_CSD_CMD_SET_NORMAL (1<<0) 88 | #define EXT_CSD_BOOT_WP_B_PWR_WP_DIS (0x40) 89 | #define EXT_CSD_BOOT_WP_B_PERM_WP_DIS (0x10) 90 | #define EXT_CSD_BOOT_WP_B_PERM_WP_EN (0x04) 91 | #define EXT_CSD_BOOT_WP_B_PWR_WP_EN (0x01) 92 | #define EXT_CSD_BOOT_INFO_HS_MODE (1<<2) 93 | #define EXT_CSD_BOOT_INFO_DDR_DDR (1<<1) 94 | #define EXT_CSD_BOOT_INFO_ALT (1<<0) 95 | #define EXT_CSD_BOOT_CFG_ACK (1<<6) 96 | #define EXT_CSD_BOOT_CFG_EN (0x38) 97 | #define EXT_CSD_BOOT_CFG_ACC (0x07) 98 | #define EXT_CSD_RST_N_EN_MASK (0x03) 99 | #define EXT_CSD_HW_RESET_EN (0x01) 100 | #define EXT_CSD_HW_RESET_DIS (0x02) 101 | #define EXT_CSD_PART_CONFIG_ACC_MASK (0x7) 102 | #define EXT_CSD_PART_CONFIG_ACC_BOOT0 (0x1) 103 | #define EXT_CSD_PART_CONFIG_ACC_BOOT1 (0x2) 104 | #define EXT_CSD_PART_CONFIG_ACC_USER_AREA (0x7) 105 | #define EXT_CSD_PART_CONFIG_ACC_ACK (0x40) 106 | #define EXT_CSD_PARTITIONING_EN (1<<0) 107 | #define EXT_CSD_ENH_ATTRIBUTE_EN (1<<1) 108 | #define EXT_CSD_ENH_USR (1<<0) 109 | 110 | /* From kernel linux/mmc/core.h */ 111 | #define MMC_RSP_PRESENT (1 << 0) 112 | #define MMC_RSP_136 (1 << 1) /* 136 bit response */ 113 | #define MMC_RSP_CRC (1 << 2) /* expect valid crc */ 114 | #define MMC_RSP_BUSY (1 << 3) /* card may send busy */ 115 | #define MMC_RSP_OPCODE (1 << 4) /* response contains opcode */ 116 | 117 | #define MMC_CMD_AC (0 << 5) 118 | #define MMC_CMD_ADTC (1 << 5) 119 | 120 | #define MMC_RSP_SPI_S1 (1 << 7) /* one status byte */ 121 | #define MMC_RSP_SPI_BUSY (1 << 10) /* card may send busy */ 122 | 123 | #define MMC_RSP_SPI_R1 (MMC_RSP_SPI_S1) 124 | #define MMC_RSP_SPI_R1B (MMC_RSP_SPI_S1|MMC_RSP_SPI_BUSY) 125 | 126 | #define MMC_RSP_R1 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE) 127 | #define MMC_RSP_R1B (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE|MMC_RSP_BUSY) 128 | -------------------------------------------------------------------------------- /mmc_cmds.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This program is free software; you can redistribute it and/or 3 | * modify it under the terms of the GNU General Public 4 | * License v2 as published by the Free Software Foundation. 5 | * 6 | * This program is distributed in the hope that it will be useful, 7 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 9 | * General Public License for more details. 10 | * 11 | * You should have received a copy of the GNU General Public 12 | * License along with this program; if not, write to the 13 | * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 14 | * Boston, MA 021110-1307, USA. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include "mmc.h" 31 | #include "mmc_cmds.h" 32 | 33 | int read_extcsd(int fd, __u8 *ext_csd) 34 | { 35 | int ret = 0; 36 | struct mmc_ioc_cmd idata; 37 | memset(&idata, 0, sizeof(idata)); 38 | memset(ext_csd, 0, sizeof(__u8) * 512); 39 | idata.write_flag = 0; 40 | idata.opcode = MMC_SEND_EXT_CSD; 41 | idata.arg = 0; 42 | idata.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC; 43 | idata.blksz = 512; 44 | idata.blocks = 1; 45 | mmc_ioc_cmd_set_data(idata, ext_csd); 46 | 47 | ret = ioctl(fd, MMC_IOC_CMD, &idata); 48 | if (ret) 49 | perror("ioctl"); 50 | 51 | return ret; 52 | } 53 | 54 | int write_extcsd_value(int fd, __u8 index, __u8 value) 55 | { 56 | int ret = 0; 57 | struct mmc_ioc_cmd idata; 58 | 59 | memset(&idata, 0, sizeof(idata)); 60 | idata.write_flag = 1; 61 | idata.opcode = MMC_SWITCH; 62 | idata.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | 63 | (index << 16) | 64 | (value << 8) | 65 | EXT_CSD_CMD_SET_NORMAL; 66 | idata.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC; 67 | 68 | ret = ioctl(fd, MMC_IOC_CMD, &idata); 69 | if (ret) 70 | perror("ioctl"); 71 | 72 | return ret; 73 | } 74 | 75 | int write_vendor_cmd(int fd, __u32 arg) 76 | { 77 | int ret = 0; 78 | struct mmc_ioc_cmd idata; 79 | 80 | memset(&idata, 0, sizeof(idata)); 81 | idata.write_flag = 1; 82 | idata.opcode = MMC_CMD62; 83 | idata.arg = arg; 84 | idata.flags = MMC_RSP_R1B | MMC_CMD_AC; 85 | 86 | ret = ioctl(fd, MMC_IOC_CMD, &idata); 87 | if (ret) 88 | perror("ioctl"); 89 | 90 | return ret; 91 | } 92 | 93 | int send_status(int fd, __u32 *response) 94 | { 95 | int ret = 0; 96 | struct mmc_ioc_cmd idata; 97 | 98 | memset(&idata, 0, sizeof(idata)); 99 | idata.opcode = MMC_SEND_STATUS; 100 | idata.arg = (1 << 16); 101 | idata.flags = MMC_RSP_R1 | MMC_CMD_AC; 102 | 103 | ret = ioctl(fd, MMC_IOC_CMD, &idata); 104 | if (ret) 105 | perror("ioctl"); 106 | 107 | *response = idata.response[0]; 108 | 109 | return ret; 110 | } 111 | 112 | void print_writeprotect_status(__u8 *ext_csd) 113 | { 114 | __u8 reg; 115 | __u8 ext_csd_rev = ext_csd[192]; 116 | 117 | /* A43: reserved [174:0] */ 118 | if (ext_csd_rev >= 5) { 119 | printf("Boot write protection status registers" 120 | " [BOOT_WP_STATUS]: 0x%02x\n", ext_csd[174]); 121 | 122 | reg = ext_csd[EXT_CSD_BOOT_WP]; 123 | printf("Boot Area Write protection [BOOT_WP]: 0x%02x\n", reg); 124 | printf(" Power ro locking: "); 125 | if (reg & EXT_CSD_BOOT_WP_B_PWR_WP_DIS) 126 | printf("not possible\n"); 127 | else 128 | printf("possible\n"); 129 | 130 | printf(" Permanent ro locking: "); 131 | if (reg & EXT_CSD_BOOT_WP_B_PERM_WP_DIS) 132 | printf("not possible\n"); 133 | else 134 | printf("possible\n"); 135 | 136 | printf(" ro lock status: "); 137 | if (reg & EXT_CSD_BOOT_WP_B_PWR_WP_EN) 138 | printf("locked until next power on\n"); 139 | else if (reg & EXT_CSD_BOOT_WP_B_PERM_WP_EN) 140 | printf("locked permanently\n"); 141 | else 142 | printf("not locked\n"); 143 | } 144 | } 145 | 146 | int do_writeprotect_get(int nargs, char **argv) 147 | { 148 | __u8 ext_csd[512]; 149 | int fd, ret; 150 | char *device; 151 | 152 | CHECK(nargs != 2, "Usage: mmc writeprotect get \n", 153 | exit(1)); 154 | 155 | device = argv[1]; 156 | 157 | fd = open(device, O_RDWR); 158 | if (fd < 0) { 159 | perror("open"); 160 | exit(1); 161 | } 162 | 163 | ret = read_extcsd(fd, ext_csd); 164 | if (ret) { 165 | fprintf(stderr, "Could not read EXT_CSD from %s\n", device); 166 | exit(1); 167 | } 168 | 169 | print_writeprotect_status(ext_csd); 170 | 171 | return ret; 172 | } 173 | 174 | int do_writeprotect_set(int nargs, char **argv) 175 | { 176 | __u8 ext_csd[512], value; 177 | int fd, ret; 178 | char *device; 179 | 180 | CHECK(nargs != 2, "Usage: mmc writeprotect set \n", 181 | exit(1)); 182 | 183 | device = argv[1]; 184 | 185 | fd = open(device, O_RDWR); 186 | if (fd < 0) { 187 | perror("open"); 188 | exit(1); 189 | } 190 | 191 | ret = read_extcsd(fd, ext_csd); 192 | if (ret) { 193 | fprintf(stderr, "Could not read EXT_CSD from %s\n", device); 194 | exit(1); 195 | } 196 | 197 | value = ext_csd[EXT_CSD_BOOT_WP] | 198 | EXT_CSD_BOOT_WP_B_PWR_WP_EN; 199 | ret = write_extcsd_value(fd, EXT_CSD_BOOT_WP, value); 200 | if (ret) { 201 | fprintf(stderr, "Could not write 0x%02x to " 202 | "EXT_CSD[%d] in %s\n", 203 | value, EXT_CSD_BOOT_WP, device); 204 | exit(1); 205 | } 206 | 207 | return ret; 208 | } 209 | 210 | int do_disable_512B_emulation(int nargs, char **argv) 211 | { 212 | __u8 ext_csd[512], native_sector_size, data_sector_size, wr_rel_param; 213 | int fd, ret; 214 | char *device; 215 | 216 | CHECK(nargs != 2, "Usage: mmc disable 512B emulation \n", exit(1)); 217 | device = argv[1]; 218 | 219 | fd = open(device, O_RDWR); 220 | if (fd < 0) { 221 | perror("open"); 222 | exit(1); 223 | } 224 | 225 | ret = read_extcsd(fd, ext_csd); 226 | if (ret) { 227 | fprintf(stderr, "Could not read EXT_CSD from %s\n", device); 228 | exit(1); 229 | } 230 | 231 | wr_rel_param = ext_csd[EXT_CSD_WR_REL_PARAM]; 232 | native_sector_size = ext_csd[EXT_CSD_NATIVE_SECTOR_SIZE]; 233 | data_sector_size = ext_csd[EXT_CSD_DATA_SECTOR_SIZE]; 234 | 235 | if (native_sector_size && !data_sector_size && 236 | (wr_rel_param & EN_REL_WR)) { 237 | ret = write_extcsd_value(fd, EXT_CSD_USE_NATIVE_SECTOR, 1); 238 | 239 | if (ret) { 240 | fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n", 241 | 1, EXT_CSD_BOOT_WP, device); 242 | exit(1); 243 | } 244 | printf("MMC disable 512B emulation successful. Now reset the device to switch to 4KB native sector mode.\n"); 245 | } else if (native_sector_size && data_sector_size) { 246 | printf("MMC 512B emulation mode is already disabled; doing nothing.\n"); 247 | } else { 248 | printf("MMC does not support disabling 512B emulation mode.\n"); 249 | } 250 | 251 | return ret; 252 | } 253 | 254 | int do_write_boot_en(int nargs, char **argv) 255 | { 256 | __u8 ext_csd[512]; 257 | __u8 value = 0; 258 | int fd, ret; 259 | char *device; 260 | int boot_area, send_ack; 261 | 262 | CHECK(nargs != 4, "Usage: mmc bootpart enable " 263 | " \n", exit(1)); 264 | 265 | /* 266 | * If is 1, the device will send acknowledgment 267 | * pattern "010" to the host when boot operation begins. 268 | * If is 0, it won't. 269 | */ 270 | boot_area = strtol(argv[1], NULL, 10); 271 | send_ack = strtol(argv[2], NULL, 10); 272 | device = argv[3]; 273 | 274 | fd = open(device, O_RDWR); 275 | if (fd < 0) { 276 | perror("open"); 277 | exit(1); 278 | } 279 | 280 | ret = read_extcsd(fd, ext_csd); 281 | if (ret) { 282 | fprintf(stderr, "Could not read EXT_CSD from %s\n", device); 283 | exit(1); 284 | } 285 | 286 | value = ext_csd[EXT_CSD_PART_CONFIG]; 287 | 288 | switch (boot_area) { 289 | case EXT_CSD_PART_CONFIG_ACC_BOOT0: 290 | value |= (1 << 3); 291 | value &= ~(3 << 4); 292 | break; 293 | case EXT_CSD_PART_CONFIG_ACC_BOOT1: 294 | value |= (1 << 4); 295 | value &= ~(1 << 3); 296 | value &= ~(1 << 5); 297 | break; 298 | case EXT_CSD_PART_CONFIG_ACC_USER_AREA: 299 | value |= (boot_area << 3); 300 | break; 301 | default: 302 | fprintf(stderr, "Cannot enable the boot area\n"); 303 | exit(1); 304 | } 305 | if (send_ack) 306 | value |= EXT_CSD_PART_CONFIG_ACC_ACK; 307 | else 308 | value &= ~EXT_CSD_PART_CONFIG_ACC_ACK; 309 | 310 | ret = write_extcsd_value(fd, EXT_CSD_PART_CONFIG, value); 311 | if (ret) { 312 | fprintf(stderr, "Could not write 0x%02x to " 313 | "EXT_CSD[%d] in %s\n", 314 | value, EXT_CSD_PART_CONFIG, device); 315 | exit(1); 316 | } 317 | return ret; 318 | } 319 | 320 | int do_hwreset(int value, int nargs, char **argv) 321 | { 322 | __u8 ext_csd[512]; 323 | int fd, ret; 324 | char *device; 325 | 326 | CHECK(nargs != 2, "Usage: mmc hwreset enable \n", 327 | exit(1)); 328 | 329 | device = argv[1]; 330 | 331 | fd = open(device, O_RDWR); 332 | if (fd < 0) { 333 | perror("open"); 334 | exit(1); 335 | } 336 | 337 | ret = read_extcsd(fd, ext_csd); 338 | if (ret) { 339 | fprintf(stderr, "Could not read EXT_CSD from %s\n", device); 340 | exit(1); 341 | } 342 | 343 | if ((ext_csd[EXT_CSD_RST_N_FUNCTION] & EXT_CSD_RST_N_EN_MASK) == 344 | EXT_CSD_HW_RESET_EN) { 345 | fprintf(stderr, 346 | "H/W Reset is already permanently enabled on %s\n", 347 | device); 348 | exit(1); 349 | } 350 | if ((ext_csd[EXT_CSD_RST_N_FUNCTION] & EXT_CSD_RST_N_EN_MASK) == 351 | EXT_CSD_HW_RESET_DIS) { 352 | fprintf(stderr, 353 | "H/W Reset is already permanently disabled on %s\n", 354 | device); 355 | exit(1); 356 | } 357 | 358 | ret = write_extcsd_value(fd, EXT_CSD_RST_N_FUNCTION, value); 359 | if (ret) { 360 | fprintf(stderr, 361 | "Could not write 0x%02x to EXT_CSD[%d] in %s\n", 362 | value, EXT_CSD_RST_N_FUNCTION, device); 363 | exit(1); 364 | } 365 | 366 | return ret; 367 | } 368 | 369 | int do_hwreset_en(int nargs, char **argv) 370 | { 371 | return do_hwreset(EXT_CSD_HW_RESET_EN, nargs, argv); 372 | } 373 | 374 | int do_hwreset_dis(int nargs, char **argv) 375 | { 376 | return do_hwreset(EXT_CSD_HW_RESET_DIS, nargs, argv); 377 | } 378 | 379 | int do_write_bkops_en(int nargs, char **argv) 380 | { 381 | __u8 ext_csd[512], value = 0; 382 | int fd, ret; 383 | char *device; 384 | 385 | CHECK(nargs != 2, "Usage: mmc bkops enable \n", 386 | exit(1)); 387 | 388 | device = argv[1]; 389 | 390 | fd = open(device, O_RDWR); 391 | if (fd < 0) { 392 | perror("open"); 393 | exit(1); 394 | } 395 | 396 | ret = read_extcsd(fd, ext_csd); 397 | if (ret) { 398 | fprintf(stderr, "Could not read EXT_CSD from %s\n", device); 399 | exit(1); 400 | } 401 | 402 | if (!(ext_csd[EXT_CSD_BKOPS_SUPPORT] & 0x1)) { 403 | fprintf(stderr, "%s doesn't support BKOPS\n", device); 404 | exit(1); 405 | } 406 | 407 | ret = write_extcsd_value(fd, EXT_CSD_BKOPS_EN, BKOPS_ENABLE); 408 | if (ret) { 409 | fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n", 410 | value, EXT_CSD_BKOPS_EN, device); 411 | exit(1); 412 | } 413 | 414 | return ret; 415 | } 416 | 417 | int do_status_get(int nargs, char **argv) 418 | { 419 | __u32 response; 420 | int fd, ret; 421 | char *device; 422 | 423 | CHECK(nargs != 2, "Usage: mmc status get \n", 424 | exit(1)); 425 | 426 | device = argv[1]; 427 | 428 | fd = open(device, O_RDWR); 429 | if (fd < 0) { 430 | perror("open"); 431 | exit(1); 432 | } 433 | 434 | ret = send_status(fd, &response); 435 | if (ret) { 436 | fprintf(stderr, "Could not read response to SEND_STATUS from %s\n", device); 437 | exit(1); 438 | } 439 | 440 | printf("SEND_STATUS response: 0x%08x\n", response); 441 | 442 | return ret; 443 | } 444 | 445 | unsigned int get_sector_count(__u8 *ext_csd) 446 | { 447 | return (ext_csd[EXT_CSD_SEC_COUNT_3] << 24) | 448 | (ext_csd[EXT_CSD_SEC_COUNT_2] << 16) | 449 | (ext_csd[EXT_CSD_SEC_COUNT_1] << 8) | 450 | ext_csd[EXT_CSD_SEC_COUNT_0]; 451 | } 452 | 453 | int is_blockaddresed(__u8 *ext_csd) 454 | { 455 | unsigned int sectors = get_sector_count(ext_csd); 456 | 457 | return (sectors > (2u * 1024 * 1024 * 1024) / 512); 458 | } 459 | 460 | unsigned int get_hc_wp_grp_size(__u8 *ext_csd) 461 | { 462 | return ext_csd[221]; 463 | } 464 | 465 | unsigned int get_hc_erase_grp_size(__u8 *ext_csd) 466 | { 467 | return ext_csd[224]; 468 | } 469 | 470 | int do_enh_area_set(int nargs, char **argv) 471 | { 472 | __u8 value; 473 | __u8 ext_csd[512]; 474 | int fd, ret; 475 | char *device; 476 | int dry_run = 1; 477 | unsigned int start_kib, length_kib, enh_start_addr, enh_size_mult; 478 | unsigned long align; 479 | 480 | CHECK(nargs != 5, "Usage: mmc enh_area set <-y|-n> " 481 | "\n", exit(1)); 482 | 483 | if (!strcmp("-y", argv[1])) 484 | dry_run = 0; 485 | 486 | start_kib = strtol(argv[2], NULL, 10); 487 | length_kib = strtol(argv[3], NULL, 10); 488 | device = argv[4]; 489 | 490 | fd = open(device, O_RDWR); 491 | if (fd < 0) { 492 | perror("open"); 493 | exit(1); 494 | } 495 | 496 | ret = read_extcsd(fd, ext_csd); 497 | if (ret) { 498 | fprintf(stderr, "Could not read EXT_CSD from %s\n", device); 499 | exit(1); 500 | } 501 | 502 | /* assert ENH_ATTRIBUTE_EN */ 503 | if (!(ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & EXT_CSD_ENH_ATTRIBUTE_EN)) 504 | { 505 | printf(" Device cannot have enhanced tech.\n"); 506 | exit(1); 507 | } 508 | 509 | /* assert not PARTITION_SETTING_COMPLETED */ 510 | if (ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED]) 511 | { 512 | printf(" Device is already partitioned\n"); 513 | exit(1); 514 | } 515 | 516 | align = 512l * get_hc_wp_grp_size(ext_csd) * get_hc_erase_grp_size(ext_csd); 517 | 518 | enh_size_mult = (length_kib + align/2l) / align; 519 | 520 | enh_start_addr = start_kib * 1024 / (is_blockaddresed(ext_csd) ? 512 : 1); 521 | enh_start_addr /= align; 522 | enh_start_addr *= align; 523 | 524 | /* set EXT_CSD_ERASE_GROUP_DEF bit 0 */ 525 | ret = write_extcsd_value(fd, EXT_CSD_ERASE_GROUP_DEF, 0x1); 526 | if (ret) { 527 | fprintf(stderr, "Could not write 0x1 to " 528 | "EXT_CSD[%d] in %s\n", 529 | EXT_CSD_ERASE_GROUP_DEF, device); 530 | exit(1); 531 | } 532 | 533 | /* write to ENH_START_ADDR and ENH_SIZE_MULT and PARTITIONS_ATTRIBUTE's ENH_USR bit */ 534 | value = (enh_start_addr >> 24) & 0xff; 535 | ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_3, value); 536 | if (ret) { 537 | fprintf(stderr, "Could not write 0x%02x to " 538 | "EXT_CSD[%d] in %s\n", value, 539 | EXT_CSD_ENH_START_ADDR_3, device); 540 | exit(1); 541 | } 542 | value = (enh_start_addr >> 16) & 0xff; 543 | ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_2, value); 544 | if (ret) { 545 | fprintf(stderr, "Could not write 0x%02x to " 546 | "EXT_CSD[%d] in %s\n", value, 547 | EXT_CSD_ENH_START_ADDR_2, device); 548 | exit(1); 549 | } 550 | value = (enh_start_addr >> 8) & 0xff; 551 | ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_1, value); 552 | if (ret) { 553 | fprintf(stderr, "Could not write 0x%02x to " 554 | "EXT_CSD[%d] in %s\n", value, 555 | EXT_CSD_ENH_START_ADDR_1, device); 556 | exit(1); 557 | } 558 | value = enh_start_addr & 0xff; 559 | ret = write_extcsd_value(fd, EXT_CSD_ENH_START_ADDR_0, value); 560 | if (ret) { 561 | fprintf(stderr, "Could not write 0x%02x to " 562 | "EXT_CSD[%d] in %s\n", value, 563 | EXT_CSD_ENH_START_ADDR_0, device); 564 | exit(1); 565 | } 566 | 567 | value = (enh_size_mult >> 16) & 0xff; 568 | ret = write_extcsd_value(fd, EXT_CSD_ENH_SIZE_MULT_2, value); 569 | if (ret) { 570 | fprintf(stderr, "Could not write 0x%02x to " 571 | "EXT_CSD[%d] in %s\n", value, 572 | EXT_CSD_ENH_SIZE_MULT_2, device); 573 | exit(1); 574 | } 575 | value = (enh_size_mult >> 8) & 0xff; 576 | ret = write_extcsd_value(fd, EXT_CSD_ENH_SIZE_MULT_1, value); 577 | if (ret) { 578 | fprintf(stderr, "Could not write 0x%02x to " 579 | "EXT_CSD[%d] in %s\n", value, 580 | EXT_CSD_ENH_SIZE_MULT_1, device); 581 | exit(1); 582 | } 583 | value = enh_size_mult & 0xff; 584 | ret = write_extcsd_value(fd, EXT_CSD_ENH_SIZE_MULT_0, value); 585 | if (ret) { 586 | fprintf(stderr, "Could not write 0x%02x to " 587 | "EXT_CSD[%d] in %s\n", value, 588 | EXT_CSD_ENH_SIZE_MULT_0, device); 589 | exit(1); 590 | } 591 | 592 | ret = write_extcsd_value(fd, EXT_CSD_PARTITIONS_ATTRIBUTE, EXT_CSD_ENH_USR); 593 | if (ret) { 594 | fprintf(stderr, "Could not write EXT_CSD_ENH_USR to " 595 | "EXT_CSD[%d] in %s\n", 596 | EXT_CSD_PARTITIONS_ATTRIBUTE, device); 597 | exit(1); 598 | } 599 | 600 | if (dry_run) 601 | { 602 | fprintf(stderr, "NOT setting PARTITION_SETTING_COMPLETED\n"); 603 | exit(1); 604 | } 605 | 606 | fprintf(stderr, "setting OTP PARTITION_SETTING_COMPLETED!\n"); 607 | ret = write_extcsd_value(fd, EXT_CSD_PARTITION_SETTING_COMPLETED, 0x1); 608 | if (ret) { 609 | fprintf(stderr, "Could not write 0x1 to " 610 | "EXT_CSD[%d] in %s\n", 611 | EXT_CSD_PARTITION_SETTING_COMPLETED, device); 612 | exit(1); 613 | } 614 | 615 | __u32 response; 616 | ret = send_status(fd, &response); 617 | if (ret) { 618 | fprintf(stderr, "Could not get response to SEND_STATUS from %s\n", device); 619 | exit(1); 620 | } 621 | 622 | if (response & R1_SWITCH_ERROR) 623 | { 624 | fprintf(stderr, "Setting ENH_USR area failed on %s\n", device); 625 | exit(1); 626 | } 627 | 628 | fprintf(stderr, "Setting ENH_USR area on %s SUCCESS\n", device); 629 | fprintf(stderr, "Device power cycle needed for settings to take effect.\n" 630 | "Confirm that PARTITION_SETTING_COMPLETED bit is set using 'extcsd read'" 631 | "after power cycle\n"); 632 | 633 | return 0; 634 | } 635 | 636 | int do_read_extcsd(int nargs, char **argv) 637 | { 638 | __u8 ext_csd[512], ext_csd_rev, reg; 639 | int fd, ret; 640 | char *device; 641 | const char *str; 642 | 643 | CHECK(nargs != 2, "Usage: mmc extcsd read \n", 644 | exit(1)); 645 | 646 | device = argv[1]; 647 | 648 | fd = open(device, O_RDWR); 649 | if (fd < 0) { 650 | perror("open"); 651 | exit(1); 652 | } 653 | 654 | ret = read_extcsd(fd, ext_csd); 655 | if (ret) { 656 | fprintf(stderr, "Could not read EXT_CSD from %s\n", device); 657 | exit(1); 658 | } 659 | 660 | ext_csd_rev = ext_csd[192]; 661 | 662 | switch (ext_csd_rev) { 663 | case 6: 664 | str = "4.5"; 665 | break; 666 | case 5: 667 | str = "4.41"; 668 | break; 669 | case 3: 670 | str = "4.3"; 671 | break; 672 | case 2: 673 | str = "4.2"; 674 | break; 675 | case 1: 676 | str = "4.1"; 677 | break; 678 | case 0: 679 | str = "4.0"; 680 | break; 681 | default: 682 | goto out_free; 683 | } 684 | printf("=============================================\n"); 685 | printf(" Extended CSD rev 1.%d (MMC %s)\n", ext_csd_rev, str); 686 | printf("=============================================\n\n"); 687 | 688 | if (ext_csd_rev < 3) 689 | goto out_free; /* No ext_csd */ 690 | 691 | /* Parse the Extended CSD registers. 692 | * Reserved bit should be read as "0" in case of spec older 693 | * than A441. 694 | */ 695 | reg = ext_csd[EXT_CSD_S_CMD_SET]; 696 | printf("Card Supported Command sets [S_CMD_SET: 0x%02x]\n", reg); 697 | if (!reg) 698 | printf(" - Standard MMC command sets\n"); 699 | 700 | reg = ext_csd[EXT_CSD_HPI_FEATURE]; 701 | printf("HPI Features [HPI_FEATURE: 0x%02x]: ", reg); 702 | if (reg & EXT_CSD_HPI_SUPP) { 703 | if (reg & EXT_CSD_HPI_IMPL) 704 | printf("implementation based on CMD12\n"); 705 | else 706 | printf("implementation based on CMD13\n"); 707 | } 708 | 709 | printf("Background operations support [BKOPS_SUPPORT: 0x%02x]\n", 710 | ext_csd[502]); 711 | 712 | if (ext_csd_rev >= 6) { 713 | printf("Max Packet Read Cmd [MAX_PACKED_READS: 0x%02x]\n", 714 | ext_csd[501]); 715 | printf("Max Packet Write Cmd [MAX_PACKED_WRITES: 0x%02x]\n", 716 | ext_csd[500]); 717 | printf("Data TAG support [DATA_TAG_SUPPORT: 0x%02x]\n", 718 | ext_csd[499]); 719 | 720 | printf("Data TAG Unit Size [TAG_UNIT_SIZE: 0x%02x]\n", 721 | ext_csd[498]); 722 | printf("Tag Resources Size [TAG_RES_SIZE: 0x%02x]\n", 723 | ext_csd[497]); 724 | printf("Context Management Capabilities" 725 | " [CONTEXT_CAPABILITIES: 0x%02x]\n", ext_csd[496]); 726 | printf("Large Unit Size [LARGE_UNIT_SIZE_M1: 0x%02x]\n", 727 | ext_csd[495]); 728 | printf("Extended partition attribute support" 729 | " [EXT_SUPPORT: 0x%02x]\n", ext_csd[494]); 730 | printf("Generic CMD6 Timer [GENERIC_CMD6_TIME: 0x%02x]\n", 731 | ext_csd[248]); 732 | printf("Power off notification [POWER_OFF_LONG_TIME: 0x%02x]\n", 733 | ext_csd[247]); 734 | printf("Cache Size [CACHE_SIZE] is %d KiB\n", 735 | ext_csd[249] << 0 | (ext_csd[250] << 8) | 736 | (ext_csd[251] << 16) | (ext_csd[252] << 24)); 737 | } 738 | 739 | /* A441: Reserved [501:247] 740 | A43: reserved [246:229] */ 741 | if (ext_csd_rev >= 5) { 742 | printf("Background operations status" 743 | " [BKOPS_STATUS: 0x%02x]\n", ext_csd[246]); 744 | 745 | /* CORRECTLY_PRG_SECTORS_NUM [245:242] TODO */ 746 | 747 | printf("1st Initialisation Time after programmed sector" 748 | " [INI_TIMEOUT_AP: 0x%02x]\n", ext_csd[241]); 749 | 750 | /* A441: reserved [240] */ 751 | printf("Power class for 52MHz, DDR at 3.6V" 752 | " [PWR_CL_DDR_52_360: 0x%02x]\n", ext_csd[239]); 753 | printf("Power class for 52MHz, DDR at 1.95V" 754 | " [PWR_CL_DDR_52_195: 0x%02x]\n", ext_csd[238]); 755 | 756 | /* A441: reserved [237-236] */ 757 | 758 | if (ext_csd_rev >= 6) { 759 | printf("Power class for 200MHz at 3.6V" 760 | " [PWR_CL_200_360: 0x%02x]\n", ext_csd[237]); 761 | printf("Power class for 200MHz, at 1.95V" 762 | " [PWR_CL_200_195: 0x%02x]\n", ext_csd[236]); 763 | } 764 | printf("Minimum Performance for 8bit at 52MHz in DDR mode:\n"); 765 | printf(" [MIN_PERF_DDR_W_8_52: 0x%02x]\n", ext_csd[235]); 766 | printf(" [MIN_PERF_DDR_R_8_52: 0x%02x]\n", ext_csd[234]); 767 | /* A441: reserved [233] */ 768 | printf("TRIM Multiplier [TRIM_MULT: 0x%02x]\n", ext_csd[232]); 769 | printf("Secure Feature support [SEC_FEATURE_SUPPORT: 0x%02x]\n", 770 | ext_csd[231]); 771 | } 772 | if (ext_csd_rev == 5) { /* Obsolete in 4.5 */ 773 | printf("Secure Erase Multiplier [SEC_ERASE_MULT: 0x%02x]\n", 774 | ext_csd[230]); 775 | printf("Secure TRIM Multiplier [SEC_TRIM_MULT: 0x%02x]\n", 776 | ext_csd[229]); 777 | } 778 | reg = ext_csd[EXT_CSD_BOOT_INFO]; 779 | printf("Boot Information [BOOT_INFO: 0x%02x]\n", reg); 780 | if (reg & EXT_CSD_BOOT_INFO_ALT) 781 | printf(" Device supports alternative boot method\n"); 782 | if (reg & EXT_CSD_BOOT_INFO_DDR_DDR) 783 | printf(" Device supports dual data rate during boot\n"); 784 | if (reg & EXT_CSD_BOOT_INFO_HS_MODE) 785 | printf(" Device supports high speed timing during boot\n"); 786 | 787 | /* A441/A43: reserved [227] */ 788 | printf("Boot partition size [BOOT_SIZE_MULTI: 0x%02x]\n", ext_csd[226]); 789 | printf("Access size [ACC_SIZE: 0x%02x]\n", ext_csd[225]); 790 | 791 | reg = get_hc_erase_grp_size(ext_csd); 792 | printf("High-capacity erase unit size [HC_ERASE_GRP_SIZE: 0x%02x]\n", 793 | reg); 794 | printf(" i.e. %u KiB\n", 512 * reg); 795 | 796 | printf("High-capacity erase timeout [ERASE_TIMEOUT_MULT: 0x%02x]\n", 797 | ext_csd[223]); 798 | printf("Reliable write sector count [REL_WR_SEC_C: 0x%02x]\n", 799 | ext_csd[222]); 800 | 801 | reg = get_hc_wp_grp_size(ext_csd); 802 | printf("High-capacity W protect group size [HC_WP_GRP_SIZE: 0x%02x]\n", 803 | reg); 804 | printf(" i.e. %lu KiB\n", 512l * get_hc_erase_grp_size(ext_csd) * reg); 805 | 806 | printf("Sleep current (VCC) [S_C_VCC: 0x%02x]\n", ext_csd[220]); 807 | printf("Sleep current (VCCQ) [S_C_VCCQ: 0x%02x]\n", ext_csd[219]); 808 | /* A441/A43: reserved [218] */ 809 | printf("Sleep/awake timeout [S_A_TIMEOUT: 0x%02x]\n", ext_csd[217]); 810 | /* A441/A43: reserved [216] */ 811 | 812 | unsigned int sectors = get_sector_count(ext_csd); 813 | printf("Sector Count [SEC_COUNT: 0x%08x]\n", sectors); 814 | if (is_blockaddresed(ext_csd)) 815 | printf(" Device is block-addressed\n"); 816 | else 817 | printf(" Device is NOT block-addressed\n"); 818 | 819 | /* A441/A43: reserved [211] */ 820 | printf("Minimum Write Performance for 8bit:\n"); 821 | printf(" [MIN_PERF_W_8_52: 0x%02x]\n", ext_csd[210]); 822 | printf(" [MIN_PERF_R_8_52: 0x%02x]\n", ext_csd[209]); 823 | printf(" [MIN_PERF_W_8_26_4_52: 0x%02x]\n", ext_csd[208]); 824 | printf(" [MIN_PERF_R_8_26_4_52: 0x%02x]\n", ext_csd[207]); 825 | printf("Minimum Write Performance for 4bit:\n"); 826 | printf(" [MIN_PERF_W_4_26: 0x%02x]\n", ext_csd[206]); 827 | printf(" [MIN_PERF_R_4_26: 0x%02x]\n", ext_csd[205]); 828 | /* A441/A43: reserved [204] */ 829 | printf("Power classes registers:\n"); 830 | printf(" [PWR_CL_26_360: 0x%02x]\n", ext_csd[203]); 831 | printf(" [PWR_CL_52_360: 0x%02x]\n", ext_csd[202]); 832 | printf(" [PWR_CL_26_195: 0x%02x]\n", ext_csd[201]); 833 | printf(" [PWR_CL_52_195: 0x%02x]\n", ext_csd[200]); 834 | 835 | /* A43: reserved [199:198] */ 836 | if (ext_csd_rev >= 5) { 837 | printf("Partition switching timing " 838 | "[PARTITION_SWITCH_TIME: 0x%02x]\n", ext_csd[199]); 839 | printf("Out-of-interrupt busy timing" 840 | " [OUT_OF_INTERRUPT_TIME: 0x%02x]\n", ext_csd[198]); 841 | } 842 | 843 | /* A441/A43: reserved [197] [195] [193] [190] [188] 844 | * [186] [184] [182] [180] [176] */ 845 | 846 | if (ext_csd_rev >= 6) 847 | printf("I/O Driver Strength [DRIVER_STRENGTH: 0x%02x]\n", 848 | ext_csd[197]); 849 | 850 | /* DEVICE_TYPE in A45, CARD_TYPE in A441 */ 851 | reg = ext_csd[196]; 852 | printf("Card Type [CARD_TYPE: 0x%02x]\n", reg); 853 | if (reg & 0x20) printf(" HS200 Single Data Rate eMMC @200MHz 1.2VI/O\n"); 854 | if (reg & 0x10) printf(" HS200 Single Data Rate eMMC @200MHz 1.8VI/O\n"); 855 | if (reg & 0x08) printf(" HS Dual Data Rate eMMC @52MHz 1.2VI/O\n"); 856 | if (reg & 0x04) printf(" HS Dual Data Rate eMMC @52MHz 1.8V or 3VI/O\n"); 857 | if (reg & 0x02) printf(" HS eMMC @52MHz - at rated device voltage(s)\n"); 858 | if (reg & 0x01) printf(" HS eMMC @26MHz - at rated device voltage(s)\n"); 859 | 860 | printf("CSD structure version [CSD_STRUCTURE: 0x%02x]\n", ext_csd[194]); 861 | /* ext_csd_rev = ext_csd[192] (already done!!!) */ 862 | printf("Command set [CMD_SET: 0x%02x]\n", ext_csd[191]); 863 | printf("Command set revision [CMD_SET_REV: 0x%02x]\n", ext_csd[189]); 864 | printf("Power class [POWER_CLASS: 0x%02x]\n", ext_csd[187]); 865 | printf("High-speed interface timing [HS_TIMING: 0x%02x]\n", 866 | ext_csd[185]); 867 | /* bus_width: ext_csd[183] not readable */ 868 | printf("Erased memory content [ERASED_MEM_CONT: 0x%02x]\n", 869 | ext_csd[181]); 870 | reg = ext_csd[EXT_CSD_BOOT_CFG]; 871 | printf("Boot configuration bytes [PARTITION_CONFIG: 0x%02x]\n", reg); 872 | switch ((reg & EXT_CSD_BOOT_CFG_EN)>>3) { 873 | case 0x0: 874 | printf(" Not boot enable\n"); 875 | break; 876 | case 0x1: 877 | printf(" Boot Partition 1 enabled\n"); 878 | break; 879 | case 0x2: 880 | printf(" Boot Partition 2 enabled\n"); 881 | break; 882 | case 0x7: 883 | printf(" User Area Enabled for boot\n"); 884 | break; 885 | } 886 | switch (reg & EXT_CSD_BOOT_CFG_ACC) { 887 | case 0x0: 888 | printf(" No access to boot partition\n"); 889 | break; 890 | case 0x1: 891 | printf(" R/W Boot Partition 1\n"); 892 | break; 893 | case 0x2: 894 | printf(" R/W Boot Partition 2\n"); 895 | break; 896 | case 0x3: 897 | printf(" R/W Replay Protected Memory Block (RPMB)\n"); 898 | break; 899 | default: 900 | printf(" Access to General Purpose partition %d\n", 901 | (reg & EXT_CSD_BOOT_CFG_ACC) - 3); 902 | break; 903 | } 904 | 905 | printf("Boot config protection [BOOT_CONFIG_PROT: 0x%02x]\n", 906 | ext_csd[178]); 907 | printf("Boot bus Conditions [BOOT_BUS_CONDITIONS: 0x%02x]\n", 908 | ext_csd[177]); 909 | printf("High-density erase group definition" 910 | " [ERASE_GROUP_DEF: 0x%02x]\n", ext_csd[EXT_CSD_ERASE_GROUP_DEF]); 911 | 912 | print_writeprotect_status(ext_csd); 913 | 914 | if (ext_csd_rev >= 5) { 915 | /* A441]: reserved [172] */ 916 | printf("User area write protection register" 917 | " [USER_WP]: 0x%02x\n", ext_csd[171]); 918 | /* A441]: reserved [170] */ 919 | printf("FW configuration [FW_CONFIG]: 0x%02x\n", ext_csd[169]); 920 | printf("RPMB Size [RPMB_SIZE_MULT]: 0x%02x\n", ext_csd[168]); 921 | printf("Write reliability setting register" 922 | " [WR_REL_SET]: 0x%02x\n", ext_csd[167]); 923 | printf("Write reliability parameter register" 924 | " [WR_REL_PARAM]: 0x%02x\n", ext_csd[166]); 925 | /* sanitize_start ext_csd[165]]: not readable 926 | * bkops_start ext_csd[164]]: only writable */ 927 | printf("Enable background operations handshake" 928 | " [BKOPS_EN]: 0x%02x\n", ext_csd[163]); 929 | printf("H/W reset function" 930 | " [RST_N_FUNCTION]: 0x%02x\n", ext_csd[162]); 931 | printf("HPI management [HPI_MGMT]: 0x%02x\n", ext_csd[161]); 932 | reg = ext_csd[EXT_CSD_PARTITIONING_SUPPORT]; 933 | printf("Partitioning Support [PARTITIONING_SUPPORT]: 0x%02x\n", 934 | reg); 935 | if (reg & EXT_CSD_PARTITIONING_EN) 936 | printf(" Device support partitioning feature\n"); 937 | else 938 | printf(" Device NOT support partitioning feature\n"); 939 | if (reg & EXT_CSD_ENH_ATTRIBUTE_EN) 940 | printf(" Device can have enhanced tech.\n"); 941 | else 942 | printf(" Device cannot have enhanced tech.\n"); 943 | 944 | reg = (ext_csd[159] << 16) | (ext_csd[158] << 8) | 945 | ext_csd[157]; 946 | printf("Max Enhanced Area Size [MAX_ENH_SIZE_MULT]: 0x%06x\n", 947 | reg); 948 | unsigned int wp_sz = get_hc_wp_grp_size(ext_csd); 949 | unsigned int erase_sz = get_hc_erase_grp_size(ext_csd); 950 | printf(" i.e. %lu KiB\n", 512l * reg * wp_sz * erase_sz); 951 | 952 | printf("Partitions attribute [PARTITIONS_ATTRIBUTE]: 0x%02x\n", 953 | ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE]); 954 | reg = ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED]; 955 | printf("Partitioning Setting" 956 | " [PARTITION_SETTING_COMPLETED]: 0x%02x\n", 957 | reg); 958 | if (reg) 959 | printf(" Device partition setting complete\n"); 960 | else 961 | printf(" Device partition setting NOT complete\n"); 962 | 963 | printf("General Purpose Partition Size\n" 964 | " [GP_SIZE_MULT_4]: 0x%06x\n", (ext_csd[154] << 16) | 965 | (ext_csd[153] << 8) | ext_csd[152]); 966 | printf(" [GP_SIZE_MULT_3]: 0x%06x\n", (ext_csd[151] << 16) | 967 | (ext_csd[150] << 8) | ext_csd[149]); 968 | printf(" [GP_SIZE_MULT_2]: 0x%06x\n", (ext_csd[148] << 16) | 969 | (ext_csd[147] << 8) | ext_csd[146]); 970 | printf(" [GP_SIZE_MULT_1]: 0x%06x\n", (ext_csd[145] << 16) | 971 | (ext_csd[144] << 8) | ext_csd[143]); 972 | 973 | reg = (ext_csd[EXT_CSD_ENH_SIZE_MULT_2] << 16) | 974 | (ext_csd[EXT_CSD_ENH_SIZE_MULT_1] << 8) | 975 | ext_csd[EXT_CSD_ENH_SIZE_MULT_0]; 976 | printf("Enhanced User Data Area Size" 977 | " [ENH_SIZE_MULT]: 0x%06x\n", reg); 978 | printf(" i.e. %lu KiB\n", 512l * reg * 979 | get_hc_erase_grp_size(ext_csd) * 980 | get_hc_wp_grp_size(ext_csd)); 981 | 982 | reg = (ext_csd[EXT_CSD_ENH_START_ADDR_3] << 24) | 983 | (ext_csd[EXT_CSD_ENH_START_ADDR_2] << 16) | 984 | (ext_csd[EXT_CSD_ENH_START_ADDR_1] << 8) | 985 | ext_csd[EXT_CSD_ENH_START_ADDR_0]; 986 | printf("Enhanced User Data Start Address" 987 | " [ENH_START_ADDR]: 0x%06x\n", reg); 988 | printf(" i.e. %lu bytes offset\n", (is_blockaddresed(ext_csd) ? 989 | 1l : 512l) * reg); 990 | 991 | /* A441]: reserved [135] */ 992 | printf("Bad Block Management mode" 993 | " [SEC_BAD_BLK_MGMNT]: 0x%02x\n", ext_csd[134]); 994 | /* A441: reserved [133:0] */ 995 | } 996 | /* B45 */ 997 | if (ext_csd_rev >= 6) { 998 | int j; 999 | /* tcase_support ext_csd[132] not readable */ 1000 | printf("Periodic Wake-up [PERIODIC_WAKEUP]: 0x%02x\n", 1001 | ext_csd[131]); 1002 | printf("Program CID/CSD in DDR mode support" 1003 | " [PROGRAM_CID_CSD_DDR_SUPPORT]: 0x%02x\n", 1004 | ext_csd[130]); 1005 | 1006 | for (j = 127; j >= 64; j--) 1007 | printf("Vendor Specific Fields" 1008 | " [VENDOR_SPECIFIC_FIELD[%d]]: 0x%02x\n", 1009 | j, ext_csd[j]); 1010 | 1011 | printf("Native sector size [NATIVE_SECTOR_SIZE]: 0x%02x\n", 1012 | ext_csd[63]); 1013 | printf("Sector size emulation [USE_NATIVE_SECTOR]: 0x%02x\n", 1014 | ext_csd[62]); 1015 | printf("Sector size [DATA_SECTOR_SIZE]: 0x%02x\n", ext_csd[61]); 1016 | printf("1st initialization after disabling sector" 1017 | " size emulation [INI_TIMEOUT_EMU]: 0x%02x\n", 1018 | ext_csd[60]); 1019 | printf("Class 6 commands control [CLASS_6_CTRL]: 0x%02x\n", 1020 | ext_csd[59]); 1021 | printf("Number of addressed group to be Released" 1022 | "[DYNCAP_NEEDED]: 0x%02x\n", ext_csd[58]); 1023 | printf("Exception events control" 1024 | " [EXCEPTION_EVENTS_CTRL]: 0x%04x\n", 1025 | (ext_csd[57] << 8) | ext_csd[56]); 1026 | printf("Exception events status" 1027 | "[EXCEPTION_EVENTS_STATUS]: 0x%04x\n", 1028 | (ext_csd[55] << 8) | ext_csd[54]); 1029 | printf("Extended Partitions Attribute" 1030 | " [EXT_PARTITIONS_ATTRIBUTE]: 0x%04x\n", 1031 | (ext_csd[53] << 8) | ext_csd[52]); 1032 | 1033 | for (j = 51; j >= 37; j--) 1034 | printf("Context configuration" 1035 | " [CONTEXT_CONF[%d]]: 0x%02x\n", j, ext_csd[j]); 1036 | 1037 | printf("Packed command status" 1038 | " [PACKED_COMMAND_STATUS]: 0x%02x\n", ext_csd[36]); 1039 | printf("Packed command failure index" 1040 | " [PACKED_FAILURE_INDEX]: 0x%02x\n", ext_csd[35]); 1041 | printf("Power Off Notification" 1042 | " [POWER_OFF_NOTIFICATION]: 0x%02x\n", ext_csd[34]); 1043 | printf("Control to turn the Cache ON/OFF" 1044 | " [CACHE_CTRL]: 0x%02x\n", ext_csd[33]); 1045 | /* flush_cache ext_csd[32] not readable */ 1046 | /*Reserved [31:0] */ 1047 | } 1048 | 1049 | out_free: 1050 | return ret; 1051 | } 1052 | 1053 | int do_sanitize(int nargs, char **argv) 1054 | { 1055 | int fd, ret; 1056 | char *device; 1057 | 1058 | CHECK(nargs != 2, "Usage: mmc sanitize \n", 1059 | exit(1)); 1060 | 1061 | device = argv[1]; 1062 | 1063 | fd = open(device, O_RDWR); 1064 | if (fd < 0) { 1065 | perror("open"); 1066 | exit(1); 1067 | } 1068 | 1069 | ret = write_extcsd_value(fd, EXT_CSD_SANITIZE_START, 1); 1070 | if (ret) { 1071 | fprintf(stderr, "Could not write 0x%02x to EXT_CSD[%d] in %s\n", 1072 | 1, EXT_CSD_SANITIZE_START, device); 1073 | exit(1); 1074 | } 1075 | 1076 | return ret; 1077 | 1078 | } 1079 | 1080 | int do_vendor_cmd(int nargs, char **argv) 1081 | { 1082 | int fd, ret; 1083 | char *device; 1084 | __u8 ext_csd[512]; 1085 | unsigned int size; 1086 | 1087 | CHECK(nargs != 3, "Usage: mmc vendor 0|1|2 \n", 1088 | exit(1)); 1089 | 1090 | size = strtol(argv[1], NULL, 10); 1091 | device = argv[2]; 1092 | 1093 | fd = open(device, O_RDWR); 1094 | if (fd < 0) { 1095 | perror("open"); 1096 | exit(1); 1097 | } 1098 | 1099 | ret = write_vendor_cmd(fd, MMC_CMD62_ARG1); 1100 | if (ret) { 1101 | fprintf(stderr, "MMC_CMD62: Could not write ARG1 0x%08x to %s\n", MMC_CMD62_ARG1, device); 1102 | exit(1); 1103 | } 1104 | 1105 | ret = write_vendor_cmd(fd, MMC_CMD62_ARG2); 1106 | if (ret) { 1107 | fprintf(stderr, "MMC_CMD62: Could not write ARG2 0x%08x to %s\n", MMC_CMD62_ARG2, device); 1108 | exit(1); 1109 | } 1110 | 1111 | fprintf(stderr, "MMC_CMD62: writing bootsize=%u to %s\n", size, device); 1112 | 1113 | ret = write_vendor_cmd(fd, size); 1114 | if (ret) { 1115 | fprintf(stderr, "MMC_CMD62: Could not write bootsize %u to %s\n", size, device); 1116 | exit(1); 1117 | } 1118 | 1119 | return ret; 1120 | } 1121 | -------------------------------------------------------------------------------- /mmc_cmds.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This program is free software; you can redistribute it and/or 3 | * modify it under the terms of the GNU General Public 4 | * License v2 as published by the Free Software Foundation. 5 | * 6 | * This program is distributed in the hope that it will be useful, 7 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 9 | * General Public License for more details. 10 | * 11 | * You should have received a copy of the GNU General Public 12 | * License along with this program; if not, write to the 13 | * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 14 | * Boston, MA 021110-1307, USA. 15 | */ 16 | 17 | /* mmc_cmds.c */ 18 | int do_read_extcsd(int nargs, char **argv); 19 | int do_write_extcsd(int nargs, char **argv); 20 | int do_writeprotect_get(int nargs, char **argv); 21 | int do_writeprotect_set(int nargs, char **argv); 22 | int do_disable_512B_emulation(int nargs, char **argv); 23 | int do_write_boot_en(int nargs, char **argv); 24 | int do_write_bkops_en(int nargs, char **argv); 25 | int do_hwreset_en(int nargs, char **argv); 26 | int do_hwreset_dis(int nargs, char **argv); 27 | int do_sanitize(int nargs, char **argv); 28 | int do_status_get(int nargs, char **argv); 29 | int do_enh_area_set(int nargs, char **argv); 30 | int do_vendor_cmd(int nargs, char **argv); 31 | -------------------------------------------------------------------------------- /mmc_utils: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hashcode/mmc-utils/75d7fec3b607ee45271ff4bbea0b22c82c390029/mmc_utils --------------------------------------------------------------------------------