diff options
| author | Baruch Siach <baruch@tkos.co.il> | 2010-10-18 02:36:34 +0200 |
|---|---|---|
| committer | Denys Vlasenko <vda.linux@googlemail.com> | 2010-10-18 02:36:34 +0200 |
| commit | 36af2f7977edc818af8c01392ef700439a9fe163 (patch) | |
| tree | 37c43ec830c9c961c76e49bd710e02b3b238eb2c /miscutils | |
| parent | 873bb31d1703aae080d1928b5928c0011a944485 (diff) | |
| download | busybox-w32-36af2f7977edc818af8c01392ef700439a9fe163.tar.gz busybox-w32-36af2f7977edc818af8c01392ef700439a9fe163.tar.bz2 busybox-w32-36af2f7977edc818af8c01392ef700439a9fe163.zip | |
nanddump: new applet
function old new delta
nandwrite_main 418 936 +518
dump_bad - 119 +119
packed_usage 27752 27810 +58
next_good_eraseblock 84 106 +22
applet_names 2366 2375 +9
applet_main 1380 1384 +4
applet_nameofs 690 692 +2
bbconfig_config_bz2 4932 4922 -10
------------------------------------------------------------------------------
(add/remove: 1/0 grow/shrink: 6/1 up/down: 732/-10) Total: 722 bytes
Signed-off-by: Baruch Siach <baruch@tkos.co.il>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
Diffstat (limited to 'miscutils')
| -rw-r--r-- | miscutils/nandwrite.c | 155 |
1 files changed, 127 insertions, 28 deletions
diff --git a/miscutils/nandwrite.c b/miscutils/nandwrite.c index f42242687..6c85ea346 100644 --- a/miscutils/nandwrite.c +++ b/miscutils/nandwrite.c | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * nandwrite.c - ported to busybox from mtd-utils | 2 | * nandwrite and nanddump ported to busybox from mtd-utils |
| 3 | * | 3 | * |
| 4 | * Author: Baruch Siach <baruch@tkos.co.il>, Orex Computed Radiography | 4 | * Author: Baruch Siach <baruch@tkos.co.il>, Orex Computed Radiography |
| 5 | * | 5 | * |
| @@ -9,8 +9,10 @@ | |||
| 9 | */ | 9 | */ |
| 10 | 10 | ||
| 11 | //applet:IF_NANDWRITE(APPLET(nandwrite, _BB_DIR_USR_SBIN, _BB_SUID_DROP)) | 11 | //applet:IF_NANDWRITE(APPLET(nandwrite, _BB_DIR_USR_SBIN, _BB_SUID_DROP)) |
| 12 | //applet:IF_NANDWRITE(APPLET_ODDNAME(nanddump, nandwrite, _BB_DIR_USR_SBIN, _BB_SUID_DROP, nanddump)) | ||
| 12 | 13 | ||
| 13 | //kbuild:lib-$(CONFIG_NANDWRITE) += nandwrite.o | 14 | //kbuild:lib-$(CONFIG_NANDWRITE) += nandwrite.o |
| 15 | //kbuild:lib-$(CONFIG_NANDDUMP) += nandwrite.o | ||
| 14 | 16 | ||
| 15 | //config:config NANDWRITE | 17 | //config:config NANDWRITE |
| 16 | //config: bool "nandwrite" | 18 | //config: bool "nandwrite" |
| @@ -18,9 +20,13 @@ | |||
| 18 | //config: depends on PLATFORM_LINUX | 20 | //config: depends on PLATFORM_LINUX |
| 19 | //config: help | 21 | //config: help |
| 20 | //config: Write to the specified MTD device, with bad blocks awareness | 22 | //config: Write to the specified MTD device, with bad blocks awareness |
| 21 | 23 | //config: | |
| 22 | #include "libbb.h" | 24 | //config:config NANDDUMP |
| 23 | #include <mtd/mtd-user.h> | 25 | //config: bool "nanddump" |
| 26 | //config: default n | ||
| 27 | //config: depends on PLATFORM_LINUX | ||
| 28 | //config: help | ||
| 29 | //config: Dump the content of raw NAND chip | ||
| 24 | 30 | ||
| 25 | //usage:#define nandwrite_trivial_usage | 31 | //usage:#define nandwrite_trivial_usage |
| 26 | //usage: "[-p] [-s ADDR] MTD_DEVICE [FILE]" | 32 | //usage: "[-p] [-s ADDR] MTD_DEVICE [FILE]" |
| @@ -30,18 +36,65 @@ | |||
| 30 | //usage: "\n -p Pad to page size" | 36 | //usage: "\n -p Pad to page size" |
| 31 | //usage: "\n -s ADDR Start address" | 37 | //usage: "\n -s ADDR Start address" |
| 32 | 38 | ||
| 39 | //usage:#define nanddump_trivial_usage | ||
| 40 | //usage: "[-o] [-b] [-s ADDR] [-f FILE] MTD_DEVICE" | ||
| 41 | //usage:#define nanddump_full_usage "\n\n" | ||
| 42 | //usage: "Dump the sepcified MTD device\n" | ||
| 43 | //usage: "\nOptions:" | ||
| 44 | //usage: "\n -o Omit oob data" | ||
| 45 | //usage: "\n -b Omit bad block from the dump" | ||
| 46 | //usage: "\n -s ADDR Start address" | ||
| 47 | //usage: "\n -l LEN Length" | ||
| 48 | //usage: "\n -f FILE Dump to file ('-' for stdout)" | ||
| 49 | |||
| 50 | #include "libbb.h" | ||
| 51 | #include <mtd/mtd-user.h> | ||
| 52 | |||
| 53 | #define IS_NANDDUMP (ENABLE_NANDDUMP && (!ENABLE_NANDWRITE || (applet_name[4] == 'd'))) | ||
| 54 | #define IS_NANDWRITE (ENABLE_NANDWRITE && (!ENABLE_NANDDUMP || (applet_name[4] != 'd'))) | ||
| 55 | |||
| 56 | #define OPT_p (1 << 0) /* nandwrite only */ | ||
| 57 | #define OPT_o (1 << 0) /* nanddump only */ | ||
| 58 | #define OPT_s (1 << 1) | ||
| 59 | #define OPT_b (1 << 2) | ||
| 60 | #define OPT_f (1 << 3) | ||
| 61 | #define OPT_l (1 << 4) | ||
| 62 | |||
| 63 | #define NAND_MAX_OOBSIZE 256 | ||
| 64 | /* helper for writing out 0xff for bad blocks pad */ | ||
| 65 | static void dump_bad(struct mtd_info_user *meminfo, unsigned len, int oob) | ||
| 66 | { | ||
| 67 | unsigned char buf[meminfo->writesize]; | ||
| 68 | unsigned count; | ||
| 69 | |||
| 70 | /* round len to the next page */ | ||
| 71 | len = (len | ~(meminfo->writesize - 1)) + 1; | ||
| 72 | |||
| 73 | memset(buf, 0xff, sizeof(buf)); | ||
| 74 | for (count = 0; count < len; count += meminfo->writesize) { | ||
| 75 | xwrite(STDOUT_FILENO, buf, meminfo->writesize); | ||
| 76 | if (oob) | ||
| 77 | xwrite(STDOUT_FILENO, buf, meminfo->oobsize); | ||
| 78 | } | ||
| 79 | } | ||
| 80 | |||
| 33 | static unsigned next_good_eraseblock(int fd, struct mtd_info_user *meminfo, | 81 | static unsigned next_good_eraseblock(int fd, struct mtd_info_user *meminfo, |
| 34 | unsigned block_offset) | 82 | unsigned block_offset) |
| 35 | { | 83 | { |
| 36 | while (1) { | 84 | while (1) { |
| 37 | loff_t offs; | 85 | loff_t offs; |
| 38 | if (block_offset >= meminfo->size) | 86 | |
| 39 | bb_error_msg_and_die("not enough space in MTD device"); | 87 | if (block_offset >= meminfo->size) { |
| 88 | if (IS_NANDWRITE) | ||
| 89 | bb_error_msg_and_die("not enough space in MTD device"); | ||
| 90 | return block_offset; /* let the caller exit */ | ||
| 91 | } | ||
| 40 | offs = block_offset; | 92 | offs = block_offset; |
| 41 | if (xioctl(fd, MEMGETBADBLOCK, &offs) == 0) | 93 | if (xioctl(fd, MEMGETBADBLOCK, &offs) == 0) |
| 42 | return block_offset; | 94 | return block_offset; |
| 43 | /* ioctl returned 1 => "bad block" */ | 95 | /* ioctl returned 1 => "bad block" */ |
| 44 | printf("Skipping bad block at 0x%08x\n", block_offset); | 96 | if (IS_NANDWRITE) |
| 97 | printf("Skipping bad block at 0x%08x\n", block_offset); | ||
| 45 | block_offset += meminfo->erasesize; | 98 | block_offset += meminfo->erasesize; |
| 46 | } | 99 | } |
| 47 | } | 100 | } |
| @@ -49,31 +102,49 @@ static unsigned next_good_eraseblock(int fd, struct mtd_info_user *meminfo, | |||
| 49 | int nandwrite_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 102 | int nandwrite_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
| 50 | int nandwrite_main(int argc UNUSED_PARAM, char **argv) | 103 | int nandwrite_main(int argc UNUSED_PARAM, char **argv) |
| 51 | { | 104 | { |
| 105 | /* Buffer for OOB data */ | ||
| 106 | unsigned char oobbuf[NAND_MAX_OOBSIZE]; | ||
| 52 | unsigned opts; | 107 | unsigned opts; |
| 53 | int fd; | 108 | int fd; |
| 54 | ssize_t cnt; | 109 | ssize_t cnt; |
| 55 | unsigned mtdoffset, meminfo_writesize, blockstart; | 110 | unsigned mtdoffset, meminfo_writesize, blockstart, limit; |
| 111 | unsigned end_addr = ~0; | ||
| 56 | struct mtd_info_user meminfo; | 112 | struct mtd_info_user meminfo; |
| 113 | struct mtd_oob_buf oob; | ||
| 57 | unsigned char *filebuf; | 114 | unsigned char *filebuf; |
| 58 | const char *opt_s = "0"; | 115 | const char *opt_s = "0", *opt_f = "-", *opt_l; |
| 59 | enum { | 116 | |
| 60 | OPT_p = (1 << 0), | 117 | if (IS_NANDDUMP) { |
| 61 | OPT_s = (1 << 1), | 118 | opt_complementary = "=1"; |
| 62 | }; | 119 | opts = getopt32(argv, "os:bf:l:", &opt_s, &opt_f, &opt_l); |
| 63 | 120 | } else { /* nandwrite */ | |
| 64 | opt_complementary = "-1:?2"; | 121 | opt_complementary = "-1:?2"; |
| 65 | opts = getopt32(argv, "ps:", &opt_s); | 122 | opts = getopt32(argv, "ps:", &opt_s); |
| 123 | } | ||
| 66 | argv += optind; | 124 | argv += optind; |
| 67 | 125 | ||
| 68 | if (argv[1]) | 126 | if (IS_NANDWRITE && argv[1]) |
| 69 | xmove_fd(xopen_stdin(argv[1]), STDIN_FILENO); | 127 | opt_f = argv[1]; |
| 128 | if (!LONE_DASH(opt_f)) { | ||
| 129 | int tmp_fd = xopen(opt_f, | ||
| 130 | IS_NANDDUMP ? O_WRONLY | O_TRUNC | O_CREAT : O_RDONLY | ||
| 131 | ); | ||
| 132 | xmove_fd(tmp_fd, IS_NANDDUMP ? STDOUT_FILENO : STDIN_FILENO); | ||
| 133 | } | ||
| 70 | 134 | ||
| 71 | fd = xopen(argv[0], O_RDWR); | 135 | fd = xopen(argv[0], O_RDWR); |
| 72 | xioctl(fd, MEMGETINFO, &meminfo); | 136 | xioctl(fd, MEMGETINFO, &meminfo); |
| 73 | 137 | ||
| 74 | mtdoffset = bb_strtou(opt_s, NULL, 0); | 138 | oob.start = 0; |
| 75 | if (errno) | 139 | oob.length = meminfo.oobsize; |
| 76 | bb_error_msg_and_die("invalid number '%s'", opt_s); | 140 | oob.ptr = oobbuf; |
| 141 | |||
| 142 | mtdoffset = xstrtou(opt_s, 0); | ||
| 143 | if (IS_NANDDUMP && (opts & OPT_l)) { | ||
| 144 | unsigned length = xstrtou(opt_l, 0); | ||
| 145 | if (length < meminfo.size - mtdoffset) | ||
| 146 | end_addr = mtdoffset + length; | ||
| 147 | } | ||
| 77 | 148 | ||
| 78 | /* Pull it into a CPU register (hopefully) - smaller code that way */ | 149 | /* Pull it into a CPU register (hopefully) - smaller code that way */ |
| 79 | meminfo_writesize = meminfo.writesize; | 150 | meminfo_writesize = meminfo.writesize; |
| @@ -91,20 +162,39 @@ int nandwrite_main(int argc UNUSED_PARAM, char **argv) | |||
| 91 | * bad. | 162 | * bad. |
| 92 | */ | 163 | */ |
| 93 | tmp = next_good_eraseblock(fd, &meminfo, blockstart); | 164 | tmp = next_good_eraseblock(fd, &meminfo, blockstart); |
| 94 | if (tmp != blockstart) /* bad block(s), advance mtdoffset */ | 165 | if (tmp != blockstart) { |
| 166 | /* bad block(s), advance mtdoffset */ | ||
| 167 | if (IS_NANDDUMP & !(opts & OPT_b)) { | ||
| 168 | int bad_len = MIN(tmp, end_addr) - mtdoffset; | ||
| 169 | dump_bad(&meminfo, bad_len, !(opts & OPT_o)); | ||
| 170 | } | ||
| 95 | mtdoffset = tmp; | 171 | mtdoffset = tmp; |
| 172 | } | ||
| 96 | } | 173 | } |
| 97 | 174 | ||
| 98 | cnt = -1; | 175 | cnt = -1; |
| 99 | while (mtdoffset < meminfo.size) { | 176 | limit = MIN(meminfo.size, end_addr); |
| 177 | while (mtdoffset < limit) { | ||
| 178 | int input_fd = IS_NANDWRITE ? STDIN_FILENO : fd; | ||
| 179 | int output_fd = IS_NANDWRITE ? fd : STDOUT_FILENO; | ||
| 180 | |||
| 100 | blockstart = mtdoffset & ~(meminfo.erasesize - 1); | 181 | blockstart = mtdoffset & ~(meminfo.erasesize - 1); |
| 101 | if (blockstart == mtdoffset) { | 182 | if (blockstart == mtdoffset) { |
| 102 | /* starting a new eraseblock */ | 183 | /* starting a new eraseblock */ |
| 103 | mtdoffset = next_good_eraseblock(fd, &meminfo, blockstart); | 184 | mtdoffset = next_good_eraseblock(fd, &meminfo, blockstart); |
| 104 | printf("Writing at 0x%08x\n", mtdoffset); | 185 | if (IS_NANDWRITE) |
| 186 | printf("Writing at 0x%08x\n", mtdoffset); | ||
| 187 | else if (mtdoffset > blockstart) { | ||
| 188 | int bad_len = MIN(mtdoffset, limit) - blockstart; | ||
| 189 | dump_bad(&meminfo, bad_len, !(opts & OPT_o)); | ||
| 190 | } | ||
| 191 | if (mtdoffset >= limit) | ||
| 192 | break; | ||
| 105 | } | 193 | } |
| 194 | xlseek(fd, mtdoffset, SEEK_SET); | ||
| 195 | |||
| 106 | /* get some more data from input */ | 196 | /* get some more data from input */ |
| 107 | cnt = full_read(STDIN_FILENO, filebuf, meminfo_writesize); | 197 | cnt = full_read(input_fd, filebuf, meminfo_writesize); |
| 108 | if (cnt == 0) { | 198 | if (cnt == 0) { |
| 109 | /* even with -p, we do not pad past the end of input | 199 | /* even with -p, we do not pad past the end of input |
| 110 | * (-p only zero-pads last incomplete page) | 200 | * (-p only zero-pads last incomplete page) |
| @@ -112,20 +202,29 @@ int nandwrite_main(int argc UNUSED_PARAM, char **argv) | |||
| 112 | break; | 202 | break; |
| 113 | } | 203 | } |
| 114 | if (cnt < meminfo_writesize) { | 204 | if (cnt < meminfo_writesize) { |
| 205 | if (IS_NANDDUMP) | ||
| 206 | bb_error_msg_and_die("short read"); | ||
| 115 | if (!(opts & OPT_p)) | 207 | if (!(opts & OPT_p)) |
| 116 | bb_error_msg_and_die("input size is not rounded up to page size, " | 208 | bb_error_msg_and_die("input size is not rounded up to page size, " |
| 117 | "use -p to zero pad"); | 209 | "use -p to zero pad"); |
| 118 | /* zero pad to end of write block */ | 210 | /* zero pad to end of write block */ |
| 119 | memset(filebuf + cnt, 0, meminfo_writesize - cnt); | 211 | memset(filebuf + cnt, 0, meminfo_writesize - cnt); |
| 120 | } | 212 | } |
| 121 | xlseek(fd, mtdoffset, SEEK_SET); | 213 | xwrite(output_fd, filebuf, meminfo_writesize); |
| 122 | xwrite(fd, filebuf, meminfo_writesize); | 214 | |
| 215 | if (IS_NANDDUMP && !(opts & OPT_o)) { | ||
| 216 | /* Dump OOB data */ | ||
| 217 | oob.start = mtdoffset; | ||
| 218 | xioctl(fd, MEMREADOOB, &oob); | ||
| 219 | xwrite(output_fd, oobbuf, meminfo.oobsize); | ||
| 220 | } | ||
| 221 | |||
| 123 | mtdoffset += meminfo_writesize; | 222 | mtdoffset += meminfo_writesize; |
| 124 | if (cnt < meminfo_writesize) | 223 | if (cnt < meminfo_writesize) |
| 125 | break; | 224 | break; |
| 126 | } | 225 | } |
| 127 | 226 | ||
| 128 | if (cnt != 0) { | 227 | if (IS_NANDWRITE && cnt != 0) { |
| 129 | /* We filled entire MTD, but did we reach EOF on input? */ | 228 | /* We filled entire MTD, but did we reach EOF on input? */ |
| 130 | if (full_read(STDIN_FILENO, filebuf, meminfo_writesize) != 0) { | 229 | if (full_read(STDIN_FILENO, filebuf, meminfo_writesize) != 0) { |
| 131 | /* no */ | 230 | /* no */ |
