Files
zTC1/mico-os/libraries/filesystem/FatFs/fatfs_user_api_driver.c

668 lines
21 KiB
C

/**
******************************************************************************
* @file fatfs_user_api_driver.c
* @author You xx
* @version V1.0.0
* @date 28-Nov-2016
* @brief This file provide User API driver for FatFs
******************************************************************************
*
* UNPUBLISHED PROPRIETARY SOURCE CODE
* Copyright (c) 2016 MXCHIP Inc.
*
* The contents of this file may not be disclosed to third parties, copied or
* duplicated in any form, in whole or in part, without the prior written
* permission of MXCHIP Corporation.
******************************************************************************
*/
#include "common.h"
#include "mico_result.h"
#include "mico_filesystem_internal.h"
#include "StringUtils.h"
/******************************************************
* Macros
******************************************************/
/******************************************************
* Constants
******************************************************/
/******************************************************
* Enumerations
******************************************************/
/******************************************************
* Type Definitions
******************************************************/
/******************************************************
* Structures
******************************************************/
/******************************************************
* Static Function Declarations
******************************************************/
static OSStatus fatfs_init( void );
static OSStatus fatfs_mount( mico_block_device_t* device, mico_filesystem_t* fs_handle_out );
static OSStatus fatfs_unmount( mico_filesystem_t* fs_handle );
static OSStatus fatfs_file_get_details( mico_filesystem_t* fs_handle, const char* filename,
mico_dir_entry_details_t* details_out );
static OSStatus fatfs_file_open( mico_filesystem_t* fs_handle, mico_file_t* file_handle_out, const char* filename,
mico_filesystem_open_mode_t mode );
static OSStatus fatfs_file_seek( mico_file_t* file_handle, int64_t offset, mico_filesystem_seek_type_t whence );
static OSStatus fatfs_file_tell( mico_file_t* file_handle, uint64_t* location );
static OSStatus fatfs_file_read( mico_file_t* file_handle, void* data, uint64_t bytes_to_read,
uint64_t* returned_bytes_count );
static OSStatus fatfs_file_write( mico_file_t* file_handle, const void* data, uint64_t bytes_to_write,
uint64_t* written_bytes_count );
static OSStatus fatfs_file_flush( mico_file_t* file_handle );
static int fatfs_file_end_reached( mico_file_t* file_handle );
static OSStatus fatfs_file_close( mico_file_t* file_handle );
static OSStatus fatfs_file_delete( mico_filesystem_t* fs_handle, const char* filename );
static OSStatus fatfs_dir_open( mico_filesystem_t* fs_handle, mico_dir_t* dir_handle, const char* dir_name );
static OSStatus fatfs_dir_read( mico_dir_t* dir_handle, char* name_buffer, unsigned int name_buffer_length,
mico_dir_entry_type_t* type, mico_dir_entry_details_t* details );
static int fatfs_dir_end_reached( mico_dir_t* dir_handle );
static OSStatus fatfs_dir_rewind( mico_dir_t* dir_handle );
static OSStatus fatfs_dir_close( mico_dir_t* dir_handle );
static OSStatus fatfs_dir_create( mico_filesystem_t* fs_handle, const char* directory_name );
static OSStatus fatfs_format( mico_block_device_t* device );
static OSStatus fatfs_get_info( mico_filesystem_info* info,char* mounted_name );
static OSStatus fatfs_scan_files( char* mounted_name, mico_scan_file_handle arg );
/******************************************************
* Variable Definitions
******************************************************/
/* This is the User API driver structure for FatFS */
mico_filesystem_driver_t mico_filesystem_driver_fatfs =
{
.init = fatfs_init,
.mount = fatfs_mount,
.unmount = fatfs_unmount,
.file_get_details = fatfs_file_get_details,
.file_open = fatfs_file_open,
.file_seek = fatfs_file_seek,
.file_tell = fatfs_file_tell,
.file_read = fatfs_file_read,
.file_write = fatfs_file_write,
.file_flush = fatfs_file_flush,
.file_end_reached = fatfs_file_end_reached,
.file_close = fatfs_file_close,
.file_delete = fatfs_file_delete,
.dir_open = fatfs_dir_open,
.dir_read = fatfs_dir_read,
.dir_end_reached = fatfs_dir_end_reached,
.dir_rewind = fatfs_dir_rewind,
.dir_close = fatfs_dir_close,
.dir_create = fatfs_dir_create,
.format = fatfs_format,
.get_info = fatfs_get_info,
.scan_files = fatfs_scan_files,
};
/******************************************************
* Function Definitions
******************************************************/
/* Initialises FatFS shim - nothing to be done */
static OSStatus fatfs_init( void )
{
return kNoErr;
}
/* Internal function for mounting a FatFS filesystem from a block device (with a "mount_now" parameter) */
static OSStatus fatfs_internal_mount( mico_block_device_t* device, mico_filesystem_t* fs_handle_out,
mico_bool_t mount_now )
{
static uint8_t next_drive_id = 0;
FRESULT fatfs_result;
/* Create a logical drive name "0:", "1:" etc */
unsigned_to_decimal_string( next_drive_id, (char*) &fs_handle_out->data.fatfs.drive_id, 1, 3 );
strcat( (char*) &fs_handle_out->data.fatfs.drive_id, ":" );
next_drive_id++;
/* Mount the drive */
fatfs_result = f_mount( &fs_handle_out->data.fatfs.handle, (char*) &fs_handle_out->data.fatfs.drive_id,
(mount_now == MICO_TRUE) ? 1 : 0, device );
if ( fatfs_result != FR_OK )
{
return MICO_FILESYSTEM_ERROR;
}
return kNoErr;
}
/* Unmounts a FatFS filesystem from a block device */
static OSStatus fatfs_unmount( mico_filesystem_t* fs_handle )
{
FRESULT fatfs_result;
/* Unmount the drive */
fatfs_result = f_mount( NULL, (TCHAR*) &fs_handle->data.fatfs.drive_id, 1, NULL );
if ( fatfs_result != FR_OK )
{
return MICO_FILESYSTEM_ERROR;
}
return kNoErr;
}
/* Formats a block device with a FatFS filesystem */
static OSStatus fatfs_format( mico_block_device_t* device )
{
FRESULT fatfs_result;
OSStatus result;
mico_filesystem_t fs_handle;
/* Check that the block sizes are OK */
/* Init the block device to populate the device sizes */
result = device->driver->init( device, BLOCK_DEVICE_READ_ONLY );
if ( result != kNoErr )
{
return result;
}
if ( (device->erase_block_size != BLOCK_DEVICE_ERASE_NOT_REQUIRED) &&
(((device->erase_block_size < DEFAULT_SECTOR_SIZE) &&
(( DEFAULT_SECTOR_SIZE % device->erase_block_size) != 0))
||
((device->erase_block_size >= DEFAULT_SECTOR_SIZE) &&
((device->erase_block_size % DEFAULT_SECTOR_SIZE) != 0))) )
{
/* Erase block size is invalid - not a multiple or sub-multiple of DEFAULT_SECTOR_SIZE */
return MICO_FILESYSTEM_BLOCK_SIZE_BAD;
}
if ( (device->write_block_size != BLOCK_DEVICE_WRITE_NOT_ALLOWED) &&
(((device->write_block_size < DEFAULT_SECTOR_SIZE) &&
(( DEFAULT_SECTOR_SIZE % device->write_block_size) != 0))
||
((device->write_block_size >= DEFAULT_SECTOR_SIZE) &&
((device->write_block_size % DEFAULT_SECTOR_SIZE) != 0))) )
{
/* Write block size is invalid - not a multiple or sub-multiple of DEFAULT_SECTOR_SIZE */
return MICO_FILESYSTEM_BLOCK_SIZE_BAD;
}
if ( ((device->read_block_size < DEFAULT_SECTOR_SIZE) &&
(( DEFAULT_SECTOR_SIZE % device->read_block_size) != 0))
||
((device->read_block_size >= DEFAULT_SECTOR_SIZE) &&
((device->read_block_size % DEFAULT_SECTOR_SIZE) != 0)) )
{
/* Read block size is invalid - not a multiple or sub-multiple of DEFAULT_SECTOR_SIZE */
return MICO_FILESYSTEM_BLOCK_SIZE_BAD;
}
/* Temporarily mount the drive (with mount-later flag) */
result = fatfs_internal_mount( device, &fs_handle, MICO_FALSE );
if ( result != kNoErr )
{
return result;
}
/* Format device */
fatfs_result = f_mkfs( (char*) &fs_handle.data.fatfs.drive_id, 1, DEFAULT_SECTOR_SIZE );
if ( fatfs_result != FR_OK )
{
return MICO_FILESYSTEM_ERROR;
}
/* Unmount again */
return fatfs_unmount( &fs_handle );
}
/* Mounts a FatFS filesystem from a block device */
static OSStatus fatfs_mount( mico_block_device_t* device, mico_filesystem_t* fs_handle_out )
{
OSStatus result;
/* Format if required */
if ( device->init_data->volatile_and_requires_format_when_mounting == MICO_TRUE )
{
/* Format device */
result = fatfs_format( device );
if ( result != kNoErr )
{
return result;
}
}
/* Do the mount */
// return fatfs_internal_mount( device, fs_handle_out, MICO_TRUE );
return fatfs_internal_mount( device, fs_handle_out, MICO_FALSE );
}
/* Opens a file within a FatFS filesystem */
static OSStatus fatfs_file_open( mico_filesystem_t* fs_handle, mico_file_t* file_handle_out, const char* filename,
mico_filesystem_open_mode_t mode )
{
FRESULT fatfs_result;
FATFS_BYTE fatfs_mode;
FIL* file_handle = &file_handle_out->data.fatfs;
/* Match the mico mode to a FatFS mode */
switch ( mode )
{
case MICO_FILESYSTEM_OPEN_ZERO_LENGTH:
fatfs_mode = FA_CREATE_ALWAYS | FA_READ | FA_WRITE;
break;
case MICO_FILESYSTEM_OPEN_WRITE_CREATE:
case MICO_FILESYSTEM_OPEN_APPEND_CREATE:
fatfs_mode = FA_OPEN_ALWAYS | FA_READ | FA_WRITE;
break;
case MICO_FILESYSTEM_OPEN_FOR_READ:
fatfs_mode = FA_OPEN_EXISTING | FA_READ;
break;
case MICO_FILESYSTEM_OPEN_FOR_WRITE:
case MICO_FILESYSTEM_OPEN_APPEND:
fatfs_mode = FA_OPEN_EXISTING | FA_READ | FA_WRITE;
break;
default:
/* Unknown mode */
return kParamErr;
}
/* Change default drive to the requested drive */
fatfs_result = f_chdrive( (char*) &fs_handle->data.fatfs.drive_id );
if ( fatfs_result != FR_OK )
{
return MICO_FILESYSTEM_ERROR;
}
/* Open the file */
fatfs_result = f_open( file_handle, filename, fatfs_mode );
if ( fatfs_result != FR_OK )
{
return MICO_FILESYSTEM_ERROR;
}
/* If appending was requested , move to the end of the file */
if ( (mode == MICO_FILESYSTEM_OPEN_APPEND) ||
(mode == MICO_FILESYSTEM_OPEN_APPEND_CREATE) )
{
/* Seek to end of the file */
fatfs_result = f_lseek( file_handle, f_size( file_handle ) );
if ( fatfs_result != FR_OK )
{
f_close( file_handle );
return MICO_FILESYSTEM_ERROR;
}
}
return kNoErr;
}
/* Get details of a file within a FatFS filesystem */
static OSStatus fatfs_file_get_details( mico_filesystem_t* fs_handle, const char* filename,
mico_dir_entry_details_t* details_out )
{
FILINFO file_stat;
FRESULT fatfs_result;
/* Change default drive to the requested drive */
fatfs_result = f_chdrive( (TCHAR*) &fs_handle->data.fatfs.drive_id );
if ( fatfs_result != FR_OK )
{
return MICO_FILESYSTEM_ERROR;
}
fatfs_result = f_stat( filename, &file_stat );
if ( fatfs_result != FR_OK )
{
return MICO_FILESYSTEM_ERROR;
}
/* Fill in the details structure */
details_out->size = file_stat.fsize;
details_out->attributes_available = file_stat.fattrib;
details_out->date_time = (file_stat.fdate << 16) + file_stat.ftime; /* TODO: This is wrong - need to do conversion */
details_out->attributes_available = MICO_TRUE;
details_out->date_time_available = MICO_TRUE;
details_out->permissions_available = MICO_FALSE;
return kNoErr;
}
/* Close a file within a FatFS filesystem */
static OSStatus fatfs_file_close( mico_file_t* file_handle )
{
FRESULT fatfs_result;
fatfs_result = f_close( &file_handle->data.fatfs );
if ( fatfs_result != FR_OK )
{
return MICO_FILESYSTEM_ERROR;
}
return kNoErr;
}
/* Delete a file within a FatFS filesystem */
static OSStatus fatfs_file_delete( mico_filesystem_t* fs_handle, const char* filename )
{
FRESULT fatfs_result;
/* Change default drive to the requested drive */
fatfs_result = f_chdrive( (TCHAR*) &fs_handle->data.fatfs.drive_id );
if ( fatfs_result != FR_OK )
{
return MICO_FILESYSTEM_ERROR;
}
/* Delete the file */
fatfs_result = f_unlink( filename );
if ( fatfs_result != FR_OK )
{
return MICO_FILESYSTEM_ERROR;
}
return kNoErr;
}
/* Seek to a location in an open file within a FatFS filesystem */
static OSStatus fatfs_file_seek( mico_file_t* file_handle, int64_t offset, mico_filesystem_seek_type_t whence )
{
FRESULT fatfs_result;
FATFS_DWORD new_location;
FATFS_DWORD file_size = f_size( &file_handle->data.fatfs );
/* Translate mico "whence" to an absolute location, since FatFS does not do relative seeks */
switch ( whence )
{
case MICO_FILESYSTEM_SEEK_SET:
new_location = offset;
break;
case MICO_FILESYSTEM_SEEK_CUR:
new_location = f_tell( &file_handle->data.fatfs ) + offset;
break;
case MICO_FILESYSTEM_SEEK_END:
new_location = file_size + offset;
break;
default:
return kParamErr;
}
if ( new_location < 0 )
{
return kParamErr;
}
/* Perform the seek */
fatfs_result = f_lseek( &file_handle->data.fatfs, new_location );
if ( fatfs_result != FR_OK )
{
return MICO_FILESYSTEM_ERROR;
}
return kNoErr;
}
/* Get the current location in an open file within a FatFS filesystem */
static OSStatus fatfs_file_tell( mico_file_t* file_handle, uint64_t* location )
{
*location = f_tell( &file_handle->data.fatfs );
return kNoErr;
}
/* Read data from an open file within a FatFS filesystem */
static OSStatus fatfs_file_read( mico_file_t* file_handle, void* data, uint64_t bytes_to_read,
uint64_t* returned_bytes_count )
{
FRESULT fatfs_result;
FATFS_UINT bytes_read;
fatfs_result = f_read( &file_handle->data.fatfs, data, bytes_to_read, &bytes_read );
if ( fatfs_result != FR_OK )
{
return MICO_FILESYSTEM_ERROR;
}
*returned_bytes_count = bytes_read;
return kNoErr;
}
/* Write data to an open file within a FatFS filesystem */
static OSStatus fatfs_file_write( mico_file_t* file_handle, const void* data, uint64_t bytes_to_write,
uint64_t* written_bytes_count )
{
FRESULT fatfs_result;
FATFS_UINT bytes_written;
fatfs_result = f_write( &file_handle->data.fatfs, data, bytes_to_write, &bytes_written );
if ( fatfs_result != FR_OK )
{
return MICO_FILESYSTEM_ERROR;
}
*written_bytes_count = bytes_written;
return kNoErr;
}
/* Flush unwritten data in an open file within a FatFS filesystem */
static OSStatus fatfs_file_flush( mico_file_t* file_handle )
{
FRESULT fatfs_result;
fatfs_result = f_sync( &file_handle->data.fatfs );
if ( fatfs_result != FR_OK )
{
return MICO_FILESYSTEM_ERROR;
}
return kNoErr;
}
/* Get end-of-file (EOF) flag for an open file within a FatFS filesystem */
static int fatfs_file_end_reached( mico_file_t* file_handle )
{
return f_eof( &file_handle->data.fatfs );
}
/* Opens a directory within a FatFS filesystem */
static OSStatus fatfs_dir_open( mico_filesystem_t* fs_handle, mico_dir_t* dir_handle, const char* dir_name )
{
FRESULT fatfs_result;
/* Change default drive to the requested drive */
fatfs_result = f_chdrive( (TCHAR*) &fs_handle->data.fatfs.drive_id );
if ( fatfs_result != FR_OK )
{
return MICO_FILESYSTEM_ERROR;
}
fatfs_result = f_opendir( &dir_handle->data.fatfs.handle, (TCHAR*) dir_name );
if ( fatfs_result != FR_OK )
{
return MICO_FILESYSTEM_ERROR;
}
dir_handle->data.fatfs.eodir = MICO_FALSE;
return kNoErr;
}
/* Reads directory entry from an open within a FatFS filesystem */
static OSStatus fatfs_dir_read( mico_dir_t* dir_handle, char* name_buffer, unsigned int name_buffer_length,
mico_dir_entry_type_t* type, mico_dir_entry_details_t* details )
{
FRESULT fatfs_result;
FILINFO info;
#if _USE_LFN
info.lfname = name_buffer;
info.lfsize = name_buffer_length;
#endif /* if _USE_LFN */
fatfs_result = f_readdir( &dir_handle->data.fatfs.handle, &info );
if ( fatfs_result != FR_OK )
{
return MICO_FILESYSTEM_ERROR;
}
/* Zero length filename indicates end of directory entries */
if ( info.fname[0] == '\x00' )
{
dir_handle->data.fatfs.eodir = MICO_TRUE;
return MICO_FILESYSTEM_END_OF_RESOURCE;
}
/* Copy the directory entry name to the output buffer */
#if _USE_LFN
if ( name_buffer[0] == '\x00' ) /* Indicates that the filename is only in the short filename part */
{
strlcpy( name_buffer, info.fname, MIN( name_buffer_length, 13 ) );
}
#else
strlcpy( name_buffer, info.fname, MIN( name_buffer_length, 13 ) );
#endif /* _USE_LFN */
/* Copy the directory entry details to the detail structure */
details->size = info.fsize;
details->attributes = info.fattrib;
details->date_time = (info.fdate << 16) + info.ftime; /* TODO: This is wrong - need to do conversion */
details->attributes_available = MICO_TRUE;
details->date_time_available = MICO_TRUE;
details->permissions_available = MICO_FALSE;
*type = ((details->attributes & AM_DIR) != 0) ? MICO_FILESYSTEM_DIR : MICO_FILESYSTEM_FILE;
return kNoErr;
}
/* Get end-of-directory flag for an open directory within a FatFS filesystem */
static int fatfs_dir_end_reached( mico_dir_t* dir_handle )
{
return dir_handle->data.fatfs.eodir;
}
/* Moves the current location within a directory back to the first entry within a FatFS filesystem */
static OSStatus fatfs_dir_rewind( mico_dir_t* dir_handle )
{
FRESULT fatfs_result;
/* Rewind is achieved by passing NULL to f_readdir */
fatfs_result = f_readdir( &dir_handle->data.fatfs.handle, NULL );
if ( fatfs_result != FR_OK )
{
return MICO_FILESYSTEM_ERROR;
}
dir_handle->data.fatfs.eodir = MICO_FALSE;
return kNoErr;
}
/* Closes an open directory within a FatFS filesystem */
static OSStatus fatfs_dir_close( mico_dir_t* dir_handle )
{
FRESULT fatfs_result;
fatfs_result = f_closedir( &dir_handle->data.fatfs.handle );
if ( fatfs_result != FR_OK )
{
return MICO_FILESYSTEM_ERROR;
}
return kNoErr;
}
/* Creates a new directory within a FatFS filesystem */
static OSStatus fatfs_dir_create( mico_filesystem_t* fs_handle, const char* directory_name )
{
FRESULT fatfs_result;
/* Change default drive to the requested drive */
fatfs_result = f_chdrive( (TCHAR*) &fs_handle->data.fatfs.drive_id );
if ( fatfs_result != FR_OK )
{
return MICO_FILESYSTEM_ERROR;
}
/* Create the directory */
fatfs_result = f_mkdir( directory_name );
if ( (fatfs_result != FR_OK) && (fatfs_result != FR_EXIST) )
{
return MICO_FILESYSTEM_ERROR;
}
return kNoErr;
}
static OSStatus fatfs_get_info( mico_filesystem_info* info,char* mounted_name )
{
FATFS *fs;
uint8_t res;
unsigned long fre_clust = 0, fre_sect = 0, tot_sect = 0;
memset( info, 0x0, sizeof(mico_filesystem_info) );
res = f_getfree( (const TCHAR*) mounted_name, &fre_clust, &fs );
if ( res == 0 )
{
tot_sect = (fs->n_fatent - 2) * fs->csize;
fre_sect = fre_clust * fs->csize;
#if _MAX_SS!=512
tot_sect *= fs->ssize / 512;
fre_sect *= fs->ssize / 512;
#endif
if ( tot_sect < 20480 )
{
/* Print free space in unit of KB (assuming 512 bytes/sector) */
info->total_space = tot_sect >> 1;
info->free_space = fre_sect >> 1;
} else
{
/* Print free space in unit of MB (assuming 512 bytes/sector) */
info->total_space = tot_sect >> 11;
info->free_space = fre_sect >> 11;
}
}
return kNoErr;
}
static OSStatus fatfs_scan_files( char* mounted_name, mico_scan_file_handle arg )
{
FRESULT res;
FILINFO fno;
FATFS_DIR dir;
char *fn; /* This function is assuming non-Unicode cfg. */
#if _USE_LFN
static char lfn[_MAX_LFN + 1]; /* Buffer to store the LFN */
fno.lfname = lfn;
fno.lfsize = sizeof lfn;
#endif
res = f_opendir(&dir, mounted_name); /* Open the directory */
if (res == FR_OK) {
for (;;) {
res = f_readdir(&dir, &fno); /* Read a directory item */
if (res != FR_OK || fno.fname[0] == 0) break; /* Break on error or end of dir */
if (fno.fname[0] == '.') continue; /* Ignore dot entry */
#if _USE_LFN
fn = *fno.lfname ? fno.lfname : fno.fname;
#else
fn = fno.fname;
#endif
arg( mounted_name,fn );
}
f_closedir(&dir);
}
return res;
}