diff --git a/.gitattributes b/.gitattributes index 032c1bd1..176a458f 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1 @@ * text=auto - diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a448c0ab..e5c59cfa 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -103,6 +103,32 @@ jobs: -o firmware/.obj/${TARGET}/full.hex -Intel done + - name: 'Generate full dfu file' + if: ${{ !github.event.pull_request.head.repo.fork }} + uses: ./.github/actions/docker + with: + run: | + for TARGET in ${TARGETS} + do + hex2dfu \ + -i firmware/.obj/${TARGET}/full.hex \ + -o artifacts/flipper-z-${TARGET}-full-${{steps.names.outputs.suffix}}.dfu \ + -l "Flipper Zero $(echo $TARGET | tr a-z A-Z)" + done + + - name: 'Generate full json file' + if: ${{ !github.event.pull_request.head.repo.fork }} + uses: ./.github/actions/docker + with: + run: | + for TARGET in ${TARGETS} + do + jq -s '.[0] * .[1]' \ + bootloader/.obj/${TARGET}/bootloader.json \ + firmware/.obj/${TARGET}/firmware.json \ + > artifacts/flipper-z-${TARGET}-full-${{steps.names.outputs.suffix}}.json + done + - name: 'Move upload files' if: ${{ !github.event.pull_request.head.repo.fork }} uses: ./.github/actions/docker @@ -116,25 +142,16 @@ jobs: artifacts/flipper-z-${TARGET}-bootloader-${{steps.names.outputs.suffix}}.bin mv bootloader/.obj/${TARGET}/bootloader.elf \ artifacts/flipper-z-${TARGET}-bootloader-${{steps.names.outputs.suffix}}.elf + mv bootloader/.obj/${TARGET}/bootloader.json \ + artifacts/flipper-z-${TARGET}-bootloader-${{steps.names.outputs.suffix}}.json mv firmware/.obj/${TARGET}/firmware.dfu \ artifacts/flipper-z-${TARGET}-firmware-${{steps.names.outputs.suffix}}.dfu mv firmware/.obj/${TARGET}/firmware.bin \ artifacts/flipper-z-${TARGET}-firmware-${{steps.names.outputs.suffix}}.bin mv firmware/.obj/${TARGET}/firmware.elf \ artifacts/flipper-z-${TARGET}-firmware-${{steps.names.outputs.suffix}}.elf - done - - - name: 'Generate full dfu file' - if: ${{ !github.event.pull_request.head.repo.fork }} - uses: ./.github/actions/docker - with: - run: | - for TARGET in ${TARGETS} - do - hex2dfu \ - -i firmware/.obj/${TARGET}/full.hex \ - -o artifacts/flipper-z-${TARGET}-full-${{steps.names.outputs.suffix}}.dfu \ - -l "Flipper Zero $(echo $TARGET | tr a-z A-Z)" + mv firmware/.obj/${TARGET}/firmware.json \ + artifacts/flipper-z-${TARGET}-firmware-${{steps.names.outputs.suffix}}.json done - name: 'Full flash asssembly: bootloader as base' diff --git a/applications/about/about.c b/applications/about/about.c index a24d8ee9..8d739b79 100644 --- a/applications/about/about.c +++ b/applications/about/about.c @@ -129,11 +129,11 @@ static DialogMessageButton fw_version_screen(DialogsApp* dialogs, DialogMessage* return result; } -static DialogMessageButton boot_version_screen(DialogsApp* dialogs, DialogMessage* message) { +static DialogMessageButton bootloader_version_screen(DialogsApp* dialogs, DialogMessage* message) { DialogMessageButton result; string_t buffer; string_init(buffer); - const Version* ver = furi_hal_version_get_boot_version(); + const Version* ver = furi_hal_version_get_bootloader_version(); if(!ver) { string_cat_printf(buffer, "No info\n"); @@ -167,7 +167,7 @@ const AboutDialogScreen about_screens[] = { icon2_screen, hw_version_screen, fw_version_screen, - boot_version_screen}; + bootloader_version_screen}; const size_t about_screens_count = sizeof(about_screens) / sizeof(AboutDialogScreen); diff --git a/applications/bt/bt_service/bt.c b/applications/bt/bt_service/bt.c index b85139fd..2ea1599b 100755 --- a/applications/bt/bt_service/bt.c +++ b/applications/bt/bt_service/bt.c @@ -84,7 +84,7 @@ static void bt_on_data_received_callback(uint8_t* data, uint16_t size, void* con furi_assert(context); Bt* bt = context; - size_t bytes_processed = rpc_feed_bytes(bt->rpc_session, data, size, 1000); + size_t bytes_processed = rpc_session_feed(bt->rpc_session, data, size, 1000); if(bytes_processed != size) { FURI_LOG_E(BT_SERVICE_TAG, "Only %d of %d bytes processed by RPC", bytes_processed, size); } @@ -129,8 +129,9 @@ static void bt_on_gap_event_callback(BleEvent event, void* context) { furi_check(osMessageQueuePut(bt->message_queue, &message, 0, osWaitForever) == osOK); // Open RPC session FURI_LOG_I(BT_SERVICE_TAG, "Open RPC connection"); - bt->rpc_session = rpc_open_session(bt->rpc); - rpc_set_send_bytes_callback(bt->rpc_session, bt_rpc_send_bytes_callback, bt); + bt->rpc_session = rpc_session_open(bt->rpc); + rpc_session_set_send_bytes_callback(bt->rpc_session, bt_rpc_send_bytes_callback); + rpc_session_set_context(bt->rpc_session, bt); furi_hal_bt_set_data_event_callbacks( bt_on_data_received_callback, bt_on_data_sent_callback, bt); // Update battery level @@ -142,7 +143,7 @@ static void bt_on_gap_event_callback(BleEvent event, void* context) { } else if(event.type == BleEventTypeDisconnected) { FURI_LOG_I(BT_SERVICE_TAG, "Close RPC connection"); if(bt->rpc_session) { - rpc_close_session(bt->rpc_session); + rpc_session_close(bt->rpc_session); bt->rpc_session = NULL; } } else if(event.type == BleEventTypeStartAdvertising) { diff --git a/applications/cli/cli.c b/applications/cli/cli.c index d9fde866..7d60f7d1 100644 --- a/applications/cli/cli.c +++ b/applications/cli/cli.c @@ -263,7 +263,7 @@ static void cli_handle_autocomplete(Cli* cli) { cli->cursor_position = string_size(cli->line); } // Cleanup - string_clean(common); + string_clear(common); // Show prompt cli_prompt(cli); } diff --git a/applications/cli/cli_commands.c b/applications/cli/cli_commands.c index fd87625b..e4053c4c 100644 --- a/applications/cli/cli_commands.c +++ b/applications/cli/cli_commands.c @@ -56,13 +56,13 @@ static const uint8_t enclave_signature_expected[ENCLAVE_SIGNATURE_KEY_SLOTS][ENC */ void cli_command_device_info(Cli* cli, string_t args, void* context) { // Device Info version - printf("device_info_major : %d\r\n", 1); - printf("device_info_minor : %d\r\n", 0); + printf("device_info_major : %d\r\n", 2); + printf("device_info_minor : %d\r\n", 0); // Model name - printf("hardware_model : %s\r\n", furi_hal_version_get_model_name()); + printf("hardware_model : %s\r\n", furi_hal_version_get_model_name()); // Unique ID - printf("hardware_uid : "); + printf("hardware_uid : "); const uint8_t* uid = furi_hal_version_uid(); for(size_t i = 0; i < furi_hal_version_uid_size(); i++) { printf("%02X", uid[i]); @@ -70,69 +70,69 @@ void cli_command_device_info(Cli* cli, string_t args, void* context) { printf("\r\n"); // OTP Revision - printf("hardware_otp_ver : %d\r\n", furi_hal_version_get_otp_version()); - printf("hardware_timestamp : %lu\r\n", furi_hal_version_get_hw_timestamp()); + printf("hardware_otp_ver : %d\r\n", furi_hal_version_get_otp_version()); + printf("hardware_timestamp : %lu\r\n", furi_hal_version_get_hw_timestamp()); // Board Revision - printf("hardware_ver : %d\r\n", furi_hal_version_get_hw_version()); - printf("hardware_target : %d\r\n", furi_hal_version_get_hw_target()); - printf("hardware_body : %d\r\n", furi_hal_version_get_hw_body()); - printf("hardware_connect : %d\r\n", furi_hal_version_get_hw_connect()); - printf("hardware_display : %d\r\n", furi_hal_version_get_hw_display()); + printf("hardware_ver : %d\r\n", furi_hal_version_get_hw_version()); + printf("hardware_target : %d\r\n", furi_hal_version_get_hw_target()); + printf("hardware_body : %d\r\n", furi_hal_version_get_hw_body()); + printf("hardware_connect : %d\r\n", furi_hal_version_get_hw_connect()); + printf("hardware_display : %d\r\n", furi_hal_version_get_hw_display()); // Board Personification - printf("hardware_color : %d\r\n", furi_hal_version_get_hw_color()); - printf("hardware_region : %d\r\n", furi_hal_version_get_hw_region()); + printf("hardware_color : %d\r\n", furi_hal_version_get_hw_color()); + printf("hardware_region : %d\r\n", furi_hal_version_get_hw_region()); const char* name = furi_hal_version_get_name_ptr(); if(name) { - printf("hardware_name : %s\r\n", name); + printf("hardware_name : %s\r\n", name); } // Bootloader Version - const Version* boot_version = furi_hal_version_get_boot_version(); - if(boot_version) { - printf("boot_commit : %s\r\n", version_get_githash(boot_version)); - printf("boot_branch : %s\r\n", version_get_gitbranch(boot_version)); - printf("boot_branch_num : %s\r\n", version_get_gitbranchnum(boot_version)); - printf("boot_version : %s\r\n", version_get_version(boot_version)); - printf("boot_build_date : %s\r\n", version_get_builddate(boot_version)); - printf("boot_target : %d\r\n", version_get_target(boot_version)); + const Version* bootloader_version = furi_hal_version_get_bootloader_version(); + if(bootloader_version) { + printf("bootloader_commit : %s\r\n", version_get_githash(bootloader_version)); + printf("bootloader_branch : %s\r\n", version_get_gitbranch(bootloader_version)); + printf("bootloader_branch_num : %s\r\n", version_get_gitbranchnum(bootloader_version)); + printf("bootloader_version : %s\r\n", version_get_version(bootloader_version)); + printf("bootloader_build_date : %s\r\n", version_get_builddate(bootloader_version)); + printf("bootloader_target : %d\r\n", version_get_target(bootloader_version)); } // Firmware version const Version* firmware_version = furi_hal_version_get_firmware_version(); if(firmware_version) { - printf("firmware_commit : %s\r\n", version_get_githash(firmware_version)); - printf("firmware_branch : %s\r\n", version_get_gitbranch(firmware_version)); - printf("firmware_branch_num : %s\r\n", version_get_gitbranchnum(firmware_version)); - printf("firmware_version : %s\r\n", version_get_version(firmware_version)); - printf("firmware_build_date : %s\r\n", version_get_builddate(firmware_version)); - printf("firmware_target : %d\r\n", version_get_target(firmware_version)); + printf("firmware_commit : %s\r\n", version_get_githash(firmware_version)); + printf("firmware_branch : %s\r\n", version_get_gitbranch(firmware_version)); + printf("firmware_branch_num : %s\r\n", version_get_gitbranchnum(firmware_version)); + printf("firmware_version : %s\r\n", version_get_version(firmware_version)); + printf("firmware_build_date : %s\r\n", version_get_builddate(firmware_version)); + printf("firmware_target : %d\r\n", version_get_target(firmware_version)); } WirelessFwInfo_t pWirelessInfo; if(furi_hal_bt_is_alive() && SHCI_GetWirelessFwInfo(&pWirelessInfo) == SHCI_Success) { - printf("radio_alive : true\r\n"); + printf("radio_alive : true\r\n"); // FUS Info - printf("radio_fus_major : %d\r\n", pWirelessInfo.FusVersionMajor); - printf("radio_fus_minor : %d\r\n", pWirelessInfo.FusVersionMinor); - printf("radio_fus_sub : %d\r\n", pWirelessInfo.FusVersionSub); - printf("radio_fus_sram2b : %dK\r\n", pWirelessInfo.FusMemorySizeSram2B); - printf("radio_fus_sram2a : %dK\r\n", pWirelessInfo.FusMemorySizeSram2A); - printf("radio_fus_flash : %dK\r\n", pWirelessInfo.FusMemorySizeFlash * 4); + printf("radio_fus_major : %d\r\n", pWirelessInfo.FusVersionMajor); + printf("radio_fus_minor : %d\r\n", pWirelessInfo.FusVersionMinor); + printf("radio_fus_sub : %d\r\n", pWirelessInfo.FusVersionSub); + printf("radio_fus_sram2b : %dK\r\n", pWirelessInfo.FusMemorySizeSram2B); + printf("radio_fus_sram2a : %dK\r\n", pWirelessInfo.FusMemorySizeSram2A); + printf("radio_fus_flash : %dK\r\n", pWirelessInfo.FusMemorySizeFlash * 4); // Stack Info - printf("radio_stack_type : %d\r\n", pWirelessInfo.StackType); - printf("radio_stack_major : %d\r\n", pWirelessInfo.VersionMajor); - printf("radio_stack_minor : %d\r\n", pWirelessInfo.VersionMinor); - printf("radio_stack_sub : %d\r\n", pWirelessInfo.VersionSub); - printf("radio_stack_branch : %d\r\n", pWirelessInfo.VersionBranch); - printf("radio_stack_release : %d\r\n", pWirelessInfo.VersionReleaseType); - printf("radio_stack_sram2b : %dK\r\n", pWirelessInfo.MemorySizeSram2B); - printf("radio_stack_sram2a : %dK\r\n", pWirelessInfo.MemorySizeSram2A); - printf("radio_stack_sram1 : %dK\r\n", pWirelessInfo.MemorySizeSram1); - printf("radio_stack_flash : %dK\r\n", pWirelessInfo.MemorySizeFlash * 4); + printf("radio_stack_type : %d\r\n", pWirelessInfo.StackType); + printf("radio_stack_major : %d\r\n", pWirelessInfo.VersionMajor); + printf("radio_stack_minor : %d\r\n", pWirelessInfo.VersionMinor); + printf("radio_stack_sub : %d\r\n", pWirelessInfo.VersionSub); + printf("radio_stack_branch : %d\r\n", pWirelessInfo.VersionBranch); + printf("radio_stack_release : %d\r\n", pWirelessInfo.VersionReleaseType); + printf("radio_stack_sram2b : %dK\r\n", pWirelessInfo.MemorySizeSram2B); + printf("radio_stack_sram2a : %dK\r\n", pWirelessInfo.MemorySizeSram2A); + printf("radio_stack_sram1 : %dK\r\n", pWirelessInfo.MemorySizeSram1); + printf("radio_stack_flash : %dK\r\n", pWirelessInfo.MemorySizeFlash * 4); // Mac address - printf("radio_ble_mac : "); + printf("radio_ble_mac : "); const uint8_t* ble_mac = furi_hal_version_get_ble_mac(); for(size_t i = 0; i < 6; i++) { printf("%02X", ble_mac[i]); @@ -154,12 +154,12 @@ void cli_command_device_info(Cli* cli, string_t args, void* context) { furi_hal_crypto_store_unload_key(key_slot + 1); } } - printf("enclave_valid_keys : %d\r\n", enclave_valid_keys); + printf("enclave_valid_keys : %d\r\n", enclave_valid_keys); printf( - "enclave_valid : %s\r\n", + "enclave_valid : %s\r\n", (enclave_valid_keys == ENCLAVE_SIGNATURE_KEY_SLOTS) ? "true" : "false"); } else { - printf("radio_alive : false\r\n"); + printf("radio_alive : false\r\n"); } } diff --git a/applications/desktop/desktop.c b/applications/desktop/desktop.c index 4aa21453..aa9b4ed5 100644 --- a/applications/desktop/desktop.c +++ b/applications/desktop/desktop.c @@ -41,6 +41,7 @@ Desktop* desktop_alloc() { desktop->debug_view = desktop_debug_alloc(); desktop->first_start_view = desktop_first_start_alloc(); desktop->hw_mismatch_popup = popup_alloc(); + desktop->code_input = code_input_alloc(); view_dispatcher_add_view( desktop->view_dispatcher, DesktopViewMain, desktop_main_get_view(desktop->main_view)); @@ -62,7 +63,8 @@ Desktop* desktop_alloc() { desktop->view_dispatcher, DesktopViewHwMismatch, popup_get_view(desktop->hw_mismatch_popup)); - + view_dispatcher_add_view( + desktop->view_dispatcher, DesktopViewPinSetup, code_input_get_view(desktop->code_input)); // Lock icon desktop->lock_viewport = view_port_alloc(); view_port_set_width(desktop->lock_viewport, icon_get_width(&I_Lock_8x8)); @@ -82,6 +84,7 @@ void desktop_free(Desktop* desktop) { view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewDebug); view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewFirstStart); view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewHwMismatch); + view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewPinSetup); view_dispatcher_free(desktop->view_dispatcher); scene_manager_free(desktop->scene_manager); @@ -92,6 +95,7 @@ void desktop_free(Desktop* desktop) { desktop_debug_free(desktop->debug_view); desktop_first_start_free(desktop->first_start_view); popup_free(desktop->hw_mismatch_popup); + code_input_free(desktop->code_input); furi_record_close("gui"); desktop->gui = NULL; diff --git a/applications/desktop/desktop_i.h b/applications/desktop/desktop_i.h index 62caff0f..0bf5f3da 100644 --- a/applications/desktop/desktop_i.h +++ b/applications/desktop/desktop_i.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -29,6 +30,7 @@ typedef enum { DesktopViewDebug, DesktopViewFirstStart, DesktopViewHwMismatch, + DesktopViewPinSetup, DesktopViewTotal, } DesktopViewEnum; @@ -46,7 +48,10 @@ struct Desktop { DesktopLockMenuView* lock_menu; DesktopLockedView* locked_view; DesktopDebugView* debug_view; + CodeInput* code_input; + DesktopSettings settings; + PinCode pincode_buffer; ViewPort* lock_viewport; }; diff --git a/applications/desktop/desktop_settings/desktop_settings.h b/applications/desktop/desktop_settings/desktop_settings.h index a2aead82..c8f324a1 100644 --- a/applications/desktop/desktop_settings/desktop_settings.h +++ b/applications/desktop/desktop_settings/desktop_settings.h @@ -3,11 +3,20 @@ #include #include -#define DESKTOP_SETTINGS_VER (0) +#define DESKTOP_SETTINGS_VER (1) +#define PIN_MAX_LENGTH 12 + +typedef struct { + uint8_t length; + uint8_t data[PIN_MAX_LENGTH]; +} PinCode; typedef struct { uint8_t version; uint16_t favorite; + + PinCode pincode; + bool locked; } DesktopSettings; bool desktop_settings_load(DesktopSettings* desktop_settings); diff --git a/applications/desktop/desktop_settings/desktop_settings_app.c b/applications/desktop/desktop_settings/desktop_settings_app.c index 3c7610f5..e284ff80 100644 --- a/applications/desktop/desktop_settings/desktop_settings_app.c +++ b/applications/desktop/desktop_settings/desktop_settings_app.c @@ -33,10 +33,13 @@ DesktopSettingsApp* desktop_settings_app_alloc() { app->submenu = submenu_alloc(); view_dispatcher_add_view( - app->view_dispatcher, DesktopSettingsAppViewMain, submenu_get_view(app->submenu)); + app->view_dispatcher, DesktopSettingsAppViewMenu, submenu_get_view(app->submenu)); + app->code_input = code_input_alloc(); view_dispatcher_add_view( - app->view_dispatcher, DesktopSettingsAppViewFavorite, submenu_get_view(app->submenu)); + app->view_dispatcher, + DesktopSettingsAppViewPincodeInput, + code_input_get_view(app->code_input)); scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneStart); return app; @@ -45,9 +48,10 @@ DesktopSettingsApp* desktop_settings_app_alloc() { void desktop_settings_app_free(DesktopSettingsApp* app) { furi_assert(app); // Variable item list - view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewMain); - view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewFavorite); + view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewMenu); submenu_free(app->submenu); + view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewPincodeInput); + code_input_free(app->code_input); // View dispatcher view_dispatcher_free(app->view_dispatcher); scene_manager_free(app->scene_manager); diff --git a/applications/desktop/desktop_settings/desktop_settings_app.h b/applications/desktop/desktop_settings/desktop_settings_app.h index ba381cde..8ec8e892 100644 --- a/applications/desktop/desktop_settings/desktop_settings_app.h +++ b/applications/desktop/desktop_settings/desktop_settings_app.h @@ -6,20 +6,32 @@ #include #include #include +#include #include "desktop_settings.h" + #include "scenes/desktop_settings_scene.h" typedef enum { - DesktopSettingsAppViewMain, - DesktopSettingsAppViewFavorite, + CodeEventsSetPin, + CodeEventsChangePin, + CodeEventsDisablePin, +} CodeEventsEnum; + +typedef enum { + DesktopSettingsAppViewMenu, + DesktopSettingsAppViewPincodeInput, } DesktopSettingsAppView; typedef struct { DesktopSettings settings; + Gui* gui; SceneManager* scene_manager; ViewDispatcher* view_dispatcher; Submenu* submenu; + CodeInput* code_input; + + uint8_t menu_idx; } DesktopSettingsApp; diff --git a/applications/desktop/desktop_settings/scenes/desktop_settings_scene_config.h b/applications/desktop/desktop_settings/scenes/desktop_settings_scene_config.h index a2abec0a..126873db 100644 --- a/applications/desktop/desktop_settings/scenes/desktop_settings_scene_config.h +++ b/applications/desktop/desktop_settings/scenes/desktop_settings_scene_config.h @@ -1,2 +1,4 @@ ADD_SCENE(desktop_settings, start, Start) ADD_SCENE(desktop_settings, favorite, Favorite) +ADD_SCENE(desktop_settings, pincode_menu, PinCodeMenu) +ADD_SCENE(desktop_settings, pincode_input, PinCodeInput) diff --git a/applications/desktop/desktop_settings/scenes/desktop_settings_scene_favorite.c b/applications/desktop/desktop_settings/scenes/desktop_settings_scene_favorite.c index e8559017..1fead1f7 100644 --- a/applications/desktop/desktop_settings/scenes/desktop_settings_scene_favorite.c +++ b/applications/desktop/desktop_settings/scenes/desktop_settings_scene_favorite.c @@ -22,7 +22,7 @@ void desktop_settings_scene_favorite_on_enter(void* context) { submenu_set_header(app->submenu, "Quick access app:"); submenu_set_selected_item(app->submenu, app->settings.favorite); - view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewFavorite); + view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewMenu); } bool desktop_settings_scene_favorite_on_event(void* context, SceneManagerEvent event) { diff --git a/applications/desktop/desktop_settings/scenes/desktop_settings_scene_pincode_input.c b/applications/desktop/desktop_settings/scenes/desktop_settings_scene_pincode_input.c new file mode 100644 index 00000000..d809f999 --- /dev/null +++ b/applications/desktop/desktop_settings/scenes/desktop_settings_scene_pincode_input.c @@ -0,0 +1,62 @@ +#include "../desktop_settings_app.h" + +#define SCENE_EXIT_EVENT (0U) + +void desktop_settings_scene_ok_callback(void* context) { + DesktopSettingsApp* app = context; + uint32_t state = + scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppScenePinCodeInput); + + if(state == CodeEventsDisablePin) { + memset(app->settings.pincode.data, 0, app->settings.pincode.length * sizeof(uint8_t)); + app->settings.pincode.length = 0; + } + + view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EXIT_EVENT); +} + +void desktop_settings_scene_pincode_input_on_enter(void* context) { + DesktopSettingsApp* app = context; + CodeInput* code_input = app->code_input; + + uint32_t state = + scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppScenePinCodeInput); + bool update = state != CodeEventsDisablePin; + + code_input_set_header_text(code_input, "PIN Code Setup"); + code_input_set_result_callback( + code_input, + desktop_settings_scene_ok_callback, + NULL, + app, + app->settings.pincode.data, + &app->settings.pincode.length, + update); + + view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewPincodeInput); +} + +bool desktop_settings_scene_pincode_input_on_event(void* context, SceneManagerEvent event) { + DesktopSettingsApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { + case SCENE_EXIT_EVENT: + scene_manager_previous_scene(app->scene_manager); + consumed = true; + break; + + default: + consumed = true; + break; + } + } + return consumed; +} + +void desktop_settings_scene_pincode_input_on_exit(void* context) { + DesktopSettingsApp* app = context; + code_input_set_result_callback(app->code_input, NULL, NULL, NULL, NULL, NULL, 0); + code_input_set_header_text(app->code_input, ""); +} diff --git a/applications/desktop/desktop_settings/scenes/desktop_settings_scene_pincode_menu.c b/applications/desktop/desktop_settings/scenes/desktop_settings_scene_pincode_menu.c new file mode 100644 index 00000000..78c2eee9 --- /dev/null +++ b/applications/desktop/desktop_settings/scenes/desktop_settings_scene_pincode_menu.c @@ -0,0 +1,78 @@ +#include "../desktop_settings_app.h" +#include "applications.h" + +static void desktop_settings_scene_pincode_menu_submenu_callback(void* context, uint32_t index) { + DesktopSettingsApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void desktop_settings_scene_pincode_menu_on_enter(void* context) { + DesktopSettingsApp* app = context; + Submenu* submenu = app->submenu; + submenu_clean(submenu); + + if(!app->settings.pincode.length) { + submenu_add_item( + submenu, + "Set Pin", + CodeEventsSetPin, + desktop_settings_scene_pincode_menu_submenu_callback, + app); + + } else { + submenu_add_item( + submenu, + "Change Pin", + CodeEventsChangePin, + desktop_settings_scene_pincode_menu_submenu_callback, + app); + + submenu_add_item( + submenu, + "Disable", + CodeEventsDisablePin, + desktop_settings_scene_pincode_menu_submenu_callback, + app); + } + + submenu_set_header(app->submenu, "Pin code settings:"); + submenu_set_selected_item(app->submenu, app->menu_idx); + view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewMenu); +} + +bool desktop_settings_scene_pincode_menu_on_event(void* context, SceneManagerEvent event) { + DesktopSettingsApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { + case CodeEventsSetPin: + scene_manager_set_scene_state( + app->scene_manager, DesktopSettingsAppScenePinCodeInput, event.event); + scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinCodeInput); + consumed = true; + break; + case CodeEventsChangePin: + scene_manager_set_scene_state( + app->scene_manager, DesktopSettingsAppScenePinCodeInput, event.event); + scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinCodeInput); + consumed = true; + break; + case CodeEventsDisablePin: + scene_manager_set_scene_state( + app->scene_manager, DesktopSettingsAppScenePinCodeInput, event.event); + scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinCodeInput); + consumed = true; + break; + default: + consumed = true; + break; + } + } + return consumed; +} + +void desktop_settings_scene_pincode_menu_on_exit(void* context) { + DesktopSettingsApp* app = context; + submenu_clean(app->submenu); +} diff --git a/applications/desktop/desktop_settings/scenes/desktop_settings_scene_start.c b/applications/desktop/desktop_settings/scenes/desktop_settings_scene_start.c index 33d66d0c..43b541ac 100755 --- a/applications/desktop/desktop_settings/scenes/desktop_settings_scene_start.c +++ b/applications/desktop/desktop_settings/scenes/desktop_settings_scene_start.c @@ -29,7 +29,7 @@ void desktop_settings_scene_start_on_enter(void* context) { desktop_settings_scene_start_submenu_callback, app); - view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewMain); + view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewMenu); } bool desktop_settings_scene_start_on_event(void* context, SceneManagerEvent event) { @@ -39,7 +39,11 @@ bool desktop_settings_scene_start_on_event(void* context, SceneManagerEvent even if(event.type == SceneManagerEventTypeCustom) { switch(event.event) { case DesktopSettingsStartSubmenuIndexFavorite: - scene_manager_next_scene(app->scene_manager, DesktopSettingsAppViewFavorite); + scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite); + consumed = true; + break; + case DesktopSettingsStartSubmenuIndexPinSetup: + scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinCodeMenu); consumed = true; break; } diff --git a/applications/desktop/scenes/desktop_scene_config.h b/applications/desktop/scenes/desktop_scene_config.h index 067de7c4..e84db67d 100644 --- a/applications/desktop/scenes/desktop_scene_config.h +++ b/applications/desktop/scenes/desktop_scene_config.h @@ -4,3 +4,4 @@ ADD_SCENE(desktop, locked, Locked) ADD_SCENE(desktop, debug, Debug) ADD_SCENE(desktop, first_start, FirstStart) ADD_SCENE(desktop, hw_mismatch, HwMismatch) +ADD_SCENE(desktop, pinsetup, PinSetup) diff --git a/applications/desktop/scenes/desktop_scene_lock_menu.c b/applications/desktop/scenes/desktop_scene_lock_menu.c index 537469b5..46561e47 100644 --- a/applications/desktop/scenes/desktop_scene_lock_menu.c +++ b/applications/desktop/scenes/desktop_scene_lock_menu.c @@ -9,7 +9,10 @@ void desktop_scene_lock_menu_callback(DesktopLockMenuEvent event, void* context) void desktop_scene_lock_menu_on_enter(void* context) { Desktop* desktop = (Desktop*)context; + desktop_settings_load(&desktop->settings); + desktop_lock_menu_set_callback(desktop->lock_menu, desktop_scene_lock_menu_callback, desktop); + desktop_lock_menu_pin_set(desktop->lock_menu, desktop->settings.pincode.length > 0); view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewLockMenu); } @@ -20,10 +23,25 @@ bool desktop_scene_lock_menu_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { switch(event.event) { case DesktopLockMenuEventLock: + scene_manager_set_scene_state( + desktop->scene_manager, DesktopSceneLocked, DesktopLockedNoPin); scene_manager_next_scene(desktop->scene_manager, DesktopSceneLocked); consumed = true; break; + case DesktopLockMenuEventPinLock: + if(desktop->settings.pincode.length > 0) { + desktop->settings.locked = true; + desktop_settings_save(&desktop->settings); + scene_manager_set_scene_state( + desktop->scene_manager, DesktopSceneLocked, DesktopLockedWithPin); + scene_manager_next_scene(desktop->scene_manager, DesktopSceneLocked); + } else { + scene_manager_next_scene(desktop->scene_manager, DesktopScenePinSetup); + } + + consumed = true; + break; case DesktopLockMenuEventExit: scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain); consumed = true; diff --git a/applications/desktop/scenes/desktop_scene_locked.c b/applications/desktop/scenes/desktop_scene_locked.c index 6e7064ad..ad436212 100644 --- a/applications/desktop/scenes/desktop_scene_locked.c +++ b/applications/desktop/scenes/desktop_scene_locked.c @@ -15,12 +15,39 @@ void desktop_scene_locked_on_enter(void* context) { desktop_locked_update_hint_timeout(locked_view); desktop_locked_set_dolphin_animation(locked_view); + uint32_t state = scene_manager_get_scene_state(desktop->scene_manager, DesktopViewLocked); + + desktop_locked_with_pin(desktop->locked_view, state == DesktopLockedWithPin); + view_port_enabled_set(desktop->lock_viewport, true); osTimerStart(locked_view->timer, 63); view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewLocked); } +static bool desktop_scene_locked_check_pin(Desktop* desktop, DesktopMainEvent event) { + bool match = false; + + size_t length = desktop->pincode_buffer.length; + length = code_input_push(desktop->pincode_buffer.data, length, event); + desktop->pincode_buffer.length = length; + + match = code_input_compare( + desktop->pincode_buffer.data, + length, + desktop->settings.pincode.data, + desktop->settings.pincode.length); + + if(match) { + desktop->pincode_buffer.length = 0; + desktop->settings.locked = false; + desktop_settings_save(&desktop->settings); + desktop_main_unlocked(desktop->main_view); + } + + return match; +} + bool desktop_scene_locked_on_event(void* context, SceneManagerEvent event) { Desktop* desktop = (Desktop*)context; @@ -36,7 +63,17 @@ bool desktop_scene_locked_on_event(void* context, SceneManagerEvent event) { case DesktopLockedEventUpdate: desktop_locked_manage_redraw(desktop->locked_view); consumed = true; + break; + case DesktopLockedEventInputReset: + desktop->pincode_buffer.length = 0; + break; default: + if(desktop_scene_locked_check_pin(desktop, event.event)) { + scene_manager_set_scene_state( + desktop->scene_manager, DesktopSceneMain, DesktopMainEventUnlocked); + scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain); + consumed = true; + } break; } } diff --git a/applications/desktop/scenes/desktop_scene_main.c b/applications/desktop/scenes/desktop_scene_main.c index 4826c2cd..4065b51c 100644 --- a/applications/desktop/scenes/desktop_scene_main.c +++ b/applications/desktop/scenes/desktop_scene_main.c @@ -34,6 +34,8 @@ void desktop_scene_main_on_enter(void* context) { desktop_main_set_callback(main_view, desktop_scene_main_callback, desktop); view_port_enabled_set(desktop->lock_viewport, false); + desktop_settings_load(&desktop->settings); + if(scene_manager_get_scene_state(desktop->scene_manager, DesktopSceneMain) == DesktopMainEventUnlocked) { desktop_main_unlocked(desktop->main_view); diff --git a/applications/desktop/scenes/desktop_scene_pinsetup.c b/applications/desktop/scenes/desktop_scene_pinsetup.c new file mode 100644 index 00000000..78515d7e --- /dev/null +++ b/applications/desktop/scenes/desktop_scene_pinsetup.c @@ -0,0 +1,50 @@ +#include "../desktop_i.h" + +#define SCENE_EXIT_EVENT (0U) + +void desktop_scene_ok_callback(void* context) { + Desktop* app = context; + desktop_settings_save(&app->settings); + view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EXIT_EVENT); +} + +void desktop_scene_pinsetup_on_enter(void* context) { + Desktop* app = context; + CodeInput* code_input = app->code_input; + + code_input_set_result_callback( + code_input, + desktop_scene_ok_callback, + NULL, + app, + app->settings.pincode.data, + &app->settings.pincode.length, + true); + + view_dispatcher_switch_to_view(app->view_dispatcher, DesktopViewPinSetup); +} + +bool desktop_scene_pinsetup_on_event(void* context, SceneManagerEvent event) { + Desktop* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { + case SCENE_EXIT_EVENT: + scene_manager_previous_scene(app->scene_manager); + consumed = true; + break; + + default: + consumed = true; + break; + } + } + return consumed; +} + +void desktop_scene_pinsetup_on_exit(void* context) { + Desktop* app = context; + code_input_set_result_callback(app->code_input, NULL, NULL, NULL, NULL, NULL, 0); + code_input_set_header_text(app->code_input, ""); +} diff --git a/applications/desktop/views/desktop_debug.c b/applications/desktop/views/desktop_debug.c index cd3dfd15..ae5f1f8d 100644 --- a/applications/desktop/views/desktop_debug.c +++ b/applications/desktop/views/desktop_debug.c @@ -42,7 +42,7 @@ void desktop_debug_render(Canvas* canvas, void* model) { my_name ? my_name : "Unknown"); canvas_draw_str(canvas, 5, 23, buffer); - ver = m->screen == DesktopViewStatsBoot ? furi_hal_version_get_boot_version() : + ver = m->screen == DesktopViewStatsBoot ? furi_hal_version_get_bootloader_version() : furi_hal_version_get_firmware_version(); if(!ver) { diff --git a/applications/desktop/views/desktop_lock_menu.c b/applications/desktop/views/desktop_lock_menu.c index 11392cd8..52a8df56 100644 --- a/applications/desktop/views/desktop_lock_menu.c +++ b/applications/desktop/views/desktop_lock_menu.c @@ -12,6 +12,14 @@ void desktop_lock_menu_set_callback( lock_menu->context = context; } +void desktop_lock_menu_pin_set(DesktopLockMenuView* lock_menu, bool pin_is_set) { + with_view_model( + lock_menu->view, (DesktopLockMenuViewModel * model) { + model->pin_set = pin_is_set; + return true; + }); +} + void desktop_lock_menu_reset_idx(DesktopLockMenuView* lock_menu) { with_view_model( lock_menu->view, (DesktopLockMenuViewModel * model) { @@ -26,6 +34,10 @@ static void lock_menu_callback(void* context, uint8_t index) { switch(index) { case 0: // lock lock_menu->callback(DesktopLockMenuEventLock, lock_menu->context); + break; + case 1: // lock + lock_menu->callback(DesktopLockMenuEventPinLock, lock_menu->context); + break; default: // wip message with_view_model( lock_menu->view, (DesktopLockMenuViewModel * model) { @@ -37,7 +49,7 @@ static void lock_menu_callback(void* context, uint8_t index) { } void desktop_lock_menu_render(Canvas* canvas, void* model) { - const char* Lockmenu_Items[3] = {"Lock", "Set PIN", "DUMB mode"}; + const char* Lockmenu_Items[3] = {"Lock", "Lock with PIN", "DUMB mode"}; DesktopLockMenuViewModel* m = model; canvas_clear(canvas); @@ -47,13 +59,13 @@ void desktop_lock_menu_render(Canvas* canvas, void* model) { canvas_set_font(canvas, FontSecondary); for(uint8_t i = 0; i < 3; ++i) { - canvas_draw_str_aligned( - canvas, - 64, - 13 + (i * 17), - AlignCenter, - AlignCenter, - (m->hint_timeout && m->idx == i && m->idx) ? "Not implemented" : Lockmenu_Items[i]); + const char* str = Lockmenu_Items[i]; + + if(i == 1 && !m->pin_set) str = "Set PIN"; + if(m->hint_timeout && m->idx == 2 && m->idx == i) str = "Not implemented"; + + canvas_draw_str_aligned(canvas, 64, 13 + (i * 17), AlignCenter, AlignCenter, str); + if(m->idx == i) elements_frame(canvas, 15, 5 + (i * 17), 98, 15); } } diff --git a/applications/desktop/views/desktop_lock_menu.h b/applications/desktop/views/desktop_lock_menu.h index 714069cb..f86b5d74 100644 --- a/applications/desktop/views/desktop_lock_menu.h +++ b/applications/desktop/views/desktop_lock_menu.h @@ -11,6 +11,7 @@ typedef enum { DesktopLockMenuEventLock, DesktopLockMenuEventUnlock, + DesktopLockMenuEventPinLock, DesktopLockMenuEventExit, } DesktopLockMenuEvent; @@ -27,6 +28,7 @@ struct DesktopLockMenuView { typedef struct { uint8_t idx; uint8_t hint_timeout; + bool pin_set; } DesktopLockMenuViewModel; void desktop_lock_menu_set_callback( @@ -35,6 +37,7 @@ void desktop_lock_menu_set_callback( void* context); View* desktop_lock_menu_get_view(DesktopLockMenuView* lock_menu); +void desktop_lock_menu_pin_set(DesktopLockMenuView* lock_menu, bool pin_is_set); void desktop_lock_menu_reset_idx(DesktopLockMenuView* lock_menu); DesktopLockMenuView* desktop_lock_menu_alloc(); void desktop_lock_menu_free(DesktopLockMenuView* lock_menu); diff --git a/applications/desktop/views/desktop_locked.c b/applications/desktop/views/desktop_locked.c index b4942506..f4373dfb 100644 --- a/applications/desktop/views/desktop_locked.c +++ b/applications/desktop/views/desktop_locked.c @@ -80,6 +80,14 @@ void desktop_locked_reset_counter(DesktopLockedView* locked_view) { }); } +void desktop_locked_with_pin(DesktopLockedView* locked_view, bool locked) { + with_view_model( + locked_view->view, (DesktopLockedViewModel * model) { + model->pin_lock = locked; + return true; + }); +} + void desktop_locked_render(Canvas* canvas, void* model) { DesktopLockedViewModel* m = model; uint32_t now = osKernelGetTickCount(); @@ -100,7 +108,7 @@ void desktop_locked_render(Canvas* canvas, void* model) { canvas_set_font(canvas, FontPrimary); elements_multiline_text_framed(canvas, 42, 30, "Locked"); - } else { + } else if(!m->pin_lock) { canvas_set_font(canvas, FontSecondary); canvas_draw_icon(canvas, 13, 5, &I_LockPopup_100x49); elements_multiline_text(canvas, 65, 20, "To unlock\npress:"); @@ -116,27 +124,49 @@ View* desktop_locked_get_view(DesktopLockedView* locked_view) { bool desktop_locked_input(InputEvent* event, void* context) { furi_assert(event); furi_assert(context); - DesktopLockedView* locked_view = context; + + uint32_t press_time = 0; + bool locked_with_pin = false; + + with_view_model( + locked_view->view, (DesktopLockedViewModel * model) { + locked_with_pin = model->pin_lock; + return false; + }); + if(event->type == InputTypeShort) { - desktop_locked_update_hint_timeout(locked_view); + if(locked_with_pin) { + press_time = osKernelGetTickCount(); - if(event->key == InputKeyBack) { - uint32_t press_time = osKernelGetTickCount(); - // check if pressed sequentially - if(press_time - locked_view->lock_lastpress > UNLOCK_RST_TIMEOUT) { + if(press_time - locked_view->lock_lastpress > UNLOCK_RST_TIMEOUT * 3) { locked_view->lock_lastpress = press_time; - locked_view->lock_count = 0; - } else if(press_time - locked_view->lock_lastpress < UNLOCK_RST_TIMEOUT) { - locked_view->lock_lastpress = press_time; - locked_view->lock_count++; + locked_view->callback(DesktopLockedEventInputReset, locked_view->context); } - if(locked_view->lock_count == UNLOCK_CNT) { - locked_view->lock_count = 0; - locked_view->callback(DesktopLockedEventUnlock, locked_view->context); + locked_view->callback(event->key, locked_view->context); + } else { + desktop_locked_update_hint_timeout(locked_view); + + if(event->key == InputKeyBack) { + press_time = osKernelGetTickCount(); + // check if pressed sequentially + if(press_time - locked_view->lock_lastpress < UNLOCK_RST_TIMEOUT) { + locked_view->lock_lastpress = press_time; + locked_view->lock_count++; + } + + if(locked_view->lock_count == UNLOCK_CNT) { + locked_view->lock_count = 0; + locked_view->callback(DesktopLockedEventUnlock, locked_view->context); + } } } + + if(press_time - locked_view->lock_lastpress > UNLOCK_RST_TIMEOUT) { + locked_view->lock_lastpress = press_time; + locked_view->lock_count = 0; + } } // All events consumed return true; diff --git a/applications/desktop/views/desktop_locked.h b/applications/desktop/views/desktop_locked.h index c85705a8..f7d635b3 100644 --- a/applications/desktop/views/desktop_locked.h +++ b/applications/desktop/views/desktop_locked.h @@ -15,10 +15,16 @@ #define DOOR_R_POS_MIN 60 typedef enum { - DesktopLockedEventUnlock, - DesktopLockedEventUpdate, + DesktopLockedEventUnlock = 10U, + DesktopLockedEventUpdate = 11U, + DesktopLockedEventInputReset = 12U, } DesktopLockedEvent; +typedef enum { + DesktopLockedWithPin, + DesktopLockedNoPin, +} DesktopLockedSceneState; + typedef struct DesktopLockedView DesktopLockedView; typedef void (*DesktopLockedViewCallback)(DesktopLockedEvent event, void* context); @@ -42,6 +48,7 @@ typedef struct { int8_t door_right_x; bool animation_seq_end; + bool pin_lock; } DesktopLockedViewModel; void desktop_locked_set_callback( @@ -58,5 +65,4 @@ void desktop_locked_manage_redraw(DesktopLockedView* locked_view); View* desktop_locked_get_view(DesktopLockedView* locked_view); DesktopLockedView* desktop_locked_alloc(); void desktop_locked_free(DesktopLockedView* locked_view); -void desktop_main_unlocked(DesktopMainView* main_view); -void desktop_main_reset_hint(DesktopMainView* main_view); \ No newline at end of file +void desktop_locked_with_pin(DesktopLockedView* lock_menu, bool locked); \ No newline at end of file diff --git a/applications/desktop/views/desktop_main.c b/applications/desktop/views/desktop_main.c index a35ba008..00a606e8 100644 --- a/applications/desktop/views/desktop_main.c +++ b/applications/desktop/views/desktop_main.c @@ -67,6 +67,7 @@ bool desktop_main_input(InputEvent* event, void* context) { } else if(event->key == InputKeyLeft && event->type == InputTypeShort) { main_view->callback(DesktopMainEventOpenFavorite, main_view->context); } + desktop_main_reset_hint(main_view); return true; diff --git a/applications/desktop/views/desktop_main.h b/applications/desktop/views/desktop_main.h index 78678b8c..1b631f6f 100644 --- a/applications/desktop/views/desktop_main.h +++ b/applications/desktop/views/desktop_main.h @@ -7,12 +7,12 @@ #include typedef enum { - DesktopMainEventOpenMenu, DesktopMainEventOpenLockMenu, - DesktopMainEventOpenDebug, - DesktopMainEventUnlocked, DesktopMainEventOpenArchive, DesktopMainEventOpenFavorite, + DesktopMainEventOpenMenu, + DesktopMainEventOpenDebug, + DesktopMainEventUnlocked, } DesktopMainEvent; typedef struct DesktopMainView DesktopMainView; @@ -37,9 +37,8 @@ void desktop_main_set_callback( void* context); View* desktop_main_get_view(DesktopMainView* main_view); - DesktopMainView* desktop_main_alloc(); - void desktop_main_free(DesktopMainView* main_view); - void desktop_main_switch_dolphin_animation(DesktopMainView* main_view); +void desktop_main_unlocked(DesktopMainView* main_view); +void desktop_main_reset_hint(DesktopMainView* main_view); diff --git a/applications/gpio/gpio_app_i.h b/applications/gpio/gpio_app_i.h index bfeab404..226fa06e 100644 --- a/applications/gpio/gpio_app_i.h +++ b/applications/gpio/gpio_app_i.h @@ -12,6 +12,14 @@ #include #include "views/gpio_test.h" +#define GPIO_SCENE_START_CUSTOM_EVENT_OTG_OFF (0UL) +#define GPIO_SCENE_START_CUSTOM_EVENT_OTG_ON (1UL) +#define GPIO_SCENE_START_CUSTOM_EVENT_TEST (2UL) +#define GPIO_SCENE_START_CUSTOM_EVENT_USB_UART (3UL) + +#define GPIO_SCENE_USB_UART_CUSTOM_EVENT_ENABLE (4UL) +#define GPIO_SCENE_USB_UART_CUSTOM_EVENT_DISABLE (5UL) + struct GpioApp { Gui* gui; ViewDispatcher* view_dispatcher; diff --git a/applications/gpio/scenes/gpio_scene_start.c b/applications/gpio/scenes/gpio_scene_start.c index b3ed40fd..b7d94fe7 100644 --- a/applications/gpio/scenes/gpio_scene_start.c +++ b/applications/gpio/scenes/gpio_scene_start.c @@ -1,11 +1,6 @@ #include "../gpio_app_i.h" #include "furi-hal-power.h" -#define GPIO_SCENE_START_CUSTOM_EVENT_OTG_OFF (0UL) -#define GPIO_SCENE_START_CUSTOM_EVENT_OTG_ON (1UL) -#define GPIO_SCENE_START_CUSTOM_EVENT_TEST (2UL) -#define GPIO_SCENE_START_CUSTOM_EVENT_USB_UART (3UL) - enum GpioItem { GpioItemOtg, GpioItemTest, diff --git a/applications/gpio/scenes/gpio_scene_usb_uart.c b/applications/gpio/scenes/gpio_scene_usb_uart.c index 05de8e7b..c8ec0141 100644 --- a/applications/gpio/scenes/gpio_scene_usb_uart.c +++ b/applications/gpio/scenes/gpio_scene_usb_uart.c @@ -1,17 +1,6 @@ +#include "../usb_uart_bridge.h" #include "../gpio_app_i.h" #include "furi-hal.h" -#include -#include -#include "usb_cdc.h" - -#define USB_PKT_LEN CDC_DATA_SZ -#define USB_UART_RX_BUF_SIZE (USB_PKT_LEN * 3) -#define USB_UART_TX_BUF_SIZE (USB_PKT_LEN * 3) - -typedef enum { - WorkerCmdStop = (1 << 0), - -} WorkerCommandFlags; typedef enum { UsbUartLineIndexVcp, @@ -21,42 +10,7 @@ typedef enum { UsbUartLineIndexDisable, } LineIndex; -typedef enum { - UsbUartPortUSART1 = 0, - UsbUartPortLPUART1 = 1, -} PortIdx; - -typedef struct { - uint8_t vcp_ch; - PortIdx uart_ch; - uint32_t baudrate; -} UsbUartConfig; - -typedef struct { - UsbUartConfig cfg_cur; - UsbUartConfig cfg_set; - char br_text[8]; - - bool running; - osThreadId_t parent_thread; - - osThreadAttr_t thread_attr; - osThreadId_t thread; - - osThreadAttr_t tx_thread_attr; - osThreadId_t tx_thread; - - StreamBufferHandle_t rx_stream; - osSemaphoreId_t rx_done_sem; - osSemaphoreId_t usb_sof_sem; - - StreamBufferHandle_t tx_stream; - - uint8_t rx_buf[USB_PKT_LEN]; - uint8_t tx_buf[USB_PKT_LEN]; -} UsbUartParams; - -static UsbUartParams* usb_uart; +static UsbUartConfig* cfg_set; static const char* vcp_ch[] = {"0 (CLI)", "1"}; static const char* uart_ch[] = {"USART1", "LPUART1"}; @@ -73,197 +27,14 @@ static const uint32_t baudrate_list[] = { 921600, }; -static void vcp_on_cdc_tx_complete(); -static void vcp_on_cdc_rx(); -static void vcp_state_callback(uint8_t state); -static void vcp_on_cdc_control_line(uint8_t state); -static void vcp_on_line_config(struct usb_cdc_line_coding* config); - -static CdcCallbacks cdc_cb = { - vcp_on_cdc_tx_complete, - vcp_on_cdc_rx, - vcp_state_callback, - vcp_on_cdc_control_line, - vcp_on_line_config, -}; - -/* USB UART worker */ - -static void usb_uart_tx_thread(void* context); - -static void usb_uart_on_irq_cb(UartIrqEvent ev, uint8_t data) { - BaseType_t xHigherPriorityTaskWoken = pdFALSE; - - if(ev == UartIrqEventRXNE) { - size_t ret = - xStreamBufferSendFromISR(usb_uart->rx_stream, &data, 1, &xHigherPriorityTaskWoken); - furi_check(ret == 1); - ret = xStreamBufferBytesAvailable(usb_uart->rx_stream); - if(ret > USB_PKT_LEN) osSemaphoreRelease(usb_uart->rx_done_sem); - } else if(ev == UartIrqEventIDLE) { - osSemaphoreRelease(usb_uart->rx_done_sem); - } - - portYIELD_FROM_ISR(xHigherPriorityTaskWoken); -} - -static void usb_uart_worker(void* context) { - memcpy(&usb_uart->cfg_cur, &usb_uart->cfg_set, sizeof(UsbUartConfig)); - - usb_uart->rx_stream = xStreamBufferCreate(USB_UART_RX_BUF_SIZE, 1); - usb_uart->rx_done_sem = osSemaphoreNew(1, 1, NULL); - usb_uart->usb_sof_sem = osSemaphoreNew(1, 1, NULL); - - usb_uart->tx_stream = xStreamBufferCreate(USB_UART_TX_BUF_SIZE, 1); - - usb_uart->tx_thread = NULL; - usb_uart->tx_thread_attr.name = "usb_uart_tx"; - usb_uart->tx_thread_attr.stack_size = 512; - - UsbMode usb_mode_prev = furi_hal_usb_get_config(); - if(usb_uart->cfg_cur.vcp_ch == 0) { - furi_hal_usb_set_config(UsbModeVcpSingle); - furi_hal_vcp_disable(); - } else { - furi_hal_usb_set_config(UsbModeVcpDual); - } - - if(usb_uart->cfg_cur.uart_ch == UsbUartPortUSART1) { - furi_hal_usart_init(); - furi_hal_usart_set_irq_cb(usb_uart_on_irq_cb); - if(usb_uart->cfg_cur.baudrate != 0) - furi_hal_usart_set_br(usb_uart->cfg_cur.baudrate); - else - vcp_on_line_config(furi_hal_cdc_get_port_settings(usb_uart->cfg_cur.vcp_ch)); - } else if(usb_uart->cfg_cur.uart_ch == UsbUartPortLPUART1) { - furi_hal_lpuart_init(); - furi_hal_lpuart_set_irq_cb(usb_uart_on_irq_cb); - if(usb_uart->cfg_cur.baudrate != 0) - furi_hal_lpuart_set_br(usb_uart->cfg_cur.baudrate); - else - vcp_on_line_config(furi_hal_cdc_get_port_settings(usb_uart->cfg_cur.vcp_ch)); - } - - furi_hal_cdc_set_callbacks(usb_uart->cfg_cur.vcp_ch, &cdc_cb); - usb_uart->tx_thread = osThreadNew(usb_uart_tx_thread, NULL, &usb_uart->tx_thread_attr); - - while(1) { - furi_check(osSemaphoreAcquire(usb_uart->rx_done_sem, osWaitForever) == osOK); - if(osThreadFlagsWait(WorkerCmdStop, osFlagsWaitAny, 0) == WorkerCmdStop) break; - size_t len = 0; - do { - len = xStreamBufferReceive(usb_uart->rx_stream, usb_uart->rx_buf, USB_PKT_LEN, 0); - if(len > 0) { - if(osSemaphoreAcquire(usb_uart->usb_sof_sem, 100) == osOK) - furi_hal_cdc_send(usb_uart->cfg_cur.vcp_ch, usb_uart->rx_buf, len); - else - xStreamBufferReset(usb_uart->rx_stream); - } - } while(len > 0); - } - - osThreadTerminate(usb_uart->tx_thread); - - if(usb_uart->cfg_cur.uart_ch == UsbUartPortUSART1) - furi_hal_usart_deinit(); - else if(usb_uart->cfg_cur.uart_ch == UsbUartPortLPUART1) - furi_hal_lpuart_deinit(); - - furi_hal_cdc_set_callbacks(usb_uart->cfg_cur.vcp_ch, NULL); - furi_hal_usb_set_config(usb_mode_prev); - if(usb_uart->cfg_cur.vcp_ch == 0) furi_hal_vcp_enable(); - - vStreamBufferDelete(usb_uart->rx_stream); - osSemaphoreDelete(usb_uart->rx_done_sem); - osSemaphoreDelete(usb_uart->usb_sof_sem); - - vStreamBufferDelete(usb_uart->tx_stream); - osThreadFlagsSet(usb_uart->parent_thread, WorkerCmdStop); - osThreadExit(); -} - -static void usb_uart_tx_thread(void* context) { - uint8_t data = 0; - while(1) { - size_t len = xStreamBufferReceive(usb_uart->tx_stream, &data, 1, osWaitForever); - if(len > 0) { - if(usb_uart->cfg_cur.uart_ch == UsbUartPortUSART1) - furi_hal_usart_tx(&data, len); - else if(usb_uart->cfg_cur.uart_ch == UsbUartPortLPUART1) - furi_hal_lpuart_tx(&data, len); - } - } - osThreadExit(); -} - -/* VCP callbacks */ - -static void vcp_on_cdc_tx_complete() { - osSemaphoreRelease(usb_uart->usb_sof_sem); -} - -static void vcp_on_cdc_rx() { - BaseType_t xHigherPriorityTaskWoken = pdFALSE; - - uint16_t max_len = xStreamBufferSpacesAvailable(usb_uart->tx_stream); - if(max_len > 0) { - if(max_len > USB_PKT_LEN) max_len = USB_PKT_LEN; - int32_t size = furi_hal_cdc_receive(usb_uart->cfg_cur.vcp_ch, usb_uart->tx_buf, max_len); - - if(size > 0) { - size_t ret = xStreamBufferSendFromISR( - usb_uart->tx_stream, usb_uart->tx_buf, size, &xHigherPriorityTaskWoken); - furi_check(ret == size); - } - } - portYIELD_FROM_ISR(xHigherPriorityTaskWoken); -} - -static void vcp_state_callback(uint8_t state) { -} - -static void vcp_on_cdc_control_line(uint8_t state) { -} - -static void vcp_on_line_config(struct usb_cdc_line_coding* config) { - if((usb_uart->cfg_cur.baudrate == 0) && (config->dwDTERate != 0)) { - if(usb_uart->cfg_cur.uart_ch == UsbUartPortUSART1) - furi_hal_usart_set_br(config->dwDTERate); - else if(usb_uart->cfg_cur.uart_ch == UsbUartPortLPUART1) - furi_hal_lpuart_set_br(config->dwDTERate); - } -} - -/* USB UART app */ - -static void usb_uart_enable() { - if(usb_uart->running == false) { - usb_uart->thread = NULL; - usb_uart->thread_attr.name = "usb_uart"; - usb_uart->thread_attr.stack_size = 1024; - usb_uart->parent_thread = osThreadGetId(); - usb_uart->running = true; - usb_uart->thread = osThreadNew(usb_uart_worker, NULL, &usb_uart->thread_attr); - } -} - -static void usb_uart_disable() { - if(usb_uart->running == true) { - osThreadFlagsSet(usb_uart->thread, WorkerCmdStop); - osSemaphoreRelease(usb_uart->rx_done_sem); - osThreadFlagsWait(WorkerCmdStop, osFlagsWaitAny, osWaitForever); - usb_uart->running = false; - } -} - bool gpio_scene_usb_uart_on_event(void* context, SceneManagerEvent event) { //GpioApp* app = context; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { - if(event.event == UsbUartLineIndexEnable) { - usb_uart_enable(); - } else if(event.event == UsbUartLineIndexDisable) { + if(event.event == GPIO_SCENE_USB_UART_CUSTOM_EVENT_ENABLE) { + usb_uart_enable(cfg_set); + } else if(event.event == GPIO_SCENE_USB_UART_CUSTOM_EVENT_DISABLE) { usb_uart_disable(); } consumed = true; @@ -271,15 +42,13 @@ bool gpio_scene_usb_uart_on_event(void* context, SceneManagerEvent event) { return consumed; } -/* Scene callbacks */ - static void line_vcp_cb(VariableItem* item) { //GpioApp* app = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, vcp_ch[index]); - usb_uart->cfg_set.vcp_ch = index; + cfg_set->vcp_ch = index; } static void line_port_cb(VariableItem* item) { @@ -288,34 +57,44 @@ static void line_port_cb(VariableItem* item) { variable_item_set_current_value_text(item, uart_ch[index]); - usb_uart->cfg_set.uart_ch = index; + if(index == 0) + cfg_set->uart_ch = FuriHalUartIdUSART1; + else if(index == 1) + cfg_set->uart_ch = FuriHalUartIdLPUART1; } static void line_baudrate_cb(VariableItem* item) { //GpioApp* app = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); + char br_text[8]; + if(index > 0) { - snprintf(usb_uart->br_text, 7, "%lu", baudrate_list[index - 1]); - variable_item_set_current_value_text(item, usb_uart->br_text); - usb_uart->cfg_set.baudrate = baudrate_list[index - 1]; + snprintf(br_text, 7, "%lu", baudrate_list[index - 1]); + variable_item_set_current_value_text(item, br_text); + cfg_set->baudrate = baudrate_list[index - 1]; } else { variable_item_set_current_value_text(item, baudrate_mode[index]); - usb_uart->cfg_set.baudrate = 0; + cfg_set->baudrate = 0; } } static void gpio_scene_usb_uart_enter_callback(void* context, uint32_t index) { furi_assert(context); GpioApp* app = context; - view_dispatcher_send_custom_event(app->view_dispatcher, index); + if(index == UsbUartLineIndexEnable) + view_dispatcher_send_custom_event( + app->view_dispatcher, GPIO_SCENE_USB_UART_CUSTOM_EVENT_ENABLE); + else if(index == UsbUartLineIndexDisable) + view_dispatcher_send_custom_event( + app->view_dispatcher, GPIO_SCENE_USB_UART_CUSTOM_EVENT_DISABLE); } void gpio_scene_usb_uart_on_enter(void* context) { GpioApp* app = context; VariableItemList* var_item_list = app->var_item_list; - usb_uart = furi_alloc(sizeof(UsbUartParams)); + cfg_set = furi_alloc(sizeof(UsbUartConfig)); VariableItem* item; @@ -348,5 +127,5 @@ void gpio_scene_usb_uart_on_exit(void* context) { GpioApp* app = context; usb_uart_disable(); variable_item_list_clean(app->var_item_list); - free(usb_uart); -} \ No newline at end of file + free(cfg_set); +} diff --git a/applications/gpio/usb_uart_bridge.c b/applications/gpio/usb_uart_bridge.c new file mode 100644 index 00000000..b50cc158 --- /dev/null +++ b/applications/gpio/usb_uart_bridge.c @@ -0,0 +1,246 @@ +#include "usb_uart_bridge.h" +#include "furi-hal.h" +#include +#include +#include "usb_cdc.h" + +#define USB_PKT_LEN CDC_DATA_SZ +#define USB_UART_RX_BUF_SIZE (USB_PKT_LEN * 3) +#define USB_UART_TX_BUF_SIZE (USB_PKT_LEN * 3) + +typedef enum { + WorkerEvtStop = (1 << 0), + WorkerEvtRxReady = (1 << 1), + + WorkerEvtTxStop = (1 << 2), + WorkerEvtTxReady = (1 << 3), + + WorkerEvtSof = (1 << 4), + +} WorkerEvtFlags; + +#define WORKER_ALL_RX_EVENTS (WorkerEvtStop | WorkerEvtRxReady) +#define WORKER_ALL_TX_EVENTS (WorkerEvtTxStop | WorkerEvtTxReady) + +typedef struct { + UsbUartConfig cfg; + + FuriThread* thread; + FuriThread* tx_thread; + + osEventFlagsId_t events; + + StreamBufferHandle_t rx_stream; + StreamBufferHandle_t tx_stream; + + uint8_t rx_buf[USB_PKT_LEN]; + uint8_t tx_buf[USB_PKT_LEN]; + + bool buf_full; +} UsbUartParams; + +static UsbUartParams* usb_uart; +static bool running = false; + +static void vcp_on_cdc_tx_complete(); +static void vcp_on_cdc_rx(); +static void vcp_state_callback(uint8_t state); +static void vcp_on_cdc_control_line(uint8_t state); +static void vcp_on_line_config(struct usb_cdc_line_coding* config); + +static CdcCallbacks cdc_cb = { + vcp_on_cdc_tx_complete, + vcp_on_cdc_rx, + vcp_state_callback, + vcp_on_cdc_control_line, + vcp_on_line_config, +}; + +/* USB UART worker */ + +static int32_t usb_uart_tx_thread(void* context); + +static void usb_uart_on_irq_cb(UartIrqEvent ev, uint8_t data) { + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + + if(ev == UartIrqEventRXNE) { + xStreamBufferSendFromISR(usb_uart->rx_stream, &data, 1, &xHigherPriorityTaskWoken); + + size_t ret = xStreamBufferBytesAvailable(usb_uart->rx_stream); + if(ret > USB_PKT_LEN) osEventFlagsSet(usb_uart->events, WorkerEvtRxReady); + } else if(ev == UartIrqEventIDLE) { + osEventFlagsSet(usb_uart->events, WorkerEvtRxReady); + } + + portYIELD_FROM_ISR(xHigherPriorityTaskWoken); +} + +static int32_t usb_uart_worker(void* context) { + memcpy(&usb_uart->cfg, context, sizeof(UsbUartConfig)); + + usb_uart->rx_stream = xStreamBufferCreate(USB_UART_RX_BUF_SIZE, 1); + usb_uart->tx_stream = xStreamBufferCreate(USB_UART_TX_BUF_SIZE, 1); + + usb_uart->tx_thread = furi_thread_alloc(); + furi_thread_set_name(usb_uart->tx_thread, "usb_uart_tx"); + furi_thread_set_stack_size(usb_uart->tx_thread, 512); + furi_thread_set_context(usb_uart->tx_thread, NULL); + furi_thread_set_callback(usb_uart->tx_thread, usb_uart_tx_thread); + + UsbMode usb_mode_prev = furi_hal_usb_get_config(); + if(usb_uart->cfg.vcp_ch == 0) { + furi_hal_usb_set_config(UsbModeVcpSingle); + furi_hal_vcp_disable(); + osEventFlagsSet(usb_uart->events, WorkerEvtSof); + } else { + furi_hal_usb_set_config(UsbModeVcpDual); + } + + if(usb_uart->cfg.uart_ch == FuriHalUartIdUSART1) { + furi_hal_console_disable(); + } else if(usb_uart->cfg.uart_ch == FuriHalUartIdLPUART1) { + furi_hal_uart_init(usb_uart->cfg.uart_ch, 115200); + furi_hal_uart_set_irq_cb(usb_uart->cfg.uart_ch, usb_uart_on_irq_cb); + } + + furi_hal_uart_set_irq_cb(usb_uart->cfg.uart_ch, usb_uart_on_irq_cb); + if(usb_uart->cfg.baudrate != 0) + furi_hal_uart_set_br(usb_uart->cfg.uart_ch, usb_uart->cfg.baudrate); + else + vcp_on_line_config(furi_hal_cdc_get_port_settings(usb_uart->cfg.vcp_ch)); + + furi_hal_cdc_set_callbacks(usb_uart->cfg.vcp_ch, &cdc_cb); + + furi_thread_start(usb_uart->tx_thread); + + while(1) { + uint32_t events = osEventFlagsWait( + usb_uart->events, WORKER_ALL_RX_EVENTS, osFlagsWaitAny, osWaitForever); + furi_check((events & osFlagsError) == 0); + if(events & WorkerEvtStop) break; + if(events & WorkerEvtRxReady) { + size_t len = 0; + do { + len = xStreamBufferReceive(usb_uart->rx_stream, usb_uart->rx_buf, USB_PKT_LEN, 0); + if(len > 0) { + if((osEventFlagsWait(usb_uart->events, WorkerEvtSof, osFlagsWaitAny, 100) & + osFlagsError) == 0) + furi_hal_cdc_send(usb_uart->cfg.vcp_ch, usb_uart->rx_buf, len); + else + xStreamBufferReset(usb_uart->rx_stream); + } + } while(len > 0); + } + } + + osEventFlagsSet(usb_uart->events, WorkerEvtTxStop); + furi_thread_join(usb_uart->tx_thread); + furi_thread_free(usb_uart->tx_thread); + + if(usb_uart->cfg.uart_ch == FuriHalUartIdUSART1) + furi_hal_console_enable(); + else if(usb_uart->cfg.uart_ch == FuriHalUartIdLPUART1) + furi_hal_uart_deinit(usb_uart->cfg.uart_ch); + + furi_hal_cdc_set_callbacks(usb_uart->cfg.vcp_ch, NULL); + furi_hal_usb_set_config(usb_mode_prev); + if(usb_uart->cfg.vcp_ch == 0) furi_hal_vcp_enable(); + + vStreamBufferDelete(usb_uart->rx_stream); + vStreamBufferDelete(usb_uart->tx_stream); + + return 0; +} + +static int32_t usb_uart_tx_thread(void* context) { + uint8_t data[USB_PKT_LEN]; + while(1) { + uint32_t events = osEventFlagsWait( + usb_uart->events, WORKER_ALL_TX_EVENTS, osFlagsWaitAny, osWaitForever); + furi_check((events & osFlagsError) == 0); + if(events & WorkerEvtTxStop) break; + if(events & WorkerEvtTxReady) { + size_t len = 0; + do { + len = xStreamBufferReceive(usb_uart->tx_stream, &data, 1, 0); + if(len > 0) { + furi_hal_uart_tx(usb_uart->cfg.uart_ch, data, len); + } + if((usb_uart->buf_full == true) && + (xStreamBufferBytesAvailable(usb_uart->tx_stream) == 0)) { + // Stream buffer was overflown, but now is free. Reading USB buffer to resume USB transfers + usb_uart->buf_full = false; + int32_t size = furi_hal_cdc_receive(usb_uart->cfg.vcp_ch, data, USB_PKT_LEN); + if(size > 0) { + furi_hal_uart_tx(usb_uart->cfg.uart_ch, data, size); + } + } + } while(len > 0); + } + } + return 0; +} + +/* VCP callbacks */ + +static void vcp_on_cdc_tx_complete() { + osEventFlagsSet(usb_uart->events, WorkerEvtSof); +} + +static void vcp_on_cdc_rx() { + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + + uint16_t max_len = xStreamBufferSpacesAvailable(usb_uart->tx_stream); + if(max_len >= USB_PKT_LEN) { + //if(max_len > USB_PKT_LEN) max_len = USB_PKT_LEN; + int32_t size = furi_hal_cdc_receive(usb_uart->cfg.vcp_ch, usb_uart->tx_buf, USB_PKT_LEN); + if(size > 0) { + size_t ret = xStreamBufferSendFromISR( + usb_uart->tx_stream, usb_uart->tx_buf, size, &xHigherPriorityTaskWoken); + furi_check(ret == size); + } + } else { + usb_uart->buf_full = true; + } + osEventFlagsSet(usb_uart->events, WorkerEvtTxReady); + portYIELD_FROM_ISR(xHigherPriorityTaskWoken); +} + +static void vcp_state_callback(uint8_t state) { +} + +static void vcp_on_cdc_control_line(uint8_t state) { +} + +static void vcp_on_line_config(struct usb_cdc_line_coding* config) { + if((usb_uart->cfg.baudrate == 0) && (config->dwDTERate != 0)) + furi_hal_uart_set_br(usb_uart->cfg.uart_ch, config->dwDTERate); +} + +void usb_uart_enable(UsbUartConfig* cfg) { + if(running == false) { + running = true; + usb_uart = furi_alloc(sizeof(UsbUartParams)); + + usb_uart->thread = furi_thread_alloc(); + furi_thread_set_name(usb_uart->thread, "usb_uart"); + furi_thread_set_stack_size(usb_uart->thread, 1024); + furi_thread_set_context(usb_uart->thread, cfg); + furi_thread_set_callback(usb_uart->thread, usb_uart_worker); + + usb_uart->events = osEventFlagsNew(NULL); + + furi_thread_start(usb_uart->thread); + } +} + +void usb_uart_disable() { + if(running == true) { + osEventFlagsSet(usb_uart->events, WorkerEvtStop); + furi_thread_join(usb_uart->thread); + furi_thread_free(usb_uart->thread); + osEventFlagsDelete(usb_uart->events); + free(usb_uart); + running = false; + } +} diff --git a/applications/gpio/usb_uart_bridge.h b/applications/gpio/usb_uart_bridge.h new file mode 100644 index 00000000..2fe6d1d8 --- /dev/null +++ b/applications/gpio/usb_uart_bridge.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +typedef struct { + uint8_t vcp_ch; + uint8_t uart_ch; + uint32_t baudrate; +} UsbUartConfig; + +void usb_uart_enable(UsbUartConfig* cfg); + +void usb_uart_disable(); diff --git a/applications/gui/canvas.c b/applications/gui/canvas.c old mode 100755 new mode 100644 index 1a2bb06f..5ab42875 --- a/applications/gui/canvas.c +++ b/applications/gui/canvas.c @@ -6,22 +6,32 @@ #include #include +const CanvasFontParameters canvas_font_params[FontTotalNumber] = { + [FontPrimary] = {.leading_default = 12, .leading_min = 11, .height = 8, .descender = 2}, + [FontSecondary] = {.leading_default = 11, .leading_min = 9, .height = 7, .descender = 2}, + [FontKeyboard] = {.leading_default = 11, .leading_min = 9, .height = 7, .descender = 2}, + [FontBigNumbers] = {.leading_default = 18, .leading_min = 16, .height = 15, .descender = 0}, +}; + Canvas* canvas_init() { Canvas* canvas = furi_alloc(sizeof(Canvas)); furi_hal_power_insomnia_enter(); - canvas->orientation = CanvasOrientationHorizontal; + // Setup u8g2 u8g2_Setup_st756x_flipper(&canvas->fb, U8G2_R0, u8x8_hw_spi_stm32, u8g2_gpio_and_delay_stm32); - - // send init sequence to the display, display is in sleep mode after this + canvas->orientation = CanvasOrientationHorizontal; + // Initialize display u8g2_InitDisplay(&canvas->fb); - // wake up display - u8g2_ClearBuffer(&canvas->fb); + // Wake up display u8g2_SetPowerSave(&canvas->fb, 0); - u8g2_SendBuffer(&canvas->fb); + + // Clear buffer and send to device + canvas_clear(canvas); + canvas_commit(canvas); furi_hal_power_insomnia_exit(); + return canvas; } @@ -32,9 +42,12 @@ void canvas_free(Canvas* canvas) { void canvas_reset(Canvas* canvas) { furi_assert(canvas); + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); canvas_set_font(canvas, FontSecondary); + canvas_set_font_direction(canvas, CanvasFontDirectionLeftToRight); } void canvas_commit(Canvas* canvas) { @@ -86,6 +99,12 @@ uint8_t canvas_current_font_height(Canvas* canvas) { return font_height; } +CanvasFontParameters* canvas_get_font_params(Canvas* canvas, Font font) { + furi_assert(canvas); + furi_assert(font < FontTotalNumber); + return (CanvasFontParameters*)&canvas_font_params[font]; +} + void canvas_clear(Canvas* canvas) { furi_assert(canvas); u8g2_ClearBuffer(&canvas->fb); @@ -96,6 +115,11 @@ void canvas_set_color(Canvas* canvas, Color color) { u8g2_SetDrawColor(&canvas->fb, color); } +void canvas_set_font_direction(Canvas* canvas, CanvasFontDirection dir) { + furi_assert(canvas); + u8g2_SetFontDirection(&canvas->fb, dir); +} + void canvas_invert_color(Canvas* canvas) { canvas->fb.draw_color = !canvas->fb.draw_color; } diff --git a/applications/gui/canvas.h b/applications/gui/canvas.h index cbf9d068..b197e928 100644 --- a/applications/gui/canvas.h +++ b/applications/gui/canvas.h @@ -20,7 +20,15 @@ typedef enum { } Color; /** Fonts enumeration */ -typedef enum { FontPrimary, FontSecondary, FontKeyboard, FontBigNumbers } Font; +typedef enum { + FontPrimary, + FontSecondary, + FontKeyboard, + FontBigNumbers, + + // Keep last for fonts number calculation + FontTotalNumber, +} Font; /** Alignment enumeration */ typedef enum { @@ -37,6 +45,22 @@ typedef enum { CanvasOrientationVertical, } CanvasOrientation; +/** Font Direction */ +typedef enum { + CanvasFontDirectionLeftToRight, + CanvasFontDirectionTopToDown, + CanvasFontDirectionRightToLeft, + CanvasFontDirectionDownToTop, +} CanvasFontDirection; + +/** Font parameters */ +typedef struct { + uint8_t leading_default; + uint8_t leading_min; + uint8_t height; + uint8_t descender; +} CanvasFontParameters; + /** Canvas anonymouse structure */ typedef struct Canvas Canvas; @@ -64,6 +88,15 @@ uint8_t canvas_height(Canvas* canvas); */ uint8_t canvas_current_font_height(Canvas* canvas); +/** Get font parameters + * + * @param canvas Canvas instance + * @param font Font + * + * @return pointer to CanvasFontParameters structure + */ +CanvasFontParameters* canvas_get_font_params(Canvas* canvas, Font font); + /** Clear canvas * * @param canvas Canvas instance @@ -77,6 +110,14 @@ void canvas_clear(Canvas* canvas); */ void canvas_set_color(Canvas* canvas, Color color); +/** Set font swap + * Argument String Rotation Description + * + * @param canvas Canvas instance + * @param dir Direction font + */ +void canvas_set_font_direction(Canvas* canvas, CanvasFontDirection dir); + /** Invert drawing color * * @param canvas Canvas instance diff --git a/applications/gui/elements.c b/applications/gui/elements.c old mode 100644 new mode 100755 index 4a577780..0a03ed29 --- a/applications/gui/elements.c +++ b/applications/gui/elements.c @@ -10,6 +10,18 @@ #include #include +#include + +typedef struct { + uint8_t x; + uint8_t y; + uint8_t leading_min; + uint8_t leading_default; + uint8_t height; + uint8_t descender; + uint8_t len; + const char* text; +} ElementTextBoxLine; void elements_progress_bar( Canvas* canvas, @@ -352,3 +364,206 @@ void elements_string_fit_width(Canvas* canvas, string_t string, uint8_t width) { string_cat(string, "..."); } } + +void elements_text_box( + Canvas* canvas, + uint8_t x, + uint8_t y, + uint8_t width, + uint8_t height, + Align horizontal, + Align vertical, + const char* text) { + furi_assert(canvas); + + ElementTextBoxLine line[ELEMENTS_MAX_LINES_NUM]; + bool bold = false; + bool mono = false; + bool inversed = false; + bool inversed_present = false; + Font current_font = FontSecondary; + Font prev_font = FontSecondary; + CanvasFontParameters* font_params = canvas_get_font_params(canvas, current_font); + + // Fill line parameters + uint8_t line_leading_min = font_params->leading_min; + uint8_t line_leading_default = font_params->leading_default; + uint8_t line_height = font_params->height; + uint8_t line_descender = font_params->descender; + uint8_t line_num = 0; + uint8_t line_width = 0; + uint8_t line_len = 0; + uint8_t total_height_min = 0; + uint8_t total_height_default = 0; + uint16_t i = 0; + bool full_text_processed = false; + + canvas_set_font(canvas, FontSecondary); + + // Fill all lines + line[0].text = text; + for(i = 0; !full_text_processed; i++) { + line_len++; + // Identify line height + if(prev_font != current_font) { + font_params = canvas_get_font_params(canvas, current_font); + line_leading_min = MAX(line_leading_min, font_params->leading_min); + line_leading_default = MAX(line_leading_default, font_params->leading_default); + line_height = MAX(line_height, font_params->height); + line_descender = MAX(line_descender, font_params->descender); + prev_font = current_font; + } + // Set the font + if(text[i] == '\e' && text[i + 1]) { + i++; + line_len++; + if(text[i] == ELEMENTS_BOLD_MARKER) { + if(bold) { + current_font = FontSecondary; + } else { + current_font = FontPrimary; + } + canvas_set_font(canvas, current_font); + bold = !bold; + } + if(text[i] == ELEMENTS_MONO_MARKER) { + if(mono) { + current_font = FontSecondary; + } else { + current_font = FontKeyboard; + } + canvas_set_font(canvas, FontKeyboard); + mono = !mono; + } + if(text[i] == ELEMENTS_INVERSED_MARKER) { + inversed_present = true; + } + continue; + } + if(text[i] != '\n') { + line_width += canvas_glyph_width(canvas, text[i]); + } + // Process new line + if(text[i] == '\n' || text[i] == '\0' || line_width > width) { + if(line_width > width) { + line_width -= canvas_glyph_width(canvas, text[i--]); + line_len--; + } + if(text[i] == '\0') { + full_text_processed = true; + } + if(inversed_present) { + line_leading_min += 1; + line_leading_default += 1; + inversed_present = false; + } + line[line_num].leading_min = line_leading_min; + line[line_num].leading_default = line_leading_default; + line[line_num].height = line_height; + line[line_num].descender = line_descender; + if(total_height_min + line_leading_min > height) { + line_num--; + break; + } + total_height_min += line_leading_min; + total_height_default += line_leading_default; + line[line_num].len = line_len; + if(horizontal == AlignCenter) { + line[line_num].x = x + (width - line_width) / 2; + } else if(horizontal == AlignRight) { + line[line_num].x = x + (width - line_width); + } else { + line[line_num].x = x; + } + line[line_num].y = total_height_min; + line_num++; + if(text[i + 1]) { + line[line_num].text = &text[i + 1]; + } + + line_leading_min = font_params->leading_min; + line_height = font_params->height; + line_descender = font_params->descender; + line_width = 0; + line_len = 0; + } + } + + // Set vertical alignment for all lines + if(full_text_processed) { + if(total_height_default < height) { + if(vertical == AlignTop) { + line[0].y = y + line[0].height; + } else if(vertical == AlignCenter) { + line[0].y = y + line[0].height + (height - total_height_default) / 2; + } else if(vertical == AlignBottom) { + line[0].y = y + line[0].height + (height - total_height_default); + } + if(line_num > 1) { + for(uint8_t i = 1; i < line_num; i++) { + line[i].y = line[i - 1].y + line[i - 1].leading_default; + } + } + } else if(line_num > 1) { + uint8_t free_pixel_num = height - total_height_min; + uint8_t fill_pixel = 0; + uint8_t j = 1; + line[0].y = line[0].height; + while(fill_pixel < free_pixel_num) { + line[j].y = line[j - 1].y + line[j - 1].leading_min + 1; + fill_pixel++; + j = j % (line_num - 1) + 1; + } + } + } + + // Draw line by line + canvas_set_font(canvas, FontSecondary); + bold = false; + mono = false; + inversed = false; + for(uint8_t i = 0; i < line_num; i++) { + for(uint8_t j = 0; j < line[i].len; j++) { + // Process format symbols + if(line[i].text[j] == ELEMENTS_BOLD_MARKER) { + if(bold) { + current_font = FontSecondary; + } else { + current_font = FontPrimary; + } + canvas_set_font(canvas, current_font); + bold = !bold; + continue; + } + if(line[i].text[j] == ELEMENTS_MONO_MARKER) { + if(mono) { + current_font = FontSecondary; + } else { + current_font = FontKeyboard; + } + canvas_set_font(canvas, current_font); + mono = !mono; + continue; + } + if(line[i].text[j] == ELEMENTS_INVERSED_MARKER) { + inversed = !inversed; + continue; + } + if(inversed) { + canvas_draw_box( + canvas, + line[i].x - 1, + line[i].y - line[i].height - 1, + canvas_glyph_width(canvas, line[i].text[j]) + 1, + line[i].height + line[i].descender + 2); + canvas_invert_color(canvas); + canvas_draw_glyph(canvas, line[i].x, line[i].y, line[i].text[j]); + canvas_invert_color(canvas); + } else { + canvas_draw_glyph(canvas, line[i].x, line[i].y, line[i].text[j]); + } + line[i].x += canvas_glyph_width(canvas, line[i].text[j]); + } + } + canvas_set_font(canvas, FontSecondary); +} diff --git a/applications/gui/elements.h b/applications/gui/elements.h old mode 100644 new mode 100755 index 32156dc2..52b26864 --- a/applications/gui/elements.h +++ b/applications/gui/elements.h @@ -16,12 +16,19 @@ extern "C" { #endif +#define ELEMENTS_MAX_LINES_NUM (7) +#define ELEMENTS_BOLD_MARKER '#' +#define ELEMENTS_MONO_MARKER '*' +#define ELEMENTS_INVERSED_MARKER '!' + /** Draw progress bar. - * @param x - progress bar position on X axis - * @param y - progress bar position on Y axis - * @param width - progress bar width - * @param progress - progress in unnamed metric - * @param total - total amount in unnamed metric + * + * @param canvas Canvas instance + * @param x progress bar position on X axis + * @param y progress bar position on Y axis + * @param width progress bar width + * @param progress progress in unnamed metric + * @param total total amount in unnamed metric */ void elements_progress_bar( Canvas* canvas, @@ -32,11 +39,13 @@ void elements_progress_bar( uint8_t total); /** Draw scrollbar on canvas at specific position. - * @param x - scrollbar position on X axis - * @param y - scrollbar position on Y axis - * @param height - scrollbar height - * @param pos - current element - * @param total - total elements + * + * @param canvas Canvas instance + * @param x scrollbar position on X axis + * @param y scrollbar position on Y axis + * @param height scrollbar height + * @param pos current element + * @param total total elements */ void elements_scrollbar_pos( Canvas* canvas, @@ -47,37 +56,49 @@ void elements_scrollbar_pos( uint16_t total); /** Draw scrollbar on canvas. - * width 3px, height equal to canvas height - * @param pos - current element of total elements - * @param total - total elements + * @note width 3px, height equal to canvas height + * + * @param canvas Canvas instance + * @param pos current element of total elements + * @param total total elements */ void elements_scrollbar(Canvas* canvas, uint16_t pos, uint16_t total); /** Draw rounded frame - * @param x, y - top left corner coordinates - * @param width, height - frame width and height + * + * @param canvas Canvas instance + * @param x, y top left corner coordinates + * @param width, height frame width and height */ void elements_frame(Canvas* canvas, uint8_t x, uint8_t y, uint8_t width, uint8_t height); /** Draw button in left corner - * @param str - button text + * + * @param canvas Canvas instance + * @param str button text */ void elements_button_left(Canvas* canvas, const char* str); /** Draw button in right corner - * @param str - button text + * + * @param canvas Canvas instance + * @param str button text */ void elements_button_right(Canvas* canvas, const char* str); /** Draw button in center - * @param str - button text + * + * @param canvas Canvas instance + * @param str button text */ void elements_button_center(Canvas* canvas, const char* str); /** Draw aligned multiline text - * @param x, y - coordinates based on align param - * @param horizontal, vertical - aligment of multiline text - * @param text - string (possible multiline) + * + * @param canvas Canvas instance + * @param x, y coordinates based on align param + * @param horizontal, vertical aligment of multiline text + * @param text string (possible multiline) */ void elements_multiline_text_aligned( Canvas* canvas, @@ -88,20 +109,26 @@ void elements_multiline_text_aligned( const char* text); /** Draw multiline text - * @param x, y - top left corner coordinates - * @param text - string (possible multiline) + * + * @param canvas Canvas instance + * @param x, y top left corner coordinates + * @param text string (possible multiline) */ void elements_multiline_text(Canvas* canvas, uint8_t x, uint8_t y, const char* text); /** Draw framed multiline text - * @param x, y - top left corner coordinates - * @param text - string (possible multiline) + * + * @param canvas Canvas instance + * @param x, y top left corner coordinates + * @param text string (possible multiline) */ void elements_multiline_text_framed(Canvas* canvas, uint8_t x, uint8_t y, const char* text); /** Draw slightly rounded frame - * @param x, y - top left corner coordinates - * @param width, height - size of frame + * + * @param canvas Canvas instance + * @param x, y top left corner coordinates + * @param width, height size of frame */ void elements_slightly_rounded_frame( Canvas* canvas, @@ -111,8 +138,10 @@ void elements_slightly_rounded_frame( uint8_t height); /** Draw slightly rounded box - * @param x, y - top left corner coordinates - * @param width, height - size of box + * + * @param canvas Canvas instance + * @param x, y top left corner coordinates + * @param width, height size of box */ void elements_slightly_rounded_box( Canvas* canvas, @@ -122,19 +151,47 @@ void elements_slightly_rounded_box( uint8_t height); /** Draw bubble frame for text - * @param x - left x coordinates - * @param y - top y coordinate - * @param width - bubble width - * @param height - bubble height + * + * @param canvas Canvas instance + * @param x left x coordinates + * @param y top y coordinate + * @param width bubble width + * @param height bubble height */ void elements_bubble(Canvas* canvas, uint8_t x, uint8_t y, uint8_t width, uint8_t height); /** Trim string buffer to fit width in pixels - * @param string - string to trim - * @param width - max width + * + * @param canvas Canvas instance + * @param string string to trim + * @param width max width */ void elements_string_fit_width(Canvas* canvas, string_t string, uint8_t width); +/** Draw text box element + * + * @param canvas Canvas instance + * @param x x coordinate + * @param y y coordinate + * @param width width to fit text + * @param height height to fit text + * @param horizontal Align instance + * @param vertical Align instance + * @param[in] text Formatted text. The following formats are available: + * "\e#Bold text\e#" - bold font is used + * "\e*Monospaced text\e*" - monospaced font is used + * "\e#Inversed text\e#" - white text on black background + */ +void elements_text_box( + Canvas* canvas, + uint8_t x, + uint8_t y, + uint8_t width, + uint8_t height, + Align horizontal, + Align vertical, + const char* text); + #ifdef __cplusplus } #endif diff --git a/applications/gui/modules/code_input.c b/applications/gui/modules/code_input.c new file mode 100644 index 00000000..62f5118d --- /dev/null +++ b/applications/gui/modules/code_input.c @@ -0,0 +1,475 @@ +#include "code_input.h" +#include +#include + +#define MAX_CODE_LEN 10 + +struct CodeInput { + View* view; +}; + +typedef enum { + CodeInputStateVerify, + CodeInputStateUpdate, + CodeInputStateTotal, +} CodeInputStateEnum; + +typedef enum { + CodeInputFirst, + CodeInputSecond, + CodeInputTotal, +} CodeInputsEnum; + +typedef struct { + uint8_t state; + uint8_t current; + bool ext_update; + + uint8_t input_length[CodeInputTotal]; + uint8_t local_buffer[CodeInputTotal][MAX_CODE_LEN]; + + CodeInputOkCallback ok_callback; + CodeInputFailCallback fail_callback; + void* callback_context; + + const char* header; + + uint8_t* ext_buffer; + uint8_t* ext_buffer_length; +} CodeInputModel; + +static const Icon* keys_assets[] = { + [InputKeyUp] = &I_ButtonUp_7x4, + [InputKeyDown] = &I_ButtonDown_7x4, + [InputKeyRight] = &I_ButtonRight_4x7, + [InputKeyLeft] = &I_ButtonLeft_4x7, +}; + +/** + * @brief Compare buffers + * + * @param in Input buffer pointer + * @param len_in Input array length + * @param src Source buffer pointer + * @param len_src Source array length + */ + +bool code_input_compare(uint8_t* in, size_t len_in, uint8_t* src, size_t len_src) { + bool result = false; + do { + result = (len_in && len_src); + if(!result) { + break; + } + result = (len_in == len_src); + if(!result) { + break; + } + for(size_t i = 0; i < len_in; i++) { + result = (in[i] == src[i]); + if(!result) { + break; + } + } + } while(false); + + return result; +} + +/** + * @brief Compare local buffers + * + * @param model + */ +static bool code_input_compare_local(CodeInputModel* model) { + uint8_t* source = model->local_buffer[CodeInputFirst]; + size_t source_length = model->input_length[CodeInputFirst]; + + uint8_t* input = model->local_buffer[CodeInputSecond]; + size_t input_length = model->input_length[CodeInputSecond]; + + return code_input_compare(input, input_length, source, source_length); +} + +/** + * @brief Compare ext with local + * + * @param model + */ +static bool code_input_compare_ext(CodeInputModel* model) { + uint8_t* input = model->local_buffer[CodeInputFirst]; + size_t input_length = model->input_length[CodeInputFirst]; + + uint8_t* source = model->ext_buffer; + size_t source_length = *model->ext_buffer_length; + + return code_input_compare(input, input_length, source, source_length); +} + +/** + * @brief Set ext buffer + * + * @param model + */ +static void code_input_set_ext(CodeInputModel* model) { + *model->ext_buffer_length = model->input_length[CodeInputFirst]; + for(size_t i = 0; i <= model->input_length[CodeInputFirst]; i++) { + model->ext_buffer[i] = model->local_buffer[CodeInputFirst][i]; + } +} + +/** + * @brief Draw input sequence + * + * @param canvas + * @param buffer + * @param length + * @param x + * @param y + * @param active + */ +static void code_input_draw_sequence( + Canvas* canvas, + uint8_t* buffer, + uint8_t length, + uint8_t x, + uint8_t y, + bool active) { + uint8_t pos_x = x + 6; + uint8_t pos_y = y + 3; + + if(active) canvas_draw_icon(canvas, x - 4, y + 5, &I_ButtonRightSmall_3x5); + + elements_slightly_rounded_frame(canvas, x, y, 116, 15); + + for(size_t i = 0; i < length; i++) { + // maybe symmetrical assets? :-/ + uint8_t offset_y = buffer[i] < 2 ? 2 + (buffer[i] * 2) : 1; + canvas_draw_icon(canvas, pos_x, pos_y + offset_y, keys_assets[buffer[i]]); + pos_x += buffer[i] > 1 ? 9 : 11; + } +} + +/** + * @brief Reset input count + * + * @param model + */ +static void code_input_reset_count(CodeInputModel* model) { + model->input_length[model->current] = 0; +} + +/** + * @brief Call input callback + * + * @param model + */ +static void code_input_call_ok_callback(CodeInputModel* model) { + if(model->ok_callback != NULL) { + model->ok_callback(model->callback_context); + } +} + +/** + * @brief Call changed callback + * + * @param model + */ +static void code_input_call_fail_callback(CodeInputModel* model) { + if(model->fail_callback != NULL) { + model->fail_callback(model->callback_context); + } +} + +/** + * @brief Handle Back button + * + * @param model + */ +static bool code_input_handle_back(CodeInputModel* model) { + if(model->current && !model->input_length[model->current]) { + --model->current; + return true; + } + + if(model->input_length[model->current]) { + code_input_reset_count(model); + return true; + } + + code_input_call_fail_callback(model); + return false; +} + +/** + * @brief Handle OK button + * + * @param model + */ +static void code_input_handle_ok(CodeInputModel* model) { + switch(model->state) { + case CodeInputStateVerify: + + if(code_input_compare_ext(model)) { + if(model->ext_update) { + model->state = CodeInputStateUpdate; + } else { + code_input_call_ok_callback(model); + } + } + code_input_reset_count(model); + break; + + case CodeInputStateUpdate: + + if(!model->current && model->input_length[model->current]) { + model->current++; + } else { + if(code_input_compare_local(model)) { + if(model->ext_update) { + code_input_set_ext(model); + } + code_input_call_ok_callback(model); + } else { + code_input_reset_count(model); + } + } + + break; + default: + break; + } +} + +/** + * @brief Handle input + * + * @param model + * @param key + */ + +size_t code_input_push(uint8_t* buffer, size_t length, InputKey key) { + buffer[length] = key; + length = CLAMP(length + 1, MAX_CODE_LEN, 0); + return length; +} + +/** + * @brief Handle D-pad keys + * + * @param model + * @param key + */ +static void code_input_handle_dpad(CodeInputModel* model, InputKey key) { + uint8_t at = model->current; + size_t new_length = code_input_push(model->local_buffer[at], model->input_length[at], key); + model->input_length[at] = new_length; +} + +/** + * @brief Draw callback + * + * @param canvas + * @param _model + */ +static void code_input_view_draw_callback(Canvas* canvas, void* _model) { + CodeInputModel* model = _model; + uint8_t y_offset = 0; + if(!strlen(model->header)) y_offset = 5; + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + + canvas_draw_str(canvas, 2, 9, model->header); + + canvas_set_font(canvas, FontSecondary); + + switch(model->state) { + case CodeInputStateVerify: + code_input_draw_sequence( + canvas, + model->local_buffer[CodeInputFirst], + model->input_length[CodeInputFirst], + 6, + 30 - y_offset, + true); + break; + case CodeInputStateUpdate: + code_input_draw_sequence( + canvas, + model->local_buffer[CodeInputFirst], + model->input_length[CodeInputFirst], + 6, + 14 - y_offset, + !model->current); + code_input_draw_sequence( + canvas, + model->local_buffer[CodeInputSecond], + model->input_length[CodeInputSecond], + 6, + 44 - y_offset, + model->current); + + if(model->current) canvas_draw_str(canvas, 2, 39 - y_offset, "Repeat code"); + + break; + default: + break; + } +} + +/** + * @brief Input callback + * + * @param event + * @param context + * @return true + * @return false + */ +static bool code_input_view_input_callback(InputEvent* event, void* context) { + CodeInput* code_input = context; + furi_assert(code_input); + bool consumed = false; + + if(event->type == InputTypeShort || event->type == InputTypeRepeat) { + switch(event->key) { + case InputKeyBack: + with_view_model( + code_input->view, (CodeInputModel * model) { + consumed = code_input_handle_back(model); + return true; + }); + break; + + case InputKeyOk: + with_view_model( + code_input->view, (CodeInputModel * model) { + code_input_handle_ok(model); + return true; + }); + consumed = true; + break; + default: + + with_view_model( + code_input->view, (CodeInputModel * model) { + code_input_handle_dpad(model, event->key); + return true; + }); + consumed = true; + break; + } + } + + return consumed; +} + +/** + * @brief Reset all input-related data in model + * + * @param model CodeInputModel + */ +static void code_input_reset_model_input_data(CodeInputModel* model) { + model->current = 0; + model->input_length[CodeInputFirst] = 0; + model->input_length[CodeInputSecond] = 0; + model->ext_buffer = NULL; + model->ext_update = false; + model->state = 0; +} + +/** + * @brief Allocate and initialize code input. This code input is used to enter codes. + * + * @return CodeInput instance pointer + */ +CodeInput* code_input_alloc() { + CodeInput* code_input = furi_alloc(sizeof(CodeInput)); + code_input->view = view_alloc(); + view_set_context(code_input->view, code_input); + view_allocate_model(code_input->view, ViewModelTypeLocking, sizeof(CodeInputModel)); + view_set_draw_callback(code_input->view, code_input_view_draw_callback); + view_set_input_callback(code_input->view, code_input_view_input_callback); + + with_view_model( + code_input->view, (CodeInputModel * model) { + model->header = ""; + model->ok_callback = NULL; + model->fail_callback = NULL; + model->callback_context = NULL; + code_input_reset_model_input_data(model); + return true; + }); + + return code_input; +} + +/** + * @brief Deinitialize and free code input + * + * @param code_input Code input instance + */ +void code_input_free(CodeInput* code_input) { + furi_assert(code_input); + view_free(code_input->view); + free(code_input); +} + +/** + * @brief Get code input view + * + * @param code_input code input instance + * @return View instance that can be used for embedding + */ +View* code_input_get_view(CodeInput* code_input) { + furi_assert(code_input); + return code_input->view; +} + +/** + * @brief Set code input callbacks + * + * @param code_input code input instance + * @param ok_callback input callback fn + * @param fail_callback code match callback fn + * @param callback_context callback context + * @param buffer buffer + * @param buffer_length ptr to buffer length uint + * @param ext_update true to update buffer + */ +void code_input_set_result_callback( + CodeInput* code_input, + CodeInputOkCallback ok_callback, + CodeInputFailCallback fail_callback, + void* callback_context, + uint8_t* buffer, + uint8_t* buffer_length, + bool ext_update) { + with_view_model( + code_input->view, (CodeInputModel * model) { + code_input_reset_model_input_data(model); + model->ok_callback = ok_callback; + model->fail_callback = fail_callback; + model->callback_context = callback_context; + + model->ext_buffer = buffer; + model->ext_buffer_length = buffer_length; + model->state = (*buffer_length == 0) ? 1 : 0; + model->ext_update = ext_update; + + return true; + }); +} + +/** + * @brief Set code input header text + * + * @param code_input code input instance + * @param text text to be shown + */ +void code_input_set_header_text(CodeInput* code_input, const char* text) { + with_view_model( + code_input->view, (CodeInputModel * model) { + model->header = text; + return true; + }); +} diff --git a/applications/gui/modules/code_input.h b/applications/gui/modules/code_input.h new file mode 100644 index 00000000..d6a43fc1 --- /dev/null +++ b/applications/gui/modules/code_input.h @@ -0,0 +1,91 @@ +/** + * @file code_input.h + * GUI: CodeInput keyboard view module API + */ + +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** Code input anonymous structure */ +typedef struct CodeInput CodeInput; + +/** callback that is executed when entered code matches ext buffer */ +typedef void (*CodeInputOkCallback)(void* context); + +/** callback that is executed when entered code does not matches ext buffer */ +typedef void (*CodeInputFailCallback)(void* context); + +/** Allocate and initialize code input. This code input is used to enter codes. + * + * @return CodeInput instance pointer + */ +CodeInput* code_input_alloc(); + +/** Deinitialize and free code input + * + * @param code_input Code input instance + */ +void code_input_free(CodeInput* code_input); + +/** Get code input view + * + * @param code_input code input instance + * + * @return View instance that can be used for embedding + */ +View* code_input_get_view(CodeInput* code_input); + +/** Set code input result callback + * + * @param code_input code input instance + * @param ok_callback ok callback fn + * @param fail_callback fail callback fn + * @param callback_context callback context + * @param buffer buffer to use + * @param buffer_length buffer length + * @param update set true to update buffer + */ +void code_input_set_result_callback( + CodeInput* code_input, + CodeInputOkCallback ok_callback, + CodeInputFailCallback fail_callback, + void* callback_context, + uint8_t* buffer, + uint8_t* buffer_length, + bool update); + +/** Set code input header text + * + * @param code_input code input instance + * @param text text to be shown + */ +void code_input_set_header_text(CodeInput* code_input, const char* text); + +/** Compare two buffers + * + * @param in buffer to compare to source + * @param len_in length of input buffer + * @param src source buffer + * @param len_src length of insourceput buffer + * @return true if buffers match + */ + +bool code_input_compare(uint8_t* in, size_t len_in, uint8_t* src, size_t len_src); + +/** Push input into the end of array + * + * @param buffer buffer + * @param length length of buffer + * @param key input key + * @return new length of input buffer + */ +size_t code_input_push(uint8_t* buffer, size_t length, InputKey key); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/applications/gui/modules/widget.c b/applications/gui/modules/widget.c index 92ca9133..76779463 100755 --- a/applications/gui/modules/widget.c +++ b/applications/gui/modules/widget.c @@ -146,6 +146,21 @@ void widget_add_string_element( widget_add_element(widget, string_element); } +void widget_add_text_box_element( + Widget* widget, + uint8_t x, + uint8_t y, + uint8_t width, + uint8_t height, + Align horizontal, + Align vertical, + const char* text) { + furi_assert(widget); + WidgetElement* text_box_element = + widget_element_text_box_create(x, y, width, height, horizontal, vertical, text); + widget_add_element(widget, text_box_element); +} + void widget_add_button_element( Widget* widget, GuiButtonType button_type, diff --git a/applications/gui/modules/widget.h b/applications/gui/modules/widget.h index 09177c57..af881be1 100755 --- a/applications/gui/modules/widget.h +++ b/applications/gui/modules/widget.h @@ -75,6 +75,30 @@ void widget_add_string_element( Font font, const char* text); +/** Add Text Box Element + * + * @param widget Widget instance + * @param x x coordinate + * @param y y coordinate + * @param width width to fit text + * @param height height to fit text + * @param horizontal Align instance + * @param vertical Align instance + * @param[in] text Formatted text. The following formats are available: + * "\e#Bold text\e#" - bold font is used + * "\e*Monospaced text\e*" - monospaced font is used + * "\e#Inversed text\e#" - white text on black background + */ +void widget_add_text_box_element( + Widget* widget, + uint8_t x, + uint8_t y, + uint8_t width, + uint8_t height, + Align horizontal, + Align vertical, + const char* text); + /** Add Button Element * * @param widget Widget instance diff --git a/applications/gui/modules/widget_elements/widget_element_i.h b/applications/gui/modules/widget_elements/widget_element_i.h index bbc58ff9..d7b4e463 100755 --- a/applications/gui/modules/widget_elements/widget_element_i.h +++ b/applications/gui/modules/widget_elements/widget_element_i.h @@ -52,6 +52,16 @@ WidgetElement* widget_element_string_create( Font font, const char* text); +/** Create text box element */ +WidgetElement* widget_element_text_box_create( + uint8_t x, + uint8_t y, + uint8_t width, + uint8_t height, + Align horizontal, + Align vertical, + const char* text); + /** Create button element */ WidgetElement* widget_element_button_create( GuiButtonType button_type, diff --git a/applications/gui/modules/widget_elements/widget_element_text_box.c b/applications/gui/modules/widget_elements/widget_element_text_box.c new file mode 100644 index 00000000..9ee33188 --- /dev/null +++ b/applications/gui/modules/widget_elements/widget_element_text_box.c @@ -0,0 +1,71 @@ +#include "widget_element_i.h" +#include +#include + +typedef struct { + uint8_t x; + uint8_t y; + uint8_t width; + uint8_t height; + Align horizontal; + Align vertical; + string_t text; +} GuiTextBoxModel; + +static void gui_text_box_draw(Canvas* canvas, WidgetElement* element) { + furi_assert(canvas); + furi_assert(element); + GuiTextBoxModel* model = element->model; + + if(string_size(model->text)) { + elements_text_box( + canvas, + model->x, + model->y, + model->width, + model->height, + model->horizontal, + model->vertical, + string_get_cstr(model->text)); + } +} + +static void gui_text_box_free(WidgetElement* gui_string) { + furi_assert(gui_string); + + GuiTextBoxModel* model = gui_string->model; + string_clear(model->text); + free(gui_string->model); + free(gui_string); +} + +WidgetElement* widget_element_text_box_create( + uint8_t x, + uint8_t y, + uint8_t width, + uint8_t height, + Align horizontal, + Align vertical, + const char* text) { + furi_assert(text); + + // Allocate and init model + GuiTextBoxModel* model = furi_alloc(sizeof(GuiTextBoxModel)); + model->x = x; + model->y = y; + model->width = width; + model->height = height; + model->horizontal = horizontal; + model->vertical = vertical; + string_init_set_str(model->text, text); + + // Allocate and init Element + WidgetElement* gui_string = furi_alloc(sizeof(WidgetElement)); + gui_string->parent = NULL; + gui_string->input = NULL; + gui_string->draw = gui_text_box_draw; + gui_string->free = gui_text_box_free; + gui_string->model = model; + + return gui_string; +} diff --git a/applications/nfc/scenes/nfc_scene_delete.c b/applications/nfc/scenes/nfc_scene_delete.c index 993e1e37..0ea1b214 100755 --- a/applications/nfc/scenes/nfc_scene_delete.c +++ b/applications/nfc/scenes/nfc_scene_delete.c @@ -12,8 +12,8 @@ void nfc_scene_delete_on_enter(void* context) { // Setup Custom Widget view char delete_str[64]; - snprintf(delete_str, sizeof(delete_str), "Delete %s", nfc->dev.dev_name); - widget_add_string_element(nfc->widget, 64, 6, AlignCenter, AlignTop, FontPrimary, delete_str); + snprintf(delete_str, sizeof(delete_str), "\e#Delete %s\e#", nfc->dev.dev_name); + widget_add_text_box_element(nfc->widget, 0, 0, 128, 24, AlignCenter, AlignCenter, delete_str); widget_add_button_element( nfc->widget, GuiButtonTypeLeft, "Back", nfc_scene_delete_widget_callback, nfc); widget_add_button_element( diff --git a/applications/nfc/scenes/nfc_scene_device_info.c b/applications/nfc/scenes/nfc_scene_device_info.c index 674b3f6d..54196a32 100755 --- a/applications/nfc/scenes/nfc_scene_device_info.c +++ b/applications/nfc/scenes/nfc_scene_device_info.c @@ -35,8 +35,8 @@ void nfc_scene_device_info_on_enter(void* context) { Nfc* nfc = context; // Setup Custom Widget view - widget_add_string_element( - nfc->widget, 64, 6, AlignCenter, AlignTop, FontSecondary, nfc->dev.dev_name); + widget_add_text_box_element( + nfc->widget, 0, 0, 128, 24, AlignCenter, AlignCenter, nfc->dev.dev_name); widget_add_button_element( nfc->widget, GuiButtonTypeLeft, "Back", nfc_scene_device_info_widget_callback, nfc); widget_add_button_element( diff --git a/applications/power/power_cli.c b/applications/power/power_cli.c index 4240ae45..63c0fda5 100644 --- a/applications/power/power_cli.c +++ b/applications/power/power_cli.c @@ -23,7 +23,7 @@ void power_cli_factory_reset(Cli* cli, string_t args, void* context) { char c = cli_getc(cli); if(c == 'y' || c == 'Y') { printf("Data will be wiped after reboot.\r\n"); - furi_hal_boot_set_flags(FuriHalBootFlagFactoryReset); + furi_hal_bootloader_set_flags(FuriHalBootloaderFlagFactoryReset); power_reboot(PowerBootModeNormal); } else { printf("Safe choice.\r\n"); diff --git a/applications/power/power_service/power_api.c b/applications/power/power_service/power_api.c index 4147d17d..46fa21ef 100644 --- a/applications/power/power_service/power_api.c +++ b/applications/power/power_service/power_api.c @@ -1,7 +1,7 @@ #include "power_i.h" #include #include "furi-hal-power.h" -#include "furi-hal-boot.h" +#include "furi-hal-bootloader.h" void power_off(Power* power) { furi_hal_power_off(); @@ -14,9 +14,9 @@ void power_off(Power* power) { void power_reboot(PowerBootMode mode) { if(mode == PowerBootModeNormal) { - furi_hal_boot_set_mode(FuriHalBootModeNormal); + furi_hal_bootloader_set_mode(FuriHalBootloaderModeNormal); } else if(mode == PowerBootModeDfu) { - furi_hal_boot_set_mode(FuriHalBootModeDFU); + furi_hal_bootloader_set_mode(FuriHalBootloaderModeDFU); } furi_hal_power_reset(); } diff --git a/applications/rpc/rpc.c b/applications/rpc/rpc.c index adb23e78..b8d6a979 100644 --- a/applications/rpc/rpc.c +++ b/applications/rpc/rpc.c @@ -1,22 +1,20 @@ -#include "cmsis_os.h" -#include "cmsis_os2.h" -#include "flipper.pb.h" -#include "furi-hal-delay.h" -#include "furi/check.h" -#include "furi/log.h" -#include -#include "pb.h" -#include "pb_decode.h" -#include "pb_encode.h" -#include "portmacro.h" -#include "status.pb.h" -#include "storage.pb.h" +#include "rpc_i.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include -#include #include +#include #include -#include "rpc_i.h" #define RPC_TAG "RPC" @@ -51,10 +49,11 @@ static RpcSystemCallbacks rpc_systems[] = { struct RpcSession { RpcSendBytesCallback send_bytes_callback; - void* send_bytes_context; - osMutexId_t send_bytes_mutex; + RpcSessionClosedCallback closed_callback; + void* context; + osMutexId_t callbacks_mutex; Rpc* rpc; - bool terminate_session; + bool terminate; void** system_contexts; }; @@ -70,6 +69,20 @@ struct Rpc { static bool content_callback(pb_istream_t* stream, const pb_field_t* field, void** arg); +static void rpc_close_session_process(const PB_Main* msg_request, void* context) { + furi_assert(msg_request); + furi_assert(context); + + Rpc* rpc = context; + rpc_send_and_release_empty(rpc, msg_request->command_id, PB_CommandStatus_OK); + + osMutexAcquire(rpc->session.callbacks_mutex, osWaitForever); + if(rpc->session.closed_callback) { + rpc->session.closed_callback(rpc->session.context); + } + osMutexRelease(rpc->session.callbacks_mutex); +} + static size_t rpc_sprintf_msg_file( string_t str, const char* prefix, @@ -105,6 +118,31 @@ static size_t rpc_sprintf_msg_file( return cnt; } +void rpc_print_data(const char* prefix, uint8_t* buffer, size_t size) { + string_t str; + string_init(str); + string_reserve(str, 100 + size * 5); + + string_cat_printf(str, "\r\n%s DEC(%d): {", prefix, size); + for(int i = 0; i < size; ++i) { + string_cat_printf(str, "%d, ", buffer[i]); + } + string_cat_printf(str, "}\r\n"); + + printf("%s", string_get_cstr(str)); + string_clean(str); + string_reserve(str, 100 + size * 3); + + string_cat_printf(str, "%s HEX(%d): {", prefix, size); + for(int i = 0; i < size; ++i) { + string_cat_printf(str, "%02X", buffer[i]); + } + string_cat_printf(str, "}\r\n\r\n"); + + printf("%s", string_get_cstr(str)); + string_clear(str); +} + void rpc_print_message(const PB_Main* message) { string_t str; string_init(str); @@ -120,6 +158,9 @@ void rpc_print_message(const PB_Main* message) { /* not implemented yet */ string_cat_printf(str, "\tNOT_IMPLEMENTED (%d) {\r\n", message->which_content); break; + case PB_Main_stop_session_tag: + string_cat_printf(str, "\tstop_session {\r\n"); + break; case PB_Main_app_start_tag: { string_cat_printf(str, "\tapp_start {\r\n"); const char* name = message->content.app_start.name; @@ -242,7 +283,7 @@ static Rpc* rpc_alloc(void) { return rpc; } -RpcSession* rpc_open_session(Rpc* rpc) { +RpcSession* rpc_session_open(Rpc* rpc) { furi_assert(rpc); bool result = false; furi_check(osMutexAcquire(rpc->busy_mutex, osWaitForever) == osOK); @@ -256,41 +297,94 @@ RpcSession* rpc_open_session(Rpc* rpc) { if(result) { RpcSession* session = &rpc->session; - session->send_bytes_mutex = osMutexNew(NULL); + session->callbacks_mutex = osMutexNew(NULL); session->rpc = rpc; - session->terminate_session = false; + session->terminate = false; + xStreamBufferReset(rpc->stream); + session->system_contexts = furi_alloc(COUNT_OF(rpc_systems) * sizeof(void*)); for(int i = 0; i < COUNT_OF(rpc_systems); ++i) { session->system_contexts[i] = rpc_systems[i].alloc(rpc); } + + RpcHandler rpc_handler = { + .message_handler = rpc_close_session_process, + .decode_submessage = NULL, + .context = rpc, + }; + rpc_add_handler(rpc, PB_Main_stop_session_tag, &rpc_handler); + FURI_LOG_D(RPC_TAG, "Session started\r\n"); } return result ? &rpc->session : NULL; /* support 1 open session for now */ } -void rpc_close_session(RpcSession* session) { +void rpc_session_close(RpcSession* session) { furi_assert(session); furi_assert(session->rpc); furi_assert(session->rpc->busy); - rpc_set_send_bytes_callback(session, NULL, NULL); + rpc_session_set_send_bytes_callback(session, NULL); + rpc_session_set_close_callback(session, NULL); osEventFlagsSet(session->rpc->events, RPC_EVENT_DISCONNECT); } -void rpc_set_send_bytes_callback(RpcSession* session, RpcSendBytesCallback callback, void* context) { +static void rpc_free_session(RpcSession* session) { + furi_assert(session); + + for(int i = 0; i < COUNT_OF(rpc_systems); ++i) { + if(rpc_systems[i].free) { + rpc_systems[i].free(session->system_contexts[i]); + } + } + free(session->system_contexts); + osMutexDelete(session->callbacks_mutex); + RpcHandlerDict_clean(session->rpc->handlers); + + session->context = NULL; + session->closed_callback = NULL; + session->send_bytes_callback = NULL; +} + +void rpc_session_set_context(RpcSession* session, void* context) { furi_assert(session); furi_assert(session->rpc); furi_assert(session->rpc->busy); - osMutexAcquire(session->send_bytes_mutex, osWaitForever); - session->send_bytes_callback = callback; - session->send_bytes_context = context; - osMutexRelease(session->send_bytes_mutex); + osMutexAcquire(session->callbacks_mutex, osWaitForever); + session->context = context; + osMutexRelease(session->callbacks_mutex); } +void rpc_session_set_close_callback(RpcSession* session, RpcSessionClosedCallback callback) { + furi_assert(session); + furi_assert(session->rpc); + furi_assert(session->rpc->busy); + + osMutexAcquire(session->callbacks_mutex, osWaitForever); + session->closed_callback = callback; + osMutexRelease(session->callbacks_mutex); +} + +void rpc_session_set_send_bytes_callback(RpcSession* session, RpcSendBytesCallback callback) { + furi_assert(session); + furi_assert(session->rpc); + furi_assert(session->rpc->busy); + + osMutexAcquire(session->callbacks_mutex, osWaitForever); + session->send_bytes_callback = callback; + osMutexRelease(session->callbacks_mutex); +} + +/* Doesn't forbid using rpc_feed_bytes() after session close - it's safe. + * Because any bytes received in buffer will be flushed before next session. + * If bytes get into stream buffer before it's get epmtified and this + * command is gets processed - it's safe either. But case of it is quite + * odd: client sends close request and sends command after. + */ size_t - rpc_feed_bytes(RpcSession* session, uint8_t* encoded_bytes, size_t size, TickType_t timeout) { + rpc_session_feed(RpcSession* session, uint8_t* encoded_bytes, size_t size, TickType_t timeout) { furi_assert(session); Rpc* rpc = session->rpc; furi_assert(rpc->busy); @@ -306,6 +400,8 @@ bool rpc_pb_stream_read(pb_istream_t* istream, pb_byte_t* buf, size_t count) { uint32_t flags = 0; size_t bytes_received = 0; + furi_assert(istream->bytes_left); + while(1) { bytes_received += xStreamBufferReceive(rpc->stream, buf + bytes_received, count - bytes_received, 0); @@ -315,7 +411,9 @@ bool rpc_pb_stream_read(pb_istream_t* istream, pb_byte_t* buf, size_t count) { flags = osEventFlagsWait(rpc->events, RPC_EVENTS_ALL, 0, osWaitForever); if(flags & RPC_EVENT_DISCONNECT) { if(xStreamBufferIsEmpty(rpc->stream)) { - rpc->session.terminate_session = true; + rpc->session.terminate = true; + istream->bytes_left = 0; + bytes_received = 0; break; } else { /* Save disconnect flag and continue reading buffer */ @@ -325,61 +423,44 @@ bool rpc_pb_stream_read(pb_istream_t* istream, pb_byte_t* buf, size_t count) { } } +#if DEBUG_PRINT + rpc_print_data("INPUT", buf, bytes_received); +#endif + return (count == bytes_received); } -void rpc_encode_and_send(Rpc* rpc, PB_Main* main_message) { +void rpc_send_and_release(Rpc* rpc, PB_Main* message) { furi_assert(rpc); - furi_assert(main_message); + furi_assert(message); RpcSession* session = &rpc->session; pb_ostream_t ostream = PB_OSTREAM_SIZING; #if DEBUG_PRINT FURI_LOG_I(RPC_TAG, "OUTPUT:"); - rpc_print_message(main_message); + rpc_print_message(message); #endif - bool result = pb_encode_ex(&ostream, &PB_Main_msg, main_message, PB_ENCODE_DELIMITED); + bool result = pb_encode_ex(&ostream, &PB_Main_msg, message, PB_ENCODE_DELIMITED); furi_check(result && ostream.bytes_written); uint8_t* buffer = furi_alloc(ostream.bytes_written); ostream = pb_ostream_from_buffer(buffer, ostream.bytes_written); - pb_encode_ex(&ostream, &PB_Main_msg, main_message, PB_ENCODE_DELIMITED); + pb_encode_ex(&ostream, &PB_Main_msg, message, PB_ENCODE_DELIMITED); - { #if DEBUG_PRINT - string_t str; - string_init(str); - string_reserve(str, 100 + ostream.bytes_written * 5); + rpc_print_data("OUTPUT", buffer, ostream.bytes_written); +#endif - string_cat_printf(str, "\r\nREPONSE DEC(%d): {", ostream.bytes_written); - for(int i = 0; i < ostream.bytes_written; ++i) { - string_cat_printf(str, "%d, ", buffer[i]); - } - string_cat_printf(str, "}\r\n"); - - printf("%s", string_get_cstr(str)); - string_clean(str); - string_reserve(str, 100 + ostream.bytes_written * 3); - - string_cat_printf(str, "REPONSE HEX(%d): {", ostream.bytes_written); - for(int i = 0; i < ostream.bytes_written; ++i) { - string_cat_printf(str, "%02X", buffer[i]); - } - string_cat_printf(str, "}\r\n\r\n"); - - printf("%s", string_get_cstr(str)); -#endif // DEBUG_PRINT - - osMutexAcquire(session->send_bytes_mutex, osWaitForever); - if(session->send_bytes_callback) { - session->send_bytes_callback( - session->send_bytes_context, buffer, ostream.bytes_written); - } - osMutexRelease(session->send_bytes_mutex); + osMutexAcquire(session->callbacks_mutex, osWaitForever); + if(session->send_bytes_callback) { + session->send_bytes_callback(session->context, buffer, ostream.bytes_written); } + osMutexRelease(session->callbacks_mutex); + free(buffer); + pb_release(&PB_Main_msg, message); } static bool content_callback(pb_istream_t* stream, const pb_field_t* field, void** arg) { @@ -399,12 +480,17 @@ int32_t rpc_srv(void* p) { Rpc* rpc = rpc_alloc(); furi_record_create("rpc", rpc); + Cli* cli = furi_record_open("cli"); + + cli_add_command( + cli, "start_rpc_session", CliCommandFlagParallelSafe, rpc_cli_command_start_session, rpc); + while(1) { pb_istream_t istream = { .callback = rpc_pb_stream_read, .state = rpc, .errmsg = NULL, - .bytes_left = 0x7FFFFFFF, + .bytes_left = 1024, /* max incoming message size */ }; if(pb_decode_ex(&istream, &PB_Main_msg, rpc->decoded_message, PB_DECODE_DELIMITED)) { @@ -417,35 +503,25 @@ int32_t rpc_srv(void* p) { if(handler && handler->message_handler) { handler->message_handler(rpc->decoded_message, handler->context); - } else if(!handler) { + } else if(!handler && !rpc->session.terminate) { FURI_LOG_E( - RPC_TAG, - "Unhandled message, tag: %d\r\n", - rpc->decoded_message->which_content); + RPC_TAG, "Unhandled message, tag: %d", rpc->decoded_message->which_content); } - pb_release(&PB_Main_msg, rpc->decoded_message); } else { - pb_release(&PB_Main_msg, rpc->decoded_message); - RpcSession* session = &rpc->session; - if(session->terminate_session) { - session->terminate_session = false; - osEventFlagsClear(rpc->events, RPC_EVENTS_ALL); - FURI_LOG_D(RPC_TAG, "Session terminated\r\n"); - for(int i = 0; i < COUNT_OF(rpc_systems); ++i) { - if(rpc_systems[i].free) { - rpc_systems[i].free(session->system_contexts[i]); - } - } - free(session->system_contexts); - osMutexDelete(session->send_bytes_mutex); - RpcHandlerDict_clean(rpc->handlers); - rpc->busy = false; - } else { - xStreamBufferReset(rpc->stream); - FURI_LOG_E( - RPC_TAG, "Decode failed, error: \'%.128s\'\r\n", PB_GET_ERROR(&istream)); + xStreamBufferReset(rpc->stream); + if(!rpc->session.terminate) { + FURI_LOG_E(RPC_TAG, "Decode failed, error: \'%.128s\'", PB_GET_ERROR(&istream)); } } + + pb_release(&PB_Main_msg, rpc->decoded_message); + + if(rpc->session.terminate) { + FURI_LOG_D(RPC_TAG, "Session terminated"); + osEventFlagsClear(rpc->events, RPC_EVENTS_ALL); + rpc_free_session(&rpc->session); + rpc->busy = false; + } } return 0; } @@ -456,13 +532,13 @@ void rpc_add_handler(Rpc* rpc, pb_size_t message_tag, RpcHandler* handler) { RpcHandlerDict_set_at(rpc->handlers, message_tag, *handler); } -void rpc_encode_and_send_empty(Rpc* rpc, uint32_t command_id, PB_CommandStatus status) { +void rpc_send_and_release_empty(Rpc* rpc, uint32_t command_id, PB_CommandStatus status) { PB_Main message = { .command_id = command_id, .command_status = status, .has_next = false, .which_content = PB_Main_empty_tag, }; - rpc_encode_and_send(rpc, &message); + rpc_send_and_release(rpc, &message); pb_release(&PB_Main_msg, &message); } diff --git a/applications/rpc/rpc.h b/applications/rpc/rpc.h index 91557606..29e37773 100644 --- a/applications/rpc/rpc.h +++ b/applications/rpc/rpc.h @@ -1,16 +1,79 @@ #pragma once #include #include +#include #include "cmsis_os.h" +/** Rpc interface. Used for opening session only. */ typedef struct Rpc Rpc; +/** Rpc session interface */ typedef struct RpcSession RpcSession; +/** Callback to send to client any data (e.g. response to command) */ typedef void (*RpcSendBytesCallback)(void* context, uint8_t* bytes, size_t bytes_len); +/** Callback to notify transport layer that close_session command + * is received. Any other actions lays on transport layer. + * No destruction or session close preformed. */ +typedef void (*RpcSessionClosedCallback)(void* context); -RpcSession* rpc_open_session(Rpc* rpc); -void rpc_close_session(RpcSession* session); -/* WARN: can't call RPC API within RpcSendBytesCallback */ -void rpc_set_send_bytes_callback(RpcSession* session, RpcSendBytesCallback callback, void* context); -size_t - rpc_feed_bytes(RpcSession* session, uint8_t* encoded_bytes, size_t size, TickType_t timeout); +/** Open RPC session + * + * USAGE: + * 1) rpc_session_open(); + * 2) rpc_session_set_context(); + * 3) rpc_session_set_send_bytes_callback(); + * 4) rpc_session_set_close_callback(); + * 5) while(1) { + * rpc_session_feed(); + * } + * 6) rpc_session_close(); + * + * + * @param rpc instance + * @return pointer to RpcSession descriptor, or + * NULL if RPC is busy and can't open session now + */ +RpcSession* rpc_session_open(Rpc* rpc); + +/** Close RPC session + * It is guaranteed that no callbacks will be called + * as soon as session is closed. So no need in setting + * callbacks to NULL after session close. + * + * @param session pointer to RpcSession descriptor + */ +void rpc_session_close(RpcSession* session); + +/** Set session context for callbacks to pass + * + * @param session pointer to RpcSession descriptor + * @param context context to pass to callbacks + */ +void rpc_session_set_context(RpcSession* session, void* context); + +/** Set callback to send bytes to client + * WARN: It's forbidden to call RPC API within RpcSendBytesCallback + * + * @param session pointer to RpcSession descriptor + * @param callback callback to send bytes to client (can be NULL) + */ +void rpc_session_set_send_bytes_callback(RpcSession* session, RpcSendBytesCallback callback); + +/** Set callback to be called when RPC command to close session is received + * WARN: It's forbidden to call RPC API within RpcSessionClosedCallback + * + * @param session pointer to RpcSession descriptor + * @param callback callback to inform about RPC close session command (can be NULL) + */ +void rpc_session_set_close_callback(RpcSession* session, RpcSessionClosedCallback callback); + +/** Give bytes to RPC service to decode them and perform command + * + * @param session pointer to RpcSession descriptor + * @param buffer buffer to provide to RPC service + * @param size size of buffer + * @param timeout max timeout to wait till all buffer will be consumed + * + * @return actually consumed bytes + */ +size_t rpc_session_feed(RpcSession* session, uint8_t* buffer, size_t size, TickType_t timeout); diff --git a/applications/rpc/rpc_app.c b/applications/rpc/rpc_app.c index db4e5567..d2c0cdc0 100644 --- a/applications/rpc/rpc_app.c +++ b/applications/rpc/rpc_app.c @@ -34,7 +34,7 @@ void rpc_system_app_start_process(const PB_Main* request, void* context) { furi_record_close("loader"); - rpc_encode_and_send_empty(rpc, request->command_id, result); + rpc_send_and_release_empty(rpc, request->command_id, result); } void rpc_system_app_lock_status_process(const PB_Main* request, void* context) { @@ -56,7 +56,8 @@ void rpc_system_app_lock_status_process(const PB_Main* request, void* context) { furi_record_close("loader"); - rpc_encode_and_send(rpc, &response); + rpc_send_and_release(rpc, &response); + pb_release(&PB_Main_msg, &response); } void* rpc_system_app_alloc(Rpc* rpc) { diff --git a/applications/rpc/rpc_cli.c b/applications/rpc/rpc_cli.c new file mode 100644 index 00000000..853e9dd3 --- /dev/null +++ b/applications/rpc/rpc_cli.c @@ -0,0 +1,59 @@ +#include +#include +#include +#include + +typedef struct { + Cli* cli; + bool session_close_request; +} CliRpc; + +#define CLI_READ_BUFFER_SIZE 100 + +static void rpc_send_bytes_callback(void* context, uint8_t* bytes, size_t bytes_len) { + furi_assert(context); + furi_assert(bytes); + furi_assert(bytes_len); + CliRpc* cli_rpc = context; + + cli_write(cli_rpc->cli, bytes, bytes_len); +} + +static void rpc_session_close_callback(void* context) { + furi_assert(context); + CliRpc* cli_rpc = context; + + cli_rpc->session_close_request = true; +} + +void rpc_cli_command_start_session(Cli* cli, string_t args, void* context) { + Rpc* rpc = context; + + RpcSession* rpc_session = rpc_session_open(rpc); + if(rpc_session == NULL) { + printf("Another session is in progress\r\n"); + return; + } + + CliRpc cli_rpc = {.cli = cli, .session_close_request = false}; + rpc_session_set_context(rpc_session, &cli_rpc); + rpc_session_set_send_bytes_callback(rpc_session, rpc_send_bytes_callback); + rpc_session_set_close_callback(rpc_session, rpc_session_close_callback); + + uint8_t* buffer = furi_alloc(CLI_READ_BUFFER_SIZE); + size_t size_received = 0; + + while(1) { + size_received = furi_hal_vcp_rx_with_timeout(buffer, CLI_READ_BUFFER_SIZE, 50); + if(!furi_hal_vcp_is_connected() || cli_rpc.session_close_request) { + break; + } + + if(size_received) { + rpc_session_feed(rpc_session, buffer, size_received, 3000); + } + } + + rpc_session_close(rpc_session); + free(buffer); +} diff --git a/applications/rpc/rpc_i.h b/applications/rpc/rpc_i.h index 5bcb9d9b..1bcf7c86 100644 --- a/applications/rpc/rpc_i.h +++ b/applications/rpc/rpc_i.h @@ -1,9 +1,10 @@ #pragma once #include "rpc.h" -#include "pb.h" -#include "pb_decode.h" -#include "pb_encode.h" -#include "flipper.pb.h" +#include +#include +#include +#include +#include typedef void* (*RpcSystemAlloc)(Rpc*); typedef void (*RpcSystemFree)(void*); @@ -15,8 +16,8 @@ typedef struct { void* context; } RpcHandler; -void rpc_encode_and_send(Rpc* rpc, PB_Main* main_message); -void rpc_encode_and_send_empty(Rpc* rpc, uint32_t command_id, PB_CommandStatus status); +void rpc_send_and_release(Rpc* rpc, PB_Main* main_message); +void rpc_send_and_release_empty(Rpc* rpc, uint32_t command_id, PB_CommandStatus status); void rpc_add_handler(Rpc* rpc, pb_size_t message_tag, RpcHandler* handler); void* rpc_system_status_alloc(Rpc* rpc); @@ -25,3 +26,4 @@ void rpc_system_storage_free(void* ctx); void* rpc_system_app_alloc(Rpc* rpc); void rpc_print_message(const PB_Main* message); +void rpc_cli_command_start_session(Cli* cli, string_t args, void* context); diff --git a/applications/rpc/rpc_status.c b/applications/rpc/rpc_status.c index 524675d9..e8f4e273 100644 --- a/applications/rpc/rpc_status.c +++ b/applications/rpc/rpc_status.c @@ -9,7 +9,8 @@ void rpc_system_status_ping_process(const PB_Main* msg_request, void* context) { msg_response.command_id = msg_request->command_id; msg_response.which_content = PB_Main_ping_response_tag; - rpc_encode_and_send(context, &msg_response); + rpc_send_and_release(context, &msg_response); + pb_release(&PB_Main_msg, &msg_response); } void* rpc_system_status_alloc(Rpc* rpc) { diff --git a/applications/rpc/rpc_storage.c b/applications/rpc/rpc_storage.c index 96842cba..f8e0a97e 100644 --- a/applications/rpc/rpc_storage.c +++ b/applications/rpc/rpc_storage.c @@ -35,7 +35,7 @@ static void rpc_system_storage_reset_state(RpcStorageSystem* rpc_storage, bool s if(rpc_storage->state != RpcStorageStateIdle) { if(send_error) { - rpc_encode_and_send_empty( + rpc_send_and_release_empty( rpc_storage->rpc, rpc_storage->current_command_id, PB_CommandStatus_ERROR_CONTINUOUS_COMMAND_INTERRUPTED); @@ -96,6 +96,31 @@ static PB_CommandStatus rpc_system_storage_get_file_error(File* file) { return rpc_system_storage_get_error(storage_file_get_error(file)); } +static void rpc_system_storage_list_root(const PB_Main* request, void* context) { + RpcStorageSystem* rpc_storage = context; + const char* hard_coded_dirs[] = {"any", "int", "ext"}; + + PB_Main response = { + .has_next = false, + .command_id = request->command_id, + .command_status = PB_CommandStatus_OK, + .which_content = PB_Main_storage_list_response_tag, + }; + furi_assert(COUNT_OF(hard_coded_dirs) < COUNT_OF(response.content.storage_list_response.file)); + + for(int i = 0; i < COUNT_OF(hard_coded_dirs); ++i) { + ++response.content.storage_list_response.file_count; + response.content.storage_list_response.file[i].data = NULL; + response.content.storage_list_response.file[i].size = 0; + response.content.storage_list_response.file[i].type = PB_Storage_File_FileType_DIR; + char* str = furi_alloc(strlen(hard_coded_dirs[i]) + 1); + strcpy(str, hard_coded_dirs[i]); + response.content.storage_list_response.file[i].name = str; + } + + rpc_send_and_release(rpc_storage->rpc, &response); +} + static void rpc_system_storage_list_process(const PB_Main* request, void* context) { furi_assert(request); furi_assert(context); @@ -104,6 +129,11 @@ static void rpc_system_storage_list_process(const PB_Main* request, void* contex RpcStorageSystem* rpc_storage = context; rpc_system_storage_reset_state(rpc_storage, true); + if(!strcmp(request->content.storage_list_request.path, "/")) { + rpc_system_storage_list_root(request, context); + return; + } + Storage* fs_api = furi_record_open("storage"); File* dir = storage_file_alloc(fs_api); @@ -132,8 +162,7 @@ static void rpc_system_storage_list_process(const PB_Main* request, void* contex if(i == COUNT_OF(list->file)) { list->file_count = i; response.has_next = true; - rpc_encode_and_send(rpc_storage->rpc, &response); - pb_release(&PB_Main_msg, &response); + rpc_send_and_release(rpc_storage->rpc, &response); i = 0; } list->file[i].type = (fileinfo.flags & FSF_DIRECTORY) ? PB_Storage_File_FileType_DIR : @@ -150,8 +179,7 @@ static void rpc_system_storage_list_process(const PB_Main* request, void* contex } response.has_next = false; - rpc_encode_and_send(rpc_storage->rpc, &response); - pb_release(&PB_Main_msg, &response); + rpc_send_and_release(rpc_storage->rpc, &response); storage_dir_close(dir); storage_file_free(dir); @@ -168,9 +196,6 @@ static void rpc_system_storage_read_process(const PB_Main* request, void* contex /* use same message memory to send reponse */ PB_Main* response = furi_alloc(sizeof(PB_Main)); - response->command_id = request->command_id; - response->which_content = PB_Main_storage_read_response_tag; - response->command_status = PB_CommandStatus_OK; const char* path = request->content.storage_read_request.path; Storage* fs_api = furi_record_open("storage"); File* file = storage_file_alloc(fs_api); @@ -178,10 +203,13 @@ static void rpc_system_storage_read_process(const PB_Main* request, void* contex if(storage_file_open(file, path, FSAM_READ, FSOM_OPEN_EXISTING)) { size_t size_left = storage_file_size(file); - response->content.storage_read_response.has_file = true; - response->content.storage_read_response.file.data = - furi_alloc(PB_BYTES_ARRAY_T_ALLOCSIZE(MIN(size_left, MAX_DATA_SIZE))); do { + response->command_id = request->command_id; + response->which_content = PB_Main_storage_read_response_tag; + response->command_status = PB_CommandStatus_OK; + response->content.storage_read_response.has_file = true; + response->content.storage_read_response.file.data = + furi_alloc(PB_BYTES_ARRAY_T_ALLOCSIZE(MIN(size_left, MAX_DATA_SIZE))); uint8_t* buffer = response->content.storage_read_response.file.data->bytes; uint16_t* read_size_msg = &response->content.storage_read_response.file.data->size; @@ -192,21 +220,19 @@ static void rpc_system_storage_read_process(const PB_Main* request, void* contex if(result) { response->has_next = (size_left > 0); - rpc_encode_and_send(rpc_storage->rpc, response); - // no pb_release(...); + rpc_send_and_release(rpc_storage->rpc, response); } } while((size_left != 0) && result); if(!result) { - rpc_encode_and_send_empty( + rpc_send_and_release_empty( rpc_storage->rpc, request->command_id, rpc_system_storage_get_file_error(file)); } } else { - rpc_encode_and_send_empty( + rpc_send_and_release_empty( rpc_storage->rpc, request->command_id, rpc_system_storage_get_file_error(file)); } - pb_release(&PB_Main_msg, response); free(response); storage_file_close(file); storage_file_free(file); @@ -245,14 +271,14 @@ static void rpc_system_storage_write_process(const PB_Main* request, void* conte result = (written_size == buffer_size); if(result && !request->has_next) { - rpc_encode_and_send_empty( + rpc_send_and_release_empty( rpc_storage->rpc, rpc_storage->current_command_id, PB_CommandStatus_OK); rpc_system_storage_reset_state(rpc_storage, false); } } if(!result) { - rpc_encode_and_send_empty( + rpc_send_and_release_empty( rpc_storage->rpc, rpc_storage->current_command_id, rpc_system_storage_get_file_error(file)); @@ -260,23 +286,57 @@ static void rpc_system_storage_write_process(const PB_Main* request, void* conte } } +static bool rpc_system_storage_is_dir_is_empty(Storage* fs_api, const char* path) { + FileInfo fileinfo; + bool is_dir_is_empty = false; + FS_Error error = storage_common_stat(fs_api, path, &fileinfo); + if((error == FSE_OK) && (fileinfo.flags & FSF_DIRECTORY)) { + File* dir = storage_file_alloc(fs_api); + if(storage_dir_open(dir, path)) { + char* name = furi_alloc(MAX_NAME_LENGTH); + is_dir_is_empty = !storage_dir_read(dir, &fileinfo, name, MAX_NAME_LENGTH); + free(name); + } + storage_dir_close(dir); + storage_file_free(dir); + } + + return is_dir_is_empty; +} + static void rpc_system_storage_delete_process(const PB_Main* request, void* context) { furi_assert(request); furi_assert(request->which_content == PB_Main_storage_delete_request_tag); furi_assert(context); RpcStorageSystem* rpc_storage = context; - PB_CommandStatus status; + PB_CommandStatus status = PB_CommandStatus_ERROR; rpc_system_storage_reset_state(rpc_storage, true); Storage* fs_api = furi_record_open("storage"); - char* path = request->content.storage_mkdir_request.path; - if(path) { - FS_Error error = storage_common_remove(fs_api, path); - status = rpc_system_storage_get_error(error); - } else { + + char* path = request->content.storage_delete_request.path; + if(!path) { status = PB_CommandStatus_ERROR_INVALID_PARAMETERS; + } else { + FS_Error error_remove = storage_common_remove(fs_api, path); + // FSE_DENIED is for empty directory, but not only for this + // that's why we have to check it + if((error_remove == FSE_DENIED) && !rpc_system_storage_is_dir_is_empty(fs_api, path)) { + if(request->content.storage_delete_request.recursive) { + bool deleted = storage_simply_remove_recursive(fs_api, path); + status = deleted ? PB_CommandStatus_OK : PB_CommandStatus_ERROR; + } else { + status = PB_CommandStatus_ERROR_STORAGE_DIR_NOT_EMPTY; + } + } else if(error_remove == FSE_NOT_EXIST) { + status = PB_CommandStatus_OK; + } else { + status = rpc_system_storage_get_error(error_remove); + } } - rpc_encode_and_send_empty(rpc_storage->rpc, request->command_id, status); + + furi_record_close("storage"); + rpc_send_and_release_empty(rpc_storage->rpc, request->command_id, status); } static void rpc_system_storage_mkdir_process(const PB_Main* request, void* context) { @@ -295,7 +355,7 @@ static void rpc_system_storage_mkdir_process(const PB_Main* request, void* conte } else { status = PB_CommandStatus_ERROR_INVALID_PARAMETERS; } - rpc_encode_and_send_empty(rpc_storage->rpc, request->command_id, status); + rpc_send_and_release_empty(rpc_storage->rpc, request->command_id, status); } static void rpc_system_storage_md5sum_process(const PB_Main* request, void* context) { @@ -307,7 +367,7 @@ static void rpc_system_storage_md5sum_process(const PB_Main* request, void* cont const char* filename = request->content.storage_md5sum_request.path; if(!filename) { - rpc_encode_and_send_empty( + rpc_send_and_release_empty( rpc_storage->rpc, request->command_id, PB_CommandStatus_ERROR_INVALID_PARAMETERS); return; } @@ -349,9 +409,9 @@ static void rpc_system_storage_md5sum_process(const PB_Main* request, void* cont free(hash); free(data); storage_file_close(file); - rpc_encode_and_send(rpc_storage->rpc, &response); + rpc_send_and_release(rpc_storage->rpc, &response); } else { - rpc_encode_and_send_empty( + rpc_send_and_release_empty( rpc_storage->rpc, request->command_id, rpc_system_storage_get_file_error(file)); } diff --git a/applications/storage/storage-external-api.c b/applications/storage/storage-external-api.c index aa11161e..d74cbb77 100644 --- a/applications/storage/storage-external-api.c +++ b/applications/storage/storage-external-api.c @@ -1,7 +1,11 @@ +#include +#include #include "storage.h" #include "storage-i.h" #include "storage-message.h" +#define MAX_NAME_LENGTH 256 + #define S_API_PROLOGUE \ osSemaphoreId_t semaphore = osSemaphoreNew(1, 0, NULL); \ furi_check(semaphore != NULL); @@ -382,6 +386,67 @@ void storage_file_free(File* file) { free(file); } +bool storage_simply_remove_recursive(Storage* storage, const char* path) { + furi_assert(storage); + furi_assert(path); + FileInfo fileinfo; + bool result = false; + string_t fullname; + string_t cur_dir; + + if(storage_simply_remove(storage, path)) { + return true; + } + + char* name = furi_alloc(MAX_NAME_LENGTH + 1); + File* dir = storage_file_alloc(storage); + string_init_set_str(cur_dir, path); + bool go_deeper = false; + + while(1) { + if(!storage_dir_open(dir, string_get_cstr(cur_dir))) { + storage_dir_close(dir); + break; + } + + while(storage_dir_read(dir, &fileinfo, name, MAX_NAME_LENGTH)) { + if(fileinfo.flags & FSF_DIRECTORY) { + string_cat_printf(cur_dir, "/%s", name); + go_deeper = true; + break; + } + + string_init_printf(fullname, "%s/%s", string_get_cstr(cur_dir), name); + FS_Error error = storage_common_remove(storage, string_get_cstr(fullname)); + furi_assert(error == FSE_OK); + string_clear(fullname); + } + storage_dir_close(dir); + + if(go_deeper) { + go_deeper = false; + continue; + } + + FS_Error error = storage_common_remove(storage, string_get_cstr(cur_dir)); + furi_assert(error == FSE_OK); + + if(string_cmp(cur_dir, path)) { + size_t last_char = string_search_rchar(cur_dir, '/'); + furi_assert(last_char != STRING_FAILURE); + string_left(cur_dir, last_char); + } else { + result = true; + break; + } + } + + storage_file_free(dir); + string_clear(cur_dir); + free(name); + return result; +} + bool storage_simply_remove(Storage* storage, const char* path) { FS_Error result; result = storage_common_remove(storage, path); @@ -392,4 +457,4 @@ bool storage_simply_mkdir(Storage* storage, const char* path) { FS_Error result; result = storage_common_mkdir(storage, path); return result == FSE_OK || result == FSE_EXIST; -} \ No newline at end of file +} diff --git a/applications/storage/storage.h b/applications/storage/storage.h index d38153bf..aabe8ddc 100644 --- a/applications/storage/storage.h +++ b/applications/storage/storage.h @@ -240,6 +240,14 @@ FS_Error storage_sd_status(Storage* api); */ bool storage_simply_remove(Storage* storage, const char* path); +/** + * Removes a file/directory from the repository, the directory can be not empty + * @param storage pointer to the api + * @param path + * @return true on success or if file/dir is not exist + */ +bool storage_simply_remove_recursive(Storage* storage, const char* path); + /** * Creates a directory * @param storage @@ -250,4 +258,4 @@ bool storage_simply_mkdir(Storage* storage, const char* path); #ifdef __cplusplus } -#endif \ No newline at end of file +#endif diff --git a/applications/storage/storages/storage-int.c b/applications/storage/storages/storage-int.c index 87c02786..fb4c07be 100644 --- a/applications/storage/storages/storage-int.c +++ b/applications/storage/storages/storage-int.c @@ -163,15 +163,15 @@ static LFSData* storage_int_lfs_data_alloc() { static void storage_int_lfs_mount(LFSData* lfs_data, StorageData* storage) { int err; - FuriHalBootFlag boot_flags = furi_hal_boot_get_flags(); + FuriHalBootloaderFlag bootloader_flags = furi_hal_bootloader_get_flags(); lfs_t* lfs = &lfs_data->lfs; - if(boot_flags & FuriHalBootFlagFactoryReset) { + if(bootloader_flags & FuriHalBootloaderFlagFactoryReset) { // Factory reset err = lfs_format(lfs, &lfs_data->config); if(err == 0) { FURI_LOG_I(TAG, "Factory reset: Format successful, trying to mount"); - furi_hal_boot_set_flags(boot_flags & ~FuriHalBootFlagFactoryReset); + furi_hal_bootloader_set_flags(bootloader_flags & ~FuriHalBootloaderFlagFactoryReset); err = lfs_mount(lfs, &lfs_data->config); if(err == 0) { FURI_LOG_I(TAG, "Factory reset: Mounted"); diff --git a/applications/subghz/helpers/subghz_custom_event.h b/applications/subghz/helpers/subghz_custom_event.h new file mode 100644 index 00000000..5a423e6f --- /dev/null +++ b/applications/subghz/helpers/subghz_custom_event.h @@ -0,0 +1,34 @@ +#pragma once + +typedef enum { + SubghzCustomEventManagerNoSet = 0, + SubghzCustomEventManagerSet, + + SubghzCustomEventSceneDeleteSuccess = 100, + SubghzCustomEventSceneDelete, + SubghzCustomEventSceneReceiverInfoTxStart, + SubghzCustomEventSceneReceiverInfoTxStop, + SubghzCustomEventSceneReceiverInfoSave, + SubghzCustomEventSceneSaveName, + SubghzCustomEventSceneSaveSuccess, + SubghzCustomEventSceneShowError, + SubghzCustomEventSceneShowOnlyRX, + + SubghzCustomEventSceneNeedSavingNo, + SubghzCustomEventSceneNeedSavingYes, + + SubghzCustomEventViewReceverOK, + SubghzCustomEventViewReceverConfig, + SubghzCustomEventViewReceverBack, + + SubghzCustomEventViewReadRAWBack, + SubghzCustomEventViewReadRAWIDLE, + SubghzCustomEventViewReadRAWREC, + SubghzCustomEventViewReadRAWConfig, + SubghzCustomEventViewReadRAWMore, + + SubghzCustomEventViewTransmitterBack, + SubghzCustomEventViewTransmitterSendStart, + SubghzCustomEventViewTransmitterSendStop, + SubghzCustomEventViewTransmitterError, +} SubghzCustomEvent; \ No newline at end of file diff --git a/applications/subghz/scenes/subghz_scene_config.h b/applications/subghz/scenes/subghz_scene_config.h index eca3cc35..6d89cb74 100644 --- a/applications/subghz/scenes/subghz_scene_config.h +++ b/applications/subghz/scenes/subghz_scene_config.h @@ -16,4 +16,7 @@ ADD_SCENE(subghz, test_static, TestStatic) ADD_SCENE(subghz, test_carrier, TestCarrier) ADD_SCENE(subghz, test_packet, TestPacket) ADD_SCENE(subghz, set_type, SetType) -ADD_SCENE(subghz, frequency_analyzer, FrequencyAnalyzer) \ No newline at end of file +ADD_SCENE(subghz, frequency_analyzer, FrequencyAnalyzer) +ADD_SCENE(subghz, read_raw, ReadRAW) +ADD_SCENE(subghz, read_raw_menu, ReadRAWMenu) +ADD_SCENE(subghz, need_saving, NeedSaving) \ No newline at end of file diff --git a/applications/subghz/scenes/subghz_scene_delete.c b/applications/subghz/scenes/subghz_scene_delete.c index 94782e88..e79e3a6d 100644 --- a/applications/subghz/scenes/subghz_scene_delete.c +++ b/applications/subghz/scenes/subghz_scene_delete.c @@ -1,15 +1,11 @@ #include "../subghz_i.h" - -typedef enum { - SubGhzSceneDeleteInfoCustomEventDelete, -} SubGhzSceneDeleteInfoCustomEvent; +#include "../helpers/subghz_custom_event.h" void subghz_scene_delete_callback(GuiButtonType result, InputType type, void* context) { furi_assert(context); SubGhz* subghz = context; if((result == GuiButtonTypeRight) && (type == InputTypeShort)) { - view_dispatcher_send_custom_event( - subghz->view_dispatcher, SubGhzSceneDeleteInfoCustomEventDelete); + view_dispatcher_send_custom_event(subghz->view_dispatcher, SubghzCustomEventSceneDelete); } } @@ -53,7 +49,7 @@ void subghz_scene_delete_on_enter(void* context) { bool subghz_scene_delete_on_event(void* context, SceneManagerEvent event) { SubGhz* subghz = context; if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubGhzSceneDeleteInfoCustomEventDelete) { + if(event.event == SubghzCustomEventSceneDelete) { memcpy(subghz->file_name_tmp, subghz->file_name, strlen(subghz->file_name)); if(subghz_delete_file(subghz)) { scene_manager_next_scene(subghz->scene_manager, SubGhzSceneDeleteSuccess); diff --git a/applications/subghz/scenes/subghz_scene_delete_success.c b/applications/subghz/scenes/subghz_scene_delete_success.c index bfafb7e5..04b48531 100644 --- a/applications/subghz/scenes/subghz_scene_delete_success.c +++ b/applications/subghz/scenes/subghz_scene_delete_success.c @@ -1,10 +1,10 @@ #include "../subghz_i.h" - -#define SCENE_DELETE_SUCCESS_CUSTOM_EVENT (0UL) +#include "../helpers/subghz_custom_event.h" void subghz_scene_delete_success_popup_callback(void* context) { SubGhz* subghz = context; - view_dispatcher_send_custom_event(subghz->view_dispatcher, SCENE_DELETE_SUCCESS_CUSTOM_EVENT); + view_dispatcher_send_custom_event( + subghz->view_dispatcher, SubghzCustomEventSceneDeleteSuccess); } void subghz_scene_delete_success_on_enter(void* context) { @@ -25,7 +25,7 @@ bool subghz_scene_delete_success_on_event(void* context, SceneManagerEvent event SubGhz* subghz = context; if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SCENE_DELETE_SUCCESS_CUSTOM_EVENT) { + if(event.event == SubghzCustomEventSceneDeleteSuccess) { return scene_manager_search_and_switch_to_previous_scene( subghz->scene_manager, SubGhzSceneStart); } diff --git a/applications/subghz/scenes/subghz_scene_frequency_analyzer.c b/applications/subghz/scenes/subghz_scene_frequency_analyzer.c index b67494f0..548a0f8f 100644 --- a/applications/subghz/scenes/subghz_scene_frequency_analyzer.c +++ b/applications/subghz/scenes/subghz_scene_frequency_analyzer.c @@ -1,7 +1,7 @@ #include "../subghz_i.h" #include "../views/subghz_frequency_analyzer.h" -void subghz_scene_frequency_analyzer_callback(SubghzFrequencyAnalyzerEvent event, void* context) { +void subghz_scene_frequency_analyzer_callback(SubghzCustomEvent event, void* context) { furi_assert(context); SubGhz* subghz = context; view_dispatcher_send_custom_event(subghz->view_dispatcher, event); @@ -15,13 +15,7 @@ void subghz_scene_frequency_analyzer_on_enter(void* context) { } bool subghz_scene_frequency_analyzer_on_event(void* context, SceneManagerEvent event) { - SubGhz* subghz = context; - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubghzFrequencyAnalyzerEventOnlyRx) { - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx); - return true; - } - } + //SubGhz* subghz = context; return false; } diff --git a/applications/subghz/scenes/subghz_scene_need_saving.c b/applications/subghz/scenes/subghz_scene_need_saving.c new file mode 100644 index 00000000..c7f83ce0 --- /dev/null +++ b/applications/subghz/scenes/subghz_scene_need_saving.c @@ -0,0 +1,63 @@ +#include "../subghz_i.h" +#include "../helpers/subghz_custom_event.h" + +void subghz_scene_need_saving_callback(GuiButtonType result, InputType type, void* context) { + furi_assert(context); + SubGhz* subghz = context; + + if((result == GuiButtonTypeRight) && (type == InputTypeShort)) { + view_dispatcher_send_custom_event( + subghz->view_dispatcher, SubghzCustomEventSceneNeedSavingYes); + } else if((result == GuiButtonTypeLeft) && (type == InputTypeShort)) { + view_dispatcher_send_custom_event( + subghz->view_dispatcher, SubghzCustomEventSceneNeedSavingNo); + } +} + +void subghz_scene_need_saving_on_enter(void* context) { + SubGhz* subghz = context; + + widget_add_string_multiline_element( + subghz->widget, + 64, + 25, + AlignCenter, + AlignCenter, + FontSecondary, + "There is an unsaved data.\nDo you want to save it?"); + + widget_add_button_element( + subghz->widget, GuiButtonTypeRight, "Save", subghz_scene_need_saving_callback, subghz); + widget_add_button_element( + subghz->widget, GuiButtonTypeLeft, "Delete", subghz_scene_need_saving_callback, subghz); + + view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewWidget); +} + +bool subghz_scene_need_saving_on_event(void* context, SceneManagerEvent event) { + SubGhz* subghz = context; + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubghzCustomEventSceneNeedSavingYes) { + subghz->txrx->rx_key_state = SubGhzRxKeyStateNeedSave; + scene_manager_previous_scene(subghz->scene_manager); + return true; + } else if(event.event == SubghzCustomEventSceneNeedSavingNo) { + if(subghz->txrx->rx_key_state == SubGhzRxKeyStateExit) { + subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; + scene_manager_search_and_switch_to_previous_scene( + subghz->scene_manager, SubGhzSceneStart); + } else { + subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; + scene_manager_previous_scene(subghz->scene_manager); + } + + return true; + } + } + return false; +} + +void subghz_scene_need_saving_on_exit(void* context) { + SubGhz* subghz = context; + widget_clear(subghz->widget); +} diff --git a/applications/subghz/scenes/subghz_scene_read_raw.c b/applications/subghz/scenes/subghz_scene_read_raw.c new file mode 100644 index 00000000..6d4a72d1 --- /dev/null +++ b/applications/subghz/scenes/subghz_scene_read_raw.c @@ -0,0 +1,190 @@ +#include "../subghz_i.h" +#include "../views/subghz_read_raw.h" +#include +#include + +static void subghz_scene_read_raw_update_statusbar(void* context) { + furi_assert(context); + SubGhz* subghz = context; + char frequency_str[20]; + char preset_str[10]; + + snprintf( + frequency_str, + sizeof(frequency_str), + "%03ld.%02ld", + subghz->txrx->frequency / 1000000 % 1000, + subghz->txrx->frequency / 10000 % 100); + if(subghz->txrx->preset == FuriHalSubGhzPresetOok650Async || + subghz->txrx->preset == FuriHalSubGhzPresetOok270Async) { + snprintf(preset_str, sizeof(preset_str), "AM"); + } else if( + subghz->txrx->preset == FuriHalSubGhzPreset2FSKDev238Async || + subghz->txrx->preset == FuriHalSubGhzPreset2FSKDev476Async) { + snprintf(preset_str, sizeof(preset_str), "FM"); + } else { + furi_crash(NULL); + } + + subghz_read_raw_add_data_statusbar(subghz->subghz_read_raw, frequency_str, preset_str); +} + +void subghz_scene_read_raw_callback(SubghzCustomEvent event, void* context) { + furi_assert(context); + SubGhz* subghz = context; + view_dispatcher_send_custom_event(subghz->view_dispatcher, event); +} + +void subghz_scene_read_raw_on_enter(void* context) { + SubGhz* subghz = context; + + if(subghz->txrx->rx_key_state == SubGhzRxKeyStateNeedSave) { + view_dispatcher_send_custom_event( + subghz->view_dispatcher, SubghzCustomEventViewReadRAWMore); + } else { + subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; + } + + subghz_scene_read_raw_update_statusbar(subghz); + subghz_read_raw_set_callback(subghz->subghz_read_raw, subghz_scene_read_raw_callback, subghz); + + subghz->txrx->protocol_result = subghz_parser_get_by_name(subghz->txrx->parser, "RAW"); + furi_assert(subghz->txrx->protocol_result); + + subghz_worker_set_pair_callback( + subghz->txrx->worker, (SubGhzWorkerPairCallback)subghz_parser_raw_parse); + + view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewReadRAW); +} + +bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { + SubGhz* subghz = context; + if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { + case SubghzCustomEventViewReadRAWBack: + if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { + subghz_rx_end(subghz); + subghz_sleep(subghz); + }; + subghz->txrx->frequency = subghz_frequencies[subghz_frequencies_433_92]; + subghz->txrx->preset = FuriHalSubGhzPresetOok650Async; + subghz_protocol_raw_save_to_file_stop( + (SubGhzProtocolRAW*)subghz->txrx->protocol_result); + subghz->state_notifications = NOTIFICATION_IDLE_STATE; + + if(subghz->txrx->rx_key_state == SubGhzRxKeyStateAddKey) { + subghz->txrx->rx_key_state = SubGhzRxKeyStateExit; + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneNeedSaving); + } else { + scene_manager_search_and_switch_to_previous_scene( + subghz->scene_manager, SubGhzSceneStart); + } + + return true; + break; + case SubghzCustomEventViewReadRAWConfig: + scene_manager_set_scene_state( + subghz->scene_manager, SubGhzSceneReadRAW, SubghzCustomEventManagerSet); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiverConfig); + return true; + break; + case SubghzCustomEventViewReadRAWIDLE: + if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { + subghz_rx_end(subghz); + subghz_sleep(subghz); + }; + subghz_protocol_raw_save_to_file_stop( + (SubGhzProtocolRAW*)subghz->txrx->protocol_result); + subghz->state_notifications = NOTIFICATION_IDLE_STATE; + + subghz->txrx->rx_key_state = SubGhzRxKeyStateAddKey; + + return true; + break; + case SubghzCustomEventViewReadRAWREC: + + if(subghz->txrx->rx_key_state != SubGhzRxKeyStateIDLE) { + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneNeedSaving); + } else { + if(subghz_protocol_raw_save_to_file_init( + (SubGhzProtocolRAW*)subghz->txrx->protocol_result, + "Raw_temp", + subghz->txrx->frequency, + subghz->txrx->preset)) { + if((subghz->txrx->txrx_state == SubGhzTxRxStateIDLE) || + (subghz->txrx->txrx_state == SubGhzTxRxStateSleep)) { + subghz_begin(subghz, subghz->txrx->preset); + subghz_rx(subghz, subghz->txrx->frequency); + } + subghz->state_notifications = NOTIFICATION_RX_STATE; + } else { + string_set(subghz->error_str, "No SD card"); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); + } + } + + return true; + break; + case SubghzCustomEventViewReadRAWMore: + if(strcmp( + subghz_protocol_get_last_file_name( + (SubGhzProtocolRAW*)subghz->txrx->protocol_result), + "")) { + strlcpy( + subghz->file_name, + subghz_protocol_get_last_file_name( + (SubGhzProtocolRAW*)subghz->txrx->protocol_result), + strlen(subghz_protocol_get_last_file_name( + (SubGhzProtocolRAW*)subghz->txrx->protocol_result)) + + 1); + //set the path to read the file + string_t temp_str; + string_init_printf( + temp_str, + "%s/%s%s", + SUBGHZ_APP_PATH_FOLDER, + subghz->file_name, + SUBGHZ_APP_EXTENSION); + subghz_protocol_set_last_file_name( + (SubGhzProtocolRAW*)subghz->txrx->protocol_result, string_get_cstr(temp_str)); + string_clear(temp_str); + + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReadRAWMenu); + } + return true; + break; + + default: + break; + } + } else if(event.type == SceneManagerEventTypeTick) { + switch(subghz->state_notifications) { + case NOTIFICATION_RX_STATE: + notification_message(subghz->notifications, &sequence_blink_blue_10); + subghz_read_raw_update_sample_write( + subghz->subghz_read_raw, + subghz_protocol_raw_get_sample_write( + (SubGhzProtocolRAW*)subghz->txrx->protocol_result)); + subghz_read_raw_add_data_rssi(subghz->subghz_read_raw, furi_hal_subghz_get_rssi()); + break; + default: + break; + } + } + return false; +} + +void subghz_scene_read_raw_on_exit(void* context) { + SubGhz* subghz = context; + + //Stop CC1101 + if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { + subghz_rx_end(subghz); + subghz_sleep(subghz); + }; + subghz->state_notifications = NOTIFICATION_IDLE_STATE; + + //Сallback restoration + subghz_worker_set_pair_callback( + subghz->txrx->worker, (SubGhzWorkerPairCallback)subghz_parser_parse); +} diff --git a/applications/subghz/scenes/subghz_scene_read_raw_menu.c b/applications/subghz/scenes/subghz_scene_read_raw_menu.c new file mode 100644 index 00000000..2c6deefc --- /dev/null +++ b/applications/subghz/scenes/subghz_scene_read_raw_menu.c @@ -0,0 +1,72 @@ +#include "../subghz_i.h" + +enum SubmenuIndex { + SubmenuIndexEmulate, + SubmenuIndexEdit, + SubmenuIndexDelete, +}; + +void subghz_scene_read_raw_menu_submenu_callback(void* context, uint32_t index) { + SubGhz* subghz = context; + view_dispatcher_send_custom_event(subghz->view_dispatcher, index); +} + +void subghz_scene_read_raw_menu_on_enter(void* context) { + SubGhz* subghz = context; + submenu_add_item( + subghz->submenu, + "Emulate", + SubmenuIndexEmulate, + subghz_scene_read_raw_menu_submenu_callback, + subghz); + + submenu_add_item( + subghz->submenu, + "Save", + SubmenuIndexEdit, + subghz_scene_read_raw_menu_submenu_callback, + subghz); + + submenu_add_item( + subghz->submenu, + "Delete", + SubmenuIndexDelete, + subghz_scene_read_raw_menu_submenu_callback, + subghz); + + submenu_set_selected_item( + subghz->submenu, + scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneSavedMenu)); + + view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewMenu); +} + +bool subghz_scene_read_raw_menu_on_event(void* context, SceneManagerEvent event) { + SubGhz* subghz = context; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexEmulate) { + scene_manager_set_scene_state( + subghz->scene_manager, SubGhzSceneReadRAWMenu, SubmenuIndexEmulate); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneTransmitter); + return true; + } else if(event.event == SubmenuIndexDelete) { + scene_manager_set_scene_state( + subghz->scene_manager, SubGhzSceneReadRAWMenu, SubmenuIndexDelete); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneDelete); + return true; + } else if(event.event == SubmenuIndexEdit) { + scene_manager_set_scene_state( + subghz->scene_manager, SubGhzSceneReadRAWMenu, SubghzCustomEventManagerSet); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName); + return true; + } + } + return false; +} + +void subghz_scene_read_raw_menu_on_exit(void* context) { + SubGhz* subghz = context; + submenu_clean(subghz->submenu); + subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; +} diff --git a/applications/subghz/scenes/subghz_scene_receiver.c b/applications/subghz/scenes/subghz_scene_receiver.c index fe29c0cf..107381c2 100644 --- a/applications/subghz/scenes/subghz_scene_receiver.c +++ b/applications/subghz/scenes/subghz_scene_receiver.c @@ -34,7 +34,7 @@ static void subghz_scene_receiver_update_statusbar(void* context) { string_clear(history_stat_str); } -void subghz_scene_receiver_callback(SubghzReceverEvent event, void* context) { +void subghz_scene_receiver_callback(SubghzCustomEvent event, void* context) { furi_assert(context); SubGhz* subghz = context; view_dispatcher_send_custom_event(subghz->view_dispatcher, event); @@ -87,7 +87,7 @@ void subghz_scene_receiver_on_enter(void* context) { if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { subghz_rx_end(subghz); }; - if((subghz->txrx->txrx_state == SubGhzTxRxStateIdle) || + if((subghz->txrx->txrx_state == SubGhzTxRxStateIDLE) || (subghz->txrx->txrx_state == SubGhzTxRxStateSleep)) { subghz_begin(subghz, subghz->txrx->preset); subghz_rx(subghz, subghz->txrx->frequency); @@ -102,8 +102,9 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { switch(event.event) { - case SubghzReceverEventBack: + case SubghzCustomEventViewReceverBack: // Stop CC1101 Rx + subghz->state_notifications = NOTIFICATION_IDLE_STATE; if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { subghz_rx_end(subghz); subghz_sleep(subghz); @@ -118,12 +119,12 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) { subghz->scene_manager, SubGhzSceneStart); return true; break; - case SubghzReceverEventOK: + case SubghzCustomEventViewReceverOK: subghz->txrx->idx_menu_chosen = subghz_receiver_get_idx_menu(subghz->subghz_receiver); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiverInfo); return true; break; - case SubghzReceverEventConfig: + case SubghzCustomEventViewReceverConfig: subghz->state_notifications = NOTIFICATION_IDLE_STATE; subghz->txrx->idx_menu_chosen = subghz_receiver_get_idx_menu(subghz->subghz_receiver); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiverConfig); diff --git a/applications/subghz/scenes/subghz_scene_receiver_config.c b/applications/subghz/scenes/subghz_scene_receiver_config.c index 3991a999..4243b09b 100644 --- a/applications/subghz/scenes/subghz_scene_receiver_config.c +++ b/applications/subghz/scenes/subghz_scene_receiver_config.c @@ -66,6 +66,8 @@ static void subghz_scene_receiver_config_set_frequency(VariableItem* item) { if(subghz->txrx->hopper_state == SubGhzHopperStateOFF) { variable_item_set_current_value_text(item, subghz_frequencies_text[index]); subghz->txrx->frequency = subghz_frequencies[index]; + } else { + variable_item_set_current_value_index(item, subghz_frequencies_433_92); } } @@ -88,22 +90,24 @@ static void subghz_scene_receiver_config_set_hopping_runing(VariableItem* item) subghz->scene_manager, SubGhzSceneReceiverConfig), subghz_frequencies_text[subghz_frequencies_433_92]); subghz->txrx->frequency = subghz_frequencies[subghz_frequencies_433_92]; + variable_item_set_current_value_index( + (VariableItem*)scene_manager_get_scene_state( + subghz->scene_manager, SubGhzSceneReceiverConfig), + subghz_frequencies_433_92); } else { variable_item_set_current_value_text( (VariableItem*)scene_manager_get_scene_state( subghz->scene_manager, SubGhzSceneReceiverConfig), " -----"); + variable_item_set_current_value_index( + (VariableItem*)scene_manager_get_scene_state( + subghz->scene_manager, SubGhzSceneReceiverConfig), + subghz_frequencies_433_92); } subghz->txrx->hopper_state = hopping_value[index]; } -void subghz_scene_receiver_config_callback(SubghzReceverEvent event, void* context) { - furi_assert(context); - SubGhz* subghz = context; - view_dispatcher_send_custom_event(subghz->view_dispatcher, event); -} - void subghz_scene_receiver_config_on_enter(void* context) { SubGhz* subghz = context; VariableItem* item; @@ -122,16 +126,19 @@ void subghz_scene_receiver_config_on_enter(void* context) { variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, subghz_frequencies_text[value_index]); - item = variable_item_list_add( - subghz->variable_item_list, - "Hopping:", - HOPPING_COUNT, - subghz_scene_receiver_config_set_hopping_runing, - subghz); - value_index = subghz_scene_receiver_config_hopper_value_index( - subghz->txrx->hopper_state, hopping_value, HOPPING_COUNT, subghz); - variable_item_set_current_value_index(item, value_index); - variable_item_set_current_value_text(item, hopping_text[value_index]); + if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) != + SubghzCustomEventManagerSet) { + item = variable_item_list_add( + subghz->variable_item_list, + "Hopping:", + HOPPING_COUNT, + subghz_scene_receiver_config_set_hopping_runing, + subghz); + value_index = subghz_scene_receiver_config_hopper_value_index( + subghz->txrx->hopper_state, hopping_value, HOPPING_COUNT, subghz); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, hopping_text[value_index]); + } item = variable_item_list_add( subghz->variable_item_list, @@ -155,4 +162,6 @@ bool subghz_scene_receiver_config_on_event(void* context, SceneManagerEvent even void subghz_scene_receiver_config_on_exit(void* context) { SubGhz* subghz = context; variable_item_list_clean(subghz->variable_item_list); + scene_manager_set_scene_state( + subghz->scene_manager, SubGhzSceneReadRAW, SubghzCustomEventManagerNoSet); } diff --git a/applications/subghz/scenes/subghz_scene_receiver_info.c b/applications/subghz/scenes/subghz_scene_receiver_info.c index 6b33a686..1e9f100a 100644 --- a/applications/subghz/scenes/subghz_scene_receiver_info.c +++ b/applications/subghz/scenes/subghz_scene_receiver_info.c @@ -1,10 +1,5 @@ #include "../subghz_i.h" - -typedef enum { - SubGhzSceneReceiverInfoCustomEventTxStart, - SubGhzSceneReceiverInfoCustomEventTxStop, - SubGhzSceneReceiverInfoCustomEventSave, -} SubGhzSceneReceiverInfoCustomEvent; +#include "../helpers/subghz_custom_event.h" void subghz_scene_receiver_info_callback(GuiButtonType result, InputType type, void* context) { furi_assert(context); @@ -12,13 +7,13 @@ void subghz_scene_receiver_info_callback(GuiButtonType result, InputType type, v if((result == GuiButtonTypeCenter) && (type == InputTypePress)) { view_dispatcher_send_custom_event( - subghz->view_dispatcher, SubGhzSceneReceiverInfoCustomEventTxStart); + subghz->view_dispatcher, SubghzCustomEventSceneReceiverInfoTxStart); } else if((result == GuiButtonTypeCenter) && (type == InputTypeRelease)) { view_dispatcher_send_custom_event( - subghz->view_dispatcher, SubGhzSceneReceiverInfoCustomEventTxStop); + subghz->view_dispatcher, SubghzCustomEventSceneReceiverInfoTxStop); } else if((result == GuiButtonTypeRight) && (type == InputTypeShort)) { view_dispatcher_send_custom_event( - subghz->view_dispatcher, SubGhzSceneReceiverInfoCustomEventSave); + subghz->view_dispatcher, SubghzCustomEventSceneReceiverInfoSave); } } @@ -101,7 +96,7 @@ void subghz_scene_receiver_info_on_enter(void* context) { bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event) { SubGhz* subghz = context; if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubGhzSceneReceiverInfoCustomEventTxStart) { + if(event.event == SubghzCustomEventSceneReceiverInfoTxStart) { //CC1101 Stop RX -> Start TX if(subghz->txrx->hopper_state != SubGhzHopperStateOFF) { subghz->txrx->hopper_state = SubGhzHopperStatePause; @@ -112,7 +107,8 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event) if(!subghz_scene_receiver_info_update_parser(subghz)) { return false; } - if(subghz->txrx->txrx_state == SubGhzTxRxStateIdle) { + if(subghz->txrx->txrx_state == SubGhzTxRxStateIDLE || + subghz->txrx->txrx_state == SubGhzTxRxStateSleep) { if(!subghz_tx_start(subghz)) { scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx); } else { @@ -120,13 +116,13 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event) } } return true; - } else if(event.event == SubGhzSceneReceiverInfoCustomEventTxStop) { + } else if(event.event == SubghzCustomEventSceneReceiverInfoTxStop) { //CC1101 Stop Tx -> Start RX subghz->state_notifications = NOTIFICATION_IDLE_STATE; if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { subghz_tx_stop(subghz); } - if(subghz->txrx->txrx_state == SubGhzTxRxStateIdle) { + if(subghz->txrx->txrx_state == SubGhzTxRxStateIDLE) { subghz_begin(subghz, subghz->txrx->preset); subghz_rx(subghz, subghz->txrx->frequency); } @@ -135,7 +131,7 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event) } subghz->state_notifications = NOTIFICATION_RX_STATE; return true; - } else if(event.event == SubGhzSceneReceiverInfoCustomEventSave) { + } else if(event.event == SubghzCustomEventSceneReceiverInfoSave) { //CC1101 Stop RX -> Save subghz->state_notifications = NOTIFICATION_IDLE_STATE; if(subghz->txrx->hopper_state != SubGhzHopperStateOFF) { diff --git a/applications/subghz/scenes/subghz_scene_save_name.c b/applications/subghz/scenes/subghz_scene_save_name.c index ee21238a..6cba3240 100644 --- a/applications/subghz/scenes/subghz_scene_save_name.c +++ b/applications/subghz/scenes/subghz_scene_save_name.c @@ -1,12 +1,11 @@ #include "../subghz_i.h" #include #include "file-worker.h" - -#define SCENE_SAVE_NAME_CUSTOM_EVENT (0UL) +#include "../helpers/subghz_custom_event.h" void subghz_scene_save_name_text_input_callback(void* context) { SubGhz* subghz = context; - view_dispatcher_send_custom_event(subghz->view_dispatcher, SCENE_SAVE_NAME_CUSTOM_EVENT); + view_dispatcher_send_custom_event(subghz->view_dispatcher, SubghzCustomEventSceneSaveName); } void subghz_scene_save_name_on_enter(void* context) { @@ -21,6 +20,10 @@ void subghz_scene_save_name_on_enter(void* context) { dev_name_empty = true; } else { memcpy(subghz->file_name_tmp, subghz->file_name, strlen(subghz->file_name)); + if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAWMenu) == + SubghzCustomEventManagerSet) { + subghz_get_next_name_file(subghz); + } } text_input_set_header_text(text_input, "Name signal"); @@ -38,12 +41,14 @@ bool subghz_scene_save_name_on_event(void* context, SceneManagerEvent event) { SubGhz* subghz = context; if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SCENE_SAVE_NAME_CUSTOM_EVENT) { - if(strcmp(subghz->file_name, "") && - subghz_save_protocol_to_file(subghz, subghz->file_name)) { + if(event.event == SubghzCustomEventSceneSaveName) { + if(strcmp(subghz->file_name, "")) { if(strcmp(subghz->file_name_tmp, "")) { - subghz_delete_file(subghz); + subghz_rename_file(subghz); + } else { + subghz_save_protocol_to_file(subghz, subghz->file_name); } + subghz_file_name_clear(subghz); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveSuccess); return true; @@ -62,4 +67,6 @@ void subghz_scene_save_name_on_exit(void* context) { // Clear view text_input_clean(subghz->text_input); + scene_manager_set_scene_state( + subghz->scene_manager, SubGhzSceneReadRAWMenu, SubghzCustomEventManagerNoSet); } diff --git a/applications/subghz/scenes/subghz_scene_save_success.c b/applications/subghz/scenes/subghz_scene_save_success.c index dc267486..31e7d3ee 100644 --- a/applications/subghz/scenes/subghz_scene_save_success.c +++ b/applications/subghz/scenes/subghz_scene_save_success.c @@ -1,10 +1,9 @@ #include "../subghz_i.h" - -#define SCENE_SAVE_SUCCESS_CUSTOM_EVENT (0UL) +#include "../helpers/subghz_custom_event.h" void subghz_scene_save_success_popup_callback(void* context) { SubGhz* subghz = context; - view_dispatcher_send_custom_event(subghz->view_dispatcher, SCENE_SAVE_SUCCESS_CUSTOM_EVENT); + view_dispatcher_send_custom_event(subghz->view_dispatcher, SubghzCustomEventSceneSaveSuccess); } void subghz_scene_save_success_on_enter(void* context) { @@ -24,7 +23,7 @@ void subghz_scene_save_success_on_enter(void* context) { bool subghz_scene_save_success_on_event(void* context, SceneManagerEvent event) { SubGhz* subghz = context; if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SCENE_SAVE_SUCCESS_CUSTOM_EVENT) { + if(event.event == SubghzCustomEventSceneSaveSuccess) { if(!scene_manager_search_and_switch_to_previous_scene( subghz->scene_manager, SubGhzSceneReceiver)) { scene_manager_search_and_switch_to_previous_scene( diff --git a/applications/subghz/scenes/subghz_scene_saved_menu.c b/applications/subghz/scenes/subghz_scene_saved_menu.c index 59d7e3e8..a25f8e6f 100644 --- a/applications/subghz/scenes/subghz_scene_saved_menu.c +++ b/applications/subghz/scenes/subghz_scene_saved_menu.c @@ -19,12 +19,14 @@ void subghz_scene_saved_menu_on_enter(void* context) { SubmenuIndexEmulate, subghz_scene_saved_menu_submenu_callback, subghz); + submenu_add_item( subghz->submenu, "Edit name", SubmenuIndexEdit, subghz_scene_saved_menu_submenu_callback, subghz); + submenu_add_item( subghz->submenu, "Delete", diff --git a/applications/subghz/scenes/subghz_scene_show_error.c b/applications/subghz/scenes/subghz_scene_show_error.c index 2599f24e..c02ad34a 100644 --- a/applications/subghz/scenes/subghz_scene_show_error.c +++ b/applications/subghz/scenes/subghz_scene_show_error.c @@ -1,10 +1,9 @@ #include "../subghz_i.h" - -#define SCENE_NO_MAN_CUSTOM_EVENT (11UL) +#include "../helpers/subghz_custom_event.h" void subghz_scene_show_error_popup_callback(void* context) { SubGhz* subghz = context; - view_dispatcher_send_custom_event(subghz->view_dispatcher, SCENE_NO_MAN_CUSTOM_EVENT); + view_dispatcher_send_custom_event(subghz->view_dispatcher, SubghzCustomEventSceneShowError); } void subghz_scene_show_error_on_enter(void* context) { @@ -24,7 +23,7 @@ void subghz_scene_show_error_on_enter(void* context) { bool subghz_scene_show_error_on_event(void* context, SceneManagerEvent event) { SubGhz* subghz = context; if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SCENE_NO_MAN_CUSTOM_EVENT) { + if(event.event == SubghzCustomEventSceneShowError) { scene_manager_search_and_switch_to_previous_scene( subghz->scene_manager, SubGhzSceneStart); return true; diff --git a/applications/subghz/scenes/subghz_scene_show_only_rx.c b/applications/subghz/scenes/subghz_scene_show_only_rx.c index e59e1d68..a62969d4 100644 --- a/applications/subghz/scenes/subghz_scene_show_only_rx.c +++ b/applications/subghz/scenes/subghz_scene_show_only_rx.c @@ -1,10 +1,9 @@ #include "../subghz_i.h" - -#define SCENE_NO_MAN_CUSTOM_EVENT (11UL) +#include "../helpers/subghz_custom_event.h" void subghz_scene_show_only_rx_popup_callback(void* context) { SubGhz* subghz = context; - view_dispatcher_send_custom_event(subghz->view_dispatcher, SCENE_NO_MAN_CUSTOM_EVENT); + view_dispatcher_send_custom_event(subghz->view_dispatcher, SubghzCustomEventSceneShowOnlyRX); } void subghz_scene_show_only_rx_on_enter(void* context) { @@ -30,7 +29,7 @@ void subghz_scene_show_only_rx_on_enter(void* context) { const bool subghz_scene_show_only_rx_on_event(void* context, SceneManagerEvent event) { SubGhz* subghz = context; if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SCENE_NO_MAN_CUSTOM_EVENT) { + if(event.event == SubghzCustomEventSceneShowOnlyRX) { scene_manager_previous_scene(subghz->scene_manager); return true; } diff --git a/applications/subghz/scenes/subghz_scene_start.c b/applications/subghz/scenes/subghz_scene_start.c index c131a0a8..fe37d7ae 100644 --- a/applications/subghz/scenes/subghz_scene_start.c +++ b/applications/subghz/scenes/subghz_scene_start.c @@ -1,11 +1,12 @@ #include "../subghz_i.h" enum SubmenuIndex { - SubmenuIndexRead, + SubmenuIndexRead = 10, SubmenuIndexSaved, SubmenuIndexTest, SubmenuIndexAddManualy, SubmenuIndexFrequencyAnalyzer, + SubmenuIndexReadRAW, }; void subghz_scene_start_submenu_callback(void* context, uint32_t index) { @@ -20,6 +21,12 @@ void subghz_scene_start_on_enter(void* context) { } submenu_add_item( subghz->submenu, "Read", SubmenuIndexRead, subghz_scene_start_submenu_callback, subghz); + submenu_add_item( + subghz->submenu, + "Read Raw", + SubmenuIndexReadRAW, + subghz_scene_start_submenu_callback, + subghz); submenu_add_item( subghz->submenu, "Saved", SubmenuIndexSaved, subghz_scene_start_submenu_callback, subghz); submenu_add_item( @@ -47,7 +54,12 @@ bool subghz_scene_start_on_event(void* context, SceneManagerEvent event) { SubGhz* subghz = context; if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubmenuIndexRead) { + if(event.event == SubmenuIndexReadRAW) { + scene_manager_set_scene_state( + subghz->scene_manager, SubGhzSceneStart, SubmenuIndexReadRAW); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReadRAW); + return true; + } else if(event.event == SubmenuIndexRead) { scene_manager_set_scene_state( subghz->scene_manager, SubGhzSceneStart, SubmenuIndexRead); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiver); diff --git a/applications/subghz/scenes/subghz_scene_transmitter.c b/applications/subghz/scenes/subghz_scene_transmitter.c index 4aa5f87f..28d49397 100644 --- a/applications/subghz/scenes/subghz_scene_transmitter.c +++ b/applications/subghz/scenes/subghz_scene_transmitter.c @@ -2,13 +2,13 @@ #include "../views/subghz_transmitter.h" #include -void subghz_scene_transmitter_callback(SubghzTransmitterEvent event, void* context) { +void subghz_scene_transmitter_callback(SubghzCustomEvent event, void* context) { furi_assert(context); SubGhz* subghz = context; view_dispatcher_send_custom_event(subghz->view_dispatcher, event); } -static void subghz_scene_transmitter_update_data_show(void* context) { +bool subghz_scene_transmitter_update_data_show(void* context) { SubGhz* subghz = context; if(subghz->txrx->protocol_result && subghz->txrx->protocol_result->get_upload_protocol) { @@ -51,17 +51,22 @@ static void subghz_scene_transmitter_update_data_show(void* context) { preset_str, show_button); string_clear(key_str); - } else { - string_set(subghz->error_str, "Protocol not found"); - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); + + return true; } + return false; } void subghz_scene_transmitter_on_enter(void* context) { SubGhz* subghz = context; + if(!subghz_scene_transmitter_update_data_show(subghz)) { + view_dispatcher_send_custom_event( + subghz->view_dispatcher, SubghzCustomEventViewTransmitterError); + } + subghz_transmitter_set_callback( subghz->subghz_transmitter, subghz_scene_transmitter_callback, subghz); - subghz_scene_transmitter_update_data_show(subghz); + subghz->state_notifications = NOTIFICATION_IDLE_STATE; view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewTransmitter); } @@ -69,12 +74,12 @@ void subghz_scene_transmitter_on_enter(void* context) { bool subghz_scene_transmitter_on_event(void* context, SceneManagerEvent event) { SubGhz* subghz = context; if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubghzTransmitterEventSendStart) { + if(event.event == SubghzCustomEventViewTransmitterSendStart) { subghz->state_notifications = NOTIFICATION_IDLE_STATE; if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { subghz_rx_end(subghz); } - if((subghz->txrx->txrx_state == SubGhzTxRxStateIdle) || + if((subghz->txrx->txrx_state == SubGhzTxRxStateIDLE) || (subghz->txrx->txrx_state == SubGhzTxRxStateSleep)) { if(!subghz_tx_start(subghz)) { scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx); @@ -84,18 +89,21 @@ bool subghz_scene_transmitter_on_event(void* context, SceneManagerEvent event) { } } return true; - } else if(event.event == SubghzTransmitterEventSendStop) { + } else if(event.event == SubghzCustomEventViewTransmitterSendStop) { subghz->state_notifications = NOTIFICATION_IDLE_STATE; if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { subghz_tx_stop(subghz); subghz_sleep(subghz); } return true; - } else if(event.event == SubghzTransmitterEventBack) { + } else if(event.event == SubghzCustomEventViewTransmitterBack) { subghz->state_notifications = NOTIFICATION_IDLE_STATE; scene_manager_search_and_switch_to_previous_scene( subghz->scene_manager, SubGhzSceneStart); return true; + } else if(event.event == SubghzCustomEventViewTransmitterError) { + string_set(subghz->error_str, "Protocol not found"); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); } } else if(event.type == SceneManagerEventTypeTick) { if(subghz->state_notifications == NOTIFICATION_TX_STATE) { diff --git a/applications/subghz/subghz.c b/applications/subghz/subghz.c index 7c71e25f..63156717 100644 --- a/applications/subghz/subghz.c +++ b/applications/subghz/subghz.c @@ -140,6 +140,13 @@ SubGhz* subghz_alloc() { SubGhzViewFrequencyAnalyzer, subghz_frequency_analyzer_get_view(subghz->subghz_frequency_analyzer)); + // Read RAW + subghz->subghz_read_raw = subghz_read_raw_alloc(); + view_dispatcher_add_view( + subghz->view_dispatcher, + SubGhzViewReadRAW, + subghz_read_raw_get_view(subghz->subghz_read_raw)); + // Carrier Test Module subghz->subghz_test_carrier = subghz_test_carrier_alloc(); view_dispatcher_add_view( @@ -167,6 +174,7 @@ SubGhz* subghz_alloc() { subghz->txrx->preset = FuriHalSubGhzPresetOok650Async; subghz->txrx->txrx_state = SubGhzTxRxStateSleep; subghz->txrx->hopper_state = SubGhzHopperStateOFF; + subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; subghz->txrx->history = subghz_history_alloc(); subghz->txrx->worker = subghz_worker_alloc(); subghz->txrx->parser = subghz_parser_alloc(); @@ -181,6 +189,7 @@ SubGhz* subghz_alloc() { subghz_parser_load_keeloq_file(subghz->txrx->parser, "/ext/subghz/keeloq_mfcodes"); subghz_parser_load_nice_flor_s_file(subghz->txrx->parser, "/ext/subghz/nice_floor_s_rx"); + subghz_parser_load_came_atomo_file(subghz->txrx->parser, "/ext/subghz/came_atomo"); //subghz_parser_enable_dump_text(subghz->protocol, subghz_text_callback, subghz); @@ -226,6 +235,10 @@ void subghz_free(SubGhz* subghz) { view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewFrequencyAnalyzer); subghz_frequency_analyzer_free(subghz->subghz_frequency_analyzer); + // Read RAW + view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewReadRAW); + subghz_read_raw_free(subghz->subghz_read_raw); + // Submenu view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewMenu); submenu_free(subghz->submenu); diff --git a/applications/subghz/subghz_i.c b/applications/subghz/subghz_i.c index 2f849e41..fe8bfff1 100644 --- a/applications/subghz/subghz_i.c +++ b/applications/subghz/subghz_i.c @@ -16,7 +16,7 @@ void subghz_begin(SubGhz* subghz, FuriHalSubGhzPreset preset) { furi_hal_subghz_idle(); furi_hal_subghz_load_preset(preset); hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); - subghz->txrx->txrx_state = SubGhzTxRxStateIdle; + subghz->txrx->txrx_state = SubGhzTxRxStateIDLE; } uint32_t subghz_rx(SubGhz* subghz, uint32_t frequency) { @@ -59,7 +59,7 @@ void subghz_idle(SubGhz* subghz) { furi_assert(subghz); furi_assert(subghz->txrx->txrx_state != SubGhzTxRxStateSleep); furi_hal_subghz_idle(); - subghz->txrx->txrx_state = SubGhzTxRxStateIdle; + subghz->txrx->txrx_state = SubGhzTxRxStateIDLE; } void subghz_rx_end(SubGhz* subghz) { @@ -70,7 +70,7 @@ void subghz_rx_end(SubGhz* subghz) { furi_hal_subghz_stop_async_rx(); } furi_hal_subghz_idle(); - subghz->txrx->txrx_state = SubGhzTxRxStateIdle; + subghz->txrx->txrx_state = SubGhzTxRxStateIDLE; } void subghz_sleep(SubGhz* subghz) { @@ -191,7 +191,7 @@ bool subghz_key_load(SubGhz* subghz, const char* file_path) { break; } if(!subghz->txrx->protocol_result->to_load_protocol_from_file( - file_worker, subghz->txrx->protocol_result)) { + file_worker, subghz->txrx->protocol_result, string_get_cstr(path))) { break; } loaded = true; @@ -208,6 +208,30 @@ bool subghz_key_load(SubGhz* subghz, const char* file_path) { return loaded; } +bool subghz_get_next_name_file(SubGhz* subghz) { + furi_assert(subghz); + + FileWorker* file_worker = file_worker_alloc(false); + string_t temp_str; + string_init(temp_str); + bool res = false; + + if(strcmp(subghz->file_name, "")) { + //get the name of the next free file + file_worker_get_next_filename( + file_worker, SUBGHZ_RAW_PATH_FOLDER, subghz->file_name, SUBGHZ_APP_EXTENSION, temp_str); + + memcpy(subghz->file_name, string_get_cstr(temp_str), strlen(string_get_cstr(temp_str))); + res = true; + } + + string_clear(temp_str); + file_worker_close(file_worker); + file_worker_free(file_worker); + + return res; +} + bool subghz_save_protocol_to_file(SubGhz* subghz, const char* dev_name) { furi_assert(subghz); furi_assert(subghz->txrx->protocol_result); @@ -334,8 +358,10 @@ bool subghz_load_protocol_from_file(SubGhz* subghz) { if(subghz->txrx->protocol_result == NULL) { break; } - if(!subghz->txrx->protocol_result->to_load_protocol_from_file( - file_worker, subghz->txrx->protocol_result)) { + + if(subghz->txrx->protocol_result->to_load_protocol_from_file == NULL || + !subghz->txrx->protocol_result->to_load_protocol_from_file( + file_worker, subghz->txrx->protocol_result, string_get_cstr(protocol_file_name))) { break; } res = true; @@ -354,6 +380,28 @@ bool subghz_load_protocol_from_file(SubGhz* subghz) { return res; } +bool subghz_rename_file(SubGhz* subghz) { + furi_assert(subghz); + bool ret = false; + string_t old_path; + string_t new_path; + + FileWorker* file_worker = file_worker_alloc(false); + + string_init_printf( + old_path, "%s/%s%s", SUBGHZ_APP_PATH_FOLDER, subghz->file_name_tmp, SUBGHZ_APP_EXTENSION); + + string_init_printf( + new_path, "%s/%s%s", SUBGHZ_APP_PATH_FOLDER, subghz->file_name, SUBGHZ_APP_EXTENSION); + + ret = file_worker_rename(file_worker, string_get_cstr(old_path), string_get_cstr(new_path)); + string_clear(old_path); + string_clear(new_path); + file_worker_close(file_worker); + file_worker_free(file_worker); + return ret; +} + bool subghz_delete_file(SubGhz* subghz) { furi_assert(subghz); @@ -442,7 +490,7 @@ void subghz_hopper_update(SubGhz* subghz) { if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { subghz_rx_end(subghz); }; - if(subghz->txrx->txrx_state == SubGhzTxRxStateIdle) { + if(subghz->txrx->txrx_state == SubGhzTxRxStateIDLE) { subghz_parser_reset(subghz->txrx->parser); subghz->txrx->frequency = subghz_hopper_frequencies[subghz->txrx->hopper_idx_frequency]; subghz_rx(subghz, subghz->txrx->frequency); diff --git a/applications/subghz/subghz_i.h b/applications/subghz/subghz_i.h index 88be9203..70a39f23 100644 --- a/applications/subghz/subghz_i.h +++ b/applications/subghz/subghz_i.h @@ -4,6 +4,7 @@ #include "views/subghz_receiver.h" #include "views/subghz_transmitter.h" #include "views/subghz_frequency_analyzer.h" +#include "views/subghz_read_raw.h" #include "views/subghz_test_static.h" #include "views/subghz_test_carrier.h" @@ -46,7 +47,7 @@ extern const uint32_t subghz_frequencies_433_92; /** SubGhzTxRx state */ typedef enum { - SubGhzTxRxStateIdle, + SubGhzTxRxStateIDLE, SubGhzTxRxStateRx, SubGhzTxRxStateTx, SubGhzTxRxStateSleep, @@ -60,6 +61,15 @@ typedef enum { SubGhzHopperStateRSSITimeOut, } SubGhzHopperState; +/** SubGhzRxKeyState state */ +typedef enum { + SubGhzRxKeyStateIDLE, + SubGhzRxKeyStateNoSave, + SubGhzRxKeyStateNeedSave, + SubGhzRxKeyStateAddKey, + SubGhzRxKeyStateExit, +} SubGhzRxKeyState; + struct SubGhzTxRx { SubGhzWorker* worker; SubGhzParser* parser; @@ -70,10 +80,10 @@ struct SubGhzTxRx { SubGhzHistory* history; uint16_t idx_menu_chosen; SubGhzTxRxState txrx_state; - //bool hopper_runing; SubGhzHopperState hopper_state; uint8_t hopper_timeout; uint8_t hopper_idx_frequency; + SubGhzRxKeyState rx_key_state; }; typedef struct SubGhzTxRx SubGhzTxRx; @@ -100,6 +110,7 @@ struct SubGhz { VariableItemList* variable_item_list; SubghzFrequencyAnalyzer* subghz_frequency_analyzer; + SubghzReadRAW* subghz_read_raw; SubghzTestStatic* subghz_test_static; SubghzTestCarrier* subghz_test_carrier; SubghzTestPacket* subghz_test_packet; @@ -116,6 +127,7 @@ typedef enum { SubGhzViewTransmitter, SubGhzViewVariableItemList, SubGhzViewFrequencyAnalyzer, + SubGhzViewReadRAW, SubGhzViewStatic, SubGhzViewTestCarrier, SubGhzViewTestPacket, @@ -128,8 +140,10 @@ void subghz_sleep(SubGhz* subghz); bool subghz_tx_start(SubGhz* subghz); void subghz_tx_stop(SubGhz* subghz); bool subghz_key_load(SubGhz* subghz, const char* file_path); +bool subghz_get_next_name_file(SubGhz* subghz); bool subghz_save_protocol_to_file(SubGhz* subghz, const char* dev_name); bool subghz_load_protocol_from_file(SubGhz* subghz); +bool subghz_rename_file(SubGhz* subghz); bool subghz_delete_file(SubGhz* subghz); void subghz_file_name_clear(SubGhz* subghz); uint32_t subghz_random_serial(void); diff --git a/applications/subghz/views/subghz_frequency_analyzer.h b/applications/subghz/views/subghz_frequency_analyzer.h index ebfcb173..78280503 100644 --- a/applications/subghz/views/subghz_frequency_analyzer.h +++ b/applications/subghz/views/subghz_frequency_analyzer.h @@ -1,14 +1,11 @@ #pragma once #include - -typedef enum { - SubghzFrequencyAnalyzerEventOnlyRx, -} SubghzFrequencyAnalyzerEvent; +#include "../helpers/subghz_custom_event.h" typedef struct SubghzFrequencyAnalyzer SubghzFrequencyAnalyzer; -typedef void (*SubghzFrequencyAnalyzerCallback)(SubghzFrequencyAnalyzerEvent event, void* context); +typedef void (*SubghzFrequencyAnalyzerCallback)(SubghzCustomEvent event, void* context); void subghz_frequency_analyzer_set_callback( SubghzFrequencyAnalyzer* subghz_frequency_analyzer, diff --git a/applications/subghz/views/subghz_read_raw.c b/applications/subghz/views/subghz_read_raw.c new file mode 100644 index 00000000..7c24654b --- /dev/null +++ b/applications/subghz/views/subghz_read_raw.c @@ -0,0 +1,274 @@ +#include "subghz_read_raw.h" +#include "../subghz_i.h" + +#include +#include +#include +#include +#include +#include + +#include +#define SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE 100 + +typedef enum { + SubghzReadRAWStatusStart, + SubghzReadRAWStatusIDLE, + SubghzReadRAWStatusREC, + //SubghzReadRAWStatusShowName, +} SubghzReadRAWStatus; + +struct SubghzReadRAW { + View* view; + SubghzReadRAWCallback callback; + void* context; +}; + +typedef struct { + string_t frequency_str; + string_t preset_str; + string_t sample_write; + uint8_t* rssi_history; + bool rssi_history_end; + uint8_t ind_write; + SubghzReadRAWStatus satus; +} SubghzReadRAWModel; + +void subghz_read_raw_set_callback( + SubghzReadRAW* subghz_read_raw, + SubghzReadRAWCallback callback, + void* context) { + furi_assert(subghz_read_raw); + furi_assert(callback); + subghz_read_raw->callback = callback; + subghz_read_raw->context = context; +} + +void subghz_read_raw_add_data_statusbar( + SubghzReadRAW* instance, + const char* frequency_str, + const char* preset_str) { + furi_assert(instance); + with_view_model( + instance->view, (SubghzReadRAWModel * model) { + string_set(model->frequency_str, frequency_str); + string_set(model->preset_str, preset_str); + return true; + }); +} + +void subghz_read_raw_add_data_rssi(SubghzReadRAW* instance, float rssi) { + furi_assert(instance); + uint8_t u_rssi = 0; + + if(rssi < -90) { + u_rssi = 0; + } else { + u_rssi = (uint8_t)((rssi + 90) / 2.7); + } + //if(u_rssi > 34) u_rssi = 34; + + with_view_model( + instance->view, (SubghzReadRAWModel * model) { + model->rssi_history[model->ind_write++] = u_rssi; + if(model->ind_write > SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE) { + model->rssi_history_end = true; + model->ind_write = 0; + } + return true; + }); +} + +void subghz_read_raw_update_sample_write(SubghzReadRAW* instance, size_t sample) { + furi_assert(instance); + + with_view_model( + instance->view, (SubghzReadRAWModel * model) { + string_printf(model->sample_write, "%d spl.", sample); + return false; + }); +} + +void subghz_read_raw_draw_rssi(Canvas* canvas, SubghzReadRAWModel* model) { + int ind = 0; + int base = 0; + if(model->rssi_history_end == false) { + for(int i = model->ind_write; i >= 0; i--) { + canvas_draw_line(canvas, i, 47, i, 47 - model->rssi_history[i]); + } + if(model->ind_write > 3) { + canvas_draw_line(canvas, model->ind_write, 47, model->ind_write, 13); + canvas_draw_line(canvas, model->ind_write - 2, 12, model->ind_write + 2, 12); + canvas_draw_line(canvas, model->ind_write - 1, 13, model->ind_write + 1, 13); + } + } else { + base = SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE - model->ind_write; + for(int i = SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE; i >= 0; i--) { + ind = i - base; + if(ind < 0) ind += SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE; + canvas_draw_line(canvas, i, 47, i, 47 - model->rssi_history[ind]); + } + canvas_draw_line( + canvas, SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE, 47, SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE, 13); + canvas_draw_line( + canvas, + SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE - 2, + 12, + SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE + 2, + 12); + canvas_draw_line( + canvas, + SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE - 1, + 13, + SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE + 1, + 13); + } +} + +void subghz_read_raw_draw(Canvas* canvas, SubghzReadRAWModel* model) { + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 5, 8, string_get_cstr(model->frequency_str)); + canvas_draw_str(canvas, 40, 8, string_get_cstr(model->preset_str)); + canvas_draw_str_aligned( + canvas, 126, 0, AlignRight, AlignTop, string_get_cstr(model->sample_write)); + + canvas_draw_line(canvas, 0, 14, 115, 14); + subghz_read_raw_draw_rssi(canvas, model); + canvas_draw_line(canvas, 0, 48, 115, 48); + canvas_draw_line(canvas, 115, 14, 115, 48); + + if(model->satus == SubghzReadRAWStatusIDLE) { + elements_button_left(canvas, "Config"); + elements_button_center(canvas, "REC"); + elements_button_right(canvas, "More"); + } else if(model->satus == SubghzReadRAWStatusStart) { + elements_button_left(canvas, "Config"); + elements_button_center(canvas, "REC"); + } else { + elements_button_center(canvas, "Stop"); + } + + canvas_set_font_direction(canvas, 3); + canvas_draw_str(canvas, 126, 40, "RSSI"); + canvas_set_font_direction(canvas, 0); +} + +bool subghz_read_raw_input(InputEvent* event, void* context) { + furi_assert(context); + SubghzReadRAW* instance = context; + + if(event->key == InputKeyBack && event->type == InputTypeShort) { + instance->callback(SubghzCustomEventViewReadRAWBack, instance->context); + } else if(event->key == InputKeyLeft && event->type == InputTypeShort) { + with_view_model( + instance->view, (SubghzReadRAWModel * model) { + if(model->satus == SubghzReadRAWStatusIDLE || + model->satus == SubghzReadRAWStatusStart) { + instance->callback(SubghzCustomEventViewReadRAWConfig, instance->context); + } + return true; + }); + } else if(event->key == InputKeyRight && event->type == InputTypeShort) { + with_view_model( + instance->view, (SubghzReadRAWModel * model) { + if(model->satus == SubghzReadRAWStatusIDLE) { + instance->callback(SubghzCustomEventViewReadRAWMore, instance->context); + } + return true; + }); + } else if(event->key == InputKeyOk && event->type == InputTypeShort) { + with_view_model( + instance->view, (SubghzReadRAWModel * model) { + if(model->satus == SubghzReadRAWStatusIDLE || + model->satus == SubghzReadRAWStatusStart) { + instance->callback(SubghzCustomEventViewReadRAWREC, instance->context); + model->satus = SubghzReadRAWStatusREC; + model->ind_write = 0; + model->rssi_history_end = false; + } else { + instance->callback(SubghzCustomEventViewReadRAWIDLE, instance->context); + model->satus = SubghzReadRAWStatusIDLE; + } + return true; + }); + } + + return true; +} + +void subghz_read_raw_enter(void* context) { + furi_assert(context); + SubghzReadRAW* instance = context; + + with_view_model( + instance->view, (SubghzReadRAWModel * model) { + model->satus = SubghzReadRAWStatusStart; + model->rssi_history = furi_alloc(SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE * sizeof(uint8_t)); + model->rssi_history_end = false; + model->ind_write = 0; + string_set(model->sample_write, "0 spl."); + return true; + }); +} + +void subghz_read_raw_exit(void* context) { + furi_assert(context); + SubghzReadRAW* instance = context; + + with_view_model( + instance->view, (SubghzReadRAWModel * model) { + if(model->satus != SubghzReadRAWStatusIDLE && + model->satus != SubghzReadRAWStatusStart) { + instance->callback(SubghzCustomEventViewReadRAWIDLE, instance->context); + model->satus = SubghzReadRAWStatusStart; + } + string_clean(model->frequency_str); + string_clean(model->preset_str); + string_clean(model->sample_write); + free(model->rssi_history); + return true; + }); +} + +SubghzReadRAW* subghz_read_raw_alloc() { + SubghzReadRAW* instance = furi_alloc(sizeof(SubghzReadRAW)); + + // View allocation and configuration + instance->view = view_alloc(); + view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(SubghzReadRAWModel)); + view_set_context(instance->view, instance); + view_set_draw_callback(instance->view, (ViewDrawCallback)subghz_read_raw_draw); + view_set_input_callback(instance->view, subghz_read_raw_input); + view_set_enter_callback(instance->view, subghz_read_raw_enter); + view_set_exit_callback(instance->view, subghz_read_raw_exit); + + with_view_model( + instance->view, (SubghzReadRAWModel * model) { + string_init(model->frequency_str); + string_init(model->preset_str); + string_init(model->sample_write); + return true; + }); + + return instance; +} + +void subghz_read_raw_free(SubghzReadRAW* instance) { + furi_assert(instance); + + with_view_model( + instance->view, (SubghzReadRAWModel * model) { + string_clear(model->frequency_str); + string_clear(model->preset_str); + string_clear(model->sample_write); + return true; + }); + view_free(instance->view); + free(instance); +} + +View* subghz_read_raw_get_view(SubghzReadRAW* instance) { + furi_assert(instance); + return instance->view; +} \ No newline at end of file diff --git a/applications/subghz/views/subghz_read_raw.h b/applications/subghz/views/subghz_read_raw.h new file mode 100644 index 00000000..d6a2337f --- /dev/null +++ b/applications/subghz/views/subghz_read_raw.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include "../helpers/subghz_custom_event.h" + +typedef struct SubghzReadRAW SubghzReadRAW; + +typedef void (*SubghzReadRAWCallback)(SubghzCustomEvent event, void* context); + +void subghz_read_raw_set_callback( + SubghzReadRAW* subghz_read_raw, + SubghzReadRAWCallback callback, + void* context); + +SubghzReadRAW* subghz_read_raw_alloc(); + +void subghz_read_raw_free(SubghzReadRAW* subghz_static); + +void subghz_read_raw_add_data_statusbar( + SubghzReadRAW* instance, + const char* frequency_str, + const char* preset_str); + +void subghz_read_raw_update_sample_write(SubghzReadRAW* instance, size_t sample); + +void subghz_read_raw_add_data_rssi(SubghzReadRAW* instance, float rssi); + +View* subghz_read_raw_get_view(SubghzReadRAW* subghz_static); diff --git a/applications/subghz/views/subghz_receiver.c b/applications/subghz/views/subghz_receiver.c index 26aec5e7..f854e81d 100644 --- a/applications/subghz/views/subghz_receiver.c +++ b/applications/subghz/views/subghz_receiver.c @@ -181,7 +181,7 @@ bool subghz_receiver_input(InputEvent* event, void* context) { SubghzReceiver* subghz_receiver = context; if(event->key == InputKeyBack && event->type == InputTypeShort) { - subghz_receiver->callback(SubghzReceverEventBack, subghz_receiver->context); + subghz_receiver->callback(SubghzCustomEventViewReceverBack, subghz_receiver->context); } else if( event->key == InputKeyUp && (event->type == InputTypeShort || event->type == InputTypeRepeat)) { @@ -199,12 +199,13 @@ bool subghz_receiver_input(InputEvent* event, void* context) { return true; }); } else if(event->key == InputKeyLeft && event->type == InputTypeShort) { - subghz_receiver->callback(SubghzReceverEventConfig, subghz_receiver->context); + subghz_receiver->callback(SubghzCustomEventViewReceverConfig, subghz_receiver->context); } else if(event->key == InputKeyOk && event->type == InputTypeShort) { with_view_model( subghz_receiver->view, (SubghzReceiverModel * model) { if(model->history_item != 0) { - subghz_receiver->callback(SubghzReceverEventOK, subghz_receiver->context); + subghz_receiver->callback( + SubghzCustomEventViewReceverOK, subghz_receiver->context); } return false; }); diff --git a/applications/subghz/views/subghz_receiver.h b/applications/subghz/views/subghz_receiver.h index be9a9ef1..e26ee2c2 100644 --- a/applications/subghz/views/subghz_receiver.h +++ b/applications/subghz/views/subghz_receiver.h @@ -1,16 +1,11 @@ #pragma once #include - -typedef enum { - SubghzReceverEventOK, - SubghzReceverEventConfig, - SubghzReceverEventBack, -} SubghzReceverEvent; +#include "../helpers/subghz_custom_event.h" typedef struct SubghzReceiver SubghzReceiver; -typedef void (*SubghzReceiverCallback)(SubghzReceverEvent event, void* context); +typedef void (*SubghzReceiverCallback)(SubghzCustomEvent event, void* context); void subghz_receiver_set_callback( SubghzReceiver* subghz_receiver, diff --git a/applications/subghz/views/subghz_transmitter.c b/applications/subghz/views/subghz_transmitter.c index 6e29f5dd..b5adf3e9 100644 --- a/applications/subghz/views/subghz_transmitter.c +++ b/applications/subghz/views/subghz_transmitter.c @@ -111,10 +111,12 @@ bool subghz_transmitter_input(InputEvent* event, void* context) { }); if(can_be_sent && event->key == InputKeyOk && event->type == InputTypePress) { - subghz_transmitter->callback(SubghzTransmitterEventSendStart, subghz_transmitter->context); + subghz_transmitter->callback( + SubghzCustomEventViewTransmitterSendStart, subghz_transmitter->context); return true; } else if(can_be_sent && event->key == InputKeyOk && event->type == InputTypeRelease) { - subghz_transmitter->callback(SubghzTransmitterEventSendStop, subghz_transmitter->context); + subghz_transmitter->callback( + SubghzCustomEventViewTransmitterSendStop, subghz_transmitter->context); return true; } diff --git a/applications/subghz/views/subghz_transmitter.h b/applications/subghz/views/subghz_transmitter.h index 8ddeaaf8..995e08f6 100644 --- a/applications/subghz/views/subghz_transmitter.h +++ b/applications/subghz/views/subghz_transmitter.h @@ -1,17 +1,11 @@ #pragma once #include - -typedef enum { - SubghzTransmitterEventSendStart, - SubghzTransmitterEventSendStop, - SubghzTransmitterEventBack, - SubghzTransmitterEventNoMan, -} SubghzTransmitterEvent; +#include "../helpers/subghz_custom_event.h" typedef struct SubghzTransmitter SubghzTransmitter; -typedef void (*SubghzTransmitterCallback)(SubghzTransmitterEvent event, void* context); +typedef void (*SubghzTransmitterCallback)(SubghzCustomEvent event, void* context); void subghz_transmitter_set_callback( SubghzTransmitter* subghz_transmitter, diff --git a/applications/tests/furi_record_test.c b/applications/tests/furi_record_test.c index 7f8a0507..e7eebe4b 100644 --- a/applications/tests/furi_record_test.c +++ b/applications/tests/furi_record_test.c @@ -11,7 +11,10 @@ void test_furi_create_open() { // 2. Open it void* record = furi_record_open("test/holding"); mu_assert_pointers_eq(record, &test_data); + + // 3. Close it furi_record_close("test/holding"); + // 4. Clean up furi_record_destroy("test/holding"); } diff --git a/applications/tests/irda_decoder_encoder/irda_decoder_encoder_test.c b/applications/tests/irda_decoder_encoder/irda_decoder_encoder_test.c index 908e196d..80f036b0 100644 --- a/applications/tests/irda_decoder_encoder/irda_decoder_encoder_test.c +++ b/applications/tests/irda_decoder_encoder/irda_decoder_encoder_test.c @@ -325,7 +325,6 @@ MU_TEST_SUITE(test_irda_decoder_encoder) { int run_minunit_test_irda_decoder_encoder() { MU_RUN_SUITE(test_irda_decoder_encoder); - MU_REPORT(); return MU_EXIT_CODE; } diff --git a/applications/tests/minunit.h b/applications/tests/minunit.h index 466cf9c1..b12a87ca 100644 --- a/applications/tests/minunit.h +++ b/applications/tests/minunit.h @@ -79,6 +79,9 @@ extern "C" { __attribute__((unused)) static void (*minunit_setup)(void) = NULL; __attribute__((unused)) static void (*minunit_teardown)(void) = NULL; +void minunit_print_progress(void); +void minunit_print_fail(const char* error); + /* Definitions */ #define MU_TEST(method_name) static void method_name(void) #define MU_TEST_SUITE(suite_name) static void suite_name(void) @@ -108,8 +111,7 @@ __attribute__((unused)) static void (*minunit_teardown)(void) = NULL; minunit_run++; \ if(minunit_status) { \ minunit_fail++; \ - printf("F"); \ - printf("\n%s\n", minunit_last_message); \ + minunit_print_fail(minunit_last_message); \ } fflush(stdout); \ if(minunit_teardown)(*minunit_teardown)();) @@ -142,7 +144,7 @@ __attribute__((unused)) static void (*minunit_teardown)(void) = NULL; #test); \ minunit_status = 1; \ return; \ - } else { printf("."); }) + } else { minunit_print_progress(); }) #define mu_fail(message) \ MU__SAFE_BLOCK(minunit_assert++; snprintf( \ @@ -169,7 +171,7 @@ __attribute__((unused)) static void (*minunit_teardown)(void) = NULL; message); \ minunit_status = 1; \ return; \ - } else { printf("."); }) + } else { minunit_print_progress(); }) #define mu_assert_int_eq(expected, result) \ MU__SAFE_BLOCK( \ @@ -187,7 +189,7 @@ __attribute__((unused)) static void (*minunit_teardown)(void) = NULL; minunit_tmp_r); \ minunit_status = 1; \ return; \ - } else { printf("."); }) + } else { minunit_print_progress(); }) #define mu_assert_int_not_eq(expected, result) \ MU__SAFE_BLOCK( \ @@ -204,7 +206,7 @@ __attribute__((unused)) static void (*minunit_teardown)(void) = NULL; minunit_tmp_e); \ minunit_status = 1; \ return; \ - } else { printf("."); }) + } else { minunit_print_progress(); }) #define mu_assert_int_greater_than(val, result) \ MU__SAFE_BLOCK( \ @@ -222,7 +224,7 @@ __attribute__((unused)) static void (*minunit_teardown)(void) = NULL; minunit_tmp_e); \ minunit_status = 1; \ return; \ - } else { printf("."); }) + } else { minunit_print_progress(); }) #define mu_assert_int_less_than(val, result) \ MU__SAFE_BLOCK( \ @@ -240,7 +242,7 @@ __attribute__((unused)) static void (*minunit_teardown)(void) = NULL; minunit_tmp_e); \ minunit_status = 1; \ return; \ - } else { printf("."); }) + } else { minunit_print_progress(); }) #define mu_assert_int_between(expected_lower, expected_upper, result) \ MU__SAFE_BLOCK( \ @@ -261,7 +263,7 @@ __attribute__((unused)) static void (*minunit_teardown)(void) = NULL; minunit_tmp_m); \ minunit_status = 1; \ return; \ - } else { printf("."); }) + } else { minunit_print_progress(); }) #define mu_assert_int_in(expected, array_length, result) \ MU__SAFE_BLOCK( \ @@ -288,7 +290,7 @@ __attribute__((unused)) static void (*minunit_teardown)(void) = NULL; minunit_tmp_r); \ minunit_status = 1; \ return; \ - } else { printf("."); }) + } else { minunit_print_progress(); }) #define mu_assert_double_eq(expected, result) \ MU__SAFE_BLOCK( \ @@ -309,7 +311,7 @@ __attribute__((unused)) static void (*minunit_teardown)(void) = NULL; minunit_tmp_r); \ minunit_status = 1; \ return; \ - } else { printf("."); }) + } else { minunit_print_progress(); }) #define mu_assert_double_greater_than(val, result) \ MU__SAFE_BLOCK( \ @@ -327,7 +329,7 @@ __attribute__((unused)) static void (*minunit_teardown)(void) = NULL; minunit_tmp_e); \ minunit_status = 1; \ return; \ - } else { printf("."); }) + } else { minunit_print_progress(); }) #define mu_assert_double_less_than(val, result) \ MU__SAFE_BLOCK( \ @@ -345,7 +347,7 @@ __attribute__((unused)) static void (*minunit_teardown)(void) = NULL; minunit_tmp_e); \ minunit_status = 1; \ return; \ - } else { printf("."); }) + } else { minunit_print_progress(); }) #define mu_assert_double_between(expected_lower, expected_upper, result) \ MU__SAFE_BLOCK( \ @@ -366,7 +368,7 @@ __attribute__((unused)) static void (*minunit_teardown)(void) = NULL; minunit_tmp_m); \ minunit_status = 1; \ return; \ - } else { printf("."); }) + } else { minunit_print_progress(); }) #define mu_assert_string_eq(expected, result) \ MU__SAFE_BLOCK( \ @@ -386,39 +388,39 @@ __attribute__((unused)) static void (*minunit_teardown)(void) = NULL; minunit_tmp_r); \ minunit_status = 1; \ return; \ - } else { printf("."); }) + } else { minunit_print_progress(); }) -#define mu_assert_null(result) \ - MU__SAFE_BLOCK( \ - minunit_assert++; if(result == NULL) { printf("."); } else { \ - snprintf( \ - minunit_last_message, \ - MINUNIT_MESSAGE_LEN, \ - "%s failed:\n\t%s:%d: Expected result was not NULL", \ - __func__, \ - __FILE__, \ - __LINE__); \ - minunit_status = 1; \ - return; \ +#define mu_assert_null(result) \ + MU__SAFE_BLOCK( \ + minunit_assert++; if(result == NULL) { minunit_print_progress(); } else { \ + snprintf( \ + minunit_last_message, \ + MINUNIT_MESSAGE_LEN, \ + "%s failed:\n\t%s:%d: Expected result was not NULL", \ + __func__, \ + __FILE__, \ + __LINE__); \ + minunit_status = 1; \ + return; \ }) -#define mu_assert_not_null(result) \ - MU__SAFE_BLOCK( \ - minunit_assert++; if(result != NULL) { printf("."); } else { \ - snprintf( \ - minunit_last_message, \ - MINUNIT_MESSAGE_LEN, \ - "%s failed:\n\t%s:%d: Expected result was not NULL", \ - __func__, \ - __FILE__, \ - __LINE__); \ - minunit_status = 1; \ - return; \ +#define mu_assert_not_null(result) \ + MU__SAFE_BLOCK( \ + minunit_assert++; if(result != NULL) { minunit_print_progress(); } else { \ + snprintf( \ + minunit_last_message, \ + MINUNIT_MESSAGE_LEN, \ + "%s failed:\n\t%s:%d: Expected result was not NULL", \ + __func__, \ + __FILE__, \ + __LINE__); \ + minunit_status = 1; \ + return; \ }) #define mu_assert_pointers_eq(pointer1, pointer2) \ MU__SAFE_BLOCK( \ - minunit_assert++; if(pointer1 == pointer2) { printf("."); } else { \ + minunit_assert++; if(pointer1 == pointer2) { minunit_print_progress(); } else { \ snprintf( \ minunit_last_message, \ MINUNIT_MESSAGE_LEN, \ @@ -432,7 +434,7 @@ __attribute__((unused)) static void (*minunit_teardown)(void) = NULL; #define mu_assert_pointers_not_eq(pointer1, pointer2) \ MU__SAFE_BLOCK( \ - minunit_assert++; if(pointer1 != pointer2) { printf("."); } else { \ + minunit_assert++; if(pointer1 != pointer2) { minunit_print_progress(); } else { \ snprintf( \ minunit_last_message, \ MINUNIT_MESSAGE_LEN, \ @@ -603,4 +605,4 @@ __attribute__((unused)) static double mu_timer_cpu(void) { } #endif -#endif /* MINUNIT_MINUNIT_H */ \ No newline at end of file +#endif /* MINUNIT_MINUNIT_H */ diff --git a/applications/tests/minunit_test.c b/applications/tests/minunit_test.c index 8c94c845..1e71fd07 100644 --- a/applications/tests/minunit_test.c +++ b/applications/tests/minunit_test.c @@ -62,7 +62,6 @@ MU_TEST_SUITE(test_suite) { int run_minunit() { MU_RUN_SUITE(test_suite); - MU_REPORT(); return MU_EXIT_CODE; } diff --git a/applications/tests/rpc/rpc_test.c b/applications/tests/rpc/rpc_test.c index 18f1c7a8..3f59cebf 100644 --- a/applications/tests/rpc/rpc_test.c +++ b/applications/tests/rpc/rpc_test.c @@ -56,25 +56,40 @@ static void test_rpc_compare_messages(PB_Main* result, PB_Main* expected); static void test_rpc_decode_and_compare(MsgList_t expected_msg_list); static void test_rpc_free_msg_list(MsgList_t msg_list); -static void test_rpc_storage_setup(void) { +static void test_rpc_setup(void) { furi_assert(!rpc); furi_assert(!session); furi_assert(!output_stream); rpc = furi_record_open("rpc"); for(int i = 0; !session && (i < 10000); ++i) { - session = rpc_open_session(rpc); + session = rpc_session_open(rpc); delay(1); } furi_assert(session); + output_stream = xStreamBufferCreate(1000, 1); + mu_assert(session, "failed to start session"); + rpc_session_set_send_bytes_callback(session, output_bytes_callback); + rpc_session_set_context(session, output_stream); +} + +static void test_rpc_teardown(void) { + rpc_session_close(session); + furi_record_close("rpc"); + vStreamBufferDelete(output_stream); + ++command_id; + output_stream = NULL; + rpc = NULL; + session = NULL; +} + +static void test_rpc_storage_setup(void) { + test_rpc_setup(); + Storage* fs_api = furi_record_open("storage"); clean_directory(fs_api, TEST_DIR_NAME); furi_record_close("storage"); - - output_stream = xStreamBufferCreate(1000, 1); - mu_assert(session, "failed to start session"); - rpc_set_send_bytes_callback(session, output_bytes_callback, output_stream); } static void test_rpc_storage_teardown(void) { @@ -82,13 +97,7 @@ static void test_rpc_storage_teardown(void) { clean_directory(fs_api, TEST_DIR_NAME); furi_record_close("storage"); - rpc_close_session(session); - furi_record_close("rpc"); - vStreamBufferDelete(output_stream); - ++command_id; - output_stream = NULL; - rpc = NULL; - session = NULL; + test_rpc_teardown(); } static void clean_directory(Storage* fs_api, const char* clean_dir) { @@ -197,10 +206,12 @@ static void test_rpc_create_simple_message( const char* str, uint32_t command_id) { furi_assert(message); - furi_assert(str); - char* str_copy = furi_alloc(strlen(str) + 1); - strcpy(str_copy, str); + char* str_copy = NULL; + if(str) { + str_copy = furi_alloc(strlen(str) + 1); + strcpy(str_copy, str); + } message->command_id = command_id; message->command_status = PB_CommandStatus_OK; message->cb_content.funcs.encode = NULL; @@ -292,7 +303,7 @@ static void test_rpc_encode_and_feed_one(PB_Main* request) { size_t bytes_left = ostream.bytes_written; uint8_t* buffer_ptr = buffer; do { - size_t bytes_sent = rpc_feed_bytes(session, buffer_ptr, bytes_left, 1000); + size_t bytes_sent = rpc_session_feed(session, buffer_ptr, bytes_left, 1000); mu_check(bytes_sent > 0); bytes_left -= bytes_sent; @@ -402,6 +413,38 @@ static bool test_rpc_pb_stream_read(pb_istream_t* istream, pb_byte_t* buf, size_ return (count == bytes_received); } +static void + test_rpc_storage_list_create_expected_list_root(MsgList_t msg_list, uint32_t command_id) { + PB_Main* message = MsgList_push_new(msg_list); + message->has_next = false; + message->cb_content.funcs.encode = NULL; + message->command_id = command_id; + message->which_content = PB_Main_storage_list_response_tag; + + message->content.storage_list_response.file_count = 3; + message->content.storage_list_response.file[0].data = NULL; + message->content.storage_list_response.file[1].data = NULL; + message->content.storage_list_response.file[2].data = NULL; + + message->content.storage_list_response.file[0].size = 0; + message->content.storage_list_response.file[1].size = 0; + message->content.storage_list_response.file[2].size = 0; + + message->content.storage_list_response.file[0].type = PB_Storage_File_FileType_DIR; + message->content.storage_list_response.file[1].type = PB_Storage_File_FileType_DIR; + message->content.storage_list_response.file[2].type = PB_Storage_File_FileType_DIR; + + char* str = furi_alloc(4); + strcpy(str, "any"); + message->content.storage_list_response.file[0].name = str; + str = furi_alloc(4); + strcpy(str, "int"); + message->content.storage_list_response.file[1].name = str; + str = furi_alloc(4); + strcpy(str, "ext"); + message->content.storage_list_response.file[2].name = str; +} + static void test_rpc_storage_list_create_expected_list( MsgList_t msg_list, const char* path, @@ -505,7 +548,11 @@ static void test_rpc_storage_list_run(const char* path, uint32_t command_id) { MsgList_init(expected_msg_list); test_rpc_create_simple_message(&request, PB_Main_storage_list_request_tag, path, command_id); - test_rpc_storage_list_create_expected_list(expected_msg_list, path, command_id); + if(!strcmp(path, "/")) { + test_rpc_storage_list_create_expected_list_root(expected_msg_list, command_id); + } else { + test_rpc_storage_list_create_expected_list(expected_msg_list, path, command_id); + } test_rpc_encode_and_feed_one(&request); test_rpc_decode_and_compare(expected_msg_list); @@ -514,6 +561,7 @@ static void test_rpc_storage_list_run(const char* path, uint32_t command_id) { } MU_TEST(test_storage_list) { + test_rpc_storage_list_run("/", ++command_id); test_rpc_storage_list_run("/ext/nfc", ++command_id); test_rpc_storage_list_run("/int", ++command_id); @@ -597,12 +645,23 @@ static void test_storage_read_run(const char* path, uint32_t command_id) { test_rpc_free_msg_list(expected_msg_list); } +static bool test_is_exists(const char* path) { + Storage* fs_api = furi_record_open("storage"); + FileInfo fileinfo; + FS_Error result = storage_common_stat(fs_api, path, &fileinfo); + + furi_check((result == FSE_OK) || (result == FSE_NOT_EXIST)); + + return result == FSE_OK; +} + static void test_create_dir(const char* path) { Storage* fs_api = furi_record_open("storage"); FS_Error error = storage_common_mkdir(fs_api, path); (void)error; furi_assert((error == FSE_OK) || (error == FSE_EXIST)); furi_record_close("storage"); + furi_check(test_is_exists(path)); } static void test_create_file(const char* path, size_t size) { @@ -625,6 +684,7 @@ static void test_create_file(const char* path, size_t size) { storage_file_free(file); furi_record_close("storage"); + furi_check(test_is_exists(path)); } MU_TEST(test_storage_read) { @@ -829,12 +889,17 @@ MU_TEST(test_storage_interrupt_continuous_another_system) { test_rpc_free_msg_list(expected_msg_list); } -static void test_storage_delete_run(const char* path, size_t command_id, PB_CommandStatus status) { +static void test_storage_delete_run( + const char* path, + size_t command_id, + PB_CommandStatus status, + bool recursive) { PB_Main request; MsgList_t expected_msg_list; MsgList_init(expected_msg_list); test_rpc_create_simple_message(&request, PB_Main_storage_delete_request_tag, path, command_id); + request.content.storage_delete_request.recursive = recursive; test_rpc_add_empty_to_list(expected_msg_list, status, command_id); test_rpc_encode_and_feed_one(&request); @@ -844,16 +909,69 @@ static void test_storage_delete_run(const char* path, size_t command_id, PB_Comm test_rpc_free_msg_list(expected_msg_list); } -MU_TEST(test_storage_delete) { - test_create_file(TEST_DIR "empty.txt", 0); - test_storage_delete_run(TEST_DIR "empty.txt", ++command_id, PB_CommandStatus_OK); - test_storage_delete_run( - TEST_DIR "empty.txt", ++command_id, PB_CommandStatus_ERROR_STORAGE_NOT_EXIST); +#define TEST_DIR_RMRF_NAME TEST_DIR "rmrf_test" +#define TEST_DIR_RMRF TEST_DIR_RMRF_NAME "/" +MU_TEST(test_storage_delete_recursive) { + test_create_dir(TEST_DIR_RMRF_NAME); + + test_create_dir(TEST_DIR_RMRF "dir1"); + test_create_file(TEST_DIR_RMRF "dir1/file1", 1); + + test_create_dir(TEST_DIR_RMRF "dir1/dir1"); + test_create_dir(TEST_DIR_RMRF "dir1/dir2"); + test_create_file(TEST_DIR_RMRF "dir1/dir2/file1", 1); + test_create_file(TEST_DIR_RMRF "dir1/dir2/file2", 1); + test_create_dir(TEST_DIR_RMRF "dir1/dir3"); + test_create_dir(TEST_DIR_RMRF "dir1/dir3/dir1"); + test_create_dir(TEST_DIR_RMRF "dir1/dir3/dir1/dir1"); + test_create_dir(TEST_DIR_RMRF "dir1/dir3/dir1/dir1/dir1"); + test_create_dir(TEST_DIR_RMRF "dir1/dir3/dir1/dir1/dir1/dir1"); + + test_create_dir(TEST_DIR_RMRF "dir2"); + test_create_dir(TEST_DIR_RMRF "dir2/dir1"); + test_create_dir(TEST_DIR_RMRF "dir2/dir2"); + test_create_file(TEST_DIR_RMRF "dir2/dir2/file1", 1); + + test_create_dir(TEST_DIR_RMRF "dir2/dir2/dir1"); + test_create_dir(TEST_DIR_RMRF "dir2/dir2/dir1/dir1"); + test_create_dir(TEST_DIR_RMRF "dir2/dir2/dir1/dir1/dir1"); + test_create_file(TEST_DIR_RMRF "dir2/dir2/dir1/dir1/dir1/file1", 1); - test_create_dir(TEST_DIR "dir1"); - test_storage_delete_run(TEST_DIR "dir1", ++command_id, PB_CommandStatus_OK); test_storage_delete_run( - TEST_DIR "dir1", ++command_id, PB_CommandStatus_ERROR_STORAGE_NOT_EXIST); + TEST_DIR_RMRF_NAME, ++command_id, PB_CommandStatus_ERROR_STORAGE_DIR_NOT_EMPTY, false); + mu_check(test_is_exists(TEST_DIR_RMRF_NAME)); + test_storage_delete_run(TEST_DIR_RMRF_NAME, ++command_id, PB_CommandStatus_OK, true); + mu_check(!test_is_exists(TEST_DIR_RMRF_NAME)); + test_storage_delete_run(TEST_DIR_RMRF_NAME, ++command_id, PB_CommandStatus_OK, false); + mu_check(!test_is_exists(TEST_DIR_RMRF_NAME)); + + test_create_dir(TEST_DIR_RMRF_NAME); + test_storage_delete_run(TEST_DIR_RMRF_NAME, ++command_id, PB_CommandStatus_OK, true); + mu_check(!test_is_exists(TEST_DIR_RMRF_NAME)); + + test_create_dir(TEST_DIR "file1"); + test_storage_delete_run(TEST_DIR "file1", ++command_id, PB_CommandStatus_OK, true); + mu_check(!test_is_exists(TEST_DIR "file1")); +} + +MU_TEST(test_storage_delete) { + test_storage_delete_run(NULL, ++command_id, PB_CommandStatus_ERROR_INVALID_PARAMETERS, false); + + furi_check(!test_is_exists(TEST_DIR "empty.txt")); + test_storage_delete_run(TEST_DIR "empty.txt", ++command_id, PB_CommandStatus_OK, false); + mu_check(!test_is_exists(TEST_DIR "empty.txt")); + + test_create_file(TEST_DIR "empty.txt", 0); + test_storage_delete_run(TEST_DIR "empty.txt", ++command_id, PB_CommandStatus_OK, false); + mu_check(!test_is_exists(TEST_DIR "empty.txt")); + + furi_check(!test_is_exists(TEST_DIR "dir1")); + test_create_dir(TEST_DIR "dir1"); + test_storage_delete_run(TEST_DIR "dir1", ++command_id, PB_CommandStatus_OK, false); + mu_check(!test_is_exists(TEST_DIR "dir1")); + + test_storage_delete_run(TEST_DIR "dir1", ++command_id, PB_CommandStatus_OK, false); + mu_check(!test_is_exists(TEST_DIR "dir1")); } static void test_storage_mkdir_run(const char* path, size_t command_id, PB_CommandStatus status) { @@ -872,18 +990,17 @@ static void test_storage_mkdir_run(const char* path, size_t command_id, PB_Comma } MU_TEST(test_storage_mkdir) { + furi_check(!test_is_exists(TEST_DIR "dir1")); test_storage_mkdir_run(TEST_DIR "dir1", ++command_id, PB_CommandStatus_OK); + mu_check(test_is_exists(TEST_DIR "dir1")); + test_storage_mkdir_run(TEST_DIR "dir1", ++command_id, PB_CommandStatus_ERROR_STORAGE_EXIST); + mu_check(test_is_exists(TEST_DIR "dir1")); + + furi_check(!test_is_exists(TEST_DIR "dir2")); test_create_dir(TEST_DIR "dir2"); test_storage_mkdir_run(TEST_DIR "dir2", ++command_id, PB_CommandStatus_ERROR_STORAGE_EXIST); - - Storage* fs_api = furi_record_open("storage"); - FS_Error error = storage_common_remove(fs_api, TEST_DIR "dir1"); - (void)error; - furi_assert(error == FSE_OK); - furi_record_close("storage"); - - test_storage_mkdir_run(TEST_DIR "dir1", ++command_id, PB_CommandStatus_OK); + mu_check(test_is_exists(TEST_DIR "dir2")); } static void test_storage_calculate_md5sum(const char* path, char* md5sum) { @@ -1013,7 +1130,7 @@ MU_TEST(test_ping) { // 4) test for fill buffer till end (great varint) and close connection MU_TEST_SUITE(test_rpc_status) { - MU_SUITE_CONFIGURE(&test_rpc_storage_setup, &test_rpc_storage_teardown); + MU_SUITE_CONFIGURE(&test_rpc_setup, &test_rpc_teardown); MU_RUN_TEST(test_ping); } @@ -1026,6 +1143,7 @@ MU_TEST_SUITE(test_rpc_storage) { MU_RUN_TEST(test_storage_write_read); MU_RUN_TEST(test_storage_write); MU_RUN_TEST(test_storage_delete); + MU_RUN_TEST(test_storage_delete_recursive); MU_RUN_TEST(test_storage_mkdir); MU_RUN_TEST(test_storage_md5sum); MU_RUN_TEST(test_storage_interrupt_continuous_same_system); @@ -1112,20 +1230,19 @@ MU_TEST(test_app_start_and_lock_status) { "skynet_destroy_world_app", NULL, PB_CommandStatus_ERROR_INVALID_PARAMETERS, ++command_id); test_app_get_status_lock_run(false, ++command_id); - test_app_start_run("Delay Test App", "0", PB_CommandStatus_OK, ++command_id); + test_app_start_run("Delay Test", "0", PB_CommandStatus_OK, ++command_id); delay(100); test_app_get_status_lock_run(false, ++command_id); - test_app_start_run("Delay Test App", "200", PB_CommandStatus_OK, ++command_id); + test_app_start_run("Delay Test", "200", PB_CommandStatus_OK, ++command_id); test_app_get_status_lock_run(true, ++command_id); delay(100); test_app_get_status_lock_run(true, ++command_id); - test_app_start_run( - "Delay Test App", "0", PB_CommandStatus_ERROR_APP_SYSTEM_LOCKED, ++command_id); + test_app_start_run("Delay Test", "0", PB_CommandStatus_ERROR_APP_SYSTEM_LOCKED, ++command_id); delay(200); test_app_get_status_lock_run(false, ++command_id); - test_app_start_run("Delay Test App", "500", PB_CommandStatus_OK, ++command_id); + test_app_start_run("Delay Test", "500", PB_CommandStatus_OK, ++command_id); delay(100); test_app_get_status_lock_run(true, ++command_id); test_app_start_run("Infrared", "0", PB_CommandStatus_ERROR_APP_SYSTEM_LOCKED, ++command_id); @@ -1140,16 +1257,22 @@ MU_TEST(test_app_start_and_lock_status) { } MU_TEST_SUITE(test_rpc_app) { - MU_SUITE_CONFIGURE(&test_rpc_storage_setup, &test_rpc_storage_teardown); + MU_SUITE_CONFIGURE(&test_rpc_setup, &test_rpc_teardown); MU_RUN_TEST(test_app_start_and_lock_status); } int run_minunit_test_rpc() { - MU_RUN_SUITE(test_rpc_storage); + Storage* storage = furi_record_open("storage"); + furi_record_close("storage"); + if(storage_sd_status(storage) != FSE_OK) { + FURI_LOG_E("UNIT_TESTS", "SD card not mounted - skip storage tests"); + } else { + MU_RUN_SUITE(test_rpc_storage); + } + MU_RUN_SUITE(test_rpc_status); MU_RUN_SUITE(test_rpc_app); - MU_REPORT(); return MU_EXIT_CODE; } diff --git a/applications/tests/test_index.c b/applications/tests/test_index.c index 7158b304..d55a1e45 100644 --- a/applications/tests/test_index.c +++ b/applications/tests/test_index.c @@ -7,10 +7,27 @@ #include #include +#define TESTS_TAG "UNIT_TESTS" + int run_minunit(); int run_minunit_test_irda_decoder_encoder(); int run_minunit_test_rpc(); +void minunit_print_progress(void) { + static char progress[] = {'\\', '|', '/', '-'}; + static uint8_t progress_counter = 0; + static TickType_t last_tick = 0; + TickType_t current_tick = xTaskGetTickCount(); + if(current_tick - last_tick > 20) { + last_tick = current_tick; + printf("[%c]\033[3D", progress[++progress_counter % COUNT_OF(progress)]); + } +} + +void minunit_print_fail(const char* str) { + printf("%s\n", str); +} + void unit_tests_cli(Cli* cli, string_t args, void* context) { uint32_t test_result = 0; minunit_run = 0; @@ -25,21 +42,30 @@ void unit_tests_cli(Cli* cli, string_t args, void* context) { furi_record_close("notification"); if(loader_is_locked(loader)) { - FURI_LOG_E("UNIT_TESTS", "RPC: stop all applications to run tests"); + FURI_LOG_E(TESTS_TAG, "RPC: stop all applications to run tests"); notification_message(notification, &sequence_blink_magenta_100); } else { notification_message_block(notification, &sequence_set_only_blue_255); + uint32_t heap_before = memmgr_get_free_heap(); + test_result |= run_minunit(); test_result |= run_minunit_test_irda_decoder_encoder(); test_result |= run_minunit_test_rpc(); if(test_result == 0) { + delay(200); /* wait for tested services and apps to deallocate */ + uint32_t heap_after = memmgr_get_free_heap(); notification_message(notification, &sequence_success); - FURI_LOG_I("UNIT_TESTS", "PASSED"); + if(heap_after != heap_before) { + FURI_LOG_E(TESTS_TAG, "Leaked: %d", heap_before - heap_after); + } else { + FURI_LOG_I(TESTS_TAG, "No leaks"); + } + FURI_LOG_I(TESTS_TAG, "PASSED"); } else { notification_message(notification, &sequence_error); - FURI_LOG_E("UNIT_TESTS", "FAILED"); + FURI_LOG_E(TESTS_TAG, "FAILED"); } } } diff --git a/assets/compiled/flipper.pb.c b/assets/compiled/flipper.pb.c index d81c78ec..684ffe8c 100644 --- a/assets/compiled/flipper.pb.c +++ b/assets/compiled/flipper.pb.c @@ -9,6 +9,9 @@ PB_BIND(PB_Empty, PB_Empty, AUTO) +PB_BIND(PB_StopSession, PB_StopSession, AUTO) + + PB_BIND(PB_Main, PB_Main, AUTO) diff --git a/assets/compiled/flipper.pb.h b/assets/compiled/flipper.pb.h index d37a38a2..bb2a3535 100644 --- a/assets/compiled/flipper.pb.h +++ b/assets/compiled/flipper.pb.h @@ -30,7 +30,8 @@ typedef enum _PB_CommandStatus { PB_CommandStatus_ERROR_STORAGE_INTERNAL = 11, /* *< Internal error */ PB_CommandStatus_ERROR_STORAGE_NOT_IMPLEMENTED = 12, /* *< Functon not implemented */ PB_CommandStatus_ERROR_STORAGE_ALREADY_OPEN = 13, /* *< File/Dir already opened */ - PB_CommandStatus_ERROR_APP_CANT_START = 16, /* *< Can't start app - or internal error */ + PB_CommandStatus_ERROR_STORAGE_DIR_NOT_EMPTY = 18, /* *< Directory, you're going to remove is not empty */ + PB_CommandStatus_ERROR_APP_CANT_START = 16, /* *< Can't start app - internal error */ PB_CommandStatus_ERROR_APP_SYSTEM_LOCKED = 17 /* *< Another app is running */ } PB_CommandStatus; @@ -42,6 +43,10 @@ typedef struct _PB_Empty { char dummy_field; } PB_Empty; +typedef struct _PB_StopSession { + char dummy_field; +} PB_StopSession; + typedef struct _PB_Main { uint32_t command_id; PB_CommandStatus command_status; @@ -64,14 +69,15 @@ typedef struct _PB_Main { PB_App_Start app_start; PB_App_LockStatusRequest app_lock_status_request; PB_App_LockStatusResponse app_lock_status_response; + PB_StopSession stop_session; } content; } PB_Main; /* Helper constants for enums */ #define _PB_CommandStatus_MIN PB_CommandStatus_OK -#define _PB_CommandStatus_MAX PB_CommandStatus_ERROR_APP_SYSTEM_LOCKED -#define _PB_CommandStatus_ARRAYSIZE ((PB_CommandStatus)(PB_CommandStatus_ERROR_APP_SYSTEM_LOCKED+1)) +#define _PB_CommandStatus_MAX PB_CommandStatus_ERROR_STORAGE_DIR_NOT_EMPTY +#define _PB_CommandStatus_ARRAYSIZE ((PB_CommandStatus)(PB_CommandStatus_ERROR_STORAGE_DIR_NOT_EMPTY+1)) #ifdef __cplusplus @@ -80,8 +86,10 @@ extern "C" { /* Initializer values for message structs */ #define PB_Empty_init_default {0} +#define PB_StopSession_init_default {0} #define PB_Main_init_default {0, _PB_CommandStatus_MIN, 0, {{NULL}, NULL}, 0, {PB_Empty_init_default}} #define PB_Empty_init_zero {0} +#define PB_StopSession_init_zero {0} #define PB_Main_init_zero {0, _PB_CommandStatus_MIN, 0, {{NULL}, NULL}, 0, {PB_Empty_init_zero}} /* Field tags (for use in manual encoding/decoding) */ @@ -103,6 +111,7 @@ extern "C" { #define PB_Main_app_start_tag 16 #define PB_Main_app_lock_status_request_tag 17 #define PB_Main_app_lock_status_response_tag 18 +#define PB_Main_stop_session_tag 19 /* Struct field encoding specification for nanopb */ #define PB_Empty_FIELDLIST(X, a) \ @@ -110,6 +119,11 @@ extern "C" { #define PB_Empty_CALLBACK NULL #define PB_Empty_DEFAULT NULL +#define PB_StopSession_FIELDLIST(X, a) \ + +#define PB_StopSession_CALLBACK NULL +#define PB_StopSession_DEFAULT NULL + #define PB_Main_FIELDLIST(X, a) \ X(a, STATIC, SINGULAR, UINT32, command_id, 1) \ X(a, STATIC, SINGULAR, UENUM, command_status, 2) \ @@ -128,7 +142,8 @@ X(a, STATIC, ONEOF, MSG_W_CB, (content,storage_md5sum_request,content.stora X(a, STATIC, ONEOF, MSG_W_CB, (content,storage_md5sum_response,content.storage_md5sum_response), 15) \ X(a, STATIC, ONEOF, MSG_W_CB, (content,app_start,content.app_start), 16) \ X(a, STATIC, ONEOF, MSG_W_CB, (content,app_lock_status_request,content.app_lock_status_request), 17) \ -X(a, STATIC, ONEOF, MSG_W_CB, (content,app_lock_status_response,content.app_lock_status_response), 18) +X(a, STATIC, ONEOF, MSG_W_CB, (content,app_lock_status_response,content.app_lock_status_response), 18) \ +X(a, STATIC, ONEOF, MSG_W_CB, (content,stop_session,content.stop_session), 19) #define PB_Main_CALLBACK NULL #define PB_Main_DEFAULT NULL #define PB_Main_content_empty_MSGTYPE PB_Empty @@ -146,16 +161,20 @@ X(a, STATIC, ONEOF, MSG_W_CB, (content,app_lock_status_response,content.app #define PB_Main_content_app_start_MSGTYPE PB_App_Start #define PB_Main_content_app_lock_status_request_MSGTYPE PB_App_LockStatusRequest #define PB_Main_content_app_lock_status_response_MSGTYPE PB_App_LockStatusResponse +#define PB_Main_content_stop_session_MSGTYPE PB_StopSession extern const pb_msgdesc_t PB_Empty_msg; +extern const pb_msgdesc_t PB_StopSession_msg; extern const pb_msgdesc_t PB_Main_msg; /* Defines for backwards compatibility with code written before nanopb-0.4.0 */ #define PB_Empty_fields &PB_Empty_msg +#define PB_StopSession_fields &PB_StopSession_msg #define PB_Main_fields &PB_Main_msg /* Maximum encoded size of messages (where known) */ #define PB_Empty_size 0 +#define PB_StopSession_size 0 #if defined(PB_Storage_ListRequest_size) && defined(PB_Storage_ListResponse_size) && defined(PB_Storage_ReadRequest_size) && defined(PB_Storage_ReadResponse_size) && defined(PB_Storage_WriteRequest_size) && defined(PB_Storage_DeleteRequest_size) && defined(PB_Storage_MkdirRequest_size) && defined(PB_Storage_Md5sumRequest_size) && defined(PB_App_Start_size) #define PB_Main_size (10 + sizeof(union PB_Main_content_size_union)) union PB_Main_content_size_union {char f7[(6 + PB_Storage_ListRequest_size)]; char f8[(6 + PB_Storage_ListResponse_size)]; char f9[(6 + PB_Storage_ReadRequest_size)]; char f10[(6 + PB_Storage_ReadResponse_size)]; char f11[(6 + PB_Storage_WriteRequest_size)]; char f12[(6 + PB_Storage_DeleteRequest_size)]; char f13[(6 + PB_Storage_MkdirRequest_size)]; char f14[(6 + PB_Storage_Md5sumRequest_size)]; char f16[(7 + PB_App_Start_size)]; char f0[36];}; diff --git a/assets/compiled/storage.pb.h b/assets/compiled/storage.pb.h index cdb47372..ea6291b9 100644 --- a/assets/compiled/storage.pb.h +++ b/assets/compiled/storage.pb.h @@ -16,10 +16,6 @@ typedef enum _PB_Storage_File_FileType { } PB_Storage_File_FileType; /* Struct definitions */ -typedef struct _PB_Storage_DeleteRequest { - char *path; -} PB_Storage_DeleteRequest; - typedef struct _PB_Storage_ListRequest { char *path; } PB_Storage_ListRequest; @@ -36,6 +32,11 @@ typedef struct _PB_Storage_ReadRequest { char *path; } PB_Storage_ReadRequest; +typedef struct _PB_Storage_DeleteRequest { + char *path; + bool recursive; +} PB_Storage_DeleteRequest; + typedef struct _PB_Storage_File { PB_Storage_File_FileType type; char *name; @@ -81,7 +82,7 @@ extern "C" { #define PB_Storage_ReadRequest_init_default {NULL} #define PB_Storage_ReadResponse_init_default {false, PB_Storage_File_init_default} #define PB_Storage_WriteRequest_init_default {NULL, false, PB_Storage_File_init_default} -#define PB_Storage_DeleteRequest_init_default {NULL} +#define PB_Storage_DeleteRequest_init_default {NULL, 0} #define PB_Storage_MkdirRequest_init_default {NULL} #define PB_Storage_Md5sumRequest_init_default {NULL} #define PB_Storage_Md5sumResponse_init_default {""} @@ -91,17 +92,18 @@ extern "C" { #define PB_Storage_ReadRequest_init_zero {NULL} #define PB_Storage_ReadResponse_init_zero {false, PB_Storage_File_init_zero} #define PB_Storage_WriteRequest_init_zero {NULL, false, PB_Storage_File_init_zero} -#define PB_Storage_DeleteRequest_init_zero {NULL} +#define PB_Storage_DeleteRequest_init_zero {NULL, 0} #define PB_Storage_MkdirRequest_init_zero {NULL} #define PB_Storage_Md5sumRequest_init_zero {NULL} #define PB_Storage_Md5sumResponse_init_zero {""} /* Field tags (for use in manual encoding/decoding) */ -#define PB_Storage_DeleteRequest_path_tag 1 #define PB_Storage_ListRequest_path_tag 1 #define PB_Storage_Md5sumRequest_path_tag 1 #define PB_Storage_MkdirRequest_path_tag 1 #define PB_Storage_ReadRequest_path_tag 1 +#define PB_Storage_DeleteRequest_path_tag 1 +#define PB_Storage_DeleteRequest_recursive_tag 2 #define PB_Storage_File_type_tag 1 #define PB_Storage_File_name_tag 2 #define PB_Storage_File_size_tag 3 @@ -151,7 +153,8 @@ X(a, STATIC, OPTIONAL, MESSAGE, file, 2) #define PB_Storage_WriteRequest_file_MSGTYPE PB_Storage_File #define PB_Storage_DeleteRequest_FIELDLIST(X, a) \ -X(a, POINTER, SINGULAR, STRING, path, 1) +X(a, POINTER, SINGULAR, STRING, path, 1) \ +X(a, STATIC, SINGULAR, BOOL, recursive, 2) #define PB_Storage_DeleteRequest_CALLBACK NULL #define PB_Storage_DeleteRequest_DEFAULT NULL diff --git a/assets/protobuf b/assets/protobuf index 8e6db414..021ba48a 160000 --- a/assets/protobuf +++ b/assets/protobuf @@ -1 +1 @@ -Subproject commit 8e6db414beed5aff0902f2cca2f4146a0dffb7a1 +Subproject commit 021ba48abb64d25c7094da13b752fe37d4bf6007 diff --git a/bootloader/targets/f6/furi-hal/furi-hal-version.c b/bootloader/targets/f6/furi-hal/furi-hal-version.c index ff3b2699..df6d61b0 100644 --- a/bootloader/targets/f6/furi-hal/furi-hal-version.c +++ b/bootloader/targets/f6/furi-hal/furi-hal-version.c @@ -272,7 +272,7 @@ const struct Version* furi_hal_version_get_firmware_version(void) { return version_get(); } -const struct Version* furi_hal_version_get_boot_version(void) { +const struct Version* furi_hal_version_get_bootloader_version(void) { #ifdef NO_BOOTLOADER return 0; #else diff --git a/bootloader/targets/f7/furi-hal/furi-hal-version.c b/bootloader/targets/f7/furi-hal/furi-hal-version.c index ff3b2699..df6d61b0 100644 --- a/bootloader/targets/f7/furi-hal/furi-hal-version.c +++ b/bootloader/targets/f7/furi-hal/furi-hal-version.c @@ -272,7 +272,7 @@ const struct Version* furi_hal_version_get_firmware_version(void) { return version_get(); } -const struct Version* furi_hal_version_get_boot_version(void) { +const struct Version* furi_hal_version_get_bootloader_version(void) { #ifdef NO_BOOTLOADER return 0; #else diff --git a/bootloader/targets/furi-hal-include/furi-hal-version.h b/bootloader/targets/furi-hal-include/furi-hal-version.h index 99a00533..3ed8b60b 100644 --- a/bootloader/targets/furi-hal-include/furi-hal-version.h +++ b/bootloader/targets/furi-hal-include/furi-hal-version.h @@ -148,7 +148,7 @@ const uint8_t* furi_hal_version_get_ble_mac(); * * @return Address of boot version structure. */ -const struct Version* furi_hal_version_get_boot_version(); +const struct Version* furi_hal_version_get_bootloader_version(); /** Get address of version structure of firmware. * diff --git a/core/flipper.c b/core/flipper.c index 785a4463..c8d12068 100755 --- a/core/flipper.c +++ b/core/flipper.c @@ -25,7 +25,7 @@ static void flipper_print_version(const char* target, const Version* version) { void flipper_init() { const Version* version; - version = (const Version*)furi_hal_version_get_boot_version(); + version = (const Version*)furi_hal_version_get_bootloader_version(); flipper_print_version("Bootloader", version); version = (const Version*)furi_hal_version_get_firmware_version(); diff --git a/core/furi/record.c b/core/furi/record.c index 7bc736df..85d196c0 100644 --- a/core/furi/record.c +++ b/core/furi/record.c @@ -85,6 +85,7 @@ bool furi_record_destroy(const char* name) { furi_assert(record_data); if(record_data->holders_count == 0) { FuriRecordDataDict_erase(furi_record->records, name_str); + furi_check(osOK == osEventFlagsDelete(record_data->flags)); ret = true; } diff --git a/docker/Dockerfile b/docker/Dockerfile index f130305e..d987d75a 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -15,6 +15,7 @@ RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install --no-instal libxml2-dev \ libxslt1-dev \ zlib1g-dev \ + jq \ wget && \ apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* diff --git a/firmware/targets/f6/furi-hal/furi-hal-boot.c b/firmware/targets/f6/furi-hal/furi-hal-bootloader.c similarity index 62% rename from firmware/targets/f6/furi-hal/furi-hal-boot.c rename to firmware/targets/f6/furi-hal/furi-hal-bootloader.c index 978711c5..2962877e 100644 --- a/firmware/targets/f6/furi-hal/furi-hal-boot.c +++ b/firmware/targets/f6/furi-hal/furi-hal-bootloader.c @@ -1,4 +1,4 @@ -#include +#include #include #include @@ -7,25 +7,25 @@ #define BOOT_REQUEST_CLEAN 0xDADEDADE #define BOOT_REQUEST_DFU 0xDF00B000 -void furi_hal_boot_init() { +void furi_hal_bootloader_init() { #ifndef DEBUG LL_RTC_BAK_SetRegister(RTC, LL_RTC_BKP_DR0, BOOT_REQUEST_TAINTED); #endif FURI_LOG_I("FuriHalBoot", "Init OK"); } -void furi_hal_boot_set_mode(FuriHalBootMode mode) { - if (mode == FuriHalBootModeNormal) { +void furi_hal_bootloader_set_mode(FuriHalBootloaderMode mode) { + if (mode == FuriHalBootloaderModeNormal) { LL_RTC_BAK_SetRegister(RTC, LL_RTC_BKP_DR0, BOOT_REQUEST_CLEAN); - } else if (mode == FuriHalBootModeDFU) { + } else if (mode == FuriHalBootloaderModeDFU) { LL_RTC_BAK_SetRegister(RTC, LL_RTC_BKP_DR0, BOOT_REQUEST_DFU); } } -void furi_hal_boot_set_flags(FuriHalBootFlag flags) { +void furi_hal_bootloader_set_flags(FuriHalBootloaderFlag flags) { LL_RTC_BAK_SetRegister(RTC, LL_RTC_BKP_DR2, flags); } -FuriHalBootFlag furi_hal_boot_get_flags() { +FuriHalBootloaderFlag furi_hal_bootloader_get_flags() { return LL_RTC_BAK_GetRegister(RTC, LL_RTC_BKP_DR2); } \ No newline at end of file diff --git a/firmware/targets/f6/furi-hal/furi-hal-clock.c b/firmware/targets/f6/furi-hal/furi-hal-clock.c index 2544c769..fd4899d4 100644 --- a/firmware/targets/f6/furi-hal/furi-hal-clock.c +++ b/firmware/targets/f6/furi-hal/furi-hal-clock.c @@ -84,6 +84,7 @@ void furi_hal_clock_init() { LL_RCC_EnableRTC(); LL_RCC_SetUSARTClockSource(LL_RCC_USART1_CLKSOURCE_PCLK2); + LL_RCC_SetLPUARTClockSource(LL_RCC_LPUART1_CLKSOURCE_PCLK1); LL_RCC_SetADCClockSource(LL_RCC_ADC_CLKSOURCE_PLLSAI1); LL_RCC_SetI2CClockSource(LL_RCC_I2C1_CLKSOURCE_PCLK1); LL_RCC_SetRNGClockSource(LL_RCC_RNG_CLKSOURCE_CLK48); @@ -117,6 +118,7 @@ void furi_hal_clock_init() { // APB1 LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_RTCAPB); LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM2); + LL_APB1_GRP2_EnableClock(LL_APB1_GRP2_PERIPH_LPUART1); // APB2 LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_USART1); diff --git a/firmware/targets/f6/furi-hal/furi-hal-console.c b/firmware/targets/f6/furi-hal/furi-hal-console.c index 552f9e77..ffe340b9 100644 --- a/firmware/targets/f6/furi-hal/furi-hal-console.c +++ b/firmware/targets/f6/furi-hal/furi-hal-console.c @@ -1,5 +1,5 @@ #include -#include +#include #include #include @@ -12,98 +12,23 @@ volatile bool furi_hal_console_alive = false; -static void (*irq_cb)(uint8_t ev, uint8_t data); - void furi_hal_console_init() { - LL_GPIO_InitTypeDef GPIO_InitStruct = {0}; - GPIO_InitStruct.Pin = LL_GPIO_PIN_6|LL_GPIO_PIN_7; - GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE; - GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW; - GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL; - GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; - GPIO_InitStruct.Alternate = LL_GPIO_AF_7; - LL_GPIO_Init(GPIOB, &GPIO_InitStruct); - - LL_USART_InitTypeDef USART_InitStruct = {0}; - USART_InitStruct.PrescalerValue = LL_USART_PRESCALER_DIV1; - USART_InitStruct.BaudRate = CONSOLE_BAUDRATE; - USART_InitStruct.DataWidth = LL_USART_DATAWIDTH_8B; - USART_InitStruct.StopBits = LL_USART_STOPBITS_1; - USART_InitStruct.Parity = LL_USART_PARITY_NONE; - USART_InitStruct.TransferDirection = LL_USART_DIRECTION_TX_RX; - USART_InitStruct.HardwareFlowControl = LL_USART_HWCONTROL_NONE; - USART_InitStruct.OverSampling = LL_USART_OVERSAMPLING_16; - LL_USART_Init(USART1, &USART_InitStruct); - LL_USART_SetTXFIFOThreshold(USART1, LL_USART_FIFOTHRESHOLD_1_2); - LL_USART_EnableFIFO(USART1); - LL_USART_ConfigAsyncMode(USART1); - - LL_USART_Enable(USART1); - - while(!LL_USART_IsActiveFlag_TEACK(USART1)) ; - - LL_USART_EnableIT_RXNE_RXFNE(USART1); - LL_USART_EnableIT_IDLE(USART1); - HAL_NVIC_SetPriority(USART1_IRQn, 5, 0); - + furi_hal_uart_init(FuriHalUartIdUSART1, CONSOLE_BAUDRATE); furi_hal_console_alive = true; FURI_LOG_I("FuriHalConsole", "Init OK"); } -void furi_hal_usart_init() { - furi_hal_console_alive = false; -} - -void furi_hal_usart_set_br(uint32_t baud) { - if (LL_USART_IsEnabled(USART1)) { - // Wait for transfer complete flag - while (!LL_USART_IsActiveFlag_TC(USART1)); - LL_USART_Disable(USART1); - uint32_t uartclk = LL_RCC_GetUSARTClockFreq(LL_RCC_USART1_CLKSOURCE); - LL_USART_SetBaudRate(USART1, uartclk, LL_USART_PRESCALER_DIV1, LL_USART_OVERSAMPLING_16, baud); - LL_USART_Enable(USART1); - } -} - -void furi_hal_usart_deinit() { +void furi_hal_console_enable() { + furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, NULL); while (!LL_USART_IsActiveFlag_TC(USART1)); - furi_hal_usart_set_br(CONSOLE_BAUDRATE); + furi_hal_uart_set_br(FuriHalUartIdUSART1, CONSOLE_BAUDRATE); furi_hal_console_alive = true; } -void furi_hal_usart_tx(const uint8_t* buffer, size_t buffer_size) { - if (LL_USART_IsEnabled(USART1) == 0) - return; - - while(buffer_size > 0) { - while (!LL_USART_IsActiveFlag_TXE(USART1)); - - LL_USART_TransmitData8(USART1, *buffer); - - buffer++; - buffer_size--; - } -} - -void furi_hal_usart_set_irq_cb(void (*cb)(UartIrqEvent ev, uint8_t data)) { - irq_cb = cb; - if (irq_cb == NULL) - NVIC_DisableIRQ(USART1_IRQn); - else - NVIC_EnableIRQ(USART1_IRQn); -} - -void USART1_IRQHandler(void) { - if (LL_USART_IsActiveFlag_RXNE_RXFNE(USART1)) { - uint8_t data = LL_USART_ReceiveData8(USART1); - irq_cb(UartIrqEventRXNE, data); - } else if (LL_USART_IsActiveFlag_IDLE(USART1)) { - irq_cb(UartIrqEventIDLE, 0); - LL_USART_ClearFlag_IDLE(USART1); - } - - //TODO: more events +void furi_hal_console_disable() { + while (!LL_USART_IsActiveFlag_TC(USART1)); + furi_hal_console_alive = false; } void furi_hal_console_tx(const uint8_t* buffer, size_t buffer_size) { @@ -111,7 +36,7 @@ void furi_hal_console_tx(const uint8_t* buffer, size_t buffer_size) { return; // Transmit data - furi_hal_usart_tx(buffer, buffer_size); + furi_hal_uart_tx(FuriHalUartIdUSART1, (uint8_t*)buffer, buffer_size); // Wait for TC flag to be raised for last char while (!LL_USART_IsActiveFlag_TC(USART1)); } @@ -121,9 +46,9 @@ void furi_hal_console_tx_with_new_line(const uint8_t* buffer, size_t buffer_size return; // Transmit data - furi_hal_usart_tx(buffer, buffer_size); + furi_hal_uart_tx(FuriHalUartIdUSART1, (uint8_t*)buffer, buffer_size); // Transmit new line symbols - furi_hal_usart_tx((const uint8_t*)"\r\n", 2); + furi_hal_uart_tx(FuriHalUartIdUSART1, (uint8_t*)"\r\n", 2); // Wait for TC flag to be raised for last char while (!LL_USART_IsActiveFlag_TC(USART1)); } @@ -140,4 +65,4 @@ void furi_hal_console_printf(const char format[], ...) { void furi_hal_console_puts(const char *data) { furi_hal_console_tx((const uint8_t*)data, strlen(data)); -} \ No newline at end of file +} diff --git a/firmware/targets/f6/furi-hal/furi-hal-console.h b/firmware/targets/f6/furi-hal/furi-hal-console.h index 013653ba..4c10d81e 100644 --- a/firmware/targets/f6/furi-hal/furi-hal-console.h +++ b/firmware/targets/f6/furi-hal/furi-hal-console.h @@ -15,6 +15,10 @@ typedef enum { void furi_hal_console_init(); +void furi_hal_console_enable(); + +void furi_hal_console_disable(); + void furi_hal_console_tx(const uint8_t* buffer, size_t buffer_size); void furi_hal_console_tx_with_new_line(const uint8_t* buffer, size_t buffer_size); @@ -29,18 +33,6 @@ void furi_hal_console_printf(const char format[], ...); void furi_hal_console_puts(const char* data); - -void furi_hal_usart_init(); - -void furi_hal_usart_deinit(); - -void furi_hal_usart_set_br(uint32_t baud); - -void furi_hal_usart_tx(const uint8_t* buffer, size_t buffer_size); - -void furi_hal_usart_set_irq_cb(void (*cb)(UartIrqEvent ev, uint8_t data)); - - #ifdef __cplusplus } #endif diff --git a/firmware/targets/f6/furi-hal/furi-hal-lpuart.c b/firmware/targets/f6/furi-hal/furi-hal-lpuart.c deleted file mode 100644 index 31aa8b86..00000000 --- a/firmware/targets/f6/furi-hal/furi-hal-lpuart.c +++ /dev/null @@ -1,105 +0,0 @@ -#include -#include -#include -#include - -#include - -static void (*irq_cb)(uint8_t ev, uint8_t data); - -void furi_hal_lpuart_init() { - LL_GPIO_InitTypeDef GPIO_InitStruct = {0}; - GPIO_InitStruct.Pin = PC0_Pin|PC1_Pin; - GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE; - GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW; - GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL; - GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; - GPIO_InitStruct.Alternate = LL_GPIO_AF_8; - LL_GPIO_Init(GPIOC, &GPIO_InitStruct); - - LL_RCC_SetLPUARTClockSource(LL_RCC_LPUART1_CLKSOURCE_PCLK1); - LL_APB1_GRP2_EnableClock(LL_APB1_GRP2_PERIPH_LPUART1); - - LL_LPUART_InitTypeDef LPUART_InitStruct = {0}; - LPUART_InitStruct.PrescalerValue = LL_LPUART_PRESCALER_DIV1; - LPUART_InitStruct.BaudRate = 115200; - LPUART_InitStruct.DataWidth = LL_LPUART_DATAWIDTH_8B; - LPUART_InitStruct.StopBits = LL_LPUART_STOPBITS_1; - LPUART_InitStruct.Parity = LL_LPUART_PARITY_NONE; - LPUART_InitStruct.TransferDirection = LL_LPUART_DIRECTION_TX_RX; - LPUART_InitStruct.HardwareFlowControl = LL_LPUART_HWCONTROL_NONE; - LL_LPUART_Init(LPUART1, &LPUART_InitStruct); - LL_LPUART_SetTXFIFOThreshold(LPUART1, LL_LPUART_FIFOTHRESHOLD_1_8); - LL_LPUART_SetRXFIFOThreshold(LPUART1, LL_LPUART_FIFOTHRESHOLD_1_8); - LL_LPUART_EnableFIFO(LPUART1); - - LL_LPUART_Enable(LPUART1); - - while((!(LL_LPUART_IsActiveFlag_TEACK(LPUART1))) || (!(LL_LPUART_IsActiveFlag_REACK(LPUART1)))); - - LL_LPUART_EnableIT_RXNE_RXFNE(LPUART1); - LL_LPUART_EnableIT_IDLE(LPUART1); - HAL_NVIC_SetPriority(LPUART1_IRQn, 5, 0); - - FURI_LOG_I("FuriHalLpUart", "Init OK"); -} - -void furi_hal_lpuart_set_br(uint32_t baud) { - if (LL_LPUART_IsEnabled(LPUART1)) { - // Wait for transfer complete flag - while (!LL_LPUART_IsActiveFlag_TC(LPUART1)); - LL_LPUART_Disable(LPUART1); - uint32_t uartclk = LL_RCC_GetLPUARTClockFreq(LL_RCC_GetLPUARTClockSource(LL_RCC_LPUART1_CLKSOURCE_PCLK1)); - if (uartclk/baud > 4095) { - LL_LPUART_SetPrescaler(LPUART1, LL_LPUART_PRESCALER_DIV32); - LL_LPUART_SetBaudRate(LPUART1, uartclk, LL_LPUART_PRESCALER_DIV32, baud); - } else { - LL_LPUART_SetPrescaler(LPUART1, LL_LPUART_PRESCALER_DIV1); - LL_LPUART_SetBaudRate(LPUART1, uartclk, LL_LPUART_PRESCALER_DIV1, baud); - } - - LL_LPUART_Enable(LPUART1); - } -} - -void furi_hal_lpuart_deinit() { - furi_hal_lpuart_set_irq_cb(NULL); - LL_GPIO_SetPinMode(GPIOC, PC0_Pin, LL_GPIO_MODE_ANALOG); - LL_GPIO_SetPinMode(GPIOC, PC1_Pin, LL_GPIO_MODE_ANALOG); - LL_LPUART_Disable(LPUART1); - LL_APB1_GRP2_DisableClock(LL_APB1_GRP2_PERIPH_LPUART1); -} - -void furi_hal_lpuart_tx(const uint8_t* buffer, size_t buffer_size) { - if (LL_LPUART_IsEnabled(LPUART1) == 0) - return; - - while(buffer_size > 0) { - while (!LL_LPUART_IsActiveFlag_TXE(LPUART1)); - - LL_LPUART_TransmitData8(LPUART1, *buffer); - - buffer++; - buffer_size--; - } -} - -void furi_hal_lpuart_set_irq_cb(void (*cb)(UartIrqEvent ev, uint8_t data)) { - irq_cb = cb; - if (irq_cb == NULL) - NVIC_DisableIRQ(LPUART1_IRQn); - else - NVIC_EnableIRQ(LPUART1_IRQn); -} - -void LPUART1_IRQHandler(void) { - if (LL_LPUART_IsActiveFlag_RXNE_RXFNE(LPUART1)) { - uint8_t data = LL_LPUART_ReceiveData8(LPUART1); - irq_cb(UartIrqEventRXNE, data); - } else if (LL_LPUART_IsActiveFlag_IDLE(LPUART1)) { - irq_cb(UartIrqEventIDLE, 0); - LL_LPUART_ClearFlag_IDLE(LPUART1); - } - - //TODO: more events -} diff --git a/firmware/targets/f6/furi-hal/furi-hal-lpuart.h b/firmware/targets/f6/furi-hal/furi-hal-lpuart.h deleted file mode 100644 index 118a9a9c..00000000 --- a/firmware/targets/f6/furi-hal/furi-hal-lpuart.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include -#include -#include "furi-hal-console.h" - -#ifdef __cplusplus -extern "C" { -#endif - - -void furi_hal_lpuart_init(); - -void furi_hal_lpuart_deinit(); - -void furi_hal_lpuart_set_br(uint32_t baud); - -void furi_hal_lpuart_tx(const uint8_t* buffer, size_t buffer_size); - -void furi_hal_lpuart_set_irq_cb(void (*cb)(UartIrqEvent ev, uint8_t data)); - -#ifdef __cplusplus -} -#endif diff --git a/firmware/targets/f6/furi-hal/furi-hal-subghz.c b/firmware/targets/f6/furi-hal/furi-hal-subghz.c index 02b91277..e25c40d1 100644 --- a/firmware/targets/f6/furi-hal/furi-hal-subghz.c +++ b/firmware/targets/f6/furi-hal/furi-hal-subghz.c @@ -658,6 +658,7 @@ static void furi_hal_subghz_async_tx_refill(uint32_t* buffer, size_t samples) { bool is_odd = samples % 2; LevelDuration ld = furi_hal_subghz_async_tx.callback(furi_hal_subghz_async_tx.callback_context); + if(level_duration_is_wait(ld)) return; if(level_duration_is_reset(ld)) { // One more even sample required to end at low level if(is_odd) { diff --git a/firmware/targets/f6/furi-hal/furi-hal-uart.c b/firmware/targets/f6/furi-hal/furi-hal-uart.c new file mode 100644 index 00000000..ff2d94a7 --- /dev/null +++ b/firmware/targets/f6/furi-hal/furi-hal-uart.c @@ -0,0 +1,201 @@ +#include +#include +#include +#include +#include + +#include + +static void (*irq_cb[2])(uint8_t ev, uint8_t data); + +static void furi_hal_usart_init(uint32_t baud) { + hal_gpio_init_ex( + &gpio_usart_tx, + GpioModeAltFunctionPushPull, + GpioPullUp, + GpioSpeedVeryHigh, + GpioAltFn7USART1); + hal_gpio_init_ex( + &gpio_usart_rx, + GpioModeAltFunctionPushPull, + GpioPullUp, + GpioSpeedVeryHigh, + GpioAltFn7USART1); + + LL_USART_InitTypeDef USART_InitStruct = {0}; + USART_InitStruct.PrescalerValue = LL_USART_PRESCALER_DIV1; + USART_InitStruct.BaudRate = baud; + USART_InitStruct.DataWidth = LL_USART_DATAWIDTH_8B; + USART_InitStruct.StopBits = LL_USART_STOPBITS_1; + USART_InitStruct.Parity = LL_USART_PARITY_NONE; + USART_InitStruct.TransferDirection = LL_USART_DIRECTION_TX_RX; + USART_InitStruct.HardwareFlowControl = LL_USART_HWCONTROL_NONE; + USART_InitStruct.OverSampling = LL_USART_OVERSAMPLING_16; + LL_USART_Init(USART1, &USART_InitStruct); + LL_USART_SetTXFIFOThreshold(USART1, LL_USART_FIFOTHRESHOLD_1_2); + LL_USART_EnableFIFO(USART1); + LL_USART_ConfigAsyncMode(USART1); + + LL_USART_Enable(USART1); + + while(!LL_USART_IsActiveFlag_TEACK(USART1)); + + LL_USART_EnableIT_RXNE_RXFNE(USART1); + LL_USART_EnableIT_IDLE(USART1); + HAL_NVIC_SetPriority(USART1_IRQn, 5, 0); +} + +static void furi_hal_lpuart_init(uint32_t baud) { + hal_gpio_init_ex( + &gpio_ext_pc0, + GpioModeAltFunctionPushPull, + GpioPullUp, + GpioSpeedVeryHigh, + GpioAltFn8LPUART1); + hal_gpio_init_ex( + &gpio_ext_pc1, + GpioModeAltFunctionPushPull, + GpioPullUp, + GpioSpeedVeryHigh, + GpioAltFn8LPUART1); + + LL_LPUART_InitTypeDef LPUART_InitStruct = {0}; + LPUART_InitStruct.PrescalerValue = LL_LPUART_PRESCALER_DIV1; + LPUART_InitStruct.BaudRate = 115200; + LPUART_InitStruct.DataWidth = LL_LPUART_DATAWIDTH_8B; + LPUART_InitStruct.StopBits = LL_LPUART_STOPBITS_1; + LPUART_InitStruct.Parity = LL_LPUART_PARITY_NONE; + LPUART_InitStruct.TransferDirection = LL_LPUART_DIRECTION_TX_RX; + LPUART_InitStruct.HardwareFlowControl = LL_LPUART_HWCONTROL_NONE; + LL_LPUART_Init(LPUART1, &LPUART_InitStruct); + LL_LPUART_SetTXFIFOThreshold(LPUART1, LL_LPUART_FIFOTHRESHOLD_1_8); + LL_LPUART_SetRXFIFOThreshold(LPUART1, LL_LPUART_FIFOTHRESHOLD_1_8); + LL_LPUART_EnableFIFO(LPUART1); + + LL_LPUART_Enable(LPUART1); + + while((!(LL_LPUART_IsActiveFlag_TEACK(LPUART1))) || (!(LL_LPUART_IsActiveFlag_REACK(LPUART1)))); + + furi_hal_uart_set_br(FuriHalUartIdLPUART1, baud); + + LL_LPUART_EnableIT_RXNE_RXFNE(LPUART1); + LL_LPUART_EnableIT_IDLE(LPUART1); + HAL_NVIC_SetPriority(LPUART1_IRQn, 5, 0); +} + +void furi_hal_uart_init(FuriHalUartId ch, uint32_t baud) { + if (ch == FuriHalUartIdLPUART1) + furi_hal_lpuart_init(baud); + else if (ch == FuriHalUartIdUSART1) + furi_hal_usart_init(baud); +} + +void furi_hal_uart_set_br(FuriHalUartId ch, uint32_t baud) { + if (ch == FuriHalUartIdUSART1) { + if (LL_USART_IsEnabled(USART1)) { + // Wait for transfer complete flag + while (!LL_USART_IsActiveFlag_TC(USART1)); + LL_USART_Disable(USART1); + uint32_t uartclk = LL_RCC_GetUSARTClockFreq(LL_RCC_USART1_CLKSOURCE); + LL_USART_SetBaudRate(USART1, uartclk, LL_USART_PRESCALER_DIV1, LL_USART_OVERSAMPLING_16, baud); + LL_USART_Enable(USART1); + } + } else if (ch == FuriHalUartIdLPUART1) { + if (LL_LPUART_IsEnabled(LPUART1)) { + // Wait for transfer complete flag + while (!LL_LPUART_IsActiveFlag_TC(LPUART1)); + LL_LPUART_Disable(LPUART1); + uint32_t uartclk = LL_RCC_GetLPUARTClockFreq(LL_RCC_GetLPUARTClockSource(LL_RCC_LPUART1_CLKSOURCE_PCLK1)); + if (uartclk/baud > 4095) { + LL_LPUART_SetPrescaler(LPUART1, LL_LPUART_PRESCALER_DIV32); + LL_LPUART_SetBaudRate(LPUART1, uartclk, LL_LPUART_PRESCALER_DIV32, baud); + } else { + LL_LPUART_SetPrescaler(LPUART1, LL_LPUART_PRESCALER_DIV1); + LL_LPUART_SetBaudRate(LPUART1, uartclk, LL_LPUART_PRESCALER_DIV1, baud); + } + LL_LPUART_Enable(LPUART1); + } + } +} + +void furi_hal_uart_deinit(FuriHalUartId ch) { + furi_hal_uart_set_irq_cb(ch, NULL); + if (ch == FuriHalUartIdUSART1) { + LL_USART_Disable(USART1); + hal_gpio_init(&gpio_usart_tx, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + hal_gpio_init(&gpio_usart_rx, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + } else if (ch == FuriHalUartIdLPUART1) { + LL_LPUART_Disable(LPUART1); + hal_gpio_init(&gpio_ext_pc0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + hal_gpio_init(&gpio_ext_pc1, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + } +} + +void furi_hal_uart_tx(FuriHalUartId ch, uint8_t* buffer, size_t buffer_size) { + if (ch == FuriHalUartIdUSART1) { + if (LL_USART_IsEnabled(USART1) == 0) + return; + + while(buffer_size > 0) { + while (!LL_USART_IsActiveFlag_TXE(USART1)); + + LL_USART_TransmitData8(USART1, *buffer); + buffer++; + buffer_size--; + } + + } else if (ch == FuriHalUartIdLPUART1) { + if (LL_LPUART_IsEnabled(LPUART1) == 0) + return; + + while(buffer_size > 0) { + while (!LL_LPUART_IsActiveFlag_TXE(LPUART1)); + + LL_LPUART_TransmitData8(LPUART1, *buffer); + + buffer++; + buffer_size--; + } + } +} + +void furi_hal_uart_set_irq_cb(FuriHalUartId ch, void (*cb)(UartIrqEvent ev, uint8_t data)) { + if (cb == NULL) { + if (ch == FuriHalUartIdUSART1) + NVIC_DisableIRQ(USART1_IRQn); + else if (ch == FuriHalUartIdLPUART1) + NVIC_DisableIRQ(LPUART1_IRQn); + irq_cb[ch] = cb; + } else { + irq_cb[ch] = cb; + if (ch == FuriHalUartIdUSART1) + NVIC_EnableIRQ(USART1_IRQn); + else if (ch == FuriHalUartIdLPUART1) + NVIC_EnableIRQ(LPUART1_IRQn); + } +} + +void LPUART1_IRQHandler(void) { + if (LL_LPUART_IsActiveFlag_RXNE_RXFNE(LPUART1)) { + uint8_t data = LL_LPUART_ReceiveData8(LPUART1); + irq_cb[FuriHalUartIdLPUART1](UartIrqEventRXNE, data); + } else if (LL_LPUART_IsActiveFlag_IDLE(LPUART1)) { + irq_cb[FuriHalUartIdLPUART1](UartIrqEventIDLE, 0); + LL_LPUART_ClearFlag_IDLE(LPUART1); + } else if (LL_LPUART_IsActiveFlag_ORE(LPUART1)) { + LL_LPUART_ClearFlag_ORE(LPUART1); + } + //TODO: more events +} + +void USART1_IRQHandler(void) { + if (LL_USART_IsActiveFlag_RXNE_RXFNE(USART1)) { + uint8_t data = LL_USART_ReceiveData8(USART1); + irq_cb[FuriHalUartIdUSART1](UartIrqEventRXNE, data); + } else if (LL_USART_IsActiveFlag_IDLE(USART1)) { + irq_cb[FuriHalUartIdUSART1](UartIrqEventIDLE, 0); + LL_USART_ClearFlag_IDLE(USART1); + } else if (LL_USART_IsActiveFlag_ORE(USART1)) { + LL_USART_ClearFlag_ORE(USART1); + } +} diff --git a/firmware/targets/f6/furi-hal/furi-hal-uart.h b/firmware/targets/f6/furi-hal/furi-hal-uart.h new file mode 100644 index 00000000..6be156b7 --- /dev/null +++ b/firmware/targets/f6/furi-hal/furi-hal-uart.h @@ -0,0 +1,29 @@ +#pragma once + +#include +#include +#include "furi-hal-console.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + FuriHalUartIdUSART1, + FuriHalUartIdLPUART1, +} FuriHalUartId; + + +void furi_hal_uart_init(FuriHalUartId ch, uint32_t baud); + +void furi_hal_uart_deinit(FuriHalUartId ch); + +void furi_hal_uart_set_br(FuriHalUartId ch, uint32_t baud); + +void furi_hal_uart_tx(FuriHalUartId ch, uint8_t* buffer, size_t buffer_size); + +void furi_hal_uart_set_irq_cb(FuriHalUartId ch, void (*cb)(UartIrqEvent ev, uint8_t data)); + +#ifdef __cplusplus +} +#endif diff --git a/firmware/targets/f6/furi-hal/furi-hal-usb-cdc.c b/firmware/targets/f6/furi-hal/furi-hal-usb-cdc.c index e643fe57..9386b100 100644 --- a/firmware/targets/f6/furi-hal/furi-hal-usb-cdc.c +++ b/firmware/targets/f6/furi-hal/furi-hal-usb-cdc.c @@ -7,12 +7,12 @@ #include "usb_cdc.h" #define CDC0_RXD_EP 0x01 -#define CDC0_TXD_EP 0x82 -#define CDC0_NTF_EP 0x83 +#define CDC0_TXD_EP 0x81 +#define CDC0_NTF_EP 0x82 -#define CDC1_RXD_EP 0x04 -#define CDC1_TXD_EP 0x85 -#define CDC1_NTF_EP 0x86 +#define CDC1_RXD_EP 0x03 +#define CDC1_TXD_EP 0x83 +#define CDC1_NTF_EP 0x84 #define CDC_NTF_SZ 0x08 diff --git a/firmware/targets/f6/furi-hal/furi-hal-vcp.c b/firmware/targets/f6/furi-hal/furi-hal-vcp.c index b975495d..b2f17842 100644 --- a/firmware/targets/f6/furi-hal/furi-hal-vcp.c +++ b/firmware/targets/f6/furi-hal/furi-hal-vcp.c @@ -157,3 +157,8 @@ static void vcp_on_cdc_rx() { static void vcp_on_cdc_tx_complete() { osSemaphoreRelease(furi_hal_vcp->tx_semaphore); } + +bool furi_hal_vcp_is_connected(void) { + return furi_hal_vcp->connected; +} + diff --git a/firmware/targets/f6/furi-hal/furi-hal-version.c b/firmware/targets/f6/furi-hal/furi-hal-version.c index 7ee67c2a..5b412315 100644 --- a/firmware/targets/f6/furi-hal/furi-hal-version.c +++ b/firmware/targets/f6/furi-hal/furi-hal-version.c @@ -276,7 +276,7 @@ const struct Version* furi_hal_version_get_firmware_version(void) { return version_get(); } -const struct Version* furi_hal_version_get_boot_version(void) { +const struct Version* furi_hal_version_get_bootloader_version(void) { #ifdef NO_BOOTLOADER return 0; #else diff --git a/firmware/targets/f6/furi-hal/furi-hal.c b/firmware/targets/f6/furi-hal/furi-hal.c index bcddc6e0..aa7536ef 100644 --- a/firmware/targets/f6/furi-hal/furi-hal.c +++ b/firmware/targets/f6/furi-hal/furi-hal.c @@ -16,7 +16,7 @@ void furi_hal_init() { MX_RTC_Init(); FURI_LOG_I("HAL", "RTC OK"); - furi_hal_boot_init(); + furi_hal_bootloader_init(); furi_hal_version_init(); furi_hal_spi_init(); diff --git a/firmware/targets/f6/stm32wb55xx_flash_cm4_no_boot.ld b/firmware/targets/f6/stm32wb55xx_flash_cm4_no_bootloader.ld similarity index 100% rename from firmware/targets/f6/stm32wb55xx_flash_cm4_no_boot.ld rename to firmware/targets/f6/stm32wb55xx_flash_cm4_no_bootloader.ld diff --git a/firmware/targets/f6/stm32wb55xx_flash_cm4_boot.ld b/firmware/targets/f6/stm32wb55xx_flash_cm4_with_bootloader.ld similarity index 100% rename from firmware/targets/f6/stm32wb55xx_flash_cm4_boot.ld rename to firmware/targets/f6/stm32wb55xx_flash_cm4_with_bootloader.ld diff --git a/firmware/targets/f6/target.mk b/firmware/targets/f6/target.mk index f942ba0f..0b18617e 100644 --- a/firmware/targets/f6/target.mk +++ b/firmware/targets/f6/target.mk @@ -153,9 +153,9 @@ C_SOURCES += \ # Linker options ifeq ($(NO_BOOTLOADER), 1) -LDFLAGS += -T$(MXPROJECT_DIR)/stm32wb55xx_flash_cm4_no_boot.ld +LDFLAGS += -T$(MXPROJECT_DIR)/stm32wb55xx_flash_cm4_no_bootloader.ld else -LDFLAGS += -T$(MXPROJECT_DIR)/stm32wb55xx_flash_cm4_boot.ld +LDFLAGS += -T$(MXPROJECT_DIR)/stm32wb55xx_flash_cm4_with_bootloader.ld endif SVD_FILE = ../debug/STM32WB55_CM4.svd diff --git a/firmware/targets/f7/furi-hal/furi-hal-boot.c b/firmware/targets/f7/furi-hal/furi-hal-bootloader.c similarity index 62% rename from firmware/targets/f7/furi-hal/furi-hal-boot.c rename to firmware/targets/f7/furi-hal/furi-hal-bootloader.c index 978711c5..2962877e 100644 --- a/firmware/targets/f7/furi-hal/furi-hal-boot.c +++ b/firmware/targets/f7/furi-hal/furi-hal-bootloader.c @@ -1,4 +1,4 @@ -#include +#include #include #include @@ -7,25 +7,25 @@ #define BOOT_REQUEST_CLEAN 0xDADEDADE #define BOOT_REQUEST_DFU 0xDF00B000 -void furi_hal_boot_init() { +void furi_hal_bootloader_init() { #ifndef DEBUG LL_RTC_BAK_SetRegister(RTC, LL_RTC_BKP_DR0, BOOT_REQUEST_TAINTED); #endif FURI_LOG_I("FuriHalBoot", "Init OK"); } -void furi_hal_boot_set_mode(FuriHalBootMode mode) { - if (mode == FuriHalBootModeNormal) { +void furi_hal_bootloader_set_mode(FuriHalBootloaderMode mode) { + if (mode == FuriHalBootloaderModeNormal) { LL_RTC_BAK_SetRegister(RTC, LL_RTC_BKP_DR0, BOOT_REQUEST_CLEAN); - } else if (mode == FuriHalBootModeDFU) { + } else if (mode == FuriHalBootloaderModeDFU) { LL_RTC_BAK_SetRegister(RTC, LL_RTC_BKP_DR0, BOOT_REQUEST_DFU); } } -void furi_hal_boot_set_flags(FuriHalBootFlag flags) { +void furi_hal_bootloader_set_flags(FuriHalBootloaderFlag flags) { LL_RTC_BAK_SetRegister(RTC, LL_RTC_BKP_DR2, flags); } -FuriHalBootFlag furi_hal_boot_get_flags() { +FuriHalBootloaderFlag furi_hal_bootloader_get_flags() { return LL_RTC_BAK_GetRegister(RTC, LL_RTC_BKP_DR2); } \ No newline at end of file diff --git a/firmware/targets/f7/furi-hal/furi-hal-clock.c b/firmware/targets/f7/furi-hal/furi-hal-clock.c index 2544c769..fd4899d4 100644 --- a/firmware/targets/f7/furi-hal/furi-hal-clock.c +++ b/firmware/targets/f7/furi-hal/furi-hal-clock.c @@ -84,6 +84,7 @@ void furi_hal_clock_init() { LL_RCC_EnableRTC(); LL_RCC_SetUSARTClockSource(LL_RCC_USART1_CLKSOURCE_PCLK2); + LL_RCC_SetLPUARTClockSource(LL_RCC_LPUART1_CLKSOURCE_PCLK1); LL_RCC_SetADCClockSource(LL_RCC_ADC_CLKSOURCE_PLLSAI1); LL_RCC_SetI2CClockSource(LL_RCC_I2C1_CLKSOURCE_PCLK1); LL_RCC_SetRNGClockSource(LL_RCC_RNG_CLKSOURCE_CLK48); @@ -117,6 +118,7 @@ void furi_hal_clock_init() { // APB1 LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_RTCAPB); LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM2); + LL_APB1_GRP2_EnableClock(LL_APB1_GRP2_PERIPH_LPUART1); // APB2 LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_USART1); diff --git a/firmware/targets/f7/furi-hal/furi-hal-console.c b/firmware/targets/f7/furi-hal/furi-hal-console.c index 552f9e77..ffe340b9 100644 --- a/firmware/targets/f7/furi-hal/furi-hal-console.c +++ b/firmware/targets/f7/furi-hal/furi-hal-console.c @@ -1,5 +1,5 @@ #include -#include +#include #include #include @@ -12,98 +12,23 @@ volatile bool furi_hal_console_alive = false; -static void (*irq_cb)(uint8_t ev, uint8_t data); - void furi_hal_console_init() { - LL_GPIO_InitTypeDef GPIO_InitStruct = {0}; - GPIO_InitStruct.Pin = LL_GPIO_PIN_6|LL_GPIO_PIN_7; - GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE; - GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW; - GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL; - GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; - GPIO_InitStruct.Alternate = LL_GPIO_AF_7; - LL_GPIO_Init(GPIOB, &GPIO_InitStruct); - - LL_USART_InitTypeDef USART_InitStruct = {0}; - USART_InitStruct.PrescalerValue = LL_USART_PRESCALER_DIV1; - USART_InitStruct.BaudRate = CONSOLE_BAUDRATE; - USART_InitStruct.DataWidth = LL_USART_DATAWIDTH_8B; - USART_InitStruct.StopBits = LL_USART_STOPBITS_1; - USART_InitStruct.Parity = LL_USART_PARITY_NONE; - USART_InitStruct.TransferDirection = LL_USART_DIRECTION_TX_RX; - USART_InitStruct.HardwareFlowControl = LL_USART_HWCONTROL_NONE; - USART_InitStruct.OverSampling = LL_USART_OVERSAMPLING_16; - LL_USART_Init(USART1, &USART_InitStruct); - LL_USART_SetTXFIFOThreshold(USART1, LL_USART_FIFOTHRESHOLD_1_2); - LL_USART_EnableFIFO(USART1); - LL_USART_ConfigAsyncMode(USART1); - - LL_USART_Enable(USART1); - - while(!LL_USART_IsActiveFlag_TEACK(USART1)) ; - - LL_USART_EnableIT_RXNE_RXFNE(USART1); - LL_USART_EnableIT_IDLE(USART1); - HAL_NVIC_SetPriority(USART1_IRQn, 5, 0); - + furi_hal_uart_init(FuriHalUartIdUSART1, CONSOLE_BAUDRATE); furi_hal_console_alive = true; FURI_LOG_I("FuriHalConsole", "Init OK"); } -void furi_hal_usart_init() { - furi_hal_console_alive = false; -} - -void furi_hal_usart_set_br(uint32_t baud) { - if (LL_USART_IsEnabled(USART1)) { - // Wait for transfer complete flag - while (!LL_USART_IsActiveFlag_TC(USART1)); - LL_USART_Disable(USART1); - uint32_t uartclk = LL_RCC_GetUSARTClockFreq(LL_RCC_USART1_CLKSOURCE); - LL_USART_SetBaudRate(USART1, uartclk, LL_USART_PRESCALER_DIV1, LL_USART_OVERSAMPLING_16, baud); - LL_USART_Enable(USART1); - } -} - -void furi_hal_usart_deinit() { +void furi_hal_console_enable() { + furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, NULL); while (!LL_USART_IsActiveFlag_TC(USART1)); - furi_hal_usart_set_br(CONSOLE_BAUDRATE); + furi_hal_uart_set_br(FuriHalUartIdUSART1, CONSOLE_BAUDRATE); furi_hal_console_alive = true; } -void furi_hal_usart_tx(const uint8_t* buffer, size_t buffer_size) { - if (LL_USART_IsEnabled(USART1) == 0) - return; - - while(buffer_size > 0) { - while (!LL_USART_IsActiveFlag_TXE(USART1)); - - LL_USART_TransmitData8(USART1, *buffer); - - buffer++; - buffer_size--; - } -} - -void furi_hal_usart_set_irq_cb(void (*cb)(UartIrqEvent ev, uint8_t data)) { - irq_cb = cb; - if (irq_cb == NULL) - NVIC_DisableIRQ(USART1_IRQn); - else - NVIC_EnableIRQ(USART1_IRQn); -} - -void USART1_IRQHandler(void) { - if (LL_USART_IsActiveFlag_RXNE_RXFNE(USART1)) { - uint8_t data = LL_USART_ReceiveData8(USART1); - irq_cb(UartIrqEventRXNE, data); - } else if (LL_USART_IsActiveFlag_IDLE(USART1)) { - irq_cb(UartIrqEventIDLE, 0); - LL_USART_ClearFlag_IDLE(USART1); - } - - //TODO: more events +void furi_hal_console_disable() { + while (!LL_USART_IsActiveFlag_TC(USART1)); + furi_hal_console_alive = false; } void furi_hal_console_tx(const uint8_t* buffer, size_t buffer_size) { @@ -111,7 +36,7 @@ void furi_hal_console_tx(const uint8_t* buffer, size_t buffer_size) { return; // Transmit data - furi_hal_usart_tx(buffer, buffer_size); + furi_hal_uart_tx(FuriHalUartIdUSART1, (uint8_t*)buffer, buffer_size); // Wait for TC flag to be raised for last char while (!LL_USART_IsActiveFlag_TC(USART1)); } @@ -121,9 +46,9 @@ void furi_hal_console_tx_with_new_line(const uint8_t* buffer, size_t buffer_size return; // Transmit data - furi_hal_usart_tx(buffer, buffer_size); + furi_hal_uart_tx(FuriHalUartIdUSART1, (uint8_t*)buffer, buffer_size); // Transmit new line symbols - furi_hal_usart_tx((const uint8_t*)"\r\n", 2); + furi_hal_uart_tx(FuriHalUartIdUSART1, (uint8_t*)"\r\n", 2); // Wait for TC flag to be raised for last char while (!LL_USART_IsActiveFlag_TC(USART1)); } @@ -140,4 +65,4 @@ void furi_hal_console_printf(const char format[], ...) { void furi_hal_console_puts(const char *data) { furi_hal_console_tx((const uint8_t*)data, strlen(data)); -} \ No newline at end of file +} diff --git a/firmware/targets/f7/furi-hal/furi-hal-console.h b/firmware/targets/f7/furi-hal/furi-hal-console.h index 013653ba..4c10d81e 100644 --- a/firmware/targets/f7/furi-hal/furi-hal-console.h +++ b/firmware/targets/f7/furi-hal/furi-hal-console.h @@ -15,6 +15,10 @@ typedef enum { void furi_hal_console_init(); +void furi_hal_console_enable(); + +void furi_hal_console_disable(); + void furi_hal_console_tx(const uint8_t* buffer, size_t buffer_size); void furi_hal_console_tx_with_new_line(const uint8_t* buffer, size_t buffer_size); @@ -29,18 +33,6 @@ void furi_hal_console_printf(const char format[], ...); void furi_hal_console_puts(const char* data); - -void furi_hal_usart_init(); - -void furi_hal_usart_deinit(); - -void furi_hal_usart_set_br(uint32_t baud); - -void furi_hal_usart_tx(const uint8_t* buffer, size_t buffer_size); - -void furi_hal_usart_set_irq_cb(void (*cb)(UartIrqEvent ev, uint8_t data)); - - #ifdef __cplusplus } #endif diff --git a/firmware/targets/f7/furi-hal/furi-hal-lpuart.c b/firmware/targets/f7/furi-hal/furi-hal-lpuart.c deleted file mode 100644 index 31aa8b86..00000000 --- a/firmware/targets/f7/furi-hal/furi-hal-lpuart.c +++ /dev/null @@ -1,105 +0,0 @@ -#include -#include -#include -#include - -#include - -static void (*irq_cb)(uint8_t ev, uint8_t data); - -void furi_hal_lpuart_init() { - LL_GPIO_InitTypeDef GPIO_InitStruct = {0}; - GPIO_InitStruct.Pin = PC0_Pin|PC1_Pin; - GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE; - GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW; - GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL; - GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; - GPIO_InitStruct.Alternate = LL_GPIO_AF_8; - LL_GPIO_Init(GPIOC, &GPIO_InitStruct); - - LL_RCC_SetLPUARTClockSource(LL_RCC_LPUART1_CLKSOURCE_PCLK1); - LL_APB1_GRP2_EnableClock(LL_APB1_GRP2_PERIPH_LPUART1); - - LL_LPUART_InitTypeDef LPUART_InitStruct = {0}; - LPUART_InitStruct.PrescalerValue = LL_LPUART_PRESCALER_DIV1; - LPUART_InitStruct.BaudRate = 115200; - LPUART_InitStruct.DataWidth = LL_LPUART_DATAWIDTH_8B; - LPUART_InitStruct.StopBits = LL_LPUART_STOPBITS_1; - LPUART_InitStruct.Parity = LL_LPUART_PARITY_NONE; - LPUART_InitStruct.TransferDirection = LL_LPUART_DIRECTION_TX_RX; - LPUART_InitStruct.HardwareFlowControl = LL_LPUART_HWCONTROL_NONE; - LL_LPUART_Init(LPUART1, &LPUART_InitStruct); - LL_LPUART_SetTXFIFOThreshold(LPUART1, LL_LPUART_FIFOTHRESHOLD_1_8); - LL_LPUART_SetRXFIFOThreshold(LPUART1, LL_LPUART_FIFOTHRESHOLD_1_8); - LL_LPUART_EnableFIFO(LPUART1); - - LL_LPUART_Enable(LPUART1); - - while((!(LL_LPUART_IsActiveFlag_TEACK(LPUART1))) || (!(LL_LPUART_IsActiveFlag_REACK(LPUART1)))); - - LL_LPUART_EnableIT_RXNE_RXFNE(LPUART1); - LL_LPUART_EnableIT_IDLE(LPUART1); - HAL_NVIC_SetPriority(LPUART1_IRQn, 5, 0); - - FURI_LOG_I("FuriHalLpUart", "Init OK"); -} - -void furi_hal_lpuart_set_br(uint32_t baud) { - if (LL_LPUART_IsEnabled(LPUART1)) { - // Wait for transfer complete flag - while (!LL_LPUART_IsActiveFlag_TC(LPUART1)); - LL_LPUART_Disable(LPUART1); - uint32_t uartclk = LL_RCC_GetLPUARTClockFreq(LL_RCC_GetLPUARTClockSource(LL_RCC_LPUART1_CLKSOURCE_PCLK1)); - if (uartclk/baud > 4095) { - LL_LPUART_SetPrescaler(LPUART1, LL_LPUART_PRESCALER_DIV32); - LL_LPUART_SetBaudRate(LPUART1, uartclk, LL_LPUART_PRESCALER_DIV32, baud); - } else { - LL_LPUART_SetPrescaler(LPUART1, LL_LPUART_PRESCALER_DIV1); - LL_LPUART_SetBaudRate(LPUART1, uartclk, LL_LPUART_PRESCALER_DIV1, baud); - } - - LL_LPUART_Enable(LPUART1); - } -} - -void furi_hal_lpuart_deinit() { - furi_hal_lpuart_set_irq_cb(NULL); - LL_GPIO_SetPinMode(GPIOC, PC0_Pin, LL_GPIO_MODE_ANALOG); - LL_GPIO_SetPinMode(GPIOC, PC1_Pin, LL_GPIO_MODE_ANALOG); - LL_LPUART_Disable(LPUART1); - LL_APB1_GRP2_DisableClock(LL_APB1_GRP2_PERIPH_LPUART1); -} - -void furi_hal_lpuart_tx(const uint8_t* buffer, size_t buffer_size) { - if (LL_LPUART_IsEnabled(LPUART1) == 0) - return; - - while(buffer_size > 0) { - while (!LL_LPUART_IsActiveFlag_TXE(LPUART1)); - - LL_LPUART_TransmitData8(LPUART1, *buffer); - - buffer++; - buffer_size--; - } -} - -void furi_hal_lpuart_set_irq_cb(void (*cb)(UartIrqEvent ev, uint8_t data)) { - irq_cb = cb; - if (irq_cb == NULL) - NVIC_DisableIRQ(LPUART1_IRQn); - else - NVIC_EnableIRQ(LPUART1_IRQn); -} - -void LPUART1_IRQHandler(void) { - if (LL_LPUART_IsActiveFlag_RXNE_RXFNE(LPUART1)) { - uint8_t data = LL_LPUART_ReceiveData8(LPUART1); - irq_cb(UartIrqEventRXNE, data); - } else if (LL_LPUART_IsActiveFlag_IDLE(LPUART1)) { - irq_cb(UartIrqEventIDLE, 0); - LL_LPUART_ClearFlag_IDLE(LPUART1); - } - - //TODO: more events -} diff --git a/firmware/targets/f7/furi-hal/furi-hal-lpuart.h b/firmware/targets/f7/furi-hal/furi-hal-lpuart.h deleted file mode 100644 index 118a9a9c..00000000 --- a/firmware/targets/f7/furi-hal/furi-hal-lpuart.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include -#include -#include "furi-hal-console.h" - -#ifdef __cplusplus -extern "C" { -#endif - - -void furi_hal_lpuart_init(); - -void furi_hal_lpuart_deinit(); - -void furi_hal_lpuart_set_br(uint32_t baud); - -void furi_hal_lpuart_tx(const uint8_t* buffer, size_t buffer_size); - -void furi_hal_lpuart_set_irq_cb(void (*cb)(UartIrqEvent ev, uint8_t data)); - -#ifdef __cplusplus -} -#endif diff --git a/firmware/targets/f7/furi-hal/furi-hal-subghz.c b/firmware/targets/f7/furi-hal/furi-hal-subghz.c index 02b91277..e25c40d1 100644 --- a/firmware/targets/f7/furi-hal/furi-hal-subghz.c +++ b/firmware/targets/f7/furi-hal/furi-hal-subghz.c @@ -658,6 +658,7 @@ static void furi_hal_subghz_async_tx_refill(uint32_t* buffer, size_t samples) { bool is_odd = samples % 2; LevelDuration ld = furi_hal_subghz_async_tx.callback(furi_hal_subghz_async_tx.callback_context); + if(level_duration_is_wait(ld)) return; if(level_duration_is_reset(ld)) { // One more even sample required to end at low level if(is_odd) { diff --git a/firmware/targets/f7/furi-hal/furi-hal-uart.c b/firmware/targets/f7/furi-hal/furi-hal-uart.c new file mode 100644 index 00000000..c6e74101 --- /dev/null +++ b/firmware/targets/f7/furi-hal/furi-hal-uart.c @@ -0,0 +1,197 @@ +#include +#include +#include +#include +#include + +#include + +static void (*irq_cb[2])(uint8_t ev, uint8_t data); + +static void furi_hal_usart_init(uint32_t baud) { + hal_gpio_init_ex( + &gpio_usart_tx, + GpioModeAltFunctionPushPull, + GpioPullUp, + GpioSpeedVeryHigh, + GpioAltFn7USART1); + hal_gpio_init_ex( + &gpio_usart_rx, + GpioModeAltFunctionPushPull, + GpioPullUp, + GpioSpeedVeryHigh, + GpioAltFn7USART1); + + LL_USART_InitTypeDef USART_InitStruct = {0}; + USART_InitStruct.PrescalerValue = LL_USART_PRESCALER_DIV1; + USART_InitStruct.BaudRate = baud; + USART_InitStruct.DataWidth = LL_USART_DATAWIDTH_8B; + USART_InitStruct.StopBits = LL_USART_STOPBITS_1; + USART_InitStruct.Parity = LL_USART_PARITY_NONE; + USART_InitStruct.TransferDirection = LL_USART_DIRECTION_TX_RX; + USART_InitStruct.HardwareFlowControl = LL_USART_HWCONTROL_NONE; + USART_InitStruct.OverSampling = LL_USART_OVERSAMPLING_16; + LL_USART_Init(USART1, &USART_InitStruct); + LL_USART_SetTXFIFOThreshold(USART1, LL_USART_FIFOTHRESHOLD_1_2); + LL_USART_EnableFIFO(USART1); + LL_USART_ConfigAsyncMode(USART1); + + LL_USART_Enable(USART1); + + while(!LL_USART_IsActiveFlag_TEACK(USART1)); + + LL_USART_EnableIT_RXNE_RXFNE(USART1); + LL_USART_EnableIT_IDLE(USART1); + HAL_NVIC_SetPriority(USART1_IRQn, 5, 0); +} + +static void furi_hal_lpuart_init(uint32_t baud) { + hal_gpio_init_ex( + &gpio_ext_pc0, + GpioModeAltFunctionPushPull, + GpioPullUp, + GpioSpeedVeryHigh, + GpioAltFn8LPUART1); + hal_gpio_init_ex( + &gpio_ext_pc1, + GpioModeAltFunctionPushPull, + GpioPullUp, + GpioSpeedVeryHigh, + GpioAltFn8LPUART1); + + LL_LPUART_InitTypeDef LPUART_InitStruct = {0}; + LPUART_InitStruct.PrescalerValue = LL_LPUART_PRESCALER_DIV1; + LPUART_InitStruct.BaudRate = 115200; + LPUART_InitStruct.DataWidth = LL_LPUART_DATAWIDTH_8B; + LPUART_InitStruct.StopBits = LL_LPUART_STOPBITS_1; + LPUART_InitStruct.Parity = LL_LPUART_PARITY_NONE; + LPUART_InitStruct.TransferDirection = LL_LPUART_DIRECTION_TX_RX; + LPUART_InitStruct.HardwareFlowControl = LL_LPUART_HWCONTROL_NONE; + LL_LPUART_Init(LPUART1, &LPUART_InitStruct); + LL_LPUART_SetTXFIFOThreshold(LPUART1, LL_LPUART_FIFOTHRESHOLD_1_8); + LL_LPUART_SetRXFIFOThreshold(LPUART1, LL_LPUART_FIFOTHRESHOLD_1_8); + LL_LPUART_EnableFIFO(LPUART1); + + LL_LPUART_Enable(LPUART1); + + while((!(LL_LPUART_IsActiveFlag_TEACK(LPUART1))) || (!(LL_LPUART_IsActiveFlag_REACK(LPUART1)))); + + furi_hal_uart_set_br(FuriHalUartIdLPUART1, baud); + + LL_LPUART_EnableIT_RXNE_RXFNE(LPUART1); + LL_LPUART_EnableIT_IDLE(LPUART1); + HAL_NVIC_SetPriority(LPUART1_IRQn, 5, 0); +} + +void furi_hal_uart_init(FuriHalUartId ch, uint32_t baud) { + if (ch == FuriHalUartIdLPUART1) + furi_hal_lpuart_init(baud); + else if (ch == FuriHalUartIdUSART1) + furi_hal_usart_init(baud); +} + +void furi_hal_uart_set_br(FuriHalUartId ch, uint32_t baud) { + if (ch == FuriHalUartIdUSART1) { + if (LL_USART_IsEnabled(USART1)) { + // Wait for transfer complete flag + while (!LL_USART_IsActiveFlag_TC(USART1)); + LL_USART_Disable(USART1); + uint32_t uartclk = LL_RCC_GetUSARTClockFreq(LL_RCC_USART1_CLKSOURCE); + LL_USART_SetBaudRate(USART1, uartclk, LL_USART_PRESCALER_DIV1, LL_USART_OVERSAMPLING_16, baud); + LL_USART_Enable(USART1); + } + } else if (ch == FuriHalUartIdLPUART1) { + if (LL_LPUART_IsEnabled(LPUART1)) { + // Wait for transfer complete flag + while (!LL_LPUART_IsActiveFlag_TC(LPUART1)); + LL_LPUART_Disable(LPUART1); + uint32_t uartclk = LL_RCC_GetLPUARTClockFreq(LL_RCC_GetLPUARTClockSource(LL_RCC_LPUART1_CLKSOURCE_PCLK1)); + if (uartclk/baud > 4095) { + LL_LPUART_SetPrescaler(LPUART1, LL_LPUART_PRESCALER_DIV32); + LL_LPUART_SetBaudRate(LPUART1, uartclk, LL_LPUART_PRESCALER_DIV32, baud); + } else { + LL_LPUART_SetPrescaler(LPUART1, LL_LPUART_PRESCALER_DIV1); + LL_LPUART_SetBaudRate(LPUART1, uartclk, LL_LPUART_PRESCALER_DIV1, baud); + } + LL_LPUART_Enable(LPUART1); + } + } +} + +void furi_hal_uart_deinit(FuriHalUartId ch) { + furi_hal_uart_set_irq_cb(ch, NULL); + if (ch == FuriHalUartIdUSART1) { + LL_USART_Disable(USART1); + hal_gpio_init(&gpio_usart_tx, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + hal_gpio_init(&gpio_usart_rx, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + } else if (ch == FuriHalUartIdLPUART1) { + LL_LPUART_Disable(LPUART1); + hal_gpio_init(&gpio_ext_pc0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + hal_gpio_init(&gpio_ext_pc1, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + } +} + +void furi_hal_uart_tx(FuriHalUartId ch, uint8_t* buffer, size_t buffer_size) { + if (ch == FuriHalUartIdUSART1) { + if (LL_USART_IsEnabled(USART1) == 0) + return; + + while(buffer_size > 0) { + while (!LL_USART_IsActiveFlag_TXE(USART1)); + + LL_USART_TransmitData8(USART1, *buffer); + buffer++; + buffer_size--; + } + + } else if (ch == FuriHalUartIdLPUART1) { + if (LL_LPUART_IsEnabled(LPUART1) == 0) + return; + + while(buffer_size > 0) { + while (!LL_LPUART_IsActiveFlag_TXE(LPUART1)); + + LL_LPUART_TransmitData8(LPUART1, *buffer); + + buffer++; + buffer_size--; + } + } +} + +void furi_hal_uart_set_irq_cb(FuriHalUartId ch, void (*cb)(UartIrqEvent ev, uint8_t data)) { + if (cb == NULL) { + if (ch == FuriHalUartIdUSART1) + NVIC_DisableIRQ(USART1_IRQn); + else if (ch == FuriHalUartIdLPUART1) + NVIC_DisableIRQ(LPUART1_IRQn); + irq_cb[ch] = cb; + } else { + irq_cb[ch] = cb; + if (ch == FuriHalUartIdUSART1) + NVIC_EnableIRQ(USART1_IRQn); + else if (ch == FuriHalUartIdLPUART1) + NVIC_EnableIRQ(LPUART1_IRQn); + } +} + +void LPUART1_IRQHandler(void) { + if (LL_LPUART_IsActiveFlag_RXNE_RXFNE(LPUART1)) { + uint8_t data = LL_LPUART_ReceiveData8(LPUART1); + irq_cb[FuriHalUartIdLPUART1](UartIrqEventRXNE, data); + } else if (LL_LPUART_IsActiveFlag_IDLE(LPUART1)) { + irq_cb[FuriHalUartIdLPUART1](UartIrqEventIDLE, 0); + LL_LPUART_ClearFlag_IDLE(LPUART1); + } + //TODO: more events +} + +void USART1_IRQHandler(void) { + if (LL_USART_IsActiveFlag_RXNE_RXFNE(USART1)) { + uint8_t data = LL_USART_ReceiveData8(USART1); + irq_cb[FuriHalUartIdUSART1](UartIrqEventRXNE, data); + } else if (LL_USART_IsActiveFlag_IDLE(USART1)) { + irq_cb[FuriHalUartIdUSART1](UartIrqEventIDLE, 0); + LL_USART_ClearFlag_IDLE(USART1); + } +} diff --git a/firmware/targets/f7/furi-hal/furi-hal-uart.h b/firmware/targets/f7/furi-hal/furi-hal-uart.h new file mode 100644 index 00000000..6be156b7 --- /dev/null +++ b/firmware/targets/f7/furi-hal/furi-hal-uart.h @@ -0,0 +1,29 @@ +#pragma once + +#include +#include +#include "furi-hal-console.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + FuriHalUartIdUSART1, + FuriHalUartIdLPUART1, +} FuriHalUartId; + + +void furi_hal_uart_init(FuriHalUartId ch, uint32_t baud); + +void furi_hal_uart_deinit(FuriHalUartId ch); + +void furi_hal_uart_set_br(FuriHalUartId ch, uint32_t baud); + +void furi_hal_uart_tx(FuriHalUartId ch, uint8_t* buffer, size_t buffer_size); + +void furi_hal_uart_set_irq_cb(FuriHalUartId ch, void (*cb)(UartIrqEvent ev, uint8_t data)); + +#ifdef __cplusplus +} +#endif diff --git a/firmware/targets/f7/furi-hal/furi-hal-usb-cdc.c b/firmware/targets/f7/furi-hal/furi-hal-usb-cdc.c index e643fe57..9386b100 100644 --- a/firmware/targets/f7/furi-hal/furi-hal-usb-cdc.c +++ b/firmware/targets/f7/furi-hal/furi-hal-usb-cdc.c @@ -7,12 +7,12 @@ #include "usb_cdc.h" #define CDC0_RXD_EP 0x01 -#define CDC0_TXD_EP 0x82 -#define CDC0_NTF_EP 0x83 +#define CDC0_TXD_EP 0x81 +#define CDC0_NTF_EP 0x82 -#define CDC1_RXD_EP 0x04 -#define CDC1_TXD_EP 0x85 -#define CDC1_NTF_EP 0x86 +#define CDC1_RXD_EP 0x03 +#define CDC1_TXD_EP 0x83 +#define CDC1_NTF_EP 0x84 #define CDC_NTF_SZ 0x08 diff --git a/firmware/targets/f7/furi-hal/furi-hal-vcp.c b/firmware/targets/f7/furi-hal/furi-hal-vcp.c index b975495d..b2f17842 100644 --- a/firmware/targets/f7/furi-hal/furi-hal-vcp.c +++ b/firmware/targets/f7/furi-hal/furi-hal-vcp.c @@ -157,3 +157,8 @@ static void vcp_on_cdc_rx() { static void vcp_on_cdc_tx_complete() { osSemaphoreRelease(furi_hal_vcp->tx_semaphore); } + +bool furi_hal_vcp_is_connected(void) { + return furi_hal_vcp->connected; +} + diff --git a/firmware/targets/f7/furi-hal/furi-hal-version.c b/firmware/targets/f7/furi-hal/furi-hal-version.c index 64641fc6..e4adebc0 100644 --- a/firmware/targets/f7/furi-hal/furi-hal-version.c +++ b/firmware/targets/f7/furi-hal/furi-hal-version.c @@ -276,7 +276,7 @@ const struct Version* furi_hal_version_get_firmware_version(void) { return version_get(); } -const struct Version* furi_hal_version_get_boot_version(void) { +const struct Version* furi_hal_version_get_bootloader_version(void) { #ifdef NO_BOOTLOADER return 0; #else diff --git a/firmware/targets/f7/furi-hal/furi-hal.c b/firmware/targets/f7/furi-hal/furi-hal.c index bcddc6e0..aa7536ef 100644 --- a/firmware/targets/f7/furi-hal/furi-hal.c +++ b/firmware/targets/f7/furi-hal/furi-hal.c @@ -16,7 +16,7 @@ void furi_hal_init() { MX_RTC_Init(); FURI_LOG_I("HAL", "RTC OK"); - furi_hal_boot_init(); + furi_hal_bootloader_init(); furi_hal_version_init(); furi_hal_spi_init(); diff --git a/firmware/targets/f7/stm32wb55xx_flash_cm4_no_boot.ld b/firmware/targets/f7/stm32wb55xx_flash_cm4_no_bootloader.ld similarity index 100% rename from firmware/targets/f7/stm32wb55xx_flash_cm4_no_boot.ld rename to firmware/targets/f7/stm32wb55xx_flash_cm4_no_bootloader.ld diff --git a/firmware/targets/f7/stm32wb55xx_flash_cm4_boot.ld b/firmware/targets/f7/stm32wb55xx_flash_cm4_with_bootloader.ld similarity index 100% rename from firmware/targets/f7/stm32wb55xx_flash_cm4_boot.ld rename to firmware/targets/f7/stm32wb55xx_flash_cm4_with_bootloader.ld diff --git a/firmware/targets/f7/target.mk b/firmware/targets/f7/target.mk index be91cee9..184bb9d2 100644 --- a/firmware/targets/f7/target.mk +++ b/firmware/targets/f7/target.mk @@ -153,9 +153,9 @@ C_SOURCES += \ # Linker options ifeq ($(NO_BOOTLOADER), 1) -LDFLAGS += -T$(MXPROJECT_DIR)/stm32wb55xx_flash_cm4_no_boot.ld +LDFLAGS += -T$(MXPROJECT_DIR)/stm32wb55xx_flash_cm4_no_bootloader.ld else -LDFLAGS += -T$(MXPROJECT_DIR)/stm32wb55xx_flash_cm4_boot.ld +LDFLAGS += -T$(MXPROJECT_DIR)/stm32wb55xx_flash_cm4_with_bootloader.ld endif SVD_FILE = ../debug/STM32WB55_CM4.svd diff --git a/firmware/targets/furi-hal-include/furi-hal-boot.h b/firmware/targets/furi-hal-include/furi-hal-boot.h deleted file mode 100644 index 195a7bb0..00000000 --- a/firmware/targets/furi-hal-include/furi-hal-boot.h +++ /dev/null @@ -1,50 +0,0 @@ -/** - * @file furi-hal-boot.h - * Bootloader HAL API - */ - -#pragma once - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** Boot modes */ -typedef enum { - FuriHalBootModeNormal, - FuriHalBootModeDFU -} FuriHalBootMode; - -/** Boot flags */ -typedef enum { - FuriHalBootFlagDefault=0, - FuriHalBootFlagFactoryReset=1, -} FuriHalBootFlag; - -/** Initialize boot subsystem - */ -void furi_hal_boot_init(); - -/** Set boot mode - * - * @param[in] mode FuriHalBootMode - */ -void furi_hal_boot_set_mode(FuriHalBootMode mode); - -/** Set boot flags - * - * @param[in] flags FuriHalBootFlag - */ -void furi_hal_boot_set_flags(FuriHalBootFlag flags); - -/** Get boot flag - * - * @return FuriHalBootFlag - */ -FuriHalBootFlag furi_hal_boot_get_flags(); - -#ifdef __cplusplus -} -#endif diff --git a/firmware/targets/furi-hal-include/furi-hal-bootloader.h b/firmware/targets/furi-hal-include/furi-hal-bootloader.h new file mode 100644 index 00000000..a1ef2c26 --- /dev/null +++ b/firmware/targets/furi-hal-include/furi-hal-bootloader.h @@ -0,0 +1,50 @@ +/** + * @file furi-hal-bootloader.h + * Bootloader HAL API + */ + +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** Boot modes */ +typedef enum { + FuriHalBootloaderModeNormal, + FuriHalBootloaderModeDFU +} FuriHalBootloaderMode; + +/** Boot flags */ +typedef enum { + FuriHalBootloaderFlagDefault=0, + FuriHalBootloaderFlagFactoryReset=1, +} FuriHalBootloaderFlag; + +/** Initialize boot subsystem + */ +void furi_hal_bootloader_init(); + +/** Set bootloader mode + * + * @param[in] mode FuriHalBootloaderMode + */ +void furi_hal_bootloader_set_mode(FuriHalBootloaderMode mode); + +/** Set bootloader flags + * + * @param[in] flags FuriHalBootloaderFlag + */ +void furi_hal_bootloader_set_flags(FuriHalBootloaderFlag flags); + +/** Get boot flag + * + * @return FuriHalBootloaderFlag + */ +FuriHalBootloaderFlag furi_hal_bootloader_get_flags(); + +#ifdef __cplusplus +} +#endif diff --git a/firmware/targets/furi-hal-include/furi-hal-vcp.h b/firmware/targets/furi-hal-include/furi-hal-vcp.h index 996232cf..27c04fb1 100644 --- a/firmware/targets/furi-hal-include/furi-hal-vcp.h +++ b/firmware/targets/furi-hal-include/furi-hal-vcp.h @@ -52,6 +52,12 @@ size_t furi_hal_vcp_rx_with_timeout(uint8_t* buffer, size_t size, uint32_t timeo */ void furi_hal_vcp_tx(const uint8_t* buffer, size_t size); +/** Check whether VCP is connected + * + * @return true if connected + */ +bool furi_hal_vcp_is_connected(void); + #ifdef __cplusplus } #endif diff --git a/firmware/targets/furi-hal-include/furi-hal-version.h b/firmware/targets/furi-hal-include/furi-hal-version.h index 00dc5cba..29343d57 100644 --- a/firmware/targets/furi-hal-include/furi-hal-version.h +++ b/firmware/targets/furi-hal-include/furi-hal-version.h @@ -148,7 +148,7 @@ const uint8_t* furi_hal_version_get_ble_mac(); * * @return Address of boot version structure. */ -const struct Version* furi_hal_version_get_boot_version(); +const struct Version* furi_hal_version_get_bootloader_version(); /** Get address of version structure of firmware. * diff --git a/firmware/targets/furi-hal-include/furi-hal.h b/firmware/targets/furi-hal-include/furi-hal.h index 757dd121..eb1fefeb 100644 --- a/firmware/targets/furi-hal-include/furi-hal.h +++ b/firmware/targets/furi-hal-include/furi-hal.h @@ -9,7 +9,7 @@ template struct STOP_EXTERNING_ME {}; #endif -#include "furi-hal-boot.h" +#include "furi-hal-bootloader.h" #include "furi-hal-clock.h" #include "furi-hal-crypto.h" #include "furi-hal-console.h" @@ -36,7 +36,7 @@ template struct STOP_EXTERNING_ME {}; #include "furi-hal-usb.h" #include "furi-hal-usb-hid.h" #include "furi-hal-compress.h" -#include "furi-hal-lpuart.h" +#include "furi-hal-uart.h" /** Init furi-hal */ void furi_hal_init(); diff --git a/lib/app-scened-template/file-worker.c b/lib/app-scened-template/file-worker.c index c38c1261..dec23cf2 100644 --- a/lib/app-scened-template/file-worker.c +++ b/lib/app-scened-template/file-worker.c @@ -79,6 +79,32 @@ bool file_worker_remove(FileWorker* file_worker, const char* filename) { return file_worker_check_common_errors(file_worker); } +void file_worker_get_next_filename( + FileWorker* file_worker, + const char* dirname, + const char* filename, + const char* fileextension, + string_t nextfilename) { + string_t temp_str; + string_init(temp_str); + uint16_t num = 0; + + string_printf(temp_str, "%s/%s%s", dirname, filename, fileextension); + + while(storage_common_stat(file_worker->api, string_get_cstr(temp_str), NULL) == FSE_OK) { + num++; + string_printf(temp_str, "%s/%s%d%s", dirname, filename, num, fileextension); + } + + if(num) { + string_printf(nextfilename, "%s%d", filename, num); + } else { + string_printf(nextfilename, "%s", filename); + } + + string_clear(temp_str); +} + bool file_worker_read(FileWorker* file_worker, void* buffer, uint16_t bytes_to_read) { if(!file_worker_read_internal(file_worker, buffer, bytes_to_read)) { return false; @@ -355,7 +381,11 @@ bool file_worker_read_until_buffered( return string_size(str_result) || *file_buf_cnt; } -bool file_worker_get_value_from_key(FileWorker* file_worker, string_t key, char delimiter, string_t value) { +bool file_worker_get_value_from_key( + FileWorker* file_worker, + string_t key, + char delimiter, + string_t value) { bool found = false; string_t next_line; string_t next_key; diff --git a/lib/app-scened-template/file-worker.h b/lib/app-scened-template/file-worker.h index 3a2b83de..1b5f3407 100644 --- a/lib/app-scened-template/file-worker.h +++ b/lib/app-scened-template/file-worker.h @@ -68,6 +68,22 @@ bool file_worker_mkdir(FileWorker* file_worker, const char* dirname); */ bool file_worker_remove(FileWorker* file_worker, const char* filename); +/** + * @brief Get next free filename. + * + * @param file_worker FileWorker instance + * @param dirname + * @param filename + * @param fileextension + * @param nextfilename return name + */ +void file_worker_get_next_filename( + FileWorker* file_worker, + const char* dirname, + const char* filename, + const char* fileextension, + string_t nextfilename); + /** * @brief Reads data from a file. * @@ -194,7 +210,11 @@ bool file_worker_read_until_buffered( * @param value value for given key * @return true on success */ -bool file_worker_get_value_from_key(FileWorker* file_worker, string_t key, char delimiter, string_t value); +bool file_worker_get_value_from_key( + FileWorker* file_worker, + string_t key, + char delimiter, + string_t value); /** * @brief Check whether file exist or not diff --git a/lib/subghz/protocols/subghz_protocol_came.c b/lib/subghz/protocols/subghz_protocol_came.c index cb1c6720..866e577b 100644 --- a/lib/subghz/protocols/subghz_protocol_came.c +++ b/lib/subghz/protocols/subghz_protocol_came.c @@ -182,7 +182,8 @@ void subghz_protocol_came_to_save_str(SubGhzProtocolCame* instance, string_t out bool subghz_protocol_came_to_load_protocol_from_file( FileWorker* file_worker, - SubGhzProtocolCame* instance) { + SubGhzProtocolCame* instance, + const char* file_path) { bool loaded = false; string_t temp_str; string_init(temp_str); diff --git a/lib/subghz/protocols/subghz_protocol_came.h b/lib/subghz/protocols/subghz_protocol_came.h index 3edcd8f9..603ca21a 100644 --- a/lib/subghz/protocols/subghz_protocol_came.h +++ b/lib/subghz/protocols/subghz_protocol_came.h @@ -56,15 +56,17 @@ void subghz_protocol_came_to_save_str(SubGhzProtocolCame* instance, string_t out * * @param file_worker - FileWorker file_worker * @param instance - SubGhzProtocolCame instance + * @param file_path - file path * @return bool */ bool subghz_protocol_came_to_load_protocol_from_file( FileWorker* file_worker, - SubGhzProtocolCame* instance); + SubGhzProtocolCame* instance, + const char* file_path); /** Loading protocol from bin data * * @param instance - SubGhzProtocolCame instance * @param context - SubGhzProtocolCommonLoad context */ -void subghz_decoder_came_to_load_protocol(SubGhzProtocolCame* instance, void* context); \ No newline at end of file +void subghz_decoder_came_to_load_protocol(SubGhzProtocolCame* instance, void* context); diff --git a/lib/subghz/protocols/subghz_protocol_came_atomo.c b/lib/subghz/protocols/subghz_protocol_came_atomo.c index 0422ff6a..37c0860f 100644 --- a/lib/subghz/protocols/subghz_protocol_came_atomo.c +++ b/lib/subghz/protocols/subghz_protocol_came_atomo.c @@ -2,9 +2,12 @@ #include "subghz_protocol_common.h" #include +#define SUBGHZ_NO_CAME_ATOMO_RAINBOW_TABLE 0xFFFFFFFFFFFFFFFF + struct SubGhzProtocolCameAtomo { SubGhzProtocolCommon common; ManchesterState manchester_saved_state; + const char* rainbow_table_file_name; }; typedef enum { @@ -39,11 +42,118 @@ void subghz_protocol_came_atomo_free(SubGhzProtocolCameAtomo* instance) { free(instance); } +void subghz_protocol_came_atomo_name_file(SubGhzProtocolCameAtomo* instance, const char* name) { + instance->rainbow_table_file_name = name; + printf("Loading CAME Atomo rainbow table %s\r\n", name); +} + +/** Read bytes from rainbow table + * + * @param instance - SubGhzProtocolCameAtomo* instance + * @param number_atomo_magic_xor + * @return atomo_magic_xor + */ +uint64_t subghz_came_atomo_get_atomo_magic_xor_in_file( + SubGhzProtocolCameAtomo* instance, + uint8_t number_atomo_magic_xor) { + if(!strcmp(instance->rainbow_table_file_name, "")) return SUBGHZ_NO_CAME_ATOMO_RAINBOW_TABLE; + + uint8_t buffer[sizeof(uint64_t)] = {0}; + uint32_t address = number_atomo_magic_xor * sizeof(uint64_t); + uint64_t atomo_magic_xor = 0; + + FileWorker* file_worker = file_worker_alloc(true); + if(file_worker_open( + file_worker, instance->rainbow_table_file_name, FSAM_READ, FSOM_OPEN_EXISTING)) { + file_worker_seek(file_worker, address, true); + file_worker_read(file_worker, &buffer, sizeof(uint64_t)); + for(size_t i = 0; i < sizeof(uint64_t); i++) { + atomo_magic_xor = (atomo_magic_xor << 8) | buffer[i]; + } + } else { + atomo_magic_xor = SUBGHZ_NO_CAME_ATOMO_RAINBOW_TABLE; + } + file_worker_close(file_worker); + file_worker_free(file_worker); + + return atomo_magic_xor; +} + /** Analysis of received data * * @param instance SubGhzProtocolCameAtomo instance */ void subghz_protocol_came_atomo_remote_controller(SubGhzProtocolCameAtomo* instance) { + /* + * 0x1fafef3ed0f7d9ef + * 0x185fcc1531ee86e7 + * 0x184fa96912c567ff + * 0x187f8a42f3dc38f7 + * 0x186f63915492a5cd + * 0x181f40bab58bfac5 + * 0x180f25c696a01bdd + * 0x183f06ed77b944d5 + * 0x182ef661d83d21a9 + * 0x18ded54a39247ea1 + * 0x18ceb0361a0f9fb9 + * 0x18fe931dfb16c0b1 + * 0x18ee7ace5c585d8b + * ........ + * transmission consists of 99 parcels with increasing counter while holding down the button + * with each new press, the counter in the encrypted part increases + * + * 0x1FAFF13ED0F7D9EF + * 0x1FAFF11ED0F7D9EF + * 0x1FAFF10ED0F7D9EF + * 0x1FAFF0FED0F7D9EF + * 0x1FAFF0EED0F7D9EF + * 0x1FAFF0DED0F7D9EF + * 0x1FAFF0CED0F7D9EF + * 0x1FAFF0BED0F7D9EF + * 0x1FAFF0AED0F7D9EF + * + * where 0x1FAF - parcel counter, 0хF0A - button press counter, + * 0xED0F7D9E - serial number, 0хF - key + * 0x1FAF parcel counter - 1 in the parcel queue ^ 0x185F = 0x07F0 + * 0x185f ^ 0x185F = 0x0000 + * 0x184f ^ 0x185F = 0x0010 + * 0x187f ^ 0x185F = 0x0020 + * ..... + * 0x182e ^ 0x185F = 0x0071 + * 0x18de ^ 0x185F = 0x0081 + * ..... + * 0x1e43 ^ 0x185F = 0x061C + * where the last nibble is incremented every 8 samples + * + * Decode + * + * 0x1cf6931dfb16c0b1 => 0x1cf6 + * 0x1cf6 ^ 0x185F = 0x04A9 + * 0x04A9 => 0x04A = 74 (dec) + * 74+1 % 32(atomo_magic_xor) = 11 + * GET atomo_magic_xor[11] = 0xXXXXXXXXXXXXXXXX + * 0x931dfb16c0b1 ^ 0xXXXXXXXXXXXXXXXX = 0xEF3ED0F7D9EF + * 0xEF3 ED0F7D9E F => 0xEF3 - CNT, 0xED0F7D9E - SN, 0xF - key + * + * */ + + uint16_t parcel_counter = instance->common.code_last_found >> 48; + parcel_counter = parcel_counter ^ 0x185F; + parcel_counter >>= 4; + uint8_t ind = (parcel_counter + 1) % 32; + uint64_t temp_data = instance->common.code_last_found & 0x0000FFFFFFFFFFFF; + uint64_t atomo_magic_xor = subghz_came_atomo_get_atomo_magic_xor_in_file(instance, ind); + + if(atomo_magic_xor != SUBGHZ_NO_CAME_ATOMO_RAINBOW_TABLE) { + temp_data = temp_data ^ atomo_magic_xor; + instance->common.cnt = temp_data >> 36; + instance->common.serial = (temp_data >> 4) & 0x000FFFFFFFF; + instance->common.btn = temp_data & 0xF; + } else { + instance->common.cnt = 0; + instance->common.serial = 0; + instance->common.btn = 0; + } } void subghz_protocol_came_atomo_reset(SubGhzProtocolCameAtomo* instance) { @@ -89,26 +199,10 @@ void subghz_protocol_came_atomo_parse( } else if(DURATION_DIFF(duration, instance->common.te_long) < instance->common.te_delta) { event = ManchesterEventLongLow; } else if(duration >= (instance->common.te_long * 2 + instance->common.te_delta)) { - if(instance->common.code_count_bit >= + if(instance->common.code_count_bit == instance->common.code_min_count_bit_for_found) { instance->common.code_last_found = instance->common.code_found; instance->common.code_last_count_bit = instance->common.code_count_bit; - // uint32_t code_found_hi = instance->common.code_last_found >> 32; - // uint32_t code_found_lo = instance->common.code_last_found & 0x00000000ffffffff; - - // uint64_t code_found_reverse = subghz_protocol_common_reverse_key( - // instance->common.code_last_found, instance->common.code_last_count_bit); - - // uint32_t code_found_reverse_hi = code_found_reverse >> 32; - // uint32_t code_found_reverse_lo = code_found_reverse & 0x00000000ffffffff; - // FURI_LOG_I( - // "ATOMO", - // "%08lX%08lX %08lX%08lX %d", - // code_found_hi, - // code_found_lo, - // code_found_reverse_hi, - // code_found_reverse_lo, - // instance->common.code_last_count_bit); if(instance->common.callback) instance->common.callback( (SubGhzProtocolCommon*)instance, instance->common.context); @@ -151,17 +245,24 @@ void subghz_protocol_came_atomo_parse( } } void subghz_protocol_came_atomo_to_str(SubGhzProtocolCameAtomo* instance, string_t output) { + subghz_protocol_came_atomo_remote_controller(instance); uint32_t code_found_hi = instance->common.code_last_found >> 32; uint32_t code_found_lo = instance->common.code_last_found & 0x00000000ffffffff; string_cat_printf( output, - "%s %dbit\r\n" - "Key:0x%lX%08lX\r\n", + "%s %db\r\n" + "Key:0x%lX%08lX\r\n" + "Sn:0x%08lX Btn:0x%01X\r\n" + "Cnt:0x%03X\r\n", + instance->common.name, instance->common.code_last_count_bit, code_found_hi, - code_found_lo); + code_found_lo, + instance->common.serial, + instance->common.btn, + instance->common.cnt); } // void subghz_protocol_came_atomo_to_save_str(SubGhzProtocolCameAtomo* instance, string_t output) { @@ -178,7 +279,8 @@ void subghz_protocol_came_atomo_to_str(SubGhzProtocolCameAtomo* instance, string // bool subghz_protocol_came_atomo_to_load_protocol_from_file( // FileWorker* file_worker, -// SubGhzProtocolCameAtomo* instance) { +// SubGhzProtocolCameAtomo* instance, +// const char* file_path) { // bool loaded = false; // string_t temp_str; // string_init(temp_str); diff --git a/lib/subghz/protocols/subghz_protocol_came_atomo.h b/lib/subghz/protocols/subghz_protocol_came_atomo.h index 9547fec2..2b349399 100644 --- a/lib/subghz/protocols/subghz_protocol_came_atomo.h +++ b/lib/subghz/protocols/subghz_protocol_came_atomo.h @@ -16,6 +16,13 @@ SubGhzProtocolCameAtomo* subghz_protocol_came_atomo_alloc(); */ void subghz_protocol_came_atomo_free(SubGhzProtocolCameAtomo* instance); +/** File name rainbow table CAME Atomo + * + * @param instance - SubGhzProtocolCameAtomo instance + * @param file_name - "path/file_name" + */ +void subghz_protocol_came_atomo_name_file(SubGhzProtocolCameAtomo* instance, const char* name); + // /** Get upload protocol // * // * @param instance - SubGhzProtocolCameAtomo instance @@ -59,11 +66,13 @@ void subghz_protocol_came_atomo_to_str(SubGhzProtocolCameAtomo* instance, string // * // * @param file_worker - FileWorker file_worker // * @param instance - SubGhzProtocolCameAtomo instance +// * @param file_path - file path // * @return bool // */ // bool subghz_protocol_came_atomo_to_load_protocol_from_file( // FileWorker* file_worker, -// SubGhzProtocolCameAtomo* instance); +// SubGhzProtocolCameAtomo* instance, +// const char* file_path); /** Loading protocol from bin data * diff --git a/lib/subghz/protocols/subghz_protocol_came_twee.c b/lib/subghz/protocols/subghz_protocol_came_twee.c index 7a7274e8..8e527c8f 100644 --- a/lib/subghz/protocols/subghz_protocol_came_twee.c +++ b/lib/subghz/protocols/subghz_protocol_came_twee.c @@ -341,7 +341,8 @@ void subghz_protocol_came_twee_to_save_str(SubGhzProtocolCameTwee* instance, str bool subghz_protocol_came_twee_to_load_protocol_from_file( FileWorker* file_worker, - SubGhzProtocolCameTwee* instance) { + SubGhzProtocolCameTwee* instance, + const char* file_path) { bool loaded = false; string_t temp_str; string_init(temp_str); diff --git a/lib/subghz/protocols/subghz_protocol_came_twee.h b/lib/subghz/protocols/subghz_protocol_came_twee.h index 09a36e21..c8f3d465 100644 --- a/lib/subghz/protocols/subghz_protocol_came_twee.h +++ b/lib/subghz/protocols/subghz_protocol_came_twee.h @@ -59,11 +59,13 @@ void subghz_protocol_came_twee_to_save_str(SubGhzProtocolCameTwee* instance, str * * @param file_worker - FileWorker file_worker * @param instance - SubGhzProtocolCameTwee instance + * @param file_path - file path * @return bool */ bool subghz_protocol_came_twee_to_load_protocol_from_file( FileWorker* file_worker, - SubGhzProtocolCameTwee* instance); + SubGhzProtocolCameTwee* instance, + const char* file_path); /** Loading protocol from bin data * diff --git a/lib/subghz/protocols/subghz_protocol_common.c b/lib/subghz/protocols/subghz_protocol_common.c index 2bc0133d..be1fea94 100644 --- a/lib/subghz/protocols/subghz_protocol_common.c +++ b/lib/subghz/protocols/subghz_protocol_common.c @@ -2,7 +2,6 @@ #include #include - SubGhzProtocolCommonEncoder* subghz_protocol_encoder_common_alloc() { SubGhzProtocolCommonEncoder* instance = furi_alloc(sizeof(SubGhzProtocolCommonEncoder)); instance->upload = furi_alloc(SUBGHZ_ENCODER_UPLOAD_MAX_SIZE * sizeof(LevelDuration)); @@ -13,6 +12,9 @@ SubGhzProtocolCommonEncoder* subghz_protocol_encoder_common_alloc() { void subghz_protocol_encoder_common_free(SubGhzProtocolCommonEncoder* instance) { furi_assert(instance); + if(instance->callback_end) { + instance->callback_end((SubGhzProtocolCommon*)instance->context_end); + } free(instance->upload); free(instance); } @@ -22,10 +24,34 @@ size_t subghz_encoder_common_get_repeat_left(SubGhzProtocolCommonEncoder* instan return instance->repeat; } +void subghz_protocol_encoder_common_set_callback( + SubGhzProtocolCommonEncoder* instance, + SubGhzProtocolCommonEncoderCallback callback, + void* context) { + furi_assert(instance); + furi_assert(callback); + instance->callback = callback; + instance->context = context; +} + +void subghz_protocol_encoder_common_set_callback_end( + SubGhzProtocolCommonEncoder* instance, + SubGhzProtocolCommonEncoderCallbackEnd callback_end, + void* context_end) { + furi_assert(instance); + furi_assert(callback_end); + instance->callback_end = callback_end; + instance->context_end = context_end; +} + LevelDuration subghz_protocol_encoder_common_yield(void* context) { SubGhzProtocolCommonEncoder* instance = context; - if(instance->repeat == 0){ + if(instance->callback) { + return instance->callback((SubGhzProtocolCommon*)instance->context); + } + + if(instance->repeat == 0) { return level_duration_reset(); } @@ -39,46 +65,53 @@ LevelDuration subghz_protocol_encoder_common_yield(void* context) { return ret; } -void subghz_protocol_common_add_bit(SubGhzProtocolCommon *common, uint8_t bit){ +void subghz_protocol_common_add_bit(SubGhzProtocolCommon* common, uint8_t bit) { common->code_found = common->code_found << 1 | bit; common->code_count_bit++; } -bool subghz_protocol_common_check_interval(SubGhzProtocolCommon *common, uint32_t duration, uint16_t duration_check) { - if ((duration_check >= (duration - common->te_delta))&&(duration_check <= (duration + common->te_delta))){ +bool subghz_protocol_common_check_interval( + SubGhzProtocolCommon* common, + uint32_t duration, + uint16_t duration_check) { + if((duration_check >= (duration - common->te_delta)) && + (duration_check <= (duration + common->te_delta))) { return true; } else { return false; } } -uint64_t subghz_protocol_common_reverse_key(uint64_t key, uint8_t count_bit){ - uint64_t key_reverse=0; - for(uint8_t i=0; icallback = callback; common->context = context; } - void subghz_protocol_common_to_str(SubGhzProtocolCommon* instance, string_t output) { - if (instance->to_string) { + if(instance->to_string) { instance->to_string(instance, output); } else { uint32_t code_found_hi = instance->code_found >> 32; uint32_t code_found_lo = instance->code_found & 0x00000000ffffffff; - uint64_t code_found_reverse = subghz_protocol_common_reverse_key(instance->code_found, instance->code_count_bit); + uint64_t code_found_reverse = + subghz_protocol_common_reverse_key(instance->code_found, instance->code_count_bit); - uint32_t code_found_reverse_hi = code_found_reverse>>32; - uint32_t code_found_reverse_lo = code_found_reverse&0x00000000ffffffff; + uint32_t code_found_reverse_hi = code_found_reverse >> 32; + uint32_t code_found_reverse_lo = code_found_reverse & 0x00000000ffffffff; - if (code_found_hi>0) { + if(code_found_hi > 0) { string_cat_printf( output, "Protocol %s, %d Bit\r\n" @@ -92,8 +125,7 @@ void subghz_protocol_common_to_str(SubGhzProtocolCommon* instance, string_t outp code_found_reverse_hi, code_found_reverse_lo, instance->serial, - instance->btn - ); + instance->btn); } else { string_cat_printf( output, @@ -108,8 +140,7 @@ void subghz_protocol_common_to_str(SubGhzProtocolCommon* instance, string_t outp code_found_reverse_hi, code_found_reverse_lo, instance->serial, - instance->btn - ); + instance->btn); } } } @@ -124,9 +155,9 @@ bool subghz_protocol_common_read_hex(string_t str, uint8_t* buff, uint16_t len) if(hex_char_to_hex_nibble(string_get_char(str, 0), &nibble_high) && hex_char_to_hex_nibble(string_get_char(str, 1), &nibble_low)) { buff[i] = (nibble_high << 4) | nibble_low; - if(string_size(str)>2){ + if(string_size(str) > 2) { string_right(str, 2); - }else if(icommon.code_last_found = data->code_found; instance->common.code_last_count_bit = data->code_count_bit; subghz_protocol_gate_tx_check_remote_controller(instance); -} \ No newline at end of file +} diff --git a/lib/subghz/protocols/subghz_protocol_gate_tx.h b/lib/subghz/protocols/subghz_protocol_gate_tx.h index 7b91f7dc..57ca5832 100644 --- a/lib/subghz/protocols/subghz_protocol_gate_tx.h +++ b/lib/subghz/protocols/subghz_protocol_gate_tx.h @@ -22,7 +22,9 @@ void subghz_protocol_gate_tx_free(SubGhzProtocolGateTX* instance); * @param encoder - SubGhzProtocolCommonEncoder encoder * @return bool */ -bool subghz_protocol_gate_tx_send_key(SubGhzProtocolGateTX* instance, SubGhzProtocolCommonEncoder* encoder); +bool subghz_protocol_gate_tx_send_key( + SubGhzProtocolGateTX* instance, + SubGhzProtocolCommonEncoder* encoder); /** Reset internal state * @param instance - SubGhzProtocolGateTX instance @@ -54,9 +56,13 @@ void subghz_protocol_gate_tx_to_save_str(SubGhzProtocolGateTX* instance, string_ * * @param file_worker - FileWorker file_worker * @param instance - SubGhzProtocolGateTX instance + * @param file_path - file path * @return bool */ -bool subghz_protocol_gate_tx_to_load_protocol_from_file(FileWorker* file_worker, SubGhzProtocolGateTX* instance); +bool subghz_protocol_gate_tx_to_load_protocol_from_file( + FileWorker* file_worker, + SubGhzProtocolGateTX* instance, + const char* file_path); /** Loading protocol from bin data * diff --git a/lib/subghz/protocols/subghz_protocol_hormann.c b/lib/subghz/protocols/subghz_protocol_hormann.c new file mode 100644 index 00000000..0d4df921 --- /dev/null +++ b/lib/subghz/protocols/subghz_protocol_hormann.c @@ -0,0 +1,254 @@ +#include "subghz_protocol_hormann.h" +#include "subghz_protocol_common.h" + +struct SubGhzProtocolHormann { + SubGhzProtocolCommon common; +}; + +typedef enum { + HormannDecoderStepReset = 0, + HormannDecoderStepFoundStartHeader, + HormannDecoderStepFoundHeader, + HormannDecoderStepFoundStartBit, + HormannDecoderStepSaveDuration, + HormannDecoderStepCheckDuration, +} HormannDecoderStep; + +SubGhzProtocolHormann* subghz_protocol_hormann_alloc() { + SubGhzProtocolHormann* instance = furi_alloc(sizeof(SubGhzProtocolHormann)); + + instance->common.name = "Hormann HSM"; + instance->common.code_min_count_bit_for_found = 44; + instance->common.te_short = 511; + instance->common.te_long = 1022; + instance->common.te_delta = 200; + instance->common.type_protocol = SubGhzProtocolCommonTypeStatic; + instance->common.to_string = (SubGhzProtocolCommonToStr)subghz_protocol_hormann_to_str; + instance->common.to_save_string = + (SubGhzProtocolCommonGetStrSave)subghz_protocol_hormann_to_save_str; + instance->common.to_load_protocol_from_file = + (SubGhzProtocolCommonLoadFromFile)subghz_protocol_hormann_to_load_protocol_from_file; + instance->common.to_load_protocol = + (SubGhzProtocolCommonLoadFromRAW)subghz_decoder_hormann_to_load_protocol; + instance->common.get_upload_protocol = + (SubGhzProtocolCommonEncoderGetUpLoad)subghz_protocol_hormann_send_key; + + return instance; +} + +void subghz_protocol_hormann_free(SubGhzProtocolHormann* instance) { + furi_assert(instance); + free(instance); +} + +bool subghz_protocol_hormann_send_key( + SubGhzProtocolHormann* instance, + SubGhzProtocolCommonEncoder* encoder) { + furi_assert(instance); + furi_assert(encoder); + + size_t index = 0; + encoder->size_upload = 3 + (instance->common.code_last_count_bit * 2 + 2) * 20 + 1; + if(encoder->size_upload > SUBGHZ_ENCODER_UPLOAD_MAX_SIZE) return false; + //Send header + encoder->upload[index++] = + level_duration_make(false, (uint32_t)instance->common.te_short * 64); + encoder->upload[index++] = level_duration_make(true, (uint32_t)instance->common.te_short * 64); + encoder->upload[index++] = + level_duration_make(false, (uint32_t)instance->common.te_short * 64); + encoder->repeat = 10; + + for(size_t repeat = 0; repeat < 20; repeat++) { + //Send start bit + encoder->upload[index++] = + level_duration_make(true, (uint32_t)instance->common.te_short * 24); + encoder->upload[index++] = level_duration_make(false, (uint32_t)instance->common.te_short); + //Send key data + for(uint8_t i = instance->common.code_last_count_bit; i > 0; i--) { + if(bit_read(instance->common.code_last_found, i - 1)) { + //send bit 1 + encoder->upload[index++] = + level_duration_make(true, (uint32_t)instance->common.te_long); + encoder->upload[index++] = + level_duration_make(false, (uint32_t)instance->common.te_short); + } else { + //send bit 0 + encoder->upload[index++] = + level_duration_make(true, (uint32_t)instance->common.te_short); + encoder->upload[index++] = + level_duration_make(false, (uint32_t)instance->common.te_long); + } + } + } + encoder->upload[index++] = level_duration_make(true, (uint32_t)instance->common.te_short * 24); + return true; +} + +void subghz_protocol_hormann_reset(SubGhzProtocolHormann* instance) { + instance->common.parser_step = HormannDecoderStepReset; +} + +void subghz_protocol_hormann_parse(SubGhzProtocolHormann* instance, bool level, uint32_t duration) { + switch(instance->common.parser_step) { + case HormannDecoderStepReset: + if((level) && (DURATION_DIFF(duration, instance->common.te_short * 64) < + instance->common.te_delta * 64)) { + instance->common.parser_step = HormannDecoderStepFoundStartHeader; + } else { + instance->common.parser_step = HormannDecoderStepReset; + } + break; + case HormannDecoderStepFoundStartHeader: + if((!level) && (DURATION_DIFF(duration, instance->common.te_short * 64) < + instance->common.te_delta * 64)) { + instance->common.parser_step = HormannDecoderStepFoundHeader; + } else { + instance->common.parser_step = HormannDecoderStepReset; + } + break; + case HormannDecoderStepFoundHeader: + if((level) && (DURATION_DIFF(duration, instance->common.te_short * 24) < + instance->common.te_delta * 24)) { + instance->common.parser_step = HormannDecoderStepFoundStartBit; + } else { + instance->common.parser_step = HormannDecoderStepReset; + } + break; + case HormannDecoderStepFoundStartBit: + if((!level) && + (DURATION_DIFF(duration, instance->common.te_short) < instance->common.te_delta)) { + instance->common.parser_step = HormannDecoderStepSaveDuration; + instance->common.code_found = 0; + instance->common.code_count_bit = 0; + } else { + instance->common.parser_step = HormannDecoderStepReset; + } + break; + case HormannDecoderStepSaveDuration: + if(level) { //save interval + if(duration >= (instance->common.te_short * 5)) { + instance->common.parser_step = HormannDecoderStepFoundStartBit; + if(instance->common.code_count_bit >= + instance->common.code_min_count_bit_for_found) { + instance->common.serial = 0x0; + instance->common.btn = 0x0; + + instance->common.code_last_found = instance->common.code_found; + instance->common.code_last_count_bit = instance->common.code_count_bit; + + if(instance->common.callback) + instance->common.callback( + (SubGhzProtocolCommon*)instance, instance->common.context); + } + break; + } + instance->common.te_last = duration; + instance->common.parser_step = HormannDecoderStepCheckDuration; + } else { + instance->common.parser_step = HormannDecoderStepReset; + } + break; + case HormannDecoderStepCheckDuration: + if(!level) { + if((DURATION_DIFF(instance->common.te_last, instance->common.te_short) < + instance->common.te_delta) && + (DURATION_DIFF(duration, instance->common.te_long) < instance->common.te_delta)) { + subghz_protocol_common_add_bit(&instance->common, 0); + instance->common.parser_step = HormannDecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->common.te_last, instance->common.te_long) < + instance->common.te_delta) && + (DURATION_DIFF(duration, instance->common.te_short) < instance->common.te_delta)) { + subghz_protocol_common_add_bit(&instance->common, 1); + instance->common.parser_step = HormannDecoderStepSaveDuration; + } else + instance->common.parser_step = HormannDecoderStepReset; + } else { + instance->common.parser_step = HormannDecoderStepReset; + } + break; + } +} + +void subghz_protocol_hormann_to_str(SubGhzProtocolHormann* instance, string_t output) { + uint32_t code_found_hi = instance->common.code_last_found >> 32; + uint32_t code_found_lo = instance->common.code_last_found & 0x00000000ffffffff; + instance->common.btn = (instance->common.code_last_found >> 4) & 0xF; + + string_cat_printf( + output, + "%s\r\n" + "%dbit\r\n" + "Key:0x%03lX%08lX\r\n" + "Btn:0x%01X", + instance->common.name, + instance->common.code_last_count_bit, + code_found_hi, + code_found_lo, + instance->common.btn); +} + +void subghz_protocol_hormann_to_save_str(SubGhzProtocolHormann* instance, string_t output) { + string_printf( + output, + "Protocol: %s\n" + "Bit: %d\n" + "Key: %08lX%08lX\n", + instance->common.name, + instance->common.code_last_count_bit, + (uint32_t)(instance->common.code_last_found >> 32), + (uint32_t)(instance->common.code_last_found & 0x00000000ffffffff)); +} + +bool subghz_protocol_hormann_to_load_protocol_from_file( + FileWorker* file_worker, + SubGhzProtocolHormann* instance, + const char* file_path) { + bool loaded = false; + string_t temp_str; + string_init(temp_str); + int res = 0; + int data = 0; + + do { + // Read and parse bit data from 2nd line + if(!file_worker_read_until(file_worker, temp_str, '\n')) { + break; + } + res = sscanf(string_get_cstr(temp_str), "Bit: %d\n", &data); + if(res != 1) { + break; + } + instance->common.code_last_count_bit = (uint8_t)data; + + // Read and parse key data from 3nd line + if(!file_worker_read_until(file_worker, temp_str, '\n')) { + break; + } + // strlen("Key: ") = 5 + string_right(temp_str, 5); + + uint8_t buf_key[8] = {0}; + if(!subghz_protocol_common_read_hex(temp_str, buf_key, 8)) { + break; + } + + for(uint8_t i = 0; i < 8; i++) { + instance->common.code_last_found = instance->common.code_last_found << 8 | buf_key[i]; + } + + loaded = true; + } while(0); + + string_clear(temp_str); + + return loaded; +} + +void subghz_decoder_hormann_to_load_protocol(SubGhzProtocolHormann* instance, void* context) { + furi_assert(context); + furi_assert(instance); + SubGhzProtocolCommonLoad* data = context; + instance->common.code_last_found = data->code_found; + instance->common.code_last_count_bit = data->code_count_bit; +} diff --git a/lib/subghz/protocols/subghz_protocol_hormann.h b/lib/subghz/protocols/subghz_protocol_hormann.h new file mode 100644 index 00000000..2d2276fb --- /dev/null +++ b/lib/subghz/protocols/subghz_protocol_hormann.h @@ -0,0 +1,72 @@ +#pragma once + +#include "subghz_protocol_common.h" + +typedef struct SubGhzProtocolHormann SubGhzProtocolHormann; + +/** Allocate SubGhzProtocolHormann + * + * @return SubGhzProtocolHormann* + */ +SubGhzProtocolHormann* subghz_protocol_hormann_alloc(); + +/** Free SubGhzProtocolHormann + * + * @param instance + */ +void subghz_protocol_hormann_free(SubGhzProtocolHormann* instance); + +/** Get upload protocol + * + * @param instance - SubGhzProtocolHormann instance + * @param encoder - SubGhzProtocolCommonEncoder encoder + * @return bool + */ +bool subghz_protocol_hormann_send_key( + SubGhzProtocolHormann* instance, + SubGhzProtocolCommonEncoder* encoder); + +/** Reset internal state + * @param instance - SubGhzProtocolHormann instance + */ +void subghz_protocol_hormann_reset(SubGhzProtocolHormann* instance); + +/** Parse accepted duration + * + * @param instance - SubGhzProtocolHormann instance + * @param data - LevelDuration level_duration + */ +void subghz_protocol_hormann_parse(SubGhzProtocolHormann* instance, bool level, uint32_t duration); + +/** Outputting information from the parser + * + * @param instance - SubGhzProtocolHormann* instance + * @param output - output string + */ +void subghz_protocol_hormann_to_str(SubGhzProtocolHormann* instance, string_t output); + +/** Get a string to save the protocol + * + * @param instance - SubGhzProtocolHormann instance + * @param output - the resulting string + */ +void subghz_protocol_hormann_to_save_str(SubGhzProtocolHormann* instance, string_t output); + +/** Loading protocol from file + * + * @param file_worker - FileWorker file_worker + * @param instance - SubGhzProtocolHormann instance + * @param file_path - file path + * @return bool + */ +bool subghz_protocol_hormann_to_load_protocol_from_file( + FileWorker* file_worker, + SubGhzProtocolHormann* instance, + const char* file_path); + +/** Loading protocol from bin data + * + * @param instance - SubGhzProtocolHormann instance + * @param context - SubGhzProtocolCommonLoad context + */ +void subghz_decoder_hormann_to_load_protocol(SubGhzProtocolHormann* instance, void* context); diff --git a/lib/subghz/protocols/subghz_protocol_keeloq.c b/lib/subghz/protocols/subghz_protocol_keeloq.c index 9c81dc09..609809ca 100644 --- a/lib/subghz/protocols/subghz_protocol_keeloq.c +++ b/lib/subghz/protocols/subghz_protocol_keeloq.c @@ -436,7 +436,8 @@ void subghz_protocol_keeloq_to_save_str(SubGhzProtocolKeeloq* instance, string_t bool subghz_protocol_keeloq_to_load_protocol_from_file( FileWorker* file_worker, - SubGhzProtocolKeeloq* instance) { + SubGhzProtocolKeeloq* instance, + const char* file_path) { bool loaded = false; string_t temp_str; string_init(temp_str); diff --git a/lib/subghz/protocols/subghz_protocol_keeloq.h b/lib/subghz/protocols/subghz_protocol_keeloq.h index 7b2cc6d8..4312f653 100644 --- a/lib/subghz/protocols/subghz_protocol_keeloq.h +++ b/lib/subghz/protocols/subghz_protocol_keeloq.h @@ -53,7 +53,9 @@ uint64_t subghz_protocol_keeloq_gen_key(void* context); * @param encoder - SubGhzProtocolCommonEncoder encoder * @return bool */ -bool subghz_protocol_keeloq_send_key(SubGhzProtocolKeeloq* instance, SubGhzProtocolCommonEncoder* encoder); +bool subghz_protocol_keeloq_send_key( + SubGhzProtocolKeeloq* instance, + SubGhzProtocolCommonEncoder* encoder); /** Reset internal state * @param instance - SubGhzProtocolKeeloq instance @@ -85,9 +87,13 @@ void subghz_protocol_keeloq_to_save_str(SubGhzProtocolKeeloq* instance, string_t * * @param file_worker - FileWorker file_worker * @param instance - SubGhzProtocolKeeloq instance + * @param file_path - file path * @return bool */ -bool subghz_protocol_keeloq_to_load_protocol_from_file(FileWorker* file_worker, SubGhzProtocolKeeloq* instance); +bool subghz_protocol_keeloq_to_load_protocol_from_file( + FileWorker* file_worker, + SubGhzProtocolKeeloq* instance, + const char* file_path); /** Loading protocol from bin data * diff --git a/lib/subghz/protocols/subghz_protocol_nero_radio.c b/lib/subghz/protocols/subghz_protocol_nero_radio.c index 2331e3ab..af2a8f1b 100644 --- a/lib/subghz/protocols/subghz_protocol_nero_radio.c +++ b/lib/subghz/protocols/subghz_protocol_nero_radio.c @@ -245,7 +245,8 @@ void subghz_protocol_nero_radio_to_save_str(SubGhzProtocolNeroRadio* instance, s bool subghz_protocol_nero_radio_to_load_protocol_from_file( FileWorker* file_worker, - SubGhzProtocolNeroRadio* instance) { + SubGhzProtocolNeroRadio* instance, + const char* file_path) { bool loaded = false; string_t temp_str; string_init(temp_str); diff --git a/lib/subghz/protocols/subghz_protocol_nero_radio.h b/lib/subghz/protocols/subghz_protocol_nero_radio.h index 11f13201..8d00aad2 100644 --- a/lib/subghz/protocols/subghz_protocol_nero_radio.h +++ b/lib/subghz/protocols/subghz_protocol_nero_radio.h @@ -22,7 +22,9 @@ void subghz_protocol_nero_radio_free(SubGhzProtocolNeroRadio* instance); * @param encoder - SubGhzProtocolCommonEncoder encoder * @return bool */ -bool subghz_protocol_nero_radio_send_key(SubGhzProtocolNeroRadio* instance, SubGhzProtocolCommonEncoder* encoder); +bool subghz_protocol_nero_radio_send_key( + SubGhzProtocolNeroRadio* instance, + SubGhzProtocolCommonEncoder* encoder); /** Reset internal state * @param instance - SubGhzProtocolNeroRadio instance @@ -40,7 +42,10 @@ void subghz_protocol_nero_radio_check_remote_controller(SubGhzProtocolNeroRadio* * @param instance - SubGhzProtocolNeroRadio instance * @param data - LevelDuration level_duration */ -void subghz_protocol_nero_radio_parse(SubGhzProtocolNeroRadio* instance, bool level, uint32_t duration); +void subghz_protocol_nero_radio_parse( + SubGhzProtocolNeroRadio* instance, + bool level, + uint32_t duration); /** Outputting information from the parser * @@ -60,9 +65,13 @@ void subghz_protocol_nero_radio_to_save_str(SubGhzProtocolNeroRadio* instance, s * * @param file_worker - FileWorker file_worker * @param instance - SubGhzProtocolNeroRadio instance + * @param file_path - file path * @return bool */ -bool subghz_protocol_nero_radio_to_load_protocol_from_file(FileWorker* file_worker, SubGhzProtocolNeroRadio* instance); +bool subghz_protocol_nero_radio_to_load_protocol_from_file( + FileWorker* file_worker, + SubGhzProtocolNeroRadio* instance, + const char* file_path); /** Loading protocol from bin data * diff --git a/lib/subghz/protocols/subghz_protocol_nero_sketch.c b/lib/subghz/protocols/subghz_protocol_nero_sketch.c index 24a7efc0..c1948ecf 100644 --- a/lib/subghz/protocols/subghz_protocol_nero_sketch.c +++ b/lib/subghz/protocols/subghz_protocol_nero_sketch.c @@ -238,7 +238,8 @@ void subghz_protocol_nero_sketch_to_save_str(SubGhzProtocolNeroSketch* instance, bool subghz_protocol_nero_sketch_to_load_protocol_from_file( FileWorker* file_worker, - SubGhzProtocolNeroSketch* instance) { + SubGhzProtocolNeroSketch* instance, + const char* file_path) { bool loaded = false; string_t temp_str; string_init(temp_str); @@ -282,4 +283,4 @@ void subghz_decoder_nero_sketch_to_load_protocol(SubGhzProtocolNeroSketch* insta SubGhzProtocolCommonLoad* data = context; instance->common.code_last_found = data->code_found; instance->common.code_last_count_bit = data->code_count_bit; -} \ No newline at end of file +} diff --git a/lib/subghz/protocols/subghz_protocol_nero_sketch.h b/lib/subghz/protocols/subghz_protocol_nero_sketch.h index 0fe781c8..8c3af225 100644 --- a/lib/subghz/protocols/subghz_protocol_nero_sketch.h +++ b/lib/subghz/protocols/subghz_protocol_nero_sketch.h @@ -22,7 +22,9 @@ void subghz_protocol_nero_sketch_free(SubGhzProtocolNeroSketch* instance); * @param encoder - SubGhzProtocolCommonEncoder encoder * @return bool */ -bool subghz_protocol_nero_sketch_send_key(SubGhzProtocolNeroSketch* instance, SubGhzProtocolCommonEncoder* encoder); +bool subghz_protocol_nero_sketch_send_key( + SubGhzProtocolNeroSketch* instance, + SubGhzProtocolCommonEncoder* encoder); /** Reset internal state * @param instance - SubGhzProtocolNeroSketch instance @@ -40,7 +42,10 @@ void subghz_protocol_nero_sketch_check_remote_controller(SubGhzProtocolNeroSketc * @param instance - SubGhzProtocolNeroSketch instance * @param data - LevelDuration level_duration */ -void subghz_protocol_nero_sketch_parse(SubGhzProtocolNeroSketch* instance, bool level, uint32_t duration); +void subghz_protocol_nero_sketch_parse( + SubGhzProtocolNeroSketch* instance, + bool level, + uint32_t duration); /** Outputting information from the parser * @@ -60,13 +65,17 @@ void subghz_protocol_nero_sketch_to_save_str(SubGhzProtocolNeroSketch* instance, * * @param file_worker - FileWorker file_worker * @param instance - SubGhzProtocolNeroSketch instance + * @param file_path - file path * @return bool */ -bool subghz_protocol_nero_sketch_to_load_protocol_from_file(FileWorker* file_worker, SubGhzProtocolNeroSketch* instance); +bool subghz_protocol_nero_sketch_to_load_protocol_from_file( + FileWorker* file_worker, + SubGhzProtocolNeroSketch* instance, + const char* file_path); /** Loading protocol from bin data * * @param instance - SubGhzProtocolNeroSketch instance * @param context - SubGhzProtocolCommonLoad context */ -void subghz_decoder_nero_sketch_to_load_protocol(SubGhzProtocolNeroSketch* instance, void* context); \ No newline at end of file +void subghz_decoder_nero_sketch_to_load_protocol(SubGhzProtocolNeroSketch* instance, void* context); diff --git a/lib/subghz/protocols/subghz_protocol_nice_flo.c b/lib/subghz/protocols/subghz_protocol_nice_flo.c index 25991380..6c16315e 100644 --- a/lib/subghz/protocols/subghz_protocol_nice_flo.c +++ b/lib/subghz/protocols/subghz_protocol_nice_flo.c @@ -179,7 +179,8 @@ void subghz_protocol_nice_flo_to_save_str(SubGhzProtocolNiceFlo* instance, strin bool subghz_protocol_nice_flo_to_load_protocol_from_file( FileWorker* file_worker, - SubGhzProtocolNiceFlo* instance) { + SubGhzProtocolNiceFlo* instance, + const char* file_path) { bool loaded = false; string_t temp_str; string_init(temp_str); @@ -224,4 +225,4 @@ void subghz_decoder_nice_flo_to_load_protocol(SubGhzProtocolNiceFlo* instance, v instance->common.code_last_count_bit = data->code_count_bit; instance->common.serial = 0x0; instance->common.btn = 0x0; -} \ No newline at end of file +} diff --git a/lib/subghz/protocols/subghz_protocol_nice_flo.h b/lib/subghz/protocols/subghz_protocol_nice_flo.h index c5ed8759..3695fea3 100644 --- a/lib/subghz/protocols/subghz_protocol_nice_flo.h +++ b/lib/subghz/protocols/subghz_protocol_nice_flo.h @@ -22,7 +22,9 @@ void subghz_protocol_nice_flo_free(SubGhzProtocolNiceFlo* instance); * @param encoder - SubGhzProtocolCommonEncoder encoder * @return bool */ -bool subghz_protocol_nice_flo_send_key(SubGhzProtocolNiceFlo* instance, SubGhzProtocolCommonEncoder* encoder); +bool subghz_protocol_nice_flo_send_key( + SubGhzProtocolNiceFlo* instance, + SubGhzProtocolCommonEncoder* encoder); /** Reset internal state * @param instance - SubGhzProtocolNiceFlo instance @@ -54,9 +56,13 @@ void subghz_protocol_nice_flo_to_save_str(SubGhzProtocolNiceFlo* instance, strin * * @param file_worker - FileWorker file_worker * @param instance - SubGhzProtocolNiceFlo instance + * @param file_path - file path * @return bool */ -bool subghz_protocol_nice_flo_to_load_protocol_from_file(FileWorker* file_worker, SubGhzProtocolNiceFlo* instance); +bool subghz_protocol_nice_flo_to_load_protocol_from_file( + FileWorker* file_worker, + SubGhzProtocolNiceFlo* instance, + const char* file_path); /** Loading protocol from bin data * diff --git a/lib/subghz/protocols/subghz_protocol_princeton.c b/lib/subghz/protocols/subghz_protocol_princeton.c index 822a5917..81b65592 100644 --- a/lib/subghz/protocols/subghz_protocol_princeton.c +++ b/lib/subghz/protocols/subghz_protocol_princeton.c @@ -308,7 +308,8 @@ void subghz_decoder_princeton_to_save_str(SubGhzDecoderPrinceton* instance, stri bool subghz_decoder_princeton_to_load_protocol_from_file( FileWorker* file_worker, - SubGhzDecoderPrinceton* instance) { + SubGhzDecoderPrinceton* instance, + const char* file_path) { bool loaded = false; string_t temp_str; string_init(temp_str); diff --git a/lib/subghz/protocols/subghz_protocol_princeton.h b/lib/subghz/protocols/subghz_protocol_princeton.h index 9ccb7cd3..b8c448d2 100644 --- a/lib/subghz/protocols/subghz_protocol_princeton.h +++ b/lib/subghz/protocols/subghz_protocol_princeton.h @@ -111,15 +111,17 @@ void subghz_decoder_princeton_to_save_str(SubGhzDecoderPrinceton* instance, stri * * @param file_worker - FileWorker file_worker * @param instance - SubGhzDecoderPrinceton instance + * @param file_path - file path * @return bool */ -bool subghz_decoder_princeton_to_load_protocol_from_file(FileWorker* file_worker, SubGhzDecoderPrinceton* instance); +bool subghz_decoder_princeton_to_load_protocol_from_file( + FileWorker* file_worker, + SubGhzDecoderPrinceton* instance, + const char* file_path); /** Loading protocol from bin data * * @param instance - SubGhzDecoderPrinceton instance * @param context - SubGhzProtocolCommonLoad context */ -void subghz_decoder_princeton_to_load_protocol( - SubGhzDecoderPrinceton* instance, - void* context) ; +void subghz_decoder_princeton_to_load_protocol(SubGhzDecoderPrinceton* instance, void* context); diff --git a/lib/subghz/protocols/subghz_protocol_raw.c b/lib/subghz/protocols/subghz_protocol_raw.c new file mode 100644 index 00000000..7c711e8a --- /dev/null +++ b/lib/subghz/protocols/subghz_protocol_raw.c @@ -0,0 +1,280 @@ +#include "subghz_protocol_raw.h" +#include "file-worker.h" +#include "../subghz_file_encoder_worker.h" + +#define SUBGHZ_DOWNLOAD_MAX_SIZE 512 + +struct SubGhzProtocolRAW { + SubGhzProtocolCommon common; + + int16_t* upload_raw; + uint16_t ind_write; + FileWorker* file_worker; + SubGhzFileEncoderWorker* file_worker_encoder; + uint32_t file_is_open; + string_t file_name; + size_t sample_write; + bool last_level; +}; + +typedef enum { + RAWFileIsOpenClose = 0, + RAWFileIsOpenWrite, + RAWFileIsOpenRead, +} RAWFilIsOpen; + +SubGhzProtocolRAW* subghz_protocol_raw_alloc(void) { + SubGhzProtocolRAW* instance = furi_alloc(sizeof(SubGhzProtocolRAW)); + + instance->upload_raw = NULL; + instance->ind_write = 0; + + instance->last_level = false; + + instance->file_worker = file_worker_alloc(false); + instance->file_is_open = RAWFileIsOpenClose; + string_init(instance->file_name); + + instance->common.name = "RAW"; + instance->common.code_min_count_bit_for_found = 0; + instance->common.te_short = 80; + instance->common.te_long = 32700; + instance->common.te_delta = 0; + instance->common.type_protocol = SubGhzProtocolCommonTypeRAW; + instance->common.to_load_protocol_from_file = + (SubGhzProtocolCommonLoadFromFile)subghz_protocol_raw_to_load_protocol_from_file; + instance->common.to_string = (SubGhzProtocolCommonToStr)subghz_protocol_raw_to_str; + //instance->common.to_load_protocol = + // (SubGhzProtocolCommonLoadFromRAW)subghz_decoder_raw_to_load_protocol; + instance->common.get_upload_protocol = + (SubGhzProtocolCommonEncoderGetUpLoad)subghz_protocol_raw_send_key; + + return instance; +} + +void subghz_protocol_raw_free(SubGhzProtocolRAW* instance) { + furi_assert(instance); + string_clear(instance->file_name); + file_worker_free(instance->file_worker); + free(instance); +} + +void subghz_protocol_raw_file_encoder_worker_stop(void* context) { + furi_assert(context); + SubGhzProtocolRAW* instance = context; + if(subghz_file_encoder_worker_is_running(instance->file_worker_encoder)) { + subghz_file_encoder_worker_stop(instance->file_worker_encoder); + subghz_file_encoder_worker_free(instance->file_worker_encoder); + instance->file_is_open = RAWFileIsOpenClose; + } +} + +bool subghz_protocol_raw_send_key( + SubGhzProtocolRAW* instance, + SubGhzProtocolCommonEncoder* encoder) { + furi_assert(instance); + furi_assert(encoder); + + bool loaded = false; + + instance->file_worker_encoder = subghz_file_encoder_worker_alloc(); + + if(subghz_file_encoder_worker_start( + instance->file_worker_encoder, string_get_cstr(instance->file_name))) { + //the worker needs a file in order to open and read part of the file + osDelay(100); + instance->file_is_open = RAWFileIsOpenRead; + subghz_protocol_encoder_common_set_callback( + encoder, subghz_file_encoder_worker_get_level_duration, instance->file_worker_encoder); + subghz_protocol_encoder_common_set_callback_end( + encoder, subghz_protocol_raw_file_encoder_worker_stop, instance); + + loaded = true; + } else { + subghz_protocol_raw_file_encoder_worker_stop(instance); + } + return loaded; +} + +void subghz_protocol_raw_reset(SubGhzProtocolRAW* instance) { + instance->ind_write = 0; +} + +void subghz_protocol_raw_parse(SubGhzProtocolRAW* instance, bool level, uint32_t duration) { + if(instance->upload_raw != NULL) { + if(duration > instance->common.te_short) { + if(duration > instance->common.te_long) duration = instance->common.te_long; + if(instance->last_level != level) { + instance->last_level = (level ? true : false); + instance->upload_raw[instance->ind_write++] = (level ? duration : -duration); + } + } + + if(instance->ind_write == SUBGHZ_DOWNLOAD_MAX_SIZE) { + subghz_protocol_raw_save_to_file_write(instance); + } + } +} + +void subghz_protocol_raw_to_str(SubGhzProtocolRAW* instance, string_t output) { + string_cat_printf(output, "RAW Date"); +} + +const char* subghz_protocol_get_last_file_name(SubGhzProtocolRAW* instance) { + return string_get_cstr(instance->file_name); +} + +void subghz_protocol_set_last_file_name(SubGhzProtocolRAW* instance, const char* name) { + string_printf(instance->file_name, "%s", name); +} + +bool subghz_protocol_raw_save_to_file_init( + SubGhzProtocolRAW* instance, + const char* dev_name, + uint32_t frequency, + FuriHalSubGhzPreset preset) { + furi_assert(instance); + + string_t dev_file_name; + string_init(dev_file_name); + string_t temp_str; + string_init(temp_str); + bool init = false; + + do { + // Create subghz folder directory if necessary + if(!file_worker_mkdir(instance->file_worker, SUBGHZ_RAW_FOLDER)) { + break; + } + // Create saved directory if necessary + if(!file_worker_mkdir(instance->file_worker, SUBGHZ_RAW_PATH_FOLDER)) { + break; + } + + string_set(instance->file_name, dev_name); + // First remove subghz device file if it was saved + string_printf( + dev_file_name, "%s/%s%s", SUBGHZ_APP_PATH_FOLDER, dev_name, SUBGHZ_APP_EXTENSION); + if(!file_worker_remove(instance->file_worker, string_get_cstr(dev_file_name))) { + break; + } + // Open file + if(!file_worker_open( + instance->file_worker, string_get_cstr(dev_file_name), FSAM_WRITE, FSOM_CREATE_ALWAYS)) { + break; + } + + // //get the name of the next free file + // file_worker_get_next_filename( + // instance->file_worker, + // SUBGHZ_RAW_PATH_FOLDER, + // dev_name, + // SUBGHZ_APP_EXTENSION, + // temp_str); + + // string_set(instance->file_name, temp_str); + + // string_printf( + // dev_file_name, + // "%s/%s%s", + // SUBGHZ_RAW_PATH_FOLDER, + // string_get_cstr(temp_str), + // SUBGHZ_APP_EXTENSION); + // // Open file + // if(!file_worker_open( + // instance->file_worker, + // string_get_cstr(dev_file_name), + // FSAM_WRITE, + // FSOM_CREATE_ALWAYS)) { + // break; + // } + + //Get string frequency preset protocol + string_printf( + temp_str, + "Frequency: %d\n" + "Preset: %d\n" + "Protocol: RAW\n", + (int)frequency, + (int)preset); + + if(!file_worker_write( + instance->file_worker, string_get_cstr(temp_str), string_size(temp_str))) { + break; + } + + instance->upload_raw = furi_alloc(SUBGHZ_DOWNLOAD_MAX_SIZE * sizeof(uint16_t)); + instance->file_is_open = RAWFileIsOpenWrite; + instance->sample_write = 0; + init = true; + } while(0); + + string_clear(temp_str); + string_clear(dev_file_name); + + return init; +} + +void subghz_protocol_raw_save_to_file_stop(SubGhzProtocolRAW* instance) { + furi_assert(instance); + + if(instance->file_is_open == RAWFileIsOpenWrite && instance->ind_write) + subghz_protocol_raw_save_to_file_write(instance); + if(instance->file_is_open != RAWFileIsOpenClose) { + free(instance->upload_raw); + instance->upload_raw = NULL; + } + + file_worker_close(instance->file_worker); + instance->file_is_open = RAWFileIsOpenClose; +} + +bool subghz_protocol_raw_save_to_file_write(SubGhzProtocolRAW* instance) { + furi_assert(instance); + + string_t temp_str; + string_init(temp_str); + bool is_write = false; + if(instance->file_is_open == RAWFileIsOpenWrite) { + do { + string_printf(temp_str, "RAW_Data: "); + + if(!file_worker_write( + instance->file_worker, string_get_cstr(temp_str), string_size(temp_str))) { + break; + } + + for(size_t i = 0; i < instance->ind_write - 1; i++) { + string_printf(temp_str, "%d, ", instance->upload_raw[i]); + if(!file_worker_write( + instance->file_worker, string_get_cstr(temp_str), string_size(temp_str))) { + break; + } + } + + string_printf(temp_str, "%d\n", instance->upload_raw[instance->ind_write - 1]); + if(!file_worker_write( + instance->file_worker, string_get_cstr(temp_str), string_size(temp_str))) { + break; + } + + instance->sample_write += instance->ind_write; + instance->ind_write = 0; + is_write = true; + } while(0); + string_clear(temp_str); + } + return is_write; +} + +size_t subghz_protocol_raw_get_sample_write(SubGhzProtocolRAW* instance) { + return instance->sample_write + instance->ind_write; +} + +bool subghz_protocol_raw_to_load_protocol_from_file( + FileWorker* file_worker, + SubGhzProtocolRAW* instance, + const char* file_path) { + subghz_protocol_set_last_file_name(instance, file_path); + return true; +} \ No newline at end of file diff --git a/lib/subghz/protocols/subghz_protocol_raw.h b/lib/subghz/protocols/subghz_protocol_raw.h new file mode 100644 index 00000000..00c84314 --- /dev/null +++ b/lib/subghz/protocols/subghz_protocol_raw.h @@ -0,0 +1,64 @@ +#pragma once + +#include "subghz_protocol_common.h" + +typedef struct SubGhzProtocolRAW SubGhzProtocolRAW; + +/** Allocate SubGhzProtocolRAW + * + * @return SubGhzProtocolRAW* + */ +SubGhzProtocolRAW* subghz_protocol_raw_alloc(); + +/** Free SubGhzProtocolRAW + * + * @param instance + */ +void subghz_protocol_raw_free(SubGhzProtocolRAW* instance); + +/** Reset internal state + * @param instance - SubGhzProtocolRAW instance + */ +void subghz_protocol_raw_reset(SubGhzProtocolRAW* instance); + +/** Get upload protocol + * + * @param instance - SubGhzProtocolRAW instance + * @param encoder - SubGhzProtocolCommonEncoder encoder + * @return bool + */ +bool subghz_protocol_raw_send_key( + SubGhzProtocolRAW* instance, + SubGhzProtocolCommonEncoder* encoder); + +/** Parse accepted duration + * + * @param instance - SubGhzProtocolRAW instance + * @param data - LevelDuration level_duration + */ +void subghz_protocol_raw_parse(SubGhzProtocolRAW* instance, bool level, uint32_t duration); + +/** Outputting information from the parser + * + * @param instance - SubGhzProtocolRAW* instance + * @param output - output string + */ +void subghz_protocol_raw_to_str(SubGhzProtocolRAW* instance, string_t output); + +const char* subghz_protocol_get_last_file_name(SubGhzProtocolRAW* instance); + +void subghz_protocol_set_last_file_name(SubGhzProtocolRAW* instance, const char* name); + +bool subghz_protocol_raw_save_to_file_init( + SubGhzProtocolRAW* instance, + const char* dev_name, + uint32_t frequency, + FuriHalSubGhzPreset preset); +void subghz_protocol_raw_save_to_file_stop(SubGhzProtocolRAW* instance); +bool subghz_protocol_raw_save_to_file_write(SubGhzProtocolRAW* instance); +size_t subghz_protocol_raw_get_sample_write(SubGhzProtocolRAW* instance); + +bool subghz_protocol_raw_to_load_protocol_from_file( + FileWorker* file_worker, + SubGhzProtocolRAW* instance, + const char* file_path); \ No newline at end of file diff --git a/lib/subghz/subghz_file_encoder_worker.c b/lib/subghz/subghz_file_encoder_worker.c new file mode 100644 index 00000000..0e502fdf --- /dev/null +++ b/lib/subghz/subghz_file_encoder_worker.c @@ -0,0 +1,210 @@ +#include "subghz_file_encoder_worker.h" +#include + +#include "file-worker.h" + +#define SUBGHZ_FILE_ENCODER_LOAD 512 + +struct SubGhzFileEncoderWorker { + FuriThread* thread; + StreamBufferHandle_t stream; + FileWorker* file_worker; + + volatile bool worker_running; + bool level; + int16_t duration; + string_t str_data; + string_t file_path; +}; + +void subghz_file_encoder_worker_add_livel_duration( + SubGhzFileEncoderWorker* instance, + int16_t duration) { + bool res = true; + if(duration < 0 && !instance->level) { + instance->duration += duration; + res = false; + } else if(duration > 0 && instance->level) { + instance->duration += duration; + res = false; + } else if(duration == 0) { + instance->duration = 0; + } + + if(res) { + instance->level = !instance->level; + instance->duration += duration; + xStreamBufferSend(instance->stream, &instance->duration, sizeof(int16_t), 10); + instance->duration = 0; + } +} + +bool subghz_file_encoder_worker_data_parse( + SubGhzFileEncoderWorker* instance, + const char* strStart, + size_t len) { + char* str1; + size_t ind_start = (size_t)strStart; //store the start address of the beginning of the line + bool res = false; + + str1 = strstr( + strStart, "RAW_Data: "); //looking for the beginning of the desired title in the line + if(str1 != NULL) { + str1 = strchr( + str1, + ' '); //if found, shift the pointer by 1 element per line "RAW_Data: -1, 2, -2..." + subghz_file_encoder_worker_add_livel_duration(instance, atoi(str1)); + while( + strchr(str1, ',') != NULL && + ((size_t)str1 < + (len + + ind_start))) { //check that there is still an element in the line and that it has not gone beyond the line + str1 = strchr(str1, ','); + str1 += 2; //if found, shift the pointer by next element per line + subghz_file_encoder_worker_add_livel_duration(instance, atoi(str1)); + } + res = true; + } + return res; +} + +LevelDuration subghz_file_encoder_worker_get_level_duration(void* context) { + furi_assert(context); + SubGhzFileEncoderWorker* instance = context; + int16_t duration; + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + int ret = xStreamBufferReceiveFromISR( + instance->stream, &duration, sizeof(int16_t), &xHigherPriorityTaskWoken); + portYIELD_FROM_ISR(xHigherPriorityTaskWoken); + if(ret == sizeof(int16_t)) { + LevelDuration level_duration = {.level = LEVEL_DURATION_RESET}; + if(duration < 0) { + level_duration = level_duration_make(false, duration * -1); + } else if(duration > 0) { + level_duration = level_duration_make(true, duration); + } else if(duration == 0) { + level_duration = level_duration_reset(); + FURI_LOG_I("SubGhzFileEncoderWorker", "Stop transmission"); + } + return level_duration; + } else { + FURI_LOG_E("SubGhzFileEncoderWorker", "Slow flash read"); + return level_duration_wait(); + } +} + +/** Worker thread + * + * @param context + * @return exit code + */ +static int32_t subghz_file_encoder_worker_thread(void* context) { + SubGhzFileEncoderWorker* instance = context; + FURI_LOG_I("SubGhzFileEncoderWorker", "Worker start"); + bool res = false; + do { + if(!file_worker_open( + instance->file_worker, + string_get_cstr(instance->file_path), + FSAM_READ, + FSOM_OPEN_EXISTING)) { + break; + } + //todo skips 3 lines file header + if(!file_worker_read_until(instance->file_worker, instance->str_data, '\n')) { + break; + } + if(!file_worker_read_until(instance->file_worker, instance->str_data, '\n')) { + break; + } + if(!file_worker_read_until(instance->file_worker, instance->str_data, '\n')) { + break; + } + res = true; + FURI_LOG_I("SubGhzFileEncoderWorker", "Start transmission"); + } while(0); + + while(res && instance->worker_running) { + size_t stream_free_byte = xStreamBufferSpacesAvailable(instance->stream); + if((stream_free_byte / sizeof(int16_t)) >= SUBGHZ_FILE_ENCODER_LOAD) { + if(file_worker_read_until(instance->file_worker, instance->str_data, '\n')) { + if(!subghz_file_encoder_worker_data_parse( + instance, + string_get_cstr(instance->str_data), + strlen(string_get_cstr(instance->str_data)))) { + //to stop DMA correctly + subghz_file_encoder_worker_add_livel_duration(instance, LEVEL_DURATION_RESET); + subghz_file_encoder_worker_add_livel_duration(instance, LEVEL_DURATION_RESET); + + break; + } + } else { + subghz_file_encoder_worker_add_livel_duration(instance, LEVEL_DURATION_RESET); + subghz_file_encoder_worker_add_livel_duration(instance, LEVEL_DURATION_RESET); + break; + } + } + } + //waiting for the end of the transfer + while(instance->worker_running) { + osDelay(50); + } + file_worker_close(instance->file_worker); + FURI_LOG_I("SubGhzFileEncoderWorker", "Worker stop"); + return 0; +} + +SubGhzFileEncoderWorker* subghz_file_encoder_worker_alloc() { + SubGhzFileEncoderWorker* instance = furi_alloc(sizeof(SubGhzFileEncoderWorker)); + + instance->thread = furi_thread_alloc(); + furi_thread_set_name(instance->thread, "subghz_file_encoder_worker"); + furi_thread_set_stack_size(instance->thread, 2048); + furi_thread_set_context(instance->thread, instance); + furi_thread_set_callback(instance->thread, subghz_file_encoder_worker_thread); + instance->stream = xStreamBufferCreate(sizeof(int16_t) * 4096, sizeof(int16_t)); + + instance->file_worker = file_worker_alloc(false); + string_init(instance->str_data); + string_init(instance->file_path); + instance->level = false; + + return instance; +} + +void subghz_file_encoder_worker_free(SubGhzFileEncoderWorker* instance) { + furi_assert(instance); + + vStreamBufferDelete(instance->stream); + furi_thread_free(instance->thread); + + string_clear(instance->str_data); + string_clear(instance->file_path); + file_worker_free(instance->file_worker); + + free(instance); +} + +bool subghz_file_encoder_worker_start(SubGhzFileEncoderWorker* instance, const char* file_path) { + furi_assert(instance); + furi_assert(!instance->worker_running); + + xStreamBufferReset(instance->stream); + string_set(instance->file_path, file_path); + instance->worker_running = true; + bool res = furi_thread_start(instance->thread); + return res; +} + +void subghz_file_encoder_worker_stop(SubGhzFileEncoderWorker* instance) { + furi_assert(instance); + furi_assert(instance->worker_running); + + instance->worker_running = false; + furi_thread_join(instance->thread); +} + +bool subghz_file_encoder_worker_is_running(SubGhzFileEncoderWorker* instance) { + furi_assert(instance); + return instance->worker_running; +} diff --git a/lib/subghz/subghz_file_encoder_worker.h b/lib/subghz/subghz_file_encoder_worker.h new file mode 100644 index 00000000..0c014a0b --- /dev/null +++ b/lib/subghz/subghz_file_encoder_worker.h @@ -0,0 +1,39 @@ +#pragma once + +#include + +typedef struct SubGhzFileEncoderWorker SubGhzFileEncoderWorker; + +/** Allocate SubGhzFileEncoderWorker + * + * @return SubGhzFileEncoderWorker* + */ +SubGhzFileEncoderWorker* subghz_file_encoder_worker_alloc(); + +/** Free SubGhzFileEncoderWorker + * + * @param instance SubGhzFileEncoderWorker instance + */ +void subghz_file_encoder_worker_free(SubGhzFileEncoderWorker* instance); + +LevelDuration subghz_file_encoder_worker_get_level_duration(void* context); + +/** Start SubGhzFileEncoderWorker + * + * @param instance SubGhzFileEncoderWorker instance + * @return bool - true if ok + */ +bool subghz_file_encoder_worker_start(SubGhzFileEncoderWorker* instance, const char* file_path); + +/** Stop SubGhzFileEncoderWorker + * + * @param instance SubGhzFileEncoderWorker instance + */ +void subghz_file_encoder_worker_stop(SubGhzFileEncoderWorker* instance); + +/** Check if worker is running + * + * @param instance SubGhzFileEncoderWorker instance + * @return bool - true if running + */ +bool subghz_file_encoder_worker_is_running(SubGhzFileEncoderWorker* instance); diff --git a/lib/subghz/subghz_parser.c b/lib/subghz/subghz_parser.c index faf0b6dd..f702dda9 100644 --- a/lib/subghz/subghz_parser.c +++ b/lib/subghz/subghz_parser.c @@ -16,6 +16,8 @@ #include "protocols/subghz_protocol_nero_radio.h" #include "protocols/subghz_protocol_scher_khan.h" #include "protocols/subghz_protocol_kia.h" +#include "protocols/subghz_protocol_raw.h" +#include "protocols/subghz_protocol_hormann.h" #include "subghz_keystore.h" @@ -38,6 +40,8 @@ typedef enum { SubGhzProtocolTypeNeroRadio, SubGhzProtocolTypeScherKhan, SubGhzProtocolTypeKIA, + SubGhzProtocolTypeRAW, + SubGhzProtocolTypeHormann, SubGhzProtocolTypeMax, } SubGhzProtocolType; @@ -109,6 +113,10 @@ SubGhzParser* subghz_parser_alloc() { (SubGhzProtocolCommon*)subghz_protocol_scher_khan_alloc(); instance->protocols[SubGhzProtocolTypeKIA] = (SubGhzProtocolCommon*)subghz_protocol_kia_alloc(); + instance->protocols[SubGhzProtocolTypeRAW] = + (SubGhzProtocolCommon*)subghz_protocol_raw_alloc(); + instance->protocols[SubGhzProtocolTypeHormann] = + (SubGhzProtocolCommon*)subghz_protocol_hormann_alloc(); return instance; } @@ -143,6 +151,9 @@ void subghz_parser_free(SubGhzParser* instance) { subghz_protocol_scher_khan_free( (SubGhzProtocolScherKhan*)instance->protocols[SubGhzProtocolTypeScherKhan]); subghz_protocol_kia_free((SubGhzProtocolKIA*)instance->protocols[SubGhzProtocolTypeKIA]); + subghz_protocol_raw_free((SubGhzProtocolRAW*)instance->protocols[SubGhzProtocolTypeRAW]); + subghz_protocol_hormann_free( + (SubGhzProtocolHormann*)instance->protocols[SubGhzProtocolTypeHormann]); subghz_keystore_free(instance->keystore); @@ -197,6 +208,11 @@ void subghz_parser_load_nice_flor_s_file(SubGhzParser* instance, const char* fil (SubGhzProtocolNiceFlorS*)instance->protocols[SubGhzProtocolTypeNiceFlorS], file_name); } +void subghz_parser_load_came_atomo_file(SubGhzParser* instance, const char* file_name) { + subghz_protocol_came_atomo_name_file( + (SubGhzProtocolCameAtomo*)instance->protocols[SubGhzProtocolTypeCameAtomo], file_name); +} + void subghz_parser_load_keeloq_file(SubGhzParser* instance, const char* file_name) { subghz_keystore_load(instance->keystore, file_name); } @@ -229,6 +245,14 @@ void subghz_parser_reset(SubGhzParser* instance) { subghz_protocol_scher_khan_reset( (SubGhzProtocolScherKhan*)instance->protocols[SubGhzProtocolTypeScherKhan]); subghz_protocol_kia_reset((SubGhzProtocolKIA*)instance->protocols[SubGhzProtocolTypeKIA]); + subghz_protocol_raw_reset((SubGhzProtocolRAW*)instance->protocols[SubGhzProtocolTypeRAW]); + subghz_protocol_hormann_reset( + (SubGhzProtocolHormann*)instance->protocols[SubGhzProtocolTypeHormann]); +} + +void subghz_parser_raw_parse(SubGhzParser* instance, bool level, uint32_t duration) { + subghz_protocol_raw_parse( + (SubGhzProtocolRAW*)instance->protocols[SubGhzProtocolTypeRAW], level, duration); } void subghz_parser_parse(SubGhzParser* instance, bool level, uint32_t duration) { @@ -274,4 +298,6 @@ void subghz_parser_parse(SubGhzParser* instance, bool level, uint32_t duration) duration); subghz_protocol_kia_parse( (SubGhzProtocolKIA*)instance->protocols[SubGhzProtocolTypeKIA], level, duration); + subghz_protocol_hormann_parse( + (SubGhzProtocolHormann*)instance->protocols[SubGhzProtocolTypeHormann], level, duration); } diff --git a/lib/subghz/subghz_parser.h b/lib/subghz/subghz_parser.h index 69341c18..7e6c72b8 100644 --- a/lib/subghz/subghz_parser.h +++ b/lib/subghz/subghz_parser.h @@ -3,7 +3,7 @@ #include "protocols/subghz_protocol_common.h" typedef void (*SubGhzProtocolTextCallback)(string_t text, void* context); -typedef void (*SubGhzProtocolCommonCallbackDump)(SubGhzProtocolCommon *parser, void* context); +typedef void (*SubGhzProtocolCommonCallbackDump)(SubGhzProtocolCommon* parser, void* context); typedef struct SubGhzParser SubGhzParser; @@ -33,7 +33,10 @@ SubGhzProtocolCommon* subghz_parser_get_by_name(SubGhzParser* instance, const ch * @param callback - SubGhzProtocolTextCallback callback * @param context */ -void subghz_parser_enable_dump_text(SubGhzParser* instance, SubGhzProtocolTextCallback callback, void* context); +void subghz_parser_enable_dump_text( + SubGhzParser* instance, + SubGhzProtocolTextCallback callback, + void* context); /** Outputting data SubGhzParser from all parsers * @@ -41,7 +44,10 @@ void subghz_parser_enable_dump_text(SubGhzParser* instance, SubGhzProtocolTextCa * @param callback - SubGhzProtocolTextCallback callback * @param context */ -void subghz_parser_enable_dump(SubGhzParser* instance, SubGhzProtocolCommonCallbackDump callback, void* context); +void subghz_parser_enable_dump( + SubGhzParser* instance, + SubGhzProtocolCommonCallbackDump callback, + void* context); /** File name rainbow table Nice Flor-S * @@ -50,6 +56,13 @@ void subghz_parser_enable_dump(SubGhzParser* instance, SubGhzProtocolCommonCallb */ void subghz_parser_load_nice_flor_s_file(SubGhzParser* instance, const char* file_name); +/** File name rainbow table Came Atomo + * + * @param instance - SubGhzParser instance + * @param file_name - "path/file_name" + */ +void subghz_parser_load_came_atomo_file(SubGhzParser* instance, const char* file_name); + /** File upload manufacture keys * * @param instance - SubGhzParser instance @@ -63,6 +76,8 @@ void subghz_parser_load_keeloq_file(SubGhzParser* instance, const char* file_nam */ void subghz_parser_reset(SubGhzParser* instance); +void subghz_parser_raw_parse(SubGhzParser* instance, bool level, uint32_t duration); + /** Loading data into all parsers * * @param instance - SubGhzParser instance diff --git a/lib/toolbox/level_duration.h b/lib/toolbox/level_duration.h index 958c3890..bef0b6ec 100644 --- a/lib/toolbox/level_duration.h +++ b/lib/toolbox/level_duration.h @@ -9,6 +9,7 @@ #define LEVEL_DURATION_RESET 0U #define LEVEL_DURATION_LEVEL_LOW 1U #define LEVEL_DURATION_LEVEL_HIGH 2U +#define LEVEL_DURATION_WAIT 3U #define LEVEL_DURATION_RESERVED 0x800000U typedef struct { @@ -29,10 +30,20 @@ static inline LevelDuration level_duration_reset() { return level_duration; } +static inline LevelDuration level_duration_wait() { + LevelDuration level_duration; + level_duration.level = LEVEL_DURATION_WAIT; + return level_duration; +} + static inline bool level_duration_is_reset(LevelDuration level_duration) { return level_duration.level == LEVEL_DURATION_RESET; } +static inline bool level_duration_is_wait(LevelDuration level_duration) { + return level_duration.level == LEVEL_DURATION_WAIT; +} + static inline bool level_duration_get_level(LevelDuration level_duration) { return level_duration.level == LEVEL_DURATION_LEVEL_HIGH; } diff --git a/make/rules.mk b/make/rules.mk index 390b9ee0..9f1402b9 100644 --- a/make/rules.mk +++ b/make/rules.mk @@ -32,7 +32,7 @@ CHECK_AND_REINIT_SUBMODULES_SHELL=\ fi $(info $(shell $(CHECK_AND_REINIT_SUBMODULES_SHELL))) -all: $(OBJ_DIR)/$(PROJECT).elf $(OBJ_DIR)/$(PROJECT).hex $(OBJ_DIR)/$(PROJECT).bin $(OBJ_DIR)/$(PROJECT).dfu +all: $(OBJ_DIR)/$(PROJECT).elf $(OBJ_DIR)/$(PROJECT).hex $(OBJ_DIR)/$(PROJECT).bin $(OBJ_DIR)/$(PROJECT).dfu $(OBJ_DIR)/$(PROJECT).json $(OBJ_DIR)/$(PROJECT).elf: $(OBJECTS) @echo "\tLD\t" $@ @@ -54,6 +54,10 @@ $(OBJ_DIR)/$(PROJECT).dfu: $(OBJ_DIR)/$(PROJECT).hex -o $(OBJ_DIR)/$(PROJECT).dfu \ -l "Flipper Zero $(shell echo $(TARGET) | tr a-z A-Z)" > /dev/null +$(OBJ_DIR)/$(PROJECT).json: $(OBJ_DIR)/$(PROJECT).dfu + @echo "\tJSON\t" $@ + @python3 ../scripts/meta.py -p $(PROJECT) $(CFLAGS) > $(OBJ_DIR)/$(PROJECT).json + $(OBJ_DIR)/%.o: %.c $(OBJ_DIR)/BUILD_FLAGS @echo "\tCC\t" $< "->" $@ @$(CC) $(CFLAGS) -c $< -o $@ diff --git a/scripts/flash.py b/scripts/flash.py index 0e920076..be65b1dc 100755 --- a/scripts/flash.py +++ b/scripts/flash.py @@ -18,14 +18,14 @@ class Main(App): self.parser_wipe = self.subparsers.add_parser("wipe", help="Wipe MCU Flash") self.parser_wipe.set_defaults(func=self.wipe) # Core 1 boot - self.parser_core1boot = self.subparsers.add_parser( - "core1boot", help="Flash Core1 Bootloader" + self.parser_core1bootloader = self.subparsers.add_parser( + "core1bootloader", help="Flash Core1 Bootloader" ) - self._addArgsSWD(self.parser_core1boot) - self.parser_core1boot.add_argument( + self._addArgsSWD(self.parser_core1bootloader) + self.parser_core1bootloader.add_argument( "bootloader", type=str, help="Bootloader binary" ) - self.parser_core1boot.set_defaults(func=self.core1boot) + self.parser_core1bootloader.set_defaults(func=self.core1bootloader) # Core 1 firmware self.parser_core1firmware = self.subparsers.add_parser( "core1firmware", help="Flash Core1 Firmware" @@ -37,7 +37,7 @@ class Main(App): self.parser_core1firmware.set_defaults(func=self.core1firmware) # Core 1 all self.parser_core1 = self.subparsers.add_parser( - "core1", help="Flash Core1 Boot and Firmware" + "core1", help="Flash Core1 Bootloader and Firmware" ) self._addArgsSWD(self.parser_core1) self.parser_core1.add_argument("bootloader", type=str, help="Bootloader binary") @@ -97,7 +97,7 @@ class Main(App): self.logger.info(f"Complete") return 0 - def core1boot(self): + def core1bootloader(self): self.logger.info(f"Flashing bootloader") cp = CubeProgrammer(self.args.port) cp.flashBin("0x08000000", self.args.bootloader) diff --git a/scripts/flipper/copro.py b/scripts/flipper/copro.py index a7f3b95f..f5198e44 100644 --- a/scripts/flipper/copro.py +++ b/scripts/flipper/copro.py @@ -19,7 +19,7 @@ MANIFEST_TEMPLATE = { "minor": 12, "sub": 1, "branch": 0, - "release": 7, + "release": 1, }, "files": [], }, diff --git a/scripts/flipper/storage.py b/scripts/flipper/storage.py index 589c5768..73799cef 100644 --- a/scripts/flipper/storage.py +++ b/scripts/flipper/storage.py @@ -65,7 +65,7 @@ class FlipperStorage: self.port.reset_input_buffer() # Send a command with a known syntax to make sure the buffer is flushed self.send("device_info\r") - self.read.until("hardware_model :") + self.read.until("hardware_model") # And read buffer until we get prompt self.read.until(self.CLI_PROMPT) diff --git a/scripts/meta.py b/scripts/meta.py new file mode 100644 index 00000000..3cb8cb78 --- /dev/null +++ b/scripts/meta.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python3 + +import argparse +import json + + +class Main: + def __init__(self): + # parse CFLAGS + self.parser = argparse.ArgumentParser(allow_abbrev=False) + self.parser.add_argument("-p", dest="project", required=True) + self.parser.add_argument("-DBUILD_DATE", dest="build_date", required=True) + self.parser.add_argument("-DGIT_COMMIT", dest="commit", required=True) + self.parser.add_argument("-DGIT_BRANCH", dest="branch", required=True) + self.parser.add_argument("-DTARGET", dest="target", type=int, required=True) + + def __call__(self): + self.args, _ = self.parser.parse_known_args() + + meta = {} + for k, v in vars(self.args).items(): + if k == "project": + continue + if isinstance(v, str): + v = v.strip('"') + meta[self.args.project + "_" + k] = v + + print(json.dumps(meta, indent=4)) + + +if __name__ == "__main__": + Main()() diff --git a/scripts/otp.py b/scripts/otp.py index c16f98fe..edad4b07 100755 --- a/scripts/otp.py +++ b/scripts/otp.py @@ -170,9 +170,9 @@ class Main(App): os.remove(filename) except Exception as e: self.logger.exception(e) - return 0 + return 1 - return 1 + return 0 def flash_second(self): self.logger.info(f"Flashing second block of OTP")