static int program_sdata(uffs_Device *dev, int block, int page) { uffs_FileEmu *emu = (uffs_FileEmu *)(dev->attr->_private); int abs_page; struct uffs_StorageAttrSt *attr = dev->attr; u8 ecc_buf[RS_ECC_SIZE]; int writtern; // In the real world, MLC controller will generate RS-ECC code in serial data buffer // and might start auto programing NAND flash. Here, we use software ECC to emulate RS-ECC. memset(ecc_buf, 0xFF, sizeof(ecc_buf)); uffs_EccMake(g_sdata_buf, attr->page_data_size, ecc_buf); feed_sdata(ecc_buf, RS_ECC_SIZE); uffs_Assert(g_sdata_buf_pointer == PAGE_FULL_SIZE, "Serial Data Buffer is not fully filled !!"); abs_page = attr->pages_per_block * block + page; fseek(emu->fp, abs_page * PAGE_FULL_SIZE, SEEK_SET); writtern = fwrite(g_sdata_buf, 1, PAGE_FULL_SIZE, emu->fp); return (writtern == PAGE_FULL_SIZE) ? UFFS_FLASH_NO_ERR : UFFS_FLASH_IO_ERR; }
static int load_sdata(uffs_Device *dev, int block, int page) { uffs_FileEmu *emu = (uffs_FileEmu *)(dev->attr->_private); int abs_page; struct uffs_StorageAttrSt *attr = dev->attr; int nread; int ret; u8 ecc_buf[RS_ECC_SIZE]; u8 *ecc_store; abs_page = attr->pages_per_block * block + page; fseek(emu->fp, abs_page * PAGE_FULL_SIZE, SEEK_SET); nread = fread(g_sdata_buf, 1, PAGE_FULL_SIZE, emu->fp); g_sdata_buf_pointer = 0; ret = ((nread == PAGE_FULL_SIZE) ? UFFS_FLASH_NO_ERR : UFFS_FLASH_IO_ERR); if (ret == UFFS_FLASH_NO_ERR) { // Perform ECC check & correction // In the real world, this is done by MLC controller hardware memset(ecc_buf, 0xFF, RS_ECC_SIZE); uffs_EccMake(g_sdata_buf, attr->page_data_size, ecc_buf); ecc_store = g_sdata_buf + PAGE_FULL_SIZE - RS_ECC_SIZE; ret = uffs_EccCorrect(g_sdata_buf, attr->page_data_size, ecc_store, ecc_buf); ret = (ret < 0 ? UFFS_FLASH_ECC_FAIL : (ret > 0 ? UFFS_FLASH_ECC_OK : UFFS_FLASH_NO_ERR)); } return ret; }
static int femu_hw_WritePageWithLayout(uffs_Device *dev, u32 block, u32 page, const u8 *data, int data_len, const u8 *ecc, const uffs_TagStore *ts) { int written; int abs_page; int full_page_size; uffs_FileEmu *emu; struct uffs_StorageAttrSt *attr = dev->attr; u8 spare[UFFS_MAX_SPARE_SIZE]; u8 ecc_buf[UFFS_MAX_ECC_SIZE]; int spare_len; emu = (uffs_FileEmu *)(dev->attr->_private); if (!emu || !(emu->fp)) { goto err; } abs_page = attr->pages_per_block * block + page; full_page_size = attr->page_data_size + attr->spare_size; if (data && data_len > 0) { if (data_len > attr->page_data_size) goto err; emu->em_monitor_page[abs_page]++; if (emu->em_monitor_page[abs_page] > PAGE_DATA_WRITE_COUNT_LIMIT) { MSG("Warrning: block %d page %d exceed it's maximum write time!", block, page); goto err; } fseek(emu->fp, abs_page * full_page_size, SEEK_SET); written = fwrite(data, 1, data_len, emu->fp); if (written != data_len) { MSG("write page I/O error ?"); goto err; } dev->st.page_write_count++; dev->st.io_write += written; } if (ts) { emu->em_monitor_spare[abs_page]++; if (emu->em_monitor_spare[abs_page] > PAGE_SPARE_WRITE_COUNT_LIMIT) { MSG("Warrning: block %d page %d (spare) exceed it's maximum write time!", block, page); goto err; } uffs_Assert(data != NULL, "BUG: Write spare without data ?"); uffs_EccMake(data, data_len, ecc_buf); uffs_FlashMakeSpare(dev, ts, ecc_buf, spare); spare_len = dev->mem.spare_data_size; fseek(emu->fp, abs_page * full_page_size + attr->page_data_size, SEEK_SET); written = fwrite(spare, 1, spare_len, emu->fp); if (written != spare_len) { MSG("write spare I/O error ?"); goto err; } dev->st.spare_write_count++; dev->st.io_write += written; } if (data == NULL && ts == NULL) { // mark bad block fseek(emu->fp, abs_page * full_page_size + attr->page_data_size + attr->block_status_offs, SEEK_SET); written = fwrite("\0", 1, 1, emu->fp); if (written != 1) { MSG("write bad block mark I/O error ?"); goto err; } dev->st.io_write++; } fflush(emu->fp); return UFFS_FLASH_NO_ERR; err: fflush(emu->fp); return UFFS_FLASH_IO_ERR; }
static URET femu_hw_ReadPageWithLayout(uffs_Device *dev, u32 block, u32 page, u8* data, int data_len, u8 *ecc, uffs_TagStore *ts, u8 *ecc_store) { int nread; uffs_FileEmu *emu; int abs_page; int full_page_size; struct uffs_StorageAttrSt *attr = dev->attr; unsigned char status; u8 spare[UFFS_MAX_SPARE_SIZE]; int spare_len; emu = (uffs_FileEmu *)(dev->attr->_private); if (!emu || !(emu->fp)) { goto err; } abs_page = attr->pages_per_block * block + page; full_page_size = attr->page_data_size + attr->spare_size; if (data && data_len > 0) { if (data_len > attr->page_data_size) goto err; fseek(emu->fp, abs_page * full_page_size, SEEK_SET); nread = fread(data, 1, data_len, emu->fp); if (nread != data_len) { MSG("read page I/O error ?"); goto err; } dev->st.io_read += nread; dev->st.page_read_count++; if (ecc) { // calculate ECC for data uffs_EccMake(data, data_len, ecc); } } if (ts) { spare_len = dev->mem.spare_data_size; fseek(emu->fp, abs_page * full_page_size + attr->page_data_size, SEEK_SET); nread = fread(spare, 1, spare_len, emu->fp); if (nread != spare_len) { MSG("read page spare I/O error ?"); goto err; } // unload ts and ecc from spare uffs_FlashUnloadSpare(dev, spare, ts, ecc_store); dev->st.io_read += nread; dev->st.spare_read_count++; } if (data == NULL && ts == NULL) { // read bad block mark fseek(emu->fp, abs_page * full_page_size + attr->page_data_size + attr->block_status_offs, SEEK_SET); nread = fread(&status, 1, 1, emu->fp); if (nread != 1) { MSG("read badblock mark I/O error ?"); goto err; } dev->st.io_read++; return status == 0xFF ? UFFS_FLASH_NO_ERR : UFFS_FLASH_BAD_BLK; } return UFFS_FLASH_NO_ERR; err: return UFFS_FLASH_IO_ERR; }
/** * 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; }