diff options
-rw-r--r-- | miscutils/nandwrite.c | 128 |
1 files changed, 128 insertions, 0 deletions
diff --git a/miscutils/nandwrite.c b/miscutils/nandwrite.c new file mode 100644 index 000000000..a20f7f384 --- /dev/null +++ b/miscutils/nandwrite.c @@ -0,0 +1,128 @@ +/* + * nandwrite.c - ported to busybox from mtd-utils + * + * Author: Baruch Siach <baruch@tkos.co.il>, Orex Computed Radiography + * + * Licensed under GPLv2, see file LICENSE in this source tree. + * + * TODO: add support for large (>4GB) MTD devices + */ + +//applet:IF_NANDWRITE(APPLET(nandwrite, _BB_DIR_USR_SBIN, _BB_SUID_DROP)) + +//kbuild:lib-$(CONFIG_NANDWRITE) += nandwrite.o + +//config:config NANDWRITE +//config: bool "nandwrite" +//config: default n +//config: depends on PLATFORM_LINUX +//config: help +//config: Write to the specified MTD device, with bad blocks awareness + +#include "libbb.h" +#include <mtd/mtd-user.h> + +//usage:#define nandwrite_trivial_usage +//usage: "[-p] [-s ADDR] MTD_DEVICE [FILE]" +//usage:#define nandwrite_full_usage "\n\n" +//usage: "Write to the specified MTD device\n" +//usage: "\nOptions:" +//usage: "\n -p Pad to page size" +//usage: "\n -s ADDR Start address" + +static unsigned next_good_eraseblock(int fd, struct mtd_info_user *meminfo, + unsigned block_offset) +{ + while (1) { + loff_t offs; + if (block_offset >= meminfo->size) + bb_error_msg_and_die("not enough space in MTD device"); + offs = block_offset; + if (xioctl(fd, MEMGETBADBLOCK, &offs) == 0) + return block_offset; + /* ioctl returned 1 => "bad block" */ + printf("Skipping bad block at 0x%08x\n", block_offset); + block_offset += meminfo->erasesize; + } +} + +int nandwrite_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int nandwrite_main(int argc UNUSED_PARAM, char **argv) +{ + unsigned opts; + int fd; + ssize_t cnt; + unsigned mtdoffset, meminfo_writesize; + struct mtd_info_user meminfo; + unsigned char *filebuf; + const char *opt_s = "0"; + enum { + OPT_p = (1 << 0), + OPT_s = (1 << 1), + }; + + opt_complementary = "-1:?2"; + opts = getopt32(argv, "ps:", &opt_s); + argv += optind; + + if (argv[1]) + xmove_fd(xopen_stdin(argv[1]), STDIN_FILENO); + + fd = xopen(argv[0], O_RDWR); + xioctl(fd, MEMGETINFO, &meminfo); + + mtdoffset = xatou(opt_s); + + /* Pull it into a CPU register (hopefully) - smaller code that way */ + meminfo_writesize = meminfo.writesize; + + if (mtdoffset & (meminfo_writesize - 1)) + bb_error_msg_and_die("start address is not page aligned"); + + filebuf = xmalloc(meminfo_writesize); + + cnt = -1; + while (mtdoffset < meminfo.size) { + unsigned blockstart = mtdoffset & ~(meminfo.erasesize - 1); + if (blockstart == mtdoffset) { + /* starting a new eraseblock */ + mtdoffset = next_good_eraseblock(fd, &meminfo, blockstart); + printf("Writing at 0x%08x\n", mtdoffset); + } + /* get some more data from input */ + cnt = full_read(STDIN_FILENO, filebuf, meminfo_writesize); + if (cnt == 0) { + /* even with -p, we do not pad past the end of input + * (-p only zero-pads last incomplete page) + */ + break; + } + if (cnt < meminfo_writesize) { + if (!(opts & OPT_p)) + bb_error_msg_and_die("input size is not rounded up to page size, " + "use -p to zero pad"); + /* zero pad to end of write block */ + memset(filebuf + cnt, 0, meminfo_writesize - cnt); + } + xlseek(fd, mtdoffset, SEEK_SET); + xwrite(fd, filebuf, meminfo_writesize); + mtdoffset += meminfo_writesize; + if (cnt < meminfo_writesize) + break; + } + + if (cnt != 0) { + /* We filled entire MTD, but did we reach EOF on input? */ + if (full_read(STDIN_FILENO, filebuf, meminfo_writesize) != 0) { + /* no */ + bb_error_msg_and_die("not enough space in MTD device"); + } + } + + if (ENABLE_FEATURE_CLEAN_UP) { + free(filebuf); + close(fd); + } + + return EXIT_SUCCESS; +} |