git-svn-id: https://rt-thread.googlecode.com/svn/trunk@1245 bbd45198-f89e-11dd-88c7-29a3b14d5316
This commit is contained in:
parent
ef0d03aaff
commit
ac07efd463
|
@ -0,0 +1,126 @@
|
||||||
|
Import('RTT_ROOT')
|
||||||
|
Import('rtconfig')
|
||||||
|
from building import *
|
||||||
|
|
||||||
|
# The set of source files associated with this SConscript file.
|
||||||
|
dfs = Split("""
|
||||||
|
src/dfs.c
|
||||||
|
src/dfs_fs.c
|
||||||
|
src/dfs_file.c
|
||||||
|
src/dfs_posix.c
|
||||||
|
""")
|
||||||
|
|
||||||
|
# DFS-ELMFAT options
|
||||||
|
elmfat = Split("""
|
||||||
|
filesystems/elmfat/dfs_elm.c
|
||||||
|
filesystems/elmfat/ff.c
|
||||||
|
""")
|
||||||
|
|
||||||
|
# DFS-ROMFS options
|
||||||
|
romfs = Split("""
|
||||||
|
filesystems/romfs/dfs_romfs.c
|
||||||
|
filesystems/romfs/romfs.c
|
||||||
|
""")
|
||||||
|
|
||||||
|
# DFS-DeviceFS options
|
||||||
|
devfs = Split("""
|
||||||
|
filesystems/devfs/devfs.c
|
||||||
|
filesystems/devfs/console.c
|
||||||
|
""")
|
||||||
|
|
||||||
|
# DFS-YAFFS2 options
|
||||||
|
yaffs2_main = Split("""
|
||||||
|
filesystems/yaffs2/direct/yaffscfg.c
|
||||||
|
filesystems/yaffs2/direct/yaffs_fileem.c
|
||||||
|
filesystems/yaffs2/direct/yaffsfs.c
|
||||||
|
filesystems/yaffs2/direct/dfs_yaffs2.c
|
||||||
|
""")
|
||||||
|
|
||||||
|
yaffs2_comm = Split("""
|
||||||
|
filesystems/yaffs2/yaffs_ecc.c
|
||||||
|
filesystems/yaffs2/yaffs_guts.c
|
||||||
|
filesystems/yaffs2/yaffs_packedtags1.c
|
||||||
|
filesystems/yaffs2/yaffs_tagscompat.c
|
||||||
|
filesystems/yaffs2/yaffs_packedtags2.c
|
||||||
|
filesystems/yaffs2/yaffs_tagsvalidity.c
|
||||||
|
filesystems/yaffs2/yaffs_nand.c
|
||||||
|
filesystems/yaffs2/yaffs_checkptrw.c
|
||||||
|
filesystems/yaffs2/yaffs_qsort.c
|
||||||
|
""")
|
||||||
|
|
||||||
|
nfs = Split('''
|
||||||
|
filesystems/nfs/mount_clnt.c
|
||||||
|
filesystems/nfs/mount_xdr.c
|
||||||
|
filesystems/nfs/nfs_clnt.c
|
||||||
|
filesystems/nfs/nfs_xdr.c
|
||||||
|
filesystems/nfs/dfs_nfs.c
|
||||||
|
filesystems/nfs/rpc/auth_none.c
|
||||||
|
filesystems/nfs/rpc/clnt_generic.c
|
||||||
|
filesystems/nfs/rpc/clnt_udp.c
|
||||||
|
filesystems/nfs/rpc/rpc_prot.c
|
||||||
|
filesystems/nfs/rpc/pmap.c
|
||||||
|
filesystems/nfs/rpc/xdr.c
|
||||||
|
filesystems/nfs/rpc/xdr_mem.c
|
||||||
|
''')
|
||||||
|
|
||||||
|
uffs = Split('''
|
||||||
|
filesystems/uffs/src/uffs/uffs_badblock.c
|
||||||
|
filesystems/uffs/src/uffs/uffs_blockinfo.c
|
||||||
|
filesystems/uffs/src/uffs/uffs_buf.c
|
||||||
|
filesystems/uffs/src/uffs/uffs_debug.c
|
||||||
|
filesystems/uffs/src/uffs/uffs_device.c
|
||||||
|
filesystems/uffs/src/uffs/uffs_ecc.c
|
||||||
|
filesystems/uffs/src/uffs/uffs_fd.c
|
||||||
|
filesystems/uffs/src/uffs/uffs_find.c
|
||||||
|
filesystems/uffs/src/uffs/uffs_flash.c
|
||||||
|
filesystems/uffs/src/uffs/uffs_fs.c
|
||||||
|
filesystems/uffs/src/uffs/uffs_init.c
|
||||||
|
filesystems/uffs/src/uffs/uffs_mem.c
|
||||||
|
filesystems/uffs/src/uffs/uffs_mtb.c
|
||||||
|
filesystems/uffs/src/uffs/uffs_pool.c
|
||||||
|
filesystems/uffs/src/uffs/uffs_public.c
|
||||||
|
filesystems/uffs/src/uffs/uffs_tree.c
|
||||||
|
filesystems/uffs/src/uffs/uffs_utils.c
|
||||||
|
filesystems/uffs/src/uffs/uffs_version.c
|
||||||
|
filesystems/uffs/dfs_uffs.c
|
||||||
|
''')
|
||||||
|
|
||||||
|
others = '''
|
||||||
|
filesystems/uffs/dfs_nand_if.c
|
||||||
|
filesystems/uffs/uffs_ext.c
|
||||||
|
filesystems/uffs/flash/k9f2g08.c
|
||||||
|
filesystems/uffs/flash/nand_ids.c
|
||||||
|
'''
|
||||||
|
|
||||||
|
src_local = dfs
|
||||||
|
# The set of source files associated with this SConscript file.
|
||||||
|
path = [RTT_ROOT + '/components/dfs', RTT_ROOT + '/components/dfs/include']
|
||||||
|
|
||||||
|
if GetDepend('RT_USING_DFS_YAFFS2'):
|
||||||
|
src_local = src_local + yaffs2_main + yaffs2_comm
|
||||||
|
path = path + [RTT_ROOT + '/components/dfs/filesystems/yaffs2', RTT_ROOT + '/components/dfs/filesystems/yaffs2/direct']
|
||||||
|
|
||||||
|
if GetDepend('RT_USING_DFS_ELMFAT'):
|
||||||
|
if GetDepend('RT_DFS_ELM_USE_LFN'):
|
||||||
|
elmfat += ['filesystems/elmfat/option/cc936.c']
|
||||||
|
src_local = src_local + elmfat
|
||||||
|
|
||||||
|
if GetDepend(['RT_USING_DFS_NFS', 'RT_USING_LWIP']):
|
||||||
|
src_local = src_local + nfs
|
||||||
|
path = path + [RTT_ROOT + '/components/dfs/filesystems/nfs']
|
||||||
|
|
||||||
|
if GetDepend('RT_USING_DFS_ROMFS'):
|
||||||
|
src_local = src_local + romfs
|
||||||
|
path = path + [RTT_ROOT + '/components/dfs/filesystems/romfs']
|
||||||
|
|
||||||
|
if GetDepend('RT_USING_DFS_DEVFS'):
|
||||||
|
src_local = src_local + devfs
|
||||||
|
path = path + [RTT_ROOT + '/components/dfs/filesystems/devfs']
|
||||||
|
|
||||||
|
if GetDepend('RT_USING_DFS_DEVFS'):
|
||||||
|
src_local = src_local + uffs
|
||||||
|
path = path + [RTT_ROOT + '/components/dfs/filesystems/uffs/src/inc', RTT_ROOT + '/components/dfs/filesystems/uffs', RTT_ROOT + '/components/dfs/filesystems/uffs/flash']
|
||||||
|
|
||||||
|
group = DefineGroup('Filesystem', src_local, depend = ['RT_USING_DFS'], CPPPATH = path)
|
||||||
|
|
||||||
|
Return('group')
|
|
@ -0,0 +1,293 @@
|
||||||
|
/*
|
||||||
|
This file is part of UFFS, the Ultra-low-cost Flash File System.
|
||||||
|
|
||||||
|
Copyright (C) 2005-2009 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
|
||||||
|
|
||||||
|
This exception does not invalidate any other reasons why a work based
|
||||||
|
on this file might be covered by the GNU General Public License.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* \file nand flash interface example
|
||||||
|
* \brief example for using nand flash driver and multiple partitions, with system memory allocator.
|
||||||
|
* \author Ricky Zheng, created at 27 Nov, 2007
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <rtthread.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <dfs_fs.h>
|
||||||
|
#include "uffs/uffs_device.h"
|
||||||
|
#include "uffs/uffs_flash.h"
|
||||||
|
#include "uffs/uffs_mtb.h"
|
||||||
|
#include "uffs/uffs_fs.h"
|
||||||
|
#include "uffs/uffs_utils.h"
|
||||||
|
#include "uffs/uffs_find.h"
|
||||||
|
#include "uffs/uffs_fd.h"
|
||||||
|
#include "uffs_ext.h"
|
||||||
|
|
||||||
|
#include "k9f2g08.h"
|
||||||
|
|
||||||
|
#define PFX "nand-drv:"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Note: all=0,read manufacturer id and device id only.
|
||||||
|
* all=1,read all bytes,comprise additional ids.
|
||||||
|
*/
|
||||||
|
static void nand_read_chip_ids(u8* buf, UBOOL all)
|
||||||
|
{
|
||||||
|
K9F2G08_ReadChipID(buf, all);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nand_write_page_data(uffs_Device *dev, u32 block, u32 page, const u8 *buffer, int len, u8 *ecc)
|
||||||
|
{
|
||||||
|
K9F2G08_WritePage(block,page,buffer,len,ecc);
|
||||||
|
|
||||||
|
dev->st.page_write_count++;
|
||||||
|
return UFFS_FLASH_NO_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int nand_write_page_spare(uffs_Device *dev, u32 block, u32 page, const u8 *spare, int ofs, int len, UBOOL eod)
|
||||||
|
{
|
||||||
|
K9F2G08_WriteTags(block,page,spare,ofs,len);
|
||||||
|
|
||||||
|
dev->st.spare_write_count++;
|
||||||
|
return UFFS_FLASH_NO_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nand_read_page_data(uffs_Device *dev, u32 block, u32 page, u8 *buffer, int len, u8 *ecc)
|
||||||
|
{
|
||||||
|
K9F2G08_ReadPage(block,page,buffer,len,ecc);
|
||||||
|
|
||||||
|
dev->st.page_read_count++;
|
||||||
|
return UFFS_FLASH_NO_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nand_read_page_spare(uffs_Device *dev, u32 block, u32 page, u8 *spare, int ofs, int len)
|
||||||
|
{
|
||||||
|
K9F2G08_ReadTags(block,page,spare,ofs,len);
|
||||||
|
|
||||||
|
dev->st.spare_read_count++;
|
||||||
|
return UFFS_FLASH_NO_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nand_erase_block(uffs_Device *dev, u32 block)
|
||||||
|
{
|
||||||
|
K9F2G08_EraseBlock(block);
|
||||||
|
|
||||||
|
dev->st.block_erase_count++;
|
||||||
|
return UFFS_FLASH_NO_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nand_mark_badblock(uffs_Device *dev,u32 block)
|
||||||
|
{
|
||||||
|
return K9F2G08_Mark_badblk(block);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nand_is_badblock(uffs_Device *dev,u32 block)
|
||||||
|
{
|
||||||
|
return K9F2G08_Check_badblk(block);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uffs_FlashOps nand_driver_ops =
|
||||||
|
{
|
||||||
|
nand_read_page_data, //ReadPageData
|
||||||
|
nand_read_page_spare, //ReadPageSpare
|
||||||
|
NULL, //ReadPageSpareWithLayout
|
||||||
|
nand_write_page_data, //WritePageData
|
||||||
|
nand_write_page_spare, //WritePageSpare
|
||||||
|
NULL, //WriteFullPage
|
||||||
|
nand_is_badblock, //IsBadBlock
|
||||||
|
nand_mark_badblock, //MarkBadBlock
|
||||||
|
nand_erase_block, //EraseBlock
|
||||||
|
};
|
||||||
|
|
||||||
|
//change these parameters to fit your nand flash specification
|
||||||
|
//#define MAN_ID MAN_ID_SAMSUNG // simulate Samsung's NAND flash
|
||||||
|
|
||||||
|
static struct uffs_StorageAttrSt flash_storage = {0};
|
||||||
|
|
||||||
|
static int initDevice(uffs_Device *dev)
|
||||||
|
{
|
||||||
|
dev->ops = &nand_driver_ops;
|
||||||
|
return RT_EOK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int releaseDevice(uffs_Device *dev)
|
||||||
|
{
|
||||||
|
return RT_EOK;
|
||||||
|
}
|
||||||
|
|
||||||
|
#include <dfs_uffs.h>
|
||||||
|
|
||||||
|
static uffs_Device uffs_device = {0};
|
||||||
|
/* define mount table,UFFS FS private data */
|
||||||
|
/* it is absolute accessing for uffs.*/
|
||||||
|
static uffs_MountTable uffs_mount_table =
|
||||||
|
{
|
||||||
|
&uffs_device,
|
||||||
|
0,
|
||||||
|
TOTAL_BLOCKS-1,
|
||||||
|
"/" ,
|
||||||
|
NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
#include "nand.h"
|
||||||
|
extern struct nand_flash_dev nand_flash_ids[];
|
||||||
|
extern struct nand_manufacturers nand_manuf_ids[];
|
||||||
|
|
||||||
|
struct nand_flash_dev* nand_init(u8* buf)
|
||||||
|
{
|
||||||
|
struct nand_flash_dev* type=RT_NULL;
|
||||||
|
int i, dev_id,maf_id;
|
||||||
|
|
||||||
|
K9F2G08_Reset();
|
||||||
|
rt_kprintf("nand: ");
|
||||||
|
|
||||||
|
nand_read_chip_ids(buf,0);
|
||||||
|
maf_id= buf[0];
|
||||||
|
/* Try to identify manufacturer */
|
||||||
|
for (i = 0; nand_manuf_ids[i].id != 0x0; i++)
|
||||||
|
{
|
||||||
|
if (nand_manuf_ids[i].id == maf_id)
|
||||||
|
{
|
||||||
|
rt_kprintf("%s ",nand_manuf_ids[i].name);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(nand_manuf_ids[i].id == 0x0)
|
||||||
|
{
|
||||||
|
rt_kprintf("%s\n",nand_manuf_ids[i].name);
|
||||||
|
return RT_NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_id = buf[1];
|
||||||
|
/* Lookup the flash id */
|
||||||
|
for(i = 0; nand_flash_ids[i].name != RT_NULL; i++)
|
||||||
|
{
|
||||||
|
if(dev_id == nand_flash_ids[i].id)
|
||||||
|
{
|
||||||
|
type = &nand_flash_ids[i];
|
||||||
|
rt_kprintf("%s\n",nand_flash_ids[i].name);
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return RT_NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* RT-Thread Device Driver Interface */
|
||||||
|
/* UFFS FileSystem NandFlash InterFace */
|
||||||
|
/* we don't use entity, let uffs autarky */
|
||||||
|
|
||||||
|
struct rt_device nand_device;
|
||||||
|
|
||||||
|
static rt_err_t rt_nand_init(rt_device_t dev)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static rt_err_t rt_nand_open(rt_device_t dev, u16 oflag)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static rt_err_t rt_nand_close(rt_device_t dev)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static rt_err_t rt_nand_control(rt_device_t dev, u8 cmd, void *args)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static rt_size_t rt_nand_read(rt_device_t dev, rt_off_t pos, void* buffer, rt_size_t size)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static rt_size_t rt_nand_write (rt_device_t dev, rt_off_t pos, const void* buffer, rt_size_t size)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rt_hw_nand_init(void)
|
||||||
|
{
|
||||||
|
struct nand_flash_dev *type = RT_NULL;
|
||||||
|
u8 buf[5];
|
||||||
|
if((type = nand_init(buf)) != RT_NULL)
|
||||||
|
{
|
||||||
|
uffs_MountTable *entry;
|
||||||
|
struct uffs_StorageAttrSt *chip = &flash_storage;
|
||||||
|
|
||||||
|
rt_device_t dev = &nand_device;
|
||||||
|
/* fill in NandFlash device struct */
|
||||||
|
dev->type = RT_Device_Class_Block;
|
||||||
|
dev->init = rt_nand_init;
|
||||||
|
dev->open = rt_nand_open;
|
||||||
|
dev->close = rt_nand_close;
|
||||||
|
dev->read = rt_nand_read;
|
||||||
|
dev->write = rt_nand_write;
|
||||||
|
dev->control = rt_nand_control;
|
||||||
|
dev->user_data = &uffs_mount_table;
|
||||||
|
/* register nandfalsh device */
|
||||||
|
rt_device_register(&nand_device, "nand0",
|
||||||
|
RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_REMOVABLE | RT_DEVICE_FLAG_STANDALONE);
|
||||||
|
|
||||||
|
/* about uffs codes */
|
||||||
|
entry = &uffs_mount_table;
|
||||||
|
//entry->lock = rt_sem_create("sem_nand0", 1, RT_IPC_FLAG_FIFO);//??it's lonely!how to do?
|
||||||
|
|
||||||
|
uffs_MemSetupSystemAllocator(&(entry->dev->mem));
|
||||||
|
entry->dev->Init = initDevice;
|
||||||
|
entry->dev->Release = releaseDevice;
|
||||||
|
entry->dev->attr = chip;
|
||||||
|
uffs_RegisterMountTable(entry);
|
||||||
|
|
||||||
|
/* Newer devices have all the information in additional id bytes */
|
||||||
|
if(!type->pagesize)
|
||||||
|
{
|
||||||
|
int extid;
|
||||||
|
nand_read_chip_ids(buf,1);//reread chip ids,the all and the one.
|
||||||
|
|
||||||
|
/* The 3rd id byte holds MLC / multichip data,untapped */
|
||||||
|
/* The 4th id byte is the important one */
|
||||||
|
extid = buf[3];
|
||||||
|
/* Calc pagesize */
|
||||||
|
chip->page_data_size = 1024 << (extid & 0x3);
|
||||||
|
extid >>= 2;
|
||||||
|
/* Calc oobsize */
|
||||||
|
chip->spare_size = (8<<(extid & 0x01))*(chip->page_data_size>>9);
|
||||||
|
extid >>= 2;
|
||||||
|
/* Calc blocksize. Blocksize is multiples of 64KiB */
|
||||||
|
chip->pages_per_block = ((64*1024)<<(extid & 0x03))/(chip->page_data_size);
|
||||||
|
/* The 5th id byte */
|
||||||
|
chip->total_blocks = (type->chipsize*1024*1024) /
|
||||||
|
chip->page_data_size / chip->pages_per_block;
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{ /* Old devices have chip data hardcoded in the device id table */
|
||||||
|
chip->page_data_size = type->pagesize;
|
||||||
|
chip->pages_per_block = type->blocksize / type->pagesize;
|
||||||
|
chip->spare_size = chip->page_data_size / 32;
|
||||||
|
chip->total_blocks = (type->chipsize*1024*1024) / type->blocksize;
|
||||||
|
}
|
||||||
|
if(type->options & NAND_SAMSUNG_LP_OPTIONS)
|
||||||
|
chip->block_status_offs = NAND_LARGE_BADBLOCK_POS;
|
||||||
|
else
|
||||||
|
chip->block_status_offs = NAND_SMALL_BADBLOCK_POS;
|
||||||
|
chip->ecc_opt = UFFS_ECC_SOFT; /* ecc option, do not use ECC,debug */
|
||||||
|
chip->layout_opt = UFFS_LAYOUT_UFFS; /* let UFFS do the spare layout */
|
||||||
|
#if (0) //DEBUG trace facility
|
||||||
|
rt_kprintf("page_data_size = %d\n",chip->page_data_size);
|
||||||
|
rt_kprintf("pages_per_block = %d\n",chip->pages_per_block);
|
||||||
|
rt_kprintf("spare_size = %d\n",chip->spare_size);
|
||||||
|
rt_kprintf("total_blocks = %d\n",chip->total_blocks);
|
||||||
|
rt_kprintf("block_stat_offs = %d\n",chip->block_status_offs);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//end of file
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
#ifndef __K9F2G08_H__
|
||||||
|
#define __K9F2G08_H__
|
||||||
|
|
||||||
|
#include <rtdef.h>
|
||||||
|
#include <nand.h>
|
||||||
|
|
||||||
|
void K9F2G08_Program(u32 blockIndex, u32 srcAddress, u32 fileSize);
|
||||||
|
|
||||||
|
//*************** H/W dependent functions ***************
|
||||||
|
void K9F2G08_ReadChipID(u8* buf, UBOOL all);
|
||||||
|
|
||||||
|
int K9F2G08_Check_badblk(u32 block);
|
||||||
|
int K9F2G08_EraseBlock (u32 block);
|
||||||
|
int K9F2G08_Mark_badblk (u32 block);
|
||||||
|
int K9F2G08_ReadPage (u32 block, u32 page, u8 *buffer, int len, u8 *ecc);
|
||||||
|
int K9F2G08_WritePage (u32 block, u32 page, const u8 *buffer, int len, const u8 *ecc);
|
||||||
|
int K9F2G08_ReadTags (u32 block, u32 page, u8 *spare, int ofs, int len);
|
||||||
|
int K9F2G08_WriteTags (u32 block, u32 page, const u8 *spare, int ofs, int len);
|
||||||
|
|
||||||
|
void K9F2G08_Reset(void);
|
||||||
|
|
||||||
|
void K9F2G08_Init(void);
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,366 @@
|
||||||
|
/**
|
||||||
|
* 用户要自己实现这个文件中的接口函数,不一样的芯片会有不同的访问命令
|
||||||
|
* 这个例程文件是关于Samsung k9f2g08芯片的,属于大页nandflash
|
||||||
|
*/
|
||||||
|
#include <nand.h>
|
||||||
|
#include "s3c24x0.h"
|
||||||
|
|
||||||
|
#include <k9f2g08.h>
|
||||||
|
|
||||||
|
#define nand_write_cmd(cmd) (NFCMD = (cmd))
|
||||||
|
#define nand_write_addr(addr) (NFADDR = (addr))
|
||||||
|
#define nand_cs_en() (NFCONT &= ~(1<<1))
|
||||||
|
#define nand_cs_ds() (NFCONT |= (1<<1))
|
||||||
|
#define nand_Init_ECC() (NFCONT |= (1<<4))
|
||||||
|
#define nand_read() (NFDATA8)
|
||||||
|
#define nand_write(data) (NFDATA8 = (data))
|
||||||
|
|
||||||
|
#define nand_wait() {while(!(NFSTAT&(1<<0)));} //wait tWB and check F_RNB pin.
|
||||||
|
|
||||||
|
//NAND Flash Command.support K9F2G08
|
||||||
|
#define K9F2G08_CMD_READ0 0x00 // Read0
|
||||||
|
//#define K9F2G08_CMD_READ1 1 // Read1,K9F2G08 don't support the command.
|
||||||
|
#define K9F2G08_CMD_RANDOM_DATA_OUT 0x05 // Random data output
|
||||||
|
#define K9F2G08_CMD_PAGEPROG 0x10 // Write phase 2
|
||||||
|
#define K9F2G08_CMD_READ30 0x30 // Read30
|
||||||
|
#define K9F2G08_CMD_READ35 0x35 // Read35
|
||||||
|
//#define K9F2G08_CMD_READOOB 0x50 // Read oob
|
||||||
|
#define K9F2G08_CMD_ERASE1 0x60 // Erase phase 1
|
||||||
|
#define K9F2G08_CMD_STATUS 0x70 // Status read
|
||||||
|
#define K9F2G08_CMD_READ_EDC 0x7b // Read EDC Status
|
||||||
|
#define K9F2G08_CMD_SEQIN 0x80 // Write phase 1
|
||||||
|
#define K9F2G08_CMD_RANDOM_DATA_IN 0x85 // Random data input Copy-Back Program(0x85,0x10)
|
||||||
|
#define K9F2G08_CMD_READID 0x90 // ReadID,all-purpose command
|
||||||
|
#define K9F2G08_CMD_ERASE2 0xd0 // Erase phase 2
|
||||||
|
#define K9F2G08_CMD_RESET 0xff // Reset
|
||||||
|
|
||||||
|
#define BAD_CHECK (0)
|
||||||
|
#define ECC_CHECK (0)
|
||||||
|
|
||||||
|
|
||||||
|
//*************************************************
|
||||||
|
//** H/W dependent functions **
|
||||||
|
//*************************************************
|
||||||
|
|
||||||
|
// HCLK=100Mhz
|
||||||
|
#define TACLS 1 //1clk(0ns)
|
||||||
|
#define TWRPH0 4 //3clk(25ns)
|
||||||
|
#define TWRPH1 0 //1clk(10ns) //TACLS+TWRPH0+TWRPH1>=50ns
|
||||||
|
|
||||||
|
int read_nand_stats(void) // R/B 未接好?
|
||||||
|
{
|
||||||
|
u8 stat;
|
||||||
|
|
||||||
|
nand_write_cmd(K9F2G08_CMD_STATUS);//0x70
|
||||||
|
|
||||||
|
stat = nand_read();//读出返回的数据
|
||||||
|
|
||||||
|
if(stat&1) return 1; // I/O0=1失败
|
||||||
|
else return 0; // I/O0=0成功
|
||||||
|
}
|
||||||
|
|
||||||
|
//擦除一个块
|
||||||
|
//返回0,successful
|
||||||
|
//返回1,error
|
||||||
|
int K9F2G08_EraseBlock(u32 block)
|
||||||
|
{
|
||||||
|
int stat;
|
||||||
|
u32 _page = block*PAGES_PER_BLOCK;
|
||||||
|
|
||||||
|
nand_cs_en();
|
||||||
|
|
||||||
|
nand_write_cmd(K9F2G08_CMD_ERASE1); // Erase one block 1st command
|
||||||
|
|
||||||
|
nand_write_addr(_page&0xff); // Page number=0
|
||||||
|
nand_write_addr((_page>>8)&0xff);
|
||||||
|
nand_write_addr((_page>>16)&0xff);
|
||||||
|
|
||||||
|
nand_write_cmd(K9F2G08_CMD_ERASE2); // Erase one blcok 2nd command
|
||||||
|
|
||||||
|
nand_wait(); // Wait tBERS max 3ms.
|
||||||
|
|
||||||
|
stat = read_nand_stats();
|
||||||
|
nand_cs_ds();
|
||||||
|
return stat;
|
||||||
|
}
|
||||||
|
|
||||||
|
//return 1 if it's a bad block, 0 if it's good.
|
||||||
|
int K9F2G08_Check_badblk(u32 block) //0:bad,1:good
|
||||||
|
{
|
||||||
|
u8 data;
|
||||||
|
u32 _page;//块的首页地址
|
||||||
|
|
||||||
|
_page = block*PAGES_PER_BLOCK; // For 2'nd cycle I/O[7:5]
|
||||||
|
|
||||||
|
nand_cs_en();
|
||||||
|
|
||||||
|
nand_write_cmd(K9F2G08_CMD_READ0); // Spare array read command
|
||||||
|
nand_write_addr(PAGE_DATA_SIZE&0xff); // Read the mark of bad block in spare array(M addr=5)
|
||||||
|
nand_write_addr((PAGE_DATA_SIZE>>8)&0xff);
|
||||||
|
nand_write_addr(_page&0xff); // The mark of bad block is in 0 page
|
||||||
|
nand_write_addr((_page>>8)&0xff); // For block number A[24:17]
|
||||||
|
nand_write_addr((_page>>16)&0xff); // For block number A[25]
|
||||||
|
nand_write_cmd(K9F2G08_CMD_READ30);
|
||||||
|
|
||||||
|
nand_wait(); // Wait tR(max 12us)
|
||||||
|
|
||||||
|
data=nand_read();
|
||||||
|
nand_cs_ds();
|
||||||
|
if(data==0x00)
|
||||||
|
return 1;//坏块
|
||||||
|
else
|
||||||
|
return 0;//好块
|
||||||
|
}
|
||||||
|
|
||||||
|
//return 0 if ok, 1:fail
|
||||||
|
int K9F2G08_Mark_badblk(u32 block)
|
||||||
|
{
|
||||||
|
u8 stat;
|
||||||
|
u32 _page = block*PAGES_PER_BLOCK;
|
||||||
|
|
||||||
|
nand_cs_en();
|
||||||
|
|
||||||
|
nand_write_cmd(K9F2G08_CMD_SEQIN); // Write 1st command
|
||||||
|
|
||||||
|
nand_write_addr(PAGE_DATA_SIZE & 0xff); // The mark of bad block
|
||||||
|
nand_write_addr((PAGE_DATA_SIZE>>8)&0xff);
|
||||||
|
nand_write_addr(_page&0xff); // marked 5th spare array
|
||||||
|
nand_write_addr((_page>>8)&0xff); // in the 1st page.
|
||||||
|
nand_write_addr((_page>>16)&0xff); //
|
||||||
|
|
||||||
|
nand_write(0x00); //坏块标记
|
||||||
|
|
||||||
|
nand_write_cmd(K9F2G08_CMD_PAGEPROG); // Write 2nd command
|
||||||
|
|
||||||
|
nand_wait(); // Wait tPROG(200~500us)
|
||||||
|
stat = read_nand_stats();//查询是否成功
|
||||||
|
nand_cs_ds();
|
||||||
|
|
||||||
|
return stat;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int K9F2G08_ReadPage(u32 block, u32 page, u8 *buffer, int len, u8 *ecc)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
u32 _page = block*PAGES_PER_BLOCK + page;
|
||||||
|
|
||||||
|
// NF_RSTECC(); // Initialize ECC
|
||||||
|
|
||||||
|
nand_cs_en();
|
||||||
|
|
||||||
|
nand_write_cmd(K9F2G08_CMD_READ0); // Read command
|
||||||
|
nand_write_addr(0x00); // Column = 0
|
||||||
|
nand_write_addr(0x00);
|
||||||
|
nand_write_addr(_page&0xff); //
|
||||||
|
nand_write_addr((_page>>8)&0xff); // Block & Page num.
|
||||||
|
nand_write_addr((_page>>16)&0xff); //
|
||||||
|
|
||||||
|
nand_write_cmd(K9F2G08_CMD_READ30);
|
||||||
|
|
||||||
|
nand_wait(); // Wait tR(max 12us)
|
||||||
|
for(i=0;i<len;i++)
|
||||||
|
{
|
||||||
|
buffer[i] = nand_read(); // Read one page
|
||||||
|
}
|
||||||
|
|
||||||
|
nand_cs_ds();
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int K9F2G08_ReadTags(u32 block, u32 page, u8 *spare, int ofs, int len)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
u32 _page = block*PAGES_PER_BLOCK + page;
|
||||||
|
|
||||||
|
// NF_RSTECC(); // Initialize ECC
|
||||||
|
|
||||||
|
nand_cs_en();
|
||||||
|
|
||||||
|
nand_write_cmd(K9F2G08_CMD_READ0); // Read command
|
||||||
|
nand_write_addr((PAGE_DATA_SIZE+ofs)&0xff); // Column = 0
|
||||||
|
nand_write_addr(((PAGE_DATA_SIZE+ofs)>>8)&0xff);
|
||||||
|
nand_write_addr(_page&0xff); //
|
||||||
|
nand_write_addr((_page>>8)&0xff); // Block & Page num.
|
||||||
|
nand_write_addr((_page>>16)&0xff); //
|
||||||
|
|
||||||
|
nand_write_cmd(K9F2G08_CMD_READ30);
|
||||||
|
|
||||||
|
nand_wait(); // Wait tR(max 12us)
|
||||||
|
for(i=0;i<len;i++)
|
||||||
|
{
|
||||||
|
spare[i] = nand_read(); // Read one page
|
||||||
|
}
|
||||||
|
|
||||||
|
nand_cs_ds();
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//写一页数据
|
||||||
|
//返回0,successful
|
||||||
|
//返回1,error
|
||||||
|
int K9F2G08_WritePage(u32 block, u32 page, const u8 *buffer, int len, const u8 *ecc)
|
||||||
|
{
|
||||||
|
int i,stat;
|
||||||
|
|
||||||
|
u32 _page = block*PAGES_PER_BLOCK + page;
|
||||||
|
|
||||||
|
//nand_Init_ECC(); // Initialize ECC
|
||||||
|
|
||||||
|
nand_cs_en();
|
||||||
|
|
||||||
|
nand_write_cmd(K9F2G08_CMD_SEQIN); //0x80 Write 1st command
|
||||||
|
for(i=0;i<10;i++);
|
||||||
|
nand_write_addr(0x00); // Column 0
|
||||||
|
nand_write_addr(0x00);
|
||||||
|
nand_write_addr(_page&0xff); //
|
||||||
|
nand_write_addr((_page>>8)&0xff); // Block & page num.
|
||||||
|
nand_write_addr((_page>>16)&0xff); //
|
||||||
|
|
||||||
|
for(i=0;i<len;i++)
|
||||||
|
{
|
||||||
|
nand_write(*buffer++); // Write one page to NFM from buffer
|
||||||
|
}
|
||||||
|
nand_write_cmd(K9F2G08_CMD_PAGEPROG); //0x10 Write 2nd command
|
||||||
|
|
||||||
|
nand_wait(); //wait tPROG 200~500us;
|
||||||
|
|
||||||
|
stat = read_nand_stats();
|
||||||
|
nand_cs_ds();
|
||||||
|
|
||||||
|
return stat;
|
||||||
|
}
|
||||||
|
|
||||||
|
int K9F2G08_WriteTags(u32 block, u32 page, const u8 *spare, int ofs, int len)
|
||||||
|
{
|
||||||
|
int i,stat;
|
||||||
|
u32 _page = block*PAGES_PER_BLOCK + page;
|
||||||
|
|
||||||
|
//nand_Init_ECC(); // Initialize ECC
|
||||||
|
|
||||||
|
nand_cs_en();
|
||||||
|
|
||||||
|
nand_write_cmd(K9F2G08_CMD_SEQIN); //0x80 Write 1st command
|
||||||
|
for(i=0;i<10;i++);
|
||||||
|
nand_write_addr((PAGE_DATA_SIZE+ofs)&0xff); // Column 0
|
||||||
|
nand_write_addr(((PAGE_DATA_SIZE+ofs)>>8)&0xff);
|
||||||
|
nand_write_addr(_page&0xff); //
|
||||||
|
nand_write_addr((_page>>8)&0xff); // Block & page num.
|
||||||
|
nand_write_addr((_page>>16)&0xff); //
|
||||||
|
|
||||||
|
for(i=0;i<len;i++)
|
||||||
|
{
|
||||||
|
nand_write(*spare++); // Write one page to NFM from buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
nand_write_cmd(K9F2G08_CMD_PAGEPROG); //0x10 Write 2nd command
|
||||||
|
|
||||||
|
nand_wait(); //wait tPROG 200~500us;
|
||||||
|
|
||||||
|
stat = read_nand_stats();
|
||||||
|
if(!stat) // Page write error
|
||||||
|
{
|
||||||
|
nand_cs_ds();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nand_cs_ds();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//find frist shift bit
|
||||||
|
//rt_inline int generic_ffs(int x)
|
||||||
|
//{
|
||||||
|
// int r = 1;
|
||||||
|
//
|
||||||
|
// if(!x)
|
||||||
|
// return 0;
|
||||||
|
//
|
||||||
|
// if(!(x & 0xffff)) {x >>= 16;r += 16;}
|
||||||
|
// if(!(x & 0xff)) {x >>= 8;r += 8;}
|
||||||
|
// if(!(x & 0xf)) {x >>= 4;r += 4;}
|
||||||
|
// if(!(x & 3)) {x >>= 2;r += 2;}
|
||||||
|
// if(!(x & 1)) {x >>= 1;r += 1;}
|
||||||
|
//
|
||||||
|
// return r;
|
||||||
|
//}
|
||||||
|
|
||||||
|
/* when all is true,read all byte */
|
||||||
|
void K9F2G08_ReadChipID(u8* buf, UBOOL all)
|
||||||
|
{
|
||||||
|
nand_cs_en();
|
||||||
|
|
||||||
|
nand_write_cmd(K9F2G08_CMD_READID); //0x90
|
||||||
|
nand_write_addr(K9F2G08_CMD_READ0);
|
||||||
|
|
||||||
|
buf[0] = nand_read();//制造商ID
|
||||||
|
buf[1] = nand_read();//芯片ID
|
||||||
|
if(all)
|
||||||
|
{
|
||||||
|
buf[2] = nand_read();
|
||||||
|
buf[3] = nand_read();
|
||||||
|
//buf[4] = nand_read(); //有的芯片没有第5个字节
|
||||||
|
}
|
||||||
|
|
||||||
|
nand_cs_ds();
|
||||||
|
}
|
||||||
|
|
||||||
|
void K9F2G08_Init(void)
|
||||||
|
{
|
||||||
|
NFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4)|(0<<0);
|
||||||
|
NFCONT = (0<<13)|(0<<12)|(0<<10)|(0<<9)|(0<<8)|(1<<6)|(1<<5)|(1<<4)|(1<<1)|(1<<0);
|
||||||
|
NFSTAT = 0;
|
||||||
|
// 1 1 1 1, 1 xxx, r xxx, r xxx
|
||||||
|
// En 512B 4step ECCR nFCE=H tACLS tWRPH0 tWRPH1
|
||||||
|
}
|
||||||
|
|
||||||
|
void K9F2G08_Reset(void)
|
||||||
|
{
|
||||||
|
nand_cs_en();
|
||||||
|
|
||||||
|
nand_write_cmd(0xFF); //reset command
|
||||||
|
|
||||||
|
nand_wait(); //wait 200~500us;
|
||||||
|
nand_cs_ds();
|
||||||
|
|
||||||
|
K9F2G08_Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
#if (0)
|
||||||
|
int K9F2G08_ReadChunk(u32 chunk, u8 *data, u8 *tags)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
nand_cs_en();
|
||||||
|
|
||||||
|
nand_write_cmd(K9F2G08_CMD_READ0); // Read command
|
||||||
|
nand_write_addr(0x00);
|
||||||
|
nand_write_addr(0x00);
|
||||||
|
nand_write_addr(chunk & 0xff); //
|
||||||
|
nand_write_addr((chunk >> 8) & 0xff); // Block & Page num.
|
||||||
|
nand_write_addr((chunk >> 16) & 0xff); //
|
||||||
|
//nand_Init_ECC();
|
||||||
|
nand_write_cmd(K9F2G08_CMD_READ30); // Read command
|
||||||
|
|
||||||
|
nand_wait(); // Wait tR(max 12us)
|
||||||
|
|
||||||
|
for(i = 0; i < PAGE_DATA_SIZE; i++)
|
||||||
|
{
|
||||||
|
data[i] = nand_read(); // Read one page
|
||||||
|
}
|
||||||
|
for(i = 0; i < PAGE_SPARE_SIZE; i++)
|
||||||
|
{
|
||||||
|
tags[i] = nand_read(); // Read spare array
|
||||||
|
}
|
||||||
|
|
||||||
|
nand_cs_ds();
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
|
@ -0,0 +1,77 @@
|
||||||
|
//
|
||||||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Use of this source code is subject to the terms of the Microsoft end-user
|
||||||
|
// license agreement (EULA) under which you licensed this SOFTWARE PRODUCT.
|
||||||
|
// If you did not accept the terms of the EULA, you are not authorized to use
|
||||||
|
// this source code. For a copy of the EULA, please see the LICENSE.RTF on your
|
||||||
|
// install media.
|
||||||
|
//
|
||||||
|
/*++
|
||||||
|
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
|
||||||
|
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
||||||
|
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE.
|
||||||
|
--*/
|
||||||
|
#ifndef __NAND_H__
|
||||||
|
#define __NAND_H__
|
||||||
|
|
||||||
|
#include <s3c24x0.h>
|
||||||
|
#include <uffs/uffs_types.h>
|
||||||
|
|
||||||
|
/* define low accessing value */
|
||||||
|
#define TOTAL_BLOCKS 2048 /* total block of whole chip */
|
||||||
|
#define PAGE_DATA_SIZE 2048 /* max size of page data */
|
||||||
|
#define PAGE_SPARE_SIZE 64 /* max size of extended partition */
|
||||||
|
#define PAGES_PER_BLOCK 64 /* max pages per block' */
|
||||||
|
#define PAGE_SIZE (PAGE_DATA_SIZE+PAGE_SPARE_SIZE)/* max size per whole page */
|
||||||
|
#define BLOCK_DATA_SIZE (PAGE_DATA_SIZE*PAGES_PER_BLOCK)/* max size per block' */
|
||||||
|
|
||||||
|
//bad flags offset in the oob area.
|
||||||
|
#define NAND_SMALL_BADBLOCK_POS 5 //small page FLASH
|
||||||
|
#define NAND_LARGE_BADBLOCK_POS 0 //large page FLASH
|
||||||
|
|
||||||
|
/* Option constants for bizarre disfunctionality and real
|
||||||
|
* features
|
||||||
|
*/
|
||||||
|
/* Chip can not auto increment pages */
|
||||||
|
#define NAND_NO_AUTOINCR 0x00000001
|
||||||
|
/* Buswitdh is 16 bit */
|
||||||
|
#define NAND_BUSWIDTH_16 0x00000002
|
||||||
|
/* Device supports partial programming without padding */
|
||||||
|
#define NAND_NO_PADDING 0x00000004
|
||||||
|
/* Chip has cache program function */
|
||||||
|
#define NAND_CACHEPRG 0x00000008
|
||||||
|
/* Chip has copy back function */
|
||||||
|
#define NAND_COPYBACK 0x00000010
|
||||||
|
/* AND Chip which has 4 banks and a confusing page / block
|
||||||
|
* assignment. See Renesas datasheet for further information */
|
||||||
|
#define NAND_IS_AND 0x00000020
|
||||||
|
/* Chip has a array of 4 pages which can be read without
|
||||||
|
* additional ready /busy waits */
|
||||||
|
#define NAND_4PAGE_ARRAY 0x00000040
|
||||||
|
|
||||||
|
/* Options valid for Samsung large page devices */
|
||||||
|
#define NAND_SAMSUNG_LP_OPTIONS \
|
||||||
|
(NAND_NO_PADDING | NAND_CACHEPRG | NAND_COPYBACK)
|
||||||
|
|
||||||
|
|
||||||
|
struct nand_flash_dev
|
||||||
|
|
||||||
|
{
|
||||||
|
char *name; //chip name
|
||||||
|
int id; //chip ID
|
||||||
|
unsigned long pagesize; //max pages
|
||||||
|
unsigned long chipsize; //size of whole chip iMB
|
||||||
|
unsigned long blocksize;//size of block
|
||||||
|
unsigned long options; //option
|
||||||
|
};
|
||||||
|
|
||||||
|
struct nand_manufacturers
|
||||||
|
{
|
||||||
|
int id;
|
||||||
|
char * name;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /*__NAND_H__*/
|
|
@ -0,0 +1,135 @@
|
||||||
|
/*
|
||||||
|
* drivers/mtd/nandids.c
|
||||||
|
*
|
||||||
|
* Copyright (C) 2002 Thomas Gleixner (tglx@linutronix.de)
|
||||||
|
*
|
||||||
|
* $Id: nand_ids.c,v 1.10 2004/05/26 13:40:12 gleixner Exp $
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <rtdef.h>
|
||||||
|
|
||||||
|
#include "flash/nand.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chip ID list
|
||||||
|
*
|
||||||
|
* name, ID, pagesize, chipsize in MegaByte, eraseblock size,options
|
||||||
|
*
|
||||||
|
* if 0, get this information from the extended chip ID
|
||||||
|
*/
|
||||||
|
struct nand_flash_dev nand_flash_ids[] =
|
||||||
|
{
|
||||||
|
{"NAND 1MiB 5V 8-bit", 0x6e, 256, 1, 0x1000, 0},
|
||||||
|
{"NAND 2MiB 5V 8-bit", 0x64, 256, 2, 0x1000, 0},
|
||||||
|
{"NAND 4MiB 5V 8-bit", 0x6b, 512, 4, 0x2000, 0},
|
||||||
|
{"NAND 1MiB 3,3V 8-bit", 0xe8, 256, 1, 0x1000, 0},
|
||||||
|
{"NAND 1MiB 3,3V 8-bit", 0xec, 256, 1, 0x1000, 0},
|
||||||
|
{"NAND 2MiB 3,3V 8-bit", 0xea, 256, 2, 0x1000, 0},
|
||||||
|
{"NAND 4MiB 3,3V 8-bit", 0xd5, 512, 4, 0x2000, 0},
|
||||||
|
{"NAND 4MiB 3,3V 8-bit", 0xe3, 512, 4, 0x2000, 0},
|
||||||
|
{"NAND 4MiB 3,3V 8-bit", 0xe5, 512, 4, 0x2000, 0},
|
||||||
|
{"NAND 8MiB 3,3V 8-bit", 0xd6, 512, 8, 0x2000, 0},
|
||||||
|
|
||||||
|
{"NAND 8MiB 1,8V 8-bit", 0x39, 512, 8, 0x2000, 0},
|
||||||
|
{"NAND 8MiB 3,3V 8-bit", 0xe6, 512, 8, 0x2000, 0},
|
||||||
|
{"NAND 8MiB 1,8V 16-bit", 0x49, 512, 8, 0x2000, NAND_BUSWIDTH_16},
|
||||||
|
{"NAND 8MiB 3,3V 16-bit", 0x59, 512, 8, 0x2000, NAND_BUSWIDTH_16},
|
||||||
|
|
||||||
|
{"NAND 16MiB 1,8V 8-bit", 0x33, 512, 16, 0x4000, 0},
|
||||||
|
{"NAND 16MiB 3,3V 8-bit", 0x73, 512, 16, 0x4000, 0},
|
||||||
|
{"NAND 16MiB 1,8V 16-bit", 0x43, 512, 16, 0x4000, NAND_BUSWIDTH_16},
|
||||||
|
{"NAND 16MiB 3,3V 16-bit", 0x53, 512, 16, 0x4000, NAND_BUSWIDTH_16},
|
||||||
|
|
||||||
|
{"NAND 32MiB 1,8V 8-bit", 0x35, 512, 32, 0x4000, 0},
|
||||||
|
{"NAND 32MiB 3,3V 8-bit", 0x75, 512, 32, 0x4000, 0},
|
||||||
|
{"NAND 32MiB 1,8V 16-bit", 0x45, 512, 32, 0x4000, NAND_BUSWIDTH_16},
|
||||||
|
{"NAND 32MiB 3,3V 16-bit", 0x55, 512, 32, 0x4000, NAND_BUSWIDTH_16},
|
||||||
|
|
||||||
|
{"NAND 64MiB 1,8V 8-bit", 0x36, 512, 64, 0x4000, 0},
|
||||||
|
{"NAND 64MiB 3,3V 8-bit", 0x76, 512, 64, 0x4000, 0},
|
||||||
|
{"NAND 64MiB 1,8V 16-bit", 0x46, 512, 64, 0x4000, NAND_BUSWIDTH_16},
|
||||||
|
{"NAND 64MiB 3,3V 16-bit", 0x56, 512, 64, 0x4000, NAND_BUSWIDTH_16},
|
||||||
|
|
||||||
|
{"NAND 128MiB 1,8V 8-bit", 0x78, 512, 128, 0x4000, 0},
|
||||||
|
{"NAND 128MiB 3,3V 8-bit", 0x79, 512, 128, 0x4000, 0},
|
||||||
|
{"NAND 128MiB 1,8V 16-bit", 0x72, 512, 128, 0x4000, NAND_BUSWIDTH_16},
|
||||||
|
{"NAND 128MiB 3,3V 16-bit", 0x74, 512, 128, 0x4000, NAND_BUSWIDTH_16},
|
||||||
|
|
||||||
|
{"NAND 256MiB 3,3V 8-bit", 0x71, 512, 256, 0x4000, 0},
|
||||||
|
|
||||||
|
/* {"NAND 512MiB 3,3V 8-bit", 0xDC, 512, 512, 0x4000, 0}, */
|
||||||
|
{"NAND 512MiB 3,3V 8-bit", 0xDC, 0, 512, 0, 0},
|
||||||
|
|
||||||
|
/* These are the new chips with large page size. The pagesize
|
||||||
|
* and the erasesize is determined from the extended id bytes
|
||||||
|
*/
|
||||||
|
/* 1 Gigabit */
|
||||||
|
{"NAND 128MiB 1,8V 8-bit", 0xA1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
|
||||||
|
{"NAND 128MiB 3,3V 8-bit", 0xF1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
|
||||||
|
{"NAND 128MiB 1,8V 16-bit", 0xB1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
|
||||||
|
{"NAND 128MiB 3,3V 16-bit", 0xC1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
|
||||||
|
|
||||||
|
/* 2 Gigabit */
|
||||||
|
{"NAND 256MiB 1,8V 8-bit", 0xAA, 0, 256, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
|
||||||
|
{"NAND 256MiB 3,3V 8-bit", 0xDA, 0, 256, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
|
||||||
|
{"NAND 256MiB 1,8V 16-bit", 0xBA, 0, 256, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
|
||||||
|
{"NAND 256MiB 3,3V 16-bit", 0xCA, 0, 256, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
|
||||||
|
|
||||||
|
/* 4 Gigabit */
|
||||||
|
{"NAND 512MiB 1,8V 8-bit", 0xAC, 0, 512, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
|
||||||
|
{"NAND 512MiB 3,3V 8-bit", 0xDC, 0, 512, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
|
||||||
|
{"NAND 512MiB 1,8V 16-bit", 0xBC, 0, 512, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
|
||||||
|
{"NAND 512MiB 3,3V 16-bit", 0xCC, 0, 512, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
|
||||||
|
|
||||||
|
/* 8 Gigabit */
|
||||||
|
{"NAND 1GiB 1,8V 8-bit", 0xA3, 0, 1024, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
|
||||||
|
{"NAND 1GiB 3,3V 8-bit", 0xD3, 0, 1024, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
|
||||||
|
{"NAND 1GiB 1,8V 16-bit", 0xB3, 0, 1024, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
|
||||||
|
{"NAND 1GiB 3,3V 16-bit", 0xC3, 0, 1024, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
|
||||||
|
|
||||||
|
/* 16 Gigabit */
|
||||||
|
{"NAND 2GiB 1,8V 8-bit", 0xA5, 0, 2048, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
|
||||||
|
{"NAND 2GiB 3,3V 8-bit", 0xD5, 0, 2048, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
|
||||||
|
{"NAND 2GiB 1,8V 16-bit", 0xB5, 0, 2048, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
|
||||||
|
{"NAND 2GiB 3,3V 16-bit", 0xC5, 0, 2048, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
|
||||||
|
|
||||||
|
/* Renesas AND 1 Gigabit. Those chips do not support extended id and have a strange page/block layout !
|
||||||
|
* The chosen minimum erasesize is 4 * 2 * 2048 = 16384 Byte, as those chips have an array of 4 page planes
|
||||||
|
* 1 block = 2 pages, but due to plane arrangement the blocks 0-3 consists of page 0 + 4,1 + 5, 2 + 6, 3 + 7
|
||||||
|
* Anyway JFFS2 would increase the eraseblock size so we chose a combined one which can be erased in one go
|
||||||
|
* There are more speed improvements for reads and writes possible, but not implemented now
|
||||||
|
*/
|
||||||
|
{"AND 128MiB 3,3V 8-bit", 0x01, 2048, 128, 0x4000, NAND_IS_AND | NAND_NO_AUTOINCR | NAND_4PAGE_ARRAY},
|
||||||
|
|
||||||
|
{RT_NULL,}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* NAND Flash 生产厂家代码
|
||||||
|
*/
|
||||||
|
#define NAND_MFR_TOSHIBA 0x98
|
||||||
|
#define NAND_MFR_SAMSUNG 0xec
|
||||||
|
#define NAND_MFR_FUJITSU 0x04
|
||||||
|
#define NAND_MFR_NATIONAL 0x8f
|
||||||
|
#define NAND_MFR_RENESAS 0x07
|
||||||
|
#define NAND_MFR_STMICRO 0x20
|
||||||
|
|
||||||
|
/* 制造商ID列表 */
|
||||||
|
struct nand_manufacturers nand_manuf_ids[] =
|
||||||
|
{
|
||||||
|
{NAND_MFR_TOSHIBA, "Toshiba"},
|
||||||
|
{NAND_MFR_SAMSUNG, "Samsung"},
|
||||||
|
{NAND_MFR_FUJITSU, "Fujitsu"},
|
||||||
|
{NAND_MFR_NATIONAL,"National"},
|
||||||
|
{NAND_MFR_RENESAS, "Renesas"},
|
||||||
|
{NAND_MFR_STMICRO, "ST Micro"},
|
||||||
|
{0x0, "Unknown"}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,234 @@
|
||||||
|
/*
|
||||||
|
This file is part of UFFS, the Ultra-low-cost Flash File System.
|
||||||
|
|
||||||
|
Copyright (C) 2005-2010 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
|
||||||
|
|
||||||
|
UFFS is free software; you can redistribute it and/or modify it under
|
||||||
|
the GNU Library General Public License as published by the Free Software
|
||||||
|
Foundation; either version 2 of the License, or (at your option) any
|
||||||
|
later version.
|
||||||
|
|
||||||
|
UFFS is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
or GNU Library General Public License, as applicable, for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
and GNU Library General Public License along with UFFS; if not, write
|
||||||
|
to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
As a special exception, if other files instantiate templates or use
|
||||||
|
macros or inline functions from this file, or you compile this file
|
||||||
|
and link it with other works to produce a work based on this file,
|
||||||
|
this file does not by itself cause the resulting work to be covered
|
||||||
|
by the GNU General Public License. However the source code for this
|
||||||
|
file must still be made available in accordance with section (3) of
|
||||||
|
the GNU General Public License v2.
|
||||||
|
|
||||||
|
This exception does not invalidate any other reasons why a work based
|
||||||
|
on this file might be covered by the GNU General Public License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \file uffs_fileem_ecc_hw.c
|
||||||
|
* \brief emulate uffs file system for hardware ECC.
|
||||||
|
*
|
||||||
|
* In this emulator, we call 'uffs_FlashMakeSpare()' to do the layout job
|
||||||
|
* and call 'uffs_EccMake()' to calculate ECC.
|
||||||
|
*
|
||||||
|
* \author Ricky Zheng @ Oct, 2010
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "uffs/uffs_device.h"
|
||||||
|
#include "uffs_fileem.h"
|
||||||
|
#include "uffs/uffs_ecc.h"
|
||||||
|
|
||||||
|
#define PFX "femu: "
|
||||||
|
#define MSG(msg,...) uffs_PerrorRaw(UFFS_ERR_NORMAL, msg, ## __VA_ARGS__)
|
||||||
|
#define MSGLN(msg,...) uffs_Perror(UFFS_ERR_NORMAL, msg, ## __VA_ARGS__)
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uffs_FlashOps g_femu_ops_ecc_hw = {
|
||||||
|
femu_InitFlash, // InitFlash()
|
||||||
|
femu_ReleaseFlash, // ReleaseFlash()
|
||||||
|
NULL, // ReadPage()
|
||||||
|
femu_hw_ReadPageWithLayout, // ReadPageWithLayout()
|
||||||
|
NULL, // WritePage()
|
||||||
|
femu_hw_WritePageWithLayout,// WritePageWithLayout()
|
||||||
|
NULL, // IsBadBlock(), let UFFS take care of it.
|
||||||
|
NULL, // MarkBadBlock(), let UFFS take care of it.
|
||||||
|
femu_EraseBlock, // EraseBlock()
|
||||||
|
};
|
|
@ -0,0 +1,350 @@
|
||||||
|
/*
|
||||||
|
This file is part of UFFS, the Ultra-low-cost Flash File System.
|
||||||
|
|
||||||
|
Copyright (C) 2005-2010 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
|
||||||
|
|
||||||
|
UFFS is free software; you can redistribute it and/or modify it under
|
||||||
|
the GNU Library General Public License as published by the Free Software
|
||||||
|
Foundation; either version 2 of the License, or (at your option) any
|
||||||
|
later version.
|
||||||
|
|
||||||
|
UFFS is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
or GNU Library General Public License, as applicable, for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
and GNU Library General Public License along with UFFS; if not, write
|
||||||
|
to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
As a special exception, if other files instantiate templates or use
|
||||||
|
macros or inline functions from this file, or you compile this file
|
||||||
|
and link it with other works to produce a work based on this file,
|
||||||
|
this file does not by itself cause the resulting work to be covered
|
||||||
|
by the GNU General Public License. However the source code for this
|
||||||
|
file must still be made available in accordance with section (3) of
|
||||||
|
the GNU General Public License v2.
|
||||||
|
|
||||||
|
This exception does not invalidate any other reasons why a work based
|
||||||
|
on this file might be covered by the GNU General Public License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \file uffs_fileem_ecc_hw_auto.c
|
||||||
|
*
|
||||||
|
* \brief Emulate uffs file system for auto hardware ECC or RS error collection.
|
||||||
|
*
|
||||||
|
* This emulator emulate LPC32x0 MLC NAND controller which generate 10 bytes
|
||||||
|
* Reed-Solomon error correction code (RS-ECC) for every 518 bytes data.
|
||||||
|
*
|
||||||
|
* For small page MLC have 16 bytes spare area leves only 6 bytes for 'meta-data',
|
||||||
|
* no enough room for UFFS's 8 bytes tag and bad block mark. For this reason,
|
||||||
|
* we adjust page data/spare boundary to 508/20.
|
||||||
|
*
|
||||||
|
* This emulator does not calculate real RS-ECC code, instead, we use software ECC
|
||||||
|
* to calculate 6 bytes ECC code, so this solution does not have the same error
|
||||||
|
* correcting cabability of RS-ECC.
|
||||||
|
*
|
||||||
|
* Note: the MLC controller strictly require sequencial access to serial data buffer.
|
||||||
|
*
|
||||||
|
* \author Ricky Zheng @ Oct, 2010
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "uffs/uffs_device.h"
|
||||||
|
#include "uffs_fileem.h"
|
||||||
|
|
||||||
|
#define PFX "femu: "
|
||||||
|
#define MSG(msg,...) uffs_PerrorRaw(UFFS_ERR_NORMAL, msg, ## __VA_ARGS__)
|
||||||
|
#define MSGLN(msg,...) uffs_Perror(UFFS_ERR_NORMAL, msg, ## __VA_ARGS__)
|
||||||
|
|
||||||
|
#define RS_ECC_SIZE 10
|
||||||
|
#define PAGE_DATA_SIZE 508
|
||||||
|
#define PAGE_SPARE_SIZE 20
|
||||||
|
#define PAGE_FULL_SIZE (PAGE_DATA_SIZE + PAGE_SPARE_SIZE)
|
||||||
|
static u8 g_sdata_buf[PAGE_FULL_SIZE]; // emulating LPC32x0's 528-bytes serial data buffer
|
||||||
|
|
||||||
|
static int g_sdata_buf_pointer = 0;
|
||||||
|
|
||||||
|
static void start_sdata_access()
|
||||||
|
{
|
||||||
|
g_sdata_buf_pointer = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void feed_sdata(const u8 *data, int len)
|
||||||
|
{
|
||||||
|
uffs_Assert(g_sdata_buf_pointer + len <= sizeof(g_sdata_buf), "BUG: Serial Data Buffer overflow !!");
|
||||||
|
if (data)
|
||||||
|
memcpy(g_sdata_buf + g_sdata_buf_pointer, data, len);
|
||||||
|
g_sdata_buf_pointer += len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void feed_sdata_constant(u8 val, int num)
|
||||||
|
{
|
||||||
|
uffs_Assert(g_sdata_buf_pointer + num <= sizeof(g_sdata_buf), "BUG: Serial Data Buffer overflow !!");
|
||||||
|
memset(g_sdata_buf + g_sdata_buf_pointer, val, num);
|
||||||
|
g_sdata_buf_pointer += num;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void drain_sdata(u8 *data, int len)
|
||||||
|
{
|
||||||
|
uffs_Assert(sizeof(g_sdata_buf) - g_sdata_buf_pointer >= len, "BUG: Serial Data Buffer overdrain !!");
|
||||||
|
if (data)
|
||||||
|
memcpy(data, g_sdata_buf + g_sdata_buf_pointer, len);
|
||||||
|
g_sdata_buf_pointer += len;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 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 femu_hw_auto_InitFlash(uffs_Device *dev)
|
||||||
|
{
|
||||||
|
struct uffs_StorageAttrSt *attr = dev->attr;
|
||||||
|
|
||||||
|
// now this is a good chance to adjust page data/spare boundary
|
||||||
|
if (attr->page_data_size + attr->spare_size != PAGE_FULL_SIZE) {
|
||||||
|
MSGLN("This emulator emulates only for page size %d bytes !", PAGE_FULL_SIZE);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (attr->spare_size < PAGE_SPARE_SIZE) {
|
||||||
|
attr->page_data_size -= (PAGE_SPARE_SIZE - attr->spare_size);
|
||||||
|
attr->spare_size = PAGE_SPARE_SIZE;
|
||||||
|
MSGLN("Adjust page data/spare boundary to %d/%d", attr->page_data_size, attr->spare_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// and fix ECC size
|
||||||
|
attr->ecc_size = RS_ECC_SIZE;
|
||||||
|
MSGLN("Adjust ECC size to %d bytes", attr->ecc_size);
|
||||||
|
|
||||||
|
return femu_InitFlash(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int femu_hw_auto_WritePageWithLayout(uffs_Device *dev, u32 block, u32 page,
|
||||||
|
const u8 *data, int data_len, const u8 *ecc, const uffs_TagStore *ts)
|
||||||
|
{
|
||||||
|
int abs_page;
|
||||||
|
uffs_FileEmu *emu;
|
||||||
|
struct uffs_StorageAttrSt *attr = dev->attr;
|
||||||
|
u8 spare[PAGE_SPARE_SIZE];
|
||||||
|
int ret = UFFS_FLASH_IO_ERR;
|
||||||
|
|
||||||
|
emu = (uffs_FileEmu *)(dev->attr->_private);
|
||||||
|
|
||||||
|
if (!emu || !(emu->fp)) {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
abs_page = attr->pages_per_block * block + page;
|
||||||
|
|
||||||
|
start_sdata_access();
|
||||||
|
|
||||||
|
dev->st.page_write_count++;
|
||||||
|
dev->st.spare_write_count++;
|
||||||
|
dev->st.io_write += PAGE_FULL_SIZE;
|
||||||
|
|
||||||
|
if (data || ts) {
|
||||||
|
// normal page write
|
||||||
|
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) {
|
||||||
|
MSGLN("Warrning: block %d page %d exceed it's maximum write time!", block, page);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy data to serial data buffer
|
||||||
|
feed_sdata(data, data_len);
|
||||||
|
|
||||||
|
// Pad the rest data as 0xFF
|
||||||
|
feed_sdata_constant(0xFF, attr->page_data_size - data_len);
|
||||||
|
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// We still need to feed data to serial data buffer to make MLC controller happy
|
||||||
|
// The current UFFS won't write ts only, so we'll never run to here.
|
||||||
|
feed_sdata_constant(0xFF, attr->page_data_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ts) {
|
||||||
|
|
||||||
|
emu->em_monitor_spare[abs_page]++;
|
||||||
|
if (emu->em_monitor_spare[abs_page] > PAGE_SPARE_WRITE_COUNT_LIMIT) {
|
||||||
|
MSGLN("Warrning: block %d page %d (spare) exceed it's maximum write time!", block, page);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(spare, 0xFF, sizeof(spare));
|
||||||
|
uffs_FlashMakeSpare(dev, ts, NULL, spare); // do not pack ECC, as MLC controller will
|
||||||
|
// automatically write RS-ECC to the latest 10 bytes.
|
||||||
|
|
||||||
|
// feed spare data to serial data buffer
|
||||||
|
feed_sdata(spare, PAGE_SPARE_SIZE - RS_ECC_SIZE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// mark bad block
|
||||||
|
|
||||||
|
// feed data to serial data buffer to make MLC controller happy
|
||||||
|
feed_sdata_constant(0xFF, attr->page_data_size);
|
||||||
|
|
||||||
|
memset(spare, 0xFF, sizeof(spare));
|
||||||
|
spare[attr->block_status_offs] = 0;
|
||||||
|
|
||||||
|
// feed spare data to serial data buffer
|
||||||
|
feed_sdata(spare, PAGE_SPARE_SIZE - RS_ECC_SIZE);
|
||||||
|
|
||||||
|
dev->st.io_write++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// now, program serial data buffer to NAND flash
|
||||||
|
ret = program_sdata(dev, block, page);
|
||||||
|
|
||||||
|
fflush(emu->fp);
|
||||||
|
return ret;
|
||||||
|
err:
|
||||||
|
fflush(emu->fp);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static URET femu_hw_auto_ReadPageWithLayout(uffs_Device *dev, u32 block, u32 page, u8* data, int data_len, u8 *ecc,
|
||||||
|
uffs_TagStore *ts, u8 *ecc_store)
|
||||||
|
{
|
||||||
|
uffs_FileEmu *emu;
|
||||||
|
int abs_page;
|
||||||
|
struct uffs_StorageAttrSt *attr = dev->attr;
|
||||||
|
unsigned char status;
|
||||||
|
int spare_len;
|
||||||
|
u8 spare[PAGE_SPARE_SIZE];
|
||||||
|
u8 ecc_buf[RS_ECC_SIZE];
|
||||||
|
int ret = UFFS_FLASH_IO_ERR;
|
||||||
|
|
||||||
|
emu = (uffs_FileEmu *)(dev->attr->_private);
|
||||||
|
|
||||||
|
if (!emu || !(emu->fp)) {
|
||||||
|
goto ext;
|
||||||
|
}
|
||||||
|
|
||||||
|
abs_page = attr->pages_per_block * block + page;
|
||||||
|
|
||||||
|
// now load full page to serial data buffer
|
||||||
|
ret = load_sdata(dev, block, page);
|
||||||
|
if (ret != UFFS_FLASH_NO_ERR)
|
||||||
|
goto ext;
|
||||||
|
|
||||||
|
start_sdata_access();
|
||||||
|
|
||||||
|
dev->st.io_read += PAGE_FULL_SIZE;
|
||||||
|
dev->st.page_read_count++;
|
||||||
|
dev->st.spare_read_count++;
|
||||||
|
|
||||||
|
if (data || ts) {
|
||||||
|
|
||||||
|
if (data && data_len > 0) {
|
||||||
|
if (data_len > attr->page_data_size)
|
||||||
|
goto ext;
|
||||||
|
|
||||||
|
drain_sdata(data, data_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ts) {
|
||||||
|
if (g_sdata_buf_pointer < attr->page_data_size)
|
||||||
|
drain_sdata(NULL, attr->page_data_size - g_sdata_buf_pointer);
|
||||||
|
|
||||||
|
drain_sdata(spare, PAGE_SPARE_SIZE - RS_ECC_SIZE);
|
||||||
|
|
||||||
|
// unload ts from spare
|
||||||
|
uffs_FlashUnloadSpare(dev, spare, ts, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// read bad block mark
|
||||||
|
drain_sdata(NULL, attr->page_data_size + attr->block_status_offs - 1);
|
||||||
|
drain_sdata(&status, 1);
|
||||||
|
|
||||||
|
ret = (status == 0xFF ? UFFS_FLASH_NO_ERR : UFFS_FLASH_BAD_BLK);
|
||||||
|
}
|
||||||
|
|
||||||
|
ext:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uffs_FlashOps g_femu_ops_ecc_hw_auto = {
|
||||||
|
femu_hw_auto_InitFlash, // InitFlash()
|
||||||
|
femu_ReleaseFlash, // ReleaseFlash()
|
||||||
|
NULL, // ReadPage()
|
||||||
|
femu_hw_auto_ReadPageWithLayout, // ReadPageWithLayout()
|
||||||
|
NULL, // WritePage()
|
||||||
|
femu_hw_auto_WritePageWithLayout, // WirtePageWithLayout()
|
||||||
|
NULL, // IsBadBlock(), let UFFS take care of it.
|
||||||
|
NULL, // MarkBadBlock(), let UFFS take care of it.
|
||||||
|
femu_EraseBlock, // EraseBlock()
|
||||||
|
};
|
|
@ -0,0 +1,212 @@
|
||||||
|
/*
|
||||||
|
This file is part of UFFS, the Ultra-low-cost Flash File System.
|
||||||
|
|
||||||
|
Copyright (C) 2005-2010 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
|
||||||
|
|
||||||
|
UFFS is free software; you can redistribute it and/or modify it under
|
||||||
|
the GNU Library General Public License as published by the Free Software
|
||||||
|
Foundation; either version 2 of the License, or (at your option) any
|
||||||
|
later version.
|
||||||
|
|
||||||
|
UFFS is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
or GNU Library General Public License, as applicable, for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
and GNU Library General Public License along with UFFS; if not, write
|
||||||
|
to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
As a special exception, if other files instantiate templates or use
|
||||||
|
macros or inline functions from this file, or you compile this file
|
||||||
|
and link it with other works to produce a work based on this file,
|
||||||
|
this file does not by itself cause the resulting work to be covered
|
||||||
|
by the GNU General Public License. However the source code for this
|
||||||
|
file must still be made available in accordance with section (3) of
|
||||||
|
the GNU General Public License v2.
|
||||||
|
|
||||||
|
This exception does not invalidate any other reasons why a work based
|
||||||
|
on this file might be covered by the GNU General Public License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \file uffs_fileem_ecc_soft.c
|
||||||
|
* \brief emulate uffs file system for software ECC
|
||||||
|
* \author Ricky Zheng @ Oct, 2010
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "uffs/uffs_device.h"
|
||||||
|
#include "uffs/uffs_flash.h"
|
||||||
|
#include "uffs_fileem.h"
|
||||||
|
|
||||||
|
#define PFX "femu: "
|
||||||
|
#define MSG(msg,...) uffs_PerrorRaw(UFFS_ERR_NORMAL, msg, ## __VA_ARGS__)
|
||||||
|
#define MSGLN(msg,...) uffs_Perror(UFFS_ERR_NORMAL, msg, ## __VA_ARGS__)
|
||||||
|
|
||||||
|
static int femu_WritePage(uffs_Device *dev, u32 block, u32 page_num,
|
||||||
|
const u8 *data, int data_len, const u8 *spare, int spare_len)
|
||||||
|
{
|
||||||
|
int written;
|
||||||
|
int abs_page;
|
||||||
|
int full_page_size;
|
||||||
|
uffs_FileEmu *emu;
|
||||||
|
struct uffs_StorageAttrSt *attr = dev->attr;
|
||||||
|
|
||||||
|
emu = (uffs_FileEmu *)(dev->attr->_private);
|
||||||
|
|
||||||
|
if (!emu || !(emu->fp)) {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
abs_page = attr->pages_per_block * block + page_num;
|
||||||
|
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) {
|
||||||
|
MSGLN("Warrning: block %d page %d exceed it's maximum write time!", block, page_num);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
fseek(emu->fp, abs_page * full_page_size, SEEK_SET);
|
||||||
|
|
||||||
|
written = fwrite(data, 1, data_len, emu->fp);
|
||||||
|
|
||||||
|
if (written != data_len) {
|
||||||
|
MSGLN("write page I/O error ?");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev->st.page_write_count++;
|
||||||
|
dev->st.io_write += written;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (spare && spare_len > 0) {
|
||||||
|
if (spare_len > attr->spare_size)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
emu->em_monitor_spare[abs_page]++;
|
||||||
|
if (emu->em_monitor_spare[abs_page] > PAGE_SPARE_WRITE_COUNT_LIMIT) {
|
||||||
|
MSGLN("Warrning: block %d page %d (spare) exceed it's maximum write time!", block, page_num);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
MSGLN("write spare I/O error ?");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev->st.spare_write_count++;
|
||||||
|
dev->st.io_write += written;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data == NULL && spare == 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) {
|
||||||
|
MSGLN("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_ReadPage(uffs_Device *dev, u32 block, u32 page_num, u8 *data, int data_len, u8 *ecc,
|
||||||
|
u8 *spare, int spare_len)
|
||||||
|
{
|
||||||
|
int nread;
|
||||||
|
uffs_FileEmu *emu;
|
||||||
|
int abs_page;
|
||||||
|
int full_page_size;
|
||||||
|
struct uffs_StorageAttrSt *attr = dev->attr;
|
||||||
|
unsigned char status;
|
||||||
|
|
||||||
|
emu = (uffs_FileEmu *)(dev->attr->_private);
|
||||||
|
|
||||||
|
if (!emu || !(emu->fp)) {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
abs_page = attr->pages_per_block * block + page_num;
|
||||||
|
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) {
|
||||||
|
MSGLN("read page I/O error ?");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
dev->st.io_read += nread;
|
||||||
|
dev->st.page_read_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (spare && spare_len > 0) {
|
||||||
|
if (spare_len > attr->spare_size)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
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) {
|
||||||
|
MSGLN("read page spare I/O error ?");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
dev->st.io_read += nread;
|
||||||
|
dev->st.spare_read_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data == NULL && spare == 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) {
|
||||||
|
MSGLN("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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uffs_FlashOps g_femu_ops_ecc_soft = {
|
||||||
|
femu_InitFlash, // InitFlash()
|
||||||
|
femu_ReleaseFlash, // ReleaseFlash()
|
||||||
|
femu_ReadPage, // ReadPage()
|
||||||
|
NULL, // ReadPageWithLayout
|
||||||
|
femu_WritePage, // WritePage()
|
||||||
|
NULL, // WirtePageWithLayout
|
||||||
|
NULL, // IsBadBlock(), let UFFS take care of it.
|
||||||
|
NULL, // MarkBadBlock(), let UFFS take care of it.
|
||||||
|
femu_EraseBlock, // EraseBlock()
|
||||||
|
};
|
|
@ -0,0 +1,224 @@
|
||||||
|
/*
|
||||||
|
This file is part of UFFS, the Ultra-low-cost Flash File System.
|
||||||
|
|
||||||
|
Copyright (C) 2005-2009 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
|
||||||
|
|
||||||
|
UFFS is free software; you can redistribute it and/or modify it under
|
||||||
|
the GNU Library General Public License as published by the Free Software
|
||||||
|
Foundation; either version 2 of the License, or (at your option) any
|
||||||
|
later version.
|
||||||
|
|
||||||
|
UFFS is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
or GNU Library General Public License, as applicable, for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
and GNU Library General Public License along with UFFS; if not, write
|
||||||
|
to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
As a special exception, if other files instantiate templates or use
|
||||||
|
macros or inline functions from this file, or you compile this file
|
||||||
|
and link it with other works to produce a work based on this file,
|
||||||
|
this file does not by itself cause the resulting work to be covered
|
||||||
|
by the GNU General Public License. However the source code for this
|
||||||
|
file must still be made available in accordance with section (3) of
|
||||||
|
the GNU General Public License v2.
|
||||||
|
|
||||||
|
This exception does not invalidate any other reasons why a work based
|
||||||
|
on this file might be covered by the GNU General Public License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \file uffs_fileem_share.c
|
||||||
|
* \brief emulate uffs file system, shared functions
|
||||||
|
* \author Ricky Zheng, created Nov, 2010
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "uffs/uffs_device.h"
|
||||||
|
#include "uffs_fileem.h"
|
||||||
|
|
||||||
|
#define PFX "femu: "
|
||||||
|
|
||||||
|
/****************************************************************/
|
||||||
|
/* Shared flash driver functions: */
|
||||||
|
/* */
|
||||||
|
/* femu_InitFlash(), femu_ReleaseFlash(), femu_EraseBlock() */
|
||||||
|
/* */
|
||||||
|
/****************************************************************/
|
||||||
|
|
||||||
|
static u8 g_page_buf[UFFS_MAX_PAGE_SIZE + UFFS_MAX_SPARE_SIZE];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create emulator disk, initialise monitors, inject manufacture bad blocks, etc.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
int femu_InitFlash(uffs_Device *dev)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int fSize;
|
||||||
|
int written;
|
||||||
|
u8 * p = g_page_buf;
|
||||||
|
uffs_FileEmu *emu;
|
||||||
|
|
||||||
|
struct uffs_StorageAttrSt *attr = dev->attr;
|
||||||
|
|
||||||
|
int full_page_size = attr->page_data_size + attr->spare_size;
|
||||||
|
int blk_size = full_page_size * attr->pages_per_block;
|
||||||
|
int total_pages = attr->total_blocks * attr->pages_per_block;
|
||||||
|
|
||||||
|
emu = (uffs_FileEmu *)(dev->attr->_private);
|
||||||
|
|
||||||
|
if (emu->initCount > 0) {
|
||||||
|
emu->initCount++;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (emu->emu_filename == NULL)
|
||||||
|
emu->emu_filename = UFFS_FEMU_FILE_NAME;
|
||||||
|
|
||||||
|
uffs_Perror(UFFS_ERR_NORMAL, "femu device init.");
|
||||||
|
|
||||||
|
emu->em_monitor_page = (u8 *) malloc(total_pages);
|
||||||
|
if (!emu->em_monitor_page)
|
||||||
|
return -1;
|
||||||
|
emu->em_monitor_spare = (u8 *) malloc(total_pages);
|
||||||
|
if (!emu->em_monitor_spare)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
|
||||||
|
//clear monitor
|
||||||
|
memset(emu->em_monitor_page, 0, total_pages);
|
||||||
|
memset(emu->em_monitor_spare, 0, total_pages);
|
||||||
|
|
||||||
|
emu->fp = fopen(emu->emu_filename, "rb");
|
||||||
|
if (emu->fp == NULL) {
|
||||||
|
emu->fp = fopen(emu->emu_filename, "ab+");
|
||||||
|
if (emu->fp == NULL) {
|
||||||
|
printf(PFX"Failed to create uffs emulation file.");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fseek(emu->fp, 0, SEEK_END);
|
||||||
|
fSize = ftell(emu->fp);
|
||||||
|
|
||||||
|
if (fSize < total_pages * full_page_size) {
|
||||||
|
printf("Creating uffs emulation file\n");
|
||||||
|
fseek(emu->fp, 0, SEEK_SET);
|
||||||
|
memset(p, 0xff, full_page_size);
|
||||||
|
for (i = 0; i < total_pages; i++) {
|
||||||
|
written = fwrite(p, 1, full_page_size, emu->fp);
|
||||||
|
if (written != full_page_size) {
|
||||||
|
printf("Write failed\n");
|
||||||
|
fclose(emu->fp);
|
||||||
|
emu->fp = NULL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fflush(emu->fp);
|
||||||
|
fclose(emu->fp);
|
||||||
|
|
||||||
|
emu->fp = fopen(emu->emu_filename, "rb+");
|
||||||
|
if (emu->fp == NULL) {
|
||||||
|
printf(PFX"Can't open emulation file.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
emu->initCount++;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Release resources
|
||||||
|
*/
|
||||||
|
int femu_ReleaseFlash(uffs_Device *dev)
|
||||||
|
{
|
||||||
|
uffs_FileEmu *emu;
|
||||||
|
|
||||||
|
emu = (uffs_FileEmu *)(dev->attr->_private);
|
||||||
|
|
||||||
|
emu->initCount--;
|
||||||
|
|
||||||
|
if (emu->initCount == 0) {
|
||||||
|
|
||||||
|
uffs_Perror(UFFS_ERR_NORMAL, "femu device release.");
|
||||||
|
|
||||||
|
if (emu->fp) {
|
||||||
|
fclose(emu->fp);
|
||||||
|
emu->fp = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (emu->em_monitor_page)
|
||||||
|
free(emu->em_monitor_page);
|
||||||
|
if (emu->em_monitor_spare)
|
||||||
|
free(emu->em_monitor_spare);
|
||||||
|
emu->em_monitor_page = NULL;
|
||||||
|
emu->em_monitor_spare = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int femu_EraseBlock(uffs_Device *dev, u32 blockNumber)
|
||||||
|
{
|
||||||
|
|
||||||
|
int i;
|
||||||
|
u8 * pg = g_page_buf;
|
||||||
|
int pg_size, pgd_size, sp_size, blks, blk_pgs, blk_size;
|
||||||
|
uffs_FileEmu *emu;
|
||||||
|
emu = (uffs_FileEmu *)(dev->attr->_private);
|
||||||
|
if (!emu || !(emu->fp))
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
pg_size = dev->attr->page_data_size + dev->attr->spare_size;
|
||||||
|
pgd_size = dev->attr->page_data_size;
|
||||||
|
sp_size = dev->attr->spare_size;
|
||||||
|
blk_pgs = dev->attr->pages_per_block;
|
||||||
|
blks = dev->attr->total_blocks;
|
||||||
|
blk_size = dev->attr->page_data_size * dev->attr->pages_per_block;
|
||||||
|
|
||||||
|
printf("femu: erase block %d\n", blockNumber);
|
||||||
|
|
||||||
|
if ((int)blockNumber >= blks) {
|
||||||
|
printf("Attempt to erase non-existant block %d\n",blockNumber);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
//clear this block monitors
|
||||||
|
memset(emu->em_monitor_page + (blockNumber * blk_pgs),
|
||||||
|
0,
|
||||||
|
blk_pgs * sizeof(u8));
|
||||||
|
memset(emu->em_monitor_spare + (blockNumber * blk_pgs),
|
||||||
|
0,
|
||||||
|
blk_pgs * sizeof(u8));
|
||||||
|
|
||||||
|
memset(pg, 0xff, (pgd_size + sp_size));
|
||||||
|
|
||||||
|
fseek(emu->fp, blockNumber * blk_pgs * (pgd_size + sp_size), SEEK_SET);
|
||||||
|
|
||||||
|
for (i = 0; i < blk_pgs; i++) {
|
||||||
|
fwrite(pg, 1, (pgd_size + sp_size), emu->fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
fflush(emu->fp);
|
||||||
|
dev->st.block_erase_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return UFFS_FLASH_NO_ERR;
|
||||||
|
err:
|
||||||
|
return UFFS_FLASH_IO_ERR;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,260 @@
|
||||||
|
/*
|
||||||
|
This file is part of UFFS, the Ultra-low-cost Flash File System.
|
||||||
|
|
||||||
|
Copyright (C) 2005-2009 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
|
||||||
|
|
||||||
|
UFFS is free software; you can redistribute it and/or modify it under
|
||||||
|
the GNU Library General Public License as published by the Free Software
|
||||||
|
Foundation; either version 2 of the License, or (at your option) any
|
||||||
|
later version.
|
||||||
|
|
||||||
|
UFFS is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
or GNU Library General Public License, as applicable, for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
and GNU Library General Public License along with UFFS; if not, write
|
||||||
|
to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
As a special exception, if other files instantiate templates or use
|
||||||
|
macros or inline functions from this file, or you compile this file
|
||||||
|
and link it with other works to produce a work based on this file,
|
||||||
|
this file does not by itself cause the resulting work to be covered
|
||||||
|
by the GNU General Public License. However the source code for this
|
||||||
|
file must still be made available in accordance with section (3) of
|
||||||
|
the GNU General Public License v2.
|
||||||
|
|
||||||
|
This exception does not invalidate any other reasons why a work based
|
||||||
|
on this file might be covered by the GNU General Public License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \file uffs_fileem_wrap.c
|
||||||
|
*
|
||||||
|
* \brief file emulator wrapper functions for injecting bad blocks or ECC errors.
|
||||||
|
*
|
||||||
|
* \author Ricky Zheng, created Nov, 2010
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "uffs/uffs_device.h"
|
||||||
|
#include "uffs_fileem.h"
|
||||||
|
|
||||||
|
#define PFX "femu: "
|
||||||
|
|
||||||
|
#ifdef UFFS_FEMU_ENABLE_INJECTION
|
||||||
|
|
||||||
|
struct uffs_FileEmuBitFlip {
|
||||||
|
int block;
|
||||||
|
int page;
|
||||||
|
int offset;
|
||||||
|
u8 mask;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* simulate bad blocks */
|
||||||
|
#define FILEEMU_STOCK_BAD_BLOCKS {5, 18} // bad block come from manufacture
|
||||||
|
#define FILEEMU_ERASE_BAD_BLOCKS {10, 15} // new bad block discovered when erasing
|
||||||
|
|
||||||
|
/* simulating bit flip */
|
||||||
|
#define FILEEMU_WRITE_BIT_FLIP \
|
||||||
|
{ \
|
||||||
|
{2, 2, 10, 1 << 4}, /* block 2, page 2, offset 10, bit 4 */ \
|
||||||
|
{2, 4, -3, 1 << 2}, /* block 2, page 4, spare offset 3, bit 2*/ \
|
||||||
|
{6, 1, 5, 1 << 3}, /* block 6, page 1, offset 5, bit 3 */ \
|
||||||
|
{6, 1, 15, 1 << 7}, /* block 6, page 1, offset 300, bit 7 */ \
|
||||||
|
{8, 2, 2, 1 << 1}, /* block 8, page 2, offset 2, bit 1 */ \
|
||||||
|
{8, 2, 100, 1 << 5},/* block 8, page 2, offset 100, bit 5 */ \
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int femu_InitFlash_wrap(uffs_Device *dev);
|
||||||
|
|
||||||
|
static int femu_ReadPage_wrap(uffs_Device *dev, u32 block, u32 page, u8 *data, int data_len, u8 *ecc,
|
||||||
|
u8 *spare, int spare_len);
|
||||||
|
static int femu_WritePage_wrap(uffs_Device *dev, u32 block, u32 page,
|
||||||
|
const u8 *data, int data_len, const u8 *spare, int spare_len);
|
||||||
|
static int femu_WritePageWithLayout_wrap(uffs_Device *dev, u32 block, u32 page, const u8* data, int data_len, const u8 *ecc,
|
||||||
|
const uffs_TagStore *ts);
|
||||||
|
static int femu_EraseBlock_wrap(uffs_Device *dev, u32 blockNumber);
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void femu_setup_wrapper_functions(uffs_Device *dev)
|
||||||
|
{
|
||||||
|
uffs_FileEmu *emu;
|
||||||
|
emu = (uffs_FileEmu *)(dev->attr->_private);
|
||||||
|
|
||||||
|
// setup wrap functions, for inject ECC errors, etc.
|
||||||
|
|
||||||
|
memcpy(&emu->ops_orig, dev->ops, sizeof(struct uffs_FlashOpsSt));
|
||||||
|
|
||||||
|
if (dev->ops->InitFlash)
|
||||||
|
dev->ops->InitFlash = femu_InitFlash_wrap;
|
||||||
|
if (dev->ops->EraseBlock)
|
||||||
|
dev->ops->EraseBlock = femu_EraseBlock_wrap;
|
||||||
|
if (dev->ops->ReadPage)
|
||||||
|
dev->ops->ReadPage = femu_ReadPage_wrap;
|
||||||
|
//if (dev->ops->ReadPageWithLayout)
|
||||||
|
// dev->ops->ReadPageWithLayout = femu_ReadPageWithLayout_wrap;
|
||||||
|
if (dev->ops->WritePage)
|
||||||
|
dev->ops->WritePage = femu_WritePage_wrap;
|
||||||
|
if (dev->ops->WritePageWithLayout)
|
||||||
|
dev->ops->WritePageWithLayout = femu_WritePageWithLayout_wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int femu_InitFlash_wrap(uffs_Device *dev)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
uffs_FileEmu *emu = (uffs_FileEmu *)(dev->attr->_private);
|
||||||
|
|
||||||
|
#ifdef FILEEMU_STOCK_BAD_BLOCKS
|
||||||
|
int bad_blocks[] = FILEEMU_STOCK_BAD_BLOCKS;
|
||||||
|
int j;
|
||||||
|
u8 x = 0;
|
||||||
|
struct uffs_StorageAttrSt *attr = dev->attr;
|
||||||
|
int full_page_size = attr->page_data_size + attr->spare_size;
|
||||||
|
int blk_size = full_page_size * attr->pages_per_block;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (emu->initCount == 0) {
|
||||||
|
ret = emu->ops_orig.InitFlash(dev);
|
||||||
|
#ifdef FILEEMU_STOCK_BAD_BLOCKS
|
||||||
|
if (ret >= 0) {
|
||||||
|
for (j = 0; j < ARRAY_SIZE(bad_blocks); j++) {
|
||||||
|
if (bad_blocks[j] < dev->attr->total_blocks) {
|
||||||
|
printf(" --- manufacture bad block %d ---\n", bad_blocks[j]);
|
||||||
|
fseek(emu->fp, bad_blocks[j] * blk_size + attr->page_data_size + dev->attr->block_status_offs, SEEK_SET);
|
||||||
|
fwrite(&x, 1, 1, emu->fp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ret = emu->ops_orig.InitFlash(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int femu_ReadPage_wrap(uffs_Device *dev, u32 block, u32 page, u8 *data, int data_len, u8 *ecc,
|
||||||
|
u8 *spare, int spare_len)
|
||||||
|
{
|
||||||
|
uffs_FileEmu *emu = (uffs_FileEmu *)(dev->attr->_private);
|
||||||
|
|
||||||
|
//printf("femu: Read block %d page %d data %d spare %d\n", block, page, data_len, spare_len);
|
||||||
|
|
||||||
|
return emu->ops_orig.ReadPage(dev, block, page, data, data_len, ecc, spare, spare_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////// wraper functions ///////////////////////////
|
||||||
|
|
||||||
|
static void InjectBitFlip(uffs_Device *dev, u32 block, u32 page)
|
||||||
|
{
|
||||||
|
#ifdef FILEEMU_WRITE_BIT_FLIP
|
||||||
|
uffs_FileEmu *emu = (uffs_FileEmu *)(dev->attr->_private);
|
||||||
|
struct uffs_FileEmuBitFlip flips[] = FILEEMU_WRITE_BIT_FLIP;
|
||||||
|
struct uffs_FileEmuBitFlip *x;
|
||||||
|
u8 buf[UFFS_MAX_PAGE_SIZE + UFFS_MAX_SPARE_SIZE];
|
||||||
|
u8 *data = buf;
|
||||||
|
u8 *spare = buf + dev->attr->page_data_size;
|
||||||
|
int full_page_size = dev->attr->page_data_size + dev->attr->spare_size;
|
||||||
|
int blk_size = full_page_size * dev->attr->pages_per_block;
|
||||||
|
int page_offset = block * blk_size + full_page_size * page;
|
||||||
|
|
||||||
|
int i;
|
||||||
|
u8 *p;
|
||||||
|
|
||||||
|
fseek(emu->fp, page_offset, SEEK_SET);
|
||||||
|
fread(buf, 1, full_page_size, emu->fp);
|
||||||
|
|
||||||
|
p = NULL;
|
||||||
|
for (i = 0; i < ARRAY_SIZE(flips); i++) {
|
||||||
|
x = &flips[i];
|
||||||
|
if (x->block == block && x->page == page) {
|
||||||
|
if (x->offset >= 0) {
|
||||||
|
printf(" --- Inject data bit flip at block%d, page%d, offset%d, mask%d --- \n", block, page, x->offset, x->mask);
|
||||||
|
p = (u8 *)(data + x->offset);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
printf(" --- Inject spare bit flip at block%d, page%d, offset%d, mask%d --- \n", block, page, -x->offset, x->mask);
|
||||||
|
p = (u8 *)(spare - x->offset);
|
||||||
|
}
|
||||||
|
*p = (*p & ~x->mask) | (~(*p & x->mask) & x->mask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p) {
|
||||||
|
fseek(emu->fp, page_offset, SEEK_SET);
|
||||||
|
fwrite(buf, 1, full_page_size, emu->fp);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static int femu_WritePage_wrap(uffs_Device *dev, u32 block, u32 page,
|
||||||
|
const u8 *data, int data_len, const u8 *spare, int spare_len)
|
||||||
|
{
|
||||||
|
uffs_FileEmu *emu = (uffs_FileEmu *)(dev->attr->_private);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
//printf("femu: Write block %d page %d data %d spare %d\n", block, page, data_len, spare_len);
|
||||||
|
|
||||||
|
ret = emu->ops_orig.WritePage(dev, block, page, data, data_len, spare, spare_len);
|
||||||
|
|
||||||
|
InjectBitFlip(dev, block, page);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int femu_WritePageWithLayout_wrap(uffs_Device *dev, u32 block, u32 page, const u8* data, int data_len, const u8 *ecc,
|
||||||
|
const uffs_TagStore *ts)
|
||||||
|
{
|
||||||
|
uffs_FileEmu *emu = (uffs_FileEmu *)(dev->attr->_private);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
//printf("femu: Write block %d page %d data %d spare %d\n", block, page, data_len, spare_len);
|
||||||
|
|
||||||
|
ret = emu->ops_orig.WritePageWithLayout(dev, block, page, data, data_len, ecc, ts);
|
||||||
|
|
||||||
|
InjectBitFlip(dev, block, page);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int femu_EraseBlock_wrap(uffs_Device *dev, u32 blockNumber)
|
||||||
|
{
|
||||||
|
uffs_FileEmu *emu = (uffs_FileEmu *)(dev->attr->_private);
|
||||||
|
|
||||||
|
#ifdef FILEEMU_ERASE_BAD_BLOCKS
|
||||||
|
int blocks[] = FILEEMU_ERASE_BAD_BLOCKS;
|
||||||
|
int i;
|
||||||
|
URET ret;
|
||||||
|
ret = emu->ops_orig.EraseBlock(dev, blockNumber);
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(blocks); i++) {
|
||||||
|
if (blockNumber == blocks[i]) {
|
||||||
|
printf(" --- Inject bad block%d when erasing --- \n", blockNumber);
|
||||||
|
ret = UFFS_FLASH_BAD_BLK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
return emu->ops_orig.EraseBlock(dev, blockNumber);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // UFFS_FEMU_ENABLE_INJECTION
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////
|
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
This file is part of UFFS, the Ultra-low-cost Flash File System.
|
||||||
|
|
||||||
|
Copyright (C) 2005-2009 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
|
||||||
|
|
||||||
|
UFFS is free software; you can redistribute it and/or modify it under
|
||||||
|
the GNU Library General Public License as published by the Free Software
|
||||||
|
Foundation; either version 2 of the License, or (at your option) any
|
||||||
|
later version.
|
||||||
|
|
||||||
|
UFFS is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
or GNU Library General Public License, as applicable, for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
and GNU Library General Public License along with UFFS; if not, write
|
||||||
|
to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
As a special exception, if other files instantiate templates or use
|
||||||
|
macros or inline functions from this file, or you compile this file
|
||||||
|
and link it with other works to produce a work based on this file,
|
||||||
|
this file does not by itself cause the resulting work to be covered
|
||||||
|
by the GNU General Public License. However the source code for this
|
||||||
|
file must still be made available in accordance with section (3) of
|
||||||
|
the GNU General Public License v2.
|
||||||
|
|
||||||
|
This exception does not invalidate any other reasons why a work based
|
||||||
|
on this file might be covered by the GNU General Public License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \file uffs_fileem.h
|
||||||
|
* \brief Emulate NAND flash with host file.
|
||||||
|
* \author Ricky Zheng
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _UFFS_FILEEM_H_
|
||||||
|
#define _UFFS_FILEEM_H_
|
||||||
|
|
||||||
|
#include "uffs_device.h"
|
||||||
|
|
||||||
|
typedef struct uffs_FileEmuSt {
|
||||||
|
int initCount;
|
||||||
|
FILE *fp;
|
||||||
|
u8 *em_monitor_page;
|
||||||
|
u8 * em_monitor_spare;
|
||||||
|
const char *emu_filename;
|
||||||
|
} uffs_FileEmu;
|
||||||
|
|
||||||
|
void uffs_fileem_setup_device(uffs_Device *dev);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -0,0 +1,320 @@
|
||||||
|
/*
|
||||||
|
This file is part of UFFS, the Ultra-low-cost Flash File System.
|
||||||
|
uffs filesystem examples.
|
||||||
|
*/
|
||||||
|
#include <rtthread.h>
|
||||||
|
|
||||||
|
//#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "uffs/uffs_config.h"
|
||||||
|
#include "uffs/uffs_public.h"
|
||||||
|
#include "uffs/uffs_fs.h"
|
||||||
|
#include "uffs/uffs_utils.h"
|
||||||
|
#include "uffs/uffs_core.h"
|
||||||
|
#include "uffs/uffs_mtb.h"
|
||||||
|
#include "uffs/uffs_find.h"
|
||||||
|
#include "uffs/uffs_fd.h"
|
||||||
|
#include "emu/cmdline.h"
|
||||||
|
#include "uffs_ext.h"
|
||||||
|
|
||||||
|
#include <dfs_posix.h>
|
||||||
|
#include <filerw.h>
|
||||||
|
|
||||||
|
#ifdef RT_USING_FINSH
|
||||||
|
#include <finsh.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define PFX "exp: "
|
||||||
|
|
||||||
|
#define MAX_PATH_LENGTH 128
|
||||||
|
|
||||||
|
#if (0)
|
||||||
|
//uffs拷贝函数,参数之间加空格
|
||||||
|
//需要从elm拷贝到uffs时(跨文件系统),参数名称前加::
|
||||||
|
//例如uffs_copy("::/01.hdc /dir1/01.hdc")
|
||||||
|
//上例从SD卡拷贝一个文件01.hdc到flash中,
|
||||||
|
//也可用dfs的函数,那样就不用考虑是否跨文件系统了.
|
||||||
|
int uffs_copy(const char *tail)
|
||||||
|
{
|
||||||
|
const char *src;
|
||||||
|
const char *des;
|
||||||
|
char buf[100];
|
||||||
|
int fd1=-1, fd2=-1;
|
||||||
|
int len;
|
||||||
|
int src_local = FALSE, des_local = FALSE;
|
||||||
|
int fd3=-1, fd4=-1;
|
||||||
|
|
||||||
|
if(!tail)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
src = cli_getparam(tail, &des);
|
||||||
|
|
||||||
|
if(!des)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if(memcmp(src, "::", 2) == 0)
|
||||||
|
{
|
||||||
|
src += 2;
|
||||||
|
src_local = TRUE;
|
||||||
|
}
|
||||||
|
if(memcmp(des, "::", 2) == 0)
|
||||||
|
{
|
||||||
|
des += 2;
|
||||||
|
des_local = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(src_local)
|
||||||
|
{
|
||||||
|
//if((fp1 = fopen(src, "rb")) == NULL)
|
||||||
|
if((fd3 = open(src,O_RDONLY,0)) < 0)
|
||||||
|
{
|
||||||
|
uffs_Perror(UFFS_ERR_NORMAL, "Can't open %s for copy.", src);
|
||||||
|
goto fail_ext;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if((fd1 = uffs_open(src, UO_RDONLY)) < 0)
|
||||||
|
{
|
||||||
|
uffs_Perror(UFFS_ERR_NORMAL, "Can't open %s for copy.", src);
|
||||||
|
goto fail_ext;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(des_local)
|
||||||
|
{
|
||||||
|
if((fd4 = open(des,O_WRONLY | O_CREAT,0)) < 0)
|
||||||
|
{
|
||||||
|
uffs_Perror(UFFS_ERR_NORMAL, "Can't open %s for copy.", des);
|
||||||
|
goto fail_ext;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if((fd2 = uffs_open(des, UO_RDWR|UO_CREATE|UO_TRUNC)) < 0)
|
||||||
|
{
|
||||||
|
uffs_Perror(UFFS_ERR_NORMAL, "Can't open %s for copy.", des);
|
||||||
|
goto fail_ext;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uffs_Perror(UFFS_ERR_NORMAL, "copy %s to %s... ",src,des);
|
||||||
|
|
||||||
|
while((src_local ? (1) : (uffs_eof(fd1) == 0)))
|
||||||
|
{
|
||||||
|
if(src_local)
|
||||||
|
{
|
||||||
|
len = read(fd3, buf, sizeof(buf));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
len = uffs_read(fd1, buf, sizeof(buf));
|
||||||
|
}
|
||||||
|
if(len == 0)
|
||||||
|
break;
|
||||||
|
if(len < 0)
|
||||||
|
{
|
||||||
|
uffs_Perror(UFFS_ERR_NORMAL, "read file %s fail!", src);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(des_local)
|
||||||
|
{
|
||||||
|
if(write(fd4, buf, len) != len)
|
||||||
|
{
|
||||||
|
uffs_Perror(UFFS_ERR_NORMAL, "write file %s fail!", des);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(uffs_write(fd2, buf, len) != len)
|
||||||
|
{
|
||||||
|
uffs_Perror(UFFS_ERR_NORMAL, "write file %s fail ?", des);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
uffs_Perror(UFFS_ERR_NORMAL, "succ.");
|
||||||
|
fail_ext:
|
||||||
|
if(fd1 > 0)
|
||||||
|
uffs_close(fd1);
|
||||||
|
if(fd2 > 0)
|
||||||
|
uffs_close(fd2);
|
||||||
|
if(fd3 > 0)
|
||||||
|
close(fd3);
|
||||||
|
if(fd4 > 0)
|
||||||
|
close(fd4);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
FINSH_FUNCTION_EXPORT(uffs_copy, copy files. local file start with ::)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//计算路径下的文件(夹)个数
|
||||||
|
int CountFileUnder(const char *dir)
|
||||||
|
{
|
||||||
|
int count = 0;
|
||||||
|
uffs_DIR *dirp;
|
||||||
|
|
||||||
|
dirp = uffs_opendir(dir);
|
||||||
|
if(dirp)
|
||||||
|
{
|
||||||
|
while(uffs_readdir(dirp) != NULL)
|
||||||
|
count++;
|
||||||
|
if(dirp != NULL)
|
||||||
|
uffs_closedir(dirp);
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能: 列出文件清单
|
||||||
|
* 输入参数: name:分区名称
|
||||||
|
* 返回参数: 成功:TRUE,失败:rt_false
|
||||||
|
*/
|
||||||
|
int uffs_ls(const char *name)
|
||||||
|
{
|
||||||
|
uffs_DIR *dirp;
|
||||||
|
struct uffs_dirent *ent;
|
||||||
|
struct uffs_stat stat_buf;
|
||||||
|
int count = 0;
|
||||||
|
char buf[MAX_FILENAME_LENGTH+2];
|
||||||
|
char *sub;
|
||||||
|
|
||||||
|
if(name == NULL)
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
dirp = uffs_opendir(name); //会获得一个uffs_DIR实例
|
||||||
|
|
||||||
|
if(dirp == NULL)
|
||||||
|
{
|
||||||
|
rt_kprintf("Can't open '%s' for list\n", name);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rt_kprintf("%-16s%-8s%-8s%-8s\n","name","type","size","serial");
|
||||||
|
rt_kprintf("-----------------------------------------\n");
|
||||||
|
ent = uffs_readdir(dirp);
|
||||||
|
while(ent)
|
||||||
|
{
|
||||||
|
rt_kprintf("%-16s", ent->d_name);
|
||||||
|
strcpy(buf, name);
|
||||||
|
sub = buf;
|
||||||
|
if(name[strlen(name)-1] != '/')
|
||||||
|
sub = strcat(buf, "/");
|
||||||
|
sub = strcat(sub, ent->d_name);
|
||||||
|
|
||||||
|
if(ent->d_type & FILE_ATTR_DIR)
|
||||||
|
{
|
||||||
|
sub = strcat(sub, "/");
|
||||||
|
rt_kprintf("%-8s", "<DIR>");
|
||||||
|
rt_kprintf("%-8d", CountFileUnder(sub));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
uffs_stat(sub, &stat_buf);
|
||||||
|
rt_kprintf("%-8s", "");
|
||||||
|
rt_kprintf("%-8d", stat_buf.st_size);
|
||||||
|
}
|
||||||
|
rt_kprintf("%-8d\n", ent->d_ino);
|
||||||
|
count++;
|
||||||
|
ent = uffs_readdir(dirp);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(dirp != NULL)
|
||||||
|
uffs_closedir(dirp);
|
||||||
|
|
||||||
|
rt_kprintf("Total: %d objects.\n", count);
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能: 格式化分区
|
||||||
|
* 输入参数: 分区名称
|
||||||
|
* 返回参数:
|
||||||
|
*/
|
||||||
|
int uffs_format(const char *name)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
const char *mount = "/";
|
||||||
|
uffs_Device *dev;
|
||||||
|
|
||||||
|
if(name)
|
||||||
|
{
|
||||||
|
mount = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev = uffs_GetDeviceFromMountPoint(mount);
|
||||||
|
if(dev == NULL)
|
||||||
|
{
|
||||||
|
uffs_Perror(UFFS_ERR_NORMAL, "Can't get device from mount point.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(dev->ref_count == 1)
|
||||||
|
{
|
||||||
|
ret = uffs_FormatDevice(dev);
|
||||||
|
uffs_Perror(UFFS_ERR_NORMAL, "Format %s.",ret==RT_EOK?"succ":"fail");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
uffs_Perror(UFFS_ERR_NORMAL, "dev->ref_count: %d, can't format this device.", dev->ref_count);
|
||||||
|
}
|
||||||
|
uffs_PutDevice(dev);
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能: 创建一个文件
|
||||||
|
* 输入参数: 文件名称
|
||||||
|
* 返回参数:
|
||||||
|
*/
|
||||||
|
int uffs_mkfile(const char *name)
|
||||||
|
{
|
||||||
|
uffs_Object *fp;
|
||||||
|
int ret = 0;
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
fp = uffs_GetObject();
|
||||||
|
|
||||||
|
if(fp != NULL)
|
||||||
|
{
|
||||||
|
if(uffs_CreateObject(fp, name, UO_CREATE) != U_SUCC)
|
||||||
|
{
|
||||||
|
err = fp->err;
|
||||||
|
ret = -1;
|
||||||
|
uffs_Perror(UFFS_ERR_NORMAL, "Create %s fail, err: %d", name, uffs_get_error());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
uffs_Perror(UFFS_ERR_NORMAL, "Create %s succ.", name);
|
||||||
|
uffs_CloseObject(fp);
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
uffs_PutObject(fp);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
err = UEMFILE;
|
||||||
|
ret = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uffs_set_error(-err);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#if (0)
|
||||||
|
#ifdef RT_USING_FINSH
|
||||||
|
FINSH_FUNCTION_EXPORT(uffs_ls, list uffs system files.)
|
||||||
|
FINSH_FUNCTION_EXPORT(uffs_mkfile, make uffs system file.)
|
||||||
|
FINSH_FUNCTION_EXPORT(uffs_mkdir, make uffs system dir.)
|
||||||
|
FINSH_FUNCTION_EXPORT(uffs_rmdir, remove uffs system dir.)
|
||||||
|
FINSH_FUNCTION_EXPORT(uffs_format, format uffs partition.)
|
||||||
|
#endif
|
||||||
|
#endif
|
|
@ -0,0 +1,11 @@
|
||||||
|
|
||||||
|
#ifndef __UFFS_EXT_H__
|
||||||
|
#define __UFFS_EXT_H__
|
||||||
|
|
||||||
|
int CountFileUnder(const char *dir);
|
||||||
|
int uffs_ls(const char *name);
|
||||||
|
int uffs_format(const char *name);
|
||||||
|
int uffs_mkfile(const char *name);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
About uffs_demo_win32.exe?
|
||||||
|
Compile OS: Windows XP SP3
|
||||||
|
Compile Tools:Visul C++ 6.0
|
||||||
|
|
||||||
|
This is a uffs filesystem demo applet.
|
||||||
|
you can make it ,in accordance with you resource.
|
||||||
|
Use document :
|
||||||
|
\uffs\src\emu\*.*
|
File diff suppressed because one or more lines are too long
Binary file not shown.
Loading…
Reference in New Issue