From d5339f8270336e362a0dd9af69380845fa4bb95d Mon Sep 17 00:00:00 2001 From: gornekich Date: Mon, 20 May 2024 16:13:20 +0100 Subject: [PATCH 1/3] [FL-3816] Text Box rework (#3642) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * text box: rework text box to use less memory * text box: optimize lines iteration * text box: fix back scrolling * text box: add end focus support * text box: allocate index buffer on stack instead of heap * text box: fix index decrement * apps: add text box debug app * debug: rework text box view app with view stack * debug app: rename text_box_test app to text_box_element_test * text box: rework text box module * debug: update text box view test * text box: fix end focus initial offset * debug: fix memory leak in text box view test app Co-authored-by: あく Co-authored-by: hedger --- .../text_box_element_test/application.fam | 10 + .../text_box_element_test.c} | 2 +- .../application.fam | 6 +- .../text_box_view_test/text_box_view_test.c | 133 ++++++++ applications/services/gui/modules/text_box.c | 317 ++++++++++++------ 5 files changed, 361 insertions(+), 107 deletions(-) create mode 100644 applications/debug/text_box_element_test/application.fam rename applications/debug/{text_box_test/text_box_test.c => text_box_element_test/text_box_element_test.c} (98%) rename applications/debug/{text_box_test => text_box_view_test}/application.fam (55%) create mode 100644 applications/debug/text_box_view_test/text_box_view_test.c diff --git a/applications/debug/text_box_element_test/application.fam b/applications/debug/text_box_element_test/application.fam new file mode 100644 index 000000000..5e1abcddc --- /dev/null +++ b/applications/debug/text_box_element_test/application.fam @@ -0,0 +1,10 @@ +App( + appid="text_box_element_test", + name="Text Box Element Test", + apptype=FlipperAppType.DEBUG, + entry_point="text_box_element_test_app", + requires=["gui"], + stack_size=1 * 1024, + order=140, + fap_category="Debug", +) diff --git a/applications/debug/text_box_test/text_box_test.c b/applications/debug/text_box_element_test/text_box_element_test.c similarity index 98% rename from applications/debug/text_box_test/text_box_test.c rename to applications/debug/text_box_element_test/text_box_element_test.c index b980f686e..2b9475d2b 100644 --- a/applications/debug/text_box_test/text_box_test.c +++ b/applications/debug/text_box_element_test/text_box_element_test.c @@ -71,7 +71,7 @@ static void text_box_test_input_callback(InputEvent* input_event, void* ctx) { furi_message_queue_put(event_queue, input_event, FuriWaitForever); } -int32_t text_box_test_app(void* p) { +int32_t text_box_element_test_app(void* p) { UNUSED(p); FuriMessageQueue* event_queue = furi_message_queue_alloc(32, sizeof(InputEvent)); furi_check(event_queue); diff --git a/applications/debug/text_box_test/application.fam b/applications/debug/text_box_view_test/application.fam similarity index 55% rename from applications/debug/text_box_test/application.fam rename to applications/debug/text_box_view_test/application.fam index 823c21d06..e356a278e 100644 --- a/applications/debug/text_box_test/application.fam +++ b/applications/debug/text_box_view_test/application.fam @@ -1,8 +1,8 @@ App( - appid="text_box_test", - name="Text Box Test", + appid="text_box_view_test", + name="Text Box View Test", apptype=FlipperAppType.DEBUG, - entry_point="text_box_test_app", + entry_point="text_box_view_test_app", requires=["gui"], stack_size=1 * 1024, order=140, diff --git a/applications/debug/text_box_view_test/text_box_view_test.c b/applications/debug/text_box_view_test/text_box_view_test.c new file mode 100644 index 000000000..4414835ec --- /dev/null +++ b/applications/debug/text_box_view_test/text_box_view_test.c @@ -0,0 +1,133 @@ +#include +#include +#include +#include +#include + +#define TAG "TextBoxViewTest" + +typedef struct { + TextBoxFont font; + TextBoxFocus focus; + const char* text; +} TextBoxViewTestContent; + +static const TextBoxViewTestContent text_box_view_test_content_arr[] = { + { + .font = TextBoxFontText, + .focus = TextBoxFocusStart, + .text = "Hello, let's test text box. Press Right and Left to switch content", + }, + { + .font = TextBoxFontText, + .focus = TextBoxFocusStart, + .text = + "Verify that symbols don't overlap borders: llllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllend", + }, + { + .font = TextBoxFontText, + .focus = TextBoxFocusStart, + .text = + "\n\n\n Start from several newline chars. Verify that scrolling doesn't break.\n\n\n\n\nThe end", + }, + { + .font = TextBoxFontText, + .focus = TextBoxFocusStart, + .text = + "Let's test big text.\n\n The ARM Cortex-M is a group of 32-bit RISC ARM processor cores licensed by ARM Limited. These cores are optimized for low-cost and energy-efficient integrated circuits, which have been embedded in tens of billions of consumer devices.[1] Though they are most often the main component of microcontroller chips, sometimes they are embedded inside other types of chips too. The Cortex-M family consists of Cortex-M0,[2] Cortex-M0+,[3] Cortex-M1,[4] Cortex-M3,[5] Cortex-M4,[6] Cortex-M7,[7] Cortex-M23,[8] Cortex-M33,[9] Cortex-M35P,[10] Cortex-M52,[11] Cortex-M55,[12] Cortex-M85.[13] A floating-point unit (FPU) option is available for Cortex-M4 / M7 / M33 / M35P / M52 / M55 / M85 cores, and when included in the silicon these cores are sometimes known as \"Cortex-MxF\", where 'x' is the core variant.\n\nThe ARM Cortex-M family are ARM microprocessor cores that are designed for use in microcontrollers, ASICs, ASSPs, FPGAs, and SoCs. Cortex-M cores are commonly used as dedicated microcontroller chips, but also are hidden inside of SoC chips as power management controllers, I/O controllers, system controllers, touch screen controllers, smart battery controllers, and sensor controllers. The main difference from Cortex-A cores is that Cortex-M cores have no memory management unit (MMU) for virtual memory, considered essential for full-fledged operating systems. Cortex-M programs instead run bare metal or on one of the many real-time operating systems which support a Cortex-M.Though 8-bit microcontrollers were very popular in the past, Cortex-M has slowly been chipping away at the 8-bit market as the prices of low-end Cortex-M chips have moved downward. Cortex-M have become a popular replacements for 8-bit chips in applications that benefit from 32-bit math operations, and replacing older legacy ARM cores such as ARM7 and ARM9.", + }, + { + .font = TextBoxFontText, + .focus = TextBoxFocusEnd, + .text = + "The same but with EndFocus\n\n The ARM Cortex-M is a group of 32-bit RISC ARM processor cores licensed by ARM Limited. These cores are optimized for low-cost and energy-efficient integrated circuits, which have been embedded in tens of billions of consumer devices.[1] Though they are most often the main component of microcontroller chips, sometimes they are embedded inside other types of chips too. The Cortex-M family consists of Cortex-M0,[2] Cortex-M0+,[3] Cortex-M1,[4] Cortex-M3,[5] Cortex-M4,[6] Cortex-M7,[7] Cortex-M23,[8] Cortex-M33,[9] Cortex-M35P,[10] Cortex-M52,[11] Cortex-M55,[12] Cortex-M85.[13] A floating-point unit (FPU) option is available for Cortex-M4 / M7 / M33 / M35P / M52 / M55 / M85 cores, and when included in the silicon these cores are sometimes known as \"Cortex-MxF\", where 'x' is the core variant.\n\nThe ARM Cortex-M family are ARM microprocessor cores that are designed for use in microcontrollers, ASICs, ASSPs, FPGAs, and SoCs. Cortex-M cores are commonly used as dedicated microcontroller chips, but also are hidden inside of SoC chips as power management controllers, I/O controllers, system controllers, touch screen controllers, smart battery controllers, and sensor controllers. The main difference from Cortex-A cores is that Cortex-M cores have no memory management unit (MMU) for virtual memory, considered essential for full-fledged operating systems. Cortex-M programs instead run bare metal or on one of the many real-time operating systems which support a Cortex-M.Though 8-bit microcontrollers were very popular in the past, Cortex-M has slowly been chipping away at the 8-bit market as the prices of low-end Cortex-M chips have moved downward. Cortex-M have become a popular replacements for 8-bit chips in applications that benefit from 32-bit math operations, and replacing older legacy ARM cores such as ARM7 and ARM9.", + }, + { + .font = TextBoxFontHex, + .focus = TextBoxFocusEnd, + .text = + "0000 0000 0000 0000\n1111 1111 1111 1111\n2222 2222 2222 2222\n3333 3333 3333 3333\n4444 4444 4444 4444\n5555 5555 5555 5555\n6666 6666 6666 6666\n7777 7777 7777 7777\n8888 8888 8888 8888\n9999 9999 9999 9999\n0000 0000 0000 0000\n1111 1111 1111 1111\n2222 2222 2222 2222\n3333 3333 3333 3333\n4444 4444 4444 4444\n5555 5555 5555 5555\n6666 6666 6666 6666\n7777 7777 7777 7777\n8888 8888 8888 8888\n9999 9999 9999 9999\n0000 0000 0000 0000\n1111 1111 1111 1111\n2222 2222 2222 2222\n3333 3333 3333 3333\n4444 4444 4444 4444\n5555 5555 5555 5555\n6666 6666 6666 6666\n7777 7777 7777 7777\n8888 8888 8888 8888\n9999 9999 9999 9999\n0000 0000 0000 0000\n1111 1111 1111 1111\n2222 2222 2222 2222\n3333 3333 3333 3333\n4444 4444 4444 4444\n5555 5555 5555 5555\n6666 6666 6666 6666\n7777 7777 7777 7777\n8888 8888 8888 8888\n9999 9999 9999 9999\n0000 0000 0000 0000\n1111 1111 1111 1111\n2222 2222 2222 2222\n3333 3333 3333 3333\n4444 4444 4444 4444\n5555 5555 5555 5555\n6666 6666 6666 6666\n7777 7777 7777 7777\n8888 8888 8888 8888\n9999 9999 9999 9999\n0000 0000 0000 0000\n1111 1111 1111 1111\n2222 2222 2222 2222\n3333 3333 3333 3333\n4444 4444 4444 4444\n5555 5555 5555 5555\n6666 6666 6666 6666\n7777 7777 7777 7777\n8888 8888 8888 8888\n9999 9999 9999 9999\n0000 0000 0000 0000\n1111 1111 1111 1111\n2222 2222 2222 2222\n3333 3333 3333 3333\n4444 4444 4444 4444\n5555 5555 5555 5555\n6666 6666 6666 6666\n7777 7777 7777 7777\n8888 8888 8888 8888\n9999 9999 9999 9999\n0000 0000 0000 0000\n1111 1111 1111 1111\n2222 2222 2222 2222\n3333 3333 3333 3333\n4444 4444 4444 4444\n5555 5555 5555 5555\n6666 6666 6666 6666\n7777 7777 7777 7777\n8888 8888 8888 8888\n9999 9999 9999 9999\n0000 0000 0000 0000\n1111 1111 1111 1111\n2222 2222 2222 2222\n3333 3333 3333 3333\n4444 4444 4444 4444\n5555 5555 5555 5555\n6666 6666 6666 6666\n7777 7777 7777 7777\n8888 8888 8888 8888\n9999 9999 9999 9999\n0000 0000 0000 0000\n1111 1111 1111 1111\n2222 2222 2222 2222\n3333 3333 3333 3333\n4444 4444 4444 4444\n5555 5555 5555 5555\n6666 6666 6666 6666\n7777 7777 7777 7777\n8888 8888 8888 8888\n9999 9999 9999 9999\n0000 0000 0000 0000\n1111 1111 1111 1111\n2222 2222 2222 2222\n3333 3333 3333 3333\n4444 4444 4444 4444\n5555 5555 5555 5555\n6666 6666 6666 6666\n7777 7777 7777 7777\n8888 8888 8888 8888\n9999 9999 9999 9999", + }, +}; + +typedef struct { + TextBox* text_box; + ViewDispatcher* view_dispatcher; + size_t current_content_i; +} TextBoxViewTest; + +static void text_box_update_view(TextBoxViewTest* instance) { + text_box_reset(instance->text_box); + + const TextBoxViewTestContent* content = + &text_box_view_test_content_arr[instance->current_content_i]; + text_box_set_font(instance->text_box, content->font); + text_box_set_focus(instance->text_box, content->focus); + text_box_set_text(instance->text_box, content->text); +} + +static bool text_box_switch_view_input_callback(InputEvent* event, void* context) { + bool consumed = false; + TextBoxViewTest* instance = context; + size_t contents_cnt = COUNT_OF(text_box_view_test_content_arr); + + if(event->type == InputTypeShort) { + if(event->key == InputKeyRight) { + if(instance->current_content_i < contents_cnt - 1) { + instance->current_content_i++; + text_box_update_view(instance); + consumed = true; + } + } else if(event->key == InputKeyLeft) { + if(instance->current_content_i > 0) { + instance->current_content_i--; + text_box_update_view(instance); + consumed = true; + } + } else if(event->key == InputKeyBack) { + view_dispatcher_stop(instance->view_dispatcher); + } + } + + return consumed; +} + +int32_t text_box_view_test_app(void* p) { + UNUSED(p); + + Gui* gui = furi_record_open(RECORD_GUI); + ViewDispatcher* view_dispatcher = view_dispatcher_alloc(); + view_dispatcher_attach_to_gui(view_dispatcher, gui, ViewDispatcherTypeFullscreen); + view_dispatcher_enable_queue(view_dispatcher); + + TextBoxViewTest instance = { + .text_box = text_box_alloc(), + .current_content_i = 0, + .view_dispatcher = view_dispatcher, + }; + + text_box_update_view(&instance); + + View* text_box_switch_view = view_alloc(); + view_set_input_callback(text_box_switch_view, text_box_switch_view_input_callback); + view_set_context(text_box_switch_view, &instance); + + ViewStack* view_stack = view_stack_alloc(); + view_stack_add_view(view_stack, text_box_switch_view); + view_stack_add_view(view_stack, text_box_get_view(instance.text_box)); + + view_dispatcher_add_view(view_dispatcher, 0, view_stack_get_view(view_stack)); + view_dispatcher_switch_to_view(view_dispatcher, 0); + + view_dispatcher_run(view_dispatcher); + + view_dispatcher_remove_view(view_dispatcher, 0); + view_dispatcher_free(view_dispatcher); + view_stack_free(view_stack); + view_free(text_box_switch_view); + text_box_free(instance.text_box); + + furi_record_close(RECORD_GUI); + + return 0; +} diff --git a/applications/services/gui/modules/text_box.c b/applications/services/gui/modules/text_box.c index 9c2959b30..954847c65 100644 --- a/applications/services/gui/modules/text_box.c +++ b/applications/services/gui/modules/text_box.c @@ -4,8 +4,13 @@ #include #include -#define TEXT_BOX_MAX_SYMBOL_WIDTH (10) -#define TEXT_BOX_LINE_WIDTH (120) +#define TEXT_BOX_TEXT_WIDTH (120) +#define TEXT_BOX_TEXT_HEIGHT (56) +#define TEXT_BOX_MAX_LINES_PER_SCREEN (10) + +#define TEXT_BOX_LINES_SCROLL_SPEED_MEDIUM (3) +#define TEXT_BOX_LINES_SCROLL_SPEED_FAST (5) +#define TEXT_BOX_LINES_SCROLL_SPEED_SATURATION (9) struct TextBox { View* view; @@ -14,13 +19,19 @@ struct TextBox { }; typedef struct { - const char* text; - char* text_pos; - FuriString* text_formatted; - int32_t scroll_pos; - int32_t scroll_num; TextBoxFont font; TextBoxFocus focus; + const char* text; + + int32_t scroll_pos; + int32_t scroll_num; + int32_t lines_on_screen; + + int32_t line_offset; + int32_t text_offset; + FuriString* text_on_screen; + FuriString* text_line; + bool formatted; } TextBoxModel; @@ -29,20 +40,11 @@ static void text_box_process_down(TextBox* text_box, uint8_t lines) { text_box->view, TextBoxModel * model, { - if(model->scroll_pos < model->scroll_num - lines) { + if(model->scroll_pos + lines < model->scroll_num) { model->scroll_pos += lines; - for(uint8_t i = 0; i < lines; i++) { - // Search next line start - while(*model->text_pos++ != '\n') - ; - } - } else if(lines > 1) { - lines = model->scroll_num - model->scroll_pos - 1; - model->scroll_pos = model->scroll_num - 1; - for(uint8_t i = 0; i < lines; i++) { - // Search next line start - while(*model->text_pos++ != '\n') - ; + } else { + if(model->scroll_num > 0) { + model->scroll_pos = model->scroll_num - 1; } } }, @@ -54,69 +56,196 @@ static void text_box_process_up(TextBox* text_box, uint8_t lines) { text_box->view, TextBoxModel * model, { - if(model->scroll_pos > lines - 1) { + if(model->scroll_pos - lines > 0) { model->scroll_pos -= lines; - for(uint8_t i = 0; i < lines; i++) { - // Reach last symbol of previous line - model->text_pos--; - // Search previous line start - while((model->text_pos != model->text) && (*(--model->text_pos) != '\n')) - ; - if(*model->text_pos == '\n') { - model->text_pos++; - } - } - } else if(lines > 1) { - lines = model->scroll_pos; + } else { model->scroll_pos = 0; - model->text_pos = (char*)model->text; } }, true); } -static void text_box_insert_endline(Canvas* canvas, TextBoxModel* model) { - size_t i = 0; - size_t line_width = 0; - const char* str = model->text; - size_t line_num = 0; +static bool text_box_view_input_callback(InputEvent* event, void* context) { + furi_assert(context); - while(str[i] != '\0') { - char symb = str[i++]; - if(symb != '\n') { + TextBox* text_box = context; + bool consumed = false; + + if(event->type == InputTypeShort || event->type == InputTypeRepeat) { + int32_t scroll_speed = 1; + if(text_box->button_held_for_ticks > TEXT_BOX_LINES_SCROLL_SPEED_FAST) { + if(text_box->button_held_for_ticks % 2) { + scroll_speed = 0; + } else { + scroll_speed = + (text_box->button_held_for_ticks > TEXT_BOX_LINES_SCROLL_SPEED_SATURATION) ? + TEXT_BOX_LINES_SCROLL_SPEED_FAST : + TEXT_BOX_LINES_SCROLL_SPEED_MEDIUM; + } + } + + if(event->key == InputKeyDown) { + text_box_process_down(text_box, scroll_speed); + consumed = true; + } else if(event->key == InputKeyUp) { + text_box_process_up(text_box, scroll_speed); + consumed = true; + } + + text_box->button_held_for_ticks++; + } else if(event->type == InputTypeRelease) { + text_box->button_held_for_ticks = 0; + consumed = true; + } + + return consumed; +} + +static bool text_box_end_of_text_reached(TextBoxModel* model) { + return model->text[model->text_offset] == '\0'; +} + +static bool text_box_start_of_text_reached(TextBoxModel* model) { + return model->text_offset == 0; +} + +static void text_box_seek_next_line(Canvas* canvas, TextBoxModel* model) { + size_t line_width = 0; + + while(!text_box_end_of_text_reached(model)) { + char symb = model->text[model->text_offset]; + if(symb == '\n') { + model->text_offset++; + break; + } else { size_t glyph_width = canvas_glyph_width(canvas, symb); - if(line_width + glyph_width > TEXT_BOX_LINE_WIDTH) { - line_num++; - line_width = 0; - furi_string_push_back(model->text_formatted, '\n'); + if(line_width + glyph_width > TEXT_BOX_TEXT_WIDTH) { + break; } line_width += glyph_width; - } else { - line_num++; - line_width = 0; + model->text_offset++; } - furi_string_push_back(model->text_formatted, symb); } - line_num++; - model->text = furi_string_get_cstr(model->text_formatted); - model->text_pos = (char*)model->text; - size_t lines_on_screen = 56 / canvas_current_font_height(canvas); - if(model->focus == TextBoxFocusEnd && line_num > lines_on_screen) { - // Set text position to 5th line from the end - const char* end = model->text + furi_string_size(model->text_formatted); - for(size_t i = 0; i < line_num - lines_on_screen; i++) { - while(model->text_pos < end) { - if(*model->text_pos++ == '\n') break; - } +} + +static void text_box_seek_end_of_prev_line(TextBoxModel* model) { + do { + if(text_box_start_of_text_reached(model)) break; + model->text_offset--; + if(text_box_start_of_text_reached(model)) break; + if(model->text[model->text_offset] == '\n') { + model->text_offset--; + } + } while(false); +} + +static void text_box_seek_prev_paragraph(TextBoxModel* model) { + while(!text_box_start_of_text_reached(model)) { + if(model->text[model->text_offset] == '\n') { + model->text_offset++; + break; + } + model->text_offset--; + } +} + +static void text_box_seek_prev_line(Canvas* canvas, TextBoxModel* model) { + int32_t start_text_offset = model->text_offset; + + text_box_seek_end_of_prev_line(model); + text_box_seek_prev_paragraph(model); + + int32_t current_text_offset = model->text_offset; + while(true) { + text_box_seek_next_line(canvas, model); + if(model->text_offset == start_text_offset) { + break; + } + current_text_offset = model->text_offset; + } + model->text_offset = current_text_offset; +} + +static void text_box_move_line_offset(Canvas* canvas, TextBoxModel* model, int32_t line_offset) { + if(line_offset >= 0) { + for(int32_t i = 0; i < line_offset; i++) { + text_box_seek_next_line(canvas, model); } - model->scroll_num = line_num - (lines_on_screen - 1); - model->scroll_pos = line_num - lines_on_screen; } else { - model->scroll_num = MAX(line_num - (lines_on_screen - 1), 0u); - model->scroll_pos = 0; + for(int32_t i = 0; i < (-line_offset); i++) { + text_box_seek_prev_line(canvas, model); + } } } +static void text_box_update_screen_text(Canvas* canvas, TextBoxModel* model) { + furi_string_reset(model->text_on_screen); + furi_string_reset(model->text_line); + + int32_t start_text_offset = model->text_offset; + + for(int32_t i = 0; i < model->lines_on_screen; i++) { + int32_t current_line_text_offset = model->text_offset; + text_box_seek_next_line(canvas, model); + int32_t next_line_text_offset = model->text_offset; + furi_string_set_strn( + model->text_line, + &model->text[current_line_text_offset], + next_line_text_offset - current_line_text_offset); + size_t str_len = furi_string_size(model->text_line); + if(furi_string_get_char(model->text_line, str_len - 1) != '\n') { + furi_string_push_back(model->text_line, '\n'); + } + furi_string_cat(model->text_on_screen, model->text_line); + + if(text_box_end_of_text_reached(model)) break; + current_line_text_offset = next_line_text_offset; + } + + model->text_offset = start_text_offset; +} + +static void text_box_update_text_on_screen(Canvas* canvas, TextBoxModel* model) { + int32_t line_offset = model->scroll_pos - model->line_offset; + text_box_move_line_offset(canvas, model, line_offset); + text_box_update_screen_text(canvas, model); + model->line_offset = model->scroll_pos; +} + +static void text_box_prepare_model(Canvas* canvas, TextBoxModel* model) { + int32_t lines_num = 0; + model->text_offset = 0; + model->scroll_num = 0; + model->scroll_pos = 0; + model->line_offset = 0; + model->lines_on_screen = TEXT_BOX_TEXT_HEIGHT / canvas_current_font_height(canvas); + + // Cache text offset to quick final text offset update if TextBoxFocusEnd is set + int32_t window_offset[TEXT_BOX_MAX_LINES_PER_SCREEN] = {}; + do { + window_offset[lines_num % model->lines_on_screen] = model->text_offset; + text_box_seek_next_line(canvas, model); + lines_num++; + } while(!text_box_end_of_text_reached(model)); + lines_num++; + + if(model->focus == TextBoxFocusEnd) { + if(lines_num > model->lines_on_screen) { + model->text_offset = window_offset[(lines_num - 1) % model->lines_on_screen]; + } + } else { + model->text_offset = 0; + } + + if(lines_num > model->lines_on_screen) { + model->scroll_num = lines_num - model->lines_on_screen; + model->scroll_pos = (model->focus == TextBoxFocusEnd) ? model->scroll_num - 1 : 0; + } + + text_box_update_screen_text(canvas, model); + model->line_offset = model->scroll_pos; +} + static void text_box_view_draw_callback(Canvas* canvas, void* _model) { TextBoxModel* model = _model; @@ -132,44 +261,17 @@ static void text_box_view_draw_callback(Canvas* canvas, void* _model) { } if(!model->formatted) { - text_box_insert_endline(canvas, model); + text_box_prepare_model(canvas, model); model->formatted = true; } elements_slightly_rounded_frame(canvas, 0, 0, 124, 64); - elements_multiline_text(canvas, 3, 11, model->text_pos); elements_scrollbar(canvas, model->scroll_pos, model->scroll_num); -} -static bool text_box_view_input_callback(InputEvent* event, void* context) { - furi_assert(context); - - TextBox* text_box = context; - bool consumed = false; - if(event->type == InputTypeShort || event->type == InputTypeRepeat) { - int32_t scroll_speed = 1; - if(text_box->button_held_for_ticks > 5) { - if(text_box->button_held_for_ticks % 2) { - scroll_speed = 0; - } else { - scroll_speed = text_box->button_held_for_ticks > 9 ? 5 : 3; - } - } - - if(event->key == InputKeyDown) { - text_box_process_down(text_box, scroll_speed); - consumed = true; - } else if(event->key == InputKeyUp) { - text_box_process_up(text_box, scroll_speed); - consumed = true; - } - - text_box->button_held_for_ticks++; - } else if(event->type == InputTypeRelease) { - text_box->button_held_for_ticks = 0; - consumed = true; + if(model->line_offset != model->scroll_pos) { + text_box_update_text_on_screen(canvas, model); } - return consumed; + elements_multiline_text(canvas, 3, 11, furi_string_get_cstr(model->text_on_screen)); } TextBox* text_box_alloc(void) { @@ -185,7 +287,8 @@ TextBox* text_box_alloc(void) { TextBoxModel * model, { model->text = NULL; - model->text_formatted = furi_string_alloc_set(""); + model->text_on_screen = furi_string_alloc(); + model->text_line = furi_string_alloc(); model->formatted = false; model->font = TextBoxFontText; }, @@ -198,7 +301,13 @@ void text_box_free(TextBox* text_box) { furi_check(text_box); with_view_model( - text_box->view, TextBoxModel * model, { furi_string_free(model->text_formatted); }, true); + text_box->view, + TextBoxModel * model, + { + furi_string_free(model->text_on_screen); + furi_string_free(model->text_line); + }, + true); view_free(text_box->view); free(text_box); } @@ -216,9 +325,15 @@ void text_box_reset(TextBox* text_box) { TextBoxModel * model, { model->text = NULL; - furi_string_set(model->text_formatted, ""); model->font = TextBoxFontText; model->focus = TextBoxFocusStart; + furi_string_reset(model->text_line); + furi_string_reset(model->text_on_screen); + model->line_offset = 0; + model->text_offset = 0; + model->lines_on_screen = 0; + model->scroll_num = 0; + model->scroll_pos = 0; model->formatted = false; }, true); @@ -227,16 +342,12 @@ void text_box_reset(TextBox* text_box) { void text_box_set_text(TextBox* text_box, const char* text) { furi_check(text_box); furi_check(text); - size_t str_length = strlen(text); - size_t formating_margin = str_length * TEXT_BOX_MAX_SYMBOL_WIDTH / TEXT_BOX_LINE_WIDTH; with_view_model( text_box->view, TextBoxModel * model, { model->text = text; - furi_string_reset(model->text_formatted); - furi_string_reserve(model->text_formatted, str_length + formating_margin); model->formatted = false; }, true); From 9e42e00eadd79ab8414ec31b3af1ccb5a14c48d0 Mon Sep 17 00:00:00 2001 From: hedger Date: Mon, 20 May 2024 21:23:47 +0400 Subject: [PATCH 2/3] Icons: compression fixes & larger dimension support (#3564) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * toolbox, gui: fixes for compressed icon handling * ufbt: fixes for generated vscode project * scripts: increased max dimensions for image converter * icon type changes * linter fixes; api sync * gui: docs fix * toolbox: fixed potential decoder buffer overflow * minor cleanup * fbt: sdk: suppressed deprecation warnings in API table * toolbox: compress: added unit tests vscode: now installs resources for unit_tests unit_tests: now loads subghz region data * toolbox: compress: review fixes, pt 1 * compress: now passes decoder buffer size as constructor argument; auto-resize decoder buffer; crash on failed icon decompression * PVS fixes * pvs fixes, pt2 * doxygen fixes * investigating unit test failures * investigating unit test failures * investigating unit test failures * investigating unit test failures * investigating unit test failures * UnitTests: move all tests into plugins, brakes testing * UnitTests: add plugin API and update plugin entrypoints * UniTests: Test runner that works with plugins * fbt: extra filtering for extapps to include in build * UnitTests: filter tests by name * loader: restored API table for unit_test build config * Add various missing symbols to API table * UnitTest: fail on plugin load error * UnitTests: cleanup plugin api and reporting * unit_tests: composite resolver * UnitTests: remove unused declaration * unit_tests, nfc: moved mock nfc implementation to libnfc * unit_tests: api: removed redundant #define * toolbox: compress: removed size_hint for icons; triggering furi_check on oversized icons * gui: icon, icon_animation: removed size hit APIs * Format Sources. Cleanup code. * loader: refuse to start .fal as app * toolbox: compress: fixed memory corruption in operations with small destination buffer; added unit tests for that case * unit_tests: proper test skipping; better selective test interface * unit_tests: moved 'loading' logging to proper location Co-authored-by: あく --- .vscode/example/tasks.json | 2 +- applications/debug/unit_tests/application.fam | 204 ++++++++++++++++- .../unit_tests/compress/compressed.bin | Bin 0 -> 135 bytes .../unit_tests/compress/uncompressed.bin | Bin 0 -> 640 bytes applications/debug/unit_tests/test_index.c | 168 -------------- applications/debug/unit_tests/test_runner.c | 216 ++++++++++++++++++ applications/debug/unit_tests/test_runner.h | 12 + .../{ => tests}/bit_lib/bit_lib_test.c | 6 +- .../debug/unit_tests/{ => tests}/bt/bt_test.c | 4 +- .../debug/unit_tests/tests/common/common.c | 42 ++++ .../unit_tests/tests/compress/compress_test.c | 159 +++++++++++++ .../datetime}/datetimelib_test.c | 6 +- .../dialogs_file_browser_options.c | 4 +- .../{storage => tests/dirwalk}/dirwalk_test.c | 6 +- .../{ => tests}/expansion/expansion_test.c | 4 +- .../flipper_format/flipper_format_test.c | 4 +- .../flipper_format_string_test.c | 4 +- .../float_tools/float_tools_test.c | 4 +- .../{ => tests}/furi/furi_memmgr_test.c | 2 +- .../{ => tests}/furi/furi_pubsub_test.c | 2 +- .../{ => tests}/furi/furi_record_test.c | 2 +- .../unit_tests/{ => tests}/furi/furi_test.c | 4 +- .../{ => tests}/furi_hal/furi_hal_tests.c | 4 +- .../furi_hal_crypto}/furi_hal_crypto_tests.c | 4 +- .../furi_string}/furi_string_test.c | 6 +- .../{ => tests}/infrared/infrared_test.c | 4 +- .../{ => tests}/lfrfid/lfrfid_protocols.c | 6 +- .../{ => tests}/manifest/manifest.c | 6 +- .../debug/unit_tests/{ => tests}/minunit.h | 0 .../unit_tests/{ => tests}/minunit_vars.h | 0 .../unit_tests/{ => tests}/minunit_vars_ex.h | 0 .../unit_tests/{ => tests}/nfc/nfc_test.c | 4 +- .../unit_tests/{ => tests}/power/power_test.c | 4 +- .../protocol_dict/protocol_dict_test.c | 6 +- .../unit_tests/{ => tests}/rpc/rpc_test.c | 4 +- .../{ => tests}/storage/storage_test.c | 4 +- .../{ => tests}/stream/stream_test.c | 4 +- .../{ => tests}/subghz/subghz_test.c | 4 +- applications/debug/unit_tests/tests/test.h | 12 + .../debug/unit_tests/tests/test_api.h | 29 +++ .../{ => tests}/varint/varint_test.c | 8 +- .../debug/unit_tests/unit_test_api_table.cpp | 19 ++ .../debug/unit_tests/unit_test_api_table_i.h | 29 +++ applications/debug/unit_tests/unit_tests.c | 21 ++ applications/services/bt/application.fam | 2 +- .../services/bt/bt_service/bt_keys_storage.h | 8 + .../services/desktop/helpers/slideshow.c | 1 - applications/services/gui/canvas.c | 6 +- applications/services/gui/canvas_i.h | 2 + applications/services/gui/icon.c | 18 +- applications/services/gui/icon.h | 28 ++- applications/services/gui/icon_i.h | 6 +- .../loader/firmware_api/firmware_api.cpp | 14 -- applications/services/loader/loader.c | 6 + .../services/loader/loader_applications.c | 2 +- applications/services/rpc/rpc_i.h | 8 + lib/nfc/SConscript | 1 + lib/nfc/nfc.c | 2 +- .../nfc/nfc_transport.c => lib/nfc/nfc_mock.c | 0 lib/subghz/SConscript | 1 + lib/subghz/subghz_file_encoder_worker.h | 8 + lib/toolbox/SConscript | 3 + lib/toolbox/compress.c | 171 ++++++++------ lib/toolbox/compress.h | 18 +- scripts/assets.py | 4 +- scripts/fbt/appmanifest.py | 28 ++- scripts/fbt_tools/fbt_extapps.py | 3 +- scripts/fbt_tools/fbt_sdk.py | 1 + targets/f18/api_symbols.csv | 36 ++- targets/f18/target.json | 2 +- targets/f7/api_symbols.csv | 50 +++- targets/f7/target.json | 2 +- 72 files changed, 1127 insertions(+), 337 deletions(-) create mode 100644 applications/debug/unit_tests/resources/unit_tests/compress/compressed.bin create mode 100644 applications/debug/unit_tests/resources/unit_tests/compress/uncompressed.bin delete mode 100644 applications/debug/unit_tests/test_index.c create mode 100644 applications/debug/unit_tests/test_runner.c create mode 100644 applications/debug/unit_tests/test_runner.h rename applications/debug/unit_tests/{ => tests}/bit_lib/bit_lib_test.c (99%) rename applications/debug/unit_tests/{ => tests}/bt/bt_test.c (98%) create mode 100644 applications/debug/unit_tests/tests/common/common.c create mode 100644 applications/debug/unit_tests/tests/compress/compress_test.c rename applications/debug/unit_tests/{datetimelib => tests/datetime}/datetimelib_test.c (98%) rename applications/debug/unit_tests/{dialogs => tests/dialogs_file_browser_options}/dialogs_file_browser_options.c (93%) rename applications/debug/unit_tests/{storage => tests/dirwalk}/dirwalk_test.c (99%) rename applications/debug/unit_tests/{ => tests}/expansion/expansion_test.c (99%) rename applications/debug/unit_tests/{ => tests}/flipper_format/flipper_format_test.c (99%) rename applications/debug/unit_tests/{flipper_format => tests/flipper_format_string}/flipper_format_string_test.c (99%) rename applications/debug/unit_tests/{ => tests}/float_tools/float_tools_test.c (97%) rename applications/debug/unit_tests/{ => tests}/furi/furi_memmgr_test.c (97%) rename applications/debug/unit_tests/{ => tests}/furi/furi_pubsub_test.c (98%) rename applications/debug/unit_tests/{ => tests}/furi/furi_record_test.c (96%) rename applications/debug/unit_tests/{ => tests}/furi/furi_test.c (94%) rename applications/debug/unit_tests/{ => tests}/furi_hal/furi_hal_tests.c (99%) rename applications/debug/unit_tests/{furi_hal => tests/furi_hal_crypto}/furi_hal_crypto_tests.c (99%) rename applications/debug/unit_tests/{furi => tests/furi_string}/furi_string_test.c (99%) rename applications/debug/unit_tests/{ => tests}/infrared/infrared_test.c (99%) rename applications/debug/unit_tests/{ => tests}/lfrfid/lfrfid_protocols.c (99%) rename applications/debug/unit_tests/{ => tests}/manifest/manifest.c (97%) rename applications/debug/unit_tests/{ => tests}/minunit.h (100%) rename applications/debug/unit_tests/{ => tests}/minunit_vars.h (100%) rename applications/debug/unit_tests/{ => tests}/minunit_vars_ex.h (100%) rename applications/debug/unit_tests/{ => tests}/nfc/nfc_test.c (99%) rename applications/debug/unit_tests/{ => tests}/power/power_test.c (98%) rename applications/debug/unit_tests/{ => tests}/protocol_dict/protocol_dict_test.c (98%) rename applications/debug/unit_tests/{ => tests}/rpc/rpc_test.c (99%) rename applications/debug/unit_tests/{ => tests}/storage/storage_test.c (99%) rename applications/debug/unit_tests/{ => tests}/stream/stream_test.c (99%) rename applications/debug/unit_tests/{ => tests}/subghz/subghz_test.c (99%) create mode 100644 applications/debug/unit_tests/tests/test.h create mode 100644 applications/debug/unit_tests/tests/test_api.h rename applications/debug/unit_tests/{ => tests}/varint/varint_test.c (97%) create mode 100644 applications/debug/unit_tests/unit_test_api_table.cpp create mode 100644 applications/debug/unit_tests/unit_test_api_table_i.h create mode 100644 applications/debug/unit_tests/unit_tests.c rename applications/debug/unit_tests/nfc/nfc_transport.c => lib/nfc/nfc_mock.c (100%) diff --git a/.vscode/example/tasks.json b/.vscode/example/tasks.json index e36a1fe47..1bc6d9ee7 100644 --- a/.vscode/example/tasks.json +++ b/.vscode/example/tasks.json @@ -79,7 +79,7 @@ "label": "[Debug:unit_tests] Flash (USB)", "group": "build", "type": "shell", - "command": "./fbt FIRMWARE_APP_SET=unit_tests FORCE=1 flash_usb" + "command": "./fbt FIRMWARE_APP_SET=unit_tests FORCE=1 flash_usb_full" }, { "label": "[Debug] Flash (USB, with resources)", diff --git a/applications/debug/unit_tests/application.fam b/applications/debug/unit_tests/application.fam index aa25dab27..f5f84ead7 100644 --- a/applications/debug/unit_tests/application.fam +++ b/applications/debug/unit_tests/application.fam @@ -2,8 +2,9 @@ App( appid="unit_tests", apptype=FlipperAppType.STARTUP, entry_point="unit_tests_on_system_start", + sources=["unit_tests.c", "test_runner.c", "unit_test_api_table.cpp"], cdefines=["APP_UNIT_TESTS"], - requires=["system_settings"], + requires=["system_settings", "subghz_start"], provides=["delay_test"], resources="resources", order=100, @@ -12,9 +13,210 @@ App( App( appid="delay_test", name="Delay Test", + sources=["tests/common/*.c", "tests/rpc/*.c"], apptype=FlipperAppType.SYSTEM, entry_point="delay_test_app", stack_size=1 * 1024, requires=["unit_tests"], order=110, ) + +App( + appid="test_varint", + sources=["tests/common/*.c", "tests/varint/*.c"], + apptype=FlipperAppType.PLUGIN, + entry_point="get_api", + requires=["unit_tests"], +) + +App( + appid="test_furi", + sources=["tests/common/*.c", "tests/furi/*.c"], + apptype=FlipperAppType.PLUGIN, + entry_point="get_api", + requires=["unit_tests"], +) + +App( + appid="test_furi_hal", + sources=["tests/common/*.c", "tests/furi_hal/*.c"], + apptype=FlipperAppType.PLUGIN, + entry_point="get_api", + requires=["unit_tests"], +) + +App( + appid="test_furi_hal_crypto", + sources=["tests/common/*.c", "tests/furi_hal_crypto/*.c"], + apptype=FlipperAppType.PLUGIN, + entry_point="get_api", + requires=["unit_tests"], +) + +App( + appid="test_furi_string", + sources=["tests/common/*.c", "tests/furi_string/*.c"], + apptype=FlipperAppType.PLUGIN, + entry_point="get_api", + requires=["unit_tests"], +) + +App( + appid="test_storage", + sources=["tests/common/*.c", "tests/storage/*.c"], + apptype=FlipperAppType.PLUGIN, + entry_point="get_api", + requires=["unit_tests"], +) + +App( + appid="test_stream", + sources=["tests/common/*.c", "tests/stream/*.c"], + apptype=FlipperAppType.PLUGIN, + entry_point="get_api", + requires=["unit_tests"], +) + +App( + appid="test_dirwalk", + sources=["tests/common/*.c", "tests/dirwalk/*.c"], + apptype=FlipperAppType.PLUGIN, + entry_point="get_api", + requires=["unit_tests"], +) + +App( + appid="test_manifest", + sources=["tests/common/*.c", "tests/manifest/*.c"], + apptype=FlipperAppType.PLUGIN, + entry_point="get_api", + requires=["unit_tests"], +) + +App( + appid="test_flipper_format", + sources=["tests/common/*.c", "tests/flipper_format/*.c"], + apptype=FlipperAppType.PLUGIN, + entry_point="get_api", + requires=["unit_tests"], +) + +App( + appid="test_flipper_format_string", + sources=["tests/common/*.c", "tests/flipper_format_string/*.c"], + apptype=FlipperAppType.PLUGIN, + entry_point="get_api", + requires=["unit_tests"], +) + +App( + appid="test_rpc", + sources=["tests/common/*.c", "tests/rpc/*.c"], + apptype=FlipperAppType.PLUGIN, + entry_point="get_api", + requires=["unit_tests"], +) + +App( + appid="test_subghz", + sources=["tests/common/*.c", "tests/subghz/*.c"], + apptype=FlipperAppType.PLUGIN, + entry_point="get_api", + requires=["unit_tests"], +) + +App( + appid="test_infrared", + sources=["tests/common/*.c", "tests/infrared/*.c"], + apptype=FlipperAppType.PLUGIN, + entry_point="get_api", + requires=["unit_tests"], +) + +App( + appid="test_nfc", + sources=["tests/common/*.c", "tests/nfc/*.c"], + apptype=FlipperAppType.PLUGIN, + entry_point="get_api", + requires=["unit_tests"], +) + +App( + appid="test_power", + sources=["tests/common/*.c", "tests/power/*.c"], + apptype=FlipperAppType.PLUGIN, + entry_point="get_api", + requires=["unit_tests"], +) + +App( + appid="test_protocol_dict", + sources=["tests/common/*.c", "tests/protocol_dict/*.c"], + apptype=FlipperAppType.PLUGIN, + entry_point="get_api", + requires=["unit_tests"], +) + +App( + appid="test_lfrfid", + sources=["tests/common/*.c", "tests/lfrfid/*.c"], + apptype=FlipperAppType.PLUGIN, + entry_point="get_api", + requires=["unit_tests"], +) + +App( + appid="test_bit_lib", + sources=["tests/common/*.c", "tests/bit_lib/*.c"], + apptype=FlipperAppType.PLUGIN, + entry_point="get_api", + requires=["unit_tests"], +) + +App( + appid="test_datetime", + sources=["tests/common/*.c", "tests/datetime/*.c"], + apptype=FlipperAppType.PLUGIN, + entry_point="get_api", + requires=["unit_tests"], +) + +App( + appid="test_float_tools", + sources=["tests/common/*.c", "tests/float_tools/*.c"], + apptype=FlipperAppType.PLUGIN, + entry_point="get_api", + requires=["unit_tests"], +) + +App( + appid="test_bt", + sources=["tests/common/*.c", "tests/bt/*.c"], + apptype=FlipperAppType.PLUGIN, + entry_point="get_api", + requires=["unit_tests"], +) + +App( + appid="test_dialogs_file_browser_options", + sources=["tests/common/*.c", "tests/dialogs_file_browser_options/*.c"], + apptype=FlipperAppType.PLUGIN, + entry_point="get_api", + requires=["unit_tests"], +) + +App( + appid="test_expansion", + sources=["tests/common/*.c", "tests/expansion/*.c"], + apptype=FlipperAppType.PLUGIN, + entry_point="get_api", + requires=["unit_tests"], +) + +App( + appid="test_compress", + sources=["tests/common/*.c", "tests/compress/*.c"], + apptype=FlipperAppType.PLUGIN, + entry_point="get_api", + requires=["unit_tests"], +) diff --git a/applications/debug/unit_tests/resources/unit_tests/compress/compressed.bin b/applications/debug/unit_tests/resources/unit_tests/compress/compressed.bin new file mode 100644 index 0000000000000000000000000000000000000000..95d729295a76c87fa0b6058cef17939599df1731 GIT binary patch literal 135 zcmZQ%Xl7_|NC>b{5#wcVaY_u>kzyvx-{F!JXt5+rSG>n9IdI35GF$lx9w|YVD${(` yr+B3X?M&Ggt3SghE!c9&wOsQ#e(AwGpM0yem;dqq_&@uP|NH;cKmJb==K}yMC+6cQE@6%&_`l#-T_m6KOcR8m$^Ra4i{)Y8_`)zddH bG%_|ZH8Z!cw6eCbwX*jN>NV1 literal 0 HcmV?d00001 diff --git a/applications/debug/unit_tests/test_index.c b/applications/debug/unit_tests/test_index.c deleted file mode 100644 index 5d0282bd7..000000000 --- a/applications/debug/unit_tests/test_index.c +++ /dev/null @@ -1,168 +0,0 @@ -#include -#include -#include -#include "minunit_vars.h" -#include -#include -#include - -#define TAG "UnitTests" - -int run_minunit_test_furi(void); -int run_minunit_test_furi_hal(void); -int run_minunit_test_furi_hal_crypto(void); -int run_minunit_test_furi_string(void); -int run_minunit_test_infrared(void); -int run_minunit_test_rpc(void); -int run_minunit_test_manifest(void); -int run_minunit_test_flipper_format(void); -int run_minunit_test_flipper_format_string(void); -int run_minunit_test_stream(void); -int run_minunit_test_storage(void); -int run_minunit_test_subghz(void); -int run_minunit_test_dirwalk(void); -int run_minunit_test_power(void); -int run_minunit_test_protocol_dict(void); -int run_minunit_test_lfrfid_protocols(void); -int run_minunit_test_nfc(void); -int run_minunit_test_bit_lib(void); -int run_minunit_test_datetime(void); -int run_minunit_test_float_tools(void); -int run_minunit_test_bt(void); -int run_minunit_test_dialogs_file_browser_options(void); -int run_minunit_test_expansion(void); - -typedef int (*UnitTestEntry)(void); - -typedef struct { - const char* name; - const UnitTestEntry entry; -} UnitTest; - -const UnitTest unit_tests[] = { - {.name = "furi", .entry = run_minunit_test_furi}, - {.name = "furi_hal", .entry = run_minunit_test_furi_hal}, - {.name = "furi_hal_crypto", .entry = run_minunit_test_furi_hal_crypto}, - {.name = "furi_string", .entry = run_minunit_test_furi_string}, - {.name = "storage", .entry = run_minunit_test_storage}, - {.name = "stream", .entry = run_minunit_test_stream}, - {.name = "dirwalk", .entry = run_minunit_test_dirwalk}, - {.name = "manifest", .entry = run_minunit_test_manifest}, - {.name = "flipper_format", .entry = run_minunit_test_flipper_format}, - {.name = "flipper_format_string", .entry = run_minunit_test_flipper_format_string}, - {.name = "rpc", .entry = run_minunit_test_rpc}, - {.name = "subghz", .entry = run_minunit_test_subghz}, - {.name = "infrared", .entry = run_minunit_test_infrared}, - {.name = "nfc", .entry = run_minunit_test_nfc}, - {.name = "power", .entry = run_minunit_test_power}, - {.name = "protocol_dict", .entry = run_minunit_test_protocol_dict}, - {.name = "lfrfid", .entry = run_minunit_test_lfrfid_protocols}, - {.name = "bit_lib", .entry = run_minunit_test_bit_lib}, - {.name = "datetime", .entry = run_minunit_test_datetime}, - {.name = "float_tools", .entry = run_minunit_test_float_tools}, - {.name = "bt", .entry = run_minunit_test_bt}, - {.name = "dialogs_file_browser_options", - .entry = run_minunit_test_dialogs_file_browser_options}, - {.name = "expansion", .entry = run_minunit_test_expansion}, -}; - -void minunit_print_progress(void) { - static const char progress[] = {'\\', '|', '/', '-'}; - static uint8_t progress_counter = 0; - static uint32_t last_tick = 0; - uint32_t current_tick = furi_get_tick(); - if(current_tick - last_tick > 20) { - last_tick = current_tick; - printf("[%c]\033[3D", progress[++progress_counter % COUNT_OF(progress)]); - fflush(stdout); - } -} - -void minunit_print_fail(const char* str) { - printf(_FURI_LOG_CLR_E "%s\r\n" _FURI_LOG_CLR_RESET, str); -} - -void minunit_printf_warning(const char* format, ...) { - FuriString* str = furi_string_alloc(); - va_list args; - va_start(args, format); - furi_string_vprintf(str, format, args); - va_end(args); - printf(_FURI_LOG_CLR_W "%s\r\n" _FURI_LOG_CLR_RESET, furi_string_get_cstr(str)); - furi_string_free(str); -} - -void unit_tests_cli(Cli* cli, FuriString* args, void* context) { - UNUSED(cli); - UNUSED(args); - UNUSED(context); - minunit_run = 0; - minunit_assert = 0; - minunit_fail = 0; - minunit_status = 0; - - Loader* loader = furi_record_open(RECORD_LOADER); - NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); - - // TODO FL-3491: lock device while test running - if(loader_is_locked(loader)) { - printf("RPC: stop all applications to run tests\r\n"); - notification_message(notification, &sequence_blink_magenta_100); - } else { - notification_message_block(notification, &sequence_set_only_blue_255); - - uint32_t heap_before = memmgr_get_free_heap(); - uint32_t cycle_counter = furi_get_tick(); - - for(size_t i = 0; i < COUNT_OF(unit_tests); i++) { - if(cli_cmd_interrupt_received(cli)) { - break; - } - - if(furi_string_size(args)) { - if(furi_string_cmp_str(args, unit_tests[i].name) == 0) { - unit_tests[i].entry(); - } else { - printf("Skipping %s\r\n", unit_tests[i].name); - } - } else { - unit_tests[i].entry(); - } - } - - if(minunit_run != 0) { - printf("\r\nFailed tests: %u\r\n", minunit_fail); - - // Time report - cycle_counter = (furi_get_tick() - cycle_counter); - printf("Consumed: %lu ms\r\n", cycle_counter); - - // Wait for tested services and apps to deallocate memory - furi_delay_ms(200); - uint32_t heap_after = memmgr_get_free_heap(); - printf("Leaked: %ld\r\n", heap_before - heap_after); - - // Final Report - if(minunit_fail == 0) { - notification_message(notification, &sequence_success); - printf("Status: PASSED\r\n"); - } else { - notification_message(notification, &sequence_error); - printf("Status: FAILED\r\n"); - } - } - } - - furi_record_close(RECORD_NOTIFICATION); - furi_record_close(RECORD_LOADER); -} - -void unit_tests_on_system_start(void) { -#ifdef SRV_CLI - Cli* cli = furi_record_open(RECORD_CLI); - - // We need to launch apps from tests, so we cannot lock loader - cli_add_command(cli, "unit_tests", CliCommandFlagParallelSafe, unit_tests_cli, NULL); - furi_record_close(RECORD_CLI); -#endif -} diff --git a/applications/debug/unit_tests/test_runner.c b/applications/debug/unit_tests/test_runner.c new file mode 100644 index 000000000..6af807086 --- /dev/null +++ b/applications/debug/unit_tests/test_runner.c @@ -0,0 +1,216 @@ +#include "test_runner.h" + +#include "tests/test_api.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +extern const ElfApiInterface* const unit_tests_api_interface; + +#define TAG "TestRunner" + +#define PLUGINS_PATH "/ext/apps_data/unit_tests/plugins" + +struct TestRunner { + Storage* storage; + Loader* loader; + NotificationApp* notification; + + // Temporary used things + Cli* cli; + FuriString* args; + + // ELF related stuff + CompositeApiResolver* composite_resolver; + + // Report data + int minunit_run; + int minunit_assert; + int minunit_fail; + int minunit_status; +}; + +TestRunner* test_runner_alloc(Cli* cli, FuriString* args) { + TestRunner* instance = malloc(sizeof(TestRunner)); + + instance->storage = furi_record_open(RECORD_STORAGE); + instance->loader = furi_record_open(RECORD_LOADER); + instance->notification = furi_record_open(RECORD_NOTIFICATION); + + instance->cli = cli; + instance->args = args; + + instance->composite_resolver = composite_api_resolver_alloc(); + composite_api_resolver_add(instance->composite_resolver, firmware_api_interface); + composite_api_resolver_add(instance->composite_resolver, unit_tests_api_interface); + + return instance; +} + +void test_runner_free(TestRunner* instance) { + furi_assert(instance); + + composite_api_resolver_free(instance->composite_resolver); + + furi_record_close(RECORD_NOTIFICATION); + instance->notification = NULL; + + furi_record_close(RECORD_LOADER); + instance->loader = NULL; + + furi_record_close(RECORD_STORAGE); + instance->storage = NULL; + + free(instance); +} + +static bool test_runner_run_plugin(TestRunner* instance, const char* path) { + furi_assert(instance); + + FURI_LOG_D(TAG, "Loading %s", path); + FlipperApplication* lib = flipper_application_alloc( + instance->storage, composite_api_resolver_get(instance->composite_resolver)); + + bool result = false; + instance->minunit_fail = -1; + do { + FlipperApplicationPreloadStatus preload_res = flipper_application_preload(lib, path); + + if(preload_res != FlipperApplicationPreloadStatusSuccess) { + FURI_LOG_E(TAG, "Failed to preload %s, %d", path, preload_res); + break; + } + + if(!flipper_application_is_plugin(lib)) { + FURI_LOG_E(TAG, "Not a plugin %s", path); + break; + } + + FlipperApplicationLoadStatus load_status = flipper_application_map_to_memory(lib); + if(load_status != FlipperApplicationLoadStatusSuccess) { + FURI_LOG_E(TAG, "Failed to load %s", path); + break; + } + + const FlipperAppPluginDescriptor* app_descriptor = + flipper_application_plugin_get_descriptor(lib); + + const TestApi* test = app_descriptor->entry_point; + + instance->minunit_fail = test->run(); + + instance->minunit_run += test->get_minunit_run(); + instance->minunit_assert += test->get_minunit_assert(); + instance->minunit_status += test->get_minunit_status(); + + result = (instance->minunit_fail == 0); + } while(false); + + flipper_application_free(lib); + + return result; +} + +static void test_runner_run_internal(TestRunner* instance) { + furi_assert(instance); + + char file_name_buffer[256]; + FuriString* file_name = furi_string_alloc(); + FuriString* file_basename = furi_string_alloc(); + File* directory = storage_file_alloc(instance->storage); + + do { + if(!storage_dir_open(directory, PLUGINS_PATH)) { + FURI_LOG_E(TAG, "Failed to open directory %s", PLUGINS_PATH); + break; + } + + while(true) { + if(cli_cmd_interrupt_received(instance->cli)) { + break; + } + + if(!storage_dir_read(directory, NULL, file_name_buffer, sizeof(file_name_buffer))) { + break; + } + + furi_string_set(file_name, file_name_buffer); + if(!furi_string_end_with_str(file_name, ".fal")) { + continue; + } + + path_concat(PLUGINS_PATH, file_name_buffer, file_name); + + path_extract_filename(file_name, file_basename, true); + const char* file_basename_cstr = furi_string_get_cstr(file_basename); + + bool result = true; + if(furi_string_size(instance->args)) { + if(furi_string_cmp_str(instance->args, file_basename_cstr) == 0) { + result = test_runner_run_plugin(instance, furi_string_get_cstr(file_name)); + } else { + printf("Skipping %s\r\n", file_basename_cstr); + } + } else { + result = test_runner_run_plugin(instance, furi_string_get_cstr(file_name)); + } + + if(!result) { + printf("Failed to execute test: %s\r\n", file_basename_cstr); + break; + } + } + } while(false); + + storage_dir_close(directory); + storage_file_free(directory); + furi_string_free(file_name); + furi_string_free(file_basename); +} + +void test_runner_run(TestRunner* instance) { + furi_assert(instance); + + // TODO FL-3491: lock device while test running + if(loader_is_locked(instance->loader)) { + printf("RPC: stop all applications to run tests\r\n"); + notification_message(instance->notification, &sequence_blink_magenta_100); + } else { + notification_message_block(instance->notification, &sequence_set_only_blue_255); + + uint32_t heap_before = memmgr_get_free_heap(); + uint32_t cycle_counter = furi_get_tick(); + + test_runner_run_internal(instance); + + if(instance->minunit_run != 0) { + printf("\r\nFailed tests: %d\r\n", instance->minunit_fail); + + // Time report + cycle_counter = (furi_get_tick() - cycle_counter); + printf("Consumed: %lu ms\r\n", cycle_counter); + + // Wait for tested services and apps to deallocate memory + furi_delay_ms(200); + uint32_t heap_after = memmgr_get_free_heap(); + printf("Leaked: %ld\r\n", heap_before - heap_after); + + // Final Report + if(instance->minunit_fail == 0) { + notification_message(instance->notification, &sequence_success); + printf("Status: PASSED\r\n"); + } else { + notification_message(instance->notification, &sequence_error); + printf("Status: FAILED\r\n"); + } + } + } +} diff --git a/applications/debug/unit_tests/test_runner.h b/applications/debug/unit_tests/test_runner.h new file mode 100644 index 000000000..024799ebe --- /dev/null +++ b/applications/debug/unit_tests/test_runner.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +typedef struct TestRunner TestRunner; +typedef struct Cli Cli; + +TestRunner* test_runner_alloc(Cli* cli, FuriString* args); + +void test_runner_free(TestRunner* isntance); + +void test_runner_run(TestRunner* isntance); \ No newline at end of file diff --git a/applications/debug/unit_tests/bit_lib/bit_lib_test.c b/applications/debug/unit_tests/tests/bit_lib/bit_lib_test.c similarity index 99% rename from applications/debug/unit_tests/bit_lib/bit_lib_test.c rename to applications/debug/unit_tests/tests/bit_lib/bit_lib_test.c index 239a3b562..d12aeb2aa 100644 --- a/applications/debug/unit_tests/bit_lib/bit_lib_test.c +++ b/applications/debug/unit_tests/tests/bit_lib/bit_lib_test.c @@ -1,5 +1,5 @@ #include -#include "../minunit.h" +#include "../test.h" #include MU_TEST(test_bit_lib_increment_index) { @@ -737,4 +737,6 @@ MU_TEST_SUITE(test_bit_lib) { int run_minunit_test_bit_lib(void) { MU_RUN_SUITE(test_bit_lib); return MU_EXIT_CODE; -} \ No newline at end of file +} + +TEST_API_DEFINE(run_minunit_test_bit_lib) diff --git a/applications/debug/unit_tests/bt/bt_test.c b/applications/debug/unit_tests/tests/bt/bt_test.c similarity index 98% rename from applications/debug/unit_tests/bt/bt_test.c rename to applications/debug/unit_tests/tests/bt/bt_test.c index a09b9894b..e7704381d 100644 --- a/applications/debug/unit_tests/bt/bt_test.c +++ b/applications/debug/unit_tests/tests/bt/bt_test.c @@ -1,6 +1,6 @@ #include #include -#include "../minunit.h" +#include "../test.h" #include #include @@ -108,3 +108,5 @@ int run_minunit_test_bt(void) { MU_RUN_SUITE(test_bt); return MU_EXIT_CODE; } + +TEST_API_DEFINE(run_minunit_test_bt) diff --git a/applications/debug/unit_tests/tests/common/common.c b/applications/debug/unit_tests/tests/common/common.c new file mode 100644 index 000000000..aef6aa1a4 --- /dev/null +++ b/applications/debug/unit_tests/tests/common/common.c @@ -0,0 +1,42 @@ +#include "../test.h" +#include "../minunit_vars.h" + +#include + +void minunit_print_progress(void) { + static const char progress[] = {'\\', '|', '/', '-'}; + static uint8_t progress_counter = 0; + static uint32_t last_tick = 0; + uint32_t current_tick = furi_get_tick(); + if(current_tick - last_tick > 20) { + last_tick = current_tick; + printf("[%c]\033[3D", progress[++progress_counter % COUNT_OF(progress)]); + fflush(stdout); + } +} + +void minunit_print_fail(const char* str) { + printf(_FURI_LOG_CLR_E "%s\r\n" _FURI_LOG_CLR_RESET, str); +} + +void minunit_printf_warning(const char* format, ...) { + FuriString* str = furi_string_alloc(); + va_list args; + va_start(args, format); + furi_string_vprintf(str, format, args); + va_end(args); + printf(_FURI_LOG_CLR_W "%s\r\n" _FURI_LOG_CLR_RESET, furi_string_get_cstr(str)); + furi_string_free(str); +} + +int get_minunit_run(void) { + return minunit_run; +} + +int get_minunit_assert(void) { + return minunit_assert; +} + +int get_minunit_status(void) { + return minunit_status; +} diff --git a/applications/debug/unit_tests/tests/compress/compress_test.c b/applications/debug/unit_tests/tests/compress/compress_test.c new file mode 100644 index 000000000..8f6153297 --- /dev/null +++ b/applications/debug/unit_tests/tests/compress/compress_test.c @@ -0,0 +1,159 @@ +#include "../test.h" + +#include + +#include +#include +#include + +#include + +#include + +#define COMPRESS_UNIT_TESTS_PATH(path) EXT_PATH("unit_tests/compress/" path) + +static void compress_test_reference_comp_decomp() { + Storage* storage = furi_record_open(RECORD_STORAGE); + + File* compressed_file = storage_file_alloc(storage); + File* decompressed_file = storage_file_alloc(storage); + + mu_assert( + storage_file_open( + compressed_file, + COMPRESS_UNIT_TESTS_PATH("compressed.bin"), + FSAM_READ, + FSOM_OPEN_EXISTING), + "Failed to open compressed file"); + mu_assert( + storage_file_open( + decompressed_file, + COMPRESS_UNIT_TESTS_PATH("uncompressed.bin"), + FSAM_READ, + FSOM_OPEN_EXISTING), + "Failed to open decompressed file"); + + uint64_t compressed_ref_size = storage_file_size(compressed_file); + uint64_t decompressed_ref_size = storage_file_size(decompressed_file); + + mu_assert(compressed_ref_size > 0 && decompressed_ref_size > 0, "Invalid file sizes"); + + uint8_t* compressed_ref_buff = malloc(compressed_ref_size); + uint8_t* decompressed_ref_buff = malloc(decompressed_ref_size); + + mu_assert( + storage_file_read(compressed_file, compressed_ref_buff, compressed_ref_size) == + compressed_ref_size, + "Failed to read compressed file"); + + mu_assert( + storage_file_read(decompressed_file, decompressed_ref_buff, decompressed_ref_size) == + decompressed_ref_size, + "Failed to read decompressed file"); + + storage_file_free(compressed_file); + storage_file_free(decompressed_file); + furi_record_close(RECORD_STORAGE); + + uint8_t* temp_buffer = malloc(1024); + Compress* comp = compress_alloc(1024); + + size_t encoded_size = 0; + mu_assert( + compress_encode( + comp, decompressed_ref_buff, decompressed_ref_size, temp_buffer, 1024, &encoded_size), + "Compress failed"); + + mu_assert(encoded_size == compressed_ref_size, "Encoded size is not equal to reference size"); + + mu_assert( + memcmp(temp_buffer, compressed_ref_buff, compressed_ref_size) == 0, + "Encoded buffer is not equal to reference"); + + size_t decoded_size = 0; + mu_assert( + compress_decode( + comp, compressed_ref_buff, compressed_ref_size, temp_buffer, 1024, &decoded_size), + "Decompress failed"); + + mu_assert( + decoded_size == decompressed_ref_size, "Decoded size is not equal to reference size"); + + mu_assert( + memcmp(temp_buffer, decompressed_ref_buff, decompressed_ref_size) == 0, + "Decoded buffer is not equal to reference"); + + compress_free(comp); + + free(temp_buffer); + free(compressed_ref_buff); + free(decompressed_ref_buff); +} + +static void compress_test_random_comp_decomp() { + static const size_t src_buffer_size = 1024; + static const size_t encoded_buffer_size = 1024; + static const size_t small_buffer_size = src_buffer_size / 32; + + // We only fill half of the buffer with random data, so if anything goes wrong, there's no overflow + static const size_t src_data_size = src_buffer_size / 2; + + Compress* comp = compress_alloc(src_buffer_size); + uint8_t* src_buff = malloc(src_buffer_size); + uint8_t* encoded_buff = malloc(encoded_buffer_size); + uint8_t* decoded_buff = malloc(src_buffer_size); + uint8_t* small_buff = malloc(small_buffer_size); + + furi_hal_random_fill_buf(src_buff, src_data_size); + + size_t encoded_size = 0; + + mu_assert( + compress_encode( + comp, src_buff, src_data_size, encoded_buff, encoded_buffer_size, &encoded_size), + "Compress failed"); + + mu_assert(encoded_size > 0, "Encoded size is zero"); + + size_t small_enc_dec_size = 0; + mu_assert( + compress_encode( + comp, src_buff, src_data_size, small_buff, small_buffer_size, &small_enc_dec_size) == + false, + "Compress to small buffer failed"); + + size_t decoded_size = 0; + mu_assert( + compress_decode( + comp, encoded_buff, encoded_size, decoded_buff, src_buffer_size, &decoded_size), + "Decompress failed"); + mu_assert(decoded_size == src_data_size, "Decoded size is not equal to source size"); + + mu_assert( + memcmp(src_buff, decoded_buff, src_data_size) == 0, + "Decoded buffer is not equal to source"); + + mu_assert( + compress_decode( + comp, encoded_buff, encoded_size, small_buff, small_buffer_size, &small_enc_dec_size) == + false, + "Decompress to small buffer failed"); + + free(small_buff); + free(src_buff); + free(encoded_buff); + free(decoded_buff); + compress_free(comp); +} + +MU_TEST_SUITE(test_compress) { + MU_RUN_TEST(compress_test_random_comp_decomp); + MU_RUN_TEST(compress_test_reference_comp_decomp); +} + +int run_minunit_test_compress(void) { + MU_RUN_SUITE(test_compress); + return MU_EXIT_CODE; +} + +TEST_API_DEFINE(run_minunit_test_compress) diff --git a/applications/debug/unit_tests/datetimelib/datetimelib_test.c b/applications/debug/unit_tests/tests/datetime/datetimelib_test.c similarity index 98% rename from applications/debug/unit_tests/datetimelib/datetimelib_test.c rename to applications/debug/unit_tests/tests/datetime/datetimelib_test.c index c171a4413..7d90f18de 100644 --- a/applications/debug/unit_tests/datetimelib/datetimelib_test.c +++ b/applications/debug/unit_tests/tests/datetime/datetimelib_test.c @@ -1,5 +1,5 @@ #include -#include "../minunit.h" +#include "../test.h" #include @@ -188,4 +188,6 @@ int run_minunit_test_datetime(void) { MU_RUN_SUITE(test_datetime_validate_datetime); return MU_EXIT_CODE; -} \ No newline at end of file +} + +TEST_API_DEFINE(run_minunit_test_datetime) diff --git a/applications/debug/unit_tests/dialogs/dialogs_file_browser_options.c b/applications/debug/unit_tests/tests/dialogs_file_browser_options/dialogs_file_browser_options.c similarity index 93% rename from applications/debug/unit_tests/dialogs/dialogs_file_browser_options.c rename to applications/debug/unit_tests/tests/dialogs_file_browser_options/dialogs_file_browser_options.c index 657b933df..e42e00f9a 100644 --- a/applications/debug/unit_tests/dialogs/dialogs_file_browser_options.c +++ b/applications/debug/unit_tests/tests/dialogs_file_browser_options/dialogs_file_browser_options.c @@ -1,6 +1,6 @@ #include -#include "../minunit.h" +#include "../test.h" MU_TEST(test_dialog_file_browser_set_basic_options_should_init_all_fields) { mu_assert( @@ -30,3 +30,5 @@ int run_minunit_test_dialogs_file_browser_options(void) { return MU_EXIT_CODE; } + +TEST_API_DEFINE(run_minunit_test_dialogs_file_browser_options) diff --git a/applications/debug/unit_tests/storage/dirwalk_test.c b/applications/debug/unit_tests/tests/dirwalk/dirwalk_test.c similarity index 99% rename from applications/debug/unit_tests/storage/dirwalk_test.c rename to applications/debug/unit_tests/tests/dirwalk/dirwalk_test.c index 415ec7dd4..c2474e755 100644 --- a/applications/debug/unit_tests/storage/dirwalk_test.c +++ b/applications/debug/unit_tests/tests/dirwalk/dirwalk_test.c @@ -1,4 +1,4 @@ -#include "../minunit.h" +#include "../test.h" #include #include #include @@ -269,4 +269,6 @@ MU_TEST_SUITE(test_dirwalk_suite) { int run_minunit_test_dirwalk(void) { MU_RUN_SUITE(test_dirwalk_suite); return MU_EXIT_CODE; -} \ No newline at end of file +} + +TEST_API_DEFINE(run_minunit_test_dirwalk) diff --git a/applications/debug/unit_tests/expansion/expansion_test.c b/applications/debug/unit_tests/tests/expansion/expansion_test.c similarity index 99% rename from applications/debug/unit_tests/expansion/expansion_test.c rename to applications/debug/unit_tests/tests/expansion/expansion_test.c index c6a143ed3..79d9b0a57 100644 --- a/applications/debug/unit_tests/expansion/expansion_test.c +++ b/applications/debug/unit_tests/tests/expansion/expansion_test.c @@ -1,4 +1,4 @@ -#include "../minunit.h" +#include "../test.h" #include #include @@ -198,3 +198,5 @@ int run_minunit_test_expansion(void) { MU_RUN_SUITE(test_expansion_suite); return MU_EXIT_CODE; } + +TEST_API_DEFINE(run_minunit_test_expansion) diff --git a/applications/debug/unit_tests/flipper_format/flipper_format_test.c b/applications/debug/unit_tests/tests/flipper_format/flipper_format_test.c similarity index 99% rename from applications/debug/unit_tests/flipper_format/flipper_format_test.c rename to applications/debug/unit_tests/tests/flipper_format/flipper_format_test.c index 471055fb0..1a2eaf222 100644 --- a/applications/debug/unit_tests/flipper_format/flipper_format_test.c +++ b/applications/debug/unit_tests/tests/flipper_format/flipper_format_test.c @@ -2,7 +2,7 @@ #include #include #include -#include "../minunit.h" +#include "../test.h" #define TEST_DIR TEST_DIR_NAME "/" #define TEST_DIR_NAME EXT_PATH("unit_tests_tmp") @@ -549,3 +549,5 @@ int run_minunit_test_flipper_format(void) { MU_RUN_SUITE(flipper_format); return MU_EXIT_CODE; } + +TEST_API_DEFINE(run_minunit_test_flipper_format) diff --git a/applications/debug/unit_tests/flipper_format/flipper_format_string_test.c b/applications/debug/unit_tests/tests/flipper_format_string/flipper_format_string_test.c similarity index 99% rename from applications/debug/unit_tests/flipper_format/flipper_format_string_test.c rename to applications/debug/unit_tests/tests/flipper_format_string/flipper_format_string_test.c index 99bb5dda5..821b1ba22 100644 --- a/applications/debug/unit_tests/flipper_format/flipper_format_string_test.c +++ b/applications/debug/unit_tests/tests/flipper_format_string/flipper_format_string_test.c @@ -3,7 +3,7 @@ #include #include #include -#include "../minunit.h" +#include "../test.h" static const char* test_filetype = "Flipper Format test"; static const uint32_t test_version = 666; @@ -335,3 +335,5 @@ int run_minunit_test_flipper_format_string(void) { MU_RUN_SUITE(flipper_format_string_suite); return MU_EXIT_CODE; } + +TEST_API_DEFINE(run_minunit_test_flipper_format_string) diff --git a/applications/debug/unit_tests/float_tools/float_tools_test.c b/applications/debug/unit_tests/tests/float_tools/float_tools_test.c similarity index 97% rename from applications/debug/unit_tests/float_tools/float_tools_test.c rename to applications/debug/unit_tests/tests/float_tools/float_tools_test.c index 91ac46937..d016ca4a2 100644 --- a/applications/debug/unit_tests/float_tools/float_tools_test.c +++ b/applications/debug/unit_tests/tests/float_tools/float_tools_test.c @@ -1,7 +1,7 @@ #include #include -#include "../minunit.h" +#include "../test.h" MU_TEST(float_tools_equal_test) { mu_check(float_is_equal(FLT_MAX, FLT_MAX)); @@ -58,3 +58,5 @@ int run_minunit_test_float_tools(void) { MU_RUN_SUITE(float_tools_suite); return MU_EXIT_CODE; } + +TEST_API_DEFINE(run_minunit_test_float_tools) diff --git a/applications/debug/unit_tests/furi/furi_memmgr_test.c b/applications/debug/unit_tests/tests/furi/furi_memmgr_test.c similarity index 97% rename from applications/debug/unit_tests/furi/furi_memmgr_test.c rename to applications/debug/unit_tests/tests/furi/furi_memmgr_test.c index 01e2c17f6..837fd6085 100644 --- a/applications/debug/unit_tests/furi/furi_memmgr_test.c +++ b/applications/debug/unit_tests/tests/furi/furi_memmgr_test.c @@ -1,4 +1,4 @@ -#include "../minunit.h" +#include "../test.h" #include #include #include diff --git a/applications/debug/unit_tests/furi/furi_pubsub_test.c b/applications/debug/unit_tests/tests/furi/furi_pubsub_test.c similarity index 98% rename from applications/debug/unit_tests/furi/furi_pubsub_test.c rename to applications/debug/unit_tests/tests/furi/furi_pubsub_test.c index 8925ff42e..fdf15e6f5 100644 --- a/applications/debug/unit_tests/furi/furi_pubsub_test.c +++ b/applications/debug/unit_tests/tests/furi/furi_pubsub_test.c @@ -1,7 +1,7 @@ #include #include #include -#include "../minunit.h" +#include "../test.h" const uint32_t context_value = 0xdeadbeef; const uint32_t notify_value_0 = 0x12345678; diff --git a/applications/debug/unit_tests/furi/furi_record_test.c b/applications/debug/unit_tests/tests/furi/furi_record_test.c similarity index 96% rename from applications/debug/unit_tests/furi/furi_record_test.c rename to applications/debug/unit_tests/tests/furi/furi_record_test.c index 10a5a8393..6a2b5a3d2 100644 --- a/applications/debug/unit_tests/furi/furi_record_test.c +++ b/applications/debug/unit_tests/tests/furi/furi_record_test.c @@ -1,7 +1,7 @@ #include #include #include -#include "../minunit.h" +#include "../test.h" #define TEST_RECORD_NAME "test/holding" diff --git a/applications/debug/unit_tests/furi/furi_test.c b/applications/debug/unit_tests/tests/furi/furi_test.c similarity index 94% rename from applications/debug/unit_tests/furi/furi_test.c rename to applications/debug/unit_tests/tests/furi/furi_test.c index e287f9927..eab9a917d 100644 --- a/applications/debug/unit_tests/furi/furi_test.c +++ b/applications/debug/unit_tests/tests/furi/furi_test.c @@ -1,7 +1,7 @@ #include #include #include -#include "../minunit.h" +#include "../test.h" // v2 tests void test_furi_create_open(void); @@ -55,3 +55,5 @@ int run_minunit_test_furi(void) { return MU_EXIT_CODE; } + +TEST_API_DEFINE(run_minunit_test_furi) diff --git a/applications/debug/unit_tests/furi_hal/furi_hal_tests.c b/applications/debug/unit_tests/tests/furi_hal/furi_hal_tests.c similarity index 99% rename from applications/debug/unit_tests/furi_hal/furi_hal_tests.c rename to applications/debug/unit_tests/tests/furi_hal/furi_hal_tests.c index 0c5cec8a6..985daa03d 100644 --- a/applications/debug/unit_tests/furi_hal/furi_hal_tests.c +++ b/applications/debug/unit_tests/tests/furi_hal/furi_hal_tests.c @@ -4,7 +4,7 @@ #include #include #include -#include "../minunit.h" +#include "../test.h" #include #define DATA_SIZE 4 @@ -232,3 +232,5 @@ int run_minunit_test_furi_hal(void) { MU_RUN_SUITE(furi_hal_i2c_ext_suite); return MU_EXIT_CODE; } + +TEST_API_DEFINE(run_minunit_test_furi_hal) diff --git a/applications/debug/unit_tests/furi_hal/furi_hal_crypto_tests.c b/applications/debug/unit_tests/tests/furi_hal_crypto/furi_hal_crypto_tests.c similarity index 99% rename from applications/debug/unit_tests/furi_hal/furi_hal_crypto_tests.c rename to applications/debug/unit_tests/tests/furi_hal_crypto/furi_hal_crypto_tests.c index c2bd6c5f8..0a264a18a 100644 --- a/applications/debug/unit_tests/furi_hal/furi_hal_crypto_tests.c +++ b/applications/debug/unit_tests/tests/furi_hal_crypto/furi_hal_crypto_tests.c @@ -1,7 +1,7 @@ #include #include #include -#include "../minunit.h" +#include "../test.h" static const uint8_t key_ctr_1[32] = { 0x77, 0x6B, 0xEF, 0xF2, 0x85, 0x1D, 0xB0, 0x6F, 0x4C, 0x8A, 0x05, 0x42, 0xC8, 0x69, 0x6F, 0x6C, @@ -600,3 +600,5 @@ int run_minunit_test_furi_hal_crypto(void) { MU_RUN_SUITE(furi_hal_crypto_gcm_test); return MU_EXIT_CODE; } + +TEST_API_DEFINE(run_minunit_test_furi_hal_crypto) diff --git a/applications/debug/unit_tests/furi/furi_string_test.c b/applications/debug/unit_tests/tests/furi_string/furi_string_test.c similarity index 99% rename from applications/debug/unit_tests/furi/furi_string_test.c rename to applications/debug/unit_tests/tests/furi_string/furi_string_test.c index 853076b67..949d8c048 100644 --- a/applications/debug/unit_tests/furi/furi_string_test.c +++ b/applications/debug/unit_tests/tests/furi_string/furi_string_test.c @@ -1,5 +1,5 @@ #include -#include "../minunit.h" +#include "../test.h" static void test_setup(void) { } @@ -466,4 +466,6 @@ int run_minunit_test_furi_string(void) { MU_RUN_SUITE(test_suite); return MU_EXIT_CODE; -} \ No newline at end of file +} + +TEST_API_DEFINE(run_minunit_test_furi_string) diff --git a/applications/debug/unit_tests/infrared/infrared_test.c b/applications/debug/unit_tests/tests/infrared/infrared_test.c similarity index 99% rename from applications/debug/unit_tests/infrared/infrared_test.c rename to applications/debug/unit_tests/tests/infrared/infrared_test.c index 4442fedb3..922577aa8 100644 --- a/applications/debug/unit_tests/infrared/infrared_test.c +++ b/applications/debug/unit_tests/tests/infrared/infrared_test.c @@ -2,7 +2,7 @@ #include #include #include -#include "../minunit.h" +#include "../test.h" #define IR_TEST_FILES_DIR EXT_PATH("unit_tests/infrared/") #define IR_TEST_FILE_PREFIX "test_" @@ -549,3 +549,5 @@ int run_minunit_test_infrared(void) { MU_RUN_SUITE(infrared_test); return MU_EXIT_CODE; } + +TEST_API_DEFINE(run_minunit_test_infrared) diff --git a/applications/debug/unit_tests/lfrfid/lfrfid_protocols.c b/applications/debug/unit_tests/tests/lfrfid/lfrfid_protocols.c similarity index 99% rename from applications/debug/unit_tests/lfrfid/lfrfid_protocols.c rename to applications/debug/unit_tests/tests/lfrfid/lfrfid_protocols.c index 6a7dbab9c..fc380ecc4 100644 --- a/applications/debug/unit_tests/lfrfid/lfrfid_protocols.c +++ b/applications/debug/unit_tests/tests/lfrfid/lfrfid_protocols.c @@ -1,5 +1,5 @@ #include -#include "../minunit.h" +#include "../test.h" #include #include #include @@ -550,4 +550,6 @@ MU_TEST_SUITE(test_lfrfid_protocols_suite) { int run_minunit_test_lfrfid_protocols(void) { MU_RUN_SUITE(test_lfrfid_protocols_suite); return MU_EXIT_CODE; -} \ No newline at end of file +} + +TEST_API_DEFINE(run_minunit_test_lfrfid_protocols) diff --git a/applications/debug/unit_tests/manifest/manifest.c b/applications/debug/unit_tests/tests/manifest/manifest.c similarity index 97% rename from applications/debug/unit_tests/manifest/manifest.c rename to applications/debug/unit_tests/tests/manifest/manifest.c index e8ac93d7c..fcbab0ae8 100644 --- a/applications/debug/unit_tests/manifest/manifest.c +++ b/applications/debug/unit_tests/tests/manifest/manifest.c @@ -1,5 +1,5 @@ #include -#include "../minunit.h" +#include "../test.h" #include #define TAG "Manifest" @@ -72,4 +72,6 @@ MU_TEST_SUITE(manifest_suite) { int run_minunit_test_manifest(void) { MU_RUN_SUITE(manifest_suite); return MU_EXIT_CODE; -} \ No newline at end of file +} + +TEST_API_DEFINE(run_minunit_test_manifest) diff --git a/applications/debug/unit_tests/minunit.h b/applications/debug/unit_tests/tests/minunit.h similarity index 100% rename from applications/debug/unit_tests/minunit.h rename to applications/debug/unit_tests/tests/minunit.h diff --git a/applications/debug/unit_tests/minunit_vars.h b/applications/debug/unit_tests/tests/minunit_vars.h similarity index 100% rename from applications/debug/unit_tests/minunit_vars.h rename to applications/debug/unit_tests/tests/minunit_vars.h diff --git a/applications/debug/unit_tests/minunit_vars_ex.h b/applications/debug/unit_tests/tests/minunit_vars_ex.h similarity index 100% rename from applications/debug/unit_tests/minunit_vars_ex.h rename to applications/debug/unit_tests/tests/minunit_vars_ex.h diff --git a/applications/debug/unit_tests/nfc/nfc_test.c b/applications/debug/unit_tests/tests/nfc/nfc_test.c similarity index 99% rename from applications/debug/unit_tests/nfc/nfc_test.c rename to applications/debug/unit_tests/tests/nfc/nfc_test.c index 8bb88df9a..4de364a09 100644 --- a/applications/debug/unit_tests/nfc/nfc_test.c +++ b/applications/debug/unit_tests/tests/nfc/nfc_test.c @@ -24,7 +24,7 @@ #include #include -#include "../minunit.h" +#include "../test.h" #define TAG "NfcTest" @@ -820,3 +820,5 @@ int run_minunit_test_nfc(void) { MU_RUN_SUITE(nfc); return MU_EXIT_CODE; } + +TEST_API_DEFINE(run_minunit_test_nfc) diff --git a/applications/debug/unit_tests/power/power_test.c b/applications/debug/unit_tests/tests/power/power_test.c similarity index 98% rename from applications/debug/unit_tests/power/power_test.c rename to applications/debug/unit_tests/tests/power/power_test.c index 03e0ad269..d121acba6 100644 --- a/applications/debug/unit_tests/power/power_test.c +++ b/applications/debug/unit_tests/tests/power/power_test.c @@ -1,6 +1,6 @@ #include #include -#include "../minunit.h" +#include "../test.h" static void power_test_deinit(void) { // Try to reset to default charge voltage limit @@ -67,3 +67,5 @@ int run_minunit_test_power(void) { MU_RUN_SUITE(test_power_suite); return MU_EXIT_CODE; } + +TEST_API_DEFINE(run_minunit_test_power) diff --git a/applications/debug/unit_tests/protocol_dict/protocol_dict_test.c b/applications/debug/unit_tests/tests/protocol_dict/protocol_dict_test.c similarity index 98% rename from applications/debug/unit_tests/protocol_dict/protocol_dict_test.c rename to applications/debug/unit_tests/tests/protocol_dict/protocol_dict_test.c index 6ecaa1a52..9ced106ab 100644 --- a/applications/debug/unit_tests/protocol_dict/protocol_dict_test.c +++ b/applications/debug/unit_tests/tests/protocol_dict/protocol_dict_test.c @@ -1,5 +1,5 @@ #include -#include "../minunit.h" +#include "../test.h" #include typedef enum { @@ -219,4 +219,6 @@ MU_TEST_SUITE(test_protocol_dict_suite) { int run_minunit_test_protocol_dict(void) { MU_RUN_SUITE(test_protocol_dict_suite); return MU_EXIT_CODE; -} \ No newline at end of file +} + +TEST_API_DEFINE(run_minunit_test_protocol_dict) diff --git a/applications/debug/unit_tests/rpc/rpc_test.c b/applications/debug/unit_tests/tests/rpc/rpc_test.c similarity index 99% rename from applications/debug/unit_tests/rpc/rpc_test.c rename to applications/debug/unit_tests/tests/rpc/rpc_test.c index cd692d69d..be7b331f9 100644 --- a/applications/debug/unit_tests/rpc/rpc_test.c +++ b/applications/debug/unit_tests/tests/rpc/rpc_test.c @@ -17,7 +17,7 @@ #include #include -#include "../minunit.h" +#include "../test.h" #include #include @@ -1864,3 +1864,5 @@ int32_t delay_test_app(void* p) { return 0; } + +TEST_API_DEFINE(run_minunit_test_rpc) diff --git a/applications/debug/unit_tests/storage/storage_test.c b/applications/debug/unit_tests/tests/storage/storage_test.c similarity index 99% rename from applications/debug/unit_tests/storage/storage_test.c rename to applications/debug/unit_tests/tests/storage/storage_test.c index e4361f06c..1d887ced0 100644 --- a/applications/debug/unit_tests/storage/storage_test.c +++ b/applications/debug/unit_tests/tests/storage/storage_test.c @@ -1,4 +1,4 @@ -#include "../minunit.h" +#include "../test.h" #include #include @@ -712,3 +712,5 @@ int run_minunit_test_storage(void) { MU_RUN_SUITE(test_md5_calc_suite); return MU_EXIT_CODE; } + +TEST_API_DEFINE(run_minunit_test_storage) diff --git a/applications/debug/unit_tests/stream/stream_test.c b/applications/debug/unit_tests/tests/stream/stream_test.c similarity index 99% rename from applications/debug/unit_tests/stream/stream_test.c rename to applications/debug/unit_tests/tests/stream/stream_test.c index 3e31773b4..a9c22c723 100644 --- a/applications/debug/unit_tests/stream/stream_test.c +++ b/applications/debug/unit_tests/tests/stream/stream_test.c @@ -4,7 +4,7 @@ #include #include #include -#include "../minunit.h" +#include "../test.h" static const char* stream_test_data = "I write differently from what I speak, " "I speak differently from what I think, " @@ -530,3 +530,5 @@ int run_minunit_test_stream(void) { MU_RUN_SUITE(stream_suite); return MU_EXIT_CODE; } + +TEST_API_DEFINE(run_minunit_test_stream) diff --git a/applications/debug/unit_tests/subghz/subghz_test.c b/applications/debug/unit_tests/tests/subghz/subghz_test.c similarity index 99% rename from applications/debug/unit_tests/subghz/subghz_test.c rename to applications/debug/unit_tests/tests/subghz/subghz_test.c index a2f00885b..030a4223f 100644 --- a/applications/debug/unit_tests/subghz/subghz_test.c +++ b/applications/debug/unit_tests/tests/subghz/subghz_test.c @@ -1,6 +1,6 @@ #include #include -#include "../minunit.h" +#include "../test.h" #include #include #include @@ -908,3 +908,5 @@ int run_minunit_test_subghz(void) { MU_RUN_SUITE(subghz); return MU_EXIT_CODE; } + +TEST_API_DEFINE(run_minunit_test_subghz) diff --git a/applications/debug/unit_tests/tests/test.h b/applications/debug/unit_tests/tests/test.h new file mode 100644 index 000000000..bd7acc00e --- /dev/null +++ b/applications/debug/unit_tests/tests/test.h @@ -0,0 +1,12 @@ +#pragma once + +// Framework +#include "minunit.h" + +#include "test_api.h" + +int get_minunit_run(void); + +int get_minunit_assert(void); + +int get_minunit_status(void); diff --git a/applications/debug/unit_tests/tests/test_api.h b/applications/debug/unit_tests/tests/test_api.h new file mode 100644 index 000000000..55b9f7885 --- /dev/null +++ b/applications/debug/unit_tests/tests/test_api.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +#define APPID "UnitTest" +#define API_VERSION (0u) + +typedef struct { + int (*run)(void); + int (*get_minunit_run)(void); + int (*get_minunit_assert)(void); + int (*get_minunit_status)(void); +} TestApi; + +#define TEST_API_DEFINE(entrypoint) \ + const TestApi test_api = { \ + .run = entrypoint, \ + .get_minunit_run = get_minunit_run, \ + .get_minunit_assert = get_minunit_assert, \ + .get_minunit_status = get_minunit_status, \ + }; \ + const FlipperAppPluginDescriptor app_descriptor = { \ + .appid = APPID, \ + .ep_api_version = API_VERSION, \ + .entry_point = &test_api, \ + }; \ + const FlipperAppPluginDescriptor* get_api(void) { \ + return &app_descriptor; \ + } diff --git a/applications/debug/unit_tests/varint/varint_test.c b/applications/debug/unit_tests/tests/varint/varint_test.c similarity index 97% rename from applications/debug/unit_tests/varint/varint_test.c rename to applications/debug/unit_tests/tests/varint/varint_test.c index ac444013d..3fdf5115a 100644 --- a/applications/debug/unit_tests/varint/varint_test.c +++ b/applications/debug/unit_tests/tests/varint/varint_test.c @@ -1,6 +1,8 @@ #include #include -#include "../minunit.h" + +#include "../test.h" + #include #include @@ -85,4 +87,6 @@ MU_TEST_SUITE(test_varint_suite) { int run_minunit_test_varint(void) { MU_RUN_SUITE(test_varint_suite); return MU_EXIT_CODE; -} \ No newline at end of file +} + +TEST_API_DEFINE(run_minunit_test_varint) diff --git a/applications/debug/unit_tests/unit_test_api_table.cpp b/applications/debug/unit_tests/unit_test_api_table.cpp new file mode 100644 index 000000000..8205d4372 --- /dev/null +++ b/applications/debug/unit_tests/unit_test_api_table.cpp @@ -0,0 +1,19 @@ +#include +#include + +#include "unit_test_api_table_i.h" + +static_assert(!has_hash_collisions(unit_tests_api_table), "Detected API method hash collision!"); + +constexpr HashtableApiInterface unit_tests_hashtable_api_interface{ + { + .api_version_major = 0, + .api_version_minor = 0, + .resolver_callback = &elf_resolve_from_hashtable, + }, + unit_tests_api_table.cbegin(), + unit_tests_api_table.cend(), +}; + +extern "C" const ElfApiInterface* const unit_tests_api_interface = + &unit_tests_hashtable_api_interface; diff --git a/applications/debug/unit_tests/unit_test_api_table_i.h b/applications/debug/unit_tests/unit_test_api_table_i.h new file mode 100644 index 000000000..8c2fa4687 --- /dev/null +++ b/applications/debug/unit_tests/unit_test_api_table_i.h @@ -0,0 +1,29 @@ +#include +#include +#include +#include +#include + +#include +#include + +static constexpr auto unit_tests_api_table = sort(create_array_t( + API_METHOD(resource_manifest_reader_alloc, ResourceManifestReader*, (Storage*)), + API_METHOD(resource_manifest_reader_free, void, (ResourceManifestReader*)), + API_METHOD(resource_manifest_reader_open, bool, (ResourceManifestReader*, const char* filename)), + API_METHOD(resource_manifest_reader_next, ResourceManifestEntry*, (ResourceManifestReader*)), + API_METHOD(resource_manifest_reader_previous, ResourceManifestEntry*, (ResourceManifestReader*)), + API_METHOD(slix_process_iso15693_3_error, SlixError, (Iso15693_3Error)), + API_METHOD(iso15693_3_poller_get_data, const Iso15693_3Data*, (Iso15693_3Poller*)), + API_METHOD(rpc_system_storage_get_error, PB_CommandStatus, (FS_Error)), + API_METHOD(xQueueSemaphoreTake, BaseType_t, (QueueHandle_t, TickType_t)), + API_METHOD(vQueueDelete, void, (QueueHandle_t)), + API_METHOD( + xQueueGenericCreate, + QueueHandle_t, + (const UBaseType_t, const UBaseType_t, const uint8_t)), + API_METHOD( + xQueueGenericSend, + BaseType_t, + (QueueHandle_t, const void* const, TickType_t, const BaseType_t)), + API_VARIABLE(PB_Main_msg, PB_Main_msg_t))); diff --git a/applications/debug/unit_tests/unit_tests.c b/applications/debug/unit_tests/unit_tests.c new file mode 100644 index 000000000..237cb9080 --- /dev/null +++ b/applications/debug/unit_tests/unit_tests.c @@ -0,0 +1,21 @@ +#include +#include + +#include "test_runner.h" + +void unit_tests_cli(Cli* cli, FuriString* args, void* context) { + UNUSED(cli); + UNUSED(context); + + TestRunner* test_runner = test_runner_alloc(cli, args); + test_runner_run(test_runner); + test_runner_free(test_runner); +} + +void unit_tests_on_system_start(void) { +#ifdef SRV_CLI + Cli* cli = furi_record_open(RECORD_CLI); + cli_add_command(cli, "unit_tests", CliCommandFlagParallelSafe, unit_tests_cli, NULL); + furi_record_close(RECORD_CLI); +#endif +} diff --git a/applications/services/bt/application.fam b/applications/services/bt/application.fam index 2e97dc1d6..2d2840e3a 100644 --- a/applications/services/bt/application.fam +++ b/applications/services/bt/application.fam @@ -14,7 +14,7 @@ App( ], stack_size=1 * 1024, order=20, - sdk_headers=["bt_service/bt.h"], + sdk_headers=["bt_service/bt.h", "bt_service/bt_keys_storage.h"], ) App( diff --git a/applications/services/bt/bt_service/bt_keys_storage.h b/applications/services/bt/bt_service/bt_keys_storage.h index cb808ca30..587dd570d 100644 --- a/applications/services/bt/bt_service/bt_keys_storage.h +++ b/applications/services/bt/bt_service/bt_keys_storage.h @@ -3,6 +3,10 @@ #include #include +#ifdef __cplusplus +extern "C" { +#endif + typedef struct BtKeysStorage BtKeysStorage; BtKeysStorage* bt_keys_storage_alloc(const char* keys_storage_path); @@ -18,3 +22,7 @@ bool bt_keys_storage_load(BtKeysStorage* instance); bool bt_keys_storage_update(BtKeysStorage* instance, uint8_t* start_addr, uint32_t size); bool bt_keys_storage_delete(BtKeysStorage* instance); + +#ifdef __cplusplus +} +#endif diff --git a/applications/services/desktop/helpers/slideshow.c b/applications/services/desktop/helpers/slideshow.c index efd02c4bc..ec02f27a1 100644 --- a/applications/services/desktop/helpers/slideshow.c +++ b/applications/services/desktop/helpers/slideshow.c @@ -1,6 +1,5 @@ #include "slideshow.h" -#include #include #include #include diff --git a/applications/services/gui/canvas.c b/applications/services/gui/canvas.c index c29316587..de09305aa 100644 --- a/applications/services/gui/canvas.c +++ b/applications/services/gui/canvas.c @@ -15,7 +15,7 @@ const CanvasFontParameters canvas_font_params[FontTotalNumber] = { Canvas* canvas_init(void) { Canvas* canvas = malloc(sizeof(Canvas)); - canvas->compress_icon = compress_icon_alloc(); + canvas->compress_icon = compress_icon_alloc(ICON_DECOMPRESSOR_BUFFER_SIZE); // Initialize mutex canvas->mutex = furi_mutex_alloc(FuriMutexTypeNormal); @@ -390,7 +390,7 @@ void canvas_draw_icon_ex( x += canvas->offset_x; y += canvas->offset_y; uint8_t* icon_data = NULL; - compress_icon_decode(canvas->compress_icon, icon_get_data(icon), &icon_data); + compress_icon_decode(canvas->compress_icon, icon_get_frame_data(icon, 0), &icon_data); canvas_draw_u8g2_bitmap( &canvas->fb, x, y, icon_get_width(icon), icon_get_height(icon), icon_data, rotation); } @@ -402,7 +402,7 @@ void canvas_draw_icon(Canvas* canvas, int32_t x, int32_t y, const Icon* icon) { x += canvas->offset_x; y += canvas->offset_y; uint8_t* icon_data = NULL; - compress_icon_decode(canvas->compress_icon, icon_get_data(icon), &icon_data); + compress_icon_decode(canvas->compress_icon, icon_get_frame_data(icon, 0), &icon_data); canvas_draw_u8g2_bitmap( &canvas->fb, x, y, icon_get_width(icon), icon_get_height(icon), icon_data, IconRotation0); } diff --git a/applications/services/gui/canvas_i.h b/applications/services/gui/canvas_i.h index c204b970f..449db71db 100644 --- a/applications/services/gui/canvas_i.h +++ b/applications/services/gui/canvas_i.h @@ -12,6 +12,8 @@ #include #include +#define ICON_DECOMPRESSOR_BUFFER_SIZE (128u * 64 / 8) + #ifdef __cplusplus extern "C" { #endif diff --git a/applications/services/gui/icon.c b/applications/services/gui/icon.c index e216b9255..6e015ed75 100644 --- a/applications/services/gui/icon.c +++ b/applications/services/gui/icon.c @@ -1,13 +1,16 @@ +#include "icon.h" #include "icon_i.h" #include -uint8_t icon_get_width(const Icon* instance) { +#include + +uint16_t icon_get_width(const Icon* instance) { furi_check(instance); return instance->width; } -uint8_t icon_get_height(const Icon* instance) { +uint16_t icon_get_height(const Icon* instance) { furi_check(instance); return instance->height; @@ -16,5 +19,14 @@ uint8_t icon_get_height(const Icon* instance) { const uint8_t* icon_get_data(const Icon* instance) { furi_check(instance); - return instance->frames[0]; + return icon_get_frame_data(instance, 0); +} + +uint32_t icon_get_frame_count(const Icon* instance) { + return instance->frame_count; +} + +const uint8_t* icon_get_frame_data(const Icon* instance, uint32_t frame) { + furi_check(frame < instance->frame_count); + return instance->frames[frame]; } diff --git a/applications/services/gui/icon.h b/applications/services/gui/icon.h index e33c4d34d..fc19c0a7d 100644 --- a/applications/services/gui/icon.h +++ b/applications/services/gui/icon.h @@ -6,6 +6,7 @@ #pragma once #include +#include #ifdef __cplusplus extern "C" { @@ -19,7 +20,7 @@ typedef struct Icon Icon; * * @return width in pixels */ -uint8_t icon_get_width(const Icon* instance); +uint16_t icon_get_width(const Icon* instance); /** Get icon height * @@ -27,15 +28,32 @@ uint8_t icon_get_width(const Icon* instance); * * @return height in pixels */ -uint8_t icon_get_height(const Icon* instance); +uint16_t icon_get_height(const Icon* instance); -/** Get Icon XBM bitmap data +/** Get Icon XBM bitmap data for the first frame * * @param[in] instance pointer to Icon data * - * @return pointer to XBM bitmap data + * @return pointer to compressed XBM bitmap data */ -const uint8_t* icon_get_data(const Icon* instance); +FURI_DEPRECATED const uint8_t* icon_get_data(const Icon* instance); + +/** Get Icon frame count + * + * @param[in] instance pointer to Icon data + * + * @return frame count + */ +uint32_t icon_get_frame_count(const Icon* instance); + +/** Get Icon XBM bitmap data for a particular frame + * + * @param[in] instance pointer to Icon data + * @param[in] frame frame index + * + * @return pointer to compressed XBM bitmap data + */ +const uint8_t* icon_get_frame_data(const Icon* instance, uint32_t frame); #ifdef __cplusplus } diff --git a/applications/services/gui/icon_i.h b/applications/services/gui/icon_i.h index 58cac8b41..e33cdbf23 100644 --- a/applications/services/gui/icon_i.h +++ b/applications/services/gui/icon_i.h @@ -4,11 +4,11 @@ */ #pragma once -#include "icon.h" +#include struct Icon { - const uint8_t width; - const uint8_t height; + const uint16_t width; + const uint16_t height; const uint8_t frame_count; const uint8_t frame_rate; const uint8_t* const* frames; diff --git a/applications/services/loader/firmware_api/firmware_api.cpp b/applications/services/loader/firmware_api/firmware_api.cpp index 833f99abe..45953eddf 100644 --- a/applications/services/loader/firmware_api/firmware_api.cpp +++ b/applications/services/loader/firmware_api/firmware_api.cpp @@ -10,19 +10,6 @@ static_assert(!has_hash_collisions(elf_api_table), "Detected API method hash collision!"); -#ifdef APP_UNIT_TESTS -constexpr HashtableApiInterface mock_elf_api_interface{ - { - .api_version_major = 0, - .api_version_minor = 0, - .resolver_callback = &elf_resolve_from_hashtable, - }, - nullptr, - nullptr, -}; - -const ElfApiInterface* const firmware_api_interface = &mock_elf_api_interface; -#else constexpr HashtableApiInterface elf_api_interface{ { .api_version_major = (elf_api_version >> 16), @@ -33,7 +20,6 @@ constexpr HashtableApiInterface elf_api_interface{ elf_api_table.cend(), }; const ElfApiInterface* const firmware_api_interface = &elf_api_interface; -#endif extern "C" void furi_hal_info_get_api_version(uint16_t* major, uint16_t* minor) { *major = firmware_api_interface->api_version_major; diff --git a/applications/services/loader/loader.c b/applications/services/loader/loader.c index cedc2e83e..c02eefd04 100644 --- a/applications/services/loader/loader.c +++ b/applications/services/loader/loader.c @@ -353,6 +353,12 @@ static LoaderStatus loader_start_external_app( FURI_LOG_I(TAG, "Loaded in %zums", (size_t)(furi_get_tick() - start)); + if(flipper_application_is_plugin(loader->app.fap)) { + status = loader_make_status_error( + LoaderStatusErrorInternal, error_message, "Plugin %s is not runnable", path); + break; + } + loader->app.thread = flipper_application_alloc_thread(loader->app.fap, args); FuriString* app_name = furi_string_alloc(); path_extract_filename_no_ext(path, app_name); diff --git a/applications/services/loader/loader_applications.c b/applications/services/loader/loader_applications.c index d52aec097..0c5d93cc8 100644 --- a/applications/services/loader/loader_applications.c +++ b/applications/services/loader/loader_applications.c @@ -92,7 +92,7 @@ static bool loader_applications_item_callback( path, loader_applications_app->storage, icon_ptr, item_name); } else { path_extract_filename(path, item_name, false); - memcpy(*icon_ptr, icon_get_data(&I_js_script_10px), FAP_MANIFEST_MAX_ICON_SIZE); + memcpy(*icon_ptr, icon_get_frame_data(&I_js_script_10px, 0), FAP_MANIFEST_MAX_ICON_SIZE); return true; } } diff --git a/applications/services/rpc/rpc_i.h b/applications/services/rpc/rpc_i.h index ffca50231..20baca7b1 100644 --- a/applications/services/rpc/rpc_i.h +++ b/applications/services/rpc/rpc_i.h @@ -7,6 +7,10 @@ #include #include +#ifdef __cplusplus +extern "C" { +#endif + typedef void* (*RpcSystemAlloc)(RpcSession* session); typedef void (*RpcSystemFree)(void* context); typedef void (*PBMessageHandler)(const PB_Main* msg_request, void* context); @@ -45,3 +49,7 @@ void rpc_debug_print_data(const char* prefix, uint8_t* buffer, size_t size); void rpc_cli_command_start_session(Cli* cli, FuriString* args, void* context); PB_CommandStatus rpc_system_storage_get_error(FS_Error fs_error); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/lib/nfc/SConscript b/lib/nfc/SConscript index f047101e6..c6caffdb0 100644 --- a/lib/nfc/SConscript +++ b/lib/nfc/SConscript @@ -33,6 +33,7 @@ env.Append( File("protocols/mf_ultralight/mf_ultralight_poller.h"), File("protocols/mf_classic/mf_classic_poller.h"), File("protocols/mf_desfire/mf_desfire_poller.h"), + File("protocols/slix/slix_poller.h"), File("protocols/st25tb/st25tb_poller.h"), File("protocols/felica/felica_poller.h"), # Listeners diff --git a/lib/nfc/nfc.c b/lib/nfc/nfc.c index 59d254592..d0d342a6b 100644 --- a/lib/nfc/nfc.c +++ b/lib/nfc/nfc.c @@ -660,4 +660,4 @@ NfcError nfc_felica_listener_set_sensf_res_data( return nfc_process_hal_error(error); } -#endif // APP_UNIT_TESTS +#endif // FW_CFG_unit_tests diff --git a/applications/debug/unit_tests/nfc/nfc_transport.c b/lib/nfc/nfc_mock.c similarity index 100% rename from applications/debug/unit_tests/nfc/nfc_transport.c rename to lib/nfc/nfc_mock.c diff --git a/lib/subghz/SConscript b/lib/subghz/SConscript index d0bc2a254..933cd8f99 100644 --- a/lib/subghz/SConscript +++ b/lib/subghz/SConscript @@ -25,6 +25,7 @@ env.Append( File("subghz_protocol_registry.h"), File("devices/cc1101_configs.h"), File("devices/cc1101_int/cc1101_int_interconnect.h"), + File("subghz_file_encoder_worker.h"), ], ) diff --git a/lib/subghz/subghz_file_encoder_worker.h b/lib/subghz/subghz_file_encoder_worker.h index ae2f4a2a0..a6a24dc74 100644 --- a/lib/subghz/subghz_file_encoder_worker.h +++ b/lib/subghz/subghz_file_encoder_worker.h @@ -2,6 +2,10 @@ #include +#ifdef __cplusplus +extern "C" { +#endif + typedef void (*SubGhzFileEncoderWorkerCallbackEnd)(void* context); typedef struct SubGhzFileEncoderWorker SubGhzFileEncoderWorker; @@ -59,3 +63,7 @@ void subghz_file_encoder_worker_stop(SubGhzFileEncoderWorker* instance); * @return bool - true if running */ bool subghz_file_encoder_worker_is_running(SubGhzFileEncoderWorker* instance); + +#ifdef __cplusplus +} +#endif diff --git a/lib/toolbox/SConscript b/lib/toolbox/SConscript index 121362424..11e01a8c9 100644 --- a/lib/toolbox/SConscript +++ b/lib/toolbox/SConscript @@ -35,6 +35,9 @@ env.Append( File("simple_array.h"), File("bit_buffer.h"), File("keys_dict.h"), + File("pulse_protocols/pulse_glue.h"), + File("md5_calc.h"), + File("varint.h"), ], ) diff --git a/lib/toolbox/compress.c b/lib/toolbox/compress.c index 70db47968..780bea27a 100644 --- a/lib/toolbox/compress.c +++ b/lib/toolbox/compress.c @@ -3,6 +3,9 @@ #include #include #include +#include + +#define TAG "Compress" /** Defines encoder and decoder window size */ #define COMPRESS_EXP_BUFF_SIZE_LOG (8u) @@ -10,9 +13,16 @@ /** Defines encoder and decoder lookahead buffer size */ #define COMPRESS_LOOKAHEAD_BUFF_SIZE_LOG (4u) -/** Buffer sizes for input and output data */ -#define COMPRESS_ICON_ENCODED_BUFF_SIZE (1024u) -#define COMPRESS_ICON_DECODED_BUFF_SIZE (1024u) +/** Buffer size for input data */ +#define COMPRESS_ICON_ENCODED_BUFF_SIZE (256u) + +static bool compress_decode_internal( + heatshrink_decoder* decoder, + const uint8_t* data_in, + size_t data_in_size, + uint8_t* data_out, + size_t data_out_size, + size_t* data_res_size); typedef struct { uint8_t is_compressed; @@ -24,55 +34,51 @@ _Static_assert(sizeof(CompressHeader) == 4, "Incorrect CompressHeader size"); struct CompressIcon { heatshrink_decoder* decoder; - uint8_t decoded_buff[COMPRESS_ICON_DECODED_BUFF_SIZE]; + uint8_t* buffer; + size_t buffer_size; }; -CompressIcon* compress_icon_alloc(void) { +CompressIcon* compress_icon_alloc(size_t decode_buf_size) { CompressIcon* instance = malloc(sizeof(CompressIcon)); instance->decoder = heatshrink_decoder_alloc( COMPRESS_ICON_ENCODED_BUFF_SIZE, COMPRESS_EXP_BUFF_SIZE_LOG, COMPRESS_LOOKAHEAD_BUFF_SIZE_LOG); heatshrink_decoder_reset(instance->decoder); - memset(instance->decoded_buff, 0, sizeof(instance->decoded_buff)); + + instance->buffer_size = decode_buf_size + 4; /* To account for heatshrink's poller quirks */ + instance->buffer = malloc(instance->buffer_size); return instance; } void compress_icon_free(CompressIcon* instance) { furi_check(instance); + free(instance->buffer); heatshrink_decoder_free(instance->decoder); free(instance); } -void compress_icon_decode(CompressIcon* instance, const uint8_t* icon_data, uint8_t** decoded_buff) { +void compress_icon_decode(CompressIcon* instance, const uint8_t* icon_data, uint8_t** output) { furi_check(instance); furi_check(icon_data); - furi_check(decoded_buff); + furi_check(output); CompressHeader* header = (CompressHeader*)icon_data; if(header->is_compressed) { - size_t data_processed = 0; - heatshrink_decoder_sink( + size_t decoded_size = 0; + /* If decompression fails - check that decode_buf_size is large enough */ + furi_check(compress_decode_internal( instance->decoder, - (uint8_t*)&icon_data[sizeof(CompressHeader)], - header->compressed_buff_size, - &data_processed); - while(1) { - HSD_poll_res res = heatshrink_decoder_poll( - instance->decoder, - instance->decoded_buff, - sizeof(instance->decoded_buff), - &data_processed); - furi_check((res == HSDR_POLL_EMPTY) || (res == HSDR_POLL_MORE)); - if(res != HSDR_POLL_MORE) { - break; - } - } - heatshrink_decoder_reset(instance->decoder); - *decoded_buff = instance->decoded_buff; + icon_data, + /* Decoder will check/process headers again - need to pass them */ + sizeof(CompressHeader) + header->compressed_buff_size, + instance->buffer, + instance->buffer_size, + &decoded_size)); + *output = instance->buffer; } else { - *decoded_buff = (uint8_t*)&icon_data[1]; + *output = (uint8_t*)&icon_data[1]; } } @@ -81,12 +87,6 @@ struct Compress { heatshrink_decoder* decoder; }; -static void compress_reset(Compress* compress) { - furi_assert(compress); - heatshrink_encoder_reset(compress->encoder); - heatshrink_decoder_reset(compress->decoder); -} - Compress* compress_alloc(uint16_t compress_buff_size) { Compress* compress = malloc(sizeof(Compress)); compress->encoder = @@ -105,16 +105,16 @@ void compress_free(Compress* compress) { free(compress); } -bool compress_encode( - Compress* compress, +static bool compress_encode_internal( + heatshrink_encoder* encoder, uint8_t* data_in, size_t data_in_size, uint8_t* data_out, size_t data_out_size, size_t* data_res_size) { - furi_assert(compress); - furi_assert(data_in); - furi_assert(data_in_size); + furi_check(encoder); + furi_check(data_in); + furi_check(data_in_size); size_t sink_size = 0; size_t poll_size = 0; @@ -125,10 +125,10 @@ bool compress_encode( size_t sunk = 0; size_t res_buff_size = sizeof(CompressHeader); - // Sink data to encoding buffer + /* Sink data to encoding buffer */ while((sunk < data_in_size) && !encode_failed) { - sink_res = heatshrink_encoder_sink( - compress->encoder, &data_in[sunk], data_in_size - sunk, &sink_size); + sink_res = + heatshrink_encoder_sink(encoder, &data_in[sunk], data_in_size - sunk, &sink_size); if(sink_res != HSER_SINK_OK) { encode_failed = true; break; @@ -136,10 +136,7 @@ bool compress_encode( sunk += sink_size; do { poll_res = heatshrink_encoder_poll( - compress->encoder, - &data_out[res_buff_size], - data_out_size - res_buff_size, - &poll_size); + encoder, &data_out[res_buff_size], data_out_size - res_buff_size, &poll_size); if(poll_res < 0) { encode_failed = true; break; @@ -148,31 +145,30 @@ bool compress_encode( } while(poll_res == HSER_POLL_MORE); } - // Notify sinking complete and poll encoded data - finish_res = heatshrink_encoder_finish(compress->encoder); + /* Notify sinking complete and poll encoded data */ + finish_res = heatshrink_encoder_finish(encoder); if(finish_res < 0) { encode_failed = true; } else { do { poll_res = heatshrink_encoder_poll( - compress->encoder, - &data_out[res_buff_size], - data_out_size - 4 - res_buff_size, - &poll_size); + encoder, &data_out[res_buff_size], data_out_size - res_buff_size, &poll_size); if(poll_res < 0) { encode_failed = true; break; } res_buff_size += poll_size; - finish_res = heatshrink_encoder_finish(compress->encoder); + finish_res = heatshrink_encoder_finish(encoder); } while(finish_res != HSER_FINISH_DONE); } bool result = true; - // Write encoded data to output buffer if compression is efficient. Else - write header and original data + /* Write encoded data to output buffer if compression is efficient. Otherwise, write header and original data */ if(!encode_failed && (res_buff_size < data_in_size + 1)) { CompressHeader header = { - .is_compressed = 0x01, .reserved = 0x00, .compressed_buff_size = res_buff_size}; + .is_compressed = 0x01, + .reserved = 0x00, + .compressed_buff_size = res_buff_size - sizeof(CompressHeader)}; memcpy(data_out, &header, sizeof(header)); *data_res_size = res_buff_size; } else if(data_out_size > data_in_size) { @@ -183,22 +179,21 @@ bool compress_encode( *data_res_size = 0; result = false; } - compress_reset(compress); - + heatshrink_encoder_reset(encoder); return result; } -bool compress_decode( - Compress* compress, - uint8_t* data_in, +static bool compress_decode_internal( + heatshrink_decoder* decoder, + const uint8_t* data_in, size_t data_in_size, uint8_t* data_out, size_t data_out_size, size_t* data_res_size) { - furi_assert(compress); - furi_assert(data_in); - furi_assert(data_out); - furi_assert(data_res_size); + furi_check(decoder); + furi_check(data_in); + furi_check(data_out); + furi_check(data_res_size); bool result = false; bool decode_failed = false; @@ -211,12 +206,15 @@ bool compress_decode( CompressHeader* header = (CompressHeader*)data_in; if(header->is_compressed) { - // Sink data to decoding buffer + /* Sink data to decoding buffer */ size_t compressed_size = header->compressed_buff_size; - size_t sunk = sizeof(CompressHeader); + size_t sunk = 0; while(sunk < compressed_size && !decode_failed) { sink_res = heatshrink_decoder_sink( - compress->decoder, &data_in[sunk], compressed_size - sunk, &sink_size); + decoder, + (uint8_t*)&data_in[sizeof(CompressHeader) + sunk], + compressed_size - sunk, + &sink_size); if(sink_res < 0) { decode_failed = true; break; @@ -224,25 +222,28 @@ bool compress_decode( sunk += sink_size; do { poll_res = heatshrink_decoder_poll( - compress->decoder, &data_out[res_buff_size], data_out_size, &poll_size); - if(poll_res < 0) { + decoder, &data_out[res_buff_size], data_out_size - res_buff_size, &poll_size); + if((poll_res < 0) || ((data_out_size - res_buff_size) == 0)) { decode_failed = true; break; } res_buff_size += poll_size; } while(poll_res == HSDR_POLL_MORE); } - // Notify sinking complete and poll decoded data + /* Notify sinking complete and poll decoded data */ if(!decode_failed) { - finish_res = heatshrink_decoder_finish(compress->decoder); + finish_res = heatshrink_decoder_finish(decoder); if(finish_res < 0) { decode_failed = true; } else { do { poll_res = heatshrink_decoder_poll( - compress->decoder, &data_out[res_buff_size], data_out_size, &poll_size); + decoder, + &data_out[res_buff_size], + data_out_size - res_buff_size, + &poll_size); res_buff_size += poll_size; - finish_res = heatshrink_decoder_finish(compress->decoder); + finish_res = heatshrink_decoder_finish(decoder); } while(finish_res != HSDR_FINISH_DONE); } } @@ -253,9 +254,31 @@ bool compress_decode( *data_res_size = data_in_size - 1; result = true; } else { + /* Not enough space in output buffer */ result = false; } - compress_reset(compress); - + heatshrink_decoder_reset(decoder); return result; } + +bool compress_encode( + Compress* compress, + uint8_t* data_in, + size_t data_in_size, + uint8_t* data_out, + size_t data_out_size, + size_t* data_res_size) { + return compress_encode_internal( + compress->encoder, data_in, data_in_size, data_out, data_out_size, data_res_size); +} + +bool compress_decode( + Compress* compress, + uint8_t* data_in, + size_t data_in_size, + uint8_t* data_out, + size_t data_out_size, + size_t* data_res_size) { + return compress_decode_internal( + compress->decoder, data_in, data_in_size, data_out, data_out_size, data_res_size); +} diff --git a/lib/toolbox/compress.h b/lib/toolbox/compress.h index f844802ec..f08e17584 100644 --- a/lib/toolbox/compress.h +++ b/lib/toolbox/compress.h @@ -16,10 +16,14 @@ extern "C" { typedef struct CompressIcon CompressIcon; /** Initialize icon compressor + * + * @param[in] decode_buf_size The icon buffer size for decoding. Ensure that + * it's big enough for any icons that you are + * planning to decode with it. * * @return Compress Icon instance */ -CompressIcon* compress_icon_alloc(void); +CompressIcon* compress_icon_alloc(size_t decode_buf_size); /** Free icon compressor * @@ -29,14 +33,16 @@ void compress_icon_free(CompressIcon* instance); /** Decompress icon * - * @warning decoded_buff pointer set by this function is valid till next + * @warning output pointer set by this function is valid till next * `compress_icon_decode` or `compress_icon_free` call * - * @param instance The Compress Icon instance - * @param icon_data pointer to icon data - * @param[in] decoded_buff pointer to decoded buffer pointer + * @param instance The Compress Icon instance + * @param icon_data pointer to icon data. + * @param[in] output pointer to decoded buffer pointer. Data in buffer is + * valid till next call. If icon data was not compressed, + * pointer within icon_data is returned */ -void compress_icon_decode(CompressIcon* instance, const uint8_t* icon_data, uint8_t** decoded_buff); +void compress_icon_decode(CompressIcon* instance, const uint8_t* icon_data, uint8_t** output); /** Compress control structure */ typedef struct Compress Compress; diff --git a/scripts/assets.py b/scripts/assets.py index 711c1b440..c3d6081c8 100755 --- a/scripts/assets.py +++ b/scripts/assets.py @@ -24,8 +24,8 @@ ICONS_TEMPLATE_C_FRAME = "const uint8_t {name}[] = {data};\n" ICONS_TEMPLATE_C_DATA = "const uint8_t* const {name}[] = {data};\n" ICONS_TEMPLATE_C_ICONS = "const Icon {name} = {{.width={width},.height={height},.frame_count={frame_count},.frame_rate={frame_rate},.frames=_{name}}};\n" -MAX_IMAGE_WIDTH = 128 -MAX_IMAGE_HEIGHT = 64 +MAX_IMAGE_WIDTH = 2**16 - 1 +MAX_IMAGE_HEIGHT = 2**16 - 1 class Main(App): diff --git a/scripts/fbt/appmanifest.py b/scripts/fbt/appmanifest.py index d32869b10..b5b5d6e12 100644 --- a/scripts/fbt/appmanifest.py +++ b/scripts/fbt/appmanifest.py @@ -351,10 +351,10 @@ class AppBuildset: ).append(app) def get_ext_apps(self): - return self.extapps + return list(self.extapps) def get_incompatible_ext_apps(self): - return self.incompatible_extapps + return list(self.incompatible_extapps) def _check_conflicts(self): conflicts = [] @@ -399,14 +399,30 @@ class AppBuildset: def _group_plugins(self): known_extensions = self.get_apps_of_type(FlipperAppType.PLUGIN, all_known=True) for extension_app in known_extensions: + keep_app = False for parent_app_id in extension_app.requires: try: parent_app = self.appmgr.get(parent_app_id) parent_app._plugins.append(extension_app) + + if ( + parent_app.apptype in self.BUILTIN_APP_TYPES + and parent_app_id in self.appnames + ) or parent_app.apptype not in self.BUILTIN_APP_TYPES: + keep_app |= True + except FlipperManifestException: self._writer( f"Module {extension_app.appid} has unknown parent {parent_app_id}" ) + keep_app = True + # Debug output for plugin parentage + # print( + # f"Module {extension_app.appid} has parents {extension_app.requires} keep={keep_app}" + # ) + if not keep_app and extension_app in self.extapps: + # print(f"Excluding plugin {extension_app.appid}") + self.extapps.remove(extension_app) def get_apps_cdefs(self): cdefs = set() @@ -432,9 +448,11 @@ class AppBuildset: return sorted( filter( lambda app: app.apptype == apptype, - self.appmgr.known_apps.values() - if all_known - else map(self.appmgr.get, self.appnames), + ( + self.appmgr.known_apps.values() + if all_known + else map(self.appmgr.get, self.appnames) + ), ), key=lambda app: app.order, ) diff --git a/scripts/fbt_tools/fbt_extapps.py b/scripts/fbt_tools/fbt_extapps.py index a7914c4f8..540510b0d 100644 --- a/scripts/fbt_tools/fbt_extapps.py +++ b/scripts/fbt_tools/fbt_extapps.py @@ -461,7 +461,8 @@ def _gather_app_components(env, appname) -> AppDeploymentComponents: else: # host app is a built-in app components.add_app(artifacts_app_to_run) - components.extra_launch_args = f"-a {host_app.name}" + if host_app.name: + components.extra_launch_args = f"-a {host_app.name}" else: raise UserError("Host app is unknown") else: diff --git a/scripts/fbt_tools/fbt_sdk.py b/scripts/fbt_tools/fbt_sdk.py index a3f7faa57..ef2aa9146 100644 --- a/scripts/fbt_tools/fbt_sdk.py +++ b/scripts/fbt_tools/fbt_sdk.py @@ -198,6 +198,7 @@ def gen_sdk_data(sdk_cache: SdkCache): api_def.extend( (f"#include <{h.name}>" for h in sdk_cache.get_headers()), ) + api_def.append('#pragma GCC diagnostic ignored "-Wdeprecated-declarations"') api_def.append(f"const int elf_api_version = {sdk_cache.version.as_int()};") diff --git a/targets/f18/api_symbols.csv b/targets/f18/api_symbols.csv index ce7ad2536..04ceb928c 100644 --- a/targets/f18/api_symbols.csv +++ b/targets/f18/api_symbols.csv @@ -1,6 +1,7 @@ entry,status,name,type,params -Version,+,61.4,, +Version,+,62.3,, Header,+,applications/services/bt/bt_service/bt.h,, +Header,+,applications/services/bt/bt_service/bt_keys_storage.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, Header,+,applications/services/dialogs/dialogs.h,, @@ -156,10 +157,12 @@ Header,+,lib/toolbox/hex.h,, Header,+,lib/toolbox/keys_dict.h,, Header,+,lib/toolbox/manchester_decoder.h,, Header,+,lib/toolbox/manchester_encoder.h,, +Header,+,lib/toolbox/md5_calc.h,, Header,+,lib/toolbox/name_generator.h,, Header,+,lib/toolbox/path.h,, Header,+,lib/toolbox/pretty_format.h,, Header,+,lib/toolbox/protocols/protocol_dict.h,, +Header,+,lib/toolbox/pulse_protocols/pulse_glue.h,, Header,+,lib/toolbox/saved_struct.h,, Header,+,lib/toolbox/simple_array.h,, Header,+,lib/toolbox/stream/buffered_file_stream.h,, @@ -168,6 +171,7 @@ Header,+,lib/toolbox/stream/stream.h,, Header,+,lib/toolbox/stream/string_stream.h,, Header,+,lib/toolbox/tar/tar_archive.h,, Header,+,lib/toolbox/value_index.h,, +Header,+,lib/toolbox/varint.h,, Header,+,lib/toolbox/version.h,, Header,+,targets/f18/furi_hal/furi_hal_resources.h,, Header,+,targets/f18/furi_hal/furi_hal_spi_config.h,, @@ -680,8 +684,15 @@ Function,+,ble_svc_serial_update_tx,_Bool,"BleServiceSerial*, uint8_t*, uint16_t Function,-,bsearch,void*,"const void*, const void*, size_t, size_t, __compar_fn_t" Function,+,bt_disconnect,void,Bt* Function,+,bt_forget_bonded_devices,void,Bt* +Function,+,bt_keys_storage_alloc,BtKeysStorage*,const char* +Function,+,bt_keys_storage_delete,_Bool,BtKeysStorage* +Function,+,bt_keys_storage_free,void,BtKeysStorage* +Function,+,bt_keys_storage_load,_Bool,BtKeysStorage* Function,+,bt_keys_storage_set_default_path,void,Bt* +Function,+,bt_keys_storage_set_file_path,void,"BtKeysStorage*, const char*" +Function,+,bt_keys_storage_set_ram_params,void,"BtKeysStorage*, uint8_t*, uint16_t" Function,+,bt_keys_storage_set_storage_path,void,"Bt*, const char*" +Function,+,bt_keys_storage_update,_Bool,"BtKeysStorage*, uint8_t*, uint32_t" Function,+,bt_profile_restore_default,_Bool,Bt* Function,+,bt_profile_start,FuriHalBleProfileBase*,"Bt*, const FuriHalBleProfileTemplate*, FuriHalBleProfileParams" Function,+,bt_set_status_changed_callback,void,"Bt*, BtStatusChangedCallback, void*" @@ -773,7 +784,7 @@ Function,+,compress_alloc,Compress*,uint16_t Function,+,compress_decode,_Bool,"Compress*, uint8_t*, size_t, uint8_t*, size_t, size_t*" Function,+,compress_encode,_Bool,"Compress*, uint8_t*, size_t, uint8_t*, size_t, size_t*" Function,+,compress_free,void,Compress* -Function,+,compress_icon_alloc,CompressIcon*, +Function,+,compress_icon_alloc,CompressIcon*,size_t Function,+,compress_icon_decode,void,"CompressIcon*, const uint8_t*, uint8_t**" Function,+,compress_icon_free,void,CompressIcon* Function,-,copysign,double,"double, double" @@ -1490,7 +1501,7 @@ Function,+,furi_mutex_free,void,FuriMutex* Function,+,furi_mutex_get_owner,FuriThreadId,FuriMutex* Function,+,furi_mutex_release,FuriStatus,FuriMutex* Function,+,furi_pubsub_alloc,FuriPubSub*, -Function,-,furi_pubsub_free,void,FuriPubSub* +Function,+,furi_pubsub_free,void,FuriPubSub* Function,+,furi_pubsub_publish,void,"FuriPubSub*, void*" Function,+,furi_pubsub_subscribe,FuriPubSubSubscription*,"FuriPubSub*, FuriPubSubCallback, void*" Function,+,furi_pubsub_unsubscribe,void,"FuriPubSub*, FuriPubSubSubscription*" @@ -1675,8 +1686,10 @@ Function,+,icon_animation_set_update_callback,void,"IconAnimation*, IconAnimatio Function,+,icon_animation_start,void,IconAnimation* Function,+,icon_animation_stop,void,IconAnimation* Function,+,icon_get_data,const uint8_t*,const Icon* -Function,+,icon_get_height,uint8_t,const Icon* -Function,+,icon_get_width,uint8_t,const Icon* +Function,+,icon_get_frame_count,uint32_t,const Icon* +Function,+,icon_get_frame_data,const uint8_t*,"const Icon*, uint32_t" +Function,+,icon_get_height,uint16_t,const Icon* +Function,+,icon_get_width,uint16_t,const Icon* Function,-,ilogb,int,double Function,-,ilogbf,int,float Function,-,ilogbl,int,long double @@ -1980,6 +1993,8 @@ Function,-,mbedtls_sha256_update,int,"mbedtls_sha256_context*, const unsigned ch Function,-,mblen,int,"const char*, size_t" Function,-,mbstowcs,size_t,"wchar_t*, const char*, size_t" Function,-,mbtowc,int,"wchar_t*, const char*, size_t" +Function,+,md5_calc_file,_Bool,"File*, const char*, unsigned char[16], FS_Error*" +Function,+,md5_string_calc_file,_Bool,"File*, const char*, FuriString*, FS_Error*" Function,-,memccpy,void*,"void*, const void*, int, size_t" Function,+,memchr,void*,"const void*, int, size_t" Function,+,memcmp,int,"const void*, const void*, size_t" @@ -2254,6 +2269,11 @@ Function,+,protocol_dict_render_brief_data,void,"ProtocolDict*, FuriString*, siz Function,+,protocol_dict_render_data,void,"ProtocolDict*, FuriString*, size_t" Function,+,protocol_dict_render_uid,void,"ProtocolDict*, FuriString*, size_t" Function,+,protocol_dict_set_data,void,"ProtocolDict*, size_t, const uint8_t*, size_t" +Function,+,pulse_glue_alloc,PulseGlue*, +Function,+,pulse_glue_free,void,PulseGlue* +Function,+,pulse_glue_pop,void,"PulseGlue*, uint32_t*, uint32_t*" +Function,+,pulse_glue_push,_Bool,"PulseGlue*, _Bool, uint32_t" +Function,+,pulse_glue_reset,void,PulseGlue* Function,-,pulse_reader_alloc,PulseReader*,"const GpioPin*, uint32_t" Function,-,pulse_reader_free,void,PulseReader* Function,-,pulse_reader_receive,uint32_t,"PulseReader*, int" @@ -2642,6 +2662,12 @@ Function,+,variable_item_list_set_selected_item,void,"VariableItemList*, uint8_t Function,+,variable_item_set_current_value_index,void,"VariableItem*, uint8_t" Function,+,variable_item_set_current_value_text,void,"VariableItem*, const char*" Function,+,variable_item_set_values_count,void,"VariableItem*, uint8_t" +Function,+,varint_int32_length,size_t,int32_t +Function,+,varint_int32_pack,size_t,"int32_t, uint8_t*" +Function,+,varint_int32_unpack,size_t,"int32_t*, const uint8_t*, size_t" +Function,+,varint_uint32_length,size_t,uint32_t +Function,+,varint_uint32_pack,size_t,"uint32_t, uint8_t*" +Function,+,varint_uint32_unpack,size_t,"uint32_t*, const uint8_t*, size_t" Function,-,vasiprintf,int,"char**, const char*, __gnuc_va_list" Function,-,vasniprintf,char*,"char*, size_t*, const char*, __gnuc_va_list" Function,-,vasnprintf,char*,"char*, size_t*, const char*, __gnuc_va_list" diff --git a/targets/f18/target.json b/targets/f18/target.json index 43e9254cd..229ec0a7a 100644 --- a/targets/f18/target.json +++ b/targets/f18/target.json @@ -30,7 +30,6 @@ "mjs", "mbedtls", "flipper_application", - "toolbox", "u8g2", "nanopb", "update_util", @@ -38,6 +37,7 @@ "flipperformat", "flipper18", "bit_lib", + "toolbox", "datetime" ], "excluded_sources": [ diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 5a44dbc6f..e7a97aff9 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -1,7 +1,8 @@ entry,status,name,type,params -Version,+,61.4,, +Version,+,62.3,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, Header,+,applications/services/bt/bt_service/bt.h,, +Header,+,applications/services/bt/bt_service/bt_keys_storage.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, Header,+,applications/services/dialogs/dialogs.h,, @@ -158,6 +159,7 @@ Header,+,lib/nfc/protocols/mf_ultralight/mf_ultralight_listener.h,, Header,+,lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.h,, Header,+,lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_sync.h,, Header,+,lib/nfc/protocols/slix/slix.h,, +Header,+,lib/nfc/protocols/slix/slix_poller.h,, Header,+,lib/nfc/protocols/st25tb/st25tb.h,, Header,+,lib/nfc/protocols/st25tb/st25tb_poller.h,, Header,+,lib/nfc/protocols/st25tb/st25tb_poller_sync.h,, @@ -206,6 +208,7 @@ Header,+,lib/subghz/protocols/public_api.h,, Header,+,lib/subghz/protocols/raw.h,, Header,+,lib/subghz/receiver.h,, Header,+,lib/subghz/registry.h,, +Header,+,lib/subghz/subghz_file_encoder_worker.h,, Header,+,lib/subghz/subghz_protocol_registry.h,, Header,+,lib/subghz/subghz_setting.h,, Header,+,lib/subghz/subghz_tx_rx_worker.h,, @@ -222,10 +225,12 @@ Header,+,lib/toolbox/hex.h,, Header,+,lib/toolbox/keys_dict.h,, Header,+,lib/toolbox/manchester_decoder.h,, Header,+,lib/toolbox/manchester_encoder.h,, +Header,+,lib/toolbox/md5_calc.h,, Header,+,lib/toolbox/name_generator.h,, Header,+,lib/toolbox/path.h,, Header,+,lib/toolbox/pretty_format.h,, Header,+,lib/toolbox/protocols/protocol_dict.h,, +Header,+,lib/toolbox/pulse_protocols/pulse_glue.h,, Header,+,lib/toolbox/saved_struct.h,, Header,+,lib/toolbox/simple_array.h,, Header,+,lib/toolbox/stream/buffered_file_stream.h,, @@ -234,6 +239,7 @@ Header,+,lib/toolbox/stream/stream.h,, Header,+,lib/toolbox/stream/string_stream.h,, Header,+,lib/toolbox/tar/tar_archive.h,, Header,+,lib/toolbox/value_index.h,, +Header,+,lib/toolbox/varint.h,, Header,+,lib/toolbox/version.h,, Header,+,targets/f7/ble_glue/furi_ble/event_dispatcher.h,, Header,+,targets/f7/ble_glue/furi_ble/gatt.h,, @@ -751,8 +757,15 @@ Function,+,ble_svc_serial_update_tx,_Bool,"BleServiceSerial*, uint8_t*, uint16_t Function,-,bsearch,void*,"const void*, const void*, size_t, size_t, __compar_fn_t" Function,+,bt_disconnect,void,Bt* Function,+,bt_forget_bonded_devices,void,Bt* +Function,+,bt_keys_storage_alloc,BtKeysStorage*,const char* +Function,+,bt_keys_storage_delete,_Bool,BtKeysStorage* +Function,+,bt_keys_storage_free,void,BtKeysStorage* +Function,+,bt_keys_storage_load,_Bool,BtKeysStorage* Function,+,bt_keys_storage_set_default_path,void,Bt* +Function,+,bt_keys_storage_set_file_path,void,"BtKeysStorage*, const char*" +Function,+,bt_keys_storage_set_ram_params,void,"BtKeysStorage*, uint8_t*, uint16_t" Function,+,bt_keys_storage_set_storage_path,void,"Bt*, const char*" +Function,+,bt_keys_storage_update,_Bool,"BtKeysStorage*, uint8_t*, uint32_t" Function,+,bt_profile_restore_default,_Bool,Bt* Function,+,bt_profile_start,FuriHalBleProfileBase*,"Bt*, const FuriHalBleProfileTemplate*, FuriHalBleProfileParams" Function,+,bt_set_status_changed_callback,void,"Bt*, BtStatusChangedCallback, void*" @@ -844,7 +857,7 @@ Function,+,compress_alloc,Compress*,uint16_t Function,+,compress_decode,_Bool,"Compress*, uint8_t*, size_t, uint8_t*, size_t, size_t*" Function,+,compress_encode,_Bool,"Compress*, uint8_t*, size_t, uint8_t*, size_t, size_t*" Function,+,compress_free,void,Compress* -Function,+,compress_icon_alloc,CompressIcon*, +Function,+,compress_icon_alloc,CompressIcon*,size_t Function,+,compress_icon_decode,void,"CompressIcon*, const uint8_t*, uint8_t**" Function,+,compress_icon_free,void,CompressIcon* Function,-,copysign,double,"double, double" @@ -1696,7 +1709,7 @@ Function,+,furi_mutex_free,void,FuriMutex* Function,+,furi_mutex_get_owner,FuriThreadId,FuriMutex* Function,+,furi_mutex_release,FuriStatus,FuriMutex* Function,+,furi_pubsub_alloc,FuriPubSub*, -Function,-,furi_pubsub_free,void,FuriPubSub* +Function,+,furi_pubsub_free,void,FuriPubSub* Function,+,furi_pubsub_publish,void,"FuriPubSub*, void*" Function,+,furi_pubsub_subscribe,FuriPubSubSubscription*,"FuriPubSub*, FuriPubSubCallback, void*" Function,+,furi_pubsub_unsubscribe,void,"FuriPubSub*, FuriPubSubSubscription*" @@ -1920,8 +1933,10 @@ Function,+,icon_animation_set_update_callback,void,"IconAnimation*, IconAnimatio Function,+,icon_animation_start,void,IconAnimation* Function,+,icon_animation_stop,void,IconAnimation* Function,+,icon_get_data,const uint8_t*,const Icon* -Function,+,icon_get_height,uint8_t,const Icon* -Function,+,icon_get_width,uint8_t,const Icon* +Function,+,icon_get_frame_count,uint32_t,const Icon* +Function,+,icon_get_frame_data,const uint8_t*,"const Icon*, uint32_t" +Function,+,icon_get_height,uint16_t,const Icon* +Function,+,icon_get_width,uint16_t,const Icon* Function,-,ilogb,int,double Function,-,ilogbf,int,float Function,-,ilogbl,int,long double @@ -2390,6 +2405,8 @@ Function,-,mbedtls_sha256_update,int,"mbedtls_sha256_context*, const unsigned ch Function,-,mblen,int,"const char*, size_t" Function,-,mbstowcs,size_t,"wchar_t*, const char*, size_t" Function,-,mbtowc,int,"wchar_t*, const char*, size_t" +Function,+,md5_calc_file,_Bool,"File*, const char*, unsigned char[16], FS_Error*" +Function,+,md5_string_calc_file,_Bool,"File*, const char*, FuriString*, FS_Error*" Function,-,memccpy,void*,"void*, const void*, int, size_t" Function,+,memchr,void*,"const void*, int, size_t" Function,+,memcmp,int,"const void*, const void*, size_t" @@ -2852,6 +2869,11 @@ Function,+,protocol_dict_render_brief_data,void,"ProtocolDict*, FuriString*, siz Function,+,protocol_dict_render_data,void,"ProtocolDict*, FuriString*, size_t" Function,+,protocol_dict_render_uid,void,"ProtocolDict*, FuriString*, size_t" Function,+,protocol_dict_set_data,void,"ProtocolDict*, size_t, const uint8_t*, size_t" +Function,+,pulse_glue_alloc,PulseGlue*, +Function,+,pulse_glue_free,void,PulseGlue* +Function,+,pulse_glue_pop,void,"PulseGlue*, uint32_t*, uint32_t*" +Function,+,pulse_glue_push,_Bool,"PulseGlue*, _Bool, uint32_t" +Function,+,pulse_glue_reset,void,PulseGlue* Function,-,pulse_reader_alloc,PulseReader*,"const GpioPin*, uint32_t" Function,-,pulse_reader_free,void,PulseReader* Function,-,pulse_reader_receive,uint32_t,"PulseReader*, int" @@ -2992,6 +3014,11 @@ Function,+,slix_is_counter_increment_protected,_Bool,const SlixData* Function,+,slix_is_equal,_Bool,"const SlixData*, const SlixData*" Function,+,slix_is_privacy_mode,_Bool,const SlixData* Function,+,slix_load,_Bool,"SlixData*, FlipperFormat*, uint32_t" +Function,+,slix_poller_get_nxp_system_info,SlixError,"SlixPoller*, SlixSystemInfo*" +Function,+,slix_poller_get_random_number,SlixError,"SlixPoller*, SlixRandomNumber*" +Function,+,slix_poller_read_signature,SlixError,"SlixPoller*, SlixSignature*" +Function,+,slix_poller_send_frame,SlixError,"SlixPoller*, const BitBuffer*, BitBuffer*, uint32_t" +Function,+,slix_poller_set_password,SlixError,"SlixPoller*, SlixPasswordType, SlixPassword, SlixRandomNumber" Function,+,slix_reset,void,SlixData* Function,+,slix_save,_Bool,"const SlixData*, FlipperFormat*" Function,+,slix_set_uid,_Bool,"SlixData*, const uint8_t*, size_t" @@ -3244,6 +3271,13 @@ Function,+,subghz_environment_set_alutech_at_4n_rainbow_table_file_name,void,"Su Function,+,subghz_environment_set_came_atomo_rainbow_table_file_name,void,"SubGhzEnvironment*, const char*" Function,+,subghz_environment_set_nice_flor_s_rainbow_table_file_name,void,"SubGhzEnvironment*, const char*" Function,+,subghz_environment_set_protocol_registry,void,"SubGhzEnvironment*, const SubGhzProtocolRegistry*" +Function,+,subghz_file_encoder_worker_alloc,SubGhzFileEncoderWorker*, +Function,+,subghz_file_encoder_worker_callback_end,void,"SubGhzFileEncoderWorker*, SubGhzFileEncoderWorkerCallbackEnd, void*" +Function,+,subghz_file_encoder_worker_free,void,SubGhzFileEncoderWorker* +Function,+,subghz_file_encoder_worker_get_level_duration,LevelDuration,void* +Function,+,subghz_file_encoder_worker_is_running,_Bool,SubGhzFileEncoderWorker* +Function,+,subghz_file_encoder_worker_start,_Bool,"SubGhzFileEncoderWorker*, const char*, const char*" +Function,+,subghz_file_encoder_worker_stop,void,SubGhzFileEncoderWorker* Function,-,subghz_keystore_alloc,SubGhzKeystore*, Function,-,subghz_keystore_free,void,SubGhzKeystore* Function,-,subghz_keystore_get_data,SubGhzKeyArray_t*,SubGhzKeystore* @@ -3442,6 +3476,12 @@ Function,+,variable_item_list_set_selected_item,void,"VariableItemList*, uint8_t Function,+,variable_item_set_current_value_index,void,"VariableItem*, uint8_t" Function,+,variable_item_set_current_value_text,void,"VariableItem*, const char*" Function,+,variable_item_set_values_count,void,"VariableItem*, uint8_t" +Function,+,varint_int32_length,size_t,int32_t +Function,+,varint_int32_pack,size_t,"int32_t, uint8_t*" +Function,+,varint_int32_unpack,size_t,"int32_t*, const uint8_t*, size_t" +Function,+,varint_uint32_length,size_t,uint32_t +Function,+,varint_uint32_pack,size_t,"uint32_t, uint8_t*" +Function,+,varint_uint32_unpack,size_t,"uint32_t*, const uint8_t*, size_t" Function,-,vasiprintf,int,"char**, const char*, __gnuc_va_list" Function,-,vasniprintf,char*,"char*, size_t*, const char*, __gnuc_va_list" Function,-,vasnprintf,char*,"char*, size_t*, const char*, __gnuc_va_list" diff --git a/targets/f7/target.json b/targets/f7/target.json index 25872198b..eae92a5cd 100644 --- a/targets/f7/target.json +++ b/targets/f7/target.json @@ -45,7 +45,6 @@ "mbedtls", "lfrfid", "flipper_application", - "toolbox", "u8g2", "nanopb", "update_util", @@ -53,6 +52,7 @@ "flipperformat", "flipper7", "bit_lib", + "toolbox", "datetime" ] } From e0797131eca42411e6c31d799b9f7c407f50a79e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Mon, 20 May 2024 19:17:01 +0100 Subject: [PATCH 3/3] FuriHal: add flash ops stats, workaround bug in SHCI_C2_SetSystemClock (#3657) * FuriHal: add flash ops stats, workaround bug in SHCI_C2_SetSystemClock * hal: flash: added FLASH_OP_DEBUG to enable latency measurement Co-authored-by: hedger Co-authored-by: hedger --- targets/f7/furi_hal/furi_hal_clock.c | 20 +++++++++++--------- targets/f7/furi_hal/furi_hal_flash.c | 27 +++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 9 deletions(-) diff --git a/targets/f7/furi_hal/furi_hal_clock.c b/targets/f7/furi_hal/furi_hal_clock.c index 9184fa715..ca4c7ad4d 100644 --- a/targets/f7/furi_hal/furi_hal_clock.c +++ b/targets/f7/furi_hal/furi_hal_clock.c @@ -133,7 +133,7 @@ void furi_hal_clock_switch_hse2hsi(void) { ; LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_HSI); - furi_assert(LL_RCC_GetSMPSClockSelection() == LL_RCC_SMPS_CLKSOURCE_HSI); + furi_check(LL_RCC_GetSMPSClockSelection() == LL_RCC_SMPS_CLKSOURCE_HSI); while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_HSI) ; @@ -170,7 +170,7 @@ void furi_hal_clock_switch_hsi2hse(void) { } bool furi_hal_clock_switch_hse2pll(void) { - furi_assert(LL_RCC_GetSysClkSource() == LL_RCC_SYS_CLKSOURCE_STATUS_HSE); + furi_check(LL_RCC_GetSysClkSource() == LL_RCC_SYS_CLKSOURCE_STATUS_HSE); LL_RCC_PLL_Enable(); LL_RCC_PLLSAI1_Enable(); @@ -180,12 +180,13 @@ bool furi_hal_clock_switch_hse2pll(void) { while(!LL_RCC_PLLSAI1_IsReady()) ; - if(SHCI_C2_SetSystemClock(SET_SYSTEM_CLOCK_HSE_TO_PLL) != SHCI_Success) { + // This API returns garbage if stack version < 1.20.0 + SHCI_C2_SetSystemClock(SET_SYSTEM_CLOCK_HSE_TO_PLL); + // So we'll check results by asking hardware directly + if(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_PLL) { return false; } - furi_check(LL_RCC_GetSysClkSource() == LL_RCC_SYS_CLKSOURCE_STATUS_PLL); - LL_SetSystemCoreClock(CPU_CLOCK_PLL_HZ); SysTick->LOAD = (uint32_t)((SystemCoreClock / 1000) - 1UL); @@ -193,18 +194,19 @@ bool furi_hal_clock_switch_hse2pll(void) { } bool furi_hal_clock_switch_pll2hse(void) { - furi_assert(LL_RCC_GetSysClkSource() == LL_RCC_SYS_CLKSOURCE_STATUS_PLL); + furi_check(LL_RCC_GetSysClkSource() == LL_RCC_SYS_CLKSOURCE_STATUS_PLL); LL_RCC_HSE_Enable(); while(!LL_RCC_HSE_IsReady()) ; - if(SHCI_C2_SetSystemClock(SET_SYSTEM_CLOCK_PLL_ON_TO_HSE) != SHCI_Success) { + // This API returns garbage if stack version < 1.20.0 + SHCI_C2_SetSystemClock(SET_SYSTEM_CLOCK_PLL_ON_TO_HSE); + // So we'll check results by asking hardware directly + if(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_HSE) { return false; } - furi_check(LL_RCC_GetSysClkSource() == LL_RCC_SYS_CLKSOURCE_STATUS_HSE); - LL_SetSystemCoreClock(CPU_CLOCK_HSE_HZ); SysTick->LOAD = (uint32_t)((SystemCoreClock / 1000) - 1UL); diff --git a/targets/f7/furi_hal/furi_hal_flash.c b/targets/f7/furi_hal/furi_hal_flash.c index 138e07eab..43f80c61e 100644 --- a/targets/f7/furi_hal/furi_hal_flash.c +++ b/targets/f7/furi_hal/furi_hal_flash.c @@ -16,6 +16,12 @@ #define TAG "FuriHalFlash" +#ifdef FLASH_OP_DEBUG +#undef FURI_LOG_T +#define FURI_LOG_T(...) +#else +#endif + #define FURI_HAL_CRITICAL_MSG "Critical flash operation fail" #define FURI_HAL_FLASH_READ_BLOCK (8U) #define FURI_HAL_FLASH_WRITE_BLOCK (8U) @@ -291,6 +297,7 @@ bool furi_hal_flash_wait_last_operation(uint32_t timeout) { } void furi_hal_flash_erase(uint8_t page) { + uint32_t op_stat = DWT->CYCCNT; furi_hal_flash_begin(true); /* Ensure that controller state is valid */ @@ -313,6 +320,12 @@ void furi_hal_flash_erase(uint8_t page) { furi_hal_flush_cache(); furi_hal_flash_end(true); + op_stat = DWT->CYCCNT - op_stat; + FURI_LOG_T( + TAG, + "erase took %lu clocks or %fus", + op_stat, + (double)((float)op_stat / (float)furi_hal_cortex_instructions_per_microsecond())); } static inline void furi_hal_flash_write_dword_internal_nowait(size_t address, uint64_t* data) { @@ -335,6 +348,7 @@ static inline void furi_hal_flash_write_dword_internal(size_t address, uint64_t* } void furi_hal_flash_write_dword(size_t address, uint64_t data) { + uint32_t op_stat = DWT->CYCCNT; furi_hal_flash_begin(false); /* Ensure that controller state is valid */ @@ -357,6 +371,12 @@ void furi_hal_flash_write_dword(size_t address, uint64_t data) { /* Wait for last operation to be completed */ furi_check(furi_hal_flash_wait_last_operation(FURI_HAL_FLASH_TIMEOUT)); + op_stat = DWT->CYCCNT - op_stat; + FURI_LOG_T( + TAG, + "write_dword took %lu clocks or %fus", + op_stat, + (double)((float)op_stat / (float)furi_hal_cortex_instructions_per_microsecond())); } static size_t furi_hal_flash_get_page_address(uint8_t page) { @@ -369,6 +389,7 @@ void furi_hal_flash_program_page(const uint8_t page, const uint8_t* data, uint16 furi_hal_flash_erase(page); + uint32_t op_stat = DWT->CYCCNT; furi_hal_flash_begin(false); furi_check(furi_hal_flash_wait_last_operation(FURI_HAL_FLASH_TIMEOUT)); @@ -428,6 +449,12 @@ void furi_hal_flash_program_page(const uint8_t page, const uint8_t* data, uint16 CLEAR_BIT(FLASH->CR, FLASH_CR_PG); furi_hal_flash_end(false); + op_stat = DWT->CYCCNT - op_stat; + FURI_LOG_T( + TAG, + "program_page took %lu clocks or %fus", + op_stat, + (double)((float)op_stat / (float)furi_hal_cortex_instructions_per_microsecond())); } int16_t furi_hal_flash_get_page_number(size_t address) {