mirror of
https://github.com/DarkFlippers/unleashed-firmware
synced 2024-11-10 06:54:19 +00:00
Storage: remove LFS (#3577)
* Storage: drop internal storage * Storage: rollback some unnecessary changes * Storage: rollback some unnecessary changes part 2 * Storage: cleanup various defines and int handling. Ble: allow short connection interval if internal flash is not used. * Storage: do not return storage if it is not ready * Save PIN code to RTC, update settings * Simplify the code, clean up includes * Rearrange some code * apps: storage_move_to_sd: conditionally enable with --extra-define=STORAGE_INT_ON_LFS * Load Desktop settings automatically * Redirect /any to /ext * Abolish storage_move_to_sd app * Remove as many mentions of ANY_PATH as possible * Fix desktop settings wrongly not loading * Improve desktop settings handling and strings * Load BLE settings and keys automatically * Improve BLE configuration procedure * Do not load bluetooth keys twice if they were already loaded * Load dolphin state automatically * Fix merge artifact * Load notification settings automatically * Update desktop settings strings * Load expansion settings automatically * Do not use thread signals to reload desktop settings * Load region data automatically, separate to its own hook * Improve ble behaviour with no keys * Fix Dolphin state not resetting correctly * Add a status check * Make Desktop save its own settings * Check result when taking and releasing mutex * Improve default thread signal handling in FuriEventLoop * Make bt service in charge of saving settings, add settings api * Fix a deadlock due to timer thread not receiving time * Lock core2 when reinitialising bt * Update clang-format * Revert "Update clang-format" This reverts commit d61295ac063c6ec879375ceeab54d6ff2c90a9a1. * Format sources with clang-format * Revert old stack size for desktop settings * Allocate big struct dynamically * Simplify PIN comparison * Save pointer to storage in Desktop object * Fix region provisioning for hardware regions * Remove stale TODO + siimplify code * Clean up region.c * Use sizeof instead of macro define * Limit PIN length to 10 for consistency * Emit a warning upon usage of /any * Add delay after finding flipper * Remove unnecessary delay * Remove all mentions of STORAGE_INT_ON_LFS * Remove littlefs and internal storage * Remove all possible LittleFS mentions * Fix browser tab in Archive * Ble: fix connection interval explanation * Bump API Symbols * BLE: Update comments interval connection comments * Storage: clear FuriHalRtcFlagStorageFormatInternal if set --------- Co-authored-by: Georgii Surkov <georgii.surkov@outlook.com> Co-authored-by: hedger <hedger@nanode.su> Co-authored-by: Georgii Surkov <37121527+gsurkov@users.noreply.github.com>
This commit is contained in:
parent
6c6d64f1bd
commit
28272f7a7a
114 changed files with 1353 additions and 1985 deletions
3
.gitmodules
vendored
3
.gitmodules
vendored
|
@ -1,9 +1,6 @@
|
|||
[submodule "lib/mlib"]
|
||||
path = lib/mlib
|
||||
url = https://github.com/P-p-H-d/mlib.git
|
||||
[submodule "lib/littlefs"]
|
||||
path = lib/littlefs
|
||||
url = https://github.com/littlefs-project/littlefs.git
|
||||
[submodule "lib/nanopb"]
|
||||
path = lib/nanopb
|
||||
url = https://github.com/nanopb/nanopb.git
|
||||
|
|
|
@ -1 +1 @@
|
|||
--ignore-ccache -C gccarm --rules-config .pvsconfig -e lib/cmsis_core -e lib/fatfs -e lib/fnv1a-hash -e lib/FreeRTOS-Kernel -e lib/heatshrink -e lib/libusb_stm32 -e lib/littlefs -e lib/mbedtls -e lib/microtar -e lib/mlib -e lib/stm32wb_cmsis -e lib/stm32wb_copro -e lib/stm32wb_hal -e lib/u8g2 -e lib/nanopb -e lib/mjs -e */arm-none-eabi/*
|
||||
--ignore-ccache -C gccarm --rules-config .pvsconfig -e lib/cmsis_core -e lib/fatfs -e lib/fnv1a-hash -e lib/FreeRTOS-Kernel -e lib/heatshrink -e lib/libusb_stm32 -e lib/mbedtls -e lib/microtar -e lib/mlib -e lib/stm32wb_cmsis -e lib/stm32wb_copro -e lib/stm32wb_hal -e lib/u8g2 -e lib/nanopb -e lib/mjs -e */arm-none-eabi/*
|
||||
|
|
|
@ -19,7 +19,7 @@ bool file_browser_scene_start_on_event(void* context, SceneManagerEvent event) {
|
|||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
furi_string_set(app->file_path, ANY_PATH("badusb/demo_windows.txt"));
|
||||
furi_string_set(app->file_path, EXT_PATH("badusb/demo_windows.txt"));
|
||||
scene_manager_next_scene(app->scene_manager, FileBrowserSceneBrowser);
|
||||
consumed = true;
|
||||
} else if(event.type == SceneManagerEventTypeTick) {
|
||||
|
|
|
@ -30,8 +30,8 @@ bool archive_app_is_available(void* context, const char* path) {
|
|||
bool file_exists = false;
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
|
||||
if(storage_file_exists(storage, ANY_PATH("u2f/key.u2f"))) {
|
||||
file_exists = storage_file_exists(storage, ANY_PATH("u2f/cnt.u2f"));
|
||||
if(storage_file_exists(storage, EXT_PATH("u2f/key.u2f"))) {
|
||||
file_exists = storage_file_exists(storage, EXT_PATH("u2f/cnt.u2f"));
|
||||
}
|
||||
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
@ -68,8 +68,8 @@ void archive_app_delete_file(void* context, const char* path) {
|
|||
|
||||
if(app == ArchiveAppTypeU2f) {
|
||||
Storage* fs_api = furi_record_open(RECORD_STORAGE);
|
||||
res = (storage_common_remove(fs_api, ANY_PATH("u2f/key.u2f")) == FSE_OK);
|
||||
res |= (storage_common_remove(fs_api, ANY_PATH("u2f/cnt.u2f")) == FSE_OK);
|
||||
res = (storage_common_remove(fs_api, EXT_PATH("u2f/key.u2f")) == FSE_OK);
|
||||
res |= (storage_common_remove(fs_api, EXT_PATH("u2f/cnt.u2f")) == FSE_OK);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
||||
if(archive_is_favorite("/app:u2f/U2F Token")) {
|
||||
|
|
|
@ -450,16 +450,14 @@ void archive_favorites_move_mode(ArchiveBrowserView* browser, bool active) {
|
|||
}
|
||||
|
||||
static bool archive_is_dir_exists(FuriString* path) {
|
||||
if(furi_string_equal(path, STORAGE_ANY_PATH_PREFIX)) {
|
||||
return true;
|
||||
}
|
||||
bool state = false;
|
||||
FileInfo file_info;
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
if(storage_common_stat(storage, furi_string_get_cstr(path), &file_info) == FSE_OK) {
|
||||
if(file_info_is_dir(&file_info)) {
|
||||
state = true;
|
||||
}
|
||||
|
||||
if(furi_string_equal(path, STORAGE_EXT_PATH_PREFIX)) {
|
||||
state = storage_sd_status(storage) == FSE_OK;
|
||||
} else if(storage_common_stat(storage, furi_string_get_cstr(path), &file_info) == FSE_OK) {
|
||||
state = file_info_is_dir(&file_info);
|
||||
}
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
return state;
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
#include <storage/storage.h>
|
||||
|
||||
#define ARCHIVE_FAV_PATH ANY_PATH("favorites.txt")
|
||||
#define ARCHIVE_FAV_TEMP_PATH ANY_PATH("favorites.tmp")
|
||||
#define ARCHIVE_FAV_PATH EXT_PATH("favorites.txt")
|
||||
#define ARCHIVE_FAV_TEMP_PATH EXT_PATH("favorites.tmp")
|
||||
|
||||
uint16_t archive_favorites_count(void* context);
|
||||
bool archive_favorites_read(void* context);
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
#include "ibutton_custom_event.h"
|
||||
#include "scenes/ibutton_scene.h"
|
||||
|
||||
#define IBUTTON_APP_FOLDER ANY_PATH("ibutton")
|
||||
#define IBUTTON_APP_FOLDER EXT_PATH("ibutton")
|
||||
#define IBUTTON_APP_FILENAME_PREFIX "iBtn"
|
||||
#define IBUTTON_APP_FILENAME_EXTENSION ".ibtn"
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@
|
|||
#define INFRARED_MAX_BUTTON_NAME_LENGTH 22
|
||||
#define INFRARED_MAX_REMOTE_NAME_LENGTH 22
|
||||
|
||||
#define INFRARED_APP_FOLDER ANY_PATH("infrared")
|
||||
#define INFRARED_APP_FOLDER EXT_PATH("infrared")
|
||||
#define INFRARED_APP_EXTENSION ".ir"
|
||||
|
||||
#define INFRARED_DEFAULT_REMOTE_NAME "Remote"
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
#define LFRFID_KEY_NAME_SIZE 22
|
||||
#define LFRFID_TEXT_STORE_SIZE 40
|
||||
|
||||
#define LFRFID_APP_FOLDER ANY_PATH("lfrfid")
|
||||
#define LFRFID_APP_FOLDER EXT_PATH("lfrfid")
|
||||
#define LFRFID_SD_FOLDER EXT_PATH("lfrfid")
|
||||
#define LFRFID_APP_FILENAME_PREFIX "RFID"
|
||||
#define LFRFID_APP_FILENAME_EXTENSION ".rfid"
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
#include <nfc/protocols/mf_classic/mf_classic.h>
|
||||
#include <furi/furi.h>
|
||||
|
||||
#define NFC_APP_FOLDER ANY_PATH("nfc")
|
||||
#define NFC_APP_FOLDER EXT_PATH("nfc")
|
||||
#define NFC_APP_MF_CLASSIC_DICT_USER_PATH (NFC_APP_FOLDER "/assets/mf_classic_dict_user.nfc")
|
||||
|
||||
struct MfUserDict {
|
||||
|
|
|
@ -67,7 +67,7 @@
|
|||
#define NFC_TEXT_STORE_SIZE 128
|
||||
#define NFC_BYTE_INPUT_STORE_SIZE 10
|
||||
#define NFC_LOG_SIZE_MAX (1024)
|
||||
#define NFC_APP_FOLDER ANY_PATH("nfc")
|
||||
#define NFC_APP_FOLDER EXT_PATH("nfc")
|
||||
#define NFC_APP_EXTENSION ".nfc"
|
||||
#define NFC_APP_SHADOW_EXTENSION ".shd"
|
||||
#define NFC_APP_FILENAME_PREFIX "NFC"
|
||||
|
|
|
@ -592,7 +592,7 @@ void subghz_cli_command_tx_from_file(Cli* cli, FuriString* args, void* context)
|
|||
UNUSED(context);
|
||||
FuriString* file_name;
|
||||
file_name = furi_string_alloc();
|
||||
furi_string_set(file_name, ANY_PATH("subghz/test.sub"));
|
||||
furi_string_set(file_name, EXT_PATH("subghz/test.sub"));
|
||||
uint32_t repeat = 10;
|
||||
uint32_t device_ind = 0; // 0 - CC1101_INT, 1 - CC1101_EXT
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <furi_hal_random.h>
|
||||
#include <littlefs/lfs_util.h> // for lfs_tobe32
|
||||
|
||||
#include <mbedtls/sha256.h>
|
||||
#include <mbedtls/md.h>
|
||||
|
@ -319,6 +318,10 @@ static uint16_t u2f_register(U2fData* U2F, uint8_t* buf) {
|
|||
return sizeof(U2fRegisterResp) + cert_len + signature_len + 2;
|
||||
}
|
||||
|
||||
static inline uint32_t u2f_to_big_endian(uint32_t a) {
|
||||
return __builtin_bswap32(a);
|
||||
}
|
||||
|
||||
static uint16_t u2f_authenticate(U2fData* U2F, uint8_t* buf) {
|
||||
U2fAuthReq* req = (U2fAuthReq*)buf;
|
||||
U2fAuthResp* resp = (U2fAuthResp*)buf;
|
||||
|
@ -348,7 +351,7 @@ static uint16_t u2f_authenticate(U2fData* U2F, uint8_t* buf) {
|
|||
U2F->user_present = false;
|
||||
|
||||
// The 4 byte counter is represented in big endian. Increment it before use
|
||||
be_u2f_counter = lfs_tobe32(U2F->counter + 1);
|
||||
be_u2f_counter = u2f_to_big_endian(U2F->counter + 1);
|
||||
|
||||
// Generate hash
|
||||
{
|
||||
|
|
|
@ -61,6 +61,21 @@ static void bt_pin_code_view_port_input_callback(InputEvent* event, void* contex
|
|||
}
|
||||
}
|
||||
|
||||
static void bt_storage_callback(const void* message, void* context) {
|
||||
furi_assert(context);
|
||||
Bt* bt = context;
|
||||
const StorageEvent* event = message;
|
||||
|
||||
if(event->type == StorageEventTypeCardMount) {
|
||||
const BtMessage msg = {
|
||||
.type = BtMessageTypeReloadKeysSettings,
|
||||
};
|
||||
|
||||
furi_check(
|
||||
furi_message_queue_put(bt->message_queue, &msg, FuriWaitForever) == FuriStatusOk);
|
||||
}
|
||||
}
|
||||
|
||||
static ViewPort* bt_pin_code_view_port_alloc(Bt* bt) {
|
||||
ViewPort* view_port = view_port_alloc();
|
||||
view_port_draw_callback_set(view_port, bt_pin_code_view_port_draw_callback, bt);
|
||||
|
@ -143,8 +158,6 @@ Bt* bt_alloc(void) {
|
|||
// Init default maximum packet size
|
||||
bt->max_packet_size = BLE_PROFILE_SERIAL_PACKET_SIZE_MAX;
|
||||
bt->current_profile = NULL;
|
||||
// Load settings
|
||||
bt_settings_load(&bt->bt_settings);
|
||||
// Keys storage
|
||||
bt->keys_storage = bt_keys_storage_alloc(BT_KEYS_STORAGE_PATH);
|
||||
// Alloc queue
|
||||
|
@ -396,6 +409,8 @@ void bt_close_rpc_connection(Bt* bt) {
|
|||
|
||||
static void bt_change_profile(Bt* bt, BtMessage* message) {
|
||||
if(furi_hal_bt_is_gatt_gap_supported()) {
|
||||
bt_settings_load(&bt->bt_settings);
|
||||
|
||||
bt_close_rpc_connection(bt);
|
||||
|
||||
bt_keys_storage_load(bt->keys_storage);
|
||||
|
@ -439,6 +454,87 @@ static void bt_close_connection(Bt* bt, BtMessage* message) {
|
|||
if(message->lock) api_lock_unlock(message->lock);
|
||||
}
|
||||
|
||||
static void bt_apply_settings(Bt* bt) {
|
||||
if(bt->bt_settings.enabled) {
|
||||
furi_hal_bt_start_advertising();
|
||||
} else {
|
||||
furi_hal_bt_stop_advertising();
|
||||
}
|
||||
}
|
||||
|
||||
static void bt_load_keys(Bt* bt) {
|
||||
if(!furi_hal_bt_is_gatt_gap_supported()) {
|
||||
bt_show_warning(bt, "Unsupported radio stack");
|
||||
bt->status = BtStatusUnavailable;
|
||||
return;
|
||||
|
||||
} else if(bt_keys_storage_is_changed(bt->keys_storage)) {
|
||||
FURI_LOG_I(TAG, "Loading new keys");
|
||||
|
||||
bt_close_rpc_connection(bt);
|
||||
bt_keys_storage_load(bt->keys_storage);
|
||||
|
||||
bt->current_profile = NULL;
|
||||
|
||||
} else {
|
||||
FURI_LOG_I(TAG, "Keys unchanged");
|
||||
}
|
||||
}
|
||||
|
||||
static void bt_start_application(Bt* bt) {
|
||||
if(!bt->current_profile) {
|
||||
bt->current_profile =
|
||||
furi_hal_bt_change_app(ble_profile_serial, NULL, bt_on_gap_event_callback, bt);
|
||||
|
||||
if(!bt->current_profile) {
|
||||
FURI_LOG_E(TAG, "BLE App start failed");
|
||||
bt->status = BtStatusUnavailable;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void bt_load_settings(Bt* bt) {
|
||||
bt_settings_load(&bt->bt_settings);
|
||||
bt_apply_settings(bt);
|
||||
}
|
||||
|
||||
static void bt_handle_get_settings(Bt* bt, BtMessage* message) {
|
||||
furi_assert(message->lock);
|
||||
*message->data.settings = bt->bt_settings;
|
||||
api_lock_unlock(message->lock);
|
||||
}
|
||||
|
||||
static void bt_handle_set_settings(Bt* bt, BtMessage* message) {
|
||||
furi_assert(message->lock);
|
||||
bt->bt_settings = *message->data.csettings;
|
||||
|
||||
bt_apply_settings(bt);
|
||||
bt_settings_save(&bt->bt_settings);
|
||||
|
||||
api_lock_unlock(message->lock);
|
||||
}
|
||||
|
||||
static void bt_handle_reload_keys_settings(Bt* bt) {
|
||||
bt_load_keys(bt);
|
||||
bt_start_application(bt);
|
||||
bt_load_settings(bt);
|
||||
}
|
||||
|
||||
static void bt_init_keys_settings(Bt* bt) {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
furi_pubsub_subscribe(storage_get_pubsub(storage), bt_storage_callback, bt);
|
||||
|
||||
if(storage_sd_status(storage) != FSE_OK) {
|
||||
FURI_LOG_D(TAG, "SD Card not ready, skipping settings");
|
||||
|
||||
// Just start the BLE serial application without loading the keys or settings
|
||||
bt_start_application(bt);
|
||||
return;
|
||||
}
|
||||
|
||||
bt_handle_reload_keys_settings(bt);
|
||||
}
|
||||
|
||||
bool bt_remote_rssi(Bt* bt, uint8_t* rssi) {
|
||||
furi_assert(bt);
|
||||
|
||||
|
@ -465,35 +561,18 @@ int32_t bt_srv(void* p) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
// Load keys
|
||||
if(!bt_keys_storage_load(bt->keys_storage)) {
|
||||
FURI_LOG_W(TAG, "Failed to load bonding keys");
|
||||
}
|
||||
|
||||
// Start radio stack
|
||||
if(!furi_hal_bt_start_radio_stack()) {
|
||||
FURI_LOG_E(TAG, "Radio stack start failed");
|
||||
}
|
||||
|
||||
if(furi_hal_bt_is_gatt_gap_supported()) {
|
||||
bt->current_profile =
|
||||
furi_hal_bt_start_app(ble_profile_serial, NULL, bt_on_gap_event_callback, bt);
|
||||
if(!bt->current_profile) {
|
||||
FURI_LOG_E(TAG, "BLE App start failed");
|
||||
} else {
|
||||
if(bt->bt_settings.enabled) {
|
||||
furi_hal_bt_start_advertising();
|
||||
}
|
||||
if(furi_hal_bt_start_radio_stack()) {
|
||||
bt_init_keys_settings(bt);
|
||||
furi_hal_bt_set_key_storage_change_callback(bt_on_key_storage_change_callback, bt);
|
||||
}
|
||||
|
||||
} else {
|
||||
bt_show_warning(bt, "Unsupported radio stack");
|
||||
bt->status = BtStatusUnavailable;
|
||||
FURI_LOG_E(TAG, "Radio stack start failed");
|
||||
}
|
||||
|
||||
furi_record_create(RECORD_BT, bt);
|
||||
|
||||
BtMessage message;
|
||||
|
||||
while(1) {
|
||||
furi_check(
|
||||
furi_message_queue_get(bt->message_queue, &message, FuriWaitForever) == FuriStatusOk);
|
||||
|
@ -523,7 +602,14 @@ int32_t bt_srv(void* p) {
|
|||
bt_close_connection(bt, &message);
|
||||
} else if(message.type == BtMessageTypeForgetBondedDevices) {
|
||||
bt_keys_storage_delete(bt->keys_storage);
|
||||
} else if(message.type == BtMessageTypeGetSettings) {
|
||||
bt_handle_get_settings(bt, &message);
|
||||
} else if(message.type == BtMessageTypeSetSettings) {
|
||||
bt_handle_set_settings(bt, &message);
|
||||
} else if(message.type == BtMessageTypeReloadKeysSettings) {
|
||||
bt_handle_reload_keys_settings(bt);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -77,3 +77,39 @@ void bt_keys_storage_set_default_path(Bt* bt) {
|
|||
|
||||
bt_keys_storage_set_file_path(bt->keys_storage, BT_KEYS_STORAGE_PATH);
|
||||
}
|
||||
|
||||
/*
|
||||
* Private API for the Settings app
|
||||
*/
|
||||
|
||||
void bt_get_settings(Bt* bt, BtSettings* settings) {
|
||||
furi_assert(bt);
|
||||
furi_assert(settings);
|
||||
|
||||
BtMessage message = {
|
||||
.lock = api_lock_alloc_locked(),
|
||||
.type = BtMessageTypeGetSettings,
|
||||
.data.settings = settings,
|
||||
};
|
||||
|
||||
furi_check(
|
||||
furi_message_queue_put(bt->message_queue, &message, FuriWaitForever) == FuriStatusOk);
|
||||
|
||||
api_lock_wait_unlock_and_free(message.lock);
|
||||
}
|
||||
|
||||
void bt_set_settings(Bt* bt, const BtSettings* settings) {
|
||||
furi_assert(bt);
|
||||
furi_assert(settings);
|
||||
|
||||
BtMessage message = {
|
||||
.lock = api_lock_alloc_locked(),
|
||||
.type = BtMessageTypeSetSettings,
|
||||
.data.csettings = settings,
|
||||
};
|
||||
|
||||
furi_check(
|
||||
furi_message_queue_put(bt->message_queue, &message, FuriWaitForever) == FuriStatusOk);
|
||||
|
||||
api_lock_wait_unlock_and_free(message.lock);
|
||||
}
|
||||
|
|
|
@ -32,6 +32,9 @@ typedef enum {
|
|||
BtMessageTypeSetProfile,
|
||||
BtMessageTypeDisconnect,
|
||||
BtMessageTypeForgetBondedDevices,
|
||||
BtMessageTypeGetSettings,
|
||||
BtMessageTypeSetSettings,
|
||||
BtMessageTypeReloadKeysSettings,
|
||||
} BtMessageType;
|
||||
|
||||
typedef struct {
|
||||
|
@ -49,6 +52,8 @@ typedef union {
|
|||
} profile;
|
||||
FuriHalBleProfileParams profile_params;
|
||||
BtKeyStorageUpdateData key_storage_data;
|
||||
BtSettings* settings;
|
||||
const BtSettings* csettings;
|
||||
} BtMessageData;
|
||||
|
||||
typedef struct {
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
struct BtKeysStorage {
|
||||
uint8_t* nvm_sram_buff;
|
||||
uint16_t nvm_sram_buff_size;
|
||||
uint16_t current_size;
|
||||
FuriString* file_path;
|
||||
};
|
||||
|
||||
|
@ -66,44 +67,114 @@ void bt_keys_storage_set_ram_params(BtKeysStorage* instance, uint8_t* buff, uint
|
|||
instance->nvm_sram_buff_size = size;
|
||||
}
|
||||
|
||||
static bool bt_keys_storage_file_exists(const char* file_path) {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
FileInfo file_info;
|
||||
const bool ret = storage_common_stat(storage, file_path, &file_info) == FSE_OK &&
|
||||
file_info.size != 0;
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool bt_keys_storage_validate_file(const char* file_path, size_t* payload_size) {
|
||||
uint8_t magic, version;
|
||||
size_t size;
|
||||
|
||||
if(!saved_struct_get_metadata(file_path, &magic, &version, &size)) {
|
||||
FURI_LOG_E(TAG, "Failed to get metadata");
|
||||
return false;
|
||||
|
||||
} else if(magic != BT_KEYS_STORAGE_MAGIC || version != BT_KEYS_STORAGE_VERSION) {
|
||||
FURI_LOG_E(TAG, "File version mismatch");
|
||||
return false;
|
||||
}
|
||||
|
||||
*payload_size = size;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bt_keys_storage_is_changed(BtKeysStorage* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
bool is_changed = false;
|
||||
uint8_t* data_buffer = NULL;
|
||||
|
||||
do {
|
||||
const char* file_path = furi_string_get_cstr(instance->file_path);
|
||||
size_t payload_size;
|
||||
|
||||
if(!bt_keys_storage_file_exists(file_path)) {
|
||||
FURI_LOG_W(TAG, "Missing or empty file");
|
||||
break;
|
||||
|
||||
} else if(!bt_keys_storage_validate_file(file_path, &payload_size)) {
|
||||
FURI_LOG_E(TAG, "Invalid or corrupted file");
|
||||
break;
|
||||
}
|
||||
|
||||
data_buffer = malloc(payload_size);
|
||||
|
||||
const bool data_loaded = saved_struct_load(
|
||||
file_path, data_buffer, payload_size, BT_KEYS_STORAGE_MAGIC, BT_KEYS_STORAGE_VERSION);
|
||||
|
||||
if(!data_loaded) {
|
||||
FURI_LOG_E(TAG, "Failed to load file");
|
||||
break;
|
||||
|
||||
} else if(payload_size == instance->current_size) {
|
||||
furi_hal_bt_nvm_sram_sem_acquire();
|
||||
is_changed = memcmp(data_buffer, instance->nvm_sram_buff, payload_size);
|
||||
furi_hal_bt_nvm_sram_sem_release();
|
||||
|
||||
} else {
|
||||
FURI_LOG_D(TAG, "Size mismatch");
|
||||
is_changed = true;
|
||||
}
|
||||
} while(false);
|
||||
|
||||
if(data_buffer) {
|
||||
free(data_buffer);
|
||||
}
|
||||
|
||||
return is_changed;
|
||||
}
|
||||
|
||||
bool bt_keys_storage_load(BtKeysStorage* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
bool loaded = false;
|
||||
|
||||
do {
|
||||
const char* file_path = furi_string_get_cstr(instance->file_path);
|
||||
|
||||
// Get payload size
|
||||
uint8_t magic = 0, version = 0;
|
||||
size_t payload_size = 0;
|
||||
if(!saved_struct_get_metadata(
|
||||
furi_string_get_cstr(instance->file_path), &magic, &version, &payload_size)) {
|
||||
FURI_LOG_E(TAG, "Failed to read payload size");
|
||||
size_t payload_size;
|
||||
if(!bt_keys_storage_validate_file(file_path, &payload_size)) {
|
||||
FURI_LOG_E(TAG, "Invalid or corrupted file");
|
||||
break;
|
||||
}
|
||||
|
||||
if(magic != BT_KEYS_STORAGE_MAGIC || version != BT_KEYS_STORAGE_VERSION) {
|
||||
FURI_LOG_E(TAG, "Saved data version is mismatched");
|
||||
break;
|
||||
}
|
||||
|
||||
if(payload_size > instance->nvm_sram_buff_size) {
|
||||
FURI_LOG_E(TAG, "Saved data doesn't fit ram buffer");
|
||||
} else if(payload_size > instance->nvm_sram_buff_size) {
|
||||
FURI_LOG_E(TAG, "NVM RAM buffer overflow");
|
||||
break;
|
||||
}
|
||||
|
||||
// Load saved data to ram
|
||||
furi_hal_bt_nvm_sram_sem_acquire();
|
||||
bool data_loaded = saved_struct_load(
|
||||
furi_string_get_cstr(instance->file_path),
|
||||
const bool data_loaded = saved_struct_load(
|
||||
file_path,
|
||||
instance->nvm_sram_buff,
|
||||
payload_size,
|
||||
BT_KEYS_STORAGE_MAGIC,
|
||||
BT_KEYS_STORAGE_VERSION);
|
||||
furi_hal_bt_nvm_sram_sem_release();
|
||||
|
||||
if(!data_loaded) {
|
||||
FURI_LOG_E(TAG, "Failed to load struct");
|
||||
FURI_LOG_E(TAG, "Failed to load file");
|
||||
break;
|
||||
}
|
||||
|
||||
instance->current_size = payload_size;
|
||||
|
||||
loaded = true;
|
||||
} while(false);
|
||||
|
||||
|
@ -130,6 +201,8 @@ bool bt_keys_storage_update(BtKeysStorage* instance, uint8_t* start_addr, uint32
|
|||
break;
|
||||
}
|
||||
|
||||
instance->current_size = new_size;
|
||||
|
||||
furi_hal_bt_nvm_sram_sem_acquire();
|
||||
bool data_updated = saved_struct_save(
|
||||
furi_string_get_cstr(instance->file_path),
|
||||
|
@ -138,10 +211,12 @@ bool bt_keys_storage_update(BtKeysStorage* instance, uint8_t* start_addr, uint32
|
|||
BT_KEYS_STORAGE_MAGIC,
|
||||
BT_KEYS_STORAGE_VERSION);
|
||||
furi_hal_bt_nvm_sram_sem_release();
|
||||
|
||||
if(!data_updated) {
|
||||
FURI_LOG_E(TAG, "Failed to update key storage");
|
||||
break;
|
||||
}
|
||||
|
||||
updated = true;
|
||||
} while(false);
|
||||
|
||||
|
|
|
@ -17,6 +17,8 @@ void bt_keys_storage_set_file_path(BtKeysStorage* instance, const char* path);
|
|||
|
||||
void bt_keys_storage_set_ram_params(BtKeysStorage* instance, uint8_t* buff, uint16_t size);
|
||||
|
||||
bool bt_keys_storage_is_changed(BtKeysStorage* instance);
|
||||
|
||||
bool bt_keys_storage_load(BtKeysStorage* instance);
|
||||
|
||||
bool bt_keys_storage_update(BtKeysStorage* instance, uint8_t* start_addr, uint32_t size);
|
||||
|
|
8
applications/services/bt/bt_service/bt_settings_api_i.h
Normal file
8
applications/services/bt/bt_service/bt_settings_api_i.h
Normal file
|
@ -0,0 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
#include "bt.h"
|
||||
#include "../bt_settings.h"
|
||||
|
||||
void bt_get_settings(Bt* bt, BtSettings* settings);
|
||||
|
||||
void bt_set_settings(Bt* bt, const BtSettings* settings);
|
|
@ -1,23 +1,36 @@
|
|||
#include "bt_settings.h"
|
||||
#include "bt_settings_filename.h"
|
||||
|
||||
#include <furi.h>
|
||||
#include <lib/toolbox/saved_struct.h>
|
||||
#include <storage/storage.h>
|
||||
#include <toolbox/saved_struct.h>
|
||||
|
||||
#define TAG "BtSettings"
|
||||
|
||||
#define BT_SETTINGS_PATH INT_PATH(BT_SETTINGS_FILE_NAME)
|
||||
#define BT_SETTINGS_VERSION (0)
|
||||
#define BT_SETTINGS_MAGIC (0x19)
|
||||
|
||||
bool bt_settings_load(BtSettings* bt_settings) {
|
||||
void bt_settings_load(BtSettings* bt_settings) {
|
||||
furi_assert(bt_settings);
|
||||
|
||||
return saved_struct_load(
|
||||
const bool success = saved_struct_load(
|
||||
BT_SETTINGS_PATH, bt_settings, sizeof(BtSettings), BT_SETTINGS_MAGIC, BT_SETTINGS_VERSION);
|
||||
|
||||
if(!success) {
|
||||
FURI_LOG_W(TAG, "Failed to load settings, using defaults");
|
||||
memset(bt_settings, 0, sizeof(BtSettings));
|
||||
bt_settings_save(bt_settings);
|
||||
}
|
||||
}
|
||||
|
||||
bool bt_settings_save(const BtSettings* bt_settings) {
|
||||
void bt_settings_save(const BtSettings* bt_settings) {
|
||||
furi_assert(bt_settings);
|
||||
|
||||
return saved_struct_save(
|
||||
const bool success = saved_struct_save(
|
||||
BT_SETTINGS_PATH, bt_settings, sizeof(BtSettings), BT_SETTINGS_MAGIC, BT_SETTINGS_VERSION);
|
||||
|
||||
if(!success) {
|
||||
FURI_LOG_E(TAG, "Failed to save settings");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
#pragma once
|
||||
|
||||
#include "bt_settings_filename.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
@ -13,9 +10,9 @@ typedef struct {
|
|||
bool enabled;
|
||||
} BtSettings;
|
||||
|
||||
bool bt_settings_load(BtSettings* bt_settings);
|
||||
void bt_settings_load(BtSettings* bt_settings);
|
||||
|
||||
bool bt_settings_save(const BtSettings* bt_settings);
|
||||
void bt_settings_save(const BtSettings* bt_settings);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -97,9 +97,12 @@ void animation_manager_set_interact_callback(
|
|||
|
||||
void animation_manager_set_dummy_mode_state(AnimationManager* animation_manager, bool enabled) {
|
||||
furi_assert(animation_manager);
|
||||
// Prevent change of animations if mode is the same
|
||||
if(animation_manager->dummy_mode != enabled) {
|
||||
animation_manager->dummy_mode = enabled;
|
||||
animation_manager_start_new_idle(animation_manager);
|
||||
}
|
||||
}
|
||||
|
||||
static void animation_manager_check_blocking_callback(const void* message, void* context) {
|
||||
const StorageEvent* storage_event = message;
|
||||
|
|
|
@ -1,31 +1,24 @@
|
|||
#include <storage/storage.h>
|
||||
#include <assets_icons.h>
|
||||
#include <gui/gui.h>
|
||||
#include <gui/gui_i.h>
|
||||
#include <gui/view_stack.h>
|
||||
#include <notification/notification.h>
|
||||
#include <notification/notification_messages.h>
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include "desktop_i.h"
|
||||
|
||||
#include <cli/cli.h>
|
||||
#include <cli/cli_vcp.h>
|
||||
#include <locale/locale.h>
|
||||
|
||||
#include "animations/animation_manager.h"
|
||||
#include "desktop/scenes/desktop_scene.h"
|
||||
#include "desktop/scenes/desktop_scene_i.h"
|
||||
#include "desktop/views/desktop_view_locked.h"
|
||||
#include "desktop/views/desktop_view_pin_input.h"
|
||||
#include "desktop/views/desktop_view_pin_timeout.h"
|
||||
#include "desktop_i.h"
|
||||
#include "helpers/pin.h"
|
||||
#include "helpers/slideshow_filename.h"
|
||||
#include <gui/gui_i.h>
|
||||
|
||||
#include <locale/locale.h>
|
||||
#include <storage/storage.h>
|
||||
|
||||
#include <assets_icons.h>
|
||||
|
||||
#include "scenes/desktop_scene.h"
|
||||
#include "scenes/desktop_scene_locked.h"
|
||||
|
||||
#define TAG "Desktop"
|
||||
|
||||
static void desktop_auto_lock_arm(Desktop*);
|
||||
static void desktop_auto_lock_inhibit(Desktop*);
|
||||
static void desktop_start_auto_lock_timer(Desktop*);
|
||||
static void desktop_apply_settings(Desktop*);
|
||||
|
||||
static void desktop_loader_callback(const void* message, void* context) {
|
||||
furi_assert(context);
|
||||
|
@ -42,6 +35,16 @@ static void desktop_loader_callback(const void* message, void* context) {
|
|||
}
|
||||
}
|
||||
|
||||
static void desktop_storage_callback(const void* message, void* context) {
|
||||
furi_assert(context);
|
||||
Desktop* desktop = context;
|
||||
const StorageEvent* event = message;
|
||||
|
||||
if(event->type == StorageEventTypeCardMount) {
|
||||
view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopGlobalReloadSettings);
|
||||
}
|
||||
}
|
||||
|
||||
static void desktop_lock_icon_draw_callback(Canvas* canvas, void* context) {
|
||||
UNUSED(context);
|
||||
furi_assert(canvas);
|
||||
|
@ -122,31 +125,39 @@ static bool desktop_custom_event_callback(void* context, uint32_t event) {
|
|||
furi_assert(context);
|
||||
Desktop* desktop = (Desktop*)context;
|
||||
|
||||
switch(event) {
|
||||
case DesktopGlobalBeforeAppStarted:
|
||||
if(event == DesktopGlobalBeforeAppStarted) {
|
||||
if(animation_manager_is_animation_loaded(desktop->animation_manager)) {
|
||||
animation_manager_unload_and_stall_animation(desktop->animation_manager);
|
||||
}
|
||||
desktop_auto_lock_inhibit(desktop);
|
||||
furi_semaphore_release(desktop->animation_semaphore);
|
||||
return true;
|
||||
case DesktopGlobalAfterAppFinished:
|
||||
animation_manager_load_and_continue_animation(desktop->animation_manager);
|
||||
DESKTOP_SETTINGS_LOAD(&desktop->settings);
|
||||
|
||||
desktop_clock_reconfigure(desktop);
|
||||
if(!furi_hal_rtc_is_flag_set(FuriHalRtcFlagLock)) {
|
||||
desktop_auto_lock_inhibit(desktop);
|
||||
desktop->app_running = true;
|
||||
|
||||
furi_semaphore_release(desktop->animation_semaphore);
|
||||
|
||||
} else if(event == DesktopGlobalAfterAppFinished) {
|
||||
animation_manager_load_and_continue_animation(desktop->animation_manager);
|
||||
desktop_auto_lock_arm(desktop);
|
||||
}
|
||||
return true;
|
||||
case DesktopGlobalAutoLock:
|
||||
if(!loader_is_locked(desktop->loader) && !desktop->locked) {
|
||||
desktop->app_running = false;
|
||||
|
||||
} else if(event == DesktopGlobalAutoLock) {
|
||||
if(!desktop->app_running && !desktop->locked) {
|
||||
desktop_lock(desktop);
|
||||
}
|
||||
return true;
|
||||
|
||||
} else if(event == DesktopGlobalSaveSettings) {
|
||||
desktop_settings_save(&desktop->settings);
|
||||
desktop_apply_settings(desktop);
|
||||
|
||||
} else if(event == DesktopGlobalReloadSettings) {
|
||||
desktop_settings_load(&desktop->settings);
|
||||
desktop_apply_settings(desktop);
|
||||
|
||||
} else {
|
||||
return scene_manager_handle_custom_event(desktop->scene_manager, event);
|
||||
}
|
||||
|
||||
return scene_manager_handle_custom_event(desktop->scene_manager, event);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool desktop_back_event_callback(void* context) {
|
||||
|
@ -206,84 +217,45 @@ static void desktop_clock_timer_callback(void* context) {
|
|||
furi_assert(context);
|
||||
Desktop* desktop = context;
|
||||
|
||||
if(gui_active_view_port_count(desktop->gui, GuiLayerStatusBarLeft) < 6) {
|
||||
const bool clock_enabled = gui_active_view_port_count(desktop->gui, GuiLayerStatusBarLeft) < 6;
|
||||
|
||||
if(clock_enabled) {
|
||||
desktop_clock_update(desktop);
|
||||
|
||||
view_port_enabled_set(desktop->clock_viewport, true);
|
||||
} else {
|
||||
view_port_enabled_set(desktop->clock_viewport, false);
|
||||
}
|
||||
}
|
||||
|
||||
void desktop_lock(Desktop* desktop) {
|
||||
furi_assert(!desktop->locked);
|
||||
|
||||
furi_hal_rtc_set_flag(FuriHalRtcFlagLock);
|
||||
|
||||
if(desktop->settings.pin_code.length) {
|
||||
Cli* cli = furi_record_open(RECORD_CLI);
|
||||
cli_session_close(cli);
|
||||
furi_record_close(RECORD_CLI);
|
||||
view_port_enabled_set(desktop->clock_viewport, clock_enabled);
|
||||
}
|
||||
|
||||
desktop_auto_lock_inhibit(desktop);
|
||||
scene_manager_set_scene_state(
|
||||
desktop->scene_manager, DesktopSceneLocked, SCENE_LOCKED_FIRST_ENTER);
|
||||
scene_manager_next_scene(desktop->scene_manager, DesktopSceneLocked);
|
||||
static void desktop_apply_settings(Desktop* desktop) {
|
||||
desktop->in_transition = true;
|
||||
|
||||
DesktopStatus status = {.locked = true};
|
||||
furi_pubsub_publish(desktop->status_pubsub, &status);
|
||||
desktop_clock_reconfigure(desktop);
|
||||
|
||||
desktop->locked = true;
|
||||
}
|
||||
view_port_enabled_set(desktop->dummy_mode_icon_viewport, desktop->settings.dummy_mode);
|
||||
desktop_main_set_dummy_mode_state(desktop->main_view, desktop->settings.dummy_mode);
|
||||
animation_manager_set_dummy_mode_state(
|
||||
desktop->animation_manager, desktop->settings.dummy_mode);
|
||||
|
||||
void desktop_unlock(Desktop* desktop) {
|
||||
furi_assert(desktop->locked);
|
||||
|
||||
view_port_enabled_set(desktop->lock_icon_viewport, false);
|
||||
Gui* gui = furi_record_open(RECORD_GUI);
|
||||
gui_set_lockdown(gui, false);
|
||||
furi_record_close(RECORD_GUI);
|
||||
desktop_view_locked_unlock(desktop->locked_view);
|
||||
scene_manager_search_and_switch_to_previous_scene(desktop->scene_manager, DesktopSceneMain);
|
||||
if(!desktop->app_running && !desktop->locked) {
|
||||
desktop_auto_lock_arm(desktop);
|
||||
furi_hal_rtc_reset_flag(FuriHalRtcFlagLock);
|
||||
furi_hal_rtc_set_pin_fails(0);
|
||||
|
||||
if(desktop->settings.pin_code.length) {
|
||||
Cli* cli = furi_record_open(RECORD_CLI);
|
||||
cli_session_open(cli, &cli_vcp);
|
||||
furi_record_close(RECORD_CLI);
|
||||
}
|
||||
|
||||
DesktopStatus status = {.locked = false};
|
||||
furi_pubsub_publish(desktop->status_pubsub, &status);
|
||||
|
||||
desktop->locked = false;
|
||||
}
|
||||
|
||||
void desktop_set_dummy_mode_state(Desktop* desktop, bool enabled) {
|
||||
desktop->in_transition = true;
|
||||
view_port_enabled_set(desktop->dummy_mode_icon_viewport, enabled);
|
||||
desktop_main_set_dummy_mode_state(desktop->main_view, enabled);
|
||||
animation_manager_set_dummy_mode_state(desktop->animation_manager, enabled);
|
||||
desktop->settings.dummy_mode = enabled;
|
||||
DESKTOP_SETTINGS_SAVE(&desktop->settings);
|
||||
desktop->in_transition = false;
|
||||
}
|
||||
|
||||
void desktop_set_stealth_mode_state(Desktop* desktop, bool enabled) {
|
||||
desktop->in_transition = true;
|
||||
if(enabled) {
|
||||
furi_hal_rtc_set_flag(FuriHalRtcFlagStealthMode);
|
||||
} else {
|
||||
furi_hal_rtc_reset_flag(FuriHalRtcFlagStealthMode);
|
||||
}
|
||||
view_port_enabled_set(desktop->stealth_mode_icon_viewport, enabled);
|
||||
desktop->in_transition = false;
|
||||
static void desktop_init_settings(Desktop* desktop) {
|
||||
furi_pubsub_subscribe(storage_get_pubsub(desktop->storage), desktop_storage_callback, desktop);
|
||||
|
||||
if(storage_sd_status(desktop->storage) != FSE_OK) {
|
||||
FURI_LOG_D(TAG, "SD Card not ready, skipping settings");
|
||||
return;
|
||||
}
|
||||
|
||||
Desktop* desktop_alloc(void) {
|
||||
desktop_settings_load(&desktop->settings);
|
||||
desktop_apply_settings(desktop);
|
||||
}
|
||||
|
||||
static Desktop* desktop_alloc(void) {
|
||||
Desktop* desktop = malloc(sizeof(Desktop));
|
||||
|
||||
desktop->animation_semaphore = furi_semaphore_alloc(1, 0);
|
||||
|
@ -392,14 +364,13 @@ Desktop* desktop_alloc(void) {
|
|||
}
|
||||
gui_add_view_port(desktop->gui, desktop->stealth_mode_icon_viewport, GuiLayerStatusBarLeft);
|
||||
|
||||
// Unload animations before starting an application
|
||||
desktop->loader = furi_record_open(RECORD_LOADER);
|
||||
furi_pubsub_subscribe(loader_get_pubsub(desktop->loader), desktop_loader_callback, desktop);
|
||||
|
||||
desktop->storage = furi_record_open(RECORD_STORAGE);
|
||||
desktop->notification = furi_record_open(RECORD_NOTIFICATION);
|
||||
desktop->app_start_stop_subscription = furi_pubsub_subscribe(
|
||||
loader_get_pubsub(desktop->loader), desktop_loader_callback, desktop);
|
||||
|
||||
desktop->input_events_pubsub = furi_record_open(RECORD_INPUT_EVENTS);
|
||||
desktop->input_events_subscription = NULL;
|
||||
|
||||
desktop->auto_lock_timer =
|
||||
furi_timer_alloc(desktop_auto_lock_timer_callback, FuriTimerTypeOnce, desktop);
|
||||
|
@ -409,19 +380,95 @@ Desktop* desktop_alloc(void) {
|
|||
desktop->update_clock_timer =
|
||||
furi_timer_alloc(desktop_clock_timer_callback, FuriTimerTypePeriodic, desktop);
|
||||
|
||||
desktop->app_running = loader_is_locked(desktop->loader);
|
||||
|
||||
furi_record_create(RECORD_DESKTOP, desktop);
|
||||
|
||||
return desktop;
|
||||
}
|
||||
|
||||
static bool desktop_check_file_flag(const char* flag_path) {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
bool exists = storage_common_stat(storage, flag_path, NULL) == FSE_OK;
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
/*
|
||||
* Private API
|
||||
*/
|
||||
|
||||
return exists;
|
||||
void desktop_lock(Desktop* desktop) {
|
||||
furi_assert(!desktop->locked);
|
||||
|
||||
furi_hal_rtc_set_flag(FuriHalRtcFlagLock);
|
||||
|
||||
if(desktop_pin_code_is_set()) {
|
||||
Cli* cli = furi_record_open(RECORD_CLI);
|
||||
cli_session_close(cli);
|
||||
furi_record_close(RECORD_CLI);
|
||||
}
|
||||
|
||||
desktop_auto_lock_inhibit(desktop);
|
||||
scene_manager_set_scene_state(
|
||||
desktop->scene_manager, DesktopSceneLocked, DesktopSceneLockedStateFirstEnter);
|
||||
scene_manager_next_scene(desktop->scene_manager, DesktopSceneLocked);
|
||||
|
||||
DesktopStatus status = {.locked = true};
|
||||
furi_pubsub_publish(desktop->status_pubsub, &status);
|
||||
|
||||
desktop->locked = true;
|
||||
}
|
||||
|
||||
void desktop_unlock(Desktop* desktop) {
|
||||
furi_assert(desktop->locked);
|
||||
|
||||
view_port_enabled_set(desktop->lock_icon_viewport, false);
|
||||
Gui* gui = furi_record_open(RECORD_GUI);
|
||||
gui_set_lockdown(gui, false);
|
||||
furi_record_close(RECORD_GUI);
|
||||
desktop_view_locked_unlock(desktop->locked_view);
|
||||
scene_manager_search_and_switch_to_previous_scene(desktop->scene_manager, DesktopSceneMain);
|
||||
desktop_auto_lock_arm(desktop);
|
||||
furi_hal_rtc_reset_flag(FuriHalRtcFlagLock);
|
||||
furi_hal_rtc_set_pin_fails(0);
|
||||
|
||||
if(desktop_pin_code_is_set()) {
|
||||
Cli* cli = furi_record_open(RECORD_CLI);
|
||||
cli_session_open(cli, &cli_vcp);
|
||||
furi_record_close(RECORD_CLI);
|
||||
}
|
||||
|
||||
DesktopStatus status = {.locked = false};
|
||||
furi_pubsub_publish(desktop->status_pubsub, &status);
|
||||
|
||||
desktop->locked = false;
|
||||
}
|
||||
|
||||
void desktop_set_dummy_mode_state(Desktop* desktop, bool enabled) {
|
||||
desktop->in_transition = true;
|
||||
|
||||
view_port_enabled_set(desktop->dummy_mode_icon_viewport, enabled);
|
||||
desktop_main_set_dummy_mode_state(desktop->main_view, enabled);
|
||||
animation_manager_set_dummy_mode_state(desktop->animation_manager, enabled);
|
||||
desktop->settings.dummy_mode = enabled;
|
||||
|
||||
desktop->in_transition = false;
|
||||
|
||||
desktop_settings_save(&desktop->settings);
|
||||
}
|
||||
|
||||
void desktop_set_stealth_mode_state(Desktop* desktop, bool enabled) {
|
||||
desktop->in_transition = true;
|
||||
|
||||
if(enabled) {
|
||||
furi_hal_rtc_set_flag(FuriHalRtcFlagStealthMode);
|
||||
} else {
|
||||
furi_hal_rtc_reset_flag(FuriHalRtcFlagStealthMode);
|
||||
}
|
||||
|
||||
view_port_enabled_set(desktop->stealth_mode_icon_viewport, enabled);
|
||||
|
||||
desktop->in_transition = false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Public API
|
||||
*/
|
||||
|
||||
bool desktop_api_is_locked(Desktop* instance) {
|
||||
furi_assert(instance);
|
||||
return furi_hal_rtc_is_flag_set(FuriHalRtcFlagLock);
|
||||
|
@ -437,6 +484,30 @@ FuriPubSub* desktop_api_get_status_pubsub(Desktop* instance) {
|
|||
return instance->status_pubsub;
|
||||
}
|
||||
|
||||
void desktop_api_reload_settings(Desktop* instance) {
|
||||
furi_assert(instance);
|
||||
view_dispatcher_send_custom_event(instance->view_dispatcher, DesktopGlobalReloadSettings);
|
||||
}
|
||||
|
||||
void desktop_api_get_settings(Desktop* instance, DesktopSettings* settings) {
|
||||
furi_assert(instance);
|
||||
furi_assert(settings);
|
||||
|
||||
*settings = instance->settings;
|
||||
}
|
||||
|
||||
void desktop_api_set_settings(Desktop* instance, const DesktopSettings* settings) {
|
||||
furi_assert(instance);
|
||||
furi_assert(settings);
|
||||
|
||||
instance->settings = *settings;
|
||||
view_dispatcher_send_custom_event(instance->view_dispatcher, DesktopGlobalSaveSettings);
|
||||
}
|
||||
|
||||
/*
|
||||
* Application thread
|
||||
*/
|
||||
|
||||
int32_t desktop_srv(void* p) {
|
||||
UNUSED(p);
|
||||
|
||||
|
@ -449,31 +520,15 @@ int32_t desktop_srv(void* p) {
|
|||
|
||||
Desktop* desktop = desktop_alloc();
|
||||
|
||||
bool loaded = DESKTOP_SETTINGS_LOAD(&desktop->settings);
|
||||
if(!loaded) {
|
||||
memset(&desktop->settings, 0, sizeof(desktop->settings));
|
||||
DESKTOP_SETTINGS_SAVE(&desktop->settings);
|
||||
}
|
||||
|
||||
view_port_enabled_set(desktop->dummy_mode_icon_viewport, desktop->settings.dummy_mode);
|
||||
|
||||
desktop_clock_reconfigure(desktop);
|
||||
|
||||
desktop_main_set_dummy_mode_state(desktop->main_view, desktop->settings.dummy_mode);
|
||||
animation_manager_set_dummy_mode_state(
|
||||
desktop->animation_manager, desktop->settings.dummy_mode);
|
||||
desktop_init_settings(desktop);
|
||||
|
||||
scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain);
|
||||
|
||||
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagLock)) {
|
||||
desktop_lock(desktop);
|
||||
} else {
|
||||
if(!loader_is_locked(desktop->loader)) {
|
||||
desktop_auto_lock_arm(desktop);
|
||||
}
|
||||
}
|
||||
|
||||
if(desktop_check_file_flag(SLIDESHOW_FS_PATH)) {
|
||||
if(storage_file_exists(desktop->storage, SLIDESHOW_FS_PATH)) {
|
||||
scene_manager_next_scene(desktop->scene_manager, DesktopSceneSlideshow);
|
||||
}
|
||||
|
||||
|
@ -497,14 +552,12 @@ int32_t desktop_srv(void* p) {
|
|||
}
|
||||
|
||||
// Special case: autostart application is already running
|
||||
if(loader_is_locked(desktop->loader) &&
|
||||
animation_manager_is_animation_loaded(desktop->animation_manager)) {
|
||||
if(desktop->app_running && animation_manager_is_animation_loaded(desktop->animation_manager)) {
|
||||
animation_manager_unload_and_stall_animation(desktop->animation_manager);
|
||||
}
|
||||
|
||||
view_dispatcher_run(desktop->view_dispatcher);
|
||||
|
||||
furi_crash("That was unexpected");
|
||||
|
||||
// Should never get here (a service thread will crash automatically if it returns)
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -2,16 +2,22 @@
|
|||
|
||||
#include <furi.h>
|
||||
|
||||
typedef struct Desktop Desktop;
|
||||
#include "desktop_settings.h"
|
||||
|
||||
#define RECORD_DESKTOP "desktop"
|
||||
|
||||
bool desktop_api_is_locked(Desktop* instance);
|
||||
|
||||
void desktop_api_unlock(Desktop* instance);
|
||||
typedef struct Desktop Desktop;
|
||||
|
||||
typedef struct {
|
||||
bool locked;
|
||||
} DesktopStatus;
|
||||
|
||||
bool desktop_api_is_locked(Desktop* instance);
|
||||
|
||||
void desktop_api_unlock(Desktop* instance);
|
||||
|
||||
FuriPubSub* desktop_api_get_status_pubsub(Desktop* instance);
|
||||
|
||||
void desktop_api_get_settings(Desktop* instance, DesktopSettings* settings);
|
||||
|
||||
void desktop_api_set_settings(Desktop* instance, const DesktopSettings* settings);
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
#include "desktop.h"
|
||||
#include "desktop_settings.h"
|
||||
|
||||
#include "animations/animation_manager.h"
|
||||
#include "views/desktop_view_pin_timeout.h"
|
||||
#include "views/desktop_view_pin_input.h"
|
||||
|
@ -9,9 +11,7 @@
|
|||
#include "views/desktop_view_lock_menu.h"
|
||||
#include "views/desktop_view_debug.h"
|
||||
#include "views/desktop_view_slideshow.h"
|
||||
#include <desktop/desktop_settings.h>
|
||||
|
||||
#include <furi.h>
|
||||
#include <gui/gui.h>
|
||||
#include <gui/view_stack.h>
|
||||
#include <gui/view_dispatcher.h>
|
||||
|
@ -42,9 +42,8 @@ typedef struct {
|
|||
} DesktopClock;
|
||||
|
||||
struct Desktop {
|
||||
// Scene
|
||||
FuriThread* scene_thread;
|
||||
// GUI
|
||||
|
||||
Gui* gui;
|
||||
ViewDispatcher* view_dispatcher;
|
||||
SceneManager* scene_manager;
|
||||
|
@ -56,42 +55,38 @@ struct Desktop {
|
|||
DesktopMainView* main_view;
|
||||
DesktopViewPinTimeout* pin_timeout_view;
|
||||
DesktopSlideshowView* slideshow_view;
|
||||
DesktopViewPinInput* pin_input_view;
|
||||
|
||||
ViewStack* main_view_stack;
|
||||
ViewStack* locked_view_stack;
|
||||
|
||||
DesktopSettings settings;
|
||||
DesktopViewPinInput* pin_input_view;
|
||||
|
||||
ViewPort* lock_icon_viewport;
|
||||
ViewPort* dummy_mode_icon_viewport;
|
||||
ViewPort* clock_viewport;
|
||||
ViewPort* stealth_mode_icon_viewport;
|
||||
|
||||
AnimationManager* animation_manager;
|
||||
|
||||
Loader* loader;
|
||||
Storage* storage;
|
||||
NotificationApp* notification;
|
||||
|
||||
FuriPubSubSubscription* app_start_stop_subscription;
|
||||
FuriPubSub* status_pubsub;
|
||||
FuriPubSub* input_events_pubsub;
|
||||
FuriPubSubSubscription* input_events_subscription;
|
||||
|
||||
FuriTimer* auto_lock_timer;
|
||||
FuriTimer* update_clock_timer;
|
||||
|
||||
FuriPubSub* status_pubsub;
|
||||
AnimationManager* animation_manager;
|
||||
FuriSemaphore* animation_semaphore;
|
||||
|
||||
DesktopClock clock;
|
||||
DesktopSettings settings;
|
||||
|
||||
bool in_transition : 1;
|
||||
bool locked : 1;
|
||||
|
||||
FuriSemaphore* animation_semaphore;
|
||||
bool in_transition;
|
||||
bool app_running;
|
||||
bool locked;
|
||||
};
|
||||
|
||||
Desktop* desktop_alloc(void);
|
||||
|
||||
void desktop_free(Desktop* desktop);
|
||||
void desktop_lock(Desktop* desktop);
|
||||
void desktop_unlock(Desktop* desktop);
|
||||
void desktop_set_dummy_mode_state(Desktop* desktop, bool enabled);
|
||||
|
|
79
applications/services/desktop/desktop_settings.c
Normal file
79
applications/services/desktop/desktop_settings.c
Normal file
|
@ -0,0 +1,79 @@
|
|||
#include "desktop_settings.h"
|
||||
#include "desktop_settings_filename.h"
|
||||
|
||||
#include <saved_struct.h>
|
||||
#include <storage/storage.h>
|
||||
|
||||
#define TAG "DesktopSettings"
|
||||
|
||||
#define DESKTOP_SETTINGS_VER_10 (10)
|
||||
#define DESKTOP_SETTINGS_VER (11)
|
||||
|
||||
#define DESKTOP_SETTINGS_PATH INT_PATH(DESKTOP_SETTINGS_FILE_NAME)
|
||||
#define DESKTOP_SETTINGS_MAGIC (0x17)
|
||||
|
||||
typedef struct {
|
||||
uint8_t reserved[11];
|
||||
DesktopSettings settings;
|
||||
} DesktopSettingsV10;
|
||||
|
||||
// Actual size of DesktopSettings v10
|
||||
static_assert(sizeof(DesktopSettingsV10) == 1044);
|
||||
|
||||
void desktop_settings_load(DesktopSettings* settings) {
|
||||
furi_assert(settings);
|
||||
|
||||
bool success = false;
|
||||
|
||||
do {
|
||||
uint8_t version;
|
||||
if(!saved_struct_get_metadata(DESKTOP_SETTINGS_PATH, NULL, &version, NULL)) break;
|
||||
|
||||
if(version == DESKTOP_SETTINGS_VER) {
|
||||
success = saved_struct_load(
|
||||
DESKTOP_SETTINGS_PATH,
|
||||
settings,
|
||||
sizeof(DesktopSettings),
|
||||
DESKTOP_SETTINGS_MAGIC,
|
||||
DESKTOP_SETTINGS_VER);
|
||||
|
||||
} else if(version == DESKTOP_SETTINGS_VER_10) {
|
||||
DesktopSettingsV10* settings_v10 = malloc(sizeof(DesktopSettingsV10));
|
||||
|
||||
success = saved_struct_load(
|
||||
DESKTOP_SETTINGS_PATH,
|
||||
settings_v10,
|
||||
sizeof(DesktopSettingsV10),
|
||||
DESKTOP_SETTINGS_MAGIC,
|
||||
DESKTOP_SETTINGS_VER_10);
|
||||
|
||||
if(success) {
|
||||
*settings = settings_v10->settings;
|
||||
}
|
||||
|
||||
free(settings_v10);
|
||||
}
|
||||
|
||||
} while(false);
|
||||
|
||||
if(!success) {
|
||||
FURI_LOG_W(TAG, "Failed to load file, using defaults");
|
||||
memset(settings, 0, sizeof(DesktopSettings));
|
||||
desktop_settings_save(settings);
|
||||
}
|
||||
}
|
||||
|
||||
void desktop_settings_save(const DesktopSettings* settings) {
|
||||
furi_assert(settings);
|
||||
|
||||
const bool success = saved_struct_save(
|
||||
DESKTOP_SETTINGS_PATH,
|
||||
settings,
|
||||
sizeof(DesktopSettings),
|
||||
DESKTOP_SETTINGS_MAGIC,
|
||||
DESKTOP_SETTINGS_VER);
|
||||
|
||||
if(!success) {
|
||||
FURI_LOG_E(TAG, "Failed to save file");
|
||||
}
|
||||
}
|
|
@ -1,12 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include "desktop_settings_filename.h"
|
||||
|
||||
#include <furi_hal.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <toolbox/saved_struct.h>
|
||||
#include <storage/storage.h>
|
||||
|
||||
#define DESKTOP_SETTINGS_VER (13)
|
||||
|
||||
|
@ -44,7 +38,7 @@
|
|||
#define DISPLAY_BATTERY_BAR_PERCENT 5
|
||||
|
||||
typedef enum {
|
||||
FavoriteAppLeftShort = 0,
|
||||
FavoriteAppLeftShort,
|
||||
FavoriteAppLeftLong,
|
||||
FavoriteAppRightShort,
|
||||
FavoriteAppRightLong,
|
||||
|
@ -67,16 +61,10 @@ typedef enum {
|
|||
} DummyAppShortcut;
|
||||
|
||||
typedef struct {
|
||||
InputKey data[MAX_PIN_SIZE];
|
||||
uint8_t length;
|
||||
} PinCode;
|
||||
|
||||
typedef struct {
|
||||
char name_or_path[MAX_APP_LENGTH];
|
||||
char name_or_path[128];
|
||||
} FavoriteApp;
|
||||
|
||||
typedef struct {
|
||||
PinCode pin_code;
|
||||
uint32_t auto_lock_delay_ms;
|
||||
uint8_t displayBatteryPercentage;
|
||||
uint8_t dummy_mode;
|
||||
|
@ -84,3 +72,6 @@ typedef struct {
|
|||
FavoriteApp favorite_apps[FavoriteAppNumber];
|
||||
FavoriteApp dummy_apps[DummyAppNumber];
|
||||
} DesktopSettings;
|
||||
|
||||
void desktop_settings_load(DesktopSettings* settings);
|
||||
void desktop_settings_save(const DesktopSettings* settings);
|
||||
|
|
|
@ -1,72 +0,0 @@
|
|||
#include "pin.h"
|
||||
|
||||
#include <notification/notification.h>
|
||||
#include <notification/notification_messages.h>
|
||||
#include <stddef.h>
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <gui/gui.h>
|
||||
|
||||
static const NotificationSequence sequence_pin_fail = {
|
||||
&message_display_backlight_on,
|
||||
|
||||
&message_red_255,
|
||||
&message_vibro_on,
|
||||
&message_delay_100,
|
||||
&message_vibro_off,
|
||||
&message_red_0,
|
||||
|
||||
&message_delay_250,
|
||||
|
||||
&message_red_255,
|
||||
&message_vibro_on,
|
||||
&message_delay_100,
|
||||
&message_vibro_off,
|
||||
&message_red_0,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const uint8_t desktop_helpers_fails_timeout[] = {
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
30,
|
||||
60,
|
||||
90,
|
||||
120,
|
||||
150,
|
||||
180,
|
||||
/* +60 for every next fail */
|
||||
};
|
||||
|
||||
void desktop_pin_lock_error_notify(void) {
|
||||
NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
|
||||
notification_message(notification, &sequence_pin_fail);
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
}
|
||||
|
||||
uint32_t desktop_pin_lock_get_fail_timeout(void) {
|
||||
uint32_t pin_fails = furi_hal_rtc_get_pin_fails();
|
||||
uint32_t pin_timeout = 0;
|
||||
uint32_t max_index = COUNT_OF(desktop_helpers_fails_timeout) - 1;
|
||||
if(pin_fails <= max_index) {
|
||||
pin_timeout = desktop_helpers_fails_timeout[pin_fails];
|
||||
} else {
|
||||
pin_timeout = desktop_helpers_fails_timeout[max_index] + (pin_fails - max_index) * 60;
|
||||
}
|
||||
|
||||
return pin_timeout;
|
||||
}
|
||||
|
||||
bool desktop_pin_compare(const PinCode* pin_code1, const PinCode* pin_code2) {
|
||||
furi_assert(pin_code1);
|
||||
furi_assert(pin_code2);
|
||||
bool result = false;
|
||||
|
||||
if(pin_code1->length == pin_code2->length) {
|
||||
result = !memcmp(pin_code1->data, pin_code2->data, pin_code1->length);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
#pragma once
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "../desktop.h"
|
||||
#include <desktop/desktop_settings.h>
|
||||
|
||||
void desktop_pin_lock_error_notify(void);
|
||||
|
||||
uint32_t desktop_pin_lock_get_fail_timeout(void);
|
||||
|
||||
bool desktop_pin_compare(const PinCode* pin_code1, const PinCode* pin_code2);
|
103
applications/services/desktop/helpers/pin_code.c
Normal file
103
applications/services/desktop/helpers/pin_code.c
Normal file
|
@ -0,0 +1,103 @@
|
|||
#include "pin_code.h"
|
||||
|
||||
#include <furi_hal_rtc.h>
|
||||
|
||||
#include <furi.h>
|
||||
#include <notification/notification_messages.h>
|
||||
|
||||
#define DESKTOP_PIN_CODE_DIGIT_BIT_WIDTH (2)
|
||||
#define DESKTOP_PIN_CODE_LENGTH_OFFSET (28)
|
||||
|
||||
static const NotificationSequence sequence_pin_fail = {
|
||||
&message_display_backlight_on,
|
||||
|
||||
&message_red_255,
|
||||
&message_vibro_on,
|
||||
&message_delay_100,
|
||||
&message_vibro_off,
|
||||
&message_red_0,
|
||||
|
||||
&message_delay_250,
|
||||
|
||||
&message_red_255,
|
||||
&message_vibro_on,
|
||||
&message_delay_100,
|
||||
&message_vibro_off,
|
||||
&message_red_0,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const uint8_t desktop_helpers_fails_timeout[] = {
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
30,
|
||||
60,
|
||||
90,
|
||||
120,
|
||||
150,
|
||||
180,
|
||||
/* +60 for every next fail */
|
||||
};
|
||||
|
||||
static uint32_t desktop_pin_code_pack(const DesktopPinCode* pin_code) {
|
||||
furi_check(pin_code);
|
||||
furi_check(pin_code->length <= sizeof(pin_code->data));
|
||||
|
||||
uint32_t reg_value = 0;
|
||||
|
||||
for(uint8_t i = 0; i < pin_code->length; ++i) {
|
||||
furi_check(pin_code->data[i] < (1 << DESKTOP_PIN_CODE_DIGIT_BIT_WIDTH));
|
||||
reg_value |= (uint32_t)pin_code->data[i] << (i * DESKTOP_PIN_CODE_DIGIT_BIT_WIDTH);
|
||||
}
|
||||
|
||||
reg_value |= (uint32_t)pin_code->length << DESKTOP_PIN_CODE_LENGTH_OFFSET;
|
||||
|
||||
return reg_value;
|
||||
}
|
||||
|
||||
bool desktop_pin_code_is_set(void) {
|
||||
return furi_hal_rtc_get_pin_value() >> DESKTOP_PIN_CODE_LENGTH_OFFSET;
|
||||
}
|
||||
|
||||
void desktop_pin_code_set(const DesktopPinCode* pin_code) {
|
||||
furi_hal_rtc_set_pin_value(desktop_pin_code_pack(pin_code));
|
||||
}
|
||||
|
||||
void desktop_pin_code_reset(void) {
|
||||
furi_hal_rtc_set_pin_value(0);
|
||||
}
|
||||
|
||||
bool desktop_pin_code_check(const DesktopPinCode* pin_code) {
|
||||
return furi_hal_rtc_get_pin_value() == desktop_pin_code_pack(pin_code);
|
||||
}
|
||||
|
||||
bool desktop_pin_code_is_equal(const DesktopPinCode* pin_code1, const DesktopPinCode* pin_code2) {
|
||||
furi_check(pin_code1);
|
||||
furi_check(pin_code1->length <= sizeof(pin_code1->data));
|
||||
furi_check(pin_code2);
|
||||
furi_check(pin_code2->length <= sizeof(pin_code2->data));
|
||||
|
||||
return pin_code1->length == pin_code2->length &&
|
||||
memcmp(pin_code1->data, pin_code2->data, pin_code1->length) == 0;
|
||||
}
|
||||
|
||||
void desktop_pin_lock_error_notify(void) {
|
||||
NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
|
||||
notification_message(notification, &sequence_pin_fail);
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
}
|
||||
|
||||
uint32_t desktop_pin_lock_get_fail_timeout(void) {
|
||||
uint32_t pin_fails = furi_hal_rtc_get_pin_fails();
|
||||
uint32_t pin_timeout = 0;
|
||||
uint32_t max_index = COUNT_OF(desktop_helpers_fails_timeout) - 1;
|
||||
if(pin_fails <= max_index) {
|
||||
pin_timeout = desktop_helpers_fails_timeout[pin_fails];
|
||||
} else {
|
||||
pin_timeout = desktop_helpers_fails_timeout[max_index] + (pin_fails - max_index) * 60;
|
||||
}
|
||||
|
||||
return pin_timeout;
|
||||
}
|
25
applications/services/desktop/helpers/pin_code.h
Normal file
25
applications/services/desktop/helpers/pin_code.h
Normal file
|
@ -0,0 +1,25 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define DESKTOP_PIN_CODE_MAX_LEN (10)
|
||||
|
||||
typedef struct {
|
||||
uint8_t data[DESKTOP_PIN_CODE_MAX_LEN];
|
||||
uint8_t length;
|
||||
} DesktopPinCode;
|
||||
|
||||
bool desktop_pin_code_is_set(void);
|
||||
|
||||
void desktop_pin_code_set(const DesktopPinCode* pin_code);
|
||||
|
||||
void desktop_pin_code_reset(void);
|
||||
|
||||
bool desktop_pin_code_check(const DesktopPinCode* pin_code);
|
||||
|
||||
bool desktop_pin_code_is_equal(const DesktopPinCode* pin_code1, const DesktopPinCode* pin_code2);
|
||||
|
||||
void desktop_pin_lock_error_notify(void);
|
||||
|
||||
uint32_t desktop_pin_lock_get_fail_timeout(void);
|
|
@ -1,4 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#define SCENE_LOCKED_FIRST_ENTER 0
|
||||
#define SCENE_LOCKED_REPEAT_ENTER 1
|
|
@ -20,7 +20,6 @@ void desktop_scene_lock_menu_callback(DesktopEvent event, void* context) {
|
|||
void desktop_scene_lock_menu_on_enter(void* context) {
|
||||
Desktop* desktop = (Desktop*)context;
|
||||
|
||||
DESKTOP_SETTINGS_LOAD(&desktop->settings);
|
||||
scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 0);
|
||||
desktop_lock_menu_set_callback(desktop->lock_menu, desktop_scene_lock_menu_callback, desktop);
|
||||
desktop_lock_menu_set_dummy_mode_state(desktop->lock_menu, desktop->settings.dummy_mode);
|
||||
|
@ -38,12 +37,9 @@ bool desktop_scene_lock_menu_on_event(void* context, SceneManagerEvent event) {
|
|||
if(event.type == SceneManagerEventTypeTick) {
|
||||
bool check_pin_changed =
|
||||
scene_manager_get_scene_state(desktop->scene_manager, DesktopSceneLockMenu);
|
||||
if(check_pin_changed) {
|
||||
DESKTOP_SETTINGS_LOAD(&desktop->settings);
|
||||
if(desktop->settings.pin_code.length > 0) {
|
||||
if(check_pin_changed && desktop_pin_code_is_set()) {
|
||||
scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 0);
|
||||
}
|
||||
}
|
||||
} else if(event.type == SceneManagerEventTypeCustom) {
|
||||
switch(event.event) {
|
||||
case DesktopLockMenuEventLock:
|
||||
|
|
|
@ -6,12 +6,12 @@
|
|||
|
||||
#include "../desktop.h"
|
||||
#include "../desktop_i.h"
|
||||
#include "../helpers/pin.h"
|
||||
#include "../helpers/pin_code.h"
|
||||
#include "../animations/animation_manager.h"
|
||||
#include "../views/desktop_events.h"
|
||||
#include "../views/desktop_view_locked.h"
|
||||
#include "desktop_scene.h"
|
||||
#include "desktop_scene_i.h"
|
||||
#include "desktop_scene_locked.h"
|
||||
|
||||
#define WRONG_PIN_HEADER_TIMEOUT 3000
|
||||
#define INPUT_PIN_VIEW_TIMEOUT 15000
|
||||
|
@ -42,15 +42,13 @@ void desktop_scene_locked_on_enter(void* context) {
|
|||
|
||||
bool switch_to_timeout_scene = false;
|
||||
uint32_t state = scene_manager_get_scene_state(desktop->scene_manager, DesktopSceneLocked);
|
||||
if(state == SCENE_LOCKED_FIRST_ENTER) {
|
||||
bool pin_locked = desktop->settings.pin_code.length > 0;
|
||||
if(state == DesktopSceneLockedStateFirstEnter) {
|
||||
view_port_enabled_set(desktop->lock_icon_viewport, true);
|
||||
Gui* gui = furi_record_open(RECORD_GUI);
|
||||
gui_set_lockdown(gui, true);
|
||||
furi_record_close(RECORD_GUI);
|
||||
|
||||
if(pin_locked) {
|
||||
DESKTOP_SETTINGS_LOAD(&desktop->settings);
|
||||
if(desktop_pin_code_is_set()) {
|
||||
desktop_view_locked_lock(desktop->locked_view, true);
|
||||
uint32_t pin_timeout = desktop_pin_lock_get_fail_timeout();
|
||||
if(pin_timeout > 0) {
|
||||
|
@ -65,7 +63,7 @@ void desktop_scene_locked_on_enter(void* context) {
|
|||
desktop_view_locked_close_doors(desktop->locked_view);
|
||||
}
|
||||
scene_manager_set_scene_state(
|
||||
desktop->scene_manager, DesktopSceneLocked, SCENE_LOCKED_REPEAT_ENTER);
|
||||
desktop->scene_manager, DesktopSceneLocked, DesktopSceneLockedStateRepeatEnter);
|
||||
}
|
||||
|
||||
if(switch_to_timeout_scene) {
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
typedef enum {
|
||||
DesktopSceneLockedStateFirstEnter,
|
||||
DesktopSceneLockedStateRepeatEnter,
|
||||
} DesktopSceneLockedState;
|
|
@ -155,25 +155,21 @@ bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) {
|
|||
}
|
||||
|
||||
case DesktopMainEventOpenFavoriteLeftShort:
|
||||
DESKTOP_SETTINGS_LOAD(&desktop->settings);
|
||||
desktop_scene_main_start_favorite(
|
||||
desktop, &desktop->settings.favorite_apps[FavoriteAppLeftShort]);
|
||||
consumed = true;
|
||||
break;
|
||||
case DesktopMainEventOpenFavoriteLeftLong:
|
||||
DESKTOP_SETTINGS_LOAD(&desktop->settings);
|
||||
desktop_scene_main_start_favorite(
|
||||
desktop, &desktop->settings.favorite_apps[FavoriteAppLeftLong]);
|
||||
consumed = true;
|
||||
break;
|
||||
case DesktopMainEventOpenFavoriteRightShort:
|
||||
DESKTOP_SETTINGS_LOAD(&desktop->settings);
|
||||
desktop_scene_main_start_favorite(
|
||||
desktop, &desktop->settings.favorite_apps[FavoriteAppRightShort]);
|
||||
consumed = true;
|
||||
break;
|
||||
case DesktopMainEventOpenFavoriteRightLong:
|
||||
DESKTOP_SETTINGS_LOAD(&desktop->settings);
|
||||
desktop_scene_main_start_favorite(
|
||||
desktop, &desktop->settings.favorite_apps[FavoriteAppRightLong]);
|
||||
consumed = true;
|
||||
|
@ -189,7 +185,6 @@ bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) {
|
|||
break;
|
||||
case DesktopAnimationEventInteractAnimation:
|
||||
if(!animation_manager_interact_process(desktop->animation_manager)) {
|
||||
DESKTOP_SETTINGS_LOAD(&desktop->settings);
|
||||
if(!desktop->settings.dummy_mode) {
|
||||
desktop_scene_main_open_app_or_profile(
|
||||
desktop, &desktop->settings.favorite_apps[FavoriteAppRightShort]);
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
#include "../desktop_i.h"
|
||||
#include "../views/desktop_events.h"
|
||||
#include "../views/desktop_view_pin_input.h"
|
||||
#include "../helpers/pin.h"
|
||||
#include "../helpers/pin_code.h"
|
||||
#include "desktop_scene.h"
|
||||
|
||||
#define WRONG_PIN_HEADER_TIMEOUT 3000
|
||||
|
@ -49,10 +49,12 @@ static void desktop_scene_pin_input_back_callback(void* context) {
|
|||
view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopPinInputEventBack);
|
||||
}
|
||||
|
||||
static void desktop_scene_pin_input_done_callback(const PinCode* pin_code, void* context) {
|
||||
static void desktop_scene_pin_input_done_callback(const DesktopPinCode* pin_code, void* context) {
|
||||
Desktop* desktop = (Desktop*)context;
|
||||
if(desktop_pin_compare(&desktop->settings.pin_code, pin_code)) {
|
||||
|
||||
if(desktop_pin_code_check(pin_code)) {
|
||||
view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopPinInputEventUnlocked);
|
||||
|
||||
} else {
|
||||
uint32_t pin_fails = furi_hal_rtc_get_pin_fails();
|
||||
furi_hal_rtc_set_pin_fails(pin_fails + 1);
|
||||
|
|
|
@ -45,9 +45,6 @@ bool desktop_scene_slideshow_on_event(void* context, SceneManagerEvent event) {
|
|||
}
|
||||
|
||||
void desktop_scene_slideshow_on_exit(void* context) {
|
||||
UNUSED(context);
|
||||
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
storage_common_remove(storage, SLIDESHOW_FS_PATH);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
Desktop* desktop = context;
|
||||
storage_common_remove(desktop->storage, SLIDESHOW_FS_PATH);
|
||||
}
|
||||
|
|
|
@ -60,4 +60,6 @@ typedef enum {
|
|||
DesktopGlobalAfterAppFinished,
|
||||
DesktopGlobalAutoLock,
|
||||
DesktopGlobalApiUnlock,
|
||||
DesktopGlobalSaveSettings,
|
||||
DesktopGlobalReloadSettings,
|
||||
} DesktopEvent;
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
#include <stdint.h>
|
||||
|
||||
#include "desktop_view_pin_input.h"
|
||||
#include <desktop/desktop_settings.h>
|
||||
|
||||
#define NO_ACTIVITY_TIMEOUT 15000
|
||||
|
||||
|
@ -14,6 +13,9 @@
|
|||
#define DEFAULT_PIN_X 64
|
||||
#define DEFAULT_PIN_Y 32
|
||||
|
||||
#define MIN_PIN_LENGTH 4
|
||||
#define MAX_PIN_LENGTH DESKTOP_PIN_CODE_MAX_LEN
|
||||
|
||||
struct DesktopViewPinInput {
|
||||
View* view;
|
||||
DesktopViewPinInputCallback back_callback;
|
||||
|
@ -24,7 +26,7 @@ struct DesktopViewPinInput {
|
|||
};
|
||||
|
||||
typedef struct {
|
||||
PinCode pin;
|
||||
DesktopPinCode pin;
|
||||
bool pin_hidden;
|
||||
bool locked_input;
|
||||
uint8_t pin_x;
|
||||
|
@ -50,7 +52,7 @@ static bool desktop_view_pin_input_input(InputEvent* event, void* context) {
|
|||
|
||||
bool call_back_callback = false;
|
||||
bool call_done_callback = false;
|
||||
PinCode pin_code = {0};
|
||||
DesktopPinCode pin_code = {0};
|
||||
|
||||
if(event->type == InputTypeShort) {
|
||||
switch(event->key) {
|
||||
|
@ -59,13 +61,13 @@ static bool desktop_view_pin_input_input(InputEvent* event, void* context) {
|
|||
case InputKeyDown:
|
||||
case InputKeyUp:
|
||||
if(!model->locked_input) {
|
||||
if(model->pin.length < MAX_PIN_SIZE) {
|
||||
if(model->pin.length < MAX_PIN_LENGTH) {
|
||||
model->pin.data[model->pin.length++] = event->key;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case InputKeyOk:
|
||||
if(model->pin.length >= MIN_PIN_SIZE) {
|
||||
if(model->pin.length >= MIN_PIN_LENGTH) {
|
||||
call_done_callback = true;
|
||||
pin_code = model->pin;
|
||||
}
|
||||
|
@ -102,7 +104,7 @@ static void desktop_view_pin_input_draw_cells(Canvas* canvas, DesktopViewPinInpu
|
|||
furi_assert(model);
|
||||
|
||||
uint8_t draw_pin_size = MAX(4, model->pin.length + 1);
|
||||
if(model->locked_input || (model->pin.length == MAX_PIN_SIZE)) {
|
||||
if(model->locked_input || (model->pin.length == MAX_PIN_LENGTH)) {
|
||||
draw_pin_size = model->pin.length;
|
||||
}
|
||||
|
||||
|
@ -155,7 +157,7 @@ static void desktop_view_pin_input_draw(Canvas* canvas, void* context) {
|
|||
canvas_draw_str(canvas, 16, 60, "= clear");
|
||||
}
|
||||
|
||||
if(model->button_label && ((model->pin.length >= MIN_PIN_SIZE) || model->locked_input)) {
|
||||
if(model->button_label && ((model->pin.length >= MIN_PIN_LENGTH) || model->locked_input)) {
|
||||
elements_button_center(canvas, model->button_label);
|
||||
}
|
||||
|
||||
|
@ -247,7 +249,7 @@ void desktop_view_pin_input_unlock_input(DesktopViewPinInput* pin_input) {
|
|||
view_commit_model(pin_input->view, true);
|
||||
}
|
||||
|
||||
void desktop_view_pin_input_set_pin(DesktopViewPinInput* pin_input, const PinCode* pin) {
|
||||
void desktop_view_pin_input_set_pin(DesktopViewPinInput* pin_input, const DesktopPinCode* pin) {
|
||||
furi_assert(pin_input);
|
||||
furi_assert(pin);
|
||||
|
||||
|
|
|
@ -1,16 +1,17 @@
|
|||
#pragma once
|
||||
|
||||
#include <gui/view.h>
|
||||
#include <desktop/desktop_settings.h>
|
||||
|
||||
#include "../helpers/pin_code.h"
|
||||
|
||||
typedef void (*DesktopViewPinInputCallback)(void*);
|
||||
typedef void (*DesktopViewPinInputDoneCallback)(const PinCode* pin_code, void*);
|
||||
typedef void (*DesktopViewPinInputDoneCallback)(const DesktopPinCode* pin_code, void*);
|
||||
typedef struct DesktopViewPinInput DesktopViewPinInput;
|
||||
|
||||
DesktopViewPinInput* desktop_view_pin_input_alloc(void);
|
||||
void desktop_view_pin_input_free(DesktopViewPinInput*);
|
||||
|
||||
void desktop_view_pin_input_set_pin(DesktopViewPinInput* pin_input, const PinCode* pin);
|
||||
void desktop_view_pin_input_set_pin(DesktopViewPinInput* pin_input, const DesktopPinCode* pin_code);
|
||||
void desktop_view_pin_input_reset_pin(DesktopViewPinInput* pin_input);
|
||||
void desktop_view_pin_input_hide_pin(DesktopViewPinInput* pin_input, bool pin_hidden);
|
||||
void desktop_view_pin_input_set_label_button(DesktopViewPinInput* pin_input, const char* label);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "dolphin_i.h"
|
||||
|
||||
#include <furi_hal.h>
|
||||
#include <storage/storage.h>
|
||||
|
||||
#define TAG "Dolphin"
|
||||
|
||||
|
@ -223,6 +224,10 @@ static bool dolphin_process_event(FuriMessageQueue* queue, void* context) {
|
|||
dolphin_state_increase_level(dolphin->state);
|
||||
furi_event_loop_timer_start(dolphin->flush_timer, FLUSH_TIMEOUT_TICKS);
|
||||
|
||||
} else if(event.type == DolphinEventTypeReloadState) {
|
||||
dolphin_state_load(dolphin->state);
|
||||
furi_event_loop_timer_start(dolphin->butthurt_timer, BUTTHURT_INCREASE_PERIOD_TICKS);
|
||||
|
||||
} else {
|
||||
furi_crash();
|
||||
}
|
||||
|
@ -232,6 +237,32 @@ static bool dolphin_process_event(FuriMessageQueue* queue, void* context) {
|
|||
return true;
|
||||
}
|
||||
|
||||
static void dolphin_storage_callback(const void* message, void* context) {
|
||||
furi_assert(context);
|
||||
Dolphin* dolphin = context;
|
||||
const StorageEvent* event = message;
|
||||
|
||||
if(event->type == StorageEventTypeCardMount) {
|
||||
DolphinEvent event = {
|
||||
.type = DolphinEventTypeReloadState,
|
||||
};
|
||||
|
||||
dolphin_event_send_async(dolphin, &event);
|
||||
}
|
||||
}
|
||||
|
||||
static void dolphin_init_state(Dolphin* dolphin) {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
furi_pubsub_subscribe(storage_get_pubsub(storage), dolphin_storage_callback, dolphin);
|
||||
|
||||
if(storage_sd_status(storage) != FSE_OK) {
|
||||
FURI_LOG_D(TAG, "SD Card not ready, skipping state");
|
||||
return;
|
||||
}
|
||||
|
||||
dolphin_state_load(dolphin->state);
|
||||
}
|
||||
|
||||
// Application thread
|
||||
|
||||
int32_t dolphin_srv(void* p) {
|
||||
|
@ -247,7 +278,7 @@ int32_t dolphin_srv(void* p) {
|
|||
Dolphin* dolphin = dolphin_alloc();
|
||||
furi_record_create(RECORD_DOLPHIN, dolphin);
|
||||
|
||||
dolphin_state_load(dolphin->state);
|
||||
dolphin_init_state(dolphin);
|
||||
|
||||
furi_event_loop_message_queue_subscribe(
|
||||
dolphin->event_loop,
|
||||
|
|
|
@ -12,6 +12,7 @@ typedef enum {
|
|||
DolphinEventTypeStats,
|
||||
DolphinEventTypeFlush,
|
||||
DolphinEventTypeLevel,
|
||||
DolphinEventTypeReloadState,
|
||||
} DolphinEventType;
|
||||
|
||||
typedef struct {
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
#include "dolphin_state.h"
|
||||
#include "dolphin/helpers/dolphin_deed.h"
|
||||
#include "dolphin_state_filename.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <storage/storage.h>
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
|
||||
#include <storage/storage.h>
|
||||
#include <toolbox/saved_struct.h>
|
||||
|
||||
#define TAG "DolphinState"
|
||||
|
@ -26,29 +25,28 @@ void dolphin_state_free(DolphinState* dolphin_state) {
|
|||
free(dolphin_state);
|
||||
}
|
||||
|
||||
bool dolphin_state_save(DolphinState* dolphin_state) {
|
||||
void dolphin_state_save(DolphinState* dolphin_state) {
|
||||
if(!dolphin_state->dirty) {
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
|
||||
bool result = saved_struct_save(
|
||||
bool success = saved_struct_save(
|
||||
DOLPHIN_STATE_PATH,
|
||||
&dolphin_state->data,
|
||||
sizeof(DolphinStoreData),
|
||||
DOLPHIN_STATE_HEADER_MAGIC,
|
||||
DOLPHIN_STATE_HEADER_VERSION);
|
||||
|
||||
if(result) {
|
||||
if(success) {
|
||||
FURI_LOG_I(TAG, "State saved");
|
||||
dolphin_state->dirty = false;
|
||||
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "Failed to save state");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool dolphin_state_load(DolphinState* dolphin_state) {
|
||||
void dolphin_state_load(DolphinState* dolphin_state) {
|
||||
bool success = saved_struct_load(
|
||||
DOLPHIN_STATE_PATH,
|
||||
&dolphin_state->data,
|
||||
|
@ -64,12 +62,12 @@ bool dolphin_state_load(DolphinState* dolphin_state) {
|
|||
}
|
||||
|
||||
if(!success) {
|
||||
FURI_LOG_W(TAG, "Reset dolphin-state");
|
||||
memset(dolphin_state, 0, sizeof(*dolphin_state));
|
||||
dolphin_state->dirty = true;
|
||||
}
|
||||
FURI_LOG_W(TAG, "Reset Dolphin state");
|
||||
memset(dolphin_state, 0, sizeof(DolphinState));
|
||||
|
||||
return success;
|
||||
dolphin_state->dirty = true;
|
||||
dolphin_state_save(dolphin_state);
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t dolphin_state_timestamp(void) {
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
#pragma once
|
||||
|
||||
#include "dolphin_deed.h"
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "dolphin_deed.h"
|
||||
|
||||
typedef struct DolphinState DolphinState;
|
||||
typedef struct {
|
||||
|
@ -25,9 +25,9 @@ DolphinState* dolphin_state_alloc(void);
|
|||
|
||||
void dolphin_state_free(DolphinState* dolphin_state);
|
||||
|
||||
bool dolphin_state_save(DolphinState* dolphin_state);
|
||||
void dolphin_state_save(DolphinState* dolphin_state);
|
||||
|
||||
bool dolphin_state_load(DolphinState* dolphin_state);
|
||||
void dolphin_state_load(DolphinState* dolphin_state);
|
||||
|
||||
void dolphin_state_clear_limits(DolphinState* dolphin_state);
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <furi_hal_serial_control.h>
|
||||
|
||||
#include <furi.h>
|
||||
#include <storage/storage.h>
|
||||
#include <toolbox/api_lock.h>
|
||||
|
||||
#include "expansion_worker.h"
|
||||
|
@ -25,6 +26,7 @@ typedef enum {
|
|||
ExpansionMessageTypeEnable,
|
||||
ExpansionMessageTypeDisable,
|
||||
ExpansionMessageTypeSetListenSerial,
|
||||
ExpansionMessageTypeReloadSettings,
|
||||
ExpansionMessageTypeModuleConnected,
|
||||
ExpansionMessageTypeModuleDisconnected,
|
||||
ExpansionMessageTypeConnectionEstablished,
|
||||
|
@ -103,7 +105,10 @@ static void
|
|||
return;
|
||||
}
|
||||
|
||||
if(instance->settings.uart_index < FuriHalSerialIdMax) {
|
||||
ExpansionSettings settings;
|
||||
expansion_settings_load(&settings);
|
||||
|
||||
if(settings.uart_index < FuriHalSerialIdMax) {
|
||||
instance->state = ExpansionStateEnabled;
|
||||
instance->serial_id = instance->settings.uart_index;
|
||||
furi_hal_serial_control_set_expansion_callback(
|
||||
|
@ -116,7 +121,6 @@ static void
|
|||
static void
|
||||
expansion_control_handler_disable(Expansion* instance, const ExpansionMessageData* data) {
|
||||
UNUSED(data);
|
||||
|
||||
if(instance->state == ExpansionStateDisabled) {
|
||||
return;
|
||||
} else if(
|
||||
|
@ -136,10 +140,10 @@ static void
|
|||
static void expansion_control_handler_set_listen_serial(
|
||||
Expansion* instance,
|
||||
const ExpansionMessageData* data) {
|
||||
furi_check(data->serial_id < FuriHalSerialIdMax);
|
||||
if(instance->state != ExpansionStateDisabled && instance->serial_id == data->serial_id) {
|
||||
return;
|
||||
|
||||
if(instance->state == ExpansionStateRunning ||
|
||||
instance->state == ExpansionStateConnectionEstablished) {
|
||||
} else if(instance->state == ExpansionStateRunning) {
|
||||
expansion_worker_stop(instance->worker);
|
||||
expansion_worker_free(instance->worker);
|
||||
|
||||
|
@ -156,6 +160,26 @@ static void expansion_control_handler_set_listen_serial(
|
|||
FURI_LOG_D(TAG, "Listen serial changed to %s", expansion_uart_names[instance->serial_id]);
|
||||
}
|
||||
|
||||
static void expansion_control_handler_reload_settings(
|
||||
Expansion* instance,
|
||||
const ExpansionMessageData* data) {
|
||||
UNUSED(data);
|
||||
|
||||
ExpansionSettings settings;
|
||||
expansion_settings_load(&settings);
|
||||
|
||||
if(settings.uart_index < FuriHalSerialIdMax) {
|
||||
const ExpansionMessageData data = {
|
||||
.serial_id = settings.uart_index,
|
||||
};
|
||||
|
||||
expansion_control_handler_set_listen_serial(instance, &data);
|
||||
|
||||
} else {
|
||||
expansion_control_handler_disable(instance, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void expansion_control_handler_module_connected(
|
||||
Expansion* instance,
|
||||
const ExpansionMessageData* data) {
|
||||
|
@ -211,6 +235,7 @@ static const ExpansionControlHandler expansion_control_handlers[] = {
|
|||
[ExpansionMessageTypeEnable] = expansion_control_handler_enable,
|
||||
[ExpansionMessageTypeDisable] = expansion_control_handler_disable,
|
||||
[ExpansionMessageTypeSetListenSerial] = expansion_control_handler_set_listen_serial,
|
||||
[ExpansionMessageTypeReloadSettings] = expansion_control_handler_reload_settings,
|
||||
[ExpansionMessageTypeModuleConnected] = expansion_control_handler_module_connected,
|
||||
[ExpansionMessageTypeModuleDisconnected] = expansion_control_handler_module_disconnected,
|
||||
[ExpansionMessageTypeConnectionEstablished] = expansion_control_handler_connection_established,
|
||||
|
@ -249,6 +274,22 @@ static Expansion* expansion_alloc(void) {
|
|||
return instance;
|
||||
}
|
||||
|
||||
static void expansion_storage_callback(const void* message, void* context) {
|
||||
furi_assert(context);
|
||||
|
||||
const StorageEvent* event = message;
|
||||
Expansion* instance = context;
|
||||
|
||||
if(event->type == StorageEventTypeCardMount) {
|
||||
ExpansionMessage em = {
|
||||
.type = ExpansionMessageTypeReloadSettings,
|
||||
.api_lock = NULL,
|
||||
};
|
||||
|
||||
furi_check(furi_message_queue_put(instance->queue, &em, FuriWaitForever) == FuriStatusOk);
|
||||
}
|
||||
}
|
||||
|
||||
void expansion_on_system_start(void* arg) {
|
||||
UNUSED(arg);
|
||||
|
||||
|
@ -256,7 +297,14 @@ void expansion_on_system_start(void* arg) {
|
|||
furi_record_create(RECORD_EXPANSION, instance);
|
||||
furi_thread_start(instance->thread);
|
||||
|
||||
expansion_settings_load(&instance->settings);
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
furi_pubsub_subscribe(storage_get_pubsub(storage), expansion_storage_callback, instance);
|
||||
|
||||
if(storage_sd_status(storage) != FSE_OK) {
|
||||
FURI_LOG_D(TAG, "SD Card not ready, skipping settings");
|
||||
return;
|
||||
}
|
||||
|
||||
expansion_enable(instance);
|
||||
}
|
||||
|
||||
|
|
|
@ -6,29 +6,40 @@
|
|||
|
||||
#include "expansion_settings_filename.h"
|
||||
|
||||
#define TAG "ExpansionSettings"
|
||||
|
||||
#define EXPANSION_SETTINGS_PATH INT_PATH(EXPANSION_SETTINGS_FILE_NAME)
|
||||
#define EXPANSION_SETTINGS_VERSION (0)
|
||||
#define EXPANSION_SETTINGS_MAGIC (0xEA)
|
||||
|
||||
bool expansion_settings_load(ExpansionSettings* settings) {
|
||||
void expansion_settings_load(ExpansionSettings* settings) {
|
||||
furi_assert(settings);
|
||||
if(!saved_struct_load(
|
||||
EXPANSION_SETTINGS_PATH,
|
||||
settings,
|
||||
sizeof(ExpansionSettings),
|
||||
EXPANSION_SETTINGS_MAGIC,
|
||||
EXPANSION_SETTINGS_VERSION)) {
|
||||
settings->uart_index = FuriHalSerialIdMax;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool expansion_settings_save(const ExpansionSettings* settings) {
|
||||
furi_assert(settings);
|
||||
return saved_struct_save(
|
||||
const bool success = saved_struct_load(
|
||||
EXPANSION_SETTINGS_PATH,
|
||||
settings,
|
||||
sizeof(ExpansionSettings),
|
||||
EXPANSION_SETTINGS_MAGIC,
|
||||
EXPANSION_SETTINGS_VERSION);
|
||||
|
||||
if(!success) {
|
||||
FURI_LOG_W(TAG, "Failed to load file, using defaults");
|
||||
memset(settings, 0, sizeof(ExpansionSettings));
|
||||
expansion_settings_save(settings);
|
||||
}
|
||||
}
|
||||
|
||||
void expansion_settings_save(const ExpansionSettings* settings) {
|
||||
furi_assert(settings);
|
||||
|
||||
const bool success = saved_struct_save(
|
||||
EXPANSION_SETTINGS_PATH,
|
||||
settings,
|
||||
sizeof(ExpansionSettings),
|
||||
EXPANSION_SETTINGS_MAGIC,
|
||||
EXPANSION_SETTINGS_VERSION);
|
||||
|
||||
if(!success) {
|
||||
FURI_LOG_E(TAG, "Failed to save file");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,18 +25,16 @@ typedef struct {
|
|||
/**
|
||||
* @brief Load expansion module support settings from file.
|
||||
*
|
||||
* @param[out] settings pointer to an ExpansionSettings instance to load settings into.
|
||||
* @returns true if the settings were successfully loaded, false otherwise.
|
||||
* @param[in,out] settings pointer to an ExpansionSettings instance to load settings into.
|
||||
*/
|
||||
bool expansion_settings_load(ExpansionSettings* settings);
|
||||
void expansion_settings_load(ExpansionSettings* settings);
|
||||
|
||||
/**
|
||||
* @brief Save expansion module support settings to file.
|
||||
*
|
||||
* @param[in] settings pointer to an ExpansionSettings instance to save settings from.
|
||||
* @returns true if the settings were successfully saved, false otherwise.
|
||||
*/
|
||||
bool expansion_settings_save(const ExpansionSettings* settings);
|
||||
void expansion_settings_save(const ExpansionSettings* settings);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
#define TAG "BrowserWorker"
|
||||
|
||||
#define ASSETS_DIR "assets"
|
||||
#define BROWSER_ROOT STORAGE_ANY_PATH_PREFIX
|
||||
#define BROWSER_ROOT STORAGE_EXT_PATH_PREFIX
|
||||
#define FILE_NAME_LEN_MAX 256
|
||||
#define LONG_LOAD_THRESHOLD 100
|
||||
|
||||
|
|
|
@ -438,7 +438,7 @@ static bool notification_load_settings(NotificationApp* app) {
|
|||
File* file = storage_file_alloc(furi_record_open(RECORD_STORAGE));
|
||||
const size_t settings_size = sizeof(NotificationSettings);
|
||||
|
||||
FURI_LOG_I(TAG, "loading settings from \"%s\"", NOTIFICATION_SETTINGS_PATH);
|
||||
FURI_LOG_I(TAG, "Loading \"%s\"", NOTIFICATION_SETTINGS_PATH);
|
||||
bool fs_result =
|
||||
storage_file_open(file, NOTIFICATION_SETTINGS_PATH, FSAM_READ, FSOM_OPEN_EXISTING);
|
||||
|
||||
|
@ -451,8 +451,6 @@ static bool notification_load_settings(NotificationApp* app) {
|
|||
}
|
||||
|
||||
if(fs_result) {
|
||||
FURI_LOG_I(TAG, "load success");
|
||||
|
||||
if(settings.version != NOTIFICATION_SETTINGS_VERSION) {
|
||||
FURI_LOG_E(
|
||||
TAG, "version(%d != %d) mismatch", settings.version, NOTIFICATION_SETTINGS_VERSION);
|
||||
|
@ -462,7 +460,7 @@ static bool notification_load_settings(NotificationApp* app) {
|
|||
furi_kernel_unlock();
|
||||
}
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "load failed, %s", storage_file_get_error_desc(file));
|
||||
FURI_LOG_E(TAG, "Load failed, %s", storage_file_get_error_desc(file));
|
||||
}
|
||||
|
||||
storage_file_close(file);
|
||||
|
@ -477,7 +475,7 @@ static bool notification_save_settings(NotificationApp* app) {
|
|||
File* file = storage_file_alloc(furi_record_open(RECORD_STORAGE));
|
||||
const size_t settings_size = sizeof(NotificationSettings);
|
||||
|
||||
FURI_LOG_I(TAG, "saving settings to \"%s\"", NOTIFICATION_SETTINGS_PATH);
|
||||
FURI_LOG_I(TAG, "Saving \"%s\"", NOTIFICATION_SETTINGS_PATH);
|
||||
|
||||
furi_kernel_lock();
|
||||
memcpy(&settings, &app->settings, settings_size);
|
||||
|
@ -495,9 +493,8 @@ static bool notification_save_settings(NotificationApp* app) {
|
|||
}
|
||||
|
||||
if(fs_result) {
|
||||
FURI_LOG_I(TAG, "save success");
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "save failed, %s", storage_file_get_error_desc(file));
|
||||
FURI_LOG_E(TAG, "Save failed, %s", storage_file_get_error_desc(file));
|
||||
}
|
||||
|
||||
storage_file_close(file);
|
||||
|
@ -556,14 +553,46 @@ static NotificationApp* notification_app_alloc(void) {
|
|||
return app;
|
||||
}
|
||||
|
||||
static void notification_storage_callback(const void* message, void* context) {
|
||||
furi_assert(context);
|
||||
NotificationApp* app = context;
|
||||
const StorageEvent* event = message;
|
||||
|
||||
if(event->type == StorageEventTypeCardMount) {
|
||||
NotificationAppMessage m = {
|
||||
.type = LoadSettingsMessage,
|
||||
};
|
||||
|
||||
furi_check(furi_message_queue_put(app->queue, &m, FuriWaitForever) == FuriStatusOk);
|
||||
}
|
||||
}
|
||||
|
||||
static void notification_apply_settings(NotificationApp* app) {
|
||||
if(!notification_load_settings(app)) {
|
||||
notification_save_settings(app);
|
||||
}
|
||||
|
||||
notification_apply_lcd_contrast(app);
|
||||
}
|
||||
|
||||
static void notification_init_settings(NotificationApp* app) {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
furi_pubsub_subscribe(storage_get_pubsub(storage), notification_storage_callback, app);
|
||||
|
||||
if(storage_sd_status(storage) != FSE_OK) {
|
||||
FURI_LOG_D(TAG, "SD Card not ready, skipping settings");
|
||||
return;
|
||||
}
|
||||
|
||||
notification_apply_settings(app);
|
||||
}
|
||||
|
||||
// App
|
||||
int32_t notification_srv(void* p) {
|
||||
UNUSED(p);
|
||||
NotificationApp* app = notification_app_alloc();
|
||||
|
||||
if(!notification_load_settings(app)) {
|
||||
notification_save_settings(app);
|
||||
}
|
||||
notification_init_settings(app);
|
||||
|
||||
notification_vibro_off();
|
||||
notification_sound_off();
|
||||
|
@ -571,7 +600,6 @@ int32_t notification_srv(void* p) {
|
|||
notification_apply_internal_led_layer(&app->led[0], 0x00);
|
||||
notification_apply_internal_led_layer(&app->led[1], 0x00);
|
||||
notification_apply_internal_led_layer(&app->led[2], 0x00);
|
||||
notification_apply_lcd_contrast(app);
|
||||
|
||||
furi_record_create(RECORD_NOTIFICATION, app);
|
||||
|
||||
|
@ -589,6 +617,9 @@ int32_t notification_srv(void* p) {
|
|||
case SaveSettingsMessage:
|
||||
notification_save_settings(app);
|
||||
break;
|
||||
case LoadSettingsMessage:
|
||||
notification_load_settings(app);
|
||||
break;
|
||||
}
|
||||
|
||||
if(message.back_event != NULL) {
|
||||
|
|
|
@ -11,6 +11,7 @@ typedef enum {
|
|||
NotificationLayerMessage,
|
||||
InternalLayerMessage,
|
||||
SaveSettingsMessage,
|
||||
LoadSettingsMessage,
|
||||
} NotificationAppMessageType;
|
||||
|
||||
typedef struct {
|
||||
|
|
10
applications/services/region/application.fam
Normal file
10
applications/services/region/application.fam
Normal file
|
@ -0,0 +1,10 @@
|
|||
App(
|
||||
appid="region",
|
||||
name="RegionSrv",
|
||||
apptype=FlipperAppType.STARTUP,
|
||||
targets=["f7"],
|
||||
entry_point="region_on_system_start",
|
||||
cdefines=["SRV_REGION"],
|
||||
requires=["storage"],
|
||||
order=170,
|
||||
)
|
147
applications/services/region/region.c
Normal file
147
applications/services/region/region.c
Normal file
|
@ -0,0 +1,147 @@
|
|||
#include <furi_hal_region.h>
|
||||
|
||||
#include <furi.h>
|
||||
#include <storage/storage.h>
|
||||
|
||||
#include <flipper.pb.h>
|
||||
#include <pb_decode.h>
|
||||
|
||||
#define TAG "RegionSrv"
|
||||
|
||||
#define SUBGHZ_REGION_FILENAME INT_PATH(".region_data")
|
||||
|
||||
static bool region_istream_read(pb_istream_t* istream, pb_byte_t* buf, size_t count) {
|
||||
File* file = istream->state;
|
||||
size_t ret = storage_file_read(file, buf, count);
|
||||
return count == ret;
|
||||
}
|
||||
|
||||
static bool region_istream_decode_band(pb_istream_t* stream, const pb_field_t* field, void** arg) {
|
||||
UNUSED(field);
|
||||
|
||||
FuriHalRegion* region = *arg;
|
||||
|
||||
PB_Region_Band band = {0};
|
||||
if(!pb_decode(stream, PB_Region_Band_fields, &band)) {
|
||||
FURI_LOG_E(TAG, "PB Region band decode error: %s", PB_GET_ERROR(stream));
|
||||
return false;
|
||||
}
|
||||
|
||||
region->bands_count += 1;
|
||||
region = realloc( //-V701
|
||||
region,
|
||||
sizeof(FuriHalRegion) + sizeof(FuriHalRegionBand) * region->bands_count);
|
||||
size_t pos = region->bands_count - 1;
|
||||
region->bands[pos].start = band.start;
|
||||
region->bands[pos].end = band.end;
|
||||
region->bands[pos].power_limit = band.power_limit;
|
||||
region->bands[pos].duty_cycle = band.duty_cycle;
|
||||
*arg = region;
|
||||
|
||||
FURI_LOG_I(
|
||||
TAG,
|
||||
"Add allowed band: start %luHz, stop %luHz, power_limit %ddBm, duty_cycle %u%%",
|
||||
band.start,
|
||||
band.end,
|
||||
band.power_limit,
|
||||
band.duty_cycle);
|
||||
return true;
|
||||
}
|
||||
|
||||
static int32_t region_load_file(void* context) {
|
||||
UNUSED(context);
|
||||
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
File* file = storage_file_alloc(storage);
|
||||
|
||||
PB_Region pb_region = {0};
|
||||
pb_region.bands.funcs.decode = region_istream_decode_band;
|
||||
|
||||
do {
|
||||
FileInfo fileinfo = {0};
|
||||
|
||||
if(storage_common_stat(storage, SUBGHZ_REGION_FILENAME, &fileinfo) != FSE_OK ||
|
||||
fileinfo.size == 0) {
|
||||
FURI_LOG_W(TAG, "Region file missing or empty");
|
||||
break;
|
||||
|
||||
} else if(!storage_file_open(file, SUBGHZ_REGION_FILENAME, FSAM_READ, FSOM_OPEN_EXISTING)) {
|
||||
FURI_LOG_E(TAG, "Failed to open region file");
|
||||
break;
|
||||
}
|
||||
|
||||
pb_istream_t istream = {
|
||||
.callback = region_istream_read,
|
||||
.state = file,
|
||||
.errmsg = NULL,
|
||||
.bytes_left = fileinfo.size,
|
||||
};
|
||||
|
||||
pb_region.bands.arg = malloc(sizeof(FuriHalRegion));
|
||||
|
||||
if(!pb_decode(&istream, PB_Region_fields, &pb_region)) {
|
||||
FURI_LOG_E(TAG, "Failed to decode region file");
|
||||
free(pb_region.bands.arg);
|
||||
break;
|
||||
}
|
||||
|
||||
FuriHalRegion* region = pb_region.bands.arg;
|
||||
|
||||
memcpy(
|
||||
region->country_code,
|
||||
pb_region.country_code->bytes,
|
||||
MIN(pb_region.country_code->size, sizeof(region->country_code) - 1));
|
||||
|
||||
furi_hal_region_set(region);
|
||||
|
||||
FURI_LOG_I(TAG, "Dynamic region set: %s", region->country_code);
|
||||
} while(0);
|
||||
|
||||
pb_release(PB_Region_fields, &pb_region);
|
||||
storage_file_free(file);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void region_loader_pending_callback(void* context, uint32_t arg) {
|
||||
UNUSED(arg);
|
||||
|
||||
FuriThread* loader = context;
|
||||
furi_thread_join(loader);
|
||||
furi_thread_free(loader);
|
||||
}
|
||||
|
||||
static void region_loader_state_callback(FuriThreadState state, void* context) {
|
||||
UNUSED(context);
|
||||
|
||||
if(state == FuriThreadStateStopped) {
|
||||
furi_timer_pending_callback(region_loader_pending_callback, furi_thread_get_current(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void region_storage_callback(const void* message, void* context) {
|
||||
UNUSED(context);
|
||||
const StorageEvent* event = message;
|
||||
|
||||
if(event->type == StorageEventTypeCardMount) {
|
||||
FuriThread* loader = furi_thread_alloc_ex(NULL, 2048, region_load_file, NULL);
|
||||
furi_thread_set_state_callback(loader, region_loader_state_callback);
|
||||
furi_thread_start(loader);
|
||||
}
|
||||
}
|
||||
|
||||
int32_t region_on_system_start(void* p) {
|
||||
UNUSED(p);
|
||||
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
furi_pubsub_subscribe(storage_get_pubsub(storage), region_storage_callback, NULL);
|
||||
|
||||
if(storage_sd_status(storage) != FSE_OK) {
|
||||
FURI_LOG_D(TAG, "SD Card not ready, skipping dynamic region");
|
||||
return 0;
|
||||
}
|
||||
|
||||
region_load_file(NULL);
|
||||
return 0;
|
||||
}
|
|
@ -3,7 +3,6 @@
|
|||
#include "storage_message.h"
|
||||
#include "storage_processing.h"
|
||||
#include "storage/storage_glue.h"
|
||||
#include "storages/storage_int.h"
|
||||
#include "storages/storage_ext.h"
|
||||
#include <assets_icons.h>
|
||||
|
||||
|
@ -42,9 +41,6 @@ Storage* storage_app_alloc(void) {
|
|||
storage_data_timestamp(&app->storage[i]);
|
||||
}
|
||||
|
||||
#ifndef FURI_RAM_EXEC
|
||||
storage_int_init(&app->storage[ST_INT]);
|
||||
#endif
|
||||
storage_ext_init(&app->storage[ST_EXT]);
|
||||
|
||||
// sd icon gui
|
||||
|
@ -106,6 +102,11 @@ int32_t storage_srv(void* p) {
|
|||
Storage* app = storage_app_alloc();
|
||||
furi_record_create(RECORD_STORAGE, app);
|
||||
|
||||
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagStorageFormatInternal)) {
|
||||
FURI_LOG_W(TAG, "Format Internal not supported, clearing flag");
|
||||
furi_hal_rtc_reset_flag(FuriHalRtcFlagStorageFormatInternal);
|
||||
}
|
||||
|
||||
StorageMessage message;
|
||||
while(1) {
|
||||
if(furi_message_queue_get(app->message_queue, &message, STORAGE_TICK) == FuriStatusOk) {
|
||||
|
|
|
@ -506,7 +506,7 @@ FS_Error storage_sd_status(Storage* storage);
|
|||
|
||||
/******************* Internal LFS Functions *******************/
|
||||
|
||||
typedef void (*Storage_name_converter)(FuriString*);
|
||||
typedef void (*StorageNameConverter)(FuriString*);
|
||||
|
||||
/**
|
||||
* @brief Back up the internal storage contents to a *.tar archive.
|
||||
|
@ -526,7 +526,7 @@ FS_Error storage_int_backup(Storage* storage, const char* dstname);
|
|||
* @return FSE_OK if the storage was successfully restored, any other error code on failure.
|
||||
*/
|
||||
FS_Error
|
||||
storage_int_restore(Storage* storage, const char* dstname, Storage_name_converter converter);
|
||||
storage_int_restore(Storage* storage, const char* dstname, StorageNameConverter converter);
|
||||
|
||||
/***************** Simplified Functions ******************/
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ static void storage_cli_info(Cli* cli, FuriString* path, FuriString* args) {
|
|||
storage_cli_print_error(error);
|
||||
} else {
|
||||
printf(
|
||||
"Label: %s\r\nType: LittleFS\r\n%luKiB total\r\n%luKiB free\r\n",
|
||||
"Label: %s\r\nType: Virtual\r\n%luKiB total\r\n%luKiB free\r\n",
|
||||
furi_hal_version_get_name_ptr() ? furi_hal_version_get_name_ptr() : "Unknown",
|
||||
(uint32_t)(total_space / 1024),
|
||||
(uint32_t)(free_space / 1024));
|
||||
|
|
|
@ -14,7 +14,7 @@ FS_Error storage_int_backup(Storage* storage, const char* dstname) {
|
|||
}
|
||||
|
||||
FS_Error
|
||||
storage_int_restore(Storage* storage, const char* srcname, Storage_name_converter converter) {
|
||||
storage_int_restore(Storage* storage, const char* srcname, StorageNameConverter converter) {
|
||||
furi_check(storage);
|
||||
|
||||
TarArchive* archive = tar_archive_alloc(storage);
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
#include <m-list.h>
|
||||
#include <m-dict.h>
|
||||
|
||||
#define TAG "Storage"
|
||||
|
||||
#define STORAGE_PATH_PREFIX_LEN 4u
|
||||
_Static_assert(
|
||||
sizeof(STORAGE_ANY_PATH_PREFIX) == STORAGE_PATH_PREFIX_LEN + 1,
|
||||
|
@ -60,36 +62,27 @@ static StorageType storage_get_type_by_path(FuriString* path) {
|
|||
|
||||
return type;
|
||||
}
|
||||
static void storage_path_change_to_real_storage(FuriString* path, StorageType real_storage) {
|
||||
if(furi_string_search(path, STORAGE_ANY_PATH_PREFIX) == 0) {
|
||||
switch(real_storage) {
|
||||
case ST_EXT:
|
||||
furi_string_replace_at(
|
||||
path, 0, strlen(STORAGE_EXT_PATH_PREFIX), STORAGE_EXT_PATH_PREFIX);
|
||||
break;
|
||||
case ST_INT:
|
||||
furi_string_replace_at(
|
||||
path, 0, strlen(STORAGE_INT_PATH_PREFIX), STORAGE_INT_PATH_PREFIX);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static FS_Error storage_get_data(Storage* app, FuriString* path, StorageData** storage) {
|
||||
StorageType type = storage_get_type_by_path(path);
|
||||
|
||||
if(storage_type_is_valid(type)) {
|
||||
// Any storage phase-out: redirect "/any" to "/ext"
|
||||
if(type == ST_ANY) {
|
||||
type = ST_INT;
|
||||
if(storage_data_status(&app->storage[ST_EXT]) == StorageStatusOK) {
|
||||
FURI_LOG_W(
|
||||
TAG,
|
||||
STORAGE_ANY_PATH_PREFIX " is deprecated, use " STORAGE_EXT_PATH_PREFIX " instead");
|
||||
furi_string_replace_at(
|
||||
path, 0, strlen(STORAGE_EXT_PATH_PREFIX), STORAGE_EXT_PATH_PREFIX);
|
||||
type = ST_EXT;
|
||||
}
|
||||
storage_path_change_to_real_storage(path, type);
|
||||
|
||||
furi_assert(type == ST_EXT);
|
||||
|
||||
if(storage_data_status(&app->storage[type]) != StorageStatusOK) {
|
||||
return FSE_NOT_READY;
|
||||
}
|
||||
|
||||
furi_assert(type == ST_EXT || type == ST_INT);
|
||||
*storage = &app->storage[type];
|
||||
|
||||
return FSE_OK;
|
||||
|
@ -559,6 +552,16 @@ void storage_process_alias(
|
|||
furi_string_get_cstr(apps_assets_path_with_appsid));
|
||||
|
||||
furi_string_free(apps_assets_path_with_appsid);
|
||||
|
||||
} else if(furi_string_start_with(path, STORAGE_INT_PATH_PREFIX)) {
|
||||
furi_string_replace_at(
|
||||
path, 0, strlen(STORAGE_INT_PATH_PREFIX), STORAGE_EXT_PATH_PREFIX "/.int");
|
||||
|
||||
FuriString* int_on_ext_path = furi_string_alloc_set(STORAGE_EXT_PATH_PREFIX "/.int");
|
||||
if(storage_process_common_stat(app, int_on_ext_path, NULL) != FSE_OK) {
|
||||
storage_process_common_mkdir(app, int_on_ext_path);
|
||||
}
|
||||
furi_string_free(int_on_ext_path);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,744 +0,0 @@
|
|||
#include "storage_int.h"
|
||||
#include <lfs.h>
|
||||
#include <furi_hal.h>
|
||||
#include <toolbox/path.h>
|
||||
|
||||
#define TAG "StorageInt"
|
||||
|
||||
#define STORAGE_PATH STORAGE_INT_PATH_PREFIX
|
||||
#define LFS_CLEAN_FINGERPRINT 0
|
||||
|
||||
/* When less than LFS_RESERVED_PAGES_COUNT are left free, creation &
|
||||
* modification of non-dot files is restricted */
|
||||
#define LFS_RESERVED_PAGES_COUNT 3
|
||||
|
||||
typedef struct {
|
||||
const size_t start_address;
|
||||
const size_t start_page;
|
||||
struct lfs_config config;
|
||||
lfs_t lfs;
|
||||
} LFSData;
|
||||
|
||||
typedef struct {
|
||||
void* data;
|
||||
bool open;
|
||||
} LFSHandle;
|
||||
|
||||
static LFSHandle* lfs_handle_alloc_file(void) {
|
||||
LFSHandle* handle = malloc(sizeof(LFSHandle));
|
||||
handle->data = malloc(sizeof(lfs_file_t));
|
||||
return handle;
|
||||
}
|
||||
|
||||
static LFSHandle* lfs_handle_alloc_dir(void) {
|
||||
LFSHandle* handle = malloc(sizeof(LFSHandle));
|
||||
handle->data = malloc(sizeof(lfs_dir_t));
|
||||
return handle;
|
||||
}
|
||||
|
||||
/* INTERNALS */
|
||||
|
||||
static lfs_dir_t* lfs_handle_get_dir(LFSHandle* handle) {
|
||||
return handle->data;
|
||||
}
|
||||
|
||||
static lfs_file_t* lfs_handle_get_file(LFSHandle* handle) {
|
||||
return handle->data;
|
||||
}
|
||||
|
||||
static void lfs_handle_free(LFSHandle* handle) {
|
||||
free(handle->data);
|
||||
free(handle);
|
||||
}
|
||||
|
||||
static void lfs_handle_set_open(LFSHandle* handle) {
|
||||
handle->open = true;
|
||||
}
|
||||
|
||||
static bool lfs_handle_is_open(LFSHandle* handle) {
|
||||
return handle->open;
|
||||
}
|
||||
|
||||
static lfs_t* lfs_get_from_storage(StorageData* storage) {
|
||||
return &((LFSData*)storage->data)->lfs;
|
||||
}
|
||||
|
||||
static LFSData* lfs_data_get_from_storage(StorageData* storage) {
|
||||
return (LFSData*)storage->data;
|
||||
}
|
||||
|
||||
static int storage_int_device_read(
|
||||
const struct lfs_config* c,
|
||||
lfs_block_t block,
|
||||
lfs_off_t off,
|
||||
void* buffer,
|
||||
lfs_size_t size) {
|
||||
LFSData* lfs_data = c->context;
|
||||
size_t address = lfs_data->start_address + block * c->block_size + off;
|
||||
|
||||
FURI_LOG_T(
|
||||
TAG,
|
||||
"Device read: block %lu, off %lu, buffer: %p, size %lu, translated address: %p",
|
||||
block,
|
||||
off,
|
||||
buffer,
|
||||
size,
|
||||
(void*)address);
|
||||
|
||||
memcpy(buffer, (void*)address, size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int storage_int_device_prog(
|
||||
const struct lfs_config* c,
|
||||
lfs_block_t block,
|
||||
lfs_off_t off,
|
||||
const void* buffer,
|
||||
lfs_size_t size) {
|
||||
LFSData* lfs_data = c->context;
|
||||
size_t address = lfs_data->start_address + block * c->block_size + off;
|
||||
|
||||
FURI_LOG_T(
|
||||
TAG,
|
||||
"Device prog: block %lu, off %lu, buffer: %p, size %lu, translated address: %p",
|
||||
block,
|
||||
off,
|
||||
buffer,
|
||||
size,
|
||||
(void*)address);
|
||||
|
||||
int ret = 0;
|
||||
while(size > 0) {
|
||||
furi_hal_flash_write_dword(address, *(uint64_t*)buffer);
|
||||
address += c->prog_size;
|
||||
buffer += c->prog_size;
|
||||
size -= c->prog_size;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int storage_int_device_erase(const struct lfs_config* c, lfs_block_t block) {
|
||||
LFSData* lfs_data = c->context;
|
||||
size_t page = lfs_data->start_page + block;
|
||||
|
||||
FURI_LOG_D(TAG, "Device erase: page %lu, translated page: %zx", block, page);
|
||||
|
||||
furi_hal_flash_erase(page);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int storage_int_device_sync(const struct lfs_config* c) {
|
||||
UNUSED(c);
|
||||
FURI_LOG_D(TAG, "Device sync: skipping");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static LFSData* storage_int_lfs_data_alloc(void) {
|
||||
LFSData* lfs_data = malloc(sizeof(LFSData));
|
||||
|
||||
// Internal storage start address
|
||||
*(size_t*)(&lfs_data->start_address) = furi_hal_flash_get_free_page_start_address();
|
||||
*(size_t*)(&lfs_data->start_page) =
|
||||
(lfs_data->start_address - furi_hal_flash_get_base()) / furi_hal_flash_get_page_size();
|
||||
|
||||
// LFS configuration
|
||||
// Glue and context
|
||||
lfs_data->config.context = lfs_data;
|
||||
lfs_data->config.read = storage_int_device_read;
|
||||
lfs_data->config.prog = storage_int_device_prog;
|
||||
lfs_data->config.erase = storage_int_device_erase;
|
||||
lfs_data->config.sync = storage_int_device_sync;
|
||||
|
||||
// Block device description
|
||||
lfs_data->config.read_size = furi_hal_flash_get_read_block_size();
|
||||
lfs_data->config.prog_size = furi_hal_flash_get_write_block_size();
|
||||
lfs_data->config.block_size = furi_hal_flash_get_page_size();
|
||||
lfs_data->config.block_count = furi_hal_flash_get_free_page_count();
|
||||
lfs_data->config.block_cycles = furi_hal_flash_get_cycles_count();
|
||||
lfs_data->config.cache_size = 16;
|
||||
lfs_data->config.lookahead_size = 16;
|
||||
|
||||
return lfs_data;
|
||||
}
|
||||
|
||||
// Returns true if fingerprint was invalid and LFS reformatting is needed
|
||||
static bool storage_int_check_and_set_fingerprint(LFSData* lfs_data) {
|
||||
bool value = false;
|
||||
|
||||
uint32_t os_fingerprint = 0;
|
||||
os_fingerprint |= ((lfs_data->start_page & 0xFF) << 0);
|
||||
os_fingerprint |= ((lfs_data->config.block_count & 0xFF) << 8);
|
||||
os_fingerprint |= ((LFS_DISK_VERSION_MAJOR & 0xFFFF) << 16);
|
||||
|
||||
uint32_t rtc_fingerprint = furi_hal_rtc_get_register(FuriHalRtcRegisterLfsFingerprint);
|
||||
if(rtc_fingerprint == LFS_CLEAN_FINGERPRINT) {
|
||||
FURI_LOG_I(TAG, "Storing LFS fingerprint in RTC");
|
||||
furi_hal_rtc_set_register(FuriHalRtcRegisterLfsFingerprint, os_fingerprint);
|
||||
} else if(rtc_fingerprint != os_fingerprint) {
|
||||
FURI_LOG_E(TAG, "LFS fingerprint mismatch");
|
||||
furi_hal_rtc_set_register(FuriHalRtcRegisterLfsFingerprint, os_fingerprint);
|
||||
value = true;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static void storage_int_lfs_mount(LFSData* lfs_data, StorageData* storage) {
|
||||
int err;
|
||||
lfs_t* lfs = &lfs_data->lfs;
|
||||
|
||||
bool was_fingerprint_outdated = storage_int_check_and_set_fingerprint(lfs_data);
|
||||
bool need_format = furi_hal_rtc_is_flag_set(FuriHalRtcFlagStorageFormatInternal) ||
|
||||
was_fingerprint_outdated;
|
||||
|
||||
if(need_format) {
|
||||
// Format storage
|
||||
err = lfs_format(lfs, &lfs_data->config);
|
||||
if(err == 0) {
|
||||
FURI_LOG_I(TAG, "Factory reset: Format successful, trying to mount");
|
||||
furi_hal_rtc_reset_flag(FuriHalRtcFlagStorageFormatInternal);
|
||||
err = lfs_mount(lfs, &lfs_data->config);
|
||||
if(err == 0) {
|
||||
FURI_LOG_I(TAG, "Factory reset: Mounted");
|
||||
storage->status = StorageStatusOK;
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "Factory reset: Mount after format failed");
|
||||
storage->status = StorageStatusNotMounted;
|
||||
}
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "Factory reset: Format failed");
|
||||
storage->status = StorageStatusNoFS;
|
||||
}
|
||||
} else {
|
||||
// Normal
|
||||
err = lfs_mount(lfs, &lfs_data->config);
|
||||
if(err == 0) {
|
||||
FURI_LOG_I(TAG, "Mounted");
|
||||
storage->status = StorageStatusOK;
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "Mount failed, formatting");
|
||||
err = lfs_format(lfs, &lfs_data->config);
|
||||
if(err == 0) {
|
||||
FURI_LOG_I(TAG, "Format successful, trying to mount");
|
||||
err = lfs_mount(lfs, &lfs_data->config);
|
||||
if(err == 0) {
|
||||
FURI_LOG_I(TAG, "Mounted");
|
||||
storage->status = StorageStatusOK;
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "Mount after format failed");
|
||||
storage->status = StorageStatusNotMounted;
|
||||
}
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "Format failed");
|
||||
storage->status = StorageStatusNoFS;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/****************** Common Functions ******************/
|
||||
|
||||
static FS_Error storage_int_parse_error(int error) {
|
||||
FS_Error result;
|
||||
|
||||
if(error >= LFS_ERR_OK) {
|
||||
result = FSE_OK;
|
||||
} else {
|
||||
switch(error) {
|
||||
case LFS_ERR_NOENT:
|
||||
result = FSE_NOT_EXIST;
|
||||
break;
|
||||
case LFS_ERR_EXIST:
|
||||
result = FSE_EXIST;
|
||||
break;
|
||||
case LFS_ERR_NOTEMPTY:
|
||||
result = FSE_DENIED;
|
||||
break;
|
||||
case LFS_ERR_INVAL:
|
||||
case LFS_ERR_NOATTR:
|
||||
result = FSE_INVALID_PARAMETER;
|
||||
break;
|
||||
case LFS_ERR_BADF:
|
||||
case LFS_ERR_ISDIR:
|
||||
case LFS_ERR_NOTDIR:
|
||||
case LFS_ERR_NAMETOOLONG:
|
||||
result = FSE_INVALID_NAME;
|
||||
break;
|
||||
case LFS_ERR_IO:
|
||||
case LFS_ERR_FBIG:
|
||||
case LFS_ERR_NOSPC:
|
||||
case LFS_ERR_NOMEM:
|
||||
case LFS_ERR_CORRUPT:
|
||||
default:
|
||||
result = FSE_INTERNAL;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Returns false if less than reserved space is left free */
|
||||
static bool storage_int_check_for_free_space(StorageData* storage) {
|
||||
LFSData* lfs_data = lfs_data_get_from_storage(storage);
|
||||
|
||||
lfs_ssize_t result = lfs_fs_size(lfs_get_from_storage(storage));
|
||||
if(result >= 0) {
|
||||
lfs_size_t free_space =
|
||||
(lfs_data->config.block_count - result) * lfs_data->config.block_size;
|
||||
|
||||
return free_space > LFS_RESERVED_PAGES_COUNT * furi_hal_flash_get_page_size();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
/******************* File Functions *******************/
|
||||
|
||||
static bool storage_int_file_open(
|
||||
void* ctx,
|
||||
File* file,
|
||||
const char* path,
|
||||
FS_AccessMode access_mode,
|
||||
FS_OpenMode open_mode) {
|
||||
StorageData* storage = ctx;
|
||||
lfs_t* lfs = lfs_get_from_storage(storage);
|
||||
|
||||
bool enough_free_space = storage_int_check_for_free_space(storage);
|
||||
|
||||
int flags = 0;
|
||||
|
||||
if(access_mode & FSAM_READ) flags |= LFS_O_RDONLY;
|
||||
if(access_mode & FSAM_WRITE) flags |= LFS_O_WRONLY;
|
||||
|
||||
if(open_mode & FSOM_OPEN_EXISTING) flags |= 0;
|
||||
if(open_mode & FSOM_OPEN_ALWAYS) flags |= LFS_O_CREAT;
|
||||
if(open_mode & FSOM_OPEN_APPEND) flags |= LFS_O_CREAT | LFS_O_APPEND;
|
||||
if(open_mode & FSOM_CREATE_NEW) flags |= LFS_O_CREAT | LFS_O_EXCL;
|
||||
if(open_mode & FSOM_CREATE_ALWAYS) flags |= LFS_O_CREAT | LFS_O_TRUNC;
|
||||
|
||||
LFSHandle* handle = lfs_handle_alloc_file();
|
||||
storage_set_storage_file_data(file, handle, storage);
|
||||
|
||||
if(!enough_free_space) {
|
||||
FuriString* filename;
|
||||
filename = furi_string_alloc();
|
||||
path_extract_basename(path, filename);
|
||||
bool is_dot_file =
|
||||
(!furi_string_empty(filename) && (furi_string_get_char(filename, 0) == '.'));
|
||||
furi_string_free(filename);
|
||||
|
||||
/* Restrict write & creation access to all non-dot files */
|
||||
if(!is_dot_file && (flags & (LFS_O_CREAT | LFS_O_WRONLY))) {
|
||||
file->internal_error_id = LFS_ERR_NOSPC;
|
||||
file->error_id = FSE_DENIED;
|
||||
FURI_LOG_W(TAG, "Denied access to '%s': no free space", path);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
file->internal_error_id = lfs_file_open(lfs, lfs_handle_get_file(handle), path, flags);
|
||||
|
||||
if(file->internal_error_id >= LFS_ERR_OK) {
|
||||
lfs_handle_set_open(handle);
|
||||
}
|
||||
|
||||
file->error_id = storage_int_parse_error(file->internal_error_id);
|
||||
|
||||
return file->error_id == FSE_OK;
|
||||
}
|
||||
|
||||
static bool storage_int_file_close(void* ctx, File* file) {
|
||||
StorageData* storage = ctx;
|
||||
lfs_t* lfs = lfs_get_from_storage(storage);
|
||||
LFSHandle* handle = storage_get_storage_file_data(file, storage);
|
||||
|
||||
if(lfs_handle_is_open(handle)) {
|
||||
file->internal_error_id = lfs_file_close(lfs, lfs_handle_get_file(handle));
|
||||
} else {
|
||||
file->internal_error_id = LFS_ERR_BADF;
|
||||
}
|
||||
|
||||
file->error_id = storage_int_parse_error(file->internal_error_id);
|
||||
lfs_handle_free(handle);
|
||||
return file->error_id == FSE_OK;
|
||||
}
|
||||
|
||||
static uint16_t
|
||||
storage_int_file_read(void* ctx, File* file, void* buff, uint16_t const bytes_to_read) {
|
||||
StorageData* storage = ctx;
|
||||
lfs_t* lfs = lfs_get_from_storage(storage);
|
||||
LFSHandle* handle = storage_get_storage_file_data(file, storage);
|
||||
|
||||
uint16_t bytes_read = 0;
|
||||
|
||||
if(lfs_handle_is_open(handle)) {
|
||||
file->internal_error_id =
|
||||
lfs_file_read(lfs, lfs_handle_get_file(handle), buff, bytes_to_read);
|
||||
} else {
|
||||
file->internal_error_id = LFS_ERR_BADF;
|
||||
}
|
||||
|
||||
file->error_id = storage_int_parse_error(file->internal_error_id);
|
||||
|
||||
if(file->error_id == FSE_OK) {
|
||||
bytes_read = file->internal_error_id;
|
||||
file->internal_error_id = 0;
|
||||
}
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
static uint16_t
|
||||
storage_int_file_write(void* ctx, File* file, const void* buff, uint16_t const bytes_to_write) {
|
||||
StorageData* storage = ctx;
|
||||
lfs_t* lfs = lfs_get_from_storage(storage);
|
||||
LFSHandle* handle = storage_get_storage_file_data(file, storage);
|
||||
|
||||
uint16_t bytes_written = 0;
|
||||
|
||||
if(lfs_handle_is_open(handle)) {
|
||||
file->internal_error_id =
|
||||
lfs_file_write(lfs, lfs_handle_get_file(handle), buff, bytes_to_write);
|
||||
} else {
|
||||
file->internal_error_id = LFS_ERR_BADF;
|
||||
}
|
||||
|
||||
file->error_id = storage_int_parse_error(file->internal_error_id);
|
||||
|
||||
if(file->error_id == FSE_OK) {
|
||||
bytes_written = file->internal_error_id;
|
||||
file->internal_error_id = 0;
|
||||
}
|
||||
return bytes_written;
|
||||
}
|
||||
|
||||
static bool
|
||||
storage_int_file_seek(void* ctx, File* file, const uint32_t offset, const bool from_start) {
|
||||
StorageData* storage = ctx;
|
||||
lfs_t* lfs = lfs_get_from_storage(storage);
|
||||
LFSHandle* handle = storage_get_storage_file_data(file, storage);
|
||||
|
||||
if(lfs_handle_is_open(handle)) {
|
||||
if(from_start) {
|
||||
file->internal_error_id =
|
||||
lfs_file_seek(lfs, lfs_handle_get_file(handle), offset, LFS_SEEK_SET);
|
||||
} else {
|
||||
file->internal_error_id =
|
||||
lfs_file_seek(lfs, lfs_handle_get_file(handle), offset, LFS_SEEK_CUR);
|
||||
}
|
||||
} else {
|
||||
file->internal_error_id = LFS_ERR_BADF;
|
||||
}
|
||||
|
||||
file->error_id = storage_int_parse_error(file->internal_error_id);
|
||||
return file->error_id == FSE_OK;
|
||||
}
|
||||
|
||||
static uint64_t storage_int_file_tell(void* ctx, File* file) {
|
||||
StorageData* storage = ctx;
|
||||
lfs_t* lfs = lfs_get_from_storage(storage);
|
||||
LFSHandle* handle = storage_get_storage_file_data(file, storage);
|
||||
|
||||
if(lfs_handle_is_open(handle)) {
|
||||
file->internal_error_id = lfs_file_tell(lfs, lfs_handle_get_file(handle));
|
||||
} else {
|
||||
file->internal_error_id = LFS_ERR_BADF;
|
||||
}
|
||||
|
||||
file->error_id = storage_int_parse_error(file->internal_error_id);
|
||||
|
||||
int32_t position = 0;
|
||||
if(file->error_id == FSE_OK) {
|
||||
position = file->internal_error_id;
|
||||
file->internal_error_id = 0;
|
||||
}
|
||||
|
||||
return position;
|
||||
}
|
||||
|
||||
static bool storage_int_file_truncate(void* ctx, File* file) {
|
||||
StorageData* storage = ctx;
|
||||
lfs_t* lfs = lfs_get_from_storage(storage);
|
||||
LFSHandle* handle = storage_get_storage_file_data(file, storage);
|
||||
|
||||
if(lfs_handle_is_open(handle)) {
|
||||
file->internal_error_id = lfs_file_tell(lfs, lfs_handle_get_file(handle));
|
||||
file->error_id = storage_int_parse_error(file->internal_error_id);
|
||||
|
||||
if(file->error_id == FSE_OK) {
|
||||
uint32_t position = file->internal_error_id;
|
||||
file->internal_error_id =
|
||||
lfs_file_truncate(lfs, lfs_handle_get_file(handle), position);
|
||||
file->error_id = storage_int_parse_error(file->internal_error_id);
|
||||
}
|
||||
} else {
|
||||
file->internal_error_id = LFS_ERR_BADF;
|
||||
file->error_id = storage_int_parse_error(file->internal_error_id);
|
||||
}
|
||||
|
||||
return file->error_id == FSE_OK;
|
||||
}
|
||||
|
||||
static bool storage_int_file_sync(void* ctx, File* file) {
|
||||
StorageData* storage = ctx;
|
||||
lfs_t* lfs = lfs_get_from_storage(storage);
|
||||
LFSHandle* handle = storage_get_storage_file_data(file, storage);
|
||||
|
||||
if(lfs_handle_is_open(handle)) {
|
||||
file->internal_error_id = lfs_file_sync(lfs, lfs_handle_get_file(handle));
|
||||
} else {
|
||||
file->internal_error_id = LFS_ERR_BADF;
|
||||
}
|
||||
|
||||
file->error_id = storage_int_parse_error(file->internal_error_id);
|
||||
return file->error_id == FSE_OK;
|
||||
}
|
||||
|
||||
static uint64_t storage_int_file_size(void* ctx, File* file) {
|
||||
StorageData* storage = ctx;
|
||||
lfs_t* lfs = lfs_get_from_storage(storage);
|
||||
LFSHandle* handle = storage_get_storage_file_data(file, storage);
|
||||
|
||||
if(lfs_handle_is_open(handle)) {
|
||||
file->internal_error_id = lfs_file_size(lfs, lfs_handle_get_file(handle));
|
||||
} else {
|
||||
file->internal_error_id = LFS_ERR_BADF;
|
||||
}
|
||||
|
||||
file->error_id = storage_int_parse_error(file->internal_error_id);
|
||||
|
||||
uint32_t size = 0;
|
||||
if(file->error_id == FSE_OK) {
|
||||
size = file->internal_error_id;
|
||||
file->internal_error_id = 0;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static bool storage_int_file_eof(void* ctx, File* file) {
|
||||
StorageData* storage = ctx;
|
||||
lfs_t* lfs = lfs_get_from_storage(storage);
|
||||
LFSHandle* handle = storage_get_storage_file_data(file, storage);
|
||||
|
||||
bool eof = true;
|
||||
|
||||
if(lfs_handle_is_open(handle)) {
|
||||
int32_t position = lfs_file_tell(lfs, lfs_handle_get_file(handle));
|
||||
int32_t size = lfs_file_size(lfs, lfs_handle_get_file(handle));
|
||||
|
||||
if(position < 0) {
|
||||
file->internal_error_id = position;
|
||||
} else if(size < 0) {
|
||||
file->internal_error_id = size;
|
||||
} else {
|
||||
file->internal_error_id = LFS_ERR_OK;
|
||||
eof = (position >= size);
|
||||
}
|
||||
} else {
|
||||
file->internal_error_id = LFS_ERR_BADF;
|
||||
}
|
||||
|
||||
file->error_id = storage_int_parse_error(file->internal_error_id);
|
||||
return eof;
|
||||
}
|
||||
|
||||
/******************* Dir Functions *******************/
|
||||
|
||||
static bool storage_int_dir_open(void* ctx, File* file, const char* path) {
|
||||
StorageData* storage = ctx;
|
||||
lfs_t* lfs = lfs_get_from_storage(storage);
|
||||
|
||||
LFSHandle* handle = lfs_handle_alloc_dir();
|
||||
storage_set_storage_file_data(file, handle, storage);
|
||||
|
||||
file->internal_error_id = lfs_dir_open(lfs, lfs_handle_get_dir(handle), path);
|
||||
if(file->internal_error_id >= LFS_ERR_OK) {
|
||||
lfs_handle_set_open(handle);
|
||||
}
|
||||
|
||||
file->error_id = storage_int_parse_error(file->internal_error_id);
|
||||
return file->error_id == FSE_OK;
|
||||
}
|
||||
|
||||
static bool storage_int_dir_close(void* ctx, File* file) {
|
||||
StorageData* storage = ctx;
|
||||
lfs_t* lfs = lfs_get_from_storage(storage);
|
||||
LFSHandle* handle = storage_get_storage_file_data(file, storage);
|
||||
|
||||
if(lfs_handle_is_open(handle)) {
|
||||
file->internal_error_id = lfs_dir_close(lfs, lfs_handle_get_dir(handle));
|
||||
} else {
|
||||
file->internal_error_id = LFS_ERR_BADF;
|
||||
}
|
||||
|
||||
file->error_id = storage_int_parse_error(file->internal_error_id);
|
||||
lfs_handle_free(handle);
|
||||
return file->error_id == FSE_OK;
|
||||
}
|
||||
|
||||
static bool storage_int_dir_read(
|
||||
void* ctx,
|
||||
File* file,
|
||||
FileInfo* fileinfo,
|
||||
char* name,
|
||||
const uint16_t name_length) {
|
||||
StorageData* storage = ctx;
|
||||
lfs_t* lfs = lfs_get_from_storage(storage);
|
||||
LFSHandle* handle = storage_get_storage_file_data(file, storage);
|
||||
|
||||
if(lfs_handle_is_open(handle)) {
|
||||
struct lfs_info _fileinfo;
|
||||
|
||||
// LFS returns virtual directories "." and "..", so we read until we get something meaningful or an empty string
|
||||
do {
|
||||
file->internal_error_id = lfs_dir_read(lfs, lfs_handle_get_dir(handle), &_fileinfo);
|
||||
file->error_id = storage_int_parse_error(file->internal_error_id);
|
||||
} while(strcmp(_fileinfo.name, ".") == 0 || strcmp(_fileinfo.name, "..") == 0);
|
||||
|
||||
if(fileinfo != NULL) {
|
||||
fileinfo->size = _fileinfo.size;
|
||||
fileinfo->flags = 0;
|
||||
if(_fileinfo.type & LFS_TYPE_DIR) fileinfo->flags |= FSF_DIRECTORY;
|
||||
}
|
||||
|
||||
if(name != NULL) {
|
||||
snprintf(name, name_length, "%s", _fileinfo.name);
|
||||
}
|
||||
|
||||
// set FSE_NOT_EXIST error on end of directory
|
||||
if(file->internal_error_id == 0) {
|
||||
file->error_id = FSE_NOT_EXIST;
|
||||
}
|
||||
} else {
|
||||
file->internal_error_id = LFS_ERR_BADF;
|
||||
file->error_id = storage_int_parse_error(file->internal_error_id);
|
||||
}
|
||||
|
||||
return file->error_id == FSE_OK;
|
||||
}
|
||||
|
||||
static bool storage_int_dir_rewind(void* ctx, File* file) {
|
||||
StorageData* storage = ctx;
|
||||
lfs_t* lfs = lfs_get_from_storage(storage);
|
||||
LFSHandle* handle = storage_get_storage_file_data(file, storage);
|
||||
|
||||
if(lfs_handle_is_open(handle)) {
|
||||
file->internal_error_id = lfs_dir_rewind(lfs, lfs_handle_get_dir(handle));
|
||||
} else {
|
||||
file->internal_error_id = LFS_ERR_BADF;
|
||||
}
|
||||
|
||||
file->error_id = storage_int_parse_error(file->internal_error_id);
|
||||
return file->error_id == FSE_OK;
|
||||
}
|
||||
|
||||
/******************* Common FS Functions *******************/
|
||||
|
||||
static FS_Error storage_int_common_stat(void* ctx, const char* path, FileInfo* fileinfo) {
|
||||
StorageData* storage = ctx;
|
||||
lfs_t* lfs = lfs_get_from_storage(storage);
|
||||
struct lfs_info _fileinfo;
|
||||
int result = lfs_stat(lfs, path, &_fileinfo);
|
||||
|
||||
if(fileinfo != NULL) {
|
||||
fileinfo->size = _fileinfo.size;
|
||||
fileinfo->flags = 0;
|
||||
if(_fileinfo.type & LFS_TYPE_DIR) fileinfo->flags |= FSF_DIRECTORY;
|
||||
}
|
||||
|
||||
return storage_int_parse_error(result);
|
||||
}
|
||||
|
||||
static FS_Error storage_int_common_remove(void* ctx, const char* path) {
|
||||
StorageData* storage = ctx;
|
||||
lfs_t* lfs = lfs_get_from_storage(storage);
|
||||
int result = lfs_remove(lfs, path);
|
||||
return storage_int_parse_error(result);
|
||||
}
|
||||
|
||||
static FS_Error storage_int_common_mkdir(void* ctx, const char* path) {
|
||||
StorageData* storage = ctx;
|
||||
lfs_t* lfs = lfs_get_from_storage(storage);
|
||||
int result = lfs_mkdir(lfs, path);
|
||||
return storage_int_parse_error(result);
|
||||
}
|
||||
|
||||
static FS_Error storage_int_common_fs_info(
|
||||
void* ctx,
|
||||
const char* fs_path,
|
||||
uint64_t* total_space,
|
||||
uint64_t* free_space) {
|
||||
UNUSED(fs_path);
|
||||
StorageData* storage = ctx;
|
||||
|
||||
lfs_t* lfs = lfs_get_from_storage(storage);
|
||||
LFSData* lfs_data = lfs_data_get_from_storage(storage);
|
||||
|
||||
if(total_space) {
|
||||
*total_space = lfs_data->config.block_size * lfs_data->config.block_count;
|
||||
}
|
||||
|
||||
lfs_ssize_t result = lfs_fs_size(lfs);
|
||||
if(free_space && (result >= 0)) {
|
||||
*free_space = (lfs_data->config.block_count - result) * lfs_data->config.block_size;
|
||||
}
|
||||
|
||||
return storage_int_parse_error(result);
|
||||
}
|
||||
|
||||
static bool storage_int_common_equivalent_path(const char* path1, const char* path2) {
|
||||
return strcmp(path1, path2) == 0;
|
||||
}
|
||||
|
||||
/******************* Init Storage *******************/
|
||||
static const FS_Api fs_api = {
|
||||
.file =
|
||||
{
|
||||
.open = storage_int_file_open,
|
||||
.close = storage_int_file_close,
|
||||
.read = storage_int_file_read,
|
||||
.write = storage_int_file_write,
|
||||
.seek = storage_int_file_seek,
|
||||
.tell = storage_int_file_tell,
|
||||
.truncate = storage_int_file_truncate,
|
||||
.size = storage_int_file_size,
|
||||
.sync = storage_int_file_sync,
|
||||
.eof = storage_int_file_eof,
|
||||
},
|
||||
.dir =
|
||||
{
|
||||
.open = storage_int_dir_open,
|
||||
.close = storage_int_dir_close,
|
||||
.read = storage_int_dir_read,
|
||||
.rewind = storage_int_dir_rewind,
|
||||
},
|
||||
.common =
|
||||
{
|
||||
.stat = storage_int_common_stat,
|
||||
.mkdir = storage_int_common_mkdir,
|
||||
.remove = storage_int_common_remove,
|
||||
.fs_info = storage_int_common_fs_info,
|
||||
.equivalent_path = storage_int_common_equivalent_path,
|
||||
},
|
||||
};
|
||||
|
||||
void storage_int_init(StorageData* storage) {
|
||||
FURI_LOG_I(TAG, "Starting");
|
||||
LFSData* lfs_data = storage_int_lfs_data_alloc();
|
||||
FURI_LOG_I(
|
||||
TAG,
|
||||
"Config: start %p, read %lu, write %lu, page size: %lu, page count: %lu, cycles: %ld",
|
||||
(void*)lfs_data->start_address,
|
||||
lfs_data->config.read_size,
|
||||
lfs_data->config.prog_size,
|
||||
lfs_data->config.block_size,
|
||||
lfs_data->config.block_count,
|
||||
lfs_data->config.block_cycles);
|
||||
|
||||
storage_int_lfs_mount(lfs_data, storage);
|
||||
|
||||
storage->data = lfs_data;
|
||||
storage->api.tick = NULL;
|
||||
storage->fs_api = &fs_api;
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
#pragma once
|
||||
#include <furi.h>
|
||||
#include "../storage_glue.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void storage_int_init(StorageData* storage);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
|
@ -15,8 +15,6 @@ static bool bt_settings_back_event_callback(void* context) {
|
|||
BtSettingsApp* bt_settings_app_alloc(void) {
|
||||
BtSettingsApp* app = malloc(sizeof(BtSettingsApp));
|
||||
|
||||
// Load settings
|
||||
bt_settings_load(&app->settings);
|
||||
app->gui = furi_record_open(RECORD_GUI);
|
||||
app->bt = furi_record_open(RECORD_BT);
|
||||
|
||||
|
@ -48,6 +46,8 @@ BtSettingsApp* bt_settings_app_alloc(void) {
|
|||
view_dispatcher_add_view(
|
||||
app->view_dispatcher, BtSettingsAppViewPopup, popup_get_view(app->popup));
|
||||
|
||||
bt_get_settings(app->bt, &app->settings);
|
||||
|
||||
// Set first scene
|
||||
scene_manager_next_scene(app->scene_manager, BtSettingsAppSceneStart);
|
||||
return app;
|
||||
|
@ -55,6 +55,7 @@ BtSettingsApp* bt_settings_app_alloc(void) {
|
|||
|
||||
void bt_settings_app_free(BtSettingsApp* app) {
|
||||
furi_assert(app);
|
||||
bt_set_settings(app->bt, &app->settings);
|
||||
// Gui modules
|
||||
view_dispatcher_remove_view(app->view_dispatcher, BtSettingsAppViewVarItemList);
|
||||
variable_item_list_free(app->var_item_list);
|
||||
|
@ -79,7 +80,6 @@ extern int32_t bt_settings_app(void* p) {
|
|||
UNUSED(p);
|
||||
BtSettingsApp* app = bt_settings_app_alloc();
|
||||
view_dispatcher_run(app->view_dispatcher);
|
||||
bt_settings_save(&app->settings);
|
||||
bt_settings_app_free(app);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -1,18 +1,21 @@
|
|||
#pragma once
|
||||
|
||||
#include <furi.h>
|
||||
#include <bt/bt_service/bt.h>
|
||||
|
||||
#include <gui/gui.h>
|
||||
#include <gui/view.h>
|
||||
#include <gui/view_dispatcher.h>
|
||||
#include <gui/scene_manager.h>
|
||||
#include <assets_icons.h>
|
||||
|
||||
#include <gui/modules/variable_item_list.h>
|
||||
#include <gui/modules/dialog_ex.h>
|
||||
#include <gui/modules/popup.h>
|
||||
|
||||
#include <bt/bt_settings.h>
|
||||
#include <bt/bt_service/bt.h>
|
||||
#include <bt/bt_service/bt_settings_api_i.h>
|
||||
|
||||
#include <assets_icons.h>
|
||||
|
||||
#include "scenes/bt_settings_scene.h"
|
||||
|
||||
enum BtSettingsCustomEvent {
|
||||
|
|
|
@ -70,18 +70,17 @@ bool bt_settings_scene_start_on_event(void* context, SceneManagerEvent event) {
|
|||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == BtSettingOn) {
|
||||
furi_hal_bt_start_advertising();
|
||||
app->settings.enabled = true;
|
||||
consumed = true;
|
||||
} else if(event.event == BtSettingOff) {
|
||||
app->settings.enabled = false;
|
||||
furi_hal_bt_stop_advertising();
|
||||
consumed = true;
|
||||
} else if(event.event == BtSettingsCustomEventForgetDevices) {
|
||||
scene_manager_next_scene(app->scene_manager, BtSettingsAppSceneForgetDevConfirm);
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,9 +5,11 @@
|
|||
#include <flipper_format/flipper_format.h>
|
||||
#include <power/power_service/power.h>
|
||||
|
||||
#include <desktop/desktop.h>
|
||||
#include <desktop/views/desktop_view_pin_input.h>
|
||||
|
||||
#include "desktop_settings_app.h"
|
||||
#include "scenes/desktop_settings_scene.h"
|
||||
#include <desktop/views/desktop_view_pin_input.h>
|
||||
|
||||
static bool desktop_settings_custom_event_callback(void* context, uint32_t event) {
|
||||
furi_assert(context);
|
||||
|
@ -131,18 +133,20 @@ void desktop_settings_app_free(DesktopSettingsApp* app) {
|
|||
}
|
||||
|
||||
extern int32_t desktop_settings_app(void* p) {
|
||||
DesktopSettingsApp* app = desktop_settings_app_alloc();
|
||||
DESKTOP_SETTINGS_LOAD(&app->settings);
|
||||
UNUSED(p);
|
||||
|
||||
DesktopSettingsApp* app = desktop_settings_app_alloc();
|
||||
Desktop* desktop = furi_record_open(RECORD_DESKTOP);
|
||||
|
||||
desktop_api_get_settings(desktop, &app->settings);
|
||||
|
||||
if(p && (strcmp(p, DESKTOP_SETTINGS_RUN_PIN_SETUP_ARG) == 0)) {
|
||||
scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinSetupHowto);
|
||||
} else {
|
||||
scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneStart);
|
||||
}
|
||||
|
||||
view_dispatcher_run(app->view_dispatcher);
|
||||
|
||||
DESKTOP_SETTINGS_SAVE(&app->settings);
|
||||
desktop_api_set_settings(desktop, &app->settings);
|
||||
furi_record_close(RECORD_DESKTOP);
|
||||
|
||||
desktop_settings_app_free(app);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -40,7 +40,7 @@ typedef struct {
|
|||
DesktopSettingsViewPinSetupHowto* pin_setup_howto_view;
|
||||
DesktopSettingsViewPinSetupHowto2* pin_setup_howto2_view;
|
||||
|
||||
PinCode pincode_buffer;
|
||||
DesktopPinCode pincode_buffer;
|
||||
bool pincode_buffer_filled;
|
||||
|
||||
bool save_name;
|
||||
|
|
|
@ -194,19 +194,25 @@ bool desktop_settings_scene_favorite_on_event(void* context, SceneManagerEvent e
|
|||
strncpy(
|
||||
curr_favorite_app->name_or_path,
|
||||
furi_string_get_cstr(temp_path),
|
||||
MAX_APP_LENGTH);
|
||||
sizeof(curr_favorite_app->name_or_path));
|
||||
consumed = true;
|
||||
}
|
||||
} else {
|
||||
size_t app_index = event.event - MAIN_LIST_APPLICATION_OFFSET;
|
||||
const char* name = favorite_fap_get_app_name(app_index);
|
||||
if(name) strncpy(curr_favorite_app->name_or_path, name, MAX_APP_LENGTH);
|
||||
if(name)
|
||||
strncpy(
|
||||
curr_favorite_app->name_or_path,
|
||||
name,
|
||||
sizeof(curr_favorite_app->name_or_path));
|
||||
consumed = true;
|
||||
}
|
||||
if(consumed) {
|
||||
scene_manager_previous_scene(app->scene_manager);
|
||||
};
|
||||
consumed = true;
|
||||
|
||||
desktop_settings_save(&app->settings);
|
||||
}
|
||||
|
||||
furi_string_free(temp_path);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#include <stdint.h>
|
||||
#include <core/check.h>
|
||||
#include <gui/scene_manager.h>
|
||||
#include <desktop/helpers/pin.h>
|
||||
#include <desktop/helpers/pin_code.h>
|
||||
#include "../desktop_settings_app.h"
|
||||
#include <desktop/desktop_settings.h>
|
||||
#include <desktop/views/desktop_view_pin_input.h>
|
||||
|
@ -12,13 +12,14 @@
|
|||
#define SCENE_EVENT_PINS_EQUAL (1U)
|
||||
#define SCENE_EVENT_PINS_DIFFERENT (2U)
|
||||
|
||||
static void pin_auth_done_callback(const PinCode* pin_code, void* context) {
|
||||
static void pin_auth_done_callback(const DesktopPinCode* pin_code, void* context) {
|
||||
furi_assert(pin_code);
|
||||
furi_assert(context);
|
||||
DesktopSettingsApp* app = context;
|
||||
|
||||
DesktopSettingsApp* app = context;
|
||||
app->pincode_buffer = *pin_code;
|
||||
if(desktop_pin_compare(&app->settings.pin_code, pin_code)) {
|
||||
|
||||
if(desktop_pin_code_check(pin_code)) {
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_PINS_EQUAL);
|
||||
} else {
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_PINS_DIFFERENT);
|
||||
|
@ -31,10 +32,9 @@ static void pin_auth_back_callback(void* context) {
|
|||
}
|
||||
|
||||
void desktop_settings_scene_pin_auth_on_enter(void* context) {
|
||||
DesktopSettingsApp* app = context;
|
||||
furi_assert(desktop_pin_code_is_set());
|
||||
|
||||
DESKTOP_SETTINGS_LOAD(&app->settings);
|
||||
furi_assert(app->settings.pin_code.length > 0);
|
||||
DesktopSettingsApp* app = context;
|
||||
|
||||
desktop_view_pin_input_set_context(app->pin_input_view, app);
|
||||
desktop_view_pin_input_set_back_callback(app->pin_input_view, pin_auth_back_callback);
|
||||
|
|
|
@ -17,9 +17,8 @@ static void pin_disable_back_callback(void* context) {
|
|||
void desktop_settings_scene_pin_disable_on_enter(void* context) {
|
||||
furi_assert(context);
|
||||
DesktopSettingsApp* app = context;
|
||||
app->settings.pin_code.length = 0;
|
||||
memset(app->settings.pin_code.data, '0', sizeof(app->settings.pin_code.data));
|
||||
DESKTOP_SETTINGS_SAVE(&app->settings);
|
||||
|
||||
desktop_pin_code_reset();
|
||||
|
||||
popup_set_context(app->popup, app);
|
||||
popup_set_callback(app->popup, pin_disable_back_callback);
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
#include <desktop/views/desktop_view_pin_input.h>
|
||||
#include "desktop_settings_scene.h"
|
||||
#include "desktop_settings_scene_i.h"
|
||||
#include <desktop/helpers/pin.h>
|
||||
#include <desktop/helpers/pin_code.h>
|
||||
#include "../desktop_settings_app.h"
|
||||
|
||||
#define SCENE_EVENT_EXIT (0U)
|
||||
|
@ -17,7 +17,7 @@ static void pin_error_back_callback(void* context) {
|
|||
view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_EXIT);
|
||||
}
|
||||
|
||||
static void pin_error_done_callback(const PinCode* pin_code, void* context) {
|
||||
static void pin_error_done_callback(const DesktopPinCode* pin_code, void* context) {
|
||||
UNUSED(pin_code);
|
||||
furi_assert(context);
|
||||
DesktopSettingsApp* app = context;
|
||||
|
|
|
@ -19,7 +19,7 @@ void desktop_settings_scene_pin_menu_on_enter(void* context) {
|
|||
Submenu* submenu = app->submenu;
|
||||
submenu_reset(submenu);
|
||||
|
||||
if(!app->settings.pin_code.length) {
|
||||
if(!desktop_pin_code_is_set()) {
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"Set PIN",
|
||||
|
|
|
@ -7,14 +7,14 @@
|
|||
#include <desktop/views/desktop_view_pin_input.h>
|
||||
#include "desktop_settings_scene.h"
|
||||
#include "desktop_settings_scene_i.h"
|
||||
#include <desktop/helpers/pin.h>
|
||||
#include <desktop/helpers/pin_code.h>
|
||||
|
||||
#define SCENE_EVENT_EXIT (0U)
|
||||
#define SCENE_EVENT_1ST_PIN_ENTERED (1U)
|
||||
#define SCENE_EVENT_PINS_EQUAL (2U)
|
||||
#define SCENE_EVENT_PINS_DIFFERENT (3U)
|
||||
|
||||
static void pin_setup_done_callback(const PinCode* pin_code, void* context) {
|
||||
static void pin_setup_done_callback(const DesktopPinCode* pin_code, void* context) {
|
||||
furi_assert(pin_code);
|
||||
furi_assert(context);
|
||||
DesktopSettingsApp* app = context;
|
||||
|
@ -25,7 +25,7 @@ static void pin_setup_done_callback(const PinCode* pin_code, void* context) {
|
|||
view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_1ST_PIN_ENTERED);
|
||||
} else {
|
||||
app->pincode_buffer_filled = false;
|
||||
if(desktop_pin_compare(&app->pincode_buffer, pin_code)) {
|
||||
if(desktop_pin_code_is_equal(&app->pincode_buffer, pin_code)) {
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_PINS_EQUAL);
|
||||
} else {
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_PINS_DIFFERENT);
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
|
||||
#define SCENE_EVENT_DONE (0U)
|
||||
|
||||
static void pin_setup_done_callback(const PinCode* pin_code, void* context) {
|
||||
static void pin_setup_done_callback(const DesktopPinCode* pin_code, void* context) {
|
||||
furi_assert(pin_code);
|
||||
furi_assert(context);
|
||||
DesktopSettingsApp* app = context;
|
||||
|
@ -22,8 +22,8 @@ static void pin_setup_done_callback(const PinCode* pin_code, void* context) {
|
|||
void desktop_settings_scene_pin_setup_done_on_enter(void* context) {
|
||||
DesktopSettingsApp* app = context;
|
||||
|
||||
app->settings.pin_code = app->pincode_buffer;
|
||||
DESKTOP_SETTINGS_SAVE(&app->settings);
|
||||
desktop_pin_code_set(&app->pincode_buffer);
|
||||
|
||||
NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
|
||||
notification_message(notification, &sequence_single_vibro);
|
||||
notification_message(notification, &sequence_blink_green_10);
|
||||
|
@ -32,7 +32,7 @@ void desktop_settings_scene_pin_setup_done_on_enter(void* context) {
|
|||
desktop_view_pin_input_set_context(app->pin_input_view, app);
|
||||
desktop_view_pin_input_set_back_callback(app->pin_input_view, NULL);
|
||||
desktop_view_pin_input_set_done_callback(app->pin_input_view, pin_setup_done_callback);
|
||||
desktop_view_pin_input_set_pin(app->pin_input_view, &app->settings.pin_code);
|
||||
desktop_view_pin_input_set_pin(app->pin_input_view, &app->pincode_buffer);
|
||||
desktop_view_pin_input_set_label_button(app->pin_input_view, "Done");
|
||||
desktop_view_pin_input_set_label_primary(app->pin_input_view, 29, 8, "PIN Activated!");
|
||||
desktop_view_pin_input_set_label_secondary(
|
||||
|
|
|
@ -27,6 +27,8 @@ static uint32_t expansion_settings_app_exit(void* context) {
|
|||
static ExpansionSettingsApp* expansion_settings_app_alloc(void) {
|
||||
ExpansionSettingsApp* app = malloc(sizeof(ExpansionSettingsApp));
|
||||
|
||||
expansion_settings_load(&app->settings);
|
||||
|
||||
app->gui = furi_record_open(RECORD_GUI);
|
||||
app->expansion = furi_record_open(RECORD_EXPANSION);
|
||||
app->settings = expansion_get_settings(app->expansion);
|
||||
|
|
|
@ -27,7 +27,7 @@ void storage_settings_scene_internal_info_on_enter(void* context) {
|
|||
} else {
|
||||
furi_string_printf(
|
||||
app->text_string,
|
||||
"Name: %s\nType: LittleFS\nTotal: %lu KiB\nFree: %lu KiB",
|
||||
"Name: %s\nType: Virtual\nTotal: %lu KiB\nFree: %lu KiB",
|
||||
furi_hal_version_get_name_ptr() ? furi_hal_version_get_name_ptr() : "Unknown",
|
||||
(uint32_t)(total_space / 1024),
|
||||
(uint32_t)(free_space / 1024));
|
||||
|
|
|
@ -4,7 +4,6 @@ App(
|
|||
apptype=FlipperAppType.METAPACKAGE,
|
||||
provides=[
|
||||
"updater_app",
|
||||
"storage_move_to_sd",
|
||||
"js_app",
|
||||
"js_app_start",
|
||||
# "archive",
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
App(
|
||||
appid="storage_move_to_sd",
|
||||
name="StorageMoveToSd",
|
||||
apptype=FlipperAppType.SYSTEM,
|
||||
entry_point="storage_move_to_sd_app",
|
||||
requires=["gui", "storage"],
|
||||
provides=["storage_move_to_sd_start"],
|
||||
stack_size=2 * 1024,
|
||||
order=30,
|
||||
)
|
||||
|
||||
App(
|
||||
appid="storage_move_to_sd_start",
|
||||
apptype=FlipperAppType.STARTUP,
|
||||
entry_point="storage_move_to_sd_start",
|
||||
requires=["storage"],
|
||||
order=120,
|
||||
)
|
|
@ -1,30 +0,0 @@
|
|||
#include "storage_move_to_sd_scene.h"
|
||||
|
||||
// Generate scene on_enter handlers array
|
||||
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,
|
||||
void (*const storage_move_to_sd_on_enter_handlers[])(void*) = {
|
||||
#include "storage_move_to_sd_scene_config.h"
|
||||
};
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Generate scene on_event handlers array
|
||||
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,
|
||||
bool (*const storage_move_to_sd_on_event_handlers[])(void* context, SceneManagerEvent event) = {
|
||||
#include "storage_move_to_sd_scene_config.h"
|
||||
};
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Generate scene on_exit handlers array
|
||||
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,
|
||||
void (*const storage_move_to_sd_on_exit_handlers[])(void* context) = {
|
||||
#include "storage_move_to_sd_scene_config.h"
|
||||
};
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Initialize scene handlers configuration structure
|
||||
const SceneManagerHandlers storage_move_to_sd_scene_handlers = {
|
||||
.on_enter_handlers = storage_move_to_sd_on_enter_handlers,
|
||||
.on_event_handlers = storage_move_to_sd_on_event_handlers,
|
||||
.on_exit_handlers = storage_move_to_sd_on_exit_handlers,
|
||||
.scene_num = StorageMoveToSdSceneNum,
|
||||
};
|
|
@ -1,29 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <gui/scene_manager.h>
|
||||
|
||||
// Generate scene id and total number
|
||||
#define ADD_SCENE(prefix, name, id) StorageMoveToSd##id,
|
||||
typedef enum {
|
||||
#include "storage_move_to_sd_scene_config.h"
|
||||
StorageMoveToSdSceneNum,
|
||||
} StorageMoveToSdScene;
|
||||
#undef ADD_SCENE
|
||||
|
||||
extern const SceneManagerHandlers storage_move_to_sd_scene_handlers;
|
||||
|
||||
// Generate scene on_enter handlers declaration
|
||||
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);
|
||||
#include "storage_move_to_sd_scene_config.h"
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Generate scene on_event handlers declaration
|
||||
#define ADD_SCENE(prefix, name, id) \
|
||||
bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event);
|
||||
#include "storage_move_to_sd_scene_config.h"
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Generate scene on_exit handlers declaration
|
||||
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context);
|
||||
#include "storage_move_to_sd_scene_config.h"
|
||||
#undef ADD_SCENE
|
|
@ -1,2 +0,0 @@
|
|||
ADD_SCENE(storage_move_to_sd, confirm, Confirm)
|
||||
ADD_SCENE(storage_move_to_sd, progress, Progress)
|
|
@ -1,70 +0,0 @@
|
|||
#include "../storage_move_to_sd.h"
|
||||
#include <gui/canvas.h>
|
||||
#include <gui/modules/widget_elements/widget_element_i.h>
|
||||
#include <storage/storage.h>
|
||||
|
||||
static void storage_move_to_sd_scene_confirm_widget_callback(
|
||||
GuiButtonType result,
|
||||
InputType type,
|
||||
void* context) {
|
||||
StorageMoveToSd* app = context;
|
||||
furi_assert(app);
|
||||
if(type == InputTypeShort) {
|
||||
if(result == GuiButtonTypeRight) {
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, MoveToSdCustomEventConfirm);
|
||||
} else if(result == GuiButtonTypeLeft) {
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, MoveToSdCustomEventExit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void storage_move_to_sd_scene_confirm_on_enter(void* context) {
|
||||
StorageMoveToSd* app = context;
|
||||
|
||||
widget_add_button_element(
|
||||
app->widget,
|
||||
GuiButtonTypeLeft,
|
||||
"Cancel",
|
||||
storage_move_to_sd_scene_confirm_widget_callback,
|
||||
app);
|
||||
widget_add_button_element(
|
||||
app->widget,
|
||||
GuiButtonTypeRight,
|
||||
"Confirm",
|
||||
storage_move_to_sd_scene_confirm_widget_callback,
|
||||
app);
|
||||
|
||||
widget_add_string_element(
|
||||
app->widget, 64, 10, AlignCenter, AlignCenter, FontPrimary, "SD card inserted");
|
||||
widget_add_string_multiline_element(
|
||||
app->widget,
|
||||
64,
|
||||
32,
|
||||
AlignCenter,
|
||||
AlignCenter,
|
||||
FontSecondary,
|
||||
"Move data from\ninternal storage to SD card?");
|
||||
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, StorageMoveToSdViewWidget);
|
||||
}
|
||||
|
||||
bool storage_move_to_sd_scene_confirm_on_event(void* context, SceneManagerEvent event) {
|
||||
StorageMoveToSd* app = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == MoveToSdCustomEventConfirm) {
|
||||
scene_manager_next_scene(app->scene_manager, StorageMoveToSdProgress);
|
||||
consumed = true;
|
||||
} else if(event.event == MoveToSdCustomEventExit) {
|
||||
view_dispatcher_stop(app->view_dispatcher);
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void storage_move_to_sd_scene_confirm_on_exit(void* context) {
|
||||
StorageMoveToSd* app = context;
|
||||
widget_reset(app->widget);
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
#include "../storage_move_to_sd.h"
|
||||
|
||||
void storage_move_to_sd_scene_progress_on_enter(void* context) {
|
||||
StorageMoveToSd* app = context;
|
||||
|
||||
widget_add_string_element(
|
||||
app->widget, 64, 10, AlignCenter, AlignCenter, FontPrimary, "Moving...");
|
||||
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, StorageMoveToSdViewWidget);
|
||||
|
||||
storage_move_to_sd_perform();
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, MoveToSdCustomEventExit);
|
||||
}
|
||||
|
||||
bool storage_move_to_sd_scene_progress_on_event(void* context, SceneManagerEvent event) {
|
||||
StorageMoveToSd* app = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
view_dispatcher_stop(app->view_dispatcher);
|
||||
} else if(event.type == SceneManagerEventTypeBack) {
|
||||
consumed = true;
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void storage_move_to_sd_scene_progress_on_exit(void* context) {
|
||||
StorageMoveToSd* app = context;
|
||||
widget_reset(app->widget);
|
||||
}
|
|
@ -1,203 +0,0 @@
|
|||
#include "storage_move_to_sd.h"
|
||||
|
||||
#include <core/common_defines.h>
|
||||
#include <core/log.h>
|
||||
#include <loader/loader.h>
|
||||
#include <toolbox/dir_walk.h>
|
||||
#include <toolbox/path.h>
|
||||
#include <furi_hal.h>
|
||||
|
||||
#define TAG "MoveToSd"
|
||||
|
||||
#define MOVE_SRC STORAGE_INT_PATH_PREFIX
|
||||
#define MOVE_DST STORAGE_EXT_PATH_PREFIX
|
||||
|
||||
static bool storage_move_to_sd_check_entry(const char* name, FileInfo* fileinfo, void* ctx) {
|
||||
UNUSED(ctx);
|
||||
if(file_info_is_dir(fileinfo)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return name && (*name != '.');
|
||||
}
|
||||
|
||||
static void storage_move_to_sd_remove_region() {
|
||||
if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) return;
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
|
||||
if(storage_common_exists(storage, INT_PATH(".region_data"))) {
|
||||
storage_common_remove(storage, INT_PATH(".region_data"));
|
||||
}
|
||||
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
bool storage_move_to_sd_perform(void) {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
|
||||
DirWalk* dir_walk = dir_walk_alloc(storage);
|
||||
dir_walk_set_recursive(dir_walk, false);
|
||||
dir_walk_set_filter_cb(dir_walk, storage_move_to_sd_check_entry, NULL);
|
||||
|
||||
FuriString *path_src, *path_dst;
|
||||
|
||||
path_dst = furi_string_alloc();
|
||||
path_src = furi_string_alloc();
|
||||
|
||||
if(dir_walk_open(dir_walk, STORAGE_INT_PATH_PREFIX)) {
|
||||
while(dir_walk_read(dir_walk, path_src, NULL) == DirWalkOK) {
|
||||
furi_string_set(path_dst, path_src);
|
||||
furi_string_replace_at(
|
||||
path_dst, 0, strlen(STORAGE_INT_PATH_PREFIX), STORAGE_EXT_PATH_PREFIX);
|
||||
|
||||
storage_common_merge(
|
||||
storage, furi_string_get_cstr(path_src), furi_string_get_cstr(path_dst));
|
||||
storage_simply_remove_recursive(storage, furi_string_get_cstr(path_src));
|
||||
}
|
||||
}
|
||||
|
||||
dir_walk_free(dir_walk);
|
||||
furi_string_free(path_dst);
|
||||
furi_string_free(path_src);
|
||||
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool storage_move_to_sd_check(void) {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
|
||||
bool should_migrate = false;
|
||||
|
||||
DirWalk* dir_walk = dir_walk_alloc(storage);
|
||||
dir_walk_set_recursive(dir_walk, false);
|
||||
dir_walk_set_filter_cb(dir_walk, storage_move_to_sd_check_entry, NULL);
|
||||
|
||||
FuriString* name;
|
||||
name = furi_string_alloc();
|
||||
|
||||
if(dir_walk_open(dir_walk, STORAGE_INT_PATH_PREFIX)) {
|
||||
// if at least 1 entry is present, we should migrate
|
||||
should_migrate = (dir_walk_read(dir_walk, name, NULL) == DirWalkOK);
|
||||
}
|
||||
|
||||
dir_walk_free(dir_walk);
|
||||
furi_string_free(name);
|
||||
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
||||
return should_migrate;
|
||||
}
|
||||
|
||||
static bool storage_move_to_sd_custom_event_callback(void* context, uint32_t event) {
|
||||
furi_assert(context);
|
||||
StorageMoveToSd* app = context;
|
||||
return scene_manager_handle_custom_event(app->scene_manager, event);
|
||||
}
|
||||
|
||||
static bool storage_move_to_sd_back_event_callback(void* context) {
|
||||
furi_assert(context);
|
||||
StorageMoveToSd* app = context;
|
||||
return scene_manager_handle_back_event(app->scene_manager);
|
||||
}
|
||||
|
||||
static void storage_move_to_sd_unmount_callback(const void* message, void* context) {
|
||||
StorageMoveToSd* app = context;
|
||||
furi_assert(app);
|
||||
const StorageEvent* storage_event = message;
|
||||
|
||||
if((storage_event->type == StorageEventTypeCardUnmount) ||
|
||||
(storage_event->type == StorageEventTypeCardMountError)) {
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, MoveToSdCustomEventExit);
|
||||
}
|
||||
}
|
||||
|
||||
static StorageMoveToSd* storage_move_to_sd_alloc(void) {
|
||||
StorageMoveToSd* app = malloc(sizeof(StorageMoveToSd));
|
||||
|
||||
app->gui = furi_record_open(RECORD_GUI);
|
||||
app->notifications = furi_record_open(RECORD_NOTIFICATION);
|
||||
|
||||
app->view_dispatcher = view_dispatcher_alloc();
|
||||
app->scene_manager = scene_manager_alloc(&storage_move_to_sd_scene_handlers, app);
|
||||
|
||||
view_dispatcher_enable_queue(app->view_dispatcher);
|
||||
view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
|
||||
|
||||
view_dispatcher_set_custom_event_callback(
|
||||
app->view_dispatcher, storage_move_to_sd_custom_event_callback);
|
||||
view_dispatcher_set_navigation_event_callback(
|
||||
app->view_dispatcher, storage_move_to_sd_back_event_callback);
|
||||
|
||||
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
|
||||
|
||||
app->widget = widget_alloc();
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher, StorageMoveToSdViewWidget, widget_get_view(app->widget));
|
||||
|
||||
scene_manager_next_scene(app->scene_manager, StorageMoveToSdConfirm);
|
||||
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
app->sub = furi_pubsub_subscribe(
|
||||
storage_get_pubsub(storage), storage_move_to_sd_unmount_callback, app);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
static void storage_move_to_sd_free(StorageMoveToSd* app) {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
furi_pubsub_unsubscribe(storage_get_pubsub(storage), app->sub);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
|
||||
view_dispatcher_remove_view(app->view_dispatcher, StorageMoveToSdViewWidget);
|
||||
widget_free(app->widget);
|
||||
view_dispatcher_free(app->view_dispatcher);
|
||||
scene_manager_free(app->scene_manager);
|
||||
|
||||
furi_record_close(RECORD_GUI);
|
||||
|
||||
free(app);
|
||||
}
|
||||
|
||||
int32_t storage_move_to_sd_app(void* p) {
|
||||
UNUSED(p);
|
||||
|
||||
if(storage_move_to_sd_check()) {
|
||||
StorageMoveToSd* app = storage_move_to_sd_alloc();
|
||||
notification_message(app->notifications, &sequence_display_backlight_on);
|
||||
view_dispatcher_run(app->view_dispatcher);
|
||||
storage_move_to_sd_free(app);
|
||||
} else {
|
||||
FURI_LOG_I(TAG, "Nothing to move");
|
||||
}
|
||||
|
||||
// Remove unused region file from int memory
|
||||
storage_move_to_sd_remove_region();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void storage_move_to_sd_mount_callback(const void* message, void* context) {
|
||||
UNUSED(context);
|
||||
|
||||
const StorageEvent* storage_event = message;
|
||||
|
||||
if(storage_event->type == StorageEventTypeCardMount) {
|
||||
Loader* loader = furi_record_open(RECORD_LOADER);
|
||||
loader_start(loader, "StorageMoveToSd", NULL, NULL);
|
||||
furi_record_close(RECORD_LOADER);
|
||||
}
|
||||
}
|
||||
|
||||
int32_t storage_move_to_sd_start(void* p) {
|
||||
UNUSED(p);
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
|
||||
furi_pubsub_subscribe(storage_get_pubsub(storage), storage_move_to_sd_mount_callback, NULL);
|
||||
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
return 0;
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
#pragma once
|
||||
#include <gui/gui.h>
|
||||
#include <gui/view.h>
|
||||
#include <gui/view_dispatcher.h>
|
||||
#include <gui/scene_manager.h>
|
||||
#include <gui/modules/widget.h>
|
||||
#include <gui/modules/popup.h>
|
||||
#include <gui/modules/widget_elements/widget_element_i.h>
|
||||
|
||||
#include <notification/notification_messages.h>
|
||||
#include <storage/storage.h>
|
||||
#include <storage/storage_sd_api.h>
|
||||
#include <furi.h>
|
||||
|
||||
#include "scenes/storage_move_to_sd_scene.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
MoveToSdCustomEventExit,
|
||||
MoveToSdCustomEventConfirm,
|
||||
} MoveToSdCustomEvent;
|
||||
|
||||
typedef struct {
|
||||
// records
|
||||
Gui* gui;
|
||||
Widget* widget;
|
||||
NotificationApp* notifications;
|
||||
|
||||
// view management
|
||||
SceneManager* scene_manager;
|
||||
ViewDispatcher* view_dispatcher;
|
||||
|
||||
FuriPubSubSubscription* sub;
|
||||
|
||||
} StorageMoveToSd;
|
||||
|
||||
typedef enum {
|
||||
StorageMoveToSdViewWidget,
|
||||
} StorageMoveToSdView;
|
||||
|
||||
bool storage_move_to_sd_perform(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
|
@ -6,7 +6,6 @@
|
|||
#include <storage/storage.h>
|
||||
#include <toolbox/path.h>
|
||||
#include <update_util/dfu_file.h>
|
||||
#include <update_util/lfs_backup.h>
|
||||
#include <update_util/update_operation.h>
|
||||
|
||||
#define TAG "UpdWorker"
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
#include <storage/storage.h>
|
||||
#include <toolbox/path.h>
|
||||
#include <update_util/dfu_file.h>
|
||||
#include <update_util/lfs_backup.h>
|
||||
#include <update_util/update_operation.h>
|
||||
#include <toolbox/tar/tar_archive.h>
|
||||
#include <toolbox/crc32_calc.h>
|
||||
|
|
|
@ -1021,7 +1021,6 @@ RECURSIVE = YES
|
|||
|
||||
EXCLUDE = $(DOXY_SRC_ROOT)/lib/mlib \
|
||||
$(DOXY_SRC_ROOT)/lib/STM32CubeWB \
|
||||
$(DOXY_SRC_ROOT)/lib/littlefs \
|
||||
$(DOXY_SRC_ROOT)/lib/nanopb \
|
||||
$(DOXY_SRC_ROOT)/assets/protobuf \
|
||||
$(DOXY_SRC_ROOT)/lib/libusb_stm32 \
|
||||
|
|
|
@ -107,10 +107,13 @@ void furi_event_loop_run(FuriEventLoop* instance) {
|
|||
furi_check(instance);
|
||||
furi_check(instance->thread_id == furi_thread_get_current_id());
|
||||
|
||||
furi_event_loop_init_tick(instance);
|
||||
|
||||
// Set the default signal callback if none was previously set
|
||||
if(furi_thread_get_signal_callback(instance->thread_id) == NULL) {
|
||||
furi_thread_set_signal_callback(
|
||||
instance->thread_id, furi_event_loop_signal_callback, instance);
|
||||
}
|
||||
|
||||
furi_event_loop_init_tick(instance);
|
||||
|
||||
while(true) {
|
||||
instance->state = FuriEventLoopStateIdle;
|
||||
|
@ -177,8 +180,11 @@ void furi_event_loop_run(FuriEventLoop* instance) {
|
|||
}
|
||||
}
|
||||
|
||||
// Disable the default signal callback
|
||||
if(furi_thread_get_signal_callback(instance->thread_id) == furi_event_loop_signal_callback) {
|
||||
furi_thread_set_signal_callback(instance->thread_id, NULL, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void furi_event_loop_stop(FuriEventLoop* instance) {
|
||||
furi_check(instance);
|
||||
|
|
|
@ -318,6 +318,12 @@ void furi_thread_set_signal_callback(
|
|||
thread->signal_context = context;
|
||||
}
|
||||
|
||||
FuriThreadSignalCallback furi_thread_get_signal_callback(const FuriThread* thread) {
|
||||
furi_check(thread);
|
||||
|
||||
return thread->signal_callback;
|
||||
}
|
||||
|
||||
bool furi_thread_signal(const FuriThread* thread, uint32_t signal, void* arg) {
|
||||
furi_check(thread);
|
||||
|
||||
|
|
|
@ -270,7 +270,7 @@ FuriThreadState furi_thread_get_state(FuriThread* thread);
|
|||
/**
|
||||
* @brief Set a signal handler callback for a FuriThread instance.
|
||||
*
|
||||
* The thread MUST be stopped when calling this function.
|
||||
* The thread MUST be stopped when calling this function if calling it from another thread.
|
||||
*
|
||||
* @param[in,out] thread pointer to the FuriThread instance to be modified
|
||||
* @param[in] callback pointer to a user-specified callback function
|
||||
|
@ -281,6 +281,14 @@ void furi_thread_set_signal_callback(
|
|||
FuriThreadSignalCallback callback,
|
||||
void* context);
|
||||
|
||||
/**
|
||||
* @brief Get a signal callback for a FuriThread instance.
|
||||
*
|
||||
* @param[in] thread pointer to the FuriThread instance to be queried
|
||||
* @return pointer to the callback function or NULL if none has been set
|
||||
*/
|
||||
FuriThreadSignalCallback furi_thread_get_signal_callback(const FuriThread* thread);
|
||||
|
||||
/**
|
||||
* @brief Send a signal to a FuriThread instance.
|
||||
*
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
- `infrared` - Infrared library, used by Infrared application
|
||||
- `lfrfid` - LF-RFID library, used by LF RFID application
|
||||
- `libusb_stm32` - LibUSB for STM32 series MCU
|
||||
- `littlefs` - LittleFS file system driver, used by internal storage
|
||||
- `mbedtls` - MbedTLS cryptography library
|
||||
- `microtar` - MicroTAR library
|
||||
- `mjs` - MJs, javascript engine library
|
||||
|
|
|
@ -27,7 +27,6 @@ libs = env.BuildModules(
|
|||
"one_wire",
|
||||
"ibutton",
|
||||
"infrared",
|
||||
"littlefs",
|
||||
"subghz",
|
||||
"nfc",
|
||||
"digital_signal",
|
||||
|
|
|
@ -373,6 +373,12 @@ bool ble_profile_hid_mouse_scroll(FuriHalBleProfileBase* profile, int8_t delta)
|
|||
return state;
|
||||
}
|
||||
|
||||
// AN5289: 4.7, in order to use flash controller interval must be at least 25ms + advertisement, which is 30 ms
|
||||
// Since we don't use flash controller anymore interval can be lowered to 7.5ms
|
||||
#define CONNECTION_INTERVAL_MIN (0x0006)
|
||||
// Up to 45 ms
|
||||
#define CONNECTION_INTERVAL_MAX (0x24)
|
||||
|
||||
static GapConfig template_config = {
|
||||
.adv_service_uuid = HUMAN_INTERFACE_DEVICE_SERVICE_UUID,
|
||||
.appearance_char = GAP_APPEARANCE_KEYBOARD,
|
||||
|
@ -380,8 +386,8 @@ static GapConfig template_config = {
|
|||
.pairing_method = GapPairingPinCodeVerifyYesNo,
|
||||
.conn_param =
|
||||
{
|
||||
.conn_int_min = 0x18, // AN5289: 4.7, we need at least 25ms + advertisement, which is 30 ms
|
||||
.conn_int_max = 0x24, // 45 ms
|
||||
.conn_int_min = CONNECTION_INTERVAL_MIN,
|
||||
.conn_int_max = CONNECTION_INTERVAL_MAX,
|
||||
.slave_latency = 0,
|
||||
.supervisor_timeout = 0,
|
||||
},
|
||||
|
|
204
lib/lfs_config.h
204
lib/lfs_config.h
|
@ -1,204 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <furi.h>
|
||||
|
||||
#ifdef FURI_NDEBUG
|
||||
#define LFS_NO_ASSERT
|
||||
#define LFS_ASSERT(x)
|
||||
#else
|
||||
#define LFS_ASSERT furi_assert
|
||||
#endif
|
||||
|
||||
#define LFS_TAG "Lfs"
|
||||
|
||||
#ifdef FURI_LFS_DEBUG
|
||||
#define LFS_TRACE(...) FURI_LOG_T(LFS_TAG, __VA_ARGS__);
|
||||
|
||||
#define LFS_DEBUG(...) FURI_LOG_D(LFS_TAG, __VA_ARGS__);
|
||||
#else
|
||||
#define LFS_TRACE(...)
|
||||
|
||||
#define LFS_DEBUG(...)
|
||||
#endif // FURI_LFS_DEBUG
|
||||
|
||||
#define LFS_WARN(...) FURI_LOG_W(LFS_TAG, __VA_ARGS__);
|
||||
|
||||
#define LFS_ERROR(...) FURI_LOG_E(LFS_TAG, __VA_ARGS__);
|
||||
|
||||
// Because crc
|
||||
#undef LFS_CONFIG
|
||||
|
||||
// System includes
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#ifndef LFS_NO_MALLOC
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifndef LFS_NO_ASSERT
|
||||
#include <assert.h>
|
||||
#endif
|
||||
#if !defined(LFS_NO_DEBUG) || !defined(LFS_NO_WARN) || !defined(LFS_NO_ERROR) || \
|
||||
defined(LFS_YES_TRACE)
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Builtin functions, these may be replaced by more efficient
|
||||
// toolchain-specific implementations. LFS_NO_INTRINSICS falls back to a more
|
||||
// expensive basic C implementation for debugging purposes
|
||||
|
||||
// Min/max functions for unsigned 32-bit numbers
|
||||
static inline uint32_t lfs_max(uint32_t a, uint32_t b) {
|
||||
return (a > b) ? a : b;
|
||||
}
|
||||
|
||||
static inline uint32_t lfs_min(uint32_t a, uint32_t b) {
|
||||
return (a < b) ? a : b;
|
||||
}
|
||||
|
||||
// Align to nearest multiple of a size
|
||||
static inline uint32_t lfs_aligndown(uint32_t a, uint32_t alignment) {
|
||||
return a - (a % alignment);
|
||||
}
|
||||
|
||||
static inline uint32_t lfs_alignup(uint32_t a, uint32_t alignment) {
|
||||
return lfs_aligndown(a + alignment - 1, alignment);
|
||||
}
|
||||
|
||||
// Find the smallest power of 2 greater than or equal to a
|
||||
static inline uint32_t lfs_npw2(uint32_t a) {
|
||||
#if !defined(LFS_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM))
|
||||
return 32 - __builtin_clz(a - 1);
|
||||
#else
|
||||
uint32_t r = 0;
|
||||
uint32_t s;
|
||||
a -= 1;
|
||||
s = (a > 0xffff) << 4;
|
||||
a >>= s;
|
||||
r |= s;
|
||||
s = (a > 0xff) << 3;
|
||||
a >>= s;
|
||||
r |= s;
|
||||
s = (a > 0xf) << 2;
|
||||
a >>= s;
|
||||
r |= s;
|
||||
s = (a > 0x3) << 1;
|
||||
a >>= s;
|
||||
r |= s;
|
||||
return (r | (a >> 1)) + 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Count the number of trailing binary zeros in a
|
||||
// lfs_ctz(0) may be undefined
|
||||
static inline uint32_t lfs_ctz(uint32_t a) {
|
||||
#if !defined(LFS_NO_INTRINSICS) && defined(__GNUC__)
|
||||
return __builtin_ctz(a);
|
||||
#else
|
||||
return lfs_npw2((a & -a) + 1) - 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Count the number of binary ones in a
|
||||
static inline uint32_t lfs_popc(uint32_t a) {
|
||||
#if !defined(LFS_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM))
|
||||
return __builtin_popcount(a);
|
||||
#else
|
||||
a = a - ((a >> 1) & 0x55555555);
|
||||
a = (a & 0x33333333) + ((a >> 2) & 0x33333333);
|
||||
return (((a + (a >> 4)) & 0xf0f0f0f) * 0x1010101) >> 24;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Find the sequence comparison of a and b, this is the distance
|
||||
// between a and b ignoring overflow
|
||||
static inline int lfs_scmp(uint32_t a, uint32_t b) {
|
||||
return (int)(unsigned)(a - b);
|
||||
}
|
||||
|
||||
// Convert between 32-bit little-endian and native order
|
||||
static inline uint32_t lfs_fromle32(uint32_t a) {
|
||||
#if !defined(LFS_NO_INTRINSICS) && \
|
||||
((defined(BYTE_ORDER) && defined(ORDER_LITTLE_ENDIAN) && \
|
||||
BYTE_ORDER == ORDER_LITTLE_ENDIAN) || \
|
||||
(defined(__BYTE_ORDER) && defined(__ORDER_LITTLE_ENDIAN) && \
|
||||
__BYTE_ORDER == __ORDER_LITTLE_ENDIAN) || \
|
||||
(defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \
|
||||
__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
|
||||
return a;
|
||||
#elif !defined(LFS_NO_INTRINSICS) && \
|
||||
((defined(BYTE_ORDER) && defined(ORDER_BIG_ENDIAN) && BYTE_ORDER == ORDER_BIG_ENDIAN) || \
|
||||
(defined(__BYTE_ORDER) && defined(__ORDER_BIG_ENDIAN) && \
|
||||
__BYTE_ORDER == __ORDER_BIG_ENDIAN) || \
|
||||
(defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && \
|
||||
__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__))
|
||||
return __builtin_bswap32(a);
|
||||
#else
|
||||
return (((uint8_t*)&a)[0] << 0) | (((uint8_t*)&a)[1] << 8) | (((uint8_t*)&a)[2] << 16) |
|
||||
(((uint8_t*)&a)[3] << 24);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline uint32_t lfs_tole32(uint32_t a) {
|
||||
return lfs_fromle32(a);
|
||||
}
|
||||
|
||||
// Convert between 32-bit big-endian and native order
|
||||
static inline uint32_t lfs_frombe32(uint32_t a) {
|
||||
#if !defined(LFS_NO_INTRINSICS) && \
|
||||
((defined(BYTE_ORDER) && defined(ORDER_LITTLE_ENDIAN) && \
|
||||
BYTE_ORDER == ORDER_LITTLE_ENDIAN) || \
|
||||
(defined(__BYTE_ORDER) && defined(__ORDER_LITTLE_ENDIAN) && \
|
||||
__BYTE_ORDER == __ORDER_LITTLE_ENDIAN) || \
|
||||
(defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \
|
||||
__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
|
||||
return __builtin_bswap32(a);
|
||||
#elif !defined(LFS_NO_INTRINSICS) && \
|
||||
((defined(BYTE_ORDER) && defined(ORDER_BIG_ENDIAN) && BYTE_ORDER == ORDER_BIG_ENDIAN) || \
|
||||
(defined(__BYTE_ORDER) && defined(__ORDER_BIG_ENDIAN) && \
|
||||
__BYTE_ORDER == __ORDER_BIG_ENDIAN) || \
|
||||
(defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && \
|
||||
__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__))
|
||||
return a;
|
||||
#else
|
||||
return (((uint8_t*)&a)[0] << 24) | (((uint8_t*)&a)[1] << 16) | (((uint8_t*)&a)[2] << 8) |
|
||||
(((uint8_t*)&a)[3] << 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline uint32_t lfs_tobe32(uint32_t a) {
|
||||
return lfs_frombe32(a);
|
||||
}
|
||||
|
||||
// Calculate CRC-32 with polynomial = 0x04c11db7
|
||||
uint32_t lfs_crc(uint32_t crc, const void* buffer, size_t size);
|
||||
|
||||
// Allocate memory, only used if buffers are not provided to littlefs
|
||||
// Note, memory must be 64-bit aligned
|
||||
static inline void* lfs_malloc(size_t size) {
|
||||
#ifndef LFS_NO_MALLOC
|
||||
return malloc(size);
|
||||
#else
|
||||
(void)size;
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Deallocate memory, only used if buffers are not provided to littlefs
|
||||
static inline void lfs_free(void* p) {
|
||||
#ifndef LFS_NO_MALLOC
|
||||
free(p);
|
||||
#else
|
||||
(void)p;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
|
@ -1 +0,0 @@
|
|||
Subproject commit 611c9b20db2b99faee261daa7cc9bbe175d3eaca
|
|
@ -1,22 +0,0 @@
|
|||
Import("env")
|
||||
|
||||
env.Append(
|
||||
CPPPATH=[
|
||||
"#/lib/littlefs",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
libenv = env.Clone(FW_LIB_NAME="littlefs")
|
||||
libenv.ApplyLibFlags()
|
||||
libenv.Append(
|
||||
CPPDEFINES=[
|
||||
("LFS_CONFIG", "lfs_config.h"),
|
||||
],
|
||||
)
|
||||
|
||||
sources = Glob("littlefs/*.c", source=True)
|
||||
|
||||
lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources)
|
||||
libenv.Install("${LIB_DIST_DIR}", lib)
|
||||
Return("lib")
|
|
@ -23,8 +23,8 @@ void subghz_device_registry_init(void) {
|
|||
firmware_api_interface);
|
||||
|
||||
//TODO FL-3556: fix path to plugins
|
||||
if(plugin_manager_load_all(subghz_device->manager, EXT_PATH("apps_data/subghz/plugins")) !=
|
||||
//if(plugin_manager_load_all(subghz_device->manager, APP_DATA_PATH("plugins")) !=
|
||||
if(plugin_manager_load_all(subghz_device->manager, "/ext/apps_data/subghz/plugins") !=
|
||||
PluginManagerErrorNone) {
|
||||
FURI_LOG_E(TAG, "Failed to load all libs");
|
||||
}
|
||||
|
|
|
@ -1,11 +1,37 @@
|
|||
#include "crc32_calc.h"
|
||||
#include <littlefs/lfs_util.h>
|
||||
|
||||
#define CRC_DATA_BUFFER_MAX_LEN 512
|
||||
|
||||
uint32_t crc32_calc_buffer(uint32_t crc, const void* buffer, size_t size) {
|
||||
// TODO FL-3547: consider removing dependency on LFS
|
||||
return ~lfs_crc(~crc, buffer, size);
|
||||
crc = ~crc;
|
||||
|
||||
static const uint32_t rtable[16] = {
|
||||
0x00000000,
|
||||
0x1db71064,
|
||||
0x3b6e20c8,
|
||||
0x26d930ac,
|
||||
0x76dc4190,
|
||||
0x6b6b51f4,
|
||||
0x4db26158,
|
||||
0x5005713c,
|
||||
0xedb88320,
|
||||
0xf00f9344,
|
||||
0xd6d6a3e8,
|
||||
0xcb61b38c,
|
||||
0x9b64c2b0,
|
||||
0x86d3d2d4,
|
||||
0xa00ae278,
|
||||
0xbdbdf21c,
|
||||
};
|
||||
|
||||
const uint8_t* data = buffer;
|
||||
|
||||
for(size_t i = 0; i < size; i++) {
|
||||
crc = (crc >> 4) ^ rtable[(crc ^ (data[i] >> 0)) & 0xf];
|
||||
crc = (crc >> 4) ^ rtable[(crc ^ (data[i] >> 4)) & 0xf];
|
||||
}
|
||||
|
||||
return ~crc;
|
||||
}
|
||||
|
||||
uint32_t crc32_calc_file(File* file, const FileCrcProgressCb progress_cb, void* context) {
|
||||
|
|
|
@ -159,8 +159,8 @@ static bool file_stream_delete_and_insert(
|
|||
FuriString* tmp_name;
|
||||
tmp_name = furi_string_alloc();
|
||||
storage_get_next_filename(
|
||||
_stream->storage, STORAGE_ANY_PATH_PREFIX, ".scratch", ".pad", tmp_name, 255);
|
||||
scratch_name = furi_string_alloc_printf(ANY_PATH("%s.pad"), furi_string_get_cstr(tmp_name));
|
||||
_stream->storage, STORAGE_EXT_PATH_PREFIX, ".scratch", ".pad", tmp_name, 255);
|
||||
scratch_name = furi_string_alloc_printf(EXT_PATH("%s.pad"), furi_string_get_cstr(tmp_name));
|
||||
furi_string_free(tmp_name);
|
||||
|
||||
do {
|
||||
|
|
|
@ -289,7 +289,7 @@ bool tar_archive_file_finalize(TarArchive* archive) {
|
|||
typedef struct {
|
||||
TarArchive* archive;
|
||||
const char* work_dir;
|
||||
Storage_name_converter converter;
|
||||
TarArchiveNameConverter converter;
|
||||
} TarArchiveDirectoryOpParams;
|
||||
|
||||
static bool archive_extract_current_file(TarArchive* archive, const char* dst_path) {
|
||||
|
@ -386,7 +386,7 @@ static int archive_extract_foreach_cb(mtar_t* tar, const mtar_header_t* header,
|
|||
bool tar_archive_unpack_to(
|
||||
TarArchive* archive,
|
||||
const char* destination,
|
||||
Storage_name_converter converter) {
|
||||
TarArchiveNameConverter converter) {
|
||||
furi_check(archive);
|
||||
TarArchiveDirectoryOpParams param = {
|
||||
.archive = archive,
|
||||
|
|
|
@ -54,6 +54,8 @@ bool tar_archive_open(TarArchive* archive, const char* path, TarOpenMode mode);
|
|||
*/
|
||||
void tar_archive_free(TarArchive* archive);
|
||||
|
||||
typedef void (*TarArchiveNameConverter)(FuriString*);
|
||||
|
||||
/* High-level API - assumes archive is open */
|
||||
|
||||
/** Unpack tar archive to destination
|
||||
|
@ -67,7 +69,7 @@ void tar_archive_free(TarArchive* archive);
|
|||
bool tar_archive_unpack_to(
|
||||
TarArchive* archive,
|
||||
const char* destination,
|
||||
Storage_name_converter converter);
|
||||
TarArchiveNameConverter converter);
|
||||
|
||||
/** Add file to tar archive
|
||||
*
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue