Newer
Older
/*
* (C) Copyright 2002
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
* (C) Copyright 2002
* Robert Schwebel, Pengutronix, <r.schwebel@pengutronix.de>
* (C) Copyright 2003
* Kai-Uwe Bloem, Auerswald GmbH & Co KG, <linux-development@auerswald.de>
* (C) Copyright 2005
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
*
* Added support for reading flash partition table from environment.
* Parsing routines are based on driver/mtd/cmdline.c from the linux 2.4
* kernel tree.
*
* $Id: cmdlinepart.c,v 1.17 2004/11/26 11:18:47 lavinen Exp $
* Copyright 2002 SYSGO Real-Time Solutions GmbH
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
/*
* Three environment variables are used by the parsing routines:
*
* 'partition' - keeps current partition identifier
*
* partition := <part-id>
* <part-id> := <dev-id>,part_num
*
* 'mtdids' - linux kernel mtd device id <-> u-boot device id mapping
*
* mtdids=<idmap>[,<idmap>,...]
*
* <idmap> := <dev-id>=<mtd-id>
* <dev-id> := 'nand'|'nor'|'onenand'<dev-num>
* <dev-num> := mtd device number, 0...
* <mtd-id> := unique device tag used by linux kernel to find mtd device (mtd->name)
*
*
* 'mtdparts' - partition list
*
* mtdparts=mtdparts=<mtd-def>[;<mtd-def>...]
*
* <mtd-def> := <mtd-id>:<part-def>[,<part-def>...]
* <mtd-id> := unique device tag used by linux kernel to find mtd device (mtd->name)
* <part-def> := <size>[@<offset>][<name>][<ro-flag>]
* <size> := standard linux memsize OR '-' to denote all remaining space
* <offset> := partition start offset within the device
* <name> := '(' NAME ')'
* <ro-flag> := when set to 'ro' makes partition read-only (not used, passed to kernel)
*
* Notes:
* - each <mtd-id> used in mtdparts must albo exist in 'mtddis' mapping
* - if the above variables are not set defaults for a given target are used
*
* Examples:
*
* 1 NOR Flash, with 1 single writable partition:
* mtdids=nor0=edb7312-nor
* mtdparts=mtdparts=edb7312-nor:-
*
* 1 NOR Flash with 2 partitions, 1 NAND with one
* mtdids=nor0=edb7312-nor,nand0=edb7312-nand
* mtdparts=mtdparts=edb7312-nor:256k(ARMboot)ro,-(root);edb7312-nand:-(home)
*
*/
/*
*/
#include <common.h>
#include <command.h>
#include <malloc.h>
#include <jffs2/jffs2.h>
#include <linux/list.h>
#include <linux/ctype.h>
#include <cramfs/cramfs_fs.h>
#if defined(CONFIG_CMD_NAND)
Jean-Christophe PLAGNIOL-VILLARD
committed
#ifdef CONFIG_NAND_LEGACY
Bartlomiej Sieka
committed
#include <linux/mtd/nand_legacy.h>
Jean-Christophe PLAGNIOL-VILLARD
committed
#else /* !CONFIG_NAND_LEGACY */
Bartlomiej Sieka
committed
#include <linux/mtd/nand.h>
Jean-Christophe PLAGNIOL-VILLARD
committed
#endif /* !CONFIG_NAND_LEGACY */
#if defined(CONFIG_CMD_ONENAND)
#include <linux/mtd/mtd.h>
#include <linux/mtd/onenand.h>
#include <onenand_uboot.h>
#endif
/* enable/disable debugging messages */
#define DEBUG_JFFS
#undef DEBUG_JFFS
# define DEBUGF(fmt, args...) printf(fmt ,##args)
#else
# define DEBUGF(fmt, args...)
#endif
/* special size referring to all the remaining space in a partition */
#define SIZE_REMAINING 0xFFFFFFFF
/* special offset value, it is used when not provided by user
*
* this value is used temporarily during parsing, later such offests
* are recalculated */
#define OFFSET_NOT_SPECIFIED 0xFFFFFFFF
/* minimum partition size */
#define MIN_PART_SIZE 4096
/* this flag needs to be set in part_info struct mask_flags
* field for read-only partitions */
#define MTD_WRITEABLE_CMD 1
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
#ifdef CONFIG_JFFS2_CMDLINE
/* default values for mtdids and mtdparts variables */
#if defined(MTDIDS_DEFAULT)
static const char *const mtdids_default = MTDIDS_DEFAULT;
#else
#warning "MTDIDS_DEFAULT not defined!"
static const char *const mtdids_default = NULL;
#endif
#if defined(MTDPARTS_DEFAULT)
static const char *const mtdparts_default = MTDPARTS_DEFAULT;
#else
#warning "MTDPARTS_DEFAULT not defined!"
static const char *const mtdparts_default = NULL;
#endif
/* copies of last seen 'mtdids', 'mtdparts' and 'partition' env variables */
#define MTDIDS_MAXLEN 128
#define MTDPARTS_MAXLEN 512
#define PARTITION_MAXLEN 16
static char last_ids[MTDIDS_MAXLEN];
static char last_parts[MTDPARTS_MAXLEN];
static char last_partition[PARTITION_MAXLEN];
/* low level jffs2 cache cleaning routine */
extern void jffs2_free_cache(struct part_info *part);
/* mtdids mapping list, filled by parse_ids() */
struct list_head mtdids;
/* device/partition list, parse_cmdline() parses into here */
struct list_head devices;
#endif /* #ifdef CONFIG_JFFS2_CMDLINE */
/* current active device and partition number */
static struct mtd_device *current_dev = NULL;
static u8 current_partnum = 0;
#if defined(CONFIG_CMD_CRAMFS)
extern int cramfs_check (struct part_info *info);
extern int cramfs_load (char *loadoffset, struct part_info *info, char *filename);
extern int cramfs_ls (struct part_info *info, char *filename);
extern int cramfs_info (struct part_info *info);
#else
/* defining empty macros for function names is ugly but avoids ifdef clutter
* all over the code */
#define cramfs_check(x) (0)
#define cramfs_load(x,y,z) (-1)
#define cramfs_ls(x,y) (0)
#define cramfs_info(x) (0)
#endif
static struct part_info* jffs2_part_info(struct mtd_device *dev, unsigned int part_num);
/* command line only routines */
#ifdef CONFIG_JFFS2_CMDLINE
static struct mtdids* id_find_by_mtd_id(const char *mtd_id, unsigned int mtd_id_len);
static int device_del(struct mtd_device *dev);
/**
* Parses a string into a number. The number stored at ptr is
* potentially suffixed with K (for kilobytes, or 1024 bytes),
* M (for megabytes, or 1048576 bytes), or G (for gigabytes, or
* 1073741824). If the number is suffixed with K, M, or G, then
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
* the return value is the number multiplied by one kilobyte, one
* megabyte, or one gigabyte, respectively.
*
* @param ptr where parse begins
* @param retptr output pointer to next char after parse completes (output)
* @return resulting unsigned int
*/
static unsigned long memsize_parse (const char *const ptr, const char **retptr)
{
unsigned long ret = simple_strtoul(ptr, (char **)retptr, 0);
switch (**retptr) {
case 'G':
case 'g':
ret <<= 10;
case 'M':
case 'm':
ret <<= 10;
case 'K':
case 'k':
ret <<= 10;
(*retptr)++;
default:
break;
}
return ret;
}
/**
* Format string describing supplied size. This routine does the opposite job
* to memsize_parse(). Size in bytes is converted to string and if possible
* shortened by using k (kilobytes), m (megabytes) or g (gigabytes) suffix.
*
* Note, that this routine does not check for buffer overflow, it's the caller
* who must assure enough space.
*
* @param buf output buffer
* @param size size to be converted to string
*/
static void memsize_format(char *buf, u32 size)
{
#define SIZE_GB ((u32)1024*1024*1024)
#define SIZE_MB ((u32)1024*1024)
#define SIZE_KB ((u32)1024)
if ((size % SIZE_GB) == 0)
sprintf(buf, "%ug", size/SIZE_GB);
else if ((size % SIZE_MB) == 0)
sprintf(buf, "%um", size/SIZE_MB);
else if (size % SIZE_KB == 0)
sprintf(buf, "%uk", size/SIZE_KB);
else
sprintf(buf, "%u", size);
}
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
/**
* This routine does global indexing of all partitions. Resulting index for
* current partition is saved in 'mtddevnum'. Current partition name in
* 'mtddevname'.
*/
static void index_partitions(void)
{
char buf[16];
u16 mtddevnum;
struct part_info *part;
struct list_head *dentry;
struct mtd_device *dev;
DEBUGF("--- index partitions ---\n");
if (current_dev) {
mtddevnum = 0;
list_for_each(dentry, &devices) {
dev = list_entry(dentry, struct mtd_device, link);
if (dev == current_dev) {
mtddevnum += current_partnum;
sprintf(buf, "%d", mtddevnum);
setenv("mtddevnum", buf);
break;
}
mtddevnum += dev->num_parts;
}
part = jffs2_part_info(current_dev, current_partnum);
setenv("mtddevname", part->name);
DEBUGF("=> mtddevnum %d,\n=> mtddevname %s\n", mtddevnum, part->name);
} else {
setenv("mtddevnum", NULL);
setenv("mtddevname", NULL);
DEBUGF("=> mtddevnum NULL\n=> mtddevname NULL\n");
}
}
/**
* Save current device and partition in environment variable 'partition'.
*/
static void current_save(void)
{
char buf[16];
DEBUGF("--- current_save ---\n");
if (current_dev) {
sprintf(buf, "%s%d,%d", MTD_DEV_TYPE(current_dev->id->type),
current_dev->id->num, current_partnum);
setenv("partition", buf);
strncpy(last_partition, buf, 16);
DEBUGF("=> partition %s\n", buf);
} else {
setenv("partition", NULL);
last_partition[0] = '\0';
DEBUGF("=> partition NULL\n");
}
index_partitions();
}
/**
* Performs sanity check for supplied NOR flash partition. Table of existing
* NOR flash devices is searched and partition device is located. Alignment
* with the granularity of NOR flash sectors is verified.
*
* @param id of the parent device
* @param part partition to validate
* @return 0 if partition is valid, 1 otherwise
*/
static int part_validate_nor(struct mtdids *id, struct part_info *part)
{
#if defined(CONFIG_CMD_FLASH)
/* info for FLASH chips */
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
flash_info_t *flash;
int offset_aligned;
u32 end_offset;
int i;
flash = &flash_info[id->num];
offset_aligned = 0;
for (i = 0; i < flash->sector_count; i++) {
if ((flash->start[i] - flash->start[0]) == part->offset) {
offset_aligned = 1;
break;
}
}
if (offset_aligned == 0) {
printf("%s%d: partition (%s) start offset alignment incorrect\n",
MTD_DEV_TYPE(id->type), id->num, part->name);
return 1;
}
end_offset = part->offset + part->size;
for (i = 0; i < flash->sector_count; i++) {
if ((flash->start[i] - flash->start[0]) == end_offset)
return 0;
}
if (flash->size == end_offset)
return 0;
printf("%s%d: partition (%s) size alignment incorrect\n",
MTD_DEV_TYPE(id->type), id->num, part->name);
#endif
return 1;
}
/**
* Performs sanity check for supplied NAND flash partition. Table of existing
* NAND flash devices is searched and partition device is located. Alignment
* with the granularity of nand erasesize is verified.
*
* @param id of the parent device
* @param part partition to validate
* @return 0 if partition is valid, 1 otherwise
*/
static int part_validate_nand(struct mtdids *id, struct part_info *part)
{
#if defined(CONFIG_JFFS2_NAND) && defined(CONFIG_CMD_NAND)
/* info for NAND chips */
if ((unsigned long)(part->offset) % nand->erasesize) {
printf("%s%d: partition (%s) start offset alignment incorrect\n",
MTD_DEV_TYPE(id->type), id->num, part->name);
return 1;
}
if (part->size % nand->erasesize) {
printf("%s%d: partition (%s) size alignment incorrect\n",
MTD_DEV_TYPE(id->type), id->num, part->name);
return 1;
}
return 0;
#else
return 1;
#endif
}
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
/**
* Performs sanity check for supplied OneNAND flash partition.
* Table of existing OneNAND flash devices is searched and partition device
* is located. Alignment with the granularity of nand erasesize is verified.
*
* @param id of the parent device
* @param part partition to validate
* @return 0 if partition is valid, 1 otherwise
*/
static int part_validate_onenand(struct mtdids *id, struct part_info *part)
{
#if defined(CONFIG_CMD_ONENAND)
/* info for OneNAND chips */
struct mtd_info *mtd;
mtd = &onenand_mtd;
if ((unsigned long)(part->offset) % mtd->erasesize) {
printf("%s%d: partition (%s) start offset"
"alignment incorrect\n",
MTD_DEV_TYPE(id->type), id->num, part->name);
return 1;
}
if (part->size % mtd->erasesize) {
printf("%s%d: partition (%s) size alignment incorrect\n",
MTD_DEV_TYPE(id->type), id->num, part->name);
return 1;
}
return 0;
#else
return 1;
#endif
}
/**
* Performs sanity check for supplied partition. Offset and size are verified
* to be within valid range. Partition type is checked and either
* parts_validate_nor() or parts_validate_nand() is called with the argument
* of part.
*
* @param id of the parent device
* @param part partition to validate
* @return 0 if partition is valid, 1 otherwise
*/
static int part_validate(struct mtdids *id, struct part_info *part)
{
if (part->size == SIZE_REMAINING)
part->size = id->size - part->offset;
if (part->offset > id->size) {
printf("%s: offset %08x beyond flash size %08x\n",
id->mtd_id, part->offset, id->size);
return 1;
}
if ((part->offset + part->size) <= part->offset) {
printf("%s%d: partition (%s) size too big\n",
MTD_DEV_TYPE(id->type), id->num, part->name);
return 1;
}
if (part->offset + part->size > id->size) {
printf("%s: partitioning exceeds flash size\n", id->mtd_id);
return 1;
}
if (id->type == MTD_DEV_TYPE_NAND)
return part_validate_nand(id, part);
else if (id->type == MTD_DEV_TYPE_NOR)
return part_validate_nor(id, part);
else if (id->type == MTD_DEV_TYPE_ONENAND)
return part_validate_onenand(id, part);
else
DEBUGF("part_validate: invalid dev type\n");
return 1;
}
/**
* Delete selected partition from the partion list of the specified device.
*
* @param dev device to delete partition from
* @param part partition to delete
* @return 0 on success, 1 otherwise
*/
static int part_del(struct mtd_device *dev, struct part_info *part)
{
u8 current_save_needed = 0;
/* if there is only one partition, remove whole device */
if (dev->num_parts == 1)
return device_del(dev);
/* otherwise just delete this partition */
if (dev == current_dev) {
/* we are modyfing partitions for the current device,
* update current */
struct part_info *curr_pi;
curr_pi = jffs2_part_info(current_dev, current_partnum);
if (curr_pi) {
if (curr_pi == part) {
printf("current partition deleted, resetting current to 0\n");
current_partnum = 0;
} else if (part->offset <= curr_pi->offset) {
current_save_needed = 1;
}
}
Jean-Christophe PLAGNIOL-VILLARD
committed
#ifdef CONFIG_NAND_LEGACY
jffs2_free_cache(part);
list_del(&part->link);
free(part);
dev->num_parts--;
if (current_save_needed > 0)
current_save();
else
index_partitions();
return 0;
}
/**
* Delete all partitions from parts head list, free memory.
*
* @param head list of partitions to delete
*/
static void part_delall(struct list_head *head)
{
struct list_head *entry, *n;
struct part_info *part_tmp;
/* clean tmp_list and free allocated memory */
list_for_each_safe(entry, n, head) {
part_tmp = list_entry(entry, struct part_info, link);
Jean-Christophe PLAGNIOL-VILLARD
committed
#ifdef CONFIG_NAND_LEGACY
jffs2_free_cache(part_tmp);
list_del(entry);
free(part_tmp);
}
}
/**
* Add new partition to the supplied partition list. Make sure partitions are
* sorted by offset in ascending order.
*
* @param head list this partition is to be added to
* @param new partition to be added
*/
static int part_sort_add(struct mtd_device *dev, struct part_info *part)
{
struct list_head *entry;
struct part_info *new_pi, *curr_pi;
/* link partition to parrent dev */
part->dev = dev;
if (list_empty(&dev->parts)) {
DEBUGF("part_sort_add: list empty\n");
list_add(&part->link, &dev->parts);
dev->num_parts++;
index_partitions();
return 0;
}
new_pi = list_entry(&part->link, struct part_info, link);
/* get current partition info if we are updating current device */
curr_pi = NULL;
if (dev == current_dev)
curr_pi = jffs2_part_info(current_dev, current_partnum);
list_for_each(entry, &dev->parts) {
struct part_info *pi;
pi = list_entry(entry, struct part_info, link);
/* be compliant with kernel cmdline, allow only one partition at offset zero */
if ((new_pi->offset == pi->offset) && (pi->offset == 0)) {
printf("cannot add second partition at offset 0\n");
return 1;
}
if (new_pi->offset <= pi->offset) {
list_add_tail(&part->link, entry);
dev->num_parts++;
if (curr_pi && (pi->offset <= curr_pi->offset)) {
/* we are modyfing partitions for the current
* device, update current */
current_partnum++;
current_save();
} else {
index_partitions();
}
return 0;
}
}
list_add_tail(&part->link, &dev->parts);
dev->num_parts++;
index_partitions();
return 0;
}
/**
* Add provided partition to the partition list of a given device.
*
* @param dev device to which partition is added
* @param part partition to be added
* @return 0 on success, 1 otherwise
*/
static int part_add(struct mtd_device *dev, struct part_info *part)
{
/* verify alignment and size */
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
if (part_validate(dev->id, part) != 0)
return 1;
/* partition is ok, add it to the list */
if (part_sort_add(dev, part) != 0)
return 1;
return 0;
}
/**
* Parse one partition definition, allocate memory and return pointer to this
* location in retpart.
*
* @param partdef pointer to the partition definition string i.e. <part-def>
* @param ret output pointer to next char after parse completes (output)
* @param retpart pointer to the allocated partition (output)
* @return 0 on success, 1 otherwise
*/
static int part_parse(const char *const partdef, const char **ret, struct part_info **retpart)
{
struct part_info *part;
unsigned long size;
unsigned long offset;
const char *name;
int name_len;
unsigned int mask_flags;
const char *p;
p = partdef;
*retpart = NULL;
*ret = NULL;
/* fetch the partition size */
if (*p == '-') {
/* assign all remaining space to this partition */
DEBUGF("'-': remaining size assigned\n");
size = SIZE_REMAINING;
p++;
} else {
size = memsize_parse(p, &p);
if (size < MIN_PART_SIZE) {
printf("partition size too small (%lx)\n", size);
return 1;
}
}
offset = OFFSET_NOT_SPECIFIED;
if (*p == '@') {
p++;
offset = memsize_parse(p, &p);
}
if (*p == '(') {
name = ++p;
if ((p = strchr(name, ')')) == NULL) {
printf("no closing ) found in partition name\n");
return 1;
}
name_len = p - name + 1;
if ((name_len - 1) == 0) {
printf("empty partition name\n");
return 1;
}
p++;
} else {
/* 0x00000000@0x00000000 */
name_len = 22;
name = NULL;
}
mask_flags = 0;
if (strncmp(p, "ro", 2) == 0) {
mask_flags |= MTD_WRITEABLE_CMD;
p += 2;
}
/* check for next partition definition */
if (*p == ',') {
if (size == SIZE_REMAINING) {
*ret = NULL;
printf("no partitions allowed after a fill-up partition\n");
return 1;
}
*ret = ++p;
} else if ((*p == ';') || (*p == '\0')) {
*ret = p;
} else {
printf("unexpected character '%c' at the end of partition\n", *p);
*ret = NULL;
return 1;
}
/* allocate memory */
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
part = (struct part_info *)malloc(sizeof(struct part_info) + name_len);
if (!part) {
printf("out of memory\n");
return 1;
}
memset(part, 0, sizeof(struct part_info) + name_len);
part->size = size;
part->offset = offset;
part->mask_flags = mask_flags;
part->name = (char *)(part + 1);
if (name) {
/* copy user provided name */
strncpy(part->name, name, name_len - 1);
part->auto_name = 0;
} else {
/* auto generated name in form of size@offset */
sprintf(part->name, "0x%08lx@0x%08lx", size, offset);
part->auto_name = 1;
}
part->name[name_len - 1] = '\0';
INIT_LIST_HEAD(&part->link);
DEBUGF("+ partition: name %-22s size 0x%08x offset 0x%08x mask flags %d\n",
part->name, part->size,
part->offset, part->mask_flags);
*retpart = part;
return 0;
}
#endif/* #ifdef CONFIG_JFFS2_CMDLINE */
/**
* Check device number to be within valid range for given device type.
*
* @param dev device to validate
* @return 0 if device is valid, 1 otherwise
*/
static int device_validate(u8 type, u8 num, u32 *size)
{
if (type == MTD_DEV_TYPE_NOR) {
#if defined(CONFIG_CMD_FLASH)
if (num < CONFIG_SYS_MAX_FLASH_BANKS) {
*size = flash_info[num].size;
return 0;
}
printf("no such FLASH device: %s%d (valid range 0 ... %d\n",
MTD_DEV_TYPE(type), num, CONFIG_SYS_MAX_FLASH_BANKS - 1);
#else
printf("support for FLASH devices not present\n");
#endif
} else if (type == MTD_DEV_TYPE_NAND) {
#if defined(CONFIG_JFFS2_NAND) && defined(CONFIG_CMD_NAND)
if (num < CONFIG_SYS_MAX_NAND_DEVICE) {
Jean-Christophe PLAGNIOL-VILLARD
committed
#ifndef CONFIG_NAND_LEGACY
extern struct nand_chip nand_dev_desc[CONFIG_SYS_MAX_NAND_DEVICE];
*size = nand_dev_desc[num].totlen;
return 0;
}
printf("no such NAND device: %s%d (valid range 0 ... %d)\n",
MTD_DEV_TYPE(type), num, CONFIG_SYS_MAX_NAND_DEVICE - 1);
#else
printf("support for NAND devices not present\n");
#endif
} else if (type == MTD_DEV_TYPE_ONENAND) {
#if defined(CONFIG_CMD_ONENAND)
*size = onenand_mtd.size;
return 0;
#else
printf("support for OneNAND devices not present\n");
#endif
} else
printf("Unknown defice type %d\n", type);
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
return 1;
}
#ifdef CONFIG_JFFS2_CMDLINE
/**
* Delete all mtd devices from a supplied devices list, free memory allocated for
* each device and delete all device partitions.
*
* @return 0 on success, 1 otherwise
*/
static int device_delall(struct list_head *head)
{
struct list_head *entry, *n;
struct mtd_device *dev_tmp;
/* clean devices list */
list_for_each_safe(entry, n, head) {
dev_tmp = list_entry(entry, struct mtd_device, link);
list_del(entry);
part_delall(&dev_tmp->parts);
free(dev_tmp);
}
INIT_LIST_HEAD(&devices);
return 0;
}
/**
* If provided device exists it's partitions are deleted, device is removed
* from device list and device memory is freed.
*
* @param dev device to be deleted
* @return 0 on success, 1 otherwise
*/
static int device_del(struct mtd_device *dev)
{
part_delall(&dev->parts);
list_del(&dev->link);
free(dev);
if (dev == current_dev) {
/* we just deleted current device */
if (list_empty(&devices)) {
current_dev = NULL;
} else {
/* reset first partition from first dev from the
* devices list as current */
current_dev = list_entry(devices.next, struct mtd_device, link);
current_partnum = 0;
}
current_save();
return 0;
}
index_partitions();
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
return 0;
}
/**
* Search global device list and return pointer to the device of type and num
* specified.
*
* @param type device type
* @param num device number
* @return NULL if requested device does not exist
*/
static struct mtd_device* device_find(u8 type, u8 num)
{
struct list_head *entry;
struct mtd_device *dev_tmp;
list_for_each(entry, &devices) {
dev_tmp = list_entry(entry, struct mtd_device, link);
if ((dev_tmp->id->type == type) && (dev_tmp->id->num == num))
return dev_tmp;
}
return NULL;
}
/**
* Add specified device to the global device list.
*
* @param dev device to be added
*/
static void device_add(struct mtd_device *dev)
{
u8 current_save_needed = 0;
if (list_empty(&devices)) {
current_dev = dev;
current_partnum = 0;
current_save_needed = 1;
}
list_add_tail(&dev->link, &devices);
if (current_save_needed > 0)
current_save();
else
index_partitions();
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
}
/**
* Parse device type, name and mtd-id. If syntax is ok allocate memory and
* return pointer to the device structure.
*
* @param mtd_dev pointer to the device definition string i.e. <mtd-dev>
* @param ret output pointer to next char after parse completes (output)
* @param retdev pointer to the allocated device (output)
* @return 0 on success, 1 otherwise
*/
static int device_parse(const char *const mtd_dev, const char **ret, struct mtd_device **retdev)
{
struct mtd_device *dev;
struct part_info *part;
struct mtdids *id;
const char *mtd_id;
unsigned int mtd_id_len;
const char *p, *pend;
LIST_HEAD(tmp_list);
struct list_head *entry, *n;
u16 num_parts;
u32 offset;
int err = 1;
p = mtd_dev;
*retdev = NULL;
*ret = NULL;
DEBUGF("===device_parse===\n");
/* fetch <mtd-id> */
mtd_id = p;
if (!(p = strchr(mtd_id, ':'))) {
printf("no <mtd-id> identifier\n");
return 1;
}
mtd_id_len = p - mtd_id + 1;
p++;
/* verify if we have a valid device specified */
if ((id = id_find_by_mtd_id(mtd_id, mtd_id_len - 1)) == NULL) {
printf("invalid mtd device '%.*s'\n", mtd_id_len - 1, mtd_id);
return 1;
}
DEBUGF("dev type = %d (%s), dev num = %d, mtd-id = %s\n",
id->type, MTD_DEV_TYPE(id->type),
id->num, id->mtd_id);
pend = strchr(p, ';');
DEBUGF("parsing partitions %.*s\n", (pend ? pend - p : strlen(p)), p);
/* parse partitions */
num_parts = 0;
offset = 0;
if ((dev = device_find(id->type, id->num)) != NULL) {
/* if device already exists start at the end of the last partition */
part = list_entry(dev->parts.prev, struct part_info, link);
offset = part->offset + part->size;
}
while (p && (*p != '\0') && (*p != ';')) {
err = 1;
if ((part_parse(p, &p, &part) != 0) || (!part))
break;
/* calculate offset when not specified */
if (part->offset == OFFSET_NOT_SPECIFIED)
part->offset = offset;
else
offset = part->offset;
/* verify alignment and size */
if (part_validate(id, part) != 0)
break;
offset += part->size;
/* partition is ok, add it to the list */
list_add_tail(&part->link, &tmp_list);
num_parts++;
err = 0;
}