Chapter 43. Usage
MMFS is accessed through the FILEIO package which presents a standard
POSIX compatible IO interface through which applications use standard
open()
, read()
,
write()
and close()
calls. Streaming support is provided through a small library, mmfslib,
that presents a more application-friendly interface.
43.1. FILEIO Interface
MMFS supplies most of the standard file IO functionality. However, since it is optimized for supporting streamed data, it has a number of restrictions that mean that it does not always behave like a general-purpose filesystem.
-
Files may not be resized after creation and are essentially
write-once/read-many. Between the initial
open()
andclose()
that creates a file it will be extended as requires. On subsequent opens, even those that specifyO_WRITE
, data may only be written to the existing file extent. -
If an attempt is made to create a file that already exists, the
open()
will fail. Instead the file must be deleted first and may then be created anew. - Creating a file with
O_EXCL
will always fail. -
If an attempt is made to rename a file to a filename that already
exists, the
rename()
will fail, rather than overwriting the destination file. This includes attempting to rename the file to its own name. Instead the destination file must be deleted first.
43.2. MMFSLib
MMFS provides a simple library for handling streamed data. This comprises a small set of API function. The following sections describe the API, followed by a simple example.
43.2.1. MMFSLib API
The following functions are supported.
-
int mmfs_stream_open( const char *path, int flags);
Open an MMFS file for streaming. The
flags
argument is eitherMMFS_FLAGS_READ
to open an existing file for reading orMMFS_FLAGS_WRITE
to create a new file for writing. If the file doesn't exist (when opening for reading) or it does exist (when opening for writing) -1 will be returned anderrno
will be set to a suitable error code. On success a file descriptor is returned which may be used in other mmfslib calls, or in normal FILEIO calls.The stream starts in
RANDOM
mode and must be set to streaming mode with a call tommfs_stream_set_mode()
.-
int mmfs_stream_info( int fd, mmfs_file_info *info );
This function returns information about the file. The mmfs_file_info structure contains the following fields:
-
buffer_size
-
The size of data buffers that will be exchanged using
mmfs_stream_next_buffer()
. -
multi_buffers
-
The number of buffers that the application may have in hand between
calls to
mmfs_stream_next_buffer()
. -
file_size
- The current size of the file.
-
max_size
- The maximum size the file may grow to.
-
-
int mmfs_stream_set_mode( int fd, int mode, int stride );
Set the file mode. The mode may be one of the following:
-
MMFS_MODE_RANDOM
- This is the default mode. A file in this mode should be accessed using the standard filesystem API.
-
MMFS_MODE_READ_FORWARD
-
In this mode the file is being read forward. The
stride
argument defines the number of buffers skipped between each call to mmfs_stream_next_buffer(). A stride of 1 will read the whole file sequentially. A stride of 2 will read every other buffer; a stride of 3 will read every third buffer, and so on. -
MMFS_MODE_READ_BACKWARD
-
This is similar to
MMFS_MODE_READ_FORWARD
except that the file is read backwards. The stride applies in exactly the same way except, obviously, the buffers supplied move progressively backwards through the file. -
MMFS_MODE_WRITE
-
This sets the file up for streamed writing. The
stride
argument is not used and is forced to 1.
Mode changes take effect immediately and apply from the file's current location. A file only becomes capable of streaming after
mmfs_stream_set_mode()
has been called for the first time. Changing file mode during streaming may incur a performance penalty as new data blocks are fetched. A file may not be switched from a read mode to a write mode or vice versa.-
-
int mmfs_stream_get_mode( int fd, int *mode, int *speed );
-
This function returns the mode and speed previously set by a call to
mmfs_stream_set_mode()
. -
int mmfs_stream_next_buffer( int fd, void **buffer );
This function fetches the next stream buffer. The exact semantics of this function depend on the mode and the level of multi-buffering.
If the file has been set to one of the read modes, then each call returns the next buffer full of data from the file according to the direction and stride. The level of multi-buffering determines how many buffers the application may have in hand at any one time. For example, with a multi-buffering level of 2, the first two calls to this routine will return the first two buffers from the stream. The third call will return the third buffer, but will also cause the first buffer to become invalid and be returned to the filesystem for reuse. The fourth call will return the fourth buffer but will also invalidate the second buffer, and so on through the stream.
If the file has been set to the write mode, then each call returns an empty buffer for the application to fill with data. The multi-buffering level determines when the buffers will be written to the file. For example, with a multi-buffering level of 2, the first two calls will return an empty buffer each. The third call will cause the first buffer to be written to the file and will return a new empty buffer to replace it. The fourth call will cause the second buffer to be written to the file and a new buffer to be returned, and so on.
-
int mmfs_stream_set_data( int fd, void *buffer );
-
This function sets the per-directory entry data on the file. The
buffer
argument must point toMMFS_DATASIZE
bytes of data that will be written into the directory entry. -
int mmfs_stream_get_data( int fd, void *buffer );
-
This function reads the per-directory entry data on the file. The
buffer
argument must point toMMFS_DATASIZE
bytes of memory that will be set to the data read from the directory entry. -
int mmfs_stream_close( int fd );
- This function closes the file. Any buffers still in possession of the application will be invalidated. If the file was open for writing the contents of these buffers will be written to the file.
43.2.2. Example
The following code provides a very simple example of how MMFSLib should be used. The code presented here is somewhat simplified and for clarity does not contain any error checking and recovery. It is assumed that the IO devices are accessed via a simple DMA interface; clearly real devices might be more complex than this.
First, a simple routine to stream data from a device to a file for a given duration:
static void write_stream( char *name, int duration ) { int i; int fd; int result; void *buffer; int bufno = 0; int buffer_size; mmfs_file_info info; cyg_tick_count end; // Open the stream for writing. fd = mmfs_stream_open( name, MMFS_OPEN_WRITE ); // Get stream information, we are only interested in the buffer // size. result = mmfs_stream_info( fd, &info ); buffer_size = info.buffer_size; // Set the stream into streamed write mode. The filesystem in now // ready to stream data to this file. result = mmfs_stream_set_mode( fd, MMFS_MODE_WRITE, 1 ); // Convert duration from seconds to an absolute end time in system // ticks. end = cyg_current_time() + duration*ticks_per_second; // Prime the device with the first set of buffers, this will start // the DMA transfers going. for( i = 0; i < CYGNUM_FS_MMFS_MULTI_BUFFER; i++ ) { result = mmfs_stream_next_buffer( fd, &buffer ); dma_start( &input, bufno, DMA_READ, buffer, buffer_size ); bufno++; if( bufno >= CYGNUM_FS_MMFS_MULTI_BUFFER ) bufno = 0; } // Wait for the first buffer to fill. dma_wait( &input, bufno ); // Now stream to the file for the given duration. while( cyg_current_time() < end ) { // Fetch a new buffer from MMFS. As a side effect this also // invalidates the oldest buffer, which will be the one that // has just finished its DMA transfer. result = mmfs_stream_next_buffer( fd, &buffer ); // Set up a DMA transfer from the device dma_start( &input, bufno, DMA_READ, buffer, buffer_size ); bufno++; if( bufno >= CYGNUM_FS_MMFS_MULTI_BUFFER ) bufno = 0; // Wait for the next device buffer to complete. dma_wait( &input, bufno ); } // Wait for remaining buffers to finish for( i = 0; i < CYGNUM_FS_MMFS_MULTI_BUFFER; i++ ) { bufno++; if( bufno >= CYGNUM_FS_MMFS_MULTI_BUFFER ) bufno = 0; dma_wait( &input, bufno ); } // Finally close the stream. result = mmfs_stream_close( fd ); }
The code to read a stream is very similar, although this time it is parameterized by the required stride rather than the duration:
static void read_stream( char *name, int stride ) { int i; int fd; int result; void *buffer; int bufno = 0; int buffer_size; mmfs_file_info info; // Open the stream for reading. fd = mmfs_stream_open( name, MMFS_OPEN_READ ); // Get stream information, we are only interested in the buffer // size. result = mmfs_stream_info( fd, &info ); buffer_size = info.buffer_size; // Set the stream into streamed read mode using the given // stride. The filesystem will start preparation for streaming by // prefetching the first data blocks up to the multi-buffer limit. result = mmfs_stream_set_mode( fd, MMFS_MODE_READ_FORWARD, stride ); // Prime the device with the first set of buffers, this will start // the DMA transfers going. for( i = 0; i < CYGNUM_FS_MMFS_MULTI_BUFFER; i++ ) { result = mmfs_stream_next_buffer( fd, &buffer ); dma_start( &output, bufno, DMA_WRITE, buffer, buffer_size ); bufno++; if( bufno >= CYGNUM_FS_MMFS_MULTI_BUFFER ) bufno = 0; } // Wait for the first buffer to empty. dma_wait( &input, bufno ); // Now stream from the file to the device until we reach the end // of the file. for(;;) { // Fetch a new buffer full of data from MMFS. As a side effect // this also invalidates the oldest buffer, which will be // the one that has just finished its DMA transfer. result = mmfs_stream_next_buffer( fd, &buffer ); if( result < 0 && errno == EEOF ) break; // Set up a DMA transfer to the device dma_start( &output, bufno, DMA_WRITE, buffer, buffer_size ); bufno++; if( bufno >= CYGNUM_FS_MMFS_MULTI_BUFFER ) bufno = 0; // Wait for the next device buffer to complete. dma_wait( &output, bufno ); } // Wait for remaining buffers to finish for( i = 0; i < CYGNUM_FS_MMFS_MULTI_BUFFER; i++ ) { bufno++; if( bufno >= CYGNUM_FS_MMFS_MULTI_BUFFER ) bufno = 0; dma_wait( &output, bufno ); } // Finally close the stream. result = mmfs_stream_close( fd ); }
The example.c
test program contains versions of
both of these routines.
2024-03-18 | eCosPro Non-Commercial Public License |