// return -1 if do not need to read next tag static int DumpTag(struct uffs_DeviceSt *dev, int block, int page, uffs_Tags *tag, dump_msg_cb *dump) { struct uffs_TagStoreSt *s = &tag->s; struct uffs_MiniHeaderSt header; URET ret; if (!TAG_IS_DIRTY(tag)) { // is a clean page ? ret = uffs_LoadMiniHeader(dev, block, page, &header); if (ret == U_FAIL) { dump(dev, "Fail to load mini header from page 0\n"); } else { if (header.status == 0xFF) dump(dev, "page %d CLEAN\n", page); else { dump(dev, "page %d NOT clean ! header: ", page); DumpBufHex(dev, (u8 *)&header, sizeof(header), dump); dump(dev, ", tag: "); DumpBufHex(dev, (u8 *)s, sizeof(struct uffs_TagStoreSt), dump); dump(dev, "\n"); } } return -1; } dump(dev, " - page %2d/%2d %s %d/%d len%4d\n", page, s->page_id, GetTagName(s), s->serial, s->parent, s->data_len); return 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; }
static URET _BuildTreeStepOne(uffs_Device *dev) { int block_lt; uffs_BlockInfo *bc = NULL; TreeNode *node; struct uffs_TreeSt *tree; uffs_Pool *pool; struct uffs_MiniHeaderSt header; URET ret = U_SUCC; struct BlockTypeStatSt st = {0, 0, 0}; tree = &(dev->tree); pool = TPOOL(dev); tree->bad = NULL; tree->bad_count = 0; tree->erased = NULL; tree->erased_tail = NULL; tree->erased_count = 0; uffs_Perror(UFFS_MSG_NOISY, "build tree step one"); // printf("s:%d e:%d\n", dev->par.start, dev->par.end); for (block_lt = dev->par.start; block_lt <= dev->par.end; block_lt++) { bc = uffs_BlockInfoGet(dev, block_lt); if (bc == NULL) { uffs_Perror(UFFS_MSG_SERIOUS, "step one:fail to get block info"); ret = U_FAIL; break; } node = (TreeNode *)uffs_PoolGet(pool); if (node == NULL) { uffs_Perror(UFFS_MSG_SERIOUS, "insufficient tree node!"); ret = U_FAIL; break; } // Need to check bad block at first ! if (uffs_FlashIsBadBlock(dev, block_lt) == U_TRUE) { node->u.list.block = block_lt; uffs_TreeInsertToBadBlockList(dev, node); uffs_Perror(UFFS_MSG_NORMAL, "found bad block %d", block_lt); } else if (uffs_IsPageErased(dev, bc, 0) == U_TRUE) { //@ read one spare: 0 // page 0 tag shows it's an erased block, we need to check the mini header status to make sure it is clean. if (uffs_LoadMiniHeader(dev, block_lt, 0, &header) == U_FAIL) { uffs_Perror(UFFS_MSG_SERIOUS, "I/O error when reading mini header !" "block %d page %d", block_lt, 0); ret = U_FAIL; break; } if (header.status != 0xFF) { // page 0 tag is clean but page data is dirty ??? // this block should be erased immediately ! uffs_FlashEraseBlock(dev, block_lt); } node->u.list.block = block_lt; if (HAVE_BADBLOCK(dev)) { uffs_Perror(UFFS_MSG_NORMAL, "New bad block (%d) discovered.", block_lt); uffs_BadBlockProcess(dev, node); } else { // page 0 is clean does not means all pages in this block are clean, // need to check this block later before use it. uffs_TreeInsertToErasedListTailEx(dev, node, 1); } } else { // this block have valid data page(s). ret = _ScanAndFixUnCleanPage(dev, bc); if (ret == U_FAIL) break; ret = _BuildValidTreeNode(dev, node, bc, &st); if (ret == U_FAIL) break; } uffs_BlockInfoPut(dev, bc); } //end of for if(ret == U_FAIL) uffs_BlockInfoPut(dev, bc); uffs_Perror(UFFS_MSG_NORMAL, "DIR %d, FILE %d, DATA %d", st.dir, st.file, st.data); return ret; }