From 641d418c8a8155f505f6208b96699ffb659da3df Mon Sep 17 00:00:00 2001 From: Waldemar Brodkorb Date: Thu, 4 Feb 2010 00:58:31 +0100 Subject: add a nand package with nand tools --- package/nand/Makefile | 39 +++ package/nand/src/nanddump.c | 421 ++++++++++++++++++++++++++++ package/nand/src/nandwrite.c | 647 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1107 insertions(+) create mode 100644 package/nand/Makefile create mode 100644 package/nand/src/nanddump.c create mode 100644 package/nand/src/nandwrite.c (limited to 'package') diff --git a/package/nand/Makefile b/package/nand/Makefile new file mode 100644 index 000000000..c453e6a82 --- /dev/null +++ b/package/nand/Makefile @@ -0,0 +1,39 @@ +# This file is part of the OpenADK project. OpenADK is copyrighted +# material, please see the LICENCE file in the top-level directory. + +include ${TOPDIR}/rules.mk + +PKG_NAME:= nand +PKG_VERSION:= 1.0 +PKG_RELEASE:= 1 +PKG_DESCR:= NAND utility +PKG_SECTION:= base + +PKG_TARGET_DEPENDS:= rb532 + +WRKDIST= ${WRKDIR}/${PKG_NAME}-${PKG_VERSION} +NO_DISTFILES:= 1 + +include ${TOPDIR}/mk/package.mk + +$(eval $(call PKG_template,NAND,${PKG_NAME},${PKG_VERSION}-${PKG_RELEASE},${PKG_DEPENDS},${PKG_DESCR},${PKG_SECTION})) + +PKGDFLT_NAND= y + +BUILD_STYLE:= manual +INSTALL_STYLE:= manual + +do-build: + mkdir -p ${WRKBUILD} + ${CP} ./src/* ${WRKBUILD} + ${TARGET_CC} ${TCPPFLAGS} ${TCFLAGS} -o ${WRKBUILD}/nandwrite \ + ${WRKBUILD}/nandwrite.c + ${TARGET_CC} ${TCPPFLAGS} ${TCFLAGS} -o ${WRKBUILD}/nanddump \ + ${WRKBUILD}/nanddump.c + +do-install: + ${INSTALL_DIR} ${IDIR_NAND}/sbin + ${INSTALL_BIN} ${WRKBUILD}/nandwrite ${IDIR_NAND}/sbin + ${INSTALL_BIN} ${WRKBUILD}/nanddump ${IDIR_NAND}/sbin + +include ${TOPDIR}/mk/pkg-bottom.mk diff --git a/package/nand/src/nanddump.c b/package/nand/src/nanddump.c new file mode 100644 index 000000000..678d6847a --- /dev/null +++ b/package/nand/src/nanddump.c @@ -0,0 +1,421 @@ +/* + * nanddump.c + * + * Copyright (C) 2000 David Woodhouse (dwmw2@infradead.org) + * Steven J. Hill (sjhill@realitydiluted.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Overview: + * This utility dumps the contents of raw NAND chips or NAND + * chips contained in DoC devices. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define PROGRAM "nanddump" +#define VERSION "$Revision: 1.29 $" + +static struct nand_oobinfo none_oobinfo = { + .useecc = MTD_NANDECC_OFF, +}; + +static void display_help (void) +{ + printf( +"Usage: nanddump [OPTIONS] MTD-device\n" +"Dumps the contents of a nand mtd partition.\n" +"\n" +" --help Display this help and exit\n" +" --version Output version information and exit\n" +"-f file --file=file Dump to file\n" +"-i --ignoreerrors Ignore errors\n" +"-l length --length=length Length\n" +"-n --noecc Read without error correction\n" +"-o --omitoob Omit oob data\n" +"-b --omitbad Omit bad blocks from the dump\n" +"-p --prettyprint Print nice (hexdump)\n" +"-q --quiet Don't display progress and status messages\n" +"-s addr --startaddress=addr Start address\n" + ); + exit(EXIT_SUCCESS); +} + +static void display_version (void) +{ + printf(PROGRAM " " VERSION "\n" + "\n" + PROGRAM " comes with NO WARRANTY\n" + "to the extent permitted by law.\n" + "\n" + "You may redistribute copies of " PROGRAM "\n" + "under the terms of the GNU General Public Licence.\n" + "See the file `COPYING' for more information.\n"); + exit(EXIT_SUCCESS); +} + +// Option variables + +static bool ignoreerrors = false; // ignore errors +static bool pretty_print = false; // print nice in ascii +static bool noecc = false; // don't error correct +static bool omitoob = false; // omit oob data +static unsigned long start_addr; // start address +static unsigned long length; // dump length +static const char *mtddev; // mtd device name +static const char *dumpfile; // dump file name +static bool omitbad = false; +static bool quiet = false; // suppress diagnostic output + +static void process_options (int argc, char * const argv[]) +{ + int error = 0; + + for (;;) { + int option_index = 0; + static const char *short_options = "bs:f:il:opqn"; + static const struct option long_options[] = { + {"help", no_argument, 0, 0}, + {"version", no_argument, 0, 0}, + {"file", required_argument, 0, 'f'}, + {"ignoreerrors", no_argument, 0, 'i'}, + {"prettyprint", no_argument, 0, 'p'}, + {"omitoob", no_argument, 0, 'o'}, + {"omitbad", no_argument, 0, 'b'}, + {"startaddress", required_argument, 0, 's'}, + {"length", required_argument, 0, 'l'}, + {"noecc", no_argument, 0, 'n'}, + {"quiet", no_argument, 0, 'q'}, + {0, 0, 0, 0}, + }; + + int c = getopt_long(argc, argv, short_options, + long_options, &option_index); + if (c == EOF) { + break; + } + + switch (c) { + case 0: + switch (option_index) { + case 0: + display_help(); + break; + case 1: + display_version(); + break; + } + break; + case 'b': + omitbad = true; + break; + case 's': + start_addr = strtol(optarg, NULL, 0); + break; + case 'f': + if (!(dumpfile = strdup(optarg))) { + perror("stddup"); + exit(EXIT_FAILURE); + } + break; + case 'i': + ignoreerrors = true; + break; + case 'l': + length = strtol(optarg, NULL, 0); + break; + case 'o': + omitoob = true; + break; + case 'p': + pretty_print = true; + break; + case 'q': + quiet = true; + break; + case 'n': + noecc = true; + break; + case '?': + error++; + break; + } + } + + if (quiet && pretty_print) { + fprintf(stderr, "The quiet and pretty print options are mutually-\n" + "exclusive. Choose one or the other.\n"); + exit(EXIT_FAILURE); + } + + if ((argc - optind) != 1 || error) + display_help (); + + mtddev = argv[optind]; +} + +/* + * Buffers for reading data from flash + */ +static unsigned char readbuf[4096]; +static unsigned char oobbuf[128]; + +/* + * Main program + */ +int main(int argc, char * const argv[]) +{ + unsigned long ofs, end_addr = 0; + unsigned long long blockstart = 1; + int ret, i, fd, ofd, bs, badblock = 0; + struct mtd_oob_buf oob = {0, 16, oobbuf}; + mtd_info_t meminfo; + char pretty_buf[80]; + int oobinfochanged = 0 ; + struct nand_oobinfo old_oobinfo; + struct mtd_ecc_stats stat1, stat2; + bool eccstats = false; + + process_options(argc, argv); + + /* Open MTD device */ + if ((fd = open(mtddev, O_RDONLY)) == -1) { + perror(mtddev); + exit (EXIT_FAILURE); + } + + /* Fill in MTD device capability structure */ + if (ioctl(fd, MEMGETINFO, &meminfo) != 0) { + perror("MEMGETINFO"); + close(fd); + exit (EXIT_FAILURE); + } + + /* Make sure device page sizes are valid */ + if (!(meminfo.oobsize == 128 && meminfo.writesize == 4096) && + !(meminfo.oobsize == 64 && meminfo.writesize == 2048) && + !(meminfo.oobsize == 32 && meminfo.writesize == 1024) && + !(meminfo.oobsize == 16 && meminfo.writesize == 512) && + !(meminfo.oobsize == 8 && meminfo.writesize == 256)) { + fprintf(stderr, "Unknown flash (not normal NAND)\n"); + close(fd); + exit(EXIT_FAILURE); + } + /* Read the real oob length */ + oob.length = meminfo.oobsize; + + if (noecc) { + ret = ioctl(fd, MTDFILEMODE, (void *) MTD_MODE_RAW); + if (ret == 0) { + oobinfochanged = 2; + } else { + switch (errno) { + case ENOTTY: + if (ioctl (fd, MEMGETOOBSEL, &old_oobinfo) != 0) { + perror ("MEMGETOOBSEL"); + close (fd); + exit (EXIT_FAILURE); + } + if (ioctl (fd, MEMSETOOBSEL, &none_oobinfo) != 0) { + perror ("MEMSETOOBSEL"); + close (fd); + exit (EXIT_FAILURE); + } + oobinfochanged = 1; + break; + default: + perror ("MTDFILEMODE"); + close (fd); + exit (EXIT_FAILURE); + } + } + } else { + + /* check if we can read ecc stats */ + if (!ioctl(fd, ECCGETSTATS, &stat1)) { + eccstats = true; + if (!quiet) { + fprintf(stderr, "ECC failed: %d\n", stat1.failed); + fprintf(stderr, "ECC corrected: %d\n", stat1.corrected); + fprintf(stderr, "Number of bad blocks: %d\n", stat1.badblocks); + fprintf(stderr, "Number of bbt blocks: %d\n", stat1.bbtblocks); + } + } else + perror("No ECC status information available"); + } + + /* Open output file for writing. If file name is "-", write to standard + * output. */ + if (!dumpfile) { + ofd = STDOUT_FILENO; + } else if ((ofd = open(dumpfile, O_WRONLY | O_TRUNC | O_CREAT, 0644))== -1) { + perror (dumpfile); + close(fd); + exit(EXIT_FAILURE); + } + + /* Initialize start/end addresses and block size */ + if (length) + end_addr = start_addr + length; + if (!length || end_addr > meminfo.size) + end_addr = meminfo.size; + + bs = meminfo.writesize; + + /* Print informative message */ + + if (!quiet) { + fprintf(stderr, "Block size %u, page size %u, OOB size %u\n", + meminfo.erasesize, meminfo.writesize, meminfo.oobsize); + fprintf(stderr, + "Dumping data starting at 0x%08x and ending at 0x%08x...\n", + (unsigned int) start_addr, (unsigned int) end_addr); + } + /* Dump the flash contents */ + for (ofs = start_addr; ofs < end_addr ; ofs+=bs) { + + // new eraseblock , check for bad block + if (blockstart != (ofs & (~meminfo.erasesize + 1))) { + blockstart = ofs & (~meminfo.erasesize + 1); + if ((badblock = ioctl(fd, MEMGETBADBLOCK, &blockstart)) < 0) { + perror("ioctl(MEMGETBADBLOCK)"); + goto closeall; + } + } + + if (badblock) { + if (omitbad) + continue; + memset (readbuf, 0xff, bs); + } else { + /* Read page data and exit on failure */ + if (pread(fd, readbuf, bs, ofs) != bs) { + perror("pread"); + goto closeall; + } + } + + /* ECC stats available ? */ + if (eccstats) { + if (ioctl(fd, ECCGETSTATS, &stat2)) { + perror("ioctl(ECCGETSTATS)"); + goto closeall; + } + if (stat1.failed != stat2.failed) + fprintf(stderr, "ECC: %d uncorrectable bitflip(s)" + " at offset 0x%08lx\n", + stat2.failed - stat1.failed, ofs); + if (stat1.corrected != stat2.corrected) + fprintf(stderr, "ECC: %d corrected bitflip(s) at" + " offset 0x%08lx\n", + stat2.corrected - stat1.corrected, ofs); + stat1 = stat2; + } + + /* Write out page data */ + if (pretty_print) { + for (i = 0; i < bs; i += 16) { + sprintf(pretty_buf, + "0x%08x: %02x %02x %02x %02x %02x %02x %02x " + "%02x %02x %02x %02x %02x %02x %02x %02x %02x\n", + (unsigned int) (ofs + i), readbuf[i], + readbuf[i+1], readbuf[i+2], + readbuf[i+3], readbuf[i+4], + readbuf[i+5], readbuf[i+6], + readbuf[i+7], readbuf[i+8], + readbuf[i+9], readbuf[i+10], + readbuf[i+11], readbuf[i+12], + readbuf[i+13], readbuf[i+14], + readbuf[i+15]); + write(ofd, pretty_buf, 60); + } + } else + write(ofd, readbuf, bs); + + + + if (omitoob) + continue; + + if (badblock) { + memset (readbuf, 0xff, meminfo.oobsize); + } else { + /* Read OOB data and exit on failure */ + oob.start = ofs; + if (ioctl(fd, MEMREADOOB, &oob) != 0) { + perror("ioctl(MEMREADOOB)"); + goto closeall; + } + } + + /* Write out OOB data */ + if (pretty_print) { + if (meminfo.oobsize < 16) { + sprintf(pretty_buf, " OOB Data: %02x %02x %02x %02x %02x %02x " + "%02x %02x\n", + oobbuf[0], oobbuf[1], oobbuf[2], + oobbuf[3], oobbuf[4], oobbuf[5], + oobbuf[6], oobbuf[7]); + write(ofd, pretty_buf, 48); + continue; + } + + for (i = 0; i < meminfo.oobsize; i += 16) { + sprintf(pretty_buf, " OOB Data: %02x %02x %02x %02x %02x %02x " + "%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", + oobbuf[i], oobbuf[i+1], oobbuf[i+2], + oobbuf[i+3], oobbuf[i+4], oobbuf[i+5], + oobbuf[i+6], oobbuf[i+7], oobbuf[i+8], + oobbuf[i+9], oobbuf[i+10], oobbuf[i+11], + oobbuf[i+12], oobbuf[i+13], oobbuf[i+14], + oobbuf[i+15]); + write(ofd, pretty_buf, 60); + } + } else + write(ofd, oobbuf, meminfo.oobsize); + } + + /* reset oobinfo */ + if (oobinfochanged == 1) { + if (ioctl (fd, MEMSETOOBSEL, &old_oobinfo) != 0) { + perror ("MEMSETOOBSEL"); + close(fd); + close(ofd); + return EXIT_FAILURE; + } + } + /* Close the output file and MTD device */ + close(fd); + close(ofd); + + /* Exit happy */ + return EXIT_SUCCESS; + +closeall: + /* The new mode change is per file descriptor ! */ + if (oobinfochanged == 1) { + if (ioctl (fd, MEMSETOOBSEL, &old_oobinfo) != 0) { + perror ("MEMSETOOBSEL"); + } + } + close(fd); + close(ofd); + exit(EXIT_FAILURE); +} diff --git a/package/nand/src/nandwrite.c b/package/nand/src/nandwrite.c new file mode 100644 index 000000000..0b2a9ee8b --- /dev/null +++ b/package/nand/src/nandwrite.c @@ -0,0 +1,647 @@ +/* + * nandwrite.c + * + * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) + * 2003 Thomas Gleixner (tglx@linutronix.de) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Overview: + * This utility writes a binary image directly to a NAND flash + * chip or NAND chips contained in DoC devices. This is the + * "inverse operation" of nanddump. + * + * tglx: Major rewrite to handle bad blocks, write data with or without ECC + * write oob data only on request + * + * Bug/ToDo: + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "mtd/mtd-user.h" + +#define PROGRAM "nandwrite" +#define VERSION "$Revision: 1.32 $" + +#define MAX_PAGE_SIZE 4096 +#define MAX_OOB_SIZE 128 + +/* + * Buffer array used for writing data + */ +static unsigned char writebuf[MAX_PAGE_SIZE]; +static unsigned char oobbuf[MAX_OOB_SIZE]; +static unsigned char oobreadbuf[MAX_OOB_SIZE]; + +// oob layouts to pass into the kernel as default +static struct nand_oobinfo none_oobinfo = { + .useecc = MTD_NANDECC_OFF, +}; + +static struct nand_oobinfo jffs2_oobinfo = { + .useecc = MTD_NANDECC_PLACE, + .eccbytes = 6, + .eccpos = { 0, 1, 2, 3, 6, 7 } +}; + +static struct nand_oobinfo yaffs_oobinfo = { + .useecc = MTD_NANDECC_PLACE, + .eccbytes = 6, + .eccpos = { 8, 9, 10, 13, 14, 15} +}; + +static struct nand_oobinfo autoplace_oobinfo = { + .useecc = MTD_NANDECC_AUTOPLACE +}; + +static void display_help (void) +{ + printf( +"Usage: nandwrite [OPTION] MTD_DEVICE [INPUTFILE|-]\n" +"Writes to the specified MTD device.\n" +"\n" +" -a, --autoplace Use auto oob layout\n" +" -j, --jffs2 Force jffs2 oob layout (legacy support)\n" +" -y, --yaffs Force yaffs oob layout (legacy support)\n" +" -f, --forcelegacy Force legacy support on autoplacement-enabled mtd\n" +" device\n" +" -m, --markbad Mark blocks bad if write fails\n" +" -n, --noecc Write without ecc\n" +" -o, --oob Image contains oob data\n" +" -s addr, --start=addr Set start address (default is 0)\n" +" -p, --pad Pad to page size\n" +" -b, --blockalign=1|2|4 Set multiple of eraseblocks to align to\n" +" -q, --quiet Don't display progress messages\n" +" --help Display this help and exit\n" +" --version Output version information and exit\n" + ); + exit (EXIT_SUCCESS); +} + +static void display_version (void) +{ + printf(PROGRAM " " VERSION "\n" + "\n" + "Copyright (C) 2003 Thomas Gleixner \n" + "\n" + PROGRAM " comes with NO WARRANTY\n" + "to the extent permitted by law.\n" + "\n" + "You may redistribute copies of " PROGRAM "\n" + "under the terms of the GNU General Public Licence.\n" + "See the file `COPYING' for more information.\n"); + exit (EXIT_SUCCESS); +} + +static const char *standard_input = "-"; +static const char *mtd_device, *img; +static int mtdoffset = 0; +static bool quiet = false; +static bool writeoob = false; +static bool autoplace = false; +static bool markbad = false; +static bool forcejffs2 = false; +static bool forceyaffs = false; +static bool forcelegacy = false; +static bool noecc = false; +static bool pad = false; +static int blockalign = 1; /*default to using 16K block size */ + +static void process_options (int argc, char * const argv[]) +{ + int error = 0; + + for (;;) { + int option_index = 0; + static const char *short_options = "ab:fjmnopqs:y"; + static const struct option long_options[] = { + {"help", no_argument, 0, 0}, + {"version", no_argument, 0, 0}, + {"autoplace", no_argument, 0, 'a'}, + {"blockalign", required_argument, 0, 'b'}, + {"forcelegacy", no_argument, 0, 'f'}, + {"jffs2", no_argument, 0, 'j'}, + {"markbad", no_argument, 0, 'm'}, + {"noecc", no_argument, 0, 'n'}, + {"oob", no_argument, 0, 'o'}, + {"pad", no_argument, 0, 'p'}, + {"quiet", no_argument, 0, 'q'}, + {"start", required_argument, 0, 's'}, + {"yaffs", no_argument, 0, 'y'}, + {0, 0, 0, 0}, + }; + + int c = getopt_long(argc, argv, short_options, + long_options, &option_index); + if (c == EOF) { + break; + } + + switch (c) { + case 0: + switch (option_index) { + case 0: + display_help(); + break; + case 1: + display_version(); + break; + } + break; + case 'q': + quiet = true; + break; + case 'a': + autoplace = true; + break; + case 'j': + forcejffs2 = true; + break; + case 'y': + forceyaffs = true; + break; + case 'f': + forcelegacy = true; + break; + case 'n': + noecc = true; + break; + case 'm': + markbad = true; + break; + case 'o': + writeoob = true; + break; + case 'p': + pad = true; + break; + case 's': + mtdoffset = strtol (optarg, NULL, 0); + break; + case 'b': + blockalign = atoi (optarg); + break; + case '?': + error++; + break; + } + } + + if (mtdoffset < 0) { + fprintf(stderr, "Can't specify a negative device offset `%d'\n", + mtdoffset); + exit (EXIT_FAILURE); + } + + argc -= optind; + argv += optind; + + /* + * There must be at least the MTD device node positional + * argument remaining and, optionally, the input file. + */ + + if (argc < 1 || argc > 2 || error) + display_help (); + + mtd_device = argv[0]; + + /* + * Standard input may be specified either explictly as "-" or + * implicity by simply omitting the second of the two + * positional arguments. + */ + + img = ((argc == 2) ? argv[1] : standard_input); +} + +static void erase_buffer(void *buffer, size_t size) +{ + const uint8_t kEraseByte = 0xff; + + if (buffer != NULL && size > 0) { + memset(buffer, kEraseByte, size); + } +} + +/* + * Main program + */ +int main(int argc, char * const argv[]) +{ + int cnt = 0; + int fd = -1; + int ifd = -1; + int imglen = 0, pagelen; + bool baderaseblock = false; + int blockstart = -1; + struct mtd_info_user meminfo; + struct mtd_oob_buf oob; + loff_t offs; + int ret, readlen; + int oobinfochanged = 0; + struct nand_oobinfo old_oobinfo; + int readcnt = 0; + + process_options(argc, argv); + + erase_buffer(oobbuf, sizeof(oobbuf)); + + if (pad && writeoob) { + fprintf(stderr, "Can't pad when oob data is present.\n"); + exit (EXIT_FAILURE); + } + + /* Open the device */ + if ((fd = open(mtd_device, O_RDWR)) == -1) { + perror(mtd_device); + exit (EXIT_FAILURE); + } + + /* Fill in MTD device capability structure */ + if (ioctl(fd, MEMGETINFO, &meminfo) != 0) { + perror("MEMGETINFO"); + close(fd); + exit (EXIT_FAILURE); + } + + /* Set erasesize to specified number of blocks - to match jffs2 + * (virtual) block size */ + meminfo.erasesize *= blockalign; + + /* Make sure device page sizes are valid */ + if (!(meminfo.oobsize == 16 && meminfo.writesize == 512) && + !(meminfo.oobsize == 8 && meminfo.writesize == 256) && + !(meminfo.oobsize == 64 && meminfo.writesize == 2048) && + !(meminfo.oobsize == 128 && meminfo.writesize == 4096)) { + fprintf(stderr, "Unknown flash (not normal NAND)\n"); + close(fd); + exit (EXIT_FAILURE); + } + + if (autoplace) { + /* Read the current oob info */ + if (ioctl (fd, MEMGETOOBSEL, &old_oobinfo) != 0) { + perror ("MEMGETOOBSEL"); + close (fd); + exit (EXIT_FAILURE); + } + + // autoplace ECC ? + if (autoplace && (old_oobinfo.useecc != MTD_NANDECC_AUTOPLACE)) { + + if (ioctl (fd, MEMSETOOBSEL, &autoplace_oobinfo) != 0) { + perror ("MEMSETOOBSEL"); + close (fd); + exit (EXIT_FAILURE); + } + oobinfochanged = 1; + } + } + + if (noecc) { + ret = ioctl(fd, MTDFILEMODE, (void *) MTD_MODE_RAW); + if (ret == 0) { + oobinfochanged = 2; + } else { + switch (errno) { + case ENOTTY: + if (ioctl (fd, MEMGETOOBSEL, &old_oobinfo) != 0) { + perror ("MEMGETOOBSEL"); + close (fd); + exit (EXIT_FAILURE); + } + if (ioctl (fd, MEMSETOOBSEL, &none_oobinfo) != 0) { + perror ("MEMSETOOBSEL"); + close (fd); + exit (EXIT_FAILURE); + } + oobinfochanged = 1; + break; + default: + perror ("MTDFILEMODE"); + close (fd); + exit (EXIT_FAILURE); + } + } + } + + /* + * force oob layout for jffs2 or yaffs ? + * Legacy support + */ + if (forcejffs2 || forceyaffs) { + struct nand_oobinfo *oobsel = forcejffs2 ? &jffs2_oobinfo : &yaffs_oobinfo; + + if (autoplace) { + fprintf(stderr, "Autoplacement is not possible for legacy -j/-y options\n"); + goto restoreoob; + } + if ((old_oobinfo.useecc == MTD_NANDECC_AUTOPLACE) && !forcelegacy) { + fprintf(stderr, "Use -f option to enforce legacy placement on autoplacement enabled mtd device\n"); + goto restoreoob; + } + if (meminfo.oobsize == 8) { + if (forceyaffs) { + fprintf (stderr, "YAFSS cannot operate on 256 Byte page size"); + goto restoreoob; + } + /* Adjust number of ecc bytes */ + jffs2_oobinfo.eccbytes = 3; + } + + if (ioctl (fd, MEMSETOOBSEL, oobsel) != 0) { + perror ("MEMSETOOBSEL"); + goto restoreoob; + } + } + + oob.length = meminfo.oobsize; + oob.ptr = noecc ? oobreadbuf : oobbuf; + + /* Determine if we are reading from standard input or from a file. */ + if (strcmp(img, standard_input) == 0) { + ifd = STDIN_FILENO; + } else { + ifd = open(img, O_RDONLY); + } + + if (ifd == -1) { + perror(img); + goto restoreoob; + } + + /* For now, don't allow writing oob when reading from standard input. */ + if (ifd == STDIN_FILENO && writeoob) { + fprintf(stderr, "Can't write oob when reading from standard input.\n"); + goto closeall; + } + + pagelen = meminfo.writesize + ((writeoob) ? meminfo.oobsize : 0); + + /* + * For the standard input case, the input size is merely an + * invariant placeholder and is set to the write page + * size. Otherwise, just use the input file size. + * + * TODO: Add support for the -l,--length=length option (see + * previous discussion by Tommi Airikka at + * + */ + + if (ifd == STDIN_FILENO) { + imglen = pagelen; + } else { + imglen = lseek(ifd, 0, SEEK_END); + lseek (ifd, 0, SEEK_SET); + } + + // Check, if file is page-aligned + if ((!pad) && ((imglen % pagelen) != 0)) { + fprintf (stderr, "Input file is not page-aligned. Use the padding " + "option.\n"); + goto closeall; + } + + // Check, if length fits into device + if ( ((imglen / pagelen) * meminfo.writesize) > (meminfo.size - mtdoffset)) { + fprintf (stderr, "Image %d bytes, NAND page %d bytes, OOB area %u bytes, device size %u bytes\n", + imglen, pagelen, meminfo.writesize, meminfo.size); + perror ("Input file does not fit into device"); + goto closeall; + } + + /* + * Get data from input and write to the device while there is + * still input to read and we are still within the device + * bounds. Note that in the case of standard input, the input + * length is simply a quasi-boolean flag whose values are page + * length or zero. + */ + while (imglen && (mtdoffset < meminfo.size)) { + // new eraseblock , check for bad block(s) + // Stay in the loop to be sure if the mtdoffset changes because + // of a bad block, that the next block that will be written to + // is also checked. Thus avoiding errors if the block(s) after the + // skipped block(s) is also bad (number of blocks depending on + // the blockalign + while (blockstart != (mtdoffset & (~meminfo.erasesize + 1))) { + blockstart = mtdoffset & (~meminfo.erasesize + 1); + offs = blockstart; + baderaseblock = false; + if (!quiet) + fprintf (stdout, "Writing data to block %d at offset 0x%x\n", + blockstart / meminfo.erasesize, blockstart); + + /* Check all the blocks in an erase block for bad blocks */ + do { + if ((ret = ioctl(fd, MEMGETBADBLOCK, &offs)) < 0) { + perror("ioctl(MEMGETBADBLOCK)"); + goto closeall; + } + if (ret == 1) { + baderaseblock = true; + if (!quiet) + fprintf (stderr, "Bad block at %x, %u block(s) " + "from %x will be skipped\n", + (int) offs, blockalign, blockstart); + } + + if (baderaseblock) { + mtdoffset = blockstart + meminfo.erasesize; + } + offs += meminfo.erasesize / blockalign ; + } while ( offs < blockstart + meminfo.erasesize ); + + } + + readlen = meminfo.writesize; + + if (ifd != STDIN_FILENO) { + int tinycnt = 0; + + if (pad && (imglen < readlen)) + { + readlen = imglen; + erase_buffer(writebuf + readlen, meminfo.writesize - readlen); + } + + /* Read Page Data from input file */ + while(tinycnt < readlen) { + cnt = read(ifd, writebuf + tinycnt, readlen - tinycnt); + if (cnt == 0) { // EOF + break; + } else if (cnt < 0) { + perror ("File I/O error on input file"); + goto closeall; + } + tinycnt += cnt; + } + } else { + int tinycnt = 0; + + while(tinycnt < readlen) { + cnt = read(ifd, writebuf + tinycnt, readlen - tinycnt); + if (cnt == 0) { // EOF + break; + } else if (cnt < 0) { + perror ("File I/O error on stdin"); + goto closeall; + } + tinycnt += cnt; + } + + /* No padding needed - we are done */ + if (tinycnt == 0) { + imglen = 0; + break; + } + + /* No more bytes - we are done after writing the remaining bytes */ + if (cnt == 0) { + imglen = 0; + } + + /* Padding */ + if (pad && (tinycnt < readlen)) { + erase_buffer(writebuf + tinycnt, meminfo.writesize - tinycnt); + } + } + + if (writeoob) { + int tinycnt = 0; + + while(tinycnt < readlen) { + cnt = read(ifd, oobreadbuf + tinycnt, meminfo.oobsize - tinycnt); + if (cnt == 0) { // EOF + break; + } else if (cnt < 0) { + perror ("File I/O error on input file"); + goto closeall; + } + tinycnt += cnt; + } + + if (!noecc) { + int i, start, len; + /* + * We use autoplacement and have the oobinfo with the autoplacement + * information from the kernel available + * + * Modified to support out of order oobfree segments, + * such as the layout used by diskonchip.c + */ + if (!oobinfochanged && (old_oobinfo.useecc == MTD_NANDECC_AUTOPLACE)) { + for (i = 0;old_oobinfo.oobfree[i][1]; i++) { + /* Set the reserved bytes to 0xff */ + start = old_oobinfo.oobfree[i][0]; + len = old_oobinfo.oobfree[i][1]; + memcpy(oobbuf + start, + oobreadbuf + start, + len); + } + } else { + /* Set at least the ecc byte positions to 0xff */ + start = old_oobinfo.eccbytes; + len = meminfo.oobsize - start; + memcpy(oobbuf + start, + oobreadbuf + start, + len); + } + } + /* Write OOB data first, as ecc will be placed in there*/ + oob.start = mtdoffset; + if (ioctl(fd, MEMWRITEOOB, &oob) != 0) { + perror ("ioctl(MEMWRITEOOB)"); + goto closeall; + } + imglen -= meminfo.oobsize; + } + + /* Write out the Page data */ + if (pwrite(fd, writebuf, meminfo.writesize, mtdoffset) != meminfo.writesize) { + int rewind_blocks; + off_t rewind_bytes; + erase_info_t erase; + + perror ("pwrite"); + /* Must rewind to blockstart if we can */ + rewind_blocks = (mtdoffset - blockstart) / meminfo.writesize; /* Not including the one we just attempted */ + rewind_bytes = (rewind_blocks * meminfo.writesize) + readlen; + if (writeoob) + rewind_bytes += (rewind_blocks + 1) * meminfo.oobsize; + if (lseek(ifd, -rewind_bytes, SEEK_CUR) == -1) { + perror("lseek"); + fprintf(stderr, "Failed to seek backwards to recover from write error\n"); + goto closeall; + } + erase.start = blockstart; + erase.length = meminfo.erasesize; + fprintf(stderr, "Erasing failed write from %08lx-%08lx\n", + (long)erase.start, (long)erase.start+erase.length-1); + if (ioctl(fd, MEMERASE, &erase) != 0) { + perror("MEMERASE"); + goto closeall; + } + + if (markbad) { + loff_t bad_addr = mtdoffset & (~(meminfo.erasesize / blockalign) + 1); + fprintf(stderr, "Marking block at %08lx bad\n", (long)bad_addr); + if (ioctl(fd, MEMSETBADBLOCK, &bad_addr)) { + perror("MEMSETBADBLOCK"); + /* But continue anyway */ + } + } + mtdoffset = blockstart + meminfo.erasesize; + imglen += rewind_blocks * meminfo.writesize; + + continue; + } + if (ifd != STDIN_FILENO) { + imglen -= readlen; + } + mtdoffset += meminfo.writesize; + } + +closeall: + close(ifd); + +restoreoob: + if (oobinfochanged == 1) { + if (ioctl (fd, MEMSETOOBSEL, &old_oobinfo) != 0) { + perror ("MEMSETOOBSEL"); + close (fd); + exit (EXIT_FAILURE); + } + } + + close(fd); + + if ((ifd != STDIN_FILENO) && (imglen > 0)) { + perror ("Data was only partially written due to error\n"); + exit (EXIT_FAILURE); + } + + /* Return happy */ + return EXIT_SUCCESS; +} -- cgit v1.2.3