Fix WS2812 led definition
[inav.git] / src / main / blackbox / blackbox_io.c
blob7a6f04149d3f9e56ee8eb833042c9318b4dc7175
1 /*
2 * This file is part of Cleanflight.
4 * Cleanflight is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * Cleanflight is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
18 #include <stdlib.h>
19 #include <stdarg.h>
20 #include <string.h>
22 #include "platform.h"
24 #ifdef USE_BLACKBOX
26 #include "blackbox.h"
27 #include "blackbox_io.h"
29 #include "common/axis.h"
30 #include "common/encoding.h"
31 #include "common/maths.h"
32 #include "common/printf.h"
33 #include "common/typeconversion.h"
35 #include "config/parameter_group.h"
36 #include "config/parameter_group_ids.h"
38 #include "io/asyncfatfs/asyncfatfs.h"
39 #include "io/flashfs.h"
40 #include "io/serial.h"
42 #include "msp/msp_serial.h"
44 #include "sensors/gyro.h"
45 #include "fc/config.h"
47 #define BLACKBOX_SERIAL_PORT_MODE MODE_TX
49 // How many bytes can we transmit per loop iteration when writing headers?
50 static uint8_t blackboxMaxHeaderBytesPerIteration;
52 // How many bytes can we write *this* iteration without overflowing transmit buffers or overstressing the OpenLog?
53 int32_t blackboxHeaderBudget;
55 STATIC_UNIT_TESTED serialPort_t *blackboxPort = NULL;
56 #ifndef UNIT_TEST
57 static portSharing_e blackboxPortSharing;
58 #endif // UNIT_TEST
60 #ifdef USE_SDCARD
62 static struct {
63 afatfsFilePtr_t logFile;
64 afatfsFilePtr_t logDirectory;
65 afatfsFinder_t logDirectoryFinder;
66 uint32_t largestLogFileNumber;
68 enum {
69 BLACKBOX_SDCARD_INITIAL,
70 BLACKBOX_SDCARD_WAITING,
71 BLACKBOX_SDCARD_ENUMERATE_FILES,
72 BLACKBOX_SDCARD_CHANGE_INTO_LOG_DIRECTORY,
73 BLACKBOX_SDCARD_READY_TO_CREATE_LOG,
74 BLACKBOX_SDCARD_READY_TO_LOG
75 } state;
76 } blackboxSDCard;
78 #endif
80 #ifndef UNIT_TEST
81 void blackboxOpen(void)
83 serialPort_t *sharedBlackboxAndMspPort = findSharedSerialPort(FUNCTION_BLACKBOX, FUNCTION_MSP);
84 if (sharedBlackboxAndMspPort) {
85 mspSerialReleasePortIfAllocated(sharedBlackboxAndMspPort);
88 #endif // UNIT_TEST
90 void blackboxWrite(uint8_t value)
92 switch (blackboxConfig()->device) {
93 #ifdef USE_FLASHFS
94 case BLACKBOX_DEVICE_FLASH:
95 flashfsWriteByte(value); // Write byte asynchronously
96 break;
97 #endif
98 #ifdef USE_SDCARD
99 case BLACKBOX_DEVICE_SDCARD:
100 afatfs_fputc(blackboxSDCard.logFile, value);
101 break;
102 #endif
103 case BLACKBOX_DEVICE_SERIAL:
104 default:
105 serialWrite(blackboxPort, value);
106 break;
110 // Print the null-terminated string 's' to the blackbox device and return the number of bytes written
111 int blackboxPrint(const char *s)
113 int length;
114 const uint8_t *pos;
116 switch (blackboxConfig()->device) {
118 #ifdef USE_FLASHFS
119 case BLACKBOX_DEVICE_FLASH:
120 length = strlen(s);
121 flashfsWrite((const uint8_t*) s, length, false); // Write asynchronously
122 break;
123 #endif
125 #ifdef USE_SDCARD
126 case BLACKBOX_DEVICE_SDCARD:
127 length = strlen(s);
128 afatfs_fwrite(blackboxSDCard.logFile, (const uint8_t*) s, length); // Ignore failures due to buffers filling up
129 break;
130 #endif
132 case BLACKBOX_DEVICE_SERIAL:
133 default:
134 pos = (uint8_t*) s;
135 while (*pos) {
136 serialWrite(blackboxPort, *pos);
137 pos++;
140 length = pos - (uint8_t*) s;
141 break;
144 return length;
148 * If there is data waiting to be written to the blackbox device, attempt to write (a portion of) that now.
150 * Intended to be called regularly for the blackbox device to perform housekeeping.
152 void blackboxDeviceFlush(void)
154 switch (blackboxConfig()->device) {
155 #ifdef USE_FLASHFS
157 * This is our only output device which requires us to call flush() in order for it to write anything. The other
158 * devices will progressively write in the background without Blackbox calling anything.
160 case BLACKBOX_DEVICE_FLASH:
161 flashfsFlushAsync();
162 break;
163 #endif
165 default:
171 * If there is data waiting to be written to the blackbox device, attempt to write (a portion of) that now.
173 * Returns true if all data has been written to the device.
175 bool blackboxDeviceFlushForce(void)
177 switch (blackboxConfig()->device) {
178 case BLACKBOX_DEVICE_SERIAL:
179 // Nothing to speed up flushing on serial, as serial is continuously being drained out of its buffer
180 return isSerialTransmitBufferEmpty(blackboxPort);
182 #ifdef USE_FLASHFS
183 case BLACKBOX_DEVICE_FLASH:
184 return flashfsFlushAsync();
185 #endif
187 #ifdef USE_SDCARD
188 case BLACKBOX_DEVICE_SDCARD:
189 /* SD card will flush itself without us calling it, but we need to call flush manually in order to check
190 * if it's done yet or not!
192 return afatfs_flush();
193 #endif
195 default:
196 return false;
201 * Attempt to open the logging device. Returns true if successful.
203 #ifndef UNIT_TEST
204 bool blackboxDeviceOpen(void)
206 switch (blackboxConfig()->device) {
207 case BLACKBOX_DEVICE_SERIAL:
209 serialPortConfig_t *portConfig = findSerialPortConfig(FUNCTION_BLACKBOX);
210 baudRate_e baudRateIndex;
211 portOptions_t portOptions = SERIAL_PARITY_NO | SERIAL_NOT_INVERTED;
213 if (!portConfig) {
214 return false;
217 blackboxPortSharing = determinePortSharing(portConfig, FUNCTION_BLACKBOX);
218 baudRateIndex = portConfig->peripheral_baudrateIndex;
220 if (baudRates[baudRateIndex] == 230400) {
222 * OpenLog's 230400 baud rate is very inaccurate, so it requires a larger inter-character gap in
223 * order to maintain synchronization.
225 portOptions |= SERIAL_STOPBITS_2;
226 } else {
227 portOptions |= SERIAL_STOPBITS_1;
230 blackboxPort = openSerialPort(portConfig->identifier, FUNCTION_BLACKBOX, NULL, NULL, baudRates[baudRateIndex],
231 BLACKBOX_SERIAL_PORT_MODE, portOptions);
234 * The slowest MicroSD cards have a write latency approaching 150ms. The OpenLog's buffer is about 900
235 * bytes. In order for its buffer to be able to absorb this latency we must write slower than 6000 B/s.
237 * So:
238 * Bytes per loop iteration = floor((looptime_ns / 1000000.0) * 6000)
239 * = floor((looptime_ns * 6000) / 1000000.0)
240 * = floor((looptime_ns * 3) / 500.0)
241 * = (looptime_ns * 3) / 500
243 blackboxMaxHeaderBytesPerIteration = constrain((getLooptime() * 3) / 500, 1, BLACKBOX_TARGET_HEADER_BUDGET_PER_ITERATION);
245 return blackboxPort != NULL;
247 break;
248 #ifdef USE_FLASHFS
249 case BLACKBOX_DEVICE_FLASH:
250 if (flashfsGetSize() == 0 || isBlackboxDeviceFull()) {
251 return false;
254 blackboxMaxHeaderBytesPerIteration = BLACKBOX_TARGET_HEADER_BUDGET_PER_ITERATION;
256 return true;
257 break;
258 #endif
259 #ifdef USE_SDCARD
260 case BLACKBOX_DEVICE_SDCARD:
261 if (afatfs_getFilesystemState() == AFATFS_FILESYSTEM_STATE_FATAL || afatfs_getFilesystemState() == AFATFS_FILESYSTEM_STATE_UNKNOWN || afatfs_isFull()) {
262 return false;
265 blackboxMaxHeaderBytesPerIteration = BLACKBOX_TARGET_HEADER_BUDGET_PER_ITERATION;
267 return true;
268 break;
269 #endif
270 default:
271 return false;
274 #endif // UNIT_TEST
277 * Close the Blackbox logging device immediately without attempting to flush any remaining data.
279 #ifndef UNIT_TEST
280 void blackboxDeviceClose(void)
282 switch (blackboxConfig()->device) {
283 case BLACKBOX_DEVICE_SERIAL:
284 // Since the serial port could be shared with other processes, we have to give it back here
285 closeSerialPort(blackboxPort);
286 blackboxPort = NULL;
289 * Normally this would be handled by mw.c, but since we take an unknown amount
290 * of time to shut down asynchronously, we're the only ones that know when to call it.
292 if (blackboxPortSharing == PORTSHARING_SHARED) {
293 mspSerialAllocatePorts();
295 break;
296 #ifdef USE_FLASHFS
297 case BLACKBOX_DEVICE_FLASH:
298 // Some flash device, e.g., NAND devices, require explicit close to flush internally buffered data.
299 flashfsClose();
300 break;
301 #endif
302 default:
306 #endif // UNIT_TEST
308 #ifdef USE_SDCARD
310 static void blackboxLogDirCreated(afatfsFilePtr_t directory)
312 if (directory) {
313 blackboxSDCard.logDirectory = directory;
315 afatfs_findFirst(blackboxSDCard.logDirectory, &blackboxSDCard.logDirectoryFinder);
317 blackboxSDCard.state = BLACKBOX_SDCARD_ENUMERATE_FILES;
318 } else {
319 // Retry
320 blackboxSDCard.state = BLACKBOX_SDCARD_INITIAL;
324 static void blackboxLogFileCreated(afatfsFilePtr_t file)
326 if (file) {
327 blackboxSDCard.logFile = file;
329 blackboxSDCard.largestLogFileNumber++;
331 blackboxSDCard.state = BLACKBOX_SDCARD_READY_TO_LOG;
332 } else {
333 // Retry
334 blackboxSDCard.state = BLACKBOX_SDCARD_READY_TO_CREATE_LOG;
338 static void blackboxCreateLogFile(void)
340 uint32_t remainder = blackboxSDCard.largestLogFileNumber + 1;
342 char filename[13];
344 filename[0] = 'L';
345 filename[1] = 'O';
346 filename[2] = 'G';
348 for (int i = 7; i >= 3; i--) {
349 filename[i] = (remainder % 10) + '0';
350 remainder /= 10;
353 filename[8] = '.';
354 filename[9] = 'T';
355 filename[10] = 'X';
356 filename[11] = 'T';
357 filename[12] = 0;
359 blackboxSDCard.state = BLACKBOX_SDCARD_WAITING;
361 afatfs_fopen(filename, "as", blackboxLogFileCreated);
365 * Begin a new log on the SDCard.
367 * Keep calling until the function returns true (open is complete).
369 static bool blackboxSDCardBeginLog(void)
371 fatDirectoryEntry_t *directoryEntry;
373 doMore:
374 switch (blackboxSDCard.state) {
375 case BLACKBOX_SDCARD_INITIAL:
376 if (afatfs_getFilesystemState() == AFATFS_FILESYSTEM_STATE_READY) {
377 blackboxSDCard.state = BLACKBOX_SDCARD_WAITING;
379 afatfs_mkdir("logs", blackboxLogDirCreated);
381 break;
383 case BLACKBOX_SDCARD_WAITING:
384 // Waiting for directory entry to be created
385 break;
387 case BLACKBOX_SDCARD_ENUMERATE_FILES:
388 while (afatfs_findNext(blackboxSDCard.logDirectory, &blackboxSDCard.logDirectoryFinder, &directoryEntry) == AFATFS_OPERATION_SUCCESS) {
389 if (directoryEntry && !fat_isDirectoryEntryTerminator(directoryEntry)) {
390 // If this is a log file, parse the log number from the filename
391 if (
392 directoryEntry->filename[0] == 'L' && directoryEntry->filename[1] == 'O' && directoryEntry->filename[2] == 'G'
393 && directoryEntry->filename[8] == 'T' && directoryEntry->filename[9] == 'X' && directoryEntry->filename[10] == 'T'
395 char logSequenceNumberString[6];
397 memcpy(logSequenceNumberString, directoryEntry->filename + 3, 5);
398 logSequenceNumberString[5] = '\0';
400 blackboxSDCard.largestLogFileNumber = MAX((uint32_t) fastA2I(logSequenceNumberString), blackboxSDCard.largestLogFileNumber);
402 } else {
403 // We're done checking all the files on the card, now we can create a new log file
404 afatfs_findLast(blackboxSDCard.logDirectory);
406 blackboxSDCard.state = BLACKBOX_SDCARD_CHANGE_INTO_LOG_DIRECTORY;
407 goto doMore;
410 break;
412 case BLACKBOX_SDCARD_CHANGE_INTO_LOG_DIRECTORY:
413 // Change into the log directory:
414 if (afatfs_chdir(blackboxSDCard.logDirectory)) {
415 // We no longer need our open handle on the log directory
416 afatfs_fclose(blackboxSDCard.logDirectory, NULL);
417 blackboxSDCard.logDirectory = NULL;
419 blackboxSDCard.state = BLACKBOX_SDCARD_READY_TO_CREATE_LOG;
420 goto doMore;
422 break;
424 case BLACKBOX_SDCARD_READY_TO_CREATE_LOG:
425 blackboxCreateLogFile();
426 break;
428 case BLACKBOX_SDCARD_READY_TO_LOG:
429 return true; // Log has been created!
432 // Not finished init yet
433 return false;
436 #endif
439 * Begin a new log (for devices which support separations between the logs of multiple flights).
441 * Keep calling until the function returns true (open is complete).
443 bool blackboxDeviceBeginLog(void)
445 switch (blackboxConfig()->device) {
446 #ifdef USE_SDCARD
447 case BLACKBOX_DEVICE_SDCARD:
448 return blackboxSDCardBeginLog();
449 #endif
450 default:
451 return true;
457 * Terminate the current log (for devices which support separations between the logs of multiple flights).
459 * retainLog - Pass true if the log should be kept, or false if the log should be discarded (if supported).
461 * Keep calling until this returns true
463 bool blackboxDeviceEndLog(bool retainLog)
465 #ifndef USE_SDCARD
466 (void) retainLog;
467 #endif
469 switch (blackboxConfig()->device) {
470 #ifdef USE_SDCARD
471 case BLACKBOX_DEVICE_SDCARD:
472 // Keep retrying until the close operation queues
473 if (
474 (retainLog && afatfs_fclose(blackboxSDCard.logFile, NULL))
475 || (!retainLog && afatfs_funlink(blackboxSDCard.logFile, NULL))
477 // Don't bother waiting the for the close to complete, it's queued now and will complete eventually
478 blackboxSDCard.logFile = NULL;
479 blackboxSDCard.state = BLACKBOX_SDCARD_READY_TO_CREATE_LOG;
480 return true;
482 return false;
483 #endif
484 default:
485 return true;
489 bool isBlackboxDeviceFull(void)
491 switch (blackboxConfig()->device) {
492 case BLACKBOX_DEVICE_SERIAL:
493 return false;
495 #ifdef USE_FLASHFS
496 case BLACKBOX_DEVICE_FLASH:
497 return flashfsIsEOF();
498 #endif
500 #ifdef USE_SDCARD
501 case BLACKBOX_DEVICE_SDCARD:
502 return afatfs_isFull();
503 #endif
505 default:
506 return false;
511 * Call once every loop iteration in order to maintain the global blackboxHeaderBudget with the number of bytes we can
512 * transmit this iteration.
514 void blackboxReplenishHeaderBudget(void)
516 int32_t freeSpace;
518 switch (blackboxConfig()->device) {
519 case BLACKBOX_DEVICE_SERIAL:
520 freeSpace = serialTxBytesFree(blackboxPort);
521 break;
522 #ifdef USE_FLASHFS
523 case BLACKBOX_DEVICE_FLASH:
524 freeSpace = flashfsGetWriteBufferFreeSpace();
525 break;
526 #endif
527 #ifdef USE_SDCARD
528 case BLACKBOX_DEVICE_SDCARD:
529 freeSpace = afatfs_getFreeBufferSpace();
530 break;
531 #endif
532 default:
533 freeSpace = 0;
536 blackboxHeaderBudget = MIN(MIN(freeSpace, blackboxHeaderBudget + blackboxMaxHeaderBytesPerIteration), BLACKBOX_MAX_ACCUMULATED_HEADER_BUDGET);
540 * You must call this function before attempting to write Blackbox header bytes to ensure that the write will not
541 * cause buffers to overflow. The number of bytes you can write is capped by the blackboxHeaderBudget. Calling this
542 * reservation function doesn't decrease blackboxHeaderBudget, so you must manually decrement that variable by the
543 * number of bytes you actually wrote.
545 * When the Blackbox device is FlashFS, a successful return code guarantees that no data will be lost if you write that
546 * many bytes to the device (i.e. FlashFS's buffers won't overflow).
548 * When the device is a serial port, a successful return code guarantees that Cleanflight's serial Tx buffer will not
549 * overflow, and the outgoing bandwidth is likely to be small enough to give the OpenLog time to absorb MicroSD card
550 * latency. However the OpenLog could still end up silently dropping data.
552 * Returns:
553 * BLACKBOX_RESERVE_SUCCESS - Upon success
554 * BLACKBOX_RESERVE_TEMPORARY_FAILURE - The buffer is currently too full to service the request, try again later
555 * BLACKBOX_RESERVE_PERMANENT_FAILURE - The buffer is too small to ever service this request
557 blackboxBufferReserveStatus_e blackboxDeviceReserveBufferSpace(int32_t bytes)
559 if (bytes <= blackboxHeaderBudget) {
560 return BLACKBOX_RESERVE_SUCCESS;
563 // Handle failure:
564 switch (blackboxConfig()->device) {
565 case BLACKBOX_DEVICE_SERIAL:
567 * One byte of the tx buffer isn't available for user data (due to its circular list implementation),
568 * hence the -1. Note that the USB VCP implementation doesn't use a buffer and has txBufferSize set to zero.
570 if (blackboxPort->txBufferSize && bytes > (int32_t) blackboxPort->txBufferSize - 1) {
571 return BLACKBOX_RESERVE_PERMANENT_FAILURE;
574 return BLACKBOX_RESERVE_TEMPORARY_FAILURE;
576 #ifdef USE_FLASHFS
577 case BLACKBOX_DEVICE_FLASH:
578 if (bytes > (int32_t) flashfsGetWriteBufferSize()) {
579 return BLACKBOX_RESERVE_PERMANENT_FAILURE;
582 if (bytes > (int32_t) flashfsGetWriteBufferFreeSpace()) {
584 * The write doesn't currently fit in the buffer, so try to make room for it. Our flushing here means
585 * that the Blackbox header writing code doesn't have to guess about the best time to ask flashfs to
586 * flush, and doesn't stall waiting for a flush that would otherwise not automatically be called.
588 flashfsFlushAsync();
591 return BLACKBOX_RESERVE_TEMPORARY_FAILURE;
592 #endif
594 #ifdef USE_SDCARD
595 case BLACKBOX_DEVICE_SDCARD:
596 // Assume that all writes will fit in the SDCard's buffers
597 return BLACKBOX_RESERVE_TEMPORARY_FAILURE;
598 #endif
600 default:
601 return BLACKBOX_RESERVE_PERMANENT_FAILURE;
605 #endif