Merge with /home/wd/git/u-boot/testing-NAND/ to add new NAND handling.
[platform/kernel/u-boot.git] / common / cmd_jffs2.c
1 /*
2  * (C) Copyright 2002
3  * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
4  *
5  * (C) Copyright 2002
6  * Robert Schwebel, Pengutronix, <r.schwebel@pengutronix.de>
7  *
8  * (C) Copyright 2003
9  * Kai-Uwe Bloem, Auerswald GmbH & Co KG, <linux-development@auerswald.de>
10  *
11  * (C) Copyright 2005
12  * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
13  *
14  *   Added support for reading flash partition table from environment.
15  *   Parsing routines are based on driver/mtd/cmdline.c from the linux 2.4
16  *   kernel tree.
17  *
18  *   $Id: cmdlinepart.c,v 1.17 2004/11/26 11:18:47 lavinen Exp $
19  *   Copyright 2002 SYSGO Real-Time Solutions GmbH
20  *
21  * See file CREDITS for list of people who contributed to this
22  * project.
23  *
24  * This program is free software; you can redistribute it and/or
25  * modify it under the terms of the GNU General Public License as
26  * published by the Free Software Foundation; either version 2 of
27  * the License, or (at your option) any later version.
28  *
29  * This program is distributed in the hope that it will be useful,
30  * but WITHOUT ANY WARRANTY; without even the implied warranty of
31  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
32  * GNU General Public License for more details.
33  *
34  * You should have received a copy of the GNU General Public License
35  * along with this program; if not, write to the Free Software
36  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
37  * MA 02111-1307 USA
38  */
39
40 /*
41  * Three environment variables are used by the parsing routines:
42  *
43  * 'partition' - keeps current partition identifier
44  *
45  * partition  := <part-id>
46  * <part-id>  := <dev-id>,part_num
47  *
48  *
49  * 'mtdids' - linux kernel mtd device id <-> u-boot device id mapping
50  *
51  * mtdids=<idmap>[,<idmap>,...]
52  *
53  * <idmap>    := <dev-id>=<mtd-id>
54  * <dev-id>   := 'nand'|'nor'<dev-num>
55  * <dev-num>  := mtd device number, 0...
56  * <mtd-id>   := unique device tag used by linux kernel to find mtd device (mtd->name)
57  *
58  *
59  * 'mtdparts' - partition list
60  *
61  * mtdparts=mtdparts=<mtd-def>[;<mtd-def>...]
62  *
63  * <mtd-def>  := <mtd-id>:<part-def>[,<part-def>...]
64  * <mtd-id>   := unique device tag used by linux kernel to find mtd device (mtd->name)
65  * <part-def> := <size>[@<offset>][<name>][<ro-flag>]
66  * <size>     := standard linux memsize OR '-' to denote all remaining space
67  * <offset>   := partition start offset within the device
68  * <name>     := '(' NAME ')'
69  * <ro-flag>  := when set to 'ro' makes partition read-only (not used, passed to kernel)
70  *
71  * Notes:
72  * - each <mtd-id> used in mtdparts must albo exist in 'mtddis' mapping
73  * - if the above variables are not set defaults for a given target are used
74  *
75  * Examples:
76  *
77  * 1 NOR Flash, with 1 single writable partition:
78  * mtdids=nor0=edb7312-nor
79  * mtdparts=mtdparts=edb7312-nor:-
80  *
81  * 1 NOR Flash with 2 partitions, 1 NAND with one
82  * mtdids=nor0=edb7312-nor,nand0=edb7312-nand
83  * mtdparts=mtdparts=edb7312-nor:256k(ARMboot)ro,-(root);edb7312-nand:-(home)
84  *
85  */
86
87 /*
88  * JFFS2/CRAMFS support
89  */
90 #include <common.h>
91 #include <command.h>
92 #include <malloc.h>
93 #include <jffs2/jffs2.h>
94 #include <linux/mtd/nand.h>
95 #include <linux/list.h>
96 #include <linux/ctype.h>
97
98 #if (CONFIG_COMMANDS & CFG_CMD_JFFS2)
99
100 #include <cramfs/cramfs_fs.h>
101
102 #ifdef CONFIG_NEW_NAND_CODE
103 #include <nand.h>
104 #endif
105
106 /* enable/disable debugging messages */
107 #define DEBUG_JFFS
108 #undef  DEBUG_JFFS
109
110 #ifdef  DEBUG_JFFS
111 # define DEBUGF(fmt, args...)   printf(fmt ,##args)
112 #else
113 # define DEBUGF(fmt, args...)
114 #endif
115
116 /* special size referring to all the remaining space in a partition */
117 #define SIZE_REMAINING          0xFFFFFFFF
118
119 /* special offset value, it is used when not provided by user
120  *
121  * this value is used temporarily during parsing, later such offests
122  * are recalculated */
123 #define OFFSET_NOT_SPECIFIED    0xFFFFFFFF
124
125 /* minimum partition size */
126 #define MIN_PART_SIZE           4096
127
128 /* this flag needs to be set in part_info struct mask_flags
129  * field for read-only partitions */
130 #define MTD_WRITEABLE_CMD               1
131
132 #ifdef CONFIG_JFFS2_CMDLINE
133 /* default values for mtdids and mtdparts variables */
134 #if defined(MTDIDS_DEFAULT)
135 static const char *const mtdids_default = MTDIDS_DEFAULT;
136 #else
137 #warning "MTDIDS_DEFAULT not defined!"
138 static const char *const mtdids_default = NULL;
139 #endif
140
141 #if defined(MTDPARTS_DEFAULT)
142 static const char *const mtdparts_default = MTDPARTS_DEFAULT;
143 #else
144 #warning "MTDPARTS_DEFAULT not defined!"
145 static const char *const mtdparts_default = NULL;
146 #endif
147
148 /* copies of last seen 'mtdids', 'mtdparts' and 'partition' env variables */
149 #define MTDIDS_MAXLEN           128
150 #define MTDPARTS_MAXLEN         512
151 #define PARTITION_MAXLEN        16
152 static char last_ids[MTDIDS_MAXLEN];
153 static char last_parts[MTDPARTS_MAXLEN];
154 static char last_partition[PARTITION_MAXLEN];
155
156 /* low level jffs2 cache cleaning routine */
157 extern void jffs2_free_cache(struct part_info *part);
158
159 /* mtdids mapping list, filled by parse_ids() */
160 struct list_head mtdids;
161
162 /* device/partition list, parse_cmdline() parses into here */
163 struct list_head devices;
164 #endif /* #ifdef CONFIG_JFFS2_CMDLINE */
165
166 /* current active device and partition number */
167 static struct mtd_device *current_dev = NULL;
168 static u8 current_partnum = 0;
169
170 extern int cramfs_check (struct part_info *info);
171 extern int cramfs_load (char *loadoffset, struct part_info *info, char *filename);
172 extern int cramfs_ls (struct part_info *info, char *filename);
173 extern int cramfs_info (struct part_info *info);
174
175 static struct part_info* jffs2_part_info(struct mtd_device *dev, unsigned int part_num);
176
177 /* command line only routines */
178 #ifdef CONFIG_JFFS2_CMDLINE
179
180 static struct mtdids* id_find_by_mtd_id(const char *mtd_id, unsigned int mtd_id_len);
181 static int device_del(struct mtd_device *dev);
182
183 /**
184  * Parses a string into a number.  The number stored at ptr is
185  * potentially suffixed with K (for kilobytes, or 1024 bytes),
186  * M (for megabytes, or 1048576 bytes), or G (for gigabytes, or
187  * 1073741824).  If the number is suffixed with K, M, or G, then
188  * the return value is the number multiplied by one kilobyte, one
189  * megabyte, or one gigabyte, respectively.
190  *
191  * @param ptr where parse begins
192  * @param retptr output pointer to next char after parse completes (output)
193  * @return resulting unsigned int
194  */
195 static unsigned long memsize_parse (const char *const ptr, const char **retptr)
196 {
197         unsigned long ret = simple_strtoul(ptr, (char **)retptr, 0);
198
199         switch (**retptr) {
200                 case 'G':
201                 case 'g':
202                         ret <<= 10;
203                 case 'M':
204                 case 'm':
205                         ret <<= 10;
206                 case 'K':
207                 case 'k':
208                         ret <<= 10;
209                         (*retptr)++;
210                 default:
211                         break;
212         }
213
214         return ret;
215 }
216
217 /**
218  * Format string describing supplied size. This routine does the opposite job
219  * to memsize_parse(). Size in bytes is converted to string and if possible
220  * shortened by using k (kilobytes), m (megabytes) or g (gigabytes) suffix.
221  *
222  * Note, that this routine does not check for buffer overflow, it's the caller
223  * who must assure enough space.
224  *
225  * @param buf output buffer
226  * @param size size to be converted to string
227  */
228 static void memsize_format(char *buf, u32 size)
229 {
230 #define SIZE_GB ((u32)1024*1024*1024)
231 #define SIZE_MB ((u32)1024*1024)
232 #define SIZE_KB ((u32)1024)
233
234         if ((size % SIZE_GB) == 0)
235                 sprintf(buf, "%lug", size/SIZE_GB);
236         else if ((size % SIZE_MB) == 0)
237                 sprintf(buf, "%lum", size/SIZE_MB);
238         else if (size % SIZE_KB == 0)
239                 sprintf(buf, "%luk", size/SIZE_KB);
240         else
241                 sprintf(buf, "%lu", size);
242 }
243
244 /**
245  * This routine does global indexing of all partitions. Resulting index for
246  * current partition is saved in 'mtddevnum'. Current partition name in
247  * 'mtddevname'.
248  */
249 static void index_partitions(void)
250 {
251         char buf[16];
252         u16 mtddevnum;
253         struct part_info *part;
254         struct list_head *dentry;
255         struct mtd_device *dev;
256
257         DEBUGF("--- index partitions ---\n");
258
259         if (current_dev) {
260                 mtddevnum = 0;
261                 list_for_each(dentry, &devices) {
262                         dev = list_entry(dentry, struct mtd_device, link);
263                         if (dev == current_dev) {
264                                 mtddevnum += current_partnum;
265                                 sprintf(buf, "%d", mtddevnum);
266                                 setenv("mtddevnum", buf);
267                                 break;
268                         }
269                         mtddevnum += dev->num_parts;
270                 }
271
272                 part = jffs2_part_info(current_dev, current_partnum);
273                 setenv("mtddevname", part->name);
274
275                 DEBUGF("=> mtddevnum %d,\n=> mtddevname %s\n", mtddevnum, part->name);
276         } else {
277                 setenv("mtddevnum", NULL);
278                 setenv("mtddevname", NULL);
279
280                 DEBUGF("=> mtddevnum NULL\n=> mtddevname NULL\n");
281         }
282 }
283
284 /**
285  * Save current device and partition in environment variable 'partition'.
286  */
287 static void current_save(void)
288 {
289         char buf[16];
290
291         DEBUGF("--- current_save ---\n");
292
293         if (current_dev) {
294                 sprintf(buf, "%s%d,%d", MTD_DEV_TYPE(current_dev->id->type),
295                                         current_dev->id->num, current_partnum);
296
297                 setenv("partition", buf);
298                 strncpy(last_partition, buf, 16);
299
300                 DEBUGF("=> partition %s\n", buf);
301         } else {
302                 setenv("partition", NULL);
303                 last_partition[0] = '\0';
304
305                 DEBUGF("=> partition NULL\n");
306         }
307         index_partitions();
308 }
309
310 /**
311  * Performs sanity check for supplied NOR flash partition. Table of existing
312  * NOR flash devices is searched and partition device is located. Alignment
313  * with the granularity of NOR flash sectors is verified.
314  *
315  * @param id of the parent device
316  * @param part partition to validate
317  * @return 0 if partition is valid, 1 otherwise
318  */
319 static int part_validate_nor(struct mtdids *id, struct part_info *part)
320 {
321 #if (CONFIG_COMMANDS & CFG_CMD_FLASH)
322         /* info for FLASH chips */
323         extern flash_info_t flash_info[];
324         flash_info_t *flash;
325         int offset_aligned;
326         u32 end_offset;
327         int i;
328
329         flash = &flash_info[id->num];
330
331         offset_aligned = 0;
332         for (i = 0; i < flash->sector_count; i++) {
333                 if ((flash->start[i] - flash->start[0]) == part->offset) {
334                         offset_aligned = 1;
335                         break;
336                 }
337         }
338         if (offset_aligned == 0) {
339                 printf("%s%d: partition (%s) start offset alignment incorrect\n",
340                                 MTD_DEV_TYPE(id->type), id->num, part->name);
341                 return 1;
342         }
343
344         end_offset = part->offset + part->size;
345         for (i = 0; i < flash->sector_count; i++) {
346                 if ((flash->start[i] - flash->start[0]) == end_offset)
347                         return 0;
348         }
349
350         if (flash->size == end_offset)
351                 return 0;
352
353         printf("%s%d: partition (%s) size alignment incorrect\n",
354                         MTD_DEV_TYPE(id->type), id->num, part->name);
355 #endif
356         return 1;
357 }
358
359 /**
360  * Performs sanity check for supplied NAND flash partition. Table of existing
361  * NAND flash devices is searched and partition device is located. Alignment
362  * with the granularity of nand erasesize is verified.
363  *
364  * @param id of the parent device
365  * @param part partition to validate
366  * @return 0 if partition is valid, 1 otherwise
367  */
368 static int part_validate_nand(struct mtdids *id, struct part_info *part)
369 {
370 #if defined(CONFIG_JFFS2_NAND) && (CONFIG_COMMANDS & CFG_CMD_NAND)
371         /* info for NAND chips */
372         nand_info_t *nand;
373
374         nand = &nand_info[id->num];
375
376         if ((unsigned long)(part->offset) % nand->erasesize) {
377                 printf("%s%d: partition (%s) start offset alignment incorrect\n",
378                                 MTD_DEV_TYPE(id->type), id->num, part->name);
379                 return 1;
380         }
381
382         if (part->size % nand->erasesize) {
383                 printf("%s%d: partition (%s) size alignment incorrect\n",
384                                 MTD_DEV_TYPE(id->type), id->num, part->name);
385                 return 1;
386         }
387
388         return 0;
389 #else
390         return 1;
391 #endif
392 }
393
394 /**
395  * Performs sanity check for supplied partition. Offset and size are verified
396  * to be within valid range. Partition type is checked and either
397  * parts_validate_nor() or parts_validate_nand() is called with the argument
398  * of part.
399  *
400  * @param id of the parent device
401  * @param part partition to validate
402  * @return 0 if partition is valid, 1 otherwise
403  */
404 static int part_validate(struct mtdids *id, struct part_info *part)
405 {
406         if (part->size == SIZE_REMAINING)
407                 part->size = id->size - part->offset;
408
409         if (part->offset > id->size) {
410                 printf("%s: offset %08lx beyond flash size %08lx\n",
411                                 id->mtd_id, part->offset, id->size);
412                 return 1;
413         }
414
415         if ((part->offset + part->size) <= part->offset) {
416                 printf("%s%d: partition (%s) size too big\n",
417                                 MTD_DEV_TYPE(id->type), id->num, part->name);
418                 return 1;
419         }
420
421         if (part->offset + part->size > id->size) {
422                 printf("%s: partitioning exceeds flash size\n", id->mtd_id);
423                 return 1;
424         }
425
426         if (id->type == MTD_DEV_TYPE_NAND)
427                 return part_validate_nand(id, part);
428         else if (id->type == MTD_DEV_TYPE_NOR)
429                 return part_validate_nor(id, part);
430         else
431                 DEBUGF("part_validate: invalid dev type\n");
432
433         return 1;
434 }
435
436 /**
437  * Delete selected partition from the partion list of the specified device.
438  *
439  * @param dev device to delete partition from
440  * @param part partition to delete
441  * @return 0 on success, 1 otherwise
442  */
443 static int part_del(struct mtd_device *dev, struct part_info *part)
444 {
445         u8 current_save_needed = 0;
446
447         /* if there is only one partition, remove whole device */
448         if (dev->num_parts == 1)
449                 return device_del(dev);
450
451         /* otherwise just delete this partition */
452
453         if (dev == current_dev) {
454                 /* we are modyfing partitions for the current device,
455                  * update current */
456                 struct part_info *curr_pi;
457                 curr_pi = jffs2_part_info(current_dev, current_partnum);
458
459                 if (curr_pi) {
460                         if (curr_pi == part) {
461                                 printf("current partition deleted, resetting current to 0\n");
462                                 current_partnum = 0;
463                         } else if (part->offset <= curr_pi->offset) {
464                                 current_partnum--;
465                         }
466                         current_save_needed = 1;
467                 }
468         }
469
470 #ifndef CONFIG_NEW_NAND_CODE
471         jffs2_free_cache(part);
472 #endif
473         list_del(&part->link);
474         free(part);
475         dev->num_parts--;
476
477         if (current_save_needed > 0)
478                 current_save();
479         else
480                 index_partitions();
481
482         return 0;
483 }
484
485 /**
486  * Delete all partitions from parts head list, free memory.
487  *
488  * @param head list of partitions to delete
489  */
490 static void part_delall(struct list_head *head)
491 {
492         struct list_head *entry, *n;
493         struct part_info *part_tmp;
494
495         /* clean tmp_list and free allocated memory */
496         list_for_each_safe(entry, n, head) {
497                 part_tmp = list_entry(entry, struct part_info, link);
498
499 #ifndef CONFIG_NEW_NAND_CODE
500                 jffs2_free_cache(part_tmp);
501 #endif
502                 list_del(entry);
503                 free(part_tmp);
504         }
505 }
506
507 /**
508  * Add new partition to the supplied partition list. Make sure partitions are
509  * sorted by offset in ascending order.
510  *
511  * @param head list this partition is to be added to
512  * @param new partition to be added
513  */
514 static int part_sort_add(struct mtd_device *dev, struct part_info *part)
515 {
516         struct list_head *entry;
517         struct part_info *new_pi, *curr_pi;
518
519         /* link partition to parrent dev */
520         part->dev = dev;
521
522         if (list_empty(&dev->parts)) {
523                 DEBUGF("part_sort_add: list empty\n");
524                 list_add(&part->link, &dev->parts);
525                 dev->num_parts++;
526                 index_partitions();
527                 return 0;
528         }
529
530         new_pi = list_entry(&part->link, struct part_info, link);
531
532         /* get current partition info if we are updating current device */
533         curr_pi = NULL;
534         if (dev == current_dev)
535                 curr_pi = jffs2_part_info(current_dev, current_partnum);
536
537         list_for_each(entry, &dev->parts) {
538                 struct part_info *pi;
539
540                 pi = list_entry(entry, struct part_info, link);
541
542                 /* be compliant with kernel cmdline, allow only one partition at offset zero */
543                 if ((new_pi->offset == pi->offset) && (pi->offset == 0)) {
544                         printf("cannot add second partition at offset 0\n");
545                         return 1;
546                 }
547
548                 if (new_pi->offset <= pi->offset) {
549                         list_add_tail(&part->link, entry);
550                         dev->num_parts++;
551
552                         if (curr_pi && (pi->offset <= curr_pi->offset)) {
553                                 /* we are modyfing partitions for the current
554                                  * device, update current */
555                                 current_partnum++;
556                                 current_save();
557                         } else {
558                                 index_partitions();
559                         }
560                         return 0;
561                 }
562         }
563
564         list_add_tail(&part->link, &dev->parts);
565         dev->num_parts++;
566         index_partitions();
567         return 0;
568 }
569
570 /**
571  * Add provided partition to the partition list of a given device.
572  *
573  * @param dev device to which partition is added
574  * @param part partition to be added
575  * @return 0 on success, 1 otherwise
576  */
577 static int part_add(struct mtd_device *dev, struct part_info *part)
578 {
579         /* verify alignment and size */
580         if (part_validate(dev->id, part) != 0)
581                 return 1;
582
583         /* partition is ok, add it to the list */
584         if (part_sort_add(dev, part) != 0)
585                 return 1;
586
587         return 0;
588 }
589
590 /**
591  * Parse one partition definition, allocate memory and return pointer to this
592  * location in retpart.
593  *
594  * @param partdef pointer to the partition definition string i.e. <part-def>
595  * @param ret output pointer to next char after parse completes (output)
596  * @param retpart pointer to the allocated partition (output)
597  * @return 0 on success, 1 otherwise
598  */
599 static int part_parse(const char *const partdef, const char **ret, struct part_info **retpart)
600 {
601         struct part_info *part;
602         unsigned long size;
603         unsigned long offset;
604         const char *name;
605         int name_len;
606         unsigned int mask_flags;
607         const char *p;
608
609         p = partdef;
610         *retpart = NULL;
611         *ret = NULL;
612
613         /* fetch the partition size */
614         if (*p == '-') {
615                 /* assign all remaining space to this partition */
616                 DEBUGF("'-': remaining size assigned\n");
617                 size = SIZE_REMAINING;
618                 p++;
619         } else {
620                 size = memsize_parse(p, &p);
621                 if (size < MIN_PART_SIZE) {
622                         printf("partition size too small (%lx)\n", size);
623                         return 1;
624                 }
625         }
626
627         /* check for offset */
628         offset = OFFSET_NOT_SPECIFIED;
629         if (*p == '@') {
630                 p++;
631                 offset = memsize_parse(p, &p);
632         }
633
634         /* now look for the name */
635         if (*p == '(') {
636                 name = ++p;
637                 if ((p = strchr(name, ')')) == NULL) {
638                         printf("no closing ) found in partition name\n");
639                         return 1;
640                 }
641                 name_len = p - name + 1;
642                 if ((name_len - 1) == 0) {
643                         printf("empty partition name\n");
644                         return 1;
645                 }
646                 p++;
647         } else {
648                 /* 0x00000000@0x00000000 */
649                 name_len = 22;
650                 name = NULL;
651         }
652
653         /* test for options */
654         mask_flags = 0;
655         if (strncmp(p, "ro", 2) == 0) {
656                 mask_flags |= MTD_WRITEABLE_CMD;
657                 p += 2;
658         }
659
660         /* check for next partition definition */
661         if (*p == ',') {
662                 if (size == SIZE_REMAINING) {
663                         *ret = NULL;
664                         printf("no partitions allowed after a fill-up partition\n");
665                         return 1;
666                 }
667                 *ret = ++p;
668         } else if ((*p == ';') || (*p == '\0')) {
669                 *ret = p;
670         } else {
671                 printf("unexpected character '%c' at the end of partition\n", *p);
672                 *ret = NULL;
673                 return 1;
674         }
675
676         /*  allocate memory */
677         part = (struct part_info *)malloc(sizeof(struct part_info) + name_len);
678         if (!part) {
679                 printf("out of memory\n");
680                 return 1;
681         }
682         memset(part, 0, sizeof(struct part_info) + name_len);
683         part->size = size;
684         part->offset = offset;
685         part->mask_flags = mask_flags;
686         part->name = (char *)(part + 1);
687
688         if (name) {
689                 /* copy user provided name */
690                 strncpy(part->name, name, name_len - 1);
691                 part->auto_name = 0;
692         } else {
693                 /* auto generated name in form of size@offset */
694                 sprintf(part->name, "0x%08lx@0x%08lx", size, offset);
695                 part->auto_name = 1;
696         }
697
698         part->name[name_len - 1] = '\0';
699         INIT_LIST_HEAD(&part->link);
700
701         DEBUGF("+ partition: name %-22s size 0x%08x offset 0x%08x mask flags %d\n",
702                         part->name, part->size,
703                         part->offset, part->mask_flags);
704
705         *retpart = part;
706         return 0;
707 }
708 #endif/* #ifdef CONFIG_JFFS2_CMDLINE */
709
710 /**
711  * Check device number to be within valid range for given device type.
712  *
713  * @param dev device to validate
714  * @return 0 if device is valid, 1 otherwise
715  */
716 static int device_validate(u8 type, u8 num, u32 *size)
717 {
718         if (type == MTD_DEV_TYPE_NOR) {
719 #if (CONFIG_COMMANDS & CFG_CMD_FLASH)
720                 if (num < CFG_MAX_FLASH_BANKS) {
721                         extern flash_info_t flash_info[];
722                         *size = flash_info[num].size;
723
724                         return 0;
725                 }
726
727                 printf("no such FLASH device: %s%d (valid range 0 ... %d\n",
728                                 MTD_DEV_TYPE(type), num, CFG_MAX_FLASH_BANKS - 1);
729 #else
730                 printf("support for FLASH devices not present\n");
731 #endif
732         } else if (type == MTD_DEV_TYPE_NAND) {
733 #if defined(CONFIG_JFFS2_NAND) && (CONFIG_COMMANDS & CFG_CMD_NAND)
734                 if (num < CFG_MAX_NAND_DEVICE) {
735 #ifdef CONFIG_NEW_NAND_CODE
736                         *size = nand_info[num].size;
737 #else
738                         extern struct nand_chip nand_dev_desc[CFG_MAX_NAND_DEVICE];
739                         *size = nand_dev_desc[num].totlen;
740 #endif
741                         return 0;
742                 }
743
744                 printf("no such NAND device: %s%d (valid range 0 ... %d)\n",
745                                 MTD_DEV_TYPE(type), num, CFG_MAX_NAND_DEVICE - 1);
746 #else
747                 printf("support for NAND devices not present\n");
748 #endif
749         }
750
751         return 1;
752 }
753
754 #ifdef CONFIG_JFFS2_CMDLINE
755 /**
756  * Delete all mtd devices from a supplied devices list, free memory allocated for
757  * each device and delete all device partitions.
758  *
759  * @return 0 on success, 1 otherwise
760  */
761 static int device_delall(struct list_head *head)
762 {
763         struct list_head *entry, *n;
764         struct mtd_device *dev_tmp;
765
766         /* clean devices list */
767         list_for_each_safe(entry, n, head) {
768                 dev_tmp = list_entry(entry, struct mtd_device, link);
769                 list_del(entry);
770                 part_delall(&dev_tmp->parts);
771                 free(dev_tmp);
772         }
773         INIT_LIST_HEAD(&devices);
774
775         return 0;
776 }
777
778 /**
779  * If provided device exists it's partitions are deleted, device is removed
780  * from device list and device memory is freed.
781  *
782  * @param dev device to be deleted
783  * @return 0 on success, 1 otherwise
784  */
785 static int device_del(struct mtd_device *dev)
786 {
787         part_delall(&dev->parts);
788         list_del(&dev->link);
789         free(dev);
790
791         if (dev == current_dev) {
792                 /* we just deleted current device */
793                 if (list_empty(&devices)) {
794                         current_dev = NULL;
795                 } else {
796                         /* reset first partition from first dev from the
797                          * devices list as current */
798                         current_dev = list_entry(devices.next, struct mtd_device, link);
799                         current_partnum = 0;
800                 }
801                 current_save();
802                 return 0;
803         }
804
805         index_partitions();
806         return 0;
807 }
808
809 /**
810  * Search global device list and return pointer to the device of type and num
811  * specified.
812  *
813  * @param type device type
814  * @param num device number
815  * @return NULL if requested device does not exist
816  */
817 static struct mtd_device* device_find(u8 type, u8 num)
818 {
819         struct list_head *entry;
820         struct mtd_device *dev_tmp;
821
822         list_for_each(entry, &devices) {
823                 dev_tmp = list_entry(entry, struct mtd_device, link);
824
825                 if ((dev_tmp->id->type == type) && (dev_tmp->id->num == num))
826                         return dev_tmp;
827         }
828
829         return NULL;
830 }
831
832 /**
833  * Add specified device to the global device list.
834  *
835  * @param dev device to be added
836  */
837 static void device_add(struct mtd_device *dev)
838 {
839         u8 current_save_needed = 0;
840
841         if (list_empty(&devices)) {
842                 current_dev = dev;
843                 current_partnum = 0;
844                 current_save_needed = 1;
845         }
846
847         list_add_tail(&dev->link, &devices);
848
849         if (current_save_needed > 0)
850                 current_save();
851         else
852                 index_partitions();
853 }
854
855 /**
856  * Parse device type, name and mtd-id. If syntax is ok allocate memory and
857  * return pointer to the device structure.
858  *
859  * @param mtd_dev pointer to the device definition string i.e. <mtd-dev>
860  * @param ret output pointer to next char after parse completes (output)
861  * @param retdev pointer to the allocated device (output)
862  * @return 0 on success, 1 otherwise
863  */
864 static int device_parse(const char *const mtd_dev, const char **ret, struct mtd_device **retdev)
865 {
866         struct mtd_device *dev;
867         struct part_info *part;
868         struct mtdids *id;
869         const char *mtd_id;
870         unsigned int mtd_id_len;
871         const char *p, *pend;
872         LIST_HEAD(tmp_list);
873         struct list_head *entry, *n;
874         u16 num_parts;
875         u32 offset;
876         int err = 1;
877
878         p = mtd_dev;
879         *retdev = NULL;
880         *ret = NULL;
881
882         DEBUGF("===device_parse===\n");
883
884         /* fetch <mtd-id> */
885         mtd_id = p;
886         if (!(p = strchr(mtd_id, ':'))) {
887                 printf("no <mtd-id> identifier\n");
888                 return 1;
889         }
890         mtd_id_len = p - mtd_id + 1;
891         p++;
892
893         /* verify if we have a valid device specified */
894         if ((id = id_find_by_mtd_id(mtd_id, mtd_id_len - 1)) == NULL) {
895                 printf("invalid mtd device '%.*s'\n", mtd_id_len - 1, mtd_id);
896                 return 1;
897         }
898
899         DEBUGF("dev type = %d (%s), dev num = %d, mtd-id = %s\n",
900                         id->type, MTD_DEV_TYPE(id->type),
901                         id->num, id->mtd_id);
902         pend = strchr(p, ';');
903         DEBUGF("parsing partitions %.*s\n", (pend ? pend - p : strlen(p)), p);
904
905
906         /* parse partitions */
907         num_parts = 0;
908
909         offset = 0;
910         if ((dev = device_find(id->type, id->num)) != NULL) {
911                 /* if device already exists start at the end of the last partition */
912                 part = list_entry(dev->parts.prev, struct part_info, link);
913                 offset = part->offset + part->size;
914         }
915
916         while (p && (*p != '\0') && (*p != ';')) {
917                 err = 1;
918                 if ((part_parse(p, &p, &part) != 0) || (!part))
919                         break;
920
921                 /* calculate offset when not specified */
922                 if (part->offset == OFFSET_NOT_SPECIFIED)
923                         part->offset = offset;
924                 else
925                         offset = part->offset;
926
927                 /* verify alignment and size */
928                 if (part_validate(id, part) != 0)
929                         break;
930
931                 offset += part->size;
932
933                 /* partition is ok, add it to the list */
934                 list_add_tail(&part->link, &tmp_list);
935                 num_parts++;
936                 err = 0;
937         }
938         if (err == 1) {
939                 part_delall(&tmp_list);
940                 return 1;
941         }
942
943         if (num_parts == 0) {
944                 printf("no partitions for device %s%d (%s)\n",
945                                 MTD_DEV_TYPE(id->type), id->num, id->mtd_id);
946                 return 1;
947         }
948
949         DEBUGF("\ntotal partitions: %d\n", num_parts);
950
951         /* check for next device presence */
952         if (p) {
953                 if (*p == ';') {
954                         *ret = ++p;
955                 } else if (*p == '\0') {
956                         *ret = p;
957                 } else {
958                         printf("unexpected character '%c' at the end of device\n", *p);
959                         *ret = NULL;
960                         return 1;
961                 }
962         }
963
964         /* allocate memory for mtd_device structure */
965         if ((dev = (struct mtd_device *)malloc(sizeof(struct mtd_device))) == NULL) {
966                 printf("out of memory\n");
967                 return 1;
968         }
969         memset(dev, 0, sizeof(struct mtd_device));
970         dev->id = id;
971         dev->num_parts = 0; /* part_sort_add increments num_parts */
972         INIT_LIST_HEAD(&dev->parts);
973         INIT_LIST_HEAD(&dev->link);
974
975         /* move partitions from tmp_list to dev->parts */
976         list_for_each_safe(entry, n, &tmp_list) {
977                 part = list_entry(entry, struct part_info, link);
978                 list_del(entry);
979                 if (part_sort_add(dev, part) != 0) {
980                         device_del(dev);
981                         return 1;
982                 }
983         }
984
985         *retdev = dev;
986
987         DEBUGF("===\n\n");
988         return 0;
989 }
990
991 /**
992  * Initialize global device list.
993  *
994  * @return 0 on success, 1 otherwise
995  */
996 static int devices_init(void)
997 {
998         last_parts[0] = '\0';
999         current_dev = NULL;
1000         current_save();
1001
1002         return device_delall(&devices);
1003 }
1004
1005 /*
1006  * Search global mtdids list and find id of requested type and number.
1007  *
1008  * @return pointer to the id if it exists, NULL otherwise
1009  */
1010 static struct mtdids* id_find(u8 type, u8 num)
1011 {
1012         struct list_head *entry;
1013         struct mtdids *id;
1014
1015         list_for_each(entry, &mtdids) {
1016                 id = list_entry(entry, struct mtdids, link);
1017
1018                 if ((id->type == type) && (id->num == num))
1019                         return id;
1020         }
1021
1022         return NULL;
1023 }
1024
1025 /**
1026  * Search global mtdids list and find id of a requested mtd_id.
1027  *
1028  * Note: first argument is not null terminated.
1029  *
1030  * @param mtd_id string containing requested mtd_id
1031  * @param mtd_id_len length of supplied mtd_id
1032  * @return pointer to the id if it exists, NULL otherwise
1033  */
1034 static struct mtdids* id_find_by_mtd_id(const char *mtd_id, unsigned int mtd_id_len)
1035 {
1036         struct list_head *entry;
1037         struct mtdids *id;
1038
1039         DEBUGF("--- id_find_by_mtd_id: '%.*s' (len = %d)\n",
1040                         mtd_id_len, mtd_id, mtd_id_len);
1041
1042         list_for_each(entry, &mtdids) {
1043                 id = list_entry(entry, struct mtdids, link);
1044
1045                 DEBUGF("entry: '%s' (len = %d)\n",
1046                                 id->mtd_id, strlen(id->mtd_id));
1047
1048                 if (mtd_id_len != strlen(id->mtd_id))
1049                         continue;
1050                 if (strncmp(id->mtd_id, mtd_id, mtd_id_len) == 0)
1051                         return id;
1052         }
1053
1054         return NULL;
1055 }
1056 #endif /* #ifdef CONFIG_JFFS2_CMDLINE */
1057
1058 /**
1059  * Parse device id string <dev-id> := 'nand'|'nor'<dev-num>, return device
1060  * type and number.
1061  *
1062  * @param id string describing device id
1063  * @param ret_id output pointer to next char after parse completes (output)
1064  * @param dev_type parsed device type (output)
1065  * @param dev_num parsed device number (output)
1066  * @return 0 on success, 1 otherwise
1067  */
1068 int id_parse(const char *id, const char **ret_id, u8 *dev_type, u8 *dev_num)
1069 {
1070         const char *p = id;
1071
1072         *dev_type = 0;
1073         if (strncmp(p, "nand", 4) == 0) {
1074                 *dev_type = MTD_DEV_TYPE_NAND;
1075                 p += 4;
1076         } else if (strncmp(p, "nor", 3) == 0) {
1077                 *dev_type = MTD_DEV_TYPE_NOR;
1078                 p += 3;
1079         } else {
1080                 printf("incorrect device type in %s\n", id);
1081                 return 1;
1082         }
1083
1084         if (!isdigit(*p)) {
1085                 printf("incorrect device number in %s\n", id);
1086                 return 1;
1087         }
1088
1089         *dev_num = simple_strtoul(p, (char **)&p, 0);
1090         if (ret_id)
1091                 *ret_id = p;
1092         return 0;
1093 }
1094
1095 #ifdef CONFIG_JFFS2_CMDLINE
1096 /**
1097  * Process all devices and generate corresponding mtdparts string describing
1098  * all partitions on all devices.
1099  *
1100  * @param buf output buffer holding generated mtdparts string (output)
1101  * @param buflen buffer size
1102  * @return 0 on success, 1 otherwise
1103  */
1104 static int generate_mtdparts(char *buf, u32 buflen)
1105 {
1106         struct list_head *pentry, *dentry;
1107         struct mtd_device *dev;
1108         struct part_info *part, *prev_part;
1109         char *p = buf;
1110         char tmpbuf[32];
1111         u32 size, offset, len, part_cnt;
1112         u32 maxlen = buflen - 1;
1113
1114         DEBUGF("--- generate_mtdparts ---\n");
1115
1116         if (list_empty(&devices)) {
1117                 buf[0] = '\0';
1118                 return 0;
1119         }
1120
1121         sprintf(p, "mtdparts=");
1122         p += 9;
1123
1124         list_for_each(dentry, &devices) {
1125                 dev = list_entry(dentry, struct mtd_device, link);
1126
1127                 /* copy mtd_id */
1128                 len = strlen(dev->id->mtd_id) + 1;
1129                 if (len > maxlen)
1130                         goto cleanup;
1131                 memcpy(p, dev->id->mtd_id, len - 1);
1132                 p += len - 1;
1133                 *(p++) = ':';
1134                 maxlen -= len;
1135
1136                 /* format partitions */
1137                 prev_part = NULL;
1138                 part_cnt = 0;
1139                 list_for_each(pentry, &dev->parts) {
1140                         part = list_entry(pentry, struct part_info, link);
1141                         size = part->size;
1142                         offset = part->offset;
1143                         part_cnt++;
1144
1145                         /* partition size */
1146                         memsize_format(tmpbuf, size);
1147                         len = strlen(tmpbuf);
1148                         if (len > maxlen)
1149                                 goto cleanup;
1150                         memcpy(p, tmpbuf, len);
1151                         p += len;
1152                         maxlen -= len;
1153
1154
1155                         /* add offset only when there is a gap between
1156                          * partitions */
1157                         if ((!prev_part && (offset != 0)) ||
1158                                         (prev_part && ((prev_part->offset + prev_part->size) != part->offset))) {
1159
1160                                 memsize_format(tmpbuf, offset);
1161                                 len = strlen(tmpbuf) + 1;
1162                                 if (len > maxlen)
1163                                         goto cleanup;
1164                                 *(p++) = '@';
1165                                 memcpy(p, tmpbuf, len - 1);
1166                                 p += len - 1;
1167                                 maxlen -= len;
1168                         }
1169
1170                         /* copy name only if user supplied */
1171                         if(!part->auto_name) {
1172                                 len = strlen(part->name) + 2;
1173                                 if (len > maxlen)
1174                                         goto cleanup;
1175
1176                                 *(p++) = '(';
1177                                 memcpy(p, part->name, len - 2);
1178                                 p += len - 2;
1179                                 *(p++) = ')';
1180                                 maxlen -= len;
1181                         }
1182
1183                         /* ro mask flag */
1184                         if (part->mask_flags && MTD_WRITEABLE_CMD) {
1185                                 len = 2;
1186                                 if (len > maxlen)
1187                                         goto cleanup;
1188                                 *(p++) = 'r';
1189                                 *(p++) = 'o';
1190                                 maxlen -= 2;
1191                         }
1192
1193                         /* print ',' separator if there are other partitions
1194                          * following */
1195                         if (dev->num_parts > part_cnt) {
1196                                 if (1 > maxlen)
1197                                         goto cleanup;
1198                                 *(p++) = ',';
1199                                 maxlen--;
1200                         }
1201                         prev_part = part;
1202                 }
1203                 /* print ';' separator if there are other devices following */
1204                 if (dentry->next != &devices) {
1205                         if (1 > maxlen)
1206                                 goto cleanup;
1207                         *(p++) = ';';
1208                         maxlen--;
1209                 }
1210         }
1211
1212         /* we still have at least one char left, as we decremented maxlen at
1213          * the begining */
1214         *p = '\0';
1215
1216         return 0;
1217
1218 cleanup:
1219         last_parts[0] = '\0';
1220         return 1;
1221 }
1222
1223 /**
1224  * Call generate_mtdparts to process all devices and generate corresponding
1225  * mtdparts string, save it in mtdparts environment variable.
1226  *
1227  * @param buf output buffer holding generated mtdparts string (output)
1228  * @param buflen buffer size
1229  * @return 0 on success, 1 otherwise
1230  */
1231 static int generate_mtdparts_save(char *buf, u32 buflen)
1232 {
1233         int ret;
1234
1235         ret = generate_mtdparts(buf, buflen);
1236
1237         if ((buf[0] != '\0') && (ret == 0))
1238                 setenv("mtdparts", buf);
1239         else
1240                 setenv("mtdparts", NULL);
1241
1242         return ret;
1243 }
1244
1245 /**
1246  * Format and print out a partition list for each device from global device
1247  * list.
1248  */
1249 static void list_partitions(void)
1250 {
1251         struct list_head *dentry, *pentry;
1252         struct part_info *part;
1253         struct mtd_device *dev;
1254         int part_num;
1255
1256         DEBUGF("\n---list_partitions---\n");
1257         list_for_each(dentry, &devices) {
1258                 dev = list_entry(dentry, struct mtd_device, link);
1259                 printf("\ndevice %s%d <%s>, # parts = %d\n",
1260                                 MTD_DEV_TYPE(dev->id->type), dev->id->num,
1261                                 dev->id->mtd_id, dev->num_parts);
1262                 printf(" #: name\t\t\tsize\t\toffset\t\tmask_flags\n");
1263
1264                 /* list partitions for given device */
1265                 part_num = 0;
1266                 list_for_each(pentry, &dev->parts) {
1267                         part = list_entry(pentry, struct part_info, link);
1268                         printf(" %d: %-22s\t0x%08x\t0x%08x\t%d\n",
1269                                         part_num, part->name, part->size,
1270                                         part->offset, part->mask_flags);
1271
1272                         part_num++;
1273                 }
1274         }
1275         if (list_empty(&devices))
1276                 printf("no partitions defined\n");
1277
1278         /* current_dev is not NULL only when we have non empty device list */
1279         if (current_dev) {
1280                 part = jffs2_part_info(current_dev, current_partnum);
1281                 if (part) {
1282                         printf("\nactive partition: %s%d,%d - (%s) 0x%08lx @ 0x%08lx\n",
1283                                         MTD_DEV_TYPE(current_dev->id->type),
1284                                         current_dev->id->num, current_partnum,
1285                                         part->name, part->size, part->offset);
1286                 } else {
1287                         printf("could not get current partition info\n\n");
1288                 }
1289         }
1290
1291         printf("\ndefaults:\n");
1292         printf("mtdids  : %s\n", mtdids_default);
1293         printf("mtdparts: %s\n", mtdparts_default);
1294 }
1295
1296 /**
1297  * Given partition identifier in form of <dev_type><dev_num>,<part_num> find
1298  * corresponding device and verify partition number.
1299  *
1300  * @param id string describing device and partition
1301  * @param dev pointer to the requested device (output)
1302  * @param part_num verified partition number (output)
1303  * @param part pointer to requested partition (output)
1304  * @return 0 on success, 1 otherwise
1305  */
1306 int find_dev_and_part(const char *id, struct mtd_device **dev,
1307                 u8 *part_num, struct part_info **part)
1308 {
1309         u8 type, dnum, pnum;
1310         const char *p;
1311
1312         DEBUGF("--- find_dev_and_part ---\nid = %s\n", id);
1313
1314         p = id;
1315         *dev = NULL;
1316         *part = NULL;
1317         *part_num = 0;
1318
1319         if (id_parse(p, &p, &type, &dnum) != 0)
1320                 return 1;
1321
1322         if ((*p++ != ',') || (*p == '\0')) {
1323                 printf("no partition number specified\n");
1324                 return 1;
1325         }
1326         pnum = simple_strtoul(p, (char **)&p, 0);
1327         if (*p != '\0') {
1328                 printf("unexpected trailing character '%c'\n", *p);
1329                 return 1;
1330         }
1331
1332         if ((*dev = device_find(type, dnum)) == NULL) {
1333                 printf("no such device %s%d\n", MTD_DEV_TYPE(type), dnum);
1334                 return 1;
1335         }
1336
1337         if ((*part = jffs2_part_info(*dev, pnum)) == NULL) {
1338                 printf("no such partition\n");
1339                 *dev = NULL;
1340                 return 1;
1341         }
1342
1343         *part_num = pnum;
1344
1345         return 0;
1346 }
1347
1348 /**
1349  * Find and delete partition. For partition id format see find_dev_and_part().
1350  *
1351  * @param id string describing device and partition
1352  * @return 0 on success, 1 otherwise
1353  */
1354 static int delete_partition(const char *id)
1355 {
1356         u8 pnum;
1357         struct mtd_device *dev;
1358         struct part_info *part;
1359
1360         if (find_dev_and_part(id, &dev, &pnum, &part) == 0) {
1361
1362                 DEBUGF("delete_partition: device = %s%d, partition %d = (%s) 0x%08lx@0x%08lx\n",
1363                                 MTD_DEV_TYPE(dev->id->type), dev->id->num, pnum,
1364                                 part->name, part->size, part->offset);
1365
1366                 if (part_del(dev, part) != 0)
1367                         return 1;
1368
1369                 if (generate_mtdparts_save(last_parts, MTDPARTS_MAXLEN) != 0) {
1370                         printf("generated mtdparts too long, reseting to null\n");
1371                         return 1;
1372                 }
1373                 return 0;
1374         }
1375
1376         printf("partition %s not found\n", id);
1377         return 1;
1378 }
1379
1380 /**
1381  * Accept character string describing mtd partitions and call device_parse()
1382  * for each entry. Add created devices to the global devices list.
1383  *
1384  * @param mtdparts string specifing mtd partitions
1385  * @return 0 on success, 1 otherwise
1386  */
1387 static int parse_mtdparts(const char *const mtdparts)
1388 {
1389         const char *p = mtdparts;
1390         struct mtd_device *dev;
1391         int err = 1;
1392
1393         DEBUGF("\n---parse_mtdparts---\nmtdparts = %s\n\n", p);
1394
1395         /* delete all devices and partitions */
1396         if (devices_init() != 0) {
1397                 printf("could not initialise device list\n");
1398                 return err;
1399         }
1400
1401         /* re-read 'mtdparts' variable, devices_init may be updating env */
1402         p = getenv("mtdparts");
1403
1404         if (strncmp(p, "mtdparts=", 9) != 0) {
1405                 printf("mtdparts variable doesn't start with 'mtdparts='\n");
1406                 return err;
1407         }
1408         p += 9;
1409
1410         while (p && (*p != '\0')) {
1411                 err = 1;
1412                 if ((device_parse(p, &p, &dev) != 0) || (!dev))
1413                         break;
1414
1415                 DEBUGF("+ device: %s\t%d\t%s\n", MTD_DEV_TYPE(dev->id->type),
1416                                 dev->id->num, dev->id->mtd_id);
1417
1418                 /* check if parsed device is already on the list */
1419                 if (device_find(dev->id->type, dev->id->num) != NULL) {
1420                         printf("device %s%d redefined, please correct mtdparts variable\n",
1421                                         MTD_DEV_TYPE(dev->id->type), dev->id->num);
1422                         break;
1423                 }
1424
1425                 list_add_tail(&dev->link, &devices);
1426                 err = 0;
1427         }
1428         if (err == 1) {
1429                 device_delall(&devices);
1430                 return 1;
1431         }
1432
1433         return 0;
1434 }
1435
1436 /**
1437  * Parse provided string describing mtdids mapping (see file header for mtdids
1438  * variable format). Allocate memory for each entry and add all found entries
1439  * to the global mtdids list.
1440  *
1441  * @param ids mapping string
1442  * @return 0 on success, 1 otherwise
1443  */
1444 static int parse_mtdids(const char *const ids)
1445 {
1446         const char *p = ids;
1447         const char *mtd_id;
1448         int mtd_id_len;
1449         struct mtdids *id;
1450         struct list_head *entry, *n;
1451         struct mtdids *id_tmp;
1452         u8 type, num;
1453         u32 size;
1454         int ret = 1;
1455
1456         DEBUGF("\n---parse_mtdids---\nmtdids = %s\n\n", ids);
1457
1458         /* clean global mtdids list */
1459         list_for_each_safe(entry, n, &mtdids) {
1460                 id_tmp = list_entry(entry, struct mtdids, link);
1461                 DEBUGF("mtdids del: %d %d\n", id_tmp->type, id_tmp->num);
1462                 list_del(entry);
1463                 free(id_tmp);
1464         }
1465         last_ids[0] = '\0';
1466         INIT_LIST_HEAD(&mtdids);
1467
1468         while(p && (*p != '\0')) {
1469
1470                 ret = 1;
1471                 /* parse 'nor'|'nand'<dev-num> */
1472                 if (id_parse(p, &p, &type, &num) != 0)
1473                         break;
1474
1475                 if (*p != '=') {
1476                         printf("mtdids: incorrect <dev-num>\n");
1477                         break;
1478                 }
1479                 p++;
1480
1481                 /* check if requested device exists */
1482                 if (device_validate(type, num, &size) != 0)
1483                         return 1;
1484
1485                 /* locate <mtd-id> */
1486                 mtd_id = p;
1487                 if ((p = strchr(mtd_id, ',')) != NULL) {
1488                         mtd_id_len = p - mtd_id + 1;
1489                         p++;
1490                 } else {
1491                         mtd_id_len = strlen(mtd_id) + 1;
1492                 }
1493                 if (mtd_id_len == 0) {
1494                         printf("mtdids: no <mtd-id> identifier\n");
1495                         break;
1496                 }
1497
1498                 /* check if this id is already on the list */
1499                 int double_entry = 0;
1500                 list_for_each(entry, &mtdids) {
1501                         id_tmp = list_entry(entry, struct mtdids, link);
1502                         if ((id_tmp->type == type) && (id_tmp->num == num)) {
1503                                 double_entry = 1;
1504                                 break;
1505                         }
1506                 }
1507                 if (double_entry) {
1508                         printf("device id %s%d redefined, please correct mtdids variable\n",
1509                                         MTD_DEV_TYPE(type), num);
1510                         break;
1511                 }
1512
1513                 /* allocate mtdids structure */
1514                 if (!(id = (struct mtdids *)malloc(sizeof(struct mtdids) + mtd_id_len))) {
1515                         printf("out of memory\n");
1516                         break;
1517                 }
1518                 memset(id, 0, sizeof(struct mtdids) + mtd_id_len);
1519                 id->num = num;
1520                 id->type = type;
1521                 id->size = size;
1522                 id->mtd_id = (char *)(id + 1);
1523                 strncpy(id->mtd_id, mtd_id, mtd_id_len - 1);
1524                 id->mtd_id[mtd_id_len - 1] = '\0';
1525                 INIT_LIST_HEAD(&id->link);
1526
1527                 DEBUGF("+ id %s%d\t%16d bytes\t%s\n",
1528                                 MTD_DEV_TYPE(id->type), id->num,
1529                                 id->size, id->mtd_id);
1530
1531                 list_add_tail(&id->link, &mtdids);
1532                 ret = 0;
1533         }
1534         if (ret == 1) {
1535                 /* clean mtdids list and free allocated memory */
1536                 list_for_each_safe(entry, n, &mtdids) {
1537                         id_tmp = list_entry(entry, struct mtdids, link);
1538                         list_del(entry);
1539                         free(id_tmp);
1540                 }
1541                 return 1;
1542         }
1543
1544         return 0;
1545 }
1546
1547 /**
1548  * Parse and initialize global mtdids mapping and create global
1549  * device/partition list.
1550  *
1551  * @return 0 on success, 1 otherwise
1552  */
1553 int mtdparts_init(void)
1554 {
1555         static int initialized = 0;
1556         const char *ids, *parts;
1557         const char *current_partition;
1558         int ids_changed;
1559         char tmp_ep[PARTITION_MAXLEN];
1560
1561         DEBUGF("\n---mtdparts_init---\n");
1562         if (!initialized) {
1563                 INIT_LIST_HEAD(&mtdids);
1564                 INIT_LIST_HEAD(&devices);
1565                 memset(last_ids, 0, MTDIDS_MAXLEN);
1566                 memset(last_parts, 0, MTDPARTS_MAXLEN);
1567                 memset(last_partition, 0, PARTITION_MAXLEN);
1568                 initialized = 1;
1569         }
1570
1571         /* get variables */
1572         ids = getenv("mtdids");
1573         parts = getenv("mtdparts");
1574         current_partition = getenv("partition");
1575
1576         /* save it for later parsing, cannot rely on current partition pointer
1577          * as 'partition' variable may be updated during init */
1578         tmp_ep[0] = '\0';
1579         if (current_partition)
1580                 strncpy(tmp_ep, current_partition, PARTITION_MAXLEN);
1581
1582         DEBUGF("last_ids  : %s\n", last_ids);
1583         DEBUGF("env_ids   : %s\n", ids);
1584         DEBUGF("last_parts: %s\n", last_parts);
1585         DEBUGF("env_parts : %s\n\n", parts);
1586
1587         DEBUGF("last_partition : %s\n", last_partition);
1588         DEBUGF("env_partition  : %s\n", current_partition);
1589
1590         /* if mtdids varible is empty try to use defaults */
1591         if (!ids) {
1592                 if (mtdids_default) {
1593                         DEBUGF("mtdids variable not defined, using default\n");
1594                         ids = mtdids_default;
1595                         setenv("mtdids", (char *)ids);
1596                 } else {
1597                         printf("mtdids not defined, no default present\n");
1598                         return 1;
1599                 }
1600         }
1601         if (strlen(ids) > MTDIDS_MAXLEN - 1) {
1602                 printf("mtdids too long (> %d)\n", MTDIDS_MAXLEN);
1603                 return 1;
1604         }
1605
1606         /* do no try to use defaults when mtdparts variable is not defined,
1607          * just check the length */
1608         if (!parts)
1609                 printf("mtdparts variable not set, see 'help mtdparts'\n");
1610
1611         if (parts && (strlen(parts) > MTDPARTS_MAXLEN - 1)) {
1612                 printf("mtdparts too long (> %d)\n", MTDPARTS_MAXLEN);
1613                 return 1;
1614         }
1615
1616         /* check if we have already parsed those mtdids */
1617         if ((last_ids[0] != '\0') && (strcmp(last_ids, ids) == 0)) {
1618                 ids_changed = 0;
1619         } else {
1620                 ids_changed = 1;
1621
1622                 if (parse_mtdids(ids) != 0) {
1623                         devices_init();
1624                         return 1;
1625                 }
1626
1627                 /* ok it's good, save new ids */
1628                 strncpy(last_ids, ids, MTDIDS_MAXLEN);
1629         }
1630
1631         /* parse partitions if either mtdparts or mtdids were updated */
1632         if (parts && ((last_parts[0] == '\0') || ((strcmp(last_parts, parts) != 0)) || ids_changed)) {
1633                 if (parse_mtdparts(parts) != 0)
1634                         return 1;
1635
1636                 if (list_empty(&devices)) {
1637                         printf("mtdparts_init: no valid partitions\n");
1638                         return 1;
1639                 }
1640
1641                 /* ok it's good, save new parts */
1642                 strncpy(last_parts, parts, MTDPARTS_MAXLEN);
1643
1644                 /* reset first partition from first dev from the list as current */
1645                 current_dev = list_entry(devices.next, struct mtd_device, link);
1646                 current_partnum = 0;
1647                 current_save();
1648
1649                 DEBUGF("mtdparts_init: current_dev  = %s%d, current_partnum = %d\n",
1650                                 MTD_DEV_TYPE(current_dev->id->type),
1651                                 current_dev->id->num, current_partnum);
1652         }
1653
1654         /* mtdparts variable was reset to NULL, delete all devices/partitions */
1655         if (!parts && (last_parts[0] != '\0'))
1656                 return devices_init();
1657
1658         /* do not process current partition if mtdparts variable is null */
1659         if (!parts)
1660                 return 0;
1661
1662         /* is current partition set in environment? if so, use it */
1663         if ((tmp_ep[0] != '\0') && (strcmp(tmp_ep, last_partition) != 0)) {
1664                 struct part_info *p;
1665                 struct mtd_device *cdev;
1666                 u8 pnum;
1667
1668                 DEBUGF("--- getting current partition: %s\n", tmp_ep);
1669
1670                 if (find_dev_and_part(tmp_ep, &cdev, &pnum, &p) == 0) {
1671                         current_dev = cdev;
1672                         current_partnum = pnum;
1673                         current_save();
1674                 }
1675         } else if (getenv("partition") == NULL) {
1676                 DEBUGF("no partition variable set, setting...\n");
1677                 current_save();
1678         }
1679
1680         return 0;
1681 }
1682 #else /* #ifdef CONFIG_JFFS2_CMDLINE */
1683 /*
1684  * 'Static' version of command line mtdparts_init() routine. Single partition on
1685  * a single device configuration.
1686  */
1687
1688 /**
1689  * Parse and initialize global mtdids mapping and create global
1690  * device/partition list.
1691  *
1692  * @return 0 on success, 1 otherwise
1693  */
1694 int mtdparts_init(void)
1695 {
1696         static int initialized = 0;
1697         u32 size;
1698         char *dev_name;
1699
1700         DEBUGF("\n---mtdparts_init---\n");
1701         if (!initialized) {
1702                 struct mtdids *id;
1703                 struct part_info *part;
1704
1705                 initialized = 1;
1706                 current_dev = (struct mtd_device *)
1707                         malloc(sizeof(struct mtd_device) +
1708                                         sizeof(struct part_info) +
1709                                         sizeof(struct mtdids));
1710                 if (!current_dev) {
1711                         printf("out of memory\n");
1712                         return 1;
1713                 }
1714                 memset(current_dev, 0, sizeof(struct mtd_device) +
1715                                         sizeof(struct part_info) + sizeof(struct mtdids));
1716
1717                 id = (struct mtdids *)(current_dev + 1);
1718                 part = (struct part_info *)(id + 1);
1719
1720                 /* id */
1721                 id->mtd_id = "single part";
1722
1723 #if defined(CONFIG_JFFS2_DEV)
1724                 dev_name = CONFIG_JFFS2_DEV;
1725 #else
1726                 dev_name = "nor0";
1727 #endif
1728
1729                 if ((id_parse(dev_name, NULL, &id->type, &id->num) != 0) ||
1730                                 (device_validate(id->type, id->num, &size) != 0)) {
1731                         printf("incorrect device: %s%d\n", MTD_DEV_TYPE(id->type), id->num);
1732                         free(current_dev);
1733                         return 1;
1734                 }
1735                 id->size = size;
1736                 INIT_LIST_HEAD(&id->link);
1737
1738                 DEBUGF("dev id: type = %d, num = %d, size = 0x%08lx, mtd_id = %s\n",
1739                                 id->type, id->num, id->size, id->mtd_id);
1740
1741                 /* partition */
1742                 part->name = "static";
1743                 part->auto_name = 0;
1744
1745 #if defined(CONFIG_JFFS2_PART_SIZE)
1746                 part->size = CONFIG_JFFS2_PART_SIZE;
1747 #else
1748                 part->size = SIZE_REMAINING;
1749 #endif
1750
1751 #if defined(CONFIG_JFFS2_PART_OFFSET)
1752                 part->offset = CONFIG_JFFS2_PART_OFFSET;
1753 #else
1754                 part->offset = 0x00000000;
1755 #endif
1756
1757                 part->dev = current_dev;
1758                 INIT_LIST_HEAD(&part->link);
1759
1760                 /* recalculate size if needed */
1761                 if (part->size == SIZE_REMAINING)
1762                         part->size = id->size - part->offset;
1763
1764                 DEBUGF("part  : name = %s, size = 0x%08lx, offset = 0x%08lx\n",
1765                                 part->name, part->size, part->offset);
1766
1767                 /* device */
1768                 current_dev->id = id;
1769                 INIT_LIST_HEAD(&current_dev->link);
1770                 current_dev->num_parts = 1;
1771                 INIT_LIST_HEAD(&current_dev->parts);
1772                 list_add(&part->link, &current_dev->parts);
1773         }
1774
1775         return 0;
1776 }
1777 #endif /* #ifdef CONFIG_JFFS2_CMDLINE */
1778
1779 /**
1780  * Return pointer to the partition of a requested number from a requested
1781  * device.
1782  *
1783  * @param dev device that is to be searched for a partition
1784  * @param part_num requested partition number
1785  * @return pointer to the part_info, NULL otherwise
1786  */
1787 static struct part_info* jffs2_part_info(struct mtd_device *dev, unsigned int part_num)
1788 {
1789         struct list_head *entry;
1790         struct part_info *part;
1791         int num;
1792
1793         if (!dev)
1794                 return NULL;
1795
1796         DEBUGF("\n--- jffs2_part_info: partition number %d for device %s%d (%s)\n",
1797                         part_num, MTD_DEV_TYPE(dev->id->type),
1798                         dev->id->num, dev->id->mtd_id);
1799
1800         if (part_num >= dev->num_parts) {
1801                 printf("invalid partition number %d for device %s%d (%s)\n",
1802                                 part_num, MTD_DEV_TYPE(dev->id->type),
1803                                 dev->id->num, dev->id->mtd_id);
1804                 return NULL;
1805         }
1806
1807         /* locate partition number, return it */
1808         num = 0;
1809         list_for_each(entry, &dev->parts) {
1810                 part = list_entry(entry, struct part_info, link);
1811
1812                 if (part_num == num++) {
1813                         return part;
1814                 }
1815         }
1816
1817         return NULL;
1818 }
1819
1820 /***************************************************/
1821 /* U-boot commands                                 */
1822 /***************************************************/
1823
1824 /**
1825  * Routine implementing fsload u-boot command. This routine tries to load
1826  * a requested file from jffs2/cramfs filesystem on a current partition.
1827  *
1828  * @param cmdtp command internal data
1829  * @param flag command flag
1830  * @param argc number of arguments supplied to the command
1831  * @param argv arguments list
1832  * @return 0 on success, 1 otherwise
1833  */
1834 int do_jffs2_fsload(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
1835 {
1836         char *fsname;
1837         char *filename;
1838         int size;
1839         struct part_info *part;
1840         ulong offset = load_addr;
1841
1842         /* pre-set Boot file name */
1843         if ((filename = getenv("bootfile")) == NULL) {
1844                 filename = "uImage";
1845         }
1846
1847         if (argc == 2) {
1848                 filename = argv[1];
1849         }
1850         if (argc == 3) {
1851                 offset = simple_strtoul(argv[1], NULL, 16);
1852                 load_addr = offset;
1853                 filename = argv[2];
1854         }
1855
1856         /* make sure we are in sync with env variables */
1857         if (mtdparts_init() !=0)
1858                 return 1;
1859
1860         if ((part = jffs2_part_info(current_dev, current_partnum))){
1861
1862                 /* check partition type for cramfs */
1863                 fsname = (cramfs_check(part) ? "CRAMFS" : "JFFS2");
1864                 printf("### %s loading '%s' to 0x%lx\n", fsname, filename, offset);
1865
1866                 if (cramfs_check(part)) {
1867                         size = cramfs_load ((char *) offset, part, filename);
1868                 } else {
1869                         /* if this is not cramfs assume jffs2 */
1870                         size = jffs2_1pass_load((char *)offset, part, filename);
1871                 }
1872
1873                 if (size > 0) {
1874                         char buf[10];
1875                         printf("### %s load complete: %d bytes loaded to 0x%lx\n",
1876                                 fsname, size, offset);
1877                         sprintf(buf, "%x", size);
1878                         setenv("filesize", buf);
1879                 } else {
1880                         printf("### %s LOAD ERROR<%x> for %s!\n", fsname, size, filename);
1881                 }
1882
1883                 return !(size > 0);
1884         }
1885         return 1;
1886 }
1887
1888 /**
1889  * Routine implementing u-boot ls command which lists content of a given
1890  * directory on a current partition.
1891  *
1892  * @param cmdtp command internal data
1893  * @param flag command flag
1894  * @param argc number of arguments supplied to the command
1895  * @param argv arguments list
1896  * @return 0 on success, 1 otherwise
1897  */
1898 int do_jffs2_ls(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
1899 {
1900         char *filename = "/";
1901         int ret;
1902         struct part_info *part;
1903
1904         if (argc == 2)
1905                 filename = argv[1];
1906
1907         /* make sure we are in sync with env variables */
1908         if (mtdparts_init() !=0)
1909                 return 1;
1910
1911         if ((part = jffs2_part_info(current_dev, current_partnum))){
1912
1913                 /* check partition type for cramfs */
1914                 if (cramfs_check(part)) {
1915                         ret = cramfs_ls (part, filename);
1916                 } else {
1917                         /* if this is not cramfs assume jffs2 */
1918                         ret = jffs2_1pass_ls(part, filename);
1919                 }
1920
1921                 return ret ? 0 : 1;
1922         }
1923         return 1;
1924 }
1925
1926 /**
1927  * Routine implementing u-boot fsinfo command. This routine prints out
1928  * miscellaneous filesystem informations/statistics.
1929  *
1930  * @param cmdtp command internal data
1931  * @param flag command flag
1932  * @param argc number of arguments supplied to the command
1933  * @param argv arguments list
1934  * @return 0 on success, 1 otherwise
1935  */
1936 int do_jffs2_fsinfo(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
1937 {
1938         struct part_info *part;
1939         char *fsname;
1940         int ret;
1941
1942         /* make sure we are in sync with env variables */
1943         if (mtdparts_init() !=0)
1944                 return 1;
1945
1946         if ((part = jffs2_part_info(current_dev, current_partnum))){
1947
1948                 /* check partition type for cramfs */
1949                 fsname = (cramfs_check(part) ? "CRAMFS" : "JFFS2");
1950                 printf("### filesystem type is %s\n", fsname);
1951
1952                 if (cramfs_check(part)) {
1953                         ret = cramfs_info (part);
1954                 } else {
1955                         /* if this is not cramfs assume jffs2 */
1956                         ret = jffs2_1pass_info(part);
1957                 }
1958
1959                 return ret ? 0 : 1;
1960         }
1961         return 1;
1962 }
1963
1964 /* command line only */
1965 #ifdef CONFIG_JFFS2_CMDLINE
1966 /**
1967  * Routine implementing u-boot chpart command. Sets new current partition based
1968  * on the user supplied partition id. For partition id format see find_dev_and_part().
1969  *
1970  * @param cmdtp command internal data
1971  * @param flag command flag
1972  * @param argc number of arguments supplied to the command
1973  * @param argv arguments list
1974  * @return 0 on success, 1 otherwise
1975  */
1976 int do_jffs2_chpart(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
1977 {
1978 /* command line only */
1979         struct mtd_device *dev;
1980         struct part_info *part;
1981         u8 pnum;
1982
1983         if (mtdparts_init() !=0)
1984                 return 1;
1985
1986         if (argc < 2) {
1987                 printf("no partition id specified\n");
1988                 return 1;
1989         }
1990
1991         if (find_dev_and_part(argv[1], &dev, &pnum, &part) != 0)
1992                 return 1;
1993
1994         current_dev = dev;
1995         current_partnum = pnum;
1996         current_save();
1997
1998         printf("partition changed to %s%d,%d\n",
1999                         MTD_DEV_TYPE(dev->id->type), dev->id->num, pnum);
2000
2001         return 0;
2002 }
2003
2004 /**
2005  * Routine implementing u-boot mtdparts command. Initialize/update default global
2006  * partition list and process user partition request (list, add, del).
2007  *
2008  * @param cmdtp command internal data
2009  * @param flag command flag
2010  * @param argc number of arguments supplied to the command
2011  * @param argv arguments list
2012  * @return 0 on success, 1 otherwise
2013  */
2014 int do_jffs2_mtdparts(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
2015 {
2016         if (argc == 2) {
2017                 if (strcmp(argv[1], "default") == 0) {
2018                         setenv("mtdids", (char *)mtdids_default);
2019                         setenv("mtdparts", (char *)mtdparts_default);
2020                         setenv("partition", NULL);
2021
2022                         mtdparts_init();
2023                         return 0;
2024                 } else if (strcmp(argv[1], "delall") == 0) {
2025                         /* this may be the first run, initialize lists if needed */
2026                         mtdparts_init();
2027
2028                         setenv("mtdparts", NULL);
2029
2030                         /* devices_init() calls current_save() */
2031                         return devices_init();
2032                 }
2033         }
2034
2035         /* make sure we are in sync with env variables */
2036         if (mtdparts_init() != 0)
2037                 return 1;
2038
2039         if (argc == 1) {
2040                 list_partitions();
2041                 return 0;
2042         }
2043
2044         /* mtdparts add <mtd-dev> <size>[@<offset>] <name> [ro] */
2045         if (((argc == 5) || (argc == 6)) && (strcmp(argv[1], "add") == 0)) {
2046 #define PART_ADD_DESC_MAXLEN 64
2047                 char tmpbuf[PART_ADD_DESC_MAXLEN];
2048                 u8 type, num, len;
2049                 struct mtd_device *dev;
2050                 struct mtd_device *dev_tmp;
2051                 struct mtdids *id;
2052                 struct part_info *p;
2053
2054                 if (id_parse(argv[2], NULL, &type, &num) != 0)
2055                         return 1;
2056
2057                 if ((id = id_find(type, num)) == NULL) {
2058                         printf("no such device %s defined in mtdids variable\n", argv[2]);
2059                         return 1;
2060                 }
2061
2062                 len = strlen(id->mtd_id) + 1;   /* 'mtd_id:' */
2063                 len += strlen(argv[3]);         /* size@offset */
2064                 len += strlen(argv[4]) + 2;     /* '(' name ')' */
2065                 if (argv[5] && (strlen(argv[5]) == 2))
2066                         len += 2;               /* 'ro' */
2067
2068                 if (len >= PART_ADD_DESC_MAXLEN) {
2069                         printf("too long partition description\n");
2070                         return 1;
2071                 }
2072                 sprintf(tmpbuf, "%s:%s(%s)%s",
2073                                 id->mtd_id, argv[3], argv[4], argv[5] ? argv[5] : "");
2074                 DEBUGF("add tmpbuf: %s\n", tmpbuf);
2075
2076                 if ((device_parse(tmpbuf, NULL, &dev) != 0) || (!dev))
2077                         return 1;
2078
2079                 DEBUGF("+ %s\t%d\t%s\n", MTD_DEV_TYPE(dev->id->type),
2080                                 dev->id->num, dev->id->mtd_id);
2081
2082                 if ((dev_tmp = device_find(dev->id->type, dev->id->num)) == NULL) {
2083                         device_add(dev);
2084                 } else {
2085                         /* merge new partition with existing ones*/
2086                         p = list_entry(dev->parts.next, struct part_info, link);
2087                         if (part_add(dev_tmp, p) != 0) {
2088                                 device_del(dev);
2089                                 return 1;
2090                         }
2091                 }
2092
2093                 if (generate_mtdparts_save(last_parts, MTDPARTS_MAXLEN) != 0) {
2094                         printf("generated mtdparts too long, reseting to null\n");
2095                         return 1;
2096                 }
2097
2098                 return 0;
2099         }
2100
2101         /* mtdparts del part-id */
2102         if ((argc == 3) && (strcmp(argv[1], "del") == 0)) {
2103                 DEBUGF("del: part-id = %s\n", argv[2]);
2104
2105                 return delete_partition(argv[2]);
2106         }
2107
2108         printf ("Usage:\n%s\n", cmdtp->usage);
2109         return 1;
2110 }
2111 #endif /* #ifdef CONFIG_JFFS2_CMDLINE */
2112
2113 /***************************************************/
2114 U_BOOT_CMD(
2115         fsload, 3,      0,      do_jffs2_fsload,
2116         "fsload\t- load binary file from a filesystem image\n",
2117         "[ off ] [ filename ]\n"
2118         "    - load binary file from flash bank\n"
2119         "      with offset 'off'\n"
2120 );
2121 U_BOOT_CMD(
2122         ls,     2,      1,      do_jffs2_ls,
2123         "ls\t- list files in a directory (default /)\n",
2124         "[ directory ]\n"
2125         "    - list files in a directory.\n"
2126 );
2127
2128 U_BOOT_CMD(
2129         fsinfo, 1,      1,      do_jffs2_fsinfo,
2130         "fsinfo\t- print information about filesystems\n",
2131         "    - print information about filesystems\n"
2132 );
2133
2134 #ifdef CONFIG_JFFS2_CMDLINE
2135 U_BOOT_CMD(
2136         chpart, 2,      0,      do_jffs2_chpart,
2137         "chpart\t- change active partition\n",
2138         "part-id\n"
2139         "    - change active partition (e.g. part-id = nand0,1)\n"
2140 );
2141
2142 U_BOOT_CMD(
2143         mtdparts,       6,      0,      do_jffs2_mtdparts,
2144         "mtdparts- define flash/nand partitions\n",
2145         "\n"
2146         "    - list partition table\n"
2147         "mtdparts delall\n"
2148         "    - delete all partitions\n"
2149         "mtdparts del part-id\n"
2150         "    - delete partition (e.g. part-id = nand0,1)\n"
2151         "mtdparts add <mtd-dev> <size>[@<offset>] [<name>] [ro]\n"
2152         "    - add partition\n"
2153         "mtdparts default\n"
2154         "    - reset partition table to defaults\n\n"
2155         "-----\n\n"
2156         "this command uses three environment variables:\n\n"
2157         "'partition' - keeps current partition identifier\n\n"
2158         "partition  := <part-id>\n"
2159         "<part-id>  := <dev-id>,part_num\n\n"
2160         "'mtdids' - linux kernel mtd device id <-> u-boot device id mapping\n\n"
2161         "mtdids=<idmap>[,<idmap>,...]\n\n"
2162         "<idmap>    := <dev-id>=<mtd-id>\n"
2163         "<dev-id>   := 'nand'|'nor'<dev-num>\n"
2164         "<dev-num>  := mtd device number, 0...\n"
2165         "<mtd-id>   := unique device tag used by linux kernel to find mtd device (mtd->name)\n\n"
2166         "'mtdparts' - partition list\n\n"
2167         "mtdparts=mtdparts=<mtd-def>[;<mtd-def>...]\n\n"
2168         "<mtd-def>  := <mtd-id>:<part-def>[,<part-def>...]\n"
2169         "<mtd-id>   := unique device tag used by linux kernel to find mtd device (mtd->name)\n"
2170         "<part-def> := <size>[@<offset>][<name>][<ro-flag>]\n"
2171         "<size>     := standard linux memsize OR '-' to denote all remaining space\n"
2172         "<offset>   := partition start offset within the device\n"
2173         "<name>     := '(' NAME ')'\n"
2174         "<ro-flag>  := when set to 'ro' makes partition read-only (not used, passed to kernel)\n"
2175 );
2176 #endif /* #ifdef CONFIG_JFFS2_CMDLINE */
2177
2178 /***************************************************/
2179
2180 #endif /* CFG_CMD_JFFS2 */