get rid of $Id$ - it has never helped us and it has broken too many patches ;)
[openwrt/svn-archive/archive.git] / package / mtd / src / mtd.c
1 /*
2 * mtd - simple memory technology device manipulation tool
3 *
4 * Copyright (C) 2005 Waldemar Brodkorb <wbx@dass-it.de>,
5 * Felix Fietkau <nbd@openwrt.org>
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 *
21 *
22 * The code is based on the linux-mtd examples.
23 */
24
25 #include <limits.h>
26 #include <unistd.h>
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <stdint.h>
30 #include <signal.h>
31 #include <sys/ioctl.h>
32 #include <sys/syscall.h>
33 #include <fcntl.h>
34 #include <errno.h>
35 #include <error.h>
36 #include <time.h>
37 #include <string.h>
38 #include <sys/ioctl.h>
39 #include <sys/types.h>
40 #include <sys/param.h>
41 #include <sys/mount.h>
42 #include <sys/stat.h>
43 #include <sys/reboot.h>
44 #include <linux/reboot.h>
45 #include "mtd-api.h"
46 #include "mtd.h"
47
48 #define MAX_ARGS 8
49 #define JFFS2_DEFAULT_DIR "" /* directory name without /, empty means root dir */
50
51 struct trx_header {
52 uint32_t magic; /* "HDR0" */
53 uint32_t len; /* Length of file including header */
54 uint32_t crc32; /* 32-bit CRC from flag_version to end of file */
55 uint32_t flag_version; /* 0:15 flags, 16:31 version */
56 uint32_t offsets[3]; /* Offsets of partitions from start of header */
57 };
58
59 static char *buf = NULL;
60 static char *imagefile = NULL;
61 static char *jffs2file = NULL, *jffs2dir = JFFS2_DEFAULT_DIR;
62 static int buflen = 0;
63 int quiet;
64 int mtdsize = 0;
65 int erasesize = 0;
66
67 int mtd_open(const char *mtd, bool block)
68 {
69 FILE *fp;
70 char dev[PATH_MAX];
71 int i;
72 int ret;
73 int flags = O_RDWR | O_SYNC;
74
75 if ((fp = fopen("/proc/mtd", "r"))) {
76 while (fgets(dev, sizeof(dev), fp)) {
77 if (sscanf(dev, "mtd%d:", &i) && strstr(dev, mtd)) {
78 snprintf(dev, sizeof(dev), "/dev/mtd%s/%d", (block ? "block" : ""), i);
79 if ((ret=open(dev, flags))<0) {
80 snprintf(dev, sizeof(dev), "/dev/mtd%s%d", (block ? "block" : ""), i);
81 ret=open(dev, flags);
82 }
83 fclose(fp);
84 return ret;
85 }
86 }
87 fclose(fp);
88 }
89
90 return open(mtd, flags);
91 }
92
93 int mtd_check_open(const char *mtd)
94 {
95 struct mtd_info_user mtdInfo;
96 int fd;
97
98 fd = mtd_open(mtd, false);
99 if(fd < 0) {
100 fprintf(stderr, "Could not open mtd device: %s\n", mtd);
101 return 0;
102 }
103
104 if(ioctl(fd, MEMGETINFO, &mtdInfo)) {
105 fprintf(stderr, "Could not get MTD device info from %s\n", mtd);
106 close(fd);
107 return 0;
108 }
109 mtdsize = mtdInfo.size;
110 erasesize = mtdInfo.erasesize;
111
112 return fd;
113 }
114
115 int mtd_erase_block(int fd, int offset)
116 {
117 struct erase_info_user mtdEraseInfo;
118
119 mtdEraseInfo.start = offset;
120 mtdEraseInfo.length = erasesize;
121 ioctl(fd, MEMUNLOCK, &mtdEraseInfo);
122 if (ioctl (fd, MEMERASE, &mtdEraseInfo) < 0) {
123 fprintf(stderr, "Erasing mtd failed.\n");
124 exit(1);
125 }
126 return 0;
127 }
128
129 int mtd_write_buffer(int fd, const char *buf, int offset, int length)
130 {
131 lseek(fd, offset, SEEK_SET);
132 write(fd, buf, length);
133 return 0;
134 }
135
136
137 static int
138 image_check(int imagefd, const char *mtd)
139 {
140 int ret = 1;
141 #ifdef target_brcm
142 ret = trx_check(imagefd, mtd, buf, &buflen);
143 #endif
144 return ret;
145 }
146
147 static int mtd_check(const char *mtd)
148 {
149 int fd;
150
151 fd = mtd_check_open(mtd);
152 if (!fd)
153 return 0;
154
155 if (!buf)
156 buf = malloc(erasesize);
157
158 close(fd);
159 return 1;
160 }
161
162 static int
163 mtd_unlock(const char *mtd)
164 {
165 int fd;
166 struct erase_info_user mtdLockInfo;
167
168 fd = mtd_check_open(mtd);
169 if(fd <= 0) {
170 fprintf(stderr, "Could not open mtd device: %s\n", mtd);
171 exit(1);
172 }
173
174 if (quiet < 2)
175 fprintf(stderr, "Unlocking %s ...\n", mtd);
176
177 mtdLockInfo.start = 0;
178 mtdLockInfo.length = mtdsize;
179 if(ioctl(fd, MEMUNLOCK, &mtdLockInfo)) {
180 close(fd);
181 return 0;
182 }
183
184 close(fd);
185 return 0;
186 }
187
188 static int
189 mtd_erase(const char *mtd)
190 {
191 int fd;
192 struct erase_info_user mtdEraseInfo;
193
194 if (quiet < 2)
195 fprintf(stderr, "Erasing %s ...\n", mtd);
196
197 fd = mtd_check_open(mtd);
198 if(fd <= 0) {
199 fprintf(stderr, "Could not open mtd device: %s\n", mtd);
200 exit(1);
201 }
202
203 mtdEraseInfo.length = erasesize;
204
205 for (mtdEraseInfo.start = 0;
206 mtdEraseInfo.start < mtdsize;
207 mtdEraseInfo.start += erasesize) {
208
209 ioctl(fd, MEMUNLOCK, &mtdEraseInfo);
210 if(ioctl(fd, MEMERASE, &mtdEraseInfo))
211 fprintf(stderr, "Failed to erase block on %s at 0x%x\n", mtd, mtdEraseInfo.start);
212 }
213
214 close(fd);
215 return 0;
216
217 }
218
219 static int
220 mtd_refresh(const char *mtd)
221 {
222 int fd;
223
224 if (quiet < 2)
225 fprintf(stderr, "Refreshing mtd partition %s ... ", mtd);
226
227 fd = mtd_check_open(mtd);
228 if(fd <= 0) {
229 fprintf(stderr, "Could not open mtd device: %s\n", mtd);
230 exit(1);
231 }
232
233 if (ioctl(fd, MTDREFRESH, NULL)) {
234 fprintf(stderr, "Failed to refresh the MTD device\n");
235 close(fd);
236 exit(1);
237 }
238 close(fd);
239
240 if (quiet < 2)
241 fprintf(stderr, "\n");
242
243 return 0;
244 }
245
246 static int
247 mtd_write(int imagefd, const char *mtd)
248 {
249 int fd, result;
250 ssize_t r, w, e;
251
252 fd = mtd_check_open(mtd);
253 if(fd < 0) {
254 fprintf(stderr, "Could not open mtd device: %s\n", mtd);
255 exit(1);
256 }
257
258 if (quiet < 2)
259 fprintf(stderr, "Writing from %s to %s ... ", imagefile, mtd);
260
261 r = w = e = 0;
262 if (!quiet)
263 fprintf(stderr, " [ ]");
264
265 for (;;) {
266 /* buffer may contain data already (from trx check) */
267 do {
268 r = read(imagefd, buf + buflen, erasesize - buflen);
269 if (r < 0) {
270 if ((errno == EINTR) || (errno == EAGAIN))
271 continue;
272 else {
273 perror("read");
274 break;
275 }
276 }
277
278 if (r == 0)
279 break;
280
281 buflen += r;
282 } while (buflen < erasesize);
283
284 if (buflen == 0)
285 break;
286
287 if (jffs2file) {
288 if (memcmp(buf, JFFS2_EOF, sizeof(JFFS2_EOF)) == 0) {
289 if (!quiet)
290 fprintf(stderr, "\b\b\b ");
291 if (quiet < 2)
292 fprintf(stderr, "\nAppending jffs2 data to from %s to %s...", jffs2file, mtd);
293 /* got an EOF marker - this is the place to add some jffs2 data */
294 mtd_replace_jffs2(mtd, fd, e, jffs2file);
295 goto done;
296 }
297 /* no EOF marker, make sure we figure out the last inode number
298 * before appending some data */
299 mtd_parse_jffs2data(buf, jffs2dir);
300 }
301
302 /* need to erase the next block before writing data to it */
303 while (w + buflen > e) {
304 if (!quiet)
305 fprintf(stderr, "\b\b\b[e]");
306
307 mtd_erase_block(fd, e);
308
309 /* erase the chunk */
310 e += erasesize;
311 }
312
313 if (!quiet)
314 fprintf(stderr, "\b\b\b[w]");
315
316 if ((result = write(fd, buf, buflen)) < buflen) {
317 if (result < 0) {
318 fprintf(stderr, "Error writing image.\n");
319 exit(1);
320 } else {
321 fprintf(stderr, "Insufficient space.\n");
322 exit(1);
323 }
324 }
325 w += buflen;
326
327 buflen = 0;
328 }
329 if (!quiet)
330 fprintf(stderr, "\b\b\b\b");
331
332 done:
333 if (quiet < 2)
334 fprintf(stderr, "\n");
335
336 close(fd);
337 return 0;
338 }
339
340 static void usage(void)
341 {
342 fprintf(stderr, "Usage: mtd [<options> ...] <command> [<arguments> ...] <device>\n\n"
343 "The device is in the format of mtdX (eg: mtd4) or its label.\n"
344 "mtd recognizes these commands:\n"
345 " unlock unlock the device\n"
346 " refresh refresh mtd partition\n"
347 " erase erase all data on device\n"
348 " write <imagefile>|- write <imagefile> (use - for stdin) to device\n"
349 " jffs2write <file> append <file> to the jffs2 partition on the device\n"
350 "Following options are available:\n"
351 " -q quiet mode (once: no [w] on writing,\n"
352 " twice: no status messages)\n"
353 " -r reboot after successful command\n"
354 " -f force write without trx checks\n"
355 " -e <device> erase <device> before executing the command\n"
356 " -d <name> directory for jffs2write, defaults to \"tmp\"\n"
357 " -j <name> integrate <file> into jffs2 data when writing an image\n"
358 "\n"
359 "Example: To write linux.trx to mtd4 labeled as linux and reboot afterwards\n"
360 " mtd -r write linux.trx linux\n\n");
361 exit(1);
362 }
363
364 static void do_reboot(void)
365 {
366 fprintf(stderr, "Rebooting ...\n");
367 fflush(stderr);
368
369 /* try regular reboot method first */
370 system("/sbin/reboot");
371 sleep(2);
372
373 /* if we're still alive at this point, force the kernel to reboot */
374 syscall(SYS_reboot,LINUX_REBOOT_MAGIC1,LINUX_REBOOT_MAGIC2,LINUX_REBOOT_CMD_RESTART,NULL);
375 }
376
377 int main (int argc, char **argv)
378 {
379 int ch, i, boot, imagefd = 0, force, unlocked;
380 char *erase[MAX_ARGS], *device = NULL;
381 enum {
382 CMD_ERASE,
383 CMD_WRITE,
384 CMD_UNLOCK,
385 CMD_REFRESH,
386 CMD_JFFS2WRITE
387 } cmd = -1;
388
389 erase[0] = NULL;
390 boot = 0;
391 force = 0;
392 buflen = 0;
393 quiet = 0;
394
395 while ((ch = getopt(argc, argv, "frqe:d:j:")) != -1)
396 switch (ch) {
397 case 'f':
398 force = 1;
399 break;
400 case 'r':
401 boot = 1;
402 break;
403 case 'j':
404 jffs2file = optarg;
405 break;
406 case 'q':
407 quiet++;
408 break;
409 case 'e':
410 i = 0;
411 while ((erase[i] != NULL) && ((i + 1) < MAX_ARGS))
412 i++;
413
414 erase[i++] = optarg;
415 erase[i] = NULL;
416 break;
417 case 'd':
418 jffs2dir = optarg;
419 break;
420 case '?':
421 default:
422 usage();
423 }
424 argc -= optind;
425 argv += optind;
426
427 if (argc < 2)
428 usage();
429
430 if ((strcmp(argv[0], "unlock") == 0) && (argc == 2)) {
431 cmd = CMD_UNLOCK;
432 device = argv[1];
433 } else if ((strcmp(argv[0], "refresh") == 0) && (argc == 2)) {
434 cmd = CMD_REFRESH;
435 device = argv[1];
436 } else if ((strcmp(argv[0], "erase") == 0) && (argc == 2)) {
437 cmd = CMD_ERASE;
438 device = argv[1];
439 } else if ((strcmp(argv[0], "write") == 0) && (argc == 3)) {
440 cmd = CMD_WRITE;
441 device = argv[2];
442
443 if (strcmp(argv[1], "-") == 0) {
444 imagefile = "<stdin>";
445 imagefd = 0;
446 } else {
447 imagefile = argv[1];
448 if ((imagefd = open(argv[1], O_RDONLY)) < 0) {
449 fprintf(stderr, "Couldn't open image file: %s!\n", imagefile);
450 exit(1);
451 }
452 }
453
454 if (!mtd_check(device)) {
455 fprintf(stderr, "Can't open device for writing!\n");
456 exit(1);
457 }
458 /* check trx file before erasing or writing anything */
459 if (!image_check(imagefd, device) && !force) {
460 fprintf(stderr, "Image check failed.\n");
461 exit(1);
462 }
463 } else if ((strcmp(argv[0], "jffs2write") == 0) && (argc == 3)) {
464 cmd = CMD_JFFS2WRITE;
465 device = argv[2];
466
467 imagefile = argv[1];
468 if (!mtd_check(device)) {
469 fprintf(stderr, "Can't open device for writing!\n");
470 exit(1);
471 }
472 } else {
473 usage();
474 }
475
476 sync();
477
478 i = 0;
479 unlocked = 0;
480 while (erase[i] != NULL) {
481 mtd_unlock(erase[i]);
482 mtd_erase(erase[i]);
483 if (strcmp(erase[i], device) == 0)
484 unlocked = 1;
485 i++;
486 }
487
488
489 switch (cmd) {
490 case CMD_UNLOCK:
491 if (!unlocked)
492 mtd_unlock(device);
493 break;
494 case CMD_ERASE:
495 if (!unlocked)
496 mtd_unlock(device);
497 mtd_erase(device);
498 break;
499 case CMD_WRITE:
500 if (!unlocked)
501 mtd_unlock(device);
502 mtd_write(imagefd, device);
503 break;
504 case CMD_JFFS2WRITE:
505 if (!unlocked)
506 mtd_unlock(device);
507 mtd_write_jffs2(device, imagefile, jffs2dir);
508 break;
509 case CMD_REFRESH:
510 mtd_refresh(device);
511 break;
512 }
513
514 sync();
515
516 if (boot)
517 do_reboot();
518
519 return 0;
520 }