From 0428e82b14065e36cdae352b1b59a6ededa3c6f8 Mon Sep 17 00:00:00 2001 From: porta Date: Thu, 12 Sep 2024 19:32:07 +0300 Subject: [PATCH 1/2] Fix USB-UART bridge exit screen stopping the bridge prematurely (#3892) * fix: exit screen stopping bridge prematurely * refactor: merge exit confirmation scene into main usb uart scene --- .../main/gpio/scenes/gpio_scene_config.h | 1 - .../gpio/scenes/gpio_scene_exit_confirm.c | 44 ------------------- .../main/gpio/scenes/gpio_scene_usb_uart.c | 32 +++++++++----- 3 files changed, 21 insertions(+), 56 deletions(-) delete mode 100644 applications/main/gpio/scenes/gpio_scene_exit_confirm.c diff --git a/applications/main/gpio/scenes/gpio_scene_config.h b/applications/main/gpio/scenes/gpio_scene_config.h index d6fd24d19..3406e42d3 100644 --- a/applications/main/gpio/scenes/gpio_scene_config.h +++ b/applications/main/gpio/scenes/gpio_scene_config.h @@ -3,4 +3,3 @@ ADD_SCENE(gpio, test, Test) ADD_SCENE(gpio, usb_uart, UsbUart) ADD_SCENE(gpio, usb_uart_cfg, UsbUartCfg) ADD_SCENE(gpio, usb_uart_close_rpc, UsbUartCloseRpc) -ADD_SCENE(gpio, exit_confirm, ExitConfirm) diff --git a/applications/main/gpio/scenes/gpio_scene_exit_confirm.c b/applications/main/gpio/scenes/gpio_scene_exit_confirm.c deleted file mode 100644 index efb0734a3..000000000 --- a/applications/main/gpio/scenes/gpio_scene_exit_confirm.c +++ /dev/null @@ -1,44 +0,0 @@ -#include "gpio_app_i.h" - -void gpio_scene_exit_confirm_dialog_callback(DialogExResult result, void* context) { - GpioApp* app = context; - - view_dispatcher_send_custom_event(app->view_dispatcher, result); -} - -void gpio_scene_exit_confirm_on_enter(void* context) { - GpioApp* app = context; - DialogEx* dialog = app->dialog; - - dialog_ex_set_context(dialog, app); - dialog_ex_set_left_button_text(dialog, "Exit"); - dialog_ex_set_right_button_text(dialog, "Stay"); - dialog_ex_set_header(dialog, "Exit USB-UART?", 22, 12, AlignLeft, AlignTop); - dialog_ex_set_result_callback(dialog, gpio_scene_exit_confirm_dialog_callback); - - view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewExitConfirm); -} - -bool gpio_scene_exit_confirm_on_event(void* context, SceneManagerEvent event) { - GpioApp* app = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == DialogExResultRight) { - consumed = scene_manager_previous_scene(app->scene_manager); - } else if(event.event == DialogExResultLeft) { - scene_manager_search_and_switch_to_previous_scene(app->scene_manager, GpioSceneStart); - } - } else if(event.type == SceneManagerEventTypeBack) { - consumed = true; - } - - return consumed; -} - -void gpio_scene_exit_confirm_on_exit(void* context) { - GpioApp* app = context; - - // Clean view - dialog_ex_reset(app->dialog); -} diff --git a/applications/main/gpio/scenes/gpio_scene_usb_uart.c b/applications/main/gpio/scenes/gpio_scene_usb_uart.c index 9a3514ca4..e3e7e8c24 100644 --- a/applications/main/gpio/scenes/gpio_scene_usb_uart.c +++ b/applications/main/gpio/scenes/gpio_scene_usb_uart.c @@ -6,7 +6,7 @@ typedef struct { UsbUartState state; } SceneUsbUartBridge; -static SceneUsbUartBridge* scene_usb_uart; +static SceneUsbUartBridge* scene_usb_uart = NULL; void gpio_scene_usb_uart_callback(GpioCustomEvent event, void* context) { furi_assert(context); @@ -14,10 +14,21 @@ void gpio_scene_usb_uart_callback(GpioCustomEvent event, void* context) { view_dispatcher_send_custom_event(app->view_dispatcher, event); } +void gpio_scene_usb_uart_dialog_callback(DialogExResult result, void* context) { + GpioApp* app = context; + if(result == DialogExResultLeft) { + usb_uart_disable(app->usb_uart_bridge); + free(scene_usb_uart); + scene_usb_uart = NULL; + scene_manager_search_and_switch_to_previous_scene(app->scene_manager, GpioSceneStart); + } else { + view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewUsbUart); + } +} + void gpio_scene_usb_uart_on_enter(void* context) { GpioApp* app = context; - uint32_t prev_state = scene_manager_get_scene_state(app->scene_manager, GpioAppViewUsbUart); - if(prev_state == 0) { + if(!scene_usb_uart) { scene_usb_uart = malloc(sizeof(SceneUsbUartBridge)); scene_usb_uart->cfg.vcp_ch = 0; scene_usb_uart->cfg.uart_ch = 0; @@ -31,7 +42,6 @@ void gpio_scene_usb_uart_on_enter(void* context) { usb_uart_get_state(app->usb_uart_bridge, &scene_usb_uart->state); gpio_usb_uart_set_callback(app->gpio_usb_uart, gpio_scene_usb_uart_callback, app); - scene_manager_set_scene_state(app->scene_manager, GpioSceneUsbUart, 0); view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewUsbUart); notification_message(app->notifications, &sequence_display_backlight_enforce_on); } @@ -39,11 +49,16 @@ void gpio_scene_usb_uart_on_enter(void* context) { bool gpio_scene_usb_uart_on_event(void* context, SceneManagerEvent event) { GpioApp* app = context; if(event.type == SceneManagerEventTypeCustom) { - scene_manager_set_scene_state(app->scene_manager, GpioSceneUsbUart, 1); scene_manager_next_scene(app->scene_manager, GpioSceneUsbUartCfg); return true; } else if(event.type == SceneManagerEventTypeBack) { - scene_manager_next_scene(app->scene_manager, GpioSceneExitConfirm); + DialogEx* dialog = app->dialog; + dialog_ex_set_context(dialog, app); + dialog_ex_set_left_button_text(dialog, "Exit"); + dialog_ex_set_right_button_text(dialog, "Stay"); + dialog_ex_set_header(dialog, "Exit USB-UART?", 22, 12, AlignLeft, AlignTop); + dialog_ex_set_result_callback(dialog, gpio_scene_usb_uart_dialog_callback); + view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewExitConfirm); return true; } else if(event.type == SceneManagerEventTypeTick) { uint32_t tx_cnt_last = scene_usb_uart->state.tx_cnt; @@ -61,10 +76,5 @@ bool gpio_scene_usb_uart_on_event(void* context, SceneManagerEvent event) { void gpio_scene_usb_uart_on_exit(void* context) { GpioApp* app = context; - uint32_t prev_state = scene_manager_get_scene_state(app->scene_manager, GpioSceneUsbUart); - if(prev_state == 0) { - usb_uart_disable(app->usb_uart_bridge); - free(scene_usb_uart); - } notification_message(app->notifications, &sequence_display_backlight_enforce_auto); } From b670d5b6e2081bec5abcf3a693db5ce3af5d8ab2 Mon Sep 17 00:00:00 2001 From: porta Date: Fri, 13 Sep 2024 20:31:07 +0300 Subject: [PATCH 2/2] [FL-3885] Put errno into TCB (#3893) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: thread-safe errno * ci: fix pvs warning * ci: silence pvs warning * fix: 🤯 * test: convert test app into a unit test --- .../unit_tests/tests/furi/furi_errno_test.c | 51 +++++++++++++++++++ .../debug/unit_tests/tests/furi/furi_test.c | 6 +++ targets/f7/inc/FreeRTOSConfig.h | 13 +++-- 3 files changed, 67 insertions(+), 3 deletions(-) create mode 100644 applications/debug/unit_tests/tests/furi/furi_errno_test.c diff --git a/applications/debug/unit_tests/tests/furi/furi_errno_test.c b/applications/debug/unit_tests/tests/furi/furi_errno_test.c new file mode 100644 index 000000000..b42e7c082 --- /dev/null +++ b/applications/debug/unit_tests/tests/furi/furi_errno_test.c @@ -0,0 +1,51 @@ +#include +#include +#include "../test.h" // IWYU pragma: keep + +#define TAG "ErrnoTest" +#define THREAD_CNT 16 +#define ITER_CNT 1000 + +static int32_t errno_fuzzer(void* context) { + int start_value = (int)context; + int32_t fails = 0; + + for(int i = start_value; i < start_value + ITER_CNT; i++) { + errno = i; + furi_thread_yield(); + if(errno != i) fails++; + } + + for(int i = 0; i < ITER_CNT; i++) { + errno = 0; + furi_thread_yield(); + UNUSED(strtol("123456", NULL, 10)); // -V530 + furi_thread_yield(); + if(errno != 0) fails++; + + errno = 0; + furi_thread_yield(); + UNUSED(strtol("123456123456123456123456123456123456123456123456", NULL, 10)); // -V530 + furi_thread_yield(); + if(errno != ERANGE) fails++; + } + + return fails; +} + +void test_errno_saving(void) { + FuriThread* threads[THREAD_CNT]; + + for(int i = 0; i < THREAD_CNT; i++) { + int start_value = i * ITER_CNT; + threads[i] = furi_thread_alloc_ex("ErrnoFuzzer", 1024, errno_fuzzer, (void*)start_value); + furi_thread_set_priority(threads[i], FuriThreadPriorityNormal); + furi_thread_start(threads[i]); + } + + for(int i = 0; i < THREAD_CNT; i++) { + furi_thread_join(threads[i]); + mu_assert_int_eq(0, furi_thread_get_return_code(threads[i])); + furi_thread_free(threads[i]); + } +} diff --git a/applications/debug/unit_tests/tests/furi/furi_test.c b/applications/debug/unit_tests/tests/furi/furi_test.c index be579d2b8..2a76d5184 100644 --- a/applications/debug/unit_tests/tests/furi/furi_test.c +++ b/applications/debug/unit_tests/tests/furi/furi_test.c @@ -8,6 +8,7 @@ void test_furi_concurrent_access(void); void test_furi_pubsub(void); void test_furi_memmgr(void); void test_furi_event_loop(void); +void test_errno_saving(void); static int foo = 0; @@ -42,6 +43,10 @@ MU_TEST(mu_test_furi_event_loop) { test_furi_event_loop(); } +MU_TEST(mu_test_errno_saving) { + test_errno_saving(); +} + MU_TEST_SUITE(test_suite) { MU_SUITE_CONFIGURE(&test_setup, &test_teardown); MU_RUN_TEST(test_check); @@ -51,6 +56,7 @@ MU_TEST_SUITE(test_suite) { MU_RUN_TEST(mu_test_furi_pubsub); MU_RUN_TEST(mu_test_furi_memmgr); MU_RUN_TEST(mu_test_furi_event_loop); + MU_RUN_TEST(mu_test_errno_saving); } int run_minunit_test_furi(void) { diff --git a/targets/f7/inc/FreeRTOSConfig.h b/targets/f7/inc/FreeRTOSConfig.h index 2948faef9..82cda2c6d 100644 --- a/targets/f7/inc/FreeRTOSConfig.h +++ b/targets/f7/inc/FreeRTOSConfig.h @@ -2,6 +2,7 @@ #if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__) #include +#include #pragma GCC diagnostic ignored "-Wredundant-decls" #endif @@ -26,6 +27,7 @@ #define configUSE_16_BIT_TICKS 0 #define configMAX_PRIORITIES (32) #define configMINIMAL_STACK_SIZE ((uint16_t)128) +#define configUSE_POSIX_ERRNO 1 /* Heap size determined automatically by linker */ // #define configTOTAL_HEAP_SIZE ((size_t)0) @@ -146,9 +148,14 @@ standard names. */ #define configOVERRIDE_DEFAULT_TICK_CONFIGURATION \ 1 /* required only for Keil but does not hurt otherwise */ -#define traceTASK_SWITCHED_IN() \ - extern void furi_hal_mpu_set_stack_protection(uint32_t* stack); \ - furi_hal_mpu_set_stack_protection((uint32_t*)pxCurrentTCB->pxStack) +#define traceTASK_SWITCHED_IN() \ + extern void furi_hal_mpu_set_stack_protection(uint32_t* stack); \ + furi_hal_mpu_set_stack_protection((uint32_t*)pxCurrentTCB->pxStack); \ + errno = pxCurrentTCB->iTaskErrno +// ^^^^^ acquire errno directly from TCB because FreeRTOS assigns its `FreeRTOS_errno' _after_ our hook is called + +// referencing `FreeRTOS_errno' here vvvvv because FreeRTOS calls our hook _before_ copying the value into the TCB, hence a manual write to the TCB would get overwritten +#define traceTASK_SWITCHED_OUT() FreeRTOS_errno = errno #define portCLEAN_UP_TCB(pxTCB) \ extern void furi_thread_cleanup_tcb_event(TaskHandle_t task); \