1.介绍
本章将告诉你在Tornado下怎样去配置TrueFFS并包含它。为了在一个vxworks映象中包含TrueFFS,你必须编辑config.h文件并定义INCLUDE_TFFS。这使得vxworks的初始化代码调用tffsDrv()来创建管理TrueFFS所需的结构和全局变量,并为所有挂接了的flash设备注册socket组件驱动。在链接的时候,通过解析与tffsDrv()相关联的符号(symbols)可以将TrueFFS所必需的软件模块链接到vxworks映象中。
为了支持TrueFFS,每一个bsp必须包含一个sysTffs.c文件。它将TrueFFS所有的层(翻译层,socket层和MTD层)链接到一起并和vxworks绑定。因此,你必须编辑这个文件并决定哪一种MTD和翻译层模块应该包含到TrueFFS中。另外,如果你的目标系统包含了一个MMU 单元,你还得编辑sysLib.c中的sysPhysMemDesc[ ]数组。
在重新编译vxworks映象并重启目标系统后,你应该可以使用诸如格式化flash、创建TrueFFS块设备、绑定此块设备到dosFs所必要的功能。
2.配置和使用TrueFFS总述
配置vxworks使其包含TrueFFS需要编辑如下:
对于一个支持TrueFFS的BSP来说,以上所有文件必须放在target/config下你的BSP配置目录里。但是, sysTffs.c不会自动捆绑到这个目录。相反,几个sysTffs.c不同的版本被捆绑到src/drv/tffs/sockets下,如 ads860-sysTffs.c, mv177-sysTffs.c, hkbaha47-sysTffs.c等等。阅读一下src/drv/tffs/sockets/README这个文件,你就可以确定哪一个 bspname-sysTffs.c适合你的BSP。这个README也描述了要支持TrueFFS所有BSP需要修改的特定地方。
当你启动该映象后,它会自动运行tffsDrv( )。这个函数自动为每一个flash设备注册一个socket组件。这时flash设备还没有挂上块设备驱动。但socket组件驱动已经为调用 tffsDevFormat( )函数提供了充足的条件。为了使用TrueFFS,必须用这个函数来格式化flash媒体。为了在socket组件的顶部创建一个TrueFFS块设备并 mount dos文件系统到这个块设备上,你还得调用usrTffsConfig()函数。下面就具体讲讲这几个文件的修改。
修改Makefile:
为了加入sysTffs.o的编译,你应该在其中加入如下的宏定义:
MACH_EXTRA = sysTffs.o
修改config.h:
对于大多数的BSP来说,包含TrueFFS也就是要在config.h中加入如下的两个宏定义:
#ifndef INCLUDE_TFFS
#define INCLUDE_TFFS
#endif
#ifndef INCLUDE_DOSFS
#define INCLUDE_DOSFS
#endif
当然如果你想使用tffsShow()和tffsShowAll()来查看socket信息,你还要加上#define INCLUDE_SHOW_ROUTINES这样一条宏定义。
修改sysLib.c:
如果你的目标系统包含了MMU模块,那么它的BSP在sysLib.c文件里面就定义了一个sysPhysMemDesc[ ]表。典型地,这个表告诉MMU包含启动映象(boot image)的存储区域是WRITABLE_NOT(不可写)的,或者说是ROM型的。ROM曾经是唯一能可靠存储启动映象的技术,所以vxworks一直默认包含一个启动映象的存储区域为ROM型的。然而随着flash技术的到来,这种可能性已经得到了扩展。因为它即可写也可以可靠地存储启动映象。所以你必须编辑sysPhysMemDesc[ ],重新设置启动映象所在的存储区域为WRITABLE(可写)型的。
修改sysTffs.c:
这个文件的主要功能就是定义一些BSP特殊的socket代码,作为连接flash硬件和vxworks的桥梁。缺省地,一个WRS支持的 sysTffs.c包含了所有的翻译层模块,所有的MTD层模块,以及tffsBootImagePut( ), tffsShow( ), tffsShowAll( )这样的工具函数。为了减小映象的大小,你可以通过编辑sysTffs.c来去掉一些你知道对你的应用不必要的模块。
首先,我们应该选择翻译层模块。根据不同的flash技术有三种翻译层供你选择,如下表所示:
----------------------
宏定义符号| 相应的flash技术
----------------------
INCLUDE_TL_NFTL |NAND-based flash
INCLUDE_TL_FTL |NOR-based flash
INCLUDE_TL_SSFDC |SSFDC flash
----------------------
我们一般的flash芯片如sst39vf040,sst39vf160,am29lv160等都是NOR-based flash型,所以可以只“#define INCLUDE_TL_FTL”。
接下来,我们选择MTD层模块。Vxworks自带了支持一些flash型号的MTD层驱动模块。如下表所示:
____________________________________
宏定义符号相应的flash设备
INCLUDE_MTD_I28F016Intel 28f016
INCLUDE_MTD_I28F008Intel 28f008
INCLUDE_MTD_I28F008_BAJAIntel 28f008 on the Heurikon Baja 4000
INCLUDE_MTD_AMDAMD, Fujitsu: 29F0{40,80,16} 8-bit devices
INCLUDE_MTD_CDSNToshiba, Samsung: NAND CDSN devices
INCLUDE_MTD_DOC2Toshiba, Samsung: NAND DiskOnChip2000
INCLUDE_MTD_CFISCSCFI/SCS device
INCLUDE_MTD_WAMDAMD, Fujitsu 29F0{40,80,16} 16-bit devices
______________________________________
同翻译层一样你也可以去掉一些不必要的模块宏定义。比如,如果你使用的是8bit数据宽度的AMD29F040,那么你完全可以undefine掉除了INCLUDE_MTD_AMD以外的其它宏定义。如果你所用的flash型号很不幸不在这里面,那么你只有自己写MTD层驱动模块了。这时你可以在 src/drv/tffs下找一个比较类似的驱动来修改然后把它放到你的BSP目录下,并修改Makefile加入其.o文件到MACH_EXTRA的. o文件列表中。当然你也可以把它放到src/drv/tffs下,同样地修改该目录下的Makefile文件。
另外,缺省情况下sysTffs.c定义了INCLUDE_TFFS_BOOT_IMAGE。这将在sysTffs.o中自动包含了 tffsBootImagePut( )函数。通过使用tffsBootImagePut( )函数,你可以绕过TrueFFS(和它的翻译层)而直接向flash存储空间写数据。用tffsDevFormat( )函数可以将TrueFFS管理区域的起始地址定位到一个偏移地址上,从而在TrueFFS之外留下一个自由区域,可以用来放置启动映象(boot image)。tffsBootImagePut( )可以将启动映象写入这个自由区域。
再下来是选择socket层特征。尽管在sysTffs.c文件中设置一些宏定义可以加入翻译层模块和MTD层模块以及其它的一些相关工具模块,但文件的大部分内容还是专注于socket组件驱动程序的定义。这些驱动程序是一个标准的API,作为连接设备硬件和vxworks的桥梁。它们在很大程度上由你使用的flash硬件来决定相应的功能定义。Tornado的TrueFFS支持三种通常种类的flash硬件:
后面两者都是不可移动的媒介,它们的socket接口相对来说要简单一些。所以对于大多数标准API函数来说,可以简单地“作一点或根本不作”。然而 PC flash卡则是可移动的设备,所以接口处理就要复杂得多。大多数BSP提供的接口程序都很精干,但过于简单。它们都假定socket包含了一张 flash卡,但不支持热拔插。目前,这种简单的PCIC驱动有big-endian 和little-endian两种版本。big-endian版本是由ads860的BSP带有的,little-endian版本则是由PC386和 PC486的BSP提供的。相对于PCIC驱动有两种宏定义:INCLUDE_SOCKET_PCIC0和 INCLUDE_SOCKET_PCIC1。前者是为slot 0创建一个socket接口,后者是为slot 1创建一个socket接口。
对于PC386和PC486来说,你可选择一个比简单的PCIC更加完善的socket接口。对此,后面我们有时间的话还会细讲。
3.格式化Flash
为了使用TrueFFS,要先调用tffsDevFormat( )函数来格式式flash。在格式化的过程中,该函数先擦除flash然后将TrueFFS数据管理结构(data-management structures)写到位于每个擦除单元(如扇区)起始处的头里。tffsDevFormat( )需要两个输入参数:设备号(socket组件号)和一个指向格式化参数(FormatParams)的指针。设备号由socket组件在系统中注册的先后顺序决定。而FormatParams结构则是传递如何格式化此flash的值。这个结构定义如下:
typedef struct {
/* FTL formatting section */
long int bootImageLen;/*bootImage需要从flash媒体开始处预留的长度*/
unsigned percentUse;/*flash媒体被格式化的百分率,为了提高TrueFFS的性能,不要设为100%,以便任何时候都有空余 空间。默认值为99%*/
unsigned noOfSpareUnits;/*空余擦除单元数目,目的在于flash出现坏块时可以用它来替代,默认为1*/
unsigned long vmAddressingLimit;/* FTL 在RAM中映射的大小,默认为61Kbytes*/
FLStatus (*progressCallback)(int totalUnitsToFormat, int totalUnitsFormattedSoFar);/* 回调函数,用来监测flash擦除过程,如果返回值为OK,则继续,否则停止擦除。*/
/* DOS formatting section */
charvolumeId[4];/*Dos卷标号*/
char FAR1 * volumeLabel;/*Dos卷标字符串,如果为NULL,则没有卷标*/
unsignednoOfFATcopies;/* 文件分配表(FAT)的拷贝数,正常情况下只使用一个FAT,而另一个只有在使用的FAT被破坏的情况下用来恢复分配表,默认为2*/
unsigned embeddedCISlength; /* CIS 嵌在单元头部(unit header)之后的字节长度*/
char FAR1 * embeddedCIS;/* 单元头部被结构化用来作为一个PCMCIA ''tuple'' 链(a CIS)的起始,它包含了一个数据组织tuple,通常用16进制的0xFF来标示上一个单元头部结束的位置(''end-of-tuple- chain'')。*/
} FormatParams;
/* 默认的FormatParams 结构*/
#define STD_FORMAT_PARAMS {0, 99, 1, 0x10000l, NULL, {0,0,0,0}, NULL, 2, 0, NULL}。
一般来说,你可以把这个指针赋为NULL(0),这样就告诉tffsDevFormat( )使用在dosformt.h中定义的默认FormatParams结构。在这个默认的结构中定义的值对于绝大多数的应用已经足够了。然而,如果你想在该 flash媒体上共用TrueFFS和引导映象(boot image),这个缺省的结构就不太合适了。你必须修改bootImageLen的值,以适应boot Image的大小,tffsDevFormat( )在格式化的时候会保留这一部分空间。
另外,我们还应该了解另一个格式化函数sysTffsFormat( )。它在内部其实还是调用了tffsDevFormat( )函数。它没有入口参数,它使用自己的FormatParams参数来格式化flash。要了解更详细的信息你可以参阅bsp中sysTffs.c文件中的sysTffsFormat( )函数。
4.设置簇的大小
在为TrueFFS格式化一个flash设备时,扇区都被分配到簇里面,一个簇包含的扇区数由flMinClusterSize来决定,这是一个在 dosFormat.c中定义的int型全局变量。合法的值是2的非负数n次幂(1,2,4,8,……)。默认值为4。你可以减小这个值以获得更好的存贮密度,但由于FAT的入口数是有限的,flash媒体上的可寻址空间也会相应减小。例如,如果把flMinClusterSize设为1,则最大的可寻址空间为16M字节。
5.与bootImage区域相关的问题
正如上面所说的,当tffsDevFormat( )在一个偏移地址上格式化flash时,它不会碰偏移地址以下的的空间。正常情况下,这是好事。然而,如果先前使用了TrueFFS格式化了偏移地址以下的空间,那么这些空间的擦除单元就会包含tffsDevFormat( )写入的TrueFFS格式化头部信息。当TrueFFS挂接(mount)一个flash设备时,它会扫描整个flash的TrueFFS格式化头部信息,用来建立一个它控制的flash memory的区域表。如果在偏移地址之下的区域碰巧包含这样的头,那么TrueFFS挂接(mount)程序还可以看到这些头,结果当然会失败。
怎样解决这个问题呢?你可以调用tffsRawio( )来对这些有问题的单元作一个物理擦除。这非常有效,但也非常危险。如果误用,有可能永久地损坏flash。在你的嵌入式系统开发初期,你可以小心地使用它。如果已经是产品了,你就必须保证你的应用程序和它的用户不被伤及。下图就是调用tffsRawio( )的情况。
6.创建TrueFFS块设备
在你能创建一个逻辑TrueFFS块设备之前,你需要运行tffsDrv( )。如果你正确配置了VxWorks,它会在启动的时候自动加载。tffsDrv( )为Tornado初始化TrueFFS,包括建立互斥信号量、全局变量和用来管理TrueFFS的数据结构。也包括为目标机上所有的flash设备注册 socket组件的驱动程序。
注册socket组件的驱动程序从获取FLSocket中预先分配的5元素(5-element)TrueFFS内部数组开始。下一步是更新FLSocket结构以包含那些控制flash设备基本硬件接口的数据和函数指针。
当TrueFFS需要和具体的socket硬件打交道时,它使用设备号(0-4)作为索引来查找它的FLSocket结构,然后用相应结构中的函数来控制它的硬件接口需求。虽然这些socket接口函数并没有提供完整的块设备接口,但它们的确提供了一个足够好的使用tffsDevFormat( )的接口。这对于一个从未格式化的flash媒体来说是非常重要的,因为这种能力使得在此阶段创建一个TrueFFS块设备成为可能。
注册完一个socket组件驱动后,就可以调用tffsDevCreate( )在此之上创建一个TrueFFS块设备。作为一个输入参数,你必须为它指定一个设备号(0-4)。也就进入FLSocket结构数组的索引。作为设备号,它稍后对于dosFs是可见的。
在创建了TrueFFS块设备后,你必须调用dosFsDevInit( )函数将dos文件系统挂接(mount)到它上面。之后你就可以象从一个标准disk设备上读写flash了。为了方便,函数 usrTffsConfig( )将tffsDevCreate( )和dosFsDevInit( )合成了,并包含了一些创建TrueFFS块设备和挂接dosFS必要的函数,你只需调用它就可以了。以下是bootCofig.c中关于TrueFFS 块设备的代码,当采用tffs方式引导vxworks内核时,该函数被调用。
LOCAL STATUS tffsLoad
(
intdrive,/* TFFS drive number (0 - (noOfDrives-1)) */
intremovable,/* 0 - nonremovable flash media */
char* fileName,/* file name to download */
FUNCPTR * pEntry
)
{
int fd;
if (tffsDrv () != OK)
{
printErr ("Could not initialize.\n");
return (ERROR);
}
printf ("Attaching to TFFS... ");
dosFsInit (NUM_DOSFS_FILES);/* initialize DOS-FS */
if (usrTffsConfig (drive, removable, fileName) == ERROR)
{
printErr ("usrTffsConfig failed.\n");
return (ERROR);
}
printErr ("done.\n");
/* load the boot file */
printErr ("Loading %s...", fileName);
if ((fd = open (fileName, O_RDONLY, 0)) == ERROR)
{
printErr ("\nCannot open \"%s\".\n", fileName);
return (ERROR);
}
if (bootLoadModule (fd, pEntry) != OK)
goto tffsLoadErr;
close (fd);
return (OK);
}