91e169eb413114539c4c646b72a5b5516cee57bb
[lede.git] / package / system / 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  * Copyright (C) 2005-2009 Felix Fietkau <nbd@nbd.name>
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 v2
9  * as published by the Free Software Foundation.
10  *
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.
15  *
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.
19  *
20  *
21  * The code is based on the linux-mtd examples.
22  */
23
24 #define _GNU_SOURCE
25 #include <byteswap.h>
26 #include <endian.h>
27 #include <limits.h>
28 #include <unistd.h>
29 #include <stdlib.h>
30 #include <stdio.h>
31 #include <stdint.h>
32 #include <signal.h>
33 #include <sys/ioctl.h>
34 #include <sys/syscall.h>
35 #include <fcntl.h>
36 #include <errno.h>
37 #include <time.h>
38 #include <string.h>
39 #include <sys/ioctl.h>
40 #include <sys/types.h>
41 #include <sys/param.h>
42 #include <sys/mount.h>
43 #include <sys/stat.h>
44 #include <sys/reboot.h>
45 #include <linux/reboot.h>
46 #include <mtd/mtd-user.h>
47 #include "fis.h"
48 #include "mtd.h"
49
50 #include <libubox/md5.h>
51
52 #define MAX_ARGS 8
53 #define JFFS2_DEFAULT_DIR       "" /* directory name without /, empty means root dir */
54
55 #define TRX_MAGIC               0x48445230      /* "HDR0" */
56 #define SEAMA_MAGIC             0x5ea3a417
57
58 #if !defined(__BYTE_ORDER)
59 #error "Unknown byte order"
60 #endif
61
62 #if __BYTE_ORDER == __BIG_ENDIAN
63 #define cpu_to_be32(x)  (x)
64 #define be32_to_cpu(x)  (x)
65 #elif __BYTE_ORDER == __LITTLE_ENDIAN
66 #define cpu_to_be32(x)  bswap_32(x)
67 #define be32_to_cpu(x)  bswap_32(x)
68 #else
69 #error "Unsupported endianness"
70 #endif
71
72 enum mtd_image_format {
73         MTD_IMAGE_FORMAT_UNKNOWN,
74         MTD_IMAGE_FORMAT_TRX,
75         MTD_IMAGE_FORMAT_SEAMA,
76 };
77
78 static char *buf = NULL;
79 static char *imagefile = NULL;
80 static enum mtd_image_format imageformat = MTD_IMAGE_FORMAT_UNKNOWN;
81 static char *jffs2file = NULL, *jffs2dir = JFFS2_DEFAULT_DIR;
82 static int buflen = 0;
83 int quiet;
84 int no_erase;
85 int mtdsize = 0;
86 int erasesize = 0;
87 int jffs2_skip_bytes=0;
88 int mtdtype = 0;
89
90 int mtd_open(const char *mtd, bool block)
91 {
92         FILE *fp;
93         char dev[PATH_MAX];
94         int i;
95         int ret;
96         int flags = O_RDWR | O_SYNC;
97         char name[PATH_MAX];
98
99         snprintf(name, sizeof(name), "\"%s\"", mtd);
100         if ((fp = fopen("/proc/mtd", "r"))) {
101                 while (fgets(dev, sizeof(dev), fp)) {
102                         if (sscanf(dev, "mtd%d:", &i) && strstr(dev, name)) {
103                                 snprintf(dev, sizeof(dev), "/dev/mtd%s/%d", (block ? "block" : ""), i);
104                                 if ((ret=open(dev, flags))<0) {
105                                         snprintf(dev, sizeof(dev), "/dev/mtd%s%d", (block ? "block" : ""), i);
106                                         ret=open(dev, flags);
107                                 }
108                                 fclose(fp);
109                                 return ret;
110                         }
111                 }
112                 fclose(fp);
113         }
114
115         return open(mtd, flags);
116 }
117
118 int mtd_check_open(const char *mtd)
119 {
120         struct mtd_info_user mtdInfo;
121         int fd;
122
123         fd = mtd_open(mtd, false);
124         if(fd < 0) {
125                 fprintf(stderr, "Could not open mtd device: %s\n", mtd);
126                 return -1;
127         }
128
129         if(ioctl(fd, MEMGETINFO, &mtdInfo)) {
130                 fprintf(stderr, "Could not get MTD device info from %s\n", mtd);
131                 close(fd);
132                 return -1;
133         }
134         mtdsize = mtdInfo.size;
135         erasesize = mtdInfo.erasesize;
136         mtdtype = mtdInfo.type;
137
138         return fd;
139 }
140
141 int mtd_block_is_bad(int fd, int offset)
142 {
143         int r = 0;
144         loff_t o = offset;
145
146         if (mtdtype == MTD_NANDFLASH)
147         {
148                 r = ioctl(fd, MEMGETBADBLOCK, &o);
149                 if (r < 0)
150                 {
151                         fprintf(stderr, "Failed to get erase block status\n");
152                         exit(1);
153                 }
154         }
155         return r;
156 }
157
158 int mtd_erase_block(int fd, int offset)
159 {
160         struct erase_info_user mtdEraseInfo;
161
162         mtdEraseInfo.start = offset;
163         mtdEraseInfo.length = erasesize;
164         ioctl(fd, MEMUNLOCK, &mtdEraseInfo);
165         if (ioctl (fd, MEMERASE, &mtdEraseInfo) < 0)
166                 return -1;
167
168         return 0;
169 }
170
171 int mtd_write_buffer(int fd, const char *buf, int offset, int length)
172 {
173         lseek(fd, offset, SEEK_SET);
174         write(fd, buf, length);
175         return 0;
176 }
177
178 static int
179 image_check(int imagefd, const char *mtd)
180 {
181         uint32_t magic;
182         int ret = 1;
183         int bufread;
184
185         while (buflen < sizeof(magic)) {
186                 bufread = read(imagefd, buf + buflen, sizeof(magic) - buflen);
187                 if (bufread < 1)
188                         break;
189
190                 buflen += bufread;
191         }
192
193         if (buflen < sizeof(magic)) {
194                 fprintf(stdout, "Could not get image magic\n");
195                 return 0;
196         }
197
198         magic = ((uint32_t *)buf)[0];
199
200         if (be32_to_cpu(magic) == TRX_MAGIC)
201                 imageformat = MTD_IMAGE_FORMAT_TRX;
202         else if (be32_to_cpu(magic) == SEAMA_MAGIC)
203                 imageformat = MTD_IMAGE_FORMAT_SEAMA;
204
205         switch (imageformat) {
206         case MTD_IMAGE_FORMAT_TRX:
207                 if (trx_check)
208                         ret = trx_check(imagefd, mtd, buf, &buflen);
209                 break;
210         case MTD_IMAGE_FORMAT_SEAMA:
211                 break;
212         default:
213 #ifdef target_brcm
214                 if (!strcmp(mtd, "firmware"))
215                         ret = 0;
216 #endif
217                 break;
218         }
219
220         return ret;
221 }
222
223 static int mtd_check(const char *mtd)
224 {
225         char *next = NULL;
226         char *str = NULL;
227         int fd;
228
229         if (strchr(mtd, ':')) {
230                 str = strdup(mtd);
231                 mtd = str;
232         }
233
234         do {
235                 next = strchr(mtd, ':');
236                 if (next) {
237                         *next = 0;
238                         next++;
239                 }
240
241                 fd = mtd_check_open(mtd);
242                 if (fd < 0)
243                         return 0;
244
245                 if (!buf)
246                         buf = malloc(erasesize);
247
248                 close(fd);
249                 mtd = next;
250         } while (next);
251
252         if (str)
253                 free(str);
254
255         return 1;
256 }
257
258 static int
259 mtd_unlock(const char *mtd)
260 {
261         struct erase_info_user mtdLockInfo;
262         char *next = NULL;
263         char *str = NULL;
264         int fd;
265
266         if (strchr(mtd, ':')) {
267                 str = strdup(mtd);
268                 mtd = str;
269         }
270
271         do {
272                 next = strchr(mtd, ':');
273                 if (next) {
274                         *next = 0;
275                         next++;
276                 }
277
278                 fd = mtd_check_open(mtd);
279                 if(fd < 0) {
280                         fprintf(stderr, "Could not open mtd device: %s\n", mtd);
281                         exit(1);
282                 }
283
284                 if (quiet < 2)
285                         fprintf(stderr, "Unlocking %s ...\n", mtd);
286
287                 mtdLockInfo.start = 0;
288                 mtdLockInfo.length = mtdsize;
289                 ioctl(fd, MEMUNLOCK, &mtdLockInfo);
290                 close(fd);
291                 mtd = next;
292         } while (next);
293
294         if (str)
295                 free(str);
296
297         return 0;
298 }
299
300 static int
301 mtd_erase(const char *mtd)
302 {
303         int fd;
304         struct erase_info_user mtdEraseInfo;
305
306         if (quiet < 2)
307                 fprintf(stderr, "Erasing %s ...\n", mtd);
308
309         fd = mtd_check_open(mtd);
310         if(fd < 0) {
311                 fprintf(stderr, "Could not open mtd device: %s\n", mtd);
312                 exit(1);
313         }
314
315         mtdEraseInfo.length = erasesize;
316
317         for (mtdEraseInfo.start = 0;
318                  mtdEraseInfo.start < mtdsize;
319                  mtdEraseInfo.start += erasesize) {
320                 if (mtd_block_is_bad(fd, mtdEraseInfo.start)) {
321                         if (!quiet)
322                                 fprintf(stderr, "\nSkipping bad block at 0x%x   ", mtdEraseInfo.start);
323                 } else {
324                         ioctl(fd, MEMUNLOCK, &mtdEraseInfo);
325                         if(ioctl(fd, MEMERASE, &mtdEraseInfo))
326                                 fprintf(stderr, "Failed to erase block on %s at 0x%x\n", mtd, mtdEraseInfo.start);
327                 }
328         }
329
330         close(fd);
331         return 0;
332
333 }
334
335 static int
336 mtd_dump(const char *mtd, int part_offset, int size)
337 {
338         int ret = 0, offset = 0;
339         int fd;
340         char *buf;
341
342         if (quiet < 2)
343                 fprintf(stderr, "Dumping %s ...\n", mtd);
344
345         fd = mtd_check_open(mtd);
346         if(fd < 0) {
347                 fprintf(stderr, "Could not open mtd device: %s\n", mtd);
348                 return -1;
349         }
350
351         if (!size)
352                 size = mtdsize;
353
354         if (part_offset)
355                 lseek(fd, part_offset, SEEK_SET);
356
357         buf = malloc(erasesize);
358         if (!buf)
359                 return -1;
360
361         do {
362                 int len = (size > erasesize) ? (erasesize) : (size);
363                 int rlen = read(fd, buf, len);
364
365                 if (rlen < 0) {
366                         if (errno == EINTR)
367                                 continue;
368                         ret = -1;
369                         goto out;
370                 }
371                 if (!rlen || rlen != len)
372                         break;
373                 if (mtd_block_is_bad(fd, offset)) {
374                         fprintf(stderr, "skipping bad block at 0x%08x\n", offset);
375                 } else {
376                         size -= rlen;
377                         write(1, buf, rlen);
378                 }
379                 offset += rlen;
380         } while (size > 0);
381
382 out:
383         close(fd);
384         return ret;
385 }
386
387 static int
388 mtd_verify(const char *mtd, char *file)
389 {
390         uint32_t f_md5[4], m_md5[4];
391         struct stat s;
392         md5_ctx_t ctx;
393         int ret = 0;
394         int fd;
395
396         if (quiet < 2)
397                 fprintf(stderr, "Verifying %s against %s ...\n", mtd, file);
398
399         if (stat(file, &s) || md5sum(file, f_md5) < 0) {
400                 fprintf(stderr, "Failed to hash %s\n", file);
401                 return -1;
402         }
403
404         fd = mtd_check_open(mtd);
405         if(fd < 0) {
406                 fprintf(stderr, "Could not open mtd device: %s\n", mtd);
407                 return -1;
408         }
409
410         md5_begin(&ctx);
411         do {
412                 char buf[256];
413                 int len = (s.st_size > sizeof(buf)) ? (sizeof(buf)) : (s.st_size);
414                 int rlen = read(fd, buf, len);
415
416                 if (rlen < 0) {
417                         if (errno == EINTR)
418                                 continue;
419                         ret = -1;
420                         goto out;
421                 }
422                 if (!rlen)
423                         break;
424                 md5_hash(buf, rlen, &ctx);
425                 s.st_size -= rlen;
426         } while (s.st_size > 0);
427
428         md5_end(m_md5, &ctx);
429
430         fprintf(stderr, "%08x%08x%08x%08x - %s\n", m_md5[0], m_md5[1], m_md5[2], m_md5[3], mtd);
431         fprintf(stderr, "%08x%08x%08x%08x - %s\n", f_md5[0], f_md5[1], f_md5[2], f_md5[3], file);
432
433         ret = memcmp(f_md5, m_md5, sizeof(m_md5));
434         if (!ret)
435                 fprintf(stderr, "Success\n");
436         else
437                 fprintf(stderr, "Failed\n");
438
439 out:
440         close(fd);
441         return ret;
442 }
443
444 static void
445 indicate_writing(const char *mtd)
446 {
447         if (quiet < 2)
448                 fprintf(stderr, "\nWriting from %s to %s ... ", imagefile, mtd);
449
450         if (!quiet)
451                 fprintf(stderr, " [ ]");
452 }
453
454 static int
455 mtd_write(int imagefd, const char *mtd, char *fis_layout, size_t part_offset)
456 {
457         char *next = NULL;
458         char *str = NULL;
459         int fd, result;
460         ssize_t r, w, e;
461         ssize_t skip = 0;
462         uint32_t offset = 0;
463         int jffs2_replaced = 0;
464         int skip_bad_blocks = 0;
465
466 #ifdef FIS_SUPPORT
467         static struct fis_part new_parts[MAX_ARGS];
468         static struct fis_part old_parts[MAX_ARGS];
469         int n_new = 0, n_old = 0;
470
471         if (fis_layout) {
472                 const char *tmp = mtd;
473                 char *word, *brkt;
474                 int ret;
475
476                 memset(&old_parts, 0, sizeof(old_parts));
477                 memset(&new_parts, 0, sizeof(new_parts));
478
479                 do {
480                         next = strchr(tmp, ':');
481                         if (!next)
482                                 next = (char *) tmp + strlen(tmp);
483
484                         memcpy(old_parts[n_old].name, tmp, next - tmp);
485
486                         n_old++;
487                         tmp = next + 1;
488                 } while(*next);
489
490                 for (word = strtok_r(fis_layout, ",", &brkt);
491                      word;
492                          word = strtok_r(NULL, ",", &brkt)) {
493
494                         tmp = strtok(word, ":");
495                         strncpy((char *) new_parts[n_new].name, tmp, sizeof(new_parts[n_new].name) - 1);
496
497                         tmp = strtok(NULL, ":");
498                         if (!tmp)
499                                 goto next;
500
501                         new_parts[n_new].size = strtoul(tmp, NULL, 0);
502
503                         tmp = strtok(NULL, ":");
504                         if (!tmp)
505                                 goto next;
506
507                         new_parts[n_new].loadaddr = strtoul(tmp, NULL, 16);
508 next:
509                         n_new++;
510                 }
511                 ret = fis_validate(old_parts, n_old, new_parts, n_new);
512                 if (ret < 0) {
513                         fprintf(stderr, "Failed to validate the new FIS partition table\n");
514                         exit(1);
515                 }
516                 if (ret == 0)
517                         fis_layout = NULL;
518         }
519 #endif
520
521         if (strchr(mtd, ':')) {
522                 str = strdup(mtd);
523                 mtd = str;
524         }
525
526         r = 0;
527
528 resume:
529         next = strchr(mtd, ':');
530         if (next) {
531                 *next = 0;
532                 next++;
533         }
534
535         fd = mtd_check_open(mtd);
536         if(fd < 0) {
537                 fprintf(stderr, "Could not open mtd device: %s\n", mtd);
538                 exit(1);
539         }
540         if (part_offset > 0) {
541                 fprintf(stderr, "Seeking on mtd device '%s' to: %zu\n", mtd, part_offset);
542                 lseek(fd, part_offset, SEEK_SET);
543         }
544
545         indicate_writing(mtd);
546
547         w = e = 0;
548         for (;;) {
549                 /* buffer may contain data already (from trx check or last mtd partition write attempt) */
550                 while (buflen < erasesize) {
551                         r = read(imagefd, buf + buflen, erasesize - buflen);
552                         if (r < 0) {
553                                 if ((errno == EINTR) || (errno == EAGAIN))
554                                         continue;
555                                 else {
556                                         perror("read");
557                                         break;
558                                 }
559                         }
560
561                         if (r == 0)
562                                 break;
563
564                         buflen += r;
565                 }
566
567                 if (buflen == 0)
568                         break;
569
570                 if (buflen < erasesize) {
571                         /* Pad block to eraseblock size */
572                         memset(&buf[buflen], 0xff, erasesize - buflen);
573                         buflen = erasesize;
574                 }
575
576                 if (skip > 0) {
577                         skip -= buflen;
578                         buflen = 0;
579                         if (skip <= 0)
580                                 indicate_writing(mtd);
581
582                         continue;
583                 }
584
585                 if (jffs2file && w >= jffs2_skip_bytes) {
586                         if (memcmp(buf, JFFS2_EOF, sizeof(JFFS2_EOF) - 1) == 0) {
587                                 if (!quiet)
588                                         fprintf(stderr, "\b\b\b   ");
589                                 if (quiet < 2)
590                                         fprintf(stderr, "\nAppending jffs2 data from %s to %s..\n.", jffs2file, mtd);
591                                 /* got an EOF marker - this is the place to add some jffs2 data */
592                                 skip = mtd_replace_jffs2(mtd, fd, e, jffs2file);
593                                 jffs2_replaced = 1;
594
595                                 /* don't add it again */
596                                 jffs2file = NULL;
597
598                                 w += skip;
599                                 e += skip;
600                                 skip -= buflen;
601                                 buflen = 0;
602                                 offset = 0;
603                                 continue;
604                         }
605                         /* no EOF marker, make sure we figure out the last inode number
606                          * before appending some data */
607                         mtd_parse_jffs2data(buf, jffs2dir);
608                 }
609
610                 /* need to erase the next block before writing data to it */
611                 if(!no_erase)
612                 {
613                         while (w + buflen > e - skip_bad_blocks) {
614                                 if (!quiet)
615                                         fprintf(stderr, "\b\b\b[e]");
616
617                                 if (mtd_block_is_bad(fd, e)) {
618                                         if (!quiet)
619                                                 fprintf(stderr, "\nSkipping bad block at 0x%08zx   ", e);
620
621                                         skip_bad_blocks += erasesize;
622                                         e += erasesize;
623
624                                         // Move the file pointer along over the bad block.
625                                         lseek(fd, erasesize, SEEK_CUR);
626                                         continue;
627                                 }
628
629                                 if (mtd_erase_block(fd, e) < 0) {
630                                         if (next) {
631                                                 if (w < e) {
632                                                         write(fd, buf + offset, e - w);
633                                                         offset = e - w;
634                                                 }
635                                                 w = 0;
636                                                 e = 0;
637                                                 close(fd);
638                                                 mtd = next;
639                                                 fprintf(stderr, "\b\b\b   \n");
640                                                 goto resume;
641                                         } else {
642                                                 fprintf(stderr, "Failed to erase block\n");
643                                                 exit(1);
644                                         }
645                                 }
646
647                                 /* erase the chunk */
648                                 e += erasesize;
649                         }
650                 }
651
652                 if (!quiet)
653                         fprintf(stderr, "\b\b\b[w]");
654
655                 if ((result = write(fd, buf + offset, buflen)) < buflen) {
656                         if (result < 0) {
657                                 fprintf(stderr, "Error writing image.\n");
658                                 exit(1);
659                         } else {
660                                 fprintf(stderr, "Insufficient space.\n");
661                                 exit(1);
662                         }
663                 }
664                 w += buflen;
665
666                 buflen = 0;
667                 offset = 0;
668         }
669
670         if (jffs2_replaced) {
671                 switch (imageformat) {
672                 case MTD_IMAGE_FORMAT_TRX:
673                         if (trx_fixup)
674                                 trx_fixup(fd, mtd);
675                         break;
676                 case MTD_IMAGE_FORMAT_SEAMA:
677                         if (mtd_fixseama)
678                                 mtd_fixseama(mtd, 0, 0);
679                         break;
680                 default:
681                         break;
682                 }
683         }
684
685         if (!quiet)
686                 fprintf(stderr, "\b\b\b\b    ");
687
688         if (quiet < 2)
689                 fprintf(stderr, "\n");
690
691 #ifdef FIS_SUPPORT
692         if (fis_layout) {
693                 if (fis_remap(old_parts, n_old, new_parts, n_new) < 0)
694                         fprintf(stderr, "Failed to update the FIS partition table\n");
695         }
696 #endif
697
698         close(fd);
699         return 0;
700 }
701
702 static void usage(void)
703 {
704         fprintf(stderr, "Usage: mtd [<options> ...] <command> [<arguments> ...] <device>[:<device>...]\n\n"
705         "The device is in the format of mtdX (eg: mtd4) or its label.\n"
706         "mtd recognizes these commands:\n"
707         "        unlock                  unlock the device\n"
708         "        refresh                 refresh mtd partition\n"
709         "        erase                   erase all data on device\n"
710         "        verify <imagefile>|-    verify <imagefile> (use - for stdin) to device\n"
711         "        write <imagefile>|-     write <imagefile> (use - for stdin) to device\n"
712         "        jffs2write <file>       append <file> to the jffs2 partition on the device\n");
713         if (mtd_resetbc) {
714             fprintf(stderr,
715         "        resetbc <device>        reset the uboot boot counter\n");
716         }
717         if (mtd_fixtrx) {
718             fprintf(stderr,
719         "        fixtrx                  fix the checksum in a trx header on first boot\n");
720         }
721         if (mtd_fixseama) {
722             fprintf(stderr,
723         "        fixseama                fix the checksum in a seama header on first boot\n");
724         }
725         fprintf(stderr,
726         "Following options are available:\n"
727         "        -q                      quiet mode (once: no [w] on writing,\n"
728         "                                           twice: no status messages)\n"
729         "        -n                      write without first erasing the blocks\n"
730         "        -r                      reboot after successful command\n"
731         "        -f                      force write without trx checks\n"
732         "        -e <device>             erase <device> before executing the command\n"
733         "        -d <name>               directory for jffs2write, defaults to \"tmp\"\n"
734         "        -j <name>               integrate <file> into jffs2 data when writing an image\n"
735         "        -s <number>             skip the first n bytes when appending data to the jffs2 partiton, defaults to \"0\"\n"
736         "        -p <number>             write beginning at partition offset\n"
737         "        -l <length>             the length of data that we want to dump\n");
738         if (mtd_fixtrx) {
739             fprintf(stderr,
740         "        -o offset               offset of the image header in the partition(for fixtrx)\n");
741         }
742         if (mtd_fixtrx || mtd_fixseama) {
743                 fprintf(stderr,
744         "        -c datasize             amount of data to be used for checksum calculation (for fixtrx / fixseama)\n");
745         }
746         fprintf(stderr,
747 #ifdef FIS_SUPPORT
748         "        -F <part>[:<size>[:<entrypoint>]][,<part>...]\n"
749         "                                alter the fis partition table to create new partitions replacing\n"
750         "                                the partitions provided as argument to the write command\n"
751         "                                (only valid together with the write command)\n"
752 #endif
753         "\n"
754         "Example: To write linux.trx to mtd4 labeled as linux and reboot afterwards\n"
755         "         mtd -r write linux.trx linux\n\n");
756         exit(1);
757 }
758
759 static void do_reboot(void)
760 {
761         fprintf(stderr, "Rebooting ...\n");
762         fflush(stderr);
763
764         /* try regular reboot method first */
765         system("/sbin/reboot");
766         sleep(2);
767
768         /* if we're still alive at this point, force the kernel to reboot */
769         syscall(SYS_reboot,LINUX_REBOOT_MAGIC1,LINUX_REBOOT_MAGIC2,LINUX_REBOOT_CMD_RESTART,NULL);
770 }
771
772 int main (int argc, char **argv)
773 {
774         int ch, i, boot, imagefd = 0, force, unlocked;
775         char *erase[MAX_ARGS], *device = NULL;
776         char *fis_layout = NULL;
777         size_t offset = 0, data_size = 0, part_offset = 0, dump_len = 0;
778         enum {
779                 CMD_ERASE,
780                 CMD_WRITE,
781                 CMD_UNLOCK,
782                 CMD_JFFS2WRITE,
783                 CMD_FIXTRX,
784                 CMD_FIXSEAMA,
785                 CMD_VERIFY,
786                 CMD_DUMP,
787                 CMD_RESETBC,
788         } cmd = -1;
789
790         erase[0] = NULL;
791         boot = 0;
792         force = 0;
793         buflen = 0;
794         quiet = 0;
795         no_erase = 0;
796
797         while ((ch = getopt(argc, argv,
798 #ifdef FIS_SUPPORT
799                         "F:"
800 #endif
801                         "frnqe:d:s:j:p:o:c:l:")) != -1)
802                 switch (ch) {
803                         case 'f':
804                                 force = 1;
805                                 break;
806                         case 'r':
807                                 boot = 1;
808                                 break;
809                         case 'n':
810                                 no_erase = 1;
811                                 break;
812                         case 'j':
813                                 jffs2file = optarg;
814                                 break;
815                         case 's':
816                                 errno = 0;
817                                 jffs2_skip_bytes = strtoul(optarg, 0, 0);
818                                 if (errno) {
819                                                 fprintf(stderr, "-s: illegal numeric string\n");
820                                                 usage();
821                                 }
822                                 break;
823                         case 'q':
824                                 quiet++;
825                                 break;
826                         case 'e':
827                                 i = 0;
828                                 while ((erase[i] != NULL) && ((i + 1) < MAX_ARGS))
829                                         i++;
830
831                                 erase[i++] = optarg;
832                                 erase[i] = NULL;
833                                 break;
834                         case 'd':
835                                 jffs2dir = optarg;
836                                 break;
837                         case 'p':
838                                 errno = 0;
839                                 part_offset = strtoul(optarg, 0, 0);
840                                 if (errno) {
841                                         fprintf(stderr, "-p: illegal numeric string\n");
842                                         usage();
843                                 }
844                                 break;
845                         case 'l':
846                                 errno = 0;
847                                 dump_len = strtoul(optarg, 0, 0);
848                                 if (errno) {
849                                         fprintf(stderr, "-l: illegal numeric string\n");
850                                         usage();
851                                 }
852                                 break;
853                         case 'o':
854                                 errno = 0;
855                                 offset = strtoul(optarg, 0, 0);
856                                 if (errno) {
857                                         fprintf(stderr, "-o: illegal numeric string\n");
858                                         usage();
859                                 }
860                                 break;
861                         case 'c':
862                                 errno = 0;
863                                 data_size = strtoul(optarg, 0, 0);
864                                 if (errno) {
865                                         fprintf(stderr, "-c: illegal numeric string\n");
866                                         usage();
867                                 }
868                                 break;
869 #ifdef FIS_SUPPORT
870                         case 'F':
871                                 fis_layout = optarg;
872                                 break;
873 #endif
874                         case '?':
875                         default:
876                                 usage();
877                 }
878         argc -= optind;
879         argv += optind;
880
881         if (argc < 2)
882                 usage();
883
884         if ((strcmp(argv[0], "unlock") == 0) && (argc == 2)) {
885                 cmd = CMD_UNLOCK;
886                 device = argv[1];
887         } else if ((strcmp(argv[0], "erase") == 0) && (argc == 2)) {
888                 cmd = CMD_ERASE;
889                 device = argv[1];
890         } else if (((strcmp(argv[0], "resetbc") == 0) && (argc == 2)) && mtd_resetbc) {
891                 cmd = CMD_RESETBC;
892                 device = argv[1];
893         } else if (((strcmp(argv[0], "fixtrx") == 0) && (argc == 2)) && mtd_fixtrx) {
894                 cmd = CMD_FIXTRX;
895                 device = argv[1];
896         } else if (((strcmp(argv[0], "fixseama") == 0) && (argc == 2)) && mtd_fixseama) {
897                 cmd = CMD_FIXSEAMA;
898                 device = argv[1];
899         } else if ((strcmp(argv[0], "verify") == 0) && (argc == 3)) {
900                 cmd = CMD_VERIFY;
901                 imagefile = argv[1];
902                 device = argv[2];
903         } else if ((strcmp(argv[0], "dump") == 0) && (argc == 2)) {
904                 cmd = CMD_DUMP;
905                 device = argv[1];
906         } else if ((strcmp(argv[0], "write") == 0) && (argc == 3)) {
907                 cmd = CMD_WRITE;
908                 device = argv[2];
909
910                 if (strcmp(argv[1], "-") == 0) {
911                         imagefile = "<stdin>";
912                         imagefd = 0;
913                 } else {
914                         imagefile = argv[1];
915                         if ((imagefd = open(argv[1], O_RDONLY)) < 0) {
916                                 fprintf(stderr, "Couldn't open image file: %s!\n", imagefile);
917                                 exit(1);
918                         }
919                 }
920
921                 if (!mtd_check(device)) {
922                         fprintf(stderr, "Can't open device for writing!\n");
923                         exit(1);
924                 }
925                 /* check trx file before erasing or writing anything */
926                 if (!image_check(imagefd, device) && !force) {
927                         fprintf(stderr, "Image check failed.\n");
928                         exit(1);
929                 }
930         } else if ((strcmp(argv[0], "jffs2write") == 0) && (argc == 3)) {
931                 cmd = CMD_JFFS2WRITE;
932                 device = argv[2];
933
934                 imagefile = argv[1];
935                 if (!mtd_check(device)) {
936                         fprintf(stderr, "Can't open device for writing!\n");
937                         exit(1);
938                 }
939         } else {
940                 usage();
941         }
942
943         sync();
944
945         i = 0;
946         unlocked = 0;
947         while (erase[i] != NULL) {
948                 mtd_unlock(erase[i]);
949                 mtd_erase(erase[i]);
950                 if (strcmp(erase[i], device) == 0)
951                         unlocked = 1;
952                 i++;
953         }
954
955         switch (cmd) {
956                 case CMD_UNLOCK:
957                         if (!unlocked)
958                                 mtd_unlock(device);
959                         break;
960                 case CMD_VERIFY:
961                         mtd_verify(device, imagefile);
962                         break;
963                 case CMD_DUMP:
964                         mtd_dump(device, offset, dump_len);
965                         break;
966                 case CMD_ERASE:
967                         if (!unlocked)
968                                 mtd_unlock(device);
969                         mtd_erase(device);
970                         break;
971                 case CMD_WRITE:
972                         if (!unlocked)
973                                 mtd_unlock(device);
974                         mtd_write(imagefd, device, fis_layout, part_offset);
975                         break;
976                 case CMD_JFFS2WRITE:
977                         if (!unlocked)
978                                 mtd_unlock(device);
979                         mtd_write_jffs2(device, imagefile, jffs2dir);
980                         break;
981                 case CMD_FIXTRX:
982                         if (mtd_fixtrx) {
983                                 mtd_fixtrx(device, offset, data_size);
984                         }
985                         break;
986                 case CMD_RESETBC:
987                         if (mtd_resetbc) {
988                                 mtd_resetbc(device);
989                         }
990                         break;
991                 case CMD_FIXSEAMA:
992                         if (mtd_fixseama)
993                                 mtd_fixseama(device, 0, data_size);
994                         break;
995         }
996
997         sync();
998
999         if (boot)
1000                 do_reboot();
1001
1002         return 0;
1003 }