get rid of $Id$ - it has never helped us and it has broken too many patches ;)
[oweals/openwrt.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 }