//----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // 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 ( ;ilast_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; 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)); fat_list_init(&_free_file_list); fat_list_init(&_open_file_list); // Add all file objects to free list for (i=0;iflags = 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 ( ;ilast_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 \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