Paparazzi UAS  v5.12_stable-4-g9b43e9b
Paparazzi is a free software Unmanned Aircraft System.
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
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 #define MIN(x , y) (((x) < (y)) ? (x) : (y))
42 #define MAX(x , y) (((x) > (y)) ? (x) : (y))
43 #define IS_POWER_OF_TWO(s) ((s) && !((s) & ((s) - 1)))
44 
45 #ifndef SDLOG_NUM_FILES
46 #error SDLOG_NUM_FILES should be defined in mcuconf.h
47 #endif
48 
49 #if _FATFS < 8000
50 #if _FS_SHARE != 0 && _FS_SHARE < SDLOG_NUM_FILES
51 #error if _FS_SHARE is not zero, it should be equal of superior to SDLOG_NUM_FILES
52 #endif
53 
54 
55 #else // _FATFS > 8000
56 #if _FS_LOCK != 0 && _FS_LOCK < SDLOG_NUM_FILES
57 #error if _FS_LOCK is not zero, it should be equal of superior to SDLOG_NUM_FILES
58 #endif
59 #endif
60 
61 #ifndef SDLOG_ALL_BUFFERS_SIZE
62 #error SDLOG_ALL_BUFFERS_SIZE should be defined in mcuconf.h
63 #endif
64 
65 #if SDLOG_ALL_BUFFERS_SIZE > 65536
66 #error constraint 512 <= SDLOG_ALL_BUFFERS_SIZE <= 65536 not meet
67 #endif
68 
69 #define SDLOG_WRITE_BUFFER_SIZE (SDLOG_ALL_BUFFERS_SIZE/SDLOG_NUM_FILES)
70 
71 #ifndef SDLOG_MAX_MESSAGE_LEN
72 #error SDLOG_MAX_MESSAGE_LENshould be defined in mcuconf.h
73 #endif
74 
75 #ifndef SDLOG_QUEUE_BUCKETS
76 #error SDLOG_QUEUE_BUCKETS should be defined in mcuconf.h
77 #endif
78 
79 #if _FS_REENTRANT == 0
80 #warning "_FS_REENTRANT = 0 in ffconf.h DO NOT open close file during log"
81 #endif
82 
83 #if SDLOG_WRITE_BUFFER_SIZE < 512
84 #error SDLOG_ALL_BUFFERS_SIZE / SDLOG_NUM_FILES cannot be < 512
85 #endif
86 
87 #if (!(IS_POWER_OF_TWO (SDLOG_WRITE_BUFFER_SIZE)))
88 #error SDLOG_ALL_BUFFERS_SIZE / SDLOG_NUM_FILES should be a POWER OF 2
89 #endif
90 
91 #ifdef SDLOG_NEED_QUEUE
93 
94 #include "mcu_periph/ram_arch.h"
95 
96 
97 /*
98  The buffers that do DMA are the caches (named buf) in the FIL and FATFS struct of fatfs library
99  It's the only buffers that have to reside in DMA capable memory.
100 
101  The buffer associated with message queue, and the cache buffer for caching file write
102  could reside in non DMA capable memory.
103 
104  stm32f4 : regular sram : 128ko, dma, slow
105  ccm sram : 64ko, no_dma, fast
106 
107  stm32f7 : regular sram : 256ko, dma only possible if data cache are explicitely flushed, fast
108  dtcm sram : 64ko, dma, slow (no cache)
109  */
110 
111 
112 
113 static msg_t IN_STD_SECTION_CLEAR(queMbBuffer[SDLOG_QUEUE_BUCKETS]);
114 static MsgQueue messagesQueue;
115 
116 #define WRITE_BYTE_CACHE_SIZE 15 // limit overhead :
117 // malloc (15+1) occupies 20bytes
118 typedef struct {
119  uint8_t fcntl: 2;
120  uint8_t fd: 6;
121 } FileOp;
122 
123 struct LogMessage {
124  FileOp op;
125  char mess[0];
126 };
127 
128 struct FilePoolUnit {
129  FIL fil;
130  uint32_t autoFlushPeriod;
131  systime_t lastFlushTs;
132  bool inUse;
133  bool tagAtClose;
134  // optimise write byte by caching at send level now that we are based upon tlsf where
135  // allocation granularity is 16 bytes
136  LogMessage *writeByteCache;
137  uint8_t writeByteSeek;
138 };
139 
140 static struct FilePoolUnit IN_DMA_SECTION(fileDes[SDLOG_NUM_FILES]) = {
141  [0 ... SDLOG_NUM_FILES - 1] = {
142  .fil = {{0}}, .inUse = false, .tagAtClose = false,
143  .writeByteCache = NULL, .writeByteSeek = 0
144  }
145 };
146 
147 static volatile size_t nbBytesWritten = 0;
148 static SdioError storageStatus = SDLOG_OK;
149 
150 typedef enum {
151  FCNTL_WRITE = 0b00,
152  FCNTL_FLUSH = 0b01,
153  FCNTL_CLOSE = 0b10,
154  FCNTL_EXIT = 0b11
155 } FileFcntl;
156 
157 struct _SdLogBuffer {
158  LogMessage *lm;
159  size_t len;
161 } ;
162 
163 
164 #define LOG_MESSAGE_PREBUF_LEN (SDLOG_MAX_MESSAGE_LEN+sizeof(LogMessage))
165 #endif // SDLOG_NEED_QUEUE
166 
167 /* File system object */
168 static IN_DMA_SECTION(FATFS fatfs);
169 
170 #ifdef SDLOG_NEED_QUEUE
171 static size_t logMessageLen(const LogMessage *lm);
172 static size_t logRawLen(const size_t len);
173 static SdioError sdLoglaunchThread(void);
174 static SdioError sdLogStopThread(void);
175 static thread_t *sdLogThd = NULL;
176 static SdioError getNextFIL(FileDes *fd);
177 static void removeFromQueue(const size_t nbMsgToRFemov);
178 static void cleanQueue(const bool allQueue);
179 static SdioError sdLogExpandLogFile(const FileDes fileObject, const size_t sizeInMo,
180  const bool preallocate);
181 
182 #if (CH_KERNEL_MAJOR > 2)
183 static void thdSdLog(void *arg) ;
184 #else
185 static msg_t thdSdLog(void *arg) ;
186 #endif
187 
188 #endif // SDLOG_NEED_QUEUE
189 
190 static int32_t uiGetIndexOfLogFile(const char *prefix, const char *fileName) ;
191 static inline SdioError flushWriteByteBuffer(const FileDes fd);
192 
193 SdioError sdLogInit(uint32_t *freeSpaceInKo)
194 {
195  DWORD clusters = 0;
196  FATFS *fsp = NULL;
197  nbBytesWritten = 0;
198 
199  // if init is already done, return ERROR
200  if (sdLogThd != NULL) {
201  *freeSpaceInKo = 0;
202  return storageStatus = SDLOG_WAS_LAUNCHED;
203  }
204 
205 #ifdef SDLOG_NEED_QUEUE
206  msgqueue_init(&messagesQueue, &HEAP_DEFAULT, queMbBuffer, SDLOG_QUEUE_BUCKETS);
207 #endif
208 
209  if (!sdc_lld_is_card_inserted(NULL)) {
210  return storageStatus = SDLOG_NOCARD;
211  }
212 
213 
214  sdio_connect();
215  chThdSleepMilliseconds(10);
216  sdio_disconnect();
217 
218  if (sdio_connect() == FALSE) {
219  return storageStatus = SDLOG_NOCARD;
220  }
221 
222 #if _FATFS < 8000
223  FRESULT rc = f_mount(0, &fatfs);
224 #else
225  FRESULT rc = f_mount(&fatfs, "/", 1);
226 #endif
227 
228  if (rc != FR_OK) {
229  return storageStatus = SDLOG_FATFS_ERROR;
230  }
231 
232  if (freeSpaceInKo != NULL) {
233  f_getfree("/", &clusters, &fsp);
234  *freeSpaceInKo = clusters * (uint32_t)fatfs.csize / 2;
235  }
236 
237 #ifdef SDLOG_NEED_QUEUE
238  for (uint8_t i = 0; i < SDLOG_NUM_FILES; i++) {
239  fileDes[i].inUse = fileDes[i].tagAtClose = false;
240  fileDes[i].writeByteCache = NULL;
241  fileDes[i].writeByteSeek = 0;
242  }
243 
244  storageStatus = sdLoglaunchThread();
245 #else
246  storageStatus = SDLOG_OK;
247 #endif
248  return storageStatus;
249 
250 }
251 
252 
254 {
255 #if _FATFS < 8000
256  FRESULT rc = f_mount(0, NULL);
257 #else
258  FRESULT rc = f_mount(NULL, "", 0);
259 #endif
260  if (rc != FR_OK) {
261  return storageStatus = SDLOG_FATFS_ERROR;
262  }
263 
264  // if we mount, unmount, don't disconnect sdio
265  /* if (sdio_disconnect () == FALSE) */
266  /* return SDLOG_NOCARD; */
267 
268  return storageStatus = SDLOG_OK;
269 }
270 
271 
272 
273 #ifdef SDLOG_NEED_QUEUE
274 SdioError sdLogOpenLog(FileDes *fd, const char *directoryName, const char *prefix,
275  const uint32_t autoFlushPeriod, const bool appendTagAtClose,
276  const size_t sizeInMo, const bool preallocate)
277 {
278  FRESULT rc; /* fatfs result code */
279  SdioError sde = SDLOG_OK; /* sdio result code */
280  //DIR dir; /* Directory object */
281  //FILINFO fno; /* File information object */
282  char fileName[32];
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, sizeof(fileName), +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 
422  const SdioError status = flushWriteByteBuffer(fd);
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 
461  const SdioError status = flushWriteByteBuffer(fd);
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 
528  const SdioError status = flushWriteByteBuffer(fd);
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 {
591  SdioError status = SDLOG_OK;
592 
593  if ((fd < 0) || (fd >= SDLOG_NUM_FILES) || (fileDes[fd].inUse == false)) {
594  status = SDLOG_FATFS_ERROR;
595  goto fail;
596  }
597 
598  status = flushWriteByteBuffer(fd);
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
610  status = SDLOG_QUEUEFULL;
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) {
649  const SdioError status = flushWriteByteBuffer(fd);
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 _USE_LFN == 2
665 #if _FS_EXFAT
666 static THD_WORKING_AREA(waThdSdLog, WA_LOG_BASE_SIZE+((_MAX_LFN+1)*2)+(19*32));
667 #else
668 static THD_WORKING_AREA(waThdSdLog, WA_LOG_BASE_SIZE+((_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 #if (CH_KERNEL_MAJOR > 2)
920 static void thdSdLog(void *arg)
921 #else
922 static msg_t thdSdLog(void *arg)
923 #endif
924 {
925  (void) arg;
926  struct PerfBuffer {
927  // each element of buffer should be word aligned for sdio efficient write
928  ALIGNED_VAR(4) uint8_t buffer[SDLOG_WRITE_BUFFER_SIZE] ;
929  uint16_t size;
930  } ;
931 
932  UINT bw;
933  static IN_DMA_SECTION_CLEAR(struct PerfBuffer perfBuffers[SDLOG_NUM_FILES]);
934  storageStatus = SDLOG_OK;
935  chRegSetThreadName("thdSdLog");
936  while (true) {
937  LogMessage *lm = NULL;
938  const int32_t retLen = (int32_t)(msgqueue_pop(&messagesQueue, (void **) &lm));
939  if (retLen > 0) {
940  FIL *fo = &fileDes[lm->op.fd].fil;
941  uint8_t *const perfBuffer = perfBuffers[lm->op.fd].buffer;
942 
943  switch (lm->op.fcntl) {
944 
945  case FCNTL_FLUSH:
946  case FCNTL_CLOSE: {
947  const uint16_t curBufFill = perfBuffers[lm->op.fd].size;
948  if (fileDes[lm->op.fd].inUse) {
949  if (curBufFill) {
950  f_write(fo, perfBuffer, curBufFill, &bw);
951  nbBytesWritten += bw;
952  perfBuffers[lm->op.fd].size = 0;
953  }
954  if (lm->op.fcntl == FCNTL_FLUSH) {
955  f_sync(fo);
956  } else { // close
957  if (fileDes[lm->op.fd].tagAtClose) {
958  f_write(fo, "\r\nEND_OF_LOG\r\n", 14, &bw);
959  nbBytesWritten += bw;
960  }
961  f_close(fo);
962  fileDes[lm->op.fd].inUse = false; // store that file is closed
963  }
964  }
965  }
966  break;
967 
968  case FCNTL_EXIT:
969  tlsf_free_r(&HEAP_DEFAULT, lm); // to avoid a memory leak
970  chThdExit(storageStatus = SDLOG_NOTHREAD);
971  break; /* To exit from thread when asked : chThdTerminate
972  then send special message with FCNTL_EXIT */
973 
974 
975  case FCNTL_WRITE: {
976  const uint16_t curBufFill = perfBuffers[lm->op.fd].size;
977  if (fileDes[lm->op.fd].inUse) {
978  const int32_t messLen = retLen - (int32_t)(sizeof(LogMessage));
979  if (messLen < (SDLOG_WRITE_BUFFER_SIZE - curBufFill)) {
980  // the buffer can accept this message
981  memcpy(&(perfBuffer[curBufFill]), lm->mess, (size_t)(messLen));
982  perfBuffers[lm->op.fd].size = (uint16_t)((perfBuffers[lm->op.fd].size) + messLen);
983  } else {
984  // fill the buffer
985  const int32_t stayLen = SDLOG_WRITE_BUFFER_SIZE - curBufFill;
986  memcpy(&(perfBuffer[curBufFill]), lm->mess, (size_t)(stayLen));
987  FRESULT rc = f_write(fo, perfBuffer, SDLOG_WRITE_BUFFER_SIZE, &bw);
988  nbBytesWritten += bw;
989  // if there an autoflush period specified, flush to the mass storage media
990  // if timer has expired and rearm.
991  if (fileDes[lm->op.fd].autoFlushPeriod) {
992  const systime_t now = chVTGetSystemTimeX();
993  if ((now - fileDes[lm->op.fd].lastFlushTs) >
994  (fileDes[lm->op.fd].autoFlushPeriod * CH_CFG_ST_FREQUENCY)) {
995  f_sync(fo);
996  fileDes[lm->op.fd].lastFlushTs = now;
997  }
998  }
999  if (rc) {
1000  //chThdExit(storageStatus = SDLOG_FATFS_ERROR);
1001  storageStatus = SDLOG_FATFS_ERROR;
1002  } else if (bw != SDLOG_WRITE_BUFFER_SIZE) {
1003  chThdExit(storageStatus = SDLOG_FSFULL);
1004  }
1005 
1006  memcpy(perfBuffer, &(lm->mess[stayLen]), (uint32_t)(messLen - stayLen));
1007  perfBuffers[lm->op.fd].size = (uint16_t)(messLen - stayLen); // curBufFill
1008  }
1009  }
1010  }
1011  }
1012  tlsf_free_r(&HEAP_DEFAULT, lm);
1013  } else {
1014  chThdExit(storageStatus = SDLOG_INTERNAL_ERROR);
1015  }
1016  }
1017 #if (CH_KERNEL_MAJOR == 2)
1018  return SDLOG_OK;
1019 #endif
1020 }
1021 
1022 static size_t logMessageLen(const LogMessage *lm)
1023 {
1024  return sizeof(LogMessage) + strnlen(lm->mess, SDLOG_MAX_MESSAGE_LEN);
1025 }
1026 
1027 static size_t logRawLen(const size_t len)
1028 {
1029  return sizeof(LogMessage) + len;
1030 }
1031 
1032 static SdioError getNextFIL(FileDes *fd)
1033 {
1034  // if there is a free slot in fileDes, use it
1035  // else, if all slots are buzy, maximum open files limit
1036  // is reach.
1037  for (FileDes i = 0; i < SDLOG_NUM_FILES; i++) {
1038  if (fileDes[i].inUse == false) {
1039  *fd = i;
1040  fileDes[i].inUse = true;
1041  return SDLOG_OK;
1042  }
1043  }
1044  return SDLOG_FDFULL;
1045 }
1046 
1047 size_t sdLogGetNbBytesWrittenToStorage(void)
1048 {
1049  return nbBytesWritten;
1050 }
1051 
1052 SdioError sdLogGetStorageStatus(void)
1053 {
1054  return storageStatus;
1055 }
1056 
1057 
1058 #endif
SdioError sdLogFinish(void)
unmount filesystem
Definition: sdLog.c:253
unsigned short uint16_t
Definition: types.h:16
Specific RAM section for DMA usage on F7.
int32_t msgqueue_pop_timeout(MsgQueue *que, void **msgPtr, const systime_t timout)
receive message specifying timeout
Definition: msg_queue.c:139
status
Definition: anemotaxis.c:10
static IN_DMA_SECTION(FATFS fatfs)
Mini printf-like functionality.
#define MAX(x, y)
Definition: sdLog.c:42
arch independent SDIO API
int8_t FileDes
Definition: sdLog.h:124
#define IN_STD_SECTION_CLEAR(var)
Definition: ram_arch.h:58
static SdioError flushWriteByteBuffer(const FileDes fd)
#define SDLOG_WRITE_BUFFER_SIZE
Definition: sdLog.c:69
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
#define SDLOG_MAX_MESSAGE_LEN
Definition: mcuconf.h:388
void chsnprintf(char *buffer, size_t size, const char *fmt,...)
Definition: printf.c:377
#define HEAP_DEFAULT
static int32_t uiGetIndexOfLogFile(const char *prefix, const char *fileName)
Definition: sdLog.c:843
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
#define NUMBERMAX
Definition: sdLog.h:35
bool sdio_disconnect(void)
Disconnect a SD card on SDIO peripheral.
Definition: sdio_arch.c:87
#define NUMBERFMF
Definition: sdLog.h:36
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)
SdioError sdLogInit(uint32_t *freeSpaceInKo)
initialise sdLog
Definition: sdLog.c:193
#define FALSE
Definition: std.h:5
#define true
Definition: rtwtypes.h:28
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
uint32_t DWORD
Definition: usb_msd.c:133
#define SDLOG_NUM_FILES
Definition: mcuconf.h:389
#define SDLOG_QUEUE_BUCKETS
Definition: mcuconf.h:387
bool sdio_connect(void)
Connect a SD card on SDIO peripheral.
Definition: sdio_arch.c:48
static const float offset[]
void * tlsf_realloc_r(tlsf_memory_heap_t *heap, void *ptr, size_t bytes)
unsigned long uint32_t
Definition: types.h:18
SdioError removeEmptyLogs(const char *directoryName, const char *prefix, const size_t sizeConsideredEmpty)
remove spurious log file left on sd
Definition: sdLog.c:783
SdioError
Definition: sdLog.h:107
signed long int32_t
Definition: types.h:19
static int fo
Definition: chdk_pipe.c:24
void chvsnprintf(char *buffer, size_t size, const char *fmt, va_list ap)
Definition: printf.c:372
int32_t msgqueue_pop(MsgQueue *que, void **msgPtr)
wait then receive message
Definition: msg_queue.c:134
unsigned char uint8_t
Definition: types.h:14
#define _MAX_LFN
Definition: ffconf.h:105
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
int fd
Definition: serial.c:26
#define IN_DMA_SECTION_CLEAR(var)
Definition: ram_arch.h:66
#define CH_CFG_ST_FREQUENCY
System tick frequency.
Definition: chconf.h:51
#define unlikely(x)
Definition: sdLog.c:39
mailbox_t mb
Definition: msg_queue.h:226
struct _SdLogBuffer SdLogBuffer
Definition: sdLog.h:123
void * tlsf_malloc_r(tlsf_memory_heap_t *heap, size_t bytes)
static THD_WORKING_AREA(wa_thd_spi1, 1024)
#define MIN(x, y)
Definition: sdLog.c:41