blob: f42242687c1b1e4d1ba7151494991d4075d93924 [file] [log] [blame]
Baruch Siach6f32ea42010-08-25 16:36:17 +02001/*
2 * nandwrite.c - ported to busybox from mtd-utils
3 *
4 * Author: Baruch Siach <baruch@tkos.co.il>, Orex Computed Radiography
5 *
6 * Licensed under GPLv2, see file LICENSE in this source tree.
7 *
8 * TODO: add support for large (>4GB) MTD devices
9 */
10
11//applet:IF_NANDWRITE(APPLET(nandwrite, _BB_DIR_USR_SBIN, _BB_SUID_DROP))
12
13//kbuild:lib-$(CONFIG_NANDWRITE) += nandwrite.o
14
15//config:config NANDWRITE
16//config: bool "nandwrite"
17//config: default n
18//config: depends on PLATFORM_LINUX
19//config: help
20//config: Write to the specified MTD device, with bad blocks awareness
21
22#include "libbb.h"
23#include <mtd/mtd-user.h>
24
25//usage:#define nandwrite_trivial_usage
26//usage: "[-p] [-s ADDR] MTD_DEVICE [FILE]"
27//usage:#define nandwrite_full_usage "\n\n"
28//usage: "Write to the specified MTD device\n"
29//usage: "\nOptions:"
30//usage: "\n -p Pad to page size"
31//usage: "\n -s ADDR Start address"
32
33static unsigned next_good_eraseblock(int fd, struct mtd_info_user *meminfo,
34 unsigned block_offset)
35{
36 while (1) {
37 loff_t offs;
38 if (block_offset >= meminfo->size)
39 bb_error_msg_and_die("not enough space in MTD device");
40 offs = block_offset;
41 if (xioctl(fd, MEMGETBADBLOCK, &offs) == 0)
42 return block_offset;
43 /* ioctl returned 1 => "bad block" */
44 printf("Skipping bad block at 0x%08x\n", block_offset);
45 block_offset += meminfo->erasesize;
46 }
47}
48
49int nandwrite_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
50int nandwrite_main(int argc UNUSED_PARAM, char **argv)
51{
52 unsigned opts;
53 int fd;
54 ssize_t cnt;
Baruch Siach7715b482010-08-29 10:36:50 +030055 unsigned mtdoffset, meminfo_writesize, blockstart;
Baruch Siach6f32ea42010-08-25 16:36:17 +020056 struct mtd_info_user meminfo;
57 unsigned char *filebuf;
58 const char *opt_s = "0";
59 enum {
60 OPT_p = (1 << 0),
61 OPT_s = (1 << 1),
62 };
63
64 opt_complementary = "-1:?2";
65 opts = getopt32(argv, "ps:", &opt_s);
66 argv += optind;
67
68 if (argv[1])
69 xmove_fd(xopen_stdin(argv[1]), STDIN_FILENO);
70
71 fd = xopen(argv[0], O_RDWR);
72 xioctl(fd, MEMGETINFO, &meminfo);
73
Baruch Siacha78227d2010-08-29 10:36:49 +030074 mtdoffset = bb_strtou(opt_s, NULL, 0);
Denys Vlasenkob32a5432010-08-29 13:29:02 +020075 if (errno)
76 bb_error_msg_and_die("invalid number '%s'", opt_s);
Baruch Siach6f32ea42010-08-25 16:36:17 +020077
78 /* Pull it into a CPU register (hopefully) - smaller code that way */
79 meminfo_writesize = meminfo.writesize;
80
81 if (mtdoffset & (meminfo_writesize - 1))
82 bb_error_msg_and_die("start address is not page aligned");
83
84 filebuf = xmalloc(meminfo_writesize);
85
Baruch Siach7715b482010-08-29 10:36:50 +030086 blockstart = mtdoffset & ~(meminfo.erasesize - 1);
87 if (blockstart != mtdoffset) {
88 unsigned tmp;
89 /* mtdoffset is in the middle of an erase block, verify that
90 * this block is OK. Advance mtdoffset only if this block is
91 * bad.
92 */
93 tmp = next_good_eraseblock(fd, &meminfo, blockstart);
94 if (tmp != blockstart) /* bad block(s), advance mtdoffset */
95 mtdoffset = tmp;
96 }
97
Baruch Siach6f32ea42010-08-25 16:36:17 +020098 cnt = -1;
99 while (mtdoffset < meminfo.size) {
Baruch Siach7715b482010-08-29 10:36:50 +0300100 blockstart = mtdoffset & ~(meminfo.erasesize - 1);
Baruch Siach6f32ea42010-08-25 16:36:17 +0200101 if (blockstart == mtdoffset) {
102 /* starting a new eraseblock */
103 mtdoffset = next_good_eraseblock(fd, &meminfo, blockstart);
104 printf("Writing at 0x%08x\n", mtdoffset);
105 }
106 /* get some more data from input */
107 cnt = full_read(STDIN_FILENO, filebuf, meminfo_writesize);
108 if (cnt == 0) {
109 /* even with -p, we do not pad past the end of input
110 * (-p only zero-pads last incomplete page)
111 */
112 break;
113 }
114 if (cnt < meminfo_writesize) {
115 if (!(opts & OPT_p))
116 bb_error_msg_and_die("input size is not rounded up to page size, "
117 "use -p to zero pad");
118 /* zero pad to end of write block */
119 memset(filebuf + cnt, 0, meminfo_writesize - cnt);
120 }
121 xlseek(fd, mtdoffset, SEEK_SET);
122 xwrite(fd, filebuf, meminfo_writesize);
123 mtdoffset += meminfo_writesize;
124 if (cnt < meminfo_writesize)
125 break;
126 }
127
128 if (cnt != 0) {
129 /* We filled entire MTD, but did we reach EOF on input? */
130 if (full_read(STDIN_FILENO, filebuf, meminfo_writesize) != 0) {
131 /* no */
132 bb_error_msg_and_die("not enough space in MTD device");
133 }
134 }
135
136 if (ENABLE_FEATURE_CLEAN_UP) {
137 free(filebuf);
138 close(fd);
139 }
140
141 return EXIT_SUCCESS;
142}