procd: various improvements to nand.sh
[openwrt.git] / package / system / mtd / src / mtd.c
index 73335f3..6dd6eea 100644 (file)
@@ -21,6 +21,7 @@
  * The code is based on the linux-mtd examples.
  */
 
+#define _GNU_SOURCE
 #include <limits.h>
 #include <unistd.h>
 #include <stdlib.h>
@@ -58,6 +59,7 @@ int no_erase;
 int mtdsize = 0;
 int erasesize = 0;
 int jffs2_skip_bytes=0;
+int mtdtype = 0;
 
 int mtd_open(const char *mtd, bool block)
 {
@@ -66,10 +68,12 @@ int mtd_open(const char *mtd, bool block)
        int i;
        int ret;
        int flags = O_RDWR | O_SYNC;
+       char name[PATH_MAX];
 
+       snprintf(name, sizeof(name), "\"%s\"", mtd);
        if ((fp = fopen("/proc/mtd", "r"))) {
                while (fgets(dev, sizeof(dev), fp)) {
-                       if (sscanf(dev, "mtd%d:", &i) && strstr(dev, mtd)) {
+                       if (sscanf(dev, "mtd%d:", &i) && strstr(dev, name)) {
                                snprintf(dev, sizeof(dev), "/dev/mtd%s/%d", (block ? "block" : ""), i);
                                if ((ret=open(dev, flags))<0) {
                                        snprintf(dev, sizeof(dev), "/dev/mtd%s%d", (block ? "block" : ""), i);
@@ -103,10 +107,28 @@ int mtd_check_open(const char *mtd)
        }
        mtdsize = mtdInfo.size;
        erasesize = mtdInfo.erasesize;
+       mtdtype = mtdInfo.type;
 
        return fd;
 }
 
+int mtd_block_is_bad(int fd, int offset)
+{
+       int r = 0;
+       loff_t o = offset;
+
+       if (mtdtype == MTD_NANDFLASH)
+       {
+               r = ioctl(fd, MEMGETBADBLOCK, &o);
+               if (r < 0)
+               {
+                       fprintf(stderr, "Failed to get erase block status\n");
+                       exit(1);
+               }
+       }
+       return r;
+}
+
 int mtd_erase_block(int fd, int offset)
 {
        struct erase_info_user mtdEraseInfo;
@@ -236,10 +258,14 @@ mtd_erase(const char *mtd)
        for (mtdEraseInfo.start = 0;
                 mtdEraseInfo.start < mtdsize;
                 mtdEraseInfo.start += erasesize) {
-
-               ioctl(fd, MEMUNLOCK, &mtdEraseInfo);
-               if(ioctl(fd, MEMERASE, &mtdEraseInfo))
-                       fprintf(stderr, "Failed to erase block on %s at 0x%x\n", mtd, mtdEraseInfo.start);
+               if (mtd_block_is_bad(fd, mtdEraseInfo.start)) {
+                       if (!quiet)
+                               fprintf(stderr, "\nSkipping bad block at 0x%x   ", mtdEraseInfo.start);
+               } else {
+                       ioctl(fd, MEMUNLOCK, &mtdEraseInfo);
+                       if(ioctl(fd, MEMERASE, &mtdEraseInfo))
+                               fprintf(stderr, "Failed to erase block on %s at 0x%x\n", mtd, mtdEraseInfo.start);
+               }
        }
 
        close(fd);
@@ -324,6 +350,7 @@ mtd_write(int imagefd, const char *mtd, char *fis_layout, size_t part_offset)
        ssize_t skip = 0;
        uint32_t offset = 0;
        int jffs2_replaced = 0;
+       int skip_bad_blocks = 0;
 
 #ifdef FIS_SUPPORT
        static struct fis_part new_parts[MAX_ARGS];
@@ -429,6 +456,12 @@ resume:
                if (buflen == 0)
                        break;
 
+               if (buflen < erasesize) {
+                       /* Pad block to eraseblock size */
+                       memset(&buf[buflen], 0xff, erasesize - buflen);
+                       buflen = erasesize;
+               }
+
                if (skip > 0) {
                        skip -= buflen;
                        buflen = 0;
@@ -466,10 +499,21 @@ resume:
                /* need to erase the next block before writing data to it */
                if(!no_erase)
                {
-                       while (w + buflen > e) {
+                       while (w + buflen > e - skip_bad_blocks) {
                                if (!quiet)
                                        fprintf(stderr, "\b\b\b[e]");
 
+                               if (mtd_block_is_bad(fd, e)) {
+                                       if (!quiet)
+                                               fprintf(stderr, "\nSkipping bad block at 0x%08x   ", e);
+
+                                       skip_bad_blocks += erasesize;
+                                       e += erasesize;
+
+                                       // Move the file pointer along over the bad block.
+                                       lseek(fd, erasesize, SEEK_CUR);
+                                       continue;
+                               }
 
                                if (mtd_erase_block(fd, e) < 0) {
                                        if (next) {