diff options
Diffstat (limited to 'drivers/mtd/nand/nand_util.c')
| -rw-r--r-- | drivers/mtd/nand/nand_util.c | 68 | 
1 files changed, 60 insertions, 8 deletions
| diff --git a/drivers/mtd/nand/nand_util.c b/drivers/mtd/nand/nand_util.c index ff2d34830..4727f9c98 100644 --- a/drivers/mtd/nand/nand_util.c +++ b/drivers/mtd/nand/nand_util.c @@ -416,11 +416,13 @@ int nand_unlock(struct mtd_info *mtd, loff_t start, size_t length,   * @param nand NAND device   * @param offset offset in flash   * @param length image length + * @param used length of flash needed for the requested length   * @return 0 if the image fits and there are no bad blocks   *         1 if the image fits, but there are bad blocks   *        -1 if the image does not fit   */ -static int check_skip_len(nand_info_t *nand, loff_t offset, size_t length) +static int check_skip_len(nand_info_t *nand, loff_t offset, size_t length, +		size_t *used)  {  	size_t len_excl_bad = 0;  	int ret = 0; @@ -442,8 +444,13 @@ static int check_skip_len(nand_info_t *nand, loff_t offset, size_t length)  			ret = 1;  		offset += block_len; +		*used += block_len;  	} +	/* If the length is not a multiple of block_len, adjust. */ +	if (len_excl_bad > length) +		*used -= (len_excl_bad - length); +  	return ret;  } @@ -476,23 +483,36 @@ static size_t drop_ffs(const nand_info_t *nand, const u_char *buf,   * Write image to NAND flash.   * Blocks that are marked bad are skipped and the is written to the next   * block instead as long as the image is short enough to fit even after - * skipping the bad blocks. + * skipping the bad blocks.  Due to bad blocks we may not be able to + * perform the requested write.  In the case where the write would + * extend beyond the end of the NAND device, both length and actual (if + * not NULL) are set to 0.  In the case where the write would extend + * beyond the limit we are passed, length is set to 0 and actual is set + * to the required length.   *   * @param nand  	NAND device   * @param offset	offset in flash   * @param length	buffer length + * @param actual	set to size required to write length worth of + *			buffer or 0 on error, if not NULL + * @param lim		maximum size that actual may be in order to not + *			exceed the buffer   * @param buffer        buffer to read from   * @param flags		flags modifying the behaviour of the write to NAND   * @return		0 in case of success   */  int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length, -			u_char *buffer, int flags) +		size_t *actual, loff_t lim, u_char *buffer, int flags)  {  	int rval = 0, blocksize;  	size_t left_to_write = *length; +	size_t used_for_write = 0;  	u_char *p_buffer = buffer;  	int need_skip; +	if (actual) +		*actual = 0; +  #ifdef CONFIG_CMD_NAND_YAFFS  	if (flags & WITH_YAFFS_OOB) {  		if (flags & ~WITH_YAFFS_OOB) @@ -529,13 +549,23 @@ int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,  		return -EINVAL;  	} -	need_skip = check_skip_len(nand, offset, *length); +	need_skip = check_skip_len(nand, offset, *length, &used_for_write); + +	if (actual) +		*actual = used_for_write; +  	if (need_skip < 0) {  		printf("Attempt to write outside the flash area\n");  		*length = 0;  		return -EINVAL;  	} +	if (used_for_write > lim) { +		puts("Size of write exceeds partition or device limit\n"); +		*length = 0; +		return -EFBIG; +	} +  	if (!need_skip && !(flags & WITH_DROP_FFS)) {  		rval = nand_write(nand, offset, length, buffer);  		if (rval == 0) @@ -626,36 +656,58 @@ int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,   *   * Read image from NAND flash.   * Blocks that are marked bad are skipped and the next block is read - * instead as long as the image is short enough to fit even after skipping the - * bad blocks. + * instead as long as the image is short enough to fit even after + * skipping the bad blocks.  Due to bad blocks we may not be able to + * perform the requested read.  In the case where the read would extend + * beyond the end of the NAND device, both length and actual (if not + * NULL) are set to 0.  In the case where the read would extend beyond + * the limit we are passed, length is set to 0 and actual is set to the + * required length.   *   * @param nand NAND device   * @param offset offset in flash   * @param length buffer length, on return holds number of read bytes + * @param actual set to size required to read length worth of buffer or 0 + * on error, if not NULL + * @param lim maximum size that actual may be in order to not exceed the + * buffer   * @param buffer buffer to write to   * @return 0 in case of success   */  int nand_read_skip_bad(nand_info_t *nand, loff_t offset, size_t *length, -		       u_char *buffer) +		size_t *actual, loff_t lim, u_char *buffer)  {  	int rval;  	size_t left_to_read = *length; +	size_t used_for_read = 0;  	u_char *p_buffer = buffer;  	int need_skip;  	if ((offset & (nand->writesize - 1)) != 0) {  		printf("Attempt to read non page-aligned data\n");  		*length = 0; +		if (actual) +			*actual = 0;  		return -EINVAL;  	} -	need_skip = check_skip_len(nand, offset, *length); +	need_skip = check_skip_len(nand, offset, *length, &used_for_read); + +	if (actual) +		*actual = used_for_read; +  	if (need_skip < 0) {  		printf("Attempt to read outside the flash area\n");  		*length = 0;  		return -EINVAL;  	} +	if (used_for_read > lim) { +		puts("Size of read exceeds partition or device limit\n"); +		*length = 0; +		return -EFBIG; +	} +  	if (!need_skip) {  		rval = nand_read(nand, offset, length, buffer);  		if (!rval || rval == -EUCLEAN) |