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