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