2.2   An Overview of Configuring and Using TrueFFS

Configuring VxWorks to include TrueFFS for Tornado is a matter of rebuilding the VxWorks image after editing the following files:

When you boot this image, it automatically runs tffsDrv( ). This function call automatically registers a socket component for each flash device. At this point, there is not yet a block device driver for the flash, but the socket component driver does provide enough of a connection to support a call to tffsDevFormat( ). Use this routine to format the flash medium for use with TrueFFS. To create a TrueFFS block device on top of the socket component and then mount dosFs on that block device, call usrTffsConfig( ).

2.2.1   Configuring VxWorks and TrueFFS

As mentioned above, configuring VxWorks to include TrueFFS requires edits to:

For a BSP that supports TrueFFS, all these files must reside within your BSP configuration directory under target/config. However, sysTffs.c is not automatically shipped in this directory. Instead, several versions of sysTffs.c are shipped in src/drv/tffs/sockets as ads860-sysTffs.c, mv177-sysTffs.c, hkbaha47-sysTffs.c, and so on.

Read src/drv/tffs/sockets/README to determine which bspname-sysTffs.c is appropriate to your BSP. This README file also contains brief descriptions of all the BSP-specific changes needed to support TrueFFS. After choosing a sysTffs.c file, copy it to your BSP's target/config/bspname directory.

Changing the Makefile

To add sysTffs.o to the list of objects it built for your BSP, edit your BSP's Makefile and add the following configuration macro definition:

MACH_EXTRA = sysTffs.o 

Changing config.h

For most BSPs, including TrueFFS means editing config.h and defining INCLUDE_TFFS. Because TrueFFS provides a block driver for use with dosFs, you also want to define INCLUDE_DOSFS. To avoid a flood of compiler warnings, you should make these #define statements conditional. For example:

#ifndef INCLUDE_TFFS 
#define INCLUDE_TFFS 
#endif 
 
#ifndef INCLUDE_DOSFS 
#define INCLUDE_DOSFS 
#endif

Changing sysLib.c

If your target includes an MMU, its BSP defines a sysPhysMemDesc[ ] table in its sysLib.c. Typically, this table tells the MMU that the memory area that contains the boot image is WRITABLE_NOT, or, in other words, ROM. Until recently, ROM was the only technology that could reliably store a memory-resident boot image. As a result, VxWorks has always defaulted to the assumption that the memory area containing a boot image is ROM.

However, with the advent of flash technology, the possibilities have been expanded. Flash memory is both writable and capable of reliably storing a boot image. Thus, if you intend to store a boot image in flash memory, you must edit sysPhysMemDesc[ ] and reassign the boot image memory area as WRITABLE.

Changing sysTffs.c

The primary function of this file is to define the BSP-specific socket code that bridges the gap between the flash hardware and VxWorks. You also use sysTffs.c to determine which software modules you want to include in TrueFFS. By default, a WRS-supplied sysTffs.c includes all translation layer modules, all MTD modules, as well as code for tffsBootImagePut( ), tffsShow( ), and tffsShowAll( ). To reduce the image size, you can edit sysTffs.c to exclude modules that you know to be unnecessary to your particular application.

Choosing Translation Layer Modules

Use the symbolic constants shown in Table 2-1 to determine which translation layer modules are included in sysTffs.o.

Table 2-1:  Defines for Including Translation Layer Modules


Symbolic Constant
 
Associated Flash Technology
 

INCLUDE_TL_NFTL
 
NAND-based flash
 
INCLUDE_TL_FTL
 
NOR-based flash
 
INCLUDE_TL_SSFDC
 
SSFDC flash
 

If you need to save on memory space and know that your embedded system does not need to support the hardware associated with a specific module, undefine the associated constant. This excludes that module from sysTffs.o. For example, if the only flash device included on your target were a flash array implemented on AMD 29F040 flash, a NOR-based technology, you would define INCLUDE_TL_FTL and undefine INCLUDE_TL_SSFDC and INCLUDE_TL_NFTL.

Choosing MTDs

Use the symbolic constants shown in Table 2-2 to determine which MTDs are included in sysTffs.o. By default, sysTffs.c defines all the constants shown in Table 2-2 and thus includes all MTDs.

Table 2-2:  Defines for Including an MTD


Symbolic Constant
 
Associated Flash Device
 

INCLUDE_MTD_I28F016
 
Intel 28f016
 
INCLUDE_MTD_I28F008
 
Intel 28f008
 
INCLUDE_MTD_I28F008_BAJA
 
Intel 28f008 on the Heurikon Baja 4000
 
INCLUDE_MTD_AMD
 
AMD, Fujitsu: 29F0{40,80,16} 8-bit devices
 
INCLUDE_MTD_CDSN
 
Toshiba, Samsung: NAND CDSN devices
 
INCLUDE_MTD_DOC2
 
Toshiba, Samsung: NAND DiskOnChip 2000 devices
 
INCLUDE_MTD_CFISCS
 
CFI/SCS device
 
INCLUDE_MTD_WAMD
 
AMD, Fujitsu 29F0{40,80,16} 16-bit devices
 

If you need to save on memory space and know that your embedded system does not need to support the hardware associated with a specific MTD, undefine the associated constant. This excludes that MTD from sysTffs.o. For example, if the only flash device included on your target was an 8-bit flash array implemented on AMD 29F040 flash, you could safely undefine all the MTD defines other than INCLUDE_MTD_AMD. This guarantees that sysTffs.o includes only that MTD.


*   

NOTE: If you write your own MTDs, put them in src/drv/tffs. You also need to modify the src/drv/tffs/Makefile so that make knows how to generate objects for the sources you add. Likewise, you need to modify your BSP's sysTffs.c as well as the mtdTable[ ] in src/drv/tffs/tffsConfig.c.
Including Support for Flash-Resident Boot Images

By default, sysTffs.c defines INCLUDE_TFFS_BOOT_IMAGE. Defining this constant automatically includes tffsBootImagePut( ) in your sysTffs.o.

Using tffsBootImagePut( ), you can bypass TrueFFS (and its translation layer) and write directly into any location in flash memory. Although the translation services of TrueFFS provide many advantages for managing the data associated with a file system, those same services also complicate the use of flash memory as a boot device. The only practical solution to this problem is to exclude TrueFFS from the boot area of flash.

Using tffsDevFormat( ), it is possible to format flash memory so that the TrueFFS-managed area starts at an offset. This creates a fallow area within flash that is totally outside the reach of TrueFFS. To write a boot image into this fallow area, use tffsBootImagePut( ).


*   

WARNING: Because tffsBootImagePut( )lets you write directly to any area of flash, it is possible to accidentally overwrite and corrupt the TrueFFS-managed area of flash. For more on this and other dangers associated with this powerful utility, see the reference entry for tffsBootImagePut( ).
Including Support for Flash Show Functionality

Defining INCLUDE_SHOW_ROUTINES automatically includes tffsShow( )and tffsShowAll( ) in sysTffs.o. The tffsShow( ) routine prints device information for a specified socket interface. It is particularly useful when trying to determine the number of erase units required to write a boot image. The tffsShowAll( ) routine provides the same information for all socket interfaces registered with VxWorks. If you want to exclude these routines from TrueFFS, undefine them in you BSP's config.h. However, be aware that doing so excludes the show routines from utilities other than TrueFFS for Tornado.

Configuring Socket Layer Features

Although define statements in sysTffs.c control the inclusion of translation layer modules and MTDs and other flash-related utilities, the bulk of the file is dedicated to defining the functions that comprise the socket component driver. These functions are a standardized API that bridges the gap between the device hardware and VxWorks.1

The socket layer code in your BSP's sysTffs.c must provide reasonable definitions for all the functions in the standardized socket layer API. What constitutes a reasonable function definition depends largely on the flash hardware you have chosen. TrueFFS for Tornado supports three general categories of flash hardware:

  • PC flash cards (removable flash media)
  • DiskOnChip 2000 devices
  • board-resident flash arrays

Both the DiskOnChip 2000 and the board-resident flash arrays are non-removable flash media. Their socket interface needs are relatively simple. As a result, simple "do little or nothing" routines are reasonable implementations for many of the standard socket API functions. All BSPs that support TrueFFS for Tornado contain socket code that can handle the simple socket interface needs of these board-resident flash media.

However, the PC flash cards are a removable flash medium. As a result, their socket interface needs are much more demanding. The interface provided with most BSPs is robust but rather simple. It assumes the socket contains a flash card, and it does not support hot swapping. Currently, there is a big-endian and a little-endian version of this simple PCIC driver. The big-endian version is implemented in the sysTffs.c that ships with the ads860 BSP. The little-endian version is available in the pc386 and the pc486 BSPs.

Associated with the PCIC driver are two defines, INCLUDE_SOCKET_PCIC0 and INCLUDE_SOCKET_PCIC1. Edit your BSP's sysTffs.c and define or undefine these constants according to your needs:

INCLUDE_SOCKET_PCIC0
Creates a socket interface driver for slot 0.

INCLUDE_SOCKET_PCIC1
Creates a socket interface driver for slot 1.

For the pc386 and pc486 BSPs, you have the option of including a more sophisticated socket interface driver than the simple PCIC driver. For more information on this interface, see 2.4 Socket Layer Options for the pc386 and pc486 BSPs.

2.2.2   Formatting Flash

The tffsDevFormat( )function formats flash memory for use with TrueFFS. During the formatting process, this function erases the flash medium and then writes the TrueFFS data-management structures into a header at the start of each erase unit.

As input, tffsDevFormat( )expects a drive number (socket component number) and a pointer to a FormatParams structure. The drive number identifies the flash medium to be formatted and is determined by the order in which the socket components were registered. The FormatParams structure passes in values that specify how you want the flash to be formatted.

To facilitate calling tffsDevFormat( )from a target shell, the second parameter accepts a 0 (the value zero) instead of a FormatParams pointer. This value tells tffsDevFormat( )to use the default FormatParams structure defined and initialized in dosformt.h. The values defined in this default structure are good enough for most purposes.

However, if you need to share the flash medium between TrueFFS and a boot image, the default values in dosformt.h are inadequate. This is because you need to format the flash so that the TrueFFS segment starts at an offset. To do this, you must submit a FormatParams structure whose bootImageLen member value is at least as large as the boot image.

When tffsDevFormat( ) formats flash, it notes the offset, then erases and formats all erase units with starting addresses higher than the offset. The erase unit containing the offset address (and all previous erase units) are left completely untouched. This preserves any data stored before the offset address.

Setting the Cluster Size

When formatting a flash device for use with TrueFFS, each sector on the medium is assigned to a cluster. The number of sectors assigned to each cluster is determined by flMinClusterSize, an int global variable defined in dosFormat.c. Valid values for this global variable are non-negative integer powers of two (1, 2, 4, 8,...). The default value is 4.

For a finer granularity in describing the flash storage space, you can change the value of flMinClusterSize to something smaller than 4. However, because the number of FAT entries is limited, the addressable space on the flash medium is reduced if flMinClusterSize is reduced. For example, 16 megabytes is the largest drive addressable if flMinClusterSize is set to one.

Thus, choosing an flMinClusterSize value means making a compromise between a small address space and small inter-cluster dead spaces. For most file systems, a default flMinClusterSize of 4 is a reasonable compromise.


*   

NOTE: If you change flMinClusterSize, the value remains constant until you explicitly reset it or reboot. The value of flMinClusterSize does not revert to the default after rebooting, but not at the end of a format session. If a target system includes multiple flash drives that require different flMinClusterSize values, make sure you set flMinClusterSize correctly prior to formatting each drive.
About sysTffsFormat( )

In some of the configuration examples that finish up this chapter, you are asked to call sysTffsFormat( ). Internally, sysTffsFormat( ) calls tffsDevFormat( ). As input to tffsDevFormat( ), the sysTffsFormat( ) function supplies a pointer to a FormatParams structure whose bootImageLen member specifies the offset after which to format the flash medium for use with TrueFFS. The area below this offset is excluded from TrueFFS. Typically, you would use such an area to store a boot image. For more information, see the BSP-specific reference entry for the sysTffsFormat( )defined in your BSP's sysTffs.c file.

Problems Associated with Expanding the Region Assigned to Boot Images

As mentioned above, when tffsDevFormat( ) formats flash above an offset, it does not touch the flash below that offset. Normally, this is a good thing. However, problems can arise if any of the erase units below the offset had previously been formatted for use by TrueFFS. These erase units would contain TrueFFS formatting headers written by an earlier call to tffsDevFormat( ).

When TrueFFS mounts a flash device, it scans the entire flash medium for TrueFFS formatting headers. It uses these headers to construct a map of the regions of flash memory that fall under its control. If the flash below the current offset happens to contain TrueFFS headers left over from a previous tffsDevFormat( ) call, the TrueFFS mount can still see these headers and will fail as a result.

To overcome this problem, you can use tffsRawio( )to do a physical erase of the problem erase units. However, this is a very powerful and dangerous utility. If misused, it can permanently damage flash memory. During the development phase of your embedded system, you can probably trust yourself to use it carefully. However, if you need to make the functionality of this utility available in the deployed version of your product, you should surround that functionality with protections appropriate to your application and its users.

2.2.3   Creating TrueFFS Block Devices

Before you can create a logical TrueFFS block device, you need to have already run tffsDrv( ). If you configured VxWorks correctly, this is handled for you automatically at boot time. Running tffsDrv( )initializes TrueFFS for Tornado, which includes setting up the mutual exclusion semaphore, global variables, and data structures needed to manage TrueFFS. This initialization also includes the registration of socket component drivers for all the flash devices on the target.

Registering a socket component driver with TrueFFS starts with getting an FLSocket structure from a preallocated 5-element TrueFFS-internal array of FLSocket structures. The next step is to update that FLSocket structure to contain data and pointers to functions that handle the basic hardware interface to the flash device.

When TrueFFS needs to interact with the socket hardware for a particular flash device, it uses the drive number (0 through 4) as an index into its array of FLSocket structures. TrueFFS then uses the functions referenced in that structure to handle its hardware interface needs. Although these socket interface functions do not quite provide a block device interface, they do provide an interface that is good enough for utilities such as tffsDevFormat( ), a function you can use to format the flash medium for use with TrueFFS. This ability at this stage is important as it is not possible to create a TrueFFS block device for a flash medium that has not been previously formatted for use with TrueFFS.

After registering a socket component driver for a flash device, it is possible to create a TrueFFS block device on top of the socket component driver. To do this, call tffsDevCreate( ). As input, you must specify a number (0 through 4, inclusive) that identifies the socket component driver on top of which to construct the TrueFFS block device. The tffsDevCreate( )call uses this number as an index into the array of FLSocket structures. This number is visible later to dosFs as the driver number.

After creating the TrueFFS block device, you must mount dosFs onto the device. To do this, you must call dosFsDevInit( ). After mounting dosFs, you can read and write from flash memory just as you would from a standard disk drive. In the examples at the end of this chapter, there are no calls to tffsDevCreate( ) or dosFsDevInit( ). Instead, there is a call to usrTffsConfig( ). Internally, this function handles the call to tffsDevCreate( ), dosFsDevInit( ), and the other functions necessary to create a TrueFFS block device and mount dosFs on that device.2


1:  For more information the standardized socket layer API, see 3. Writing Socket Component Drivers and MTDs.

2:  To see the source code for usrTffsConfig( ), look in target/src/config/usrTffs.c.