From f353e5708de6d509e1a9245d4c3e23fc9f5ffafe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Thu, 5 Sep 2024 22:32:48 +0900 Subject: [PATCH 1/8] Gui: change dialog_ex text ownership model (#3831) * Gui: change dialog_ex text ownership model * Gui: change text ownership model part 2 * Examples: fix DialogEx usage in number input * Gui: fix nullptr dereference in DialogEx, proper reset procedure Co-authored-by: hedger --- .../example_number_input_scene_show_number.c | 3 +- applications/services/gui/modules/dialog_ex.c | 119 ++++++++++-------- 2 files changed, 69 insertions(+), 53 deletions(-) diff --git a/applications/examples/example_number_input/scenes/example_number_input_scene_show_number.c b/applications/examples/example_number_input/scenes/example_number_input_scene_show_number.c index 2afdaf5c1..d10d97c24 100644 --- a/applications/examples/example_number_input/scenes/example_number_input_scene_show_number.c +++ b/applications/examples/example_number_input/scenes/example_number_input_scene_show_number.c @@ -13,8 +13,7 @@ static void example_number_input_scene_update_view(void* context) { dialog_ex_set_header(dialog_ex, "The number is", 64, 0, AlignCenter, AlignTop); - static char buffer[12]; //needs static for extended lifetime - + char buffer[12] = {}; snprintf(buffer, sizeof(buffer), "%ld", app->current_number); dialog_ex_set_text(dialog_ex, buffer, 64, 29, AlignCenter, AlignCenter); diff --git a/applications/services/gui/modules/dialog_ex.c b/applications/services/gui/modules/dialog_ex.c index 7171f6892..75209a408 100644 --- a/applications/services/gui/modules/dialog_ex.c +++ b/applications/services/gui/modules/dialog_ex.c @@ -10,7 +10,7 @@ struct DialogEx { }; typedef struct { - const char* text; + FuriString* text; uint8_t x; uint8_t y; Align horizontal; @@ -28,16 +28,15 @@ typedef struct { TextElement text; IconElement icon; - const char* left_text; - const char* center_text; - const char* right_text; + FuriString* left_text; + FuriString* center_text; + FuriString* right_text; } DialogExModel; static void dialog_ex_view_draw_callback(Canvas* canvas, void* _model) { DialogExModel* model = _model; // Prepare canvas - canvas_clear(canvas); canvas_set_color(canvas, ColorBlack); if(model->icon.icon != NULL) { @@ -46,94 +45,94 @@ static void dialog_ex_view_draw_callback(Canvas* canvas, void* _model) { // Draw header canvas_set_font(canvas, FontPrimary); - if(model->header.text != NULL) { + if(furi_string_size(model->header.text)) { elements_multiline_text_aligned( canvas, model->header.x, model->header.y, model->header.horizontal, model->header.vertical, - model->header.text); + furi_string_get_cstr(model->header.text)); } // Draw text canvas_set_font(canvas, FontSecondary); - if(model->text.text != NULL) { + if(furi_string_size(model->text.text)) { elements_multiline_text_aligned( canvas, model->text.x, model->text.y, model->text.horizontal, model->text.vertical, - model->text.text); + furi_string_get_cstr(model->text.text)); } // Draw buttons - if(model->left_text != NULL) { - elements_button_left(canvas, model->left_text); + if(furi_string_size(model->left_text)) { + elements_button_left(canvas, furi_string_get_cstr(model->left_text)); } - if(model->center_text != NULL) { - elements_button_center(canvas, model->center_text); + if(furi_string_size(model->center_text)) { + elements_button_center(canvas, furi_string_get_cstr(model->center_text)); } - if(model->right_text != NULL) { - elements_button_right(canvas, model->right_text); + if(furi_string_size(model->right_text)) { + elements_button_right(canvas, furi_string_get_cstr(model->right_text)); } } static bool dialog_ex_view_input_callback(InputEvent* event, void* context) { DialogEx* dialog_ex = context; bool consumed = false; - const char* left_text = NULL; - const char* center_text = NULL; - const char* right_text = NULL; + bool left_text_present = false; + bool center_text_present = false; + bool right_text_present = false; with_view_model( dialog_ex->view, DialogExModel * model, { - left_text = model->left_text; - center_text = model->center_text; - right_text = model->right_text; + left_text_present = furi_string_size(model->left_text); + center_text_present = furi_string_size(model->center_text); + right_text_present = furi_string_size(model->right_text); }, - true); + false); if(dialog_ex->callback) { if(event->type == InputTypeShort) { - if(event->key == InputKeyLeft && left_text != NULL) { + if(event->key == InputKeyLeft && left_text_present) { dialog_ex->callback(DialogExResultLeft, dialog_ex->context); consumed = true; - } else if(event->key == InputKeyOk && center_text != NULL) { + } else if(event->key == InputKeyOk && center_text_present) { dialog_ex->callback(DialogExResultCenter, dialog_ex->context); consumed = true; - } else if(event->key == InputKeyRight && right_text != NULL) { + } else if(event->key == InputKeyRight && right_text_present) { dialog_ex->callback(DialogExResultRight, dialog_ex->context); consumed = true; } } if(event->type == InputTypePress && dialog_ex->enable_extended_events) { - if(event->key == InputKeyLeft && left_text != NULL) { + if(event->key == InputKeyLeft && left_text_present) { dialog_ex->callback(DialogExPressLeft, dialog_ex->context); consumed = true; - } else if(event->key == InputKeyOk && center_text != NULL) { + } else if(event->key == InputKeyOk && center_text_present) { dialog_ex->callback(DialogExPressCenter, dialog_ex->context); consumed = true; - } else if(event->key == InputKeyRight && right_text != NULL) { + } else if(event->key == InputKeyRight && right_text_present) { dialog_ex->callback(DialogExPressRight, dialog_ex->context); consumed = true; } } if(event->type == InputTypeRelease && dialog_ex->enable_extended_events) { - if(event->key == InputKeyLeft && left_text != NULL) { + if(event->key == InputKeyLeft && left_text_present) { dialog_ex->callback(DialogExReleaseLeft, dialog_ex->context); consumed = true; - } else if(event->key == InputKeyOk && center_text != NULL) { + } else if(event->key == InputKeyOk && center_text_present) { dialog_ex->callback(DialogExReleaseCenter, dialog_ex->context); consumed = true; - } else if(event->key == InputKeyRight && right_text != NULL) { + } else if(event->key == InputKeyRight && right_text_present) { dialog_ex->callback(DialogExReleaseRight, dialog_ex->context); consumed = true; } @@ -154,13 +153,13 @@ DialogEx* dialog_ex_alloc(void) { dialog_ex->view, DialogExModel * model, { - model->header.text = NULL; + model->header.text = furi_string_alloc(); model->header.x = 0; model->header.y = 0; model->header.horizontal = AlignLeft; model->header.vertical = AlignBottom; - model->text.text = NULL; + model->text.text = furi_string_alloc(); model->text.x = 0; model->text.y = 0; model->text.horizontal = AlignLeft; @@ -170,17 +169,28 @@ DialogEx* dialog_ex_alloc(void) { model->icon.y = 0; model->icon.icon = NULL; - model->left_text = NULL; - model->center_text = NULL; - model->right_text = NULL; + model->left_text = furi_string_alloc(); + model->center_text = furi_string_alloc(); + model->right_text = furi_string_alloc(); }, - true); + false); dialog_ex->enable_extended_events = false; return dialog_ex; } void dialog_ex_free(DialogEx* dialog_ex) { furi_check(dialog_ex); + with_view_model( + dialog_ex->view, + DialogExModel * model, + { + furi_string_free(model->header.text); + furi_string_free(model->text.text); + furi_string_free(model->left_text); + furi_string_free(model->center_text); + furi_string_free(model->right_text); + }, + false); view_free(dialog_ex->view); free(dialog_ex); } @@ -212,7 +222,7 @@ void dialog_ex_set_header( dialog_ex->view, DialogExModel * model, { - model->header.text = text; + furi_string_set(model->header.text, text); model->header.x = x; model->header.y = y; model->header.horizontal = horizontal; @@ -233,7 +243,7 @@ void dialog_ex_set_text( dialog_ex->view, DialogExModel * model, { - model->text.text = text; + furi_string_set(model->text.text, text); model->text.x = x; model->text.y = y; model->text.horizontal = horizontal; @@ -257,34 +267,41 @@ void dialog_ex_set_icon(DialogEx* dialog_ex, uint8_t x, uint8_t y, const Icon* i void dialog_ex_set_left_button_text(DialogEx* dialog_ex, const char* text) { furi_check(dialog_ex); - with_view_model(dialog_ex->view, DialogExModel * model, { model->left_text = text; }, true); + with_view_model( + dialog_ex->view, DialogExModel * model, { furi_string_set(model->left_text, text); }, true); } void dialog_ex_set_center_button_text(DialogEx* dialog_ex, const char* text) { furi_check(dialog_ex); - with_view_model(dialog_ex->view, DialogExModel * model, { model->center_text = text; }, true); + with_view_model( + dialog_ex->view, + DialogExModel * model, + { furi_string_set(model->center_text, text); }, + true); } void dialog_ex_set_right_button_text(DialogEx* dialog_ex, const char* text) { furi_check(dialog_ex); - with_view_model(dialog_ex->view, DialogExModel * model, { model->right_text = text; }, true); + with_view_model( + dialog_ex->view, + DialogExModel * model, + { furi_string_set(model->right_text, text); }, + true); } void dialog_ex_reset(DialogEx* dialog_ex) { furi_check(dialog_ex); - TextElement clean_text_el = { - .text = NULL, .x = 0, .y = 0, .horizontal = AlignLeft, .vertical = AlignLeft}; - IconElement clean_icon_el = {.icon = NULL, .x = 0, .y = 0}; with_view_model( dialog_ex->view, DialogExModel * model, { - model->header = clean_text_el; - model->text = clean_text_el; - model->icon = clean_icon_el; - model->left_text = NULL; - model->center_text = NULL; - model->right_text = NULL; + model->icon.icon = NULL; + furi_string_reset(model->header.text); + furi_string_reset(model->text.text); + + furi_string_reset(model->left_text); + furi_string_reset(model->center_text); + furi_string_reset(model->right_text); }, true); dialog_ex->context = NULL; From b040db07f46ad148eac8f9a5eaf30215b16c6e12 Mon Sep 17 00:00:00 2001 From: DerSkythe <31771569+derskythe@users.noreply.github.com> Date: Thu, 5 Sep 2024 17:50:33 +0400 Subject: [PATCH 2/8] Gui: Add up and down button drawing functions to GUI elements (#3804) * feat: Add up and down button drawing functions to GUI elements Two button drawing functions, elements_button_up and elements_button_down, have been added to the GUI elements. These functions allow a button to be drawn at the top left and top right corner of the canvas respectively, with an accompanying string and icon. The underlying layout and design of these buttons is defined within these functions. * feat: Add null checks for Canvas parameter in button functions Added furi_check to ensure the Canvas parameter is not null in elements_button_up and elements_button_down functions. This prevents potential crashes due to dereferencing a null pointer. Co-authored-by: hedger Co-authored-by: Aleksandr Kutuzov --- applications/services/gui/elements.c | 64 ++++++++++++++++++++++++++++ applications/services/gui/elements.h | 22 ++++++++++ targets/f18/api_symbols.csv | 16 ++++--- targets/f7/api_symbols.csv | 4 +- 4 files changed, 98 insertions(+), 8 deletions(-) diff --git a/applications/services/gui/elements.c b/applications/services/gui/elements.c index 5b38c5c79..3e12c09cc 100644 --- a/applications/services/gui/elements.c +++ b/applications/services/gui/elements.c @@ -185,6 +185,70 @@ void elements_button_right(Canvas* canvas, const char* str) { canvas_invert_color(canvas); } +void elements_button_up(Canvas* canvas, const char* str) { + furi_check(canvas); + + const Icon* icon = &I_ButtonUp_7x4; + + const size_t button_height = 12; + const size_t vertical_offset = 3; + const size_t horizontal_offset = 3; + const size_t string_width = canvas_string_width(canvas, str); + const int32_t icon_h_offset = 3; + const int32_t icon_width_with_offset = icon_get_width(icon) + icon_h_offset; + const int32_t icon_v_offset = icon_get_height(icon) + (int32_t)vertical_offset; + const size_t button_width = string_width + horizontal_offset * 2 + icon_width_with_offset; + + const int32_t x = 0; + const int32_t y = 0 + button_height; + + int32_t line_x = x + button_width; + int32_t line_y = y - button_height; + + canvas_draw_box(canvas, x, line_y, button_width, button_height); + canvas_draw_line(canvas, line_x + 0, line_y, line_x + 0, y - 1); + canvas_draw_line(canvas, line_x + 1, line_y, line_x + 1, y - 2); + canvas_draw_line(canvas, line_x + 2, line_y, line_x + 2, y - 3); + + canvas_invert_color(canvas); + canvas_draw_icon(canvas, x + horizontal_offset, y - icon_v_offset, icon); + canvas_draw_str( + canvas, x + horizontal_offset + icon_width_with_offset, y - vertical_offset, str); + canvas_invert_color(canvas); +} + +void elements_button_down(Canvas* canvas, const char* str) { + furi_check(canvas); + + const Icon* icon = &I_ButtonDown_7x4; + + const size_t button_height = 12; + const size_t vertical_offset = 3; + const size_t horizontal_offset = 3; + const size_t string_width = canvas_string_width(canvas, str); + const int32_t icon_h_offset = 3; + const int32_t icon_width_with_offset = icon_get_width(icon) + icon_h_offset; + const int32_t icon_v_offset = icon_get_height(icon) + vertical_offset + 1; + const size_t button_width = string_width + horizontal_offset * 2 + icon_width_with_offset; + + const int32_t x = canvas_width(canvas); + const int32_t y = button_height; + + int32_t line_x = x - button_width; + int32_t line_y = y - button_height; + + canvas_draw_box(canvas, line_x, line_y, button_width, button_height); + canvas_draw_line(canvas, line_x - 1, line_y, line_x - 1, y - 1); + canvas_draw_line(canvas, line_x - 2, line_y, line_x - 2, y - 2); + canvas_draw_line(canvas, line_x - 3, line_y, line_x - 3, y - 3); + + canvas_invert_color(canvas); + canvas_draw_str(canvas, x - button_width + horizontal_offset, y - vertical_offset, str); + canvas_draw_icon( + canvas, x - horizontal_offset - icon_get_width(icon), y - icon_v_offset, icon); + canvas_invert_color(canvas); +} + void elements_button_center(Canvas* canvas, const char* str) { furi_check(canvas); diff --git a/applications/services/gui/elements.h b/applications/services/gui/elements.h index 88a004815..0ec0f86cb 100644 --- a/applications/services/gui/elements.h +++ b/applications/services/gui/elements.h @@ -96,6 +96,28 @@ void elements_button_left(Canvas* canvas, const char* str); */ void elements_button_right(Canvas* canvas, const char* str); +/** + * @brief This function draws a button in the top left corner of the canvas with icon and string. + * + * The design and layout of the button is defined within this function. + * + * @param[in] canvas This is a pointer to the @c Canvas structure where the button will be drawn. + * @param[in] str This is a pointer to the character string that will be drawn within the button. + * + */ +void elements_button_up(Canvas* canvas, const char* str); + +/** + * @brief This function draws a button in the top right corner of the canvas with icon and string. + * + * The design and layout of the button is defined within this function. + * + * @param[in] canvas This is a pointer to the @c Canvas structure where the button will be drawn. + * @param[in] str This is a pointer to the character string that will be drawn within the button. + * + */ +void elements_button_down(Canvas* canvas, const char* str); + /** Draw button in center * * @param canvas Canvas instance diff --git a/targets/f18/api_symbols.csv b/targets/f18/api_symbols.csv index 57cbd1d62..2c1a56565 100644 --- a/targets/f18/api_symbols.csv +++ b/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,72.1,, +Version,+,72.2,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/bt/bt_service/bt_keys_storage.h,, Header,+,applications/services/cli/cli.h,, @@ -13,13 +13,13 @@ Header,+,applications/services/gui/icon_i.h,, Header,+,applications/services/gui/modules/button_menu.h,, Header,+,applications/services/gui/modules/button_panel.h,, Header,+,applications/services/gui/modules/byte_input.h,, -Header,+,applications/services/gui/modules/number_input.h,, Header,+,applications/services/gui/modules/dialog_ex.h,, Header,+,applications/services/gui/modules/empty_screen.h,, Header,+,applications/services/gui/modules/file_browser.h,, Header,+,applications/services/gui/modules/file_browser_worker.h,, Header,+,applications/services/gui/modules/loading.h,, Header,+,applications/services/gui/modules/menu.h,, +Header,+,applications/services/gui/modules/number_input.h,, Header,+,applications/services/gui/modules/popup.h,, Header,+,applications/services/gui/modules/submenu.h,, Header,+,applications/services/gui/modules/text_box.h,, @@ -723,11 +723,6 @@ Function,+,byte_input_free,void,ByteInput* Function,+,byte_input_get_view,View*,ByteInput* Function,+,byte_input_set_header_text,void,"ByteInput*, const char*" Function,+,byte_input_set_result_callback,void,"ByteInput*, ByteInputCallback, ByteChangedCallback, void*, uint8_t*, uint8_t" -Function,+,number_input_alloc,NumberInput*, -Function,+,number_input_free,void,NumberInput* -Function,+,number_input_get_view,View*,NumberInput* -Function,+,number_input_set_header_text,void,"NumberInput*, const char*" -Function,+,number_input_set_result_callback,void,"NumberInput*, NumberInputCallback, void*, int32_t, int32_t, int32_t" Function,-,bzero,void,"void*, size_t" Function,+,calloc,void*,"size_t, size_t" Function,+,canvas_clear,void,Canvas* @@ -883,8 +878,10 @@ Function,+,elements_bold_rounded_frame,void,"Canvas*, int32_t, int32_t, size_t, Function,+,elements_bubble,void,"Canvas*, int32_t, int32_t, size_t, size_t" Function,+,elements_bubble_str,void,"Canvas*, int32_t, int32_t, const char*, Align, Align" Function,+,elements_button_center,void,"Canvas*, const char*" +Function,+,elements_button_down,void,"Canvas*, const char*" Function,+,elements_button_left,void,"Canvas*, const char*" Function,+,elements_button_right,void,"Canvas*, const char*" +Function,+,elements_button_up,void,"Canvas*, const char*" Function,+,elements_frame,void,"Canvas*, int32_t, int32_t, size_t, size_t" Function,+,elements_multiline_text,void,"Canvas*, int32_t, int32_t, const char*" Function,+,elements_multiline_text_aligned,void,"Canvas*, int32_t, int32_t, Align, Align, const char*" @@ -2197,6 +2194,11 @@ Function,+,notification_internal_message_block,void,"NotificationApp*, const Not Function,+,notification_message,void,"NotificationApp*, const NotificationSequence*" Function,+,notification_message_block,void,"NotificationApp*, const NotificationSequence*" Function,-,nrand48,long,unsigned short[3] +Function,+,number_input_alloc,NumberInput*, +Function,+,number_input_free,void,NumberInput* +Function,+,number_input_get_view,View*,NumberInput* +Function,+,number_input_set_header_text,void,"NumberInput*, const char*" +Function,+,number_input_set_result_callback,void,"NumberInput*, NumberInputCallback, void*, int32_t, int32_t, int32_t" Function,-,on_exit,int,"void (*)(int, void*), void*" Function,+,onewire_host_alloc,OneWireHost*,const GpioPin* Function,+,onewire_host_free,void,OneWireHost* diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index e44b3356c..e5292d936 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,72.1,, +Version,+,72.2,, 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,, @@ -965,8 +965,10 @@ Function,+,elements_bold_rounded_frame,void,"Canvas*, int32_t, int32_t, size_t, Function,+,elements_bubble,void,"Canvas*, int32_t, int32_t, size_t, size_t" Function,+,elements_bubble_str,void,"Canvas*, int32_t, int32_t, const char*, Align, Align" Function,+,elements_button_center,void,"Canvas*, const char*" +Function,+,elements_button_down,void,"Canvas*, const char*" Function,+,elements_button_left,void,"Canvas*, const char*" Function,+,elements_button_right,void,"Canvas*, const char*" +Function,+,elements_button_up,void,"Canvas*, const char*" Function,+,elements_frame,void,"Canvas*, int32_t, int32_t, size_t, size_t" Function,+,elements_multiline_text,void,"Canvas*, int32_t, int32_t, const char*" Function,+,elements_multiline_text_aligned,void,"Canvas*, int32_t, int32_t, Align, Align, const char*" From fa2d611652c2658c3f5499493e9020f07b549062 Mon Sep 17 00:00:00 2001 From: Georgii Surkov <37121527+gsurkov@users.noreply.github.com> Date: Thu, 5 Sep 2024 15:40:14 +0100 Subject: [PATCH 3/8] [FL-3889] 5V on GPIO control for ext. modules (#3830) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Make file extensions case-insensitive * Bump protobuf version * Add support for 5V control via RPC * Add support for 5V control via Expansion protocol * Update running instructions * Update expansion module documentation * Prettify condition * Test RPC OTG control as well * Assets: bump protobuf version * Disable PVS license expiration check, fix PVS warnings Co-authored-by: あく --- .../debug/expansion_test/expansion_test.c | 59 +++++++++++++++++-- .../services/expansion/expansion_protocol.h | 24 +++++++- .../services/expansion/expansion_worker.c | 26 ++++++-- .../services/gui/modules/byte_input.c | 6 +- applications/services/rpc/rpc_gpio.c | 45 ++++++++++++++ assets/protobuf | 2 +- documentation/ExpansionModules.md | 18 ++++-- scripts/fbt_tools/pvsstudio.py | 1 + 8 files changed, 160 insertions(+), 21 deletions(-) diff --git a/applications/debug/expansion_test/expansion_test.c b/applications/debug/expansion_test/expansion_test.c index e3bcf4e9c..665874f47 100644 --- a/applications/debug/expansion_test/expansion_test.c +++ b/applications/debug/expansion_test/expansion_test.c @@ -6,18 +6,27 @@ * 13 -> 16 (USART TX to LPUART RX) * 14 -> 15 (USART RX to LPUART TX) * + * Optional: Connect an LED with an appropriate series resistor + * between pins 1 and 8. It will always be on if the device is + * connected to USB power, so unplug it before running the app. + * * What this application does: * * - Enables module support and emulates the module on a single device * (hence the above connection), * - Connects to the expansion module service, sets baud rate, + * - Enables OTG (5V) on GPIO via plain expansion protocol, + * - Waits 5 cycles of idle loop (1 second), * - Starts the RPC session, + * - Disables OTG (5V) on GPIO via RPC messages, + * - Waits 5 cycles of idle loop (1 second), * - Creates a directory at `/ext/ExpansionTest` and writes a file * named `test.txt` under it, * - Plays an audiovisual alert (sound and blinking display), - * - Waits 10 cycles of idle loop, + * - Enables OTG (5V) on GPIO via RPC messages, + * - Waits 5 cycles of idle loop (1 second), * - Stops the RPC session, - * - Waits another 10 cycles of idle loop, + * - Disables OTG (5V) on GPIO via plain expansion protocol, * - Exits (plays a sound if any of the above steps failed). */ #include @@ -302,6 +311,22 @@ static bool expansion_test_app_handshake(ExpansionTestApp* instance) { return success; } +static bool expansion_test_app_enable_otg(ExpansionTestApp* instance, bool enable) { + bool success = false; + + do { + const ExpansionFrameControlCommand command = enable ? + ExpansionFrameControlCommandEnableOtg : + ExpansionFrameControlCommandDisableOtg; + if(!expansion_test_app_send_control_request(instance, command)) break; + if(!expansion_test_app_receive_frame(instance, &instance->frame)) break; + if(!expansion_test_app_is_success_response(&instance->frame)) break; + success = true; + } while(false); + + return success; +} + static bool expansion_test_app_start_rpc(ExpansionTestApp* instance) { bool success = false; @@ -396,6 +421,27 @@ static bool expansion_test_app_rpc_alert(ExpansionTestApp* instance) { return success; } +static bool expansion_test_app_rpc_enable_otg(ExpansionTestApp* instance, bool enable) { + bool success = false; + + instance->msg.command_id++; + instance->msg.command_status = PB_CommandStatus_OK; + instance->msg.which_content = PB_Main_gpio_set_otg_mode_tag; + instance->msg.content.gpio_set_otg_mode.mode = enable ? PB_Gpio_GpioOtgMode_ON : + PB_Gpio_GpioOtgMode_OFF; + instance->msg.has_next = false; + + do { + if(!expansion_test_app_send_rpc_request(instance, &instance->msg)) break; + if(!expansion_test_app_receive_rpc_request(instance, &instance->msg)) break; + if(instance->msg.which_content != PB_Main_empty_tag) break; + if(instance->msg.command_status != PB_CommandStatus_OK) break; + success = true; + } while(false); + + return success; +} + static bool expansion_test_app_idle(ExpansionTestApp* instance, uint32_t num_cycles) { uint32_t num_cycles_done; for(num_cycles_done = 0; num_cycles_done < num_cycles; ++num_cycles_done) { @@ -434,13 +480,18 @@ int32_t expansion_test_app(void* p) { if(!expansion_test_app_send_presence(instance)) break; if(!expansion_test_app_wait_ready(instance)) break; if(!expansion_test_app_handshake(instance)) break; + if(!expansion_test_app_enable_otg(instance, true)) break; + if(!expansion_test_app_idle(instance, 5)) break; if(!expansion_test_app_start_rpc(instance)) break; + if(!expansion_test_app_rpc_enable_otg(instance, false)) break; + if(!expansion_test_app_idle(instance, 5)) break; if(!expansion_test_app_rpc_mkdir(instance)) break; if(!expansion_test_app_rpc_write(instance)) break; if(!expansion_test_app_rpc_alert(instance)) break; - if(!expansion_test_app_idle(instance, 10)) break; + if(!expansion_test_app_rpc_enable_otg(instance, true)) break; + if(!expansion_test_app_idle(instance, 5)) break; if(!expansion_test_app_stop_rpc(instance)) break; - if(!expansion_test_app_idle(instance, 10)) break; + if(!expansion_test_app_enable_otg(instance, false)) break; success = true; } while(false); diff --git a/applications/services/expansion/expansion_protocol.h b/applications/services/expansion/expansion_protocol.h index 6ed818f82..a8d682330 100644 --- a/applications/services/expansion/expansion_protocol.h +++ b/applications/services/expansion/expansion_protocol.h @@ -64,8 +64,28 @@ typedef enum { * @brief Enumeration of suported control commands. */ typedef enum { - ExpansionFrameControlCommandStartRpc = 0x00, /**< Start an RPC session. */ - ExpansionFrameControlCommandStopRpc = 0x01, /**< Stop an open RPC session. */ + /** @brief Start an RPC session. + * + * Must only be used while the RPC session is NOT active. + */ + ExpansionFrameControlCommandStartRpc = 0x00, + /** @brief Stop an open RPC session. + * + * Must only be used while the RPC session IS active. + */ + ExpansionFrameControlCommandStopRpc = 0x01, + /** @brief Enable OTG (5V) on external GPIO. + * + * Must only be used while the RPC session is NOT active, + * otherwise OTG is to be controlled via RPC messages. + */ + ExpansionFrameControlCommandEnableOtg = 0x02, + /** @brief Disable OTG (5V) on external GPIO. + * + * Must only be used while the RPC session is NOT active, + * otherwise OTG is to be controlled via RPC messages. + */ + ExpansionFrameControlCommandDisableOtg = 0x03, } ExpansionFrameControlCommand; #pragma pack(push, 1) diff --git a/applications/services/expansion/expansion_worker.c b/applications/services/expansion/expansion_worker.c index 449d02cff..c05b9cc85 100644 --- a/applications/services/expansion/expansion_worker.c +++ b/applications/services/expansion/expansion_worker.c @@ -245,9 +245,18 @@ static bool expansion_worker_handle_state_connected( do { if(rx_frame->header.type == ExpansionFrameTypeControl) { - if(rx_frame->content.control.command != ExpansionFrameControlCommandStartRpc) break; - instance->state = ExpansionWorkerStateRpcActive; - if(!expansion_worker_rpc_session_open(instance)) break; + const uint8_t command = rx_frame->content.control.command; + if(command == ExpansionFrameControlCommandStartRpc) { + if(!expansion_worker_rpc_session_open(instance)) break; + instance->state = ExpansionWorkerStateRpcActive; + } else if(command == ExpansionFrameControlCommandEnableOtg) { + furi_hal_power_enable_otg(); + } else if(command == ExpansionFrameControlCommandDisableOtg) { + furi_hal_power_disable_otg(); + } else { + break; + } + if(!expansion_worker_send_status_response(instance, ExpansionFrameErrorNone)) break; } else if(rx_frame->header.type == ExpansionFrameTypeHeartbeat) { @@ -279,9 +288,14 @@ static bool expansion_worker_handle_state_rpc_active( if(size_consumed != rx_frame->content.data.size) break; } else if(rx_frame->header.type == ExpansionFrameTypeControl) { - if(rx_frame->content.control.command != ExpansionFrameControlCommandStopRpc) break; - instance->state = ExpansionWorkerStateConnected; - expansion_worker_rpc_session_close(instance); + const uint8_t command = rx_frame->content.control.command; + if(command == ExpansionFrameControlCommandStopRpc) { + instance->state = ExpansionWorkerStateConnected; + expansion_worker_rpc_session_close(instance); + } else { + break; + } + if(!expansion_worker_send_status_response(instance, ExpansionFrameErrorNone)) break; } else if(rx_frame->header.type == ExpansionFrameTypeStatus) { diff --git a/applications/services/gui/modules/byte_input.c b/applications/services/gui/modules/byte_input.c index 6e85401da..be94ed9ab 100644 --- a/applications/services/gui/modules/byte_input.c +++ b/applications/services/gui/modules/byte_input.c @@ -33,7 +33,7 @@ typedef struct { static const uint8_t keyboard_origin_x = 7; static const uint8_t keyboard_origin_y = 31; -static const uint8_t keyboard_row_count = 2; +static const int8_t keyboard_row_count = 2; static const uint8_t enter_symbol = '\r'; static const uint8_t backspace_symbol = '\b'; static const uint8_t max_drawable_bytes = 8; @@ -649,11 +649,11 @@ static void byte_input_view_draw_callback(Canvas* canvas, void* _model) { } canvas_set_font(canvas, FontKeyboard); // Draw keyboard - for(uint8_t row = 0; row < keyboard_row_count; row++) { + for(int8_t row = 0; row < keyboard_row_count; row++) { const uint8_t column_count = byte_input_get_row_size(row); const ByteInputKey* keys = byte_input_get_row(row); - for(size_t column = 0; column < column_count; column++) { + for(uint8_t column = 0; column < column_count; column++) { if(keys[column].value == enter_symbol) { canvas_set_color(canvas, ColorBlack); if(model->selected_row == row && model->selected_column == column) { diff --git a/applications/services/rpc/rpc_gpio.c b/applications/services/rpc/rpc_gpio.c index 09e738505..40fc898a0 100644 --- a/applications/services/rpc/rpc_gpio.c +++ b/applications/services/rpc/rpc_gpio.c @@ -2,6 +2,7 @@ #include "rpc_i.h" #include "gpio.pb.h" #include +#include #include static const GpioPin* rpc_pin_to_hal_pin(PB_Gpio_GpioPin rpc_pin) { @@ -188,6 +189,44 @@ void rpc_system_gpio_set_input_pull(const PB_Main* request, void* context) { free(response); } +void rpc_system_gpio_get_otg_mode(const PB_Main* request, void* context) { + furi_assert(request); + furi_assert(context); + furi_assert(request->which_content == PB_Main_gpio_get_otg_mode_tag); + + RpcSession* session = context; + + const bool otg_enabled = furi_hal_power_is_otg_enabled(); + + PB_Main* response = malloc(sizeof(PB_Main)); + response->command_id = request->command_id; + response->which_content = PB_Main_gpio_get_otg_mode_response_tag; + response->content.gpio_get_otg_mode_response.mode = otg_enabled ? PB_Gpio_GpioOtgMode_ON : + PB_Gpio_GpioOtgMode_OFF; + + rpc_send_and_release(session, response); + + free(response); +} + +void rpc_system_gpio_set_otg_mode(const PB_Main* request, void* context) { + furi_assert(request); + furi_assert(context); + furi_assert(request->which_content == PB_Main_gpio_set_otg_mode_tag); + + RpcSession* session = context; + + const PB_Gpio_GpioOtgMode mode = request->content.gpio_set_otg_mode.mode; + + if(mode == PB_Gpio_GpioOtgMode_OFF) { + furi_hal_power_disable_otg(); + } else { + furi_hal_power_enable_otg(); + } + + rpc_send_and_release_empty(session, request->command_id, PB_CommandStatus_OK); +} + void* rpc_system_gpio_alloc(RpcSession* session) { furi_assert(session); @@ -212,5 +251,11 @@ void* rpc_system_gpio_alloc(RpcSession* session) { rpc_handler.message_handler = rpc_system_gpio_set_input_pull; rpc_add_handler(session, PB_Main_gpio_set_input_pull_tag, &rpc_handler); + rpc_handler.message_handler = rpc_system_gpio_get_otg_mode; + rpc_add_handler(session, PB_Main_gpio_get_otg_mode_tag, &rpc_handler); + + rpc_handler.message_handler = rpc_system_gpio_set_otg_mode; + rpc_add_handler(session, PB_Main_gpio_set_otg_mode_tag, &rpc_handler); + return NULL; } diff --git a/assets/protobuf b/assets/protobuf index 816de200a..6c7c0d55e 160000 --- a/assets/protobuf +++ b/assets/protobuf @@ -1 +1 @@ -Subproject commit 816de200a4a43efc25c5b92d6a57fc982d7e988a +Subproject commit 6c7c0d55e82cb89223cf4890a540af4cff837fa7 diff --git a/documentation/ExpansionModules.md b/documentation/ExpansionModules.md index 470564e57..fd9703adc 100644 --- a/documentation/ExpansionModules.md +++ b/documentation/ExpansionModules.md @@ -73,7 +73,7 @@ If the requested baud rate is supported by the host, it SHALL respond with a STA ### Control frame -CONTROL frames are used to control various aspects of the communication. As of now, the sole purpose of CONTROL frames is to start and stop the RPC session. +CONTROL frames are used to control various aspects of the communication and enable/disable various device features. | Header (1 byte) | Contents (1 byte) | Checksum (1 byte) | |-----------------|-------------------|-------------------| @@ -81,10 +81,18 @@ CONTROL frames are used to control various aspects of the communication. As of n The `Command` field SHALL have one of the followind values: -| Command | Meaning | -|---------|-------------------| -| 0x00 | Start RPC session | -| 0x01 | Stop RPC session | +| Command | Meaning | Note | +|---------|--------------------------|:----:| +| 0x00 | Start RPC session | 1 | +| 0x01 | Stop RPC session | 2 | +| 0x02 | Enable OTG (5V) on GPIO | 3 | +| 0x03 | Disable OTG (5V) on GPIO | 3 | + +Notes: + +1. Must only be used while the RPC session NOT active. +2. Must only be used while the RPC session IS active. +3. See 1, otherwise OTG is to be controlled via RPC messages. ### Data frame diff --git a/scripts/fbt_tools/pvsstudio.py b/scripts/fbt_tools/pvsstudio.py index 290531321..1a55278dc 100644 --- a/scripts/fbt_tools/pvsstudio.py +++ b/scripts/fbt_tools/pvsstudio.py @@ -47,6 +47,7 @@ def generate(env): PVSOPTIONS=[ "@.pvsoptions", "-j${PVSNCORES}", + "--disableLicenseExpirationCheck", # "--incremental", # kinda broken on PVS side ], PVSCONVOPTIONS=[ From 4a58930247ae1eb27e41b8016f1730eed4d56ca9 Mon Sep 17 00:00:00 2001 From: Filipe Paz Rodrigues Date: Thu, 5 Sep 2024 08:04:32 -0700 Subject: [PATCH 4/8] CCID: App changes (#3837) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Keep ccid_callback and buffer as private to the iso7816_handler - set usb ccid callback from iso7816_handler (to ensure the right structure is being passed) - make iso7816 related code independent from furi related code (goal is to make it independently testable) - rename vars Co-authored-by: あく --- applications/debug/ccid_test/ccid_test_app.c | 7 ++- .../debug/ccid_test/iso7816/iso7816_handler.c | 46 +++++++++++++++---- .../debug/ccid_test/iso7816/iso7816_handler.h | 8 ++-- .../debug/ccid_test/iso7816/iso7816_t0_apdu.c | 37 ++++++++------- .../debug/ccid_test/iso7816/iso7816_t0_apdu.h | 3 +- 5 files changed, 64 insertions(+), 37 deletions(-) diff --git a/applications/debug/ccid_test/ccid_test_app.c b/applications/debug/ccid_test/ccid_test_app.c index abb8ad3dd..4158c1a60 100644 --- a/applications/debug/ccid_test/ccid_test_app.c +++ b/applications/debug/ccid_test/ccid_test_app.c @@ -105,7 +105,7 @@ void ccid_test_app_free(CcidTestApp* app) { furi_record_close(RECORD_GUI); app->gui = NULL; - free(app->iso7816_handler); + iso7816_handler_free(app->iso7816_handler); // Free rest free(app); @@ -121,8 +121,7 @@ int32_t ccid_test_app(void* p) { furi_hal_usb_unlock(); furi_check(furi_hal_usb_set_config(&usb_ccid, &app->ccid_cfg) == true); - furi_hal_usb_ccid_set_callbacks( - (CcidCallbacks*)&app->iso7816_handler->ccid_callbacks, app->iso7816_handler); + iso7816_handler_set_usb_ccid_callbacks(); furi_hal_usb_ccid_insert_smartcard(); //handle button events @@ -142,7 +141,7 @@ int32_t ccid_test_app(void* p) { } //tear down USB - furi_hal_usb_ccid_set_callbacks(NULL, NULL); + iso7816_handler_reset_usb_ccid_callbacks(); furi_hal_usb_set_config(usb_mode_prev, NULL); //teardown view diff --git a/applications/debug/ccid_test/iso7816/iso7816_handler.c b/applications/debug/ccid_test/iso7816/iso7816_handler.c index 97214d1b2..8f0f758b2 100644 --- a/applications/debug/ccid_test/iso7816/iso7816_handler.c +++ b/applications/debug/ccid_test/iso7816/iso7816_handler.c @@ -6,11 +6,17 @@ #include #include +#include "iso7816_handler.h" + #include "iso7816_t0_apdu.h" #include "iso7816_atr.h" -#include "iso7816_handler.h" #include "iso7816_response.h" +static Iso7816Handler* iso7816_handler; +static CcidCallbacks* ccid_callbacks; +static uint8_t* command_apdu_buffer; +static uint8_t* response_apdu_buffer; + void iso7816_icc_power_on_callback(uint8_t* atr_data, uint32_t* atr_data_len, void* context) { furi_check(context); @@ -40,12 +46,11 @@ void iso7816_xfr_datablock_callback( Iso7816Handler* handler = (Iso7816Handler*)context; - ISO7816_Response_APDU* response_apdu = (ISO7816_Response_APDU*)&handler->response_apdu_buffer; - - ISO7816_Command_APDU* command_apdu = (ISO7816_Command_APDU*)&handler->command_apdu_buffer; + ISO7816_Response_APDU* response_apdu = (ISO7816_Response_APDU*)response_apdu_buffer; + ISO7816_Command_APDU* command_apdu = (ISO7816_Command_APDU*)command_apdu_buffer; uint8_t result = iso7816_read_command_apdu( - command_apdu, pc_to_reader_datablock, pc_to_reader_datablock_len); + command_apdu, pc_to_reader_datablock, pc_to_reader_datablock_len, CCID_SHORT_APDU_SIZE); if(result == ISO7816_READ_COMMAND_APDU_OK) { handler->iso7816_process_command(command_apdu, response_apdu); @@ -61,8 +66,31 @@ void iso7816_xfr_datablock_callback( } Iso7816Handler* iso7816_handler_alloc() { - Iso7816Handler* handler = malloc(sizeof(Iso7816Handler)); - handler->ccid_callbacks.icc_power_on_callback = iso7816_icc_power_on_callback; - handler->ccid_callbacks.xfr_datablock_callback = iso7816_xfr_datablock_callback; - return handler; + iso7816_handler = malloc(sizeof(Iso7816Handler)); + + command_apdu_buffer = malloc(sizeof(ISO7816_Command_APDU) + CCID_SHORT_APDU_SIZE); + response_apdu_buffer = malloc(sizeof(ISO7816_Response_APDU) + CCID_SHORT_APDU_SIZE); + + ccid_callbacks = malloc(sizeof(CcidCallbacks)); + ccid_callbacks->icc_power_on_callback = iso7816_icc_power_on_callback; + ccid_callbacks->xfr_datablock_callback = iso7816_xfr_datablock_callback; + + return iso7816_handler; +} + +void iso7816_handler_set_usb_ccid_callbacks() { + furi_hal_usb_ccid_set_callbacks(ccid_callbacks, iso7816_handler); +} + +void iso7816_handler_reset_usb_ccid_callbacks() { + furi_hal_usb_ccid_set_callbacks(NULL, NULL); +} + +void iso7816_handler_free(Iso7816Handler* handler) { + free(ccid_callbacks); + + free(command_apdu_buffer); + free(response_apdu_buffer); + + free(handler); } diff --git a/applications/debug/ccid_test/iso7816/iso7816_handler.h b/applications/debug/ccid_test/iso7816/iso7816_handler.h index d67118ce6..4f9703e46 100644 --- a/applications/debug/ccid_test/iso7816/iso7816_handler.h +++ b/applications/debug/ccid_test/iso7816/iso7816_handler.h @@ -5,14 +5,14 @@ #include "iso7816_t0_apdu.h" typedef struct { - CcidCallbacks ccid_callbacks; void (*iso7816_answer_to_reset)(Iso7816Atr* atr); void (*iso7816_process_command)( const ISO7816_Command_APDU* command, ISO7816_Response_APDU* response); - - uint8_t command_apdu_buffer[sizeof(ISO7816_Command_APDU) + CCID_SHORT_APDU_SIZE]; - uint8_t response_apdu_buffer[sizeof(ISO7816_Response_APDU) + CCID_SHORT_APDU_SIZE]; } Iso7816Handler; Iso7816Handler* iso7816_handler_alloc(); + +void iso7816_handler_free(Iso7816Handler* handler); +void iso7816_handler_set_usb_ccid_callbacks(); +void iso7816_handler_reset_usb_ccid_callbacks(); diff --git a/applications/debug/ccid_test/iso7816/iso7816_t0_apdu.c b/applications/debug/ccid_test/iso7816/iso7816_t0_apdu.c index 216f2582f..023daebe2 100644 --- a/applications/debug/ccid_test/iso7816/iso7816_t0_apdu.c +++ b/applications/debug/ccid_test/iso7816/iso7816_t0_apdu.c @@ -1,49 +1,48 @@ /* Implements rudimentary iso7816-3 support for APDU (T=0) */ #include #include -#include -#include #include "iso7816_t0_apdu.h" -//reads dataBuffer with dataLen size, translate it into a ISO7816_Command_APDU type +//reads pc_to_reader_datablock_len with pc_to_reader_datablock_len size, translate it into a ISO7816_Command_APDU type //extra data will be pointed to commandDataBuffer uint8_t iso7816_read_command_apdu( ISO7816_Command_APDU* command, - const uint8_t* dataBuffer, - uint32_t dataLen) { - command->CLA = dataBuffer[0]; - command->INS = dataBuffer[1]; - command->P1 = dataBuffer[2]; - command->P2 = dataBuffer[3]; + const uint8_t* pc_to_reader_datablock, + uint32_t pc_to_reader_datablock_len, + uint32_t max_apdu_size) { + command->CLA = pc_to_reader_datablock[0]; + command->INS = pc_to_reader_datablock[1]; + command->P1 = pc_to_reader_datablock[2]; + command->P2 = pc_to_reader_datablock[3]; - if(dataLen == 4) { + if(pc_to_reader_datablock_len == 4) { command->Lc = 0; command->Le = 0; command->LePresent = false; return ISO7816_READ_COMMAND_APDU_OK; - } else if(dataLen == 5) { + } else if(pc_to_reader_datablock_len == 5) { //short le command->Lc = 0; - command->Le = dataBuffer[4]; + command->Le = pc_to_reader_datablock[4]; command->LePresent = true; return ISO7816_READ_COMMAND_APDU_OK; - } else if(dataLen > 5 && dataBuffer[4] != 0x00) { + } else if(pc_to_reader_datablock_len > 5 && pc_to_reader_datablock[4] != 0x00) { //short lc - command->Lc = dataBuffer[4]; - if(command->Lc > 0 && command->Lc < CCID_SHORT_APDU_SIZE) { //-V560 - memcpy(command->Data, &dataBuffer[5], command->Lc); + command->Lc = pc_to_reader_datablock[4]; + if(command->Lc > 0 && command->Lc < max_apdu_size) { //-V560 + memcpy(command->Data, &pc_to_reader_datablock[5], command->Lc); //does it have a short le too? - if(dataLen == (uint32_t)(command->Lc + 5)) { + if(pc_to_reader_datablock_len == (uint32_t)(command->Lc + 5)) { command->Le = 0; command->LePresent = false; return ISO7816_READ_COMMAND_APDU_OK; - } else if(dataLen == (uint32_t)(command->Lc + 6)) { - command->Le = dataBuffer[dataLen - 1]; + } else if(pc_to_reader_datablock_len == (uint32_t)(command->Lc + 6)) { + command->Le = pc_to_reader_datablock[pc_to_reader_datablock_len - 1]; command->LePresent = true; return ISO7816_READ_COMMAND_APDU_OK; diff --git a/applications/debug/ccid_test/iso7816/iso7816_t0_apdu.h b/applications/debug/ccid_test/iso7816/iso7816_t0_apdu.h index a21dfbafc..bbd2a1b23 100644 --- a/applications/debug/ccid_test/iso7816/iso7816_t0_apdu.h +++ b/applications/debug/ccid_test/iso7816/iso7816_t0_apdu.h @@ -34,7 +34,8 @@ typedef struct { uint8_t iso7816_read_command_apdu( ISO7816_Command_APDU* command, const uint8_t* pc_to_reader_datablock, - uint32_t pc_to_reader_datablock_len); + uint32_t pc_to_reader_datablock_len, + uint32_t max_apdu_size); void iso7816_write_response_apdu( const ISO7816_Response_APDU* response, uint8_t* reader_to_pc_datablock, From 6a48dd28f552a2767bb99c3725415e922edf9976 Mon Sep 17 00:00:00 2001 From: Skorpionm <85568270+Skorpionm@users.noreply.github.com> Date: Thu, 5 Sep 2024 19:10:12 +0400 Subject: [PATCH 5/8] SubGhz: Fix RPC status for ButtonRelease event (#3838) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- applications/main/subghz/scenes/subghz_scene_rpc.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/applications/main/subghz/scenes/subghz_scene_rpc.c b/applications/main/subghz/scenes/subghz_scene_rpc.c index f8bf066d5..42ec98a39 100644 --- a/applications/main/subghz/scenes/subghz_scene_rpc.c +++ b/applications/main/subghz/scenes/subghz_scene_rpc.c @@ -57,7 +57,8 @@ bool subghz_scene_rpc_on_event(void* context, SceneManagerEvent event) { default: //if(SubGhzTxRxStartTxStateOk) result = true; subghz_blink_start(subghz); - state = SubGhzRpcStateTx; + scene_manager_set_scene_state( + subghz->scene_manager, SubGhzSceneRpc, SubGhzRpcStateTx); break; } } @@ -69,7 +70,8 @@ bool subghz_scene_rpc_on_event(void* context, SceneManagerEvent event) { subghz_blink_stop(subghz); result = true; } - state = SubGhzRpcStateIdle; + scene_manager_set_scene_state( + subghz->scene_manager, SubGhzSceneRpc, SubGhzRpcStateIdle); rpc_system_app_confirm(subghz->rpc_ctx, result); } else if(event.event == SubGhzCustomEventSceneRpcLoad) { bool result = false; From c9791a280aef959c69f23036676a7699f70d4bed Mon Sep 17 00:00:00 2001 From: porta Date: Thu, 5 Sep 2024 20:02:42 +0300 Subject: [PATCH 6/8] [FL-3884] Proper integer parsing (#3839) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: strint_to_uint32 and tests * fix: permit explicit bases and prefixes * feat: strint_to_{int32,uint16,int16} * feat: strint_to_u?int64 * refactor: replace strtol, strtoul, sscanf with strint_to_* * fix: api symbols * docs: document parameter `end` of strint_to_uint_32 * style: apply changes requested by hedger * refactor: fix pvs-studio diagnostic * style: apply changes requested by CookiePLMonster * fix: unused var * fix: pointer type * refactor: convert atoi to strint_to_* * fix: strint_to_uint8 doesn't actually exist ._ . * fix: memory leak * style: address review comments * Toolbox: couple small comments in the code and doxygen comment update. SubGhz, Loader: fix strint usage. * Loader: fix incorrect cast Co-authored-by: あく --- .../rpc_debug_app_scene_input_error_code.c | 7 +- applications/debug/uart_echo/uart_echo.c | 4 +- applications/debug/unit_tests/application.fam | 8 + .../unit_tests/tests/strint/strint_test.c | 142 ++++++++++++++++++ .../main/bad_usb/helpers/ducky_script.c | 3 +- applications/main/infrared/infrared_cli.c | 20 ++- .../main/nfc/plugins/supported_cards/itso.c | 6 +- applications/main/subghz/subghz_cli.c | 74 +++++---- applications/services/cli/cli_commands.c | 7 +- .../services/gui/modules/number_input.c | 13 +- applications/services/loader/loader_cli.c | 13 +- applications/services/storage/storage_cli.c | 13 +- lib/flipper_format/flipper_format_stream.c | 13 +- lib/subghz/subghz_file_encoder_worker.c | 26 ++-- lib/toolbox/SConscript | 1 + lib/toolbox/args.c | 6 +- lib/toolbox/strint.c | 121 +++++++++++++++ lib/toolbox/strint.h | 70 +++++++++ lib/update_util/resources/manifest.c | 8 +- targets/f18/api_symbols.csv | 7 + targets/f7/api_symbols.csv | 7 + 21 files changed, 479 insertions(+), 90 deletions(-) create mode 100644 applications/debug/unit_tests/tests/strint/strint_test.c create mode 100644 lib/toolbox/strint.c create mode 100644 lib/toolbox/strint.h diff --git a/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_input_error_code.c b/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_input_error_code.c index 367ca7a4f..be7774881 100644 --- a/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_input_error_code.c +++ b/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_input_error_code.c @@ -1,5 +1,7 @@ #include "../rpc_debug_app.h" +#include + static bool rpc_debug_app_scene_input_error_code_validator_callback( const char* text, FuriString* error, @@ -44,9 +46,8 @@ bool rpc_debug_app_scene_input_error_code_on_event(void* context, SceneManagerEv if(event.type == SceneManagerEventTypeCustom) { if(event.event == RpcDebugAppCustomEventInputErrorCode) { - char* end; - int error_code = strtol(app->text_store, &end, 10); - if(!*end) { + uint32_t error_code; + if(strint_to_uint32(app->text_store, NULL, &error_code, 10) == StrintParseNoError) { rpc_system_app_set_error_code(app->rpc, error_code); } scene_manager_previous_scene(app->scene_manager); diff --git a/applications/debug/uart_echo/uart_echo.c b/applications/debug/uart_echo/uart_echo.c index be807168a..4298dc33d 100644 --- a/applications/debug/uart_echo/uart_echo.c +++ b/applications/debug/uart_echo/uart_echo.c @@ -6,6 +6,8 @@ #include #include +#include + #include #include @@ -320,7 +322,7 @@ int32_t uart_echo_app(void* p) { uint32_t baudrate = DEFAULT_BAUD_RATE; if(p) { const char* baudrate_str = p; - if(sscanf(baudrate_str, "%lu", &baudrate) != 1) { + if(strint_to_uint32(baudrate_str, NULL, &baudrate, 10) != StrintParseNoError) { FURI_LOG_E(TAG, "Invalid baudrate: %s", baudrate_str); baudrate = DEFAULT_BAUD_RATE; } diff --git a/applications/debug/unit_tests/application.fam b/applications/debug/unit_tests/application.fam index f5f84ead7..c87305847 100644 --- a/applications/debug/unit_tests/application.fam +++ b/applications/debug/unit_tests/application.fam @@ -220,3 +220,11 @@ App( entry_point="get_api", requires=["unit_tests"], ) + +App( + appid="test_strint", + sources=["tests/common/*.c", "tests/strint/*.c"], + apptype=FlipperAppType.PLUGIN, + entry_point="get_api", + requires=["unit_tests"], +) diff --git a/applications/debug/unit_tests/tests/strint/strint_test.c b/applications/debug/unit_tests/tests/strint/strint_test.c new file mode 100644 index 000000000..d8fd9113d --- /dev/null +++ b/applications/debug/unit_tests/tests/strint/strint_test.c @@ -0,0 +1,142 @@ +#include +#include + +#include "../test.h" // IWYU pragma: keep + +#include + +MU_TEST(strint_test_basic) { + uint32_t result = 0; + mu_assert_int_eq(StrintParseNoError, strint_to_uint32("123456", NULL, &result, 10)); + mu_assert_int_eq(123456, result); +} + +MU_TEST(strint_test_junk) { + uint32_t result = 0; + mu_assert_int_eq(StrintParseNoError, strint_to_uint32(" 123456 ", NULL, &result, 10)); + mu_assert_int_eq(123456, result); + mu_assert_int_eq( + StrintParseNoError, strint_to_uint32(" \r\n\r\n 123456 ", NULL, &result, 10)); + mu_assert_int_eq(123456, result); +} + +MU_TEST(strint_test_tail) { + uint32_t result = 0; + char* tail; + mu_assert_int_eq(StrintParseNoError, strint_to_uint32("123456tail", &tail, &result, 10)); + mu_assert_int_eq(123456, result); + mu_assert_string_eq("tail", tail); + mu_assert_int_eq( + StrintParseNoError, strint_to_uint32(" \r\n 123456tail", &tail, &result, 10)); + mu_assert_int_eq(123456, result); + mu_assert_string_eq("tail", tail); +} + +MU_TEST(strint_test_errors) { + uint32_t result = 123; + mu_assert_int_eq(StrintParseAbsentError, strint_to_uint32("", NULL, &result, 10)); + mu_assert_int_eq(123, result); + mu_assert_int_eq(StrintParseAbsentError, strint_to_uint32(" asd\r\n", NULL, &result, 10)); + mu_assert_int_eq(123, result); + mu_assert_int_eq(StrintParseSignError, strint_to_uint32("+++123456", NULL, &result, 10)); + mu_assert_int_eq(123, result); + mu_assert_int_eq(StrintParseSignError, strint_to_uint32("-1", NULL, &result, 10)); + mu_assert_int_eq(123, result); + mu_assert_int_eq( + StrintParseOverflowError, + strint_to_uint32("0xAAAAAAAAAAAAAAAADEADBEEF!!!!!!", NULL, &result, 0)); + mu_assert_int_eq(123, result); + mu_assert_int_eq(StrintParseOverflowError, strint_to_uint32("4294967296", NULL, &result, 0)); + mu_assert_int_eq(123, result); + + int32_t result_i32 = 123; + mu_assert_int_eq( + StrintParseOverflowError, strint_to_int32("-2147483649", NULL, &result_i32, 0)); + mu_assert_int_eq(123, result_i32); +} + +MU_TEST(strint_test_bases) { + uint32_t result = 0; + + mu_assert_int_eq(StrintParseNoError, strint_to_uint32("0x123", NULL, &result, 0)); + mu_assert_int_eq(0x123, result); + mu_assert_int_eq(StrintParseNoError, strint_to_uint32("0X123", NULL, &result, 0)); + mu_assert_int_eq(0x123, result); + mu_assert_int_eq(StrintParseNoError, strint_to_uint32("0xDEADBEEF", NULL, &result, 0)); + mu_assert_int_eq(0xDEADBEEF, result); + mu_assert_int_eq(StrintParseNoError, strint_to_uint32("0xDEADBEEF", NULL, &result, 16)); + mu_assert_int_eq(0xDEADBEEF, result); + mu_assert_int_eq(StrintParseNoError, strint_to_uint32("123", NULL, &result, 16)); + mu_assert_int_eq(0x123, result); + + mu_assert_int_eq(StrintParseNoError, strint_to_uint32("123", NULL, &result, 0)); + mu_assert_int_eq(123, result); + + mu_assert_int_eq(StrintParseNoError, strint_to_uint32("0123", NULL, &result, 0)); + mu_assert_int_eq(0123, result); + mu_assert_int_eq(StrintParseNoError, strint_to_uint32("0123", NULL, &result, 8)); + mu_assert_int_eq(0123, result); + mu_assert_int_eq(StrintParseNoError, strint_to_uint32("123", NULL, &result, 8)); + mu_assert_int_eq(0123, result); + + mu_assert_int_eq(StrintParseNoError, strint_to_uint32("0b101", NULL, &result, 0)); + mu_assert_int_eq(0b101, result); + mu_assert_int_eq(StrintParseNoError, strint_to_uint32("0b101", NULL, &result, 2)); + mu_assert_int_eq(0b101, result); + mu_assert_int_eq(StrintParseNoError, strint_to_uint32("0B101", NULL, &result, 0)); + mu_assert_int_eq(0b101, result); + mu_assert_int_eq(StrintParseNoError, strint_to_uint32("101", NULL, &result, 2)); + mu_assert_int_eq(0b101, result); +} + +MU_TEST_SUITE(strint_test_limits) { + uint64_t result_u64 = 0; + mu_assert_int_eq( + StrintParseNoError, strint_to_uint64("18446744073709551615", NULL, &result_u64, 0)); + // `mu_assert_int_eq' does not support longs :( + mu_assert(UINT64_MAX == result_u64, "result does not equal UINT64_MAX"); + + int64_t result_i64 = 0; + mu_assert_int_eq( + StrintParseNoError, strint_to_int64("9223372036854775807", NULL, &result_i64, 0)); + mu_assert(INT64_MAX == result_i64, "result does not equal INT64_MAX"); + mu_assert_int_eq( + StrintParseNoError, strint_to_int64("-9223372036854775808", NULL, &result_i64, 0)); + mu_assert(INT64_MIN == result_i64, "result does not equal INT64_MIN"); + + uint32_t result_u32 = 0; + mu_assert_int_eq(StrintParseNoError, strint_to_uint32("4294967295", NULL, &result_u32, 0)); + mu_assert_int_eq(UINT32_MAX, result_u32); + + int32_t result_i32 = 0; + mu_assert_int_eq(StrintParseNoError, strint_to_int32("2147483647", NULL, &result_i32, 0)); + mu_assert_int_eq(INT32_MAX, result_i32); + mu_assert_int_eq(StrintParseNoError, strint_to_int32("-2147483648", NULL, &result_i32, 0)); + mu_assert_int_eq(INT32_MIN, result_i32); + + uint16_t result_u16 = 0; + mu_assert_int_eq(StrintParseNoError, strint_to_uint16("65535", NULL, &result_u16, 0)); + mu_assert_int_eq(UINT16_MAX, result_u16); + + int16_t result_i16 = 0; + mu_assert_int_eq(StrintParseNoError, strint_to_int16("32767", NULL, &result_i16, 0)); + mu_assert_int_eq(INT16_MAX, result_i16); + mu_assert_int_eq(StrintParseNoError, strint_to_int16("-32768", NULL, &result_i16, 0)); + mu_assert_int_eq(INT16_MIN, result_i16); +} + +MU_TEST_SUITE(test_strint_suite) { + MU_RUN_TEST(strint_test_basic); + MU_RUN_TEST(strint_test_junk); + MU_RUN_TEST(strint_test_tail); + MU_RUN_TEST(strint_test_errors); + MU_RUN_TEST(strint_test_bases); + MU_RUN_TEST(strint_test_limits); +} + +int run_minunit_test_strint(void) { + MU_RUN_SUITE(test_strint_suite); + return MU_EXIT_CODE; +} + +TEST_API_DEFINE(run_minunit_test_strint) diff --git a/applications/main/bad_usb/helpers/ducky_script.c b/applications/main/bad_usb/helpers/ducky_script.c index 2ee66955c..ccc3caa81 100644 --- a/applications/main/bad_usb/helpers/ducky_script.c +++ b/applications/main/bad_usb/helpers/ducky_script.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include "ducky_script.h" #include "ducky_script_i.h" @@ -64,7 +65,7 @@ uint16_t ducky_get_keycode(BadUsbScript* bad_usb, const char* param, bool accept bool ducky_get_number(const char* param, uint32_t* val) { uint32_t value = 0; - if(sscanf(param, "%lu", &value) == 1) { + if(strint_to_uint32(param, NULL, &value, 10) == StrintParseNoError) { *val = value; return true; } diff --git a/applications/main/infrared/infrared_cli.c b/applications/main/infrared/infrared_cli.c index 169f0c63d..00246a90f 100644 --- a/applications/main/infrared/infrared_cli.c +++ b/applications/main/infrared/infrared_cli.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include "infrared_signal.h" @@ -176,25 +177,28 @@ static bool infrared_cli_parse_raw(const char* str, InfraredSignal* signal) { return false; } - uint32_t* timings = malloc(sizeof(uint32_t) * MAX_TIMINGS_AMOUNT); - uint32_t frequency = atoi(frequency_str); - float duty_cycle = (float)atoi(duty_cycle_str) / 100; + uint32_t frequency; + uint32_t duty_cycle_u32; + if(strint_to_uint32(frequency_str, NULL, &frequency, 10) != StrintParseNoError || + strint_to_uint32(duty_cycle_str, NULL, &duty_cycle_u32, 10) != StrintParseNoError) + return false; + float duty_cycle = duty_cycle_u32 / 100.0f; str += strlen(frequency_str) + strlen(duty_cycle_str) + INFRARED_CLI_BUF_SIZE; + uint32_t* timings = malloc(sizeof(uint32_t) * MAX_TIMINGS_AMOUNT); size_t timings_size = 0; while(1) { while(*str == ' ') { ++str; } - char timing_str[INFRARED_CLI_BUF_SIZE]; - if(sscanf(str, "%9s", timing_str) != 1) { + uint32_t timing; + char* next_token; + if(strint_to_uint32(str, &next_token, &timing, 10) != StrintParseNoError) { break; } - - str += strlen(timing_str); - uint32_t timing = atoi(timing_str); + str = next_token; if((timing <= 0) || (timings_size >= MAX_TIMINGS_AMOUNT)) { break; diff --git a/applications/main/nfc/plugins/supported_cards/itso.c b/applications/main/nfc/plugins/supported_cards/itso.c index 1b61c26e7..a9be0a1f9 100644 --- a/applications/main/nfc/plugins/supported_cards/itso.c +++ b/applications/main/nfc/plugins/supported_cards/itso.c @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -72,7 +73,10 @@ static bool itso_parse(const NfcDevice* device, FuriString* parsed_data) { dateBuff[17] = '\0'; // DateStamp is defined in BS EN 1545 - Days passed since 01/01/1997 - uint32_t dateStamp = (int)strtol(datep, NULL, 16); + uint32_t dateStamp; + if(strint_to_uint32(datep, NULL, &dateStamp, 16) != StrintParseNoError) { + return false; + } uint32_t unixTimestamp = dateStamp * 24 * 60 * 60 + 852076800U; furi_string_set(parsed_data, "\e#ITSO Card\n"); diff --git a/applications/main/subghz/subghz_cli.c b/applications/main/subghz/subghz_cli.c index 4f5c4cb62..6375f2eee 100644 --- a/applications/main/subghz/subghz_cli.c +++ b/applications/main/subghz/subghz_cli.c @@ -3,18 +3,20 @@ #include #include -#include -#include +#include +#include #include #include #include #include -#include #include #include #include +#include +#include + #include "helpers/subghz_chat.h" #include @@ -71,9 +73,8 @@ void subghz_cli_command_tx_carrier(Cli* cli, FuriString* args, void* context) { uint32_t frequency = 433920000; if(furi_string_size(args)) { - int ret = sscanf(furi_string_get_cstr(args), "%lu", &frequency); - if(ret != 1) { - printf("sscanf returned %d, frequency: %lu\r\n", ret, frequency); + if(strint_to_uint32(furi_string_get_cstr(args), NULL, &frequency, 10) != + StrintParseNoError) { cli_print_usage("subghz tx_carrier", "", furi_string_get_cstr(args)); return; } @@ -115,9 +116,8 @@ void subghz_cli_command_rx_carrier(Cli* cli, FuriString* args, void* context) { uint32_t frequency = 433920000; if(furi_string_size(args)) { - int ret = sscanf(furi_string_get_cstr(args), "%lu", &frequency); - if(ret != 1) { - printf("sscanf returned %d, frequency: %lu\r\n", ret, frequency); + if(strint_to_uint32(furi_string_get_cstr(args), NULL, &frequency, 10) != + StrintParseNoError) { cli_print_usage("subghz rx_carrier", "", furi_string_get_cstr(args)); return; } @@ -181,23 +181,14 @@ void subghz_cli_command_tx(Cli* cli, FuriString* args, void* context) { uint32_t device_ind = 0; // 0 - CC1101_INT, 1 - CC1101_EXT if(furi_string_size(args)) { - int ret = sscanf( - furi_string_get_cstr(args), - "%lx %lu %lu %lu %lu", - &key, - &frequency, - &te, - &repeat, - &device_ind); - if(ret != 5) { - printf( - "sscanf returned %d, key: %lx, frequency: %lu, te: %lu, repeat: %lu, device: %lu\r\n ", - ret, - key, - frequency, - te, - repeat, - device_ind); + char* args_cstr = (char*)furi_string_get_cstr(args); + StrintParseError parse_err = StrintParseNoError; + parse_err |= strint_to_uint32(args_cstr, &args_cstr, &key, 16); + parse_err |= strint_to_uint32(args_cstr, &args_cstr, &frequency, 10); + parse_err |= strint_to_uint32(args_cstr, &args_cstr, &te, 10); + parse_err |= strint_to_uint32(args_cstr, &args_cstr, &repeat, 10); + parse_err |= strint_to_uint32(args_cstr, &args_cstr, &device_ind, 10); + if(parse_err) { cli_print_usage( "subghz tx", "<3 Byte Key: in hex> ", @@ -314,10 +305,11 @@ void subghz_cli_command_rx(Cli* cli, FuriString* args, void* context) { uint32_t device_ind = 0; // 0 - CC1101_INT, 1 - CC1101_EXT if(furi_string_size(args)) { - int ret = sscanf(furi_string_get_cstr(args), "%lu %lu", &frequency, &device_ind); - if(ret != 2) { - printf( - "sscanf returned %d, frequency: %lu device: %lu\r\n", ret, frequency, device_ind); + char* args_cstr = (char*)furi_string_get_cstr(args); + StrintParseError parse_err = StrintParseNoError; + parse_err |= strint_to_uint32(args_cstr, &args_cstr, &frequency, 10); + parse_err |= strint_to_uint32(args_cstr, &args_cstr, &device_ind, 10); + if(parse_err) { cli_print_usage( "subghz rx", " ", @@ -401,9 +393,8 @@ void subghz_cli_command_rx_raw(Cli* cli, FuriString* args, void* context) { uint32_t frequency = 433920000; if(furi_string_size(args)) { - int ret = sscanf(furi_string_get_cstr(args), "%lu", &frequency); - if(ret != 1) { - printf("sscanf returned %d, frequency: %lu\r\n", ret, frequency); + if(strint_to_uint32(furi_string_get_cstr(args), NULL, &frequency, 10) != + StrintParseNoError) { cli_print_usage("subghz rx", "", furi_string_get_cstr(args)); return; } @@ -622,9 +613,11 @@ void subghz_cli_command_tx_from_file(Cli* cli, FuriString* args, void* context) } if(furi_string_size(args)) { - int ret = sscanf(furi_string_get_cstr(args), "%lu %lu", &repeat, &device_ind); - if(ret != 2) { - printf("sscanf returned %d, repeat: %lu device: %lu\r\n", ret, repeat, device_ind); + char* args_cstr = (char*)furi_string_get_cstr(args); + StrintParseError parse_err = StrintParseNoError; + parse_err |= strint_to_uint32(args_cstr, &args_cstr, &frequency, 10); + parse_err |= strint_to_uint32(args_cstr, &args_cstr, &device_ind, 10); + if(parse_err) { cli_print_usage( "subghz tx_from_file:", " ", @@ -936,10 +929,11 @@ static void subghz_cli_command_chat(Cli* cli, FuriString* args) { uint32_t device_ind = 0; // 0 - CC1101_INT, 1 - CC1101_EXT if(furi_string_size(args)) { - int ret = sscanf(furi_string_get_cstr(args), "%lu %lu", &frequency, &device_ind); - if(ret != 2) { - printf("sscanf returned %d, Frequency: %lu\r\n", ret, frequency); - printf("sscanf returned %d, Device: %lu\r\n", ret, device_ind); + char* args_cstr = (char*)furi_string_get_cstr(args); + StrintParseError parse_err = StrintParseNoError; + parse_err |= strint_to_uint32(args_cstr, &args_cstr, &frequency, 10); + parse_err |= strint_to_uint32(args_cstr, &args_cstr, &device_ind, 10); + if(parse_err) { cli_print_usage( "subghz chat", " ", diff --git a/applications/services/cli/cli_commands.c b/applications/services/cli/cli_commands.c index b4eeebbe6..c3539813b 100644 --- a/applications/services/cli/cli_commands.c +++ b/applications/services/cli/cli_commands.c @@ -9,6 +9,7 @@ #include #include #include +#include // Close to ISO, `date +'%Y-%m-%d %H:%M:%S %u'` #define CLI_DATE_FORMAT "%.4d-%.2d-%.2d %.2d:%.2d:%.2d %d" @@ -361,9 +362,9 @@ void cli_command_led(Cli* cli, FuriString* args, void* context) { } furi_string_free(light_name); // Read light value from the rest of the string - char* end_ptr; - uint32_t value = strtoul(furi_string_get_cstr(args), &end_ptr, 0); - if(!(value < 256 && *end_ptr == '\0')) { + uint32_t value; + if(strint_to_uint32(furi_string_get_cstr(args), NULL, &value, 0) != StrintParseNoError || + value >= 256) { cli_print_usage("led", " <0-255>", furi_string_get_cstr(args)); return; } diff --git a/applications/services/gui/modules/number_input.c b/applications/services/gui/modules/number_input.c index 777e55747..5a5e56c1c 100644 --- a/applications/services/gui/modules/number_input.c +++ b/applications/services/gui/modules/number_input.c @@ -3,6 +3,7 @@ #include #include #include +#include struct NumberInput { View* view; @@ -163,7 +164,11 @@ static void number_input_handle_right(NumberInputModel* model) { } static bool is_number_too_large(NumberInputModel* model) { - int64_t value = strtoll(furi_string_get_cstr(model->text_buffer), NULL, 10); + int64_t value; + if(strint_to_int64(furi_string_get_cstr(model->text_buffer), NULL, &value, 10) != + StrintParseNoError) { + return true; + } if(value > (int64_t)model->max_value) { return true; } @@ -171,7 +176,11 @@ static bool is_number_too_large(NumberInputModel* model) { } static bool is_number_too_small(NumberInputModel* model) { - int64_t value = strtoll(furi_string_get_cstr(model->text_buffer), NULL, 10); + int64_t value; + if(strint_to_int64(furi_string_get_cstr(model->text_buffer), NULL, &value, 10) != + StrintParseNoError) { + return true; + } if(value < (int64_t)model->min_value) { return true; } diff --git a/applications/services/loader/loader_cli.c b/applications/services/loader/loader_cli.c index a0254f0d0..f3ea30df2 100644 --- a/applications/services/loader/loader_cli.c +++ b/applications/services/loader/loader_cli.c @@ -4,6 +4,7 @@ #include #include #include +#include #include static void loader_cli_print_usage(void) { @@ -89,18 +90,22 @@ static void loader_cli_close(Loader* loader) { static void loader_cli_signal(FuriString* args, Loader* loader) { uint32_t signal; - void* arg = NULL; + uint32_t arg = 0; + StrintParseError parse_err = 0; + char* args_cstr = (char*)furi_string_get_cstr(args); + parse_err |= strint_to_uint32(args_cstr, &args_cstr, &signal, 10); + parse_err |= strint_to_uint32(args_cstr, &args_cstr, &arg, 16); - if(!sscanf(furi_string_get_cstr(args), "%lu %p", &signal, &arg)) { + if(parse_err) { printf("Signal must be a decimal number\r\n"); } else if(!loader_is_locked(loader)) { printf("No application is running\r\n"); } else { - const bool is_handled = loader_signal(loader, signal, arg); + const bool is_handled = loader_signal(loader, signal, (void*)arg); printf( "Signal %lu with argument 0x%p was %s\r\n", signal, - arg, + (void*)arg, is_handled ? "handled" : "ignored"); } } diff --git a/applications/services/storage/storage_cli.c b/applications/services/storage/storage_cli.c index a18b28940..441b58da6 100644 --- a/applications/services/storage/storage_cli.c +++ b/applications/services/storage/storage_cli.c @@ -3,8 +3,9 @@ #include #include -#include #include +#include +#include #include #include #include @@ -267,9 +268,8 @@ static void storage_cli_read_chunks(Cli* cli, FuriString* path, FuriString* args File* file = storage_file_alloc(api); uint32_t buffer_size; - int parsed_count = sscanf(furi_string_get_cstr(args), "%lu", &buffer_size); - - if(parsed_count != 1) { + if(strint_to_uint32(furi_string_get_cstr(args), NULL, &buffer_size, 10) != + StrintParseNoError) { storage_cli_print_usage(); } else if(storage_file_open(file, furi_string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) { uint64_t file_size = storage_file_size(file); @@ -307,9 +307,8 @@ static void storage_cli_write_chunk(Cli* cli, FuriString* path, FuriString* args File* file = storage_file_alloc(api); uint32_t buffer_size; - int parsed_count = sscanf(furi_string_get_cstr(args), "%lu", &buffer_size); - - if(parsed_count != 1) { + if(strint_to_uint32(furi_string_get_cstr(args), NULL, &buffer_size, 10) != + StrintParseNoError) { storage_cli_print_usage(); } else { if(storage_file_open(file, furi_string_get_cstr(path), FSAM_WRITE, FSOM_OPEN_APPEND)) { diff --git a/lib/flipper_format/flipper_format_stream.c b/lib/flipper_format/flipper_format_stream.c index b9d33169c..8a16dbfd9 100644 --- a/lib/flipper_format/flipper_format_stream.c +++ b/lib/flipper_format/flipper_format_stream.c @@ -1,5 +1,6 @@ #include #include +#include #include #include "flipper_format_stream.h" #include "flipper_format_stream_i.h" @@ -396,14 +397,16 @@ bool flipper_format_stream_read_value_line( #endif case FlipperStreamValueInt32: { int32_t* data = _data; - scan_values = sscanf(furi_string_get_cstr(value), "%" PRIi32, &data[i]); + if(strint_to_int32(furi_string_get_cstr(value), NULL, &data[i], 10) == + StrintParseNoError) { + scan_values = 1; + } }; break; case FlipperStreamValueUint32: { uint32_t* data = _data; - // Minus sign is allowed in scanf() for unsigned numbers, resulting in unintentionally huge values with no error reported - if(!furi_string_start_with(value, "-")) { - scan_values = - sscanf(furi_string_get_cstr(value), "%" PRIu32, &data[i]); + if(strint_to_uint32(furi_string_get_cstr(value), NULL, &data[i], 10) == + StrintParseNoError) { + scan_values = 1; } }; break; case FlipperStreamValueHexUint64: { diff --git a/lib/subghz/subghz_file_encoder_worker.c b/lib/subghz/subghz_file_encoder_worker.c index 1ccde73a4..60737250b 100644 --- a/lib/subghz/subghz_file_encoder_worker.c +++ b/lib/subghz/subghz_file_encoder_worker.c @@ -4,6 +4,7 @@ #include #include #include +#include #define TAG "SubGhzFileEncoderWorker" @@ -45,27 +46,26 @@ void subghz_file_encoder_worker_add_level_duration( } bool subghz_file_encoder_worker_data_parse(SubGhzFileEncoderWorker* instance, const char* strStart) { - char* str1; - bool res = false; // Line sample: "RAW_Data: -1, 2, -2..." - // Look for a key in the line - str1 = strstr(strStart, "RAW_Data: "); + // Look for the key in the line + char* str = strstr(strStart, "RAW_Data: "); + bool res = false; - if(str1 != NULL) { + if(str) { // Skip key - str1 = strchr(str1, ' '); + str = strchr(str, ' '); - // Check that there is still an element in the line - while(strchr(str1, ' ') != NULL) { - str1 = strchr(str1, ' '); - - // Skip space - str1 += 1; - subghz_file_encoder_worker_add_level_duration(instance, atoi(str1)); + // Parse next element + int32_t duration; + while(strint_to_int32(str, &str, &duration, 10) == StrintParseNoError) { + subghz_file_encoder_worker_add_level_duration(instance, duration); + if(*str == ',') str++; // could also be `\0` } + res = true; } + return res; } diff --git a/lib/toolbox/SConscript b/lib/toolbox/SConscript index 11e01a8c9..03b8999c4 100644 --- a/lib/toolbox/SConscript +++ b/lib/toolbox/SConscript @@ -29,6 +29,7 @@ env.Append( File("stream/file_stream.h"), File("stream/string_stream.h"), File("stream/buffered_file_stream.h"), + File("strint.h"), File("protocols/protocol_dict.h"), File("pretty_format.h"), File("hex.h"), diff --git a/lib/toolbox/args.c b/lib/toolbox/args.c index aa790ad68..914b093ba 100644 --- a/lib/toolbox/args.c +++ b/lib/toolbox/args.c @@ -1,5 +1,7 @@ #include "args.h" #include "hex.h" +#include "strint.h" +#include "m-core.h" size_t args_get_first_word_length(FuriString* args) { size_t ws = furi_string_search_char(args, ' '); @@ -21,7 +23,9 @@ bool args_read_int_and_trim(FuriString* args, int* value) { return false; } - if(sscanf(furi_string_get_cstr(args), "%d", value) == 1) { + int32_t temp; + if(strint_to_int32(furi_string_get_cstr(args), NULL, &temp, 10) == StrintParseNoError) { + *value = temp; furi_string_right(args, cmd_length); furi_string_trim(args); return true; diff --git a/lib/toolbox/strint.c b/lib/toolbox/strint.c new file mode 100644 index 000000000..8c7f36976 --- /dev/null +++ b/lib/toolbox/strint.c @@ -0,0 +1,121 @@ +#include "strint.h" + +#include + +// Splitting out the actual parser helps reduce code size. The manually +// monomorphized `strint_to_*`s are just wrappers around this generic +// implementation. +/** + * @brief Converts a string to a `uint64_t` and an auxillary sign bit, checking + * the bounds of the integer. + * @param [in] str Input string + * @param [out] end Pointer to first character after the number in input string + * @param [out] abs_out Absolute part of result + * @param [out] negative_out Sign part of result (true=negative, false=positive) + * @param [in] base Integer base + * @param [in] max_abs_negative Largest permissible absolute part of result if + * the sign is negative + * @param [in] max_positive Largest permissible absolute part of result if the + * sign is positive + */ +StrintParseError strint_to_uint64_internal( + const char* str, + char** end, + uint64_t* abs_out, + bool* negative_out, + uint8_t base, + uint64_t max_abs_negative, + uint64_t max_positive) { + // skip whitespace + while(((*str >= '\t') && (*str <= '\r')) || *str == ' ') { + str++; + } + + // read sign + bool negative = false; + if(*str == '+' || *str == '-') { + if(*str == '-') negative = true; + str++; + } + if(*str == '+' || *str == '-') return StrintParseSignError; + if(max_abs_negative == 0 && negative) return StrintParseSignError; + + // infer base + // not assigning directly to `base' to permit prefixes with explicit bases + uint8_t inferred_base = 0; + if(strncasecmp(str, "0x", 2) == 0) { + inferred_base = 16; + str += 2; + } else if(strncasecmp(str, "0b", 2) == 0) { + inferred_base = 2; + str += 2; + } else if(*str == '0') { + inferred_base = 8; + str++; + } else { + inferred_base = 10; + } + if(base == 0) base = inferred_base; + + // read digits + uint64_t limit = negative ? max_abs_negative : max_positive; + uint64_t mul_limit = limit / base; + uint64_t result = 0; + int read_total = 0; + while(*str != 0) { + int digit_value; + if(*str >= '0' && *str <= '9') { + digit_value = *str - '0'; + } else if(*str >= 'A' && *str <= 'Z') { + digit_value = *str - 'A' + 10; + } else if(*str >= 'a' && *str <= 'z') { + digit_value = *str - 'a' + 10; + } else { + break; + } + + if(digit_value >= base) { + break; + } + + if(result > mul_limit) return StrintParseOverflowError; + result *= base; + if(result > limit - digit_value) return StrintParseOverflowError; + result += digit_value; + + read_total++; + str++; + } + + if(read_total == 0) { + if(inferred_base == 8) { + // there's just a single zero + result = 0; + } else { + return StrintParseAbsentError; + } + } + + if(abs_out) *abs_out = result; + if(negative_out) *negative_out = negative; + if(end) *end = (char*)str; // rabbit hole: https://c-faq.com/ansi/constmismatch.html + return StrintParseNoError; +} + +#define STRINT_MONO(name, ret_type, neg_abs_limit, pos_limit) \ + StrintParseError name(const char* str, char** end, ret_type* out, uint8_t base) { \ + uint64_t absolute; \ + bool negative; \ + StrintParseError err = strint_to_uint64_internal( \ + str, end, &absolute, &negative, base, (neg_abs_limit), (pos_limit)); \ + if(err) return err; \ + if(out) *out = (negative ? (-(ret_type)absolute) : ((ret_type)absolute)); \ + return StrintParseNoError; \ + } + +STRINT_MONO(strint_to_uint64, uint64_t, 0, UINT64_MAX) +STRINT_MONO(strint_to_int64, int64_t, (uint64_t)INT64_MAX + 1, INT64_MAX) +STRINT_MONO(strint_to_uint32, uint32_t, 0, UINT32_MAX) +STRINT_MONO(strint_to_int32, int32_t, (uint64_t)INT32_MAX + 1, INT32_MAX) +STRINT_MONO(strint_to_uint16, uint16_t, 0, UINT16_MAX) +STRINT_MONO(strint_to_int16, int16_t, (uint64_t)INT16_MAX + 1, INT16_MAX) diff --git a/lib/toolbox/strint.h b/lib/toolbox/strint.h new file mode 100644 index 000000000..c27cdcb4e --- /dev/null +++ b/lib/toolbox/strint.h @@ -0,0 +1,70 @@ +/** + * @file strint.h + * Performs conversions between strings and integers. + */ +#pragma once + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** String to integer conversion error */ +typedef enum { + StrintParseNoError, //!< Conversion performed successfully + StrintParseSignError, //!< Multiple leading `+` or `-` characters, or leading `-` character if the type is unsigned + StrintParseAbsentError, //!< No valid digits after the leading whitespace, sign and prefix + StrintParseOverflowError, //!< Result does not fit in the requested type +} StrintParseError; + +/** See `strint_to_uint32` */ +StrintParseError strint_to_uint64(const char* str, char** end, uint64_t* out, uint8_t base); + +/** See `strint_to_uint32` */ +StrintParseError strint_to_int64(const char* str, char** end, int64_t* out, uint8_t base); + +/** Converts a string to a `uint32_t` + * + * @param[in] str Input string + * @param[out] end Pointer to first character after the number in input string + * @param[out] out Parse result + * @param[in] base Integer base + * + * @return Parse error + * + * Parses the number in the input string. The number may be surrounded by + * whitespace characters to the left and any non-digit characters to the right. + * What's considered a digit is determined by the input base in the following + * order: `0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ`. The number may be prefixed + * with either a `+` or a `-` to indicate its sign. The pointer to the first + * character after the leading whitespace, allowed prefixes and digits is + * assigned to `end`. + * + * If the input base is 0, the base is inferred from the leading characters of + * the number: + * - If it starts with `0x`, it's read in base 16; + * - If it starts with a `0`, it's read in base 8; + * - If it starts with `0b`, it's read in base 2. + * - Otherwise, it's read in base 10. + * + * For a description of the return codes, see `StrintParseError`. If the return + * code is something other than `StrintParseNoError`, the values at `end` and + * `out` are unaltered. + */ +StrintParseError strint_to_uint32(const char* str, char** end, uint32_t* out, uint8_t base); + +/** See `strint_to_uint32` */ +StrintParseError strint_to_int32(const char* str, char** end, int32_t* out, uint8_t base); + +/** See `strint_to_uint32` */ +StrintParseError strint_to_uint16(const char* str, char** end, uint16_t* out, uint8_t base); + +/** See `strint_to_uint32` */ +StrintParseError strint_to_int16(const char* str, char** end, int16_t* out, uint8_t base); + +#ifdef __cplusplus +} +#endif diff --git a/lib/update_util/resources/manifest.c b/lib/update_util/resources/manifest.c index 580a76d45..92d84a779 100644 --- a/lib/update_util/resources/manifest.c +++ b/lib/update_util/resources/manifest.c @@ -1,6 +1,7 @@ #include "manifest.h" #include +#include #include struct ResourceManifestReader { @@ -97,7 +98,12 @@ ResourceManifestEntry* resource_manifest_reader_next(ResourceManifestReader* res furi_string_right( resource_manifest->linebuf, sizeof(resource_manifest->entry.hash) * 2 + 1); - resource_manifest->entry.size = atoi(furi_string_get_cstr(resource_manifest->linebuf)); + if(strint_to_uint32( + furi_string_get_cstr(resource_manifest->linebuf), + NULL, + &resource_manifest->entry.size, + 10) != StrintParseNoError) + break; /* Remove size */ size_t offs = furi_string_search_char(resource_manifest->linebuf, ':'); diff --git a/targets/f18/api_symbols.csv b/targets/f18/api_symbols.csv index 2c1a56565..b603813a7 100644 --- a/targets/f18/api_symbols.csv +++ b/targets/f18/api_symbols.csv @@ -170,6 +170,7 @@ Header,+,lib/toolbox/stream/buffered_file_stream.h,, Header,+,lib/toolbox/stream/file_stream.h,, Header,+,lib/toolbox/stream/stream.h,, Header,+,lib/toolbox/stream/string_stream.h,, +Header,+,lib/toolbox/strint.h,, Header,+,lib/toolbox/tar/tar_archive.h,, Header,+,lib/toolbox/value_index.h,, Header,+,lib/toolbox/varint.h,, @@ -2589,6 +2590,12 @@ Function,-,strerror,char*,int Function,-,strerror_l,char*,"int, locale_t" Function,-,strerror_r,char*,"int, char*, size_t" Function,+,string_stream_alloc,Stream*, +Function,+,strint_to_int16,StrintParseError,"const char*, char**, int16_t*, uint8_t" +Function,+,strint_to_int32,StrintParseError,"const char*, char**, int32_t*, uint8_t" +Function,+,strint_to_int64,StrintParseError,"const char*, char**, int64_t*, uint8_t" +Function,+,strint_to_uint16,StrintParseError,"const char*, char**, uint16_t*, uint8_t" +Function,+,strint_to_uint32,StrintParseError,"const char*, char**, uint32_t*, uint8_t" +Function,+,strint_to_uint64,StrintParseError,"const char*, char**, uint64_t*, uint8_t" Function,-,strlcat,size_t,"char*, const char*, size_t" Function,+,strlcpy,size_t,"char*, const char*, size_t" Function,+,strlen,size_t,const char* diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index e5292d936..1d5b98fe3 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -242,6 +242,7 @@ Header,+,lib/toolbox/stream/buffered_file_stream.h,, Header,+,lib/toolbox/stream/file_stream.h,, Header,+,lib/toolbox/stream/stream.h,, Header,+,lib/toolbox/stream/string_stream.h,, +Header,+,lib/toolbox/strint.h,, Header,+,lib/toolbox/tar/tar_archive.h,, Header,+,lib/toolbox/value_index.h,, Header,+,lib/toolbox/varint.h,, @@ -3266,6 +3267,12 @@ Function,-,strerror,char*,int Function,-,strerror_l,char*,"int, locale_t" Function,-,strerror_r,char*,"int, char*, size_t" Function,+,string_stream_alloc,Stream*, +Function,+,strint_to_int16,StrintParseError,"const char*, char**, int16_t*, uint8_t" +Function,+,strint_to_int32,StrintParseError,"const char*, char**, int32_t*, uint8_t" +Function,+,strint_to_int64,StrintParseError,"const char*, char**, int64_t*, uint8_t" +Function,+,strint_to_uint16,StrintParseError,"const char*, char**, uint16_t*, uint8_t" +Function,+,strint_to_uint32,StrintParseError,"const char*, char**, uint32_t*, uint8_t" +Function,+,strint_to_uint64,StrintParseError,"const char*, char**, uint64_t*, uint8_t" Function,-,strlcat,size_t,"char*, const char*, size_t" Function,+,strlcpy,size_t,"char*, const char*, size_t" Function,+,strlen,size_t,const char* From feb1b2f34942962d886683fd7fb1310c480f2a7f Mon Sep 17 00:00:00 2001 From: hedger Date: Thu, 5 Sep 2024 20:44:22 +0300 Subject: [PATCH 7/8] [FL-3882] Clean up of LFS traces (#3849) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * updater, storage: removed mentions of LFS from public APIs; updated corresponding strings * rpc: updated include path Co-authored-by: あく --- applications/services/rpc/rpc_storage.c | 6 +++--- applications/services/storage/storage.h | 2 +- applications/system/updater/cli/updater_cli.c | 6 +++--- applications/system/updater/util/update_task.c | 14 +++++++------- applications/system/updater/util/update_task.h | 4 ++-- .../updater/util/update_task_worker_backup.c | 14 +++++++------- .../updater/util/update_task_worker_flasher.c | 2 +- documentation/OTA.md | 4 ++-- lib/update_util/{lfs_backup.c => int_backup.c} | 16 ++++++++-------- lib/update_util/int_backup.h | 18 ++++++++++++++++++ lib/update_util/lfs_backup.h | 18 ------------------ targets/f7/furi_hal/furi_hal_rtc.h | 3 ++- 12 files changed, 54 insertions(+), 53 deletions(-) rename lib/update_util/{lfs_backup.c => int_backup.c} (77%) create mode 100644 lib/update_util/int_backup.h delete mode 100644 lib/update_util/lfs_backup.h diff --git a/applications/services/rpc/rpc_storage.c b/applications/services/rpc/rpc_storage.c index 89991aa86..aedcf8c94 100644 --- a/applications/services/rpc/rpc_storage.c +++ b/applications/services/rpc/rpc_storage.c @@ -7,7 +7,7 @@ #include #include #include -#include +#include #include #include @@ -656,7 +656,7 @@ static void rpc_system_storage_backup_create_process(const PB_Main* request, voi rpc_system_storage_reset_state(rpc_storage, session, true); - bool backup_ok = lfs_backup_create( + bool backup_ok = int_backup_create( rpc_storage->api, request->content.storage_backup_create_request.archive_path); rpc_send_and_release_empty( @@ -676,7 +676,7 @@ static void rpc_system_storage_backup_restore_process(const PB_Main* request, vo rpc_system_storage_reset_state(rpc_storage, session, true); - bool backup_ok = lfs_backup_unpack( + bool backup_ok = int_backup_unpack( rpc_storage->api, request->content.storage_backup_restore_request.archive_path); rpc_send_and_release_empty( diff --git a/applications/services/storage/storage.h b/applications/services/storage/storage.h index 6dbeb0d36..072db1305 100644 --- a/applications/services/storage/storage.h +++ b/applications/services/storage/storage.h @@ -504,7 +504,7 @@ FS_Error storage_sd_info(Storage* storage, SDInfo* info); */ FS_Error storage_sd_status(Storage* storage); -/******************* Internal LFS Functions *******************/ +/************ Internal Storage Backup/Restore ************/ typedef void (*StorageNameConverter)(FuriString*); diff --git a/applications/system/updater/cli/updater_cli.c b/applications/system/updater/cli/updater_cli.c index 0b734c0f4..56a16bd9d 100644 --- a/applications/system/updater/cli/updater_cli.c +++ b/applications/system/updater/cli/updater_cli.c @@ -8,7 +8,7 @@ #include #include #include -#include +#include #include typedef void (*cmd_handler)(FuriString* args); @@ -35,7 +35,7 @@ static void updater_cli_install(FuriString* manifest_path) { static void updater_cli_backup(FuriString* args) { printf("Backup /int to '%s'\r\n", furi_string_get_cstr(args)); Storage* storage = furi_record_open(RECORD_STORAGE); - bool success = lfs_backup_create(storage, furi_string_get_cstr(args)); + bool success = int_backup_create(storage, furi_string_get_cstr(args)); furi_record_close(RECORD_STORAGE); printf("Result: %s\r\n", success ? "OK" : "FAIL"); } @@ -43,7 +43,7 @@ static void updater_cli_backup(FuriString* args) { static void updater_cli_restore(FuriString* args) { printf("Restore /int from '%s'\r\n", furi_string_get_cstr(args)); Storage* storage = furi_record_open(RECORD_STORAGE); - bool success = lfs_backup_unpack(storage, furi_string_get_cstr(args)); + bool success = int_backup_unpack(storage, furi_string_get_cstr(args)); furi_record_close(RECORD_STORAGE); printf("Result: %s\r\n", success ? "OK" : "FAIL"); } diff --git a/applications/system/updater/util/update_task.c b/applications/system/updater/util/update_task.c index 8f051ff77..cca488475 100644 --- a/applications/system/updater/util/update_task.c +++ b/applications/system/updater/util/update_task.c @@ -22,8 +22,8 @@ static const char* update_task_stage_descr[] = { [UpdateTaskStageRadioInstall] = "Installing radio FW", [UpdateTaskStageRadioBusy] = "Core 2 busy", [UpdateTaskStageOBValidation] = "Validating opt. bytes", - [UpdateTaskStageLfsBackup] = "Backing up LFS", - [UpdateTaskStageLfsRestore] = "Restoring LFS", + [UpdateTaskStageIntBackup] = "Backing up configuration", + [UpdateTaskStageIntRestore] = "Restoring configuration", [UpdateTaskStageResourcesFileCleanup] = "Cleaning up files", [UpdateTaskStageResourcesDirCleanup] = "Cleaning up directories", [UpdateTaskStageResourcesFileUnpack] = "Extracting resources", @@ -82,7 +82,7 @@ static const struct { }, #ifndef FURI_RAM_EXEC { - .stage = UpdateTaskStageLfsBackup, + .stage = UpdateTaskStageIntBackup, .percent_min = 0, .percent_max = 100, .descr = "FS R/W error", @@ -193,10 +193,10 @@ static const struct { #endif #ifndef FURI_RAM_EXEC { - .stage = UpdateTaskStageLfsRestore, + .stage = UpdateTaskStageIntRestore, .percent_min = 0, .percent_max = 100, - .descr = "LFS I/O error", + .descr = "SD card I/O error", }, { .stage = UpdateTaskStageResourcesFileCleanup, @@ -245,7 +245,7 @@ static const UpdateTaskStageGroupMap update_task_stage_progress[] = { [UpdateTaskStageProgress] = STAGE_DEF(UpdateTaskStageGroupMisc, 0), [UpdateTaskStageReadManifest] = STAGE_DEF(UpdateTaskStageGroupPreUpdate, 45), - [UpdateTaskStageLfsBackup] = STAGE_DEF(UpdateTaskStageGroupPreUpdate, 5), + [UpdateTaskStageIntBackup] = STAGE_DEF(UpdateTaskStageGroupPreUpdate, 5), [UpdateTaskStageRadioImageValidate] = STAGE_DEF(UpdateTaskStageGroupRadio, 15), [UpdateTaskStageRadioErase] = STAGE_DEF(UpdateTaskStageGroupRadio, 25), @@ -259,7 +259,7 @@ static const UpdateTaskStageGroupMap update_task_stage_progress[] = { [UpdateTaskStageFlashWrite] = STAGE_DEF(UpdateTaskStageGroupFirmware, 100), [UpdateTaskStageFlashValidate] = STAGE_DEF(UpdateTaskStageGroupFirmware, 20), - [UpdateTaskStageLfsRestore] = STAGE_DEF(UpdateTaskStageGroupPostUpdate, 5), + [UpdateTaskStageIntRestore] = STAGE_DEF(UpdateTaskStageGroupPostUpdate, 5), [UpdateTaskStageResourcesFileCleanup] = STAGE_DEF(UpdateTaskStageGroupResources, 100), [UpdateTaskStageResourcesDirCleanup] = STAGE_DEF(UpdateTaskStageGroupResources, 50), diff --git a/applications/system/updater/util/update_task.h b/applications/system/updater/util/update_task.h index 52bdfdbd2..c346c55fa 100644 --- a/applications/system/updater/util/update_task.h +++ b/applications/system/updater/util/update_task.h @@ -16,7 +16,7 @@ typedef enum { UpdateTaskStageProgress = 0, UpdateTaskStageReadManifest, - UpdateTaskStageLfsBackup, + UpdateTaskStageIntBackup, UpdateTaskStageRadioImageValidate, UpdateTaskStageRadioErase, @@ -30,7 +30,7 @@ typedef enum { UpdateTaskStageFlashWrite, UpdateTaskStageFlashValidate, - UpdateTaskStageLfsRestore, + UpdateTaskStageIntRestore, UpdateTaskStageResourcesFileCleanup, UpdateTaskStageResourcesDirCleanup, UpdateTaskStageResourcesFileUnpack, diff --git a/applications/system/updater/util/update_task_worker_backup.c b/applications/system/updater/util/update_task_worker_backup.c index 8d5039a16..1b5bea25b 100644 --- a/applications/system/updater/util/update_task_worker_backup.c +++ b/applications/system/updater/util/update_task_worker_backup.c @@ -7,7 +7,7 @@ #include #include #include -#include +#include #include #include #include @@ -21,14 +21,14 @@ static bool update_task_pre_update(UpdateTask* update_task) { backup_file_path = furi_string_alloc(); path_concat( furi_string_get_cstr(update_task->update_path), - LFS_BACKUP_DEFAULT_FILENAME, + INT_BACKUP_DEFAULT_FILENAME, backup_file_path); - update_task_set_progress(update_task, UpdateTaskStageLfsBackup, 0); + update_task_set_progress(update_task, UpdateTaskStageIntBackup, 0); /* to avoid bootloops */ furi_hal_rtc_set_boot_mode(FuriHalRtcBootModeNormal); if((success = - lfs_backup_create(update_task->storage, furi_string_get_cstr(backup_file_path)))) { + int_backup_create(update_task->storage, furi_string_get_cstr(backup_file_path)))) { furi_hal_rtc_set_boot_mode(FuriHalRtcBootModeUpdate); } @@ -145,12 +145,12 @@ static bool update_task_post_update(UpdateTask* update_task) { do { path_concat( furi_string_get_cstr(update_task->update_path), - LFS_BACKUP_DEFAULT_FILENAME, + INT_BACKUP_DEFAULT_FILENAME, file_path); - update_task_set_progress(update_task, UpdateTaskStageLfsRestore, 0); + update_task_set_progress(update_task, UpdateTaskStageIntRestore, 0); - CHECK_RESULT(lfs_backup_unpack(update_task->storage, furi_string_get_cstr(file_path))); + CHECK_RESULT(int_backup_unpack(update_task->storage, furi_string_get_cstr(file_path))); if(update_task->state.groups & UpdateTaskStageGroupResources) { TarUnpackProgress progress = { diff --git a/applications/system/updater/util/update_task_worker_flasher.c b/applications/system/updater/util/update_task_worker_flasher.c index e7e1bbbed..a464815f0 100644 --- a/applications/system/updater/util/update_task_worker_flasher.c +++ b/applications/system/updater/util/update_task_worker_flasher.c @@ -341,7 +341,7 @@ int32_t update_task_worker_flash_writer(void* context) { } furi_hal_rtc_set_boot_mode(FuriHalRtcBootModePostUpdate); - // Format LFS before restoring backup on next boot + // Clean up /int before restoring backup on next boot furi_hal_rtc_set_flag(FuriHalRtcFlagStorageFormatInternal); #ifdef FURI_NDEBUG // Production diff --git a/documentation/OTA.md b/documentation/OTA.md index 0456eab1f..9783a7047 100644 --- a/documentation/OTA.md +++ b/documentation/OTA.md @@ -83,7 +83,7 @@ Even if something goes wrong, updater allows you to retry failed operations and | | | **50** | Package has mismatching HW target | | | | **60** | Missing DFU file | | | | **80** | Missing radio firmware file | -| Backing up LFS | **2** | **0-100** | FS read/write error | +| Backing up configuration| **2** | **0-100** | FS read/write error | | Checking radio FW | **3** | **0-99** | Error reading radio firmware file | | | | **100** | CRC mismatch | | Uninstalling radio FW | **4** | **0** | SHCI Delete command error | @@ -101,7 +101,7 @@ Even if something goes wrong, updater allows you to retry failed operations and | | | **99-100** | Corrupted DFU file | | Writing flash | **10** | **0-100** | Block read/write error | | Validating flash | **11** | **0-100** | Block read/write error | -| Restoring LFS | **12** | **0-100** | FS read/write error | +| Restoring configuration | **12** | **0-100** | FS read/write error | | Updating resources | **13-15** | **0-100** | SD card read/write error | ## Building update packages diff --git a/lib/update_util/lfs_backup.c b/lib/update_util/int_backup.c similarity index 77% rename from lib/update_util/lfs_backup.c rename to lib/update_util/int_backup.c index 7786524ef..a904db247 100644 --- a/lib/update_util/lfs_backup.c +++ b/lib/update_util/int_backup.c @@ -1,4 +1,4 @@ -#include "lfs_backup.h" +#include "int_backup.h" #include @@ -9,7 +9,7 @@ #include #include -#define LFS_BACKUP_DEFAULT_LOCATION EXT_PATH(LFS_BACKUP_DEFAULT_FILENAME) +#define INT_BACKUP_DEFAULT_LOCATION EXT_PATH(INT_BACKUP_DEFAULT_FILENAME) static void backup_name_converter(FuriString* filename) { if(furi_string_empty(filename) || (furi_string_get_char(filename, 0) == '.')) { @@ -34,18 +34,18 @@ static void backup_name_converter(FuriString* filename) { } } -bool lfs_backup_create(Storage* storage, const char* destination) { +bool int_backup_create(Storage* storage, const char* destination) { const char* final_destination = - destination && strlen(destination) ? destination : LFS_BACKUP_DEFAULT_LOCATION; + destination && strlen(destination) ? destination : INT_BACKUP_DEFAULT_LOCATION; return storage_int_backup(storage, final_destination) == FSE_OK; } -bool lfs_backup_exists(Storage* storage, const char* source) { - const char* final_source = source && strlen(source) ? source : LFS_BACKUP_DEFAULT_LOCATION; +bool int_backup_exists(Storage* storage, const char* source) { + const char* final_source = source && strlen(source) ? source : INT_BACKUP_DEFAULT_LOCATION; return storage_common_stat(storage, final_source, NULL) == FSE_OK; } -bool lfs_backup_unpack(Storage* storage, const char* source) { - const char* final_source = source && strlen(source) ? source : LFS_BACKUP_DEFAULT_LOCATION; +bool int_backup_unpack(Storage* storage, const char* source) { + const char* final_source = source && strlen(source) ? source : INT_BACKUP_DEFAULT_LOCATION; return storage_int_restore(storage, final_source, backup_name_converter) == FSE_OK; } diff --git a/lib/update_util/int_backup.h b/lib/update_util/int_backup.h new file mode 100644 index 000000000..168efda50 --- /dev/null +++ b/lib/update_util/int_backup.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + +#define INT_BACKUP_DEFAULT_FILENAME "backup.tar" + +#ifdef __cplusplus +extern "C" { +#endif + +bool int_backup_create(Storage* storage, const char* destination); +bool int_backup_exists(Storage* storage, const char* source); +bool int_backup_unpack(Storage* storage, const char* source); + +#ifdef __cplusplus +} +#endif diff --git a/lib/update_util/lfs_backup.h b/lib/update_util/lfs_backup.h deleted file mode 100644 index 5a7738c86..000000000 --- a/lib/update_util/lfs_backup.h +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -#include -#include - -#define LFS_BACKUP_DEFAULT_FILENAME "backup.tar" - -#ifdef __cplusplus -extern "C" { -#endif - -bool lfs_backup_create(Storage* storage, const char* destination); -bool lfs_backup_exists(Storage* storage, const char* source); -bool lfs_backup_unpack(Storage* storage, const char* source); - -#ifdef __cplusplus -} -#endif diff --git a/targets/f7/furi_hal/furi_hal_rtc.h b/targets/f7/furi_hal/furi_hal_rtc.h index 030b464cf..c5eab12ec 100644 --- a/targets/f7/furi_hal/furi_hal_rtc.h +++ b/targets/f7/furi_hal/furi_hal_rtc.h @@ -9,6 +9,7 @@ #include #include +#include #ifdef __cplusplus extern "C" { @@ -44,7 +45,7 @@ typedef enum { FuriHalRtcRegisterHeader, /**< RTC structure header */ FuriHalRtcRegisterSystem, /**< Various system bits */ FuriHalRtcRegisterVersion, /**< Pointer to Version */ - FuriHalRtcRegisterLfsFingerprint, /**< LFS geometry fingerprint */ + FuriHalRtcRegisterLfsFingerprint FURI_DEPRECATED, /**< LFS geometry fingerprint */ FuriHalRtcRegisterFaultData, /**< Pointer to last fault message */ FuriHalRtcRegisterPinFails, /**< Failed PINs count */ /* Index of FS directory entry corresponding to FW update to be applied */ From 62bbf406be28bd5855a0a160e73e001f40c1d8b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Fri, 6 Sep 2024 03:55:43 +0900 Subject: [PATCH 8/8] Debug: use proper hook for handle_exit in flipperapps (#3842) * Debug: use proper hook for handle_exit in flipperapps * fbt: flash firmware for `blackmagic` target Co-authored-by: hedger Co-authored-by: Georgii Surkov <37121527+gsurkov@users.noreply.github.com> --- SConstruct | 3 ++- scripts/debug/flipperapps.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/SConstruct b/SConstruct index b4c8d7b29..127967968 100644 --- a/SConstruct +++ b/SConstruct @@ -234,7 +234,7 @@ firmware_debug = distenv.PhonyTarget( ) distenv.Depends(firmware_debug, firmware_flash) -distenv.PhonyTarget( +firmware_blackmagic = distenv.PhonyTarget( "blackmagic", "${GDBPYCOM}", source=firmware_env["FW_ELF"], @@ -242,6 +242,7 @@ distenv.PhonyTarget( GDBREMOTE="${BLACKMAGIC_ADDR}", FBT_FAP_DEBUG_ELF_ROOT=firmware_env["FBT_FAP_DEBUG_ELF_ROOT"], ) +distenv.Depends(firmware_blackmagic, firmware_flash) # Debug alien elf debug_other_opts = [ diff --git a/scripts/debug/flipperapps.py b/scripts/debug/flipperapps.py index 6dba89a56..81aa43c34 100644 --- a/scripts/debug/flipperapps.py +++ b/scripts/debug/flipperapps.py @@ -124,7 +124,7 @@ class SetFapDebugElfRoot(gdb.Command): print(f"Set '{arg}' as debug info lookup path for Flipper external apps") helper.attach_to_fw() gdb.events.stop.connect(helper.handle_stop) - gdb.events.exited.connect(helper.handle_exit) + gdb.events.gdb_exiting.connect(helper.handle_exit) except gdb.error as e: print(f"Support for Flipper external apps debug is not available: {e}")