diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 8d12d6bf4..24d5cbc62 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -49,6 +49,7 @@ lib/cyfral/** @DrZlo13 lib/drivers/** @skotopes @gornekich lib/fatfs/** @DrZlo13 lib/fnv1a-hash/** @DrZlo13 +lib/littlefs/** @skotopes lib/mlib/** @skotopes lib/onewire/** @DrZlo13 lib/qrcode/** @DrZlo13 diff --git a/.gitmodules b/.gitmodules index bd243b024..bf8bd6858 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,6 @@ [submodule "applications/floopper-bloopper"] path = applications/floopper-bloopper url = https://github.com/Flipper-Zero/floopper-bloopper.git +[submodule "lib/littlefs"] + path = lib/littlefs + url = https://github.com/littlefs-project/littlefs.git diff --git a/applications/app-loader/app-loader.c b/applications/app-loader/app-loader.c index 6b9e4171b..732766ecf 100644 --- a/applications/app-loader/app-loader.c +++ b/applications/app-loader/app-loader.c @@ -58,7 +58,7 @@ void app_loader_thread_state_callback(FuriThreadState state, void* context) { } int32_t app_loader(void* p) { - FURI_LOG_I("APPLOADER", "Started"); + FURI_LOG_I("app-loader", "Starting"); state.thread = furi_thread_alloc(); furi_thread_set_state_context(state.thread, &state); furi_thread_set_state_callback(state.thread, app_loader_thread_state_callback); @@ -67,6 +67,7 @@ int32_t app_loader(void* p) { state.cli = furi_record_open("cli"); // Main menu + FURI_LOG_I("app-loader", "Building main menu"); with_value_mutex( menu_mutex, (Menu * menu) { for(size_t i = 0; i < FLIPPER_APPS_COUNT; i++) { @@ -93,6 +94,7 @@ int32_t app_loader(void* p) { }); // Plugins + FURI_LOG_I("app-loader", "Building plugins menu"); with_value_mutex( menu_mutex, (Menu * menu) { MenuItem* menu_plugins = @@ -124,6 +126,7 @@ int32_t app_loader(void* p) { }); // Debug + FURI_LOG_I("app-loader", "Building debug menu"); with_value_mutex( menu_mutex, (Menu * menu) { MenuItem* menu_debug = @@ -159,7 +162,7 @@ int32_t app_loader(void* p) { (*FLIPPER_ON_SYSTEM_START[i])(); } - FURI_LOG_I("APPLOADER", "OK"); + FURI_LOG_I("app-loader", "Started"); while(1) { osThreadSuspend(osThreadGetId()); diff --git a/applications/applications.c b/applications/applications.c index 1647ccf18..a0db64873 100644 --- a/applications/applications.c +++ b/applications/applications.c @@ -34,6 +34,7 @@ int32_t keypad_test(void* p); int32_t scene_app(void* p); int32_t passport(void* p); int32_t app_accessor(void* p); +int32_t internal_storage_task(void* p); // On system start hooks declaration void nfc_cli_init(); @@ -73,6 +74,13 @@ const FlipperApplication FLIPPER_SERVICES[] = { {.app = sd_filesystem, .name = "sd_filesystem", .stack_size = 4096, .icon = A_Plugins_14}, #endif +#ifdef SRV_INTERNAL_STORAGE + {.app = internal_storage_task, + .name = "internal_storage", + .stack_size = 2048, + .icon = A_Plugins_14}, +#endif + #ifdef SRV_DOLPHIN {.app = dolphin_task, .name = "dolphin_task", .stack_size = 1024, .icon = A_Plugins_14}, #endif diff --git a/applications/applications.mk b/applications/applications.mk index 028f6f5d1..c2bc8f297 100644 --- a/applications/applications.mk +++ b/applications/applications.mk @@ -17,6 +17,7 @@ SRV_POWER = 1 SRV_BT = 1 SRV_CLI = 1 SRV_SD_FILESYSTEM = 1 +SRV_INTERNAL_STORAGE = 1 SRV_DOLPHIN = 1 # Main Apps @@ -288,11 +289,16 @@ ifeq ($(SRV_GUI), 1) CFLAGS += -DSRV_GUI endif -SRV_SD_FILESYSTEM ?= 0 +SRV_SD_FILESYSTEM ?= 0 ifeq ($(SRV_SD_FILESYSTEM), 1) CFLAGS += -DSRV_SD_FILESYSTEM endif +SRV_INTERNAL_STORAGE ?= 0 +ifeq ($(SRV_INTERNAL_STORAGE), 1) +CFLAGS += -DSRV_INTERNAL_STORAGE +endif + SRV_INPUT ?= 0 ifeq ($(SRV_INPUT), 1) CFLAGS += -DSRV_INPUT diff --git a/applications/dolphin/dolphin_state.c b/applications/dolphin/dolphin_state.c index 46be7f507..d9429b456 100644 --- a/applications/dolphin/dolphin_state.c +++ b/applications/dolphin/dolphin_state.c @@ -1,77 +1,129 @@ #include "dolphin_state.h" + +#include #include -#include #include +#define DOLPHIN_STORE_KEY "dolphin_state" +#define DOLPHIN_STORE_HEADER_MAGIC 0xD0 +#define DOLPHIN_STORE_HEADER_VERSION 0x01 +#define DOLPHIN_LVL_THRESHOLD 20.0f + +typedef struct { + uint8_t magic; + uint8_t version; + uint8_t checksum; + uint8_t flags; + uint32_t timestamp; +} DolphinStoreHeader; + +typedef struct { + uint32_t limit_ibutton; + uint32_t limit_nfc; + uint32_t limit_ir; + uint32_t limit_rfid; + + uint32_t flags; + uint32_t icounter; + uint32_t butthurt; +} DolphinStoreData; + +typedef struct { + DolphinStoreHeader header; + DolphinStoreData data; +} DolphinStore; + +struct DolphinState { + InternalStorage* internal_storage; + DolphinStoreData data; +}; + DolphinState* dolphin_state_alloc() { DolphinState* dolphin_state = furi_alloc(sizeof(DolphinState)); + dolphin_state->internal_storage = furi_record_open("internal-storage"); return dolphin_state; } void dolphin_state_free(DolphinState* dolphin_state) { + furi_record_close("internal-storage"); free(dolphin_state); } bool dolphin_state_save(DolphinState* dolphin_state) { - if(!api_hal_flash_erase(DOLPHIN_DATA_PAGE, 1)) { - return false; - } - + DolphinStore store; + FURI_LOG_I("dolphin-state", "Saving state to internal-storage"); + // Calculate checksum uint8_t* source = (uint8_t*)&dolphin_state->data; uint8_t checksum = 0; - for(size_t i = 0; i < sizeof(DolphinData); i++) { + for(size_t i = 0; i < sizeof(DolphinStoreData); i++) { checksum += source[i]; } - DolphinDataHeader header; - header.magic = DOLPHIN_DATA_HEADER_MAGIC; - header.version = DOLPHIN_DATA_HEADER_VERSION; - header.checksum = checksum; - header.flags = 0; - header.timestamp = 0; - if(!api_hal_flash_write_dword(DOLPHIN_DATA_HEADER_ADDRESS, *(uint64_t*)&header)) { + // Set header + store.header.magic = DOLPHIN_STORE_HEADER_MAGIC; + store.header.version = DOLPHIN_STORE_HEADER_VERSION; + store.header.checksum = checksum; + store.header.flags = 0; + store.header.timestamp = 0; + // Set data + store.data = dolphin_state->data; + // Store + int ret = internal_storage_write_key( + dolphin_state->internal_storage, DOLPHIN_STORE_KEY, (uint8_t*)&store, sizeof(DolphinStore)); + if(ret != sizeof(DolphinStore)) { + FURI_LOG_E("dolphin-state", "Save failed. Storage returned: %d", ret); return false; } - uint8_t destination[sizeof(uint64_t)]; - size_t block_count = sizeof(DolphinData) / sizeof(uint64_t) + 1; - size_t offset = 0; - for(size_t i = 0; i < block_count; i++) { - for(size_t n = 0; n < sizeof(uint64_t); n++) { - if(offset < sizeof(DolphinData)) { - destination[n] = source[offset]; - } else { - destination[n] = 0; - } - offset++; - } - if(!api_hal_flash_write_dword( - DOLPHIN_DATA_DATA_ADDRESS + i * sizeof(uint64_t), *(uint64_t*)destination)) { - return false; - } - } + FURI_LOG_I("dolphin-state", "Saved"); return true; } bool dolphin_state_load(DolphinState* dolphin_state) { - const DolphinDataHeader* header = (const DolphinDataHeader*)DOLPHIN_DATA_HEADER_ADDRESS; - if(header->magic == DOLPHIN_DATA_HEADER_MAGIC && - header->version == DOLPHIN_DATA_HEADER_VERSION) { + DolphinStore store; + // Read Dolphin State Store + FURI_LOG_I("dolphin-state", "Loading state from internal-storage"); + int ret = internal_storage_read_key( + dolphin_state->internal_storage, DOLPHIN_STORE_KEY, (uint8_t*)&store, sizeof(DolphinStore)); + if(ret != sizeof(DolphinStore)) { + FURI_LOG_E("dolphin-state", "Load failed. Storage returned: %d", ret); + return false; + } + + FURI_LOG_I("dolphin-state", "State loaded, verifying header"); + if(store.header.magic == DOLPHIN_STORE_HEADER_MAGIC && + store.header.version == DOLPHIN_STORE_HEADER_VERSION) { + FURI_LOG_I( + "dolphin-state", + "Magic(%d) and Version(%d) match", + store.header.magic, + store.header.version); uint8_t checksum = 0; - const uint8_t* source = (const uint8_t*)DOLPHIN_DATA_DATA_ADDRESS; - for(size_t i = 0; i < sizeof(DolphinData); i++) { + const uint8_t* source = (const uint8_t*)&store.data; + for(size_t i = 0; i < sizeof(DolphinStoreData); i++) { checksum += source[i]; } - if(header->checksum == checksum) { - memcpy( - &dolphin_state->data, (const void*)DOLPHIN_DATA_DATA_ADDRESS, sizeof(DolphinData)); + if(store.header.checksum == checksum) { + FURI_LOG_I("dolphin-state", "Checksum(%d) match", store.header.checksum); + dolphin_state->data = store.data; return true; + } else { + FURI_LOG_E( + "dolphin-state", "Checksum(%d != %d) mismatch", store.header.checksum, checksum); } + } else { + FURI_LOG_E( + "dolphin-state", + "Magic(%d != %d) and Version(%d != %d) mismatch", + store.header.magic, + DOLPHIN_STORE_HEADER_MAGIC, + store.header.version, + DOLPHIN_STORE_HEADER_VERSION); } return false; } void dolphin_state_clear(DolphinState* dolphin_state) { - memset(&dolphin_state->data, 0, sizeof(DolphinData)); + memset(&dolphin_state->data, 0, sizeof(DolphinStoreData)); } void dolphin_state_on_deed(DolphinState* dolphin_state, DolphinDeed deed) { diff --git a/applications/dolphin/dolphin_state.h b/applications/dolphin/dolphin_state.h index 5157569a2..cbd9611a6 100644 --- a/applications/dolphin/dolphin_state.h +++ b/applications/dolphin/dolphin_state.h @@ -4,38 +4,6 @@ #include #include -#define DOLPHIN_DATA_PAGE 0xC0 -#define DOLPHIN_DATA_HEADER_ADDRESS 0x080C0000U -#define DOLPHIN_DATA_DATA_ADDRESS (DOLPHIN_DATA_HEADER_ADDRESS + sizeof(DolphinDataHeader)) - -#define DOLPHIN_DATA_HEADER_MAGIC 0xD0 -#define DOLPHIN_DATA_HEADER_VERSION 0x01 - -#define DOLPHIN_LVL_THRESHOLD 20.0f - -typedef struct { - uint8_t magic; - uint8_t version; - uint8_t checksum; - uint8_t flags; - uint32_t timestamp; -} DolphinDataHeader; - -typedef struct { - uint32_t limit_ibutton; - uint32_t limit_nfc; - uint32_t limit_ir; - uint32_t limit_rfid; - - uint32_t flags; - uint32_t icounter; - uint32_t butthurt; -} DolphinData; - -struct DolphinState { - DolphinData data; -}; - typedef struct DolphinState DolphinState; DolphinState* dolphin_state_alloc(); diff --git a/applications/dolphin/passport/passport.c b/applications/dolphin/passport/passport.c index 5d498eabf..caa4c102d 100644 --- a/applications/dolphin/passport/passport.c +++ b/applications/dolphin/passport/passport.c @@ -78,14 +78,14 @@ static void render_callback(Canvas* canvas, void* ctx) { } int32_t passport(void* p) { - DolphinState _state; + DolphinState* dolphin_state = dolphin_state_alloc(); ValueMutex state_mutex; - dolphin_state_load(&_state); + dolphin_state_load(dolphin_state); osMessageQueueId_t event_queue = osMessageQueueNew(2, sizeof(AppEvent), NULL); furi_check(event_queue); - if(!init_mutex(&state_mutex, &_state, sizeof(DolphinState))) { + if(!init_mutex(&state_mutex, dolphin_state, sizeof(DolphinState*))) { printf("[Passport] cannot create mutex\r\n"); return 0; } @@ -121,5 +121,7 @@ int32_t passport(void* p) { osMessageQueueDelete(event_queue); + dolphin_state_free(dolphin_state); + return 0; } diff --git a/applications/internal-storage/internal-storage-i.h b/applications/internal-storage/internal-storage-i.h new file mode 100644 index 000000000..c8b3ac8d5 --- /dev/null +++ b/applications/internal-storage/internal-storage-i.h @@ -0,0 +1,62 @@ +#pragma once + +#include "internal-storage.h" +#include +#include +#include + +#define INTERNAL_STORAGE_THREAD_FLAG_CALL_COMPLETE (1) + +struct InternalStorage { + osMessageQueueId_t queue; + InternalStorageState state; + const size_t start_address; + const size_t start_page; + struct lfs_config config; + lfs_t lfs; +}; + +typedef struct { + const char* key; + uint8_t* buffer; + size_t size; + int ret; +} InternalStorageCommandKey; + +typedef void (*InternalStorageCommandFunction)(InternalStorage* internal_storage, void* data); + +typedef struct { + osThreadId thread; + InternalStorageCommandFunction function; + void* data; +} InternalStorageCommand; + +int internal_storage_device_read( + const struct lfs_config* c, + lfs_block_t block, + lfs_off_t off, + void* buffer, + lfs_size_t size); + +int internal_storage_device_prog( + const struct lfs_config* c, + lfs_block_t block, + lfs_off_t off, + const void* buffer, + lfs_size_t size); + +int internal_storage_device_erase(const struct lfs_config* c, lfs_block_t block); + +int internal_storage_device_sync(const struct lfs_config* c); + +InternalStorage* internal_storage_alloc(); + +void internal_storage_free(InternalStorage* internal_storage); + +int32_t internal_storage_task(void* p); + +void _internal_storage_read_key(InternalStorage* internal_storage, InternalStorageCommandKey* data); + +void _internal_storage_write_key( + InternalStorage* internal_storage, + InternalStorageCommandKey* data); diff --git a/applications/internal-storage/internal-storage.c b/applications/internal-storage/internal-storage.c new file mode 100644 index 000000000..31c346478 --- /dev/null +++ b/applications/internal-storage/internal-storage.c @@ -0,0 +1,229 @@ +#include "internal-storage-i.h" + +int internal_storage_device_read( + const struct lfs_config* c, + lfs_block_t block, + lfs_off_t off, + void* buffer, + lfs_size_t size) { + InternalStorage* internal_storage = c->context; + size_t address = internal_storage->start_address + block * c->block_size + off; + + FURI_LOG_D( + "internal-storage", + "Device read: block %d, off %d, buffer: %p, size %d, translated address: %p", + block, + off, + buffer, + size, + address); + + memcpy(buffer, (void*)address, size); + + return 0; +} + +int internal_storage_device_prog( + const struct lfs_config* c, + lfs_block_t block, + lfs_off_t off, + const void* buffer, + lfs_size_t size) { + InternalStorage* internal_storage = c->context; + size_t address = internal_storage->start_address + block * c->block_size + off; + + FURI_LOG_D( + "internal-storage", + "Device prog: block %d, off %d, buffer: %p, size %d, translated address: %p", + block, + off, + buffer, + size, + address); + + int ret = 0; + while(size > 0) { + if(!api_hal_flash_write_dword(address, *(uint64_t*)buffer)) { + ret = -1; + break; + } + address += c->prog_size; + buffer += c->prog_size; + size -= c->prog_size; + } + + return ret; +} + +int internal_storage_device_erase(const struct lfs_config* c, lfs_block_t block) { + InternalStorage* internal_storage = c->context; + size_t page = internal_storage->start_page + block; + + FURI_LOG_D("internal-storage", "Device erase: page %d, translated page: %d", block, page); + + if(api_hal_flash_erase(page, 1)) { + return 0; + } else { + return -1; + } +} + +int internal_storage_device_sync(const struct lfs_config* c) { + FURI_LOG_D("internal-storage", "Device sync: skipping, cause "); + return 0; +} + +InternalStorage* internal_storage_alloc() { + InternalStorage* internal_storage = furi_alloc(sizeof(InternalStorage)); + + internal_storage->queue = osMessageQueueNew(8, sizeof(InternalStorageCommand), NULL); + + // Internal storage start address + internal_storage->state = InternalStorageStateInitializing; + + // Internal storage start address + *(size_t*)(&internal_storage->start_address) = api_hal_flash_get_free_page_start_address(); + *(size_t*)(&internal_storage->start_page) = + (internal_storage->start_address - api_hal_flash_get_base()) / + api_hal_flash_get_page_size(); + + // LFS configuration + // Glue and context + internal_storage->config.context = internal_storage; + internal_storage->config.read = internal_storage_device_read; + internal_storage->config.prog = internal_storage_device_prog; + internal_storage->config.erase = internal_storage_device_erase; + internal_storage->config.sync = internal_storage_device_sync; + // Block device description + internal_storage->config.read_size = api_hal_flash_get_read_block_size(); + internal_storage->config.prog_size = api_hal_flash_get_write_block_size(); + internal_storage->config.block_size = api_hal_flash_get_page_size(); + internal_storage->config.block_count = api_hal_flash_get_free_page_count(); + internal_storage->config.block_cycles = api_hal_flash_get_cycles_count(); + internal_storage->config.cache_size = 16; + internal_storage->config.lookahead_size = 16; + + return internal_storage; +} + +void internal_storage_free(InternalStorage* internal_storage) { + furi_assert(internal_storage); + free(internal_storage); +} + +int32_t internal_storage_task(void* p) { + FURI_LOG_I("internal-storage", "Starting"); + InternalStorage* internal_storage = internal_storage_alloc(); + FURI_LOG_I( + "internal-storage", + "Config: start %p, read %d, write %d, page size: %d, page count: %d, cycles: %d", + internal_storage->start_address, + internal_storage->config.read_size, + internal_storage->config.prog_size, + internal_storage->config.block_size, + internal_storage->config.block_count, + internal_storage->config.block_cycles); + + int err = lfs_mount(&internal_storage->lfs, &internal_storage->config); + if(err == 0) { + FURI_LOG_I("internal-storage", "Mounted"); + internal_storage->state = InternalStorageStateReady; + } else { + FURI_LOG_E("internal-storage", "Mount failed, formatting"); + err = lfs_format(&internal_storage->lfs, &internal_storage->config); + if(err == 0) { + FURI_LOG_I("internal-storage", "Format successful, trying to mount"); + err = lfs_mount(&internal_storage->lfs, &internal_storage->config); + if(err == 0) { + FURI_LOG_I("internal-storage", "Mounted"); + internal_storage->state = InternalStorageStateReady; + } else { + FURI_LOG_E("internal-storage", "Mount after format failed"); + internal_storage->state = InternalStorageStateBroken; + } + } else { + FURI_LOG_E("internal-storage", "Format failed"); + internal_storage->state = InternalStorageStateBroken; + } + } + + furi_record_create("internal-storage", internal_storage); + + InternalStorageCommand command; + while(1) { + furi_check( + osMessageQueueGet(internal_storage->queue, &command, NULL, osWaitForever) == osOK); + command.function(internal_storage, command.data); + osThreadFlagsSet(command.thread, INTERNAL_STORAGE_THREAD_FLAG_CALL_COMPLETE); + } + + lfs_unmount(&internal_storage->lfs); + internal_storage_free(internal_storage); + + return 0; +} + +void _internal_storage_read_key(InternalStorage* internal_storage, InternalStorageCommandKey* data) { + lfs_file_t file; + int ret = lfs_file_open(&internal_storage->lfs, &file, data->key, LFS_O_RDONLY); + if(ret == 0) { + ret = lfs_file_read(&internal_storage->lfs, &file, data->buffer, data->size); + lfs_file_close(&internal_storage->lfs, &file); + } + data->ret = ret; +} + +int internal_storage_read_key( + InternalStorage* internal_storage, + const char* key, + uint8_t* buffer, + size_t size) { + osThreadId_t caller_thread = osThreadGetId(); + if(caller_thread == 0) { + return -1; + } + + InternalStorageCommandKey data = {.key = key, .buffer = buffer, .size = size, .ret = 0}; + InternalStorageCommand command = { + .thread = caller_thread, + .function = (InternalStorageCommandFunction)_internal_storage_read_key, + .data = &data, + }; + furi_check(osMessageQueuePut(internal_storage->queue, &command, 0, osWaitForever) == osOK); + osThreadFlagsWait(INTERNAL_STORAGE_THREAD_FLAG_CALL_COMPLETE, osFlagsWaitAny, osWaitForever); + return data.ret; +} + +void _internal_storage_write_key( + InternalStorage* internal_storage, + InternalStorageCommandKey* data) { + lfs_file_t file; + int ret = lfs_file_open( + &internal_storage->lfs, &file, data->key, LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC); + if(ret == 0) { + ret = lfs_file_write(&internal_storage->lfs, &file, data->buffer, data->size); + lfs_file_close(&internal_storage->lfs, &file); + } + data->ret = ret; +} + +int internal_storage_write_key( + InternalStorage* internal_storage, + const char* key, + uint8_t* buffer, + size_t size) { + osThreadId_t caller_thread = osThreadGetId(); + if(caller_thread == 0) { + return -1; + } + + InternalStorageCommandKey data = {.key = key, .buffer = buffer, .size = size, .ret = 0}; + InternalStorageCommand command = { + .thread = caller_thread, + .function = (InternalStorageCommandFunction)_internal_storage_write_key, + .data = &data, + }; + furi_check(osMessageQueuePut(internal_storage->queue, &command, 0, osWaitForever) == osOK); + osThreadFlagsWait(INTERNAL_STORAGE_THREAD_FLAG_CALL_COMPLETE, osFlagsWaitAny, osWaitForever); + return data.ret; +} diff --git a/applications/internal-storage/internal-storage.h b/applications/internal-storage/internal-storage.h new file mode 100644 index 000000000..5d0a2018b --- /dev/null +++ b/applications/internal-storage/internal-storage.h @@ -0,0 +1,40 @@ +#pragma once + +#include +#include +#include + +/* Internal storage state */ +typedef enum { + InternalStorageStateInitializing, + InternalStorageStateReady, + InternalStorageStateBroken, +} InternalStorageState; + +typedef struct InternalStorage InternalStorage; + +/** Read key, blocking api + * @param internal_storage - InternalStorage instance + * @param key - file name to read data from + * @param buffer - pointer to data buffer + * @param size - buffer size + * @return negative on error, otherwise data read + */ +int internal_storage_read_key( + InternalStorage* internal_storage, + const char* key, + uint8_t* buffer, + size_t size); + +/** Write key, blocking api + * @param internal_storage - InternalStorage instance + * @param key - file name to store data to + * @param buffer - pointer to data buffer + * @param size - buffer size + * @return negative on error, otherwise data written + */ +int internal_storage_write_key( + InternalStorage* internal_storage, + const char* key, + uint8_t* buffer, + size_t size); diff --git a/core/furi/log.c b/core/furi/log.c index a35d602af..c1b5560d3 100644 --- a/core/furi/log.c +++ b/core/furi/log.c @@ -1,12 +1,14 @@ #include "log.h" #include #include "check.h" +#include typedef struct { FuriLogLevel log_level; FuriLogPrint print; FuriLogVPrint vprint; FuriLogTimestamp timetamp; + osMutexId_t mutex; } FuriLogParams; static FuriLogParams furi_log; @@ -17,14 +19,17 @@ void furi_log_init() { furi_log.print = printf; furi_log.vprint = vprintf; furi_log.timetamp = HAL_GetTick; + furi_log.mutex = osMutexNew(NULL); } void furi_log_print(FuriLogLevel level, const char* format, ...) { va_list args; va_start(args, format); if(level <= furi_log.log_level) { + osMutexAcquire(furi_log.mutex, osWaitForever); furi_log.print("%lu ", furi_log.timetamp()); furi_log.vprint(format, args); + osMutexRelease(furi_log.mutex); } va_end(args); } diff --git a/firmware/targets/f5/api-hal/api-hal-flash.c b/firmware/targets/f5/api-hal/api-hal-flash.c index 63f583d5b..97a9afa28 100644 --- a/firmware/targets/f5/api-hal/api-hal-flash.c +++ b/firmware/targets/f5/api-hal/api-hal-flash.c @@ -1,6 +1,59 @@ #include #include #include +#include + +/* Free flash space borders, exported by linker */ +extern const void __free_flash_start__; +extern const void __free_flash_end__; + +#define API_HAL_FLASH_READ_BLOCK 8 +#define API_HAL_FLASH_WRITE_BLOCK 8 +#define API_HAL_FLASH_PAGE_SIZE 4096 +#define API_HAL_FLASH_CYCLES_COUNT 10000 + +size_t api_hal_flash_get_base() { + return FLASH_BASE; +} + +size_t api_hal_flash_get_read_block_size() { + return API_HAL_FLASH_READ_BLOCK; +} + +size_t api_hal_flash_get_write_block_size() { + return API_HAL_FLASH_WRITE_BLOCK; +} + +size_t api_hal_flash_get_page_size() { + return API_HAL_FLASH_PAGE_SIZE; +} + +size_t api_hal_flash_get_cycles_count() { + return API_HAL_FLASH_CYCLES_COUNT; +} + +const void* api_hal_flash_get_free_start_address() { + return &__free_flash_start__; +} + +const void* api_hal_flash_get_free_end_address() { + return &__free_flash_end__; +} + +size_t api_hal_flash_get_free_page_start_address() { + size_t start = (size_t)api_hal_flash_get_free_start_address(); + size_t page_start = start - start % API_HAL_FLASH_PAGE_SIZE; + if (page_start != start) { + page_start += API_HAL_FLASH_PAGE_SIZE; + } + return page_start; +} + +size_t api_hal_flash_get_free_page_count() { + size_t end = (size_t)api_hal_flash_get_free_end_address(); + size_t page_start = (size_t)api_hal_flash_get_free_page_start_address(); + return (end-page_start) / API_HAL_FLASH_PAGE_SIZE; +} bool api_hal_flash_erase(uint8_t page, uint8_t count) { if (!api_hal_bt_lock_flash()) { @@ -25,7 +78,7 @@ bool api_hal_flash_write_dword(size_t address, uint64_t data) { return status == HAL_OK; } -bool api_hal_flash_write_row(size_t address, size_t source_address) { +bool api_hal_flash_write_dword_from(size_t address, size_t source_address) { if (!api_hal_bt_lock_flash()) { return false; } diff --git a/firmware/targets/f5/api-hal/api-hal-flash.h b/firmware/targets/f5/api-hal/api-hal-flash.h index 2b1553220..eb7270bef 100644 --- a/firmware/targets/f5/api-hal/api-hal-flash.h +++ b/firmware/targets/f5/api-hal/api-hal-flash.h @@ -4,6 +4,51 @@ #include #include +/** Get flash base address + * @return pointer to flash base + */ +size_t api_hal_flash_get_base(); + +/** Get flash read block size + * @return size in bytes + */ +size_t api_hal_flash_get_read_block_size(); + +/** Get flash write block size + * @return size in bytes + */ +size_t api_hal_flash_get_write_block_size(); + +/** Get flash page size + * @return size in bytes + */ +size_t api_hal_flash_get_page_size(); + +/** Get expected flash cycles count + * @return count of erase-write operations + */ +size_t api_hal_flash_get_cycles_count(); + +/** Get free flash start address + * @return pointer to free region start + */ +const void* api_hal_flash_get_free_start_address(); + +/** Get free flash end address + * @return pointer to free region end + */ +const void* api_hal_flash_get_free_end_address(); + +/** Get first free page start address + * @return first free page memory address + */ +size_t api_hal_flash_get_free_page_start_address(); + +/** Get free page count + * @return free page count + */ +size_t api_hal_flash_get_free_page_count(); + /* * Erase Flash * Locking operation, uses HSEM to manage shared access. @@ -21,9 +66,9 @@ bool api_hal_flash_erase(uint8_t page, uint8_t count); bool api_hal_flash_write_dword(size_t address, uint64_t data); /* - * Write page (4096 bytes or 64 rows of double words). + * Write double word (64 bits) from address * Locking operation, uses HSEM to manage shared access. - * @param address - destination address, must be page aligned + * @param address - destination address, must be block aligned * @param source_address - source address */ -bool api_hal_flash_write_page(size_t address, size_t source_address); +bool api_hal_flash_write_dword_from(size_t address, size_t source_address); diff --git a/firmware/targets/f5/ble-glue/tl_dbg_conf.h b/firmware/targets/f5/ble-glue/tl_dbg_conf.h index 62c30fb8e..8f705e619 100644 --- a/firmware/targets/f5/ble-glue/tl_dbg_conf.h +++ b/firmware/targets/f5/ble-glue/tl_dbg_conf.h @@ -50,7 +50,7 @@ extern UART_HandleTypeDef DEBUG_UART; #define TL_HCI_EVT_DBG_EN 0 /* Reports BLE Asynchronous Events received from CPU2 */ #define TL_HCI_EVT_DBG_RAW_EN 0 /* Reports raw data BLE Asynchronous Events received from CPU2 */ -#define TL_MM_DBG_EN 1 /* Reports the informations of the buffer released to CPU2 */ +#define TL_MM_DBG_EN 0 /* Reports the informations of the buffer released to CPU2 */ /** * Macro definition diff --git a/firmware/targets/f5/stm32wb55xx_flash_cm4_boot.ld b/firmware/targets/f5/stm32wb55xx_flash_cm4_boot.ld index 41882af64..6c5e92ef3 100644 --- a/firmware/targets/f5/stm32wb55xx_flash_cm4_boot.ld +++ b/firmware/targets/f5/stm32wb55xx_flash_cm4_boot.ld @@ -168,7 +168,13 @@ SECTIONS . = ALIGN(8); } >RAM1 - + /* Free Flash space, that can be used for internal storage */ + .free_flash(NOLOAD): + { + __free_flash_start__ = .; + . = ORIGIN(FLASH) + LENGTH(FLASH); + __free_flash_end__ = .; + } >FLASH /* Remove information from the standard libraries */ /DISCARD/ : diff --git a/firmware/targets/f5/stm32wb55xx_flash_cm4_no_boot.ld b/firmware/targets/f5/stm32wb55xx_flash_cm4_no_boot.ld index 8c31fce37..4cd7e899d 100644 --- a/firmware/targets/f5/stm32wb55xx_flash_cm4_no_boot.ld +++ b/firmware/targets/f5/stm32wb55xx_flash_cm4_no_boot.ld @@ -49,8 +49,8 @@ ENTRY(Reset_Handler) /* Highest address of the user mode stack */ _estack = 0x20030000; /* end of RAM */ /* Generate a link error if heap and stack don't fit into RAM */ -_Min_Heap_Size = 0x200; /* required amount of heap */ -_Min_Stack_Size = 0x400; /* required amount of stack */ +_Min_Heap_Size = 0x400; /* required amount of heap */ +_Min_Stack_Size = 0x1000; /* required amount of stack */ /* Specify the memory areas */ MEMORY @@ -168,7 +168,13 @@ SECTIONS . = ALIGN(8); } >RAM1 - + /* Free Flash space, that can be used for internal storage */ + .free_flash(NOLOAD): + { + __free_flash_start__ = .; + . = ORIGIN(FLASH) + LENGTH(FLASH); + __free_flash_end__ = .; + } >FLASH /* Remove information from the standard libraries */ /DISCARD/ : diff --git a/lib/lib.mk b/lib/lib.mk index 1704f2bfb..99f88b83d 100644 --- a/lib/lib.mk +++ b/lib/lib.mk @@ -36,6 +36,13 @@ C_SOURCES += $(FATFS_DIR)/ff_gen_drv.c C_SOURCES += $(FATFS_DIR)/diskio.c C_SOURCES += $(FATFS_DIR)/option/unicode.c +ifeq ($(SRV_INTERNAL_STORAGE), 1) +LITTLEFS_DIR = $(LIB_DIR)/littlefs +CFLAGS += -I$(LITTLEFS_DIR) +C_SOURCES += $(LITTLEFS_DIR)/lfs.c +C_SOURCES += $(LITTLEFS_DIR)/lfs_util.c +endif + ifeq ($(APP_NFC), 1) ST25RFAL002_DIR = $(LIB_DIR)/ST25RFAL002 CFLAGS += -I$(ST25RFAL002_DIR) diff --git a/lib/littlefs b/lib/littlefs new file mode 160000 index 000000000..1863dc788 --- /dev/null +++ b/lib/littlefs @@ -0,0 +1 @@ +Subproject commit 1863dc7883d82bd6ca79faa164b65341064d1c16