/** * \brief load page spare data to given block info structure * with given page number * \param[in] dev uffs device * \param[in] work given block info to be filled with * \param[in] page given page number to be read from, * if #UFFS_ALL_PAGES is presented, it will read * all pages, otherwise it will read only one given page. * \return load result * \retval U_SUCC successful * \retval U_FAIL fail to load * \note work->block must be set before load block info */ URET uffs_BlockInfoLoad(uffs_Device *dev, uffs_BlockInfo *work, int page) { int i, ret; uffs_PageSpare *spare; if (page == UFFS_ALL_PAGES) { for (i = 0; i < dev->attr->pages_per_block; i++) { spare = &(work->spares[i]); if (spare->expired == 0) continue; ret = uffs_FlashReadPageTag(dev, work->block, i, &(spare->tag)); if (UFFS_FLASH_HAVE_ERR(ret)) { uffs_Perror(UFFS_MSG_SERIOUS, "load block %d page %d spare fail.", work->block, i); return U_FAIL; } spare->expired = 0; work->expired_count--; } } else { if (page < 0 || page >= dev->attr->pages_per_block) { uffs_Perror(UFFS_MSG_SERIOUS, "page out of range !"); return U_FAIL; } spare = &(work->spares[page]); if (spare->expired != 0) { ret = uffs_FlashReadPageTag(dev, work->block, page, &(spare->tag)); if (UFFS_FLASH_HAVE_ERR(ret)) { uffs_Perror(UFFS_MSG_SERIOUS, "load block %d page %d spare fail.", work->block, page); return U_FAIL; } spare->expired = 0; work->expired_count--; } } return U_SUCC; }
static void DumpBlock(struct uffs_DeviceSt *dev, int block, dump_msg_cb *dump) { int i; struct uffs_StorageAttrSt *attr = dev->attr; uffs_Tags tag; URET ret; dump(dev, "--- Block %d ---\n", block); if (uffs_FlashIsBadBlock(dev, block)) { dump(dev, "Bad block\n\n"); return; } for (i = 0; i < attr->pages_per_block; i++) { memset(&tag, 0xFF, sizeof(tag)); ret = uffs_FlashReadPageTag(dev, block, i, &tag); if (ret == UFFS_FLASH_IO_ERR) { dump(dev, "page %d tag I/O error\n", i); continue; } else if (ret == UFFS_FLASH_ECC_FAIL) { dump(dev, "page %d tag ECC error\n", i); continue; } else if (ret == UFFS_FLASH_NO_ERR || ret == UFFS_FLASH_ECC_OK) { if (ret == UFFS_FLASH_ECC_OK) dump(dev, "page %d tag has bit flip, corrected by ECC\n", i); if (DumpTag(dev, block, i, &tag, dump) == 0) continue; else break; } else { dump(dev, "read page %d tag return unexpected: %d\n", i, ret); continue; } } dump(dev, "\n"); }
static void do_dump_device(uffs_Device *dev) { URET ret; int block, page; uffs_Tags tag; uffs_Buf *buf; buf = uffs_BufClone(dev, NULL); if (buf == NULL) { MSGLN("Can't clone buf"); return; } for (block = dev->par.start; block <= dev->par.end; block++) { MSG("---- block %d ----\n", block); for (page = 0; page < dev->attr->pages_per_block; page++) { MSG(" == page %d ==\n", page); ret = uffs_FlashReadPage(dev, block, page, buf, U_FALSE); if (UFFS_FLASH_HAVE_ERR(ret)) { MSG(" !!! Read page failed, ret = %d !!!\n", ret); } else { do_dump_page(dev, buf); if (buf->header[0] != 0xFF) { ret = uffs_FlashReadPageTag(dev, block, page, &tag); if (UFFS_FLASH_HAVE_ERR(ret)) { MSG(" !!! Read TAG failed, ret = %d !!!\n", ret); } else { do_dump_tag(dev, &tag); } } } } } uffs_BufFreeClone(dev, buf); }
/* 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; }
/* usage: t_pgrw * * This test case test page read/write */ static BOOL cmdTestPageReadWrite(const char *tail) { TreeNode *node; uffs_Device *dev; uffs_Tags local_tag; uffs_Tags *tag = &local_tag; int ret; u16 block; u16 page; uffs_Buf *buf; u32 i; 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_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; 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); 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_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."); ext: if (node) { uffs_FlashEraseBlock(dev, node->u.list.block); if (HAVE_BADBLOCK(dev)) uffs_BadBlockProcess(dev, node); else uffs_InsertToErasedListHead(dev, node); } if (dev) uffs_PutDevice(dev); if (buf) uffs_BufFreeClone(dev, buf); return TRUE; }
/** * 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; }