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