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:
MX 2024-08-10 13:18:51 +03:00
parent 6c6d64f1bd
commit 28272f7a7a
No known key found for this signature in database
GPG key ID: 7CCC66B7DBDD1C83
114 changed files with 1353 additions and 1985 deletions

3
.gitmodules vendored
View file

@ -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

View file

@ -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/*

View file

@ -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) {

View file

@ -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")) {

View file

@ -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;

View file

@ -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);

View file

@ -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"

View file

@ -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"

View file

@ -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"

View file

@ -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 {

View file

@ -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"

View file

@ -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

View file

@ -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
{

View file

@ -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");
}
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);
// 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();
}
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;
}

View file

@ -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);
}

View file

@ -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 {

View file

@ -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);

View file

@ -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);

View 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);

View file

@ -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");
}
}

View file

@ -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
}

View file

@ -97,8 +97,11 @@ void animation_manager_set_interact_callback(
void animation_manager_set_dummy_mode_state(AnimationManager* animation_manager, bool enabled) {
furi_assert(animation_manager);
animation_manager->dummy_mode = enabled;
animation_manager_start_new_idle(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) {

View file

@ -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_arm(desktop);
}
return true;
case DesktopGlobalAutoLock:
if(!loader_is_locked(desktop->loader) && !desktop->locked) {
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);
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);
}
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);
DesktopStatus status = {.locked = true};
furi_pubsub_publish(desktop->status_pubsub, &status);
desktop->locked = true;
view_port_enabled_set(desktop->clock_viewport, clock_enabled);
}
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->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) {
static void desktop_apply_settings(Desktop* desktop) {
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_clock_reconfigure(desktop);
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);
if(!desktop->app_running && !desktop->locked) {
desktop_auto_lock_arm(desktop);
}
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);
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;
}
view_port_enabled_set(desktop->stealth_mode_icon_viewport, enabled);
desktop->in_transition = false;
desktop_settings_load(&desktop->settings);
desktop_apply_settings(desktop);
}
Desktop* desktop_alloc(void) {
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;
}

View file

@ -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);

View file

@ -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);

View 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");
}
}

View 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);

View file

@ -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;
}

View file

@ -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);

View 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;
}

View 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);

View file

@ -1,4 +0,0 @@
#pragma once
#define SCENE_LOCKED_FIRST_ENTER 0
#define SCENE_LOCKED_REPEAT_ENTER 1

View file

@ -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,11 +37,8 @@ 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) {
scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 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) {

View file

@ -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) {

View file

@ -0,0 +1,6 @@
#pragma once
typedef enum {
DesktopSceneLockedStateFirstEnter,
DesktopSceneLockedStateRepeatEnter,
} DesktopSceneLockedState;

View file

@ -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]);

View file

@ -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);

View file

@ -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);
}

View file

@ -60,4 +60,6 @@ typedef enum {
DesktopGlobalAfterAppFinished,
DesktopGlobalAutoLock,
DesktopGlobalApiUnlock,
DesktopGlobalSaveSettings,
DesktopGlobalReloadSettings,
} DesktopEvent;

View file

@ -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);

View file

@ -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);

View file

@ -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,

View file

@ -12,6 +12,7 @@ typedef enum {
DolphinEventTypeStats,
DolphinEventTypeFlush,
DolphinEventTypeLevel,
DolphinEventTypeReloadState,
} DolphinEventType;
typedef struct {

View file

@ -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) {

View file

@ -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);

View file

@ -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);
}

View file

@ -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");
}
}

View 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
}

View file

@ -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

View file

@ -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) {

View file

@ -11,6 +11,7 @@ typedef enum {
NotificationLayerMessage,
InternalLayerMessage,
SaveSettingsMessage,
LoadSettingsMessage,
} NotificationAppMessageType;
typedef struct {

View 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,
)

View 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;
}

View file

@ -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) {

View file

@ -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 ******************/

View file

@ -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));

View file

@ -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);

View file

@ -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) {
type = ST_EXT;
}
storage_path_change_to_real_storage(path, type);
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;
}
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);
}
}

View file

@ -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;
}

View file

@ -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

View file

@ -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;
}

View file

@ -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 {

View file

@ -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;
}

View file

@ -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);
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);
}
DesktopSettingsApp* app = desktop_settings_app_alloc();
Desktop* desktop = furi_record_open(RECORD_DESKTOP);
desktop_api_get_settings(desktop, &app->settings);
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;

View file

@ -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;

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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;

View file

@ -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",

View file

@ -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);

View file

@ -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(

View file

@ -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);

View file

@ -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));

View file

@ -4,7 +4,6 @@ App(
apptype=FlipperAppType.METAPACKAGE,
provides=[
"updater_app",
"storage_move_to_sd",
"js_app",
"js_app_start",
# "archive",

View file

@ -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,
)

View file

@ -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,
};

View file

@ -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

View file

@ -1,2 +0,0 @@
ADD_SCENE(storage_move_to_sd, confirm, Confirm)
ADD_SCENE(storage_move_to_sd, progress, Progress)

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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

View file

@ -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"

View file

@ -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>

View file

@ -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 \

View file

@ -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_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,7 +180,10 @@ void furi_event_loop_run(FuriEventLoop* instance) {
}
}
furi_thread_set_signal_callback(instance->thread_id, NULL, NULL);
// 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) {

View file

@ -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);

View file

@ -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.
*

View file

@ -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

View file

@ -27,7 +27,6 @@ libs = env.BuildModules(
"one_wire",
"ibutton",
"infrared",
"littlefs",
"subghz",
"nfc",
"digital_signal",

View file

@ -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,
},

View file

@ -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

View file

@ -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")

View file

@ -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, APP_DATA_PATH("plugins")) !=
if(plugin_manager_load_all(subghz_device->manager, "/ext/apps_data/subghz/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")) !=
PluginManagerErrorNone) {
FURI_LOG_E(TAG, "Failed to load all libs");
}

View file

@ -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) {

View file

@ -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 {

View file

@ -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,

View file

@ -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