Skip to content
Snippets Groups Projects
mtdparts.c 52.4 KiB
Newer Older
  • Learn to ignore specific revisions
  • static struct mtdids* id_find_by_mtd_id(const char *mtd_id, unsigned int mtd_id_len)
    {
    	struct list_head *entry;
    	struct mtdids *id;
    
    
    	debug("--- id_find_by_mtd_id: '%.*s' (len = %d)\n",
    
    			mtd_id_len, mtd_id, mtd_id_len);
    
    	list_for_each(entry, &mtdids) {
    		id = list_entry(entry, struct mtdids, link);
    
    
    		debug("entry: '%s' (len = %zu)\n",
    
    				id->mtd_id, strlen(id->mtd_id));
    
    		if (mtd_id_len != strlen(id->mtd_id))
    			continue;
    		if (strncmp(id->mtd_id, mtd_id, mtd_id_len) == 0)
    			return id;
    	}
    
    	return NULL;
    }
    
    /**
     * Parse device id string <dev-id> := 'nand'|'nor'|'onenand'<dev-num>,
     * return device type and number.
     *
     * @param id string describing device id
     * @param ret_id output pointer to next char after parse completes (output)
     * @param dev_type parsed device type (output)
     * @param dev_num parsed device number (output)
     * @return 0 on success, 1 otherwise
     */
    
    int mtd_id_parse(const char *id, const char **ret_id, u8 *dev_type,
    		 u8 *dev_num)
    
    {
    	const char *p = id;
    
    	*dev_type = 0;
    	if (strncmp(p, "nand", 4) == 0) {
    		*dev_type = MTD_DEV_TYPE_NAND;
    		p += 4;
    	} else if (strncmp(p, "nor", 3) == 0) {
    		*dev_type = MTD_DEV_TYPE_NOR;
    		p += 3;
    	} else if (strncmp(p, "onenand", 7) == 0) {
    		*dev_type = MTD_DEV_TYPE_ONENAND;
    		p += 7;
    	} else {
    		printf("incorrect device type in %s\n", id);
    		return 1;
    	}
    
    	if (!isdigit(*p)) {
    		printf("incorrect device number in %s\n", id);
    		return 1;
    	}
    
    	*dev_num = simple_strtoul(p, (char **)&p, 0);
    	if (ret_id)
    		*ret_id = p;
    	return 0;
    }
    
    /**
     * Process all devices and generate corresponding mtdparts string describing
     * all partitions on all devices.
     *
     * @param buf output buffer holding generated mtdparts string (output)
     * @param buflen buffer size
     * @return 0 on success, 1 otherwise
     */
    static int generate_mtdparts(char *buf, u32 buflen)
    {
    	struct list_head *pentry, *dentry;
    	struct mtd_device *dev;
    	struct part_info *part, *prev_part;
    	char *p = buf;
    	char tmpbuf[32];
    
    	u64 size, offset;
    	u32 len, part_cnt;
    
    	u32 maxlen = buflen - 1;
    
    
    	debug("--- generate_mtdparts ---\n");
    
    
    	if (list_empty(&devices)) {
    		buf[0] = '\0';
    		return 0;
    	}
    
    
    	strcpy(p, "mtdparts=");
    
    	p += 9;
    
    	list_for_each(dentry, &devices) {
    		dev = list_entry(dentry, struct mtd_device, link);
    
    		/* copy mtd_id */
    		len = strlen(dev->id->mtd_id) + 1;
    		if (len > maxlen)
    			goto cleanup;
    		memcpy(p, dev->id->mtd_id, len - 1);
    		p += len - 1;
    		*(p++) = ':';
    		maxlen -= len;
    
    		/* format partitions */
    		prev_part = NULL;
    		part_cnt = 0;
    		list_for_each(pentry, &dev->parts) {
    			part = list_entry(pentry, struct part_info, link);
    			size = part->size;
    			offset = part->offset;
    			part_cnt++;
    
    			/* partition size */
    			memsize_format(tmpbuf, size);
    			len = strlen(tmpbuf);
    			if (len > maxlen)
    				goto cleanup;
    			memcpy(p, tmpbuf, len);
    			p += len;
    			maxlen -= len;
    
    
    			/* add offset only when there is a gap between
    			 * partitions */
    			if ((!prev_part && (offset != 0)) ||
    					(prev_part && ((prev_part->offset + prev_part->size) != part->offset))) {
    
    				memsize_format(tmpbuf, offset);
    				len = strlen(tmpbuf) + 1;
    				if (len > maxlen)
    					goto cleanup;
    				*(p++) = '@';
    				memcpy(p, tmpbuf, len - 1);
    				p += len - 1;
    				maxlen -= len;
    			}
    
    			/* copy name only if user supplied */
    			if(!part->auto_name) {
    				len = strlen(part->name) + 2;
    				if (len > maxlen)
    					goto cleanup;
    
    				*(p++) = '(';
    				memcpy(p, part->name, len - 2);
    				p += len - 2;
    				*(p++) = ')';
    				maxlen -= len;
    			}
    
    			/* ro mask flag */
    			if (part->mask_flags && MTD_WRITEABLE_CMD) {
    				len = 2;
    				if (len > maxlen)
    					goto cleanup;
    				*(p++) = 'r';
    				*(p++) = 'o';
    				maxlen -= 2;
    			}
    
    			/* print ',' separator if there are other partitions
    			 * following */
    			if (dev->num_parts > part_cnt) {
    				if (1 > maxlen)
    					goto cleanup;
    				*(p++) = ',';
    				maxlen--;
    			}
    			prev_part = part;
    		}
    		/* print ';' separator if there are other devices following */
    		if (dentry->next != &devices) {
    			if (1 > maxlen)
    				goto cleanup;
    			*(p++) = ';';
    			maxlen--;
    		}
    	}
    
    	/* we still have at least one char left, as we decremented maxlen at
    	 * the begining */
    	*p = '\0';
    
    	return 0;
    
    cleanup:
    	last_parts[0] = '\0';
    	return 1;
    }
    
    /**
     * Call generate_mtdparts to process all devices and generate corresponding
     * mtdparts string, save it in mtdparts environment variable.
     *
     * @param buf output buffer holding generated mtdparts string (output)
     * @param buflen buffer size
     * @return 0 on success, 1 otherwise
     */
    static int generate_mtdparts_save(char *buf, u32 buflen)
    {
    	int ret;
    
    	ret = generate_mtdparts(buf, buflen);
    
    	if ((buf[0] != '\0') && (ret == 0))
    		setenv("mtdparts", buf);
    	else
    		setenv("mtdparts", NULL);
    
    	return ret;
    }
    
    
    #if defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES)
    
     * Get the net size (w/o bad blocks) of the given partition.
     *
     * @param mtd the mtd info
     * @param part the partition
     * @return the calculated net size of this partition
    
    static uint64_t net_part_size(struct mtd_info *mtd, struct part_info *part)
    {
    
    	uint64_t i, net_size = 0;
    
    
    	if (!mtd->block_isbad)
    		return part->size;
    
    	for (i = 0; i < part->size; i += mtd->erasesize) {
    		if (!mtd->block_isbad(mtd, part->offset + i))
    			net_size += mtd->erasesize;
    	}
    
    	return net_size;
    }
    #endif
    
    static void print_partition_table(void)
    
    {
    	struct list_head *dentry, *pentry;
    	struct part_info *part;
    	struct mtd_device *dev;
    	int part_num;
    
    	list_for_each(dentry, &devices) {
    		dev = list_entry(dentry, struct mtd_device, link);
    
    		/* list partitions for given device */
    		part_num = 0;
    #if defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES)
    		struct mtd_info *mtd;
    
    		if (get_mtd_info(dev->id->type, dev->id->num, &mtd))
    			return;
    
    		printf("\ndevice %s%d <%s>, # parts = %d\n",
    				MTD_DEV_TYPE(dev->id->type), dev->id->num,
    				dev->id->mtd_id, dev->num_parts);
    		printf(" #: name\t\tsize\t\tnet size\toffset\t\tmask_flags\n");
    
    		list_for_each(pentry, &dev->parts) {
    			u32 net_size;
    			char *size_note;
    
    			part = list_entry(pentry, struct part_info, link);
    			net_size = net_part_size(mtd, part);
    			size_note = part->size == net_size ? " " : " (!)";
    			printf("%2d: %-20s0x%08x\t0x%08x%s\t0x%08x\t%d\n",
    					part_num, part->name, part->size,
    					net_size, size_note, part->offset,
    					part->mask_flags);
    #else /* !defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES) */
    
    		printf("\ndevice %s%d <%s>, # parts = %d\n",
    				MTD_DEV_TYPE(dev->id->type), dev->id->num,
    				dev->id->mtd_id, dev->num_parts);
    
    		printf(" #: name\t\tsize\t\toffset\t\tmask_flags\n");
    
    
    		list_for_each(pentry, &dev->parts) {
    			part = list_entry(pentry, struct part_info, link);
    
    			printf("%2d: %-20s0x%08llx\t0x%08llx\t%d\n",
    
    					part_num, part->name, part->size,
    					part->offset, part->mask_flags);
    
    #endif /* defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES) */
    
    	if (list_empty(&devices))
    		printf("no partitions defined\n");
    
    }
    
    /**
     * Format and print out a partition list for each device from global device
     * list.
     */
    static void list_partitions(void)
    {
    	struct part_info *part;
    
    	debug("\n---list_partitions---\n");
    	print_partition_table();
    
    	/* current_mtd_dev is not NULL only when we have non empty device list */
    	if (current_mtd_dev) {
    		part = mtd_part_info(current_mtd_dev, current_mtd_partnum);
    
    		if (part) {
    
    			printf("\nactive partition: %s%d,%d - (%s) 0x%08llx @ 0x%08llx\n",
    
    					MTD_DEV_TYPE(current_mtd_dev->id->type),
    					current_mtd_dev->id->num, current_mtd_partnum,
    
    					part->name, part->size, part->offset);
    		} else {
    			printf("could not get current partition info\n\n");
    		}
    	}
    
    	printf("\ndefaults:\n");
    
    	printf("mtdids  : %s\n",
    		mtdids_default ? mtdids_default : "none");
    
    	/*
    	 * Using printf() here results in printbuffer overflow
    	 * if default mtdparts string is greater than console
    	 * printbuffer. Use puts() to prevent system crashes.
    	 */
    	puts("mtdparts: ");
    	puts(mtdparts_default ? mtdparts_default : "none");
    	puts("\n");
    
    }
    
    /**
     * Given partition identifier in form of <dev_type><dev_num>,<part_num> find
     * corresponding device and verify partition number.
     *
     * @param id string describing device and partition or partition name
     * @param dev pointer to the requested device (output)
     * @param part_num verified partition number (output)
     * @param part pointer to requested partition (output)
     * @return 0 on success, 1 otherwise
     */
    int find_dev_and_part(const char *id, struct mtd_device **dev,
    		u8 *part_num, struct part_info **part)
    {
    	struct list_head *dentry, *pentry;
    	u8 type, dnum, pnum;
    	const char *p;
    
    
    	debug("--- find_dev_and_part ---\nid = %s\n", id);
    
    
    	list_for_each(dentry, &devices) {
    		*part_num = 0;
    		*dev = list_entry(dentry, struct mtd_device, link);
    		list_for_each(pentry, &(*dev)->parts) {
    			*part = list_entry(pentry, struct part_info, link);
    			if (strcmp((*part)->name, id) == 0)
    				return 0;
    			(*part_num)++;
    		}
    	}
    
    	p = id;
    	*dev = NULL;
    	*part = NULL;
    	*part_num = 0;
    
    	if (mtd_id_parse(p, &p, &type, &dnum) != 0)
    		return 1;
    
    	if ((*p++ != ',') || (*p == '\0')) {
    		printf("no partition number specified\n");
    		return 1;
    	}
    	pnum = simple_strtoul(p, (char **)&p, 0);
    	if (*p != '\0') {
    		printf("unexpected trailing character '%c'\n", *p);
    		return 1;
    	}
    
    	if ((*dev = device_find(type, dnum)) == NULL) {
    		printf("no such device %s%d\n", MTD_DEV_TYPE(type), dnum);
    		return 1;
    	}
    
    	if ((*part = mtd_part_info(*dev, pnum)) == NULL) {
    		printf("no such partition\n");
    		*dev = NULL;
    		return 1;
    	}
    
    	*part_num = pnum;
    
    	return 0;
    }
    
    /**
     * Find and delete partition. For partition id format see find_dev_and_part().
     *
     * @param id string describing device and partition
     * @return 0 on success, 1 otherwise
     */
    static int delete_partition(const char *id)
    {
    	u8 pnum;
    	struct mtd_device *dev;
    	struct part_info *part;
    
    	if (find_dev_and_part(id, &dev, &pnum, &part) == 0) {
    
    
    		debug("delete_partition: device = %s%d, partition %d = (%s) 0x%08llx@0x%08llx\n",
    
    				MTD_DEV_TYPE(dev->id->type), dev->id->num, pnum,
    				part->name, part->size, part->offset);
    
    		if (part_del(dev, part) != 0)
    			return 1;
    
    		if (generate_mtdparts_save(last_parts, MTDPARTS_MAXLEN) != 0) {
    
    			printf("generated mtdparts too long, resetting to null\n");
    
    			return 1;
    		}
    		return 0;
    	}
    
    	printf("partition %s not found\n", id);
    	return 1;
    }
    
    
    #if defined(CONFIG_CMD_MTDPARTS_SPREAD)
    /**
     * Increase the size of the given partition so that it's net size is at least
     * as large as the size member and such that the next partition would start on a
     * good block if it were adjacent to this partition.
     *
     * @param mtd the mtd device
     * @param part the partition
     * @param next_offset pointer to the offset of the next partition after this
     *                    partition's size has been modified (output)
     */
    static void spread_partition(struct mtd_info *mtd, struct part_info *part,
    			     uint64_t *next_offset)
    {
    	uint64_t net_size, padding_size = 0;
    	int truncated;
    
    	mtd_get_len_incl_bad(mtd, part->offset, part->size, &net_size,
    			     &truncated);
    
    	/*
    	 * Absorb bad blocks immediately following this
    	 * partition also into the partition, such that
    	 * the next partition starts with a good block.
    	 */
    	if (!truncated) {
    		mtd_get_len_incl_bad(mtd, part->offset + net_size,
    				     mtd->erasesize, &padding_size, &truncated);
    		if (truncated)
    			padding_size = 0;
    		else
    			padding_size -= mtd->erasesize;
    	}
    
    	if (truncated) {
    		printf("truncated partition %s to %lld bytes\n", part->name,
    		       (uint64_t) net_size + padding_size);
    	}
    
    	part->size = net_size + padding_size;
    	*next_offset = part->offset + part->size;
    }
    
    /**
     * Adjust all of the partition sizes, such that all partitions are at least
     * as big as their mtdparts environment variable sizes and they each start
     * on a good block.
     *
     * @return 0 on success, 1 otherwise
     */
    static int spread_partitions(void)
    {
    	struct list_head *dentry, *pentry;
    	struct mtd_device *dev;
    	struct part_info *part;
    	struct mtd_info *mtd;
    	int part_num;
    	uint64_t cur_offs;
    
    	list_for_each(dentry, &devices) {
    		dev = list_entry(dentry, struct mtd_device, link);
    
    		if (get_mtd_info(dev->id->type, dev->id->num, &mtd))
    			return 1;
    
    		part_num = 0;
    		cur_offs = 0;
    		list_for_each(pentry, &dev->parts) {
    			part = list_entry(pentry, struct part_info, link);
    
    			debug("spread_partitions: device = %s%d, partition %d ="
    
    Steve Rae's avatar
    Steve Rae committed
    				" (%s) 0x%08llx@0x%08llx\n",
    
    				MTD_DEV_TYPE(dev->id->type), dev->id->num,
    				part_num, part->name, part->size,
    				part->offset);
    
    			if (cur_offs > part->offset)
    				part->offset = cur_offs;
    
    			spread_partition(mtd, part, &cur_offs);
    
    			part_num++;
    		}
    	}
    
    	index_partitions();
    
    	if (generate_mtdparts_save(last_parts, MTDPARTS_MAXLEN) != 0) {
    
    		printf("generated mtdparts too long, resetting to null\n");
    
    		return 1;
    	}
    	return 0;
    }
    #endif /* CONFIG_CMD_MTDPARTS_SPREAD */
    
    
    /**
     * The mtdparts variable tends to be long. If we need to access it
     * before the env is relocated, then we need to use our own stack
     * buffer.  gd->env_buf will be too small.
     *
     * @param buf temporary buffer pointer MTDPARTS_MAXLEN long
     * @return mtdparts variable string, NULL if not found
     */
    static const char *getenv_mtdparts(char *buf)
    {
    	if (gd->flags & GD_FLG_ENV_READY)
    		return getenv("mtdparts");
    	if (getenv_f("mtdparts", buf, MTDPARTS_MAXLEN) != -1)
    		return buf;
    	return NULL;
    }
    
    
    /**
     * Accept character string describing mtd partitions and call device_parse()
     * for each entry. Add created devices to the global devices list.
     *
     * @param mtdparts string specifing mtd partitions
     * @return 0 on success, 1 otherwise
     */
    static int parse_mtdparts(const char *const mtdparts)
    {
    
    	struct mtd_device *dev;
    	int err = 1;
    
    	char tmp_parts[MTDPARTS_MAXLEN];
    
    	debug("\n---parse_mtdparts---\nmtdparts = %s\n\n", p);
    
    
    	/* delete all devices and partitions */
    	if (mtd_devices_init() != 0) {
    		printf("could not initialise device list\n");
    		return err;
    	}
    
    	/* re-read 'mtdparts' variable, mtd_devices_init may be updating env */
    
    	p = getenv_mtdparts(tmp_parts);
    
    	if (strncmp(p, "mtdparts=", 9) != 0) {
    		printf("mtdparts variable doesn't start with 'mtdparts='\n");
    		return err;
    	}
    	p += 9;
    
    
    		err = 1;
    		if ((device_parse(p, &p, &dev) != 0) || (!dev))
    			break;
    
    
    		debug("+ device: %s\t%d\t%s\n", MTD_DEV_TYPE(dev->id->type),
    
    				dev->id->num, dev->id->mtd_id);
    
    		/* check if parsed device is already on the list */
    		if (device_find(dev->id->type, dev->id->num) != NULL) {
    			printf("device %s%d redefined, please correct mtdparts variable\n",
    					MTD_DEV_TYPE(dev->id->type), dev->id->num);
    			break;
    		}
    
    		list_add_tail(&dev->link, &devices);
    		err = 0;
    	}
    
    		device_delall(&devices);
    
    
    }
    
    /**
     * Parse provided string describing mtdids mapping (see file header for mtdids
     * variable format). Allocate memory for each entry and add all found entries
     * to the global mtdids list.
     *
     * @param ids mapping string
     * @return 0 on success, 1 otherwise
     */
    static int parse_mtdids(const char *const ids)
    {
    	const char *p = ids;
    	const char *mtd_id;
    	int mtd_id_len;
    	struct mtdids *id;
    	struct list_head *entry, *n;
    	struct mtdids *id_tmp;
    	u8 type, num;
    
    	debug("\n---parse_mtdids---\nmtdids = %s\n\n", ids);
    
    
    	/* clean global mtdids list */
    	list_for_each_safe(entry, n, &mtdids) {
    		id_tmp = list_entry(entry, struct mtdids, link);
    
    		debug("mtdids del: %d %d\n", id_tmp->type, id_tmp->num);
    
    		list_del(entry);
    		free(id_tmp);
    	}
    	last_ids[0] = '\0';
    	INIT_LIST_HEAD(&mtdids);
    
    	while(p && (*p != '\0')) {
    
    		ret = 1;
    		/* parse 'nor'|'nand'|'onenand'<dev-num> */
    		if (mtd_id_parse(p, &p, &type, &num) != 0)
    			break;
    
    		if (*p != '=') {
    			printf("mtdids: incorrect <dev-num>\n");
    			break;
    		}
    		p++;
    
    		/* check if requested device exists */
    		if (mtd_device_validate(type, num, &size) != 0)
    			return 1;
    
    		/* locate <mtd-id> */
    		mtd_id = p;
    		if ((p = strchr(mtd_id, ',')) != NULL) {
    			mtd_id_len = p - mtd_id + 1;
    			p++;
    		} else {
    			mtd_id_len = strlen(mtd_id) + 1;
    		}
    		if (mtd_id_len == 0) {
    			printf("mtdids: no <mtd-id> identifier\n");
    			break;
    		}
    
    		/* check if this id is already on the list */
    		int double_entry = 0;
    		list_for_each(entry, &mtdids) {
    			id_tmp = list_entry(entry, struct mtdids, link);
    			if ((id_tmp->type == type) && (id_tmp->num == num)) {
    				double_entry = 1;
    				break;
    			}
    		}
    		if (double_entry) {
    			printf("device id %s%d redefined, please correct mtdids variable\n",
    					MTD_DEV_TYPE(type), num);
    			break;
    		}
    
    		/* allocate mtdids structure */
    		if (!(id = (struct mtdids *)malloc(sizeof(struct mtdids) + mtd_id_len))) {
    			printf("out of memory\n");
    			break;
    		}
    		memset(id, 0, sizeof(struct mtdids) + mtd_id_len);
    		id->num = num;
    		id->type = type;
    		id->size = size;
    		id->mtd_id = (char *)(id + 1);
    		strncpy(id->mtd_id, mtd_id, mtd_id_len - 1);
    		id->mtd_id[mtd_id_len - 1] = '\0';
    		INIT_LIST_HEAD(&id->link);
    
    
    		debug("+ id %s%d\t%16lld bytes\t%s\n",
    
    				MTD_DEV_TYPE(id->type), id->num,
    				id->size, id->mtd_id);
    
    		list_add_tail(&id->link, &mtdids);
    		ret = 0;
    	}
    	if (ret == 1) {
    		/* clean mtdids list and free allocated memory */
    		list_for_each_safe(entry, n, &mtdids) {
    			id_tmp = list_entry(entry, struct mtdids, link);
    			list_del(entry);
    			free(id_tmp);
    		}
    		return 1;
    	}
    
    	return 0;
    }
    
    
    /**
     * Parse and initialize global mtdids mapping and create global
     * device/partition list.
     *
     * @return 0 on success, 1 otherwise
     */
    int mtdparts_init(void)
    {
    	static int initialized = 0;
    	const char *ids, *parts;
    	const char *current_partition;
    	int ids_changed;
    	char tmp_ep[PARTITION_MAXLEN];
    
    	char tmp_parts[MTDPARTS_MAXLEN];
    
    	debug("\n---mtdparts_init---\n");
    
    	if (!initialized) {
    		INIT_LIST_HEAD(&mtdids);
    		INIT_LIST_HEAD(&devices);
    		memset(last_ids, 0, MTDIDS_MAXLEN);
    		memset(last_parts, 0, MTDPARTS_MAXLEN);
    		memset(last_partition, 0, PARTITION_MAXLEN);
    
    #if defined(CONFIG_SYS_MTDPARTS_RUNTIME)
    		board_mtdparts_default(&mtdids_default, &mtdparts_default);
    #endif
    
    		use_defaults = 1;
    
    		initialized = 1;
    	}
    
    	/* get variables */
    	ids = getenv("mtdids");
    
    	parts = getenv_mtdparts(tmp_parts);
    
    	current_partition = getenv("partition");
    
    	/* save it for later parsing, cannot rely on current partition pointer
    	 * as 'partition' variable may be updated during init */
    	tmp_ep[0] = '\0';
    	if (current_partition)
    		strncpy(tmp_ep, current_partition, PARTITION_MAXLEN);
    
    
    	debug("last_ids  : %s\n", last_ids);
    	debug("env_ids   : %s\n", ids);
    	debug("last_parts: %s\n", last_parts);
    	debug("env_parts : %s\n\n", parts);
    
    	debug("last_partition : %s\n", last_partition);
    	debug("env_partition  : %s\n", current_partition);
    
    	/* if mtdids variable is empty try to use defaults */
    
    	if (!ids) {
    		if (mtdids_default) {
    
    			debug("mtdids variable not defined, using default\n");
    
    			ids = mtdids_default;
    			setenv("mtdids", (char *)ids);
    		} else {
    			printf("mtdids not defined, no default present\n");
    			return 1;
    		}
    	}
    	if (strlen(ids) > MTDIDS_MAXLEN - 1) {
    		printf("mtdids too long (> %d)\n", MTDIDS_MAXLEN);
    		return 1;
    	}
    
    
    	/* use defaults when mtdparts variable is not defined
    	 * once mtdparts is saved environment, drop use_defaults flag */
    	if (!parts) {
    		if (mtdparts_default && use_defaults) {
    			parts = mtdparts_default;
    			if (setenv("mtdparts", (char *)parts) == 0)
    				use_defaults = 0;
    		} else
    			printf("mtdparts variable not set, see 'help mtdparts'\n");
    	}
    
    
    	if (parts && (strlen(parts) > MTDPARTS_MAXLEN - 1)) {
    		printf("mtdparts too long (> %d)\n", MTDPARTS_MAXLEN);
    		return 1;
    	}
    
    	/* check if we have already parsed those mtdids */
    	if ((last_ids[0] != '\0') && (strcmp(last_ids, ids) == 0)) {
    		ids_changed = 0;
    	} else {
    		ids_changed = 1;
    
    		if (parse_mtdids(ids) != 0) {
    			mtd_devices_init();
    			return 1;
    		}
    
    		/* ok it's good, save new ids */
    		strncpy(last_ids, ids, MTDIDS_MAXLEN);
    	}
    
    	/* parse partitions if either mtdparts or mtdids were updated */
    	if (parts && ((last_parts[0] == '\0') || ((strcmp(last_parts, parts) != 0)) || ids_changed)) {
    		if (parse_mtdparts(parts) != 0)
    			return 1;
    
    		if (list_empty(&devices)) {
    			printf("mtdparts_init: no valid partitions\n");
    			return 1;
    		}
    
    		/* ok it's good, save new parts */
    		strncpy(last_parts, parts, MTDPARTS_MAXLEN);
    
    		/* reset first partition from first dev from the list as current */
    
    		current_mtd_dev = list_entry(devices.next, struct mtd_device, link);
    		current_mtd_partnum = 0;
    
    		current_save();
    
    
    		debug("mtdparts_init: current_mtd_dev  = %s%d, current_mtd_partnum = %d\n",
    
    				MTD_DEV_TYPE(current_mtd_dev->id->type),
    				current_mtd_dev->id->num, current_mtd_partnum);
    
    	}
    
    	/* mtdparts variable was reset to NULL, delete all devices/partitions */
    	if (!parts && (last_parts[0] != '\0'))
    		return mtd_devices_init();
    
    	/* do not process current partition if mtdparts variable is null */
    	if (!parts)
    		return 0;
    
    	/* is current partition set in environment? if so, use it */
    	if ((tmp_ep[0] != '\0') && (strcmp(tmp_ep, last_partition) != 0)) {
    		struct part_info *p;
    		struct mtd_device *cdev;
    		u8 pnum;
    
    
    		debug("--- getting current partition: %s\n", tmp_ep);
    
    
    		if (find_dev_and_part(tmp_ep, &cdev, &pnum, &p) == 0) {
    
    			current_mtd_dev = cdev;
    			current_mtd_partnum = pnum;
    
    			current_save();
    		}
    	} else if (getenv("partition") == NULL) {
    
    		debug("no partition variable set, setting...\n");
    
    		current_save();
    	}
    
    	return 0;
    }
    
    /**
     * Return pointer to the partition of a requested number from a requested
     * device.
     *
     * @param dev device that is to be searched for a partition
     * @param part_num requested partition number
     * @return pointer to the part_info, NULL otherwise
     */
    static struct part_info* mtd_part_info(struct mtd_device *dev, unsigned int part_num)
    {
    	struct list_head *entry;
    	struct part_info *part;
    	int num;
    
    	if (!dev)
    		return NULL;
    
    
    	debug("\n--- mtd_part_info: partition number %d for device %s%d (%s)\n",
    
    			part_num, MTD_DEV_TYPE(dev->id->type),
    			dev->id->num, dev->id->mtd_id);
    
    	if (part_num >= dev->num_parts) {
    		printf("invalid partition number %d for device %s%d (%s)\n",
    				part_num, MTD_DEV_TYPE(dev->id->type),
    				dev->id->num, dev->id->mtd_id);
    		return NULL;
    	}
    
    	/* locate partition number, return it */
    	num = 0;
    	list_for_each(entry, &dev->parts) {
    		part = list_entry(entry, struct part_info, link);
    
    		if (part_num == num++) {
    			return part;
    		}
    	}
    
    	return NULL;
    }
    
    /***************************************************/
    
    /* U-Boot commands				   */
    
    /***************************************************/
    /* command line only */
    /**
     * Routine implementing u-boot chpart command. Sets new current partition based
     * on the user supplied partition id. For partition id format see find_dev_and_part().
     *
     * @param cmdtp command internal data
     * @param flag command flag
     * @param argc number of arguments supplied to the command
     * @param argv arguments list
     * @return 0 on success, 1 otherwise
     */
    
    static int do_chpart(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
    
    {
    /* command line only */
    	struct mtd_device *dev;
    	struct part_info *part;
    	u8 pnum;
    
    	if (mtdparts_init() !=0)
    		return 1;
    
    	if (argc < 2) {
    		printf("no partition id specified\n");
    		return 1;
    	}
    
    	if (find_dev_and_part(argv[1], &dev, &pnum, &part) != 0)
    		return 1;
    
    
    	current_mtd_dev = dev;
    	current_mtd_partnum = pnum;
    
    	current_save();
    
    	printf("partition changed to %s%d,%d\n",
    			MTD_DEV_TYPE(dev->id->type), dev->id->num, pnum);
    
    	return 0;
    }
    
    /**
     * Routine implementing u-boot mtdparts command. Initialize/update default global
     * partition list and process user partition request (list, add, del).
     *
     * @param cmdtp command internal data
     * @param flag command flag
     * @param argc number of arguments supplied to the command
     * @param argv arguments list
     * @return 0 on success, 1 otherwise
     */
    
    static int do_mtdparts(cmd_tbl_t *cmdtp, int flag, int argc,
    		       char * const argv[])
    
    {
    	if (argc == 2) {
    		if (strcmp(argv[1], "default") == 0) {
    
    			setenv("mtdids", NULL);
    			setenv("mtdparts", NULL);
    
    			setenv("partition", NULL);
    
    			use_defaults = 1;
    
    
    			mtdparts_init();
    			return 0;
    		} else if (strcmp(argv[1], "delall") == 0) {
    			/* this may be the first run, initialize lists if needed */
    			mtdparts_init();
    
    			setenv("mtdparts", NULL);
    
    			/* mtd_devices_init() calls current_save() */
    			return mtd_devices_init();
    		}
    	}
    
    	/* make sure we are in sync with env variables */
    	if (mtdparts_init() != 0)
    		return 1;
    
    	if (argc == 1) {
    		list_partitions();
    		return 0;
    	}
    
    	/* mtdparts add <mtd-dev> <size>[@<offset>] <name> [ro] */
    
    	if (((argc == 5) || (argc == 6)) && (strncmp(argv[1], "add", 3) == 0)) {
    
    #define PART_ADD_DESC_MAXLEN 64
    		char tmpbuf[PART_ADD_DESC_MAXLEN];
    
    #if defined(CONFIG_CMD_MTDPARTS_SPREAD)
    		struct mtd_info *mtd;
    		uint64_t next_offset;
    #endif
    
    		u8 type, num, len;
    		struct mtd_device *dev;
    		struct mtd_device *dev_tmp;
    		struct mtdids *id;
    		struct part_info *p;
    
    		if (mtd_id_parse(argv[2], NULL, &type, &num) != 0)
    			return 1;
    
    		if ((id = id_find(type, num)) == NULL) {
    			printf("no such device %s defined in mtdids variable\n", argv[2]);
    			return 1;
    		}
    
    		len = strlen(id->mtd_id) + 1;	/* 'mtd_id:' */