Add AFATFS support to Blackbox

This commit is contained in:
Nicholas Sherlock 2015-11-17 21:14:40 +13:00 committed by borisbstyle
parent 04d8dd27bf
commit 14c13085b7
8 changed files with 389 additions and 156 deletions

View File

@ -258,6 +258,7 @@ static const blackboxSimpleFieldDefinition_t blackboxSlowFields[] = {
typedef enum BlackboxState {
BLACKBOX_STATE_DISABLED = 0,
BLACKBOX_STATE_STOPPED,
BLACKBOX_STATE_PREPARE_LOG_FILE,
BLACKBOX_STATE_SEND_HEADER,
BLACKBOX_STATE_SEND_MAIN_FIELD_HEADER,
BLACKBOX_STATE_SEND_GPS_H_HEADER,
@ -479,7 +480,6 @@ static void blackboxSetState(BlackboxState newState)
break;
case BLACKBOX_STATE_SHUTTING_DOWN:
xmitState.u.startTime = millis();
blackboxDeviceEndLog();
break;
default:
;
@ -787,8 +787,20 @@ static void validateBlackboxConfig()
masterConfig.blackbox_rate_denom /= div;
}
if (masterConfig.blackbox_device >= BLACKBOX_DEVICE_END) {
masterConfig.blackbox_device = BLACKBOX_DEVICE_SERIAL;
// If we've chosen an unsupported device, change the device to serial
switch (masterConfig.blackbox_device) {
#ifdef USE_FLASHFS
case BLACKBOX_DEVICE_FLASH:
#endif
#ifdef USE_SDCARD
case BLACKBOX_DEVICE_SDCARD:
#endif
case BLACKBOX_DEVICE_SERIAL:
// Device supported, leave the setting alone
break;
default:
masterConfig.blackbox_device = BLACKBOX_DEVICE_SERIAL;
}
}
@ -834,7 +846,7 @@ void startBlackbox(void)
*/
blackboxLastArmingBeep = getArmingBeepTimeMicros();
blackboxSetState(BLACKBOX_STATE_SEND_HEADER);
blackboxSetState(BLACKBOX_STATE_PREPARE_LOG_FILE);
}
}
@ -1301,6 +1313,11 @@ void handleBlackbox(void)
}
switch (blackboxState) {
case BLACKBOX_STATE_PREPARE_LOG_FILE:
if (blackboxDeviceBeginLog()) {
blackboxSetState(BLACKBOX_STATE_SEND_HEADER);
}
break;
case BLACKBOX_STATE_SEND_HEADER:
//On entry of this state, xmitState.headerIndex is 0 and startTime is intialised
@ -1409,7 +1426,7 @@ void handleBlackbox(void)
*
* Don't wait longer than it could possibly take if something funky happens.
*/
if (millis() > xmitState.u.startTime + BLACKBOX_SHUTDOWN_TIMEOUT_MILLIS || blackboxDeviceFlush()) {
if (blackboxDeviceEndLog() && (millis() > xmitState.u.startTime + BLACKBOX_SHUTDOWN_TIMEOUT_MILLIS || blackboxDeviceFlush())) {
blackboxDeviceClose();
blackboxSetState(BLACKBOX_STATE_STOPPED);
}

View File

@ -77,7 +77,20 @@ static portSharing_e blackboxPortSharing;
#ifdef USE_SDCARD
static afatfsFilePtr_t sdFile;
static struct {
afatfsFilePtr_t logFile;
afatfsFilePtr_t logDirectory;
afatfsFinder_t logDirectoryFinder;
uint32_t largestLogFileNumber;
enum {
BLACKBOX_SDCARD_INITIAL,
BLACKBOX_SDCARD_WAITING,
BLACKBOX_SDCARD_ENUMERATE_FILES,
BLACKBOX_SDCARD_READY_TO_CREATE_LOG,
BLACKBOX_SDCARD_READY_TO_LOG
} state;
} blackboxSDCard;
#endif
@ -91,7 +104,7 @@ void blackboxWrite(uint8_t value)
#endif
#ifdef USE_SDCARD
case BLACKBOX_DEVICE_SDCARD:
afatfs_fputc(sdFile, value);
afatfs_fputc(blackboxSDCard.logFile, value);
break;
#endif
case BLACKBOX_DEVICE_SERIAL:
@ -167,7 +180,7 @@ int blackboxPrint(const char *s)
#ifdef USE_SDCARD
case BLACKBOX_DEVICE_SDCARD:
length = strlen(s);
afatfs_fwrite(sdFile, (const uint8_t*) s, length); // Ignore failures due to buffers filling up
afatfs_fwrite(blackboxSDCard.logFile, (const uint8_t*) s, length); // Ignore failures due to buffers filling up
break;
#endif
@ -577,7 +590,7 @@ bool blackboxDeviceOpen(void)
#endif
#ifdef USE_SDCARD
case BLACKBOX_DEVICE_SDCARD:
if (afatfs_getFilesystemState() != AFATFS_FILESYSTEM_STATE_READY || afatfs_isFull()) {
if (afatfs_getFilesystemState() == AFATFS_FILESYSTEM_STATE_FATAL || afatfs_getFilesystemState() == AFATFS_FILESYSTEM_STATE_UNKNOWN || afatfs_isFull()) {
return false;
}
@ -598,6 +611,7 @@ void blackboxDeviceClose(void)
{
switch (masterConfig.blackbox_device) {
case BLACKBOX_DEVICE_SERIAL:
// Since the serial port could be shared with other processes, we have to give it back here
closeSerialPort(blackboxPort);
blackboxPort = NULL;
@ -608,34 +622,163 @@ void blackboxDeviceClose(void)
if (blackboxPortSharing == PORTSHARING_SHARED) {
mspAllocateSerialPorts(&masterConfig.serialConfig);
}
break;
#ifdef USE_FLASHFS
case BLACKBOX_DEVICE_FLASH:
// No-op since the flash doesn't have a "close" and there's nobody else to hand control of it to.
break;
#endif
#ifdef USE_SDCARD
case BLACKBOX_DEVICE_SDCARD:
afatfs_fclose(sdFile, NULL);
sdFile = NULL;
break;
#endif
break;
default:
;
}
}
static void blackboxLogDirCreated(afatfsFilePtr_t directory)
{
blackboxSDCard.logDirectory = directory;
afatfs_findFirst(blackboxSDCard.logDirectory, &blackboxSDCard.logDirectoryFinder);
blackboxSDCard.state = BLACKBOX_SDCARD_ENUMERATE_FILES;
}
static void blackboxLogFileCreated(afatfsFilePtr_t file)
{
blackboxSDCard.logFile = file;
blackboxSDCard.state = BLACKBOX_SDCARD_READY_TO_LOG;
}
static void blackboxCreateLogFile()
{
blackboxSDCard.largestLogFileNumber++;
uint32_t remainder = blackboxSDCard.largestLogFileNumber;
char filename[13];
filename[0] = 'L';
filename[1] = 'O';
filename[2] = 'G';
for (int i = 7; i >= 3; i--) {
filename[i] = (remainder % 10) + '0';
remainder /= 10;
}
filename[8] = '.';
filename[9] = 'T';
filename[10] = 'X';
filename[11] = 'T';
filename[12] = 0;
blackboxSDCard.state = BLACKBOX_SDCARD_WAITING;
afatfs_fopen(filename, "as", blackboxLogFileCreated);
}
/**
* Terminate the current log (for devices which support separations between the logs of multiple flights)
* Begin a new log on the SDCard.
*
* Keep calling until the function returns true (open is complete).
*/
void blackboxDeviceEndLog(void)
static bool blackboxSDCardBeginLog()
{
fatDirectoryEntry_t *directoryEntry;
doMore:
switch (blackboxSDCard.state) {
case BLACKBOX_SDCARD_INITIAL:
if (afatfs_getFilesystemState() == AFATFS_FILESYSTEM_STATE_READY) {
blackboxSDCard.state = BLACKBOX_SDCARD_WAITING;
afatfs_mkdir("logs", blackboxLogDirCreated);
}
return false;
case BLACKBOX_SDCARD_WAITING:
// Waiting for directory entry to be created
return false;
case BLACKBOX_SDCARD_ENUMERATE_FILES:
while (afatfs_findNext(blackboxSDCard.logDirectory, &blackboxSDCard.logDirectoryFinder, &directoryEntry) == AFATFS_OPERATION_SUCCESS) {
if (directoryEntry && !fat_isDirectoryEntryTerminator(directoryEntry)) {
// If this is a log file, parse the log number from the filename
if (
directoryEntry->filename[0] == 'L' && directoryEntry->filename[1] == 'O' && directoryEntry->filename[2] == 'G'
&& directoryEntry->filename[8] == 'T' && directoryEntry->filename[9] == 'X' && directoryEntry->filename[10] == 'T'
) {
char logSequenceNumberString[6];
memcpy(logSequenceNumberString, directoryEntry->filename + 3, 5);
logSequenceNumberString[5] = '\0';
blackboxSDCard.largestLogFileNumber = atoi(logSequenceNumberString);
}
} else {
afatfs_findLast(blackboxSDCard.logDirectory);
// We're done checking all the files on the card, now we can create a new log file
// Change into the log directory:
afatfs_chdir(blackboxSDCard.logDirectory);
// We no longer need our open handle on that Directory
afatfs_fclose(blackboxSDCard.logDirectory, NULL);
blackboxSDCard.logDirectory = NULL;
blackboxSDCard.state = BLACKBOX_SDCARD_READY_TO_CREATE_LOG;
goto doMore;
}
}
return false;
case BLACKBOX_SDCARD_READY_TO_CREATE_LOG:
blackboxCreateLogFile();
return false;
case BLACKBOX_SDCARD_READY_TO_LOG:
return true;
}
return false;
}
/**
* Begin a new log (for devices which support separations between the logs of multiple flights).
*
* Keep calling until the function returns true (open is complete).
*/
bool blackboxDeviceBeginLog(void)
{
switch (masterConfig.blackbox_device) {
#ifdef USE_SDCARD
case BLACKBOX_DEVICE_SDCARD:
if (afatfs_fclose(sdFile, NULL)) {
sdFile = NULL;
}
break;
return blackboxSDCardBeginLog();
#endif
default:
return true;
}
}
/**
* Terminate the current log (for devices which support separations between the logs of multiple flights).
*
* Keep calling until this returns true
*/
bool blackboxDeviceEndLog(void)
{
switch (masterConfig.blackbox_device) {
#ifdef USE_SDCARD
case BLACKBOX_DEVICE_SDCARD:
// Keep retrying until the close operation queues
if (afatfs_fclose(blackboxSDCard.logFile, NULL)) {
// Don't bother waiting the for the close to complete, it's queued now and will complete eventually
blackboxSDCard.logFile = NULL;
blackboxSDCard.state = BLACKBOX_SDCARD_READY_TO_CREATE_LOG;
return true;
}
return false;
#endif
default:
return true;
}
}

View File

@ -26,10 +26,10 @@ typedef enum BlackboxDevice {
BLACKBOX_DEVICE_SERIAL = 0,
#ifdef USE_FLASHFS
BLACKBOX_DEVICE_FLASH,
BLACKBOX_DEVICE_FLASH = 1,
#endif
#ifdef USE_SDCARD
BLACKBOX_DEVICE_SDCARD,
BLACKBOX_DEVICE_SDCARD = 2,
#endif
BLACKBOX_DEVICE_END
@ -75,7 +75,9 @@ void blackboxWriteFloat(float value);
bool blackboxDeviceFlush(void);
bool blackboxDeviceOpen(void);
void blackboxDeviceClose(void);
void blackboxDeviceEndLog(void);
bool blackboxDeviceBeginLog(void);
bool blackboxDeviceEndLog(void);
bool isBlackboxDeviceFull(void);

View File

@ -207,18 +207,19 @@ typedef struct sdcardCSD_t {
#define SDCARD_R1_STATUS_BIT_ADDRESS_ERROR 32
#define SDCARD_R1_STATUS_BIT_PARAMETER_ERROR 64
#define SDCARD_CSD_STRUCTURE_VERSION_1 0
#define SDCARD_CSD_STRUCTURE_VERSION_2 1
#define SDCARD_CSD_STRUCTURE_VERSION_1 0
#define SDCARD_CSD_STRUCTURE_VERSION_2 1
#define SDCARD_VOLTAGE_ACCEPTED_2_7_to_3_6 0x01
#define SDCARD_VOLTAGE_ACCEPTED_LVR 0x02
#define SDCARD_VOLTAGE_ACCEPTED_2_7_to_3_6 0x01
#define SDCARD_VOLTAGE_ACCEPTED_LVR 0x02
#define SDCARD_COMMAND_GO_IDLE_STATE 0
#define SDCARD_COMMAND_SEND_OP_COND 1
#define SDCARD_COMMAND_SEND_IF_COND 8
#define SDCARD_COMMAND_SEND_IF_COND 8
#define SDCARD_COMMAND_SEND_CSD 9
#define SDCARD_COMMAND_SEND_CID 10
#define SDCARD_COMMAND_STOP_TRANSMISSION 12
#define SDCARD_COMMAND_SEND_STATUS 13
#define SDCARD_COMMAND_SET_BLOCKLEN 16
#define SDCARD_COMMAND_READ_SINGLE_BLOCK 17
#define SDCARD_COMMAND_READ_MULTIPLE_BLOCK 18

View File

@ -129,6 +129,9 @@ typedef struct afatfsCacheBlockDescriptor_t {
/*
* The state of this block must not transition (do not flush to disk, do not discard). This is useful for a sector
* which is currently being written to by the application (so flushing it would be a waste of time).
*
* This is a binary state rather than a counter because we assume that only one file will be responsible for the
* sectors it locks.
*/
unsigned locked:1;
@ -312,7 +315,9 @@ typedef struct afatfsFile_t {
* seek across a sector boundary. This allows fwrite() to complete faster because it doesn't need to check the
* cache on every call.
*/
int8_t lockedCacheIndex;
int8_t writeLockedCacheIndex;
// Ditto for fread():
int8_t readRetainCacheIndex;
// The position of our directory entry on the disk (so we can update it without consulting a parent directory file)
afatfsDirEntryPointer_t directoryEntryPos;
@ -392,7 +397,7 @@ static afatfs_t afatfs;
static void afatfs_fileOperationContinue(afatfsFile_t *file);
static uint8_t* afatfs_fileLockCursorSectorForWrite(afatfsFilePtr_t file);
static uint8_t* afatfs_fileGetCursorSectorForRead(afatfsFilePtr_t file);
static uint8_t* afatfs_fileRetainCursorSectorForRead(afatfsFilePtr_t file);
static uint32_t roundUpTo(uint32_t value, uint32_t rounding)
{
@ -659,6 +664,7 @@ bool afatfs_flush()
if (afatfs.cacheDescriptor[i].state == AFATFS_CACHE_STATE_DIRTY && !afatfs.cacheDescriptor[i].locked) {
afatfs_cacheFlushSector(i);
// That flush will take time to complete so we may as well tell caller to come back later
return false;
}
}
@ -957,9 +963,13 @@ static afatfsOperationStatus_e afatfs_FATSetNextCluster(uint32_t startCluster, u
static void afatfs_fileUnlockCacheSector(afatfsFilePtr_t file)
{
if (file->lockedCacheIndex != -1) {
afatfs.cacheDescriptor[file->lockedCacheIndex].locked = 0;
file->lockedCacheIndex = -1;
if (file->writeLockedCacheIndex != -1) {
afatfs.cacheDescriptor[file->writeLockedCacheIndex].locked = 0;
file->writeLockedCacheIndex = -1;
}
if (file->readRetainCacheIndex != -1) {
afatfs.cacheDescriptor[file->readRetainCacheIndex].retainCount = MAX(afatfs.cacheDescriptor[file->readRetainCacheIndex].retainCount - 1, 0);
file->readRetainCacheIndex = -1;
}
}
@ -1209,6 +1219,9 @@ static afatfsOperationStatus_e afatfs_saveDirectoryEntry(afatfsFilePtr_t file)
uint8_t *sector;
afatfsOperationStatus_e result;
if (file->directoryEntryPos.sectorNumberPhysical == 0)
return AFATFS_OPERATION_SUCCESS; // Root directories don't have a directory entry
result = afatfs_cacheSector(file->directoryEntryPos.sectorNumberPhysical, &sector, AFATFS_CACHE_READ | AFATFS_CACHE_WRITE);
if (result == AFATFS_OPERATION_SUCCESS) {
@ -1548,20 +1561,26 @@ static bool afatfs_isEndOfAllocatedFile(afatfsFilePtr_t file)
}
/**
* Take a lock on the sector at the current file cursor position.
*
* Returns a pointer to the sector buffer if successful, or NULL if at the end of file (check afatfs_isEndOfAllocatedFile())
* or the sector has not yet been read in from disk.
*/
static uint8_t* afatfs_fileGetCursorSectorForRead(afatfsFilePtr_t file)
static uint8_t* afatfs_fileRetainCursorSectorForRead(afatfsFilePtr_t file)
{
uint8_t *result;
uint32_t physicalSector = afatfs_fileGetCursorPhysicalSector(file);
if (file->lockedCacheIndex != -1) {
if (!afatfs_assert(physicalSector == afatfs.cacheDescriptor[file->lockedCacheIndex].sectorIndex)) {
/* If we've already got a locked sector then we can assume that was the same one that's at the cursor (because this
* cache is invalidated when crossing a sector boundary)
*/
if (file->readRetainCacheIndex != -1) {
if (!afatfs_assert(physicalSector == afatfs.cacheDescriptor[file->readRetainCacheIndex].sectorIndex)) {
return NULL;
}
result = afatfs_cacheSectorGetMemory(file->lockedCacheIndex);
result = afatfs_cacheSectorGetMemory(file->readRetainCacheIndex);
} else {
if (afatfs_isEndOfAllocatedFile(file)) {
return NULL;
@ -1572,7 +1591,7 @@ static uint8_t* afatfs_fileGetCursorSectorForRead(afatfsFilePtr_t file)
afatfsOperationStatus_e status = afatfs_cacheSector(
physicalSector,
&result,
AFATFS_CACHE_READ | AFATFS_CACHE_LOCK
AFATFS_CACHE_READ | AFATFS_CACHE_RETAIN
);
if (status != AFATFS_OPERATION_SUCCESS) {
@ -1580,7 +1599,67 @@ static uint8_t* afatfs_fileGetCursorSectorForRead(afatfsFilePtr_t file)
return NULL;
}
file->lockedCacheIndex = afatfs_getCacheDescriptorIndexForBuffer(result);
file->readRetainCacheIndex = afatfs_getCacheDescriptorIndexForBuffer(result);
}
return result;
}
/**
* Lock the sector at the file's cursor position for write, and return a reference to the memory for that sector.
*
* Returns NULL if the cache was too busy, try again later.
*/
static uint8_t* afatfs_fileLockCursorSectorForWrite(afatfsFilePtr_t file)
{
afatfsOperationStatus_e status;
uint8_t *result;
if (file->writeLockedCacheIndex != -1) {
uint32_t physicalSector = afatfs_fileGetCursorPhysicalSector(file);
if (!afatfs_assert(physicalSector == afatfs.cacheDescriptor[file->writeLockedCacheIndex].sectorIndex)) {
return NULL;
}
result = afatfs_cacheSectorGetMemory(file->writeLockedCacheIndex);
} else {
// Find / allocate a sector and lock it in the cache so we can rely on it sticking around
// Are we at the start of an empty file or the end of a non-empty file? If so we need to add a cluster
if (afatfs_isEndOfAllocatedFile(file) && afatfs_appendFreeCluster(file) != AFATFS_OPERATION_SUCCESS) {
// The extension of the file is in progress so please call us again later to try again
return NULL;
}
uint32_t physicalSector = afatfs_fileGetCursorPhysicalSector(file);
uint8_t cacheFlags = AFATFS_CACHE_WRITE | AFATFS_CACHE_LOCK;
uint32_t cursorOffsetInSector = file->cursorOffset % AFATFS_SECTOR_SIZE;
/*
* If there is data before the write point, or there could be data after the write-point
* then we need to have the original contents of the sector in the cache for us to merge into
*/
if (
cursorOffsetInSector > 0
|| (file->cursorOffset & ~(AFATFS_SECTOR_SIZE - 1)) + AFATFS_SECTOR_SIZE < file->directoryEntry.fileSize
) {
cacheFlags |= AFATFS_CACHE_READ;
}
status = afatfs_cacheSector(
physicalSector,
&result,
cacheFlags
);
if (status != AFATFS_OPERATION_SUCCESS) {
// Not enough cache available to accept this write / sector not ready for read
return NULL;
}
file->writeLockedCacheIndex = afatfs_getCacheDescriptorIndexForBuffer(result);
}
return result;
@ -1822,7 +1901,7 @@ afatfsOperationStatus_e afatfs_findNext(afatfsFilePtr_t directory, afatfsFinder_
}
}
sector = afatfs_fileGetCursorSectorForRead(directory);
sector = afatfs_fileRetainCursorSectorForRead(directory);
if (sector) {
finder->entryIndex++;
@ -1843,6 +1922,14 @@ afatfsOperationStatus_e afatfs_findNext(afatfsFilePtr_t directory, afatfsFinder_
}
}
/**
* Release resources associated with a find operation.
*/
void afatfs_findLast(afatfsFilePtr_t directory)
{
afatfs_fileUnlockCacheSector(directory);
}
/**
* Initialise the finder so that the first call with the directory to findNext() will return the first file in the
* directory.
@ -1888,8 +1975,8 @@ static afatfsOperationStatus_e afatfs_extendSubdirectoryContinue(afatfsFile_t *d
memset(sectorBuffer, 0, AFATFS_SECTOR_SIZE);
// If this is the first sector of the directory, create the "." and ".." entries
if (directory->cursorOffset == 0) {
// If this is the first sector of a non-root directory, create the "." and ".." entries
if (afatfs.currentDirectory.directoryEntryPos.sectorNumberPhysical != 0 && directory->cursorOffset == 0) {
fatDirectoryEntry_t *dirEntries = (fatDirectoryEntry_t *) sectorBuffer;
memset(dirEntries[0].filename, ' ', sizeof(dirEntries[0].filename));
@ -2005,6 +2092,7 @@ static afatfsOperationStatus_e afatfs_allocateDirectoryEntry(afatfsFilePtr_t dir
if (fat_isDirectoryEntryEmpty(*dirEntry) || fat_isDirectoryEntryTerminator(*dirEntry)) {
afatfs_cacheSectorMarkDirty((uint8_t*) *dirEntry);
afatfs_findLast(directory);
return AFATFS_OPERATION_SUCCESS;
}
} else {
@ -2193,29 +2281,41 @@ static void afatfs_createFileContinue(afatfsFile_t *file)
do {
status = afatfs_findNext(&afatfs.currentDirectory, &file->directoryEntryPos, &entry);
if (status == AFATFS_OPERATION_SUCCESS) {
if (entry == NULL || fat_isDirectoryEntryTerminator(entry)) {
if ((file->mode & AFATFS_FILE_MODE_CREATE) != 0) {
// The file didn't already exist, so we can create it. Allocate a new directory entry
afatfs_findFirst(&afatfs.currentDirectory, &file->directoryEntryPos);
switch (status) {
case AFATFS_OPERATION_SUCCESS:
// Is this the last entry in the directory?
if (entry == NULL || fat_isDirectoryEntryTerminator(entry)) {
afatfs_findLast(&afatfs.currentDirectory);
opState->phase = AFATFS_CREATEFILE_PHASE_CREATE_NEW_FILE;
goto doMore;
} else {
// File not found.
opState->phase = AFATFS_CREATEFILE_PHASE_FAILURE;
goto doMore;
}
} else if (strncmp(entry->filename, (char*) file->directoryEntry.filename, FAT_FILENAME_LENGTH) == 0) {
// We found a file with this name!
memcpy(&file->directoryEntry, entry, sizeof(file->directoryEntry));
if ((file->mode & AFATFS_FILE_MODE_CREATE) != 0) {
// The file didn't already exist, so we can create it. Allocate a new directory entry
afatfs_findFirst(&afatfs.currentDirectory, &file->directoryEntryPos);
opState->phase = AFATFS_CREATEFILE_PHASE_SUCCESS;
opState->phase = AFATFS_CREATEFILE_PHASE_CREATE_NEW_FILE;
goto doMore;
} else {
// File not found.
opState->phase = AFATFS_CREATEFILE_PHASE_FAILURE;
goto doMore;
}
} else if (strncmp(entry->filename, (char*) file->directoryEntry.filename, FAT_FILENAME_LENGTH) == 0) {
// We found a file with this name!
memcpy(&file->directoryEntry, entry, sizeof(file->directoryEntry));
afatfs_findLast(&afatfs.currentDirectory);
opState->phase = AFATFS_CREATEFILE_PHASE_SUCCESS;
goto doMore;
} // Else this entry doesn't match, fall through and continue the search
break;
case AFATFS_OPERATION_FAILURE:
afatfs_findLast(&afatfs.currentDirectory);
opState->phase = AFATFS_CREATEFILE_PHASE_FAILURE;
goto doMore;
}
} else if (status == AFATFS_OPERATION_FAILURE) {
opState->phase = AFATFS_CREATEFILE_PHASE_FAILURE;
goto doMore;
break;
case AFATFS_OPERATION_IN_PROGRESS:
;
}
} while (status == AFATFS_OPERATION_SUCCESS);
break;
@ -2299,7 +2399,8 @@ static void afatfs_createFileContinue(afatfsFile_t *file)
static void afatfs_initFileHandle(afatfsFilePtr_t file)
{
memset(file, 0, sizeof(*file));
file->lockedCacheIndex = -1;
file->writeLockedCacheIndex = -1;
file->readRetainCacheIndex = -1;
}
static void afatfs_funlinkContinue(afatfsFilePtr_t file)
@ -2410,12 +2511,12 @@ static void afatfs_closeFileContinue(afatfsFilePtr_t file)
/*
* Directories don't update their parent directory entries over time, because their fileSize field in the directory
* never changes (when we add the first clsuter to the directory we save the directory entry at that point and it
* never changes (when we add the first cluster to the directory we save the directory entry at that point and it
* doesn't change afterwards).
*
* So don't bother trying to save their directory entries during fclose().
*/
if (file->type != AFATFS_FILE_TYPE_DIRECTORY) {
if (file->type != AFATFS_FILE_TYPE_DIRECTORY && file->type != AFATFS_FILE_TYPE_FAT16_ROOT_DIRECTORY) {
if (afatfs_saveDirectoryEntry(file) != AFATFS_OPERATION_SUCCESS) {
return;
}
@ -2467,16 +2568,20 @@ bool afatfs_fclose(afatfsFilePtr_t file, afatfsCallback_t callback)
* Create a new directory with the given name, or open the directory if it already exists.
*
* The directory will be passed to the callback, or NULL if the creation failed.
*
* Returns true if the directory creation was begun, or false if there are too many open files.
*/
afatfsFilePtr_t afatfs_mkdir(const char *filename, afatfsFileCallback_t callback)
bool afatfs_mkdir(const char *filename, afatfsFileCallback_t callback)
{
afatfsFilePtr_t file = afatfs_allocateFileHandle();
if (file) {
afatfs_createFile(file, filename, FAT_FILE_ATTRIBUTE_DIRECTORY, AFATFS_FILE_MODE_CREATE | AFATFS_FILE_MODE_READ | AFATFS_FILE_MODE_WRITE, callback);
} else if (callback) {
callback(NULL);
}
return file;
return file != NULL;
}
/**
@ -2509,6 +2614,9 @@ bool afatfs_chdir(afatfsFilePtr_t directory)
afatfs.currentDirectory.directoryEntry.firstClusterLow = afatfs.rootDirectoryCluster & 0xFFFF;
afatfs.currentDirectory.directoryEntry.attrib = FAT_FILE_ATTRIBUTE_DIRECTORY;
// Root directories don't have a directory entry to represent themselves:
afatfs.currentDirectory.directoryEntryPos.sectorNumberPhysical = 0;
afatfs_fseek(&afatfs.currentDirectory, 0, AFATFS_SEEK_SET);
return true;
@ -2582,76 +2690,17 @@ bool afatfs_fopen(const char *filename, const char *mode, afatfsFileCallback_t c
return file != NULL;
}
/**
* Lock the sector at the file's cursor position for write, and return a reference to the memory for that sector.
*
* Returns NULL if the cache was too busy, try again later.
*/
static uint8_t* afatfs_fileLockCursorSectorForWrite(afatfsFilePtr_t file)
{
afatfsOperationStatus_e status;
uint8_t *result;
if (file->lockedCacheIndex != -1) {
/*uint32_t physicalSector = afatfs_fileGetCursorPhysicalSector(file);
if (!afatfs_assert(physicalSector == afatfs.cacheDescriptor[file->lockedCacheIndex].sectorIndex)) {
return NULL;
}*/
result = afatfs_cacheSectorGetMemory(file->lockedCacheIndex);
} else {
// Find / allocate a sector and lock it in the cache so we can rely on it sticking around
if (afatfs_isEndOfAllocatedFile(file)) {
;
}
// Are we at the start of an empty file or the end of a non-empty file? If so we need to add a cluster
if (afatfs_isEndOfAllocatedFile(file) && afatfs_appendFreeCluster(file) != AFATFS_OPERATION_SUCCESS) {
// The extension of the file is in progress so please call us again later to try again
return NULL;
}
uint32_t physicalSector = afatfs_fileGetCursorPhysicalSector(file);
uint8_t cacheFlags = AFATFS_CACHE_WRITE | AFATFS_CACHE_LOCK;
uint32_t cursorOffsetInSector = file->cursorOffset % AFATFS_SECTOR_SIZE;
/*
* If there is data before the write point, or there could be data after the write-point
* then we need to have the original contents of the sector in the cache for us to merge into
*/
if (
cursorOffsetInSector > 0
|| (file->cursorOffset & ~(AFATFS_SECTOR_SIZE - 1)) + AFATFS_SECTOR_SIZE < file->directoryEntry.fileSize
) {
cacheFlags |= AFATFS_CACHE_READ;
}
status = afatfs_cacheSector(
physicalSector,
&result,
cacheFlags
);
if (status != AFATFS_OPERATION_SUCCESS) {
// Not enough cache available to accept this write / sector not ready for read
return NULL;
}
file->lockedCacheIndex = afatfs_getCacheDescriptorIndexForBuffer(result);
}
return result;
}
void afatfs_fputc(afatfsFilePtr_t file, uint8_t c)
{
uint32_t cursorOffsetInSector = file->cursorOffset % AFATFS_SECTOR_SIZE;
int cacheIndex = file->lockedCacheIndex;
int cacheIndex = file->writeLockedCacheIndex;
if (cacheIndex != -1 && cursorOffsetInSector != AFATFS_SECTOR_SIZE - 1) {
afatfs_cacheSectorGetMemory(cacheIndex)[cursorOffsetInSector] = c;
file->cursorOffset++;
// TODO we could probably just defer this to the next seek, file close, or slow-path fwrite() call?
file->directoryEntry.fileSize = MAX(file->directoryEntry.fileSize, file->cursorOffset);
} else {
// Slow path
@ -2754,7 +2803,7 @@ uint32_t afatfs_fread(afatfsFilePtr_t file, uint8_t *buffer, uint32_t len)
uint32_t bytesToReadThisSector = MIN(AFATFS_SECTOR_SIZE - cursorOffsetInSector, len);
uint8_t *sectorBuffer;
sectorBuffer = afatfs_fileGetCursorSectorForRead(file);
sectorBuffer = afatfs_fileRetainCursorSectorForRead(file);
if (!sectorBuffer) {
// Cache is currently busy
return readBytes;
@ -3111,27 +3160,45 @@ void afatfs_init()
*/
bool afatfs_destroy()
{
// Don't attempt detailed cleanup if the filesystem is in an odd state
// Only attempt detailed cleanup if the filesystem is in reasonable looking state
if (afatfs.filesystemState == AFATFS_FILESYSTEM_STATE_READY) {
#ifdef AFATFS_USE_FREEFILE
afatfs_fclose(&afatfs.freeFile, NULL);
#endif
int openFileCount = 0;
for (int i = 0; i < AFATFS_MAX_OPEN_FILES; i++) {
afatfs_fclose(&afatfs.openFiles[i], NULL);
}
afatfs_poll();
for (int i = 0; i < AFATFS_NUM_CACHE_SECTORS; i++) {
// Flush even if the pages are "locked"
if (afatfs.cacheDescriptor[i].state == AFATFS_CACHE_STATE_DIRTY) {
if (afatfs_cacheFlushSector(i)) {
// Card will be busy making that write so don't bother trying to flush any other pages right now
return false;
}
if (afatfs.openFiles[i].type != AFATFS_FILE_TYPE_NONE) {
afatfs_fclose(&afatfs.openFiles[i], NULL);
openFileCount++;
}
}
#ifdef AFATFS_USE_FREEFILE
if (afatfs.freeFile.type != AFATFS_FILE_TYPE_NONE) {
afatfs_fclose(&afatfs.freeFile, NULL);
openFileCount++;
}
#endif
if (afatfs.currentDirectory.type != AFATFS_FILE_TYPE_NONE) {
afatfs_fclose(&afatfs.currentDirectory, NULL);
openFileCount++;
}
if (!afatfs_flush()) {
afatfs_poll();
return false;
}
if (openFileCount > 0) {
return false;
}
#ifdef AFATFS_DEBUG
/* All sector locks should have been released by closing the files, so the subsequent flush should have written
* all dirty pages to disk. If not, something's wrong:
*/
for (int i = 0; i < AFATFS_NUM_CACHE_SECTORS; i++) {
afatfs_assert(afatfs.cacheDescriptor[i].state != AFATFS_CACHE_STATE_DIRTY);
}
#endif
}
// Clear the afatfs so it's as if we never ran

View File

@ -48,11 +48,12 @@ uint32_t afatfs_fread(afatfsFilePtr_t file, uint8_t *buffer, uint32_t len);
afatfsOperationStatus_e afatfs_fseek(afatfsFilePtr_t file, int32_t offset, afatfsSeek_e whence);
bool afatfs_ftell(afatfsFilePtr_t file, uint32_t *position);
afatfsFilePtr_t afatfs_mkdir(const char *filename, afatfsFileCallback_t complete);
bool afatfs_mkdir(const char *filename, afatfsFileCallback_t complete);
bool afatfs_chdir(afatfsFilePtr_t dirHandle);
void afatfs_findFirst(afatfsFilePtr_t directory, afatfsFinder_t *finder);
afatfsOperationStatus_e afatfs_findNext(afatfsFilePtr_t directory, afatfsFinder_t *finder, fatDirectoryEntry_t **dirEntry);
void afatfs_findLast(afatfsFilePtr_t directory);
bool afatfs_flush();
void afatfs_init();

View File

@ -353,7 +353,7 @@ static const char * const lookupTableGimbalMode[] = {
};
static const char * const lookupTableBlackboxDevice[] = {
"SERIAL", "SPIFLASH"
"SERIAL", "SPIFLASH", "SDCARD"
};

View File

@ -65,6 +65,7 @@
#include "io/gimbal.h"
#include "io/ledstrip.h"
#include "io/display.h"
#include "io/asyncfatfs/asyncfatfs.h"
#include "sensors/sensors.h"
#include "sensors/sonar.h"
@ -538,6 +539,7 @@ void init(void)
#ifdef USE_SDCARD
sdcard_init();
afatfs_init();
#endif
#ifdef BLACKBOX