版本V2.0
翻译自YAFFS官方版文档,译者做了少许修改。
原文地址: http://www.yaffs.net/yaffs-porting-guide
(暂略部分)
对NAND Flash编程时,只能将1编程为0,因此要将0x00变为0xFF,需要先将NAND擦除,这同时会将该Block其余部分也擦除。然而,后续的程序在没有0变为1的情况下,就不需要擦除,这在一些后续数据有规律的情况下可能很有用。
刚出厂的NAND Flash就包含坏块,坏块是不可靠的,其中存储的数据可能会轻易崩溃。因此需要记录这些坏块,OOB数据区用于标记坏块。
NAND通常产生比特位错误,因此,OOB数据区也用于存储错误校验码(Error Correction Code, ECC),这样就可以甄别一些错误。
这里,最好先搞明白YAFFS期望的NAND FLASH的行为,这样你就知道要将YAFFS运行起来,需要在NAND的驱动程序中提供哪些东西。 Yaffs使用了一个相当抽象的NAND FLASH模型。它以高度灵活的方式使用YAFFS。YAFFS是为NAND FLASH设计的,它作了以下的假设或定义。
YAFFS 分别用块号和 chunk id 标示块和 chunk 。它将空块(填满0xFF)当作空闲块或者已擦除块。这样,格式化一个YAFFS分区等价于擦除所有未损坏的块。
因此YAFFS最少需要一些函数能够擦除块,读一个页,写一个页。如果能够仅读取OOB数据,而仅仅为了读取/更新OOB数据就读写整个页的方式,耗费大量总线串行读写周期。this is an overhead that can be avoided using the random access read and write features of some nand chips
编写Balloon开发板(V3.01)上的三星628 K9F2G08U0M PCB0的驱动程序
三星芯片每一个坏块的第一个或第二个页的第一个字节不是0xFF 这些坏块的标记并被记录下来,并且当又发现一个坏块时,按同样的方式添加坏块记录。Yaffs希望你能提供一个函数,在它认为必要的情况下,可以标记一个块为坏块。
YAFFS自己记录坏块,因此驱动程序不要担心mask out 坏块。
从CVS中提取代码 第一步就是从获取YAFFS的代码。 译者注:这里的说明已经过时了,因此不再翻译。
对最新的代码,你需要做一些小的改动,才能在编译时没有错误和警告。在本文档中有一个补丁文件(译者注:这是针对2009年的版本来说的)
你需要提供一个loff_t的定义(在yaffs2/direct/ydirectenv.h)中。这个类型是存储文件字节大小的。因此为了支持你希望支持的最大的文件大小,你得提供一个足够大的数据类型,unsigned long是一个不错的选择。 编辑 yaffsfs.c,将#if修改为#ifdef。然后你会得到一些相关的警告和错误,你需要修正一些printf格式化字符串使之正确(将几个 '%d' 改变为 '%ld')
change yaffs/ydirectenv.h + typedef unsigned long loff_t; change yaffs2/direct/yaffsfs.c ~line 1117 - #if CONFIG_YAFFS_WINCE + #ifdef CONFIG_YAFFS_WINCE
仅需要提取少量文件。使用yaffs的直接接口,你不需要所有的文件。你实际需要的文件列在下面,其余文件不需要编译。
direct/yaffsfs.c
yaffs_guts.c
direct/yaffscfg.c
yaffs_nand.c
yaffs_tagsvalidity.c
yaffs_checkptrw.c
yaffs_qsort.c
yaffs_tagscompat.c
yaffs_ecc.c
yaffs_packedtags2.c
yaffs有很多不同的配置选项,这里给出一些你可能会用于Yaffs直接接口的选项。
在编译YAFFS的时候,指明这些选项,比如-DCONFIG_YAFFS_DIRECT
你需要向YAFFS提供配置信息,包括,你所使用的YAFFS芯片的详细信息,以及它读/写/擦除flash时需要调用的函数。这些函数构成了yaffs和底层硬件驱动程序的桥梁。这些自定义配置在yaffscfg.h中完成。在yaffscfg.h中,你需要提供一些类似操作系统的函数,以及一个初始化yaffs的函数。比如,需要提供malloc和free函数。这里列出了需要实现的函数。
void yaffsfs_SetError(int err);
报告系统错误,例如,若存在errno,则使用它。
void *yaffs_malloc(size_t size); void yaffs_free(void *ptr);
作为的标准的malloc和free,如果您的系统之已经包含了这两个函数,那么仅仅调用就成了。
void yaffsfs_LocalInitialisation(void);
void yaffsfs_Lock(void);
void yaffsfs_Unlock(void);
使用yaffsfs_LocalInitalisation初始化实时操作系统状态,如果该系统是多线程的,那么应该初始化信号量/互斥锁,用于加锁或解锁。在 LocalInitialisation 中应该使用信号量来加锁或解锁,在多线程访问的时候锁住YAFFS。
u32 yaffsfs_CurrentTime(void);
这个函数可以用于系统中任何时间的增加。如果不需要,将该函数直接返回0即可。
int yaffs_StartUp(void);
返回值: YAFFS_OK 或者 YAFFS_FAIL
这个函数向yaffs提供很多底层nand设备的特性信息。在此函数中,你需要建立'yaffs_Device'(yaffs_guts.h), 'yaffsfs_DeviceConfiguration'(direct/yaffscfg.h),如何建立一个yaffs设备将在下一节描述。
一旦你建立了需要的yaffs_Device结构,你需要调用yaffs_initialise(yaffs_guts.h),通过一个以null指针结尾的 yaffs_DeviceConfiguration结构。一个yaffs_DeviceConfiguration结构包含跟该设备相关的,一个指向yaffs_Device结构的指针和一个“挂载点”字符串(prefix)。
一个yaffs设备是一个逻辑设备,它代表了一个物理设备的部分或整体。你可以认为它是一个nand上的一个“分区”。比如,该分区可能覆盖整个NAND,也许只是一半,而另外一半就是另一个yaffs_Device.它也可以用于你使用一个非flash设备(比如RAM)来测试的情况下。
一个yaffs_Device记录了起始和结束块。通过改变它的起始和结束块,你就可以在同一个物理设备上使用不止一个的yaffs_Device。
这里将需要你自己建立的yaffs_Device结构的数据域列出,其他数据域由yaffs自动创建。
int nDataBytesPerChunk
如其名,这是每一个chunk的字节数,还记得吧,在yaffs术语中,一个页就是一个chunk,因而它也是一页的字节数。它是数据字节数,即不包含OOB的数据。比如一页时2048字节+64字节的OOB,那么数值nDataBytesPerChunk为2048
int nChunksPerBlock
物理nand设备中每页包含的chunk(就是nand上的页)的数目,最少是2.
int spareBytesPerChunk
空闲域(spare area)大小,比如:每个chunk(页)的OOB字节数
int startBlock
该逻辑yaffs_Device设备第一个块的块号(而字节地址),注意,yaffs需要第一个块是空闲的,因此你不可以设置该变量为0,如果设置为0,yaffs会给它加1,并且会在结束块号上也加1,在你设置设备从块0开始,到最后一个块结束,这意味着yaffs试图写一个不存在的块,从而出现错误。
int endBlock
该逻辑yaffs_Device设备的最后一个块号。如果startBlock为0,那么yaffs会使用endBlock+1,至少使startBlock+nReservedBlocks+2
int nReservedBlocks
这是YAFFS必须保留,用于垃圾回收和块错误恢复的可擦除块的数目。至少是2,但是5更好。如果你使用一个不会损坏的介质,比如RAM或者RAM盘,或者主机文件系统模拟,那么可以是2。
int nShortOpCaches
配置当前设备YAFFS Cache项的数目。0值表示不使用cache。对于大多数系统,推荐使用10到20之间的一个数值。不能大于YAFFS_MAX_SHORT_OP_CACHES定义的数值。
int useNANDECC
这是一个标志,用于指示是由yaffs执行ECC计算,还是由NAND驱动程序来执行ECC计算。(译者注:此数值取0,则使用yaffs来执行ECC计算,软件ECC计算。如果想要使用硬件ECC校验时,应该设置为1,并且在NAND驱动程序中加入硬件ECC校验的代码。)
void *genericDevice
这是一个指针,它应该指向任何数据,底层NAND驱动程序需要知道以从物理设备读,写。
int isYaffs2
我们使用的是YAFFS2么? 本文档中,我们仅讨论yaffs2
int inbandTags
是否为OOB区,如果不是,那么它为真,仅用于yaffs2
u32 totalBytesPerChunk
这个名字可能有点误导人,它应该等于nDataBytesPerChunk ,而非其名字暗示的nDataBytesPerChunk + spareBytesPerChunk。如果inbandTags为真,那么yaffs设置nDataBytesPerChunk,因此有足够的空闲空间存储数据,yaffs会在空闲域中正常存储。
write/readChunkWithTagsFromNAND, markNANDBlockBad queryNANDBlock
这些都是函数指针,你需要提供这些函数来给yaffs,读写nandf。
如果使用yaffs2,你需要向yaffs提供函数指针供yaffs调用,来读写nand flash。这些函数实现的样例在yaffs_mtdif.c.这些函数为linux的mtd层提供相同作用,读写功能。下面的函数执行成功时返回YAFFS_OK,失败时返回YAFFS_FAIL
int (*writeChunkWithTagsToNAND) (struct yaffs_DeviceStruct * dev, int chunkInNAND, const u8 * data, const yaffs_ExtendedTags * tags); dev: 要写入的yaffs_Device逻辑设备. chunkInNAND: 将chunk写入的页 Data: 指向需要写入的数据的指针 tags: 未压缩(打包)的OOB数据 Return: YAFFS_OK / YAFFS_FAIL
该函数将页(chunk)写入nand中。在该函数中,你可能想先检索你放入dev→genericDevice(在yaffs_StartUp函数中)的所有有用信息并且通过它来调用你先前所写的函数,向nand中写入数据。yaffs2的数据和标签(tags)永远不应为 NULL. chunkInNand 是将要写入的页的页号,而不是需要转换的地址,如果那是你的底层函数要写入到nand期望的东西。
然后你将需要压缩标签(tags)成一个yaffs_PackedTags2结构,这会利用所有跟 yaffs_ExtendedTags 标签结构相关的信息,并且会在将他们置于packedtags2结构之前,计算 ECC 数据。该结构就是你要写入到nand中空闲区域(spare area)的OOB数据。
如何写这些数据到OOB数据取决于你使用的硬件的体系结构、nand芯片,以及坏块是如何标记的。如,第一个字节用于坏块标记,那么就可以在第一个字节可被获取之后,简单地写入标签。压缩后标签中的ECC数据仅包含对OOB数据中标签的错误校正和检测,如果你想要对数据(译者注:这是的数据是指数据区的数据)做ECC,那你必须自己做。
对数据字节执行ECC校验,你可以调用yaffs_ECCCalculate函数,它计算256字节大小的数据段的ECC数据。这个函数每256字得到3字节。所以对每页中2048个字节的数据,你需要额外的(2048/256) * 3 = 8 * 3 = 24个ECC数据。这些数据可以写在坏块数据和压缩标签数据之后。
int (*readChunkWithTagsFromNAND) (struct yaffs_DeviceStruct * dev, int chunkInNAND, u8 * data, yaffs_ExtendedTags * tags); dev: 要写入的yaffs_Device逻辑设备. chunkInNAND: 将chunk读入的页 Data: 指向需要读入的数据的缓冲区指针 tags: 指向未压缩(打包)的OOB数据的缓冲区指针 Return: YAFFS_OK / YAFFS_FAIL
该函数执行上一个函数的相反的功能,首先,读取数据和OOB字节,接着将这些输入放在一个由参数data指向的缓冲区中,然后使用yaffs_UnpackTags2把未压缩的OOB字节放在参数tags指向的缓冲区中
一旦标签被解压缩,你应该对刚读回数据重新计算ECC,就像你在写操作的时候做的那样。然后使用新计算的ECC和你读出的ECC,利用yaffs_ECCCorrect来校验数据。
根据yaffs_ECCCorrect返回值,无论有任何被校正的数据还是校正失败,你都应该设置tags→eccResult来反映这种变化。如果数据校验的结果比已经在那里的糟糕,那yaffs_UnpackTags2也得设置tags→eccResult,这样你只需要更新它。例如,假设标签被解压缩以后,eccResult是YAFFS_ECC_RESULT_FIXED,对读回的数据做ECC校验发现没有没有错误,那么你应该离开unpackedtags2和YAFFS_ECC_RESULT_FIXED,并且不要设置为YAFFS_ECC_RESULT_NO_ERROR。
int (*markNANDBlockBad) (struct yaffs_DeviceStruct * dev, int blockNo); dev: 要写入的yaffs_Device逻辑设备. blockNo: 要标记的块. Return: YAFFS_OK / YAFFS_FAIL
它应标记坏块。意味着,读OOB数据、改变相关的标记坏块的字节,以及写回OOB数据
int (*queryNANDBlock) (struct yaffs_DeviceStruct * dev, int blockNo, yaffs_BlockState * state, u32 *sequenceNumber); dev: 要写入的yaffs_Device逻辑设备. blockNo: 要标记的块. state: Upon returning this should be set to the relevant state of this particular block. Sequance number: 该块的顺序号(The Sequence number),为0表示此块未使用 Return: YAFFS_OK / YAFFS_FAIL
它应检查一个块是否是有效的。
如果在OOB中设置了坏块标记,那么*state应该被赋值为YAFFS_BLOCK_STATE_DEAD,*sequenceNumber赋值为0,然后返回YAFFS_FAIL。
如果该块没坏,那么应解压缩标签。标签解压缩后,若发现chunk已使用(查看tags.chunkUsed),则*sequenceNumber应赋值为tags.sequenceNumber,*state赋值为YAFFS_BLOCK_STATE_NEEDS_SCANNING,否则该块未使用,则*sequenceNumber赋值为0,*state赋值为YAFFS_BLOCK_STATE_EMPTY
yaffs为连接的应用程序提供了一组函数。大部分跟标准C库函数,如open/close一致,只是增加了yaffs_前缀,如 yaffs_open. 这些函数定义在direct/yaffsfs.h中。
初始化yaffs来完成读写,你必须在每个你要使用的yaffs设备上调用yaffs_mount。比如yaffs_mount(”/boot”)。这可以在系统启动的时候执行,如果存在一个操作系统,那么程序需要考虑这点。在你完成使用的时候,你也需要调用yaffs_umount函数,这样yaffs就会将它需要状态写入磁盘。
如果读写文件的应用层接口已经存在,你可以根据相关的操作系统调用来封装相关的函数调用。
比如简单的编写
#define open(path, oflag, mode) yaffs_open(path,oflag,mode)
当不止一个文件系统存在时,则使用更复杂的方法提供间接的函数调用。 例如:
int open(const char *path,...) { //判断正在使用那个文件系统 if(it_is_a_yaffs_path(path)) { return yaffs_open(...); } else { //做其他工作 } }
ballon开发板的最新源代码可以从SVN中提取。不同的ballon版本有不同的代码。获取代码,创建工具链的方法在下面的网页中有详细的描述 http://www.balloonboard.org/software.html
如果你仅想看看最新的主干代码,那么下面会提供你想要的。
svn checkout svn://balloonboard.org/balloon/trunk/
版本V2.0
翻译自YAFFS官方版文档,译者做了少许修改。
原文地址: http://www.yaffs.net/yaffs-porting-guide
(暂略部分)
对NAND Flash编程时,只能将1编程为0,因此要将0x00变为0xFF,需要先将NAND擦除,这同时会将该Block其余部分也擦除。然而,后续的程序在没有0变为1的情况下,就不需要擦除,这在一些后续数据有规律的情况下可能很有用。
刚出厂的NAND Flash就包含坏块,坏块是不可靠的,其中存储的数据可能会轻易崩溃。因此需要记录这些坏块,OOB数据区用于标记坏块。
NAND通常产生比特位错误,因此,OOB数据区也用于存储错误校验码(Error Correction Code, ECC),这样就可以甄别一些错误。
这里,最好先搞明白YAFFS期望的NAND FLASH的行为,这样你就知道要将YAFFS运行起来,需要在NAND的驱动程序中提供哪些东西。 Yaffs使用了一个相当抽象的NAND FLASH模型。它以高度灵活的方式使用YAFFS。YAFFS是为NAND FLASH设计的,它作了以下的假设或定义。
YAFFS 分别用块号和 chunk id 标示块和 chunk 。它将空块(填满0xFF)当作空闲块或者已擦除块。这样,格式化一个YAFFS分区等价于擦除所有未损坏的块。
因此YAFFS最少需要一些函数能够擦除块,读一个页,写一个页。如果能够仅读取OOB数据,而仅仅为了读取/更新OOB数据就读写整个页的方式,耗费大量总线串行读写周期。this is an overhead that can be avoided using the random access read and write features of some nand chips
编写Balloon开发板(V3.01)上的三星628 K9F2G08U0M PCB0的驱动程序
三星芯片每一个坏块的第一个或第二个页的第一个字节不是0xFF 这些坏块的标记并被记录下来,并且当又发现一个坏块时,按同样的方式添加坏块记录。Yaffs希望你能提供一个函数,在它认为必要的情况下,可以标记一个块为坏块。
YAFFS自己记录坏块,因此驱动程序不要担心mask out 坏块。
从CVS中提取代码 第一步就是从获取YAFFS的代码。 译者注:这里的说明已经过时了,因此不再翻译。
对最新的代码,你需要做一些小的改动,才能在编译时没有错误和警告。在本文档中有一个补丁文件(译者注:这是针对2009年的版本来说的)
你需要提供一个loff_t的定义(在yaffs2/direct/ydirectenv.h)中。这个类型是存储文件字节大小的。因此为了支持你希望支持的最大的文件大小,你得提供一个足够大的数据类型,unsigned long是一个不错的选择。 编辑 yaffsfs.c,将#if修改为#ifdef。然后你会得到一些相关的警告和错误,你需要修正一些printf格式化字符串使之正确(将几个 '%d' 改变为 '%ld')
change yaffs/ydirectenv.h + typedef unsigned long loff_t; change yaffs2/direct/yaffsfs.c ~line 1117 - #if CONFIG_YAFFS_WINCE + #ifdef CONFIG_YAFFS_WINCE
仅需要提取少量文件。使用yaffs的直接接口,你不需要所有的文件。你实际需要的文件列在下面,其余文件不需要编译。
direct/yaffsfs.c
yaffs_guts.c
direct/yaffscfg.c
yaffs_nand.c
yaffs_tagsvalidity.c
yaffs_checkptrw.c
yaffs_qsort.c
yaffs_tagscompat.c
yaffs_ecc.c
yaffs_packedtags2.c
yaffs有很多不同的配置选项,这里给出一些你可能会用于Yaffs直接接口的选项。
在编译YAFFS的时候,指明这些选项,比如-DCONFIG_YAFFS_DIRECT
你需要向YAFFS提供配置信息,包括,你所使用的YAFFS芯片的详细信息,以及它读/写/擦除flash时需要调用的函数。这些函数构成了yaffs和底层硬件驱动程序的桥梁。这些自定义配置在yaffscfg.h中完成。在yaffscfg.h中,你需要提供一些类似操作系统的函数,以及一个初始化yaffs的函数。比如,需要提供malloc和free函数。这里列出了需要实现的函数。
void yaffsfs_SetError(int err);
报告系统错误,例如,若存在errno,则使用它。
void *yaffs_malloc(size_t size); void yaffs_free(void *ptr);
作为的标准的malloc和free,如果您的系统之已经包含了这两个函数,那么仅仅调用就成了。
void yaffsfs_LocalInitialisation(void);
void yaffsfs_Lock(void);
void yaffsfs_Unlock(void);
使用yaffsfs_LocalInitalisation初始化实时操作系统状态,如果该系统是多线程的,那么应该初始化信号量/互斥锁,用于加锁或解锁。在 LocalInitialisation 中应该使用信号量来加锁或解锁,在多线程访问的时候锁住YAFFS。
u32 yaffsfs_CurrentTime(void);
这个函数可以用于系统中任何时间的增加。如果不需要,将该函数直接返回0即可。
int yaffs_StartUp(void);
返回值: YAFFS_OK 或者 YAFFS_FAIL
这个函数向yaffs提供很多底层nand设备的特性信息。在此函数中,你需要建立'yaffs_Device'(yaffs_guts.h), 'yaffsfs_DeviceConfiguration'(direct/yaffscfg.h),如何建立一个yaffs设备将在下一节描述。
一旦你建立了需要的yaffs_Device结构,你需要调用yaffs_initialise(yaffs_guts.h),通过一个以null指针结尾的 yaffs_DeviceConfiguration结构。一个yaffs_DeviceConfiguration结构包含跟该设备相关的,一个指向yaffs_Device结构的指针和一个“挂载点”字符串(prefix)。
一个yaffs设备是一个逻辑设备,它代表了一个物理设备的部分或整体。你可以认为它是一个nand上的一个“分区”。比如,该分区可能覆盖整个NAND,也许只是一半,而另外一半就是另一个yaffs_Device.它也可以用于你使用一个非flash设备(比如RAM)来测试的情况下。
一个yaffs_Device记录了起始和结束块。通过改变它的起始和结束块,你就可以在同一个物理设备上使用不止一个的yaffs_Device。
这里将需要你自己建立的yaffs_Device结构的数据域列出,其他数据域由yaffs自动创建。
int nDataBytesPerChunk
如其名,这是每一个chunk的字节数,还记得吧,在yaffs术语中,一个页就是一个chunk,因而它也是一页的字节数。它是数据字节数,即不包含OOB的数据。比如一页时2048字节+64字节的OOB,那么数值nDataBytesPerChunk为2048
int nChunksPerBlock
物理nand设备中每页包含的chunk(就是nand上的页)的数目,最少是2.
int spareBytesPerChunk
空闲域(spare area)大小,比如:每个chunk(页)的OOB字节数
int startBlock
该逻辑yaffs_Device设备第一个块的块号(而字节地址),注意,yaffs需要第一个块是空闲的,因此你不可以设置该变量为0,如果设置为0,yaffs会给它加1,并且会在结束块号上也加1,在你设置设备从块0开始,到最后一个块结束,这意味着yaffs试图写一个不存在的块,从而出现错误。
int endBlock
该逻辑yaffs_Device设备的最后一个块号。如果startBlock为0,那么yaffs会使用endBlock+1,至少使startBlock+nReservedBlocks+2
int nReservedBlocks
这是YAFFS必须保留,用于垃圾回收和块错误恢复的可擦除块的数目。至少是2,但是5更好。如果你使用一个不会损坏的介质,比如RAM或者RAM盘,或者主机文件系统模拟,那么可以是2。
int nShortOpCaches
配置当前设备YAFFS Cache项的数目。0值表示不使用cache。对于大多数系统,推荐使用10到20之间的一个数值。不能大于YAFFS_MAX_SHORT_OP_CACHES定义的数值。
int useNANDECC
这是一个标志,用于指示是由yaffs执行ECC计算,还是由NAND驱动程序来执行ECC计算。(译者注:此数值取0,则使用yaffs来执行ECC计算,软件ECC计算。如果想要使用硬件ECC校验时,应该设置为1,并且在NAND驱动程序中加入硬件ECC校验的代码。)
void *genericDevice
这是一个指针,它应该指向任何数据,底层NAND驱动程序需要知道以从物理设备读,写。
int isYaffs2
我们使用的是YAFFS2么? 本文档中,我们仅讨论yaffs2
int inbandTags
是否为OOB区,如果不是,那么它为真,仅用于yaffs2
u32 totalBytesPerChunk
这个名字可能有点误导人,它应该等于nDataBytesPerChunk ,而非其名字暗示的nDataBytesPerChunk + spareBytesPerChunk。如果inbandTags为真,那么yaffs设置nDataBytesPerChunk,因此有足够的空闲空间存储数据,yaffs会在空闲域中正常存储。
write/readChunkWithTagsFromNAND, markNANDBlockBad queryNANDBlock
这些都是函数指针,你需要提供这些函数来给yaffs,读写nandf。
如果使用yaffs2,你需要向yaffs提供函数指针供yaffs调用,来读写nand flash。这些函数实现的样例在yaffs_mtdif.c.这些函数为linux的mtd层提供相同作用,读写功能。下面的函数执行成功时返回YAFFS_OK,失败时返回YAFFS_FAIL
int (*writeChunkWithTagsToNAND) (struct yaffs_DeviceStruct * dev, int chunkInNAND, const u8 * data, const yaffs_ExtendedTags * tags); dev: 要写入的yaffs_Device逻辑设备. chunkInNAND: 将chunk写入的页 Data: 指向需要写入的数据的指针 tags: 未压缩(打包)的OOB数据 Return: YAFFS_OK / YAFFS_FAIL
该函数将页(chunk)写入nand中。在该函数中,你可能想先检索你放入dev→genericDevice(在yaffs_StartUp函数中)的所有有用信息并且通过它来调用你先前所写的函数,向nand中写入数据。yaffs2的数据和标签(tags)永远不应为 NULL. chunkInNand 是将要写入的页的页号,而不是需要转换的地址,如果那是你的底层函数要写入到nand期望的东西。
然后你将需要压缩标签(tags)成一个yaffs_PackedTags2结构,这会利用所有跟 yaffs_ExtendedTags 标签结构相关的信息,并且会在将他们置于packedtags2结构之前,计算 ECC 数据。该结构就是你要写入到nand中空闲区域(spare area)的OOB数据。
如何写这些数据到OOB数据取决于你使用的硬件的体系结构、nand芯片,以及坏块是如何标记的。如,第一个字节用于坏块标记,那么就可以在第一个字节可被获取之后,简单地写入标签。压缩后标签中的ECC数据仅包含对OOB数据中标签的错误校正和检测,如果你想要对数据(译者注:这是的数据是指数据区的数据)做ECC,那你必须自己做。
对数据字节执行ECC校验,你可以调用yaffs_ECCCalculate函数,它计算256字节大小的数据段的ECC数据。这个函数每256字得到3字节。所以对每页中2048个字节的数据,你需要额外的(2048/256) * 3 = 8 * 3 = 24个ECC数据。这些数据可以写在坏块数据和压缩标签数据之后。
int (*readChunkWithTagsFromNAND) (struct yaffs_DeviceStruct * dev, int chunkInNAND, u8 * data, yaffs_ExtendedTags * tags); dev: 要写入的yaffs_Device逻辑设备. chunkInNAND: 将chunk读入的页 Data: 指向需要读入的数据的缓冲区指针 tags: 指向未压缩(打包)的OOB数据的缓冲区指针 Return: YAFFS_OK / YAFFS_FAIL
该函数执行上一个函数的相反的功能,首先,读取数据和OOB字节,接着将这些输入放在一个由参数data指向的缓冲区中,然后使用yaffs_UnpackTags2把未压缩的OOB字节放在参数tags指向的缓冲区中
一旦标签被解压缩,你应该对刚读回数据重新计算ECC,就像你在写操作的时候做的那样。然后使用新计算的ECC和你读出的ECC,利用yaffs_ECCCorrect来校验数据。
根据yaffs_ECCCorrect返回值,无论有任何被校正的数据还是校正失败,你都应该设置tags→eccResult来反映这种变化。如果数据校验的结果比已经在那里的糟糕,那yaffs_UnpackTags2也得设置tags→eccResult,这样你只需要更新它。例如,假设标签被解压缩以后,eccResult是YAFFS_ECC_RESULT_FIXED,对读回的数据做ECC校验发现没有没有错误,那么你应该离开unpackedtags2和YAFFS_ECC_RESULT_FIXED,并且不要设置为YAFFS_ECC_RESULT_NO_ERROR。
int (*markNANDBlockBad) (struct yaffs_DeviceStruct * dev, int blockNo); dev: 要写入的yaffs_Device逻辑设备. blockNo: 要标记的块. Return: YAFFS_OK / YAFFS_FAIL
它应标记坏块。意味着,读OOB数据、改变相关的标记坏块的字节,以及写回OOB数据
int (*queryNANDBlock) (struct yaffs_DeviceStruct * dev, int blockNo, yaffs_BlockState * state, u32 *sequenceNumber); dev: 要写入的yaffs_Device逻辑设备. blockNo: 要标记的块. state: Upon returning this should be set to the relevant state of this particular block. Sequance number: 该块的顺序号(The Sequence number),为0表示此块未使用 Return: YAFFS_OK / YAFFS_FAIL
它应检查一个块是否是有效的。
如果在OOB中设置了坏块标记,那么*state应该被赋值为YAFFS_BLOCK_STATE_DEAD,*sequenceNumber赋值为0,然后返回YAFFS_FAIL。
如果该块没坏,那么应解压缩标签。标签解压缩后,若发现chunk已使用(查看tags.chunkUsed),则*sequenceNumber应赋值为tags.sequenceNumber,*state赋值为YAFFS_BLOCK_STATE_NEEDS_SCANNING,否则该块未使用,则*sequenceNumber赋值为0,*state赋值为YAFFS_BLOCK_STATE_EMPTY
yaffs为连接的应用程序提供了一组函数。大部分跟标准C库函数,如open/close一致,只是增加了yaffs_前缀,如 yaffs_open. 这些函数定义在direct/yaffsfs.h中。
初始化yaffs来完成读写,你必须在每个你要使用的yaffs设备上调用yaffs_mount。比如yaffs_mount(”/boot”)。这可以在系统启动的时候执行,如果存在一个操作系统,那么程序需要考虑这点。在你完成使用的时候,你也需要调用yaffs_umount函数,这样yaffs就会将它需要状态写入磁盘。
如果读写文件的应用层接口已经存在,你可以根据相关的操作系统调用来封装相关的函数调用。
比如简单的编写
#define open(path, oflag, mode) yaffs_open(path,oflag,mode)
当不止一个文件系统存在时,则使用更复杂的方法提供间接的函数调用。 例如:
int open(const char *path,...) { //判断正在使用那个文件系统 if(it_is_a_yaffs_path(path)) { return yaffs_open(...); } else { //做其他工作 } }
ballon开发板的最新源代码可以从SVN中提取。不同的ballon版本有不同的代码。获取代码,创建工具链的方法在下面的网页中有详细的描述 http://www.balloonboard.org/software.html
如果你仅想看看最新的主干代码,那么下面会提供你想要的。
svn checkout svn://balloonboard.org/balloon/trunk/