昆明理工大学嵌入式实验室(
szarm@21cn.com
szarm@tom.com)
(转载请注明出处)
====================================================================
一、简介:
Yaffs(Yet Another Flash File System)文件系统是专门针对NAND 闪存设计的嵌入式文件系统,目前有YAFFS 和YAFFS2 两个版本,两个版本的主要区别之一在于YAFFS2 能够更好的支持大容量的NAND FLASH芯片。
Yaffs 文件系统有些类似于JFFS/JFFS2 文件系统,与之不同的是JFFS1/2 文件系统最初是针对NOR FLASH的应用场合设计的,而NOR FLASH 和NAND FLASH本质上有较大的区别,所以尽管JFFS1/2 文件系统也能应用于NAND FLASH,但由于它在内存占用和启动时间方面针对NOR 的特性做了一些取舍,所以对NAND来说通常并不是最优的方案。
二, yaffs/yaffs2在2.6.9中的移植
(1),下载YAFFS文件系统代码,下载网址:
http://www.aleph1.co.uk/cgi-bin/viewcvs.cgi/
点击页面左下角的Download tarball即可下载全部相关代码。
(2),将下载的文件yaffs.tar.gz拷贝到你的一个临时文件夹中,假设是/tmp,并解压:
# cd /tmp
# tar xzvf yaffs.tar.gz
解压后得到Development文件夹,该文件夹里有两个子文件:yaffs和yaffs2。考虑到现在的NAND FLASH容量越做越大,而且yaffs2可以自动选择挂载是yaffs1还是yaffs2文件系统,这里我们移植yaffs2。当然如果你的NAND FLASH只是512+16B的,可以只移植yaffs,因为即使你移植了yaffs2,它也会自动选择挂载yaffs1的。
(3)在要移植的内核目录下建立yaffs2文件夹,并将需要的文件拷贝过来:
# cd $(linuxdir)/fs
# mkdir yaffs2
(1),下载YAFFS文件系统代码,下载网址:
http://www.aleph1.co.uk/cgi-bin/viewcvs.cgi/
点击页面左下角的Download tarball即可下载全部相关代码。
(2),将下载的文件yaffs.tar.gz拷贝到你的一个临时文件夹中,假设是/tmp,并解压:
# cd /tmp
# tar xzvf yaffs.tar.gz
解压后得到Development文件夹,该文件夹里有两个子文件:yaffs和yaffs2。考虑到现在的NAND FLASH容量越做越大,而且yaffs2可以自动选择挂载是yaffs1还是yaffs2文件系统,这里我们移植yaffs2。当然如果你的NAND FLASH只是512+16B的,可以只移植yaffs,因为即使你移植了yaffs2,它也会自动选择挂载yaffs1的。
(3)在要移植的内核目录下建立yaffs2文件夹,并将需要的文件拷贝过来:
# cd $(linuxdir)/fs
# mkdir yaffs2
# cd yafs2
# cp /tmp/Development/yaffs2/*.c .
# cp /tmp/Development/yaffs/*.h .
# cp /tmp/Development/yaffs2/Makefile-kernel Makefile
# cp /tmp/Development/yaffs2/Kconfig .
(4),修改$(linuxdir)/fs/yaffs2中的文件:
在开始未修改该目录.c文件前,会出现很多的built-in.o的错误,要么是提示某些函数未定义,要么是某些函数重复定义,花了我很长的时间,终于找到问题所在。部分错误提示如下:(这是我开始编译yaffs时的错误,yaffs2的错误更多,但类型差不多)
fs/built-in.o(.text+0x8478c): In function `yaffs_internal_read_super_mtd':
: undefined reference to `nandmtd_WriteChunkToNAND'
fs/built-in.o(.text+0x84790): In function `yaffs_internal_read_super_mtd':
: undefined reference to `nandmtd_ReadChunkFromNAND'
fs/built-in.o(.text+0x84794): In function `yaffs_internal_read_super_mtd':
: undefined reference to `nandmtd_EraseBlockInNAND'
fs/built-in.o(.text+0x84798): In function `yaffs_internal_read_super_mtd':
: undefined reference to `nandmtd_InitialiseNAND'
查看.c文件,我发觉有很多是通过Kconfig文件里定义的CONFIG常量来判断执行的,而这些常量在你make menuconfig/xconfig后退出后会将你的选择保存到<linux/config.h>文件中,但yaffs2目录下的有些.c文件中引用了这些常量来判断,却并未加入引用config.h文件,从而会出错。将yaffs2目录下所有引用了这些常量的.c文件中加入对config.h的引用即可: #include <linux/config.h>
(5),修改$(linuxdir)/fs/Makefile 和 Kconfig文件。
# cd $(linuxdir)/fs
# vi Makefile (将下面一行添加到Makefile中)
obj-$(CONFIG_YAFFS_FS) += yaffs2/
# vi Kconfig (将下面一行添加到Kconfig中)
source "fs/yaffs2/Kconfig
(6),在编译内核时选择:
<*> YAFFS2 file system support
<*> 512 byte / page devices
<*> Lets Yaffs do its own ECC
<*> 2048 byte (or larger) / page devices
<*> Autoselect yaffs2 format
<*> Disable lazy loading
<*> Turn off wide tnodes
<*> Turn off debug chunk erase check
通过mkyaffsimage 制做出来的image 其OOB 中也包含它自己计算的ECC校验数据,其校验算法有可能和MTD NAND 驱动的校验算法不同,如果在内核中由MTD来处理ECC,会造成MTD 认为所有的page 都校验错误。所以,这也是我前面说最好把Lets Yaffs do its own ECC 选上的原因,同时,要把MTD NAND 驱动中的ECC 校验关闭。在$(linuxdir)/drivers/mtd/nand/your_nand.c的初始化函数中设置this-eccmode = NAND_ECC_NONE.这样做实际是使用了yaffs的ECC,但在读写yaffs分区时分出现提示说:
Reading data from NAND FLASH without ECC is not recommended
Writing data without ECC to NAND-FLASH is not recommended
个人认为,其实已经使用了yaffs的ECC,只是在传给MTD时,MTD没认可,参考mtd/nand/nand_base.c文件里的nand_read_ecc函数以及yaffs2/yaffs_fs.c文件中在于ECC的定义。这里应该是提示没用NAND的ECC,而使用的是yaffs的ECC。这可以通过mkyaffsimage制作一个image来验证。解决方法:要么使用nand的ECC,将yaffs的ECC屏蔽掉;要么将nand_base.c里的相关错误信息屏蔽掉。
(7),修改你要移植的芯片中的Makefile,在devices后面添加mtdX和mtdblockX设备,设备个数视你的nand的分区个数而定,这里我的flash在your_nand.c中分成了三个区。
/
mtd0,c,90,0 mtd1,c,90,1 mtd2,c,90,2 /
/
mtdblock0,b,31,0 mtdblock1,b,31,1 mtdblock2,b,31,2
(8),编译后将kernel和文件系统下载到板子上,查看结果并挂载。
# cat /proc/mtd (查看分区情况)
mtd0: 00400000 00004000 "EV40 flash partition 1"
mtd1: 00400000 00004000 "EV40 flash partition 2"
mtd2: 00800000 00004000 "EV40 flash partition 3"
# cat /proc/filesystems (查看内核支持的文件系统)
nodev sysfs
nodev rootfs
nodev bdev
nodev proc
nodev sockfs
nodev futexfs
nodev pipefs
nodev eventpollfs
ext2
nodev ramfs
nodev devfs
nodev nfs
nodev jffs2
romfs
yaffs
yaffs2
nodev rpc_pipefs
我们发现系统已经支持yaffs和yaffs2文件系统了。
# eraseall /dev/mtd0 (在挂载之前可以先将该分区里的内容擦除掉)
# mount –t yaffs2 /dev/mtdblock0 /mnt (将第1个分区挂载到/mnt目录下)
yaffs: dev is 32505856 name is "mtdblock0"
yaffs: Attempting MTD mount on 31.0, "mtdblock0"
yaffs: auto selecting yaffs1
我们发现如果你的flash是512+16B / page的,即使你选择的是yaffs2文件系统,内核也会自动选择挂载yaffs1文件系统。
# cp /tmp/Development/yaffs2/*.c .
# cp /tmp/Development/yaffs/*.h .
# cp /tmp/Development/yaffs2/Makefile-kernel Makefile
# cp /tmp/Development/yaffs2/Kconfig .
(4),修改$(linuxdir)/fs/yaffs2中的文件:
在开始未修改该目录.c文件前,会出现很多的built-in.o的错误,要么是提示某些函数未定义,要么是某些函数重复定义,花了我很长的时间,终于找到问题所在。部分错误提示如下:(这是我开始编译yaffs时的错误,yaffs2的错误更多,但类型差不多)
fs/built-in.o(.text+0x8478c): In function `yaffs_internal_read_super_mtd':
: undefined reference to `nandmtd_WriteChunkToNAND'
fs/built-in.o(.text+0x84790): In function `yaffs_internal_read_super_mtd':
: undefined reference to `nandmtd_ReadChunkFromNAND'
fs/built-in.o(.text+0x84794): In function `yaffs_internal_read_super_mtd':
: undefined reference to `nandmtd_EraseBlockInNAND'
fs/built-in.o(.text+0x84798): In function `yaffs_internal_read_super_mtd':
: undefined reference to `nandmtd_InitialiseNAND'
查看.c文件,我发觉有很多是通过Kconfig文件里定义的CONFIG常量来判断执行的,而这些常量在你make menuconfig/xconfig后退出后会将你的选择保存到<linux/config.h>文件中,但yaffs2目录下的有些.c文件中引用了这些常量来判断,却并未加入引用config.h文件,从而会出错。将yaffs2目录下所有引用了这些常量的.c文件中加入对config.h的引用即可: #include <linux/config.h>
(5),修改$(linuxdir)/fs/Makefile 和 Kconfig文件。
# cd $(linuxdir)/fs
# vi Makefile (将下面一行添加到Makefile中)
obj-$(CONFIG_YAFFS_FS) += yaffs2/
# vi Kconfig (将下面一行添加到Kconfig中)
source "fs/yaffs2/Kconfig
(6),在编译内核时选择:
<*> YAFFS2 file system support
<*> 512 byte / page devices
<*> Lets Yaffs do its own ECC
<*> 2048 byte (or larger) / page devices
<*> Autoselect yaffs2 format
<*> Disable lazy loading
<*> Turn off wide tnodes
<*> Turn off debug chunk erase check
通过mkyaffsimage 制做出来的image 其OOB 中也包含它自己计算的ECC校验数据,其校验算法有可能和MTD NAND 驱动的校验算法不同,如果在内核中由MTD来处理ECC,会造成MTD 认为所有的page 都校验错误。所以,这也是我前面说最好把Lets Yaffs do its own ECC 选上的原因,同时,要把MTD NAND 驱动中的ECC 校验关闭。在$(linuxdir)/drivers/mtd/nand/your_nand.c的初始化函数中设置this-eccmode = NAND_ECC_NONE.这样做实际是使用了yaffs的ECC,但在读写yaffs分区时分出现提示说:
Reading data from NAND FLASH without ECC is not recommended
Writing data without ECC to NAND-FLASH is not recommended
个人认为,其实已经使用了yaffs的ECC,只是在传给MTD时,MTD没认可,参考mtd/nand/nand_base.c文件里的nand_read_ecc函数以及yaffs2/yaffs_fs.c文件中在于ECC的定义。这里应该是提示没用NAND的ECC,而使用的是yaffs的ECC。这可以通过mkyaffsimage制作一个image来验证。解决方法:要么使用nand的ECC,将yaffs的ECC屏蔽掉;要么将nand_base.c里的相关错误信息屏蔽掉。
(7),修改你要移植的芯片中的Makefile,在devices后面添加mtdX和mtdblockX设备,设备个数视你的nand的分区个数而定,这里我的flash在your_nand.c中分成了三个区。
/
mtd0,c,90,0 mtd1,c,90,1 mtd2,c,90,2 /
/
mtdblock0,b,31,0 mtdblock1,b,31,1 mtdblock2,b,31,2
(8),编译后将kernel和文件系统下载到板子上,查看结果并挂载。
# cat /proc/mtd (查看分区情况)
mtd0: 00400000 00004000 "EV40 flash partition 1"
mtd1: 00400000 00004000 "EV40 flash partition 2"
mtd2: 00800000 00004000 "EV40 flash partition 3"
# cat /proc/filesystems (查看内核支持的文件系统)
nodev sysfs
nodev rootfs
nodev bdev
nodev proc
nodev sockfs
nodev futexfs
nodev pipefs
nodev eventpollfs
ext2
nodev ramfs
nodev devfs
nodev nfs
nodev jffs2
romfs
yaffs
yaffs2
nodev rpc_pipefs
我们发现系统已经支持yaffs和yaffs2文件系统了。
# eraseall /dev/mtd0 (在挂载之前可以先将该分区里的内容擦除掉)
# mount –t yaffs2 /dev/mtdblock0 /mnt (将第1个分区挂载到/mnt目录下)
yaffs: dev is 32505856 name is "mtdblock0"
yaffs: Attempting MTD mount on 31.0, "mtdblock0"
yaffs: auto selecting yaffs1
我们发现如果你的flash是512+16B / page的,即使你选择的是yaffs2文件系统,内核也会自动选择挂载yaffs1文件系统。