blob: bd19942657b080c011b2c1efd6c5278702153146 [file] [log] [blame]
Arve Hjønnevåg7c953d02009-09-17 17:30:55 -07001#include <ctype.h>
2#include <errno.h>
3#include <fcntl.h>
Elliott Hughesa5f28622014-06-25 16:37:18 -07004#include <inttypes.h>
5#include <stdio.h>
6#include <stdlib.h>
7#include <string.h>
Olivier Baillyb93e5812010-11-17 11:47:23 -08008#include <unistd.h>
Arve Hjønnevåg7c953d02009-09-17 17:30:55 -07009
10#include <mtd/mtd-user.h>
11#include <sys/ioctl.h>
12
Arve Hjønnevågb6b87932010-03-03 16:48:38 -080013static int test_empty(const char *buf, size_t size)
Arve Hjønnevåg7c953d02009-09-17 17:30:55 -070014{
15 while(size--) {
Elliott Hughesb05a2e52013-11-11 16:17:49 -080016 if (*buf++ != (char) 0xff)
Arve Hjønnevåg7c953d02009-09-17 17:30:55 -070017 return 0;
18 }
19 return 1;
20}
21
22int nandread_main(int argc, char **argv)
23{
24 char *devname = NULL;
25 char *filename = NULL;
26 char *statusfilename = NULL;
27 char *statusext = ".stat";
28 int fd;
29 int outfd = -1;
30 FILE *statusfile = NULL;
31 int ret;
32 int verbose = 0;
33 void *buffer;
Arve Hjønnevågb6b87932010-03-03 16:48:38 -080034 loff_t pos, opos, end, bpos;
35 loff_t start = 0, len = 0;
Arve Hjønnevåg7c953d02009-09-17 17:30:55 -070036 int c;
37 int i;
38 int empty_pages = 0;
39 int page_count = 0;
40 int bad_block;
Arve Hjønnevågb6b87932010-03-03 16:48:38 -080041 int rawmode = 0;
Arve Hjønnevåg7c953d02009-09-17 17:30:55 -070042 uint32_t *oob_data;
43 uint8_t *oob_fixed;
44 size_t spare_size = 64;
45 struct mtd_info_user mtdinfo;
46 struct mtd_ecc_stats initial_ecc, last_ecc, ecc;
47 struct mtd_oob_buf oobbuf;
Elliott Hughesb05a2e52013-11-11 16:17:49 -080048 nand_ecclayout_t ecclayout;
Arve Hjønnevåg7c953d02009-09-17 17:30:55 -070049
50 do {
Arve Hjønnevågb6b87932010-03-03 16:48:38 -080051 c = getopt(argc, argv, "d:f:s:S:L:Rhv");
Arve Hjønnevåg7c953d02009-09-17 17:30:55 -070052 if (c == EOF)
53 break;
54 switch (c) {
55 case 'd':
56 devname = optarg;
57 break;
58 case 'f':
59 filename = optarg;
60 break;
61 case 's':
62 spare_size = atoi(optarg);
63 break;
Arve Hjønnevågb6b87932010-03-03 16:48:38 -080064 case 'S':
65 start = strtoll(optarg, NULL, 0);
66 break;
67 case 'L':
68 len = strtoll(optarg, NULL, 0);
69 break;
70 case 'R':
71 rawmode = 1;
72 break;
Arve Hjønnevåg7c953d02009-09-17 17:30:55 -070073 case 'v':
74 verbose++;
75 break;
76 case 'h':
77 fprintf(stderr, "%s [-d <dev>] [-f file] [-s sparesize] [-vh]\n"
78 " -d <dev> Read from <dev>\n"
79 " -f <file> Write to <file>\n"
80 " -s <size> Number of spare bytes in file (default 64)\n"
Arve Hjønnevågb6b87932010-03-03 16:48:38 -080081 " -R Raw mode\n"
82 " -S <start> Start offset (default 0)\n"
83 " -L <len> Length (default 0)\n"
Arve Hjønnevåg7c953d02009-09-17 17:30:55 -070084 " -v Print info\n"
85 " -h Print help\n", argv[0]);
86 return -1;
87 case '?':
88 fprintf(stderr, "%s: invalid option -%c\n",
89 argv[0], optopt);
90 exit(1);
91 }
92 } while (1);
93
94 if (optind < argc) {
95 fprintf(stderr, "%s: extra arguments\n", argv[0]);
96 return 1;
97 }
98 if (!devname) {
99 fprintf(stderr, "%s: specify device name\n", argv[0]);
100 return 1;
101 }
102
103 fd = open(devname, O_RDONLY);
104 if (fd < 0) {
105 fprintf(stderr, "cannot open %s, %s\n", devname, strerror(errno));
106 return 1;
107 }
108
109 if (filename) {
110 outfd = creat(filename, 0666);
111 if (outfd < 0) {
112 fprintf(stderr, "cannot open %s, %s\n", filename, strerror(errno));
113 return 1;
114 }
115 statusfilename = malloc(strlen(filename) + strlen(statusext) + 1);
116 strcpy(statusfilename, filename);
117 strcat(statusfilename, statusext);
118 statusfile = fopen(statusfilename, "w+");
119 if (!statusfile) {
120 fprintf(stderr, "cannot open %s, %s\n", statusfilename, strerror(errno));
121 return 1;
122 }
123 }
124
125 ret = ioctl(fd, MEMGETINFO, &mtdinfo);
126 if (ret) {
127 fprintf(stderr, "failed get mtd info for %s, %s\n",
128 devname, strerror(errno));
129 return 1;
130 }
131
132 if (verbose) {
133 printf("size: %u\n", mtdinfo.size);
134 printf("erase size: %u\n", mtdinfo.erasesize);
135 printf("write size: %u\n", mtdinfo.writesize);
136 printf("oob size: %u\n", mtdinfo.oobsize);
137 }
138
139 buffer = malloc(mtdinfo.writesize + mtdinfo.oobsize + spare_size);
140 if (!buffer) {
141 fprintf(stderr, "failed allocate readbuffer size %u\n",
142 mtdinfo.writesize + mtdinfo.oobsize);
143 return 1;
144 }
145
146 oobbuf.length = mtdinfo.oobsize;
147 oob_data = (uint32_t *)((uint8_t *)buffer + mtdinfo.writesize);
Arve Hjønnevågb6b87932010-03-03 16:48:38 -0800148 memset(oob_data, 0xff, mtdinfo.oobsize + spare_size);
Arve Hjønnevåg7c953d02009-09-17 17:30:55 -0700149 oobbuf.ptr = (uint8_t *)oob_data + spare_size;
150
151 ret = ioctl(fd, ECCGETLAYOUT, &ecclayout);
152 if (ret) {
153 fprintf(stderr, "failed get ecc layout for %s, %s\n",
154 devname, strerror(errno));
155 return 1;
156 }
157 if (verbose) {
158 printf("ecc bytes: %u\n", ecclayout.eccbytes);
159 printf("oobavail: %u\n", ecclayout.oobavail);
160 }
161 if (ecclayout.oobavail > spare_size)
Elliott Hughesccecf142014-01-16 10:53:11 -0800162 printf("oobavail, %d > image spare size, %zu\n", ecclayout.oobavail, spare_size);
Arve Hjønnevåg7c953d02009-09-17 17:30:55 -0700163
164 ret = ioctl(fd, ECCGETSTATS, &initial_ecc);
165 if (ret) {
166 fprintf(stderr, "failed get ecc stats for %s, %s\n",
167 devname, strerror(errno));
168 return 1;
169 }
170 last_ecc = initial_ecc;
171
172 if (verbose) {
173 printf("initial ecc corrected: %u\n", initial_ecc.corrected);
174 printf("initial ecc failed: %u\n", initial_ecc.failed);
175 printf("initial ecc badblocks: %u\n", initial_ecc.badblocks);
176 printf("initial ecc bbtblocks: %u\n", initial_ecc.bbtblocks);
177 }
178
Arve Hjønnevågb6b87932010-03-03 16:48:38 -0800179 if (rawmode) {
180 rawmode = mtdinfo.oobsize;
Elliott Hughesb05a2e52013-11-11 16:17:49 -0800181 ret = ioctl(fd, MTDFILEMODE, MTD_FILE_MODE_RAW);
Arve Hjønnevågb6b87932010-03-03 16:48:38 -0800182 if (ret) {
183 fprintf(stderr, "failed set raw mode for %s, %s\n",
184 devname, strerror(errno));
185 return 1;
186 }
187 }
188
189 end = len ? (start + len) : mtdinfo.size;
190 for (pos = start, opos = 0; pos < end; pos += mtdinfo.writesize) {
Arve Hjønnevåg7c953d02009-09-17 17:30:55 -0700191 bad_block = 0;
192 if (verbose > 3)
Elliott Hughesa5f28622014-06-25 16:37:18 -0700193 printf("reading at %" PRIx64 "\n", pos);
Arve Hjønnevåg7c953d02009-09-17 17:30:55 -0700194 lseek64(fd, pos, SEEK_SET);
Arve Hjønnevågb6b87932010-03-03 16:48:38 -0800195 ret = read(fd, buffer, mtdinfo.writesize + rawmode);
Arve Hjønnevåg7c953d02009-09-17 17:30:55 -0700196 if (ret < (int)mtdinfo.writesize) {
Elliott Hughesa5f28622014-06-25 16:37:18 -0700197 fprintf(stderr, "short read at %" PRIx64 ", %d\n", pos, ret);
Arve Hjønnevåg7c953d02009-09-17 17:30:55 -0700198 bad_block = 2;
199 }
Arve Hjønnevågb6b87932010-03-03 16:48:38 -0800200 if (!rawmode) {
201 oobbuf.start = pos;
202 ret = ioctl(fd, MEMREADOOB, &oobbuf);
203 if (ret) {
Elliott Hughesa5f28622014-06-25 16:37:18 -0700204 fprintf(stderr, "failed to read oob data at %" PRIx64 ", %d\n", pos, ret);
Arve Hjønnevågb6b87932010-03-03 16:48:38 -0800205 bad_block = 2;
206 }
Arve Hjønnevåg7c953d02009-09-17 17:30:55 -0700207 }
208 ret = ioctl(fd, ECCGETSTATS, &ecc);
209 if (ret) {
210 fprintf(stderr, "failed get ecc stats for %s, %s\n",
211 devname, strerror(errno));
212 return 1;
213 }
Arve Hjønnevågb6b87932010-03-03 16:48:38 -0800214 bpos = pos / mtdinfo.erasesize * mtdinfo.erasesize;
215 ret = ioctl(fd, MEMGETBADBLOCK, &bpos);
Arve Hjønnevåg7c953d02009-09-17 17:30:55 -0700216 if (ret && errno != EOPNOTSUPP) {
Elliott Hughesa5f28622014-06-25 16:37:18 -0700217 printf("badblock at %" PRIx64 "\n", pos);
Arve Hjønnevåg7c953d02009-09-17 17:30:55 -0700218 bad_block = 1;
219 }
220 if (ecc.corrected != last_ecc.corrected)
Elliott Hughesa5f28622014-06-25 16:37:18 -0700221 printf("ecc corrected, %u, at %" PRIx64 "\n", ecc.corrected - last_ecc.corrected, pos);
Arve Hjønnevåg7c953d02009-09-17 17:30:55 -0700222 if (ecc.failed != last_ecc.failed)
Elliott Hughesa5f28622014-06-25 16:37:18 -0700223 printf("ecc failed, %u, at %" PRIx64 "\n", ecc.failed - last_ecc.failed, pos);
Arve Hjønnevåg7c953d02009-09-17 17:30:55 -0700224 if (ecc.badblocks != last_ecc.badblocks)
Elliott Hughesa5f28622014-06-25 16:37:18 -0700225 printf("ecc badblocks, %u, at %" PRIx64 "\n", ecc.badblocks - last_ecc.badblocks, pos);
Arve Hjønnevåg7c953d02009-09-17 17:30:55 -0700226 if (ecc.bbtblocks != last_ecc.bbtblocks)
Elliott Hughesa5f28622014-06-25 16:37:18 -0700227 printf("ecc bbtblocks, %u, at %" PRIx64 "\n", ecc.bbtblocks - last_ecc.bbtblocks, pos);
Arve Hjønnevåg7c953d02009-09-17 17:30:55 -0700228
Arve Hjønnevågb6b87932010-03-03 16:48:38 -0800229 if (!rawmode) {
230 oob_fixed = (uint8_t *)oob_data;
231 for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES; i++) {
232 int len = ecclayout.oobfree[i].length;
233 if (oob_fixed + len > oobbuf.ptr)
234 len = oobbuf.ptr - oob_fixed;
235 if (len) {
236 memcpy(oob_fixed, oobbuf.ptr + ecclayout.oobfree[i].offset, len);
237 oob_fixed += len;
238 }
Arve Hjønnevåg7c953d02009-09-17 17:30:55 -0700239 }
240 }
241
242 if (outfd >= 0) {
243 ret = write(outfd, buffer, mtdinfo.writesize + spare_size);
244 if (ret < (int)(mtdinfo.writesize + spare_size)) {
Elliott Hughesa5f28622014-06-25 16:37:18 -0700245 fprintf(stderr, "short write at %" PRIx64 ", %d\n", pos, ret);
Arve Hjønnevåg7c953d02009-09-17 17:30:55 -0700246 close(outfd);
247 outfd = -1;
248 }
249 if (ecc.corrected != last_ecc.corrected)
Elliott Hughesa5f28622014-06-25 16:37:18 -0700250 fprintf(statusfile, "%08" PRIx64 ": ecc corrected\n", opos);
Arve Hjønnevåg7c953d02009-09-17 17:30:55 -0700251 if (ecc.failed != last_ecc.failed)
Elliott Hughesa5f28622014-06-25 16:37:18 -0700252 fprintf(statusfile, "%08" PRIx64 ": ecc failed\n", opos);
Arve Hjønnevåg7c953d02009-09-17 17:30:55 -0700253 if (bad_block == 1)
Elliott Hughesa5f28622014-06-25 16:37:18 -0700254 fprintf(statusfile, "%08" PRIx64 ": badblock\n", opos);
Arve Hjønnevåg7c953d02009-09-17 17:30:55 -0700255 if (bad_block == 2)
Elliott Hughesa5f28622014-06-25 16:37:18 -0700256 fprintf(statusfile, "%08" PRIx64 ": read error\n", opos);
Arve Hjønnevåg7c953d02009-09-17 17:30:55 -0700257 opos += mtdinfo.writesize + spare_size;
258 }
259
260 last_ecc = ecc;
261 page_count++;
262 if (test_empty(buffer, mtdinfo.writesize + mtdinfo.oobsize + spare_size))
263 empty_pages++;
264 else if (verbose > 2 || (verbose > 1 && !(pos & (mtdinfo.erasesize - 1))))
Elliott Hughesa5f28622014-06-25 16:37:18 -0700265 printf("page at %" PRIx64 " (%d oobbytes): %08x %08x %08x %08x "
Arve Hjønnevåg7c953d02009-09-17 17:30:55 -0700266 "%08x %08x %08x %08x\n", pos, oobbuf.start,
267 oob_data[0], oob_data[1], oob_data[2], oob_data[3],
268 oob_data[4], oob_data[5], oob_data[6], oob_data[7]);
269 }
270
271 if (outfd >= 0) {
272 fprintf(statusfile, "read %d pages, %d empty\n", page_count, empty_pages);
273 fprintf(statusfile, "total ecc corrected, %u\n", ecc.corrected - initial_ecc.corrected);
274 fprintf(statusfile, "total ecc failed, %u\n", ecc.failed - initial_ecc.failed);
275 fprintf(statusfile, "total ecc badblocks, %u\n", ecc.badblocks - initial_ecc.badblocks);
276 fprintf(statusfile, "total ecc bbtblocks, %u\n", ecc.bbtblocks - initial_ecc.bbtblocks);
277 }
278 if (verbose) {
279 printf("total ecc corrected, %u\n", ecc.corrected - initial_ecc.corrected);
280 printf("total ecc failed, %u\n", ecc.failed - initial_ecc.failed);
281 printf("total ecc badblocks, %u\n", ecc.badblocks - initial_ecc.badblocks);
282 printf("total ecc bbtblocks, %u\n", ecc.bbtblocks - initial_ecc.bbtblocks);
283 }
284 printf("read %d pages, %d empty\n", page_count, empty_pages);
285
286 return 0;
287}