Paparazzi UAS  v5.15_devel-230-gc96ce27
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, char *fileName, const size_t nameLength)
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 
292  /* local file descriptor
293  using fd is a bad idea since fd is set before fatfs objets are coherents
294  in a multithreaded application where sdLogXXX are done before sdLogWriteLog is done
295  we can have a race condition. setting fd only when fatfs files are opened resolve the problem
296  */
297  FileDes ldf;
298 
299  sde = getNextFIL(&ldf);
300  if (sde != SDLOG_OK) {
301  return storageStatus = sde;
302  }
303 
304  sde = getFileName(prefix, directoryName, fileName, nameLength, +1);
305  if (sde != SDLOG_OK) {
306  // sd card is not inserted, so logging task can be deleted
307  return storageStatus = SDLOG_FATFS_ERROR;
308  }
309 
310 
311  rc = f_open(&fileDes[ldf].fil, fileName, FA_WRITE | FA_CREATE_ALWAYS);
312  if (rc) {
313  fileDes[ldf].inUse = false;
314  return storageStatus = SDLOG_FATFS_ERROR;
315  } else {
316  fileDes[ldf].tagAtClose = appendTagAtClose;
317  fileDes[ldf].autoFlushPeriod = autoFlushPeriod;
318  fileDes[ldf].lastFlushTs = 0;
319  sde = sdLogExpandLogFile(ldf, sizeInMo, preallocate);
320  }
321 
322  *fd = ldf;
323  return storageStatus = sde;
324 }
325 
326 SdioError sdLogCloseAllLogs(bool flush)
327 {
328  FRESULT rc = 0; /* Result code */
329 
330  // do not flush what is in ram, close as soon as possible
331  if (flush == false) {
332  UINT bw;
333  // stop worker thread then close file
334  cleanQueue(true);
335  sdLogStopThread();
336 
337  for (FileDes fd = 0; fd < SDLOG_NUM_FILES; fd++) {
338  if (fileDes[fd].inUse) {
339  FIL *fo = &fileDes[fd].fil;
340  if (fileDes[fd].tagAtClose) {
341  f_write(fo, "\r\nEND_OF_LOG\r\n", 14, &bw);
342  nbBytesWritten += bw;
343  }
344  FRESULT trc = f_close(fo);
345  fileDes[fd].inUse = false;
346  if (!rc) {
347  rc = trc;
348  }
349  }
350  }
351 
352  if (rc) {
353  return storageStatus = SDLOG_FATFS_ERROR;
354  }
355 
356  // flush ram buffer then close
357  } else { // flush == true
358  if (sdLogThd == NULL) {
359  // something goes wrong, log thread is no more working
360  return storageStatus = SDLOG_NOTHREAD;
361  }
362 
363  // queue flush + close order, then stop worker thread
364  for (FileDes fd = 0; fd < SDLOG_NUM_FILES; fd++) {
365  if (fileDes[fd].inUse) {
367  sdLogCloseLog(fd);
368  }
369  }
370 
371  LogMessage *lm = tlsf_malloc_r(&HEAP_DEFAULT, sizeof(LogMessage));
372  if (lm == NULL) {
373  return storageStatus = SDLOG_MEMFULL;
374  }
375 
376  lm->op.fcntl = FCNTL_EXIT;
377 
378  if (msgqueue_send(&messagesQueue, lm, sizeof(LogMessage), MsgQueue_REGULAR) < 0) {
379  return storageStatus = SDLOG_QUEUEFULL;
380  } else {
381  chThdWait(sdLogThd);
382  sdLogThd = NULL;
383  }
384 
385  }
386  return storageStatus = SDLOG_OK;
387 }
388 
389 SdioError sdLogFlushAllLogs(void)
390 {
392  if (sdLogThd == NULL) {
393  // something goes wrong, log thread is no more working
394  return storageStatus = SDLOG_NOTHREAD;
395  }
396 
397  // queue flush + close order, then stop worker thread
398  for (FileDes fd = 0; fd < SDLOG_NUM_FILES; fd++) {
399  if (fileDes[fd].inUse) {
400  status = sdLogFlushLog(fd);
401  if (status != SDLOG_OK) {
402  break;
403  }
404  }
405  }
406 
407  return storageStatus = status;
408 }
409 
410 
411 #define FD_CHECK(fd) if ((fd < 0) || (fd >= SDLOG_NUM_FILES) \
412  || (fileDes[fd].inUse == false)) \
413  return SDLOG_FATFS_ERROR
414 
415 
416 SdioError sdLogExpandLogFile(const FileDes fd, const size_t sizeInMo,
417  const bool preallocate)
418 {
419  FD_CHECK(fd);
420 
421  // expand with opt=1 : pre allocate file now
422  const FRESULT rc = f_expand(&fileDes[fd].fil, sizeInMo * 1024 * 1024, preallocate);
423  return (rc == FR_OK) ? SDLOG_OK : SDLOG_CANNOT_EXPAND;
424 }
425 
426 SdioError sdLogWriteLog(const FileDes fd, const char *fmt, ...)
427 {
428  FD_CHECK(fd);
429 
430  const SdioError status = flushWriteByteBuffer(fd);
431  storageStatus = status;
432  if (status != SDLOG_OK) {
433  return status;
434  }
435 
436  va_list ap;
437  va_start(ap, fmt);
438 
439  LogMessage *lm = tlsf_malloc_r(&HEAP_DEFAULT, LOG_MESSAGE_PREBUF_LEN);
440  if (lm == NULL) {
441  va_end(ap);
442  return storageStatus = SDLOG_MEMFULL;
443  }
444 
445  lm->op.fcntl = FCNTL_WRITE;
446  lm->op.fd = fd & 0x1f;
447 
448  chvsnprintf(lm->mess, SDLOG_MAX_MESSAGE_LEN - 1, fmt, ap);
449  lm->mess[SDLOG_MAX_MESSAGE_LEN - 1] = 0;
450  va_end(ap);
451 
452  const size_t msgLen = logMessageLen(lm);
453  lm = tlsf_realloc_r(&HEAP_DEFAULT, lm, msgLen);
454  if (lm == NULL) {
455  return storageStatus = SDLOG_MEMFULL;
456  }
457 
458  if (msgqueue_send(&messagesQueue, lm, msgLen, MsgQueue_REGULAR) < 0) {
459  return storageStatus = SDLOG_QUEUEFULL;
460  }
461 
462  return SDLOG_OK;
463 }
464 
465 SdioError sdLogFlushLog(const FileDes fd)
466 {
467  FD_CHECK(fd);
468 
469  const SdioError status = flushWriteByteBuffer(fd);
470  storageStatus = status;
471  if (status != SDLOG_OK) {
472  return status;
473  }
474 
475  // give room to send a flush order if the queue is full
476  cleanQueue(false);
477 
478  LogMessage *lm = tlsf_malloc_r(&HEAP_DEFAULT, sizeof(LogMessage));
479  if (lm == NULL) {
480  return storageStatus = SDLOG_MEMFULL;
481  }
482 
483  lm->op.fcntl = FCNTL_FLUSH;
484  lm->op.fd = fd & 0x1f;
485 
486  if (msgqueue_send(&messagesQueue, lm, sizeof(LogMessage), MsgQueue_REGULAR) < 0) {
487  return storageStatus = SDLOG_QUEUEFULL;
488  }
489 
490  return SDLOG_OK;
491 }
492 
493 SdioError sdLogCloseLog(const FileDes fd)
494 {
495  FD_CHECK(fd);
496 
497  cleanQueue(false);
498  LogMessage *lm = tlsf_malloc_r(&HEAP_DEFAULT, sizeof(LogMessage));
499  if (lm == NULL) {
500  return storageStatus = SDLOG_MEMFULL;
501  }
502 
503  lm->op.fcntl = FCNTL_CLOSE;
504  lm->op.fd = fd & 0x1f;
505 
506  if (msgqueue_send(&messagesQueue, lm, sizeof(LogMessage), MsgQueue_REGULAR) < 0) {
507  return storageStatus = SDLOG_QUEUEFULL;
508  }
509 
510  return storageStatus = SDLOG_OK;
511 }
512 
513 
514 
515 
516 
517 static inline SdioError flushWriteByteBuffer(const FileDes fd)
518 {
519  FD_CHECK(fd);
520 
521  if (unlikely(fileDes[fd].writeByteCache != NULL)) {
522  if (msgqueue_send(&messagesQueue, fileDes[fd].writeByteCache,
523  sizeof(LogMessage) + fileDes[fd].writeByteSeek,
524  MsgQueue_REGULAR) < 0) {
525  return storageStatus = SDLOG_QUEUEFULL;
526  }
527  fileDes[fd].writeByteCache = NULL;
528  }
529  return storageStatus = SDLOG_OK;
530 }
531 
532 SdioError sdLogWriteRaw(const FileDes fd, const uint8_t *buffer, const size_t len)
533 {
534  FD_CHECK(fd);
535 
536  const SdioError status = flushWriteByteBuffer(fd);
537  storageStatus = status;
538  if (status != SDLOG_OK) {
539  return status;
540  }
541 
542  LogMessage *lm = tlsf_malloc_r(&HEAP_DEFAULT, logRawLen(len));
543  if (lm == NULL) {
544  return storageStatus = SDLOG_MEMFULL;
545  }
546 
547  lm->op.fcntl = FCNTL_WRITE;
548  lm->op.fd = fd & 0x1f;
549  memcpy(lm->mess, buffer, len);
550 
551  if (msgqueue_send(&messagesQueue, lm, logRawLen(len), MsgQueue_REGULAR) < 0) {
552  return storageStatus = SDLOG_QUEUEFULL;
553  }
554 
555  return SDLOG_OK;
556 }
557 
558 SdioError sdLogAllocSDB(SdLogBuffer **sdb, const size_t len)
559 {
560  *sdb = tlsf_malloc_r(&HEAP_DEFAULT, logRawLen(len));
561  if (*sdb == NULL) {
562  return storageStatus = SDLOG_MEMFULL;
563  }
564 
565  LogMessage *lm = tlsf_malloc_r(&HEAP_DEFAULT, logRawLen(len));
566  if (lm == NULL) {
567  tlsf_free_r(&HEAP_DEFAULT, *sdb);
568  return storageStatus = SDLOG_MEMFULL;
569  }
570 
571  (*sdb)->lm = lm;
572  (*sdb)->len = len;
573  (*sdb)->offset = 0;
574  return storageStatus = SDLOG_OK;
575 }
576 
577 char *sdLogGetBufferFromSDB(SdLogBuffer *sdb)
578 {
579  return sdb->lm->mess + sdb->offset;
580 }
581 
582 bool sdLogSeekBufferFromSDB(SdLogBuffer *sdb, uint32_t offset)
583 {
584  if ((sdb->offset + offset) < sdb->len) {
585  sdb->offset += offset;
586  return true;
587  } else {
588  return false;
589  }
590 }
591 
592 size_t sdLogGetBufferLenFromSDB(SdLogBuffer *sdb)
593 {
594  return sdb->len - sdb->offset;
595 }
596 
597 SdioError sdLogWriteSDB(const FileDes fd, SdLogBuffer *sdb)
598 {
599  SdioError status = SDLOG_OK;
600 
601  if ((fd < 0) || (fd >= SDLOG_NUM_FILES) || (fileDes[fd].inUse == false)) {
602  status = SDLOG_FATFS_ERROR;
603  goto fail;
604  }
605 
606  status = flushWriteByteBuffer(fd);
607  if (status != SDLOG_OK) {
608  goto fail;
609  }
610 
611 
612  sdb->lm->op.fcntl = FCNTL_WRITE;
613  sdb->lm->op.fd = fd & 0x1f;
614 
615  if (msgqueue_send(&messagesQueue, sdb->lm, logRawLen(sdb->len), MsgQueue_REGULAR) < 0) {
616  // msgqueue_send take care of freeing lm memory even in case of failure
617  // just need to free sdb memory
618  status = SDLOG_QUEUEFULL;
619  }
620 
621  goto exit;
622 
623 fail:
624  tlsf_free_r(&HEAP_DEFAULT, sdb->lm);
625 
626 exit:
627  tlsf_free_r(&HEAP_DEFAULT, sdb);
628  return storageStatus = status;
629 }
630 
631 
632 
633 
634 SdioError sdLogWriteByte(const FileDes fd, const uint8_t value)
635 {
636  FD_CHECK(fd);
637  LogMessage *lm;
638 
639  if (fileDes[fd].writeByteCache == NULL) {
640  lm = tlsf_malloc_r(&HEAP_DEFAULT, sizeof(LogMessage) + WRITE_BYTE_CACHE_SIZE);
641  if (lm == NULL) {
642  return storageStatus = SDLOG_MEMFULL;
643  }
644 
645  lm->op.fcntl = FCNTL_WRITE;
646  lm->op.fd = fd & 0x1f;
647 
648  fileDes[fd].writeByteCache = lm;
649  fileDes[fd].writeByteSeek = 0;
650  } else {
651  lm = fileDes[fd].writeByteCache;
652  }
653 
654  lm->mess[fileDes[fd].writeByteSeek++] = value;
655 
656  if (fileDes[fd].writeByteSeek == WRITE_BYTE_CACHE_SIZE) {
657  const SdioError status = flushWriteByteBuffer(fd);
658  // message is not sent so allocated buffer will not be released by receiver side
659  // instead of freeing buffer, we just reset cache seek.
660  if (status == SDLOG_QUEUEFULL) {
661  fileDes[fd].writeByteSeek = 0;
662  return storageStatus = SDLOG_QUEUEFULL;
663  }
664  }
665  return storageStatus = SDLOG_OK;
666 }
667 
668 /*
669  if fatfs use stack for working buffers, stack size should be reserved accordingly
670  */
671 #define WA_LOG_BASE_SIZE 1024
672 #if _USE_LFN == 2
673 #if _FS_EXFAT
674 static THD_WORKING_AREA(waThdSdLog, WA_LOG_BASE_SIZE+((_MAX_LFN+1)*2)+(19*32));
675 #else
676 static THD_WORKING_AREA(waThdSdLog, WA_LOG_BASE_SIZE+((_MAX_LFN+1)*2));
677 #endif
678 #else
679 static THD_WORKING_AREA(waThdSdLog, WA_LOG_BASE_SIZE);
680 #endif
681 
682 SdioError sdLoglaunchThread ()
683 {
684  chThdSleepMilliseconds(100);
685 
686  sdLogThd = chThdCreateStatic(waThdSdLog, sizeof(waThdSdLog),
687  NORMALPRIO + 1, thdSdLog, NULL);
688  if (sdLogThd == NULL) {
689  return storageStatus = SDLOG_INTERNAL_ERROR;
690  } else {
691  return storageStatus = SDLOG_OK;
692  }
693 }
694 
695 SdioError sdLogStopThread(void)
696 {
697  SdioError retVal = SDLOG_OK;
698 
699  storageStatus = retVal;
700  if (sdLogThd == NULL) {
701  return storageStatus = SDLOG_NOTHREAD;
702  }
703 
704  LogMessage lm;
705 
706  /* // ask for closing (after flushing) all opened files */
707  /* for (uint8_t i=0; i<SDLOG_NUM_FILES; i++) { */
708  /* if (fileDes[i].inUse) { */
709  /* flushWriteByteBuffer (i); */
710  /* lm.op.fcntl = FCNTL_CLOSE; */
711  /* lm.op.fd = i & 0x1f; */
712  /* if (msgqueue_copy_send (&messagesQueue, &lm, sizeof(LogMessage), MsgQueue_OUT_OF_BAND) < 0) { */
713  /* retVal= SDLOG_QUEUEFULL; */
714  /* } */
715  /* } */
716  /* } */
717 
718  lm.op.fcntl = FCNTL_EXIT;
719  if (msgqueue_copy_send(&messagesQueue, &lm, sizeof(LogMessage), MsgQueue_OUT_OF_BAND) < 0) {
720  retVal = SDLOG_QUEUEFULL;
721  }
722 
723  chThdWait(sdLogThd);
724  sdLogThd = NULL;
725  return storageStatus = retVal;
726 }
727 #endif
728 
729 
730 SdioError getFileName(const char *prefix, const char *directoryName,
731  char *nextFileName, const size_t nameLength, const int indexOffset)
732 {
733  DIR dir; /* Directory object */
734  FRESULT rc; /* Result code */
735  FILINFO fno; /* File information object */
736  int32_t fileIndex ;
737  int32_t maxCurrentIndex = 0;
738 
739 
740  const size_t directoryNameLen = MIN(strlen(directoryName), 128);
741  const size_t slashDirNameLen = directoryNameLen + 2;
742  char slashDirName[slashDirNameLen];
743  strlcpy(slashDirName, "/", slashDirNameLen);
744  strlcat(slashDirName, directoryName, slashDirNameLen);
745 
746  rc = f_opendir(&dir, directoryName);
747  if (rc != FR_OK) {
748  rc = f_mkdir(slashDirName);
749  if (rc != FR_OK) {
750  return storageStatus = SDLOG_FATFS_ERROR;
751  }
752  rc = f_opendir(&dir, directoryName);
753  if (rc != FR_OK) {
754  return storageStatus = SDLOG_FATFS_ERROR;
755  }
756  }
757 
758  for (;;) {
759  rc = f_readdir(&dir, &fno); /* Read a directory item */
760  if (rc != FR_OK || fno.fname[0] == 0) { break; } /* Error or end of dir */
761 
762 
763  if (fno.fname[0] == '.') { continue; }
764 
765  if (!(fno.fattrib & AM_DIR)) {
766  // DebugTrace ("fno.fsize=%d fn=%s\n", fno.fsize, fn);
767  fileIndex = uiGetIndexOfLogFile(prefix, fno.fname);
768  maxCurrentIndex = MAX(maxCurrentIndex, fileIndex);
769  }
770  }
771  if (rc) {
772  return storageStatus = SDLOG_FATFS_ERROR;
773  }
774 
775  rc = f_closedir(&dir);
776  if (rc) {
777  return storageStatus = SDLOG_FATFS_ERROR;
778  }
779 
780  if (maxCurrentIndex < NUMBERMAX) {
781  chsnprintf(nextFileName, nameLength, NUMBERFMF,
782  directoryName, prefix, maxCurrentIndex + indexOffset);
783  return storageStatus = SDLOG_OK;
784  } else {
785  chsnprintf(nextFileName, nameLength, "%s\\%s%.ERR",
786  directoryName, prefix);
787  return storageStatus = SDLOG_LOGNUM_ERROR;
788  }
789 }
790 
791 SdioError removeEmptyLogs(const char *directoryName, const char *prefix, const size_t sizeConsideredEmpty)
792 {
793  DIR dir; /* Directory object */
794  FRESULT rc; /* Result code */
795  FILINFO fno; /* File information object */
796 
797 
798  rc = f_opendir(&dir, directoryName);
799  if (rc != FR_OK) {
800  return storageStatus = SDLOG_FATFS_NOENT;
801  }
802 
803  for (;;) {
804  rc = f_readdir(&dir, &fno); /* Read a directory item */
805  if (rc != FR_OK || fno.fname[0] == 0) { break; } /* Error or end of dir */
806 
807  if (fno.fname[0] == '.') { continue; }
808 
809  if (!(fno.fattrib & AM_DIR)) {
810  // DebugTrace ("fno.fsize=%d fn=%s\n", fno.fsize, fn);
811  if ((strncmp(fno.fname, prefix, strlen(prefix)) == 0) && (fno.fsize <= sizeConsideredEmpty)) {
812  char absPathName[128];
813  strlcpy(absPathName, directoryName, sizeof(absPathName));
814  strlcat(absPathName, "/", sizeof(absPathName));
815  strlcat(absPathName, fno.fname, sizeof(absPathName));
816  rc = f_unlink(absPathName);
817  if (rc) {
818  break;
819  }
820  }
821  }
822  }
823 
824  if (rc) {
825  return storageStatus = SDLOG_FATFS_ERROR;
826  }
827 
828  rc = f_closedir(&dir);
829  if (rc) {
830  return storageStatus = SDLOG_FATFS_ERROR;
831  }
832 
833  return storageStatus = SDLOG_OK;
834 }
835 
836 /*
837 # _____ _ _
838 # | __ \ (_) | |
839 # | |__) | _ __ _ __ __ __ _ | |_ ___
840 # | ___/ | '__| | | \ \ / / / _` | | __| / _ \
841 # | | | | | | \ V / | (_| | \ |_ | __/
842 # |_| |_| |_| \_/ \__,_| \__| \___|
843 */
844 
845 
846 
847 
848 
849 
850 
851 int32_t uiGetIndexOfLogFile(const char *prefix, const char *fileName)
852 {
853  const size_t len = strlen(prefix);
854 
855  // if filename does not began with prefix, return 0
856  if (strncmp(prefix, fileName, len) != 0) {
857  return 0;
858  }
859 
860  // we point on the first char after prefix
861  const char *suffix = &(fileName[len]);
862 
863  // we test that suffix is valid (at least begin with digit)
864  if (!isdigit((int) suffix[0])) {
865  // DebugTrace ("DBG> suffix = %s", suffix);
866  return 0;
867  }
868 
869  return (int32_t) atoi(suffix);
870 }
871 
872 
873 #ifdef SDLOG_NEED_QUEUE
874 static void cleanQueue(const bool allQueue)
875 {
876  if (allQueue == false) {
877  do {
878  struct tlsf_stat_t stat;
879  tlsf_stat_r(&HEAP_DEFAULT, &stat);
880  const size_t freeRam = stat.mfree;
881  chSysLock();
882  const bool queue_full = (chMBGetFreeCountI(&messagesQueue.mb) <= 0);
883  chSysUnlock();
884  // DebugTrace ("sdLogCloseLog freeRam=%d queue_full=%d", freeRam, queue_full);
885  if ((freeRam < 200) || (queue_full == true)) {
886  removeFromQueue(1);
887  } else {
888  break;
889  }
890  } while (true);
891  } else {
892  removeFromQueue(SDLOG_QUEUE_BUCKETS);
893  }
894 }
895 
896 static void removeFromQueue(const size_t nbMsgToRFemove)
897 {
898  /* struct tlsf_stat_t stat; */
899 
900  /* tlsf_stat_r (&HEAP_DEFAULT, &stat); */
901  /* size_t freeRam = stat.mfree; */
902  /* chSysLock(); */
903  /* size_t queueBuckets = chMBGetFreeCountI(&messagesQueue.mb); */
904  /* chSysUnlock(); */
905 
906  /* DebugTrace ("Before removeFromQueue (%d) : ram=%d buck=%d", nbMsgToRFemove, freeRam, queueBuckets); */
907 
908  LogMessage *lm = NULL;
909  for (size_t i = 0; i < nbMsgToRFemove; i++) {
910  const int32_t retLen = (int32_t)(msgqueue_pop_timeout(&messagesQueue, (void **) &lm, TIME_IMMEDIATE));
911  if (retLen < 0) {
912  break;
913  }
915  }
916 
917  /* tlsf_stat_r (&HEAP_DEFAULT, &stat); */
918  /* freeRam = stat.mfree; */
919  /* chSysLock(); */
920  /* queueBuckets = chMBGetFreeCountI(&messagesQueue.mb); */
921  /* chSysUnlock(); */
922 
923  /* DebugTrace ("After removeFromQueue (%d) : ram=%d buck=%d", nbMsgToRFemove, freeRam, queueBuckets); */
924 }
925 
926 
927 #if (CH_KERNEL_MAJOR > 2)
928 static void thdSdLog(void *arg)
929 #else
930 static msg_t thdSdLog(void *arg)
931 #endif
932 {
933  (void) arg;
934  struct PerfBuffer {
935  // each element of buffer should be word aligned for sdio efficient write
936  ALIGNED_VAR(4) uint8_t buffer[SDLOG_WRITE_BUFFER_SIZE] ;
937  uint16_t size;
938  } ;
939 
940  UINT bw;
941  static IN_DMA_SECTION_CLEAR(struct PerfBuffer perfBuffers[SDLOG_NUM_FILES]);
942  storageStatus = SDLOG_OK;
943  chRegSetThreadName("thdSdLog");
944  while (true) {
945  LogMessage *lm = NULL;
946  const int32_t retLen = (int32_t)(msgqueue_pop(&messagesQueue, (void **) &lm));
947  if (retLen > 0) {
948  FIL *fo = &fileDes[lm->op.fd].fil;
949  uint8_t *const perfBuffer = perfBuffers[lm->op.fd].buffer;
950 
951  switch (lm->op.fcntl) {
952 
953  case FCNTL_FLUSH:
954  case FCNTL_CLOSE: {
955  const uint16_t curBufFill = perfBuffers[lm->op.fd].size;
956  if (fileDes[lm->op.fd].inUse) {
957  if (curBufFill) {
958  f_write(fo, perfBuffer, curBufFill, &bw);
959  nbBytesWritten += bw;
960  perfBuffers[lm->op.fd].size = 0;
961  }
962  if (lm->op.fcntl == FCNTL_FLUSH) {
963  f_sync(fo);
964  } else { // close
965  if (fileDes[lm->op.fd].tagAtClose) {
966  f_write(fo, "\r\nEND_OF_LOG\r\n", 14, &bw);
967  nbBytesWritten += bw;
968  }
969  f_close(fo);
970  fileDes[lm->op.fd].inUse = false; // store that file is closed
971  }
972  }
973  }
974  break;
975 
976  case FCNTL_EXIT:
977  tlsf_free_r(&HEAP_DEFAULT, lm); // to avoid a memory leak
978  chThdExit(storageStatus = SDLOG_NOTHREAD);
979  break; /* To exit from thread when asked : chThdTerminate
980  then send special message with FCNTL_EXIT */
981 
982 
983  case FCNTL_WRITE: {
984  const uint16_t curBufFill = perfBuffers[lm->op.fd].size;
985  if (fileDes[lm->op.fd].inUse) {
986  const int32_t messLen = retLen - (int32_t)(sizeof(LogMessage));
987  if (messLen < (SDLOG_WRITE_BUFFER_SIZE - curBufFill)) {
988  // the buffer can accept this message
989  memcpy(&(perfBuffer[curBufFill]), lm->mess, (size_t)(messLen));
990  perfBuffers[lm->op.fd].size = (uint16_t)((perfBuffers[lm->op.fd].size) + messLen);
991  } else {
992  // fill the buffer
993  const int32_t stayLen = SDLOG_WRITE_BUFFER_SIZE - curBufFill;
994  memcpy(&(perfBuffer[curBufFill]), lm->mess, (size_t)(stayLen));
995  FRESULT rc = f_write(fo, perfBuffer, SDLOG_WRITE_BUFFER_SIZE, &bw);
996  nbBytesWritten += bw;
997  // if there an autoflush period specified, flush to the mass storage media
998  // if timer has expired and rearm.
999  if (fileDes[lm->op.fd].autoFlushPeriod) {
1000  const systime_t now = chVTGetSystemTimeX();
1001  if ((now - fileDes[lm->op.fd].lastFlushTs) >
1002  (fileDes[lm->op.fd].autoFlushPeriod * CH_CFG_ST_FREQUENCY)) {
1003  f_sync(fo);
1004  fileDes[lm->op.fd].lastFlushTs = now;
1005  }
1006  }
1007  if (rc) {
1008  //chThdExit(storageStatus = SDLOG_FATFS_ERROR);
1009  storageStatus = SDLOG_FATFS_ERROR;
1010  } else if (bw != SDLOG_WRITE_BUFFER_SIZE) {
1011  chThdExit(storageStatus = SDLOG_FSFULL);
1012  }
1013 
1014  memcpy(perfBuffer, &(lm->mess[stayLen]), (uint32_t)(messLen - stayLen));
1015  perfBuffers[lm->op.fd].size = (uint16_t)(messLen - stayLen); // curBufFill
1016  }
1017  }
1018  }
1019  }
1020  tlsf_free_r(&HEAP_DEFAULT, lm);
1021  } else {
1022  chThdExit(storageStatus = SDLOG_INTERNAL_ERROR);
1023  }
1024  }
1025 #if (CH_KERNEL_MAJOR == 2)
1026  return SDLOG_OK;
1027 #endif
1028 }
1029 
1030 static size_t logMessageLen(const LogMessage *lm)
1031 {
1032  return sizeof(LogMessage) + strnlen(lm->mess, SDLOG_MAX_MESSAGE_LEN);
1033 }
1034 
1035 static size_t logRawLen(const size_t len)
1036 {
1037  return sizeof(LogMessage) + len;
1038 }
1039 
1040 static SdioError getNextFIL(FileDes *fd)
1041 {
1042  // if there is a free slot in fileDes, use it
1043  // else, if all slots are buzy, maximum open files limit
1044  // is reach.
1045  for (FileDes i = 0; i < SDLOG_NUM_FILES; i++) {
1046  if (fileDes[i].inUse == false) {
1047  *fd = i;
1048  fileDes[i].inUse = true;
1049  return SDLOG_OK;
1050  }
1051  }
1052  return SDLOG_FDFULL;
1053 }
1054 
1055 size_t sdLogGetNbBytesWrittenToStorage(void)
1056 {
1057  return nbBytesWritten;
1058 }
1059 
1060 SdioError sdLogGetStorageStatus(void)
1061 {
1062  return storageStatus;
1063 }
1064 
1065 
1066 #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:128
#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:405
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:851
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: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:730
uint32_t DWORD
Definition: usb_msd.c:133
#define SDLOG_NUM_FILES
Definition: mcuconf.h:406
#define SDLOG_QUEUE_BUCKETS
Definition: mcuconf.h:404
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
static const float dir[]
SdioError removeEmptyLogs(const char *directoryName, const char *prefix, const size_t sizeConsideredEmpty)
remove spurious log file left on sd
Definition: sdLog.c:791
static THD_WORKING_AREA(wa_thd_spi1, SPI_THREAD_STACK_SIZE)
SdioError
Definition: sdLog.h:111
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:127
void * tlsf_malloc_r(tlsf_memory_heap_t *heap, size_t bytes)
#define MIN(x, y)
Definition: sdLog.c:42