Example #1
0
/**
 * \brief put all memory block back, return how many memory blocks were put back
 */
int uffs_PoolPutAll(uffs_Pool *pool)
{
	void *p = NULL;
	int count = 0;

	do {
		p = uffs_PoolFindNextAllocated(pool, p);
		if (p) {
			uffs_PoolPut(pool, p);
			count++;
		}
	} while (p);

	return count;
}
/**
 * Put all dir entry buf match dev
 */
int uffs_DirEntryBufPutAll(uffs_Device *dev)
{
	int count = 0;
	uffs_DIR *dirp = NULL;

	do {
		dirp = (uffs_DIR *) uffs_PoolFindNextAllocated(&_dir_pool, dirp);
		if (dirp && dirp->obj && dirp->obj->dev &&
				dirp->obj->dev->dev_num == dev->dev_num) {
			uffs_PoolPut(&_dir_pool, dirp);
			count++;
		}
	} while (dirp);

	return count;
}
/**
 * mark a clean page tag as 'dirty' and 'invalid'.
 *
 * \param[in] dev uffs device
 * \param[in] bc block info
 * \param[in] page
 *
 * \return	#UFFS_FLASH_NO_ERR: success.
 *			#UFFS_FLASH_IO_ERR: I/O error, expect retry ?
 *			#UFFS_FLASH_BAD_BLK: a new bad block detected.
 */
int uffs_FlashMarkDirtyPage(uffs_Device *dev, uffs_BlockInfo *bc, int page)
{
	u8 *spare;
	uffs_FlashOps *ops = dev->ops;
	UBOOL is_bad = U_FALSE;
	int ret = UFFS_FLASH_UNKNOWN_ERR;
	int block = bc->block;
	uffs_Tags *tag = GET_TAG(bc, page);
	struct uffs_TagStoreSt *ts = &tag->s;

	spare = (u8 *) uffs_PoolGet(SPOOL(dev));
	if (spare == NULL)
		goto ext;

	memset(ts, 0xFF, sizeof(struct uffs_TagStoreSt));
	ts->dirty = TAG_DIRTY;  // set only 'dirty' bit, leave 'valid' bit to 1 (invalid).
	
	if (dev->attr->ecc_opt != UFFS_ECC_NONE)
		TagMakeEcc(ts);

	if (ops->WritePageWithLayout) {
		ret = ops->WritePageWithLayout(dev, block, page, NULL, 0, NULL, ts);
	}
	else {
		uffs_FlashMakeSpare(dev, ts, NULL, spare);
		ret = ops->WritePage(dev, block, page, NULL, 0, spare, dev->mem.spare_data_size);
	}

	if (UFFS_FLASH_IS_BAD_BLOCK(ret))
		is_bad = U_TRUE;

ext:
	if (is_bad)
		uffs_BadBlockAdd(dev, block);

	if (spare)
		uffs_PoolPut(SPOOL(dev), spare);

	return ret;
}
Example #4
0
static void PutDirEntry(uffs_DIR *p)
{
    uffs_PoolPut(&_dir_pool, p);
}
Example #5
0
File: uffs_fs.c Project: mazj/uffs
/**
 * put the object struct back to system
 */
void uffs_PutObject(uffs_Object *obj)
{
	if (obj)
		uffs_PoolPut(&_object_pool, obj);
}
static void _ForceFormatAndCheckBlock(uffs_Device *dev, int block)
{
	int i, j;
	uffs_Buf *buf = NULL;
	UBOOL bad = U_TRUE;
	URET ret;
	struct uffs_FlashOpsSt *ops = dev->ops;
	struct uffs_TagStoreSt ts;
	u8 *spare = NULL;

	buf = uffs_BufClone(dev, NULL);
	if (buf == NULL) {
		uffs_Perror(UFFS_MSG_SERIOUS,
					"Alloc page buffer fail ! Format stoped.");
		goto ext;
	}

	spare = (u8 *)uffs_PoolGet(SPOOL(dev));
	if (spare == NULL)
		goto ext;

	//step 1: Erase, fully fill with 0x0, and check
	ret = uffs_FlashEraseBlock(dev, block);
	if (UFFS_FLASH_IS_BAD_BLOCK(ret))
		goto bad_out;

	memset(buf->header, 0, dev->com.pg_size);
	memset(&ts, 0, sizeof(ts));
	memset(spare, 0, dev->attr->spare_size);

	for (i = 0; i < dev->attr->pages_per_block; i++) {
		if (ops->WritePageWithLayout)
			ret = ops->WritePageWithLayout(dev, block, i, buf->header, dev->com.pg_size, NULL, &ts);
		else
			ret = ops->WritePage(dev, block, i, buf->header, dev->com.pg_size, spare, dev->attr->spare_size);

		if (UFFS_FLASH_IS_BAD_BLOCK(ret))
			goto bad_out;
	}
	for (i = 0; i < dev->attr->pages_per_block; i++) {
		memset(buf->header, 0xFF, dev->com.pg_size);
		memset(&ts, 0xFF, sizeof(ts));
		memset(spare, 0xFF, dev->attr->spare_size);

		if (ops->ReadPageWithLayout) {
			ret = ops->ReadPageWithLayout(dev, block, i, buf->header, dev->com.pg_size, NULL, &ts, NULL);
			if (UFFS_FLASH_IS_BAD_BLOCK(ret))
				goto bad_out;
			for (j = 0; j < dev->com.pg_size; j++)
				if (buf->header[j] != 0)
					goto bad_out;
			for (j = 0; j < sizeof(ts); j++)
				if (((u8 *)&ts)[j] != 0)
					goto bad_out;
		}
		else {
			ret = ops->ReadPage(dev, block, i, buf->header, dev->com.pg_size, NULL, spare, dev->attr->spare_size);
			if (UFFS_FLASH_IS_BAD_BLOCK(ret))
				goto bad_out;
			for (j = 0; j < dev->com.pg_size; j++)
				if (buf->header[j] != 0)
					goto bad_out;
			for (j = 0; j < dev->attr->spare_size; j++)
				if (spare[j] != 0)
					goto bad_out;
		}
	}

	//step 2: Erase, and check
	ret = uffs_FlashEraseBlock(dev, block);
	if (UFFS_FLASH_IS_BAD_BLOCK(ret))
		goto bad_out;

	for (i = 0; i < dev->attr->pages_per_block; i++) {
		memset(buf->header, 0, dev->com.pg_size);
		memset(&ts, 0, sizeof(ts));
		memset(spare, 0, dev->attr->spare_size);

		if (ops->ReadPageWithLayout) {
			ret = ops->ReadPageWithLayout(dev, block, i, buf->header, dev->com.pg_size, NULL, &ts, NULL);
			if (UFFS_FLASH_IS_BAD_BLOCK(ret))
				goto bad_out;
			for (j = 0; j < dev->com.pg_size; j++)
				if (buf->header[j] != 0xFF)
					goto bad_out;
			for (j = 0; j < sizeof(ts); j++)
				if (((u8 *)&ts)[j] != 0xFF)
					goto bad_out;
		}
		else {
			ret = ops->ReadPage(dev, block, i, buf->header, dev->com.pg_size, NULL, spare, dev->attr->spare_size);
			if (UFFS_FLASH_IS_BAD_BLOCK(ret))
				goto bad_out;
			for (j = 0; j < dev->com.pg_size; j++)
				if (buf->header[j] != 0xFF)
					goto bad_out;
			for (j = 0; j < dev->attr->spare_size; j++)
				if (spare[j] != 0xFF)
					goto bad_out;
		}
	}

	// format succ
	bad = U_FALSE;

bad_out:
	if (bad == U_TRUE)
		uffs_FlashMarkBadBlock(dev, block);
ext:
	if (buf)
		uffs_BufFreeClone(dev, buf);

	if (spare)
		uffs_PoolPut(SPOOL(dev), spare);

	return;
}
/**
 * Check the block by reading all pages.
 *
 * \return U_SUCC - all pages are clean,
 *         U_FAIL - block is not clean
 */
URET uffs_FlashCheckErasedBlock(uffs_Device *dev, int block)
{
	u8 *spare = NULL;
	uffs_FlashOps *ops = dev->ops;
	int ret = U_SUCC;
	int page;
	int flash_ret;
	u8 ecc_store[UFFS_MAX_ECC_SIZE];
	uffs_TagStore ts;
	uffs_Buf *buf = NULL;
	int size = dev->com.pg_size;
	int i;
	u8 *p;
	
	spare = (u8 *) uffs_PoolGet(SPOOL(dev));
	
	if (spare == NULL) {
		uffs_Perror(UFFS_MSG_SERIOUS, "Can't allocate spare buf.");
		goto ext;
	}
	
	buf = uffs_BufClone(dev, NULL);
	
	if (buf == NULL) {
		uffs_Perror(UFFS_MSG_SERIOUS, "Can't clone buf.");
		goto ext;
	}
	
	for (page = 0; page < dev->attr->pages_per_block; page++) {
		if (ops->ReadPageWithLayout) {
			
			flash_ret = ops->ReadPageWithLayout(dev, block, page, buf->header, size, NULL, &ts, ecc_store);
			
			if (flash_ret != UFFS_FLASH_IO_ERR) {
				// check page tag, should be all 0xFF
				for (i = 0, p = (u8 *)(&ts); i < sizeof(ts); i++, p++) {
					if (*p != 0xFF) {
						ret = U_FAIL;
						goto ext;
					}
				}
				
				// for hw or soft ecc, check stored ecc, should be all 0xFF
				if (dev->attr->ecc_opt == UFFS_ECC_HW ||
					dev->attr->ecc_opt == UFFS_ECC_SOFT)
				{
					for (i = 0, p = ecc_store; i < ECC_SIZE(dev); i++, p++) {
						if (*p != 0xFF) {
							ret = U_FAIL;
							goto ext;
						}
					}
				}
			}
		}
		else {
			
			flash_ret = ops->ReadPage(dev, block, page, buf->header, size, NULL, spare, dev->attr->spare_size);
			if (flash_ret != UFFS_FLASH_IO_ERR) {
				// check spare data, should be all 0xFF
				for (i = 0, p = spare; i < dev->attr->spare_size; i++, p++) {
					if (*p != 0xFF) {
						ret = U_FAIL;
						goto ext;
					}
				}
			}
		}
		
		if (flash_ret != UFFS_FLASH_IO_ERR) {
			// check page data, should be all 0xFF
			for (i = 0, p = buf->header; i < size; i++, p++) {
				if (*p != 0xFF) {
					ret = U_FAIL;
					goto ext;
				}
			}
		}
		
	}
	

ext:
	if (spare)
		uffs_PoolPut(SPOOL(dev), spare);
	
	if (buf)
		uffs_BufFreeClone(dev, buf);
	
	
	return ret;
}
/**
 * write the whole page, include data and tag
 *
 * \param[in] dev uffs device
 * \param[in] block
 * \param[in] page
 * \param[in] buf contains data to be wrote
 * \param[in] tag tag to be wrote
 *
 * \return	#UFFS_FLASH_NO_ERR: success.
 *			#UFFS_FLASH_IO_ERR: I/O error, expect retry ?
 *			#UFFS_FLASH_BAD_BLK: a new bad block detected.
 */
int uffs_FlashWritePageCombine(uffs_Device *dev,
							   int block, int page,
							   uffs_Buf *buf, uffs_Tags *tag)
{
	uffs_FlashOps *ops = dev->ops;
	int size = dev->com.pg_size;
	u8 ecc_buf[UFFS_MAX_ECC_SIZE];
	u8 *ecc = NULL;
	u8 *spare;
	struct uffs_MiniHeaderSt *header;
	int ret = UFFS_FLASH_UNKNOWN_ERR;
	UBOOL is_bad = U_FALSE;
	uffs_Buf *verify_buf;
#ifdef CONFIG_PAGE_WRITE_VERIFY
	uffs_Tags chk_tag;
#endif
	
	spare = (u8 *) uffs_PoolGet(SPOOL(dev));
	if (spare == NULL)
		goto ext;

	// setup header
	header = HEADER(buf);
	memset(header, 0xFF, sizeof(struct uffs_MiniHeaderSt));
	header->status = 0;
#ifdef CONFIG_ENABLE_PAGE_DATA_CRC
	header->crc = uffs_crc16sum(buf->data, size - sizeof(struct uffs_MiniHeaderSt));
#endif

	// setup tag
	TAG_DIRTY_BIT(tag) = TAG_DIRTY;		//!< set dirty bit
	TAG_VALID_BIT(tag) = TAG_VALID;		//!< set valid bit
	SEAL_TAG(tag);						//!< seal tag (the real seal byte will be set in uffs_FlashMakeSpare())

	if (dev->attr->ecc_opt != UFFS_ECC_NONE)
		TagMakeEcc(&tag->s);
	else
		tag->s.tag_ecc = TAG_ECC_DEFAULT;
	
	if (dev->attr->ecc_opt == UFFS_ECC_SOFT) {
		uffs_EccMake(buf->header, size, ecc_buf);
		ecc = ecc_buf;
	}
	else if (dev->attr->ecc_opt == UFFS_ECC_HW) {
		ecc = ecc_buf;
	}

	if (ops->WritePageWithLayout) {
		ret = ops->WritePageWithLayout(dev, block, page,
							buf->header, size, ecc, &tag->s);
	}
	else {

		if (!uffs_Assert(!(dev->attr->layout_opt == UFFS_LAYOUT_FLASH ||
					dev->attr->ecc_opt == UFFS_ECC_HW ||
					dev->attr->ecc_opt == UFFS_ECC_HW_AUTO), "WritePageWithLayout() not implemented ?")) {
			ret = UFFS_FLASH_IO_ERR;
			goto ext;
		}

		uffs_FlashMakeSpare(dev, &tag->s, ecc, spare);

		ret = ops->WritePage(dev, block, page, buf->header, size, spare, dev->mem.spare_data_size);

	}
	
	if (UFFS_FLASH_IS_BAD_BLOCK(ret))
		is_bad = U_TRUE;

	if (UFFS_FLASH_HAVE_ERR(ret))
		goto ext;

#ifdef CONFIG_PAGE_WRITE_VERIFY
	verify_buf = uffs_BufClone(dev, NULL);
	if (verify_buf) {
		ret = uffs_FlashReadPage(dev, block, page, verify_buf, U_FALSE);
		if (!UFFS_FLASH_HAVE_ERR(ret)) {
			if (memcmp(buf->header, verify_buf->header, size) != 0) {
				uffs_Perror(UFFS_MSG_NORMAL,
							"Page write verify failed (block %d page %d)",
							block, page);
				ret = UFFS_FLASH_BAD_BLK;
			}
		}

		uffs_BufFreeClone(dev, verify_buf);
	}
	else {
		uffs_Perror(UFFS_MSG_SERIOUS, "Insufficient buf, clone buf failed.");
	}

	ret = uffs_FlashReadPageTag(dev, block, page, &chk_tag);
	if (UFFS_FLASH_HAVE_ERR(ret))
		goto ext;
	
	if (memcmp(&tag->s, &chk_tag.s, sizeof(uffs_TagStore)) != 0) {
		uffs_Perror(UFFS_MSG_NORMAL, "Page tag write verify failed (block %d page %d)",
					block, page);
		ret = UFFS_FLASH_BAD_BLK;
	}

	if (UFFS_FLASH_IS_BAD_BLOCK(ret))
		is_bad = U_TRUE;
	
#endif
ext:
	if (is_bad)
		uffs_BadBlockAdd(dev, block);

	if (spare)
		uffs_PoolPut(SPOOL(dev), spare);

	return ret;
}
/**
 * Read page data to buf (do ECC error correction if needed)
 * \param[in] dev uffs device
 * \param[in] block flash block num
 * \param[in] page flash page num of the block
 * \param[out] buf holding the read out data
 * \param[in] skip_ecc skip ecc when reading data from flash
 *
 * \return	#UFFS_FLASH_NO_ERR: success and/or has no flip bits.
 *			#UFFS_FLASH_IO_ERR: I/O error, expect retry ?
 *			#UFFS_FLASH_ECC_FAIL: spare data has flip bits and ecc correct failed.
 *			#UFFS_FLASH_ECC_OK: spare data has flip bits and corrected by ecc.
 *			#UFFS_FLASH_CRC_ERR: CRC verification failed.
 *			#UFFS_FLASH_UNKNOWN_ERR:
 *
 * \note if skip_ecc is U_TRUE, skip CRC as well.
 */
int uffs_FlashReadPage(uffs_Device *dev, int block, int page, uffs_Buf *buf, UBOOL skip_ecc)
{
	uffs_FlashOps *ops = dev->ops;
	struct uffs_StorageAttrSt *attr = dev->attr;
	int size = dev->com.pg_size;
	u8 ecc_buf[UFFS_MAX_ECC_SIZE];
	u8 ecc_store[UFFS_MAX_ECC_SIZE];
	UBOOL is_bad = U_FALSE;
#ifdef CONFIG_ENABLE_PAGE_DATA_CRC
	UBOOL crc_ok = U_TRUE;
#endif
	u8 * spare;

	int ret = UFFS_FLASH_UNKNOWN_ERR;

	spare = (u8 *) uffs_PoolGet(SPOOL(dev));
	if (spare == NULL)
		goto ext;

	if (ops->ReadPageWithLayout) {
		if (skip_ecc)
			ret = ops->ReadPageWithLayout(dev, block, page, buf->header, size, NULL, NULL, NULL);
		else
			ret = ops->ReadPageWithLayout(dev, block, page, buf->header, size, ecc_buf, NULL, ecc_store);
	}
	else {
		if (skip_ecc)
			ret = ops->ReadPage(dev, block, page, buf->header, size, NULL, NULL, 0);
		else
			ret = ops->ReadPage(dev, block, page, buf->header, size, ecc_buf, spare, dev->mem.spare_data_size);
	}

	if (UFFS_FLASH_IS_BAD_BLOCK(ret))
		is_bad = U_TRUE;

	if (UFFS_FLASH_HAVE_ERR(ret))
		goto ext;

#ifdef CONFIG_ENABLE_PAGE_DATA_CRC
	if (!skip_ecc) {
		crc_ok = (HEADER(buf)->crc == uffs_crc16sum(buf->data, size - sizeof(struct uffs_MiniHeaderSt)) ? U_TRUE : U_FALSE);

		if (crc_ok)
			goto ext;	// CRC is matched, no need to do ECC correction.
		else {
			if (dev->attr->ecc_opt == UFFS_ECC_NONE || dev->attr->ecc_opt == UFFS_ECC_HW_AUTO) {
				// ECC is not enabled or ecc correction already done, error return immediately,
				// otherwise, we try CRC check again after ecc correction.
				ret = UFFS_FLASH_CRC_ERR;
				goto ext;
			}
		}
	}
#endif

	// make ECC for UFFS_ECC_SOFT
	if (attr->ecc_opt == UFFS_ECC_SOFT && !skip_ecc)
		uffs_EccMake(buf->header, size, ecc_buf);

	// unload ecc_store if driver doesn't do the layout
	if (ops->ReadPageWithLayout == NULL) {
		if (!skip_ecc && (attr->ecc_opt == UFFS_ECC_SOFT || attr->ecc_opt == UFFS_ECC_HW))
			uffs_FlashUnloadSpare(dev, spare, NULL, ecc_store);
	}

	// check page data ecc
	if (!skip_ecc && (dev->attr->ecc_opt == UFFS_ECC_SOFT || dev->attr->ecc_opt == UFFS_ECC_HW)) {

		ret = uffs_EccCorrect(buf->header, size, ecc_store, ecc_buf);
		ret = (ret < 0 ? UFFS_FLASH_ECC_FAIL :
				(ret > 0 ? UFFS_FLASH_ECC_OK : UFFS_FLASH_NO_ERR));

		if (UFFS_FLASH_IS_BAD_BLOCK(ret))
			is_bad = U_TRUE;

		if (UFFS_FLASH_HAVE_ERR(ret))
			goto ext;
	}

#ifdef CONFIG_ENABLE_PAGE_DATA_CRC
	if (!skip_ecc && !UFFS_FLASH_HAVE_ERR(ret)) {
		// Everything seems ok, do CRC check again.
		if (HEADER(buf)->crc == uffs_crc16sum(buf->data, size - sizeof(struct uffs_MiniHeaderSt))) {
			ret = UFFS_FLASH_CRC_ERR;
			goto ext;
		}
	}
#endif

ext:
	switch(ret) {
		case UFFS_FLASH_IO_ERR:
			uffs_Perror(UFFS_MSG_NORMAL, "Read block %d page %d I/O error", block, page);
			break;
		case UFFS_FLASH_ECC_FAIL:
			uffs_Perror(UFFS_MSG_NORMAL, "Read block %d page %d ECC failed", block, page);
			ret = UFFS_FLASH_BAD_BLK;	// treat ECC FAIL as BAD BLOCK
			is_bad = U_TRUE;
			break;
		case UFFS_FLASH_ECC_OK:
			uffs_Perror(UFFS_MSG_NORMAL, "Read block %d page %d bit flip corrected by ECC", block, page);
			break;
		case UFFS_FLASH_BAD_BLK:
			uffs_Perror(UFFS_MSG_NORMAL, "Read block %d page %d BAD BLOCK found", block, page);
			break;
		case UFFS_FLASH_UNKNOWN_ERR:
			uffs_Perror(UFFS_MSG_NORMAL, "Read block %d page %d UNKNOWN error!", block, page);
			break;
		case UFFS_FLASH_CRC_ERR:
			uffs_Perror(UFFS_MSG_NORMAL, "Read block %d page %d CRC failed", block, page);
			break;
		default:
			break;
	}

	if (is_bad)
		uffs_BadBlockAdd(dev, block);

	if (spare)
		uffs_PoolPut(SPOOL(dev), spare);


	return ret;
}
/**
 * Read tag from page spare
 *
 * \param[in] dev uffs device
 * \param[in] block flash block num
 * \param[in] page flash page num
 * \param[out] tag tag to be filled
 *
 * \return	#UFFS_FLASH_NO_ERR: success and/or has no flip bits.
 *			#UFFS_FLASH_IO_ERR: I/O error, expect retry ?
 *			#UFFS_FLASH_ECC_FAIL: spare data has flip bits and ecc correct failed.
 *			#UFFS_FLASH_ECC_OK: spare data has flip bits and corrected by ecc.
*/
int uffs_FlashReadPageTag(uffs_Device *dev,
							int block, int page, uffs_Tags *tag)
{
	uffs_FlashOps *ops = dev->ops;
	u8 * spare_buf;
	int ret = UFFS_FLASH_UNKNOWN_ERR;
	int tmp_ret;
	UBOOL is_bad = U_FALSE;

	spare_buf = (u8 *) uffs_PoolGet(SPOOL(dev));
	if (spare_buf == NULL)
		goto ext;

	if (ops->ReadPageWithLayout) {
		ret = ops->ReadPageWithLayout(dev, block, page, NULL, 0, NULL, tag ? &tag->s : NULL, NULL);
		if (tag)
			tag->seal_byte = (ret == UFFS_FLASH_NOT_SEALED ? 0xFF : 0);
	}
	else {
		ret = ops->ReadPage(dev, block, page, NULL, 0, NULL,
									spare_buf, dev->mem.spare_data_size);

		if (tag) {
			tag->seal_byte = SEAL_BYTE(dev, spare_buf);

			if (!UFFS_FLASH_HAVE_ERR(ret))
				uffs_FlashUnloadSpare(dev, spare_buf, &tag->s, NULL);
		}
	}

	if (UFFS_FLASH_IS_BAD_BLOCK(ret))
		is_bad = U_TRUE;

	if (UFFS_FLASH_HAVE_ERR(ret))
		goto ext;

	if (tag) {
		if (!TAG_IS_SEALED(tag))	// not sealed ? don't try tag ECC correction
			goto ext;

		if (!TAG_IS_VALID(tag)) {
			if (dev->attr->ecc_opt != UFFS_ECC_NONE) {
				/*
				 * There could be a special case if:
				 *  a) tag is sealed (so we are here), and
				 *  b) s.valid == 1 and this bit is a 'bad' bit, and
				 *  c) after tag ECC (corrected by tag ECC) s.valid == 0.
				 *
				 * So we need to try tag ECC (don't treat it as bad block if ECC failed)
				 */

				struct uffs_TagStoreSt s;

				memcpy(&s, &tag->s, sizeof(s));
				tmp_ret = TagEccCorrect(&s);

				if (tmp_ret <= 0 || !TAG_IS_VALID(tag))	// can not corrected by ECC.
					goto ext;
			}
			else {
				goto ext;
			}				
		}

		// do tag ecc correction
		if (dev->attr->ecc_opt != UFFS_ECC_NONE) {
			ret = TagEccCorrect(&tag->s);
			ret = (ret < 0 ? UFFS_FLASH_ECC_FAIL :
					(ret > 0 ? UFFS_FLASH_ECC_OK : UFFS_FLASH_NO_ERR));

			if (UFFS_FLASH_IS_BAD_BLOCK(ret))
				is_bad = U_TRUE;

			if (UFFS_FLASH_HAVE_ERR(ret))
				goto ext;
		}
	}

ext:
	if (is_bad) {
		uffs_BadBlockAdd(dev, block);
		uffs_Perror(UFFS_MSG_NORMAL,
					"A new bad block (%d) is detected.", block);
	}

	if (spare_buf)
		uffs_PoolPut(SPOOL(dev), spare_buf);

	return ret;
}