You've already forked uiflow-micropython
mirror of
https://github.com/m5stack/uiflow-micropython.git
synced 2026-05-20 10:39:27 -07:00
a7a022b99c
Signed-off-by: lbuque <lbuque@163.com>
495 lines
17 KiB
C
495 lines
17 KiB
C
/*
|
|
* ESPRESSIF MIT License
|
|
*
|
|
* Copyright (c) 2019 <ESPRESSIF SYSTEMS (SHANGHAI) CO., LTD>
|
|
* Copyright (c) 2024 M5Stack Technology CO LTD
|
|
*
|
|
* Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case,
|
|
* it is free of charge, to any person obtaining a copy of this software and associated
|
|
* documentation files (the "Software"), to deal in the Software without restriction, including
|
|
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
|
* to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in all copies or
|
|
* substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
*
|
|
*/
|
|
|
|
#include "errno.h"
|
|
#include <string.h>
|
|
|
|
#include "audio_element.h"
|
|
#include "audio_error.h"
|
|
#include "audio_mem.h"
|
|
|
|
#include "esp_log.h"
|
|
#include "vfs_stream.h"
|
|
#include "wav_head.h"
|
|
|
|
#include <extmod/vfs.h>
|
|
#include <extmod/vfs_lfs.h>
|
|
#include "extmod/vfs_fat.h"
|
|
#include "lib/littlefs/lfs2.h"
|
|
#include "lib/oofatfs/ff.h"
|
|
#include "py/builtin.h"
|
|
#include "py/objstr.h"
|
|
#include "py/runtime.h"
|
|
#include "py/stream.h"
|
|
|
|
#define FILE_WAV_SUFFIX_TYPE "wav"
|
|
#define FILE_OPUS_SUFFIX_TYPE "opus"
|
|
#define FILE_AMR_SUFFIX_TYPE "amr"
|
|
#define FILE_AMRWB_SUFFIX_TYPE "Wamr"
|
|
|
|
static const char *TAG = "VFS_STREAM";
|
|
|
|
typedef enum {
|
|
STREAM_TYPE_UNKNOW,
|
|
STREAM_TYPE_WAV,
|
|
STREAM_TYPE_OPUS,
|
|
STREAM_TYPE_AMR,
|
|
STREAM_TYPE_AMRWB,
|
|
} wr_stream_type_t;
|
|
|
|
// micropython/extmod/vfs_lfs.c line: 115
|
|
typedef struct _mp_obj_vfs_lfs2_t {
|
|
mp_obj_base_t base;
|
|
mp_vfs_blockdev_t blockdev;
|
|
bool enable_mtime;
|
|
vstr_t cur_dir;
|
|
struct lfs2_config config;
|
|
lfs2_t lfs;
|
|
} mp_obj_vfs_lfs2_t;
|
|
|
|
typedef struct vfs_stream {
|
|
audio_stream_type_t type;
|
|
int block_size;
|
|
bool is_open;
|
|
wr_stream_type_t w_type;
|
|
FATFS *fatfs;
|
|
lfs2_t *lfs2;
|
|
union {
|
|
FIL fat_file;
|
|
lfs2_file_t *lfs2_file;
|
|
} file;
|
|
} vfs_stream_t;
|
|
|
|
static struct lfs2_file_config lfs2_file_conf = { 0 };
|
|
|
|
static esp_err_t _vfs_open(audio_element_handle_t self);
|
|
static esp_err_t _vfs_close(audio_element_handle_t self);
|
|
static int _vfs_process(audio_element_handle_t self, char *in_buffer, int in_len);
|
|
static esp_err_t _vfs_destroy(audio_element_handle_t self);
|
|
static int _vfs_write(audio_element_handle_t self, char *buffer, int len, TickType_t ticks_to_wait, void *context);
|
|
static int _vfs_read(audio_element_handle_t self, char *buffer, int len, TickType_t ticks_to_wait, void *context);
|
|
|
|
static wr_stream_type_t get_type(const char *str);
|
|
|
|
|
|
audio_element_handle_t vfs_stream_init(vfs_stream_cfg_t *config) {
|
|
audio_element_handle_t el;
|
|
vfs_stream_t *vfs = audio_calloc(1, sizeof(vfs_stream_t));
|
|
|
|
AUDIO_MEM_CHECK(TAG, vfs, return NULL);
|
|
|
|
// esp_vfs_littlefs_conf_t conf = {
|
|
// .base_path = "/flash",
|
|
// .partition_label = "vfs",
|
|
// .format_if_mount_failed = false,
|
|
// .dont_mount = true,
|
|
// };
|
|
|
|
// esp_vfs_littlefs_register(&conf);
|
|
|
|
const char *path_out;
|
|
mp_vfs_mount_t *existing_mount = mp_vfs_lookup_path("/flash", &path_out);
|
|
if (existing_mount == MP_VFS_NONE || existing_mount == MP_VFS_ROOT) {
|
|
ESP_LOGE(TAG, "No vfs mount");
|
|
goto _vfs_init_exit;
|
|
}
|
|
// fs_user_mount_t *user_mount = MP_OBJ_TO_PTR(existing_mount->obj);
|
|
// if (user_mount == NULL) {
|
|
// ESP_LOGE(TAG, "No user mount");
|
|
// goto _vfs_init_exit;
|
|
// }
|
|
vfs->lfs2 = &((mp_obj_vfs_lfs2_t *)MP_OBJ_TO_PTR(existing_mount->obj))->lfs;
|
|
|
|
audio_element_cfg_t cfg = DEFAULT_AUDIO_ELEMENT_CONFIG();
|
|
cfg.open = _vfs_open;
|
|
cfg.close = _vfs_close;
|
|
cfg.process = _vfs_process;
|
|
cfg.destroy = _vfs_destroy;
|
|
cfg.task_stack = config->task_stack;
|
|
cfg.task_prio = config->task_prio;
|
|
cfg.task_core = config->task_core;
|
|
cfg.out_rb_size = config->out_rb_size;
|
|
cfg.buffer_len = config->buf_sz;
|
|
if (cfg.buffer_len == 0) {
|
|
cfg.buffer_len = VFS_STREAM_BUF_SIZE;
|
|
}
|
|
|
|
cfg.tag = "file";
|
|
vfs->type = config->type;
|
|
|
|
if (config->type == AUDIO_STREAM_WRITER) {
|
|
cfg.write = _vfs_write;
|
|
} else {
|
|
cfg.read = _vfs_read;
|
|
}
|
|
el = audio_element_init(&cfg);
|
|
|
|
AUDIO_MEM_CHECK(TAG, el, goto _vfs_init_exit);
|
|
audio_element_setdata(el, vfs);
|
|
return el;
|
|
_vfs_init_exit:
|
|
audio_free(vfs);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static wr_stream_type_t get_type(const char *str) {
|
|
char *relt = strrchr(str, '.');
|
|
if (relt != NULL) {
|
|
relt++;
|
|
ESP_LOGD(TAG, "result = %s", relt);
|
|
if (strncasecmp(relt, FILE_WAV_SUFFIX_TYPE, 3) == 0) {
|
|
return STREAM_TYPE_WAV;
|
|
} else if (strncasecmp(relt, FILE_OPUS_SUFFIX_TYPE, 4) == 0) {
|
|
return STREAM_TYPE_OPUS;
|
|
} else if (strncasecmp(relt, FILE_AMR_SUFFIX_TYPE, 3) == 0) {
|
|
return STREAM_TYPE_AMR;
|
|
} else if (strncasecmp(relt, FILE_AMRWB_SUFFIX_TYPE, 4) == 0) {
|
|
return STREAM_TYPE_AMRWB;
|
|
} else {
|
|
return STREAM_TYPE_UNKNOW;
|
|
}
|
|
} else {
|
|
return STREAM_TYPE_UNKNOW;
|
|
}
|
|
}
|
|
|
|
|
|
static esp_err_t _vfs_open(audio_element_handle_t self) {
|
|
vfs_stream_t *vfs = (vfs_stream_t *)audio_element_getdata(self);
|
|
|
|
char *uri = audio_element_get_uri(self);
|
|
if (uri == NULL) {
|
|
ESP_LOGE(TAG, "Error, uri is not set");
|
|
return ESP_FAIL;
|
|
}
|
|
ESP_LOGI(TAG, "_vfs_open, uri:%s", uri);
|
|
char *path = strstr(uri, "file:/");
|
|
if (path == NULL) {
|
|
ESP_LOGE(TAG, "Error, need file path to open");
|
|
return ESP_FAIL;
|
|
}
|
|
if (vfs->is_open) {
|
|
ESP_LOGE(TAG, "already opened");
|
|
return ESP_FAIL;
|
|
}
|
|
path += strlen("file:/");
|
|
|
|
const char *path_out;
|
|
mp_vfs_mount_t *existing_mount = mp_vfs_lookup_path(path, &path_out);
|
|
if (existing_mount == MP_VFS_NONE || existing_mount == MP_VFS_ROOT) {
|
|
ESP_LOGE(TAG, "No vfs mount");
|
|
return ESP_FAIL;
|
|
}
|
|
// fs_user_mount_t *user_mount = MP_OBJ_TO_PTR(existing_mount->obj);
|
|
// if (user_mount == NULL) {
|
|
// ESP_LOGE(TAG, "No user mount");
|
|
// goto _vfs_init_exit;
|
|
// }
|
|
if (strstr(uri, "flash")) {
|
|
ESP_LOGD(TAG, "in flash");
|
|
vfs->lfs2 = &((mp_obj_vfs_lfs2_t *)MP_OBJ_TO_PTR(existing_mount->obj))->lfs;
|
|
} else if (strstr(uri, "sd")) {
|
|
ESP_LOGD(TAG, "in sd");
|
|
vfs->fatfs = &((fs_user_mount_t *)MP_OBJ_TO_PTR(existing_mount->obj))->fatfs;
|
|
}
|
|
|
|
ESP_LOGI(TAG, "_vfs_open, path:%s", path_out);
|
|
|
|
audio_element_info_t info;
|
|
audio_element_getinfo(self, &info);
|
|
|
|
if (vfs->lfs2) {
|
|
if (vfs->type == AUDIO_STREAM_READER) {
|
|
if (lfs2_file_conf.buffer == NULL) {
|
|
lfs2_file_conf.buffer = malloc(vfs->lfs2->cfg->cache_size * sizeof(uint8_t));
|
|
}
|
|
vfs->file.lfs2_file = (lfs2_file_t *)malloc(1 * sizeof(lfs2_file_t));
|
|
int res = lfs2_file_opencfg(vfs->lfs2, vfs->file.lfs2_file, path_out, LFS2_O_RDONLY, &lfs2_file_conf);
|
|
|
|
if (res == LFS2_ERR_OK) {
|
|
struct lfs2_info fno;
|
|
lfs2_stat(vfs->lfs2, path_out, &fno);
|
|
info.total_bytes = fno.size;
|
|
ESP_LOGI(TAG, "File size: %d byte, file position: %d", (int)fno.size, (int)info.byte_pos);
|
|
if (info.byte_pos > 0) {
|
|
if (lfs2_file_seek(vfs->lfs2, vfs->file.lfs2_file, info.byte_pos, LFS2_SEEK_SET) < 0) {
|
|
ESP_LOGE(TAG, "Error seek file. Error message: %s, line: %d", strerror(errno), __LINE__);
|
|
return ESP_FAIL;
|
|
}
|
|
}
|
|
} else {
|
|
ESP_LOGE(TAG, "failed to open %s(%d)", path_out, res);
|
|
return ESP_FAIL;
|
|
}
|
|
} else if (vfs->type == AUDIO_STREAM_WRITER) {
|
|
if (lfs2_file_conf.buffer == NULL) {
|
|
lfs2_file_conf.buffer = malloc(vfs->lfs2->cfg->cache_size * sizeof(uint8_t));
|
|
}
|
|
vfs->file.lfs2_file = (lfs2_file_t *)malloc(1 * sizeof(lfs2_file_t));
|
|
int res = lfs2_file_opencfg(vfs->lfs2, vfs->file.lfs2_file, path_out, LFS2_O_RDWR | LFS2_O_CREAT | LFS2_O_TRUNC, &lfs2_file_conf);
|
|
|
|
if (res == LFS2_ERR_OK) {
|
|
vfs->w_type = get_type(path_out);
|
|
UINT bw = 0;
|
|
if ((STREAM_TYPE_WAV == vfs->w_type)) {
|
|
wav_header_t info = {0};
|
|
lfs2_file_write(vfs->lfs2, vfs->file.lfs2_file, &info, sizeof(wav_header_t));
|
|
lfs2_file_sync(vfs->lfs2, vfs->file.lfs2_file);
|
|
} else if ((STREAM_TYPE_AMR == vfs->w_type)) {
|
|
lfs2_file_write(vfs->lfs2, vfs->file.lfs2_file, "#!AMR\n", 6);
|
|
lfs2_file_sync(vfs->lfs2, vfs->file.lfs2_file);
|
|
} else if ((STREAM_TYPE_AMRWB == vfs->w_type)) {
|
|
lfs2_file_write(vfs->lfs2, vfs->file.lfs2_file, "#!AMR-WB\n", 9);
|
|
lfs2_file_sync(vfs->lfs2, vfs->file.lfs2_file);
|
|
}
|
|
} else {
|
|
ESP_LOGE(TAG, "failed to open %s", path_out);
|
|
return ESP_FAIL;
|
|
}
|
|
} else {
|
|
ESP_LOGE(TAG, "vfs must be Reader or Writer");
|
|
return ESP_FAIL;
|
|
}
|
|
} else if (vfs->fatfs) {
|
|
if (vfs->type == AUDIO_STREAM_READER) {
|
|
FRESULT ret = f_open(vfs->fatfs, &vfs->file.fat_file, path_out, FA_READ);
|
|
if (ret == FR_OK) {
|
|
FILINFO fno = { 0 };
|
|
f_stat(vfs->fatfs, path_out, &fno);
|
|
info.total_bytes = fno.fsize;
|
|
ESP_LOGI(TAG, "File size: %d byte, file position: %d", (int)fno.fsize, (int)info.byte_pos);
|
|
if (info.byte_pos > 0) {
|
|
if (f_lseek(&vfs->file.fat_file, info.byte_pos) < 0) {
|
|
ESP_LOGE(TAG, "Error seek file. Error message: %s, line: %d", strerror(errno), __LINE__);
|
|
return ESP_FAIL;
|
|
}
|
|
}
|
|
} else {
|
|
ESP_LOGE(TAG, "failed to open %s(%d)", path_out, ret);
|
|
return ESP_FAIL;
|
|
}
|
|
} else if (vfs->type == AUDIO_STREAM_WRITER) {
|
|
FRESULT ret = f_open(vfs->fatfs, &vfs->file.fat_file, path_out, FA_WRITE | FA_CREATE_ALWAYS);
|
|
if (ret == FR_OK) {
|
|
vfs->w_type = get_type(path_out);
|
|
UINT bw = 0;
|
|
if ((STREAM_TYPE_WAV == vfs->w_type)) {
|
|
wav_header_t info = {0};
|
|
f_write(&vfs->file.fat_file, &info, sizeof(wav_header_t), &bw);
|
|
f_sync(&vfs->file.fat_file);
|
|
} else if ((STREAM_TYPE_AMR == vfs->w_type)) {
|
|
f_write(&vfs->file.fat_file, "#!AMR\n", 6, &bw);
|
|
f_sync(&vfs->file.fat_file);
|
|
} else if ((STREAM_TYPE_AMRWB == vfs->w_type)) {
|
|
f_write(&vfs->file.fat_file, "#!AMR-WB\n", 9, &bw);
|
|
f_sync(&vfs->file.fat_file);
|
|
}
|
|
} else {
|
|
ESP_LOGE(TAG, "failed to open %s", path_out);
|
|
return ESP_FAIL;
|
|
}
|
|
} else {
|
|
ESP_LOGE(TAG, "vfs must be Reader or Writer");
|
|
return ESP_FAIL;
|
|
}
|
|
}
|
|
|
|
vfs->is_open = true;
|
|
int ret = audio_element_set_total_bytes(self, info.total_bytes);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int _vfs_read(audio_element_handle_t self, char *buffer, int len, TickType_t ticks_to_wait, void *context) {
|
|
vfs_stream_t *vfs = (vfs_stream_t *)audio_element_getdata(self);
|
|
audio_element_info_t info;
|
|
audio_element_getinfo(self, &info);
|
|
|
|
ESP_LOGI(TAG, "read len=%d, pos=%d/%d", len, (int)info.byte_pos, (int)info.total_bytes);
|
|
|
|
if (vfs->lfs2) {
|
|
UINT rlen = 0;
|
|
rlen = lfs2_file_read(vfs->lfs2, vfs->file.lfs2_file, buffer, len);
|
|
if (rlen <= 0) {
|
|
ESP_LOGW(TAG, "No more data,ret:%d", rlen);
|
|
rlen = 0;
|
|
} else {
|
|
info.byte_pos += rlen;
|
|
audio_element_setinfo(self, &info);
|
|
}
|
|
return rlen;
|
|
} else if (vfs->fatfs) {
|
|
UINT rlen = 0;
|
|
FRESULT ret = f_read(&vfs->file.fat_file, buffer, len, &rlen);
|
|
if (ret != FR_OK) {
|
|
ESP_LOGW(TAG, "No more data,ret:%d", ret);
|
|
rlen = 0;
|
|
} else {
|
|
info.byte_pos += rlen;
|
|
audio_element_setinfo(self, &info);
|
|
}
|
|
return rlen;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int _vfs_write(audio_element_handle_t self, char *buffer, int len, TickType_t ticks_to_wait, void *context) {
|
|
vfs_stream_t *vfs = (vfs_stream_t *)audio_element_getdata(self);
|
|
audio_element_info_t info;
|
|
audio_element_getinfo(self, &info);
|
|
UINT wlen = 0;
|
|
|
|
if (vfs->lfs2) {
|
|
wlen = lfs2_file_write(vfs->lfs2, vfs->file.lfs2_file, buffer, len);
|
|
lfs2_file_sync(vfs->lfs2, vfs->file.lfs2_file);
|
|
} else if (vfs->fatfs) {
|
|
f_write(&vfs->file.fat_file, buffer, len, &wlen);
|
|
f_sync(&vfs->file.fat_file);
|
|
}
|
|
|
|
ESP_LOGI(TAG, "mp_stream_posix_write,%d, errno:%d,pos:%d", wlen, errno, (int)info.byte_pos);
|
|
if (wlen > 0) {
|
|
info.byte_pos += wlen;
|
|
audio_element_setinfo(self, &info);
|
|
}
|
|
return wlen;
|
|
}
|
|
|
|
|
|
static int _vfs_process(audio_element_handle_t self, char *in_buffer, int in_len) {
|
|
int r_size = audio_element_input(self, in_buffer, in_len);
|
|
int w_size = 0;
|
|
if (r_size > 0) {
|
|
w_size = audio_element_output(self, in_buffer, r_size);
|
|
} else {
|
|
w_size = r_size;
|
|
}
|
|
return w_size;
|
|
}
|
|
|
|
|
|
static esp_err_t _vfs_close(audio_element_handle_t self) {
|
|
vfs_stream_t *vfs = (vfs_stream_t *)audio_element_getdata(self);
|
|
|
|
if (vfs->lfs2) {
|
|
if (
|
|
AUDIO_STREAM_WRITER == vfs->type
|
|
&& STREAM_TYPE_WAV == vfs->w_type
|
|
) {
|
|
wav_header_t *wav_info = (wav_header_t *)audio_malloc(sizeof(wav_header_t));
|
|
|
|
AUDIO_MEM_CHECK(TAG, wav_info, return ESP_ERR_NO_MEM);
|
|
|
|
lfs2_file_seek(vfs->lfs2, vfs->file.lfs2_file, 0, LFS2_SEEK_SET);
|
|
audio_element_info_t info;
|
|
UINT bw = 0;
|
|
audio_element_getinfo(self, &info);
|
|
wav_head_init(wav_info, info.sample_rates, info.bits, info.channels);
|
|
wav_head_size(wav_info, (uint32_t)info.byte_pos);
|
|
lfs2_file_write(vfs->lfs2, vfs->file.lfs2_file, wav_info, sizeof(wav_header_t));
|
|
lfs2_file_sync(vfs->lfs2, vfs->file.lfs2_file);
|
|
lfs2_file_close(vfs->lfs2, vfs->file.lfs2_file);
|
|
audio_free(wav_info);
|
|
}
|
|
|
|
if (vfs->is_open) {
|
|
lfs2_file_close(vfs->lfs2, vfs->file.lfs2_file);
|
|
free(vfs->file.lfs2_file);
|
|
vfs->is_open = false;
|
|
}
|
|
} else if (vfs->fatfs) {
|
|
if (
|
|
AUDIO_STREAM_WRITER == vfs->type
|
|
&& STREAM_TYPE_WAV == vfs->w_type
|
|
) {
|
|
wav_header_t *wav_info = (wav_header_t *)audio_malloc(sizeof(wav_header_t));
|
|
|
|
AUDIO_MEM_CHECK(TAG, wav_info, return ESP_ERR_NO_MEM);
|
|
|
|
f_lseek(&vfs->file.fat_file, 0);
|
|
audio_element_info_t info;
|
|
UINT bw = 0;
|
|
audio_element_getinfo(self, &info);
|
|
wav_head_init(wav_info, info.sample_rates, info.bits, info.channels);
|
|
wav_head_size(wav_info, (uint32_t)info.byte_pos);
|
|
f_write(&vfs->file.fat_file, wav_info, sizeof(wav_header_t), &bw);
|
|
f_sync(&vfs->file.fat_file);
|
|
f_close(&vfs->file.fat_file);
|
|
audio_free(wav_info);
|
|
}
|
|
|
|
if (vfs->is_open) {
|
|
f_close(&vfs->file.fat_file);
|
|
vfs->is_open = false;
|
|
}
|
|
}
|
|
|
|
if (AEL_STATE_PAUSED != audio_element_get_state(self)) {
|
|
audio_element_report_info(self);
|
|
audio_element_info_t info = { 0 };
|
|
audio_element_getinfo(self, &info);
|
|
info.byte_pos = 0;
|
|
audio_element_setinfo(self, &info);
|
|
}
|
|
|
|
vfs->lfs2 = NULL;
|
|
vfs->fatfs = NULL;
|
|
|
|
return ESP_OK;
|
|
}
|
|
|
|
|
|
static esp_err_t _vfs_destroy(audio_element_handle_t self) {
|
|
vfs_stream_t *vfs = (vfs_stream_t *)audio_element_getdata(self);
|
|
audio_free(vfs);
|
|
return ESP_OK;
|
|
}
|
|
|
|
|
|
void log_file(char *buffer, size_t len) {
|
|
const char *path_out;
|
|
mp_vfs_mount_t *existing_mount = mp_vfs_lookup_path("/flash/output", &path_out);
|
|
if (existing_mount == MP_VFS_NONE || existing_mount == MP_VFS_ROOT) {
|
|
ESP_LOGE(TAG, "No vfs mount");
|
|
return;
|
|
}
|
|
lfs2_t *log_lfs = &((mp_obj_vfs_lfs2_t *)MP_OBJ_TO_PTR(existing_mount->obj))->lfs;
|
|
|
|
struct lfs2_file_config config;
|
|
memset(&config, 0x00, sizeof(config));
|
|
|
|
config.buffer = malloc(log_lfs->cfg->cache_size * sizeof(uint8_t));
|
|
lfs2_file_t *file = (lfs2_file_t *)malloc(1 * sizeof(lfs2_file_t));
|
|
int res = lfs2_file_opencfg(log_lfs, file, "/output", LFS2_O_RDWR | LFS2_O_CREAT | LFS2_O_APPEND, &config);
|
|
lfs2_file_write(log_lfs, file, buffer, len);
|
|
lfs2_file_sync(log_lfs, file);
|
|
lfs2_file_close(log_lfs, file);
|
|
|
|
free(config.buffer);
|
|
free(file);
|
|
}
|