Skip to content
Snippets Groups Projects
cmd_jffs2.c 52.2 KiB
Newer Older
  • Learn to ignore specific revisions
  • 	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 {
    		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;
    }
    
    #ifdef CONFIG_JFFS2_CMDLINE
    /**
     * 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];
    	u32 size, offset, len, part_cnt;
    	u32 maxlen = buflen - 1;
    
    	DEBUGF("--- generate_mtdparts ---\n");
    
    	if (list_empty(&devices)) {
    		buf[0] = '\0';
    		return 0;
    	}
    	
    	sprintf(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) {
    				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;
    }
    
    /**
     * Format and print out a partition list for each device from global device
     * list.
     */
    static void list_partitions(void)
    {
    	struct list_head *dentry, *pentry;
    	struct part_info *part;
    	struct mtd_device *dev;
    	int part_num;
    
    	DEBUGF("\n---list_partitions---\n");
    	list_for_each(dentry, &devices) {
    		dev = list_entry(dentry, struct mtd_device, link);
    		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\t\tsize\t\toffset\t\tmask_flags\n");
    		
    		/* list partitions for given device */
    		part_num = 0;
    		list_for_each(pentry, &dev->parts) {
    			part = list_entry(pentry, struct part_info, link);
    			printf(" %d: %-22s\t0x%08x\t0x%08x\t%d\n",
    					part_num, part->name, part->size,
    					part->offset, part->mask_flags);
    
    			part_num++;
    		}
    	}
    	if (list_empty(&devices))
    		printf("no partitions defined\n");
    
    	/* current_dev is not NULL only when we have non empty device list */
    	if (current_dev) {
    		part = jffs2_part_info(current_dev, current_partnum);
    		if (part) {
    			printf("\nactive partition: %s%d,%d - (%s) 0x%08lx @ 0x%08lx\n",
    					MTD_DEV_TYPE(current_dev->id->type),
    					current_dev->id->num, current_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);
    	printf("mtdparts: %s\n", mtdparts_default);
    }
    
    /**
     * Given partition identifier in form of <dev_type><dev_num>,<part_num> find
     * corresponding device and verify partition number.
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
     *
    
     * @param id string describing device and partition
     * @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)
    {
    	u8 type, dnum, pnum;
    	const char *p;
    
    	DEBUGF("--- find_dev_and_part ---\nid = %s\n", id);
    
    	p = id;
    	*dev = NULL;
    	*part = NULL;
    	*part_num = 0;
    
    	if (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 = jffs2_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().
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
     *
    
     * @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) {
    
    		DEBUGF("delete_partition: device = %s%d, partition %d = (%s) 0x%08lx@0x%08lx\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, reseting to null\n");
    			return 1;
    		}
    		return 0;
    	}
    
    	printf("partition %s not found\n", id);
    	return 1;
    }
    
    /**
     * Accept character string describing mtd partitions and call device_parse()
     * for each entry. Add created devices to the global devices list.
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
     *
    
     * @param mtdparts string specifing mtd partitions
     * @return 0 on success, 1 otherwise
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
     */
    
    static int parse_mtdparts(const char *const mtdparts)
    {
    	const char *p = mtdparts;
    	struct mtd_device *dev;
    	int err = 1;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    	DEBUGF("\n---parse_mtdparts---\nmtdparts = %s\n\n", p);
    
    	/* delete all devices and partitions */
    	if (devices_init() != 0) {
    		printf("could not initialise device list\n");
    		return err;
    	}
    
    	/* re-read 'mtdparts' variable, devices_init may be updating env */
    	p = getenv("mtdparts");
    	
    	if (strncmp(p, "mtdparts=", 9) != 0) {
    		printf("mtdparts variable doesn't start with 'mtdparts='\n");
    		return err;
    	}
    	p += 9;
    
    	while (p && (*p != '\0')) {
    		err = 1;
    		if ((device_parse(p, &p, &dev) != 0) || (!dev))
    			break;
    
    		DEBUGF("+ 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;
    	}
    	if (err == 1) {
    		device_delall(&devices);
    		return 1;
    	}
    
    	return 0;
    }
    
    /**
     * 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
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
     */
    
    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;
    	u32 size;
    	int ret = 1;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    	DEBUGF("\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);
    		DEBUGF("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'<dev-num> */
    		if (id_parse(p, &p, &type, &num) != 0)
    			break;
    
    		if (*p != '=') {
    			printf("mtdids: incorrect <dev-num>\n");
    			break;
    		}
    		p++;
    
    		/* check if requested device exists */
    		if (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;
    		}
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    		/* 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);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    		DEBUGF("+ id %s%d\t%16d 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;
    	}
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    /**
     * Parse and initialize global mtdids mapping and create global
     * device/partition list.
     *
     * @return 0 on success, 1 otherwise
     */
    int mtdparts_init(void)
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    {
    
    	static int initialized = 0;
    	const char *ids, *parts;
    	const char *current_partition;
    	int ids_changed;
    	char tmp_ep[PARTITION_MAXLEN];
    
    	DEBUGF("\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);
    		initialized = 1;
    	}
    
    	/* get variables */
    	ids = getenv("mtdids");
    	parts = getenv("mtdparts");
    	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);
    
    	DEBUGF("last_ids  : %s\n", last_ids);
    	DEBUGF("env_ids   : %s\n", ids);
    	DEBUGF("last_parts: %s\n", last_parts);
    	DEBUGF("env_parts : %s\n\n", parts);
    
    	DEBUGF("last_partition : %s\n", last_partition);
    	DEBUGF("env_partition  : %s\n", current_partition);
    
    	/* if mtdids varible is empty try to use defaults */
    	if (!ids) {
    		if (mtdids_default) {
    			DEBUGF("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;
    	}
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    	/* do no try to use defaults when mtdparts variable is not defined,
    	 * just check the length */
    	if (!parts)
    		printf("mtdparts variable not set, see 'help mtdparts'\n");
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    	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) {
    			device_delall(&devices);
    			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_dev = list_entry(devices.next, struct mtd_device, link);
    		current_partnum = 0;
    		current_save();
    
    		DEBUGF("mtdparts_init: current_dev  = %s%d, current_partnum = %d\n",
    				MTD_DEV_TYPE(current_dev->id->type),
    				current_dev->id->num, current_partnum);
    	}
    
    	/* mtdparts variable was reset to NULL, delete all devices/partitions */
    	if (!parts && (last_parts[0] != '\0'))
    		return 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;
    
    		DEBUGF("--- getting current partition: %s\n", tmp_ep);
    
    		if (find_dev_and_part(tmp_ep, &cdev, &pnum, &p) == 0) {
    			current_dev = cdev;
    			current_partnum = pnum;
    			current_save();
    		}
    	} else if (getenv("partition") == NULL) {
    		DEBUGF("no partition variable set, setting...\n");
    		current_save();
    	}
    
    	return 0;
    }
    #else /* #ifdef CONFIG_JFFS2_CMDLINE */
    /*
     * 'Static' version of command line mtdparts_init() routine. Single partition on
     * a single device configuration.
     */
    
    /**
     * 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;
    	u32 size;
    	char *dev_name;
    
    	DEBUGF("\n---mtdparts_init---\n");
    	if (!initialized) {
    
    		struct mtdids *id;
    		struct part_info *part;
    
    
    		initialized = 1;
    		current_dev = (struct mtd_device *)
    			malloc(sizeof(struct mtd_device) +
    					sizeof(struct part_info) +
    					sizeof(struct mtdids));
    		if (!current_dev) {
    			printf("out of memory\n");
    			return 1;
    		}
    		memset(current_dev, 0, sizeof(struct mtd_device) +
    					sizeof(struct part_info) + sizeof(struct mtdids));
    
    
    		id = (struct mtdids *)(current_dev + 1);
    		part = (struct part_info *)(id + 1);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    #if defined(CONFIG_JFFS2_DEV)
    		dev_name = CONFIG_JFFS2_DEV;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    #else
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    #endif
    
    
    		if ((id_parse(dev_name, NULL, &id->type, &id->num) != 0) ||
    				(device_validate(id->type, id->num, &size) != 0)) {
    			printf("incorrect device: %s%d\n", MTD_DEV_TYPE(id->type), id->num);
    			free(current_dev);
    			return 1;
    		}
    		id->size = size;
    		INIT_LIST_HEAD(&id->link);
    
    		DEBUGF("dev id: type = %d, num = %d, size = 0x%08lx, mtd_id = %s\n",
    				id->type, id->num, id->size, id->mtd_id);
    
    		/* partition */
    		part->name = "static";
    		part->auto_name = 0;
    
    #if defined(CONFIG_JFFS2_PART_SIZE)
    		part->size = CONFIG_JFFS2_PART_SIZE;
    #else
    		part->size = SIZE_REMAINING;
    #endif
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    #if defined(CONFIG_JFFS2_PART_OFFSET)
    		part->offset = CONFIG_JFFS2_PART_OFFSET;
    #else
    		part->offset = 0x00000000;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    #endif
    
    
    		part->dev = current_dev;
    		INIT_LIST_HEAD(&part->link);
    
    		/* recalculate size if needed */
    		if (part->size == SIZE_REMAINING)
    			part->size = id->size - part->offset;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    		DEBUGF("part  : name = %s, size = 0x%08lx, offset = 0x%08lx\n",
    				part->name, part->size, part->offset);
    
    		/* device */
    		current_dev->id = id;
    		INIT_LIST_HEAD(&current_dev->link);
    		current_dev->num_parts = 1;
    		INIT_LIST_HEAD(&current_dev->parts);
    		list_add(&part->link, &current_dev->parts);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	}
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	return 0;
    }
    
    #endif /* #ifdef CONFIG_JFFS2_CMDLINE */
    
    /**
     * 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* jffs2_part_info(struct mtd_device *dev, unsigned int part_num)
    
    	struct list_head *entry;
    	struct part_info *part;
    	int num;
    
    	DEBUGF("\n--- jffs2_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;
    		}
    
    /***************************************************/
    /* U-boot commands				   */
    /***************************************************/
    
    /**
     * Routine implementing fsload u-boot command. This routine tries to load
     * a requested file from jffs2/cramfs filesystem on a current partition.
     *
     * @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
     */
    int do_jffs2_fsload(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    {
    
    	char *fsname;
    
    	char *filename;
    
    	int size;
    	struct part_info *part;
    	ulong offset = load_addr;
    
    
    	/* pre-set Boot file name */
    	if ((filename = getenv("bootfile")) == NULL) {
    		filename = "uImage";
    	}
    
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	if (argc == 2) {
    		filename = argv[1];
    	}
    	if (argc == 3) {
    		offset = simple_strtoul(argv[1], NULL, 16);
    
    		load_addr = offset;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		filename = argv[2];
    	}
    
    
    	/* make sure we are in sync with env variables */
    	if (mtdparts_init() !=0)
    		return 1;
    
    	if ((part = jffs2_part_info(current_dev, current_partnum))){
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    		/* check partition type for cramfs */
    		fsname = (cramfs_check(part) ? "CRAMFS" : "JFFS2");
    		printf("### %s loading '%s' to 0x%lx\n", fsname, filename, offset);
    
    		if (cramfs_check(part)) {
    			size = cramfs_load ((char *) offset, part, filename);
    		} else {
    			/* if this is not cramfs assume jffs2 */
    			size = jffs2_1pass_load((char *)offset, part, filename);
    		}
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    		if (size > 0) {
    			char buf[10];
    
    			printf("### %s load complete: %d bytes loaded to 0x%lx\n",
    				fsname, size, offset);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    			sprintf(buf, "%x", size);
    			setenv("filesize", buf);
    		} else {
    
    			printf("### %s LOAD ERROR<%x> for %s!\n", fsname, size, filename);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		}
    
    		return !(size > 0);
    	}
    	return 0;
    }
    
    
    /**
     * Routine implementing u-boot ls command which lists content of a given
     * directory on a current partition.
     *
     * @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
     */
    int do_jffs2_ls(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    {
    
    	char *filename = "/";
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	int ret;
    	struct part_info *part;
    
    	if (argc == 2)
    		filename = argv[1];
    
    
    	/* make sure we are in sync with env variables */
    	if (mtdparts_init() !=0)
    		return 1;
    
    	if ((part = jffs2_part_info(current_dev, current_partnum))){
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    		/* check partition type for cramfs */
    		if (cramfs_check(part)) {
    			ret = cramfs_ls (part, filename);
    		} else {
    			/* if this is not cramfs assume jffs2 */
    			ret = jffs2_1pass_ls(part, filename);
    		}
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    		return (ret == 1);
    	}
    	return 0;
    }
    
    
    /**
     * Routine implementing u-boot fsinfo command. This routine prints out
     * miscellaneous filesystem informations/statistics.
     *
     * @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
     */
    int do_jffs2_fsinfo(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    {
    	struct part_info *part;
    
    	char *fsname;
    	int ret;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    	/* make sure we are in sync with env variables */
    	if (mtdparts_init() !=0)
    		return 1;
    	
    	if ((part = jffs2_part_info(current_dev, current_partnum))){
    
    
    		/* check partition type for cramfs */
    		fsname = (cramfs_check(part) ? "CRAMFS" : "JFFS2");
    		printf("### filesystem type is %s\n", fsname);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    		if (cramfs_check(part)) {
    			ret = cramfs_info (part);
    		} else {
    			/* if this is not cramfs assume jffs2 */
    			ret = jffs2_1pass_info(part);
    		}
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    		return (ret == 1);
    	}
    	return 0;
    }
    
    
    /* command line only */
    #ifdef CONFIG_JFFS2_CMDLINE
    /**
     * 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
     */
    int do_jffs2_chpart(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    {
    
    /* command line only */
    	struct mtd_device *dev;
    	struct part_info *part;
    	u8 pnum;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    	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_dev = dev;
    	current_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
     */
    int do_jffs2_mtdparts(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
    {
    	if (argc == 2) {
    		if (strcmp(argv[1], "default") == 0) {
    			setenv("mtdids", (char *)mtdids_default);
    			setenv("mtdparts", (char *)mtdparts_default);
    			setenv("partition", NULL);
    
    			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);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    			/* devices_init() calls current_save() */
    			return devices_init();
    		}
    
    	/* make sure we are in sync with env variables */
    	if (mtdparts_init() != 0)
    		return 1;
    
    	if (argc == 1) {
    		list_partitions();
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		return 0;
    	}
    
    	
    	/* mtdparts add <mtd-dev> <size>[@<offset>] <name> [ro] */
    	if (((argc == 5) || (argc == 6)) && (strcmp(argv[1], "add") == 0)) {
    #define PART_ADD_DESC_MAXLEN 64
    		char tmpbuf[PART_ADD_DESC_MAXLEN];
    		u8 type, num, len;
    		struct mtd_device *dev;
    		struct mtd_device *dev_tmp;
    		struct mtdids *id;
    		struct part_info *p;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    		if (id_parse(argv[2], NULL, &type, &num) != 0)
    			return 1;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    		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:' */
    		len += strlen(argv[3]);		/* size@offset */
    		len += strlen(argv[4]) + 2;	/* '(' name ')' */
    		if (argv[5] && (strlen(argv[5]) == 2))
    			len += 2;		/* 'ro' */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    		if (len >= PART_ADD_DESC_MAXLEN) {
    			printf("too long partition description\n");
    			return 1;
    		}
    		sprintf(tmpbuf, "%s:%s(%s)%s",