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 <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
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", "<device>\n"
55 		"Print extcsd data from <device>.",
56 	  NULL
57 	},
58 	{ do_writeprotect_get, -1,
59 	  "writeprotect get", "<device>\n"
60 		"Determine the eMMC writeprotect status of <device>.",
61 	  NULL
62 	},
63 	{ do_writeprotect_set, -1,
64 	  "writeprotect set", "<device>\n"
65 		"Set the eMMC writeprotect status of <device>.\nThis sets the eMMC to be write-protected until next boot.",
66 	  NULL
67 	},
68 	{ do_disable_512B_emulation, -1,
69 	  "disable 512B emulation", "<device>\n"
70 		"Set the eMMC data sector size to 4KB by disabling emulation on\n<device>.",
71 	  NULL
72 	},
73 	{ do_create_gp_partition, -6,
74 	  "gp create", "<-y|-n> " "<length KiB> " "<partition> " "<enh_attr> " "<ext_attr> " "<device>\n"
75 		"create general purpose partition for the <device>.\nDry-run only unless -y is passed.\nNOTE!  This is a one-time programmable (unreversible) change.\nTo set enhanced attribute to general partition being created set\n <enh_attr> to 1 else set it to 0.\nTo set extended attribute to general partition\n set <ext_attr> to 1,2 else set it to 0",
76 	  NULL
77 	},
78 	{ do_enh_area_set, -4,
79 	  "enh_area set", "<-y|-n> " "<start KiB> " "<length KiB> " "<device>\n"
80 		"Enable the enhanced user area for the <device>.\nDry-run only unless -y is passed.\nNOTE!  This is a one-time programmable (unreversible) change.",
81 	  NULL
82 	},
83 	{ do_write_reliability_set, -2,
84 	  "write_reliability set", "<-y|-n> " "<partition> " "<device>\n"
85 		"Enable write reliability per partition for the <device>.\nDry-run only unless -y is passed.\nNOTE!  This is a one-time programmable (unreversible) change.",
86 	  NULL
87 	},
88 	{ do_status_get, -1,
89 	  "status get", "<device>\n"
90 	  "Print the response to STATUS_SEND (CMD13).",
91 	  NULL
92 	},
93 	{ do_write_boot_en, -3,
94 	  "bootpart enable", "<boot_partition> " "<send_ack> " "<device>\n"
95 		"Enable the boot partition for the <device>.\nTo receive acknowledgment of boot from the card set <send_ack>\nto 1, else set it to 0.",
96 	  NULL
97 	},
98 	{ do_boot_bus_conditions_set, -4,
99 	  "bootbus set", "<boot_mode> " "<reset_boot_bus_conditions> " "<boot_bus_width> " "<device>\n"
100 	  "Set Boot Bus Conditions.\n"
101 	  "<boot_mode> must be \"single_backward|single_hs|dual\"\n"
102 	  "<reset_boot_bus_conditions> must be \"x1|retain\"\n"
103 	  "<boot_bus_width> must be \"x1|x4|x8\"",
104 	  NULL
105 	},
106 	{ do_write_bkops_en, -1,
107 	  "bkops enable", "<device>\n"
108 		"Enable the eMMC BKOPS feature on <device>.\nNOTE!  This is a one-time programmable (unreversible) change.",
109 	  NULL
110 	},
111 	{ do_hwreset_en, -1,
112 	  "hwreset enable", "<device>\n"
113 		"Permanently enable the eMMC H/W Reset feature on <device>.\nNOTE!  This is a one-time programmable (unreversible) change.",
114 	  NULL
115 	},
116 	{ do_hwreset_dis, -1,
117 	  "hwreset disable", "<device>\n"
118 		"Permanently disable the eMMC H/W Reset feature on <device>.\nNOTE!  This is a one-time programmable (unreversible) change.",
119 	  NULL
120 	},
121 	{ do_sanitize, -1,
122 	  "sanitize", "<device>\n"
123 		"Send Sanitize command to the <device>.\nThis will delete the unmapped memory region of the device.",
124 	  NULL
125 	},
126 	{ do_rpmb_write_key, -1,
127 	  "rpmb write-key", "<rpmb device> <key file>\n"
128 		  "Program authentication key which is 32 bytes length and stored\n"
129 		  "in the specified file. Also you can specify '-' instead of\n"
130 		  "key file path to read the key from stdin.\n"
131 		  "NOTE!  This is a one-time programmable (unreversible) change.\n"
132 		  "Example:\n"
133 		  "  $ echo -n AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHH | \\\n"
134 		  "    mmc rpmb write-key /dev/mmcblk0rpmb -",
135 	  NULL
136 	},
137 	{ do_rpmb_read_counter, -1,
138 	  "rpmb read-counter", "<rpmb device>\n"
139 		  "Counter value for the <rpmb device> will be read to stdout.",
140 	  NULL
141 	},
142 	{ do_rpmb_read_block, -1,
143 	  "rpmb read-block", "<rpmb device> <address> <blocks count> <output file> [key file]\n"
144 		  "Blocks of 256 bytes will be read from <rpmb device> to output\n"
145 		  "file or stdout if '-' is specified. If key is specified - read\n"
146 		  "data will be verified. Instead of regular path you can specify\n"
147 		  "'-' to read key from stdin.\n"
148 		  "Example:\n"
149 		  "  $ echo -n AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHH | \\\n"
150 		  "    mmc rpmb read-block /dev/mmcblk0rpmb 0x02 2 /tmp/block -\n"
151 		  "or read two blocks without verification\n"
152 		  "  $ mmc rpmb read-block /dev/mmcblk0rpmb 0x02 2 /tmp/block",
153 	  NULL
154 	},
155 	{ do_rpmb_write_block, -1,
156 	  "rpmb write-block", "<rpmb device> <address> <256 byte data file> <key file>\n"
157 		  "Block of 256 bytes will be written from data file to\n"
158 		  "<rpmb device>. Also you can specify '-' instead of key\n"
159 		  "file path or data file to read the data from stdin.\n"
160 		  "Example:\n"
161 		  "  $ (awk 'BEGIN {while (c++<256) printf \"a\"}' | \\\n"
162 		  "    echo -n AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHH) | \\\n"
163 		  "    mmc rpmb write-block /dev/mmcblk0rpmb 0x02 - -",
164 	  NULL
165 	},
166 	{ do_cache_en, -1,
167 	  "cache enable", "<device>\n"
168 		"Enable the eMMC cache feature on <device>.\n"
169 		"NOTE! The cache is an optional feature on devices >= eMMC4.5.",
170 	  NULL
171 	},
172 	{ do_cache_dis, -1,
173 	  "cache disable", "<device>\n"
174 		"Disable the eMMC cache feature on <device>.\n"
175 		"NOTE! The cache is an optional feature on devices >= eMMC4.5.",
176 	  NULL
177 	},
178 	{ 0, 0, 0, 0 }
179 };
180 
get_prgname(char * programname)181 static char *get_prgname(char *programname)
182 {
183 	char	*np;
184 	np = strrchr(programname,'/');
185 	if(!np)
186 		np = programname;
187 	else
188 		np++;
189 
190 	return np;
191 }
192 
print_help(char * programname,struct Command * cmd,int helptype)193 static void print_help(char *programname, struct Command *cmd, int helptype)
194 {
195 	char	*pc;
196 
197 	printf("\t%s %s ", programname, cmd->verb );
198 
199 	if (helptype == ADVANCED_HELP && cmd->adv_help)
200 		for(pc = cmd->adv_help; *pc; pc++){
201 			putchar(*pc);
202 			if(*pc == '\n')
203 				printf("\t\t");
204 		}
205 	else
206 		for(pc = cmd->help; *pc; pc++){
207 			putchar(*pc);
208 			if(*pc == '\n')
209 				printf("\t\t");
210 		}
211 
212 	putchar('\n');
213 }
214 
help(char * np)215 static void help(char *np)
216 {
217 	struct Command *cp;
218 
219 	printf("Usage:\n");
220 	for( cp = commands; cp->verb; cp++ )
221 		print_help(np, cp, BASIC_HELP);
222 
223 	printf("\n\t%s help|--help|-h\n\t\tShow the help.\n",np);
224 	printf("\n\t%s <cmd> --help\n\t\tShow detailed help for a command or subset of commands.\n",np);
225 	printf("\n%s\n", MMC_VERSION);
226 }
227 
split_command(char * cmd,char *** commands)228 static int split_command(char *cmd, char ***commands)
229 {
230 	int	c, l;
231 	char	*p, *s;
232 
233 	for( *commands = 0, l = c = 0, p = s = cmd ; ; p++, l++ ){
234 		if ( *p && *p != ' ' )
235 			continue;
236 
237 		/* c + 2 so that we have room for the null */
238 		(*commands) = realloc( (*commands), sizeof(char *)*(c + 2));
239 		(*commands)[c] = strndup(s, l);
240 		c++;
241 		l = 0;
242 		s = p+1;
243 		if( !*p ) break;
244 	}
245 
246 	(*commands)[c] = 0;
247 	return c;
248 }
249 
250 /*
251 	This function checks if the passed command is ambiguous
252 */
check_ambiguity(struct Command * cmd,char ** argv)253 static int check_ambiguity(struct Command *cmd, char **argv){
254 	int		i;
255 	struct Command	*cp;
256 	/* check for ambiguity */
257 	for( i = 0 ; i < cmd->ncmds ; i++ ){
258 		int match;
259 		for( match = 0, cp = commands; cp->verb; cp++ ){
260 			int	j, skip;
261 			char	*s1, *s2;
262 
263 			if( cp->ncmds < i )
264 				continue;
265 
266 			for( skip = 0, j = 0 ; j < i ; j++ )
267 				if( strcmp(cmd->cmds[j], cp->cmds[j])){
268 					skip=1;
269 					break;
270 				}
271 			if(skip)
272 				continue;
273 
274 			if( !strcmp(cmd->cmds[i], cp->cmds[i]))
275 				continue;
276 			for(s2 = cp->cmds[i], s1 = argv[i+1];
277 				*s1 == *s2 && *s1; s1++, s2++ ) ;
278 			if( !*s1 )
279 				match++;
280 		}
281 		if(match){
282 			int j;
283 			fprintf(stderr, "ERROR: in command '");
284 			for( j = 0 ; j <= i ; j++ )
285 				fprintf(stderr, "%s%s",j?" ":"", argv[j+1]);
286 			fprintf(stderr, "', '%s' is ambiguous\n",argv[j]);
287 			return -2;
288 		}
289 	}
290 	return 0;
291 }
292 
293 /*
294  * This function, compacts the program name and the command in the first
295  * element of the '*av' array
296  */
prepare_args(int * ac,char *** av,char * prgname,struct Command * cmd)297 static int prepare_args(int *ac, char ***av, char *prgname, struct Command *cmd ){
298 
299 	char	**ret;
300 	int	i;
301 	char	*newname;
302 
303 	ret = (char **)malloc(sizeof(char*)*(*ac+1));
304 	newname = (char*)malloc(strlen(prgname)+strlen(cmd->verb)+2);
305 	if( !ret || !newname ){
306 		free(ret);
307 		free(newname);
308 		return -1;
309 	}
310 
311 	ret[0] = newname;
312 	for(i=0; i < *ac ; i++ )
313 		ret[i+1] = (*av)[i];
314 
315 	strcpy(newname, prgname);
316 	strcat(newname, " ");
317 	strcat(newname, cmd->verb);
318 
319 	(*ac)++;
320 	*av = ret;
321 
322 	return 0;
323 
324 }
325 
326 /*
327 	This function performs the following jobs:
328 	- show the help if '--help' or 'help' or '-h' are passed
329 	- verify that a command is not ambiguous, otherwise show which
330 	  part of the command is ambiguous
331 	- if after a (even partial) command there is '--help' show detailed help
332 	  for all the matching commands
333 	- if the command doesn't match show an error
334 	- finally, if a command matches, they return which command matched and
335 	  the arguments
336 
337 	The function return 0 in case of help is requested; <0 in case
338 	of uncorrect command; >0 in case of matching commands
339 	argc, argv are the arg-counter and arg-vector (input)
340 	*nargs_ is the number of the arguments after the command (output)
341 	**cmd_  is the invoked command (output)
342 	***args_ are the arguments after the command
343 
344 */
parse_args(int argc,char ** argv,CommandFunction * func_,int * nargs_,char ** cmd_,char *** args_)345 static int parse_args(int argc, char **argv,
346 		      CommandFunction *func_,
347 		      int *nargs_, char **cmd_, char ***args_ )
348 {
349 	struct Command	*cp;
350 	struct Command	*matchcmd=0;
351 	char		*prgname = get_prgname(argv[0]);
352 	int		i=0, helprequested=0;
353 
354 	if( argc < 2 || !strcmp(argv[1], "help") ||
355 		!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")){
356 		help(prgname);
357 		return 0;
358 	}
359 
360 	for( cp = commands; cp->verb; cp++ )
361 		if( !cp->ncmds)
362 			cp->ncmds = split_command(cp->verb, &(cp->cmds));
363 
364 	for( cp = commands; cp->verb; cp++ ){
365 		int     match;
366 
367 		if( argc-1 < cp->ncmds )
368 			continue;
369 		for( match = 1, i = 0 ; i < cp->ncmds ; i++ ){
370 			char	*s1, *s2;
371 			s1 = cp->cmds[i];
372 			s2 = argv[i+1];
373 
374 			for(s2 = cp->cmds[i], s1 = argv[i+1];
375 				*s1 == *s2 && *s1;
376 				s1++, s2++ ) ;
377 			if( *s1 ){
378 				match=0;
379 				break;
380 			}
381 		}
382 
383 		/* If you understand why this code works ...
384 			you are a genious !! */
385 		if(argc>i+1 && !strcmp(argv[i+1],"--help")){
386 			if(!helprequested)
387 				printf("Usage:\n");
388 			print_help(prgname, cp, ADVANCED_HELP);
389 			helprequested=1;
390 			continue;
391 		}
392 
393 		if(!match)
394 			continue;
395 
396 		matchcmd = cp;
397 		*nargs_  = argc-matchcmd->ncmds-1;
398 		*cmd_ = matchcmd->verb;
399 		*args_ = argv+matchcmd->ncmds+1;
400 		*func_ = cp->func;
401 
402 		break;
403 	}
404 
405 	if(helprequested){
406 		printf("\n%s\n", MMC_VERSION);
407 		return 0;
408 	}
409 
410 	if(!matchcmd){
411 		fprintf( stderr, "ERROR: unknown command '%s'\n",argv[1]);
412 		help(prgname);
413 		return -1;
414 	}
415 
416 	if(check_ambiguity(matchcmd, argv))
417 		return -2;
418 
419 	/* check the number of argument */
420 	if (matchcmd->nargs < 0 && matchcmd->nargs < -*nargs_ ){
421 		fprintf(stderr, "ERROR: '%s' requires minimum %d arg(s)\n",
422 			matchcmd->verb, -matchcmd->nargs);
423 			return -2;
424 	}
425 	if(matchcmd->nargs >= 0 && matchcmd->nargs != *nargs_ && matchcmd->nargs != 999){
426 		fprintf(stderr, "ERROR: '%s' requires %d arg(s)\n",
427 			matchcmd->verb, matchcmd->nargs);
428 			return -2;
429 	}
430 
431         if (prepare_args( nargs_, args_, prgname, matchcmd )){
432                 fprintf(stderr, "ERROR: not enough memory\\n");
433 		return -20;
434         }
435 
436 
437 	return 1;
438 }
main(int ac,char ** av)439 int main(int ac, char **av )
440 {
441 	char		*cmd=0, **args=0;
442 	int		nargs=0, r;
443 	CommandFunction func=0;
444 
445 	r = parse_args(ac, av, &func, &nargs, &cmd, &args);
446 	if( r <= 0 ){
447 		/* error or no command to parse*/
448 		exit(-r);
449 	}
450 
451 	exit(func(nargs, args));
452 }
453 
454