From e3f95a326bab32c9fd3c3dd66a0fe9ec7b6e9105 Mon Sep 17 00:00:00 2001 From: Astra <93453568+Astrrra@users.noreply.github.com> Date: Wed, 15 May 2024 03:58:09 +0900 Subject: [PATCH 01/11] [FL-3797] Settings menu refactoring (#3632) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Settings menu refactoring * Update F18 api * Wording changes * Update certification icon * Desktop: optimize settings save routine, fix navigation lag * Gui: add submenu position getter. Desktop: proper menu index preservation. * Gui: proper index getter for submenu. Desktop: cleaner settings navigation. Co-authored-by: あく --- applications/services/gui/elements.h | 24 ++- applications/services/gui/modules/submenu.c | 39 ++++ applications/services/gui/modules/submenu.h | 20 +- applications/settings/about/about.c | 12 +- .../bt_settings_scene_forget_dev_confirm.c | 6 +- .../scenes/bt_settings_scene_start.c | 2 +- .../desktop_settings/desktop_settings_app.c | 4 + .../desktop_settings/desktop_settings_app.h | 4 +- .../scenes/desktop_settings_scene_config.h | 3 + .../scenes/desktop_settings_scene_favorite.c | 84 ++++++-- .../scenes/desktop_settings_scene_pin_menu.c | 7 +- .../scenes/desktop_settings_scene_pin_setup.c | 1 + .../desktop_settings_scene_pin_setup_done.c | 1 + ...settings_scene_quick_apps_direction_menu.c | 187 ++++++++++++++++++ .../desktop_settings_scene_quick_apps_menu.c | 80 ++++++++ .../scenes/desktop_settings_scene_start.c | 78 +------- .../desktop_settings_view_pin_setup_howto2.c | 4 +- .../power_settings_app/power_settings_app.h | 2 + .../scenes/power_settings_scene_config.h | 1 + .../scenes/power_settings_scene_power_off.c | 10 +- .../scenes/power_settings_scene_reboot.c | 12 +- .../power_settings_scene_reboot_confirm.c | 66 +++++++ .../scenes/storage_settings_scene_benchmark.c | 28 ++- ...storage_settings_scene_benchmark_confirm.c | 70 +++++++ .../scenes/storage_settings_scene_config.h | 1 + .../storage_settings_scene_factory_reset.c | 8 +- .../storage_settings_scene_format_confirm.c | 4 +- .../storage_settings_scene_formatting.c | 15 +- .../storage_settings_scene_internal_info.c | 2 +- .../scenes/storage_settings_scene_sd_info.c | 30 ++- .../scenes/storage_settings_scene_start.c | 2 +- .../About/CertificationChina1_122x47.png | Bin 5215 -> 0 bytes .../About/CertificationChina1_124x47.png | Bin 0 -> 429 bytes ...h_55x52.png => LoadingHourglass_24x24.png} | Bin 3898 -> 3650 bytes assets/icons/Settings/dolph_cry_49x54.png | Bin 0 -> 973 bytes assets/icons/Settings/qr_benchmark_25x25.png | Bin 0 -> 395 bytes targets/f18/api_symbols.csv | 4 +- targets/f7/api_symbols.csv | 4 +- 38 files changed, 673 insertions(+), 142 deletions(-) create mode 100644 applications/settings/desktop_settings/scenes/desktop_settings_scene_quick_apps_direction_menu.c create mode 100644 applications/settings/desktop_settings/scenes/desktop_settings_scene_quick_apps_menu.c create mode 100644 applications/settings/power_settings_app/scenes/power_settings_scene_reboot_confirm.c create mode 100644 applications/settings/storage_settings/scenes/storage_settings_scene_benchmark_confirm.c delete mode 100644 assets/icons/About/CertificationChina1_122x47.png create mode 100644 assets/icons/About/CertificationChina1_124x47.png rename assets/icons/Settings/{Cry_dolph_55x52.png => LoadingHourglass_24x24.png} (70%) create mode 100644 assets/icons/Settings/dolph_cry_49x54.png create mode 100644 assets/icons/Settings/qr_benchmark_25x25.png diff --git a/applications/services/gui/elements.h b/applications/services/gui/elements.h index 833d5d6ee..54d78420e 100644 --- a/applications/services/gui/elements.h +++ b/applications/services/gui/elements.h @@ -121,7 +121,8 @@ void elements_multiline_text_aligned( /** Draw multiline text * * @param canvas Canvas instance - * @param x, y top left corner coordinates + * @param x top left corner coordinates + * @param y top left corner coordinates * @param text string (possible multiline) */ void elements_multiline_text(Canvas* canvas, int32_t x, int32_t y, const char* text); @@ -129,7 +130,8 @@ void elements_multiline_text(Canvas* canvas, int32_t x, int32_t y, const char* t /** Draw framed multiline text * * @param canvas Canvas instance - * @param x, y top left corner coordinates + * @param x top left corner coordinates + * @param y top left corner coordinates * @param text string (possible multiline) */ void elements_multiline_text_framed(Canvas* canvas, int32_t x, int32_t y, const char* text); @@ -137,8 +139,10 @@ void elements_multiline_text_framed(Canvas* canvas, int32_t x, int32_t y, const /** Draw slightly rounded frame * * @param canvas Canvas instance - * @param x, y top left corner coordinates - * @param width, height size of frame + * @param x top left corner coordinates + * @param y top left corner coordinates + * @param width width of frame + * @param height height of frame */ void elements_slightly_rounded_frame( Canvas* canvas, @@ -150,8 +154,10 @@ void elements_slightly_rounded_frame( /** Draw slightly rounded box * * @param canvas Canvas instance - * @param x, y top left corner coordinates - * @param width, height size of box + * @param x top left corner coordinates + * @param y top left corner coordinates + * @param width height of box + * @param height height of box */ void elements_slightly_rounded_box( Canvas* canvas, @@ -163,8 +169,10 @@ void elements_slightly_rounded_box( /** Draw bold rounded frame * * @param canvas Canvas instance - * @param x, y top left corner coordinates - * @param width, height size of frame + * @param x top left corner coordinates + * @param y top left corner coordinates + * @param width width of frame + * @param height height of frame */ void elements_bold_rounded_frame(Canvas* canvas, int32_t x, int32_t y, size_t width, size_t height); diff --git a/applications/services/gui/modules/submenu.c b/applications/services/gui/modules/submenu.c index 35916fe23..5b1bdccda 100644 --- a/applications/services/gui/modules/submenu.c +++ b/applications/services/gui/modules/submenu.c @@ -215,6 +215,26 @@ void submenu_add_item( true); } +void submenu_change_item_label(Submenu* submenu, uint32_t index, const char* label) { + furi_check(submenu); + furi_check(label); + + with_view_model( + submenu->view, + SubmenuModel * model, + { + SubmenuItemArray_it_t it; + for(SubmenuItemArray_it(it, model->items); !SubmenuItemArray_end_p(it); + SubmenuItemArray_next(it)) { + if(index == SubmenuItemArray_cref(it)->index) { + furi_string_set_str(SubmenuItemArray_cref(it)->label, label); + break; + } + } + }, + true); +} + void submenu_reset(Submenu* submenu) { furi_check(submenu); @@ -230,6 +250,25 @@ void submenu_reset(Submenu* submenu) { true); } +uint32_t submenu_get_selected_item(Submenu* submenu) { + furi_check(submenu); + + uint32_t selected_item_index = 0; + + with_view_model( + submenu->view, + SubmenuModel * model, + { + if(model->position < SubmenuItemArray_size(model->items)) { + const SubmenuItem* item = SubmenuItemArray_cget(model->items, model->position); + selected_item_index = item->index; + } + }, + false); + + return selected_item_index; +} + void submenu_set_selected_item(Submenu* submenu, uint32_t index) { furi_check(submenu); with_view_model( diff --git a/applications/services/gui/modules/submenu.h b/applications/services/gui/modules/submenu.h index 6ae148eb5..e435f94a2 100644 --- a/applications/services/gui/modules/submenu.h +++ b/applications/services/gui/modules/submenu.h @@ -53,16 +53,32 @@ void submenu_add_item( SubmenuItemCallback callback, void* callback_context); +/** Change label of an existing item + * + * @param submenu Submenu instance + * @param index The index of the item + * @param label The new label + */ +void submenu_change_item_label(Submenu* submenu, uint32_t index, const char* label); + /** Remove all items from submenu * * @param submenu Submenu instance */ void submenu_reset(Submenu* submenu); -/** Set submenu item selector +/** Get submenu selected item index * * @param submenu Submenu instance - * @param index The index + * + * @return Index of the selected item + */ +uint32_t submenu_get_selected_item(Submenu* submenu); + +/** Set submenu selected item by index + * + * @param submenu Submenu instance + * @param index The index of the selected item */ void submenu_set_selected_item(Submenu* submenu, uint32_t index); diff --git a/applications/settings/about/about.c b/applications/settings/about/about.c index 8f0798d9c..973d1f481 100644 --- a/applications/settings/about/about.c +++ b/applications/settings/about/about.c @@ -41,7 +41,7 @@ static DialogMessageButton about_screen_product(DialogsApp* dialogs, DialogMessa static DialogMessageButton about_screen_address(DialogsApp* dialogs, DialogMessage* message) { DialogMessageButton result; - const char* screen_text = "Flipper Devices Inc\n" + const char* screen_text = "Flipper Devices Inc.\n" "Suite B #551, 2803\n" "Philadelphia Pike, Claymont\n" "DE, USA 19703\n"; @@ -56,7 +56,7 @@ static DialogMessageButton about_screen_compliance(DialogsApp* dialogs, DialogMe DialogMessageButton result; const char* screen_text = "For all compliance\n" - "certificates please visit:\n" + "certificates, please visit:\n" "www.flipp.dev/compliance"; dialog_message_set_text(message, screen_text, 0, 0, AlignLeft, AlignTop); @@ -97,7 +97,7 @@ static DialogMessageButton about_screen_cert_china_0(DialogsApp* dialogs, Dialog static DialogMessageButton about_screen_cert_china_1(DialogsApp* dialogs, DialogMessage* message) { DialogMessageButton result; - dialog_message_set_icon(message, &I_CertificationChina1_122x47, 3, 3); + dialog_message_set_icon(message, &I_CertificationChina1_124x47, 3, 3); dialog_message_set_text( message, furi_hal_version_get_srrc_id(), 55, 11, AlignLeft, AlignBottom); result = dialog_message_show(dialogs, message); @@ -227,9 +227,11 @@ int32_t about_settings_app(void* p) { while(1) { if(screen_index >= COUNT_OF(about_screens) - 1) { - dialog_message_set_buttons(message, "Back", NULL, NULL); + dialog_message_set_buttons(message, "Prev.", NULL, NULL); + } else if(screen_index == 0) { + dialog_message_set_buttons(message, NULL, NULL, "Next"); } else { - dialog_message_set_buttons(message, "Back", NULL, "Next"); + dialog_message_set_buttons(message, "Prev.", NULL, "Next"); } screen_result = about_screens[screen_index](dialogs, message); diff --git a/applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_confirm.c b/applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_confirm.c index 31921b9f3..d72cb95e7 100644 --- a/applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_confirm.c +++ b/applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_confirm.c @@ -10,10 +10,10 @@ void bt_settings_scene_forget_dev_confirm_dialog_callback(DialogExResult result, void bt_settings_scene_forget_dev_confirm_on_enter(void* context) { BtSettingsApp* app = context; DialogEx* dialog = app->dialog; - dialog_ex_set_header(dialog, "Unpair All Devices?", 64, 3, AlignCenter, AlignTop); + dialog_ex_set_header(dialog, "Unpair All Devices?", 64, 0, AlignCenter, AlignTop); dialog_ex_set_text( - dialog, "All previous pairings\nwill be lost!", 64, 22, AlignCenter, AlignTop); - dialog_ex_set_left_button_text(dialog, "Back"); + dialog, "All previous pairings\nwill be lost!", 64, 14, AlignCenter, AlignTop); + dialog_ex_set_left_button_text(dialog, "Cancel"); dialog_ex_set_right_button_text(dialog, "Unpair"); dialog_ex_set_context(dialog, app); dialog_ex_set_result_callback(dialog, bt_settings_scene_forget_dev_confirm_dialog_callback); diff --git a/applications/settings/bt_settings_app/scenes/bt_settings_scene_start.c b/applications/settings/bt_settings_app/scenes/bt_settings_scene_start.c index c148f0943..1d72a9e6f 100644 --- a/applications/settings/bt_settings_app/scenes/bt_settings_scene_start.c +++ b/applications/settings/bt_settings_app/scenes/bt_settings_scene_start.c @@ -53,7 +53,7 @@ void bt_settings_scene_start_on_enter(void* context) { variable_item_set_current_value_index(item, BtSettingOff); variable_item_set_current_value_text(item, bt_settings_text[BtSettingOff]); } - variable_item_list_add(var_item_list, "Forget All Paired Devices", 1, NULL, NULL); + variable_item_list_add(var_item_list, "Unpair All Devices", 1, NULL, NULL); variable_item_list_set_enter_callback( var_item_list, bt_settings_scene_start_var_list_enter_callback, app); } else { diff --git a/applications/settings/desktop_settings/desktop_settings_app.c b/applications/settings/desktop_settings/desktop_settings_app.c index b030656f7..238d866f2 100644 --- a/applications/settings/desktop_settings/desktop_settings_app.c +++ b/applications/settings/desktop_settings/desktop_settings_app.c @@ -92,6 +92,7 @@ void desktop_settings_app_free(DesktopSettingsApp* app) { extern int32_t desktop_settings_app(void* p) { DesktopSettingsApp* app = desktop_settings_app_alloc(); DESKTOP_SETTINGS_LOAD(&app->settings); + if(p && (strcmp(p, DESKTOP_SETTINGS_RUN_PIN_SETUP_ARG) == 0)) { scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinSetupHowto); } else { @@ -99,6 +100,9 @@ extern int32_t desktop_settings_app(void* p) { } view_dispatcher_run(app->view_dispatcher); + + DESKTOP_SETTINGS_SAVE(&app->settings); desktop_settings_app_free(app); + return 0; } diff --git a/applications/settings/desktop_settings/desktop_settings_app.h b/applications/settings/desktop_settings/desktop_settings_app.h index 6f97564c9..1a2c733ed 100644 --- a/applications/settings/desktop_settings/desktop_settings_app.h +++ b/applications/settings/desktop_settings/desktop_settings_app.h @@ -40,5 +40,7 @@ typedef struct { PinCode pincode_buffer; bool pincode_buffer_filled; - uint8_t menu_idx; + uint32_t pin_menu_idx; + uint32_t quick_apps_menu_idx; + uint32_t quick_apps_direction_menu_idx; } DesktopSettingsApp; diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_config.h b/applications/settings/desktop_settings/scenes/desktop_settings_scene_config.h index 5bc52172b..52b8829be 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_config.h +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_config.h @@ -9,3 +9,6 @@ ADD_SCENE(desktop_settings, pin_setup, PinSetup) ADD_SCENE(desktop_settings, pin_setup_howto, PinSetupHowto) ADD_SCENE(desktop_settings, pin_setup_howto2, PinSetupHowto2) ADD_SCENE(desktop_settings, pin_setup_done, PinSetupDone) + +ADD_SCENE(desktop_settings, quick_apps_menu, QuickAppsMenu) +ADD_SCENE(desktop_settings, quick_apps_direction_menu, QuickAppsDirectionMenu) \ No newline at end of file diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_favorite.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_favorite.c index db5d231ff..d77c8adda 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_favorite.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_favorite.c @@ -9,11 +9,17 @@ #define APPS_COUNT (FLIPPER_APPS_COUNT + FLIPPER_EXTERNAL_APPS_COUNT) #define DEFAULT_INDEX (0) -#define EXTERNAL_BROWSER_NAME ("Apps Menu (Default)") -#define PASSPORT_NAME ("Passport (Default)") +#define EXTERNAL_BROWSER_NAME ("( ) Apps Menu (Default)") +#define EXTERNAL_BROWSER_NAME_SELECTED ("(*) Apps Menu (Default)") +#define PASSPORT_NAME ("( ) Passport (Default)") +#define PASSPORT_NAME_SELECTED ("(*) Passport (Default)") + +#define SELECTED_PREFIX ("(*) ") +#define NOT_SELECTED_PREFIX ("( ) ") #define EXTERNAL_APPLICATION_INDEX (1) -#define EXTERNAL_APPLICATION_NAME ("[Select App]") +#define EXTERNAL_APPLICATION_NAME ("( ) [Select App]") +#define EXTERNAL_APPLICATION_NAME_SELECTED ("(*) [Select App]") #define PRESELECTED_SPECIAL 0xffffffff @@ -61,7 +67,6 @@ void desktop_settings_scene_favorite_on_enter(void* context) { scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneFavorite); uint32_t pre_select_item = PRESELECTED_SPECIAL; FavoriteApp* curr_favorite_app = NULL; - bool is_dummy_app = false; bool default_passport = false; if((favorite_id & SCENE_STATE_SET_DUMMY_APP) == 0) { @@ -74,7 +79,6 @@ void desktop_settings_scene_favorite_on_enter(void* context) { favorite_id &= ~(SCENE_STATE_SET_DUMMY_APP); furi_assert(favorite_id < DummyAppNumber); curr_favorite_app = &app->settings.dummy_apps[favorite_id]; - is_dummy_app = true; default_passport = true; } @@ -94,29 +98,76 @@ void desktop_settings_scene_favorite_on_enter(void* context) { desktop_settings_scene_favorite_submenu_callback, app); - if(!is_dummy_app) { - for(size_t i = 0; i < APPS_COUNT; i++) { - const char* name = favorite_fap_get_app_name(i); + FuriString* full_name = furi_string_alloc(); - submenu_add_item( - submenu, name, i + 2, desktop_settings_scene_favorite_submenu_callback, app); + for(size_t i = 0; i < APPS_COUNT; i++) { + const char* name = favorite_fap_get_app_name(i); - // Select favorite item in submenu - if(!strcmp(name, curr_favorite_app->name_or_path)) { - pre_select_item = i + 2; - } + // Add the prefix + furi_string_reset(full_name); + if(!strcmp(name, curr_favorite_app->name_or_path)) { + furi_string_set_str(full_name, SELECTED_PREFIX); + } else { + furi_string_set_str(full_name, NOT_SELECTED_PREFIX); + } + furi_string_cat_str(full_name, name); + + submenu_add_item( + submenu, + furi_string_get_cstr(full_name), + i + 2, + desktop_settings_scene_favorite_submenu_callback, + app); + + // Select favorite item in submenu + if(!strcmp(name, curr_favorite_app->name_or_path)) { + pre_select_item = i + 2; } } if(pre_select_item == PRESELECTED_SPECIAL) { if(curr_favorite_app->name_or_path[0] == '\0') { pre_select_item = DEFAULT_INDEX; + submenu_change_item_label( + submenu, + DEFAULT_INDEX, + default_passport ? (PASSPORT_NAME_SELECTED) : (EXTERNAL_BROWSER_NAME_SELECTED)); } else { pre_select_item = EXTERNAL_APPLICATION_INDEX; + submenu_change_item_label( + submenu, EXTERNAL_APPLICATION_INDEX, EXTERNAL_APPLICATION_NAME_SELECTED); } } - submenu_set_header(submenu, is_dummy_app ? ("Dummy Mode App") : ("Favorite App")); + switch(favorite_id) { + case SCENE_STATE_SET_FAVORITE_APP | FavoriteAppLeftShort: + submenu_set_header(submenu, "Left - Short"); + break; + case SCENE_STATE_SET_FAVORITE_APP | FavoriteAppLeftLong: + submenu_set_header(submenu, "Left - Long"); + break; + case SCENE_STATE_SET_FAVORITE_APP | FavoriteAppRightShort: + submenu_set_header(submenu, "Right - Short"); + break; + case SCENE_STATE_SET_FAVORITE_APP | FavoriteAppRightLong: + submenu_set_header(submenu, "Right - Long"); + break; + case SCENE_STATE_SET_DUMMY_APP | DummyAppLeft: + submenu_set_header(submenu, "Left"); + break; + case SCENE_STATE_SET_DUMMY_APP | DummyAppRight: + submenu_set_header(submenu, "Right"); + break; + case SCENE_STATE_SET_DUMMY_APP | DummyAppDown: + submenu_set_header(submenu, "Down"); + break; + case SCENE_STATE_SET_DUMMY_APP | DummyAppOk: + submenu_set_header(submenu, "Middle"); + break; + default: + break; + } + submenu_set_selected_item(submenu, pre_select_item); // If set during loop, visual glitch. view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewMenu); @@ -177,6 +228,8 @@ bool desktop_settings_scene_favorite_on_event(void* context, SceneManagerEvent e scene_manager_previous_scene(app->scene_manager); }; consumed = true; + + DESKTOP_SETTINGS_SAVE(&app->settings); } furi_string_free(temp_path); @@ -185,6 +238,5 @@ bool desktop_settings_scene_favorite_on_event(void* context, SceneManagerEvent e void desktop_settings_scene_favorite_on_exit(void* context) { DesktopSettingsApp* app = context; - DESKTOP_SETTINGS_SAVE(&app->settings); submenu_reset(app->submenu); } diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_menu.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_menu.c index 86e756ede..9fdd68896 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_menu.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_menu.c @@ -44,7 +44,7 @@ void desktop_settings_scene_pin_menu_on_enter(void* context) { } submenu_set_header(app->submenu, "PIN Code Settings"); - submenu_set_selected_item(app->submenu, app->menu_idx); + submenu_set_selected_item(app->submenu, app->pin_menu_idx); view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewMenu); } @@ -76,11 +76,16 @@ bool desktop_settings_scene_pin_menu_on_event(void* context, SceneManagerEvent e consumed = true; break; } + } else if(event.type == SceneManagerEventTypeBack) { + submenu_set_selected_item(app->submenu, 0); } + return consumed; } void desktop_settings_scene_pin_menu_on_exit(void* context) { DesktopSettingsApp* app = context; + + app->pin_menu_idx = submenu_get_selected_item(app->submenu); submenu_reset(app->submenu); } diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup.c index 1603aa337..5b8aa8638 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup.c @@ -97,6 +97,7 @@ bool desktop_settings_scene_pin_setup_on_event(void* context, SceneManagerEvent break; } } + return consumed; } diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup_done.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup_done.c index b829f995b..4cef4ba98 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup_done.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup_done.c @@ -27,6 +27,7 @@ void desktop_settings_scene_pin_setup_done_on_enter(void* context) { DESKTOP_SETTINGS_SAVE(&app->settings); NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); notification_message(notification, &sequence_single_vibro); + notification_message(notification, &sequence_blink_green_10); furi_record_close(RECORD_NOTIFICATION); desktop_view_pin_input_set_context(app->pin_input_view, app); diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_quick_apps_direction_menu.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_quick_apps_direction_menu.c new file mode 100644 index 000000000..dbc877ede --- /dev/null +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_quick_apps_direction_menu.c @@ -0,0 +1,187 @@ +#include +#include + +#include "../desktop_settings_app.h" +#include "desktop_settings_scene.h" +#include "desktop_settings_scene_i.h" + +enum QuickAppsSubmenuIndex { + QuickAppsSubmenuIndexFavoriteLeftClick, + QuickAppsSubmenuIndexFavoriteRightClick, + QuickAppsSubmenuIndexFavoriteLeftHold, + QuickAppsSubmenuIndexFavoriteRightHold, + QuickAppsSubmenuIndexDummyLeftClick, + QuickAppsSubmenuIndexDummyRightClick, + QuickAppsSubmenuIndexDummyDownClick, + QuickAppsSubmenuIndexDummyMiddleClick, +}; + +static void desktop_settings_scene_quick_apps_direction_menu_submenu_callback( + void* context, + uint32_t index) { + DesktopSettingsApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void desktop_settings_scene_quick_apps_direction_menu_on_enter(void* context) { + DesktopSettingsApp* app = context; + Submenu* submenu = app->submenu; + submenu_reset(submenu); + + uint32_t favorite_id = scene_manager_get_scene_state( + app->scene_manager, DesktopSettingsAppSceneQuickAppsDirectionMenu); + + if(favorite_id == SCENE_STATE_SET_FAVORITE_APP) { + submenu_add_item( + submenu, + "Left - Click", + QuickAppsSubmenuIndexFavoriteLeftClick, + desktop_settings_scene_quick_apps_direction_menu_submenu_callback, + app); + + submenu_add_item( + submenu, + "Right - Click", + QuickAppsSubmenuIndexFavoriteRightClick, + desktop_settings_scene_quick_apps_direction_menu_submenu_callback, + app); + + submenu_add_item( + submenu, + "Left - Hold", + QuickAppsSubmenuIndexFavoriteLeftHold, + desktop_settings_scene_quick_apps_direction_menu_submenu_callback, + app); + + submenu_add_item( + submenu, + "Right - Hold", + QuickAppsSubmenuIndexFavoriteRightHold, + desktop_settings_scene_quick_apps_direction_menu_submenu_callback, + app); + + submenu_set_header(app->submenu, "Default Mode"); + } else { + submenu_add_item( + submenu, + "Left - Click", + QuickAppsSubmenuIndexDummyLeftClick, + desktop_settings_scene_quick_apps_direction_menu_submenu_callback, + app); + + submenu_add_item( + submenu, + "Right - Click", + QuickAppsSubmenuIndexDummyRightClick, + desktop_settings_scene_quick_apps_direction_menu_submenu_callback, + app); + + submenu_add_item( + submenu, + "Down - Click", + QuickAppsSubmenuIndexDummyDownClick, + desktop_settings_scene_quick_apps_direction_menu_submenu_callback, + app); + + submenu_add_item( + submenu, + "Middle - Click", + QuickAppsSubmenuIndexDummyMiddleClick, + desktop_settings_scene_quick_apps_direction_menu_submenu_callback, + app); + + submenu_set_header(app->submenu, "Dummy Mode"); + } + + submenu_set_selected_item(app->submenu, app->quick_apps_direction_menu_idx); + view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewMenu); +} + +bool desktop_settings_scene_quick_apps_direction_menu_on_event( + void* context, + SceneManagerEvent event) { + DesktopSettingsApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { + case QuickAppsSubmenuIndexFavoriteLeftClick: + scene_manager_set_scene_state( + app->scene_manager, + DesktopSettingsAppSceneFavorite, + SCENE_STATE_SET_FAVORITE_APP | FavoriteAppLeftShort); + scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite); + consumed = true; + break; + case QuickAppsSubmenuIndexFavoriteRightClick: + scene_manager_set_scene_state( + app->scene_manager, + DesktopSettingsAppSceneFavorite, + SCENE_STATE_SET_FAVORITE_APP | FavoriteAppRightShort); + scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite); + consumed = true; + break; + case QuickAppsSubmenuIndexFavoriteLeftHold: + scene_manager_set_scene_state( + app->scene_manager, + DesktopSettingsAppSceneFavorite, + SCENE_STATE_SET_FAVORITE_APP | FavoriteAppLeftLong); + scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite); + consumed = true; + break; + case QuickAppsSubmenuIndexFavoriteRightHold: + scene_manager_set_scene_state( + app->scene_manager, + DesktopSettingsAppSceneFavorite, + SCENE_STATE_SET_FAVORITE_APP | FavoriteAppRightLong); + scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite); + consumed = true; + break; + case QuickAppsSubmenuIndexDummyLeftClick: + scene_manager_set_scene_state( + app->scene_manager, + DesktopSettingsAppSceneFavorite, + SCENE_STATE_SET_DUMMY_APP | DummyAppLeft); + scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite); + consumed = true; + break; + case QuickAppsSubmenuIndexDummyRightClick: + scene_manager_set_scene_state( + app->scene_manager, + DesktopSettingsAppSceneFavorite, + SCENE_STATE_SET_DUMMY_APP | DummyAppRight); + scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite); + consumed = true; + break; + case QuickAppsSubmenuIndexDummyDownClick: + scene_manager_set_scene_state( + app->scene_manager, + DesktopSettingsAppSceneFavorite, + SCENE_STATE_SET_DUMMY_APP | DummyAppDown); + scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite); + consumed = true; + break; + case QuickAppsSubmenuIndexDummyMiddleClick: + scene_manager_set_scene_state( + app->scene_manager, + DesktopSettingsAppSceneFavorite, + SCENE_STATE_SET_DUMMY_APP | DummyAppOk); + scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite); + consumed = true; + break; + default: + consumed = true; + break; + } + } else if(event.type == SceneManagerEventTypeBack) { + submenu_set_selected_item(app->submenu, 0); + } + + return consumed; +} + +void desktop_settings_scene_quick_apps_direction_menu_on_exit(void* context) { + DesktopSettingsApp* app = context; + app->quick_apps_direction_menu_idx = submenu_get_selected_item(app->submenu); + submenu_reset(app->submenu); +} diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_quick_apps_menu.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_quick_apps_menu.c new file mode 100644 index 000000000..acba4fa25 --- /dev/null +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_quick_apps_menu.c @@ -0,0 +1,80 @@ +#include +#include + +#include "../desktop_settings_app.h" +#include "desktop_settings_scene.h" +#include "desktop_settings_scene_i.h" + +#define SCENE_EVENT_SET_DEFAULT (0U) +#define SCENE_EVENT_SET_DUMMY (1U) + +static void + desktop_settings_scene_quick_apps_menu_submenu_callback(void* context, uint32_t index) { + DesktopSettingsApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void desktop_settings_scene_quick_apps_menu_on_enter(void* context) { + DesktopSettingsApp* app = context; + Submenu* submenu = app->submenu; + submenu_reset(submenu); + + submenu_add_item( + submenu, + "Default Mode", + SCENE_EVENT_SET_DEFAULT, + desktop_settings_scene_quick_apps_menu_submenu_callback, + app); + + submenu_add_item( + submenu, + "Dummy Mode", + SCENE_EVENT_SET_DUMMY, + desktop_settings_scene_quick_apps_menu_submenu_callback, + app); + + submenu_set_header(app->submenu, "Set Quick Access Apps"); + submenu_set_selected_item(app->submenu, app->quick_apps_menu_idx); + view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewMenu); +} + +bool desktop_settings_scene_quick_apps_menu_on_event(void* context, SceneManagerEvent event) { + DesktopSettingsApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { + case SCENE_EVENT_SET_DEFAULT: + scene_manager_set_scene_state( + app->scene_manager, + DesktopSettingsAppSceneQuickAppsDirectionMenu, + SCENE_STATE_SET_FAVORITE_APP); + scene_manager_next_scene( + app->scene_manager, DesktopSettingsAppSceneQuickAppsDirectionMenu); + consumed = true; + break; + case SCENE_EVENT_SET_DUMMY: + scene_manager_set_scene_state( + app->scene_manager, + DesktopSettingsAppSceneQuickAppsDirectionMenu, + SCENE_STATE_SET_DUMMY_APP); + scene_manager_next_scene( + app->scene_manager, DesktopSettingsAppSceneQuickAppsDirectionMenu); + consumed = true; + break; + default: + consumed = true; + break; + } + } else if(event.type == SceneManagerEventTypeBack) { + submenu_set_selected_item(app->submenu, 0); + } + + return consumed; +} + +void desktop_settings_scene_quick_apps_menu_on_exit(void* context) { + DesktopSettingsApp* app = context; + app->quick_apps_menu_idx = submenu_get_selected_item(app->submenu); + submenu_reset(app->submenu); +} diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_start.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_start.c index 3e77bd8a1..8dec26016 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_start.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_start.c @@ -9,14 +9,7 @@ typedef enum { DesktopSettingsPinSetup = 0, DesktopSettingsAutoLockDelay, DesktopSettingsClockDisplay, - DesktopSettingsFavoriteLeftShort, - DesktopSettingsFavoriteLeftLong, - DesktopSettingsFavoriteRightShort, - DesktopSettingsFavoriteRightLong, - DesktopSettingsDummyLeft, - DesktopSettingsDummyRight, - DesktopSettingsDummyDown, - DesktopSettingsDummyOk, + DesktopSettingsFavoriteApps, } DesktopSettingsEntry; #define AUTO_LOCK_DELAY_COUNT 6 @@ -93,15 +86,7 @@ void desktop_settings_scene_start_on_enter(void* context) { variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, clock_enable_text[value_index]); - variable_item_list_add(variable_item_list, "Favorite App - Left Short", 1, NULL, NULL); - variable_item_list_add(variable_item_list, "Favorite App - Left Long", 1, NULL, NULL); - variable_item_list_add(variable_item_list, "Favorite App - Right Short", 1, NULL, NULL); - variable_item_list_add(variable_item_list, "Favorite App - Right Long", 1, NULL, NULL); - - variable_item_list_add(variable_item_list, "Dummy Mode App - Left", 1, NULL, NULL); - variable_item_list_add(variable_item_list, "Dummy Mode App - Right", 1, NULL, NULL); - variable_item_list_add(variable_item_list, "Dummy Mode App - Down", 1, NULL, NULL); - variable_item_list_add(variable_item_list, "Dummy Mode App - Ok", 1, NULL, NULL); + variable_item_list_add(variable_item_list, "Set Quick Access Apps", 1, NULL, NULL); variable_item_list_set_enter_callback( variable_item_list, desktop_settings_scene_start_var_list_enter_callback, app); @@ -119,62 +104,8 @@ bool desktop_settings_scene_start_on_event(void* context, SceneManagerEvent even scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinMenu); break; - case DesktopSettingsFavoriteLeftShort: - scene_manager_set_scene_state( - app->scene_manager, - DesktopSettingsAppSceneFavorite, - SCENE_STATE_SET_FAVORITE_APP | FavoriteAppLeftShort); - scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite); - break; - case DesktopSettingsFavoriteLeftLong: - scene_manager_set_scene_state( - app->scene_manager, - DesktopSettingsAppSceneFavorite, - SCENE_STATE_SET_FAVORITE_APP | FavoriteAppLeftLong); - scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite); - break; - case DesktopSettingsFavoriteRightShort: - scene_manager_set_scene_state( - app->scene_manager, - DesktopSettingsAppSceneFavorite, - SCENE_STATE_SET_FAVORITE_APP | FavoriteAppRightShort); - scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite); - break; - case DesktopSettingsFavoriteRightLong: - scene_manager_set_scene_state( - app->scene_manager, - DesktopSettingsAppSceneFavorite, - SCENE_STATE_SET_FAVORITE_APP | FavoriteAppRightLong); - scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite); - break; - - case DesktopSettingsDummyLeft: - scene_manager_set_scene_state( - app->scene_manager, - DesktopSettingsAppSceneFavorite, - SCENE_STATE_SET_DUMMY_APP | DummyAppLeft); - scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite); - break; - case DesktopSettingsDummyRight: - scene_manager_set_scene_state( - app->scene_manager, - DesktopSettingsAppSceneFavorite, - SCENE_STATE_SET_DUMMY_APP | DummyAppRight); - scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite); - break; - case DesktopSettingsDummyDown: - scene_manager_set_scene_state( - app->scene_manager, - DesktopSettingsAppSceneFavorite, - SCENE_STATE_SET_DUMMY_APP | DummyAppDown); - scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite); - break; - case DesktopSettingsDummyOk: - scene_manager_set_scene_state( - app->scene_manager, - DesktopSettingsAppSceneFavorite, - SCENE_STATE_SET_DUMMY_APP | DummyAppOk); - scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite); + case DesktopSettingsFavoriteApps: + scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneQuickAppsMenu); break; default: @@ -188,5 +119,4 @@ bool desktop_settings_scene_start_on_event(void* context, SceneManagerEvent even void desktop_settings_scene_start_on_exit(void* context) { DesktopSettingsApp* app = context; variable_item_list_reset(app->variable_item_list); - DESKTOP_SETTINGS_SAVE(&app->settings); } diff --git a/applications/settings/desktop_settings/views/desktop_settings_view_pin_setup_howto2.c b/applications/settings/desktop_settings/views/desktop_settings_view_pin_setup_howto2.c index b09b0b95f..6148747a2 100644 --- a/applications/settings/desktop_settings/views/desktop_settings_view_pin_setup_howto2.c +++ b/applications/settings/desktop_settings/views/desktop_settings_view_pin_setup_howto2.c @@ -24,9 +24,9 @@ static void desktop_settings_view_pin_setup_howto2_draw(Canvas* canvas, void* mo elements_multiline_text_aligned( canvas, 64, - 24, - AlignCenter, + 0, AlignCenter, + AlignTop, "Forgotten PIN can only be\n" "reset with entire device.\n" "Read docs How to reset PIN."); diff --git a/applications/settings/power_settings_app/power_settings_app.h b/applications/settings/power_settings_app/power_settings_app.h index cd05846c0..09cc5af7a 100644 --- a/applications/settings/power_settings_app/power_settings_app.h +++ b/applications/settings/power_settings_app/power_settings_app.h @@ -30,3 +30,5 @@ typedef enum { PowerSettingsAppViewSubmenu, PowerSettingsAppViewDialog, } PowerSettingsAppView; + +typedef enum { RebootTypeDFU, RebootTypeNormal } RebootType; diff --git a/applications/settings/power_settings_app/scenes/power_settings_scene_config.h b/applications/settings/power_settings_app/scenes/power_settings_scene_config.h index cc8656dcf..f57071b9b 100644 --- a/applications/settings/power_settings_app/scenes/power_settings_scene_config.h +++ b/applications/settings/power_settings_app/scenes/power_settings_scene_config.h @@ -1,4 +1,5 @@ ADD_SCENE(power_settings, start, Start) ADD_SCENE(power_settings, battery_info, BatteryInfo) ADD_SCENE(power_settings, reboot, Reboot) +ADD_SCENE(power_settings, reboot_confirm, RebootConfirm) ADD_SCENE(power_settings, power_off, PowerOff) diff --git a/applications/settings/power_settings_app/scenes/power_settings_scene_power_off.c b/applications/settings/power_settings_app/scenes/power_settings_scene_power_off.c index c3f5d5ad8..6cd9c5c67 100644 --- a/applications/settings/power_settings_app/scenes/power_settings_scene_power_off.c +++ b/applications/settings/power_settings_app/scenes/power_settings_scene_power_off.c @@ -10,12 +10,12 @@ void power_settings_scene_power_off_on_enter(void* context) { PowerSettingsApp* app = context; DialogEx* dialog = app->dialog; - dialog_ex_set_header(dialog, "Turn OFF Device?", 64, 2, AlignCenter, AlignTop); + dialog_ex_set_header(dialog, "Turn Off Device?", 64, 0, AlignCenter, AlignTop); dialog_ex_set_text( - dialog, " I will be\nwaiting for\n you here...", 78, 16, AlignLeft, AlignTop); - dialog_ex_set_icon(dialog, 21, 13, &I_Cry_dolph_55x52); - dialog_ex_set_left_button_text(dialog, "Back"); - dialog_ex_set_right_button_text(dialog, "OFF"); + dialog, " I will be\nwaiting for\n you here...", 78, 14, AlignLeft, AlignTop); + dialog_ex_set_icon(dialog, 14, 10, &I_dolph_cry_49x54); + dialog_ex_set_left_button_text(dialog, "Cancel"); + dialog_ex_set_right_button_text(dialog, "Power Off"); dialog_ex_set_result_callback(dialog, power_settings_scene_power_off_dialog_callback); dialog_ex_set_context(dialog, app); diff --git a/applications/settings/power_settings_app/scenes/power_settings_scene_reboot.c b/applications/settings/power_settings_app/scenes/power_settings_scene_reboot.c index 2d5dedfd4..187d969ed 100644 --- a/applications/settings/power_settings_app/scenes/power_settings_scene_reboot.c +++ b/applications/settings/power_settings_app/scenes/power_settings_scene_reboot.c @@ -24,7 +24,7 @@ void power_settings_scene_reboot_on_enter(void* context) { app); submenu_add_item( submenu, - "Flipper OS", + "Reboot Flipper", PowerSettingsRebootSubmenuIndexOs, power_settings_scene_reboot_submenu_callback, app); @@ -33,14 +33,18 @@ void power_settings_scene_reboot_on_enter(void* context) { } bool power_settings_scene_reboot_on_event(void* context, SceneManagerEvent event) { - UNUSED(context); + PowerSettingsApp* app = context; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { if(event.event == PowerSettingsRebootSubmenuIndexDfu) { - power_reboot(PowerBootModeDfu); + scene_manager_set_scene_state( + app->scene_manager, PowerSettingsAppSceneRebootConfirm, RebootTypeDFU); + scene_manager_next_scene(app->scene_manager, PowerSettingsAppSceneRebootConfirm); } else if(event.event == PowerSettingsRebootSubmenuIndexOs) { - power_reboot(PowerBootModeNormal); + scene_manager_set_scene_state( + app->scene_manager, PowerSettingsAppSceneRebootConfirm, RebootTypeNormal); + scene_manager_next_scene(app->scene_manager, PowerSettingsAppSceneRebootConfirm); } consumed = true; } diff --git a/applications/settings/power_settings_app/scenes/power_settings_scene_reboot_confirm.c b/applications/settings/power_settings_app/scenes/power_settings_scene_reboot_confirm.c new file mode 100644 index 000000000..62e06de92 --- /dev/null +++ b/applications/settings/power_settings_app/scenes/power_settings_scene_reboot_confirm.c @@ -0,0 +1,66 @@ +#include "../power_settings_app.h" + +void power_settings_scene_reboot_confirm_dialog_callback(DialogExResult result, void* context) { + furi_assert(context); + PowerSettingsApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, result); +} + +void power_settings_scene_reboot_confirm_on_enter(void* context) { + PowerSettingsApp* app = context; + DialogEx* dialog = app->dialog; + + RebootType reboot_type = + scene_manager_get_scene_state(app->scene_manager, PowerSettingsAppSceneRebootConfirm); + + if(reboot_type == RebootTypeDFU) { + dialog_ex_set_header(dialog, "Reboot to DFU Mode?", 64, 0, AlignCenter, AlignTop); + dialog_ex_set_text( + dialog, + "Needed for device maintenance\nor firmware upgrades", + 64, + 14, + AlignCenter, + AlignTop); + } else if(reboot_type == RebootTypeNormal) { + dialog_ex_set_header(dialog, "Reboot Flipper?", 64, 0, AlignCenter, AlignTop); + dialog_ex_set_text( + dialog, "May help with some firmware\n issues", 64, 14, AlignCenter, AlignTop); + } else { + furi_crash("Invalid reboot type"); + } + + dialog_ex_set_left_button_text(dialog, "Cancel"); + dialog_ex_set_right_button_text(dialog, "Reboot"); + + dialog_ex_set_result_callback(dialog, power_settings_scene_reboot_confirm_dialog_callback); + dialog_ex_set_context(dialog, app); + + view_dispatcher_switch_to_view(app->view_dispatcher, PowerSettingsAppViewDialog); +} + +bool power_settings_scene_reboot_confirm_on_event(void* context, SceneManagerEvent event) { + PowerSettingsApp* app = context; + bool consumed = false; + RebootType reboot_type = + scene_manager_get_scene_state(app->scene_manager, PowerSettingsAppSceneRebootConfirm); + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == DialogExResultLeft) { + scene_manager_previous_scene(app->scene_manager); + } else if(event.event == DialogExResultRight) { + if(reboot_type == RebootTypeDFU) { + power_reboot(PowerBootModeDfu); + } else { + power_reboot(PowerBootModeNormal); + } + } + consumed = true; + } + return consumed; +} + +void power_settings_scene_reboot_confirm_on_exit(void* context) { + PowerSettingsApp* app = context; + dialog_ex_reset(app->dialog); +} diff --git a/applications/settings/storage_settings/scenes/storage_settings_scene_benchmark.c b/applications/settings/storage_settings/scenes/storage_settings_scene_benchmark.c index e734c78e0..bfc9ac9c9 100644 --- a/applications/settings/storage_settings/scenes/storage_settings_scene_benchmark.c +++ b/applications/settings/storage_settings/scenes/storage_settings_scene_benchmark.c @@ -1,5 +1,7 @@ #include "../storage_settings.h" #include +#include +#include #define BENCH_DATA_SIZE 4096 #define BENCH_COUNT 6 @@ -86,7 +88,8 @@ static void storage_settings_scene_benchmark(StorageSettings* app) { uint32_t bench_w_speed[BENCH_COUNT] = {0, 0, 0, 0, 0, 0}; uint32_t bench_r_speed[BENCH_COUNT] = {0, 0, 0, 0, 0, 0}; - dialog_ex_set_header(dialog_ex, "Benchmarking...", 64, 32, AlignCenter, AlignCenter); + dialog_ex_set_header(dialog_ex, "Benchmarking...", 74, 32, AlignCenter, AlignCenter); + dialog_ex_set_icon(dialog_ex, 12, 20, &I_LoadingHourglass_24x24); for(size_t i = 0; i < BENCH_COUNT; i++) { if(!storage_settings_scene_bench_write( app->fs_api, bench_size[i], bench_data, &bench_w_speed[i])) @@ -95,6 +98,7 @@ static void storage_settings_scene_benchmark(StorageSettings* app) { if(i > 0) furi_string_cat_printf(app->text_string, "\n"); furi_string_cat_printf(app->text_string, "%ub : W %luK ", bench_size[i], bench_w_speed[i]); dialog_ex_set_header(dialog_ex, NULL, 0, 0, AlignCenter, AlignCenter); + dialog_ex_set_icon(dialog_ex, 0, 0, NULL); dialog_ex_set_text( dialog_ex, furi_string_get_cstr(app->text_string), 0, 32, AlignLeft, AlignCenter); @@ -110,6 +114,12 @@ static void storage_settings_scene_benchmark(StorageSettings* app) { dialog_ex, furi_string_get_cstr(app->text_string), 0, 32, AlignLeft, AlignCenter); } + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); + notification_message(notification, &sequence_single_vibro); + notification_message(notification, &sequence_set_green_255); + notification_message(notification, &sequence_success); + furi_record_close(RECORD_NOTIFICATION); + free(bench_data); } @@ -146,11 +156,17 @@ bool storage_settings_scene_benchmark_on_event(void* context, SceneManagerEvent if(event.type == SceneManagerEventTypeCustom) { switch(event.event) { case DialogExResultCenter: - consumed = scene_manager_previous_scene(app->scene_manager); + consumed = scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, StorageSettingsStart); break; } - } else if(event.type == SceneManagerEventTypeBack && sd_status != FSE_OK) { - consumed = true; + } else if(event.type == SceneManagerEventTypeBack) { + if(sd_status == FSE_OK) { + consumed = scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, StorageSettingsStart); + } else { + consumed = true; + } } return consumed; @@ -160,6 +176,10 @@ void storage_settings_scene_benchmark_on_exit(void* context) { StorageSettings* app = context; DialogEx* dialog_ex = app->dialog_ex; + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); + notification_message(notification, &sequence_reset_green); + furi_record_close(RECORD_NOTIFICATION); + dialog_ex_reset(dialog_ex); furi_string_reset(app->text_string); diff --git a/applications/settings/storage_settings/scenes/storage_settings_scene_benchmark_confirm.c b/applications/settings/storage_settings/scenes/storage_settings_scene_benchmark_confirm.c new file mode 100644 index 000000000..2f8644761 --- /dev/null +++ b/applications/settings/storage_settings/scenes/storage_settings_scene_benchmark_confirm.c @@ -0,0 +1,70 @@ +#include "../storage_settings.h" + +static void + storage_settings_scene_benchmark_confirm_dialog_callback(DialogExResult result, void* context) { + StorageSettings* app = context; + + view_dispatcher_send_custom_event(app->view_dispatcher, result); +} + +void storage_settings_scene_benchmark_confirm_on_enter(void* context) { + StorageSettings* app = context; + DialogEx* dialog_ex = app->dialog_ex; + + FS_Error sd_status = storage_sd_status(app->fs_api); + + if(sd_status == FSE_NOT_READY) { + dialog_ex_set_icon(dialog_ex, 83, 22, &I_WarningDolphinFlip_45x42); + dialog_ex_set_header(dialog_ex, "SD Card Not Mounted", 64, 3, AlignCenter, AlignTop); + dialog_ex_set_text( + dialog_ex, "Try to reinsert\nor format SD\ncard.", 3, 19, AlignLeft, AlignTop); + dialog_ex_set_center_button_text(dialog_ex, "Ok"); + } else { + dialog_ex_set_header(dialog_ex, "Benchmark SD Card?", 64, 0, AlignCenter, AlignTop); + dialog_ex_set_text( + dialog_ex, + "SD will be tested in SPI\nmode. Learn more:\nr.flipper.net/sd_test", + 0, + 12, + AlignLeft, + AlignTop); + dialog_ex_set_icon(dialog_ex, 103, 12, &I_qr_benchmark_25x25); + dialog_ex_set_left_button_text(dialog_ex, "Cancel"); + dialog_ex_set_right_button_text(dialog_ex, "Benchmark"); + } + + dialog_ex_set_context(dialog_ex, app); + dialog_ex_set_result_callback( + dialog_ex, storage_settings_scene_benchmark_confirm_dialog_callback); + + view_dispatcher_switch_to_view(app->view_dispatcher, StorageSettingsViewDialogEx); +} + +bool storage_settings_scene_benchmark_confirm_on_event(void* context, SceneManagerEvent event) { + StorageSettings* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { + case DialogExResultLeft: + case DialogExResultCenter: + consumed = scene_manager_previous_scene(app->scene_manager); + break; + case DialogExResultRight: + scene_manager_next_scene(app->scene_manager, StorageSettingsBenchmark); + consumed = true; + break; + } + } else if(event.type == SceneManagerEventTypeBack) { + consumed = true; + } + + return consumed; +} + +void storage_settings_scene_benchmark_confirm_on_exit(void* context) { + StorageSettings* app = context; + DialogEx* dialog_ex = app->dialog_ex; + + dialog_ex_reset(dialog_ex); +} diff --git a/applications/settings/storage_settings/scenes/storage_settings_scene_config.h b/applications/settings/storage_settings/scenes/storage_settings_scene_config.h index 18e7ba5aa..d6e76894b 100644 --- a/applications/settings/storage_settings/scenes/storage_settings_scene_config.h +++ b/applications/settings/storage_settings/scenes/storage_settings_scene_config.h @@ -5,5 +5,6 @@ ADD_SCENE(storage_settings, format_confirm, FormatConfirm) ADD_SCENE(storage_settings, formatting, Formatting) ADD_SCENE(storage_settings, sd_info, SDInfo) ADD_SCENE(storage_settings, internal_info, InternalInfo) +ADD_SCENE(storage_settings, benchmark_confirm, BenchmarkConfirm) ADD_SCENE(storage_settings, benchmark, Benchmark) ADD_SCENE(storage_settings, factory_reset, FactoryReset) diff --git a/applications/settings/storage_settings/scenes/storage_settings_scene_factory_reset.c b/applications/settings/storage_settings/scenes/storage_settings_scene_factory_reset.c index 5832c6589..2d977176a 100644 --- a/applications/settings/storage_settings/scenes/storage_settings_scene_factory_reset.c +++ b/applications/settings/storage_settings/scenes/storage_settings_scene_factory_reset.c @@ -21,14 +21,14 @@ void storage_settings_scene_factory_reset_on_enter(void* context) { dialog_ex_set_left_button_text(dialog_ex, "Cancel"); dialog_ex_set_right_button_text(dialog_ex, "Erase"); - dialog_ex_set_header(dialog_ex, "Confirm Factory Reset", 64, 10, AlignCenter, AlignCenter); + dialog_ex_set_header(dialog_ex, "Confirm Factory Reset?", 64, 0, AlignCenter, AlignTop); dialog_ex_set_text( dialog_ex, - "Internal storage will be erased\r\nData and settings will be lost!", + "Internal storage will be erased\ndata and settings will be lost!", 64, - 32, + 14, AlignCenter, - AlignCenter); + AlignTop); view_dispatcher_switch_to_view(app->view_dispatcher, StorageSettingsViewDialogEx); } diff --git a/applications/settings/storage_settings/scenes/storage_settings_scene_format_confirm.c b/applications/settings/storage_settings/scenes/storage_settings_scene_format_confirm.c index 862f55a46..13b368d3f 100644 --- a/applications/settings/storage_settings/scenes/storage_settings_scene_format_confirm.c +++ b/applications/settings/storage_settings/scenes/storage_settings_scene_format_confirm.c @@ -20,8 +20,8 @@ void storage_settings_scene_format_confirm_on_enter(void* context) { dialog_ex, "Try to reinsert\nor format SD\ncard.", 3, 19, AlignLeft, AlignTop); dialog_ex_set_center_button_text(dialog_ex, "Ok"); } else { - dialog_ex_set_header(dialog_ex, "Format SD Card?", 64, 10, AlignCenter, AlignCenter); - dialog_ex_set_text(dialog_ex, "All data will be lost!", 64, 32, AlignCenter, AlignCenter); + dialog_ex_set_header(dialog_ex, "Format SD Card?", 64, 0, AlignCenter, AlignTop); + dialog_ex_set_text(dialog_ex, "All data will be lost!", 64, 12, AlignCenter, AlignTop); dialog_ex_set_left_button_text(dialog_ex, "Cancel"); dialog_ex_set_right_button_text(dialog_ex, "Format"); } diff --git a/applications/settings/storage_settings/scenes/storage_settings_scene_formatting.c b/applications/settings/storage_settings/scenes/storage_settings_scene_formatting.c index f107aacea..2fb232f14 100644 --- a/applications/settings/storage_settings/scenes/storage_settings_scene_formatting.c +++ b/applications/settings/storage_settings/scenes/storage_settings_scene_formatting.c @@ -1,4 +1,6 @@ #include "../storage_settings.h" +#include +#include static const NotificationMessage message_green_165 = { .type = NotificationMessageTypeLedGreen, @@ -31,7 +33,8 @@ void storage_settings_scene_formatting_on_enter(void* context) { FS_Error error; DialogEx* dialog_ex = app->dialog_ex; - dialog_ex_set_header(dialog_ex, "Formatting...", 64, 32, AlignCenter, AlignCenter); + dialog_ex_set_header(dialog_ex, "Formatting...", 70, 32, AlignCenter, AlignCenter); + dialog_ex_set_icon(dialog_ex, 15, 20, &I_LoadingHourglass_24x24); view_dispatcher_switch_to_view(app->view_dispatcher, StorageSettingsViewDialogEx); notification_message_block(app->notification, &sequence_set_formatting_leds); @@ -44,11 +47,17 @@ void storage_settings_scene_formatting_on_enter(void* context) { if(error != FSE_OK) { dialog_ex_set_header(dialog_ex, "Cannot Format SD Card", 64, 10, AlignCenter, AlignCenter); + dialog_ex_set_icon(dialog_ex, 0, 0, NULL); dialog_ex_set_text( dialog_ex, storage_error_get_desc(error), 64, 32, AlignCenter, AlignCenter); } else { dialog_ex_set_icon(dialog_ex, 83, 22, &I_WarningDolphinFlip_45x42); dialog_ex_set_header(dialog_ex, "Format\ncomplete!", 14, 15, AlignLeft, AlignTop); + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); + notification_message(notification, &sequence_single_vibro); + notification_message(notification, &sequence_set_green_255); + notification_message(notification, &sequence_success); + furi_record_close(RECORD_NOTIFICATION); } dialog_ex_set_center_button_text(dialog_ex, "OK"); } @@ -75,5 +84,9 @@ void storage_settings_scene_formatting_on_exit(void* context) { StorageSettings* app = context; DialogEx* dialog_ex = app->dialog_ex; + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); + notification_message(notification, &sequence_reset_green); + furi_record_close(RECORD_NOTIFICATION); + dialog_ex_reset(dialog_ex); } diff --git a/applications/settings/storage_settings/scenes/storage_settings_scene_internal_info.c b/applications/settings/storage_settings/scenes/storage_settings_scene_internal_info.c index f205efc0a..b7620b6e8 100644 --- a/applications/settings/storage_settings/scenes/storage_settings_scene_internal_info.c +++ b/applications/settings/storage_settings/scenes/storage_settings_scene_internal_info.c @@ -27,7 +27,7 @@ void storage_settings_scene_internal_info_on_enter(void* context) { } else { furi_string_printf( app->text_string, - "Label: %s\nType: LittleFS\n%lu KiB total\n%lu KiB free", + "Name: %s\nType: LittleFS\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)); diff --git a/applications/settings/storage_settings/scenes/storage_settings_scene_sd_info.c b/applications/settings/storage_settings/scenes/storage_settings_scene_sd_info.c index aa9662a71..cad3fbfaf 100644 --- a/applications/settings/storage_settings/scenes/storage_settings_scene_sd_info.c +++ b/applications/settings/storage_settings/scenes/storage_settings_scene_sd_info.c @@ -27,12 +27,31 @@ void storage_settings_scene_sd_info_on_enter(void* context) { } else { furi_string_printf( app->text_string, - "Label: %s\nType: %s\n%lu KiB total\n%lu KiB free\n" - "%02X%s %s v%i.%i\nSN:%04lX %02i/%i", + "Label: %s\nType: %s\n", sd_info.label, - sd_api_get_fs_type_text(sd_info.fs_type), - sd_info.kb_total, - sd_info.kb_free, + sd_api_get_fs_type_text(sd_info.fs_type)); + + if(sd_info.kb_total < 1024) { + furi_string_cat_printf(app->text_string, "Total: %lu KiB\n", sd_info.kb_total); + } else if(sd_info.kb_total < 1024 * 1024) { + furi_string_cat_printf(app->text_string, "Total: %lu MiB\n", sd_info.kb_total / 1024); + } else { + furi_string_cat_printf( + app->text_string, "Total: %lu GiB\n", sd_info.kb_total / (1024 * 1024)); + } + + if(sd_info.kb_free < 1024) { + furi_string_cat_printf(app->text_string, "Free: %lu KiB\n", sd_info.kb_free); + } else if(sd_info.kb_free < 1024 * 1024) { + furi_string_cat_printf(app->text_string, "Free: %lu MiB\n", sd_info.kb_free / 1024); + } else { + furi_string_cat_printf( + app->text_string, "Free: %lu GiB\n", sd_info.kb_free / (1024 * 1024)); + } + + furi_string_cat_printf( + app->text_string, + "%02X%s %s v%i.%i\nSN:%04lX %02i/%i", sd_info.manufacturer_id, sd_info.oem_id, sd_info.product_name, @@ -41,6 +60,7 @@ void storage_settings_scene_sd_info_on_enter(void* context) { sd_info.product_serial_number, sd_info.manufacturing_month, sd_info.manufacturing_year); + dialog_ex_set_text( dialog_ex, furi_string_get_cstr(app->text_string), 4, 1, AlignLeft, AlignTop); } diff --git a/applications/settings/storage_settings/scenes/storage_settings_scene_start.c b/applications/settings/storage_settings/scenes/storage_settings_scene_start.c index 0e667024f..e351a2ef7 100644 --- a/applications/settings/storage_settings/scenes/storage_settings_scene_start.c +++ b/applications/settings/storage_settings/scenes/storage_settings_scene_start.c @@ -109,7 +109,7 @@ bool storage_settings_scene_start_on_event(void* context, SceneManagerEvent even case StorageSettingsStartSubmenuIndexBenchy: scene_manager_set_scene_state( app->scene_manager, StorageSettingsStart, StorageSettingsStartSubmenuIndexBenchy); - scene_manager_next_scene(app->scene_manager, StorageSettingsBenchmark); + scene_manager_next_scene(app->scene_manager, StorageSettingsBenchmarkConfirm); consumed = true; break; case StorageSettingsStartSubmenuIndexFactoryReset: diff --git a/assets/icons/About/CertificationChina1_122x47.png b/assets/icons/About/CertificationChina1_122x47.png deleted file mode 100644 index c5eebec6776af61659c796fa55741f9a549bedae..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5215 zcmb7IXH-*Lw++2_sUn0RO@vUScL+@hy%@YfiS?M;TCvfOBcMCi;^oyL!DL?r%WX9fn%M3I3I5$Mj59D zI^k6&{yyFYgMcR?ST8luWvD4o2Ze?Mo3c4X?^iN$r3UiAVttjt z;K0B@$v|026xtmut)!#`mXZO>$Vd<)BrriptP@TGi4i=e_{jl*V_eV(Un~NJ1Rirb zIivisY9J612mT_5;}CzcBQd|)Ckg>RM!?dNQsBRa`+A_TD2xZn_m7JHmh%Vvk3=lO z?Z2FJjQk^X}L{)9{sIJoy^2*L-B#1Ms714+xs{09pA z3wlA}1pGs}pVUy4E0J0DKOjRL9aA*Q4dG41AP9`Fw^Ptb#Ic0RhCWX2a3eU<9qXY6 z`o;PuV1sZy-v7Op6X3WxZ~tR=SRoNaIewOT0$u(SQv)fqqK!r9pew)vc zom9enX>1GxIMy!GY|xoX05*hTs&^}aLrd>3aC{QGO9IsdY_~*$MQ@^=U5WtSWV4e5 z!ZN_@hE47%+aI|<3ZPd+&oKkSJGAXY&oj^IjFAFf8Ut^!lWH2pHO_J{FH6 z(Y7mVft_o*6d`N58~vNGbUxizE5NG@>JGOl!resXV7ig#OWRF%{ZpwsS>vw)w+99a zM58a$R-LJh5Y)Lh@MhOoWEqvs9hDs%Is~*{)g67+Izx)!3;W!n{u;;?uSIe*QniID zYV*hB#iQ1<#uV+ z-EcY#^`<}o%SM!-fPAc?Wg6$1;ssfm&U>oyPMvoxI%1k2@eDwF;5lkTQ-XII^oGs8 z_OdM8E;(DHJ5%iNT=012qOjll?@wxURqNCrz;g5MkV>;VKLYuoFLY!xt8mTr?C@P}d2A!gaE8R|3V>3)+eXqP$9HaI1VV$B?Hs_=IZV5<`6YT`4 z^G;+T`IfC}BQr|L{tT^%zUgBEXp(u!tskeq4DTbOVQu&eYSHA zE}$Eu^u$kTJ>|EI>v^hAW+05lRJ3(PG!FXdXy8H*}*l7(?YAx;bztxv#CLcjR$hqJ`1Xy()bqXoY$RaCyY< zF^gAc^0{?Ke>H3C;qV3=T(!|u|9~lsEYLdjHEE`8CNgv2VWak7hIVIEcb;h8IW~7V zXSJd^B0s(}ojNzJ%I>P&IKZ?CWf|sxn#ovWfi!QOR#*!J93=1T?C>9T>GD!J7H|e1 zFon#njI3J~lC(7HkTyOx=$uZVBRRSxHI?p0Z4@C?8MW_6k>A2e9?8s1;;acWpqO>q z9IboM@w)Jg5}MQG1g8TWW`F`UbilK{uaM^7SHk` zHlPsvR-N9F=Z?BtvXYbcjl@nz#ARXOb~k7EWeV%04^Gf^b%$2vD!vsmFtTQzk$O>s1Bn|zyA8)Emo zKg$H`bx25|#2}q|Vp#|8%!L`y41R`hhQ^udD@#=h1jL=jYZ!W;i?zVg;G9soz$)mB z$W>wg)QNPtS8lF0Xi=r4^8y1xHa7#)Qc}U`1!)>)wW33(_%G7y8f0CBX^&n+6xtPH z&D726%IYmOJVA1C%5k=aDUzI|Kyc6})R9kW;8Q0e=TM}Ee$5nWW-jQtT^ z#Yx4l5s3%_Ld2uN1GR*G#-MgHUk`jA{9yR4$>7?%NiP|>Jh`B3?S5FcRrXePrksO} zD7_bDoOHffU#TOnqn-1nhgq$6?Ni&}@+?~`+qhb!#}-1iPT!;3W6=xz+eBH2{eBjs zVYi+LM8DLcuts+=Q*@=``IQD}gN=#)z*R5| zm0*8s!t8}{h|#28K}kVLGbF6}$p0`!$0*h}Ha_+|uerk9#?3%V9R^Yc2qm2I8hr(K z@C8@ySgr*gNm*~HCim#mN?e`XQ2CFJ*pH>|rC&@Z??v8&>+CSS@VUXG!qt7+M0Ub? z%yZ1$*^|``BnNXr_uTKcoV@V%LN~Vflk_{J?1QZ4tP$_xQNNnS^J*M%x4Iv?)>Y#B zllt5H=NEGN34I!{5!M)@}Z9kC+_1qynTT2 z$LE>pw8iQV^KYp0SXcxXl0A67e*DmCHDx=G|B&>BIGUEKFm-&c0_-VMK2 z7!E91t4XaF)D>(<4xV$L?%pA+41XHfFxi=$o8)%nYhfy7#Bq6Wi_(vA>}I$Oyq;c} zX`)Bdq*0uq$9Hc{znRLX?uu-W?2RO7W3|(?hhoQ=>Zto^%NbS~zES6J1b}jx`M6)6 zKcF0l7Mc36ju&hKxj||nzk%35!+PpB(D7-ya!K0yx;Jd4KO<}XYtKFtpzK(8w(b~9 ztV_x}bxkpui}SP%`_9==$ie1r4NYG(4UG!hgfOL$q+wIKYvx%KPh+>UDbNbT&`j3c zYSm)Q=K7$lo`aFxMfMW;2dTfMy{j+YN}}Pe@iZE~oKjWn@+76^i~@XY@2lSIw=Vv? zmOCv4zQDYj`Drg@4`nrC9s1XLr{Ir!m)LzN9#Md!Bie4YAyW%H8kZ0c92PeE++m?G z@#<$aWYyx;*E;OJ+J1eMyPlh*w1##;H!N;17SFA|7FjG=Mu{Uhk2+MUJm}fprqVn9zNx+uRko}w>6)P1^N-VDS8Lkc*6!%!rK>D9G)iJJ z2M)dLdzv1wrl*YxrG(P%6DLWHr8VB=<=R0K-8#h#g%900M+I!R&ggQ$y|Ln^!h>(1 z)FxoXDR!ZI840P`l!yc?cq1} zFYKpnENpsh01TnouygRSxLOtSwAiCJz5Ml+fupvgVz->Z`VAk4`b+hJU*abR&1bDFYTn@%(`;+4 z?FlcuRw}l~Tc4j^&iyREHaKbVl5^$OTF6Uq$mG^x>V6JWCDZ#u&%@MV&8PV?@HETiObFj&SB&WP;WYn-%EX1gxAmHE#ebb8VYOL#ZA(Dx}Kj76XBOL z@py^x+$?(};V0o!B4|>e!iHaX4|_8@yLj;CjM5tB=q<%|Ol)F4=kgUbTc)FhuY^|l zf%@;4>@#@?F9`dg*RLHB4jKw3ZoN2+IYfBdMvIwFJE zpe7NRRcuV;`%K>IPR;eI+wXn``pqXM{QP;&rA~7<3fgkU{*goef!p!S=HG3(&A+tt z$hq{Y@ppAKjl3JbGo!TF{iyl%C8=;#dusmhu~X*`J-bm|1yq2!+sV8YYKc%vQ#U`J z$LlKcKEan(Boo>@Cqb%&XH0tpzTFoW9`ra$3N(oSyugjqAR|~7jKk-uSSHHRL~MS{ zzQFW!F}-+OPOwPhZAG=>h@Q{ahb+VRTbmE$E}6(Se)2~+gj(^?jfX|O-ZK$1aPmmL+?^DQM`oti{VrBbfo(e9UvGgc)A@+W8;%_fED}|FnOnPK?GXmez zW9t&0Pn3&eSM}__;_~zzfie)P~nyw>V28Q1yeB61}Te|h>4g?otq5}8d8k~gkzumg2!gZFl zvLbryU#=#LFB7VnwGMuGM=?6KN|&VstPd;9m`M@C*(i}iCKHr)RPy}}iBZL^W|Y~O z6;h37DM7!#(aewmJKZ$H#RrX${wG2(jr3!~vR zq@K!$o8e3fw89tJOLxcKk*PoJ8CMnaZCfx2`ig0ESwY6mP9$`FW*js7GX11~xrE9Z rqu(eo!N7TLbCW}jx6tHO{sF*qKQAKgqL%dWdqG28V@QRzW90t;o{#2fgCGpt{r{gnt;J-61p8ogdWO`YAeYSz*bx9<@-Fd~ zR^6UY2lfUE#NfC!4BYfFWoocr~N1$?W` zz-`wpS@O%m@F$g4XC<_Qv$fkXZ&C;TS)RL*0#|Id*=r76cgrDw#~cVF)3frB zOw(6pd7MjmbS9^n%de7q8gmH*eN7^$NxuPF6@EXoi8>^*7SDDC7pUIr0?rh?%La<(z0qUK*$+(Ip2u9X-3pC)3^K@Q zP(0Wili7~plh?swHA|K(`DYv#LuSDVdAX8cGcc_&^DP1Iueb^Gy_#h1Ky^%CZLm*qp|d(S6?g!z6ae*z2u Xf^!1xOY<4R00000NkvXXu0mjf#QVd4 literal 0 HcmV?d00001 diff --git a/assets/icons/Settings/Cry_dolph_55x52.png b/assets/icons/Settings/LoadingHourglass_24x24.png similarity index 70% rename from assets/icons/Settings/Cry_dolph_55x52.png rename to assets/icons/Settings/LoadingHourglass_24x24.png index 86d9db1b497cd9e49bb62987cb35075b4bc7a410..9c49dcad1cb6060c4057ad5c44d67f440c2a91b1 100644 GIT binary patch delta 422 zcmdlbcSuIDGr-TCmrII^fq{Y7)59eQNK1e)2Q!eozVhs;jf&-5j2e??a`kB#>Lwc) z7$}%q>RBcyC!3_CS{myp7#SED=^Gg98=B}Enphc_TNxQm_T?^_tiz);c^7x~WIkR6 zeq%jj1r6WC(@vVoXfTjNL2^%w0_l%nc1)ogJM_ja}WG%`J=!jVz6g zEtM21atnNYtz7bxOLJ56N<3X`m4G68DVZr&P`wto^iHoSkw4cEn{ pO$PgN{;}_HzIpl9d4&=d35G^5hRi9SpH2l?;pyt^U8eBS^7 delta 696 zcmX>kvrA5~Gr-TCmrII^fq{Y7)59eQNSgz(2?sNfl$;oFYolU07h~DvnOuEp2D-@x z1_lb|mU=0PNr}lxNjeHf21bVZ1}6H3Cb|X&R>qcACMJ{pxl1O$;ZB*nhg;L7z}MHx zzbG?3GcPg6B|o_|H#M)s)5TV)BDX*CnUiJFPK_^myVa^ud6XN>+|9>De^w8xxAobJJ#WAGf)|P3v z`Hm=XxXk?j|D~_l-ED`@Hm1CFp8ZHLP{OpE_li$WhwqX}g~vj^|IU?<-sg5(fvPv+mW0F?cs@P-Z_VT$$NG{hH`nVv zlUXCaFKOP6gLCvACkH&!sy`rF_L%=!(v$w{^*^3oN|~~gdE1i<1+O@bw2yXO77kgf z#DDEo?1Ytqd#-*JoKmzopr06iN2s{jB1 diff --git a/assets/icons/Settings/dolph_cry_49x54.png b/assets/icons/Settings/dolph_cry_49x54.png new file mode 100644 index 0000000000000000000000000000000000000000..351a849b09030fd4628bbbf582503dfb83672c59 GIT binary patch literal 973 zcmV;;12X)HP)^74TPjRsUxkj1>*;PV6t7eFDw(WuIv6mwruS4`RwBiLhH>T z{w$4%m|Dg4qvN>VK1(Upyp@rywexF>C)i^7{(8MCrLVy9Z9!%5NH{TpXdrs6H9UHa z5tNL;i)}WJG~PVpYsanUTl7ke$dvP#V@%tpH;=Rut}S|{M%YEIJL9co;dDw;^F6Cx z$C{R$Km$;4PdV&`Rz~sBj04qO|QCb*yOzu=3VLgIgDU4W=?ZS-np$Do9 zhIg?90##V7#>C3X=H0*(UG*tA$;#mqvEK`;^KJx|I80`*G(HKtxvHb0M2oTx^GQRZ zjXW8l6~GHGuao=%-Y?aPPG+T!&!Mk8ZIfTy+8p>pnN1$ zM{-HmvWmISe<{7O+N*>!( z{e2b19v&n4Ah0~v;G(bJalS_mL-X5?9s$fNN;B$hE5mjbdn8;*TyJgW1^nJ450hI^ zo!E@ATh?HQ*?1H}tuGE3Eu!jT*EN1~XI;q723Df=ndO_!B`g%U7L7XY*jel{Lzy>v z!!x=);vDO+1%M3qrZ$DTrql1s--KqG{?#)hgFUPmL_Q`q<_79m{qTrHKj{%mgL>gvQdo~O z(*W-T_vTa&OKbIFhvCq_Mf4)ulx$Add!@Pg$M`GA@@)IQc1CSRm`6`|Vg#N+4Ab5K v&c;UCdJm6;)7|(ljL0hGuP2^DR(t*eG-K{iyaDCP00000NkvXXu0mjfItI*` literal 0 HcmV?d00001 diff --git a/assets/icons/Settings/qr_benchmark_25x25.png b/assets/icons/Settings/qr_benchmark_25x25.png new file mode 100644 index 0000000000000000000000000000000000000000..c5f9df119582ba1625b5c40d869150d72e49bf37 GIT binary patch literal 395 zcmV;60d)R}P)P000>X1^@s6#OZ}&00009a7bBm000XU z000XU0RWnu7ytkO0drDELIAGL9O(c600d`2O+f$vv5yPj=?b%4XtA$ZUrb~kgF?s8V;qp}O#oaJ%lv*EAWmF>5n1cxby zZ@WK8Co29`v?;A*8@aB&yK3c{ZjAdo-yow0t#gmg;IW%dkZh1tUj>h|n{q#{*|F34 z*{Pe2!F^X5K6B(u(0zn1Pbpp zTxys%i6%2vhazbe_HAZVefNc^`O$7OBU_K!K=C@ Date: Wed, 15 May 2024 14:25:02 +0300 Subject: [PATCH 02/11] Add new map analyser (#3648) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add new map analyser * Fix typos Co-authored-by: hedger Co-authored-by: あく --- .github/workflows/build.yml | 29 +--- scripts/map_analyse_upload.py | 86 +++++++++++ scripts/map_mariadb_insert.py | 139 ----------------- scripts/map_parser.py | 274 ---------------------------------- 4 files changed, 92 insertions(+), 436 deletions(-) create mode 100755 scripts/map_analyse_upload.py delete mode 100755 scripts/map_mariadb_insert.py delete mode 100755 scripts/map_parser.py diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 252310af6..2bc2178ae 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -19,7 +19,7 @@ jobs: strategy: fail-fast: false matrix: - target: [f7, f18] + target: [f7, f18] steps: - name: 'Wipe workspace' run: find ./ -mount -maxdepth 1 -exec rm -rf {} \; @@ -103,29 +103,12 @@ jobs: run: | cp build/${DEFAULT_TARGET}-firmware-*/firmware.elf.map map_analyser_files/firmware.elf.map cp build/${DEFAULT_TARGET}-firmware-*/firmware.elf map_analyser_files/firmware.elf - cp ${{ github.event_path }} map_analyser_files/event.json source scripts/toolchain/fbtenv.sh - get_size() - { - SECTION="$1"; - arm-none-eabi-size \ - -A map_analyser_files/firmware.elf \ - | grep "^$SECTION" | awk '{print $2}' - } - export BSS_SIZE="$(get_size ".bss")" - export TEXT_SIZE="$(get_size ".text")" - export RODATA_SIZE="$(get_size ".rodata")" - export DATA_SIZE="$(get_size ".data")" - export FREE_FLASH_SIZE="$(get_size ".free_flash")" - python3 -m pip install mariadb==1.1.6 cxxfilt==0.3.0 - python3 scripts/map_parser.py map_analyser_files/firmware.elf.map map_analyser_files/firmware.elf.map.all - python3 scripts/map_mariadb_insert.py \ - ${{ secrets.AMAP_MARIADB_USER }} \ - ${{ secrets.AMAP_MARIADB_PASSWORD }} \ - ${{ secrets.AMAP_MARIADB_HOST }} \ - ${{ secrets.AMAP_MARIADB_PORT }} \ - ${{ secrets.AMAP_MARIADB_DATABASE }} \ - map_analyser_files/firmware.elf.map.all + python3 scripts/map_analyse_upload.py \ + "--elf_file=map_analyser_files/firmware.elf" \ + "--map_file=map_analyser_files/firmware.elf.map" \ + "--analyser_url=${{ secrets.ANALYSER_URL }}" \ + "--analyser_token=${{ secrets.ANALYSER_TOKEN }}"; - name: 'Find previous comment' if: ${{ !github.event.pull_request.head.repo.fork && matrix.target == env.DEFAULT_TARGET && github.event.pull_request }} diff --git a/scripts/map_analyse_upload.py b/scripts/map_analyse_upload.py new file mode 100755 index 000000000..38d961879 --- /dev/null +++ b/scripts/map_analyse_upload.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python3 + +import os +import requests +import argparse +import subprocess + +# usage: +# COMMIT_HASH, COMMIT_MSG, BRANCH_NAME, +# PULL_ID(optional), PULL_NAME(optional) must be set as envs +# maybe from sctipts/get_env.py +# other args must be set via command line args + + +class AnalyseRequest: + def __init__(self): + self.commit_hash = os.environ["COMMIT_HASH"] + self.commit_msg = os.environ["COMMIT_MSG"] + self.branch_name = os.environ["BRANCH_NAME"] + self.pull_id = os.getenv("PULL_ID", default=None) + self.pull_name = os.getenv("PULL_NAME", default=None) + + def get_payload(self): + return vars(self) + + +class AnalyseUploader: + def __init__(self): + self.args = self.parse_args() + + @staticmethod + def get_sections_size(elf_file) -> dict: + ret = dict() + all_sizes = subprocess.check_output( + ["arm-none-eabi-size", "-A", elf_file], shell=False + ) + all_sizes = all_sizes.splitlines() + + sections_to_keep = (".text", ".rodata", ".data", ".bss", ".free_flash") + for line in all_sizes: + line = line.decode("utf-8") + parts = line.split() + if len(parts) != 3: + continue + section, size, _ = parts + if section not in sections_to_keep: + continue + section_size_payload_name = ( + section[1:] if section.startswith(".") else section + ) + section_size_payload_name += "_size" + ret[section_size_payload_name] = size + return ret + + @staticmethod + def parse_args(): + parser = argparse.ArgumentParser() + parser.add_argument("--elf_file", help="Firmware ELF file", required=True) + parser.add_argument("--map_file", help="Firmware MAP file", required=True) + parser.add_argument( + "--analyser_token", help="Analyser auth token", required=True + ) + parser.add_argument( + "--analyser_url", help="Analyser analyse url", required=True + ) + args = parser.parse_args() + return args + + def upload_analyse_request(self): + payload = AnalyseRequest().get_payload() | self.get_sections_size( + self.args.elf_file + ) + headers = {"Authorization": f"Bearer {self.args.analyser_token}"} + file = {"map_file": open(self.args.map_file, "rb")} + response = requests.post( + self.args.analyser_url, data=payload, files=file, headers=headers + ) + if not response.ok: + raise Exception( + f"Failed to upload map file, code: {response.status_code}, reason: {response.text}" + ) + + +if __name__ == "__main__": + analyzer = AnalyseUploader() + analyzer.upload_analyse_request() diff --git a/scripts/map_mariadb_insert.py b/scripts/map_mariadb_insert.py deleted file mode 100755 index a4c9ed5c7..000000000 --- a/scripts/map_mariadb_insert.py +++ /dev/null @@ -1,139 +0,0 @@ -#!/usr/bin/env python3 - -# Requiremets: -# mariadb==1.1.6 - -from datetime import datetime -import argparse -import mariadb -import sys -import os - - -def parseArgs(): - parser = argparse.ArgumentParser() - parser.add_argument("db_user", help="MariaDB user") - parser.add_argument("db_pass", help="MariaDB password") - parser.add_argument("db_host", help="MariaDB hostname") - parser.add_argument("db_port", type=int, help="MariaDB port") - parser.add_argument("db_name", help="MariaDB database") - parser.add_argument("report_file", help="Report file(.map.all)") - args = parser.parse_args() - return args - - -def mariadbConnect(args): - try: - conn = mariadb.connect( - user=args.db_user, - password=args.db_pass, - host=args.db_host, - port=args.db_port, - database=args.db_name, - ) - except mariadb.Error as e: - print(f"Error connecting to MariaDB: {e}") - sys.exit(1) - return conn - - -def parseEnv(): - outArr = [] - outArr.append(datetime.now().strftime("%Y-%m-%d %H:%M:%S")) - outArr.append(os.getenv("COMMIT_HASH", default=None)) - outArr.append(os.getenv("COMMIT_MSG", default=None)) - outArr.append(os.getenv("BRANCH_NAME", default=None)) - outArr.append(os.getenv("BSS_SIZE", default=None)) - outArr.append(os.getenv("TEXT_SIZE", default=None)) - outArr.append(os.getenv("RODATA_SIZE", default=None)) - outArr.append(os.getenv("DATA_SIZE", default=None)) - outArr.append(os.getenv("FREE_FLASH_SIZE", default=None)) - outArr.append(os.getenv("PULL_ID", default=None)) - outArr.append(os.getenv("PULL_NAME", default=None)) - return outArr - - -def createTables(cur, conn): - headerTable = "CREATE TABLE IF NOT EXISTS `header` ( \ - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, \ - `datetime` datetime NOT NULL, \ - `commit` varchar(40) NOT NULL, \ - `commit_msg` text NOT NULL, \ - `branch_name` text NOT NULL, \ - `bss_size` int(10) unsigned NOT NULL, \ - `text_size` int(10) unsigned NOT NULL, \ - `rodata_size` int(10) unsigned NOT NULL, \ - `data_size` int(10) unsigned NOT NULL, \ - `free_flash_size` int(10) unsigned NOT NULL, \ - `pullrequest_id` int(10) unsigned DEFAULT NULL, \ - `pullrequest_name` text DEFAULT NULL, \ - PRIMARY KEY (`id`), \ - KEY `header_id_index` (`id`) )" - dataTable = "CREATE TABLE IF NOT EXISTS `data` ( \ - `header_id` int(10) unsigned NOT NULL, \ - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, \ - `section` text NOT NULL, \ - `address` text NOT NULL, \ - `size` int(10) unsigned NOT NULL, \ - `name` text NOT NULL, \ - `lib` text NOT NULL, \ - `obj_name` text NOT NULL, \ - PRIMARY KEY (`id`), \ - KEY `data_id_index` (`id`), \ - KEY `data_header_id_index` (`header_id`), \ - CONSTRAINT `data_header_id_foreign` FOREIGN KEY (`header_id`) REFERENCES `header` (`id`) )" - cur.execute(headerTable) - cur.execute(dataTable) - conn.commit() - - -def insertHeader(data, cur, conn): - query = "INSERT INTO `header` ( \ - datetime, commit, commit_msg, branch_name, bss_size, text_size, \ - rodata_size, data_size, free_flash_size, pullrequest_id, pullrequest_name) \ - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" - cur.execute(query, data) - conn.commit() - return cur.lastrowid - - -def parseFile(fileObj, headerID): - arr = [] - fileLines = fileObj.readlines() - for line in fileLines: - lineArr = [] - tempLineArr = line.split("\t") - lineArr.append(headerID) - lineArr.append(tempLineArr[0]) # section - lineArr.append(int(tempLineArr[2], 16)) # address hex - lineArr.append(int(tempLineArr[3])) # size - lineArr.append(tempLineArr[4]) # name - lineArr.append(tempLineArr[5]) # lib - lineArr.append(tempLineArr[6]) # obj_name - arr.append(tuple(lineArr)) - return arr - - -def insertData(data, cur, conn): - query = "INSERT INTO `data` ( \ - header_id, section, address, size, \ - name, lib, obj_name) \ - VALUES (?, ?, ?, ?, ? ,?, ?)" - cur.executemany(query, data) - conn.commit() - - -def main(): - args = parseArgs() - dbConn = mariadbConnect(args) - reportFile = open(args.report_file) - dbCurs = dbConn.cursor() - createTables(dbCurs, dbConn) - headerID = insertHeader(parseEnv(), dbCurs, dbConn) - insertData(parseFile(reportFile, headerID), dbCurs, dbConn) - reportFile.close() - dbCurs.close() - - -if __name__ == "__main__": - main() diff --git a/scripts/map_parser.py b/scripts/map_parser.py deleted file mode 100755 index 1efc4fe82..000000000 --- a/scripts/map_parser.py +++ /dev/null @@ -1,274 +0,0 @@ -#!/usr/bin/env python3 - -# Requiremets: -# cxxfilt==0.3.0 - -# Most part of this code written by Lars-Dominik Braun https://github.com/PromyLOPh/linkermapviz -# and distributes under MIT licence - -# Copyright (c) 2017 Lars-Dominik Braun -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. - -import sys -import re -import os -from typing import TextIO -from cxxfilt import demangle - - -class Objectfile: - def __init__(self, section: str, offset: int, size: int, comment: str): - self.section = section.strip() - self.offset = offset - self.size = size - self.path = (None, None) - self.basepath = None - - if comment: - self.path = re.match(r"^(.+?)(?:\(([^\)]+)\))?$", comment).groups() - self.basepath = os.path.basename(self.path[0]) - - self.children = [] - - def __repr__(self) -> str: - return f"" - - -def update_children_size(children: list[list], subsection_size: int) -> list: - # set subsection size to an only child - if len(children) == 1: - children[0][1] = subsection_size - return children - - rest_size = subsection_size - - for index in range(1, len(children)): - if rest_size > 0: - # current size = current address - previous child address - child_size = children[index][0] - children[index - 1][0] - rest_size -= child_size - children[index - 1][1] = child_size - - # if there is rest size, set it to the last child element - if rest_size > 0: - children[-1][1] = rest_size - - return children - - -def parse_sections(file_name: str) -> list: - """ - Quick&Dirty parsing for GNU ld’s linker map output, needs LANG=C, because - some messages are localized. - """ - - sections = [] - with open(file_name, "r") as file: - # skip until memory map is found - found = False - - while True: - line = file.readline() - if not line: - break - if line.strip() == "Memory Configuration": - found = True - break - - if not found: - raise Exception(f"Memory configuration is not found in the{input_file}") - - # long section names result in a linebreak afterwards - sectionre = re.compile( - "(?P
.+?|.{14,}\n)[ ]+0x(?P[0-9a-f]+)[ ]+0x(?P[0-9a-f]+)(?:[ ]+(?P.+))?\n+", - re.I, - ) - subsectionre = re.compile( - "[ ]{16}0x(?P[0-9a-f]+)[ ]+(?P.+)\n+", re.I - ) - s = file.read() - pos = 0 - - while True: - m = sectionre.match(s, pos) - if not m: - # skip that line - try: - nextpos = s.index("\n", pos) + 1 - pos = nextpos - continue - except ValueError: - break - - pos = m.end() - section = m.group("section") - v = m.group("offset") - offset = int(v, 16) if v is not None else None - v = m.group("size") - size = int(v, 16) if v is not None else None - comment = m.group("comment") - - if section != "*default*" and size > 0: - of = Objectfile(section, offset, size, comment) - - if section.startswith(" "): - children = [] - sections[-1].children.append(of) - - while True: - m = subsectionre.match(s, pos) - if not m: - break - pos = m.end() - offset, function = m.groups() - offset = int(offset, 16) - if sections and sections[-1].children: - children.append([offset, 0, function]) - - if children: - children = update_children_size( - children=children, subsection_size=of.size - ) - - sections[-1].children[-1].children.extend(children) - - else: - sections.append(of) - - return sections - - -def get_subsection_name(section_name: str, subsection: Objectfile) -> str: - subsection_split_names = subsection.section.split(".") - if subsection.section.startswith("."): - subsection_split_names = subsection_split_names[1:] - - return ( - f".{subsection_split_names[1]}" - if len(subsection_split_names) > 2 - else section_name - ) - - -def write_subsection( - section_name: str, - subsection_name: str, - address: str, - size: int, - demangled_name: str, - module_name: str, - file_name: str, - mangled_name: str, - write_file_object: TextIO, -) -> None: - write_file_object.write( - f"{section_name}\t" - f"{subsection_name}\t" - f"{address}\t" - f"{size}\t" - f"{demangled_name}\t" - f"{module_name}\t" - f"{file_name}\t" - f"{mangled_name}\n" - ) - - -def save_subsection( - section_name: str, subsection: Objectfile, write_file_object: TextIO -) -> None: - subsection_name = get_subsection_name(section_name, subsection) - module_name = subsection.path[0] - file_name = subsection.path[1] - - if not file_name: - file_name, module_name = module_name, "" - - if not subsection.children: - address = f"{subsection.offset:x}" - size = subsection.size - mangled_name = ( - "" - if subsection.section == section_name - else subsection.section.split(".")[-1] - ) - demangled_name = demangle(mangled_name) if mangled_name else mangled_name - - write_subsection( - section_name=section_name, - subsection_name=subsection_name, - address=address, - size=size, - demangled_name=demangled_name, - module_name=module_name, - file_name=file_name, - mangled_name=mangled_name, - write_file_object=write_file_object, - ) - return - - for subsection_child in subsection.children: - address = f"{subsection_child[0]:x}" - size = subsection_child[1] - mangled_name = subsection_child[2] - demangled_name = demangle(mangled_name) - - write_subsection( - section_name=section_name, - subsection_name=subsection_name, - address=address, - size=size, - demangled_name=demangled_name, - module_name=module_name, - file_name=file_name, - mangled_name=mangled_name, - write_file_object=write_file_object, - ) - - -def save_section(section: Objectfile, write_file_object: TextIO) -> None: - section_name = section.section - for subsection in section.children: - save_subsection( - section_name=section_name, - subsection=subsection, - write_file_object=write_file_object, - ) - - -def save_parsed_data(parsed_data: list[Objectfile], output_file_name: str) -> None: - with open(output_file_name, "w") as write_file_object: - for section in parsed_data: - if section.children: - save_section(section=section, write_file_object=write_file_object) - - -if __name__ == "__main__": - if len(sys.argv) < 3: - raise Exception(f"Usage: {sys.argv[0]} ") - - input_file = sys.argv[1] - output_file = sys.argv[2] - - parsed_sections = parse_sections(input_file) - - if parsed_sections is None: - raise Exception(f"Memory configuration is not {input_file}") - - save_parsed_data(parsed_sections, output_file) From 1d17206e2358583a7feac2e142218f5b4ca38148 Mon Sep 17 00:00:00 2001 From: Sergei Gavrilov Date: Thu, 16 May 2024 01:47:21 +1000 Subject: [PATCH 03/11] TLSF memory allocator. Less free flash, moar free ram. (#3572) * add tlsf as submodule * libs: tlsf * Furi: tlsf as allocator * Furi: heap walker * shmal fixshesh * f18: tlsf * PVS: ignore tlsf * I like to moving * merge upcoming changes * memmgr: alloc aligned, realloc * Furi: distinct name for auxiliary memory pool * Furi: put idle and timer thread to mem2 * Furi: fix smal things in allocator * Furi: remove aligned_free. Use free instead. * aligned_malloc -> aligned_alloc * aligned_alloc, parameters order * aligned_alloc: check that alignment is correct * unit test: malloc * unit tests: realloc and test with memory fragmentation * unit tests: aligned_alloc * update api * updater: properly read large update file Co-authored-by: Aleksandr Kutuzov --- .gitmodules | 3 + .pvsoptions | 2 +- .../debug/unit_tests/furi/furi_memmgr_test.c | 262 +++++- .../debug/unit_tests/furi/furi_test.c | 2 + applications/services/cli/cli_commands.c | 48 +- furi/core/memmgr.c | 39 +- furi/core/memmgr.h | 25 +- furi/core/memmgr_heap.c | 786 ++++++------------ furi/core/memmgr_heap.h | 12 +- furi/core/thread.c | 4 +- furi/flipper.c | 13 +- lib/SConscript | 1 + lib/flipper_application/elf/elf_file.c | 8 +- lib/tlsf | 1 + lib/tlsf.scons | 21 + targets/f18/api_symbols.csv | 12 +- targets/f18/target.json | 3 +- targets/f7/api_symbols.csv | 12 +- targets/f7/fatfs/sector_cache.c | 2 +- targets/f7/src/update.c | 14 +- targets/f7/target.json | 3 +- 21 files changed, 628 insertions(+), 645 deletions(-) create mode 160000 lib/tlsf create mode 100644 lib/tlsf.scons diff --git a/.gitmodules b/.gitmodules index c4c68a6a7..a8d12803c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -41,3 +41,6 @@ [submodule "documentation/doxygen/doxygen-awesome-css"] path = documentation/doxygen/doxygen-awesome-css url = https://github.com/jothepro/doxygen-awesome-css.git +[submodule "lib/tlsf"] + path = lib/tlsf + url = https://github.com/espressif/tlsf diff --git a/.pvsoptions b/.pvsoptions index 8606eef15..590a34de8 100644 --- a/.pvsoptions +++ b/.pvsoptions @@ -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/tlsf -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/* diff --git a/applications/debug/unit_tests/furi/furi_memmgr_test.c b/applications/debug/unit_tests/furi/furi_memmgr_test.c index 01e2c17f6..399e2d418 100644 --- a/applications/debug/unit_tests/furi/furi_memmgr_test.c +++ b/applications/debug/unit_tests/furi/furi_memmgr_test.c @@ -1,8 +1,5 @@ #include "../minunit.h" -#include -#include -#include -#include +#include void test_furi_memmgr(void) { void* ptr; @@ -37,3 +34,260 @@ void test_furi_memmgr(void) { } free(ptr); } + +static void test_memmgr_malloc(const size_t allocation_size) { + uint8_t* ptr = NULL; + const char* error_message = NULL; + + FURI_CRITICAL_ENTER(); + + ptr = malloc(allocation_size); + + // test that we can allocate memory + if(ptr == NULL) { + error_message = "malloc failed"; + } + + // test that memory is zero-initialized after allocation + for(size_t i = 0; i < allocation_size; i++) { + if(ptr[i] != 0) { + error_message = "memory is not zero-initialized after malloc"; + break; + } + } + memset(ptr, 0x55, allocation_size); + free(ptr); + + // test that memory is zero-initialized after free + // we know that allocator can use this memory for inner purposes + // so we check that memory at least partially zero-initialized + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wuse-after-free" + + size_t zero_count = 0; + for(size_t i = 0; i < allocation_size; i++) { + if(ptr[i] == 0) { + zero_count++; + } + } + +#pragma GCC diagnostic pop + + // check that at least 75% of memory is zero-initialized + if(zero_count < (allocation_size * 0.75)) { + error_message = "seems that memory is not zero-initialized after free (malloc)"; + } + + FURI_CRITICAL_EXIT(); + + if(error_message != NULL) { + mu_fail(error_message); + } +} + +static void test_memmgr_realloc(const size_t allocation_size) { + uint8_t* ptr = NULL; + const char* error_message = NULL; + + FURI_CRITICAL_ENTER(); + + ptr = realloc(ptr, allocation_size); + + // test that we can allocate memory + if(ptr == NULL) { + error_message = "realloc(NULL) failed"; + } + + // test that memory is zero-initialized after allocation + for(size_t i = 0; i < allocation_size; i++) { + if(ptr[i] != 0) { + error_message = "memory is not zero-initialized after realloc(NULL)"; + break; + } + } + + memset(ptr, 0x55, allocation_size); + + ptr = realloc(ptr, allocation_size * 2); + + // test that we can reallocate memory + if(ptr == NULL) { + error_message = "realloc failed"; + } + + // test that memory content is preserved + for(size_t i = 0; i < allocation_size; i++) { + if(ptr[i] != 0x55) { + error_message = "memory is not reallocated after realloc"; + break; + } + } + + // test that remaining memory is zero-initialized + size_t non_zero_count = 0; + for(size_t i = allocation_size; i < allocation_size * 2; i++) { + if(ptr[i] != 0) { + non_zero_count += 1; + } + } + + // check that at most of memory is zero-initialized + // we know that allocator not always can restore content size from a pointer + // so we check against small threshold + if(non_zero_count > 4) { + error_message = "seems that memory is not zero-initialized after realloc"; + } + + uint8_t* null_ptr = realloc(ptr, 0); + + // test that we can free memory + if(null_ptr != NULL) { + error_message = "realloc(0) failed"; + } + + // test that memory is zero-initialized after realloc(0) + // we know that allocator can use this memory for inner purposes + // so we check that memory at least partially zero-initialized + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wuse-after-free" + + size_t zero_count = 0; + for(size_t i = 0; i < allocation_size; i++) { + if(ptr[i] == 0) { + zero_count++; + } + } + +#pragma GCC diagnostic pop + + // check that at least 75% of memory is zero-initialized + if(zero_count < (allocation_size * 0.75)) { + error_message = "seems that memory is not zero-initialized after realloc(0)"; + } + + FURI_CRITICAL_EXIT(); + + if(error_message != NULL) { + mu_fail(error_message); + } +} + +static void test_memmgr_alloc_aligned(const size_t allocation_size, const size_t alignment) { + uint8_t* ptr = NULL; + const char* error_message = NULL; + + FURI_CRITICAL_ENTER(); + + ptr = aligned_alloc(alignment, allocation_size); + + // test that we can allocate memory + if(ptr == NULL) { + error_message = "aligned_alloc failed"; + } + + // test that memory is aligned + if(((uintptr_t)ptr % alignment) != 0) { + error_message = "memory is not aligned after aligned_alloc"; + } + + // test that memory is zero-initialized after allocation + for(size_t i = 0; i < allocation_size; i++) { + if(ptr[i] != 0) { + error_message = "memory is not zero-initialized after aligned_alloc"; + break; + } + } + memset(ptr, 0x55, allocation_size); + free(ptr); + + // test that memory is zero-initialized after free + // we know that allocator can use this memory for inner purposes + // so we check that memory at least partially zero-initialized + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wuse-after-free" + + size_t zero_count = 0; + for(size_t i = 0; i < allocation_size; i++) { + if(ptr[i] == 0) { + zero_count++; + } + } + +#pragma GCC diagnostic pop + + // check that at least 75% of memory is zero-initialized + if(zero_count < (allocation_size * 0.75)) { + error_message = "seems that memory is not zero-initialized after free (aligned_alloc)"; + } + + FURI_CRITICAL_EXIT(); + + if(error_message != NULL) { + mu_fail(error_message); + } +} + +void test_furi_memmgr_advanced(void) { + const size_t sizes[] = {50, 100, 500, 1000, 5000, 10000}; + const size_t sizes_count = sizeof(sizes) / sizeof(sizes[0]); + const size_t alignments[] = {4, 8, 16, 32, 64, 128, 256, 512, 1024}; + const size_t alignments_count = sizeof(alignments) / sizeof(alignments[0]); + + // do test without memory fragmentation + { + for(size_t i = 0; i < sizes_count; i++) { + test_memmgr_malloc(sizes[i]); + } + + for(size_t i = 0; i < sizes_count; i++) { + test_memmgr_realloc(sizes[i]); + } + + for(size_t i = 0; i < sizes_count; i++) { + for(size_t j = 0; j < alignments_count; j++) { + test_memmgr_alloc_aligned(sizes[i], alignments[j]); + } + } + } + + // do test with memory fragmentation + { + void* blocks[sizes_count]; + void* guards[sizes_count - 1]; + + // setup guards + for(size_t i = 0; i < sizes_count; i++) { + blocks[i] = malloc(sizes[i]); + if(i < sizes_count - 1) { + guards[i] = malloc(sizes[i]); + } + } + + for(size_t i = 0; i < sizes_count; i++) { + free(blocks[i]); + } + + // do test + for(size_t i = 0; i < sizes_count; i++) { + test_memmgr_malloc(sizes[i]); + } + + for(size_t i = 0; i < sizes_count; i++) { + test_memmgr_realloc(sizes[i]); + } + + for(size_t i = 0; i < sizes_count; i++) { + for(size_t j = 0; j < alignments_count; j++) { + test_memmgr_alloc_aligned(sizes[i], alignments[j]); + } + } + + // cleanup guards + for(size_t i = 0; i < sizes_count - 1; i++) { + free(guards[i]); + } + } +} \ No newline at end of file diff --git a/applications/debug/unit_tests/furi/furi_test.c b/applications/debug/unit_tests/furi/furi_test.c index e287f9927..e0b5916d5 100644 --- a/applications/debug/unit_tests/furi/furi_test.c +++ b/applications/debug/unit_tests/furi/furi_test.c @@ -9,6 +9,7 @@ void test_furi_concurrent_access(void); void test_furi_pubsub(void); void test_furi_memmgr(void); +void test_furi_memmgr_advanced(void); static int foo = 0; @@ -37,6 +38,7 @@ MU_TEST(mu_test_furi_memmgr) { // this test is not accurate, but gives a basic understanding // that memory management is working fine test_furi_memmgr(); + test_furi_memmgr_advanced(); } MU_TEST_SUITE(test_suite) { diff --git a/applications/services/cli/cli_commands.c b/applications/services/cli/cli_commands.c index 43f1c01c4..56d05785f 100644 --- a/applications/services/cli/cli_commands.c +++ b/applications/services/cli/cli_commands.c @@ -425,8 +425,34 @@ void cli_command_free(Cli* cli, FuriString* args, void* context) { printf("Minimum heap size: %zu\r\n", memmgr_get_minimum_free_heap()); printf("Maximum heap block: %zu\r\n", memmgr_heap_get_max_free_block()); - printf("Pool free: %zu\r\n", memmgr_pool_get_free()); - printf("Maximum pool block: %zu\r\n", memmgr_pool_get_max_block()); + printf("Aux pool total free: %zu\r\n", memmgr_aux_pool_get_free()); + printf("Aux pool max free block: %zu\r\n", memmgr_pool_get_max_block()); +} + +typedef struct { + void* addr; + size_t size; +} FreeBlockInfo; + +#define FREE_BLOCK_INFO_MAX 128 + +typedef struct { + FreeBlockInfo free_blocks[FREE_BLOCK_INFO_MAX]; + size_t free_blocks_count; +} FreeBlockContext; + +static bool free_block_walker(void* pointer, size_t size, bool used, void* context) { + FreeBlockContext* free_blocks = (FreeBlockContext*)context; + if(!used) { + if(free_blocks->free_blocks_count < FREE_BLOCK_INFO_MAX) { + free_blocks->free_blocks[free_blocks->free_blocks_count].addr = pointer; + free_blocks->free_blocks[free_blocks->free_blocks_count].size = size; + free_blocks->free_blocks_count++; + } else { + return false; + } + } + return true; } void cli_command_free_blocks(Cli* cli, FuriString* args, void* context) { @@ -434,7 +460,23 @@ void cli_command_free_blocks(Cli* cli, FuriString* args, void* context) { UNUSED(args); UNUSED(context); - memmgr_heap_printf_free_blocks(); + FreeBlockContext* free_blocks = malloc(sizeof(FreeBlockContext)); + free_blocks->free_blocks_count = 0; + + memmgr_heap_walk_blocks(free_block_walker, free_blocks); + + for(size_t i = 0; i < free_blocks->free_blocks_count; i++) { + printf( + "A %p S %zu\r\n", + (void*)free_blocks->free_blocks[i].addr, + free_blocks->free_blocks[i].size); + } + + if(free_blocks->free_blocks_count == FREE_BLOCK_INFO_MAX) { + printf("... and more\r\n"); + } + + free(free_blocks); } void cli_command_i2c(Cli* cli, FuriString* args, void* context) { diff --git a/furi/core/memmgr.c b/furi/core/memmgr.c index 768adc05d..768d44890 100644 --- a/furi/core/memmgr.c +++ b/furi/core/memmgr.c @@ -4,6 +4,8 @@ #include extern void* pvPortMalloc(size_t xSize); +extern void* pvPortAllocAligned(size_t xSize, size_t xAlignment); +extern void* pvPortRealloc(void* pv, size_t xSize); extern void vPortFree(void* pv); extern size_t xPortGetFreeHeapSize(void); extern size_t xPortGetTotalHeapSize(void); @@ -18,18 +20,7 @@ void free(void* ptr) { } void* realloc(void* ptr, size_t size) { - if(size == 0) { - vPortFree(ptr); - return NULL; - } - - void* p = pvPortMalloc(size); - if(ptr != NULL) { - memcpy(p, ptr, size); - vPortFree(ptr); - } - - return p; + return pvPortRealloc(ptr, size); } void* calloc(size_t count, size_t size) { @@ -47,6 +38,10 @@ char* strdup(const char* s) { return y; } +void* aligned_alloc(size_t alignment, size_t size) { + return pvPortAllocAligned(size, alignment); +} + size_t memmgr_get_free_heap(void) { return xPortGetFreeHeapSize(); } @@ -79,33 +74,17 @@ void* __wrap__realloc_r(struct _reent* r, void* ptr, size_t size) { return realloc(ptr, size); } -void* memmgr_alloc_from_pool(size_t size) { +void* memmgr_aux_pool_alloc(size_t size) { void* p = furi_hal_memory_alloc(size); if(p == NULL) p = malloc(size); return p; } -size_t memmgr_pool_get_free(void) { +size_t memmgr_aux_pool_get_free(void) { return furi_hal_memory_get_free(); } size_t memmgr_pool_get_max_block(void) { return furi_hal_memory_max_pool_block(); -} - -void* aligned_malloc(size_t size, size_t alignment) { - void* p1; // original block - void** p2; // aligned block - int offset = alignment - 1 + sizeof(void*); - if((p1 = (void*)malloc(size + offset)) == NULL) { - return NULL; - } - p2 = (void**)(((size_t)(p1) + offset) & ~(alignment - 1)); - p2[-1] = p1; - return p2; -} - -void aligned_free(void* p) { - free(((void**)p)[-1]); } \ No newline at end of file diff --git a/furi/core/memmgr.h b/furi/core/memmgr.h index bc0c35faa..796a1f537 100644 --- a/furi/core/memmgr.h +++ b/furi/core/memmgr.h @@ -36,37 +36,22 @@ size_t memmgr_get_total_heap(void); size_t memmgr_get_minimum_free_heap(void); /** - * An aligned version of malloc, used when you need to get the aligned space on the heap - * Freeing the received address is performed ONLY through the aligned_free function - * @param size - * @param alignment - * @return void* - */ -void* aligned_malloc(size_t size, size_t alignment); - -/** - * Freed space obtained through the aligned_malloc function - * @param p pointer to result of aligned_malloc - */ -void aligned_free(void* p); - -/** - * @brief Allocate memory from separate memory pool. That memory can't be freed. + * @brief Allocate memory from the auxiliary memory pool. That memory can't be freed. * * @param size * @return void* */ -void* memmgr_alloc_from_pool(size_t size); +void* memmgr_aux_pool_alloc(size_t size); /** - * @brief Get free memory pool size + * @brief Get the auxiliary pool free memory size * * @return size_t */ -size_t memmgr_pool_get_free(void); +size_t memmgr_aux_pool_get_free(void); /** - * @brief Get max free block size from memory pool + * @brief Get max free block size from the auxiliary memory pool * * @return size_t */ diff --git a/furi/core/memmgr_heap.c b/furi/core/memmgr_heap.c index 3f62b518c..3dfc7f5ac 100644 --- a/furi/core/memmgr_heap.c +++ b/furi/core/memmgr_heap.c @@ -1,124 +1,18 @@ -/* - * FreeRTOS Kernel V10.2.1 - * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * http://www.FreeRTOS.org - * http://aws.amazon.com/freertos - * - * 1 tab == 4 spaces! - */ - -/* - * A sample implementation of pvPortMalloc() and vPortFree() that combines - * (coalescences) adjacent memory blocks as they are freed, and in so doing - * limits memory fragmentation. - * - * See heap_1.c, heap_2.c and heap_3.c for alternative implementations, and the - * memory management pages of http://www.FreeRTOS.org for more information. - */ - -#include "memmgr_heap.h" -#include "check.h" -#include -#include -#include -#include -#include - -/* Defining MPU_WRAPPERS_INCLUDED_FROM_API_FILE prevents task.h from redefining -all the API functions to use the MPU wrappers. That should only be done when -task.h is included from an application file. */ -#define MPU_WRAPPERS_INCLUDED_FROM_API_FILE - +#include +#include +#include #include #include - -#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE - -#ifdef HEAP_PRINT_DEBUG -#error This feature is broken, logging transport must be replaced with RTT -#endif - -#if(configSUPPORT_DYNAMIC_ALLOCATION == 0) -#error This file must not be used if configSUPPORT_DYNAMIC_ALLOCATION is 0 -#endif - -/* Block sizes must not get too small. */ -#define heapMINIMUM_BLOCK_SIZE ((size_t)(xHeapStructSize << 1)) - -/* Assumes 8bit bytes! */ -#define heapBITS_PER_BYTE ((size_t)8) - -/* Heap start end symbols provided by linker */ -extern const void __heap_start__; -extern const void __heap_end__; -uint8_t* ucHeap = (uint8_t*)&__heap_start__; - -/* Define the linked list structure. This is used to link free blocks in order -of their memory address. */ -typedef struct A_BLOCK_LINK { - struct A_BLOCK_LINK* pxNextFreeBlock; /*<< The next free block in the list. */ - size_t xBlockSize; /*<< The size of the free block. */ -} BlockLink_t; - -/*-----------------------------------------------------------*/ - -/* - * Inserts a block of memory that is being freed into the correct position in - * the list of free memory blocks. The block being freed will be merged with - * the block in front it and/or the block behind it if the memory blocks are - * adjacent to each other. - */ -static void prvInsertBlockIntoFreeList(BlockLink_t* pxBlockToInsert); - -/* - * Called automatically to setup the required heap structures the first time - * pvPortMalloc() is called. - */ -static void prvHeapInit(void); - -/*-----------------------------------------------------------*/ - -/* The size of the structure placed at the beginning of each allocated memory -block must by correctly byte aligned. */ -static const size_t xHeapStructSize = (sizeof(BlockLink_t) + ((size_t)(portBYTE_ALIGNMENT - 1))) & - ~((size_t)portBYTE_ALIGNMENT_MASK); - -/* Create a couple of list links to mark the start and end of the list. */ -static BlockLink_t xStart, *pxEnd = NULL; - -/* Keeps track of the number of free bytes remaining, but says nothing about -fragmentation. */ -static size_t xFreeBytesRemaining = 0U; -static size_t xMinimumEverFreeBytesRemaining = 0U; - -/* Gets set to the top bit of an size_t type. When this bit in the xBlockSize -member of an BlockLink_t structure is set then the block belongs to the -application. When the bit is free the block is still part of the free heap -space. */ -static size_t xBlockAllocatedBit = 0; - -/* Furi heap extension */ #include -/* Allocation tracking types */ +extern const void __heap_start__; +extern const void __heap_end__; + +static tlsf_t tlsf = NULL; +static size_t heap_used = 0; +static size_t heap_max_used = 0; + +// Allocation tracking types DICT_DEF2(MemmgrHeapAllocDict, uint32_t, uint32_t) //-V1048 DICT_DEF2( //-V1048 @@ -128,17 +22,35 @@ DICT_DEF2( //-V1048 MemmgrHeapAllocDict_t, DICT_OPLIST(MemmgrHeapAllocDict)) -/* Thread allocation tracing storage */ +// Thread allocation tracing storage static MemmgrHeapThreadDict_t memmgr_heap_thread_dict = {0}; static volatile uint32_t memmgr_heap_thread_trace_depth = 0; -/* Initialize tracing storage on start */ -void memmgr_heap_init(void) { +static inline void memmgr_lock(void) { + vTaskSuspendAll(); +} + +static inline void memmgr_unlock(void) { + xTaskResumeAll(); +} + +static inline size_t memmgr_get_heap_size(void) { + return (size_t)&__heap_end__ - (size_t)&__heap_start__; +} + +// Initialize tracing storage +static void memmgr_heap_init(void) { MemmgrHeapThreadDict_init(memmgr_heap_thread_dict); } +__attribute__((constructor)) static void memmgr_init(void) { + size_t pool_size = (size_t)&__heap_end__ - (size_t)&__heap_start__; + tlsf = tlsf_create_with_pool((void*)&__heap_start__, pool_size, pool_size); + memmgr_heap_init(); +} + void memmgr_heap_enable_thread_trace(FuriThreadId thread_id) { - vTaskSuspendAll(); + memmgr_lock(); { memmgr_heap_thread_trace_depth++; furi_check(MemmgrHeapThreadDict_get(memmgr_heap_thread_dict, (uint32_t)thread_id) == NULL); @@ -148,53 +60,20 @@ void memmgr_heap_enable_thread_trace(FuriThreadId thread_id) { MemmgrHeapAllocDict_clear(alloc_dict); memmgr_heap_thread_trace_depth--; } - (void)xTaskResumeAll(); + memmgr_unlock(); } void memmgr_heap_disable_thread_trace(FuriThreadId thread_id) { - vTaskSuspendAll(); + memmgr_lock(); { memmgr_heap_thread_trace_depth++; furi_check(MemmgrHeapThreadDict_erase(memmgr_heap_thread_dict, (uint32_t)thread_id)); memmgr_heap_thread_trace_depth--; } - (void)xTaskResumeAll(); + memmgr_unlock(); } -size_t memmgr_heap_get_thread_memory(FuriThreadId thread_id) { - size_t leftovers = MEMMGR_HEAP_UNKNOWN; - vTaskSuspendAll(); - { - memmgr_heap_thread_trace_depth++; - MemmgrHeapAllocDict_t* alloc_dict = - MemmgrHeapThreadDict_get(memmgr_heap_thread_dict, (uint32_t)thread_id); - if(alloc_dict) { - leftovers = 0; - MemmgrHeapAllocDict_it_t alloc_dict_it; - for(MemmgrHeapAllocDict_it(alloc_dict_it, *alloc_dict); - !MemmgrHeapAllocDict_end_p(alloc_dict_it); - MemmgrHeapAllocDict_next(alloc_dict_it)) { - MemmgrHeapAllocDict_itref_t* data = MemmgrHeapAllocDict_ref(alloc_dict_it); - if(data->key != 0) { - uint8_t* puc = (uint8_t*)data->key; - puc -= xHeapStructSize; - BlockLink_t* pxLink = (void*)puc; - - if((pxLink->xBlockSize & xBlockAllocatedBit) != 0 && - pxLink->pxNextFreeBlock == NULL) { - leftovers += data->value; - } - } - } - } - memmgr_heap_thread_trace_depth--; - } - (void)xTaskResumeAll(); - return leftovers; -} - -#undef traceMALLOC -static inline void traceMALLOC(void* pointer, size_t size) { +static inline void memmgr_heap_trace_malloc(void* pointer, size_t size) { FuriThreadId thread_id = furi_thread_get_current_id(); if(thread_id && memmgr_heap_thread_trace_depth == 0) { memmgr_heap_thread_trace_depth++; @@ -207,9 +86,7 @@ static inline void traceMALLOC(void* pointer, size_t size) { } } -#undef traceFREE -static inline void traceFREE(void* pointer, size_t size) { - UNUSED(size); +static inline void memmgr_heap_trace_free(void* pointer) { FuriThreadId thread_id = furi_thread_get_current_id(); if(thread_id && memmgr_heap_thread_trace_depth == 0) { memmgr_heap_thread_trace_depth++; @@ -224,441 +101,248 @@ static inline void traceFREE(void* pointer, size_t size) { } } -size_t memmgr_heap_get_max_free_block(void) { - size_t max_free_size = 0; - BlockLink_t* pxBlock; - vTaskSuspendAll(); - - pxBlock = xStart.pxNextFreeBlock; - while(pxBlock->pxNextFreeBlock != NULL) { - if(pxBlock->xBlockSize > max_free_size) { - max_free_size = pxBlock->xBlockSize; +size_t memmgr_heap_get_thread_memory(FuriThreadId thread_id) { + size_t leftovers = MEMMGR_HEAP_UNKNOWN; + memmgr_lock(); + { + memmgr_heap_thread_trace_depth++; + MemmgrHeapAllocDict_t* alloc_dict = + MemmgrHeapThreadDict_get(memmgr_heap_thread_dict, (uint32_t)thread_id); + if(alloc_dict) { + leftovers = 0; + MemmgrHeapAllocDict_it_t alloc_dict_it; + for(MemmgrHeapAllocDict_it(alloc_dict_it, *alloc_dict); + !MemmgrHeapAllocDict_end_p(alloc_dict_it); + MemmgrHeapAllocDict_next(alloc_dict_it)) { + MemmgrHeapAllocDict_itref_t* data = MemmgrHeapAllocDict_ref(alloc_dict_it); + if(data->key != 0) { + block_header_t* block = block_from_ptr((uint8_t*)data->key); + if(!block_is_free(block)) { + leftovers += data->value; + } + } + } } - pxBlock = pxBlock->pxNextFreeBlock; + memmgr_heap_thread_trace_depth--; + } + memmgr_unlock(); + return leftovers; +} + +static bool tlsf_walker_max_free(void* ptr, size_t size, int used, void* user) { + UNUSED(ptr); + + bool free = !used; + size_t* max_free_block_size = (size_t*)user; + if(free && size > *max_free_block_size) { + *max_free_block_size = size; } - xTaskResumeAll(); - return max_free_size; + return true; } -void memmgr_heap_printf_free_blocks(void) { - BlockLink_t* pxBlock; - //TODO enable when we can do printf with a locked scheduler - //vTaskSuspendAll(); +size_t memmgr_heap_get_max_free_block(void) { + size_t max_free_block_size = 0; - pxBlock = xStart.pxNextFreeBlock; - while(pxBlock->pxNextFreeBlock != NULL) { - printf("A %p S %lu\r\n", (void*)pxBlock, (uint32_t)pxBlock->xBlockSize); - pxBlock = pxBlock->pxNextFreeBlock; - } + memmgr_lock(); - //xTaskResumeAll(); + pool_t pool = tlsf_get_pool(tlsf); + tlsf_walk_pool(pool, tlsf_walker_max_free, &max_free_block_size); + + memmgr_unlock(); + + return max_free_block_size; } -#ifdef HEAP_PRINT_DEBUG -char* ultoa(unsigned long num, char* str, int radix) { - char temp[33]; // at radix 2 the string is at most 32 + 1 null long. - int temp_loc = 0; - int digit; - int str_loc = 0; +typedef struct { + BlockWalker walker; + void* context; +} BlockWalkerWrapper; - //construct a backward string of the number. - do { - digit = (unsigned long)num % ((unsigned long)radix); - if(digit < 10) - temp[temp_loc++] = digit + '0'; - else - temp[temp_loc++] = digit - 10 + 'A'; - num = ((unsigned long)num) / ((unsigned long)radix); - } while((unsigned long)num > 0); - - temp_loc--; - - //now reverse the string. - while(temp_loc >= 0) { // while there are still chars - str[str_loc++] = temp[temp_loc--]; - } - str[str_loc] = 0; // add null termination. - - return str; +static bool tlsf_walker_wrapper(void* ptr, size_t size, int used, void* user) { + BlockWalkerWrapper* wrapper = (BlockWalkerWrapper*)user; + return wrapper->walker(ptr, size, used, wrapper->context); } -static void print_heap_init(void) { - char tmp_str[33]; - size_t heap_start = (size_t)&__heap_start__; - size_t heap_end = (size_t)&__heap_end__; +void memmgr_heap_walk_blocks(BlockWalker walker, void* context) { + memmgr_lock(); - // {PHStart|heap_start|heap_end} - FURI_CRITICAL_ENTER(); - furi_log_puts("{PHStart|"); - ultoa(heap_start, tmp_str, 16); - furi_log_puts(tmp_str); - furi_log_puts("|"); - ultoa(heap_end, tmp_str, 16); - furi_log_puts(tmp_str); - furi_log_puts("}\r\n"); - FURI_CRITICAL_EXIT(); + BlockWalkerWrapper wrapper = {walker, context}; + pool_t pool = tlsf_get_pool(tlsf); + tlsf_walk_pool(pool, tlsf_walker_wrapper, &wrapper); + + memmgr_unlock(); } -static void print_heap_malloc(void* ptr, size_t size) { - char tmp_str[33]; - const char* name = furi_thread_get_name(furi_thread_get_current_id()); - if(!name) { - name = ""; - } - - // {thread name|m|address|size} - FURI_CRITICAL_ENTER(); - furi_log_puts("{"); - furi_log_puts(name); - furi_log_puts("|m|0x"); - ultoa((unsigned long)ptr, tmp_str, 16); - furi_log_puts(tmp_str); - furi_log_puts("|"); - utoa(size, tmp_str, 10); - furi_log_puts(tmp_str); - furi_log_puts("}\r\n"); - FURI_CRITICAL_EXIT(); -} - -static void print_heap_free(void* ptr) { - char tmp_str[33]; - const char* name = furi_thread_get_name(furi_thread_get_current_id()); - if(!name) { - name = ""; - } - - // {thread name|f|address} - FURI_CRITICAL_ENTER(); - furi_log_puts("{"); - furi_log_puts(name); - furi_log_puts("|f|0x"); - ultoa((unsigned long)ptr, tmp_str, 16); - furi_log_puts(tmp_str); - furi_log_puts("}\r\n"); - FURI_CRITICAL_EXIT(); -} -#endif -/*-----------------------------------------------------------*/ - -void* pvPortMalloc(size_t xWantedSize) { - BlockLink_t *pxBlock, *pxPreviousBlock, *pxNewBlockLink; - void* pvReturn = NULL; - size_t to_wipe = xWantedSize; - +void* pvPortMalloc(size_t xSize) { + // memory management in ISR is not allowed if(FURI_IS_IRQ_MODE()) { furi_crash("memmgt in ISR"); } -#ifdef HEAP_PRINT_DEBUG - BlockLink_t* print_heap_block = NULL; -#endif + memmgr_lock(); - /* If this is the first call to malloc then the heap will require - initialisation to setup the list of free blocks. */ - if(pxEnd == NULL) { -#ifdef HEAP_PRINT_DEBUG - print_heap_init(); -#endif - - vTaskSuspendAll(); - { - prvHeapInit(); - memmgr_heap_init(); - } - (void)xTaskResumeAll(); - } else { - mtCOVERAGE_TEST_MARKER(); - } - - vTaskSuspendAll(); - { - /* Check the requested block size is not so large that the top bit is - set. The top bit of the block size member of the BlockLink_t structure - is used to determine who owns the block - the application or the - kernel, so it must be free. */ - if((xWantedSize & xBlockAllocatedBit) == 0) { - /* The wanted size is increased so it can contain a BlockLink_t - structure in addition to the requested amount of bytes. */ - if(xWantedSize > 0) { - xWantedSize += xHeapStructSize; - - /* Ensure that blocks are always aligned to the required number - of bytes. */ - if((xWantedSize & portBYTE_ALIGNMENT_MASK) != 0x00) { - /* Byte alignment required. */ - xWantedSize += (portBYTE_ALIGNMENT - (xWantedSize & portBYTE_ALIGNMENT_MASK)); - configASSERT((xWantedSize & portBYTE_ALIGNMENT_MASK) == 0); - } else { - mtCOVERAGE_TEST_MARKER(); - } - } else { - mtCOVERAGE_TEST_MARKER(); - } - - if((xWantedSize > 0) && (xWantedSize <= xFreeBytesRemaining)) { - /* Traverse the list from the start (lowest address) block until - one of adequate size is found. */ - pxPreviousBlock = &xStart; - pxBlock = xStart.pxNextFreeBlock; - while((pxBlock->xBlockSize < xWantedSize) && (pxBlock->pxNextFreeBlock != NULL)) { - pxPreviousBlock = pxBlock; - pxBlock = pxBlock->pxNextFreeBlock; - } - - /* If the end marker was reached then a block of adequate size - was not found. */ - if(pxBlock != pxEnd) { - /* Return the memory space pointed to - jumping over the - BlockLink_t structure at its start. */ - pvReturn = - (void*)(((uint8_t*)pxPreviousBlock->pxNextFreeBlock) + xHeapStructSize); - - /* This block is being returned for use so must be taken out - of the list of free blocks. */ - pxPreviousBlock->pxNextFreeBlock = pxBlock->pxNextFreeBlock; - - /* If the block is larger than required it can be split into - two. */ - if((pxBlock->xBlockSize - xWantedSize) > heapMINIMUM_BLOCK_SIZE) { - /* This block is to be split into two. Create a new - block following the number of bytes requested. The void - cast is used to prevent byte alignment warnings from the - compiler. */ - pxNewBlockLink = (void*)(((uint8_t*)pxBlock) + xWantedSize); - configASSERT((((size_t)pxNewBlockLink) & portBYTE_ALIGNMENT_MASK) == 0); - - /* Calculate the sizes of two blocks split from the - single block. */ - pxNewBlockLink->xBlockSize = pxBlock->xBlockSize - xWantedSize; - pxBlock->xBlockSize = xWantedSize; - - /* Insert the new block into the list of free blocks. */ - prvInsertBlockIntoFreeList(pxNewBlockLink); - } else { - mtCOVERAGE_TEST_MARKER(); - } - - xFreeBytesRemaining -= pxBlock->xBlockSize; - - if(xFreeBytesRemaining < xMinimumEverFreeBytesRemaining) { - xMinimumEverFreeBytesRemaining = xFreeBytesRemaining; - } else { - mtCOVERAGE_TEST_MARKER(); - } - - /* The block is being returned - it is allocated and owned - by the application and has no "next" block. */ - pxBlock->xBlockSize |= xBlockAllocatedBit; - pxBlock->pxNextFreeBlock = NULL; - -#ifdef HEAP_PRINT_DEBUG - print_heap_block = pxBlock; -#endif - } else { - mtCOVERAGE_TEST_MARKER(); - } - } else { - mtCOVERAGE_TEST_MARKER(); - } + // allocate block + void* data = tlsf_malloc(tlsf, xSize); + if(data == NULL) { + if(xSize == 0) { + furi_crash("malloc(0)"); } else { - mtCOVERAGE_TEST_MARKER(); - } - - traceMALLOC(pvReturn, xWantedSize); - } - (void)xTaskResumeAll(); - -#ifdef HEAP_PRINT_DEBUG - print_heap_malloc(print_heap_block, print_heap_block->xBlockSize & ~xBlockAllocatedBit); -#endif - -#if(configUSE_MALLOC_FAILED_HOOK == 1) - { - if(pvReturn == NULL) { - extern void vApplicationMallocFailedHook(void); - vApplicationMallocFailedHook(); - } else { - mtCOVERAGE_TEST_MARKER(); + furi_crash("out of memory"); } } -#endif - configASSERT((((size_t)pvReturn) & (size_t)portBYTE_ALIGNMENT_MASK) == 0); + // update heap usage + heap_used += tlsf_block_size(data); + heap_used += tlsf_alloc_overhead(); + if(heap_used > heap_max_used) { + heap_max_used = heap_used; + } - furi_check(pvReturn, xWantedSize ? "out of memory" : "malloc(0)"); - pvReturn = memset(pvReturn, 0, to_wipe); - return pvReturn; + // trace allocation + memmgr_heap_trace_malloc(data, xSize); + + memmgr_unlock(); + + // clear block content + memset(data, 0, xSize); + + return data; } -/*-----------------------------------------------------------*/ void vPortFree(void* pv) { - uint8_t* puc = (uint8_t*)pv; - BlockLink_t* pxLink; - + // memory management in ISR is not allowed if(FURI_IS_IRQ_MODE()) { furi_crash("memmgt in ISR"); } + // ignore NULL pointer if(pv != NULL) { - /* The memory being freed will have an BlockLink_t structure immediately - before it. */ - puc -= xHeapStructSize; + memmgr_lock(); - /* This casting is to keep the compiler from issuing warnings. */ - pxLink = (void*)puc; + // get block size + size_t block_size = tlsf_block_size(pv); - /* Check the block is actually allocated. */ - configASSERT((pxLink->xBlockSize & xBlockAllocatedBit) != 0); - configASSERT(pxLink->pxNextFreeBlock == NULL); + // clear block content + memset(pv, 0, block_size); - if((pxLink->xBlockSize & xBlockAllocatedBit) != 0) { - if(pxLink->pxNextFreeBlock == NULL) { - /* The block is being returned to the heap - it is no longer - allocated. */ - pxLink->xBlockSize &= ~xBlockAllocatedBit; + // update heap usage + heap_used -= block_size; + heap_used -= tlsf_alloc_overhead(); -#ifdef HEAP_PRINT_DEBUG - print_heap_free(pxLink); -#endif + // free + tlsf_free(tlsf, pv); - vTaskSuspendAll(); - { - furi_assert((size_t)pv >= SRAM_BASE); - furi_assert((size_t)pv < SRAM_BASE + 1024 * 256); - furi_assert(pxLink->xBlockSize >= xHeapStructSize); - furi_assert((pxLink->xBlockSize - xHeapStructSize) < 1024 * 256); + // trace free + memmgr_heap_trace_free(pv); - /* Add this block to the list of free blocks. */ - xFreeBytesRemaining += pxLink->xBlockSize; - traceFREE(pv, pxLink->xBlockSize); - memset(pv, 0, pxLink->xBlockSize - xHeapStructSize); - prvInsertBlockIntoFreeList(((BlockLink_t*)pxLink)); - } - (void)xTaskResumeAll(); - } else { - mtCOVERAGE_TEST_MARKER(); - } - } else { - mtCOVERAGE_TEST_MARKER(); - } - } else { -#ifdef HEAP_PRINT_DEBUG - print_heap_free(pv); -#endif + memmgr_unlock(); } } -/*-----------------------------------------------------------*/ -size_t xPortGetTotalHeapSize(void) { - return (size_t)&__heap_end__ - (size_t)&__heap_start__; +extern void* pvPortAllocAligned(size_t xSize, size_t xAlignment) { + // memory management in ISR is not allowed + if(FURI_IS_IRQ_MODE()) { + furi_crash("memmgt in ISR"); + } + + // alignment must be power of 2 + if((xAlignment & (xAlignment - 1)) != 0) { + furi_crash("invalid alignment"); + } + + memmgr_lock(); + + // allocate block + void* data = tlsf_memalign(tlsf, xAlignment, xSize); + if(data == NULL) { + if(xSize == 0) { + furi_crash("malloc_aligned(0)"); + } else { + furi_crash("out of memory"); + } + } + + // update heap usage + heap_used += tlsf_block_size(data); + heap_used += tlsf_alloc_overhead(); + if(heap_used > heap_max_used) { + heap_max_used = heap_used; + } + + // trace allocation + memmgr_heap_trace_malloc(data, xSize); + + memmgr_unlock(); + + // clear block content + memset(data, 0, xSize); + + return data; +} + +extern void* pvPortRealloc(void* pv, size_t xSize) { + // realloc(ptr, 0) is equivalent to free(ptr) + if(xSize == 0) { + vPortFree(pv); + return NULL; + } + + // realloc(NULL, size) is equivalent to malloc(size) + if(pv == NULL) { + return pvPortMalloc(xSize); + } + + /* realloc things */ + + // memory management in ISR is not allowed + if(FURI_IS_IRQ_MODE()) { + furi_crash("memmgt in ISR"); + } + + memmgr_lock(); + + // trace old block as free + size_t old_size = tlsf_block_size(pv); + + // trace free + memmgr_heap_trace_free(pv); + + // reallocate block + void* data = tlsf_realloc(tlsf, pv, xSize); + if(data == NULL) { + furi_crash("out of memory"); + } + + // update heap usage + heap_used -= old_size; + heap_used += tlsf_block_size(data); + if(heap_used > heap_max_used) { + heap_max_used = heap_used; + } + + // trace allocation + memmgr_heap_trace_malloc(data, xSize); + + memmgr_unlock(); + + // clear remain block content, if the new size is bigger + // can't guarantee that all data will be zeroed, cos tlsf_block_size is not always the same as xSize + if(xSize > old_size) { + memset((uint8_t*)data + old_size, 0, xSize - old_size); + } + + return data; } -/*-----------------------------------------------------------*/ size_t xPortGetFreeHeapSize(void) { - return xFreeBytesRemaining; + return memmgr_get_heap_size() - heap_used - tlsf_size(tlsf); +} + +size_t xPortGetTotalHeapSize(void) { + return memmgr_get_heap_size(); } -/*-----------------------------------------------------------*/ size_t xPortGetMinimumEverFreeHeapSize(void) { - return xMinimumEverFreeBytesRemaining; -} -/*-----------------------------------------------------------*/ - -void vPortInitialiseBlocks(void) { - /* This just exists to keep the linker quiet. */ -} -/*-----------------------------------------------------------*/ - -static void prvHeapInit(void) { - BlockLink_t* pxFirstFreeBlock; - uint8_t* pucAlignedHeap; - size_t uxAddress; - size_t xTotalHeapSize = (size_t)&__heap_end__ - (size_t)&__heap_start__; - - /* Ensure the heap starts on a correctly aligned boundary. */ - uxAddress = (size_t)ucHeap; - - if((uxAddress & portBYTE_ALIGNMENT_MASK) != 0) { - uxAddress += (portBYTE_ALIGNMENT - 1); - uxAddress &= ~((size_t)portBYTE_ALIGNMENT_MASK); - xTotalHeapSize -= uxAddress - (size_t)ucHeap; - } - - pucAlignedHeap = (uint8_t*)uxAddress; - - /* xStart is used to hold a pointer to the first item in the list of free - blocks. The void cast is used to prevent compiler warnings. */ - xStart.pxNextFreeBlock = (void*)pucAlignedHeap; - xStart.xBlockSize = (size_t)0; - - /* pxEnd is used to mark the end of the list of free blocks and is inserted - at the end of the heap space. */ - uxAddress = ((size_t)pucAlignedHeap) + xTotalHeapSize; - uxAddress -= xHeapStructSize; - uxAddress &= ~((size_t)portBYTE_ALIGNMENT_MASK); - pxEnd = (void*)uxAddress; - pxEnd->xBlockSize = 0; - pxEnd->pxNextFreeBlock = NULL; - - /* To start with there is a single free block that is sized to take up the - entire heap space, minus the space taken by pxEnd. */ - pxFirstFreeBlock = (void*)pucAlignedHeap; - pxFirstFreeBlock->xBlockSize = uxAddress - (size_t)pxFirstFreeBlock; - pxFirstFreeBlock->pxNextFreeBlock = pxEnd; - - /* Only one block exists - and it covers the entire usable heap space. */ - xMinimumEverFreeBytesRemaining = pxFirstFreeBlock->xBlockSize; - xFreeBytesRemaining = pxFirstFreeBlock->xBlockSize; - - /* Work out the position of the top bit in a size_t variable. */ - xBlockAllocatedBit = ((size_t)1) << ((sizeof(size_t) * heapBITS_PER_BYTE) - 1); -} -/*-----------------------------------------------------------*/ - -static void prvInsertBlockIntoFreeList(BlockLink_t* pxBlockToInsert) { - BlockLink_t* pxIterator; - uint8_t* puc; - - /* Iterate through the list until a block is found that has a higher address - than the block being inserted. */ - for(pxIterator = &xStart; pxIterator->pxNextFreeBlock < pxBlockToInsert; - pxIterator = pxIterator->pxNextFreeBlock) { - /* Nothing to do here, just iterate to the right position. */ - } - - /* Do the block being inserted, and the block it is being inserted after - make a contiguous block of memory? */ - puc = (uint8_t*)pxIterator; - if((puc + pxIterator->xBlockSize) == (uint8_t*)pxBlockToInsert) { - pxIterator->xBlockSize += pxBlockToInsert->xBlockSize; - pxBlockToInsert = pxIterator; - } else { - mtCOVERAGE_TEST_MARKER(); - } - - /* Do the block being inserted, and the block it is being inserted before - make a contiguous block of memory? */ - puc = (uint8_t*)pxBlockToInsert; - if((puc + pxBlockToInsert->xBlockSize) == (uint8_t*)pxIterator->pxNextFreeBlock) { - if(pxIterator->pxNextFreeBlock != pxEnd) { - /* Form one big block from the two blocks. */ - pxBlockToInsert->xBlockSize += pxIterator->pxNextFreeBlock->xBlockSize; - pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock->pxNextFreeBlock; - } else { - pxBlockToInsert->pxNextFreeBlock = pxEnd; - } - } else { - pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock; - } - - /* If the block being inserted plugged a gab, so was merged with the block - before and the block after, then it's pxNextFreeBlock pointer will have - already been set, and should not be set here as that would make it point - to itself. */ - if(pxIterator != pxBlockToInsert) { - pxIterator->pxNextFreeBlock = pxBlockToInsert; - } else { - mtCOVERAGE_TEST_MARKER(); - } -} + return memmgr_get_heap_size() - heap_max_used - tlsf_size(tlsf); +} \ No newline at end of file diff --git a/furi/core/memmgr_heap.h b/furi/core/memmgr_heap.h index 7d889f152..2f61deb64 100644 --- a/furi/core/memmgr_heap.h +++ b/furi/core/memmgr_heap.h @@ -40,9 +40,17 @@ size_t memmgr_heap_get_thread_memory(FuriThreadId thread_id); */ size_t memmgr_heap_get_max_free_block(void); -/** Print the address and size of all free blocks to stdout +typedef bool (*BlockWalker)(void* pointer, size_t size, bool used, void* context); + +/** + * @brief Walk through all heap blocks + * @warning This function will lock memory manager and may cause deadlocks if any malloc/free is called inside the callback. + * Also, printf and furi_log contains malloc calls, so do not use them. + * + * @param walker + * @param context */ -void memmgr_heap_printf_free_blocks(void); +void memmgr_heap_walk_blocks(BlockWalker walker, void* context); #ifdef __cplusplus } diff --git a/furi/core/thread.c b/furi/core/thread.c index f9f73b4f7..c9bf79d32 100644 --- a/furi/core/thread.c +++ b/furi/core/thread.c @@ -276,8 +276,8 @@ void furi_thread_start(FuriThread* thread) { stack, thread, priority, - memmgr_alloc_from_pool(sizeof(StackType_t) * stack), - memmgr_alloc_from_pool(sizeof(StaticTask_t))); + memmgr_aux_pool_alloc(sizeof(StackType_t) * stack), + memmgr_aux_pool_alloc(sizeof(StaticTask_t))); } else { BaseType_t ret = xTaskCreate( furi_thread_body, thread->name, stack, thread, priority, &thread->task_handle); diff --git a/furi/flipper.c b/furi/flipper.c index c7ba3b4fb..6c7b9831a 100644 --- a/furi/flipper.c +++ b/furi/flipper.c @@ -51,12 +51,17 @@ void flipper_init(void) { FURI_LOG_I(TAG, "Startup complete"); } +PLACE_IN_SECTION("MB_MEM2") static StaticTask_t idle_task_tcb; +PLACE_IN_SECTION("MB_MEM2") static StackType_t idle_task_stack[configIDLE_TASK_STACK_DEPTH]; +PLACE_IN_SECTION("MB_MEM2") static StaticTask_t timer_task_tcb; +PLACE_IN_SECTION("MB_MEM2") static StackType_t timer_task_stack[configTIMER_TASK_STACK_DEPTH]; + void vApplicationGetIdleTaskMemory( StaticTask_t** tcb_ptr, StackType_t** stack_ptr, uint32_t* stack_size) { - *tcb_ptr = memmgr_alloc_from_pool(sizeof(StaticTask_t)); - *stack_ptr = memmgr_alloc_from_pool(sizeof(StackType_t) * configIDLE_TASK_STACK_DEPTH); + *tcb_ptr = &idle_task_tcb; + *stack_ptr = idle_task_stack; *stack_size = configIDLE_TASK_STACK_DEPTH; } @@ -64,7 +69,7 @@ void vApplicationGetTimerTaskMemory( StaticTask_t** tcb_ptr, StackType_t** stack_ptr, uint32_t* stack_size) { - *tcb_ptr = memmgr_alloc_from_pool(sizeof(StaticTask_t)); - *stack_ptr = memmgr_alloc_from_pool(sizeof(StackType_t) * configTIMER_TASK_STACK_DEPTH); + *tcb_ptr = &timer_task_tcb; + *stack_ptr = timer_task_stack; *stack_size = configTIMER_TASK_STACK_DEPTH; } \ No newline at end of file diff --git a/lib/SConscript b/lib/SConscript index 812573932..29c48de6d 100644 --- a/lib/SConscript +++ b/lib/SConscript @@ -13,6 +13,7 @@ env.Append( libs = env.BuildModules( [ + "tlsf", "mlib", "stm32wb", "freertos", diff --git a/lib/flipper_application/elf/elf_file.c b/lib/flipper_application/elf/elf_file.c index 398f25209..3e10ae3fe 100644 --- a/lib/flipper_application/elf/elf_file.c +++ b/lib/flipper_application/elf/elf_file.c @@ -466,7 +466,7 @@ static bool elf_load_section_data(ELFFile* elf, ELFSection* section, Elf32_Shdr* return true; } - section->data = aligned_malloc(section_header->sh_size, section_header->sh_addralign); + section->data = aligned_alloc(section_header->sh_addralign, section_header->sh_size); section->size = section_header->sh_size; if(section_header->sh_type == SHT_NOBITS) { @@ -718,7 +718,7 @@ static bool elf_relocate_fast(ELFFile* elf, ELFSection* s) { } } - aligned_free(s->fast_rel->data); + free(s->fast_rel->data); free(s->fast_rel); s->fast_rel = NULL; @@ -785,10 +785,10 @@ void elf_file_free(ELFFile* elf) { ELFSectionDict_next(it)) { const ELFSectionDict_itref_t* itref = ELFSectionDict_cref(it); if(itref->value.data) { - aligned_free(itref->value.data); + free(itref->value.data); } if(itref->value.fast_rel) { - aligned_free(itref->value.fast_rel->data); + free(itref->value.fast_rel->data); free(itref->value.fast_rel); } free((void*)itref->key); diff --git a/lib/tlsf b/lib/tlsf new file mode 160000 index 000000000..8fc595fe2 --- /dev/null +++ b/lib/tlsf @@ -0,0 +1 @@ +Subproject commit 8fc595fe223cd0b3b5d7b29eb86825e4bd38e6e8 diff --git a/lib/tlsf.scons b/lib/tlsf.scons new file mode 100644 index 000000000..0a8419dbd --- /dev/null +++ b/lib/tlsf.scons @@ -0,0 +1,21 @@ +Import("env") + +env.Append( + CPPPATH=[ + "#/lib/tlsf", + ], +) + + +libenv = env.Clone(FW_LIB_NAME="tlsf") +libenv.ApplyLibFlags() + +libenv.Append( + CPPDEFINES=[], +) + +sources = [File("tlsf/tlsf.c")] + +lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources) +libenv.Install("${LIB_DIST_DIR}", lib) +Return("lib") diff --git a/targets/f18/api_symbols.csv b/targets/f18/api_symbols.csv index 492539d46..ef2d2fcb6 100644 --- a/targets/f18/api_symbols.csv +++ b/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,61.3,, +Version,+,62.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -515,9 +515,7 @@ Function,-,acosh,double,double Function,-,acoshf,float,float Function,-,acoshl,long double,long double Function,-,acosl,long double,long double -Function,-,aligned_alloc,void*,"size_t, size_t" -Function,+,aligned_free,void,void* -Function,+,aligned_malloc,void*,"size_t, size_t" +Function,+,aligned_alloc,void*,"size_t, size_t" Function,-,arc4random,__uint32_t, Function,-,arc4random_buf,void,"void*, size_t" Function,-,arc4random_uniform,__uint32_t,__uint32_t @@ -1984,7 +1982,8 @@ Function,+,memchr,void*,"const void*, int, size_t" Function,+,memcmp,int,"const void*, const void*, size_t" Function,+,memcpy,void*,"void*, const void*, size_t" Function,-,memmem,void*,"const void*, size_t, const void*, size_t" -Function,-,memmgr_alloc_from_pool,void*,size_t +Function,+,memmgr_aux_pool_alloc,void*,size_t +Function,+,memmgr_aux_pool_get_free,size_t, Function,+,memmgr_get_free_heap,size_t, Function,+,memmgr_get_minimum_free_heap,size_t, Function,+,memmgr_get_total_heap,size_t, @@ -1992,8 +1991,7 @@ Function,+,memmgr_heap_disable_thread_trace,void,FuriThreadId Function,+,memmgr_heap_enable_thread_trace,void,FuriThreadId Function,+,memmgr_heap_get_max_free_block,size_t, Function,+,memmgr_heap_get_thread_memory,size_t,FuriThreadId -Function,+,memmgr_heap_printf_free_blocks,void, -Function,-,memmgr_pool_get_free,size_t, +Function,+,memmgr_heap_walk_blocks,void,"BlockWalker, void*" Function,-,memmgr_pool_get_max_block,size_t, Function,+,memmove,void*,"void*, const void*, size_t" Function,-,mempcpy,void*,"void*, const void*, size_t" diff --git a/targets/f18/target.json b/targets/f18/target.json index 43e9254cd..a61c1373e 100644 --- a/targets/f18/target.json +++ b/targets/f18/target.json @@ -13,6 +13,7 @@ "print", "flipper18", "furi", + "tlsf", "freertos", "stm32wb", "hwdrivers", @@ -68,4 +69,4 @@ "ibutton", "infrared" ] -} +} \ No newline at end of file diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index e209023b5..1f959f0c7 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,61.3,, +Version,+,62.0,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, @@ -586,9 +586,7 @@ Function,-,acosh,double,double Function,-,acoshf,float,float Function,-,acoshl,long double,long double Function,-,acosl,long double,long double -Function,-,aligned_alloc,void*,"size_t, size_t" -Function,+,aligned_free,void,void* -Function,+,aligned_malloc,void*,"size_t, size_t" +Function,+,aligned_alloc,void*,"size_t, size_t" Function,-,arc4random,__uint32_t, Function,-,arc4random_buf,void,"void*, size_t" Function,-,arc4random_uniform,__uint32_t,__uint32_t @@ -2394,7 +2392,8 @@ Function,+,memchr,void*,"const void*, int, size_t" Function,+,memcmp,int,"const void*, const void*, size_t" Function,+,memcpy,void*,"void*, const void*, size_t" Function,-,memmem,void*,"const void*, size_t, const void*, size_t" -Function,-,memmgr_alloc_from_pool,void*,size_t +Function,+,memmgr_aux_pool_alloc,void*,size_t +Function,+,memmgr_aux_pool_get_free,size_t, Function,+,memmgr_get_free_heap,size_t, Function,+,memmgr_get_minimum_free_heap,size_t, Function,+,memmgr_get_total_heap,size_t, @@ -2402,8 +2401,7 @@ Function,+,memmgr_heap_disable_thread_trace,void,FuriThreadId Function,+,memmgr_heap_enable_thread_trace,void,FuriThreadId Function,+,memmgr_heap_get_max_free_block,size_t, Function,+,memmgr_heap_get_thread_memory,size_t,FuriThreadId -Function,+,memmgr_heap_printf_free_blocks,void, -Function,-,memmgr_pool_get_free,size_t, +Function,+,memmgr_heap_walk_blocks,void,"BlockWalker, void*" Function,-,memmgr_pool_get_max_block,size_t, Function,+,memmove,void*,"void*, const void*, size_t" Function,-,mempcpy,void*,"void*, const void*, size_t" diff --git a/targets/f7/fatfs/sector_cache.c b/targets/f7/fatfs/sector_cache.c index 319dd2173..df86cb7f1 100644 --- a/targets/f7/fatfs/sector_cache.c +++ b/targets/f7/fatfs/sector_cache.c @@ -19,7 +19,7 @@ static SectorCache* cache = NULL; void sector_cache_init(void) { if(cache == NULL) { - cache = memmgr_alloc_from_pool(sizeof(SectorCache)); + cache = memmgr_aux_pool_alloc(sizeof(SectorCache)); } if(cache != NULL) { diff --git a/targets/f7/src/update.c b/targets/f7/src/update.c index e6cb4aabe..261adb5ca 100644 --- a/targets/f7/src/update.c +++ b/targets/f7/src/update.c @@ -78,21 +78,21 @@ static bool flipper_update_load_stage(const FuriString* work_dir, UpdateManifest furi_string_free(loader_img_path); void* img = malloc(stat.fsize); - uint32_t bytes_read = 0; + uint32_t read_total = 0; + uint16_t read_current = 0; const uint16_t MAX_READ = 0xFFFF; uint32_t crc = 0; do { - uint16_t size_read = 0; - if(f_read(&file, img + bytes_read, MAX_READ, &size_read) != FR_OK) { //-V769 + if(f_read(&file, img + read_total, MAX_READ, &read_current) != FR_OK) { //-V769 break; } - crc = crc32_calc_buffer(crc, img + bytes_read, size_read); - bytes_read += size_read; - } while(bytes_read == MAX_READ); + crc = crc32_calc_buffer(crc, img + read_total, read_current); + read_total += read_current; + } while(read_current == MAX_READ); do { - if((bytes_read != stat.fsize) || (crc != manifest->staged_loader_crc)) { + if((read_total != stat.fsize) || (crc != manifest->staged_loader_crc)) { break; } diff --git a/targets/f7/target.json b/targets/f7/target.json index 25872198b..caa3f58ee 100644 --- a/targets/f7/target.json +++ b/targets/f7/target.json @@ -22,6 +22,7 @@ "print", "flipper7", "furi", + "tlsf", "freertos", "stm32wb", "hwdrivers", @@ -55,4 +56,4 @@ "bit_lib", "datetime" ] -} +} \ No newline at end of file From 50bee67748f139fc52561e9adb4e06816a05f088 Mon Sep 17 00:00:00 2001 From: Eric Betts Date: Wed, 15 May 2024 11:09:08 -0700 Subject: [PATCH 04/11] Skylanders plugin (#3315) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * PoC skylanders plugin * based on https://github.com/RogueMaster/flipperzero-firmware-wPlugins/commit/db0c7e4fad5c214c36aa8753d27befe49676a985 * More figures * Fix util methods Co-authored-by: あく Co-authored-by: gornekich --- applications/main/nfc/application.fam | 9 + .../nfc/plugins/supported_cards/skylanders.c | 873 ++++++++++++++++++ 2 files changed, 882 insertions(+) create mode 100644 applications/main/nfc/plugins/supported_cards/skylanders.c diff --git a/applications/main/nfc/application.fam b/applications/main/nfc/application.fam index b6a0f09d3..898434bb7 100644 --- a/applications/main/nfc/application.fam +++ b/applications/main/nfc/application.fam @@ -182,6 +182,15 @@ App( sources=["plugins/supported_cards/itso.c"], ) +App( + appid="skylanders_parser", + apptype=FlipperAppType.PLUGIN, + entry_point="skylanders_plugin_ep", + targets=["f7"], + requires=["nfc"], + sources=["plugins/supported_cards/skylanders.c"], +) + App( appid="nfc_start", targets=["f7"], diff --git a/applications/main/nfc/plugins/supported_cards/skylanders.c b/applications/main/nfc/plugins/supported_cards/skylanders.c new file mode 100644 index 000000000..a60a5dba3 --- /dev/null +++ b/applications/main/nfc/plugins/supported_cards/skylanders.c @@ -0,0 +1,873 @@ +#include "nfc_supported_card_plugin.h" + +#include + +#include +#include +#include + +#define TAG "Skylanders" + +static const uint64_t skylanders_key = 0x4b0b20107ccb; + +bool skylanders_verify(Nfc* nfc) { + bool verified = false; + + do { + const uint8_t verify_sector = 0; + uint8_t block_num = mf_classic_get_first_block_num_of_sector(verify_sector); + FURI_LOG_D(TAG, "Verifying sector %u", verify_sector); + + MfClassicKey key = {}; + bit_lib_num_to_bytes_be(skylanders_key, COUNT_OF(key.data), key.data); + + MfClassicAuthContext auth_ctx = {}; + MfClassicError error = + mf_classic_poller_sync_auth(nfc, block_num, &key, MfClassicKeyTypeA, &auth_ctx); + + if(error != MfClassicErrorNone) { + FURI_LOG_D(TAG, "Failed to read block %u: %d", block_num, error); + break; + } + + verified = true; + } while(false); + + return verified; +} + +static bool skylanders_read(Nfc* nfc, NfcDevice* device) { + furi_assert(nfc); + furi_assert(device); + + bool is_read = false; + + MfClassicData* data = mf_classic_alloc(); + nfc_device_copy_data(device, NfcProtocolMfClassic, data); + + do { + MfClassicType type = MfClassicType1k; + MfClassicError error = mf_classic_poller_sync_detect_type(nfc, &type); + if(error != MfClassicErrorNone) break; + + data->type = type; + MfClassicDeviceKeys keys = {}; + for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) { + bit_lib_num_to_bytes_be(skylanders_key, sizeof(MfClassicKey), keys.key_a[i].data); + FURI_BIT_SET(keys.key_a_mask, i); + bit_lib_num_to_bytes_be(skylanders_key, sizeof(MfClassicKey), keys.key_b[i].data); + FURI_BIT_SET(keys.key_b_mask, i); + } + + error = mf_classic_poller_sync_read(nfc, &keys, data); + if(error != MfClassicErrorNone) { + FURI_LOG_W(TAG, "Failed to read data"); + break; + } + + nfc_device_set_data(device, NfcProtocolMfClassic, data); + + is_read = mf_classic_is_card_read(data); + } while(false); + + mf_classic_free(data); + + return is_read; +} + +static uint8_t fill_name(const uint16_t id, FuriString* name) { + // USED RESEARCH FROM https://github.com/silicontrip/SkyReader/blob/master/toynames.cpp#L15C1-L163C1 + // AND https://github.com/bettse/Solarbreeze/blob/master/Solarbreeze/ThePoster.swift#L438C1-L681C1 + switch(id) { + case 0x0000: + furi_string_cat_printf(name, "Whirlwind"); + break; + case 0x0001: + furi_string_cat_printf(name, "Sonic Boom"); + break; + case 0x0002: + furi_string_cat_printf(name, "Warnado"); + break; + case 0x0003: + furi_string_cat_printf(name, "Lightning Rod"); + break; + case 0x0004: + furi_string_cat_printf(name, "Bash"); + break; + case 0x0005: + furi_string_cat_printf(name, "Terrafin"); + break; + case 0x0006: + furi_string_cat_printf(name, "Dino-Rang"); + break; + case 0x0007: + furi_string_cat_printf(name, "Prism Break"); + break; + case 0x0008: + furi_string_cat_printf(name, "Sunburn"); + break; + case 0x0009: + furi_string_cat_printf(name, "Eruptor"); + break; + case 0x000A: + furi_string_cat_printf(name, "Ignitor"); + break; + case 0x000B: + furi_string_cat_printf(name, "Flameslinger"); + break; + case 0x000C: + furi_string_cat_printf(name, "Zap"); + break; + case 0x000D: + furi_string_cat_printf(name, "Wham-Shell"); + break; + case 0x000E: + furi_string_cat_printf(name, "Gill Grunt"); + break; + case 0x000F: + furi_string_cat_printf(name, "Slam Bam"); + break; + case 0x0010: + furi_string_cat_printf(name, "Spyro"); + break; + case 0x0011: + furi_string_cat_printf(name, "Voodood"); + break; + case 0x0012: + furi_string_cat_printf(name, "Double Trouble"); + break; + case 0x0013: + furi_string_cat_printf(name, "Trigger Happy"); + break; + case 0x0014: + furi_string_cat_printf(name, "Drobot"); + break; + case 0x0015: + furi_string_cat_printf(name, "Drill Sergeant"); + break; + case 0x0016: + furi_string_cat_printf(name, "Boomer"); + break; + case 0x0017: + furi_string_cat_printf(name, "Wrecking Ball"); + break; + case 0x0018: + furi_string_cat_printf(name, "Camo"); + break; + case 0x0019: + furi_string_cat_printf(name, "Zook"); + break; + case 0x001A: + furi_string_cat_printf(name, "Stealth Elf"); + break; + case 0x001B: + furi_string_cat_printf(name, "Stump Smash"); + break; + case 0x001C: + furi_string_cat_printf(name, "Dark Spyro"); + break; + case 0x001D: + furi_string_cat_printf(name, "Hex"); + break; + case 0x001E: + furi_string_cat_printf(name, "Chop Chop"); + break; + case 0x001F: + furi_string_cat_printf(name, "Ghost Roaster"); + break; + case 0x0020: + furi_string_cat_printf(name, "Cynder"); + break; + case 0x0064: + furi_string_cat_printf(name, "Jet Vac"); + break; + case 0x0065: + furi_string_cat_printf(name, "Swarm"); + break; + case 0x0066: + furi_string_cat_printf(name, "Crusher"); + break; + case 0x0067: + furi_string_cat_printf(name, "Flashwing"); + break; + case 0x0068: + furi_string_cat_printf(name, "Hot Head"); + break; + case 0x0069: + furi_string_cat_printf(name, "Hot Dog"); + break; + case 0x006A: + furi_string_cat_printf(name, "Chill"); + break; + case 0x006B: + furi_string_cat_printf(name, "Thumpback"); + break; + case 0x006C: + furi_string_cat_printf(name, "Pop Fizz"); + break; + case 0x006D: + furi_string_cat_printf(name, "Ninjini"); + break; + case 0x006E: + furi_string_cat_printf(name, "Bouncer"); + break; + case 0x006F: + furi_string_cat_printf(name, "Sprocket"); + break; + case 0x0070: + furi_string_cat_printf(name, "Tree Rex"); + break; + case 0x0071: + furi_string_cat_printf(name, "Shroomboom"); + break; + case 0x0072: + furi_string_cat_printf(name, "Eye-Brawl"); + break; + case 0x0073: + furi_string_cat_printf(name, "Fright Rider"); + break; + case 0x00C8: + furi_string_cat_printf(name, "Anvil Rain"); + break; + case 0x00C9: + furi_string_cat_printf(name, "Treasure Chest"); + break; + case 0x00CA: + furi_string_cat_printf(name, "Healing Elixer"); + break; + case 0x00CB: + furi_string_cat_printf(name, "Ghost Swords"); + break; + case 0x00CC: + furi_string_cat_printf(name, "Time Twister"); + break; + case 0x00CD: + furi_string_cat_printf(name, "Sky-Iron Shield"); + break; + case 0x00CE: + furi_string_cat_printf(name, "Winged Boots"); + break; + case 0x00CF: + furi_string_cat_printf(name, "Sparx Dragonfly"); + break; + case 0x00D0: + furi_string_cat_printf(name, "Dragonfire Cannon"); + break; + case 0x00D1: + furi_string_cat_printf(name, "Scorpion Striker Catapult"); + break; + case 0x00D2: + furi_string_cat_printf(name, "Trap - Magic"); + break; + case 0x00D3: + furi_string_cat_printf(name, "Trap - Water"); + break; + case 0x00D4: + furi_string_cat_printf(name, "Trap - Air"); + break; + case 0x00D5: + furi_string_cat_printf(name, "Trap - Undead"); + break; + case 0x00D6: + furi_string_cat_printf(name, "Trap - Tech"); + break; + case 0x00D7: + furi_string_cat_printf(name, "Trap - Fire"); + break; + case 0x00D8: + furi_string_cat_printf(name, "Trap - Earth"); + break; + case 0x00D9: + furi_string_cat_printf(name, "Trap - Life"); + break; + case 0x00DA: + furi_string_cat_printf(name, "Trap - Light"); + break; + case 0x00DB: + furi_string_cat_printf(name, "Trap - Dark"); + break; + case 0x00DC: + furi_string_cat_printf(name, "Trap - Kaos"); + break; + case 0x00E6: + furi_string_cat_printf(name, "Hand Of Fate"); + break; + case 0x00E7: + furi_string_cat_printf(name, "Piggy Bank"); + break; + case 0x00E8: + furi_string_cat_printf(name, "Rocket Ram"); + break; + case 0x00E9: + furi_string_cat_printf(name, "Tiki Speaky"); + break; + case 0x00EB: + furi_string_cat_printf(name, "Imaginite Mystery Chest"); + break; + case 0x012C: + furi_string_cat_printf(name, "Dragons Peak"); + break; + case 0x012D: + furi_string_cat_printf(name, "Empire of Ice"); + break; + case 0x012E: + furi_string_cat_printf(name, "Pirate Seas"); + break; + case 0x012F: + furi_string_cat_printf(name, "Darklight Crypt"); + break; + case 0x0130: + furi_string_cat_printf(name, "Volcanic Vault"); + break; + case 0x0131: + furi_string_cat_printf(name, "Mirror Of Mystery"); + break; + case 0x0132: + furi_string_cat_printf(name, "Nightmare Express"); + break; + case 0x0133: + furi_string_cat_printf(name, "Sunscraper Spire"); + break; + case 0x0134: + furi_string_cat_printf(name, "Midnight Museum"); + break; + case 0x0194: + furi_string_cat_printf(name, "Bash"); + break; + case 0x01A0: + furi_string_cat_printf(name, "Spyro"); + break; + case 0x01A3: + furi_string_cat_printf(name, "Trigger Happy"); + break; + case 0x01AE: + furi_string_cat_printf(name, "Chop Chop"); + break; + case 0x01C2: + furi_string_cat_printf(name, "Gusto"); + break; + case 0x01C3: + furi_string_cat_printf(name, "Thunderbolt"); + break; + case 0x01C4: + furi_string_cat_printf(name, "Fling Kong"); + break; + case 0x01C5: + furi_string_cat_printf(name, "Blades"); + break; + case 0x01C6: + furi_string_cat_printf(name, "Wallop"); + break; + case 0x01C7: + furi_string_cat_printf(name, "Head Rush"); + break; + case 0x01C8: + furi_string_cat_printf(name, "Fist Bump"); + break; + case 0x01C9: + furi_string_cat_printf(name, "Rocky Roll"); + break; + case 0x01CA: + furi_string_cat_printf(name, "Wildfire"); + break; + case 0x01CB: + furi_string_cat_printf(name, "Ka Boom"); + break; + case 0x01CC: + furi_string_cat_printf(name, "Trail Blazer"); + break; + case 0x01CD: + furi_string_cat_printf(name, "Torch"); + break; + case 0x01CE: + furi_string_cat_printf(name, "Snap Shot"); + break; + case 0x01CF: + furi_string_cat_printf(name, "Lob Star"); + break; + case 0x01D0: + furi_string_cat_printf(name, "Flip Wreck"); + break; + case 0x01D1: + furi_string_cat_printf(name, "Echo"); + break; + case 0x01D2: + furi_string_cat_printf(name, "Blastermind"); + break; + case 0x01D3: + furi_string_cat_printf(name, "Enigma"); + break; + case 0x01D4: + furi_string_cat_printf(name, "Deja Vu"); + break; + case 0x01D5: + furi_string_cat_printf(name, "Cobra Cadabra"); + break; + case 0x01D6: + furi_string_cat_printf(name, "Jawbreaker"); + break; + case 0x01D7: + furi_string_cat_printf(name, "Gearshift"); + break; + case 0x01D8: + furi_string_cat_printf(name, "Chopper"); + break; + case 0x01D9: + furi_string_cat_printf(name, "Tread Head"); + break; + case 0x01DA: + furi_string_cat_printf(name, "Bushwhack"); + break; + case 0x01DB: + furi_string_cat_printf(name, "Tuff Luck"); + break; + case 0x01DC: + furi_string_cat_printf(name, "Food Fight"); + break; + case 0x01DD: + furi_string_cat_printf(name, "High Five"); + break; + case 0x01DE: + furi_string_cat_printf(name, "Krypt King"); + break; + case 0x01DF: + furi_string_cat_printf(name, "Short Cut"); + break; + case 0x01E0: + furi_string_cat_printf(name, "Bat Spin"); + break; + case 0x01E1: + furi_string_cat_printf(name, "Funny Bone"); + break; + case 0x01E2: + furi_string_cat_printf(name, "Knight light"); + break; + case 0x01E3: + furi_string_cat_printf(name, "Spotlight"); + break; + case 0x01E4: + furi_string_cat_printf(name, "Knight Mare"); + break; + case 0x01E5: + furi_string_cat_printf(name, "Blackout"); + break; + case 0x01F6: + furi_string_cat_printf(name, "Bop"); + break; + case 0x01F7: + furi_string_cat_printf(name, "Spry"); + break; + case 0x01F8: + furi_string_cat_printf(name, "Hijinx"); + break; + case 0x01F9: + furi_string_cat_printf(name, "Terrabite"); + break; + case 0x01FA: + furi_string_cat_printf(name, "Breeze"); + break; + case 0x01FB: + furi_string_cat_printf(name, "Weeruptor"); + break; + case 0x01FC: + furi_string_cat_printf(name, "Pet Vac"); + break; + case 0x01FD: + furi_string_cat_printf(name, "Small Fry"); + break; + case 0x01FE: + furi_string_cat_printf(name, "Drobit"); + break; + case 0x0202: + furi_string_cat_printf(name, "Gill Runt"); + break; + case 0x0207: + furi_string_cat_printf(name, "Trigger Snappy"); + break; + case 0x020E: + furi_string_cat_printf(name, "Whisper Elf"); + break; + case 0x021C: + furi_string_cat_printf(name, "Barkley"); + break; + case 0x021D: + furi_string_cat_printf(name, "Thumpling"); + break; + case 0x021E: + furi_string_cat_printf(name, "Mini Jini"); + break; + case 0x021F: + furi_string_cat_printf(name, "Eye Small"); + break; + case 0x0259: + furi_string_cat_printf(name, "King Pen"); + break; + case 0x0265: + furi_string_cat_printf(name, "Golden Queen"); + break; + case 0x02AD: + furi_string_cat_printf(name, "Fire Acorn"); + break; + case 0x03E8: + furi_string_cat_printf(name, "(Boom) Jet"); + break; + case 0x03E9: + furi_string_cat_printf(name, "(Free) Ranger"); + break; + case 0x03EA: + furi_string_cat_printf(name, "(Rubble) Rouser"); + break; + case 0x03EB: + furi_string_cat_printf(name, "(Doom) Stone"); + break; + case 0x03EC: + furi_string_cat_printf(name, "Blast Zone"); + break; + case 0x03ED: + furi_string_cat_printf(name, "(Fire) Kraken"); + break; + case 0x03EE: + furi_string_cat_printf(name, "(Stink) Bomb"); + break; + case 0x03EF: + furi_string_cat_printf(name, "(Grilla) Drilla"); + break; + case 0x03F0: + furi_string_cat_printf(name, "(Hoot) Loop"); + break; + case 0x03F1: + furi_string_cat_printf(name, "(Trap) Shadow"); + break; + case 0x03F2: + furi_string_cat_printf(name, "(Magna) Charge"); + break; + case 0x03F3: + furi_string_cat_printf(name, "(Spy) Rise"); + break; + case 0x03F4: + furi_string_cat_printf(name, "(Night) Shift"); + break; + case 0x03F5: + furi_string_cat_printf(name, "(Rattle) Shake"); + break; + case 0x03F6: + furi_string_cat_printf(name, "(Freeze) Blade"); + break; + case 0x03F7: + furi_string_cat_printf(name, "Wash Buckler"); + break; + case 0x07D0: + furi_string_cat_printf(name, "Boom (Jet)"); + break; + case 0x07D1: + furi_string_cat_printf(name, "Free (Ranger)"); + break; + case 0x07D2: + furi_string_cat_printf(name, "Rubble (Rouser)"); + break; + case 0x07D3: + furi_string_cat_printf(name, "Doom (Stone)"); + break; + case 0x07D4: + furi_string_cat_printf(name, "Blast Zone (Head)"); + break; + case 0x07D5: + furi_string_cat_printf(name, "Fire (Kraken)"); + break; + case 0x07D6: + furi_string_cat_printf(name, "Stink (Bomb)"); + break; + case 0x07D7: + furi_string_cat_printf(name, "Grilla (Drilla)"); + break; + case 0x07D8: + furi_string_cat_printf(name, "Hoot (Loop)"); + break; + case 0x07D9: + furi_string_cat_printf(name, "Trap (Shadow)"); + break; + case 0x07DA: + furi_string_cat_printf(name, "Magna (Charge)"); + break; + case 0x07DB: + furi_string_cat_printf(name, "Spy (Rise)"); + break; + case 0x07DC: + furi_string_cat_printf(name, "Night (Shift)"); + break; + case 0x07DD: + furi_string_cat_printf(name, "Rattle (Shake)"); + break; + case 0x07DE: + furi_string_cat_printf(name, "Freeze (Blade)"); + break; + case 0x07DF: + furi_string_cat_printf(name, "Wash Buckler (Head)"); + break; + case 0x0BB8: + furi_string_cat_printf(name, "Scratch"); + break; + case 0x0BB9: + furi_string_cat_printf(name, "Pop Thorn"); + break; + case 0x0BBA: + furi_string_cat_printf(name, "Slobber Tooth"); + break; + case 0x0BBB: + furi_string_cat_printf(name, "Scorp"); + break; + case 0x0BBC: + furi_string_cat_printf(name, "Fryno"); + break; + case 0x0BBD: + furi_string_cat_printf(name, "Smolderdash"); + break; + case 0x0BBE: + furi_string_cat_printf(name, "Bumble Blast"); + break; + case 0x0BBF: + furi_string_cat_printf(name, "Zoo Lou"); + break; + case 0x0BC0: + furi_string_cat_printf(name, "Dune Bug"); + break; + case 0x0BC1: + furi_string_cat_printf(name, "Star Strike"); + break; + case 0x0BC2: + furi_string_cat_printf(name, "Countdown"); + break; + case 0x0BC3: + furi_string_cat_printf(name, "Wind Up"); + break; + case 0x0BC4: + furi_string_cat_printf(name, "Roller Brawl"); + break; + case 0x0BC5: + furi_string_cat_printf(name, "Grim Creeper"); + break; + case 0x0BC6: + furi_string_cat_printf(name, "Rip Tide"); + break; + case 0x0BC7: + furi_string_cat_printf(name, "Punk Shock"); + break; + case 0x0C80: + furi_string_cat_printf(name, "Battle Hammer"); + break; + case 0x0C81: + furi_string_cat_printf(name, "Sky Diamond"); + break; + case 0x0C82: + furi_string_cat_printf(name, "Platinum Sheep"); + break; + case 0x0C83: + furi_string_cat_printf(name, "Groove Machine"); + break; + case 0x0C84: + furi_string_cat_printf(name, "UFO Hat"); + break; + case 0x0C94: + furi_string_cat_printf(name, "Jet Stream"); + break; + case 0x0C95: + furi_string_cat_printf(name, "Tomb Buggy"); + break; + case 0x0C96: + furi_string_cat_printf(name, "Reef Ripper"); + break; + case 0x0C97: + furi_string_cat_printf(name, "Burn Cycle"); + break; + case 0x0C98: + furi_string_cat_printf(name, "Hot Streak"); + break; + case 0x0C99: + furi_string_cat_printf(name, "Shark Tank"); + break; + case 0x0C9A: + furi_string_cat_printf(name, "Thump Truck"); + break; + case 0x0C9B: + furi_string_cat_printf(name, "Crypt Crusher"); + break; + case 0x0C9C: + furi_string_cat_printf(name, "Stealth Stinger"); + break; + case 0x0C9F: + furi_string_cat_printf(name, "Dive Bomber"); + break; + case 0x0CA0: + furi_string_cat_printf(name, "Sky Slicer"); + break; + case 0x0CA1: + furi_string_cat_printf(name, "Clown Cruiser"); + break; + case 0x0CA2: + furi_string_cat_printf(name, "Gold Rusher"); + break; + case 0x0CA3: + furi_string_cat_printf(name, "Shield Striker"); + break; + case 0x0CA4: + furi_string_cat_printf(name, "Sun Runner"); + break; + case 0x0CA5: + furi_string_cat_printf(name, "Sea Shadow"); + break; + case 0x0CA6: + furi_string_cat_printf(name, "Splatter Splasher"); + break; + case 0x0CA7: + furi_string_cat_printf(name, "Soda Skimmer"); + break; + case 0x0CA8: + furi_string_cat_printf(name, "Barrel Blaster"); + break; + case 0x0CA9: + furi_string_cat_printf(name, "Buzz Wing"); + break; + case 0x0CE4: + furi_string_cat_printf(name, "Sheep Wreck Island"); + break; + case 0x0CE5: + furi_string_cat_printf(name, "Tower of Time"); + break; + case 0x0CE6: + furi_string_cat_printf(name, "Fiery Forge"); + break; + case 0x0CE7: + furi_string_cat_printf(name, "Arkeyan Crossbow"); + break; + case 0x0D48: + furi_string_cat_printf(name, "Fiesta"); + break; + case 0x0D49: + furi_string_cat_printf(name, "High Volt"); + break; + case 0x0D4A: + furi_string_cat_printf(name, "Splat"); + break; + case 0x0D4E: + furi_string_cat_printf(name, "Stormblade"); + break; + case 0x0D53: + furi_string_cat_printf(name, "Smash It"); + break; + case 0x0D54: + furi_string_cat_printf(name, "Spitfire"); + break; + case 0x0D55: + furi_string_cat_printf(name, "Hurricane Jet-Vac"); + break; + case 0x0D56: + furi_string_cat_printf(name, "Double Dare Trigger Happy"); + break; + case 0x0D57: + furi_string_cat_printf(name, "Super Shot Stealth Elf"); + break; + case 0x0D58: + furi_string_cat_printf(name, "Shark Shooter Terrafin"); + break; + case 0x0D59: + furi_string_cat_printf(name, "Bone Bash Roller Brawl"); + break; + case 0x0D5C: + furi_string_cat_printf(name, "Big Bubble Pop Fizz"); + break; + case 0x0D5D: + furi_string_cat_printf(name, "Lava Lance Eruptor"); + break; + case 0x0D5E: + furi_string_cat_printf(name, "Deep Dive Gill Grunt"); + break; + case 0x0D5F: + furi_string_cat_printf(name, "Turbo Charge Donkey Kong"); + break; + case 0x0D60: + furi_string_cat_printf(name, "Hammer Slam Bowser"); + break; + case 0x0D61: + furi_string_cat_printf(name, "Dive-Clops"); + break; + case 0x0D62: + furi_string_cat_printf(name, "Astroblast"); + break; + case 0x0D63: + furi_string_cat_printf(name, "Nightfall"); + break; + case 0x0D64: + furi_string_cat_printf(name, "Thrillipede"); + break; + case 0x0DAC: + furi_string_cat_printf(name, "Sky Trophy"); + break; + case 0x0DAD: + furi_string_cat_printf(name, "Land Trophy"); + break; + case 0x0DAE: + furi_string_cat_printf(name, "Sea Trophy"); + break; + case 0x0DAF: + furi_string_cat_printf(name, "Kaos Trophy"); + break; + default: + furi_string_cat_printf(name, "Unknown"); + break; + } + + return true; +} + +static bool skylanders_parse(const NfcDevice* device, FuriString* parsed_data) { + furi_assert(device); + + const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic); + + bool parsed = false; + FuriString* name = furi_string_alloc(); + + do { + // verify key + const uint8_t verify_sector = 0; + MfClassicSectorTrailer* sec_tr = + mf_classic_get_sector_trailer_by_sector(data, verify_sector); + uint64_t key = bit_lib_bytes_to_num_be(sec_tr->key_a.data, 6); + if(key != skylanders_key) break; + + const uint16_t id = (uint16_t)*data->block[1].data; + if(id == 0) break; + + bool success = fill_name(id, name); + if(!success) break; + + furi_string_printf(parsed_data, "\e#Skylanders\n%s", furi_string_get_cstr(name)); + + parsed = true; + + } while(false); + + furi_string_free(name); + + return parsed; +} + +/* Actual implementation of app<>plugin interface */ +static const NfcSupportedCardsPlugin skylanders_plugin = { + .protocol = NfcProtocolMfClassic, + .verify = skylanders_verify, + .read = skylanders_read, + .parse = skylanders_parse, +}; + +/* Plugin descriptor to comply with basic plugin specification */ +static const FlipperAppPluginDescriptor skylanders_plugin_descriptor = { + .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID, + .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION, + .entry_point = &skylanders_plugin, +}; + +/* Plugin entry point - must return a pointer to const descriptor */ +const FlipperAppPluginDescriptor* skylanders_plugin_ep(void) { + return &skylanders_plugin_descriptor; +} From 7c63bf7574c4616ccff023c97dc8507e210ba59b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Thu, 16 May 2024 15:43:27 +0100 Subject: [PATCH 05/11] Revert "TLSF memory allocator. Less free flash, moar free ram. (#3572)" (#3651) * Revert "TLSF memory allocator. Less free flash, moar free ram. (#3572)" This reverts commit 1d17206e2358583a7feac2e142218f5b4ca38148. * Fix PVS warnings * github: logging for ticket number checks to stdout * memgr: removed offending todo --------- Co-authored-by: hedger --- .../workflows/lint_and_submodule_check.yml | 2 + .gitmodules | 3 - .pvsoptions | 2 +- .../debug/unit_tests/furi/furi_memmgr_test.c | 262 +----- .../debug/unit_tests/furi/furi_test.c | 2 - .../nfc/plugins/supported_cards/skylanders.c | 16 +- applications/services/cli/cli_commands.c | 48 +- furi/core/memmgr.c | 39 +- furi/core/memmgr.h | 25 +- furi/core/memmgr_heap.c | 786 ++++++++++++------ furi/core/memmgr_heap.h | 12 +- furi/core/thread.c | 4 +- furi/flipper.c | 13 +- lib/SConscript | 1 - lib/flipper_application/elf/elf_file.c | 8 +- lib/tlsf | 1 - lib/tlsf.scons | 21 - targets/f18/api_symbols.csv | 12 +- targets/f18/target.json | 3 +- targets/f7/api_symbols.csv | 12 +- targets/f7/fatfs/sector_cache.c | 2 +- targets/f7/src/update.c | 14 +- targets/f7/target.json | 3 +- 23 files changed, 651 insertions(+), 640 deletions(-) delete mode 160000 lib/tlsf delete mode 100644 lib/tlsf.scons diff --git a/.github/workflows/lint_and_submodule_check.yml b/.github/workflows/lint_and_submodule_check.yml index 3f4d8c79b..3063d943d 100644 --- a/.github/workflows/lint_and_submodule_check.yml +++ b/.github/workflows/lint_and_submodule_check.yml @@ -54,6 +54,8 @@ jobs: echo "\`\`\`" >> $GITHUB_STEP_SUMMARY; echo "$MISSING_TICKETS" >> $GITHUB_STEP_SUMMARY; echo "\`\`\`" >> $GITHUB_STEP_SUMMARY; + echo "Error: Missing issue number in comment(s):"; + echo "$MISSING_TICKETS"; exit 1; else echo "No new TODOs without tickets found" >> $GITHUB_STEP_SUMMARY; diff --git a/.gitmodules b/.gitmodules index a8d12803c..c4c68a6a7 100644 --- a/.gitmodules +++ b/.gitmodules @@ -41,6 +41,3 @@ [submodule "documentation/doxygen/doxygen-awesome-css"] path = documentation/doxygen/doxygen-awesome-css url = https://github.com/jothepro/doxygen-awesome-css.git -[submodule "lib/tlsf"] - path = lib/tlsf - url = https://github.com/espressif/tlsf diff --git a/.pvsoptions b/.pvsoptions index 590a34de8..8606eef15 100644 --- a/.pvsoptions +++ b/.pvsoptions @@ -1 +1 @@ ---ignore-ccache -C gccarm --rules-config .pvsconfig -e lib/cmsis_core -e lib/tlsf -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/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/* diff --git a/applications/debug/unit_tests/furi/furi_memmgr_test.c b/applications/debug/unit_tests/furi/furi_memmgr_test.c index 399e2d418..01e2c17f6 100644 --- a/applications/debug/unit_tests/furi/furi_memmgr_test.c +++ b/applications/debug/unit_tests/furi/furi_memmgr_test.c @@ -1,5 +1,8 @@ #include "../minunit.h" -#include +#include +#include +#include +#include void test_furi_memmgr(void) { void* ptr; @@ -34,260 +37,3 @@ void test_furi_memmgr(void) { } free(ptr); } - -static void test_memmgr_malloc(const size_t allocation_size) { - uint8_t* ptr = NULL; - const char* error_message = NULL; - - FURI_CRITICAL_ENTER(); - - ptr = malloc(allocation_size); - - // test that we can allocate memory - if(ptr == NULL) { - error_message = "malloc failed"; - } - - // test that memory is zero-initialized after allocation - for(size_t i = 0; i < allocation_size; i++) { - if(ptr[i] != 0) { - error_message = "memory is not zero-initialized after malloc"; - break; - } - } - memset(ptr, 0x55, allocation_size); - free(ptr); - - // test that memory is zero-initialized after free - // we know that allocator can use this memory for inner purposes - // so we check that memory at least partially zero-initialized - -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wuse-after-free" - - size_t zero_count = 0; - for(size_t i = 0; i < allocation_size; i++) { - if(ptr[i] == 0) { - zero_count++; - } - } - -#pragma GCC diagnostic pop - - // check that at least 75% of memory is zero-initialized - if(zero_count < (allocation_size * 0.75)) { - error_message = "seems that memory is not zero-initialized after free (malloc)"; - } - - FURI_CRITICAL_EXIT(); - - if(error_message != NULL) { - mu_fail(error_message); - } -} - -static void test_memmgr_realloc(const size_t allocation_size) { - uint8_t* ptr = NULL; - const char* error_message = NULL; - - FURI_CRITICAL_ENTER(); - - ptr = realloc(ptr, allocation_size); - - // test that we can allocate memory - if(ptr == NULL) { - error_message = "realloc(NULL) failed"; - } - - // test that memory is zero-initialized after allocation - for(size_t i = 0; i < allocation_size; i++) { - if(ptr[i] != 0) { - error_message = "memory is not zero-initialized after realloc(NULL)"; - break; - } - } - - memset(ptr, 0x55, allocation_size); - - ptr = realloc(ptr, allocation_size * 2); - - // test that we can reallocate memory - if(ptr == NULL) { - error_message = "realloc failed"; - } - - // test that memory content is preserved - for(size_t i = 0; i < allocation_size; i++) { - if(ptr[i] != 0x55) { - error_message = "memory is not reallocated after realloc"; - break; - } - } - - // test that remaining memory is zero-initialized - size_t non_zero_count = 0; - for(size_t i = allocation_size; i < allocation_size * 2; i++) { - if(ptr[i] != 0) { - non_zero_count += 1; - } - } - - // check that at most of memory is zero-initialized - // we know that allocator not always can restore content size from a pointer - // so we check against small threshold - if(non_zero_count > 4) { - error_message = "seems that memory is not zero-initialized after realloc"; - } - - uint8_t* null_ptr = realloc(ptr, 0); - - // test that we can free memory - if(null_ptr != NULL) { - error_message = "realloc(0) failed"; - } - - // test that memory is zero-initialized after realloc(0) - // we know that allocator can use this memory for inner purposes - // so we check that memory at least partially zero-initialized - -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wuse-after-free" - - size_t zero_count = 0; - for(size_t i = 0; i < allocation_size; i++) { - if(ptr[i] == 0) { - zero_count++; - } - } - -#pragma GCC diagnostic pop - - // check that at least 75% of memory is zero-initialized - if(zero_count < (allocation_size * 0.75)) { - error_message = "seems that memory is not zero-initialized after realloc(0)"; - } - - FURI_CRITICAL_EXIT(); - - if(error_message != NULL) { - mu_fail(error_message); - } -} - -static void test_memmgr_alloc_aligned(const size_t allocation_size, const size_t alignment) { - uint8_t* ptr = NULL; - const char* error_message = NULL; - - FURI_CRITICAL_ENTER(); - - ptr = aligned_alloc(alignment, allocation_size); - - // test that we can allocate memory - if(ptr == NULL) { - error_message = "aligned_alloc failed"; - } - - // test that memory is aligned - if(((uintptr_t)ptr % alignment) != 0) { - error_message = "memory is not aligned after aligned_alloc"; - } - - // test that memory is zero-initialized after allocation - for(size_t i = 0; i < allocation_size; i++) { - if(ptr[i] != 0) { - error_message = "memory is not zero-initialized after aligned_alloc"; - break; - } - } - memset(ptr, 0x55, allocation_size); - free(ptr); - - // test that memory is zero-initialized after free - // we know that allocator can use this memory for inner purposes - // so we check that memory at least partially zero-initialized - -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wuse-after-free" - - size_t zero_count = 0; - for(size_t i = 0; i < allocation_size; i++) { - if(ptr[i] == 0) { - zero_count++; - } - } - -#pragma GCC diagnostic pop - - // check that at least 75% of memory is zero-initialized - if(zero_count < (allocation_size * 0.75)) { - error_message = "seems that memory is not zero-initialized after free (aligned_alloc)"; - } - - FURI_CRITICAL_EXIT(); - - if(error_message != NULL) { - mu_fail(error_message); - } -} - -void test_furi_memmgr_advanced(void) { - const size_t sizes[] = {50, 100, 500, 1000, 5000, 10000}; - const size_t sizes_count = sizeof(sizes) / sizeof(sizes[0]); - const size_t alignments[] = {4, 8, 16, 32, 64, 128, 256, 512, 1024}; - const size_t alignments_count = sizeof(alignments) / sizeof(alignments[0]); - - // do test without memory fragmentation - { - for(size_t i = 0; i < sizes_count; i++) { - test_memmgr_malloc(sizes[i]); - } - - for(size_t i = 0; i < sizes_count; i++) { - test_memmgr_realloc(sizes[i]); - } - - for(size_t i = 0; i < sizes_count; i++) { - for(size_t j = 0; j < alignments_count; j++) { - test_memmgr_alloc_aligned(sizes[i], alignments[j]); - } - } - } - - // do test with memory fragmentation - { - void* blocks[sizes_count]; - void* guards[sizes_count - 1]; - - // setup guards - for(size_t i = 0; i < sizes_count; i++) { - blocks[i] = malloc(sizes[i]); - if(i < sizes_count - 1) { - guards[i] = malloc(sizes[i]); - } - } - - for(size_t i = 0; i < sizes_count; i++) { - free(blocks[i]); - } - - // do test - for(size_t i = 0; i < sizes_count; i++) { - test_memmgr_malloc(sizes[i]); - } - - for(size_t i = 0; i < sizes_count; i++) { - test_memmgr_realloc(sizes[i]); - } - - for(size_t i = 0; i < sizes_count; i++) { - for(size_t j = 0; j < alignments_count; j++) { - test_memmgr_alloc_aligned(sizes[i], alignments[j]); - } - } - - // cleanup guards - for(size_t i = 0; i < sizes_count - 1; i++) { - free(guards[i]); - } - } -} \ No newline at end of file diff --git a/applications/debug/unit_tests/furi/furi_test.c b/applications/debug/unit_tests/furi/furi_test.c index e0b5916d5..e287f9927 100644 --- a/applications/debug/unit_tests/furi/furi_test.c +++ b/applications/debug/unit_tests/furi/furi_test.c @@ -9,7 +9,6 @@ void test_furi_concurrent_access(void); void test_furi_pubsub(void); void test_furi_memmgr(void); -void test_furi_memmgr_advanced(void); static int foo = 0; @@ -38,7 +37,6 @@ MU_TEST(mu_test_furi_memmgr) { // this test is not accurate, but gives a basic understanding // that memory management is working fine test_furi_memmgr(); - test_furi_memmgr_advanced(); } MU_TEST_SUITE(test_suite) { diff --git a/applications/main/nfc/plugins/supported_cards/skylanders.c b/applications/main/nfc/plugins/supported_cards/skylanders.c index a60a5dba3..fca1c3185 100644 --- a/applications/main/nfc/plugins/supported_cards/skylanders.c +++ b/applications/main/nfc/plugins/supported_cards/skylanders.c @@ -92,6 +92,7 @@ static uint8_t fill_name(const uint16_t id, FuriString* name) { furi_string_cat_printf(name, "Lightning Rod"); break; case 0x0004: + case 0x0194: furi_string_cat_printf(name, "Bash"); break; case 0x0005: @@ -128,6 +129,7 @@ static uint8_t fill_name(const uint16_t id, FuriString* name) { furi_string_cat_printf(name, "Slam Bam"); break; case 0x0010: + case 0x01A0: furi_string_cat_printf(name, "Spyro"); break; case 0x0011: @@ -137,6 +139,7 @@ static uint8_t fill_name(const uint16_t id, FuriString* name) { furi_string_cat_printf(name, "Double Trouble"); break; case 0x0013: + case 0x01A3: furi_string_cat_printf(name, "Trigger Happy"); break; case 0x0014: @@ -170,6 +173,7 @@ static uint8_t fill_name(const uint16_t id, FuriString* name) { furi_string_cat_printf(name, "Hex"); break; case 0x001E: + case 0x01AE: furi_string_cat_printf(name, "Chop Chop"); break; case 0x001F: @@ -331,18 +335,6 @@ static uint8_t fill_name(const uint16_t id, FuriString* name) { case 0x0134: furi_string_cat_printf(name, "Midnight Museum"); break; - case 0x0194: - furi_string_cat_printf(name, "Bash"); - break; - case 0x01A0: - furi_string_cat_printf(name, "Spyro"); - break; - case 0x01A3: - furi_string_cat_printf(name, "Trigger Happy"); - break; - case 0x01AE: - furi_string_cat_printf(name, "Chop Chop"); - break; case 0x01C2: furi_string_cat_printf(name, "Gusto"); break; diff --git a/applications/services/cli/cli_commands.c b/applications/services/cli/cli_commands.c index 56d05785f..43f1c01c4 100644 --- a/applications/services/cli/cli_commands.c +++ b/applications/services/cli/cli_commands.c @@ -425,34 +425,8 @@ void cli_command_free(Cli* cli, FuriString* args, void* context) { printf("Minimum heap size: %zu\r\n", memmgr_get_minimum_free_heap()); printf("Maximum heap block: %zu\r\n", memmgr_heap_get_max_free_block()); - printf("Aux pool total free: %zu\r\n", memmgr_aux_pool_get_free()); - printf("Aux pool max free block: %zu\r\n", memmgr_pool_get_max_block()); -} - -typedef struct { - void* addr; - size_t size; -} FreeBlockInfo; - -#define FREE_BLOCK_INFO_MAX 128 - -typedef struct { - FreeBlockInfo free_blocks[FREE_BLOCK_INFO_MAX]; - size_t free_blocks_count; -} FreeBlockContext; - -static bool free_block_walker(void* pointer, size_t size, bool used, void* context) { - FreeBlockContext* free_blocks = (FreeBlockContext*)context; - if(!used) { - if(free_blocks->free_blocks_count < FREE_BLOCK_INFO_MAX) { - free_blocks->free_blocks[free_blocks->free_blocks_count].addr = pointer; - free_blocks->free_blocks[free_blocks->free_blocks_count].size = size; - free_blocks->free_blocks_count++; - } else { - return false; - } - } - return true; + printf("Pool free: %zu\r\n", memmgr_pool_get_free()); + printf("Maximum pool block: %zu\r\n", memmgr_pool_get_max_block()); } void cli_command_free_blocks(Cli* cli, FuriString* args, void* context) { @@ -460,23 +434,7 @@ void cli_command_free_blocks(Cli* cli, FuriString* args, void* context) { UNUSED(args); UNUSED(context); - FreeBlockContext* free_blocks = malloc(sizeof(FreeBlockContext)); - free_blocks->free_blocks_count = 0; - - memmgr_heap_walk_blocks(free_block_walker, free_blocks); - - for(size_t i = 0; i < free_blocks->free_blocks_count; i++) { - printf( - "A %p S %zu\r\n", - (void*)free_blocks->free_blocks[i].addr, - free_blocks->free_blocks[i].size); - } - - if(free_blocks->free_blocks_count == FREE_BLOCK_INFO_MAX) { - printf("... and more\r\n"); - } - - free(free_blocks); + memmgr_heap_printf_free_blocks(); } void cli_command_i2c(Cli* cli, FuriString* args, void* context) { diff --git a/furi/core/memmgr.c b/furi/core/memmgr.c index 768d44890..768adc05d 100644 --- a/furi/core/memmgr.c +++ b/furi/core/memmgr.c @@ -4,8 +4,6 @@ #include extern void* pvPortMalloc(size_t xSize); -extern void* pvPortAllocAligned(size_t xSize, size_t xAlignment); -extern void* pvPortRealloc(void* pv, size_t xSize); extern void vPortFree(void* pv); extern size_t xPortGetFreeHeapSize(void); extern size_t xPortGetTotalHeapSize(void); @@ -20,7 +18,18 @@ void free(void* ptr) { } void* realloc(void* ptr, size_t size) { - return pvPortRealloc(ptr, size); + if(size == 0) { + vPortFree(ptr); + return NULL; + } + + void* p = pvPortMalloc(size); + if(ptr != NULL) { + memcpy(p, ptr, size); + vPortFree(ptr); + } + + return p; } void* calloc(size_t count, size_t size) { @@ -38,10 +47,6 @@ char* strdup(const char* s) { return y; } -void* aligned_alloc(size_t alignment, size_t size) { - return pvPortAllocAligned(size, alignment); -} - size_t memmgr_get_free_heap(void) { return xPortGetFreeHeapSize(); } @@ -74,17 +79,33 @@ void* __wrap__realloc_r(struct _reent* r, void* ptr, size_t size) { return realloc(ptr, size); } -void* memmgr_aux_pool_alloc(size_t size) { +void* memmgr_alloc_from_pool(size_t size) { void* p = furi_hal_memory_alloc(size); if(p == NULL) p = malloc(size); return p; } -size_t memmgr_aux_pool_get_free(void) { +size_t memmgr_pool_get_free(void) { return furi_hal_memory_get_free(); } size_t memmgr_pool_get_max_block(void) { return furi_hal_memory_max_pool_block(); +} + +void* aligned_malloc(size_t size, size_t alignment) { + void* p1; // original block + void** p2; // aligned block + int offset = alignment - 1 + sizeof(void*); + if((p1 = (void*)malloc(size + offset)) == NULL) { + return NULL; + } + p2 = (void**)(((size_t)(p1) + offset) & ~(alignment - 1)); + p2[-1] = p1; + return p2; +} + +void aligned_free(void* p) { + free(((void**)p)[-1]); } \ No newline at end of file diff --git a/furi/core/memmgr.h b/furi/core/memmgr.h index 796a1f537..bc0c35faa 100644 --- a/furi/core/memmgr.h +++ b/furi/core/memmgr.h @@ -36,22 +36,37 @@ size_t memmgr_get_total_heap(void); size_t memmgr_get_minimum_free_heap(void); /** - * @brief Allocate memory from the auxiliary memory pool. That memory can't be freed. + * An aligned version of malloc, used when you need to get the aligned space on the heap + * Freeing the received address is performed ONLY through the aligned_free function + * @param size + * @param alignment + * @return void* + */ +void* aligned_malloc(size_t size, size_t alignment); + +/** + * Freed space obtained through the aligned_malloc function + * @param p pointer to result of aligned_malloc + */ +void aligned_free(void* p); + +/** + * @brief Allocate memory from separate memory pool. That memory can't be freed. * * @param size * @return void* */ -void* memmgr_aux_pool_alloc(size_t size); +void* memmgr_alloc_from_pool(size_t size); /** - * @brief Get the auxiliary pool free memory size + * @brief Get free memory pool size * * @return size_t */ -size_t memmgr_aux_pool_get_free(void); +size_t memmgr_pool_get_free(void); /** - * @brief Get max free block size from the auxiliary memory pool + * @brief Get max free block size from memory pool * * @return size_t */ diff --git a/furi/core/memmgr_heap.c b/furi/core/memmgr_heap.c index 3dfc7f5ac..3cee0d377 100644 --- a/furi/core/memmgr_heap.c +++ b/furi/core/memmgr_heap.c @@ -1,18 +1,124 @@ -#include -#include -#include +/* + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ + +/* + * A sample implementation of pvPortMalloc() and vPortFree() that combines + * (coalescences) adjacent memory blocks as they are freed, and in so doing + * limits memory fragmentation. + * + * See heap_1.c, heap_2.c and heap_3.c for alternative implementations, and the + * memory management pages of http://www.FreeRTOS.org for more information. + */ + +#include "memmgr_heap.h" +#include "check.h" +#include +#include +#include +#include +#include + +/* Defining MPU_WRAPPERS_INCLUDED_FROM_API_FILE prevents task.h from redefining +all the API functions to use the MPU wrappers. That should only be done when +task.h is included from an application file. */ +#define MPU_WRAPPERS_INCLUDED_FROM_API_FILE + #include #include -#include +#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE + +#ifdef HEAP_PRINT_DEBUG +#error This feature is broken, logging transport must be replaced with RTT +#endif + +#if(configSUPPORT_DYNAMIC_ALLOCATION == 0) +#error This file must not be used if configSUPPORT_DYNAMIC_ALLOCATION is 0 +#endif + +/* Block sizes must not get too small. */ +#define heapMINIMUM_BLOCK_SIZE ((size_t)(xHeapStructSize << 1)) + +/* Assumes 8bit bytes! */ +#define heapBITS_PER_BYTE ((size_t)8) + +/* Heap start end symbols provided by linker */ extern const void __heap_start__; extern const void __heap_end__; +uint8_t* ucHeap = (uint8_t*)&__heap_start__; -static tlsf_t tlsf = NULL; -static size_t heap_used = 0; -static size_t heap_max_used = 0; +/* Define the linked list structure. This is used to link free blocks in order +of their memory address. */ +typedef struct A_BLOCK_LINK { + struct A_BLOCK_LINK* pxNextFreeBlock; /*<< The next free block in the list. */ + size_t xBlockSize; /*<< The size of the free block. */ +} BlockLink_t; -// Allocation tracking types +/*-----------------------------------------------------------*/ + +/* + * Inserts a block of memory that is being freed into the correct position in + * the list of free memory blocks. The block being freed will be merged with + * the block in front it and/or the block behind it if the memory blocks are + * adjacent to each other. + */ +static void prvInsertBlockIntoFreeList(BlockLink_t* pxBlockToInsert); + +/* + * Called automatically to setup the required heap structures the first time + * pvPortMalloc() is called. + */ +static void prvHeapInit(void); + +/*-----------------------------------------------------------*/ + +/* The size of the structure placed at the beginning of each allocated memory +block must by correctly byte aligned. */ +static const size_t xHeapStructSize = (sizeof(BlockLink_t) + ((size_t)(portBYTE_ALIGNMENT - 1))) & + ~((size_t)portBYTE_ALIGNMENT_MASK); + +/* Create a couple of list links to mark the start and end of the list. */ +static BlockLink_t xStart, *pxEnd = NULL; + +/* Keeps track of the number of free bytes remaining, but says nothing about +fragmentation. */ +static size_t xFreeBytesRemaining = 0U; +static size_t xMinimumEverFreeBytesRemaining = 0U; + +/* Gets set to the top bit of an size_t type. When this bit in the xBlockSize +member of an BlockLink_t structure is set then the block belongs to the +application. When the bit is free the block is still part of the free heap +space. */ +static size_t xBlockAllocatedBit = 0; + +/* Furi heap extension */ +#include + +/* Allocation tracking types */ DICT_DEF2(MemmgrHeapAllocDict, uint32_t, uint32_t) //-V1048 DICT_DEF2( //-V1048 @@ -22,35 +128,17 @@ DICT_DEF2( //-V1048 MemmgrHeapAllocDict_t, DICT_OPLIST(MemmgrHeapAllocDict)) -// Thread allocation tracing storage +/* Thread allocation tracing storage */ static MemmgrHeapThreadDict_t memmgr_heap_thread_dict = {0}; static volatile uint32_t memmgr_heap_thread_trace_depth = 0; -static inline void memmgr_lock(void) { - vTaskSuspendAll(); -} - -static inline void memmgr_unlock(void) { - xTaskResumeAll(); -} - -static inline size_t memmgr_get_heap_size(void) { - return (size_t)&__heap_end__ - (size_t)&__heap_start__; -} - -// Initialize tracing storage -static void memmgr_heap_init(void) { +/* Initialize tracing storage on start */ +void memmgr_heap_init(void) { MemmgrHeapThreadDict_init(memmgr_heap_thread_dict); } -__attribute__((constructor)) static void memmgr_init(void) { - size_t pool_size = (size_t)&__heap_end__ - (size_t)&__heap_start__; - tlsf = tlsf_create_with_pool((void*)&__heap_start__, pool_size, pool_size); - memmgr_heap_init(); -} - void memmgr_heap_enable_thread_trace(FuriThreadId thread_id) { - memmgr_lock(); + vTaskSuspendAll(); { memmgr_heap_thread_trace_depth++; furi_check(MemmgrHeapThreadDict_get(memmgr_heap_thread_dict, (uint32_t)thread_id) == NULL); @@ -60,20 +148,53 @@ void memmgr_heap_enable_thread_trace(FuriThreadId thread_id) { MemmgrHeapAllocDict_clear(alloc_dict); memmgr_heap_thread_trace_depth--; } - memmgr_unlock(); + (void)xTaskResumeAll(); } void memmgr_heap_disable_thread_trace(FuriThreadId thread_id) { - memmgr_lock(); + vTaskSuspendAll(); { memmgr_heap_thread_trace_depth++; furi_check(MemmgrHeapThreadDict_erase(memmgr_heap_thread_dict, (uint32_t)thread_id)); memmgr_heap_thread_trace_depth--; } - memmgr_unlock(); + (void)xTaskResumeAll(); } -static inline void memmgr_heap_trace_malloc(void* pointer, size_t size) { +size_t memmgr_heap_get_thread_memory(FuriThreadId thread_id) { + size_t leftovers = MEMMGR_HEAP_UNKNOWN; + vTaskSuspendAll(); + { + memmgr_heap_thread_trace_depth++; + MemmgrHeapAllocDict_t* alloc_dict = + MemmgrHeapThreadDict_get(memmgr_heap_thread_dict, (uint32_t)thread_id); + if(alloc_dict) { + leftovers = 0; + MemmgrHeapAllocDict_it_t alloc_dict_it; + for(MemmgrHeapAllocDict_it(alloc_dict_it, *alloc_dict); + !MemmgrHeapAllocDict_end_p(alloc_dict_it); + MemmgrHeapAllocDict_next(alloc_dict_it)) { + MemmgrHeapAllocDict_itref_t* data = MemmgrHeapAllocDict_ref(alloc_dict_it); + if(data->key != 0) { + uint8_t* puc = (uint8_t*)data->key; + puc -= xHeapStructSize; + BlockLink_t* pxLink = (void*)puc; + + if((pxLink->xBlockSize & xBlockAllocatedBit) != 0 && + pxLink->pxNextFreeBlock == NULL) { + leftovers += data->value; + } + } + } + } + memmgr_heap_thread_trace_depth--; + } + (void)xTaskResumeAll(); + return leftovers; +} + +#undef traceMALLOC +static inline void traceMALLOC(void* pointer, size_t size) { FuriThreadId thread_id = furi_thread_get_current_id(); if(thread_id && memmgr_heap_thread_trace_depth == 0) { memmgr_heap_thread_trace_depth++; @@ -86,7 +207,9 @@ static inline void memmgr_heap_trace_malloc(void* pointer, size_t size) { } } -static inline void memmgr_heap_trace_free(void* pointer) { +#undef traceFREE +static inline void traceFREE(void* pointer, size_t size) { + UNUSED(size); FuriThreadId thread_id = furi_thread_get_current_id(); if(thread_id && memmgr_heap_thread_trace_depth == 0) { memmgr_heap_thread_trace_depth++; @@ -101,248 +224,441 @@ static inline void memmgr_heap_trace_free(void* pointer) { } } -size_t memmgr_heap_get_thread_memory(FuriThreadId thread_id) { - size_t leftovers = MEMMGR_HEAP_UNKNOWN; - memmgr_lock(); - { - memmgr_heap_thread_trace_depth++; - MemmgrHeapAllocDict_t* alloc_dict = - MemmgrHeapThreadDict_get(memmgr_heap_thread_dict, (uint32_t)thread_id); - if(alloc_dict) { - leftovers = 0; - MemmgrHeapAllocDict_it_t alloc_dict_it; - for(MemmgrHeapAllocDict_it(alloc_dict_it, *alloc_dict); - !MemmgrHeapAllocDict_end_p(alloc_dict_it); - MemmgrHeapAllocDict_next(alloc_dict_it)) { - MemmgrHeapAllocDict_itref_t* data = MemmgrHeapAllocDict_ref(alloc_dict_it); - if(data->key != 0) { - block_header_t* block = block_from_ptr((uint8_t*)data->key); - if(!block_is_free(block)) { - leftovers += data->value; - } - } - } - } - memmgr_heap_thread_trace_depth--; - } - memmgr_unlock(); - return leftovers; -} - -static bool tlsf_walker_max_free(void* ptr, size_t size, int used, void* user) { - UNUSED(ptr); - - bool free = !used; - size_t* max_free_block_size = (size_t*)user; - if(free && size > *max_free_block_size) { - *max_free_block_size = size; - } - - return true; -} - size_t memmgr_heap_get_max_free_block(void) { - size_t max_free_block_size = 0; + size_t max_free_size = 0; + BlockLink_t* pxBlock; + vTaskSuspendAll(); - memmgr_lock(); + pxBlock = xStart.pxNextFreeBlock; + while(pxBlock->pxNextFreeBlock != NULL) { + if(pxBlock->xBlockSize > max_free_size) { + max_free_size = pxBlock->xBlockSize; + } + pxBlock = pxBlock->pxNextFreeBlock; + } - pool_t pool = tlsf_get_pool(tlsf); - tlsf_walk_pool(pool, tlsf_walker_max_free, &max_free_block_size); - - memmgr_unlock(); - - return max_free_block_size; + xTaskResumeAll(); + return max_free_size; } -typedef struct { - BlockWalker walker; - void* context; -} BlockWalkerWrapper; +void memmgr_heap_printf_free_blocks(void) { + BlockLink_t* pxBlock; + //can be enabled once we can do printf with a locked scheduler + //vTaskSuspendAll(); -static bool tlsf_walker_wrapper(void* ptr, size_t size, int used, void* user) { - BlockWalkerWrapper* wrapper = (BlockWalkerWrapper*)user; - return wrapper->walker(ptr, size, used, wrapper->context); + pxBlock = xStart.pxNextFreeBlock; + while(pxBlock->pxNextFreeBlock != NULL) { + printf("A %p S %lu\r\n", (void*)pxBlock, (uint32_t)pxBlock->xBlockSize); + pxBlock = pxBlock->pxNextFreeBlock; + } + + //xTaskResumeAll(); } -void memmgr_heap_walk_blocks(BlockWalker walker, void* context) { - memmgr_lock(); +#ifdef HEAP_PRINT_DEBUG +char* ultoa(unsigned long num, char* str, int radix) { + char temp[33]; // at radix 2 the string is at most 32 + 1 null long. + int temp_loc = 0; + int digit; + int str_loc = 0; - BlockWalkerWrapper wrapper = {walker, context}; - pool_t pool = tlsf_get_pool(tlsf); - tlsf_walk_pool(pool, tlsf_walker_wrapper, &wrapper); + //construct a backward string of the number. + do { + digit = (unsigned long)num % ((unsigned long)radix); + if(digit < 10) + temp[temp_loc++] = digit + '0'; + else + temp[temp_loc++] = digit - 10 + 'A'; + num = ((unsigned long)num) / ((unsigned long)radix); + } while((unsigned long)num > 0); - memmgr_unlock(); + temp_loc--; + + //now reverse the string. + while(temp_loc >= 0) { // while there are still chars + str[str_loc++] = temp[temp_loc--]; + } + str[str_loc] = 0; // add null termination. + + return str; } -void* pvPortMalloc(size_t xSize) { - // memory management in ISR is not allowed +static void print_heap_init(void) { + char tmp_str[33]; + size_t heap_start = (size_t)&__heap_start__; + size_t heap_end = (size_t)&__heap_end__; + + // {PHStart|heap_start|heap_end} + FURI_CRITICAL_ENTER(); + furi_log_puts("{PHStart|"); + ultoa(heap_start, tmp_str, 16); + furi_log_puts(tmp_str); + furi_log_puts("|"); + ultoa(heap_end, tmp_str, 16); + furi_log_puts(tmp_str); + furi_log_puts("}\r\n"); + FURI_CRITICAL_EXIT(); +} + +static void print_heap_malloc(void* ptr, size_t size) { + char tmp_str[33]; + const char* name = furi_thread_get_name(furi_thread_get_current_id()); + if(!name) { + name = ""; + } + + // {thread name|m|address|size} + FURI_CRITICAL_ENTER(); + furi_log_puts("{"); + furi_log_puts(name); + furi_log_puts("|m|0x"); + ultoa((unsigned long)ptr, tmp_str, 16); + furi_log_puts(tmp_str); + furi_log_puts("|"); + utoa(size, tmp_str, 10); + furi_log_puts(tmp_str); + furi_log_puts("}\r\n"); + FURI_CRITICAL_EXIT(); +} + +static void print_heap_free(void* ptr) { + char tmp_str[33]; + const char* name = furi_thread_get_name(furi_thread_get_current_id()); + if(!name) { + name = ""; + } + + // {thread name|f|address} + FURI_CRITICAL_ENTER(); + furi_log_puts("{"); + furi_log_puts(name); + furi_log_puts("|f|0x"); + ultoa((unsigned long)ptr, tmp_str, 16); + furi_log_puts(tmp_str); + furi_log_puts("}\r\n"); + FURI_CRITICAL_EXIT(); +} +#endif +/*-----------------------------------------------------------*/ + +void* pvPortMalloc(size_t xWantedSize) { + BlockLink_t *pxBlock, *pxPreviousBlock, *pxNewBlockLink; + void* pvReturn = NULL; + size_t to_wipe = xWantedSize; + if(FURI_IS_IRQ_MODE()) { furi_crash("memmgt in ISR"); } - memmgr_lock(); +#ifdef HEAP_PRINT_DEBUG + BlockLink_t* print_heap_block = NULL; +#endif - // allocate block - void* data = tlsf_malloc(tlsf, xSize); - if(data == NULL) { - if(xSize == 0) { - furi_crash("malloc(0)"); + /* If this is the first call to malloc then the heap will require + initialisation to setup the list of free blocks. */ + if(pxEnd == NULL) { +#ifdef HEAP_PRINT_DEBUG + print_heap_init(); +#endif + + vTaskSuspendAll(); + { + prvHeapInit(); + memmgr_heap_init(); + } + (void)xTaskResumeAll(); + } else { + mtCOVERAGE_TEST_MARKER(); + } + + vTaskSuspendAll(); + { + /* Check the requested block size is not so large that the top bit is + set. The top bit of the block size member of the BlockLink_t structure + is used to determine who owns the block - the application or the + kernel, so it must be free. */ + if((xWantedSize & xBlockAllocatedBit) == 0) { + /* The wanted size is increased so it can contain a BlockLink_t + structure in addition to the requested amount of bytes. */ + if(xWantedSize > 0) { + xWantedSize += xHeapStructSize; + + /* Ensure that blocks are always aligned to the required number + of bytes. */ + if((xWantedSize & portBYTE_ALIGNMENT_MASK) != 0x00) { + /* Byte alignment required. */ + xWantedSize += (portBYTE_ALIGNMENT - (xWantedSize & portBYTE_ALIGNMENT_MASK)); + configASSERT((xWantedSize & portBYTE_ALIGNMENT_MASK) == 0); + } else { + mtCOVERAGE_TEST_MARKER(); + } + } else { + mtCOVERAGE_TEST_MARKER(); + } + + if((xWantedSize > 0) && (xWantedSize <= xFreeBytesRemaining)) { + /* Traverse the list from the start (lowest address) block until + one of adequate size is found. */ + pxPreviousBlock = &xStart; + pxBlock = xStart.pxNextFreeBlock; + while((pxBlock->xBlockSize < xWantedSize) && (pxBlock->pxNextFreeBlock != NULL)) { + pxPreviousBlock = pxBlock; + pxBlock = pxBlock->pxNextFreeBlock; + } + + /* If the end marker was reached then a block of adequate size + was not found. */ + if(pxBlock != pxEnd) { + /* Return the memory space pointed to - jumping over the + BlockLink_t structure at its start. */ + pvReturn = + (void*)(((uint8_t*)pxPreviousBlock->pxNextFreeBlock) + xHeapStructSize); + + /* This block is being returned for use so must be taken out + of the list of free blocks. */ + pxPreviousBlock->pxNextFreeBlock = pxBlock->pxNextFreeBlock; + + /* If the block is larger than required it can be split into + two. */ + if((pxBlock->xBlockSize - xWantedSize) > heapMINIMUM_BLOCK_SIZE) { + /* This block is to be split into two. Create a new + block following the number of bytes requested. The void + cast is used to prevent byte alignment warnings from the + compiler. */ + pxNewBlockLink = (void*)(((uint8_t*)pxBlock) + xWantedSize); + configASSERT((((size_t)pxNewBlockLink) & portBYTE_ALIGNMENT_MASK) == 0); + + /* Calculate the sizes of two blocks split from the + single block. */ + pxNewBlockLink->xBlockSize = pxBlock->xBlockSize - xWantedSize; + pxBlock->xBlockSize = xWantedSize; + + /* Insert the new block into the list of free blocks. */ + prvInsertBlockIntoFreeList(pxNewBlockLink); + } else { + mtCOVERAGE_TEST_MARKER(); + } + + xFreeBytesRemaining -= pxBlock->xBlockSize; + + if(xFreeBytesRemaining < xMinimumEverFreeBytesRemaining) { + xMinimumEverFreeBytesRemaining = xFreeBytesRemaining; + } else { + mtCOVERAGE_TEST_MARKER(); + } + + /* The block is being returned - it is allocated and owned + by the application and has no "next" block. */ + pxBlock->xBlockSize |= xBlockAllocatedBit; + pxBlock->pxNextFreeBlock = NULL; + +#ifdef HEAP_PRINT_DEBUG + print_heap_block = pxBlock; +#endif + } else { + mtCOVERAGE_TEST_MARKER(); + } + } else { + mtCOVERAGE_TEST_MARKER(); + } } else { - furi_crash("out of memory"); + mtCOVERAGE_TEST_MARKER(); + } + + traceMALLOC(pvReturn, xWantedSize); + } + (void)xTaskResumeAll(); + +#ifdef HEAP_PRINT_DEBUG + print_heap_malloc(print_heap_block, print_heap_block->xBlockSize & ~xBlockAllocatedBit); +#endif + +#if(configUSE_MALLOC_FAILED_HOOK == 1) + { + if(pvReturn == NULL) { + extern void vApplicationMallocFailedHook(void); + vApplicationMallocFailedHook(); + } else { + mtCOVERAGE_TEST_MARKER(); } } +#endif - // update heap usage - heap_used += tlsf_block_size(data); - heap_used += tlsf_alloc_overhead(); - if(heap_used > heap_max_used) { - heap_max_used = heap_used; - } + configASSERT((((size_t)pvReturn) & (size_t)portBYTE_ALIGNMENT_MASK) == 0); - // trace allocation - memmgr_heap_trace_malloc(data, xSize); - - memmgr_unlock(); - - // clear block content - memset(data, 0, xSize); - - return data; + furi_check(pvReturn, xWantedSize ? "out of memory" : "malloc(0)"); + pvReturn = memset(pvReturn, 0, to_wipe); + return pvReturn; } +/*-----------------------------------------------------------*/ void vPortFree(void* pv) { - // memory management in ISR is not allowed + uint8_t* puc = (uint8_t*)pv; + BlockLink_t* pxLink; + if(FURI_IS_IRQ_MODE()) { furi_crash("memmgt in ISR"); } - // ignore NULL pointer if(pv != NULL) { - memmgr_lock(); + /* The memory being freed will have an BlockLink_t structure immediately + before it. */ + puc -= xHeapStructSize; - // get block size - size_t block_size = tlsf_block_size(pv); + /* This casting is to keep the compiler from issuing warnings. */ + pxLink = (void*)puc; - // clear block content - memset(pv, 0, block_size); + /* Check the block is actually allocated. */ + configASSERT((pxLink->xBlockSize & xBlockAllocatedBit) != 0); + configASSERT(pxLink->pxNextFreeBlock == NULL); - // update heap usage - heap_used -= block_size; - heap_used -= tlsf_alloc_overhead(); + if((pxLink->xBlockSize & xBlockAllocatedBit) != 0) { + if(pxLink->pxNextFreeBlock == NULL) { + /* The block is being returned to the heap - it is no longer + allocated. */ + pxLink->xBlockSize &= ~xBlockAllocatedBit; - // free - tlsf_free(tlsf, pv); +#ifdef HEAP_PRINT_DEBUG + print_heap_free(pxLink); +#endif - // trace free - memmgr_heap_trace_free(pv); + vTaskSuspendAll(); + { + furi_assert((size_t)pv >= SRAM_BASE); + furi_assert((size_t)pv < SRAM_BASE + 1024 * 256); + furi_assert(pxLink->xBlockSize >= xHeapStructSize); + furi_assert((pxLink->xBlockSize - xHeapStructSize) < 1024 * 256); - memmgr_unlock(); - } -} - -extern void* pvPortAllocAligned(size_t xSize, size_t xAlignment) { - // memory management in ISR is not allowed - if(FURI_IS_IRQ_MODE()) { - furi_crash("memmgt in ISR"); - } - - // alignment must be power of 2 - if((xAlignment & (xAlignment - 1)) != 0) { - furi_crash("invalid alignment"); - } - - memmgr_lock(); - - // allocate block - void* data = tlsf_memalign(tlsf, xAlignment, xSize); - if(data == NULL) { - if(xSize == 0) { - furi_crash("malloc_aligned(0)"); + /* Add this block to the list of free blocks. */ + xFreeBytesRemaining += pxLink->xBlockSize; + traceFREE(pv, pxLink->xBlockSize); + memset(pv, 0, pxLink->xBlockSize - xHeapStructSize); + prvInsertBlockIntoFreeList(((BlockLink_t*)pxLink)); + } + (void)xTaskResumeAll(); + } else { + mtCOVERAGE_TEST_MARKER(); + } } else { - furi_crash("out of memory"); + mtCOVERAGE_TEST_MARKER(); } + } else { +#ifdef HEAP_PRINT_DEBUG + print_heap_free(pv); +#endif } - - // update heap usage - heap_used += tlsf_block_size(data); - heap_used += tlsf_alloc_overhead(); - if(heap_used > heap_max_used) { - heap_max_used = heap_used; - } - - // trace allocation - memmgr_heap_trace_malloc(data, xSize); - - memmgr_unlock(); - - // clear block content - memset(data, 0, xSize); - - return data; -} - -extern void* pvPortRealloc(void* pv, size_t xSize) { - // realloc(ptr, 0) is equivalent to free(ptr) - if(xSize == 0) { - vPortFree(pv); - return NULL; - } - - // realloc(NULL, size) is equivalent to malloc(size) - if(pv == NULL) { - return pvPortMalloc(xSize); - } - - /* realloc things */ - - // memory management in ISR is not allowed - if(FURI_IS_IRQ_MODE()) { - furi_crash("memmgt in ISR"); - } - - memmgr_lock(); - - // trace old block as free - size_t old_size = tlsf_block_size(pv); - - // trace free - memmgr_heap_trace_free(pv); - - // reallocate block - void* data = tlsf_realloc(tlsf, pv, xSize); - if(data == NULL) { - furi_crash("out of memory"); - } - - // update heap usage - heap_used -= old_size; - heap_used += tlsf_block_size(data); - if(heap_used > heap_max_used) { - heap_max_used = heap_used; - } - - // trace allocation - memmgr_heap_trace_malloc(data, xSize); - - memmgr_unlock(); - - // clear remain block content, if the new size is bigger - // can't guarantee that all data will be zeroed, cos tlsf_block_size is not always the same as xSize - if(xSize > old_size) { - memset((uint8_t*)data + old_size, 0, xSize - old_size); - } - - return data; -} - -size_t xPortGetFreeHeapSize(void) { - return memmgr_get_heap_size() - heap_used - tlsf_size(tlsf); } +/*-----------------------------------------------------------*/ size_t xPortGetTotalHeapSize(void) { - return memmgr_get_heap_size(); + return (size_t)&__heap_end__ - (size_t)&__heap_start__; } +/*-----------------------------------------------------------*/ + +size_t xPortGetFreeHeapSize(void) { + return xFreeBytesRemaining; +} +/*-----------------------------------------------------------*/ size_t xPortGetMinimumEverFreeHeapSize(void) { - return memmgr_get_heap_size() - heap_max_used - tlsf_size(tlsf); -} \ No newline at end of file + return xMinimumEverFreeBytesRemaining; +} +/*-----------------------------------------------------------*/ + +void vPortInitialiseBlocks(void) { + /* This just exists to keep the linker quiet. */ +} +/*-----------------------------------------------------------*/ + +static void prvHeapInit(void) { + BlockLink_t* pxFirstFreeBlock; + uint8_t* pucAlignedHeap; + size_t uxAddress; + size_t xTotalHeapSize = (size_t)&__heap_end__ - (size_t)&__heap_start__; + + /* Ensure the heap starts on a correctly aligned boundary. */ + uxAddress = (size_t)ucHeap; + + if((uxAddress & portBYTE_ALIGNMENT_MASK) != 0) { + uxAddress += (portBYTE_ALIGNMENT - 1); + uxAddress &= ~((size_t)portBYTE_ALIGNMENT_MASK); + xTotalHeapSize -= uxAddress - (size_t)ucHeap; + } + + pucAlignedHeap = (uint8_t*)uxAddress; + + /* xStart is used to hold a pointer to the first item in the list of free + blocks. The void cast is used to prevent compiler warnings. */ + xStart.pxNextFreeBlock = (void*)pucAlignedHeap; + xStart.xBlockSize = (size_t)0; + + /* pxEnd is used to mark the end of the list of free blocks and is inserted + at the end of the heap space. */ + uxAddress = ((size_t)pucAlignedHeap) + xTotalHeapSize; + uxAddress -= xHeapStructSize; + uxAddress &= ~((size_t)portBYTE_ALIGNMENT_MASK); + pxEnd = (void*)uxAddress; + pxEnd->xBlockSize = 0; + pxEnd->pxNextFreeBlock = NULL; + + /* To start with there is a single free block that is sized to take up the + entire heap space, minus the space taken by pxEnd. */ + pxFirstFreeBlock = (void*)pucAlignedHeap; + pxFirstFreeBlock->xBlockSize = uxAddress - (size_t)pxFirstFreeBlock; + pxFirstFreeBlock->pxNextFreeBlock = pxEnd; + + /* Only one block exists - and it covers the entire usable heap space. */ + xMinimumEverFreeBytesRemaining = pxFirstFreeBlock->xBlockSize; + xFreeBytesRemaining = pxFirstFreeBlock->xBlockSize; + + /* Work out the position of the top bit in a size_t variable. */ + xBlockAllocatedBit = ((size_t)1) << ((sizeof(size_t) * heapBITS_PER_BYTE) - 1); +} +/*-----------------------------------------------------------*/ + +static void prvInsertBlockIntoFreeList(BlockLink_t* pxBlockToInsert) { + BlockLink_t* pxIterator; + uint8_t* puc; + + /* Iterate through the list until a block is found that has a higher address + than the block being inserted. */ + for(pxIterator = &xStart; pxIterator->pxNextFreeBlock < pxBlockToInsert; + pxIterator = pxIterator->pxNextFreeBlock) { + /* Nothing to do here, just iterate to the right position. */ + } + + /* Do the block being inserted, and the block it is being inserted after + make a contiguous block of memory? */ + puc = (uint8_t*)pxIterator; + if((puc + pxIterator->xBlockSize) == (uint8_t*)pxBlockToInsert) { + pxIterator->xBlockSize += pxBlockToInsert->xBlockSize; + pxBlockToInsert = pxIterator; + } else { + mtCOVERAGE_TEST_MARKER(); + } + + /* Do the block being inserted, and the block it is being inserted before + make a contiguous block of memory? */ + puc = (uint8_t*)pxBlockToInsert; + if((puc + pxBlockToInsert->xBlockSize) == (uint8_t*)pxIterator->pxNextFreeBlock) { + if(pxIterator->pxNextFreeBlock != pxEnd) { + /* Form one big block from the two blocks. */ + pxBlockToInsert->xBlockSize += pxIterator->pxNextFreeBlock->xBlockSize; + pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock->pxNextFreeBlock; + } else { + pxBlockToInsert->pxNextFreeBlock = pxEnd; + } + } else { + pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock; + } + + /* If the block being inserted plugged a gab, so was merged with the block + before and the block after, then it's pxNextFreeBlock pointer will have + already been set, and should not be set here as that would make it point + to itself. */ + if(pxIterator != pxBlockToInsert) { + pxIterator->pxNextFreeBlock = pxBlockToInsert; + } else { + mtCOVERAGE_TEST_MARKER(); + } +} diff --git a/furi/core/memmgr_heap.h b/furi/core/memmgr_heap.h index 2f61deb64..7d889f152 100644 --- a/furi/core/memmgr_heap.h +++ b/furi/core/memmgr_heap.h @@ -40,17 +40,9 @@ size_t memmgr_heap_get_thread_memory(FuriThreadId thread_id); */ size_t memmgr_heap_get_max_free_block(void); -typedef bool (*BlockWalker)(void* pointer, size_t size, bool used, void* context); - -/** - * @brief Walk through all heap blocks - * @warning This function will lock memory manager and may cause deadlocks if any malloc/free is called inside the callback. - * Also, printf and furi_log contains malloc calls, so do not use them. - * - * @param walker - * @param context +/** Print the address and size of all free blocks to stdout */ -void memmgr_heap_walk_blocks(BlockWalker walker, void* context); +void memmgr_heap_printf_free_blocks(void); #ifdef __cplusplus } diff --git a/furi/core/thread.c b/furi/core/thread.c index c9bf79d32..f9f73b4f7 100644 --- a/furi/core/thread.c +++ b/furi/core/thread.c @@ -276,8 +276,8 @@ void furi_thread_start(FuriThread* thread) { stack, thread, priority, - memmgr_aux_pool_alloc(sizeof(StackType_t) * stack), - memmgr_aux_pool_alloc(sizeof(StaticTask_t))); + memmgr_alloc_from_pool(sizeof(StackType_t) * stack), + memmgr_alloc_from_pool(sizeof(StaticTask_t))); } else { BaseType_t ret = xTaskCreate( furi_thread_body, thread->name, stack, thread, priority, &thread->task_handle); diff --git a/furi/flipper.c b/furi/flipper.c index 6c7b9831a..c7ba3b4fb 100644 --- a/furi/flipper.c +++ b/furi/flipper.c @@ -51,17 +51,12 @@ void flipper_init(void) { FURI_LOG_I(TAG, "Startup complete"); } -PLACE_IN_SECTION("MB_MEM2") static StaticTask_t idle_task_tcb; -PLACE_IN_SECTION("MB_MEM2") static StackType_t idle_task_stack[configIDLE_TASK_STACK_DEPTH]; -PLACE_IN_SECTION("MB_MEM2") static StaticTask_t timer_task_tcb; -PLACE_IN_SECTION("MB_MEM2") static StackType_t timer_task_stack[configTIMER_TASK_STACK_DEPTH]; - void vApplicationGetIdleTaskMemory( StaticTask_t** tcb_ptr, StackType_t** stack_ptr, uint32_t* stack_size) { - *tcb_ptr = &idle_task_tcb; - *stack_ptr = idle_task_stack; + *tcb_ptr = memmgr_alloc_from_pool(sizeof(StaticTask_t)); + *stack_ptr = memmgr_alloc_from_pool(sizeof(StackType_t) * configIDLE_TASK_STACK_DEPTH); *stack_size = configIDLE_TASK_STACK_DEPTH; } @@ -69,7 +64,7 @@ void vApplicationGetTimerTaskMemory( StaticTask_t** tcb_ptr, StackType_t** stack_ptr, uint32_t* stack_size) { - *tcb_ptr = &timer_task_tcb; - *stack_ptr = timer_task_stack; + *tcb_ptr = memmgr_alloc_from_pool(sizeof(StaticTask_t)); + *stack_ptr = memmgr_alloc_from_pool(sizeof(StackType_t) * configTIMER_TASK_STACK_DEPTH); *stack_size = configTIMER_TASK_STACK_DEPTH; } \ No newline at end of file diff --git a/lib/SConscript b/lib/SConscript index 29c48de6d..812573932 100644 --- a/lib/SConscript +++ b/lib/SConscript @@ -13,7 +13,6 @@ env.Append( libs = env.BuildModules( [ - "tlsf", "mlib", "stm32wb", "freertos", diff --git a/lib/flipper_application/elf/elf_file.c b/lib/flipper_application/elf/elf_file.c index 3e10ae3fe..398f25209 100644 --- a/lib/flipper_application/elf/elf_file.c +++ b/lib/flipper_application/elf/elf_file.c @@ -466,7 +466,7 @@ static bool elf_load_section_data(ELFFile* elf, ELFSection* section, Elf32_Shdr* return true; } - section->data = aligned_alloc(section_header->sh_addralign, section_header->sh_size); + section->data = aligned_malloc(section_header->sh_size, section_header->sh_addralign); section->size = section_header->sh_size; if(section_header->sh_type == SHT_NOBITS) { @@ -718,7 +718,7 @@ static bool elf_relocate_fast(ELFFile* elf, ELFSection* s) { } } - free(s->fast_rel->data); + aligned_free(s->fast_rel->data); free(s->fast_rel); s->fast_rel = NULL; @@ -785,10 +785,10 @@ void elf_file_free(ELFFile* elf) { ELFSectionDict_next(it)) { const ELFSectionDict_itref_t* itref = ELFSectionDict_cref(it); if(itref->value.data) { - free(itref->value.data); + aligned_free(itref->value.data); } if(itref->value.fast_rel) { - free(itref->value.fast_rel->data); + aligned_free(itref->value.fast_rel->data); free(itref->value.fast_rel); } free((void*)itref->key); diff --git a/lib/tlsf b/lib/tlsf deleted file mode 160000 index 8fc595fe2..000000000 --- a/lib/tlsf +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 8fc595fe223cd0b3b5d7b29eb86825e4bd38e6e8 diff --git a/lib/tlsf.scons b/lib/tlsf.scons deleted file mode 100644 index 0a8419dbd..000000000 --- a/lib/tlsf.scons +++ /dev/null @@ -1,21 +0,0 @@ -Import("env") - -env.Append( - CPPPATH=[ - "#/lib/tlsf", - ], -) - - -libenv = env.Clone(FW_LIB_NAME="tlsf") -libenv.ApplyLibFlags() - -libenv.Append( - CPPDEFINES=[], -) - -sources = [File("tlsf/tlsf.c")] - -lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources) -libenv.Install("${LIB_DIST_DIR}", lib) -Return("lib") diff --git a/targets/f18/api_symbols.csv b/targets/f18/api_symbols.csv index ef2d2fcb6..492539d46 100644 --- a/targets/f18/api_symbols.csv +++ b/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,62.0,, +Version,+,61.3,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -515,7 +515,9 @@ Function,-,acosh,double,double Function,-,acoshf,float,float Function,-,acoshl,long double,long double Function,-,acosl,long double,long double -Function,+,aligned_alloc,void*,"size_t, size_t" +Function,-,aligned_alloc,void*,"size_t, size_t" +Function,+,aligned_free,void,void* +Function,+,aligned_malloc,void*,"size_t, size_t" Function,-,arc4random,__uint32_t, Function,-,arc4random_buf,void,"void*, size_t" Function,-,arc4random_uniform,__uint32_t,__uint32_t @@ -1982,8 +1984,7 @@ Function,+,memchr,void*,"const void*, int, size_t" Function,+,memcmp,int,"const void*, const void*, size_t" Function,+,memcpy,void*,"void*, const void*, size_t" Function,-,memmem,void*,"const void*, size_t, const void*, size_t" -Function,+,memmgr_aux_pool_alloc,void*,size_t -Function,+,memmgr_aux_pool_get_free,size_t, +Function,-,memmgr_alloc_from_pool,void*,size_t Function,+,memmgr_get_free_heap,size_t, Function,+,memmgr_get_minimum_free_heap,size_t, Function,+,memmgr_get_total_heap,size_t, @@ -1991,7 +1992,8 @@ Function,+,memmgr_heap_disable_thread_trace,void,FuriThreadId Function,+,memmgr_heap_enable_thread_trace,void,FuriThreadId Function,+,memmgr_heap_get_max_free_block,size_t, Function,+,memmgr_heap_get_thread_memory,size_t,FuriThreadId -Function,+,memmgr_heap_walk_blocks,void,"BlockWalker, void*" +Function,+,memmgr_heap_printf_free_blocks,void, +Function,-,memmgr_pool_get_free,size_t, Function,-,memmgr_pool_get_max_block,size_t, Function,+,memmove,void*,"void*, const void*, size_t" Function,-,mempcpy,void*,"void*, const void*, size_t" diff --git a/targets/f18/target.json b/targets/f18/target.json index a61c1373e..43e9254cd 100644 --- a/targets/f18/target.json +++ b/targets/f18/target.json @@ -13,7 +13,6 @@ "print", "flipper18", "furi", - "tlsf", "freertos", "stm32wb", "hwdrivers", @@ -69,4 +68,4 @@ "ibutton", "infrared" ] -} \ No newline at end of file +} diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 1f959f0c7..e209023b5 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,62.0,, +Version,+,61.3,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, @@ -586,7 +586,9 @@ Function,-,acosh,double,double Function,-,acoshf,float,float Function,-,acoshl,long double,long double Function,-,acosl,long double,long double -Function,+,aligned_alloc,void*,"size_t, size_t" +Function,-,aligned_alloc,void*,"size_t, size_t" +Function,+,aligned_free,void,void* +Function,+,aligned_malloc,void*,"size_t, size_t" Function,-,arc4random,__uint32_t, Function,-,arc4random_buf,void,"void*, size_t" Function,-,arc4random_uniform,__uint32_t,__uint32_t @@ -2392,8 +2394,7 @@ Function,+,memchr,void*,"const void*, int, size_t" Function,+,memcmp,int,"const void*, const void*, size_t" Function,+,memcpy,void*,"void*, const void*, size_t" Function,-,memmem,void*,"const void*, size_t, const void*, size_t" -Function,+,memmgr_aux_pool_alloc,void*,size_t -Function,+,memmgr_aux_pool_get_free,size_t, +Function,-,memmgr_alloc_from_pool,void*,size_t Function,+,memmgr_get_free_heap,size_t, Function,+,memmgr_get_minimum_free_heap,size_t, Function,+,memmgr_get_total_heap,size_t, @@ -2401,7 +2402,8 @@ Function,+,memmgr_heap_disable_thread_trace,void,FuriThreadId Function,+,memmgr_heap_enable_thread_trace,void,FuriThreadId Function,+,memmgr_heap_get_max_free_block,size_t, Function,+,memmgr_heap_get_thread_memory,size_t,FuriThreadId -Function,+,memmgr_heap_walk_blocks,void,"BlockWalker, void*" +Function,+,memmgr_heap_printf_free_blocks,void, +Function,-,memmgr_pool_get_free,size_t, Function,-,memmgr_pool_get_max_block,size_t, Function,+,memmove,void*,"void*, const void*, size_t" Function,-,mempcpy,void*,"void*, const void*, size_t" diff --git a/targets/f7/fatfs/sector_cache.c b/targets/f7/fatfs/sector_cache.c index df86cb7f1..319dd2173 100644 --- a/targets/f7/fatfs/sector_cache.c +++ b/targets/f7/fatfs/sector_cache.c @@ -19,7 +19,7 @@ static SectorCache* cache = NULL; void sector_cache_init(void) { if(cache == NULL) { - cache = memmgr_aux_pool_alloc(sizeof(SectorCache)); + cache = memmgr_alloc_from_pool(sizeof(SectorCache)); } if(cache != NULL) { diff --git a/targets/f7/src/update.c b/targets/f7/src/update.c index 261adb5ca..e6cb4aabe 100644 --- a/targets/f7/src/update.c +++ b/targets/f7/src/update.c @@ -78,21 +78,21 @@ static bool flipper_update_load_stage(const FuriString* work_dir, UpdateManifest furi_string_free(loader_img_path); void* img = malloc(stat.fsize); - uint32_t read_total = 0; - uint16_t read_current = 0; + uint32_t bytes_read = 0; const uint16_t MAX_READ = 0xFFFF; uint32_t crc = 0; do { - if(f_read(&file, img + read_total, MAX_READ, &read_current) != FR_OK) { //-V769 + uint16_t size_read = 0; + if(f_read(&file, img + bytes_read, MAX_READ, &size_read) != FR_OK) { //-V769 break; } - crc = crc32_calc_buffer(crc, img + read_total, read_current); - read_total += read_current; - } while(read_current == MAX_READ); + crc = crc32_calc_buffer(crc, img + bytes_read, size_read); + bytes_read += size_read; + } while(bytes_read == MAX_READ); do { - if((read_total != stat.fsize) || (crc != manifest->staged_loader_crc)) { + if((bytes_read != stat.fsize) || (crc != manifest->staged_loader_crc)) { break; } diff --git a/targets/f7/target.json b/targets/f7/target.json index caa3f58ee..25872198b 100644 --- a/targets/f7/target.json +++ b/targets/f7/target.json @@ -22,7 +22,6 @@ "print", "flipper7", "furi", - "tlsf", "freertos", "stm32wb", "hwdrivers", @@ -56,4 +55,4 @@ "bit_lib", "datetime" ] -} \ No newline at end of file +} From 63403bbae2f46e41d9d3b136e228e0b602e1461e Mon Sep 17 00:00:00 2001 From: WillyJL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 16 May 2024 18:55:08 +0100 Subject: [PATCH 06/11] JS: Add submenu module (#3601) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * JS: Add submenu module * Using view_holder instead of view_dispatcher Co-authored-by: nminaylov Co-authored-by: あく --- applications/services/gui/application.fam | 1 + applications/system/js_app/application.fam | 8 + .../js_app/examples/apps/Scripts/submenu.js | 11 ++ .../system/js_app/modules/js_submenu.c | 148 ++++++++++++++++++ targets/f18/api_symbols.csv | 13 +- targets/f7/api_symbols.csv | 13 +- 6 files changed, 192 insertions(+), 2 deletions(-) create mode 100644 applications/system/js_app/examples/apps/Scripts/submenu.js create mode 100644 applications/system/js_app/modules/js_submenu.c diff --git a/applications/services/gui/application.fam b/applications/services/gui/application.fam index 869d964dd..b7dd18baa 100644 --- a/applications/services/gui/application.fam +++ b/applications/services/gui/application.fam @@ -16,6 +16,7 @@ App( "elements.h", "view_dispatcher.h", "view_stack.h", + "view_holder.h", "modules/button_menu.h", "modules/byte_input.h", "modules/popup.h", diff --git a/applications/system/js_app/application.fam b/applications/system/js_app/application.fam index 114bec55f..a955ef355 100644 --- a/applications/system/js_app/application.fam +++ b/applications/system/js_app/application.fam @@ -46,3 +46,11 @@ App( requires=["js_app"], sources=["modules/js_serial.c"], ) + +App( + appid="js_submenu", + apptype=FlipperAppType.PLUGIN, + entry_point="js_submenu_ep", + requires=["js_app"], + sources=["modules/js_submenu.c"], +) diff --git a/applications/system/js_app/examples/apps/Scripts/submenu.js b/applications/system/js_app/examples/apps/Scripts/submenu.js new file mode 100644 index 000000000..245551309 --- /dev/null +++ b/applications/system/js_app/examples/apps/Scripts/submenu.js @@ -0,0 +1,11 @@ +let submenu = require("submenu"); + +submenu.addItem("Item 1", 0); +submenu.addItem("Item 2", 1); +submenu.addItem("Item 3", 2); + +submenu.setHeader("Select an option:"); + +let result = submenu.show(); +// Returns undefined when pressing back +print("Result:", result); diff --git a/applications/system/js_app/modules/js_submenu.c b/applications/system/js_app/modules/js_submenu.c new file mode 100644 index 000000000..058b32fd0 --- /dev/null +++ b/applications/system/js_app/modules/js_submenu.c @@ -0,0 +1,148 @@ +#include +#include +#include +#include +#include "../js_modules.h" + +typedef struct { + Submenu* submenu; + ViewHolder* view_holder; + FuriApiLock lock; + uint32_t result; + bool accepted; +} JsSubmenuInst; + +static JsSubmenuInst* get_this_ctx(struct mjs* mjs) { + mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0); + JsSubmenuInst* submenu = mjs_get_ptr(mjs, obj_inst); + furi_assert(submenu); + return submenu; +} + +static void ret_bad_args(struct mjs* mjs, const char* error) { + mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "%s", error); + mjs_return(mjs, MJS_UNDEFINED); +} + +static bool check_arg_count(struct mjs* mjs, size_t count) { + size_t num_args = mjs_nargs(mjs); + if(num_args != count) { + ret_bad_args(mjs, "Wrong argument count"); + return false; + } + return true; +} + +static void submenu_callback(void* context, uint32_t id) { + JsSubmenuInst* submenu = context; + submenu->result = id; + submenu->accepted = true; + api_lock_unlock(submenu->lock); +} + +static void submenu_exit(void* context) { + JsSubmenuInst* submenu = context; + submenu->result = 0; + submenu->accepted = false; + api_lock_unlock(submenu->lock); +} + +static void js_submenu_add_item(struct mjs* mjs) { + JsSubmenuInst* submenu = get_this_ctx(mjs); + if(!check_arg_count(mjs, 2)) return; + + mjs_val_t label_arg = mjs_arg(mjs, 0); + const char* label = mjs_get_string(mjs, &label_arg, NULL); + if(!label) { + ret_bad_args(mjs, "Label must be a string"); + return; + } + + mjs_val_t id_arg = mjs_arg(mjs, 1); + if(!mjs_is_number(id_arg)) { + ret_bad_args(mjs, "Id must be a number"); + return; + } + int32_t id = mjs_get_int32(mjs, id_arg); + + submenu_add_item(submenu->submenu, label, id, submenu_callback, submenu); + + mjs_return(mjs, MJS_UNDEFINED); +} + +static void js_submenu_set_header(struct mjs* mjs) { + JsSubmenuInst* submenu = get_this_ctx(mjs); + if(!check_arg_count(mjs, 1)) return; + + mjs_val_t header_arg = mjs_arg(mjs, 0); + const char* header = mjs_get_string(mjs, &header_arg, NULL); + if(!header) { + ret_bad_args(mjs, "Header must be a string"); + return; + } + + submenu_set_header(submenu->submenu, header); + + mjs_return(mjs, MJS_UNDEFINED); +} + +static void js_submenu_show(struct mjs* mjs) { + JsSubmenuInst* submenu = get_this_ctx(mjs); + if(!check_arg_count(mjs, 0)) return; + + submenu->lock = api_lock_alloc_locked(); + Gui* gui = furi_record_open(RECORD_GUI); + submenu->view_holder = view_holder_alloc(); + view_holder_attach_to_gui(submenu->view_holder, gui); + view_holder_set_back_callback(submenu->view_holder, submenu_exit, submenu); + + view_holder_set_view(submenu->view_holder, submenu_get_view(submenu->submenu)); + view_holder_start(submenu->view_holder); + api_lock_wait_unlock(submenu->lock); + + view_holder_stop(submenu->view_holder); + view_holder_free(submenu->view_holder); + furi_record_close(RECORD_GUI); + api_lock_free(submenu->lock); + + submenu_reset(submenu->submenu); + if(submenu->accepted) { + mjs_return(mjs, mjs_mk_number(mjs, submenu->result)); + } else { + mjs_return(mjs, MJS_UNDEFINED); + } +} + +static void* js_submenu_create(struct mjs* mjs, mjs_val_t* object) { + JsSubmenuInst* submenu = malloc(sizeof(JsSubmenuInst)); + mjs_val_t submenu_obj = mjs_mk_object(mjs); + mjs_set(mjs, submenu_obj, INST_PROP_NAME, ~0, mjs_mk_foreign(mjs, submenu)); + mjs_set(mjs, submenu_obj, "addItem", ~0, MJS_MK_FN(js_submenu_add_item)); + mjs_set(mjs, submenu_obj, "setHeader", ~0, MJS_MK_FN(js_submenu_set_header)); + mjs_set(mjs, submenu_obj, "show", ~0, MJS_MK_FN(js_submenu_show)); + submenu->submenu = submenu_alloc(); + *object = submenu_obj; + return submenu; +} + +static void js_submenu_destroy(void* inst) { + JsSubmenuInst* submenu = inst; + submenu_free(submenu->submenu); + free(submenu); +} + +static const JsModuleDescriptor js_submenu_desc = { + "submenu", + js_submenu_create, + js_submenu_destroy, +}; + +static const FlipperAppPluginDescriptor submenu_plugin_descriptor = { + .appid = PLUGIN_APP_ID, + .ep_api_version = PLUGIN_API_VERSION, + .entry_point = &js_submenu_desc, +}; + +const FlipperAppPluginDescriptor* js_submenu_ep(void) { + return &submenu_plugin_descriptor; +} diff --git a/targets/f18/api_symbols.csv b/targets/f18/api_symbols.csv index 492539d46..ce7ad2536 100644 --- a/targets/f18/api_symbols.csv +++ b/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,61.3,, +Version,+,61.4,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -27,6 +27,7 @@ Header,+,applications/services/gui/modules/variable_item_list.h,, Header,+,applications/services/gui/modules/widget.h,, Header,+,applications/services/gui/modules/widget_elements/widget_element.h,, Header,+,applications/services/gui/view_dispatcher.h,, +Header,+,applications/services/gui/view_holder.h,, Header,+,applications/services/gui/view_stack.h,, Header,+,applications/services/input/input.h,, Header,+,applications/services/loader/firmware_api/firmware_api.h,, @@ -2683,6 +2684,16 @@ Function,+,view_dispatcher_switch_to_view,void,"ViewDispatcher*, uint32_t" Function,+,view_free,void,View* Function,+,view_free_model,void,View* Function,+,view_get_model,void*,View* +Function,+,view_holder_alloc,ViewHolder*, +Function,+,view_holder_attach_to_gui,void,"ViewHolder*, Gui*" +Function,+,view_holder_free,void,ViewHolder* +Function,+,view_holder_get_free_context,void*,ViewHolder* +Function,+,view_holder_set_back_callback,void,"ViewHolder*, BackCallback, void*" +Function,+,view_holder_set_free_callback,void,"ViewHolder*, FreeCallback, void*" +Function,+,view_holder_set_view,void,"ViewHolder*, View*" +Function,+,view_holder_start,void,ViewHolder* +Function,+,view_holder_stop,void,ViewHolder* +Function,+,view_holder_update,void,"View*, void*" Function,+,view_port_alloc,ViewPort*, Function,+,view_port_draw_callback_set,void,"ViewPort*, ViewPortDrawCallback, void*" Function,+,view_port_enabled_set,void,"ViewPort*, _Bool" diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index e209023b5..5a44dbc6f 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,61.3,, +Version,+,61.4,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, @@ -28,6 +28,7 @@ Header,+,applications/services/gui/modules/variable_item_list.h,, Header,+,applications/services/gui/modules/widget.h,, Header,+,applications/services/gui/modules/widget_elements/widget_element.h,, Header,+,applications/services/gui/view_dispatcher.h,, +Header,+,applications/services/gui/view_holder.h,, Header,+,applications/services/gui/view_stack.h,, Header,+,applications/services/input/input.h,, Header,+,applications/services/loader/firmware_api/firmware_api.h,, @@ -3483,6 +3484,16 @@ Function,+,view_dispatcher_switch_to_view,void,"ViewDispatcher*, uint32_t" Function,+,view_free,void,View* Function,+,view_free_model,void,View* Function,+,view_get_model,void*,View* +Function,+,view_holder_alloc,ViewHolder*, +Function,+,view_holder_attach_to_gui,void,"ViewHolder*, Gui*" +Function,+,view_holder_free,void,ViewHolder* +Function,+,view_holder_get_free_context,void*,ViewHolder* +Function,+,view_holder_set_back_callback,void,"ViewHolder*, BackCallback, void*" +Function,+,view_holder_set_free_callback,void,"ViewHolder*, FreeCallback, void*" +Function,+,view_holder_set_view,void,"ViewHolder*, View*" +Function,+,view_holder_start,void,ViewHolder* +Function,+,view_holder_stop,void,ViewHolder* +Function,+,view_holder_update,void,"View*, void*" Function,+,view_port_alloc,ViewPort*, Function,+,view_port_draw_callback_set,void,"ViewPort*, ViewPortDrawCallback, void*" Function,+,view_port_enabled_set,void,"ViewPort*, _Bool" From 603a86dbe6b59fd475863cf40b96e981fdfd535b Mon Sep 17 00:00:00 2001 From: Astra <93453568+Astrrra@users.noreply.github.com> Date: Fri, 17 May 2024 20:52:42 +0900 Subject: [PATCH 07/11] [FL-3797] Settings refactor fixes (#3654) --- .../scenes/desktop_settings_scene_pin_disable.c | 2 +- .../scenes/desktop_settings_scene_pin_menu.c | 2 +- .../scenes/storage_settings_scene_formatting.c | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_disable.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_disable.c index c3e22805d..2f21fd999 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_disable.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_disable.c @@ -25,7 +25,7 @@ void desktop_settings_scene_pin_disable_on_enter(void* context) { popup_set_context(app->popup, app); popup_set_callback(app->popup, pin_disable_back_callback); popup_set_icon(app->popup, 0, 2, &I_DolphinMafia_119x62); - popup_set_header(app->popup, "PIN\nDeleted!", 100, 0, AlignCenter, AlignTop); + popup_set_header(app->popup, "Removed", 100, 10, AlignCenter, AlignTop); popup_set_timeout(app->popup, 1500); popup_enable_timeout(app->popup); view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewIdPopup); diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_menu.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_menu.c index 9fdd68896..950be4c5a 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_menu.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_menu.c @@ -37,7 +37,7 @@ void desktop_settings_scene_pin_menu_on_enter(void* context) { submenu_add_item( submenu, - "Disable", + "Remove PIN", SCENE_EVENT_DISABLE_PIN, desktop_settings_scene_pin_menu_submenu_callback, app); diff --git a/applications/settings/storage_settings/scenes/storage_settings_scene_formatting.c b/applications/settings/storage_settings/scenes/storage_settings_scene_formatting.c index 2fb232f14..6a958610e 100644 --- a/applications/settings/storage_settings/scenes/storage_settings_scene_formatting.c +++ b/applications/settings/storage_settings/scenes/storage_settings_scene_formatting.c @@ -51,15 +51,15 @@ void storage_settings_scene_formatting_on_enter(void* context) { dialog_ex_set_text( dialog_ex, storage_error_get_desc(error), 64, 32, AlignCenter, AlignCenter); } else { - dialog_ex_set_icon(dialog_ex, 83, 22, &I_WarningDolphinFlip_45x42); - dialog_ex_set_header(dialog_ex, "Format\ncomplete!", 14, 15, AlignLeft, AlignTop); + dialog_ex_set_icon(dialog_ex, 48, 6, &I_DolphinDone_80x58); + dialog_ex_set_header(dialog_ex, "Formatted", 5, 10, AlignLeft, AlignTop); NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); notification_message(notification, &sequence_single_vibro); notification_message(notification, &sequence_set_green_255); notification_message(notification, &sequence_success); furi_record_close(RECORD_NOTIFICATION); } - dialog_ex_set_center_button_text(dialog_ex, "OK"); + dialog_ex_set_left_button_text(dialog_ex, "Finish"); } bool storage_settings_scene_formatting_on_event(void* context, SceneManagerEvent event) { @@ -68,7 +68,7 @@ bool storage_settings_scene_formatting_on_event(void* context, SceneManagerEvent if(event.type == SceneManagerEventTypeCustom) { switch(event.event) { - case DialogExResultCenter: + case DialogExResultLeft: consumed = scene_manager_search_and_switch_to_previous_scene( app->scene_manager, StorageSettingsStart); break; From 217bfac2fc8d281f6ba496e6a42786fef38ec606 Mon Sep 17 00:00:00 2001 From: gornekich Date: Fri, 17 May 2024 12:58:32 +0100 Subject: [PATCH 08/11] NFC: add Slix capabilities (#3652) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * iso15693 listener: fix inventory cmd and buffer overflow * iso15 listener: fix read multiple blocks command * slix: print password * slix: add capabilities field * slix listener: skip password validation for special capability * slix: fix capability name * slix: add capabilities handler to verify and reset * nfc test: introduce slix tests * fbt: change toolchain back to 33 version * slix: fix saving capablities comment * unit tests: add slix files to resources * slix: fix set passwrd signature * nfc tests: add set correct password test * nfc test: complete slix password tests * nfc test: add slix file test * nfc test: handle errors in worker callback * iso15693_3: code clean up * iso15693_listener: fix incorrect afi handling * slix: chage capabilities format to one word camel case * unit tests: update nfc files with new slix format Co-authored-by: あく --- applications/debug/unit_tests/nfc/nfc_test.c | 145 ++++++++++++++++++ .../debug/unit_tests/nfc/nfc_transport.c | 5 +- .../nfc/Slix_cap_accept_all_pass.nfc | 41 +++++ .../unit_tests/nfc/Slix_cap_default.nfc | 41 +++++ .../unit_tests/nfc/Slix_cap_missed.nfc | 39 +++++ .../iso15693_3/iso15693_3_listener.c | 3 +- .../iso15693_3/iso15693_3_listener_i.c | 17 +- lib/nfc/protocols/slix/slix.c | 64 +++++++- lib/nfc/protocols/slix/slix.h | 8 + lib/nfc/protocols/slix/slix_listener_i.c | 7 + lib/nfc/protocols/slix/slix_poller.c | 6 +- lib/nfc/protocols/slix/slix_poller.h | 12 +- lib/nfc/protocols/slix/slix_poller_i.c | 11 +- 13 files changed, 375 insertions(+), 24 deletions(-) create mode 100644 applications/debug/unit_tests/resources/unit_tests/nfc/Slix_cap_accept_all_pass.nfc create mode 100644 applications/debug/unit_tests/resources/unit_tests/nfc/Slix_cap_default.nfc create mode 100644 applications/debug/unit_tests/resources/unit_tests/nfc/Slix_cap_missed.nfc diff --git a/applications/debug/unit_tests/nfc/nfc_test.c b/applications/debug/unit_tests/nfc/nfc_test.c index c6304d53c..8bb88df9a 100644 --- a/applications/debug/unit_tests/nfc/nfc_test.c +++ b/applications/debug/unit_tests/nfc/nfc_test.c @@ -13,6 +13,12 @@ #include #include #include +#include +#include +#include +#include +#include + #include #include @@ -42,6 +48,19 @@ typedef struct { FuriThreadId thread_id; } NfcTestMfClassicSendFrameTest; +typedef enum { + NfcTestSlixPollerSetPasswordStateGetRandomNumber, + NfcTestSlixPollerSetPasswordStateSetPassword, +} NfcTestSlixPollerSetPasswordState; + +typedef struct { + FuriThreadId thread_id; + NfcTestSlixPollerSetPasswordState state; + SlixRandomNumber random_number; + SlixPassword password; + SlixError error; +} NfcTestSlixPollerSetPasswordContext; + typedef struct { Storage* storage; } NfcTest; @@ -627,6 +646,127 @@ MU_TEST(mf_classic_dict_test) { "Remove test dict failed"); } +MU_TEST(slix_file_with_capabilities_test) { + NfcDevice* nfc_device_missed_cap = nfc_device_alloc(); + mu_assert( + nfc_device_load(nfc_device_missed_cap, EXT_PATH("unit_tests/nfc/Slix_cap_missed.nfc")), + "nfc_device_load() failed\r\n"); + + NfcDevice* nfc_device_default_cap = nfc_device_alloc(); + mu_assert( + nfc_device_load(nfc_device_default_cap, EXT_PATH("unit_tests/nfc/Slix_cap_default.nfc")), + "nfc_device_load() failed\r\n"); + + mu_assert( + nfc_device_is_equal(nfc_device_missed_cap, nfc_device_default_cap), + "nfc_device_is_equal() failed\r\n"); + + nfc_device_free(nfc_device_default_cap); + nfc_device_free(nfc_device_missed_cap); +} + +NfcCommand slix_poller_set_password_callback(NfcGenericEventEx event, void* context) { + furi_check(event.poller); + furi_check(event.parent_event_data); + furi_check(context); + + NfcCommand command = NfcCommandContinue; + Iso15693_3PollerEvent* iso15_event = event.parent_event_data; + SlixPoller* poller = event.poller; + NfcTestSlixPollerSetPasswordContext* slix_ctx = context; + + if(iso15_event->type == Iso15693_3PollerEventTypeReady) { + iso15693_3_copy( + poller->data->iso15693_3_data, iso15693_3_poller_get_data(poller->iso15693_3_poller)); + + if(slix_ctx->state == NfcTestSlixPollerSetPasswordStateGetRandomNumber) { + slix_ctx->error = slix_poller_get_random_number(poller, &slix_ctx->random_number); + if(slix_ctx->error != SlixErrorNone) { + furi_thread_flags_set(slix_ctx->thread_id, NFC_TEST_FLAG_WORKER_DONE); + command = NfcCommandStop; + } else { + slix_ctx->state = NfcTestSlixPollerSetPasswordStateSetPassword; + } + } else if(slix_ctx->state == NfcTestSlixPollerSetPasswordStateSetPassword) { + slix_ctx->error = slix_poller_set_password( + poller, SlixPasswordTypeRead, slix_ctx->password, slix_ctx->random_number); + furi_thread_flags_set(slix_ctx->thread_id, NFC_TEST_FLAG_WORKER_DONE); + command = NfcCommandStop; + } + } else { + slix_ctx->error = slix_process_iso15693_3_error(iso15_event->data->error); + furi_thread_flags_set(slix_ctx->thread_id, NFC_TEST_FLAG_WORKER_DONE); + command = NfcCommandStop; + } + + return command; +} + +static void slix_set_password_test(const char* file_path, SlixPassword pass, bool correct_pass) { + FURI_LOG_I(TAG, "Testing file: %s", file_path); + + Nfc* poller = nfc_alloc(); + Nfc* listener = nfc_alloc(); + + NfcDevice* nfc_device = nfc_device_alloc(); + mu_assert(nfc_device_load(nfc_device, file_path), "nfc_device_load() failed\r\n"); + + const SlixData* slix_data = nfc_device_get_data(nfc_device, NfcProtocolSlix); + NfcListener* slix_listener = nfc_listener_alloc(listener, NfcProtocolSlix, slix_data); + nfc_listener_start(slix_listener, NULL, NULL); + + SlixCapabilities slix_capabilities = slix_data->capabilities; + + NfcPoller* slix_poller = nfc_poller_alloc(poller, NfcProtocolSlix); + + NfcTestSlixPollerSetPasswordContext slix_poller_context = { + .thread_id = furi_thread_get_current_id(), + .state = NfcTestSlixPollerSetPasswordStateGetRandomNumber, + .password = pass, + .error = SlixErrorNone, + }; + + nfc_poller_start_ex(slix_poller, slix_poller_set_password_callback, &slix_poller_context); + + uint32_t flag = + furi_thread_flags_wait(NFC_TEST_FLAG_WORKER_DONE, FuriFlagWaitAny, FuriWaitForever); + mu_assert(flag == NFC_TEST_FLAG_WORKER_DONE, "Wrong thread flag\r\n"); + + nfc_poller_stop(slix_poller); + nfc_poller_free(slix_poller); + nfc_listener_stop(slix_listener); + nfc_listener_free(slix_listener); + + mu_assert( + slix_poller_context.state == NfcTestSlixPollerSetPasswordStateSetPassword, + "Poller failed before setting password\r\n"); + + if((slix_capabilities == SlixCapabilitiesAcceptAllPasswords) || (correct_pass)) { + mu_assert(slix_poller_context.error == SlixErrorNone, "Failed to set password\r\n"); + } else { + mu_assert( + slix_poller_context.error == SlixErrorTimeout, + "Must have received SlixErrorTimeout\r\n"); + } + + nfc_device_free(nfc_device); + nfc_free(listener); + nfc_free(poller); +} + +MU_TEST(slix_set_password_default_cap_correct_pass) { + slix_set_password_test(EXT_PATH("unit_tests/nfc/Slix_cap_default.nfc"), 0x00000000, true); +} + +MU_TEST(slix_set_password_default_cap_incorrect_pass) { + slix_set_password_test(EXT_PATH("unit_tests/nfc/Slix_cap_default.nfc"), 0x12341234, false); +} + +MU_TEST(slix_set_password_access_all_passwords_cap) { + slix_set_password_test( + EXT_PATH("unit_tests/nfc/Slix_cap_accept_all_pass.nfc"), 0x12341234, false); +} + MU_TEST_SUITE(nfc) { nfc_test_alloc(); @@ -668,6 +808,11 @@ MU_TEST_SUITE(nfc) { MU_RUN_TEST(mf_classic_send_frame_test); MU_RUN_TEST(mf_classic_dict_test); + MU_RUN_TEST(slix_file_with_capabilities_test); + MU_RUN_TEST(slix_set_password_default_cap_correct_pass); + MU_RUN_TEST(slix_set_password_default_cap_incorrect_pass); + MU_RUN_TEST(slix_set_password_access_all_passwords_cap); + nfc_test_free(); } diff --git a/applications/debug/unit_tests/nfc/nfc_transport.c b/applications/debug/unit_tests/nfc/nfc_transport.c index 6886ef66d..df0e009e7 100644 --- a/applications/debug/unit_tests/nfc/nfc_transport.c +++ b/applications/debug/unit_tests/nfc/nfc_transport.c @@ -55,6 +55,7 @@ struct Nfc { Iso14443_3aColResStatus col_res_status; Iso14443_3aColResData col_res_data; + bool software_col_res_required; NfcEventCallback callback; void* context; @@ -170,6 +171,7 @@ NfcError nfc_iso14443a_listener_set_col_res_data( furi_check(atqa); nfc_prepare_col_res_data(instance, uid, uid_len, atqa, sak); + instance->software_col_res_required = true; return NfcErrorNone; } @@ -275,7 +277,8 @@ static int32_t nfc_worker_listener(void* context) { } else if(message.type == NfcMessageTypeTx) { nfc_test_print( NfcTransportLogLevelInfo, "RDR", message.data.data, message.data.data_bits); - if(instance->col_res_status != Iso14443_3aColResStatusDone) { + if(instance->software_col_res_required && + (instance->col_res_status != Iso14443_3aColResStatusDone)) { nfc_worker_listener_pass_col_res( instance, message.data.data, message.data.data_bits); } else { diff --git a/applications/debug/unit_tests/resources/unit_tests/nfc/Slix_cap_accept_all_pass.nfc b/applications/debug/unit_tests/resources/unit_tests/nfc/Slix_cap_accept_all_pass.nfc new file mode 100644 index 000000000..0a8db9ae9 --- /dev/null +++ b/applications/debug/unit_tests/resources/unit_tests/nfc/Slix_cap_accept_all_pass.nfc @@ -0,0 +1,41 @@ +Filetype: Flipper NFC device +Version: 4 +# Device type can be ISO14443-3A, ISO14443-3B, ISO14443-4A, ISO14443-4B, ISO15693-3, FeliCa, NTAG/Ultralight, Mifare Classic, Mifare DESFire, SLIX, ST25TB +Device type: SLIX +# UID is common for all formats +UID: E0 04 01 08 49 D0 DC 81 +# ISO15693-3 specific data +# Data Storage Format Identifier +DSFID: 01 +# Application Family Identifier +AFI: 3D +# IC Reference - Vendor specific meaning +IC Reference: 01 +# Lock Bits +Lock DSFID: true +Lock AFI: true +# Number of memory blocks, valid range = 1..256 +Block Count: 80 +# Size of a single memory block, valid range = 01...20 (hex) +Block Size: 04 +Data Content: 03 0A 82 ED 86 39 61 D2 03 14 1E 32 B6 CA 00 3C 36 42 0C 33 53 30 37 32 32 34 30 30 00 00 00 00 00 FF 04 01 01 00 00 00 A3 03 1E 00 26 00 00 00 00 00 0F 00 76 03 65 01 00 00 00 00 85 01 34 00 75 09 05 00 01 00 00 00 00 00 00 00 00 00 00 00 D7 FA 00 1C 9E 1C 67 27 00 30 30 30 30 30 30 30 30 30 30 00 00 00 97 25 55 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 32 8C 00 30 53 48 80 DE 00 00 00 00 F4 C3 58 2B 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 11 F3 00 2C DD C3 3E 91 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E5 FF 00 01 +# Block Security Status: 01 = locked, 00 = not locked +Security Status: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +# SLIX specific data +# SLIX capabilities field affects emulation modes. Possible options: Default, AcceptAllPasswords +Capabilities: AcceptAllPasswords +# Passwords are optional. If a password is omitted, a default value will be used +Password Read: 00 00 00 00 +Password Write: 00 00 00 00 +Password Privacy: 0F 0F 0F 0F +Password Destroy: 0F 0F 0F 0F +Password EAS: 00 00 00 00 +# This is the card's secp128r1 elliptic curve signature. It can not be calculated without knowing NXP's private key. +Signature: A6 25 54 03 74 24 C4 38 36 F4 89 70 76 1A 72 27 54 D9 E7 3D 38 CB 4C 1B 3E FD 0E DF 8A F6 7E 3D +Privacy Mode: false +# Protection pointer configuration +Protection Pointer: 32 +Protection Condition: 02 +# SLIX Lock Bits +Lock EAS: true +Lock PPL: true diff --git a/applications/debug/unit_tests/resources/unit_tests/nfc/Slix_cap_default.nfc b/applications/debug/unit_tests/resources/unit_tests/nfc/Slix_cap_default.nfc new file mode 100644 index 000000000..d1af957dc --- /dev/null +++ b/applications/debug/unit_tests/resources/unit_tests/nfc/Slix_cap_default.nfc @@ -0,0 +1,41 @@ +Filetype: Flipper NFC device +Version: 4 +# Device type can be ISO14443-3A, ISO14443-3B, ISO14443-4A, ISO14443-4B, ISO15693-3, FeliCa, NTAG/Ultralight, Mifare Classic, Mifare DESFire, SLIX, ST25TB +Device type: SLIX +# UID is common for all formats +UID: E0 04 01 08 49 D0 DC 81 +# ISO15693-3 specific data +# Data Storage Format Identifier +DSFID: 01 +# Application Family Identifier +AFI: 3D +# IC Reference - Vendor specific meaning +IC Reference: 01 +# Lock Bits +Lock DSFID: true +Lock AFI: true +# Number of memory blocks, valid range = 1..256 +Block Count: 80 +# Size of a single memory block, valid range = 01...20 (hex) +Block Size: 04 +Data Content: 03 0A 82 ED 86 39 61 D2 03 14 1E 32 B6 CA 00 3C 36 42 0C 33 53 30 37 32 32 34 30 30 00 00 00 00 00 FF 04 01 01 00 00 00 A3 03 1E 00 26 00 00 00 00 00 0F 00 76 03 65 01 00 00 00 00 85 01 34 00 75 09 05 00 01 00 00 00 00 00 00 00 00 00 00 00 D7 FA 00 1C 9E 1C 67 27 00 30 30 30 30 30 30 30 30 30 30 00 00 00 97 25 55 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 32 8C 00 30 53 48 80 DE 00 00 00 00 F4 C3 58 2B 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 11 F3 00 2C DD C3 3E 91 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E5 FF 00 01 +# Block Security Status: 01 = locked, 00 = not locked +Security Status: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +# SLIX specific data +# SLIX capabilities field affects emulation modes. Possible options: Default, AcceptAllPasswords +Capabilities: Default +# Passwords are optional. If a password is omitted, a default value will be used +Password Read: 00 00 00 00 +Password Write: 00 00 00 00 +Password Privacy: 0F 0F 0F 0F +Password Destroy: 0F 0F 0F 0F +Password EAS: 00 00 00 00 +# This is the card's secp128r1 elliptic curve signature. It can not be calculated without knowing NXP's private key. +Signature: A6 25 54 03 74 24 C4 38 36 F4 89 70 76 1A 72 27 54 D9 E7 3D 38 CB 4C 1B 3E FD 0E DF 8A F6 7E 3D +Privacy Mode: false +# Protection pointer configuration +Protection Pointer: 32 +Protection Condition: 02 +# SLIX Lock Bits +Lock EAS: true +Lock PPL: true diff --git a/applications/debug/unit_tests/resources/unit_tests/nfc/Slix_cap_missed.nfc b/applications/debug/unit_tests/resources/unit_tests/nfc/Slix_cap_missed.nfc new file mode 100644 index 000000000..35650b0e8 --- /dev/null +++ b/applications/debug/unit_tests/resources/unit_tests/nfc/Slix_cap_missed.nfc @@ -0,0 +1,39 @@ +Filetype: Flipper NFC device +Version: 4 +# Device type can be ISO14443-3A, ISO14443-3B, ISO14443-4A, ISO14443-4B, ISO15693-3, FeliCa, NTAG/Ultralight, Mifare Classic, Mifare DESFire, SLIX, ST25TB +Device type: SLIX +# UID is common for all formats +UID: E0 04 01 08 49 D0 DC 81 +# ISO15693-3 specific data +# Data Storage Format Identifier +DSFID: 01 +# Application Family Identifier +AFI: 3D +# IC Reference - Vendor specific meaning +IC Reference: 01 +# Lock Bits +Lock DSFID: true +Lock AFI: true +# Number of memory blocks, valid range = 1..256 +Block Count: 80 +# Size of a single memory block, valid range = 01...20 (hex) +Block Size: 04 +Data Content: 03 0A 82 ED 86 39 61 D2 03 14 1E 32 B6 CA 00 3C 36 42 0C 33 53 30 37 32 32 34 30 30 00 00 00 00 00 FF 04 01 01 00 00 00 A3 03 1E 00 26 00 00 00 00 00 0F 00 76 03 65 01 00 00 00 00 85 01 34 00 75 09 05 00 01 00 00 00 00 00 00 00 00 00 00 00 D7 FA 00 1C 9E 1C 67 27 00 30 30 30 30 30 30 30 30 30 30 00 00 00 97 25 55 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 32 8C 00 30 53 48 80 DE 00 00 00 00 F4 C3 58 2B 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 11 F3 00 2C DD C3 3E 91 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E5 FF 00 01 +# Block Security Status: 01 = locked, 00 = not locked +Security Status: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +# SLIX specific data +# Passwords are optional. If a password is omitted, a default value will be used +Password Read: 00 00 00 00 +Password Write: 00 00 00 00 +Password Privacy: 0F 0F 0F 0F +Password Destroy: 0F 0F 0F 0F +Password EAS: 00 00 00 00 +# This is the card's secp128r1 elliptic curve signature. It can not be calculated without knowing NXP's private key. +Signature: A6 25 54 03 74 24 C4 38 36 F4 89 70 76 1A 72 27 54 D9 E7 3D 38 CB 4C 1B 3E FD 0E DF 8A F6 7E 3D +Privacy Mode: false +# Protection pointer configuration +Protection Pointer: 32 +Protection Condition: 02 +# SLIX Lock Bits +Lock EAS: true +Lock PPL: true diff --git a/lib/nfc/protocols/iso15693_3/iso15693_3_listener.c b/lib/nfc/protocols/iso15693_3/iso15693_3_listener.c index 84e750858..151e4ae4a 100644 --- a/lib/nfc/protocols/iso15693_3/iso15693_3_listener.c +++ b/lib/nfc/protocols/iso15693_3/iso15693_3_listener.c @@ -8,7 +8,7 @@ #define TAG "Iso15693_3Listener" -#define ISO15693_3_LISTENER_BUFFER_SIZE (64U) +#define ISO15693_3_LISTENER_BUFFER_SIZE (256U) Iso15693_3Listener* iso15693_3_listener_alloc(Nfc* nfc, Iso15693_3Data* data) { furi_assert(nfc); @@ -67,6 +67,7 @@ NfcCommand iso15693_3_listener_run(NfcGenericEvent event, void* context) { if(nfc_event->type == NfcEventTypeRxEnd) { BitBuffer* rx_buffer = nfc_event->data.buffer; + bit_buffer_reset(instance->tx_buffer); if(iso13239_crc_check(Iso13239CrcTypeDefault, rx_buffer)) { iso13239_crc_trim(rx_buffer); diff --git a/lib/nfc/protocols/iso15693_3/iso15693_3_listener_i.c b/lib/nfc/protocols/iso15693_3/iso15693_3_listener_i.c index a8dec7ae3..6132fbf47 100644 --- a/lib/nfc/protocols/iso15693_3/iso15693_3_listener_i.c +++ b/lib/nfc/protocols/iso15693_3/iso15693_3_listener_i.c @@ -64,7 +64,9 @@ static Iso15693_3Error iso15693_3_listener_inventory_handler( if(afi_flag) { const uint8_t afi = *data++; // When AFI flag is set, ignore non-matching requests - if(afi != instance->data->system_info.afi) break; + if(afi != 0) { + if(afi != instance->data->system_info.afi) break; + } } const uint8_t mask_len = *data++; @@ -260,16 +262,9 @@ static Iso15693_3Error iso15693_3_listener_read_multi_blocks_handler( } const uint32_t block_index_start = request->first_block_num; - const uint32_t block_index_end = block_index_start + request->block_count; - - const uint32_t block_count = request->block_count + 1; - const uint32_t block_count_max = instance->data->system_info.block_count; - const uint32_t block_count_available = block_count_max - block_index_start; - - if(block_count > block_count_available) { - error = Iso15693_3ErrorInternal; - break; - } + const uint32_t block_index_end = + MIN((block_index_start + request->block_count + 1), + ((uint32_t)instance->data->system_info.block_count - 1)); error = iso15693_3_listener_extension_handler( instance, diff --git a/lib/nfc/protocols/slix/slix.c b/lib/nfc/protocols/slix/slix.c index 533ecff74..f6ce885d4 100644 --- a/lib/nfc/protocols/slix/slix.c +++ b/lib/nfc/protocols/slix/slix.c @@ -14,6 +14,7 @@ #define SLIX_TYPE_INDICATOR_SLIX (0x02U) #define SLIX_TYPE_INDICATOR_SLIX2 (0x01U) +#define SLIX_CAPABILITIES_KEY "Capabilities" #define SLIX_PASSWORD_READ_KEY "Password Read" #define SLIX_PASSWORD_WRITE_KEY "Password Write" #define SLIX_PASSWORD_PRIVACY_KEY "Password Privacy" @@ -69,6 +70,11 @@ static const SlixTypeFeatures slix_type_features[] = { [SlixTypeSlix2] = SLIX_TYPE_FEATURES_SLIX2, }; +static const char* slix_capabilities_names[SlixCapabilitiesCount] = { + [SlixCapabilitiesDefault] = "Default", + [SlixCapabilitiesAcceptAllPasswords] = "AcceptAllPasswords", +}; + typedef struct { const char* key; SlixTypeFeatures feature_flag; @@ -110,6 +116,7 @@ void slix_reset(SlixData* data) { furi_check(data); iso15693_3_reset(data->iso15693_3_data); + data->capabilities = SlixCapabilitiesDefault; slix_password_set_defaults(data->passwords); memset(&data->system_info, 0, sizeof(SlixSystemInfo)); @@ -123,6 +130,7 @@ void slix_copy(SlixData* data, const SlixData* other) { furi_check(other); iso15693_3_copy(data->iso15693_3_data, other->iso15693_3_data); + data->capabilities = other->capabilities; memcpy(data->passwords, other->passwords, sizeof(SlixPassword) * SlixPasswordTypeCount); memcpy(data->signature, other->signature, sizeof(SlixSignature)); @@ -138,6 +146,30 @@ bool slix_verify(SlixData* data, const FuriString* device_type) { return false; } +static bool slix_load_capabilities(SlixData* data, FlipperFormat* ff) { + bool capabilities_loaded = false; + FuriString* capabilities_str = furi_string_alloc(); + + if(!flipper_format_read_string(ff, SLIX_CAPABILITIES_KEY, capabilities_str)) { + if(flipper_format_rewind(ff)) { + data->capabilities = SlixCapabilitiesDefault; + capabilities_loaded = true; + } + } else { + for(size_t i = 0; i < COUNT_OF(slix_capabilities_names); i++) { + if(furi_string_cmp_str(capabilities_str, slix_capabilities_names[i]) == 0) { + data->capabilities = i; + capabilities_loaded = true; + break; + } + } + } + + furi_string_free(capabilities_str); + + return capabilities_loaded; +} + static bool slix_load_passwords(SlixPassword* passwords, SlixType slix_type, FlipperFormat* ff) { bool ret = true; @@ -164,13 +196,14 @@ bool slix_load(SlixData* data, FlipperFormat* ff, uint32_t version) { furi_check(ff); bool loaded = false; - do { if(!iso15693_3_load(data->iso15693_3_data, ff, version)) break; const SlixType slix_type = slix_get_type(data); if(slix_type >= SlixTypeCount) break; + if(!slix_load_capabilities(data, ff)) break; + if(!slix_load_passwords(data->passwords, slix_type, ff)) break; if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_SIGNATURE)) { @@ -220,6 +253,33 @@ bool slix_load(SlixData* data, FlipperFormat* ff, uint32_t version) { return loaded; } +static bool slix_save_capabilities(const SlixData* data, FlipperFormat* ff) { + bool save_success = false; + + FuriString* tmp_str = furi_string_alloc(); + do { + furi_string_set_str( + tmp_str, "SLIX capabilities field affects emulation modes. Possible options: "); + for(size_t i = 0; i < SlixCapabilitiesCount; i++) { + furi_string_cat_str(tmp_str, slix_capabilities_names[i]); + if(i < SlixCapabilitiesCount - 1) { + furi_string_cat(tmp_str, ", "); + } + } + if(!flipper_format_write_comment_cstr(ff, furi_string_get_cstr(tmp_str))) break; + + if(!flipper_format_write_string_cstr( + ff, SLIX_CAPABILITIES_KEY, slix_capabilities_names[data->capabilities])) + break; + + save_success = true; + } while(false); + + furi_string_free(tmp_str); + + return save_success; +} + static bool slix_save_passwords(const SlixPassword* passwords, SlixType slix_type, FlipperFormat* ff) { bool ret = true; @@ -251,6 +311,8 @@ bool slix_save(const SlixData* data, FlipperFormat* ff) { if(!iso15693_3_save(data->iso15693_3_data, ff)) break; if(!flipper_format_write_comment_cstr(ff, SLIX_PROTOCOL_NAME " specific data")) break; + if(!slix_save_capabilities(data, ff)) break; + if(!flipper_format_write_comment_cstr( ff, "Passwords are optional. If a password is omitted, a default value will be used")) diff --git a/lib/nfc/protocols/slix/slix.h b/lib/nfc/protocols/slix/slix.h index 2de26847a..cc2390c6e 100644 --- a/lib/nfc/protocols/slix/slix.h +++ b/lib/nfc/protocols/slix/slix.h @@ -91,12 +91,20 @@ typedef struct { SlixLockBits lock_bits; } SlixSystemInfo; +typedef enum { + SlixCapabilitiesDefault, + SlixCapabilitiesAcceptAllPasswords, + + SlixCapabilitiesCount, +} SlixCapabilities; + typedef struct { Iso15693_3Data* iso15693_3_data; SlixSystemInfo system_info; SlixSignature signature; SlixPassword passwords[SlixPasswordTypeCount]; SlixPrivacy privacy; + SlixCapabilities capabilities; } SlixData; SlixData* slix_alloc(void); diff --git a/lib/nfc/protocols/slix/slix_listener_i.c b/lib/nfc/protocols/slix/slix_listener_i.c index 15ab2cd3c..66c4241cb 100644 --- a/lib/nfc/protocols/slix/slix_listener_i.c +++ b/lib/nfc/protocols/slix/slix_listener_i.c @@ -54,6 +54,13 @@ static SlixError slix_listener_set_password( } SlixListenerSessionState* session_state = &instance->session_state; + + // With AcceptAllPassword capability set skip password validation + if(instance->data->capabilities == SlixCapabilitiesAcceptAllPasswords) { + session_state->password_match[password_type] = true; + break; + } + session_state->password_match[password_type] = (password == slix_get_password(slix_data, password_type)); diff --git a/lib/nfc/protocols/slix/slix_poller.c b/lib/nfc/protocols/slix/slix_poller.c index 3c9a7cce4..aeb1180cf 100644 --- a/lib/nfc/protocols/slix/slix_poller.c +++ b/lib/nfc/protocols/slix/slix_poller.c @@ -114,7 +114,8 @@ static NfcCommand slix_poller_handler_check_privacy_password(SlixPoller* instanc break; } - instance->error = slix_poller_set_password(instance, SlixPasswordTypePrivacy, pwd); + instance->error = slix_poller_set_password( + instance, SlixPasswordTypePrivacy, pwd, instance->random_number); if(instance->error != SlixErrorNone) { command = NfcCommandReset; break; @@ -145,7 +146,8 @@ static NfcCommand slix_poller_handler_privacy_unlock(SlixPoller* instance) { instance->error = slix_poller_get_random_number(instance, &instance->random_number); if(instance->error != SlixErrorNone) break; - instance->error = slix_poller_set_password(instance, SlixPasswordTypePrivacy, pwd); + instance->error = slix_poller_set_password( + instance, SlixPasswordTypePrivacy, pwd, instance->random_number); if(instance->error != SlixErrorNone) { command = NfcCommandReset; break; diff --git a/lib/nfc/protocols/slix/slix_poller.h b/lib/nfc/protocols/slix/slix_poller.h index 4ea7f880d..e78f7882a 100644 --- a/lib/nfc/protocols/slix/slix_poller.h +++ b/lib/nfc/protocols/slix/slix_poller.h @@ -107,12 +107,16 @@ SlixError slix_poller_get_random_number(SlixPoller* instance, SlixRandomNumber* * Must ONLY be used inside the callback function. * * @param[in, out] instance pointer to the instance to be used in the transaction. - * @param[out] type SlixPasswordType instance. - * @param[out] password SlixPassword instance. + * @param[in] type SlixPasswordType instance. + * @param[in] password SlixPassword instance. + * @param[in] random_number SlixRandomNumber instance. * @return SlixErrorNone on success, an error code on failure. */ -SlixError - slix_poller_set_password(SlixPoller* instance, SlixPasswordType type, SlixPassword password); +SlixError slix_poller_set_password( + SlixPoller* instance, + SlixPasswordType type, + SlixPassword password, + SlixRandomNumber random_number); #ifdef __cplusplus } diff --git a/lib/nfc/protocols/slix/slix_poller_i.c b/lib/nfc/protocols/slix/slix_poller_i.c index 9b0b5ec55..ee6912cc4 100644 --- a/lib/nfc/protocols/slix/slix_poller_i.c +++ b/lib/nfc/protocols/slix/slix_poller_i.c @@ -92,8 +92,11 @@ SlixError slix_poller_get_random_number(SlixPoller* instance, SlixRandomNumber* return error; } -SlixError - slix_poller_set_password(SlixPoller* instance, SlixPasswordType type, SlixPassword password) { +SlixError slix_poller_set_password( + SlixPoller* instance, + SlixPasswordType type, + SlixPassword password, + SlixRandomNumber random_number) { furi_assert(instance); bool skip_uid = (type == SlixPasswordTypePrivacy); @@ -102,8 +105,8 @@ SlixError uint8_t password_type = (0x01 << type); bit_buffer_append_byte(instance->tx_buffer, password_type); - uint8_t rn_l = instance->random_number >> 8; - uint8_t rn_h = instance->random_number; + uint8_t rn_l = random_number >> 8; + uint8_t rn_h = random_number; uint32_t double_rand_num = (rn_h << 24) | (rn_l << 16) | (rn_h << 8) | rn_l; uint32_t xored_password = double_rand_num ^ password; uint8_t xored_password_arr[4] = {}; From c673b53e21ea6e8ac57e6cf9823929521d0ddba7 Mon Sep 17 00:00:00 2001 From: WillyJL <49810075+Willy-JL@users.noreply.github.com> Date: Fri, 17 May 2024 17:45:40 +0100 Subject: [PATCH 09/11] JS: Add math module (#3598) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * JS: Add math module * Double constants * Error on argument type mismatch * Fix missing returns * Using sin, exp from c library * asin, acos, pow, sqrt too * Js: tests for math module and various fixes. Co-authored-by: あく Co-authored-by: nminaylov --- applications/system/js_app/application.fam | 8 + .../js_app/examples/apps/Scripts/math.js | 69 ++++ applications/system/js_app/js_thread.c | 2 +- applications/system/js_app/modules/js_math.c | 354 ++++++++++++++++++ lib/mjs/mjs_core.c | 2 +- 5 files changed, 433 insertions(+), 2 deletions(-) create mode 100644 applications/system/js_app/examples/apps/Scripts/math.js create mode 100644 applications/system/js_app/modules/js_math.c diff --git a/applications/system/js_app/application.fam b/applications/system/js_app/application.fam index a955ef355..920e888cc 100644 --- a/applications/system/js_app/application.fam +++ b/applications/system/js_app/application.fam @@ -54,3 +54,11 @@ App( requires=["js_app"], sources=["modules/js_submenu.c"], ) + +App( + appid="js_math", + apptype=FlipperAppType.PLUGIN, + entry_point="js_math_ep", + requires=["js_app"], + sources=["modules/js_math.c"], +) diff --git a/applications/system/js_app/examples/apps/Scripts/math.js b/applications/system/js_app/examples/apps/Scripts/math.js new file mode 100644 index 000000000..c5a0bf18d --- /dev/null +++ b/applications/system/js_app/examples/apps/Scripts/math.js @@ -0,0 +1,69 @@ +let math = require("math"); + +print("math.abs(-5):", math.abs(-5)); +print("math.acos(0.5):", math.acos(0.5)); +print("math.acosh(2):", math.acosh(2)); +print("math.asin(0.5):", math.asin(0.5)); +print("math.asinh(2):", math.asinh(2)); +print("math.atan(1):", math.atan(1)); +print("math.atan2(1, 1):", math.atan2(1, 1)); +print("math.atanh(0.5):", math.atanh(0.5)); +print("math.cbrt(27):", math.cbrt(27)); +print("math.ceil(5.3):", math.ceil(5.3)); +print("math.clz32(1):", math.clz32(1)); +print("math.cos(math.PI):", math.cos(math.PI)); +print("math.exp(1):", math.exp(1)); +print("math.floor(5.7):", math.floor(5.7)); +print("math.max(3, 5):", math.max(3, 5)); +print("math.min(3, 5):", math.min(3, 5)); +print("math.pow(2, 3):", math.pow(2, 3)); +print("math.random():", math.random()); +print("math.sign(-5):", math.sign(-5)); +print("math.sin(math.PI/2):", math.sin(math.PI / 2)); +print("math.sqrt(25):", math.sqrt(25)); +print("math.trunc(5.7):", math.trunc(5.7)); + +// Unit tests. Please add more if you have time and knowledge. +// math.EPSILON on Flipper Zero is 2.22044604925031308085e-16 + +let succeeded = 0; +let failed = 0; + +function test(text, result, expected, epsilon) { + let is_equal = math.is_equal(result, expected, epsilon); + if (is_equal) { + succeeded += 1; + } else { + failed += 1; + print(text, "expected", expected, "got", result); + } +} + +test("math.abs(5)", math.abs(-5), 5, math.EPSILON); +test("math.abs(0.5)", math.abs(-0.5), 0.5, math.EPSILON); +test("math.abs(5)", math.abs(5), 5, math.EPSILON); +test("math.abs(-0.5)", math.abs(0.5), 0.5, math.EPSILON); +test("math.acos(0.5)", math.acos(0.5), 1.0471975511965976, math.EPSILON); +test("math.acosh(2)", math.acosh(2), 1.3169578969248166, math.EPSILON); +test("math.asin(0.5)", math.asin(0.5), 0.5235987755982988, math.EPSILON); +test("math.asinh(2)", math.asinh(2), 1.4436354751788103, math.EPSILON); +test("math.atan(1)", math.atan(1), 0.7853981633974483, math.EPSILON); +test("math.atan2(1, 1)", math.atan2(1, 1), 0.7853981633974483, math.EPSILON); +test("math.atanh(0.5)", math.atanh(0.5), 0.5493061443340549, math.EPSILON); +test("math.cbrt(27)", math.cbrt(27), 3, math.EPSILON); +test("math.ceil(5.3)", math.ceil(5.3), 6, math.EPSILON); +test("math.clz32(1)", math.clz32(1), 31, math.EPSILON); +test("math.floor(5.7)", math.floor(5.7), 5, math.EPSILON); +test("math.max(3, 5)", math.max(3, 5), 5, math.EPSILON); +test("math.min(3, 5)", math.min(3, 5), 3, math.EPSILON); +test("math.pow(2, 3)", math.pow(2, 3), 8, math.EPSILON); +test("math.sign(-5)", math.sign(-5), -1, math.EPSILON); +test("math.sqrt(25)", math.sqrt(25), 5, math.EPSILON); +test("math.trunc(5.7)", math.trunc(5.7), 5, math.EPSILON); +test("math.cos(math.PI)", math.cos(math.PI), -1, math.EPSILON * 18); // Error 3.77475828372553223744e-15 +test("math.exp(1)", math.exp(1), 2.718281828459045, math.EPSILON * 2); // Error 4.44089209850062616169e-16 +test("math.sin(math.PI / 2)", math.sin(math.PI / 2), 1, math.EPSILON * 4.5); // Error 9.99200722162640886381e-16 + +if (failed > 0) { + print("!!!", failed, "Unit tests failed !!!"); +} \ No newline at end of file diff --git a/applications/system/js_app/js_thread.c b/applications/system/js_app/js_thread.c index 759d63b0e..78b6f6ff4 100644 --- a/applications/system/js_app/js_thread.c +++ b/applications/system/js_app/js_thread.c @@ -285,7 +285,7 @@ static int32_t js_thread(void* arg) { } const char* stack_trace = mjs_get_stack_trace(mjs); if(stack_trace != NULL) { - FURI_LOG_E(TAG, "Stack trace:\n%s", stack_trace); + FURI_LOG_E(TAG, "Stack trace:\r\n%s", stack_trace); if(worker->app_callback) { worker->app_callback(JsThreadEventErrorTrace, stack_trace, worker->context); } diff --git a/applications/system/js_app/modules/js_math.c b/applications/system/js_app/modules/js_math.c new file mode 100644 index 000000000..766156818 --- /dev/null +++ b/applications/system/js_app/modules/js_math.c @@ -0,0 +1,354 @@ +#include "../js_modules.h" +#include "furi_hal_random.h" +#include + +#define JS_MATH_PI ((double)M_PI) +#define JS_MATH_E ((double)M_E) +#define JS_MATH_EPSILON ((double)DBL_EPSILON) + +#define TAG "JsMath" + +static void ret_bad_args(struct mjs* mjs, const char* error) { + mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "%s", error); + mjs_return(mjs, MJS_UNDEFINED); +} + +static bool check_args(struct mjs* mjs, size_t count) { + size_t num_args = mjs_nargs(mjs); + if(num_args != count) { + ret_bad_args(mjs, "Wrong argument count"); + return false; + } + for(size_t i = 0; i < count; i++) { + if(!mjs_is_number(mjs_arg(mjs, i))) { + ret_bad_args(mjs, "Wrong argument type"); + return false; + } + } + return true; +} + +void js_math_is_equal(struct mjs* mjs) { + if(!check_args(mjs, 3)) { + return; + } + + double a = mjs_get_double(mjs, mjs_arg(mjs, 0)); + double b = mjs_get_double(mjs, mjs_arg(mjs, 1)); + double e = mjs_get_double(mjs, mjs_arg(mjs, 2)); + double f = fabs(a - b); + + mjs_return(mjs, mjs_mk_boolean(mjs, (f <= e))); +} + +void js_math_abs(struct mjs* mjs) { + if(!check_args(mjs, 1)) { + return; + } + + double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); + + mjs_return(mjs, mjs_mk_number(mjs, fabs(x))); +} + +void js_math_acos(struct mjs* mjs) { + if(!check_args(mjs, 1)) { + return; + } + + double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); + if(x < (double)-1. || x > (double)1.) { + ret_bad_args(mjs, "Invalid input value for math.acos"); + return; + } + + mjs_return(mjs, mjs_mk_number(mjs, acos(x))); +} + +void js_math_acosh(struct mjs* mjs) { + if(!check_args(mjs, 1)) { + return; + } + + double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); + if(x < (double)1.) { + ret_bad_args(mjs, "Invalid input value for math.acosh"); + return; + } + + mjs_return(mjs, mjs_mk_number(mjs, log(x + sqrt(x * x - (double)1.)))); +} + +void js_math_asin(struct mjs* mjs) { + if(!check_args(mjs, 1)) { + return; + } + + double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); + + mjs_return(mjs, mjs_mk_number(mjs, asin(x))); +} + +void js_math_asinh(struct mjs* mjs) { + if(!check_args(mjs, 1)) { + return; + } + + double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); + + mjs_return(mjs, mjs_mk_number(mjs, log(x + sqrt(x * x + (double)1.)))); +} + +void js_math_atan(struct mjs* mjs) { + if(!check_args(mjs, 1)) { + return; + } + + double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); + + mjs_return(mjs, mjs_mk_number(mjs, atan(x))); +} + +void js_math_atan2(struct mjs* mjs) { + if(!check_args(mjs, 2)) { + return; + } + + double y = mjs_get_double(mjs, mjs_arg(mjs, 0)); + double x = mjs_get_double(mjs, mjs_arg(mjs, 1)); + + mjs_return(mjs, mjs_mk_number(mjs, atan2(y, x))); +} + +void js_math_atanh(struct mjs* mjs) { + if(!check_args(mjs, 1)) { + return; + } + + double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); + if(x < (double)-1. || x > (double)1.) { + ret_bad_args(mjs, "Invalid input value for math.atanh"); + return; + } + + mjs_return(mjs, mjs_mk_number(mjs, (double)0.5 * log(((double)1. + x) / ((double)1. - x)))); +} + +void js_math_cbrt(struct mjs* mjs) { + if(!check_args(mjs, 1)) { + return; + } + + double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); + + mjs_return(mjs, mjs_mk_number(mjs, cbrt(x))); +} + +void js_math_ceil(struct mjs* mjs) { + if(!check_args(mjs, 1)) { + return; + } + + double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); + mjs_return(mjs, mjs_mk_number(mjs, ceil(x))); +} + +void js_math_clz32(struct mjs* mjs) { + if(!check_args(mjs, 1)) { + return; + } + + unsigned int x = (unsigned int)mjs_get_int(mjs, mjs_arg(mjs, 0)); + int count = 0; + while(x) { + x >>= 1; + count++; + } + + mjs_return(mjs, mjs_mk_number(mjs, 32 - count)); +} + +void js_math_cos(struct mjs* mjs) { + if(!check_args(mjs, 1)) { + return; + } + + double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); + + mjs_return(mjs, mjs_mk_number(mjs, cos(x))); +} + +void js_math_exp(struct mjs* mjs) { + if(!check_args(mjs, 1)) { + return; + } + + double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); + + mjs_return(mjs, mjs_mk_number(mjs, exp(x))); +} + +void js_math_floor(struct mjs* mjs) { + if(!check_args(mjs, 1)) { + return; + } + + double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); + + mjs_return(mjs, mjs_mk_number(mjs, floor(x))); +} + +void js_math_log(struct mjs* mjs) { + if(!check_args(mjs, 1)) { + return; + } + + double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); + if(x <= 0) { + ret_bad_args(mjs, "Invalid input value for math.log"); + return; + } + + mjs_return(mjs, mjs_mk_number(mjs, log(x))); +} + +void js_math_max(struct mjs* mjs) { + if(!check_args(mjs, 2)) { + return; + } + + double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); + double y = mjs_get_double(mjs, mjs_arg(mjs, 1)); + + mjs_return(mjs, mjs_mk_number(mjs, x > y ? x : y)); +} + +void js_math_min(struct mjs* mjs) { + if(!check_args(mjs, 2)) { + return; + } + + double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); + double y = mjs_get_double(mjs, mjs_arg(mjs, 1)); + + mjs_return(mjs, mjs_mk_number(mjs, x < y ? x : y)); +} + +void js_math_pow(struct mjs* mjs) { + if(!check_args(mjs, 2)) { + return; + } + + double base = mjs_get_double(mjs, mjs_arg(mjs, 0)); + double exponent = mjs_get_double(mjs, mjs_arg(mjs, 1)); + + mjs_return(mjs, mjs_mk_number(mjs, pow(base, exponent))); +} + +void js_math_random(struct mjs* mjs) { + if(!check_args(mjs, 0)) { + return; + } + + // double clearly provides more bits for entropy then we pack + // 32bit should be enough for now, but fix it maybe + const uint32_t random_val = furi_hal_random_get(); + double rnd = (double)random_val / (double)FURI_HAL_RANDOM_MAX; + + mjs_return(mjs, mjs_mk_number(mjs, rnd)); +} + +void js_math_sign(struct mjs* mjs) { + if(!check_args(mjs, 1)) { + return; + } + + double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); + + mjs_return( + mjs, + mjs_mk_number(mjs, x == (double)0. ? 0 : (x < (double)0. ? (double)-1.0 : (double)1.0))); +} + +void js_math_sin(struct mjs* mjs) { + if(!check_args(mjs, 1)) { + return; + } + + double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); + + mjs_return(mjs, mjs_mk_number(mjs, sin(x))); +} + +void js_math_sqrt(struct mjs* mjs) { + if(!check_args(mjs, 1)) { + return; + } + + double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); + if(x < (double)0.) { + ret_bad_args(mjs, "Invalid input value for math.sqrt"); + return; + } + + mjs_return(mjs, mjs_mk_number(mjs, sqrt(x))); +} + +void js_math_trunc(struct mjs* mjs) { + if(!check_args(mjs, 1)) { + return; + } + + double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); + + mjs_return(mjs, mjs_mk_number(mjs, x < (double)0. ? ceil(x) : floor(x))); +} + +static void* js_math_create(struct mjs* mjs, mjs_val_t* object) { + mjs_val_t math_obj = mjs_mk_object(mjs); + mjs_set(mjs, math_obj, "is_equal", ~0, MJS_MK_FN(js_math_is_equal)); + mjs_set(mjs, math_obj, "abs", ~0, MJS_MK_FN(js_math_abs)); + mjs_set(mjs, math_obj, "acos", ~0, MJS_MK_FN(js_math_acos)); + mjs_set(mjs, math_obj, "acosh", ~0, MJS_MK_FN(js_math_acosh)); + mjs_set(mjs, math_obj, "asin", ~0, MJS_MK_FN(js_math_asin)); + mjs_set(mjs, math_obj, "asinh", ~0, MJS_MK_FN(js_math_asinh)); + mjs_set(mjs, math_obj, "atan", ~0, MJS_MK_FN(js_math_atan)); + mjs_set(mjs, math_obj, "atan2", ~0, MJS_MK_FN(js_math_atan2)); + mjs_set(mjs, math_obj, "atanh", ~0, MJS_MK_FN(js_math_atanh)); + mjs_set(mjs, math_obj, "cbrt", ~0, MJS_MK_FN(js_math_cbrt)); + mjs_set(mjs, math_obj, "ceil", ~0, MJS_MK_FN(js_math_ceil)); + mjs_set(mjs, math_obj, "clz32", ~0, MJS_MK_FN(js_math_clz32)); + mjs_set(mjs, math_obj, "cos", ~0, MJS_MK_FN(js_math_cos)); + mjs_set(mjs, math_obj, "exp", ~0, MJS_MK_FN(js_math_exp)); + mjs_set(mjs, math_obj, "floor", ~0, MJS_MK_FN(js_math_floor)); + mjs_set(mjs, math_obj, "log", ~0, MJS_MK_FN(js_math_log)); + mjs_set(mjs, math_obj, "max", ~0, MJS_MK_FN(js_math_max)); + mjs_set(mjs, math_obj, "min", ~0, MJS_MK_FN(js_math_min)); + mjs_set(mjs, math_obj, "pow", ~0, MJS_MK_FN(js_math_pow)); + mjs_set(mjs, math_obj, "random", ~0, MJS_MK_FN(js_math_random)); + mjs_set(mjs, math_obj, "sign", ~0, MJS_MK_FN(js_math_sign)); + mjs_set(mjs, math_obj, "sin", ~0, MJS_MK_FN(js_math_sin)); + mjs_set(mjs, math_obj, "sqrt", ~0, MJS_MK_FN(js_math_sqrt)); + mjs_set(mjs, math_obj, "trunc", ~0, MJS_MK_FN(js_math_trunc)); + mjs_set(mjs, math_obj, "PI", ~0, mjs_mk_number(mjs, JS_MATH_PI)); + mjs_set(mjs, math_obj, "E", ~0, mjs_mk_number(mjs, JS_MATH_E)); + mjs_set(mjs, math_obj, "EPSILON", ~0, mjs_mk_number(mjs, JS_MATH_EPSILON)); + *object = math_obj; + return (void*)1; +} + +static const JsModuleDescriptor js_math_desc = { + "math", + js_math_create, + NULL, +}; + +static const FlipperAppPluginDescriptor plugin_descriptor = { + .appid = PLUGIN_APP_ID, + .ep_api_version = PLUGIN_API_VERSION, + .entry_point = &js_math_desc, +}; + +const FlipperAppPluginDescriptor* js_math_ep(void) { + return &plugin_descriptor; +} diff --git a/lib/mjs/mjs_core.c b/lib/mjs/mjs_core.c index aae196599..bcdcb364a 100644 --- a/lib/mjs/mjs_core.c +++ b/lib/mjs/mjs_core.c @@ -280,7 +280,7 @@ static void mjs_append_stack_trace_line(struct mjs* mjs, size_t offset) { const char* filename = mjs_get_bcode_filename_by_offset(mjs, offset); int line_no = mjs_get_lineno_by_offset(mjs, offset); char* new_line = NULL; - const char* fmt = "at %s:%d\n"; + const char* fmt = "\tat %s:%d\r\n"; if(filename == NULL) { // fprintf( // stderr, From 0d456aa5505ca7361aba50011db30ffaf10633e2 Mon Sep 17 00:00:00 2001 From: WillyJL <49810075+Willy-JL@users.noreply.github.com> Date: Fri, 17 May 2024 18:43:52 +0100 Subject: [PATCH 10/11] JS: Add textbox module (#3597) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * JS: Add textbox module * Using view_holder instead of view_dispatcher, more checks in js_textbox_show * API version sync * Rename emptyText() to clearText() * Keeping view_holder allocated for thread sefety * Js: proper comparision with 0 in js_math_sign * Js: add comments and fix condition race in textbox Co-authored-by: あく Co-authored-by: nminaylov --- applications/system/js_app/application.fam | 8 + .../js_app/examples/apps/Scripts/textbox.js | 30 +++ applications/system/js_app/modules/js_math.c | 3 +- .../system/js_app/modules/js_textbox.c | 220 ++++++++++++++++++ 4 files changed, 260 insertions(+), 1 deletion(-) create mode 100644 applications/system/js_app/examples/apps/Scripts/textbox.js create mode 100644 applications/system/js_app/modules/js_textbox.c diff --git a/applications/system/js_app/application.fam b/applications/system/js_app/application.fam index 920e888cc..a7ae5c7c7 100644 --- a/applications/system/js_app/application.fam +++ b/applications/system/js_app/application.fam @@ -62,3 +62,11 @@ App( requires=["js_app"], sources=["modules/js_math.c"], ) + +App( + appid="js_textbox", + apptype=FlipperAppType.PLUGIN, + entry_point="js_textbox_ep", + requires=["js_app"], + sources=["modules/js_textbox.c"], +) diff --git a/applications/system/js_app/examples/apps/Scripts/textbox.js b/applications/system/js_app/examples/apps/Scripts/textbox.js new file mode 100644 index 000000000..6caf37234 --- /dev/null +++ b/applications/system/js_app/examples/apps/Scripts/textbox.js @@ -0,0 +1,30 @@ +let textbox = require("textbox"); + +// You should set config before adding text +// Focus (start / end), Font (text / hex) +textbox.setConfig("end", "text"); + +// Can make sure it's cleared before showing, in case of reusing in same script +// (Closing textbox already clears the text, but maybe you added more in a loop for example) +textbox.clearText(); + +// Add default text +textbox.addText("Example dynamic updating textbox\n"); + +// Non-blocking, can keep updating text after, can close in JS or in GUI +textbox.show(); + +let i = 0; +while (textbox.isOpen() && i < 20) { + print("console", i++); + + // Add text to textbox buffer + textbox.addText("textbox " + to_string(i) + "\n"); + + delay(500); +} + +// If not closed by user (instead i < 20 is false above), close forcefully +if (textbox.isOpen()) { + textbox.close(); +} diff --git a/applications/system/js_app/modules/js_math.c b/applications/system/js_app/modules/js_math.c index 766156818..b4c1cdca2 100644 --- a/applications/system/js_app/modules/js_math.c +++ b/applications/system/js_app/modules/js_math.c @@ -267,7 +267,8 @@ void js_math_sign(struct mjs* mjs) { mjs_return( mjs, - mjs_mk_number(mjs, x == (double)0. ? 0 : (x < (double)0. ? (double)-1.0 : (double)1.0))); + mjs_mk_number( + mjs, fabs(x) <= JS_MATH_EPSILON ? 0 : (x < (double)0. ? (double)-1.0 : (double)1.0))); } void js_math_sin(struct mjs* mjs) { diff --git a/applications/system/js_app/modules/js_textbox.c b/applications/system/js_app/modules/js_textbox.c new file mode 100644 index 000000000..d15cd2779 --- /dev/null +++ b/applications/system/js_app/modules/js_textbox.c @@ -0,0 +1,220 @@ +#include +#include +#include "../js_modules.h" + +typedef struct { + TextBox* text_box; + ViewHolder* view_holder; + FuriString* text; + bool is_shown; +} JsTextboxInst; + +static JsTextboxInst* get_this_ctx(struct mjs* mjs) { + mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0); + JsTextboxInst* textbox = mjs_get_ptr(mjs, obj_inst); + furi_assert(textbox); + return textbox; +} + +static void ret_bad_args(struct mjs* mjs, const char* error) { + mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "%s", error); + mjs_return(mjs, MJS_UNDEFINED); +} + +static bool check_arg_count(struct mjs* mjs, size_t count) { + size_t num_args = mjs_nargs(mjs); + if(num_args != count) { + ret_bad_args(mjs, "Wrong argument count"); + return false; + } + return true; +} + +static void js_textbox_set_config(struct mjs* mjs) { + JsTextboxInst* textbox = get_this_ctx(mjs); + if(!check_arg_count(mjs, 2)) return; + + TextBoxFocus set_focus = TextBoxFocusStart; + mjs_val_t focus_arg = mjs_arg(mjs, 0); + const char* focus = mjs_get_string(mjs, &focus_arg, NULL); + if(!focus) { + ret_bad_args(mjs, "Focus must be a string"); + return; + } else { + if(!strncmp(focus, "start", strlen("start"))) { + set_focus = TextBoxFocusStart; + } else if(!strncmp(focus, "end", strlen("end"))) { + set_focus = TextBoxFocusEnd; + } else { + ret_bad_args(mjs, "Bad focus value"); + return; + } + } + + TextBoxFont set_font = TextBoxFontText; + mjs_val_t font_arg = mjs_arg(mjs, 1); + const char* font = mjs_get_string(mjs, &font_arg, NULL); + if(!font) { + ret_bad_args(mjs, "Font must be a string"); + return; + } else { + if(!strncmp(font, "text", strlen("text"))) { + set_font = TextBoxFontText; + } else if(!strncmp(font, "hex", strlen("hex"))) { + set_font = TextBoxFontHex; + } else { + ret_bad_args(mjs, "Bad font value"); + return; + } + } + + text_box_set_focus(textbox->text_box, set_focus); + text_box_set_font(textbox->text_box, set_font); + + mjs_return(mjs, MJS_UNDEFINED); +} + +static void js_textbox_add_text(struct mjs* mjs) { + JsTextboxInst* textbox = get_this_ctx(mjs); + if(!check_arg_count(mjs, 1)) return; + + mjs_val_t text_arg = mjs_arg(mjs, 0); + size_t text_len = 0; + const char* text = mjs_get_string(mjs, &text_arg, &text_len); + if(!text) { + ret_bad_args(mjs, "Text must be a string"); + return; + } + + // Avoid condition race between GUI and JS thread + text_box_set_text(textbox->text_box, ""); + + size_t new_len = furi_string_size(textbox->text) + text_len; + if(new_len >= 4096) { + furi_string_right(textbox->text, new_len / 2); + } + + furi_string_cat(textbox->text, text); + + text_box_set_text(textbox->text_box, furi_string_get_cstr(textbox->text)); + + mjs_return(mjs, MJS_UNDEFINED); +} + +static void js_textbox_clear_text(struct mjs* mjs) { + JsTextboxInst* textbox = get_this_ctx(mjs); + if(!check_arg_count(mjs, 0)) return; + + // Avoid condition race between GUI and JS thread + text_box_set_text(textbox->text_box, ""); + + furi_string_reset(textbox->text); + + text_box_set_text(textbox->text_box, furi_string_get_cstr(textbox->text)); + + mjs_return(mjs, MJS_UNDEFINED); +} + +static void js_textbox_is_open(struct mjs* mjs) { + JsTextboxInst* textbox = get_this_ctx(mjs); + if(!check_arg_count(mjs, 0)) return; + + mjs_return(mjs, mjs_mk_boolean(mjs, textbox->is_shown)); +} + +static void textbox_callback(void* context, uint32_t arg) { + UNUSED(arg); + JsTextboxInst* textbox = context; + view_holder_stop(textbox->view_holder); + textbox->is_shown = false; +} + +static void textbox_exit(void* context) { + JsTextboxInst* textbox = context; + // Using timer to schedule view_holder stop, will not work under high CPU load + furi_timer_pending_callback(textbox_callback, textbox, 0); +} + +static void js_textbox_show(struct mjs* mjs) { + JsTextboxInst* textbox = get_this_ctx(mjs); + if(!check_arg_count(mjs, 0)) return; + + if(textbox->is_shown) { + mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, "Textbox is already shown"); + mjs_return(mjs, MJS_UNDEFINED); + return; + } + + view_holder_start(textbox->view_holder); + textbox->is_shown = true; + + mjs_return(mjs, MJS_UNDEFINED); +} + +static void js_textbox_close(struct mjs* mjs) { + JsTextboxInst* textbox = get_this_ctx(mjs); + if(!check_arg_count(mjs, 0)) return; + + view_holder_stop(textbox->view_holder); + textbox->is_shown = false; + + mjs_return(mjs, MJS_UNDEFINED); +} + +static void* js_textbox_create(struct mjs* mjs, mjs_val_t* object) { + JsTextboxInst* textbox = malloc(sizeof(JsTextboxInst)); + + mjs_val_t textbox_obj = mjs_mk_object(mjs); + mjs_set(mjs, textbox_obj, INST_PROP_NAME, ~0, mjs_mk_foreign(mjs, textbox)); + mjs_set(mjs, textbox_obj, "setConfig", ~0, MJS_MK_FN(js_textbox_set_config)); + mjs_set(mjs, textbox_obj, "addText", ~0, MJS_MK_FN(js_textbox_add_text)); + mjs_set(mjs, textbox_obj, "clearText", ~0, MJS_MK_FN(js_textbox_clear_text)); + mjs_set(mjs, textbox_obj, "isOpen", ~0, MJS_MK_FN(js_textbox_is_open)); + mjs_set(mjs, textbox_obj, "show", ~0, MJS_MK_FN(js_textbox_show)); + mjs_set(mjs, textbox_obj, "close", ~0, MJS_MK_FN(js_textbox_close)); + + textbox->text = furi_string_alloc(); + textbox->text_box = text_box_alloc(); + + Gui* gui = furi_record_open(RECORD_GUI); + textbox->view_holder = view_holder_alloc(); + view_holder_attach_to_gui(textbox->view_holder, gui); + view_holder_set_back_callback(textbox->view_holder, textbox_exit, textbox); + view_holder_set_view(textbox->view_holder, text_box_get_view(textbox->text_box)); + + *object = textbox_obj; + return textbox; +} + +static void js_textbox_destroy(void* inst) { + JsTextboxInst* textbox = inst; + + view_holder_stop(textbox->view_holder); + view_holder_free(textbox->view_holder); + textbox->view_holder = NULL; + + furi_record_close(RECORD_GUI); + + text_box_reset(textbox->text_box); + furi_string_reset(textbox->text); + + text_box_free(textbox->text_box); + furi_string_free(textbox->text); + free(textbox); +} + +static const JsModuleDescriptor js_textbox_desc = { + "textbox", + js_textbox_create, + js_textbox_destroy, +}; + +static const FlipperAppPluginDescriptor textbox_plugin_descriptor = { + .appid = PLUGIN_APP_ID, + .ep_api_version = PLUGIN_API_VERSION, + .entry_point = &js_textbox_desc, +}; + +const FlipperAppPluginDescriptor* js_textbox_ep(void) { + return &textbox_plugin_descriptor; +} \ No newline at end of file From de4b086083b8f9914631969c236505ac8c83ee5d Mon Sep 17 00:00:00 2001 From: WillyJL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 18 May 2024 19:40:01 +0100 Subject: [PATCH 11/11] Fix calling both `view_free_model()` and `view_free()` (#3655) --- applications/services/gui/view.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/applications/services/gui/view.c b/applications/services/gui/view.c index 8fc5c2699..a35c2fa38 100644 --- a/applications/services/gui/view.c +++ b/applications/services/gui/view.c @@ -97,10 +97,11 @@ void view_free_model(View* view) { furi_mutex_free(model->mutex); free(model->data); free(model); - view->model = NULL; } else { furi_crash(); } + view->model = NULL; + view->model_type = ViewModelTypeNone; } void* view_get_model(View* view) {