diff options
author | David Woodhouse <dwmw2@infradead.org> | 2007-08-09 16:23:19 +0800 |
---|---|---|
committer | David Woodhouse <dwmw2@infradead.org> | 2007-08-09 16:23:19 +0800 |
commit | 2c629e07b87746d49e267db286f096541ef01d90 (patch) | |
tree | 1c16c288e563b108bc2f6145306817f7d4c142f9 | |
parent | b431ea844a1770f46eeac738df7595c549a3deac (diff) |
Add nand integrity testing utility.
Signed-off-by: David Woodhouse <dwmw2@infradead.org>
-rw-r--r-- | Makefile | 8 | ||||
-rw-r--r-- | nandtest.c | 172 |
2 files changed, 179 insertions, 1 deletions
@@ -21,7 +21,7 @@ endif RAWTARGETS = ftl_format flash_erase flash_eraseall nanddump doc_loadbios \ mkfs.jffs ftl_check mkfs.jffs2 flash_lock flash_unlock flash_info \ - flash_otp_info flash_otp_dump mtd_debug flashcp nandwrite \ + flash_otp_info flash_otp_dump mtd_debug flashcp nandwrite nandtest \ jffs2dump \ nftldump nftl_format docfdisk \ rfddump rfdformat \ @@ -72,6 +72,12 @@ $(BUILDDIR)/jffs2dump: $(BUILDDIR)/jffs2dump.o $(BUILDDIR)/crc32.o $(BUILDDIR)/sumtool: $(BUILDDIR)/sumtool.o $(BUILDDIR)/crc32.o $(CC) $(LDFLAGS) -o $@ $^ +$(BUILDDIR)/serve_image: $(BUILDDIR)/serve_image.o $(BUILDDIR)/crc32.o + $(CC) $(LDFLAGS) -o $@ $^ + +$(BUILDDIR)/recv_image: $(BUILDDIR)/recv_image.o $(BUILDDIR)/crc32.o + $(CC) $(LDFLAGS) -o $@ $^ + install: ${TARGETS} mkdir -p ${DESTDIR}/${SBINDIR} install -m0755 ${TARGETS} ${DESTDIR}/${SBINDIR}/ diff --git a/nandtest.c b/nandtest.c new file mode 100644 index 0000000..254d41f --- /dev/null +++ b/nandtest.c @@ -0,0 +1,172 @@ +#define _GNU_SOURCE +#include <ctype.h> +#include <errno.h> +#include <fcntl.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" + + + +/* + * Main program + */ +int main(int argc, char **argv) +{ + int fd; + int block, i; + unsigned char *wbuf, *rbuf; + struct mtd_info_user meminfo; + struct mtd_ecc_stats oldstats, newstats; + int seed; + int pass; + int nr_passes = 1; + + if (argc == 4) { + seed = atol(argv[3]); + argc = 3; + } + if (argc == 3) { + nr_passes = atol(argv[2]); + argc = 2; + } + if (argc != 2) { + fprintf(stderr, "usage: %s <device> [<passes>] [<random seed>]\n", + (strrchr(argv[0],',')?:argv[0]-1)+1); + exit(1); + } + + fd = open(argv[1], O_RDWR); + if (fd < 0) { + perror("open"); + exit(1); + } + + if (ioctl(fd, MEMGETINFO, &meminfo)) { + perror("MEMGETINFO"); + close(fd); + exit(1); + } + + wbuf = malloc(meminfo.erasesize * 2); + if (!wbuf) { + fprintf(stderr, "Could not allocate %d bytes for buffer\n", + meminfo.erasesize * 2); + exit(1); + } + rbuf = wbuf + meminfo.erasesize; + + if (ioctl(fd, ECCGETSTATS, &oldstats)) { + perror("ECCGETSTATS"); + close(fd); + exit(1); + } + + printf("ECC corrections: %d\n", oldstats.corrected); + printf("ECC failures : %d\n", oldstats.failed); + printf("Bad blocks : %d\n", oldstats.badblocks); + printf("BBT blocks : %d\n", oldstats.bbtblocks); + + for (pass = 0; pass < nr_passes; pass++) { + + for (block = 0; block < meminfo.size / meminfo.erasesize ; block++) { + loff_t ofs = block * meminfo.erasesize; + struct erase_info_user er; + ssize_t len; + + seed = rand(); + srand(seed); + + if (ioctl(fd, MEMGETBADBLOCK, &ofs)) { + printf("\rBad block at 0x%08x\n", (unsigned)ofs); + continue; + } + + printf("\r%08x: erasing... ", (unsigned)ofs); + fflush(stdout); + + er.start = ofs; + er.length = meminfo.erasesize; + + if (ioctl(fd, MEMERASE, &er)) { + perror("MEMERASE"); + exit(1); + } + + printf("\r%08x: writing...", (unsigned)ofs); + fflush(stdout); + + for (i=0; i<meminfo.erasesize; i++) + wbuf[i] = rand(); + + len = pwrite(fd, wbuf, meminfo.erasesize, ofs); + if (len < 0) { + printf("\n"); + perror("write"); + exit(1); + } + if (len < meminfo.erasesize) { + printf("\n"); + fprintf(stderr, "Short write (%d bytes)\n", len); + exit(1); + } + + printf("\r%08x: reading...", (unsigned)ofs); + fflush(stdout); + + len = pread(fd, rbuf, meminfo.erasesize, ofs); + if (len < meminfo.erasesize) { + printf("\n"); + if (len) + fprintf(stderr, "Short read (%d bytes)\n", len); + else + perror("read"); + exit(1); + } + + if (ioctl(fd, ECCGETSTATS, &newstats)) { + printf("\n"); + perror("ECCGETSTATS"); + close(fd); + exit(1); + } + + if (newstats.corrected > oldstats.corrected) { + printf("\nECC corrected at %08x\n", (unsigned) ofs); + newstats.corrected = oldstats.corrected; + } + if (newstats.failed > oldstats.failed) { + printf("\nECC failed at %08x\n", (unsigned) ofs); + newstats.corrected = oldstats.corrected; + } + if (len < meminfo.erasesize) + exit(1); + + printf("\r%08x: checking...", (unsigned)ofs); + fflush(stdout); + + if (memcmp(wbuf, rbuf, meminfo.erasesize)) { + printf("\n"); + fprintf(stderr, "compare failed. seed %d\n", seed); + for (i=0; i<meminfo.erasesize; i++) { + if (wbuf[i] != rbuf[i]) + printf("Byte 0x%x is %02x should be %02x\n", + i, rbuf[i], wbuf[i]); + } + exit(1); + } + } + printf("\nFinished pass %d successfully\n", pass+1); + } + /* Return happy */ + return 0; +} |