[U-Boot] [PATCH 6/6 v2] Support environment in NAND

Guennadi Liakhovetski lg at denx.de
Fri Aug 29 11:29:50 CEST 2008


Add support for environment in NAND with automatic recognition, including
unaligned environment, bad-block skipping, redundant environment copy.
A new parameter is introduced to limit the number of sectors that may be
used on NAND, when skipping bad blocks, it is unused on NOR.

Take care to preserve backwards compatibility on NOR, including identical
erase / write behaviour.

Also fix some return codes.

Signed-off-by: Guennadi Liakhovetski <lg at denx.de>
---

I am not resending patches 1-5, they remain unchanged.

Changes since v1: instead of abusing the "erase sector size" parameter on 
NAND and retrieving it from the kernel, switched back to using 
user-provided erase size, and introduced a new parameter for the number of 
sectors, that can be used.

 tools/env/README        |    6 +
 tools/env/fw_env.c      |  504 +++++++++++++++++++++++++++++++++--------------
 tools/env/fw_env.config |    6 +-
 3 files changed, 365 insertions(+), 151 deletions(-)

diff --git a/tools/env/README b/tools/env/README
index f8a644e..f32f872 100644
--- a/tools/env/README
+++ b/tools/env/README
@@ -22,9 +22,11 @@ following lines are relevant:
 #define DEVICE1_OFFSET    0x0000
 #define ENV1_SIZE         0x4000
 #define DEVICE1_ESIZE     0x4000
+#define DEVICE1_ENVSECTORS     2
 #define DEVICE2_OFFSET    0x0000
 #define ENV2_SIZE         0x4000
 #define DEVICE2_ESIZE     0x4000
+#define DEVICE2_ENVSECTORS     2
 
 Current configuration matches the environment layout of the TRAB
 board.
@@ -46,3 +48,7 @@ then 1 sector.
 
 DEVICEx_ESIZE defines the size of the first sector in the flash
 partition where the environment resides.
+
+DEVICEx_ENVSECTORS defines the number of sectors that may be used for
+this environment instance. On NAND this is used to limit the range
+within which bad blocks are skipped, on NOR it is unused.
diff --git a/tools/env/fw_env.c b/tools/env/fw_env.c
index 931e647..9adfdad 100644
--- a/tools/env/fw_env.c
+++ b/tools/env/fw_env.c
@@ -44,11 +44,18 @@
 #define	CMD_GETENV	"fw_printenv"
 #define	CMD_SETENV	"fw_setenv"
 
+#define min(x, y) ({				\
+	typeof(x) _min1 = (x);			\
+	typeof(y) _min2 = (y);			\
+	(void) (&_min1 == &_min2);		\
+	_min1 < _min2 ? _min1 : _min2; })
+
 typedef struct envdev_s {
 	char devname[16];		/* Device name */
 	ulong devoff;			/* Device offset */
 	ulong env_size;			/* environment size */
 	ulong erase_size;		/* device erase size */
+	ulong env_sectors;		/* number of environment sectors */
 } envdev_t;
 
 static envdev_t envdevices[2];
@@ -58,6 +65,7 @@ static int dev_current;
 #define DEVOFFSET(i)  envdevices[(i)].devoff
 #define ENVSIZE(i)    envdevices[(i)].env_size
 #define DEVESIZE(i)   envdevices[(i)].erase_size
+#define ENVSECTORS(i) envdevices[(i)].env_sectors
 
 #define CFG_ENV_SIZE ENVSIZE(dev_current)
 
@@ -88,6 +96,7 @@ static int HaveRedundEnv = 0;
 #define ENV_FLAGS(e) e.image->redund.flags
 
 static unsigned char active_flag = 1;
+/* obsolete_flag must be 0 to efficiently set it on NOR flash without erasing */
 static unsigned char obsolete_flag = 0;
 
 
@@ -309,11 +318,12 @@ int fw_setenv (int argc, char *argv[])
 	char *name;
 
 	if (argc < 2) {
-		return EINVAL;
+		errno = EINVAL;
+		return -1;
 	}
 
 	if (env_init ())
-		return errno;
+		return -1;
 
 	name = argv[1];
 
@@ -325,7 +335,8 @@ int fw_setenv (int argc, char *argv[])
 			if (nxt >= &environment.data[ENV_SIZE]) {
 				fprintf (stderr, "## Error: "
 					"environment not terminated\n");
-				return EINVAL;
+				errno = EINVAL;
+				return -1;
 			}
 		}
 		if ((oldval = envmatch (name, env)) != NULL)
@@ -342,7 +353,8 @@ int fw_setenv (int argc, char *argv[])
 		if ((strcmp (name, "ethaddr") == 0) ||
 			(strcmp (name, "serial#") == 0)) {
 			fprintf (stderr, "Can't overwrite \"%s\"\n", name);
-			return EROFS;
+			errno = EROFS;
+			return -1;
 		}
 
 		if (*++nxt == '\0') {
@@ -413,179 +425,366 @@ int fw_setenv (int argc, char *argv[])
 	return 0;
 }
 
+static int flash_bad_block (int dev, int fd, struct mtd_info_user *mtdinfo,
+			    loff_t *blockstart, size_t blocklen)
+{
+	if (mtdinfo->type == MTD_NANDFLASH) {
+		int badblock = ioctl (fd, MEMGETBADBLOCK, blockstart);
+
+		if (badblock < 0) {
+			perror ("Cannot read bad block mark");
+			return badblock;
+		}
+
+		if (badblock) {
+#ifdef DEBUG
+			fprintf (stderr, "Bad block at 0x%llx, "
+				 "skipping\n", *blockstart);
+#endif
+			*blockstart += blocklen;
+			return badblock;
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * We are called with count == 0 for backing up as much data from the
+ * range as possible
+ */
 static int flash_read_buf (int dev, int fd, void *buf, size_t count,
-			   off_t offset)
+			   off_t offset, size_t range)
 {
+	struct mtd_info_user mtdinfo;
+	/* erase / write length - one block on NAND, 0 on NOR */
+	size_t blocklen;
+	/* progress counter */
+	size_t processed = 0;
+	/* current read length */
+	size_t readlen = count ? : range;
+	/* offset to the first erase block (aligned) */
+	off_t erase_offset;
+	/* offset inside the erase block to the start of the data */
+	off_t block_seek;
+	/* running start of the current block - MEMGETBADBLOCK needs 64 bits */
+	loff_t blockstart;
+	/* mode used, when reading out all "good" blocks from the range */
+	int backup_mode = !count;
 	int rc;
 
-	rc = lseek (fd, offset, SEEK_SET);
-	if (rc == -1) {
-		fprintf (stderr,
-			 "seek error on %s: %s\n",
-			 DEVNAME (dev), strerror (errno));
+	if (!count)
+		count = range;
+
+	rc = ioctl (fd, MEMGETINFO, &mtdinfo);
+	if (rc < 0) {
+		perror ("Cannot get MTD information");
 		return rc;
 	}
 
-	rc = read (fd, buf, count);
-	if (rc != count) {
-		fprintf (stderr,
-			 "Read error on %s: %s\n",
-			 DEVNAME (dev), strerror (errno));
-		return -1;
+	/* Erase sector size is always a power of 2 */
+	erase_offset = offset & ~(DEVESIZE (dev) - 1);
+
+	blockstart = erase_offset;
+	/* Offset inside a block */
+	block_seek = offset - erase_offset;
+
+	if (mtdinfo.type == MTD_NANDFLASH) {
+		/*
+		 * NAND: calculate which blocks we are reading. We have
+		 * to read one block at a time to skip bad blocks.
+		 */
+		blocklen = DEVESIZE (dev);
+		/* Limit to one block for the first read */
+		if (readlen > blocklen - block_seek)
+			readlen = blocklen - block_seek;
+	} else {
+		blocklen = 0;
 	}
 
-	return rc;
+	/* This only runs once for NOR flash */
+	while (processed < count) {
+		rc = flash_bad_block (dev, fd, &mtdinfo, &blockstart, blocklen);
+		if (rc < 0) {
+			return -1;
+		} else if (blockstart + block_seek + readlen >
+			   erase_offset + range) {
+			/* End of range is reached */
+			if (backup_mode) {
+				return processed;
+			} else {
+				fprintf (stderr,
+					 "Too few good blocks within range\n");
+				return -1;
+			}
+		} else if (rc) {
+			continue;
+		}
+
+		/*
+		 * If a block is bad, we retry in the next block
+		 * at the same offset - see common/env_nand.c::
+		 * writeenv()
+		 */
+		lseek (fd, blockstart + block_seek, SEEK_SET);
+
+		rc = read (fd, buf + processed, readlen);
+		if (rc != readlen) {
+			fprintf (stderr, "Read error on %s: %s\n",
+				 DEVNAME (dev), strerror (errno));
+			return -1;
+		}
+#ifdef DEBUG
+		fprintf (stderr, "Read 0x%x bytes at 0x%llx\n",
+			 rc, blockstart + block_seek);
+#endif
+		processed += readlen;
+		readlen = min(blocklen, count - processed);
+		block_seek = 0;
+		blockstart += blocklen;
+	}
+
+	return processed;
 }
 
-static int flash_write (void)
+/*
+ * Write count bytes at offset, but stay within
+ * ENVSETCORS (dev) sectors of DEVOFFSET (dev)
+ */
+static int __flash_write_buf (int dev, int fd, void *buf, size_t count,
+			      off_t offset, struct mtd_info_user *mtdinfo)
 {
-	int fd_current, fd_target, rc, dev_target;
-	erase_info_t erase_current = {}, erase_target;
 	char *data = NULL;
+	erase_info_t erase;
+	/* erase / write length - one block on NAND, whole area on NOR */
+	size_t blocklen;
+	/* whole area that can be erased - may include bad blocks */
+	size_t erase_len;
+	/* length of erase sector */
+	size_t erasesize;
+	/* progress counter */
+	size_t processed = 0;
+	/* total size to actually write - excludinig bad blocks */
+	size_t write_total;
+	/* offset to the first erase block (aligned) below offset */
 	off_t erase_offset;
-	struct mtd_info_user mtdinfo_target;
+	/* offset inside the erase block to the start of the data */
+	off_t block_seek;
+	/* end of the last block we may use */
+	off_t top_of_range;
+	/* running start of the current block - MEMGETBADBLOCK needs 64 bits */
+	loff_t blockstart;
+	int rc;
 
-	/* dev_current: fd_current, erase_current */
-	if ((fd_current = open (DEVNAME (dev_current), O_RDWR)) < 0) {
-		fprintf (stderr,
-			 "Can't open %s: %s\n",
-			 DEVNAME (dev_current), strerror (errno));
-		return -1;
-	}
+	erasesize = DEVESIZE (dev);
 
-	if (HaveRedundEnv) {
-		/* switch to next partition for writing */
-		dev_target = !dev_current;
-		/* dev_target: fd_target, erase_target */
-		if ((fd_target = open (DEVNAME (dev_target), O_RDWR)) < 0) {
-			fprintf (stderr,
-				 "Can't open %s: %s\n",
-				 DEVNAME (dev_target),
-				 strerror (errno));
-			return -1;
-		}
-	} else {
-		dev_target = dev_current;
-		fd_target = fd_current;
-	}
+	/* Erase sector size is always a power of 2 */
+	top_of_range = (DEVOFFSET (dev) & ~(erasesize - 1)) +
+		ENVSECTORS (dev) * erasesize;
+
+	erase_offset = offset & ~(erasesize - 1);
+
+	/* Maximum area we may use */
+	erase_len = top_of_range - erase_offset;
+
+	blockstart = erase_offset;
+	/* Offset inside a block */
+	block_seek = offset - erase_offset;
 
 	/*
-	 * Support environment anywhere within erase sectors: read out the
-	 * complete area to be erased, replace the environment image, write
-	 * the whole block back again.
+	 * Support data anywhere within erase sectors: read out the complete
+	 * area to be erased, replace the environment image, write the whole
+	 * block back again.
 	 */
-	if (DEVESIZE (dev_target) > CFG_ENV_SIZE) {
-		data = malloc (DEVESIZE (dev_target));
+	if (erase_len > count) {
+		data = malloc (erase_len);
 		if (!data) {
 			fprintf (stderr,
-				 "Cannot malloc %lu bytes: %s\n",
-				 DEVESIZE (dev_target),
-				 strerror (errno));
+				 "Cannot malloc %u bytes: %s\n",
+				 erase_len, strerror (errno));
 			return -1;
 		}
 
-		rc = ioctl (fd_target, MEMGETINFO, &mtdinfo_target);
-		if (rc < 0) {
-			perror ("Cannot get MTD information");
+		/*
+		 * This is different from a normal read. We have to read as much
+		 * as we can from a certain area, and it should be at least X
+		 * bytes, instead of having to read a fixed number of bytes as
+		 * usual. This also tells us how much data "fits" in the good
+		 * blocks in the area.
+		 */
+		write_total = flash_read_buf (dev, fd, data, 0,
+					      erase_offset, erase_len);
+		if (write_total < block_seek + CFG_ENV_SIZE)
 			return -1;
-		}
-
-		/* Erase sector size is always a power of 2 */
-		erase_offset = DEVOFFSET (dev_target) &
-			~(mtdinfo_target.erasesize - 1);
-
-		rc = flash_read_buf (dev_target, fd_target, data,
-				     DEVESIZE (dev_target), erase_offset);
-		if (rc < 0)
-			return rc;
 
 		/* Overwrite the old environment */
-		memcpy(DEVOFFSET (dev_target) - erase_offset + data,
-		       environment.image, CFG_ENV_SIZE);
+		memcpy(data + block_seek, buf, count);
 	} else {
+		if (erase_offset != offset || erase_len != count) {
+			fprintf (stderr, "Data doesn\'t fit in the sectors!\n");
+			return -1;
+		}
 		data = (char *)environment.image;
-		erase_offset = DEVOFFSET (dev_target);
+		write_total = count;
 	}
 
-	printf ("Unlocking flash...\n");
-	erase_target.length = DEVESIZE (dev_target);
-	erase_target.start = DEVOFFSET (dev_target);
-	ioctl (fd_target, MEMUNLOCK, &erase_target);
+	if (mtdinfo->type == MTD_NANDFLASH)
+		/*
+		 * NAND: calculate which blocks we are writing. We have
+		 * to write one block at a time to skip bad blocks.
+		 */
+		blocklen = erasesize;
+	else
+		blocklen = erase_len;
 
-	if (HaveRedundEnv) {
-		erase_current.length = DEVESIZE (dev_current);
-		erase_current.start = DEVOFFSET (dev_current);
-		ioctl (fd_current, MEMUNLOCK, &erase_current);
-		ENV_FLAGS(environment) = active_flag;
+	erase.length = blocklen;
+
+	while (processed < write_total) {
+		rc = flash_bad_block (dev, fd, mtdinfo, &blockstart, blocklen);
+		if (rc < 0) {
+			return rc;
+		} else if (blockstart + blocklen > top_of_range) {
+			fprintf (stderr, "End of range reached, aborting\n");
+			return -1;
+		} else if (rc) {
+			continue;
+		}
+
+		erase.start = blockstart;
+		ioctl (fd, MEMUNLOCK, &erase);
+
+		if (ioctl (fd, MEMERASE, &erase) != 0) {
+			fprintf (stderr, "MTD erase error on %s: %s\n",
+				 DEVNAME (dev),
+				 strerror (errno));
+			return -1;
+		}
+
+		if (lseek (fd, blockstart, SEEK_SET) == -1) {
+			fprintf (stderr,
+				 "Seek error on %s: %s\n",
+				 DEVNAME (dev), strerror (errno));
+			return -1;
+		}
+
+#ifdef DEBUG
+		printf ("Writing 0x%x bytes at 0x%llx\n", blocklen, blockstart);
+#endif
+		if (write (fd, data + processed, blocklen) != blocklen) {
+			fprintf (stderr, "Write error on %s: %s\n",
+				 DEVNAME (dev), strerror (errno));
+			return -1;
+		}
+
+		ioctl (fd, MEMLOCK, &erase);
+
+		processed += blocklen;
+		block_seek = 0;
+		blockstart += blocklen;
 	}
 
-	printf ("Done\n");
+	if (erase_len > CFG_ENV_SIZE)
+		free (data);
+
+	return processed;
+}
 
-	printf ("Erasing old environment...\n");
+static int flash_write_buf (int dev, int fd, void *buf, size_t count,
+			    off_t offset)
+{
+	struct mtd_info_user mtdinfo;
+	int rc;
 
-	if (ioctl (fd_target, MEMERASE, &erase_target) != 0) {
-		fprintf (stderr, "MTD erase error on %s: %s\n",
-			 DEVNAME (dev_target),
-			 strerror (errno));
+	rc = ioctl (fd, MEMGETINFO, &mtdinfo);
+	if (rc < 0) {
+		perror ("Cannot get MTD information");
 		return -1;
 	}
 
-	printf ("Done\n");
+	return __flash_write_buf (dev, fd, buf, count, offset, &mtdinfo);
+}
 
-	printf ("Writing environment to %s...\n", DEVNAME (dev_target));
-	if (lseek (fd_target, erase_offset, SEEK_SET) == -1) {
-		fprintf (stderr,
-			 "seek error on %s: %s\n",
-			 DEVNAME (dev_target), strerror (errno));
+static int flash_flag_obsolete (int dev, int fd, off_t offset)
+{
+	struct mtd_info_user mtdinfo;
+	int rc;
+
+	rc = ioctl (fd, MEMGETINFO, &mtdinfo);
+	if (rc < 0) {
+		perror ("Cannot get MTD information");
 		return -1;
 	}
 
-	if (write (fd_target, data, DEVESIZE (dev_target)) !=
-	    DEVESIZE (dev_target)) {
-		fprintf (stderr,
-			 "Write error on %s: %s\n",
-			 DEVNAME (dev_target), strerror (errno));
-		return -1;
+	if (mtdinfo.type == MTD_NANDFLASH) {
+		/*
+		 * No luck on NAND - we could save the erase, but have to write
+		 * a whole erase block anyway
+		 */
+		rc = __flash_write_buf (dev, fd, &obsolete_flag,
+					sizeof (obsolete_flag), offset,
+					&mtdinfo);
+	} else {
+		/* This relies on the fact, that obsolete_flag == 0 */
+		rc = lseek (fd, offset, SEEK_SET);
+		if (rc < 0) {
+			fprintf (stderr, "Cannot seek to set the flag on %s \n",
+				 DEVNAME (dev));
+			return rc;
+		}
+		rc = write (fd, &obsolete_flag, sizeof (obsolete_flag));
+		if (rc < 0)
+			perror ("Could not set obsolete flag");
 	}
+	return rc;
+}
 
-	if (DEVESIZE (dev_target) > CFG_ENV_SIZE)
-		free (data);
+static int flash_write (void)
+{
+	int fd_current, fd_target, rc, dev_target;
 
-	if (HaveRedundEnv) {
-		/* change flag on current active env partition */
-		if (lseek (fd_current, DEVOFFSET (dev_current) + sizeof (ulong),
-			   SEEK_SET) == -1) {
-			fprintf (stderr, "seek error on %s: %s\n",
-				 DEVNAME (dev_current), strerror (errno));
-			return -1;
-		}
-		if (write (fd_current, &obsolete_flag,
-			   sizeof (obsolete_flag)) != sizeof (obsolete_flag)) {
-			fprintf (stderr,
-				 "Write error on %s: %s\n",
-				 DEVNAME (dev_current), strerror (errno));
-			return -1;
-		}
+	/* dev_current: fd_current, erase_current */
+	if ((fd_current = open (DEVNAME (dev_current), O_RDWR)) < 0) {
+		fprintf (stderr,
+			 "Can't open %s: %s\n",
+			 DEVNAME (dev_current), strerror (errno));
+		return -1;
 	}
-	printf ("Done\n");
-	printf ("Locking ...\n");
-	ioctl (fd_target, MEMLOCK, &erase_target);
+
 	if (HaveRedundEnv) {
-		ioctl (fd_current, MEMLOCK, &erase_current);
-		if (close (fd_target)) {
+		/* switch to next partition for writing */
+		dev_target = !dev_current;
+		/* dev_target: fd_target, erase_target */
+		if ((fd_target = open (DEVNAME (dev_target), O_RDWR)) < 0) {
 			fprintf (stderr,
-				 "I/O error on %s: %s\n",
+				 "Can't open %s: %s\n",
 				 DEVNAME (dev_target),
 				 strerror (errno));
 			return -1;
 		}
+		ENV_FLAGS(environment) = active_flag;
+	} else {
+		dev_target = dev_current;
+		fd_target = fd_current;
+	}
+
+	printf ("Writing new environment at 0x%lx\n", DEVOFFSET (dev_target));
+	rc = flash_write_buf (dev_target, fd_target, environment.image,
+			      CFG_ENV_SIZE, DEVOFFSET (dev_target));
+	if (rc < 0)
+		return rc;
+
+	if (HaveRedundEnv) {
+		off_t offset = DEVOFFSET (dev_current) +
+			offsetof(union env_image, redund.flags);
+		printf ("Setting obsolete flag for environment at 0x%lx\n",
+			DEVOFFSET (dev_current));
+		flash_flag_obsolete(dev_current, fd_current, offset);
 	}
-	printf ("Done\n");
 
-	if (close (fd_current)) {
+	if (close (fd_target)) {
 		fprintf (stderr,
 			 "I/O error on %s: %s\n",
-			 DEVNAME (dev_current), strerror (errno));
+			 DEVNAME (dev_target), strerror (errno));
 		return -1;
 	}
 
@@ -604,10 +803,10 @@ static int flash_read (void)
 		return -1;
 	}
 
+	/* Only try within CFG_ENV_RANGE */
 	rc = flash_read_buf (dev_current, fd, environment.image, CFG_ENV_SIZE,
-			     DEVOFFSET (dev_current));
-	if (rc < 0)
-		return rc;
+			     DEVOFFSET (dev_current),
+			     ENVSECTORS (dev_current) * DEVESIZE (dev_current));
 
 	if (close (fd)) {
 		fprintf (stderr,
@@ -616,8 +815,7 @@ static int flash_read (void)
 		return -1;
 	}
 
-	/* everything ok */
-	return 0;
+	return rc < 0 ? rc : 0;
 }
 
 /*
@@ -649,13 +847,13 @@ static int env_init (void)
 	char flag1, flag2, *addr2;
 
 	if (parse_config ())		/* should fill envdevices */
-		return 1;
+		return -1;
 
 	if ((addr1 = calloc (1, CFG_ENV_SIZE)) == NULL) {
 		fprintf (stderr,
 			"Not enough memory for environment (%ld bytes)\n",
 			CFG_ENV_SIZE);
-		return errno;
+		return -1;
 	}
 
 	/* read environment from FLASH to local buffer */
@@ -663,9 +861,8 @@ static int env_init (void)
 	environment.data = HaveRedundEnv ? environment.image->redund.data :
 		environment.image->single.data;
 	dev_current = 0;
-	if (flash_read ()) {
-		return errno;
-	}
+	if (flash_read ())
+		return -1;
 
 	crc1 = crc32 (0, (uint8_t *) environment.data, ENV_SIZE);
 	crc1_ok = (crc1 == environment.image->single.crc);
@@ -683,13 +880,12 @@ static int env_init (void)
 			fprintf (stderr,
 				"Not enough memory for environment (%ld bytes)\n",
 				CFG_ENV_SIZE);
-			return errno;
+			return -1;
 		}
 		environment.image = (union env_image *)addr2;
 
-		if (flash_read ()) {
-			return errno;
-		}
+		if (flash_read ())
+			return -1;
 
 		crc2 = crc32 (0, (uint8_t *) environment.image->redund.data,
 			      ENV_SIZE);
@@ -767,18 +963,20 @@ static int parse_config ()
 	if (get_config (CONFIG_FILE)) {
 		fprintf (stderr,
 			"Cannot parse config file: %s\n", strerror (errno));
-		return 1;
+		return -1;
 	}
 #else
 	strcpy (DEVNAME (0), DEVICE1_NAME);
 	DEVOFFSET (0) = DEVICE1_OFFSET;
 	ENVSIZE (0) = ENV1_SIZE;
 	DEVESIZE (0) = DEVICE1_ESIZE;
+	ENVSECTORS (0) = DEVICE1_ENVSECTORS;
 #ifdef HAVE_REDUND
 	strcpy (DEVNAME (1), DEVICE2_NAME);
 	DEVOFFSET (1) = DEVICE2_OFFSET;
 	ENVSIZE (1) = ENV2_SIZE;
 	DEVESIZE (1) = DEVICE2_ESIZE;
+	ENVSECTORS (1) = DEVICE2_ENVSECTORS;
 	HaveRedundEnv = 1;
 #endif
 #endif
@@ -786,14 +984,14 @@ static int parse_config ()
 		fprintf (stderr,
 			"Cannot access MTD device %s: %s\n",
 			DEVNAME (0), strerror (errno));
-		return 1;
+		return -1;
 	}
 
 	if (HaveRedundEnv && stat (DEVNAME (1), &st)) {
 		fprintf (stderr,
 			"Cannot access MTD device %s: %s\n",
 			DEVNAME (1), strerror (errno));
-		return 1;
+		return -1;
 	}
 	return 0;
 }
@@ -806,21 +1004,27 @@ static int get_config (char *fname)
 	int rc;
 	char dump[128];
 
-	if ((fp = fopen (fname, "r")) == NULL) {
-		return 1;
-	}
-
-	while ((i < 2) && ((rc = fscanf (fp, "%s %lx %lx %lx",
-				  DEVNAME (i),
-				  &DEVOFFSET (i),
-				  &ENVSIZE (i),
-				  &DEVESIZE (i)  )) != EOF)) {
+	if ((fp = fopen (fname, "r")) == NULL)
+		return -1;
 
+	while (i < 2 && fgets (dump, sizeof (dump), fp)) {
 		/* Skip incomplete conversions and comment strings */
-		if ((rc < 3) || (*DEVNAME (i) == '#')) {
-			fgets (dump, sizeof (dump), fp);	/* Consume till end */
+		if (dump[0] == '#')
 			continue;
-		}
+
+		rc = sscanf (dump, "%s %lx %lx %lx %lx",
+			     DEVNAME (i),
+			     &DEVOFFSET (i),
+			     &ENVSIZE (i),
+			     &DEVESIZE (i),
+			     &ENVSECTORS (i));
+
+		if (rc < 4)
+			continue;
+
+		if (rc < 5)
+			/* Default - 1 sector */
+			ENVSECTORS (i) = 1;
 
 		i++;
 	}
@@ -829,7 +1033,7 @@ static int get_config (char *fname)
 	HaveRedundEnv = i - 1;
 	if (!i) {			/* No valid entries found */
 		errno = EINVAL;
-		return 1;
+		return -1;
 	} else
 		return 0;
 }
diff --git a/tools/env/fw_env.config b/tools/env/fw_env.config
index 2432bd8..0fe37c9 100644
--- a/tools/env/fw_env.config
+++ b/tools/env/fw_env.config
@@ -1,7 +1,11 @@
 # Configuration file for fw_(printenv/saveenv) utility.
 # Up to two entries are valid, in this case the redundand
 # environment sector is assumed present.
+# Notice, that the "Number of sectors" is ignored on NOR.
 
-# MTD device name	Device offset	Env. size	Flash sector size
+# MTD device name	Device offset	Env. size	Flash sector size	Number of sectors
 /dev/mtd1		0x0000		0x4000		0x4000
 /dev/mtd2		0x0000		0x4000		0x4000
+
+# NAND example
+#/dev/mtd0		0x4000		0x4000		0x20000			2
-- 
1.5.4



More information about the U-Boot mailing list