|
| 1 | +/* |
| 2 | + * SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD |
| 3 | + * |
| 4 | + * SPDX-License-Identifier: MIT |
| 5 | + */ |
| 6 | + |
| 7 | +#include <esp_log.h> |
| 8 | +#include "py/runtime.h" |
| 9 | +#include "py/stream.h" |
| 10 | + |
| 11 | +#include <extmod/vfs.h> |
| 12 | +#include <extmod/vfs_lfs.h> |
| 13 | +#include "extmod/vfs_fat.h" |
| 14 | +#include "lib/littlefs/lfs2.h" |
| 15 | +#include "lib/oofatfs/ff.h" |
| 16 | + |
| 17 | +#include "lvgl/lvgl.h" |
| 18 | +#include "./../../cmodules/lv_binding_micropython/driver/include/common.h" |
| 19 | +#include "string.h" |
| 20 | + |
| 21 | +static const char *TAG = "lv_utils"; |
| 22 | + |
| 23 | +// micropython/extmod/vfs_lfs.c line: 115 |
| 24 | +typedef struct _mp_obj_vfs_lfs2_t { |
| 25 | + mp_obj_base_t base; |
| 26 | + mp_vfs_blockdev_t blockdev; |
| 27 | + bool enable_mtime; |
| 28 | + vstr_t cur_dir; |
| 29 | + struct lfs2_config config; |
| 30 | + lfs2_t lfs; |
| 31 | +} mp_obj_vfs_lfs2_t; |
| 32 | + |
| 33 | +typedef struct vfs_stream_t { |
| 34 | + bool is_open; |
| 35 | + FATFS *fatfs; |
| 36 | + lfs2_t *lfs2; |
| 37 | + struct lfs2_file_config lfs2_file_conf; |
| 38 | + union { |
| 39 | + FIL fat_file; |
| 40 | + lfs2_file_t *lfs2_file; |
| 41 | + } file; |
| 42 | +} vfs_stream_t; |
| 43 | + |
| 44 | + |
| 45 | +static void *lv_utils_fs_open_cb(lv_fs_drv_t *drv, const char *path, lv_fs_mode_t mode) { |
| 46 | + LV_UNUSED(drv); |
| 47 | + lv_fs_res_t res = LV_FS_RES_NOT_IMP; |
| 48 | + |
| 49 | + ESP_LOGI("lv_utils", "fs_open_cb: path=%s, mode=%d\n", path, mode); |
| 50 | + |
| 51 | + vfs_stream_t *vfs = lv_malloc(sizeof(vfs_stream_t)); |
| 52 | + |
| 53 | + const char *path_out; |
| 54 | + mp_vfs_mount_t *existing_mount = mp_vfs_lookup_path(path, &path_out); |
| 55 | + if (existing_mount == MP_VFS_NONE || existing_mount == MP_VFS_ROOT) { |
| 56 | + ESP_LOGE(TAG, "No vfs mount"); |
| 57 | + goto _vfs_init_exit; |
| 58 | + } |
| 59 | + if (strstr(path, "flash")) { |
| 60 | + ESP_LOGD(TAG, "in flash"); |
| 61 | + vfs->lfs2 = &((mp_obj_vfs_lfs2_t *)MP_OBJ_TO_PTR(existing_mount->obj))->lfs; |
| 62 | + } else if (strstr(path, "sd")) { |
| 63 | + ESP_LOGD(TAG, "in sd"); |
| 64 | + vfs->fatfs = &((fs_user_mount_t *)MP_OBJ_TO_PTR(existing_mount->obj))->fatfs; |
| 65 | + } |
| 66 | + |
| 67 | + if (vfs->lfs2) { |
| 68 | + vfs->lfs2_file_conf.buffer = lv_malloc(vfs->lfs2->cfg->cache_size * sizeof(uint8_t)); |
| 69 | + vfs->file.lfs2_file = (lfs2_file_t *)lv_malloc(1 * sizeof(lfs2_file_t)); |
| 70 | + int flags = 0; |
| 71 | + if (mode == LV_FS_MODE_WR) { |
| 72 | + flags = LFS2_O_WRONLY; |
| 73 | + } else if (mode == LV_FS_MODE_RD) { |
| 74 | + flags = LFS2_O_RDONLY; |
| 75 | + } else if (mode == (LV_FS_MODE_WR | LV_FS_MODE_RD)) { |
| 76 | + flags = LFS2_O_RDWR; |
| 77 | + } |
| 78 | + int res = lfs2_file_opencfg(vfs->lfs2, vfs->file.lfs2_file, path_out, flags, &vfs->lfs2_file_conf); |
| 79 | + if (res != LFS2_ERR_OK) { |
| 80 | + ESP_LOGE(TAG, "failed to open %s(%d)", path_out, res); |
| 81 | + goto _vfs_init_exit; |
| 82 | + } |
| 83 | + } else if (vfs->fatfs) { |
| 84 | + BYTE fmode = 0; |
| 85 | + if (mode == LV_FS_MODE_WR) { |
| 86 | + fmode = FA_WRITE; |
| 87 | + } else if (mode == LV_FS_MODE_RD) { |
| 88 | + fmode = FA_READ; |
| 89 | + } else if (mode == (LV_FS_MODE_WR | LV_FS_MODE_RD)) { |
| 90 | + fmode = FA_OPEN_ALWAYS; |
| 91 | + } |
| 92 | + FRESULT ret = f_open(vfs->fatfs, &vfs->file.fat_file, path_out, fmode); |
| 93 | + if (ret != FR_OK) { |
| 94 | + ESP_LOGE(TAG, "failed to open %s(%d)", path_out, ret); |
| 95 | + goto _vfs_init_exit; |
| 96 | + } |
| 97 | + } |
| 98 | + |
| 99 | + return vfs; |
| 100 | +_vfs_init_exit: |
| 101 | + if (vfs->lfs2_file_conf.buffer) { |
| 102 | + lv_free(vfs->lfs2_file_conf.buffer); |
| 103 | + } |
| 104 | + if (vfs->file.lfs2_file) { |
| 105 | + lv_free(vfs->file.lfs2_file); |
| 106 | + } |
| 107 | + lv_free(vfs); |
| 108 | + |
| 109 | + return NULL; |
| 110 | +} |
| 111 | +DEFINE_PTR_OBJ(lv_utils_fs_open_cb); |
| 112 | + |
| 113 | + |
| 114 | +static lv_fs_res_t lv_utils_fs_read_cb(lv_fs_drv_t *drv, void *file_p, void *buf, uint32_t btr, uint32_t *br) { |
| 115 | + LV_UNUSED(drv); |
| 116 | + vfs_stream_t *vfs = file_p; |
| 117 | + |
| 118 | + if (vfs->lfs2) { |
| 119 | + *br = lfs2_file_read(vfs->lfs2, vfs->file.lfs2_file, (uint8_t *)buf, btr); |
| 120 | + return (int32_t)(*br) < 0 ? LV_FS_RES_UNKNOWN : LV_FS_RES_OK; |
| 121 | + } else if (vfs->fatfs) { |
| 122 | + FRESULT res = f_read(&vfs->file.fat_file, buf, btr, (UINT *)br); |
| 123 | + return res == FR_OK ? LV_FS_RES_OK : LV_FS_RES_UNKNOWN; |
| 124 | + } |
| 125 | + |
| 126 | + return LV_FS_RES_UNKNOWN; |
| 127 | +} |
| 128 | +DEFINE_PTR_OBJ(lv_utils_fs_read_cb); |
| 129 | + |
| 130 | + |
| 131 | +static lv_fs_res_t lv_utils_fs_write_cb(lv_fs_drv_t *drv, void *file_p, const void *buf, uint32_t btw, uint32_t *bw) { |
| 132 | + LV_UNUSED(drv); |
| 133 | + vfs_stream_t *vfs = file_p; |
| 134 | + |
| 135 | + if (vfs->lfs2) { |
| 136 | + *bw = lfs2_file_write(vfs->lfs2, vfs->file.lfs2_file, (uint8_t *)buf, btw); |
| 137 | + lfs2_file_sync(vfs->lfs2, vfs->file.lfs2_file); |
| 138 | + } else if (vfs->fatfs) { |
| 139 | + f_write(&vfs->file.fat_file, (uint8_t *)buf, btw, (UINT *)bw); |
| 140 | + f_sync(&vfs->file.fat_file); |
| 141 | + } |
| 142 | + return (int32_t)(*bw) < 0 ? LV_FS_RES_UNKNOWN : LV_FS_RES_OK; |
| 143 | +} |
| 144 | +DEFINE_PTR_OBJ(lv_utils_fs_write_cb); |
| 145 | + |
| 146 | + |
| 147 | +static lv_fs_res_t lv_utils_fs_seek_cb(lv_fs_drv_t *drv, void *file_p, uint32_t pos, lv_fs_whence_t whence) { |
| 148 | + LV_UNUSED(drv); |
| 149 | + vfs_stream_t *vfs = file_p; |
| 150 | + |
| 151 | + if (vfs->lfs2) { |
| 152 | + int mode = 0; |
| 153 | + if (whence == LV_FS_SEEK_SET) { |
| 154 | + mode = LFS2_SEEK_SET; |
| 155 | + } else if (whence == LV_FS_SEEK_CUR) { |
| 156 | + mode = LFS2_SEEK_CUR; |
| 157 | + } else if (whence == LV_FS_SEEK_END) { |
| 158 | + mode = LFS2_SEEK_END; |
| 159 | + } |
| 160 | + |
| 161 | + int rc = lfs2_file_seek(vfs->lfs2, vfs->file.lfs2_file, pos, mode); |
| 162 | + return rc < 0 ? LV_FS_RES_UNKNOWN : LV_FS_RES_OK; |
| 163 | + } else if (vfs->fatfs) { |
| 164 | + FRESULT res = FR_INT_ERR; |
| 165 | + switch (whence) { |
| 166 | + case LV_FS_SEEK_SET: |
| 167 | + res = f_lseek(&vfs->file.fat_file, pos); |
| 168 | + break; |
| 169 | + case LV_FS_SEEK_CUR: |
| 170 | + res = f_lseek(&vfs->file.fat_file, f_tell((FIL *)&vfs->file.fat_file) + pos); |
| 171 | + break; |
| 172 | + case LV_FS_SEEK_END: |
| 173 | + res = f_lseek(&vfs->file.fat_file, f_size((FIL *)&vfs->file.fat_file) + pos); |
| 174 | + break; |
| 175 | + default: |
| 176 | + break; |
| 177 | + } |
| 178 | + return res == FR_OK ? LV_FS_RES_OK : LV_FS_RES_UNKNOWN; |
| 179 | + } |
| 180 | + |
| 181 | + return LV_FS_RES_UNKNOWN; |
| 182 | +} |
| 183 | +DEFINE_PTR_OBJ(lv_utils_fs_seek_cb); |
| 184 | + |
| 185 | + |
| 186 | +static lv_fs_res_t lv_utils_fs_tell_cb(lv_fs_drv_t *drv, void *file_p, uint32_t *pos_p) { |
| 187 | + LV_UNUSED(drv); |
| 188 | + vfs_stream_t *vfs = file_p; |
| 189 | + |
| 190 | + if (vfs->lfs2) { |
| 191 | + *pos_p = lfs2_file_tell(vfs->lfs2, vfs->file.lfs2_file); |
| 192 | + } else if (vfs->fatfs) { |
| 193 | + *pos_p = f_tell((FIL *)&vfs->file.fat_file); |
| 194 | + } |
| 195 | + |
| 196 | + return (int32_t)(*pos_p) < 0 ? LV_FS_RES_UNKNOWN : LV_FS_RES_OK; |
| 197 | +} |
| 198 | +DEFINE_PTR_OBJ(lv_utils_fs_tell_cb); |
| 199 | + |
| 200 | +static lv_fs_res_t lv_utils_fs_close_cb(lv_fs_drv_t *drv, void *file_p) { |
| 201 | + LV_UNUSED(drv); |
| 202 | + vfs_stream_t *vfs = file_p; |
| 203 | + |
| 204 | + if (vfs->lfs2) { |
| 205 | + lfs2_file_close(vfs->lfs2, vfs->file.lfs2_file); |
| 206 | + lv_free(vfs->file.lfs2_file); |
| 207 | + lv_free(vfs->lfs2_file_conf.buffer); |
| 208 | + } else if (vfs->fatfs) { |
| 209 | + f_close(&vfs->file.fat_file); |
| 210 | + } |
| 211 | + |
| 212 | + return LV_FS_RES_OK; |
| 213 | +} |
| 214 | +DEFINE_PTR_OBJ(lv_utils_fs_close_cb); |
| 215 | + |
| 216 | + |
| 217 | +static const mp_rom_map_elem_t lv_utils_module_globals_table[] = { |
| 218 | + /* *FORMAT-OFF* */ |
| 219 | + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR__lv_utils) }, |
| 220 | + { MP_ROM_QSTR(MP_QSTR_fs_open_cb), MP_ROM_PTR(&PTR_OBJ(lv_utils_fs_open_cb)) }, |
| 221 | + { MP_ROM_QSTR(MP_QSTR_fs_read_cb), MP_ROM_PTR(&PTR_OBJ(lv_utils_fs_read_cb)) }, |
| 222 | + { MP_ROM_QSTR(MP_QSTR_fs_write_cb), MP_ROM_PTR(&PTR_OBJ(lv_utils_fs_write_cb)) }, |
| 223 | + { MP_ROM_QSTR(MP_QSTR_fs_seek_cb), MP_ROM_PTR(&PTR_OBJ(lv_utils_fs_seek_cb)) }, |
| 224 | + { MP_ROM_QSTR(MP_QSTR_fs_tell_cb), MP_ROM_PTR(&PTR_OBJ(lv_utils_fs_tell_cb)) }, |
| 225 | + { MP_ROM_QSTR(MP_QSTR_fs_close_cb), MP_ROM_PTR(&PTR_OBJ(lv_utils_fs_close_cb)) }, |
| 226 | + /* *FORMAT-ON* */ |
| 227 | +}; |
| 228 | + |
| 229 | +static MP_DEFINE_CONST_DICT(lv_utils_module_globals, lv_utils_module_globals_table); |
| 230 | + |
| 231 | +const mp_obj_module_t lv_utils_module = { |
| 232 | + .base = {&mp_type_module}, |
| 233 | + .globals = (mp_obj_dict_t *)&lv_utils_module_globals, |
| 234 | +}; |
| 235 | + |
| 236 | +MP_REGISTER_MODULE(MP_QSTR__lv_utils, lv_utils_module); |
0 commit comments