Paparazzi UAS  v6.1.0_stable
Paparazzi is a free software Unmanned Aircraft System.
sdLog.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2013-2016 Gautier Hattenberger, Alexandre Bustico
3  *
4  * This file is part of paparazzi.
5  *
6  * paparazzi is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2, or (at your option)
9  * any later version.
10  *
11  * paparazzi is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with paparazzi; see the file COPYING. If not, write to
18  * the Free Software Foundation, 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21 
22 /*
23  * @file modules/loggers/sdlog_chibios/sdLog.c
24  * @brief sdlog API using ChibiOS and Fatfs
25  *
26  */
27 
28 #include <string.h>
29 #include <stdlib.h>
30 #include <ch.h>
31 #include <hal.h>
32 #include <ff.h>
34 #include "printf.h"
35 #include "mcu_periph/sdio.h"
36 #include <ctype.h>
37 
38 #define likely(x) __builtin_expect(!!(x), 1)
39 #define unlikely(x) __builtin_expect(!!(x), 0)
40 
41 #ifndef MIN
42 #define MIN(x , y) (((x) < (y)) ? (x) : (y))
43 #endif
44 #ifndef MAX
45 #define MAX(x , y) (((x) > (y)) ? (x) : (y))
46 #endif
47 #define IS_POWER_OF_TWO(s) ((s) && !((s) & ((s) - 1)))
48 
49 #ifndef SDLOG_NUM_FILES
50 #error SDLOG_NUM_FILES should be defined in mcuconf.h
51 #endif
52 
53 
54 #ifndef FFCONF_DEF
55 #define FFCONF_DEF _FATFS
56 #endif
57 
58 #if FFCONF_DEF < 8000
59 #error upgrade FATFS to 0.14 at least
60 #else // FFCONF_DEF > 8000
61 #if FF_FS_LOCK != 0 && FF_FS_LOCK < SDLOG_NUM_FILES
62 #error if FF_FS_LOCK is not zero, it should be equal of superior to SDLOG_NUM_FILES
63 #endif
64 #endif
65 
66 #ifndef SDLOG_ALL_BUFFERS_SIZE
67 #error SDLOG_ALL_BUFFERS_SIZE should be defined in mcuconf.h
68 #endif
69 
70 #if SDLOG_ALL_BUFFERS_SIZE > 65536
71 #error constraint 512 <= SDLOG_ALL_BUFFERS_SIZE <= 65536 not meet
72 #endif
73 
74 #define SDLOG_WRITE_BUFFER_SIZE (SDLOG_ALL_BUFFERS_SIZE/SDLOG_NUM_FILES)
75 
76 #ifndef SDLOG_MAX_MESSAGE_LEN
77 #error SDLOG_MAX_MESSAGE_LENshould be defined in mcuconf.h
78 #endif
79 
80 #ifndef SDLOG_QUEUE_BUCKETS
81 #error SDLOG_QUEUE_BUCKETS should be defined in mcuconf.h
82 #endif
83 
84 #if FF_FS_REENTRANT == 0
85 #warning "FF_FS_REENTRANT = 0 in ffconf.h DO NOT open close file during log"
86 #endif
87 
88 #if SDLOG_WRITE_BUFFER_SIZE < 512
89 #error SDLOG_ALL_BUFFERS_SIZE / SDLOG_NUM_FILES cannot be < 512
90 #endif
91 
92 #if (!(IS_POWER_OF_TWO (SDLOG_WRITE_BUFFER_SIZE)))
93 #error SDLOG_ALL_BUFFERS_SIZE / SDLOG_NUM_FILES should be a POWER OF 2
94 #endif
95 
96 #ifdef SDLOG_NEED_QUEUE
98 
99 #include "mcu_periph/ram_arch.h"
100 
101 
102 /*
103  The buffers that do DMA are the caches (named buf) in the FIL and FATFS struct of fatfs library
104  It's the only buffers that have to reside in DMA capable memory.
105 
106  The buffer associated with message queue, and the cache buffer for caching file write
107  could reside in non DMA capable memory.
108 
109  stm32f4 : regular sram : 128ko, dma, slow
110  ccm sram : 64ko, no_dma, fast
111 
112  stm32f7 : regular sram : 256ko, dma only possible if data cache are explicitely flushed, fast
113  dtcm sram : 64ko, dma, slow (no cache)
114  */
115 
116 
117 
118 static msg_t IN_STD_SECTION_CLEAR(queMbBuffer[SDLOG_QUEUE_BUCKETS]);
119 static MsgQueue messagesQueue;
120 
121 #define WRITE_BYTE_CACHE_SIZE 15 // limit overhead :
122 // malloc (15+1) occupies 20bytes
123 typedef struct {
124  uint8_t fcntl: 2;
125  uint8_t fd: 6;
126 } FileOp;
127 
128 struct LogMessage {
129  FileOp op;
130  char mess[0];
131 };
132 
133 struct FilePoolUnit {
134  FIL fil;
135  uint32_t autoFlushPeriod;
136  systime_t lastFlushTs;
137  bool inUse;
138  bool tagAtClose;
139  // optimise write byte by caching at send level now that we are based upon tlsf where
140  // allocation granularity is 16 bytes
141  LogMessage *writeByteCache;
142  uint8_t writeByteSeek;
143 };
144 
145 static struct FilePoolUnit IN_DMA_SECTION(fileDes[SDLOG_NUM_FILES]) = {
146  [0 ... SDLOG_NUM_FILES - 1] = {
147  .fil = {{0}}, .inUse = false, .tagAtClose = false,
148  .writeByteCache = NULL, .writeByteSeek = 0
149  }
150 };
151 
152 static volatile size_t nbBytesWritten = 0;
153 static SdioError storageStatus = SDLOG_OK;
154 
155 typedef enum {
156  FCNTL_WRITE = 0b00,
157  FCNTL_FLUSH = 0b01,
158  FCNTL_CLOSE = 0b10,
159  FCNTL_EXIT = 0b11
160 } FileFcntl;
161 
162 struct _SdLogBuffer {
163  LogMessage *lm;
164  size_t len;
166 } ;
167 
168 
169 #define LOG_MESSAGE_PREBUF_LEN (SDLOG_MAX_MESSAGE_LEN+sizeof(LogMessage))
170 #endif // SDLOG_NEED_QUEUE
171 
172 /* File system object */
173 static IN_DMA_SECTION(FATFS fatfs);
174 
175 #ifdef SDLOG_NEED_QUEUE
176 static size_t logMessageLen(const LogMessage *lm);
177 static size_t logRawLen(const size_t len);
178 static SdioError sdLoglaunchThread(void);
179 static SdioError sdLogStopThread(void);
180 static thread_t *sdLogThd = NULL;
181 static SdioError getNextFIL(FileDes *fd);
182 static void removeFromQueue(const size_t nbMsgToRFemov);
183 static void cleanQueue(const bool allQueue);
184 static SdioError sdLogExpandLogFile(const FileDes fileObject, const size_t sizeInMo,
185  const bool preallocate);
186 
187 static void thdSdLog(void *arg) ;
188 
189 #endif // SDLOG_NEED_QUEUE
190 
191 static int32_t uiGetIndexOfLogFile(const char *prefix, const char *fileName) ;
192 static inline SdioError flushWriteByteBuffer(const FileDes fd);
193 
194 SdioError sdLogInit(uint32_t *freeSpaceInKo)
195 {
196  DWORD clusters = 0;
197  FATFS *fsp = NULL;
198  nbBytesWritten = 0;
199 
200  // if init is already done, return ERROR
201  if (sdLogThd != NULL) {
202  *freeSpaceInKo = 0;
203  return storageStatus = SDLOG_WAS_LAUNCHED;
204  }
205 
206 #ifdef SDLOG_NEED_QUEUE
207  msgqueue_init(&messagesQueue, &HEAP_DEFAULT, queMbBuffer, SDLOG_QUEUE_BUCKETS);
208 #endif
209 
210  if (!sdc_lld_is_card_inserted(NULL)) {
211  return storageStatus = SDLOG_NOCARD;
212  }
213 
214 
215  sdio_connect();
216  chThdSleepMilliseconds(10);
217  sdio_disconnect();
218 
219  if (sdio_connect() == FALSE) {
220  return storageStatus = SDLOG_NOCARD;
221  }
222 
223 #if FFCONF_DEF < 8000
224  FRESULT rc = f_mount(0, &fatfs);
225 #else
226  FRESULT rc = f_mount(&fatfs, "/", 1);
227 #endif
228 
229  if (rc != FR_OK) {
230  return storageStatus = SDLOG_FATFS_ERROR;
231  }
232 
233  if (freeSpaceInKo != NULL) {
234  f_getfree("/", &clusters, &fsp);
235  *freeSpaceInKo = clusters * (uint32_t)fatfs.csize / 2;
236  }
237 
238 #ifdef SDLOG_NEED_QUEUE
239  for (uint8_t i = 0; i < SDLOG_NUM_FILES; i++) {
240  fileDes[i].inUse = fileDes[i].tagAtClose = false;
241  fileDes[i].writeByteCache = NULL;
242  fileDes[i].writeByteSeek = 0;
243  }
244 
245  storageStatus = sdLoglaunchThread();
246 #else
247  storageStatus = SDLOG_OK;
248 #endif
249  return storageStatus;
250 
251 }
252 
253 
255 {
256 #if FFCONF_DEF < 8000
257  FRESULT rc = f_mount(0, NULL);
258 #else
259  FRESULT rc = f_mount(NULL, "", 0);
260 #endif
261  if (rc != FR_OK) {
262  return storageStatus = SDLOG_FATFS_ERROR;
263  }
264 
265  // if we mount, unmount, don't disconnect sdio
266  /* if (sdio_disconnect () == FALSE) */
267  /* return SDLOG_NOCARD; */
268 
269  return storageStatus = SDLOG_OK;
270 }
271 
272 
273 
274 #ifdef SDLOG_NEED_QUEUE
275 SdioError sdLogOpenLog(FileDes *fd, const char *directoryName, const char *prefix,
276  const uint32_t autoFlushPeriod, const bool appendTagAtClose,
277  const size_t sizeInMo, const bool preallocate, char *fileName, const size_t nameLength)
278 {
279  FRESULT rc; /* fatfs result code */
280  SdioError sde = SDLOG_OK; /* sdio result code */
281  //DIR dir; /* Directory object */
282  //FILINFO fno; /* File information object */
283 
284  /* local file descriptor
285  using fd is a bad idea since fd is set before fatfs objets are coherents
286  in a multithreaded application where sdLogXXX are done before sdLogWriteLog is done
287  we can have a race condition. setting fd only when fatfs files are opened resolve the problem
288  */
289  FileDes ldf;
290 
291  sde = getNextFIL(&ldf);
292  if (sde != SDLOG_OK) {
293  return storageStatus = sde;
294  }
295 
296  sde = getFileName(prefix, directoryName, fileName, nameLength, +1);
297  if (sde != SDLOG_OK) {
298  // sd card is not inserted, so logging task can be deleted
299  return storageStatus = SDLOG_FATFS_ERROR;
300  }
301 
302 
303  rc = f_open(&fileDes[ldf].fil, fileName, FA_WRITE | FA_CREATE_ALWAYS);
304  if (rc) {
305  fileDes[ldf].inUse = false;
306  return storageStatus = SDLOG_FATFS_ERROR;
307  } else {
308  fileDes[ldf].tagAtClose = appendTagAtClose;
309  fileDes[ldf].autoFlushPeriod = autoFlushPeriod;
310  fileDes[ldf].lastFlushTs = 0;
311  sde = sdLogExpandLogFile(ldf, sizeInMo, preallocate);
312  }
313 
314  *fd = ldf;
315  return storageStatus = sde;
316 }
317 
318 SdioError sdLogCloseAllLogs(bool flush)
319 {
320  FRESULT rc = 0; /* Result code */
321 
322  // do not flush what is in ram, close as soon as possible
323  if (flush == false) {
324  UINT bw;
325  // stop worker thread then close file
326  cleanQueue(true);
327  sdLogStopThread();
328 
329  for (FileDes fd = 0; fd < SDLOG_NUM_FILES; fd++) {
330  if (fileDes[fd].inUse) {
331  FIL *fo = &fileDes[fd].fil;
332  if (fileDes[fd].tagAtClose) {
333  f_write(fo, "\r\nEND_OF_LOG\r\n", 14, &bw);
334  nbBytesWritten += bw;
335  }
336  FRESULT trc = f_close(fo);
337  fileDes[fd].inUse = false;
338  if (!rc) {
339  rc = trc;
340  }
341  }
342  }
343 
344  if (rc) {
345  return storageStatus = SDLOG_FATFS_ERROR;
346  }
347 
348  // flush ram buffer then close
349  } else { // flush == true
350  if (sdLogThd == NULL) {
351  // something goes wrong, log thread is no more working
352  return storageStatus = SDLOG_NOTHREAD;
353  }
354 
355  // queue flush + close order, then stop worker thread
356  for (FileDes fd = 0; fd < SDLOG_NUM_FILES; fd++) {
357  if (fileDes[fd].inUse) {
359  sdLogCloseLog(fd);
360  }
361  }
362 
363  LogMessage *lm = tlsf_malloc_r(&HEAP_DEFAULT, sizeof(LogMessage));
364  if (lm == NULL) {
365  return storageStatus = SDLOG_MEMFULL;
366  }
367 
368  lm->op.fcntl = FCNTL_EXIT;
369 
370  if (msgqueue_send(&messagesQueue, lm, sizeof(LogMessage), MsgQueue_REGULAR) < 0) {
371  return storageStatus = SDLOG_QUEUEFULL;
372  } else {
373  chThdWait(sdLogThd);
374  sdLogThd = NULL;
375  }
376 
377  }
378  return storageStatus = SDLOG_OK;
379 }
380 
381 SdioError sdLogFlushAllLogs(void)
382 {
384  if (sdLogThd == NULL) {
385  // something goes wrong, log thread is no more working
386  return storageStatus = SDLOG_NOTHREAD;
387  }
388 
389  // queue flush + close order, then stop worker thread
390  for (FileDes fd = 0; fd < SDLOG_NUM_FILES; fd++) {
391  if (fileDes[fd].inUse) {
392  status = sdLogFlushLog(fd);
393  if (status != SDLOG_OK) {
394  break;
395  }
396  }
397  }
398 
399  return storageStatus = status;
400 }
401 
402 
403 #define FD_CHECK(fd) if ((fd < 0) || (fd >= SDLOG_NUM_FILES) \
404  || (fileDes[fd].inUse == false)) \
405  return SDLOG_FATFS_ERROR
406 
407 
408 SdioError sdLogExpandLogFile(const FileDes fd, const size_t sizeInMo,
409  const bool preallocate)
410 {
411  FD_CHECK(fd);
412 
413  // expand with opt=1 : pre allocate file now
414  const FRESULT rc = f_expand(&fileDes[fd].fil, sizeInMo * 1024 * 1024, preallocate);
415  return (rc == FR_OK) ? SDLOG_OK : SDLOG_CANNOT_EXPAND;
416 }
417 
418 SdioError sdLogWriteLog(const FileDes fd, const char *fmt, ...)
419 {
420  FD_CHECK(fd);
421 
423  storageStatus = status;
424  if (status != SDLOG_OK) {
425  return status;
426  }
427 
428  va_list ap;
429  va_start(ap, fmt);
430 
431  LogMessage *lm = tlsf_malloc_r(&HEAP_DEFAULT, LOG_MESSAGE_PREBUF_LEN);
432  if (lm == NULL) {
433  va_end(ap);
434  return storageStatus = SDLOG_MEMFULL;
435  }
436 
437  lm->op.fcntl = FCNTL_WRITE;
438  lm->op.fd = fd & 0x1f;
439 
440  chvsnprintf(lm->mess, SDLOG_MAX_MESSAGE_LEN - 1, fmt, ap);
441  lm->mess[SDLOG_MAX_MESSAGE_LEN - 1] = 0;
442  va_end(ap);
443 
444  const size_t msgLen = logMessageLen(lm);
445  lm = tlsf_realloc_r(&HEAP_DEFAULT, lm, msgLen);
446  if (lm == NULL) {
447  return storageStatus = SDLOG_MEMFULL;
448  }
449 
450  if (msgqueue_send(&messagesQueue, lm, msgLen, MsgQueue_REGULAR) < 0) {
451  return storageStatus = SDLOG_QUEUEFULL;
452  }
453 
454  return SDLOG_OK;
455 }
456 
457 SdioError sdLogFlushLog(const FileDes fd)
458 {
459  FD_CHECK(fd);
460 
462  storageStatus = status;
463  if (status != SDLOG_OK) {
464  return status;
465  }
466 
467  // give room to send a flush order if the queue is full
468  cleanQueue(false);
469 
470  LogMessage *lm = tlsf_malloc_r(&HEAP_DEFAULT, sizeof(LogMessage));
471  if (lm == NULL) {
472  return storageStatus = SDLOG_MEMFULL;
473  }
474 
475  lm->op.fcntl = FCNTL_FLUSH;
476  lm->op.fd = fd & 0x1f;
477 
478  if (msgqueue_send(&messagesQueue, lm, sizeof(LogMessage), MsgQueue_REGULAR) < 0) {
479  return storageStatus = SDLOG_QUEUEFULL;
480  }
481 
482  return SDLOG_OK;
483 }
484 
485 SdioError sdLogCloseLog(const FileDes fd)
486 {
487  FD_CHECK(fd);
488 
489  cleanQueue(false);
490  LogMessage *lm = tlsf_malloc_r(&HEAP_DEFAULT, sizeof(LogMessage));
491  if (lm == NULL) {
492  return storageStatus = SDLOG_MEMFULL;
493  }
494 
495  lm->op.fcntl = FCNTL_CLOSE;
496  lm->op.fd = fd & 0x1f;
497 
498  if (msgqueue_send(&messagesQueue, lm, sizeof(LogMessage), MsgQueue_REGULAR) < 0) {
499  return storageStatus = SDLOG_QUEUEFULL;
500  }
501 
502  return storageStatus = SDLOG_OK;
503 }
504 
505 
506 
507 
508 
509 static inline SdioError flushWriteByteBuffer(const FileDes fd)
510 {
511  FD_CHECK(fd);
512 
513  if (unlikely(fileDes[fd].writeByteCache != NULL)) {
514  if (msgqueue_send(&messagesQueue, fileDes[fd].writeByteCache,
515  sizeof(LogMessage) + fileDes[fd].writeByteSeek,
516  MsgQueue_REGULAR) < 0) {
517  return storageStatus = SDLOG_QUEUEFULL;
518  }
519  fileDes[fd].writeByteCache = NULL;
520  }
521  return storageStatus = SDLOG_OK;
522 }
523 
524 SdioError sdLogWriteRaw(const FileDes fd, const uint8_t *buffer, const size_t len)
525 {
526  FD_CHECK(fd);
527 
529  storageStatus = status;
530  if (status != SDLOG_OK) {
531  return status;
532  }
533 
534  LogMessage *lm = tlsf_malloc_r(&HEAP_DEFAULT, logRawLen(len));
535  if (lm == NULL) {
536  return storageStatus = SDLOG_MEMFULL;
537  }
538 
539  lm->op.fcntl = FCNTL_WRITE;
540  lm->op.fd = fd & 0x1f;
541  memcpy(lm->mess, buffer, len);
542 
543  if (msgqueue_send(&messagesQueue, lm, logRawLen(len), MsgQueue_REGULAR) < 0) {
544  return storageStatus = SDLOG_QUEUEFULL;
545  }
546 
547  return SDLOG_OK;
548 }
549 
550 SdioError sdLogAllocSDB(SdLogBuffer **sdb, const size_t len)
551 {
552  *sdb = tlsf_malloc_r(&HEAP_DEFAULT, logRawLen(len));
553  if (*sdb == NULL) {
554  return storageStatus = SDLOG_MEMFULL;
555  }
556 
557  LogMessage *lm = tlsf_malloc_r(&HEAP_DEFAULT, logRawLen(len));
558  if (lm == NULL) {
559  tlsf_free_r(&HEAP_DEFAULT, *sdb);
560  return storageStatus = SDLOG_MEMFULL;
561  }
562 
563  (*sdb)->lm = lm;
564  (*sdb)->len = len;
565  (*sdb)->offset = 0;
566  return storageStatus = SDLOG_OK;
567 }
568 
569 char *sdLogGetBufferFromSDB(SdLogBuffer *sdb)
570 {
571  return sdb->lm->mess + sdb->offset;
572 }
573 
574 bool sdLogSeekBufferFromSDB(SdLogBuffer *sdb, uint32_t offset)
575 {
576  if ((sdb->offset + offset) < sdb->len) {
577  sdb->offset += offset;
578  return true;
579  } else {
580  return false;
581  }
582 }
583 
584 size_t sdLogGetBufferLenFromSDB(SdLogBuffer *sdb)
585 {
586  return sdb->len - sdb->offset;
587 }
588 
589 SdioError sdLogWriteSDB(const FileDes fd, SdLogBuffer *sdb)
590 {
592 
593  if ((fd < 0) || (fd >= SDLOG_NUM_FILES) || (fileDes[fd].inUse == false)) {
595  goto fail;
596  }
597 
599  if (status != SDLOG_OK) {
600  goto fail;
601  }
602 
603 
604  sdb->lm->op.fcntl = FCNTL_WRITE;
605  sdb->lm->op.fd = fd & 0x1f;
606 
607  if (msgqueue_send(&messagesQueue, sdb->lm, logRawLen(sdb->len), MsgQueue_REGULAR) < 0) {
608  // msgqueue_send take care of freeing lm memory even in case of failure
609  // just need to free sdb memory
611  }
612 
613  goto exit;
614 
615 fail:
616  tlsf_free_r(&HEAP_DEFAULT, sdb->lm);
617 
618 exit:
619  tlsf_free_r(&HEAP_DEFAULT, sdb);
620  return storageStatus = status;
621 }
622 
623 
624 
625 
626 SdioError sdLogWriteByte(const FileDes fd, const uint8_t value)
627 {
628  FD_CHECK(fd);
629  LogMessage *lm;
630 
631  if (fileDes[fd].writeByteCache == NULL) {
632  lm = tlsf_malloc_r(&HEAP_DEFAULT, sizeof(LogMessage) + WRITE_BYTE_CACHE_SIZE);
633  if (lm == NULL) {
634  return storageStatus = SDLOG_MEMFULL;
635  }
636 
637  lm->op.fcntl = FCNTL_WRITE;
638  lm->op.fd = fd & 0x1f;
639 
640  fileDes[fd].writeByteCache = lm;
641  fileDes[fd].writeByteSeek = 0;
642  } else {
643  lm = fileDes[fd].writeByteCache;
644  }
645 
646  lm->mess[fileDes[fd].writeByteSeek++] = value;
647 
648  if (fileDes[fd].writeByteSeek == WRITE_BYTE_CACHE_SIZE) {
650  // message is not sent so allocated buffer will not be released by receiver side
651  // instead of freeing buffer, we just reset cache seek.
652  if (status == SDLOG_QUEUEFULL) {
653  fileDes[fd].writeByteSeek = 0;
654  return storageStatus = SDLOG_QUEUEFULL;
655  }
656  }
657  return storageStatus = SDLOG_OK;
658 }
659 
660 /*
661  if fatfs use stack for working buffers, stack size should be reserved accordingly
662  */
663 #define WA_LOG_BASE_SIZE 1024
664 #if FF_USE_LFN == 2
665 #if FF_FS_EXFAT
666 static IN_DMA_SECTION_NOINIT(THD_WORKING_AREA(waThdSdLog, WA_LOG_BASE_SIZE+((FF_MAX_LFN+1)*2)+(19*32)));
667 #else
668 static IN_DMA_SECTION_NOINIT(THD_WORKING_AREA(waThdSdLog, WA_LOG_BASE_SIZE+((FF_MAX_LFN+1)*2)));
669 #endif
670 #else
671 static THD_WORKING_AREA(waThdSdLog, WA_LOG_BASE_SIZE);
672 #endif
673 
674 SdioError sdLoglaunchThread ()
675 {
676  chThdSleepMilliseconds(100);
677 
678  sdLogThd = chThdCreateStatic(waThdSdLog, sizeof(waThdSdLog),
679  NORMALPRIO + 1, thdSdLog, NULL);
680  if (sdLogThd == NULL) {
681  return storageStatus = SDLOG_INTERNAL_ERROR;
682  } else {
683  return storageStatus = SDLOG_OK;
684  }
685 }
686 
687 SdioError sdLogStopThread(void)
688 {
689  SdioError retVal = SDLOG_OK;
690 
691  storageStatus = retVal;
692  if (sdLogThd == NULL) {
693  return storageStatus = SDLOG_NOTHREAD;
694  }
695 
696  LogMessage lm;
697 
698  /* // ask for closing (after flushing) all opened files */
699  /* for (uint8_t i=0; i<SDLOG_NUM_FILES; i++) { */
700  /* if (fileDes[i].inUse) { */
701  /* flushWriteByteBuffer (i); */
702  /* lm.op.fcntl = FCNTL_CLOSE; */
703  /* lm.op.fd = i & 0x1f; */
704  /* if (msgqueue_copy_send (&messagesQueue, &lm, sizeof(LogMessage), MsgQueue_OUT_OF_BAND) < 0) { */
705  /* retVal= SDLOG_QUEUEFULL; */
706  /* } */
707  /* } */
708  /* } */
709 
710  lm.op.fcntl = FCNTL_EXIT;
711  if (msgqueue_copy_send(&messagesQueue, &lm, sizeof(LogMessage), MsgQueue_OUT_OF_BAND) < 0) {
712  retVal = SDLOG_QUEUEFULL;
713  }
714 
715  chThdWait(sdLogThd);
716  sdLogThd = NULL;
717  return storageStatus = retVal;
718 }
719 #endif
720 
721 
722 SdioError getFileName(const char *prefix, const char *directoryName,
723  char *nextFileName, const size_t nameLength, const int indexOffset)
724 {
725  DIR dir; /* Directory object */
726  FRESULT rc; /* Result code */
727  FILINFO fno; /* File information object */
728  int32_t fileIndex ;
729  int32_t maxCurrentIndex = 0;
730 
731 
732  const size_t directoryNameLen = MIN(strlen(directoryName), 128);
733  const size_t slashDirNameLen = directoryNameLen + 2;
734  char slashDirName[slashDirNameLen];
735  strlcpy(slashDirName, "/", slashDirNameLen);
736  strlcat(slashDirName, directoryName, slashDirNameLen);
737 
738  rc = f_opendir(&dir, directoryName);
739  if (rc != FR_OK) {
740  rc = f_mkdir(slashDirName);
741  if (rc != FR_OK) {
742  return storageStatus = SDLOG_FATFS_ERROR;
743  }
744  rc = f_opendir(&dir, directoryName);
745  if (rc != FR_OK) {
746  return storageStatus = SDLOG_FATFS_ERROR;
747  }
748  }
749 
750  for (;;) {
751  rc = f_readdir(&dir, &fno); /* Read a directory item */
752  if (rc != FR_OK || fno.fname[0] == 0) { break; } /* Error or end of dir */
753 
754 
755  if (fno.fname[0] == '.') { continue; }
756 
757  if (!(fno.fattrib & AM_DIR)) {
758  // DebugTrace ("fno.fsize=%d fn=%s\n", fno.fsize, fn);
759  fileIndex = uiGetIndexOfLogFile(prefix, fno.fname);
760  maxCurrentIndex = MAX(maxCurrentIndex, fileIndex);
761  }
762  }
763  if (rc) {
764  return storageStatus = SDLOG_FATFS_ERROR;
765  }
766 
767  rc = f_closedir(&dir);
768  if (rc) {
769  return storageStatus = SDLOG_FATFS_ERROR;
770  }
771 
772  if (maxCurrentIndex < NUMBERMAX) {
773  chsnprintf(nextFileName, nameLength, NUMBERFMF,
774  directoryName, prefix, maxCurrentIndex + indexOffset);
775  return storageStatus = SDLOG_OK;
776  } else {
777  chsnprintf(nextFileName, nameLength, "%s\\%s%.ERR",
778  directoryName, prefix);
779  return storageStatus = SDLOG_LOGNUM_ERROR;
780  }
781 }
782 
783 SdioError removeEmptyLogs(const char *directoryName, const char *prefix, const size_t sizeConsideredEmpty)
784 {
785  DIR dir; /* Directory object */
786  FRESULT rc; /* Result code */
787  FILINFO fno; /* File information object */
788 
789 
790  rc = f_opendir(&dir, directoryName);
791  if (rc != FR_OK) {
792  return storageStatus = SDLOG_FATFS_NOENT;
793  }
794 
795  for (;;) {
796  rc = f_readdir(&dir, &fno); /* Read a directory item */
797  if (rc != FR_OK || fno.fname[0] == 0) { break; } /* Error or end of dir */
798 
799  if (fno.fname[0] == '.') { continue; }
800 
801  if (!(fno.fattrib & AM_DIR)) {
802  // DebugTrace ("fno.fsize=%d fn=%s\n", fno.fsize, fn);
803  if ((strncmp(fno.fname, prefix, strlen(prefix)) == 0) && (fno.fsize <= sizeConsideredEmpty)) {
804  char absPathName[128];
805  strlcpy(absPathName, directoryName, sizeof(absPathName));
806  strlcat(absPathName, "/", sizeof(absPathName));
807  strlcat(absPathName, fno.fname, sizeof(absPathName));
808  rc = f_unlink(absPathName);
809  if (rc) {
810  break;
811  }
812  }
813  }
814  }
815 
816  if (rc) {
817  return storageStatus = SDLOG_FATFS_ERROR;
818  }
819 
820  rc = f_closedir(&dir);
821  if (rc) {
822  return storageStatus = SDLOG_FATFS_ERROR;
823  }
824 
825  return storageStatus = SDLOG_OK;
826 }
827 
828 /*
829 # _____ _ _
830 # | __ \ (_) | |
831 # | |__) | _ __ _ __ __ __ _ | |_ ___
832 # | ___/ | '__| | | \ \ / / / _` | | __| / _ \
833 # | | | | | | \ V / | (_| | \ |_ | __/
834 # |_| |_| |_| \_/ \__,_| \__| \___|
835 */
836 
837 
838 
839 
840 
841 
842 
843 int32_t uiGetIndexOfLogFile(const char *prefix, const char *fileName)
844 {
845  const size_t len = strlen(prefix);
846 
847  // if filename does not began with prefix, return 0
848  if (strncmp(prefix, fileName, len) != 0) {
849  return 0;
850  }
851 
852  // we point on the first char after prefix
853  const char *suffix = &(fileName[len]);
854 
855  // we test that suffix is valid (at least begin with digit)
856  if (!isdigit((int) suffix[0])) {
857  // DebugTrace ("DBG> suffix = %s", suffix);
858  return 0;
859  }
860 
861  return (int32_t) atoi(suffix);
862 }
863 
864 
865 #ifdef SDLOG_NEED_QUEUE
866 static void cleanQueue(const bool allQueue)
867 {
868  if (allQueue == false) {
869  do {
870  struct tlsf_stat_t stat;
871  tlsf_stat_r(&HEAP_DEFAULT, &stat);
872  const size_t freeRam = stat.mfree;
873  chSysLock();
874  const bool queue_full = (chMBGetFreeCountI(&messagesQueue.mb) <= 0);
875  chSysUnlock();
876  // DebugTrace ("sdLogCloseLog freeRam=%d queue_full=%d", freeRam, queue_full);
877  if ((freeRam < 200) || (queue_full == true)) {
878  removeFromQueue(1);
879  } else {
880  break;
881  }
882  } while (true);
883  } else {
884  removeFromQueue(SDLOG_QUEUE_BUCKETS);
885  }
886 }
887 
888 static void removeFromQueue(const size_t nbMsgToRFemove)
889 {
890  /* struct tlsf_stat_t stat; */
891 
892  /* tlsf_stat_r (&HEAP_DEFAULT, &stat); */
893  /* size_t freeRam = stat.mfree; */
894  /* chSysLock(); */
895  /* size_t queueBuckets = chMBGetFreeCountI(&messagesQueue.mb); */
896  /* chSysUnlock(); */
897 
898  /* DebugTrace ("Before removeFromQueue (%d) : ram=%d buck=%d", nbMsgToRFemove, freeRam, queueBuckets); */
899 
900  LogMessage *lm = NULL;
901  for (size_t i = 0; i < nbMsgToRFemove; i++) {
902  const int32_t retLen = (int32_t)(msgqueue_pop_timeout(&messagesQueue, (void **) &lm, TIME_IMMEDIATE));
903  if (retLen < 0) {
904  break;
905  }
907  }
908 
909  /* tlsf_stat_r (&HEAP_DEFAULT, &stat); */
910  /* freeRam = stat.mfree; */
911  /* chSysLock(); */
912  /* queueBuckets = chMBGetFreeCountI(&messagesQueue.mb); */
913  /* chSysUnlock(); */
914 
915  /* DebugTrace ("After removeFromQueue (%d) : ram=%d buck=%d", nbMsgToRFemove, freeRam, queueBuckets); */
916 }
917 
918 
919 static void thdSdLog(void *arg)
920 {
921  (void) arg;
922  struct PerfBuffer {
923  // each element of buffer should be word aligned for sdio efficient write
924  ALIGNED_VAR(4) uint8_t buffer[SDLOG_WRITE_BUFFER_SIZE] ;
925  uint16_t size;
926  } ;
927 
928  UINT bw;
929  static IN_DMA_SECTION_CLEAR(struct PerfBuffer perfBuffers[SDLOG_NUM_FILES]);
930  storageStatus = SDLOG_OK;
931  chRegSetThreadName("thdSdLog");
932  while (true) {
933  LogMessage *lm = NULL;
934  const int32_t retLen = (int32_t)(msgqueue_pop(&messagesQueue, (void **) &lm));
935  if (retLen > 0) {
936  FIL *fo = &fileDes[lm->op.fd].fil;
937  uint8_t *const perfBuffer = perfBuffers[lm->op.fd].buffer;
938 
939  switch (lm->op.fcntl) {
940 
941  case FCNTL_FLUSH:
942  case FCNTL_CLOSE: {
943  const uint16_t curBufFill = perfBuffers[lm->op.fd].size;
944  if (fileDes[lm->op.fd].inUse) {
945  if (curBufFill) {
946  f_write(fo, perfBuffer, curBufFill, &bw);
947  nbBytesWritten += bw;
948  perfBuffers[lm->op.fd].size = 0;
949  }
950  if (lm->op.fcntl == FCNTL_FLUSH) {
951  f_sync(fo);
952  } else { // close
953  if (fileDes[lm->op.fd].tagAtClose) {
954  f_write(fo, "\r\nEND_OF_LOG\r\n", 14, &bw);
955  nbBytesWritten += bw;
956  }
957  f_close(fo);
958  fileDes[lm->op.fd].inUse = false; // store that file is closed
959  }
960  }
961  }
962  break;
963 
964  case FCNTL_EXIT:
965  tlsf_free_r(&HEAP_DEFAULT, lm); // to avoid a memory leak
966  chThdExit(storageStatus = SDLOG_NOTHREAD);
967  break; /* To exit from thread when asked : chThdTerminate
968  then send special message with FCNTL_EXIT */
969 
970 
971  case FCNTL_WRITE: {
972  const uint16_t curBufFill = perfBuffers[lm->op.fd].size;
973  if (fileDes[lm->op.fd].inUse) {
974  const int32_t messLen = retLen - (int32_t)(sizeof(LogMessage));
975  if (messLen < (SDLOG_WRITE_BUFFER_SIZE - curBufFill)) {
976  // the buffer can accept this message
977  memcpy(&(perfBuffer[curBufFill]), lm->mess, (size_t)(messLen));
978  perfBuffers[lm->op.fd].size = (uint16_t)((perfBuffers[lm->op.fd].size) + messLen);
979  } else {
980  // fill the buffer
981  const int32_t stayLen = SDLOG_WRITE_BUFFER_SIZE - curBufFill;
982  memcpy(&(perfBuffer[curBufFill]), lm->mess, (size_t)(stayLen));
983  FRESULT rc = f_write(fo, perfBuffer, SDLOG_WRITE_BUFFER_SIZE, &bw);
984  nbBytesWritten += bw;
985  // if there an autoflush period specified, flush to the mass storage media
986  // if timer has expired and rearm.
987  if (fileDes[lm->op.fd].autoFlushPeriod) {
988  const systime_t now = chVTGetSystemTimeX();
989  if ((now - fileDes[lm->op.fd].lastFlushTs) >
990  (fileDes[lm->op.fd].autoFlushPeriod * CH_CFG_ST_FREQUENCY)) {
991  f_sync(fo);
992  fileDes[lm->op.fd].lastFlushTs = now;
993  }
994  }
995  if (rc) {
996  //chThdExit(storageStatus = SDLOG_FATFS_ERROR);
997  storageStatus = SDLOG_FATFS_ERROR;
998  } else if (bw != SDLOG_WRITE_BUFFER_SIZE) {
999  chThdExit(storageStatus = SDLOG_FSFULL);
1000  }
1001 
1002  memcpy(perfBuffer, &(lm->mess[stayLen]), (uint32_t)(messLen - stayLen));
1003  perfBuffers[lm->op.fd].size = (uint16_t)(messLen - stayLen); // curBufFill
1004  }
1005  }
1006  }
1007  }
1008  tlsf_free_r(&HEAP_DEFAULT, lm);
1009  } else {
1010  chThdExit(storageStatus = SDLOG_INTERNAL_ERROR);
1011  }
1012  }
1013 }
1014 
1015 static size_t logMessageLen(const LogMessage *lm)
1016 {
1017  return sizeof(LogMessage) + strnlen(lm->mess, SDLOG_MAX_MESSAGE_LEN);
1018 }
1019 
1020 static size_t logRawLen(const size_t len)
1021 {
1022  return sizeof(LogMessage) + len;
1023 }
1024 
1025 static SdioError getNextFIL(FileDes *fd)
1026 {
1027  // if there is a free slot in fileDes, use it
1028  // else, if all slots are buzy, maximum open files limit
1029  // is reach.
1030  for (FileDes i = 0; i < SDLOG_NUM_FILES; i++) {
1031  if (fileDes[i].inUse == false) {
1032  *fd = i;
1033  fileDes[i].inUse = true;
1034  return SDLOG_OK;
1035  }
1036  }
1037  return SDLOG_FDFULL;
1038 }
1039 
1040 size_t sdLogGetNbBytesWrittenToStorage(void)
1041 {
1042  return nbBytesWritten;
1043 }
1044 
1045 SdioError sdLogGetStorageStatus(void)
1046 {
1047  return storageStatus;
1048 }
1049 
1050 
1051 #endif
SDLOG_WRITE_BUFFER_SIZE
#define SDLOG_WRITE_BUFFER_SIZE
Definition: sdLog.c:74
uint32_t
unsigned int uint32_t
Typedef defining 32 bit unsigned int type.
Definition: vl53l1_types.h:78
uint8_t
unsigned char uint8_t
Typedef defining 8 bit unsigned char type.
Definition: vl53l1_types.h:98
SDLOG_FSFULL
@ SDLOG_FSFULL
Definition: sdLog.h:116
msgqueue_pop
int32_t msgqueue_pop(MsgQueue *que, void **msgPtr)
wait then receive message
Definition: msg_queue.c:134
NUMBERMAX
#define NUMBERMAX
Definition: sdLog.h:35
status
uint8_t status
Definition: nps_radio_control_spektrum.c:101
tlsf_stat_r
void tlsf_stat_r(tlsf_memory_heap_t *heap, struct tlsf_stat_t *stat)
Definition: tlsf_malloc_arch.c:127
SDLOG_OK
@ SDLOG_OK
Definition: sdLog.h:112
SDLOG_FATFS_ERROR
@ SDLOG_FATFS_ERROR
Definition: sdLog.h:114
IN_DMA_SECTION
static IN_DMA_SECTION(FATFS fatfs)
SdLogBuffer
struct _SdLogBuffer SdLogBuffer
Definition: sdLog.h:127
SDLOG_FDFULL
@ SDLOG_FDFULL
Definition: sdLog.h:117
sdLog.h
sdLogFinish
SdioError sdLogFinish(void)
unmount filesystem
Definition: sdLog.c:254
SDLOG_INTERNAL_ERROR
@ SDLOG_INTERNAL_ERROR
Definition: sdLog.h:121
SDLOG_MAX_MESSAGE_LEN
#define SDLOG_MAX_MESSAGE_LEN
Definition: mcuconf.h:434
SDLOG_QUEUE_BUCKETS
#define SDLOG_QUEUE_BUCKETS
Definition: mcuconf.h:433
chsnprintf
void chsnprintf(char *buffer, size_t size, const char *fmt,...)
Definition: printf.c:377
CH_CFG_ST_FREQUENCY
#define CH_CFG_ST_FREQUENCY
System tick frequency.
Definition: chconf.h:55
unlikely
#define unlikely(x)
Definition: sdLog.c:39
SDLOG_FATFS_NOENT
@ SDLOG_FATFS_NOENT
Definition: sdLog.h:115
MIN
#define MIN(x, y)
Definition: sdLog.c:42
MsgQueue::mb
mailbox_t mb
Definition: msg_queue.h:226
SDLOG_MEMFULL
@ SDLOG_MEMFULL
Definition: sdLog.h:119
DWORD
uint32_t DWORD
Definition: usb_msd.c:133
NUMBERFMF
#define NUMBERFMF
Definition: sdLog.h:36
MsgQueue_REGULAR
@ MsgQueue_REGULAR
Definition: msg_queue.h:47
SDLOG_QUEUEFULL
@ SDLOG_QUEUEFULL
Definition: sdLog.h:118
dir
static const float dir[]
Definition: shift_tracking.c:91
printf.h
Mini printf-like functionality.
SDLOG_NOTHREAD
@ SDLOG_NOTHREAD
Definition: sdLog.h:120
sdio_disconnect
bool sdio_disconnect(void)
Disconnect a SD card on SDIO peripheral.
Definition: sdio_arch.c:87
SDLOG_NUM_FILES
#define SDLOG_NUM_FILES
Definition: mcuconf.h:435
removeEmptyLogs
SdioError removeEmptyLogs(const char *directoryName, const char *prefix, const size_t sizeConsideredEmpty)
remove spurious log file left on sd
Definition: sdLog.c:783
sdio.h
arch independent SDIO API
tlsf_realloc_r
void * tlsf_realloc_r(tlsf_memory_heap_t *heap, void *ptr, size_t bytes)
Definition: tlsf_malloc_arch.c:111
tlsf_free_r
void tlsf_free_r(tlsf_memory_heap_t *heap, void *ptr)
Definition: tlsf_malloc_arch.c:119
true
#define true
Definition: microrl.h:6
msgqueue_pop_timeout
int32_t msgqueue_pop_timeout(MsgQueue *que, void **msgPtr, const systime_t timout)
receive message specifying timeout
Definition: msg_queue.c:139
msgqueue_init
void msgqueue_init(MsgQueue *que, tlsf_memory_heap_t *heap, msg_t *mb_buf, const cnt_t mb_size)
initialise MsgQueue
Definition: msg_queue.c:42
THD_WORKING_AREA
static THD_WORKING_AREA(wa_thd_spi1, SPI_THREAD_STACK_SIZE)
msgqueue_send
int32_t msgqueue_send(MsgQueue *que, void *msg, const uint16_t msgLen, const MsgQueueUrgency urgency)
send a buffer previously allocated by msgqueue_malloc_before_send
Definition: msg_queue.c:69
msg_queue.h
SDLOG_CANNOT_EXPAND
@ SDLOG_CANNOT_EXPAND
Definition: sdLog.h:122
FileDes
int8_t FileDes
Definition: sdLog.h:128
MsgQueue_OUT_OF_BAND
@ MsgQueue_OUT_OF_BAND
Definition: msg_queue.h:47
SDLOG_NOCARD
@ SDLOG_NOCARD
Definition: sdLog.h:113
fd
int fd
Definition: serial.c:26
int32_t
int int32_t
Typedef defining 32 bit int type.
Definition: vl53l1_types.h:83
tlsf_malloc_r
void * tlsf_malloc_r(tlsf_memory_heap_t *heap, size_t bytes)
Definition: tlsf_malloc_arch.c:95
offset
static const float offset[]
Definition: dw1000_arduino.c:199
msgqueue_copy_send
int32_t msgqueue_copy_send(MsgQueue *que, const void *msg, const uint16_t msgLen, const MsgQueueUrgency urgency)
send a buffer NOT previously allocated
Definition: msg_queue.c:127
chvsnprintf
void chvsnprintf(char *buffer, size_t size, const char *fmt, va_list ap)
Definition: printf.c:372
ram_arch.h
MAX
#define MAX(x, y)
Definition: sdLog.c:45
sdio_connect
bool sdio_connect(void)
Connect a SD card on SDIO peripheral.
Definition: sdio_arch.c:48
flushWriteByteBuffer
static SdioError flushWriteByteBuffer(const FileDes fd)
getFileName
SdioError getFileName(const char *prefix, const char *directoryName, char *nextFileName, const size_t nameLength, const int indexOffset)
get last used name for a pattern, then add offset and return valid filename
Definition: sdLog.c:722
tlsf_stat_t
Definition: tlsf_malloc.h:55
IN_STD_SECTION_CLEAR
#define IN_STD_SECTION_CLEAR(var)
Definition: ram_arch.h:67
SDLOG_WAS_LAUNCHED
@ SDLOG_WAS_LAUNCHED
Definition: sdLog.h:124
FALSE
#define FALSE
Definition: std.h:5
FF_MAX_LFN
#define FF_MAX_LFN
Definition: ffconf.h:105
SdioError
SdioError
Definition: sdLog.h:111
uint16_t
unsigned short uint16_t
Typedef defining 16 bit unsigned short type.
Definition: vl53l1_types.h:88
IN_DMA_SECTION_NOINIT
#define IN_DMA_SECTION_NOINIT(var)
Definition: ram_arch.h:74
fo
static int fo
Definition: chdk_pipe.c:24
MsgQueue
Definition: msg_queue.h:225
SDLOG_LOGNUM_ERROR
@ SDLOG_LOGNUM_ERROR
Definition: sdLog.h:123
uiGetIndexOfLogFile
static int32_t uiGetIndexOfLogFile(const char *prefix, const char *fileName)
Definition: sdLog.c:843
sdLogInit
SdioError sdLogInit(uint32_t *freeSpaceInKo)
initialise sdLog
Definition: sdLog.c:194
HEAP_DEFAULT
#define HEAP_DEFAULT
Definition: tlsf_malloc_arch.h:44
IN_DMA_SECTION_CLEAR
#define IN_DMA_SECTION_CLEAR(var)
Definition: ram_arch.h:75