8 Flash存储区块设备驱动
——可选的组件TrueFFS
8.1引论
Tornado的TrueFFS是一个可选的产品,它提供了对很多Flash存储器的块访问接口。TrueFFS是一种与Vxworks兼容的M-2.0系统的应用实现。这个系统是可重入的(reentrant),线程安全,并且支持所有的只要能运行Vxworks的cpu架构。
本章首先简要介绍flash存储器,接着一步步的概述如何建造TrueFFS系统。然后详细介绍这些步骤,包括对编写你自己的socket驱动和MTD组件有用的部分——这是本章的重点。
注意:尽管力图使文件系统尽量中立,但是这个版本的TrueFFS产品是对Vxworks的块设备驱动,所以我们只保证与本产品提供的MS-DOS兼容文件系统正常运行。
8.1.1选择TrueFFS作为介质
TrueFFS应用程序可以像操作磁盘上的MS-DOS文件系统那样简单的读写Flash存储器。但是,当然这些背后的存储介质大大不同。虽然对于高层应用而言这些不同之处是彻底透明的,但是当设计一个嵌入系统时这些知识是非常关键的。
即使没有供电,Flash存储器也可以长期存储数据。Flash的物理部件是能耗极小的固态盘。因此Flash存储器是理想的移动、手持设备的存储器。一些嵌入式设备部署在恶劣的环境中机械磁盘无法工作,Flash存储器是一个非易失的可靠方案。
但是受擦除次数限制,Flash的寿命有限,并且TrueFFS只支持那些块对称的的设备。而且一些在磁介质的块设备中的特性也没有得到支持。读写不均等,这是个Flash存储的典型特点,并且读速度要快于写速度。详情参见8.13Flash功能。TrueFFS也不支持ioctrl函数。
注意:无论以字节、字还是双字为单位,虽然可以将内存中的任意大小的块写入,但是只能以块为单位进行擦除。延长Flash寿命的最好途径就是使损耗均衡。详情参见8.13。
注意:TrueFFS尚不支持分区表。
警告:Vxworks中在处理IO请求时会检查对应的任务的优先级,因此如果当IO设备忙碌时,分别有高、低优先级的任务请求IO服务,那么IO设备空闲后高优先级任务的IO请求会首先得到服务——即使低优先级任务比高优先级任务更早提出IO请求。
8.1.2 TrueFFS的层次
TrueFFS由一个核心层和三个功能层(翻译层、存储技术驱动层和套接字层)组成。三个功能层以源码、二进制文件的形式提供。详情参见参见8.13。
图 8-1 TrueFFS是一个分层的产品
l Core Layer核心层。
这一层将其它的层连接到一起。核心层也为其他层提供了工作和处理全局事件的通道——比如后台处理(backgrounding),垃圾收集,定时器和其他系统资源。核心层以二进制文件的形式提供。
l Translation Layer翻译层
本层维护了一个从存储介质的文件系统视图到Flash擦除块的映射。块分配映射(Block Allocation Map)是实现损耗均衡和错误恢复的基础部件。翻译层是介质(NOR或SSFDC)相关的。翻译层以二进制文件形式提供。
l MTD Layer介质技术驱动层
MTD层实现了底层的对介质的编程(映射,读,写,擦除)。MTDs以二进制和源码形式提供。
l Socket Layer套接字层
套接字层提供了TrueFFS访问板上硬件之间的接口。提供了板卡相关的硬件访问例程。套接字层负责电源管理,卡检测,窗口管理和套接字注册。套接字层以源码形式提供。
8.2用TrueFFS搭建系统
本节从较高层面描述开发过程,突出了为支持TrueFFS而需要对Vxwork进行的配置、构建的步骤。
Step1:选择一个MTD组件
选择一个你的硬件相应的MTD,从TrueFFS支持的那些MTD中选一个,你也可以自己编写一个。详情参见8.3选择一个MTD组件。
Step2:标识套接字驱动
确保你有一个可用的套接字驱动。套接字驱动是一个源代码组件,是在文件sysTffs.c中实现的。对于一些BSP而言,套接字驱动位于BSP路径中。如果没有的话,你可以生成一个针对你的硬件的普通框架文件。详情参见8.4标识套接字驱动。
Step3: 配置系统
为支持TrueFFS,需要添加合适的组件。最小的支持需要dosFs和4个TrueFFS层组件。详情参见8.5配置和构建工程。
注意:
组件的详情用的是简写TFFS,而不是TrueFFS。
Step4: 构建工程
在构建工程之前,应该确认MTD的二进制文件应该更新,BSP路径中的套接字驱动文件应该是个可用的版本。详情参见8.5.7构建系统的工程。
Step5:启动目标机,格式化驱动器
下一步,启动目标机,然后从shell中格式化驱动器。详情参见8.6格式化驱动器。
注意:
为了在Flash中给boot代码保留一个区域,参见8.7为写Boot Image创建区域。
Step6:装载驱动器(Mount the Drive)
在TrueFFS的Flash驱动器上装载Vxworks的DOS文件系统。详情参见8.8装载驱动器。
Step7:测试驱动器
测试你的驱动器。
一个办法是快速检查(sanity check),把宿主机中的一个文本文件拷贝到目标机的Flash中,然后把文件在拷贝到控制台或临时文件中以作比较。下面是一个在shell中作sanity check的例子:
%->@copy "host:/home/panloki/.cshrc" "/flashDrive0/myCshrc"
Copy Ok: 4266 bytes copied
Value = 0 = 0x0
%->@copy "/flashDrive0/myCshrc"
...
...
...
Copy Ok: 4266 bytes copied
Value = 0 = 0x0
注意:
copy命令需要对dosFs支持组件做相应的配置,详情参见《可选的dosFs组件》。
8.3选择一个MTD组件
installDir/target/src/drv/tffs路径中包含了以下类型的MTD组件的源代码:
l 与Intel, AMD, Fujitsu, Sharp设备配合工作的MTD
l 两种用于与通用Flash接口(Common Flash Interface, CFI)兼容的设备的通用MTD
为了更好的支持创新,这些MTD试图覆盖更广范围的设备和总线结构,因此这些驱动与特定驱动相比显得大而慢。如果这些驱动的尺寸或者性能达不到要求,你可以修改这些驱动。8.12编写MTD组件专门论述这个话题。
在8.11使用MTD-支持的Flash设备中有这些MTD的完整列表,并且也有CFI的MTD的详细描述,可以估计其中有没有支持你的设备上使用TrueFFS的驱动。设备通常是由它们的JEDEC ID来标识的。如果找到了适合你的Flash设备的驱动,你可以直接使用MTD的二进制文件。只有当你修改了MTD后才需要重新编译其源代码。
注意:
8.5.4节描述了MTD组件及其详情。
8.4标识套接字驱动
套接字驱动必须与你的BSP匹配。一些BSP包含了套接字驱动,另一些则没有。套接字驱动文件是sysTffs.c,位于BSP的路径下(如果存在的话)。
如果你的BSP没有提供这个文件,可以根据8.10的过程去做。
无论在什么情况下,构建过程都需要在BSP路径下有一个可用的套接字驱动sysTffs.c。详情参见8.5.6。
8.5配置和构建工程
支持TrueFFS的Vxworks系统包括了:
l 完全支持dosFs文件系统的配置
l 一个TrueFFS核心组件,INCLUDE_TFFS
l 来自TrueFFS的至少三个层的软件模块(每层至少一个软件模块)
可以通过命令行或者Tornado的IDE工程工具来配置和构建系统。当选择了一个配置、构建系统的方法后,记住如下标准:
从命令行配置、构建系统包括编辑包含了组件清单和初始化参数的文本文件,包含了调用make工具区构建系统映像。这个过程需要你提供准确的组件依赖关系。
通过IDE配置、构建系统提供了简单和准确的增加所需组建的关系,但是构建过程会比命令行格式慢一些。
如果没有提供套接字驱动或MTD,那么配置和构建过程需要重新考虑,驱动需要注册,MTD需要合适的组件描述。详情参见8.10编写套接字驱动,8.12.4定义自己的MTD作为组件。
关于配置过程的信息,参见Tornado User's Guide: Configuration and Build and the Tornado User's Guide: Projects.。
注意:
Tornado所含的TrueFFS是支持多个MTD和套接字驱动的源代码,MTD位于target/src/drv/tffs,套接字驱动在sysTffs.c中定义。
8.5.1包含文件系统组件
为TrueFFS配置好的系统如果没有Vxworks兼容文件系统——MS-DOS的话就毫无意义。因此,有dosFs的支持和其所依赖的所有组件,都必须包含到你的TrueFFS系统中。详情参见5.2.2配置你的系统。
并且会有一些不必须但是可能对你有用的文件系统组件。这些组件增加了对使用文件系统的基本功能的支持,比如ls, cd, copy等等。
8.5.2包含核心组件
所有的系统必须包含TrueFFS核心组件,INCLUDE_TFFS。
Figure 8-2: Adding TrueFFS Components from the Project Facility
定义这个组件会在系统启动时触发正确的事件序列,使得TruFFS初始化。它也保证了套接字驱动被包含到系统中。
8.5.3包含工具组件
这部分描述TrueFFS工具组件及其目的和默认配置选项。为了创建一个TrueFFS系统是不需改动这些组件的默认配置的。
INCLUDE_TFFS_SHOW
包含这个组件增加了两个TrueFFS配置显示工具——tffsShow()和tffsShowAll()。tffsShow()例程为一个指定的套接字结构打印设备信息,当写boot image时需要确定擦除单元的数量,这个例程在确定这个数字时格外有用。tffsShowAll()例程为所有的在Vxworks中注册了的套接字接口提供了同样的信息,这个例程可以在shell中使用,会以在系统中注册的顺序列出所有驱动。这个组件不是默认包含的。你可以从project facility中或在config.h中定义INCLUDE_TFFS_SHOW使能它。
注意:
INCLUDE_TFFS_BOOT_IMAGE是在套接字驱动(sysTffs.c)中默认定义的。它是用于配置Flash-驻留型boot image的。定义这个常量自动在sysTffs.o中包括了tffsBootImagePut()。这个例程用于将boot image写入到Flash存储器中。参见8.7.3将Boot Image写入到Flash中。
8.5.4包含MTD组件
增加与你的Flash适应的MTD组件(参考8.3)。若你自己编写了MTD,参考8.12.4以确保它被正确的包含。可以通过项目工具或命令行的形式通过在套接字驱动文件中做定义来构建(参考8.5.7)。无论选哪种方法,同样都需要配置MTD组件、构建系统。
TrueFFS提供了支持Intel, AMD, Fujitsu, Sharp的MTD组件,列表如下:
INCLUDE_MTD_CFISCS
CFI/SCS device; for details, see CFI/SCS Flash Support.
INCLUDE_MTD_CFIAMD
CFI-compliant AMD and Fujitsu devices; for details, see AMD/Fujitsu CFI Flash Support.
INCLUDE_MTD_I28F016
Intel 28f016; for details, see Intel 28F016 Flash Support.
INCLUDE_MTD_I28F008
Intel 28f008; for details, see Intel 28F008 Flash Support.
INCLUDE_MTD_AMD
AMD, Fujitsu: 29F0{40,80,16} 8-bit devices; for details, see AMD/Fujitsu Flash Support.
INCLUDE_MTD_WAMD
AMD, Fujitsu 29F0{40,80,16} 16-bit devices.
INCLUDE_MTD_I28F008_BAJA
Intel 28f008 on the Heurikon Baja 4000.
通常,在组件描述文件(在工程工具中包含)中定义的MTD都明确要求了对应的翻译层。但是,如果你从命令行构建系统或者自己编写MTD,就需要自己明确的包含相应的翻译层。
注意:
虽然在config.h中也定义了组件,对于MTD而言这种方法不好,因为这可能或导致与工程的工具配置冲突。
8.5.5包含翻译层
翻译层是由你的Flash介质所决定的。Flash的两种类型是NOR和NAND。本翻译层产品支持NOR和遵从SSFDC规范的NAND。详情参见8.11.3。
翻译层仅提供二进制文件。翻译层组件列表如下:
INCLUDE_TL_FTL
本翻译层提供给NOR设备。如果你可以在Flash中执行代码,那么你的设备就是NOR设备。
INCLUDE_TL_SSFDC
本翻译层提供给遵从东芝的Solid State Floppy Disk Controller 规范的NAND设备,TrueFFS只支持遵从上述协议的NAND设备。
组件描述文件描述了翻译层和MTD的依赖关系。因此,当通过工程工具来配置的的时候,你不必明确选择一个翻译层。构建过程会自动处理的。
如果不使用工程工具,你必须自己负责选择正确的翻译层。与MTD一样,当通过命令行形式配置和构建时,你应该在sysTffs.c中定义翻译层时使用条件子句#ifndef PROJECT_BUILD。详情参见条件编译。
详情参见8.12.4。
8.5.6添加套接字驱动
为了在项目中包含套接字驱动,首先在你的BSP路径中必须有一个可用的套接字驱动程序文件sysTffs.c。
包含套接字驱动相对机械化一些。构建过程会在套接字驱动文件sysTffs.c中检查INCLUDE_TFFS。如果你的BSP没有提供套接字驱动,参见8.10。
8.5.7构建系统的工程
可以从命令行或者Tornado的IDE中构建系统。在构建系统时,需要考虑以下两个问题:
条件编译
sysTffs.c中通过#ifndef PROJECT_BUILD条件子句定义了一些常量。默认的情况下,这些常量定义了TrueFFS提供的所有的MTD组件。这个PROJECT_BUILD子句有条件的包含了为命令行形式提供的组件,没有包含为工程工具构建提供的组件(因为你可以通过GUI来包含它们)。因此在配置MTD和翻译层时也需要这么做。
如果从命令行编译,并且希望节省存储空间,你可以撤销那些你的硬件并不支持的MTD的定义。
更新组件
在构建系统系统前,必须更新MTD的二进制文件,sysTffs.c必须是可以工作的。
MTD是以源代码和二进制形式提供的,如果是自己编写MTD需要重新构建二进制文件。如果installDir/target/src/drv/tffs/*.c文件比installDir/target/lib/archFamily/arch/common/libtffs.a要新,需要在构建系统前重新构建它。
8.6格式化驱动器
启动系统。系统启动、注册套接字驱动后,启动shell。在shell中执行tffsDevFormat()以格式化驱动器。这个例程是在tffsDrv.h中定义的,
tffsDevFormat (int tffsDriveNo, int formatArg);
注意:
即使没有与Flash相关联的块设备,你也可以格式化Flash介质。
警告:
有的设备的文件系统是共享启动代码的,使用了tffsDevFormat()后会导致系统无法启动。只有重新烧录映像文件后系统才可使用。烧录后,通过格式化创建的文件系统也会被破坏。
8.6.1指明驱动器号
tffsDriveNo是第一个参数,是驱动器号码(套接字驱动号码)。驱动器号码标识了待格式化的Flash介质,是套接字驱动注册sysTffsInit()时的次序。多数常见的系统只有一个驱动器,但是TrueFFS支持最多5个驱动器(0-4)。
8.6.2格式化设备
firnatArg是第二个参数,是一个指向结构体tffsDevFormatParams的指针(强制类型转化为int)。这个结构体描述了应该怎样被格式化。结构体在installDir/target/h/tffs/tffsDrv.h中定义如下:
typedef struct
{
tffsFormatParams formatParams;
unsigned formatFlags;
}tffsDevFormatParams;
formatParams是第一个成员,是tffsFormatParams类型的变量。第二个参数formatFlags是unsigned int型变量。
TFFS_STD_FORMAT_PARAMS宏
为了方便从shell中调用tffsDevFormat()函数,可以简单的为第二个参数传0或null指针。TFFS_STD_FORMAT_PARAMS是0, 它在tffsDrv.h中定义了默认的tffsDevFormatParams结构:
#define TFFS_STD_FORMAT_PARAMS { {0, 99, 1, 0x10000l, NULL, {0,0,0,0}, NULL, 2, 0, NULL}, FTL_FORMAT_IF_NEEDED}
这个宏为结构体的两个变量都赋了初值如下:
formatParams = {0, 99, 1, 0x10000l, NULL, {0,0,0,0}, NULL, 2, 0, NULL}
formatFlags = FTL_FORMAT_IF_NEEDED
下面描述了这些默认值和其他的可选值的意义。
formatParams成员
formatParams成员是tffsFormatParams类型的,这个结构和宏TFFS_STD_FORMAT_PARAMS都在installDir/target/h/tffs/tffsDrv.h中作了定义。
如果使用了TFFS_STD_FORMAT_PARAMS,那么默认值是要格式化整个Flash介质。最常见的改变formatParam参数的原因就是需要保留一个启动区域。如果需要创建boot image区域,就需要修改tffsFormatParams结构体的第一个参数bootImageLen。详情参见8.7。
formatFlags成员
第二个成员formatFlags决定了驱动器的格式化选项。它有许多可选值如下:
FTL_FORMAT 1 做FAT和FTL格式化
FTL_FORMAT_IF_NEEDED 2 做FAT格式化,如果需要的话就FTL格式化
NO_FTL_FORMAT 0 仅做FAT格式化
默认的TFFS_STD_FORMAT_PARAMS使用的的是FTL_FORMAT_IF_NEEDED。
8.7为写入BootImage而创建区域
尽管TrueFFS的翻译层具有很多管理文件系统数据的优势,这些服务同样也使得从Flash启动系统变得更复杂。唯一的解决办法就是创建一个不使用TrueFFS的boot image区域,然后将boot image写入其中。本节首先描述这个技术细节情况,其次是如何创建区域,然后是如何写入boot image。
8.7.1Flash写保护
TrueFFS系统要求所有与文件系统交互的Flash设备,包括boot image区域和NVRAM区区不应该是通过MMU来做写保护的。因为这是所有针对系统的命令执行的必然要求。设备的写保护功能会影响这种行为。详情参见rfaWriteProtected。
但是你可以保留一块不收文件系统干预的潜伏区。TrueFFS系统支持用户在格式化设备时指定一个启动代码潜伏区。潜伏区总是在Flash的起始位置。也有很多体系结构端口是要求潜伏区在Flash末尾的,这是由MTD实现的(“欺骗”MTD说系统的存储空间比实际拥有的要小一些)。然后告诉格式化命令说不需要潜伏区。TrueFFS不关心如何管理潜伏区,也不关心这些“欺骗”对潜伏区有什么影响。
8.7.2创建Boot Image区域
为了创建Boot Image区域,需要格式化Flash存储器使得TrueFFS的段(segemt)从一个偏移开始。这就会创建一个不被TrueFFS格式化的潜伏区,保留了一个boot image区域。如果想要更新boot image,可以把boot image写入潜伏区,详情参见8.7.3。
从一个偏移处格式化
为了从一个偏移处格式化Flash,需要将tffsFormatParams结构体初始化成一个值,使得为boot image留一点空间。指定bootImageLen成员的数值,使它最少要与boot image一样大小。bootImageLen成员指明了从这个偏移往后才使用TrueFFS系统。关于bootImageLen和结构体的其他成员参见installDir/target/h/tffs/tffsDrv.h中的命令。
bootImageLen偏移之前的区域排除在TrueFFS之外,对boot image而言这个特殊的区域是必须的,因为对于启动代码而言,它的Flash不需要TrueFFS提供的的翻译和损耗均衡服务。当tffsDevFormat()格式化Flash时,它注意到这个偏移,然后将高于这个偏移的单元擦除并格式化。偏移地址及其之前的单元会被完封不动的保留着,这样其中的数据也会完封不动。
详情参见8.13。
使用BSP帮助例程
一些BSP提供一个可选的、BSP相关的帮助例程,sysTffsFormat()。这个例程可以从外部调用用以创建或者预留boot image区域。这个例程首先建立指向tffsFormatParam结构的指针,tffsFormatParam是之前用bootImageLen数值初始化了偏移的结构体。然后点用tffsDevFormat()。
许多BSP比如das860都包含了一个sysTffsFormat()例程,为boot image保留了0.5MB的空间。这是一个例子:
STATUS sysTffsFormat (void)
{
STATUS status;
tffsDevFormatParams params =
{
#define HALF_FORMAT
/* lower 0.5MB for bootimage, upper 1.5MB for TFFS */
#ifdef HALF_FORMAT
{0x80000l, 99, 1, 0x10000l, NULL, {0,0,0,0}, NULL, 2, 0, NULL},
#else
{0x000000l, 99, 1, 0x10000l, NULL, {0,0,0,0}, NULL, 2, 0, NULL},
#endif /* HALF_FORMAT */
FTL_FORMAT_IF_NEEDED
};
/* we assume that the drive number 0 is SIMM */
status = tffsDevFormat (0, (int)¶ms);
return (status);
}
要参考sysTffsFormat()用法,参见套接字驱动installDir/target/src/drv/tffs/sockets。如果你的BSP不支持sysTffsFormat()例程,或者创建一个相似的例程或者给tffsDevFormat()传递合适的参数。
8.7.3 将boot image写到Flash中
如果已经创建了boot image区域,需要将boot image写到Flash中。使用tffsBootImagePut()函数可以绕过TrueFFS(以及它的翻译层),直接写到Flash的存储位置中。但是因为tffsBootImagePut()函数依赖于tffsRawio()的调用,所以一旦装载了TrueFFS的卷标就不能再使用这个例程了。
警告:
因为tffsBootImagePut()可以让你直接写到Flash上,所以也有可能将TrueFFS管理的区域破坏。关于使用这个utility的详细信息,请参考Vxworks API Reference的tffsBootImagePut()。
tffsBootImagePut()例程是在installDir/target/src/drv/tffs/tffsConfig.c中定义的:
STATUS tffsBootImagePut
(
int driveNo, /* TFFS drive number */
int offset, /* offset in the flash chip/card */
char * filename /* binary format of the bootimage */
)
本例程接收的参数:
driveNo:与格式化例程里的参数一样
offset:boot image写入的位置到Flash存储起始地址的偏移(通常为0)
filename:指向boot image(bootApp或boot ROM image)的指针
8.8装载驱动器
下一步,使用usrTffsConfig()例程,为TureFFS Flash驱动器装载VxWorks的DOS文件系统。这个例程定义在installDir/target/config/comps/src/usrTffs.c:
STATUS usrTffsConfig
(
int drive, /* drive number of TFFS */
int removable, /* 0 for nonremovable flash media */
char * fileName /* mount point */
)
本例程接收3个参数:
drive:指明了TFFS的Flash驱动器的驱动器号,有效值是从0到BSP中的套接字接口数
removable:指明介质是否可移动。0-不可移动,1-可移动
fileName:指明了装载点,例如:’/tffs0/’
下面的例子运行usrTffsConfig()以把驱动器连接到dosFs上,然后运行devs,列出所有的驱动:
% usrTffsConfig 0,0,"/flashDrive0/"
% devs
drv name
0 /null
1 /tyCo/0
1 /tyCo/1
5 host:
6 /vio
2 /flashDrive0/
usrTffsConfig()在内部调用其他一些例程,比如tffsDevCreate()是其中之一。它在套接字驱动器顶段创建一个TrueFFS块设备。它用0-4标识套接则驱动,查找FLSocket结构。tffsDevCreate()接收一个数值,在对应的套接字之上建立一个TrueFFS块设备。
TrueFFS块设备创建后,调用了dcacheDevCreate()和dosFsDevCreate()。这个例程在设备上装载dosFs。装载后,可以像对标准磁盘那样对Flash进行IO操作了。
8.9 运行shell命令
下面每一个例子都假设您已经build了VxWorks并且启动了target。