[U-Boot] [PATCH] cmd_nand.c: converted to new style subcmd handling

converted to new style subcmd handling. partly tested on sheevaplug read and information commands tested on sheevaplug (sorry did not want to nuke my device so I did not want to test things like nand scrub)
Signed-off-by: Frans Meulenbroeks fransmeulenbroeks@gmail.com
---
note that nand biterr has no implementation --- common/cmd_nand.c | 595 ++++++++++++++++++++++++++++++++++------------------- 1 files changed, 386 insertions(+), 209 deletions(-)
diff --git a/common/cmd_nand.c b/common/cmd_nand.c index 9b0c930..d537b4b 100644 --- a/common/cmd_nand.c +++ b/common/cmd_nand.c @@ -204,281 +204,458 @@ static void nand_print_info(int idx) nand->name, nand->erasesize >> 10); }
-int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) +static int nand_quiet(void) { - int i, dev, ret = 0; - ulong addr, off; - size_t size; - char *cmd, *s; - nand_info_t *nand; + const char *quiet_str = getenv("quiet"); #ifdef CONFIG_SYS_NAND_QUIET int quiet = CONFIG_SYS_NAND_QUIET; #else int quiet = 0; #endif - const char *quiet_str = getenv("quiet"); - - /* at least two arguments please */ - if (argc < 2) - goto usage; - if (quiet_str) quiet = simple_strtoul(quiet_str, NULL, 0) != 0; + return quiet; +}
- cmd = argv[1]; +static nand_info_t *get_nand(void) +{ + /* the following commands operate on the current device */ + if (nand_curr_device < 0 || nand_curr_device >= CONFIG_SYS_MAX_NAND_DEVICE || + !nand_info[nand_curr_device].name) { + puts("\nno devices available\n"); + return NULL; + } + return &nand_info[nand_curr_device]; +}
- if (strcmp(cmd, "info") == 0) { +static int do_nand_info(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) +{ + int i;
- putc('\n'); - for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++) { - if (nand_info[i].name) - nand_print_info(i); - } - return 0; + putc('\n'); + for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++) { + if (nand_info[i].name) + nand_print_info(i); } + return 0; +}
- if (strcmp(cmd, "device") == 0) { +static int do_nand_device(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) +{ + int dev;
- if (argc < 3) { - putc('\n'); - if ((nand_curr_device < 0) || - (nand_curr_device >= CONFIG_SYS_MAX_NAND_DEVICE)) - puts("no devices available\n"); - else - nand_print_info(nand_curr_device); - return 0; - } - dev = (int)simple_strtoul(argv[2], NULL, 10); - if (dev < 0 || dev >= CONFIG_SYS_MAX_NAND_DEVICE || !nand_info[dev].name) { - puts("No such device\n"); - return 1; - } - printf("Device %d: %s", dev, nand_info[dev].name); - puts("... is now current device\n"); - nand_curr_device = dev; + if (argc < 2) { + putc('\n'); + if ((nand_curr_device < 0) || + (nand_curr_device >= CONFIG_SYS_MAX_NAND_DEVICE)) + puts("no devices available\n"); + else + nand_print_info(nand_curr_device); + return 0; + } + dev = (int)simple_strtoul(argv[1], NULL, 10); + if (dev < 0 || dev >= CONFIG_SYS_MAX_NAND_DEVICE || !nand_info[dev].name) { + puts("No such device\n"); + return 1; + } + printf("Device %d: %s", dev, nand_info[dev].name); + puts("... is now current device\n"); + nand_curr_device = dev;
#ifdef CONFIG_SYS_NAND_SELECT_DEVICE - /* - * Select the chip in the board/cpu specific driver - */ - board_nand_select_device(nand_info[dev].priv, dev); + /* + * Select the chip in the board/cpu specific driver + */ + board_nand_select_device(nand_info[dev].priv, dev); #endif
- return 0; + return 0; +} + +static int do_nand_read(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) +{ + int ret = 0; + size_t size; + ulong off; + ulong addr; + char *s; + nand_info_t *nand; + + nand = get_nand(); + if (!nand) + return 1; + + if (argc < 4) + { + cmd_usage(cmdtp); + return 1; }
- if (strcmp(cmd, "bad") != 0 && strcmp(cmd, "erase") != 0 && - strncmp(cmd, "dump", 4) != 0 && - strncmp(cmd, "read", 4) != 0 && strncmp(cmd, "write", 5) != 0 && - strcmp(cmd, "scrub") != 0 && strcmp(cmd, "markbad") != 0 && - strcmp(cmd, "biterr") != 0 && - strcmp(cmd, "lock") != 0 && strcmp(cmd, "unlock") != 0 ) - goto usage; + addr = (ulong)simple_strtoul(argv[1], NULL, 16);
- /* the following commands operate on the current device */ - if (nand_curr_device < 0 || nand_curr_device >= CONFIG_SYS_MAX_NAND_DEVICE || - !nand_info[nand_curr_device].name) { - puts("\nno devices available\n"); + printf("\nNAND read: "); + if (arg_off_size(argc - 2, argv + 2, nand, &off, &size) != 0) + return 1; + + s = strchr(argv[0], '.'); + if (!s || !strcmp(s, ".jffs2") || + !strcmp(s, ".e") || !strcmp(s, ".i")) { + ret = nand_read_skip_bad(nand, off, &size, (u_char *)addr); + } else if (!strcmp(s, ".oob")) { + /* out-of-band data */ + mtd_oob_ops_t ops = { + .oobbuf = (u8 *)addr, + .ooblen = size, + .mode = MTD_OOB_RAW + }; + + ret = nand->read_oob(nand, off, &ops); + } else { + printf("Unknown nand command suffix '%s'.\n", s); return 1; } - nand = &nand_info[nand_curr_device];
- if (strcmp(cmd, "bad") == 0) { - printf("\nDevice %d bad blocks:\n", nand_curr_device); - for (off = 0; off < nand->size; off += nand->erasesize) - if (nand_block_isbad(nand, off)) - printf(" %08lx\n", off); - return 0; + printf(" %zu bytes read: %s\n", size, ret ? "ERROR" : "OK"); + + return ret == 0 ? 0 : 1; +} + +static int do_nand_write(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) +{ + int ret = 0; + size_t size; + ulong off; + ulong addr; + char *s; + nand_info_t *nand; + + nand = get_nand(); + if (!nand) + return 1; + + if (argc < 4) + { + cmd_usage(cmdtp); + return 1; + } + + addr = (ulong)simple_strtoul(argv[1], NULL, 16); + + printf("\nNAND write: "); + if (arg_off_size(argc - 2, argv + 2, nand, &off, &size) != 0) + return 1; + + s = strchr(argv[0], '.'); + if (!s || !strcmp(s, ".jffs2") || + !strcmp(s, ".e") || !strcmp(s, ".i")) { + ret = nand_write_skip_bad(nand, off, &size, (u_char *)addr); + } else if (!strcmp(s, ".oob")) { + /* out-of-band data */ + mtd_oob_ops_t ops = { + .oobbuf = (u8 *)addr, + .ooblen = size, + .mode = MTD_OOB_RAW + }; + + ret = nand->write_oob(nand, off, &ops); + } else { + printf("Unknown nand command suffix '%s'.\n", s); + return 1; }
+ printf(" %zu bytes written: %s\n", size, ret ? "ERROR" : "OK"); + + return ret == 0 ? 0 : 1; +} + +static int do_nand_erase(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) +{ /* * Syntax is: * 0 1 2 3 4 * nand erase [clean] [off size] */ - if (strcmp(cmd, "erase") == 0 || strcmp(cmd, "scrub") == 0) { - nand_erase_options_t opts; - /* "clean" at index 2 means request to write cleanmarker */ - int clean = argc > 2 && !strcmp("clean", argv[2]); - int o = clean ? 3 : 2; - int scrub = !strcmp(cmd, "scrub"); - - printf("\nNAND %s: ", scrub ? "scrub" : "erase"); - /* skip first two or three arguments, look for offset and size */ - if (arg_off_size(argc - o, argv + o, nand, &off, &size) != 0) - return 1;
- memset(&opts, 0, sizeof(opts)); - opts.offset = off; - opts.length = size; - opts.jffs2 = clean; - opts.quiet = quiet; - - if (scrub) { - puts("Warning: " - "scrub option will erase all factory set " - "bad blocks!\n" - " " - "There is no reliable way to recover them.\n" - " " - "Use this command only for testing purposes " - "if you\n" - " " - "are sure of what you are doing!\n" - "\nReally scrub this NAND flash? <y/N>\n"); - - if (getc() == 'y') { - puts("y"); - if (getc() == '\r') - opts.scrub = 1; - else { - puts("scrub aborted\n"); - return -1; - } - } else { - puts("scrub aborted\n"); - return -1; - } - } - ret = nand_erase_opts(nand, &opts); - printf("%s\n", ret ? "ERROR" : "OK"); + int ret = 0; + size_t size; + ulong off; + nand_erase_options_t opts; + /* "clean" at index 1 means request to write cleanmarker */ + int clean = argc > 1 && !strcmp("clean", argv[1]); + int o = clean ? 2 : 1; + nand_info_t *nand; + int quiet = nand_quiet();
- return ret == 0 ? 0 : 1; - } + nand = get_nand(); + if (!nand) + return 1;
- if (strncmp(cmd, "dump", 4) == 0) { - if (argc < 3) - goto usage; + printf("\nNAND erase: "); + /* skip first two or three arguments, look for offset and size */ + if (arg_off_size(argc - o, argv + o, nand, &off, &size) != 0) + return 1;
- s = strchr(cmd, '.'); - off = (int)simple_strtoul(argv[2], NULL, 16); + memset(&opts, 0, sizeof(opts)); + opts.offset = off; + opts.length = size; + opts.jffs2 = clean; + opts.quiet = quiet;
- if (s != NULL && strcmp(s, ".oob") == 0) - ret = nand_dump(nand, off, 1); - else - ret = nand_dump(nand, off, 0); + ret = nand_erase_opts(nand, &opts); + printf("%s\n", ret ? "ERROR" : "OK"); + + return ret == 0 ? 0 : 1; +}
- return ret == 0 ? 1 : 0; +static int do_nand_scrub(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) +{ + /* + * Syntax is: + * 0 1 2 3 4 + * nand scrub [clean] [off size] + */
+ int ret = 0; + size_t size; + ulong off; + nand_erase_options_t opts; + /* "clean" at index 1 means request to write cleanmarker */ + int clean = argc > 1 && !strcmp("clean", argv[1]); + int o = clean ? 2 : 1; + nand_info_t *nand; + int quiet = nand_quiet(); + + nand = get_nand(); + if (!nand) + return 1; + + printf("\nNAND scrub: "); + /* skip first two or three arguments, look for offset and size */ + if (arg_off_size(argc - o, argv + o, nand, &off, &size) != 0) + return 1; + + memset(&opts, 0, sizeof(opts)); + opts.offset = off; + opts.length = size; + opts.jffs2 = clean; + opts.quiet = quiet; + + puts("Warning: " + "scrub option will erase all factory set " + "bad blocks!\n" + " " + "There is no reliable way to recover them.\n" + " " + "Use this command only for testing purposes " + "if you\n" + " " + "are sure of what you are doing!\n" + "\nReally scrub this NAND flash? <y/N>\n"); + + if (getc() == 'y') { + puts("y"); + if (getc() == '\r') + opts.scrub = 1; + else { + puts("scrub aborted\n"); + return -1; + } + } else { + puts("scrub aborted\n"); + return -1; } + ret = nand_erase_opts(nand, &opts); + printf("%s\n", ret ? "ERROR" : "OK");
- if (strncmp(cmd, "read", 4) == 0 || strncmp(cmd, "write", 5) == 0) { - int read; + return ret == 0 ? 0 : 1; +}
- if (argc < 4) - goto usage; +static int do_nand_bad(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) +{ + ulong off; + nand_info_t *nand;
- addr = (ulong)simple_strtoul(argv[2], NULL, 16); + nand = get_nand(); + if (!nand) + return 1;
- read = strncmp(cmd, "read", 4) == 0; /* 1 = read, 0 = write */ - printf("\nNAND %s: ", read ? "read" : "write"); - if (arg_off_size(argc - 3, argv + 3, nand, &off, &size) != 0) - return 1; + printf("\nDevice %d bad blocks:\n", nand_curr_device); + for (off = 0; off < nand->size; off += nand->erasesize) + if (nand_block_isbad(nand, off)) + printf(" %08lx\n", off); + return 0; +}
- s = strchr(cmd, '.'); - if (!s || !strcmp(s, ".jffs2") || - !strcmp(s, ".e") || !strcmp(s, ".i")) { - if (read) - ret = nand_read_skip_bad(nand, off, &size, - (u_char *)addr); - else - ret = nand_write_skip_bad(nand, off, &size, - (u_char *)addr); - } else if (!strcmp(s, ".oob")) { - /* out-of-band data */ - mtd_oob_ops_t ops = { - .oobbuf = (u8 *)addr, - .ooblen = size, - .mode = MTD_OOB_RAW - }; - - if (read) - ret = nand->read_oob(nand, off, &ops); - else - ret = nand->write_oob(nand, off, &ops); - } else { - printf("Unknown nand command suffix '%s'.\n", s); - return 1; - } +static int do_nand_dump(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) +{ + int ret = 0; + ulong off; + char *s; + nand_info_t *nand;
- printf(" %zu bytes %s: %s\n", size, - read ? "read" : "written", ret ? "ERROR" : "OK"); + nand = get_nand(); + if (!nand) + return 1;
- return ret == 0 ? 0 : 1; + if (argc < 2) + { + cmd_usage(cmdtp); + return 1; }
- if (strcmp(cmd, "markbad") == 0) { - argc -= 2; - argv += 2; + s = strchr(argv[0], '.'); + off = (int)simple_strtoul(argv[1], NULL, 16);
- if (argc <= 0) - goto usage; + if (s != NULL && strcmp(s, ".oob") == 0) + ret = nand_dump(nand, off, 1); + else + ret = nand_dump(nand, off, 0);
- while (argc > 0) { - addr = simple_strtoul(*argv, NULL, 16); + return ret == 0 ? 1 : 0; +}
- if (nand->block_markbad(nand, addr)) { - printf("block 0x%08lx NOT marked " - "as bad! ERROR %d\n", - addr, ret); - ret = 1; - } else { - printf("block 0x%08lx successfully " - "marked as bad\n", - addr); - } - --argc; - ++argv; - } - return ret; - } +static int do_nand_markbad(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) +{ + int ret = 0; + ulong addr; + nand_info_t *nand; + + nand = get_nand(); + if (!nand) + return 1;
- if (strcmp(cmd, "biterr") == 0) { - /* todo */ + argc--; + argv++; + + if (argc <= 0) + { + cmd_usage(cmdtp); return 1; }
-#ifdef CONFIG_CMD_NAND_LOCK_UNLOCK - if (strcmp(cmd, "lock") == 0) { - int tight = 0; - int status = 0; - if (argc == 3) { - if (!strcmp("tight", argv[2])) - tight = 1; - if (!strcmp("status", argv[2])) - status = 1; - } - if (status) { - do_nand_status(nand); + while (argc > 0) { + addr = simple_strtoul(*argv, NULL, 16); + + if (nand->block_markbad(nand, addr)) { + printf("block 0x%08lx NOT marked " + "as bad! ERROR %d\n", + addr, ret); + ret = 1; } else { - if (!nand_lock(nand, tight)) { - puts("NAND flash successfully locked\n"); - } else { - puts("Error locking NAND flash\n"); - return 1; - } + printf("block 0x%08lx successfully " + "marked as bad\n", + addr); } - return 0; + argc--; + argv++; } + return ret; +}
- if (strcmp(cmd, "unlock") == 0) { - if (arg_off_size(argc - 2, argv + 2, nand, &off, &size) < 0) - return 1; +static int do_nand_biterr(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) +{ + nand_info_t *nand;
- if (!nand_unlock(nand, off, size)) { - puts("NAND flash successfully unlocked\n"); + nand = get_nand(); + if (!nand) + return 1; + + /* todo */ + return 1; +} + +#ifdef CONFIG_CMD_NAND_LOCK_UNLOCK +static int do_nand_lock(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) +{ + ulong addr; + size_t size; + nand_info_t *nand; + int tight = 0; + int status = 0; + + nand = get_nand(); + if (!nand) + return 1; + + if (argc == 2) { + if (!strcmp("tight", argv[1])) + tight = 1; + if (!strcmp("status", argv[1])) + status = 1; + } + if (status) { + do_nand_status(nand); + } else { + if (!nand_lock(nand, tight)) { + puts("NAND flash successfully locked\n"); } else { - puts("Error unlocking NAND flash, " - "write and erase will probably fail\n"); + puts("Error locking NAND flash\n"); return 1; } - return 0; } + return 0; +} + +static int do_nand_unlock(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) +{ + ulong addr; + size_t size; + nand_info_t *nand; + + argc--; + argv++; + + nand = get_nand(); + if (!nand) + return 1; + + if (arg_off_size(argc, argv, nand, &off, &size) < 0) + return 1; + + if (!nand_unlock(nand, off, size)) { + puts("NAND flash successfully unlocked\n"); + } else { + puts("Error unlocking NAND flash, " + "write and erase will probably fail\n"); + return 1; + } + return 0; +} #endif
-usage: - cmd_usage(cmdtp); - return 1; +static cmd_tbl_t cmd_nand_sub[] = { + U_BOOT_CMD_MKENT(info, 1, 1, do_nand_info, "", ""), + U_BOOT_CMD_MKENT(device, 2, 1, do_nand_device, "", ""), + U_BOOT_CMD_MKENT(read, 4, 1, do_nand_read, "", ""), + U_BOOT_CMD_MKENT(write, 4, 1, do_nand_write, "", ""), + U_BOOT_CMD_MKENT(erase, 4, 1, do_nand_erase, "", ""), + U_BOOT_CMD_MKENT(bad, 1, 1, do_nand_bad, "", ""), + U_BOOT_CMD_MKENT(dump, 2, 1, do_nand_dump, "", ""), + U_BOOT_CMD_MKENT(scrub, 1, 1, do_nand_scrub, "", ""), + U_BOOT_CMD_MKENT(markbad, CONFIG_SYS_MAXARGS, 1, do_nand_markbad, "", ""), + U_BOOT_CMD_MKENT(biterr, 2, 1, do_nand_biterr, "", ""), +#ifdef CONFIG_CMD_NAND_LOCK_UNLOCK + U_BOOT_CMD_MKENT(lock, 1, 1, do_nand_lock, "", ""), + U_BOOT_CMD_MKENT(unlock, 1, 1, do_nand_unlock, "", ""), +#endif +}; + +static int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) +{ + cmd_tbl_t *c; + + /* Strip off leading 'nand' command argument */ + argc--; + argv++; + + c = find_cmd_tbl(argv[0], &cmd_nand_sub[0], ARRAY_SIZE(cmd_nand_sub)); + + if (c) { + return c->cmd(cmdtp, flag, argc, argv); + } else { + cmd_usage(cmdtp); + return 1; + } }
U_BOOT_CMD(nand, CONFIG_SYS_MAXARGS, 1, do_nand,

On Sun, Apr 04, 2010 at 09:53:16PM +0200, Frans Meulenbroeks wrote:
converted to new style subcmd handling. partly tested on sheevaplug read and information commands tested on sheevaplug (sorry did not want to nuke my device so I did not want to test things like nand scrub)
Signed-off-by: Frans Meulenbroeks fransmeulenbroeks@gmail.com
Sorry for the delay...
+static int do_nand_read(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) +{
The read and write functions are quite similar, and were not duplicated previously. Use one function, distinguishing with argv[0] (i.e. use subcmd only where it makes sense).
Likewise for erase and scrub.
- if (argc < 4)
- {
cmd_usage(cmdtp);
}return 1;
The opening brace should be on the same line as the "if".
-Scott

Was afk for 3.5 weeks, so didn't get to this.Sorry.
2010/4/19 Scott Wood scottwood@freescale.com:
On Sun, Apr 04, 2010 at 09:53:16PM +0200, Frans Meulenbroeks wrote:
converted to new style subcmd handling. partly tested on sheevaplug read and information commands tested on sheevaplug (sorry did not want to nuke my device so I did not want to test things like nand scrub)
Signed-off-by: Frans Meulenbroeks fransmeulenbroeks@gmail.com
Sorry for the delay...
+static int do_nand_read(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) +{
The read and write functions are quite similar, and were not duplicated previously. Use one function, distinguishing with argv[0] (i.e. use subcmd only where it makes sense).
Cool. didn't think of using argv[0] here. Will do.
Likewise for erase and scrub.
Ok.
- if (argc < 4)
- {
- cmd_usage(cmdtp);
- return 1;
}
The opening brace should be on the same line as the "if".
Oops. sorry, will fix. Might take a little time as lots of things pile up in 3.5 weeks ....
Thanks for the feedback.
Frans
participants (2)
-
Frans Meulenbroeks
-
Scott Wood