/** * DEPRECATED FUCNTIONS * To read the tag string from device you shoul use the keytag_recv * fuction, that return the string if we had received it. */ void keytag_poll(struct TagPacket *pkt) { #warning __FILTER_NEXT_WARNING__ #warning keytag_poll function is depreca use keytag_recv instead uint8_t buf[CONFIG_TAG_MAX_LEN]; int len; if ((len = keytag_recv(pkt, buf, sizeof(buf))) != EOF) kfile_write(pkt->host, buf, len); }
/** * Generic putc() implementation using \a fd->write. */ int kfile_putc(int _c, struct KFile *fd) { unsigned char c = (unsigned char)_c; if (kfile_write(fd, &c, sizeof(c)) == sizeof(c)) return (int)((unsigned char)_c); else return EOF; }
/** * KFile read/write subtest. * Try to write/read in the same \a f file location \a size bytes. * \return true if all is ok, false otherwise * \note Restore file position at exit (if no error) * \note Test buffer \a buf must be filled with * the following statement: * \code * buf[i] = i & 0xff * \endcode */ static bool kfile_rwTest(KFile *f, uint8_t *buf, size_t size) { /* * Write test buffer */ if (kfile_write(f, buf, size) != size) { LOG_ERR("error writing buf"); return false; } kfile_seek(f, -(kfile_off_t)size, KSM_SEEK_CUR); /* * Reset test buffer */ memset(buf, 0, size); /* * Read file in test buffer */ if (kfile_read(f, buf, size) != size) { LOG_ERR("error reading buf"); return false; } kfile_seek(f, -(kfile_off_t)size, KSM_SEEK_CUR); /* * Check test result */ for (size_t i = 0; i < size; i++) { if (buf[i] != (i & 0xff)) { LOG_ERR("error comparing at index [%d] read [%02x] expected [%02x]\n", i, buf[i], i); return false; } } return true; }
kfile_off_t kfile_copy(KFile *src, KFile *dst, kfile_off_t size) { char buf[32]; kfile_off_t cp_len = 0; while (size) { size_t len = MIN(sizeof(buf), (size_t)size); if (kfile_read(src, buf, len) != len) break; size_t wr_len = kfile_write(dst, buf, len); cp_len += wr_len; size -= len; if (wr_len != len) break; } return cp_len; }
static int8_t _gsmWriteLine(const char *cmd) { size_t i; // NOTE: debugging should no be mixed to modem command and response to // avoid timeing issues and discrepancy between debug and release // versions gsmDebug("TX [%s]\n", cmd); // Purge any buffered data before sending a new command ser_purge(gsm); // Clear error flags ser_setstatus(gsm, 0); // Sending the AT command WATCHDOG_RESET(); for (i=0; cmd[i]!='\0'; i++) { kfile_putc(cmd[i], &(gsm->fd)); WATCHDOG_RESET(); } kfile_write(&(gsm->fd), "\r\n", 2); return i; }
/** * Write \a _buf in serial flash memory * * Before to write data into flash we must enable * memory writing. To do this we send a WRE command opcode. * After this command the flash is ready to be write, and so * we send a PROGRAM opcode followed to 3 byte of * address memory, at the end of last address byte * we can send the data. * When we finish to send all data, we disable cs * and flash write received data bytes on its memory. * * \note: WARNING: you could write only on erased memory section! * Each write time you could write max a memory page size, * because if you write more than memory page size the * address roll over to first byte of page. * * \return the number of bytes write. */ static size_t flash25_write(struct KFile *_fd, const void *_buf, size_t size) { flash25Offset_t offset; flash25Size_t total_write = 0; flash25Size_t wr_len; const uint8_t *data = (const uint8_t *) _buf; Flash25 *fd = FLASH25_CAST(_fd); ASSERT(fd->fd.seek_pos + (kfile_off_t)size <= fd->fd.size); size = MIN((kfile_off_t)size, fd->fd.size - fd->fd.seek_pos); while (size) { offset = fd->fd.seek_pos % (flash25Size_t)FLASH25_PAGE_SIZE; wr_len = MIN((flash25Size_t)size, FLASH25_PAGE_SIZE - (flash25Size_t)offset); kprintf("[seek_pos-<%lu>, offset-<%d>]\n", fd->fd.seek_pos, offset); /* * We check serial flash memory state, and wait until ready-flag * is high. */ flash25_waitReady(fd); /* * Start write cycle. * We could write only data not more long than one * page size. * * To write on serial flash memory we must first * enable write with a WREN opcode command, before * the PROGRAM opcode. * * \note: the same byte cannot be reprogrammed without * erasing the whole sector first. */ flash25_sendCmd(fd, FLASH25_WREN); SS_ACTIVE(); kfile_putc(FLASH25_PROGRAM, fd->channel); /* * Address that we want to write. */ kfile_putc((fd->fd.seek_pos >> 16) & 0xFF, fd->channel); kfile_putc((fd->fd.seek_pos >> 8) & 0xFF, fd->channel); kfile_putc(fd->fd.seek_pos & 0xFF, fd->channel); kfile_write(fd->channel, data, wr_len); SS_INACTIVE(); data += wr_len; fd->fd.seek_pos += wr_len; size -= wr_len; total_write += wr_len; } kprintf("written %lu bytes\n", total_write); return total_write; }
/** * \brief Receive a file using the XModem protocol. * * \param ch Channel to use for transfer * \param fd Destination file * * \note This function allocates a large amount of stack (\see XM_BUFSIZE). */ bool xmodem_recv(KFile *ch, KFile *fd) { char block_buffer[XM_BUFSIZE]; /* Buffer to hold a block of data */ int c, i, blocksize; int blocknr = 0, last_block_done = 0, retries = 0; char *buf; uint8_t checksum; uint16_t crc; bool purge = false; bool usecrc = true; LOG_INFO("Starting Transfer...\n"); purge = true; kfile_clearerr(ch); /* Send initial NAK to start transmission */ for(;;) { if (XMODEM_CHECK_ABORT) { kfile_putc(XM_CAN, ch); kfile_putc(XM_CAN, ch); LOG_INFO("Transfer aborted\n"); return false; } /* * Discard incoming input until a timeout occurs, then send * a NAK to the transmitter. */ if (purge) { purge = false; if (kfile_error(ch)) { LOG_ERR("Retries %d\n", retries); } kfile_resync(ch, 200); retries++; if (retries >= CONFIG_XMODEM_MAXRETRIES) { kfile_putc(XM_CAN, ch); kfile_putc(XM_CAN, ch); LOG_INFO("Transfer aborted\n"); return false; } /* Transmission start? */ if (blocknr == 0) { if (retries < CONFIG_XMODEM_MAXCRCRETRIES) { LOG_INFO("Request Tx (CRC)\n"); kfile_putc(XM_C, ch); } else { /* Give up with CRC and fall back to checksum */ usecrc = false; LOG_INFO("Request Tx (BCC)\n"); kfile_putc(XM_NAK, ch); } } else kfile_putc(XM_NAK, ch); } switch (kfile_getc(ch)) { #if XM_BUFSIZE >= 1024 case XM_STX: /* Start of header (1024-byte block) */ blocksize = 1024; goto getblock; #endif case XM_SOH: /* Start of header (128-byte block) */ blocksize = 128; /* Needed to avoid warning if XM_BUFSIZE < 1024 */ getblock: /* Get block number */ c = kfile_getc(ch); /* Check complemented block number */ if ((~c & 0xff) != kfile_getc(ch)) { LOG_WARN("Bad blk (%d)\n", c); purge = true; break; } /* Determine which block is being sent */ if (c == (blocknr & 0xff)) { /* Last block repeated */ LOG_INFO("Repeat blk %d\n", blocknr); } else if (c == ((blocknr + 1) & 0xff)) { /* Next block */ LOG_INFO("Recv blk %d\n", ++blocknr); } else { /* Sync lost */ LOG_WARN("Sync lost (%d/%d)\n", c, blocknr); purge = true; break; } buf = block_buffer; /* Reset pointer to start of buffer */ checksum = 0; crc = 0; for (i = 0; i < blocksize; i++) { if ((c = kfile_getc(ch)) == EOF) { purge = true; break; } /* Store in buffer */ *buf++ = (char)c; /* Calculate block checksum or CRC */ if (usecrc) crc = UPDCRC16(c, crc); else checksum += (char)c; } if (purge) break; /* Get the checksum byte or the CRC-16 MSB */ if ((c = kfile_getc(ch)) == EOF) { purge = true; break; } if (usecrc) { crc = UPDCRC16(c, crc); /* Get CRC-16 LSB */ if ((c = kfile_getc(ch)) == EOF) { purge = true; break; } crc = UPDCRC16(c, crc); if (crc) { LOG_ERR("Bad CRC: %04x\n", crc); purge = true; break; } } /* Compare the checksum */ else if (c != checksum) { LOG_ERR("Bad sum: %04x/%04x\n", checksum, c); purge = true; break; } /* * Avoid flushing the same block twice. * This could happen when the sender does not receive our * acknowledge and resends the same block. */ if (last_block_done < blocknr) { /* Call user function to flush the buffer */ if (kfile_write(fd, block_buffer, blocksize)) { /* Acknowledge block and clear error counter */ kfile_putc(XM_ACK, ch); retries = 0; last_block_done = blocknr; } else { /* User callback failed: abort transfer immediately */ retries = CONFIG_XMODEM_MAXRETRIES; purge = true; } } break; case XM_EOT: /* End of transmission */ kfile_putc(XM_ACK, ch); LOG_INFO("Transfer completed\n"); return true; case EOF: /* Timeout or serial error */ purge = true; break; default: LOG_INFO("Skipping garbage\n"); purge = true; break; } } /* End forever */ }
/* * ext3301: modified to encrypt/decrypt files moving to/from * an encrypted folder. * Rename still succeeds if an encrypt/decrypt operation fails. */ static int ext2_rename (struct inode * old_dir, struct dentry * old_dentry, struct inode * new_dir, struct dentry * new_dentry ) { struct inode * old_inode = old_dentry->d_inode; struct inode * new_inode = new_dentry->d_inode; struct page * dir_page = NULL; struct ext2_dir_entry_2 * dir_de = NULL; struct page * old_page; struct ext2_dir_entry_2 * old_de; int err = -ENOENT; bool is_encryptable, src_encrypt, dest_encrypt; int i; struct file * fcrypt; ssize_t nchunk, nread, nwritten; loff_t fpos, fseekpos; unsigned int fsize, fremaining; char * buf, * strbuf1, * strbuf2, * path_src, * path_dest; size_t blocksize = INODE_BLKSIZE(old_inode); // dquot_initialize(old_dir); dquot_initialize(new_dir); old_de = ext2_find_entry (old_dir, &old_dentry->d_name, &old_page); if (!old_de) goto out; if (S_ISDIR(old_inode->i_mode)) { err = -EIO; dir_de = ext2_dotdot(old_inode, &dir_page); if (!dir_de) goto out_old; } if (new_inode) { struct page *new_page; struct ext2_dir_entry_2 *new_de; err = -ENOTEMPTY; if (dir_de && !ext2_empty_dir (new_inode)) goto out_dir; err = -ENOENT; new_de = ext2_find_entry (new_dir, &new_dentry->d_name, &new_page); if (!new_de) goto out_dir; ext2_set_link(new_dir, new_de, new_page, old_inode, 1); new_inode->i_ctime = CURRENT_TIME_SEC; if (dir_de) drop_nlink(new_inode); inode_dec_link_count(new_inode); } else { err = ext2_add_link(new_dentry, old_inode); if (err) goto out_dir; if (dir_de) inode_inc_link_count(new_dir); } // allocate buffers strbuf1 = kmalloc((size_t)512, GFP_KERNEL); strbuf2 = kmalloc((size_t)512, GFP_KERNEL); buf = kmalloc(blocksize, GFP_KERNEL); if (!buf || !strbuf1 || !strbuf2) return -ENOMEM; // check if the source XOR destination lie under /encrypt, // and both entries are regular or immediate files is_encryptable = (I_ISIM(old_inode) || I_ISREG(old_inode)); src_encrypt = ext3301_isencrypted(old_dentry); dest_encrypt = ext3301_isencrypted(new_dentry); path_src = ext3301_getpath(old_dentry, strbuf1, blocksize); path_dest = ext3301_getpath(new_dentry, strbuf2, blocksize); // decide whether to encrypt dbg(KERN_DEBUG "rename (%s --> %s)\n", path_src, path_dest); if (is_encryptable) { dbg_cr(KERN_DEBUG "- File encryptable type (regular/immediate)\n"); if (src_encrypt && dest_encrypt) { dbg_cr(KERN_DEBUG "- File moving inside /encrypt (no change))\n"); } else if (src_encrypt) { dbg_cr(KERN_DEBUG "- File moving out of /encrypt. Decrypting..\n"); goto cryptstart; } else if (dest_encrypt) { dbg_cr(KERN_DEBUG "- File moving into /encrypt. Encrypting..\n"); goto cryptstart; } else { dbg_cr(KERN_DEBUG "- Src/dest directories not encryptable\n"); } } else { dbg_cr(KERN_DEBUG "- File not an encryptable type\n"); } goto cryptdone; /* encrypt/decrypt file */ cryptstart: // open file if (!path_src) goto cryptfail; fcrypt = kfile_open(path_src, O_RDWR); if (!fcrypt) goto cryptfail; fsize = FILP_FSIZE(fcrypt); fremaining = fsize; fpos = 0; fseekpos = 0; dbg_cr(KERN_DEBUG " - Opened %s (Fsize: %d)\n", FILP_NAME(fcrypt), fsize); // special case: nothing to encrypt if (fsize==0) goto cryptclose; // loop: read, encrypt, write while (fremaining > 0) { // choose a chunk size nchunk = (fremaining > blocksize ? blocksize : (ssize_t)fremaining); dbg_cr(KERN_DEBUG " - Starting a %d-byte chunk at pos %u.\n", (int)nchunk, (unsigned int)fpos); // read a chunk; make sure we read all bytes requested. fpos = fseekpos; nread = kfile_read(fcrypt, buf, (size_t)nchunk, &fpos); // this inequality covers error conditions (nread<0) and // partial reads (0<=nread<=nchunk && nread != nchunk) if (nread != nchunk) { kfile_close(fcrypt); goto cryptfail; } // encrypt the buffer for (i=0; i<nchunk; i++) buf[i] ^= crypter_key; // write the chunk back fpos = fseekpos; nwritten = kfile_write(fcrypt, buf, (size_t)nchunk, &fpos); if (nwritten != nchunk) { kfile_close(fcrypt); goto cryptfail; } // move the file marker forward, decrease the #bytes remaining fseekpos += nchunk; fremaining -= nchunk; } // sync the read/write operations to disk. Very important! kfile_sync(fcrypt); cryptclose: kfile_close(fcrypt); goto cryptdone; cryptfail: // encrypt/decrypt failed if (dest_encrypt) printk(KERN_WARNING "Crypting file entering /%s failed: ino %lu\n", crypter_dir, INODE_INO(old_inode)); else if (src_encrypt) printk(KERN_WARNING "Decrypting file leaving /%s failed: ino %lu\n", crypter_dir, INODE_INO(old_inode)); goto cryptdone; cryptdone: /* * Like most other Unix systems, set the ctime for inodes on a * rename. */ old_inode->i_ctime = CURRENT_TIME_SEC; mark_inode_dirty(old_inode); ext2_delete_entry (old_de, old_page); if (dir_de) { if (old_dir != new_dir) ext2_set_link(old_inode, dir_de, dir_page, new_dir, 0); else { kunmap(dir_page); page_cache_release(dir_page); } inode_dec_link_count(old_dir); } goto out_free; out_dir: if (dir_de) { kunmap(dir_page); page_cache_release(dir_page); } out_old: kunmap(old_page); page_cache_release(old_page); out: return err; out_free: // free buffers kfree(strbuf1); kfree(strbuf2); kfree(buf); return 0; }
/** * KFile read/write test. * This function write and read \a test_buf long \a size * on \a fd handler. * \a save_buf can be NULL or a buffer where to save previous file content. */ int kfile_testRunGeneric(KFile *fd, uint8_t *test_buf, uint8_t *save_buf, size_t size) { /* * Part of test buf size that you would write. * This var is used in test 3 to check kfile_write * when writing beyond filesize limit. */ kfile_off_t len = size / 2; /* Fill test buffer */ for (size_t i = 0; i < size; i++) test_buf[i] = (i & 0xff); /* * If necessary, user can save content, * for later restore. */ if (save_buf) { LOG_INFO("Saved content..form [%ld] to [%ld]\n", fd->seek_pos, fd->seek_pos + size); kfile_read(fd, save_buf, size); } /* TEST 1 BEGIN. */ LOG_INFO("Test 1: write from pos 0 to [%ld]\n", (long)size); /* * Seek to addr 0. */ if (kfile_seek(fd, 0, KSM_SEEK_SET) != 0) goto kfile_test_end; /* * Test read/write to address 0..size */ if (!kfile_rwTest(fd, test_buf, size)) goto kfile_test_end; LOG_INFO("Test 1: ok!\n"); /* * Restore previous read content. */ if (save_buf) { kfile_seek(fd, 0, KSM_SEEK_SET); if (kfile_write(fd, save_buf, size) != size) goto kfile_test_end; LOG_INFO("Restore content..form [%ld] to [%ld]\n", fd->seek_pos, fd->seek_pos + size); } /* TEST 1 END. */ /* TEST 2 BEGIN. */ LOG_INFO("Test 2: write from pos [%ld] to [%ld]\n", fd->size/2 , fd->size/2 + size); /* * Go to half test size. */ kfile_seek(fd, (fd->size / 2), KSM_SEEK_SET); /* * If necessary, user can save content * for later restore. */ if (save_buf) { kfile_read(fd, save_buf, size); kfile_seek(fd, -(kfile_off_t)size, KSM_SEEK_CUR); LOG_INFO("Saved content..form [%ld] to [%ld]\n", fd->seek_pos, fd->seek_pos + size); } /* * Test read/write to address filesize/2 ... filesize/2 + size */ if (!kfile_rwTest(fd, test_buf, size)) goto kfile_test_end; LOG_INFO("Test 2: ok!\n"); /* * Restore previous content. */ if (save_buf) { kfile_seek(fd, -(kfile_off_t)size, KSM_SEEK_CUR); if (kfile_write(fd, save_buf, size) != size) goto kfile_test_end; LOG_INFO("Restore content..form [%ld] to [%ld]\n", fd->seek_pos, fd->seek_pos + size); } /* TEST 2 END. */ /* TEST 3 BEGIN. */ LOG_INFO("Test 3: write outside of fd->size limit [%ld]\n", fd->size); /* * Go to the Flash end */ kfile_seek(fd, -len, KSM_SEEK_END); /* * If necessary, user can save content, * for later restore. */ if (save_buf) { kfile_read(fd, save_buf, len); kfile_seek(fd, -len, KSM_SEEK_CUR); LOG_INFO("Saved content..form [%ld] to [%ld]\n", fd->seek_pos, fd->seek_pos + len); } /* * Test read/write to address (filesize - size) ... filesize */ if (kfile_rwTest(fd, test_buf, size)) goto kfile_test_end; kprintf("Test 3: ok!\n"); /* * Restore previous read content */ if (save_buf) { kfile_seek(fd, -len, KSM_SEEK_END); if ((kfile_off_t)kfile_write(fd, save_buf, len) != len) goto kfile_test_end; LOG_INFO("Restore content..form [%ld] to [%ld]\n", fd->seek_pos, fd->seek_pos + len); } /* TEST 3 END. */ kfile_close(fd); return 0; kfile_test_end: kfile_close(fd); LOG_ERR("One kfile_test failed!\n"); return EOF; }
int main(void) { // SD fat filesystem context FATFS fs; // Context files that we would manage FatFile ini_file; FatFile log_file; init(); LOG_INFO("SD fat example project %s: %d times\n", VERS_HOST, VERS_BUILD); while (1) { bool sd_ok = true; FRESULT result; // Setting info INISetting ini_set; memset(&ini_set, 0, sizeof(ini_set)); sd_ok = sd_init(&spi_dma.fd); if (sd_ok) { LOG_INFO("Mount FAT filesystem.\n"); if ((result = f_mount(0, &fs)) != FR_OK) { LOG_ERR("Mounting FAT volumes error[%d]\n", result); sd_ok = false; } LOG_INFO("Read setting from ini file: %s\n", INI_FILE_NAME); if (sd_ok && ((result = fatfile_open(&ini_file, INI_FILE_NAME, FA_READ)) != FR_OK)) { LOG_ERR("Could not open ini file: %s error[%d,]\n", INI_FILE_NAME, result); sd_ok = false; } if (sd_ok) { /* * If sd is ok, we read all setting from INI file. * NOTE: if one ini key or section was not found into INI * file, the iniparser return the defaul value. */ ini_getString(&ini_file.fd, LOG_SECTION, LOG_NAME_KEY, "default.log", ini_set.name, sizeof(ini_set.name)); LOG_INFO("Log file name [%s]\n", ini_set.name); char tmp[25]; ini_getString(&ini_file.fd, LOG_SECTION, LOG_ON_SERIAL, "1", tmp, sizeof(tmp)); ini_set.log_on_serial = atoi(tmp); LOG_INFO("Log serial [%d]\n", ini_set.log_on_serial); ini_getString(&ini_file.fd, LOG_SECTION, LOG_ON_FILE, "1", tmp, sizeof(tmp)); ini_set.log_on_file = atoi(tmp); LOG_INFO("Log sd [%d]\n", ini_set.log_on_file); ini_getString(&ini_file.fd, LOG_SECTION, LOG_SAMPLE_TIME, "500", tmp, sizeof(tmp)); ini_set.sample_time = atoi(tmp); LOG_INFO("Sample time [%ld]\n", ini_set.sample_time); ini_getString(&ini_file.fd, SERIAL_LOG, SERIAL_PORT, "0", tmp, sizeof(tmp)); ini_set.port = atoi(tmp); LOG_INFO("Serial port [%d]\n", ini_set.port); ini_getString(&ini_file.fd, SERIAL_LOG, SERIAL_BAUD, "115200", tmp, sizeof(tmp)); ini_set.baud = atoi(tmp); LOG_INFO("Serial buad [%d]\n", ini_set.baud); ini_getString(&ini_file.fd, LOG_FORMAT_SEC, FORMAT_LINE_HEADER, "BeRTOS: ", ini_set.line_header, sizeof(ini_set.line_header)); LOG_INFO("Serial line header[%s]\n", ini_set.line_header); ini_getString(&ini_file.fd, LOG_FORMAT_SEC, FORMAT_FIELD_SEP, ",", ini_set.field_sep, sizeof(ini_set.field_sep)); LOG_INFO("Serial char sep[%s]\n", ini_set.field_sep); ini_getString(&ini_file.fd, TEMPERATURE, TEMP_UNIT_LABEL, "C", ini_set.temp_unit_label, sizeof(ini_set.temp_unit_label)); LOG_INFO("Temp unit label[%s]\n", ini_set.temp_unit_label); ini_getString(&ini_file.fd, PRESSURE, PRESS_UNIT_LABEL, "hPa", ini_set.press_unit_label, sizeof(ini_set.press_unit_label)); LOG_INFO("Press unit label[%s]\n", ini_set.press_unit_label); ini_getString(&ini_file.fd, VOLTAGE, VOLTAGE_UNIT_LABEL, "V", ini_set.voltage_unit_label, sizeof(ini_set.voltage_unit_label)); LOG_INFO("Press unit label[%s]\n", ini_set.voltage_unit_label); } } if (ini_set.log_on_serial) { // Init serial log port ser_init(&log_port, ini_set.port); ser_setbaudrate(&log_port, ini_set.baud); LOG_INFO("SERIAL init..port[%d] buad[%d]\n", ini_set.port, ini_set.baud); } char log_string[160]; memset(log_string, 0, sizeof(log_string)); // Supplay voltage uint16_t vdd = ADC_RANGECONV(adc_read(SUPPLAY_VOLTAGE_CH), 0, ADC_SUPPLAY_VOLTAGE); // Read temperature int16_t tmp = tmp123_read(&temp_sensor_bus.fd); // Read pressure uint16_t vout = ADC_RANGECONV(adc_read(PRESSURE_SENSOR_CH), 0, vdd); int16_t press = mpxx6115a_press(vout, vdd); /* * Format string whit read data * line_header + temp + temp_unit_label + field_sep + press + press_unit_label + field_sep + vdd + voltage_unit_label */ int wr_len = sprintf(log_string, "%s%d.%01d%s%s%d%s%s%d.%d%s\r\n", ini_set.line_header, tmp / 10, ABS(tmp % 10), ini_set.temp_unit_label, ini_set.field_sep, press, ini_set.press_unit_label, ini_set.field_sep, vdd / 1000, ABS(vdd % 1000), ini_set.voltage_unit_label); /* * if SD is ok, try to open log file and write our data and, only * if by configuration we have enable the log on file */ if (sd_ok && ini_set.log_on_file) { // Open log file and do not overwrite the previous log file result = fatfile_open(&log_file, ini_set.name, FA_OPEN_EXISTING | FA_WRITE); // If the log file there isn't we create the new one if (result == FR_NO_FILE) { result = fatfile_open(&log_file, ini_set.name, FA_CREATE_NEW | FA_WRITE); LOG_INFO("Create the log file: %s\n", ini_set.name); } if ( result == FR_OK) { LOG_INFO("Opened log file '%s' size %ld\n", ini_set.name, log_file.fat_file.fsize); // To append data we should go to end of file before to start to write kfile_seek(&log_file.fd, 0, KSM_SEEK_END); int len = kfile_write(&log_file.fd, log_string, wr_len); // Flush data and close the files. kfile_flush(&log_file.fd); kfile_close(&log_file.fd); // Unmount always to prevent accidental sd remove. f_mount(0, NULL); LOG_INFO("Wrote [%d]\n", len); } else { LOG_ERR("Unable to open file: '%s' error[%d]\n", ini_set.name, result); } } // If by configuration we have enable the log on serial, we log it if (ini_set.log_on_serial) kfile_write(&log_port.fd, log_string, wr_len); timer_delay(ini_set.sample_time); } return 0; }