diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index eb099843..ed2eebf9 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -56,6 +56,7 @@ target_sources(app PRIVATE src/zsw_light_sensor.c) target_sources_ifdef(CONFIG_SPI_FLASH_LOADER app PRIVATE src/zsw_rtt_flash_loader.c) target_sources_ifdef(CONFIG_FILE_SYSTEM_LITTLEFS app PRIVATE src/zsw_filesystem.c) +target_sources_ifdef(CONFIG_FILE_SYSTEM_LITTLEFS app PRIVATE src/zsw_lvgl_spi_decoder.c) if (DFU_BUILD) target_sources(app PRIVATE src/dfu.c) diff --git a/app/scripts/create_lvgl_image.py b/app/scripts/create_lvgl_image.py new file mode 100644 index 00000000..dd641121 --- /dev/null +++ b/app/scripts/create_lvgl_image.py @@ -0,0 +1,69 @@ +import os +import argparse +from struct import * + +MAX_FILE_NAME = 16 +FILE_TABLE_MAX_LEN = 1024 # TODO USE and match with c code +''' +magic_number:uint32 +header_len:uint32 +total_length:uint32 +num_files:uint32 +filename[MAX_FILE_NAME + 1] +offset:uint32 +len:uint32 +filename[MAX_FILE_NAME + 1] +offset:uint32 +len:uint32 +... +''' +# TODO add total length ijn the beginning? + +def create_fs_image(img_filename, source_dir, block_size=4096): + table = {} + offset = 0 + files_image = bytearray() + header_image = bytearray() + for root, dirs, files in os.walk(source_dir): + print(f"root {root} dirs {dirs} files {files}") + for filename in files: + path = os.path.join(root, filename) + relpath = os.path.relpath(path, start=source_dir) + print(f"Adding {path}") + with open(path, "rb") as infile: + files_image.extend(infile.read()) + header_image = bytearray() + table[filename] = { + 'offset': offset, + 'len': infile.tell() + } + offset = offset + infile.tell() + print(table) + header_image = pack(" +#include +#include +#include +#include +#include +#include "lv_conf.h" +#include LV_MEM_CUSTOM_INCLUDE + + +#define SPI_FLASH_SECTOR_SIZE 4096 + +#define FLASH_PARTITION_NAME lvgl_fs_partition + +#define FLASH_PARTITION_ID FIXED_PARTITION_ID(FLASH_PARTITION_NAME) +#define FLASH_PARTITION_DEVICE FIXED_PARTITION_DEVICE(FLASH_PARTITION_NAME) +#define FLASH_PARTITION_OFFSET FIXED_PARTITION_OFFSET(FLASH_PARTITION_NAME) + +#define FILE_TABLE_MAX_LEN 1024 +#define MAX_FILE_NAME_LEN 16 +#define MAX_OPENED_FILES 8 + +typedef struct file_header_t { + uint8_t filename[MAX_FILE_NAME_LEN]; + uint32_t offset; + uint32_t len; +} file_header_t; + +typedef struct file_table_t { + uint32_t header_length; // Image offset counted from after this. + uint32_t total_length; + uint32_t magic; + uint32_t num_files; + file_header_t file_headers[FILE_TABLE_MAX_LEN / sizeof(file_header_t)]; +} file_table_t; + +typedef struct opened_file_t { + file_header_t* header; + uint32_t index; +} opened_file_t; + +static file_table_t file_table; +static opened_file_t opened_files[MAX_OPENED_FILES]; + +static const struct flash_area *flash_area; + +static file_header_t* find_file(const char* name) +{ + for (int i = 0; i < file_table.num_files; i++) { + if (strncmp(name, file_table.file_headers[i].filename, MAX_FILE_NAME_LEN) == 0) { + return &file_table.file_headers[i]; + } + } + return NULL; +} + +static opened_file_t* find_free_opened_file(void) +{ + for (int i = 0; i < MAX_OPENED_FILES; i++) { + if (opened_files[i].header == NULL) { + return &opened_files[i]; + } + } + return NULL; +} + +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) +{ + file_header_t* file = find_file(path); + opened_file_t* open_file; + + if (!file) { + return NULL; + } + + open_file = find_free_opened_file(); + if (!file) { + return NULL; + } + + open_file->header = file; + open_file->index = 0; + + return open_file; +} + +static lv_fs_res_t lvgl_fs_close(struct _lv_fs_drv_t *drv, void *file) +{ + opened_file_t* open_file = (opened_file_t*)file; + open_file->header = NULL; + open_file->index = 0; + return errno_to_lv_fs_res(0); +} + +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 rc; + opened_file_t* open_file = (opened_file_t*)file; + + rc = flash_area_read(flash_area, 40 + open_file->index/*open_file->header->offset + open_file->index + file_table.header_length - 2*/, buf, btr); + if (rc != 0) { + printk("Flash read failed! %d\n", rc); + *br = 0; + return errno_to_lv_fs_res(rc); + } + *br = btr; + return errno_to_lv_fs_res(0); +} + +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) +{ + return LV_FS_RES_NOT_IMP; +} + +static lv_fs_res_t lvgl_fs_seek(struct _lv_fs_drv_t *drv, void *file, uint32_t pos, + lv_fs_whence_t whence) +{ + opened_file_t* open_file = (opened_file_t*)file; + + switch (whence) { + case LV_FS_SEEK_END: + open_file->index = open_file->header->len; + break; + case LV_FS_SEEK_CUR: + // We are already there? + break; + case LV_FS_SEEK_SET: + default: + open_file->index = pos; + break; + } + + return errno_to_lv_fs_res(0); +} + +static lv_fs_res_t lvgl_fs_tell(struct _lv_fs_drv_t *drv, void *file, uint32_t *pos_p) +{ + opened_file_t* open_file = (opened_file_t*)file; + *pos_p = open_file->index; + return LV_FS_RES_OK; +} + +static void *lvgl_fs_dir_open(struct _lv_fs_drv_t *drv, const char *path) +{ + return NULL; +} + +static lv_fs_res_t lvgl_fs_dir_read(struct _lv_fs_drv_t *drv, void *dir, char *fn) +{ + 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 = 0; + return errno_to_lv_fs_res(err); +} + +static lv_fs_drv_t fs_drv; + +int zsw_decoder_init(void) +{ + int rc; + 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 = 'S'; + 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); + + memset(opened_files, 0, sizeof(opened_files)); + + rc = flash_area_open(FLASH_PARTITION_ID, &flash_area); + + if (rc != 0) { + printk("FAIL: unable to find flash area %u: %d\n", FLASH_PARTITION_ID, rc); + return 0; + } + + rc = flash_area_read(flash_area, 0, &file_table, FILE_TABLE_MAX_LEN); + if (rc != 0) { + printk("Flash read failed! %d\n", rc); + return rc; + } + return 0; +} + +SYS_INIT(zsw_decoder_init, APPLICATION, 99); diff --git a/app/src/zsw_rtt_flash_loader.c b/app/src/zsw_rtt_flash_loader.c index 44e9380f..f5f8f9d4 100644 --- a/app/src/zsw_rtt_flash_loader.c +++ b/app/src/zsw_rtt_flash_loader.c @@ -1,9 +1,3 @@ -/* - * Copyright (c) 2023 Jakob Krantz - * - * SPDX-License-Identifier: Apache-2.0 - */ - #include #include #include