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.
int mmfs_stream_open( const char *path, int flags);
Open an MMFS file for streaming. The flags
argument is either MMFS_FLAGS_READ to open an
existing file for reading or MMFS_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 and errno 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 to
mmfs_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 to
MMFS_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 to
MMFS_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.
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.