nandread.c revision b93e5812faffd3b6c5fb349072413aace31918d8
1#include <stdio.h>
2#include <stdlib.h>
3#include <string.h>
4#include <ctype.h>
5#include <errno.h>
6#include <fcntl.h>
7#include <unistd.h>
8
9#include <mtd/mtd-user.h>
10#include <sys/ioctl.h>
11
12static int test_empty(const char *buf, size_t size)
13{
14    while(size--) {
15        if (*buf++ != 0xff)
16            return 0;
17    }
18    return 1;
19}
20
21int nandread_main(int argc, char **argv)
22{
23    char *devname = NULL;
24    char *filename = NULL;
25    char *statusfilename = NULL;
26    char *statusext = ".stat";
27    int fd;
28    int outfd = -1;
29    FILE *statusfile = NULL;
30    int ret;
31    int verbose = 0;
32    void *buffer;
33    loff_t pos, opos, end, bpos;
34    loff_t start = 0, len = 0;
35    int c;
36    int i;
37    int empty_pages = 0;
38    int page_count = 0;
39    int bad_block;
40    int rawmode = 0;
41    uint32_t *oob_data;
42    uint8_t *oob_fixed;
43    size_t spare_size = 64;
44    struct mtd_info_user mtdinfo;
45    struct mtd_ecc_stats initial_ecc, last_ecc, ecc;
46    struct mtd_oob_buf oobbuf;
47    struct nand_ecclayout ecclayout;
48
49    do {
50        c = getopt(argc, argv, "d:f:s:S:L:Rhv");
51        if (c == EOF)
52            break;
53        switch (c) {
54        case 'd':
55            devname = optarg;
56            break;
57        case 'f':
58            filename = optarg;
59            break;
60        case 's':
61            spare_size = atoi(optarg);
62            break;
63        case 'S':
64            start = strtoll(optarg, NULL, 0);
65            break;
66        case 'L':
67            len = strtoll(optarg, NULL, 0);
68            break;
69        case 'R':
70            rawmode = 1;
71            break;
72        case 'v':
73            verbose++;
74            break;
75        case 'h':
76            fprintf(stderr, "%s [-d <dev>] [-f file] [-s sparesize] [-vh]\n"
77                    "  -d <dev>   Read from <dev>\n"
78                    "  -f <file>  Write to <file>\n"
79                    "  -s <size>  Number of spare bytes in file (default 64)\n"
80                    "  -R         Raw mode\n"
81                    "  -S <start> Start offset (default 0)\n"
82                    "  -L <len>   Length (default 0)\n"
83                    "  -v         Print info\n"
84                    "  -h         Print help\n", argv[0]);
85            return -1;
86        case '?':
87            fprintf(stderr, "%s: invalid option -%c\n",
88                argv[0], optopt);
89            exit(1);
90        }
91    } while (1);
92
93    if (optind < argc) {
94        fprintf(stderr, "%s: extra arguments\n", argv[0]);
95        return 1;
96    }
97    if (!devname) {
98        fprintf(stderr, "%s: specify device name\n", argv[0]);
99        return 1;
100    }
101
102    fd = open(devname, O_RDONLY);
103    if (fd < 0) {
104        fprintf(stderr, "cannot open %s, %s\n", devname, strerror(errno));
105        return 1;
106    }
107
108    if (filename) {
109        outfd = creat(filename, 0666);
110        if (outfd < 0) {
111            fprintf(stderr, "cannot open %s, %s\n", filename, strerror(errno));
112            return 1;
113        }
114        statusfilename = malloc(strlen(filename) + strlen(statusext) + 1);
115        strcpy(statusfilename, filename);
116        strcat(statusfilename, statusext);
117        statusfile = fopen(statusfilename, "w+");
118        if (!statusfile) {
119            fprintf(stderr, "cannot open %s, %s\n", statusfilename, strerror(errno));
120            return 1;
121        }
122    }
123
124    ret = ioctl(fd, MEMGETINFO, &mtdinfo);
125    if (ret) {
126        fprintf(stderr, "failed get mtd info for %s, %s\n",
127                devname, strerror(errno));
128        return 1;
129    }
130
131    if (verbose) {
132        printf("size: %u\n", mtdinfo.size);
133        printf("erase size: %u\n", mtdinfo.erasesize);
134        printf("write size: %u\n", mtdinfo.writesize);
135        printf("oob size: %u\n", mtdinfo.oobsize);
136    }
137
138    buffer = malloc(mtdinfo.writesize + mtdinfo.oobsize + spare_size);
139    if (!buffer) {
140        fprintf(stderr, "failed allocate readbuffer size %u\n",
141                mtdinfo.writesize + mtdinfo.oobsize);
142        return 1;
143    }
144
145    oobbuf.length = mtdinfo.oobsize;
146    oob_data = (uint32_t *)((uint8_t *)buffer + mtdinfo.writesize);
147    memset(oob_data, 0xff, mtdinfo.oobsize + spare_size);
148    oobbuf.ptr = (uint8_t *)oob_data + spare_size;
149
150    ret = ioctl(fd, ECCGETLAYOUT, &ecclayout);
151    if (ret) {
152        fprintf(stderr, "failed get ecc layout for %s, %s\n",
153                devname, strerror(errno));
154        return 1;
155    }
156    if (verbose) {
157        printf("ecc bytes: %u\n", ecclayout.eccbytes);
158        printf("oobavail: %u\n", ecclayout.oobavail);
159    }
160    if (ecclayout.oobavail > spare_size)
161        printf("oobavail, %d > image spare size, %d\n", ecclayout.oobavail, spare_size);
162
163    ret = ioctl(fd, ECCGETSTATS, &initial_ecc);
164    if (ret) {
165        fprintf(stderr, "failed get ecc stats for %s, %s\n",
166                devname, strerror(errno));
167        return 1;
168    }
169    last_ecc = initial_ecc;
170
171    if (verbose) {
172        printf("initial ecc corrected: %u\n", initial_ecc.corrected);
173        printf("initial ecc failed: %u\n", initial_ecc.failed);
174        printf("initial ecc badblocks: %u\n", initial_ecc.badblocks);
175        printf("initial ecc bbtblocks: %u\n", initial_ecc.bbtblocks);
176    }
177
178    if (rawmode) {
179        rawmode = mtdinfo.oobsize;
180        ret = ioctl(fd, MTDFILEMODE, MTD_MODE_RAW);
181        if (ret) {
182            fprintf(stderr, "failed set raw mode for %s, %s\n",
183                    devname, strerror(errno));
184            return 1;
185        }
186    }
187
188    end = len ? (start + len) : mtdinfo.size;
189    for (pos = start, opos = 0; pos < end; pos += mtdinfo.writesize) {
190        bad_block = 0;
191        if (verbose > 3)
192            printf("reading at %llx\n", pos);
193        lseek64(fd, pos, SEEK_SET);
194        ret = read(fd, buffer, mtdinfo.writesize + rawmode);
195        if (ret < (int)mtdinfo.writesize) {
196            fprintf(stderr, "short read at %llx, %d\n", pos, ret);
197            bad_block = 2;
198        }
199        if (!rawmode) {
200            oobbuf.start = pos;
201            ret = ioctl(fd, MEMREADOOB, &oobbuf);
202            if (ret) {
203                fprintf(stderr, "failed to read oob data at %llx, %d\n", pos, ret);
204                bad_block = 2;
205            }
206        }
207        ret = ioctl(fd, ECCGETSTATS, &ecc);
208        if (ret) {
209            fprintf(stderr, "failed get ecc stats for %s, %s\n",
210                    devname, strerror(errno));
211            return 1;
212        }
213        bpos = pos / mtdinfo.erasesize * mtdinfo.erasesize;
214        ret = ioctl(fd, MEMGETBADBLOCK, &bpos);
215        if (ret && errno != EOPNOTSUPP) {
216            printf("badblock at %llx\n", pos);
217            bad_block = 1;
218        }
219        if (ecc.corrected != last_ecc.corrected)
220            printf("ecc corrected, %u, at %llx\n", ecc.corrected - last_ecc.corrected, pos);
221        if (ecc.failed != last_ecc.failed)
222            printf("ecc failed, %u, at %llx\n", ecc.failed - last_ecc.failed, pos);
223        if (ecc.badblocks != last_ecc.badblocks)
224            printf("ecc badblocks, %u, at %llx\n", ecc.badblocks - last_ecc.badblocks, pos);
225        if (ecc.bbtblocks != last_ecc.bbtblocks)
226            printf("ecc bbtblocks, %u, at %llx\n", ecc.bbtblocks - last_ecc.bbtblocks, pos);
227
228        if (!rawmode) {
229            oob_fixed = (uint8_t *)oob_data;
230            for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES; i++) {
231                int len = ecclayout.oobfree[i].length;
232                if (oob_fixed + len > oobbuf.ptr)
233                    len = oobbuf.ptr - oob_fixed;
234                if (len) {
235                    memcpy(oob_fixed, oobbuf.ptr + ecclayout.oobfree[i].offset, len);
236                    oob_fixed += len;
237                }
238            }
239        }
240
241        if (outfd >= 0) {
242            ret = write(outfd, buffer, mtdinfo.writesize + spare_size);
243            if (ret < (int)(mtdinfo.writesize + spare_size)) {
244                fprintf(stderr, "short write at %llx, %d\n", pos, ret);
245                close(outfd);
246                outfd = -1;
247            }
248            if (ecc.corrected != last_ecc.corrected)
249                fprintf(statusfile, "%08llx: ecc corrected\n", opos);
250            if (ecc.failed != last_ecc.failed)
251                fprintf(statusfile, "%08llx: ecc failed\n", opos);
252            if (bad_block == 1)
253                fprintf(statusfile, "%08llx: badblock\n", opos);
254            if (bad_block == 2)
255                fprintf(statusfile, "%08llx: read error\n", opos);
256            opos += mtdinfo.writesize + spare_size;
257        }
258
259        last_ecc = ecc;
260        page_count++;
261        if (test_empty(buffer, mtdinfo.writesize + mtdinfo.oobsize + spare_size))
262            empty_pages++;
263        else if (verbose > 2 || (verbose > 1 && !(pos & (mtdinfo.erasesize - 1))))
264            printf("page at %llx (%d oobbytes): %08x %08x %08x %08x "
265                   "%08x %08x %08x %08x\n", pos, oobbuf.start,
266                   oob_data[0], oob_data[1], oob_data[2], oob_data[3],
267                   oob_data[4], oob_data[5], oob_data[6], oob_data[7]);
268    }
269
270    if (outfd >= 0) {
271        fprintf(statusfile, "read %d pages, %d empty\n", page_count, empty_pages);
272        fprintf(statusfile, "total ecc corrected, %u\n", ecc.corrected - initial_ecc.corrected);
273        fprintf(statusfile, "total ecc failed, %u\n", ecc.failed - initial_ecc.failed);
274        fprintf(statusfile, "total ecc badblocks, %u\n", ecc.badblocks - initial_ecc.badblocks);
275        fprintf(statusfile, "total ecc bbtblocks, %u\n", ecc.bbtblocks - initial_ecc.bbtblocks);
276    }
277    if (verbose) {
278        printf("total ecc corrected, %u\n", ecc.corrected - initial_ecc.corrected);
279        printf("total ecc failed, %u\n", ecc.failed - initial_ecc.failed);
280        printf("total ecc badblocks, %u\n", ecc.badblocks - initial_ecc.badblocks);
281        printf("total ecc bbtblocks, %u\n", ecc.bbtblocks - initial_ecc.bbtblocks);
282    }
283    printf("read %d pages, %d empty\n", page_count, empty_pages);
284
285    return 0;
286}
287