diff options
Diffstat (limited to 'nand-utils/nandtest.c')
-rw-r--r-- | nand-utils/nandtest.c | 317 |
1 files changed, 317 insertions, 0 deletions
diff --git a/nand-utils/nandtest.c b/nand-utils/nandtest.c new file mode 100644 index 0000000..2ef7cc8 --- /dev/null +++ b/nand-utils/nandtest.c @@ -0,0 +1,317 @@ +#define PROGRAM_NAME "nandtest" + +#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" +#include "common.h" + +void usage(int status) +{ + fprintf(status ? stderr : stdout, + "usage: %s [OPTIONS] <device>\n\n" + " -h, --help Display this help output\n" + " -m, --markbad Mark blocks bad if they appear so\n" + " -s, --seed Supply random seed\n" + " -p, --passes Number of passes\n" + " -r <n>, --reads=<n> Read & check <n> times per pass\n" + " -o, --offset Start offset on flash\n" + " -l, --length Length of flash to test\n" + " -k, --keep Restore existing contents after test\n", + PROGRAM_NAME); + exit(status); +} + +struct mtd_info_user meminfo; +struct mtd_ecc_stats oldstats, newstats; +int fd; +int markbad=0; +int seed; + +int read_and_compare(loff_t ofs, unsigned char *data, unsigned char *rbuf) +{ + ssize_t len; + int i; + + len = pread(fd, rbuf, meminfo.erasesize, ofs); + if (len < meminfo.erasesize) { + printf("\n"); + if (len) + fprintf(stderr, "Short read (%zd 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("\n %d bit(s) ECC corrected at %08x\n", + newstats.corrected - oldstats.corrected, + (unsigned) ofs); + oldstats.corrected = newstats.corrected; + } + if (newstats.failed > oldstats.failed) { + printf("\nECC failed at %08x\n", (unsigned) ofs); + oldstats.failed = newstats.failed; + } + + printf("\r%08x: checking...", (unsigned)ofs); + fflush(stdout); + + if (memcmp(data, rbuf, meminfo.erasesize)) { + printf("\n"); + fprintf(stderr, "compare failed. seed %d\n", seed); + for (i=0; i<meminfo.erasesize; i++) { + if (data[i] != rbuf[i]) + printf("Byte 0x%x is %02x should be %02x\n", + i, rbuf[i], data[i]); + } + return 1; + } + return 0; +} + +int erase_and_write(loff_t ofs, unsigned char *data, unsigned char *rbuf, int nr_reads) +{ + struct erase_info_user er; + ssize_t len; + int i, read_errs = 0; + + printf("\r%08x: erasing... ", (unsigned)ofs); + fflush(stdout); + + er.start = ofs; + er.length = meminfo.erasesize; + + if (ioctl(fd, MEMERASE, &er)) { + perror("MEMERASE"); + if (markbad) { + printf("Mark block bad at %08lx\n", (long)ofs); + ioctl(fd, MEMSETBADBLOCK, &ofs); + } + return 1; + } + + printf("\r%08x: writing...", (unsigned)ofs); + fflush(stdout); + + len = pwrite(fd, data, meminfo.erasesize, ofs); + if (len < 0) { + printf("\n"); + perror("write"); + if (markbad) { + printf("Mark block bad at %08lx\n", (long)ofs); + ioctl(fd, MEMSETBADBLOCK, &ofs); + } + return 1; + } + if (len < meminfo.erasesize) { + printf("\n"); + fprintf(stderr, "Short write (%zd bytes)\n", len); + exit(1); + } + + for (i=1; i<=nr_reads; i++) { + printf("\r%08x: reading (%d of %d)...", (unsigned)ofs, i, nr_reads); + fflush(stdout); + if (read_and_compare(ofs, data, rbuf)) + read_errs++; + } + if (read_errs) { + fprintf(stderr, "read/check %d of %d failed. seed %d\n", read_errs, nr_reads, seed); + return 1; + } + return 0; +} + + +/* + * Main program + */ +int main(int argc, char **argv) +{ + int i; + unsigned char *wbuf, *rbuf, *kbuf; + int pass; + int nr_passes = 1; + int nr_reads = 4; + int keep_contents = 0; + uint32_t offset = 0; + uint32_t length = -1; + int error = 0; + + seed = time(NULL); + + for (;;) { + static const char short_options[] = "hkl:mo:p:r:s:"; + static const struct option long_options[] = { + { "help", no_argument, 0, 'h' }, + { "markbad", no_argument, 0, 'm' }, + { "seed", required_argument, 0, 's' }, + { "passes", required_argument, 0, 'p' }, + { "offset", required_argument, 0, 'o' }, + { "length", required_argument, 0, 'l' }, + { "reads", required_argument, 0, 'r' }, + { "keep", no_argument, 0, 'k' }, + {0, 0, 0, 0}, + }; + int option_index = 0; + int c = getopt_long(argc, argv, short_options, long_options, &option_index); + if (c == EOF) + break; + + switch (c) { + case 'h': + usage(0); + break; + + case '?': + usage(1); + break; + + case 'm': + markbad = 1; + break; + + case 'k': + keep_contents = 1; + break; + + case 's': + seed = atol(optarg); + break; + + case 'p': + nr_passes = atol(optarg); + break; + + case 'r': + nr_reads = atol(optarg); + break; + + case 'o': + offset = simple_strtoul(optarg, &error); + break; + + case 'l': + length = simple_strtoul(optarg, &error); + break; + + } + } + if (argc - optind != 1) + usage(1); + if (error) + errmsg_die("Try --help for more information"); + + fd = open(argv[optind], O_RDWR); + if (fd < 0) { + perror("open"); + exit(1); + } + + if (ioctl(fd, MEMGETINFO, &meminfo)) { + perror("MEMGETINFO"); + close(fd); + exit(1); + } + + if (length == -1) + length = meminfo.size; + + if (offset % meminfo.erasesize) { + fprintf(stderr, "Offset %x not multiple of erase size %x\n", + offset, meminfo.erasesize); + exit(1); + } + if (length % meminfo.erasesize) { + fprintf(stderr, "Length %x not multiple of erase size %x\n", + length, meminfo.erasesize); + exit(1); + } + if (length + offset > meminfo.size) { + fprintf(stderr, "Length %x + offset %x exceeds device size %x\n", + length, offset, meminfo.size); + exit(1); + } + + wbuf = malloc(meminfo.erasesize * 3); + if (!wbuf) { + fprintf(stderr, "Could not allocate %d bytes for buffer\n", + meminfo.erasesize * 2); + exit(1); + } + rbuf = wbuf + meminfo.erasesize; + kbuf = rbuf + 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); + + srand(seed); + + for (pass = 0; pass < nr_passes; pass++) { + loff_t test_ofs; + + for (test_ofs = offset; test_ofs < offset+length; test_ofs += meminfo.erasesize) { + ssize_t len; + + seed = rand(); + srand(seed); + + if (ioctl(fd, MEMGETBADBLOCK, &test_ofs)) { + printf("\rBad block at 0x%08x\n", (unsigned)test_ofs); + continue; + } + + for (i=0; i<meminfo.erasesize; i++) + wbuf[i] = rand(); + + if (keep_contents) { + printf("\r%08x: reading... ", (unsigned)test_ofs); + fflush(stdout); + + len = pread(fd, kbuf, meminfo.erasesize, test_ofs); + if (len < meminfo.erasesize) { + printf("\n"); + if (len) + fprintf(stderr, "Short read (%zd bytes)\n", len); + else + perror("read"); + exit(1); + } + } + if (erase_and_write(test_ofs, wbuf, rbuf, nr_reads)) + continue; + if (keep_contents) + erase_and_write(test_ofs, kbuf, rbuf, 1); + } + printf("\nFinished pass %d successfully\n", pass+1); + } + /* Return happy */ + return 0; +} |