ltp: update to latest
[openadk.git] / package / nand / src / nand.c
blob85df62677c784128b8abdcdff843f035db74dfc3
1 /*
2 * nand - simple nand memory technology device manipulation tool
4 * Copyright (C) 2010 Waldemar Brodkorb <wbx@openadk.org>
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 * The code is based on the mtd-utils nandwrite and flash_erase_all.
23 #define _GNU_SOURCE
24 #include <ctype.h>
25 #include <errno.h>
26 #include <err.h>
27 #include <fcntl.h>
28 #include <limits.h>
29 #include <stdbool.h>
30 #include <stddef.h>
31 #include <stdint.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <time.h>
36 #include <unistd.h>
37 #include <sys/stat.h>
38 #include <sys/mount.h>
39 #include <sys/ioctl.h>
40 #include <sys/types.h>
41 #include <sys/syscall.h>
42 #include <getopt.h>
44 #include "mtd/mtd-user.h"
45 #include <linux/reboot.h>
47 int nand_open(const char *, int);
48 int nand_erase(const char *);
49 int nand_info(const char *);
50 int nand_write(const char*, const char *, int);
51 void usage(void) __attribute__((noreturn));
53 #define MAX_PAGE_SIZE 4096
54 #define MAX_OOB_SIZE 128
56 static unsigned char writebuf[MAX_PAGE_SIZE];
57 static unsigned char oobbuf[MAX_OOB_SIZE];
58 static unsigned char oobreadbuf[MAX_OOB_SIZE];
60 static struct nand_oobinfo autoplace_oobinfo = {
61 .useecc = MTD_NANDECC_AUTOPLACE
64 static void erase_buffer(void *buffer, size_t size)
66 const uint8_t kEraseByte = 0xff;
68 if (buffer != NULL && size > 0) {
69 memset(buffer, kEraseByte, size);
73 int nand_open(const char *nand, int flags) {
75 FILE *fp;
76 char dev[PATH_MAX];
77 int i;
79 if ((fp = fopen("/proc/mtd", "r"))) {
80 while (fgets(dev, sizeof(dev), fp)) {
81 if (sscanf(dev, "mtd%d:", &i) && strstr(dev, nand)) {
82 snprintf(dev, sizeof(dev), "/dev/mtd%d", i);
83 fclose(fp);
84 return open(dev, flags);
87 fclose(fp);
90 return open(nand, flags);
93 int nand_info(const char *nand) {
95 int fd, ret;
96 mtd_info_t nandinfo;
97 loff_t offset;
99 if ((fd = nand_open(nand, O_RDONLY)) < 0) {
100 fprintf(stderr, "nand: unable to open MTD device %s\n", nand);
101 return 1;
104 if (ioctl(fd, MEMGETINFO, &nandinfo) != 0) {
105 fprintf(stderr, "nand: unable to get MTD device info from %s\n", nand);
106 return 1;
109 if (nandinfo.type == MTD_NANDFLASH) {
110 fprintf(stdout, "MTD devise is NAND\n");
111 } else {
112 fprintf(stdout, "MTD devise is NOT NAND\n");
113 return 1;
116 fprintf(stdout, "NAND block/erase size is: %u\n", nandinfo.erasesize);
117 fprintf(stdout, "NAND page size is: %u\n", nandinfo.writesize);
118 fprintf(stdout, "NAND OOB size is: %u\n", nandinfo.oobsize);
119 fprintf(stdout, "NAND partition size is: %u\n", nandinfo.size);
121 for (offset = 0; offset < nandinfo.size; offset += nandinfo.erasesize) {
122 ret = ioctl(fd, MEMGETBADBLOCK, &offset);
123 if (ret > 0) {
124 printf("\nSkipping bad block at %llu\n", offset);
125 continue;
126 } else if (ret < 0) {
127 if (errno == EOPNOTSUPP) {
128 fprintf(stderr, "Bad block check not available\n");
129 return 1;
134 return 0;
137 int nand_erase(const char *nand) {
139 mtd_info_t meminfo;
140 struct nand_oobinfo oobinfo;
141 int fd, clmpos, clmlen;
142 erase_info_t erase;
144 clmpos = 0;
145 clmlen = 8;
147 erase_buffer(oobbuf, sizeof(oobbuf));
149 if ((fd = nand_open(nand, O_RDWR)) < 0) {
150 fprintf(stderr, "nand: %s: unable to open MTD device\n", nand);
151 return 1;
154 if (ioctl(fd, MEMGETINFO, &meminfo) != 0) {
155 fprintf(stderr, "nand: %s: unable to get MTD device info\n", nand);
156 return 1;
159 erase.length = meminfo.erasesize;
161 for (erase.start = 0; erase.start < meminfo.size; erase.start += meminfo.erasesize) {
162 if (ioctl(fd, MEMERASE, &erase) != 0) {
163 fprintf(stderr, "\nnand: %s: MTD Erase failure: %s\n", nand, strerror(errno));
164 continue;
167 struct mtd_oob_buf oob;
169 if (ioctl(fd, MEMGETOOBSEL, &oobinfo) != 0) {
170 fprintf(stderr, "Unable to get NAND oobinfo\n");
171 return 1;
174 if (oobinfo.useecc != MTD_NANDECC_AUTOPLACE) {
175 fprintf(stderr, "NAND device/driver does not support autoplacement of OOB\n");
176 return 1;
179 if (!oobinfo.oobfree[0][1]) {
180 fprintf(stderr, "Autoplacement selected and no empty space in oob\n");
181 return 1;
183 clmpos = oobinfo.oobfree[0][0];
184 clmlen = oobinfo.oobfree[0][1];
185 if (clmlen > 8)
186 clmlen = 8;
188 //fprintf(stdout, "Using clmlen: %d clmpos: %d\n", clmlen, clmpos);
190 oob.ptr = oobbuf;
191 oob.start = erase.start + clmpos;
192 oob.length = clmlen;
193 if (ioctl (fd, MEMWRITEOOB, &oob) != 0) {
194 fprintf(stderr, "\nnand: %s: MTD writeoob failure: %s\n", nand, strerror(errno));
195 continue;
198 return 0;
201 int nand_write(const char *img, const char *nand, int quiet) {
203 static bool pad = true;
204 static const char *standard_input = "-";
205 static bool markbad = true;
206 static int mtdoffset = 0;
207 int cnt = 0;
208 int fd = -1;
209 int ifd = -1;
210 int imglen = 0, pagelen;
211 bool baderaseblock = false;
212 int blockstart = -1;
213 struct mtd_info_user meminfo;
214 struct mtd_oob_buf oob;
215 loff_t offs;
216 int ret, readlen;
218 erase_buffer(oobbuf, sizeof(oobbuf));
220 /* Open the device */
221 if ((fd = nand_open(nand, O_RDWR | O_SYNC)) == -1) {
222 perror(nand);
223 exit (EXIT_FAILURE);
226 /* Fill in MTD device capability structure */
227 if (ioctl(fd, MEMGETINFO, &meminfo) != 0) {
228 perror("MEMGETINFO");
229 close(fd);
230 exit (EXIT_FAILURE);
233 /* Make sure device page sizes are valid */
234 if (!(meminfo.oobsize == 16 && meminfo.writesize == 512) &&
235 !(meminfo.oobsize == 8 && meminfo.writesize == 256) &&
236 !(meminfo.oobsize == 64 && meminfo.writesize == 2048) &&
237 !(meminfo.oobsize == 128 && meminfo.writesize == 4096)) {
238 fprintf(stderr, "Unknown flash (not normal NAND)\n");
239 close(fd);
240 exit (EXIT_FAILURE);
243 oob.length = meminfo.oobsize;
244 oob.ptr = oobbuf;
246 /* Determine if we are reading from standard input or from a file. */
247 if (strcmp(img, standard_input) == 0) {
248 ifd = STDIN_FILENO;
249 } else {
250 ifd = open(img, O_RDONLY);
253 if (ifd == -1) {
254 perror(img);
255 goto restoreoob;
258 pagelen = meminfo.writesize;
261 * For the standard input case, the input size is merely an
262 * invariant placeholder and is set to the write page
263 * size. Otherwise, just use the input file size.
266 if (ifd == STDIN_FILENO) {
267 imglen = pagelen;
268 } else {
269 imglen = lseek(ifd, 0, SEEK_END);
270 lseek (ifd, 0, SEEK_SET);
273 // Check, if file is page-aligned
274 if ((!pad) && ((imglen % pagelen) != 0)) {
275 fprintf (stderr, "Input file is not page-aligned. Use the padding "
276 "option.\n");
277 goto closeall;
280 // Check, if length fits into device
281 if ( ((imglen / pagelen) * meminfo.writesize) > (meminfo.size - mtdoffset)) {
282 fprintf (stderr, "Image %d bytes, NAND page %d bytes, OOB area %u bytes, device size %u bytes\n",
283 imglen, pagelen, meminfo.writesize, meminfo.size);
284 perror ("Input file does not fit into device");
285 goto closeall;
289 * Get data from input and write to the device while there is
290 * still input to read and we are still within the device
291 * bounds. Note that in the case of standard input, the input
292 * length is simply a quasi-boolean flag whose values are page
293 * length or zero.
295 while (imglen && (mtdoffset < meminfo.size)) {
296 // new eraseblock , check for bad block(s)
297 // Stay in the loop to be sure if the mtdoffset changes because
298 // of a bad block, that the next block that will be written to
299 // is also checked. Thus avoiding errors if the block(s) after the
300 // skipped block(s) is also bad
301 while (blockstart != (mtdoffset & (~meminfo.erasesize + 1))) {
302 blockstart = mtdoffset & (~meminfo.erasesize + 1);
303 offs = blockstart;
304 baderaseblock = false;
305 if (quiet < 2)
306 fprintf (stdout, "Writing data to block %d at offset 0x%x\n",
307 blockstart / meminfo.erasesize, blockstart);
309 /* Check all the blocks in an erase block for bad blocks */
310 do {
311 if ((ret = ioctl(fd, MEMGETBADBLOCK, &offs)) < 0) {
312 perror("ioctl(MEMGETBADBLOCK)");
313 goto closeall;
315 if (ret == 1) {
316 baderaseblock = true;
317 if (!quiet)
318 fprintf (stderr, "Bad block at %x "
319 "from %x will be skipped\n",
320 (int) offs, blockstart);
323 if (baderaseblock) {
324 mtdoffset = blockstart + meminfo.erasesize;
326 offs += meminfo.erasesize;
327 } while ( offs < blockstart + meminfo.erasesize );
331 readlen = meminfo.writesize;
333 if (ifd != STDIN_FILENO) {
334 int tinycnt = 0;
336 if (pad && (imglen < readlen))
338 readlen = imglen;
339 erase_buffer(writebuf + readlen, meminfo.writesize - readlen);
342 /* Read Page Data from input file */
343 while(tinycnt < readlen) {
344 cnt = read(ifd, writebuf + tinycnt, readlen - tinycnt);
345 if (cnt == 0) { // EOF
346 break;
347 } else if (cnt < 0) {
348 perror ("File I/O error on input file");
349 goto closeall;
351 tinycnt += cnt;
353 } else {
354 int tinycnt = 0;
356 while(tinycnt < readlen) {
357 cnt = read(ifd, writebuf + tinycnt, readlen - tinycnt);
358 if (cnt == 0) { // EOF
359 break;
360 } else if (cnt < 0) {
361 perror ("File I/O error on stdin");
362 goto closeall;
364 tinycnt += cnt;
367 /* No padding needed - we are done */
368 if (tinycnt == 0) {
369 imglen = 0;
370 break;
373 /* No more bytes - we are done after writing the remaining bytes */
374 if (cnt == 0) {
375 imglen = 0;
378 /* Padding */
379 if (pad && (tinycnt < readlen)) {
380 erase_buffer(writebuf + tinycnt, meminfo.writesize - tinycnt);
384 /* Write out the Page data */
385 if (pwrite(fd, writebuf, meminfo.writesize, mtdoffset) != meminfo.writesize) {
386 int rewind_blocks;
387 off_t rewind_bytes;
388 erase_info_t erase;
390 perror ("pwrite");
391 /* Must rewind to blockstart if we can */
392 rewind_blocks = (mtdoffset - blockstart) / meminfo.writesize; /* Not including the one we just attempted */
393 rewind_bytes = (rewind_blocks * meminfo.writesize) + readlen;
394 if (lseek(ifd, -rewind_bytes, SEEK_CUR) == -1) {
395 perror("lseek");
396 fprintf(stderr, "Failed to seek backwards to recover from write error\n");
397 goto closeall;
399 erase.start = blockstart;
400 erase.length = meminfo.erasesize;
401 fprintf(stderr, "Erasing failed write from %08lx-%08lx\n",
402 (long)erase.start, (long)erase.start+erase.length-1);
403 if (ioctl(fd, MEMERASE, &erase) != 0) {
404 perror("MEMERASE");
405 goto closeall;
408 if (markbad) {
409 loff_t bad_addr = mtdoffset & (~(meminfo.erasesize) + 1);
410 fprintf(stderr, "Marking block at %08lx bad\n", (long)bad_addr);
411 if (ioctl(fd, MEMSETBADBLOCK, &bad_addr)) {
412 perror("MEMSETBADBLOCK");
413 /* But continue anyway */
416 mtdoffset = blockstart + meminfo.erasesize;
417 imglen += rewind_blocks * meminfo.writesize;
419 continue;
421 if (ifd != STDIN_FILENO) {
422 imglen -= readlen;
424 mtdoffset += meminfo.writesize;
427 closeall:
428 close(ifd);
430 restoreoob:
432 close(fd);
435 if ((ifd != STDIN_FILENO) && (imglen > 0)) {
436 perror ("Data was only partially written due to error\n");
437 exit (EXIT_FAILURE);
440 /* Return happy */
441 return EXIT_SUCCESS;
444 void
445 usage(void)
447 fprintf(stderr, "Usage: nand [<options> ...] <command> [<arguments> ...] <device>\n\n"
448 "The device is in the format of mtdX (eg: mtd4) or its label.\n"
449 "nand recognises these commands:\n"
450 " erase erase all data on device\n"
451 " info print information about device\n"
452 " write <imagefile>|- write <imagefile> (use - for stdin) to device\n"
453 "Following options are available:\n"
454 " -q quiet mode\n"
455 " -r reboot after successful command\n"
456 "Example: To write linux.img to mtd partition labeled as linux\n"
457 " nand write linux.img linux\n\n");
458 exit(1);
461 int main(int argc, char **argv) {
463 int ch, quiet, boot;
464 char *device;
465 enum {
466 CMD_INFO,
467 CMD_ERASE,
468 CMD_WRITE,
469 } cmd;
471 boot = 0;
472 quiet = 0;
474 while ((ch = getopt(argc, argv, "Fqr:")) != -1)
475 switch (ch) {
476 case 'F':
477 quiet = 1;
478 /* FALLTHROUGH */
479 case 'q':
480 quiet++;
481 break;
482 case 'r':
483 boot = 1;
484 break;
485 case '?':
486 default:
487 usage();
489 argc -= optind;
490 argv += optind;
492 if (argc < 2)
493 usage();
495 if ((strcmp(argv[0], "erase") == 0) && (argc == 2)) {
496 cmd = CMD_ERASE;
497 device = argv[1];
498 } else if ((strcmp(argv[0], "info") == 0) && (argc == 2)) {
499 cmd = CMD_INFO;
500 device = argv[1];
501 } else if ((strcmp(argv[0], "write") == 0) && (argc == 3)) {
502 cmd = CMD_WRITE;
503 device = argv[2];
504 } else {
505 usage();
508 sync();
510 switch (cmd) {
511 case CMD_INFO:
512 if (quiet < 2)
513 fprintf(stderr, "Info about %s ...\n", device);
514 nand_info(device);
515 break;
516 case CMD_ERASE:
517 if (quiet < 2)
518 fprintf(stderr, "Erasing %s ...\n", device);
519 nand_erase(device);
520 break;
521 case CMD_WRITE:
522 if (quiet < 2)
523 fprintf(stderr, "Writing from %s to %s ... ", argv[1], device);
524 nand_erase(device);
525 nand_write(argv[1], device, quiet);
526 if (quiet < 2)
527 fprintf(stderr, "\n");
528 break;
531 sync();
533 if (boot) {
534 fprintf(stderr, "\nRebooting ... ");
535 fflush(stdout);
536 fflush(stderr);
537 syscall(SYS_reboot,LINUX_REBOOT_MAGIC1,LINUX_REBOOT_MAGIC2,LINUX_REBOOT_CMD_RESTART,NULL);
540 return 0;