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.
*
* (C) Copyright 2008
* Harald Welte, OpenMoko, Inc., Harald Welte <laforge@openmoko.org>
*
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
* $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 <linux/list.h>
#include <linux/ctype.h>
#include <linux/err.h>
#include <linux/mtd/mtd.h>
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
#if defined(CONFIG_CMD_NAND)
#include <linux/mtd/nand.h>
#include <nand.h>
#endif
#if defined(CONFIG_CMD_ONENAND)
#include <linux/mtd/onenand.h>
#include <onenand_uboot.h>
#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
/* default values for mtdids and mtdparts variables */
#if defined(MTDIDS_DEFAULT)
static const char *const mtdids_default = MTDIDS_DEFAULT;
#else
static const char *const mtdids_default = NULL;
#endif
#if defined(MTDPARTS_DEFAULT)
static const char *const mtdparts_default = MTDPARTS_DEFAULT;
#else
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() */
/* device/partition list, parse_cmdline() parses into here */
/* current active device and partition number */
struct mtd_device *current_mtd_dev = NULL;
u8 current_mtd_partnum = 0;
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
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
static struct part_info* mtd_part_info(struct mtd_device *dev, unsigned int part_num);
/* command line only routines */
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
* 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);
}
/**
* 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;
debug("--- index partitions ---\n");
if (current_mtd_dev) {
mtddevnum = 0;
list_for_each(dentry, &devices) {
dev = list_entry(dentry, struct mtd_device, link);
if (dev == current_mtd_dev) {
mtddevnum += current_mtd_partnum;
sprintf(buf, "%d", mtddevnum);
setenv("mtddevnum", buf);
break;
}
mtddevnum += dev->num_parts;
}
part = mtd_part_info(current_mtd_dev, current_mtd_partnum);
setenv("mtddevname", part->name);
debug("=> mtddevnum %d,\n=> mtddevname %s\n", mtddevnum, part->name);
} else {
setenv("mtddevnum", NULL);
setenv("mtddevname", NULL);
debug("=> mtddevnum NULL\n=> mtddevname NULL\n");
}
}
/**
* Save current device and partition in environment variable 'partition'.
*/
static void current_save(void)
{
char buf[16];
debug("--- current_save ---\n");
if (current_mtd_dev) {
sprintf(buf, "%s%d,%d", MTD_DEV_TYPE(current_mtd_dev->id->type),
current_mtd_dev->id->num, current_mtd_partnum);
setenv("partition", buf);
strncpy(last_partition, buf, 16);
debug("=> partition %s\n", buf);
} else {
setenv("partition", NULL);
last_partition[0] = '\0';
debug("=> partition NULL\n");
/**
* Produce a mtd_info given a type and num.
*
* @param type mtd type
* @param num mtd number
* @param mtd a pointer to an mtd_info instance (output)
* @return 0 if device is valid, 1 otherwise
*/
static int get_mtd_info(u8 type, u8 num, struct mtd_info **mtd)
{
char mtd_dev[16];
sprintf(mtd_dev, "%s%d", MTD_DEV_TYPE(type), num);
*mtd = get_mtd_device_nm(mtd_dev);
if (IS_ERR(*mtd)) {
printf("Device %s not found!\n", mtd_dev);
return 1;
}
return 0;
}
* Performs sanity check for supplied flash partition.
* Table of existing MTD 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_eraseblock(struct mtdids *id, struct part_info *part)
struct mtd_info *mtd = NULL;
int i, j;
ulong start;
if (get_mtd_info(id->type, id->num, &mtd))
part->sector_size = mtd->erasesize;
if (!mtd->numeraseregions) {
/*
* Only one eraseregion (NAND, OneNAND or uniform NOR),
* checking for alignment is easy here
*/
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;
}
} else {
/*
* Multiple eraseregions (non-uniform NOR),
* checking for alignment is more complex here
*/
/* Check start alignment */
for (i = 0; i < mtd->numeraseregions; i++) {
start = mtd->eraseregions[i].offset;
for (j = 0; j < mtd->eraseregions[i].numblocks; j++) {
if (part->offset == start)
goto start_ok;
start += mtd->eraseregions[i].erasesize;
}
}
printf("%s%d: partition (%s) start offset alignment incorrect\n",
MTD_DEV_TYPE(id->type), id->num, part->name);
start_ok:
/* Check end/size alignment */
for (i = 0; i < mtd->numeraseregions; i++) {
start = mtd->eraseregions[i].offset;
for (j = 0; j < mtd->eraseregions[i].numblocks; j++) {
if ((part->offset + part->size) == start)
goto end_ok;
start += mtd->eraseregions[i].erasesize;
}
}
/* Check last sector alignment */
if ((part->offset + part->size) == start)
goto end_ok;
printf("%s%d: partition (%s) size alignment incorrect\n",
MTD_DEV_TYPE(id->type), id->num, part->name);
end_ok:
return 0;
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
}
return 0;
}
/**
* 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;
}
/*
* Now we need to check if the partition starts and ends on
* sector (eraseblock) regions
*/
return part_validate_eraseblock(id, part);
}
/**
* 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_mtd_dev) {
/* we are modyfing partitions for the current device,
* update current */
struct part_info *curr_pi;
curr_pi = mtd_part_info(current_mtd_dev, current_mtd_partnum);
if (curr_pi) {
if (curr_pi == part) {
printf("current partition deleted, resetting current to 0\n");
current_mtd_partnum = 0;
} else if (part->offset <= curr_pi->offset) {
current_mtd_partnum--;
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
}
current_save_needed = 1;
}
}
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);
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)) {
debug("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_mtd_dev)
curr_pi = mtd_part_info(current_mtd_dev, current_mtd_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_mtd_partnum++;
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
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 */
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 */
debug("'-': remaining size assigned\n");
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
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
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
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;
}
}
/* check for offset */
offset = OFFSET_NOT_SPECIFIED;
if (*p == '@') {
p++;
offset = memsize_parse(p, &p);
}
/* now look for the name */
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;
}
/* test for options */
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 */
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);
debug("+ 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;
}
/**
* Check device number to be within valid range for given device type.
*
* @param type mtd type
* @param num mtd number
* @param size a pointer to the size of the mtd device (output)
* @return 0 if device is valid, 1 otherwise
*/
static int mtd_device_validate(u8 type, u8 num, u32 *size)
struct mtd_info *mtd = NULL;
if (get_mtd_info(type, num, &mtd))
return 1;
*size = mtd->size;
return 0;
723
724
725
726
727
728
729
730
731
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
}
/**
* 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_mtd_dev) {
/* we just deleted current device */
if (list_empty(&devices)) {
current_mtd_dev = NULL;
} else {
/* reset first partition from first dev from the
* devices list as current */
current_mtd_dev = list_entry(devices.next, struct mtd_device, link);
current_mtd_partnum = 0;
}
current_save();
return 0;
}
index_partitions();
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
*/
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_mtd_dev = dev;
current_mtd_partnum = 0;
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
current_save_needed = 1;
}
list_add_tail(&dev->link, &devices);
if (current_save_needed > 0)
current_save();
else
index_partitions();
}
/**
* 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;
const char *pend;
LIST_HEAD(tmp_list);
struct list_head *entry, *n;
u16 num_parts;
u32 offset;
int err = 1;
debug("===device_parse===\n");
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;
}
#ifdef DEBUG
pend = strchr(p, ';');
#endif
debug("dev type = %d (%s), dev num = %d, mtd-id = %s\n",
id->type, MTD_DEV_TYPE(id->type),
id->num, id->mtd_id);
debug("parsing partitions %.*s\n", (pend ? pend - p : strlen(p)), p);
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
/* 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;
}
if (err == 1) {
part_delall(&tmp_list);
return 1;
}
if (num_parts == 0) {
printf("no partitions for device %s%d (%s)\n",
MTD_DEV_TYPE(id->type), id->num, id->mtd_id);
return 1;
}
debug("\ntotal partitions: %d\n", num_parts);
/* check for next device presence */
if (p) {
if (*p == ';') {
} else {
printf("unexpected character '%c' at the end of device\n", *p);
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
return 1;
}
}
/* allocate memory for mtd_device structure */
if ((dev = (struct mtd_device *)malloc(sizeof(struct mtd_device))) == NULL) {
printf("out of memory\n");
return 1;
}
memset(dev, 0, sizeof(struct mtd_device));
dev->id = id;
dev->num_parts = 0; /* part_sort_add increments num_parts */
INIT_LIST_HEAD(&dev->parts);
INIT_LIST_HEAD(&dev->link);
/* move partitions from tmp_list to dev->parts */
list_for_each_safe(entry, n, &tmp_list) {
part = list_entry(entry, struct part_info, link);
list_del(entry);
if (part_sort_add(dev, part) != 0) {
device_del(dev);
return 1;
}
}
*retdev = dev;
return 0;
}
/**
* Initialize global device list.
*
* @return 0 on success, 1 otherwise
*/
static int mtd_devices_init(void)
{
last_parts[0] = '\0';
current_mtd_dev = NULL;
current_save();
return device_delall(&devices);
}
/*
* Search global mtdids list and find id of requested type and number.
*
* @return pointer to the id if it exists, NULL otherwise
*/
static struct mtdids* id_find(u8 type, u8 num)
{
struct list_head *entry;
struct mtdids *id;
list_for_each(entry, &mtdids) {
id = list_entry(entry, struct mtdids, link);
if ((id->type == type) && (id->num == num))
return id;
}
return NULL;