Browse Source

add a nand package with nand tools

Waldemar Brodkorb 14 years ago
parent
commit
641d418c8a
3 changed files with 1107 additions and 0 deletions
  1. 39 0
      package/nand/Makefile
  2. 421 0
      package/nand/src/nanddump.c
  3. 647 0
      package/nand/src/nandwrite.c

+ 39 - 0
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

+ 421 - 0
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 <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <asm/types.h>
+#include <mtd/mtd-user.h>
+
+#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);
+}

+ 647 - 0
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 <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <getopt.h>
+
+#include <asm/types.h>
+#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 <tommi.airikka@ericsson.com> at
+	 * <http://lists.infradead.org/pipermail/linux-mtd/2008-September/
+	 * 022913.html>
+	 */
+
+	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;
+}