Add AFATFS support to Blackbox
This commit is contained in:
parent
04d8dd27bf
commit
14c13085b7
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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, §or, 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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -353,7 +353,7 @@ static const char * const lookupTableGimbalMode[] = {
|
|||
};
|
||||
|
||||
static const char * const lookupTableBlackboxDevice[] = {
|
||||
"SERIAL", "SPIFLASH"
|
||||
"SERIAL", "SPIFLASH", "SDCARD"
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue