2017-03-25 22:03:19 +01:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// FAT16/32 File IO Library
|
|
|
|
// V2.6
|
|
|
|
// Ultra-Embedded.com
|
|
|
|
// Copyright 2003 - 2012
|
|
|
|
//
|
|
|
|
// Email: admin@ultra-embedded.com
|
|
|
|
//
|
|
|
|
// License: GPL
|
|
|
|
// If you would like a version with a more permissive license for use in
|
|
|
|
// closed source commercial applications please contact me for details.
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
//
|
|
|
|
// This file is part of FAT File IO Library.
|
|
|
|
//
|
|
|
|
// FAT File IO Library is free software; you can redistribute it and/or modify
|
|
|
|
// it under the terms of the GNU General Public License as published by
|
|
|
|
// the Free Software Foundation; either version 2 of the License, or
|
|
|
|
// (at your option) any later version.
|
|
|
|
//
|
|
|
|
// FAT File IO Library is distributed in the hope that it will be useful,
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
// GNU General Public License for more details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU General Public License
|
|
|
|
// along with FAT File IO Library; if not, write to the Free Software
|
|
|
|
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#include "../imports.h"
|
|
|
|
#include "fat_defs.h"
|
|
|
|
#include "fat_access.h"
|
|
|
|
#include "fat_table.h"
|
|
|
|
#include "fat_write.h"
|
|
|
|
#include "fat_misc.h"
|
|
|
|
#include "fat_string.h"
|
|
|
|
#include "fat_filelib.h"
|
|
|
|
#include "fat_cache.h"
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Locals
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
static FL_FILE _files[FATFS_MAX_OPEN_FILES];
|
|
|
|
static int _filelib_init = 0;
|
|
|
|
static int _filelib_valid = 0;
|
|
|
|
static struct fatfs _fs;
|
|
|
|
static struct fat_list _open_file_list;
|
|
|
|
static struct fat_list _free_file_list;
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Macros
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
// Macro for checking if file lib is initialised
|
|
|
|
#define CHECK_FL_INIT() { if (_filelib_init==0) fl_init(); }
|
|
|
|
|
|
|
|
#define FL_LOCK(a) do { if ((a)->fl_lock) (a)->fl_lock(); } while (0)
|
|
|
|
#define FL_UNLOCK(a) do { if ((a)->fl_unlock) (a)->fl_unlock(); } while (0)
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Local Functions
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
//static void _fl_init();
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// _allocate_file: Find a slot in the open files buffer for a new file
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
static FL_FILE* _allocate_file(void)
|
|
|
|
{
|
|
|
|
// Allocate free file
|
|
|
|
struct fat_node *node = fat_list_pop_head(&_free_file_list);
|
|
|
|
|
|
|
|
// Add to open list
|
|
|
|
if (node)
|
|
|
|
fat_list_insert_last(&_open_file_list, node);
|
|
|
|
|
|
|
|
return fat_list_entry(node, FL_FILE, list_node);
|
|
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// _check_file_open: Returns true if the file is already open
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
static int _check_file_open(FL_FILE* file)
|
|
|
|
{
|
|
|
|
struct fat_node *node;
|
|
|
|
|
|
|
|
// Compare open files
|
|
|
|
fat_list_for_each(&_open_file_list, node)
|
|
|
|
{
|
|
|
|
FL_FILE* openFile = fat_list_entry(node, FL_FILE, list_node);
|
|
|
|
|
|
|
|
// If not the current file
|
|
|
|
if (openFile != file)
|
|
|
|
{
|
|
|
|
// Compare path and name
|
|
|
|
if ( (fatfs_compare_names(openFile->path,file->path)) && (fatfs_compare_names(openFile->filename,file->filename)) )
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// _free_file: Free open file handle
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
static void _free_file(FL_FILE* file)
|
|
|
|
{
|
|
|
|
// Remove from open list
|
|
|
|
fat_list_remove(&_open_file_list, &file->list_node);
|
|
|
|
|
|
|
|
// Add to free list
|
|
|
|
fat_list_insert_last(&_free_file_list, &file->list_node);
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Low Level
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// _open_directory: Cycle through path string to find the start cluster
|
|
|
|
// address of the highest subdir.
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
static int _open_directory(char *path, uint32 *pathCluster)
|
|
|
|
{
|
|
|
|
int levels;
|
|
|
|
int sublevel;
|
|
|
|
char currentfolder[FATFS_MAX_LONG_FILENAME];
|
|
|
|
struct fat_dir_entry sfEntry;
|
|
|
|
uint32 startcluster;
|
|
|
|
|
|
|
|
// Set starting cluster to root cluster
|
|
|
|
startcluster = fatfs_get_root_cluster(&_fs);
|
|
|
|
|
|
|
|
// Find number of levels
|
|
|
|
levels = fatfs_total_path_levels(path);
|
|
|
|
|
|
|
|
// Cycle through each level and get the start sector
|
|
|
|
for (sublevel=0;sublevel<(levels+1);sublevel++)
|
|
|
|
{
|
|
|
|
if (fatfs_get_substring(path, sublevel, currentfolder, sizeof(currentfolder)) == -1)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
// Find clusteraddress for folder (currentfolder)
|
|
|
|
if (fatfs_get_file_entry(&_fs, startcluster, currentfolder,&sfEntry))
|
|
|
|
{
|
|
|
|
// Check entry is folder
|
|
|
|
if (fatfs_entry_is_dir(&sfEntry))
|
|
|
|
startcluster = ((FAT_HTONS((uint32)sfEntry.FstClusHI))<<16) + FAT_HTONS(sfEntry.FstClusLO);
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
*pathCluster = startcluster;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// _create_directory: Cycle through path string and create the end directory
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#if FATFS_INC_WRITE_SUPPORT
|
|
|
|
static int _create_directory(char *path)
|
|
|
|
{
|
|
|
|
FL_FILE* file;
|
|
|
|
struct fat_dir_entry sfEntry;
|
|
|
|
char shortFilename[FAT_SFN_SIZE_FULL];
|
|
|
|
int tailNum = 0;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
// Allocate a new file handle
|
|
|
|
file = _allocate_file();
|
|
|
|
if (!file)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
// Clear filename
|
|
|
|
memset(file->path, '\0', sizeof(file->path));
|
|
|
|
memset(file->filename, '\0', sizeof(file->filename));
|
|
|
|
|
|
|
|
// Split full path into filename and directory path
|
|
|
|
if (fatfs_split_path((char*)path, file->path, sizeof(file->path), file->filename, sizeof(file->filename)) == -1)
|
|
|
|
{
|
|
|
|
_free_file(file);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if file already open
|
|
|
|
if (_check_file_open(file))
|
|
|
|
{
|
|
|
|
_free_file(file);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If file is in the root dir
|
|
|
|
if (file->path[0] == 0)
|
|
|
|
file->parentcluster = fatfs_get_root_cluster(&_fs);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Find parent directory start cluster
|
|
|
|
if (!_open_directory(file->path, &file->parentcluster))
|
|
|
|
{
|
|
|
|
_free_file(file);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if same filename exists in directory
|
|
|
|
if (fatfs_get_file_entry(&_fs, file->parentcluster, file->filename,&sfEntry) == 1)
|
|
|
|
{
|
|
|
|
_free_file(file);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
file->startcluster = 0;
|
|
|
|
|
|
|
|
// Create the file space for the folder (at least one clusters worth!)
|
|
|
|
if (!fatfs_allocate_free_space(&_fs, 1, &file->startcluster, 1))
|
|
|
|
{
|
|
|
|
_free_file(file);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Erase new directory cluster
|
|
|
|
memset(file->file_data_sector, 0x00, FAT_SECTOR_SIZE);
|
|
|
|
for (i=0;i<_fs.sectors_per_cluster;i++)
|
|
|
|
{
|
|
|
|
if (!fatfs_write_sector(&_fs, file->startcluster, i, file->file_data_sector))
|
|
|
|
{
|
|
|
|
_free_file(file);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#if FATFS_INC_LFN_SUPPORT
|
|
|
|
|
|
|
|
// Generate a short filename & tail
|
|
|
|
tailNum = 0;
|
|
|
|
do
|
|
|
|
{
|
|
|
|
// Create a standard short filename (without tail)
|
|
|
|
fatfs_lfn_create_sfn(shortFilename, file->filename);
|
|
|
|
|
|
|
|
// If second hit or more, generate a ~n tail
|
|
|
|
if (tailNum != 0)
|
|
|
|
fatfs_lfn_generate_tail((char*)file->shortfilename, shortFilename, tailNum);
|
|
|
|
// Try with no tail if first entry
|
|
|
|
else
|
|
|
|
memcpy(file->shortfilename, shortFilename, FAT_SFN_SIZE_FULL);
|
|
|
|
|
|
|
|
// Check if entry exists already or not
|
|
|
|
if (fatfs_sfn_exists(&_fs, file->parentcluster, (char*)file->shortfilename) == 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
tailNum++;
|
|
|
|
}
|
|
|
|
while (tailNum < 9999);
|
|
|
|
|
|
|
|
// We reached the max number of duplicate short file names (unlikely!)
|
|
|
|
if (tailNum == 9999)
|
|
|
|
{
|
|
|
|
// Delete allocated space
|
|
|
|
fatfs_free_cluster_chain(&_fs, file->startcluster);
|
|
|
|
|
|
|
|
_free_file(file);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
// Create a standard short filename (without tail)
|
|
|
|
if (!fatfs_lfn_create_sfn(shortFilename, file->filename))
|
|
|
|
{
|
|
|
|
// Delete allocated space
|
|
|
|
fatfs_free_cluster_chain(&_fs, file->startcluster);
|
|
|
|
|
|
|
|
_free_file(file);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Copy to SFN space
|
|
|
|
memcpy(file->shortfilename, shortFilename, FAT_SFN_SIZE_FULL);
|
|
|
|
|
|
|
|
// Check if entry exists already
|
|
|
|
if (fatfs_sfn_exists(&_fs, file->parentcluster, (char*)file->shortfilename))
|
|
|
|
{
|
|
|
|
// Delete allocated space
|
|
|
|
fatfs_free_cluster_chain(&_fs, file->startcluster);
|
|
|
|
|
|
|
|
_free_file(file);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Add file to disk
|
|
|
|
if (!fatfs_add_file_entry(&_fs, file->parentcluster, (char*)file->filename, (char*)file->shortfilename, file->startcluster, 0, 1))
|
|
|
|
{
|
|
|
|
// Delete allocated space
|
|
|
|
fatfs_free_cluster_chain(&_fs, file->startcluster);
|
|
|
|
|
|
|
|
_free_file(file);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// General
|
|
|
|
file->filelength = 0;
|
|
|
|
file->bytenum = 0;
|
|
|
|
file->file_data_address = 0xFFFFFFFF;
|
|
|
|
file->file_data_dirty = 0;
|
|
|
|
file->filelength_changed = 0;
|
|
|
|
|
|
|
|
// Quick lookup for next link in the chain
|
|
|
|
file->last_fat_lookup.ClusterIdx = 0xFFFFFFFF;
|
|
|
|
file->last_fat_lookup.CurrentCluster = 0xFFFFFFFF;
|
|
|
|
|
|
|
|
fatfs_fat_purge(&_fs);
|
|
|
|
|
|
|
|
_free_file(file);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// _open_file: Open a file for reading
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
static FL_FILE* _open_file(const char *path)
|
|
|
|
{
|
|
|
|
FL_FILE* file;
|
|
|
|
struct fat_dir_entry sfEntry;
|
|
|
|
|
|
|
|
// Allocate a new file handle
|
|
|
|
file = _allocate_file();
|
|
|
|
if (!file)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
// Clear filename
|
|
|
|
memset(file->path, '\0', sizeof(file->path));
|
|
|
|
memset(file->filename, '\0', sizeof(file->filename));
|
|
|
|
|
|
|
|
// Split full path into filename and directory path
|
|
|
|
if (fatfs_split_path((char*)path, file->path, sizeof(file->path), file->filename, sizeof(file->filename)) == -1)
|
|
|
|
{
|
|
|
|
_free_file(file);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if file already open
|
|
|
|
if (_check_file_open(file))
|
|
|
|
{
|
|
|
|
_free_file(file);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If file is in the root dir
|
|
|
|
if (file->path[0]==0)
|
|
|
|
file->parentcluster = fatfs_get_root_cluster(&_fs);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Find parent directory start cluster
|
|
|
|
if (!_open_directory(file->path, &file->parentcluster))
|
|
|
|
{
|
|
|
|
_free_file(file);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Using dir cluster address search for filename
|
|
|
|
if (fatfs_get_file_entry(&_fs, file->parentcluster, file->filename,&sfEntry))
|
|
|
|
// Make sure entry is file not dir!
|
|
|
|
if (fatfs_entry_is_file(&sfEntry))
|
|
|
|
{
|
|
|
|
// Initialise file details
|
|
|
|
memcpy(file->shortfilename, sfEntry.Name, FAT_SFN_SIZE_FULL);
|
|
|
|
file->filelength = FAT_HTONL(sfEntry.FileSize);
|
|
|
|
file->bytenum = 0;
|
|
|
|
file->startcluster = ((FAT_HTONS((uint32)sfEntry.FstClusHI))<<16) + FAT_HTONS(sfEntry.FstClusLO);
|
|
|
|
file->file_data_address = 0xFFFFFFFF;
|
|
|
|
file->file_data_dirty = 0;
|
|
|
|
file->filelength_changed = 0;
|
|
|
|
|
|
|
|
// Quick lookup for next link in the chain
|
|
|
|
file->last_fat_lookup.ClusterIdx = 0xFFFFFFFF;
|
|
|
|
file->last_fat_lookup.CurrentCluster = 0xFFFFFFFF;
|
|
|
|
|
|
|
|
fatfs_cache_init(&_fs, file);
|
|
|
|
|
|
|
|
fatfs_fat_purge(&_fs);
|
|
|
|
|
|
|
|
return file;
|
|
|
|
}
|
|
|
|
|
|
|
|
_free_file(file);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// _create_file: Create a new file
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#if FATFS_INC_WRITE_SUPPORT
|
|
|
|
static FL_FILE* _create_file(const char *filename)
|
|
|
|
{
|
|
|
|
FL_FILE* file;
|
|
|
|
struct fat_dir_entry sfEntry;
|
|
|
|
char shortFilename[FAT_SFN_SIZE_FULL];
|
|
|
|
int tailNum = 0;
|
|
|
|
|
|
|
|
// No write access?
|
|
|
|
if (!_fs.disk_io.write_media)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
// Allocate a new file handle
|
|
|
|
file = _allocate_file();
|
|
|
|
if (!file)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
// Clear filename
|
|
|
|
memset(file->path, '\0', sizeof(file->path));
|
|
|
|
memset(file->filename, '\0', sizeof(file->filename));
|
|
|
|
|
|
|
|
// Split full path into filename and directory path
|
|
|
|
if (fatfs_split_path((char*)filename, file->path, sizeof(file->path), file->filename, sizeof(file->filename)) == -1)
|
|
|
|
{
|
|
|
|
_free_file(file);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if file already open
|
|
|
|
if (_check_file_open(file))
|
|
|
|
{
|
|
|
|
_free_file(file);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If file is in the root dir
|
|
|
|
if (file->path[0] == 0)
|
|
|
|
file->parentcluster = fatfs_get_root_cluster(&_fs);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Find parent directory start cluster
|
|
|
|
if (!_open_directory(file->path, &file->parentcluster))
|
|
|
|
{
|
|
|
|
_free_file(file);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if same filename exists in directory
|
|
|
|
if (fatfs_get_file_entry(&_fs, file->parentcluster, file->filename,&sfEntry) == 1)
|
|
|
|
{
|
|
|
|
_free_file(file);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
file->startcluster = 0;
|
|
|
|
|
|
|
|
// Create the file space for the file (at least one clusters worth!)
|
|
|
|
if (!fatfs_allocate_free_space(&_fs, 1, &file->startcluster, 1))
|
|
|
|
{
|
|
|
|
_free_file(file);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if FATFS_INC_LFN_SUPPORT
|
|
|
|
// Generate a short filename & tail
|
|
|
|
tailNum = 0;
|
|
|
|
do
|
|
|
|
{
|
|
|
|
// Create a standard short filename (without tail)
|
|
|
|
fatfs_lfn_create_sfn(shortFilename, file->filename);
|
|
|
|
|
|
|
|
// If second hit or more, generate a ~n tail
|
|
|
|
if (tailNum != 0)
|
|
|
|
fatfs_lfn_generate_tail((char*)file->shortfilename, shortFilename, tailNum);
|
|
|
|
// Try with no tail if first entry
|
|
|
|
else
|
|
|
|
memcpy(file->shortfilename, shortFilename, FAT_SFN_SIZE_FULL);
|
|
|
|
|
|
|
|
// Check if entry exists already or not
|
|
|
|
if (fatfs_sfn_exists(&_fs, file->parentcluster, (char*)file->shortfilename) == 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
tailNum++;
|
|
|
|
}
|
|
|
|
while (tailNum < 9999);
|
|
|
|
|
|
|
|
// We reached the max number of duplicate short file names (unlikely!)
|
|
|
|
if (tailNum == 9999)
|
|
|
|
{
|
|
|
|
// Delete allocated space
|
|
|
|
fatfs_free_cluster_chain(&_fs, file->startcluster);
|
|
|
|
|
|
|
|
_free_file(file);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
// Create a standard short filename (without tail)
|
|
|
|
if (!fatfs_lfn_create_sfn(shortFilename, file->filename))
|
|
|
|
{
|
|
|
|
// Delete allocated space
|
|
|
|
fatfs_free_cluster_chain(&_fs, file->startcluster);
|
|
|
|
|
|
|
|
_free_file(file);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Copy to SFN space
|
|
|
|
memcpy(file->shortfilename, shortFilename, FAT_SFN_SIZE_FULL);
|
|
|
|
|
|
|
|
// Check if entry exists already
|
|
|
|
if (fatfs_sfn_exists(&_fs, file->parentcluster, (char*)file->shortfilename))
|
|
|
|
{
|
|
|
|
// Delete allocated space
|
|
|
|
fatfs_free_cluster_chain(&_fs, file->startcluster);
|
|
|
|
|
|
|
|
_free_file(file);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Add file to disk
|
|
|
|
if (!fatfs_add_file_entry(&_fs, file->parentcluster, (char*)file->filename, (char*)file->shortfilename, file->startcluster, 0, 0))
|
|
|
|
{
|
|
|
|
// Delete allocated space
|
|
|
|
fatfs_free_cluster_chain(&_fs, file->startcluster);
|
|
|
|
|
|
|
|
_free_file(file);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// General
|
|
|
|
file->filelength = 0;
|
|
|
|
file->bytenum = 0;
|
|
|
|
file->file_data_address = 0xFFFFFFFF;
|
|
|
|
file->file_data_dirty = 0;
|
|
|
|
file->filelength_changed = 0;
|
|
|
|
|
|
|
|
// Quick lookup for next link in the chain
|
|
|
|
file->last_fat_lookup.ClusterIdx = 0xFFFFFFFF;
|
|
|
|
file->last_fat_lookup.CurrentCluster = 0xFFFFFFFF;
|
|
|
|
|
|
|
|
fatfs_cache_init(&_fs, file);
|
|
|
|
|
|
|
|
fatfs_fat_purge(&_fs);
|
|
|
|
|
|
|
|
return file;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// _read_sectors: Read sector(s) from disk to file
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
static uint32 _read_sectors(FL_FILE* file, uint32 offset, uint8 *buffer, uint32 count)
|
|
|
|
{
|
|
|
|
uint32 Sector = 0;
|
|
|
|
uint32 ClusterIdx = 0;
|
|
|
|
uint32 Cluster = 0;
|
|
|
|
uint32 i;
|
|
|
|
uint32 lba;
|
|
|
|
|
|
|
|
// Find cluster index within file & sector with cluster
|
|
|
|
ClusterIdx = offset / _fs.sectors_per_cluster;
|
|
|
|
Sector = offset - (ClusterIdx * _fs.sectors_per_cluster);
|
|
|
|
|
|
|
|
// Limit number of sectors read to the number remaining in this cluster
|
|
|
|
if ((Sector + count) > _fs.sectors_per_cluster)
|
|
|
|
count = _fs.sectors_per_cluster - Sector;
|
|
|
|
|
|
|
|
// Quick lookup for next link in the chain
|
|
|
|
if (ClusterIdx == file->last_fat_lookup.ClusterIdx)
|
|
|
|
Cluster = file->last_fat_lookup.CurrentCluster;
|
|
|
|
// Else walk the chain
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Starting from last recorded cluster?
|
|
|
|
if (ClusterIdx && ClusterIdx == file->last_fat_lookup.ClusterIdx + 1)
|
|
|
|
{
|
|
|
|
i = file->last_fat_lookup.ClusterIdx;
|
|
|
|
Cluster = file->last_fat_lookup.CurrentCluster;
|
|
|
|
}
|
|
|
|
// Start searching from the beginning..
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Set start of cluster chain to initial value
|
|
|
|
i = 0;
|
|
|
|
Cluster = file->startcluster;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Follow chain to find cluster to read
|
|
|
|
for ( ;i<ClusterIdx; i++)
|
|
|
|
{
|
|
|
|
uint32 nextCluster;
|
|
|
|
|
|
|
|
// Does the entry exist in the cache?
|
|
|
|
if (!fatfs_cache_get_next_cluster(&_fs, file, i, &nextCluster))
|
|
|
|
{
|
|
|
|
// Scan file linked list to find next entry
|
|
|
|
nextCluster = fatfs_find_next_cluster(&_fs, Cluster);
|
|
|
|
|
|
|
|
// Push entry into cache
|
|
|
|
fatfs_cache_set_next_cluster(&_fs, file, i, nextCluster);
|
|
|
|
}
|
|
|
|
|
|
|
|
Cluster = nextCluster;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Record current cluster lookup details (if valid)
|
|
|
|
if (Cluster != FAT32_LAST_CLUSTER)
|
|
|
|
{
|
|
|
|
file->last_fat_lookup.CurrentCluster = Cluster;
|
|
|
|
file->last_fat_lookup.ClusterIdx = ClusterIdx;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If end of cluster chain then return false
|
|
|
|
if (Cluster == FAT32_LAST_CLUSTER)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
// Calculate sector address
|
|
|
|
lba = fatfs_lba_of_cluster(&_fs, Cluster) + Sector;
|
|
|
|
|
|
|
|
// Read sector of file
|
|
|
|
if (fatfs_sector_read(&_fs, lba, buffer, count))
|
|
|
|
return count;
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// External API
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// fl_init: Initialise library
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void fl_init(void)
|
|
|
|
{
|
|
|
|
int i;
|
2017-03-27 22:04:19 +02:00
|
|
|
memset(_files, 0, sizeof(_files));
|
|
|
|
memset(&_filelib_init, 0, sizeof(_filelib_init));
|
|
|
|
memset(&_filelib_valid, 0, sizeof(_filelib_valid));
|
|
|
|
memset(&_fs, 0, sizeof(_fs));
|
|
|
|
memset(&_open_file_list, 0, sizeof(_open_file_list));
|
|
|
|
memset(&_free_file_list, 0, sizeof(_free_file_list));
|
2017-03-25 22:03:19 +01:00
|
|
|
|
|
|
|
fat_list_init(&_free_file_list);
|
|
|
|
fat_list_init(&_open_file_list);
|
|
|
|
|
|
|
|
// Add all file objects to free list
|
|
|
|
for (i=0;i<FATFS_MAX_OPEN_FILES;i++)
|
|
|
|
fat_list_insert_last(&_free_file_list, &_files[i].list_node);
|
|
|
|
|
|
|
|
_filelib_init = 1;
|
|
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// fl_attach_locks:
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void fl_attach_locks(void (*lock)(void), void (*unlock)(void))
|
|
|
|
{
|
|
|
|
_fs.fl_lock = lock;
|
|
|
|
_fs.fl_unlock = unlock;
|
|
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// fl_attach_media:
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
int fl_attach_media(fn_diskio_read rd, fn_diskio_write wr)
|
|
|
|
{
|
|
|
|
int res;
|
|
|
|
|
|
|
|
// If first call to library, initialise
|
|
|
|
CHECK_FL_INIT();
|
|
|
|
|
|
|
|
_fs.disk_io.read_media = rd;
|
|
|
|
_fs.disk_io.write_media = wr;
|
|
|
|
|
|
|
|
// Initialise FAT parameters
|
|
|
|
if ((res = fatfs_init(&_fs)) != FAT_INIT_OK)
|
|
|
|
{
|
|
|
|
FAT_PRINTF(("FAT_FS: Error could not load FAT details (%d)!\r\n", res));
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
_filelib_valid = 1;
|
|
|
|
return FAT_INIT_OK;
|
|
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// fl_shutdown: Call before shutting down system
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void fl_shutdown(void)
|
|
|
|
{
|
|
|
|
// If first call to library, initialise
|
|
|
|
CHECK_FL_INIT();
|
|
|
|
|
|
|
|
FL_LOCK(&_fs);
|
|
|
|
fatfs_fat_purge(&_fs);
|
|
|
|
FL_UNLOCK(&_fs);
|
|
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// fopen: Open or Create a file for reading or writing
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void* fl_fopen(const char *path, const char *mode)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
FL_FILE* file;
|
|
|
|
uint8 flags = 0;
|
|
|
|
|
|
|
|
// If first call to library, initialise
|
|
|
|
CHECK_FL_INIT();
|
|
|
|
|
|
|
|
if (!_filelib_valid)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (!path || !mode)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
// Supported Modes:
|
|
|
|
// "r" Open a file for reading.
|
|
|
|
// The file must exist.
|
|
|
|
// "w" Create an empty file for writing.
|
|
|
|
// If a file with the same name already exists its content is erased and the file is treated as a new empty file.
|
|
|
|
// "a" Append to a file.
|
|
|
|
// Writing operations append data at the end of the file.
|
|
|
|
// The file is created if it does not exist.
|
|
|
|
// "r+" Open a file for update both reading and writing.
|
|
|
|
// The file must exist.
|
|
|
|
// "w+" Create an empty file for both reading and writing.
|
|
|
|
// If a file with the same name already exists its content is erased and the file is treated as a new empty file.
|
|
|
|
// "a+" Open a file for reading and appending.
|
|
|
|
// All writing operations are performed at the end of the file, protecting the previous content to be overwritten.
|
|
|
|
// You can reposition (fseek, rewind) the internal pointer to anywhere in the file for reading, but writing operations
|
|
|
|
// will move it back to the end of file.
|
|
|
|
// The file is created if it does not exist.
|
|
|
|
|
|
|
|
for (i=0;i<(int)strlen(mode);i++)
|
|
|
|
{
|
|
|
|
switch (mode[i])
|
|
|
|
{
|
|
|
|
case 'r':
|
|
|
|
case 'R':
|
|
|
|
flags |= FILE_READ;
|
|
|
|
break;
|
|
|
|
case 'w':
|
|
|
|
case 'W':
|
|
|
|
flags |= FILE_WRITE;
|
|
|
|
flags |= FILE_ERASE;
|
|
|
|
flags |= FILE_CREATE;
|
|
|
|
break;
|
|
|
|
case 'a':
|
|
|
|
case 'A':
|
|
|
|
flags |= FILE_WRITE;
|
|
|
|
flags |= FILE_APPEND;
|
|
|
|
flags |= FILE_CREATE;
|
|
|
|
break;
|
|
|
|
case '+':
|
|
|
|
if (flags & FILE_READ)
|
|
|
|
flags |= FILE_WRITE;
|
|
|
|
else if (flags & FILE_WRITE)
|
|
|
|
{
|
|
|
|
flags |= FILE_READ;
|
|
|
|
flags |= FILE_ERASE;
|
|
|
|
flags |= FILE_CREATE;
|
|
|
|
}
|
|
|
|
else if (flags & FILE_APPEND)
|
|
|
|
{
|
|
|
|
flags |= FILE_READ;
|
|
|
|
flags |= FILE_WRITE;
|
|
|
|
flags |= FILE_APPEND;
|
|
|
|
flags |= FILE_CREATE;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'b':
|
|
|
|
case 'B':
|
|
|
|
flags |= FILE_BINARY;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
file = NULL;
|
|
|
|
|
|
|
|
#if FATFS_INC_WRITE_SUPPORT == 0
|
|
|
|
// No write support!
|
|
|
|
flags &= ~(FILE_CREATE | FILE_WRITE | FILE_APPEND);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// No write access - remove write/modify flags
|
|
|
|
if (!_fs.disk_io.write_media)
|
|
|
|
flags &= ~(FILE_CREATE | FILE_WRITE | FILE_APPEND);
|
|
|
|
|
|
|
|
FL_LOCK(&_fs);
|
|
|
|
|
|
|
|
// Read
|
|
|
|
if (flags & FILE_READ)
|
|
|
|
file = _open_file(path);
|
|
|
|
|
|
|
|
// Create New
|
|
|
|
#if FATFS_INC_WRITE_SUPPORT
|
|
|
|
if (!file && (flags & FILE_CREATE))
|
|
|
|
file = _create_file(path);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Write Existing (and not open due to read or create)
|
|
|
|
if (!(flags & FILE_READ))
|
|
|
|
if ((flags & FILE_CREATE) && !file)
|
|
|
|
if (flags & (FILE_WRITE | FILE_APPEND))
|
|
|
|
file = _open_file(path);
|
|
|
|
|
|
|
|
if (file)
|
|
|
|
file->flags = flags;
|
|
|
|
|
|
|
|
FL_UNLOCK(&_fs);
|
|
|
|
return file;
|
|
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// _write_sectors: Write sector(s) to disk
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#if FATFS_INC_WRITE_SUPPORT
|
|
|
|
static uint32 _write_sectors(FL_FILE* file, uint32 offset, uint8 *buf, uint32 count)
|
|
|
|
{
|
|
|
|
uint32 SectorNumber = 0;
|
|
|
|
uint32 ClusterIdx = 0;
|
|
|
|
uint32 Cluster = 0;
|
|
|
|
uint32 LastCluster = FAT32_LAST_CLUSTER;
|
|
|
|
uint32 i;
|
|
|
|
uint32 lba;
|
|
|
|
uint32 TotalWriteCount = count;
|
|
|
|
|
|
|
|
// Find values for Cluster index & sector within cluster
|
|
|
|
ClusterIdx = offset / _fs.sectors_per_cluster;
|
|
|
|
SectorNumber = offset - (ClusterIdx * _fs.sectors_per_cluster);
|
|
|
|
|
|
|
|
// Limit number of sectors written to the number remaining in this cluster
|
|
|
|
if ((SectorNumber + count) > _fs.sectors_per_cluster)
|
|
|
|
count = _fs.sectors_per_cluster - SectorNumber;
|
|
|
|
|
|
|
|
// Quick lookup for next link in the chain
|
|
|
|
if (ClusterIdx == file->last_fat_lookup.ClusterIdx)
|
|
|
|
Cluster = file->last_fat_lookup.CurrentCluster;
|
|
|
|
// Else walk the chain
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Starting from last recorded cluster?
|
|
|
|
if (ClusterIdx && ClusterIdx == file->last_fat_lookup.ClusterIdx + 1)
|
|
|
|
{
|
|
|
|
i = file->last_fat_lookup.ClusterIdx;
|
|
|
|
Cluster = file->last_fat_lookup.CurrentCluster;
|
|
|
|
}
|
|
|
|
// Start searching from the beginning..
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Set start of cluster chain to initial value
|
|
|
|
i = 0;
|
|
|
|
Cluster = file->startcluster;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Follow chain to find cluster to read
|
|
|
|
for ( ;i<ClusterIdx; i++)
|
|
|
|
{
|
|
|
|
uint32 nextCluster;
|
|
|
|
|
|
|
|
// Does the entry exist in the cache?
|
|
|
|
if (!fatfs_cache_get_next_cluster(&_fs, file, i, &nextCluster))
|
|
|
|
{
|
|
|
|
// Scan file linked list to find next entry
|
|
|
|
nextCluster = fatfs_find_next_cluster(&_fs, Cluster);
|
|
|
|
|
|
|
|
// Push entry into cache
|
|
|
|
fatfs_cache_set_next_cluster(&_fs, file, i, nextCluster);
|
|
|
|
}
|
|
|
|
|
|
|
|
LastCluster = Cluster;
|
|
|
|
Cluster = nextCluster;
|
|
|
|
|
|
|
|
// Dont keep following a dead end
|
|
|
|
if (Cluster == FAT32_LAST_CLUSTER)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we have reached the end of the chain, allocate more!
|
|
|
|
if (Cluster == FAT32_LAST_CLUSTER)
|
|
|
|
{
|
|
|
|
// Add some more cluster(s) to the last good cluster chain
|
|
|
|
if (!fatfs_add_free_space(&_fs, &LastCluster, (TotalWriteCount + _fs.sectors_per_cluster -1) / _fs.sectors_per_cluster))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
Cluster = LastCluster;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Record current cluster lookup details
|
|
|
|
file->last_fat_lookup.CurrentCluster = Cluster;
|
|
|
|
file->last_fat_lookup.ClusterIdx = ClusterIdx;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Calculate write address
|
|
|
|
lba = fatfs_lba_of_cluster(&_fs, Cluster) + SectorNumber;
|
|
|
|
|
|
|
|
if (fatfs_sector_write(&_fs, lba, buf, count))
|
|
|
|
return count;
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// fl_fflush: Flush un-written data to the file
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
int fl_fflush(void *f)
|
|
|
|
{
|
|
|
|
#if FATFS_INC_WRITE_SUPPORT
|
|
|
|
FL_FILE *file = (FL_FILE *)f;
|
|
|
|
|
|
|
|
// If first call to library, initialise
|
|
|
|
CHECK_FL_INIT();
|
|
|
|
|
|
|
|
if (file)
|
|
|
|
{
|
|
|
|
FL_LOCK(&_fs);
|
|
|
|
|
|
|
|
// If some write data still in buffer
|
|
|
|
if (file->file_data_dirty)
|
|
|
|
{
|
|
|
|
// Write back current sector before loading next
|
|
|
|
if (_write_sectors(file, file->file_data_address, file->file_data_sector, 1))
|
|
|
|
file->file_data_dirty = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
FL_UNLOCK(&_fs);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// fl_fclose: Close an open file
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void fl_fclose(void *f)
|
|
|
|
{
|
|
|
|
FL_FILE *file = (FL_FILE *)f;
|
|
|
|
|
|
|
|
// If first call to library, initialise
|
|
|
|
CHECK_FL_INIT();
|
|
|
|
|
|
|
|
if (file)
|
|
|
|
{
|
|
|
|
FL_LOCK(&_fs);
|
|
|
|
|
|
|
|
// Flush un-written data to file
|
|
|
|
fl_fflush(f);
|
|
|
|
|
|
|
|
// File size changed?
|
|
|
|
if (file->filelength_changed)
|
|
|
|
{
|
|
|
|
#if FATFS_INC_WRITE_SUPPORT
|
|
|
|
// Update filesize in directory
|
|
|
|
fatfs_update_file_length(&_fs, file->parentcluster, (char*)file->shortfilename, file->filelength);
|
|
|
|
#endif
|
|
|
|
file->filelength_changed = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
file->bytenum = 0;
|
|
|
|
file->filelength = 0;
|
|
|
|
file->startcluster = 0;
|
|
|
|
file->file_data_address = 0xFFFFFFFF;
|
|
|
|
file->file_data_dirty = 0;
|
|
|
|
file->filelength_changed = 0;
|
|
|
|
|
|
|
|
// Free file handle
|
|
|
|
_free_file(file);
|
|
|
|
|
|
|
|
fatfs_fat_purge(&_fs);
|
|
|
|
|
|
|
|
FL_UNLOCK(&_fs);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// fl_fgetc: Get a character in the stream
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
int fl_fgetc(void *f)
|
|
|
|
{
|
|
|
|
int res;
|
|
|
|
uint8 data = 0;
|
|
|
|
|
|
|
|
res = fl_fread(&data, 1, 1, f);
|
|
|
|
if (res == 1)
|
|
|
|
return (int)data;
|
|
|
|
else
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// fl_fgets: Get a string from a stream
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
char *fl_fgets(char *s, int n, void *f)
|
|
|
|
{
|
|
|
|
int idx = 0;
|
|
|
|
|
|
|
|
// Space for null terminator?
|
|
|
|
if (n > 0)
|
|
|
|
{
|
|
|
|
// While space (+space for null terminator)
|
|
|
|
while (idx < (n-1))
|
|
|
|
{
|
|
|
|
int ch = fl_fgetc(f);
|
|
|
|
|
|
|
|
// EOF / Error?
|
|
|
|
if (ch < 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
// Store character read from stream
|
|
|
|
s[idx++] = (char)ch;
|
|
|
|
|
|
|
|
// End of line?
|
|
|
|
if (ch == '\n')
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (idx > 0)
|
|
|
|
s[idx] = '\0';
|
|
|
|
}
|
|
|
|
|
|
|
|
return (idx > 0) ? s : 0;
|
|
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// fl_fread: Read a block of data from the file
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
int fl_fread(void * buffer, int size, int length, void *f )
|
|
|
|
{
|
|
|
|
uint32 sector;
|
|
|
|
uint32 offset;
|
|
|
|
int copyCount;
|
|
|
|
int count = size * length;
|
|
|
|
int bytesRead = 0;
|
|
|
|
|
|
|
|
FL_FILE *file = (FL_FILE *)f;
|
|
|
|
|
|
|
|
// If first call to library, initialise
|
|
|
|
CHECK_FL_INIT();
|
|
|
|
|
|
|
|
if (buffer==NULL || file==NULL)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
// No read permissions
|
|
|
|
if (!(file->flags & FILE_READ))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
// Nothing to be done
|
|
|
|
if (!count)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
// Check if read starts past end of file
|
|
|
|
if (file->bytenum >= file->filelength)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
// Limit to file size
|
|
|
|
if ( (file->bytenum + count) > file->filelength )
|
|
|
|
count = file->filelength - file->bytenum;
|
|
|
|
|
|
|
|
// Calculate start sector
|
|
|
|
sector = file->bytenum / FAT_SECTOR_SIZE;
|
|
|
|
|
|
|
|
// Offset to start copying data from first sector
|
|
|
|
offset = file->bytenum % FAT_SECTOR_SIZE;
|
|
|
|
|
|
|
|
while (bytesRead < count)
|
|
|
|
{
|
|
|
|
// Read whole sector, read from media directly into target buffer
|
|
|
|
if ((offset == 0) && ((count - bytesRead) >= FAT_SECTOR_SIZE))
|
|
|
|
{
|
|
|
|
// Read as many sectors as possible into target buffer
|
|
|
|
uint32 sectorsRead = _read_sectors(file, sector, (uint8*)((uint8*)buffer + bytesRead), (count - bytesRead) / FAT_SECTOR_SIZE);
|
|
|
|
if (sectorsRead)
|
|
|
|
{
|
|
|
|
// We have upto one sector to copy
|
|
|
|
copyCount = FAT_SECTOR_SIZE * sectorsRead;
|
|
|
|
|
|
|
|
// Move onto next sector and reset copy offset
|
|
|
|
sector+= sectorsRead;
|
|
|
|
offset = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Do we need to re-read the sector?
|
|
|
|
if (file->file_data_address != sector)
|
|
|
|
{
|
|
|
|
// Flush un-written data to file
|
|
|
|
if (file->file_data_dirty)
|
|
|
|
fl_fflush(file);
|
|
|
|
|
|
|
|
// Get LBA of sector offset within file
|
|
|
|
if (!_read_sectors(file, sector, file->file_data_sector, 1))
|
|
|
|
// Read failed - out of range (probably)
|
|
|
|
break;
|
|
|
|
|
|
|
|
file->file_data_address = sector;
|
|
|
|
file->file_data_dirty = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// We have upto one sector to copy
|
|
|
|
copyCount = FAT_SECTOR_SIZE - offset;
|
|
|
|
|
|
|
|
// Only require some of this sector?
|
|
|
|
if (copyCount > (count - bytesRead))
|
|
|
|
copyCount = (count - bytesRead);
|
|
|
|
|
|
|
|
// Copy to application buffer
|
|
|
|
memcpy( (uint8*)((uint8*)buffer + bytesRead), (uint8*)(file->file_data_sector + offset), copyCount);
|
|
|
|
|
|
|
|
// Move onto next sector and reset copy offset
|
|
|
|
sector++;
|
|
|
|
offset = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Increase total read count
|
|
|
|
bytesRead += copyCount;
|
|
|
|
|
|
|
|
// Increment file pointer
|
|
|
|
file->bytenum += copyCount;
|
|
|
|
}
|
|
|
|
|
|
|
|
return bytesRead;
|
|
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// fl_fseek: Seek to a specific place in the file
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
int fl_fseek( void *f, long offset, int origin )
|
|
|
|
{
|
|
|
|
FL_FILE *file = (FL_FILE *)f;
|
|
|
|
int res = -1;
|
|
|
|
|
|
|
|
// If first call to library, initialise
|
|
|
|
CHECK_FL_INIT();
|
|
|
|
|
|
|
|
if (!file)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (origin == SEEK_END && offset != 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
FL_LOCK(&_fs);
|
|
|
|
|
|
|
|
// Invalidate file buffer
|
|
|
|
file->file_data_address = 0xFFFFFFFF;
|
|
|
|
file->file_data_dirty = 0;
|
|
|
|
|
|
|
|
if (origin == SEEK_SET)
|
|
|
|
{
|
|
|
|
file->bytenum = (uint32)offset;
|
|
|
|
|
|
|
|
if (file->bytenum > file->filelength)
|
|
|
|
file->bytenum = file->filelength;
|
|
|
|
|
|
|
|
res = 0;
|
|
|
|
}
|
|
|
|
else if (origin == SEEK_CUR)
|
|
|
|
{
|
|
|
|
// Positive shift
|
|
|
|
if (offset >= 0)
|
|
|
|
{
|
|
|
|
file->bytenum += offset;
|
|
|
|
|
|
|
|
if (file->bytenum > file->filelength)
|
|
|
|
file->bytenum = file->filelength;
|
|
|
|
}
|
|
|
|
// Negative shift
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Make shift positive
|
|
|
|
offset = -offset;
|
|
|
|
|
|
|
|
// Limit to negative shift to start of file
|
|
|
|
if ((uint32)offset > file->bytenum)
|
|
|
|
file->bytenum = 0;
|
|
|
|
else
|
|
|
|
file->bytenum-= offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
res = 0;
|
|
|
|
}
|
|
|
|
else if (origin == SEEK_END)
|
|
|
|
{
|
|
|
|
file->bytenum = file->filelength;
|
|
|
|
res = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
res = -1;
|
|
|
|
|
|
|
|
FL_UNLOCK(&_fs);
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// fl_fgetpos: Get the current file position
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
int fl_fgetpos(void *f , uint32 * position)
|
|
|
|
{
|
|
|
|
FL_FILE *file = (FL_FILE *)f;
|
|
|
|
|
|
|
|
if (!file)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
FL_LOCK(&_fs);
|
|
|
|
|
|
|
|
// Get position
|
|
|
|
*position = file->bytenum;
|
|
|
|
|
|
|
|
FL_UNLOCK(&_fs);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// fl_ftell: Get the current file position
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
long fl_ftell(void *f)
|
|
|
|
{
|
|
|
|
uint32 pos = 0;
|
|
|
|
|
|
|
|
fl_fgetpos(f, &pos);
|
|
|
|
|
|
|
|
return (long)pos;
|
|
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// fl_feof: Is the file pointer at the end of the stream?
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
int fl_feof(void *f)
|
|
|
|
{
|
|
|
|
FL_FILE *file = (FL_FILE *)f;
|
|
|
|
int res;
|
|
|
|
|
|
|
|
if (!file)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
FL_LOCK(&_fs);
|
|
|
|
|
|
|
|
if (file->bytenum == file->filelength)
|
|
|
|
res = EOF;
|
|
|
|
else
|
|
|
|
res = 0;
|
|
|
|
|
|
|
|
FL_UNLOCK(&_fs);
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// fl_fputc: Write a character to the stream
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#if FATFS_INC_WRITE_SUPPORT
|
|
|
|
int fl_fputc(int c, void *f)
|
|
|
|
{
|
|
|
|
uint8 data = (uint8)c;
|
|
|
|
int res;
|
|
|
|
|
|
|
|
res = fl_fwrite(&data, 1, 1, f);
|
|
|
|
if (res == 1)
|
|
|
|
return c;
|
|
|
|
else
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// fl_fwrite: Write a block of data to the stream
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#if FATFS_INC_WRITE_SUPPORT
|
|
|
|
int fl_fwrite(const void * data, int size, int count, void *f )
|
|
|
|
{
|
|
|
|
FL_FILE *file = (FL_FILE *)f;
|
|
|
|
uint32 sector;
|
|
|
|
uint32 offset;
|
|
|
|
uint32 length = (size*count);
|
|
|
|
uint8 *buffer = (uint8 *)data;
|
|
|
|
uint32 bytesWritten = 0;
|
|
|
|
uint32 copyCount;
|
|
|
|
|
|
|
|
// If first call to library, initialise
|
|
|
|
CHECK_FL_INIT();
|
|
|
|
|
|
|
|
if (!file)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
FL_LOCK(&_fs);
|
|
|
|
|
|
|
|
// No write permissions
|
|
|
|
if (!(file->flags & FILE_WRITE))
|
|
|
|
{
|
|
|
|
FL_UNLOCK(&_fs);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Append writes to end of file
|
|
|
|
if (file->flags & FILE_APPEND)
|
|
|
|
file->bytenum = file->filelength;
|
|
|
|
// Else write to current position
|
|
|
|
|
|
|
|
// Calculate start sector
|
|
|
|
sector = file->bytenum / FAT_SECTOR_SIZE;
|
|
|
|
|
|
|
|
// Offset to start copying data from first sector
|
|
|
|
offset = file->bytenum % FAT_SECTOR_SIZE;
|
|
|
|
|
|
|
|
while (bytesWritten < length)
|
|
|
|
{
|
|
|
|
// Whole sector or more to be written?
|
|
|
|
if ((offset == 0) && ((length - bytesWritten) >= FAT_SECTOR_SIZE))
|
|
|
|
{
|
|
|
|
uint32 sectorsWrote;
|
|
|
|
|
|
|
|
// Buffered sector, flush back to disk
|
|
|
|
if (file->file_data_address != 0xFFFFFFFF)
|
|
|
|
{
|
|
|
|
// Flush un-written data to file
|
|
|
|
if (file->file_data_dirty)
|
|
|
|
fl_fflush(file);
|
|
|
|
|
|
|
|
file->file_data_address = 0xFFFFFFFF;
|
|
|
|
file->file_data_dirty = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write as many sectors as possible
|
|
|
|
sectorsWrote = _write_sectors(file, sector, (uint8*)(buffer + bytesWritten), (length - bytesWritten) / FAT_SECTOR_SIZE);
|
|
|
|
copyCount = FAT_SECTOR_SIZE * sectorsWrote;
|
|
|
|
|
|
|
|
// Increase total read count
|
|
|
|
bytesWritten += copyCount;
|
|
|
|
|
|
|
|
// Increment file pointer
|
|
|
|
file->bytenum += copyCount;
|
|
|
|
|
|
|
|
// Move onto next sector and reset copy offset
|
|
|
|
sector+= sectorsWrote;
|
|
|
|
offset = 0;
|
|
|
|
|
|
|
|
if (!sectorsWrote)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// We have upto one sector to copy
|
|
|
|
copyCount = FAT_SECTOR_SIZE - offset;
|
|
|
|
|
|
|
|
// Only require some of this sector?
|
|
|
|
if (copyCount > (length - bytesWritten))
|
|
|
|
copyCount = (length - bytesWritten);
|
|
|
|
|
|
|
|
// Do we need to read a new sector?
|
|
|
|
if (file->file_data_address != sector)
|
|
|
|
{
|
|
|
|
// Flush un-written data to file
|
|
|
|
if (file->file_data_dirty)
|
|
|
|
fl_fflush(file);
|
|
|
|
|
|
|
|
// If we plan to overwrite the whole sector, we don't need to read it first!
|
|
|
|
if (copyCount != FAT_SECTOR_SIZE)
|
|
|
|
{
|
|
|
|
// NOTE: This does not have succeed; if last sector of file
|
|
|
|
// reached, no valid data will be read in, but write will
|
|
|
|
// allocate some more space for new data.
|
|
|
|
|
|
|
|
// Get LBA of sector offset within file
|
|
|
|
if (!_read_sectors(file, sector, file->file_data_sector, 1))
|
|
|
|
memset(file->file_data_sector, 0x00, FAT_SECTOR_SIZE);
|
|
|
|
}
|
|
|
|
|
|
|
|
file->file_data_address = sector;
|
|
|
|
file->file_data_dirty = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Copy from application buffer into sector buffer
|
|
|
|
memcpy((uint8*)(file->file_data_sector + offset), (uint8*)(buffer + bytesWritten), copyCount);
|
|
|
|
|
|
|
|
// Mark buffer as dirty
|
|
|
|
file->file_data_dirty = 1;
|
|
|
|
|
|
|
|
// Increase total read count
|
|
|
|
bytesWritten += copyCount;
|
|
|
|
|
|
|
|
// Increment file pointer
|
|
|
|
file->bytenum += copyCount;
|
|
|
|
|
|
|
|
// Move onto next sector and reset copy offset
|
|
|
|
sector++;
|
|
|
|
offset = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write increased extent of the file?
|
|
|
|
if (file->bytenum > file->filelength)
|
|
|
|
{
|
|
|
|
// Increase file size to new point
|
|
|
|
file->filelength = file->bytenum;
|
|
|
|
|
|
|
|
// We are changing the file length and this
|
|
|
|
// will need to be writen back at some point
|
|
|
|
file->filelength_changed = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if FATFS_INC_TIME_DATE_SUPPORT
|
|
|
|
// If time & date support is enabled, always force directory entry to be
|
|
|
|
// written in-order to update file modify / access time & date.
|
|
|
|
file->filelength_changed = 1;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
FL_UNLOCK(&_fs);
|
|
|
|
|
|
|
|
return (size*count);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// fl_fputs: Write a character string to the stream
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#if FATFS_INC_WRITE_SUPPORT
|
|
|
|
int fl_fputs(const char * str, void *f)
|
|
|
|
{
|
|
|
|
int len = (int)strlen(str);
|
|
|
|
int res = fl_fwrite(str, 1, len, f);
|
|
|
|
|
|
|
|
if (res == len)
|
|
|
|
return len;
|
|
|
|
else
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// fl_remove: Remove a file from the filesystem
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#if FATFS_INC_WRITE_SUPPORT
|
|
|
|
int fl_remove( const char * filename )
|
|
|
|
{
|
|
|
|
FL_FILE* file;
|
|
|
|
int res = -1;
|
|
|
|
|
|
|
|
FL_LOCK(&_fs);
|
|
|
|
|
|
|
|
// Use read_file as this will check if the file is already open!
|
|
|
|
file = fl_fopen((char*)filename, "r");
|
|
|
|
if (file)
|
|
|
|
{
|
|
|
|
// Delete allocated space
|
|
|
|
if (fatfs_free_cluster_chain(&_fs, file->startcluster))
|
|
|
|
{
|
|
|
|
// Remove directory entries
|
|
|
|
if (fatfs_mark_file_deleted(&_fs, file->parentcluster, (char*)file->shortfilename))
|
|
|
|
{
|
|
|
|
// Close the file handle (this should not write anything to the file
|
|
|
|
// as we have not changed the file since opening it!)
|
|
|
|
fl_fclose(file);
|
|
|
|
|
|
|
|
res = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
FL_UNLOCK(&_fs);
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// fl_createdirectory: Create a directory based on a path
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#if FATFS_INC_WRITE_SUPPORT
|
|
|
|
int fl_createdirectory(const char *path)
|
|
|
|
{
|
|
|
|
int res;
|
|
|
|
|
|
|
|
// If first call to library, initialise
|
|
|
|
CHECK_FL_INIT();
|
|
|
|
|
|
|
|
FL_LOCK(&_fs);
|
|
|
|
res =_create_directory((char*)path);
|
|
|
|
FL_UNLOCK(&_fs);
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// fl_listdirectory: List a directory based on a path
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#if FATFS_DIR_LIST_SUPPORT
|
|
|
|
void fl_listdirectory(const char *path)
|
|
|
|
{
|
|
|
|
FL_DIR dirstat;
|
|
|
|
|
|
|
|
// If first call to library, initialise
|
|
|
|
CHECK_FL_INIT();
|
|
|
|
|
|
|
|
FL_LOCK(&_fs);
|
|
|
|
|
|
|
|
FAT_PRINTF(("\r\nDirectory %s\r\n", path));
|
|
|
|
|
|
|
|
if (fl_opendir(path, &dirstat))
|
|
|
|
{
|
|
|
|
struct fs_dir_ent dirent;
|
|
|
|
|
|
|
|
while (fl_readdir(&dirstat, &dirent) == 0)
|
|
|
|
{
|
|
|
|
#if FATFS_INC_TIME_DATE_SUPPORT
|
|
|
|
int d,m,y,h,mn,s;
|
|
|
|
fatfs_convert_from_fat_time(dirent.write_time, &h,&m,&s);
|
|
|
|
fatfs_convert_from_fat_date(dirent.write_date, &d,&mn,&y);
|
|
|
|
FAT_PRINTF(("%02d/%02d/%04d %02d:%02d ", d,mn,y,h,m));
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (dirent.is_dir)
|
|
|
|
{
|
|
|
|
FAT_PRINTF(("%s <DIR>\r\n", dirent.filename));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
FAT_PRINTF(("%s [%d bytes]\r\n", dirent.filename, dirent.size));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fl_closedir(&dirstat);
|
|
|
|
}
|
|
|
|
|
|
|
|
FL_UNLOCK(&_fs);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// fl_opendir: Opens a directory for listing
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#if FATFS_DIR_LIST_SUPPORT
|
|
|
|
FL_DIR* fl_opendir(const char* path, FL_DIR *dir)
|
|
|
|
{
|
|
|
|
int levels;
|
|
|
|
int res = 1;
|
|
|
|
uint32 cluster = FAT32_INVALID_CLUSTER;
|
|
|
|
|
|
|
|
// If first call to library, initialise
|
|
|
|
CHECK_FL_INIT();
|
|
|
|
|
|
|
|
FL_LOCK(&_fs);
|
|
|
|
|
|
|
|
levels = fatfs_total_path_levels((char*)path) + 1;
|
|
|
|
|
|
|
|
// If path is in the root dir
|
|
|
|
if (levels == 0)
|
|
|
|
cluster = fatfs_get_root_cluster(&_fs);
|
|
|
|
// Find parent directory start cluster
|
|
|
|
else
|
|
|
|
res = _open_directory((char*)path, &cluster);
|
|
|
|
|
|
|
|
if (res)
|
|
|
|
fatfs_list_directory_start(&_fs, dir, cluster);
|
|
|
|
|
|
|
|
FL_UNLOCK(&_fs);
|
|
|
|
|
|
|
|
return cluster != FAT32_INVALID_CLUSTER ? dir : 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// fl_readdir: Get next item in directory
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#if FATFS_DIR_LIST_SUPPORT
|
|
|
|
int fl_readdir(FL_DIR *dirls, fl_dirent *entry)
|
|
|
|
{
|
|
|
|
int res = 0;
|
|
|
|
|
|
|
|
// If first call to library, initialise
|
|
|
|
CHECK_FL_INIT();
|
|
|
|
|
|
|
|
FL_LOCK(&_fs);
|
|
|
|
|
|
|
|
res = fatfs_list_directory_next(&_fs, dirls, entry);
|
|
|
|
|
|
|
|
FL_UNLOCK(&_fs);
|
|
|
|
|
|
|
|
return res ? 0 : -1;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// fl_closedir: Close directory after listing
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#if FATFS_DIR_LIST_SUPPORT
|
|
|
|
int fl_closedir(FL_DIR* dir)
|
|
|
|
{
|
|
|
|
// Not used
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// fl_is_dir: Is this a directory?
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#if FATFS_DIR_LIST_SUPPORT
|
|
|
|
int fl_is_dir(const char *path)
|
|
|
|
{
|
|
|
|
int res = 0;
|
|
|
|
FL_DIR dir;
|
|
|
|
|
|
|
|
if (fl_opendir(path, &dir))
|
|
|
|
{
|
|
|
|
res = 1;
|
|
|
|
fl_closedir(&dir);
|
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// fl_format: Format a partition with either FAT16 or FAT32 based on size
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#if FATFS_INC_FORMAT_SUPPORT
|
|
|
|
int fl_format(uint32 volume_sectors, const char *name)
|
|
|
|
{
|
|
|
|
return fatfs_format(&_fs, volume_sectors, name);
|
|
|
|
}
|
|
|
|
#endif /*FATFS_INC_FORMAT_SUPPORT*/
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// fl_get_fs:
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#ifdef FATFS_INC_TEST_HOOKS
|
|
|
|
struct fatfs* fl_get_fs(void)
|
|
|
|
{
|
|
|
|
return &_fs;
|
|
|
|
}
|
|
|
|
#endif
|