Esempio n. 1
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;
}
Esempio n. 2
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;
}
Esempio n. 3
0
/** 
 * \brief find a valid page with given page_id
 * \param[in] dev uffs device
 * \param[in] bc block info
 * \param[in] page_id page_id to be find
 * \return the valid page number which has given page_id
 * \retval >=0 page number
 * \retval UFFS_INVALID_PAGE page not found
 */
u16 uffs_FindPageInBlockWithPageId(uffs_Device *dev,
								   uffs_BlockInfo *bc, u16 page_id)
{
	u16 page;
	uffs_Tags *tag;

	//Indeed, the page which has page_id, should ahead of page_id ...
	for (page = page_id; page < dev->attr->pages_per_block; page++) {
		uffs_BlockInfoLoad(dev, bc, page);
		tag = &(bc->spares[page].tag);
		if (TAG_IS_GOOD(tag) && TAG_PAGE_ID(tag) == page_id)
			return page;
	}

	return UFFS_INVALID_PAGE;
}
Esempio n. 4
0
/** 
 * get free pages number
 * \param[in] dev uffs device
 * \param[in] bc block info
 */
int uffs_GetFreePagesCount(uffs_Device *dev, uffs_BlockInfo *bc)
{
	int count = 0;
	int i;

	// search from the last page ... to first page
	for (i = dev->attr->pages_per_block - 1; i >= 0; i--) {
		uffs_BlockInfoLoad(dev, bc, i);
		if (uffs_IsPageErased(dev, bc, (u16)i) == U_TRUE) {
			count++;
		}
		else {
			if (TAG_IS_GOOD(GET_TAG(bc, i)))  // it won't be any free page if we see a good tag.
				break;
		}
	}

	return count;
}
Esempio n. 5
0
static URET _BuildValidTreeNode(uffs_Device *dev,
								TreeNode *node,		//!< empty node
								uffs_BlockInfo *bc,
								struct BlockTypeStatSt *st)
{
	uffs_Tags *tag;
	TreeNode *node_alt;
	u16 block, parent, serial, block_alt, block_save;
	uffs_BlockInfo *bc_alt;
	u8 type;
	int page;
	UBOOL needToInsertToTree = U_FALSE;
	uffs_Buf *buf = NULL;
	uffs_FileInfo *info;
	u16 data_sum = 0;

	// check the first page on the block ...
	uffs_BlockInfoLoad(dev, bc, 0);

	tag = GET_TAG(bc, 0);  //get first page's tag

	if (!TAG_IS_DIRTY(tag)) {
		// should never go here ... unless mark dirty page failed ?
		uffs_Perror(UFFS_MSG_NORMAL,
					"First page is clean in a non-erased block ?");
		return U_FAIL;
	}

	if (!TAG_IS_VALID(tag)) {
		//first page is invalid ? should be erased now!
		uffs_Perror(UFFS_MSG_NORMAL,
					"first page in block %d is invalid, will be erased now!",
					bc->block);
		goto process_invalid_block;		
	}

	block = bc->block;
	parent = TAG_PARENT(tag);
	serial = TAG_SERIAL(tag);
	type = TAG_TYPE(tag);

	// check if there is an 'alternative block' 
	// (node which has the same serial number) in tree ?
	node_alt = uffs_FindFromTree(dev, type, parent, serial); 

	if (node_alt != NULL) {
		//find a alternate node ! need to check the timestamp !

		block_alt = _GetBlockFromNode(type, node_alt);

		uffs_Perror(UFFS_MSG_NORMAL,
					"Process unclean block (%d vs %d)", block, block_alt);

		if (block_alt == INVALID_UFFS_SERIAL) {
			uffs_Perror(UFFS_MSG_SERIOUS, "invalid block ?");
			return U_FAIL;
		}
		
		bc_alt = uffs_BlockInfoGet(dev, block_alt);
		if (bc_alt == NULL) {
			uffs_Perror(UFFS_MSG_SERIOUS, "can't get block info ");
			return U_FAIL;
		}
		uffs_BlockInfoLoad(dev, bc_alt, 0);
		if (uffs_IsSrcNewerThanObj (
				TAG_BLOCK_TS(tag),
				TAG_BLOCK_TS(GET_TAG(bc_alt, 0))) == U_TRUE) {

			//the node is newer than node_alt, so keep node_alt, and erase node
			uffs_FlashEraseBlock(dev, block);
			node->u.list.block = block;
			if (HAVE_BADBLOCK(dev))
				uffs_BadBlockProcess(dev, node);
			else
				uffs_TreeInsertToErasedListTail(dev, node);

			uffs_BlockInfoPut(dev, bc_alt);  //put back bc_alt before we return.
			return U_SUCC;
		}
		else {
			//the node is older than node_alt, so keep node, and erase node_alt
			//we use node as erased node to insert to erased list

			block_save = _GetBlockFromNode(type, node_alt);
			uffs_FlashEraseBlock(dev, block_save);
			node->u.list.block = block_save;
			if (HAVE_BADBLOCK(dev))
				uffs_BadBlockProcess(dev, node);
			else
				uffs_TreeInsertToErasedListTail(dev, node);

			//put back bc_alt because we don't need it anymore.
			uffs_BlockInfoPut(dev, bc_alt);
			
			//use node_alt to store new informations in following
			node = node_alt;

			needToInsertToTree = U_FALSE;
		}
	}
	else {
		needToInsertToTree = U_TRUE;
	}

	if (type == UFFS_TYPE_DIR || type == UFFS_TYPE_FILE) {
		buf = uffs_BufClone(dev, NULL);
		if (buf == NULL)
			return U_FAIL;
		uffs_BlockInfoLoad(dev, bc, UFFS_ALL_PAGES);
		page = uffs_FindPageInBlockWithPageId(dev, bc, 0);
		if (page == UFFS_INVALID_PAGE) {
			uffs_BufFreeClone(dev, buf);
			uffs_Perror(UFFS_MSG_SERIOUS,
				"Can't find any valid page for page_id=0 ? invalid block !"
				"this might be caused by the tag layout change.\n");
			goto process_invalid_block;
		}
		page = uffs_FindBestPageInBlock(dev, bc, page);
		uffs_FlashReadPage(dev, block, page, buf, U_FALSE);
		info = (uffs_FileInfo *) (buf->data);
		data_sum = uffs_MakeSum16(info->name, info->name_len);
		uffs_BufFreeClone(dev, buf);
	}

	switch (type) {
	case UFFS_TYPE_DIR:
		node->u.dir.block = bc->block;
		node->u.dir.checksum = data_sum;
		node->u.dir.parent = TAG_PARENT(tag);
		node->u.dir.serial = TAG_SERIAL(tag);
		st->dir++;
		break;
	case UFFS_TYPE_FILE:
		node->u.file.block = bc->block;
		node->u.file.checksum = data_sum;
		node->u.file.parent = TAG_PARENT(tag);
		node->u.file.serial = TAG_SERIAL(tag);
		node->u.file.len = uffs_GetBlockFileDataLength(dev, bc, UFFS_TYPE_FILE);  
		st->file++;
		break;
	case UFFS_TYPE_DATA:
		node->u.data.block = bc->block;
		node->u.data.parent = TAG_PARENT(tag);
		node->u.data.serial = TAG_SERIAL(tag);
		node->u.data.len = uffs_GetBlockFileDataLength(dev, bc, UFFS_TYPE_DATA); 
		st->data++;
		break;
	}

	if (needToInsertToTree == U_TRUE) {
		uffs_InsertNodeToTree(dev, type, node);
	}

	return U_SUCC;

process_invalid_block:
	/* erase the invalid block */
	uffs_FlashEraseBlock(dev, bc->block);

	node->u.list.block = bc->block;

	if (HAVE_BADBLOCK(dev))
		uffs_BadBlockProcess(dev, node);
	else
		uffs_TreeInsertToErasedListTail(dev, node);

	return U_SUCC;
}