// MMC filesystem implementation using FatFs #include "mmcfs.h" #include #include #include #include "ioctl.h" #include #include "platform_conf.h" #ifdef BUILD_MMCFS #include "ff.h" //~ #include "diskio.h" #include #include #include #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