diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index dcf964add..675679080 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,69 +1,69 @@ # Who owns all the fish by default -* @skotopes @DrZlo13 @hedger @gsurkov +* @DrZlo13 @hedger @gsurkov # Apps -/applications/debug/bt_debug_app/ @skotopes @DrZlo13 @hedger @gsurkov @gornekich -/applications/debug/accessor/ @skotopes @DrZlo13 @hedger @gsurkov @nminaylov -/applications/debug/battery_test_app/ @skotopes @DrZlo13 @hedger @gsurkov @gornekich -/applications/debug/bt_debug_app/ @skotopes @DrZlo13 @hedger @gsurkov @gornekich -/applications/debug/file_browser_test/ @skotopes @DrZlo13 @hedger @gsurkov @nminaylov -/applications/debug/lfrfid_debug/ @skotopes @DrZlo13 @hedger @gsurkov @nminaylov -/applications/debug/text_box_test/ @skotopes @DrZlo13 @hedger @gsurkov @nminaylov -/applications/debug/uart_echo/ @skotopes @DrZlo13 @hedger @gsurkov @nminaylov -/applications/debug/usb_mouse/ @skotopes @DrZlo13 @hedger @gsurkov @nminaylov -/applications/debug/usb_test/ @skotopes @DrZlo13 @hedger @gsurkov @nminaylov +/applications/debug/bt_debug_app/ @DrZlo13 @hedger @gsurkov @gornekich +/applications/debug/accessor/ @DrZlo13 @hedger @gsurkov @nminaylov +/applications/debug/battery_test_app/ @DrZlo13 @hedger @gsurkov @gornekich +/applications/debug/bt_debug_app/ @DrZlo13 @hedger @gsurkov @gornekich +/applications/debug/file_browser_test/ @DrZlo13 @hedger @gsurkov @nminaylov +/applications/debug/lfrfid_debug/ @DrZlo13 @hedger @gsurkov @nminaylov +/applications/debug/text_box_test/ @DrZlo13 @hedger @gsurkov @nminaylov +/applications/debug/uart_echo/ @DrZlo13 @hedger @gsurkov @nminaylov +/applications/debug/usb_mouse/ @DrZlo13 @hedger @gsurkov @nminaylov +/applications/debug/usb_test/ @DrZlo13 @hedger @gsurkov @nminaylov -/applications/main/archive/ @skotopes @DrZlo13 @hedger @gsurkov @nminaylov -/applications/main/bad_usb/ @skotopes @DrZlo13 @hedger @gsurkov @nminaylov -/applications/main/gpio/ @skotopes @DrZlo13 @hedger @gsurkov @nminaylov -/applications/main/ibutton/ @skotopes @DrZlo13 @hedger @gsurkov -/applications/main/infrared/ @skotopes @DrZlo13 @hedger @gsurkov -/applications/main/nfc/ @skotopes @DrZlo13 @hedger @gsurkov @gornekich @Astrrra -/applications/main/subghz/ @skotopes @DrZlo13 @hedger @gsurkov @Skorpionm -/applications/main/u2f/ @skotopes @DrZlo13 @hedger @gsurkov @nminaylov +/applications/main/archive/ @DrZlo13 @hedger @gsurkov @nminaylov +/applications/main/bad_usb/ @DrZlo13 @hedger @gsurkov @nminaylov +/applications/main/gpio/ @DrZlo13 @hedger @gsurkov @nminaylov +/applications/main/ibutton/ @DrZlo13 @hedger @gsurkov +/applications/main/infrared/ @DrZlo13 @hedger @gsurkov +/applications/main/nfc/ @DrZlo13 @hedger @gsurkov @gornekich +/applications/main/subghz/ @DrZlo13 @hedger @gsurkov @Skorpionm +/applications/main/u2f/ @DrZlo13 @hedger @gsurkov @nminaylov -/applications/services/bt/ @skotopes @DrZlo13 @hedger @gsurkov @gornekich -/applications/services/cli/ @skotopes @DrZlo13 @hedger @gsurkov @nminaylov -/applications/services/crypto/ @skotopes @DrZlo13 @hedger @gsurkov @nminaylov -/applications/services/desktop/ @skotopes @DrZlo13 @hedger @gsurkov @nminaylov -/applications/services/dolphin/ @skotopes @DrZlo13 @hedger @gsurkov @nminaylov -/applications/services/power/ @skotopes @DrZlo13 @hedger @gsurkov @gornekich -/applications/services/rpc/ @skotopes @DrZlo13 @hedger @gsurkov @nminaylov +/applications/services/bt/ @DrZlo13 @hedger @gsurkov @gornekich +/applications/services/cli/ @DrZlo13 @hedger @gsurkov @nminaylov +/applications/services/crypto/ @DrZlo13 @hedger @gsurkov @nminaylov +/applications/services/desktop/ @DrZlo13 @hedger @gsurkov @nminaylov +/applications/services/dolphin/ @DrZlo13 @hedger @gsurkov @nminaylov +/applications/services/power/ @DrZlo13 @hedger @gsurkov @gornekich +/applications/services/rpc/ @DrZlo13 @hedger @gsurkov @nminaylov -/applications/services/bt_settings_app/ @skotopes @DrZlo13 @hedger @gsurkov @gornekich -/applications/services/desktop_settings/ @skotopes @DrZlo13 @hedger @gsurkov @nminaylov -/applications/services/dolphin_passport/ @skotopes @DrZlo13 @hedger @gsurkov @nminaylov -/applications/services/power_settings_app/ @skotopes @DrZlo13 @hedger @gsurkov @gornekich +/applications/services/bt_settings_app/ @DrZlo13 @hedger @gsurkov @gornekich +/applications/services/desktop_settings/ @DrZlo13 @hedger @gsurkov @nminaylov +/applications/services/dolphin_passport/ @DrZlo13 @hedger @gsurkov @nminaylov +/applications/services/power_settings_app/ @DrZlo13 @hedger @gsurkov @gornekich -/applications/system/storage_move_to_sd/ @skotopes @DrZlo13 @hedger @gsurkov @nminaylov -/applications/system/js_app/ @skotopes @DrZlo13 @hedger @gsurkov @nminaylov @portasynthinca3 +/applications/system/storage_move_to_sd/ @DrZlo13 @hedger @gsurkov @nminaylov +/applications/system/js_app/ @DrZlo13 @hedger @gsurkov @nminaylov @portasynthinca3 -/applications/debug/unit_tests/ @skotopes @DrZlo13 @hedger @gsurkov @nminaylov @gornekich @Astrrra @Skorpionm +/applications/debug/unit_tests/ @DrZlo13 @hedger @gsurkov @nminaylov @gornekich @Skorpionm -/applications/examples/example_thermo/ @skotopes @DrZlo13 @hedger @gsurkov +/applications/examples/example_thermo/ @DrZlo13 @hedger @gsurkov # Firmware targets -/targets/ @skotopes @DrZlo13 @hedger @gsurkov @nminaylov +/targets/ @DrZlo13 @hedger @gsurkov @nminaylov # Assets -/applications/main/infrared/resources/ @skotopes @DrZlo13 @hedger @gsurkov +/applications/main/infrared/resources/ @DrZlo13 @hedger @gsurkov # Documentation -/documentation/ @skotopes @DrZlo13 @hedger @gsurkov -/scripts/toolchain/ @skotopes @DrZlo13 @hedger @gsurkov +/documentation/ @DrZlo13 @hedger @gsurkov @portasynthinca3 +/scripts/toolchain/ @DrZlo13 @hedger @gsurkov # Lib -/lib/stm32wb_copro/ @skotopes @DrZlo13 @hedger @gsurkov @gornekich -/lib/digital_signal/ @skotopes @DrZlo13 @hedger @gsurkov @gornekich -/lib/infrared/ @skotopes @DrZlo13 @hedger @gsurkov -/lib/lfrfid/ @skotopes @DrZlo13 @hedger @gsurkov @nminaylov -/lib/libusb_stm32/ @skotopes @DrZlo13 @hedger @gsurkov @nminaylov -/lib/mbedtls/ @skotopes @DrZlo13 @hedger @gsurkov @nminaylov -/lib/mjs/ @skotopes @DrZlo13 @hedger @gsurkov @nminaylov @portasynthinca3 -/lib/nanopb/ @skotopes @DrZlo13 @hedger @gsurkov @nminaylov -/lib/nfc/ @skotopes @DrZlo13 @hedger @gsurkov @gornekich @Astrrra -/lib/one_wire/ @skotopes @DrZlo13 @hedger @gsurkov -/lib/subghz/ @skotopes @DrZlo13 @hedger @gsurkov @Skorpionm +/lib/stm32wb_copro/ @DrZlo13 @hedger @gsurkov @gornekich +/lib/digital_signal/ @DrZlo13 @hedger @gsurkov @gornekich +/lib/infrared/ @DrZlo13 @hedger @gsurkov +/lib/lfrfid/ @DrZlo13 @hedger @gsurkov @nminaylov +/lib/libusb_stm32/ @DrZlo13 @hedger @gsurkov @nminaylov +/lib/mbedtls/ @DrZlo13 @hedger @gsurkov @nminaylov +/lib/mjs/ @DrZlo13 @hedger @gsurkov @nminaylov @portasynthinca3 +/lib/nanopb/ @DrZlo13 @hedger @gsurkov @nminaylov +/lib/nfc/ @DrZlo13 @hedger @gsurkov @gornekich +/lib/one_wire/ @DrZlo13 @hedger @gsurkov +/lib/subghz/ @DrZlo13 @hedger @gsurkov @Skorpionm # CI/CD -/.github/workflows/ @skotopes @DrZlo13 @hedger @gsurkov +/.github/workflows/ @DrZlo13 @hedger @gsurkov diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index 37df8e099..d8d83abb8 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -21,7 +21,7 @@ jobs: - name: 'Flash unit tests firmware' id: flashing if: success() - timeout-minutes: 10 + timeout-minutes: 5 run: | source scripts/toolchain/fbtenv.sh ./fbt resources firmware_latest flash LIB_DEBUG=1 FIRMWARE_APP_SET=unit_tests FORCE=1 @@ -30,7 +30,7 @@ jobs: - name: 'Copy assets and unit data, reboot and wait for flipper' id: copy if: steps.flashing.outcome == 'success' - timeout-minutes: 7 + timeout-minutes: 5 run: | source scripts/toolchain/fbtenv.sh python3 scripts/testops.py -t=15 await_flipper @@ -42,7 +42,7 @@ jobs: - name: 'Run units and validate results' id: run_units if: steps.copy.outcome == 'success' - timeout-minutes: 7 + timeout-minutes: 5 run: | source scripts/toolchain/fbtenv.sh python3 scripts/testops.py run_units diff --git a/.github/workflows/updater_test.yml b/.github/workflows/updater_test.yml index df62daf58..b5265df9c 100644 --- a/.github/workflows/updater_test.yml +++ b/.github/workflows/updater_test.yml @@ -20,7 +20,7 @@ jobs: - name: 'Flashing target firmware' id: first_full_flash - timeout-minutes: 10 + timeout-minutes: 5 run: | source scripts/toolchain/fbtenv.sh python3 scripts/testops.py -t=180 await_flipper @@ -29,7 +29,7 @@ jobs: - name: 'Validating updater' id: second_full_flash - timeout-minutes: 10 + timeout-minutes: 5 if: success() run: | source scripts/toolchain/fbtenv.sh diff --git a/SConstruct b/SConstruct index 127967968..44aa3bc17 100644 --- a/SConstruct +++ b/SConstruct @@ -412,6 +412,21 @@ distenv.PhonyTarget( ], ) + +# Measure CLI loopback performance +distenv.PhonyTarget( + "cli_perf", + [ + [ + "${PYTHON3}", + "${FBT_SCRIPT_DIR}/serial_cli_perf.py", + "-p", + "${FLIP_PORT}", + "${ARGS}", + ] + ], +) + # Update WiFi devboard firmware with release channel distenv.PhonyTarget( "devboard_flash", diff --git a/applications/debug/accessor/application.fam b/applications/debug/accessor/application.fam index 65a6c8666..4b24f98eb 100644 --- a/applications/debug/accessor/application.fam +++ b/applications/debug/accessor/application.fam @@ -6,6 +6,5 @@ App( entry_point="accessor_app", requires=["gui"], stack_size=4 * 1024, - order=40, fap_category="Debug", ) diff --git a/applications/debug/battery_test_app/application.fam b/applications/debug/battery_test_app/application.fam index 5f4acd83d..0ab68c086 100644 --- a/applications/debug/battery_test_app/application.fam +++ b/applications/debug/battery_test_app/application.fam @@ -8,7 +8,6 @@ App( "power", ], stack_size=1 * 1024, - order=130, fap_category="Debug", fap_libs=["assets"], ) diff --git a/applications/debug/blink_test/application.fam b/applications/debug/blink_test/application.fam index d7d873fb9..066e7a207 100644 --- a/applications/debug/blink_test/application.fam +++ b/applications/debug/blink_test/application.fam @@ -5,6 +5,5 @@ App( entry_point="blink_test_app", requires=["gui"], stack_size=1 * 1024, - order=10, fap_category="Debug", ) diff --git a/applications/debug/bt_debug_app/application.fam b/applications/debug/bt_debug_app/application.fam index 8ed1ccc05..831b51ade 100644 --- a/applications/debug/bt_debug_app/application.fam +++ b/applications/debug/bt_debug_app/application.fam @@ -13,6 +13,5 @@ App( "bt_debug", ], stack_size=1 * 1024, - order=110, fap_category="Debug", ) diff --git a/applications/debug/ccid_test/application.fam b/applications/debug/ccid_test/application.fam index ad9076770..dfd6de05f 100644 --- a/applications/debug/ccid_test/application.fam +++ b/applications/debug/ccid_test/application.fam @@ -10,6 +10,5 @@ App( "ccid_test", ], stack_size=1 * 1024, - order=120, fap_category="Debug", ) diff --git a/applications/debug/direct_draw/application.fam b/applications/debug/direct_draw/application.fam index 11b3bc6ba..1e7d4b1c4 100644 --- a/applications/debug/direct_draw/application.fam +++ b/applications/debug/direct_draw/application.fam @@ -5,6 +5,5 @@ App( entry_point="direct_draw_app", requires=["gui", "input"], stack_size=2 * 1024, - order=70, fap_category="Debug", ) diff --git a/applications/debug/display_test/application.fam b/applications/debug/display_test/application.fam index 7b2357b01..1e0d3f775 100644 --- a/applications/debug/display_test/application.fam +++ b/applications/debug/display_test/application.fam @@ -6,6 +6,5 @@ App( requires=["gui"], fap_libs=["u8g2"], stack_size=1 * 1024, - order=120, fap_category="Debug", ) diff --git a/applications/debug/event_loop_blink_test/application.fam b/applications/debug/event_loop_blink_test/application.fam index 7d42ad339..6e4aaa48d 100644 --- a/applications/debug/event_loop_blink_test/application.fam +++ b/applications/debug/event_loop_blink_test/application.fam @@ -5,6 +5,5 @@ App( entry_point="event_loop_blink_test_app", requires=["input"], stack_size=1 * 1024, - order=20, fap_category="Debug", ) diff --git a/applications/debug/expansion_test/application.fam b/applications/debug/expansion_test/application.fam index 9bc4b2fc2..30f325a92 100644 --- a/applications/debug/expansion_test/application.fam +++ b/applications/debug/expansion_test/application.fam @@ -6,7 +6,6 @@ App( requires=["expansion_start"], fap_libs=["assets"], stack_size=1 * 1024, - order=20, fap_category="Debug", fap_file_assets="assets", ) diff --git a/applications/debug/file_browser_test/application.fam b/applications/debug/file_browser_test/application.fam index bb08ad2c5..b610558e9 100644 --- a/applications/debug/file_browser_test/application.fam +++ b/applications/debug/file_browser_test/application.fam @@ -5,7 +5,6 @@ App( entry_point="file_browser_app", requires=["gui"], stack_size=2 * 1024, - order=150, fap_category="Debug", fap_icon_assets="icons", ) diff --git a/applications/debug/keypad_test/application.fam b/applications/debug/keypad_test/application.fam index 90851950b..ed7408e71 100644 --- a/applications/debug/keypad_test/application.fam +++ b/applications/debug/keypad_test/application.fam @@ -5,6 +5,5 @@ App( entry_point="keypad_test_app", requires=["gui"], stack_size=1 * 1024, - order=30, fap_category="Debug", ) diff --git a/applications/debug/lfrfid_debug/application.fam b/applications/debug/lfrfid_debug/application.fam index 323f77818..d312dbda2 100644 --- a/applications/debug/lfrfid_debug/application.fam +++ b/applications/debug/lfrfid_debug/application.fam @@ -11,6 +11,5 @@ App( "lfrfid_debug", ], stack_size=1 * 1024, - order=100, fap_category="Debug", ) diff --git a/applications/debug/loader_chaining_a/application.fam b/applications/debug/loader_chaining_a/application.fam new file mode 100644 index 000000000..408efdcb1 --- /dev/null +++ b/applications/debug/loader_chaining_a/application.fam @@ -0,0 +1,8 @@ +App( + appid="loader_chaining_a", + name="Loader Chaining Test: App A", + apptype=FlipperAppType.DEBUG, + entry_point="chaining_test_app_a", + stack_size=1 * 1024, + fap_category="Debug", +) diff --git a/applications/debug/loader_chaining_a/loader_chaining_a.c b/applications/debug/loader_chaining_a/loader_chaining_a.c new file mode 100644 index 000000000..b3f303e2d --- /dev/null +++ b/applications/debug/loader_chaining_a/loader_chaining_a.c @@ -0,0 +1,164 @@ +#include +#include +#include +#include +#include +#include + +#define TAG "LoaderChainingA" +#define CHAINING_TEST_B "/ext/apps/Debug/loader_chaining_b.fap" +#define NONEXISTENT_APP "Some nonexistent app" + +typedef struct { + Gui* gui; + ViewDispatcher* view_dispatcher; + Submenu* submenu; + + Loader* loader; + + DialogsApp* dialogs; +} LoaderChainingA; + +typedef enum { + LoaderChainingASubmenuLaunchB, + LoaderChainingASubmenuLaunchBThenA, + LoaderChainingASubmenuLaunchNonexistentSilent, + LoaderChainingASubmenuLaunchNonexistentGui, + LoaderChainingASubmenuLaunchNonexistentGuiThenA, +} LoaderChainingASubmenu; + +static void loader_chaining_a_submenu_callback(void* context, uint32_t index) { + LoaderChainingA* app = context; + + FuriString* self_path = furi_string_alloc(); + furi_check(loader_get_application_launch_path(app->loader, self_path)); + + switch(index) { + case LoaderChainingASubmenuLaunchB: + loader_enqueue_launch(app->loader, CHAINING_TEST_B, "Hello", LoaderDeferredLaunchFlagGui); + view_dispatcher_stop(app->view_dispatcher); + break; + + case LoaderChainingASubmenuLaunchBThenA: + loader_enqueue_launch(app->loader, CHAINING_TEST_B, "Hello", LoaderDeferredLaunchFlagGui); + loader_enqueue_launch( + app->loader, + furi_string_get_cstr(self_path), + "Hello to you from the future", + LoaderDeferredLaunchFlagGui); + + break; + + case LoaderChainingASubmenuLaunchNonexistentSilent: + loader_enqueue_launch(app->loader, NONEXISTENT_APP, NULL, LoaderDeferredLaunchFlagNone); + break; + + case LoaderChainingASubmenuLaunchNonexistentGui: + loader_enqueue_launch(app->loader, NONEXISTENT_APP, NULL, LoaderDeferredLaunchFlagGui); + break; + + case LoaderChainingASubmenuLaunchNonexistentGuiThenA: + loader_enqueue_launch(app->loader, NONEXISTENT_APP, NULL, LoaderDeferredLaunchFlagGui); + loader_enqueue_launch( + app->loader, + furi_string_get_cstr(self_path), + "Hello to you from the future", + LoaderDeferredLaunchFlagGui); + break; + } + + furi_string_free(self_path); + view_dispatcher_stop(app->view_dispatcher); +} + +static bool loader_chaining_a_nav_callback(void* context) { + LoaderChainingA* app = context; + view_dispatcher_stop(app->view_dispatcher); + return true; +} + +LoaderChainingA* loader_chaining_a_alloc(void) { + LoaderChainingA* app = malloc(sizeof(LoaderChainingA)); + app->gui = furi_record_open(RECORD_GUI); + app->loader = furi_record_open(RECORD_LOADER); + app->dialogs = furi_record_open(RECORD_DIALOGS); + app->view_dispatcher = view_dispatcher_alloc(); + app->submenu = submenu_alloc(); + + submenu_add_item( + app->submenu, + "Launch B", + LoaderChainingASubmenuLaunchB, + loader_chaining_a_submenu_callback, + app); + submenu_add_item( + app->submenu, + "Launch B, then A", + LoaderChainingASubmenuLaunchBThenA, + loader_chaining_a_submenu_callback, + app); + submenu_add_item( + app->submenu, + "Trigger error: silent", + LoaderChainingASubmenuLaunchNonexistentSilent, + loader_chaining_a_submenu_callback, + app); + submenu_add_item( + app->submenu, + "Trigger error: GUI", + LoaderChainingASubmenuLaunchNonexistentGui, + loader_chaining_a_submenu_callback, + app); + submenu_add_item( + app->submenu, + "Error, then launch A", + LoaderChainingASubmenuLaunchNonexistentGuiThenA, + loader_chaining_a_submenu_callback, + app); + + view_dispatcher_add_view(app->view_dispatcher, 0, submenu_get_view(app->submenu)); + view_dispatcher_set_navigation_event_callback( + app->view_dispatcher, loader_chaining_a_nav_callback); + view_dispatcher_set_event_callback_context(app->view_dispatcher, app); + + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + view_dispatcher_switch_to_view(app->view_dispatcher, 0); + + return app; +} + +void loader_chaining_a_free(LoaderChainingA* app) { + furi_record_close(RECORD_DIALOGS); + furi_record_close(RECORD_LOADER); + furi_record_close(RECORD_GUI); + view_dispatcher_remove_view(app->view_dispatcher, 0); + submenu_free(app->submenu); + view_dispatcher_free(app->view_dispatcher); + free(app); +} + +int32_t chaining_test_app_a(const char* arg) { + LoaderChainingA* app = loader_chaining_a_alloc(); + + if(arg) { + if(strlen(arg)) { + DialogMessage* message = dialog_message_alloc(); + FuriString* text; + + dialog_message_set_header(message, "Hi, I am A", 64, 0, AlignCenter, AlignTop); + text = furi_string_alloc_printf("Me from the past says:\n%s", arg); + dialog_message_set_buttons(message, NULL, "ok!", NULL); + + dialog_message_set_text( + message, furi_string_get_cstr(text), 64, 32, AlignCenter, AlignCenter); + dialog_message_show(app->dialogs, message); + dialog_message_free(message); + furi_string_free(text); + } + } + + view_dispatcher_run(app->view_dispatcher); + + loader_chaining_a_free(app); + return 0; +} diff --git a/applications/debug/loader_chaining_b/application.fam b/applications/debug/loader_chaining_b/application.fam new file mode 100644 index 000000000..5b8767e50 --- /dev/null +++ b/applications/debug/loader_chaining_b/application.fam @@ -0,0 +1,8 @@ +App( + appid="loader_chaining_b", + name="Loader Chaining Test: App B", + apptype=FlipperAppType.DEBUG, + entry_point="chaining_test_app_b", + stack_size=1 * 1024, + fap_category="Debug", +) diff --git a/applications/debug/loader_chaining_b/loader_chaining_b.c b/applications/debug/loader_chaining_b/loader_chaining_b.c new file mode 100644 index 000000000..439e6e25e --- /dev/null +++ b/applications/debug/loader_chaining_b/loader_chaining_b.c @@ -0,0 +1,27 @@ +#include +#include +#include + +int32_t chaining_test_app_b(const char* arg) { + if(!arg) return 0; + + Loader* loader = furi_record_open(RECORD_LOADER); + DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); + + DialogMessage* message = dialog_message_alloc(); + dialog_message_set_header(message, "Hi, I am B", 64, 0, AlignCenter, AlignTop); + FuriString* text = furi_string_alloc_printf("And A told me:\n%s", arg); + dialog_message_set_text(message, furi_string_get_cstr(text), 64, 32, AlignCenter, AlignCenter); + dialog_message_set_buttons(message, "Just quit", NULL, "Launch A"); + DialogMessageButton result = dialog_message_show(dialogs, message); + dialog_message_free(message); + furi_string_free(text); + + if(result == DialogMessageButtonRight) + loader_enqueue_launch( + loader, "/ext/apps/Debug/loader_chaining_a.fap", NULL, LoaderDeferredLaunchFlagGui); + + furi_record_close(RECORD_LOADER); + furi_record_close(RECORD_DIALOGS); + return 0; +} diff --git a/applications/debug/locale_test/application.fam b/applications/debug/locale_test/application.fam index d341122f9..757be7155 100644 --- a/applications/debug/locale_test/application.fam +++ b/applications/debug/locale_test/application.fam @@ -5,6 +5,5 @@ App( entry_point="locale_test_app", requires=["gui", "locale"], stack_size=2 * 1024, - order=70, fap_category="Debug", ) diff --git a/applications/debug/rpc_debug_app/application.fam b/applications/debug/rpc_debug_app/application.fam index d71065afa..795f83287 100644 --- a/applications/debug/rpc_debug_app/application.fam +++ b/applications/debug/rpc_debug_app/application.fam @@ -5,6 +5,5 @@ App( entry_point="rpc_debug_app", requires=["gui", "rpc_start", "notification"], stack_size=2 * 1024, - order=10, fap_category="Debug", ) diff --git a/applications/debug/speaker_debug/application.fam b/applications/debug/speaker_debug/application.fam index 68d8b188b..c7f5629a7 100644 --- a/applications/debug/speaker_debug/application.fam +++ b/applications/debug/speaker_debug/application.fam @@ -5,7 +5,6 @@ App( entry_point="speaker_debug_app", requires=["gui", "notification"], stack_size=2 * 1024, - order=10, fap_category="Debug", fap_libs=["music_worker"], ) diff --git a/applications/debug/speaker_debug/speaker_debug.c b/applications/debug/speaker_debug/speaker_debug.c index 3f685ab30..6a9956b07 100644 --- a/applications/debug/speaker_debug/speaker_debug.c +++ b/applications/debug/speaker_debug/speaker_debug.c @@ -1,8 +1,10 @@ #include #include #include -#include #include +#include +#include +#include #define TAG "SpeakerDebug" @@ -19,14 +21,14 @@ typedef struct { typedef struct { MusicWorker* music_worker; FuriMessageQueue* message_queue; - Cli* cli; + CliRegistry* cli_registry; } SpeakerDebugApp; static SpeakerDebugApp* speaker_app_alloc(void) { SpeakerDebugApp* app = (SpeakerDebugApp*)malloc(sizeof(SpeakerDebugApp)); app->music_worker = music_worker_alloc(); app->message_queue = furi_message_queue_alloc(8, sizeof(SpeakerDebugAppMessage)); - app->cli = furi_record_open(RECORD_CLI); + app->cli_registry = furi_record_open(RECORD_CLI); return app; } @@ -37,8 +39,8 @@ static void speaker_app_free(SpeakerDebugApp* app) { free(app); } -static void speaker_app_cli(Cli* cli, FuriString* args, void* context) { - UNUSED(cli); +static void speaker_app_cli(PipeSide* pipe, FuriString* args, void* context) { + UNUSED(pipe); SpeakerDebugApp* app = (SpeakerDebugApp*)context; SpeakerDebugAppMessage message; @@ -95,7 +97,8 @@ static void speaker_app_run(SpeakerDebugApp* app, const char* arg) { return; } - cli_add_command(app->cli, CLI_COMMAND, CliCommandFlagParallelSafe, speaker_app_cli, app); + cli_registry_add_command( + app->cli_registry, CLI_COMMAND, CliCommandFlagParallelSafe, speaker_app_cli, app); SpeakerDebugAppMessage message; FuriStatus status; @@ -110,7 +113,7 @@ static void speaker_app_run(SpeakerDebugApp* app, const char* arg) { } } - cli_delete_command(app->cli, CLI_COMMAND); + cli_registry_delete_command(app->cli_registry, CLI_COMMAND); } int32_t speaker_debug_app(void* arg) { diff --git a/applications/debug/subghz_test/application.fam b/applications/debug/subghz_test/application.fam index 1b3e19d73..927ca7f89 100644 --- a/applications/debug/subghz_test/application.fam +++ b/applications/debug/subghz_test/application.fam @@ -6,7 +6,6 @@ App( entry_point="subghz_test_app", requires=["gui"], stack_size=4 * 1024, - order=50, fap_icon="subghz_test_10px.png", fap_category="Debug", fap_icon_assets="images", diff --git a/applications/debug/text_box_element_test/application.fam b/applications/debug/text_box_element_test/application.fam index 5e1abcddc..78dfe75f6 100644 --- a/applications/debug/text_box_element_test/application.fam +++ b/applications/debug/text_box_element_test/application.fam @@ -5,6 +5,5 @@ App( entry_point="text_box_element_test_app", requires=["gui"], stack_size=1 * 1024, - order=140, fap_category="Debug", ) diff --git a/applications/debug/text_box_view_test/application.fam b/applications/debug/text_box_view_test/application.fam index e356a278e..6a3225d88 100644 --- a/applications/debug/text_box_view_test/application.fam +++ b/applications/debug/text_box_view_test/application.fam @@ -5,6 +5,5 @@ App( entry_point="text_box_view_test_app", requires=["gui"], stack_size=1 * 1024, - order=140, fap_category="Debug", ) diff --git a/applications/debug/uart_echo/application.fam b/applications/debug/uart_echo/application.fam index 7b030bcfa..d95302364 100644 --- a/applications/debug/uart_echo/application.fam +++ b/applications/debug/uart_echo/application.fam @@ -5,6 +5,5 @@ App( entry_point="uart_echo_app", requires=["gui"], stack_size=2 * 1024, - order=70, fap_category="Debug", ) diff --git a/applications/debug/unit_tests/application.fam b/applications/debug/unit_tests/application.fam index f92d7e66f..72b8cafcb 100644 --- a/applications/debug/unit_tests/application.fam +++ b/applications/debug/unit_tests/application.fam @@ -4,10 +4,10 @@ App( entry_point="unit_tests_on_system_start", sources=["unit_tests.c", "test_runner.c", "unit_test_api_table.cpp"], cdefines=["APP_UNIT_TESTS"], - requires=["system_settings", "subghz_start"], + requires=["system_settings", "cli_subghz"], provides=["delay_test"], resources="resources", - order=100, + order=30, ) App( @@ -18,7 +18,7 @@ App( entry_point="delay_test_app", stack_size=1 * 1024, requires=["unit_tests"], - order=110, + order=30, ) App( diff --git a/applications/debug/unit_tests/test_runner.c b/applications/debug/unit_tests/test_runner.c index de29e91b3..ad7d31b02 100644 --- a/applications/debug/unit_tests/test_runner.c +++ b/applications/debug/unit_tests/test_runner.c @@ -2,8 +2,9 @@ #include "tests/test_api.h" -#include +#include #include +#include #include #include #include @@ -25,7 +26,7 @@ struct TestRunner { NotificationApp* notification; // Temporary used things - Cli* cli; + PipeSide* pipe; FuriString* args; // ELF related stuff @@ -38,14 +39,14 @@ struct TestRunner { int minunit_status; }; -TestRunner* test_runner_alloc(Cli* cli, FuriString* args) { +TestRunner* test_runner_alloc(PipeSide* pipe, FuriString* args) { TestRunner* instance = malloc(sizeof(TestRunner)); instance->storage = furi_record_open(RECORD_STORAGE); instance->loader = furi_record_open(RECORD_LOADER); instance->notification = furi_record_open(RECORD_NOTIFICATION); - instance->cli = cli; + instance->pipe = pipe; instance->args = args; instance->composite_resolver = composite_api_resolver_alloc(); @@ -147,7 +148,7 @@ static void test_runner_run_internal(TestRunner* instance) { } while(true) { - if(cli_cmd_interrupt_received(instance->cli)) { + if(cli_is_pipe_broken_or_is_etx_next_char(instance->pipe)) { break; } diff --git a/applications/debug/unit_tests/test_runner.h b/applications/debug/unit_tests/test_runner.h index 43aba8bb1..0e9495263 100644 --- a/applications/debug/unit_tests/test_runner.h +++ b/applications/debug/unit_tests/test_runner.h @@ -1,12 +1,12 @@ #pragma once #include +#include typedef struct TestRunner TestRunner; -typedef struct Cli Cli; -TestRunner* test_runner_alloc(Cli* cli, FuriString* args); +TestRunner* test_runner_alloc(PipeSide* pipe, FuriString* args); -void test_runner_free(TestRunner* isntance); +void test_runner_free(TestRunner* instance); -void test_runner_run(TestRunner* isntance); +void test_runner_run(TestRunner* instance); diff --git a/applications/debug/unit_tests/tests/js/js_test.c b/applications/debug/unit_tests/tests/js/js_test.c index af590e899..dd695a3a1 100644 --- a/applications/debug/unit_tests/tests/js/js_test.c +++ b/applications/debug/unit_tests/tests/js/js_test.c @@ -6,9 +6,12 @@ #include #include +#include #include +#define TAG "JsUnitTests" + #define JS_SCRIPT_PATH(name) EXT_PATH("unit_tests/js/" name ".js") typedef enum { @@ -73,7 +76,311 @@ MU_TEST(js_test_storage) { js_test_run(JS_SCRIPT_PATH("storage")); } +static void js_value_test_compatibility_matrix(struct mjs* mjs) { + static const JsValueType types[] = { + JsValueTypeAny, + JsValueTypeAnyArray, + JsValueTypeAnyObject, + JsValueTypeFunction, + JsValueTypeRawPointer, + JsValueTypeInt32, + JsValueTypeDouble, + JsValueTypeString, + JsValueTypeBool, + }; + + mjs_val_t values[] = { + mjs_mk_undefined(), + mjs_mk_foreign(mjs, (void*)0xDEADBEEF), + mjs_mk_array(mjs), + mjs_mk_object(mjs), + mjs_mk_number(mjs, 123.456), + mjs_mk_string(mjs, "test", ~0, false), + mjs_mk_boolean(mjs, true), + }; + +// for proper matrix formatting and better readability +#define YES true +#define NO_ false + static const bool success_matrix[COUNT_OF(types)][COUNT_OF(values)] = { + // types: + {YES, YES, YES, YES, YES, YES, YES}, // any + {NO_, NO_, YES, NO_, NO_, NO_, NO_}, // array + {NO_, NO_, YES, YES, NO_, NO_, NO_}, // obj + {NO_, NO_, NO_, NO_, NO_, NO_, NO_}, // fn + {NO_, YES, NO_, NO_, NO_, NO_, NO_}, // ptr + {NO_, NO_, NO_, NO_, YES, NO_, NO_}, // int32 + {NO_, NO_, NO_, NO_, YES, NO_, NO_}, // double + {NO_, NO_, NO_, NO_, NO_, YES, NO_}, // str + {NO_, NO_, NO_, NO_, NO_, NO_, YES}, // bool + // + //und ptr arr obj num str bool <- values + }; +#undef NO_ +#undef YES + + for(size_t i = 0; i < COUNT_OF(types); i++) { + for(size_t j = 0; j < COUNT_OF(values); j++) { + const JsValueDeclaration declaration = { + .type = types[i], + .n_children = 0, + }; + // we only care about the status, not the result. double has the largest size out of + // all the results + uint8_t result[sizeof(double)]; + JsValueParseStatus status; + JS_VALUE_PARSE( + mjs, + JS_VALUE_PARSE_SOURCE_VALUE(&declaration), + JsValueParseFlagNone, + &status, + &values[j], + result); + if((status == JsValueParseStatusOk) != success_matrix[i][j]) { + FURI_LOG_E(TAG, "type %zu, value %zu", i, j); + mu_fail("see serial logs"); + } + } + } +} + +static void js_value_test_literal(struct mjs* mjs) { + static const JsValueType types[] = { + JsValueTypeAny, + JsValueTypeAnyArray, + JsValueTypeAnyObject, + }; + + mjs_val_t values[] = { + mjs_mk_undefined(), + mjs_mk_array(mjs), + mjs_mk_object(mjs), + }; + + mu_assert_int_eq(COUNT_OF(types), COUNT_OF(values)); + for(size_t i = 0; i < COUNT_OF(types); i++) { + const JsValueDeclaration declaration = { + .type = types[i], + .n_children = 0, + }; + mjs_val_t result; + JsValueParseStatus status; + JS_VALUE_PARSE( + mjs, + JS_VALUE_PARSE_SOURCE_VALUE(&declaration), + JsValueParseFlagNone, + &status, + &values[i], + &result); + mu_assert_int_eq(JsValueParseStatusOk, status); + mu_assert(result == values[i], "wrong result"); + } +} + +static void js_value_test_primitive( + struct mjs* mjs, + JsValueType type, + const void* c_value, + size_t c_value_size, + mjs_val_t js_val) { + const JsValueDeclaration declaration = { + .type = type, + .n_children = 0, + }; + uint8_t result[c_value_size]; + JsValueParseStatus status; + JS_VALUE_PARSE( + mjs, + JS_VALUE_PARSE_SOURCE_VALUE(&declaration), + JsValueParseFlagNone, + &status, + &js_val, + result); + mu_assert_int_eq(JsValueParseStatusOk, status); + if(type == JsValueTypeString) { + const char* result_str = *(const char**)&result; + mu_assert_string_eq(c_value, result_str); + } else { + mu_assert_mem_eq(c_value, result, c_value_size); + } +} + +static void js_value_test_primitives(struct mjs* mjs) { + int32_t i32 = 123; + js_value_test_primitive(mjs, JsValueTypeInt32, &i32, sizeof(i32), mjs_mk_number(mjs, i32)); + + double dbl = 123.456; + js_value_test_primitive(mjs, JsValueTypeDouble, &dbl, sizeof(dbl), mjs_mk_number(mjs, dbl)); + + const char* str = "test"; + js_value_test_primitive( + mjs, JsValueTypeString, str, strlen(str) + 1, mjs_mk_string(mjs, str, ~0, false)); + + bool boolean = true; + js_value_test_primitive( + mjs, JsValueTypeBool, &boolean, sizeof(boolean), mjs_mk_boolean(mjs, boolean)); +} + +static uint32_t + js_value_test_enum(struct mjs* mjs, const JsValueDeclaration* decl, const char* value) { + mjs_val_t str = mjs_mk_string(mjs, value, ~0, false); + uint32_t result; + JsValueParseStatus status; + JS_VALUE_PARSE( + mjs, JS_VALUE_PARSE_SOURCE_VALUE(decl), JsValueParseFlagNone, &status, &str, &result); + if(status != JsValueParseStatusOk) return 0; + return result; +} + +static void js_value_test_enums(struct mjs* mjs) { + static const JsValueEnumVariant enum_1_variants[] = { + {"variant 1", 1}, + {"variant 2", 2}, + {"variant 3", 3}, + }; + static const JsValueDeclaration enum_1 = JS_VALUE_ENUM(uint32_t, enum_1_variants); + + static const JsValueEnumVariant enum_2_variants[] = { + {"read", 4}, + {"write", 8}, + }; + static const JsValueDeclaration enum_2 = JS_VALUE_ENUM(uint32_t, enum_2_variants); + + mu_assert_int_eq(1, js_value_test_enum(mjs, &enum_1, "variant 1")); + mu_assert_int_eq(2, js_value_test_enum(mjs, &enum_1, "variant 2")); + mu_assert_int_eq(3, js_value_test_enum(mjs, &enum_1, "variant 3")); + mu_assert_int_eq(0, js_value_test_enum(mjs, &enum_1, "not a thing")); + + mu_assert_int_eq(0, js_value_test_enum(mjs, &enum_2, "variant 1")); + mu_assert_int_eq(0, js_value_test_enum(mjs, &enum_2, "variant 2")); + mu_assert_int_eq(0, js_value_test_enum(mjs, &enum_2, "variant 3")); + mu_assert_int_eq(0, js_value_test_enum(mjs, &enum_2, "not a thing")); + mu_assert_int_eq(4, js_value_test_enum(mjs, &enum_2, "read")); + mu_assert_int_eq(8, js_value_test_enum(mjs, &enum_2, "write")); +} + +static void js_value_test_object(struct mjs* mjs) { + static const JsValueDeclaration int_decl = JS_VALUE_SIMPLE(JsValueTypeInt32); + + static const JsValueDeclaration str_decl = JS_VALUE_SIMPLE(JsValueTypeString); + + static const JsValueEnumVariant enum_variants[] = { + {"variant 1", 1}, + {"variant 2", 2}, + {"variant 3", 3}, + }; + static const JsValueDeclaration enum_decl = JS_VALUE_ENUM(uint32_t, enum_variants); + + static const JsValueObjectField fields[] = { + {"int", &int_decl}, + {"str", &str_decl}, + {"enum", &enum_decl}, + }; + static const JsValueDeclaration object_decl = JS_VALUE_OBJECT(fields); + + mjs_val_t object = mjs_mk_object(mjs); + JS_ASSIGN_MULTI(mjs, object) { + JS_FIELD("str", mjs_mk_string(mjs, "Helloooo!", ~0, false)); + JS_FIELD("int", mjs_mk_number(mjs, 123)); + JS_FIELD("enum", mjs_mk_string(mjs, "variant 2", ~0, false)); + } + + const char* result_str; + int32_t result_int; + uint32_t result_enum; + JsValueParseStatus status; + JS_VALUE_PARSE( + mjs, + JS_VALUE_PARSE_SOURCE_VALUE(&object_decl), + JsValueParseFlagNone, + &status, + &object, + &result_int, + &result_str, + &result_enum); + mu_assert_int_eq(JsValueParseStatusOk, status); + mu_assert_string_eq("Helloooo!", result_str); + mu_assert_int_eq(123, result_int); + mu_assert_int_eq(2, result_enum); +} + +static void js_value_test_default(struct mjs* mjs) { + static const JsValueDeclaration int_decl = + JS_VALUE_SIMPLE_W_DEFAULT(JsValueTypeInt32, int32_val, 123); + static const JsValueDeclaration str_decl = JS_VALUE_SIMPLE(JsValueTypeString); + + static const JsValueObjectField fields[] = { + {"int", &int_decl}, + {"str", &str_decl}, + }; + static const JsValueDeclaration object_decl = JS_VALUE_OBJECT(fields); + + mjs_val_t object = mjs_mk_object(mjs); + JS_ASSIGN_MULTI(mjs, object) { + JS_FIELD("str", mjs_mk_string(mjs, "Helloooo!", ~0, false)); + JS_FIELD("int", mjs_mk_undefined()); + } + + const char* result_str; + int32_t result_int; + JsValueParseStatus status; + JS_VALUE_PARSE( + mjs, + JS_VALUE_PARSE_SOURCE_VALUE(&object_decl), + JsValueParseFlagNone, + &status, + &object, + &result_int, + &result_str); + mu_assert_string_eq("Helloooo!", result_str); + mu_assert_int_eq(123, result_int); +} + +static void js_value_test_args_fn(struct mjs* mjs) { + static const JsValueDeclaration arg_list[] = { + JS_VALUE_SIMPLE(JsValueTypeInt32), + JS_VALUE_SIMPLE(JsValueTypeInt32), + JS_VALUE_SIMPLE(JsValueTypeInt32), + }; + static const JsValueArguments args = JS_VALUE_ARGS(arg_list); + + int32_t a, b, c; + JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &args, &a, &b, &c); + + mu_assert_int_eq(123, a); + mu_assert_int_eq(456, b); + mu_assert_int_eq(-420, c); +} + +static void js_value_test_args(struct mjs* mjs) { + mjs_val_t function = MJS_MK_FN(js_value_test_args_fn); + + mjs_val_t result; + mjs_val_t args[] = { + mjs_mk_number(mjs, 123), + mjs_mk_number(mjs, 456), + mjs_mk_number(mjs, -420), + }; + mu_assert_int_eq( + MJS_OK, mjs_apply(mjs, &result, function, MJS_UNDEFINED, COUNT_OF(args), args)); +} + +MU_TEST(js_value_test) { + struct mjs* mjs = mjs_create(NULL); + + js_value_test_compatibility_matrix(mjs); + js_value_test_literal(mjs); + js_value_test_primitives(mjs); + js_value_test_enums(mjs); + js_value_test_object(mjs); + js_value_test_default(mjs); + js_value_test_args(mjs); + + mjs_destroy(mjs); +} + MU_TEST_SUITE(test_js) { + MU_RUN_TEST(js_value_test); MU_RUN_TEST(js_test_basic); MU_RUN_TEST(js_test_math); MU_RUN_TEST(js_test_event_loop); diff --git a/applications/debug/unit_tests/tests/minunit.h b/applications/debug/unit_tests/tests/minunit.h index 9ca3bb403..c854c4673 100644 --- a/applications/debug/unit_tests/tests/minunit.h +++ b/applications/debug/unit_tests/tests/minunit.h @@ -396,6 +396,8 @@ void minunit_printf_warning(const char* format, ...); return; \ } else { minunit_print_progress(); }) +//-V:mu_assert_string_eq:526, 547 + #define mu_assert_string_eq(expected, result) \ MU__SAFE_BLOCK( \ const char* minunit_tmp_e = expected; const char* minunit_tmp_r = result; \ @@ -416,6 +418,8 @@ void minunit_printf_warning(const char* format, ...); return; \ } else { minunit_print_progress(); }) +//-V:mu_assert_mem_eq:526 + #define mu_assert_mem_eq(expected, result, size) \ MU__SAFE_BLOCK( \ const void* minunit_tmp_e = expected; const void* minunit_tmp_r = result; \ diff --git a/applications/debug/unit_tests/tests/pipe/pipe_test.c b/applications/debug/unit_tests/tests/pipe/pipe_test.c index d440a04ee..f0227b353 100644 --- a/applications/debug/unit_tests/tests/pipe/pipe_test.c +++ b/applications/debug/unit_tests/tests/pipe/pipe_test.c @@ -25,16 +25,13 @@ MU_TEST(pipe_test_trivial) { mu_assert_int_eq(PIPE_SIZE - i, pipe_spaces_available(alice)); mu_assert_int_eq(i, pipe_bytes_available(bob)); - if(pipe_send(alice, &i, sizeof(uint8_t), 0) != sizeof(uint8_t)) { - break; - } + if(pipe_spaces_available(alice) == 0) break; + furi_check(pipe_send(alice, &i, sizeof(uint8_t)) == sizeof(uint8_t)); mu_assert_int_eq(PIPE_SIZE - i, pipe_spaces_available(bob)); mu_assert_int_eq(i, pipe_bytes_available(alice)); - if(pipe_send(bob, &i, sizeof(uint8_t), 0) != sizeof(uint8_t)) { - break; - } + furi_check(pipe_send(bob, &i, sizeof(uint8_t)) == sizeof(uint8_t)); } pipe_free(alice); @@ -43,10 +40,9 @@ MU_TEST(pipe_test_trivial) { for(uint8_t i = 0;; ++i) { mu_assert_int_eq(PIPE_SIZE - i, pipe_bytes_available(bob)); + if(pipe_bytes_available(bob) == 0) break; uint8_t value; - if(pipe_receive(bob, &value, sizeof(uint8_t), 0) != sizeof(uint8_t)) { - break; - } + furi_check(pipe_receive(bob, &value, sizeof(uint8_t)) == sizeof(uint8_t)); mu_assert_int_eq(i, value); } @@ -68,16 +64,15 @@ typedef struct { static void on_data_arrived(PipeSide* pipe, void* context) { AncillaryThreadContext* ctx = context; ctx->flag |= TestFlagDataArrived; - uint8_t buffer[PIPE_SIZE]; - size_t size = pipe_receive(pipe, buffer, sizeof(buffer), 0); - pipe_send(pipe, buffer, size, 0); + uint8_t input; + size_t size = pipe_receive(pipe, &input, sizeof(input)); + pipe_send(pipe, &input, size); } static void on_space_freed(PipeSide* pipe, void* context) { + UNUSED(pipe); AncillaryThreadContext* ctx = context; ctx->flag |= TestFlagSpaceFreed; - const char* message = "Hi!"; - pipe_send(pipe, message, strlen(message), 0); } static void on_became_broken(PipeSide* pipe, void* context) { @@ -117,22 +112,16 @@ MU_TEST(pipe_test_event_loop) { furi_thread_start(thread); const char* message = "Hello!"; - pipe_send(alice, message, strlen(message), FuriWaitForever); + pipe_send(alice, message, strlen(message)); char buffer_1[16]; - size_t size = pipe_receive(alice, buffer_1, sizeof(buffer_1), FuriWaitForever); + size_t size = pipe_receive(alice, buffer_1, strlen(message)); buffer_1[size] = 0; - char buffer_2[16]; - const char* expected_reply = "Hi!"; - size = pipe_receive(alice, buffer_2, sizeof(buffer_2), FuriWaitForever); - buffer_2[size] = 0; - pipe_free(alice); furi_thread_join(thread); mu_assert_string_eq(message, buffer_1); - mu_assert_string_eq(expected_reply, buffer_2); mu_assert_int_eq( TestFlagDataArrived | TestFlagSpaceFreed | TestFlagBecameBroken, furi_thread_get_return_code(thread)); diff --git a/applications/debug/unit_tests/tests/rpc/rpc_test.c b/applications/debug/unit_tests/tests/rpc/rpc_test.c index 5d26bdb30..82ab872ce 100644 --- a/applications/debug/unit_tests/tests/rpc/rpc_test.c +++ b/applications/debug/unit_tests/tests/rpc/rpc_test.c @@ -3,7 +3,6 @@ #include #include -#include #include #include #include diff --git a/applications/debug/unit_tests/tests/storage/storage_test.c b/applications/debug/unit_tests/tests/storage/storage_test.c index 75c52ef9a..9d5c68a44 100644 --- a/applications/debug/unit_tests/tests/storage/storage_test.c +++ b/applications/debug/unit_tests/tests/storage/storage_test.c @@ -521,11 +521,6 @@ MU_TEST(test_storage_data_path) { // check that appsdata folder exists mu_check(storage_dir_exists(storage, APPS_DATA_PATH)); - // check that cli folder exists - mu_check(storage_dir_exists(storage, APPSDATA_APP_PATH("cli"))); - - storage_simply_remove(storage, APPSDATA_APP_PATH("cli")); - furi_record_close(RECORD_STORAGE); } diff --git a/applications/debug/unit_tests/unit_test_api_table_i.h b/applications/debug/unit_tests/unit_test_api_table_i.h index 10b089022..4f0e4dec9 100644 --- a/applications/debug/unit_tests/unit_test_api_table_i.h +++ b/applications/debug/unit_tests/unit_test_api_table_i.h @@ -8,6 +8,7 @@ #include #include #include +#include static constexpr auto unit_tests_api_table = sort(create_array_t( API_METHOD(resource_manifest_reader_alloc, ResourceManifestReader*, (Storage*)), @@ -38,4 +39,16 @@ static constexpr auto unit_tests_api_table = sort(create_array_t( JsThread*, (const char* script_path, JsThreadCallback callback, void* context)), API_METHOD(js_thread_stop, void, (JsThread * worker)), + API_METHOD(js_value_buffer_size, size_t, (const JsValueParseDeclaration declaration)), + API_METHOD( + js_value_parse, + JsValueParseStatus, + (struct mjs * mjs, + const JsValueParseDeclaration declaration, + JsValueParseFlag flags, + mjs_val_t* buffer, + size_t buf_size, + mjs_val_t* source, + size_t n_c_vals, + ...)), API_VARIABLE(PB_Main_msg, PB_Main_msg_t))); diff --git a/applications/debug/unit_tests/unit_tests.c b/applications/debug/unit_tests/unit_tests.c index 237cb9080..8605cb781 100644 --- a/applications/debug/unit_tests/unit_tests.c +++ b/applications/debug/unit_tests/unit_tests.c @@ -1,21 +1,24 @@ #include -#include +#include +#include +#include +#include #include "test_runner.h" -void unit_tests_cli(Cli* cli, FuriString* args, void* context) { - UNUSED(cli); +void unit_tests_cli(PipeSide* pipe, FuriString* args, void* context) { UNUSED(context); - TestRunner* test_runner = test_runner_alloc(cli, args); + TestRunner* test_runner = test_runner_alloc(pipe, args); test_runner_run(test_runner); test_runner_free(test_runner); } void unit_tests_on_system_start(void) { #ifdef SRV_CLI - Cli* cli = furi_record_open(RECORD_CLI); - cli_add_command(cli, "unit_tests", CliCommandFlagParallelSafe, unit_tests_cli, NULL); + CliRegistry* registry = furi_record_open(RECORD_CLI); + cli_registry_add_command( + registry, "unit_tests", CliCommandFlagParallelSafe, unit_tests_cli, NULL); furi_record_close(RECORD_CLI); #endif } diff --git a/applications/debug/usb_mouse/application.fam b/applications/debug/usb_mouse/application.fam index 7747613d5..e57b3f108 100644 --- a/applications/debug/usb_mouse/application.fam +++ b/applications/debug/usb_mouse/application.fam @@ -5,6 +5,5 @@ App( entry_point="usb_mouse_app", requires=["gui"], stack_size=1 * 1024, - order=60, fap_category="Debug", ) diff --git a/applications/debug/usb_test/application.fam b/applications/debug/usb_test/application.fam index 463bb4a26..6481518b4 100644 --- a/applications/debug/usb_test/application.fam +++ b/applications/debug/usb_test/application.fam @@ -5,6 +5,5 @@ App( entry_point="usb_test_app", requires=["gui"], stack_size=1 * 1024, - order=50, fap_category="Debug", ) diff --git a/applications/debug/vibro_test/application.fam b/applications/debug/vibro_test/application.fam index c35a7223f..dafa83eac 100644 --- a/applications/debug/vibro_test/application.fam +++ b/applications/debug/vibro_test/application.fam @@ -5,6 +5,5 @@ App( entry_point="vibro_test_app", requires=["gui"], stack_size=1 * 1024, - order=20, fap_category="Debug", ) diff --git a/applications/drivers/subghz/cc1101_ext/cc1101_ext.c b/applications/drivers/subghz/cc1101_ext/cc1101_ext.c index ae3556396..357214505 100644 --- a/applications/drivers/subghz/cc1101_ext/cc1101_ext.c +++ b/applications/drivers/subghz/cc1101_ext/cc1101_ext.c @@ -85,7 +85,7 @@ typedef struct { volatile SubGhzDeviceCC1101ExtState state; volatile SubGhzDeviceCC1101ExtRegulation regulation; const GpioPin* async_mirror_pin; - FuriHalSpiBusHandle* spi_bus_handle; + const FuriHalSpiBusHandle* spi_bus_handle; const GpioPin* g0_pin; SubGhzDeviceCC1101ExtAsyncTx async_tx; SubGhzDeviceCC1101ExtAsyncRx async_rx; diff --git a/applications/main/application.fam b/applications/main/application.fam index 0a90ee224..9d8604206 100644 --- a/applications/main/application.fam +++ b/applications/main/application.fam @@ -21,11 +21,6 @@ App( name="On start hooks", apptype=FlipperAppType.METAPACKAGE, provides=[ - "ibutton_start", - "onewire_start", - "subghz_start", - "infrared_start", - "lfrfid_start", - "nfc_start", + "cli", ], ) diff --git a/applications/main/archive/application.fam b/applications/main/archive/application.fam index f0a980ab0..00075107a 100644 --- a/applications/main/archive/application.fam +++ b/applications/main/archive/application.fam @@ -7,5 +7,5 @@ App( requires=["gui"], stack_size=4 * 1024, icon="A_FileManager_14", - order=0, + order=10, ) diff --git a/applications/main/archive/helpers/archive_apps.c b/applications/main/archive/helpers/archive_apps.c index 7aca29364..65d5a5af9 100644 --- a/applications/main/archive/helpers/archive_apps.c +++ b/applications/main/archive/helpers/archive_apps.c @@ -1,8 +1,9 @@ #include "archive_apps.h" #include "archive_browser.h" -static const char* known_apps[] = { +static const char* const known_apps[] = { [ArchiveAppTypeU2f] = "u2f", + [ArchiveAppTypeSetting] = "setting", }; ArchiveAppTypeEnum archive_get_app_type(const char* path) { @@ -36,6 +37,8 @@ bool archive_app_is_available(void* context, const char* path) { furi_record_close(RECORD_STORAGE); return file_exists; + } else if(app == ArchiveAppTypeSetting) { + return true; } else { return false; } @@ -53,6 +56,9 @@ bool archive_app_read_dir(void* context, const char* path) { if(app == ArchiveAppTypeU2f) { archive_add_app_item(browser, "/app:u2f/U2F Token"); return true; + } else if(app == ArchiveAppTypeSetting) { + archive_add_app_item(browser, path); + return true; } else { return false; } @@ -75,6 +81,8 @@ void archive_app_delete_file(void* context, const char* path) { if(archive_is_favorite("/app:u2f/U2F Token")) { archive_favorites_delete("/app:u2f/U2F Token"); } + } else if(app == ArchiveAppTypeSetting) { + // can't delete a setting! } if(res) { diff --git a/applications/main/archive/helpers/archive_apps.h b/applications/main/archive/helpers/archive_apps.h index d9d1dec34..f5d050387 100644 --- a/applications/main/archive/helpers/archive_apps.h +++ b/applications/main/archive/helpers/archive_apps.h @@ -4,12 +4,14 @@ typedef enum { ArchiveAppTypeU2f, + ArchiveAppTypeSetting, ArchiveAppTypeUnknown, ArchiveAppsTotal, } ArchiveAppTypeEnum; static const ArchiveFileTypeEnum app_file_types[] = { [ArchiveAppTypeU2f] = ArchiveFileTypeU2f, + [ArchiveAppTypeSetting] = ArchiveFileTypeSetting, [ArchiveAppTypeUnknown] = ArchiveFileTypeUnknown, }; diff --git a/applications/main/archive/helpers/archive_browser.h b/applications/main/archive/helpers/archive_browser.h index fea6ddf7f..b5dc37b1d 100644 --- a/applications/main/archive/helpers/archive_browser.h +++ b/applications/main/archive/helpers/archive_browser.h @@ -7,7 +7,7 @@ #define TAB_DEFAULT ArchiveTabFavorites // Start tab #define FILE_LIST_BUF_LEN 50 -static const char* tab_default_paths[] = { +static const char* const tab_default_paths[] = { [ArchiveTabFavorites] = "/app:favorites", [ArchiveTabIButton] = EXT_PATH("ibutton"), [ArchiveTabNFC] = EXT_PATH("nfc"), @@ -20,7 +20,7 @@ static const char* tab_default_paths[] = { [ArchiveTabBrowser] = STORAGE_EXT_PATH_PREFIX, }; -static const char* known_ext[] = { +static const char* const known_ext[] = { [ArchiveFileTypeIButton] = ".ibtn", [ArchiveFileTypeNFC] = ".nfc", [ArchiveFileTypeSubGhz] = ".sub", @@ -34,6 +34,7 @@ static const char* known_ext[] = { [ArchiveFileTypeFolder] = "?", [ArchiveFileTypeUnknown] = "*", [ArchiveFileTypeAppOrJs] = ".fap|.js", + [ArchiveFileTypeSetting] = "?", }; static const ArchiveFileTypeEnum known_type[] = { diff --git a/applications/main/archive/helpers/archive_favorites.c b/applications/main/archive/helpers/archive_favorites.c index 351d68c1d..5ed3adfe3 100644 --- a/applications/main/archive/helpers/archive_favorites.c +++ b/applications/main/archive/helpers/archive_favorites.c @@ -1,9 +1,10 @@ - #include "archive_favorites.h" #include "archive_files.h" #include "archive_apps.h" #include "archive_browser.h" +#include + #define ARCHIVE_FAV_FILE_BUF_LEN 32 static bool archive_favorites_read_line(File* file, FuriString* str_result) { @@ -337,3 +338,46 @@ void archive_favorites_save(void* context) { storage_file_free(file); furi_record_close(RECORD_STORAGE); } + +void archive_favorites_handle_setting_pin_unpin(const char* app_name, const char* setting) { + DialogMessage* message = dialog_message_alloc(); + + FuriString* setting_path = furi_string_alloc_set_str(app_name); + if(setting) { + furi_string_push_back(setting_path, '/'); + furi_string_cat_str(setting_path, setting); + } + const char* setting_path_str = furi_string_get_cstr(setting_path); + + bool is_favorite = archive_is_favorite("/app:setting/%s", setting_path_str); + dialog_message_set_header( + message, + is_favorite ? "Unpin This Setting?" : "Pin This Setting?", + 64, + 0, + AlignCenter, + AlignTop); + dialog_message_set_text( + message, + is_favorite ? "It will no longer be\naccessible from the\nFavorites menu" : + "It will be accessible from the\nFavorites menu", + 64, + 32, + AlignCenter, + AlignCenter); + dialog_message_set_buttons( + message, is_favorite ? "Unpin" : "Go back", NULL, is_favorite ? "Keep pinned" : "Pin"); + + DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); + DialogMessageButton button = dialog_message_show(dialogs, message); + furi_record_close(RECORD_DIALOGS); + + if(is_favorite && button == DialogMessageButtonLeft) { + archive_favorites_delete("/app:setting/%s", setting_path_str); + } else if(!is_favorite && button == DialogMessageButtonRight) { + archive_file_append(ARCHIVE_FAV_PATH, "/app:setting/%s\n", setting_path_str); + } + + furi_string_free(setting_path); + dialog_message_free(message); +} diff --git a/applications/main/archive/helpers/archive_favorites.h b/applications/main/archive/helpers/archive_favorites.h index 75070c44d..3a43a0e75 100644 --- a/applications/main/archive/helpers/archive_favorites.h +++ b/applications/main/archive/helpers/archive_favorites.h @@ -12,3 +12,13 @@ bool archive_is_favorite(const char* format, ...) _ATTRIBUTE((__format__(__print bool archive_favorites_rename(const char* src, const char* dst); void archive_add_to_favorites(const char* file_path); void archive_favorites_save(void* context); + +/** + * Intended to be called by settings apps to handle long presses, as well as + * internally from within the archive + * + * @param app_name name of the referring application + * @param setting name of the setting, which will be both displayed to the user + * and passed to the application as an argument upon recall + */ +void archive_favorites_handle_setting_pin_unpin(const char* app_name, const char* setting); diff --git a/applications/main/archive/helpers/archive_files.h b/applications/main/archive/helpers/archive_files.h index ad7662342..ea354bd5a 100644 --- a/applications/main/archive/helpers/archive_files.h +++ b/applications/main/archive/helpers/archive_files.h @@ -20,6 +20,7 @@ typedef enum { ArchiveFileTypeFolder, ArchiveFileTypeUnknown, ArchiveFileTypeAppOrJs, + ArchiveFileTypeSetting, ArchiveFileTypeLoading, } ArchiveFileTypeEnum; diff --git a/applications/main/archive/scenes/archive_scene_browser.c b/applications/main/archive/scenes/archive_scene_browser.c index d09595037..d600815f6 100644 --- a/applications/main/archive/scenes/archive_scene_browser.c +++ b/applications/main/archive/scenes/archive_scene_browser.c @@ -42,7 +42,7 @@ static void archive_loader_callback(const void* message, void* context) { const LoaderEvent* event = message; ArchiveApp* archive = (ArchiveApp*)context; - if(event->type == LoaderEventTypeApplicationStopped) { + if(event->type == LoaderEventTypeNoMoreAppsInQueue) { view_dispatcher_send_custom_event( archive->view_dispatcher, ArchiveBrowserEventListRefresh); } @@ -52,20 +52,37 @@ static void archive_run_in_app(ArchiveBrowserView* browser, ArchiveFile_t* selec UNUSED(browser); Loader* loader = furi_record_open(RECORD_LOADER); - const char* app_name = archive_get_flipper_app_name(selected->type); - - if(app_name) { - if(selected->is_app) { - char* param = strrchr(furi_string_get_cstr(selected->path), '/'); - if(param != NULL) { - param++; - } - loader_start_with_gui_error(loader, app_name, param); + if(selected->type == ArchiveFileTypeSetting) { + FuriString* app_name = furi_string_alloc_set(selected->path); + furi_string_right(app_name, furi_string_search_char(app_name, '/', 1) + 1); + size_t slash = furi_string_search_char(app_name, '/', 1); + if(slash != FURI_STRING_FAILURE) { + furi_string_left(app_name, slash); + FuriString* app_args = + furi_string_alloc_set_str(furi_string_get_cstr(app_name) + slash + 1); + loader_start_with_gui_error( + loader, furi_string_get_cstr(app_name), furi_string_get_cstr(app_args)); + furi_string_free(app_args); } else { - loader_start_with_gui_error(loader, app_name, furi_string_get_cstr(selected->path)); + loader_start_with_gui_error(loader, furi_string_get_cstr(app_name), NULL); } + furi_string_free(app_name); } else { - loader_start_with_gui_error(loader, furi_string_get_cstr(selected->path), NULL); + const char* app_name = archive_get_flipper_app_name(selected->type); + if(app_name) { + if(selected->is_app) { + char* param = strrchr(furi_string_get_cstr(selected->path), '/'); + if(param != NULL) { + param++; + } + loader_start_with_gui_error(loader, app_name, param); + } else { + loader_start_with_gui_error( + loader, app_name, furi_string_get_cstr(selected->path)); + } + } else { + loader_start_with_gui_error(loader, furi_string_get_cstr(selected->path), NULL); + } } furi_record_close(RECORD_LOADER); diff --git a/applications/main/archive/views/archive_browser_view.c b/applications/main/archive/views/archive_browser_view.c index 9e0918373..06e3bca29 100644 --- a/applications/main/archive/views/archive_browser_view.c +++ b/applications/main/archive/views/archive_browser_view.c @@ -28,6 +28,7 @@ static const Icon* ArchiveItemIcons[] = { [ArchiveFileTypeInfrared] = &I_ir_10px, [ArchiveFileTypeBadUsb] = &I_badusb_10px, [ArchiveFileTypeU2f] = &I_u2f_10px, + [ArchiveFileTypeSetting] = &I_settings_10px, [ArchiveFileTypeUpdateManifest] = &I_update_10px, [ArchiveFileTypeFolder] = &I_dir_10px, [ArchiveFileTypeUnknown] = &I_unknown_10px, diff --git a/applications/main/bad_usb/helpers/ducky_script.c b/applications/main/bad_usb/helpers/ducky_script.c index e71c03c48..99034028d 100644 --- a/applications/main/bad_usb/helpers/ducky_script.c +++ b/applications/main/bad_usb/helpers/ducky_script.c @@ -40,11 +40,8 @@ static const uint8_t numpad_keys[10] = { }; uint32_t ducky_get_command_len(const char* line) { - uint32_t len = strlen(line); - for(uint32_t i = 0; i < len; i++) { - if(line[i] == ' ') return i; - } - return 0; + char* first_space = strchr(line, ' '); + return first_space ? (first_space - line) : 0; } bool ducky_is_line_end(const char chr) { @@ -180,37 +177,46 @@ static bool ducky_string_next(BadUsbScript* bad_usb) { static int32_t ducky_parse_line(BadUsbScript* bad_usb, FuriString* line) { uint32_t line_len = furi_string_size(line); - const char* line_tmp = furi_string_get_cstr(line); + const char* line_cstr = furi_string_get_cstr(line); if(line_len == 0) { return SCRIPT_STATE_NEXT_LINE; // Skip empty lines } - FURI_LOG_D(WORKER_TAG, "line:%s", line_tmp); + FURI_LOG_D(WORKER_TAG, "line:%s", line_cstr); // Ducky Lang Functions - int32_t cmd_result = ducky_execute_cmd(bad_usb, line_tmp); + int32_t cmd_result = ducky_execute_cmd(bad_usb, line_cstr); if(cmd_result != SCRIPT_STATE_CMD_UNKNOWN) { return cmd_result; } // Mouse Keys - uint16_t key = ducky_get_mouse_keycode_by_name(line_tmp); + uint16_t key = ducky_get_mouse_keycode_by_name(line_cstr); if(key != HID_MOUSE_INVALID) { bad_usb->hid->mouse_press(bad_usb->hid_inst, key); bad_usb->hid->mouse_release(bad_usb->hid_inst, key); return 0; } - // Special keys + modifiers - key = ducky_get_keycode(bad_usb, line_tmp, false); - if(key == HID_KEYBOARD_NONE) { - return ducky_error(bad_usb, "No keycode defined for %s", line_tmp); - } - if((key & 0xFF00) != 0) { - // It's a modifier key - line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; - key |= ducky_get_keycode(bad_usb, line_tmp, true); + // Parse chain of modifiers linked by spaces and hyphens + uint16_t modifiers = 0; + while(1) { + key = ducky_get_next_modifier_keycode_by_name(&line_cstr); + if(key == HID_KEYBOARD_NONE) break; + + modifiers |= key; + char next_char = *line_cstr; + if(next_char == ' ' || next_char == '-') line_cstr++; } + + // Main key + char next_char = *line_cstr; + uint16_t main_key = ducky_get_keycode_by_name(line_cstr); + if(!main_key && next_char) main_key = BADUSB_ASCII_TO_KEY(bad_usb, next_char); + key = modifiers | main_key; + + if(key == 0 && next_char) ducky_error(bad_usb, "No keycode defined for %s", line_cstr); + bad_usb->hid->kb_press(bad_usb->hid_inst, key); bad_usb->hid->kb_release(bad_usb->hid_inst, key); return 0; diff --git a/applications/main/bad_usb/helpers/ducky_script_i.h b/applications/main/bad_usb/helpers/ducky_script_i.h index fd95ecf58..2b15c2586 100644 --- a/applications/main/bad_usb/helpers/ducky_script_i.h +++ b/applications/main/bad_usb/helpers/ducky_script_i.h @@ -54,6 +54,8 @@ uint32_t ducky_get_command_len(const char* line); bool ducky_is_line_end(const char chr); +uint16_t ducky_get_next_modifier_keycode_by_name(const char** param); + uint16_t ducky_get_keycode_by_name(const char* param); uint16_t ducky_get_media_keycode_by_name(const char* param); diff --git a/applications/main/bad_usb/helpers/ducky_script_keycodes.c b/applications/main/bad_usb/helpers/ducky_script_keycodes.c index 7dd2e4d16..ce957bb4e 100644 --- a/applications/main/bad_usb/helpers/ducky_script_keycodes.c +++ b/applications/main/bad_usb/helpers/ducky_script_keycodes.c @@ -6,21 +6,16 @@ typedef struct { uint16_t keycode; } DuckyKey; -static const DuckyKey ducky_keys[] = { - {"CTRL-ALT", KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_ALT}, - {"CTRL-SHIFT", KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_SHIFT}, - {"ALT-SHIFT", KEY_MOD_LEFT_ALT | KEY_MOD_LEFT_SHIFT}, - {"ALT-GUI", KEY_MOD_LEFT_ALT | KEY_MOD_LEFT_GUI}, - {"GUI-SHIFT", KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_SHIFT}, - {"GUI-CTRL", KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_CTRL}, - +static const DuckyKey ducky_modifier_keys[] = { {"CTRL", KEY_MOD_LEFT_CTRL}, {"CONTROL", KEY_MOD_LEFT_CTRL}, {"SHIFT", KEY_MOD_LEFT_SHIFT}, {"ALT", KEY_MOD_LEFT_ALT}, {"GUI", KEY_MOD_LEFT_GUI}, {"WINDOWS", KEY_MOD_LEFT_GUI}, +}; +static const DuckyKey ducky_keys[] = { {"DOWNARROW", HID_KEYBOARD_DOWN_ARROW}, {"DOWN", HID_KEYBOARD_DOWN_ARROW}, {"LEFTARROW", HID_KEYBOARD_LEFT_ARROW}, @@ -119,6 +114,23 @@ static const DuckyKey ducky_mouse_keys[] = { {"WHEEL_CLICK", HID_MOUSE_BTN_WHEEL}, }; +uint16_t ducky_get_next_modifier_keycode_by_name(const char** param) { + const char* input_str = *param; + + for(size_t i = 0; i < COUNT_OF(ducky_modifier_keys); i++) { + size_t key_cmd_len = strlen(ducky_modifier_keys[i].name); + if((strncmp(input_str, ducky_modifier_keys[i].name, key_cmd_len) == 0)) { + char next_char_after_key = input_str[key_cmd_len]; + if(ducky_is_line_end(next_char_after_key) || (next_char_after_key == '-')) { + *param = &input_str[key_cmd_len]; + return ducky_modifier_keys[i].keycode; + } + } + } + + return HID_KEYBOARD_NONE; +} + uint16_t ducky_get_keycode_by_name(const char* param) { for(size_t i = 0; i < COUNT_OF(ducky_keys); i++) { size_t key_cmd_len = strlen(ducky_keys[i].name); diff --git a/applications/main/gpio/usb_uart_bridge.c b/applications/main/gpio/usb_uart_bridge.c index e6b71cb34..77cd02f63 100644 --- a/applications/main/gpio/usb_uart_bridge.c +++ b/applications/main/gpio/usb_uart_bridge.c @@ -1,7 +1,6 @@ #include "usb_uart_bridge.h" #include "usb_cdc.h" #include -#include #include #include #include @@ -106,15 +105,15 @@ static void usb_uart_on_irq_rx_dma_cb( static void usb_uart_vcp_init(UsbUartBridge* usb_uart, uint8_t vcp_ch) { furi_hal_usb_unlock(); if(vcp_ch == 0) { - Cli* cli = furi_record_open(RECORD_CLI); - cli_session_close(cli); - furi_record_close(RECORD_CLI); + CliVcp* cli_vcp = furi_record_open(RECORD_CLI_VCP); + cli_vcp_disable(cli_vcp); + furi_record_close(RECORD_CLI_VCP); furi_check(furi_hal_usb_set_config(&usb_cdc_single, NULL) == true); } else { furi_check(furi_hal_usb_set_config(&usb_cdc_dual, NULL) == true); - Cli* cli = furi_record_open(RECORD_CLI); - cli_session_open(cli, &cli_vcp); - furi_record_close(RECORD_CLI); + CliVcp* cli_vcp = furi_record_open(RECORD_CLI_VCP); + cli_vcp_enable(cli_vcp); + furi_record_close(RECORD_CLI_VCP); } furi_hal_cdc_set_callbacks(vcp_ch, (CdcCallbacks*)&cdc_cb, usb_uart); } @@ -123,9 +122,9 @@ static void usb_uart_vcp_deinit(UsbUartBridge* usb_uart, uint8_t vcp_ch) { UNUSED(usb_uart); furi_hal_cdc_set_callbacks(vcp_ch, NULL, NULL); if(vcp_ch != 0) { - Cli* cli = furi_record_open(RECORD_CLI); - cli_session_close(cli); - furi_record_close(RECORD_CLI); + CliVcp* cli_vcp = furi_record_open(RECORD_CLI_VCP); + cli_vcp_disable(cli_vcp); + furi_record_close(RECORD_CLI_VCP); } } @@ -309,9 +308,9 @@ static int32_t usb_uart_worker(void* context) { furi_hal_usb_unlock(); furi_check(furi_hal_usb_set_config(&usb_cdc_single, NULL) == true); - Cli* cli = furi_record_open(RECORD_CLI); - cli_session_open(cli, &cli_vcp); - furi_record_close(RECORD_CLI); + CliVcp* cli_vcp = furi_record_open(RECORD_CLI_VCP); + cli_vcp_enable(cli_vcp); + furi_record_close(RECORD_CLI_VCP); return 0; } diff --git a/applications/main/ibutton/application.fam b/applications/main/ibutton/application.fam index 01c02ec23..84afe0f02 100644 --- a/applications/main/ibutton/application.fam +++ b/applications/main/ibutton/application.fam @@ -13,10 +13,10 @@ App( ) App( - appid="ibutton_start", - apptype=FlipperAppType.STARTUP, + appid="cli_ikey", targets=["f7"], - entry_point="ibutton_on_system_start", + apptype=FlipperAppType.PLUGIN, + entry_point="cli_ikey_ep", + requires=["cli"], sources=["ibutton_cli.c"], - order=60, ) diff --git a/applications/main/ibutton/ibutton_cli.c b/applications/main/ibutton/ibutton_cli.c index 2338ca3c3..0b9a59586 100644 --- a/applications/main/ibutton/ibutton_cli.c +++ b/applications/main/ibutton/ibutton_cli.c @@ -1,26 +1,14 @@ #include #include -#include +#include #include +#include #include #include #include -static void ibutton_cli(Cli* cli, FuriString* args, void* context); - -// app cli function -void ibutton_on_system_start(void) { -#ifdef SRV_CLI - Cli* cli = furi_record_open(RECORD_CLI); - cli_add_command(cli, "ikey", CliCommandFlagDefault, ibutton_cli, cli); - furi_record_close(RECORD_CLI); -#else - UNUSED(ibutton_cli); -#endif -} - static void ibutton_cli_print_usage(void) { printf("Usage:\r\n"); printf("ikey read\r\n"); @@ -92,7 +80,7 @@ static void ibutton_cli_worker_read_cb(void* context) { furi_event_flag_set(event, EVENT_FLAG_IBUTTON_COMPLETE); } -static void ibutton_cli_read(Cli* cli) { +static void ibutton_cli_read(PipeSide* pipe) { iButtonProtocols* protocols = ibutton_protocols_alloc(); iButtonWorker* worker = ibutton_worker_alloc(protocols); iButtonKey* key = ibutton_key_alloc(ibutton_protocols_get_max_data_size(protocols)); @@ -113,7 +101,7 @@ static void ibutton_cli_read(Cli* cli) { break; } - if(cli_cmd_interrupt_received(cli)) break; + if(cli_is_pipe_broken_or_is_etx_next_char(pipe)) break; } ibutton_worker_stop(worker); @@ -138,7 +126,7 @@ static void ibutton_cli_worker_write_cb(void* context, iButtonWorkerWriteResult furi_event_flag_set(write_context->event, EVENT_FLAG_IBUTTON_COMPLETE); } -void ibutton_cli_write(Cli* cli, FuriString* args) { +void ibutton_cli_write(PipeSide* pipe, FuriString* args) { iButtonProtocols* protocols = ibutton_protocols_alloc(); iButtonWorker* worker = ibutton_worker_alloc(protocols); iButtonKey* key = ibutton_key_alloc(ibutton_protocols_get_max_data_size(protocols)); @@ -181,7 +169,7 @@ void ibutton_cli_write(Cli* cli, FuriString* args) { } } - if(cli_cmd_interrupt_received(cli)) break; + if(cli_is_pipe_broken_or_is_etx_next_char(pipe)) break; } } while(false); @@ -195,7 +183,7 @@ void ibutton_cli_write(Cli* cli, FuriString* args) { furi_event_flag_free(write_context.event); } -void ibutton_cli_emulate(Cli* cli, FuriString* args) { +void ibutton_cli_emulate(PipeSide* pipe, FuriString* args) { iButtonProtocols* protocols = ibutton_protocols_alloc(); iButtonWorker* worker = ibutton_worker_alloc(protocols); iButtonKey* key = ibutton_key_alloc(ibutton_protocols_get_max_data_size(protocols)); @@ -214,7 +202,7 @@ void ibutton_cli_emulate(Cli* cli, FuriString* args) { ibutton_worker_emulate_start(worker, key); - while(!cli_cmd_interrupt_received(cli)) { + while(!cli_is_pipe_broken_or_is_etx_next_char(pipe)) { furi_delay_ms(100); }; @@ -228,8 +216,7 @@ void ibutton_cli_emulate(Cli* cli, FuriString* args) { ibutton_protocols_free(protocols); } -void ibutton_cli(Cli* cli, FuriString* args, void* context) { - UNUSED(cli); +static void execute(PipeSide* pipe, FuriString* args, void* context) { UNUSED(context); FuriString* cmd; cmd = furi_string_alloc(); @@ -241,14 +228,16 @@ void ibutton_cli(Cli* cli, FuriString* args, void* context) { } if(furi_string_cmp_str(cmd, "read") == 0) { - ibutton_cli_read(cli); + ibutton_cli_read(pipe); } else if(furi_string_cmp_str(cmd, "write") == 0) { - ibutton_cli_write(cli, args); + ibutton_cli_write(pipe, args); } else if(furi_string_cmp_str(cmd, "emulate") == 0) { - ibutton_cli_emulate(cli, args); + ibutton_cli_emulate(pipe, args); } else { ibutton_cli_print_usage(); } furi_string_free(cmd); } + +CLI_COMMAND_INTERFACE(ikey, execute, CliCommandFlagDefault, 1024, CLI_APPID); diff --git a/applications/main/infrared/application.fam b/applications/main/infrared/application.fam index 575bebbe4..79b3fdbfa 100644 --- a/applications/main/infrared/application.fam +++ b/applications/main/infrared/application.fam @@ -15,14 +15,14 @@ App( ) App( - appid="infrared_start", - apptype=FlipperAppType.STARTUP, + appid="cli_ir", targets=["f7"], - entry_point="infrared_on_system_start", + apptype=FlipperAppType.PLUGIN, + entry_point="cli_ir_ep", + requires=["cli"], sources=[ "infrared_cli.c", "infrared_brute_force.c", "infrared_signal.c", ], - order=20, ) diff --git a/applications/main/infrared/infrared_brute_force.c b/applications/main/infrared/infrared_brute_force.c index 636635894..1ec4645e9 100644 --- a/applications/main/infrared/infrared_brute_force.c +++ b/applications/main/infrared/infrared_brute_force.c @@ -104,9 +104,9 @@ InfraredErrorCode infrared_brute_force_calculate_messages(InfraredBruteForce* br break; } - size_t signal_start = flipper_format_tell(ff); bool signal_valid = false; while(infrared_signal_read_name(ff, signal_name) == InfraredErrorCodeNone) { + size_t signal_start = flipper_format_tell(ff); error = infrared_signal_read_body(signal, ff); signal_valid = (!INFRARED_ERROR_PRESENT(error)) && infrared_signal_is_valid(signal); if(!signal_valid) break; diff --git a/applications/main/infrared/infrared_cli.c b/applications/main/infrared/infrared_cli.c index 85ae95658..eb13bcd79 100644 --- a/applications/main/infrared/infrared_cli.c +++ b/applications/main/infrared/infrared_cli.c @@ -1,11 +1,11 @@ -#include -#include +#include #include #include #include #include #include #include +#include #include #include "infrared_signal.h" @@ -19,14 +19,14 @@ DICT_DEF2(dict_signals, FuriString*, FURI_STRING_OPLIST, int, M_DEFAULT_OPLIST) -static void infrared_cli_start_ir_rx(Cli* cli, FuriString* args); -static void infrared_cli_start_ir_tx(Cli* cli, FuriString* args); -static void infrared_cli_process_decode(Cli* cli, FuriString* args); -static void infrared_cli_process_universal(Cli* cli, FuriString* args); +static void infrared_cli_start_ir_rx(PipeSide* pipe, FuriString* args); +static void infrared_cli_start_ir_tx(PipeSide* pipe, FuriString* args); +static void infrared_cli_process_decode(PipeSide* pipe, FuriString* args); +static void infrared_cli_process_universal(PipeSide* pipe, FuriString* args); static const struct { const char* cmd; - void (*process_function)(Cli* cli, FuriString* args); + void (*process_function)(PipeSide* pipe, FuriString* args); } infrared_cli_commands[] = { {.cmd = "rx", .process_function = infrared_cli_start_ir_rx}, {.cmd = "tx", .process_function = infrared_cli_start_ir_tx}, @@ -38,7 +38,7 @@ static void signal_received_callback(void* context, InfraredWorkerSignal* receiv furi_assert(received_signal); char buf[100]; size_t buf_cnt; - Cli* cli = (Cli*)context; + PipeSide* pipe = (PipeSide*)context; if(infrared_worker_signal_is_decoded(received_signal)) { const InfraredMessage* message = infrared_worker_get_decoded_signal(received_signal); @@ -52,20 +52,20 @@ static void signal_received_callback(void* context, InfraredWorkerSignal* receiv ROUND_UP_TO(infrared_get_protocol_command_length(message->protocol), 4), message->command, message->repeat ? " R" : ""); - cli_write(cli, (uint8_t*)buf, buf_cnt); + pipe_send(pipe, buf, buf_cnt); } else { const uint32_t* timings; size_t timings_cnt; infrared_worker_get_raw_signal(received_signal, &timings, &timings_cnt); buf_cnt = snprintf(buf, sizeof(buf), "RAW, %zu samples:\r\n", timings_cnt); - cli_write(cli, (uint8_t*)buf, buf_cnt); + pipe_send(pipe, buf, buf_cnt); for(size_t i = 0; i < timings_cnt; ++i) { buf_cnt = snprintf(buf, sizeof(buf), "%lu ", timings[i]); - cli_write(cli, (uint8_t*)buf, buf_cnt); + pipe_send(pipe, buf, buf_cnt); } buf_cnt = snprintf(buf, sizeof(buf), "\r\n"); - cli_write(cli, (uint8_t*)buf, buf_cnt); + pipe_send(pipe, buf, buf_cnt); } } @@ -124,9 +124,7 @@ static void infrared_cli_print_usage(void) { infrared_cli_print_universal_remotes(); } -static void infrared_cli_start_ir_rx(Cli* cli, FuriString* args) { - UNUSED(cli); - +static void infrared_cli_start_ir_rx(PipeSide* pipe, FuriString* args) { bool enable_decoding = true; if(!furi_string_empty(args)) { @@ -142,10 +140,10 @@ static void infrared_cli_start_ir_rx(Cli* cli, FuriString* args) { InfraredWorker* worker = infrared_worker_alloc(); infrared_worker_rx_enable_signal_decoding(worker, enable_decoding); infrared_worker_rx_start(worker); - infrared_worker_rx_set_received_signal_callback(worker, signal_received_callback, cli); + infrared_worker_rx_set_received_signal_callback(worker, signal_received_callback, pipe); printf("Receiving %s INFRARED...\r\nPress Ctrl+C to abort\r\n", enable_decoding ? "" : "RAW"); - while(!cli_cmd_interrupt_received(cli)) { + while(!cli_is_pipe_broken_or_is_etx_next_char(pipe)) { furi_delay_ms(50); } @@ -214,8 +212,8 @@ static bool infrared_cli_parse_raw(const char* str, InfraredSignal* signal) { return infrared_signal_is_valid(signal); } -static void infrared_cli_start_ir_tx(Cli* cli, FuriString* args) { - UNUSED(cli); +static void infrared_cli_start_ir_tx(PipeSide* pipe, FuriString* args) { + UNUSED(pipe); const char* str = furi_string_get_cstr(args); InfraredSignal* signal = infrared_signal_alloc(); @@ -335,8 +333,8 @@ static bool infrared_cli_decode_file(FlipperFormat* input_file, FlipperFormat* o return ret; } -static void infrared_cli_process_decode(Cli* cli, FuriString* args) { - UNUSED(cli); +static void infrared_cli_process_decode(PipeSide* pipe, FuriString* args) { + UNUSED(pipe); Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* input_file = flipper_format_buffered_file_alloc(storage); FlipperFormat* output_file = NULL; @@ -455,8 +453,10 @@ static void infrared_cli_list_remote_signals(FuriString* remote_name) { furi_record_close(RECORD_STORAGE); } -static void - infrared_cli_brute_force_signals(Cli* cli, FuriString* remote_name, FuriString* signal_name) { +static void infrared_cli_brute_force_signals( + PipeSide* pipe, + FuriString* remote_name, + FuriString* signal_name) { InfraredBruteForce* brute_force = infrared_brute_force_alloc(); FuriString* remote_path = furi_string_alloc_printf( "%s/%s.ir", INFRARED_ASSETS_FOLDER, furi_string_get_cstr(remote_name)); @@ -490,7 +490,7 @@ static void while(running) { running = infrared_brute_force_send(brute_force, current_signal); - if(cli_cmd_interrupt_received(cli)) break; + if(cli_is_pipe_broken_or_is_etx_next_char(pipe)) break; printf("\r%d%% complete.", (int)((float)current_signal++ / (float)signal_count * 100)); fflush(stdout); @@ -504,7 +504,7 @@ static void infrared_brute_force_free(brute_force); } -static void infrared_cli_process_universal(Cli* cli, FuriString* args) { +static void infrared_cli_process_universal(PipeSide* pipe, FuriString* args) { FuriString* arg1 = furi_string_alloc(); FuriString* arg2 = furi_string_alloc(); @@ -519,14 +519,14 @@ static void infrared_cli_process_universal(Cli* cli, FuriString* args) { } else if(furi_string_equal_str(arg1, "list")) { infrared_cli_list_remote_signals(arg2); } else { - infrared_cli_brute_force_signals(cli, arg1, arg2); + infrared_cli_brute_force_signals(pipe, arg1, arg2); } furi_string_free(arg1); furi_string_free(arg2); } -static void infrared_cli_start_ir(Cli* cli, FuriString* args, void* context) { +static void execute(PipeSide* pipe, FuriString* args, void* context) { UNUSED(context); if(furi_hal_infrared_is_busy()) { printf("INFRARED is busy. Exiting."); @@ -546,19 +546,12 @@ static void infrared_cli_start_ir(Cli* cli, FuriString* args, void* context) { } if(i < COUNT_OF(infrared_cli_commands)) { - infrared_cli_commands[i].process_function(cli, args); + infrared_cli_commands[i].process_function(pipe, args); } else { infrared_cli_print_usage(); } furi_string_free(command); } -void infrared_on_system_start(void) { -#ifdef SRV_CLI - Cli* cli = (Cli*)furi_record_open(RECORD_CLI); - cli_add_command(cli, "ir", CliCommandFlagDefault, infrared_cli_start_ir, NULL); - furi_record_close(RECORD_CLI); -#else - UNUSED(infrared_cli_start_ir); -#endif -} + +CLI_COMMAND_INTERFACE(ir, execute, CliCommandFlagDefault, 2048, CLI_APPID); diff --git a/applications/main/infrared/resources/infrared/assets/tv.ir b/applications/main/infrared/resources/infrared/assets/tv.ir index 6311f500c..bff8adfa0 100644 --- a/applications/main/infrared/resources/infrared/assets/tv.ir +++ b/applications/main/infrared/resources/infrared/assets/tv.ir @@ -2474,7 +2474,7 @@ protocol: RC5 address: 01 00 00 00 command: 14 00 00 00 # -# Model: Elitelux L32HD1000 +# Model: Elitelux L32HD1000 / Vivax TV-32LE114T2S2SM / Sansui # name: Power type: parsed diff --git a/applications/main/lfrfid/application.fam b/applications/main/lfrfid/application.fam index c067d786f..d6fca74f4 100644 --- a/applications/main/lfrfid/application.fam +++ b/applications/main/lfrfid/application.fam @@ -13,10 +13,10 @@ App( ) App( - appid="lfrfid_start", + appid="cli_rfid", targets=["f7"], - apptype=FlipperAppType.STARTUP, - entry_point="lfrfid_on_system_start", + apptype=FlipperAppType.PLUGIN, + entry_point="cli_rfid_ep", + requires=["cli"], sources=["lfrfid_cli.c"], - order=50, ) diff --git a/applications/main/lfrfid/lfrfid_cli.c b/applications/main/lfrfid/lfrfid_cli.c index a25032d6a..63ca046b9 100644 --- a/applications/main/lfrfid/lfrfid_cli.c +++ b/applications/main/lfrfid/lfrfid_cli.c @@ -1,11 +1,12 @@ #include #include #include -#include +#include #include #include #include #include +#include #include @@ -14,15 +15,6 @@ #include #include -static void lfrfid_cli(Cli* cli, FuriString* args, void* context); - -// app cli function -void lfrfid_on_system_start(void) { - Cli* cli = furi_record_open(RECORD_CLI); - cli_add_command(cli, "rfid", CliCommandFlagDefault, lfrfid_cli, NULL); - furi_record_close(RECORD_CLI); -} - static void lfrfid_cli_print_usage(void) { printf("Usage:\r\n"); printf("rfid read - read in ASK/PSK mode\r\n"); @@ -49,7 +41,7 @@ static void lfrfid_cli_read_callback(LFRFIDWorkerReadResult result, ProtocolId p furi_event_flag_set(context->event, 1 << result); } -static void lfrfid_cli_read(Cli* cli, FuriString* args) { +static void lfrfid_cli_read(PipeSide* pipe, FuriString* args) { FuriString* type_string; type_string = furi_string_alloc(); LFRFIDWorkerReadType type = LFRFIDWorkerReadTypeAuto; @@ -96,7 +88,7 @@ static void lfrfid_cli_read(Cli* cli, FuriString* args) { } } - if(cli_cmd_interrupt_received(cli)) break; + if(cli_is_pipe_broken_or_is_etx_next_char(pipe)) break; } lfrfid_worker_stop(worker); @@ -192,7 +184,7 @@ static void lfrfid_cli_write_callback(LFRFIDWorkerWriteResult result, void* ctx) furi_event_flag_set(events, 1 << result); } -static void lfrfid_cli_write(Cli* cli, FuriString* args) { +static void lfrfid_cli_write(PipeSide* pipe, FuriString* args) { ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); ProtocolId protocol; @@ -212,7 +204,7 @@ static void lfrfid_cli_write(Cli* cli, FuriString* args) { (1 << LFRFIDWorkerWriteProtocolCannotBeWritten) | (1 << LFRFIDWorkerWriteFobCannotBeWritten); - while(!cli_cmd_interrupt_received(cli)) { + while(!cli_is_pipe_broken_or_is_etx_next_char(pipe)) { uint32_t flags = furi_event_flag_wait(event, available_flags, FuriFlagWaitAny, 100); if(flags != (unsigned)FuriFlagErrorTimeout) { if(FURI_BIT(flags, LFRFIDWorkerWriteOK)) { @@ -239,7 +231,7 @@ static void lfrfid_cli_write(Cli* cli, FuriString* args) { furi_event_flag_free(event); } -static void lfrfid_cli_emulate(Cli* cli, FuriString* args) { +static void lfrfid_cli_emulate(PipeSide* pipe, FuriString* args) { ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); ProtocolId protocol; @@ -254,7 +246,7 @@ static void lfrfid_cli_emulate(Cli* cli, FuriString* args) { lfrfid_worker_emulate_start(worker, protocol); printf("Emulating RFID...\r\nPress Ctrl+C to abort\r\n"); - while(!cli_cmd_interrupt_received(cli)) { + while(!cli_is_pipe_broken_or_is_etx_next_char(pipe)) { furi_delay_ms(100); } printf("Emulation stopped\r\n"); @@ -265,8 +257,8 @@ static void lfrfid_cli_emulate(Cli* cli, FuriString* args) { protocol_dict_free(dict); } -static void lfrfid_cli_raw_analyze(Cli* cli, FuriString* args) { - UNUSED(cli); +static void lfrfid_cli_raw_analyze(PipeSide* pipe, FuriString* args) { + UNUSED(pipe); FuriString *filepath, *info_string; filepath = furi_string_alloc(); info_string = furi_string_alloc(); @@ -392,9 +384,7 @@ static void lfrfid_cli_raw_read_callback(LFRFIDWorkerReadRawResult result, void* furi_event_flag_set(event, 1 << result); } -static void lfrfid_cli_raw_read(Cli* cli, FuriString* args) { - UNUSED(cli); - +static void lfrfid_cli_raw_read(PipeSide* pipe, FuriString* args) { FuriString *filepath, *type_string; filepath = furi_string_alloc(); type_string = furi_string_alloc(); @@ -452,7 +442,7 @@ static void lfrfid_cli_raw_read(Cli* cli, FuriString* args) { } } - if(cli_cmd_interrupt_received(cli)) break; + if(cli_is_pipe_broken_or_is_etx_next_char(pipe)) break; } if(overrun) { @@ -479,9 +469,7 @@ static void lfrfid_cli_raw_emulate_callback(LFRFIDWorkerEmulateRawResult result, furi_event_flag_set(event, 1 << result); } -static void lfrfid_cli_raw_emulate(Cli* cli, FuriString* args) { - UNUSED(cli); - +static void lfrfid_cli_raw_emulate(PipeSide* pipe, FuriString* args) { FuriString* filepath; filepath = furi_string_alloc(); Storage* storage = furi_record_open(RECORD_STORAGE); @@ -527,7 +515,7 @@ static void lfrfid_cli_raw_emulate(Cli* cli, FuriString* args) { } } - if(cli_cmd_interrupt_received(cli)) break; + if(cli_is_pipe_broken_or_is_etx_next_char(pipe)) break; } if(overrun) { @@ -548,7 +536,7 @@ static void lfrfid_cli_raw_emulate(Cli* cli, FuriString* args) { furi_string_free(filepath); } -static void lfrfid_cli(Cli* cli, FuriString* args, void* context) { +static void execute(PipeSide* pipe, FuriString* args, void* context) { UNUSED(context); FuriString* cmd; cmd = furi_string_alloc(); @@ -560,20 +548,22 @@ static void lfrfid_cli(Cli* cli, FuriString* args, void* context) { } if(furi_string_cmp_str(cmd, "read") == 0) { - lfrfid_cli_read(cli, args); + lfrfid_cli_read(pipe, args); } else if(furi_string_cmp_str(cmd, "write") == 0) { - lfrfid_cli_write(cli, args); + lfrfid_cli_write(pipe, args); } else if(furi_string_cmp_str(cmd, "emulate") == 0) { - lfrfid_cli_emulate(cli, args); + lfrfid_cli_emulate(pipe, args); } else if(furi_string_cmp_str(cmd, "raw_read") == 0) { - lfrfid_cli_raw_read(cli, args); + lfrfid_cli_raw_read(pipe, args); } else if(furi_string_cmp_str(cmd, "raw_emulate") == 0) { - lfrfid_cli_raw_emulate(cli, args); + lfrfid_cli_raw_emulate(pipe, args); } else if(furi_string_cmp_str(cmd, "raw_analyze") == 0) { - lfrfid_cli_raw_analyze(cli, args); + lfrfid_cli_raw_analyze(pipe, args); } else { lfrfid_cli_print_usage(); } furi_string_free(cmd); } + +CLI_COMMAND_INTERFACE(rfid, execute, CliCommandFlagDefault, 2048, CLI_APPID); diff --git a/applications/main/lfrfid/lfrfid_i.h b/applications/main/lfrfid/lfrfid_i.h index 2fcedcd7f..29a5eb902 100644 --- a/applications/main/lfrfid/lfrfid_i.h +++ b/applications/main/lfrfid/lfrfid_i.h @@ -8,7 +8,6 @@ #include #include #include -#include #include #include diff --git a/applications/main/nfc/application.fam b/applications/main/nfc/application.fam index 11dca519a..03b0b67d8 100644 --- a/applications/main/nfc/application.fam +++ b/applications/main/nfc/application.fam @@ -268,10 +268,10 @@ App( ) App( - appid="nfc_start", + appid="cli_nfc", targets=["f7"], - apptype=FlipperAppType.STARTUP, - entry_point="nfc_on_system_start", + apptype=FlipperAppType.PLUGIN, + entry_point="cli_nfc_ep", + requires=["cli"], sources=["nfc_cli.c"], - order=30, ) diff --git a/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3_render.c b/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3_render.c index ba8f10b93..fbf331d3b 100644 --- a/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3_render.c +++ b/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3_render.c @@ -37,11 +37,13 @@ void nfc_render_iso15693_3_brief(const Iso15693_3Data* data, FuriString* str) { } void nfc_render_iso15693_3_system_info(const Iso15693_3Data* data, FuriString* str) { - if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_MEMORY) { + const uint16_t block_count = iso15693_3_get_block_count(data); + const uint8_t block_size = iso15693_3_get_block_size(data); + + if((data->system_info.flags & ISO15693_3_SYSINFO_FLAG_MEMORY) && + (block_count > 0 && block_size > 0)) { furi_string_cat(str, "\e#Memory data\n\e*--------------------\n"); - const uint16_t block_count = iso15693_3_get_block_count(data); - const uint8_t block_size = iso15693_3_get_block_size(data); const uint16_t display_block_count = MIN(NFC_RENDER_ISO15693_3_MAX_BYTES / block_size, block_count); diff --git a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c index 4b639f981..4053741b8 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c +++ b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c @@ -13,6 +13,7 @@ enum { SubmenuIndexDetectReader = SubmenuIndexCommonMax, SubmenuIndexDictAttack, + SubmenuIndexCrackNonces, SubmenuIndexUpdate, }; @@ -130,6 +131,13 @@ static void nfc_scene_read_menu_on_enter_mf_classic(NfcApp* instance) { SubmenuIndexDictAttack, nfc_protocol_support_common_submenu_callback, instance); + + submenu_add_item( + submenu, + "Crack nonces in MFKey32", + SubmenuIndexCrackNonces, + nfc_protocol_support_common_submenu_callback, + instance); } } @@ -191,6 +199,11 @@ static bool nfc_scene_read_menu_on_event_mf_classic(NfcApp* instance, SceneManag if(event.type == SceneManagerEventTypeCustom) { if(event.event == SubmenuIndexDetectReader) { + scene_manager_set_scene_state( + instance->scene_manager, + NfcSceneSaveConfirm, + NfcSceneSaveConfirmStateDetectReader); + scene_manager_next_scene(instance->scene_manager, NfcSceneSaveConfirm); dolphin_deed(DolphinDeedNfcDetectReader); consumed = true; @@ -200,6 +213,11 @@ static bool nfc_scene_read_menu_on_event_mf_classic(NfcApp* instance, SceneManag scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicDictAttack); } consumed = true; + } else if(event.event == SubmenuIndexCrackNonces) { + scene_manager_set_scene_state( + instance->scene_manager, NfcSceneSaveConfirm, NfcSceneSaveConfirmStateCrackNonces); + scene_manager_next_scene(instance->scene_manager, NfcSceneSaveConfirm); + consumed = true; } } diff --git a/applications/main/nfc/helpers/protocol_support/mf_desfire/mf_desfire_render.c b/applications/main/nfc/helpers/protocol_support/mf_desfire/mf_desfire_render.c index 783cbb871..96e4a30f9 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_desfire/mf_desfire_render.c +++ b/applications/main/nfc/helpers/protocol_support/mf_desfire/mf_desfire_render.c @@ -180,6 +180,9 @@ void nfc_render_mf_desfire_file_settings_data( case MfDesfireFileTypeCyclicRecord: type = "cyclic"; break; + case MfDesfireFileTypeTransactionMac: + type = "txn-mac"; + break; default: type = "unknown"; } @@ -237,6 +240,15 @@ void nfc_render_mf_desfire_file_settings_data( furi_string_cat_printf(str, "size %lu\n", record_size); furi_string_cat_printf(str, "num %lu max %lu\n", record_count, settings->record.max); break; + case MfDesfireFileTypeTransactionMac: + record_count = 0; + furi_string_cat_printf( + str, + "key opt %02X ver %02X\n", + settings->transaction_mac.key_option, + settings->transaction_mac.key_version); + furi_string_cat_printf(str, "cnt limit %lu\n", settings->transaction_mac.counter_limit); + break; } bool is_auth_required = true; diff --git a/applications/main/nfc/nfc_app.c b/applications/main/nfc/nfc_app.c index 5365d6bef..7a829c329 100644 --- a/applications/main/nfc/nfc_app.c +++ b/applications/main/nfc/nfc_app.c @@ -470,6 +470,26 @@ static void nfc_show_initial_scene_for_device(NfcApp* nfc) { scene_manager_next_scene(nfc->scene_manager, scene); } +void nfc_app_run_external(NfcApp* nfc, const char* app_path) { + furi_assert(nfc); + furi_assert(app_path); + + Loader* loader = furi_record_open(RECORD_LOADER); + + loader_enqueue_launch(loader, app_path, NULL, LoaderDeferredLaunchFlagGui); + + FuriString* self_path = furi_string_alloc(); + furi_check(loader_get_application_launch_path(loader, self_path)); + + loader_enqueue_launch( + loader, furi_string_get_cstr(self_path), NULL, LoaderDeferredLaunchFlagGui); + furi_string_free(self_path); + + furi_record_close(RECORD_LOADER); + + view_dispatcher_stop(nfc->view_dispatcher); +} + int32_t nfc_app(void* p) { if(!nfc_is_hal_ready()) return 0; diff --git a/applications/main/nfc/nfc_app_i.h b/applications/main/nfc/nfc_app_i.h index 14e484622..920127fef 100644 --- a/applications/main/nfc/nfc_app_i.h +++ b/applications/main/nfc/nfc_app_i.h @@ -10,7 +10,6 @@ #include #include #include -#include #include #include @@ -36,6 +35,7 @@ #include "helpers/felica_auth.h" #include "helpers/slix_unlock.h" +#include #include #include #include @@ -81,6 +81,8 @@ #define NFC_APP_MF_CLASSIC_DICT_SYSTEM_NESTED_PATH \ (NFC_APP_FOLDER "/assets/mf_classic_dict_nested.nfc") +#define NFC_MFKEY32_APP_PATH (EXT_PATH("apps/NFC/mfkey.fap")) + typedef enum { NfcRpcStateIdle, NfcRpcStateEmulating, @@ -168,6 +170,11 @@ typedef enum { NfcViewDetectReader, } NfcView; +typedef enum { + NfcSceneSaveConfirmStateDetectReader, + NfcSceneSaveConfirmStateCrackNonces, +} NfcSceneSaveConfirmState; + int32_t nfc_task(void* p); void nfc_text_store_set(NfcApp* nfc, const char* text, ...); @@ -203,3 +210,5 @@ bool nfc_save_file(NfcApp* instance, FuriString* path); void nfc_make_app_folder(NfcApp* instance); void nfc_append_filename_string_when_present(NfcApp* instance, FuriString* string); + +void nfc_app_run_external(NfcApp* nfc, const char* app_path); diff --git a/applications/main/nfc/nfc_cli.c b/applications/main/nfc/nfc_cli.c index 90ac26d7c..af3fd62eb 100644 --- a/applications/main/nfc/nfc_cli.c +++ b/applications/main/nfc/nfc_cli.c @@ -1,8 +1,9 @@ #include #include -#include +#include #include #include +#include #include @@ -17,7 +18,7 @@ static void nfc_cli_print_usage(void) { } } -static void nfc_cli_field(Cli* cli, FuriString* args) { +static void nfc_cli_field(PipeSide* pipe, FuriString* args) { UNUSED(args); // Check if nfc worker is not busy if(furi_hal_nfc_is_hal_ready() != FuriHalNfcErrorNone) { @@ -32,7 +33,7 @@ static void nfc_cli_field(Cli* cli, FuriString* args) { printf("Field is on. Don't leave device in this mode for too long.\r\n"); printf("Press Ctrl+C to abort\r\n"); - while(!cli_cmd_interrupt_received(cli)) { + while(!cli_is_pipe_broken_or_is_etx_next_char(pipe)) { furi_delay_ms(50); } @@ -40,7 +41,7 @@ static void nfc_cli_field(Cli* cli, FuriString* args) { furi_hal_nfc_release(); } -static void nfc_cli(Cli* cli, FuriString* args, void* context) { +static void execute(PipeSide* pipe, FuriString* args, void* context) { UNUSED(context); FuriString* cmd; cmd = furi_string_alloc(); @@ -52,7 +53,7 @@ static void nfc_cli(Cli* cli, FuriString* args, void* context) { } if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { if(furi_string_cmp_str(cmd, "field") == 0) { - nfc_cli_field(cli, args); + nfc_cli_field(pipe, args); break; } } @@ -63,12 +64,4 @@ static void nfc_cli(Cli* cli, FuriString* args, void* context) { furi_string_free(cmd); } -void nfc_on_system_start(void) { -#ifdef SRV_CLI - Cli* cli = furi_record_open(RECORD_CLI); - cli_add_command(cli, "nfc", CliCommandFlagDefault, nfc_cli, NULL); - furi_record_close(RECORD_CLI); -#else - UNUSED(nfc_cli); -#endif -} +CLI_COMMAND_INTERFACE(nfc, execute, CliCommandFlagDefault, 1024, CLI_APPID); diff --git a/applications/main/nfc/plugins/supported_cards/ndef.c b/applications/main/nfc/plugins/supported_cards/ndef.c index 389e659d3..86d31052e 100644 --- a/applications/main/nfc/plugins/supported_cards/ndef.c +++ b/applications/main/nfc/plugins/supported_cards/ndef.c @@ -23,6 +23,7 @@ #include #include +#include #define TAG "NDEF" @@ -188,30 +189,34 @@ static bool ndef_get(Ndef* ndef, size_t pos, size_t len, void* buf) { // So the first 93 (31*3) data blocks correspond to 128 real blocks. // Last 128 blocks are 8 sectors: 15 data blocks, 1 sector trailer. // So the last 120 (8*15) data blocks correspond to 128 real blocks. - div_t small_sector_data_blocks = div(pos, MF_CLASSIC_BLOCK_SIZE); + const size_t real_block_data_offset = pos % MF_CLASSIC_BLOCK_SIZE; + size_t small_sector_data_blocks = pos / MF_CLASSIC_BLOCK_SIZE; size_t large_sector_data_blocks = 0; - if(small_sector_data_blocks.quot > 93) { - large_sector_data_blocks = small_sector_data_blocks.quot - 93; - small_sector_data_blocks.quot = 93; + if(small_sector_data_blocks > 93) { + large_sector_data_blocks = small_sector_data_blocks - 93; + small_sector_data_blocks = 93; } - div_t small_sectors = div(small_sector_data_blocks.quot, 3); - size_t real_block = small_sectors.quot * 4 + small_sectors.rem; - if(small_sectors.quot >= 16) { + const size_t small_sector_block_offset = small_sector_data_blocks % 3; + const size_t small_sectors = small_sector_data_blocks / 3; + size_t real_block = small_sectors * 4 + small_sector_block_offset; + if(small_sectors >= 16) { real_block += 4; // Skip MAD2 } if(large_sector_data_blocks) { - div_t large_sectors = div(large_sector_data_blocks, 15); - real_block += large_sectors.quot * 16 + large_sectors.rem; + const size_t large_sector_block_offset = large_sector_data_blocks % 15; + const size_t large_sectors = large_sector_data_blocks / 15; + real_block += large_sectors * 16 + large_sector_block_offset; } - const uint8_t* cur = &ndef->mfc.blocks[real_block].data[small_sector_data_blocks.rem]; + const uint8_t* cur = &ndef->mfc.blocks[real_block].data[real_block_data_offset]; while(len) { size_t sector_trailer = mf_classic_get_sector_trailer_num_by_block(real_block); const uint8_t* end = &ndef->mfc.blocks[sector_trailer].data[0]; - size_t chunk_len = MIN((size_t)(end - cur), len); + const size_t chunk_len = MIN((size_t)(end - cur), len); memcpy(buf, cur, chunk_len); + buf += chunk_len; len -= chunk_len; if(len) { @@ -258,7 +263,9 @@ static inline bool is_printable(char c) { static bool is_text(const uint8_t* buf, size_t len) { for(size_t i = 0; i < len; i++) { - if(!is_printable(buf[i])) return false; + if(!is_printable(buf[i]) && !(buf[i] == '\0' && i == len - 1)) { + return false; + } } return true; } @@ -274,7 +281,7 @@ static bool ndef_dump(Ndef* ndef, const char* prefix, size_t pos, size_t len, bo for(size_t i = 0; i < len; i++) { char c; if(!ndef_get(ndef, pos + i, 1, &c)) return false; - if(!is_printable(c)) { + if(!is_printable(c) && !(c == '\0' && i == len - 1)) { furi_string_left(ndef->output, string_prev); force_hex = true; break; @@ -282,14 +289,18 @@ static bool ndef_dump(Ndef* ndef, const char* prefix, size_t pos, size_t len, bo furi_string_push_back(ndef->output, c); } } - if(force_hex) { - for(size_t i = 0; i < len; i++) { - uint8_t b; - if(!ndef_get(ndef, pos + i, 1, &b)) return false; - furi_string_cat_printf(ndef->output, "%02X ", b); + if(!force_hex) { + furi_string_cat(ndef->output, "\n"); + } else { + uint8_t buf[4]; + for(size_t i = 0; i < len; i += sizeof(buf)) { + uint8_t buf_len = MIN(sizeof(buf), len - i); + if(!ndef_get(ndef, pos + i, buf_len, &buf)) return false; + pretty_format_bytes_hex_canonical( + ndef->output, 4, PRETTY_FORMAT_FONT_MONOSPACE, buf, buf_len); + furi_string_cat(ndef->output, "\n"); } } - furi_string_cat(ndef->output, "\n"); return true; } @@ -299,9 +310,7 @@ static void if(!force_hex && is_text(buf, len)) { furi_string_cat_printf(ndef->output, "%.*s", len, (const char*)buf); } else { - for(size_t i = 0; i < len; i++) { - furi_string_cat_printf(ndef->output, "%02X ", ((const uint8_t*)buf)[i]); - } + pretty_format_bytes_hex_canonical(ndef->output, 4, PRETTY_FORMAT_FONT_MONOSPACE, buf, len); } furi_string_cat(ndef->output, "\n"); } @@ -596,7 +605,7 @@ bool ndef_parse_record( NdefTnf tnf, const char* type, uint8_t type_len) { - FURI_LOG_D(TAG, "payload type: %.*s len: %hu", type_len, type, len); + FURI_LOG_D(TAG, "payload type: %.*s len: %hu pos: %zu", type_len, type, len, pos); if(!len) { furi_string_cat(ndef->output, "Empty\n"); return true; @@ -901,13 +910,13 @@ static bool ndef_mfc_parse(const NfcDevice* device, FuriString* parsed_data) { for(uint8_t mad = 0; mad < COUNT_OF(mads); mad++) { const size_t block = mads[mad].block; const size_t sector = mf_classic_get_sector_by_block(block); - if(sector_count <= sector) break; // Skip this MAD if not present + if(sector_count <= sector) continue; // Skip this MAD if not present // Check MAD key const MfClassicSectorTrailer* sector_trailer = mf_classic_get_sector_trailer_by_sector(data, sector); const uint64_t sector_key_a = bit_lib_bytes_to_num_be( sector_trailer->key_a.data, COUNT_OF(sector_trailer->key_a.data)); - if(sector_key_a != mad_key) return false; + if(sector_key_a != mad_key) continue; // Find NDEF AIDs for(uint8_t aid_index = 0; aid_index < mads[mad].aid_count; aid_index++) { const uint8_t* aid = &data->block[block].data[2 + aid_index * AID_SIZE]; @@ -931,7 +940,7 @@ static bool ndef_mfc_parse(const NfcDevice* device, FuriString* parsed_data) { data_size = 93 + (sector_count - 32) * 15; } else { data_size = sector_count * 3; - if(sector_count >= 16) { + if(sector_count > 16) { data_size -= 3; // Skip MAD2 } } diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_mfkey_complete.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_mfkey_complete.c index 660674ceb..3311ef0e0 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_mfkey_complete.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_mfkey_complete.c @@ -1,4 +1,10 @@ #include "../nfc_app_i.h" +#include "loader/loader.h" + +typedef enum { + NfcSceneMfClassicMfKeyCompleteStateAppMissing, + NfcSceneMfClassicMfKeyCompleteStateAppPresent, +} NfcSceneMfClassicMfKeyCompleteState; void nfc_scene_mf_classic_mfkey_complete_callback( GuiButtonType result, @@ -15,22 +21,47 @@ void nfc_scene_mf_classic_mfkey_complete_on_enter(void* context) { widget_add_string_element( instance->widget, 64, 0, AlignCenter, AlignTop, FontPrimary, "Completed!"); - widget_add_string_multiline_element( - instance->widget, - 64, - 13, - AlignCenter, - AlignTop, - FontSecondary, - "Now use Mfkey32 to extract \nkeys: r.flipper.net/nfc-tools"); - widget_add_icon_element(instance->widget, 50, 39, &I_MFKey_qr_25x25); - widget_add_button_element( - instance->widget, - GuiButtonTypeRight, - "Finish", - nfc_scene_mf_classic_mfkey_complete_callback, - instance); + NfcSceneMfClassicMfKeyCompleteState scene_state = + storage_common_exists(instance->storage, NFC_MFKEY32_APP_PATH) ? + NfcSceneMfClassicMfKeyCompleteStateAppPresent : + NfcSceneMfClassicMfKeyCompleteStateAppMissing; + scene_manager_set_scene_state( + instance->scene_manager, NfcSceneMfClassicMfkeyComplete, scene_state); + + if(scene_state == NfcSceneMfClassicMfKeyCompleteStateAppMissing) { + widget_add_string_multiline_element( + instance->widget, + 64, + 13, + AlignCenter, + AlignTop, + FontSecondary, + "Now use Mfkey32 to extract \nkeys: r.flipper.net/nfc-tools"); + widget_add_icon_element(instance->widget, 50, 39, &I_MFKey_qr_25x25); + widget_add_button_element( + instance->widget, + GuiButtonTypeRight, + "Finish", + nfc_scene_mf_classic_mfkey_complete_callback, + instance); + } else { + widget_add_string_multiline_element( + instance->widget, + 60, + 16, + AlignLeft, + AlignTop, + FontSecondary, + "Now run Mfkey32\n to extract \nkeys"); + widget_add_icon_element(instance->widget, 5, 18, &I_WarningDolphin_45x42); + widget_add_button_element( + instance->widget, + GuiButtonTypeRight, + "Run", + nfc_scene_mf_classic_mfkey_complete_callback, + instance); + } view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget); } @@ -40,8 +71,14 @@ bool nfc_scene_mf_classic_mfkey_complete_on_event(void* context, SceneManagerEve if(event.type == SceneManagerEventTypeCustom) { if(event.event == GuiButtonTypeRight) { - consumed = scene_manager_search_and_switch_to_previous_scene( - instance->scene_manager, NfcSceneStart); + NfcSceneMfClassicMfKeyCompleteState scene_state = scene_manager_get_scene_state( + instance->scene_manager, NfcSceneMfClassicMfkeyComplete); + if(scene_state == NfcSceneMfClassicMfKeyCompleteStateAppMissing) { + consumed = scene_manager_search_and_switch_to_previous_scene( + instance->scene_manager, NfcSceneStart); + } else { + nfc_app_run_external(instance, NFC_MFKEY32_APP_PATH); + } } } else if(event.type == SceneManagerEventTypeBack) { const uint32_t prev_scenes[] = {NfcSceneSavedMenu, NfcSceneStart}; diff --git a/applications/main/nfc/scenes/nfc_scene_save_confirm.c b/applications/main/nfc/scenes/nfc_scene_save_confirm.c index 9d0a206d3..d8fb20642 100644 --- a/applications/main/nfc/scenes/nfc_scene_save_confirm.c +++ b/applications/main/nfc/scenes/nfc_scene_save_confirm.c @@ -29,7 +29,14 @@ bool nfc_scene_save_confirm_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); consumed = true; } else if(event.event == DialogExResultLeft) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicDetectReader); + NfcSceneSaveConfirmState scene_state = + scene_manager_get_scene_state(nfc->scene_manager, NfcSceneSaveConfirm); + + NfcScene scene = scene_state == NfcSceneSaveConfirmStateCrackNonces ? + NfcSceneMfClassicMfkeyComplete : + NfcSceneMfClassicDetectReader; + + scene_manager_next_scene(nfc->scene_manager, scene); consumed = true; } } diff --git a/applications/main/nfc/scenes/nfc_scene_save_success.c b/applications/main/nfc/scenes/nfc_scene_save_success.c index 1c76b3f87..5f812ba9c 100644 --- a/applications/main/nfc/scenes/nfc_scene_save_success.c +++ b/applications/main/nfc/scenes/nfc_scene_save_success.c @@ -29,7 +29,13 @@ bool nfc_scene_save_success_on_event(void* context, SceneManagerEvent event) { consumed = scene_manager_search_and_switch_to_previous_scene( nfc->scene_manager, NfcSceneMfClassicKeys); } else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSaveConfirm)) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicDetectReader); + NfcSceneSaveConfirmState scene_state = + scene_manager_get_scene_state(nfc->scene_manager, NfcSceneSaveConfirm); + + NfcScene scene = scene_state == NfcSceneSaveConfirmStateCrackNonces ? + NfcSceneMfClassicMfkeyComplete : + NfcSceneMfClassicDetectReader; + scene_manager_next_scene(nfc->scene_manager, scene); consumed = true; } else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { consumed = scene_manager_search_and_switch_to_another_scene( diff --git a/applications/main/onewire/application.fam b/applications/main/onewire/application.fam index 3d35abce9..e38bcdfef 100644 --- a/applications/main/onewire/application.fam +++ b/applications/main/onewire/application.fam @@ -1,6 +1,8 @@ App( - appid="onewire_start", - apptype=FlipperAppType.STARTUP, - entry_point="onewire_on_system_start", - order=60, + appid="cli_onewire", + targets=["f7"], + apptype=FlipperAppType.PLUGIN, + entry_point="cli_onewire_ep", + requires=["cli"], + sources=["onewire_cli.c"], ) diff --git a/applications/main/onewire/onewire_cli.c b/applications/main/onewire/onewire_cli.c index 74ca6bc1f..193de76e4 100644 --- a/applications/main/onewire/onewire_cli.c +++ b/applications/main/onewire/onewire_cli.c @@ -1,32 +1,20 @@ #include #include +#include #include - -#include +#include #include #include -static void onewire_cli(Cli* cli, FuriString* args, void* context); - -void onewire_on_system_start(void) { -#ifdef SRV_CLI - Cli* cli = furi_record_open(RECORD_CLI); - cli_add_command(cli, "onewire", CliCommandFlagDefault, onewire_cli, cli); - furi_record_close(RECORD_CLI); -#else - UNUSED(onewire_cli); -#endif -} - static void onewire_cli_print_usage(void) { printf("Usage:\r\n"); printf("onewire search\r\n"); } -static void onewire_cli_search(Cli* cli) { - UNUSED(cli); +static void onewire_cli_search(PipeSide* pipe) { + UNUSED(pipe); OneWireHost* onewire = onewire_host_alloc(&gpio_ibutton); Power* power = furi_record_open(RECORD_POWER); uint8_t address[8]; @@ -58,7 +46,7 @@ static void onewire_cli_search(Cli* cli) { furi_record_close(RECORD_POWER); } -void onewire_cli(Cli* cli, FuriString* args, void* context) { +static void execute(PipeSide* pipe, FuriString* args, void* context) { UNUSED(context); FuriString* cmd; cmd = furi_string_alloc(); @@ -70,8 +58,10 @@ void onewire_cli(Cli* cli, FuriString* args, void* context) { } if(furi_string_cmp_str(cmd, "search") == 0) { - onewire_cli_search(cli); + onewire_cli_search(pipe); } furi_string_free(cmd); } + +CLI_COMMAND_INTERFACE(onewire, execute, CliCommandFlagDefault, 1024, CLI_APPID); diff --git a/applications/main/subghz/application.fam b/applications/main/subghz/application.fam index 1abcf7f54..fe7b07b1e 100644 --- a/applications/main/subghz/application.fam +++ b/applications/main/subghz/application.fam @@ -20,10 +20,10 @@ App( ) App( - appid="subghz_start", + appid="cli_subghz", targets=["f7"], - apptype=FlipperAppType.STARTUP, - entry_point="subghz_on_system_start", + apptype=FlipperAppType.PLUGIN, + entry_point="cli_subghz_ep", + requires=["cli"], sources=["subghz_cli.c", "helpers/subghz_chat.c"], - order=40, ) diff --git a/applications/main/subghz/helpers/subghz_chat.c b/applications/main/subghz/helpers/subghz_chat.c index 9945b69c8..5c55aedeb 100644 --- a/applications/main/subghz/helpers/subghz_chat.c +++ b/applications/main/subghz/helpers/subghz_chat.c @@ -1,5 +1,6 @@ #include "subghz_chat.h" #include +#include #define TAG "SubGhzChat" @@ -14,7 +15,7 @@ struct SubGhzChatWorker { FuriMessageQueue* event_queue; uint32_t last_time_rx_data; - Cli* cli; + PipeSide* pipe; }; /** Worker thread @@ -30,7 +31,7 @@ static int32_t subghz_chat_worker_thread(void* context) { event.event = SubGhzChatEventUserEntrance; furi_message_queue_put(instance->event_queue, &event, 0); while(instance->worker_running) { - if(cli_read_timeout(instance->cli, (uint8_t*)&c, 1, 1000) == 1) { + if(pipe_receive(instance->pipe, (uint8_t*)&c, 1) == 1) { event.event = SubGhzChatEventInputData; event.c = c; furi_message_queue_put(instance->event_queue, &event, FuriWaitForever); @@ -55,10 +56,10 @@ static void subghz_chat_worker_update_rx_event_chat(void* context) { furi_message_queue_put(instance->event_queue, &event, FuriWaitForever); } -SubGhzChatWorker* subghz_chat_worker_alloc(Cli* cli) { +SubGhzChatWorker* subghz_chat_worker_alloc(PipeSide* pipe) { SubGhzChatWorker* instance = malloc(sizeof(SubGhzChatWorker)); - instance->cli = cli; + instance->pipe = pipe; instance->thread = furi_thread_alloc_ex("SubGhzChat", 2048, subghz_chat_worker_thread, instance); diff --git a/applications/main/subghz/helpers/subghz_chat.h b/applications/main/subghz/helpers/subghz_chat.h index 2c454b75d..25fce0ecf 100644 --- a/applications/main/subghz/helpers/subghz_chat.h +++ b/applications/main/subghz/helpers/subghz_chat.h @@ -1,7 +1,7 @@ #pragma once #include "../subghz_i.h" #include -#include +#include typedef struct SubGhzChatWorker SubGhzChatWorker; @@ -19,7 +19,7 @@ typedef struct { char c; } SubGhzChatEvent; -SubGhzChatWorker* subghz_chat_worker_alloc(Cli* cli); +SubGhzChatWorker* subghz_chat_worker_alloc(PipeSide* pipe); void subghz_chat_worker_free(SubGhzChatWorker* instance); bool subghz_chat_worker_start( SubGhzChatWorker* instance, diff --git a/applications/main/subghz/subghz_cli.c b/applications/main/subghz/subghz_cli.c index 54e02196d..674738851 100644 --- a/applications/main/subghz/subghz_cli.c +++ b/applications/main/subghz/subghz_cli.c @@ -4,6 +4,8 @@ #include #include +#include +#include #include #include @@ -16,6 +18,7 @@ #include #include +#include #include "helpers/subghz_chat.h" @@ -61,7 +64,7 @@ static SubGhzEnvironment* subghz_cli_environment_init(void) { return environment; } -void subghz_cli_command_tx_carrier(Cli* cli, FuriString* args, void* context) { +void subghz_cli_command_tx_carrier(PipeSide* pipe, FuriString* args, void* context) { UNUSED(context); uint32_t frequency = 433920000; @@ -91,7 +94,7 @@ void subghz_cli_command_tx_carrier(Cli* cli, FuriString* args, void* context) { if(furi_hal_subghz_tx()) { printf("Transmitting at frequency %lu Hz\r\n", frequency); printf("Press CTRL+C to stop\r\n"); - while(!cli_cmd_interrupt_received(cli)) { + while(!cli_is_pipe_broken_or_is_etx_next_char(pipe)) { furi_delay_ms(250); } } else { @@ -104,7 +107,7 @@ void subghz_cli_command_tx_carrier(Cli* cli, FuriString* args, void* context) { furi_hal_power_suppress_charge_exit(); } -void subghz_cli_command_rx_carrier(Cli* cli, FuriString* args, void* context) { +void subghz_cli_command_rx_carrier(PipeSide* pipe, FuriString* args, void* context) { UNUSED(context); uint32_t frequency = 433920000; @@ -132,7 +135,7 @@ void subghz_cli_command_rx_carrier(Cli* cli, FuriString* args, void* context) { furi_hal_subghz_rx(); - while(!cli_cmd_interrupt_received(cli)) { + while(!cli_is_pipe_broken_or_is_etx_next_char(pipe)) { furi_delay_ms(250); printf("RSSI: %03.1fdbm\r", (double)furi_hal_subghz_get_rssi()); fflush(stdout); @@ -165,7 +168,7 @@ static const SubGhzDevice* subghz_cli_command_get_device(uint32_t* device_ind) { return device; } -void subghz_cli_command_tx(Cli* cli, FuriString* args, void* context) { +void subghz_cli_command_tx(PipeSide* pipe, FuriString* args, void* context) { UNUSED(context); uint32_t frequency = 433920000; uint32_t key = 0x0074BADE; @@ -235,7 +238,9 @@ void subghz_cli_command_tx(Cli* cli, FuriString* args, void* context) { furi_hal_power_suppress_charge_enter(); if(subghz_devices_start_async_tx(device, subghz_transmitter_yield, transmitter)) { - while(!(subghz_devices_is_async_complete_tx(device) || cli_cmd_interrupt_received(cli))) { + while( + !(subghz_devices_is_async_complete_tx(device) || + cli_is_pipe_broken_or_is_etx_next_char(pipe))) { printf("."); fflush(stdout); furi_delay_ms(333); @@ -292,7 +297,7 @@ static void subghz_cli_command_rx_callback( furi_string_free(text); } -void subghz_cli_command_rx(Cli* cli, FuriString* args, void* context) { +void subghz_cli_command_rx(PipeSide* pipe, FuriString* args, void* context) { UNUSED(context); uint32_t frequency = 433920000; uint32_t device_ind = 0; // 0 - CC1101_INT, 1 - CC1101_EXT @@ -348,7 +353,7 @@ void subghz_cli_command_rx(Cli* cli, FuriString* args, void* context) { frequency, device_ind); LevelDuration level_duration; - while(!cli_cmd_interrupt_received(cli)) { + while(!cli_is_pipe_broken_or_is_etx_next_char(pipe)) { int ret = furi_stream_buffer_receive( instance->stream, &level_duration, sizeof(LevelDuration), 10); if(ret == sizeof(LevelDuration)) { @@ -381,7 +386,7 @@ void subghz_cli_command_rx(Cli* cli, FuriString* args, void* context) { free(instance); } -void subghz_cli_command_rx_raw(Cli* cli, FuriString* args, void* context) { +void subghz_cli_command_rx_raw(PipeSide* pipe, FuriString* args, void* context) { UNUSED(context); uint32_t frequency = 433920000; @@ -419,7 +424,7 @@ void subghz_cli_command_rx_raw(Cli* cli, FuriString* args, void* context) { printf("Listening at %lu. Press CTRL+C to stop\r\n", frequency); LevelDuration level_duration; size_t counter = 0; - while(!cli_cmd_interrupt_received(cli)) { + while(!cli_is_pipe_broken_or_is_etx_next_char(pipe)) { int ret = furi_stream_buffer_receive( instance->stream, &level_duration, sizeof(LevelDuration), 10); if(ret == 0) { @@ -455,7 +460,7 @@ void subghz_cli_command_rx_raw(Cli* cli, FuriString* args, void* context) { free(instance); } -void subghz_cli_command_decode_raw(Cli* cli, FuriString* args, void* context) { +void subghz_cli_command_decode_raw(PipeSide* pipe, FuriString* args, void* context) { UNUSED(context); FuriString* file_name; file_name = furi_string_alloc(); @@ -525,7 +530,7 @@ void subghz_cli_command_decode_raw(Cli* cli, FuriString* args, void* context) { furi_string_get_cstr(file_name)); LevelDuration level_duration; - while(!cli_cmd_interrupt_received(cli)) { + while(!cli_is_pipe_broken_or_is_etx_next_char(pipe)) { furi_delay_us(500); //you need to have time to read from the file from the SD card level_duration = subghz_file_encoder_worker_get_level_duration(file_worker_encoder); if(!level_duration_is_reset(level_duration)) { @@ -570,7 +575,7 @@ static FuriHalSubGhzPreset subghz_cli_get_preset_name(const char* preset_name) { return preset; } -void subghz_cli_command_tx_from_file(Cli* cli, FuriString* args, void* context) { // -V524 +void subghz_cli_command_tx_from_file(PipeSide* pipe, FuriString* args, void* context) { // -V524 UNUSED(context); FuriString* file_name; file_name = furi_string_alloc(); @@ -774,7 +779,7 @@ void subghz_cli_command_tx_from_file(Cli* cli, FuriString* args, void* context) if(subghz_devices_start_async_tx(device, subghz_transmitter_yield, transmitter)) { while( !(subghz_devices_is_async_complete_tx(device) || - cli_cmd_interrupt_received(cli))) { + cli_is_pipe_broken_or_is_etx_next_char(pipe))) { printf("."); fflush(stdout); furi_delay_ms(333); @@ -788,11 +793,11 @@ void subghz_cli_command_tx_from_file(Cli* cli, FuriString* args, void* context) if(!strcmp(furi_string_get_cstr(temp_str), "RAW")) { subghz_transmitter_stop(transmitter); repeat--; - if(!cli_cmd_interrupt_received(cli) && repeat) + if(!cli_is_pipe_broken_or_is_etx_next_char(pipe) && repeat) subghz_transmitter_deserialize(transmitter, fff_data_raw); } - } while(!cli_cmd_interrupt_received(cli) && + } while(!cli_is_pipe_broken_or_is_etx_next_char(pipe) && (repeat && !strcmp(furi_string_get_cstr(temp_str), "RAW"))); subghz_devices_sleep(device); @@ -837,8 +842,8 @@ static void subghz_cli_command_print_usage(void) { } } -static void subghz_cli_command_encrypt_keeloq(Cli* cli, FuriString* args) { - UNUSED(cli); +static void subghz_cli_command_encrypt_keeloq(PipeSide* pipe, FuriString* args) { + UNUSED(pipe); uint8_t iv[16]; FuriString* source; @@ -880,8 +885,8 @@ static void subghz_cli_command_encrypt_keeloq(Cli* cli, FuriString* args) { furi_string_free(source); } -static void subghz_cli_command_encrypt_raw(Cli* cli, FuriString* args) { - UNUSED(cli); +static void subghz_cli_command_encrypt_raw(PipeSide* pipe, FuriString* args) { + UNUSED(pipe); uint8_t iv[16]; FuriString* source; @@ -917,7 +922,7 @@ static void subghz_cli_command_encrypt_raw(Cli* cli, FuriString* args) { furi_string_free(source); } -static void subghz_cli_command_chat(Cli* cli, FuriString* args) { +static void subghz_cli_command_chat(PipeSide* pipe, FuriString* args) { uint32_t frequency = 433920000; uint32_t device_ind = 0; // 0 - CC1101_INT, 1 - CC1101_EXT @@ -951,7 +956,7 @@ static void subghz_cli_command_chat(Cli* cli, FuriString* args) { return; } - SubGhzChatWorker* subghz_chat = subghz_chat_worker_alloc(cli); + SubGhzChatWorker* subghz_chat = subghz_chat_worker_alloc(pipe); if(!subghz_chat_worker_start(subghz_chat, device, frequency)) { printf("Startup error SubGhzChatWorker\r\n"); @@ -992,13 +997,12 @@ static void subghz_cli_command_chat(Cli* cli, FuriString* args) { chat_event = subghz_chat_worker_get_event_chat(subghz_chat); switch(chat_event.event) { case SubGhzChatEventInputData: - if(chat_event.c == CliSymbolAsciiETX) { + if(chat_event.c == CliKeyETX) { printf("\r\n"); chat_event.event = SubGhzChatEventUserExit; subghz_chat_worker_put_event_chat(subghz_chat, &chat_event); break; - } else if( - (chat_event.c == CliSymbolAsciiBackspace) || (chat_event.c == CliSymbolAsciiDel)) { + } else if((chat_event.c == CliKeyBackspace) || (chat_event.c == CliKeyDEL)) { size_t len = furi_string_utf8_length(input); if(len > furi_string_utf8_length(name)) { printf("%s", "\e[D\e[1P"); @@ -1020,7 +1024,7 @@ static void subghz_cli_command_chat(Cli* cli, FuriString* args) { } furi_string_set(input, sysmsg); } - } else if(chat_event.c == CliSymbolAsciiCR) { + } else if(chat_event.c == CliKeyCR) { printf("\r\n"); furi_string_push_back(input, '\r'); furi_string_push_back(input, '\n'); @@ -1034,7 +1038,7 @@ static void subghz_cli_command_chat(Cli* cli, FuriString* args) { furi_string_printf(input, "%s", furi_string_get_cstr(name)); printf("%s", furi_string_get_cstr(input)); fflush(stdout); - } else if(chat_event.c == CliSymbolAsciiLF) { + } else if(chat_event.c == CliKeyLF) { //cut out the symbol \n } else { putc(chat_event.c, stdout); @@ -1088,7 +1092,7 @@ static void subghz_cli_command_chat(Cli* cli, FuriString* args) { break; } } - if(!cli_is_connected(cli)) { + if(!cli_is_pipe_broken_or_is_etx_next_char(pipe)) { printf("\r\n"); chat_event.event = SubGhzChatEventUserExit; subghz_chat_worker_put_event_chat(subghz_chat, &chat_event); @@ -1113,7 +1117,7 @@ static void subghz_cli_command_chat(Cli* cli, FuriString* args) { printf("\r\nExit chat\r\n"); } -static void subghz_cli_command(Cli* cli, FuriString* args, void* context) { +static void execute(PipeSide* pipe, FuriString* args, void* context) { FuriString* cmd; cmd = furi_string_alloc(); @@ -1124,53 +1128,53 @@ static void subghz_cli_command(Cli* cli, FuriString* args, void* context) { } if(furi_string_cmp_str(cmd, "chat") == 0) { - subghz_cli_command_chat(cli, args); + subghz_cli_command_chat(pipe, args); break; } if(furi_string_cmp_str(cmd, "tx") == 0) { - subghz_cli_command_tx(cli, args, context); + subghz_cli_command_tx(pipe, args, context); break; } if(furi_string_cmp_str(cmd, "rx") == 0) { - subghz_cli_command_rx(cli, args, context); + subghz_cli_command_rx(pipe, args, context); break; } if(furi_string_cmp_str(cmd, "rx_raw") == 0) { - subghz_cli_command_rx_raw(cli, args, context); + subghz_cli_command_rx_raw(pipe, args, context); break; } if(furi_string_cmp_str(cmd, "decode_raw") == 0) { - subghz_cli_command_decode_raw(cli, args, context); + subghz_cli_command_decode_raw(pipe, args, context); break; } if(furi_string_cmp_str(cmd, "tx_from_file") == 0) { - subghz_cli_command_tx_from_file(cli, args, context); + subghz_cli_command_tx_from_file(pipe, args, context); break; } if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { if(furi_string_cmp_str(cmd, "encrypt_keeloq") == 0) { - subghz_cli_command_encrypt_keeloq(cli, args); + subghz_cli_command_encrypt_keeloq(pipe, args); break; } if(furi_string_cmp_str(cmd, "encrypt_raw") == 0) { - subghz_cli_command_encrypt_raw(cli, args); + subghz_cli_command_encrypt_raw(pipe, args); break; } if(furi_string_cmp_str(cmd, "tx_carrier") == 0) { - subghz_cli_command_tx_carrier(cli, args, context); + subghz_cli_command_tx_carrier(pipe, args, context); break; } if(furi_string_cmp_str(cmd, "rx_carrier") == 0) { - subghz_cli_command_rx_carrier(cli, args, context); + subghz_cli_command_rx_carrier(pipe, args, context); break; } } @@ -1181,14 +1185,4 @@ static void subghz_cli_command(Cli* cli, FuriString* args, void* context) { furi_string_free(cmd); } -void subghz_on_system_start(void) { -#ifdef SRV_CLI - Cli* cli = furi_record_open(RECORD_CLI); - - cli_add_command(cli, "subghz", CliCommandFlagDefault, subghz_cli_command, NULL); - - furi_record_close(RECORD_CLI); -#else - UNUSED(subghz_cli_command); -#endif -} +CLI_COMMAND_INTERFACE(subghz, execute, CliCommandFlagDefault, 2048, CLI_APPID); diff --git a/applications/main/subghz/subghz_cli.h b/applications/main/subghz/subghz_cli.h index f6388218f..275135581 100644 --- a/applications/main/subghz/subghz_cli.h +++ b/applications/main/subghz/subghz_cli.h @@ -1,5 +1,3 @@ #pragma once -#include - void subghz_on_system_start(void); diff --git a/applications/services/application.fam b/applications/services/application.fam index 9ffb26dd6..a1a0429fa 100644 --- a/applications/services/application.fam +++ b/applications/services/application.fam @@ -3,6 +3,7 @@ App( name="Basic services", apptype=FlipperAppType.METAPACKAGE, provides=[ + "cli_vcp", "crypto_start", "rpc_start", "expansion_start", diff --git a/applications/services/bt/application.fam b/applications/services/bt/application.fam index 2d2840e3a..0e4cc918f 100644 --- a/applications/services/bt/application.fam +++ b/applications/services/bt/application.fam @@ -21,5 +21,5 @@ App( appid="bt_start", apptype=FlipperAppType.STARTUP, entry_point="bt_on_system_start", - order=70, + order=40, ) diff --git a/applications/services/bt/bt_cli.c b/applications/services/bt/bt_cli.c index 7505c424d..d81bc0505 100644 --- a/applications/services/bt/bt_cli.c +++ b/applications/services/bt/bt_cli.c @@ -1,15 +1,17 @@ #include #include -#include #include +#include +#include +#include #include #include "bt_settings.h" #include "bt_service/bt.h" #include -static void bt_cli_command_hci_info(Cli* cli, FuriString* args, void* context) { - UNUSED(cli); +static void bt_cli_command_hci_info(PipeSide* pipe, FuriString* args, void* context) { + UNUSED(pipe); UNUSED(args); UNUSED(context); FuriString* buffer; @@ -19,7 +21,7 @@ static void bt_cli_command_hci_info(Cli* cli, FuriString* args, void* context) { furi_string_free(buffer); } -static void bt_cli_command_carrier_tx(Cli* cli, FuriString* args, void* context) { +static void bt_cli_command_carrier_tx(PipeSide* pipe, FuriString* args, void* context) { UNUSED(context); int channel = 0; int power = 0; @@ -41,7 +43,7 @@ static void bt_cli_command_carrier_tx(Cli* cli, FuriString* args, void* context) printf("Press CTRL+C to stop\r\n"); furi_hal_bt_start_tone_tx(channel, 0x19 + power); - while(!cli_cmd_interrupt_received(cli)) { + while(!cli_is_pipe_broken_or_is_etx_next_char(pipe)) { furi_delay_ms(250); } furi_hal_bt_stop_tone_tx(); @@ -51,7 +53,7 @@ static void bt_cli_command_carrier_tx(Cli* cli, FuriString* args, void* context) } while(false); } -static void bt_cli_command_carrier_rx(Cli* cli, FuriString* args, void* context) { +static void bt_cli_command_carrier_rx(PipeSide* pipe, FuriString* args, void* context) { UNUSED(context); int channel = 0; @@ -69,7 +71,7 @@ static void bt_cli_command_carrier_rx(Cli* cli, FuriString* args, void* context) furi_hal_bt_start_packet_rx(channel, 1); - while(!cli_cmd_interrupt_received(cli)) { + while(!cli_is_pipe_broken_or_is_etx_next_char(pipe)) { furi_delay_ms(250); printf("RSSI: %6.1f dB\r", (double)furi_hal_bt_get_rssi()); fflush(stdout); @@ -82,7 +84,7 @@ static void bt_cli_command_carrier_rx(Cli* cli, FuriString* args, void* context) } while(false); } -static void bt_cli_command_packet_tx(Cli* cli, FuriString* args, void* context) { +static void bt_cli_command_packet_tx(PipeSide* pipe, FuriString* args, void* context) { UNUSED(context); int channel = 0; int pattern = 0; @@ -119,7 +121,7 @@ static void bt_cli_command_packet_tx(Cli* cli, FuriString* args, void* context) printf("Press CTRL+C to stop\r\n"); furi_hal_bt_start_packet_tx(channel, pattern, datarate); - while(!cli_cmd_interrupt_received(cli)) { + while(!cli_is_pipe_broken_or_is_etx_next_char(pipe)) { furi_delay_ms(250); } furi_hal_bt_stop_packet_test(); @@ -130,7 +132,7 @@ static void bt_cli_command_packet_tx(Cli* cli, FuriString* args, void* context) } while(false); } -static void bt_cli_command_packet_rx(Cli* cli, FuriString* args, void* context) { +static void bt_cli_command_packet_rx(PipeSide* pipe, FuriString* args, void* context) { UNUSED(context); int channel = 0; int datarate = 1; @@ -152,7 +154,7 @@ static void bt_cli_command_packet_rx(Cli* cli, FuriString* args, void* context) printf("Press CTRL+C to stop\r\n"); furi_hal_bt_start_packet_rx(channel, datarate); - while(!cli_cmd_interrupt_received(cli)) { + while(!cli_is_pipe_broken_or_is_etx_next_char(pipe)) { furi_delay_ms(250); printf("RSSI: %03.1f dB\r", (double)furi_hal_bt_get_rssi()); fflush(stdout); @@ -179,7 +181,7 @@ static void bt_cli_print_usage(void) { } } -static void bt_cli(Cli* cli, FuriString* args, void* context) { +static void bt_cli(PipeSide* pipe, FuriString* args, void* context) { UNUSED(context); furi_record_open(RECORD_BT); @@ -194,24 +196,24 @@ static void bt_cli(Cli* cli, FuriString* args, void* context) { break; } if(furi_string_cmp_str(cmd, "hci_info") == 0) { - bt_cli_command_hci_info(cli, args, NULL); + bt_cli_command_hci_info(pipe, args, NULL); break; } if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug) && furi_hal_bt_is_testing_supported()) { if(furi_string_cmp_str(cmd, "tx_carrier") == 0) { - bt_cli_command_carrier_tx(cli, args, NULL); + bt_cli_command_carrier_tx(pipe, args, NULL); break; } if(furi_string_cmp_str(cmd, "rx_carrier") == 0) { - bt_cli_command_carrier_rx(cli, args, NULL); + bt_cli_command_carrier_rx(pipe, args, NULL); break; } if(furi_string_cmp_str(cmd, "tx_packet") == 0) { - bt_cli_command_packet_tx(cli, args, NULL); + bt_cli_command_packet_tx(pipe, args, NULL); break; } if(furi_string_cmp_str(cmd, "rx_packet") == 0) { - bt_cli_command_packet_rx(cli, args, NULL); + bt_cli_command_packet_rx(pipe, args, NULL); break; } } @@ -229,8 +231,8 @@ static void bt_cli(Cli* cli, FuriString* args, void* context) { void bt_on_system_start(void) { #ifdef SRV_CLI - Cli* cli = furi_record_open(RECORD_CLI); - cli_add_command(cli, RECORD_BT, CliCommandFlagDefault, bt_cli, NULL); + CliRegistry* registry = furi_record_open(RECORD_CLI); + cli_registry_add_command(registry, "bt", CliCommandFlagDefault, bt_cli, NULL); furi_record_close(RECORD_CLI); #else UNUSED(bt_cli); diff --git a/applications/services/cli/application.fam b/applications/services/cli/application.fam index 7a57bb607..6e0a28164 100644 --- a/applications/services/cli/application.fam +++ b/applications/services/cli/application.fam @@ -1,10 +1,51 @@ App( appid="cli", - name="CliSrv", - apptype=FlipperAppType.SERVICE, - entry_point="cli_srv", + apptype=FlipperAppType.STARTUP, + entry_point="cli_on_system_start", cdefines=["SRV_CLI"], - stack_size=4 * 1024, - order=30, - sdk_headers=["cli.h", "cli_vcp.h"], + sources=[ + "cli_command_gpio.c", + "cli_main_commands.c", + "cli_main_shell.c", + ], + # This STARTUP has to be processed before those that depend on the "cli" record. + # "cli" used to be a SERVICE, but it's been converted into a STARTUP in order to + # reduce RAM usage. The "block until record has been created" mechanism + # unfortunately leads to a deadlock if the STARTUPs are processed sequentially. + order=0, +) + +App( + appid="cli_vcp", + name="CliVcpSrv", + apptype=FlipperAppType.SERVICE, + entry_point="cli_vcp_srv", + stack_size=1024, + order=10, + sdk_headers=["cli_vcp.h"], + sources=["cli_vcp.c"], +) + +App( + appid="cli_hello_world", + apptype=FlipperAppType.PLUGIN, + entry_point="cli_hello_world_ep", + requires=["cli"], + sources=["commands/hello_world.c"], +) + +App( + appid="cli_neofetch", + apptype=FlipperAppType.PLUGIN, + entry_point="cli_neofetch_ep", + requires=["cli"], + sources=["commands/neofetch.c"], +) + +App( + appid="cli_subshell_demo", + apptype=FlipperAppType.PLUGIN, + entry_point="cli_subshell_demo_ep", + requires=["cli"], + sources=["commands/subshell_demo.c"], ) diff --git a/applications/services/cli/cli.c b/applications/services/cli/cli.c deleted file mode 100644 index 28ba417c2..000000000 --- a/applications/services/cli/cli.c +++ /dev/null @@ -1,484 +0,0 @@ -#include "cli_i.h" -#include "cli_commands.h" -#include "cli_vcp.h" -#include -#include - -#define TAG "CliSrv" - -#define CLI_INPUT_LEN_LIMIT 256 - -Cli* cli_alloc(void) { - Cli* cli = malloc(sizeof(Cli)); - - CliCommandTree_init(cli->commands); - - cli->last_line = furi_string_alloc(); - cli->line = furi_string_alloc(); - - cli->session = NULL; - - cli->mutex = furi_mutex_alloc(FuriMutexTypeNormal); - - cli->idle_sem = furi_semaphore_alloc(1, 0); - - return cli; -} - -void cli_putc(Cli* cli, char c) { - furi_check(cli); - if(cli->session != NULL) { - cli->session->tx((uint8_t*)&c, 1); - } -} - -char cli_getc(Cli* cli) { - furi_check(cli); - char c = 0; - if(cli->session != NULL) { - if(cli->session->rx((uint8_t*)&c, 1, FuriWaitForever) == 0) { - cli_reset(cli); - furi_delay_tick(10); - } - } else { - cli_reset(cli); - furi_delay_tick(10); - } - return c; -} - -void cli_write(Cli* cli, const uint8_t* buffer, size_t size) { - furi_check(cli); - if(cli->session != NULL) { - cli->session->tx(buffer, size); - } -} - -size_t cli_read(Cli* cli, uint8_t* buffer, size_t size) { - furi_check(cli); - if(cli->session != NULL) { - return cli->session->rx(buffer, size, FuriWaitForever); - } else { - return 0; - } -} - -size_t cli_read_timeout(Cli* cli, uint8_t* buffer, size_t size, uint32_t timeout) { - furi_check(cli); - if(cli->session != NULL) { - return cli->session->rx(buffer, size, timeout); - } else { - return 0; - } -} - -bool cli_is_connected(Cli* cli) { - furi_check(cli); - if(cli->session != NULL) { - return cli->session->is_connected(); - } - return false; -} - -bool cli_cmd_interrupt_received(Cli* cli) { - furi_check(cli); - char c = '\0'; - if(cli_is_connected(cli)) { - if(cli->session->rx((uint8_t*)&c, 1, 0) == 1) { - return c == CliSymbolAsciiETX; - } - } else { - return true; - } - return false; -} - -void cli_print_usage(const char* cmd, const char* usage, const char* arg) { - furi_check(cmd); - furi_check(arg); - furi_check(usage); - - printf("%s: illegal option -- %s\r\nusage: %s %s", cmd, arg, cmd, usage); -} - -void cli_motd(void) { - printf("\r\n" - " _.-------.._ -,\r\n" - " .-\"```\"--..,,_/ /`-, -, \\ \r\n" - " .:\" /:/ /'\\ \\ ,_..., `. | |\r\n" - " / ,----/:/ /`\\ _\\~`_-\"` _;\r\n" - " ' / /`\"\"\"'\\ \\ \\.~`_-' ,-\"'/ \r\n" - " | | | 0 | | .-' ,/` /\r\n" - " | ,..\\ \\ ,.-\"` ,/` /\r\n" - " ; : `/`\"\"\\` ,/--==,/-----,\r\n" - " | `-...| -.___-Z:_______J...---;\r\n" - " : ` _-'\r\n" - " _L_ _ ___ ___ ___ ___ ____--\"`___ _ ___\r\n" - "| __|| | |_ _|| _ \\| _ \\| __|| _ \\ / __|| | |_ _|\r\n" - "| _| | |__ | | | _/| _/| _| | / | (__ | |__ | |\r\n" - "|_| |____||___||_| |_| |___||_|_\\ \\___||____||___|\r\n" - "\r\n" - "Welcome to Flipper Zero Command Line Interface!\r\n" - "Read the manual: https://docs.flipper.net/development/cli\r\n" - "Run `help` or `?` to list available commands\r\n" - "\r\n"); - - const Version* firmware_version = furi_hal_version_get_firmware_version(); - if(firmware_version) { - printf( - "Firmware version: %s %s (%s%s built on %s)\r\n", - version_get_gitbranch(firmware_version), - version_get_version(firmware_version), - version_get_githash(firmware_version), - version_get_dirty_flag(firmware_version) ? "-dirty" : "", - version_get_builddate(firmware_version)); - } -} - -void cli_nl(Cli* cli) { - UNUSED(cli); - printf("\r\n"); -} - -void cli_prompt(Cli* cli) { - UNUSED(cli); - printf("\r\n>: %s", furi_string_get_cstr(cli->line)); - fflush(stdout); -} - -void cli_reset(Cli* cli) { - // cli->last_line is cleared and cli->line's buffer moved to cli->last_line - furi_string_move(cli->last_line, cli->line); - // Reiniting cli->line - cli->line = furi_string_alloc(); - cli->cursor_position = 0; -} - -static void cli_handle_backspace(Cli* cli) { - if(cli->cursor_position > 0) { - furi_assert(furi_string_size(cli->line) > 0); - // Other side - printf("\e[D\e[1P"); - fflush(stdout); - // Our side - furi_string_replace_at(cli->line, cli->cursor_position - 1, 1, ""); - - cli->cursor_position--; - } else { - cli_putc(cli, CliSymbolAsciiBell); - } -} - -static void cli_normalize_line(Cli* cli) { - furi_string_trim(cli->line); - cli->cursor_position = furi_string_size(cli->line); -} - -static void cli_execute_command(Cli* cli, CliCommand* command, FuriString* args) { - if(!(command->flags & CliCommandFlagInsomniaSafe)) { - furi_hal_power_insomnia_enter(); - } - - // Ensure that we running alone - if(!(command->flags & CliCommandFlagParallelSafe)) { - Loader* loader = furi_record_open(RECORD_LOADER); - bool safety_lock = loader_lock(loader); - if(safety_lock) { - // Execute command - command->callback(cli, args, command->context); - loader_unlock(loader); - } else { - printf("Other application is running, close it first"); - } - furi_record_close(RECORD_LOADER); - } else { - // Execute command - command->callback(cli, args, command->context); - } - - if(!(command->flags & CliCommandFlagInsomniaSafe)) { - furi_hal_power_insomnia_exit(); - } -} - -static void cli_handle_enter(Cli* cli) { - cli_normalize_line(cli); - - if(furi_string_size(cli->line) == 0) { - cli_prompt(cli); - return; - } - - // Command and args container - FuriString* command; - command = furi_string_alloc(); - FuriString* args; - args = furi_string_alloc(); - - // Split command and args - size_t ws = furi_string_search_char(cli->line, ' '); - if(ws == FURI_STRING_FAILURE) { - furi_string_set(command, cli->line); - } else { - furi_string_set_n(command, cli->line, 0, ws); - furi_string_set_n(args, cli->line, ws, furi_string_size(cli->line)); - furi_string_trim(args); - } - - // Search for command - furi_check(furi_mutex_acquire(cli->mutex, FuriWaitForever) == FuriStatusOk); - CliCommand* cli_command_ptr = CliCommandTree_get(cli->commands, command); - - if(cli_command_ptr) { //-V547 - CliCommand cli_command; - memcpy(&cli_command, cli_command_ptr, sizeof(CliCommand)); - furi_check(furi_mutex_release(cli->mutex) == FuriStatusOk); - cli_nl(cli); - cli_execute_command(cli, &cli_command, args); - } else { - furi_check(furi_mutex_release(cli->mutex) == FuriStatusOk); - cli_nl(cli); - printf( - "`%s` command not found, use `help` or `?` to list all available commands", - furi_string_get_cstr(command)); - cli_putc(cli, CliSymbolAsciiBell); - } - - cli_reset(cli); - cli_prompt(cli); - - // Cleanup command and args - furi_string_free(command); - furi_string_free(args); -} - -static void cli_handle_autocomplete(Cli* cli) { - cli_normalize_line(cli); - - if(furi_string_size(cli->line) == 0) { - return; - } - - cli_nl(cli); - - // Prepare common base for autocomplete - FuriString* common; - common = furi_string_alloc(); - // Iterate throw commands - for - M_EACH(cli_command, cli->commands, CliCommandTree_t) { - // Process only if starts with line buffer - if(furi_string_start_with(*cli_command->key_ptr, cli->line)) { - // Show autocomplete option - printf("%s\r\n", furi_string_get_cstr(*cli_command->key_ptr)); - // Process common base for autocomplete - if(furi_string_size(common) > 0) { - // Choose shortest string - const size_t key_size = furi_string_size(*cli_command->key_ptr); - const size_t common_size = furi_string_size(common); - const size_t min_size = key_size > common_size ? common_size : key_size; - size_t i = 0; - while(i < min_size) { - // Stop when do not match - if(furi_string_get_char(*cli_command->key_ptr, i) != - furi_string_get_char(common, i)) { - break; - } - i++; - } - // Cut right part if any - furi_string_left(common, i); - } else { - // Start with something - furi_string_set(common, *cli_command->key_ptr); - } - } - } - // Replace line buffer if autocomplete better - if(furi_string_size(common) > furi_string_size(cli->line)) { - furi_string_set(cli->line, common); - cli->cursor_position = furi_string_size(cli->line); - } - // Cleanup - furi_string_free(common); - // Show prompt - cli_prompt(cli); -} - -static void cli_handle_escape(Cli* cli, char c) { - if(c == 'A') { - // Use previous command if line buffer is empty - if(furi_string_size(cli->line) == 0 && furi_string_cmp(cli->line, cli->last_line) != 0) { - // Set line buffer and cursor position - furi_string_set(cli->line, cli->last_line); - cli->cursor_position = furi_string_size(cli->line); - // Show new line to user - printf("%s", furi_string_get_cstr(cli->line)); - } - } else if(c == 'B') { - } else if(c == 'C') { - if(cli->cursor_position < furi_string_size(cli->line)) { - cli->cursor_position++; - printf("\e[C"); - } - } else if(c == 'D') { - if(cli->cursor_position > 0) { - cli->cursor_position--; - printf("\e[D"); - } - } - fflush(stdout); -} - -void cli_process_input(Cli* cli) { - char in_chr = cli_getc(cli); - size_t rx_len; - - if(in_chr == CliSymbolAsciiTab) { - cli_handle_autocomplete(cli); - } else if(in_chr == CliSymbolAsciiSOH) { - furi_delay_ms(33); // We are too fast, Minicom is not ready yet - cli_motd(); - cli_prompt(cli); - } else if(in_chr == CliSymbolAsciiETX) { - cli_reset(cli); - cli_prompt(cli); - } else if(in_chr == CliSymbolAsciiEOT) { - cli_reset(cli); - } else if(in_chr == CliSymbolAsciiEsc) { - rx_len = cli_read(cli, (uint8_t*)&in_chr, 1); - if((rx_len > 0) && (in_chr == '[')) { - cli_read(cli, (uint8_t*)&in_chr, 1); - cli_handle_escape(cli, in_chr); - } else { - cli_putc(cli, CliSymbolAsciiBell); - } - } else if(in_chr == CliSymbolAsciiBackspace || in_chr == CliSymbolAsciiDel) { - cli_handle_backspace(cli); - } else if(in_chr == CliSymbolAsciiCR) { - cli_handle_enter(cli); - } else if( - (in_chr >= 0x20 && in_chr < 0x7F) && //-V560 - (furi_string_size(cli->line) < CLI_INPUT_LEN_LIMIT)) { - if(cli->cursor_position == furi_string_size(cli->line)) { - furi_string_push_back(cli->line, in_chr); - cli_putc(cli, in_chr); - } else { - // Insert character to line buffer - const char in_str[2] = {in_chr, 0}; - furi_string_replace_at(cli->line, cli->cursor_position, 0, in_str); - - // Print character in replace mode - printf("\e[4h%c\e[4l", in_chr); - fflush(stdout); - } - cli->cursor_position++; - } else { - cli_putc(cli, CliSymbolAsciiBell); - } -} - -void cli_add_command( - Cli* cli, - const char* name, - CliCommandFlag flags, - CliCallback callback, - void* context) { - furi_check(cli); - FuriString* name_str; - name_str = furi_string_alloc_set(name); - furi_string_trim(name_str); - - size_t name_replace; - do { - name_replace = furi_string_replace(name_str, " ", "_"); - } while(name_replace != FURI_STRING_FAILURE); - - CliCommand c; - c.callback = callback; - c.context = context; - c.flags = flags; - - furi_check(furi_mutex_acquire(cli->mutex, FuriWaitForever) == FuriStatusOk); - CliCommandTree_set_at(cli->commands, name_str, c); - furi_check(furi_mutex_release(cli->mutex) == FuriStatusOk); - - furi_string_free(name_str); -} - -void cli_delete_command(Cli* cli, const char* name) { - furi_check(cli); - FuriString* name_str; - name_str = furi_string_alloc_set(name); - furi_string_trim(name_str); - - size_t name_replace; - do { - name_replace = furi_string_replace(name_str, " ", "_"); - } while(name_replace != FURI_STRING_FAILURE); - - furi_check(furi_mutex_acquire(cli->mutex, FuriWaitForever) == FuriStatusOk); - CliCommandTree_erase(cli->commands, name_str); - furi_check(furi_mutex_release(cli->mutex) == FuriStatusOk); - - furi_string_free(name_str); -} - -void cli_session_open(Cli* cli, void* session) { - furi_check(cli); - - furi_check(furi_mutex_acquire(cli->mutex, FuriWaitForever) == FuriStatusOk); - cli->session = session; - if(cli->session != NULL) { - cli->session->init(); - furi_thread_set_stdout_callback(cli->session->tx_stdout, NULL); - } else { - furi_thread_set_stdout_callback(NULL, NULL); - } - furi_semaphore_release(cli->idle_sem); - furi_check(furi_mutex_release(cli->mutex) == FuriStatusOk); -} - -void cli_session_close(Cli* cli) { - furi_check(cli); - - furi_check(furi_mutex_acquire(cli->mutex, FuriWaitForever) == FuriStatusOk); - if(cli->session != NULL) { - cli->session->deinit(); - } - cli->session = NULL; - furi_thread_set_stdout_callback(NULL, NULL); - furi_check(furi_mutex_release(cli->mutex) == FuriStatusOk); -} - -int32_t cli_srv(void* p) { - UNUSED(p); - Cli* cli = cli_alloc(); - - // Init basic cli commands - cli_commands_init(cli); - - furi_record_create(RECORD_CLI, cli); - - if(cli->session != NULL) { - furi_thread_set_stdout_callback(cli->session->tx_stdout, NULL); - } else { - furi_thread_set_stdout_callback(NULL, NULL); - } - - if(furi_hal_rtc_get_boot_mode() == FuriHalRtcBootModeNormal) { - cli_session_open(cli, &cli_vcp); - } else { - FURI_LOG_W(TAG, "Skipping start in special boot mode"); - } - - while(1) { - if(cli->session != NULL) { - cli_process_input(cli); - } else { - furi_check(furi_semaphore_acquire(cli->idle_sem, FuriWaitForever) == FuriStatusOk); - } - } - - return 0; -} diff --git a/applications/services/cli/cli.h b/applications/services/cli/cli.h deleted file mode 100644 index bb84670a7..000000000 --- a/applications/services/cli/cli.h +++ /dev/null @@ -1,134 +0,0 @@ -/** - * @file cli.h - * Cli API - */ - -#pragma once -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef enum { - CliSymbolAsciiSOH = 0x01, - CliSymbolAsciiETX = 0x03, - CliSymbolAsciiEOT = 0x04, - CliSymbolAsciiBell = 0x07, - CliSymbolAsciiBackspace = 0x08, - CliSymbolAsciiTab = 0x09, - CliSymbolAsciiLF = 0x0A, - CliSymbolAsciiCR = 0x0D, - CliSymbolAsciiEsc = 0x1B, - CliSymbolAsciiUS = 0x1F, - CliSymbolAsciiSpace = 0x20, - CliSymbolAsciiDel = 0x7F, -} CliSymbols; - -typedef enum { - CliCommandFlagDefault = 0, /**< Default, loader lock is used */ - CliCommandFlagParallelSafe = - (1 << 0), /**< Safe to run in parallel with other apps, loader lock is not used */ - CliCommandFlagInsomniaSafe = (1 << 1), /**< Safe to run with insomnia mode on */ -} CliCommandFlag; - -#define RECORD_CLI "cli" - -/** Cli type anonymous structure */ -typedef struct Cli Cli; - -/** Cli callback function pointer. Implement this interface and use - * add_cli_command - * @param args string with what was passed after command - * @param context pointer to whatever you gave us on cli_add_command - */ -typedef void (*CliCallback)(Cli* cli, FuriString* args, void* context); - -/** Add cli command Registers you command callback - * - * @param cli pointer to cli instance - * @param name command name - * @param flags CliCommandFlag - * @param callback callback function - * @param context pointer to whatever we need to pass to callback - */ -void cli_add_command( - Cli* cli, - const char* name, - CliCommandFlag flags, - CliCallback callback, - void* context); - -/** Print unified cmd usage tip - * - * @param cmd cmd name - * @param usage usage tip - * @param arg arg passed by user - */ -void cli_print_usage(const char* cmd, const char* usage, const char* arg); - -/** Delete cli command - * - * @param cli pointer to cli instance - * @param name command name - */ -void cli_delete_command(Cli* cli, const char* name); - -/** Read from terminal - * - * @param cli Cli instance - * @param buffer pointer to buffer - * @param size size of buffer in bytes - * - * @return bytes read - */ -size_t cli_read(Cli* cli, uint8_t* buffer, size_t size); - -/** Non-blocking read from terminal - * - * @param cli Cli instance - * @param buffer pointer to buffer - * @param size size of buffer in bytes - * @param timeout timeout value in ms - * - * @return bytes read - */ -size_t cli_read_timeout(Cli* cli, uint8_t* buffer, size_t size, uint32_t timeout); - -/** Non-blocking check for interrupt command received - * - * @param cli Cli instance - * - * @return true if received - */ -bool cli_cmd_interrupt_received(Cli* cli); - -/** Write to terminal Do it only from inside of cli call. - * - * @param cli Cli instance - * @param buffer pointer to buffer - * @param size size of buffer in bytes - */ -void cli_write(Cli* cli, const uint8_t* buffer, size_t size); - -/** Read character - * - * @param cli Cli instance - * - * @return char - */ -char cli_getc(Cli* cli); - -/** New line Send new ine sequence - */ -void cli_nl(Cli* cli); - -void cli_session_open(Cli* cli, void* session); - -void cli_session_close(Cli* cli); - -bool cli_is_connected(Cli* cli); - -#ifdef __cplusplus -} -#endif diff --git a/applications/services/cli/cli_command_gpio.c b/applications/services/cli/cli_command_gpio.c index 010a7dfbe..f6337265d 100644 --- a/applications/services/cli/cli_command_gpio.c +++ b/applications/services/cli/cli_command_gpio.c @@ -3,6 +3,8 @@ #include #include #include +#include +#include void cli_command_gpio_print_usage(void) { printf("Usage:\r\n"); @@ -70,8 +72,8 @@ static GpioParseReturn gpio_command_parse(FuriString* args, size_t* pin_num, uin return ret; } -void cli_command_gpio_mode(Cli* cli, FuriString* args, void* context) { - UNUSED(cli); +void cli_command_gpio_mode(PipeSide* pipe, FuriString* args, void* context) { + UNUSED(pipe); UNUSED(context); size_t num = 0; @@ -93,7 +95,7 @@ void cli_command_gpio_mode(Cli* cli, FuriString* args, void* context) { if(gpio_pins[num].debug) { //-V779 printf( "Changing this pin mode may damage hardware. Are you sure you want to continue? (y/n)?\r\n"); - char c = cli_getc(cli); + char c = getchar(); if(c != 'y' && c != 'Y') { printf("Cancelled.\r\n"); return; @@ -110,8 +112,8 @@ void cli_command_gpio_mode(Cli* cli, FuriString* args, void* context) { } } -void cli_command_gpio_read(Cli* cli, FuriString* args, void* context) { - UNUSED(cli); +void cli_command_gpio_read(PipeSide* pipe, FuriString* args, void* context) { + UNUSED(pipe); UNUSED(context); size_t num = 0; @@ -131,7 +133,8 @@ void cli_command_gpio_read(Cli* cli, FuriString* args, void* context) { printf("Pin %s <= %u", gpio_pins[num].name, val); } -void cli_command_gpio_set(Cli* cli, FuriString* args, void* context) { +void cli_command_gpio_set(PipeSide* pipe, FuriString* args, void* context) { + UNUSED(pipe); UNUSED(context); size_t num = 0; @@ -159,7 +162,7 @@ void cli_command_gpio_set(Cli* cli, FuriString* args, void* context) { if(gpio_pins[num].debug) { printf( "Setting this pin may damage hardware. Are you sure you want to continue? (y/n)?\r\n"); - char c = cli_getc(cli); + char c = getchar(); if(c != 'y' && c != 'Y') { printf("Cancelled.\r\n"); return; @@ -170,7 +173,7 @@ void cli_command_gpio_set(Cli* cli, FuriString* args, void* context) { printf("Pin %s => %u", gpio_pins[num].name, !!value); } -void cli_command_gpio(Cli* cli, FuriString* args, void* context) { +void cli_command_gpio(PipeSide* pipe, FuriString* args, void* context) { FuriString* cmd; cmd = furi_string_alloc(); @@ -181,17 +184,17 @@ void cli_command_gpio(Cli* cli, FuriString* args, void* context) { } if(furi_string_cmp_str(cmd, "mode") == 0) { - cli_command_gpio_mode(cli, args, context); + cli_command_gpio_mode(pipe, args, context); break; } if(furi_string_cmp_str(cmd, "set") == 0) { - cli_command_gpio_set(cli, args, context); + cli_command_gpio_set(pipe, args, context); break; } if(furi_string_cmp_str(cmd, "read") == 0) { - cli_command_gpio_read(cli, args, context); + cli_command_gpio_read(pipe, args, context); break; } diff --git a/applications/services/cli/cli_command_gpio.h b/applications/services/cli/cli_command_gpio.h index 7ae5aa625..c1911fb65 100644 --- a/applications/services/cli/cli_command_gpio.h +++ b/applications/services/cli/cli_command_gpio.h @@ -1,5 +1,5 @@ #pragma once -#include "cli_i.h" +#include -void cli_command_gpio(Cli* cli, FuriString* args, void* context); +void cli_command_gpio(PipeSide* pipe, FuriString* args, void* context); diff --git a/applications/services/cli/cli_commands.h b/applications/services/cli/cli_commands.h deleted file mode 100644 index 184eeb373..000000000 --- a/applications/services/cli/cli_commands.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -#include "cli_i.h" - -void cli_commands_init(Cli* cli); diff --git a/applications/services/cli/cli_i.h b/applications/services/cli/cli_i.h deleted file mode 100644 index d7351b9ff..000000000 --- a/applications/services/cli/cli_i.h +++ /dev/null @@ -1,68 +0,0 @@ -#pragma once - -#include "cli.h" - -#include -#include - -#include -#include -#include - -#include "cli_vcp.h" - -#define CLI_LINE_SIZE_MAX -#define CLI_COMMANDS_TREE_RANK 4 - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct { - CliCallback callback; - void* context; - uint32_t flags; -} CliCommand; - -struct CliSession { - void (*init)(void); - void (*deinit)(void); - size_t (*rx)(uint8_t* buffer, size_t size, uint32_t timeout); - size_t (*rx_stdin)(uint8_t* buffer, size_t size, uint32_t timeout, void* context); - void (*tx)(const uint8_t* buffer, size_t size); - void (*tx_stdout)(const char* data, size_t size, void* context); - bool (*is_connected)(void); -}; - -BPTREE_DEF2( - CliCommandTree, - CLI_COMMANDS_TREE_RANK, - FuriString*, - FURI_STRING_OPLIST, - CliCommand, - M_POD_OPLIST) - -#define M_OPL_CliCommandTree_t() BPTREE_OPLIST(CliCommandTree, M_POD_OPLIST) - -struct Cli { - CliCommandTree_t commands; - FuriMutex* mutex; - FuriSemaphore* idle_sem; - FuriString* last_line; - FuriString* line; - CliSession* session; - - size_t cursor_position; -}; - -Cli* cli_alloc(void); - -void cli_reset(Cli* cli); - -void cli_putc(Cli* cli, char c); - -void cli_stdout_callback(void* _cookie, const char* data, size_t size); - -#ifdef __cplusplus -} -#endif diff --git a/applications/services/cli/cli_commands.c b/applications/services/cli/cli_main_commands.c similarity index 74% rename from applications/services/cli/cli_commands.c rename to applications/services/cli/cli_main_commands.c index e4503b274..f84c03d8a 100644 --- a/applications/services/cli/cli_commands.c +++ b/applications/services/cli/cli_main_commands.c @@ -1,5 +1,6 @@ -#include "cli_commands.h" +#include "cli_main_commands.h" #include "cli_command_gpio.h" +#include #include #include @@ -11,6 +12,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" @@ -34,8 +36,8 @@ void cli_command_info_callback(const char* key, const char* value, bool last, vo * @param args The arguments * @param context The context */ -void cli_command_info(Cli* cli, FuriString* args, void* context) { - UNUSED(cli); +void cli_command_info(PipeSide* pipe, FuriString* args, void* context) { + UNUSED(pipe); if(context) { furi_hal_info_get(cli_command_info_callback, '_', NULL); @@ -53,56 +55,16 @@ void cli_command_info(Cli* cli, FuriString* args, void* context) { } } -void cli_command_help(Cli* cli, FuriString* args, void* context) { - UNUSED(args); - UNUSED(context); - printf("Commands available:"); - - // Command count - const size_t commands_count = CliCommandTree_size(cli->commands); - const size_t commands_count_mid = commands_count / 2 + commands_count % 2; - - // Use 2 iterators from start and middle to show 2 columns - CliCommandTree_it_t it_left; - CliCommandTree_it(it_left, cli->commands); - CliCommandTree_it_t it_right; - CliCommandTree_it(it_right, cli->commands); - for(size_t i = 0; i < commands_count_mid; i++) - CliCommandTree_next(it_right); - - // Iterate throw tree - for(size_t i = 0; i < commands_count_mid; i++) { - printf("\r\n"); - // Left Column - if(!CliCommandTree_end_p(it_left)) { - printf("%-30s", furi_string_get_cstr(*CliCommandTree_ref(it_left)->key_ptr)); - CliCommandTree_next(it_left); - } - // Right Column - if(!CliCommandTree_end_p(it_right)) { - printf("%s", furi_string_get_cstr(*CliCommandTree_ref(it_right)->key_ptr)); - CliCommandTree_next(it_right); - } - }; - - if(furi_string_size(args) > 0) { - cli_nl(cli); - printf("`"); - printf("%s", furi_string_get_cstr(args)); - printf("` command not found"); - } -} - -void cli_command_uptime(Cli* cli, FuriString* args, void* context) { - UNUSED(cli); +void cli_command_uptime(PipeSide* pipe, FuriString* args, void* context) { + UNUSED(pipe); UNUSED(args); UNUSED(context); uint32_t uptime = furi_get_tick() / furi_kernel_get_tick_frequency(); printf("Uptime: %luh%lum%lus", uptime / 60 / 60, uptime / 60 % 60, uptime % 60); } -void cli_command_date(Cli* cli, FuriString* args, void* context) { - UNUSED(cli); +void cli_command_date(PipeSide* pipe, FuriString* args, void* context) { + UNUSED(pipe); UNUSED(context); DateTime datetime = {0}; @@ -174,7 +136,8 @@ void cli_command_date(Cli* cli, FuriString* args, void* context) { #define CLI_COMMAND_LOG_BUFFER_SIZE 64 void cli_command_log_tx_callback(const uint8_t* buffer, size_t size, void* context) { - furi_stream_buffer_send(context, buffer, size, 0); + PipeSide* pipe = context; + pipe_send(pipe, buffer, size); } bool cli_command_log_level_set_from_string(FuriString* level) { @@ -196,16 +159,13 @@ bool cli_command_log_level_set_from_string(FuriString* level) { return false; } -void cli_command_log(Cli* cli, FuriString* args, void* context) { +void cli_command_log(PipeSide* pipe, FuriString* args, void* context) { UNUSED(context); - FuriStreamBuffer* ring = furi_stream_buffer_alloc(CLI_COMMAND_LOG_RING_SIZE, 1); - uint8_t buffer[CLI_COMMAND_LOG_BUFFER_SIZE]; FuriLogLevel previous_level = furi_log_get_level(); bool restore_log_level = false; if(furi_string_size(args) > 0) { if(!cli_command_log_level_set_from_string(args)) { - furi_stream_buffer_free(ring); return; } restore_log_level = true; @@ -217,16 +177,15 @@ void cli_command_log(Cli* cli, FuriString* args, void* context) { FuriLogHandler log_handler = { .callback = cli_command_log_tx_callback, - .context = ring, + .context = pipe, }; furi_log_add_handler(log_handler); printf("Use to list available log levels\r\n"); printf("Press CTRL+C to stop...\r\n"); - while(!cli_cmd_interrupt_received(cli)) { - size_t ret = furi_stream_buffer_receive(ring, buffer, CLI_COMMAND_LOG_BUFFER_SIZE, 50); - cli_write(cli, buffer, ret); + while(!cli_is_pipe_broken_or_is_etx_next_char(pipe)) { + furi_delay_ms(100); } furi_log_remove_handler(log_handler); @@ -235,12 +194,10 @@ void cli_command_log(Cli* cli, FuriString* args, void* context) { // There will be strange behaviour if log level is set from settings while log command is running furi_log_set_level(previous_level); } - - furi_stream_buffer_free(ring); } -void cli_command_sysctl_debug(Cli* cli, FuriString* args, void* context) { - UNUSED(cli); +void cli_command_sysctl_debug(PipeSide* pipe, FuriString* args, void* context) { + UNUSED(pipe); UNUSED(context); if(!furi_string_cmp(args, "0")) { furi_hal_rtc_reset_flag(FuriHalRtcFlagDebug); @@ -253,8 +210,8 @@ void cli_command_sysctl_debug(Cli* cli, FuriString* args, void* context) { } } -void cli_command_sysctl_heap_track(Cli* cli, FuriString* args, void* context) { - UNUSED(cli); +void cli_command_sysctl_heap_track(PipeSide* pipe, FuriString* args, void* context) { + UNUSED(pipe); UNUSED(context); if(!furi_string_cmp(args, "none")) { furi_hal_rtc_set_heap_track_mode(FuriHalRtcHeapTrackModeNone); @@ -288,7 +245,7 @@ void cli_command_sysctl_print_usage(void) { #endif } -void cli_command_sysctl(Cli* cli, FuriString* args, void* context) { +void cli_command_sysctl(PipeSide* pipe, FuriString* args, void* context) { FuriString* cmd; cmd = furi_string_alloc(); @@ -299,12 +256,12 @@ void cli_command_sysctl(Cli* cli, FuriString* args, void* context) { } if(furi_string_cmp_str(cmd, "debug") == 0) { - cli_command_sysctl_debug(cli, args, context); + cli_command_sysctl_debug(pipe, args, context); break; } if(furi_string_cmp_str(cmd, "heap_track") == 0) { - cli_command_sysctl_heap_track(cli, args, context); + cli_command_sysctl_heap_track(pipe, args, context); break; } @@ -314,8 +271,8 @@ void cli_command_sysctl(Cli* cli, FuriString* args, void* context) { furi_string_free(cmd); } -void cli_command_vibro(Cli* cli, FuriString* args, void* context) { - UNUSED(cli); +void cli_command_vibro(PipeSide* pipe, FuriString* args, void* context) { + UNUSED(pipe); UNUSED(context); if(!furi_string_cmp(args, "0")) { @@ -341,8 +298,8 @@ void cli_command_vibro(Cli* cli, FuriString* args, void* context) { } } -void cli_command_led(Cli* cli, FuriString* args, void* context) { - UNUSED(cli); +void cli_command_led(PipeSide* pipe, FuriString* args, void* context) { + UNUSED(pipe); UNUSED(context); // Get first word as light name NotificationMessage notification_led_message; @@ -396,23 +353,23 @@ void cli_command_led(Cli* cli, FuriString* args, void* context) { furi_record_close(RECORD_NOTIFICATION); } -static void cli_command_top(Cli* cli, FuriString* args, void* context) { - UNUSED(cli); +static void cli_command_top(PipeSide* pipe, FuriString* args, void* context) { UNUSED(context); int interval = 1000; args_read_int_and_trim(args, &interval); FuriThreadList* thread_list = furi_thread_list_alloc(); - while(!cli_cmd_interrupt_received(cli)) { + while(!cli_is_pipe_broken_or_is_etx_next_char(pipe)) { uint32_t tick = furi_get_tick(); furi_thread_enumerate(thread_list); - if(interval) printf("\e[2J\e[0;0f"); // Clear display and return to 0 + if(interval) printf(ANSI_CURSOR_POS("1", "1")); uint32_t uptime = tick / furi_kernel_get_tick_frequency(); printf( - "Threads: %zu, ISR Time: %0.2f%%, Uptime: %luh%lum%lus\r\n", + "Threads: %zu, ISR Time: %0.2f%%, Uptime: %luh%lum%lus" ANSI_ERASE_LINE( + ANSI_ERASE_FROM_CURSOR_TO_END) "\r\n", furi_thread_list_size(thread_list), (double)furi_thread_list_get_isr_time(thread_list), uptime / 60 / 60, @@ -420,14 +377,16 @@ static void cli_command_top(Cli* cli, FuriString* args, void* context) { uptime % 60); printf( - "Heap: total %zu, free %zu, minimum %zu, max block %zu\r\n\r\n", + "Heap: total %zu, free %zu, minimum %zu, max block %zu" ANSI_ERASE_LINE( + ANSI_ERASE_FROM_CURSOR_TO_END) "\r\n" ANSI_ERASE_LINE(ANSI_ERASE_FROM_CURSOR_TO_END) "\r\n", memmgr_get_total_heap(), memmgr_get_free_heap(), memmgr_get_minimum_free_heap(), memmgr_heap_get_max_free_block()); printf( - "%-17s %-20s %-10s %5s %12s %6s %10s %7s %5s\r\n", + "%-17s %-20s %-10s %5s %12s %6s %10s %7s %5s" ANSI_ERASE_LINE( + ANSI_ERASE_FROM_CURSOR_TO_END) "\r\n", "AppID", "Name", "State", @@ -436,12 +395,13 @@ static void cli_command_top(Cli* cli, FuriString* args, void* context) { "Stack", "Stack Min", "Heap", - "CPU"); + "%CPU"); for(size_t i = 0; i < furi_thread_list_size(thread_list); i++) { const FuriThreadListItem* item = furi_thread_list_get_at(thread_list, i); printf( - "%-17s %-20s %-10s %5d 0x%08lx %6lu %10lu %7zu %5.1f\r\n", + "%-17s %-20s %-10s %5d 0x%08lx %6lu %10lu %7zu %5.1f" ANSI_ERASE_LINE( + ANSI_ERASE_FROM_CURSOR_TO_END) "\r\n", item->app_id, item->name, item->state, @@ -453,6 +413,9 @@ static void cli_command_top(Cli* cli, FuriString* args, void* context) { (double)item->cpu); } + printf(ANSI_ERASE_DISPLAY(ANSI_ERASE_FROM_CURSOR_TO_END)); + fflush(stdout); + if(interval > 0) { furi_delay_ms(interval); } else { @@ -462,8 +425,8 @@ static void cli_command_top(Cli* cli, FuriString* args, void* context) { furi_thread_list_free(thread_list); } -void cli_command_free(Cli* cli, FuriString* args, void* context) { - UNUSED(cli); +void cli_command_free(PipeSide* pipe, FuriString* args, void* context) { + UNUSED(pipe); UNUSED(args); UNUSED(context); @@ -476,16 +439,16 @@ void cli_command_free(Cli* cli, FuriString* args, void* context) { printf("Maximum pool block: %zu\r\n", memmgr_pool_get_max_block()); } -void cli_command_free_blocks(Cli* cli, FuriString* args, void* context) { - UNUSED(cli); +void cli_command_free_blocks(PipeSide* pipe, FuriString* args, void* context) { + UNUSED(pipe); UNUSED(args); UNUSED(context); memmgr_heap_printf_free_blocks(); } -void cli_command_i2c(Cli* cli, FuriString* args, void* context) { - UNUSED(cli); +void cli_command_i2c(PipeSide* pipe, FuriString* args, void* context) { + UNUSED(pipe); UNUSED(args); UNUSED(context); @@ -507,24 +470,53 @@ void cli_command_i2c(Cli* cli, FuriString* args, void* context) { furi_hal_i2c_release(&furi_hal_i2c_handle_external); } -void cli_commands_init(Cli* cli) { - cli_add_command(cli, "!", CliCommandFlagParallelSafe, cli_command_info, (void*)true); - cli_add_command(cli, "info", CliCommandFlagParallelSafe, cli_command_info, NULL); - cli_add_command(cli, "device_info", CliCommandFlagParallelSafe, cli_command_info, (void*)true); +/** + * Echoes any bytes it receives except ASCII ETX (0x03, Ctrl+C) + */ +void cli_command_echo(PipeSide* pipe, FuriString* args, void* context) { + UNUSED(args); + UNUSED(context); - cli_add_command(cli, "?", CliCommandFlagParallelSafe, cli_command_help, NULL); - cli_add_command(cli, "help", CliCommandFlagParallelSafe, cli_command_help, NULL); + uint8_t buffer[256]; - cli_add_command(cli, "uptime", CliCommandFlagDefault, cli_command_uptime, NULL); - cli_add_command(cli, "date", CliCommandFlagParallelSafe, cli_command_date, NULL); - cli_add_command(cli, "log", CliCommandFlagParallelSafe, cli_command_log, NULL); - cli_add_command(cli, "sysctl", CliCommandFlagDefault, cli_command_sysctl, NULL); - cli_add_command(cli, "top", CliCommandFlagParallelSafe, cli_command_top, NULL); - cli_add_command(cli, "free", CliCommandFlagParallelSafe, cli_command_free, NULL); - cli_add_command(cli, "free_blocks", CliCommandFlagParallelSafe, cli_command_free_blocks, NULL); + while(true) { + size_t to_read = CLAMP(pipe_bytes_available(pipe), sizeof(buffer), 1UL); + size_t read = pipe_receive(pipe, buffer, to_read); + if(read < to_read) break; - cli_add_command(cli, "vibro", CliCommandFlagDefault, cli_command_vibro, NULL); - cli_add_command(cli, "led", CliCommandFlagDefault, cli_command_led, NULL); - cli_add_command(cli, "gpio", CliCommandFlagDefault, cli_command_gpio, NULL); - cli_add_command(cli, "i2c", CliCommandFlagDefault, cli_command_i2c, NULL); + if(memchr(buffer, CliKeyETX, read)) break; + + size_t written = pipe_send(pipe, buffer, read); + if(written < read) break; + } +} + +void cli_main_commands_init(CliRegistry* registry) { + cli_registry_add_command( + registry, "!", CliCommandFlagParallelSafe, cli_command_info, (void*)true); + cli_registry_add_command(registry, "info", CliCommandFlagParallelSafe, cli_command_info, NULL); + cli_registry_add_command( + registry, "device_info", CliCommandFlagParallelSafe, cli_command_info, (void*)true); + + cli_registry_add_command( + registry, "uptime", CliCommandFlagParallelSafe, cli_command_uptime, NULL); + cli_registry_add_command(registry, "date", CliCommandFlagParallelSafe, cli_command_date, NULL); + cli_registry_add_command(registry, "log", CliCommandFlagParallelSafe, cli_command_log, NULL); + cli_registry_add_command(registry, "sysctl", CliCommandFlagDefault, cli_command_sysctl, NULL); + cli_registry_add_command(registry, "top", CliCommandFlagParallelSafe, cli_command_top, NULL); + cli_registry_add_command(registry, "free", CliCommandFlagParallelSafe, cli_command_free, NULL); + cli_registry_add_command( + registry, "free_blocks", CliCommandFlagDefault, cli_command_free_blocks, NULL); + cli_registry_add_command(registry, "echo", CliCommandFlagParallelSafe, cli_command_echo, NULL); + + cli_registry_add_command(registry, "vibro", CliCommandFlagDefault, cli_command_vibro, NULL); + cli_registry_add_command(registry, "led", CliCommandFlagDefault, cli_command_led, NULL); + cli_registry_add_command(registry, "gpio", CliCommandFlagDefault, cli_command_gpio, NULL); + cli_registry_add_command(registry, "i2c", CliCommandFlagDefault, cli_command_i2c, NULL); +} + +void cli_on_system_start(void) { + CliRegistry* registry = cli_registry_alloc(); + cli_main_commands_init(registry); + furi_record_create(RECORD_CLI, registry); } diff --git a/applications/services/cli/cli_main_commands.h b/applications/services/cli/cli_main_commands.h new file mode 100644 index 000000000..d8058f467 --- /dev/null +++ b/applications/services/cli/cli_main_commands.h @@ -0,0 +1,9 @@ +#pragma once + +#include +#include + +#define RECORD_CLI "cli" +#define CLI_APPID "cli" + +void cli_main_commands_init(CliRegistry* registry); diff --git a/applications/services/cli/cli_main_shell.c b/applications/services/cli/cli_main_shell.c new file mode 100644 index 000000000..7550bef04 --- /dev/null +++ b/applications/services/cli/cli_main_shell.c @@ -0,0 +1,46 @@ +#include "cli_main_shell.h" +#include "cli_main_commands.h" +#include +#include +#include + +void cli_main_motd(void* context) { + UNUSED(context); + printf(ANSI_FLIPPER_BRAND_ORANGE + "\r\n" + " _.-------.._ -,\r\n" + " .-\"```\"--..,,_/ /`-, -, \\ \r\n" + " .:\" /:/ /'\\ \\ ,_..., `. | |\r\n" + " / ,----/:/ /`\\ _\\~`_-\"` _;\r\n" + " ' / /`\"\"\"'\\ \\ \\.~`_-' ,-\"'/ \r\n" + " | | | 0 | | .-' ,/` /\r\n" + " | ,..\\ \\ ,.-\"` ,/` /\r\n" + " ; : `/`\"\"\\` ,/--==,/-----,\r\n" + " | `-...| -.___-Z:_______J...---;\r\n" + " : ` _-'\r\n" + " _L_ _ ___ ___ ___ ___ ____--\"`___ _ ___\r\n" + "| __|| | |_ _|| _ \\| _ \\| __|| _ \\ / __|| | |_ _|\r\n" + "| _| | |__ | | | _/| _/| _| | / | (__ | |__ | |\r\n" + "|_| |____||___||_| |_| |___||_|_\\ \\___||____||___|\r\n" + "\r\n" ANSI_FG_BR_WHITE "Welcome to Flipper Zero Command Line Interface!\r\n" + "Read the manual: https://docs.flipper.net/development/cli\r\n" + "Run `help` or `?` to list available commands\r\n" + "\r\n" ANSI_RESET); + + const Version* firmware_version = furi_hal_version_get_firmware_version(); + if(firmware_version) { + printf( + "Firmware version: %s %s (%s%s built on %s)\r\n", + version_get_gitbranch(firmware_version), + version_get_version(firmware_version), + version_get_githash(firmware_version), + version_get_dirty_flag(firmware_version) ? "-dirty" : "", + version_get_builddate(firmware_version)); + } +} + +const CliCommandExternalConfig cli_main_ext_config = { + .search_directory = "/ext/apps_data/cli/plugins", + .fal_prefix = "cli_", + .appid = CLI_APPID, +}; diff --git a/applications/services/cli/cli_main_shell.h b/applications/services/cli/cli_main_shell.h new file mode 100644 index 000000000..576839990 --- /dev/null +++ b/applications/services/cli/cli_main_shell.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +void cli_main_motd(void* context); + +extern const CliCommandExternalConfig cli_main_ext_config; diff --git a/applications/services/cli/cli_vcp.c b/applications/services/cli/cli_vcp.c index aa399e78a..f4b539e26 100644 --- a/applications/services/cli/cli_vcp.c +++ b/applications/services/cli/cli_vcp.c @@ -1,323 +1,308 @@ -#include "cli_i.h" // IWYU pragma: keep +#include "cli_vcp.h" #include #include #include +#include +#include +#include +#include "cli_main_shell.h" +#include "cli_main_commands.h" #define TAG "CliVcp" -#define USB_CDC_PKT_LEN CDC_DATA_SZ -#define VCP_RX_BUF_SIZE (USB_CDC_PKT_LEN * 3) -#define VCP_TX_BUF_SIZE (USB_CDC_PKT_LEN * 3) +#define USB_CDC_PKT_LEN CDC_DATA_SZ +#define VCP_BUF_SIZE (USB_CDC_PKT_LEN * 3) +#define VCP_IF_NUM 0 +#define VCP_MESSAGE_Q_LEN 8 -#define VCP_IF_NUM 0 - -#ifdef CLI_VCP_DEBUG -#define VCP_DEBUG(...) FURI_LOG_D(TAG, __VA_ARGS__) +#ifdef CLI_VCP_TRACE +#define VCP_TRACE(...) FURI_LOG_T(__VA_ARGS__) #else -#define VCP_DEBUG(...) +#define VCP_TRACE(...) #endif -typedef enum { - VcpEvtStop = (1 << 0), - VcpEvtConnect = (1 << 1), - VcpEvtDisconnect = (1 << 2), - VcpEvtStreamRx = (1 << 3), - VcpEvtRx = (1 << 4), - VcpEvtStreamTx = (1 << 5), - VcpEvtTx = (1 << 6), -} WorkerEvtFlags; - -#define VCP_THREAD_FLAG_ALL \ - (VcpEvtStop | VcpEvtConnect | VcpEvtDisconnect | VcpEvtRx | VcpEvtTx | VcpEvtStreamRx | \ - VcpEvtStreamTx) - typedef struct { - FuriThread* thread; + enum { + CliVcpMessageTypeEnable, + CliVcpMessageTypeDisable, + } type; + union {}; +} CliVcpMessage; - FuriStreamBuffer* tx_stream; - FuriStreamBuffer* rx_stream; +typedef enum { + CliVcpInternalEventConnected = (1 << 0), + CliVcpInternalEventDisconnected = (1 << 1), + CliVcpInternalEventTxDone = (1 << 2), + CliVcpInternalEventRx = (1 << 3), +} CliVcpInternalEvent; - volatile bool connected; - volatile bool running; +#define CliVcpInternalEventAll \ + (CliVcpInternalEventConnected | CliVcpInternalEventDisconnected | CliVcpInternalEventTxDone | \ + CliVcpInternalEventRx) - FuriHalUsbInterface* usb_if_prev; +struct CliVcp { + FuriEventLoop* event_loop; + FuriMessageQueue* message_queue; // is_currently_transmitting) return; + if(!cli_vcp->own_pipe) return; -static void cli_vcp_init(void) { - if(vcp == NULL) { - vcp = malloc(sizeof(CliVcp)); - vcp->tx_stream = furi_stream_buffer_alloc(VCP_TX_BUF_SIZE, 1); - vcp->rx_stream = furi_stream_buffer_alloc(VCP_RX_BUF_SIZE, 1); + uint8_t buf[USB_CDC_PKT_LEN]; + size_t to_receive_from_pipe = MIN(sizeof(buf), pipe_bytes_available(cli_vcp->own_pipe)); + size_t length = pipe_receive(cli_vcp->own_pipe, buf, to_receive_from_pipe); + if(length > 0 || cli_vcp->previous_tx_length == USB_CDC_PKT_LEN) { + VCP_TRACE(TAG, "cdc_send length=%zu", length); + cli_vcp->is_currently_transmitting = true; + furi_hal_cdc_send(VCP_IF_NUM, buf, length); } - furi_assert(vcp->thread == NULL); - - vcp->connected = false; - - vcp->thread = furi_thread_alloc_ex("CliVcpWorker", 1024, vcp_worker, NULL); - furi_thread_start(vcp->thread); - - FURI_LOG_I(TAG, "Init OK"); + cli_vcp->previous_tx_length = length; } -static void cli_vcp_deinit(void) { - furi_thread_flags_set(furi_thread_get_id(vcp->thread), VcpEvtStop); - furi_thread_join(vcp->thread); - furi_thread_free(vcp->thread); - vcp->thread = NULL; +/** + * Called in the following cases: + * - new data arrived at the endpoint; + * - data was read out of the pipe. + */ +static void cli_vcp_maybe_receive_data(CliVcp* cli_vcp) { + if(!cli_vcp->own_pipe) return; + if(pipe_spaces_available(cli_vcp->own_pipe) < USB_CDC_PKT_LEN) return; + + uint8_t buf[USB_CDC_PKT_LEN]; + size_t length = furi_hal_cdc_receive(VCP_IF_NUM, buf, sizeof(buf)); + VCP_TRACE(TAG, "cdc_receive length=%zu", length); + furi_check(pipe_send(cli_vcp->own_pipe, buf, length) == length); } -static int32_t vcp_worker(void* context) { - UNUSED(context); - bool tx_idle = true; - size_t missed_rx = 0; - uint8_t last_tx_pkt_len = 0; +// ============= +// CDC callbacks +// ============= - // Switch USB to VCP mode (if it is not set yet) - vcp->usb_if_prev = furi_hal_usb_get_config(); - if((vcp->usb_if_prev != &usb_cdc_single) && (vcp->usb_if_prev != &usb_cdc_dual)) { +static void cli_vcp_signal_internal_event(CliVcp* cli_vcp, CliVcpInternalEvent event) { + furi_thread_flags_set(cli_vcp->thread_id, event); +} + +static void cli_vcp_cdc_tx_done(void* context) { + CliVcp* cli_vcp = context; + cli_vcp_signal_internal_event(cli_vcp, CliVcpInternalEventTxDone); +} + +static void cli_vcp_cdc_rx(void* context) { + CliVcp* cli_vcp = context; + cli_vcp_signal_internal_event(cli_vcp, CliVcpInternalEventRx); +} + +static void cli_vcp_cdc_state_callback(void* context, CdcState state) { + CliVcp* cli_vcp = context; + if(state == CdcStateDisconnected) { + cli_vcp_signal_internal_event(cli_vcp, CliVcpInternalEventDisconnected); + } + // `Connected` events are generated by DTR going active +} + +static void cli_vcp_cdc_ctrl_line_callback(void* context, CdcCtrlLine ctrl_lines) { + CliVcp* cli_vcp = context; + if(ctrl_lines & CdcCtrlLineDTR) { + cli_vcp_signal_internal_event(cli_vcp, CliVcpInternalEventConnected); + } else { + cli_vcp_signal_internal_event(cli_vcp, CliVcpInternalEventDisconnected); + } +} + +static CdcCallbacks cdc_callbacks = { + .tx_ep_callback = cli_vcp_cdc_tx_done, + .rx_ep_callback = cli_vcp_cdc_rx, + .state_callback = cli_vcp_cdc_state_callback, + .ctrl_line_callback = cli_vcp_cdc_ctrl_line_callback, + .config_callback = NULL, +}; + +// ====================== +// Pipe callback handlers +// ====================== + +static void cli_vcp_data_from_shell(PipeSide* pipe, void* context) { + UNUSED(pipe); + CliVcp* cli_vcp = context; + cli_vcp_maybe_send_data(cli_vcp); +} + +static void cli_vcp_shell_ready(PipeSide* pipe, void* context) { + UNUSED(pipe); + CliVcp* cli_vcp = context; + cli_vcp_maybe_receive_data(cli_vcp); +} + +/** + * Processes messages arriving from other threads + */ +static void cli_vcp_message_received(FuriEventLoopObject* object, void* context) { + CliVcp* cli_vcp = context; + CliVcpMessage message; + furi_check(furi_message_queue_get(object, &message, 0) == FuriStatusOk); + + switch(message.type) { + case CliVcpMessageTypeEnable: + if(cli_vcp->is_enabled) return; + FURI_LOG_D(TAG, "Enabling"); + cli_vcp->is_enabled = true; + + // switch usb mode + cli_vcp->previous_interface = furi_hal_usb_get_config(); furi_hal_usb_set_config(&usb_cdc_single, NULL); + furi_hal_cdc_set_callbacks(VCP_IF_NUM, &cdc_callbacks, cli_vcp); + break; + + case CliVcpMessageTypeDisable: + if(!cli_vcp->is_enabled) return; + FURI_LOG_D(TAG, "Disabling"); + cli_vcp->is_enabled = false; + + // restore usb mode + furi_hal_cdc_set_callbacks(VCP_IF_NUM, NULL, NULL); + furi_hal_usb_set_config(cli_vcp->previous_interface, NULL); + break; } - furi_hal_cdc_set_callbacks(VCP_IF_NUM, &cdc_cb, NULL); - - FURI_LOG_D(TAG, "Start"); - vcp->running = true; - - while(1) { - uint32_t flags = - furi_thread_flags_wait(VCP_THREAD_FLAG_ALL, FuriFlagWaitAny, FuriWaitForever); - furi_assert(!(flags & FuriFlagError)); - - // VCP session opened - if(flags & VcpEvtConnect) { - VCP_DEBUG("Connect"); - - if(vcp->connected == false) { - vcp->connected = true; - furi_stream_buffer_send(vcp->rx_stream, &ascii_soh, 1, FuriWaitForever); - } - } - - // VCP session closed - if(flags & VcpEvtDisconnect) { - VCP_DEBUG("Disconnect"); - - if(vcp->connected == true) { - vcp->connected = false; - furi_stream_buffer_receive(vcp->tx_stream, vcp->data_buffer, USB_CDC_PKT_LEN, 0); - furi_stream_buffer_send(vcp->rx_stream, &ascii_eot, 1, FuriWaitForever); - } - } - - // Rx buffer was read, maybe there is enough space for new data? - if((flags & VcpEvtStreamRx) && (missed_rx > 0)) { - VCP_DEBUG("StreamRx"); - - if(furi_stream_buffer_spaces_available(vcp->rx_stream) >= USB_CDC_PKT_LEN) { - flags |= VcpEvtRx; - missed_rx--; - } - } - - // New data received - if(flags & VcpEvtRx) { - if(furi_stream_buffer_spaces_available(vcp->rx_stream) >= USB_CDC_PKT_LEN) { - int32_t len = furi_hal_cdc_receive(VCP_IF_NUM, vcp->data_buffer, USB_CDC_PKT_LEN); - VCP_DEBUG("Rx %ld", len); - - if(len > 0) { - furi_check( - furi_stream_buffer_send( - vcp->rx_stream, vcp->data_buffer, len, FuriWaitForever) == - (size_t)len); - } - } else { - VCP_DEBUG("Rx missed"); - missed_rx++; - } - } - - // New data in Tx buffer - if(flags & VcpEvtStreamTx) { - VCP_DEBUG("StreamTx"); - - if(tx_idle) { - flags |= VcpEvtTx; - } - } - - // CDC write transfer done - if(flags & VcpEvtTx) { - size_t len = - furi_stream_buffer_receive(vcp->tx_stream, vcp->data_buffer, USB_CDC_PKT_LEN, 0); - - VCP_DEBUG("Tx %d", len); - - if(len > 0) { // Some data left in Tx buffer. Sending it now - tx_idle = false; - furi_hal_cdc_send(VCP_IF_NUM, vcp->data_buffer, len); - last_tx_pkt_len = len; - } else { // There is nothing to send. - if(last_tx_pkt_len == 64) { - // Send extra zero-length packet if last packet len is 64 to indicate transfer end - furi_hal_cdc_send(VCP_IF_NUM, NULL, 0); - } else { - // Set flag to start next transfer instantly - tx_idle = true; - } - last_tx_pkt_len = 0; - } - } - - if(flags & VcpEvtStop) { - vcp->connected = false; - vcp->running = false; - furi_hal_cdc_set_callbacks(VCP_IF_NUM, NULL, NULL); - // Restore previous USB mode (if it was set during init) - if((vcp->usb_if_prev != &usb_cdc_single) && (vcp->usb_if_prev != &usb_cdc_dual)) { - furi_hal_usb_unlock(); - furi_hal_usb_set_config(vcp->usb_if_prev, NULL); - } - furi_stream_buffer_receive(vcp->tx_stream, vcp->data_buffer, USB_CDC_PKT_LEN, 0); - furi_stream_buffer_send(vcp->rx_stream, &ascii_eot, 1, FuriWaitForever); - break; - } - } - FURI_LOG_D(TAG, "End"); - return 0; } -static size_t cli_vcp_rx(uint8_t* buffer, size_t size, uint32_t timeout) { - furi_assert(vcp); - furi_assert(buffer); +/** + * Processes messages arriving from CDC event callbacks + */ +static void cli_vcp_internal_event_happened(void* context) { + CliVcp* cli_vcp = context; + CliVcpInternalEvent event = furi_thread_flags_wait(CliVcpInternalEventAll, FuriFlagWaitAny, 0); + furi_check(!(event & FuriFlagError)); - if(vcp->running == false) { + if(event & CliVcpInternalEventDisconnected) { + if(!cli_vcp->is_connected) return; + FURI_LOG_D(TAG, "Disconnected"); + cli_vcp->is_connected = false; + + // disconnect our side of the pipe + pipe_detach_from_event_loop(cli_vcp->own_pipe); + pipe_free(cli_vcp->own_pipe); + cli_vcp->own_pipe = NULL; + } + + if(event & CliVcpInternalEventConnected) { + if(cli_vcp->is_connected) return; + FURI_LOG_D(TAG, "Connected"); + cli_vcp->is_connected = true; + + // wait for previous shell to stop + furi_check(!cli_vcp->own_pipe); + if(cli_vcp->shell) { + cli_shell_join(cli_vcp->shell); + cli_shell_free(cli_vcp->shell); + pipe_free(cli_vcp->shell_pipe); + } + + // start shell thread + PipeSideBundle bundle = pipe_alloc(VCP_BUF_SIZE, 1); + cli_vcp->own_pipe = bundle.alices_side; + cli_vcp->shell_pipe = bundle.bobs_side; + pipe_attach_to_event_loop(cli_vcp->own_pipe, cli_vcp->event_loop); + pipe_set_callback_context(cli_vcp->own_pipe, cli_vcp); + pipe_set_data_arrived_callback( + cli_vcp->own_pipe, cli_vcp_data_from_shell, FuriEventLoopEventFlagEdge); + pipe_set_space_freed_callback( + cli_vcp->own_pipe, cli_vcp_shell_ready, FuriEventLoopEventFlagEdge); + furi_delay_ms(33); // we are too fast, minicom isn't ready yet + cli_vcp->shell = cli_shell_alloc( + cli_main_motd, NULL, cli_vcp->shell_pipe, cli_vcp->main_registry, &cli_main_ext_config); + cli_shell_start(cli_vcp->shell); + } + + if(event & CliVcpInternalEventRx) { + VCP_TRACE(TAG, "Rx"); + cli_vcp_maybe_receive_data(cli_vcp); + } + + if(event & CliVcpInternalEventTxDone) { + VCP_TRACE(TAG, "TxDone"); + cli_vcp->is_currently_transmitting = false; + cli_vcp_maybe_send_data(cli_vcp); + } +} + +// ============ +// Thread stuff +// ============ + +static CliVcp* cli_vcp_alloc(void) { + CliVcp* cli_vcp = malloc(sizeof(CliVcp)); + cli_vcp->thread_id = furi_thread_get_current_id(); + + cli_vcp->event_loop = furi_event_loop_alloc(); + + cli_vcp->message_queue = furi_message_queue_alloc(VCP_MESSAGE_Q_LEN, sizeof(CliVcpMessage)); + furi_event_loop_subscribe_message_queue( + cli_vcp->event_loop, + cli_vcp->message_queue, + FuriEventLoopEventIn, + cli_vcp_message_received, + cli_vcp); + + furi_event_loop_subscribe_thread_flags( + cli_vcp->event_loop, cli_vcp_internal_event_happened, cli_vcp); + + cli_vcp->main_registry = furi_record_open(RECORD_CLI); + + return cli_vcp; +} + +int32_t cli_vcp_srv(void* p) { + UNUSED(p); + + if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) { + FURI_LOG_W(TAG, "Skipping start in special boot mode"); + furi_thread_suspend(furi_thread_get_current_id()); return 0; } - VCP_DEBUG("rx %u start", size); + CliVcp* cli_vcp = cli_vcp_alloc(); + furi_record_create(RECORD_CLI_VCP, cli_vcp); + furi_event_loop_run(cli_vcp->event_loop); - size_t rx_cnt = 0; - - while(size > 0) { - size_t batch_size = size; - if(batch_size > VCP_RX_BUF_SIZE) batch_size = VCP_RX_BUF_SIZE; - - size_t len = furi_stream_buffer_receive(vcp->rx_stream, buffer, batch_size, timeout); - VCP_DEBUG("rx %u ", batch_size); - - if(len == 0) break; - if(vcp->running == false) { - // EOT command is received after VCP session close - rx_cnt += len; - break; - } - furi_thread_flags_set(furi_thread_get_id(vcp->thread), VcpEvtStreamRx); - size -= len; - buffer += len; - rx_cnt += len; - } - - VCP_DEBUG("rx %u end", size); - return rx_cnt; + return 0; } -static size_t cli_vcp_rx_stdin(uint8_t* data, size_t size, uint32_t timeout, void* context) { - UNUSED(context); - return cli_vcp_rx(data, size, timeout); +// ========== +// Public API +// ========== + +void cli_vcp_enable(CliVcp* cli_vcp) { + CliVcpMessage message = { + .type = CliVcpMessageTypeEnable, + }; + furi_message_queue_put(cli_vcp->message_queue, &message, FuriWaitForever); } -static void cli_vcp_tx(const uint8_t* buffer, size_t size) { - furi_assert(vcp); - furi_assert(buffer); - - if(vcp->running == false) { - return; - } - - VCP_DEBUG("tx %u start", size); - - while(size > 0 && vcp->connected) { - size_t batch_size = size; - if(batch_size > USB_CDC_PKT_LEN) batch_size = USB_CDC_PKT_LEN; - - furi_stream_buffer_send(vcp->tx_stream, buffer, batch_size, FuriWaitForever); - furi_thread_flags_set(furi_thread_get_id(vcp->thread), VcpEvtStreamTx); - VCP_DEBUG("tx %u", batch_size); - - size -= batch_size; - buffer += batch_size; - } - - VCP_DEBUG("tx %u end", size); +void cli_vcp_disable(CliVcp* cli_vcp) { + CliVcpMessage message = { + .type = CliVcpMessageTypeDisable, + }; + furi_message_queue_put(cli_vcp->message_queue, &message, FuriWaitForever); } - -static void cli_vcp_tx_stdout(const char* data, size_t size, void* context) { - UNUSED(context); - cli_vcp_tx((const uint8_t*)data, size); -} - -static void vcp_state_callback(void* context, uint8_t state) { - UNUSED(context); - if(state == 0) { - furi_thread_flags_set(furi_thread_get_id(vcp->thread), VcpEvtDisconnect); - } -} - -static void vcp_on_cdc_control_line(void* context, uint8_t state) { - UNUSED(context); - // bit 0: DTR state, bit 1: RTS state - bool dtr = state & (1 << 0); - - if(dtr == true) { - furi_thread_flags_set(furi_thread_get_id(vcp->thread), VcpEvtConnect); - } else { - furi_thread_flags_set(furi_thread_get_id(vcp->thread), VcpEvtDisconnect); - } -} - -static void vcp_on_cdc_rx(void* context) { - UNUSED(context); - uint32_t ret = furi_thread_flags_set(furi_thread_get_id(vcp->thread), VcpEvtRx); - furi_check(!(ret & FuriFlagError)); -} - -static void vcp_on_cdc_tx_complete(void* context) { - UNUSED(context); - furi_thread_flags_set(furi_thread_get_id(vcp->thread), VcpEvtTx); -} - -static bool cli_vcp_is_connected(void) { - furi_assert(vcp); - return vcp->connected; -} - -CliSession cli_vcp = { - cli_vcp_init, - cli_vcp_deinit, - cli_vcp_rx, - cli_vcp_rx_stdin, - cli_vcp_tx, - cli_vcp_tx_stdout, - cli_vcp_is_connected, -}; diff --git a/applications/services/cli/cli_vcp.h b/applications/services/cli/cli_vcp.h index 3aef2ef70..10e286183 100644 --- a/applications/services/cli/cli_vcp.h +++ b/applications/services/cli/cli_vcp.h @@ -9,9 +9,12 @@ extern "C" { #endif -typedef struct CliSession CliSession; +#define RECORD_CLI_VCP "cli_vcp" -extern CliSession cli_vcp; +typedef struct CliVcp CliVcp; + +void cli_vcp_enable(CliVcp* cli_vcp); +void cli_vcp_disable(CliVcp* cli_vcp); #ifdef __cplusplus } diff --git a/applications/services/cli/commands/hello_world.c b/applications/services/cli/commands/hello_world.c new file mode 100644 index 000000000..b77f3e663 --- /dev/null +++ b/applications/services/cli/commands/hello_world.c @@ -0,0 +1,10 @@ +#include "../cli_main_commands.h" + +static void execute(PipeSide* pipe, FuriString* args, void* context) { + UNUSED(pipe); + UNUSED(args); + UNUSED(context); + puts("Hello, World!"); +} + +CLI_COMMAND_INTERFACE(hello_world, execute, CliCommandFlagParallelSafe, 768, CLI_APPID); diff --git a/applications/services/cli/commands/neofetch.c b/applications/services/cli/commands/neofetch.c new file mode 100644 index 000000000..0e50a0d8d --- /dev/null +++ b/applications/services/cli/commands/neofetch.c @@ -0,0 +1,160 @@ +#include "../cli_main_commands.h" +#include +#include +#include +#include +#include +#include + +static void execute(PipeSide* pipe, FuriString* args, void* context) { + UNUSED(pipe); + UNUSED(args); + UNUSED(context); + + static const char* const neofetch_logo[] = { + " _.-------.._ -,", + " .-\"```\"--..,,_/ /`-, -, \\ ", + " .:\" /:/ /'\\ \\ ,_..., `. | |", + " / ,----/:/ /`\\ _\\~`_-\"` _;", + " ' / /`\"\"\"'\\ \\ \\.~`_-' ,-\"'/ ", + " | | | 0 | | .-' ,/` /", + " | ,..\\ \\ ,.-\"` ,/` /", + "; : `/`\"\"\\` ,/--==,/-----,", + "| `-...| -.___-Z:_______J...---;", + ": ` _-'", + }; +#define NEOFETCH_COLOR ANSI_FLIPPER_BRAND_ORANGE + + // Determine logo parameters + size_t logo_height = COUNT_OF(neofetch_logo), logo_width = 0; + for(size_t i = 0; i < logo_height; i++) + logo_width = MAX(logo_width, strlen(neofetch_logo[i])); + logo_width += 4; // space between logo and info + + // Format hostname delimiter + const size_t size_of_hostname = 4 + strlen(furi_hal_version_get_name_ptr()); + char delimiter[64]; + memset(delimiter, '-', size_of_hostname); + delimiter[size_of_hostname] = '\0'; + + // Get heap info + size_t heap_total = memmgr_get_total_heap(); + size_t heap_used = heap_total - memmgr_get_free_heap(); + uint16_t heap_percent = (100 * heap_used) / heap_total; + + // Get storage info + Storage* storage = furi_record_open(RECORD_STORAGE); + uint64_t ext_total, ext_free, ext_used, ext_percent; + storage_common_fs_info(storage, "/ext", &ext_total, &ext_free); + ext_used = ext_total - ext_free; + ext_percent = (100 * ext_used) / ext_total; + ext_used /= 1024 * 1024; + ext_total /= 1024 * 1024; + furi_record_close(RECORD_STORAGE); + + // Get battery info + uint16_t charge_percent = furi_hal_power_get_pct(); + const char* charge_state; + if(furi_hal_power_is_charging()) { + if((charge_percent < 100) && (!furi_hal_power_is_charging_done())) { + charge_state = "charging"; + } else { + charge_state = "charged"; + } + } else { + charge_state = "discharging"; + } + + // Get misc info + uint32_t uptime = furi_get_tick() / furi_kernel_get_tick_frequency(); + const Version* version = version_get(); + uint16_t major, minor; + furi_hal_info_get_api_version(&major, &minor); + + // Print ASCII art with info + const size_t info_height = 16; + for(size_t i = 0; i < MAX(logo_height, info_height); i++) { + printf(NEOFETCH_COLOR "%-*s", logo_width, (i < logo_height) ? neofetch_logo[i] : ""); + switch(i) { + case 0: // you@ + printf("you" ANSI_RESET "@" NEOFETCH_COLOR "%s", furi_hal_version_get_name_ptr()); + break; + case 1: // delimiter + printf(ANSI_RESET "%s", delimiter); + break; + case 2: // OS: FURI (SDK .) + printf( + "OS" ANSI_RESET ": FURI %s %s %s %s (SDK %hu.%hu)", + version_get_version(version), + version_get_gitbranch(version), + version_get_version(version), + version_get_githash(version), + major, + minor); + break; + case 3: // Host: + printf( + "Host" ANSI_RESET ": %s %s", + furi_hal_version_get_model_code(), + furi_hal_version_get_device_name_ptr()); + break; + case 4: // Kernel: FreeRTOS .. + printf( + "Kernel" ANSI_RESET ": FreeRTOS %d.%d.%d", + tskKERNEL_VERSION_MAJOR, + tskKERNEL_VERSION_MINOR, + tskKERNEL_VERSION_BUILD); + break; + case 5: // Uptime: ?h?m?s + printf( + "Uptime" ANSI_RESET ": %luh%lum%lus", + uptime / 60 / 60, + uptime / 60 % 60, + uptime % 60); + break; + case 6: // ST7567 128x64 @ 1 bpp in 1.4" + printf("Display" ANSI_RESET ": ST7567 128x64 @ 1 bpp in 1.4\""); + break; + case 7: // DE: GuiSrv + printf("DE" ANSI_RESET ": GuiSrv"); + break; + case 8: // Shell: CliSrv + printf("Shell" ANSI_RESET ": CliShell"); + break; + case 9: // CPU: STM32WB55RG @ 64 MHz + printf("CPU" ANSI_RESET ": STM32WB55RG @ 64 MHz"); + break; + case 10: // Memory: / B (??%) + printf( + "Memory" ANSI_RESET ": %zu / %zu B (%hu%%)", heap_used, heap_total, heap_percent); + break; + case 11: // Disk (/ext): / MiB (??%) + printf( + "Disk (/ext)" ANSI_RESET ": %llu / %llu MiB (%llu%%)", + ext_used, + ext_total, + ext_percent); + break; + case 12: // Battery: ??% () + printf("Battery" ANSI_RESET ": %hu%% (%s)" ANSI_RESET, charge_percent, charge_state); + break; + case 13: // empty space + break; + case 14: // Colors (line 1) + for(size_t j = 30; j <= 37; j++) + printf("\e[%dm███", j); + break; + case 15: // Colors (line 2) + for(size_t j = 90; j <= 97; j++) + printf("\e[%dm███", j); + break; + default: + break; + } + printf("\r\n"); + } + printf(ANSI_RESET); +#undef NEOFETCH_COLOR +} + +CLI_COMMAND_INTERFACE(neofetch, execute, CliCommandFlagParallelSafe, 2048, CLI_APPID); diff --git a/applications/services/cli/commands/subshell_demo.c b/applications/services/cli/commands/subshell_demo.c new file mode 100644 index 000000000..f0013c4a0 --- /dev/null +++ b/applications/services/cli/commands/subshell_demo.c @@ -0,0 +1,43 @@ +#include "../cli_main_commands.h" +#include +#include +#include + +#define RAINBOW_SUBCOMMAND \ + ANSI_FG_RED "s" ANSI_FG_YELLOW "u" ANSI_FG_BLUE "b" ANSI_FG_GREEN "c" ANSI_FG_MAGENTA \ + "o" ANSI_FG_RED "m" ANSI_FG_YELLOW "m" ANSI_FG_BLUE "a" ANSI_FG_GREEN \ + "n" ANSI_FG_MAGENTA "d" + +static void subcommand(PipeSide* pipe, FuriString* args, void* context) { + UNUSED(pipe); + UNUSED(args); + UNUSED(context); + printf("This is a ✨" RAINBOW_SUBCOMMAND ANSI_RESET "✨!"); +} + +static void motd(void* context) { + UNUSED(context); + printf("\r\n"); + printf("+------------------------------------+\r\n"); + printf("| Hello world! |\r\n"); + printf("| This is the " ANSI_FG_GREEN "MOTD" ANSI_RESET " for our " ANSI_FG_BLUE + "subshell" ANSI_RESET "! |\r\n"); + printf("+------------------------------------+\r\n"); +} + +static void execute(PipeSide* pipe, FuriString* args, void* context) { + UNUSED(args); + UNUSED(context); + CliRegistry* registry = cli_registry_alloc(); + cli_registry_add_command(registry, "subcommand", CliCommandFlagParallelSafe, subcommand, NULL); + + CliShell* shell = cli_shell_alloc(motd, NULL, pipe, registry, NULL); + cli_shell_set_prompt(shell, "subshell"); + cli_shell_start(shell); + cli_shell_join(shell); + + cli_shell_free(shell); + cli_registry_free(registry); +} + +CLI_COMMAND_INTERFACE(subshell_demo, execute, CliCommandFlagParallelSafe, 2048, CLI_APPID); diff --git a/applications/services/crypto/application.fam b/applications/services/crypto/application.fam index 7771c5ed2..1d07ca409 100644 --- a/applications/services/crypto/application.fam +++ b/applications/services/crypto/application.fam @@ -2,5 +2,5 @@ App( appid="crypto_start", apptype=FlipperAppType.STARTUP, entry_point="crypto_on_system_start", - order=10, + order=20, ) diff --git a/applications/services/crypto/crypto_cli.c b/applications/services/crypto/crypto_cli.c index 744fa7151..32b4199f5 100644 --- a/applications/services/crypto/crypto_cli.c +++ b/applications/services/crypto/crypto_cli.c @@ -2,7 +2,10 @@ #include #include -#include +#include +#include +#include +#include void crypto_cli_print_usage(void) { printf("Usage:\r\n"); @@ -17,7 +20,7 @@ void crypto_cli_print_usage(void) { "\tstore_key \t - Store key in secure enclave. !!! NON-REVERSABLE OPERATION - READ MANUAL FIRST !!!\r\n"); } -void crypto_cli_encrypt(Cli* cli, FuriString* args) { +void crypto_cli_encrypt(PipeSide* pipe, FuriString* args) { int key_slot = 0; bool key_loaded = false; uint8_t iv[16]; @@ -44,15 +47,15 @@ void crypto_cli_encrypt(Cli* cli, FuriString* args) { FuriString* input; input = furi_string_alloc(); char c; - while(cli_read(cli, (uint8_t*)&c, 1) == 1) { - if(c == CliSymbolAsciiETX) { + while(pipe_receive(pipe, (uint8_t*)&c, 1) == 1) { + if(c == CliKeyETX) { printf("\r\n"); break; } else if(c >= 0x20 && c < 0x7F) { putc(c, stdout); fflush(stdout); furi_string_push_back(input, c); - } else if(c == CliSymbolAsciiCR) { + } else if(c == CliKeyCR) { printf("\r\n"); furi_string_cat(input, "\r\n"); } @@ -92,7 +95,7 @@ void crypto_cli_encrypt(Cli* cli, FuriString* args) { } } -void crypto_cli_decrypt(Cli* cli, FuriString* args) { +void crypto_cli_decrypt(PipeSide* pipe, FuriString* args) { int key_slot = 0; bool key_loaded = false; uint8_t iv[16]; @@ -119,15 +122,15 @@ void crypto_cli_decrypt(Cli* cli, FuriString* args) { FuriString* hex_input; hex_input = furi_string_alloc(); char c; - while(cli_read(cli, (uint8_t*)&c, 1) == 1) { - if(c == CliSymbolAsciiETX) { + while(pipe_receive(pipe, (uint8_t*)&c, 1) == 1) { + if(c == CliKeyETX) { printf("\r\n"); break; } else if(c >= 0x20 && c < 0x7F) { putc(c, stdout); fflush(stdout); furi_string_push_back(hex_input, c); - } else if(c == CliSymbolAsciiCR) { + } else if(c == CliKeyCR) { printf("\r\n"); } } @@ -164,8 +167,8 @@ void crypto_cli_decrypt(Cli* cli, FuriString* args) { } } -void crypto_cli_has_key(Cli* cli, FuriString* args) { - UNUSED(cli); +void crypto_cli_has_key(PipeSide* pipe, FuriString* args) { + UNUSED(pipe); int key_slot = 0; uint8_t iv[16] = {0}; @@ -186,8 +189,8 @@ void crypto_cli_has_key(Cli* cli, FuriString* args) { } while(0); } -void crypto_cli_store_key(Cli* cli, FuriString* args) { - UNUSED(cli); +void crypto_cli_store_key(PipeSide* pipe, FuriString* args) { + UNUSED(pipe); int key_slot = 0; int key_size = 0; FuriString* key_type; @@ -279,7 +282,7 @@ void crypto_cli_store_key(Cli* cli, FuriString* args) { furi_string_free(key_type); } -static void crypto_cli(Cli* cli, FuriString* args, void* context) { +static void crypto_cli(PipeSide* pipe, FuriString* args, void* context) { UNUSED(context); FuriString* cmd; cmd = furi_string_alloc(); @@ -291,22 +294,22 @@ static void crypto_cli(Cli* cli, FuriString* args, void* context) { } if(furi_string_cmp_str(cmd, "encrypt") == 0) { - crypto_cli_encrypt(cli, args); + crypto_cli_encrypt(pipe, args); break; } if(furi_string_cmp_str(cmd, "decrypt") == 0) { - crypto_cli_decrypt(cli, args); + crypto_cli_decrypt(pipe, args); break; } if(furi_string_cmp_str(cmd, "has_key") == 0) { - crypto_cli_has_key(cli, args); + crypto_cli_has_key(pipe, args); break; } if(furi_string_cmp_str(cmd, "store_key") == 0) { - crypto_cli_store_key(cli, args); + crypto_cli_store_key(pipe, args); break; } @@ -318,8 +321,8 @@ static void crypto_cli(Cli* cli, FuriString* args, void* context) { void crypto_on_system_start(void) { #ifdef SRV_CLI - Cli* cli = furi_record_open(RECORD_CLI); - cli_add_command(cli, "crypto", CliCommandFlagDefault, crypto_cli, NULL); + CliRegistry* registry = furi_record_open(RECORD_CLI); + cli_registry_add_command(registry, "crypto", CliCommandFlagDefault, crypto_cli, NULL); furi_record_close(RECORD_CLI); #else UNUSED(crypto_cli); diff --git a/applications/services/desktop/desktop.c b/applications/services/desktop/desktop.c index 1132760d5..60f1c21b9 100644 --- a/applications/services/desktop/desktop.c +++ b/applications/services/desktop/desktop.c @@ -1,6 +1,5 @@ #include "desktop_i.h" -#include #include #include @@ -28,9 +27,7 @@ static void desktop_loader_callback(const void* message, void* context) { if(event->type == LoaderEventTypeApplicationBeforeLoad) { view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopGlobalBeforeAppStarted); furi_check(furi_semaphore_acquire(desktop->animation_semaphore, 3000) == FuriStatusOk); - } else if( - event->type == LoaderEventTypeApplicationLoadFailed || - event->type == LoaderEventTypeApplicationStopped) { + } else if(event->type == LoaderEventTypeNoMoreAppsInQueue) { view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopGlobalAfterAppFinished); } } @@ -396,9 +393,9 @@ void desktop_lock(Desktop* desktop) { furi_hal_rtc_set_flag(FuriHalRtcFlagLock); if(desktop_pin_code_is_set()) { - Cli* cli = furi_record_open(RECORD_CLI); - cli_session_close(cli); - furi_record_close(RECORD_CLI); + CliVcp* cli_vcp = furi_record_open(RECORD_CLI_VCP); + cli_vcp_disable(cli_vcp); + furi_record_close(RECORD_CLI_VCP); } desktop_auto_lock_inhibit(desktop); @@ -426,9 +423,9 @@ void desktop_unlock(Desktop* desktop) { furi_hal_rtc_set_pin_fails(0); if(desktop_pin_code_is_set()) { - Cli* cli = furi_record_open(RECORD_CLI); - cli_session_open(cli, &cli_vcp); - furi_record_close(RECORD_CLI); + CliVcp* cli_vcp = furi_record_open(RECORD_CLI_VCP); + cli_vcp_enable(cli_vcp); + furi_record_close(RECORD_CLI_VCP); } DesktopStatus status = {.locked = false}; @@ -525,6 +522,10 @@ int32_t desktop_srv(void* p) { if(desktop_pin_code_is_set()) { desktop_lock(desktop); + } else { + CliVcp* cli_vcp = furi_record_open(RECORD_CLI_VCP); + cli_vcp_enable(cli_vcp); + furi_record_close(RECORD_CLI_VCP); } if(storage_file_exists(desktop->storage, SLIDESHOW_FS_PATH)) { diff --git a/applications/services/expansion/application.fam b/applications/services/expansion/application.fam index dbdde0a52..f85450e40 100644 --- a/applications/services/expansion/application.fam +++ b/applications/services/expansion/application.fam @@ -8,5 +8,5 @@ App( ], requires=["rpc_start"], provides=["expansion_settings"], - order=150, + order=100, ) diff --git a/applications/services/gui/modules/submenu.c b/applications/services/gui/modules/submenu.c index cdc1ecc28..9892c8bfd 100644 --- a/applications/services/gui/modules/submenu.c +++ b/applications/services/gui/modules/submenu.c @@ -11,8 +11,12 @@ struct Submenu { typedef struct { FuriString* label; uint32_t index; - SubmenuItemCallback callback; + union { + SubmenuItemCallback callback; + SubmenuItemCallbackEx callback_ex; + }; void* callback_context; + bool has_extended_events; } SubmenuItem; static void SubmenuItem_init(SubmenuItem* item) { @@ -57,7 +61,7 @@ typedef struct { static void submenu_process_up(Submenu* submenu); static void submenu_process_down(Submenu* submenu); -static void submenu_process_ok(Submenu* submenu); +static void submenu_process_ok(Submenu* submenu, InputType input_type); static void submenu_view_draw_callback(Canvas* canvas, void* _model) { SubmenuModel* model = _model; @@ -120,7 +124,10 @@ static bool submenu_view_input_callback(InputEvent* event, void* context) { furi_assert(submenu); bool consumed = false; - if(event->type == InputTypeShort) { + if(event->key == InputKeyOk) { + consumed = true; + submenu_process_ok(submenu, event->type); + } else if(event->type == InputTypeShort) { switch(event->key) { case InputKeyUp: consumed = true; @@ -130,10 +137,6 @@ static bool submenu_view_input_callback(InputEvent* event, void* context) { consumed = true; submenu_process_down(submenu); break; - case InputKeyOk: - consumed = true; - submenu_process_ok(submenu); - break; default: break; } @@ -211,6 +214,31 @@ void submenu_add_item( item->index = index; item->callback = callback; item->callback_context = callback_context; + item->has_extended_events = false; + }, + true); +} + +void submenu_add_item_ex( + Submenu* submenu, + const char* label, + uint32_t index, + SubmenuItemCallbackEx callback, + void* callback_context) { + SubmenuItem* item = NULL; + furi_check(label); + furi_check(submenu); + + with_view_model( + submenu->view, + SubmenuModel * model, + { + item = SubmenuItemArray_push_new(model->items); + furi_string_set_str(item->label, label); + item->index = index; + item->callback_ex = callback; + item->callback_context = callback_context; + item->has_extended_events = true; }, true); } @@ -376,7 +404,7 @@ void submenu_process_down(Submenu* submenu) { true); } -void submenu_process_ok(Submenu* submenu) { +void submenu_process_ok(Submenu* submenu, InputType input_type) { SubmenuItem* item = NULL; with_view_model( @@ -390,8 +418,12 @@ void submenu_process_ok(Submenu* submenu) { }, true); - if(item && item->callback) { + if(!item) return; + + if(!item->has_extended_events && input_type == InputTypeShort && item->callback) { item->callback(item->callback_context, item->index); + } else if(item->has_extended_events && item->callback_ex) { + item->callback_ex(item->callback_context, input_type, item->index); } } diff --git a/applications/services/gui/modules/submenu.h b/applications/services/gui/modules/submenu.h index 0408d52c0..d86284371 100644 --- a/applications/services/gui/modules/submenu.h +++ b/applications/services/gui/modules/submenu.h @@ -14,6 +14,7 @@ extern "C" { /** Submenu anonymous structure */ typedef struct Submenu Submenu; typedef void (*SubmenuItemCallback)(void* context, uint32_t index); +typedef void (*SubmenuItemCallbackEx)(void* context, InputType input_type, uint32_t index); /** Allocate and initialize submenu * @@ -53,6 +54,22 @@ void submenu_add_item( SubmenuItemCallback callback, void* callback_context); +/** Add item to submenu with extended press events + * + * @param submenu Submenu instance + * @param label menu item label + * @param index menu item index, used for callback, may be + * the same with other items + * @param callback menu item extended callback + * @param callback_context menu item callback context + */ +void submenu_add_item_ex( + Submenu* submenu, + const char* label, + uint32_t index, + SubmenuItemCallbackEx callback, + void* callback_context); + /** Change label of an existing item * * @param submenu Submenu instance diff --git a/applications/services/gui/modules/widget_elements/widget_element_text_scroll.c b/applications/services/gui/modules/widget_elements/widget_element_text_scroll.c index 4c9c39dff..491ffc6bc 100644 --- a/applications/services/gui/modules/widget_elements/widget_element_text_scroll.c +++ b/applications/services/gui/modules/widget_elements/widget_element_text_scroll.c @@ -19,8 +19,8 @@ typedef struct { uint8_t width; uint8_t height; FuriString* text; - uint8_t scroll_pos_total; - uint8_t scroll_pos_current; + uint16_t scroll_pos_total; + uint16_t scroll_pos_current; bool text_formatted; } WidgetElementTextScrollModel; diff --git a/applications/services/input/input.c b/applications/services/input/input.c index 6cbafb795..d7e9f3d31 100644 --- a/applications/services/input/input.c +++ b/applications/services/input/input.c @@ -4,8 +4,10 @@ #include #include #include -#include #include +#include +#include +#include #define INPUT_DEBOUNCE_TICKS_HALF (INPUT_DEBOUNCE_TICKS / 2) #define INPUT_PRESS_TICKS 150 @@ -25,7 +27,7 @@ typedef struct { } InputPinState; /** Input CLI command handler */ -void input_cli(Cli* cli, FuriString* args, void* context); +void input_cli(PipeSide* pipe, FuriString* args, void* context); // #define INPUT_DEBUG @@ -92,8 +94,10 @@ int32_t input_srv(void* p) { #endif #ifdef SRV_CLI - Cli* cli = furi_record_open(RECORD_CLI); - cli_add_command(cli, "input", CliCommandFlagParallelSafe, input_cli, event_pubsub); + CliRegistry* registry = furi_record_open(RECORD_CLI); + cli_registry_add_command( + registry, "input", CliCommandFlagParallelSafe, input_cli, event_pubsub); + furi_record_close(RECORD_CLI); #endif InputPinState pin_states[input_pins_count]; diff --git a/applications/services/input/input_cli.c b/applications/services/input/input_cli.c index 8e711c895..d5ea57418 100644 --- a/applications/services/input/input_cli.c +++ b/applications/services/input/input_cli.c @@ -1,8 +1,9 @@ #include "input.h" #include -#include +#include #include +#include static void input_cli_usage(void) { printf("Usage:\r\n"); @@ -19,7 +20,7 @@ static void input_cli_dump_events_callback(const void* value, void* ctx) { furi_message_queue_put(input_queue, value, FuriWaitForever); } -static void input_cli_dump(Cli* cli, FuriString* args, FuriPubSub* event_pubsub) { +static void input_cli_dump(PipeSide* pipe, FuriString* args, FuriPubSub* event_pubsub) { UNUSED(args); FuriMessageQueue* input_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); FuriPubSubSubscription* input_subscription = @@ -27,7 +28,7 @@ static void input_cli_dump(Cli* cli, FuriString* args, FuriPubSub* event_pubsub) InputEvent input_event; printf("Press CTRL+C to stop\r\n"); - while(!cli_cmd_interrupt_received(cli)) { + while(!cli_is_pipe_broken_or_is_etx_next_char(pipe)) { if(furi_message_queue_get(input_queue, &input_event, 100) == FuriStatusOk) { printf( "key: %s type: %s\r\n", @@ -47,8 +48,8 @@ static void input_cli_send_print_usage(void) { printf("\t\t \t - one of 'press', 'release', 'short', 'long'\r\n"); } -static void input_cli_send(Cli* cli, FuriString* args, FuriPubSub* event_pubsub) { - UNUSED(cli); +static void input_cli_send(PipeSide* pipe, FuriString* args, FuriPubSub* event_pubsub) { + UNUSED(pipe); InputEvent event; FuriString* key_str; key_str = furi_string_alloc(); @@ -97,8 +98,7 @@ static void input_cli_send(Cli* cli, FuriString* args, FuriPubSub* event_pubsub) furi_string_free(key_str); } -void input_cli(Cli* cli, FuriString* args, void* context) { - furi_assert(cli); +void input_cli(PipeSide* pipe, FuriString* args, void* context) { furi_assert(context); FuriPubSub* event_pubsub = context; FuriString* cmd; @@ -110,11 +110,11 @@ void input_cli(Cli* cli, FuriString* args, void* context) { break; } if(furi_string_cmp_str(cmd, "dump") == 0) { - input_cli_dump(cli, args, event_pubsub); + input_cli_dump(pipe, args, event_pubsub); break; } if(furi_string_cmp_str(cmd, "send") == 0) { - input_cli_send(cli, args, event_pubsub); + input_cli_send(pipe, args, event_pubsub); break; } diff --git a/applications/services/loader/application.fam b/applications/services/loader/application.fam index f4d006e07..5600bdf62 100644 --- a/applications/services/loader/application.fam +++ b/applications/services/loader/application.fam @@ -19,5 +19,5 @@ App( apptype=FlipperAppType.STARTUP, entry_point="loader_on_system_start", requires=["loader"], - order=90, + order=80, ) diff --git a/applications/services/loader/loader.c b/applications/services/loader/loader.c index 72cac4b62..d3cd0022e 100644 --- a/applications/services/loader/loader.c +++ b/applications/services/loader/loader.c @@ -167,6 +167,13 @@ static void loader_show_gui_error( furi_record_close(RECORD_DIALOGS); } +static void loader_generic_synchronous_request(Loader* loader, LoaderMessage* message) { + furi_check(loader); + message->api_lock = api_lock_alloc_locked(); + furi_message_queue_put(loader->queue, message, FuriWaitForever); + api_lock_wait_unlock_and_free(message->api_lock); +} + LoaderStatus loader_start(Loader* loader, const char* name, const char* args, FuriString* error_message) { furi_check(loader); @@ -202,16 +209,12 @@ void loader_start_detached_with_gui_error(Loader* loader, const char* name, cons } bool loader_lock(Loader* loader) { - furi_check(loader); - - LoaderMessage message; LoaderMessageBoolResult result; - message.type = LoaderMessageTypeLock; - message.api_lock = api_lock_alloc_locked(); - message.bool_value = &result; - furi_message_queue_put(loader->queue, &message, FuriWaitForever); - api_lock_wait_unlock_and_free(message.api_lock); - + LoaderMessage message = { + .type = LoaderMessageTypeLock, + .bool_value = &result, + }; + loader_generic_synchronous_request(loader, &message); return result.value; } @@ -225,16 +228,12 @@ void loader_unlock(Loader* loader) { } bool loader_is_locked(Loader* loader) { - furi_check(loader); - - LoaderMessage message; LoaderMessageBoolResult result; - message.type = LoaderMessageTypeIsLocked; - message.api_lock = api_lock_alloc_locked(); - message.bool_value = &result; - furi_message_queue_put(loader->queue, &message, FuriWaitForever); - api_lock_wait_unlock_and_free(message.api_lock); - + LoaderMessage message = { + .type = LoaderMessageTypeIsLocked, + .bool_value = &result, + }; + loader_generic_synchronous_request(loader, &message); return result.value; } @@ -256,42 +255,63 @@ FuriPubSub* loader_get_pubsub(Loader* loader) { } bool loader_signal(Loader* loader, uint32_t signal, void* arg) { - furi_check(loader); - LoaderMessageBoolResult result; - LoaderMessage message = { .type = LoaderMessageTypeSignal, - .api_lock = api_lock_alloc_locked(), .signal.signal = signal, .signal.arg = arg, .bool_value = &result, }; - - furi_message_queue_put(loader->queue, &message, FuriWaitForever); - api_lock_wait_unlock_and_free(message.api_lock); - + loader_generic_synchronous_request(loader, &message); return result.value; } bool loader_get_application_name(Loader* loader, FuriString* name) { - furi_check(loader); - LoaderMessageBoolResult result; - LoaderMessage message = { .type = LoaderMessageTypeGetApplicationName, - .api_lock = api_lock_alloc_locked(), .application_name = name, .bool_value = &result, }; - - furi_message_queue_put(loader->queue, &message, FuriWaitForever); - api_lock_wait_unlock_and_free(message.api_lock); - + loader_generic_synchronous_request(loader, &message); return result.value; } +bool loader_get_application_launch_path(Loader* loader, FuriString* name) { + LoaderMessageBoolResult result; + LoaderMessage message = { + .type = LoaderMessageTypeGetApplicationLaunchPath, + .application_name = name, + .bool_value = &result, + }; + loader_generic_synchronous_request(loader, &message); + return result.value; +} + +void loader_enqueue_launch( + Loader* loader, + const char* name, + const char* args, + LoaderDeferredLaunchFlag flags) { + LoaderMessage message = { + .type = LoaderMessageTypeEnqueueLaunch, + .defer_start = + { + .name_or_path = strdup(name), + .args = args ? strdup(args) : NULL, + .flags = flags, + }, + }; + loader_generic_synchronous_request(loader, &message); +} + +void loader_clear_launch_queue(Loader* loader) { + LoaderMessage message = { + .type = LoaderMessageTypeClearLaunchQueue, + }; + loader_generic_synchronous_request(loader, &message); +} + // callbacks static void loader_menu_closed_callback(void* context) { @@ -328,12 +348,10 @@ static Loader* loader_alloc(void) { Loader* loader = malloc(sizeof(Loader)); loader->pubsub = furi_pubsub_alloc(); loader->queue = furi_message_queue_alloc(1, sizeof(LoaderMessage)); - loader->loader_menu = NULL; - loader->loader_applications = NULL; - loader->app.args = NULL; - loader->app.thread = NULL; - loader->app.insomniac = false; - loader->app.fap = NULL; + loader->gui = furi_record_open(RECORD_GUI); + loader->view_holder = view_holder_alloc(); + loader->loading = loading_alloc(); + view_holder_attach_to_gui(loader->view_holder, loader->gui); return loader; } @@ -656,6 +674,10 @@ static LoaderMessageLoaderStatusResult loader_do_start_by_name( LoaderStatusErrorUnknownApp, error_message, "Application \"%s\" not found", name); } while(false); + if(status.value == LoaderStatusOk) { + loader->app.launch_path = furi_string_alloc_set_str(name); + } + return status; } @@ -673,6 +695,57 @@ static void loader_do_unlock(Loader* loader) { loader->app.thread = NULL; } +static void loader_do_emit_queue_empty_event(Loader* loader) { + FURI_LOG_I(TAG, "Launch queue empty"); + LoaderEvent event; + event.type = LoaderEventTypeNoMoreAppsInQueue; + furi_pubsub_publish(loader->pubsub, &event); +} + +static bool loader_do_deferred_launch(Loader* loader, LoaderDeferredLaunchRecord* record); + +static void loader_do_next_deferred_launch_if_available(Loader* loader) { + LoaderDeferredLaunchRecord record; + if(loader_queue_pop(&loader->launch_queue, &record)) { + loader_do_deferred_launch(loader, &record); + loader_queue_item_clear(&record); + } else { + loader_do_emit_queue_empty_event(loader); + } +} + +static bool loader_do_deferred_launch(Loader* loader, LoaderDeferredLaunchRecord* record) { + furi_assert(loader); + furi_assert(record); + + bool is_successful = false; + FuriString* error_message = furi_string_alloc(); + view_holder_set_view(loader->view_holder, loading_get_view(loader->loading)); + view_holder_send_to_front(loader->view_holder); + + do { + const char* app_name_str = record->name_or_path; + const char* app_args = record->args; + FURI_LOG_I(TAG, "Deferred launch: %s", app_name_str); + + LoaderMessageLoaderStatusResult result = + loader_do_start_by_name(loader, app_name_str, app_args, error_message); + if(result.value == LoaderStatusOk) { + is_successful = true; + break; + } + + if(record->flags & LoaderDeferredLaunchFlagGui) + loader_show_gui_error(result, app_name_str, error_message); + + loader_do_next_deferred_launch_if_available(loader); + } while(false); + + view_holder_set_view(loader->view_holder, NULL); + furi_string_free(error_message); + return is_successful; +} + static void loader_do_app_closed(Loader* loader) { furi_assert(loader->app.thread); @@ -697,11 +770,15 @@ static void loader_do_app_closed(Loader* loader) { loader->app.thread = NULL; } + furi_string_free(loader->app.launch_path); + FURI_LOG_I(TAG, "Application stopped. Free heap: %zu", memmgr_get_free_heap()); LoaderEvent event; event.type = LoaderEventTypeApplicationStopped; furi_pubsub_publish(loader->pubsub, &event); + + loader_do_next_deferred_launch_if_available(loader); } static bool loader_is_application_running(Loader* loader) { @@ -726,6 +803,15 @@ static bool loader_do_get_application_name(Loader* loader, FuriString* name) { return false; } +static bool loader_do_get_application_launch_path(Loader* loader, FuriString* path) { + if(loader_is_application_running(loader)) { + furi_string_set(path, loader->app.launch_path); + return true; + } + + return false; +} + // app int32_t loader_srv(void* p) { @@ -748,16 +834,20 @@ int32_t loader_srv(void* p) { while(true) { if(furi_message_queue_get(loader->queue, &message, FuriWaitForever) == FuriStatusOk) { switch(message.type) { - case LoaderMessageTypeStartByName: - *(message.status_value) = loader_do_start_by_name( + case LoaderMessageTypeStartByName: { + LoaderMessageLoaderStatusResult status = loader_do_start_by_name( loader, message.start.name, message.start.args, message.start.error_message); + *(message.status_value) = status; + if(status.value != LoaderStatusOk) loader_do_emit_queue_empty_event(loader); api_lock_unlock(message.api_lock); break; + } case LoaderMessageTypeStartByNameDetachedWithGuiError: { FuriString* error_message = furi_string_alloc(); LoaderMessageLoaderStatusResult status = loader_do_start_by_name( loader, message.start.name, message.start.args, error_message); loader_show_gui_error(status, message.start.name, error_message); + if(status.value != LoaderStatusOk) loader_do_emit_queue_empty_event(loader); if(message.start.name) free((void*)message.start.name); if(message.start.args) free((void*)message.start.args); furi_string_free(error_message); @@ -796,6 +886,19 @@ int32_t loader_srv(void* p) { loader_do_get_application_name(loader, message.application_name); api_lock_unlock(message.api_lock); break; + case LoaderMessageTypeGetApplicationLaunchPath: + message.bool_value->value = + loader_do_get_application_launch_path(loader, message.application_name); + api_lock_unlock(message.api_lock); + break; + case LoaderMessageTypeEnqueueLaunch: + furi_check(loader_queue_push(&loader->launch_queue, &message.defer_start)); + api_lock_unlock(message.api_lock); + break; + case LoaderMessageTypeClearLaunchQueue: + loader_queue_clear(&loader->launch_queue); + api_lock_unlock(message.api_lock); + break; } } } diff --git a/applications/services/loader/loader.h b/applications/services/loader/loader.h index cacfbff68..d732379a7 100644 --- a/applications/services/loader/loader.h +++ b/applications/services/loader/loader.h @@ -20,13 +20,19 @@ typedef enum { typedef enum { LoaderEventTypeApplicationBeforeLoad, LoaderEventTypeApplicationLoadFailed, - LoaderEventTypeApplicationStopped + LoaderEventTypeApplicationStopped, + LoaderEventTypeNoMoreAppsInQueue, //type == LoaderEventTypeApplicationStopped) { + if(event->type == LoaderEventTypeNoMoreAppsInQueue) { furi_thread_flags_set(thread_id, APPLICATION_STOP_EVENT); } } diff --git a/applications/services/loader/loader_cli.c b/applications/services/loader/loader_cli.c index f3ea30df2..265779e8f 100644 --- a/applications/services/loader/loader_cli.c +++ b/applications/services/loader/loader_cli.c @@ -1,11 +1,13 @@ #include "loader.h" #include -#include +#include +#include #include #include #include #include +#include static void loader_cli_print_usage(void) { printf("Usage:\r\n"); @@ -110,8 +112,8 @@ static void loader_cli_signal(FuriString* args, Loader* loader) { } } -static void loader_cli(Cli* cli, FuriString* args, void* context) { - UNUSED(cli); +static void loader_cli(PipeSide* pipe, FuriString* args, void* context) { + UNUSED(pipe); UNUSED(context); Loader* loader = furi_record_open(RECORD_LOADER); @@ -140,8 +142,9 @@ static void loader_cli(Cli* cli, FuriString* args, void* context) { void loader_on_system_start(void) { #ifdef SRV_CLI - Cli* cli = furi_record_open(RECORD_CLI); - cli_add_command(cli, RECORD_LOADER, CliCommandFlagParallelSafe, loader_cli, NULL); + CliRegistry* registry = furi_record_open(RECORD_CLI); + cli_registry_add_command( + registry, RECORD_LOADER, CliCommandFlagParallelSafe, loader_cli, NULL); furi_record_close(RECORD_CLI); #else UNUSED(loader_cli); diff --git a/applications/services/loader/loader_i.h b/applications/services/loader/loader_i.h index 92f1e88e0..2bf42c655 100644 --- a/applications/services/loader/loader_i.h +++ b/applications/services/loader/loader_i.h @@ -2,11 +2,20 @@ #include #include #include + +#include +#include +#include + +#include + #include "loader.h" #include "loader_menu.h" #include "loader_applications.h" +#include "loader_queue.h" typedef struct { + FuriString* launch_path; char* args; FuriThread* thread; bool insomniac; @@ -19,6 +28,12 @@ struct Loader { LoaderMenu* loader_menu; LoaderApplications* loader_applications; LoaderAppData app; + + LoaderLaunchQueue launch_queue; + + Gui* gui; + ViewHolder* view_holder; + Loading* loading; }; typedef enum { @@ -33,6 +48,9 @@ typedef enum { LoaderMessageTypeStartByNameDetachedWithGuiError, LoaderMessageTypeSignal, LoaderMessageTypeGetApplicationName, + LoaderMessageTypeGetApplicationLaunchPath, + LoaderMessageTypeEnqueueLaunch, + LoaderMessageTypeClearLaunchQueue, } LoaderMessageType; typedef struct { @@ -72,6 +90,7 @@ typedef struct { union { LoaderMessageStartByName start; + LoaderDeferredLaunchRecord defer_start; LoaderMessageSignal signal; FuriString* application_name; }; diff --git a/applications/services/loader/loader_menu.c b/applications/services/loader/loader_menu.c index 25763a0bc..dc1b673d5 100644 --- a/applications/services/loader/loader_menu.c +++ b/applications/services/loader/loader_menu.c @@ -4,6 +4,7 @@ #include #include #include +#include #include "loader.h" #include "loader_menu.h" @@ -71,10 +72,16 @@ static void loader_menu_applications_callback(void* context, uint32_t index) { loader_menu_start(name); } -static void loader_menu_settings_menu_callback(void* context, uint32_t index) { +static void + loader_menu_settings_menu_callback(void* context, InputType input_type, uint32_t index) { UNUSED(context); - const char* name = FLIPPER_SETTINGS_APPS[index].name; - loader_menu_start(name); + if(input_type == InputTypeShort) { + const char* name = FLIPPER_SETTINGS_APPS[index].name; + loader_menu_start(name); + } else if(input_type == InputTypeLong) { + const char* name = FLIPPER_SETTINGS_APPS[index].name; + archive_favorites_handle_setting_pin_unpin(name, NULL); + } } static void loader_menu_switch_to_settings(void* context, uint32_t index) { @@ -128,7 +135,7 @@ static void loader_menu_build_menu(LoaderMenuApp* app, LoaderMenu* menu) { static void loader_menu_build_submenu(LoaderMenuApp* app, LoaderMenu* loader_menu) { for(size_t i = 0; i < FLIPPER_SETTINGS_APPS_COUNT; i++) { - submenu_add_item( + submenu_add_item_ex( app->settings_menu, FLIPPER_SETTINGS_APPS[i].name, i, diff --git a/applications/services/loader/loader_queue.c b/applications/services/loader/loader_queue.c new file mode 100644 index 000000000..517dcad75 --- /dev/null +++ b/applications/services/loader/loader_queue.c @@ -0,0 +1,32 @@ +#include "loader_queue.h" + +void loader_queue_item_clear(LoaderDeferredLaunchRecord* item) { + free(item->args); + free(item->name_or_path); +} + +bool loader_queue_pop(LoaderLaunchQueue* queue, LoaderDeferredLaunchRecord* item) { + if(!queue->item_cnt) return false; + + *item = queue->items[0]; + queue->item_cnt--; + memmove( + &queue->items[0], &queue->items[1], queue->item_cnt * sizeof(LoaderDeferredLaunchRecord)); + + return true; +} + +bool loader_queue_push(LoaderLaunchQueue* queue, LoaderDeferredLaunchRecord* item) { + if(queue->item_cnt == LOADER_QUEUE_MAX_SIZE) return false; + + queue->items[queue->item_cnt] = *item; + queue->item_cnt++; + + return true; +} + +void loader_queue_clear(LoaderLaunchQueue* queue) { + for(size_t i = 0; i < queue->item_cnt; i++) + loader_queue_item_clear(&queue->items[i]); + queue->item_cnt = 0; +} diff --git a/applications/services/loader/loader_queue.h b/applications/services/loader/loader_queue.h new file mode 100644 index 000000000..c40130e39 --- /dev/null +++ b/applications/services/loader/loader_queue.h @@ -0,0 +1,53 @@ +#pragma once + +#include + +#include "loader.h" + +#define LOADER_QUEUE_MAX_SIZE 4 + +typedef struct { + char* name_or_path; + char* args; + LoaderDeferredLaunchFlag flags; +} LoaderDeferredLaunchRecord; + +typedef struct { + LoaderDeferredLaunchRecord items[LOADER_QUEUE_MAX_SIZE]; + size_t item_cnt; +} LoaderLaunchQueue; + +/** + * @brief Frees internal data in a `DeferredLaunchRecord` + * + * @param[out] item Record to clear + */ +void loader_queue_item_clear(LoaderDeferredLaunchRecord* item); + +/** + * @brief Fetches the next item from the launch queue + * + * @param[inout] queue Queue instance + * @param[out] item Item output + * + * @return `true` if `item` was populated, `false` if queue is empty + */ +bool loader_queue_pop(LoaderLaunchQueue* queue, LoaderDeferredLaunchRecord* item); + +/** + * @brief Puts an item into the launch queue + * + * @param[inout] queue Queue instance + * @param[in] item Item to put in the queue + * + * @return `true` if the item was put into the queue, `false` if there's no more + * space left + */ +bool loader_queue_push(LoaderLaunchQueue* queue, LoaderDeferredLaunchRecord* item); + +/** + * @brief Clears the launch queue + * + * @param[inout] queue Queue instance + */ +void loader_queue_clear(LoaderLaunchQueue* queue); diff --git a/applications/services/locale/application.fam b/applications/services/locale/application.fam index c762d02d6..09f49161f 100644 --- a/applications/services/locale/application.fam +++ b/applications/services/locale/application.fam @@ -4,6 +4,6 @@ App( apptype=FlipperAppType.STARTUP, entry_point="locale_on_system_start", cdefines=["SRV_LOCALE"], - order=90, + order=70, sdk_headers=["locale.h"], ) diff --git a/applications/services/power/application.fam b/applications/services/power/application.fam index f14d88c54..0e69ec6ec 100644 --- a/applications/services/power/application.fam +++ b/applications/services/power/application.fam @@ -22,5 +22,5 @@ App( apptype=FlipperAppType.STARTUP, entry_point="power_on_system_start", requires=["power"], - order=80, + order=50, ) diff --git a/applications/services/power/power_cli.c b/applications/services/power/power_cli.c index 121552768..f630cb2e8 100644 --- a/applications/services/power/power_cli.c +++ b/applications/services/power/power_cli.c @@ -1,12 +1,14 @@ #include "power_cli.h" #include -#include +#include +#include #include #include +#include -void power_cli_off(Cli* cli, FuriString* args) { - UNUSED(cli); +void power_cli_off(PipeSide* pipe, FuriString* args) { + UNUSED(pipe); UNUSED(args); Power* power = furi_record_open(RECORD_POWER); printf("It's now safe to disconnect USB from your flipper\r\n"); @@ -14,22 +16,22 @@ void power_cli_off(Cli* cli, FuriString* args) { power_off(power); } -void power_cli_reboot(Cli* cli, FuriString* args) { - UNUSED(cli); +void power_cli_reboot(PipeSide* pipe, FuriString* args) { + UNUSED(pipe); UNUSED(args); Power* power = furi_record_open(RECORD_POWER); power_reboot(power, PowerBootModeNormal); } -void power_cli_reboot2dfu(Cli* cli, FuriString* args) { - UNUSED(cli); +void power_cli_reboot2dfu(PipeSide* pipe, FuriString* args) { + UNUSED(pipe); UNUSED(args); Power* power = furi_record_open(RECORD_POWER); power_reboot(power, PowerBootModeDfu); } -void power_cli_5v(Cli* cli, FuriString* args) { - UNUSED(cli); +void power_cli_5v(PipeSide* pipe, FuriString* args) { + UNUSED(pipe); Power* power = furi_record_open(RECORD_POWER); if(!furi_string_cmp(args, "0")) { power_enable_otg(power, false); @@ -42,8 +44,8 @@ void power_cli_5v(Cli* cli, FuriString* args) { furi_record_close(RECORD_POWER); } -void power_cli_3v3(Cli* cli, FuriString* args) { - UNUSED(cli); +void power_cli_3v3(PipeSide* pipe, FuriString* args) { + UNUSED(pipe); if(!furi_string_cmp(args, "0")) { furi_hal_power_disable_external_3_3v(); } else if(!furi_string_cmp(args, "1")) { @@ -67,7 +69,7 @@ static void power_cli_command_print_usage(void) { } } -void power_cli(Cli* cli, FuriString* args, void* context) { +void power_cli(PipeSide* pipe, FuriString* args, void* context) { UNUSED(context); FuriString* cmd; cmd = furi_string_alloc(); @@ -79,28 +81,28 @@ void power_cli(Cli* cli, FuriString* args, void* context) { } if(furi_string_cmp_str(cmd, "off") == 0) { - power_cli_off(cli, args); + power_cli_off(pipe, args); break; } if(furi_string_cmp_str(cmd, "reboot") == 0) { - power_cli_reboot(cli, args); + power_cli_reboot(pipe, args); break; } if(furi_string_cmp_str(cmd, "reboot2dfu") == 0) { - power_cli_reboot2dfu(cli, args); + power_cli_reboot2dfu(pipe, args); break; } if(furi_string_cmp_str(cmd, "5v") == 0) { - power_cli_5v(cli, args); + power_cli_5v(pipe, args); break; } if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { if(furi_string_cmp_str(cmd, "3v3") == 0) { - power_cli_3v3(cli, args); + power_cli_3v3(pipe, args); break; } } @@ -113,10 +115,8 @@ void power_cli(Cli* cli, FuriString* args, void* context) { void power_on_system_start(void) { #ifdef SRV_CLI - Cli* cli = furi_record_open(RECORD_CLI); - - cli_add_command(cli, "power", CliCommandFlagParallelSafe, power_cli, NULL); - + CliRegistry* registry = furi_record_open(RECORD_CLI); + cli_registry_add_command(registry, "power", CliCommandFlagParallelSafe, power_cli, NULL); furi_record_close(RECORD_CLI); #else UNUSED(power_cli); diff --git a/applications/services/region/application.fam b/applications/services/region/application.fam index a4cdc94ea..9b2451213 100644 --- a/applications/services/region/application.fam +++ b/applications/services/region/application.fam @@ -6,5 +6,5 @@ App( entry_point="region_on_system_start", cdefines=["SRV_REGION"], requires=["storage"], - order=170, + order=120, ) diff --git a/applications/services/rpc/rpc.c b/applications/services/rpc/rpc.c index 41d55841e..1a19348ff 100644 --- a/applications/services/rpc/rpc.c +++ b/applications/services/rpc/rpc.c @@ -9,7 +9,8 @@ #include -#include +#include +#include #include #include #include @@ -435,9 +436,14 @@ void rpc_on_system_start(void* p) { rpc->busy_mutex = furi_mutex_alloc(FuriMutexTypeNormal); - Cli* cli = furi_record_open(RECORD_CLI); - cli_add_command( - cli, "start_rpc_session", CliCommandFlagParallelSafe, rpc_cli_command_start_session, rpc); + CliRegistry* registry = furi_record_open(RECORD_CLI); + cli_registry_add_command( + registry, + "start_rpc_session", + CliCommandFlagParallelSafe, + rpc_cli_command_start_session, + rpc); + furi_record_close(RECORD_CLI); furi_record_create(RECORD_RPC, rpc); } diff --git a/applications/services/rpc/rpc_cli.c b/applications/services/rpc/rpc_cli.c index 4612752a8..fda059ec8 100644 --- a/applications/services/rpc/rpc_cli.c +++ b/applications/services/rpc/rpc_cli.c @@ -1,25 +1,26 @@ -#include +#include +#include #include #include #include +#include #define TAG "RpcCli" typedef struct { - Cli* cli; + PipeSide* pipe; bool session_close_request; FuriSemaphore* terminate_semaphore; } CliRpc; -#define CLI_READ_BUFFER_SIZE 64 +#define CLI_READ_BUFFER_SIZE 64UL static void rpc_cli_send_bytes_callback(void* context, uint8_t* bytes, size_t bytes_len) { furi_assert(context); furi_assert(bytes); furi_assert(bytes_len > 0); CliRpc* cli_rpc = context; - - cli_write(cli_rpc->cli, bytes, bytes_len); + pipe_send(cli_rpc->pipe, bytes, bytes_len); } static void rpc_cli_session_close_callback(void* context) { @@ -36,9 +37,9 @@ static void rpc_cli_session_terminated_callback(void* context) { furi_semaphore_release(cli_rpc->terminate_semaphore); } -void rpc_cli_command_start_session(Cli* cli, FuriString* args, void* context) { +void rpc_cli_command_start_session(PipeSide* pipe, FuriString* args, void* context) { UNUSED(args); - furi_assert(cli); + furi_assert(pipe); furi_assert(context); Rpc* rpc = context; @@ -53,7 +54,7 @@ void rpc_cli_command_start_session(Cli* cli, FuriString* args, void* context) { return; } - CliRpc cli_rpc = {.cli = cli, .session_close_request = false}; + CliRpc cli_rpc = {.pipe = pipe, .session_close_request = false}; cli_rpc.terminate_semaphore = furi_semaphore_alloc(1, 0); rpc_session_set_context(rpc_session, &cli_rpc); rpc_session_set_send_bytes_callback(rpc_session, rpc_cli_send_bytes_callback); @@ -64,8 +65,9 @@ void rpc_cli_command_start_session(Cli* cli, FuriString* args, void* context) { size_t size_received = 0; while(1) { - size_received = cli_read_timeout(cli_rpc.cli, buffer, CLI_READ_BUFFER_SIZE, 50); - if(!cli_is_connected(cli_rpc.cli) || cli_rpc.session_close_request) { + size_t to_receive = CLAMP(pipe_bytes_available(cli_rpc.pipe), CLI_READ_BUFFER_SIZE, 1UL); + size_received = pipe_receive(cli_rpc.pipe, buffer, to_receive); + if(size_received < to_receive || cli_rpc.session_close_request) { break; } diff --git a/applications/services/rpc/rpc_i.h b/applications/services/rpc/rpc_i.h index 0342df2b6..df1f17de4 100644 --- a/applications/services/rpc/rpc_i.h +++ b/applications/services/rpc/rpc_i.h @@ -5,7 +5,7 @@ #include #include #include -#include +#include #ifdef __cplusplus extern "C" { @@ -46,7 +46,7 @@ void rpc_desktop_free(void* ctx); void rpc_debug_print_message(const PB_Main* message); void rpc_debug_print_data(const char* prefix, uint8_t* buffer, size_t size); -void rpc_cli_command_start_session(Cli* cli, FuriString* args, void* context); +void rpc_cli_command_start_session(PipeSide* pipe, FuriString* args, void* context); PB_CommandStatus rpc_system_storage_get_error(FS_Error fs_error); diff --git a/applications/services/storage/application.fam b/applications/services/storage/application.fam index 7aa721cc3..047500fa3 100644 --- a/applications/services/storage/application.fam +++ b/applications/services/storage/application.fam @@ -16,5 +16,5 @@ App( apptype=FlipperAppType.STARTUP, entry_point="storage_on_system_start", requires=["storage"], - order=90, + order=60, ) diff --git a/applications/services/storage/storage_cli.c b/applications/services/storage/storage_cli.c index 441b58da6..2dab63e53 100644 --- a/applications/services/storage/storage_cli.c +++ b/applications/services/storage/storage_cli.c @@ -1,7 +1,9 @@ #include #include -#include +#include +#include +#include #include #include #include @@ -10,6 +12,7 @@ #include #include #include +#include #define MAX_NAME_LENGTH 255 @@ -19,8 +22,8 @@ static void storage_cli_print_error(FS_Error error) { printf("Storage error: %s\r\n", storage_error_get_desc(error)); } -static void storage_cli_info(Cli* cli, FuriString* path, FuriString* args) { - UNUSED(cli); +static void storage_cli_info(PipeSide* pipe, FuriString* path, FuriString* args) { + UNUSED(pipe); UNUSED(args); Storage* api = furi_record_open(RECORD_STORAGE); @@ -69,13 +72,14 @@ static void storage_cli_info(Cli* cli, FuriString* path, FuriString* args) { furi_record_close(RECORD_STORAGE); } -static void storage_cli_format(Cli* cli, FuriString* path, FuriString* args) { +static void storage_cli_format(PipeSide* pipe, FuriString* path, FuriString* args) { + UNUSED(pipe); UNUSED(args); if(furi_string_cmp_str(path, STORAGE_INT_PATH_PREFIX) == 0) { storage_cli_print_error(FSE_NOT_IMPLEMENTED); } else if(furi_string_cmp_str(path, STORAGE_EXT_PATH_PREFIX) == 0) { printf("Formatting SD card, All data will be lost! Are you sure (y/n)?\r\n"); - char answer = cli_getc(cli); + char answer = getchar(); if(answer == 'y' || answer == 'Y') { Storage* api = furi_record_open(RECORD_STORAGE); printf("Formatting, please wait...\r\n"); @@ -96,8 +100,8 @@ static void storage_cli_format(Cli* cli, FuriString* path, FuriString* args) { } } -static void storage_cli_list(Cli* cli, FuriString* path, FuriString* args) { - UNUSED(cli); +static void storage_cli_list(PipeSide* pipe, FuriString* path, FuriString* args) { + UNUSED(pipe); UNUSED(args); if(furi_string_cmp_str(path, "/") == 0) { printf("\t[D] int\r\n"); @@ -134,13 +138,13 @@ static void storage_cli_list(Cli* cli, FuriString* path, FuriString* args) { } } -static void storage_cli_tree(Cli* cli, FuriString* path, FuriString* args) { +static void storage_cli_tree(PipeSide* pipe, FuriString* path, FuriString* args) { UNUSED(args); if(furi_string_cmp_str(path, "/") == 0) { furi_string_set(path, STORAGE_INT_PATH_PREFIX); - storage_cli_tree(cli, path, NULL); + storage_cli_tree(pipe, path, NULL); furi_string_set(path, STORAGE_EXT_PATH_PREFIX); - storage_cli_tree(cli, path, NULL); + storage_cli_tree(pipe, path, NULL); } else { Storage* api = furi_record_open(RECORD_STORAGE); DirWalk* dir_walk = dir_walk_alloc(api); @@ -176,8 +180,8 @@ static void storage_cli_tree(Cli* cli, FuriString* path, FuriString* args) { } } -static void storage_cli_read(Cli* cli, FuriString* path, FuriString* args) { - UNUSED(cli); +static void storage_cli_read(PipeSide* pipe, FuriString* path, FuriString* args) { + UNUSED(pipe); UNUSED(args); Storage* api = furi_record_open(RECORD_STORAGE); File* file = storage_file_alloc(api); @@ -208,7 +212,8 @@ static void storage_cli_read(Cli* cli, FuriString* path, FuriString* args) { furi_record_close(RECORD_STORAGE); } -static void storage_cli_write(Cli* cli, FuriString* path, FuriString* args) { +static void storage_cli_write(PipeSide* pipe, FuriString* path, FuriString* args) { + UNUSED(pipe); UNUSED(args); Storage* api = furi_record_open(RECORD_STORAGE); File* file = storage_file_alloc(api); @@ -222,9 +227,9 @@ static void storage_cli_write(Cli* cli, FuriString* path, FuriString* args) { uint32_t read_index = 0; while(true) { - uint8_t symbol = cli_getc(cli); + uint8_t symbol = getchar(); - if(symbol == CliSymbolAsciiETX) { + if(symbol == CliKeyETX) { size_t write_size = read_index % buffer_size; if(write_size > 0) { @@ -263,7 +268,8 @@ static void storage_cli_write(Cli* cli, FuriString* path, FuriString* args) { furi_record_close(RECORD_STORAGE); } -static void storage_cli_read_chunks(Cli* cli, FuriString* path, FuriString* args) { +static void storage_cli_read_chunks(PipeSide* pipe, FuriString* path, FuriString* args) { + UNUSED(pipe); Storage* api = furi_record_open(RECORD_STORAGE); File* file = storage_file_alloc(api); @@ -280,7 +286,7 @@ static void storage_cli_read_chunks(Cli* cli, FuriString* path, FuriString* args uint8_t* data = malloc(buffer_size); while(file_size > 0) { printf("\r\nReady?\r\n"); - cli_getc(cli); + getchar(); size_t read_size = storage_file_read(file, data, buffer_size); for(size_t i = 0; i < read_size; i++) { @@ -302,31 +308,34 @@ static void storage_cli_read_chunks(Cli* cli, FuriString* path, FuriString* args furi_record_close(RECORD_STORAGE); } -static void storage_cli_write_chunk(Cli* cli, FuriString* path, FuriString* args) { +static void storage_cli_write_chunk(PipeSide* pipe, FuriString* path, FuriString* args) { Storage* api = furi_record_open(RECORD_STORAGE); File* file = storage_file_alloc(api); - uint32_t buffer_size; - if(strint_to_uint32(furi_string_get_cstr(args), NULL, &buffer_size, 10) != + uint32_t need_to_read; + if(strint_to_uint32(furi_string_get_cstr(args), NULL, &need_to_read, 10) != StrintParseNoError) { storage_cli_print_usage(); } else { if(storage_file_open(file, furi_string_get_cstr(path), FSAM_WRITE, FSOM_OPEN_APPEND)) { printf("Ready\r\n"); + const size_t buffer_size = 1024; + uint8_t* buffer = malloc(buffer_size); - if(buffer_size) { - uint8_t* buffer = malloc(buffer_size); + while(need_to_read) { + size_t to_read_this_time = MIN(buffer_size, need_to_read); + size_t read_this_time = pipe_receive(pipe, buffer, to_read_this_time); + if(read_this_time != to_read_this_time) break; - size_t read_bytes = cli_read(cli, buffer, buffer_size); - - size_t written_size = storage_file_write(file, buffer, read_bytes); - - if(written_size != buffer_size) { + size_t wrote_this_time = storage_file_write(file, buffer, read_this_time); + if(wrote_this_time != read_this_time) { storage_cli_print_error(storage_file_get_error(file)); + break; } - - free(buffer); + need_to_read -= read_this_time; } + + free(buffer); } else { storage_cli_print_error(storage_file_get_error(file)); } @@ -337,8 +346,8 @@ static void storage_cli_write_chunk(Cli* cli, FuriString* path, FuriString* args furi_record_close(RECORD_STORAGE); } -static void storage_cli_stat(Cli* cli, FuriString* path, FuriString* args) { - UNUSED(cli); +static void storage_cli_stat(PipeSide* pipe, FuriString* path, FuriString* args) { + UNUSED(pipe); UNUSED(args); Storage* api = furi_record_open(RECORD_STORAGE); @@ -379,8 +388,8 @@ static void storage_cli_stat(Cli* cli, FuriString* path, FuriString* args) { furi_record_close(RECORD_STORAGE); } -static void storage_cli_timestamp(Cli* cli, FuriString* path, FuriString* args) { - UNUSED(cli); +static void storage_cli_timestamp(PipeSide* pipe, FuriString* path, FuriString* args) { + UNUSED(pipe); UNUSED(args); Storage* api = furi_record_open(RECORD_STORAGE); @@ -396,8 +405,8 @@ static void storage_cli_timestamp(Cli* cli, FuriString* path, FuriString* args) furi_record_close(RECORD_STORAGE); } -static void storage_cli_copy(Cli* cli, FuriString* old_path, FuriString* args) { - UNUSED(cli); +static void storage_cli_copy(PipeSide* pipe, FuriString* old_path, FuriString* args) { + UNUSED(pipe); Storage* api = furi_record_open(RECORD_STORAGE); FuriString* new_path; new_path = furi_string_alloc(); @@ -417,8 +426,8 @@ static void storage_cli_copy(Cli* cli, FuriString* old_path, FuriString* args) { furi_record_close(RECORD_STORAGE); } -static void storage_cli_remove(Cli* cli, FuriString* path, FuriString* args) { - UNUSED(cli); +static void storage_cli_remove(PipeSide* pipe, FuriString* path, FuriString* args) { + UNUSED(pipe); UNUSED(args); Storage* api = furi_record_open(RECORD_STORAGE); FS_Error error = storage_common_remove(api, furi_string_get_cstr(path)); @@ -430,8 +439,8 @@ static void storage_cli_remove(Cli* cli, FuriString* path, FuriString* args) { furi_record_close(RECORD_STORAGE); } -static void storage_cli_rename(Cli* cli, FuriString* old_path, FuriString* args) { - UNUSED(cli); +static void storage_cli_rename(PipeSide* pipe, FuriString* old_path, FuriString* args) { + UNUSED(pipe); Storage* api = furi_record_open(RECORD_STORAGE); FuriString* new_path; new_path = furi_string_alloc(); @@ -451,8 +460,8 @@ static void storage_cli_rename(Cli* cli, FuriString* old_path, FuriString* args) furi_record_close(RECORD_STORAGE); } -static void storage_cli_mkdir(Cli* cli, FuriString* path, FuriString* args) { - UNUSED(cli); +static void storage_cli_mkdir(PipeSide* pipe, FuriString* path, FuriString* args) { + UNUSED(pipe); UNUSED(args); Storage* api = furi_record_open(RECORD_STORAGE); FS_Error error = storage_common_mkdir(api, furi_string_get_cstr(path)); @@ -464,8 +473,8 @@ static void storage_cli_mkdir(Cli* cli, FuriString* path, FuriString* args) { furi_record_close(RECORD_STORAGE); } -static void storage_cli_md5(Cli* cli, FuriString* path, FuriString* args) { - UNUSED(cli); +static void storage_cli_md5(PipeSide* pipe, FuriString* path, FuriString* args) { + UNUSED(pipe); UNUSED(args); Storage* api = furi_record_open(RECORD_STORAGE); File* file = storage_file_alloc(api); @@ -491,8 +500,8 @@ static bool tar_extract_file_callback(const char* name, bool is_directory, void* return true; } -static void storage_cli_extract(Cli* cli, FuriString* old_path, FuriString* args) { - UNUSED(cli); +static void storage_cli_extract(PipeSide* pipe, FuriString* old_path, FuriString* args) { + UNUSED(pipe); FuriString* new_path = furi_string_alloc(); if(!args_read_probably_quoted_string_and_trim(args, new_path)) { @@ -526,7 +535,7 @@ static void storage_cli_extract(Cli* cli, FuriString* old_path, FuriString* args furi_record_close(RECORD_STORAGE); } -typedef void (*StorageCliCommandCallback)(Cli* cli, FuriString* path, FuriString* args); +typedef void (*StorageCliCommandCallback)(PipeSide* pipe, FuriString* path, FuriString* args); typedef struct { const char* command; @@ -631,7 +640,7 @@ static void storage_cli_print_usage(void) { } } -void storage_cli(Cli* cli, FuriString* args, void* context) { +void storage_cli(PipeSide* pipe, FuriString* args, void* context) { UNUSED(context); FuriString* cmd; FuriString* path; @@ -653,7 +662,7 @@ void storage_cli(Cli* cli, FuriString* args, void* context) { for(; i < COUNT_OF(storage_cli_commands); ++i) { const StorageCliCommand* command_descr = &storage_cli_commands[i]; if(furi_string_cmp_str(cmd, command_descr->command) == 0) { - command_descr->impl(cli, path, args); + command_descr->impl(pipe, path, args); break; } } @@ -667,11 +676,12 @@ void storage_cli(Cli* cli, FuriString* args, void* context) { furi_string_free(cmd); } -static void storage_cli_factory_reset(Cli* cli, FuriString* args, void* context) { +static void storage_cli_factory_reset(PipeSide* pipe, FuriString* args, void* context) { + UNUSED(pipe); UNUSED(args); UNUSED(context); printf("All data will be lost! Are you sure (y/n)?\r\n"); - char c = cli_getc(cli); + char c = getchar(); if(c == 'y' || c == 'Y') { printf("Data will be wiped after reboot.\r\n"); @@ -687,10 +697,15 @@ static void storage_cli_factory_reset(Cli* cli, FuriString* args, void* context) void storage_on_system_start(void) { #ifdef SRV_CLI - Cli* cli = furi_record_open(RECORD_CLI); - cli_add_command(cli, RECORD_STORAGE, CliCommandFlagParallelSafe, storage_cli, NULL); - cli_add_command( - cli, "factory_reset", CliCommandFlagParallelSafe, storage_cli_factory_reset, NULL); + CliRegistry* registry = furi_record_open(RECORD_CLI); + cli_registry_add_command( + registry, + "storage", + CliCommandFlagParallelSafe | CliCommandFlagUseShellThread, + storage_cli, + NULL); + cli_registry_add_command( + registry, "factory_reset", CliCommandFlagParallelSafe, storage_cli_factory_reset, NULL); furi_record_close(RECORD_CLI); #else UNUSED(storage_cli_factory_reset); diff --git a/applications/settings/power_settings_app/power_settings_app.c b/applications/settings/power_settings_app/power_settings_app.c index d43bd4108..6959bdf41 100644 --- a/applications/settings/power_settings_app/power_settings_app.c +++ b/applications/settings/power_settings_app/power_settings_app.c @@ -1,5 +1,16 @@ #include "power_settings_app.h" +const SubmenuSettingsHelperDescriptor settings_helper_descriptor = { + .app_name = "Power", + .options_cnt = 3, + .options = + { + {.name = "Battery Info", .scene_id = PowerSettingsAppSceneBatteryInfo}, + {.name = "Reboot", .scene_id = PowerSettingsAppSceneReboot}, + {.name = "Power OFF", .scene_id = PowerSettingsAppScenePowerOff}, + }, +}; + static bool power_settings_custom_event_callback(void* context, uint32_t event) { furi_assert(context); PowerSettingsApp* app = context; @@ -18,7 +29,7 @@ static void power_settings_tick_event_callback(void* context) { scene_manager_handle_tick_event(app->scene_manager); } -PowerSettingsApp* power_settings_app_alloc(uint32_t first_scene) { +PowerSettingsApp* power_settings_app_alloc(void) { PowerSettingsApp* app = malloc(sizeof(PowerSettingsApp)); // Records @@ -50,13 +61,23 @@ PowerSettingsApp* power_settings_app_alloc(uint32_t first_scene) { view_dispatcher_add_view( app->view_dispatcher, PowerSettingsAppViewDialog, dialog_ex_get_view(app->dialog)); - // Set first scene - scene_manager_next_scene(app->scene_manager, first_scene); + // Helper + app->settings_helper = submenu_settings_helpers_alloc(&settings_helper_descriptor); + submenu_settings_helpers_assign_objects( + app->settings_helper, + app->view_dispatcher, + app->scene_manager, + app->submenu, + PowerSettingsAppViewSubmenu, + PowerSettingsAppSceneStart); + return app; } void power_settings_app_free(PowerSettingsApp* app) { furi_assert(app); + // Helper + submenu_settings_helpers_free(app->settings_helper); // Views view_dispatcher_remove_view(app->view_dispatcher, PowerSettingsAppViewBatteryInfo); battery_info_free(app->batery_info); @@ -74,11 +95,14 @@ void power_settings_app_free(PowerSettingsApp* app) { } int32_t power_settings_app(void* p) { - uint32_t first_scene = PowerSettingsAppSceneStart; - if(p && strlen(p) && !strcmp(p, "off")) { - first_scene = PowerSettingsAppScenePowerOff; + PowerSettingsApp* app = power_settings_app_alloc(); + if(!submenu_settings_helpers_app_start(app->settings_helper, p)) { + uint32_t first_scene = PowerSettingsAppSceneStart; + if(p && strlen(p) && !strcmp(p, "off")) { + first_scene = PowerSettingsAppScenePowerOff; + } + scene_manager_next_scene(app->scene_manager, first_scene); } - PowerSettingsApp* app = power_settings_app_alloc(first_scene); view_dispatcher_run(app->view_dispatcher); power_settings_app_free(app); return 0; diff --git a/applications/settings/power_settings_app/power_settings_app.h b/applications/settings/power_settings_app/power_settings_app.h index e19e7c983..b33d63db2 100644 --- a/applications/settings/power_settings_app/power_settings_app.h +++ b/applications/settings/power_settings_app/power_settings_app.h @@ -14,6 +14,8 @@ #include "scenes/power_settings_scene.h" +#include + typedef struct { Power* power; Gui* gui; @@ -23,6 +25,7 @@ typedef struct { Submenu* submenu; DialogEx* dialog; PowerInfo info; + SubmenuSettingsHelper* settings_helper; } PowerSettingsApp; typedef enum { diff --git a/applications/settings/power_settings_app/scenes/power_settings_scene_start.c b/applications/settings/power_settings_app/scenes/power_settings_scene_start.c index 63f123d89..f8a5a7edb 100644 --- a/applications/settings/power_settings_app/scenes/power_settings_scene_start.c +++ b/applications/settings/power_settings_app/scenes/power_settings_scene_start.c @@ -1,64 +1,16 @@ #include "../power_settings_app.h" -enum PowerSettingsSubmenuIndex { - PowerSettingsSubmenuIndexBatteryInfo, - PowerSettingsSubmenuIndexReboot, - PowerSettingsSubmenuIndexOff, -}; - -static void power_settings_scene_start_submenu_callback(void* context, uint32_t index) { - furi_assert(context); - PowerSettingsApp* app = context; - view_dispatcher_send_custom_event(app->view_dispatcher, index); -} - void power_settings_scene_start_on_enter(void* context) { PowerSettingsApp* app = context; - Submenu* submenu = app->submenu; - - submenu_add_item( - submenu, - "Battery Info", - PowerSettingsSubmenuIndexBatteryInfo, - power_settings_scene_start_submenu_callback, - app); - submenu_add_item( - submenu, - "Reboot", - PowerSettingsSubmenuIndexReboot, - power_settings_scene_start_submenu_callback, - app); - submenu_add_item( - submenu, - "Power OFF", - PowerSettingsSubmenuIndexOff, - power_settings_scene_start_submenu_callback, - app); - submenu_set_selected_item( - submenu, scene_manager_get_scene_state(app->scene_manager, PowerSettingsAppSceneStart)); - - view_dispatcher_switch_to_view(app->view_dispatcher, PowerSettingsAppViewSubmenu); + submenu_settings_helpers_scene_enter(app->settings_helper); } bool power_settings_scene_start_on_event(void* context, SceneManagerEvent event) { PowerSettingsApp* app = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == PowerSettingsSubmenuIndexBatteryInfo) { - scene_manager_next_scene(app->scene_manager, PowerSettingsAppSceneBatteryInfo); - } else if(event.event == PowerSettingsSubmenuIndexReboot) { - scene_manager_next_scene(app->scene_manager, PowerSettingsAppSceneReboot); - } else if(event.event == PowerSettingsSubmenuIndexOff) { - scene_manager_next_scene(app->scene_manager, PowerSettingsAppScenePowerOff); - } - scene_manager_set_scene_state(app->scene_manager, PowerSettingsAppSceneStart, event.event); - consumed = true; - } - return consumed; + return submenu_settings_helpers_scene_event(app->settings_helper, event); } void power_settings_scene_start_on_exit(void* context) { PowerSettingsApp* app = context; - submenu_reset(app->submenu); + submenu_settings_helpers_scene_exit(app->settings_helper); } diff --git a/applications/settings/storage_settings/scenes/storage_settings_scene_start.c b/applications/settings/storage_settings/scenes/storage_settings_scene_start.c index e351a2ef7..f03474ac1 100644 --- a/applications/settings/storage_settings/scenes/storage_settings_scene_start.c +++ b/applications/settings/storage_settings/scenes/storage_settings_scene_start.c @@ -1,131 +1,20 @@ #include "../storage_settings.h" -enum StorageSettingsStartSubmenuIndex { - StorageSettingsStartSubmenuIndexInternalInfo, - StorageSettingsStartSubmenuIndexSDInfo, - StorageSettingsStartSubmenuIndexUnmount, - StorageSettingsStartSubmenuIndexFormat, - StorageSettingsStartSubmenuIndexBenchy, - StorageSettingsStartSubmenuIndexFactoryReset -}; - -static void storage_settings_scene_start_submenu_callback(void* context, uint32_t index) { - StorageSettings* app = context; - - view_dispatcher_send_custom_event(app->view_dispatcher, index); -} - void storage_settings_scene_start_on_enter(void* context) { StorageSettings* app = context; - Submenu* submenu = app->submenu; - - submenu_add_item( - submenu, - "About Internal Storage", - StorageSettingsStartSubmenuIndexInternalInfo, - storage_settings_scene_start_submenu_callback, - app); - submenu_add_item( - submenu, - "About SD Card", - StorageSettingsStartSubmenuIndexSDInfo, - storage_settings_scene_start_submenu_callback, - app); FS_Error sd_status = storage_sd_status(app->fs_api); - if(sd_status != FSE_OK) { - submenu_add_item( - submenu, - "Mount SD Card", - StorageSettingsStartSubmenuIndexUnmount, - storage_settings_scene_start_submenu_callback, - app); - } else { - submenu_add_item( - submenu, - "Unmount SD Card", - StorageSettingsStartSubmenuIndexUnmount, - storage_settings_scene_start_submenu_callback, - app); - } - - submenu_add_item( - submenu, - "Format SD Card", - StorageSettingsStartSubmenuIndexFormat, - storage_settings_scene_start_submenu_callback, - app); - submenu_add_item( - submenu, - "Benchmark SD Card", - StorageSettingsStartSubmenuIndexBenchy, - storage_settings_scene_start_submenu_callback, - app); - submenu_add_item( - submenu, - "Factory Reset", - StorageSettingsStartSubmenuIndexFactoryReset, - storage_settings_scene_start_submenu_callback, - app); - - submenu_set_selected_item( - submenu, scene_manager_get_scene_state(app->scene_manager, StorageSettingsStart)); - - view_dispatcher_switch_to_view(app->view_dispatcher, StorageSettingsViewSubmenu); + app->helper_descriptor->options[STORAGE_SETTINGS_MOUNT_INDEX].name = + (sd_status != FSE_OK) ? "Mount SD Card" : "Unmount SD Card"; + submenu_settings_helpers_scene_enter(app->settings_helper); } bool storage_settings_scene_start_on_event(void* context, SceneManagerEvent event) { StorageSettings* app = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - switch(event.event) { - case StorageSettingsStartSubmenuIndexSDInfo: - scene_manager_set_scene_state( - app->scene_manager, StorageSettingsStart, StorageSettingsStartSubmenuIndexSDInfo); - scene_manager_next_scene(app->scene_manager, StorageSettingsSDInfo); - consumed = true; - break; - case StorageSettingsStartSubmenuIndexInternalInfo: - scene_manager_set_scene_state( - app->scene_manager, - StorageSettingsStart, - StorageSettingsStartSubmenuIndexInternalInfo); - scene_manager_next_scene(app->scene_manager, StorageSettingsInternalInfo); - consumed = true; - break; - case StorageSettingsStartSubmenuIndexUnmount: - scene_manager_set_scene_state( - app->scene_manager, StorageSettingsStart, StorageSettingsStartSubmenuIndexUnmount); - scene_manager_next_scene(app->scene_manager, StorageSettingsUnmountConfirm); - consumed = true; - break; - case StorageSettingsStartSubmenuIndexFormat: - scene_manager_set_scene_state( - app->scene_manager, StorageSettingsStart, StorageSettingsStartSubmenuIndexFormat); - scene_manager_next_scene(app->scene_manager, StorageSettingsFormatConfirm); - consumed = true; - break; - case StorageSettingsStartSubmenuIndexBenchy: - scene_manager_set_scene_state( - app->scene_manager, StorageSettingsStart, StorageSettingsStartSubmenuIndexBenchy); - scene_manager_next_scene(app->scene_manager, StorageSettingsBenchmarkConfirm); - consumed = true; - break; - case StorageSettingsStartSubmenuIndexFactoryReset: - scene_manager_set_scene_state( - app->scene_manager, - StorageSettingsStart, - StorageSettingsStartSubmenuIndexFactoryReset); - scene_manager_next_scene(app->scene_manager, StorageSettingsFactoryReset); - consumed = true; - break; - } - } - return consumed; + return submenu_settings_helpers_scene_event(app->settings_helper, event); } void storage_settings_scene_start_on_exit(void* context) { StorageSettings* app = context; - submenu_reset(app->submenu); + submenu_settings_helpers_scene_exit(app->settings_helper); } diff --git a/applications/settings/storage_settings/storage_settings.c b/applications/settings/storage_settings/storage_settings.c index 354632890..b4cde7b6b 100644 --- a/applications/settings/storage_settings/storage_settings.c +++ b/applications/settings/storage_settings/storage_settings.c @@ -1,5 +1,19 @@ #include "storage_settings.h" +const SubmenuSettingsHelperDescriptor descriptor_template = { + .app_name = "Storage", + .options_cnt = 6, + .options = + { + {.name = "About Internal Storage", .scene_id = StorageSettingsSDInfo}, + {.name = "About SD Card", .scene_id = StorageSettingsInternalInfo}, + {.name = "Unmount SD Card", .scene_id = StorageSettingsUnmountConfirm}, + {.name = "Format SD Card", .scene_id = StorageSettingsFormatConfirm}, + {.name = "Benchmark SD Card", .scene_id = StorageSettingsBenchmarkConfirm}, + {.name = "Factory Reset", .scene_id = StorageSettingsFactoryReset}, + }, +}; + static bool storage_settings_custom_event_callback(void* context, uint32_t event) { furi_assert(context); StorageSettings* app = context; @@ -40,12 +54,27 @@ static StorageSettings* storage_settings_alloc(void) { view_dispatcher_add_view( app->view_dispatcher, StorageSettingsViewDialogEx, dialog_ex_get_view(app->dialog_ex)); - scene_manager_next_scene(app->scene_manager, StorageSettingsStart); + size_t descriptor_size = + sizeof(SubmenuSettingsHelperDescriptor) + + (descriptor_template.options_cnt * sizeof(SubmenuSettingsHelperOption)); + app->helper_descriptor = malloc(descriptor_size); + memcpy(app->helper_descriptor, &descriptor_template, descriptor_size); + app->settings_helper = submenu_settings_helpers_alloc(app->helper_descriptor); + submenu_settings_helpers_assign_objects( + app->settings_helper, + app->view_dispatcher, + app->scene_manager, + app->submenu, + StorageSettingsViewSubmenu, + StorageSettingsStart); return app; } static void storage_settings_free(StorageSettings* app) { + submenu_settings_helpers_free(app->settings_helper); + free(app->helper_descriptor); + view_dispatcher_remove_view(app->view_dispatcher, StorageSettingsViewSubmenu); submenu_free(app->submenu); @@ -68,6 +97,10 @@ int32_t storage_settings_app(void* p) { UNUSED(p); StorageSettings* app = storage_settings_alloc(); + if(!submenu_settings_helpers_app_start(app->settings_helper, p)) { + scene_manager_next_scene(app->scene_manager, StorageSettingsStart); + } + view_dispatcher_run(app->view_dispatcher); storage_settings_free(app); diff --git a/applications/settings/storage_settings/storage_settings.h b/applications/settings/storage_settings/storage_settings.h index fd841623e..5f4c6404e 100644 --- a/applications/settings/storage_settings/storage_settings.h +++ b/applications/settings/storage_settings/storage_settings.h @@ -16,6 +16,10 @@ #include "scenes/storage_settings_scene.h" +#include + +#define STORAGE_SETTINGS_MOUNT_INDEX 2 + #ifdef __cplusplus extern "C" { #endif @@ -36,6 +40,10 @@ typedef struct { // text FuriString* text_string; + + // helpers + SubmenuSettingsHelperDescriptor* helper_descriptor; + SubmenuSettingsHelper* settings_helper; } StorageSettings; typedef enum { diff --git a/applications/system/hid_app/application.fam b/applications/system/hid_app/application.fam index cc218c31a..aae5ebf9d 100644 --- a/applications/system/hid_app/application.fam +++ b/applications/system/hid_app/application.fam @@ -3,11 +3,11 @@ App( name="Remote", apptype=FlipperAppType.EXTERNAL, entry_point="hid_usb_app", - stack_size=1 * 1024, + stack_size=2 * 1024, sources=["*.c", "!transport_ble.c"], cdefines=["HID_TRANSPORT_USB"], fap_description="Use Flipper as a HID remote control over USB", - fap_version="1.0", + fap_version="1.1", fap_category="USB", fap_icon="hid_usb_10px.png", fap_icon_assets="assets", @@ -20,12 +20,12 @@ App( name="Remote", apptype=FlipperAppType.EXTERNAL, entry_point="hid_ble_app", - stack_size=1 * 1024, + stack_size=2 * 1024, sources=["*.c", "!transport_usb.c"], cdefines=["HID_TRANSPORT_BLE"], fap_libs=["ble_profile"], fap_description="Use Flipper as a HID remote control over Bluetooth", - fap_version="1.0", + fap_version="1.1", fap_category="Bluetooth", fap_icon="hid_ble_10px.png", fap_icon_assets="assets", diff --git a/applications/system/hid_app/views/hid_tiktok.c b/applications/system/hid_app/views/hid_tiktok.c index f1501027c..f0997f72e 100644 --- a/applications/system/hid_app/views/hid_tiktok.c +++ b/applications/system/hid_app/views/hid_tiktok.c @@ -103,7 +103,10 @@ static void hid_tiktok_reset_cursor(HidTikTok* hid_tiktok) { furi_delay_ms(50); } // Move cursor from the corner - hid_hal_mouse_move(hid_tiktok->hid, 20, 120); + // Actions split for some mobiles to properly process mouse movements + hid_hal_mouse_move(hid_tiktok->hid, 10, 60); + furi_delay_ms(3); + hid_hal_mouse_move(hid_tiktok->hid, 0, 60); furi_delay_ms(50); } @@ -162,29 +165,30 @@ static bool hid_tiktok_input_callback(InputEvent* event, void* context) { consumed = true; } else if(event->type == InputTypeShort) { if(event->key == InputKeyOk) { + // delays adjusted for emulation of a finger tap hid_hal_mouse_press(hid_tiktok->hid, HID_MOUSE_BTN_LEFT); - furi_delay_ms(50); + furi_delay_ms(25); hid_hal_mouse_release(hid_tiktok->hid, HID_MOUSE_BTN_LEFT); - furi_delay_ms(50); + furi_delay_ms(75); hid_hal_mouse_press(hid_tiktok->hid, HID_MOUSE_BTN_LEFT); - furi_delay_ms(50); + furi_delay_ms(25); hid_hal_mouse_release(hid_tiktok->hid, HID_MOUSE_BTN_LEFT); consumed = true; } else if(event->key == InputKeyUp) { // Emulate up swipe - hid_hal_mouse_scroll(hid_tiktok->hid, -6); hid_hal_mouse_scroll(hid_tiktok->hid, -12); - hid_hal_mouse_scroll(hid_tiktok->hid, -19); + hid_hal_mouse_scroll(hid_tiktok->hid, -24); + hid_hal_mouse_scroll(hid_tiktok->hid, -38); + hid_hal_mouse_scroll(hid_tiktok->hid, -24); hid_hal_mouse_scroll(hid_tiktok->hid, -12); - hid_hal_mouse_scroll(hid_tiktok->hid, -6); consumed = true; } else if(event->key == InputKeyDown) { // Emulate down swipe - hid_hal_mouse_scroll(hid_tiktok->hid, 6); hid_hal_mouse_scroll(hid_tiktok->hid, 12); - hid_hal_mouse_scroll(hid_tiktok->hid, 19); + hid_hal_mouse_scroll(hid_tiktok->hid, 24); + hid_hal_mouse_scroll(hid_tiktok->hid, 38); + hid_hal_mouse_scroll(hid_tiktok->hid, 24); hid_hal_mouse_scroll(hid_tiktok->hid, 12); - hid_hal_mouse_scroll(hid_tiktok->hid, 6); consumed = true; } else if(event->key == InputKeyBack) { hid_hal_consumer_key_release_all(hid_tiktok->hid); diff --git a/applications/system/js_app/application.fam b/applications/system/js_app/application.fam index db1521b9d..843ab5543 100644 --- a/applications/system/js_app/application.fam +++ b/applications/system/js_app/application.fam @@ -5,12 +5,13 @@ App( entry_point="js_app", stack_size=2 * 1024, resources="examples", - order=0, + order=10, provides=["js_app_start"], sources=[ "js_app.c", "js_modules.c", "js_thread.c", + "js_value.c", "plugin_api/app_api_table.cpp", "views/console_view.c", "modules/js_flipper.c", @@ -22,7 +23,7 @@ App( appid="js_app_start", apptype=FlipperAppType.STARTUP, entry_point="js_app_on_system_start", - order=160, + order=110, sources=["js_app.c"], ) diff --git a/applications/system/js_app/js_app.c b/applications/system/js_app/js_app.c index c321150df..3084b9b2a 100644 --- a/applications/system/js_app/js_app.c +++ b/applications/system/js_app/js_app.c @@ -4,7 +4,9 @@ #include "js_app_i.h" #include #include -#include +#include +#include +#include #define TAG "JS app" @@ -131,12 +133,14 @@ int32_t js_app(void* arg) { } //-V773 typedef struct { - Cli* cli; + PipeSide* pipe; FuriSemaphore* exit_sem; } JsCliContext; static void js_cli_print(JsCliContext* ctx, const char* msg) { - cli_write(ctx->cli, (uint8_t*)msg, strlen(msg)); + UNUSED(ctx); + UNUSED(msg); + pipe_send(ctx->pipe, msg, strlen(msg)); } static void js_cli_exit(JsCliContext* ctx) { @@ -170,7 +174,7 @@ static void js_cli_callback(JsThreadEvent event, const char* msg, void* context) } } -void js_cli_execute(Cli* cli, FuriString* args, void* context) { +void js_cli_execute(PipeSide* pipe, FuriString* args, void* context) { UNUSED(context); const char* path = furi_string_get_cstr(args); @@ -187,14 +191,14 @@ void js_cli_execute(Cli* cli, FuriString* args, void* context) { break; } - JsCliContext ctx = {.cli = cli}; + JsCliContext ctx = {.pipe = pipe}; ctx.exit_sem = furi_semaphore_alloc(1, 0); printf("Running script %s, press CTRL+C to stop\r\n", path); JsThread* js_thread = js_thread_run(path, js_cli_callback, &ctx); while(furi_semaphore_acquire(ctx.exit_sem, 100) != FuriStatusOk) { - if(cli_cmd_interrupt_received(cli)) break; + if(cli_is_pipe_broken_or_is_etx_next_char(pipe)) break; } js_thread_stop(js_thread); @@ -206,8 +210,8 @@ void js_cli_execute(Cli* cli, FuriString* args, void* context) { void js_app_on_system_start(void) { #ifdef SRV_CLI - Cli* cli = furi_record_open(RECORD_CLI); - cli_add_command(cli, "js", CliCommandFlagDefault, js_cli_execute, NULL); + CliRegistry* registry = furi_record_open(RECORD_CLI); + cli_registry_add_command(registry, "js", CliCommandFlagDefault, js_cli_execute, NULL); furi_record_close(RECORD_CLI); #endif } diff --git a/applications/system/js_app/js_modules.c b/applications/system/js_app/js_modules.c index a8480e6a2..f9c08058f 100644 --- a/applications/system/js_app/js_modules.c +++ b/applications/system/js_app/js_modules.c @@ -202,12 +202,15 @@ static JsSdkCompatStatus return JsSdkCompatStatusCompatible; } -#define JS_SDK_COMPAT_ARGS \ - int32_t major, minor; \ - JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_INT32(&major), JS_ARG_INT32(&minor)); +static const JsValueDeclaration js_sdk_version_arg_list[] = { + JS_VALUE_SIMPLE(JsValueTypeInt32), + JS_VALUE_SIMPLE(JsValueTypeInt32), +}; +static const JsValueArguments js_sdk_version_args = JS_VALUE_ARGS(js_sdk_version_arg_list); void js_sdk_compatibility_status(struct mjs* mjs) { - JS_SDK_COMPAT_ARGS; + int32_t major, minor; + JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_sdk_version_args, &major, &minor); JsSdkCompatStatus status = js_internal_sdk_compatibility_status(major, minor); switch(status) { case JsSdkCompatStatusCompatible: @@ -223,7 +226,8 @@ void js_sdk_compatibility_status(struct mjs* mjs) { } void js_is_sdk_compatible(struct mjs* mjs) { - JS_SDK_COMPAT_ARGS; + int32_t major, minor; + JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_sdk_version_args, &major, &minor); JsSdkCompatStatus status = js_internal_sdk_compatibility_status(major, minor); mjs_return(mjs, mjs_mk_boolean(mjs, status == JsSdkCompatStatusCompatible)); } @@ -246,7 +250,8 @@ static bool js_internal_compat_ask_user(const char* message) { } void js_check_sdk_compatibility(struct mjs* mjs) { - JS_SDK_COMPAT_ARGS; + int32_t major, minor; + JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_sdk_version_args, &major, &minor); JsSdkCompatStatus status = js_internal_sdk_compatibility_status(major, minor); if(status != JsSdkCompatStatusCompatible) { FURI_LOG_E( @@ -300,15 +305,20 @@ static bool js_internal_supports_all_of(struct mjs* mjs, mjs_val_t feature_arr) return true; } +static const JsValueDeclaration js_sdk_features_arg_list[] = { + JS_VALUE_SIMPLE(JsValueTypeAnyArray), +}; +static const JsValueArguments js_sdk_features_args = JS_VALUE_ARGS(js_sdk_features_arg_list); + void js_does_sdk_support(struct mjs* mjs) { mjs_val_t features; - JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_ARR(&features)); + JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_sdk_features_args, &features); mjs_return(mjs, mjs_mk_boolean(mjs, js_internal_supports_all_of(mjs, features))); } void js_check_sdk_features(struct mjs* mjs) { mjs_val_t features; - JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_ARR(&features)); + JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_sdk_features_args, &features); if(!js_internal_supports_all_of(mjs, features)) { FURI_LOG_E(TAG, "Script requests unsupported features"); diff --git a/applications/system/js_app/js_modules.h b/applications/system/js_app/js_modules.h index 892e43d4e..c6f72bbe2 100644 --- a/applications/system/js_app/js_modules.h +++ b/applications/system/js_app/js_modules.h @@ -2,10 +2,15 @@ #include #include "js_thread_i.h" +#include "js_value.h" #include #include #include +#ifdef __cplusplus +extern "C" { +#endif + #define PLUGIN_APP_ID "js" #define PLUGIN_API_VERSION 1 @@ -63,226 +68,6 @@ typedef enum { JsForeignMagic_JsEventLoopContract, } JsForeignMagic; -// Are you tired of your silly little JS+C glue code functions being 75% -// argument validation code and 25% actual logic? Introducing: ASS (Argument -// Schema for Scripts)! ASS is a set of macros that reduce the typical -// boilerplate code of "check argument count, get arguments, validate arguments, -// extract C values from arguments" down to just one line! - -/** - * When passed as the second argument to `JS_FETCH_ARGS_OR_RETURN`, signifies - * that the function requires exactly as many arguments as were specified. - */ -#define JS_EXACTLY == -/** - * When passed as the second argument to `JS_FETCH_ARGS_OR_RETURN`, signifies - * that the function requires at least as many arguments as were specified. - */ -#define JS_AT_LEAST >= - -typedef struct { - const char* name; - size_t value; -} JsEnumMapping; - -#define JS_ENUM_MAP(var_name, ...) \ - static const JsEnumMapping var_name##_mapping[] = { \ - {NULL, sizeof(var_name)}, \ - __VA_ARGS__, \ - {NULL, 0}, \ - }; - -typedef struct { - const char* name; - size_t offset; -} JsObjectMapping; - -#define JS_OBJ_MAP(var_name, ...) \ - static const JsObjectMapping var_name##_mapping[] = { \ - __VA_ARGS__, \ - {NULL, 0}, \ - }; - -typedef struct { - void* out; - int (*validator)(mjs_val_t); - void (*converter)(struct mjs*, mjs_val_t*, void* out, const void* extra); - const char* expected_type; - bool (*extended_validator)(struct mjs*, mjs_val_t, const void* extra); - const void* extra_data; -} _js_arg_decl; - -static inline void _js_to_int32(struct mjs* mjs, mjs_val_t* in, void* out, const void* extra) { - UNUSED(extra); - *(int32_t*)out = mjs_get_int32(mjs, *in); -} -#define JS_ARG_INT32(out) ((_js_arg_decl){out, mjs_is_number, _js_to_int32, "number", NULL, NULL}) - -static inline void _js_to_ptr(struct mjs* mjs, mjs_val_t* in, void* out, const void* extra) { - UNUSED(extra); - *(void**)out = mjs_get_ptr(mjs, *in); -} -#define JS_ARG_PTR(out) \ - ((_js_arg_decl){out, mjs_is_foreign, _js_to_ptr, "opaque pointer", NULL, NULL}) - -static inline void _js_to_string(struct mjs* mjs, mjs_val_t* in, void* out, const void* extra) { - UNUSED(extra); - *(const char**)out = mjs_get_string(mjs, in, NULL); -} -#define JS_ARG_STR(out) ((_js_arg_decl){out, mjs_is_string, _js_to_string, "string", NULL, NULL}) - -static inline void _js_to_bool(struct mjs* mjs, mjs_val_t* in, void* out, const void* extra) { - UNUSED(extra); - *(bool*)out = !!mjs_get_bool(mjs, *in); -} -#define JS_ARG_BOOL(out) ((_js_arg_decl){out, mjs_is_boolean, _js_to_bool, "boolean", NULL, NULL}) - -static inline void _js_passthrough(struct mjs* mjs, mjs_val_t* in, void* out, const void* extra) { - UNUSED(extra); - UNUSED(mjs); - *(mjs_val_t*)out = *in; -} -#define JS_ARG_ANY(out) ((_js_arg_decl){out, NULL, _js_passthrough, "any", NULL, NULL}) -#define JS_ARG_OBJ(out) ((_js_arg_decl){out, mjs_is_object, _js_passthrough, "any", NULL, NULL}) -#define JS_ARG_FN(out) \ - ((_js_arg_decl){out, mjs_is_function, _js_passthrough, "function", NULL, NULL}) -#define JS_ARG_ARR(out) ((_js_arg_decl){out, mjs_is_array, _js_passthrough, "array", NULL, NULL}) - -static inline bool _js_validate_struct(struct mjs* mjs, mjs_val_t val, const void* extra) { - JsForeignMagic expected_magic = (JsForeignMagic)(size_t)extra; - JsForeignMagic struct_magic = *(JsForeignMagic*)mjs_get_ptr(mjs, val); - return struct_magic == expected_magic; -} -#define JS_ARG_STRUCT(type, out) \ - ((_js_arg_decl){ \ - out, \ - mjs_is_foreign, \ - _js_to_ptr, \ - #type, \ - _js_validate_struct, \ - (void*)JsForeignMagic##_##type}) - -static inline bool _js_validate_obj_w_struct(struct mjs* mjs, mjs_val_t val, const void* extra) { - JsForeignMagic expected_magic = (JsForeignMagic)(size_t)extra; - JsForeignMagic struct_magic = *(JsForeignMagic*)JS_GET_INST(mjs, val); - return struct_magic == expected_magic; -} -#define JS_ARG_OBJ_WITH_STRUCT(type, out) \ - ((_js_arg_decl){ \ - out, \ - mjs_is_object, \ - _js_passthrough, \ - #type, \ - _js_validate_obj_w_struct, \ - (void*)JsForeignMagic##_##type}) - -static inline bool _js_validate_enum(struct mjs* mjs, mjs_val_t val, const void* extra) { - for(const JsEnumMapping* mapping = (JsEnumMapping*)extra + 1; mapping->name; mapping++) - if(strcmp(mapping->name, mjs_get_string(mjs, &val, NULL)) == 0) return true; - return false; -} -static inline void - _js_convert_enum(struct mjs* mjs, mjs_val_t* val, void* out, const void* extra) { - const JsEnumMapping* mapping = (JsEnumMapping*)extra; - size_t size = mapping->value; // get enum size from first entry - for(mapping++; mapping->name; mapping++) { - if(strcmp(mapping->name, mjs_get_string(mjs, val, NULL)) == 0) { - if(size == 1) - *(uint8_t*)out = mapping->value; - else if(size == 2) - *(uint16_t*)out = mapping->value; - else if(size == 4) - *(uint32_t*)out = mapping->value; - else if(size == 8) - *(uint64_t*)out = mapping->value; - return; - } - } - // unreachable, thanks to _js_validate_enum -} -#define JS_ARG_ENUM(var_name, name) \ - ((_js_arg_decl){ \ - &var_name, \ - mjs_is_string, \ - _js_convert_enum, \ - name " enum", \ - _js_validate_enum, \ - var_name##_mapping}) - -static inline bool _js_validate_object(struct mjs* mjs, mjs_val_t val, const void* extra) { - for(const JsObjectMapping* mapping = (JsObjectMapping*)extra; mapping->name; mapping++) - if(mjs_get(mjs, val, mapping->name, ~0) == MJS_UNDEFINED) return false; - return true; -} -static inline void - _js_convert_object(struct mjs* mjs, mjs_val_t* val, void* out, const void* extra) { - const JsObjectMapping* mapping = (JsObjectMapping*)extra; - for(; mapping->name; mapping++) { - mjs_val_t field_val = mjs_get(mjs, *val, mapping->name, ~0); - *(mjs_val_t*)((uint8_t*)out + mapping->offset) = field_val; - } -} -#define JS_ARG_OBJECT(var_name, name) \ - ((_js_arg_decl){ \ - &var_name, \ - mjs_is_object, \ - _js_convert_object, \ - name " object", \ - _js_validate_object, \ - var_name##_mapping}) - -/** - * @brief Validates and converts a JS value with a declarative interface - * - * Example: `int32_t my_value; JS_CONVERT_OR_RETURN(mjs, &mjs_val, JS_ARG_INT32(&my_value), "value source");` - * - * @warning This macro executes `return;` by design in case of a validation failure - */ -#define JS_CONVERT_OR_RETURN(mjs, value, decl, source, ...) \ - if(decl.validator) \ - if(!decl.validator(*value)) \ - JS_ERROR_AND_RETURN( \ - mjs, \ - MJS_BAD_ARGS_ERROR, \ - source ": expected %s", \ - ##__VA_ARGS__, \ - decl.expected_type); \ - if(decl.extended_validator) \ - if(!decl.extended_validator(mjs, *value, decl.extra_data)) \ - JS_ERROR_AND_RETURN( \ - mjs, \ - MJS_BAD_ARGS_ERROR, \ - source ": expected %s", \ - ##__VA_ARGS__, \ - decl.expected_type); \ - decl.converter(mjs, value, decl.out, decl.extra_data); - -//-V:JS_FETCH_ARGS_OR_RETURN:1008 -/** - * @brief Fetches and validates the arguments passed to a JS function - * - * Example: `int32_t my_arg; JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_INT32(&my_arg));` - * - * @warning This macro executes `return;` by design in case of an argument count - * mismatch or a validation failure - */ -#define JS_FETCH_ARGS_OR_RETURN(mjs, arg_operator, ...) \ - _js_arg_decl _js_args[] = {__VA_ARGS__}; \ - int _js_arg_cnt = COUNT_OF(_js_args); \ - mjs_val_t _js_arg_vals[_js_arg_cnt]; \ - if(!(mjs_nargs(mjs) arg_operator _js_arg_cnt)) \ - JS_ERROR_AND_RETURN( \ - mjs, \ - MJS_BAD_ARGS_ERROR, \ - "expected %s%d arguments, got %d", \ - #arg_operator, \ - _js_arg_cnt, \ - mjs_nargs(mjs)); \ - for(int _i = 0; _i < _js_arg_cnt; _i++) { \ - _js_arg_vals[_i] = mjs_arg(mjs, _i); \ - JS_CONVERT_OR_RETURN(mjs, &_js_arg_vals[_i], _js_args[_i], "argument %d", _i); \ - } - /** * @brief Prepends an error, sets the JS return value to `undefined` and returns * from the C function @@ -357,3 +142,7 @@ void js_does_sdk_support(struct mjs* mjs); * @brief `checkSdkFeatures` function */ void js_check_sdk_features(struct mjs* mjs); + +#ifdef __cplusplus +} +#endif diff --git a/applications/system/js_app/js_thread.c b/applications/system/js_app/js_thread.c index 4a6d23011..a41a28d11 100644 --- a/applications/system/js_app/js_thread.c +++ b/applications/system/js_app/js_thread.c @@ -198,18 +198,15 @@ static void js_require(struct mjs* mjs) { } static void js_parse_int(struct mjs* mjs) { - const char* str; - JS_FETCH_ARGS_OR_RETURN(mjs, JS_AT_LEAST, JS_ARG_STR(&str)); + static const JsValueDeclaration js_parse_int_arg_list[] = { + JS_VALUE_SIMPLE(JsValueTypeString), + JS_VALUE_SIMPLE_W_DEFAULT(JsValueTypeInt32, int32_val, 10), + }; + static const JsValueArguments js_parse_int_args = JS_VALUE_ARGS(js_parse_int_arg_list); - int32_t base = 10; - if(mjs_nargs(mjs) >= 2) { - mjs_val_t base_arg = mjs_arg(mjs, 1); - if(!mjs_is_number(base_arg)) { - mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "Base must be a number"); - mjs_return(mjs, MJS_UNDEFINED); - } - base = mjs_get_int(mjs, base_arg); - } + const char* str; + int32_t base; + JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_parse_int_args, &str, &base); int32_t num; if(strint_to_int32(str, NULL, &num, base) != StrintParseNoError) { diff --git a/applications/system/js_app/js_thread_i.h b/applications/system/js_app/js_thread_i.h index a73cbb4bc..5fbdb06d0 100644 --- a/applications/system/js_app/js_thread_i.h +++ b/applications/system/js_app/js_thread_i.h @@ -11,6 +11,10 @@ #include #include +#ifdef __cplusplus +extern "C" { +#endif + #define INST_PROP_NAME "_" typedef enum { @@ -23,3 +27,7 @@ bool js_delay_with_flags(struct mjs* mjs, uint32_t time); void js_flags_set(struct mjs* mjs, uint32_t flags); uint32_t js_flags_wait(struct mjs* mjs, uint32_t flags, uint32_t timeout); + +#ifdef __cplusplus +} +#endif diff --git a/applications/system/js_app/js_value.c b/applications/system/js_app/js_value.c new file mode 100644 index 000000000..6ce1cf37a --- /dev/null +++ b/applications/system/js_app/js_value.c @@ -0,0 +1,291 @@ +#include "js_value.h" +#include + +#ifdef APP_UNIT_TESTS +#define JS_VAL_DEBUG +#endif + +size_t js_value_buffer_size(const JsValueParseDeclaration declaration) { + if(declaration.source == JsValueParseSourceValue) { + const JsValueDeclaration* value_decl = declaration.value_decl; + JsValueType type = value_decl->type & JsValueTypeMask; + + if(type == JsValueTypeString) return 1; + + if(type == JsValueTypeObject) { + size_t total = 0; + for(size_t i = 0; i < value_decl->n_children; i++) + total += js_value_buffer_size( + JS_VALUE_PARSE_SOURCE_VALUE(value_decl->object_fields[i].value)); + return total; + } + + return 0; + + } else { + const JsValueArguments* arg_decl = declaration.argument_decl; + size_t total = 0; + for(size_t i = 0; i < arg_decl->n_children; i++) + total += js_value_buffer_size(JS_VALUE_PARSE_SOURCE_VALUE(&arg_decl->arguments[i])); + return total; + } +} + +static size_t js_value_resulting_c_values_count(const JsValueParseDeclaration declaration) { + if(declaration.source == JsValueParseSourceValue) { + const JsValueDeclaration* value_decl = declaration.value_decl; + JsValueType type = value_decl->type & JsValueTypeMask; + + if(type == JsValueTypeObject) { + size_t total = 0; + for(size_t i = 0; i < value_decl->n_children; i++) + total += js_value_resulting_c_values_count( + JS_VALUE_PARSE_SOURCE_VALUE(value_decl->object_fields[i].value)); + return total; + } + + return 1; + + } else { + const JsValueArguments* arg_decl = declaration.argument_decl; + size_t total = 0; + for(size_t i = 0; i < arg_decl->n_children; i++) + total += js_value_resulting_c_values_count( + JS_VALUE_PARSE_SOURCE_VALUE(&arg_decl->arguments[i])); + return total; + } +} + +#define PREPEND_JS_ERROR_AND_RETURN(mjs, flags, ...) \ + do { \ + if((flags) & JsValueParseFlagReturnOnError) \ + mjs_prepend_errorf((mjs), MJS_BAD_ARGS_ERROR, __VA_ARGS__); \ + return JsValueParseStatusJsError; \ + } while(0) + +#define PREPEND_JS_EXPECTED_ERROR_AND_RETURN(mjs, flags, type) \ + PREPEND_JS_ERROR_AND_RETURN(mjs, flags, "expected %s", type) + +static void js_value_assign_enum_val(void* destination, JsValueType type_w_flags, uint32_t value) { + if(type_w_flags & JsValueTypeEnumSize1) { + *(uint8_t*)destination = value; + } else if(type_w_flags & JsValueTypeEnumSize2) { + *(uint16_t*)destination = value; + } else if(type_w_flags & JsValueTypeEnumSize4) { + *(uint32_t*)destination = value; + } +} + +static bool js_value_is_null_or_undefined(mjs_val_t* val_ptr) { + return mjs_is_null(*val_ptr) || mjs_is_undefined(*val_ptr); +} + +static bool js_value_maybe_assign_default( + const JsValueDeclaration* declaration, + mjs_val_t* val_ptr, + void* destination, + size_t size) { + if((declaration->type & JsValueTypePermitNull) && js_value_is_null_or_undefined(val_ptr)) { + memcpy(destination, &declaration->default_value, size); + return true; + } + return false; +} + +typedef int (*MjsTypecheckFn)(mjs_val_t value); + +static JsValueParseStatus js_value_parse_literal( + struct mjs* mjs, + JsValueParseFlag flags, + mjs_val_t* destination, + mjs_val_t* source, + MjsTypecheckFn typecheck, + const char* type_name) { + if(!typecheck(*source)) PREPEND_JS_EXPECTED_ERROR_AND_RETURN(mjs, flags, type_name); + *destination = *source; + return JsValueParseStatusOk; +} + +static JsValueParseStatus js_value_parse_va( + struct mjs* mjs, + const JsValueParseDeclaration declaration, + JsValueParseFlag flags, + mjs_val_t* source, + mjs_val_t* buffer, + size_t* buffer_index, + va_list* out_pointers) { + if(declaration.source == JsValueParseSourceArguments) { + const JsValueArguments* arg_decl = declaration.argument_decl; + + for(size_t i = 0; i < arg_decl->n_children; i++) { + mjs_val_t arg_val = mjs_arg(mjs, i); + JsValueParseStatus status = js_value_parse_va( + mjs, + JS_VALUE_PARSE_SOURCE_VALUE(&arg_decl->arguments[i]), + flags, + &arg_val, + buffer, + buffer_index, + out_pointers); + if(status != JsValueParseStatusOk) return status; + } + + return JsValueParseStatusOk; + } + + const JsValueDeclaration* value_decl = declaration.value_decl; + JsValueType type_w_flags = value_decl->type; + JsValueType type_noflags = type_w_flags & JsValueTypeMask; + bool is_null_but_allowed = (type_w_flags & JsValueTypePermitNull) && + js_value_is_null_or_undefined(source); + + void* destination = NULL; + if(type_noflags != JsValueTypeObject) destination = va_arg(*out_pointers, void*); + + switch(type_noflags) { + // Literal terms + case JsValueTypeAny: + *(mjs_val_t*)destination = *source; + break; + case JsValueTypeAnyArray: + return js_value_parse_literal(mjs, flags, destination, source, mjs_is_array, "array"); + case JsValueTypeAnyObject: + return js_value_parse_literal(mjs, flags, destination, source, mjs_is_object, "array"); + case JsValueTypeFunction: + return js_value_parse_literal( + mjs, flags, destination, source, mjs_is_function, "function"); + + // Primitive types + case JsValueTypeRawPointer: { + if(js_value_maybe_assign_default(value_decl, source, destination, sizeof(void*))) break; + if(!mjs_is_foreign(*source)) PREPEND_JS_EXPECTED_ERROR_AND_RETURN(mjs, flags, "pointer"); + *(void**)destination = mjs_get_ptr(mjs, *source); + break; + } + case JsValueTypeInt32: { + if(js_value_maybe_assign_default(value_decl, source, destination, sizeof(int32_t))) break; + if(!mjs_is_number(*source)) PREPEND_JS_EXPECTED_ERROR_AND_RETURN(mjs, flags, "number"); + *(int32_t*)destination = mjs_get_int32(mjs, *source); + break; + } + case JsValueTypeDouble: { + if(js_value_maybe_assign_default(value_decl, source, destination, sizeof(double))) break; + if(!mjs_is_number(*source)) PREPEND_JS_EXPECTED_ERROR_AND_RETURN(mjs, flags, "number"); + *(double*)destination = mjs_get_double(mjs, *source); + break; + } + case JsValueTypeBool: { + if(js_value_maybe_assign_default(value_decl, source, destination, sizeof(bool))) break; + if(!mjs_is_boolean(*source)) PREPEND_JS_EXPECTED_ERROR_AND_RETURN(mjs, flags, "bool"); + *(bool*)destination = mjs_get_bool(mjs, *source); + break; + } + case JsValueTypeString: { + if(js_value_maybe_assign_default(value_decl, source, destination, sizeof(const char*))) + break; + if(!mjs_is_string(*source)) PREPEND_JS_EXPECTED_ERROR_AND_RETURN(mjs, flags, "string"); + buffer[*buffer_index] = *source; + *(const char**)destination = mjs_get_string(mjs, &buffer[*buffer_index], NULL); + (*buffer_index)++; + break; + } + + // Types with children + case JsValueTypeEnum: { + if(is_null_but_allowed) { + js_value_assign_enum_val( + destination, type_w_flags, value_decl->default_value.enum_val); + + } else if(mjs_is_string(*source)) { + const char* str = mjs_get_string(mjs, source, NULL); + furi_check(str); + + bool match_found = false; + for(size_t i = 0; i < value_decl->n_children; i++) { + const JsValueEnumVariant* variant = &value_decl->enum_variants[i]; + if(strcmp(str, variant->string_value) == 0) { + js_value_assign_enum_val(destination, type_w_flags, variant->num_value); + match_found = true; + break; + } + } + + if(!match_found) + PREPEND_JS_EXPECTED_ERROR_AND_RETURN(mjs, flags, "one of permitted strings"); + + } else { + PREPEND_JS_EXPECTED_ERROR_AND_RETURN(mjs, flags, "string"); + } + break; + } + + case JsValueTypeObject: { + if(!(is_null_but_allowed || mjs_is_object(*source))) + PREPEND_JS_EXPECTED_ERROR_AND_RETURN(mjs, flags, "object"); + for(size_t i = 0; i < value_decl->n_children; i++) { + const JsValueObjectField* field = &value_decl->object_fields[i]; + mjs_val_t field_val = mjs_get(mjs, *source, field->field_name, ~0); + JsValueParseStatus status = js_value_parse_va( + mjs, + JS_VALUE_PARSE_SOURCE_VALUE(field->value), + flags, + &field_val, + buffer, + buffer_index, + out_pointers); + if(status != JsValueParseStatusOk) + PREPEND_JS_ERROR_AND_RETURN(mjs, flags, "field %s: ", field->field_name); + } + break; + } + + case JsValueTypeMask: + case JsValueTypeEnumSize1: + case JsValueTypeEnumSize2: + case JsValueTypeEnumSize4: + case JsValueTypePermitNull: + furi_crash(); + } + + return JsValueParseStatusOk; +} + +JsValueParseStatus js_value_parse( + struct mjs* mjs, + const JsValueParseDeclaration declaration, + JsValueParseFlag flags, + mjs_val_t* buffer, + size_t buf_size, + mjs_val_t* source, + size_t n_c_vals, + ...) { + furi_check(mjs); + furi_check(buffer); + + if(declaration.source == JsValueParseSourceValue) { + furi_check(source); + furi_check(declaration.value_decl); + } else { + furi_check(source == NULL); + furi_check(declaration.argument_decl); + } + +#ifdef JS_VAL_DEBUG + furi_check(buf_size == js_value_buffer_size(declaration)); + furi_check(n_c_vals == js_value_resulting_c_values_count(declaration)); +#else + UNUSED(js_value_resulting_c_values_count); +#endif + + va_list out_pointers; + va_start(out_pointers, n_c_vals); + + size_t buffer_index = 0; + JsValueParseStatus status = + js_value_parse_va(mjs, declaration, flags, source, buffer, &buffer_index, &out_pointers); + furi_check(buffer_index <= buf_size); + + va_end(out_pointers); + + return status; +} diff --git a/applications/system/js_app/js_value.h b/applications/system/js_app/js_value.h new file mode 100644 index 000000000..765bcb3bb --- /dev/null +++ b/applications/system/js_app/js_value.h @@ -0,0 +1,212 @@ +#pragma once + +#include +#include "js_modules.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + // literal types + JsValueTypeAny, //pin, mode, pull_mode, GpioSpeedVeryHigh); - mjs_return(mjs, MJS_UNDEFINED); + furi_hal_gpio_init(manager_data->pin, mode, pull, GpioSpeedVeryHigh); } /** @@ -146,8 +177,13 @@ static void js_gpio_init(struct mjs* mjs) { * ``` */ static void js_gpio_write(struct mjs* mjs) { + static const JsValueDeclaration js_gpio_write_arg_list[] = { + JS_VALUE_SIMPLE(JsValueTypeBool), + }; + static const JsValueArguments js_gpio_write_args = JS_VALUE_ARGS(js_gpio_write_arg_list); bool level; - JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_BOOL(&level)); + JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_gpio_write_args, &level); + JsGpioPinInst* manager_data = JS_GET_CONTEXT(mjs); furi_hal_gpio_write(manager_data->pin, level); mjs_return(mjs, MJS_UNDEFINED); @@ -261,9 +297,16 @@ static void js_gpio_is_pwm_supported(struct mjs* mjs) { * ``` */ static void js_gpio_pwm_write(struct mjs* mjs) { - JsGpioPinInst* manager_data = JS_GET_CONTEXT(mjs); + static const JsValueDeclaration js_gpio_pwm_write_arg_list[] = { + JS_VALUE_SIMPLE(JsValueTypeInt32), + JS_VALUE_SIMPLE(JsValueTypeInt32), + }; + static const JsValueArguments js_gpio_pwm_write_args = + JS_VALUE_ARGS(js_gpio_pwm_write_arg_list); int32_t frequency, duty; - JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_INT32(&frequency), JS_ARG_INT32(&duty)); + JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_gpio_pwm_write_args, &frequency, &duty); + + JsGpioPinInst* manager_data = JS_GET_CONTEXT(mjs); if(manager_data->pwm_output == FuriHalPwmOutputIdNone) { JS_ERROR_AND_RETURN(mjs, MJS_BAD_ARGS_ERROR, "PWM is not supported on this pin"); } @@ -326,8 +369,13 @@ static void js_gpio_pwm_stop(struct mjs* mjs) { * ``` */ static void js_gpio_get(struct mjs* mjs) { + static const JsValueDeclaration js_gpio_get_arg_list[] = { + JS_VALUE_SIMPLE(JsValueTypeAny), + }; + static const JsValueArguments js_gpio_get_args = JS_VALUE_ARGS(js_gpio_get_arg_list); mjs_val_t name_arg; - JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_ANY(&name_arg)); + JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_gpio_get_args, &name_arg); + const char* name_string = mjs_get_string(mjs, &name_arg, NULL); const GpioPinRecord* pin_record = NULL; diff --git a/applications/system/js_app/modules/js_gui/file_picker.c b/applications/system/js_app/modules/js_gui/file_picker.c index 49cf5e89d..7b36596cd 100644 --- a/applications/system/js_app/modules/js_gui/file_picker.c +++ b/applications/system/js_app/modules/js_gui/file_picker.c @@ -3,8 +3,14 @@ #include static void js_gui_file_picker_pick_file(struct mjs* mjs) { + static const JsValueDeclaration js_picker_arg_list[] = { + JS_VALUE_SIMPLE(JsValueTypeString), + JS_VALUE_SIMPLE(JsValueTypeString), + }; + static const JsValueArguments js_picker_args = JS_VALUE_ARGS(js_picker_arg_list); + const char *base_path, *extension; - JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_STR(&base_path), JS_ARG_STR(&extension)); + JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_picker_args, &base_path, &extension); DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); const DialogsFileBrowserOptions browser_options = { diff --git a/applications/system/js_app/modules/js_gui/icon.c b/applications/system/js_app/modules/js_gui/icon.c index 3d8a67a8b..4fc6da2e0 100644 --- a/applications/system/js_app/modules/js_gui/icon.c +++ b/applications/system/js_app/modules/js_gui/icon.c @@ -39,9 +39,14 @@ typedef struct { FxbmIconWrapperList_t fxbm_list; } JsGuiIconInst; +static const JsValueDeclaration js_icon_get_arg_list[] = { + JS_VALUE_SIMPLE(JsValueTypeString), +}; +static const JsValueArguments js_icon_get_args = JS_VALUE_ARGS(js_icon_get_arg_list); + static void js_gui_icon_get_builtin(struct mjs* mjs) { const char* icon_name; - JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_STR(&icon_name)); + JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_icon_get_args, &icon_name); for(size_t i = 0; i < COUNT_OF(builtin_icons); i++) { if(strcmp(icon_name, builtin_icons[i].name) == 0) { @@ -55,7 +60,7 @@ static void js_gui_icon_get_builtin(struct mjs* mjs) { static void js_gui_icon_load_fxbm(struct mjs* mjs) { const char* fxbm_path; - JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_STR(&fxbm_path)); + JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_icon_get_args, &fxbm_path); Storage* storage = furi_record_open(RECORD_STORAGE); File* file = storage_file_alloc(storage); diff --git a/applications/system/js_app/modules/js_gui/js_gui.c b/applications/system/js_app/modules/js_gui/js_gui.c index e505681df..c20d980aa 100644 --- a/applications/system/js_app/modules/js_gui/js_gui.c +++ b/applications/system/js_app/modules/js_gui/js_gui.c @@ -68,8 +68,14 @@ static bool js_gui_vd_nav_callback(void* context) { * @brief `viewDispatcher.sendCustom` */ static void js_gui_vd_send_custom(struct mjs* mjs) { + static const JsValueDeclaration js_gui_vd_send_custom_arg_list[] = { + JS_VALUE_SIMPLE(JsValueTypeInt32), + }; + static const JsValueArguments js_gui_vd_send_custom_args = + JS_VALUE_ARGS(js_gui_vd_send_custom_arg_list); + int32_t event; - JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_INT32(&event)); + JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_gui_vd_send_custom_args, &event); JsGui* module = JS_GET_CONTEXT(mjs); view_dispatcher_send_custom_event(module->dispatcher, (uint32_t)event); @@ -79,15 +85,25 @@ static void js_gui_vd_send_custom(struct mjs* mjs) { * @brief `viewDispatcher.sendTo` */ static void js_gui_vd_send_to(struct mjs* mjs) { - enum { - SendDirToFront, - SendDirToBack, - } send_direction; - JS_ENUM_MAP(send_direction, {"front", SendDirToFront}, {"back", SendDirToBack}); - JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_ENUM(send_direction, "SendDirection")); + typedef enum { + JsSendDirToFront, + JsSendDirToBack, + } JsSendDir; + static const JsValueEnumVariant js_send_dir_variants[] = { + {"front", JsSendDirToFront}, + {"back", JsSendDirToBack}, + }; + static const JsValueDeclaration js_gui_vd_send_to_arg_list[] = { + JS_VALUE_ENUM(JsSendDir, js_send_dir_variants), + }; + static const JsValueArguments js_gui_vd_send_to_args = + JS_VALUE_ARGS(js_gui_vd_send_to_arg_list); + + JsSendDir send_direction; + JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_gui_vd_send_to_args, &send_direction); JsGui* module = JS_GET_CONTEXT(mjs); - if(send_direction == SendDirToBack) { + if(send_direction == JsSendDirToBack) { view_dispatcher_send_to_back(module->dispatcher); } else { view_dispatcher_send_to_front(module->dispatcher); @@ -98,8 +114,15 @@ static void js_gui_vd_send_to(struct mjs* mjs) { * @brief `viewDispatcher.switchTo` */ static void js_gui_vd_switch_to(struct mjs* mjs) { + static const JsValueDeclaration js_gui_vd_switch_to_arg_list[] = { + JS_VALUE_SIMPLE(JsValueTypeAny), + }; + static const JsValueArguments js_gui_vd_switch_to_args = + JS_VALUE_ARGS(js_gui_vd_switch_to_arg_list); + mjs_val_t view; - JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_OBJ(&view)); + JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_gui_vd_switch_to_args, &view); + JsGuiViewData* view_data = JS_GET_INST(mjs, view); mjs_val_t vd_obj = mjs_get_this(mjs); JsGui* module = JS_GET_INST(mjs, vd_obj); @@ -267,9 +290,16 @@ static bool * @brief `View.set` */ static void js_gui_view_set(struct mjs* mjs) { + static const JsValueDeclaration js_gui_view_set_arg_list[] = { + JS_VALUE_SIMPLE(JsValueTypeString), + JS_VALUE_SIMPLE(JsValueTypeAny), + }; + static const JsValueArguments js_gui_view_set_args = JS_VALUE_ARGS(js_gui_view_set_arg_list); + const char* name; mjs_val_t value; - JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_STR(&name), JS_ARG_ANY(&value)); + JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_gui_view_set_args, &name, &value); + JsGuiViewData* data = JS_GET_CONTEXT(mjs); bool success = js_gui_view_assign(mjs, name, value, data); UNUSED(success); @@ -280,12 +310,19 @@ static void js_gui_view_set(struct mjs* mjs) { * @brief `View.addChild` */ static void js_gui_view_add_child(struct mjs* mjs) { + static const JsValueDeclaration js_gui_view_add_child_arg_list[] = { + JS_VALUE_SIMPLE(JsValueTypeAny), + }; + static const JsValueArguments js_gui_view_add_child_args = + JS_VALUE_ARGS(js_gui_view_add_child_arg_list); + + mjs_val_t child; + JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_gui_view_add_child_args, &child); + JsGuiViewData* data = JS_GET_CONTEXT(mjs); if(!data->descriptor->add_child || !data->descriptor->reset_children) JS_ERROR_AND_RETURN(mjs, MJS_BAD_ARGS_ERROR, "this View can't have children"); - mjs_val_t child; - JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_ANY(&child)); bool success = data->descriptor->add_child(mjs, data->specific_view, data->custom_data, child); UNUSED(success); mjs_return(mjs, MJS_UNDEFINED); @@ -307,12 +344,19 @@ static void js_gui_view_reset_children(struct mjs* mjs) { * @brief `View.setChildren` */ static void js_gui_view_set_children(struct mjs* mjs) { + static const JsValueDeclaration js_gui_view_set_children_arg_list[] = { + JS_VALUE_SIMPLE(JsValueTypeAnyArray), + }; + static const JsValueArguments js_gui_view_set_children_args = + JS_VALUE_ARGS(js_gui_view_set_children_arg_list); + + mjs_val_t children; + JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_gui_view_set_children_args, &children); + JsGuiViewData* data = JS_GET_CONTEXT(mjs); if(!data->descriptor->add_child || !data->descriptor->reset_children) JS_ERROR_AND_RETURN(mjs, MJS_BAD_ARGS_ERROR, "this View can't have children"); - mjs_val_t children; - JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_ARR(&children)); js_gui_view_internal_set_children(mjs, children, data); } @@ -365,7 +409,6 @@ static mjs_val_t js_gui_make_view(struct mjs* mjs, const JsViewDescriptor* descr * @brief `ViewFactory.make` */ static void js_gui_vf_make(struct mjs* mjs) { - JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY); // 0 args const JsViewDescriptor* descriptor = JS_GET_CONTEXT(mjs); mjs_return(mjs, js_gui_make_view(mjs, descriptor)); } @@ -374,8 +417,15 @@ static void js_gui_vf_make(struct mjs* mjs) { * @brief `ViewFactory.makeWith` */ static void js_gui_vf_make_with(struct mjs* mjs) { - mjs_val_t props; - JS_FETCH_ARGS_OR_RETURN(mjs, JS_AT_LEAST, JS_ARG_OBJ(&props)); + static const JsValueDeclaration js_gui_vf_make_with_arg_list[] = { + JS_VALUE_SIMPLE(JsValueTypeAnyObject), + JS_VALUE_SIMPLE(JsValueTypeAny), + }; + static const JsValueArguments js_gui_vf_make_with_args = + JS_VALUE_ARGS(js_gui_vf_make_with_arg_list); + + mjs_val_t props, children; + JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_gui_vf_make_with_args, &props, &children); const JsViewDescriptor* descriptor = JS_GET_CONTEXT(mjs); // make the object like normal @@ -396,14 +446,10 @@ static void js_gui_vf_make_with(struct mjs* mjs) { } // assign children - if(mjs_nargs(mjs) >= 2) { + if(mjs_is_array(children)) { if(!data->descriptor->add_child || !data->descriptor->reset_children) JS_ERROR_AND_RETURN(mjs, MJS_BAD_ARGS_ERROR, "this View can't have children"); - mjs_val_t children = mjs_arg(mjs, 1); - if(!mjs_is_array(children)) - JS_ERROR_AND_RETURN(mjs, MJS_BAD_ARGS_ERROR, "argument 1: expected array"); - if(!js_gui_view_internal_set_children(mjs, children, data)) return; } diff --git a/applications/system/js_app/modules/js_serial.c b/applications/system/js_app/modules/js_serial.c index 20b18a4f1..d903939ce 100644 --- a/applications/system/js_app/modules/js_serial.c +++ b/applications/system/js_app/modules/js_serial.c @@ -35,60 +35,62 @@ static void } static void js_serial_setup(struct mjs* mjs) { + static const JsValueEnumVariant js_serial_id_variants[] = { + {"lpuart", FuriHalSerialIdLpuart}, + {"usart", FuriHalSerialIdUsart}, + }; + + static const JsValueEnumVariant js_serial_data_bit_variants[] = { + {"6", FuriHalSerialDataBits6}, + {"7", FuriHalSerialDataBits7}, + {"8", FuriHalSerialDataBits8}, + {"9", FuriHalSerialDataBits9}, + }; + static const JsValueDeclaration js_serial_data_bits = JS_VALUE_ENUM_W_DEFAULT( + FuriHalSerialDataBits, js_serial_data_bit_variants, FuriHalSerialDataBits8); + + static const JsValueEnumVariant js_serial_parity_variants[] = { + {"none", FuriHalSerialParityNone}, + {"even", FuriHalSerialParityEven}, + {"odd", FuriHalSerialParityOdd}, + }; + static const JsValueDeclaration js_serial_parity = JS_VALUE_ENUM_W_DEFAULT( + FuriHalSerialParity, js_serial_parity_variants, FuriHalSerialParityNone); + + static const JsValueEnumVariant js_serial_stop_bit_variants[] = { + {"0.5", FuriHalSerialStopBits0_5}, + {"1", FuriHalSerialStopBits1}, + {"1.5", FuriHalSerialStopBits1_5}, + {"2", FuriHalSerialStopBits2}, + }; + static const JsValueDeclaration js_serial_stop_bits = JS_VALUE_ENUM_W_DEFAULT( + FuriHalSerialStopBits, js_serial_stop_bit_variants, FuriHalSerialStopBits1); + + static const JsValueObjectField js_serial_framing_fields[] = { + {"dataBits", &js_serial_data_bits}, + {"parity", &js_serial_parity}, + {"stopBits", &js_serial_stop_bits}, + }; + + static const JsValueDeclaration js_serial_setup_arg_list[] = { + JS_VALUE_ENUM(FuriHalSerialId, js_serial_id_variants), + JS_VALUE_SIMPLE(JsValueTypeInt32), + JS_VALUE_OBJECT_W_DEFAULTS(js_serial_framing_fields), + }; + static const JsValueArguments js_serial_setup_args = JS_VALUE_ARGS(js_serial_setup_arg_list); + FuriHalSerialId serial_id; int32_t baudrate; - JS_ENUM_MAP(serial_id, {"lpuart", FuriHalSerialIdLpuart}, {"usart", FuriHalSerialIdUsart}); - JS_FETCH_ARGS_OR_RETURN( - mjs, JS_AT_LEAST, JS_ARG_ENUM(serial_id, "SerialId"), JS_ARG_INT32(&baudrate)); - FuriHalSerialDataBits data_bits = FuriHalSerialDataBits8; FuriHalSerialParity parity = FuriHalSerialParityNone; FuriHalSerialStopBits stop_bits = FuriHalSerialStopBits1; - if(mjs_nargs(mjs) > 2) { - struct framing { - mjs_val_t data_bits; - mjs_val_t parity; - mjs_val_t stop_bits; - } framing; - JS_OBJ_MAP( - framing, - {"dataBits", offsetof(struct framing, data_bits)}, - {"parity", offsetof(struct framing, parity)}, - {"stopBits", offsetof(struct framing, stop_bits)}); - JS_ENUM_MAP( - data_bits, - {"6", FuriHalSerialDataBits6}, - {"7", FuriHalSerialDataBits7}, - {"8", FuriHalSerialDataBits8}, - {"9", FuriHalSerialDataBits9}); - JS_ENUM_MAP( - parity, - {"none", FuriHalSerialParityNone}, - {"even", FuriHalSerialParityEven}, - {"odd", FuriHalSerialParityOdd}); - JS_ENUM_MAP( - stop_bits, - {"0.5", FuriHalSerialStopBits0_5}, - {"1", FuriHalSerialStopBits1}, - {"1.5", FuriHalSerialStopBits1_5}, - {"2", FuriHalSerialStopBits2}); - mjs_val_t framing_obj = mjs_arg(mjs, 2); - JS_CONVERT_OR_RETURN(mjs, &framing_obj, JS_ARG_OBJECT(framing, "Framing"), "argument 2"); - JS_CONVERT_OR_RETURN( - mjs, &framing.data_bits, JS_ARG_ENUM(data_bits, "DataBits"), "argument 2: dataBits"); - JS_CONVERT_OR_RETURN( - mjs, &framing.parity, JS_ARG_ENUM(parity, "Parity"), "argument 2: parity"); - JS_CONVERT_OR_RETURN( - mjs, &framing.stop_bits, JS_ARG_ENUM(stop_bits, "StopBits"), "argument 2: stopBits"); - } + JS_VALUE_PARSE_ARGS_OR_RETURN( + mjs, &js_serial_setup_args, &serial_id, &baudrate, &data_bits, &parity, &stop_bits); JsSerialInst* serial = JS_GET_CONTEXT(mjs); - if(serial->setup_done) { - mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, "Serial is already configured"); - mjs_return(mjs, MJS_UNDEFINED); - return; - } + if(serial->setup_done) + JS_ERROR_AND_RETURN(mjs, MJS_INTERNAL_ERROR, "Serial is already configured"); expansion_disable(furi_record_open(RECORD_EXPANSION)); furi_record_close(RECORD_EXPANSION); @@ -123,28 +125,20 @@ static void js_serial_deinit(JsSerialInst* js_serial) { } static void js_serial_end(struct mjs* mjs) { - mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0); - JsSerialInst* serial = mjs_get_ptr(mjs, obj_inst); + JsSerialInst* serial = JS_GET_CONTEXT(mjs); furi_assert(serial); - if(!serial->setup_done) { - mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, "Serial is not configured"); - mjs_return(mjs, MJS_UNDEFINED); - return; - } + if(!serial->setup_done) + JS_ERROR_AND_RETURN(mjs, MJS_INTERNAL_ERROR, "Serial is not configured"); js_serial_deinit(serial); } static void js_serial_write(struct mjs* mjs) { - mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0); - JsSerialInst* serial = mjs_get_ptr(mjs, obj_inst); + JsSerialInst* serial = JS_GET_CONTEXT(mjs); furi_assert(serial); - if(!serial->setup_done) { - mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, "Serial is not configured"); - mjs_return(mjs, MJS_UNDEFINED); - return; - } + if(!serial->setup_done) + JS_ERROR_AND_RETURN(mjs, MJS_INTERNAL_ERROR, "Serial is not configured"); bool args_correct = true; @@ -228,43 +222,20 @@ static size_t js_serial_receive(JsSerialInst* serial, char* buf, size_t len, uin return bytes_read; } +static const JsValueDeclaration js_serial_read_arg_list[] = { + JS_VALUE_SIMPLE(JsValueTypeInt32), + JS_VALUE_SIMPLE_W_DEFAULT(JsValueTypeInt32, int32_val, INT32_MAX), +}; +static const JsValueArguments js_serial_read_args = JS_VALUE_ARGS(js_serial_read_arg_list); + static void js_serial_read(struct mjs* mjs) { - mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0); - JsSerialInst* serial = mjs_get_ptr(mjs, obj_inst); + JsSerialInst* serial = JS_GET_CONTEXT(mjs); furi_assert(serial); - if(!serial->setup_done) { - mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, "Serial is not configured"); - mjs_return(mjs, MJS_UNDEFINED); - return; - } + if(!serial->setup_done) + JS_ERROR_AND_RETURN(mjs, MJS_INTERNAL_ERROR, "Serial is not configured"); - size_t read_len = 0; - uint32_t timeout = FuriWaitForever; - - do { - size_t num_args = mjs_nargs(mjs); - if(num_args == 1) { - mjs_val_t arg = mjs_arg(mjs, 0); - if(!mjs_is_number(arg)) { - break; - } - read_len = mjs_get_int32(mjs, arg); - } else if(num_args == 2) { - mjs_val_t len_arg = mjs_arg(mjs, 0); - mjs_val_t timeout_arg = mjs_arg(mjs, 1); - if((!mjs_is_number(len_arg)) || (!mjs_is_number(timeout_arg))) { - break; - } - read_len = mjs_get_int32(mjs, len_arg); - timeout = mjs_get_int32(mjs, timeout_arg); - } - } while(0); - - if(read_len == 0) { - mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, ""); - mjs_return(mjs, MJS_UNDEFINED); - return; - } + int32_t read_len, timeout; + JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_serial_read_args, &read_len, &timeout); char* read_buf = malloc(read_len); size_t bytes_read = js_serial_receive(serial, read_buf, read_len, timeout); @@ -278,37 +249,19 @@ static void js_serial_read(struct mjs* mjs) { } static void js_serial_readln(struct mjs* mjs) { - mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0); - JsSerialInst* serial = mjs_get_ptr(mjs, obj_inst); + JsSerialInst* serial = JS_GET_CONTEXT(mjs); furi_assert(serial); - if(!serial->setup_done) { - mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, "Serial is not configured"); - mjs_return(mjs, MJS_UNDEFINED); - return; - } + if(!serial->setup_done) + JS_ERROR_AND_RETURN(mjs, MJS_INTERNAL_ERROR, "Serial is not configured"); - bool args_correct = false; - uint32_t timeout = FuriWaitForever; + static const JsValueDeclaration js_serial_readln_arg_list[] = { + JS_VALUE_SIMPLE(JsValueTypeInt32), + }; + static const JsValueArguments js_serial_readln_args = JS_VALUE_ARGS(js_serial_readln_arg_list); - do { - size_t num_args = mjs_nargs(mjs); - if(num_args > 1) { - break; - } else if(num_args == 1) { - mjs_val_t arg = mjs_arg(mjs, 0); - if(!mjs_is_number(arg)) { - break; - } - timeout = mjs_get_int32(mjs, arg); - } - args_correct = true; - } while(0); + int32_t timeout; + JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_serial_readln_args, &timeout); - if(!args_correct) { - mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, ""); - mjs_return(mjs, MJS_UNDEFINED); - return; - } FuriString* rx_buf = furi_string_alloc(); size_t bytes_read = 0; char read_char = 0; @@ -335,42 +288,13 @@ static void js_serial_readln(struct mjs* mjs) { } static void js_serial_read_bytes(struct mjs* mjs) { - mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0); - JsSerialInst* serial = mjs_get_ptr(mjs, obj_inst); + JsSerialInst* serial = JS_GET_CONTEXT(mjs); furi_assert(serial); - if(!serial->setup_done) { - mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, "Serial is not configured"); - mjs_return(mjs, MJS_UNDEFINED); - return; - } + if(!serial->setup_done) + JS_ERROR_AND_RETURN(mjs, MJS_INTERNAL_ERROR, "Serial is not configured"); - size_t read_len = 0; - uint32_t timeout = FuriWaitForever; - - do { - size_t num_args = mjs_nargs(mjs); - if(num_args == 1) { - mjs_val_t arg = mjs_arg(mjs, 0); - if(!mjs_is_number(arg)) { - break; - } - read_len = mjs_get_int32(mjs, arg); - } else if(num_args == 2) { - mjs_val_t len_arg = mjs_arg(mjs, 0); - mjs_val_t timeout_arg = mjs_arg(mjs, 1); - if((!mjs_is_number(len_arg)) || (!mjs_is_number(timeout_arg))) { - break; - } - read_len = mjs_get_int32(mjs, len_arg); - timeout = mjs_get_int32(mjs, timeout_arg); - } - } while(0); - - if(read_len == 0) { - mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, ""); - mjs_return(mjs, MJS_UNDEFINED); - return; - } + int32_t read_len, timeout; + JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_serial_read_args, &read_len, &timeout); char* read_buf = malloc(read_len); size_t bytes_read = js_serial_receive(serial, read_buf, read_len, timeout); @@ -399,27 +323,19 @@ static char* js_serial_receive_any(JsSerialInst* serial, size_t* len, uint32_t t } static void js_serial_read_any(struct mjs* mjs) { - mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0); - JsSerialInst* serial = mjs_get_ptr(mjs, obj_inst); + JsSerialInst* serial = JS_GET_CONTEXT(mjs); furi_assert(serial); - if(!serial->setup_done) { - mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, "Serial is not configured"); - mjs_return(mjs, MJS_UNDEFINED); - return; - } + if(!serial->setup_done) + JS_ERROR_AND_RETURN(mjs, MJS_INTERNAL_ERROR, "Serial is not configured"); - uint32_t timeout = FuriWaitForever; + static const JsValueDeclaration js_serial_read_any_arg_list[] = { + JS_VALUE_SIMPLE_W_DEFAULT(JsValueTypeInt32, int32_val, INT32_MAX), + }; + static const JsValueArguments js_serial_read_any_args = + JS_VALUE_ARGS(js_serial_read_any_arg_list); - do { - size_t num_args = mjs_nargs(mjs); - if(num_args == 1) { - mjs_val_t timeout_arg = mjs_arg(mjs, 0); - if(!mjs_is_number(timeout_arg)) { - break; - } - timeout = mjs_get_int32(mjs, timeout_arg); - } - } while(0); + int32_t timeout; + JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_serial_read_any_args, &timeout); size_t bytes_read = 0; char* read_buf = js_serial_receive_any(serial, &bytes_read, timeout); @@ -663,16 +579,19 @@ static void* js_serial_create(struct mjs* mjs, mjs_val_t* object, JsModules* mod UNUSED(modules); JsSerialInst* js_serial = malloc(sizeof(JsSerialInst)); js_serial->mjs = mjs; + mjs_val_t serial_obj = mjs_mk_object(mjs); - mjs_set(mjs, serial_obj, INST_PROP_NAME, ~0, mjs_mk_foreign(mjs, js_serial)); - mjs_set(mjs, serial_obj, "setup", ~0, MJS_MK_FN(js_serial_setup)); - mjs_set(mjs, serial_obj, "end", ~0, MJS_MK_FN(js_serial_end)); - mjs_set(mjs, serial_obj, "write", ~0, MJS_MK_FN(js_serial_write)); - mjs_set(mjs, serial_obj, "read", ~0, MJS_MK_FN(js_serial_read)); - mjs_set(mjs, serial_obj, "readln", ~0, MJS_MK_FN(js_serial_readln)); - mjs_set(mjs, serial_obj, "readBytes", ~0, MJS_MK_FN(js_serial_read_bytes)); - mjs_set(mjs, serial_obj, "readAny", ~0, MJS_MK_FN(js_serial_read_any)); - mjs_set(mjs, serial_obj, "expect", ~0, MJS_MK_FN(js_serial_expect)); + JS_ASSIGN_MULTI(mjs, serial_obj) { + JS_FIELD(INST_PROP_NAME, mjs_mk_foreign(mjs, js_serial)); + JS_FIELD("setup", MJS_MK_FN(js_serial_setup)); + JS_FIELD("end", MJS_MK_FN(js_serial_end)); + JS_FIELD("write", MJS_MK_FN(js_serial_write)); + JS_FIELD("read", MJS_MK_FN(js_serial_read)); + JS_FIELD("readln", MJS_MK_FN(js_serial_readln)); + JS_FIELD("readBytes", MJS_MK_FN(js_serial_read_bytes)); + JS_FIELD("readAny", MJS_MK_FN(js_serial_read_any)); + JS_FIELD("expect", MJS_MK_FN(js_serial_expect)); + } *object = serial_obj; return js_serial; diff --git a/applications/system/js_app/modules/js_storage.c b/applications/system/js_app/modules/js_storage.c index 1d4053a5f..66d002f33 100644 --- a/applications/system/js_app/modules/js_storage.c +++ b/applications/system/js_app/modules/js_storage.c @@ -1,42 +1,79 @@ #include "../js_modules.h" // IWYU pragma: keep #include -// ---=== file ops ===--- +// ========================== +// Common argument signatures +// ========================== + +static const JsValueDeclaration js_storage_1_int_arg_list[] = { + JS_VALUE_SIMPLE(JsValueTypeInt32), +}; +static const JsValueArguments js_storage_1_int_args = JS_VALUE_ARGS(js_storage_1_int_arg_list); + +static const JsValueDeclaration js_storage_1_str_arg_list[] = { + JS_VALUE_SIMPLE(JsValueTypeString), +}; +static const JsValueArguments js_storage_1_str_args = JS_VALUE_ARGS(js_storage_1_str_arg_list); + +static const JsValueDeclaration js_storage_2_str_arg_list[] = { + JS_VALUE_SIMPLE(JsValueTypeString), + JS_VALUE_SIMPLE(JsValueTypeString), +}; +static const JsValueArguments js_storage_2_str_args = JS_VALUE_ARGS(js_storage_2_str_arg_list); + +// ====================== +// File object operations +// ====================== static void js_storage_file_close(struct mjs* mjs) { - JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY); // 0 args File* file = JS_GET_CONTEXT(mjs); mjs_return(mjs, mjs_mk_boolean(mjs, storage_file_close(file))); } static void js_storage_file_is_open(struct mjs* mjs) { - JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY); // 0 args File* file = JS_GET_CONTEXT(mjs); mjs_return(mjs, mjs_mk_boolean(mjs, storage_file_is_open(file))); } static void js_storage_file_read(struct mjs* mjs) { - enum { - ReadModeAscii, - ReadModeBinary, - } read_mode; - JS_ENUM_MAP(read_mode, {"ascii", ReadModeAscii}, {"binary", ReadModeBinary}); + typedef enum { + JsStorageReadModeAscii, + JsStorageReadModeBinary, + } JsStorageReadMode; + static const JsValueEnumVariant js_storage_read_mode_variants[] = { + {"ascii", JsStorageReadModeAscii}, + {"binary", JsStorageReadModeBinary}, + }; + static const JsValueDeclaration js_storage_read_arg_list[] = { + JS_VALUE_ENUM(JsStorageReadMode, js_storage_read_mode_variants), + JS_VALUE_SIMPLE(JsValueTypeInt32), + }; + static const JsValueArguments js_storage_read_args = JS_VALUE_ARGS(js_storage_read_arg_list); + + JsStorageReadMode read_mode; int32_t length; - JS_FETCH_ARGS_OR_RETURN( - mjs, JS_EXACTLY, JS_ARG_ENUM(read_mode, "ReadMode"), JS_ARG_INT32(&length)); + JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_storage_read_args, &read_mode, &length); + File* file = JS_GET_CONTEXT(mjs); char buffer[length]; size_t actually_read = storage_file_read(file, buffer, length); - if(read_mode == ReadModeAscii) { + if(read_mode == JsStorageReadModeAscii) { mjs_return(mjs, mjs_mk_string(mjs, buffer, actually_read, true)); - } else if(read_mode == ReadModeBinary) { + } else if(read_mode == JsStorageReadModeBinary) { mjs_return(mjs, mjs_mk_array_buf(mjs, buffer, actually_read)); } } static void js_storage_file_write(struct mjs* mjs) { + static const JsValueDeclaration js_storage_file_write_arg_list[] = { + JS_VALUE_SIMPLE(JsValueTypeAny), + }; + static const JsValueArguments js_storage_file_write_args = + JS_VALUE_ARGS(js_storage_file_write_arg_list); + mjs_val_t data; - JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_ANY(&data)); + JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_storage_file_write_args, &data); + const void* buf; size_t len; if(mjs_is_string(data)) { @@ -52,52 +89,58 @@ static void js_storage_file_write(struct mjs* mjs) { static void js_storage_file_seek_relative(struct mjs* mjs) { int32_t offset; - JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_INT32(&offset)); + JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_storage_1_int_args, &offset); File* file = JS_GET_CONTEXT(mjs); mjs_return(mjs, mjs_mk_boolean(mjs, storage_file_seek(file, offset, false))); } static void js_storage_file_seek_absolute(struct mjs* mjs) { int32_t offset; - JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_INT32(&offset)); + JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_storage_1_int_args, &offset); File* file = JS_GET_CONTEXT(mjs); mjs_return(mjs, mjs_mk_boolean(mjs, storage_file_seek(file, offset, true))); } static void js_storage_file_tell(struct mjs* mjs) { - JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY); // 0 args File* file = JS_GET_CONTEXT(mjs); mjs_return(mjs, mjs_mk_number(mjs, storage_file_tell(file))); } static void js_storage_file_truncate(struct mjs* mjs) { - JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY); // 0 args File* file = JS_GET_CONTEXT(mjs); mjs_return(mjs, mjs_mk_boolean(mjs, storage_file_truncate(file))); } static void js_storage_file_size(struct mjs* mjs) { - JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY); // 0 args File* file = JS_GET_CONTEXT(mjs); mjs_return(mjs, mjs_mk_number(mjs, storage_file_size(file))); } static void js_storage_file_eof(struct mjs* mjs) { - JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY); // 0 args File* file = JS_GET_CONTEXT(mjs); mjs_return(mjs, mjs_mk_boolean(mjs, storage_file_eof(file))); } static void js_storage_file_copy_to(struct mjs* mjs) { - File* source = JS_GET_CONTEXT(mjs); + static const JsValueDeclaration js_storage_file_write_arg_list[] = { + JS_VALUE_SIMPLE(JsValueTypeAny), + JS_VALUE_SIMPLE(JsValueTypeInt32), + }; + static const JsValueArguments js_storage_file_write_args = + JS_VALUE_ARGS(js_storage_file_write_arg_list); + mjs_val_t dest_obj; int32_t bytes; - JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_OBJ(&dest_obj), JS_ARG_INT32(&bytes)); + JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_storage_file_write_args, &dest_obj, &bytes); + + File* source = JS_GET_CONTEXT(mjs); File* destination = JS_GET_INST(mjs, dest_obj); mjs_return(mjs, mjs_mk_boolean(mjs, storage_file_copy_to_file(source, destination, bytes))); } -// ---=== top-level file ops ===--- +// ========================= +// Top-level file operations +// ========================= // common destructor for file and dir objects static void js_storage_file_destructor(struct mjs* mjs, mjs_val_t obj) { @@ -106,23 +149,33 @@ static void js_storage_file_destructor(struct mjs* mjs, mjs_val_t obj) { } static void js_storage_open_file(struct mjs* mjs) { - const char* path; - FS_AccessMode access_mode; - FS_OpenMode open_mode; - JS_ENUM_MAP(access_mode, {"r", FSAM_READ}, {"w", FSAM_WRITE}, {"rw", FSAM_READ_WRITE}); - JS_ENUM_MAP( - open_mode, + static const JsValueEnumVariant js_storage_fsam_variants[] = { + {"r", FSAM_READ}, + {"w", FSAM_WRITE}, + {"rw", FSAM_READ_WRITE}, + }; + + static const JsValueEnumVariant js_storage_fsom_variants[] = { {"open_existing", FSOM_OPEN_EXISTING}, {"open_always", FSOM_OPEN_ALWAYS}, {"open_append", FSOM_OPEN_APPEND}, {"create_new", FSOM_CREATE_NEW}, - {"create_always", FSOM_CREATE_ALWAYS}); - JS_FETCH_ARGS_OR_RETURN( - mjs, - JS_EXACTLY, - JS_ARG_STR(&path), - JS_ARG_ENUM(access_mode, "AccessMode"), - JS_ARG_ENUM(open_mode, "OpenMode")); + {"create_always", FSOM_CREATE_ALWAYS}, + }; + + static const JsValueDeclaration js_storage_open_file_arg_list[] = { + JS_VALUE_SIMPLE(JsValueTypeString), + JS_VALUE_ENUM(FS_AccessMode, js_storage_fsam_variants), + JS_VALUE_ENUM(FS_OpenMode, js_storage_fsom_variants), + }; + static const JsValueArguments js_storage_open_file_args = + JS_VALUE_ARGS(js_storage_open_file_arg_list); + + const char* path; + FS_AccessMode access_mode; + FS_OpenMode open_mode; + JS_VALUE_PARSE_ARGS_OR_RETURN( + mjs, &js_storage_open_file_args, &path, &access_mode, &open_mode); Storage* storage = JS_GET_CONTEXT(mjs); File* file = storage_file_alloc(storage); @@ -152,16 +205,18 @@ static void js_storage_open_file(struct mjs* mjs) { static void js_storage_file_exists(struct mjs* mjs) { const char* path; - JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_STR(&path)); + JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_storage_1_str_args, &path); Storage* storage = JS_GET_CONTEXT(mjs); mjs_return(mjs, mjs_mk_boolean(mjs, storage_file_exists(storage, path))); } -// ---=== dir ops ===--- +// ==================== +// Directory operations +// ==================== static void js_storage_read_directory(struct mjs* mjs) { const char* path; - JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_STR(&path)); + JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_storage_1_str_args, &path); Storage* storage = JS_GET_CONTEXT(mjs); File* dir = storage_file_alloc(storage); @@ -200,30 +255,32 @@ static void js_storage_read_directory(struct mjs* mjs) { static void js_storage_directory_exists(struct mjs* mjs) { const char* path; - JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_STR(&path)); + JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_storage_1_str_args, &path); Storage* storage = JS_GET_CONTEXT(mjs); mjs_return(mjs, mjs_mk_boolean(mjs, storage_dir_exists(storage, path))); } static void js_storage_make_directory(struct mjs* mjs) { const char* path; - JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_STR(&path)); + JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_storage_1_str_args, &path); Storage* storage = JS_GET_CONTEXT(mjs); mjs_return(mjs, mjs_mk_boolean(mjs, storage_simply_mkdir(storage, path))); } -// ---=== common ops ===--- +// ================= +// Common operations +// ================= static void js_storage_file_or_dir_exists(struct mjs* mjs) { const char* path; - JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_STR(&path)); + JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_storage_1_str_args, &path); Storage* storage = JS_GET_CONTEXT(mjs); mjs_return(mjs, mjs_mk_boolean(mjs, storage_common_exists(storage, path))); } static void js_storage_stat(struct mjs* mjs) { const char* path; - JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_STR(&path)); + JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_storage_1_str_args, &path); Storage* storage = JS_GET_CONTEXT(mjs); FileInfo file_info; uint32_t timestamp; @@ -244,21 +301,21 @@ static void js_storage_stat(struct mjs* mjs) { static void js_storage_remove(struct mjs* mjs) { const char* path; - JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_STR(&path)); + JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_storage_1_str_args, &path); Storage* storage = JS_GET_CONTEXT(mjs); mjs_return(mjs, mjs_mk_boolean(mjs, storage_simply_remove(storage, path))); } static void js_storage_rmrf(struct mjs* mjs) { const char* path; - JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_STR(&path)); + JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_storage_1_str_args, &path); Storage* storage = JS_GET_CONTEXT(mjs); mjs_return(mjs, mjs_mk_boolean(mjs, storage_simply_remove_recursive(storage, path))); } static void js_storage_rename(struct mjs* mjs) { const char *old, *new; - JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_STR(&old), JS_ARG_STR(&new)); + JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_storage_2_str_args, &old, &new); Storage* storage = JS_GET_CONTEXT(mjs); FS_Error status = storage_common_rename(storage, old, new); mjs_return(mjs, mjs_mk_boolean(mjs, status == FSE_OK)); @@ -266,7 +323,7 @@ static void js_storage_rename(struct mjs* mjs) { static void js_storage_copy(struct mjs* mjs) { const char *source, *dest; - JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_STR(&source), JS_ARG_STR(&dest)); + JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_storage_2_str_args, &source, &dest); Storage* storage = JS_GET_CONTEXT(mjs); FS_Error status = storage_common_copy(storage, source, dest); mjs_return(mjs, mjs_mk_boolean(mjs, status == FSE_OK || status == FSE_EXIST)); @@ -274,7 +331,7 @@ static void js_storage_copy(struct mjs* mjs) { static void js_storage_fs_info(struct mjs* mjs) { const char* fs; - JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_STR(&fs)); + JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_storage_1_str_args, &fs); Storage* storage = JS_GET_CONTEXT(mjs); uint64_t total_space, free_space; if(storage_common_fs_info(storage, fs, &total_space, &free_space) != FSE_OK) { @@ -290,15 +347,19 @@ static void js_storage_fs_info(struct mjs* mjs) { } static void js_storage_next_available_filename(struct mjs* mjs) { + static const JsValueDeclaration js_storage_naf_arg_list[] = { + JS_VALUE_SIMPLE(JsValueTypeString), + JS_VALUE_SIMPLE(JsValueTypeString), + JS_VALUE_SIMPLE(JsValueTypeString), + JS_VALUE_SIMPLE(JsValueTypeInt32), + }; + static const JsValueArguments js_storage_naf_args = JS_VALUE_ARGS(js_storage_naf_arg_list); + const char *dir_path, *file_name, *file_ext; int32_t max_len; - JS_FETCH_ARGS_OR_RETURN( - mjs, - JS_EXACTLY, - JS_ARG_STR(&dir_path), - JS_ARG_STR(&file_name), - JS_ARG_STR(&file_ext), - JS_ARG_INT32(&max_len)); + JS_VALUE_PARSE_ARGS_OR_RETURN( + mjs, &js_storage_naf_args, &dir_path, &file_name, &file_ext, &max_len); + Storage* storage = JS_GET_CONTEXT(mjs); FuriString* next_name = furi_string_alloc(); storage_get_next_filename(storage, dir_path, file_name, file_ext, next_name, max_len); @@ -306,23 +367,27 @@ static void js_storage_next_available_filename(struct mjs* mjs) { furi_string_free(next_name); } -// ---=== path ops ===--- +// =============== +// Path operations +// =============== static void js_storage_are_paths_equal(struct mjs* mjs) { const char *path1, *path2; - JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_STR(&path1), JS_ARG_STR(&path2)); + JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_storage_2_str_args, &path1, &path2); Storage* storage = JS_GET_CONTEXT(mjs); mjs_return(mjs, mjs_mk_boolean(mjs, storage_common_equivalent_path(storage, path1, path2))); } static void js_storage_is_subpath_of(struct mjs* mjs) { const char *parent, *child; - JS_FETCH_ARGS_OR_RETURN(mjs, JS_EXACTLY, JS_ARG_STR(&parent), JS_ARG_STR(&child)); + JS_VALUE_PARSE_ARGS_OR_RETURN(mjs, &js_storage_2_str_args, &parent, &child); Storage* storage = JS_GET_CONTEXT(mjs); mjs_return(mjs, mjs_mk_boolean(mjs, storage_common_is_subdir(storage, parent, child))); } -// ---=== module ctor & dtor ===--- +// ================== +// Module ctor & dtor +// ================== static void* js_storage_create(struct mjs* mjs, mjs_val_t* object, JsModules* modules) { UNUSED(modules); @@ -363,7 +428,9 @@ static void js_storage_destroy(void* data) { furi_record_close(RECORD_STORAGE); } -// ---=== boilerplate ===--- +// =========== +// Boilerplate +// =========== static const JsModuleDescriptor js_storage_desc = { "storage", diff --git a/applications/system/js_app/plugin_api/app_api_table_i.h b/applications/system/js_app/plugin_api/app_api_table_i.h index b2debbde8..76556bcdd 100644 --- a/applications/system/js_app/plugin_api/app_api_table_i.h +++ b/applications/system/js_app/plugin_api/app_api_table_i.h @@ -1,4 +1,5 @@ -#include "js_plugin_api.h" +#include "../js_modules.h" + /* * A list of app's private functions and objects to expose for plugins. * It is used to generate a table of symbols for import resolver to use. @@ -8,4 +9,16 @@ static constexpr auto app_api_table = sort(create_array_t( API_METHOD(js_delay_with_flags, bool, (struct mjs*, uint32_t)), API_METHOD(js_flags_set, void, (struct mjs*, uint32_t)), API_METHOD(js_flags_wait, uint32_t, (struct mjs*, uint32_t, uint32_t)), - API_METHOD(js_module_get, void*, (JsModules*, const char*)))); + API_METHOD(js_module_get, void*, (JsModules*, const char*)), + API_METHOD(js_value_buffer_size, size_t, (const JsValueParseDeclaration declaration)), + API_METHOD( + js_value_parse, + JsValueParseStatus, + (struct mjs * mjs, + const JsValueParseDeclaration declaration, + JsValueParseFlag flags, + mjs_val_t* buffer, + size_t buf_size, + mjs_val_t* source, + size_t n_c_vals, + ...)))); diff --git a/applications/system/js_app/plugin_api/js_plugin_api.h b/applications/system/js_app/plugin_api/js_plugin_api.h deleted file mode 100644 index 421b68576..000000000 --- a/applications/system/js_app/plugin_api/js_plugin_api.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef void JsModules; - -bool js_delay_with_flags(struct mjs* mjs, uint32_t time); - -void js_flags_set(struct mjs* mjs, uint32_t flags); - -uint32_t js_flags_wait(struct mjs* mjs, uint32_t flags, uint32_t timeout); - -void* js_module_get(JsModules* modules, const char* name); - -#ifdef __cplusplus -} -#endif diff --git a/applications/system/updater/application.fam b/applications/system/updater/application.fam index a693fa6f5..0c56a06ef 100644 --- a/applications/system/updater/application.fam +++ b/applications/system/updater/application.fam @@ -27,7 +27,7 @@ App( provides=["updater_start"], entry_point="updater_srv", stack_size=2 * 1024, - order=10, + order=20, ) App( @@ -35,5 +35,5 @@ App( apptype=FlipperAppType.STARTUP, entry_point="updater_on_system_start", requires=["updater_app"], - order=110, + order=90, ) diff --git a/applications/system/updater/cli/updater_cli.c b/applications/system/updater/cli/updater_cli.c index 56a16bd9d..bad8f0cd6 100644 --- a/applications/system/updater/cli/updater_cli.c +++ b/applications/system/updater/cli/updater_cli.c @@ -1,12 +1,14 @@ #include #include -#include +#include +#include #include #include #include #include #include +#include #include #include #include @@ -63,8 +65,8 @@ static const CliSubcommand update_cli_subcommands[] = { {.command = "help", .handler = updater_cli_help}, }; -static void updater_cli_ep(Cli* cli, FuriString* args, void* context) { - UNUSED(cli); +static void updater_cli_ep(PipeSide* pipe, FuriString* args, void* context) { + UNUSED(pipe); UNUSED(context); FuriString* subcommand; subcommand = furi_string_alloc(); @@ -105,8 +107,8 @@ static void updater_start_app(void* context, uint32_t arg) { void updater_on_system_start(void) { #ifdef SRV_CLI - Cli* cli = (Cli*)furi_record_open(RECORD_CLI); - cli_add_command(cli, "update", CliCommandFlagDefault, updater_cli_ep, NULL); + CliRegistry* registry = furi_record_open(RECORD_CLI); + cli_registry_add_command(registry, "update", CliCommandFlagDefault, updater_cli_ep, NULL); furi_record_close(RECORD_CLI); #else UNUSED(updater_cli_ep); diff --git a/assets/dolphin/external/L1_Doom_128x64/frame_0.png b/assets/dolphin/external/L1_Doom_128x64/frame_0.png new file mode 100644 index 000000000..974fda986 Binary files /dev/null and b/assets/dolphin/external/L1_Doom_128x64/frame_0.png differ diff --git a/assets/dolphin/external/L1_Doom_128x64/frame_1.png b/assets/dolphin/external/L1_Doom_128x64/frame_1.png new file mode 100644 index 000000000..3a9a3a71a Binary files /dev/null and b/assets/dolphin/external/L1_Doom_128x64/frame_1.png differ diff --git a/assets/dolphin/external/L1_Doom_128x64/frame_10.png b/assets/dolphin/external/L1_Doom_128x64/frame_10.png new file mode 100644 index 000000000..7cf69cc1b Binary files /dev/null and b/assets/dolphin/external/L1_Doom_128x64/frame_10.png differ diff --git a/assets/dolphin/external/L1_Doom_128x64/frame_11.png b/assets/dolphin/external/L1_Doom_128x64/frame_11.png new file mode 100644 index 000000000..99eb9b2a1 Binary files /dev/null and b/assets/dolphin/external/L1_Doom_128x64/frame_11.png differ diff --git a/assets/dolphin/external/L1_Doom_128x64/frame_12.png b/assets/dolphin/external/L1_Doom_128x64/frame_12.png new file mode 100644 index 000000000..973f97378 Binary files /dev/null and b/assets/dolphin/external/L1_Doom_128x64/frame_12.png differ diff --git a/assets/dolphin/external/L1_Doom_128x64/frame_13.png b/assets/dolphin/external/L1_Doom_128x64/frame_13.png new file mode 100644 index 000000000..1a7e06051 Binary files /dev/null and b/assets/dolphin/external/L1_Doom_128x64/frame_13.png differ diff --git a/assets/dolphin/external/L1_Doom_128x64/frame_14.png b/assets/dolphin/external/L1_Doom_128x64/frame_14.png new file mode 100644 index 000000000..428ac9f1f Binary files /dev/null and b/assets/dolphin/external/L1_Doom_128x64/frame_14.png differ diff --git a/assets/dolphin/external/L1_Doom_128x64/frame_15.png b/assets/dolphin/external/L1_Doom_128x64/frame_15.png new file mode 100644 index 000000000..aad59f943 Binary files /dev/null and b/assets/dolphin/external/L1_Doom_128x64/frame_15.png differ diff --git a/assets/dolphin/external/L1_Doom_128x64/frame_16.png b/assets/dolphin/external/L1_Doom_128x64/frame_16.png new file mode 100644 index 000000000..4dfc84535 Binary files /dev/null and b/assets/dolphin/external/L1_Doom_128x64/frame_16.png differ diff --git a/assets/dolphin/external/L1_Doom_128x64/frame_17.png b/assets/dolphin/external/L1_Doom_128x64/frame_17.png new file mode 100644 index 000000000..81f4f8beb Binary files /dev/null and b/assets/dolphin/external/L1_Doom_128x64/frame_17.png differ diff --git a/assets/dolphin/external/L1_Doom_128x64/frame_18.png b/assets/dolphin/external/L1_Doom_128x64/frame_18.png new file mode 100644 index 000000000..78ad1c754 Binary files /dev/null and b/assets/dolphin/external/L1_Doom_128x64/frame_18.png differ diff --git a/assets/dolphin/external/L1_Doom_128x64/frame_19.png b/assets/dolphin/external/L1_Doom_128x64/frame_19.png new file mode 100644 index 000000000..efd1d6b0e Binary files /dev/null and b/assets/dolphin/external/L1_Doom_128x64/frame_19.png differ diff --git a/assets/dolphin/external/L1_Doom_128x64/frame_2.png b/assets/dolphin/external/L1_Doom_128x64/frame_2.png new file mode 100644 index 000000000..d740f5e33 Binary files /dev/null and b/assets/dolphin/external/L1_Doom_128x64/frame_2.png differ diff --git a/assets/dolphin/external/L1_Doom_128x64/frame_20.png b/assets/dolphin/external/L1_Doom_128x64/frame_20.png new file mode 100644 index 000000000..24693dfe4 Binary files /dev/null and b/assets/dolphin/external/L1_Doom_128x64/frame_20.png differ diff --git a/assets/dolphin/external/L1_Doom_128x64/frame_21.png b/assets/dolphin/external/L1_Doom_128x64/frame_21.png new file mode 100644 index 000000000..f08e98d81 Binary files /dev/null and b/assets/dolphin/external/L1_Doom_128x64/frame_21.png differ diff --git a/assets/dolphin/external/L1_Doom_128x64/frame_22.png b/assets/dolphin/external/L1_Doom_128x64/frame_22.png new file mode 100644 index 000000000..baa327482 Binary files /dev/null and b/assets/dolphin/external/L1_Doom_128x64/frame_22.png differ diff --git a/assets/dolphin/external/L1_Doom_128x64/frame_23.png b/assets/dolphin/external/L1_Doom_128x64/frame_23.png new file mode 100644 index 000000000..f1b7fe627 Binary files /dev/null and b/assets/dolphin/external/L1_Doom_128x64/frame_23.png differ diff --git a/assets/dolphin/external/L1_Doom_128x64/frame_24.png b/assets/dolphin/external/L1_Doom_128x64/frame_24.png new file mode 100644 index 000000000..24693dfe4 Binary files /dev/null and b/assets/dolphin/external/L1_Doom_128x64/frame_24.png differ diff --git a/assets/dolphin/external/L1_Doom_128x64/frame_25.png b/assets/dolphin/external/L1_Doom_128x64/frame_25.png new file mode 100644 index 000000000..2f12df072 Binary files /dev/null and b/assets/dolphin/external/L1_Doom_128x64/frame_25.png differ diff --git a/assets/dolphin/external/L1_Doom_128x64/frame_26.png b/assets/dolphin/external/L1_Doom_128x64/frame_26.png new file mode 100644 index 000000000..e90099bd6 Binary files /dev/null and b/assets/dolphin/external/L1_Doom_128x64/frame_26.png differ diff --git a/assets/dolphin/external/L1_Doom_128x64/frame_27.png b/assets/dolphin/external/L1_Doom_128x64/frame_27.png new file mode 100644 index 000000000..aaf9e6654 Binary files /dev/null and b/assets/dolphin/external/L1_Doom_128x64/frame_27.png differ diff --git a/assets/dolphin/external/L1_Doom_128x64/frame_28.png b/assets/dolphin/external/L1_Doom_128x64/frame_28.png new file mode 100644 index 000000000..6e4a77679 Binary files /dev/null and b/assets/dolphin/external/L1_Doom_128x64/frame_28.png differ diff --git a/assets/dolphin/external/L1_Doom_128x64/frame_29.png b/assets/dolphin/external/L1_Doom_128x64/frame_29.png new file mode 100644 index 000000000..06fa8b6e4 Binary files /dev/null and b/assets/dolphin/external/L1_Doom_128x64/frame_29.png differ diff --git a/assets/dolphin/external/L1_Doom_128x64/frame_3.png b/assets/dolphin/external/L1_Doom_128x64/frame_3.png new file mode 100644 index 000000000..81bff4b92 Binary files /dev/null and b/assets/dolphin/external/L1_Doom_128x64/frame_3.png differ diff --git a/assets/dolphin/external/L1_Doom_128x64/frame_30.png b/assets/dolphin/external/L1_Doom_128x64/frame_30.png new file mode 100644 index 000000000..6d6717c40 Binary files /dev/null and b/assets/dolphin/external/L1_Doom_128x64/frame_30.png differ diff --git a/assets/dolphin/external/L1_Doom_128x64/frame_31.png b/assets/dolphin/external/L1_Doom_128x64/frame_31.png new file mode 100644 index 000000000..25770b6fe Binary files /dev/null and b/assets/dolphin/external/L1_Doom_128x64/frame_31.png differ diff --git a/assets/dolphin/external/L1_Doom_128x64/frame_32.png b/assets/dolphin/external/L1_Doom_128x64/frame_32.png new file mode 100644 index 000000000..24ee19f40 Binary files /dev/null and b/assets/dolphin/external/L1_Doom_128x64/frame_32.png differ diff --git a/assets/dolphin/external/L1_Doom_128x64/frame_33.png b/assets/dolphin/external/L1_Doom_128x64/frame_33.png new file mode 100644 index 000000000..3581d8d59 Binary files /dev/null and b/assets/dolphin/external/L1_Doom_128x64/frame_33.png differ diff --git a/assets/dolphin/external/L1_Doom_128x64/frame_34.png b/assets/dolphin/external/L1_Doom_128x64/frame_34.png new file mode 100644 index 000000000..914390376 Binary files /dev/null and b/assets/dolphin/external/L1_Doom_128x64/frame_34.png differ diff --git a/assets/dolphin/external/L1_Doom_128x64/frame_35.png b/assets/dolphin/external/L1_Doom_128x64/frame_35.png new file mode 100644 index 000000000..6e6ac6a02 Binary files /dev/null and b/assets/dolphin/external/L1_Doom_128x64/frame_35.png differ diff --git a/assets/dolphin/external/L1_Doom_128x64/frame_36.png b/assets/dolphin/external/L1_Doom_128x64/frame_36.png new file mode 100644 index 000000000..2f37e6371 Binary files /dev/null and b/assets/dolphin/external/L1_Doom_128x64/frame_36.png differ diff --git a/assets/dolphin/external/L1_Doom_128x64/frame_37.png b/assets/dolphin/external/L1_Doom_128x64/frame_37.png new file mode 100644 index 000000000..e6f374c4f Binary files /dev/null and b/assets/dolphin/external/L1_Doom_128x64/frame_37.png differ diff --git a/assets/dolphin/external/L1_Doom_128x64/frame_38.png b/assets/dolphin/external/L1_Doom_128x64/frame_38.png new file mode 100644 index 000000000..189a16f7b Binary files /dev/null and b/assets/dolphin/external/L1_Doom_128x64/frame_38.png differ diff --git a/assets/dolphin/external/L1_Doom_128x64/frame_4.png b/assets/dolphin/external/L1_Doom_128x64/frame_4.png new file mode 100644 index 000000000..424f481ff Binary files /dev/null and b/assets/dolphin/external/L1_Doom_128x64/frame_4.png differ diff --git a/assets/dolphin/external/L1_Doom_128x64/frame_5.png b/assets/dolphin/external/L1_Doom_128x64/frame_5.png new file mode 100644 index 000000000..fa7e6ad10 Binary files /dev/null and b/assets/dolphin/external/L1_Doom_128x64/frame_5.png differ diff --git a/assets/dolphin/external/L1_Doom_128x64/frame_6.png b/assets/dolphin/external/L1_Doom_128x64/frame_6.png new file mode 100644 index 000000000..063f13bae Binary files /dev/null and b/assets/dolphin/external/L1_Doom_128x64/frame_6.png differ diff --git a/assets/dolphin/external/L1_Doom_128x64/frame_7.png b/assets/dolphin/external/L1_Doom_128x64/frame_7.png new file mode 100644 index 000000000..0995b83bf Binary files /dev/null and b/assets/dolphin/external/L1_Doom_128x64/frame_7.png differ diff --git a/assets/dolphin/external/L1_Doom_128x64/frame_8.png b/assets/dolphin/external/L1_Doom_128x64/frame_8.png new file mode 100644 index 000000000..f5911b1b8 Binary files /dev/null and b/assets/dolphin/external/L1_Doom_128x64/frame_8.png differ diff --git a/assets/dolphin/external/L1_Doom_128x64/frame_9.png b/assets/dolphin/external/L1_Doom_128x64/frame_9.png new file mode 100644 index 000000000..57491c2ed Binary files /dev/null and b/assets/dolphin/external/L1_Doom_128x64/frame_9.png differ diff --git a/assets/dolphin/external/L1_Doom_128x64/meta.txt b/assets/dolphin/external/L1_Doom_128x64/meta.txt new file mode 100644 index 000000000..838239623 --- /dev/null +++ b/assets/dolphin/external/L1_Doom_128x64/meta.txt @@ -0,0 +1,14 @@ +Filetype: Flipper Animation +Version: 1 + +Width: 128 +Height: 64 +Passive frames: 15 +Active frames: 24 +Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 +Active cycles: 1 +Frame rate: 2 +Duration: 3600 +Active cooldown: 7 + +Bubble slots: 0 \ No newline at end of file diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_0.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_0.png deleted file mode 100755 index f1207ed14..000000000 Binary files a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_0.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_1.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_1.png deleted file mode 100755 index 9d9012281..000000000 Binary files a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_1.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_10.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_10.png deleted file mode 100755 index cb8f173b0..000000000 Binary files a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_10.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_11.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_11.png deleted file mode 100755 index 0b042e3aa..000000000 Binary files a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_11.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_12.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_12.png deleted file mode 100755 index 5d4c7e7c5..000000000 Binary files a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_12.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_2.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_2.png deleted file mode 100755 index 35ee06ee0..000000000 Binary files a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_2.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_3.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_3.png deleted file mode 100755 index 3a47dfc17..000000000 Binary files a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_3.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_4.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_4.png deleted file mode 100755 index 0b3bb3250..000000000 Binary files a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_4.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_5.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_5.png deleted file mode 100755 index 233448547..000000000 Binary files a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_5.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_6.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_6.png deleted file mode 100755 index b7164380d..000000000 Binary files a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_6.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_7.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_7.png deleted file mode 100755 index da3a78f4c..000000000 Binary files a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_7.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_8.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_8.png deleted file mode 100755 index adbe53159..000000000 Binary files a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_8.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_9.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_9.png deleted file mode 100755 index 3d81f935a..000000000 Binary files a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_9.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/meta.txt b/assets/dolphin/external/L1_Happy_holidays_128x64/meta.txt deleted file mode 100755 index a2c733397..000000000 --- a/assets/dolphin/external/L1_Happy_holidays_128x64/meta.txt +++ /dev/null @@ -1,23 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 10 -Active frames: 18 -Frames order: 0 1 2 1 0 1 2 1 0 1 2 3 4 5 6 5 4 7 2 8 9 10 11 10 9 10 11 12 -Active cycles: 1 -Frame rate: 2 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 1 - -Slot: 0 -X: 11 -Y: 19 -Text: HAPPY\nHOLIDAYS! -AlignH: Right -AlignV: Center -StartFrame: 22 -EndFrame: 27 diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_0.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_0.png deleted file mode 100755 index 0e86e6641..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_0.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_1.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_1.png deleted file mode 100755 index 219407e4b..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_1.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_10.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_10.png deleted file mode 100755 index 5459ae27c..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_10.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_11.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_11.png deleted file mode 100755 index 9f9d80de6..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_11.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_12.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_12.png deleted file mode 100755 index 7b35e1d3c..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_12.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_13.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_13.png deleted file mode 100755 index 9e560baa8..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_13.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_14.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_14.png deleted file mode 100755 index a5d6c1a7a..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_14.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_15.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_15.png deleted file mode 100755 index 57b2c9bbe..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_15.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_16.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_16.png deleted file mode 100755 index 4be832c64..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_16.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_17.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_17.png deleted file mode 100755 index 1910cf939..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_17.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_18.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_18.png deleted file mode 100755 index 0236692c6..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_18.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_19.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_19.png deleted file mode 100755 index 7450c87b4..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_19.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_2.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_2.png deleted file mode 100755 index 0355c510c..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_2.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_20.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_20.png deleted file mode 100755 index c371e217f..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_20.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_21.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_21.png deleted file mode 100755 index 822d2230e..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_21.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_22.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_22.png deleted file mode 100755 index 6d359ef86..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_22.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_23.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_23.png deleted file mode 100755 index 639834612..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_23.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_24.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_24.png deleted file mode 100755 index fbafab70f..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_24.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_25.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_25.png deleted file mode 100755 index 190985282..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_25.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_26.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_26.png deleted file mode 100755 index 894e62d29..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_26.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_27.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_27.png deleted file mode 100755 index 962349bfd..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_27.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_28.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_28.png deleted file mode 100755 index 2eb4456cc..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_28.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_29.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_29.png deleted file mode 100755 index ee325d3ee..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_29.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_3.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_3.png deleted file mode 100755 index e5772a672..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_3.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_30.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_30.png deleted file mode 100755 index 11fc5d0b4..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_30.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_31.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_31.png deleted file mode 100755 index ec47b899c..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_31.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_32.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_32.png deleted file mode 100755 index 3f345b563..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_32.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_33.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_33.png deleted file mode 100755 index c044765eb..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_33.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_34.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_34.png deleted file mode 100755 index 44c4d0d49..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_34.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_35.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_35.png deleted file mode 100755 index f70ecb3ab..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_35.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_36.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_36.png deleted file mode 100755 index 9af3a8338..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_36.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_4.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_4.png deleted file mode 100755 index 7d970234f..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_4.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_5.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_5.png deleted file mode 100755 index 70eab1f3e..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_5.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_6.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_6.png deleted file mode 100755 index 8169e0766..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_6.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_7.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_7.png deleted file mode 100755 index adee1a63d..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_7.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_8.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_8.png deleted file mode 100755 index db6e667fb..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_8.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_9.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_9.png deleted file mode 100755 index a6a37fe29..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_9.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/meta.txt b/assets/dolphin/external/L1_Sleigh_ride_128x64/meta.txt deleted file mode 100755 index 3e31e1d69..000000000 --- a/assets/dolphin/external/L1_Sleigh_ride_128x64/meta.txt +++ /dev/null @@ -1,23 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 18 -Active frames: 19 -Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 -Active cycles: 1 -Frame rate: 2 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 1 - -Slot: 0 -X: 21 -Y: 25 -Text: AAAAaAAAAHHh!! -AlignH: Right -AlignV: Bottom -StartFrame: 30 -EndFrame: 32 diff --git a/assets/dolphin/external/manifest.txt b/assets/dolphin/external/manifest.txt index 0b4b9a426..20eef3364 100644 --- a/assets/dolphin/external/manifest.txt +++ b/assets/dolphin/external/manifest.txt @@ -211,23 +211,16 @@ Min level: 1 Max level: 3 Weight: 3 -Name: L1_Happy_holidays_128x64 -Min butthurt: 0 -Max butthurt: 14 -Min level: 1 -Max level: 3 -Weight: 3 - -Name: L1_Sleigh_ride_128x64 -Min butthurt: 0 -Max butthurt: 14 -Min level: 1 -Max level: 3 -Weight: 4 - Name: L1_Showtime_128x64 Min butthurt: 0 Max butthurt: 10 Min level: 1 Max level: 3 Weight: 4 + +Name: L1_Doom_128x64 +Min butthurt: 0 +Max butthurt: 13 +Min level: 1 +Max level: 3 +Weight: 4 diff --git a/assets/icons/Archive/settings_10px.png b/assets/icons/Archive/settings_10px.png new file mode 100644 index 000000000..2c1e4a262 Binary files /dev/null and b/assets/icons/Archive/settings_10px.png differ diff --git a/documentation/doxygen/js.dox b/documentation/doxygen/js.dox index 7abde9389..fadf0c023 100644 --- a/documentation/doxygen/js.dox +++ b/documentation/doxygen/js.dox @@ -17,6 +17,7 @@ Flipper Zero's built-in JavaScript engine enables you to run lightweight scripts - @subpage js_badusb — This module allows you to emulate a standard USB keyboard - @subpage js_event_loop — The module for easy event-based developing +- @subpage js_flipper — This module allows to query device information - @subpage js_gpio — This module allows you to control GPIO pins - @subpage js_gui — This module allows you to use GUI (graphical user interface) - @subpage js_math — This module contains mathematical methods and constants diff --git a/documentation/file_formats/BadUsbScriptFormat.md b/documentation/file_formats/BadUsbScriptFormat.md index 11977c9cb..a26f12489 100644 --- a/documentation/file_formats/BadUsbScriptFormat.md +++ b/documentation/file_formats/BadUsbScriptFormat.md @@ -57,19 +57,17 @@ Pause script execution by a defined time. ### Modifier keys -Can be combined with a special key command or a single character. -| Command | Notes | -| -------------- | ---------- | -| CONTROL / CTRL | | -| SHIFT | | -| ALT | | -| WINDOWS / GUI | | -| CTRL-ALT | CTRL+ALT | -| CTRL-SHIFT | CTRL+SHIFT | -| ALT-SHIFT | ALT+SHIFT | -| ALT-GUI | ALT+WIN | -| GUI-SHIFT | WIN+SHIFT | -| GUI-CTRL | WIN+CTRL | +The following modifier keys are recognized: +| Command | Notes | +| ------- | ------------ | +| CTRL | | +| CONTROL | Same as CTRL | +| SHIFT | | +| ALT | | +| GUI | | +| WINDOWS | Same as GUI | + +You can chain multiple modifier keys together using hyphens (`-`) or spaces. ## Key hold and release diff --git a/documentation/file_formats/SubGhzFileFormats.md b/documentation/file_formats/SubGhzFileFormats.md index c4d63835e..80047faf7 100644 --- a/documentation/file_formats/SubGhzFileFormats.md +++ b/documentation/file_formats/SubGhzFileFormats.md @@ -178,7 +178,7 @@ Data_RAW: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 DE 02 D3 54 D5 4C D2 C Frequency: 433920000 Preset: FuriHalSubGhzPresetCustom Custom_preset_module: CC1101 - Сustom_preset_data: 02 0D 03 07 08 32 0B 06 14 00 13 00 12 30 11 32 10 17 18 18 19 18 1D 91 1C 00 1B 07 20 FB 22 11 21 B6 00 00 00 C0 00 00 00 00 00 00 + Custom_preset_data: 02 0D 03 07 08 32 0B 06 14 00 13 00 12 30 11 32 10 17 18 18 19 18 1D 91 1C 00 1B 07 20 FB 22 11 21 B6 00 00 00 C0 00 00 00 00 00 00 Protocol: RAW RAW_Data: 29262 361 -68 2635 -66 24113 -66 11 ... RAW_Data: -424 205 -412 159 -412 381 -240 181 ... diff --git a/documentation/js/js_badusb.md b/documentation/js/js_badusb.md index d1d1e558d..79ae53f55 100644 --- a/documentation/js/js_badusb.md +++ b/documentation/js/js_badusb.md @@ -11,8 +11,9 @@ Start USB HID with optional parameters. Should be called before all other method Configuration object *(optional)*: - vid, pid (number): VID and PID values, both are mandatory -- mfr_name (string): Manufacturer name (32 ASCII characters max), optional -- prod_name (string): Product name (32 ASCII characters max), optional +- mfrName (string): Manufacturer name (32 ASCII characters max), optional +- prodName (string): Product name (32 ASCII characters max), optional +- layoutPath (string): Path to keyboard layout file, optional **Examples** ```js @@ -21,7 +22,7 @@ badusb.setup(); // Start USB HID with custom vid:pid = AAAA:BBBB, manufacturer and product strings not defined badusb.setup({ vid: 0xAAAA, pid: 0xBBBB }); // Start USB HID with custom vid:pid = AAAA:BBBB, manufacturer string = "Flipper Devices", product string = "Flipper Zero" -badusb.setup({ vid: 0xAAAA, pid: 0xBBBB, mfr_name: "Flipper Devices", prod_name: "Flipper Zero" }); +badusb.setup({ vid: 0xAAAA, pid: 0xBBBB, mfrName: "Flipper Devices", prodName: "Flipper Zero" }); ```
@@ -122,6 +123,45 @@ badusb.println("Hello, world!"); // print "Hello, world!" and press "ENTER" ```
+## altPrint() +Prints a string by Alt+Numpad method - works only on Windows! + +**Parameters** + +- A string to print +- *(optional)* delay between key presses + +**Examples** +```js +badusb.altPrint("Hello, world!"); // print "Hello, world!" +badusb.altPrint("Hello, world!", 100); // Add 100ms delay between key presses +``` +
+ +## altPrintln() +Same as `altPrint` but ended with "ENTER" press. + +**Parameters** + +- A string to print +- *(optional)* delay between key presses + +**Examples** +```js +badusb.altPrintln("Hello, world!"); // print "Hello, world!" and press "ENTER" +``` +
+ +## quit() +Releases usb, optional, but allows to interchange with usbdisk. + +**Examples** +```js +badusb.quit(); +usbdisk.start(...) +``` +
+ # Key names list {#js_badusb_keynames} ## Modifier keys @@ -159,3 +199,4 @@ badusb.println("Hello, world!"); // print "Hello, world!" and press "ENTER" | TAB | | | MENU | Context menu key | | Fx | F1-F24 keys | +| NUMx | NUM0-NUM9 keys | diff --git a/documentation/js/js_builtin.md b/documentation/js/js_builtin.md index 0a128859a..2d2f9417a 100644 --- a/documentation/js/js_builtin.md +++ b/documentation/js/js_builtin.md @@ -39,28 +39,277 @@ print("string1", "string2", 123); ```
-## console.log() - -
- -## console.warn() - -
- -## console.error() - -
- -## console.debug() +## Console object Same as `print`, but output to serial console only, with corresponding log level. +### console.log() +
-## to_string() +### console.warn() + +
+ +### console.error() + +
+ +### console.debug() + +
+ +## load() +Runs a JS file and returns value from it. + +**Parameters** +- The path to the file +- An optional object to use as the global scope while running this file + +**Examples** +```js +load("/ext/apps/Scripts/script.js"); +``` +
+ +## chr() +Convert an ASCII character number to string. + +**Examples** +```js +chr(65); // "A" +``` +
+ +## die() +Exit JavaScript with given message. + +**Examples** +```js +die("Some error occurred"); +``` +
+ +## parseInt() +Convert a string to number with an optional base. + +**Examples** +```js +parseInt("123"); // 123 +parseInt("7b", 16); // 123 +``` +
+ +## Number object + +### Number.toString() Convert a number to string with an optional base. **Examples** ```js -to_string(123) // "123" -to_string(123, 16) // "0x7b" +let num = 123; +num.toString(); // "123" +num.toString(16); // "0x7b" +``` +
+ +## ArrayBuffer object + +**Fields** + +- byteLength: The length of the buffer in bytes +
+ +### ArrayBuffer.slice() +Creates an `ArrayBuffer` that contains a sub-part of the buffer. + +**Parameters** +- The index to start the new buffer at +- An optional non-inclusive index of where to stop the new buffer + +**Examples** +```js +Uint8Array([1, 2, 3]).buffer.slice(0, 1) // ArrayBuffer([1]) +``` +
+ +## DataView objects +Wrappers around `ArrayBuffer` objects, with dedicated types such as: +- `Uint8Array` +- `Int8Array` +- `Uint16Array` +- `Int16Array` +- `Uint32Array` +- `Int32Array` + +**Fields** + +- byteLength: The length of the buffer in bytes +- length: The length of the buffer in typed elements +- buffer: The underlying `ArrayBuffer` +
+ +## Array object + +**Fields** + +- length: How many elements there are in the array +
+ +### Array.splice() +Removes elements from the array and returns them in a new array. + +**Parameters** +- The index to start taking elements from +- An optional count of how many elements to take + +**Examples** +```js +let arr = [1, 2, 3]; +arr.splice(1); // [2, 3] +arr; // [1] +``` +
+ +### Array.push() +Adds a value to the end of the array. + +**Examples** +```js +let arr = [1, 2]; +arr.push(3); +arr; // [1, 2, 3] +``` +
+ +## String object + +**Fields** + +- length: How many characters there are in the string +
+ +### String.charCodeAt() +Returns the character code at an index in the string. + +**Examples** +```js +"A".charCodeAt(0) // 65 +``` +
+ +### String.at() +Same as `String.charCodeAt()`. +
+ +### String.indexOf() +Return index of first occurrence of substr within the string or `-1` if not found. + +**Parameters** +- Substring to search for +- Optional index to start searching from + +**Examples** +```js +"Example".indexOf("amp") // 2 +``` +
+ +### String.slice() +Return a substring between two indices. + +**Parameters** +- The index to start the new string at +- An optional non-inclusive index of where to stop the new string + +**Examples** +```js +"Example".slice(2) // "ample" +``` +
+ +### String.toUpperCase() +Transforms the string to upper case. + +**Examples** +```js +"Example".toUpperCase() // "EXAMPLE" +``` +
+ +### String.toLowerCase() +Transforms the string to lower case. + +**Examples** +```js +"Example".toLowerCase() // "example" +``` +
+ +## __dirname +Path to the directory containing the current script. + +**Examples** +```js +print(__dirname); // /ext/apps/Scripts +``` +
+ +## __filename +Path to the current script file. + +**Examples** +```js +print(__filename); // /ext/apps/Scripts/path.js +``` +
+ +# SDK compatibility methods {#js_builtin_sdk_compatibility} + +## sdkCompatibilityStatus() +Checks compatibility between the script and the JS SDK that the firmware provides. + +**Returns** +- `"compatible"` if the script and the JS SDK are compatible +- `"firmwareTooOld"` if the expected major version is larger than the version of the firmware, or if the expected minor version is larger than the version of the firmware +- `"firmwareTooNew"` if the expected major version is lower than the version of the firmware + +**Examples** +```js +sdkCompatibilityStatus(0, 3); // "compatible" +``` +
+ +## isSdkCompatible() +Checks compatibility between the script and the JS SDK that the firmware provides in a boolean fashion. + +**Examples** +```js +isSdkCompatible(0, 3); // true +``` +
+ +## checkSdkCompatibility() +Asks the user whether to continue executing the script if the versions are not compatible. Does nothing if they are. + +**Examples** +```js +checkSdkCompatibility(0, 3); +``` +
+ +## doesSdkSupport() +Checks whether all of the specified extra features are supported by the interpreter. + +**Examples** +```js +doesSdkSupport(["gui-widget"]); // true +``` +
+ +## checkSdkFeatures() +Checks whether all of the specified extra features are supported by the interpreter, asking the user if they want to continue running the script if they're not. + +**Examples** +```js +checkSdkFeatures(["gui-widget"]); ``` diff --git a/documentation/js/js_data_types.md b/documentation/js/js_data_types.md index bd3bb1f42..2a94ba5d2 100644 --- a/documentation/js/js_data_types.md +++ b/documentation/js/js_data_types.md @@ -7,7 +7,7 @@ Here is a list of common data types used by mJS. - foreign — C function or data pointer - undefined - null -- object — a data structure with named fields -- array — special type of object, all items have indexes and equal types +- Object — a data structure with named fields +- Array — special type of object, all items have indexes and equal types - ArrayBuffer — raw data buffer - DataView — provides interface for accessing ArrayBuffer contents diff --git a/documentation/js/js_developing_apps_using_js_sdk.md b/documentation/js/js_developing_apps_using_js_sdk.md index 952993566..1a764e1df 100644 --- a/documentation/js/js_developing_apps_using_js_sdk.md +++ b/documentation/js/js_developing_apps_using_js_sdk.md @@ -70,6 +70,27 @@ The JS minifier reduces the size of JavaScript files by removing unnecessary cha However, it has a drawback — it can make debugging harder, as error messages in minified files are harder to read in larger applications. For this reason, it's recommended to disable the JS minifier during debugging and it's disabled by default. To enable it, set the `minify` parameter to `true` in the `fz-sdk.config.json5` file in your app folder. This will minify your JavaScript app before loading it onto Flipper Zero. +## Differences with normal Flipper JavaScript + +With the Flipper JavaScript SDK, you will be developing in **TypeScript**. This means that you get a better development experience, with more accurate code completion and warnings when variable types are incompatible, but it also means your code will be different from basic Flipper JS. + +Some things to look out for: +- Importing modules: + - Instead of `let module = require("module");` + - You will use `import * as module from "@flipperdevices/fz-sdk/module";` +- Multiple source code files: + - The Flipper JavaScript SDK does not yet support having multiple `.ts` files and importing them + - You can use `load()`, but this will not benefit from TypeScript type checking +- Casting values: + - Some Flipper JavaScript functions will return generic types + - For example `eventLoop.subscribe()` will run your callback with a generic `Item` type + - In some cases you might need to cast these values before using them, you can do this by: + - Inline casting: `item` + - Declare with new type: `let text = item as string;` + +When you upload the script to Flipper with `npm start`, it gets transpiled to normal JavaScript and optionally minified (see below). If you're looking to share your script with others, this is what you should give them to run. + + ## What's next? You've learned how to run and debug simple JavaScript apps. But how can you access Flipper Zero's hardware from your JS code? For that, you'll need to use JS modules — which we'll cover in the next guide. diff --git a/documentation/js/js_flipper.md b/documentation/js/js_flipper.md new file mode 100644 index 000000000..a4da23868 --- /dev/null +++ b/documentation/js/js_flipper.md @@ -0,0 +1,52 @@ +# Flipper module {#js_flipper} + +The module contains methods and values to query device information and properties. Call the `require` function to load the module before first using its methods: + +```js +let flipper = require("flipper"); +``` + +# Values + +## firmwareVendor +String representing the firmware installed on the device. +Original firmware reports `"flipperdevices"`. +Do **NOT** use this to check the presence or absence of features, refer to [other ways to check SDK compatibility](#js_builtin_sdk_compatibility). + +## jsSdkVersion +Version of the JavaScript SDK. +Do **NOT** use this to check the presence or absence of features, refer to [other ways to check SDK compatibility](#js_builtin_sdk_compatibility). + +
+ +--- + +# Methods + +## getModel() +Returns the device model. + +**Example** +```js +flipper.getModel(); // "Flipper Zero" +``` + +
+ +## getName() +Returns the name of the virtual dolphin. + +**Example** +```js +flipper.getName(); // "Fur1pp44" +``` + +
+ +## getBatteryCharge() +Returns the battery charge percentage. + +**Example** +```js +flipper.getBatteryCharge(); // 100 +``` diff --git a/documentation/js/js_gpio.md b/documentation/js/js_gpio.md index 59a5504b0..d058d7329 100644 --- a/documentation/js/js_gpio.md +++ b/documentation/js/js_gpio.md @@ -24,7 +24,7 @@ delay(1000); --- # API reference -## get +## get() Gets a `Pin` object that can be used to manage a pin. **Parameters** @@ -89,3 +89,33 @@ Attaches an interrupt to a pin configured with `direction: "in"` and An event loop `Contract` object that identifies the interrupt event source. The event does not produce any extra data. + +### Pin.isPwmSupported() +Determines whether this pin supports PWM. +If `false`, all other PWM-related methods on this pin will throw an error when called. + +**Returns** + +Boolean value. + +### Pin.pwmWrite() +Sets PWM parameters and starts the PWM. +Configures the pin with `{ direction: "out", outMode: "push_pull" }`. +Throws an error if PWM is not supported on this pin. + +**Parameters** + - `freq`: Frequency in Hz + - `duty`: Duty cycle in % + +### Pin.isPwmRunning() +Determines whether PWM is running. +Throws an error if PWM is not supported on this pin. + +**Returns** + +Boolean value. + +### Pin.pwmStop() +Stops PWM. +Does not restore previous pin configuration. +Throws an error if PWM is not supported on this pin. diff --git a/documentation/js/js_gui.md b/documentation/js/js_gui.md index 2bb2a2eee..b04255008 100644 --- a/documentation/js/js_gui.md +++ b/documentation/js/js_gui.md @@ -10,12 +10,15 @@ let gui = require("gui"); GUI module has several submodules: -- @subpage js_gui__submenu — Displays a scrollable list of clickable textual entries -- @subpage js_gui__loading — Displays an animated hourglass icon -- @subpage js_gui__empty_screen — Just empty screen -- @subpage js_gui__text_input — Keyboard-like text input -- @subpage js_gui__text_box — Simple multiline text box +- @subpage js_gui__byte_input — Keyboard-like hex input - @subpage js_gui__dialog — Dialog with up to 3 options +- @subpage js_gui__empty_screen — Just empty screen +- @subpage js_gui__file_picker — Displays a file selection prompt +- @subpage js_gui__icon — Retrieves and loads icons for use in GUI +- @subpage js_gui__loading — Displays an animated hourglass icon +- @subpage js_gui__submenu — Displays a scrollable list of clickable textual entries +- @subpage js_gui__text_box — Simple multiline text box +- @subpage js_gui__text_input — Keyboard-like text input - @subpage js_gui__widget — Displays a combination of custom elements on one screen --- @@ -38,23 +41,23 @@ always access the canvas through a viewport. In Flipper's terminology, a "View" is a fullscreen design element that assumes control over the entire viewport and all input events. Different types of views are available (not all of which are unfortunately currently implemented in JS): -| View | Has JS adapter? | -|----------------------|------------------| -| `button_menu` | ❌ | -| `button_panel` | ❌ | -| `byte_input` | ❌ | -| `dialog_ex` | ✅ (as `dialog`) | -| `empty_screen` | ✅ | -| `file_browser` | ❌ | -| `loading` | ✅ | -| `menu` | ❌ | -| `number_input` | ❌ | -| `popup` | ❌ | -| `submenu` | ✅ | -| `text_box` | ✅ | -| `text_input` | ✅ | -| `variable_item_list` | ❌ | -| `widget` | ❌ | +| View | Has JS adapter? | +|----------------------|-----------------------| +| `button_menu` | ❌ | +| `button_panel` | ❌ | +| `byte_input` | ✅ | +| `dialog_ex` | ✅ (as `dialog`) | +| `empty_screen` | ✅ | +| `file_browser` | ✅ (as `file_picker`) | +| `loading` | ✅ | +| `menu` | ❌ | +| `number_input` | ❌ | +| `popup` | ❌ | +| `submenu` | ✅ | +| `text_box` | ✅ | +| `text_input` | ✅ | +| `variable_item_list` | ❌ | +| `widget` | ✅ | In JS, each view has its own set of properties (or just "props"). The programmer can manipulate these properties in two ways: @@ -173,6 +176,11 @@ triggered when the back key is pressed.
+### viewDispatcher.currentView +The `View` object currently being shown. + +
+ ## ViewFactory When you import a module implementing a view, a `ViewFactory` is instantiated. For example, in the example above, `loadingView`, `submenuView` and `emptyView` are view factories. @@ -183,8 +191,40 @@ Creates an instance of a `View`.
-### ViewFactory.make(props) -Creates an instance of a `View` and assigns initial properties from `props`. +### ViewFactory.makeWith(props, children) +Creates an instance of a `View` and assigns initial properties from `props` and optionally a list of children. **Parameters** - `props`: simple key-value object, e.g. `{ header: "Header" }` + - `children`: optional array of children, e.g. `[ { element: "button", button: "right", text: "Back" } ]` + +## View +When you call `ViewFactory.make()` or `ViewFactory.makeWith()`, a `View` is instantiated. For example, in the example above, `views.loading`, `views.demos` and `views.empty` are views. + +
+ +### View.set(property, value) +Assign value to property by name. + +**Parameters** + - `property`: name of the property to change + - `value`: value to assign to the property + +
+ +### View.addChild(child) +Adds a child to the `View`. + +**Parameters** + - `child`: the child to add, e.g. `{ element: "button", button: "right", text: "Back" }` + +The format of the `child` parameter depends on the type of View that you're working with. Look in the View documentation. + +### View.resetChildren() +Removes all children from the `View`. + +### View.setChildren(children) +Removes all previous children from the `View` and assigns new children. + +**Parameters** + - `children`: the array of new children, e.g. `[ { element: "button", button: "right", text: "Back" } ]` diff --git a/documentation/js/js_gui__byte_input.md b/documentation/js/js_gui__byte_input.md new file mode 100644 index 000000000..abbf1ccad --- /dev/null +++ b/documentation/js/js_gui__byte_input.md @@ -0,0 +1,32 @@ +# Byte input GUI view {#js_gui__byte_input} + +Displays a hexadecimal keyboard. + +Sample screenshot of the view + +```js +let eventLoop = require("event_loop"); +let gui = require("gui"); +let byteInputView = require("gui/byte_input"); +``` + +This module depends on the `gui` module, which in turn depends on the +`event_loop` module, so they **must** be imported in this order. It is also +recommended to conceptualize these modules first before using this one. + +## Example +For an example refer to the `gui.js` example script. + +## View props + +| Prop | Type | Description | +|-------------|--------|--------------------------------------------------| +| `length` | `number` | The length in bytes of the buffer to modify. | +| `header` | `string` | A single line of text that appears above the keyboard. | +| `defaultData` | `string` | Data to show by default. | + +## View events + +| Item | Type | Description | +|-------------|--------|--------------------------------------------------| +| `input` | `ArrayBuffer` | Fires when the user selects the "Save" button. | diff --git a/documentation/js/js_gui__file_picker.md b/documentation/js/js_gui__file_picker.md new file mode 100644 index 000000000..a0854f0de --- /dev/null +++ b/documentation/js/js_gui__file_picker.md @@ -0,0 +1,20 @@ +# File Picker GUI prompt {#js_gui__file_picker} + +Allows asking the user to select a file. +It is not GUI view like other JS GUI views, rather just a function that shows a prompt. + +# Example +For an example, refer to the `gui.js` example script. + +# API reference + +## pickFile() +Displays a file picker and returns the selected file, or undefined if cancelled. + +**Parameters** + - `basePath`: the path to start at + - `extension`: the file extension(s) to show (like `.sub`, `.iso|.img`, `*`) + +**Returns** + +A `string` path, or `undefined`. diff --git a/documentation/js/js_gui__icon.md b/documentation/js/js_gui__icon.md new file mode 100644 index 000000000..2c6c4c5b7 --- /dev/null +++ b/documentation/js/js_gui__icon.md @@ -0,0 +1,32 @@ +# GUI Icons {#js_gui__icon} + +Retrieves and loads icons for use with GUI views such as [Dialog](#js_gui__dialog). + +# Example +For an example, refer to the `gui.js` example script. + +# API reference + +## getBuiltin() +Gets a built-in firmware icon by its name. +Not all icons are supported, currently only `"DolphinWait_59x54"` and `"js_script_10px"` are available. + +**Parameters** + - `icon`: name of the icon + +**Returns** + +An `IconData` object. + +
+ +## loadFxbm() +Loads a `.fxbm` icon (XBM Flipper sprite, from `flipperzero-game-engine`) from file. +It will be automatically unloaded when the script exits. + +**Parameters** + - `path`: path to the `.fxbm` file + +**Returns** + +An `IconData` object. diff --git a/documentation/js/js_gui__text_box.md b/documentation/js/js_gui__text_box.md index cdbeec7b7..4c94b8c3c 100644 --- a/documentation/js/js_gui__text_box.md +++ b/documentation/js/js_gui__text_box.md @@ -21,4 +21,6 @@ For an example, refer to the `gui.js` example script. | Prop | Type | Description | |----------|---------|------------------------------------| -| `text` | `string`| Text to show in the text box. | +| `text` | `string`| Text to show in the text box. | +| `font` | `string`| The font to display the text in (`"text"` or `"hex"`). | +| `focus` | `string`| The initial focus of the text box (`"start"` or `"end"`). | diff --git a/documentation/js/js_gui__text_input.md b/documentation/js/js_gui__text_input.md index 3b8aafaad..c63297c12 100644 --- a/documentation/js/js_gui__text_input.md +++ b/documentation/js/js_gui__text_input.md @@ -21,9 +21,11 @@ For an example, refer to the `gui.js` example script. | Prop | Type | Description | |-------------|--------|--------------------------------------------------| -| `minLength` | `number` | The shortest allowed text length. | -| `maxLength` | `number` | The longest allowed text length.
Default: `32` | -| `header` | `string` | A single line of text that appears above the keyboard. | +| `minLength` | `number` | The shortest allowed text length. | +| `maxLength` | `number` | The longest allowed text length.
Default: `32` | +| `header` | `string` | A single line of text that appears above the keyboard. | +| `defaultText` | `string` | Text to show by default. | +| `defaultTextClear` | `boolean` | Whether to clear the default text on next character typed. | ## View events diff --git a/documentation/js/js_gui__widget.md b/documentation/js/js_gui__widget.md index 2c31327d2..9ea3e4dfa 100644 --- a/documentation/js/js_gui__widget.md +++ b/documentation/js/js_gui__widget.md @@ -22,3 +22,16 @@ This view does not have any props. ## Children This view has the elements as its children. +Elements are objects with properties to define them, in the form `{ element: "type", ...properties }` (e.g. `{ element: "button", button: "right", text: "Back" }`). + +| **Element Type** | **Properties** | **Description** | +|------------------|----------------|------------------------------------------------| +| `string_multiline` | `x` (number), `y` (number)
`align` ((`"t"`, `"c"`, `"b"`) + (`"l"`, `"m"`, `"r"`))
`font` (`"primary"`, `"secondary"`, `"keyboard"`, `"big_numbers"`)
`text` (string) | String of text that can span multiple lines. | +| `string` | `x` (number), `y` (number)
`align` ((`"t"`, `"c"`, `"b"`) + (`"l"`, `"m"`, `"r"`))
`font` (`"primary"`, `"secondary"`, `"keyboard"`, `"big_numbers"`)
`text` (string) | String of text. | +| `text_box` | `x` (number), `y` (number)
`w` (number), `h` (number)
`align` ((`"t"`, `"c"`, `"b"`) + (`"l"`, `"m"`, `"r"`))
`text` (string)
`stripToDots` (boolean) | Box of with text that can be scrolled vertically. | +| `text_scroll` | `x` (number), `y` (number)
`w` (number), `h` (number)
`text` (string) | Text that can be scrolled vertically. | +| `button` | `text` (string)
`button` (`"left"`, `"center"`, `"right"`) | Button at the bottom of the screen. | +| `icon` | `x` (number), `y` (number)
`iconData` ([IconData](#js_gui__icon)) | Display an icon. | +| `rect` | `x` (number), `y` (number)
`w` (number), `h` (number)
`radius` (number), `fill` (boolean) | Draw a rectangle, optionally rounded and filled. | +| `circle` | `x` (number), `y` (number)
`radius` (number), `fill` (boolean) | Draw a circle, optionally filled. | +| `line` | `x1` (number), `y1` (number)
`x2` (number), `y2` (number) | Draw a line between 2 points. | diff --git a/documentation/js/js_math.md b/documentation/js/js_math.md index 78357bbd7..fd7bede17 100644 --- a/documentation/js/js_math.md +++ b/documentation/js/js_math.md @@ -271,13 +271,29 @@ math.floor(45.95); // 45
+## log() +Return the natural logarithm of x. + +**Parameters** +- x: A number + +**Returns** + +The natural logarithm of `x`, as in `ln(x)` where `e` is the base of the natural logarithm. + +**Example** +```js +math.log(1); // 0 +math.log(3); // 1.0986122886681098 +``` + ## isEqual() -Return true if the difference between numbers `a` and `b` is less than the specified parameter `e`. +Return true if the difference between numbers `a` and `b` is less than the specified `tolerance`. **Parameters** - a: A number a - b: A number b -- e: An epsilon parameter +- tolerance: How much difference is allowed between the numbers to be considered equal **Returns** diff --git a/documentation/js/js_serial.md b/documentation/js/js_serial.md index 01a72b97b..d06c799ac 100644 --- a/documentation/js/js_serial.md +++ b/documentation/js/js_serial.md @@ -10,8 +10,15 @@ Configure serial port. Should be called before all other methods. **Parameters** -- Serial port name (usart, lpuart) +- Serial port name (`"usart"`, `"lpuart"`) - Baudrate +- Optional framing configuration object (e.g. `{ dataBits: "8", parity: "even", stopBits: "1" }`): + - `dataBits`: `"6"`, `"7"`, `"8"`, `"9"` + - 6 data bits can only be selected when parity is enabled (even or odd) + - 9 data bits can only be selected when parity is disabled (none) + - `parity`: `"none"`, `"even"`, `"odd"` + - `stopBits`: `"0.5"`, `"1"`, `"1.5"`, `"2"` + - LPUART only supports whole stop bit lengths (i.e. 1 and 2 but not 0.5 and 1.5) **Example** @@ -69,7 +76,7 @@ Read from serial port until line break character. **Parameters** -*(optional)* Timeout value in ms. +- *(optional)* Timeout value in ms. **Returns** @@ -84,6 +91,26 @@ serial.readln(5000); // Read with 5s timeout
+## readAny() +Read any available amount of data from serial port, can help avoid starving your loop with small reads. + +**Parameters** + +- *(optional)* Timeout value in ms + +**Returns** + +A sting of received characters or undefined if nothing was received before timeout. + +**Example** + +```js +serial.readAny(); // Read without timeout +serial.readAny(5000); // Read with 5s timeout +``` + +
+ ## readBytes() Read from serial port until line break character. @@ -125,8 +152,21 @@ Index of matched pattern in input patterns list, undefined if nothing was found. ```js // Wait for root shell prompt with 1s timeout, returns 0 if it was received before timeout, undefined if not -serial.expect("# ", 1000); +serial.expect("# ", 1000); // Infinitely wait for one of two strings, should return 0 if the first string got matched, 1 if the second one serial.expect([": not found", "Usage: "]); +``` + +
+ +## end() +Deinitializes serial port, allowing multiple initializations per script run. + +**Example** + +```js +serial.end(); +// Configure LPUART port with baudrate = 115200 +serial.setup("lpuart", 115200); ``` \ No newline at end of file diff --git a/documentation/js/js_storage.md b/documentation/js/js_storage.md index acfa4e331..0e435be5b 100644 --- a/documentation/js/js_storage.md +++ b/documentation/js/js_storage.md @@ -52,7 +52,7 @@ File information structure. ## File -This class implements methods for working with file. To get an object of the File class, use the `OpenFile` method. +This class implements methods for working with file. To get an object of the File class, use the `openFile` method.
@@ -186,6 +186,36 @@ Copies bytes from the R/W pointer in the current file to the R/W pointer in anot # Methods +## openFile() + +Opens a file. + +**Parameters** + +- path: The path to the file +- accessMode: How to access the file (`"r"`, `"w"` or `"rw"`) +- openMode: How to open the file (`"open_existing"`, `"open_always"`, `"open_append"`, `"create_new"` or `"create_always"`) + +**Returns** + +A `File` object on success, `undefined` otherwise. + +
+ +## fileExists() + +Detects whether a file exists. + +**Parameters** + +- path: The path to the file + +**Returns** + +`true` if file exists, `false` otherwise. + +
+ ## arePathsEqual() Determines whether the two paths are equivalent. Respects filesystem-defined path equivalence rules. diff --git a/fbt_options.py b/fbt_options.py index a7705335a..191c4ef1a 100644 --- a/fbt_options.py +++ b/fbt_options.py @@ -76,6 +76,7 @@ FIRMWARE_APPS = { "radio_device_cc1101_ext", "unit_tests", "js_app", + "archive", ], } diff --git a/furi/core/core_defines.h b/furi/core/core_defines.h index fa56150ce..2d6ced025 100644 --- a/furi/core/core_defines.h +++ b/furi/core/core_defines.h @@ -41,6 +41,16 @@ extern "C" { #define CLAMP(x, upper, lower) (MIN(upper, MAX(x, lower))) #endif +#ifndef CLAMP_WRAPAROUND +#define CLAMP_WRAPAROUND(x, upper, lower) \ + ({ \ + __typeof__(x) _x = (x); \ + __typeof__(upper) _upper = (upper); \ + __typeof__(lower) _lower = (lower); \ + (_x > _upper) ? _lower : ((_x < _lower) ? _upper : _x); \ + }) +#endif + #ifndef COUNT_OF #define COUNT_OF(x) (sizeof(x) / sizeof(x[0])) #endif diff --git a/furi/core/event_loop.c b/furi/core/event_loop.c index e09be0ca4..800fea86c 100644 --- a/furi/core/event_loop.c +++ b/furi/core/event_loop.c @@ -78,6 +78,7 @@ void furi_event_loop_free(FuriEventLoop* instance) { furi_event_loop_process_timer_queue(instance); furi_check(TimerList_empty_p(instance->timer_list)); furi_check(WaitingList_empty_p(instance->waiting_list)); + furi_check(!instance->are_thread_flags_subscribed); FuriEventLoopTree_clear(instance->tree); PendingQueue_clear(instance->pending_queue); @@ -243,6 +244,10 @@ void furi_event_loop_run(FuriEventLoop* instance) { } else if(flags & FuriEventLoopFlagPending) { furi_event_loop_process_pending_callbacks(instance); + } else if(flags & FuriEventLoopFlagThreadFlag) { + if(instance->are_thread_flags_subscribed) + instance->thread_flags_callback(instance->thread_flags_callback_context); + } else { furi_crash(); } @@ -416,6 +421,24 @@ void furi_event_loop_subscribe_mutex( instance, mutex, &furi_mutex_event_loop_contract, event, callback, context); } +void furi_event_loop_subscribe_thread_flags( + FuriEventLoop* instance, + FuriEventLoopThreadFlagsCallback callback, + void* context) { + furi_check(instance); + furi_check(callback); + furi_check(!instance->are_thread_flags_subscribed); + instance->are_thread_flags_subscribed = true; + instance->thread_flags_callback = callback; + instance->thread_flags_callback_context = context; +} + +void furi_event_loop_unsubscribe_thread_flags(FuriEventLoop* instance) { + furi_check(instance); + furi_check(instance->are_thread_flags_subscribed); + instance->are_thread_flags_subscribed = false; +} + /** * Public generic unsubscription API */ @@ -538,6 +561,25 @@ static bool furi_event_loop_item_is_waiting(FuriEventLoopItem* instance) { return instance->WaitingList.prev || instance->WaitingList.next; } +void furi_event_loop_thread_flag_callback(FuriThreadId thread_id) { + TaskHandle_t hTask = (TaskHandle_t)thread_id; + BaseType_t yield; + + if(FURI_IS_IRQ_MODE()) { + yield = pdFALSE; + (void)xTaskNotifyIndexedFromISR( + hTask, + FURI_EVENT_LOOP_FLAG_NOTIFY_INDEX, + FuriEventLoopFlagThreadFlag, + eSetBits, + &yield); + portYIELD_FROM_ISR(yield); + } else { + (void)xTaskNotifyIndexed( + hTask, FURI_EVENT_LOOP_FLAG_NOTIFY_INDEX, FuriEventLoopFlagThreadFlag, eSetBits); + } +} + /* * Internal event loop link API, used by supported primitives */ diff --git a/furi/core/event_loop.h b/furi/core/event_loop.h index d5e8710a6..63f020f78 100644 --- a/furi/core/event_loop.h +++ b/furi/core/event_loop.h @@ -203,6 +203,12 @@ typedef void FuriEventLoopObject; */ typedef void (*FuriEventLoopEventCallback)(FuriEventLoopObject* object, void* context); +/** Callback type for event loop thread flag events + * + * @param context The context that was provided upon subscription + */ +typedef void (*FuriEventLoopThreadFlagsCallback)(void* context); + /** Opaque event flag type */ typedef struct FuriEventFlag FuriEventFlag; @@ -304,6 +310,23 @@ void furi_event_loop_subscribe_mutex( FuriEventLoopEventCallback callback, void* context); +/** Subscribe to thread flag events of the current thread + * + * @param instance The Event Loop instance + * @param callback The callback to call when a flag has been set + * @param context The context for callback + */ +void furi_event_loop_subscribe_thread_flags( + FuriEventLoop* instance, + FuriEventLoopThreadFlagsCallback callback, + void* context); + +/** Unsubscribe from thread flag events of the current thread + * + * @param instance The Event Loop instance + */ +void furi_event_loop_unsubscribe_thread_flags(FuriEventLoop* instance); + /** Unsubscribe from events (common) * * @param instance The Event Loop instance diff --git a/furi/core/event_loop_i.h b/furi/core/event_loop_i.h index ef2774b97..c2f04a359 100644 --- a/furi/core/event_loop_i.h +++ b/furi/core/event_loop_i.h @@ -4,12 +4,14 @@ #include "event_loop_link_i.h" #include "event_loop_timer_i.h" #include "event_loop_tick_i.h" +#include "event_loop_thread_flag_interface.h" #include #include #include #include "thread.h" +#include "thread_i.h" struct FuriEventLoopItem { // Source @@ -50,11 +52,12 @@ typedef enum { FuriEventLoopFlagStop = (1 << 1), FuriEventLoopFlagTimer = (1 << 2), FuriEventLoopFlagPending = (1 << 3), + FuriEventLoopFlagThreadFlag = (1 << 4), } FuriEventLoopFlag; #define FuriEventLoopFlagAll \ (FuriEventLoopFlagEvent | FuriEventLoopFlagStop | FuriEventLoopFlagTimer | \ - FuriEventLoopFlagPending) + FuriEventLoopFlagPending | FuriEventLoopFlagThreadFlag) typedef enum { FuriEventLoopProcessStatusComplete, @@ -94,4 +97,9 @@ struct FuriEventLoop { PendingQueue_t pending_queue; // Tick event FuriEventLoopTick tick; + + // Thread flags callback + bool are_thread_flags_subscribed; + FuriEventLoopThreadFlagsCallback thread_flags_callback; + void* thread_flags_callback_context; }; diff --git a/furi/core/event_loop_thread_flag_interface.h b/furi/core/event_loop_thread_flag_interface.h new file mode 100644 index 000000000..05fcd47de --- /dev/null +++ b/furi/core/event_loop_thread_flag_interface.h @@ -0,0 +1,10 @@ +#pragma once + +#include "thread.h" + +/** + * @brief Notify `FuriEventLoop` that `furi_thread_flags_set` has been called + * + * @param thread_id Thread id + */ +extern void furi_event_loop_thread_flag_callback(FuriThreadId thread_id); diff --git a/furi/core/memmgr_heap.c b/furi/core/memmgr_heap.c index c8a72bc8c..3ce0558a3 100644 --- a/furi/core/memmgr_heap.c +++ b/furi/core/memmgr_heap.c @@ -295,10 +295,12 @@ void memmgr_heap_printf_free_blocks(void) { //can be enabled once we can do printf with a locked scheduler //vTaskSuspendAll(); - pxBlock = xStart.pxNextFreeBlock; - while(pxBlock->pxNextFreeBlock != NULL) { + pxBlock = heapPROTECT_BLOCK_POINTER(xStart.pxNextFreeBlock); + heapVALIDATE_BLOCK_POINTER(pxBlock); + while(pxBlock->pxNextFreeBlock != heapPROTECT_BLOCK_POINTER(NULL)) { printf("A %p S %lu\r\n", (void*)pxBlock, (uint32_t)pxBlock->xBlockSize); - pxBlock = pxBlock->pxNextFreeBlock; + pxBlock = heapPROTECT_BLOCK_POINTER(pxBlock->pxNextFreeBlock); + heapVALIDATE_BLOCK_POINTER(pxBlock); } //xTaskResumeAll(); diff --git a/furi/core/thread.c b/furi/core/thread.c index d6132cdc2..e29a8711e 100644 --- a/furi/core/thread.c +++ b/furi/core/thread.c @@ -7,6 +7,7 @@ #include "check.h" #include "common_defines.h" #include "string.h" +#include "event_loop_thread_flag_interface.h" #include "log.h" #include @@ -501,6 +502,9 @@ uint32_t furi_thread_flags_set(FuriThreadId thread_id, uint32_t flags) { (void)xTaskNotifyAndQueryIndexed(hTask, THREAD_NOTIFY_INDEX, 0, eNoAction, &rflags); } } + + furi_event_loop_thread_flag_callback(thread_id); + /* Return flags after setting */ return rflags; } diff --git a/lib/SConscript b/lib/SConscript index fb0473f8d..4e6171aba 100644 --- a/lib/SConscript +++ b/lib/SConscript @@ -43,6 +43,7 @@ libs = env.BuildModules( "ble_profile", "bit_lib", "datetime", + "ieee754_parse_wrap", ], ) diff --git a/lib/ble_profile/extra_profiles/hid_profile.c b/lib/ble_profile/extra_profiles/hid_profile.c index f559a741a..ea90d3114 100644 --- a/lib/ble_profile/extra_profiles/hid_profile.c +++ b/lib/ble_profile/extra_profiles/hid_profile.c @@ -380,7 +380,10 @@ bool ble_profile_hid_mouse_scroll(FuriHalBleProfileBase* profile, int8_t delta) #define CONNECTION_INTERVAL_MAX (0x24) static GapConfig template_config = { - .adv_service_uuid = HUMAN_INTERFACE_DEVICE_SERVICE_UUID, + .adv_service = { + .UUID_Type = UUID_TYPE_16, + .Service_UUID_16 = HUMAN_INTERFACE_DEVICE_SERVICE_UUID, + }, .appearance_char = GAP_APPEARANCE_KEYBOARD, .bonding_mode = true, .pairing_method = GapPairingPinCodeVerifyYesNo, diff --git a/lib/drivers/bq25896.c b/lib/drivers/bq25896.c index 76aae5e82..a44ff8c39 100644 --- a/lib/drivers/bq25896.c +++ b/lib/drivers/bq25896.c @@ -35,7 +35,7 @@ typedef struct { static bq25896_regs_t bq25896_regs; -bool bq25896_init(FuriHalI2cBusHandle* handle) { +bool bq25896_init(const FuriHalI2cBusHandle* handle) { bool result = true; bq25896_regs.r14.REG_RST = 1; @@ -78,19 +78,19 @@ bool bq25896_init(FuriHalI2cBusHandle* handle) { return result; } -void bq25896_set_boost_lim(FuriHalI2cBusHandle* handle, BoostLim boost_lim) { +void bq25896_set_boost_lim(const FuriHalI2cBusHandle* handle, BoostLim boost_lim) { bq25896_regs.r0A.BOOST_LIM = boost_lim; furi_hal_i2c_write_reg_8( handle, BQ25896_ADDRESS, 0x0A, *(uint8_t*)&bq25896_regs.r0A, BQ25896_I2C_TIMEOUT); } -void bq25896_poweroff(FuriHalI2cBusHandle* handle) { +void bq25896_poweroff(const FuriHalI2cBusHandle* handle) { bq25896_regs.r09.BATFET_DIS = 1; furi_hal_i2c_write_reg_8( handle, BQ25896_ADDRESS, 0x09, *(uint8_t*)&bq25896_regs.r09, BQ25896_I2C_TIMEOUT); } -ChrgStat bq25896_get_charge_status(FuriHalI2cBusHandle* handle) { +ChrgStat bq25896_get_charge_status(const FuriHalI2cBusHandle* handle) { furi_hal_i2c_read_mem( handle, BQ25896_ADDRESS, @@ -103,52 +103,52 @@ ChrgStat bq25896_get_charge_status(FuriHalI2cBusHandle* handle) { return bq25896_regs.r0B.CHRG_STAT; } -bool bq25896_is_charging(FuriHalI2cBusHandle* handle) { +bool bq25896_is_charging(const FuriHalI2cBusHandle* handle) { // Include precharge, fast charging, and charging termination done as "charging" return bq25896_get_charge_status(handle) != ChrgStatNo; } -bool bq25896_is_charging_done(FuriHalI2cBusHandle* handle) { +bool bq25896_is_charging_done(const FuriHalI2cBusHandle* handle) { return bq25896_get_charge_status(handle) == ChrgStatDone; } -void bq25896_enable_charging(FuriHalI2cBusHandle* handle) { +void bq25896_enable_charging(const FuriHalI2cBusHandle* handle) { bq25896_regs.r03.CHG_CONFIG = 1; furi_hal_i2c_write_reg_8( handle, BQ25896_ADDRESS, 0x03, *(uint8_t*)&bq25896_regs.r03, BQ25896_I2C_TIMEOUT); } -void bq25896_disable_charging(FuriHalI2cBusHandle* handle) { +void bq25896_disable_charging(const FuriHalI2cBusHandle* handle) { bq25896_regs.r03.CHG_CONFIG = 0; furi_hal_i2c_write_reg_8( handle, BQ25896_ADDRESS, 0x03, *(uint8_t*)&bq25896_regs.r03, BQ25896_I2C_TIMEOUT); } -void bq25896_enable_otg(FuriHalI2cBusHandle* handle) { +void bq25896_enable_otg(const FuriHalI2cBusHandle* handle) { bq25896_regs.r03.OTG_CONFIG = 1; furi_hal_i2c_write_reg_8( handle, BQ25896_ADDRESS, 0x03, *(uint8_t*)&bq25896_regs.r03, BQ25896_I2C_TIMEOUT); } -void bq25896_disable_otg(FuriHalI2cBusHandle* handle) { +void bq25896_disable_otg(const FuriHalI2cBusHandle* handle) { bq25896_regs.r03.OTG_CONFIG = 0; furi_hal_i2c_write_reg_8( handle, BQ25896_ADDRESS, 0x03, *(uint8_t*)&bq25896_regs.r03, BQ25896_I2C_TIMEOUT); } -bool bq25896_is_otg_enabled(FuriHalI2cBusHandle* handle) { +bool bq25896_is_otg_enabled(const FuriHalI2cBusHandle* handle) { furi_hal_i2c_read_reg_8( handle, BQ25896_ADDRESS, 0x03, (uint8_t*)&bq25896_regs.r03, BQ25896_I2C_TIMEOUT); return bq25896_regs.r03.OTG_CONFIG; } -uint16_t bq25896_get_vreg_voltage(FuriHalI2cBusHandle* handle) { +uint16_t bq25896_get_vreg_voltage(const FuriHalI2cBusHandle* handle) { furi_hal_i2c_read_reg_8( handle, BQ25896_ADDRESS, 0x06, (uint8_t*)&bq25896_regs.r06, BQ25896_I2C_TIMEOUT); return (uint16_t)bq25896_regs.r06.VREG * 16 + 3840; } -void bq25896_set_vreg_voltage(FuriHalI2cBusHandle* handle, uint16_t vreg_voltage) { +void bq25896_set_vreg_voltage(const FuriHalI2cBusHandle* handle, uint16_t vreg_voltage) { if(vreg_voltage < 3840) { // Minimum valid value is 3840 mV vreg_voltage = 3840; @@ -166,13 +166,13 @@ void bq25896_set_vreg_voltage(FuriHalI2cBusHandle* handle, uint16_t vreg_voltage handle, BQ25896_ADDRESS, 0x06, *(uint8_t*)&bq25896_regs.r06, BQ25896_I2C_TIMEOUT); } -bool bq25896_check_otg_fault(FuriHalI2cBusHandle* handle) { +bool bq25896_check_otg_fault(const FuriHalI2cBusHandle* handle) { furi_hal_i2c_read_reg_8( handle, BQ25896_ADDRESS, 0x0C, (uint8_t*)&bq25896_regs.r0C, BQ25896_I2C_TIMEOUT); return bq25896_regs.r0C.BOOST_FAULT; } -uint16_t bq25896_get_vbus_voltage(FuriHalI2cBusHandle* handle) { +uint16_t bq25896_get_vbus_voltage(const FuriHalI2cBusHandle* handle) { furi_hal_i2c_read_reg_8( handle, BQ25896_ADDRESS, 0x11, (uint8_t*)&bq25896_regs.r11, BQ25896_I2C_TIMEOUT); if(bq25896_regs.r11.VBUS_GD) { @@ -182,25 +182,25 @@ uint16_t bq25896_get_vbus_voltage(FuriHalI2cBusHandle* handle) { } } -uint16_t bq25896_get_vsys_voltage(FuriHalI2cBusHandle* handle) { +uint16_t bq25896_get_vsys_voltage(const FuriHalI2cBusHandle* handle) { furi_hal_i2c_read_reg_8( handle, BQ25896_ADDRESS, 0x0F, (uint8_t*)&bq25896_regs.r0F, BQ25896_I2C_TIMEOUT); return (uint16_t)bq25896_regs.r0F.SYSV * 20 + 2304; } -uint16_t bq25896_get_vbat_voltage(FuriHalI2cBusHandle* handle) { +uint16_t bq25896_get_vbat_voltage(const FuriHalI2cBusHandle* handle) { furi_hal_i2c_read_reg_8( handle, BQ25896_ADDRESS, 0x0E, (uint8_t*)&bq25896_regs.r0E, BQ25896_I2C_TIMEOUT); return (uint16_t)bq25896_regs.r0E.BATV * 20 + 2304; } -uint16_t bq25896_get_vbat_current(FuriHalI2cBusHandle* handle) { +uint16_t bq25896_get_vbat_current(const FuriHalI2cBusHandle* handle) { furi_hal_i2c_read_reg_8( handle, BQ25896_ADDRESS, 0x12, (uint8_t*)&bq25896_regs.r12, BQ25896_I2C_TIMEOUT); return (uint16_t)bq25896_regs.r12.ICHGR * 50; } -uint32_t bq25896_get_ntc_mpct(FuriHalI2cBusHandle* handle) { +uint32_t bq25896_get_ntc_mpct(const FuriHalI2cBusHandle* handle) { furi_hal_i2c_read_reg_8( handle, BQ25896_ADDRESS, 0x10, (uint8_t*)&bq25896_regs.r10, BQ25896_I2C_TIMEOUT); return (uint32_t)bq25896_regs.r10.TSPCT * 465 + 21000; diff --git a/lib/drivers/bq25896.h b/lib/drivers/bq25896.h index d35625ab3..69c19868c 100644 --- a/lib/drivers/bq25896.h +++ b/lib/drivers/bq25896.h @@ -7,61 +7,61 @@ #include /** Initialize Driver */ -bool bq25896_init(FuriHalI2cBusHandle* handle); +bool bq25896_init(const FuriHalI2cBusHandle* handle); /** Set boost lim*/ -void bq25896_set_boost_lim(FuriHalI2cBusHandle* handle, BoostLim boost_lim); +void bq25896_set_boost_lim(const FuriHalI2cBusHandle* handle, BoostLim boost_lim); /** Send device into shipping mode */ -void bq25896_poweroff(FuriHalI2cBusHandle* handle); +void bq25896_poweroff(const FuriHalI2cBusHandle* handle); /** Get charging status */ -ChrgStat bq25896_get_charge_status(FuriHalI2cBusHandle* handle); +ChrgStat bq25896_get_charge_status(const FuriHalI2cBusHandle* handle); /** Is currently charging */ -bool bq25896_is_charging(FuriHalI2cBusHandle* handle); +bool bq25896_is_charging(const FuriHalI2cBusHandle* handle); /** Is charging completed while connected to charger */ -bool bq25896_is_charging_done(FuriHalI2cBusHandle* handle); +bool bq25896_is_charging_done(const FuriHalI2cBusHandle* handle); /** Enable charging */ -void bq25896_enable_charging(FuriHalI2cBusHandle* handle); +void bq25896_enable_charging(const FuriHalI2cBusHandle* handle); /** Disable charging */ -void bq25896_disable_charging(FuriHalI2cBusHandle* handle); +void bq25896_disable_charging(const FuriHalI2cBusHandle* handle); /** Enable otg */ -void bq25896_enable_otg(FuriHalI2cBusHandle* handle); +void bq25896_enable_otg(const FuriHalI2cBusHandle* handle); /** Disable otg */ -void bq25896_disable_otg(FuriHalI2cBusHandle* handle); +void bq25896_disable_otg(const FuriHalI2cBusHandle* handle); /** Is otg enabled */ -bool bq25896_is_otg_enabled(FuriHalI2cBusHandle* handle); +bool bq25896_is_otg_enabled(const FuriHalI2cBusHandle* handle); /** Get VREG (charging limit) voltage in mV */ -uint16_t bq25896_get_vreg_voltage(FuriHalI2cBusHandle* handle); +uint16_t bq25896_get_vreg_voltage(const FuriHalI2cBusHandle* handle); /** Set VREG (charging limit) voltage in mV * * Valid range: 3840mV - 4208mV, in steps of 16mV */ -void bq25896_set_vreg_voltage(FuriHalI2cBusHandle* handle, uint16_t vreg_voltage); +void bq25896_set_vreg_voltage(const FuriHalI2cBusHandle* handle, uint16_t vreg_voltage); /** Check OTG BOOST Fault status */ -bool bq25896_check_otg_fault(FuriHalI2cBusHandle* handle); +bool bq25896_check_otg_fault(const FuriHalI2cBusHandle* handle); /** Get VBUS Voltage in mV */ -uint16_t bq25896_get_vbus_voltage(FuriHalI2cBusHandle* handle); +uint16_t bq25896_get_vbus_voltage(const FuriHalI2cBusHandle* handle); /** Get VSYS Voltage in mV */ -uint16_t bq25896_get_vsys_voltage(FuriHalI2cBusHandle* handle); +uint16_t bq25896_get_vsys_voltage(const FuriHalI2cBusHandle* handle); /** Get VBAT Voltage in mV */ -uint16_t bq25896_get_vbat_voltage(FuriHalI2cBusHandle* handle); +uint16_t bq25896_get_vbat_voltage(const FuriHalI2cBusHandle* handle); /** Get VBAT current in mA */ -uint16_t bq25896_get_vbat_current(FuriHalI2cBusHandle* handle); +uint16_t bq25896_get_vbat_current(const FuriHalI2cBusHandle* handle); /** Get NTC voltage in mpct of REGN */ -uint32_t bq25896_get_ntc_mpct(FuriHalI2cBusHandle* handle); +uint32_t bq25896_get_ntc_mpct(const FuriHalI2cBusHandle* handle); diff --git a/lib/drivers/bq27220.c b/lib/drivers/bq27220.c index d60e287da..127f7c6b9 100644 --- a/lib/drivers/bq27220.c +++ b/lib/drivers/bq27220.c @@ -43,7 +43,7 @@ #endif static inline bool bq27220_read_reg( - FuriHalI2cBusHandle* handle, + const FuriHalI2cBusHandle* handle, uint8_t address, uint8_t* buffer, size_t buffer_size) { @@ -52,7 +52,7 @@ static inline bool bq27220_read_reg( } static inline bool bq27220_write( - FuriHalI2cBusHandle* handle, + const FuriHalI2cBusHandle* handle, uint8_t address, const uint8_t* buffer, size_t buffer_size) { @@ -60,11 +60,11 @@ static inline bool bq27220_write( handle, BQ27220_ADDRESS, address, buffer, buffer_size, BQ27220_I2C_TIMEOUT); } -static inline bool bq27220_control(FuriHalI2cBusHandle* handle, uint16_t control) { +static inline bool bq27220_control(const FuriHalI2cBusHandle* handle, uint16_t control) { return bq27220_write(handle, CommandControl, (uint8_t*)&control, 2); } -static uint16_t bq27220_read_word(FuriHalI2cBusHandle* handle, uint8_t address) { +static uint16_t bq27220_read_word(const FuriHalI2cBusHandle* handle, uint8_t address) { uint16_t buf = BQ27220_ERROR; if(!bq27220_read_reg(handle, address, (uint8_t*)&buf, 2)) { @@ -83,7 +83,7 @@ static uint8_t bq27220_get_checksum(uint8_t* data, uint16_t len) { } static bool bq27220_parameter_check( - FuriHalI2cBusHandle* handle, + const FuriHalI2cBusHandle* handle, uint16_t address, uint32_t value, size_t size, @@ -163,7 +163,7 @@ static bool bq27220_parameter_check( } static bool bq27220_data_memory_check( - FuriHalI2cBusHandle* handle, + const FuriHalI2cBusHandle* handle, const BQ27220DMData* data_memory, bool update) { if(update) { @@ -268,7 +268,7 @@ static bool bq27220_data_memory_check( return result; } -bool bq27220_init(FuriHalI2cBusHandle* handle, const BQ27220DMData* data_memory) { +bool bq27220_init(const FuriHalI2cBusHandle* handle, const BQ27220DMData* data_memory) { bool result = false; bool reset_and_provisioning_required = false; @@ -365,7 +365,7 @@ bool bq27220_init(FuriHalI2cBusHandle* handle, const BQ27220DMData* data_memory) return result; } -bool bq27220_reset(FuriHalI2cBusHandle* handle) { +bool bq27220_reset(const FuriHalI2cBusHandle* handle) { bool result = false; do { if(!bq27220_control(handle, Control_RESET)) { @@ -396,7 +396,7 @@ bool bq27220_reset(FuriHalI2cBusHandle* handle) { return result; } -bool bq27220_seal(FuriHalI2cBusHandle* handle) { +bool bq27220_seal(const FuriHalI2cBusHandle* handle) { Bq27220OperationStatus operation_status = {0}; bool result = false; do { @@ -431,7 +431,7 @@ bool bq27220_seal(FuriHalI2cBusHandle* handle) { return result; } -bool bq27220_unseal(FuriHalI2cBusHandle* handle) { +bool bq27220_unseal(const FuriHalI2cBusHandle* handle) { Bq27220OperationStatus operation_status = {0}; bool result = false; do { @@ -465,7 +465,7 @@ bool bq27220_unseal(FuriHalI2cBusHandle* handle) { return result; } -bool bq27220_full_access(FuriHalI2cBusHandle* handle) { +bool bq27220_full_access(const FuriHalI2cBusHandle* handle) { bool result = false; do { @@ -518,29 +518,35 @@ bool bq27220_full_access(FuriHalI2cBusHandle* handle) { return result; } -uint16_t bq27220_get_voltage(FuriHalI2cBusHandle* handle) { +uint16_t bq27220_get_voltage(const FuriHalI2cBusHandle* handle) { return bq27220_read_word(handle, CommandVoltage); } -int16_t bq27220_get_current(FuriHalI2cBusHandle* handle) { +int16_t bq27220_get_current(const FuriHalI2cBusHandle* handle) { return bq27220_read_word(handle, CommandCurrent); } -bool bq27220_get_control_status(FuriHalI2cBusHandle* handle, Bq27220ControlStatus* control_status) { +bool bq27220_get_control_status( + const FuriHalI2cBusHandle* handle, + Bq27220ControlStatus* control_status) { return bq27220_read_reg(handle, CommandControl, (uint8_t*)control_status, 2); } -bool bq27220_get_battery_status(FuriHalI2cBusHandle* handle, Bq27220BatteryStatus* battery_status) { +bool bq27220_get_battery_status( + const FuriHalI2cBusHandle* handle, + Bq27220BatteryStatus* battery_status) { return bq27220_read_reg(handle, CommandBatteryStatus, (uint8_t*)battery_status, 2); } bool bq27220_get_operation_status( - FuriHalI2cBusHandle* handle, + const FuriHalI2cBusHandle* handle, Bq27220OperationStatus* operation_status) { return bq27220_read_reg(handle, CommandOperationStatus, (uint8_t*)operation_status, 2); } -bool bq27220_get_gauging_status(FuriHalI2cBusHandle* handle, Bq27220GaugingStatus* gauging_status) { +bool bq27220_get_gauging_status( + const FuriHalI2cBusHandle* handle, + Bq27220GaugingStatus* gauging_status) { // Request gauging data to be loaded to MAC if(!bq27220_control(handle, Control_GAUGING_STATUS)) { FURI_LOG_E(TAG, "DM SelectSubclass for read failed"); @@ -552,26 +558,26 @@ bool bq27220_get_gauging_status(FuriHalI2cBusHandle* handle, Bq27220GaugingStatu return bq27220_read_reg(handle, CommandMACData, (uint8_t*)gauging_status, 2); } -uint16_t bq27220_get_temperature(FuriHalI2cBusHandle* handle) { +uint16_t bq27220_get_temperature(const FuriHalI2cBusHandle* handle) { return bq27220_read_word(handle, CommandTemperature); } -uint16_t bq27220_get_full_charge_capacity(FuriHalI2cBusHandle* handle) { +uint16_t bq27220_get_full_charge_capacity(const FuriHalI2cBusHandle* handle) { return bq27220_read_word(handle, CommandFullChargeCapacity); } -uint16_t bq27220_get_design_capacity(FuriHalI2cBusHandle* handle) { +uint16_t bq27220_get_design_capacity(const FuriHalI2cBusHandle* handle) { return bq27220_read_word(handle, CommandDesignCapacity); } -uint16_t bq27220_get_remaining_capacity(FuriHalI2cBusHandle* handle) { +uint16_t bq27220_get_remaining_capacity(const FuriHalI2cBusHandle* handle) { return bq27220_read_word(handle, CommandRemainingCapacity); } -uint16_t bq27220_get_state_of_charge(FuriHalI2cBusHandle* handle) { +uint16_t bq27220_get_state_of_charge(const FuriHalI2cBusHandle* handle) { return bq27220_read_word(handle, CommandStateOfCharge); } -uint16_t bq27220_get_state_of_health(FuriHalI2cBusHandle* handle) { +uint16_t bq27220_get_state_of_health(const FuriHalI2cBusHandle* handle) { return bq27220_read_word(handle, CommandStateOfHealth); } diff --git a/lib/drivers/bq27220.h b/lib/drivers/bq27220.h index cdfcb20b1..addbf08f5 100644 --- a/lib/drivers/bq27220.h +++ b/lib/drivers/bq27220.h @@ -136,7 +136,7 @@ typedef struct BQ27220DMData BQ27220DMData; * * @return true on success, false otherwise */ -bool bq27220_init(FuriHalI2cBusHandle* handle, const BQ27220DMData* data_memory); +bool bq27220_init(const FuriHalI2cBusHandle* handle, const BQ27220DMData* data_memory); /** Reset gauge * @@ -144,7 +144,7 @@ bool bq27220_init(FuriHalI2cBusHandle* handle, const BQ27220DMData* data_memory) * * @return true on success, false otherwise */ -bool bq27220_reset(FuriHalI2cBusHandle* handle); +bool bq27220_reset(const FuriHalI2cBusHandle* handle); /** Seal gauge access * @@ -152,7 +152,7 @@ bool bq27220_reset(FuriHalI2cBusHandle* handle); * * @return true on success, false otherwise */ -bool bq27220_seal(FuriHalI2cBusHandle* handle); +bool bq27220_seal(const FuriHalI2cBusHandle* handle); /** Unseal gauge access * @@ -160,7 +160,7 @@ bool bq27220_seal(FuriHalI2cBusHandle* handle); * * @return true on success, false otherwise */ -bool bq27220_unseal(FuriHalI2cBusHandle* handle); +bool bq27220_unseal(const FuriHalI2cBusHandle* handle); /** Get full access * @@ -170,7 +170,7 @@ bool bq27220_unseal(FuriHalI2cBusHandle* handle); * * @return true on success, false otherwise */ -bool bq27220_full_access(FuriHalI2cBusHandle* handle); +bool bq27220_full_access(const FuriHalI2cBusHandle* handle); /** Get battery voltage * @@ -178,7 +178,7 @@ bool bq27220_full_access(FuriHalI2cBusHandle* handle); * * @return voltage in mV or BQ27220_ERROR */ -uint16_t bq27220_get_voltage(FuriHalI2cBusHandle* handle); +uint16_t bq27220_get_voltage(const FuriHalI2cBusHandle* handle); /** Get current * @@ -186,7 +186,7 @@ uint16_t bq27220_get_voltage(FuriHalI2cBusHandle* handle); * * @return current in mA or BQ27220_ERROR */ -int16_t bq27220_get_current(FuriHalI2cBusHandle* handle); +int16_t bq27220_get_current(const FuriHalI2cBusHandle* handle); /** Get control status * @@ -195,7 +195,9 @@ int16_t bq27220_get_current(FuriHalI2cBusHandle* handle); * * @return true on success, false otherwise */ -bool bq27220_get_control_status(FuriHalI2cBusHandle* handle, Bq27220ControlStatus* control_status); +bool bq27220_get_control_status( + const FuriHalI2cBusHandle* handle, + Bq27220ControlStatus* control_status); /** Get battery status * @@ -204,7 +206,9 @@ bool bq27220_get_control_status(FuriHalI2cBusHandle* handle, Bq27220ControlStatu * * @return true on success, false otherwise */ -bool bq27220_get_battery_status(FuriHalI2cBusHandle* handle, Bq27220BatteryStatus* battery_status); +bool bq27220_get_battery_status( + const FuriHalI2cBusHandle* handle, + Bq27220BatteryStatus* battery_status); /** Get operation status * @@ -214,7 +218,7 @@ bool bq27220_get_battery_status(FuriHalI2cBusHandle* handle, Bq27220BatteryStatu * @return true on success, false otherwise */ bool bq27220_get_operation_status( - FuriHalI2cBusHandle* handle, + const FuriHalI2cBusHandle* handle, Bq27220OperationStatus* operation_status); /** Get gauging status @@ -224,7 +228,9 @@ bool bq27220_get_operation_status( * * @return true on success, false otherwise */ -bool bq27220_get_gauging_status(FuriHalI2cBusHandle* handle, Bq27220GaugingStatus* gauging_status); +bool bq27220_get_gauging_status( + const FuriHalI2cBusHandle* handle, + Bq27220GaugingStatus* gauging_status); /** Get temperature * @@ -232,7 +238,7 @@ bool bq27220_get_gauging_status(FuriHalI2cBusHandle* handle, Bq27220GaugingStatu * * @return temperature in units of 0.1°K */ -uint16_t bq27220_get_temperature(FuriHalI2cBusHandle* handle); +uint16_t bq27220_get_temperature(const FuriHalI2cBusHandle* handle); /** Get compensated full charge capacity * @@ -240,7 +246,7 @@ uint16_t bq27220_get_temperature(FuriHalI2cBusHandle* handle); * * @return full charge capacity in mAh or BQ27220_ERROR */ -uint16_t bq27220_get_full_charge_capacity(FuriHalI2cBusHandle* handle); +uint16_t bq27220_get_full_charge_capacity(const FuriHalI2cBusHandle* handle); /** Get design capacity * @@ -248,7 +254,7 @@ uint16_t bq27220_get_full_charge_capacity(FuriHalI2cBusHandle* handle); * * @return design capacity in mAh or BQ27220_ERROR */ -uint16_t bq27220_get_design_capacity(FuriHalI2cBusHandle* handle); +uint16_t bq27220_get_design_capacity(const FuriHalI2cBusHandle* handle); /** Get remaining capacity * @@ -256,7 +262,7 @@ uint16_t bq27220_get_design_capacity(FuriHalI2cBusHandle* handle); * * @return remaining capacity in mAh or BQ27220_ERROR */ -uint16_t bq27220_get_remaining_capacity(FuriHalI2cBusHandle* handle); +uint16_t bq27220_get_remaining_capacity(const FuriHalI2cBusHandle* handle); /** Get predicted remaining battery capacity * @@ -264,7 +270,7 @@ uint16_t bq27220_get_remaining_capacity(FuriHalI2cBusHandle* handle); * * @return state of charge in percents or BQ27220_ERROR */ -uint16_t bq27220_get_state_of_charge(FuriHalI2cBusHandle* handle); +uint16_t bq27220_get_state_of_charge(const FuriHalI2cBusHandle* handle); /** Get ratio of full charge capacity over design capacity * @@ -272,4 +278,4 @@ uint16_t bq27220_get_state_of_charge(FuriHalI2cBusHandle* handle); * * @return state of health in percents or BQ27220_ERROR */ -uint16_t bq27220_get_state_of_health(FuriHalI2cBusHandle* handle); +uint16_t bq27220_get_state_of_health(const FuriHalI2cBusHandle* handle); diff --git a/lib/drivers/cc1101.c b/lib/drivers/cc1101.c index 40b286a9b..ff2f0d610 100644 --- a/lib/drivers/cc1101.c +++ b/lib/drivers/cc1101.c @@ -3,7 +3,8 @@ #include #include -static bool cc1101_spi_trx(FuriHalSpiBusHandle* handle, uint8_t* tx, uint8_t* rx, uint8_t size) { +static bool + cc1101_spi_trx(const FuriHalSpiBusHandle* handle, uint8_t* tx, uint8_t* rx, uint8_t size) { FuriHalCortexTimer timer = furi_hal_cortex_timer_get(CC1101_TIMEOUT * 1000); while(furi_hal_gpio_read(handle->miso)) { @@ -16,7 +17,7 @@ static bool cc1101_spi_trx(FuriHalSpiBusHandle* handle, uint8_t* tx, uint8_t* rx return true; } -CC1101Status cc1101_strobe(FuriHalSpiBusHandle* handle, uint8_t strobe) { +CC1101Status cc1101_strobe(const FuriHalSpiBusHandle* handle, uint8_t strobe) { uint8_t tx[1] = {strobe}; CC1101Status rx[1] = {0}; rx[0].CHIP_RDYn = 1; @@ -27,7 +28,7 @@ CC1101Status cc1101_strobe(FuriHalSpiBusHandle* handle, uint8_t strobe) { return rx[0]; } -CC1101Status cc1101_write_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t data) { +CC1101Status cc1101_write_reg(const FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t data) { uint8_t tx[2] = {reg, data}; CC1101Status rx[2] = {0}; rx[0].CHIP_RDYn = 1; @@ -39,7 +40,7 @@ CC1101Status cc1101_write_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t return rx[1]; } -CC1101Status cc1101_read_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* data) { +CC1101Status cc1101_read_reg(const FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* data) { assert(sizeof(CC1101Status) == 1); uint8_t tx[2] = {reg | CC1101_READ, 0}; CC1101Status rx[2] = {0}; @@ -52,33 +53,36 @@ CC1101Status cc1101_read_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* return rx[0]; } -uint8_t cc1101_get_partnumber(FuriHalSpiBusHandle* handle) { +uint8_t cc1101_get_partnumber(const FuriHalSpiBusHandle* handle) { uint8_t partnumber = 0; cc1101_read_reg(handle, CC1101_STATUS_PARTNUM | CC1101_BURST, &partnumber); return partnumber; } -uint8_t cc1101_get_version(FuriHalSpiBusHandle* handle) { +uint8_t cc1101_get_version(const FuriHalSpiBusHandle* handle) { uint8_t version = 0; cc1101_read_reg(handle, CC1101_STATUS_VERSION | CC1101_BURST, &version); return version; } -uint8_t cc1101_get_rssi(FuriHalSpiBusHandle* handle) { +uint8_t cc1101_get_rssi(const FuriHalSpiBusHandle* handle) { uint8_t rssi = 0; cc1101_read_reg(handle, CC1101_STATUS_RSSI | CC1101_BURST, &rssi); return rssi; } -CC1101Status cc1101_reset(FuriHalSpiBusHandle* handle) { +CC1101Status cc1101_reset(const FuriHalSpiBusHandle* handle) { return cc1101_strobe(handle, CC1101_STROBE_SRES); } -CC1101Status cc1101_get_status(FuriHalSpiBusHandle* handle) { +CC1101Status cc1101_get_status(const FuriHalSpiBusHandle* handle) { return cc1101_strobe(handle, CC1101_STROBE_SNOP); } -bool cc1101_wait_status_state(FuriHalSpiBusHandle* handle, CC1101State state, uint32_t timeout_us) { +bool cc1101_wait_status_state( + const FuriHalSpiBusHandle* handle, + CC1101State state, + uint32_t timeout_us) { bool result = false; CC1101Status status = {0}; FuriHalCortexTimer timer = furi_hal_cortex_timer_get(timeout_us); @@ -92,35 +96,35 @@ bool cc1101_wait_status_state(FuriHalSpiBusHandle* handle, CC1101State state, ui return result; } -CC1101Status cc1101_shutdown(FuriHalSpiBusHandle* handle) { +CC1101Status cc1101_shutdown(const FuriHalSpiBusHandle* handle) { return cc1101_strobe(handle, CC1101_STROBE_SPWD); } -CC1101Status cc1101_calibrate(FuriHalSpiBusHandle* handle) { +CC1101Status cc1101_calibrate(const FuriHalSpiBusHandle* handle) { return cc1101_strobe(handle, CC1101_STROBE_SCAL); } -CC1101Status cc1101_switch_to_idle(FuriHalSpiBusHandle* handle) { +CC1101Status cc1101_switch_to_idle(const FuriHalSpiBusHandle* handle) { return cc1101_strobe(handle, CC1101_STROBE_SIDLE); } -CC1101Status cc1101_switch_to_rx(FuriHalSpiBusHandle* handle) { +CC1101Status cc1101_switch_to_rx(const FuriHalSpiBusHandle* handle) { return cc1101_strobe(handle, CC1101_STROBE_SRX); } -CC1101Status cc1101_switch_to_tx(FuriHalSpiBusHandle* handle) { +CC1101Status cc1101_switch_to_tx(const FuriHalSpiBusHandle* handle) { return cc1101_strobe(handle, CC1101_STROBE_STX); } -CC1101Status cc1101_flush_rx(FuriHalSpiBusHandle* handle) { +CC1101Status cc1101_flush_rx(const FuriHalSpiBusHandle* handle) { return cc1101_strobe(handle, CC1101_STROBE_SFRX); } -CC1101Status cc1101_flush_tx(FuriHalSpiBusHandle* handle) { +CC1101Status cc1101_flush_tx(const FuriHalSpiBusHandle* handle) { return cc1101_strobe(handle, CC1101_STROBE_SFTX); } -uint32_t cc1101_set_frequency(FuriHalSpiBusHandle* handle, uint32_t value) { +uint32_t cc1101_set_frequency(const FuriHalSpiBusHandle* handle, uint32_t value) { uint64_t real_value = (uint64_t)value * CC1101_FDIV / CC1101_QUARTZ; // Sanity check @@ -135,7 +139,7 @@ uint32_t cc1101_set_frequency(FuriHalSpiBusHandle* handle, uint32_t value) { return (uint32_t)real_frequency; } -uint32_t cc1101_set_intermediate_frequency(FuriHalSpiBusHandle* handle, uint32_t value) { +uint32_t cc1101_set_intermediate_frequency(const FuriHalSpiBusHandle* handle, uint32_t value) { uint64_t real_value = value * CC1101_IFDIV / CC1101_QUARTZ; assert((real_value & 0xFF) == real_value); @@ -146,7 +150,7 @@ uint32_t cc1101_set_intermediate_frequency(FuriHalSpiBusHandle* handle, uint32_t return (uint32_t)real_frequency; } -void cc1101_set_pa_table(FuriHalSpiBusHandle* handle, const uint8_t value[8]) { +void cc1101_set_pa_table(const FuriHalSpiBusHandle* handle, const uint8_t value[8]) { uint8_t tx[9] = {CC1101_PATABLE | CC1101_BURST}; //-V1009 CC1101Status rx[9] = {0}; rx[0].CHIP_RDYn = 1; @@ -159,7 +163,7 @@ void cc1101_set_pa_table(FuriHalSpiBusHandle* handle, const uint8_t value[8]) { assert((rx[0].CHIP_RDYn | rx[8].CHIP_RDYn) == 0); } -uint8_t cc1101_write_fifo(FuriHalSpiBusHandle* handle, const uint8_t* data, uint8_t size) { +uint8_t cc1101_write_fifo(const FuriHalSpiBusHandle* handle, const uint8_t* data, uint8_t size) { uint8_t buff_tx[64]; uint8_t buff_rx[64]; buff_tx[0] = CC1101_FIFO | CC1101_BURST; @@ -170,7 +174,7 @@ uint8_t cc1101_write_fifo(FuriHalSpiBusHandle* handle, const uint8_t* data, uint return size; } -uint8_t cc1101_read_fifo(FuriHalSpiBusHandle* handle, uint8_t* data, uint8_t* size) { +uint8_t cc1101_read_fifo(const FuriHalSpiBusHandle* handle, uint8_t* data, uint8_t* size) { uint8_t buff_trx[2]; buff_trx[0] = CC1101_FIFO | CC1101_READ | CC1101_BURST; diff --git a/lib/drivers/cc1101.h b/lib/drivers/cc1101.h index c8c552bec..2828f1cdf 100644 --- a/lib/drivers/cc1101.h +++ b/lib/drivers/cc1101.h @@ -19,7 +19,7 @@ extern "C" { * * @return device status */ -CC1101Status cc1101_strobe(FuriHalSpiBusHandle* handle, uint8_t strobe); +CC1101Status cc1101_strobe(const FuriHalSpiBusHandle* handle, uint8_t strobe); /** Write device register * @@ -29,7 +29,7 @@ CC1101Status cc1101_strobe(FuriHalSpiBusHandle* handle, uint8_t strobe); * * @return device status */ -CC1101Status cc1101_write_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t data); +CC1101Status cc1101_write_reg(const FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t data); /** Read device register * @@ -39,7 +39,7 @@ CC1101Status cc1101_write_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t * * @return device status */ -CC1101Status cc1101_read_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* data); +CC1101Status cc1101_read_reg(const FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* data); /* High level API */ @@ -49,7 +49,7 @@ CC1101Status cc1101_read_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* * * @return CC1101Status structure */ -CC1101Status cc1101_reset(FuriHalSpiBusHandle* handle); +CC1101Status cc1101_reset(const FuriHalSpiBusHandle* handle); /** Get status * @@ -57,7 +57,7 @@ CC1101Status cc1101_reset(FuriHalSpiBusHandle* handle); * * @return CC1101Status structure */ -CC1101Status cc1101_get_status(FuriHalSpiBusHandle* handle); +CC1101Status cc1101_get_status(const FuriHalSpiBusHandle* handle); /** Wait specific chip state * @@ -67,7 +67,10 @@ CC1101Status cc1101_get_status(FuriHalSpiBusHandle* handle); * * @return true on success, false otherwise */ -bool cc1101_wait_status_state(FuriHalSpiBusHandle* handle, CC1101State state, uint32_t timeout_us); +bool cc1101_wait_status_state( + const FuriHalSpiBusHandle* handle, + CC1101State state, + uint32_t timeout_us); /** Enable shutdown mode * @@ -75,7 +78,7 @@ bool cc1101_wait_status_state(FuriHalSpiBusHandle* handle, CC1101State state, ui * * @return CC1101Status structure */ -CC1101Status cc1101_shutdown(FuriHalSpiBusHandle* handle); +CC1101Status cc1101_shutdown(const FuriHalSpiBusHandle* handle); /** Get Partnumber * @@ -83,7 +86,7 @@ CC1101Status cc1101_shutdown(FuriHalSpiBusHandle* handle); * * @return part number id */ -uint8_t cc1101_get_partnumber(FuriHalSpiBusHandle* handle); +uint8_t cc1101_get_partnumber(const FuriHalSpiBusHandle* handle); /** Get Version * @@ -91,7 +94,7 @@ uint8_t cc1101_get_partnumber(FuriHalSpiBusHandle* handle); * * @return version */ -uint8_t cc1101_get_version(FuriHalSpiBusHandle* handle); +uint8_t cc1101_get_version(const FuriHalSpiBusHandle* handle); /** Get raw RSSI value * @@ -99,7 +102,7 @@ uint8_t cc1101_get_version(FuriHalSpiBusHandle* handle); * * @return rssi value */ -uint8_t cc1101_get_rssi(FuriHalSpiBusHandle* handle); +uint8_t cc1101_get_rssi(const FuriHalSpiBusHandle* handle); /** Calibrate oscillator * @@ -107,13 +110,13 @@ uint8_t cc1101_get_rssi(FuriHalSpiBusHandle* handle); * * @return CC1101Status structure */ -CC1101Status cc1101_calibrate(FuriHalSpiBusHandle* handle); +CC1101Status cc1101_calibrate(const FuriHalSpiBusHandle* handle); /** Switch to idle * * @param handle - pointer to FuriHalSpiHandle */ -CC1101Status cc1101_switch_to_idle(FuriHalSpiBusHandle* handle); +CC1101Status cc1101_switch_to_idle(const FuriHalSpiBusHandle* handle); /** Switch to RX * @@ -121,7 +124,7 @@ CC1101Status cc1101_switch_to_idle(FuriHalSpiBusHandle* handle); * * @return CC1101Status structure */ -CC1101Status cc1101_switch_to_rx(FuriHalSpiBusHandle* handle); +CC1101Status cc1101_switch_to_rx(const FuriHalSpiBusHandle* handle); /** Switch to TX * @@ -129,7 +132,7 @@ CC1101Status cc1101_switch_to_rx(FuriHalSpiBusHandle* handle); * * @return CC1101Status structure */ -CC1101Status cc1101_switch_to_tx(FuriHalSpiBusHandle* handle); +CC1101Status cc1101_switch_to_tx(const FuriHalSpiBusHandle* handle); /** Flush RX FIFO * @@ -137,13 +140,13 @@ CC1101Status cc1101_switch_to_tx(FuriHalSpiBusHandle* handle); * * @return CC1101Status structure */ -CC1101Status cc1101_flush_rx(FuriHalSpiBusHandle* handle); +CC1101Status cc1101_flush_rx(const FuriHalSpiBusHandle* handle); /** Flush TX FIFO * * @param handle - pointer to FuriHalSpiHandle */ -CC1101Status cc1101_flush_tx(FuriHalSpiBusHandle* handle); +CC1101Status cc1101_flush_tx(const FuriHalSpiBusHandle* handle); /** Set Frequency * @@ -152,7 +155,7 @@ CC1101Status cc1101_flush_tx(FuriHalSpiBusHandle* handle); * * @return real frequency that were synthesized */ -uint32_t cc1101_set_frequency(FuriHalSpiBusHandle* handle, uint32_t value); +uint32_t cc1101_set_frequency(const FuriHalSpiBusHandle* handle, uint32_t value); /** Set Intermediate Frequency * @@ -161,14 +164,14 @@ uint32_t cc1101_set_frequency(FuriHalSpiBusHandle* handle, uint32_t value); * * @return real inermediate frequency that were synthesized */ -uint32_t cc1101_set_intermediate_frequency(FuriHalSpiBusHandle* handle, uint32_t value); +uint32_t cc1101_set_intermediate_frequency(const FuriHalSpiBusHandle* handle, uint32_t value); /** Set Power Amplifier level table, ramp * * @param handle - pointer to FuriHalSpiHandle * @param value - array of power level values */ -void cc1101_set_pa_table(FuriHalSpiBusHandle* handle, const uint8_t value[8]); +void cc1101_set_pa_table(const FuriHalSpiBusHandle* handle, const uint8_t value[8]); /** Write FIFO * @@ -178,7 +181,7 @@ void cc1101_set_pa_table(FuriHalSpiBusHandle* handle, const uint8_t value[8]); * * @return size, written bytes count */ -uint8_t cc1101_write_fifo(FuriHalSpiBusHandle* handle, const uint8_t* data, uint8_t size); +uint8_t cc1101_write_fifo(const FuriHalSpiBusHandle* handle, const uint8_t* data, uint8_t size); /** Read FIFO * @@ -188,7 +191,7 @@ uint8_t cc1101_write_fifo(FuriHalSpiBusHandle* handle, const uint8_t* data, uint * * @return size, read bytes count */ -uint8_t cc1101_read_fifo(FuriHalSpiBusHandle* handle, uint8_t* data, uint8_t* size); +uint8_t cc1101_read_fifo(const FuriHalSpiBusHandle* handle, uint8_t* data, uint8_t* size); #ifdef __cplusplus } diff --git a/lib/drivers/lp5562.c b/lib/drivers/lp5562.c index 30a5b559a..7db9bbce4 100644 --- a/lib/drivers/lp5562.c +++ b/lib/drivers/lp5562.c @@ -3,12 +3,12 @@ #include "lp5562_reg.h" #include -void lp5562_reset(FuriHalI2cBusHandle* handle) { +void lp5562_reset(const FuriHalI2cBusHandle* handle) { Reg0D_Reset reg = {.value = 0xFF}; furi_hal_i2c_write_reg_8(handle, LP5562_ADDRESS, 0x0D, *(uint8_t*)®, LP5562_I2C_TIMEOUT); } -void lp5562_configure(FuriHalI2cBusHandle* handle) { +void lp5562_configure(const FuriHalI2cBusHandle* handle) { Reg08_Config config = {.INT_CLK_EN = true, .PS_EN = true, .PWM_HF = true}; furi_hal_i2c_write_reg_8(handle, LP5562_ADDRESS, 0x08, *(uint8_t*)&config, LP5562_I2C_TIMEOUT); @@ -21,14 +21,17 @@ void lp5562_configure(FuriHalI2cBusHandle* handle) { furi_hal_i2c_write_reg_8(handle, LP5562_ADDRESS, 0x70, *(uint8_t*)&map, LP5562_I2C_TIMEOUT); } -void lp5562_enable(FuriHalI2cBusHandle* handle) { +void lp5562_enable(const FuriHalI2cBusHandle* handle) { Reg00_Enable reg = {.CHIP_EN = true, .LOG_EN = true}; furi_hal_i2c_write_reg_8(handle, LP5562_ADDRESS, 0x00, *(uint8_t*)®, LP5562_I2C_TIMEOUT); //>488μs delay is required after writing to 0x00 register, otherwise program engine will not work furi_delay_us(500); } -void lp5562_set_channel_current(FuriHalI2cBusHandle* handle, LP5562Channel channel, uint8_t value) { +void lp5562_set_channel_current( + const FuriHalI2cBusHandle* handle, + LP5562Channel channel, + uint8_t value) { uint8_t reg_no; if(channel == LP5562ChannelRed) { reg_no = LP5562_CHANNEL_RED_CURRENT_REGISTER; @@ -44,7 +47,10 @@ void lp5562_set_channel_current(FuriHalI2cBusHandle* handle, LP5562Channel chann furi_hal_i2c_write_reg_8(handle, LP5562_ADDRESS, reg_no, value, LP5562_I2C_TIMEOUT); } -void lp5562_set_channel_value(FuriHalI2cBusHandle* handle, LP5562Channel channel, uint8_t value) { +void lp5562_set_channel_value( + const FuriHalI2cBusHandle* handle, + LP5562Channel channel, + uint8_t value) { uint8_t reg_no; if(channel == LP5562ChannelRed) { reg_no = LP5562_CHANNEL_RED_VALUE_REGISTER; @@ -60,7 +66,7 @@ void lp5562_set_channel_value(FuriHalI2cBusHandle* handle, LP5562Channel channel furi_hal_i2c_write_reg_8(handle, LP5562_ADDRESS, reg_no, value, LP5562_I2C_TIMEOUT); } -uint8_t lp5562_get_channel_value(FuriHalI2cBusHandle* handle, LP5562Channel channel) { +uint8_t lp5562_get_channel_value(const FuriHalI2cBusHandle* handle, LP5562Channel channel) { uint8_t reg_no; uint8_t value; if(channel == LP5562ChannelRed) { @@ -78,7 +84,10 @@ uint8_t lp5562_get_channel_value(FuriHalI2cBusHandle* handle, LP5562Channel chan return value; } -void lp5562_set_channel_src(FuriHalI2cBusHandle* handle, LP5562Channel channel, LP5562Engine src) { +void lp5562_set_channel_src( + const FuriHalI2cBusHandle* handle, + LP5562Channel channel, + LP5562Engine src) { uint8_t reg_val = 0; uint8_t bit_offset = 0; @@ -107,7 +116,7 @@ void lp5562_set_channel_src(FuriHalI2cBusHandle* handle, LP5562Channel channel, } void lp5562_execute_program( - FuriHalI2cBusHandle* handle, + const FuriHalI2cBusHandle* handle, LP5562Engine eng, LP5562Channel ch, uint16_t* program) { @@ -155,7 +164,7 @@ void lp5562_execute_program( furi_hal_i2c_write_reg_8(handle, LP5562_ADDRESS, 0x00, enable_reg, LP5562_I2C_TIMEOUT); } -void lp5562_stop_program(FuriHalI2cBusHandle* handle, LP5562Engine eng) { +void lp5562_stop_program(const FuriHalI2cBusHandle* handle, LP5562Engine eng) { if((eng < LP5562Engine1) || (eng > LP5562Engine3)) return; uint8_t reg_val = 0; uint8_t bit_offset = 0; @@ -169,7 +178,7 @@ void lp5562_stop_program(FuriHalI2cBusHandle* handle, LP5562Engine eng) { } void lp5562_execute_ramp( - FuriHalI2cBusHandle* handle, + const FuriHalI2cBusHandle* handle, LP5562Engine eng, LP5562Channel ch, uint8_t val_start, @@ -213,7 +222,7 @@ void lp5562_execute_ramp( } void lp5562_execute_blink( - FuriHalI2cBusHandle* handle, + const FuriHalI2cBusHandle* handle, LP5562Engine eng, LP5562Channel ch, uint16_t on_time, diff --git a/lib/drivers/lp5562.h b/lib/drivers/lp5562.h index f5ebeeae2..2e54e1ce3 100644 --- a/lib/drivers/lp5562.h +++ b/lib/drivers/lp5562.h @@ -20,39 +20,48 @@ typedef enum { } LP5562Engine; /** Initialize Driver */ -void lp5562_reset(FuriHalI2cBusHandle* handle); +void lp5562_reset(const FuriHalI2cBusHandle* handle); /** Configure Driver */ -void lp5562_configure(FuriHalI2cBusHandle* handle); +void lp5562_configure(const FuriHalI2cBusHandle* handle); /** Enable Driver */ -void lp5562_enable(FuriHalI2cBusHandle* handle); +void lp5562_enable(const FuriHalI2cBusHandle* handle); /** Set channel current */ -void lp5562_set_channel_current(FuriHalI2cBusHandle* handle, LP5562Channel channel, uint8_t value); +void lp5562_set_channel_current( + const FuriHalI2cBusHandle* handle, + LP5562Channel channel, + uint8_t value); /** Set channel PWM value */ -void lp5562_set_channel_value(FuriHalI2cBusHandle* handle, LP5562Channel channel, uint8_t value); +void lp5562_set_channel_value( + const FuriHalI2cBusHandle* handle, + LP5562Channel channel, + uint8_t value); /** Get channel PWM value */ -uint8_t lp5562_get_channel_value(FuriHalI2cBusHandle* handle, LP5562Channel channel); +uint8_t lp5562_get_channel_value(const FuriHalI2cBusHandle* handle, LP5562Channel channel); /** Set channel source */ -void lp5562_set_channel_src(FuriHalI2cBusHandle* handle, LP5562Channel channel, LP5562Engine src); +void lp5562_set_channel_src( + const FuriHalI2cBusHandle* handle, + LP5562Channel channel, + LP5562Engine src); /** Execute program sequence */ void lp5562_execute_program( - FuriHalI2cBusHandle* handle, + const FuriHalI2cBusHandle* handle, LP5562Engine eng, LP5562Channel ch, uint16_t* program); /** Stop program sequence */ -void lp5562_stop_program(FuriHalI2cBusHandle* handle, LP5562Engine eng); +void lp5562_stop_program(const FuriHalI2cBusHandle* handle, LP5562Engine eng); /** Execute ramp program sequence */ void lp5562_execute_ramp( - FuriHalI2cBusHandle* handle, + const FuriHalI2cBusHandle* handle, LP5562Engine eng, LP5562Channel ch, uint8_t val_start, @@ -61,7 +70,7 @@ void lp5562_execute_ramp( /** Start blink program sequence */ void lp5562_execute_blink( - FuriHalI2cBusHandle* handle, + const FuriHalI2cBusHandle* handle, LP5562Engine eng, LP5562Channel ch, uint16_t on_time, diff --git a/lib/drivers/st25r3916.c b/lib/drivers/st25r3916.c index f8dc9a5eb..0721a52c7 100644 --- a/lib/drivers/st25r3916.c +++ b/lib/drivers/st25r3916.c @@ -2,7 +2,7 @@ #include -void st25r3916_mask_irq(FuriHalSpiBusHandle* handle, uint32_t mask) { +void st25r3916_mask_irq(const FuriHalSpiBusHandle* handle, uint32_t mask) { furi_assert(handle); uint8_t irq_mask_regs[4] = { @@ -14,7 +14,7 @@ void st25r3916_mask_irq(FuriHalSpiBusHandle* handle, uint32_t mask) { st25r3916_write_burst_regs(handle, ST25R3916_REG_IRQ_MASK_MAIN, irq_mask_regs, 4); } -uint32_t st25r3916_get_irq(FuriHalSpiBusHandle* handle) { +uint32_t st25r3916_get_irq(const FuriHalSpiBusHandle* handle) { furi_assert(handle); uint8_t irq_regs[4] = {}; @@ -32,7 +32,7 @@ uint32_t st25r3916_get_irq(FuriHalSpiBusHandle* handle) { return irq; } -void st25r3916_write_fifo(FuriHalSpiBusHandle* handle, const uint8_t* buff, size_t bits) { +void st25r3916_write_fifo(const FuriHalSpiBusHandle* handle, const uint8_t* buff, size_t bits) { furi_assert(handle); furi_assert(buff); @@ -45,7 +45,7 @@ void st25r3916_write_fifo(FuriHalSpiBusHandle* handle, const uint8_t* buff, size } bool st25r3916_read_fifo( - FuriHalSpiBusHandle* handle, + const FuriHalSpiBusHandle* handle, uint8_t* buff, size_t buff_size, size_t* buff_bits) { diff --git a/lib/drivers/st25r3916.h b/lib/drivers/st25r3916.h index 0e77b6317..3eddaa430 100644 --- a/lib/drivers/st25r3916.h +++ b/lib/drivers/st25r3916.h @@ -75,7 +75,7 @@ extern "C" { * @param handle - pointer to FuriHalSpiBusHandle instance * @param mask - mask of interrupts to be disabled */ -void st25r3916_mask_irq(FuriHalSpiBusHandle* handle, uint32_t mask); +void st25r3916_mask_irq(const FuriHalSpiBusHandle* handle, uint32_t mask); /** Get st25r3916 interrupts * @@ -83,7 +83,7 @@ void st25r3916_mask_irq(FuriHalSpiBusHandle* handle, uint32_t mask); * * @return received interrupts */ -uint32_t st25r3916_get_irq(FuriHalSpiBusHandle* handle); +uint32_t st25r3916_get_irq(const FuriHalSpiBusHandle* handle); /** Write FIFO * @@ -91,7 +91,7 @@ uint32_t st25r3916_get_irq(FuriHalSpiBusHandle* handle); * @param buff - buffer to write to FIFO * @param bits - number of bits to write */ -void st25r3916_write_fifo(FuriHalSpiBusHandle* handle, const uint8_t* buff, size_t bits); +void st25r3916_write_fifo(const FuriHalSpiBusHandle* handle, const uint8_t* buff, size_t bits); /** Read FIFO * @@ -103,7 +103,7 @@ void st25r3916_write_fifo(FuriHalSpiBusHandle* handle, const uint8_t* buff, size * @return true if read success, false otherwise */ bool st25r3916_read_fifo( - FuriHalSpiBusHandle* handle, + const FuriHalSpiBusHandle* handle, uint8_t* buff, size_t buff_size, size_t* buff_bits); diff --git a/lib/drivers/st25r3916_reg.c b/lib/drivers/st25r3916_reg.c index f7a47d463..cdbf0fd3d 100644 --- a/lib/drivers/st25r3916_reg.c +++ b/lib/drivers/st25r3916_reg.c @@ -28,18 +28,18 @@ (ST25R3916_CMD_LEN + \ ST25R3916_FIFO_DEPTH) /*!< ST25R3916 communication buffer: CMD + FIFO length */ -static void st25r3916_reg_tx_byte(FuriHalSpiBusHandle* handle, uint8_t byte) { +static void st25r3916_reg_tx_byte(const FuriHalSpiBusHandle* handle, uint8_t byte) { uint8_t val = byte; furi_hal_spi_bus_tx(handle, &val, 1, 5); } -void st25r3916_read_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* val) { +void st25r3916_read_reg(const FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* val) { furi_check(handle); st25r3916_read_burst_regs(handle, reg, val, 1); } void st25r3916_read_burst_regs( - FuriHalSpiBusHandle* handle, + const FuriHalSpiBusHandle* handle, uint8_t reg_start, uint8_t* values, uint8_t length) { @@ -59,14 +59,14 @@ void st25r3916_read_burst_regs( furi_hal_gpio_write(handle->cs, true); } -void st25r3916_write_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t val) { +void st25r3916_write_reg(const FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t val) { furi_check(handle); uint8_t reg_val = val; st25r3916_write_burst_regs(handle, reg, ®_val, 1); } void st25r3916_write_burst_regs( - FuriHalSpiBusHandle* handle, + const FuriHalSpiBusHandle* handle, uint8_t reg_start, const uint8_t* values, uint8_t length) { @@ -86,7 +86,10 @@ void st25r3916_write_burst_regs( furi_hal_gpio_write(handle->cs, true); } -void st25r3916_reg_write_fifo(FuriHalSpiBusHandle* handle, const uint8_t* buff, size_t length) { +void st25r3916_reg_write_fifo( + const FuriHalSpiBusHandle* handle, + const uint8_t* buff, + size_t length) { furi_check(handle); furi_check(buff); furi_check(length); @@ -98,7 +101,7 @@ void st25r3916_reg_write_fifo(FuriHalSpiBusHandle* handle, const uint8_t* buff, furi_hal_gpio_write(handle->cs, true); } -void st25r3916_reg_read_fifo(FuriHalSpiBusHandle* handle, uint8_t* buff, size_t length) { +void st25r3916_reg_read_fifo(const FuriHalSpiBusHandle* handle, uint8_t* buff, size_t length) { furi_check(handle); furi_check(buff); furi_check(length); @@ -110,7 +113,10 @@ void st25r3916_reg_read_fifo(FuriHalSpiBusHandle* handle, uint8_t* buff, size_t furi_hal_gpio_write(handle->cs, true); } -void st25r3916_write_pta_mem(FuriHalSpiBusHandle* handle, const uint8_t* values, size_t length) { +void st25r3916_write_pta_mem( + const FuriHalSpiBusHandle* handle, + const uint8_t* values, + size_t length) { furi_check(handle); furi_check(values); furi_check(length); @@ -122,7 +128,7 @@ void st25r3916_write_pta_mem(FuriHalSpiBusHandle* handle, const uint8_t* values, furi_hal_gpio_write(handle->cs, true); } -void st25r3916_read_pta_mem(FuriHalSpiBusHandle* handle, uint8_t* buff, size_t length) { +void st25r3916_read_pta_mem(const FuriHalSpiBusHandle* handle, uint8_t* buff, size_t length) { furi_check(handle); furi_check(buff); furi_check(length); @@ -136,7 +142,10 @@ void st25r3916_read_pta_mem(FuriHalSpiBusHandle* handle, uint8_t* buff, size_t l memcpy(buff, tmp_buff + 1, length); } -void st25r3916_write_ptf_mem(FuriHalSpiBusHandle* handle, const uint8_t* values, size_t length) { +void st25r3916_write_ptf_mem( + const FuriHalSpiBusHandle* handle, + const uint8_t* values, + size_t length) { furi_check(handle); furi_check(values); @@ -146,7 +155,7 @@ void st25r3916_write_ptf_mem(FuriHalSpiBusHandle* handle, const uint8_t* values, furi_hal_gpio_write(handle->cs, true); } -void st25r3916_write_pttsn_mem(FuriHalSpiBusHandle* handle, uint8_t* buff, size_t length) { +void st25r3916_write_pttsn_mem(const FuriHalSpiBusHandle* handle, uint8_t* buff, size_t length) { furi_check(handle); furi_check(buff); @@ -156,7 +165,7 @@ void st25r3916_write_pttsn_mem(FuriHalSpiBusHandle* handle, uint8_t* buff, size_ furi_hal_gpio_write(handle->cs, true); } -void st25r3916_direct_cmd(FuriHalSpiBusHandle* handle, uint8_t cmd) { +void st25r3916_direct_cmd(const FuriHalSpiBusHandle* handle, uint8_t cmd) { furi_check(handle); furi_hal_gpio_write(handle->cs, false); @@ -164,7 +173,7 @@ void st25r3916_direct_cmd(FuriHalSpiBusHandle* handle, uint8_t cmd) { furi_hal_gpio_write(handle->cs, true); } -void st25r3916_read_test_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* val) { +void st25r3916_read_test_reg(const FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* val) { furi_check(handle); furi_hal_gpio_write(handle->cs, false); @@ -174,7 +183,7 @@ void st25r3916_read_test_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* furi_hal_gpio_write(handle->cs, true); } -void st25r3916_write_test_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t val) { +void st25r3916_write_test_reg(const FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t val) { furi_check(handle); furi_hal_gpio_write(handle->cs, false); @@ -184,7 +193,7 @@ void st25r3916_write_test_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t furi_hal_gpio_write(handle->cs, true); } -void st25r3916_clear_reg_bits(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t clr_mask) { +void st25r3916_clear_reg_bits(const FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t clr_mask) { furi_check(handle); uint8_t reg_val = 0; @@ -195,7 +204,7 @@ void st25r3916_clear_reg_bits(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t } } -void st25r3916_set_reg_bits(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t set_mask) { +void st25r3916_set_reg_bits(const FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t set_mask) { furi_check(handle); uint8_t reg_val = 0; @@ -207,7 +216,7 @@ void st25r3916_set_reg_bits(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t se } void st25r3916_change_reg_bits( - FuriHalSpiBusHandle* handle, + const FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t mask, uint8_t value) { @@ -217,7 +226,7 @@ void st25r3916_change_reg_bits( } void st25r3916_modify_reg( - FuriHalSpiBusHandle* handle, + const FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t clr_mask, uint8_t set_mask) { @@ -233,7 +242,7 @@ void st25r3916_modify_reg( } void st25r3916_change_test_reg_bits( - FuriHalSpiBusHandle* handle, + const FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t mask, uint8_t value) { @@ -248,7 +257,7 @@ void st25r3916_change_test_reg_bits( } } -bool st25r3916_check_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t mask, uint8_t val) { +bool st25r3916_check_reg(const FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t mask, uint8_t val) { furi_check(handle); uint8_t reg_val = 0; diff --git a/lib/drivers/st25r3916_reg.h b/lib/drivers/st25r3916_reg.h index 5163c4423..524f93cc7 100644 --- a/lib/drivers/st25r3916_reg.h +++ b/lib/drivers/st25r3916_reg.h @@ -967,7 +967,7 @@ extern "C" { * @param reg - register address * @param val - pointer to the variable to store the read value */ -void st25r3916_read_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* val); +void st25r3916_read_reg(const FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* val); /** Read multiple registers * @@ -977,7 +977,7 @@ void st25r3916_read_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* val); * @param length - number of registers to read */ void st25r3916_read_burst_regs( - FuriHalSpiBusHandle* handle, + const FuriHalSpiBusHandle* handle, uint8_t reg_start, uint8_t* values, uint8_t length); @@ -988,7 +988,7 @@ void st25r3916_read_burst_regs( * @param reg - register address * @param val - value to write */ -void st25r3916_write_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t val); +void st25r3916_write_reg(const FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t val); /** Write multiple registers * @@ -998,7 +998,7 @@ void st25r3916_write_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t val); * @param length - number of registers to write */ void st25r3916_write_burst_regs( - FuriHalSpiBusHandle* handle, + const FuriHalSpiBusHandle* handle, uint8_t reg_start, const uint8_t* values, uint8_t length); @@ -1009,7 +1009,10 @@ void st25r3916_write_burst_regs( * @param buff - buffer to write to FIFO * @param length - number of bytes to write */ -void st25r3916_reg_write_fifo(FuriHalSpiBusHandle* handle, const uint8_t* buff, size_t length); +void st25r3916_reg_write_fifo( + const FuriHalSpiBusHandle* handle, + const uint8_t* buff, + size_t length); /** Read fifo register * @@ -1017,7 +1020,7 @@ void st25r3916_reg_write_fifo(FuriHalSpiBusHandle* handle, const uint8_t* buff, * @param buff - buffer to store the read values * @param length - number of bytes to read */ -void st25r3916_reg_read_fifo(FuriHalSpiBusHandle* handle, uint8_t* buff, size_t length); +void st25r3916_reg_read_fifo(const FuriHalSpiBusHandle* handle, uint8_t* buff, size_t length); /** Write PTA memory register * @@ -1025,7 +1028,10 @@ void st25r3916_reg_read_fifo(FuriHalSpiBusHandle* handle, uint8_t* buff, size_t * @param values - pointer to buffer to write * @param length - number of bytes to write */ -void st25r3916_write_pta_mem(FuriHalSpiBusHandle* handle, const uint8_t* values, size_t length); +void st25r3916_write_pta_mem( + const FuriHalSpiBusHandle* handle, + const uint8_t* values, + size_t length); /** Read PTA memory register * @@ -1033,7 +1039,7 @@ void st25r3916_write_pta_mem(FuriHalSpiBusHandle* handle, const uint8_t* values, * @param values - buffer to store the read values * @param length - number of bytes to read */ -void st25r3916_read_pta_mem(FuriHalSpiBusHandle* handle, uint8_t* values, size_t length); +void st25r3916_read_pta_mem(const FuriHalSpiBusHandle* handle, uint8_t* values, size_t length); /** Write PTF memory register * @@ -1041,7 +1047,10 @@ void st25r3916_read_pta_mem(FuriHalSpiBusHandle* handle, uint8_t* values, size_t * @param values - pointer to buffer to write * @param length - number of bytes to write */ -void st25r3916_write_ptf_mem(FuriHalSpiBusHandle* handle, const uint8_t* values, size_t length); +void st25r3916_write_ptf_mem( + const FuriHalSpiBusHandle* handle, + const uint8_t* values, + size_t length); /** Read PTTSN memory register * @@ -1049,21 +1058,21 @@ void st25r3916_write_ptf_mem(FuriHalSpiBusHandle* handle, const uint8_t* values, * @param values - pointer to buffer to write * @param length - number of bytes to write */ -void st25r3916_write_pttsn_mem(FuriHalSpiBusHandle* handle, uint8_t* values, size_t length); +void st25r3916_write_pttsn_mem(const FuriHalSpiBusHandle* handle, uint8_t* values, size_t length); /** Send Direct command * * @param handle - pointer to FuriHalSpiBusHandle instance * @param cmd - direct command */ -void st25r3916_direct_cmd(FuriHalSpiBusHandle* handle, uint8_t cmd); +void st25r3916_direct_cmd(const FuriHalSpiBusHandle* handle, uint8_t cmd); /** Read test register * @param handle - pointer to FuriHalSpiBusHandle instance * @param reg - register address * @param val - pointer to the variable to store the read value */ -void st25r3916_read_test_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* val); +void st25r3916_read_test_reg(const FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* val); /** Write test register * @@ -1071,7 +1080,7 @@ void st25r3916_read_test_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* * @param reg - register address * @param val - value to write */ -void st25r3916_write_test_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t val); +void st25r3916_write_test_reg(const FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t val); /** Clear register bits * @@ -1079,7 +1088,7 @@ void st25r3916_write_test_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t * @param reg - register address * @param clr_mask - bit mask to clear */ -void st25r3916_clear_reg_bits(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t clr_mask); +void st25r3916_clear_reg_bits(const FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t clr_mask); /** Set register bits * @@ -1087,7 +1096,7 @@ void st25r3916_clear_reg_bits(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t * @param reg - register address * @param set_mask - bit mask to set */ -void st25r3916_set_reg_bits(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t set_mask); +void st25r3916_set_reg_bits(const FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t set_mask); /** Change register bits * @@ -1097,7 +1106,7 @@ void st25r3916_set_reg_bits(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t se * @param value - new register value to write */ void st25r3916_change_reg_bits( - FuriHalSpiBusHandle* handle, + const FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t mask, uint8_t value); @@ -1110,7 +1119,7 @@ void st25r3916_change_reg_bits( * @param set_mask - bit mask to set */ void st25r3916_modify_reg( - FuriHalSpiBusHandle* handle, + const FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t clr_mask, uint8_t set_mask); @@ -1123,7 +1132,7 @@ void st25r3916_modify_reg( * @param value - new register value to write */ void st25r3916_change_test_reg_bits( - FuriHalSpiBusHandle* handle, + const FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t mask, uint8_t value); @@ -1137,7 +1146,7 @@ void st25r3916_change_test_reg_bits( * * @return true if register value matches the expected value, false otherwise */ -bool st25r3916_check_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t mask, uint8_t val); +bool st25r3916_check_reg(const FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t mask, uint8_t val); #ifdef __cplusplus } diff --git a/lib/ibutton/protocols/dallas/protocol_group_dallas_defs.c b/lib/ibutton/protocols/dallas/protocol_group_dallas_defs.c index b4dd51ce7..df758283b 100644 --- a/lib/ibutton/protocols/dallas/protocol_group_dallas_defs.c +++ b/lib/ibutton/protocols/dallas/protocol_group_dallas_defs.c @@ -6,7 +6,7 @@ #include "protocol_ds1971.h" #include "protocol_ds_generic.h" -const iButtonProtocolDallasBase* ibutton_protocols_dallas[] = { +const iButtonProtocolDallasBase* const ibutton_protocols_dallas[] = { [iButtonProtocolDS1990] = &ibutton_protocol_ds1990, [iButtonProtocolDS1992] = &ibutton_protocol_ds1992, [iButtonProtocolDS1996] = &ibutton_protocol_ds1996, diff --git a/lib/ibutton/protocols/dallas/protocol_group_dallas_defs.h b/lib/ibutton/protocols/dallas/protocol_group_dallas_defs.h index 2ba1dd39a..71571d91b 100644 --- a/lib/ibutton/protocols/dallas/protocol_group_dallas_defs.h +++ b/lib/ibutton/protocols/dallas/protocol_group_dallas_defs.h @@ -14,4 +14,4 @@ typedef enum { iButtonProtocolDSMax, } iButtonProtocolDallas; -extern const iButtonProtocolDallasBase* ibutton_protocols_dallas[]; +extern const iButtonProtocolDallasBase* const ibutton_protocols_dallas[]; diff --git a/lib/ibutton/protocols/misc/protocol_group_misc_defs.c b/lib/ibutton/protocols/misc/protocol_group_misc_defs.c index 09ae0bdc7..f8cae0463 100644 --- a/lib/ibutton/protocols/misc/protocol_group_misc_defs.c +++ b/lib/ibutton/protocols/misc/protocol_group_misc_defs.c @@ -3,7 +3,7 @@ #include "protocol_cyfral.h" #include "protocol_metakom.h" -const ProtocolBase* ibutton_protocols_misc[] = { +const ProtocolBase* const ibutton_protocols_misc[] = { [iButtonProtocolMiscCyfral] = &ibutton_protocol_misc_cyfral, [iButtonProtocolMiscMetakom] = &ibutton_protocol_misc_metakom, /* Add new misc protocols here */ diff --git a/lib/ibutton/protocols/misc/protocol_group_misc_defs.h b/lib/ibutton/protocols/misc/protocol_group_misc_defs.h index 0a7f92847..cde6b0aa9 100644 --- a/lib/ibutton/protocols/misc/protocol_group_misc_defs.h +++ b/lib/ibutton/protocols/misc/protocol_group_misc_defs.h @@ -8,4 +8,4 @@ typedef enum { iButtonProtocolMiscMax, } iButtonProtocolMisc; -extern const ProtocolBase* ibutton_protocols_misc[]; +extern const ProtocolBase* const ibutton_protocols_misc[]; diff --git a/lib/ibutton/protocols/protocol_group_defs.c b/lib/ibutton/protocols/protocol_group_defs.c index 40a360f0e..ac428f410 100644 --- a/lib/ibutton/protocols/protocol_group_defs.c +++ b/lib/ibutton/protocols/protocol_group_defs.c @@ -3,7 +3,7 @@ #include "dallas/protocol_group_dallas.h" #include "misc/protocol_group_misc.h" -const iButtonProtocolGroupBase* ibutton_protocol_groups[] = { +const iButtonProtocolGroupBase* const ibutton_protocol_groups[] = { [iButtonProtocolGroupDallas] = &ibutton_protocol_group_dallas, [iButtonProtocolGroupMisc] = &ibutton_protocol_group_misc, }; diff --git a/lib/ibutton/protocols/protocol_group_defs.h b/lib/ibutton/protocols/protocol_group_defs.h index 2d41e3cb8..2c00dfab4 100644 --- a/lib/ibutton/protocols/protocol_group_defs.h +++ b/lib/ibutton/protocols/protocol_group_defs.h @@ -8,4 +8,4 @@ typedef enum { iButtonProtocolGroupMax } iButtonProtocolGroup; -extern const iButtonProtocolGroupBase* ibutton_protocol_groups[]; +extern const iButtonProtocolGroupBase* const ibutton_protocol_groups[]; diff --git a/lib/ieee754_parse_wrap/SConscript b/lib/ieee754_parse_wrap/SConscript new file mode 100644 index 000000000..dc60036e0 --- /dev/null +++ b/lib/ieee754_parse_wrap/SConscript @@ -0,0 +1,31 @@ +Import("env") + +wrapped_fn_list = [ + "strtof", + "strtod", +] + +for wrapped_fn in wrapped_fn_list: + env.Append( + LINKFLAGS=[ + "-Wl,--wrap," + wrapped_fn, + ] + ) + +env.Append( + SDK_HEADERS=[ + File("wrappers.h"), + ], + LINT_SOURCES=[ + Dir("."), + ], +) + +libenv = env.Clone(FW_LIB_NAME="ieee754_parse_wrap") +libenv.ApplyLibFlags() + +sources = libenv.GlobRecursive("*.c*", ".") + +lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources) +libenv.Install("${LIB_DIST_DIR}", lib) +Return("lib") diff --git a/lib/ieee754_parse_wrap/wrappers.c b/lib/ieee754_parse_wrap/wrappers.c new file mode 100644 index 000000000..33bd38c35 --- /dev/null +++ b/lib/ieee754_parse_wrap/wrappers.c @@ -0,0 +1,14 @@ +#include "wrappers.h" + +// Based on the disassembly, providing NULL as `locale` is fine. +// The default `strtof` and `strtod` provided in the same libc_nano also just +// call these functions, but with an actual locale structure which was taking up +// lots of .data space (364 bytes). + +float __wrap_strtof(const char* in, char** tail) { + return strtof_l(in, tail, NULL); +} + +double __wrap_strtod(const char* in, char** tail) { + return strtod_l(in, tail, NULL); +} diff --git a/lib/ieee754_parse_wrap/wrappers.h b/lib/ieee754_parse_wrap/wrappers.h new file mode 100644 index 000000000..17e7acf83 --- /dev/null +++ b/lib/ieee754_parse_wrap/wrappers.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +float __wrap_strtof(const char* in, char** tail); +double __wrap_strtod(const char* in, char** tail); + +#ifdef __cplusplus +} +#endif diff --git a/lib/lfrfid/protocols/lfrfid_protocols.c b/lib/lfrfid/protocols/lfrfid_protocols.c index 238f8e0cd..c6cc57a68 100644 --- a/lib/lfrfid/protocols/lfrfid_protocols.c +++ b/lib/lfrfid/protocols/lfrfid_protocols.c @@ -22,7 +22,7 @@ #include "protocol_gproxii.h" #include "protocol_noralsy.h" -const ProtocolBase* lfrfid_protocols[] = { +const ProtocolBase* const lfrfid_protocols[] = { [LFRFIDProtocolEM4100] = &protocol_em4100, [LFRFIDProtocolEM410032] = &protocol_em4100_32, [LFRFIDProtocolEM410016] = &protocol_em4100_16, diff --git a/lib/lfrfid/protocols/lfrfid_protocols.h b/lib/lfrfid/protocols/lfrfid_protocols.h index 86c31c60b..a33e87990 100644 --- a/lib/lfrfid/protocols/lfrfid_protocols.h +++ b/lib/lfrfid/protocols/lfrfid_protocols.h @@ -37,7 +37,7 @@ typedef enum { LFRFIDProtocolMax, } LFRFIDProtocol; -extern const ProtocolBase* lfrfid_protocols[]; +extern const ProtocolBase* const lfrfid_protocols[]; typedef enum { LFRFIDWriteTypeT5577, diff --git a/lib/lfrfid/protocols/protocol_nexwatch.c b/lib/lfrfid/protocols/protocol_nexwatch.c index a794a4a8e..08324803f 100644 --- a/lib/lfrfid/protocols/protocol_nexwatch.c +++ b/lib/lfrfid/protocols/protocol_nexwatch.c @@ -21,7 +21,7 @@ typedef struct { uint8_t chk; } ProtocolNexwatchMagic; -ProtocolNexwatchMagic magic_items[] = { +static ProtocolNexwatchMagic magic_items[] = { {0xBE, "Quadrakey", 0}, {0x88, "Nexkey", 0}, {0x86, "Honeywell", 0}}; diff --git a/lib/mjs/mjs_parser.c b/lib/mjs/mjs_parser.c index 212804a86..98f60459d 100644 --- a/lib/mjs/mjs_parser.c +++ b/lib/mjs/mjs_parser.c @@ -47,7 +47,7 @@ static int ptest(struct pstate* p) { return tok; } -static int s_unary_ops[] = { +static const int s_unary_ops[] = { TOK_NOT, TOK_TILDA, TOK_PLUS_PLUS, @@ -56,10 +56,10 @@ static int s_unary_ops[] = { TOK_MINUS, TOK_PLUS, TOK_EOF}; -static int s_comparison_ops[] = {TOK_LT, TOK_LE, TOK_GT, TOK_GE, TOK_EOF}; -static int s_postfix_ops[] = {TOK_PLUS_PLUS, TOK_MINUS_MINUS, TOK_EOF}; -static int s_equality_ops[] = {TOK_EQ, TOK_NE, TOK_EQ_EQ, TOK_NE_NE, TOK_EOF}; -static int s_assign_ops[] = { +static const int s_comparison_ops[] = {TOK_LT, TOK_LE, TOK_GT, TOK_GE, TOK_EOF}; +static const int s_postfix_ops[] = {TOK_PLUS_PLUS, TOK_MINUS_MINUS, TOK_EOF}; +static const int s_equality_ops[] = {TOK_EQ, TOK_NE, TOK_EQ_EQ, TOK_NE_NE, TOK_EOF}; +static const int s_assign_ops[] = { TOK_ASSIGN, TOK_PLUS_ASSIGN, TOK_MINUS_ASSIGN, @@ -74,7 +74,7 @@ static int s_assign_ops[] = { TOK_OR_ASSIGN, TOK_EOF}; -static int findtok(int* toks, int tok) { +static int findtok(int const* toks, int tok) { int i = 0; while(tok != toks[i] && toks[i] != TOK_EOF) i++; diff --git a/lib/mjs/mjs_primitive.c b/lib/mjs/mjs_primitive.c index e73ae892d..20d2a8c66 100644 --- a/lib/mjs/mjs_primitive.c +++ b/lib/mjs/mjs_primitive.c @@ -9,6 +9,8 @@ #include "mjs_string_public.h" #include "mjs_util.h" +#include + mjs_val_t mjs_mk_null(void) { return MJS_NULL; } @@ -94,7 +96,7 @@ int mjs_is_boolean(mjs_val_t v) { } #define MJS_IS_POINTER_LEGIT(n) \ - (((n)&MJS_TAG_MASK) == 0 || ((n)&MJS_TAG_MASK) == (~0 & MJS_TAG_MASK)) + (((n) & MJS_TAG_MASK) == 0 || ((n) & MJS_TAG_MASK) == (~0 & MJS_TAG_MASK)) MJS_PRIVATE mjs_val_t mjs_pointer_to_value(struct mjs* mjs, void* p) { uint64_t n = ((uint64_t)(uintptr_t)p); @@ -165,13 +167,13 @@ MJS_PRIVATE void mjs_number_to_string(struct mjs* mjs) { mjs_val_t ret = MJS_UNDEFINED; mjs_val_t base_v = MJS_UNDEFINED; int32_t base = 10; - int32_t num; + double num; /* get number from `this` */ if(!mjs_check_arg(mjs, -1 /*this*/, "this", MJS_TYPE_NUMBER, NULL)) { goto clean; } - num = mjs_get_int32(mjs, mjs->vals.this_obj); + num = mjs_get_double(mjs, mjs->vals.this_obj); if(mjs_nargs(mjs) >= 1) { /* get base from arg 0 */ @@ -181,9 +183,20 @@ MJS_PRIVATE void mjs_number_to_string(struct mjs* mjs) { base = mjs_get_int(mjs, base_v); } - char tmp_str[] = "-2147483648"; - itoa(num, tmp_str, base); - ret = mjs_mk_string(mjs, tmp_str, ~0, true); + if(base != 10 || floor(num) == num) { + char tmp_str[] = "-2147483648"; + itoa((int32_t)num, tmp_str, base); + ret = mjs_mk_string(mjs, tmp_str, ~0, true); + } else { + char tmp_str[] = "2.22514337200000e-308"; + snprintf(tmp_str, sizeof(tmp_str), "%.*g", DBL_DIG, num); + size_t len = strlen(tmp_str); + while(len && tmp_str[len - 1] == '0') { + len--; + } + tmp_str[len] = '\0'; + ret = mjs_mk_string(mjs, tmp_str, ~0, true); + } clean: mjs_return(mjs, ret); diff --git a/lib/nfc/helpers/nfc_data_generator.c b/lib/nfc/helpers/nfc_data_generator.c index 7914c1f7f..2143f0f5f 100644 --- a/lib/nfc/helpers/nfc_data_generator.c +++ b/lib/nfc/helpers/nfc_data_generator.c @@ -392,37 +392,15 @@ static void nfc_generate_mf_classic(NfcDevice* nfc_device, uint8_t uid_len, MfCl mf_classic_set_block_read(mfc_data, 0, &mfc_data->block[0]); + // Set every block to 0x00 uint16_t block_num = mf_classic_get_total_block_num(type); - if(type == MfClassicType4k) { - // Set every block to 0x00 - for(uint16_t i = 1; i < block_num; i++) { - if(mf_classic_is_sector_trailer(i)) { - nfc_generate_mf_classic_sector_trailer(mfc_data, i); - } else { - memset(&mfc_data->block[i].data, 0x00, 16); - } - mf_classic_set_block_read(mfc_data, i, &mfc_data->block[i]); - } - } else if(type == MfClassicType1k) { - // Set every block to 0x00 - for(uint16_t i = 1; i < block_num; i++) { - if(mf_classic_is_sector_trailer(i)) { - nfc_generate_mf_classic_sector_trailer(mfc_data, i); - } else { - memset(&mfc_data->block[i].data, 0x00, 16); - } - mf_classic_set_block_read(mfc_data, i, &mfc_data->block[i]); - } - } else if(type == MfClassicTypeMini) { - // Set every block to 0x00 - for(uint16_t i = 1; i < block_num; i++) { - if(mf_classic_is_sector_trailer(i)) { - nfc_generate_mf_classic_sector_trailer(mfc_data, i); - } else { - memset(&mfc_data->block[i].data, 0x00, 16); - } - mf_classic_set_block_read(mfc_data, i, &mfc_data->block[i]); + for(uint16_t i = 1; i < block_num; i++) { + if(mf_classic_is_sector_trailer(i)) { + nfc_generate_mf_classic_sector_trailer(mfc_data, i); + } else { + memset(&mfc_data->block[i].data, 0x00, MF_CLASSIC_BLOCK_SIZE); } + mf_classic_set_block_read(mfc_data, i, &mfc_data->block[i]); } nfc_generate_mf_classic_block_0( diff --git a/lib/nfc/protocols/iso15693_3/iso15693_3.c b/lib/nfc/protocols/iso15693_3/iso15693_3.c index e2628b258..802d87f03 100644 --- a/lib/nfc/protocols/iso15693_3/iso15693_3.c +++ b/lib/nfc/protocols/iso15693_3/iso15693_3.c @@ -173,33 +173,35 @@ bool iso15693_3_load(Iso15693_3Data* data, FlipperFormat* ff, uint32_t version) if(flipper_format_key_exist(ff, ISO15693_3_BLOCK_COUNT_KEY) && flipper_format_key_exist(ff, ISO15693_3_BLOCK_SIZE_KEY)) { + data->system_info.flags |= ISO15693_3_SYSINFO_FLAG_MEMORY; + uint32_t block_count; if(!flipper_format_read_uint32(ff, ISO15693_3_BLOCK_COUNT_KEY, &block_count, 1)) break; - data->system_info.block_count = block_count; - data->system_info.flags |= ISO15693_3_SYSINFO_FLAG_MEMORY; if(!flipper_format_read_hex( ff, ISO15693_3_BLOCK_SIZE_KEY, &(data->system_info.block_size), 1)) break; - simple_array_init( - data->block_data, data->system_info.block_size * data->system_info.block_count); - - if(!flipper_format_read_hex( - ff, - ISO15693_3_DATA_CONTENT_KEY, - simple_array_get_data(data->block_data), - simple_array_get_count(data->block_data))) - break; - - if(flipper_format_key_exist(ff, ISO15693_3_SECURITY_STATUS_KEY)) { + if(data->system_info.block_count > 0 && data->system_info.block_size > 0) { + simple_array_init( + data->block_data, + data->system_info.block_size * data->system_info.block_count); simple_array_init(data->block_security, data->system_info.block_count); - const bool security_loaded = has_lock_bits ? - iso15693_3_load_security(data, ff) : - iso15693_3_load_security_legacy(data, ff); - if(!security_loaded) break; + if(!flipper_format_read_hex( + ff, + ISO15693_3_DATA_CONTENT_KEY, + simple_array_get_data(data->block_data), + simple_array_get_count(data->block_data))) + break; + + if(flipper_format_key_exist(ff, ISO15693_3_SECURITY_STATUS_KEY)) { + const bool security_loaded = has_lock_bits ? + iso15693_3_load_security(data, ff) : + iso15693_3_load_security_legacy(data, ff); + if(!security_loaded) break; + } } } @@ -260,22 +262,24 @@ bool iso15693_3_save(const Iso15693_3Data* data, FlipperFormat* ff) { ff, ISO15693_3_BLOCK_SIZE_KEY, &data->system_info.block_size, 1)) break; - if(!flipper_format_write_hex( - ff, - ISO15693_3_DATA_CONTENT_KEY, - simple_array_cget_data(data->block_data), - simple_array_get_count(data->block_data))) - break; + if(data->system_info.block_count > 0 && data->system_info.block_size > 0) { + if(!flipper_format_write_hex( + ff, + ISO15693_3_DATA_CONTENT_KEY, + simple_array_cget_data(data->block_data), + simple_array_get_count(data->block_data))) + break; - if(!flipper_format_write_comment_cstr( - ff, "Block Security Status: 01 = locked, 00 = not locked")) - break; - if(!flipper_format_write_hex( - ff, - ISO15693_3_SECURITY_STATUS_KEY, - simple_array_cget_data(data->block_security), - simple_array_get_count(data->block_security))) - break; + if(!flipper_format_write_comment_cstr( + ff, "Block Security Status: 01 = locked, 00 = not locked")) + break; + if(!flipper_format_write_hex( + ff, + ISO15693_3_SECURITY_STATUS_KEY, + simple_array_cget_data(data->block_security), + simple_array_get_count(data->block_security))) + break; + } } saved = true; } while(false); diff --git a/lib/nfc/protocols/iso15693_3/iso15693_3_poller_i.c b/lib/nfc/protocols/iso15693_3/iso15693_3_poller_i.c index 6aee84a3f..bc677ce67 100644 --- a/lib/nfc/protocols/iso15693_3/iso15693_3_poller_i.c +++ b/lib/nfc/protocols/iso15693_3/iso15693_3_poller_i.c @@ -100,10 +100,12 @@ Iso15693_3Error iso15693_3_poller_activate(Iso15693_3Poller* instance, Iso15693_ break; } - if(system_info->block_count > 0) { - // Read blocks: Optional command + if(system_info->block_count > 0 && system_info->block_size > 0) { simple_array_init( data->block_data, system_info->block_count * system_info->block_size); + simple_array_init(data->block_security, system_info->block_count); + + // Read blocks: Optional command ret = iso15693_3_poller_read_blocks( instance, simple_array_get_data(data->block_data), @@ -115,8 +117,6 @@ Iso15693_3Error iso15693_3_poller_activate(Iso15693_3Poller* instance, Iso15693_ } // Get block security status: Optional command - simple_array_init(data->block_security, system_info->block_count); - ret = iso15693_3_poller_get_blocks_security( instance, simple_array_get_data(data->block_security), system_info->block_count); if(ret != Iso15693_3ErrorNone) { diff --git a/lib/nfc/protocols/mf_classic/mf_classic_listener.c b/lib/nfc/protocols/mf_classic/mf_classic_listener.c index ef571117a..1f5eea271 100644 --- a/lib/nfc/protocols/mf_classic/mf_classic_listener.c +++ b/lib/nfc/protocols/mf_classic/mf_classic_listener.c @@ -19,7 +19,7 @@ typedef struct { uint8_t cmd_start_byte; size_t cmd_len_bits; size_t command_num; - MfClassicListenerCommandHandler* handler; + const MfClassicListenerCommandHandler* handler; } MfClassicListenerCmd; static void mf_classic_listener_prepare_emulation(MfClassicListener* instance) { @@ -441,42 +441,42 @@ static MfClassicListenerCommand return command; } -static MfClassicListenerCommandHandler mf_classic_listener_halt_handlers[] = { +static const MfClassicListenerCommandHandler mf_classic_listener_halt_handlers[] = { mf_classic_listener_halt_handler, }; -static MfClassicListenerCommandHandler mf_classic_listener_auth_key_a_handlers[] = { +static const MfClassicListenerCommandHandler mf_classic_listener_auth_key_a_handlers[] = { mf_classic_listener_auth_key_a_handler, mf_classic_listener_auth_second_part_handler, }; -static MfClassicListenerCommandHandler mf_classic_listener_auth_key_b_handlers[] = { +static const MfClassicListenerCommandHandler mf_classic_listener_auth_key_b_handlers[] = { mf_classic_listener_auth_key_b_handler, mf_classic_listener_auth_second_part_handler, }; -static MfClassicListenerCommandHandler mf_classic_listener_read_block_handlers[] = { +static const MfClassicListenerCommandHandler mf_classic_listener_read_block_handlers[] = { mf_classic_listener_read_block_handler, }; -static MfClassicListenerCommandHandler mf_classic_listener_write_block_handlers[] = { +static const MfClassicListenerCommandHandler mf_classic_listener_write_block_handlers[] = { mf_classic_listener_write_block_first_part_handler, mf_classic_listener_write_block_second_part_handler, }; -static MfClassicListenerCommandHandler mf_classic_listener_value_dec_handlers[] = { +static const MfClassicListenerCommandHandler mf_classic_listener_value_dec_handlers[] = { mf_classic_listener_value_dec_handler, mf_classic_listener_value_data_receive_handler, mf_classic_listener_value_transfer_handler, }; -static MfClassicListenerCommandHandler mf_classic_listener_value_inc_handlers[] = { +static const MfClassicListenerCommandHandler mf_classic_listener_value_inc_handlers[] = { mf_classic_listener_value_inc_handler, mf_classic_listener_value_data_receive_handler, mf_classic_listener_value_transfer_handler, }; -static MfClassicListenerCommandHandler mf_classic_listener_value_restore_handlers[] = { +static const MfClassicListenerCommandHandler mf_classic_listener_value_restore_handlers[] = { mf_classic_listener_value_restore_handler, mf_classic_listener_value_data_receive_handler, mf_classic_listener_value_transfer_handler, diff --git a/lib/nfc/protocols/mf_desfire/mf_desfire.h b/lib/nfc/protocols/mf_desfire/mf_desfire.h index 3cad11473..cb5c6cac8 100644 --- a/lib/nfc/protocols/mf_desfire/mf_desfire.h +++ b/lib/nfc/protocols/mf_desfire/mf_desfire.h @@ -104,6 +104,7 @@ typedef enum { MfDesfireFileTypeValue = 2, MfDesfireFileTypeLinearRecord = 3, MfDesfireFileTypeCyclicRecord = 4, + MfDesfireFileTypeTransactionMac = 5, } MfDesfireFileType; typedef enum { @@ -135,6 +136,11 @@ typedef struct { uint32_t max; uint32_t cur; } record; + struct { + uint8_t key_option; + uint8_t key_version; + uint32_t counter_limit; + } transaction_mac; }; } MfDesfireFileSettings; diff --git a/lib/nfc/protocols/mf_desfire/mf_desfire_i.c b/lib/nfc/protocols/mf_desfire/mf_desfire_i.c index 28284f212..b90ef8ccf 100644 --- a/lib/nfc/protocols/mf_desfire/mf_desfire_i.c +++ b/lib/nfc/protocols/mf_desfire/mf_desfire_i.c @@ -1,5 +1,7 @@ #include "mf_desfire_i.h" +#include + #define TAG "MfDesfire" #define BITS_IN_BYTE (8U) @@ -47,6 +49,10 @@ #define MF_DESFIRE_FFF_FILE_MAX_KEY "Max" #define MF_DESFIRE_FFF_FILE_CUR_KEY "Cur" +#define MF_DESFIRE_FFF_FILE_KEY_OPTION_KEY "Key Option" +#define MF_DESFIRE_FFF_FILE_KEY_VERSION_KEY "Key Version" +#define MF_DESFIRE_FFF_FILE_COUNTER_LIMIT_KEY "Counter Limit" + bool mf_desfire_version_parse(MfDesfireVersion* data, const BitBuffer* buf) { const bool can_parse = bit_buffer_get_size_bytes(buf) == sizeof(MfDesfireVersion); @@ -183,12 +189,19 @@ bool mf_desfire_file_settings_parse(MfDesfireFileSettings* data, const BitBuffer uint32_t cur : 3 * BITS_IN_BYTE; } MfDesfireFileSettingsRecord; + typedef struct FURI_PACKED { + uint8_t key_option; + uint8_t key_version; + uint8_t counter_limit[]; + } MfDesfireFileSettingsTransactionMac; + typedef struct FURI_PACKED { MfDesfireFileSettingsHeader header; union { MfDesfireFileSettingsData data; MfDesfireFileSettingsValue value; MfDesfireFileSettingsRecord record; + MfDesfireFileSettingsTransactionMac transaction_mac; }; } MfDesfireFileSettingsLayout; @@ -197,7 +210,7 @@ bool mf_desfire_file_settings_parse(MfDesfireFileSettings* data, const BitBuffer const size_t data_size = bit_buffer_get_size_bytes(buf); const uint8_t* data_ptr = bit_buffer_get_data(buf); const size_t min_data_size = - sizeof(MfDesfireFileSettingsHeader) + sizeof(MfDesfireFileSettingsData); + sizeof(MfDesfireFileSettingsHeader) + sizeof(MfDesfireFileSettingsTransactionMac); if(data_size < min_data_size) { FURI_LOG_E( @@ -217,17 +230,11 @@ bool mf_desfire_file_settings_parse(MfDesfireFileSettings* data, const BitBuffer if(file_settings_temp.type == MfDesfireFileTypeStandard || file_settings_temp.type == MfDesfireFileTypeBackup) { - memcpy( - &layout.data, - &data_ptr[sizeof(MfDesfireFileSettingsHeader)], - sizeof(MfDesfireFileSettingsData)); + memcpy(&layout.data, &data_ptr[bytes_processed], sizeof(MfDesfireFileSettingsData)); file_settings_temp.data.size = layout.data.size; bytes_processed += sizeof(MfDesfireFileSettingsData); } else if(file_settings_temp.type == MfDesfireFileTypeValue) { - memcpy( - &layout.value, - &data_ptr[sizeof(MfDesfireFileSettingsHeader)], - sizeof(MfDesfireFileSettingsValue)); + memcpy(&layout.value, &data_ptr[bytes_processed], sizeof(MfDesfireFileSettingsValue)); file_settings_temp.value.lo_limit = layout.value.lo_limit; file_settings_temp.value.hi_limit = layout.value.hi_limit; file_settings_temp.value.limited_credit_value = layout.value.limited_credit_value; @@ -237,13 +244,34 @@ bool mf_desfire_file_settings_parse(MfDesfireFileSettings* data, const BitBuffer file_settings_temp.type == MfDesfireFileTypeLinearRecord || file_settings_temp.type == MfDesfireFileTypeCyclicRecord) { memcpy( - &layout.record, - &data_ptr[sizeof(MfDesfireFileSettingsHeader)], - sizeof(MfDesfireFileSettingsRecord)); + &layout.record, &data_ptr[bytes_processed], sizeof(MfDesfireFileSettingsRecord)); file_settings_temp.record.size = layout.record.size; file_settings_temp.record.max = layout.record.max; file_settings_temp.record.cur = layout.record.cur; bytes_processed += sizeof(MfDesfireFileSettingsRecord); + } else if(file_settings_temp.type == MfDesfireFileTypeTransactionMac) { + const bool has_counter_limit = (layout.header.comm & 0x20) != 0; + memcpy( + &layout.transaction_mac, + &data_ptr[bytes_processed], + sizeof(MfDesfireFileSettingsTransactionMac)); + file_settings_temp.transaction_mac.key_option = layout.transaction_mac.key_option; + file_settings_temp.transaction_mac.key_version = layout.transaction_mac.key_version; + if(!has_counter_limit) { + file_settings_temp.transaction_mac.counter_limit = 0; + } else { + // AES (4b) or LRP (2b) + const size_t counter_limit_size = (layout.transaction_mac.key_option & 0x02) ? 4 : + 2; + memcpy( + &layout.transaction_mac, + &data_ptr[bytes_processed], + sizeof(MfDesfireFileSettingsTransactionMac) + counter_limit_size); + file_settings_temp.transaction_mac.counter_limit = bit_lib_bytes_to_num_be( + layout.transaction_mac.counter_limit, counter_limit_size); + bytes_processed += counter_limit_size; + } + bytes_processed += sizeof(MfDesfireFileSettingsTransactionMac); } else { FURI_LOG_W(TAG, "Unknown file type: %02x", file_settings_temp.type); break; @@ -483,6 +511,21 @@ bool mf_desfire_file_settings_load( furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_CUR_KEY); if(!flipper_format_read_uint32(ff, furi_string_get_cstr(key), &data->record.cur, 1)) break; + } else if(data->type == MfDesfireFileTypeTransactionMac) { + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_KEY_OPTION_KEY); + if(!flipper_format_read_hex( + ff, furi_string_get_cstr(key), &data->transaction_mac.key_option, 1)) + break; + + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_KEY_VERSION_KEY); + if(!flipper_format_read_hex( + ff, furi_string_get_cstr(key), &data->transaction_mac.key_version, 1)) + break; + + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_COUNTER_LIMIT_KEY); + if(!flipper_format_read_uint32( + ff, furi_string_get_cstr(key), &data->transaction_mac.counter_limit, 1)) + break; } success = true; @@ -731,6 +774,21 @@ bool mf_desfire_file_settings_save( furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_CUR_KEY); if(!flipper_format_write_uint32(ff, furi_string_get_cstr(key), &data->record.cur, 1)) break; + } else if(data->type == MfDesfireFileTypeTransactionMac) { + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_KEY_OPTION_KEY); + if(!flipper_format_write_hex( + ff, furi_string_get_cstr(key), &data->transaction_mac.key_option, 1)) + break; + + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_KEY_VERSION_KEY); + if(!flipper_format_write_hex( + ff, furi_string_get_cstr(key), &data->transaction_mac.key_version, 1)) + break; + + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_COUNTER_LIMIT_KEY); + if(!flipper_format_write_uint32( + ff, furi_string_get_cstr(key), &data->transaction_mac.counter_limit, 1)) + break; } success = true; diff --git a/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.c b/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.c index b8aad6e0d..a44395c60 100644 --- a/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.c +++ b/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.c @@ -551,7 +551,7 @@ MfDesfireError mf_desfire_poller_read_file_data_multi( file_type == MfDesfireFileTypeLinearRecord || file_type == MfDesfireFileTypeCyclicRecord) { error = mf_desfire_poller_read_file_records( - instance, file_id, 0, file_settings_cur->data.size, file_data); + instance, file_id, 0, file_settings_cur->record.size, file_data); } } diff --git a/lib/nfc/protocols/nfc_device_defs.c b/lib/nfc/protocols/nfc_device_defs.c index 1963faeb0..6e0688167 100644 --- a/lib/nfc/protocols/nfc_device_defs.c +++ b/lib/nfc/protocols/nfc_device_defs.c @@ -33,7 +33,7 @@ * When implementing a new protocol, add its implementation * here under its own index defined in nfc_protocol.h. */ -const NfcDeviceBase* nfc_devices[NfcProtocolNum] = { +const NfcDeviceBase* const nfc_devices[NfcProtocolNum] = { [NfcProtocolIso14443_3a] = &nfc_device_iso14443_3a, [NfcProtocolIso14443_3b] = &nfc_device_iso14443_3b, [NfcProtocolIso14443_4a] = &nfc_device_iso14443_4a, diff --git a/lib/nfc/protocols/nfc_device_defs.h b/lib/nfc/protocols/nfc_device_defs.h index f5ba2563f..e5d2707fd 100644 --- a/lib/nfc/protocols/nfc_device_defs.h +++ b/lib/nfc/protocols/nfc_device_defs.h @@ -6,7 +6,7 @@ extern "C" { #endif -extern const NfcDeviceBase* nfc_devices[]; +extern const NfcDeviceBase* const nfc_devices[]; #ifdef __cplusplus } diff --git a/lib/nfc/protocols/nfc_listener_defs.c b/lib/nfc/protocols/nfc_listener_defs.c index b0c24023f..475ff2376 100644 --- a/lib/nfc/protocols/nfc_listener_defs.c +++ b/lib/nfc/protocols/nfc_listener_defs.c @@ -9,7 +9,7 @@ #include #include -const NfcListenerBase* nfc_listeners_api[NfcProtocolNum] = { +const NfcListenerBase* const nfc_listeners_api[NfcProtocolNum] = { [NfcProtocolIso14443_3a] = &nfc_listener_iso14443_3a, [NfcProtocolIso14443_3b] = NULL, [NfcProtocolIso14443_4a] = &nfc_listener_iso14443_4a, diff --git a/lib/nfc/protocols/nfc_listener_defs.h b/lib/nfc/protocols/nfc_listener_defs.h index 4d88cc098..7bd4b49b0 100644 --- a/lib/nfc/protocols/nfc_listener_defs.h +++ b/lib/nfc/protocols/nfc_listener_defs.h @@ -7,7 +7,7 @@ extern "C" { #endif -extern const NfcListenerBase* nfc_listeners_api[NfcProtocolNum]; +extern const NfcListenerBase* const nfc_listeners_api[NfcProtocolNum]; #ifdef __cplusplus } diff --git a/lib/nfc/protocols/nfc_poller_defs.c b/lib/nfc/protocols/nfc_poller_defs.c index cbbe145db..69ecf54df 100644 --- a/lib/nfc/protocols/nfc_poller_defs.c +++ b/lib/nfc/protocols/nfc_poller_defs.c @@ -15,7 +15,7 @@ #include #include -const NfcPollerBase* nfc_pollers_api[NfcProtocolNum] = { +const NfcPollerBase* const nfc_pollers_api[NfcProtocolNum] = { [NfcProtocolIso14443_3a] = &nfc_poller_iso14443_3a, [NfcProtocolIso14443_3b] = &nfc_poller_iso14443_3b, [NfcProtocolIso14443_4a] = &nfc_poller_iso14443_4a, diff --git a/lib/nfc/protocols/nfc_poller_defs.h b/lib/nfc/protocols/nfc_poller_defs.h index a406a5f08..0156cc8b2 100644 --- a/lib/nfc/protocols/nfc_poller_defs.h +++ b/lib/nfc/protocols/nfc_poller_defs.h @@ -7,7 +7,7 @@ extern "C" { #endif -extern const NfcPollerBase* nfc_pollers_api[NfcProtocolNum]; +extern const NfcPollerBase* const nfc_pollers_api[NfcProtocolNum]; #ifdef __cplusplus } diff --git a/lib/subghz/protocols/came.c b/lib/subghz/protocols/came.c index 645f5eee1..bbe3e487f 100644 --- a/lib/subghz/protocols/came.c +++ b/lib/subghz/protocols/came.c @@ -14,12 +14,13 @@ #define TAG "SubGhzProtocolCame" -#define CAME_12_COUNT_BIT 12 -#define CAME_24_COUNT_BIT 24 -#define PRASTEL_COUNT_BIT 25 -#define PRASTEL_NAME "Prastel" -#define AIRFORCE_COUNT_BIT 18 -#define AIRFORCE_NAME "Airforce" +#define CAME_12_COUNT_BIT 12 +#define CAME_24_COUNT_BIT 24 +#define PRASTEL_25_COUNT_BIT 25 +#define PRASTEL_42_COUNT_BIT 42 +#define PRASTEL_NAME "Prastel" +#define AIRFORCE_COUNT_BIT 18 +#define AIRFORCE_NAME "Airforce" static const SubGhzBlockConst subghz_protocol_came_const = { .te_short = 320, @@ -123,6 +124,7 @@ static bool subghz_protocol_encoder_came_get_upload(SubGhzProtocolEncoderCame* i switch(instance->generic.data_count_bit) { case CAME_24_COUNT_BIT: + case PRASTEL_42_COUNT_BIT: // CAME 24 Bit = 24320 us header_te = 76; break; @@ -131,7 +133,7 @@ static bool subghz_protocol_encoder_came_get_upload(SubGhzProtocolEncoderCame* i // CAME 12 Bit Original only! and Airforce protocol = 15040 us header_te = 47; break; - case PRASTEL_COUNT_BIT: + case PRASTEL_25_COUNT_BIT: // PRASTEL = 11520 us header_te = 36; break; @@ -174,7 +176,7 @@ SubGhzProtocolStatus if(ret != SubGhzProtocolStatusOk) { break; } - if(instance->generic.data_count_bit > PRASTEL_COUNT_BIT) { + if(instance->generic.data_count_bit > PRASTEL_42_COUNT_BIT) { FURI_LOG_E(TAG, "Wrong number of bits in key"); ret = SubGhzProtocolStatusErrorValueBitCount; break; @@ -268,7 +270,8 @@ void subghz_protocol_decoder_came_feed(void* context, bool level, uint32_t durat if((instance->decoder.decode_count_bit == subghz_protocol_came_const.min_count_bit_for_found) || (instance->decoder.decode_count_bit == AIRFORCE_COUNT_BIT) || - (instance->decoder.decode_count_bit == PRASTEL_COUNT_BIT) || + (instance->decoder.decode_count_bit == PRASTEL_25_COUNT_BIT) || + (instance->decoder.decode_count_bit == PRASTEL_42_COUNT_BIT) || (instance->decoder.decode_count_bit == CAME_24_COUNT_BIT)) { instance->generic.serial = 0x0; instance->generic.btn = 0x0; @@ -337,7 +340,7 @@ SubGhzProtocolStatus if(ret != SubGhzProtocolStatusOk) { break; } - if(instance->generic.data_count_bit > PRASTEL_COUNT_BIT) { + if(instance->generic.data_count_bit > PRASTEL_42_COUNT_BIT) { FURI_LOG_E(TAG, "Wrong number of bits in key"); ret = SubGhzProtocolStatusErrorValueBitCount; break; @@ -350,23 +353,30 @@ void subghz_protocol_decoder_came_get_string(void* context, FuriString* output) furi_assert(context); SubGhzProtocolDecoderCame* instance = context; - uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff; + uint32_t code_found_lo = instance->generic.data & 0x000003ffffffffff; uint64_t code_found_reverse = subghz_protocol_blocks_reverse_key( instance->generic.data, instance->generic.data_count_bit); - uint32_t code_found_reverse_lo = code_found_reverse & 0x00000000ffffffff; + uint32_t code_found_reverse_lo = code_found_reverse & 0x000003ffffffffff; + + const char* name = instance->generic.protocol_name; + switch(instance->generic.data_count_bit) { + case PRASTEL_25_COUNT_BIT: + case PRASTEL_42_COUNT_BIT: + name = PRASTEL_NAME; + break; + case AIRFORCE_COUNT_BIT: + name = AIRFORCE_NAME; + break; + } furi_string_cat_printf( output, "%s %dbit\r\n" "Key:0x%08lX\r\n" "Yek:0x%08lX\r\n", - (instance->generic.data_count_bit == PRASTEL_COUNT_BIT ? - PRASTEL_NAME : - (instance->generic.data_count_bit == AIRFORCE_COUNT_BIT ? - AIRFORCE_NAME : - instance->generic.protocol_name)), + name, instance->generic.data_count_bit, code_found_lo, code_found_reverse_lo); diff --git a/lib/subghz/protocols/protocol_items.c b/lib/subghz/protocols/protocol_items.c index 0d9c2a088..d7a14cd1c 100644 --- a/lib/subghz/protocols/protocol_items.c +++ b/lib/subghz/protocols/protocol_items.c @@ -1,6 +1,6 @@ #include "protocol_items.h" // IWYU pragma: keep -const SubGhzProtocol* subghz_protocol_registry_items[] = { +const SubGhzProtocol* const subghz_protocol_registry_items[] = { &subghz_protocol_gate_tx, &subghz_protocol_keeloq, &subghz_protocol_star_line, diff --git a/lib/subghz/registry.h b/lib/subghz/registry.h index 8529c1097..8de376b16 100644 --- a/lib/subghz/registry.h +++ b/lib/subghz/registry.h @@ -12,7 +12,7 @@ typedef struct SubGhzProtocolRegistry SubGhzProtocolRegistry; typedef struct SubGhzProtocol SubGhzProtocol; struct SubGhzProtocolRegistry { - const SubGhzProtocol** items; + const SubGhzProtocol* const* items; const size_t size; }; diff --git a/lib/toolbox/SConscript b/lib/toolbox/SConscript index 8a1c4a8c5..ad368e2a1 100644 --- a/lib/toolbox/SConscript +++ b/lib/toolbox/SConscript @@ -12,6 +12,10 @@ env.Append( ], SDK_HEADERS=[ File("api_lock.h"), + File("cli/cli_ansi.h"), + File("cli/cli_command.h"), + File("cli/cli_registry.h"), + File("cli/shell/cli_shell.h"), File("compress.h"), File("manchester_decoder.h"), File("manchester_encoder.h"), diff --git a/lib/toolbox/cli/cli_ansi.c b/lib/toolbox/cli/cli_ansi.c new file mode 100644 index 000000000..81167643b --- /dev/null +++ b/lib/toolbox/cli/cli_ansi.c @@ -0,0 +1,123 @@ +#include "cli_ansi.h" + +typedef enum { + CliAnsiParserStateInitial, + CliAnsiParserStateEscape, + CliAnsiParserStateEscapeBrace, + CliAnsiParserStateEscapeBraceOne, + CliAnsiParserStateEscapeBraceOneSemicolon, + CliAnsiParserStateEscapeBraceOneSemicolonModifiers, +} CliAnsiParserState; + +struct CliAnsiParser { + CliAnsiParserState state; + CliModKey modifiers; +}; + +CliAnsiParser* cli_ansi_parser_alloc(void) { + CliAnsiParser* parser = malloc(sizeof(CliAnsiParser)); + return parser; +} + +void cli_ansi_parser_free(CliAnsiParser* parser) { + free(parser); +} + +/** + * @brief Converts a single character representing a special key into the enum + * representation + */ +static CliKey cli_ansi_key_from_mnemonic(char c) { + switch(c) { + case 'A': + return CliKeyUp; + case 'B': + return CliKeyDown; + case 'C': + return CliKeyRight; + case 'D': + return CliKeyLeft; + case 'F': + return CliKeyEnd; + case 'H': + return CliKeyHome; + default: + return CliKeyUnrecognized; + } +} + +#define PARSER_RESET_AND_RETURN(parser, modifiers_val, key_val) \ + do { \ + parser->state = CliAnsiParserStateInitial; \ + return (CliAnsiParserResult){ \ + .is_done = true, \ + .result = (CliKeyCombo){ \ + .modifiers = modifiers_val, \ + .key = key_val, \ + }}; \ + } while(0); + +CliAnsiParserResult cli_ansi_parser_feed(CliAnsiParser* parser, char c) { + switch(parser->state) { + case CliAnsiParserStateInitial: + // -> + if(c != CliKeyEsc) PARSER_RESET_AND_RETURN(parser, CliModKeyNo, c); // -V1048 + + // ... + parser->state = CliAnsiParserStateEscape; + break; + + case CliAnsiParserStateEscape: + // -> + if(c == CliKeyEsc) PARSER_RESET_AND_RETURN(parser, CliModKeyNo, c); + + // -> Alt + + if(c != '[') PARSER_RESET_AND_RETURN(parser, CliModKeyAlt, c); + + // [ ... + parser->state = CliAnsiParserStateEscapeBrace; + break; + + case CliAnsiParserStateEscapeBrace: + // [ -> + if(c != '1') PARSER_RESET_AND_RETURN(parser, CliModKeyNo, cli_ansi_key_from_mnemonic(c)); + + // [ 1 ... + parser->state = CliAnsiParserStateEscapeBraceOne; + break; + + case CliAnsiParserStateEscapeBraceOne: + // [ 1 -> error + if(c != ';') PARSER_RESET_AND_RETURN(parser, CliModKeyNo, CliKeyUnrecognized); + + // [ 1 ; ... + parser->state = CliAnsiParserStateEscapeBraceOneSemicolon; + break; + + case CliAnsiParserStateEscapeBraceOneSemicolon: + // [ 1 ; ... + parser->modifiers = (c - '0'); + parser->modifiers &= ~1; + parser->state = CliAnsiParserStateEscapeBraceOneSemicolonModifiers; + break; + + case CliAnsiParserStateEscapeBraceOneSemicolonModifiers: + // [ 1 ; -> + + PARSER_RESET_AND_RETURN(parser, parser->modifiers, cli_ansi_key_from_mnemonic(c)); + } + + return (CliAnsiParserResult){.is_done = false}; +} + +CliAnsiParserResult cli_ansi_parser_feed_timeout(CliAnsiParser* parser) { + CliAnsiParserResult result = {.is_done = false}; + + if(parser->state == CliAnsiParserStateEscape) { + result.is_done = true; + result.result.key = CliKeyEsc; + result.result.modifiers = CliModKeyNo; + } + + parser->state = CliAnsiParserStateInitial; + return result; +} diff --git a/lib/toolbox/cli/cli_ansi.h b/lib/toolbox/cli/cli_ansi.h new file mode 100644 index 000000000..04b6d7759 --- /dev/null +++ b/lib/toolbox/cli/cli_ansi.h @@ -0,0 +1,153 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// text styling + +#define ANSI_RESET "\e[0m" +#define ANSI_BOLD "\e[1m" +#define ANSI_FAINT "\e[2m" +#define ANSI_INVERT "\e[7m" + +#define ANSI_FG_BLACK "\e[30m" +#define ANSI_FG_RED "\e[31m" +#define ANSI_FG_GREEN "\e[32m" +#define ANSI_FG_YELLOW "\e[33m" +#define ANSI_FG_BLUE "\e[34m" +#define ANSI_FG_MAGENTA "\e[35m" +#define ANSI_FG_CYAN "\e[36m" +#define ANSI_FG_WHITE "\e[37m" +#define ANSI_FG_BR_BLACK "\e[90m" +#define ANSI_FG_BR_RED "\e[91m" +#define ANSI_FG_BR_GREEN "\e[92m" +#define ANSI_FG_BR_YELLOW "\e[93m" +#define ANSI_FG_BR_BLUE "\e[94m" +#define ANSI_FG_BR_MAGENTA "\e[95m" +#define ANSI_FG_BR_CYAN "\e[96m" +#define ANSI_FG_BR_WHITE "\e[97m" + +#define ANSI_BG_BLACK "\e[40m" +#define ANSI_BG_RED "\e[41m" +#define ANSI_BG_GREEN "\e[42m" +#define ANSI_BG_YELLOW "\e[43m" +#define ANSI_BG_BLUE "\e[44m" +#define ANSI_BG_MAGENTA "\e[45m" +#define ANSI_BG_CYAN "\e[46m" +#define ANSI_BG_WHITE "\e[47m" +#define ANSI_BG_BR_BLACK "\e[100m" +#define ANSI_BG_BR_RED "\e[101m" +#define ANSI_BG_BR_GREEN "\e[102m" +#define ANSI_BG_BR_YELLOW "\e[103m" +#define ANSI_BG_BR_BLUE "\e[104m" +#define ANSI_BG_BR_MAGENTA "\e[105m" +#define ANSI_BG_BR_CYAN "\e[106m" +#define ANSI_BG_BR_WHITE "\e[107m" + +#define ANSI_FLIPPER_BRAND_ORANGE "\e[38;2;255;130;0m" + +// cursor positioning + +#define ANSI_CURSOR_UP_BY(rows) "\e[" rows "A" +#define ANSI_CURSOR_DOWN_BY(rows) "\e[" rows "B" +#define ANSI_CURSOR_RIGHT_BY(cols) "\e[" cols "C" +#define ANSI_CURSOR_LEFT_BY(cols) "\e[" cols "D" +#define ANSI_CURSOR_DOWN_BY_AND_FIRST_COLUMN(rows) "\e[" rows "E" +#define ANSI_CURSOR_UP_BY_AND_FIRST_COLUMN(rows) "\e[" rows "F" +#define ANSI_CURSOR_HOR_POS(pos) "\e[" pos "G" +#define ANSI_CURSOR_POS(row, col) "\e[" row ";" col "H" + +// erasing + +#define ANSI_ERASE_FROM_CURSOR_TO_END "0" +#define ANSI_ERASE_FROM_START_TO_CURSOR "1" +#define ANSI_ERASE_ENTIRE "2" + +#define ANSI_ERASE_DISPLAY(portion) "\e[" portion "J" +#define ANSI_ERASE_LINE(portion) "\e[" portion "K" +#define ANSI_ERASE_SCROLLBACK_BUFFER ANSI_ERASE_DISPLAY("3") + +// misc + +#define ANSI_INSERT_MODE_ENABLE "\e[4h" +#define ANSI_INSERT_MODE_DISABLE "\e[4l" + +typedef enum { + CliKeyUnrecognized = 0, + + CliKeySOH = 0x01, + CliKeyETX = 0x03, + CliKeyEOT = 0x04, + CliKeyBell = 0x07, + CliKeyBackspace = 0x08, + CliKeyTab = 0x09, + CliKeyLF = 0x0A, + CliKeyFF = 0x0C, + CliKeyCR = 0x0D, + CliKeyETB = 0x17, + CliKeyEsc = 0x1B, + CliKeyUS = 0x1F, + CliKeySpace = 0x20, + CliKeyDEL = 0x7F, + + CliKeySpecial = 0x80, + CliKeyLeft, + CliKeyRight, + CliKeyUp, + CliKeyDown, + CliKeyHome, + CliKeyEnd, +} CliKey; + +typedef enum { + CliModKeyNo = 0, + CliModKeyAlt = 2, + CliModKeyCtrl = 4, + CliModKeyMeta = 8, +} CliModKey; + +typedef struct { + CliModKey modifiers; + CliKey key; +} CliKeyCombo; + +typedef struct CliAnsiParser CliAnsiParser; + +typedef struct { + bool is_done; + CliKeyCombo result; +} CliAnsiParserResult; + +/** + * @brief Allocates an ANSI parser + */ +CliAnsiParser* cli_ansi_parser_alloc(void); + +/** + * @brief Frees an ANSI parser + */ +void cli_ansi_parser_free(CliAnsiParser* parser); + +/** + * @brief Feeds an ANSI parser a character + */ +CliAnsiParserResult cli_ansi_parser_feed(CliAnsiParser* parser, char c); + +/** + * @brief Feeds an ANSI parser a timeout event + * + * As a user of the ANSI parser API, you are responsible for calling this + * function some time after the last character was fed into the parser. The + * recommended timeout is about 10 ms. The exact value does not matter as long + * as it is small enough for the user not notice a delay, but big enough that + * when a terminal is sending an escape sequence, this function does not get + * called in between the characters of the sequence. + */ +CliAnsiParserResult cli_ansi_parser_feed_timeout(CliAnsiParser* parser); + +#ifdef __cplusplus +} +#endif diff --git a/lib/toolbox/cli/cli_command.c b/lib/toolbox/cli/cli_command.c new file mode 100644 index 000000000..a3c9ff292 --- /dev/null +++ b/lib/toolbox/cli/cli_command.c @@ -0,0 +1,17 @@ +#include "cli_command.h" +#include "cli_ansi.h" + +bool cli_is_pipe_broken_or_is_etx_next_char(PipeSide* side) { + if(pipe_state(side) == PipeStateBroken) return true; + if(!pipe_bytes_available(side)) return false; + char c = getchar(); + return c == CliKeyETX; +} + +void cli_print_usage(const char* cmd, const char* usage, const char* arg) { + furi_check(cmd); + furi_check(arg); + furi_check(usage); + + printf("%s: illegal option -- %s\r\nusage: %s %s", cmd, arg, cmd, usage); +} diff --git a/lib/toolbox/cli/cli_command.h b/lib/toolbox/cli/cli_command.h new file mode 100644 index 000000000..2d1d851d6 --- /dev/null +++ b/lib/toolbox/cli/cli_command.h @@ -0,0 +1,103 @@ +/** + * @file cli_command.h + * Command metadata and helpers + */ + +#pragma once + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define CLI_PLUGIN_API_VERSION 1 + +typedef enum { + CliCommandFlagDefault = 0, /**< Default */ + CliCommandFlagParallelSafe = (1 << 0), /**< Safe to run in parallel with other apps */ + CliCommandFlagInsomniaSafe = (1 << 1), /**< Safe to run with insomnia mode on */ + CliCommandFlagDontAttachStdio = (1 << 2), /**< Do no attach I/O pipe to thread stdio */ + CliCommandFlagUseShellThread = + (1 + << 3), /**< Don't start a separate thread to run the command in. Incompatible with DontAttachStdio */ + + // internal flags (do not set them yourselves!) + + CliCommandFlagExternal = (1 << 4), /**< The command comes from a .fal file */ +} CliCommandFlag; + +/** + * @brief CLI command execution callback pointer + * + * This callback will be called from a separate thread spawned just for your + * command. The pipe will be installed as the thread's stdio, so you can use + * `printf`, `getchar` and other standard functions to communicate with the + * user. + * + * @param [in] pipe Pipe that can be used to send and receive data. If + * `CliCommandFlagDontAttachStdio` was not set, you can + * also use standard C functions (printf, getc, etc.) to + * access this pipe. + * @param [in] args String with what was passed after the command + * @param [in] context Whatever you provided to `cli_add_command` + */ +typedef void (*CliCommandExecuteCallback)(PipeSide* pipe, FuriString* args, void* context); + +typedef struct { + char* name; + CliCommandExecuteCallback execute_callback; + CliCommandFlag flags; + size_t stack_depth; +} CliCommandDescriptor; + +/** + * @brief Configuration for locating external commands + */ +typedef struct { + const char* search_directory; // +#include + +#define TAG "cli" + +struct CliRegistry { + CliCommandTree_t commands; + FuriMutex* mutex; +}; + +CliRegistry* cli_registry_alloc(void) { + CliRegistry* registry = malloc(sizeof(CliRegistry)); + CliCommandTree_init(registry->commands); + registry->mutex = furi_mutex_alloc(FuriMutexTypeRecursive); + return registry; +} + +void cli_registry_free(CliRegistry* registry) { + furi_check(furi_mutex_acquire(registry->mutex, FuriWaitForever) == FuriStatusOk); + furi_mutex_free(registry->mutex); + CliCommandTree_clear(registry->commands); + free(registry); +} + +void cli_registry_add_command( + CliRegistry* registry, + const char* name, + CliCommandFlag flags, + CliCommandExecuteCallback callback, + void* context) { + cli_registry_add_command_ex( + registry, name, flags, callback, context, CLI_BUILTIN_COMMAND_STACK_SIZE); +} + +void cli_registry_add_command_ex( + CliRegistry* registry, + const char* name, + CliCommandFlag flags, + CliCommandExecuteCallback callback, + void* context, + size_t stack_size) { + furi_check(registry); + furi_check(name); + furi_check(callback); + + // the shell always attaches the pipe to the stdio, thus both flags can't be used at once + if(flags & CliCommandFlagUseShellThread) furi_check(!(flags & CliCommandFlagDontAttachStdio)); + + FuriString* name_str; + name_str = furi_string_alloc_set(name); + // command cannot contain spaces + furi_check(furi_string_search_char(name_str, ' ') == FURI_STRING_FAILURE); + + CliRegistryCommand command = { + .context = context, + .execute_callback = callback, + .flags = flags, + .stack_depth = stack_size, + }; + + furi_check(furi_mutex_acquire(registry->mutex, FuriWaitForever) == FuriStatusOk); + CliCommandTree_set_at(registry->commands, name_str, command); + furi_check(furi_mutex_release(registry->mutex) == FuriStatusOk); + + furi_string_free(name_str); +} + +void cli_registry_delete_command(CliRegistry* registry, const char* name) { + furi_check(registry); + FuriString* name_str; + name_str = furi_string_alloc_set(name); + furi_string_trim(name_str); + + size_t name_replace; + do { + name_replace = furi_string_replace(name_str, " ", "_"); + } while(name_replace != FURI_STRING_FAILURE); + + furi_check(furi_mutex_acquire(registry->mutex, FuriWaitForever) == FuriStatusOk); + CliCommandTree_erase(registry->commands, name_str); + furi_check(furi_mutex_release(registry->mutex) == FuriStatusOk); + + furi_string_free(name_str); +} + +bool cli_registry_get_command( + CliRegistry* registry, + FuriString* command, + CliRegistryCommand* result) { + furi_assert(registry); + furi_check(furi_mutex_acquire(registry->mutex, FuriWaitForever) == FuriStatusOk); + CliRegistryCommand* data = CliCommandTree_get(registry->commands, command); + if(data) *result = *data; + + furi_check(furi_mutex_release(registry->mutex) == FuriStatusOk); + + return !!data; +} + +void cli_registry_remove_external_commands(CliRegistry* registry) { + furi_check(registry); + furi_check(furi_mutex_acquire(registry->mutex, FuriWaitForever) == FuriStatusOk); + + // FIXME FL-3977: memory leak somewhere within this function + + CliCommandTree_t internal_cmds; + CliCommandTree_init(internal_cmds); + for + M_EACH(item, registry->commands, CliCommandTree_t) { + if(!(item->value_ptr->flags & CliCommandFlagExternal)) + CliCommandTree_set_at(internal_cmds, *item->key_ptr, *item->value_ptr); + } + CliCommandTree_move(registry->commands, internal_cmds); + + furi_check(furi_mutex_release(registry->mutex) == FuriStatusOk); +} + +void cli_registry_reload_external_commands( + CliRegistry* registry, + const CliCommandExternalConfig* config) { + furi_check(registry); + furi_check(furi_mutex_acquire(registry->mutex, FuriWaitForever) == FuriStatusOk); + FURI_LOG_D(TAG, "Reloading ext commands"); + + cli_registry_remove_external_commands(registry); + + // iterate over files in plugin directory + Storage* storage = furi_record_open(RECORD_STORAGE); + File* plugin_dir = storage_file_alloc(storage); + + if(storage_dir_open(plugin_dir, config->search_directory)) { + char plugin_filename[64]; + FuriString* plugin_name = furi_string_alloc(); + + while(storage_dir_read(plugin_dir, NULL, plugin_filename, sizeof(plugin_filename))) { + FURI_LOG_T(TAG, "Plugin: %s", plugin_filename); + furi_string_set_str(plugin_name, plugin_filename); + + furi_check(furi_string_end_with_str(plugin_name, ".fal")); + furi_string_replace_all_str(plugin_name, ".fal", ""); + furi_check(furi_string_start_with_str(plugin_name, config->fal_prefix)); + furi_string_replace_at(plugin_name, 0, strlen(config->fal_prefix), ""); + + CliRegistryCommand command = { + .context = NULL, + .execute_callback = NULL, + .flags = CliCommandFlagExternal, + }; + CliCommandTree_set_at(registry->commands, plugin_name, command); + } + + furi_string_free(plugin_name); + } + + storage_dir_close(plugin_dir); + storage_file_free(plugin_dir); + furi_record_close(RECORD_STORAGE); + + FURI_LOG_D(TAG, "Done reloading ext commands"); + furi_check(furi_mutex_release(registry->mutex) == FuriStatusOk); +} + +void cli_registry_lock(CliRegistry* registry) { + furi_assert(registry); + furi_check(furi_mutex_acquire(registry->mutex, FuriWaitForever) == FuriStatusOk); +} + +void cli_registry_unlock(CliRegistry* registry) { + furi_assert(registry); + furi_mutex_release(registry->mutex); +} + +CliCommandTree_t* cli_registry_get_commands(CliRegistry* registry) { + furi_assert(registry); + return ®istry->commands; +} diff --git a/lib/toolbox/cli/cli_registry.h b/lib/toolbox/cli/cli_registry.h new file mode 100644 index 000000000..44650e79b --- /dev/null +++ b/lib/toolbox/cli/cli_registry.h @@ -0,0 +1,92 @@ +/** + * @file cli_registry.h + * API for registering commands with a CLI shell + */ + +#pragma once + +#include +#include +#include +#include "cli_command.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct CliRegistry CliRegistry; + +/** + * @brief Allocates a `CliRegistry`. + */ +CliRegistry* cli_registry_alloc(void); + +/** + * @brief Frees a `CliRegistry`. + */ +void cli_registry_free(CliRegistry* registry); + +/** + * @brief Registers a command with the registry. Provides less options than the + * `_ex` counterpart. + * + * @param [in] registry Pointer to registry instance + * @param [in] name Command name + * @param [in] flags see CliCommandFlag + * @param [in] callback Callback function + * @param [in] context Custom context + */ +void cli_registry_add_command( + CliRegistry* registry, + const char* name, + CliCommandFlag flags, + CliCommandExecuteCallback callback, + void* context); + +/** + * @brief Registers a command with the registry. Provides more options than the + * non-`_ex` counterpart. + * + * @param [in] registry Pointer to registry instance + * @param [in] name Command name + * @param [in] flags see CliCommandFlag + * @param [in] callback Callback function + * @param [in] context Custom context + * @param [in] stack_size Thread stack size + */ +void cli_registry_add_command_ex( + CliRegistry* registry, + const char* name, + CliCommandFlag flags, + CliCommandExecuteCallback callback, + void* context, + size_t stack_size); + +/** + * @brief Deletes a cli command + * + * @param [in] registry Pointer to registry instance + * @param [in] name Command name + */ +void cli_registry_delete_command(CliRegistry* registry, const char* name); + +/** + * @brief Unregisters all external commands + * + * @param [in] registry Pointer to registry instance + */ +void cli_registry_remove_external_commands(CliRegistry* registry); + +/** + * @brief Reloads the list of externally available commands + * + * @param [in] registry Pointer to registry instance + * @param [in] config See `CliCommandExternalConfig` + */ +void cli_registry_reload_external_commands( + CliRegistry* registry, + const CliCommandExternalConfig* config); + +#ifdef __cplusplus +} +#endif diff --git a/lib/toolbox/cli/cli_registry_i.h b/lib/toolbox/cli/cli_registry_i.h new file mode 100644 index 000000000..95b7c55da --- /dev/null +++ b/lib/toolbox/cli/cli_registry_i.h @@ -0,0 +1,55 @@ +/** + * @file cli_registry_i.h + * Internal API for getting commands registered with the CLI + */ + +#pragma once + +#include +#include +#include "cli_registry.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define CLI_BUILTIN_COMMAND_STACK_SIZE (4 * 1024U) + +typedef struct { + void* context; // +#include +#include +#include +#include +#include +#include +#include + +#define TAG "CliShell" + +#define ANSI_TIMEOUT_MS 10 + +typedef enum { + CliShellComponentCompletions, + CliShellComponentLine, + CliShellComponentMAX, //pipe); + pipe_attach_to_event_loop(cli_shell->pipe, cli_shell->event_loop); + pipe_set_callback_context(cli_shell->pipe, cli_shell); + pipe_set_data_arrived_callback(cli_shell->pipe, cli_shell_data_available, 0); + pipe_set_broken_callback(cli_shell->pipe, cli_shell_pipe_broken, 0); +} + +static void cli_shell_detach_pipe(CliShell* cli_shell) { + pipe_detach_from_event_loop(cli_shell->pipe); + furi_thread_set_stdin_callback(NULL, NULL); + furi_thread_set_stdout_callback(NULL, NULL); +} + +// ================= +// Built-in commands +// ================= + +void cli_command_reload_external(PipeSide* pipe, FuriString* args, void* context) { + UNUSED(pipe); + UNUSED(args); + CliShell* shell = context; + furi_check(shell->ext_config); + cli_registry_reload_external_commands(shell->registry, shell->ext_config); + printf("OK!"); +} + +void cli_command_help(PipeSide* pipe, FuriString* args, void* context) { + UNUSED(pipe); + UNUSED(args); + CliShell* shell = context; + CliRegistry* registry = shell->registry; + + const size_t columns = 3; + + printf("Available commands:\r\n" ANSI_FG_GREEN); + cli_registry_lock(registry); + CliCommandTree_t* commands = cli_registry_get_commands(registry); + size_t commands_count = CliCommandTree_size(*commands); + + CliCommandTree_it_t iterator; + CliCommandTree_it(iterator, *commands); + for(size_t i = 0; i < commands_count; i++) { + const CliCommandTree_itref_t* item = CliCommandTree_cref(iterator); + printf("%-30s", furi_string_get_cstr(*item->key_ptr)); + CliCommandTree_next(iterator); + + if(i % columns == columns - 1) printf("\r\n"); + } + + if(shell->ext_config) + printf( + ANSI_RESET + "\r\nIf you added a new external command and can't see it above, run `reload_ext_cmds`"); + printf(ANSI_RESET "\r\nFind out more: https://docs.flipper.net/development/cli"); + + cli_registry_unlock(registry); +} + +void cli_command_exit(PipeSide* pipe, FuriString* args, void* context) { + UNUSED(pipe); + UNUSED(args); + CliShell* shell = context; + cli_shell_line_set_about_to_exit(shell->components[CliShellComponentLine]); + furi_event_loop_stop(shell->event_loop); +} + +// ================== +// Internal functions +// ================== + +static int32_t cli_command_thread(void* context) { + CliCommandThreadData* thread_data = context; + if(!(thread_data->command->flags & CliCommandFlagDontAttachStdio)) + pipe_install_as_stdio(thread_data->pipe); + + thread_data->command->execute_callback( + thread_data->pipe, thread_data->args, thread_data->command->context); + + fflush(stdout); + return 0; +} + +void cli_shell_execute_command(CliShell* cli_shell, FuriString* command) { + // split command into command and args + size_t space = furi_string_search_char(command, ' '); + if(space == FURI_STRING_FAILURE) space = furi_string_size(command); + FuriString* command_name = furi_string_alloc_set(command); + furi_string_left(command_name, space); + FuriString* args = furi_string_alloc_set(command); + furi_string_right(args, space + 1); + + PluginManager* plugin_manager = NULL; + Loader* loader = furi_record_open(RECORD_LOADER); + bool loader_locked = false; + CliRegistryCommand command_data; + + do { + // find handler + if(!cli_registry_get_command(cli_shell->registry, command_name, &command_data)) { + printf( + ANSI_FG_RED "could not find command `%s`, try `help`" ANSI_RESET, + furi_string_get_cstr(command_name)); + break; + } + + // load external command + if(command_data.flags & CliCommandFlagExternal) { + const CliCommandExternalConfig* ext_config = cli_shell->ext_config; + plugin_manager = plugin_manager_alloc( + ext_config->appid, CLI_PLUGIN_API_VERSION, firmware_api_interface); + FuriString* path = furi_string_alloc_printf( + "%s/%s%s.fal", + ext_config->search_directory, + ext_config->fal_prefix, + furi_string_get_cstr(command_name)); + uint32_t plugin_cnt_last = plugin_manager_get_count(plugin_manager); + PluginManagerError error = + plugin_manager_load_single(plugin_manager, furi_string_get_cstr(path)); + furi_string_free(path); + + if(error != PluginManagerErrorNone) { + printf(ANSI_FG_RED "failed to load external command" ANSI_RESET); + break; + } + + const CliCommandDescriptor* plugin = + plugin_manager_get_ep(plugin_manager, plugin_cnt_last); + furi_assert(plugin); + furi_check(furi_string_cmp_str(command_name, plugin->name) == 0); + command_data.execute_callback = plugin->execute_callback; + command_data.flags = plugin->flags | CliCommandFlagExternal; + command_data.stack_depth = plugin->stack_depth; + + // external commands have to run in an external thread + furi_check(!(command_data.flags & CliCommandFlagUseShellThread)); + } + + // lock loader + if(!(command_data.flags & CliCommandFlagParallelSafe)) { + loader_locked = loader_lock(loader); + if(!loader_locked) { + printf(ANSI_FG_RED + "this command cannot be run while an application is open" ANSI_RESET); + break; + } + } + + if(command_data.flags & CliCommandFlagUseShellThread) { + // run command in this thread + command_data.execute_callback(cli_shell->pipe, args, command_data.context); + } else { + // run command in separate thread + cli_shell_detach_pipe(cli_shell); + CliCommandThreadData thread_data = { + .command = &command_data, + .pipe = cli_shell->pipe, + .args = args, + }; + FuriThread* thread = furi_thread_alloc_ex( + furi_string_get_cstr(command_name), + command_data.stack_depth, + cli_command_thread, + &thread_data); + furi_thread_start(thread); + furi_thread_join(thread); + furi_thread_free(thread); + cli_shell_install_pipe(cli_shell); + } + } while(0); + + furi_string_free(command_name); + furi_string_free(args); + + // unlock loader + if(loader_locked) loader_unlock(loader); + furi_record_close(RECORD_LOADER); + + // unload external command + if(plugin_manager) plugin_manager_free(plugin_manager); +} + +const char* cli_shell_get_prompt(CliShell* cli_shell) { + return cli_shell->prompt; +} + +// ============== +// Event handlers +// ============== + +static void cli_shell_storage_event(const void* message, void* context) { + CliShell* cli_shell = context; + const StorageEvent* event = message; + + if(event->type == StorageEventTypeCardMount) { + CliShellStorageEvent cli_event = CliShellStorageEventMount; + furi_check( + furi_message_queue_put(cli_shell->storage_event_queue, &cli_event, 0) == FuriStatusOk); + } else if(event->type == StorageEventTypeCardUnmount) { + CliShellStorageEvent cli_event = CliShellStorageEventUnmount; + furi_check( + furi_message_queue_put(cli_shell->storage_event_queue, &cli_event, 0) == FuriStatusOk); + } +} + +static void cli_shell_storage_internal_event(FuriEventLoopObject* object, void* context) { + CliShell* cli_shell = context; + FuriMessageQueue* queue = object; + CliShellStorageEvent event; + furi_check(furi_message_queue_get(queue, &event, 0) == FuriStatusOk); + + if(event == CliShellStorageEventMount) { + cli_registry_reload_external_commands(cli_shell->registry, cli_shell->ext_config); + } else if(event == CliShellStorageEventUnmount) { + cli_registry_remove_external_commands(cli_shell->registry); + } else { + furi_crash(); + } +} + +static void + cli_shell_process_parser_result(CliShell* cli_shell, CliAnsiParserResult parse_result) { + if(!parse_result.is_done) return; + CliKeyCombo key_combo = parse_result.result; + if(key_combo.key == CliKeyUnrecognized) return; + + for(size_t i = 0; i < CliShellComponentMAX; i++) { // -V1008 + CliShellKeyComboSet* set = component_key_combo_sets[i]; + void* component_context = cli_shell->components[i]; + + for(size_t j = 0; j < set->count; j++) { + if(set->records[j].combo.modifiers == key_combo.modifiers && + set->records[j].combo.key == key_combo.key) + if(set->records[j].action(key_combo, component_context)) return; + } + + if(set->fallback) + if(set->fallback(key_combo, component_context)) return; + } +} + +static void cli_shell_pipe_broken(PipeSide* pipe, void* context) { + // allow commands to be processed before we stop the shell + if(pipe_bytes_available(pipe)) return; + + CliShell* cli_shell = context; + furi_event_loop_stop(cli_shell->event_loop); +} + +static void cli_shell_data_available(PipeSide* pipe, void* context) { + UNUSED(pipe); + CliShell* cli_shell = context; + + furi_event_loop_timer_start(cli_shell->ansi_parsing_timer, furi_ms_to_ticks(ANSI_TIMEOUT_MS)); + + // process ANSI escape sequences + int c = getchar(); + furi_assert(c >= 0); + cli_shell_process_parser_result(cli_shell, cli_ansi_parser_feed(cli_shell->ansi_parser, c)); +} + +static void cli_shell_timer_expired(void* context) { + CliShell* cli_shell = context; + cli_shell_process_parser_result( + cli_shell, cli_ansi_parser_feed_timeout(cli_shell->ansi_parser)); +} + +// =========== +// Thread code +// =========== + +static void cli_shell_init(CliShell* shell) { + cli_registry_add_command( + shell->registry, + "help", + CliCommandFlagUseShellThread | CliCommandFlagParallelSafe, + cli_command_help, + shell); + cli_registry_add_command( + shell->registry, + "?", + CliCommandFlagUseShellThread | CliCommandFlagParallelSafe, + cli_command_help, + shell); + cli_registry_add_command( + shell->registry, + "exit", + CliCommandFlagUseShellThread | CliCommandFlagParallelSafe, + cli_command_exit, + shell); + + if(shell->ext_config) { + cli_registry_add_command( + shell->registry, + "reload_ext_cmds", + CliCommandFlagUseShellThread, + cli_command_reload_external, + shell); + cli_registry_reload_external_commands(shell->registry, shell->ext_config); + } + + shell->components[CliShellComponentLine] = cli_shell_line_alloc(shell); + shell->components[CliShellComponentCompletions] = cli_shell_completions_alloc( + shell->registry, shell, shell->components[CliShellComponentLine]); + + shell->ansi_parser = cli_ansi_parser_alloc(); + + shell->event_loop = furi_event_loop_alloc(); + shell->ansi_parsing_timer = furi_event_loop_timer_alloc( + shell->event_loop, cli_shell_timer_expired, FuriEventLoopTimerTypeOnce, shell); + + shell->storage_event_queue = furi_message_queue_alloc(1, sizeof(CliShellStorageEvent)); + furi_event_loop_subscribe_message_queue( + shell->event_loop, + shell->storage_event_queue, + FuriEventLoopEventIn, + cli_shell_storage_internal_event, + shell); + shell->storage = furi_record_open(RECORD_STORAGE); + shell->storage_subscription = + furi_pubsub_subscribe(storage_get_pubsub(shell->storage), cli_shell_storage_event, shell); + + cli_shell_install_pipe(shell); +} + +static void cli_shell_deinit(CliShell* shell) { + furi_pubsub_unsubscribe(storage_get_pubsub(shell->storage), shell->storage_subscription); + furi_record_close(RECORD_STORAGE); + furi_event_loop_unsubscribe(shell->event_loop, shell->storage_event_queue); + furi_message_queue_free(shell->storage_event_queue); + + cli_shell_completions_free(shell->components[CliShellComponentCompletions]); + cli_shell_line_free(shell->components[CliShellComponentLine]); + + cli_shell_detach_pipe(shell); + furi_event_loop_timer_free(shell->ansi_parsing_timer); + furi_event_loop_free(shell->event_loop); + cli_ansi_parser_free(shell->ansi_parser); +} + +static int32_t cli_shell_thread(void* context) { + CliShell* shell = context; + + // Sometimes, the other side closes the pipe even before our thread is started. Although the + // rest of the code will eventually find this out if this check is removed, there's no point in + // wasting time. + if(pipe_state(shell->pipe) == PipeStateBroken) return 0; + + cli_shell_init(shell); + FURI_LOG_D(TAG, "Started"); + + shell->motd(shell->callback_context); + cli_shell_line_prompt(shell->components[CliShellComponentLine]); + + furi_event_loop_run(shell->event_loop); + + FURI_LOG_D(TAG, "Stopped"); + cli_shell_deinit(shell); + return 0; +} + +// ========== +// Public API +// ========== + +CliShell* cli_shell_alloc( + CliShellMotd motd, + void* context, + PipeSide* pipe, + CliRegistry* registry, + const CliCommandExternalConfig* ext_config) { + furi_check(motd); + furi_check(pipe); + furi_check(registry); + + CliShell* shell = malloc(sizeof(CliShell)); + *shell = (CliShell){ + .motd = motd, + .callback_context = context, + .pipe = pipe, + .registry = registry, + .ext_config = ext_config, + }; + + shell->thread = + furi_thread_alloc_ex("CliShell", CLI_SHELL_STACK_SIZE, cli_shell_thread, shell); + + return shell; +} + +void cli_shell_free(CliShell* shell) { + furi_check(shell); + furi_thread_free(shell->thread); + free(shell); +} + +void cli_shell_start(CliShell* shell) { + furi_check(shell); + furi_thread_start(shell->thread); +} + +void cli_shell_join(CliShell* shell) { + furi_check(shell); + furi_thread_join(shell->thread); +} + +void cli_shell_set_prompt(CliShell* shell, const char* prompt) { + furi_check(shell); + shell->prompt = prompt; +} diff --git a/lib/toolbox/cli/shell/cli_shell.h b/lib/toolbox/cli/shell/cli_shell.h new file mode 100644 index 000000000..74f71273e --- /dev/null +++ b/lib/toolbox/cli/shell/cli_shell.h @@ -0,0 +1,75 @@ +#pragma once + +#include +#include +#include "../cli_registry.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define CLI_SHELL_STACK_SIZE (4 * 1024U) + +typedef struct CliShell CliShell; + +/** + * Called from the shell thread to print the Message of the Day when the shell + * is started. + */ +typedef void (*CliShellMotd)(void* context); + +/** + * @brief Allocates a shell + * + * @param [in] motd Message of the Day callback + * @param [in] context Callback context + * @param [in] pipe Pipe side to be used by the shell + * @param [in] registry Command registry + * @param [in] ext_config External command configuration. See + * `CliCommandExternalConfig`. May be NULL if support for + * external commands is not required. + * + * @return Shell instance + */ +CliShell* cli_shell_alloc( + CliShellMotd motd, + void* context, + PipeSide* pipe, + CliRegistry* registry, + const CliCommandExternalConfig* ext_config); + +/** + * @brief Frees a shell + * + * @param [in] shell Shell instance + */ +void cli_shell_free(CliShell* shell); + +/** + * @brief Starts a shell + * + * The shell runs in a separate thread. This call is non-blocking. + * + * @param [in] shell Shell instance + */ +void cli_shell_start(CliShell* shell); + +/** + * @brief Joins the shell thread + * + * @warning This call is blocking. + * + * @param [in] shell Shell instance + */ +void cli_shell_join(CliShell* shell); + +/** + * @brief Sets optional text before prompt (`>:`) + * + * @param [in] shell Shell instance + */ +void cli_shell_set_prompt(CliShell* shell, const char* prompt); + +#ifdef __cplusplus +} +#endif diff --git a/lib/toolbox/cli/shell/cli_shell_completions.c b/lib/toolbox/cli/shell/cli_shell_completions.c new file mode 100644 index 000000000..823f91fb9 --- /dev/null +++ b/lib/toolbox/cli/shell/cli_shell_completions.c @@ -0,0 +1,366 @@ +#include "cli_shell_completions.h" + +ARRAY_DEF(CommandCompletions, FuriString*, FURI_STRING_OPLIST); // -V524 +#define M_OPL_CommandCompletions_t() ARRAY_OPLIST(CommandCompletions) + +struct CliShellCompletions { + CliRegistry* registry; + CliShell* shell; + CliShellLine* line; + CommandCompletions_t variants; + size_t selected; + bool is_displaying; +}; + +#define COMPLETION_COLUMNS 3 +#define COMPLETION_COLUMN_WIDTH "30" +#define COMPLETION_COLUMN_WIDTH_I 30 + +/** + * @brief Update for the completions menu + */ +typedef enum { + CliShellCompletionsActionOpen, + CliShellCompletionsActionClose, + CliShellCompletionsActionUp, + CliShellCompletionsActionDown, + CliShellCompletionsActionLeft, + CliShellCompletionsActionRight, + CliShellCompletionsActionSelect, + CliShellCompletionsActionSelectNoClose, +} CliShellCompletionsAction; + +typedef enum { + CliShellCompletionSegmentTypeCommand, + CliShellCompletionSegmentTypeArguments, +} CliShellCompletionSegmentType; + +typedef struct { + CliShellCompletionSegmentType type; + size_t start; + size_t length; +} CliShellCompletionSegment; + +// ========== +// Public API +// ========== + +CliShellCompletions* + cli_shell_completions_alloc(CliRegistry* registry, CliShell* shell, CliShellLine* line) { + CliShellCompletions* completions = malloc(sizeof(CliShellCompletions)); + + completions->registry = registry; + completions->shell = shell; + completions->line = line; + CommandCompletions_init(completions->variants); + + return completions; +} + +void cli_shell_completions_free(CliShellCompletions* completions) { + CommandCompletions_clear(completions->variants); + free(completions); +} + +// ======= +// Helpers +// ======= + +CliShellCompletionSegment cli_shell_completions_segment(CliShellCompletions* completions) { + furi_assert(completions); + CliShellCompletionSegment segment; + + FuriString* input = furi_string_alloc_set(cli_shell_line_get_editing(completions->line)); + furi_string_left(input, cli_shell_line_get_line_position(completions->line)); + + // find index of first non-space character + size_t first_non_space = 0; + while(1) { + size_t ret = furi_string_search_char(input, ' ', first_non_space); + if(ret == FURI_STRING_FAILURE) break; + if(ret - first_non_space > 1) break; + first_non_space++; + } + + size_t first_space_in_command = furi_string_search_char(input, ' ', first_non_space); + + if(first_space_in_command == FURI_STRING_FAILURE) { + segment.type = CliShellCompletionSegmentTypeCommand; + segment.start = first_non_space; + segment.length = furi_string_size(input) - first_non_space; + } else { + segment.type = CliShellCompletionSegmentTypeArguments; + segment.start = 0; + segment.length = 0; + // support removed, might reimplement in the future + } + + furi_string_free(input); + return segment; +} + +void cli_shell_completions_fill_variants(CliShellCompletions* completions) { + furi_assert(completions); + CommandCompletions_reset(completions->variants); + + CliShellCompletionSegment segment = cli_shell_completions_segment(completions); + FuriString* input = furi_string_alloc_set(cli_shell_line_get_editing(completions->line)); + furi_string_right(input, segment.start); + furi_string_left(input, segment.length); + + if(segment.type == CliShellCompletionSegmentTypeCommand) { + CliRegistry* registry = completions->registry; + cli_registry_lock(registry); + CliCommandTree_t* commands = cli_registry_get_commands(registry); + for + M_EACH(registered_command, *commands, CliCommandTree_t) { + FuriString* command_name = *registered_command->key_ptr; + if(furi_string_start_with(command_name, input)) { + CommandCompletions_push_back(completions->variants, command_name); + } + } + cli_registry_unlock(registry); + + } else { + // support removed, might reimplement in the future + } + + furi_string_free(input); +} + +static size_t cli_shell_completions_rows_at_column(CliShellCompletions* completions, size_t x) { + size_t completions_size = CommandCompletions_size(completions->variants); + size_t n_full_rows = completions_size / COMPLETION_COLUMNS; + size_t n_cols_in_last_row = completions_size % COMPLETION_COLUMNS; + size_t n_rows_at_x = n_full_rows + ((x >= n_cols_in_last_row) ? 0 : 1); + return n_rows_at_x; +} + +void cli_shell_completions_render( + CliShellCompletions* completions, + CliShellCompletionsAction action) { + furi_assert(completions); + if(action == CliShellCompletionsActionOpen) furi_check(!completions->is_displaying); + if(action == CliShellCompletionsActionClose) furi_check(completions->is_displaying); + + char prompt[64]; + cli_shell_line_format_prompt(completions->line, prompt, sizeof(prompt)); + + if(action == CliShellCompletionsActionOpen) { + cli_shell_completions_fill_variants(completions); + completions->selected = 0; + + if(CommandCompletions_size(completions->variants) == 1) { + cli_shell_completions_render(completions, CliShellCompletionsActionSelectNoClose); + return; + } + + // show completions menu (full re-render) + printf("\n\r"); + size_t position = 0; + for + M_EACH(completion, completions->variants, CommandCompletions_t) { + if(position == completions->selected) printf(ANSI_INVERT); + printf("%-" COMPLETION_COLUMN_WIDTH "s", furi_string_get_cstr(*completion)); + if(position == completions->selected) printf(ANSI_RESET); + if((position % COMPLETION_COLUMNS == COMPLETION_COLUMNS - 1) && + position != CommandCompletions_size(completions->variants)) { + printf("\r\n"); + } + position++; + } + + if(!position) { + printf(ANSI_FG_RED "no completions" ANSI_RESET); + } + + size_t total_rows = (position / COMPLETION_COLUMNS) + 1; + printf( + ANSI_ERASE_DISPLAY(ANSI_ERASE_FROM_CURSOR_TO_END) ANSI_CURSOR_UP_BY("%zu") + ANSI_CURSOR_HOR_POS("%zu"), + total_rows, + strlen(prompt) + cli_shell_line_get_line_position(completions->line) + 1); + + completions->is_displaying = true; + + } else if(action == CliShellCompletionsActionClose) { + // clear completions menu + printf( + ANSI_CURSOR_HOR_POS("%zu") ANSI_ERASE_DISPLAY(ANSI_ERASE_FROM_CURSOR_TO_END) + ANSI_CURSOR_HOR_POS("%zu"), + strlen(prompt) + furi_string_size(cli_shell_line_get_selected(completions->line)) + 1, + strlen(prompt) + cli_shell_line_get_line_position(completions->line) + 1); + completions->is_displaying = false; + + } else if( + action == CliShellCompletionsActionUp || action == CliShellCompletionsActionDown || + action == CliShellCompletionsActionLeft || action == CliShellCompletionsActionRight) { + if(CommandCompletions_empty_p(completions->variants)) return; + + // move selection + size_t completions_size = CommandCompletions_size(completions->variants); + size_t old_selection = completions->selected; + int n_columns = (completions_size >= COMPLETION_COLUMNS) ? COMPLETION_COLUMNS : + completions_size; + int selection_unclamped = old_selection; + if(action == CliShellCompletionsActionLeft) { + selection_unclamped--; + } else if(action == CliShellCompletionsActionRight) { + selection_unclamped++; + } else { + int selection_x = old_selection % COMPLETION_COLUMNS; + int selection_y_unclamped = old_selection / COMPLETION_COLUMNS; + if(action == CliShellCompletionsActionUp) selection_y_unclamped--; + if(action == CliShellCompletionsActionDown) selection_y_unclamped++; + size_t selection_y = 0; + if(selection_y_unclamped < 0) { + selection_x = CLAMP_WRAPAROUND(selection_x - 1, n_columns - 1, 0); + selection_y = + cli_shell_completions_rows_at_column(completions, selection_x) - 1; // -V537 + } else if( + (size_t)selection_y_unclamped > + cli_shell_completions_rows_at_column(completions, selection_x) - 1) { + selection_x = CLAMP_WRAPAROUND(selection_x + 1, n_columns - 1, 0); + selection_y = 0; + } else { + selection_y = selection_y_unclamped; + } + selection_unclamped = (selection_y * COMPLETION_COLUMNS) + selection_x; + } + size_t new_selection = CLAMP_WRAPAROUND(selection_unclamped, (int)completions_size - 1, 0); + completions->selected = new_selection; + + if(new_selection != old_selection) { + // determine selection coordinates relative to top-left of suggestion menu + size_t old_x = (old_selection % COMPLETION_COLUMNS) * COMPLETION_COLUMN_WIDTH_I; + size_t old_y = old_selection / COMPLETION_COLUMNS; + size_t new_x = (new_selection % COMPLETION_COLUMNS) * COMPLETION_COLUMN_WIDTH_I; + size_t new_y = new_selection / COMPLETION_COLUMNS; + printf("\n\r"); + + // print old selection in normal colors + if(old_y) printf(ANSI_CURSOR_DOWN_BY("%zu"), old_y); + printf(ANSI_CURSOR_HOR_POS("%zu"), old_x + 1); + printf( + "%-" COMPLETION_COLUMN_WIDTH "s", + furi_string_get_cstr( + *CommandCompletions_cget(completions->variants, old_selection))); + if(old_y) printf(ANSI_CURSOR_UP_BY("%zu"), old_y); + printf(ANSI_CURSOR_HOR_POS("1")); + + // print new selection in inverted colors + if(new_y) printf(ANSI_CURSOR_DOWN_BY("%zu"), new_y); + printf(ANSI_CURSOR_HOR_POS("%zu"), new_x + 1); + printf( + ANSI_INVERT "%-" COMPLETION_COLUMN_WIDTH "s" ANSI_RESET, + furi_string_get_cstr( + *CommandCompletions_cget(completions->variants, new_selection))); + + // return cursor + printf(ANSI_CURSOR_UP_BY("%zu"), new_y + 1); + printf( + ANSI_CURSOR_HOR_POS("%zu"), + strlen(prompt) + furi_string_size(cli_shell_line_get_selected(completions->line)) + + 1); + } + + } else if(action == CliShellCompletionsActionSelectNoClose) { + // insert selection into prompt + CliShellCompletionSegment segment = cli_shell_completions_segment(completions); + FuriString* input = cli_shell_line_get_selected(completions->line); + FuriString* completion = + *CommandCompletions_cget(completions->variants, completions->selected); + furi_string_replace_at( + input, segment.start, segment.length, furi_string_get_cstr(completion)); + printf( + ANSI_CURSOR_HOR_POS("%zu") "%s" ANSI_ERASE_LINE(ANSI_ERASE_FROM_CURSOR_TO_END), + strlen(prompt) + 1, + furi_string_get_cstr(input)); + + int position_change = (int)furi_string_size(completion) - (int)segment.length; + cli_shell_line_set_line_position( + completions->line, + MAX(0, (int)cli_shell_line_get_line_position(completions->line) + position_change)); + + } else if(action == CliShellCompletionsActionSelect) { + cli_shell_completions_render(completions, CliShellCompletionsActionSelectNoClose); + cli_shell_completions_render(completions, CliShellCompletionsActionClose); + + } else { + furi_crash(); + } + + fflush(stdout); +} + +// ============== +// Input handlers +// ============== + +static bool hide_if_open_and_continue_handling(CliKeyCombo combo, void* context) { + UNUSED(combo); + CliShellCompletions* completions = context; + if(completions->is_displaying) + cli_shell_completions_render(completions, CliShellCompletionsActionClose); + return false; // process other home events +} + +static bool key_combo_cr(CliKeyCombo combo, void* context) { + UNUSED(combo); + CliShellCompletions* completions = context; + if(!completions->is_displaying) return false; + cli_shell_completions_render(completions, CliShellCompletionsActionSelect); + return true; +} + +static bool key_combo_up_down(CliKeyCombo combo, void* context) { + CliShellCompletions* completions = context; + if(!completions->is_displaying) return false; + cli_shell_completions_render( + completions, + (combo.key == CliKeyUp) ? CliShellCompletionsActionUp : CliShellCompletionsActionDown); + return true; +} + +static bool key_combo_left_right(CliKeyCombo combo, void* context) { + CliShellCompletions* completions = context; + if(!completions->is_displaying) return false; + cli_shell_completions_render( + completions, + (combo.key == CliKeyLeft) ? CliShellCompletionsActionLeft : + CliShellCompletionsActionRight); + return true; +} + +static bool key_combo_tab(CliKeyCombo combo, void* context) { + UNUSED(combo); + CliShellCompletions* completions = context; + cli_shell_completions_render( + completions, + completions->is_displaying ? CliShellCompletionsActionRight : + CliShellCompletionsActionOpen); + return true; +} + +static bool key_combo_esc(CliKeyCombo combo, void* context) { + UNUSED(combo); + CliShellCompletions* completions = context; + if(!completions->is_displaying) return false; + cli_shell_completions_render(completions, CliShellCompletionsActionClose); + return true; +} + +CliShellKeyComboSet cli_shell_completions_key_combo_set = { + .fallback = hide_if_open_and_continue_handling, + .count = 7, + .records = + { + {{CliModKeyNo, CliKeyCR}, key_combo_cr}, + {{CliModKeyNo, CliKeyUp}, key_combo_up_down}, + {{CliModKeyNo, CliKeyDown}, key_combo_up_down}, + {{CliModKeyNo, CliKeyLeft}, key_combo_left_right}, + {{CliModKeyNo, CliKeyRight}, key_combo_left_right}, + {{CliModKeyNo, CliKeyTab}, key_combo_tab}, + {{CliModKeyNo, CliKeyEsc}, key_combo_esc}, + }, +}; diff --git a/lib/toolbox/cli/shell/cli_shell_completions.h b/lib/toolbox/cli/shell/cli_shell_completions.h new file mode 100644 index 000000000..d49a1982d --- /dev/null +++ b/lib/toolbox/cli/shell/cli_shell_completions.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include +#include "cli_shell_i.h" +#include "cli_shell_line.h" +#include "../cli_registry.h" +#include "../cli_registry_i.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct CliShellCompletions CliShellCompletions; + +CliShellCompletions* + cli_shell_completions_alloc(CliRegistry* registry, CliShell* shell, CliShellLine* line); + +void cli_shell_completions_free(CliShellCompletions* completions); + +extern CliShellKeyComboSet cli_shell_completions_key_combo_set; + +#ifdef __cplusplus +} +#endif diff --git a/lib/toolbox/cli/shell/cli_shell_i.h b/lib/toolbox/cli/shell/cli_shell_i.h new file mode 100644 index 000000000..0b676b7de --- /dev/null +++ b/lib/toolbox/cli/shell/cli_shell_i.h @@ -0,0 +1,34 @@ +#pragma once + +#include "../cli_ansi.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct CliShell CliShell; + +/** + * @brief Key combo handler + * @return true if the event was handled, false otherwise + */ +typedef bool (*CliShellKeyComboAction)(CliKeyCombo combo, void* context); + +typedef struct { + CliKeyCombo combo; + CliShellKeyComboAction action; +} CliShellKeyComboRecord; + +typedef struct { + CliShellKeyComboAction fallback; + size_t count; + CliShellKeyComboRecord records[]; +} CliShellKeyComboSet; + +void cli_shell_execute_command(CliShell* cli_shell, FuriString* command); + +const char* cli_shell_get_prompt(CliShell* cli_shell); + +#ifdef __cplusplus +} +#endif diff --git a/lib/toolbox/cli/shell/cli_shell_line.c b/lib/toolbox/cli/shell/cli_shell_line.c new file mode 100644 index 000000000..4826ba252 --- /dev/null +++ b/lib/toolbox/cli/shell/cli_shell_line.c @@ -0,0 +1,378 @@ +#include "cli_shell_line.h" + +#define HISTORY_DEPTH 10 + +struct CliShellLine { + size_t history_position; + size_t line_position; + FuriString* history[HISTORY_DEPTH]; + size_t history_entries; + CliShell* shell; + bool about_to_exit; +}; + +// ========== +// Public API +// ========== + +CliShellLine* cli_shell_line_alloc(CliShell* shell) { + CliShellLine* line = malloc(sizeof(CliShellLine)); + line->shell = shell; + + line->history[0] = furi_string_alloc(); + line->history_entries = 1; + + return line; +} + +void cli_shell_line_free(CliShellLine* line) { + for(size_t i = 0; i < line->history_entries; i++) + furi_string_free(line->history[i]); + + free(line); +} + +FuriString* cli_shell_line_get_selected(CliShellLine* line) { + return line->history[line->history_position]; +} + +FuriString* cli_shell_line_get_editing(CliShellLine* line) { + return line->history[0]; +} + +void cli_shell_line_format_prompt(CliShellLine* line, char* buf, size_t length) { + UNUSED(line); + const char* prompt = cli_shell_get_prompt(line->shell); + snprintf(buf, length - 1, "%s>: ", prompt ? prompt : ""); +} + +size_t cli_shell_line_prompt_length(CliShellLine* line) { + char buffer[128]; + cli_shell_line_format_prompt(line, buffer, sizeof(buffer)); + return strlen(buffer); +} + +void cli_shell_line_prompt(CliShellLine* line) { + char buffer[32]; + cli_shell_line_format_prompt(line, buffer, sizeof(buffer)); + printf("\r\n%s", buffer); + fflush(stdout); +} + +void cli_shell_line_ensure_not_overwriting_history(CliShellLine* line) { + if(line->history_position > 0) { + FuriString* source = cli_shell_line_get_selected(line); + FuriString* destination = cli_shell_line_get_editing(line); + furi_string_set(destination, source); + line->history_position = 0; + } +} + +void cli_shell_line_set_about_to_exit(CliShellLine* line) { + line->about_to_exit = true; +} + +size_t cli_shell_line_get_line_position(CliShellLine* line) { + return line->line_position; +} + +void cli_shell_line_set_line_position(CliShellLine* line, size_t position) { + line->line_position = position; +} + +// ======= +// Helpers +// ======= + +typedef enum { + CliCharClassWord, + CliCharClassSpace, + CliCharClassOther, +} CliCharClass; + +typedef enum { + CliSkipDirectionLeft, + CliSkipDirectionRight, +} CliSkipDirection; + +CliCharClass cli_shell_line_char_class(char c) { + if((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '_') { + return CliCharClassWord; + } else if(c == ' ') { + return CliCharClassSpace; + } else { + return CliCharClassOther; + } +} + +size_t + cli_shell_line_skip_run(FuriString* string, size_t original_pos, CliSkipDirection direction) { + if(furi_string_size(string) == 0) return original_pos; + if(direction == CliSkipDirectionLeft && original_pos == 0) return original_pos; + if(direction == CliSkipDirectionRight && original_pos == furi_string_size(string)) + return original_pos; + + int8_t look_offset = (direction == CliSkipDirectionLeft) ? -1 : 0; + int8_t increment = (direction == CliSkipDirectionLeft) ? -1 : 1; + int32_t position = original_pos; + CliCharClass start_class = + cli_shell_line_char_class(furi_string_get_char(string, position + look_offset)); + + while(true) { + position += increment; + if(position < 0) break; + if(position >= (int32_t)furi_string_size(string)) break; + if(cli_shell_line_char_class(furi_string_get_char(string, position + look_offset)) != + start_class) + break; + } + + return MAX(0, position); +} + +// ============== +// Input handlers +// ============== + +static bool cli_shell_line_input_ctrl_c(CliKeyCombo combo, void* context) { + UNUSED(combo); + CliShellLine* line = context; + // reset input + furi_string_reset(cli_shell_line_get_editing(line)); + line->line_position = 0; + line->history_position = 0; + printf("^C"); + cli_shell_line_prompt(line); + return true; +} + +static bool cli_shell_line_input_cr(CliKeyCombo combo, void* context) { + UNUSED(combo); + CliShellLine* line = context; + + FuriString* command = cli_shell_line_get_selected(line); + furi_string_trim(command); + if(furi_string_empty(command)) { + cli_shell_line_prompt(line); + return true; + } + + FuriString* command_copy = furi_string_alloc_set(command); + + if(line->history_position == 0) { + for(size_t i = 1; i < line->history_entries; i++) { + if(furi_string_cmp(line->history[i], command) == 0) { + line->history_position = i; + command = cli_shell_line_get_selected(line); + furi_string_trim(command); + break; + } + } + } + + // move selected command to the front + if(line->history_position > 0) { + size_t pos = line->history_position; + size_t len = line->history_entries; + memmove( + &line->history[pos], &line->history[pos + 1], (len - pos - 1) * sizeof(FuriString*)); + furi_string_move(line->history[0], command); + line->history_entries--; + } + + // insert empty command + if(line->history_entries == HISTORY_DEPTH) { + furi_string_free(line->history[HISTORY_DEPTH - 1]); + line->history_entries--; + } + memmove(&line->history[1], &line->history[0], line->history_entries * sizeof(FuriString*)); + line->history[0] = furi_string_alloc(); + line->history_entries++; + line->line_position = 0; + line->history_position = 0; + + // execute command + printf("\r\n"); + cli_shell_execute_command(line->shell, command_copy); + furi_string_free(command_copy); + if(!line->about_to_exit) cli_shell_line_prompt(line); + return true; +} + +static bool cli_shell_line_input_up_down(CliKeyCombo combo, void* context) { + CliShellLine* line = context; + // go up and down in history + int increment = (combo.key == CliKeyUp) ? 1 : -1; + size_t new_pos = + CLAMP((int)line->history_position + increment, (int)line->history_entries - 1, 0); + + // print prompt with selected command + if(new_pos != line->history_position) { + char prompt[64]; + cli_shell_line_format_prompt(line, prompt, sizeof(prompt)); + line->history_position = new_pos; + FuriString* command = cli_shell_line_get_selected(line); + printf( + ANSI_CURSOR_HOR_POS("1") "%s%s" ANSI_ERASE_LINE(ANSI_ERASE_FROM_CURSOR_TO_END), + prompt, + furi_string_get_cstr(command)); + fflush(stdout); + line->line_position = furi_string_size(command); + } + return true; +} + +static bool cli_shell_line_input_left_right(CliKeyCombo combo, void* context) { + CliShellLine* line = context; + // go left and right in the current line + FuriString* command = cli_shell_line_get_selected(line); + int increment = (combo.key == CliKeyRight) ? 1 : -1; + size_t new_pos = + CLAMP((int)line->line_position + increment, (int)furi_string_size(command), 0); + + // move cursor + if(new_pos != line->line_position) { + line->line_position = new_pos; + printf("%s", (increment == 1) ? ANSI_CURSOR_RIGHT_BY("1") : ANSI_CURSOR_LEFT_BY("1")); + fflush(stdout); + } + return true; +} + +static bool cli_shell_line_input_home(CliKeyCombo combo, void* context) { + UNUSED(combo); + CliShellLine* line = context; + // go to the start + line->line_position = 0; + printf(ANSI_CURSOR_HOR_POS("%zu"), cli_shell_line_prompt_length(line) + 1); + fflush(stdout); + return true; +} + +static bool cli_shell_line_input_end(CliKeyCombo combo, void* context) { + UNUSED(combo); + CliShellLine* line = context; + // go to the end + line->line_position = furi_string_size(cli_shell_line_get_selected(line)); + printf( + ANSI_CURSOR_HOR_POS("%zu"), cli_shell_line_prompt_length(line) + line->line_position + 1); + fflush(stdout); + return true; +} + +static bool cli_shell_line_input_bksp(CliKeyCombo combo, void* context) { + UNUSED(combo); + CliShellLine* line = context; + // erase one character + cli_shell_line_ensure_not_overwriting_history(line); + FuriString* editing_line = cli_shell_line_get_editing(line); + if(line->line_position == 0) { + putc(CliKeyBell, stdout); + fflush(stdout); + return true; + } + line->line_position--; + furi_string_replace_at(editing_line, line->line_position, 1, ""); + + // move cursor, print the rest of the line, restore cursor + printf( + ANSI_CURSOR_LEFT_BY("1") "%s" ANSI_ERASE_LINE(ANSI_ERASE_FROM_CURSOR_TO_END), + furi_string_get_cstr(editing_line) + line->line_position); + size_t left_by = furi_string_size(editing_line) - line->line_position; + if(left_by) // apparently LEFT_BY("0") still shifts left by one ._ . + printf(ANSI_CURSOR_LEFT_BY("%zu"), left_by); + fflush(stdout); + return true; +} + +static bool cli_shell_line_input_ctrl_l(CliKeyCombo combo, void* context) { + UNUSED(combo); + CliShellLine* line = context; + // clear screen + FuriString* command = cli_shell_line_get_selected(line); + char prompt[64]; + cli_shell_line_format_prompt(line, prompt, sizeof(prompt)); + printf( + ANSI_ERASE_DISPLAY(ANSI_ERASE_ENTIRE) ANSI_ERASE_SCROLLBACK_BUFFER ANSI_CURSOR_POS( + "1", "1") "%s%s" ANSI_CURSOR_HOR_POS("%zu"), + prompt, + furi_string_get_cstr(command), + strlen(prompt) + line->line_position + 1 /* 1-based column indexing */); + fflush(stdout); + return true; +} + +static bool cli_shell_line_input_ctrl_left_right(CliKeyCombo combo, void* context) { + CliShellLine* line = context; + // skip run of similar chars to the left or right + FuriString* selected_line = cli_shell_line_get_selected(line); + CliSkipDirection direction = (combo.key == CliKeyLeft) ? CliSkipDirectionLeft : + CliSkipDirectionRight; + line->line_position = cli_shell_line_skip_run(selected_line, line->line_position, direction); + printf( + ANSI_CURSOR_HOR_POS("%zu"), cli_shell_line_prompt_length(line) + line->line_position + 1); + fflush(stdout); + return true; +} + +static bool cli_shell_line_input_ctrl_bksp(CliKeyCombo combo, void* context) { + UNUSED(combo); + CliShellLine* line = context; + // delete run of similar chars to the left + cli_shell_line_ensure_not_overwriting_history(line); + FuriString* selected_line = cli_shell_line_get_selected(line); + size_t run_start = + cli_shell_line_skip_run(selected_line, line->line_position, CliSkipDirectionLeft); + furi_string_replace_at(selected_line, run_start, line->line_position - run_start, ""); + line->line_position = run_start; + printf( + ANSI_CURSOR_HOR_POS("%zu") "%s" ANSI_ERASE_LINE(ANSI_ERASE_FROM_CURSOR_TO_END) + ANSI_CURSOR_HOR_POS("%zu"), + cli_shell_line_prompt_length(line) + line->line_position + 1, + furi_string_get_cstr(selected_line) + run_start, + cli_shell_line_prompt_length(line) + run_start + 1); + fflush(stdout); + return true; +} + +static bool cli_shell_line_input_normal(CliKeyCombo combo, void* context) { + CliShellLine* line = context; + if(combo.modifiers != CliModKeyNo) return false; + if(combo.key < CliKeySpace || combo.key >= CliKeyDEL) return false; + // insert character + cli_shell_line_ensure_not_overwriting_history(line); + FuriString* editing_line = cli_shell_line_get_editing(line); + if(line->line_position == furi_string_size(editing_line)) { + furi_string_push_back(editing_line, combo.key); + printf("%c", combo.key); + } else { + const char in_str[2] = {combo.key, 0}; + furi_string_replace_at(editing_line, line->line_position, 0, in_str); + printf(ANSI_INSERT_MODE_ENABLE "%c" ANSI_INSERT_MODE_DISABLE, combo.key); + } + fflush(stdout); + line->line_position++; + return true; +} + +CliShellKeyComboSet cli_shell_line_key_combo_set = { + .fallback = cli_shell_line_input_normal, + .count = 14, + .records = + { + {{CliModKeyNo, CliKeyETX}, cli_shell_line_input_ctrl_c}, + {{CliModKeyNo, CliKeyCR}, cli_shell_line_input_cr}, + {{CliModKeyNo, CliKeyUp}, cli_shell_line_input_up_down}, + {{CliModKeyNo, CliKeyDown}, cli_shell_line_input_up_down}, + {{CliModKeyNo, CliKeyLeft}, cli_shell_line_input_left_right}, + {{CliModKeyNo, CliKeyRight}, cli_shell_line_input_left_right}, + {{CliModKeyNo, CliKeyHome}, cli_shell_line_input_home}, + {{CliModKeyNo, CliKeyEnd}, cli_shell_line_input_end}, + {{CliModKeyNo, CliKeyBackspace}, cli_shell_line_input_bksp}, + {{CliModKeyNo, CliKeyDEL}, cli_shell_line_input_bksp}, + {{CliModKeyNo, CliKeyFF}, cli_shell_line_input_ctrl_l}, + {{CliModKeyCtrl, CliKeyLeft}, cli_shell_line_input_ctrl_left_right}, + {{CliModKeyCtrl, CliKeyRight}, cli_shell_line_input_ctrl_left_right}, + {{CliModKeyNo, CliKeyETB}, cli_shell_line_input_ctrl_bksp}, + }, +}; diff --git a/lib/toolbox/cli/shell/cli_shell_line.h b/lib/toolbox/cli/shell/cli_shell_line.h new file mode 100644 index 000000000..e40a12bd6 --- /dev/null +++ b/lib/toolbox/cli/shell/cli_shell_line.h @@ -0,0 +1,42 @@ +#pragma once + +#include + +#include "cli_shell_i.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct CliShellLine CliShellLine; + +CliShellLine* cli_shell_line_alloc(CliShell* shell); + +void cli_shell_line_free(CliShellLine* line); + +FuriString* cli_shell_line_get_selected(CliShellLine* line); + +FuriString* cli_shell_line_get_editing(CliShellLine* line); + +size_t cli_shell_line_prompt_length(CliShellLine* line); + +void cli_shell_line_format_prompt(CliShellLine* line, char* buf, size_t length); + +void cli_shell_line_prompt(CliShellLine* line); + +size_t cli_shell_line_get_line_position(CliShellLine* line); + +void cli_shell_line_set_line_position(CliShellLine* line, size_t position); + +/** + * @brief If a line from history has been selected, moves it into the active line + */ +void cli_shell_line_ensure_not_overwriting_history(CliShellLine* line); + +void cli_shell_line_set_about_to_exit(CliShellLine* line); + +extern CliShellKeyComboSet cli_shell_line_key_combo_set; + +#ifdef __cplusplus +} +#endif diff --git a/lib/toolbox/pipe.c b/lib/toolbox/pipe.c index c6a45abf1..59b2f63f4 100644 --- a/lib/toolbox/pipe.c +++ b/lib/toolbox/pipe.c @@ -1,6 +1,8 @@ #include "pipe.h" #include +#define PIPE_DEFAULT_STATE_CHECK_PERIOD furi_ms_to_ticks(100) + /** * Data shared between both sides. */ @@ -23,7 +25,7 @@ struct PipeSide { PipeSideDataArrivedCallback on_data_arrived; PipeSideSpaceFreedCallback on_space_freed; PipeSideBrokenCallback on_pipe_broken; - FuriWait stdout_timeout; + FuriWait state_check_period; }; PipeSideBundle pipe_alloc(size_t capacity, size_t trigger_level) { @@ -53,14 +55,14 @@ PipeSideBundle pipe_alloc_ex(PipeSideReceiveSettings alice, PipeSideReceiveSetti .shared = shared, .sending = alice_to_bob, .receiving = bob_to_alice, - .stdout_timeout = FuriWaitForever, + .state_check_period = PIPE_DEFAULT_STATE_CHECK_PERIOD, }; *bobs_side = (PipeSide){ .role = PipeRoleBob, .shared = shared, .sending = bob_to_alice, .receiving = alice_to_bob, - .stdout_timeout = FuriWaitForever, + .state_check_period = PIPE_DEFAULT_STATE_CHECK_PERIOD, }; return (PipeSideBundle){.alices_side = alices_side, .bobs_side = bobs_side}; @@ -99,42 +101,62 @@ void pipe_free(PipeSide* pipe) { } } -static void _pipe_stdout_cb(const char* data, size_t size, void* context) { +static void pipe_stdout_cb(const char* data, size_t size, void* context) { furi_assert(context); PipeSide* pipe = context; - while(size) { - size_t sent = pipe_send(pipe, data, size, pipe->stdout_timeout); - if(!sent) break; - data += sent; - size -= sent; - } + pipe_send(pipe, data, size); } -static size_t _pipe_stdin_cb(char* data, size_t size, FuriWait timeout, void* context) { +static size_t pipe_stdin_cb(char* data, size_t size, FuriWait timeout, void* context) { + UNUSED(timeout); furi_assert(context); PipeSide* pipe = context; - return pipe_receive(pipe, data, size, timeout); + return pipe_receive(pipe, data, size); } void pipe_install_as_stdio(PipeSide* pipe) { furi_check(pipe); - furi_thread_set_stdout_callback(_pipe_stdout_cb, pipe); - furi_thread_set_stdin_callback(_pipe_stdin_cb, pipe); + furi_thread_set_stdout_callback(pipe_stdout_cb, pipe); + furi_thread_set_stdin_callback(pipe_stdin_cb, pipe); } -void pipe_set_stdout_timeout(PipeSide* pipe, FuriWait timeout) { +void pipe_set_state_check_period(PipeSide* pipe, FuriWait check_period) { furi_check(pipe); - pipe->stdout_timeout = timeout; + pipe->state_check_period = check_period; } -size_t pipe_receive(PipeSide* pipe, void* data, size_t length, FuriWait timeout) { +size_t pipe_receive(PipeSide* pipe, void* data, size_t length) { furi_check(pipe); - return furi_stream_buffer_receive(pipe->receiving, data, length, timeout); + + size_t received = 0; + while(length) { + size_t received_this_time = + furi_stream_buffer_receive(pipe->receiving, data, length, pipe->state_check_period); + if(!received_this_time && pipe_state(pipe) == PipeStateBroken) break; + + received += received_this_time; + length -= received_this_time; + data += received_this_time; + } + + return received; } -size_t pipe_send(PipeSide* pipe, const void* data, size_t length, FuriWait timeout) { +size_t pipe_send(PipeSide* pipe, const void* data, size_t length) { furi_check(pipe); - return furi_stream_buffer_send(pipe->sending, data, length, timeout); + + size_t sent = 0; + while(length) { + size_t sent_this_time = + furi_stream_buffer_send(pipe->sending, data, length, pipe->state_check_period); + if(!sent_this_time && pipe_state(pipe) == PipeStateBroken) break; + + sent += sent_this_time; + length -= sent_this_time; + data += sent_this_time; + } + + return sent; } size_t pipe_bytes_available(PipeSide* pipe) { @@ -151,14 +173,14 @@ static void pipe_receiving_buffer_callback(FuriEventLoopObject* buffer, void* co UNUSED(buffer); PipeSide* pipe = context; furi_assert(pipe); - if(pipe->on_space_freed) pipe->on_data_arrived(pipe, pipe->callback_context); + if(pipe->on_data_arrived) pipe->on_data_arrived(pipe, pipe->callback_context); } static void pipe_sending_buffer_callback(FuriEventLoopObject* buffer, void* context) { UNUSED(buffer); PipeSide* pipe = context; furi_assert(pipe); - if(pipe->on_data_arrived) pipe->on_space_freed(pipe, pipe->callback_context); + if(pipe->on_space_freed) pipe->on_space_freed(pipe, pipe->callback_context); } static void pipe_semaphore_callback(FuriEventLoopObject* semaphore, void* context) { diff --git a/lib/toolbox/pipe.h b/lib/toolbox/pipe.h index c05e60d0c..b071975f7 100644 --- a/lib/toolbox/pipe.h +++ b/lib/toolbox/pipe.h @@ -148,38 +148,48 @@ void pipe_free(PipeSide* pipe); void pipe_install_as_stdio(PipeSide* pipe); /** - * @brief Sets the timeout for `stdout` write operations + * @brief Sets the state check period for `send` and `receive` operations * - * @note This value is set to `FuriWaitForever` when the pipe is created + * @note This value is set to 100 ms when the pipe is created * - * @param [in] pipe Pipe side to set the timeout of - * @param [in] timeout Timeout value in ticks + * `send` and `receive` will check the state of the pipe if exactly 0 bytes were + * sent or received during any given `check_period`. Read the documentation for + * `pipe_send` and `pipe_receive` for more info. + * + * @param [in] pipe Pipe side to set the check period of + * @param [in] check_period Period in ticks */ -void pipe_set_stdout_timeout(PipeSide* pipe, FuriWait timeout); +void pipe_set_state_check_period(PipeSide* pipe, FuriWait check_period); /** * @brief Receives data from the pipe. * + * This function will try to receive all of the requested bytes from the pipe. + * If at some point during the operation the pipe becomes broken, this function + * will return prematurely, in which case the return value will be less than the + * requested `length`. + * * @param [in] pipe The pipe side to read data out of * @param [out] data The buffer to fill with data * @param length Maximum length of data to read - * @param timeout The timeout (in ticks) after which the read operation is - * interrupted * @returns The number of bytes actually written into the provided buffer */ -size_t pipe_receive(PipeSide* pipe, void* data, size_t length, FuriWait timeout); +size_t pipe_receive(PipeSide* pipe, void* data, size_t length); /** * @brief Sends data into the pipe. * + * This function will try to send all of the requested bytes to the pipe. + * If at some point during the operation the pipe becomes broken, this function + * will return prematurely, in which case the return value will be less than the + * requested `length`. + * * @param [in] pipe The pipe side to send data into * @param [out] data The buffer to get data from * @param length Maximum length of data to send - * @param timeout The timeout (in ticks) after which the write operation is - * interrupted * @returns The number of bytes actually read from the provided buffer */ -size_t pipe_send(PipeSide* pipe, const void* data, size_t length, FuriWait timeout); +size_t pipe_send(PipeSide* pipe, const void* data, size_t length); /** * @brief Determines how many bytes there are in the pipe available to be read. diff --git a/lib/toolbox/pretty_format.c b/lib/toolbox/pretty_format.c index f8319b69d..496738c4d 100644 --- a/lib/toolbox/pretty_format.c +++ b/lib/toolbox/pretty_format.c @@ -36,11 +36,17 @@ void pretty_format_bytes_hex_canonical( } const size_t begin_idx = i; - const size_t end_idx = MIN(i + num_places, data_size); + const size_t wrap_idx = i + num_places; + const size_t end_idx = MIN(wrap_idx, data_size); for(size_t j = begin_idx; j < end_idx; j++) { furi_string_cat_printf(result, "%02X ", data[j]); } + if(end_idx < wrap_idx) { + for(size_t j = end_idx; j < wrap_idx; j++) { + furi_string_cat_printf(result, " "); + } + } furi_string_push_back(result, '|'); diff --git a/lib/toolbox/protocols/protocol_dict.c b/lib/toolbox/protocols/protocol_dict.c index 5680be18d..5cc46d6eb 100644 --- a/lib/toolbox/protocols/protocol_dict.c +++ b/lib/toolbox/protocols/protocol_dict.c @@ -2,12 +2,12 @@ #include "protocol_dict.h" struct ProtocolDict { - const ProtocolBase** base; + const ProtocolBase* const* base; size_t count; void* data[]; }; -ProtocolDict* protocol_dict_alloc(const ProtocolBase** protocols, size_t count) { +ProtocolDict* protocol_dict_alloc(const ProtocolBase* const* protocols, size_t count) { furi_check(protocols); ProtocolDict* dict = malloc(sizeof(ProtocolDict) + (sizeof(void*) * count)); diff --git a/lib/toolbox/protocols/protocol_dict.h b/lib/toolbox/protocols/protocol_dict.h index 543b3ead2..a7e02a988 100644 --- a/lib/toolbox/protocols/protocol_dict.h +++ b/lib/toolbox/protocols/protocol_dict.h @@ -12,7 +12,7 @@ typedef int32_t ProtocolId; #define PROTOCOL_NO (-1) #define PROTOCOL_ALL_FEATURES (0xFFFFFFFF) -ProtocolDict* protocol_dict_alloc(const ProtocolBase** protocols, size_t protocol_count); +ProtocolDict* protocol_dict_alloc(const ProtocolBase* const* protocols, size_t protocol_count); void protocol_dict_free(ProtocolDict* dict); diff --git a/lib/toolbox/settings_helpers/submenu_based.c b/lib/toolbox/settings_helpers/submenu_based.c new file mode 100644 index 000000000..ce785dd73 --- /dev/null +++ b/lib/toolbox/settings_helpers/submenu_based.c @@ -0,0 +1,103 @@ +#include "submenu_based.h" +#include + +struct SubmenuSettingsHelper { + const SubmenuSettingsHelperDescriptor* descriptor; + ViewDispatcher* view_dispatcher; + SceneManager* scene_manager; + Submenu* submenu; + uint32_t submenu_view_id; + uint32_t main_scene_id; +}; + +SubmenuSettingsHelper* + submenu_settings_helpers_alloc(const SubmenuSettingsHelperDescriptor* descriptor) { + furi_check(descriptor); + SubmenuSettingsHelper* helper = malloc(sizeof(SubmenuSettingsHelper)); + helper->descriptor = descriptor; + return helper; +} + +void submenu_settings_helpers_assign_objects( + SubmenuSettingsHelper* helper, + ViewDispatcher* view_dispatcher, + SceneManager* scene_manager, + Submenu* submenu, + uint32_t submenu_view_id, + uint32_t main_scene_id) { + furi_check(helper); + furi_check(view_dispatcher); + furi_check(scene_manager); + furi_check(submenu); + helper->view_dispatcher = view_dispatcher; + helper->scene_manager = scene_manager; + helper->submenu = submenu; + helper->submenu_view_id = submenu_view_id; + helper->main_scene_id = main_scene_id; +} + +void submenu_settings_helpers_free(SubmenuSettingsHelper* helper) { + free(helper); +} + +bool submenu_settings_helpers_app_start(SubmenuSettingsHelper* helper, void* arg) { + furi_check(helper); + if(!arg) return false; + + const char* option = arg; + for(size_t i = 0; i < helper->descriptor->options_cnt; i++) { + if(strcmp(helper->descriptor->options[i].name, option) == 0) { + scene_manager_next_scene( + helper->scene_manager, helper->descriptor->options[i].scene_id); + return true; + } + } + + return false; +} + +static void + submenu_settings_helpers_callback(void* context, InputType input_type, uint32_t index) { + SubmenuSettingsHelper* helper = context; + if(input_type == InputTypeShort) { + view_dispatcher_send_custom_event(helper->view_dispatcher, index); + } else if(input_type == InputTypeLong) { + archive_favorites_handle_setting_pin_unpin( + helper->descriptor->app_name, helper->descriptor->options[index].name); + } +} + +void submenu_settings_helpers_scene_enter(SubmenuSettingsHelper* helper) { + furi_check(helper); + for(size_t i = 0; i < helper->descriptor->options_cnt; i++) { + submenu_add_item_ex( + helper->submenu, + helper->descriptor->options[i].name, + i, + submenu_settings_helpers_callback, + helper); + } + + submenu_set_selected_item( + helper->submenu, + scene_manager_get_scene_state(helper->scene_manager, helper->main_scene_id)); + view_dispatcher_switch_to_view(helper->view_dispatcher, helper->submenu_view_id); +} + +bool submenu_settings_helpers_scene_event(SubmenuSettingsHelper* helper, SceneManagerEvent event) { + furi_check(helper); + + if(event.type == SceneManagerEventTypeCustom) { + scene_manager_next_scene( + helper->scene_manager, helper->descriptor->options[event.event].scene_id); + scene_manager_set_scene_state(helper->scene_manager, helper->main_scene_id, event.event); + return true; + } + + return false; +} + +void submenu_settings_helpers_scene_exit(SubmenuSettingsHelper* helper) { + furi_check(helper); + submenu_reset(helper->submenu); +} diff --git a/lib/toolbox/settings_helpers/submenu_based.h b/lib/toolbox/settings_helpers/submenu_based.h new file mode 100644 index 000000000..6929cb04a --- /dev/null +++ b/lib/toolbox/settings_helpers/submenu_based.h @@ -0,0 +1,89 @@ +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (*SubmenuSettingsHelpherCallback)(void* context, uint32_t index); + +typedef struct { + const char* name; + uint32_t scene_id; +} SubmenuSettingsHelperOption; + +typedef struct { + const char* app_name; + size_t options_cnt; + SubmenuSettingsHelperOption options[]; +} SubmenuSettingsHelperDescriptor; + +typedef struct SubmenuSettingsHelper SubmenuSettingsHelper; + +/** + * @brief Allocates a submenu-based settings helper + * @param descriptor settings descriptor + */ +SubmenuSettingsHelper* + submenu_settings_helpers_alloc(const SubmenuSettingsHelperDescriptor* descriptor); + +/** + * @brief Assigns dynamic objects to the submenu-based settings helper + * @param helper helper object + * @param view_dispatcher ViewDispatcher + * @param scene_manager SceneManager + * @param submenu Submenu + * @param submenu_view_id Submenu view id in the ViewDispatcher + * @param main_scene_id Main scene id in the SceneManager + */ +void submenu_settings_helpers_assign_objects( + SubmenuSettingsHelper* helper, + ViewDispatcher* view_dispatcher, + SceneManager* scene_manager, + Submenu* submenu, + uint32_t submenu_view_id, + uint32_t main_scene_id); + +/** + * @brief Frees a submenu-based settings helper + * @param helper helper object + */ +void submenu_settings_helpers_free(SubmenuSettingsHelper* helper); + +/** + * @brief App start callback for the submenu-based settings helper + * + * If an argument containing one of the options was provided, launches the + * corresponding scene. + * + * @param helper helper object + * @param arg app argument, may be NULL + * @returns true if a setting name was provided in the argument, false if normal + * app operation shall commence + */ +bool submenu_settings_helpers_app_start(SubmenuSettingsHelper* helper, void* arg); + +/** + * @brief Main scene enter callback for the submenu-based settings helper + * @param helper helper object + */ +void submenu_settings_helpers_scene_enter(SubmenuSettingsHelper* helper); + +/** + * @brief Main scene event callback for the submenu-based settings helper + * @param helper helper object + * @param event event data + * @returns true if the event was consumed, false otherwise + */ +bool submenu_settings_helpers_scene_event(SubmenuSettingsHelper* helper, SceneManagerEvent event); + +/** + * @brief Main scene exit callback for the submenu-based settings helper + * @param helper helper object + */ +void submenu_settings_helpers_scene_exit(SubmenuSettingsHelper* helper); + +#ifdef __cplusplus +} +#endif diff --git a/scripts/fbt/sdk/collector.py b/scripts/fbt/sdk/collector.py index 5615f105e..14653eb44 100644 --- a/scripts/fbt/sdk/collector.py +++ b/scripts/fbt/sdk/collector.py @@ -113,7 +113,7 @@ def stringify_descr(type_descr): # Hack if isinstance(type_descr.ptr_to, FunctionType): return stringify_descr(type_descr.ptr_to) - return f"{stringify_descr(type_descr.ptr_to)}*" + return f"{stringify_descr(type_descr.ptr_to)}*{' const' if type_descr.const else ''}" elif isinstance(type_descr, Type): return ( f"{'const ' if type_descr.const else ''}" diff --git a/scripts/fbt_tools/fbt_apps.py b/scripts/fbt_tools/fbt_apps.py index 3a17a79a3..c48e4ba80 100644 --- a/scripts/fbt_tools/fbt_apps.py +++ b/scripts/fbt_tools/fbt_apps.py @@ -150,11 +150,22 @@ def DumpApplicationConfig(target, source, env): print(fg.boldgreen("Firmware modules configuration:")) for apptype in FlipperAppType: app_sublist = env["APPBUILD"].get_apps_of_type(apptype) - if app_sublist: + # Print a warning if any apps in the list have same .order value + unique_order_values = set(app.order for app in app_sublist) + if len(app_sublist) != len(unique_order_values) and max(unique_order_values): + print( + fg.red(f"{apptype.value}: ") + + fg.yellow( + "Duplicate .order values in group:\n\t" + + ", ".join(f"{app.appid} ({app.order})" for app in app_sublist) + ) + ) + elif app_sublist: print( fg.green(f"{apptype.value}:\n\t"), ", ".join(app.appid for app in app_sublist), ) + if incompatible_ext_apps := env["APPBUILD"].get_incompatible_ext_apps(): print( fg.blue("Incompatible apps (skipped):\n\t"), diff --git a/scripts/flipper/storage.py b/scripts/flipper/storage.py index 40af5cebc..0182cf45f 100644 --- a/scripts/flipper/storage.py +++ b/scripts/flipper/storage.py @@ -109,6 +109,8 @@ class FlipperStorage: def start(self): self.port.open() + time.sleep(0.5) + self.read.until(self.CLI_PROMPT) self.port.reset_input_buffer() # Send a command with a known syntax to make sure the buffer is flushed self.send("device_info\r") diff --git a/scripts/flipper/utils/cdc.py b/scripts/flipper/utils/cdc.py index ee1125f77..00b20d6fb 100644 --- a/scripts/flipper/utils/cdc.py +++ b/scripts/flipper/utils/cdc.py @@ -1,3 +1,4 @@ +import os import serial.tools.list_ports as list_ports @@ -15,3 +16,8 @@ def resolve_port(logger, portname: str = "auto"): logger.error("Failed to find connected Flipper") elif len(flippers) > 1: logger.error("More than one Flipper is attached") + env_path = os.environ.get("FLIPPER_PATH") + if env_path: + if os.path.exists(env_path): + logger.info(f"Using FLIPPER_PATH from environment: {env_path}") + return env_path diff --git a/scripts/power.py b/scripts/power.py index 50bb2d4f7..b13e63bd1 100755 --- a/scripts/power.py +++ b/scripts/power.py @@ -3,6 +3,8 @@ import time from typing import Optional +from serial.serialutil import SerialException + from flipper.app import App from flipper.storage import FlipperStorage from flipper.utils.cdc import resolve_port @@ -32,11 +34,11 @@ class Main(App): def _get_flipper(self, retry_count: Optional[int] = 1): port = None - self.logger.info(f"Attempting to find flipper with {retry_count} attempts.") - for i in range(retry_count): time.sleep(1) - self.logger.info(f"Attempting to find flipper #{i}.") + self.logger.info( + f"Attempting to find flipper (Attempt {i + 1}/{retry_count})." + ) if port := resolve_port(self.logger, self.args.port): self.logger.info(f"Found flipper at {port}") @@ -47,8 +49,16 @@ class Main(App): return None flipper = FlipperStorage(port) - flipper.start() - return flipper + for i in range(retry_count): + try: + flipper.start() + self.logger.info("Flipper successfully started.") + return flipper + except IOError as e: + self.logger.info( + f"Failed to start flipper (Attempt {i + 1}/{retry_count}): {e}" + ) + time.sleep(1) def power_off(self): if not (flipper := self._get_flipper(retry_count=10)): diff --git a/scripts/serial_cli_perf.py b/scripts/serial_cli_perf.py new file mode 100644 index 000000000..0fed7c393 --- /dev/null +++ b/scripts/serial_cli_perf.py @@ -0,0 +1,59 @@ +import argparse +import logging +from serial import Serial +from random import randint +from time import time + +from flipper.utils.cdc import resolve_port + + +def main(): + logger = logging.getLogger() + parser = argparse.ArgumentParser() + parser.add_argument("-p", "--port", help="CDC Port", default="auto") + parser.add_argument( + "-l", "--length", type=int, help="Number of bytes to send", default=1024**2 + ) + args = parser.parse_args() + + if not (port := resolve_port(logger, args.port)): + logger.error("Is Flipper connected via USB and not in DFU mode?") + return 1 + port = Serial(port, 230400) + port.timeout = 2 + + port.read_until(b">: ") + port.write(b"echo\r") + port.read_until(b">: ") + + print(f"Transferring {args.length} bytes. Hang tight...") + + start_time = time() + + bytes_to_send = args.length + block_size = 1024 + while bytes_to_send: + actual_size = min(block_size, bytes_to_send) + # can't use 0x03 because that's ASCII ETX, or Ctrl+C + block = bytes([randint(4, 255) for _ in range(actual_size)]) + + port.write(block) + return_block = port.read(actual_size) + + if return_block != block: + logger.error("Incorrect block received") + break + + bytes_to_send -= actual_size + + end_time = time() + delta = end_time - start_time + speed = args.length / delta + print(f"Speed: {speed/1024:.2f} KiB/s") + + port.write(b"\x03") # Ctrl+C + port.close() + + +if __name__ == "__main__": + main() diff --git a/scripts/testops.py b/scripts/testops.py index 3dce51c22..119453448 100644 --- a/scripts/testops.py +++ b/scripts/testops.py @@ -4,6 +4,8 @@ import time from datetime import datetime from typing import Optional +from serial.serialutil import SerialException + from flipper.app import App from flipper.storage import FlipperStorage from flipper.utils.cdc import resolve_port @@ -34,23 +36,34 @@ class Main(App): def _get_flipper(self, retry_count: Optional[int] = 1): port = None - self.logger.info(f"Attempting to find flipper with {retry_count} attempts.") - for i in range(retry_count): time.sleep(1) - self.logger.info(f"Attempt to find flipper #{i}.") + self.logger.info( + f"Attempting to find flipper (Attempt {i + 1}/{retry_count})." + ) if port := resolve_port(self.logger, self.args.port): self.logger.info(f"Found flipper at {port}") break if not port: - self.logger.info(f"Failed to find flipper {port}") + self.logger.info(f"Failed to find flipper") return None flipper = FlipperStorage(port) - flipper.start() - return flipper + for i in range(retry_count): + try: + flipper.start() + self.logger.info("Flipper successfully started.") + return flipper + except IOError as e: + self.logger.info( + f"Failed to start flipper (Attempt {i + 1}/{retry_count}): {e}" + ) + time.sleep(1) + + self.logger.error("Flipper failed to start after all retries.") + return None def await_flipper(self): if not (flipper := self._get_flipper(retry_count=self.args.timeout)): diff --git a/scripts/update.py b/scripts/update.py index 992f75603..1c877c23e 100755 --- a/scripts/update.py +++ b/scripts/update.py @@ -34,7 +34,7 @@ class Main(App): FLASH_BASE = 0x8000000 FLASH_PAGE_SIZE = 4 * 1024 - MIN_GAP_PAGES = 1 + MIN_GAP_PAGES = 0 # Update stage file larger than that is not loadable without fix # https://github.com/flipperdevices/flipperzero-firmware/pull/3676 diff --git a/site_scons/cc.scons b/site_scons/cc.scons index c5d99b896..9960d350c 100644 --- a/site_scons/cc.scons +++ b/site_scons/cc.scons @@ -31,6 +31,7 @@ ENV.AppendUnique( "-Wundef", "-fdata-sections", "-ffunction-sections", + "-Wa,-gdwarf-sections", "-fsingle-precision-constant", "-fno-math-errno", # Generates .su files with stack usage information diff --git a/targets/f18/api_symbols.csv b/targets/f18/api_symbols.csv index 3ab8df7fa..ece75bc87 100644 --- a/targets/f18/api_symbols.csv +++ b/targets/f18/api_symbols.csv @@ -1,8 +1,7 @@ entry,status,name,type,params -Version,+,83.0,, +Version,+,85.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/bt/bt_service/bt_keys_storage.h,, -Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, Header,+,applications/services/dialogs/dialogs.h,, Header,+,applications/services/dolphin/dolphin.h,, @@ -57,6 +56,7 @@ Header,+,lib/flipper_application/plugins/plugin_manager.h,, Header,+,lib/flipper_format/flipper_format.h,, Header,+,lib/flipper_format/flipper_format_i.h,, Header,+,lib/flipper_format/flipper_format_stream.h,, +Header,+,lib/ieee754_parse_wrap/wrappers.h,, Header,+,lib/libusb_stm32/inc/hid_usage_button.h,, Header,+,lib/libusb_stm32/inc/hid_usage_consumer.h,, Header,+,lib/libusb_stm32/inc/hid_usage_desktop.h,, @@ -151,6 +151,10 @@ Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_wwdg.h,, Header,+,lib/toolbox/api_lock.h,, Header,+,lib/toolbox/args.h,, Header,+,lib/toolbox/bit_buffer.h,, +Header,+,lib/toolbox/cli/cli_ansi.h,, +Header,+,lib/toolbox/cli/cli_command.h,, +Header,+,lib/toolbox/cli/cli_registry.h,, +Header,+,lib/toolbox/cli/shell/cli_shell.h,, Header,+,lib/toolbox/compress.h,, Header,+,lib/toolbox/crc32_calc.h,, Header,+,lib/toolbox/dir_walk.h,, @@ -382,6 +386,8 @@ Function,+,__wrap_putc,int,"int, FILE*" Function,+,__wrap_putchar,int,int Function,+,__wrap_puts,int,const char* Function,+,__wrap_snprintf,int,"char*, size_t, const char*, ..." +Function,+,__wrap_strtod,double,"const char*, char**" +Function,+,__wrap_strtof,float,"const char*, char**" Function,+,__wrap_ungetc,int,"int, FILE*" Function,+,__wrap_vsnprintf,int,"char*, size_t, const char*, va_list" Function,-,_asiprintf_r,int,"_reent*, char**, const char*, ..." @@ -775,18 +781,26 @@ Function,-,ceill,long double,long double Function,-,cfree,void,void* Function,-,clearerr,void,FILE* Function,-,clearerr_unlocked,void,FILE* -Function,+,cli_add_command,void,"Cli*, const char*, CliCommandFlag, CliCallback, void*" -Function,+,cli_cmd_interrupt_received,_Bool,Cli* -Function,+,cli_delete_command,void,"Cli*, const char*" -Function,+,cli_getc,char,Cli* -Function,+,cli_is_connected,_Bool,Cli* -Function,+,cli_nl,void,Cli* +Function,+,cli_ansi_parser_alloc,CliAnsiParser*, +Function,+,cli_ansi_parser_feed,CliAnsiParserResult,"CliAnsiParser*, char" +Function,+,cli_ansi_parser_feed_timeout,CliAnsiParserResult,CliAnsiParser* +Function,+,cli_ansi_parser_free,void,CliAnsiParser* +Function,+,cli_is_pipe_broken_or_is_etx_next_char,_Bool,PipeSide* Function,+,cli_print_usage,void,"const char*, const char*, const char*" -Function,+,cli_read,size_t,"Cli*, uint8_t*, size_t" -Function,+,cli_read_timeout,size_t,"Cli*, uint8_t*, size_t, uint32_t" -Function,+,cli_session_close,void,Cli* -Function,+,cli_session_open,void,"Cli*, void*" -Function,+,cli_write,void,"Cli*, const uint8_t*, size_t" +Function,+,cli_registry_add_command,void,"CliRegistry*, const char*, CliCommandFlag, CliCommandExecuteCallback, void*" +Function,+,cli_registry_add_command_ex,void,"CliRegistry*, const char*, CliCommandFlag, CliCommandExecuteCallback, void*, size_t" +Function,+,cli_registry_alloc,CliRegistry*, +Function,+,cli_registry_delete_command,void,"CliRegistry*, const char*" +Function,+,cli_registry_free,void,CliRegistry* +Function,+,cli_registry_reload_external_commands,void,"CliRegistry*, const CliCommandExternalConfig*" +Function,+,cli_registry_remove_external_commands,void,CliRegistry* +Function,+,cli_shell_alloc,CliShell*,"CliShellMotd, void*, PipeSide*, CliRegistry*, const CliCommandExternalConfig*" +Function,+,cli_shell_free,void,CliShell* +Function,+,cli_shell_join,void,CliShell* +Function,+,cli_shell_set_prompt,void,"CliShell*, const char*" +Function,+,cli_shell_start,void,CliShell* +Function,+,cli_vcp_disable,void,CliVcp* +Function,+,cli_vcp_enable,void,CliVcp* Function,+,composite_api_resolver_add,void,"CompositeApiResolver*, const ElfApiInterface*" Function,+,composite_api_resolver_alloc,CompositeApiResolver*, Function,+,composite_api_resolver_free,void,CompositeApiResolver* @@ -1135,6 +1149,7 @@ Function,+,furi_event_loop_subscribe_message_queue,void,"FuriEventLoop*, FuriMes Function,+,furi_event_loop_subscribe_mutex,void,"FuriEventLoop*, FuriMutex*, FuriEventLoopEvent, FuriEventLoopEventCallback, void*" Function,+,furi_event_loop_subscribe_semaphore,void,"FuriEventLoop*, FuriSemaphore*, FuriEventLoopEvent, FuriEventLoopEventCallback, void*" Function,+,furi_event_loop_subscribe_stream_buffer,void,"FuriEventLoop*, FuriStreamBuffer*, FuriEventLoopEvent, FuriEventLoopEventCallback, void*" +Function,+,furi_event_loop_subscribe_thread_flags,void,"FuriEventLoop*, FuriEventLoopThreadFlagsCallback, void*" Function,+,furi_event_loop_tick_set,void,"FuriEventLoop*, uint32_t, FuriEventLoopTickCallback, void*" Function,+,furi_event_loop_timer_alloc,FuriEventLoopTimer*,"FuriEventLoop*, FuriEventLoopTimerCallback, FuriEventLoopTimerType, void*" Function,+,furi_event_loop_timer_free,void,FuriEventLoopTimer* @@ -1145,6 +1160,7 @@ Function,+,furi_event_loop_timer_restart,void,FuriEventLoopTimer* Function,+,furi_event_loop_timer_start,void,"FuriEventLoopTimer*, uint32_t" Function,+,furi_event_loop_timer_stop,void,FuriEventLoopTimer* Function,+,furi_event_loop_unsubscribe,void,"FuriEventLoop*, FuriEventLoopObject*" +Function,+,furi_event_loop_unsubscribe_thread_flags,void,FuriEventLoop* Function,+,furi_get_tick,uint32_t, Function,+,furi_hal_adc_acquire,FuriHalAdcHandle*, Function,+,furi_hal_adc_configure,void,FuriHalAdcHandle* @@ -1287,23 +1303,23 @@ Function,+,furi_hal_hid_u2f_get_request,uint32_t,uint8_t* Function,+,furi_hal_hid_u2f_is_connected,_Bool, Function,+,furi_hal_hid_u2f_send_response,void,"uint8_t*, uint8_t" Function,+,furi_hal_hid_u2f_set_callback,void,"HidU2fCallback, void*" -Function,+,furi_hal_i2c_acquire,void,FuriHalI2cBusHandle* +Function,+,furi_hal_i2c_acquire,void,const FuriHalI2cBusHandle* Function,-,furi_hal_i2c_deinit_early,void, Function,-,furi_hal_i2c_init,void, Function,-,furi_hal_i2c_init_early,void, -Function,+,furi_hal_i2c_is_device_ready,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint32_t" -Function,+,furi_hal_i2c_read_mem,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t, uint8_t*, size_t, uint32_t" -Function,+,furi_hal_i2c_read_reg_16,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t, uint16_t*, uint32_t" -Function,+,furi_hal_i2c_read_reg_8,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t, uint8_t*, uint32_t" -Function,+,furi_hal_i2c_release,void,FuriHalI2cBusHandle* -Function,+,furi_hal_i2c_rx,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t*, size_t, uint32_t" -Function,+,furi_hal_i2c_rx_ext,_Bool,"FuriHalI2cBusHandle*, uint16_t, _Bool, uint8_t*, size_t, FuriHalI2cBegin, FuriHalI2cEnd, uint32_t" -Function,+,furi_hal_i2c_trx,_Bool,"FuriHalI2cBusHandle*, uint8_t, const uint8_t*, size_t, uint8_t*, size_t, uint32_t" -Function,+,furi_hal_i2c_tx,_Bool,"FuriHalI2cBusHandle*, uint8_t, const uint8_t*, size_t, uint32_t" -Function,+,furi_hal_i2c_tx_ext,_Bool,"FuriHalI2cBusHandle*, uint16_t, _Bool, const uint8_t*, size_t, FuriHalI2cBegin, FuriHalI2cEnd, uint32_t" -Function,+,furi_hal_i2c_write_mem,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t, const uint8_t*, size_t, uint32_t" -Function,+,furi_hal_i2c_write_reg_16,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t, uint16_t, uint32_t" -Function,+,furi_hal_i2c_write_reg_8,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t, uint8_t, uint32_t" +Function,+,furi_hal_i2c_is_device_ready,_Bool,"const FuriHalI2cBusHandle*, uint8_t, uint32_t" +Function,+,furi_hal_i2c_read_mem,_Bool,"const FuriHalI2cBusHandle*, uint8_t, uint8_t, uint8_t*, size_t, uint32_t" +Function,+,furi_hal_i2c_read_reg_16,_Bool,"const FuriHalI2cBusHandle*, uint8_t, uint8_t, uint16_t*, uint32_t" +Function,+,furi_hal_i2c_read_reg_8,_Bool,"const FuriHalI2cBusHandle*, uint8_t, uint8_t, uint8_t*, uint32_t" +Function,+,furi_hal_i2c_release,void,const FuriHalI2cBusHandle* +Function,+,furi_hal_i2c_rx,_Bool,"const FuriHalI2cBusHandle*, uint8_t, uint8_t*, size_t, uint32_t" +Function,+,furi_hal_i2c_rx_ext,_Bool,"const FuriHalI2cBusHandle*, uint16_t, _Bool, uint8_t*, size_t, FuriHalI2cBegin, FuriHalI2cEnd, uint32_t" +Function,+,furi_hal_i2c_trx,_Bool,"const FuriHalI2cBusHandle*, uint8_t, const uint8_t*, size_t, uint8_t*, size_t, uint32_t" +Function,+,furi_hal_i2c_tx,_Bool,"const FuriHalI2cBusHandle*, uint8_t, const uint8_t*, size_t, uint32_t" +Function,+,furi_hal_i2c_tx_ext,_Bool,"const FuriHalI2cBusHandle*, uint16_t, _Bool, const uint8_t*, size_t, FuriHalI2cBegin, FuriHalI2cEnd, uint32_t" +Function,+,furi_hal_i2c_write_mem,_Bool,"const FuriHalI2cBusHandle*, uint8_t, uint8_t, const uint8_t*, size_t, uint32_t" +Function,+,furi_hal_i2c_write_reg_16,_Bool,"const FuriHalI2cBusHandle*, uint8_t, uint8_t, uint16_t, uint32_t" +Function,+,furi_hal_i2c_write_reg_8,_Bool,"const FuriHalI2cBusHandle*, uint8_t, uint8_t, uint8_t, uint32_t" Function,+,furi_hal_info_get,void,"PropertyValueCallback, char, void*" Function,+,furi_hal_info_get_api_version,void,"uint16_t*, uint16_t*" Function,-,furi_hal_init,void, @@ -1469,20 +1485,20 @@ Function,+,furi_hal_speaker_release,void, Function,+,furi_hal_speaker_set_volume,void,float Function,+,furi_hal_speaker_start,void,"float, float" Function,+,furi_hal_speaker_stop,void, -Function,+,furi_hal_spi_acquire,void,FuriHalSpiBusHandle* +Function,+,furi_hal_spi_acquire,void,const FuriHalSpiBusHandle* Function,+,furi_hal_spi_bus_deinit,void,FuriHalSpiBus* -Function,+,furi_hal_spi_bus_handle_deinit,void,FuriHalSpiBusHandle* -Function,+,furi_hal_spi_bus_handle_init,void,FuriHalSpiBusHandle* +Function,+,furi_hal_spi_bus_handle_deinit,void,const FuriHalSpiBusHandle* +Function,+,furi_hal_spi_bus_handle_init,void,const FuriHalSpiBusHandle* Function,+,furi_hal_spi_bus_init,void,FuriHalSpiBus* -Function,+,furi_hal_spi_bus_rx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, size_t, uint32_t" -Function,+,furi_hal_spi_bus_trx,_Bool,"FuriHalSpiBusHandle*, const uint8_t*, uint8_t*, size_t, uint32_t" -Function,+,furi_hal_spi_bus_trx_dma,_Bool,"FuriHalSpiBusHandle*, uint8_t*, uint8_t*, size_t, uint32_t" -Function,+,furi_hal_spi_bus_tx,_Bool,"FuriHalSpiBusHandle*, const uint8_t*, size_t, uint32_t" +Function,+,furi_hal_spi_bus_rx,_Bool,"const FuriHalSpiBusHandle*, uint8_t*, size_t, uint32_t" +Function,+,furi_hal_spi_bus_trx,_Bool,"const FuriHalSpiBusHandle*, const uint8_t*, uint8_t*, size_t, uint32_t" +Function,+,furi_hal_spi_bus_trx_dma,_Bool,"const FuriHalSpiBusHandle*, uint8_t*, uint8_t*, size_t, uint32_t" +Function,+,furi_hal_spi_bus_tx,_Bool,"const FuriHalSpiBusHandle*, const uint8_t*, size_t, uint32_t" Function,-,furi_hal_spi_config_deinit_early,void, Function,-,furi_hal_spi_config_init,void, Function,-,furi_hal_spi_config_init_early,void, Function,-,furi_hal_spi_dma_init,void, -Function,+,furi_hal_spi_release,void,FuriHalSpiBusHandle* +Function,+,furi_hal_spi_release,void,const FuriHalSpiBusHandle* Function,+,furi_hal_switch,void,void* Function,+,furi_hal_usb_ccid_insert_smartcard,void, Function,+,furi_hal_usb_ccid_remove_smartcard,void, @@ -1495,7 +1511,7 @@ Function,+,furi_hal_usb_is_locked,_Bool, Function,+,furi_hal_usb_lock,void, Function,+,furi_hal_usb_reinit,void, Function,+,furi_hal_usb_set_config,_Bool,"FuriHalUsbInterface*, void*" -Function,-,furi_hal_usb_set_state_callback,void,"FuriHalUsbStateCallback, void*" +Function,+,furi_hal_usb_set_state_callback,void,"FuriHalUsbStateCallback, void*" Function,+,furi_hal_usb_unlock,void, Function,+,furi_hal_version_do_i_belong_here,_Bool, Function,+,furi_hal_version_get_ble_local_device_name_ptr,const char*, @@ -1733,7 +1749,7 @@ Function,-,getchar,int, Function,-,getchar_unlocked,int, Function,-,getenv,char*,const char* Function,-,gets,char*,char* -Function,-,getsubopt,int,"char**, char**, char**" +Function,-,getsubopt,int,"char**, char* const*, char**" Function,-,getw,int,FILE* Function,+,gui_add_framebuffer_callback,void,"Gui*, GuiCanvasCommitCallback, void*" Function,+,gui_add_view_port,void,"Gui*, ViewPort*, GuiLayer" @@ -1844,6 +1860,9 @@ Function,-,llrintl,long long int,long double Function,-,llround,long long int,double Function,-,llroundf,long long int,float Function,-,llroundl,long long int,long double +Function,+,loader_clear_launch_queue,void,Loader* +Function,+,loader_enqueue_launch,void,"Loader*, const char*, const char*, LoaderDeferredLaunchFlag" +Function,+,loader_get_application_launch_path,_Bool,"Loader*, FuriString*" Function,+,loader_get_application_name,_Bool,"Loader*, FuriString*" Function,+,loader_get_pubsub,FuriPubSub*,Loader* Function,+,loader_is_locked,_Bool,Loader* @@ -2326,14 +2345,14 @@ Function,+,pipe_bytes_available,size_t,PipeSide* Function,+,pipe_detach_from_event_loop,void,PipeSide* Function,+,pipe_free,void,PipeSide* Function,+,pipe_install_as_stdio,void,PipeSide* -Function,+,pipe_receive,size_t,"PipeSide*, void*, size_t, FuriWait" +Function,+,pipe_receive,size_t,"PipeSide*, void*, size_t" Function,+,pipe_role,PipeRole,PipeSide* -Function,+,pipe_send,size_t,"PipeSide*, const void*, size_t, FuriWait" +Function,+,pipe_send,size_t,"PipeSide*, const void*, size_t" Function,+,pipe_set_broken_callback,void,"PipeSide*, PipeSideBrokenCallback, FuriEventLoopEvent" Function,+,pipe_set_callback_context,void,"PipeSide*, void*" Function,+,pipe_set_data_arrived_callback,void,"PipeSide*, PipeSideDataArrivedCallback, FuriEventLoopEvent" Function,+,pipe_set_space_freed_callback,void,"PipeSide*, PipeSideSpaceFreedCallback, FuriEventLoopEvent" -Function,+,pipe_set_stdout_timeout,void,"PipeSide*, FuriWait" +Function,+,pipe_set_state_check_period,void,"PipeSide*, FuriWait" Function,+,pipe_spaces_available,size_t,PipeSide* Function,+,pipe_state,PipeState,PipeSide* Function,+,plugin_manager_alloc,PluginManager*,"const char*, uint32_t, const ElfApiInterface*" @@ -2361,19 +2380,19 @@ Function,-,pow,double,"double, double" Function,-,pow10,double,double Function,-,pow10f,float,float Function,+,power_enable_low_battery_level_notification,void,"Power*, _Bool" +Function,+,power_enable_otg,void,"Power*, _Bool" Function,+,power_get_info,void,"Power*, PowerInfo*" Function,+,power_get_pubsub,FuriPubSub*,Power* Function,+,power_is_battery_healthy,_Bool,Power* Function,+,power_is_otg_enabled,_Bool,Power* Function,+,power_off,void,Power* Function,+,power_reboot,void,"Power*, PowerBootMode" -Function,+,power_enable_otg,void,"Power*, _Bool" Function,+,powf,float,"float, float" Function,-,powl,long double,"long double, long double" Function,+,pretty_format_bytes_hex_canonical,void,"FuriString*, size_t, const char*, const uint8_t*, size_t" Function,-,printf,int,"const char*, ..." Function,+,property_value_out,void,"PropertyValueContext*, const char*, unsigned int, ..." -Function,+,protocol_dict_alloc,ProtocolDict*,"const ProtocolBase**, size_t" +Function,+,protocol_dict_alloc,ProtocolDict*,"const ProtocolBase* const*, size_t" Function,+,protocol_dict_decoders_feed,ProtocolId,"ProtocolDict*, _Bool, uint32_t" Function,+,protocol_dict_decoders_feed_by_feature,ProtocolId,"ProtocolDict*, uint32_t, _Bool, uint32_t" Function,+,protocol_dict_decoders_feed_by_id,ProtocolId,"ProtocolDict*, size_t, _Bool, uint32_t" @@ -2536,29 +2555,29 @@ Function,+,srand,void,unsigned Function,-,srand48,void,long Function,-,srandom,void,unsigned Function,+,sscanf,int,"const char*, const char*, ..." -Function,+,st25r3916_change_reg_bits,void,"FuriHalSpiBusHandle*, uint8_t, uint8_t, uint8_t" -Function,+,st25r3916_change_test_reg_bits,void,"FuriHalSpiBusHandle*, uint8_t, uint8_t, uint8_t" -Function,+,st25r3916_check_reg,_Bool,"FuriHalSpiBusHandle*, uint8_t, uint8_t, uint8_t" -Function,+,st25r3916_clear_reg_bits,void,"FuriHalSpiBusHandle*, uint8_t, uint8_t" -Function,+,st25r3916_direct_cmd,void,"FuriHalSpiBusHandle*, uint8_t" -Function,+,st25r3916_get_irq,uint32_t,FuriHalSpiBusHandle* -Function,+,st25r3916_mask_irq,void,"FuriHalSpiBusHandle*, uint32_t" -Function,+,st25r3916_modify_reg,void,"FuriHalSpiBusHandle*, uint8_t, uint8_t, uint8_t" -Function,+,st25r3916_read_burst_regs,void,"FuriHalSpiBusHandle*, uint8_t, uint8_t*, uint8_t" -Function,+,st25r3916_read_fifo,_Bool,"FuriHalSpiBusHandle*, uint8_t*, size_t, size_t*" -Function,+,st25r3916_read_pta_mem,void,"FuriHalSpiBusHandle*, uint8_t*, size_t" -Function,+,st25r3916_read_reg,void,"FuriHalSpiBusHandle*, uint8_t, uint8_t*" -Function,+,st25r3916_read_test_reg,void,"FuriHalSpiBusHandle*, uint8_t, uint8_t*" -Function,+,st25r3916_reg_read_fifo,void,"FuriHalSpiBusHandle*, uint8_t*, size_t" -Function,+,st25r3916_reg_write_fifo,void,"FuriHalSpiBusHandle*, const uint8_t*, size_t" -Function,+,st25r3916_set_reg_bits,void,"FuriHalSpiBusHandle*, uint8_t, uint8_t" -Function,+,st25r3916_write_burst_regs,void,"FuriHalSpiBusHandle*, uint8_t, const uint8_t*, uint8_t" -Function,+,st25r3916_write_fifo,void,"FuriHalSpiBusHandle*, const uint8_t*, size_t" -Function,+,st25r3916_write_pta_mem,void,"FuriHalSpiBusHandle*, const uint8_t*, size_t" -Function,+,st25r3916_write_ptf_mem,void,"FuriHalSpiBusHandle*, const uint8_t*, size_t" -Function,+,st25r3916_write_pttsn_mem,void,"FuriHalSpiBusHandle*, uint8_t*, size_t" -Function,+,st25r3916_write_reg,void,"FuriHalSpiBusHandle*, uint8_t, uint8_t" -Function,+,st25r3916_write_test_reg,void,"FuriHalSpiBusHandle*, uint8_t, uint8_t" +Function,+,st25r3916_change_reg_bits,void,"const FuriHalSpiBusHandle*, uint8_t, uint8_t, uint8_t" +Function,+,st25r3916_change_test_reg_bits,void,"const FuriHalSpiBusHandle*, uint8_t, uint8_t, uint8_t" +Function,+,st25r3916_check_reg,_Bool,"const FuriHalSpiBusHandle*, uint8_t, uint8_t, uint8_t" +Function,+,st25r3916_clear_reg_bits,void,"const FuriHalSpiBusHandle*, uint8_t, uint8_t" +Function,+,st25r3916_direct_cmd,void,"const FuriHalSpiBusHandle*, uint8_t" +Function,+,st25r3916_get_irq,uint32_t,const FuriHalSpiBusHandle* +Function,+,st25r3916_mask_irq,void,"const FuriHalSpiBusHandle*, uint32_t" +Function,+,st25r3916_modify_reg,void,"const FuriHalSpiBusHandle*, uint8_t, uint8_t, uint8_t" +Function,+,st25r3916_read_burst_regs,void,"const FuriHalSpiBusHandle*, uint8_t, uint8_t*, uint8_t" +Function,+,st25r3916_read_fifo,_Bool,"const FuriHalSpiBusHandle*, uint8_t*, size_t, size_t*" +Function,+,st25r3916_read_pta_mem,void,"const FuriHalSpiBusHandle*, uint8_t*, size_t" +Function,+,st25r3916_read_reg,void,"const FuriHalSpiBusHandle*, uint8_t, uint8_t*" +Function,+,st25r3916_read_test_reg,void,"const FuriHalSpiBusHandle*, uint8_t, uint8_t*" +Function,+,st25r3916_reg_read_fifo,void,"const FuriHalSpiBusHandle*, uint8_t*, size_t" +Function,+,st25r3916_reg_write_fifo,void,"const FuriHalSpiBusHandle*, const uint8_t*, size_t" +Function,+,st25r3916_set_reg_bits,void,"const FuriHalSpiBusHandle*, uint8_t, uint8_t" +Function,+,st25r3916_write_burst_regs,void,"const FuriHalSpiBusHandle*, uint8_t, const uint8_t*, uint8_t" +Function,+,st25r3916_write_fifo,void,"const FuriHalSpiBusHandle*, const uint8_t*, size_t" +Function,+,st25r3916_write_pta_mem,void,"const FuriHalSpiBusHandle*, const uint8_t*, size_t" +Function,+,st25r3916_write_ptf_mem,void,"const FuriHalSpiBusHandle*, const uint8_t*, size_t" +Function,+,st25r3916_write_pttsn_mem,void,"const FuriHalSpiBusHandle*, uint8_t*, size_t" +Function,+,st25r3916_write_reg,void,"const FuriHalSpiBusHandle*, uint8_t, uint8_t" +Function,+,st25r3916_write_test_reg,void,"const FuriHalSpiBusHandle*, uint8_t, uint8_t" Function,+,storage_common_copy,FS_Error,"Storage*, const char*, const char*" Function,+,storage_common_equivalent_path,_Bool,"Storage*, const char*, const char*" Function,+,storage_common_exists,_Bool,"Storage*, const char*" @@ -2707,6 +2726,7 @@ Function,-,strverscmp,int,"const char*, const char*" Function,-,strxfrm,size_t,"char*, const char*, size_t" Function,-,strxfrm_l,size_t,"char*, const char*, size_t, locale_t" Function,+,submenu_add_item,void,"Submenu*, const char*, uint32_t, SubmenuItemCallback, void*" +Function,+,submenu_add_item_ex,void,"Submenu*, const char*, uint32_t, SubmenuItemCallbackEx, void*" Function,+,submenu_alloc,Submenu*, Function,+,submenu_change_item_label,void,"Submenu*, uint32_t, const char*" Function,+,submenu_free,void,Submenu* @@ -2933,22 +2953,21 @@ Variable,-,__stdio_exit_handler,void (*)(), Variable,+,_ctype_,const char[], Variable,+,_impure_data,_reent, Variable,+,_impure_ptr,_reent*, -Variable,-,_sys_errlist,const char*[], +Variable,-,_sys_errlist,const char* const[], Variable,-,_sys_nerr,int, Variable,-,ble_profile_hid,const FuriHalBleProfileTemplate*, -Variable,-,ble_profile_serial,const FuriHalBleProfileTemplate*, -Variable,+,cli_vcp,CliSession, +Variable,+,ble_profile_serial,const FuriHalBleProfileTemplate* const, Variable,+,compress_config_heatshrink_default,const CompressConfigHeatshrink, -Variable,+,firmware_api_interface,const ElfApiInterface*, +Variable,+,firmware_api_interface,const ElfApiInterface* const, Variable,+,furi_hal_i2c_bus_external,FuriHalI2cBus, Variable,+,furi_hal_i2c_bus_power,FuriHalI2cBus, -Variable,+,furi_hal_i2c_handle_external,FuriHalI2cBusHandle, -Variable,+,furi_hal_i2c_handle_power,FuriHalI2cBusHandle, +Variable,+,furi_hal_i2c_handle_external,const FuriHalI2cBusHandle, +Variable,+,furi_hal_i2c_handle_power,const FuriHalI2cBusHandle, Variable,+,furi_hal_spi_bus_d,FuriHalSpiBus, -Variable,+,furi_hal_spi_bus_handle_display,FuriHalSpiBusHandle, -Variable,+,furi_hal_spi_bus_handle_external,FuriHalSpiBusHandle, -Variable,+,furi_hal_spi_bus_handle_sd_fast,FuriHalSpiBusHandle, -Variable,+,furi_hal_spi_bus_handle_sd_slow,FuriHalSpiBusHandle, +Variable,+,furi_hal_spi_bus_handle_display,const FuriHalSpiBusHandle, +Variable,+,furi_hal_spi_bus_handle_external,const FuriHalSpiBusHandle, +Variable,+,furi_hal_spi_bus_handle_sd_fast,const FuriHalSpiBusHandle, +Variable,+,furi_hal_spi_bus_handle_sd_slow,const FuriHalSpiBusHandle, Variable,+,furi_hal_spi_bus_r,FuriHalSpiBus, Variable,+,furi_hal_spi_preset_1edge_low_16m,const LL_SPI_InitTypeDef, Variable,+,furi_hal_spi_preset_1edge_low_2m,const LL_SPI_InitTypeDef, diff --git a/targets/f18/furi_hal/furi_hal_spi_config.c b/targets/f18/furi_hal/furi_hal_spi_config.c index 8957bfe3a..a7393d3f0 100644 --- a/targets/f18/furi_hal/furi_hal_spi_config.c +++ b/targets/f18/furi_hal/furi_hal_spi_config.c @@ -143,7 +143,7 @@ FuriHalSpiBus furi_hal_spi_bus_d = { /* SPI Bus Handles */ inline static void furi_hal_spi_bus_r_handle_event_callback( - FuriHalSpiBusHandle* handle, + const FuriHalSpiBusHandle* handle, FuriHalSpiBusHandleEvent event, const LL_SPI_InitTypeDef* preset) { if(event == FuriHalSpiBusHandleEventInit) { @@ -189,7 +189,7 @@ inline static void furi_hal_spi_bus_r_handle_event_callback( } inline static void furi_hal_spi_bus_nfc_handle_event_callback( - FuriHalSpiBusHandle* handle, + const FuriHalSpiBusHandle* handle, FuriHalSpiBusHandleEvent event, const LL_SPI_InitTypeDef* preset) { if(event == FuriHalSpiBusHandleEventInit) { @@ -255,12 +255,12 @@ inline static void furi_hal_spi_bus_nfc_handle_event_callback( } static void furi_hal_spi_bus_handle_external_event_callback( - FuriHalSpiBusHandle* handle, + const FuriHalSpiBusHandle* handle, FuriHalSpiBusHandleEvent event) { furi_hal_spi_bus_r_handle_event_callback(handle, event, &furi_hal_spi_preset_1edge_low_2m); } -FuriHalSpiBusHandle furi_hal_spi_bus_handle_external = { +const FuriHalSpiBusHandle furi_hal_spi_bus_handle_external = { .bus = &furi_hal_spi_bus_r, .callback = furi_hal_spi_bus_handle_external_event_callback, .miso = &gpio_ext_pa6, @@ -270,7 +270,7 @@ FuriHalSpiBusHandle furi_hal_spi_bus_handle_external = { }; inline static void furi_hal_spi_bus_d_handle_event_callback( - FuriHalSpiBusHandle* handle, + const FuriHalSpiBusHandle* handle, FuriHalSpiBusHandleEvent event, const LL_SPI_InitTypeDef* preset) { if(event == FuriHalSpiBusHandleEventInit) { @@ -311,12 +311,12 @@ inline static void furi_hal_spi_bus_d_handle_event_callback( } static void furi_hal_spi_bus_handle_display_event_callback( - FuriHalSpiBusHandle* handle, + const FuriHalSpiBusHandle* handle, FuriHalSpiBusHandleEvent event) { furi_hal_spi_bus_d_handle_event_callback(handle, event, &furi_hal_spi_preset_1edge_low_4m); } -FuriHalSpiBusHandle furi_hal_spi_bus_handle_display = { +const FuriHalSpiBusHandle furi_hal_spi_bus_handle_display = { .bus = &furi_hal_spi_bus_d, .callback = furi_hal_spi_bus_handle_display_event_callback, .miso = &gpio_spi_d_miso, @@ -326,12 +326,12 @@ FuriHalSpiBusHandle furi_hal_spi_bus_handle_display = { }; static void furi_hal_spi_bus_handle_sd_fast_event_callback( - FuriHalSpiBusHandle* handle, + const FuriHalSpiBusHandle* handle, FuriHalSpiBusHandleEvent event) { furi_hal_spi_bus_d_handle_event_callback(handle, event, &furi_hal_spi_preset_1edge_low_16m); } -FuriHalSpiBusHandle furi_hal_spi_bus_handle_sd_fast = { +const FuriHalSpiBusHandle furi_hal_spi_bus_handle_sd_fast = { .bus = &furi_hal_spi_bus_d, .callback = furi_hal_spi_bus_handle_sd_fast_event_callback, .miso = &gpio_spi_d_miso, @@ -341,12 +341,12 @@ FuriHalSpiBusHandle furi_hal_spi_bus_handle_sd_fast = { }; static void furi_hal_spi_bus_handle_sd_slow_event_callback( - FuriHalSpiBusHandle* handle, + const FuriHalSpiBusHandle* handle, FuriHalSpiBusHandleEvent event) { furi_hal_spi_bus_d_handle_event_callback(handle, event, &furi_hal_spi_preset_1edge_low_2m); } -FuriHalSpiBusHandle furi_hal_spi_bus_handle_sd_slow = { +const FuriHalSpiBusHandle furi_hal_spi_bus_handle_sd_slow = { .bus = &furi_hal_spi_bus_d, .callback = furi_hal_spi_bus_handle_sd_slow_event_callback, .miso = &gpio_spi_d_miso, diff --git a/targets/f18/furi_hal/furi_hal_spi_config.h b/targets/f18/furi_hal/furi_hal_spi_config.h index da39fbfa6..2ff8b41b1 100644 --- a/targets/f18/furi_hal/furi_hal_spi_config.h +++ b/targets/f18/furi_hal/furi_hal_spi_config.h @@ -39,16 +39,16 @@ extern FuriHalSpiBus furi_hal_spi_bus_d; * Bus pins are floating on inactive state, CS high after initialization * */ -extern FuriHalSpiBusHandle furi_hal_spi_bus_handle_external; +extern const FuriHalSpiBusHandle furi_hal_spi_bus_handle_external; /** ST7567(Display) on `furi_hal_spi_bus_d` */ -extern FuriHalSpiBusHandle furi_hal_spi_bus_handle_display; +extern const FuriHalSpiBusHandle furi_hal_spi_bus_handle_display; /** SdCard in fast mode on `furi_hal_spi_bus_d` */ -extern FuriHalSpiBusHandle furi_hal_spi_bus_handle_sd_fast; +extern const FuriHalSpiBusHandle furi_hal_spi_bus_handle_sd_fast; /** SdCard in slow mode on `furi_hal_spi_bus_d` */ -extern FuriHalSpiBusHandle furi_hal_spi_bus_handle_sd_slow; +extern const FuriHalSpiBusHandle furi_hal_spi_bus_handle_sd_slow; #ifdef __cplusplus } diff --git a/targets/f18/target.json b/targets/f18/target.json index 3452c6707..1a8306596 100644 --- a/targets/f18/target.json +++ b/targets/f18/target.json @@ -36,7 +36,8 @@ "flipper18", "bit_lib", "toolbox", - "datetime" + "datetime", + "ieee754_parse_wrap" ], "excluded_sources": [ "furi_hal_infrared.c", diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 678d465a1..7ea2084c9 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -1,9 +1,8 @@ entry,status,name,type,params -Version,+,83.0,, +Version,+,85.0,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/bt/bt_service/bt_keys_storage.h,, -Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, Header,+,applications/services/dialogs/dialogs.h,, Header,+,applications/services/dolphin/dolphin.h,, @@ -61,6 +60,7 @@ Header,+,lib/flipper_format/flipper_format_stream.h,, Header,+,lib/ibutton/ibutton_key.h,, Header,+,lib/ibutton/ibutton_protocols.h,, Header,+,lib/ibutton/ibutton_worker.h,, +Header,+,lib/ieee754_parse_wrap/wrappers.h,, Header,+,lib/infrared/encoder_decoder/infrared.h,, Header,+,lib/infrared/worker/infrared_transmit.h,, Header,+,lib/infrared/worker/infrared_worker.h,, @@ -232,6 +232,10 @@ Header,+,lib/subghz/transmitter.h,, Header,+,lib/toolbox/api_lock.h,, Header,+,lib/toolbox/args.h,, Header,+,lib/toolbox/bit_buffer.h,, +Header,+,lib/toolbox/cli/cli_ansi.h,, +Header,+,lib/toolbox/cli/cli_command.h,, +Header,+,lib/toolbox/cli/cli_registry.h,, +Header,+,lib/toolbox/cli/shell/cli_shell.h,, Header,+,lib/toolbox/compress.h,, Header,+,lib/toolbox/crc32_calc.h,, Header,+,lib/toolbox/dir_walk.h,, @@ -468,6 +472,8 @@ Function,+,__wrap_putc,int,"int, FILE*" Function,+,__wrap_putchar,int,int Function,+,__wrap_puts,int,const char* Function,+,__wrap_snprintf,int,"char*, size_t, const char*, ..." +Function,+,__wrap_strtod,double,"const char*, char**" +Function,+,__wrap_strtof,float,"const char*, char**" Function,+,__wrap_ungetc,int,"int, FILE*" Function,+,__wrap_vsnprintf,int,"char*, size_t, const char*, va_list" Function,-,_asiprintf_r,int,"_reent*, char**, const char*, ..." @@ -861,18 +867,26 @@ Function,-,ceill,long double,long double Function,-,cfree,void,void* Function,-,clearerr,void,FILE* Function,-,clearerr_unlocked,void,FILE* -Function,+,cli_add_command,void,"Cli*, const char*, CliCommandFlag, CliCallback, void*" -Function,+,cli_cmd_interrupt_received,_Bool,Cli* -Function,+,cli_delete_command,void,"Cli*, const char*" -Function,+,cli_getc,char,Cli* -Function,+,cli_is_connected,_Bool,Cli* -Function,+,cli_nl,void,Cli* +Function,+,cli_ansi_parser_alloc,CliAnsiParser*, +Function,+,cli_ansi_parser_feed,CliAnsiParserResult,"CliAnsiParser*, char" +Function,+,cli_ansi_parser_feed_timeout,CliAnsiParserResult,CliAnsiParser* +Function,+,cli_ansi_parser_free,void,CliAnsiParser* +Function,+,cli_is_pipe_broken_or_is_etx_next_char,_Bool,PipeSide* Function,+,cli_print_usage,void,"const char*, const char*, const char*" -Function,+,cli_read,size_t,"Cli*, uint8_t*, size_t" -Function,+,cli_read_timeout,size_t,"Cli*, uint8_t*, size_t, uint32_t" -Function,+,cli_session_close,void,Cli* -Function,+,cli_session_open,void,"Cli*, void*" -Function,+,cli_write,void,"Cli*, const uint8_t*, size_t" +Function,+,cli_registry_add_command,void,"CliRegistry*, const char*, CliCommandFlag, CliCommandExecuteCallback, void*" +Function,+,cli_registry_add_command_ex,void,"CliRegistry*, const char*, CliCommandFlag, CliCommandExecuteCallback, void*, size_t" +Function,+,cli_registry_alloc,CliRegistry*, +Function,+,cli_registry_delete_command,void,"CliRegistry*, const char*" +Function,+,cli_registry_free,void,CliRegistry* +Function,+,cli_registry_reload_external_commands,void,"CliRegistry*, const CliCommandExternalConfig*" +Function,+,cli_registry_remove_external_commands,void,CliRegistry* +Function,+,cli_shell_alloc,CliShell*,"CliShellMotd, void*, PipeSide*, CliRegistry*, const CliCommandExternalConfig*" +Function,+,cli_shell_free,void,CliShell* +Function,+,cli_shell_join,void,CliShell* +Function,+,cli_shell_set_prompt,void,"CliShell*, const char*" +Function,+,cli_shell_start,void,CliShell* +Function,+,cli_vcp_disable,void,CliVcp* +Function,+,cli_vcp_enable,void,CliVcp* Function,+,composite_api_resolver_add,void,"CompositeApiResolver*, const ElfApiInterface*" Function,+,composite_api_resolver_alloc,CompositeApiResolver*, Function,+,composite_api_resolver_free,void,CompositeApiResolver* @@ -1058,7 +1072,7 @@ Function,+,felica_get_uid,const uint8_t*,"const FelicaData*, size_t*" Function,+,felica_is_equal,_Bool,"const FelicaData*, const FelicaData*" Function,+,felica_load,_Bool,"FelicaData*, FlipperFormat*, uint32_t" Function,+,felica_poller_activate,FelicaError,"FelicaPoller*, FelicaData*" -Function,+,felica_poller_read_blocks,FelicaError,"FelicaPoller*, const uint8_t, const uint8_t*, uint16_t, FelicaPollerReadCommandResponse**" +Function,+,felica_poller_read_blocks,FelicaError,"FelicaPoller*, const uint8_t, const uint8_t* const, uint16_t, FelicaPollerReadCommandResponse** const" Function,+,felica_poller_sync_read,FelicaError,"Nfc*, FelicaData*, const FelicaCardKey*" Function,+,felica_reset,void,FelicaData* Function,+,felica_save,_Bool,"const FelicaData*, FlipperFormat*" @@ -1256,6 +1270,7 @@ Function,+,furi_event_loop_subscribe_message_queue,void,"FuriEventLoop*, FuriMes Function,+,furi_event_loop_subscribe_mutex,void,"FuriEventLoop*, FuriMutex*, FuriEventLoopEvent, FuriEventLoopEventCallback, void*" Function,+,furi_event_loop_subscribe_semaphore,void,"FuriEventLoop*, FuriSemaphore*, FuriEventLoopEvent, FuriEventLoopEventCallback, void*" Function,+,furi_event_loop_subscribe_stream_buffer,void,"FuriEventLoop*, FuriStreamBuffer*, FuriEventLoopEvent, FuriEventLoopEventCallback, void*" +Function,+,furi_event_loop_subscribe_thread_flags,void,"FuriEventLoop*, FuriEventLoopThreadFlagsCallback, void*" Function,+,furi_event_loop_tick_set,void,"FuriEventLoop*, uint32_t, FuriEventLoopTickCallback, void*" Function,+,furi_event_loop_timer_alloc,FuriEventLoopTimer*,"FuriEventLoop*, FuriEventLoopTimerCallback, FuriEventLoopTimerType, void*" Function,+,furi_event_loop_timer_free,void,FuriEventLoopTimer* @@ -1266,6 +1281,7 @@ Function,+,furi_event_loop_timer_restart,void,FuriEventLoopTimer* Function,+,furi_event_loop_timer_start,void,"FuriEventLoopTimer*, uint32_t" Function,+,furi_event_loop_timer_stop,void,FuriEventLoopTimer* Function,+,furi_event_loop_unsubscribe,void,"FuriEventLoop*, FuriEventLoopObject*" +Function,+,furi_event_loop_unsubscribe_thread_flags,void,FuriEventLoop* Function,+,furi_get_tick,uint32_t, Function,+,furi_hal_adc_acquire,FuriHalAdcHandle*, Function,+,furi_hal_adc_configure,void,FuriHalAdcHandle* @@ -1408,23 +1424,23 @@ Function,+,furi_hal_hid_u2f_get_request,uint32_t,uint8_t* Function,+,furi_hal_hid_u2f_is_connected,_Bool, Function,+,furi_hal_hid_u2f_send_response,void,"uint8_t*, uint8_t" Function,+,furi_hal_hid_u2f_set_callback,void,"HidU2fCallback, void*" -Function,+,furi_hal_i2c_acquire,void,FuriHalI2cBusHandle* +Function,+,furi_hal_i2c_acquire,void,const FuriHalI2cBusHandle* Function,-,furi_hal_i2c_deinit_early,void, Function,-,furi_hal_i2c_init,void, Function,-,furi_hal_i2c_init_early,void, -Function,+,furi_hal_i2c_is_device_ready,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint32_t" -Function,+,furi_hal_i2c_read_mem,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t, uint8_t*, size_t, uint32_t" -Function,+,furi_hal_i2c_read_reg_16,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t, uint16_t*, uint32_t" -Function,+,furi_hal_i2c_read_reg_8,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t, uint8_t*, uint32_t" -Function,+,furi_hal_i2c_release,void,FuriHalI2cBusHandle* -Function,+,furi_hal_i2c_rx,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t*, size_t, uint32_t" -Function,+,furi_hal_i2c_rx_ext,_Bool,"FuriHalI2cBusHandle*, uint16_t, _Bool, uint8_t*, size_t, FuriHalI2cBegin, FuriHalI2cEnd, uint32_t" -Function,+,furi_hal_i2c_trx,_Bool,"FuriHalI2cBusHandle*, uint8_t, const uint8_t*, size_t, uint8_t*, size_t, uint32_t" -Function,+,furi_hal_i2c_tx,_Bool,"FuriHalI2cBusHandle*, uint8_t, const uint8_t*, size_t, uint32_t" -Function,+,furi_hal_i2c_tx_ext,_Bool,"FuriHalI2cBusHandle*, uint16_t, _Bool, const uint8_t*, size_t, FuriHalI2cBegin, FuriHalI2cEnd, uint32_t" -Function,+,furi_hal_i2c_write_mem,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t, const uint8_t*, size_t, uint32_t" -Function,+,furi_hal_i2c_write_reg_16,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t, uint16_t, uint32_t" -Function,+,furi_hal_i2c_write_reg_8,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t, uint8_t, uint32_t" +Function,+,furi_hal_i2c_is_device_ready,_Bool,"const FuriHalI2cBusHandle*, uint8_t, uint32_t" +Function,+,furi_hal_i2c_read_mem,_Bool,"const FuriHalI2cBusHandle*, uint8_t, uint8_t, uint8_t*, size_t, uint32_t" +Function,+,furi_hal_i2c_read_reg_16,_Bool,"const FuriHalI2cBusHandle*, uint8_t, uint8_t, uint16_t*, uint32_t" +Function,+,furi_hal_i2c_read_reg_8,_Bool,"const FuriHalI2cBusHandle*, uint8_t, uint8_t, uint8_t*, uint32_t" +Function,+,furi_hal_i2c_release,void,const FuriHalI2cBusHandle* +Function,+,furi_hal_i2c_rx,_Bool,"const FuriHalI2cBusHandle*, uint8_t, uint8_t*, size_t, uint32_t" +Function,+,furi_hal_i2c_rx_ext,_Bool,"const FuriHalI2cBusHandle*, uint16_t, _Bool, uint8_t*, size_t, FuriHalI2cBegin, FuriHalI2cEnd, uint32_t" +Function,+,furi_hal_i2c_trx,_Bool,"const FuriHalI2cBusHandle*, uint8_t, const uint8_t*, size_t, uint8_t*, size_t, uint32_t" +Function,+,furi_hal_i2c_tx,_Bool,"const FuriHalI2cBusHandle*, uint8_t, const uint8_t*, size_t, uint32_t" +Function,+,furi_hal_i2c_tx_ext,_Bool,"const FuriHalI2cBusHandle*, uint16_t, _Bool, const uint8_t*, size_t, FuriHalI2cBegin, FuriHalI2cEnd, uint32_t" +Function,+,furi_hal_i2c_write_mem,_Bool,"const FuriHalI2cBusHandle*, uint8_t, uint8_t, const uint8_t*, size_t, uint32_t" +Function,+,furi_hal_i2c_write_reg_16,_Bool,"const FuriHalI2cBusHandle*, uint8_t, uint8_t, uint16_t, uint32_t" +Function,+,furi_hal_i2c_write_reg_8,_Bool,"const FuriHalI2cBusHandle*, uint8_t, uint8_t, uint8_t, uint32_t" Function,+,furi_hal_ibutton_emulate_set_next,void,uint32_t Function,+,furi_hal_ibutton_emulate_start,void,"uint32_t, FuriHalIbuttonEmulateCallback, void*" Function,+,furi_hal_ibutton_emulate_stop,void, @@ -1669,20 +1685,20 @@ Function,+,furi_hal_speaker_release,void, Function,+,furi_hal_speaker_set_volume,void,float Function,+,furi_hal_speaker_start,void,"float, float" Function,+,furi_hal_speaker_stop,void, -Function,+,furi_hal_spi_acquire,void,FuriHalSpiBusHandle* +Function,+,furi_hal_spi_acquire,void,const FuriHalSpiBusHandle* Function,+,furi_hal_spi_bus_deinit,void,FuriHalSpiBus* -Function,+,furi_hal_spi_bus_handle_deinit,void,FuriHalSpiBusHandle* -Function,+,furi_hal_spi_bus_handle_init,void,FuriHalSpiBusHandle* +Function,+,furi_hal_spi_bus_handle_deinit,void,const FuriHalSpiBusHandle* +Function,+,furi_hal_spi_bus_handle_init,void,const FuriHalSpiBusHandle* Function,+,furi_hal_spi_bus_init,void,FuriHalSpiBus* -Function,+,furi_hal_spi_bus_rx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, size_t, uint32_t" -Function,+,furi_hal_spi_bus_trx,_Bool,"FuriHalSpiBusHandle*, const uint8_t*, uint8_t*, size_t, uint32_t" -Function,+,furi_hal_spi_bus_trx_dma,_Bool,"FuriHalSpiBusHandle*, uint8_t*, uint8_t*, size_t, uint32_t" -Function,+,furi_hal_spi_bus_tx,_Bool,"FuriHalSpiBusHandle*, const uint8_t*, size_t, uint32_t" +Function,+,furi_hal_spi_bus_rx,_Bool,"const FuriHalSpiBusHandle*, uint8_t*, size_t, uint32_t" +Function,+,furi_hal_spi_bus_trx,_Bool,"const FuriHalSpiBusHandle*, const uint8_t*, uint8_t*, size_t, uint32_t" +Function,+,furi_hal_spi_bus_trx_dma,_Bool,"const FuriHalSpiBusHandle*, uint8_t*, uint8_t*, size_t, uint32_t" +Function,+,furi_hal_spi_bus_tx,_Bool,"const FuriHalSpiBusHandle*, const uint8_t*, size_t, uint32_t" Function,-,furi_hal_spi_config_deinit_early,void, Function,-,furi_hal_spi_config_init,void, Function,-,furi_hal_spi_config_init_early,void, Function,-,furi_hal_spi_dma_init,void, -Function,+,furi_hal_spi_release,void,FuriHalSpiBusHandle* +Function,+,furi_hal_spi_release,void,const FuriHalSpiBusHandle* Function,-,furi_hal_subghz_dump_state,void, Function,+,furi_hal_subghz_flush_rx,void, Function,+,furi_hal_subghz_flush_tx,void, @@ -1725,7 +1741,7 @@ Function,+,furi_hal_usb_is_locked,_Bool, Function,+,furi_hal_usb_lock,void, Function,+,furi_hal_usb_reinit,void, Function,+,furi_hal_usb_set_config,_Bool,"FuriHalUsbInterface*, void*" -Function,-,furi_hal_usb_set_state_callback,void,"FuriHalUsbStateCallback, void*" +Function,+,furi_hal_usb_set_state_callback,void,"FuriHalUsbStateCallback, void*" Function,+,furi_hal_usb_unlock,void, Function,+,furi_hal_version_do_i_belong_here,_Bool, Function,+,furi_hal_version_get_ble_local_device_name_ptr,const char*, @@ -1963,7 +1979,7 @@ Function,-,getchar,int, Function,-,getchar_unlocked,int, Function,-,getenv,char*,const char* Function,-,gets,char*,char* -Function,-,getsubopt,int,"char**, char**, char**" +Function,-,getsubopt,int,"char**, char* const*, char**" Function,-,getw,int,FILE* Function,+,gui_add_framebuffer_callback,void,"Gui*, GuiCanvasCommitCallback, void*" Function,+,gui_add_view_port,void,"Gui*, ViewPort*, GuiLayer" @@ -2292,6 +2308,9 @@ Function,-,llrintl,long long int,long double Function,-,llround,long long int,double Function,-,llroundf,long long int,float Function,-,llroundl,long long int,long double +Function,+,loader_clear_launch_queue,void,Loader* +Function,+,loader_enqueue_launch,void,"Loader*, const char*, const char*, LoaderDeferredLaunchFlag" +Function,+,loader_get_application_launch_path,_Bool,"Loader*, FuriString*" Function,+,loader_get_application_name,_Bool,"Loader*, FuriString*" Function,+,loader_get_pubsub,FuriPubSub*,Loader* Function,+,loader_is_locked,_Bool,Loader* @@ -2679,7 +2698,7 @@ Function,+,mf_ultralight_3des_decrypt,void,"mbedtls_des3_context*, const uint8_t Function,+,mf_ultralight_3des_encrypt,void,"mbedtls_des3_context*, const uint8_t*, const uint8_t*, const uint8_t*, const uint8_t, uint8_t*" Function,+,mf_ultralight_3des_get_key,const uint8_t*,const MfUltralightData* Function,+,mf_ultralight_3des_key_valid,_Bool,const MfUltralightData* -Function,+,mf_ultralight_3des_shift_data,void,uint8_t* +Function,+,mf_ultralight_3des_shift_data,void,uint8_t* const Function,+,mf_ultralight_alloc,MfUltralightData*, Function,+,mf_ultralight_copy,void,"MfUltralightData*, const MfUltralightData*" Function,+,mf_ultralight_detect_protocol,_Bool,const Iso14443_3aData* @@ -2912,7 +2931,7 @@ Function,+,ntag4xx_copy,void,"Ntag4xxData*, const Ntag4xxData*" Function,+,ntag4xx_free,void,Ntag4xxData* Function,+,ntag4xx_get_base_data,Iso14443_4aData*,const Ntag4xxData* Function,+,ntag4xx_get_device_name,const char*,"const Ntag4xxData*, NfcDeviceNameType" -Function,+,ntag4xx_get_type_from_version,Ntag4xxType,const Ntag4xxVersion* +Function,+,ntag4xx_get_type_from_version,Ntag4xxType,const Ntag4xxVersion* const Function,+,ntag4xx_get_uid,const uint8_t*,"const Ntag4xxData*, size_t*" Function,+,ntag4xx_is_equal,_Bool,"const Ntag4xxData*, const Ntag4xxData*" Function,+,ntag4xx_load,_Bool,"Ntag4xxData*, FlipperFormat*, uint32_t" @@ -3000,14 +3019,14 @@ Function,+,pipe_bytes_available,size_t,PipeSide* Function,+,pipe_detach_from_event_loop,void,PipeSide* Function,+,pipe_free,void,PipeSide* Function,+,pipe_install_as_stdio,void,PipeSide* -Function,+,pipe_receive,size_t,"PipeSide*, void*, size_t, FuriWait" +Function,+,pipe_receive,size_t,"PipeSide*, void*, size_t" Function,+,pipe_role,PipeRole,PipeSide* -Function,+,pipe_send,size_t,"PipeSide*, const void*, size_t, FuriWait" +Function,+,pipe_send,size_t,"PipeSide*, const void*, size_t" Function,+,pipe_set_broken_callback,void,"PipeSide*, PipeSideBrokenCallback, FuriEventLoopEvent" Function,+,pipe_set_callback_context,void,"PipeSide*, void*" Function,+,pipe_set_data_arrived_callback,void,"PipeSide*, PipeSideDataArrivedCallback, FuriEventLoopEvent" Function,+,pipe_set_space_freed_callback,void,"PipeSide*, PipeSideSpaceFreedCallback, FuriEventLoopEvent" -Function,+,pipe_set_stdout_timeout,void,"PipeSide*, FuriWait" +Function,+,pipe_set_state_check_period,void,"PipeSide*, FuriWait" Function,+,pipe_spaces_available,size_t,PipeSide* Function,+,pipe_state,PipeState,PipeSide* Function,+,plugin_manager_alloc,PluginManager*,"const char*, uint32_t, const ElfApiInterface*" @@ -3035,19 +3054,19 @@ Function,-,pow,double,"double, double" Function,-,pow10,double,double Function,-,pow10f,float,float Function,+,power_enable_low_battery_level_notification,void,"Power*, _Bool" +Function,+,power_enable_otg,void,"Power*, _Bool" Function,+,power_get_info,void,"Power*, PowerInfo*" Function,+,power_get_pubsub,FuriPubSub*,Power* Function,+,power_is_battery_healthy,_Bool,Power* Function,+,power_is_otg_enabled,_Bool,Power* Function,+,power_off,void,Power* Function,+,power_reboot,void,"Power*, PowerBootMode" -Function,+,power_enable_otg,void,"Power*, _Bool" Function,+,powf,float,"float, float" Function,-,powl,long double,"long double, long double" Function,+,pretty_format_bytes_hex_canonical,void,"FuriString*, size_t, const char*, const uint8_t*, size_t" Function,-,printf,int,"const char*, ..." Function,+,property_value_out,void,"PropertyValueContext*, const char*, unsigned int, ..." -Function,+,protocol_dict_alloc,ProtocolDict*,"const ProtocolBase**, size_t" +Function,+,protocol_dict_alloc,ProtocolDict*,"const ProtocolBase* const*, size_t" Function,+,protocol_dict_decoders_feed,ProtocolId,"ProtocolDict*, _Bool, uint32_t" Function,+,protocol_dict_decoders_feed_by_feature,ProtocolId,"ProtocolDict*, uint32_t, _Bool, uint32_t" Function,+,protocol_dict_decoders_feed_by_id,ProtocolId,"ProtocolDict*, size_t, _Bool, uint32_t" @@ -3235,29 +3254,29 @@ Function,+,srand,void,unsigned Function,-,srand48,void,long Function,-,srandom,void,unsigned Function,+,sscanf,int,"const char*, const char*, ..." -Function,+,st25r3916_change_reg_bits,void,"FuriHalSpiBusHandle*, uint8_t, uint8_t, uint8_t" -Function,+,st25r3916_change_test_reg_bits,void,"FuriHalSpiBusHandle*, uint8_t, uint8_t, uint8_t" -Function,+,st25r3916_check_reg,_Bool,"FuriHalSpiBusHandle*, uint8_t, uint8_t, uint8_t" -Function,+,st25r3916_clear_reg_bits,void,"FuriHalSpiBusHandle*, uint8_t, uint8_t" -Function,+,st25r3916_direct_cmd,void,"FuriHalSpiBusHandle*, uint8_t" -Function,+,st25r3916_get_irq,uint32_t,FuriHalSpiBusHandle* -Function,+,st25r3916_mask_irq,void,"FuriHalSpiBusHandle*, uint32_t" -Function,+,st25r3916_modify_reg,void,"FuriHalSpiBusHandle*, uint8_t, uint8_t, uint8_t" -Function,+,st25r3916_read_burst_regs,void,"FuriHalSpiBusHandle*, uint8_t, uint8_t*, uint8_t" -Function,+,st25r3916_read_fifo,_Bool,"FuriHalSpiBusHandle*, uint8_t*, size_t, size_t*" -Function,+,st25r3916_read_pta_mem,void,"FuriHalSpiBusHandle*, uint8_t*, size_t" -Function,+,st25r3916_read_reg,void,"FuriHalSpiBusHandle*, uint8_t, uint8_t*" -Function,+,st25r3916_read_test_reg,void,"FuriHalSpiBusHandle*, uint8_t, uint8_t*" -Function,+,st25r3916_reg_read_fifo,void,"FuriHalSpiBusHandle*, uint8_t*, size_t" -Function,+,st25r3916_reg_write_fifo,void,"FuriHalSpiBusHandle*, const uint8_t*, size_t" -Function,+,st25r3916_set_reg_bits,void,"FuriHalSpiBusHandle*, uint8_t, uint8_t" -Function,+,st25r3916_write_burst_regs,void,"FuriHalSpiBusHandle*, uint8_t, const uint8_t*, uint8_t" -Function,+,st25r3916_write_fifo,void,"FuriHalSpiBusHandle*, const uint8_t*, size_t" -Function,+,st25r3916_write_pta_mem,void,"FuriHalSpiBusHandle*, const uint8_t*, size_t" -Function,+,st25r3916_write_ptf_mem,void,"FuriHalSpiBusHandle*, const uint8_t*, size_t" -Function,+,st25r3916_write_pttsn_mem,void,"FuriHalSpiBusHandle*, uint8_t*, size_t" -Function,+,st25r3916_write_reg,void,"FuriHalSpiBusHandle*, uint8_t, uint8_t" -Function,+,st25r3916_write_test_reg,void,"FuriHalSpiBusHandle*, uint8_t, uint8_t" +Function,+,st25r3916_change_reg_bits,void,"const FuriHalSpiBusHandle*, uint8_t, uint8_t, uint8_t" +Function,+,st25r3916_change_test_reg_bits,void,"const FuriHalSpiBusHandle*, uint8_t, uint8_t, uint8_t" +Function,+,st25r3916_check_reg,_Bool,"const FuriHalSpiBusHandle*, uint8_t, uint8_t, uint8_t" +Function,+,st25r3916_clear_reg_bits,void,"const FuriHalSpiBusHandle*, uint8_t, uint8_t" +Function,+,st25r3916_direct_cmd,void,"const FuriHalSpiBusHandle*, uint8_t" +Function,+,st25r3916_get_irq,uint32_t,const FuriHalSpiBusHandle* +Function,+,st25r3916_mask_irq,void,"const FuriHalSpiBusHandle*, uint32_t" +Function,+,st25r3916_modify_reg,void,"const FuriHalSpiBusHandle*, uint8_t, uint8_t, uint8_t" +Function,+,st25r3916_read_burst_regs,void,"const FuriHalSpiBusHandle*, uint8_t, uint8_t*, uint8_t" +Function,+,st25r3916_read_fifo,_Bool,"const FuriHalSpiBusHandle*, uint8_t*, size_t, size_t*" +Function,+,st25r3916_read_pta_mem,void,"const FuriHalSpiBusHandle*, uint8_t*, size_t" +Function,+,st25r3916_read_reg,void,"const FuriHalSpiBusHandle*, uint8_t, uint8_t*" +Function,+,st25r3916_read_test_reg,void,"const FuriHalSpiBusHandle*, uint8_t, uint8_t*" +Function,+,st25r3916_reg_read_fifo,void,"const FuriHalSpiBusHandle*, uint8_t*, size_t" +Function,+,st25r3916_reg_write_fifo,void,"const FuriHalSpiBusHandle*, const uint8_t*, size_t" +Function,+,st25r3916_set_reg_bits,void,"const FuriHalSpiBusHandle*, uint8_t, uint8_t" +Function,+,st25r3916_write_burst_regs,void,"const FuriHalSpiBusHandle*, uint8_t, const uint8_t*, uint8_t" +Function,+,st25r3916_write_fifo,void,"const FuriHalSpiBusHandle*, const uint8_t*, size_t" +Function,+,st25r3916_write_pta_mem,void,"const FuriHalSpiBusHandle*, const uint8_t*, size_t" +Function,+,st25r3916_write_ptf_mem,void,"const FuriHalSpiBusHandle*, const uint8_t*, size_t" +Function,+,st25r3916_write_pttsn_mem,void,"const FuriHalSpiBusHandle*, uint8_t*, size_t" +Function,+,st25r3916_write_reg,void,"const FuriHalSpiBusHandle*, uint8_t, uint8_t" +Function,+,st25r3916_write_test_reg,void,"const FuriHalSpiBusHandle*, uint8_t, uint8_t" Function,+,st25tb_alloc,St25tbData*, Function,+,st25tb_copy,void,"St25tbData*, const St25tbData*" Function,+,st25tb_free,void,St25tbData* @@ -3485,13 +3504,13 @@ Function,+,subghz_file_encoder_worker_get_level_duration,LevelDuration,void* Function,+,subghz_file_encoder_worker_is_running,_Bool,SubGhzFileEncoderWorker* Function,+,subghz_file_encoder_worker_start,_Bool,"SubGhzFileEncoderWorker*, const char*, const char*" Function,+,subghz_file_encoder_worker_stop,void,SubGhzFileEncoderWorker* -Function,-,subghz_keystore_alloc,SubGhzKeystore*, -Function,-,subghz_keystore_free,void,SubGhzKeystore* -Function,-,subghz_keystore_get_data,SubGhzKeyArray_t*,SubGhzKeystore* -Function,-,subghz_keystore_load,_Bool,"SubGhzKeystore*, const char*" -Function,-,subghz_keystore_raw_encrypted_save,_Bool,"const char*, const char*, uint8_t*" -Function,-,subghz_keystore_raw_get_data,_Bool,"const char*, size_t, uint8_t*, size_t" -Function,-,subghz_keystore_save,_Bool,"SubGhzKeystore*, const char*, uint8_t*" +Function,+,subghz_keystore_alloc,SubGhzKeystore*, +Function,+,subghz_keystore_free,void,SubGhzKeystore* +Function,+,subghz_keystore_get_data,SubGhzKeyArray_t*,SubGhzKeystore* +Function,+,subghz_keystore_load,_Bool,"SubGhzKeystore*, const char*" +Function,+,subghz_keystore_raw_encrypted_save,_Bool,"const char*, const char*, uint8_t*" +Function,+,subghz_keystore_raw_get_data,_Bool,"const char*, size_t, uint8_t*, size_t" +Function,+,subghz_keystore_save,_Bool,"SubGhzKeystore*, const char*, uint8_t*" Function,+,subghz_protocol_blocks_add_bit,void,"SubGhzBlockDecoder*, uint8_t" Function,+,subghz_protocol_blocks_add_bytes,uint8_t,"const uint8_t[], size_t" Function,+,subghz_protocol_blocks_add_to_128_bit,void,"SubGhzBlockDecoder*, uint8_t, uint64_t*" @@ -3592,6 +3611,7 @@ Function,+,subghz_worker_set_pair_callback,void,"SubGhzWorker*, SubGhzWorkerPair Function,+,subghz_worker_start,void,SubGhzWorker* Function,+,subghz_worker_stop,void,SubGhzWorker* Function,+,submenu_add_item,void,"Submenu*, const char*, uint32_t, SubmenuItemCallback, void*" +Function,+,submenu_add_item_ex,void,"Submenu*, const char*, uint32_t, SubmenuItemCallbackEx, void*" Function,+,submenu_alloc,Submenu*, Function,+,submenu_change_item_label,void,"Submenu*, uint32_t, const char*" Function,+,submenu_free,void,Submenu* @@ -3833,24 +3853,23 @@ Variable,-,__stdio_exit_handler,void (*)(), Variable,+,_ctype_,const char[], Variable,+,_impure_data,_reent, Variable,+,_impure_ptr,_reent*, -Variable,-,_sys_errlist,const char*[], +Variable,-,_sys_errlist,const char* const[], Variable,-,_sys_nerr,int, Variable,-,ble_profile_hid,const FuriHalBleProfileTemplate*, -Variable,-,ble_profile_serial,const FuriHalBleProfileTemplate*, -Variable,+,cli_vcp,CliSession, +Variable,+,ble_profile_serial,const FuriHalBleProfileTemplate* const, Variable,+,compress_config_heatshrink_default,const CompressConfigHeatshrink, -Variable,+,firmware_api_interface,const ElfApiInterface*, +Variable,+,firmware_api_interface,const ElfApiInterface* const, Variable,+,furi_hal_i2c_bus_external,FuriHalI2cBus, Variable,+,furi_hal_i2c_bus_power,FuriHalI2cBus, -Variable,+,furi_hal_i2c_handle_external,FuriHalI2cBusHandle, -Variable,+,furi_hal_i2c_handle_power,FuriHalI2cBusHandle, +Variable,+,furi_hal_i2c_handle_external,const FuriHalI2cBusHandle, +Variable,+,furi_hal_i2c_handle_power,const FuriHalI2cBusHandle, Variable,+,furi_hal_spi_bus_d,FuriHalSpiBus, -Variable,+,furi_hal_spi_bus_handle_display,FuriHalSpiBusHandle, -Variable,+,furi_hal_spi_bus_handle_external,FuriHalSpiBusHandle, -Variable,+,furi_hal_spi_bus_handle_nfc,FuriHalSpiBusHandle, -Variable,+,furi_hal_spi_bus_handle_sd_fast,FuriHalSpiBusHandle, -Variable,+,furi_hal_spi_bus_handle_sd_slow,FuriHalSpiBusHandle, -Variable,+,furi_hal_spi_bus_handle_subghz,FuriHalSpiBusHandle, +Variable,+,furi_hal_spi_bus_handle_display,const FuriHalSpiBusHandle, +Variable,+,furi_hal_spi_bus_handle_external,const FuriHalSpiBusHandle, +Variable,+,furi_hal_spi_bus_handle_nfc,const FuriHalSpiBusHandle, +Variable,+,furi_hal_spi_bus_handle_sd_fast,const FuriHalSpiBusHandle, +Variable,+,furi_hal_spi_bus_handle_sd_slow,const FuriHalSpiBusHandle, +Variable,+,furi_hal_spi_bus_handle_subghz,const FuriHalSpiBusHandle, Variable,+,furi_hal_spi_bus_r,FuriHalSpiBus, Variable,+,furi_hal_spi_preset_1edge_low_16m,const LL_SPI_InitTypeDef, Variable,+,furi_hal_spi_preset_1edge_low_2m,const LL_SPI_InitTypeDef, @@ -3908,7 +3927,7 @@ Variable,+,gpio_usb_dp,const GpioPin, Variable,+,gpio_vibro,const GpioPin, Variable,+,input_pins,const InputPin[], Variable,+,input_pins_count,const size_t, -Variable,+,lfrfid_protocols,const ProtocolBase*[], +Variable,+,lfrfid_protocols,const ProtocolBase* const[], Variable,+,message_blink_set_color_blue,const NotificationMessage, Variable,+,message_blink_set_color_cyan,const NotificationMessage, Variable,+,message_blink_set_color_green,const NotificationMessage, diff --git a/targets/f7/application_ext.ld b/targets/f7/application_ext.ld index b6496290a..456947db1 100644 --- a/targets/f7/application_ext.ld +++ b/targets/f7/application_ext.ld @@ -33,10 +33,56 @@ SECTIONS { *(COMMON) } - .ARM.attributes : { - *(.ARM.attributes) - *(.ARM.attributes.*) - } + /* Default debug-related rules from ld */ + + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .gnu.build.attributes : { *(.gnu.build.attributes .gnu.build.attributes.*) } + /* DWARF debug sections. + Symbols in the DWARF debugging sections are relative to the beginning + of the section so we begin them at 0. */ + /* DWARF 1. */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions. */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2. */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2. */ + .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line .debug_line.* .debug_line_end) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* SGI/MIPS DWARF 2 extensions. */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } + /* DWARF 3. */ + .debug_pubtypes 0 : { *(.debug_pubtypes) } + .debug_ranges 0 : { *(.debug_ranges) } + /* DWARF 5. */ + .debug_addr 0 : { *(.debug_addr) } + .debug_line_str 0 : { *(.debug_line_str) } + .debug_loclists 0 : { *(.debug_loclists) } + .debug_macro 0 : { *(.debug_macro) } + .debug_names 0 : { *(.debug_names) } + .debug_rnglists 0 : { *(.debug_rnglists) } + .debug_str_offsets 0 : { *(.debug_str_offsets) } + .debug_sup 0 : { *(.debug_sup) } + .ARM.attributes 0 : { KEEP (*(.ARM.attributes)) KEEP (*(.gnu.attributes)) } + .note.gnu.arm.ident 0 : { KEEP (*(.note.gnu.arm.ident)) } + /DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) } /DISCARD/ : { *(.comment) diff --git a/targets/f7/ble_glue/gap.c b/targets/f7/ble_glue/gap.c index 732440ccf..70c9d1c6f 100644 --- a/targets/f7/ble_glue/gap.c +++ b/targets/f7/ble_glue/gap.c @@ -23,6 +23,8 @@ typedef struct { uint16_t connection_handle; uint8_t adv_svc_uuid_len; uint8_t adv_svc_uuid[20]; + uint8_t mfg_data_len; + uint8_t mfg_data[23]; char* adv_name; } GapSvc; @@ -198,8 +200,10 @@ BleEventFlowStatus ble_event_app_notification(void* pckt) { gap->service.connection_handle = event->Connection_Handle; gap_verify_connection_parameters(gap); - // Start pairing by sending security request - aci_gap_slave_security_req(event->Connection_Handle); + if(gap->config->pairing_method != GapPairingNone) { + // Start pairing by sending security request + aci_gap_slave_security_req(event->Connection_Handle); + } } break; default: @@ -321,6 +325,14 @@ static void set_advertisment_service_uid(uint8_t* uid, uint8_t uid_len) { gap->service.adv_svc_uuid_len += uid_len; } +static void set_manufacturer_data(uint8_t* mfg_data, uint8_t mfg_data_len) { + furi_check(mfg_data_len <= sizeof(gap->service.mfg_data) - 2); + gap->service.mfg_data[0] = mfg_data_len + 1; + gap->service.mfg_data[1] = AD_TYPE_MANUFACTURER_SPECIFIC_DATA; + memcpy(&gap->service.mfg_data[gap->service.mfg_data_len], mfg_data, mfg_data_len); + gap->service.mfg_data_len += mfg_data_len; +} + static void gap_init_svc(Gap* gap) { tBleStatus status; uint32_t srd_bd_addr[2]; @@ -440,6 +452,11 @@ static void gap_advertise_start(GapState new_state) { FURI_LOG_D(TAG, "set_non_discoverable success"); } } + + if(gap->service.mfg_data_len > 0) { + hci_le_set_scan_response_data(gap->service.mfg_data_len, gap->service.mfg_data); + } + // Configure advertising status = aci_gap_set_discoverable( ADV_IND, @@ -550,11 +567,26 @@ bool gap_init(GapConfig* config, GapEventCallback on_event_cb, void* context) { gap->is_secure = false; gap->negotiation_round = 0; - uint8_t adv_service_uid[2]; - gap->service.adv_svc_uuid_len = 1; - adv_service_uid[0] = gap->config->adv_service_uuid & 0xff; - adv_service_uid[1] = gap->config->adv_service_uuid >> 8; - set_advertisment_service_uid(adv_service_uid, sizeof(adv_service_uid)); + if(gap->config->mfg_data_len > 0) { + // Offset by 2 for length + AD_TYPE_MANUFACTURER_SPECIFIC_DATA + gap->service.mfg_data_len = 2; + set_manufacturer_data(gap->config->mfg_data, gap->config->mfg_data_len); + } + + if(gap->config->adv_service.UUID_Type == UUID_TYPE_16) { + uint8_t adv_service_uid[2]; + gap->service.adv_svc_uuid_len = 1; + adv_service_uid[0] = gap->config->adv_service.Service_UUID_16 & 0xff; + adv_service_uid[1] = gap->config->adv_service.Service_UUID_16 >> 8; + set_advertisment_service_uid(adv_service_uid, sizeof(adv_service_uid)); + } else if(gap->config->adv_service.UUID_Type == UUID_TYPE_128) { + gap->service.adv_svc_uuid_len = 1; + set_advertisment_service_uid( + gap->config->adv_service.Service_UUID_128, + sizeof(gap->config->adv_service.Service_UUID_128)); + } else { + furi_crash("Invalid UUID type"); + } // Set callback gap->on_event_cb = on_event_cb; diff --git a/targets/f7/ble_glue/gap.h b/targets/f7/ble_glue/gap.h index a90d07304..b2bf0ffc1 100644 --- a/targets/f7/ble_glue/gap.h +++ b/targets/f7/ble_glue/gap.h @@ -68,7 +68,13 @@ typedef struct { } GapConnectionParamsRequest; typedef struct { - uint16_t adv_service_uuid; + struct { + uint8_t UUID_Type; + uint16_t Service_UUID_16; + uint8_t Service_UUID_128[16]; + } adv_service; + uint8_t mfg_data[23]; + uint8_t mfg_data_len; uint16_t appearance_char; bool bonding_mode; GapPairing pairing_method; diff --git a/targets/f7/ble_glue/profiles/serial_profile.c b/targets/f7/ble_glue/profiles/serial_profile.c index 1d414889f..9bba0d897 100644 --- a/targets/f7/ble_glue/profiles/serial_profile.c +++ b/targets/f7/ble_glue/profiles/serial_profile.c @@ -6,6 +6,7 @@ #include #include #include +#include typedef struct { FuriHalBleProfileBase base; @@ -46,8 +47,12 @@ static void ble_profile_serial_stop(FuriHalBleProfileBase* profile) { // Up to 45 ms #define CONNECTION_INTERVAL_MAX (0x24) -static GapConfig serial_template_config = { - .adv_service_uuid = 0x3080, +static const GapConfig serial_template_config = { + .adv_service = + { + .UUID_Type = UUID_TYPE_16, + .Service_UUID_16 = 0x3080, + }, .appearance_char = 0x8600, .bonding_mode = true, .pairing_method = GapPairingPinCodeShow, @@ -71,7 +76,8 @@ static void config->adv_name, furi_hal_version_get_ble_local_device_name_ptr(), FURI_HAL_VERSION_DEVICE_NAME_LENGTH); - config->adv_service_uuid |= furi_hal_version_get_hw_color(); + config->adv_service.UUID_Type = UUID_TYPE_16; + config->adv_service.Service_UUID_16 |= furi_hal_version_get_hw_color(); } static const FuriHalBleProfileTemplate profile_callbacks = { @@ -80,7 +86,7 @@ static const FuriHalBleProfileTemplate profile_callbacks = { .get_gap_config = ble_profile_serial_get_config, }; -const FuriHalBleProfileTemplate* ble_profile_serial = &profile_callbacks; +const FuriHalBleProfileTemplate* const ble_profile_serial = &profile_callbacks; void ble_profile_serial_set_event_callback( FuriHalBleProfileBase* profile, diff --git a/targets/f7/ble_glue/profiles/serial_profile.h b/targets/f7/ble_glue/profiles/serial_profile.h index e07eaef03..7938bfc33 100644 --- a/targets/f7/ble_glue/profiles/serial_profile.h +++ b/targets/f7/ble_glue/profiles/serial_profile.h @@ -19,7 +19,7 @@ typedef enum { typedef SerialServiceEventCallback FuriHalBtSerialCallback; /** Serial profile descriptor */ -extern const FuriHalBleProfileTemplate* ble_profile_serial; +extern const FuriHalBleProfileTemplate* const ble_profile_serial; /** Send data through BLE * diff --git a/targets/f7/fatfs/user_diskio.c b/targets/f7/fatfs/user_diskio.c index 85e5cad4f..963cb595f 100644 --- a/targets/f7/fatfs/user_diskio.c +++ b/targets/f7/fatfs/user_diskio.c @@ -9,7 +9,7 @@ static DRESULT driver_read(BYTE pdrv, BYTE* buff, DWORD sector, UINT count); static DRESULT driver_write(BYTE pdrv, const BYTE* buff, DWORD sector, UINT count); static DRESULT driver_ioctl(BYTE pdrv, BYTE cmd, void* buff); -Diskio_drvTypeDef sd_fatfs_driver = { +const Diskio_drvTypeDef sd_fatfs_driver = { driver_initialize, driver_status, driver_read, diff --git a/targets/f7/fatfs/user_diskio.h b/targets/f7/fatfs/user_diskio.h index 009a17d4b..2505de704 100644 --- a/targets/f7/fatfs/user_diskio.h +++ b/targets/f7/fatfs/user_diskio.h @@ -6,7 +6,7 @@ extern "C" { #include "fatfs/ff_gen_drv.h" -extern Diskio_drvTypeDef sd_fatfs_driver; +extern const Diskio_drvTypeDef sd_fatfs_driver; #ifdef __cplusplus } diff --git a/targets/f7/furi_hal/furi_hal_i2c.c b/targets/f7/furi_hal/furi_hal_i2c.c index 71e1a5814..7eb9b4928 100644 --- a/targets/f7/furi_hal/furi_hal_i2c.c +++ b/targets/f7/furi_hal/furi_hal_i2c.c @@ -22,7 +22,7 @@ void furi_hal_i2c_init(void) { FURI_LOG_I(TAG, "Init OK"); } -void furi_hal_i2c_acquire(FuriHalI2cBusHandle* handle) { +void furi_hal_i2c_acquire(const FuriHalI2cBusHandle* handle) { furi_hal_power_insomnia_enter(); // Lock bus access handle->bus->callback(handle->bus, FuriHalI2cBusEventLock); @@ -36,7 +36,7 @@ void furi_hal_i2c_acquire(FuriHalI2cBusHandle* handle) { handle->callback(handle, FuriHalI2cBusHandleEventActivate); } -void furi_hal_i2c_release(FuriHalI2cBusHandle* handle) { +void furi_hal_i2c_release(const FuriHalI2cBusHandle* handle) { // Ensure that current handle is our handle furi_check(handle->bus->current_handle == handle); // Deactivate handle @@ -196,7 +196,7 @@ static bool furi_hal_i2c_transaction( } bool furi_hal_i2c_rx_ext( - FuriHalI2cBusHandle* handle, + const FuriHalI2cBusHandle* handle, uint16_t address, bool ten_bit, uint8_t* data, @@ -213,7 +213,7 @@ bool furi_hal_i2c_rx_ext( } bool furi_hal_i2c_tx_ext( - FuriHalI2cBusHandle* handle, + const FuriHalI2cBusHandle* handle, uint16_t address, bool ten_bit, const uint8_t* data, @@ -230,7 +230,7 @@ bool furi_hal_i2c_tx_ext( } bool furi_hal_i2c_tx( - FuriHalI2cBusHandle* handle, + const FuriHalI2cBusHandle* handle, uint8_t address, const uint8_t* data, size_t size, @@ -242,7 +242,7 @@ bool furi_hal_i2c_tx( } bool furi_hal_i2c_rx( - FuriHalI2cBusHandle* handle, + const FuriHalI2cBusHandle* handle, uint8_t address, uint8_t* data, size_t size, @@ -254,7 +254,7 @@ bool furi_hal_i2c_rx( } bool furi_hal_i2c_trx( - FuriHalI2cBusHandle* handle, + const FuriHalI2cBusHandle* handle, uint8_t address, const uint8_t* tx_data, size_t tx_size, @@ -281,7 +281,10 @@ bool furi_hal_i2c_trx( timeout); } -bool furi_hal_i2c_is_device_ready(FuriHalI2cBusHandle* handle, uint8_t i2c_addr, uint32_t timeout) { +bool furi_hal_i2c_is_device_ready( + const FuriHalI2cBusHandle* handle, + uint8_t i2c_addr, + uint32_t timeout) { furi_check(handle); furi_check(handle->bus->current_handle == handle); furi_check(timeout > 0); @@ -314,7 +317,7 @@ bool furi_hal_i2c_is_device_ready(FuriHalI2cBusHandle* handle, uint8_t i2c_addr, } bool furi_hal_i2c_read_reg_8( - FuriHalI2cBusHandle* handle, + const FuriHalI2cBusHandle* handle, uint8_t i2c_addr, uint8_t reg_addr, uint8_t* data, @@ -325,7 +328,7 @@ bool furi_hal_i2c_read_reg_8( } bool furi_hal_i2c_read_reg_16( - FuriHalI2cBusHandle* handle, + const FuriHalI2cBusHandle* handle, uint8_t i2c_addr, uint8_t reg_addr, uint16_t* data, @@ -340,7 +343,7 @@ bool furi_hal_i2c_read_reg_16( } bool furi_hal_i2c_read_mem( - FuriHalI2cBusHandle* handle, + const FuriHalI2cBusHandle* handle, uint8_t i2c_addr, uint8_t mem_addr, uint8_t* data, @@ -352,7 +355,7 @@ bool furi_hal_i2c_read_mem( } bool furi_hal_i2c_write_reg_8( - FuriHalI2cBusHandle* handle, + const FuriHalI2cBusHandle* handle, uint8_t i2c_addr, uint8_t reg_addr, uint8_t data, @@ -368,7 +371,7 @@ bool furi_hal_i2c_write_reg_8( } bool furi_hal_i2c_write_reg_16( - FuriHalI2cBusHandle* handle, + const FuriHalI2cBusHandle* handle, uint8_t i2c_addr, uint8_t reg_addr, uint16_t data, @@ -385,7 +388,7 @@ bool furi_hal_i2c_write_reg_16( } bool furi_hal_i2c_write_mem( - FuriHalI2cBusHandle* handle, + const FuriHalI2cBusHandle* handle, uint8_t i2c_addr, uint8_t mem_addr, const uint8_t* data, diff --git a/targets/f7/furi_hal/furi_hal_i2c_config.c b/targets/f7/furi_hal/furi_hal_i2c_config.c index f9d88abb3..b10c53d32 100644 --- a/targets/f7/furi_hal/furi_hal_i2c_config.c +++ b/targets/f7/furi_hal/furi_hal_i2c_config.c @@ -74,7 +74,7 @@ FuriHalI2cBus furi_hal_i2c_bus_external = { }; void furi_hal_i2c_bus_handle_power_event( - FuriHalI2cBusHandle* handle, + const FuriHalI2cBusHandle* handle, FuriHalI2cBusHandleEvent event) { if(event == FuriHalI2cBusHandleEventActivate) { furi_hal_gpio_init_ex( @@ -120,13 +120,13 @@ void furi_hal_i2c_bus_handle_power_event( } } -FuriHalI2cBusHandle furi_hal_i2c_handle_power = { +const FuriHalI2cBusHandle furi_hal_i2c_handle_power = { .bus = &furi_hal_i2c_bus_power, .callback = furi_hal_i2c_bus_handle_power_event, }; void furi_hal_i2c_bus_handle_external_event( - FuriHalI2cBusHandle* handle, + const FuriHalI2cBusHandle* handle, FuriHalI2cBusHandleEvent event) { if(event == FuriHalI2cBusHandleEventActivate) { furi_hal_gpio_init_ex( @@ -160,7 +160,7 @@ void furi_hal_i2c_bus_handle_external_event( } } -FuriHalI2cBusHandle furi_hal_i2c_handle_external = { +const FuriHalI2cBusHandle furi_hal_i2c_handle_external = { .bus = &furi_hal_i2c_bus_external, .callback = furi_hal_i2c_bus_handle_external_event, }; diff --git a/targets/f7/furi_hal/furi_hal_i2c_config.h b/targets/f7/furi_hal/furi_hal_i2c_config.h index a8fb91835..28bd09a0a 100644 --- a/targets/f7/furi_hal/furi_hal_i2c_config.h +++ b/targets/f7/furi_hal/furi_hal_i2c_config.h @@ -17,14 +17,14 @@ extern FuriHalI2cBus furi_hal_i2c_bus_external; * Pins: PA9(SCL) / PA10(SDA), float on release * Params: 400khz */ -extern FuriHalI2cBusHandle furi_hal_i2c_handle_power; +extern const FuriHalI2cBusHandle furi_hal_i2c_handle_power; /** Handle for external i2c bus * Bus: furi_hal_i2c_bus_external * Pins: PC0(SCL) / PC1(SDA), float on release * Params: 100khz */ -extern FuriHalI2cBusHandle furi_hal_i2c_handle_external; +extern const FuriHalI2cBusHandle furi_hal_i2c_handle_external; #ifdef __cplusplus } diff --git a/targets/f7/furi_hal/furi_hal_i2c_types.h b/targets/f7/furi_hal/furi_hal_i2c_types.h index 13f361054..0a35137b0 100644 --- a/targets/f7/furi_hal/furi_hal_i2c_types.h +++ b/targets/f7/furi_hal/furi_hal_i2c_types.h @@ -25,7 +25,7 @@ typedef void (*FuriHalI2cBusEventCallback)(FuriHalI2cBus* bus, FuriHalI2cBusEven /** FuriHal i2c bus */ struct FuriHalI2cBus { I2C_TypeDef* i2c; - FuriHalI2cBusHandle* current_handle; + const FuriHalI2cBusHandle* current_handle; FuriHalI2cBusEventCallback callback; }; @@ -37,7 +37,7 @@ typedef enum { /** FuriHal i2c handle event callback */ typedef void (*FuriHalI2cBusHandleEventCallback)( - FuriHalI2cBusHandle* handle, + const FuriHalI2cBusHandle* handle, FuriHalI2cBusHandleEvent event); /** FuriHal i2c handle */ diff --git a/targets/f7/furi_hal/furi_hal_nfc.c b/targets/f7/furi_hal/furi_hal_nfc.c index ee7a04e45..d8dc0c618 100644 --- a/targets/f7/furi_hal/furi_hal_nfc.c +++ b/targets/f7/furi_hal/furi_hal_nfc.c @@ -8,7 +8,7 @@ #define TAG "FuriHalNfc" -const FuriHalNfcTechBase* furi_hal_nfc_tech[FuriHalNfcTechNum] = { +const FuriHalNfcTechBase* const furi_hal_nfc_tech[FuriHalNfcTechNum] = { [FuriHalNfcTechIso14443a] = &furi_hal_nfc_iso14443a, [FuriHalNfcTechIso14443b] = &furi_hal_nfc_iso14443b, [FuriHalNfcTechIso15693] = &furi_hal_nfc_iso15693, @@ -18,7 +18,7 @@ const FuriHalNfcTechBase* furi_hal_nfc_tech[FuriHalNfcTechNum] = { FuriHalNfc furi_hal_nfc; -static FuriHalNfcError furi_hal_nfc_turn_on_osc(FuriHalSpiBusHandle* handle) { +static FuriHalNfcError furi_hal_nfc_turn_on_osc(const FuriHalSpiBusHandle* handle) { FuriHalNfcError error = FuriHalNfcErrorNone; furi_hal_nfc_event_start(); @@ -53,7 +53,7 @@ FuriHalNfcError furi_hal_nfc_is_hal_ready(void) { error = furi_hal_nfc_acquire(); if(error != FuriHalNfcErrorNone) break; - FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + const FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; uint8_t chip_id = 0; st25r3916_read_reg(handle, ST25R3916_REG_IC_IDENTITY, &chip_id); if((chip_id & ST25R3916_REG_IC_IDENTITY_ic_type_mask) != @@ -83,7 +83,7 @@ FuriHalNfcError furi_hal_nfc_init(void) { furi_hal_nfc_low_power_mode_start(); } - FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + const FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; // Set default state st25r3916_direct_cmd(handle, ST25R3916_CMD_SET_DEFAULT); // Increase IO driver strength of MISO and IRQ @@ -282,7 +282,7 @@ FuriHalNfcError furi_hal_nfc_release(void) { FuriHalNfcError furi_hal_nfc_low_power_mode_start(void) { FuriHalNfcError error = FuriHalNfcErrorNone; - FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + const FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; st25r3916_direct_cmd(handle, ST25R3916_CMD_STOP); st25r3916_clear_reg_bits( @@ -300,7 +300,7 @@ FuriHalNfcError furi_hal_nfc_low_power_mode_start(void) { FuriHalNfcError furi_hal_nfc_low_power_mode_stop(void) { FuriHalNfcError error = FuriHalNfcErrorNone; - FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + const FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; do { furi_hal_nfc_init_gpio_isr(); @@ -318,7 +318,7 @@ FuriHalNfcError furi_hal_nfc_low_power_mode_stop(void) { return error; } -static FuriHalNfcError furi_hal_nfc_poller_init_common(FuriHalSpiBusHandle* handle) { +static FuriHalNfcError furi_hal_nfc_poller_init_common(const FuriHalSpiBusHandle* handle) { // Disable wake up st25r3916_clear_reg_bits(handle, ST25R3916_REG_OP_CONTROL, ST25R3916_REG_OP_CONTROL_wu); // Enable correlator @@ -339,7 +339,7 @@ static FuriHalNfcError furi_hal_nfc_poller_init_common(FuriHalSpiBusHandle* hand return FuriHalNfcErrorNone; } -static FuriHalNfcError furi_hal_nfc_listener_init_common(FuriHalSpiBusHandle* handle) { +static FuriHalNfcError furi_hal_nfc_listener_init_common(const FuriHalSpiBusHandle* handle) { UNUSED(handle); // No common listener configuration return FuriHalNfcErrorNone; @@ -349,7 +349,7 @@ FuriHalNfcError furi_hal_nfc_set_mode(FuriHalNfcMode mode, FuriHalNfcTech tech) furi_check(mode < FuriHalNfcModeNum); furi_check(tech < FuriHalNfcTechNum); - FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + const FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; FuriHalNfcError error = FuriHalNfcErrorNone; @@ -375,7 +375,7 @@ FuriHalNfcError furi_hal_nfc_set_mode(FuriHalNfcMode mode, FuriHalNfcTech tech) FuriHalNfcError furi_hal_nfc_reset_mode(void) { FuriHalNfcError error = FuriHalNfcErrorNone; - FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + const FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; st25r3916_direct_cmd(handle, ST25R3916_CMD_STOP); @@ -415,7 +415,7 @@ FuriHalNfcError furi_hal_nfc_reset_mode(void) { FuriHalNfcError furi_hal_nfc_field_detect_start(void) { FuriHalNfcError error = FuriHalNfcErrorNone; - FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + const FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; st25r3916_write_reg( handle, @@ -429,7 +429,7 @@ FuriHalNfcError furi_hal_nfc_field_detect_start(void) { FuriHalNfcError furi_hal_nfc_field_detect_stop(void) { FuriHalNfcError error = FuriHalNfcErrorNone; - FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + const FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; st25r3916_clear_reg_bits( handle, @@ -441,7 +441,7 @@ FuriHalNfcError furi_hal_nfc_field_detect_stop(void) { bool furi_hal_nfc_field_is_present(void) { bool is_present = false; - FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + const FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; if(st25r3916_check_reg( handle, @@ -456,7 +456,7 @@ bool furi_hal_nfc_field_is_present(void) { FuriHalNfcError furi_hal_nfc_poller_field_on(void) { FuriHalNfcError error = FuriHalNfcErrorNone; - FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + const FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; if(!st25r3916_check_reg( handle, @@ -476,7 +476,7 @@ FuriHalNfcError furi_hal_nfc_poller_field_on(void) { } FuriHalNfcError furi_hal_nfc_poller_tx_common( - FuriHalSpiBusHandle* handle, + const FuriHalSpiBusHandle* handle, const uint8_t* tx_data, size_t tx_bits) { furi_check(tx_data); @@ -508,7 +508,7 @@ FuriHalNfcError furi_hal_nfc_poller_tx_common( } FuriHalNfcError furi_hal_nfc_common_fifo_tx( - FuriHalSpiBusHandle* handle, + const FuriHalSpiBusHandle* handle, const uint8_t* tx_data, size_t tx_bits) { FuriHalNfcError err = FuriHalNfcErrorNone; @@ -523,7 +523,7 @@ FuriHalNfcError furi_hal_nfc_common_fifo_tx( FuriHalNfcError furi_hal_nfc_poller_tx(const uint8_t* tx_data, size_t tx_bits) { furi_check(furi_hal_nfc.mode == FuriHalNfcModePoller); furi_check(furi_hal_nfc.tech < FuriHalNfcTechNum); - FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + const FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; return furi_hal_nfc_tech[furi_hal_nfc.tech]->poller.tx(handle, tx_data, tx_bits); } @@ -531,7 +531,7 @@ FuriHalNfcError furi_hal_nfc_poller_tx(const uint8_t* tx_data, size_t tx_bits) { FuriHalNfcError furi_hal_nfc_poller_rx(uint8_t* rx_data, size_t rx_data_size, size_t* rx_bits) { furi_check(furi_hal_nfc.mode == FuriHalNfcModePoller); furi_check(furi_hal_nfc.tech < FuriHalNfcTechNum); - FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + const FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; return furi_hal_nfc_tech[furi_hal_nfc.tech]->poller.rx(handle, rx_data, rx_data_size, rx_bits); } @@ -556,12 +556,12 @@ FuriHalNfcError furi_hal_nfc_listener_tx(const uint8_t* tx_data, size_t tx_bits) furi_check(furi_hal_nfc.mode == FuriHalNfcModeListener); furi_check(furi_hal_nfc.tech < FuriHalNfcTechNum); - FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + const FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; return furi_hal_nfc_tech[furi_hal_nfc.tech]->listener.tx(handle, tx_data, tx_bits); } FuriHalNfcError furi_hal_nfc_common_fifo_rx( - FuriHalSpiBusHandle* handle, + const FuriHalSpiBusHandle* handle, uint8_t* rx_data, size_t rx_data_size, size_t* rx_bits) { @@ -581,13 +581,13 @@ FuriHalNfcError furi_hal_nfc_listener_rx(uint8_t* rx_data, size_t rx_data_size, furi_check(furi_hal_nfc.mode == FuriHalNfcModeListener); furi_check(furi_hal_nfc.tech < FuriHalNfcTechNum); - FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + const FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; return furi_hal_nfc_tech[furi_hal_nfc.tech]->listener.rx( handle, rx_data, rx_data_size, rx_bits); } FuriHalNfcError furi_hal_nfc_trx_reset(void) { - FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + const FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; st25r3916_direct_cmd(handle, ST25R3916_CMD_STOP); @@ -598,7 +598,7 @@ FuriHalNfcError furi_hal_nfc_listener_sleep(void) { furi_check(furi_hal_nfc.mode == FuriHalNfcModeListener); furi_check(furi_hal_nfc.tech < FuriHalNfcTechNum); - FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + const FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; return furi_hal_nfc_tech[furi_hal_nfc.tech]->listener.sleep(handle); } @@ -607,13 +607,13 @@ FuriHalNfcError furi_hal_nfc_listener_idle(void) { furi_check(furi_hal_nfc.mode == FuriHalNfcModeListener); furi_check(furi_hal_nfc.tech < FuriHalNfcTechNum); - FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + const FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; return furi_hal_nfc_tech[furi_hal_nfc.tech]->listener.idle(handle); } FuriHalNfcError furi_hal_nfc_listener_enable_rx(void) { - FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + const FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; st25r3916_direct_cmd(handle, ST25R3916_CMD_UNMASK_RECEIVE_DATA); diff --git a/targets/f7/furi_hal/furi_hal_nfc_event.c b/targets/f7/furi_hal/furi_hal_nfc_event.c index 9bcd2f1fe..56681b4fe 100644 --- a/targets/f7/furi_hal/furi_hal_nfc_event.c +++ b/targets/f7/furi_hal/furi_hal_nfc_event.c @@ -48,7 +48,7 @@ FuriHalNfcEvent furi_hal_nfc_wait_event_common(uint32_t timeout_ms) { if(event_flag != (unsigned)FuriFlagErrorTimeout) { if(event_flag & FuriHalNfcEventInternalTypeIrq) { furi_thread_flags_clear(FuriHalNfcEventInternalTypeIrq); - FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + const FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; uint32_t irq = furi_hal_nfc_get_irq(handle); if(irq & ST25R3916_IRQ_MASK_OSC) { event |= FuriHalNfcEventOscOn; @@ -101,7 +101,7 @@ FuriHalNfcEvent furi_hal_nfc_wait_event_common(uint32_t timeout_ms) { } bool furi_hal_nfc_event_wait_for_specific_irq( - FuriHalSpiBusHandle* handle, + const FuriHalSpiBusHandle* handle, uint32_t mask, uint32_t timeout_ms) { furi_check(furi_hal_nfc_event); diff --git a/targets/f7/furi_hal/furi_hal_nfc_felica.c b/targets/f7/furi_hal/furi_hal_nfc_felica.c index 6c3f55525..1267b7b13 100644 --- a/targets/f7/furi_hal/furi_hal_nfc_felica.c +++ b/targets/f7/furi_hal/furi_hal_nfc_felica.c @@ -18,7 +18,7 @@ typedef struct { } FuriHalFelicaPtMemory; #pragma pack(pop) -static FuriHalNfcError furi_hal_nfc_felica_poller_init(FuriHalSpiBusHandle* handle) { +static FuriHalNfcError furi_hal_nfc_felica_poller_init(const FuriHalSpiBusHandle* handle) { // Enable Felica mode, AM modulation st25r3916_change_reg_bits( handle, @@ -61,13 +61,13 @@ static FuriHalNfcError furi_hal_nfc_felica_poller_init(FuriHalSpiBusHandle* hand return FuriHalNfcErrorNone; } -static FuriHalNfcError furi_hal_nfc_felica_poller_deinit(FuriHalSpiBusHandle* handle) { +static FuriHalNfcError furi_hal_nfc_felica_poller_deinit(const FuriHalSpiBusHandle* handle) { UNUSED(handle); return FuriHalNfcErrorNone; } -static FuriHalNfcError furi_hal_nfc_felica_listener_init(FuriHalSpiBusHandle* handle) { +static FuriHalNfcError furi_hal_nfc_felica_listener_init(const FuriHalSpiBusHandle* handle) { furi_assert(handle); st25r3916_direct_cmd(handle, ST25R3916_CMD_SET_DEFAULT); st25r3916_write_reg( @@ -141,7 +141,7 @@ static FuriHalNfcError furi_hal_nfc_felica_listener_init(FuriHalSpiBusHandle* ha return FuriHalNfcErrorNone; } -static FuriHalNfcError furi_hal_nfc_felica_listener_deinit(FuriHalSpiBusHandle* handle) { +static FuriHalNfcError furi_hal_nfc_felica_listener_deinit(const FuriHalSpiBusHandle* handle) { UNUSED(handle); return FuriHalNfcErrorNone; } @@ -154,19 +154,19 @@ static FuriHalNfcEvent furi_hal_nfc_felica_listener_wait_event(uint32_t timeout_ } FuriHalNfcError furi_hal_nfc_felica_listener_tx( - FuriHalSpiBusHandle* handle, + const FuriHalSpiBusHandle* handle, const uint8_t* tx_data, size_t tx_bits) { furi_hal_nfc_common_fifo_tx(handle, tx_data, tx_bits); return FuriHalNfcErrorNone; } -FuriHalNfcError furi_hal_nfc_felica_listener_sleep(FuriHalSpiBusHandle* handle) { +FuriHalNfcError furi_hal_nfc_felica_listener_sleep(const FuriHalSpiBusHandle* handle) { UNUSED(handle); return FuriHalNfcErrorNone; } -FuriHalNfcError furi_hal_nfc_felica_listener_idle(FuriHalSpiBusHandle* handle) { +FuriHalNfcError furi_hal_nfc_felica_listener_idle(const FuriHalSpiBusHandle* handle) { UNUSED(handle); return FuriHalNfcErrorNone; } @@ -182,7 +182,7 @@ FuriHalNfcError furi_hal_nfc_felica_listener_set_sensf_res_data( furi_check(idm_len == FURI_HAL_FELICA_IDM_PMM_LENGTH); furi_check(pmm_len == FURI_HAL_FELICA_IDM_PMM_LENGTH); - FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + const FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; // Write PT Memory FuriHalFelicaPtMemory pt; pt.system_code = sys_code; diff --git a/targets/f7/furi_hal/furi_hal_nfc_i.h b/targets/f7/furi_hal/furi_hal_nfc_i.h index 084196451..5b0f8e68d 100644 --- a/targets/f7/furi_hal/furi_hal_nfc_i.h +++ b/targets/f7/furi_hal/furi_hal_nfc_i.h @@ -104,7 +104,7 @@ void furi_hal_nfc_timers_deinit(void); * @param[in,out] handle pointer to the SPI handle associated with the NFC chip. * @returns bitmask of zero or more occurred interrupts. */ -uint32_t furi_hal_nfc_get_irq(FuriHalSpiBusHandle* handle); +uint32_t furi_hal_nfc_get_irq(const FuriHalSpiBusHandle* handle); /** * @brief Wait until a specified type of interrupt occurs. @@ -115,7 +115,7 @@ uint32_t furi_hal_nfc_get_irq(FuriHalSpiBusHandle* handle); * @returns true if specified interrupt(s) have occured within timeout, false otherwise. */ bool furi_hal_nfc_event_wait_for_specific_irq( - FuriHalSpiBusHandle* handle, + const FuriHalSpiBusHandle* handle, uint32_t mask, uint32_t timeout_ms); @@ -137,7 +137,7 @@ FuriHalNfcEvent furi_hal_nfc_wait_event_common(uint32_t timeout_ms); * @param[in,out] handle pointer to the SPI handle associated with the NFC chip. * @returns FuriHalNfcErrorNone on success, any other error code on failure. */ -FuriHalNfcError furi_hal_nfc_common_listener_rx_start(FuriHalSpiBusHandle* handle); +FuriHalNfcError furi_hal_nfc_common_listener_rx_start(const FuriHalSpiBusHandle* handle); /** * @brief Transmit data using on-chip FIFO. @@ -150,7 +150,7 @@ FuriHalNfcError furi_hal_nfc_common_listener_rx_start(FuriHalSpiBusHandle* handl * @returns FuriHalNfcErrorNone on success, any other error code on failure. */ FuriHalNfcError furi_hal_nfc_common_fifo_tx( - FuriHalSpiBusHandle* handle, + const FuriHalSpiBusHandle* handle, const uint8_t* tx_data, size_t tx_bits); @@ -166,7 +166,7 @@ FuriHalNfcError furi_hal_nfc_common_fifo_tx( * @returns FuriHalNfcErrorNone on success, any other error code on failure. */ FuriHalNfcError furi_hal_nfc_common_fifo_rx( - FuriHalSpiBusHandle* handle, + const FuriHalSpiBusHandle* handle, uint8_t* rx_data, size_t rx_data_size, size_t* rx_bits); @@ -182,7 +182,7 @@ FuriHalNfcError furi_hal_nfc_common_fifo_rx( * @returns FuriHalNfcErrorNone on success, any other error code on failure. */ FuriHalNfcError furi_hal_nfc_poller_tx_common( - FuriHalSpiBusHandle* handle, + const FuriHalSpiBusHandle* handle, const uint8_t* tx_data, size_t tx_bits); diff --git a/targets/f7/furi_hal/furi_hal_nfc_irq.c b/targets/f7/furi_hal/furi_hal_nfc_irq.c index 90373955f..50a0139fd 100644 --- a/targets/f7/furi_hal/furi_hal_nfc_irq.c +++ b/targets/f7/furi_hal/furi_hal_nfc_irq.c @@ -8,7 +8,7 @@ static void furi_hal_nfc_int_callback(void* context) { furi_hal_nfc_event_set(FuriHalNfcEventInternalTypeIrq); } -uint32_t furi_hal_nfc_get_irq(FuriHalSpiBusHandle* handle) { +uint32_t furi_hal_nfc_get_irq(const FuriHalSpiBusHandle* handle) { uint32_t irq = 0; while(furi_hal_gpio_read_port_pin(gpio_nfc_irq_rfid_pull.port, gpio_nfc_irq_rfid_pull.pin)) { irq |= st25r3916_get_irq(handle); diff --git a/targets/f7/furi_hal/furi_hal_nfc_iso14443a.c b/targets/f7/furi_hal/furi_hal_nfc_iso14443a.c index 1ef23dfa4..be7a17264 100644 --- a/targets/f7/furi_hal/furi_hal_nfc_iso14443a.c +++ b/targets/f7/furi_hal/furi_hal_nfc_iso14443a.c @@ -13,7 +13,7 @@ static Iso14443_3aSignal* iso14443_3a_signal = NULL; -static FuriHalNfcError furi_hal_nfc_iso14443a_common_init(FuriHalSpiBusHandle* handle) { +static FuriHalNfcError furi_hal_nfc_iso14443a_common_init(const FuriHalSpiBusHandle* handle) { // Common NFC-A settings, 106 kbps // 1st stage zero = 600kHz, 3rd stage zero = 200 kHz @@ -40,7 +40,7 @@ static FuriHalNfcError furi_hal_nfc_iso14443a_common_init(FuriHalSpiBusHandle* h return FuriHalNfcErrorNone; } -static FuriHalNfcError furi_hal_nfc_iso14443a_poller_init(FuriHalSpiBusHandle* handle) { +static FuriHalNfcError furi_hal_nfc_iso14443a_poller_init(const FuriHalSpiBusHandle* handle) { // Enable ISO14443A mode, OOK modulation st25r3916_change_reg_bits( handle, @@ -57,7 +57,7 @@ static FuriHalNfcError furi_hal_nfc_iso14443a_poller_init(FuriHalSpiBusHandle* h return furi_hal_nfc_iso14443a_common_init(handle); } -static FuriHalNfcError furi_hal_nfc_iso14443a_poller_deinit(FuriHalSpiBusHandle* handle) { +static FuriHalNfcError furi_hal_nfc_iso14443a_poller_deinit(const FuriHalSpiBusHandle* handle) { st25r3916_change_reg_bits( handle, ST25R3916_REG_ISO14443A_NFC, @@ -67,7 +67,7 @@ static FuriHalNfcError furi_hal_nfc_iso14443a_poller_deinit(FuriHalSpiBusHandle* return FuriHalNfcErrorNone; } -static FuriHalNfcError furi_hal_nfc_iso14443a_listener_init(FuriHalSpiBusHandle* handle) { +static FuriHalNfcError furi_hal_nfc_iso14443a_listener_init(const FuriHalSpiBusHandle* handle) { furi_check(iso14443_3a_signal == NULL); iso14443_3a_signal = iso14443_3a_signal_alloc(&gpio_spi_r_mosi); @@ -105,7 +105,7 @@ static FuriHalNfcError furi_hal_nfc_iso14443a_listener_init(FuriHalSpiBusHandle* return furi_hal_nfc_iso14443a_common_init(handle); } -static FuriHalNfcError furi_hal_nfc_iso14443a_listener_deinit(FuriHalSpiBusHandle* handle) { +static FuriHalNfcError furi_hal_nfc_iso14443a_listener_deinit(const FuriHalSpiBusHandle* handle) { UNUSED(handle); if(iso14443_3a_signal) { @@ -118,7 +118,7 @@ static FuriHalNfcError furi_hal_nfc_iso14443a_listener_deinit(FuriHalSpiBusHandl static FuriHalNfcEvent furi_hal_nfc_iso14443_3a_listener_wait_event(uint32_t timeout_ms) { FuriHalNfcEvent event = furi_hal_nfc_wait_event_common(timeout_ms); - FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + const FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; if(event & FuriHalNfcEventListenerActive) { st25r3916_set_reg_bits( @@ -131,7 +131,7 @@ static FuriHalNfcEvent furi_hal_nfc_iso14443_3a_listener_wait_event(uint32_t tim FuriHalNfcError furi_hal_nfc_iso14443a_poller_trx_short_frame(FuriHalNfcaShortFrame frame) { FuriHalNfcError error = FuriHalNfcErrorNone; - FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + const FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; // Disable crc check st25r3916_set_reg_bits(handle, ST25R3916_REG_AUX, ST25R3916_REG_AUX_no_crc_rx); @@ -185,7 +185,7 @@ FuriHalNfcError furi_check(tx_data); FuriHalNfcError err = FuriHalNfcErrorNone; - FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + const FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; // Prepare tx st25r3916_direct_cmd(handle, ST25R3916_CMD_CLEAR_FIFO); @@ -220,7 +220,7 @@ FuriHalNfcError furi_hal_nfc_iso14443a_listener_set_col_res_data( FuriHalNfcError error = FuriHalNfcErrorNone; - FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + const FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; // Set 4 or 7 bytes UID if(uid_len == 4) { @@ -255,7 +255,7 @@ FuriHalNfcError furi_hal_nfc_iso14443a_listener_set_col_res_data( } FuriHalNfcError furi_hal_nfc_iso4443a_listener_tx( - FuriHalSpiBusHandle* handle, + const FuriHalSpiBusHandle* handle, const uint8_t* tx_data, size_t tx_bits) { FuriHalNfcError error = FuriHalNfcErrorNone; @@ -284,7 +284,7 @@ FuriHalNfcError furi_hal_nfc_iso14443a_listener_tx_custom_parity( furi_check(iso14443_3a_signal); - FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + const FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; st25r3916_direct_cmd(handle, ST25R3916_CMD_TRANSPARENT_MODE); // Reconfigure gpio for Transparent mode @@ -303,7 +303,7 @@ FuriHalNfcError furi_hal_nfc_iso14443a_listener_tx_custom_parity( return FuriHalNfcErrorNone; } -FuriHalNfcError furi_hal_nfc_iso14443_3a_listener_sleep(FuriHalSpiBusHandle* handle) { +FuriHalNfcError furi_hal_nfc_iso14443_3a_listener_sleep(const FuriHalSpiBusHandle* handle) { // Enable auto collision resolution st25r3916_clear_reg_bits( handle, ST25R3916_REG_PASSIVE_TARGET, ST25R3916_REG_PASSIVE_TARGET_d_106_ac_a); @@ -313,7 +313,7 @@ FuriHalNfcError furi_hal_nfc_iso14443_3a_listener_sleep(FuriHalSpiBusHandle* han return FuriHalNfcErrorNone; } -FuriHalNfcError furi_hal_nfc_iso14443_3a_listener_idle(FuriHalSpiBusHandle* handle) { +FuriHalNfcError furi_hal_nfc_iso14443_3a_listener_idle(const FuriHalSpiBusHandle* handle) { // Enable auto collision resolution st25r3916_clear_reg_bits( handle, ST25R3916_REG_PASSIVE_TARGET, ST25R3916_REG_PASSIVE_TARGET_d_106_ac_a); diff --git a/targets/f7/furi_hal/furi_hal_nfc_iso14443b.c b/targets/f7/furi_hal/furi_hal_nfc_iso14443b.c index bb1a63515..315223e2f 100644 --- a/targets/f7/furi_hal/furi_hal_nfc_iso14443b.c +++ b/targets/f7/furi_hal/furi_hal_nfc_iso14443b.c @@ -1,7 +1,7 @@ #include "furi_hal_nfc_i.h" #include "furi_hal_nfc_tech_i.h" -static FuriHalNfcError furi_hal_nfc_iso14443b_common_init(FuriHalSpiBusHandle* handle) { +static FuriHalNfcError furi_hal_nfc_iso14443b_common_init(const FuriHalSpiBusHandle* handle) { // Common NFC-B settings, 106kbps // 1st stage zero = 60kHz, 3rd stage zero = 200 kHz @@ -40,7 +40,7 @@ static FuriHalNfcError furi_hal_nfc_iso14443b_common_init(FuriHalSpiBusHandle* h return FuriHalNfcErrorNone; } -static FuriHalNfcError furi_hal_nfc_iso14443b_poller_init(FuriHalSpiBusHandle* handle) { +static FuriHalNfcError furi_hal_nfc_iso14443b_poller_init(const FuriHalSpiBusHandle* handle) { // Enable ISO14443B mode, AM modulation st25r3916_change_reg_bits( handle, @@ -84,7 +84,7 @@ static FuriHalNfcError furi_hal_nfc_iso14443b_poller_init(FuriHalSpiBusHandle* h return furi_hal_nfc_iso14443b_common_init(handle); } -static FuriHalNfcError furi_hal_nfc_iso14443b_poller_deinit(FuriHalSpiBusHandle* handle) { +static FuriHalNfcError furi_hal_nfc_iso14443b_poller_deinit(const FuriHalSpiBusHandle* handle) { UNUSED(handle); return FuriHalNfcErrorNone; } diff --git a/targets/f7/furi_hal/furi_hal_nfc_iso15693.c b/targets/f7/furi_hal/furi_hal_nfc_iso15693.c index 3245b67cc..d35b160f4 100644 --- a/targets/f7/furi_hal/furi_hal_nfc_iso15693.c +++ b/targets/f7/furi_hal/furi_hal_nfc_iso15693.c @@ -74,7 +74,7 @@ static void furi_hal_nfc_iso15693_poller_free(FuriHalNfcIso15693Poller* instance free(instance); } -static FuriHalNfcError furi_hal_nfc_iso15693_common_init(FuriHalSpiBusHandle* handle) { +static FuriHalNfcError furi_hal_nfc_iso15693_common_init(const FuriHalSpiBusHandle* handle) { // Common NFC-V settings, 26.48 kbps // 1st stage zero = 12 kHz, 3rd stage zero = 80 kHz, low-pass = 600 kHz @@ -112,7 +112,7 @@ static FuriHalNfcError furi_hal_nfc_iso15693_common_init(FuriHalSpiBusHandle* ha return FuriHalNfcErrorNone; } -static FuriHalNfcError furi_hal_nfc_iso15693_poller_init(FuriHalSpiBusHandle* handle) { +static FuriHalNfcError furi_hal_nfc_iso15693_poller_init(const FuriHalSpiBusHandle* handle) { furi_check(furi_hal_nfc_iso15693_poller == NULL); furi_hal_nfc_iso15693_poller = furi_hal_nfc_iso15693_poller_alloc(); @@ -141,7 +141,7 @@ static FuriHalNfcError furi_hal_nfc_iso15693_poller_init(FuriHalSpiBusHandle* ha return furi_hal_nfc_iso15693_common_init(handle); } -static FuriHalNfcError furi_hal_nfc_iso15693_poller_deinit(FuriHalSpiBusHandle* handle) { +static FuriHalNfcError furi_hal_nfc_iso15693_poller_deinit(const FuriHalSpiBusHandle* handle) { UNUSED(handle); furi_check(furi_hal_nfc_iso15693_poller); @@ -238,7 +238,7 @@ static FuriHalNfcError iso15693_3_poller_decode_frame( } static FuriHalNfcError furi_hal_nfc_iso15693_poller_tx( - FuriHalSpiBusHandle* handle, + const FuriHalSpiBusHandle* handle, const uint8_t* tx_data, size_t tx_bits) { FuriHalNfcIso15693Poller* instance = furi_hal_nfc_iso15693_poller; @@ -252,7 +252,7 @@ static FuriHalNfcError furi_hal_nfc_iso15693_poller_tx( } static FuriHalNfcError furi_hal_nfc_iso15693_poller_rx( - FuriHalSpiBusHandle* handle, + const FuriHalSpiBusHandle* handle, uint8_t* rx_data, size_t rx_data_size, size_t* rx_bits) { @@ -284,14 +284,16 @@ static FuriHalNfcError furi_hal_nfc_iso15693_poller_rx( return error; } -static void furi_hal_nfc_iso15693_listener_transparent_mode_enter(FuriHalSpiBusHandle* handle) { +static void + furi_hal_nfc_iso15693_listener_transparent_mode_enter(const FuriHalSpiBusHandle* handle) { st25r3916_direct_cmd(handle, ST25R3916_CMD_TRANSPARENT_MODE); furi_hal_spi_bus_handle_deinit(handle); furi_hal_nfc_deinit_gpio_isr(); } -static void furi_hal_nfc_iso15693_listener_transparent_mode_exit(FuriHalSpiBusHandle* handle) { +static void + furi_hal_nfc_iso15693_listener_transparent_mode_exit(const FuriHalSpiBusHandle* handle) { // Configure gpio back to SPI and exit transparent mode furi_hal_nfc_init_gpio_isr(); furi_hal_spi_bus_handle_init(handle); @@ -299,7 +301,7 @@ static void furi_hal_nfc_iso15693_listener_transparent_mode_exit(FuriHalSpiBusHa st25r3916_direct_cmd(handle, ST25R3916_CMD_UNMASK_RECEIVE_DATA); } -static FuriHalNfcError furi_hal_nfc_iso15693_listener_init(FuriHalSpiBusHandle* handle) { +static FuriHalNfcError furi_hal_nfc_iso15693_listener_init(const FuriHalSpiBusHandle* handle) { furi_check(furi_hal_nfc_iso15693_listener == NULL); furi_hal_nfc_iso15693_listener = furi_hal_nfc_iso15693_listener_alloc(); @@ -328,7 +330,7 @@ static FuriHalNfcError furi_hal_nfc_iso15693_listener_init(FuriHalSpiBusHandle* return error; } -static FuriHalNfcError furi_hal_nfc_iso15693_listener_deinit(FuriHalSpiBusHandle* handle) { +static FuriHalNfcError furi_hal_nfc_iso15693_listener_deinit(const FuriHalSpiBusHandle* handle) { furi_check(furi_hal_nfc_iso15693_listener); furi_hal_nfc_iso15693_listener_transparent_mode_exit(handle); @@ -387,7 +389,7 @@ static FuriHalNfcEvent furi_hal_nfc_iso15693_wait_event(uint32_t timeout_ms) { } static FuriHalNfcError furi_hal_nfc_iso15693_listener_tx( - FuriHalSpiBusHandle* handle, + const FuriHalSpiBusHandle* handle, const uint8_t* tx_data, size_t tx_bits) { UNUSED(handle); @@ -407,7 +409,7 @@ FuriHalNfcError furi_hal_nfc_iso15693_listener_tx_sof(void) { } static FuriHalNfcError furi_hal_nfc_iso15693_listener_rx( - FuriHalSpiBusHandle* handle, + const FuriHalSpiBusHandle* handle, uint8_t* rx_data, size_t rx_data_size, size_t* rx_bits) { @@ -425,7 +427,7 @@ static FuriHalNfcError furi_hal_nfc_iso15693_listener_rx( return FuriHalNfcErrorNone; } -FuriHalNfcError furi_hal_nfc_iso15693_listener_sleep(FuriHalSpiBusHandle* handle) { +FuriHalNfcError furi_hal_nfc_iso15693_listener_sleep(const FuriHalSpiBusHandle* handle) { UNUSED(handle); return FuriHalNfcErrorNone; diff --git a/targets/f7/furi_hal/furi_hal_nfc_tech_i.h b/targets/f7/furi_hal/furi_hal_nfc_tech_i.h index e36dc852e..4a62f67c9 100644 --- a/targets/f7/furi_hal/furi_hal_nfc_tech_i.h +++ b/targets/f7/furi_hal/furi_hal_nfc_tech_i.h @@ -25,7 +25,7 @@ extern "C" { * @param[in,out] handle pointer to the NFC chip SPI handle. * @returns FuriHalNfcErrorNone on success, any other error code on failure. */ -typedef FuriHalNfcError (*FuriHalNfcChipConfig)(FuriHalSpiBusHandle* handle); +typedef FuriHalNfcError (*FuriHalNfcChipConfig)(const FuriHalSpiBusHandle* handle); /** * @brief Transmit data using technology-specific framing and timings. @@ -36,7 +36,7 @@ typedef FuriHalNfcError (*FuriHalNfcChipConfig)(FuriHalSpiBusHandle* handle); * @returns FuriHalNfcErrorNone on success, any other error code on failure. */ typedef FuriHalNfcError ( - *FuriHalNfcTx)(FuriHalSpiBusHandle* handle, const uint8_t* tx_data, size_t tx_bits); + *FuriHalNfcTx)(const FuriHalSpiBusHandle* handle, const uint8_t* tx_data, size_t tx_bits); /** * @brief Receive data using technology-specific framing and timings. @@ -48,7 +48,7 @@ typedef FuriHalNfcError ( * @returns FuriHalNfcErrorNone on success, any other error code on failure. */ typedef FuriHalNfcError (*FuriHalNfcRx)( - FuriHalSpiBusHandle* handle, + const FuriHalSpiBusHandle* handle, uint8_t* rx_data, size_t rx_data_size, size_t* rx_bits); @@ -69,7 +69,7 @@ typedef FuriHalNfcEvent (*FuriHalNfcWaitEvent)(uint32_t timeout_ms); * @param[in,out] handle pointer to the NFC chip SPI handle. * @returns FuriHalNfcErrorNone on success, any other error code on failure. */ -typedef FuriHalNfcError (*FuriHalNfcSleep)(FuriHalSpiBusHandle* handle); +typedef FuriHalNfcError (*FuriHalNfcSleep)(const FuriHalSpiBusHandle* handle); /** * @brief Go to idle in listener mode. @@ -79,7 +79,7 @@ typedef FuriHalNfcError (*FuriHalNfcSleep)(FuriHalSpiBusHandle* handle); * @param[in,out] handle pointer to the NFC chip SPI handle. * @returns FuriHalNfcErrorNone on success, any other error code on failure. */ -typedef FuriHalNfcError (*FuriHalNfcIdle)(FuriHalSpiBusHandle* handle); +typedef FuriHalNfcError (*FuriHalNfcIdle)(const FuriHalSpiBusHandle* handle); /** * @brief Technology-specific compenstaion values for pollers. @@ -160,7 +160,7 @@ extern const FuriHalNfcTechBase furi_hal_nfc_felica; * This variable is defined in furi_hal_nfc.c. It will need to be modified * in case when a new technology is to be added. */ -extern const FuriHalNfcTechBase* furi_hal_nfc_tech[]; +extern const FuriHalNfcTechBase* const furi_hal_nfc_tech[]; #ifdef __cplusplus } diff --git a/targets/f7/furi_hal/furi_hal_sd.c b/targets/f7/furi_hal/furi_hal_sd.c index eca5b6da9..152192736 100644 --- a/targets/f7/furi_hal/furi_hal_sd.c +++ b/targets/f7/furi_hal/furi_hal_sd.c @@ -204,7 +204,7 @@ typedef struct { } SD_CardInfo; /** Pointer to currently used SPI Handle */ -FuriHalSpiBusHandle* furi_hal_sd_spi_handle = NULL; +const FuriHalSpiBusHandle* furi_hal_sd_spi_handle = NULL; static inline void sd_spi_select_card(void) { furi_hal_gpio_write(furi_hal_sd_spi_handle->cs, false); diff --git a/targets/f7/furi_hal/furi_hal_spi.c b/targets/f7/furi_hal/furi_hal_spi.c index 2a7cb7c25..9997d278d 100644 --- a/targets/f7/furi_hal/furi_hal_spi.c +++ b/targets/f7/furi_hal/furi_hal_spi.c @@ -38,17 +38,17 @@ void furi_hal_spi_bus_deinit(FuriHalSpiBus* bus) { bus->callback(bus, FuriHalSpiBusEventDeinit); } -void furi_hal_spi_bus_handle_init(FuriHalSpiBusHandle* handle) { +void furi_hal_spi_bus_handle_init(const FuriHalSpiBusHandle* handle) { furi_check(handle); handle->callback(handle, FuriHalSpiBusHandleEventInit); } -void furi_hal_spi_bus_handle_deinit(FuriHalSpiBusHandle* handle) { +void furi_hal_spi_bus_handle_deinit(const FuriHalSpiBusHandle* handle) { furi_check(handle); handle->callback(handle, FuriHalSpiBusHandleEventDeinit); } -void furi_hal_spi_acquire(FuriHalSpiBusHandle* handle) { +void furi_hal_spi_acquire(const FuriHalSpiBusHandle* handle) { furi_check(handle); furi_hal_power_insomnia_enter(); @@ -62,7 +62,7 @@ void furi_hal_spi_acquire(FuriHalSpiBusHandle* handle) { handle->callback(handle, FuriHalSpiBusHandleEventActivate); } -void furi_hal_spi_release(FuriHalSpiBusHandle* handle) { +void furi_hal_spi_release(const FuriHalSpiBusHandle* handle) { furi_check(handle); furi_check(handle->bus->current_handle == handle); @@ -77,7 +77,7 @@ void furi_hal_spi_release(FuriHalSpiBusHandle* handle) { furi_hal_power_insomnia_exit(); } -static void furi_hal_spi_bus_end_txrx(FuriHalSpiBusHandle* handle, uint32_t timeout) { +static void furi_hal_spi_bus_end_txrx(const FuriHalSpiBusHandle* handle, uint32_t timeout) { UNUSED(timeout); // FIXME while(LL_SPI_GetTxFIFOLevel(handle->bus->spi) != LL_SPI_TX_FIFO_EMPTY) ; @@ -89,7 +89,7 @@ static void furi_hal_spi_bus_end_txrx(FuriHalSpiBusHandle* handle, uint32_t time } bool furi_hal_spi_bus_rx( - FuriHalSpiBusHandle* handle, + const FuriHalSpiBusHandle* handle, uint8_t* buffer, size_t size, uint32_t timeout) { @@ -102,7 +102,7 @@ bool furi_hal_spi_bus_rx( } bool furi_hal_spi_bus_tx( - FuriHalSpiBusHandle* handle, + const FuriHalSpiBusHandle* handle, const uint8_t* buffer, size_t size, uint32_t timeout) { @@ -128,7 +128,7 @@ bool furi_hal_spi_bus_tx( } bool furi_hal_spi_bus_trx( - FuriHalSpiBusHandle* handle, + const FuriHalSpiBusHandle* handle, const uint8_t* tx_buffer, uint8_t* rx_buffer, size_t size, @@ -192,7 +192,7 @@ static void spi_dma_isr(void* context) { } bool furi_hal_spi_bus_trx_dma( - FuriHalSpiBusHandle* handle, + const FuriHalSpiBusHandle* handle, uint8_t* tx_buffer, uint8_t* rx_buffer, size_t size, diff --git a/targets/f7/furi_hal/furi_hal_spi_config.c b/targets/f7/furi_hal/furi_hal_spi_config.c index 8a694961a..ece0c05f7 100644 --- a/targets/f7/furi_hal/furi_hal_spi_config.c +++ b/targets/f7/furi_hal/furi_hal_spi_config.c @@ -147,7 +147,7 @@ FuriHalSpiBus furi_hal_spi_bus_d = { /* SPI Bus Handles */ inline static void furi_hal_spi_bus_r_handle_event_callback( - FuriHalSpiBusHandle* handle, + const FuriHalSpiBusHandle* handle, FuriHalSpiBusHandleEvent event, const LL_SPI_InitTypeDef* preset) { if(event == FuriHalSpiBusHandleEventInit) { @@ -193,7 +193,7 @@ inline static void furi_hal_spi_bus_r_handle_event_callback( } inline static void furi_hal_spi_bus_external_handle_event_callback( - FuriHalSpiBusHandle* handle, + const FuriHalSpiBusHandle* handle, FuriHalSpiBusHandleEvent event, const LL_SPI_InitTypeDef* preset) { if(event == FuriHalSpiBusHandleEventInit) { @@ -239,7 +239,7 @@ inline static void furi_hal_spi_bus_external_handle_event_callback( } inline static void furi_hal_spi_bus_nfc_handle_event_callback( - FuriHalSpiBusHandle* handle, + const FuriHalSpiBusHandle* handle, FuriHalSpiBusHandleEvent event, const LL_SPI_InitTypeDef* preset) { if(event == FuriHalSpiBusHandleEventInit) { @@ -305,12 +305,12 @@ inline static void furi_hal_spi_bus_nfc_handle_event_callback( } static void furi_hal_spi_bus_handle_subghz_event_callback( - FuriHalSpiBusHandle* handle, + const FuriHalSpiBusHandle* handle, FuriHalSpiBusHandleEvent event) { furi_hal_spi_bus_r_handle_event_callback(handle, event, &furi_hal_spi_preset_1edge_low_8m); } -FuriHalSpiBusHandle furi_hal_spi_bus_handle_subghz = { +const FuriHalSpiBusHandle furi_hal_spi_bus_handle_subghz = { .bus = &furi_hal_spi_bus_r, .callback = furi_hal_spi_bus_handle_subghz_event_callback, .miso = &gpio_spi_r_miso, @@ -320,12 +320,12 @@ FuriHalSpiBusHandle furi_hal_spi_bus_handle_subghz = { }; static void furi_hal_spi_bus_handle_nfc_event_callback( - FuriHalSpiBusHandle* handle, + const FuriHalSpiBusHandle* handle, FuriHalSpiBusHandleEvent event) { furi_hal_spi_bus_nfc_handle_event_callback(handle, event, &furi_hal_spi_preset_2edge_low_8m); } -FuriHalSpiBusHandle furi_hal_spi_bus_handle_nfc = { +const FuriHalSpiBusHandle furi_hal_spi_bus_handle_nfc = { .bus = &furi_hal_spi_bus_r, .callback = furi_hal_spi_bus_handle_nfc_event_callback, .miso = &gpio_spi_r_miso, @@ -335,13 +335,13 @@ FuriHalSpiBusHandle furi_hal_spi_bus_handle_nfc = { }; static void furi_hal_spi_bus_handle_external_event_callback( - FuriHalSpiBusHandle* handle, + const FuriHalSpiBusHandle* handle, FuriHalSpiBusHandleEvent event) { furi_hal_spi_bus_external_handle_event_callback( handle, event, &furi_hal_spi_preset_1edge_low_2m); } -FuriHalSpiBusHandle furi_hal_spi_bus_handle_external = { +const FuriHalSpiBusHandle furi_hal_spi_bus_handle_external = { .bus = &furi_hal_spi_bus_r, .callback = furi_hal_spi_bus_handle_external_event_callback, .miso = &gpio_ext_pa6, @@ -351,7 +351,7 @@ FuriHalSpiBusHandle furi_hal_spi_bus_handle_external = { }; inline static void furi_hal_spi_bus_d_handle_event_callback( - FuriHalSpiBusHandle* handle, + const FuriHalSpiBusHandle* handle, FuriHalSpiBusHandleEvent event, const LL_SPI_InitTypeDef* preset) { if(event == FuriHalSpiBusHandleEventInit) { @@ -392,12 +392,12 @@ inline static void furi_hal_spi_bus_d_handle_event_callback( } static void furi_hal_spi_bus_handle_display_event_callback( - FuriHalSpiBusHandle* handle, + const FuriHalSpiBusHandle* handle, FuriHalSpiBusHandleEvent event) { furi_hal_spi_bus_d_handle_event_callback(handle, event, &furi_hal_spi_preset_1edge_low_4m); } -FuriHalSpiBusHandle furi_hal_spi_bus_handle_display = { +const FuriHalSpiBusHandle furi_hal_spi_bus_handle_display = { .bus = &furi_hal_spi_bus_d, .callback = furi_hal_spi_bus_handle_display_event_callback, .miso = &gpio_spi_d_miso, @@ -407,12 +407,12 @@ FuriHalSpiBusHandle furi_hal_spi_bus_handle_display = { }; static void furi_hal_spi_bus_handle_sd_fast_event_callback( - FuriHalSpiBusHandle* handle, + const FuriHalSpiBusHandle* handle, FuriHalSpiBusHandleEvent event) { furi_hal_spi_bus_d_handle_event_callback(handle, event, &furi_hal_spi_preset_1edge_low_16m); } -FuriHalSpiBusHandle furi_hal_spi_bus_handle_sd_fast = { +const FuriHalSpiBusHandle furi_hal_spi_bus_handle_sd_fast = { .bus = &furi_hal_spi_bus_d, .callback = furi_hal_spi_bus_handle_sd_fast_event_callback, .miso = &gpio_spi_d_miso, @@ -422,12 +422,12 @@ FuriHalSpiBusHandle furi_hal_spi_bus_handle_sd_fast = { }; static void furi_hal_spi_bus_handle_sd_slow_event_callback( - FuriHalSpiBusHandle* handle, + const FuriHalSpiBusHandle* handle, FuriHalSpiBusHandleEvent event) { furi_hal_spi_bus_d_handle_event_callback(handle, event, &furi_hal_spi_preset_1edge_low_2m); } -FuriHalSpiBusHandle furi_hal_spi_bus_handle_sd_slow = { +const FuriHalSpiBusHandle furi_hal_spi_bus_handle_sd_slow = { .bus = &furi_hal_spi_bus_d, .callback = furi_hal_spi_bus_handle_sd_slow_event_callback, .miso = &gpio_spi_d_miso, diff --git a/targets/f7/furi_hal/furi_hal_spi_config.h b/targets/f7/furi_hal/furi_hal_spi_config.h index eab633a19..e90cd7061 100644 --- a/targets/f7/furi_hal/furi_hal_spi_config.h +++ b/targets/f7/furi_hal/furi_hal_spi_config.h @@ -28,10 +28,10 @@ extern FuriHalSpiBus furi_hal_spi_bus_r; extern FuriHalSpiBus furi_hal_spi_bus_d; /** CC1101 on `furi_hal_spi_bus_r` */ -extern FuriHalSpiBusHandle furi_hal_spi_bus_handle_subghz; +extern const FuriHalSpiBusHandle furi_hal_spi_bus_handle_subghz; /** ST25R3916 on `furi_hal_spi_bus_r` */ -extern FuriHalSpiBusHandle furi_hal_spi_bus_handle_nfc; +extern const FuriHalSpiBusHandle furi_hal_spi_bus_handle_nfc; /** External on `furi_hal_spi_bus_r` * Preset: `furi_hal_spi_preset_1edge_low_2m` @@ -45,16 +45,16 @@ extern FuriHalSpiBusHandle furi_hal_spi_bus_handle_nfc; * Bus pins are floating on inactive state, CS high after initialization * */ -extern FuriHalSpiBusHandle furi_hal_spi_bus_handle_external; +extern const FuriHalSpiBusHandle furi_hal_spi_bus_handle_external; /** ST7567(Display) on `furi_hal_spi_bus_d` */ -extern FuriHalSpiBusHandle furi_hal_spi_bus_handle_display; +extern const FuriHalSpiBusHandle furi_hal_spi_bus_handle_display; /** SdCard in fast mode on `furi_hal_spi_bus_d` */ -extern FuriHalSpiBusHandle furi_hal_spi_bus_handle_sd_fast; +extern const FuriHalSpiBusHandle furi_hal_spi_bus_handle_sd_fast; /** SdCard in slow mode on `furi_hal_spi_bus_d` */ -extern FuriHalSpiBusHandle furi_hal_spi_bus_handle_sd_slow; +extern const FuriHalSpiBusHandle furi_hal_spi_bus_handle_sd_slow; #ifdef __cplusplus } diff --git a/targets/f7/furi_hal/furi_hal_spi_types.h b/targets/f7/furi_hal/furi_hal_spi_types.h index ecc18d50d..9bf138ac0 100644 --- a/targets/f7/furi_hal/furi_hal_spi_types.h +++ b/targets/f7/furi_hal/furi_hal_spi_types.h @@ -31,7 +31,7 @@ typedef void (*FuriHalSpiBusEventCallback)(FuriHalSpiBus* bus, FuriHalSpiBusEven struct FuriHalSpiBus { SPI_TypeDef* spi; FuriHalSpiBusEventCallback callback; - FuriHalSpiBusHandle* current_handle; + const FuriHalSpiBusHandle* current_handle; }; /** FuriHal spi handle states */ @@ -44,7 +44,7 @@ typedef enum { /** FuriHal spi handle event callback */ typedef void (*FuriHalSpiBusHandleEventCallback)( - FuriHalSpiBusHandle* handle, + const FuriHalSpiBusHandle* handle, FuriHalSpiBusHandleEvent event); /** FuriHal spi handle */ diff --git a/targets/f7/furi_hal/furi_hal_usb_cdc.h b/targets/f7/furi_hal/furi_hal_usb_cdc.h index 89b68991b..50d456698 100644 --- a/targets/f7/furi_hal/furi_hal_usb_cdc.h +++ b/targets/f7/furi_hal/furi_hal_usb_cdc.h @@ -9,11 +9,21 @@ extern "C" { #endif +typedef enum { + CdcStateDisconnected, + CdcStateConnected, +} CdcState; + +typedef enum { + CdcCtrlLineDTR = (1 << 0), + CdcCtrlLineRTS = (1 << 1), +} CdcCtrlLine; + typedef struct { void (*tx_ep_callback)(void* context); void (*rx_ep_callback)(void* context); - void (*state_callback)(void* context, uint8_t state); - void (*ctrl_line_callback)(void* context, uint8_t state); + void (*state_callback)(void* context, CdcState state); + void (*ctrl_line_callback)(void* context, CdcCtrlLine ctrl_lines); void (*config_callback)(void* context, struct usb_cdc_line_coding* config); } CdcCallbacks; diff --git a/targets/f7/stm32wb55xx_flash.ld b/targets/f7/stm32wb55xx_flash.ld index ef61bb238..c31aee863 100644 --- a/targets/f7/stm32wb55xx_flash.ld +++ b/targets/f7/stm32wb55xx_flash.ld @@ -131,4 +131,55 @@ SECTIONS { MB_MEM2 (NOLOAD) : { _sMB_MEM2 = . ; *(MB_MEM2) ; _eMB_MEM2 = . ; } >RAM2A ._sram2a_free : { . = ALIGN(4); __sram2a_free__ = .; } >RAM2A ._sram2b_start : { . = ALIGN(4); __sram2b_start__ = .; } >RAM2B + + /* Default debug-related rules from ld */ + + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + .gnu.build.attributes : { *(.gnu.build.attributes .gnu.build.attributes.*) } + /* DWARF debug sections. + Symbols in the DWARF debugging sections are relative to the beginning + of the section so we begin them at 0. */ + /* DWARF 1. */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions. */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2. */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2. */ + .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line .debug_line.* .debug_line_end) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* SGI/MIPS DWARF 2 extensions. */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } + /* DWARF 3. */ + .debug_pubtypes 0 : { *(.debug_pubtypes) } + .debug_ranges 0 : { *(.debug_ranges) } + /* DWARF 5. */ + .debug_addr 0 : { *(.debug_addr) } + .debug_line_str 0 : { *(.debug_line_str) } + .debug_loclists 0 : { *(.debug_loclists) } + .debug_macro 0 : { *(.debug_macro) } + .debug_names 0 : { *(.debug_names) } + .debug_rnglists 0 : { *(.debug_rnglists) } + .debug_str_offsets 0 : { *(.debug_str_offsets) } + .debug_sup 0 : { *(.debug_sup) } + .note.gnu.arm.ident 0 : { KEEP (*(.note.gnu.arm.ident)) } + /DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) } } diff --git a/targets/f7/stm32wb55xx_ram_fw.ld b/targets/f7/stm32wb55xx_ram_fw.ld index 93579788d..c3f5f8a6a 100644 --- a/targets/f7/stm32wb55xx_ram_fw.ld +++ b/targets/f7/stm32wb55xx_ram_fw.ld @@ -129,4 +129,55 @@ SECTIONS { MB_MEM2 (NOLOAD) : { _sMB_MEM2 = . ; *(MB_MEM2) ; _eMB_MEM2 = . ; } >RAM2A ._sram2a_free : { . = ALIGN(4); __sram2a_free__ = .; } >RAM2A ._sram2b_start : { . = ALIGN(4); __sram2b_start__ = .; } >RAM2B + + /* Default debug-related rules from ld */ + + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + .gnu.build.attributes : { *(.gnu.build.attributes .gnu.build.attributes.*) } + /* DWARF debug sections. + Symbols in the DWARF debugging sections are relative to the beginning + of the section so we begin them at 0. */ + /* DWARF 1. */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions. */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2. */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2. */ + .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line .debug_line.* .debug_line_end) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* SGI/MIPS DWARF 2 extensions. */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } + /* DWARF 3. */ + .debug_pubtypes 0 : { *(.debug_pubtypes) } + .debug_ranges 0 : { *(.debug_ranges) } + /* DWARF 5. */ + .debug_addr 0 : { *(.debug_addr) } + .debug_line_str 0 : { *(.debug_line_str) } + .debug_loclists 0 : { *(.debug_loclists) } + .debug_macro 0 : { *(.debug_macro) } + .debug_names 0 : { *(.debug_names) } + .debug_rnglists 0 : { *(.debug_rnglists) } + .debug_str_offsets 0 : { *(.debug_str_offsets) } + .debug_sup 0 : { *(.debug_sup) } + .note.gnu.arm.ident 0 : { KEEP (*(.note.gnu.arm.ident)) } + /DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) } } diff --git a/targets/f7/target.json b/targets/f7/target.json index f5b3cf3b6..911aa0822 100644 --- a/targets/f7/target.json +++ b/targets/f7/target.json @@ -50,6 +50,7 @@ "flipper7", "bit_lib", "toolbox", - "datetime" + "datetime", + "ieee754_parse_wrap" ] } diff --git a/targets/furi_hal_include/furi_hal_i2c.h b/targets/furi_hal_include/furi_hal_i2c.h index 7d69cd74d..fe9f0949c 100644 --- a/targets/furi_hal_include/furi_hal_i2c.h +++ b/targets/furi_hal_include/furi_hal_i2c.h @@ -55,14 +55,14 @@ void furi_hal_i2c_init(void); * * @param handle Pointer to FuriHalI2cBusHandle instance */ -void furi_hal_i2c_acquire(FuriHalI2cBusHandle* handle); +void furi_hal_i2c_acquire(const FuriHalI2cBusHandle* handle); /** Release I2C bus handle * * @param handle Pointer to FuriHalI2cBusHandle instance acquired in * `furi_hal_i2c_acquire` */ -void furi_hal_i2c_release(FuriHalI2cBusHandle* handle); +void furi_hal_i2c_release(const FuriHalI2cBusHandle* handle); /** Perform I2C TX transfer * @@ -75,7 +75,7 @@ void furi_hal_i2c_release(FuriHalI2cBusHandle* handle); * @return true on successful transfer, false otherwise */ bool furi_hal_i2c_tx( - FuriHalI2cBusHandle* handle, + const FuriHalI2cBusHandle* handle, uint8_t address, const uint8_t* data, size_t size, @@ -96,7 +96,7 @@ bool furi_hal_i2c_tx( * @return true on successful transfer, false otherwise */ bool furi_hal_i2c_tx_ext( - FuriHalI2cBusHandle* handle, + const FuriHalI2cBusHandle* handle, uint16_t address, bool ten_bit, const uint8_t* data, @@ -116,7 +116,7 @@ bool furi_hal_i2c_tx_ext( * @return true on successful transfer, false otherwise */ bool furi_hal_i2c_rx( - FuriHalI2cBusHandle* handle, + const FuriHalI2cBusHandle* handle, uint8_t address, uint8_t* data, size_t size, @@ -136,7 +136,7 @@ bool furi_hal_i2c_rx( * @return true on successful transfer, false otherwise */ bool furi_hal_i2c_rx_ext( - FuriHalI2cBusHandle* handle, + const FuriHalI2cBusHandle* handle, uint16_t address, bool ten_bit, uint8_t* data, @@ -158,7 +158,7 @@ bool furi_hal_i2c_rx_ext( * @return true on successful transfer, false otherwise */ bool furi_hal_i2c_trx( - FuriHalI2cBusHandle* handle, + const FuriHalI2cBusHandle* handle, uint8_t address, const uint8_t* tx_data, size_t tx_size, @@ -174,7 +174,10 @@ bool furi_hal_i2c_trx( * * @return true if device present and is ready, false otherwise */ -bool furi_hal_i2c_is_device_ready(FuriHalI2cBusHandle* handle, uint8_t i2c_addr, uint32_t timeout); +bool furi_hal_i2c_is_device_ready( + const FuriHalI2cBusHandle* handle, + uint8_t i2c_addr, + uint32_t timeout); /** Perform I2C device register read (8-bit) * @@ -187,7 +190,7 @@ bool furi_hal_i2c_is_device_ready(FuriHalI2cBusHandle* handle, uint8_t i2c_addr, * @return true on successful transfer, false otherwise */ bool furi_hal_i2c_read_reg_8( - FuriHalI2cBusHandle* handle, + const FuriHalI2cBusHandle* handle, uint8_t i2c_addr, uint8_t reg_addr, uint8_t* data, @@ -204,7 +207,7 @@ bool furi_hal_i2c_read_reg_8( * @return true on successful transfer, false otherwise */ bool furi_hal_i2c_read_reg_16( - FuriHalI2cBusHandle* handle, + const FuriHalI2cBusHandle* handle, uint8_t i2c_addr, uint8_t reg_addr, uint16_t* data, @@ -222,7 +225,7 @@ bool furi_hal_i2c_read_reg_16( * @return true on successful transfer, false otherwise */ bool furi_hal_i2c_read_mem( - FuriHalI2cBusHandle* handle, + const FuriHalI2cBusHandle* handle, uint8_t i2c_addr, uint8_t mem_addr, uint8_t* data, @@ -240,7 +243,7 @@ bool furi_hal_i2c_read_mem( * @return true on successful transfer, false otherwise */ bool furi_hal_i2c_write_reg_8( - FuriHalI2cBusHandle* handle, + const FuriHalI2cBusHandle* handle, uint8_t i2c_addr, uint8_t reg_addr, uint8_t data, @@ -257,7 +260,7 @@ bool furi_hal_i2c_write_reg_8( * @return true on successful transfer, false otherwise */ bool furi_hal_i2c_write_reg_16( - FuriHalI2cBusHandle* handle, + const FuriHalI2cBusHandle* handle, uint8_t i2c_addr, uint8_t reg_addr, uint16_t data, @@ -275,7 +278,7 @@ bool furi_hal_i2c_write_reg_16( * @return true on successful transfer, false otherwise */ bool furi_hal_i2c_write_mem( - FuriHalI2cBusHandle* handle, + const FuriHalI2cBusHandle* handle, uint8_t i2c_addr, uint8_t mem_addr, const uint8_t* data, diff --git a/targets/furi_hal_include/furi_hal_spi.h b/targets/furi_hal_include/furi_hal_spi.h index d497dff5c..41f3abdaa 100644 --- a/targets/furi_hal_include/furi_hal_spi.h +++ b/targets/furi_hal_include/furi_hal_spi.h @@ -35,13 +35,13 @@ void furi_hal_spi_bus_deinit(FuriHalSpiBus* bus); * * @param handle pointer to FuriHalSpiBusHandle instance */ -void furi_hal_spi_bus_handle_init(FuriHalSpiBusHandle* handle); +void furi_hal_spi_bus_handle_init(const FuriHalSpiBusHandle* handle); /** Deinitialize SPI Bus Handle * * @param handle pointer to FuriHalSpiBusHandle instance */ -void furi_hal_spi_bus_handle_deinit(FuriHalSpiBusHandle* handle); +void furi_hal_spi_bus_handle_deinit(const FuriHalSpiBusHandle* handle); /** Acquire SPI bus * @@ -49,7 +49,7 @@ void furi_hal_spi_bus_handle_deinit(FuriHalSpiBusHandle* handle); * * @param handle pointer to FuriHalSpiBusHandle instance */ -void furi_hal_spi_acquire(FuriHalSpiBusHandle* handle); +void furi_hal_spi_acquire(const FuriHalSpiBusHandle* handle); /** Release SPI bus * @@ -57,7 +57,7 @@ void furi_hal_spi_acquire(FuriHalSpiBusHandle* handle); * * @param handle pointer to FuriHalSpiBusHandle instance */ -void furi_hal_spi_release(FuriHalSpiBusHandle* handle); +void furi_hal_spi_release(const FuriHalSpiBusHandle* handle); /** SPI Receive * @@ -69,7 +69,7 @@ void furi_hal_spi_release(FuriHalSpiBusHandle* handle); * @return true on sucess */ bool furi_hal_spi_bus_rx( - FuriHalSpiBusHandle* handle, + const FuriHalSpiBusHandle* handle, uint8_t* buffer, size_t size, uint32_t timeout); @@ -84,7 +84,7 @@ bool furi_hal_spi_bus_rx( * @return true on success */ bool furi_hal_spi_bus_tx( - FuriHalSpiBusHandle* handle, + const FuriHalSpiBusHandle* handle, const uint8_t* buffer, size_t size, uint32_t timeout); @@ -100,7 +100,7 @@ bool furi_hal_spi_bus_tx( * @return true on success */ bool furi_hal_spi_bus_trx( - FuriHalSpiBusHandle* handle, + const FuriHalSpiBusHandle* handle, const uint8_t* tx_buffer, uint8_t* rx_buffer, size_t size, @@ -117,7 +117,7 @@ bool furi_hal_spi_bus_trx( * @return true on success */ bool furi_hal_spi_bus_trx_dma( - FuriHalSpiBusHandle* handle, + const FuriHalSpiBusHandle* handle, uint8_t* tx_buffer, uint8_t* rx_buffer, size_t size,