Beispiel #1
0
static void do_dump_tag(uffs_Device *dev, uffs_Tags *tag)
{
	MSG("  tag sealed: %s\n", TAG_IS_SEALED(tag) ? "yes" : "no");
	if (TAG_IS_GOOD(tag)) {
		if (TAG_IS_DIRTY(tag)) {
			MSG("    block_ts = %d\n", tag->s.block_ts);
			MSG("    type = %d\n", tag->s.type);
			MSG("    dirty = %d\n", tag->s.dirty);
			MSG("    page_id = %d\n", tag->s.page_id);
			MSG("    serial = %d\n", tag->s.serial);
			MSG("    parent = %d\n", tag->s.parent);
			MSG("    data_len = %d\n", tag->s.data_len);
		}
		else {
			MSG("  tag is GOOD but NOT DIRTY !!!???\n");
		}
	}
	else if (TAG_IS_SEALED(tag)) {
		MSG(" tag is INVALID\n");
	}
}
Beispiel #2
0
static URET _ScanAndFixUnCleanPage(uffs_Device *dev, uffs_BlockInfo *bc)
{
	int page;
	uffs_Tags *tag;
	struct uffs_MiniHeaderSt header;

	/* in most case, the valid block contents fewer free page,
		so it's better scan from the last page ... to page 1.
		note: scanning page 0 is not necessary, will check it later.

		The worse case: read (pages_per_block - 1) * (mini header + spares) !
		most case: read one spare.
	*/
	for (page = dev->attr->pages_per_block - 1; page > 0; page--) {
		uffs_BlockInfoLoad(dev, bc, page);
		tag = GET_TAG(bc, page);

		if (TAG_IS_SEALED(tag))
			break;	// tag sealed, no unclean page in this block.

		if (TAG_IS_DIRTY(tag) || TAG_IS_VALID(tag)) {  // tag not sealed but dirty/valid ?
			uffs_Perror(UFFS_MSG_NORMAL,
					"unclean page found, block %d page %d",
					bc->block, page);

			// ok, an unclean page found.
			// This unclean page can be identified by tag.
			// We can leave it as it is, but performing a block recover would be good ?
			// There won't be another unclean page in this block ... stop here.
			break;
		}

		// now we have a clean tag (all 0xFF ?). Need to check mini header to see if it's an unclean page.
		if (uffs_LoadMiniHeader(dev, bc->block, page, &header) == U_FAIL)
			return U_FAIL;

		if (header.status != 0xFF) {
			// page data is dirty? this is an unclean page and we should explicitly mark tag as 'dirty and invalid'.
			// This writing does not violate "no partial program" claim, because we are writing to a clean page spare.
			uffs_Perror(UFFS_MSG_NORMAL,
						"unclean page found, block %d page %d, mark it.",
						bc->block, page);
			uffs_FlashMarkDirtyPage(dev, bc, page);
		}
	}

	return U_SUCC;
}
Beispiel #3
0
/** 
 * \brief Is the block erased ?
 * \param[in] dev uffs device
 * \param[in] bc block info
 * \param[in] page page number to be check
 * \retval U_TRUE block is erased, ready to use
 * \retval U_FALSE block is dirty, maybe use by file
 */
UBOOL uffs_IsPageErased(uffs_Device *dev, uffs_BlockInfo *bc, u16 page)
{
	uffs_Tags *tag;
	URET ret;

	ret = uffs_BlockInfoLoad(dev, bc, page);
	if (ret == U_SUCC) {
		tag = GET_TAG(bc, page);
		if (!TAG_IS_SEALED(tag) &&
			!TAG_IS_DIRTY(tag) &&
			!TAG_IS_VALID(tag)) {
			return U_TRUE;
		}
	}
	return U_FALSE;
}
Beispiel #4
0
/* usage: t_pgrw
 *
 * This test case test page read/write
 */
static int cmd_TestPageReadWrite(int argc, char *argv[])
{
	TreeNode *node = NULL;
	uffs_Device *dev;
	uffs_Tags local_tag;
	uffs_Tags *tag = &local_tag;
	int ret;
	u16 block;
	u16 page;
	uffs_Buf *buf = NULL;

	u32 i;
	int rc = -1;

	dev = uffs_GetDeviceFromMountPoint("/");
	if (!dev)
		goto ext;

	buf = uffs_BufClone(dev, NULL);
	if (!buf)
		goto ext;

	node = uffs_TreeGetErasedNode(dev);
	if (!node) {
		MSGLN("no free block ?");
		goto ext;
	}

	for (i = 0; i < dev->com.pg_data_size; i++) {
		buf->data[i] = i & 0xFF;
	}

	block = node->u.list.block;
	page = 1;

	TAG_DIRTY_BIT(tag) = TAG_DIRTY;
	TAG_VALID_BIT(tag) = TAG_VALID;
	TAG_DATA_LEN(tag) = dev->com.pg_data_size;
	TAG_TYPE(tag) = UFFS_TYPE_DATA;
	TAG_PAGE_ID(tag) = 3;
	TAG_PARENT(tag) = 100;
	TAG_SERIAL(tag) = 10;
	TAG_BLOCK_TS(tag) = 1;
	SEAL_TAG(tag);

	ret = uffs_FlashWritePageCombine(dev, block, page, buf, tag);
	if (UFFS_FLASH_HAVE_ERR(ret)) {
		MSGLN("Write page error: %d", ret);
		goto ext;
	}

	ret = uffs_FlashReadPage(dev, block, page, buf, U_FALSE);
	if (UFFS_FLASH_HAVE_ERR(ret)) {
		MSGLN("Read page error: %d", ret);
		goto ext;
	}

	for (i = 0; i < dev->com.pg_data_size; i++) {
		if (buf->data[i] != (i & 0xFF)) {
			MSGLN("Data verify fail at: %d", i);
			goto ext;
		}
	}

	ret = uffs_FlashReadPageTag(dev, block, page, tag);
	if (UFFS_FLASH_HAVE_ERR(ret)) {
		MSGLN("Read tag (page spare) error: %d", ret);
		goto ext;
	}
	
	// verify tag:
	if (!TAG_IS_SEALED(tag)) {
		MSGLN("not sealed ? Tag verify fail!");
		goto ext;
	}

	if (!TAG_IS_DIRTY(tag)) {
		MSGLN("not dirty ? Tag verify fail!");
		goto ext;
	}

	if (!TAG_IS_VALID(tag)) {
		MSGLN("not valid ? Tag verify fail!");
		goto ext;
	}

	if (TAG_DATA_LEN(tag) != dev->com.pg_data_size ||
		TAG_TYPE(tag) != UFFS_TYPE_DATA ||
		TAG_PAGE_ID(tag) != 3 ||
		TAG_PARENT(tag) != 100 ||
		TAG_SERIAL(tag) != 10 ||
		TAG_BLOCK_TS(tag) != 1) {

		MSGLN("Tag verify fail!");
		goto ext;
	}

	MSGLN("Page read/write test succ.");
	rc = 0;

ext:
	if (node) {
		uffs_TreeEraseNode(dev, node);
		uffs_TreeInsertToErasedListTail(dev, node);
	}

	if (dev)
		uffs_PutDevice(dev);

	if (buf)
		uffs_BufFreeClone(dev, buf);

	return rc;
}
/**
 * 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;
}