chibios_with_elua/ext/mmcfs.c

360 lines
8.1 KiB
C

// MMC filesystem implementation using FatFs
#include "mmcfs.h"
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include "ioctl.h"
#include <sys/types.h>
#include "platform_conf.h"
#ifdef BUILD_MMCFS
#include "ff.h"
//~ #include "diskio.h"
#include <fcntl.h>
#include <sys/stat.h>
#include <stdlib.h>
#define MMCFS_MAX_FDS 4
static FIL mmcfs_fd_table[ MMCFS_MAX_FDS ];
static int mmcfs_num_fd;
extern void elua_mmc_init( void );
#ifndef MMCFS_NUM_CARDS
#define NUM_CARDS 1
#else
#define NUM_CARDS MMCFS_NUM_CARDS
#endif
// Data structures used by FatFs
static FATFS mmc_fs[ NUM_CARDS ];
static FIL mmc_fileObject;
//static DIR mmc_dir;
//static FILINFO mmc_fileInfo;
typedef struct
{
DIR *dir;
struct dm_dirent *pdm;
} MMCFS_DIRENT_DATA;
static int mmcfs_find_empty_fd( void )
{
int i;
for (i = 0; i < MMCFS_MAX_FDS; i ++)
if (mmcfs_fd_table[i].fs == NULL)
return i;
return -1;
}
static int mmcfs_open_r( struct _reent *r, const char *path, int flags, int mode, void *pdata )
{
int fd;
int mmc_mode;
char *mmc_pathBuf;
int drv_num = *( int* )pdata;
if (mmcfs_num_fd == MMCFS_MAX_FDS)
{
r->_errno = ENFILE;
return -1;
}
// Default to top directory if none given
if (strchr(path, '/') == NULL)
asprintf( &mmc_pathBuf, "%d:/%s", drv_num, path );
else
asprintf( &mmc_pathBuf, "%d:%s", drv_num, path );
if( mmc_pathBuf == NULL )
{
r->_errno = ENOMEM;
return -1;
}
// Scrub binary flag, if defined
#ifdef O_BINARY
flags &= ~O_BINARY;
#endif
#if _FS_READONLY
if ((flags & O_ACCMODE) != O_RDONLY)
{
r->_errno = EROFS;
free( mmc_pathBuf );
return -1;
}
mmc_mode = FA_OPEN_EXISTING & FA_READ;
#else
// Translate fcntl.h mode to FatFs mode (by jcwren@jcwren.com)
if (((flags & (O_CREAT | O_TRUNC)) == (O_CREAT | O_TRUNC)) && (flags & (O_RDWR | O_WRONLY)))
mmc_mode = FA_CREATE_ALWAYS;
else if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
mmc_mode = FA_OPEN_EXISTING;
else if ((flags & O_CREAT) == O_CREAT)
mmc_mode = FA_OPEN_ALWAYS;
else if ((flags == O_RDONLY) || (flags == O_WRONLY) || (flags == O_RDWR))
mmc_mode = FA_OPEN_EXISTING;
else
{
r->_errno = EINVAL;
free( mmc_pathBuf );
return -1;
}
if ((flags & O_ACCMODE) == O_RDONLY)
mmc_mode |= FA_READ;
else if ((flags & O_ACCMODE) == O_WRONLY)
mmc_mode |= FA_WRITE;
else if ((flags & O_ACCMODE) == O_RDWR)
mmc_mode |= (FA_READ | FA_WRITE);
else
{
r->_errno = EINVAL;
free( mmc_pathBuf );
return -1;
}
#endif // _FS_READONLY
// Open the file for reading
if (f_open(&mmc_fileObject, mmc_pathBuf, mmc_mode) != FR_OK)
{
r->_errno = ENOENT;
free( mmc_pathBuf );
return -1;
}
if (mode & O_APPEND)
mmc_fileObject.fptr = mmc_fileObject.fsize;
fd = mmcfs_find_empty_fd();
memcpy(mmcfs_fd_table + fd, &mmc_fileObject, sizeof(FIL));
mmcfs_num_fd ++;
free( mmc_pathBuf );
return fd;
}
static int mmcfs_close_r( struct _reent *r, int fd, void *pdata )
{
FIL* pFile = mmcfs_fd_table + fd;
f_close( pFile );
memset(pFile, 0, sizeof(FIL));
mmcfs_num_fd --;
return 0;
}
static _ssize_t mmcfs_write_r( struct _reent *r, int fd, const void* ptr, size_t len, void *pdata )
{
#if _FS_READONLY
{
r->_errno = EIO;
return -1;
}
#else
UINT bytesWritten;
if (f_write(mmcfs_fd_table + fd, ptr, len, &bytesWritten) != FR_OK)
{
r->_errno = EIO;
return -1;
}
return (_ssize_t) bytesWritten;
#endif // _FS_READONLY
}
static _ssize_t mmcfs_read_r( struct _reent *r, int fd, void* ptr, size_t len, void *pdata )
{
UINT bytesRead;
if (f_read(mmcfs_fd_table + fd, ptr, len, &bytesRead) != FR_OK)
{
r->_errno = EIO;
return -1;
}
return (_ssize_t) bytesRead;
}
// lseek
static off_t mmcfs_lseek_r( struct _reent *r, int fd, off_t off, int whence, void *pdata )
{
FIL* pFile = mmcfs_fd_table + fd;
u32 newpos = 0;
switch( whence )
{
case SEEK_SET:
// seek from beginning of file
newpos = off;
break;
case SEEK_CUR:
// seek from current position
newpos = pFile->fptr + off;
break;
case SEEK_END:
// seek from end of file
newpos = pFile->fsize + off;
break;
default:
return -1;
}
if (f_lseek (pFile, newpos) != FR_OK)
return -1;
return newpos;
}
// opendir
static void* mmcfs_opendir_r( struct _reent *r, const char* dname, void *pdata )
{
void* res = NULL;
MMCFS_DIRENT_DATA *pd = ( MMCFS_DIRENT_DATA* )malloc( sizeof( MMCFS_DIRENT_DATA ) );
int drv_num = *( int* )pdata;
char *pname = NULL;
if( !pd )
return NULL;
memset( pd, 0, sizeof( MMCFS_DIRENT_DATA ) );
if( ( pd->dir = ( DIR* )malloc( sizeof( DIR ) ) ) == NULL )
goto out;
if( ( pd->pdm = ( struct dm_dirent* )malloc( sizeof( struct dm_dirent ) ) ) == NULL )
goto out;
if( !dname || strlen( dname ) == 0 )
asprintf( &pname, "%d:/", drv_num );
else
asprintf( &pname, "%d:%s", drv_num, dname );
res = f_opendir( pd->dir, pname ) != FR_OK ? NULL : pd;
out:
if( res == NULL )
{
if( pd->dir )
free( pd->dir );
if( pd->pdm )
free( pd->pdm );
free( pd );
}
if( pname )
free( pname );
return res;
}
// readdir
extern char dm_shared_fname[ DM_MAX_FNAME_LENGTH + 1 ];
static struct dm_dirent* mmcfs_readdir_r( struct _reent *r, void *d, void *pdata )
{
MMCFS_DIRENT_DATA *pd = ( MMCFS_DIRENT_DATA* )d;
DIR *pdir = pd->dir;
FILINFO mmc_file_info;
struct dm_dirent* pent = pd->pdm;
char *fn;
#if _USE_LFN
static char lfn[_MAX_LFN * (_DF1S ? 2 : 1) + 1];
mmc_file_info.lfname = lfn;
mmc_file_info.lfsize = sizeof(lfn);
#endif
while( 1 ) // loop until we get a file, error, or end of directory
{
if( f_readdir( pdir, &mmc_file_info ) != FR_OK ) // return NULL on read error
return NULL;
if( mmc_file_info.fname[ 0 ] == '\0' ) // return NULL when listing is done
return NULL;
break;
}
#if _USE_LFN
fn = *mmc_file_info.lfname ? mmc_file_info.lfname : mmc_file_info.fname;
#else
fn = mmc_file_info.fname;
#endif
if( ( mmc_file_info.fattrib & AM_DIR ) != 0 ) // if we have a file, exit loop
pent->flags = DM_DIRENT_FLAG_DIR;
else
pent->flags = 0;
strncpy( dm_shared_fname, fn, DM_MAX_FNAME_LENGTH );
pent->fname = dm_shared_fname;
pent->fsize = mmc_file_info.fsize;
pent->ftime = mmc_file_info.ftime;
return pent;
}
// closedir
static int mmcfs_closedir_r( struct _reent *r, void *d, void *pdata )
{
MMCFS_DIRENT_DATA *pd = ( MMCFS_DIRENT_DATA* )d;
free( pd->dir );
free( pd->pdm );
free( pd );
return 0;
}
static int mmcfs_mkdir_r( struct _reent *r, const char *name, mkdir_mode_t mode, void *pdata )
{
return f_mkdir( name );
}
static int mmcfs_unlink_r( struct _reent *r, const char *fname, void *pdata )
{
return f_unlink( fname );
}
static int mmcfs_rename_r( struct _reent *r, const char *oldname, const char *newname, void *pdata )
{
return f_rename( oldname, newname );
}
// MMC device descriptor structure
static const DM_DEVICE mmcfs_device =
{
mmcfs_open_r, // open
mmcfs_close_r, // close
mmcfs_write_r, // write
mmcfs_read_r, // read
mmcfs_lseek_r, // lseek
mmcfs_opendir_r, // opendir
mmcfs_readdir_r, // readdir
mmcfs_closedir_r, // closedir
NULL, // getaddr
mmcfs_mkdir_r, // mkdir
mmcfs_unlink_r, // unlink
mmcfs_unlink_r, // rmdir
mmcfs_rename_r // rename
};
int mmcfs_init()
{
//~ elua_mmc_init();
#if NUM_CARDS == 1
static int cid = 0;
// A single MMCFS
if ( f_mount( 0, mmc_fs ) != FR_OK )
return DM_ERR_INIT;
return dm_register( "/mmc", &cid, &mmcfs_device );
#else // #if NUM_CARDS == 1
int i;
static char names[ NUM_CARDS ][ 6 ];
static int ids[ NUM_CARDS ];
// [TODO] add more error checking!
for( i = 0; i < NUM_CARDS; i ++ )
if( f_mount( i, mmc_fs + i ) == FR_OK )
{
ids[ i ] = i;
sprintf( names[ i ], "/mmc%d", i );
dm_register( names[ i ], ids + i, &mmcfs_device );
}
return DM_OK;
#endif // #if NUM_CARDS == 1
}
#else // #ifdef BUILD_MMCFS
int mmcfs_init()
{
return dm_register( NULL, NULL, NULL );
}
#endif // BUILD_MMCFS