Customizing the Flash Filesystem

ホーム
Developer Resources
PDF Documents
Caution: This version of this document is no longer maintained. For the latest documentation, see http://www.qnx.com/developers/docs.

Customizing the Flash Filesystem

Introduction

Neutrino ships with a small number of prebuilt flash filesystem drivers for particular embedded systems. For the currently available drivers, look in the ${QNX_TARGET}/${PROCESSOR}/sbin directory. The flash filesystem drivers are named devf-system, where system is derived from the name of the embedded system. You'll find a general description of the flash filesystem in the System Architecture book and descriptions of all the flash filesystem drivers in the Utilities Reference.

If a driver isn't provided for your particular target embedded system, you should first try our "generic" driver (devf-generic). This driver often -- but not always -- works with standard flash hardware. The driver assumes a supported memory technology driver (MTD) and linear memory addressing.

If none of our drivers works for your hardware, you'll need to build your own driver. We provide all the source code needed for you to customize a flash filesystem driver for your target. After installation, look in the bsp_working_dir/src/hardware/flash/boards directory -- you'll find a subdirectory for each board we support.

Besides the boards directory, you should also refer to the following sources to find out what boards/drivers we currently support:

Note that we currently support customizing a driver only for embedded systems with onboard flash memory (also called a resident flash array or RFA). If you need support for removable media like PCMCIA or compact or miniature memory cards, then please contact us.

Driver structure

Every flash filesystem driver consists of the following components:

When customizing the flash filesystem driver for your system, you'll be modifying the main() routine for the flash filesystem and providing an implementation of the socket services component. The other components are supplied as libraries to link into the driver.


Structure of a flash filesystem driver


Structure of the flash filesystem driver.

resmgr and iofunc layers

Like all Neutrino device managers, the flash filesystem uses the standard resmgr/iofunc interface and accepts the standard set of resource manager messages. The flash filesystem turns these messages into read, write, and erase operations on the underlying flash devices.

For example, an open message would result in code being executed that would read the necessary filesystem data structures on the flash device and locate the requested file. A subsequent write message will modify the contents of the file on flash. Special functions, such as erasing the flash device, are implemented using devctl messages.

Flash filesystem component

The flash filesystem itself is the "personality" component of the flash filesystem driver. The filesystem contains all the code to process filesystem requests and to manage the filesystem on the flash devices. The socket and flash services components are used by the flash filesystem to access the flash devices.

The code for the flash filesystem component is platform-independent and is provided in the libfs-flash3.a library.

Socket services component

The socket services component is responsible for any system-specific initialization required by the flash devices at startup and for providing addressability to the flash devices (this applies mainly to windowed flash interfaces).

Before reading/writing the flash device, other components will use socket services to make sure the required address range can be accessed. On systems where the flash device is linearly mapped into the processor address space, addressability is trivial. On systems where the flash is either bank-switched or hidden behind some other interface (such as PCMCIA), addressability is more complicated.

The socket services component is the one that will require the most customization for your system.

Flash services component

The flash services component contains the device-specific code required to write and erase particular flash devices. This component is also called the memory technology driver (MTD).

The directory ${QNX_TARGET}/${PROCESSOR}/lib contains the MTD library libmtd-flash.a to handle the flash devices we support.


Note: bsp_working_dir/src/hardware/flash/mtd-flash contains source for the libmtd-flash.a library.

Probe routine component

The probe routine uses a special algorithm to estimate the size of the flash array. Since the source code for the probe routine is available, you should be able to readily identify any failures in the sizing algorithm.

Building your flash filesystem driver

Before you start customizing your own flash filesystem driver, you should examine the source of all the sample drivers supplied. Most likely, one of the existing drivers can be easily customized to support your system. If not, the devf-ram source provides a good template to start with.

The source tree

The source files are organized as follows:


Figure showing the flash directory structure


Flash directory structure.

The following pathnames apply to the flash filesystems:

Pathname Description
${QNX_TARGET}/usr/include/sys Header file f3s_mtd.h.
${QNX_TARGET}/usr/include/fs Header files f3s_api.h, f3s_socket.h, and f3s_flash.h.
${QNX_TARGET}/${PROCESSOR}/lib Libraries for flash filesystem and flash services.
bsp_working_dir/src/hardware/flash/boards Source code for socket services.
bsp_working_dir/src/hardware/flash/mtd-flash Source code for flash services as well as for probe routine and helper functions.

Before you modify any source, you should:

  1. Create a new directory for your driver in the bsp_working_dir/src/hardware/flash/boards directory.
  2. Copy the files from the sample directory you want into your new directory.

For example, to create a driver called myboard based on the 800FADS board example, you would:

cd bsp_working_dir/hardware/flash/boards
mkdir myboard
cp -cRv 800fads myboard
cd myboard
make clean

The copy command (cp) specifies a recursive copy (the -R option). This will copy all files from the specified source directory including the subdirectory indicating which CPU this driver should be built for. In our example above, the 800fads directory has a ppc subdirectory -- this will cause the new driver (myboard in our example) to be built for the PowerPC.

The Makefile

When you go to build your new flash filesystem driver, you don't need to change the Makefile. Our recursive makefile structure ensures you're linking to the appropriate libraries.

Making the driver

You should use the following command to make the driver:

make F3S_VER=3 MTD_VER=2

For more information, see the technical note Migrating to the New Flash Filesystem.

The main() function

The main() function for the driver, which you'll find in the main.c file in the sample directories, is the first thing that needs to be modified for your system. Let's look at the main.c file for the 800FADS board example:

/*
** File: main.c for 800FADS board
*/
#include <sys/f3s_mtd.h>
#include "f3s_800fads.h"

int main(int argc, char **argv)
{
   int error;
   static f3s_service_t service[]=
   {
      {
         sizeof(f3s_service_t),
         f3s_800fads_open,
         f3s_800fads_page,
         f3s_800fads_status,
         f3s_800fads_close
      },
      {
         /* mandatory last entry */
         0, 0, 0, 0, 0  
      }
   };

   static f3s_flash_v2_t flash[] =
   {
      {
         sizeof(f3s_flash_v2_t),
         f3s_a29f040_ident,      /* Common Ident             */
         f3s_a29f040_reset,      /* Common Reset             */

         /* v1 Read/Write/Erase/Suspend/Resume/Sync (Unused) */	  
         NULL, NULL, NULL, NULL, NULL, NULL,

         NULL,                   /* v2 Read (Use default)    */
      
         f3s_a29f040_v2write,    /* v2 Write                 */
         f3s_a29f040_v2erase,    /* v2 Erase                 */
         f3s_a29f040_v2suspend,  /* v2 Suspend               */
         f3s_a29f040_v2resume,   /* v2 Resume                */
         f3s_a29f040_v2sync,     /* v2 Sync                  */

         /* v2 Islock/Lock/Unlock/Unlockall (not supported)  */
         NULL, NULL, NULL, NULL
      },
      
      {
         /* mandatory last entry */
         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 
			
      }
   };

   /* init f3s */
   f3s_init(argc, argv, flash);

   /* start f3s */
   error = f3s_start(service, flash);

   return error;
}

The service array contains one or more f3s_service_t structures, depending on how many different sockets your driver has to support. The f3s_service_t structure, defined in <fs/f3s_socket.h>, contains function pointers to the socket services routines.

The flash array contains one or more f3s_flash_t structures, depending on how many different types of flash device your driver has to support. The f3s_flash_t structure, defined in <fs/f3s_flash.h>, contains function pointers to the flash services routines.

The f3s_init() and f3s_start() functions are defined in the <fs/f3s_api.h> header file.


Note: Don't use the <fs/f3s_socket.h>, <fs/f3s_flash.h>, and <fs/f3s_api.h> header files directly. Instead, you should include <sys/f3s_mtd.h> for backward and forward compatibility.

f3s_init()

f3s_init (int argc,
          char **argv,
          f3s_flash_t *flash_vect)

This function passes the command-line arguments to the flash filesystem component, which then initializes itself.

f3s_start()

f3s_start (f3s_service_t *service,
           f3s_flash_t *flash)

This function passes the service and flash arrays to the filesystem component so it can make calls to the socket and flash services, and then starts the driver. This function returns only when the driver is about to exit.

When writing your main.c, you'll need to enter:

If you have a system with only one socket consisting of the same flash devices, then there will be only a single entry in each array.

Socket services interface

The socket services interface, defined in the <fs/f3s_socket.h> header file, consists of the following functions:

f3s_open()

int32_t f3s_open (f3s_socket_t *socket, 
                  uint32_t flags)

This function is called to initialize a socket or a particular window in a socket. The function should process any socket options, initialize and map in the flash devices, and initialize the socket structure.

f3s_page()

uint8_t *f3s_page (f3s_socket_t *socket,
                   uint32_t flags,
                   uint32_t offset,
                   int32_t *size)

This function is called to access a window_size sized window at address offset from the start of the device; it must be provided for both bank-switched and linearly mapped flash devices. If the size parameter is non-NULL, you should set it to the size of the window. The function must return a pointer suitable for accessing the device at address offset. On error, it should return NULL and set errno to ERANGE.

f3s_status()

int32_t f3s_status (f3s_socket_t *socket,
                    uint32_t flags)

This function is called to get the socket status. It's used currently only for interfaces that support dynamic insertion and removal. For onboard flash, you should simply return EOK.

f3s_close()

void f3s_close (f3s_socket_t *socket,
                uint32_t flags)

This function is called to close the socket. If you need to, you can disable the flash device and remove any programming voltage, etc.

The following flags are defined for the flags parameter in the socket functions:

F3S_POWER_VCC
Apply read power.
F3S_POWER_VPP
Apply program power.
F3S_OPER_SOCKET
Operation applies to socket given in socket_index.
F3S_OPER_WINDOW
Operation applies to window given in window_index.

The socket parameter is used for passing arguments and returning results from the socket services and for storing information about each socket. To handle complex interfaces such as PCMCIA, the structure has been defined so that there can be more than one socket; each socket can have more than one window. A simple linear flash array would have a single socket and no windows.

The socket structure is defined as:

typedef struct f3s_socket_s
{
  /*
   * these fields are initialized by the flash file system
   * and later validated and set by the socket services
   */
  _Uint16t struct_size;    /* size of this structure */
  _Uint16t status;         /* status of this structure */
  _Uint8t *option;         /* option string from flashio */
  _Uint16t socket_index;   /* index of socket */
  _Uint16t window_index;   /* index of window */
  
  /*
   * these fields are initialized by the socket services and later
   * referenced by the flash file system
   */
  _Uint8t *name;           /* name of driver */
  _Paddr64t address;       /* physical address 0 for allocated */
  _Uint32t window_size;    /* size of window power of two mandatory */
  _Uint32t array_offset;   /* offset of array 0 for based */
  _Uint32t array_size;     /* size of array 0 for window_size */
  _Uint32t unit_size;      /* size of unit 0 for probed */
  _Uint32t flags;          /* flags for capabilities */
  _Uint16t bus_width;      /* width of bus */
  _Uint16t window_num;     /* number of windows 0 for not windowed */
  
  /*
   * these fields are initialized by the socket services and later
   * referenced by the socket services
   */
  _Uint8t* memory;         /* access pointer for window memory */
  void *socket_handle;     /* socket handle pointer for external
                              library */
  void *window_handle;     /* window handle pointer for external
                              library */
  
  /*
   * this field is modified by the socket services as different window
   * pages are selected
   */
  _Uint32t window_offset;  /* offset of window */
}
f3s_socket_t;

Here's a description of the fields:

option
Option string from command line; parse using the f3s_socket_option() function.
socket_index
Current socket.
window_index
Current window.
name
String containing name of driver.
address
Base address of flash array.
window_size
Size of window in bytes.
array_size
Size of array in bytes; 0 indicates unknown.
unit_size
Size of unit in bytes; 0 indicates probed.
flags
The flags field is currently unused.
bus_width
Width of the flash devices in bytes.
window_num
Number of windows in socket; 0 indicates non-windowed.
memory
Free for use by socket services; usually stores current window address.
socket_handle
Free for use by socket services; usually stores pointer to any extra data for socket.
window_handle
Free for use by socket services; usually stores pointer to any extra data for window.
window_offset
Offset of window from base of device in bytes.

Options parsing

The socket services should parse any applicable options before initializing the flash devices in the f3s_open() function. Two support functions are provided for this:

f3s_socket_option()

int f3s_socket_option (f3s_socket_t *socket)

Parse the driver command-line options that apply to the socket services.

Currently the following options are defined:

-s baseaddress,windowsize, arrayoffset, arraysize, unitsize, buswidth, interleave

where:

baseaddress
Base address of the socket/window.
windowsize
Size of the socket/window.
arrayoffset
Offset of window from base of devices in bytes.
arraysize
Size of array in bytes, 0 indicates unknown.
buswidth
Memory bus attached to the flash chips.
interleave
Number of physical chips interleaved to form a larger logical chip (e.g. two 16-bit chips interleaved to form a 32-bit logical chip).

f3s_socket_syspage()

int f3s_socket_syspage (f3s_socket_t *socket)

Parse the syspage options that apply to the socket services.

The syspage options allow the socket services to get any information about the flash devices in the system that is collected by the startup program and stored in the syspage. See the chapter on Customizing Image Startup Programs for more information.

Flash services interface

The flash services interface, defined in the <fs/f3s_flash.h> header file, consists of the following functions:


Note: The values for the flags parameter are defined in <fs/s3s_flash.h>. The most important one is F3S_VERIFY_WRITE. If this is set, the routine must perform a read-back verification after the write as a double check that the write succeeded. Occasionally, however, the hardware reports success even when the write didn't work as expected.

f3s_ident()

int32_t f3s_ident (f3s_dbase_t *dbase,
                   f3s_access_t *access,
                   uint32_t text_offset,
                   uint32_t flags)

Identifies the flash device at address text_offset and fills in the dbase structure with information about the device type and geometry.

f3s_reset()

void f3s_reset (f3s_dbase_t *dbase,
                f3s_access_t *access,
                uint32_t text_offset)

Resets the flash device at address text_offset into the default read-mode after calling the fs3_ident() function or after a device error.

f3s_v2read()

int32_t f3s_v2read (f3s_dbase_t *dbase,
                    f3s_access_t *access,
                    _Uint32t flags,
                    _Uint32t text_offset,
                    _Int32t buffer_size,
                    _Uint8t *buffer);

This optional function is called to read buffer_size bytes from address text_offset into buffer. Normally the flash devices will be read directly via memcpy().

On success, it should return the number of bytes read. If an error occurs, it should return -1 with errno set to one of the following:

EIO
Recoverable I/O error (e.g. failed due to low power, but corruption is localized and block will be usable after erasing).
EFAULT
Unrecoverable I/O error (e.g. block no longer usable).
EINVAL
Invalid command error.
ERANGE
Flash memory access out of range (via service->page function).
ENODEV
Flash no longer accessible (e.g. flash removed).
ESHUTDOWN
Critical error; shut down the flash driver.

f3s_v2write()

int32_t f3s_v2write (f3s_dbase_t *dbase,
                     f3s_access_t *access,
                     _Uint32t flags,
                     _Uint32t text_offset,
                     _Int32t buffer_size,
                     _Uint8t *buffer);

This function writes buffer_size bytes from buffer to address text_offset.

On success, it should return the number of bytes written. If an error occurs, it should return -1 with errno set to one of the following:

EIO
Recoverable I/O error (e.g. failed due to low power or write failed, but corruption is localized and block will be usable after erasing).
EFAULT
Unrecoverable I/O error (e.g. block no longer usable).
EROFS
Block is write protected.
EINVAL
Invalid command error.
ERANGE
Flash memory access out of range (via service->page function).
ENODEV
Flash no longer accessible (e.g. flash removed).
ESHUTDOWN
Critical error; shut down the flash driver.

f3s_v2erase()

int f3s_v2erase (f3s_dbase_t *dbase,
                 f3s_access_t *access,
                 _Uint32t flags,
                 _Uint32t text_offset);

This function begins erasing the flash block containing the text_offset. It can optionally determine if an error has already occurred, or it can just return EOK and let f3s_v2sync() detect any error.

On success, it should return EOK. If an error occurs, it should return one of the following:

EIO
Recoverable I/O error (e.g. failed due to low power or erase failed, but corruption is localized and block will be usable after an erase)
EFAULT
Unrecoverable I/O error (e.g. block no longer usable)
EROFS
Block is write protected
EINVAL
Invalid command error
EBUSY
Flash busy, try again (e.g. erasing same block twice)
ERANGE
Flash memory access out of range (via service->page function)
ENODEV
Flash no longer accessible (e.g. flash removed)
ESHUTDOWN
Critical error; shut down the flash driver.

f3s_v2suspend()

int f3s_v2suspend (f3s_dbase_t *dbase,
                   f3s_access_t *access,
                   _Uint32t flags,
                   _Uint32t text_offset);

This function suspends an erase operation, when supported, for a read or for a write.

On success, it should return EOK. If an error occurs, it should return one of the following:

EIO
Recoverable I/O error (e.g. failed due to low power or erase failed, but corruption is localized and block will be usable after erasing).
EFAULT
Unrecoverable I/O error (e.g. block no longer usable).
EINVAL
Invalid command error.
ECANCELED
Suspend canceled because erase has already completed.
ERANGE
Flash memory access out of range (via service->page function).
ENODEV
Flash no longer accessible (e.g. flash removed).
ESHUTDOWN
Critical error; shut down the flash driver.

f3s_v2resume()

int f3s_v2resume (f3s_dbase_t *dbase,
                  f3s_access_t *access,
                  _Uint32t flags,
                  _Uint32t text_offset);

This function resumes an erase operation after a suspend command has been issued.

On success, it should return EOK. If an error occurs, it should return one of the following:

EIO
Recoverable I/O error (e.g. failed due to low power or erase failed, but corruption is localized and block will be usable after erasing).
EFAULT
Unrecoverable I/O error (e.g. block no longer usable).
EINVAL
Invalid command error.
ERANGE
Flash memory access out of range (via service->page function).
ENODEV
Flash no longer accessible (e.g. flash removed).
ESHUTDOWN
Critical error; shut down the flash driver.

f3s_v2sync()

int f3s_v2sync (f3s_dbase_t *dbase,
                f3s_access_t *access,
                _Uint32t flags,
                _Uint32t text_offset);

This function determines whether an erase operation has completed and returns any detected error.

On success, it should return EOK. If an error occurs, it should return one of the following:

EAGAIN
Still erasing.
EIO
Recoverable I/O error (e.g. failed due to low power or erase failed, but corruption is localized and block will be usable after an erase).
EFAULT
Unrecoverable I/O error (e.g. block no longer usable).
EROFS
Block is write protected.
EINVAL
Invalid command error.
ERANGE
Flash memory access out of range (via service->page function).
ENODEV
Flash no longer accessible (e.g. flash removed).
ESHUTDOWN
Critical error; shut down the flash driver.

f3s_v2islock()

int f3s_v2islock (f3s_dbase_t *dbase,
                  f3s_access_t *access,
                  _Uint32t flags,
                  _Uint32t text_offset);

This function determines whether the block containing the address text_offset can be written to (we term it as success) or not.

On success, it should return EOK. If the block cannot be written to, it should return EROFS. Otherwise, an error has occurred and it should return one of the following:

EIO
Recoverable I/O error (e.g. failed due to low power or lock failed, but corruption is localized and block will be usable after an erase).
EFAULT
Unrecoverable I/O error (e.g. block no longer usable).
EINVAL
Invalid command error.
ERANGE
Flash memory access out of range (via service->page function).
ENODEV
Flash no longer accessible (e.g. flash removed).
ESHUTDOWN
Critical error; shut down the flash driver.

f3s_v2lock()

int f3s_v2lock (f3s_dbase_t *dbase,
                f3s_access_t *access,
                _Uint32t flags,
                _Uint32t text_offset);

This function write-protects the block containing the address text_offset (if supported). If the block is already locked, it does nothing.

On success, it should return EOK. If an error occurs, it should return one of the following:

EIO
Recoverable I/O error (e.g. failed due to low power or lock failed, but corruption is localized and block will be usable after an erase).
EFAULT
Unrecoverable I/O error (e.g. block no longer usable).
EINVAL
Invalid command error.
ERANGE
Flash memory access out of range (via service->page function).
ENODEV
Flash no longer accessible (e.g. flash removed).
ESHUTDOWN
Critical error; shut down the flash driver.

f3s_v2unlock()

int f3s_v2unlock (f3s_dbase_t *dbase,
                  f3s_access_t *access,
                  _Uint32t flags,
                  _Uint32t text_offset);

This function clears write-protection of the block containing the address text_offset (if supported). If the block is already unlocked, it does nothing. Note that some devices do not support unlocking of arbitrary blocks. Instead all blocks must be unlocked at the same time. In this case, use f3s_v2unlockall() instead.

On success, it should return EOK. If an error occurs, it should return one of the following:

EIO
Recoverable I/O error (e.g. failed due to low power or unlock failed, but corruption is localized and block will be usable after an erase).
EFAULT
Unrecoverable I/O error (e.g. block no longer usable).
EINVAL
Invalid command error.
ERANGE
Flash memory access out of range (via service->page function).
ENODEV
Flash no longer accessible (e.g. flash removed).
ESHUTDOWN
Critical error; shut down the flash driver.

f3s_v2unlockall()

int f3s_v2unlockall (f3s_dbase_t *dbase,
                     f3s_access_t *access,
                     _Uint32t flags,
                     _Uint32t text_offset);

This function clears all write-protected blocks on the device containing the address text_offset. Some boards use multiple chips to form one single logical device. In this situation, each chip will have f3s_v2unlockall() invoked on it separately.

On success, it should return EOK. If an error occurs, it should return one of the following:

EIO
Recoverable I/O error (e.g. failed due to low power or unlock failed, but corruption is localized and block will be usable after an erase).
EFAULT
Unrecoverable I/O error (e.g. block no longer usable).
EINVAL
Invalid command error.
ERANGE
Flash memory access out of range (via service->page function).
ENODEV
Flash no longer accessible (e.g. flash removed).
ESHUTDOWN
Critical error; shut down the flash driver.

Note: We currently don't support user-customized flash services, nor do we supply detailed descriptions of the flash services implementation.

Choosing the right routines

We provide several device-specific variants of the core set of flash services:

For example, if you have a 16-bit Intel device and you want to use f3s_v2erase(), you'd use the f3s_iCFI_v2erase() routine.

For more information, see the technical note Choosing the correct MTD Routine for the Flash Filesystem.


Note: The file <sys/f3s_mtd.h> can be found in:
bsp_working_dir/src/hardware/flash/mtd-flash/public/sys/f3s_mtd.h.

Example: The devf-ram driver

This driver uses main memory rather than flash for storing the flash filesystem. Therefore, the filesystem is not persistent -- all data is lost when the system reboots or /dev/shmem/fs0 is removed. This driver is used mainly for test purposes.

main()

In the main() function, we declare a single services array entry for the socket services functions and a null entry for the flash services functions.

/*
** File: f3s_ram_main.c
**
** Description:
**
** This file contains the main function for the f3s 
** flash filesystem
**
*/
#include "f3s_ram.h"

int main(int argc, char **argv)
{
   int error;
   static f3s_service_t service[] =
   {
      {
         sizeof(f3s_service_t),
         f3s_ram_open,
         f3s_ram_page,
         f3s_ram_status,
         f3s_ram_close
      },
      
      {
         /* mandatory last entry */
         0, 0, 0, 0, 0
      }
   };

   static f3s_flash_v2_t flash[] =
   {
      {
         sizeof(f3s_flash_v2_t),
         f3s_sram_ident,          /* Common Ident            */
         f3s_sram_reset,          /* Common Reset            */
         NULL,                    /* v1 Read (Deprecated)    */
         NULL,                    /* v1 Write (Deprecated)   */
         NULL,                    /* v1 Erase (Deprecated)   */
         NULL,                    /* v1 Suspend (Deprecated) */
         NULL,                    /* v1 Resume  (Deprecated) */
         NULL,                    /* v1 Sync (Deprecated)    */
         NULL,                    /* v2 Read (Use default)   */
         f3s_sram_v2write,        /* v2 Write                */
         f3s_sram_v2erase,        /* v2 Erase                */
         NULL,                    /* v2 Suspend (Unused)     */
         NULL,                    /* v2 Resume (Unused)      */
         f3s_sram_v2sync,         /* v2 Sync                 */
         f3s_sram_v2islock,       /* v2 Islock               */
         f3s_sram_v2lock,         /* v2 Lock                 */
         f3s_sram_v2unlock,       /* v2 Unlock               */
         f3s_sram_v2unlockall     /* v2 Unlockall            */
      },
                
      {
         /* mandatory last entry */
         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 
      }
   };

   /* init f3s */
   f3s_init(argc, argv, (f3s_flash_t *)flash);

   /* start f3s */
   error = f3s_start(service, (f3s_flash_t *)flash);

   return (error);
}

f3s_ram_open()

In the socket services open() function, we assign a name for the driver and then process any options. If no options are specified, a default size is assigned and the memory for the (virtual) flash is allocated.

/*
** File: f3s_ram_open.c
**
** Description:
**
** This file contains the open function for the ram library
**
*/
#include "f3s_ram.h"

int32_t f3s_ram_open(f3s_socket_t *socket,
                     uint32_t flags)
{
   static void *   memory;
   char           name[8];
   int                 fd;
   int               flag;

   /* check if not initialized */
   if (!memory)
   {
      /* get io privileges */
      ThreadCtl(_NTO_TCTL_IO, NULL);

      /* setup socket name */
      socket->name = "RAM (flash simulation)";

      /* check if there are socket options */
      if (f3s_socket_option(socket))
         socket->window_size = 1024 * 1024;

      /* check if array size was not chosen */
      if (!socket->array_size)
         socket->array_size = socket->window_size;

      /* check if array size was not specified */
      if (!socket->array_size) return (ENXIO);

      /* set shared memory name */
      sprintf(name, "/fs%X", socket->socket_index);

      /* open shared memory */
      fd = shm_open(name, O_CREAT | O_RDWR, 0777);
      
      if (fd < 0) return (errno);

      /* set size of shared memory */
      flag = ftruncate(fd, socket->array_size);
      
      if (flag)
      {
         close(fd);
         return (errno);
      }

      /* map physical address into memory */
      memory = mmap(NULL, socket->array_size,
                    PROT_READ | PROT_WRITE, 
                    MAP_SHARED, fd, socket->address);
				      
      if (!memory)
      {
         close(fd);
         return (errno);
      }

      /* copy socket handle */
      socket->socket_handle = (void *)fd;
   }

   /* set socket memory pointer to previously initialized
      value */
   socket->memory = memory;
   return (EOK);
}

f3s_ram_page()

In the socket services page() function, we first check that the given offset doesn't exceed the bounds of the allocated memory, and then assign the window size if required. The function returns the offset address modulo the window size.

/*
** File: f3s_ram_page.c
**
** Description:
**
** This file contains the page function for the ram library
**
*/
#include "f3s_ram.h"

uint8_t *f3s_ram_page(f3s_socket_t *socket,
                      uint32_t flags,
                      uint32_t offset,
                      int32_t *size)
{
   /* check if offset does not fit in array */
   if (offset >= socket->window_size)
   {
      errno = ERANGE;
      return (NULL);
   }

   /* select proper page */
   socket->window_offset = offset & ~(socket->window_size - 1);

   /* set size properly */
   *size = min((offset & ~(socket->window_size - 1)) +
               socket->window_size - offset, *size);

   /* return memory pointer */
   return (socket->memory + offset);
}

The socket services status() and close() don't do anything interesting in this driver.