mirror of
git://projects.qi-hardware.com/openwrt-xburst.git
synced 2025-02-27 10:17:56 +02:00
285 lines
6.0 KiB
C
285 lines
6.0 KiB
C
|
#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"
|
||
|
|
||
|
void usage(void)
|
||
|
{
|
||
|
fprintf(stderr, "usage: nandtest [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"
|
||
|
" -o, --offset Start offset on flash\n"
|
||
|
" -l, --length Length of flash to test\n"
|
||
|
" -k, --keep Restore existing contents after test\n"
|
||
|
"Warning: it is just used for SLC NAND!\n");
|
||
|
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
struct mtd_info_user meminfo;
|
||
|
struct mtd_ecc_stats oldstats, newstats;
|
||
|
int fd;
|
||
|
int markbad=0;
|
||
|
int seed;
|
||
|
|
||
|
int erase_and_write(loff_mtd_t ofs, unsigned char *data, unsigned char *rbuf)
|
||
|
{
|
||
|
struct erase_info_user er;
|
||
|
ssize_t len;
|
||
|
int i;
|
||
|
|
||
|
printf("\r%09llx: erasing... ", (loff_mtd_t)ofs);
|
||
|
fflush(stdout);
|
||
|
|
||
|
er.start = ofs;
|
||
|
er.length = meminfo.erasesize;
|
||
|
|
||
|
if (ioctl(fd, MEMERASE, &er)) {
|
||
|
perror("MEMERASE");
|
||
|
if (markbad) {
|
||
|
printf("Mark block bad at %09llx\n", (loff_mtd_t)ofs);
|
||
|
ioctl(fd, MEMSETBADBLOCK, &ofs);
|
||
|
}
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
printf("\r%09llx: writing...", (loff_mtd_t)ofs);
|
||
|
fflush(stdout);
|
||
|
|
||
|
len = pwrite(fd, data, meminfo.erasesize, ofs);
|
||
|
if (len < 0) {
|
||
|
printf("\n");
|
||
|
perror("write");
|
||
|
if (markbad) {
|
||
|
printf("Mark block bad at %09llx\n", (loff_mtd_t)ofs);
|
||
|
ioctl(fd, MEMSETBADBLOCK, &ofs);
|
||
|
}
|
||
|
return 1;
|
||
|
}
|
||
|
if (len < meminfo.erasesize) {
|
||
|
printf("\n");
|
||
|
fprintf(stderr, "Short write (%d bytes)\n", len);
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
printf("\r%09llx: reading...", (loff_mtd_t)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 %09llx\n", (loff_mtd_t) ofs);
|
||
|
oldstats.corrected = newstats.corrected;
|
||
|
}
|
||
|
if (newstats.failed > oldstats.failed) {
|
||
|
printf("\nECC failed at %09llx\n", (loff_mtd_t) ofs);
|
||
|
oldstats.corrected = newstats.corrected;
|
||
|
}
|
||
|
if (len < meminfo.erasesize)
|
||
|
exit(1);
|
||
|
|
||
|
printf("\r%09llx: checking...", (loff_mtd_t)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]);
|
||
|
}
|
||
|
exit(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 keep_contents = 0;
|
||
|
uint64_t offset = 0;
|
||
|
uint64_t length = -1;
|
||
|
|
||
|
for (;;) {
|
||
|
static const char *short_options="hkl:mo:p: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' },
|
||
|
{ "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':
|
||
|
case '?':
|
||
|
usage();
|
||
|
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 'o':
|
||
|
offset = atol(optarg);
|
||
|
break;
|
||
|
|
||
|
case 'l':
|
||
|
length = strtol(optarg, NULL, 0);
|
||
|
break;
|
||
|
|
||
|
}
|
||
|
}
|
||
|
if (argc - optind != 1)
|
||
|
usage();
|
||
|
|
||
|
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 %09llx not multiple of erase size %x\n",
|
||
|
offset, meminfo.erasesize);
|
||
|
exit(1);
|
||
|
}
|
||
|
if (length % meminfo.erasesize) {
|
||
|
fprintf(stderr, "Length %09llx not multiple of erase size %x\n",
|
||
|
length, meminfo.erasesize);
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
if (length + offset > meminfo.size) {
|
||
|
fprintf(stderr, "Length %09llx + offset %09llx exceeds device size %09llx\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);
|
||
|
|
||
|
for (pass = 0; pass < nr_passes; pass++) {
|
||
|
loff_mtd_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%09llx\n", test_ofs);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
for (i=0; i<meminfo.erasesize; i++)
|
||
|
wbuf[i] = rand();
|
||
|
|
||
|
if (keep_contents) {
|
||
|
printf("\r%09llx: reading... ", test_ofs);
|
||
|
fflush(stdout);
|
||
|
|
||
|
len = pread(fd, rbuf, meminfo.erasesize, test_ofs);
|
||
|
if (len < meminfo.erasesize) {
|
||
|
printf("\n");
|
||
|
if (len)
|
||
|
fprintf(stderr, "Short read (%d bytes)\n", len);
|
||
|
else
|
||
|
perror("read");
|
||
|
exit(1);
|
||
|
}
|
||
|
}
|
||
|
if (erase_and_write(test_ofs, wbuf, rbuf))
|
||
|
continue;
|
||
|
if (keep_contents)
|
||
|
erase_and_write(test_ofs, kbuf, rbuf);
|
||
|
}
|
||
|
printf("\nFinished pass %d successfully\n", pass+1);
|
||
|
}
|
||
|
/* Return happy */
|
||
|
return 0;
|
||
|
}
|