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.
38 #include <sys/mount.h>
39 #include <sys/ioctl.h>
40 #include <sys/types.h>
41 #include <sys/syscall.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
) {
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
);
84 return open(dev
, flags
);
90 return open(nand
, flags
);
93 int nand_info(const char *nand
) {
99 if ((fd
= nand_open(nand
, O_RDONLY
)) < 0) {
100 fprintf(stderr
, "nand: unable to open MTD device %s\n", nand
);
104 if (ioctl(fd
, MEMGETINFO
, &nandinfo
) != 0) {
105 fprintf(stderr
, "nand: unable to get MTD device info from %s\n", nand
);
109 if (nandinfo
.type
== MTD_NANDFLASH
) {
110 fprintf(stdout
, "MTD devise is NAND\n");
112 fprintf(stdout
, "MTD devise is NOT NAND\n");
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
);
124 printf("\nSkipping bad block at %llu\n", offset
);
126 } else if (ret
< 0) {
127 if (errno
== EOPNOTSUPP
) {
128 fprintf(stderr
, "Bad block check not available\n");
137 int nand_erase(const char *nand
) {
140 struct nand_oobinfo oobinfo
;
141 int fd
, clmpos
, clmlen
;
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
);
154 if (ioctl(fd
, MEMGETINFO
, &meminfo
) != 0) {
155 fprintf(stderr
, "nand: %s: unable to get MTD device info\n", nand
);
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
));
167 struct mtd_oob_buf oob
;
169 if (ioctl(fd
, MEMGETOOBSEL
, &oobinfo
) != 0) {
170 fprintf(stderr
, "Unable to get NAND oobinfo\n");
174 if (oobinfo
.useecc
!= MTD_NANDECC_AUTOPLACE
) {
175 fprintf(stderr
, "NAND device/driver does not support autoplacement of OOB\n");
179 if (!oobinfo
.oobfree
[0][1]) {
180 fprintf(stderr
, "Autoplacement selected and no empty space in oob\n");
183 clmpos
= oobinfo
.oobfree
[0][0];
184 clmlen
= oobinfo
.oobfree
[0][1];
188 //fprintf(stdout, "Using clmlen: %d clmpos: %d\n", clmlen, clmpos);
191 oob
.start
= erase
.start
+ clmpos
;
193 if (ioctl (fd
, MEMWRITEOOB
, &oob
) != 0) {
194 fprintf(stderr
, "\nnand: %s: MTD writeoob failure: %s\n", nand
, strerror(errno
));
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;
210 int imglen
= 0, pagelen
;
211 bool baderaseblock
= false;
213 struct mtd_info_user meminfo
;
214 struct mtd_oob_buf oob
;
218 erase_buffer(oobbuf
, sizeof(oobbuf
));
220 /* Open the device */
221 if ((fd
= nand_open(nand
, O_RDWR
| O_SYNC
)) == -1) {
226 /* Fill in MTD device capability structure */
227 if (ioctl(fd
, MEMGETINFO
, &meminfo
) != 0) {
228 perror("MEMGETINFO");
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");
243 oob
.length
= meminfo
.oobsize
;
246 /* Determine if we are reading from standard input or from a file. */
247 if (strcmp(img
, standard_input
) == 0) {
250 ifd
= open(img
, O_RDONLY
);
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
) {
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 "
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");
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
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);
304 baderaseblock
= false;
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 */
311 if ((ret
= ioctl(fd
, MEMGETBADBLOCK
, &offs
)) < 0) {
312 perror("ioctl(MEMGETBADBLOCK)");
316 baderaseblock
= true;
318 fprintf (stderr
, "Bad block at %x "
319 "from %x will be skipped\n",
320 (int) offs
, blockstart
);
324 mtdoffset
= blockstart
+ meminfo
.erasesize
;
326 offs
+= meminfo
.erasesize
;
327 } while ( offs
< blockstart
+ meminfo
.erasesize
);
331 readlen
= meminfo
.writesize
;
333 if (ifd
!= STDIN_FILENO
) {
336 if (pad
&& (imglen
< readlen
))
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
347 } else if (cnt
< 0) {
348 perror ("File I/O error on input file");
356 while(tinycnt
< readlen
) {
357 cnt
= read(ifd
, writebuf
+ tinycnt
, readlen
- tinycnt
);
358 if (cnt
== 0) { // EOF
360 } else if (cnt
< 0) {
361 perror ("File I/O error on stdin");
367 /* No padding needed - we are done */
373 /* No more bytes - we are done after writing the remaining bytes */
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
) {
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) {
396 fprintf(stderr
, "Failed to seek backwards to recover from write error\n");
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) {
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
;
421 if (ifd
!= STDIN_FILENO
) {
424 mtdoffset
+= meminfo
.writesize
;
435 if ((ifd != STDIN_FILENO) && (imglen > 0)) {
436 perror ("Data was only partially written due to error\n");
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"
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");
461 int main(int argc
, char **argv
) {
474 while ((ch
= getopt(argc
, argv
, "Fqr:")) != -1)
495 if ((strcmp(argv
[0], "erase") == 0) && (argc
== 2)) {
498 } else if ((strcmp(argv
[0], "info") == 0) && (argc
== 2)) {
501 } else if ((strcmp(argv
[0], "write") == 0) && (argc
== 3)) {
513 fprintf(stderr
, "Info about %s ...\n", device
);
518 fprintf(stderr
, "Erasing %s ...\n", device
);
523 fprintf(stderr
, "Writing from %s to %s ... ", argv
[1], device
);
525 nand_write(argv
[1], device
, quiet
);
527 fprintf(stderr
, "\n");
534 fprintf(stderr
, "\nRebooting ... ");
537 syscall(SYS_reboot
,LINUX_REBOOT_MAGIC1
,LINUX_REBOOT_MAGIC2
,LINUX_REBOOT_CMD_RESTART
,NULL
);