39 #define likely(x) __builtin_expect(!!(x), 1)
40 #define unlikely(x) __builtin_expect(!!(x), 0)
42 #define MIN(x , y) (((x) < (y)) ? (x) : (y))
43 #define MAX(x , y) (((x) > (y)) ? (x) : (y))
55 #ifndef SDLOG_NUM_BUFFER
56 #define SDLOG_NUM_BUFFER 2
59 #ifndef SDLOG_ALL_BUFFERS_SIZE
60 #error SDLOG_ALL_BUFFERS_SIZE should be defined in mcuconf.h
63 #define SDLOG_WRITE_BUFFER_SIZE (SDLOG_ALL_BUFFERS_SIZE/SDLOG_NUM_BUFFER)
65 #ifndef SDLOG_MAX_MESSAGE_LEN
66 #error SDLOG_MAX_MESSAGE_LEN should be defined in mcuconf.h
69 #ifndef SDLOG_QUEUE_BUCKETS
70 #error SDLOG_QUEUE_BUCKETS should be defined in mcuconf.h
73 #if _FS_REENTRANT == 0
74 #warning "_FS_REENTRANT = 0 in ffconf.h DO NOT open close file during log"
78 #ifdef SDLOG_NEED_QUEUE
82 #define NODMA_SECTION ".ram4"
83 #define DMA_SECTION ".ram0"
84 #elif defined STM32F7XX
85 #define NODMA_SECTION ".ram0"
86 #define DMA_SECTION ".ram3"
88 #error "section defined only for STM32F4 and STM32F7"
91 static msg_t queMbBuffer[
SDLOG_QUEUE_BUCKETS] __attribute__((section(NODMA_SECTION), aligned(8))) ;
94 #define WRITE_BYTE_CACHE_SIZE 15 // limit overhead :
106 struct FilePoolUnit {
112 LogMessage *writeByteCache;
118 .fil = {0}, .inUse =
false, .tagAtClose =
false,
119 .writeByteCache = NULL, .writeByteSeek = 0
130 struct _SdLogBuffer {
138 #define LOG_MESSAGE_PREBUF_LEN (SDLOG_MAX_MESSAGE_LEN+sizeof(LogMessage))
139 #endif // SDLOG_NEED_QUEUE
144 #ifdef SDLOG_NEED_QUEUE
145 static size_t logMessageLen(
const LogMessage *lm);
146 static size_t logRawLen(
const size_t len);
147 static SdioError sdLoglaunchThread(
void);
149 static thread_t *sdLogThd = NULL;
152 #if (CH_KERNEL_MAJOR > 2)
153 static void thdSdLog(
void *arg) ;
155 static msg_t thdSdLog(
void *arg) ;
158 #endif // SDLOG_NEED_QUEUE
172 if (sdLogThd != NULL) {
177 #ifdef SDLOG_NEED_QUEUE
181 if (!sdc_lld_is_card_inserted(NULL)) {
187 chThdSleepMilliseconds(10);
195 FRESULT rc = f_mount(0, &fatfs);
197 FRESULT rc = f_mount(&fatfs,
"/", 1);
204 if (freeSpaceInKo != NULL) {
205 f_getfree(
"/", &clusters, &fsp);
206 *freeSpaceInKo = clusters * (
uint32_t)fatfs.csize / 2;
209 #ifdef SDLOG_NEED_QUEUE
211 fileDes[i].inUse = fileDes[i].tagAtClose =
false;
212 fileDes[i].writeByteCache = NULL;
213 fileDes[i].writeByteSeek = 0;
216 return sdLoglaunchThread();
227 FRESULT rc = f_mount(0, NULL);
229 FRESULT rc = f_mount(NULL,
"", 0);
244 #ifdef SDLOG_NEED_QUEUE
246 bool appendTagAtClose)
254 sde = getNextFIL(fd);
259 sde =
getFileName(prefix, directoryName, fileName,
sizeof(fileName), +1);
266 rc = f_open(&fileDes[*fd].fil, fileName, FA_WRITE | FA_CREATE_ALWAYS);
268 fileDes[*
fd].inUse =
false;
271 fileDes[*
fd].tagAtClose = appendTagAtClose;
285 if (flush ==
false) {
289 if (fileDes[fd].inUse) {
290 FIL *fileObject = &fileDes[
fd].fil;
292 FRESULT trc = f_close(fileObject);
293 fileDes[
fd].inUse =
false;
306 if (sdLogThd == NULL) {
313 if (fileDes[fd].inUse) {
324 lm->op.fcntl = FCNTL_EXIT;
341 if ((fd >= SDLOG_NUM_BUFFER) || (fileDes[fd].inUse ==
false)) {
359 lm->op.fcntl = FCNTL_WRITE;
360 lm->op.fd = fd & 0x1f;
366 const size_t msgLen = logMessageLen(lm);
381 if ((fd >= SDLOG_NUM_BUFFER) || (fileDes[fd].inUse ==
false)) {
395 lm->op.fcntl = FCNTL_FLUSH;
396 lm->op.fd = fd & 0x1f;
407 if ((fd >= SDLOG_NUM_BUFFER) || (fileDes[fd].inUse ==
false)) {
416 lm->op.fcntl = FCNTL_CLOSE;
417 lm->op.fd = fd & 0x1f;
432 if ((fd >= SDLOG_NUM_BUFFER) || (fileDes[fd].inUse ==
false)) {
436 if (
unlikely(fileDes[fd].writeByteCache != NULL)) {
437 if (
msgqueue_send(&messagesQueue, fileDes[fd].writeByteCache,
438 sizeof(LogMessage) + fileDes[fd].writeByteSeek,
442 fileDes[
fd].writeByteCache = NULL;
449 if ((fd >= SDLOG_NUM_BUFFER) || (fileDes[fd].inUse ==
false)) {
463 lm->op.fcntl = FCNTL_WRITE;
464 lm->op.fd = fd & 0x1f;
465 memcpy(lm->mess, buffer, len);
495 return sdb->lm->mess + sdb->offset;
500 if ((sdb->offset + offset) < sdb->len) {
510 return sdb->len - sdb->offset;
517 if ((fd >= SDLOG_NUM_BUFFER) || (fileDes[fd].inUse ==
false)) {
527 sdb->lm->op.fcntl = FCNTL_WRITE;
528 sdb->lm->op.fd = fd & 0x1f;
550 if ((fd >= SDLOG_NUM_BUFFER) || (fileDes[fd].inUse ==
false)) {
555 if (fileDes[fd].writeByteCache == NULL) {
561 lm->op.fcntl = FCNTL_WRITE;
562 lm->op.fd = fd & 0x1f;
564 fileDes[
fd].writeByteCache = lm;
565 fileDes[
fd].writeByteSeek = 0;
567 lm = fileDes[
fd].writeByteCache;
570 lm->mess[fileDes[
fd].writeByteSeek++] = value;
572 if (fileDes[fd].writeByteSeek == WRITE_BYTE_CACHE_SIZE) {
577 fileDes[
fd].writeByteSeek = 0;
594 chThdSleepMilliseconds(100);
596 sdLogThd = chThdCreateStatic(waThdSdLog,
sizeof(waThdSdLog),
597 NORMALPRIO + 1, thdSdLog, NULL);
598 if (sdLogThd == NULL) {
609 if (sdLogThd == NULL) {
617 if (fileDes[i].inUse) {
619 lm.op.fcntl = FCNTL_CLOSE;
627 lm.op.fcntl = FCNTL_EXIT;
632 chThdTerminate(sdLogThd);
641 char *nextFileName,
const size_t nameLength,
const int indexOffset)
652 fno.lfsize =
sizeof lfn;
654 const size_t directoryNameLen =
MIN(strlen(directoryName), 128);
655 const size_t slashDirNameLen = directoryNameLen + 2;
656 char slashDirName[slashDirNameLen];
657 strlcpy(slashDirName,
"/", slashDirNameLen);
658 strlcat(slashDirName, directoryName, slashDirNameLen);
660 rc = f_opendir(&dir, directoryName);
662 rc = f_mkdir(slashDirName);
666 rc = f_opendir(&dir, directoryName);
673 rc = f_readdir(&dir, &fno);
674 if (rc != FR_OK || fno.fname[0] == 0) {
break; }
676 fn = *fno.lfname ? fno.lfname : fno.fname;
680 if (fn[0] ==
'.') {
continue; }
682 if (!(fno.fattrib & AM_DIR)) {
685 maxCurrentIndex =
MAX(maxCurrentIndex, fileIndex);
692 rc = f_closedir(&dir);
699 directoryName, prefix, maxCurrentIndex + indexOffset);
702 chsnprintf(nextFileName, nameLength,
"%s\\%s%.ERR",
703 directoryName, prefix);
717 fno.lfsize =
sizeof lfn;
720 rc = f_opendir(&dir, directoryName);
726 rc = f_readdir(&dir, &fno);
727 if (rc != FR_OK || fno.fname[0] == 0) {
break; }
729 fn = *fno.lfname ? fno.lfname : fno.fname;
733 if (fn[0] ==
'.') {
continue; }
735 if (!(fno.fattrib & AM_DIR)) {
737 if ((strncmp(fn, prefix, strlen(prefix)) == 0) && (fno.fsize <= sizeConsideredEmpty)) {
738 char absPathName[128];
739 strlcpy(absPathName, directoryName,
sizeof(absPathName));
740 strlcat(absPathName,
"/",
sizeof(absPathName));
741 strlcat(absPathName, fn,
sizeof(absPathName));
742 rc = f_unlink(absPathName);
754 rc = f_closedir(&dir);
779 const size_t len = strlen(prefix);
782 if (strncmp(prefix, fileName, len) != 0) {
787 const char *suffix = &(fileName[len]);
790 if (!isdigit((
int) suffix[0])) {
799 #ifdef SDLOG_NEED_QUEUE
800 #if (CH_KERNEL_MAJOR > 2)
801 static void thdSdLog(
void *arg)
803 static msg_t thdSdLog(
void *arg)
813 static struct PerfBuffer perfBuffers[SDLOG_NUM_BUFFER] __attribute__((section(DMA_SECTION), aligned(8))) = {
814 [0 ... SDLOG_NUM_BUFFER - 1] = {.buffer = {0}, .size = 0}
819 memset(perfBuffers, 0, SDLOG_NUM_BUFFER *
sizeof(
struct PerfBuffer));
821 chRegSetThreadName(
"thdSdLog");
822 while (!chThdShouldTerminateX()) {
826 FIL *
fo = &fileDes[lm->op.fd].fil;
827 uint8_t *
const perfBuffer = perfBuffers[lm->op.fd].buffer;
829 switch (lm->op.fcntl) {
833 const uint16_t curBufFill = perfBuffers[lm->op.fd].size;
834 if (fileDes[lm->op.fd].inUse) {
836 f_write(fo, perfBuffer, curBufFill, &bw);
837 perfBuffers[lm->op.fd].size = 0;
839 if (lm->op.fcntl == FCNTL_FLUSH) {
842 if (fileDes[lm->op.fd].tagAtClose) {
843 f_write(fo,
"\r\nEND_OF_LOG\r\n", 14, &bw);
846 fileDes[lm->op.fd].inUse =
false;
859 const uint16_t curBufFill = perfBuffers[lm->op.fd].size;
860 if (fileDes[lm->op.fd].inUse) {
864 memcpy(&(perfBuffer[curBufFill]), lm->mess, (
size_t)(messLen));
865 perfBuffers[lm->op.fd].size = (
uint16_t)((perfBuffers[lm->op.fd].size) + messLen);
869 memcpy(&(perfBuffer[curBufFill]), lm->mess, (
size_t)(stayLen));
878 memcpy(perfBuffer, &(lm->mess[stayLen]), (
uint32_t)(messLen - stayLen));
879 perfBuffers[lm->op.fd].size = (
uint16_t)(messLen - stayLen);
889 #if (CH_KERNEL_MAJOR == 2)
894 static size_t logMessageLen(
const LogMessage *lm)
899 static size_t logRawLen(
const size_t len)
901 return sizeof(LogMessage) + len;
910 if (fileDes[i].inUse ==
false) {
912 fileDes[i].inUse =
true;
SdioError sdLogFinish(void)
unmount filesystem
static struct EcefCoor_d offset
Mini printf-like functionality.
arch independent SDIO API
static SdioError flushWriteByteBuffer(const FileDes fd)
#define SDLOG_WRITE_BUFFER_SIZE
void msgqueue_init(MsgQueue *que, tlsf_memory_heap_t *heap, msg_t *mb_buf, const cnt_t mb_size)
initialise MsgQueue
#define SDLOG_MAX_MESSAGE_LEN
void chsnprintf(char *buffer, size_t size, const char *fmt,...)
static int32_t uiGetIndexOfLogFile(const char *prefix, const char *fileName)
int32_t msgqueue_copy_send(MsgQueue *que, const void *msg, const uint16_t msgLen, const MsgQueueUrgency urgency)
send a buffer NOT previously allocated
bool sdio_disconnect(void)
Disconnect a SD card on SDIO peripheral.
void tlsf_free_r(tlsf_memory_heap_t *heap, void *ptr)
SdioError sdLogInit(uint32_t *freeSpaceInKo)
initialise sdLog
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
#define SDLOG_QUEUE_BUCKETS
bool sdio_connect(void)
Connect a SD card on SDIO peripheral.
void * tlsf_realloc_r(tlsf_memory_heap_t *heap, void *ptr, size_t bytes)
SdioError removeEmptyLogs(const char *directoryName, const char *prefix, const size_t sizeConsideredEmpty)
remove spurious log file left on sd
void chvsnprintf(char *buffer, size_t size, const char *fmt, va_list ap)
int32_t msgqueue_pop(MsgQueue *que, void **msgPtr)
wait then receive message
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
struct _SdLogBuffer SdLogBuffer
void * tlsf_malloc_r(tlsf_memory_heap_t *heap, size_t bytes)
static THD_WORKING_AREA(wa_thd_spi1, 1024)