]> www.infradead.org Git - mtd-utils.git/commitdiff
Update mtd-abi.h and use new NAND ECC functionality
authorThomas Gleixner <tglx@cruncher.tec.linutronix.de>
Mon, 29 May 2006 22:41:57 +0000 (00:41 +0200)
committerThomas Gleixner <tglx@cruncher.tec.linutronix.de>
Mon, 29 May 2006 22:41:57 +0000 (00:41 +0200)
The NAND rework exposes more information to userspace and
has a different mechanism to read raw FLASH contents without
ECC. Update nanddump and nandwrite. Use the new ECC statistics
ioctl to inform the user about corrected and uncorrectable
bitflips.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
include/mtd/mtd-abi.h
nanddump.c
nandwrite.c

index 54c673f9648d41d5926bd53d94f6c57713203c0a..c11a589bdedf18d73bdb0febec30fcdc6e038632 100644 (file)
@@ -99,6 +99,8 @@ struct otp_info {
 #define OTPGETREGIONINFO       _IOW('M', 15, struct otp_info)
 #define OTPLOCK                        _IOR('M', 16, struct otp_info)
 #define ECCGETLAYOUT           _IOR('M', 17, struct nand_ecclayout)
+#define ECCGETSTATS            _IOR('M', 18, struct mtd_ecc_stats)
+#define MTDFILEMODE            _IO('M', 19)
 
 /*
  * Obsolete legacy interface. Keep it in order not to break userspace
@@ -128,4 +130,29 @@ struct nand_ecclayout {
        struct nand_oobfree oobfree[MTD_MAX_OOBFREE_ENTRIES];
 };
 
+/**
+ * struct mtd_ecc_stats - error correction status
+ *
+ * @corrected: number of corrected bits
+ * @failed:    number of uncorrectable errors
+ * @badblocks: number of bad blocks in this partition
+ * @bbtblocks: number of blocks reserved for bad block tables
+ */
+struct mtd_ecc_stats {
+       uint32_t corrected;
+       uint32_t failed;
+       uint32_t badblocks;
+       uint32_t bbtblocks;
+};
+
+/*
+ * Read/write file modes for access to MTD
+ */
+enum mtd_file_modes {
+       MTD_MODE_NORMAL = MTD_OTP_OFF,
+       MTD_MODE_OTP_FACTORY = MTD_OTP_FACTORY,
+       MTD_MODE_OTP_USER = MTD_OTP_USER,
+       MTD_MODE_RAW,
+};
+
 #endif /* __MTD_ABI_H__ */
index a03c79a3b32897092613d238b5e3100140e1dcd8..b2323f65e893a8e9531af260eaed445dd8122220 100644 (file)
@@ -17,6 +17,7 @@
 
 #define _GNU_SOURCE
 #include <ctype.h>
+#include <errno.h>
 #include <fcntl.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -42,8 +43,8 @@ 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"
+              "           --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"
@@ -70,10 +71,10 @@ void display_version (void)
 
 // Option variables
 
-int    ignoreerrors;           // ignore errors
-int    pretty_print;           // print nice in ascii
+int    ignoreerrors;           // ignore errors
+int    pretty_print;           // print nice in ascii
 int    noecc;                  // don't error correct
-int    omitoob;                // omit oob data
+int    omitoob;                // omit oob data
 unsigned long  start_addr;     // start address
 unsigned long  length;         // dump length
 char    *mtddev;               // mtd device name
@@ -174,9 +175,11 @@ int main(int argc, char **argv)
        mtd_info_t meminfo;
        char pretty_buf[80];
        int oobinfochanged = 0 ;
-       struct nand_oobinfo old_oobinfo ;
+       struct nand_oobinfo old_oobinfo;
+       struct mtd_ecc_stats stat1, stat2;
+       int eccstats = 0;
 
-       process_options(argc, argv);
+       process_options(argc, argv);
 
        /* Open MTD device */
        if ((fd = open(mtddev, O_RDONLY)) == -1) {
@@ -203,24 +206,48 @@ int main(int argc, char **argv)
        oob.length = meminfo.oobsize;
 
        if (noecc)  {
-               if (ioctl (fd, MEMGETOOBSEL, &old_oobinfo) != 0) {
-                       perror ("MEMGETOOBSEL");
-                       close (fd);
-                       exit (1);
-               }
-               if (ioctl (fd, MEMSETOOBSEL, &none_oobinfo) != 0) {
-                       perror ("MEMSETOOBSEL");
+               switch (ioctl(fd, MTDFILEMODE, (void *) MTD_MODE_RAW)) {
+
+               case -ENOTTY:
+                       if (ioctl (fd, MEMGETOOBSEL, &old_oobinfo) != 0) {
+                               perror ("MEMGETOOBSEL");
+                               close (fd);
+                               exit (1);
+                       }
+                       if (ioctl (fd, MEMSETOOBSEL, &none_oobinfo) != 0) {
+                               perror ("MEMSETOOBSEL");
+                               close (fd);
+                               exit (1);
+                       }
+                       oobinfochanged = 1;
+                       break;
+
+               case 0:
+                       oobinfochanged = 2;
+                       break;
+               default:
+                       perror ("MTDFILEMODE");
                        close (fd);
                        exit (1);
                }
-               oobinfochanged = 1 ;
+       } else {
+
+               /* check if we can read ecc stats */
+               if (!ioctl(fd, ECCGETSTATS, &stat1)) {
+                       eccstats = 1;
+                       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. */
+       /* 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) {
+       } else if ((ofd = open(dumpfile, O_WRONLY | O_TRUNC | O_CREAT, 0644))== -1) {
                perror ("open outfile");
                close(fd);
                exit(1);
@@ -230,14 +257,16 @@ int main(int argc, char **argv)
        if (length)
                end_addr = start_addr + length;
        if (!length || end_addr > meminfo.size)
-               end_addr = meminfo.size;
+               end_addr = meminfo.size;
 
        bs = meminfo.writesize;
 
        /* Print informative message */
-       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);
+       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) {
@@ -263,6 +292,23 @@ int main(int argc, char **argv)
                        }
                }
 
+               /* 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) {
@@ -283,6 +329,8 @@ int main(int argc, char **argv)
                } else
                        write(ofd, readbuf, bs);
 
+
+
                if (omitoob)
                        continue;
 
@@ -325,7 +373,7 @@ int main(int argc, char **argv)
        }
 
        /* reset oobinfo */
-       if (oobinfochanged) {
+       if (oobinfochanged == 1) {
                if (ioctl (fd, MEMSETOOBSEL, &old_oobinfo) != 0) {
                        perror ("MEMSETOOBSEL");
                        close(fd);
@@ -341,7 +389,8 @@ int main(int argc, char **argv)
        return 0;
 
  closeall:
-       if (oobinfochanged) {
+       /* The new mode change is per file descriptor ! */
+       if (oobinfochanged == 1) {
                if (ioctl (fd, MEMSETOOBSEL, &old_oobinfo) != 0)  {
                        perror ("MEMSETOOBSEL");
                }
@@ -349,5 +398,4 @@ int main(int argc, char **argv)
        close(fd);
        close(ofd);
        exit(1);
-
 }
index 10489b7368212769d2ae5c2114c92247ebe5f59f..037474afca5596a842ad37e12d6bb387529c9e62 100644 (file)
@@ -2,7 +2,7 @@
  *  nandwrite.c
  *
  *  Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
- *               2003 Thomas Gleixner (tglx@linutronix.de)
+ *               2003 Thomas Gleixner (tglx@linutronix.de)
  *
  * $Id: nandwrite.c,v 1.32 2005/11/07 11:15:13 gleixner Exp $
  *
@@ -23,6 +23,7 @@
 
 #define _GNU_SOURCE
 #include <ctype.h>
+#include <errno.h>
 #include <fcntl.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -76,18 +77,18 @@ 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"
+              "  -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 device\n"
               "  -n, --noecc           write without ecc\n"
-              "  -o, --oob             image contains oob data\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");
+              "  -q, --quiet           don't display progress messages\n"
+              "      --help            display this help and exit\n"
+              "      --version         output version information and exit\n");
        exit(0);
 }
 
@@ -106,9 +107,9 @@ void display_version (void)
        exit(0);
 }
 
-char   *mtd_device, *img;
-int    mtdoffset = 0;
-int    quiet = 0;
+char   *mtd_device, *img;
+int    mtdoffset = 0;
+int    quiet = 0;
 int    writeoob = 0;
 int    autoplace = 0;
 int    forcejffs2 = 0;
@@ -129,7 +130,7 @@ void process_options (int argc, char *argv[])
                        {"help", no_argument, 0, 0},
                        {"version", no_argument, 0, 0},
                        {"autoplace", no_argument, 0, 'a'},
-                       {"blockalign", required_argument, 0, 'b'},
+                       {"blockalign", required_argument, 0, 'b'},
                        {"forcelegacy", no_argument, 0, 'f'},
                        {"jffs2", no_argument, 0, 'j'},
                        {"noecc", no_argument, 0, 'n'},
@@ -236,8 +237,9 @@ int main(int argc, char **argv)
                exit(1);
        }
 
-        /* Set erasesize to specified number of blocks - to match jffs2 (virtual) block size */
-        meminfo.erasesize *= blockalign;
+       /* 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) &&
@@ -248,32 +250,51 @@ int main(int argc, char **argv)
                exit(1);
        }
 
-       /* Read the current oob info */
-       if (ioctl (fd, MEMGETOOBSEL, &old_oobinfo) != 0) {
-               perror ("MEMGETOOBSEL");
-               close (fd);
-               exit (1);
-       }
-
-       // write without ecc ?
-       if (noecc) {
-               if (ioctl (fd, MEMSETOOBSEL, &none_oobinfo) != 0) {
-                       perror ("MEMSETOOBSEL");
+       if (autoplace) {
+               /* Read the current oob info */
+               if (ioctl (fd, MEMGETOOBSEL, &old_oobinfo) != 0) {
+                       perror ("MEMGETOOBSEL");
                        close (fd);
                        exit (1);
                }
-               oobinfochanged = 1;
+
+               // autoplace ECC ?
+               if (autoplace && (old_oobinfo.useecc != MTD_NANDECC_AUTOPLACE)) {
+
+                       if (ioctl (fd, MEMSETOOBSEL, &autoplace_oobinfo) != 0) {
+                               perror ("MEMSETOOBSEL");
+                               close (fd);
+                               exit (1);
+                       }
+                       oobinfochanged = 1;
+               }
        }
 
-       // autoplace ECC ?
-       if (autoplace && (old_oobinfo.useecc != MTD_NANDECC_AUTOPLACE)) {
+       if (noecc)  {
+               switch (ioctl(fd, MTDFILEMODE, (void *) MTD_MODE_RAW)) {
 
-               if (ioctl (fd, MEMSETOOBSEL, &autoplace_oobinfo) != 0) {
-                       perror ("MEMSETOOBSEL");
+               case -ENOTTY:
+                       if (ioctl (fd, MEMGETOOBSEL, &old_oobinfo) != 0) {
+                               perror ("MEMGETOOBSEL");
+                               close (fd);
+                               exit (1);
+                       }
+                       if (ioctl (fd, MEMSETOOBSEL, &none_oobinfo) != 0) {
+                               perror ("MEMSETOOBSEL");
+                               close (fd);
+                               exit (1);
+                       }
+                       oobinfochanged = 1;
+                       break;
+
+               case 0:
+                       oobinfochanged = 2;
+                       break;
+               default:
+                       perror ("MTDFILEMODE");
                        close (fd);
                        exit (1);
                }
-               oobinfochanged = 1;
        }
 
        /*
@@ -292,7 +313,7 @@ int main(int argc, char **argv)
                        goto restoreoob;
                }
                if (meminfo.oobsize == 8) {
-                       if (forceyaffs) {
+                       if (forceyaffs) {
                                fprintf (stderr, "YAFSS cannot operate on 256 Byte page size");
                                goto restoreoob;
                        }
@@ -316,7 +337,7 @@ int main(int argc, char **argv)
        }
 
        // get image length
-       imglen = lseek(ifd, 0, SEEK_END);
+       imglen = lseek(ifd, 0, SEEK_END);
        lseek (ifd, 0, SEEK_SET);
 
        pagelen = meminfo.writesize + ((writeoob == 1) ? meminfo.oobsize : 0);
@@ -346,26 +367,28 @@ int main(int argc, char **argv)
                while (blockstart != (mtdoffset & (~meminfo.erasesize + 1))) {
                        blockstart = mtdoffset & (~meminfo.erasesize + 1);
                        offs = blockstart;
-                       baderaseblock = 0;
+                       baderaseblock = 0;
                        if (!quiet)
                                fprintf (stdout, "Writing data to block %x\n", blockstart);
 
-                       /* Check all the blocks in an erase block for bad blocks */
+                       /* Check all the blocks in an erase block for bad blocks */
                        do {
-                               if ((ret = ioctl(fd, MEMGETBADBLOCK, &offs)) < 0) {
+                               if ((ret = ioctl(fd, MEMGETBADBLOCK, &offs)) < 0) {
                                        perror("ioctl(MEMGETBADBLOCK)");
                                        goto closeall;
                                }
                                if (ret == 1) {
                                        baderaseblock = 1;
-                                       if (!quiet)
-                                               fprintf (stderr, "Bad block at %x, %u block(s) from %x will be skipped\n", (int) offs, blockalign, blockstart);
-                                       }
+                                       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 ;
+                               offs +=  meminfo.erasesize / blockalign ;
                        } while ( offs < blockstart + meminfo.erasesize );
 
                }
@@ -440,7 +463,7 @@ int main(int argc, char **argv)
        close(ifd);
 
  restoreoob:
-       if (oobinfochanged) {
+       if (oobinfochanged == 1) {
                if (ioctl (fd, MEMSETOOBSEL, &old_oobinfo) != 0) {
                        perror ("MEMSETOOBSEL");
                        close (fd);