39 #define MIN(x , y) (((x) < (y)) ? (x) : (y))
42 #define MAX(x , y) (((x) > (y)) ? (x) : (y))
44 #define IS_POWER_OF_TWO(s) ((s) && !((s) & ((s) - 1)))
46 #ifndef SDLOG_NUM_FILES
47 #error SDLOG_NUM_FILES should be defined in mcuconf.h
52 #define FFCONF_DEF _FATFS
56 #error upgrade FATFS to 0.14 at least
58 #if FF_FS_LOCK != 0 && FF_FS_LOCK < SDLOG_NUM_FILES
59 #error if FF_FS_LOCK is not zero, it should be equal of superior to SDLOG_NUM_FILES
63 #ifndef SDLOG_ALL_BUFFERS_SIZE
64 #error SDLOG_ALL_BUFFERS_SIZE should be defined in mcuconf.h
67 #if SDLOG_ALL_BUFFERS_SIZE > 65536
68 #error constraint 512 <= SDLOG_ALL_BUFFERS_SIZE <= 65536 not meet
71 #define SDLOG_WRITE_BUFFER_SIZE (SDLOG_ALL_BUFFERS_SIZE/SDLOG_NUM_FILES)
73 #ifndef SDLOG_MAX_MESSAGE_LEN
74 #error SDLOG_MAX_MESSAGE_LENshould be defined in mcuconf.h
77 #ifndef SDLOG_QUEUE_BUCKETS
78 #error SDLOG_QUEUE_BUCKETS should be defined in mcuconf.h
81 #if FF_FS_REENTRANT == 0
82 #warning "FF_FS_REENTRANT = 0 in ffconf.h DO NOT open close file during log"
85 #if SDLOG_WRITE_BUFFER_SIZE < 512
86 #error SDLOG_ALL_BUFFERS_SIZE / SDLOG_NUM_FILES cannot be < 512
89 #if (!(IS_POWER_OF_TWO (SDLOG_WRITE_BUFFER_SIZE)))
90 #error SDLOG_ALL_BUFFERS_SIZE / SDLOG_NUM_FILES should be a POWER OF 2
93 #ifdef SDLOG_NEED_QUEUE
127 #define IN_SDMMC_DMA_SECTION(x) IN_SDMMC_SECTION(x)
128 #define IN_SDMMC_DMA_SECTION_CLEAR(x) IN_SDMMC_SECTION_CLEAR(x)
129 #define IN_SDMMC_DMA_SECTION_NOINIT(x) IN_SDMMC_SECTION_NOINIT(x)
131 #define IN_SDMMC_DMA_SECTION(x) IN_DMA_SECTION(x)
132 #define IN_SDMMC_DMA_SECTION_CLEAR(x) IN_DMA_SECTION_CLEAR(x)
133 #define IN_SDMMC_DMA_SECTION_NOINIT(x) IN_DMA_SECTION_NOINIT(x)
152 #define WRITE_BYTE_CACHE_SIZE 15
164 struct FilePoolUnit {
167 systime_t lastFlushTs;
172 LogMessage *writeByteCache;
178 .fil = {{0}}, .inUse =
false, .tagAtClose =
false,
179 .writeByteCache = NULL, .writeByteSeek = 0
183 static volatile size_t nbBytesWritten = 0;
193 struct _SdLogBuffer {
200 #define LOG_MESSAGE_PREBUF_LEN (SDLOG_MAX_MESSAGE_LEN+sizeof(LogMessage))
206 #ifdef SDLOG_NEED_QUEUE
207 static size_t logMessageLen(
const LogMessage *lm);
208 static size_t logRawLen(
const size_t len);
209 static SdioError sdLoglaunchThread(
void);
211 static thread_t *sdLogThd = NULL;
213 static void removeFromQueue(
const size_t nbMsgToRFemov);
214 static void cleanQueue(
const bool allQueue);
215 static SdioError sdLogExpandLogFile(
const FileDes fileObject,
const size_t sizeInMo,
216 const bool preallocate);
218 static void thdSdLog(
void *arg) ;
232 if (sdLogThd != NULL) {
237 #ifdef SDLOG_NEED_QUEUE
241 if (!sdc_lld_is_card_inserted(NULL)) {
247 chThdSleepMilliseconds(10);
254 #if FFCONF_DEF < 8000
255 FRESULT rc = f_mount(0, &fatfs);
257 FRESULT rc = f_mount(&fatfs,
"/", 1);
264 if (freeSpaceInKo != NULL) {
265 f_getfree(
"/", &clusters, &fsp);
266 *freeSpaceInKo = clusters * (
uint32_t)fatfs.csize / 2;
269 #ifdef SDLOG_NEED_QUEUE
271 fileDes[i].inUse = fileDes[i].tagAtClose =
false;
272 fileDes[i].writeByteCache = NULL;
273 fileDes[i].writeByteSeek = 0;
276 storageStatus = sdLoglaunchThread();
280 return storageStatus;
287 #if FFCONF_DEF < 8000
288 FRESULT rc = f_mount(0, NULL);
290 FRESULT rc = f_mount(NULL,
"", 0);
305 #ifdef SDLOG_NEED_QUEUE
307 const uint32_t autoFlushPeriod,
const bool appendTagAtClose,
308 const size_t sizeInMo,
const bool preallocate,
char *fileName,
const size_t nameLength)
322 sde = getNextFIL(&ldf);
324 return storageStatus = sde;
327 sde =
getFileName(prefix, directoryName, fileName, nameLength, +1);
330 fileDes[ldf].inUse =
false;
335 rc = f_open(&fileDes[ldf].fil, fileName, FA_WRITE | FA_CREATE_ALWAYS);
337 fileDes[ldf].inUse =
false;
340 fileDes[ldf].tagAtClose = appendTagAtClose;
341 fileDes[ldf].autoFlushPeriod = autoFlushPeriod;
342 fileDes[ldf].lastFlushTs = 0;
343 sde = sdLogExpandLogFile(ldf, sizeInMo, preallocate);
347 return storageStatus = sde;
355 if (flush ==
false) {
362 if (fileDes[
fd].inUse) {
363 FIL *
fo = &fileDes[
fd].fil;
364 if (fileDes[
fd].tagAtClose) {
365 f_write(
fo,
"\r\nEND_OF_LOG\r\n", 14, &bw);
366 nbBytesWritten += bw;
368 FRESULT trc = f_close(
fo);
369 fileDes[
fd].inUse =
false;
382 if (sdLogThd == NULL) {
389 if (fileDes[
fd].inUse) {
400 lm->op.fcntl = FCNTL_EXIT;
416 if (sdLogThd == NULL) {
423 if (fileDes[
fd].inUse) {
431 return storageStatus =
status;
435 #define FD_CHECK(fd) if ((fd < 0) || (fd >= SDLOG_NUM_FILES) \
436 || (fileDes[fd].inUse == false)) \
437 return SDLOG_FATFS_ERROR
441 const bool preallocate)
446 const FRESULT rc = f_expand(&fileDes[
fd].fil, sizeInMo * 1024 * 1024, preallocate);
469 lm->op.fcntl = FCNTL_WRITE;
470 lm->op.fd =
fd & 0x1f;
476 const size_t msgLen = logMessageLen(lm);
507 lm->op.fcntl = FCNTL_FLUSH;
508 lm->op.fd =
fd & 0x1f;
527 lm->op.fcntl = FCNTL_CLOSE;
528 lm->op.fd =
fd & 0x1f;
545 if (unlikely(fileDes[
fd].writeByteCache != NULL)) {
547 sizeof(LogMessage) + fileDes[
fd].writeByteSeek,
551 fileDes[
fd].writeByteCache = NULL;
571 lm->op.fcntl = FCNTL_WRITE;
572 lm->op.fd =
fd & 0x1f;
573 memcpy(lm->mess, buffer, len);
603 return sdb->lm->mess + sdb->offset;
608 if ((sdb->offset +
offset) < sdb->len) {
618 return sdb->len - sdb->offset;
636 sdb->lm->op.fcntl = FCNTL_WRITE;
637 sdb->lm->op.fd =
fd & 0x1f;
652 return storageStatus =
status;
663 if (fileDes[
fd].writeByteCache == NULL) {
669 lm->op.fcntl = FCNTL_WRITE;
670 lm->op.fd =
fd & 0x1f;
672 fileDes[
fd].writeByteCache = lm;
673 fileDes[
fd].writeByteSeek = 0;
675 lm = fileDes[
fd].writeByteCache;
678 lm->mess[fileDes[
fd].writeByteSeek++] = value;
680 if (fileDes[
fd].writeByteSeek == WRITE_BYTE_CACHE_SIZE) {
685 fileDes[
fd].writeByteSeek = 0;
695 #define WA_LOG_BASE_SIZE 1024
708 chThdSleepMilliseconds(100);
710 sdLogThd = chThdCreateStatic(waThdSdLog,
sizeof(waThdSdLog),
711 NORMALPRIO + 1, thdSdLog, NULL);
712 if (sdLogThd == NULL) {
723 storageStatus = retVal;
724 if (sdLogThd == NULL) {
742 lm.op.fcntl = FCNTL_EXIT;
749 return storageStatus = retVal;
755 char *nextFileName,
const size_t nameLength,
const int indexOffset)
764 const size_t directoryNameLen =
MIN(strlen(directoryName), 128);
765 const size_t slashDirNameLen = directoryNameLen + 2;
766 char slashDirName[slashDirNameLen];
767 strlcpy(slashDirName,
"/", slashDirNameLen);
768 strlcat(slashDirName, directoryName, slashDirNameLen);
770 rc = f_opendir(&
dir, directoryName);
772 rc = f_mkdir(slashDirName);
776 rc = f_opendir(&
dir, directoryName);
783 rc = f_readdir(&
dir, &fno);
784 if (rc != FR_OK || fno.fname[0] == 0) {
break; }
787 if (fno.fname[0] ==
'.') {
continue; }
789 if (!(fno.fattrib & AM_DIR)) {
792 maxCurrentIndex =
MAX(maxCurrentIndex, fileIndex);
799 rc = f_closedir(&
dir);
806 directoryName, prefix, maxCurrentIndex + indexOffset);
809 chsnprintf(nextFileName, nameLength,
"%s\\%s%.ERR",
810 directoryName, prefix);
822 rc = f_opendir(&
dir, directoryName);
828 rc = f_readdir(&
dir, &fno);
829 if (rc != FR_OK || fno.fname[0] == 0) {
break; }
831 if (fno.fname[0] ==
'.') {
continue; }
833 if (!(fno.fattrib & AM_DIR)) {
835 if ((strncmp(fno.fname, prefix, strlen(prefix)) == 0) && (fno.fsize <= sizeConsideredEmpty)) {
836 char absPathName[128];
837 strlcpy(absPathName, directoryName,
sizeof(absPathName));
838 strlcat(absPathName,
"/",
sizeof(absPathName));
839 strlcat(absPathName, fno.fname,
sizeof(absPathName));
840 rc = f_unlink(absPathName);
852 rc = f_closedir(&
dir);
877 const size_t len = strlen(prefix);
880 if (strncmp(prefix, fileName, len) != 0) {
885 const char *suffix = &(fileName[len]);
888 if (!isdigit((
int) suffix[0])) {
897 #ifdef SDLOG_NEED_QUEUE
898 static void cleanQueue(
const bool allQueue)
900 if (allQueue ==
false) {
904 const size_t freeRam = stat.mfree;
906 const bool queue_full = (chMBGetFreeCountI(&messagesQueue.
mb) <= 0);
909 if ((freeRam < 200) || (queue_full ==
true)) {
920 static void removeFromQueue(
const size_t nbMsgToRFemove)
932 LogMessage *lm = NULL;
933 for (
size_t i = 0; i < nbMsgToRFemove; i++) {
951 static void thdSdLog(
void *arg)
961 static IN_SDMMC_DMA_SECTION_CLEAR(struct PerfBuffer perfBuffers[
SDLOG_NUM_FILES]);
963 chRegSetThreadName("thdSdLog");
965 LogMessage *lm = NULL;
968 FIL *
fo = &fileDes[lm->op.fd].fil;
969 uint8_t *
const perfBuffer = perfBuffers[lm->op.fd].buffer;
971 switch (lm->op.fcntl) {
975 const uint16_t curBufFill = perfBuffers[lm->op.fd].size;
976 if (fileDes[lm->op.fd].inUse) {
978 f_write(
fo, perfBuffer, curBufFill, &bw);
979 nbBytesWritten += bw;
980 perfBuffers[lm->op.fd].size = 0;
982 if (lm->op.fcntl == FCNTL_FLUSH) {
985 if (fileDes[lm->op.fd].tagAtClose) {
986 f_write(
fo,
"\r\nEND_OF_LOG\r\n", 14, &bw);
987 nbBytesWritten += bw;
990 fileDes[lm->op.fd].inUse =
false;
1004 const uint16_t curBufFill = perfBuffers[lm->op.fd].size;
1005 if (fileDes[lm->op.fd].inUse) {
1009 memcpy(&(perfBuffer[curBufFill]), lm->mess, (
size_t)(messLen));
1010 perfBuffers[lm->op.fd].size = (
uint16_t)((perfBuffers[lm->op.fd].size) + messLen);
1014 memcpy(&(perfBuffer[curBufFill]), lm->mess, (
size_t)(stayLen));
1016 nbBytesWritten += bw;
1019 if (fileDes[lm->op.fd].autoFlushPeriod) {
1020 const systime_t now = chVTGetSystemTimeX();
1021 if ((now - fileDes[lm->op.fd].lastFlushTs) >
1024 fileDes[lm->op.fd].lastFlushTs = now;
1034 memcpy(perfBuffer, &(lm->mess[stayLen]), (
uint32_t)(messLen - stayLen));
1035 perfBuffers[lm->op.fd].size = (
uint16_t)(messLen - stayLen);
1047 static size_t logMessageLen(
const LogMessage *lm)
1052 static size_t logRawLen(
const size_t len)
1054 return sizeof(LogMessage) + len;
1063 if (fileDes[i].inUse ==
false) {
1065 fileDes[i].inUse =
true;
1072 size_t sdLogGetNbBytesWrittenToStorage(
void)
1074 return nbBytesWritten;
1079 return storageStatus;
static const float offset[]
void chvsnprintf(char *buffer, size_t size, const char *fmt, va_list ap)
void chsnprintf(char *buffer, size_t size, const char *fmt,...)
#define CH_CFG_ST_FREQUENCY
System tick frequency.
THD_WORKING_AREA(wa_thd_ap, THD_WORKING_AREA_MAIN)
#define SDLOG_MAX_MESSAGE_LEN
#define SDLOG_QUEUE_BUCKETS
int32_t msgqueue_pop_timeout(MsgQueue *que, void **msgPtr, const systime_t timout)
receive message specifying timeout
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
int32_t msgqueue_pop(MsgQueue *que, void **msgPtr)
wait then receive message
void msgqueue_init(MsgQueue *que, tlsf_memory_heap_t *heap, msg_t *mb_buf, const cnt_t mb_size)
initialise MsgQueue
int32_t msgqueue_copy_send(MsgQueue *que, const void *msg, const uint16_t msgLen, const MsgQueueUrgency urgency)
send a buffer NOT previously allocated
Mini printf-like functionality.
Specific RAM section for DMA usage on F7.
#define IN_STD_SECTION_CLEAR(var)
SdioError sdLogFinish(void)
unmount filesystem
SdioError removeEmptyLogs(const char *directoryName, const char *prefix, const size_t sizeConsideredEmpty)
remove spurious log file left on sd
static int32_t uiGetIndexOfLogFile(const char *prefix, const char *fileName)
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
SdioError sdLogInit(uint32_t *freeSpaceInKo)
initialise sdLog
static SdioError flushWriteByteBuffer(const FileDes fd)
static IN_SDMMC_DMA_SECTION(FATFS fatfs)
#define SDLOG_WRITE_BUFFER_SIZE
struct _SdLogBuffer SdLogBuffer
arch independent SDIO API
bool sdio_connect(SDCDriver *sdc)
Connect a SD card on SDIO peripheral.
bool sdio_disconnect(SDCDriver *sdc)
Disconnect a SD card on SDIO peripheral.
void tlsf_free_r(tlsf_memory_heap_t *heap, void *ptr)
void tlsf_stat_r(tlsf_memory_heap_t *heap, struct tlsf_stat_t *stat)
void * tlsf_realloc_r(tlsf_memory_heap_t *heap, void *ptr, size_t bytes)
void * tlsf_malloc_r(tlsf_memory_heap_t *heap, size_t bytes)
unsigned short uint16_t
Typedef defining 16 bit unsigned short type.
int int32_t
Typedef defining 32 bit int type.
unsigned int uint32_t
Typedef defining 32 bit unsigned int type.
unsigned char uint8_t
Typedef defining 8 bit unsigned char type.