/* * Copyright (c) 2018-2019 Jan Van Winkel * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include "lvgl_fs.h" #include "lv_conf.h" #include LV_MEM_CUSTOM_INCLUDE static bool lvgl_fs_ready(struct _lv_fs_drv_t *drv) { return true; } static lv_fs_res_t errno_to_lv_fs_res(int err) { switch (err) { case 0: return LV_FS_RES_OK; case -EIO: /*Low level hardware error*/ return LV_FS_RES_HW_ERR; case -EBADF: /*Error in the file system structure */ return LV_FS_RES_FS_ERR; case -ENOENT: /*Driver, file or directory is not exists*/ return LV_FS_RES_NOT_EX; case -EFBIG: /*Disk full*/ return LV_FS_RES_FULL; case -EACCES: /*Access denied. Check 'fs_open' modes and write protect*/ return LV_FS_RES_DENIED; case -EBUSY: /*The file system now can't handle it, try later*/ return LV_FS_RES_BUSY; case -ENOMEM: /*Not enough memory for an internal operation*/ return LV_FS_RES_OUT_OF_MEM; case -EINVAL: /*Invalid parameter among arguments*/ return LV_FS_RES_INV_PARAM; case -ENOTSUP: /*Not supported by the filesystem*/ return LV_FS_RES_NOT_IMP; default: return LV_FS_RES_UNKNOWN; } } static void *lvgl_fs_open(struct _lv_fs_drv_t *drv, const char *path, lv_fs_mode_t mode) { int err; int zmode = FS_O_CREATE; void *file; /* LVGL is passing absolute paths without the root slash add it back * by decrementing the path pointer. */ path--; zmode |= (mode & LV_FS_MODE_WR) ? FS_O_WRITE : 0; zmode |= (mode & LV_FS_MODE_RD) ? FS_O_READ : 0; file = LV_MEM_CUSTOM_ALLOC(sizeof(struct fs_file_t)); if (!file) { return NULL; } fs_file_t_init((struct fs_file_t *)file); err = fs_open((struct fs_file_t *)file, path, zmode); if (err) { LV_MEM_CUSTOM_FREE(file); return NULL; } return file; } static lv_fs_res_t lvgl_fs_close(struct _lv_fs_drv_t *drv, void *file) { int err; err = fs_close((struct fs_file_t *)file); return errno_to_lv_fs_res(err); } static lv_fs_res_t lvgl_fs_read(struct _lv_fs_drv_t *drv, void *file, void *buf, uint32_t btr, uint32_t *br) { int err; err = fs_read((struct fs_file_t *)file, buf, btr); if (err > 0) { if (br != NULL) { *br = err; } err = 0; } else if (br != NULL) { *br = 0U; } return errno_to_lv_fs_res(err); } static lv_fs_res_t lvgl_fs_write(struct _lv_fs_drv_t *drv, void *file, const void *buf, uint32_t btw, uint32_t *bw) { int err; err = fs_write((struct fs_file_t *)file, buf, btw); if (err == btw) { if (bw != NULL) { *bw = btw; } err = 0; } else if (err < 0) { if (bw != NULL) { *bw = 0U; } } else { if (bw != NULL) { *bw = err; } err = -EFBIG; } return errno_to_lv_fs_res(err); } static lv_fs_res_t lvgl_fs_seek(struct _lv_fs_drv_t *drv, void *file, uint32_t pos, lv_fs_whence_t whence) { int err, fs_whence; switch (whence) { case LV_FS_SEEK_END: fs_whence = FS_SEEK_END; break; case LV_FS_SEEK_CUR: fs_whence = FS_SEEK_CUR; break; case LV_FS_SEEK_SET: default: fs_whence = FS_SEEK_SET; break; } err = fs_seek((struct fs_file_t *)file, pos, fs_whence); return errno_to_lv_fs_res(err); } static lv_fs_res_t lvgl_fs_tell(struct _lv_fs_drv_t *drv, void *file, uint32_t *pos_p) { off_t pos; pos = fs_tell((struct fs_file_t *)file); if (pos < 0) { return errno_to_lv_fs_res(pos); } *pos_p = pos; return LV_FS_RES_OK; } static void *lvgl_fs_dir_open(struct _lv_fs_drv_t *drv, const char *path) { void *dir; int err; /* LVGL is passing absolute paths without the root slash add it back * by decrementing the path pointer. */ path--; dir = LV_MEM_CUSTOM_ALLOC(sizeof(struct fs_dir_t)); if (!dir) { return NULL; } fs_dir_t_init((struct fs_dir_t *)dir); err = fs_opendir((struct fs_dir_t *)dir, path); if (err) { LV_MEM_CUSTOM_FREE(dir); return NULL; } return dir; } static lv_fs_res_t lvgl_fs_dir_read(struct _lv_fs_drv_t *drv, void *dir, char *fn) { /* LVGL expects a string as return parameter but the format of the * string is not documented. */ return LV_FS_RES_NOT_IMP; } static lv_fs_res_t lvgl_fs_dir_close(struct _lv_fs_drv_t *drv, void *dir) { int err; err = fs_closedir((struct fs_dir_t *)dir); LV_MEM_CUSTOM_FREE(dir); return errno_to_lv_fs_res(err); } static lv_fs_drv_t fs_drv; void lvgl_fs_init(void) { lv_fs_drv_init(&fs_drv); /* LVGL uses letter based mount points, just pass the root slash as a * letter. Note that LVGL will remove the drive letter, or in this case * the root slash, from the path passed via the FS callbacks. * Zephyr FS API assumes this slash is present so we will need to add * it back. */ fs_drv.letter = '/'; fs_drv.ready_cb = lvgl_fs_ready; fs_drv.open_cb = lvgl_fs_open; fs_drv.close_cb = lvgl_fs_close; fs_drv.read_cb = lvgl_fs_read; fs_drv.write_cb = lvgl_fs_write; fs_drv.seek_cb = lvgl_fs_seek; fs_drv.tell_cb = lvgl_fs_tell; fs_drv.dir_open_cb = lvgl_fs_dir_open; fs_drv.dir_read_cb = lvgl_fs_dir_read; fs_drv.dir_close_cb = lvgl_fs_dir_close; lv_fs_drv_register(&fs_drv); }