Merge remote-tracking branch 'ofw/dev' into mntm-dev

This commit is contained in:
Willy-JL
2024-08-05 01:50:20 +02:00
109 changed files with 1115 additions and 1860 deletions

3
.gitmodules vendored
View File

@@ -8,9 +8,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,3 +19,4 @@
- OFW: JS: Ensure proper closure of variadic function in `mjs_array` (by @derskythe)
### Removed:
- Storage: Remove LFS, all config on SD Card (by @skotopes)

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/Demos/demo_windows.txt"));
furi_string_set(app->file_path, EXT_PATH("badusb/Demos/demo_windows.txt"));
scene_manager_next_scene(app->scene_manager, FileBrowserSceneBrowser);
consumed = true;
} else if(event.type == SceneManagerEventTypeTick) {

View File

@@ -510,18 +510,14 @@ void archive_favorites_move_mode(ArchiveBrowserView* browser, bool active) {
}
static bool archive_is_dir_exists(FuriString* path) {
if(furi_string_equal(path, STORAGE_INT_PATH_PREFIX) ||
furi_string_equal(path, STORAGE_EXT_PATH_PREFIX) ||
furi_string_equal(path, STORAGE_MNT_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);
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

@@ -37,7 +37,7 @@
#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

@@ -16,6 +16,7 @@ App(
# "!helpers/subghz_chat.c",
# "!subghz_extended_freq.c",
# ],
requires=["region"],
resources="resources",
fap_libs=["hwdrivers"],
fap_icon="icon.png",

View File

@@ -25,11 +25,6 @@
#define SUBGHZ_FREQUENCY_RANGE_STR \
"299999755...348000000 or 386999938...464000000 or 778999847...928000000"
// Tx/Rx Carrier | only internal module
// Tx/Rx command | both
// Rx RAW | only internal module
// Chat | both
#define TAG "SubGhzCli"
static void subghz_cli_radio_device_power_on(void) {
@@ -478,7 +473,7 @@ void subghz_cli_command_rx_raw(Cli* cli, FuriString* args, void* context) {
void subghz_cli_command_decode_raw(Cli* cli, FuriString* args, void* context) {
UNUSED(context);
FuriString* 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"));
Storage* storage = furi_record_open(RECORD_STORAGE);
FlipperFormat* fff_data_file = flipper_format_file_alloc(storage);
@@ -592,7 +587,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

@@ -30,6 +30,9 @@ typedef enum {
BtMessageTypeSetProfile,
BtMessageTypeDisconnect,
BtMessageTypeForgetBondedDevices,
BtMessageTypeGetSettings,
BtMessageTypeSetSettings,
BtMessageTypeReloadKeysSettings,
} BtMessageType;
typedef struct {
@@ -47,6 +50,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,22 +1,35 @@
#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_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,6 +1,5 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
@@ -13,9 +12,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

@@ -3,18 +3,24 @@
#include <furi.h>
#include <input/input.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);
void desktop_run_keybind(Desktop* instance, InputType _type, InputKey _key);

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,47 +55,42 @@ 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; // Unused, kept for compatibility
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;
DesktopClock clock;
bool in_transition : 1;
bool locked : 1;
AnimationManager* animation_manager;
FuriSemaphore* animation_semaphore;
Keybinds keybinds;
DesktopClock clock;
DesktopSettings settings;
bool in_transition;
bool app_running;
bool locked;
Keybinds keybinds;
FuriPubSub* ascii_events_pubsub;
FuriPubSubSubscription* ascii_events_subscription;
};
Desktop* desktop_alloc(void);
void desktop_free(Desktop* desktop);
void desktop_lock(Desktop* desktop, bool pin_lock);
void desktop_unlock(Desktop* desktop);
void desktop_set_stealth_mode_state(Desktop* desktop, bool enabled);

View File

@@ -1,67 +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,
};
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();
if(pin_fails < 3) {
return 0;
}
// Use for loop to avoid including pow() function (4kb of dfu flash)
uint32_t mult = 1;
for(size_t i = 0; i < pin_fails - 3; i++) {
mult *= 2;
}
return 30 * mult;
}
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;
}
bool desktop_pin_is_valid(const PinCode* pin_code) {
bool ok = pin_code->length >= MIN_PIN_SIZE && pin_code->length <= MAX_PIN_SIZE;
for(size_t i = 0; ok && i < pin_code->length; i++) {
ok = ok && (pin_code->data[i] == InputKeyUp || pin_code->data[i] == InputKeyDown ||
pin_code->data[i] == InputKeyRight || pin_code->data[i] == InputKeyLeft);
}
return ok;
}

View File

@@ -1,13 +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);
bool desktop_pin_is_valid(const PinCode* pin_code);

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

@@ -59,17 +59,15 @@ bool desktop_scene_lock_menu_on_event(void* context, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeTick) {
int check_pin_changed =
scene_manager_get_scene_state(desktop->scene_manager, DesktopSceneLockMenu);
if(check_pin_changed) {
if(desktop_pin_is_valid(&desktop->settings.pin_code)) {
desktop_lock_menu_set_pin_state(desktop->lock_menu, true);
scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 0);
desktop_lock(desktop, true);
if(check_pin_changed == 2) {
Power* power = furi_record_open(RECORD_POWER);
furi_delay_ms(500);
power_off(power);
furi_record_close(RECORD_POWER);
}
if(check_pin_changed && desktop_pin_code_is_set()) {
desktop_lock_menu_set_pin_state(desktop->lock_menu, true);
scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 0);
desktop_lock(desktop, true);
if(check_pin_changed == 2) {
Power* power = furi_record_open(RECORD_POWER);
furi_delay_ms(500);
power_off(power);
furi_record_close(RECORD_POWER);
}
}
} else if(event.type == SceneManagerEventTypeCustom) {

View File

@@ -6,15 +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 <momentum/momentum.h>
#define TAG "DesktopSrv"
#include "desktop_scene_locked.h"
#define WRONG_PIN_HEADER_TIMEOUT 3000
#define INPUT_PIN_VIEW_TIMEOUT 15000
@@ -45,14 +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 = furi_hal_rtc_is_flag_set(FuriHalRtcFlagLock);
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) {
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) {
@@ -67,7 +63,7 @@ void desktop_scene_locked_on_enter(void* context) {
desktop_view_locked_close_cover(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

@@ -11,7 +11,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
@@ -50,10 +50,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() + 1;
if(pin_fails >= 10 && momentum_settings.bad_pins_format) {

View File

@@ -46,10 +46,7 @@ bool desktop_scene_slideshow_on_event(void* context, SceneManagerEvent event) {
}
void desktop_scene_slideshow_on_exit(void* context) {
Desktop* desktop = (Desktop*)context;
Desktop* desktop = context;
gui_set_hide_statusbar(desktop->gui, false);
Storage* storage = furi_record_open(RECORD_STORAGE);
storage_common_remove(storage, SLIDESHOW_FS_PATH);
furi_record_close(RECORD_STORAGE);
storage_common_remove(desktop->storage, SLIDESHOW_FS_PATH);
}

View File

@@ -56,6 +56,8 @@ typedef enum {
DesktopGlobalAfterAppFinished,
DesktopGlobalAutoLock,
DesktopGlobalApiUnlock,
DesktopGlobalSaveSettings,
DesktopGlobalReloadSettings,
DesktopMainEventLockKeypad,
DesktopLockedEventOpenPowerOff,

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>
#include <momentum/momentum.h>
#define TAG "Dolphin"
@@ -226,6 +227,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();
}
@@ -235,6 +240,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) {
@@ -250,7 +281,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,10 +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"
@@ -25,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,
@@ -63,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,21 +1,9 @@
#pragma once
#include "dolphin_deed.h"
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
#define DOLPHIN_STATE_PATH CFG_PATH("dolphin.state")
extern const uint32_t DOLPHIN_LEVELS[];
extern const size_t DOLPHIN_LEVEL_COUNT;
#define BUTTHURT_MAX 14
#define BUTTHURT_MIN 0
#include "dolphin_deed.h"
typedef struct DolphinState DolphinState;
typedef struct {
@@ -37,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,12 @@ 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 ||
instance->state == ExpansionStateConnectionEstablished) {
expansion_worker_stop(instance->worker);
expansion_worker_free(instance->worker);
@@ -156,6 +162,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 +237,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 +276,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,6 +299,14 @@ void expansion_on_system_start(void* arg) {
furi_record_create(RECORD_EXPANSION, instance);
furi_thread_start(instance->thread);
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_settings_load(&instance->settings);
expansion_enable(instance);
}

View File

@@ -5,25 +5,39 @@
#include "expansion_settings_filename.h"
#define TAG "ExpansionSettings"
#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);
return saved_struct_load(
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);
}
}
bool expansion_settings_save(const ExpansionSettings* settings) {
void expansion_settings_save(const ExpansionSettings* settings) {
furi_assert(settings);
return saved_struct_save(
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 254
#define LONG_LOAD_THRESHOLD 100

View File

@@ -423,8 +423,8 @@ static void
}
}
static bool notification_save_settings(NotificationApp* app) {
return saved_struct_save(
static bool notification_load_settings(NotificationApp* app) {
return saved_struct_load(
NOTIFICATION_SETTINGS_PATH,
&app->settings,
sizeof(NotificationSettings),
@@ -432,8 +432,8 @@ static bool notification_save_settings(NotificationApp* app) {
NOTIFICATION_SETTINGS_VERSION);
}
static bool notification_load_settings(NotificationApp* app) {
return saved_struct_load(
static bool notification_save_settings(NotificationApp* app) {
return saved_struct_save(
NOTIFICATION_SETTINGS_PATH,
&app->settings,
sizeof(NotificationSettings),
@@ -505,12 +505,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();
notification_load_settings(app);
notification_init_settings(app);
notification_vibro_off();
notification_sound_off();
@@ -518,7 +552,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);
@@ -536,6 +569,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

@@ -10,6 +10,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]);
#ifndef FURI_RAM_EXEC
storage_mnt_init(&app->storage[ST_MNT]);
@@ -105,6 +101,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

@@ -534,7 +534,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.
@@ -554,7 +554,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);
/******************* FatFs Virtual Mount 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,
@@ -62,33 +64,25 @@ 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;
}
}
}
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 || type == ST_MNT);
if(storage_data_status(&app->storage[type]) != StorageStatusOK) {
return FSE_NOT_READY;
}
*storage = &app->storage[type];
@@ -607,6 +601,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);
} else if(furi_string_start_with(path, STORAGE_CFG_PATH_PREFIX)) {
// Create config folder if it doesn't exist
FuriString* config_path = furi_string_alloc_set(STORAGE_CFG_PATH_PREFIX);

View File

@@ -1,761 +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_expand(void* ctx, File* file, const uint64_t size) {
UNUSED(ctx);
UNUSED(file);
UNUSED(size);
file->error_id = FSE_NOT_IMPLEMENTED;
return (file->error_id == FSE_OK);
}
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_rename(void* ctx, const char* old, const char* new) {
StorageData* storage = ctx;
lfs_t* lfs = lfs_get_from_storage(storage);
int result = lfs_rename(lfs, old, new);
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,
.expand = storage_int_file_expand,
.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,
.rename = storage_int_common_rename,
.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,7 +15,6 @@ static bool bt_settings_back_event_callback(void* context) {
BtSettingsApp* bt_settings_app_alloc(void) {
BtSettingsApp* app = malloc(sizeof(BtSettingsApp));
// Load settings
app->gui = furi_record_open(RECORD_GUI);
app->bt = furi_record_open(RECORD_BT);
@@ -47,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;
@@ -54,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);
@@ -78,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->bt->bt_settings);
bt_settings_app_free(app);
return 0;
}

View File

@@ -1,19 +1,21 @@
#pragma once
#include <furi.h>
#include <bt/bt_service/bt.h>
#include <bt/bt_service/bt_i.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->bt->bt_settings.enabled = true;
app->settings.enabled = true;
consumed = true;
} else if(event.event == BtSettingOff) {
app->bt->bt_settings.enabled = false;
furi_hal_bt_stop_advertising();
app->settings.enabled = false;
consumed = true;
} else if(event.event == BtSettingsCustomEventForgetDevices) {
scene_manager_next_scene(app->scene_manager, BtSettingsAppSceneForgetDevConfirm);
consumed = true;
}
}
return consumed;
}

View File

@@ -2,9 +2,11 @@
#include <gui/modules/popup.h>
#include <gui/scene_manager.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>
const char* EXTRA_KEYBINDS[] = {
"Apps Menu",
@@ -146,19 +148,20 @@ void desktop_settings_app_free(DesktopSettingsApp* app) {
}
extern int32_t desktop_settings_app(void* p) {
DesktopSettingsApp* app = desktop_settings_app_alloc();
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);
if(app->save_settings) {
DESKTOP_SETTINGS_SAVE(&app->desktop->settings);
}
desktop_api_set_settings(desktop, &app->settings);
furi_record_close(RECORD_DESKTOP);
desktop_settings_app_free(app);
return 0;

View File

@@ -51,7 +51,7 @@ typedef struct {
DesktopSettingsViewPinSetupHowto* pin_setup_howto_view;
DesktopSettingsViewPinSetupHowto2* pin_setup_howto2_view;
PinCode pincode_buffer;
DesktopPinCode pincode_buffer;
bool pincode_buffer_filled;
uint32_t pin_menu_idx;

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->desktop->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,9 +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());
furi_assert(desktop_pin_is_valid(&app->desktop->settings.pin_code));
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,10 +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->desktop->settings.pin_code.length = 0;
memset(
app->desktop->settings.pin_code.data, '0', sizeof(app->desktop->settings.pin_code.data));
app->save_settings = true;
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

@@ -20,7 +20,7 @@ void desktop_settings_scene_pin_menu_on_enter(void* context) {
Submenu* submenu = app->submenu;
submenu_reset(submenu);
if(!desktop_pin_is_valid(&app->desktop->settings.pin_code)) {
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,6 +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;
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);

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

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,188 +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>
#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 != '.');
}
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");
}
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

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

@@ -278,6 +278,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

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

Submodule lib/littlefs deleted from 611c9b20db

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

@@ -325,7 +325,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) {
@@ -429,7 +429,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

@@ -55,6 +55,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
@@ -68,7 +70,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
*

View File

@@ -39,7 +39,9 @@ class Main(App):
if port := resolve_port(self.logger, self.args.port):
self.logger.info(f"Found flipper at {port}")
time.sleep(1)
break
time.sleep(1)
if not port:

View File

@@ -687,6 +687,7 @@ Function,+,bt_forget_bonded_devices,void,Bt*
Function,+,bt_keys_storage_alloc,BtKeysStorage*,const char*
Function,+,bt_keys_storage_delete,_Bool,BtKeysStorage*
Function,+,bt_keys_storage_free,void,BtKeysStorage*
Function,+,bt_keys_storage_is_changed,_Bool,BtKeysStorage*
Function,+,bt_keys_storage_load,_Bool,BtKeysStorage*
Function,+,bt_keys_storage_set_default_path,void,Bt*
Function,+,bt_keys_storage_set_file_path,void,"BtKeysStorage*, const char*"
@@ -1372,6 +1373,7 @@ Function,+,furi_hal_rtc_get_log_baud_rate,FuriHalRtcLogBaudRate,
Function,+,furi_hal_rtc_get_log_device,FuriHalRtcLogDevice,
Function,+,furi_hal_rtc_get_log_level,uint8_t,
Function,+,furi_hal_rtc_get_pin_fails,uint32_t,
Function,-,furi_hal_rtc_get_pin_value,uint32_t,
Function,+,furi_hal_rtc_get_register,uint32_t,FuriHalRtcRegister
Function,+,furi_hal_rtc_get_timestamp,uint32_t,
Function,-,furi_hal_rtc_init,void,
@@ -1391,6 +1393,7 @@ Function,+,furi_hal_rtc_set_log_baud_rate,void,FuriHalRtcLogBaudRate
Function,+,furi_hal_rtc_set_log_device,void,FuriHalRtcLogDevice
Function,+,furi_hal_rtc_set_log_level,void,uint8_t
Function,+,furi_hal_rtc_set_pin_fails,void,uint32_t
Function,-,furi_hal_rtc_set_pin_value,void,uint32_t
Function,+,furi_hal_rtc_set_register,void,"FuriHalRtcRegister, uint32_t"
Function,+,furi_hal_rtc_sync_shadow,void,
Function,+,furi_hal_sd_get_card_state,FuriStatus,
@@ -1626,6 +1629,7 @@ Function,+,furi_thread_get_id,FuriThreadId,FuriThread*
Function,+,furi_thread_get_name,const char*,FuriThreadId
Function,+,furi_thread_get_priority,FuriThreadPriority,FuriThread*
Function,+,furi_thread_get_return_code,int32_t,FuriThread*
Function,+,furi_thread_get_signal_callback,FuriThreadSignalCallback,const FuriThread*
Function,+,furi_thread_get_stack_space,uint32_t,FuriThreadId
Function,+,furi_thread_get_state,FuriThreadState,FuriThread*
Function,+,furi_thread_get_stdout_callback,FuriThreadStdoutWriteCallback,
@@ -2509,7 +2513,7 @@ Function,+,storage_file_write,size_t,"File*, const void*, size_t"
Function,+,storage_get_next_filename,void,"Storage*, const char*, const char*, const char*, FuriString*, uint8_t"
Function,+,storage_get_pubsub,FuriPubSub*,Storage*
Function,+,storage_int_backup,FS_Error,"Storage*, const char*"
Function,+,storage_int_restore,FS_Error,"Storage*, const char*, Storage_name_converter"
Function,+,storage_int_restore,FS_Error,"Storage*, const char*, StorageNameConverter"
Function,+,storage_sd_format,FS_Error,Storage*
Function,+,storage_sd_info,FS_Error,"Storage*, SDInfo*"
Function,+,storage_sd_mount,FS_Error,Storage*
@@ -2641,7 +2645,7 @@ Function,+,tar_archive_open,_Bool,"TarArchive*, const char*, TarOpenMode"
Function,+,tar_archive_set_file_callback,void,"TarArchive*, tar_unpack_file_cb, void*"
Function,+,tar_archive_store_data,_Bool,"TarArchive*, const char*, const uint8_t*, const int32_t"
Function,+,tar_archive_unpack_file,_Bool,"TarArchive*, const char*, const char*"
Function,+,tar_archive_unpack_to,_Bool,"TarArchive*, const char*, Storage_name_converter"
Function,+,tar_archive_unpack_to,_Bool,"TarArchive*, const char*, TarArchiveNameConverter"
Function,-,tempnam,char*,"const char*, const char*"
Function,+,text_box_alloc,TextBox*,
Function,+,text_box_free,void,TextBox*
1 entry status name type params
687 Function + bt_keys_storage_alloc BtKeysStorage* const char*
688 Function + bt_keys_storage_delete _Bool BtKeysStorage*
689 Function + bt_keys_storage_free void BtKeysStorage*
690 Function + bt_keys_storage_is_changed _Bool BtKeysStorage*
691 Function + bt_keys_storage_load _Bool BtKeysStorage*
692 Function + bt_keys_storage_set_default_path void Bt*
693 Function + bt_keys_storage_set_file_path void BtKeysStorage*, const char*
1373 Function + furi_hal_rtc_get_log_device FuriHalRtcLogDevice
1374 Function + furi_hal_rtc_get_log_level uint8_t
1375 Function + furi_hal_rtc_get_pin_fails uint32_t
1376 Function - furi_hal_rtc_get_pin_value uint32_t
1377 Function + furi_hal_rtc_get_register uint32_t FuriHalRtcRegister
1378 Function + furi_hal_rtc_get_timestamp uint32_t
1379 Function - furi_hal_rtc_init void
1393 Function + furi_hal_rtc_set_log_device void FuriHalRtcLogDevice
1394 Function + furi_hal_rtc_set_log_level void uint8_t
1395 Function + furi_hal_rtc_set_pin_fails void uint32_t
1396 Function - furi_hal_rtc_set_pin_value void uint32_t
1397 Function + furi_hal_rtc_set_register void FuriHalRtcRegister, uint32_t
1398 Function + furi_hal_rtc_sync_shadow void
1399 Function + furi_hal_sd_get_card_state FuriStatus
1629 Function + furi_thread_get_name const char* FuriThreadId
1630 Function + furi_thread_get_priority FuriThreadPriority FuriThread*
1631 Function + furi_thread_get_return_code int32_t FuriThread*
1632 Function + furi_thread_get_signal_callback FuriThreadSignalCallback const FuriThread*
1633 Function + furi_thread_get_stack_space uint32_t FuriThreadId
1634 Function + furi_thread_get_state FuriThreadState FuriThread*
1635 Function + furi_thread_get_stdout_callback FuriThreadStdoutWriteCallback
2513 Function + storage_get_next_filename void Storage*, const char*, const char*, const char*, FuriString*, uint8_t
2514 Function + storage_get_pubsub FuriPubSub* Storage*
2515 Function + storage_int_backup FS_Error Storage*, const char*
2516 Function + storage_int_restore FS_Error Storage*, const char*, Storage_name_converter Storage*, const char*, StorageNameConverter
2517 Function + storage_sd_format FS_Error Storage*
2518 Function + storage_sd_info FS_Error Storage*, SDInfo*
2519 Function + storage_sd_mount FS_Error Storage*
2645 Function + tar_archive_set_file_callback void TarArchive*, tar_unpack_file_cb, void*
2646 Function + tar_archive_store_data _Bool TarArchive*, const char*, const uint8_t*, const int32_t
2647 Function + tar_archive_unpack_file _Bool TarArchive*, const char*, const char*
2648 Function + tar_archive_unpack_to _Bool TarArchive*, const char*, Storage_name_converter TarArchive*, const char*, TarArchiveNameConverter
2649 Function - tempnam char* const char*, const char*
2650 Function + text_box_alloc TextBox*
2651 Function + text_box_free void TextBox*

View File

@@ -17,7 +17,6 @@
"stm32wb",
"hwdrivers",
"fatfs",
"littlefs",
"toolbox",
"digital_signal",
"signal_reader",

View File

@@ -793,6 +793,7 @@ Function,+,bt_forget_bonded_devices,void,Bt*
Function,+,bt_keys_storage_alloc,BtKeysStorage*,const char*
Function,+,bt_keys_storage_delete,_Bool,BtKeysStorage*
Function,+,bt_keys_storage_free,void,BtKeysStorage*
Function,+,bt_keys_storage_is_changed,_Bool,BtKeysStorage*
Function,+,bt_keys_storage_load,_Bool,BtKeysStorage*
Function,+,bt_keys_storage_set_default_path,void,Bt*
Function,+,bt_keys_storage_set_file_path,void,"BtKeysStorage*, const char*"
@@ -1640,6 +1641,7 @@ Function,+,furi_hal_rtc_get_log_baud_rate,FuriHalRtcLogBaudRate,
Function,+,furi_hal_rtc_get_log_device,FuriHalRtcLogDevice,
Function,+,furi_hal_rtc_get_log_level,uint8_t,
Function,+,furi_hal_rtc_get_pin_fails,uint32_t,
Function,-,furi_hal_rtc_get_pin_value,uint32_t,
Function,+,furi_hal_rtc_get_register,uint32_t,FuriHalRtcRegister
Function,+,furi_hal_rtc_get_timestamp,uint32_t,
Function,-,furi_hal_rtc_init,void,
@@ -1659,6 +1661,7 @@ Function,+,furi_hal_rtc_set_log_baud_rate,void,FuriHalRtcLogBaudRate
Function,+,furi_hal_rtc_set_log_device,void,FuriHalRtcLogDevice
Function,+,furi_hal_rtc_set_log_level,void,uint8_t
Function,+,furi_hal_rtc_set_pin_fails,void,uint32_t
Function,-,furi_hal_rtc_set_pin_value,void,uint32_t
Function,+,furi_hal_rtc_set_register,void,"FuriHalRtcRegister, uint32_t"
Function,+,furi_hal_rtc_sync_shadow,void,
Function,+,furi_hal_sd_get_card_state,FuriStatus,
@@ -1933,6 +1936,7 @@ Function,+,furi_thread_get_id,FuriThreadId,FuriThread*
Function,+,furi_thread_get_name,const char*,FuriThreadId
Function,+,furi_thread_get_priority,FuriThreadPriority,FuriThread*
Function,+,furi_thread_get_return_code,int32_t,FuriThread*
Function,+,furi_thread_get_signal_callback,FuriThreadSignalCallback,const FuriThread*
Function,+,furi_thread_get_stack_space,uint32_t,FuriThreadId
Function,+,furi_thread_get_state,FuriThreadState,FuriThread*
Function,+,furi_thread_get_stdout_callback,FuriThreadStdoutWriteCallback,
@@ -3319,7 +3323,7 @@ Function,+,storage_file_write,size_t,"File*, const void*, size_t"
Function,+,storage_get_next_filename,void,"Storage*, const char*, const char*, const char*, FuriString*, uint8_t"
Function,+,storage_get_pubsub,FuriPubSub*,Storage*
Function,+,storage_int_backup,FS_Error,"Storage*, const char*"
Function,+,storage_int_restore,FS_Error,"Storage*, const char*, Storage_name_converter"
Function,+,storage_int_restore,FS_Error,"Storage*, const char*, StorageNameConverter"
Function,+,storage_sd_format,FS_Error,Storage*
Function,+,storage_sd_info,FS_Error,"Storage*, SDInfo*"
Function,+,storage_sd_mount,FS_Error,Storage*
@@ -3646,7 +3650,7 @@ Function,+,tar_archive_set_file_callback,void,"TarArchive*, tar_unpack_file_cb,
Function,+,tar_archive_set_read_callback,void,"TarArchive*, tar_unpack_read_cb, void*"
Function,+,tar_archive_store_data,_Bool,"TarArchive*, const char*, const uint8_t*, const int32_t"
Function,+,tar_archive_unpack_file,_Bool,"TarArchive*, const char*, const char*"
Function,+,tar_archive_unpack_to,_Bool,"TarArchive*, const char*, Storage_name_converter"
Function,+,tar_archive_unpack_to,_Bool,"TarArchive*, const char*, TarArchiveNameConverter"
Function,-,tempnam,char*,"const char*, const char*"
Function,+,text_box_alloc,TextBox*,
Function,+,text_box_free,void,TextBox*
1 entry status name type params
793 Function + bt_keys_storage_alloc BtKeysStorage* const char*
794 Function + bt_keys_storage_delete _Bool BtKeysStorage*
795 Function + bt_keys_storage_free void BtKeysStorage*
796 Function + bt_keys_storage_is_changed _Bool BtKeysStorage*
797 Function + bt_keys_storage_load _Bool BtKeysStorage*
798 Function + bt_keys_storage_set_default_path void Bt*
799 Function + bt_keys_storage_set_file_path void BtKeysStorage*, const char*
1641 Function + furi_hal_rtc_get_log_device FuriHalRtcLogDevice
1642 Function + furi_hal_rtc_get_log_level uint8_t
1643 Function + furi_hal_rtc_get_pin_fails uint32_t
1644 Function - furi_hal_rtc_get_pin_value uint32_t
1645 Function + furi_hal_rtc_get_register uint32_t FuriHalRtcRegister
1646 Function + furi_hal_rtc_get_timestamp uint32_t
1647 Function - furi_hal_rtc_init void
1661 Function + furi_hal_rtc_set_log_device void FuriHalRtcLogDevice
1662 Function + furi_hal_rtc_set_log_level void uint8_t
1663 Function + furi_hal_rtc_set_pin_fails void uint32_t
1664 Function - furi_hal_rtc_set_pin_value void uint32_t
1665 Function + furi_hal_rtc_set_register void FuriHalRtcRegister, uint32_t
1666 Function + furi_hal_rtc_sync_shadow void
1667 Function + furi_hal_sd_get_card_state FuriStatus
1936 Function + furi_thread_get_name const char* FuriThreadId
1937 Function + furi_thread_get_priority FuriThreadPriority FuriThread*
1938 Function + furi_thread_get_return_code int32_t FuriThread*
1939 Function + furi_thread_get_signal_callback FuriThreadSignalCallback const FuriThread*
1940 Function + furi_thread_get_stack_space uint32_t FuriThreadId
1941 Function + furi_thread_get_state FuriThreadState FuriThread*
1942 Function + furi_thread_get_stdout_callback FuriThreadStdoutWriteCallback
3323 Function + storage_get_next_filename void Storage*, const char*, const char*, const char*, FuriString*, uint8_t
3324 Function + storage_get_pubsub FuriPubSub* Storage*
3325 Function + storage_int_backup FS_Error Storage*, const char*
3326 Function + storage_int_restore FS_Error Storage*, const char*, Storage_name_converter Storage*, const char*, StorageNameConverter
3327 Function + storage_sd_format FS_Error Storage*
3328 Function + storage_sd_info FS_Error Storage*, SDInfo*
3329 Function + storage_sd_mount FS_Error Storage*
3650 Function + tar_archive_set_read_callback void TarArchive*, tar_unpack_read_cb, void*
3651 Function + tar_archive_store_data _Bool TarArchive*, const char*, const uint8_t*, const int32_t
3652 Function + tar_archive_unpack_file _Bool TarArchive*, const char*, const char*
3653 Function + tar_archive_unpack_to _Bool TarArchive*, const char*, Storage_name_converter TarArchive*, const char*, TarArchiveNameConverter
3654 Function - tempnam char* const char*, const char*
3655 Function + text_box_alloc TextBox*
3656 Function + text_box_free void TextBox*

View File

@@ -8,9 +8,9 @@
#define GAP_MS_TO_SCAN_INTERVAL(x) ((uint16_t)((x) / 0.625))
// Also used as an indicator of whether the beacon had ever been configured
// AN5289: 4.7, we need at least 25ms + advertisement, which is 30 ms
#define GAP_MIN_ADV_INTERVAL_MS (30u)
// 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 20ms
#define GAP_MIN_ADV_INTERVAL_MS (20U)
typedef struct {
GapExtraBeaconConfig last_config;

Some files were not shown because too many files have changed in this diff Show More