Add OSD_STATE_GROUP_ELEMENTS state to osdUpdate() and optimise DMA vs polled MAX7456...
[betaflight.git] / src / main / blackbox / blackbox_io.c
blob7db5301dd876410dc9addf058c798671fdda4388
1 /*
2 * This file is part of Cleanflight and Betaflight.
4 * Cleanflight and Betaflight are free software. You can redistribute
5 * this software and/or modify this software under the terms of the
6 * GNU General Public License as published by the Free Software
7 * Foundation, either version 3 of the License, or (at your option)
8 * any later version.
10 * Cleanflight and Betaflight are distributed in the hope that they
11 * will be useful, but WITHOUT ANY WARRANTY; without even the implied
12 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * See the GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this software.
18 * If not, see <http://www.gnu.org/licenses/>.
21 #include <stdint.h>
22 #include <stdbool.h>
23 #include <stdlib.h>
24 #include <stdarg.h>
25 #include <string.h>
27 #include "platform.h"
29 #ifdef USE_BLACKBOX
31 #include "build/debug.h"
33 // Debugging code that become useful when output bandwidth saturation is suspected.
34 // Set debug_mode = BLACKBOX_OUTPUT to see following debug values.
36 // 0: Average output bandwidth in last 100ms
37 // 1: Maximum hold of above.
38 // 2: Bytes dropped due to output buffer full.
40 // Note that bandwidth usage slightly increases when DEBUG_BB_OUTPUT is enabled,
41 // as output will include debug variables themselves.
43 #define DEBUG_BB_OUTPUT
45 #include "blackbox.h"
46 #include "blackbox_io.h"
48 #include "common/maths.h"
50 #include "flight/pid.h"
52 #include "io/asyncfatfs/asyncfatfs.h"
53 #include "io/flashfs.h"
54 #include "io/serial.h"
56 #include "msp/msp_serial.h"
58 #ifdef USE_SDCARD
59 #include "drivers/sdcard.h"
60 #endif
62 #define BLACKBOX_SERIAL_PORT_MODE MODE_TX
64 // How many bytes can we transmit per loop iteration when writing headers?
65 static uint8_t blackboxMaxHeaderBytesPerIteration;
67 // How many bytes can we write *this* iteration without overflowing transmit buffers or overstressing the OpenLog?
68 int32_t blackboxHeaderBudget;
70 static serialPort_t *blackboxPort = NULL;
71 static portSharing_e blackboxPortSharing;
73 #ifdef USE_SDCARD
75 static struct {
76 afatfsFilePtr_t logFile;
77 afatfsFilePtr_t logDirectory;
78 afatfsFinder_t logDirectoryFinder;
79 int32_t largestLogFileNumber;
81 enum {
82 BLACKBOX_SDCARD_INITIAL,
83 BLACKBOX_SDCARD_WAITING,
84 BLACKBOX_SDCARD_ENUMERATE_FILES,
85 BLACKBOX_SDCARD_CHANGE_INTO_LOG_DIRECTORY,
86 BLACKBOX_SDCARD_READY_TO_CREATE_LOG,
87 BLACKBOX_SDCARD_READY_TO_LOG
88 } state;
89 } blackboxSDCard;
91 #define LOGFILE_PREFIX "LOG"
92 #define LOGFILE_SUFFIX "BFL"
94 #endif // USE_SDCARD
96 void blackboxOpen(void)
98 serialPort_t *sharedBlackboxAndMspPort = findSharedSerialPort(FUNCTION_BLACKBOX, FUNCTION_MSP);
99 if (sharedBlackboxAndMspPort) {
100 mspSerialReleasePortIfAllocated(sharedBlackboxAndMspPort);
104 #ifdef DEBUG_BB_OUTPUT
105 static uint32_t bbBits;
106 static timeMs_t bbLastclearMs;
107 static uint16_t bbRateMax;
108 static uint32_t bbDrops;
109 #endif
111 void blackboxWrite(uint8_t value)
113 #ifdef DEBUG_BB_OUTPUT
114 bbBits += 8;
115 #endif
117 switch (blackboxConfig()->device) {
118 #ifdef USE_FLASHFS
119 case BLACKBOX_DEVICE_FLASH:
120 flashfsWriteByte(value); // Write byte asynchronously
121 break;
122 #endif
123 #ifdef USE_SDCARD
124 case BLACKBOX_DEVICE_SDCARD:
125 afatfs_fputc(blackboxSDCard.logFile, value);
126 break;
127 #endif
128 case BLACKBOX_DEVICE_SERIAL:
129 default:
131 int txBytesFree = serialTxBytesFree(blackboxPort);
133 #ifdef DEBUG_BB_OUTPUT
134 bbBits += 2;
135 DEBUG_SET(DEBUG_BLACKBOX_OUTPUT, 3, txBytesFree);
136 #endif
138 if (txBytesFree == 0) {
139 #ifdef DEBUG_BB_OUTPUT
140 ++bbDrops;
141 DEBUG_SET(DEBUG_BLACKBOX_OUTPUT, 2, bbDrops);
142 #endif
143 return;
145 serialWrite(blackboxPort, value);
147 break;
150 #ifdef DEBUG_BB_OUTPUT
151 timeMs_t now = millis();
153 if (now > bbLastclearMs + 100) { // Debug log every 100[msec]
154 uint16_t bbRate = ((bbBits * 10 + 5) / (now - bbLastclearMs)) / 10; // In unit of [Kbps]
155 DEBUG_SET(DEBUG_BLACKBOX_OUTPUT, 0, bbRate);
156 if (bbRate > bbRateMax) {
157 bbRateMax = bbRate;
158 DEBUG_SET(DEBUG_BLACKBOX_OUTPUT, 1, bbRateMax);
160 bbLastclearMs = now;
161 bbBits = 0;
163 #endif
166 // Print the null-terminated string 's' to the blackbox device and return the number of bytes written
167 int blackboxWriteString(const char *s)
169 int length;
170 const uint8_t *pos;
172 switch (blackboxConfig()->device) {
174 #ifdef USE_FLASHFS
175 case BLACKBOX_DEVICE_FLASH:
176 length = strlen(s);
177 flashfsWrite((const uint8_t*) s, length, false); // Write asynchronously
178 break;
179 #endif // USE_FLASHFS
181 #ifdef USE_SDCARD
182 case BLACKBOX_DEVICE_SDCARD:
183 length = strlen(s);
184 afatfs_fwrite(blackboxSDCard.logFile, (const uint8_t*) s, length); // Ignore failures due to buffers filling up
185 break;
186 #endif // USE_SDCARD
188 case BLACKBOX_DEVICE_SERIAL:
189 default:
190 pos = (uint8_t*) s;
191 while (*pos) {
192 blackboxWrite(*pos);
193 pos++;
196 length = pos - (uint8_t*) s;
197 break;
200 return length;
204 * If there is data waiting to be written to the blackbox device, attempt to write (a portion of) that now.
206 * Intended to be called regularly for the blackbox device to perform housekeeping.
208 void blackboxDeviceFlush(void)
210 switch (blackboxConfig()->device) {
211 #ifdef USE_FLASHFS
213 * This is our only output device which requires us to call flush() in order for it to write anything. The other
214 * devices will progressively write in the background without Blackbox calling anything.
216 case BLACKBOX_DEVICE_FLASH:
217 flashfsFlushAsync();
218 break;
219 #endif // USE_FLASHFS
221 default:
227 * If there is data waiting to be written to the blackbox device, attempt to write (a portion of) that now.
229 * Returns true if all data has been written to the device.
231 bool blackboxDeviceFlushForce(void)
233 switch (blackboxConfig()->device) {
234 case BLACKBOX_DEVICE_SERIAL:
235 // Nothing to speed up flushing on serial, as serial is continuously being drained out of its buffer
236 return isSerialTransmitBufferEmpty(blackboxPort);
238 #ifdef USE_FLASHFS
239 case BLACKBOX_DEVICE_FLASH:
240 return flashfsFlushAsync();
241 #endif // USE_FLASHFS
243 #ifdef USE_SDCARD
244 case BLACKBOX_DEVICE_SDCARD:
245 // SD card will flush itself without us calling it, but we need to call flush manually in order to check
246 // if it's done yet or not!
247 // However the "flush" only queues one dirty sector each time and the process is asynchronous. So after
248 // the last dirty sector is queued the flush returns true even though the sector may not actually have
249 // been physically written to the SD card yet.
250 return afatfs_flush();
251 #endif // USE_SDCARD
253 default:
254 return false;
258 // Flush the blackbox device and only return true if sync is actually complete.
259 // Primarily to ensure the async operations of SD card sector writes complete thus freeing the cache entries.
260 bool blackboxDeviceFlushForceComplete(void)
262 switch (blackboxConfig()->device) {
263 #ifdef USE_SDCARD
264 case BLACKBOX_DEVICE_SDCARD:
265 if (afatfs_sectorCacheInSync()) {
266 return true;
267 } else {
268 blackboxDeviceFlushForce();
269 return false;
271 #endif // USE_SDCARD
273 default:
274 return blackboxDeviceFlushForce();
279 * Attempt to open the logging device. Returns true if successful.
281 bool blackboxDeviceOpen(void)
283 switch (blackboxConfig()->device) {
284 case BLACKBOX_DEVICE_SERIAL:
286 const serialPortConfig_t *portConfig = findSerialPortConfig(FUNCTION_BLACKBOX);
287 baudRate_e baudRateIndex;
288 portOptions_e portOptions = SERIAL_PARITY_NO | SERIAL_NOT_INVERTED;
290 if (!portConfig) {
291 return false;
294 blackboxPortSharing = determinePortSharing(portConfig, FUNCTION_BLACKBOX);
295 baudRateIndex = portConfig->blackbox_baudrateIndex;
297 if (baudRates[baudRateIndex] == 230400) {
299 * OpenLog's 230400 baud rate is very inaccurate, so it requires a larger inter-character gap in
300 * order to maintain synchronization.
302 portOptions |= SERIAL_STOPBITS_2;
303 } else {
304 portOptions |= SERIAL_STOPBITS_1;
307 blackboxPort = openSerialPort(portConfig->identifier, FUNCTION_BLACKBOX, NULL, NULL, baudRates[baudRateIndex],
308 BLACKBOX_SERIAL_PORT_MODE, portOptions);
311 * The slowest MicroSD cards have a write latency approaching 400ms. The OpenLog's buffer is about 900
312 * bytes. In order for its buffer to be able to absorb this latency we must write slower than 6000 B/s.
314 * The OpenLager has a 125KB buffer for when the the MicroSD card is busy, so when the user configures
315 * high baud rates, assume the OpenLager is in use and so there is no need to constrain the writes.
317 * In all other cases, constrain the writes as follows:
319 * Bytes per loop iteration = floor((looptime_ns / 1000000.0) * 6000)
320 * = floor((looptime_ns * 6000) / 1000000.0)
321 * = floor((looptime_ns * 3) / 500.0)
322 * = (looptime_ns * 3) / 500
326 switch (baudRateIndex) {
327 case BAUD_1000000:
328 case BAUD_1500000:
329 case BAUD_2000000:
330 case BAUD_2470000:
331 // assume OpenLager in use, so do not constrain writes
332 blackboxMaxHeaderBytesPerIteration = BLACKBOX_TARGET_HEADER_BUDGET_PER_ITERATION;
333 break;
334 default:
335 blackboxMaxHeaderBytesPerIteration = constrain((targetPidLooptime * 3) / 500, 1, BLACKBOX_TARGET_HEADER_BUDGET_PER_ITERATION);
336 break;
339 return blackboxPort != NULL;
341 break;
342 #ifdef USE_FLASHFS
343 case BLACKBOX_DEVICE_FLASH:
344 if (!flashfsIsSupported() || isBlackboxDeviceFull()) {
345 return false;
348 blackboxMaxHeaderBytesPerIteration = BLACKBOX_TARGET_HEADER_BUDGET_PER_ITERATION;
350 return true;
351 break;
352 #endif // USE_FLASHFS
353 #ifdef USE_SDCARD
354 case BLACKBOX_DEVICE_SDCARD:
355 if (afatfs_getFilesystemState() == AFATFS_FILESYSTEM_STATE_FATAL || afatfs_getFilesystemState() == AFATFS_FILESYSTEM_STATE_UNKNOWN || afatfs_isFull()) {
356 return false;
359 blackboxMaxHeaderBytesPerIteration = BLACKBOX_TARGET_HEADER_BUDGET_PER_ITERATION;
361 return true;
362 break;
363 #endif // USE_SDCARD
364 default:
365 return false;
370 * Erase all blackbox logs
372 #ifdef USE_FLASHFS
373 void blackboxEraseAll(void)
375 switch (blackboxConfig()->device) {
376 case BLACKBOX_DEVICE_FLASH:
377 /* Stop the recorder as if blackbox_mode = ALWAYS it will attempt to resume writing after
378 * the erase and leave a corrupted first log.
379 * Possible enhancement here is to restart logging after erase.
381 blackboxInit();
382 flashfsEraseCompletely();
383 break;
384 default:
385 //not supported
386 break;
391 * Check to see if erasing is done
393 bool isBlackboxErased(void)
395 switch (blackboxConfig()->device) {
396 case BLACKBOX_DEVICE_FLASH:
397 return flashfsIsReady();
398 break;
399 default:
400 //not supported
401 return true;
402 break;
405 #endif
408 * Close the Blackbox logging device.
410 void blackboxDeviceClose(void)
412 switch (blackboxConfig()->device) {
413 case BLACKBOX_DEVICE_SERIAL:
414 // Can immediately close without attempting to flush any remaining data.
415 // Since the serial port could be shared with other processes, we have to give it back here
416 closeSerialPort(blackboxPort);
417 blackboxPort = NULL;
420 * Normally this would be handled by mw.c, but since we take an unknown amount
421 * of time to shut down asynchronously, we're the only ones that know when to call it.
423 if (blackboxPortSharing == PORTSHARING_SHARED) {
424 mspSerialAllocatePorts();
426 break;
427 #ifdef USE_FLASHFS
428 case BLACKBOX_DEVICE_FLASH:
429 // Some flash device, e.g., NAND devices, require explicit close to flush internally buffered data.
430 flashfsClose();
431 break;
432 #endif
433 default:
438 #ifdef USE_SDCARD
440 static void blackboxLogDirCreated(afatfsFilePtr_t directory)
442 if (directory) {
443 blackboxSDCard.logDirectory = directory;
445 afatfs_findFirst(blackboxSDCard.logDirectory, &blackboxSDCard.logDirectoryFinder);
447 blackboxSDCard.state = BLACKBOX_SDCARD_ENUMERATE_FILES;
448 } else {
449 // Retry
450 blackboxSDCard.state = BLACKBOX_SDCARD_INITIAL;
454 static void blackboxLogFileCreated(afatfsFilePtr_t file)
456 if (file) {
457 blackboxSDCard.logFile = file;
459 blackboxSDCard.largestLogFileNumber++;
461 blackboxSDCard.state = BLACKBOX_SDCARD_READY_TO_LOG;
462 } else {
463 // Retry
464 blackboxSDCard.state = BLACKBOX_SDCARD_READY_TO_CREATE_LOG;
468 static void blackboxCreateLogFile(void)
470 int32_t remainder = blackboxSDCard.largestLogFileNumber + 1;
472 char filename[] = LOGFILE_PREFIX "00000." LOGFILE_SUFFIX;
474 for (int i = 7; i >= 3; i--) {
475 filename[i] = (remainder % 10) + '0';
476 remainder /= 10;
479 blackboxSDCard.state = BLACKBOX_SDCARD_WAITING;
481 afatfs_fopen(filename, "as", blackboxLogFileCreated);
485 * Begin a new log on the SDCard.
487 * Keep calling until the function returns true (open is complete).
489 static bool blackboxSDCardBeginLog(void)
491 fatDirectoryEntry_t *directoryEntry;
493 doMore:
494 switch (blackboxSDCard.state) {
495 case BLACKBOX_SDCARD_INITIAL:
496 if (afatfs_getFilesystemState() == AFATFS_FILESYSTEM_STATE_READY) {
497 blackboxSDCard.state = BLACKBOX_SDCARD_WAITING;
499 afatfs_mkdir("logs", blackboxLogDirCreated);
501 break;
503 case BLACKBOX_SDCARD_WAITING:
504 // Waiting for directory entry to be created
505 break;
507 case BLACKBOX_SDCARD_ENUMERATE_FILES:
508 while (afatfs_findNext(blackboxSDCard.logDirectory, &blackboxSDCard.logDirectoryFinder, &directoryEntry) == AFATFS_OPERATION_SUCCESS) {
509 if (directoryEntry && !fat_isDirectoryEntryTerminator(directoryEntry)) {
510 // If this is a log file, parse the log number from the filename
511 if (strncmp(directoryEntry->filename, LOGFILE_PREFIX, strlen(LOGFILE_PREFIX)) == 0
512 && strncmp(directoryEntry->filename + 8, LOGFILE_SUFFIX, strlen(LOGFILE_SUFFIX)) == 0) {
513 char logSequenceNumberString[6];
515 memcpy(logSequenceNumberString, directoryEntry->filename + 3, 5);
516 logSequenceNumberString[5] = '\0';
518 blackboxSDCard.largestLogFileNumber = MAX((int32_t)atoi(logSequenceNumberString), blackboxSDCard.largestLogFileNumber);
520 } else {
521 // We're done checking all the files on the card, now we can create a new log file
522 afatfs_findLast(blackboxSDCard.logDirectory);
524 blackboxSDCard.state = BLACKBOX_SDCARD_CHANGE_INTO_LOG_DIRECTORY;
525 goto doMore;
528 break;
530 case BLACKBOX_SDCARD_CHANGE_INTO_LOG_DIRECTORY:
531 // Change into the log directory:
532 if (afatfs_chdir(blackboxSDCard.logDirectory)) {
533 // We no longer need our open handle on the log directory
534 afatfs_fclose(blackboxSDCard.logDirectory, NULL);
535 blackboxSDCard.logDirectory = NULL;
537 blackboxSDCard.state = BLACKBOX_SDCARD_READY_TO_CREATE_LOG;
538 goto doMore;
540 break;
542 case BLACKBOX_SDCARD_READY_TO_CREATE_LOG:
543 blackboxCreateLogFile();
544 break;
546 case BLACKBOX_SDCARD_READY_TO_LOG:
547 return true; // Log has been created!
550 // Not finished init yet
551 return false;
554 #endif // USE_SDCARD
557 * Begin a new log (for devices which support separations between the logs of multiple flights).
559 * Keep calling until the function returns true (open is complete).
561 bool blackboxDeviceBeginLog(void)
563 switch (blackboxConfig()->device) {
564 #ifdef USE_SDCARD
565 case BLACKBOX_DEVICE_SDCARD:
566 return blackboxSDCardBeginLog();
567 #endif // USE_SDCARD
568 default:
569 return true;
575 * Terminate the current log (for devices which support separations between the logs of multiple flights).
577 * retainLog - Pass true if the log should be kept, or false if the log should be discarded (if supported).
579 * Keep calling until this returns true
581 bool blackboxDeviceEndLog(bool retainLog)
583 #ifndef USE_SDCARD
584 UNUSED(retainLog);
585 #endif
587 switch (blackboxConfig()->device) {
588 #ifdef USE_SDCARD
589 case BLACKBOX_DEVICE_SDCARD:
590 // Keep retrying until the close operation queues
591 if (
592 (retainLog && afatfs_fclose(blackboxSDCard.logFile, NULL))
593 || (!retainLog && afatfs_funlink(blackboxSDCard.logFile, NULL))
595 // Don't bother waiting the for the close to complete, it's queued now and will complete eventually
596 blackboxSDCard.logFile = NULL;
597 blackboxSDCard.state = BLACKBOX_SDCARD_READY_TO_CREATE_LOG;
598 return true;
600 return false;
601 #endif // USE_SDCARD
602 default:
603 return true;
607 bool isBlackboxDeviceFull(void)
609 switch (blackboxConfig()->device) {
610 case BLACKBOX_DEVICE_SERIAL:
611 return false;
613 #ifdef USE_FLASHFS
614 case BLACKBOX_DEVICE_FLASH:
615 return flashfsIsEOF();
616 #endif // USE_FLASHFS
618 #ifdef USE_SDCARD
619 case BLACKBOX_DEVICE_SDCARD:
620 return afatfs_isFull();
621 #endif // USE_SDCARD
623 default:
624 return false;
628 bool isBlackboxDeviceWorking(void)
630 switch (blackboxConfig()->device) {
631 case BLACKBOX_DEVICE_SERIAL:
632 return blackboxPort != NULL;
634 #ifdef USE_SDCARD
635 case BLACKBOX_DEVICE_SDCARD:
636 return sdcard_isInserted() && sdcard_isFunctional() && (afatfs_getFilesystemState() == AFATFS_FILESYSTEM_STATE_READY);
637 #endif
639 #ifdef USE_FLASHFS
640 case BLACKBOX_DEVICE_FLASH:
641 return flashfsIsReady();
642 #endif
644 default:
645 return false;
649 int32_t blackboxGetLogNumber(void)
651 switch (blackboxConfig()->device) {
652 #ifdef USE_SDCARD
653 case BLACKBOX_DEVICE_SDCARD:
654 return blackboxSDCard.largestLogFileNumber;
655 #endif
657 default:
658 return -1;
663 * Call once every loop iteration in order to maintain the global blackboxHeaderBudget with the number of bytes we can
664 * transmit this iteration.
666 void blackboxReplenishHeaderBudget(void)
668 int32_t freeSpace;
670 switch (blackboxConfig()->device) {
671 case BLACKBOX_DEVICE_SERIAL:
672 freeSpace = serialTxBytesFree(blackboxPort);
673 break;
674 #ifdef USE_FLASHFS
675 case BLACKBOX_DEVICE_FLASH:
676 freeSpace = flashfsGetWriteBufferFreeSpace();
677 break;
678 #endif
679 #ifdef USE_SDCARD
680 case BLACKBOX_DEVICE_SDCARD:
681 freeSpace = afatfs_getFreeBufferSpace();
682 break;
683 #endif
684 default:
685 freeSpace = 0;
687 blackboxHeaderBudget = MIN(MIN(freeSpace, blackboxHeaderBudget + blackboxMaxHeaderBytesPerIteration), BLACKBOX_MAX_ACCUMULATED_HEADER_BUDGET);
691 * You must call this function before attempting to write Blackbox header bytes to ensure that the write will not
692 * cause buffers to overflow. The number of bytes you can write is capped by the blackboxHeaderBudget. Calling this
693 * reservation function doesn't decrease blackboxHeaderBudget, so you must manually decrement that variable by the
694 * number of bytes you actually wrote.
696 * When the Blackbox device is FlashFS, a successful return code guarantees that no data will be lost if you write that
697 * many bytes to the device (i.e. FlashFS's buffers won't overflow).
699 * When the device is a serial port, a successful return code guarantees that Cleanflight's serial Tx buffer will not
700 * overflow, and the outgoing bandwidth is likely to be small enough to give the OpenLog time to absorb MicroSD card
701 * latency. However the OpenLog could still end up silently dropping data.
703 * Returns:
704 * BLACKBOX_RESERVE_SUCCESS - Upon success
705 * BLACKBOX_RESERVE_TEMPORARY_FAILURE - The buffer is currently too full to service the request, try again later
706 * BLACKBOX_RESERVE_PERMANENT_FAILURE - The buffer is too small to ever service this request
708 blackboxBufferReserveStatus_e blackboxDeviceReserveBufferSpace(int32_t bytes)
710 if (bytes <= blackboxHeaderBudget) {
711 return BLACKBOX_RESERVE_SUCCESS;
714 // Handle failure:
715 switch (blackboxConfig()->device) {
716 case BLACKBOX_DEVICE_SERIAL:
718 * One byte of the tx buffer isn't available for user data (due to its circular list implementation),
719 * hence the -1. Note that the USB VCP implementation doesn't use a buffer and has txBufferSize set to zero.
721 if (blackboxPort->txBufferSize && bytes > (int32_t) blackboxPort->txBufferSize - 1) {
722 return BLACKBOX_RESERVE_PERMANENT_FAILURE;
724 return BLACKBOX_RESERVE_TEMPORARY_FAILURE;
726 #ifdef USE_FLASHFS
727 case BLACKBOX_DEVICE_FLASH:
728 if (bytes > (int32_t) flashfsGetWriteBufferSize()) {
729 return BLACKBOX_RESERVE_PERMANENT_FAILURE;
732 if (bytes > (int32_t) flashfsGetWriteBufferFreeSpace()) {
734 * The write doesn't currently fit in the buffer, so try to make room for it. Our flushing here means
735 * that the Blackbox header writing code doesn't have to guess about the best time to ask flashfs to
736 * flush, and doesn't stall waiting for a flush that would otherwise not automatically be called.
738 flashfsFlushAsync();
740 return BLACKBOX_RESERVE_TEMPORARY_FAILURE;
741 #endif // USE_FLASHFS
743 #ifdef USE_SDCARD
744 case BLACKBOX_DEVICE_SDCARD:
745 // Assume that all writes will fit in the SDCard's buffers
746 return BLACKBOX_RESERVE_TEMPORARY_FAILURE;
747 #endif // USE_SDCARD
749 default:
750 return BLACKBOX_RESERVE_PERMANENT_FAILURE;
754 int8_t blackboxGetLogFileNo(void)
756 #ifdef USE_BLACKBOX
757 #ifdef USE_SDCARD
758 // return current file number or -1
759 if (blackboxSDCard.state == BLACKBOX_SDCARD_READY_TO_LOG) {
760 return blackboxSDCard.largestLogFileNumber;
761 } else {
762 return -1;
764 #else
765 // will be implemented later for flash based storage
766 return -1;
767 #endif
768 #endif
770 #endif // BLACKBOX