diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index db09f754..1a0a91e6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -121,11 +121,11 @@ jobs: - name: 'Bundle core2 firmware' if: ${{ !github.event.pull_request.head.repo.fork }} - run: | - test -d core2_firmware && rm -rf core2_firmware || true - mkdir core2_firmware - ./scripts/assets.py copro lib/STM32CubeWB core2_firmware STM32WB5x - tar czpf artifacts/flipper-z-any-core2_firmware-${{steps.names.outputs.suffix}}.tgz core2_firmware + uses: ./.github/actions/docker + with: + run: | + make -C assets copro_bundle + tar czpf artifacts/flipper-z-any-core2_firmware-${{steps.names.outputs.suffix}}.tgz -C assets core2_firmware - name: 'Upload artifacts to update server' if: ${{ !github.event.pull_request.head.repo.fork }} @@ -161,7 +161,7 @@ jobs: comment-id: ${{ steps.fc.outputs.comment-id }} issue-number: ${{ github.event.pull_request.number }} body: | - [Click here](https://update.flipperzero.one/?url=https://update.flipperzero.one/builds/firmware/${{steps.names.outputs.artifacts-path}}/flipper-z-${{steps.names.outputs.default-target}}-full-${{steps.names.outputs.suffix}}.dfu&channel=${{steps.names.outputs.artifacts-path}}&version=${{steps.names.outputs.short-hash}}&target=${{steps.names.outputs.default-target}}) to flash the `${{steps.names.outputs.short-hash}}` version of this branch via WebUSB. + [Click here](https://update.flipperzero.one/builds/firmware/${{steps.names.outputs.artifacts-path}}/flipper-z-${{steps.names.outputs.default-target}}-full-${{steps.names.outputs.suffix}}.dfu) for the DFU file to flash the `${{steps.names.outputs.short-hash}}` version of this branch with the [`Install from file` option in qFlipper](https://docs.flipperzero.one/basics/firmware-update). edit-mode: replace compact: @@ -213,8 +213,8 @@ jobs: with: run: | set -e - make -C assets clean - make -C assets + make assets_rebuild assets_manifest + git diff --quiet || ( echo "Assets recompilation required."; exit 255 ) - name: 'Build the firmware in docker' uses: ./.github/actions/docker diff --git a/Makefile b/Makefile index ca3caa22..13692347 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,7 @@ PROJECT_ROOT := $(abspath $(dir $(abspath $(firstword $(MAKEFILE_LIST))))) include $(PROJECT_ROOT)/make/git.mk - -COPRO_DIR := $(PROJECT_ROOT)/lib/STM32CubeWB/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x +include $(PROJECT_ROOT)/assets/copro.mk PROJECT_SOURCE_DIRECTORIES := \ $(PROJECT_ROOT)/applications \ @@ -97,7 +96,13 @@ updater_package_bin: firmware_all updater .PHONY: updater_package updater_package: firmware_all updater assets_manifest - @$(PROJECT_ROOT)/scripts/dist.py copy -t $(TARGET) -p firmware updater -s $(DIST_SUFFIX) -r $(PROJECT_ROOT)/assets/resources --bundlever "$(VERSION_STRING)" + @$(PROJECT_ROOT)/scripts/dist.py copy \ + -t $(TARGET) -p firmware updater \ + -s $(DIST_SUFFIX) -r $(PROJECT_ROOT)/assets/resources \ + --bundlever "$(VERSION_STRING)" \ + --radio $(COPRO_STACK_BIN_PATH) \ + --radiotype $(COPRO_STACK_TYPE) \ + --obdata $(PROJECT_ROOT)/scripts/ob.data .PHONY: assets_manifest assets_manifest: @@ -109,7 +114,7 @@ assets_rebuild: .PHONY: flash_radio flash_radio: - @$(PROJECT_ROOT)/scripts/flash.py core2radio 0x080D7000 $(COPRO_DIR)/stm32wb5x_BLE_Stack_light_fw.bin + @$(PROJECT_ROOT)/scripts/flash.py core2radio $(COPRO_STACK_BIN_PATH) --addr=$(COPRO_STACK_ADDR) @$(PROJECT_ROOT)/scripts/ob.py set .PHONY: flash_radio_fus @@ -125,8 +130,8 @@ flash_radio_fus: .PHONY: flash_radio_fus_please_i_m_not_going_to_complain flash_radio_fus_please_i_m_not_going_to_complain: - @$(PROJECT_ROOT)/scripts/flash.py core2fus 0x080EC000 --statement=AGREE_TO_LOSE_FLIPPER_FEATURES_THAT_USE_CRYPTO_ENCLAVE $(COPRO_DIR)/stm32wb5x_FUS_fw_for_fus_0_5_3.bin - @$(PROJECT_ROOT)/scripts/flash.py core2fus 0x080EC000 --statement=AGREE_TO_LOSE_FLIPPER_FEATURES_THAT_USE_CRYPTO_ENCLAVE $(COPRO_DIR)/stm32wb5x_FUS_fw.bin + @$(PROJECT_ROOT)/scripts/flash.py core2fus 0x080EC000 --statement=AGREE_TO_LOSE_FLIPPER_FEATURES_THAT_USE_CRYPTO_ENCLAVE $(COPRO_FIRMWARE_DIR)/stm32wb5x_FUS_fw_for_fus_0_5_3.bin + @$(PROJECT_ROOT)/scripts/flash.py core2fus 0x080EC000 --statement=AGREE_TO_LOSE_FLIPPER_FEATURES_THAT_USE_CRYPTO_ENCLAVE $(COPRO_FIRMWARE_DIR)/stm32wb5x_FUS_fw.bin @$(PROJECT_ROOT)/scripts/ob.py set .PHONY: lint diff --git a/applications/accessor/accessor_app.cpp b/applications/accessor/accessor_app.cpp index 21b2e258..f021a816 100644 --- a/applications/accessor/accessor_app.cpp +++ b/applications/accessor/accessor_app.cpp @@ -32,7 +32,6 @@ void AccessorApp::run(void) { } AccessorApp::AccessorApp() { - furi_hal_power_insomnia_enter(); notification = static_cast(furi_record_open("notification")); onewire_host = onewire_host_alloc(); furi_hal_power_enable_otg(); @@ -42,7 +41,6 @@ AccessorApp::~AccessorApp() { furi_hal_power_disable_otg(); furi_record_close("notification"); onewire_host_free(onewire_host); - furi_hal_power_insomnia_exit(); } AccessorAppViewManager* AccessorApp::get_view_manager() { @@ -139,4 +137,4 @@ WIEGAND* AccessorApp::get_wiegand() { OneWireHost* AccessorApp::get_one_wire() { return onewire_host; -} \ No newline at end of file +} diff --git a/applications/applications.c b/applications/applications.c index 6b4c6321..b9e8bbd4 100644 --- a/applications/applications.c +++ b/applications/applications.c @@ -74,61 +74,113 @@ extern int32_t system_settings_app(void* p); const FlipperApplication FLIPPER_SERVICES[] = { /* Services */ #ifdef SRV_RPC - {.app = rpc_srv, .name = "RpcSrv", .stack_size = 1024 * 4, .icon = NULL}, + {.app = rpc_srv, + .name = "RpcSrv", + .stack_size = 1024 * 4, + .icon = NULL, + .flags = FlipperApplicationFlagDefault}, #endif #ifdef SRV_BT - {.app = bt_srv, .name = "BtSrv", .stack_size = 1024, .icon = NULL}, + {.app = bt_srv, + .name = "BtSrv", + .stack_size = 1024, + .icon = NULL, + .flags = FlipperApplicationFlagDefault}, #endif #ifdef SRV_CLI - {.app = cli_srv, .name = "CliSrv", .stack_size = 4096, .icon = NULL}, + {.app = cli_srv, + .name = "CliSrv", + .stack_size = 4096, + .icon = NULL, + .flags = FlipperApplicationFlagDefault}, #endif #ifdef SRV_DIALOGS - {.app = dialogs_srv, .name = "DialogsSrv", .stack_size = 1024, .icon = NULL}, + {.app = dialogs_srv, + .name = "DialogsSrv", + .stack_size = 1024, + .icon = NULL, + .flags = FlipperApplicationFlagDefault}, #endif #ifdef SRV_DOLPHIN - {.app = dolphin_srv, .name = "DolphinSrv", .stack_size = 1024, .icon = NULL}, + {.app = dolphin_srv, + .name = "DolphinSrv", + .stack_size = 1024, + .icon = NULL, + .flags = FlipperApplicationFlagDefault}, #endif #ifdef SRV_DESKTOP #ifdef SRV_UPDATER #error SRV_UPDATER and SRV_DESKTOP are mutually exclusive! #endif - {.app = desktop_srv, .name = "DesktopSrv", .stack_size = 2048, .icon = NULL}, + {.app = desktop_srv, + .name = "DesktopSrv", + .stack_size = 2048, + .icon = NULL, + .flags = FlipperApplicationFlagDefault}, #endif #ifdef SRV_GUI - {.app = gui_srv, .name = "GuiSrv", .stack_size = 2048, .icon = NULL}, + {.app = gui_srv, + .name = "GuiSrv", + .stack_size = 2048, + .icon = NULL, + .flags = FlipperApplicationFlagDefault}, #endif #ifdef SRV_INPUT - {.app = input_srv, .name = "InputSrv", .stack_size = 1024, .icon = NULL}, + {.app = input_srv, + .name = "InputSrv", + .stack_size = 1024, + .icon = NULL, + .flags = FlipperApplicationFlagDefault}, #endif #ifdef SRV_LOADER - {.app = loader_srv, .name = "LoaderSrv", .stack_size = 1024, .icon = NULL}, + {.app = loader_srv, + .name = "LoaderSrv", + .stack_size = 1024, + .icon = NULL, + .flags = FlipperApplicationFlagDefault}, #endif #ifdef SRV_NOTIFICATION - {.app = notification_srv, .name = "NotificationSrv", .stack_size = 1536, .icon = NULL}, + {.app = notification_srv, + .name = "NotificationSrv", + .stack_size = 1536, + .icon = NULL, + .flags = FlipperApplicationFlagDefault}, #endif #ifdef SRV_POWER - {.app = power_srv, .name = "PowerSrv", .stack_size = 1024, .icon = NULL}, + {.app = power_srv, + .name = "PowerSrv", + .stack_size = 1024, + .icon = NULL, + .flags = FlipperApplicationFlagDefault}, #endif #ifdef SRV_STORAGE - {.app = storage_srv, .name = "StorageSrv", .stack_size = 3072, .icon = NULL}, + {.app = storage_srv, + .name = "StorageSrv", + .stack_size = 3072, + .icon = NULL, + .flags = FlipperApplicationFlagDefault}, #endif #ifdef SRV_UPDATER #ifdef SRV_DESKTOP #error SRV_UPDATER and SRV_DESKTOP are mutually exclusive! #endif - {.app = updater_srv, .name = "UpdaterSrv", .stack_size = 2048, .icon = NULL}, + {.app = updater_srv, + .name = "UpdaterSrv", + .stack_size = 2048, + .icon = NULL, + .flags = FlipperApplicationFlagDefault}, #endif }; @@ -139,7 +191,11 @@ const FlipperApplication FLIPPER_SYSTEM_APPS[] = { #ifdef SRV_UPDATER #error APP_UPDATER and SRV_UPDATER are mutually exclusive! #endif - {.app = updater_srv, .name = "UpdaterApp", .stack_size = 2048, .icon = NULL}, + {.app = updater_srv, + .name = "UpdaterApp", + .stack_size = 2048, + .icon = NULL, + .flags = FlipperApplicationFlagDefault}, #endif }; @@ -149,35 +205,67 @@ const size_t FLIPPER_SYSTEM_APPS_COUNT = COUNT_OF(FLIPPER_SYSTEM_APPS); const FlipperApplication FLIPPER_APPS[] = { #ifdef APP_SUBGHZ - {.app = subghz_app, .name = "Sub-GHz", .stack_size = 2048, .icon = &A_Sub1ghz_14}, + {.app = subghz_app, + .name = "Sub-GHz", + .stack_size = 2048, + .icon = &A_Sub1ghz_14, + .flags = FlipperApplicationFlagDefault}, #endif #ifdef APP_LF_RFID - {.app = lfrfid_app, .name = "125 kHz RFID", .stack_size = 2048, .icon = &A_125khz_14}, + {.app = lfrfid_app, + .name = "125 kHz RFID", + .stack_size = 2048, + .icon = &A_125khz_14, + .flags = FlipperApplicationFlagDefault}, #endif #ifdef APP_NFC - {.app = nfc_app, .name = "NFC", .stack_size = 4096, .icon = &A_NFC_14}, + {.app = nfc_app, + .name = "NFC", + .stack_size = 4096, + .icon = &A_NFC_14, + .flags = FlipperApplicationFlagDefault}, #endif #ifdef APP_INFRARED - {.app = infrared_app, .name = "Infrared", .stack_size = 1024 * 3, .icon = &A_Infrared_14}, + {.app = infrared_app, + .name = "Infrared", + .stack_size = 1024 * 3, + .icon = &A_Infrared_14, + .flags = FlipperApplicationFlagDefault}, #endif #ifdef APP_GPIO - {.app = gpio_app, .name = "GPIO", .stack_size = 1024, .icon = &A_GPIO_14}, + {.app = gpio_app, + .name = "GPIO", + .stack_size = 1024, + .icon = &A_GPIO_14, + .flags = FlipperApplicationFlagDefault}, #endif #ifdef APP_IBUTTON - {.app = ibutton_app, .name = "iButton", .stack_size = 2048, .icon = &A_iButton_14}, + {.app = ibutton_app, + .name = "iButton", + .stack_size = 2048, + .icon = &A_iButton_14, + .flags = FlipperApplicationFlagDefault}, #endif #ifdef APP_BAD_USB - {.app = bad_usb_app, .name = "Bad USB", .stack_size = 2048, .icon = &A_BadUsb_14}, + {.app = bad_usb_app, + .name = "Bad USB", + .stack_size = 2048, + .icon = &A_BadUsb_14, + .flags = FlipperApplicationFlagDefault}, #endif #ifdef APP_U2F - {.app = u2f_app, .name = "U2F", .stack_size = 2048, .icon = &A_U2F_14}, + {.app = u2f_app, + .name = "U2F", + .stack_size = 2048, + .icon = &A_U2F_14, + .flags = FlipperApplicationFlagDefault}, #endif }; @@ -234,15 +322,27 @@ const size_t FLIPPER_ON_SYSTEM_START_COUNT = COUNT_OF(FLIPPER_ON_SYSTEM_START); // Plugin menu const FlipperApplication FLIPPER_PLUGINS[] = { #ifdef APP_BLE_HID - {.app = bt_hid_app, .name = "Bluetooth Remote", .stack_size = 1024, .icon = NULL}, + {.app = bt_hid_app, + .name = "Bluetooth Remote", + .stack_size = 1024, + .icon = NULL, + .flags = FlipperApplicationFlagDefault}, #endif #ifdef APP_MUSIC_PLAYER - {.app = music_player_app, .name = "Music Player", .stack_size = 1024, .icon = &A_Plugins_14}, + {.app = music_player_app, + .name = "Music Player", + .stack_size = 1024, + .icon = &A_Plugins_14, + .flags = FlipperApplicationFlagDefault}, #endif #ifdef APP_SNAKE_GAME - {.app = snake_game_app, .name = "Snake Game", .stack_size = 1024, .icon = &A_Plugins_14}, + {.app = snake_game_app, + .name = "Snake Game", + .stack_size = 1024, + .icon = &A_Plugins_14, + .flags = FlipperApplicationFlagDefault}, #endif }; @@ -251,108 +351,201 @@ const size_t FLIPPER_PLUGINS_COUNT = COUNT_OF(FLIPPER_PLUGINS); // Plugin menu const FlipperApplication FLIPPER_DEBUG_APPS[] = { #ifdef APP_BLINK - {.app = blink_test_app, .name = "Blink Test", .stack_size = 1024, .icon = NULL}, + {.app = blink_test_app, + .name = "Blink Test", + .stack_size = 1024, + .icon = NULL, + .flags = FlipperApplicationFlagDefault}, #endif #ifdef APP_VIBRO_TEST - {.app = vibro_test_app, .name = "Vibro Test", .stack_size = 1024, .icon = NULL}, + {.app = vibro_test_app, + .name = "Vibro Test", + .stack_size = 1024, + .icon = NULL, + .flags = FlipperApplicationFlagDefault}, #endif #ifdef APP_KEYPAD_TEST - {.app = keypad_test_app, .name = "Keypad Test", .stack_size = 1024, .icon = NULL}, + {.app = keypad_test_app, + .name = "Keypad Test", + .stack_size = 1024, + .icon = NULL, + .flags = FlipperApplicationFlagDefault}, #endif #ifdef APP_ACCESSOR - {.app = accessor_app, .name = "Accessor", .stack_size = 4096, .icon = NULL}, + {.app = accessor_app, + .name = "Accessor", + .stack_size = 4096, + .icon = NULL, + .flags = FlipperApplicationFlagDefault}, #endif #ifdef APP_USB_TEST - {.app = usb_test_app, .name = "USB Test", .stack_size = 1024, .icon = NULL}, + {.app = usb_test_app, + .name = "USB Test", + .stack_size = 1024, + .icon = NULL, + .flags = FlipperApplicationFlagDefault}, #endif #ifdef APP_USB_MOUSE - {.app = usb_mouse_app, .name = "USB Mouse Demo", .stack_size = 1024, .icon = NULL}, + {.app = usb_mouse_app, + .name = "USB Mouse Demo", + .stack_size = 1024, + .icon = NULL, + .flags = FlipperApplicationFlagDefault}, #endif #ifdef APP_UART_ECHO - {.app = uart_echo_app, .name = "Uart Echo", .stack_size = 2048, .icon = NULL}, + {.app = uart_echo_app, + .name = "Uart Echo", + .stack_size = 2048, + .icon = NULL, + .flags = FlipperApplicationFlagDefault}, #endif #ifdef APP_INFRARED_MONITOR - {.app = infrared_monitor_app, .name = "Infrared Monitor", .stack_size = 1024, .icon = NULL}, + {.app = infrared_monitor_app, + .name = "Infrared Monitor", + .stack_size = 1024, + .icon = NULL, + .flags = FlipperApplicationFlagDefault}, #endif #ifdef APP_SCENED - {.app = scened_app, .name = "Templated Scene", .stack_size = 1024, .icon = NULL}, + {.app = scened_app, + .name = "Templated Scene", + .stack_size = 1024, + .icon = NULL, + .flags = FlipperApplicationFlagDefault}, #endif #ifdef APP_LF_RFID - {.app = lfrfid_debug_app, .name = "LF-RFID Debug", .stack_size = 1024, .icon = NULL}, + {.app = lfrfid_debug_app, + .name = "LF-RFID Debug", + .stack_size = 1024, + .icon = NULL, + .flags = FlipperApplicationFlagDefault}, #endif #ifdef SRV_BT - {.app = bt_debug_app, .name = "Bluetooth Debug", .stack_size = 1024, .icon = NULL}, + {.app = bt_debug_app, + .name = "Bluetooth Debug", + .stack_size = 1024, + .icon = NULL, + .flags = FlipperApplicationFlagDefault}, #endif #ifdef APP_UNIT_TESTS - {.app = delay_test_app, .name = "Delay Test", .stack_size = 1024, .icon = NULL}, + {.app = delay_test_app, + .name = "Delay Test", + .stack_size = 1024, + .icon = NULL, + .flags = FlipperApplicationFlagDefault}, #endif #ifdef APP_DISPLAY_TEST - {.app = display_test_app, .name = "Display Test", .stack_size = 1024, .icon = NULL}, + {.app = display_test_app, + .name = "Display Test", + .stack_size = 1024, + .icon = NULL, + .flags = FlipperApplicationFlagDefault}, #endif #ifdef APP_BATTERY_TEST - {.app = battery_test_app, .name = "Battery Test", .stack_size = 1024, .icon = NULL}, + {.app = battery_test_app, + .name = "Battery Test", + .stack_size = 1024, + .icon = NULL, + .flags = FlipperApplicationFlagDefault}, #endif #ifdef APP_TEXT_BOX_TEST - {.app = text_box_test_app, .name = "Text Box Test", .stack_size = 1024, .icon = NULL}, + {.app = text_box_test_app, + .name = "Text Box Test", + .stack_size = 1024, + .icon = NULL, + .flags = FlipperApplicationFlagDefault}, #endif }; const size_t FLIPPER_DEBUG_APPS_COUNT = COUNT_OF(FLIPPER_DEBUG_APPS); #ifdef APP_ARCHIVE -const FlipperApplication FLIPPER_ARCHIVE = - {.app = archive_app, .name = "Archive", .stack_size = 4096, .icon = &A_FileManager_14}; +const FlipperApplication FLIPPER_ARCHIVE = { + .app = archive_app, + .name = "Archive", + .stack_size = 4096, + .icon = &A_FileManager_14, + .flags = FlipperApplicationFlagDefault}; #endif // Settings menu const FlipperApplication FLIPPER_SETTINGS_APPS[] = { #ifdef SRV_BT - {.app = bt_settings_app, .name = "Bluetooth", .stack_size = 1024, .icon = NULL}, + {.app = bt_settings_app, + .name = "Bluetooth", + .stack_size = 1024, + .icon = NULL, + .flags = FlipperApplicationFlagDefault}, #endif #ifdef SRV_NOTIFICATION {.app = notification_settings_app, .name = "LCD and Notifications", .stack_size = 1024, - .icon = NULL}, + .icon = NULL, + .flags = FlipperApplicationFlagDefault}, #endif #ifdef SRV_STORAGE - {.app = storage_settings_app, .name = "Storage", .stack_size = 2048, .icon = NULL}, + {.app = storage_settings_app, + .name = "Storage", + .stack_size = 2048, + .icon = NULL, + .flags = FlipperApplicationFlagDefault}, #endif #ifdef SRV_POWER - {.app = power_settings_app, .name = "Power", .stack_size = 1024, .icon = NULL}, + {.app = power_settings_app, + .name = "Power", + .stack_size = 1024, + .icon = NULL, + .flags = FlipperApplicationFlagInsomniaSafe}, #endif #ifdef SRV_DESKTOP - {.app = desktop_settings_app, .name = "Desktop", .stack_size = 1024, .icon = NULL}, + {.app = desktop_settings_app, + .name = "Desktop", + .stack_size = 1024, + .icon = NULL, + .flags = FlipperApplicationFlagDefault}, #endif #ifdef APP_PASSPORT - {.app = passport_app, .name = "Passport", .stack_size = 1024, .icon = NULL}, + {.app = passport_app, + .name = "Passport", + .stack_size = 1024, + .icon = NULL, + .flags = FlipperApplicationFlagDefault}, #endif #ifdef SRV_GUI - {.app = system_settings_app, .name = "System", .stack_size = 1024, .icon = NULL}, + {.app = system_settings_app, + .name = "System", + .stack_size = 1024, + .icon = NULL, + .flags = FlipperApplicationFlagDefault}, #endif #ifdef APP_ABOUT - {.app = about_settings_app, .name = "About", .stack_size = 1024, .icon = NULL}, + {.app = about_settings_app, + .name = "About", + .stack_size = 1024, + .icon = NULL, + .flags = FlipperApplicationFlagDefault}, #endif }; diff --git a/applications/applications.h b/applications/applications.h index 705dba28..bebe438a 100644 --- a/applications/applications.h +++ b/applications/applications.h @@ -3,11 +3,17 @@ #include #include +typedef enum { + FlipperApplicationFlagDefault = 0, + FlipperApplicationFlagInsomniaSafe = (1 << 0), +} FlipperApplicationFlag; + typedef struct { const FuriThreadCallback app; const char* name; const size_t stack_size; const Icon* icon; + const FlipperApplicationFlag flags; } FlipperApplication; typedef void (*FlipperOnStartHook)(void); diff --git a/applications/archive/archive_i.h b/applications/archive/archive_i.h index c11a1803..0822a801 100644 --- a/applications/archive/archive_i.h +++ b/applications/archive/archive_i.h @@ -27,6 +27,7 @@ struct ArchiveApp { ArchiveBrowserView* browser; TextInput* text_input; Widget* widget; + FuriPubSubSubscription* loader_stop_subscription; char text_store[MAX_NAME_LEN]; char file_extension[MAX_EXT_LEN + 1]; }; diff --git a/applications/archive/helpers/archive_browser.c b/applications/archive/helpers/archive_browser.c index 6a6ad947..b1e1e3ef 100644 --- a/applications/archive/helpers/archive_browser.c +++ b/applications/archive/helpers/archive_browser.c @@ -77,6 +77,7 @@ void archive_set_item_count(ArchiveBrowserView* browser, uint32_t count) { with_view_model( browser->view, (ArchiveBrowserViewModel * model) { model->item_cnt = count; + model->item_idx = CLAMP(model->item_idx, model->item_cnt - 1, 0); return false; }); } @@ -397,15 +398,17 @@ void archive_enter_dir(ArchiveBrowserView* browser, string_t name) { archive_dir_count_items(browser, string_get_cstr(name)); - with_view_model( - browser->view, (ArchiveBrowserViewModel * model) { - idx_last_array_push_back(model->idx_last, model->item_idx); - model->array_offset = 0; - model->item_idx = 0; - return false; - }); + if(string_cmp(browser->path, name) != 0) { + with_view_model( + browser->view, (ArchiveBrowserViewModel * model) { + idx_last_array_push_back(model->idx_last, model->item_idx); + model->array_offset = 0; + model->item_idx = 0; + return false; + }); - string_set(browser->path, name); + string_set(browser->path, name); + } archive_switch_dir(browser, string_get_cstr(browser->path)); } diff --git a/applications/archive/scenes/archive_scene_browser.c b/applications/archive/scenes/archive_scene_browser.c index 92a49304..bcfd63ba 100644 --- a/applications/archive/scenes/archive_scene_browser.c +++ b/applications/archive/scenes/archive_scene_browser.c @@ -18,6 +18,18 @@ static const char* flipper_app_name[] = { [ArchiveFileTypeUpdateManifest] = "UpdaterApp", }; +static void archive_loader_callback(const void* message, void* context) { + furi_assert(message); + furi_assert(context); + const LoaderEvent* event = message; + ArchiveApp* archive = (ArchiveApp*)context; + + if(event->type == LoaderEventTypeApplicationStopped) { + view_dispatcher_send_custom_event( + archive->view_dispatcher, ArchiveBrowserEventLoaderAppExit); + } +} + static void archive_run_in_app(ArchiveBrowserView* browser, ArchiveFile_t* selected) { Loader* loader = furi_record_open("loader"); @@ -52,6 +64,11 @@ void archive_scene_browser_on_enter(void* context) { archive_browser_set_callback(browser, archive_scene_browser_callback, archive); archive_update_focus(browser, archive->text_store); view_dispatcher_switch_to_view(archive->view_dispatcher, ArchiveViewBrowser); + + Loader* loader = furi_record_open("loader"); + archive->loader_stop_subscription = + furi_pubsub_subscribe(loader_get_pubsub(loader), archive_loader_callback, archive); + furi_record_close("loader"); } bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) { @@ -147,11 +164,25 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) { archive_file_array_load(archive->browser, 1); consumed = true; break; + case ArchiveBrowserEventLoaderAppExit: + if(!favorites) { + archive_enter_dir(browser, browser->path); + } else { + archive_favorites_read(browser); + } + + consumed = true; + break; case ArchiveBrowserEventExit: if(archive_get_depth(browser)) { archive_leave_dir(browser); } else { + Loader* loader = furi_record_open("loader"); + furi_pubsub_unsubscribe( + loader_get_pubsub(loader), archive->loader_stop_subscription); + furi_record_close("loader"); + view_dispatcher_stop(archive->view_dispatcher); } consumed = true; @@ -165,5 +196,9 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) { } void archive_scene_browser_on_exit(void* context) { - // ArchiveApp* archive = (ArchiveApp*)context; + ArchiveApp* archive = (ArchiveApp*)context; + + Loader* loader = furi_record_open("loader"); + furi_pubsub_unsubscribe(loader_get_pubsub(loader), archive->loader_stop_subscription); + furi_record_close("loader"); } diff --git a/applications/archive/views/archive_browser_view.h b/applications/archive/views/archive_browser_view.h index bec8fe2d..3aabe962 100644 --- a/applications/archive/views/archive_browser_view.h +++ b/applications/archive/views/archive_browser_view.h @@ -48,6 +48,8 @@ typedef enum { ArchiveBrowserEventLoadPrevItems, ArchiveBrowserEventLoadNextItems, + ArchiveBrowserEventLoaderAppExit, + ArchiveBrowserEventExit, } ArchiveBrowserEvent; @@ -72,7 +74,6 @@ struct ArchiveBrowserView { View* view; ArchiveBrowserViewCallback callback; void* context; - string_t path; }; diff --git a/applications/bt/bt_cli.c b/applications/bt/bt_cli.c index 4c68e597..64d69a12 100644 --- a/applications/bt/bt_cli.c +++ b/applications/bt/bt_cli.c @@ -148,7 +148,7 @@ static void bt_cli_command_packet_rx(Cli* cli, string_t args, void* context) { static void bt_cli_scan_callback(GapAddress address, void* context) { furi_assert(context); osMessageQueueId_t queue = context; - osMessageQueuePut(queue, &address, NULL, 250); + osMessageQueuePut(queue, &address, 0, 250); } static void bt_cli_command_scan(Cli* cli, string_t args, void* context) { diff --git a/applications/bt/bt_service/bt.c b/applications/bt/bt_service/bt.c index 8ce7f9ce..111ebcb2 100755 --- a/applications/bt/bt_service/bt.c +++ b/applications/bt/bt_service/bt.c @@ -60,7 +60,7 @@ static ViewPort* bt_pin_code_view_port_alloc(Bt* bt) { static void bt_pin_code_show(Bt* bt, uint32_t pin_code) { bt->pin_code = pin_code; - notification_message(bt->notification, &sequence_display_on); + notification_message(bt->notification, &sequence_display_backlight_on); gui_view_port_send_to_front(bt->gui, bt->pin_code_view_port); view_port_enabled_set(bt->pin_code_view_port, true); } @@ -74,7 +74,7 @@ static void bt_pin_code_hide(Bt* bt) { static bool bt_pin_code_verify_event_handler(Bt* bt, uint32_t pin) { furi_assert(bt); - notification_message(bt->notification, &sequence_display_on); + notification_message(bt->notification, &sequence_display_backlight_on); string_t pin_str; dialog_message_set_icon(bt->dialog_message, &I_BLE_Pairing_128x64, 0, 0); string_init_printf(pin_str, "Verify code\n%06d", pin); @@ -319,6 +319,13 @@ static void bt_change_profile(Bt* bt, BtMessage* message) { int32_t bt_srv() { Bt* bt = bt_alloc(); + if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) { + FURI_LOG_W(TAG, "Skipped BT init: device in special startup mode"); + ble_glue_wait_for_c2_start(FURI_HAL_BT_C2_START_TIMEOUT); + furi_record_create("bt", bt); + return 0; + } + // Read keys if(!bt_keys_storage_load(bt)) { FURI_LOG_W(TAG, "Failed to load bonding keys"); diff --git a/applications/cli/cli.c b/applications/cli/cli.c index 091d37f8..4e200d6b 100644 --- a/applications/cli/cli.c +++ b/applications/cli/cli.c @@ -1,9 +1,11 @@ #include "cli_i.h" #include "cli_commands.h" - +#include "cli_vcp.h" #include #include +#define TAG "CliSrv" + Cli* cli_alloc() { Cli* cli = malloc(sizeof(Cli)); @@ -12,55 +14,78 @@ Cli* cli_alloc() { string_init(cli->last_line); string_init(cli->line); + cli->session = NULL; + cli->mutex = osMutexNew(NULL); furi_check(cli->mutex); + cli->idle_sem = osSemaphoreNew(1, 0, NULL); + return cli; } -void cli_free(Cli* cli) { +void cli_putc(Cli* cli, char c) { furi_assert(cli); - - string_clear(cli->last_line); - string_clear(cli->line); - - CliCommandTree_clear(cli->commands); - - free(cli); -} - -void cli_putc(char c) { - furi_hal_vcp_tx((uint8_t*)&c, 1); + if(cli->session != NULL) { + cli->session->tx((uint8_t*)&c, 1); + } } char cli_getc(Cli* cli) { furi_assert(cli); - char c; - if(furi_hal_vcp_rx((uint8_t*)&c, 1) == 0) { + char c = 0; + if(cli->session != NULL) { + if(cli->session->rx((uint8_t*)&c, 1, osWaitForever) == 0) { + cli_reset(cli); + } + } else { cli_reset(cli); } return c; } -void cli_stdout_callback(void* _cookie, const char* data, size_t size) { - furi_hal_vcp_tx((const uint8_t*)data, size); -} - void cli_write(Cli* cli, const uint8_t* buffer, size_t size) { - return furi_hal_vcp_tx(buffer, size); + furi_assert(cli); + if(cli->session != NULL) { + cli->session->tx(buffer, size); + } } size_t cli_read(Cli* cli, uint8_t* buffer, size_t size) { - return furi_hal_vcp_rx(buffer, size); + furi_assert(cli); + if(cli->session != NULL) { + return cli->session->rx(buffer, size, osWaitForever); + } else { + return 0; + } +} + +size_t cli_read_timeout(Cli* cli, uint8_t* buffer, size_t size, uint32_t timeout) { + furi_assert(cli); + if(cli->session != NULL) { + return cli->session->rx(buffer, size, timeout); + } else { + return 0; + } } bool cli_cmd_interrupt_received(Cli* cli) { + furi_assert(cli); char c = '\0'; - if(furi_hal_vcp_rx_with_timeout((uint8_t*)&c, 1, 0) == 1) { - return c == CliSymbolAsciiETX; - } else { - return false; + if(cli->session != NULL) { + if(cli->session->rx((uint8_t*)&c, 1, 0) == 1) { + return c == CliSymbolAsciiETX; + } } + return false; +} + +bool cli_is_connected(Cli* cli) { + furi_assert(cli); + if(cli->session != NULL) { + return (cli->session->is_connected()); + } + return false; } void cli_print_usage(const char* cmd, const char* usage, const char* arg) { @@ -139,7 +164,7 @@ static void cli_handle_backspace(Cli* cli) { cli->cursor_position--; } else { - cli_putc(CliSymbolAsciiBell); + cli_putc(cli, CliSymbolAsciiBell); } } @@ -210,7 +235,7 @@ static void cli_handle_enter(Cli* cli) { printf( "`%s` command not found, use `help` or `?` to list all available commands", string_get_cstr(command)); - cli_putc(CliSymbolAsciiBell); + cli_putc(cli, CliSymbolAsciiBell); } furi_check(osMutexRelease(cli->mutex) == osOK); @@ -301,43 +326,43 @@ static void cli_handle_escape(Cli* cli, char c) { } void cli_process_input(Cli* cli) { - char c = cli_getc(cli); - size_t r; + char in_chr = cli_getc(cli); + size_t rx_len; - if(c == CliSymbolAsciiTab) { + if(in_chr == CliSymbolAsciiTab) { cli_handle_autocomplete(cli); - } else if(c == CliSymbolAsciiSOH) { + } else if(in_chr == CliSymbolAsciiSOH) { osDelay(33); // We are too fast, Minicom is not ready yet cli_motd(); cli_prompt(cli); - } else if(c == CliSymbolAsciiETX) { + } else if(in_chr == CliSymbolAsciiETX) { cli_reset(cli); cli_prompt(cli); - } else if(c == CliSymbolAsciiEOT) { + } else if(in_chr == CliSymbolAsciiEOT) { cli_reset(cli); - } else if(c == CliSymbolAsciiEsc) { - r = furi_hal_vcp_rx((uint8_t*)&c, 1); - if(r && c == '[') { - furi_hal_vcp_rx((uint8_t*)&c, 1); - cli_handle_escape(cli, c); + } else if(in_chr == CliSymbolAsciiEsc) { + rx_len = cli_read(cli, (uint8_t*)&in_chr, 1); + if((rx_len > 0) && (in_chr == '[')) { + cli_read(cli, (uint8_t*)&in_chr, 1); + cli_handle_escape(cli, in_chr); } else { - cli_putc(CliSymbolAsciiBell); + cli_putc(cli, CliSymbolAsciiBell); } - } else if(c == CliSymbolAsciiBackspace || c == CliSymbolAsciiDel) { + } else if(in_chr == CliSymbolAsciiBackspace || in_chr == CliSymbolAsciiDel) { cli_handle_backspace(cli); - } else if(c == CliSymbolAsciiCR) { + } else if(in_chr == CliSymbolAsciiCR) { cli_handle_enter(cli); - } else if(c >= 0x20 && c < 0x7F) { + } else if(in_chr >= 0x20 && in_chr < 0x7F) { if(cli->cursor_position == string_size(cli->line)) { - string_push_back(cli->line, c); - cli_putc(c); + string_push_back(cli->line, in_chr); + cli_putc(cli, in_chr); } else { // ToDo: better way? string_t temp; string_init(temp); string_reserve(temp, string_size(cli->line) + 1); string_set_strn(temp, string_get_cstr(cli->line), cli->cursor_position); - string_push_back(temp, c); + string_push_back(temp, in_chr); string_cat_str(temp, string_get_cstr(cli->line) + cli->cursor_position); // cli->line is cleared and temp's buffer moved to cli->line @@ -345,12 +370,12 @@ void cli_process_input(Cli* cli) { // NO MEMORY LEAK, STOP REPORTING IT // Print character in replace mode - printf("\e[4h%c\e[4l", c); + printf("\e[4h%c\e[4l", in_chr); fflush(stdout); } cli->cursor_position++; } else { - cli_putc(CliSymbolAsciiBell); + cli_putc(cli, CliSymbolAsciiBell); } } @@ -398,19 +423,59 @@ void cli_delete_command(Cli* cli, const char* name) { string_clear(name_str); } +void cli_session_open(Cli* cli, void* session) { + furi_assert(cli); + + furi_check(osMutexAcquire(cli->mutex, osWaitForever) == osOK); + cli->session = session; + if(cli->session != NULL) { + cli->session->init(); + furi_stdglue_set_thread_stdout_callback(cli->session->tx_stdout); + } else { + furi_stdglue_set_thread_stdout_callback(NULL); + } + osSemaphoreRelease(cli->idle_sem); + furi_check(osMutexRelease(cli->mutex) == osOK); +} + +void cli_session_close(Cli* cli) { + furi_assert(cli); + + furi_check(osMutexAcquire(cli->mutex, osWaitForever) == osOK); + if(cli->session != NULL) { + cli->session->deinit(); + } + cli->session = NULL; + furi_stdglue_set_thread_stdout_callback(NULL); + furi_check(osMutexRelease(cli->mutex) == osOK); +} + int32_t cli_srv(void* p) { Cli* cli = cli_alloc(); - furi_hal_vcp_init(); - // Init basic cli commands cli_commands_init(cli); furi_record_create("cli", cli); - furi_stdglue_set_thread_stdout_callback(cli_stdout_callback); + if(cli->session != NULL) { + furi_stdglue_set_thread_stdout_callback(cli->session->tx_stdout); + } else { + furi_stdglue_set_thread_stdout_callback(NULL); + } + + if(furi_hal_rtc_get_boot_mode() == FuriHalRtcBootModeNormal) { + cli_session_open(cli, &cli_vcp); + } else { + FURI_LOG_W(TAG, "Skipped CLI session open: device in special startup mode"); + } + while(1) { - cli_process_input(cli); + if(cli->session != NULL) { + cli_process_input(cli); + } else { + furi_check(osSemaphoreAcquire(cli->idle_sem, osWaitForever) == osOK); + } } return 0; diff --git a/applications/cli/cli.h b/applications/cli/cli.h index 1f1b61a2..29f27392 100644 --- a/applications/cli/cli.h +++ b/applications/cli/cli.h @@ -73,17 +73,28 @@ void cli_print_usage(const char* cmd, const char* usage, const char* arg); */ void cli_delete_command(Cli* cli, const char* name); -/** Read from terminal Do it only from inside of cli call. +/** Read from terminal * * @param cli Cli instance * @param buffer pointer to buffer * @param size size of buffer in bytes * - * @return bytes written + * @return bytes read */ size_t cli_read(Cli* cli, uint8_t* buffer, size_t size); -/** Not blocking check for interrupt command received +/** Non-blocking read from terminal + * + * @param cli Cli instance + * @param buffer pointer to buffer + * @param size size of buffer in bytes + * @param timeout timeout value in ms + * + * @return bytes read + */ +size_t cli_read_timeout(Cli* cli, uint8_t* buffer, size_t size, uint32_t timeout); + +/** Non-blocking check for interrupt command received * * @param cli Cli instance * @@ -111,6 +122,12 @@ char cli_getc(Cli* cli); */ void cli_nl(); +void cli_session_open(Cli* cli, void* session); + +void cli_session_close(Cli* cli); + +bool cli_is_connected(Cli* cli); + #ifdef __cplusplus } #endif diff --git a/applications/cli/cli_commands.c b/applications/cli/cli_commands.c index a58ddd49..bdec231b 100644 --- a/applications/cli/cli_commands.c +++ b/applications/cli/cli_commands.c @@ -202,7 +202,7 @@ void cli_command_led(Cli* cli, string_t args, void* context) { } else if(!string_cmp(light_name, "b")) { notification_led_message.type = NotificationMessageTypeLedBlue; } else if(!string_cmp(light_name, "bl")) { - notification_led_message.type = NotificationMessageTypeLedDisplay; + notification_led_message.type = NotificationMessageTypeLedDisplayBacklight; } else { cli_print_usage("led", " <0-255>", string_get_cstr(args)); string_clear(light_name); diff --git a/applications/cli/cli_i.h b/applications/cli/cli_i.h index c645f1a3..feb3bcb7 100755 --- a/applications/cli/cli_i.h +++ b/applications/cli/cli_i.h @@ -18,6 +18,17 @@ typedef struct { uint32_t flags; } CliCommand; +typedef struct CliSession CliSession; + +struct CliSession { + void (*init)(void); + void (*deinit)(void); + size_t (*rx)(uint8_t* buffer, size_t size, uint32_t timeout); + void (*tx)(const uint8_t* buffer, size_t size); + void (*tx_stdout)(void* _cookie, const char* data, size_t size); + bool (*is_connected)(void); +}; + BPTREE_DEF2( CliCommandTree, CLI_COMMANDS_TREE_RANK, @@ -31,18 +42,18 @@ BPTREE_DEF2( struct Cli { CliCommandTree_t commands; osMutexId_t mutex; + osSemaphoreId_t idle_sem; string_t last_line; string_t line; + CliSession* session; size_t cursor_position; }; Cli* cli_alloc(); -void cli_free(Cli* cli); - void cli_reset(Cli* cli); -void cli_putc(char c); +void cli_putc(Cli* cli, char c); void cli_stdout_callback(void* _cookie, const char* data, size_t size); diff --git a/firmware/targets/f7/furi_hal/furi_hal_vcp.c b/applications/cli/cli_vcp.c similarity index 69% rename from firmware/targets/f7/furi_hal/furi_hal_vcp.c rename to applications/cli/cli_vcp.c index a4694625..f570aee1 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_vcp.c +++ b/applications/cli/cli_vcp.c @@ -2,8 +2,9 @@ #include #include #include +#include "cli_i.h" -#define TAG "FuriHalVcp" +#define TAG "CliVcp" #define USB_CDC_PKT_LEN CDC_DATA_SZ #define VCP_RX_BUF_SIZE (USB_CDC_PKT_LEN * 3) @@ -12,19 +13,18 @@ #define VCP_IF_NUM 0 typedef enum { - VcpEvtEnable = (1 << 0), - VcpEvtDisable = (1 << 1), - VcpEvtConnect = (1 << 2), - VcpEvtDisconnect = (1 << 3), - VcpEvtStreamRx = (1 << 4), - VcpEvtRx = (1 << 5), - VcpEvtStreamTx = (1 << 6), - VcpEvtTx = (1 << 7), + VcpEvtStop = (1 << 0), + VcpEvtConnect = (1 << 1), + VcpEvtDisconnect = (1 << 2), + VcpEvtStreamRx = (1 << 3), + VcpEvtRx = (1 << 4), + VcpEvtStreamTx = (1 << 5), + VcpEvtTx = (1 << 6), } WorkerEvtFlags; -#define VCP_THREAD_FLAG_ALL \ - (VcpEvtEnable | VcpEvtDisable | VcpEvtConnect | VcpEvtDisconnect | VcpEvtRx | VcpEvtTx | \ - VcpEvtStreamRx | VcpEvtStreamTx) +#define VCP_THREAD_FLAG_ALL \ + (VcpEvtStop | VcpEvtConnect | VcpEvtDisconnect | VcpEvtRx | VcpEvtTx | VcpEvtStreamRx | \ + VcpEvtStreamTx) typedef struct { FuriThread* thread; @@ -33,9 +33,12 @@ typedef struct { StreamBufferHandle_t rx_stream; volatile bool connected; + volatile bool running; + + FuriHalUsbInterface* usb_if_prev; uint8_t data_buffer[USB_CDC_PKT_LEN]; -} FuriHalVcp; +} CliVcp; static int32_t vcp_worker(void* context); static void vcp_on_cdc_tx_complete(void* context); @@ -51,25 +54,23 @@ static CdcCallbacks cdc_cb = { NULL, }; -static FuriHalVcp* vcp = NULL; +static CliVcp* vcp = NULL; static const uint8_t ascii_soh = 0x01; static const uint8_t ascii_eot = 0x04; -void furi_hal_vcp_init() { - vcp = malloc(sizeof(FuriHalVcp)); +static void cli_vcp_init() { + if(vcp == NULL) { + vcp = malloc(sizeof(CliVcp)); + vcp->tx_stream = xStreamBufferCreate(VCP_TX_BUF_SIZE, 1); + vcp->rx_stream = xStreamBufferCreate(VCP_RX_BUF_SIZE, 1); + } + furi_assert(vcp->thread == NULL); + vcp->connected = false; - vcp->tx_stream = xStreamBufferCreate(VCP_TX_BUF_SIZE, 1); - vcp->rx_stream = xStreamBufferCreate(VCP_RX_BUF_SIZE, 1); - - if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) { - FURI_LOG_W(TAG, "Skipped worker init: device in special startup mode="); - return; - } - vcp->thread = furi_thread_alloc(); - furi_thread_set_name(vcp->thread, "VcpDriver"); + furi_thread_set_name(vcp->thread, "CliVcpWorker"); furi_thread_set_stack_size(vcp->thread, 1024); furi_thread_set_callback(vcp->thread, vcp_worker); furi_thread_start(vcp->thread); @@ -77,48 +78,35 @@ void furi_hal_vcp_init() { FURI_LOG_I(TAG, "Init OK"); } +static void cli_vcp_deinit() { + osThreadFlagsSet(furi_thread_get_thread_id(vcp->thread), VcpEvtStop); + furi_thread_join(vcp->thread); + furi_thread_free(vcp->thread); + vcp->thread = NULL; +} + static int32_t vcp_worker(void* context) { - bool enabled = true; - bool tx_idle = false; + bool tx_idle = true; size_t missed_rx = 0; uint8_t last_tx_pkt_len = 0; - furi_hal_usb_set_config(&usb_cdc_single, NULL); + // Switch USB to VCP mode (if it is not set yet) + vcp->usb_if_prev = furi_hal_usb_get_config(); + if((vcp->usb_if_prev != &usb_cdc_single) && (vcp->usb_if_prev != &usb_cdc_dual)) { + furi_hal_usb_set_config(&usb_cdc_single, NULL); + } furi_hal_cdc_set_callbacks(VCP_IF_NUM, &cdc_cb, NULL); + FURI_LOG_D(TAG, "Start"); + vcp->running = true; + while(1) { uint32_t flags = osThreadFlagsWait(VCP_THREAD_FLAG_ALL, osFlagsWaitAny, osWaitForever); furi_assert((flags & osFlagsError) == 0); - // VCP enabled - if((flags & VcpEvtEnable) && !enabled) { -#ifdef FURI_HAL_USB_VCP_DEBUG - FURI_LOG_D(TAG, "Enable"); -#endif - flags |= VcpEvtTx; - furi_hal_cdc_set_callbacks(VCP_IF_NUM, &cdc_cb, NULL); - enabled = true; - furi_hal_cdc_receive(VCP_IF_NUM, vcp->data_buffer, USB_CDC_PKT_LEN); // flush Rx buffer - if(furi_hal_cdc_get_ctrl_line_state(VCP_IF_NUM) & (1 << 0)) { - vcp->connected = true; - xStreamBufferSend(vcp->rx_stream, &ascii_soh, 1, osWaitForever); - } - } - - // VCP disabled - if((flags & VcpEvtDisable) && enabled) { -#ifdef FURI_HAL_USB_VCP_DEBUG - FURI_LOG_D(TAG, "Disable"); -#endif - enabled = false; - vcp->connected = false; - xStreamBufferReceive(vcp->tx_stream, vcp->data_buffer, USB_CDC_PKT_LEN, 0); - xStreamBufferSend(vcp->rx_stream, &ascii_eot, 1, osWaitForever); - } - // VCP session opened - if((flags & VcpEvtConnect) && enabled) { -#ifdef FURI_HAL_USB_VCP_DEBUG + if(flags & VcpEvtConnect) { +#ifdef CLI_VCP_DEBUG FURI_LOG_D(TAG, "Connect"); #endif if(vcp->connected == false) { @@ -128,8 +116,8 @@ static int32_t vcp_worker(void* context) { } // VCP session closed - if((flags & VcpEvtDisconnect) && enabled) { -#ifdef FURI_HAL_USB_VCP_DEBUG + if(flags & VcpEvtDisconnect) { +#ifdef CLI_VCP_DEBUG FURI_LOG_D(TAG, "Disconnect"); #endif if(vcp->connected == true) { @@ -140,8 +128,8 @@ static int32_t vcp_worker(void* context) { } // Rx buffer was read, maybe there is enough space for new data? - if((flags & VcpEvtStreamRx) && enabled && missed_rx > 0) { -#ifdef FURI_HAL_USB_VCP_DEBUG + if((flags & VcpEvtStreamRx) && (missed_rx > 0)) { +#ifdef CLI_VCP_DEBUG FURI_LOG_D(TAG, "StreamRx"); #endif if(xStreamBufferSpacesAvailable(vcp->rx_stream) >= USB_CDC_PKT_LEN) { @@ -151,10 +139,10 @@ static int32_t vcp_worker(void* context) { } // New data received - if((flags & VcpEvtRx)) { + if(flags & VcpEvtRx) { if(xStreamBufferSpacesAvailable(vcp->rx_stream) >= USB_CDC_PKT_LEN) { int32_t len = furi_hal_cdc_receive(VCP_IF_NUM, vcp->data_buffer, USB_CDC_PKT_LEN); -#ifdef FURI_HAL_USB_VCP_DEBUG +#ifdef CLI_VCP_DEBUG FURI_LOG_D(TAG, "Rx %d", len); #endif if(len > 0) { @@ -163,7 +151,7 @@ static int32_t vcp_worker(void* context) { len); } } else { -#ifdef FURI_HAL_USB_VCP_DEBUG +#ifdef CLI_VCP_DEBUG FURI_LOG_D(TAG, "Rx missed"); #endif missed_rx++; @@ -171,8 +159,8 @@ static int32_t vcp_worker(void* context) { } // New data in Tx buffer - if((flags & VcpEvtStreamTx) && enabled) { -#ifdef FURI_HAL_USB_VCP_DEBUG + if(flags & VcpEvtStreamTx) { +#ifdef CLI_VCP_DEBUG FURI_LOG_D(TAG, "StreamTx"); #endif if(tx_idle) { @@ -181,10 +169,10 @@ static int32_t vcp_worker(void* context) { } // CDC write transfer done - if((flags & VcpEvtTx) && enabled) { + if(flags & VcpEvtTx) { size_t len = xStreamBufferReceive(vcp->tx_stream, vcp->data_buffer, USB_CDC_PKT_LEN, 0); -#ifdef FURI_HAL_USB_VCP_DEBUG +#ifdef CLI_VCP_DEBUG FURI_LOG_D(TAG, "Tx %d", len); #endif if(len > 0) { // Some data left in Tx buffer. Sending it now @@ -202,23 +190,33 @@ static int32_t vcp_worker(void* context) { last_tx_pkt_len = 0; } } + + if(flags & VcpEvtStop) { + vcp->connected = false; + vcp->running = false; + furi_hal_cdc_set_callbacks(VCP_IF_NUM, NULL, NULL); + // Restore previous USB mode (if it was set during init) + if((vcp->usb_if_prev != &usb_cdc_single) && (vcp->usb_if_prev != &usb_cdc_dual)) { + furi_hal_usb_set_config(vcp->usb_if_prev, NULL); + } + xStreamBufferReceive(vcp->tx_stream, vcp->data_buffer, USB_CDC_PKT_LEN, 0); + xStreamBufferSend(vcp->rx_stream, &ascii_eot, 1, osWaitForever); + break; + } } + FURI_LOG_D(TAG, "End"); return 0; } -void furi_hal_vcp_enable() { - osThreadFlagsSet(furi_thread_get_thread_id(vcp->thread), VcpEvtEnable); -} - -void furi_hal_vcp_disable() { - osThreadFlagsSet(furi_thread_get_thread_id(vcp->thread), VcpEvtDisable); -} - -size_t furi_hal_vcp_rx_with_timeout(uint8_t* buffer, size_t size, uint32_t timeout) { +static size_t cli_vcp_rx(uint8_t* buffer, size_t size, uint32_t timeout) { furi_assert(vcp); furi_assert(buffer); -#ifdef FURI_HAL_USB_VCP_DEBUG + if(vcp->running == false) { + return 0; + } + +#ifdef CLI_VCP_DEBUG FURI_LOG_D(TAG, "rx %u start", size); #endif @@ -229,7 +227,7 @@ size_t furi_hal_vcp_rx_with_timeout(uint8_t* buffer, size_t size, uint32_t timeo if(batch_size > VCP_RX_BUF_SIZE) batch_size = VCP_RX_BUF_SIZE; size_t len = xStreamBufferReceive(vcp->rx_stream, buffer, batch_size, timeout); -#ifdef FURI_HAL_USB_VCP_DEBUG +#ifdef CLI_VCP_DEBUG FURI_LOG_D(TAG, "rx %u ", batch_size); #endif if(len == 0) break; @@ -239,22 +237,21 @@ size_t furi_hal_vcp_rx_with_timeout(uint8_t* buffer, size_t size, uint32_t timeo rx_cnt += len; } -#ifdef FURI_HAL_USB_VCP_DEBUG +#ifdef CLI_VCP_DEBUG FURI_LOG_D(TAG, "rx %u end", size); #endif return rx_cnt; } -size_t furi_hal_vcp_rx(uint8_t* buffer, size_t size) { - furi_assert(vcp); - return furi_hal_vcp_rx_with_timeout(buffer, size, osWaitForever); -} - -void furi_hal_vcp_tx(const uint8_t* buffer, size_t size) { +static void cli_vcp_tx(const uint8_t* buffer, size_t size) { furi_assert(vcp); furi_assert(buffer); -#ifdef FURI_HAL_USB_VCP_DEBUG + if(vcp->running == false) { + return; + } + +#ifdef CLI_VCP_DEBUG FURI_LOG_D(TAG, "tx %u start", size); #endif @@ -264,7 +261,7 @@ void furi_hal_vcp_tx(const uint8_t* buffer, size_t size) { xStreamBufferSend(vcp->tx_stream, buffer, batch_size, osWaitForever); osThreadFlagsSet(furi_thread_get_thread_id(vcp->thread), VcpEvtStreamTx); -#ifdef FURI_HAL_USB_VCP_DEBUG +#ifdef CLI_VCP_DEBUG FURI_LOG_D(TAG, "tx %u", batch_size); #endif @@ -272,11 +269,15 @@ void furi_hal_vcp_tx(const uint8_t* buffer, size_t size) { buffer += batch_size; } -#ifdef FURI_HAL_USB_VCP_DEBUG +#ifdef CLI_VCP_DEBUG FURI_LOG_D(TAG, "tx %u end", size); #endif } +static void cli_vcp_tx_stdout(void* _cookie, const char* data, size_t size) { + cli_vcp_tx((const uint8_t*)data, size); +} + static void vcp_state_callback(void* context, uint8_t state) { if(state == 0) { osThreadFlagsSet(furi_thread_get_thread_id(vcp->thread), VcpEvtDisconnect); @@ -303,7 +304,16 @@ static void vcp_on_cdc_tx_complete(void* context) { osThreadFlagsSet(furi_thread_get_thread_id(vcp->thread), VcpEvtTx); } -bool furi_hal_vcp_is_connected(void) { +static bool cli_vcp_is_connected(void) { furi_assert(vcp); return vcp->connected; } + +CliSession cli_vcp = { + cli_vcp_init, + cli_vcp_deinit, + cli_vcp_rx, + cli_vcp_tx, + cli_vcp_tx_stdout, + cli_vcp_is_connected, +}; diff --git a/applications/cli/cli_vcp.h b/applications/cli/cli_vcp.h new file mode 100644 index 00000000..2bd47efb --- /dev/null +++ b/applications/cli/cli_vcp.h @@ -0,0 +1,18 @@ +/** + * @file cli_vcp.h + * VCP HAL API + */ + +#pragma once + +#include "cli_i.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern CliSession cli_vcp; + +#ifdef __cplusplus +} +#endif diff --git a/applications/debug_tools/uart_echo.c b/applications/debug_tools/uart_echo.c index 73af1e66..0e4ee1d8 100644 --- a/applications/debug_tools/uart_echo.c +++ b/applications/debug_tools/uart_echo.c @@ -45,7 +45,7 @@ typedef enum { #define WORKER_EVENTS_MASK (WorkerEventStop | WorkerEventRx) const NotificationSequence sequence_notification = { - &message_display_on, + &message_display_backlight_on, &message_green_255, &message_delay_10, NULL, diff --git a/applications/desktop/desktop.c b/applications/desktop/desktop.c index 1c1ce04f..7686a145 100644 --- a/applications/desktop/desktop.c +++ b/applications/desktop/desktop.c @@ -14,7 +14,7 @@ #include "desktop/views/desktop_view_pin_input.h" #include "desktop/views/desktop_view_pin_timeout.h" #include "desktop_i.h" -#include "desktop_helpers.h" +#include "helpers/pin_lock.h" static void desktop_auto_lock_arm(Desktop*); static void desktop_auto_lock_inhibit(Desktop*); @@ -117,17 +117,18 @@ static void desktop_auto_lock_inhibit(Desktop* desktop) { } void desktop_lock(Desktop* desktop) { - furi_hal_rtc_set_pin_fails(0); desktop_auto_lock_inhibit(desktop); scene_manager_set_scene_state( desktop->scene_manager, DesktopSceneLocked, SCENE_LOCKED_FIRST_ENTER); scene_manager_next_scene(desktop->scene_manager, DesktopSceneLocked); - notification_message(desktop->notification, &sequence_display_off_delay_1000); + notification_message(desktop->notification, &sequence_display_backlight_off_delay_1000); } void desktop_unlock(Desktop* desktop) { - furi_hal_rtc_set_pin_fails(0); - desktop_helpers_unlock_system(desktop); + view_port_enabled_set(desktop->lock_viewport, false); + Gui* gui = furi_record_open("gui"); + gui_set_lockdown(gui, false); + furi_record_close("gui"); desktop_view_locked_unlock(desktop->locked_view); scene_manager_search_and_switch_to_previous_scene(desktop->scene_manager, DesktopSceneMain); desktop_auto_lock_arm(desktop); @@ -301,18 +302,15 @@ int32_t desktop_srv(void* p) { bool loaded = LOAD_DESKTOP_SETTINGS(&desktop->settings); if(!loaded) { - furi_hal_rtc_reset_flag(FuriHalRtcFlagLock); memset(&desktop->settings, 0, sizeof(desktop->settings)); SAVE_DESKTOP_SETTINGS(&desktop->settings); } scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain); - if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagLock) && !desktop->settings.pin_code.length) { - furi_hal_rtc_reset_flag(FuriHalRtcFlagLock); - } + desktop_pin_lock_init(&desktop->settings); - if(!furi_hal_rtc_is_flag_set(FuriHalRtcFlagLock)) { + if(!desktop_pin_lock_is_locked()) { if(!loader_is_locked(desktop->loader)) { desktop_auto_lock_arm(desktop); } diff --git a/applications/desktop/desktop_helpers.c b/applications/desktop/desktop_helpers.c deleted file mode 100644 index c3025ae1..00000000 --- a/applications/desktop/desktop_helpers.c +++ /dev/null @@ -1,82 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include "desktop_helpers.h" -#include "desktop_i.h" - -static const NotificationSequence sequence_pin_fail = { - &message_display_on, - - &message_red_255, - &message_vibro_on, - &message_delay_100, - &message_vibro_off, - &message_red_0, - - &message_delay_250, - - &message_red_255, - &message_vibro_on, - &message_delay_100, - &message_vibro_off, - &message_red_0, - NULL, -}; - -static const uint8_t desktop_helpers_fails_timeout[] = { - 0, - 0, - 0, - 0, - 30, - 60, - 90, - 120, - 150, - 180, - /* +60 for every next fail */ -}; - -void desktop_helpers_emit_error_notification() { - NotificationApp* notification = furi_record_open("notification"); - notification_message(notification, &sequence_pin_fail); - furi_record_close("notification"); -} - -void desktop_helpers_lock_system(Desktop* desktop, bool hard_lock) { - view_port_enabled_set(desktop->lock_viewport, true); - if(hard_lock) { - furi_hal_rtc_set_flag(FuriHalRtcFlagLock); - furi_hal_usb_disable(); - } - - Gui* gui = furi_record_open("gui"); - gui_set_lockdown(gui, true); - furi_record_close("gui"); -} - -void desktop_helpers_unlock_system(Desktop* desktop) { - furi_hal_rtc_reset_flag(FuriHalRtcFlagLock); - furi_hal_usb_enable(); - view_port_enabled_set(desktop->lock_viewport, false); - - Gui* gui = furi_record_open("gui"); - gui_set_lockdown(gui, false); - furi_record_close("gui"); -} - -uint32_t desktop_helpers_get_pin_fail_timeout(uint32_t pin_fails) { - uint32_t pin_timeout = 0; - uint32_t max_index = COUNT_OF(desktop_helpers_fails_timeout) - 1; - if(pin_fails <= max_index) { - pin_timeout = desktop_helpers_fails_timeout[pin_fails]; - } else { - pin_timeout = desktop_helpers_fails_timeout[max_index] + (pin_fails - max_index) * 60; - } - - return pin_timeout; -} diff --git a/applications/desktop/desktop_helpers.h b/applications/desktop/desktop_helpers.h deleted file mode 100644 index f8393df8..00000000 --- a/applications/desktop/desktop_helpers.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once -#include -#include -#include "desktop.h" - -void desktop_helpers_emit_error_notification(); -void desktop_helpers_lock_system(Desktop* desktop, bool hard_lock); -void desktop_helpers_unlock_system(Desktop* desktop); -uint32_t desktop_helpers_get_pin_fail_timeout(uint32_t pin_fails); diff --git a/applications/desktop/desktop_settings/desktop_settings.h b/applications/desktop/desktop_settings/desktop_settings.h index b56d63a7..85beb503 100644 --- a/applications/desktop/desktop_settings/desktop_settings.h +++ b/applications/desktop/desktop_settings/desktop_settings.h @@ -5,7 +5,7 @@ #include #include -#define DESKTOP_SETTINGS_VER (2) +#define DESKTOP_SETTINGS_VER (3) #define DESKTOP_SETTINGS_PATH "/int/desktop.settings" #define DESKTOP_SETTINGS_MAGIC (0x17) #define PIN_MAX_LENGTH 12 @@ -39,17 +39,6 @@ typedef struct { typedef struct { uint16_t favorite; PinCode pin_code; + uint8_t is_locked; uint32_t auto_lock_delay_ms; } DesktopSettings; - -static inline bool pins_are_equal(const PinCode* pin_code1, const PinCode* pin_code2) { - furi_assert(pin_code1); - furi_assert(pin_code2); - bool result = false; - - if(pin_code1->length == pin_code2->length) { - result = !memcmp(pin_code1->data, pin_code2->data, pin_code1->length); - } - - return result; -} diff --git a/applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_auth.c b/applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_auth.c index c57506f9..54330c74 100644 --- a/applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_auth.c +++ b/applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_auth.c @@ -1,7 +1,7 @@ #include #include #include - +#include "../../helpers/pin_lock.h" #include "../desktop_settings_app.h" #include "desktop/desktop_settings/desktop_settings.h" #include "desktop/views/desktop_view_pin_input.h" @@ -18,7 +18,7 @@ static void pin_auth_done_callback(const PinCode* pin_code, void* context) { DesktopSettingsApp* app = context; app->pincode_buffer = *pin_code; - if(pins_are_equal(&app->settings.pin_code, pin_code)) { + if(desktop_pins_are_equal(&app->settings.pin_code, pin_code)) { view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_PINS_EQUAL); } else { view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_PINS_DIFFERENT); diff --git a/applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_error.c b/applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_error.c index 07bba4a8..882b8f0a 100644 --- a/applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_error.c +++ b/applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_error.c @@ -6,7 +6,7 @@ #include "desktop/views/desktop_view_pin_input.h" #include "desktop_settings_scene.h" #include "desktop_settings_scene_i.h" -#include "../../desktop_helpers.h" +#include "../../helpers/pin_lock.h" #include "../desktop_settings_app.h" #define SCENE_EVENT_EXIT (0U) @@ -25,7 +25,7 @@ static void pin_error_done_callback(const PinCode* pin_code, void* context) { void desktop_settings_scene_pin_error_on_enter(void* context) { DesktopSettingsApp* app = context; - desktop_helpers_emit_error_notification(); + desktop_pin_lock_error_notify(); desktop_view_pin_input_set_context(app->pin_input_view, app); desktop_view_pin_input_set_back_callback(app->pin_input_view, pin_error_back_callback); diff --git a/applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_setup.c b/applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_setup.c index 5659684f..282bf853 100644 --- a/applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_setup.c +++ b/applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_setup.c @@ -7,6 +7,7 @@ #include "desktop/views/desktop_view_pin_input.h" #include "desktop_settings_scene.h" #include "desktop_settings_scene_i.h" +#include "../../helpers/pin_lock.h" #define SCENE_EVENT_EXIT (0U) #define SCENE_EVENT_1ST_PIN_ENTERED (1U) @@ -24,7 +25,7 @@ static void pin_setup_done_callback(const PinCode* pin_code, void* context) { view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_1ST_PIN_ENTERED); } else { app->pincode_buffer_filled = false; - if(pins_are_equal(&app->pincode_buffer, pin_code)) { + if(desktop_pins_are_equal(&app->pincode_buffer, pin_code)) { view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_PINS_EQUAL); } else { view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_PINS_DIFFERENT); diff --git a/applications/desktop/helpers/pin_lock.c b/applications/desktop/helpers/pin_lock.c new file mode 100644 index 00000000..00ac4177 --- /dev/null +++ b/applications/desktop/helpers/pin_lock.c @@ -0,0 +1,138 @@ + +#include +#include +#include +#include +#include +#include + +#include "../helpers/pin_lock.h" +#include "../desktop_i.h" +#include + +static const NotificationSequence sequence_pin_fail = { + &message_display_backlight_on, + + &message_red_255, + &message_vibro_on, + &message_delay_100, + &message_vibro_off, + &message_red_0, + + &message_delay_250, + + &message_red_255, + &message_vibro_on, + &message_delay_100, + &message_vibro_off, + &message_red_0, + NULL, +}; + +static const uint8_t desktop_helpers_fails_timeout[] = { + 0, + 0, + 0, + 0, + 30, + 60, + 90, + 120, + 150, + 180, + /* +60 for every next fail */ +}; + +void desktop_pin_lock_error_notify() { + NotificationApp* notification = furi_record_open("notification"); + notification_message(notification, &sequence_pin_fail); + furi_record_close("notification"); +} + +uint32_t desktop_pin_lock_get_fail_timeout() { + uint32_t pin_fails = furi_hal_rtc_get_pin_fails(); + uint32_t pin_timeout = 0; + uint32_t max_index = COUNT_OF(desktop_helpers_fails_timeout) - 1; + if(pin_fails <= max_index) { + pin_timeout = desktop_helpers_fails_timeout[pin_fails]; + } else { + pin_timeout = desktop_helpers_fails_timeout[max_index] + (pin_fails - max_index) * 60; + } + + return pin_timeout; +} + +void desktop_pin_lock(DesktopSettings* settings) { + furi_assert(settings); + + furi_hal_rtc_set_pin_fails(0); + furi_hal_rtc_set_flag(FuriHalRtcFlagLock); + Cli* cli = furi_record_open("cli"); + cli_session_close(cli); + furi_record_close("cli"); + settings->is_locked = 1; + SAVE_DESKTOP_SETTINGS(settings); +} + +void desktop_pin_unlock(DesktopSettings* settings) { + furi_assert(settings); + + furi_hal_rtc_reset_flag(FuriHalRtcFlagLock); + Cli* cli = furi_record_open("cli"); + cli_session_open(cli, &cli_vcp); + furi_record_close("cli"); + settings->is_locked = 0; + SAVE_DESKTOP_SETTINGS(settings); +} + +void desktop_pin_lock_init(DesktopSettings* settings) { + furi_assert(settings); + + if(settings->pin_code.length > 0) { + if(settings->is_locked == 1) { + furi_hal_rtc_set_flag(FuriHalRtcFlagLock); + } else { + if(desktop_pin_lock_is_locked()) { + settings->is_locked = 1; + SAVE_DESKTOP_SETTINGS(settings); + } + } + } else { + furi_hal_rtc_set_pin_fails(0); + furi_hal_rtc_reset_flag(FuriHalRtcFlagLock); + furi_hal_usb_enable(); + } + + if(desktop_pin_lock_is_locked()) { + furi_hal_usb_disable(); + } +} + +bool desktop_pin_lock_verify(const PinCode* pin_set, const PinCode* pin_entered) { + bool result = false; + if(desktop_pins_are_equal(pin_set, pin_entered)) { + furi_hal_rtc_set_pin_fails(0); + result = true; + } else { + uint32_t pin_fails = furi_hal_rtc_get_pin_fails(); + furi_hal_rtc_set_pin_fails(pin_fails + 1); + result = false; + } + return result; +} + +bool desktop_pin_lock_is_locked() { + return furi_hal_rtc_is_flag_set(FuriHalRtcFlagLock); +} + +bool desktop_pins_are_equal(const PinCode* pin_code1, const PinCode* pin_code2) { + furi_assert(pin_code1); + furi_assert(pin_code2); + bool result = false; + + if(pin_code1->length == pin_code2->length) { + result = !memcmp(pin_code1->data, pin_code2->data, pin_code1->length); + } + + return result; +} diff --git a/applications/desktop/helpers/pin_lock.h b/applications/desktop/helpers/pin_lock.h new file mode 100644 index 00000000..4454cda5 --- /dev/null +++ b/applications/desktop/helpers/pin_lock.h @@ -0,0 +1,21 @@ +#pragma once +#include +#include +#include "../desktop.h" +#include "../desktop_settings/desktop_settings.h" + +void desktop_pin_lock_error_notify(); + +uint32_t desktop_pin_lock_get_fail_timeout(); + +void desktop_pin_lock(DesktopSettings* settings); + +void desktop_pin_unlock(DesktopSettings* settings); + +bool desktop_pin_lock_is_locked(); + +void desktop_pin_lock_init(DesktopSettings* settings); + +bool desktop_pin_lock_verify(const PinCode* pin_set, const PinCode* pin_entered); + +bool desktop_pins_are_equal(const PinCode* pin_code1, const PinCode* pin_code2); diff --git a/applications/desktop/scenes/desktop_scene_lock_menu.c b/applications/desktop/scenes/desktop_scene_lock_menu.c index 7b7606b1..11478810 100644 --- a/applications/desktop/scenes/desktop_scene_lock_menu.c +++ b/applications/desktop/scenes/desktop_scene_lock_menu.c @@ -10,6 +10,7 @@ #include "../views/desktop_view_lock_menu.h" #include "desktop_scene_i.h" #include "desktop_scene.h" +#include "../helpers/pin_lock.h" #define TAG "DesktopSceneLock" @@ -53,7 +54,7 @@ bool desktop_scene_lock_menu_on_event(void* context, SceneManagerEvent event) { break; case DesktopLockMenuEventPinLock: if(desktop->settings.pin_code.length > 0) { - furi_hal_rtc_set_flag(FuriHalRtcFlagLock); + desktop_pin_lock(&desktop->settings); desktop_lock(desktop); } else { LoaderStatus status = @@ -64,7 +65,6 @@ bool desktop_scene_lock_menu_on_event(void* context, SceneManagerEvent event) { FURI_LOG_E(TAG, "Unable to start desktop settings"); } } - consumed = true; break; case DesktopLockMenuEventExit: diff --git a/applications/desktop/scenes/desktop_scene_locked.c b/applications/desktop/scenes/desktop_scene_locked.c index 65d064d8..90df2226 100644 --- a/applications/desktop/scenes/desktop_scene_locked.c +++ b/applications/desktop/scenes/desktop_scene_locked.c @@ -7,7 +7,7 @@ #include "../desktop.h" #include "../desktop_i.h" -#include "../desktop_helpers.h" +#include "../helpers/pin_lock.h" #include "../animations/animation_manager.h" #include "../views/desktop_events.h" #include "../views/desktop_view_pin_input.h" @@ -45,14 +45,17 @@ void desktop_scene_locked_on_enter(void* context) { bool switch_to_timeout_scene = false; uint32_t state = scene_manager_get_scene_state(desktop->scene_manager, DesktopSceneLocked); if(state == SCENE_LOCKED_FIRST_ENTER) { - bool pin_locked = furi_hal_rtc_is_flag_set(FuriHalRtcFlagLock); - desktop_helpers_lock_system(desktop, pin_locked); + bool pin_locked = desktop_pin_lock_is_locked(); + view_port_enabled_set(desktop->lock_viewport, true); + Gui* gui = furi_record_open("gui"); + gui_set_lockdown(gui, true); + furi_record_close("gui"); + if(pin_locked) { LOAD_DESKTOP_SETTINGS(&desktop->settings); desktop_view_locked_lock(desktop->locked_view, true); - uint32_t pin_fails = furi_hal_rtc_get_pin_fails(); - uint32_t pin_timeout = desktop_helpers_get_pin_fail_timeout(pin_fails); - if(pin_timeout) { + uint32_t pin_timeout = desktop_pin_lock_get_fail_timeout(); + if(pin_timeout > 0) { scene_manager_set_scene_state( desktop->scene_manager, DesktopScenePinTimeout, pin_timeout); switch_to_timeout_scene = true; @@ -86,7 +89,7 @@ bool desktop_scene_locked_on_event(void* context, SceneManagerEvent event) { break; case DesktopLockedEventUpdate: if(desktop_view_locked_is_locked_hint_visible(desktop->locked_view)) { - notification_message(desktop->notification, &sequence_display_off); + notification_message(desktop->notification, &sequence_display_backlight_off); } desktop_view_locked_update(desktop->locked_view); consumed = true; diff --git a/applications/desktop/scenes/desktop_scene_pin_input.c b/applications/desktop/scenes/desktop_scene_pin_input.c index 827b74a6..dd5c8ce3 100644 --- a/applications/desktop/scenes/desktop_scene_pin_input.c +++ b/applications/desktop/scenes/desktop_scene_pin_input.c @@ -12,7 +12,7 @@ #include "../animations/animation_manager.h" #include "../views/desktop_events.h" #include "../views/desktop_view_pin_input.h" -#include "../desktop_helpers.h" +#include "../helpers/pin_lock.h" #include "desktop_scene.h" #include "desktop_scene_i.h" @@ -54,7 +54,7 @@ static void desktop_scene_pin_input_back_callback(void* context) { static void desktop_scene_pin_input_done_callback(const PinCode* pin_code, void* context) { Desktop* desktop = (Desktop*)context; - if(pins_are_equal(&desktop->settings.pin_code, pin_code)) { + if(desktop_pin_lock_verify(&desktop->settings.pin_code, pin_code)) { view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopPinInputEventUnlocked); } else { view_dispatcher_send_custom_event( @@ -97,17 +97,14 @@ void desktop_scene_pin_input_on_enter(void* context) { bool desktop_scene_pin_input_on_event(void* context, SceneManagerEvent event) { Desktop* desktop = (Desktop*)context; bool consumed = false; - uint32_t pin_fails = 0; + uint32_t pin_timeout = 0; if(event.type == SceneManagerEventTypeCustom) { switch(event.event) { case DesktopPinInputEventUnlockFailed: - pin_fails = furi_hal_rtc_get_pin_fails(); - pin_fails++; - furi_hal_rtc_set_pin_fails(pin_fails); - uint32_t pin_timeout = desktop_helpers_get_pin_fail_timeout(pin_fails); + pin_timeout = desktop_pin_lock_get_fail_timeout(); if(pin_timeout > 0) { - desktop_helpers_emit_error_notification(); + desktop_pin_lock_error_notify(); scene_manager_set_scene_state( desktop->scene_manager, DesktopScenePinTimeout, pin_timeout); scene_manager_next_scene(desktop->scene_manager, DesktopScenePinTimeout); @@ -129,13 +126,14 @@ bool desktop_scene_pin_input_on_event(void* context, SceneManagerEvent event) { consumed = true; break; case DesktopPinInputEventUnlocked: + desktop_pin_unlock(&desktop->settings); desktop_unlock(desktop); consumed = true; break; case DesktopPinInputEventBack: scene_manager_search_and_switch_to_previous_scene( desktop->scene_manager, DesktopSceneLocked); - notification_message(desktop->notification, &sequence_display_off); + notification_message(desktop->notification, &sequence_display_backlight_off); consumed = true; break; } diff --git a/applications/dolphin/helpers/dolphin_state.c b/applications/dolphin/helpers/dolphin_state.c index bfd467b8..86287a2a 100644 --- a/applications/dolphin/helpers/dolphin_state.c +++ b/applications/dolphin/helpers/dolphin_state.c @@ -148,7 +148,7 @@ void dolphin_state_on_deed(DolphinState* dolphin_state, DolphinDeed deed) { dolphin_deed_get_app_limit(app) - dolphin_state->data.icounter_daily_limit[app]; uint8_t deed_weight = CLAMP(dolphin_deed_get_weight(deed), weight_limit, 0); - uint8_t xp_to_levelup = dolphin_state_xp_to_levelup(dolphin_state->data.icounter); + uint32_t xp_to_levelup = dolphin_state_xp_to_levelup(dolphin_state->data.icounter); if(xp_to_levelup) { deed_weight = MIN(xp_to_levelup, deed_weight); dolphin_state->data.icounter += deed_weight; diff --git a/applications/gpio/scenes/gpio_scene_usb_uart.c b/applications/gpio/scenes/gpio_scene_usb_uart.c index f8f473ee..aa41aaf9 100644 --- a/applications/gpio/scenes/gpio_scene_usb_uart.c +++ b/applications/gpio/scenes/gpio_scene_usb_uart.c @@ -33,7 +33,7 @@ void gpio_scene_usb_uart_on_enter(void* context) { gpio_usb_uart_set_callback(app->gpio_usb_uart, gpio_scene_usb_uart_callback, app); scene_manager_set_scene_state(app->scene_manager, GpioSceneUsbUart, 0); view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewUsbUart); - notification_message(app->notifications, &sequence_display_lock); + notification_message(app->notifications, &sequence_display_backlight_enforce_on); } bool gpio_scene_usb_uart_on_event(void* context, SceneManagerEvent event) { @@ -63,5 +63,5 @@ void gpio_scene_usb_uart_on_exit(void* context) { usb_uart_disable(app->usb_uart_bridge); free(scene_usb_uart); } - notification_message(app->notifications, &sequence_display_unlock); + notification_message(app->notifications, &sequence_display_backlight_enforce_auto); } diff --git a/applications/gpio/usb_uart_bridge.c b/applications/gpio/usb_uart_bridge.c index c33f97b1..8f27340e 100644 --- a/applications/gpio/usb_uart_bridge.c +++ b/applications/gpio/usb_uart_bridge.c @@ -3,6 +3,8 @@ #include #include #include "usb_cdc.h" +#include "cli/cli_vcp.h" +#include "cli/cli.h" #define USB_CDC_PKT_LEN CDC_DATA_SZ #define USB_UART_RX_BUF_SIZE (USB_CDC_PKT_LEN * 5) @@ -16,17 +18,16 @@ static const GpioPin* flow_pins[][2] = { }; typedef enum { - WorkerEvtReserved = (1 << 0), // Reserved for StreamBuffer internal event - WorkerEvtStop = (1 << 1), - WorkerEvtRxDone = (1 << 2), + WorkerEvtStop = (1 << 0), + WorkerEvtRxDone = (1 << 1), - WorkerEvtTxStop = (1 << 3), - WorkerEvtCdcRx = (1 << 4), + WorkerEvtTxStop = (1 << 2), + WorkerEvtCdcRx = (1 << 3), - WorkerEvtCfgChange = (1 << 5), + WorkerEvtCfgChange = (1 << 4), - WorkerEvtLineCfgSet = (1 << 6), - WorkerEvtCtrlLineSet = (1 << 7), + WorkerEvtLineCfgSet = (1 << 5), + WorkerEvtCtrlLineSet = (1 << 6), } WorkerEvtFlags; @@ -84,18 +85,29 @@ static void usb_uart_on_irq_cb(UartIrqEvent ev, uint8_t data, void* context) { static void usb_uart_vcp_init(UsbUartBridge* usb_uart, uint8_t vcp_ch) { furi_hal_usb_unlock(); + FURI_LOG_I("", "Init %d", vcp_ch); if(vcp_ch == 0) { + Cli* cli = furi_record_open("cli"); + cli_session_close(cli); + furi_record_close("cli"); furi_check(furi_hal_usb_set_config(&usb_cdc_single, NULL) == true); - furi_hal_vcp_disable(); } else { furi_check(furi_hal_usb_set_config(&usb_cdc_dual, NULL) == true); + Cli* cli = furi_record_open("cli"); + cli_session_open(cli, &cli_vcp); + furi_record_close("cli"); } furi_hal_cdc_set_callbacks(vcp_ch, (CdcCallbacks*)&cdc_cb, usb_uart); } static void usb_uart_vcp_deinit(UsbUartBridge* usb_uart, uint8_t vcp_ch) { furi_hal_cdc_set_callbacks(vcp_ch, NULL, NULL); - if(vcp_ch == 0) furi_hal_vcp_enable(); + FURI_LOG_I("", "Deinit %d", vcp_ch); + if(vcp_ch != 0) { + Cli* cli = furi_record_open("cli"); + cli_session_close(cli); + furi_record_close("cli"); + } } static void usb_uart_serial_init(UsbUartBridge* usb_uart, uint8_t uart_ch) { @@ -155,7 +167,6 @@ static int32_t usb_uart_worker(void* context) { furi_thread_set_context(usb_uart->tx_thread, usb_uart); furi_thread_set_callback(usb_uart->tx_thread, usb_uart_tx_thread); - FuriHalUsbInterface* usb_mode_prev = furi_hal_usb_get_config(); usb_uart_vcp_init(usb_uart, usb_uart->cfg.vcp_ch); usb_uart_serial_init(usb_uart, usb_uart->cfg.uart_ch); usb_uart_set_baudrate(usb_uart, usb_uart->cfg.baudrate); @@ -247,11 +258,9 @@ static int32_t usb_uart_worker(void* context) { usb_uart_update_ctrl_lines(usb_uart); } } - usb_uart_vcp_deinit(usb_uart, usb_uart->cfg.vcp_ch); usb_uart_serial_deinit(usb_uart, usb_uart->cfg.uart_ch); - furi_hal_usb_unlock(); - furi_hal_usb_set_config(usb_mode_prev, NULL); + if(usb_uart->cfg.flow_pins != 0) { furi_hal_gpio_init_simple(flow_pins[usb_uart->cfg.flow_pins - 1][0], GpioModeAnalog); furi_hal_gpio_init_simple(flow_pins[usb_uart->cfg.flow_pins - 1][1], GpioModeAnalog); @@ -265,6 +274,12 @@ static int32_t usb_uart_worker(void* context) { osMutexDelete(usb_uart->usb_mutex); osSemaphoreDelete(usb_uart->tx_sem); + furi_hal_usb_unlock(); + furi_check(furi_hal_usb_set_config(&usb_cdc_single, NULL) == true); + Cli* cli = furi_record_open("cli"); + cli_session_open(cli, &cli_vcp); + furi_record_close("cli"); + return 0; } diff --git a/applications/gui/canvas.c b/applications/gui/canvas.c index fe07bd5e..1e275aaf 100644 --- a/applications/gui/canvas.c +++ b/applications/gui/canvas.c @@ -17,8 +17,6 @@ const CanvasFontParameters canvas_font_params[FontTotalNumber] = { Canvas* canvas_init() { Canvas* canvas = malloc(sizeof(Canvas)); - furi_hal_power_insomnia_enter(); - // Setup u8g2 u8g2_Setup_st756x_flipper(&canvas->fb, U8G2_R0, u8x8_hw_spi_stm32, u8g2_gpio_and_delay_stm32); canvas->orientation = CanvasOrientationHorizontal; @@ -31,8 +29,6 @@ Canvas* canvas_init() { canvas_clear(canvas); canvas_commit(canvas); - furi_hal_power_insomnia_exit(); - return canvas; } diff --git a/applications/ibutton/ibutton_app.cpp b/applications/ibutton/ibutton_app.cpp index 61bbcffc..43eb3692 100644 --- a/applications/ibutton/ibutton_app.cpp +++ b/applications/ibutton/ibutton_app.cpp @@ -41,7 +41,6 @@ iButtonApp::iButtonApp() : notification{"notification"} , storage{"storage"} , dialogs{"dialogs"} { - furi_hal_power_insomnia_enter(); key = ibutton_key_alloc(); key_worker = ibutton_worker_alloc(); ibutton_worker_start_thread(key_worker); @@ -56,8 +55,6 @@ iButtonApp::~iButtonApp() { ibutton_worker_stop_thread(key_worker); ibutton_worker_free(key_worker); ibutton_key_free(key); - - furi_hal_power_insomnia_exit(); } iButtonAppViewManager* iButtonApp::get_view_manager() { @@ -133,18 +130,18 @@ uint8_t iButtonApp::get_file_name_size() { return file_name_size; } -void iButtonApp::notify_green_blink() { - notification_message(notification, &sequence_blink_green_10); +void iButtonApp::notify_read() { + notification_message(notification, &sequence_blink_cyan_10); +} + +void iButtonApp::notify_emulate() { + notification_message(notification, &sequence_blink_magenta_10); } void iButtonApp::notify_yellow_blink() { notification_message(notification, &sequence_blink_yellow_10); } -void iButtonApp::notify_red_blink() { - notification_message(notification, &sequence_blink_red_10); -} - void iButtonApp::notify_error() { notification_message(notification, &sequence_error); } @@ -342,4 +339,4 @@ void iButtonApp::make_app_folder() { if(!storage_simply_mkdir(storage, app_folder)) { dialog_message_show_storage_error(dialogs, "Cannot create\napp folder"); } -} \ No newline at end of file +} diff --git a/applications/ibutton/ibutton_app.h b/applications/ibutton/ibutton_app.h index 5b529a1e..d3d8cac6 100644 --- a/applications/ibutton/ibutton_app.h +++ b/applications/ibutton/ibutton_app.h @@ -75,9 +75,9 @@ public: iButtonWorker* get_key_worker(); iButtonKey* get_key(); - void notify_green_blink(); + void notify_read(); void notify_yellow_blink(); - void notify_red_blink(); + void notify_emulate(); void notify_error(); void notify_success(); diff --git a/applications/ibutton/scene/ibutton_scene_emulate.cpp b/applications/ibutton/scene/ibutton_scene_emulate.cpp index e1b08e84..da6021fe 100644 --- a/applications/ibutton/scene/ibutton_scene_emulate.cpp +++ b/applications/ibutton/scene/ibutton_scene_emulate.cpp @@ -83,7 +83,7 @@ bool iButtonSceneEmulate::on_event(iButtonApp* app, iButtonEvent* event) { app->notify_yellow_blink(); consumed = true; } else if(event->type == iButtonEvent::Type::EventTypeTick) { - app->notify_red_blink(); + app->notify_emulate(); consumed = true; } diff --git a/applications/ibutton/scene/ibutton_scene_read.cpp b/applications/ibutton/scene/ibutton_scene_read.cpp index 8a2abe61..7162652d 100644 --- a/applications/ibutton/scene/ibutton_scene_read.cpp +++ b/applications/ibutton/scene/ibutton_scene_read.cpp @@ -54,7 +54,7 @@ bool iButtonSceneRead::on_event(iButtonApp* app, iButtonEvent* event) { } } else if(event->type == iButtonEvent::Type::EventTypeTick) { consumed = true; - app->notify_red_blink(); + app->notify_read(); } return consumed; diff --git a/applications/ibutton/scene/ibutton_scene_write.cpp b/applications/ibutton/scene/ibutton_scene_write.cpp index 847e7f01..31da6dbe 100644 --- a/applications/ibutton/scene/ibutton_scene_write.cpp +++ b/applications/ibutton/scene/ibutton_scene_write.cpp @@ -95,7 +95,7 @@ bool iButtonSceneWrite::on_event(iButtonApp* app, iButtonEvent* event) { if(blink_yellow) { app->notify_yellow_blink(); } else { - app->notify_red_blink(); + app->notify_emulate(); } } diff --git a/applications/infrared/infrared_app.cpp b/applications/infrared/infrared_app.cpp index c9a7ee4f..5dc3b311 100644 --- a/applications/infrared/infrared_app.cpp +++ b/applications/infrared/infrared_app.cpp @@ -211,46 +211,12 @@ void InfraredApp::notify_success() { notification_message(notification, &sequence_success); } -void InfraredApp::notify_red_blink() { - notification_message(notification, &sequence_blink_red_10); +void InfraredApp::notify_blink_read() { + notification_message(notification, &sequence_blink_cyan_10); } -void InfraredApp::notify_click() { - static const NotificationSequence sequence = { - &message_click, - &message_delay_1, - &message_sound_off, - NULL, - }; - - notification_message_block(notification, &sequence); -} - -void InfraredApp::notify_click_and_green_blink() { - static const NotificationSequence sequence = { - &message_click, - &message_delay_1, - &message_sound_off, - &message_green_255, - &message_delay_10, - &message_green_0, - &message_do_not_reset, - NULL, - }; - - notification_message_block(notification, &sequence); -} - -void InfraredApp::notify_blink_green() { - static const NotificationSequence sequence = { - &message_green_255, - &message_delay_10, - &message_green_0, - &message_do_not_reset, - NULL, - }; - - notification_message(notification, &sequence); +void InfraredApp::notify_blink_send() { + notification_message(notification, &sequence_blink_magenta_10); } DialogsApp* InfraredApp::get_dialogs() { @@ -279,5 +245,5 @@ void InfraredApp::set_received_signal(const InfraredAppSignal& signal) { void InfraredApp::signal_sent_callback(void* context) { InfraredApp* app = static_cast(context); - app->notify_blink_green(); + app->notify_blink_send(); } diff --git a/applications/infrared/infrared_app.h b/applications/infrared/infrared_app.h index f1580402..e0db7c51 100644 --- a/applications/infrared/infrared_app.h +++ b/applications/infrared/infrared_app.h @@ -217,17 +217,13 @@ public: /** Play success notification */ void notify_success(); /** Play red blink notification */ - void notify_red_blink(); + void notify_blink_read(); /** Light green */ void notify_green_on(); /** Disable green light */ void notify_green_off(); - /** Play click sound */ - void notify_click(); - /** Play click and green notification */ - void notify_click_and_green_blink(); - /** Blink green light */ - void notify_blink_green(); + /** Blink on send */ + void notify_blink_send(); /** Get Dialogs instance */ DialogsApp* get_dialogs(); diff --git a/applications/infrared/scene/infrared_app_scene_learn.cpp b/applications/infrared/scene/infrared_app_scene_learn.cpp index 5ad783e7..3ad152cf 100644 --- a/applications/infrared/scene/infrared_app_scene_learn.cpp +++ b/applications/infrared/scene/infrared_app_scene_learn.cpp @@ -51,7 +51,7 @@ bool InfraredAppSceneLearn::on_event(InfraredApp* app, InfraredAppEvent* event) switch(event->type) { case InfraredAppEvent::Type::Tick: consumed = true; - app->notify_red_blink(); + app->notify_blink_read(); break; case InfraredAppEvent::Type::InfraredMessageReceived: app->notify_success(); diff --git a/applications/infrared/scene/infrared_app_scene_learn_success.cpp b/applications/infrared/scene/infrared_app_scene_learn_success.cpp index b620a5ed..a7f7c4cd 100644 --- a/applications/infrared/scene/infrared_app_scene_learn_success.cpp +++ b/applications/infrared/scene/infrared_app_scene_learn_success.cpp @@ -95,7 +95,6 @@ bool InfraredAppSceneLearnSuccess::on_event(InfraredApp* app, InfraredAppEvent* case DialogExPressCenter: if(!button_pressed) { button_pressed = true; - app->notify_click_and_green_blink(); auto signal = app->get_received_signal(); if(signal.is_raw()) { diff --git a/applications/infrared/scene/infrared_app_scene_remote.cpp b/applications/infrared/scene/infrared_app_scene_remote.cpp index b99f1c98..ac87b581 100644 --- a/applications/infrared/scene/infrared_app_scene_remote.cpp +++ b/applications/infrared/scene/infrared_app_scene_remote.cpp @@ -72,14 +72,12 @@ bool InfraredAppSceneRemote::on_event(InfraredApp* app, InfraredAppEvent* event) switch(event->payload.menu_index) { case ButtonIndexPlus: furi_assert(event->type == InfraredAppEvent::Type::MenuSelected); - app->notify_click(); buttonmenu_item_selected = event->payload.menu_index; app->set_learn_new_remote(false); app->switch_to_next_scene(InfraredApp::Scene::Learn); break; case ButtonIndexEdit: furi_assert(event->type == InfraredAppEvent::Type::MenuSelected); - app->notify_click(); buttonmenu_item_selected = event->payload.menu_index; app->switch_to_next_scene(InfraredApp::Scene::Edit); break; @@ -89,7 +87,6 @@ bool InfraredAppSceneRemote::on_event(InfraredApp* app, InfraredAppEvent* event) if(pressed && !button_pressed) { button_pressed = true; - app->notify_click_and_green_blink(); auto button_signal = app->get_remote_manager()->get_button_data(event->payload.menu_index); diff --git a/applications/infrared/scene/infrared_app_scene_universal_common.cpp b/applications/infrared/scene/infrared_app_scene_universal_common.cpp index 459401d3..a9220eaf 100644 --- a/applications/infrared/scene/infrared_app_scene_universal_common.cpp +++ b/applications/infrared/scene/infrared_app_scene_universal_common.cpp @@ -56,6 +56,7 @@ bool InfraredAppSceneUniversalCommon::on_event(InfraredApp* app, InfraredAppEven if(brute_force_started) { if(event->type == InfraredAppEvent::Type::Tick) { auto view_manager = app->get_view_manager(); + app->notify_blink_send(); InfraredAppEvent tick_event = {.type = InfraredAppEvent::Type::Tick}; view_manager->send_event(&tick_event); bool result = brute_force.send_next_bruteforce(); @@ -81,6 +82,7 @@ bool InfraredAppSceneUniversalCommon::on_event(InfraredApp* app, InfraredAppEven DOLPHIN_DEED(DolphinDeedIrBruteForce); brute_force_started = true; show_popup(app, record_amount); + app->notify_blink_send(); } else { app->switch_to_previous_scene(); } diff --git a/applications/lfrfid/lfrfid_app.cpp b/applications/lfrfid/lfrfid_app.cpp index 4653eb2d..c2274234 100644 --- a/applications/lfrfid/lfrfid_app.cpp +++ b/applications/lfrfid/lfrfid_app.cpp @@ -31,11 +31,9 @@ LfRfidApp::LfRfidApp() , storage{"storage"} , dialogs{"dialogs"} , text_store(40) { - furi_hal_power_insomnia_enter(); } LfRfidApp::~LfRfidApp() { - furi_hal_power_insomnia_exit(); } void LfRfidApp::run(void* _args) { @@ -201,4 +199,4 @@ void LfRfidApp::make_app_folder() { if(!storage_simply_mkdir(storage, app_folder)) { dialog_message_show_storage_error(dialogs, "Cannot create\napp folder"); } -} \ No newline at end of file +} diff --git a/applications/lfrfid/scene/lfrfid_app_scene_emulate.cpp b/applications/lfrfid/scene/lfrfid_app_scene_emulate.cpp index a46286d0..17472e16 100644 --- a/applications/lfrfid/scene/lfrfid_app_scene_emulate.cpp +++ b/applications/lfrfid/scene/lfrfid_app_scene_emulate.cpp @@ -29,7 +29,7 @@ bool LfRfidAppSceneEmulate::on_event(LfRfidApp* app, LfRfidApp::Event* event) { bool consumed = false; if(event->type == LfRfidApp::EventType::Tick) { - notification_message(app->notification, &sequence_blink_cyan_10); + notification_message(app->notification, &sequence_blink_magenta_10); } return consumed; diff --git a/applications/lfrfid/scene/lfrfid_app_scene_read.cpp b/applications/lfrfid/scene/lfrfid_app_scene_read.cpp index f5608fe4..48175590 100644 --- a/applications/lfrfid/scene/lfrfid_app_scene_read.cpp +++ b/applications/lfrfid/scene/lfrfid_app_scene_read.cpp @@ -24,9 +24,9 @@ bool LfRfidAppSceneRead::on_event(LfRfidApp* app, LfRfidApp::Event* event) { if(app->worker.any_read()) { notification_message(app->notification, &sequence_blink_green_10); } else if(app->worker.detect()) { - notification_message(app->notification, &sequence_blink_blue_10); + notification_message(app->notification, &sequence_blink_cyan_10); } else { - notification_message(app->notification, &sequence_blink_red_10); + notification_message(app->notification, &sequence_blink_cyan_10); } } } diff --git a/applications/lfrfid/scene/lfrfid_app_scene_write.cpp b/applications/lfrfid/scene/lfrfid_app_scene_write.cpp index b7efe8da..24f9fd0c 100644 --- a/applications/lfrfid/scene/lfrfid_app_scene_write.cpp +++ b/applications/lfrfid/scene/lfrfid_app_scene_write.cpp @@ -32,7 +32,7 @@ bool LfRfidAppSceneWrite::on_event(LfRfidApp* app, LfRfidApp::Event* event) { switch(result) { case RfidWorker::WriteResult::Nothing: - notification_message(app->notification, &sequence_blink_yellow_10); + notification_message(app->notification, &sequence_blink_magenta_10); break; case RfidWorker::WriteResult::Ok: notification_message(app->notification, &sequence_success); @@ -51,7 +51,7 @@ bool LfRfidAppSceneWrite::on_event(LfRfidApp* app, LfRfidApp::Event* event) { AlignTop); card_not_supported = true; } - notification_message(app->notification, &sequence_blink_red_10); + notification_message(app->notification, &sequence_blink_yellow_10); break; } } diff --git a/applications/loader/loader.c b/applications/loader/loader.c index 3882647e..43e275ab 100644 --- a/applications/loader/loader.c +++ b/applications/loader/loader.c @@ -238,7 +238,10 @@ static void loader_thread_state_callback(FuriThreadState thread_state, void* con if(thread_state == FuriThreadStateRunning) { event.type = LoaderEventTypeApplicationStarted; furi_pubsub_publish(loader_instance->pubsub, &event); - furi_hal_power_insomnia_enter(); + + if(!loader_instance->application->flags & FlipperApplicationFlagInsomniaSafe) { + furi_hal_power_insomnia_enter(); + } } else if(thread_state == FuriThreadStateStopped) { FURI_LOG_I( TAG, @@ -251,7 +254,9 @@ static void loader_thread_state_callback(FuriThreadState thread_state, void* con loader_instance->application_arguments = NULL; } - furi_hal_power_insomnia_exit(); + if(!loader_instance->application->flags & FlipperApplicationFlagInsomniaSafe) { + furi_hal_power_insomnia_exit(); + } loader_unlock(instance); event.type = LoaderEventTypeApplicationStopped; diff --git a/applications/nfc/nfc_device.c b/applications/nfc/nfc_device.c index c3e6bdd4..6b769803 100644 --- a/applications/nfc/nfc_device.c +++ b/applications/nfc/nfc_device.c @@ -262,6 +262,7 @@ static bool nfc_device_save_mifare_df_app(FlipperFormat* file, MifareDesfireAppl file, app->key_settings, string_get_cstr(prefix))) break; } + if(!app->file_head) break; uint32_t n_files = 0; for(MifareDesfireFile* f = app->file_head; f; f = f->next) { n_files++; @@ -477,6 +478,7 @@ static bool nfc_device_save_mifare_df_data(FlipperFormat* file, NfcDevice* dev) n_apps++; } if(!flipper_format_write_uint32(file, "Application Count", &n_apps, 1)) break; + if(n_apps == 0) break; tmp = malloc(n_apps * 3); int i = 0; for(MifareDesfireApplication* app = data->app_head; app; app = app->next) { diff --git a/applications/nfc/nfc_worker.c b/applications/nfc/nfc_worker.c index 13248394..6b3c8f09 100644 --- a/applications/nfc/nfc_worker.c +++ b/applications/nfc/nfc_worker.c @@ -86,7 +86,6 @@ void nfc_worker_change_state(NfcWorker* nfc_worker, NfcWorkerState state) { int32_t nfc_worker_task(void* context) { NfcWorker* nfc_worker = context; - furi_hal_power_insomnia_enter(); furi_hal_nfc_exit_sleep(); if(nfc_worker->state == NfcWorkerStateDetect) { @@ -110,7 +109,6 @@ int32_t nfc_worker_task(void* context) { } furi_hal_nfc_sleep(); nfc_worker_change_state(nfc_worker, NfcWorkerStateReady); - furi_hal_power_insomnia_exit(); return 0; } @@ -540,6 +538,7 @@ void nfc_worker_read_mifare_desfire(NfcWorker* nfc_worker) { FURI_LOG_W(TAG, "Bad DESFire GET_KEY_SETTINGS response"); free(data->master_key_settings); data->master_key_settings = NULL; + continue; } MifareDesfireKeyVersion** key_version_head = @@ -593,6 +592,7 @@ void nfc_worker_read_mifare_desfire(NfcWorker* nfc_worker) { FURI_LOG_W(TAG, "Bad DESFire GET_KEY_SETTINGS response"); free(app->key_settings); app->key_settings = NULL; + continue; } MifareDesfireKeyVersion** key_version_head = &app->key_settings->key_version_head; diff --git a/applications/notification/notification.h b/applications/notification/notification.h index b73efad2..4cb9dc5e 100644 --- a/applications/notification/notification.h +++ b/applications/notification/notification.h @@ -50,9 +50,9 @@ typedef enum { NotificationMessageTypeDelay, - NotificationMessageTypeLedDisplay, - NotificationMessageTypeLedDisplayLock, - NotificationMessageTypeLedDisplayUnlock, + NotificationMessageTypeLedDisplayBacklight, + NotificationMessageTypeLedDisplayBacklightEnforceOn, + NotificationMessageTypeLedDisplayBacklightEnforceAuto, NotificationMessageTypeDoNotReset, diff --git a/applications/notification/notification_app.c b/applications/notification/notification_app.c index 75be20b1..4db12bbf 100644 --- a/applications/notification/notification_app.c +++ b/applications/notification/notification_app.c @@ -152,7 +152,7 @@ void notification_sound_off() { static void notification_display_timer(void* ctx) { furi_assert(ctx); NotificationApp* app = ctx; - notification_message(app, &sequence_display_off); + notification_message(app, &sequence_display_backlight_off); } // message processing @@ -174,7 +174,7 @@ void notification_process_notification_message( while(notification_message != NULL) { switch(notification_message->type) { - case NotificationMessageTypeLedDisplay: + case NotificationMessageTypeLedDisplayBacklight: // if on - switch on and start timer // if off - switch off and stop timer // on timer - switch off @@ -190,7 +190,7 @@ void notification_process_notification_message( } reset_mask |= reset_display_mask; break; - case NotificationMessageTypeLedDisplayLock: + case NotificationMessageTypeLedDisplayBacklightEnforceOn: furi_assert(app->display_led_lock < UINT8_MAX); app->display_led_lock++; if(app->display_led_lock == 1) { @@ -199,7 +199,7 @@ void notification_process_notification_message( notification_message->data.led.value * display_brightness_setting); } break; - case NotificationMessageTypeLedDisplayUnlock: + case NotificationMessageTypeLedDisplayBacklightEnforceAuto: furi_assert(app->display_led_lock > 0); app->display_led_lock--; if(app->display_led_lock == 0) { @@ -322,7 +322,7 @@ void notification_process_internal_message(NotificationApp* app, NotificationApp while(notification_message != NULL) { switch(notification_message->type) { - case NotificationMessageTypeLedDisplay: + case NotificationMessageTypeLedDisplayBacklight: notification_apply_internal_led_layer( &app->display, notification_settings_get_display_brightness( @@ -442,7 +442,7 @@ static void input_event_callback(const void* value, void* context) { furi_assert(value); furi_assert(context); NotificationApp* app = context; - notification_message(app, &sequence_display_on); + notification_message(app, &sequence_display_backlight_on); } // App alloc @@ -482,7 +482,7 @@ static NotificationApp* notification_app_alloc() { // display backlight control app->event_record = furi_record_open("input_events"); furi_pubsub_subscribe(app->event_record, input_event_callback, app); - notification_message(app, &sequence_display_on); + notification_message(app, &sequence_display_backlight_on); return app; }; diff --git a/applications/notification/notification_messages.c b/applications/notification/notification_messages.c index 733b7f9c..5a95556e 100644 --- a/applications/notification/notification_messages.c +++ b/applications/notification/notification_messages.c @@ -4,24 +4,27 @@ /*********************************** Messages **********************************/ -// Display -const NotificationMessage message_display_on = { - .type = NotificationMessageTypeLedDisplay, +/** Display: backlight wakeup */ +const NotificationMessage message_display_backlight_on = { + .type = NotificationMessageTypeLedDisplayBacklight, .data.led.value = 0xFF, }; -const NotificationMessage message_display_off = { - .type = NotificationMessageTypeLedDisplay, +/** Display: backlight force off */ +const NotificationMessage message_display_backlight_off = { + .type = NotificationMessageTypeLedDisplayBacklight, .data.led.value = 0x00, }; -const NotificationMessage message_display_lock = { - .type = NotificationMessageTypeLedDisplayLock, +/** Display: backlight always on */ +const NotificationMessage message_display_backlight_enforce_on = { + .type = NotificationMessageTypeLedDisplayBacklightEnforceOn, .data.led.value = 0xFF, }; -const NotificationMessage message_display_unlock = { - .type = NotificationMessageTypeLedDisplayUnlock, +/** Display: automatic backlight management, with configured timeout */ +const NotificationMessage message_display_backlight_enforce_auto = { + .type = NotificationMessageTypeLedDisplayBacklightEnforceAuto, .data.led.value = 0x00, }; @@ -166,7 +169,7 @@ const NotificationSequence sequence_reset_rgb = { }; const NotificationSequence sequence_reset_display = { - &message_display_off, + &message_display_backlight_off, NULL, }; @@ -188,29 +191,31 @@ const NotificationSequence sequence_set_vibro_on = { }; // Display -const NotificationSequence sequence_display_on = { - &message_display_on, +const NotificationSequence sequence_display_backlight_on = { + &message_display_backlight_on, NULL, }; -const NotificationSequence sequence_display_off = { - &message_display_off, +const NotificationSequence sequence_display_backlight_off = { + &message_display_backlight_off, NULL, }; -const NotificationSequence sequence_display_lock = { - &message_display_lock, +/** Display: backlight always on lock */ +const NotificationSequence sequence_display_backlight_enforce_on = { + &message_display_backlight_enforce_on, NULL, }; -const NotificationSequence sequence_display_unlock = { - &message_display_unlock, +/** Display: backlight always on unlock */ +const NotificationSequence sequence_display_backlight_enforce_auto = { + &message_display_backlight_enforce_auto, NULL, }; -const NotificationSequence sequence_display_off_delay_1000 = { +const NotificationSequence sequence_display_backlight_off_delay_1000 = { &message_delay_1000, - &message_display_off, + &message_display_backlight_off, NULL, }; @@ -309,6 +314,13 @@ const NotificationSequence sequence_blink_cyan_10 = { NULL, }; +const NotificationSequence sequence_blink_magenta_10 = { + &message_red_255, + &message_blue_255, + &message_delay_10, + NULL, +}; + const NotificationSequence sequence_blink_red_100 = { &message_red_255, &message_delay_100, @@ -376,7 +388,7 @@ const NotificationSequence sequence_double_vibro = { }; const NotificationSequence sequence_success = { - &message_display_on, + &message_display_backlight_on, &message_green_255, &message_vibro_on, &message_note_c5, @@ -393,7 +405,7 @@ const NotificationSequence sequence_success = { }; const NotificationSequence sequence_error = { - &message_display_on, + &message_display_backlight_on, &message_red_255, &message_vibro_on, &message_note_c5, @@ -415,27 +427,27 @@ const NotificationSequence sequence_audiovisual_alert = { &message_force_display_brightness_setting_1f, &message_vibro_on, - &message_display_on, + &message_display_backlight_on, &message_note_c7, &message_delay_250, - &message_display_off, + &message_display_backlight_off, &message_note_c4, &message_delay_250, - &message_display_on, + &message_display_backlight_on, &message_note_c7, &message_delay_250, - &message_display_off, + &message_display_backlight_off, &message_note_c4, &message_delay_250, - &message_display_on, + &message_display_backlight_on, &message_note_c7, &message_delay_250, - &message_display_off, + &message_display_backlight_off, &message_note_c4, &message_delay_250, diff --git a/applications/notification/notification_messages.h b/applications/notification/notification_messages.h index b9b0d72d..c1ab3407 100644 --- a/applications/notification/notification_messages.h +++ b/applications/notification/notification_messages.h @@ -9,15 +9,10 @@ extern "C" { /*********************************** Messages **********************************/ // Display - -/** Display: backlight wakeup */ -extern const NotificationMessage message_display_on; -/** Display: backlight force off */ -extern const NotificationMessage message_display_off; -/** Display: backlight always on lock */ -extern const NotificationMessage message_display_lock; -/** Display: backlight always on unlock */ -extern const NotificationMessage message_display_unlock; +extern const NotificationMessage message_display_backlight_on; +extern const NotificationMessage message_display_backlight_off; +extern const NotificationMessage message_display_backlight_enforce_on; +extern const NotificationMessage message_display_backlight_enforce_auto; // Led ON extern const NotificationMessage message_red_255; @@ -71,15 +66,16 @@ extern const NotificationSequence sequence_set_vibro_on; // Display /** Display: backlight wakeup */ -extern const NotificationSequence sequence_display_on; +extern const NotificationSequence sequence_display_backlight_on; /** Display: backlight force off */ -extern const NotificationSequence sequence_display_off; -/** Display: backlight always on lock */ -extern const NotificationSequence sequence_display_lock; -/** Display: backlight always on unlock */ -extern const NotificationSequence sequence_display_unlock; +extern const NotificationSequence sequence_display_backlight_off; /** Display: backlight force off after a delay of 1000ms */ -extern const NotificationSequence sequence_display_off_delay_1000; +extern const NotificationSequence sequence_display_backlight_off_delay_1000; + +/** Display: backlight always on lock */ +extern const NotificationSequence sequence_display_backlight_enforce_on; +/** Display: backlight always on unlock */ +extern const NotificationSequence sequence_display_backlight_enforce_auto; // Charging extern const NotificationSequence sequence_charging; @@ -100,6 +96,7 @@ extern const NotificationSequence sequence_blink_red_10; extern const NotificationSequence sequence_blink_green_10; extern const NotificationSequence sequence_blink_yellow_10; extern const NotificationSequence sequence_blink_cyan_10; +extern const NotificationSequence sequence_blink_magenta_10; extern const NotificationSequence sequence_blink_red_100; extern const NotificationSequence sequence_blink_green_100; diff --git a/applications/notification/notification_settings_app.c b/applications/notification/notification_settings_app.c index e7a57aa6..eef14ebf 100644 --- a/applications/notification/notification_settings_app.c +++ b/applications/notification/notification_settings_app.c @@ -70,7 +70,7 @@ static void backlight_changed(VariableItem* item) { variable_item_set_current_value_text(item, backlight_text[index]); app->notification->settings.display_brightness = backlight_value[index]; - notification_message(app->notification, &sequence_display_on); + notification_message(app->notification, &sequence_display_backlight_on); } static void screen_changed(VariableItem* item) { @@ -79,7 +79,7 @@ static void screen_changed(VariableItem* item) { variable_item_set_current_value_text(item, delay_text[index]); app->notification->settings.display_off_delay_ms = delay_value[index]; - notification_message(app->notification, &sequence_display_on); + notification_message(app->notification, &sequence_display_backlight_on); } const NotificationMessage apply_message = { diff --git a/applications/power/battery_test_app/battery_test_app.c b/applications/power/battery_test_app/battery_test_app.c index 4d73fee5..1588a525 100755 --- a/applications/power/battery_test_app/battery_test_app.c +++ b/applications/power/battery_test_app/battery_test_app.c @@ -28,7 +28,7 @@ static void battery_test_battery_info_update_model(void* context) { .health = app->info.health, }; battery_info_set_data(app->batery_info, &battery_info_data); - notification_message(app->notifications, &sequence_display_on); + notification_message(app->notifications, &sequence_display_backlight_on); } BatteryTestApp* battery_test_alloc() { diff --git a/applications/rpc/rpc_cli.c b/applications/rpc/rpc_cli.c index fd1f6e7f..b9304367 100644 --- a/applications/rpc/rpc_cli.c +++ b/applications/rpc/rpc_cli.c @@ -62,8 +62,8 @@ void rpc_cli_command_start_session(Cli* cli, string_t args, void* context) { 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) { + size_received = cli_read_timeout(cli_rpc.cli, buffer, CLI_READ_BUFFER_SIZE, 50); + if(!cli_is_connected(cli_rpc.cli) || cli_rpc.session_close_request) { break; } diff --git a/applications/storage/filesystem_api_internal.h b/applications/storage/filesystem_api_internal.h index f0a3bb3f..bd4bcf82 100644 --- a/applications/storage/filesystem_api_internal.h +++ b/applications/storage/filesystem_api_internal.h @@ -6,9 +6,17 @@ extern "C" { #endif +/** File type */ +typedef enum { + FileTypeClosed, /**< Closed file */ + FileTypeOpenDir, /**< Open dir */ + FileTypeOpenFile, /**< Open file */ +} FileType; + /** Structure that hold file index and returned api errors */ struct File { uint32_t file_id; /**< File ID for internal references */ + FileType type; FS_Error error_id; /**< Standart API error from FS_Error enum */ int32_t internal_error_id; /**< Internal API error value */ void* storage; diff --git a/applications/storage/storage_external_api.c b/applications/storage/storage_external_api.c index 8e39c08b..1335c708 100644 --- a/applications/storage/storage_external_api.c +++ b/applications/storage/storage_external_api.c @@ -7,6 +7,8 @@ #define MAX_NAME_LENGTH 256 +#define TAG "StorageAPI" + #define S_API_PROLOGUE \ osSemaphoreId_t semaphore = osSemaphoreNew(1, 0, NULL); \ furi_check(semaphore != NULL); @@ -47,10 +49,6 @@ #define S_RETURN_ERROR (return_data.error_value); #define S_RETURN_CSTRING (return_data.cstring_value); -#define FILE_OPENED_FILE 1 -#define FILE_OPENED_DIR 2 -#define FILE_CLOSED 0 - typedef enum { StorageEventFlagFileClose = (1 << 0), } StorageEventFlag; @@ -72,7 +70,7 @@ static bool storage_file_open_internal( .open_mode = open_mode, }}; - file->file_id = FILE_OPENED_FILE; + file->type = FileTypeOpenFile; S_API_MESSAGE(StorageCommandFileOpen); S_API_EPILOGUE; @@ -113,6 +111,10 @@ bool storage_file_open( furi_pubsub_unsubscribe(storage_get_pubsub(file->storage), subscription); osEventFlagsDelete(event); + + FURI_LOG_T( + TAG, "File %p - %p open (%s)", (uint32_t)file - SRAM_BASE, file->file_id - SRAM_BASE, path); + return result; } @@ -124,7 +126,8 @@ bool storage_file_close(File* file) { S_API_MESSAGE(StorageCommandFileClose); S_API_EPILOGUE; - file->file_id = FILE_CLOSED; + FURI_LOG_T(TAG, "File %p - %p closed", (uint32_t)file - SRAM_BASE, file->file_id - SRAM_BASE); + file->type = FileTypeClosed; return S_RETURN_BOOL; } @@ -234,7 +237,7 @@ static bool storage_dir_open_internal(File* file, const char* path) { .path = path, }}; - file->file_id = FILE_OPENED_DIR; + file->type = FileTypeOpenDir; S_API_MESSAGE(StorageCommandDirOpen); S_API_EPILOGUE; @@ -259,6 +262,10 @@ bool storage_dir_open(File* file, const char* path) { furi_pubsub_unsubscribe(storage_get_pubsub(file->storage), subscription); osEventFlagsDelete(event); + + FURI_LOG_T( + TAG, "Dir %p - %p open (%s)", (uint32_t)file - SRAM_BASE, file->file_id - SRAM_BASE, path); + return result; } @@ -269,7 +276,9 @@ bool storage_dir_close(File* file) { S_API_MESSAGE(StorageCommandDirClose); S_API_EPILOGUE; - file->file_id = FILE_CLOSED; + FURI_LOG_T(TAG, "Dir %p - %p closed", (uint32_t)file - SRAM_BASE, file->file_id - SRAM_BASE); + + file->type = FileTypeClosed; return S_RETURN_BOOL; } @@ -448,18 +457,20 @@ FS_Error storage_sd_status(Storage* storage) { File* storage_file_alloc(Storage* storage) { File* file = malloc(sizeof(File)); - file->file_id = FILE_CLOSED; + file->type = FileTypeClosed; file->storage = storage; + FURI_LOG_T(TAG, "File/Dir %p alloc", (uint32_t)file - SRAM_BASE); + return file; } bool storage_file_is_open(File* file) { - return (file->file_id != FILE_CLOSED); + return (file->type != FileTypeClosed); } bool storage_file_is_dir(File* file) { - return (file->file_id == FILE_OPENED_DIR); + return (file->type == FileTypeOpenDir); } void storage_file_free(File* file) { @@ -471,6 +482,7 @@ void storage_file_free(File* file) { } } + FURI_LOG_T(TAG, "File/Dir %p free", (uint32_t)file - SRAM_BASE); free(file); } diff --git a/applications/storage/storage_glue.c b/applications/storage/storage_glue.c index 7ce8ec2c..09656ae3 100644 --- a/applications/storage/storage_glue.c +++ b/applications/storage/storage_glue.c @@ -39,12 +39,10 @@ void storage_data_init(StorageData* storage) { } bool storage_data_lock(StorageData* storage) { - furi_hal_power_insomnia_enter(); return (osMutexAcquire(storage->mutex, osWaitForever) == osOK); } bool storage_data_unlock(StorageData* storage) { - furi_hal_power_insomnia_exit(); return (osMutexRelease(storage->mutex) == osOK); } diff --git a/applications/subghz/helpers/subghz_chat.c b/applications/subghz/helpers/subghz_chat.c index 30618813..e6b7b7bc 100644 --- a/applications/subghz/helpers/subghz_chat.c +++ b/applications/subghz/helpers/subghz_chat.c @@ -12,6 +12,8 @@ struct SubGhzChatWorker { volatile bool worker_stoping; osMessageQueueId_t event_queue; uint32_t last_time_rx_data; + + Cli* cli; }; /** Worker thread @@ -27,7 +29,7 @@ static int32_t subghz_chat_worker_thread(void* context) { event.event = SubGhzChatEventUserEntrance; osMessageQueuePut(instance->event_queue, &event, 0, 0); while(instance->worker_running) { - if(furi_hal_vcp_rx_with_timeout((uint8_t*)&c, 1, 1000) == 1) { + if(cli_read_timeout(instance->cli, (uint8_t*)&c, 1, 1000) == 1) { event.event = SubGhzChatEventInputData; event.c = c; osMessageQueuePut(instance->event_queue, &event, 0, osWaitForever); @@ -52,9 +54,11 @@ static void subghz_chat_worker_update_rx_event_chat(void* context) { osMessageQueuePut(instance->event_queue, &event, 0, osWaitForever); } -SubGhzChatWorker* subghz_chat_worker_alloc() { +SubGhzChatWorker* subghz_chat_worker_alloc(Cli* cli) { SubGhzChatWorker* instance = malloc(sizeof(SubGhzChatWorker)); + instance->cli = cli; + instance->thread = furi_thread_alloc(); furi_thread_set_name(instance->thread, "SubGhzChat"); furi_thread_set_stack_size(instance->thread, 2048); diff --git a/applications/subghz/helpers/subghz_chat.h b/applications/subghz/helpers/subghz_chat.h index 6556d0c3..b418bbdb 100644 --- a/applications/subghz/helpers/subghz_chat.h +++ b/applications/subghz/helpers/subghz_chat.h @@ -1,5 +1,6 @@ #pragma once #include "../subghz_i.h" +#include typedef struct SubGhzChatWorker SubGhzChatWorker; @@ -17,7 +18,7 @@ typedef struct { char c; } SubGhzChatEvent; -SubGhzChatWorker* subghz_chat_worker_alloc(); +SubGhzChatWorker* subghz_chat_worker_alloc(Cli* cli); void subghz_chat_worker_free(SubGhzChatWorker* instance); bool subghz_chat_worker_start(SubGhzChatWorker* instance, uint32_t frequency); void subghz_chat_worker_stop(SubGhzChatWorker* instance); diff --git a/applications/subghz/helpers/subghz_custom_event.h b/applications/subghz/helpers/subghz_custom_event.h index 97920015..ad70be7e 100644 --- a/applications/subghz/helpers/subghz_custom_event.h +++ b/applications/subghz/helpers/subghz_custom_event.h @@ -19,6 +19,8 @@ typedef enum { SubGhzCustomEventSceneShowErrorOk, SubGhzCustomEventSceneShowErrorSub, SubGhzCustomEventSceneShowOnlyRX, + SubGhzCustomEventSceneAnalyzerLock, + SubGhzCustomEventSceneAnalyzerUnlock, SubGhzCustomEventSceneExit, SubGhzCustomEventSceneStay, @@ -35,7 +37,6 @@ typedef enum { SubGhzCustomEventViewReadRAWSendStart, SubGhzCustomEventViewReadRAWSendStop, SubGhzCustomEventViewReadRAWSave, - SubGhzCustomEventViewReadRAWVibro, SubGhzCustomEventViewReadRAWTXRXStop, SubGhzCustomEventViewReadRAWMore, diff --git a/applications/subghz/scenes/subghz_scene_frequency_analyzer.c b/applications/subghz/scenes/subghz_scene_frequency_analyzer.c index 49143b35..b42acd4d 100644 --- a/applications/subghz/scenes/subghz_scene_frequency_analyzer.c +++ b/applications/subghz/scenes/subghz_scene_frequency_analyzer.c @@ -17,10 +17,20 @@ void subghz_scene_frequency_analyzer_on_enter(void* context) { } bool subghz_scene_frequency_analyzer_on_event(void* context, SceneManagerEvent event) { - //SubGhz* subghz = context; + SubGhz* subghz = context; + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubGhzCustomEventSceneAnalyzerLock) { + notification_message(subghz->notifications, &sequence_set_green_255); + return true; + } else if(event.event == SubGhzCustomEventSceneAnalyzerUnlock) { + notification_message(subghz->notifications, &sequence_reset_rgb); + return true; + } + } return false; } void subghz_scene_frequency_analyzer_on_exit(void* context) { - // SubGhz* subghz = context; + SubGhz* subghz = context; + notification_message(subghz->notifications, &sequence_reset_rgb); } diff --git a/applications/subghz/scenes/subghz_scene_read_raw.c b/applications/subghz/scenes/subghz_scene_read_raw.c index 257b3f0c..88d0686b 100644 --- a/applications/subghz/scenes/subghz_scene_read_raw.c +++ b/applications/subghz/scenes/subghz_scene_read_raw.c @@ -168,11 +168,7 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { case SubGhzCustomEventViewReadRAWErase: subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; - return true; - break; - - case SubGhzCustomEventViewReadRAWVibro: - notification_message(subghz->notifications, &sequence_single_vibro); + notification_message(subghz->notifications, &sequence_reset_rgb); return true; break; @@ -209,7 +205,7 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { (SubGhzProtocolEncoderRAW*)subghz->txrx->transmitter->protocol_instance, subghz_scene_read_raw_callback_end_tx, subghz); - subghz->state_notifications = SubGhzNotificationStateTX; + subghz->state_notifications = SubGhzNotificationStateTx; } } } @@ -231,6 +227,10 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { subghz_rx_end(subghz); subghz_sleep(subghz); }; + + size_t spl_count = subghz_protocol_raw_get_sample_write( + (SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result); + subghz_protocol_raw_save_to_file_stop( (SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result); @@ -241,6 +241,12 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { subghz_protocol_raw_gen_fff_data(subghz->txrx->fff_data, string_get_cstr(temp_str)); string_clear(temp_str); + if(spl_count > 0) { + notification_message(subghz->notifications, &sequence_set_green_255); + } else { + notification_message(subghz->notifications, &sequence_reset_rgb); + } + subghz->state_notifications = SubGhzNotificationStateIDLE; subghz->txrx->rx_key_state = SubGhzRxKeyStateAddKey; @@ -263,7 +269,7 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { subghz_begin(subghz, subghz->txrx->preset); subghz_rx(subghz, subghz->txrx->frequency); } - subghz->state_notifications = SubGhzNotificationStateRX; + subghz->state_notifications = SubGhzNotificationStateRx; subghz->txrx->rx_key_state = SubGhzRxKeyStateAddKey; } else { string_set(subghz->error_str, "Function requires\nan SD card."); @@ -288,16 +294,16 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { } } else if(event.type == SceneManagerEventTypeTick) { switch(subghz->state_notifications) { - case SubGhzNotificationStateRX: - notification_message(subghz->notifications, &sequence_blink_blue_10); + case SubGhzNotificationStateRx: + notification_message(subghz->notifications, &sequence_blink_cyan_10); subghz_read_raw_update_sample_write( subghz->subghz_read_raw, subghz_protocol_raw_get_sample_write( (SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result)); subghz_read_raw_add_data_rssi(subghz->subghz_read_raw, furi_hal_subghz_get_rssi()); break; - case SubGhzNotificationStateTX: - notification_message(subghz->notifications, &sequence_blink_green_10); + case SubGhzNotificationStateTx: + notification_message(subghz->notifications, &sequence_blink_magenta_10); subghz_read_raw_update_sin(subghz->subghz_read_raw); break; default: @@ -316,6 +322,7 @@ void subghz_scene_read_raw_on_exit(void* context) { subghz_sleep(subghz); }; subghz->state_notifications = SubGhzNotificationStateIDLE; + notification_message(subghz->notifications, &sequence_reset_rgb); //filter restoration subghz_receiver_set_filter(subghz->txrx->receiver, SubGhzProtocolFlag_Decodable); diff --git a/applications/subghz/scenes/subghz_scene_receiver.c b/applications/subghz/scenes/subghz_scene_receiver.c index 0fb07352..19242def 100644 --- a/applications/subghz/scenes/subghz_scene_receiver.c +++ b/applications/subghz/scenes/subghz_scene_receiver.c @@ -1,6 +1,19 @@ #include "../subghz_i.h" #include "../views/receiver.h" +static const NotificationSequence subghs_sequence_rx = { + &message_green_255, + + &message_vibro_on, + &message_note_c6, + &message_delay_50, + &message_sound_off, + &message_vibro_off, + + &message_delay_50, + NULL, +}; + static void subghz_scene_receiver_update_statusbar(void* context) { SubGhz* subghz = context; string_t history_stat_str; @@ -50,6 +63,8 @@ static void subghz_scene_add_to_history_callback( subghz_receiver_reset(receiver); string_reset(str_buff); + subghz->state_notifications = SubGhzNotificationStateRxDone; + subghz_history_get_text_item_menu( subghz->txrx->history, str_buff, subghz_history_get_item(subghz->txrx->history) - 1); subghz_view_receiver_add_item_to_menu( @@ -95,7 +110,7 @@ void subghz_scene_receiver_on_enter(void* context) { subghz_receiver_set_rx_callback( subghz->txrx->receiver, subghz_scene_add_to_history_callback, subghz); - subghz->state_notifications = SubGhzNotificationStateRX; + subghz->state_notifications = SubGhzNotificationStateRx; if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { subghz_rx_end(subghz); }; @@ -161,8 +176,12 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) { } switch(subghz->state_notifications) { - case SubGhzNotificationStateRX: - notification_message(subghz->notifications, &sequence_blink_blue_10); + case SubGhzNotificationStateRx: + notification_message(subghz->notifications, &sequence_blink_cyan_10); + break; + case SubGhzNotificationStateRxDone: + notification_message(subghz->notifications, &subghs_sequence_rx); + subghz->state_notifications = SubGhzNotificationStateRx; break; default: break; diff --git a/applications/subghz/scenes/subghz_scene_receiver_info.c b/applications/subghz/scenes/subghz_scene_receiver_info.c index 3cd46558..31471184 100644 --- a/applications/subghz/scenes/subghz_scene_receiver_info.c +++ b/applications/subghz/scenes/subghz_scene_receiver_info.c @@ -126,7 +126,7 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event) subghz->txrx->history, subghz->txrx->idx_menu_chosen))) { scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx); } else { - subghz->state_notifications = SubGhzNotificationStateTX; + subghz->state_notifications = SubGhzNotificationStateTx; } } return true; @@ -143,7 +143,7 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event) if(subghz->txrx->hopper_state == SubGhzHopperStatePause) { subghz->txrx->hopper_state = SubGhzHopperStateRunnig; } - subghz->state_notifications = SubGhzNotificationStateRX; + subghz->state_notifications = SubGhzNotificationStateRx; return true; } else if(event.event == SubGhzCustomEventSceneReceiverInfoSave) { //CC1101 Stop RX -> Save @@ -171,11 +171,15 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event) subghz_hopper_update(subghz); } switch(subghz->state_notifications) { - case SubGhzNotificationStateTX: - notification_message(subghz->notifications, &sequence_blink_red_10); + case SubGhzNotificationStateTx: + notification_message(subghz->notifications, &sequence_blink_magenta_10); break; - case SubGhzNotificationStateRX: - notification_message(subghz->notifications, &sequence_blink_blue_10); + case SubGhzNotificationStateRx: + notification_message(subghz->notifications, &sequence_blink_cyan_10); + break; + case SubGhzNotificationStateRxDone: + notification_message(subghz->notifications, &sequence_blink_green_100); + subghz->state_notifications = SubGhzNotificationStateRx; break; default: break; diff --git a/applications/subghz/scenes/subghz_scene_set_type.c b/applications/subghz/scenes/subghz_scene_set_type.c index 22a15143..964b85b2 100644 --- a/applications/subghz/scenes/subghz_scene_set_type.c +++ b/applications/subghz/scenes/subghz_scene_set_type.c @@ -4,6 +4,7 @@ #include #include #include +#include #define TAG "SubGhzSetType" @@ -19,13 +20,16 @@ enum SubmenuIndex { SubmenuIndexGateTX, SubmenuIndexDoorHan_315_00, SubmenuIndexDoorHan_433_92, + SubmenuIndexFirefly_300_00, }; bool subghz_scene_set_type_submenu_gen_data_protocol( void* context, const char* protocol_name, uint64_t key, - uint32_t bit) { + uint32_t bit, + uint32_t frequency, + FuriHalSubGhzPreset preset) { furi_assert(context); SubGhz* subghz = context; @@ -44,10 +48,7 @@ bool subghz_scene_set_type_submenu_gen_data_protocol( Stream* fff_data_stream = flipper_format_get_raw_stream(subghz->txrx->fff_data); stream_clean(fff_data_stream); if(!subghz_protocol_decoder_base_serialize( - subghz->txrx->decoder_result, - subghz->txrx->fff_data, - subghz_setting_get_default_frequency(subghz->setting), - FuriHalSubGhzPresetOok650Async)) { + subghz->txrx->decoder_result, subghz->txrx->fff_data, frequency, preset)) { FURI_LOG_E(TAG, "Unable to serialize"); break; } @@ -107,6 +108,12 @@ void subghz_scene_set_type_on_enter(void* context) { SubmenuIndexCAME24bit, subghz_scene_set_type_submenu_callback, subghz); + submenu_add_item( + subghz->submenu, + "Firefly_300", + SubmenuIndexFirefly_300_00, + subghz_scene_set_type_submenu_callback, + subghz); submenu_add_item( subghz->submenu, "CAME TWEE", @@ -152,7 +159,13 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { switch(event.event) { case SubmenuIndexPricenton: key = (key & 0x00FFFFF0) | 0x4; //btn 0x1, 0x2, 0x4, 0x8 - if(subghz_scene_set_type_submenu_gen_data_protocol(subghz, "Princeton", key, 24)) { + if(subghz_scene_set_type_submenu_gen_data_protocol( + subghz, + SUBGHZ_PROTOCOL_PRINCETON_NAME, + key, + 24, + 433920000, + FuriHalSubGhzPresetOok650Async)) { uint32_t te = 400; flipper_format_update_uint32(subghz->txrx->fff_data, "TE", (uint32_t*)&te, 1); generated_protocol = true; @@ -160,32 +173,74 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { break; case SubmenuIndexNiceFlo12bit: key = (key & 0x0000FFF0) | 0x1; //btn 0x1, 0x2, 0x4 - if(subghz_scene_set_type_submenu_gen_data_protocol(subghz, "Nice FLO", key, 12)) { + if(subghz_scene_set_type_submenu_gen_data_protocol( + subghz, + SUBGHZ_PROTOCOL_NICE_FLO_NAME, + key, + 12, + 433920000, + FuriHalSubGhzPresetOok650Async)) { generated_protocol = true; } break; case SubmenuIndexNiceFlo24bit: key = (key & 0x00FFFFF0) | 0x4; //btn 0x1, 0x2, 0x4, 0x8 - if(subghz_scene_set_type_submenu_gen_data_protocol(subghz, "Nice FLO", key, 24)) { + if(subghz_scene_set_type_submenu_gen_data_protocol( + subghz, + SUBGHZ_PROTOCOL_NICE_FLO_NAME, + key, + 24, + 433920000, + FuriHalSubGhzPresetOok650Async)) { generated_protocol = true; } break; case SubmenuIndexCAME12bit: key = (key & 0x0000FFF0) | 0x1; //btn 0x1, 0x2, 0x4 - if(subghz_scene_set_type_submenu_gen_data_protocol(subghz, "CAME", key, 12)) { + if(subghz_scene_set_type_submenu_gen_data_protocol( + subghz, + SUBGHZ_PROTOCOL_CAME_NAME, + key, + 12, + 433920000, + FuriHalSubGhzPresetOok650Async)) { generated_protocol = true; } break; case SubmenuIndexCAME24bit: key = (key & 0x00FFFFF0) | 0x4; //btn 0x1, 0x2, 0x4, 0x8 - if(subghz_scene_set_type_submenu_gen_data_protocol(subghz, "CAME", key, 24)) { + if(subghz_scene_set_type_submenu_gen_data_protocol( + subghz, + SUBGHZ_PROTOCOL_CAME_NAME, + key, + 24, + 433920000, + FuriHalSubGhzPresetOok650Async)) { + generated_protocol = true; + } + break; + case SubmenuIndexFirefly_300_00: + key = (key & 0x3FF); + if(subghz_scene_set_type_submenu_gen_data_protocol( + subghz, + SUBGHZ_PROTOCOL_FIREFLY_NAME, + key, + 10, + 300000000, + FuriHalSubGhzPresetOok650Async)) { generated_protocol = true; } break; case SubmenuIndexCAMETwee: key = (key & 0x0FFFFFF0); key = 0x003FFF7200000000 | (key ^ 0xE0E0E0EE); - if(subghz_scene_set_type_submenu_gen_data_protocol(subghz, "CAME TWEE", key, 54)) { + if(subghz_scene_set_type_submenu_gen_data_protocol( + subghz, + SUBGHZ_PROTOCOL_CAME_TWEE_NAME, + key, + 54, + 433920000, + FuriHalSubGhzPresetOok650Async)) { generated_protocol = true; } break; @@ -198,13 +253,19 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { case SubmenuIndexGateTX: key = (key & 0x00F0FF00) | 0xF << 16 | 0x40; //btn 0xF, 0xC, 0xA, 0x6 (?) uint64_t rev_key = subghz_protocol_blocks_reverse_key(key, 24); - if(subghz_scene_set_type_submenu_gen_data_protocol(subghz, "GateTX", rev_key, 24)) { + if(subghz_scene_set_type_submenu_gen_data_protocol( + subghz, + SUBGHZ_PROTOCOL_GATE_TX_NAME, + rev_key, + 24, + 433920000, + FuriHalSubGhzPresetOok650Async)) { generated_protocol = true; } break; case SubmenuIndexDoorHan_433_92: - subghz->txrx->transmitter = - subghz_transmitter_alloc_init(subghz->txrx->environment, "KeeLoq"); + subghz->txrx->transmitter = subghz_transmitter_alloc_init( + subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME); if(subghz->txrx->transmitter) { subghz_protocol_keeloq_create_data( subghz->txrx->transmitter->protocol_instance, @@ -227,8 +288,8 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { } break; case SubmenuIndexDoorHan_315_00: - subghz->txrx->transmitter = - subghz_transmitter_alloc_init(subghz->txrx->environment, "KeeLoq"); + subghz->txrx->transmitter = subghz_transmitter_alloc_init( + subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME); if(subghz->txrx->transmitter) { subghz_protocol_keeloq_create_data( subghz->txrx->transmitter->protocol_instance, diff --git a/applications/subghz/scenes/subghz_scene_show_error.c b/applications/subghz/scenes/subghz_scene_show_error.c index 993da110..2998bf8e 100644 --- a/applications/subghz/scenes/subghz_scene_show_error.c +++ b/applications/subghz/scenes/subghz_scene_show_error.c @@ -1,6 +1,13 @@ #include "../subghz_i.h" #include "../helpers/subghz_custom_event.h" +static const NotificationSequence subghs_sequence_sd_error = { + &message_red_255, + &message_green_255, + &message_do_not_reset, + NULL, +}; + void subghz_scene_show_error_callback(GuiButtonType result, InputType type, void* context) { furi_assert(context); SubGhz* subghz = context; @@ -31,6 +38,8 @@ void subghz_scene_show_error_on_enter(void* context) { SubGhzCustomEventManagerSet) { widget_add_button_element( subghz->widget, GuiButtonTypeRight, "Ok", subghz_scene_show_error_callback, subghz); + } else { + notification_message(subghz->notifications, &subghs_sequence_sd_error); } widget_add_button_element( @@ -81,4 +90,5 @@ void subghz_scene_show_error_on_exit(void* context) { subghz->scene_manager, SubGhzSceneShowError, SubGhzCustomEventManagerNoSet); widget_reset(subghz->widget); string_reset(subghz->error_str); + notification_message(subghz->notifications, &sequence_reset_rgb); } diff --git a/applications/subghz/scenes/subghz_scene_show_error_sub.c b/applications/subghz/scenes/subghz_scene_show_error_sub.c index 00c4c325..64cd5b5a 100644 --- a/applications/subghz/scenes/subghz_scene_show_error_sub.c +++ b/applications/subghz/scenes/subghz_scene_show_error_sub.c @@ -18,6 +18,8 @@ void subghz_scene_show_error_sub_on_enter(void* context) { popup_set_callback(popup, subghz_scene_show_error_sub_popup_callback); popup_enable_timeout(popup); view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdPopup); + + notification_message(subghz->notifications, &sequence_set_red_255); } bool subghz_scene_show_error_sub_on_event(void* context, SceneManagerEvent event) { @@ -45,4 +47,6 @@ void subghz_scene_show_error_sub_on_exit(void* context) { popup_set_timeout(popup, 0); popup_disable_timeout(popup); string_reset(subghz->error_str); + + notification_message(subghz->notifications, &sequence_reset_rgb); } diff --git a/applications/subghz/scenes/subghz_scene_transmitter.c b/applications/subghz/scenes/subghz_scene_transmitter.c index 92768690..a7244424 100644 --- a/applications/subghz/scenes/subghz_scene_transmitter.c +++ b/applications/subghz/scenes/subghz_scene_transmitter.c @@ -76,7 +76,7 @@ bool subghz_scene_transmitter_on_event(void* context, SceneManagerEvent event) { if(!subghz_tx_start(subghz, subghz->txrx->fff_data)) { scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx); } else { - subghz->state_notifications = SubGhzNotificationStateTX; + subghz->state_notifications = SubGhzNotificationStateTx; subghz_scene_transmitter_update_data_show(subghz); } } @@ -98,8 +98,8 @@ bool subghz_scene_transmitter_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowErrorSub); } } else if(event.type == SceneManagerEventTypeTick) { - if(subghz->state_notifications == SubGhzNotificationStateTX) { - notification_message(subghz->notifications, &sequence_blink_red_10); + if(subghz->state_notifications == SubGhzNotificationStateTx) { + notification_message(subghz->notifications, &sequence_blink_magenta_10); } return true; } diff --git a/applications/subghz/subghz_cli.c b/applications/subghz/subghz_cli.c index 6928c4ba..65ea0cf2 100644 --- a/applications/subghz/subghz_cli.c +++ b/applications/subghz/subghz_cli.c @@ -525,7 +525,7 @@ static void subghz_cli_command_chat(Cli* cli, string_t args) { return; } - SubGhzChatWorker* subghz_chat = subghz_chat_worker_alloc(); + SubGhzChatWorker* subghz_chat = subghz_chat_worker_alloc(cli); if(!subghz_chat_worker_start(subghz_chat, frequency)) { printf("Startup error SubGhzChatWorker\r\n"); diff --git a/applications/subghz/subghz_i.h b/applications/subghz/subghz_i.h index 37d0d6ef..1e917af5 100644 --- a/applications/subghz/subghz_i.h +++ b/applications/subghz/subghz_i.h @@ -41,8 +41,9 @@ typedef enum { SubGhzNotificationStateStarting, SubGhzNotificationStateIDLE, - SubGhzNotificationStateTX, - SubGhzNotificationStateRX, + SubGhzNotificationStateTx, + SubGhzNotificationStateRx, + SubGhzNotificationStateRxDone, } SubGhzNotificationState; /** SubGhzTxRx state */ diff --git a/applications/subghz/views/subghz_frequency_analyzer.c b/applications/subghz/views/subghz_frequency_analyzer.c index 4e645edb..ec3c5ee9 100644 --- a/applications/subghz/views/subghz_frequency_analyzer.c +++ b/applications/subghz/views/subghz_frequency_analyzer.c @@ -19,6 +19,7 @@ struct SubGhzFrequencyAnalyzer { SubGhzFrequencyAnalyzerWorker* worker; SubGhzFrequencyAnalyzerCallback callback; void* context; + bool locked; }; typedef struct { @@ -86,6 +87,17 @@ bool subghz_frequency_analyzer_input(InputEvent* event, void* context) { void subghz_frequency_analyzer_pair_callback(void* context, uint32_t frequency, float rssi) { SubGhzFrequencyAnalyzer* instance = context; + if((rssi == 0.f) && (instance->locked)) { + if(instance->callback) { + instance->callback(SubGhzCustomEventSceneAnalyzerUnlock, instance->context); + } + } else if((rssi != 0.f) && (!instance->locked)) { + if(instance->callback) { + instance->callback(SubGhzCustomEventSceneAnalyzerLock, instance->context); + } + } + + instance->locked = (rssi != 0.f); with_view_model( instance->view, (SubGhzFrequencyAnalyzerModel * model) { model->rssi = rssi; diff --git a/applications/subghz/views/subghz_read_raw.c b/applications/subghz/views/subghz_read_raw.c index bf6abc8c..ff3ba45a 100644 --- a/applications/subghz/views/subghz_read_raw.c +++ b/applications/subghz/views/subghz_read_raw.c @@ -293,7 +293,6 @@ bool subghz_read_raw_input(InputEvent* event, void* context) { case SubGhzReadRAWStatusIDLE: // Start TX instance->callback(SubGhzCustomEventViewReadRAWSendStart, instance->context); - instance->callback(SubGhzCustomEventViewReadRAWVibro, instance->context); model->satus = SubGhzReadRAWStatusTXRepeat; ret = true; break; @@ -304,7 +303,6 @@ bool subghz_read_raw_input(InputEvent* event, void* context) { case SubGhzReadRAWStatusLoadKeyIDLE: // Start Load Key TX instance->callback(SubGhzCustomEventViewReadRAWSendStart, instance->context); - instance->callback(SubGhzCustomEventViewReadRAWVibro, instance->context); model->satus = SubGhzReadRAWStatusLoadKeyTXRepeat; ret = true; break; diff --git a/applications/u2f/scenes/u2f_scene_main.c b/applications/u2f/scenes/u2f_scene_main.c index ede8e305..d0957b59 100644 --- a/applications/u2f/scenes/u2f_scene_main.c +++ b/applications/u2f/scenes/u2f_scene_main.c @@ -59,18 +59,20 @@ bool u2f_scene_main_on_event(void* context, SceneManagerEvent event) { u2f_view_set_state(app->u2f_view, U2fMsgRegister); else if(event.event == U2fCustomEventAuth) u2f_view_set_state(app->u2f_view, U2fMsgAuth); - notification_message(app->notifications, &sequence_display_on); + notification_message(app->notifications, &sequence_display_backlight_on); notification_message(app->notifications, &sequence_single_vibro); } - notification_message(app->notifications, &sequence_blink_blue_10); + notification_message(app->notifications, &sequence_blink_magenta_10); } else if(event.event == U2fCustomEventWink) { - notification_message(app->notifications, &sequence_blink_green_10); + notification_message(app->notifications, &sequence_blink_magenta_10); } else if(event.event == U2fCustomEventAuthSuccess) { + notification_message_block(app->notifications, &sequence_set_green_255); DOLPHIN_DEED(DolphinDeedU2fAuthorized); osTimerStart(app->timer, U2F_SUCCESS_TIMEOUT); app->event_cur = U2fCustomEventNone; u2f_view_set_state(app->u2f_view, U2fMsgSuccess); } else if(event.event == U2fCustomEventTimeout) { + notification_message_block(app->notifications, &sequence_reset_rgb); app->event_cur = U2fCustomEventNone; u2f_view_set_state(app->u2f_view, U2fMsgIdle); } else if(event.event == U2fCustomEventConfirm) { @@ -78,6 +80,7 @@ bool u2f_scene_main_on_event(void* context, SceneManagerEvent event) { u2f_confirm_user_present(app->u2f_instance); } } else if(event.event == U2fCustomEventDataError) { + notification_message(app->notifications, &sequence_set_red_255); osTimerStop(app->timer); u2f_view_set_state(app->u2f_view, U2fMsgError); } @@ -108,6 +111,7 @@ void u2f_scene_main_on_enter(void* context) { void u2f_scene_main_on_exit(void* context) { U2fApp* app = context; + notification_message_block(app->notifications, &sequence_reset_rgb); osTimerStop(app->timer); osTimerDelete(app->timer); if(app->u2f_ready == true) { diff --git a/applications/updater/scenes/updater_scene_main.c b/applications/updater/scenes/updater_scene_main.c index 31a212e7..c5fc99ff 100644 --- a/applications/updater/scenes/updater_scene_main.c +++ b/applications/updater/scenes/updater_scene_main.c @@ -25,6 +25,7 @@ static void sd_mount_callback(const void* message, void* context) { void updater_scene_main_on_enter(void* context) { Updater* updater = (Updater*)context; + notification_message(updater->notification, &sequence_display_backlight_enforce_on); UpdaterMainView* main_view = updater->main_view; FuriPubSubSubscription* sub = @@ -36,9 +37,9 @@ void updater_scene_main_on_enter(void* context) { * will be missing from UI, however, /ext will be fully operational. So, until it's fixed, this * should remain commented out. */ // If (somehow) we started after SD card is mounted, initiate update immediately - //if(storage_sd_status(updater->storage) == FSE_OK) { - // view_dispatcher_send_custom_event(updater->view_dispatcher, UpdaterCustomEventStartUpdate); - //} + if(storage_sd_status(updater->storage) == FSE_OK) { + view_dispatcher_send_custom_event(updater->view_dispatcher, UpdaterCustomEventStartUpdate); + } updater_main_set_view_dispatcher(main_view, updater->view_dispatcher); view_dispatcher_switch_to_view(updater->view_dispatcher, UpdaterViewMain); @@ -64,13 +65,6 @@ bool updater_scene_main_on_event(void* context, SceneManagerEvent event) { } else if(event.type == SceneManagerEventTypeCustom) { switch(event.event) { case UpdaterCustomEventStartUpdate: - if(!update_task_is_running(updater->update_task) && - update_task_init(updater->update_task)) { - update_task_start(updater->update_task); - } - consumed = true; - break; - case UpdaterCustomEventRetryUpdate: if(!update_task_is_running(updater->update_task) && (update_task_get_state(updater->update_task)->stage != UpdateTaskStageCompleted)) @@ -99,8 +93,9 @@ bool updater_scene_main_on_event(void* context, SceneManagerEvent event) { void updater_scene_main_on_exit(void* context) { Updater* updater = (Updater*)context; + notification_message(updater->notification, &sequence_display_backlight_enforce_auto); furi_pubsub_unsubscribe( storage_get_pubsub(updater->storage), updater_main_get_storage_pubsub(updater->main_view)); scene_manager_set_scene_state(updater->scene_manager, UpdaterSceneMain, 0); -} \ No newline at end of file +} diff --git a/applications/updater/updater.c b/applications/updater/updater.c index 519ba167..c1115ce1 100644 --- a/applications/updater/updater.c +++ b/applications/updater/updater.c @@ -47,6 +47,7 @@ Updater* updater_alloc(const char* arg) { } updater->storage = furi_record_open("storage"); + updater->notification = furi_record_open("notification"); updater->gui = furi_record_open("gui"); updater->view_dispatcher = view_dispatcher_alloc(); @@ -119,6 +120,7 @@ void updater_free(Updater* updater) { furi_record_close("gui"); furi_record_close("storage"); + furi_record_close("notification"); free(updater); } diff --git a/applications/updater/updater_i.h b/applications/updater/updater_i.h index d0e7c77c..89201e1e 100644 --- a/applications/updater/updater_i.h +++ b/applications/updater/updater_i.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #ifdef __cplusplus @@ -41,6 +42,7 @@ typedef struct UpdaterManifestProcessingState { typedef struct { // GUI Gui* gui; + NotificationApp* notification; SceneManager* scene_manager; ViewDispatcher* view_dispatcher; Storage* storage; diff --git a/applications/updater/util/update_task.c b/applications/updater/util/update_task.c index 197d42f5..9fde086a 100644 --- a/applications/updater/util/update_task.c +++ b/applications/updater/util/update_task.c @@ -15,13 +15,18 @@ static const char* update_task_stage_descr[] = { [UpdateTaskStageValidateDFUImage] = "Checking DFU file", [UpdateTaskStageFlashWrite] = "Writing flash", [UpdateTaskStageFlashValidate] = "Validating", + [UpdateTaskStageRadioImageValidate] = "Checking radio image", + [UpdateTaskStageRadioErase] = "Removing radio stack", [UpdateTaskStageRadioWrite] = "Writing radio stack", - [UpdateTaskStageRadioCommit] = "Applying radio stack", + [UpdateTaskStageRadioInstall] = "Installing radio stack", + [UpdateTaskStageRadioBusy] = "Core2 is updating", + [UpdateTaskStageOBValidation] = "Validating opt. bytes", [UpdateTaskStageLfsBackup] = "Backing up LFS", [UpdateTaskStageLfsRestore] = "Restoring LFS", [UpdateTaskStageResourcesUpdate] = "Updating resources", [UpdateTaskStageCompleted] = "Completed!", [UpdateTaskStageError] = "Error", + [UpdateTaskStageOBError] = "OB error, pls report", }; static void update_task_set_status(UpdateTask* update_task, const char* status) { @@ -37,7 +42,10 @@ static void update_task_set_status(UpdateTask* update_task, const char* status) void update_task_set_progress(UpdateTask* update_task, UpdateTaskStage stage, uint8_t progress) { if(stage != UpdateTaskStageProgress) { - update_task->state.stage = stage; + // do not override more specific error states + if((update_task->state.stage < UpdateTaskStageError) || (stage < UpdateTaskStageError)) { + update_task->state.stage = stage; + } update_task->state.current_stage_idx++; update_task_set_status(update_task, NULL); } @@ -53,7 +61,7 @@ void update_task_set_progress(UpdateTask* update_task, UpdateTaskStage stage, ui progress, update_task->state.current_stage_idx, update_task->state.total_stages, - update_task->state.stage == UpdateTaskStageError, + update_task->state.stage >= UpdateTaskStageError, update_task->status_change_cb_state); } } @@ -116,6 +124,7 @@ UpdateTask* update_task_alloc() { update_task->storage = furi_record_open("storage"); update_task->file = storage_file_alloc(update_task->storage); update_task->status_change_cb = NULL; + string_init(update_task->update_path); FuriThread* thread = update_task->thread = furi_thread_alloc(); @@ -152,12 +161,6 @@ void update_task_free(UpdateTask* update_task) { free(update_task); } -bool update_task_init(UpdateTask* update_task) { - furi_assert(update_task); - string_init(update_task->update_path); - return true; -} - bool update_task_parse_manifest(UpdateTask* update_task) { furi_assert(update_task); update_task_set_progress(update_task, UpdateTaskStageReadManifest, 0); @@ -224,4 +227,4 @@ UpdateTaskState const* update_task_get_state(UpdateTask* update_task) { UpdateManifest const* update_task_get_manifest(UpdateTask* update_task) { furi_assert(update_task); return update_task->manifest; -} +} \ No newline at end of file diff --git a/applications/updater/util/update_task.h b/applications/updater/util/update_task.h index 2325824c..8afc3121 100644 --- a/applications/updater/util/update_task.h +++ b/applications/updater/util/update_task.h @@ -19,13 +19,18 @@ typedef enum { UpdateTaskStageValidateDFUImage, UpdateTaskStageFlashWrite, UpdateTaskStageFlashValidate, + UpdateTaskStageRadioImageValidate, + UpdateTaskStageRadioErase, UpdateTaskStageRadioWrite, - UpdateTaskStageRadioCommit, + UpdateTaskStageRadioInstall, + UpdateTaskStageRadioBusy, + UpdateTaskStageOBValidation, UpdateTaskStageLfsBackup, UpdateTaskStageLfsRestore, UpdateTaskStageResourcesUpdate, UpdateTaskStageCompleted, UpdateTaskStageError, + UpdateTaskStageOBError } UpdateTaskStage; typedef struct { @@ -50,8 +55,6 @@ UpdateTask* update_task_alloc(); void update_task_free(UpdateTask* update_task); -bool update_task_init(UpdateTask* update_task); - void update_task_set_progress_cb(UpdateTask* update_task, updateProgressCb cb, void* state); bool update_task_start(UpdateTask* update_task); diff --git a/applications/updater/util/update_task_workers.c b/applications/updater/util/update_task_worker_backup.c similarity index 56% rename from applications/updater/util/update_task_workers.c rename to applications/updater/util/update_task_worker_backup.c index d0abbc18..38d71dfd 100644 --- a/applications/updater/util/update_task_workers.c +++ b/applications/updater/util/update_task_worker_backup.c @@ -9,99 +9,17 @@ #include #include #include +#include + +#define TAG "UpdWorkerBackup" #define CHECK_RESULT(x) \ if(!(x)) { \ break; \ } -#define STM_DFU_VENDOR_ID 0x0483 -#define STM_DFU_PRODUCT_ID 0xDF11 -/* Written into DFU file by build pipeline */ -#define FLIPPER_ZERO_DFU_DEVICE_CODE 0xFFFF - #define EXT_PATH "/ext" -static const DfuValidationParams flipper_dfu_params = { - .device = FLIPPER_ZERO_DFU_DEVICE_CODE, - .product = STM_DFU_PRODUCT_ID, - .vendor = STM_DFU_VENDOR_ID, -}; - -static void update_task_dfu_progress(const uint8_t progress, void* context) { - UpdateTask* update_task = context; - update_task_set_progress(update_task, UpdateTaskStageProgress, progress); -} - -static bool page_task_compare_flash( - const uint8_t i_page, - const uint8_t* update_block, - uint16_t update_block_len) { - const size_t page_addr = furi_hal_flash_get_base() + furi_hal_flash_get_page_size() * i_page; - return (memcmp(update_block, (void*)page_addr, update_block_len) == 0); -} - -/* Verifies a flash operation address for fitting into writable memory - */ -static bool check_address_boundaries(const size_t address) { - const size_t min_allowed_address = furi_hal_flash_get_base(); - const size_t max_allowed_address = (size_t)furi_hal_flash_get_free_end_address(); - return ((address >= min_allowed_address) && (address < max_allowed_address)); -} - -int32_t update_task_worker_flash_writer(void* context) { - furi_assert(context); - UpdateTask* update_task = context; - bool success = false; - DfuUpdateTask page_task = { - .address_cb = &check_address_boundaries, - .progress_cb = &update_task_dfu_progress, - .task_cb = &furi_hal_flash_program_page, - .context = update_task, - }; - - update_task->state.current_stage_idx = 0; - update_task->state.total_stages = 4; - - do { - CHECK_RESULT(update_task_parse_manifest(update_task)); - - if(!string_empty_p(update_task->manifest->firmware_dfu_image)) { - update_task_set_progress(update_task, UpdateTaskStageValidateDFUImage, 0); - CHECK_RESULT( - update_task_open_file(update_task, update_task->manifest->firmware_dfu_image)); - CHECK_RESULT( - dfu_file_validate_crc(update_task->file, &update_task_dfu_progress, update_task)); - - const uint8_t valid_targets = - dfu_file_validate_headers(update_task->file, &flipper_dfu_params); - if(valid_targets == 0) { - break; - } - - update_task_set_progress(update_task, UpdateTaskStageFlashWrite, 0); - CHECK_RESULT(dfu_file_process_targets(&page_task, update_task->file, valid_targets)); - - page_task.task_cb = &page_task_compare_flash; - - update_task_set_progress(update_task, UpdateTaskStageFlashValidate, 0); - CHECK_RESULT(dfu_file_process_targets(&page_task, update_task->file, valid_targets)); - } - - update_task_set_progress(update_task, UpdateTaskStageCompleted, 100); - - furi_hal_rtc_set_boot_mode(FuriHalRtcBootModePostUpdate); - - success = true; - } while(false); - - if(!success) { - update_task_set_progress(update_task, UpdateTaskStageError, update_task->state.progress); - } - - return success ? UPDATE_TASK_NOERR : UPDATE_TASK_FAILED; -} - static bool update_task_pre_update(UpdateTask* update_task) { bool success = false; string_t backup_file_path; @@ -111,7 +29,8 @@ static bool update_task_pre_update(UpdateTask* update_task) { update_task->state.total_stages = 1; update_task_set_progress(update_task, UpdateTaskStageLfsBackup, 0); - furi_hal_rtc_set_boot_mode(FuriHalRtcBootModeNormal); // to avoid bootloops + /* to avoid bootloops */ + furi_hal_rtc_set_boot_mode(FuriHalRtcBootModeNormal); if((success = lfs_backup_create(update_task->storage, string_get_cstr(backup_file_path)))) { furi_hal_rtc_set_boot_mode(FuriHalRtcBootModeUpdate); } @@ -143,7 +62,8 @@ static bool update_task_post_update(UpdateTask* update_task) { string_t file_path; string_init(file_path); - update_task->state.total_stages = 2; + /* status text is too long, too few stages to bother with a counter */ + update_task->state.total_stages = 0; do { CHECK_RESULT(update_task_parse_manifest(update_task)); @@ -151,9 +71,6 @@ static bool update_task_post_update(UpdateTask* update_task) { string_get_cstr(update_task->update_path), LFS_BACKUP_DEFAULT_FILENAME, file_path); bool unpack_resources = !string_empty_p(update_task->manifest->resource_bundle); - if(unpack_resources) { - update_task->state.total_stages++; - } update_task_set_progress(update_task, UpdateTaskStageLfsRestore, 0); furi_hal_rtc_set_boot_mode(FuriHalRtcBootModeNormal); @@ -184,6 +101,7 @@ static bool update_task_post_update(UpdateTask* update_task) { } tar_archive_free(archive); } + success = true; } while(false); string_clear(file_path); @@ -197,7 +115,7 @@ int32_t update_task_worker_backup_restore(void* context) { FuriHalRtcBootMode boot_mode = furi_hal_rtc_get_boot_mode(); if((boot_mode != FuriHalRtcBootModePreUpdate) && (boot_mode != FuriHalRtcBootModePostUpdate)) { - // no idea how we got here. Clear to normal boot + /* no idea how we got here. Clear to normal boot */ furi_hal_rtc_set_boot_mode(FuriHalRtcBootModeNormal); return UPDATE_TASK_NOERR; } @@ -208,12 +126,17 @@ int32_t update_task_worker_backup_restore(void* context) { return UPDATE_TASK_FAILED; } + /* Waiting for BT service to 'start', so we don't race for boot mode */ + furi_record_open("bt"); + if(boot_mode == FuriHalRtcBootModePreUpdate) { success = update_task_pre_update(update_task); } else if(boot_mode == FuriHalRtcBootModePostUpdate) { success = update_task_post_update(update_task); } + furi_record_close("bt"); + if(success) { update_task_set_progress(update_task, UpdateTaskStageCompleted, 100); } else { @@ -221,4 +144,4 @@ int32_t update_task_worker_backup_restore(void* context) { } return success ? UPDATE_TASK_NOERR : UPDATE_TASK_FAILED; -} \ No newline at end of file +} diff --git a/applications/updater/util/update_task_worker_flasher.c b/applications/updater/util/update_task_worker_flasher.c new file mode 100644 index 00000000..22e94fd0 --- /dev/null +++ b/applications/updater/util/update_task_worker_flasher.c @@ -0,0 +1,363 @@ +#include "update_task.h" +#include "update_task_i.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TAG "UpdWorkerRAM" + +#define CHECK_RESULT(x) \ + if(!(x)) { \ + break; \ + } + +#define STM_DFU_VENDOR_ID 0x0483 +#define STM_DFU_PRODUCT_ID 0xDF11 +/* Written into DFU file by build pipeline */ +#define FLIPPER_ZERO_DFU_DEVICE_CODE 0xFFFF +/* Time, in ms, to wait for system restart by C2 before crashing */ +#define C2_MODE_SWITCH_TIMEOUT 10000 + +static const DfuValidationParams flipper_dfu_params = { + .device = FLIPPER_ZERO_DFU_DEVICE_CODE, + .product = STM_DFU_PRODUCT_ID, + .vendor = STM_DFU_VENDOR_ID, +}; + +static void update_task_file_progress(const uint8_t progress, void* context) { + UpdateTask* update_task = context; + update_task_set_progress(update_task, UpdateTaskStageProgress, progress); +} + +static bool page_task_compare_flash( + const uint8_t i_page, + const uint8_t* update_block, + uint16_t update_block_len) { + const size_t page_addr = furi_hal_flash_get_base() + furi_hal_flash_get_page_size() * i_page; + return (memcmp(update_block, (void*)page_addr, update_block_len) == 0); +} + +/* Verifies a flash operation address for fitting into writable memory + */ +static bool check_address_boundaries(const size_t address) { + const size_t min_allowed_address = furi_hal_flash_get_base(); + const size_t max_allowed_address = (size_t)furi_hal_flash_get_free_end_address(); + return ((address >= min_allowed_address) && (address < max_allowed_address)); +} + +static bool update_task_write_dfu(UpdateTask* update_task) { + DfuUpdateTask page_task = { + .address_cb = &check_address_boundaries, + .progress_cb = &update_task_file_progress, + .task_cb = &furi_hal_flash_program_page, + .context = update_task, + }; + + bool success = false; + do { + update_task_set_progress(update_task, UpdateTaskStageValidateDFUImage, 0); + CHECK_RESULT( + update_task_open_file(update_task, update_task->manifest->firmware_dfu_image)); + CHECK_RESULT( + dfu_file_validate_crc(update_task->file, &update_task_file_progress, update_task)); + + const uint8_t valid_targets = + dfu_file_validate_headers(update_task->file, &flipper_dfu_params); + if(valid_targets == 0) { + break; + } + + update_task_set_progress(update_task, UpdateTaskStageFlashWrite, 0); + CHECK_RESULT(dfu_file_process_targets(&page_task, update_task->file, valid_targets)); + + page_task.task_cb = &page_task_compare_flash; + + update_task_set_progress(update_task, UpdateTaskStageFlashValidate, 0); + CHECK_RESULT(dfu_file_process_targets(&page_task, update_task->file, valid_targets)); + success = true; + } while(false); + + return success; +} + +static bool update_task_write_stack_data(UpdateTask* update_task) { + furi_check(storage_file_is_open(update_task->file)); + const size_t FLASH_PAGE_SIZE = furi_hal_flash_get_page_size(); + + uint32_t stack_size = storage_file_size(update_task->file); + storage_file_seek(update_task->file, 0, true); + + if(!check_address_boundaries(update_task->manifest->radio_address) || + !check_address_boundaries(update_task->manifest->radio_address + stack_size)) { + return false; + } + + update_task_set_progress(update_task, UpdateTaskStageRadioWrite, 0); + uint8_t* fw_block = malloc(FLASH_PAGE_SIZE); + uint16_t bytes_read = 0; + uint32_t element_offs = 0; + + while(element_offs < stack_size) { + uint32_t n_bytes_to_read = FLASH_PAGE_SIZE; + if((element_offs + n_bytes_to_read) > stack_size) { + n_bytes_to_read = stack_size - element_offs; + } + + bytes_read = storage_file_read(update_task->file, fw_block, n_bytes_to_read); + if(bytes_read == 0) { + break; + } + + int16_t i_page = + furi_hal_flash_get_page_number(update_task->manifest->radio_address + element_offs); + if(i_page < 0) { + break; + } + + if(!furi_hal_flash_program_page(i_page, fw_block, bytes_read)) { + break; + } + + element_offs += bytes_read; + update_task_set_progress( + update_task, UpdateTaskStageProgress, element_offs * 100 / stack_size); + } + + free(fw_block); + return element_offs == stack_size; +} + +static void update_task_wait_for_restart(UpdateTask* update_task) { + update_task_set_progress(update_task, UpdateTaskStageRadioBusy, 10); + osDelay(C2_MODE_SWITCH_TIMEOUT); + furi_crash("C2 timeout"); +} + +static bool update_task_write_stack(UpdateTask* update_task) { + bool success = false; + do { + FURI_LOG_W(TAG, "Writing stack"); + update_task_set_progress(update_task, UpdateTaskStageRadioImageValidate, 0); + CHECK_RESULT(update_task_open_file(update_task, update_task->manifest->radio_image)); + CHECK_RESULT( + crc32_calc_file(update_task->file, &update_task_file_progress, update_task) == + update_task->manifest->radio_crc); + + CHECK_RESULT(update_task_write_stack_data(update_task)); + update_task_set_progress(update_task, UpdateTaskStageRadioInstall, 0); + CHECK_RESULT( + ble_glue_fus_stack_install(update_task->manifest->radio_address, 0) != + BleGlueCommandResultError); + update_task_set_progress(update_task, UpdateTaskStageRadioInstall, 80); + CHECK_RESULT(ble_glue_fus_wait_operation() == BleGlueCommandResultOK); + update_task_set_progress(update_task, UpdateTaskStageRadioInstall, 100); + /* ...system will restart here. */ + update_task_wait_for_restart(update_task); + success = true; + } while(false); + return success; +} + +static bool update_task_remove_stack(UpdateTask* update_task) { + bool success = false; + do { + FURI_LOG_W(TAG, "Removing stack"); + update_task_set_progress(update_task, UpdateTaskStageRadioErase, 30); + CHECK_RESULT(ble_glue_fus_stack_delete() != BleGlueCommandResultError); + update_task_set_progress(update_task, UpdateTaskStageRadioErase, 80); + CHECK_RESULT(ble_glue_fus_wait_operation() == BleGlueCommandResultOK); + update_task_set_progress(update_task, UpdateTaskStageRadioErase, 100); + /* ...system will restart here. */ + update_task_wait_for_restart(update_task); + success = true; + } while(false); + return success; +} + +static bool update_task_manage_radiostack(UpdateTask* update_task) { + bool success = false; + do { + CHECK_RESULT(ble_glue_wait_for_c2_start(FURI_HAL_BT_C2_START_TIMEOUT)); + + const BleGlueC2Info* c2_state = ble_glue_get_c2_info(); + + const UpdateManifestRadioVersion* radio_ver = &update_task->manifest->radio_version; + bool stack_version_match = (c2_state->VersionMajor == radio_ver->version.major) && + (c2_state->VersionMinor == radio_ver->version.minor) && + (c2_state->VersionSub == radio_ver->version.sub) && + (c2_state->VersionBranch == radio_ver->version.branch) && + (c2_state->VersionReleaseType == radio_ver->version.release); + bool stack_missing = (c2_state->VersionMajor == 0) && (c2_state->VersionMinor == 0); + + if(c2_state->mode == BleGlueC2ModeStack) { + /* Stack type is not available when we have FUS running. */ + bool total_stack_match = stack_version_match && + (c2_state->StackType == radio_ver->version.type); + if(total_stack_match) { + /* Nothing to do. */ + FURI_LOG_W(TAG, "Stack version is up2date"); + furi_hal_rtc_reset_flag(FuriHalRtcFlagC2Update); + success = true; + break; + } else { + /* Version or type mismatch. Let's boot to FUS and start updating. */ + FURI_LOG_W(TAG, "Restarting to FUS"); + furi_hal_rtc_set_flag(FuriHalRtcFlagC2Update); + CHECK_RESULT(furi_hal_bt_ensure_c2_mode(BleGlueC2ModeFUS)); + /* ...system will restart here. */ + update_task_wait_for_restart(update_task); + } + } else if(c2_state->mode == BleGlueC2ModeFUS) { + /* OK, we're in FUS mode. */ + update_task_set_progress(update_task, UpdateTaskStageRadioBusy, 10); + FURI_LOG_W(TAG, "Waiting for FUS to settle"); + ble_glue_fus_wait_operation(); + if(stack_version_match) { + /* We can't check StackType with FUS, but partial version matches */ + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagC2Update)) { + /* This flag was set when full version was checked. + * And something in versions of the stack didn't match. + * So, clear the flag and drop the stack. */ + furi_hal_rtc_reset_flag(FuriHalRtcFlagC2Update); + FURI_LOG_W(TAG, "Forcing stack removal (match)"); + CHECK_RESULT(update_task_remove_stack(update_task)); + } else { + /* We might just had the stack installed. + * Let's start it up to check its version */ + FURI_LOG_W(TAG, "Starting stack to check full version"); + update_task_set_progress(update_task, UpdateTaskStageRadioBusy, 40); + CHECK_RESULT(furi_hal_bt_ensure_c2_mode(BleGlueC2ModeStack)); + /* ...system will restart here. */ + update_task_wait_for_restart(update_task); + } + } else { + if(stack_missing) { + /* Install stack. */ + CHECK_RESULT(update_task_write_stack(update_task)); + } else { + CHECK_RESULT(update_task_remove_stack(update_task)); + } + } + } + } while(false); + + return success; +} + +bool update_task_validate_optionbytes(UpdateTask* update_task) { + update_task_set_progress(update_task, UpdateTaskStageOBValidation, 0); + + bool match = true; + bool ob_dirty = false; + const UpdateManifest* manifest = update_task->manifest; + const FuriHalFlashRawOptionByteData* device_data = furi_hal_flash_ob_get_raw_ptr(); + for(size_t idx = 0; idx < FURI_HAL_FLASH_OB_TOTAL_VALUES; ++idx) { + update_task_set_progress( + update_task, UpdateTaskStageProgress, idx * 100 / FURI_HAL_FLASH_OB_TOTAL_VALUES); + const uint32_t ref_value = manifest->ob_reference.obs[idx].values.base; + const uint32_t device_ob_value = device_data->obs[idx].values.base; + const uint32_t device_ob_value_masked = device_ob_value & + manifest->ob_compare_mask.obs[idx].values.base; + if(ref_value != device_ob_value_masked) { + match = false; + FURI_LOG_E( + TAG, + "OB MISMATCH: #%d: real %08X != %08X (exp.), full %08X", + idx, + device_ob_value_masked, + ref_value, + device_ob_value); + + /* any bits we are allowed to write?.. */ + bool can_patch = ((device_ob_value_masked ^ ref_value) & + manifest->ob_write_mask.obs[idx].values.base) != 0; + + if(can_patch) { + /* patch & restart loop */ + const uint32_t patched_value = + /* take all non-writable bits from real value */ + (device_ob_value & ~(manifest->ob_write_mask.obs[idx].values.base)) | + /* take all writable bits from reference value */ + (manifest->ob_reference.obs[idx].values.base & + manifest->ob_write_mask.obs[idx].values.base); + + FURI_LOG_W(TAG, "Fixing up OB byte #%d to %08X", idx, patched_value); + ob_dirty = true; + + bool is_fixed = furi_hal_flash_ob_set_word(idx, patched_value) && + ((device_data->obs[idx].values.base & + manifest->ob_compare_mask.obs[idx].values.base) == ref_value); + + if(!is_fixed) { + /* Things are so bad that fixing what we are allowed to still doesn't match + * reference value + */ + FURI_LOG_W( + TAG, + "OB #%d is FUBAR (fixed&masked %08X, not %08X)", + idx, + patched_value, + ref_value); + } + } + } else { + FURI_LOG_I( + TAG, + "OB MATCH: #%d: real %08X == %08X (exp.)", + idx, + device_ob_value_masked, + ref_value); + } + } + if(!match) { + update_task_set_progress(update_task, UpdateTaskStageOBError, 95); + } + + if(ob_dirty) { + FURI_LOG_W(TAG, "OB were changed, applying"); + furi_hal_flash_ob_apply(); + } + return match; +} + +int32_t update_task_worker_flash_writer(void* context) { + furi_assert(context); + UpdateTask* update_task = context; + bool success = false; + + update_task->state.current_stage_idx = 0; + update_task->state.total_stages = 0; + + do { + CHECK_RESULT(update_task_parse_manifest(update_task)); + + if(!string_empty_p(update_task->manifest->radio_image)) { + CHECK_RESULT(update_task_manage_radiostack(update_task)); + } + + bool check_ob = update_manifest_has_obdata(update_task->manifest); + if(check_ob) { + update_task->state.total_stages++; + CHECK_RESULT(update_task_validate_optionbytes(update_task)); + } + + if(!string_empty_p(update_task->manifest->firmware_dfu_image)) { + update_task->state.total_stages += 4; + CHECK_RESULT(update_task_write_dfu(update_task)); + } + + furi_hal_rtc_set_boot_mode(FuriHalRtcBootModePostUpdate); + + update_task_set_progress(update_task, UpdateTaskStageCompleted, 100); + success = true; + } while(false); + + return success ? UPDATE_TASK_NOERR : UPDATE_TASK_FAILED; +} \ No newline at end of file diff --git a/assets/.gitignore b/assets/.gitignore index 0a0acede..eb20456f 100644 --- a/assets/.gitignore +++ b/assets/.gitignore @@ -1 +1,2 @@ /headers +/core2_firmware diff --git a/assets/Makefile b/assets/Makefile index 19d148fd..2c743601 100644 --- a/assets/Makefile +++ b/assets/Makefile @@ -1,6 +1,7 @@ PROJECT_ROOT = $(abspath $(dir $(abspath $(firstword $(MAKEFILE_LIST))))..) include $(PROJECT_ROOT)/assets/assets.mk +include $(PROJECT_ROOT)/assets/copro.mk .PHONY: all all: icons protobuf dolphin manifest @@ -35,7 +36,13 @@ manifest: .PHONY: dolphin dolphin: $(DOLPHIN_EXTERNAL_OUTPUT_DIR) +.PHONY: copro_bundle +copro_bundle: + @mkdir -p $(COPRO_BUNDLE_DIR) + @$(ASSETS_COMPILER) copro $(COPRO_CUBE_DIR) $(COPRO_BUNDLE_DIR) $(COPRO_MCU_FAMILY) --cube_ver=$(COPRO_CUBE_VERSION) --stack_type=$(COPRO_STACK_TYPE) --stack_file=$(COPRO_STACK_BIN) --stack_addr=$(COPRO_STACK_ADDR) + clean: @echo "\tCLEAN\t" @$(RM) $(ASSETS_COMPILED_DIR)/* + @$(RM) -rf $(COPRO_BUNDLE_DIR) @$(RM) -rf $(DOLPHIN_EXTERNAL_OUTPUT_DIR) diff --git a/assets/ReadMe.md b/assets/ReadMe.md index 1afaef62..0e5b0bdb 100644 --- a/assets/ReadMe.md +++ b/assets/ReadMe.md @@ -10,6 +10,13 @@ make all ``` +# Compiling with Docker-Compose + +```bash +docker-compose exec dev make -C assets clean +docker-compose exec dev make -C assets all +``` + # Asset naming rules ## Images and Animations diff --git a/assets/copro.mk b/assets/copro.mk new file mode 100644 index 00000000..004bd8d9 --- /dev/null +++ b/assets/copro.mk @@ -0,0 +1,12 @@ +COPRO_CUBE_VERSION := 1.13.3 +COPRO_MCU_FAMILY := STM32WB5x +COPRO_STACK_BIN := stm32wb5x_BLE_Stack_light_fw.bin +# See __STACK_TYPE_CODES in scripts/flipper/assets/coprobin.py +COPRO_STACK_TYPE := ble_light +# Keep 0 for auto, or put a value from release_notes for chosen stack +COPRO_STACK_ADDR := 0 + +COPRO_BUNDLE_DIR := $(ASSETS_DIR)/core2_firmware +COPRO_CUBE_DIR := $(PROJECT_ROOT)/lib/STM32CubeWB +COPRO_FIRMWARE_DIR := $(COPRO_CUBE_DIR)/Projects/STM32WB_Copro_Wireless_Binaries/$(COPRO_MCU_FAMILY) +COPRO_STACK_BIN_PATH := $(COPRO_FIRMWARE_DIR)/$(COPRO_STACK_BIN) diff --git a/assets/resources/Manifest b/assets/resources/Manifest index 12773c00..f19edd7d 100644 --- a/assets/resources/Manifest +++ b/assets/resources/Manifest @@ -1,5 +1,5 @@ V:0 -T:1650389893 +T:1651076680 D:badusb D:dolphin D:infrared @@ -222,7 +222,7 @@ F:33b8fde22f34ef556b64b77164bc19b0:578:dolphin/L3_Lab_research_128x54/frame_8.bm F:f267f0654781049ca323b11bb4375519:581:dolphin/L3_Lab_research_128x54/frame_9.bm F:41106c0cbc5144f151b2b2d3daaa0527:727:dolphin/L3_Lab_research_128x54/meta.txt D:infrared/assets -F:5b16e1a59daf3ef1d0fc95b3b5596d67:74300:infrared/assets/tv.ir +F:d895fda2f48c6cc4c55e8a398ff52e43:74300:infrared/assets/tv.ir D:nfc/assets F:c6826a621d081d68309e4be424d3d974:4715:nfc/assets/aid.nfc F:86efbebdf41bb6bf15cc51ef88f069d5:2565:nfc/assets/country_code.nfc diff --git a/assets/resources/infrared/assets/tv.ir b/assets/resources/infrared/assets/tv.ir index 1031db63..98292a8e 100755 --- a/assets/resources/infrared/assets/tv.ir +++ b/assets/resources/infrared/assets/tv.ir @@ -1,4 +1,4 @@ -Filetype: IR signals file +Filetype: IR library file Version: 1 # name: POWER diff --git a/firmware/targets/f7/Src/update.c b/firmware/targets/f7/Src/update.c index 3e94fbec..4b3ee3ad 100644 --- a/firmware/targets/f7/Src/update.c +++ b/firmware/targets/f7/Src/update.c @@ -7,7 +7,8 @@ #include #include -#include +#include +#include static FATFS* pfs = NULL; @@ -27,7 +28,6 @@ static bool flipper_update_init() { furi_hal_delay_init(); furi_hal_spi_init(); - furi_hal_crc_init(false); MX_FATFS_Init(); if(!hal_sd_detect()) { @@ -62,17 +62,15 @@ static bool flipper_update_load_stage(const string_t work_dir, UpdateManifest* m uint32_t bytes_read = 0; const uint16_t MAX_READ = 0xFFFF; - furi_hal_crc_reset(); uint32_t crc = 0; do { uint16_t size_read = 0; if(f_read(&file, img + bytes_read, MAX_READ, &size_read) != FR_OK) { break; } - crc = furi_hal_crc_feed(img + bytes_read, size_read); + crc = crc32_calc_buffer(crc, img + bytes_read, size_read); bytes_read += size_read; } while(bytes_read == MAX_READ); - furi_hal_crc_reset(); do { if((bytes_read != stat.fsize) || (crc != manifest->staged_loader_crc)) { diff --git a/firmware/targets/f7/ble_glue/app_conf.h b/firmware/targets/f7/ble_glue/app_conf.h index 96a0c25b..1406f964 100644 --- a/firmware/targets/f7/ble_glue/app_conf.h +++ b/firmware/targets/f7/ble_glue/app_conf.h @@ -396,7 +396,7 @@ typedef enum { * keep debugger enabled while in any low power mode when set to 1 * should be set to 0 in production */ -#define CFG_DEBUGGER_SUPPORTED 0 +#define CFG_DEBUGGER_SUPPORTED 1 /** * When set to 1, the traces are enabled in the BLE services diff --git a/firmware/targets/f7/ble_glue/app_debug.c b/firmware/targets/f7/ble_glue/app_debug.c index 41d66704..e480ea36 100644 --- a/firmware/targets/f7/ble_glue/app_debug.c +++ b/firmware/targets/f7/ble_glue/app_debug.c @@ -1,25 +1,3 @@ -/* USER CODE BEGIN Header */ -/** - ****************************************************************************** - * File Name : app_debug.c - * Description : Debug capabilities source file for STM32WPAN Middleware - ****************************************************************************** - * @attention - * - *

© Copyright (c) 2020 STMicroelectronics. - * All rights reserved.

- * - * This software component is licensed by ST under Ultimate Liberty license - * SLA0044, the "License"; You may not use this file except in compliance with - * the License. You may obtain a copy of the License at: - * www.st.com/SLA0044 - * - ****************************************************************************** - */ -/* USER CODE END Header */ - -/* Includes ------------------------------------------------------------------*/ -/* USER CODE BEGIN Includes */ #include "utilities_common.h" #include "app_common.h" @@ -28,10 +6,7 @@ #include "tl.h" #include "dbg_trace.h" #include -/* USER CODE END Includes */ -/* Private typedef -----------------------------------------------------------*/ -/* USER CODE BEGIN PTD */ typedef PACKED_STRUCT { GPIO_TypeDef* port; uint16_t pin; @@ -39,10 +14,7 @@ typedef PACKED_STRUCT { uint8_t reserved; } APPD_GpioConfig_t; -/* USER CODE END PTD */ -/* Private defines -----------------------------------------------------------*/ -/* USER CODE BEGIN PD */ #define GPIO_NBR_OF_RF_SIGNALS 9 #define GPIO_CFG_NBR_OF_FEATURES 34 #define NBR_OF_TRACES_CONFIG_PARAMETERS 4 @@ -51,12 +23,11 @@ APPD_GpioConfig_t; /** * THIS SHALL BE SET TO A VALUE DIFFERENT FROM 0 ONLY ON REQUEST FROM ST SUPPORT */ -#define BLE_DTB_CFG 7 +#define BLE_DTB_CFG 0 +// #define BLE_DTB_CFG 7 #define SYS_DBG_CFG1 (SHCI_C2_DEBUG_OPTIONS_IPCORE_LP | SHCI_C2_DEBUG_OPTIONS_CPU2_STOP_EN) -/* USER CODE END PD */ /* Private variables ---------------------------------------------------------*/ -/* USER CODE BEGIN PV */ PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static SHCI_C2_DEBUG_TracesConfig_t APPD_TracesConfig = {0, 0, 0, 0}; PLACE_IN_SECTION("MB_MEM2") @@ -91,7 +62,7 @@ static const APPD_GpioConfig_t aGpioConfigList[GPIO_CFG_NBR_OF_FEATURES] = { {GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IPCC_TRACES_TX - Set on Entry / Reset on Exit */ {GPIOA, LL_GPIO_PIN_6, 1, 0}, /* HARD_FAULT - Set on Entry / Reset on Exit */ /* From v1.1.1 */ - {GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IP_CORE_LP_STATUS - Set on Entry / Reset on Exit */ + {GPIOC, LL_GPIO_PIN_1, 1, 0}, /* IP_CORE_LP_STATUS - Set on Entry / Reset on Exit */ /* From v1.2.0 */ {GPIOA, LL_GPIO_PIN_0, 0, 0}, /* END_OF_CONNECTION_EVENT - Set on Entry / Reset on Exit */ {GPIOA, LL_GPIO_PIN_0, 0, 0}, /* TIMER_SERVER_CALLBACK - Toggle on Entry */ @@ -130,65 +101,20 @@ static const APPD_GpioConfig_t aRfConfigList[GPIO_NBR_OF_RF_SIGNALS] = { {GPIOB, LL_GPIO_PIN_10, 0, 0}, /* DTB18 - FSM4 */ }; #endif -/* USER CODE END PV */ -/* Global variables ----------------------------------------------------------*/ -/* USER CODE BEGIN GV */ -/* USER CODE END GV */ - -/* Private function prototypes -----------------------------------------------*/ -/* USER CODE BEGIN PFP */ static void APPD_SetCPU2GpioConfig(void); static void APPD_BleDtbCfg(void); -/* USER CODE END PFP */ - -/* Functions Definition ------------------------------------------------------*/ -void APPD_Init(void) { -/* USER CODE BEGIN APPD_Init */ -#if(CFG_DEBUGGER_SUPPORTED == 1) - /** - * Keep debugger enabled while in any low power mode - */ - LL_DBGMCU_EnableDBGSleepMode(); - LL_DBGMCU_EnableDBGStopMode(); - - /***************** ENABLE DEBUGGER *************************************/ - LL_EXTI_EnableIT_32_63(LL_EXTI_LINE_48); - -#else - LL_GPIO_InitTypeDef gpio_config = {0}; - LL_PWR_EnableVddUSB(); - - gpio_config.Mode = LL_GPIO_MODE_ANALOG; - gpio_config.Speed = LL_GPIO_SPEED_FREQ_LOW; - // gpio_config.OutputType = LL_GPIO_OUTPUT_PUSHPULL; - // gpio_config.Pull = LL_GPIO_PULL_NO; - // gpio_config.Alternate = LL_GPIO_AF_10; - gpio_config.Pin = LL_GPIO_PIN_15 | LL_GPIO_PIN_14 | LL_GPIO_PIN_13; - LL_GPIO_Init(GPIOA, &gpio_config); - - gpio_config.Pin = LL_GPIO_PIN_4 | LL_GPIO_PIN_3; - LL_GPIO_Init(GPIOB, &gpio_config); - - LL_DBGMCU_DisableDBGSleepMode(); - LL_DBGMCU_DisableDBGStopMode(); - LL_DBGMCU_DisableDBGStandbyMode(); - -#endif /* (CFG_DEBUGGER_SUPPORTED == 1) */ +void APPD_Init() { #if(CFG_DEBUG_TRACE != 0) DbgTraceInit(); #endif APPD_SetCPU2GpioConfig(); APPD_BleDtbCfg(); - - /* USER CODE END APPD_Init */ - return; } void APPD_EnableCPU2(void) { - /* USER CODE BEGIN APPD_EnableCPU2 */ SHCI_C2_DEBUG_Init_Cmd_Packet_t DebugCmdPacket = { {{0, 0, 0}}, /**< Does not need to be initialized */ {(uint8_t*)aGpioConfigList, @@ -204,6 +130,7 @@ void APPD_EnableCPU2(void) { /** GPIO DEBUG Initialization */ SHCI_C2_DEBUG_Init(&DebugCmdPacket); + // We don't need External Power Amplifier // LL_GPIO_InitTypeDef gpio_config; // gpio_config.Pull = GPIO_NOPULL; // gpio_config.Mode = GPIO_MODE_OUTPUT_PP; @@ -212,17 +139,10 @@ void APPD_EnableCPU2(void) { // HAL_GPIO_Init(GPIOC, &gpio_config); // SHCI_C2_ExtpaConfig((uint32_t)GPIOC, LL_GPIO_PIN_3, EXT_PA_ENABLED_LOW, EXT_PA_ENABLED); - /* USER CODE END APPD_EnableCPU2 */ return; } -/************************************************************* - * - * LOCAL FUNCTIONS - * - *************************************************************/ static void APPD_SetCPU2GpioConfig(void) { - /* USER CODE BEGIN APPD_SetCPU2GpioConfig */ LL_GPIO_InitTypeDef gpio_config = {0}; uint8_t local_loop; uint16_t gpioa_pin_list; @@ -259,8 +179,9 @@ static void APPD_SetCPU2GpioConfig(void) { gpio_config.OutputType = LL_GPIO_OUTPUT_PUSHPULL; gpio_config.Pull = LL_GPIO_PULL_NO; - gpio_config.Pin = LL_GPIO_PIN_15 | LL_GPIO_PIN_14 | LL_GPIO_PIN_13; - LL_GPIO_Init(GPIOA, &gpio_config); + // Never disable SWD, why would you? + // gpio_config.Pin = LL_GPIO_PIN_15 | LL_GPIO_PIN_14 | LL_GPIO_PIN_13; + // LL_GPIO_Init(GPIOA, &gpio_config); if(gpioa_pin_list != 0) { gpio_config.Pin = gpioa_pin_list; @@ -282,13 +203,9 @@ static void APPD_SetCPU2GpioConfig(void) { LL_GPIO_Init(GPIOC, &gpio_config); LL_GPIO_ResetOutputPin(GPIOC, gpioa_pin_list); } - - /* USER CODE END APPD_SetCPU2GpioConfig */ - return; } static void APPD_BleDtbCfg(void) { -/* USER CODE BEGIN APPD_BleDtbCfg */ #if(BLE_DTB_CFG != 0) LL_GPIO_InitTypeDef gpio_config = {0}; uint8_t local_loop; @@ -304,11 +221,9 @@ static void APPD_BleDtbCfg(void) { case(uint32_t)GPIOA: gpioa_pin_list |= aRfConfigList[local_loop].pin; break; - case(uint32_t)GPIOB: gpiob_pin_list |= aRfConfigList[local_loop].pin; break; - default: break; } @@ -334,16 +249,8 @@ static void APPD_BleDtbCfg(void) { LL_GPIO_Init(GPIOB, &gpio_config); } #endif - - /* USER CODE END APPD_BleDtbCfg */ - return; } -/************************************************************* - * - * WRAP FUNCTIONS - * -*************************************************************/ #if(CFG_DEBUG_TRACE != 0) void DbgOutputInit(void) { } @@ -353,5 +260,3 @@ void DbgOutputTraces(uint8_t* p_data, uint16_t size, void (*cb)(void)) { cb(); } #endif - -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/firmware/targets/f7/ble_glue/ble_glue.c b/firmware/targets/f7/ble_glue/ble_glue.c index 9142a261..ec4a617b 100644 --- a/firmware/targets/f7/ble_glue/ble_glue.c +++ b/firmware/targets/f7/ble_glue/ble_glue.c @@ -6,7 +6,9 @@ #include "shci.h" #include "shci_tl.h" #include "app_debug.h" + #include +#include #define TAG "Core2" @@ -27,22 +29,13 @@ PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static uint8_t ble_glue_ble_spare_event_buff[sizeof(TL_PacketHeader_t) + TL_EVT_HDR_SIZE + 255]; -typedef enum { - // Stage 1: core2 startup and FUS - BleGlueStatusStartup, - BleGlueStatusBroken, - BleGlueStatusFusStarted, - // Stage 2: radio stack - BleGlueStatusRadioStackStarted, - BleGlueStatusRadioStackMissing -} BleGlueStatus; - typedef struct { osMutexId_t shci_mtx; osSemaphoreId_t shci_sem; FuriThread* thread; BleGlueStatus status; BleGlueKeyStorageChangedCallback callback; + BleGlueC2Info c2_info; void* context; } BleGlue; @@ -70,9 +63,10 @@ void ble_glue_init() { LL_RCC_SetClkAfterWakeFromStop(LL_RCC_STOP_WAKEUPCLOCK_HSI); /* Initialize the CPU2 reset value before starting CPU2 with C2BOOT */ LL_C2_PWR_SetPowerMode(LL_PWR_MODE_SHUTDOWN); - furi_hal_power_insomnia_enter(); - // APPD_Init(); +#ifdef BLE_GLUE_DEBUG + APPD_Init(); +#endif // Initialize all transport layers TL_MM_Config_t tl_mm_config; @@ -111,41 +105,103 @@ void ble_glue_init() { */ } -bool ble_glue_wait_for_fus_start(WirelessFwInfo_t* info) { - bool ret = false; +const BleGlueC2Info* ble_glue_get_c2_info() { + return &ble_glue->c2_info; +} - size_t countdown = 1000; - while(countdown > 0) { - if(ble_glue->status == BleGlueStatusFusStarted) { - ret = true; - break; +BleGlueStatus ble_glue_get_c2_status() { + return ble_glue->status; +} + +static void ble_glue_update_c2_fw_info() { + WirelessFwInfo_t wireless_info; + SHCI_GetWirelessFwInfo(&wireless_info); + BleGlueC2Info* local_info = &ble_glue->c2_info; + + local_info->VersionMajor = wireless_info.VersionMajor; + local_info->VersionMinor = wireless_info.VersionMinor; + local_info->VersionMajor = wireless_info.VersionMajor; + local_info->VersionMinor = wireless_info.VersionMinor; + local_info->VersionSub = wireless_info.VersionSub; + local_info->VersionBranch = wireless_info.VersionBranch; + local_info->VersionReleaseType = wireless_info.VersionReleaseType; + + local_info->MemorySizeSram2B = wireless_info.MemorySizeSram2B; + local_info->MemorySizeSram2A = wireless_info.MemorySizeSram2A; + local_info->MemorySizeSram1 = wireless_info.MemorySizeSram1; + local_info->MemorySizeFlash = wireless_info.MemorySizeFlash; + + local_info->StackType = wireless_info.StackType; + + local_info->FusVersionMajor = wireless_info.FusVersionMajor; + local_info->FusVersionMinor = wireless_info.FusVersionMinor; + local_info->FusVersionSub = wireless_info.FusVersionSub; + local_info->FusMemorySizeSram2B = wireless_info.FusMemorySizeSram2B; + local_info->FusMemorySizeSram2A = wireless_info.FusMemorySizeSram2A; + local_info->FusMemorySizeFlash = wireless_info.FusMemorySizeFlash; +} + +static void ble_glue_dump_stack_info() { + const BleGlueC2Info* c2_info = &ble_glue->c2_info; + FURI_LOG_I( + TAG, + "Core2: FUS: %d.%d.%d, mem %d/%d, flash %d pages", + c2_info->FusVersionMajor, + c2_info->FusVersionMinor, + c2_info->FusVersionSub, + c2_info->FusMemorySizeSram2B, + c2_info->FusMemorySizeSram2A, + c2_info->FusMemorySizeFlash); + FURI_LOG_I( + TAG, + "Core2: Stack: %d.%d.%d, branch %d, reltype %d, stacktype %d, flash %d pages", + c2_info->VersionMajor, + c2_info->VersionMinor, + c2_info->VersionSub, + c2_info->VersionBranch, + c2_info->VersionReleaseType, + c2_info->StackType, + c2_info->MemorySizeFlash); +} + +bool ble_glue_wait_for_c2_start(int32_t timeout) { + bool started = false; + + do { + // TODO: use mutex? + started = ble_glue->status == BleGlueStatusC2Started; + if(!started) { + timeout--; + osDelay(1); } - countdown--; - osDelay(1); - } + } while(!started && (timeout > 0)); - if(ble_glue->status == BleGlueStatusFusStarted) { - SHCI_GetWirelessFwInfo(info); + if(started) { + FURI_LOG_I( + TAG, + "C2 boot completed, mode: %s", + ble_glue->c2_info.mode == BleGlueC2ModeFUS ? "FUS" : "Stack"); + ble_glue_update_c2_fw_info(); + ble_glue_dump_stack_info(); } else { - FURI_LOG_E(TAG, "Failed to start FUS"); + FURI_LOG_E(TAG, "C2 startup failed"); ble_glue->status = BleGlueStatusBroken; } - return ret; + return started; } bool ble_glue_start() { furi_assert(ble_glue); - if(ble_glue->status != BleGlueStatusFusStarted) { + if(ble_glue->status != BleGlueStatusC2Started) { return false; } bool ret = false; - furi_hal_power_insomnia_enter(); if(ble_app_init()) { FURI_LOG_I(TAG, "Radio stack started"); - ble_glue->status = BleGlueStatusRadioStackStarted; + ble_glue->status = BleGlueStatusRadioStackRunning; ret = true; if(SHCI_C2_SetFlashActivityControl(FLASH_ACTIVITY_CONTROL_SEM7) == SHCI_Success) { FURI_LOG_I(TAG, "Flash activity control switched to SEM7"); @@ -157,7 +213,6 @@ bool ble_glue_start() { ble_glue->status = BleGlueStatusRadioStackMissing; ble_app_thread_stop(); } - furi_hal_power_insomnia_exit(); return ret; } @@ -167,7 +222,7 @@ bool ble_glue_is_alive() { return false; } - return ble_glue->status >= BleGlueStatusFusStarted; + return ble_glue->status >= BleGlueStatusC2Started; } bool ble_glue_is_radio_stack_ready() { @@ -175,26 +230,42 @@ bool ble_glue_is_radio_stack_ready() { return false; } - return ble_glue->status == BleGlueStatusRadioStackStarted; + return ble_glue->status == BleGlueStatusRadioStackRunning; } -bool ble_glue_radio_stack_fw_launch_started() { - bool ret = false; - // Get FUS status - SHCI_FUS_GetState_ErrorCode_t err_code = 0; - uint8_t state = SHCI_C2_FUS_GetState(&err_code); - if(state == FUS_STATE_VALUE_IDLE) { - // When FUS is running we can't read radio stack version correctly - // Trying to start radio stack fw, which leads to reset - FURI_LOG_W(TAG, "FUS is running. Restart to launch Radio Stack"); +BleGlueCommandResult ble_glue_force_c2_mode(BleGlueC2Mode desired_mode) { + furi_check(desired_mode > BleGlueC2ModeUnknown); + + if(desired_mode == ble_glue->c2_info.mode) { + return BleGlueCommandResultOK; + } + + if((ble_glue->c2_info.mode == BleGlueC2ModeFUS) && (desired_mode == BleGlueC2ModeStack)) { + if((ble_glue->c2_info.VersionMajor == 0) && (ble_glue->c2_info.VersionMinor == 0)) { + FURI_LOG_W(TAG, "Stack isn't installed!"); + return BleGlueCommandResultError; + } SHCI_CmdStatus_t status = SHCI_C2_FUS_StartWs(); if(status) { FURI_LOG_E(TAG, "Failed to start Radio Stack with status: %02X", status); - } else { - ret = true; + return BleGlueCommandResultError; } + return BleGlueCommandResultRestartPending; } - return ret; + if((ble_glue->c2_info.mode == BleGlueC2ModeStack) && (desired_mode == BleGlueC2ModeFUS)) { + SHCI_FUS_GetState_ErrorCode_t error_code = 0; + uint8_t fus_state = SHCI_C2_FUS_GetState(&error_code); + FURI_LOG_D(TAG, "FUS state: %X, error = %x", fus_state, error_code); + if(fus_state == SHCI_FUS_CMD_NOT_SUPPORTED) { + // Second call to SHCI_C2_FUS_GetState() restarts whole MCU & boots FUS + fus_state = SHCI_C2_FUS_GetState(&error_code); + FURI_LOG_D(TAG, "FUS state#2: %X, error = %x", fus_state, error_code); + return BleGlueCommandResultRestartPending; + } + return BleGlueCommandResultOK; + } + + return BleGlueCommandResultError; } static void ble_glue_sys_status_not_callback(SHCI_TL_CmdStatus_t status) { @@ -221,19 +292,26 @@ static void ble_glue_sys_status_not_callback(SHCI_TL_CmdStatus_t status) { */ static void ble_glue_sys_user_event_callback(void* pPayload) { UNUSED(pPayload); - /* Traces channel initialization */ - // APPD_EnableCPU2( ); + +#ifdef BLE_GLUE_DEBUG + APPD_EnableCPU2(); +#endif TL_AsynchEvt_t* p_sys_event = (TL_AsynchEvt_t*)(((tSHCI_UserEvtRxParam*)pPayload)->pckt->evtserial.evt.payload); if(p_sys_event->subevtcode == SHCI_SUB_EVT_CODE_READY) { - FURI_LOG_I(TAG, "Fus started"); - ble_glue->status = BleGlueStatusFusStarted; - furi_hal_power_insomnia_exit(); + FURI_LOG_I(TAG, "Core2 started"); + SHCI_C2_Ready_Evt_t* p_c2_ready_evt = (SHCI_C2_Ready_Evt_t*)p_sys_event->payload; + if(p_c2_ready_evt->sysevt_ready_rsp == WIRELESS_FW_RUNNING) { + ble_glue->c2_info.mode = BleGlueC2ModeStack; + } else if(p_c2_ready_evt->sysevt_ready_rsp == FUS_FW_RUNNING) { + ble_glue->c2_info.mode = BleGlueC2ModeFUS; + } + + ble_glue->status = BleGlueStatusC2Started; } else if(p_sys_event->subevtcode == SHCI_SUB_EVT_ERROR_NOTIF) { FURI_LOG_E(TAG, "Error during initialization"); - furi_hal_power_insomnia_exit(); } else if(p_sys_event->subevtcode == SHCI_SUB_EVT_BLE_NVM_RAM_UPDATE) { SHCI_C2_BleNvmRamUpdate_Evt_t* p_sys_ble_nvm_ram_update_event = (SHCI_C2_BleNvmRamUpdate_Evt_t*)p_sys_event->payload; @@ -308,3 +386,61 @@ void shci_cmd_resp_wait(uint32_t timeout) { osSemaphoreAcquire(ble_glue->shci_sem, osWaitForever); } } + +bool ble_glue_reinit_c2() { + return SHCI_C2_Reinit() == SHCI_Success; +} + +BleGlueCommandResult ble_glue_fus_stack_delete() { + FURI_LOG_I(TAG, "Erasing stack"); + SHCI_CmdStatus_t erase_stat = SHCI_C2_FUS_FwDelete(); + FURI_LOG_I(TAG, "Cmd res = %x", erase_stat); + if(erase_stat == SHCI_Success) { + return BleGlueCommandResultOperationOngoing; + } + ble_glue_fus_get_status(); + return BleGlueCommandResultError; +} + +BleGlueCommandResult ble_glue_fus_stack_install(uint32_t src_addr, uint32_t dst_addr) { + FURI_LOG_I(TAG, "Installing stack"); + SHCI_CmdStatus_t write_stat = SHCI_C2_FUS_FwUpgrade(src_addr, dst_addr); + FURI_LOG_I(TAG, "Cmd res = %x", write_stat); + if(write_stat == SHCI_Success) { + return BleGlueCommandResultOperationOngoing; + } + ble_glue_fus_get_status(); + return BleGlueCommandResultError; +} + +BleGlueCommandResult ble_glue_fus_get_status() { + furi_check(ble_glue->c2_info.mode == BleGlueC2ModeFUS); + SHCI_FUS_GetState_ErrorCode_t error_code = 0; + uint8_t fus_state = SHCI_C2_FUS_GetState(&error_code); + FURI_LOG_I(TAG, "FUS state: %x, error: %x", fus_state, error_code); + if((error_code != 0) || (fus_state == FUS_STATE_VALUE_ERROR)) { + return BleGlueCommandResultError; + } else if( + (fus_state >= FUS_STATE_VALUE_FW_UPGRD_ONGOING) && + (fus_state <= FUS_STATE_VALUE_SERVICE_ONGOING_END)) { + return BleGlueCommandResultOperationOngoing; + } + return BleGlueCommandResultOK; +} + +BleGlueCommandResult ble_glue_fus_wait_operation() { + furi_check(ble_glue->c2_info.mode == BleGlueC2ModeFUS); + bool wip; + do { + BleGlueCommandResult fus_status = ble_glue_fus_get_status(); + if(fus_status == BleGlueCommandResultError) { + return BleGlueCommandResultError; + } + wip = fus_status == BleGlueCommandResultOperationOngoing; + if(wip) { + osDelay(20); + } + } while(wip); + + return BleGlueCommandResultOK; +} diff --git a/firmware/targets/f7/ble_glue/ble_glue.h b/firmware/targets/f7/ble_glue/ble_glue.h index 646caa8a..e5c2738d 100644 --- a/firmware/targets/f7/ble_glue/ble_glue.h +++ b/firmware/targets/f7/ble_glue/ble_glue.h @@ -2,12 +2,53 @@ #include #include -#include #ifdef __cplusplus extern "C" { #endif +typedef enum { + BleGlueC2ModeUnknown = 0, + BleGlueC2ModeFUS, + BleGlueC2ModeStack, +} BleGlueC2Mode; + +typedef struct { + BleGlueC2Mode mode; + /** + * Wireless Info + */ + uint8_t VersionMajor; + uint8_t VersionMinor; + uint8_t VersionSub; + uint8_t VersionBranch; + uint8_t VersionReleaseType; + uint8_t MemorySizeSram2B; /*< Multiple of 1K */ + uint8_t MemorySizeSram2A; /*< Multiple of 1K */ + uint8_t MemorySizeSram1; /*< Multiple of 1K */ + uint8_t MemorySizeFlash; /*< Multiple of 4K */ + uint8_t StackType; + /** + * Fus Info + */ + uint8_t FusVersionMajor; + uint8_t FusVersionMinor; + uint8_t FusVersionSub; + uint8_t FusMemorySizeSram2B; /*< Multiple of 1K */ + uint8_t FusMemorySizeSram2A; /*< Multiple of 1K */ + uint8_t FusMemorySizeFlash; /*< Multiple of 4K */ +} BleGlueC2Info; + +typedef enum { + // Stage 1: core2 startup and FUS + BleGlueStatusStartup, + BleGlueStatusBroken, + BleGlueStatusC2Started, + // Stage 2: radio stack + BleGlueStatusRadioStackRunning, + BleGlueStatusRadioStackMissing +} BleGlueStatus; + typedef void ( *BleGlueKeyStorageChangedCallback)(uint8_t* change_addr_start, uint16_t size, void* context); @@ -26,7 +67,15 @@ bool ble_glue_start(); */ bool ble_glue_is_alive(); -bool ble_glue_wait_for_fus_start(WirelessFwInfo_t* info); +/** Waits for C2 to reports its mode to callback + * + * @return true if it reported before reaching timeout + */ +bool ble_glue_wait_for_c2_start(int32_t timeout); + +BleGlueStatus ble_glue_get_c2_status(); + +const BleGlueC2Info* ble_glue_get_c2_info(); /** Is core2 radio stack present and ready * @@ -46,12 +95,30 @@ void ble_glue_set_key_storage_changed_callback( /** Stop SHCI thread */ void ble_glue_thread_stop(); +bool ble_glue_reinit_c2(); + +typedef enum { + BleGlueCommandResultUnknown, + BleGlueCommandResultOK, + BleGlueCommandResultError, + BleGlueCommandResultRestartPending, + BleGlueCommandResultOperationOngoing, +} BleGlueCommandResult; + /** Restart MCU to launch radio stack firmware if necessary * * @return true on radio stack start command */ -bool ble_glue_radio_stack_fw_launch_started(); +BleGlueCommandResult ble_glue_force_c2_mode(BleGlueC2Mode mode); + +BleGlueCommandResult ble_glue_fus_stack_delete(); + +BleGlueCommandResult ble_glue_fus_stack_install(uint32_t src_addr, uint32_t dst_addr); + +BleGlueCommandResult ble_glue_fus_get_status(); + +BleGlueCommandResult ble_glue_fus_wait_operation(); #ifdef __cplusplus } -#endif +#endif \ No newline at end of file diff --git a/firmware/targets/f7/ble_glue/gap.c b/firmware/targets/f7/ble_glue/gap.c index b21095ed..45191a5a 100644 --- a/firmware/targets/f7/ble_glue/gap.c +++ b/firmware/targets/f7/ble_glue/gap.c @@ -115,7 +115,6 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { if(gap->enable_adv) { // Restart advertising gap_advertise_start(GapStateAdvFast); - furi_hal_power_insomnia_exit(); } GapEvent event = {.type = GapEventTypeDisconnected}; gap->on_event_cb(event, gap->context); @@ -151,8 +150,7 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { } break; - case EVT_LE_CONN_COMPLETE: - furi_hal_power_insomnia_enter(); + case EVT_LE_CONN_COMPLETE: { hci_le_connection_complete_event_rp0* event = (hci_le_connection_complete_event_rp0*)meta_evt->data; gap->connection_params.conn_interval = event->Conn_Interval; @@ -169,7 +167,7 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { gap_verify_connection_parameters(gap); // Start pairing by sending security request aci_gap_slave_security_req(event->Connection_Handle); - break; + } break; case EVT_LE_ADVERTISING_REPORT: { if(gap_scan) { @@ -414,7 +412,9 @@ static void gap_advertise_start(GapState new_state) { // Stop advertising status = aci_gap_set_non_discoverable(); if(status) { - FURI_LOG_E(TAG, "Stop Advertising Failed, result: %d", status); + FURI_LOG_E(TAG, "set_non_discoverable failed %d", status); + } else { + FURI_LOG_D(TAG, "set_non_discoverable success"); } } // Configure advertising @@ -431,7 +431,7 @@ static void gap_advertise_start(GapState new_state) { 0, 0); if(status) { - FURI_LOG_E(TAG, "Set discoverable err: %d", status); + FURI_LOG_E(TAG, "set_discoverable failed %d", status); } gap->state = new_state; GapEvent event = {.type = GapEventTypeStartAdvertising}; @@ -440,14 +440,25 @@ static void gap_advertise_start(GapState new_state) { } static void gap_advertise_stop() { + tBleStatus ret; if(gap->state > GapStateIdle) { if(gap->state == GapStateConnected) { // Terminate connection - aci_gap_terminate(gap->service.connection_handle, 0x13); + ret = aci_gap_terminate(gap->service.connection_handle, 0x13); + if(ret != BLE_STATUS_SUCCESS) { + FURI_LOG_E(TAG, "terminate failed %d", ret); + } else { + FURI_LOG_D(TAG, "terminate success"); + } } // Stop advertising osTimerStop(gap->advertise_timer); - aci_gap_set_non_discoverable(); + ret = aci_gap_set_non_discoverable(); + if(ret != BLE_STATUS_SUCCESS) { + FURI_LOG_E(TAG, "set_non_discoverable failed %d", ret); + } else { + FURI_LOG_D(TAG, "set_non_discoverable success"); + } gap->state = GapStateIdle; } GapEvent event = {.type = GapEventTypeStopAdvertising}; diff --git a/firmware/targets/f7/furi_hal/furi_hal.c b/firmware/targets/f7/furi_hal/furi_hal.c index b3362ba8..e97c0e8c 100644 --- a/firmware/targets/f7/furi_hal/furi_hal.c +++ b/firmware/targets/f7/furi_hal/furi_hal.c @@ -10,10 +10,10 @@ void furi_hal_init_early() { furi_hal_clock_init_early(); furi_hal_delay_init(); - furi_hal_os_init(); - furi_hal_resources_init_early(); + furi_hal_os_init(); + furi_hal_spi_init_early(); furi_hal_i2c_init_early(); @@ -55,7 +55,6 @@ void furi_hal_init() { FURI_LOG_I(TAG, "Speaker OK"); furi_hal_crypto_init(); - furi_hal_crc_init(true); // USB #ifndef FURI_RAM_EXEC diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt.c b/firmware/targets/f7/furi_hal/furi_hal_bt.c index a8b85495..48d69844 100755 --- a/firmware/targets/f7/furi_hal/furi_hal_bt.c +++ b/firmware/targets/f7/furi_hal/furi_hal_bt.c @@ -16,6 +16,9 @@ #define FURI_HAL_BT_DEFAULT_MAC_ADDR \ { 0x6c, 0x7a, 0xd8, 0xac, 0x57, 0x72 } +/* Time, in ms, to wait for mode transition before crashing */ +#define C2_MODE_SWITCH_TIMEOUT 10000 + osMutexId_t furi_hal_bt_core2_mtx = NULL; static FuriHalBtStack furi_hal_bt_stack = FuriHalBtStackUnknown; @@ -99,7 +102,7 @@ void furi_hal_bt_unlock_core2() { furi_check(osMutexRelease(furi_hal_bt_core2_mtx) == osOK); } -static bool furi_hal_bt_radio_stack_is_supported(WirelessFwInfo_t* info) { +static bool furi_hal_bt_radio_stack_is_supported(const BleGlueC2Info* info) { bool supported = false; if(info->StackType == INFO_STACK_TYPE_BLE_HCI) { furi_hal_bt_stack = FuriHalBtStackHciLayer; @@ -128,21 +131,21 @@ bool furi_hal_bt_start_radio_stack() { } do { - // Wait until FUS is started or timeout - WirelessFwInfo_t info = {}; - if(!ble_glue_wait_for_fus_start(&info)) { - FURI_LOG_E(TAG, "FUS start failed"); - LL_C2_PWR_SetPowerMode(LL_PWR_MODE_SHUTDOWN); + // Wait until C2 is started or timeout + if(!ble_glue_wait_for_c2_start(FURI_HAL_BT_C2_START_TIMEOUT)) { + FURI_LOG_E(TAG, "Core2 start failed"); ble_glue_thread_stop(); break; } - // If FUS is running, start radio stack fw - if(ble_glue_radio_stack_fw_launch_started()) { - // If FUS is running do nothing and wait for system reset - furi_crash("Waiting for FUS to launch radio stack firmware"); + + // If C2 is running, start radio stack fw + if(!furi_hal_bt_ensure_c2_mode(BleGlueC2ModeStack)) { + break; } - // Check weather we support radio stack - if(!furi_hal_bt_radio_stack_is_supported(&info)) { + + // Check whether we support radio stack + const BleGlueC2Info* c2_info = ble_glue_get_c2_info(); + if(!furi_hal_bt_radio_stack_is_supported(c2_info)) { FURI_LOG_E(TAG, "Unsupported radio stack"); // Don't stop SHCI for crypto enclave support break; @@ -150,7 +153,6 @@ bool furi_hal_bt_start_radio_stack() { // Starting radio stack if(!ble_glue_start()) { FURI_LOG_E(TAG, "Failed to start radio stack"); - LL_C2_PWR_SetPowerMode(LL_PWR_MODE_SHUTDOWN); ble_glue_thread_stop(); ble_app_thread_stop(); break; @@ -217,27 +219,39 @@ bool furi_hal_bt_start_app(FuriHalBtProfile profile, GapEventCallback event_cb, return ret; } +void furi_hal_bt_reinit() { + FURI_LOG_I(TAG, "Disconnect and stop advertising"); + furi_hal_bt_stop_advertising(); + + FURI_LOG_I(TAG, "Stop current profile services"); + current_profile->stop(); + + // Magic happens here + hci_reset(); + + FURI_LOG_I(TAG, "Stop BLE related RTOS threads"); + ble_app_thread_stop(); + gap_thread_stop(); + + FURI_LOG_I(TAG, "Reset SHCI"); + furi_check(ble_glue_reinit_c2()); + + osDelay(100); + ble_glue_thread_stop(); + + FURI_LOG_I(TAG, "Start BT initialization"); + furi_hal_bt_init(); + + furi_hal_bt_start_radio_stack(); +} + bool furi_hal_bt_change_app(FuriHalBtProfile profile, GapEventCallback event_cb, void* context) { furi_assert(event_cb); furi_assert(profile < FuriHalBtProfileNumber); bool ret = true; - FURI_LOG_I(TAG, "Stop current profile services"); - current_profile->stop(); - FURI_LOG_I(TAG, "Disconnect and stop advertising"); - furi_hal_bt_stop_advertising(); - FURI_LOG_I(TAG, "Shutdow 2nd core"); - LL_C2_PWR_SetPowerMode(LL_PWR_MODE_SHUTDOWN); - FURI_LOG_I(TAG, "Stop BLE related RTOS threads"); - ble_app_thread_stop(); - gap_thread_stop(); - FURI_LOG_I(TAG, "Reset SHCI"); - SHCI_C2_Reinit(); - osDelay(100); - ble_glue_thread_stop(); - FURI_LOG_I(TAG, "Start BT initialization"); - furi_hal_bt_init(); - furi_hal_bt_start_radio_stack(); + furi_hal_bt_reinit(); + ret = furi_hal_bt_start_app(profile, event_cb, context); if(ret) { current_profile = &profile_config[profile]; @@ -404,3 +418,18 @@ void furi_hal_bt_stop_scan() { gap_stop_scan(); } } + +bool furi_hal_bt_ensure_c2_mode(BleGlueC2Mode mode) { + BleGlueCommandResult fw_start_res = ble_glue_force_c2_mode(mode); + if(fw_start_res == BleGlueCommandResultOK) { + return true; + } else if(fw_start_res == BleGlueCommandResultRestartPending) { + // Do nothing and wait for system reset + osDelay(C2_MODE_SWITCH_TIMEOUT); + furi_crash("Waiting for FUS->radio stack transition"); + return true; + } + + FURI_LOG_E(TAG, "Failed to switch C2 mode: %d", fw_start_res); + return false; +} diff --git a/firmware/targets/f7/furi_hal/furi_hal_clock.c b/firmware/targets/f7/furi_hal/furi_hal_clock.c index 02264422..09efe76d 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_clock.c +++ b/firmware/targets/f7/furi_hal/furi_hal_clock.c @@ -204,6 +204,8 @@ void furi_hal_clock_switch_to_hsi() { while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_HSI) ; + + LL_FLASH_SetLatency(LL_FLASH_LATENCY_1); } void furi_hal_clock_switch_to_pll() { @@ -215,6 +217,8 @@ void furi_hal_clock_switch_to_pll() { while(!LL_RCC_PLL_IsReady()) ; + LL_FLASH_SetLatency(LL_FLASH_LATENCY_3); + LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_PLL); LL_RCC_SetSMPSClockSource(LL_RCC_SMPS_CLKSOURCE_HSE); diff --git a/firmware/targets/f7/furi_hal/furi_hal_crc.c b/firmware/targets/f7/furi_hal/furi_hal_crc.c index 321809d9..322a83bb 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_crc.c +++ b/firmware/targets/f7/furi_hal/furi_hal_crc.c @@ -34,6 +34,7 @@ void furi_hal_crc_init(bool synchronize) { void furi_hal_crc_reset() { furi_check(hal_crc_control.state == CRC_State_Ready); if(hal_crc_control.mtx) { + furi_check(osMutexGetOwner(hal_crc_control.mtx) == osThreadGetId()); osMutexRelease(hal_crc_control.mtx); } LL_CRC_ResetCRCCalculationUnit(CRC); @@ -84,5 +85,9 @@ uint32_t furi_hal_crc_feed(void* data, uint16_t length) { bool furi_hal_crc_acquire(uint32_t timeout) { furi_assert(hal_crc_control.mtx); - return osMutexAcquire(hal_crc_control.mtx, timeout) == osOK; + if(osMutexAcquire(hal_crc_control.mtx, timeout) == osOK) { + LL_CRC_ResetCRCCalculationUnit(CRC); + return true; + } + return false; } diff --git a/firmware/targets/f7/furi_hal/furi_hal_debug.c b/firmware/targets/f7/furi_hal/furi_hal_debug.c new file mode 100644 index 00000000..3b5dfe62 --- /dev/null +++ b/firmware/targets/f7/furi_hal/furi_hal_debug.c @@ -0,0 +1,20 @@ +#include + +#include +#include + +void furi_hal_debug_enable() { + // Low power mode debug + LL_DBGMCU_EnableDBGSleepMode(); + LL_DBGMCU_EnableDBGStopMode(); + LL_DBGMCU_EnableDBGStandbyMode(); + LL_EXTI_EnableIT_32_63(LL_EXTI_LINE_48); +} + +void furi_hal_debug_disable() { + // Low power mode debug + LL_DBGMCU_DisableDBGSleepMode(); + LL_DBGMCU_DisableDBGStopMode(); + LL_DBGMCU_DisableDBGStandbyMode(); + LL_EXTI_DisableIT_32_63(LL_EXTI_LINE_48); +} diff --git a/firmware/targets/f7/furi_hal/furi_hal_flash.c b/firmware/targets/f7/furi_hal/furi_hal_flash.c index aaf7f1a2..1d9c3fd9 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_flash.c +++ b/firmware/targets/f7/furi_hal/furi_hal_flash.c @@ -6,7 +6,8 @@ #include -#define FURI_HAL_TAG "FuriHalFlash" +#define TAG "FuriHalFlash" + #define FURI_HAL_CRITICAL_MSG "Critical flash operation fail" #define FURI_HAL_FLASH_READ_BLOCK 8 #define FURI_HAL_FLASH_WRITE_BLOCK 8 @@ -14,13 +15,17 @@ #define FURI_HAL_FLASH_CYCLES_COUNT 10000 #define FURI_HAL_FLASH_TIMEOUT 1000 #define FURI_HAL_FLASH_KEY1 0x45670123U - #define FURI_HAL_FLASH_KEY2 0xCDEF89ABU #define FURI_HAL_FLASH_TOTAL_PAGES 256 #define FURI_HAL_FLASH_SR_ERRORS \ (FLASH_SR_OPERR | FLASH_SR_PROGERR | FLASH_SR_WRPERR | FLASH_SR_PGAERR | FLASH_SR_SIZERR | \ FLASH_SR_PGSERR | FLASH_SR_MISERR | FLASH_SR_FASTERR | FLASH_SR_RDERR | FLASH_SR_OPTVERR) +//#define FURI_HAL_FLASH_OB_START_ADDRESS 0x1FFF8000 +#define FURI_HAL_FLASH_OPT_KEY1 0x08192A3B +#define FURI_HAL_FLASH_OPT_KEY2 0x4C5D6E7F +#define FURI_HAL_FLASH_OB_TOTAL_WORDS (0x80 / (sizeof(uint32_t) * 2)) + #define IS_ADDR_ALIGNED_64BITS(__VALUE__) (((__VALUE__)&0x7U) == (0x00UL)) #define IS_FLASH_PROGRAM_ADDRESS(__VALUE__) \ (((__VALUE__) >= FLASH_BASE) && ((__VALUE__) <= (FLASH_BASE + FLASH_SIZE - 8UL)) && \ @@ -88,7 +93,7 @@ static void furi_hal_flash_unlock() { WRITE_REG(FLASH->KEYR, FURI_HAL_FLASH_KEY1); WRITE_REG(FLASH->KEYR, FURI_HAL_FLASH_KEY2); - /* verify Flash is unlock */ + /* verify Flash is unlocked */ furi_check(READ_BIT(FLASH->CR, FLASH_CR_LOCK) == 0U); } @@ -386,4 +391,125 @@ int16_t furi_hal_flash_get_page_number(size_t address) { } return (address - flash_base) / FURI_HAL_FLASH_PAGE_SIZE; -} \ No newline at end of file +} + +uint32_t furi_hal_flash_ob_get_word(size_t word_idx, bool complementary) { + furi_check(word_idx <= FURI_HAL_FLASH_OB_TOTAL_WORDS); + const uint32_t* ob_data = (const uint32_t*)(OPTION_BYTE_BASE); + size_t raw_word_idx = word_idx * 2; + if(complementary) { + raw_word_idx += 1; + } + return ob_data[raw_word_idx]; +} + +void furi_hal_flash_ob_unlock() { + furi_check(READ_BIT(FLASH->CR, FLASH_CR_OPTLOCK) != 0U); + furi_hal_flash_begin(true); + WRITE_REG(FLASH->OPTKEYR, FURI_HAL_FLASH_OPT_KEY1); + __ISB(); + WRITE_REG(FLASH->OPTKEYR, FURI_HAL_FLASH_OPT_KEY2); + /* verify OB area is unlocked */ + furi_check(READ_BIT(FLASH->CR, FLASH_CR_OPTLOCK) == 0U); +} + +void furi_hal_flash_ob_lock() { + furi_check(READ_BIT(FLASH->CR, FLASH_CR_OPTLOCK) == 0U); + SET_BIT(FLASH->CR, FLASH_CR_OPTLOCK); + furi_hal_flash_end(true); + furi_check(READ_BIT(FLASH->CR, FLASH_CR_OPTLOCK) != 0U); +} + +typedef enum { + FuriHalFlashObInvalid, + FuriHalFlashObRegisterUserRead, + FuriHalFlashObRegisterPCROP1AStart, + FuriHalFlashObRegisterPCROP1AEnd, + FuriHalFlashObRegisterWRPA, + FuriHalFlashObRegisterWRPB, + FuriHalFlashObRegisterPCROP1BStart, + FuriHalFlashObRegisterPCROP1BEnd, + FuriHalFlashObRegisterIPCCMail, + FuriHalFlashObRegisterSecureFlash, + FuriHalFlashObRegisterC2Opts, +} FuriHalFlashObRegister; + +typedef struct { + FuriHalFlashObRegister ob_reg; + uint32_t* ob_register_address; +} FuriHalFlashObMapping; + +#define OB_REG_DEF(INDEX, REG) \ + { .ob_reg = INDEX, .ob_register_address = (uint32_t*)(REG) } + +static const FuriHalFlashObMapping furi_hal_flash_ob_reg_map[FURI_HAL_FLASH_OB_TOTAL_WORDS] = { + OB_REG_DEF(FuriHalFlashObRegisterUserRead, (&FLASH->OPTR)), + OB_REG_DEF(FuriHalFlashObRegisterPCROP1AStart, (&FLASH->PCROP1ASR)), + OB_REG_DEF(FuriHalFlashObRegisterPCROP1AEnd, (&FLASH->PCROP1AER)), + OB_REG_DEF(FuriHalFlashObRegisterWRPA, (&FLASH->WRP1AR)), + OB_REG_DEF(FuriHalFlashObRegisterWRPB, (&FLASH->WRP1BR)), + OB_REG_DEF(FuriHalFlashObRegisterPCROP1BStart, (&FLASH->PCROP1BSR)), + OB_REG_DEF(FuriHalFlashObRegisterPCROP1BEnd, (&FLASH->PCROP1BER)), + + OB_REG_DEF(FuriHalFlashObInvalid, (NULL)), + OB_REG_DEF(FuriHalFlashObInvalid, (NULL)), + OB_REG_DEF(FuriHalFlashObInvalid, (NULL)), + OB_REG_DEF(FuriHalFlashObInvalid, (NULL)), + OB_REG_DEF(FuriHalFlashObInvalid, (NULL)), + OB_REG_DEF(FuriHalFlashObInvalid, (NULL)), + + OB_REG_DEF(FuriHalFlashObRegisterIPCCMail, (NULL)), + OB_REG_DEF(FuriHalFlashObRegisterSecureFlash, (NULL)), + OB_REG_DEF(FuriHalFlashObRegisterC2Opts, (NULL)), +}; + +void furi_hal_flash_ob_apply() { + furi_hal_flash_ob_unlock(); + /* OBL_LAUNCH: When set to 1, this bit forces the option byte reloading. + * It cannot be written if OPTLOCK is set */ + SET_BIT(FLASH->CR, FLASH_CR_OBL_LAUNCH); + furi_check(furi_hal_flash_wait_last_operation(FURI_HAL_FLASH_TIMEOUT)); + furi_hal_flash_ob_lock(); +} + +bool furi_hal_flash_ob_set_word(size_t word_idx, const uint32_t value) { + furi_check(word_idx < FURI_HAL_FLASH_OB_TOTAL_WORDS); + + const FuriHalFlashObMapping* reg_def = &furi_hal_flash_ob_reg_map[word_idx]; + if(reg_def->ob_register_address == NULL) { + FURI_LOG_E(TAG, "Attempt to set RO OB word %d", word_idx); + return false; + } + + FURI_LOG_W( + TAG, + "Setting OB reg %d for word %d (addr 0x%08X) to 0x%08X", + reg_def->ob_reg, + word_idx, + reg_def->ob_register_address, + value); + + /* 1. Clear OPTLOCK option lock bit with the clearing sequence */ + furi_hal_flash_ob_unlock(); + + /* 2. Write the desired options value in the options registers */ + *reg_def->ob_register_address = value; + + /* 3. Check that no Flash memory operation is on going by checking the BSY && PESD */ + furi_check(furi_hal_flash_wait_last_operation(FURI_HAL_FLASH_TIMEOUT)); + while(LL_FLASH_IsActiveFlag_OperationSuspended()) { + osThreadYield(); + }; + + /* 4. Set the Options start bit OPTSTRT */ + SET_BIT(FLASH->CR, FLASH_CR_OPTSTRT); + + /* 5. Wait for the BSY bit to be cleared. */ + furi_check(furi_hal_flash_wait_last_operation(FURI_HAL_FLASH_TIMEOUT)); + furi_hal_flash_ob_lock(); + return true; +} + +const FuriHalFlashRawOptionByteData* furi_hal_flash_ob_get_raw_ptr() { + return (const FuriHalFlashRawOptionByteData*)OPTION_BYTE_BASE; +} diff --git a/firmware/targets/f7/furi_hal/furi_hal_flash.h b/firmware/targets/f7/furi_hal/furi_hal_flash.h index 004c1340..eac80d95 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_flash.h +++ b/firmware/targets/f7/furi_hal/furi_hal_flash.h @@ -4,6 +4,25 @@ #include #include +#define FURI_HAL_FLASH_OB_RAW_SIZE_BYTES 0x80 +#define FURI_HAL_FLASH_OB_SIZE_WORDS (FURI_HAL_FLASH_OB_RAW_SIZE_BYTES / sizeof(uint32_t)) +#define FURI_HAL_FLASH_OB_TOTAL_VALUES (FURI_HAL_FLASH_OB_SIZE_WORDS / 2) + +typedef union { + uint8_t bytes[FURI_HAL_FLASH_OB_RAW_SIZE_BYTES]; + union { + struct { + uint32_t base; + uint32_t complementary_value; + } values; + uint64_t dword; + } obs[FURI_HAL_FLASH_OB_TOTAL_VALUES]; +} FuriHalFlashRawOptionByteData; + +_Static_assert( + sizeof(FuriHalFlashRawOptionByteData) == FURI_HAL_FLASH_OB_RAW_SIZE_BYTES, + "UpdateManifestOptionByteData size error"); + /** Init flash, applying necessary workarounds */ void furi_hal_flash_init(); @@ -64,7 +83,7 @@ size_t furi_hal_flash_get_free_page_count(); /** Erase Flash * - * @warning locking operation with critical section, stales execution + * @warning locking operation with critical section, stalls execution * * @param page The page to erase * @@ -74,7 +93,7 @@ bool furi_hal_flash_erase(uint8_t page); /** Write double word (64 bits) * - * @warning locking operation with critical section, stales execution + * @warning locking operation with critical section, stalls execution * * @param address destination address, must be double word aligned. * @param data data to write @@ -85,7 +104,7 @@ bool furi_hal_flash_write_dword(size_t address, uint64_t data); /** Write aligned page data (up to page size) * - * @warning locking operation with critical section, stales execution + * @warning locking operation with critical section, stalls execution * * @param address destination address, must be page aligned. * @param data data to write @@ -99,5 +118,27 @@ bool furi_hal_flash_program_page(const uint8_t page, const uint8_t* data, uint16 * * @return page number, -1 for invalid address */ +int16_t furi_hal_flash_get_page_number(size_t address); -int16_t furi_hal_flash_get_page_number(size_t address); \ No newline at end of file +/** Writes OB word, using non-compl. index of register in Flash, OPTION_BYTE_BASE + * + * @warning locking operation with critical section, stalls execution + * + * @param word_idx OB word number + * @param value data to write + * @return true if value was written, false for read-only word + */ +bool furi_hal_flash_ob_set_word(size_t word_idx, const uint32_t value); + +/** Forces a reload of OB data from flash to registers + * + * @warning Initializes system restart + * + */ +void furi_hal_flash_ob_apply(); + +/** Get raw OB storage data + * + * @return pointer to read-only data of OB (raw + complementary values) + */ +const FuriHalFlashRawOptionByteData* furi_hal_flash_ob_get_raw_ptr(); diff --git a/firmware/targets/f7/furi_hal/furi_hal_i2c.c b/firmware/targets/f7/furi_hal/furi_hal_i2c.c index 7e0bee58..fdef1127 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_i2c.c +++ b/firmware/targets/f7/furi_hal/furi_hal_i2c.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include @@ -23,6 +24,7 @@ void furi_hal_i2c_init() { } void furi_hal_i2c_acquire(FuriHalI2cBusHandle* handle) { + furi_hal_power_insomnia_enter(); // Lock bus access handle->bus->callback(handle->bus, FuriHalI2cBusEventLock); // Ensuree that no active handle set @@ -46,6 +48,7 @@ void furi_hal_i2c_release(FuriHalI2cBusHandle* handle) { handle->bus->current_handle = NULL; // Unlock bus handle->bus->callback(handle->bus, FuriHalI2cBusEventUnlock); + furi_hal_power_insomnia_exit(); } bool furi_hal_i2c_tx( diff --git a/firmware/targets/f7/furi_hal/furi_hal_idle_timer.h b/firmware/targets/f7/furi_hal/furi_hal_idle_timer.h index 61afd631..36b45755 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_idle_timer.h +++ b/firmware/targets/f7/furi_hal/furi_hal_idle_timer.h @@ -7,12 +7,14 @@ // Timer used for tickless idle #define FURI_HAL_IDLE_TIMER_MAX 0xFFFF -#define FURI_HAL_IDLE_TIMER LPTIM2 -#define FURI_HAL_IDLE_TIMER_IRQ LPTIM2_IRQn +#define FURI_HAL_IDLE_TIMER LPTIM1 +#define FURI_HAL_IDLE_TIMER_IRQ LPTIM1_IRQn static inline void furi_hal_idle_timer_init() { // Configure clock source - LL_RCC_SetLPTIMClockSource(LL_RCC_LPTIM2_CLKSOURCE_LSE); + LL_RCC_SetLPTIMClockSource(LL_RCC_LPTIM1_CLKSOURCE_LSE); + // There is a theoretical possibility that we need it + LL_APB1_GRP1_EnableClockSleep(LL_APB1_GRP1_PERIPH_LPTIM1); // Set interrupt priority and enable them NVIC_SetPriority( FURI_HAL_IDLE_TIMER_IRQ, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 15, 0)); diff --git a/firmware/targets/f7/furi_hal/furi_hal_info.c b/firmware/targets/f7/furi_hal/furi_hal_info.c index 77eab128..bbb3907d 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_info.c +++ b/firmware/targets/f7/furi_hal/furi_hal_info.c @@ -66,44 +66,45 @@ void furi_hal_info_get(FuriHalInfoValueCallback out, void* context) { out("firmware_target", string_get_cstr(value), false, context); } - WirelessFwInfo_t pWirelessInfo; - if(furi_hal_bt_is_alive() && SHCI_GetWirelessFwInfo(&pWirelessInfo) == SHCI_Success) { + if(furi_hal_bt_is_alive()) { + const BleGlueC2Info* ble_c2_info = ble_glue_get_c2_info(); out("radio_alive", "true", false, context); + out("radio_mode", ble_c2_info->mode == BleGlueC2ModeFUS ? "FUS" : "Stack", false, context); // FUS Info - string_printf(value, "%d", pWirelessInfo.FusVersionMajor); + string_printf(value, "%d", ble_c2_info->FusVersionMajor); out("radio_fus_major", string_get_cstr(value), false, context); - string_printf(value, "%d", pWirelessInfo.FusVersionMinor); + string_printf(value, "%d", ble_c2_info->FusVersionMinor); out("radio_fus_minor", string_get_cstr(value), false, context); - string_printf(value, "%d", pWirelessInfo.FusVersionSub); + string_printf(value, "%d", ble_c2_info->FusVersionSub); out("radio_fus_sub", string_get_cstr(value), false, context); - string_printf(value, "%dK", pWirelessInfo.FusMemorySizeSram2B); + string_printf(value, "%dK", ble_c2_info->FusMemorySizeSram2B); out("radio_fus_sram2b", string_get_cstr(value), false, context); - string_printf(value, "%dK", pWirelessInfo.FusMemorySizeSram2A); + string_printf(value, "%dK", ble_c2_info->FusMemorySizeSram2A); out("radio_fus_sram2a", string_get_cstr(value), false, context); - string_printf(value, "%dK", pWirelessInfo.FusMemorySizeFlash * 4); + string_printf(value, "%dK", ble_c2_info->FusMemorySizeFlash * 4); out("radio_fus_flash", string_get_cstr(value), false, context); // Stack Info - string_printf(value, "%d", pWirelessInfo.StackType); + string_printf(value, "%d", ble_c2_info->StackType); out("radio_stack_type", string_get_cstr(value), false, context); - string_printf(value, "%d", pWirelessInfo.VersionMajor); + string_printf(value, "%d", ble_c2_info->VersionMajor); out("radio_stack_major", string_get_cstr(value), false, context); - string_printf(value, "%d", pWirelessInfo.VersionMinor); + string_printf(value, "%d", ble_c2_info->VersionMinor); out("radio_stack_minor", string_get_cstr(value), false, context); - string_printf(value, "%d", pWirelessInfo.VersionSub); + string_printf(value, "%d", ble_c2_info->VersionSub); out("radio_stack_sub", string_get_cstr(value), false, context); - string_printf(value, "%d", pWirelessInfo.VersionBranch); + string_printf(value, "%d", ble_c2_info->VersionBranch); out("radio_stack_branch", string_get_cstr(value), false, context); - string_printf(value, "%d", pWirelessInfo.VersionReleaseType); + string_printf(value, "%d", ble_c2_info->VersionReleaseType); out("radio_stack_release", string_get_cstr(value), false, context); - string_printf(value, "%dK", pWirelessInfo.MemorySizeSram2B); + string_printf(value, "%dK", ble_c2_info->MemorySizeSram2B); out("radio_stack_sram2b", string_get_cstr(value), false, context); - string_printf(value, "%dK", pWirelessInfo.MemorySizeSram2A); + string_printf(value, "%dK", ble_c2_info->MemorySizeSram2A); out("radio_stack_sram2a", string_get_cstr(value), false, context); - string_printf(value, "%dK", pWirelessInfo.MemorySizeSram1); + string_printf(value, "%dK", ble_c2_info->MemorySizeSram1); out("radio_stack_sram1", string_get_cstr(value), false, context); - string_printf(value, "%dK", pWirelessInfo.MemorySizeFlash * 4); + string_printf(value, "%dK", ble_c2_info->MemorySizeFlash * 4); out("radio_stack_flash", string_get_cstr(value), false, context); // Mac address diff --git a/firmware/targets/f7/furi_hal/furi_hal_interrupt.c b/firmware/targets/f7/furi_hal/furi_hal_interrupt.c index d122fbc7..526c9f3d 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_interrupt.c +++ b/firmware/targets/f7/furi_hal/furi_hal_interrupt.c @@ -54,6 +54,10 @@ const IRQn_Type furi_hal_interrupt_irqn[FuriHalInterruptIdMax] = { // HSEM [FuriHalInterruptIdHsem] = HSEM_IRQn, + + // LPTIMx + [FuriHalInterruptIdLpTim1] = LPTIM1_IRQn, + [FuriHalInterruptIdLpTim2] = LPTIM2_IRQn, }; __attribute__((always_inline)) static inline void @@ -82,6 +86,16 @@ void furi_hal_interrupt_init() { NVIC_SetPriority(PendSV_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 15, 0)); + NVIC_SetPriority(FPU_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 15, 0)); + NVIC_EnableIRQ(FPU_IRQn); + + LL_SYSCFG_DisableIT_FPU_IOC(); + LL_SYSCFG_DisableIT_FPU_DZC(); + LL_SYSCFG_DisableIT_FPU_UFC(); + LL_SYSCFG_DisableIT_FPU_OFC(); + LL_SYSCFG_DisableIT_FPU_IDC(); + LL_SYSCFG_DisableIT_FPU_IXC(); + FURI_LOG_I(TAG, "Init OK"); } @@ -120,82 +134,82 @@ void furi_hal_interrupt_set_isr_ex( } /* Timer 2 */ -void TIM2_IRQHandler(void) { +void TIM2_IRQHandler() { furi_hal_interrupt_call(FuriHalInterruptIdTIM2); } /* Timer 1 Update */ -void TIM1_UP_TIM16_IRQHandler(void) { +void TIM1_UP_TIM16_IRQHandler() { furi_hal_interrupt_call(FuriHalInterruptIdTim1UpTim16); } -void TIM1_TRG_COM_TIM17_IRQHandler(void) { +void TIM1_TRG_COM_TIM17_IRQHandler() { furi_hal_interrupt_call(FuriHalInterruptIdTim1TrgComTim17); } -void TIM1_CC_IRQHandler(void) { +void TIM1_CC_IRQHandler() { furi_hal_interrupt_call(FuriHalInterruptIdTim1Cc); } /* DMA 1 */ -void DMA1_Channel1_IRQHandler(void) { +void DMA1_Channel1_IRQHandler() { furi_hal_interrupt_call(FuriHalInterruptIdDma1Ch1); } -void DMA1_Channel2_IRQHandler(void) { +void DMA1_Channel2_IRQHandler() { furi_hal_interrupt_call(FuriHalInterruptIdDma1Ch2); } -void DMA1_Channel3_IRQHandler(void) { +void DMA1_Channel3_IRQHandler() { furi_hal_interrupt_call(FuriHalInterruptIdDma1Ch3); } -void DMA1_Channel4_IRQHandler(void) { +void DMA1_Channel4_IRQHandler() { furi_hal_interrupt_call(FuriHalInterruptIdDma1Ch4); } -void DMA1_Channel5_IRQHandler(void) { +void DMA1_Channel5_IRQHandler() { furi_hal_interrupt_call(FuriHalInterruptIdDma1Ch5); } -void DMA1_Channel6_IRQHandler(void) { +void DMA1_Channel6_IRQHandler() { furi_hal_interrupt_call(FuriHalInterruptIdDma1Ch6); } -void DMA1_Channel7_IRQHandler(void) { +void DMA1_Channel7_IRQHandler() { furi_hal_interrupt_call(FuriHalInterruptIdDma1Ch7); } /* DMA 2 */ -void DMA2_Channel1_IRQHandler(void) { +void DMA2_Channel1_IRQHandler() { furi_hal_interrupt_call(FuriHalInterruptIdDma2Ch1); } -void DMA2_Channel2_IRQHandler(void) { +void DMA2_Channel2_IRQHandler() { furi_hal_interrupt_call(FuriHalInterruptIdDma2Ch2); } -void DMA2_Channel3_IRQHandler(void) { +void DMA2_Channel3_IRQHandler() { furi_hal_interrupt_call(FuriHalInterruptIdDma2Ch3); } -void DMA2_Channel4_IRQHandler(void) { +void DMA2_Channel4_IRQHandler() { furi_hal_interrupt_call(FuriHalInterruptIdDma2Ch4); } -void DMA2_Channel5_IRQHandler(void) { +void DMA2_Channel5_IRQHandler() { furi_hal_interrupt_call(FuriHalInterruptIdDma2Ch5); } -void DMA2_Channel6_IRQHandler(void) { +void DMA2_Channel6_IRQHandler() { furi_hal_interrupt_call(FuriHalInterruptIdDma2Ch6); } -void DMA2_Channel7_IRQHandler(void) { +void DMA2_Channel7_IRQHandler() { furi_hal_interrupt_call(FuriHalInterruptIdDma2Ch7); } -void HSEM_IRQHandler(void) { +void HSEM_IRQHandler() { furi_hal_interrupt_call(FuriHalInterruptIdHsem); } @@ -211,11 +225,11 @@ void TAMP_STAMP_LSECSS_IRQHandler(void) { } } -void RCC_IRQHandler(void) { +void RCC_IRQHandler() { furi_hal_interrupt_call(FuriHalInterruptIdRcc); } -void NMI_Handler(void) { +void NMI_Handler() { if(LL_RCC_IsActiveFlag_HSECSS()) { LL_RCC_ClearFlag_HSECSS(); FURI_LOG_E(TAG, "HSE CSS fired: resetting system"); @@ -223,23 +237,23 @@ void NMI_Handler(void) { } } -void HardFault_Handler(void) { +void HardFault_Handler() { furi_crash("HardFault"); } -void MemManage_Handler(void) { +void MemManage_Handler() { furi_crash("MemManage"); } -void BusFault_Handler(void) { +void BusFault_Handler() { furi_crash("BusFault"); } -void UsageFault_Handler(void) { +void UsageFault_Handler() { furi_crash("UsageFault"); } -void DebugMon_Handler(void) { +void DebugMon_Handler() { } #include "usbd_core.h" @@ -249,20 +263,35 @@ extern usbd_device udev; extern void HW_IPCC_Tx_Handler(); extern void HW_IPCC_Rx_Handler(); -void SysTick_Handler(void) { +void SysTick_Handler() { furi_hal_os_tick(); } -void USB_LP_IRQHandler(void) { +void USB_LP_IRQHandler() { #ifndef FURI_RAM_EXEC usbd_poll(&udev); #endif } -void IPCC_C1_TX_IRQHandler(void) { +void USB_HP_IRQHandler() { +} + +void IPCC_C1_TX_IRQHandler() { HW_IPCC_Tx_Handler(); } -void IPCC_C1_RX_IRQHandler(void) { +void IPCC_C1_RX_IRQHandler() { HW_IPCC_Rx_Handler(); } + +void FPU_IRQHandler() { + furi_crash("FpuFault"); +} + +void LPTIM1_IRQHandler() { + furi_hal_interrupt_call(FuriHalInterruptIdLpTim1); +} + +void LPTIM2_IRQHandler() { + furi_hal_interrupt_call(FuriHalInterruptIdLpTim2); +} diff --git a/firmware/targets/f7/furi_hal/furi_hal_interrupt.h b/firmware/targets/f7/furi_hal/furi_hal_interrupt.h index 725e779e..8a280ff8 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_interrupt.h +++ b/firmware/targets/f7/furi_hal/furi_hal_interrupt.h @@ -45,6 +45,10 @@ typedef enum { // HSEM FuriHalInterruptIdHsem, + // LPTIMx + FuriHalInterruptIdLpTim1, + FuriHalInterruptIdLpTim2, + // Service value FuriHalInterruptIdMax, } FuriHalInterruptId; diff --git a/firmware/targets/f7/furi_hal/furi_hal_os.c b/firmware/targets/f7/furi_hal/furi_hal_os.c index e282645b..8beb0d08 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_os.c +++ b/firmware/targets/f7/furi_hal/furi_hal_os.c @@ -1,8 +1,10 @@ #include #include #include -#include +#include +#include #include + #include #include @@ -18,32 +20,32 @@ #define FURI_HAL_IDLE_TIMER_TICK_PER_EPOCH (FURI_HAL_OS_IDLE_CNT_TO_TICKS(FURI_HAL_IDLE_TIMER_MAX)) #define FURI_HAL_OS_MAX_SLEEP (FURI_HAL_IDLE_TIMER_TICK_PER_EPOCH - 1) +#define FURI_HAL_OS_NVIC_IS_PENDING() (NVIC->ISPR[0] || NVIC->ISPR[1]) +#define FURI_HAL_OS_EXTI_LINE_0_31 0 +#define FURI_HAL_OS_EXTI_LINE_32_63 1 + +// Arbitrary (but small) number for better tick consistency +#define FURI_HAL_OS_EXTRA_CNT 3 + #ifdef FURI_HAL_OS_DEBUG #include -#define LED_SLEEP_PORT GPIOA -#define LED_SLEEP_PIN LL_GPIO_PIN_7 -#define LED_TICK_PORT GPIOA -#define LED_TICK_PIN LL_GPIO_PIN_6 -#define LED_SECOND_PORT GPIOA -#define LED_SECOND_PIN LL_GPIO_PIN_4 - void furi_hal_os_timer_callback() { - LL_GPIO_TogglePin(LED_SECOND_PORT, LED_SECOND_PIN); + furi_hal_gpio_write(&gpio_ext_pa4, !furi_hal_gpio_read(&gpio_ext_pa4)); } #endif extern void xPortSysTickHandler(); -static volatile uint32_t furi_hal_os_skew = 0; +static volatile uint32_t furi_hal_os_skew; void furi_hal_os_init() { furi_hal_idle_timer_init(); #ifdef FURI_HAL_OS_DEBUG - LL_GPIO_SetPinMode(LED_SLEEP_PORT, LED_SLEEP_PIN, LL_GPIO_MODE_OUTPUT); - LL_GPIO_SetPinMode(LED_TICK_PORT, LED_TICK_PIN, LL_GPIO_MODE_OUTPUT); - LL_GPIO_SetPinMode(LED_SECOND_PORT, LED_SECOND_PIN, LL_GPIO_MODE_OUTPUT); + furi_hal_gpio_init_simple(&gpio_ext_pa7, GpioModeOutputPushPull); + furi_hal_gpio_init_simple(&gpio_ext_pa6, GpioModeOutputPushPull); + furi_hal_gpio_init_simple(&gpio_ext_pa4, GpioModeOutputPushPull); osTimerId_t second_timer = osTimerNew(furi_hal_os_timer_callback, osTimerPeriodic, NULL, NULL); osTimerStart(second_timer, FURI_HAL_OS_TICK_HZ); #endif @@ -54,12 +56,61 @@ void furi_hal_os_init() { void furi_hal_os_tick() { if(xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) { #ifdef FURI_HAL_OS_DEBUG - LL_GPIO_TogglePin(LED_TICK_PORT, LED_TICK_PIN); + furi_hal_gpio_write(&gpio_ext_pa6, !furi_hal_gpio_read(&gpio_ext_pa6)); #endif xPortSysTickHandler(); } } +#ifdef FURI_HAL_OS_DEBUG +// Find out the IRQ number while debugging +static void furi_hal_os_nvic_dbg_trap() { + for(int32_t i = WWDG_IRQn; i <= DMAMUX1_OVR_IRQn; i++) { + if(NVIC_GetPendingIRQ(i)) { + (void)i; + // Break here + __NOP(); + } + } +} + +// Find out the EXTI line number while debugging +static void furi_hal_os_exti_dbg_trap(uint32_t exti, uint32_t val) { + for(uint32_t i = 0; val; val >>= 1U, ++i) { + if(val & 1U) { + (void)exti; + (void)i; + // Break here + __NOP(); + } + } +} +#endif + +static inline bool furi_hal_os_is_pending_irq() { + if(FURI_HAL_OS_NVIC_IS_PENDING()) { +#ifdef FURI_HAL_OS_DEBUG + furi_hal_os_nvic_dbg_trap(); +#endif + return true; + } + + uint32_t exti_lines_active; + if((exti_lines_active = LL_EXTI_ReadFlag_0_31(LL_EXTI_LINE_ALL_0_31))) { +#ifdef FURI_HAL_OS_DEBUG + furi_hal_os_exti_dbg_trap(FURI_HAL_OS_EXTI_LINE_0_31, exti_lines_active); +#endif + return true; + } else if((exti_lines_active = LL_EXTI_ReadFlag_32_63(LL_EXTI_LINE_ALL_32_63))) { +#ifdef FURI_HAL_OS_DEBUG + furi_hal_os_exti_dbg_trap(FURI_HAL_OS_EXTI_LINE_32_63, exti_lines_active); +#endif + return true; + } + + return false; +} + static inline uint32_t furi_hal_os_sleep(TickType_t expected_idle_ticks) { // Stop ticks furi_hal_clock_suspend_tick(); @@ -68,20 +119,20 @@ static inline uint32_t furi_hal_os_sleep(TickType_t expected_idle_ticks) { furi_hal_idle_timer_start(FURI_HAL_OS_TICKS_TO_IDLE_CNT(expected_idle_ticks)); #ifdef FURI_HAL_OS_DEBUG - LL_GPIO_ResetOutputPin(LED_SLEEP_PORT, LED_SLEEP_PIN); + furi_hal_gpio_write(&gpio_ext_pa7, 0); #endif // Go to sleep mode furi_hal_power_sleep(); #ifdef FURI_HAL_OS_DEBUG - LL_GPIO_SetOutputPin(LED_SLEEP_PORT, LED_SLEEP_PIN); + furi_hal_gpio_write(&gpio_ext_pa7, 1); #endif // Calculate how much time we spent in the sleep - uint32_t after_cnt = furi_hal_idle_timer_get_cnt() + furi_hal_os_skew; + uint32_t after_cnt = furi_hal_idle_timer_get_cnt() + furi_hal_os_skew + FURI_HAL_OS_EXTRA_CNT; uint32_t after_tick = FURI_HAL_OS_IDLE_CNT_TO_TICKS(after_cnt); - furi_hal_os_skew = after_cnt - (after_cnt / after_tick); + furi_hal_os_skew = after_cnt - FURI_HAL_OS_TICKS_TO_IDLE_CNT(after_tick); bool cmpm = LL_LPTIM_IsActiveFlag_CMPM(FURI_HAL_IDLE_TIMER); bool arrm = LL_LPTIM_IsActiveFlag_ARRM(FURI_HAL_IDLE_TIMER); @@ -110,7 +161,7 @@ void vPortSuppressTicksAndSleep(TickType_t expected_idle_ticks) { __disable_irq(); // Confirm OS that sleep is still possible - if(eTaskConfirmSleepModeStatus() == eAbortSleep) { + if(eTaskConfirmSleepModeStatus() == eAbortSleep || furi_hal_os_is_pending_irq()) { __enable_irq(); return; } diff --git a/firmware/targets/f7/furi_hal/furi_hal_power.c b/firmware/targets/f7/furi_hal/furi_hal_power.c index 2b78c7f0..52bb7bcf 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_power.c +++ b/firmware/targets/f7/furi_hal/furi_hal_power.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include @@ -17,6 +18,12 @@ #define TAG "FuriHalPower" +#ifdef FURI_HAL_POWER_DEEP_SLEEP_ENABLED +#define FURI_HAL_POWER_DEEP_INSOMNIA 0 +#else +#define FURI_HAL_POWER_DEEP_INSOMNIA 1 +#endif + typedef struct { volatile uint8_t insomnia; volatile uint8_t deep_insomnia; @@ -28,7 +35,7 @@ typedef struct { static volatile FuriHalPower furi_hal_power = { .insomnia = 0, - .deep_insomnia = 1, + .deep_insomnia = FURI_HAL_POWER_DEEP_INSOMNIA, .suppress_charge = 0, }; @@ -70,11 +77,6 @@ const ParamCEDV cedv = { .DOD100 = 3299, }; -void HAL_RCC_CSSCallback(void) { - // TODO: notify user about issue with HSE - furi_hal_power_reset(); -} - void furi_hal_power_init() { LL_PWR_SetRegulVoltageScaling(LL_PWR_REGU_VOLTAGE_SCALE1); LL_PWR_SMPS_SetMode(LL_PWR_SMPS_STEP_DOWN); @@ -84,6 +86,11 @@ void furi_hal_power_init() { bq25896_init(&furi_hal_i2c_handle_power); furi_hal_i2c_release(&furi_hal_i2c_handle_power); +#ifdef FURI_HAL_OS_DEBUG + furi_hal_gpio_init_simple(&gpio_ext_pb2, GpioModeOutputPushPull); + furi_hal_gpio_init_simple(&gpio_ext_pc3, GpioModeOutputPushPull); +#endif + FURI_LOG_I(TAG, "Init OK"); } @@ -140,12 +147,28 @@ void furi_hal_power_light_sleep() { __WFI(); } +static inline void furi_hal_power_suspend_aux_periphs() { + // Disable USART + furi_hal_uart_suspend(FuriHalUartIdUSART1); + furi_hal_uart_suspend(FuriHalUartIdLPUART1); + // TODO: Disable USB +} + +static inline void furi_hal_power_resume_aux_periphs() { + // Re-enable USART + furi_hal_uart_resume(FuriHalUartIdUSART1); + furi_hal_uart_resume(FuriHalUartIdLPUART1); + // TODO: Re-enable USB +} + void furi_hal_power_deep_sleep() { + furi_hal_power_suspend_aux_periphs(); + while(LL_HSEM_1StepLock(HSEM, CFG_HW_RCC_SEMID)) ; if(!LL_HSEM_1StepLock(HSEM, CFG_HW_ENTRY_STOP_MODE_SEMID)) { - if(LL_PWR_IsActiveFlag_C2DS()) { + if(LL_PWR_IsActiveFlag_C2DS() || LL_PWR_IsActiveFlag_C2SB()) { // Release ENTRY_STOP_MODE semaphore LL_HSEM_ReleaseLock(HSEM, CFG_HW_ENTRY_STOP_MODE_SEMID, 0); @@ -163,7 +186,8 @@ void furi_hal_power_deep_sleep() { LL_HSEM_ReleaseLock(HSEM, CFG_HW_RCC_SEMID, 0); // Prepare deep sleep - LL_PWR_SetPowerMode(LL_PWR_MODE_STOP1); + LL_PWR_SetPowerMode(LL_PWR_MODE_STOP2); + LL_C2_PWR_SetPowerMode(LL_PWR_MODE_STOP2); LL_LPM_EnableDeepSleep(); #if defined(__CC_ARM) @@ -173,6 +197,15 @@ void furi_hal_power_deep_sleep() { __WFI(); + LL_LPM_EnableSleep(); + + // Make sure that values differ to prevent disaster on wfi + LL_PWR_SetPowerMode(LL_PWR_MODE_STOP0); + LL_C2_PWR_SetPowerMode(LL_PWR_MODE_SHUTDOWN); + + LL_PWR_ClearFlag_C1STOP_C1STB(); + LL_PWR_ClearFlag_C2STOP_C2STB(); + /* Release ENTRY_STOP_MODE semaphore */ LL_HSEM_ReleaseLock(HSEM, CFG_HW_ENTRY_STOP_MODE_SEMID, 0); @@ -184,13 +217,31 @@ void furi_hal_power_deep_sleep() { } LL_HSEM_ReleaseLock(HSEM, CFG_HW_RCC_SEMID, 0); + + furi_hal_power_resume_aux_periphs(); } void furi_hal_power_sleep() { if(furi_hal_power_deep_sleep_available()) { +#ifdef FURI_HAL_OS_DEBUG + furi_hal_gpio_write(&gpio_ext_pc3, 1); +#endif + furi_hal_power_deep_sleep(); + +#ifdef FURI_HAL_OS_DEBUG + furi_hal_gpio_write(&gpio_ext_pc3, 0); +#endif } else { +#ifdef FURI_HAL_OS_DEBUG + furi_hal_gpio_write(&gpio_ext_pb2, 1); +#endif + furi_hal_power_light_sleep(); + +#ifdef FURI_HAL_OS_DEBUG + furi_hal_gpio_write(&gpio_ext_pb2, 0); +#endif } } @@ -215,6 +266,38 @@ bool furi_hal_power_is_charging() { return ret; } +void furi_hal_power_shutdown() { + furi_hal_power_insomnia_enter(); + + furi_hal_bt_reinit(); + + while(LL_HSEM_1StepLock(HSEM, CFG_HW_RCC_SEMID)) + ; + + if(!LL_HSEM_1StepLock(HSEM, CFG_HW_ENTRY_STOP_MODE_SEMID)) { + if(LL_PWR_IsActiveFlag_C2DS() || LL_PWR_IsActiveFlag_C2SB()) { + // Release ENTRY_STOP_MODE semaphore + LL_HSEM_ReleaseLock(HSEM, CFG_HW_ENTRY_STOP_MODE_SEMID, 0); + } + } + + // Prepare Wakeup pin + LL_PWR_SetWakeUpPinPolarityLow(LL_PWR_WAKEUP_PIN2); + LL_PWR_EnableWakeUpPin(LL_PWR_WAKEUP_PIN2); + LL_C2_PWR_EnableWakeUpPin(LL_PWR_WAKEUP_PIN2); + + /* Release RCC semaphore */ + LL_HSEM_ReleaseLock(HSEM, CFG_HW_RCC_SEMID, 0); + + LL_PWR_DisableBootC2(); + LL_PWR_SetPowerMode(LL_PWR_MODE_SHUTDOWN); + LL_C2_PWR_SetPowerMode(LL_PWR_MODE_SHUTDOWN); + LL_LPM_EnableDeepSleep(); + + __WFI(); + furi_crash("Insomniac core2"); +} + void furi_hal_power_off() { furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); bq25896_poweroff(&furi_hal_i2c_handle_power); diff --git a/firmware/targets/f7/furi_hal/furi_hal_resources.c b/firmware/targets/f7/furi_hal/furi_hal_resources.c index 90195585..e548ffaf 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_resources.c +++ b/firmware/targets/f7/furi_hal/furi_hal_resources.c @@ -1,6 +1,9 @@ #include #include +#include +#include + const GpioPin vibro_gpio = {.port = VIBRO_GPIO_Port, .pin = VIBRO_Pin}; const GpioPin ibutton_gpio = {.port = iBTN_GPIO_Port, .pin = iBTN_Pin}; @@ -9,7 +12,7 @@ const GpioPin gpio_rf_sw_0 = {.port = RF_SW_0_GPIO_Port, .pin = RF_SW_0_Pin}; const GpioPin gpio_subghz_cs = {.port = CC1101_CS_GPIO_Port, .pin = CC1101_CS_Pin}; const GpioPin gpio_display_cs = {.port = DISPLAY_CS_GPIO_Port, .pin = DISPLAY_CS_Pin}; -const GpioPin gpio_display_rst = {.port = DISPLAY_RST_GPIO_Port, .pin = DISPLAY_RST_Pin}; +const GpioPin gpio_display_rst_n = {.port = DISPLAY_RST_GPIO_Port, .pin = DISPLAY_RST_Pin}; const GpioPin gpio_display_di = {.port = DISPLAY_DI_GPIO_Port, .pin = DISPLAY_DI_Pin}; const GpioPin gpio_sdcard_cs = {.port = SD_CS_GPIO_Port, .pin = SD_CS_Pin}; const GpioPin gpio_sdcard_cd = {.port = SD_CD_GPIO_Port, .pin = SD_CD_Pin}; @@ -72,14 +75,32 @@ const size_t input_pins_count = sizeof(input_pins) / sizeof(InputPin); void furi_hal_resources_init_early() { furi_hal_gpio_init(&gpio_button_left, GpioModeInput, GpioPullUp, GpioSpeedLow); - furi_hal_gpio_init_simple(&gpio_display_rst, GpioModeOutputPushPull); + + // Display pins + furi_hal_gpio_write(&gpio_display_rst_n, 1); + furi_hal_gpio_init_simple(&gpio_display_rst_n, GpioModeOutputPushPull); furi_hal_gpio_init_simple(&gpio_display_di, GpioModeOutputPushPull); + // Alternative pull configuration for shutdown + SET_BIT(PWR->PUCRB, DISPLAY_RST_Pin); + CLEAR_BIT(PWR->PDCRB, DISPLAY_RST_Pin); + SET_BIT(PWR->CR3, PWR_CR3_APC); + // Hard reset USB furi_hal_gpio_init_simple(&gpio_usb_dm, GpioModeOutputOpenDrain); furi_hal_gpio_init_simple(&gpio_usb_dp, GpioModeOutputOpenDrain); furi_hal_gpio_write(&gpio_usb_dm, 0); furi_hal_gpio_write(&gpio_usb_dp, 0); + + // External header pins + furi_hal_gpio_init(&gpio_ext_pc0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(&gpio_ext_pc1, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(&gpio_ext_pc3, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(&gpio_ext_pb2, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(&gpio_ext_pb3, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(&gpio_ext_pa4, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(&gpio_ext_pa6, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(&gpio_ext_pa7, GpioModeAnalog, GpioPullNo, GpioSpeedLow); } void furi_hal_resources_deinit_early() { @@ -92,19 +113,9 @@ void furi_hal_resources_init() { input_pins[i].gpio, GpioModeInterruptRiseFall, GpioPullUp, GpioSpeedLow); } - // External header pins - furi_hal_gpio_init(&gpio_ext_pc0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_init(&gpio_ext_pc1, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_init(&gpio_ext_pc3, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_init(&gpio_ext_pb2, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_init(&gpio_ext_pb3, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_init(&gpio_ext_pa4, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_init(&gpio_ext_pa6, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_init(&gpio_ext_pa7, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - // Display pins - furi_hal_gpio_init(&gpio_display_rst, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_write(&gpio_display_rst, 0); + furi_hal_gpio_init(&gpio_display_rst_n, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_write(&gpio_display_rst_n, 0); furi_hal_gpio_init(&gpio_display_di, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); furi_hal_gpio_write(&gpio_display_di, 0); diff --git a/firmware/targets/f7/furi_hal/furi_hal_resources.h b/firmware/targets/f7/furi_hal/furi_hal_resources.h index eaafd7c5..69485307 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_resources.h +++ b/firmware/targets/f7/furi_hal/furi_hal_resources.h @@ -48,7 +48,7 @@ extern const GpioPin gpio_rf_sw_0; extern const GpioPin gpio_subghz_cs; extern const GpioPin gpio_display_cs; -extern const GpioPin gpio_display_rst; +extern const GpioPin gpio_display_rst_n; extern const GpioPin gpio_display_di; extern const GpioPin gpio_sdcard_cs; extern const GpioPin gpio_sdcard_cd; diff --git a/firmware/targets/f7/furi_hal/furi_hal_rtc.c b/firmware/targets/f7/furi_hal/furi_hal_rtc.c index 748d7e41..b580a04b 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_rtc.c +++ b/firmware/targets/f7/furi_hal/furi_hal_rtc.c @@ -1,5 +1,6 @@ #include #include +#include #include #include @@ -36,7 +37,6 @@ void furi_hal_rtc_init_early() { // LSE and RTC LL_PWR_EnableBkUpAccess(); if(!RTC_CLOCK_IS_READY()) { - // Start LSI1 needed for CSS LL_RCC_LSI1_Enable(); // Try to start LSE normal way LL_RCC_LSE_SetDriveCapability(LL_RCC_LSEDRIVE_HIGH); @@ -55,8 +55,6 @@ void furi_hal_rtc_init_early() { } // Set RTC domain clock to LSE LL_RCC_SetRTCClockSource(LL_RCC_RTC_CLKSOURCE_LSE); - // Enable LSE CSS - LL_RCC_LSE_EnableCSS(); } // Enable clocking LL_RCC_EnableRTC(); @@ -74,6 +72,12 @@ void furi_hal_rtc_init_early() { data->version = FURI_HAL_RTC_HEADER_VERSION; furi_hal_rtc_set_register(FuriHalRtcRegisterHeader, data_reg); } + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + furi_hal_debug_enable(); + } else { + furi_hal_debug_disable(); + } } void furi_hal_rtc_deinit_early() { @@ -126,6 +130,10 @@ void furi_hal_rtc_set_flag(FuriHalRtcFlag flag) { DeveloperReg* data = (DeveloperReg*)&data_reg; data->flags |= flag; furi_hal_rtc_set_register(FuriHalRtcRegisterSystem, data_reg); + + if(flag & FuriHalRtcFlagDebug) { + furi_hal_debug_enable(); + } } void furi_hal_rtc_reset_flag(FuriHalRtcFlag flag) { @@ -133,6 +141,10 @@ void furi_hal_rtc_reset_flag(FuriHalRtcFlag flag) { DeveloperReg* data = (DeveloperReg*)&data_reg; data->flags &= ~flag; furi_hal_rtc_set_register(FuriHalRtcRegisterSystem, data_reg); + + if(flag & FuriHalRtcFlagDebug) { + furi_hal_debug_disable(); + } } bool furi_hal_rtc_is_flag_set(FuriHalRtcFlag flag) { diff --git a/firmware/targets/f7/furi_hal/furi_hal_spi.c b/firmware/targets/f7/furi_hal/furi_hal_spi.c index d74a394a..01cb3ae6 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_spi.c +++ b/firmware/targets/f7/furi_hal/furi_hal_spi.c @@ -1,5 +1,6 @@ #include "furi_hal_spi.h" #include "furi_hal_resources.h" +#include #include #include @@ -55,6 +56,8 @@ void furi_hal_spi_bus_handle_deinit(FuriHalSpiBusHandle* handle) { void furi_hal_spi_acquire(FuriHalSpiBusHandle* handle) { furi_assert(handle); + furi_hal_power_insomnia_enter(); + handle->bus->callback(handle->bus, FuriHalSpiBusEventLock); handle->bus->callback(handle->bus, FuriHalSpiBusEventActivate); @@ -75,6 +78,8 @@ void furi_hal_spi_release(FuriHalSpiBusHandle* handle) { // Bus events handle->bus->callback(handle->bus, FuriHalSpiBusEventDeactivate); handle->bus->callback(handle->bus, FuriHalSpiBusEventUnlock); + + furi_hal_power_insomnia_exit(); } static void furi_hal_spi_bus_end_txrx(FuriHalSpiBusHandle* handle, uint32_t timeout) { diff --git a/firmware/targets/f7/furi_hal/furi_hal_uart.c b/firmware/targets/f7/furi_hal/furi_hal_uart.c index c4ad2016..74b14f4f 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_uart.c +++ b/firmware/targets/f7/furi_hal/furi_hal_uart.c @@ -8,6 +8,8 @@ #include #include +static bool furi_hal_usart_prev_enabled[2]; + static void (*irq_cb[2])(uint8_t ev, uint8_t data, void* context); static void* irq_ctx[2]; @@ -44,7 +46,6 @@ static void furi_hal_usart_init(uint32_t baud) { ; LL_USART_EnableIT_RXNE_RXFNE(USART1); - LL_USART_EnableIT_IDLE(USART1); NVIC_SetPriority(USART1_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); } @@ -81,7 +82,6 @@ static void furi_hal_lpuart_init(uint32_t baud) { furi_hal_uart_set_br(FuriHalUartIdLPUART1, baud); LL_LPUART_EnableIT_RXNE_RXFNE(LPUART1); - LL_LPUART_EnableIT_IDLE(LPUART1); NVIC_SetPriority(LPUART1_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); } @@ -136,6 +136,28 @@ void furi_hal_uart_deinit(FuriHalUartId ch) { } } +void furi_hal_uart_suspend(FuriHalUartId channel) { + if(channel == FuriHalUartIdLPUART1 && LL_LPUART_IsEnabled(LPUART1)) { + LL_LPUART_Disable(LPUART1); + furi_hal_usart_prev_enabled[channel] = true; + } else if(channel == FuriHalUartIdUSART1 && LL_USART_IsEnabled(USART1)) { + LL_USART_Disable(USART1); + furi_hal_usart_prev_enabled[channel] = true; + } +} + +void furi_hal_uart_resume(FuriHalUartId channel) { + if(!furi_hal_usart_prev_enabled[channel]) { + return; + } else if(channel == FuriHalUartIdLPUART1) { + LL_LPUART_Enable(LPUART1); + } else if(channel == FuriHalUartIdUSART1) { + LL_USART_Enable(USART1); + } + + furi_hal_usart_prev_enabled[channel] = false; +} + void furi_hal_uart_tx(FuriHalUartId ch, uint8_t* buffer, size_t buffer_size) { if(ch == FuriHalUartIdUSART1) { if(LL_USART_IsEnabled(USART1) == 0) return; @@ -189,22 +211,15 @@ void LPUART1_IRQHandler(void) { if(LL_LPUART_IsActiveFlag_RXNE_RXFNE(LPUART1)) { uint8_t data = LL_LPUART_ReceiveData8(LPUART1); irq_cb[FuriHalUartIdLPUART1](UartIrqEventRXNE, data, irq_ctx[FuriHalUartIdLPUART1]); - } else if(LL_LPUART_IsActiveFlag_IDLE(LPUART1)) { - irq_cb[FuriHalUartIdLPUART1](UartIrqEventIDLE, 0, irq_ctx[FuriHalUartIdLPUART1]); - 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, irq_ctx[FuriHalUartIdUSART1]); - } else if(LL_USART_IsActiveFlag_IDLE(USART1)) { - irq_cb[FuriHalUartIdUSART1](UartIrqEventIDLE, 0, irq_ctx[FuriHalUartIdUSART1]); - LL_USART_ClearFlag_IDLE(USART1); } else if(LL_USART_IsActiveFlag_ORE(USART1)) { LL_USART_ClearFlag_ORE(USART1); } diff --git a/firmware/targets/f7/furi_hal/furi_hal_uart.h b/firmware/targets/f7/furi_hal/furi_hal_uart.h index 9224a0b2..07211db8 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_uart.h +++ b/firmware/targets/f7/furi_hal/furi_hal_uart.h @@ -27,8 +27,6 @@ typedef enum { */ typedef enum { UartIrqEventRXNE, - UartIrqEventIDLE, - //TODO: more events } UartIrqEvent; /** @@ -46,6 +44,20 @@ void furi_hal_uart_init(FuriHalUartId channel, uint32_t baud); */ void furi_hal_uart_deinit(FuriHalUartId channel); +/** + * Suspend UART operation + * Disables UART hardware, settings and callbacks are preserved + * @param channel UART channel + */ +void furi_hal_uart_suspend(FuriHalUartId channel); + +/** + * Resume UART operation + * Resumes UART hardware from suspended state + * @param channel UART channel + */ +void furi_hal_uart_resume(FuriHalUartId channel); + /** * Changes UART baudrate * @param channel UART channel @@ -74,4 +86,4 @@ void furi_hal_uart_set_irq_cb( #ifdef __cplusplus } -#endif \ No newline at end of file +#endif diff --git a/firmware/targets/f7/furi_hal/furi_hal_usb.c b/firmware/targets/f7/furi_hal/furi_hal_usb.c index f1044c36..d41414f8 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_usb.c +++ b/firmware/targets/f7/furi_hal/furi_hal_usb.c @@ -1,7 +1,6 @@ #include "furi_hal_version.h" #include "furi_hal_usb_i.h" #include "furi_hal_usb.h" -#include "furi_hal_vcp.h" #include #include #include @@ -77,7 +76,9 @@ void furi_hal_usb_init(void) { usb.enabled = false; usb.if_cur = NULL; NVIC_SetPriority(USB_LP_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); + NVIC_SetPriority(USB_HP_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 15, 0)); NVIC_EnableIRQ(USB_LP_IRQn); + NVIC_EnableIRQ(USB_HP_IRQn); usb.thread = furi_thread_alloc(); furi_thread_set_name(usb.thread, "UsbDriver"); 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 028e3e30..807f1634 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_usb_cdc.c +++ b/firmware/targets/f7/furi_hal/furi_hal_usb_cdc.c @@ -482,6 +482,9 @@ void furi_hal_cdc_set_callbacks(uint8_t if_num, CdcCallbacks* cb, void* context) if(callbacks[if_num]->state_callback != NULL) { if(connected == true) callbacks[if_num]->state_callback(cb_ctx[if_num], 1); } + if(callbacks[if_num]->ctrl_line_callback != NULL) { + callbacks[if_num]->ctrl_line_callback(cb_ctx[if_num], cdc_ctrl_line_state[if_num]); + } } } diff --git a/firmware/targets/f7/furi_hal/furi_hal_usb_hid.c b/firmware/targets/f7/furi_hal/furi_hal_usb_hid.c index b9b4bbb0..55825dd9 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_usb_hid.c +++ b/firmware/targets/f7/furi_hal/furi_hal_usb_hid.c @@ -9,6 +9,7 @@ #include "hid_usage_desktop.h" #include "hid_usage_button.h" #include "hid_usage_keyboard.h" +#include "hid_usage_consumer.h" #include "hid_usage_led.h" #define HID_EP_IN 0x81 @@ -18,8 +19,7 @@ #define HID_KB_MAX_KEYS 6 #define HID_CONSUMER_MAX_KEYS 2 -#define HID_PAGE_CONSUMER 0x0C -#define HID_CONSUMER_CONTROL 0x01 +#define HID_INTERVAL 2 #define HID_VID_DEFAULT 0x046D #define HID_PID_DEFAULT 0xC529 @@ -190,7 +190,7 @@ static const struct HidConfigDescriptor hid_cfg_desc = { .bEndpointAddress = HID_EP_IN, .bmAttributes = USB_EPTYPE_INTERRUPT, .wMaxPacketSize = HID_EP_SZ, - .bInterval = 10, + .bInterval = HID_INTERVAL, }, .hid_ep_out = { @@ -199,7 +199,7 @@ static const struct HidConfigDescriptor hid_cfg_desc = { .bEndpointAddress = HID_EP_OUT, .bmAttributes = USB_EPTYPE_INTERRUPT, .wMaxPacketSize = HID_EP_SZ, - .bInterval = 10, + .bInterval = HID_INTERVAL, }, }, }; diff --git a/firmware/targets/f7/target.mk b/firmware/targets/f7/target.mk index 1822b628..6410ba13 100644 --- a/firmware/targets/f7/target.mk +++ b/firmware/targets/f7/target.mk @@ -119,6 +119,10 @@ ifeq ($(FURI_HAL_USB_VCP_DEBUG), 1) CFLAGS += -DFURI_HAL_USB_VCP_DEBUG endif +ifeq ($(FURI_HAL_POWER_DEEP_SLEEP_ENABLED), 1) +CFLAGS += -DFURI_HAL_POWER_DEEP_SLEEP_ENABLED +endif + FURI_HAL_SUBGHZ_TX_GPIO ?= 0 ifneq ($(FURI_HAL_SUBGHZ_TX_GPIO), 0) CFLAGS += -DFURI_HAL_SUBGHZ_TX_GPIO=$(FURI_HAL_SUBGHZ_TX_GPIO) @@ -128,6 +132,10 @@ ifeq ($(INVERT_RFID_IN), 1) CFLAGS += -DINVERT_RFID_IN endif +ifeq ($(BLE_GLUE_DEBUG), 1) +CFLAGS += -DBLE_GLUE_DEBUG +endif + FURI_HAL_DIR = $(TARGET_DIR)/furi_hal CFLAGS += -I$(FURI_HAL_DIR) C_SOURCES += $(wildcard $(FURI_HAL_DIR)/*.c) diff --git a/firmware/targets/furi_hal_include/furi_hal.h b/firmware/targets/furi_hal_include/furi_hal.h index 76e96ef6..17814b13 100644 --- a/firmware/targets/furi_hal_include/furi_hal.h +++ b/firmware/targets/furi_hal_include/furi_hal.h @@ -12,6 +12,7 @@ template struct STOP_EXTERNING_ME {}; #include "furi_hal_clock.h" #include "furi_hal_crypto.h" #include "furi_hal_console.h" +#include "furi_hal_debug.h" #include "furi_hal_os.h" #include "furi_hal_sd.h" #include "furi_hal_i2c.h" @@ -22,7 +23,6 @@ template struct STOP_EXTERNING_ME {}; #include "furi_hal_light.h" #include "furi_hal_delay.h" #include "furi_hal_power.h" -#include "furi_hal_vcp.h" #include "furi_hal_interrupt.h" #include "furi_hal_version.h" #include "furi_hal_bt.h" @@ -39,7 +39,6 @@ template struct STOP_EXTERNING_ME {}; #include "furi_hal_uart.h" #include "furi_hal_info.h" #include "furi_hal_random.h" -#include "furi_hal_crc.h" #ifdef __cplusplus extern "C" { diff --git a/firmware/targets/furi_hal_include/furi_hal_bt.h b/firmware/targets/furi_hal_include/furi_hal_bt.h index 912132d2..73b16bec 100644 --- a/firmware/targets/furi_hal_include/furi_hal_bt.h +++ b/firmware/targets/furi_hal_include/furi_hal_bt.h @@ -16,6 +16,7 @@ #define FURI_HAL_BT_STACK_VERSION_MAJOR (1) #define FURI_HAL_BT_STACK_VERSION_MINOR (13) +#define FURI_HAL_BT_C2_START_TIMEOUT 1000 #ifdef __cplusplus extern "C" { @@ -67,6 +68,12 @@ FuriHalBtStack furi_hal_bt_get_radio_stack(); */ bool furi_hal_bt_start_app(FuriHalBtProfile profile, GapEventCallback event_cb, void* context); +/** Reinitialize core2 + * + * Also can be used to prepare core2 for stop modes + */ +void furi_hal_bt_reinit(); + /** Change BLE app * Restarts 2nd core * @@ -207,6 +214,12 @@ bool furi_hal_bt_start_scan(GapScanCallback callback, void* context); /** Stop MAC addresses scan */ void furi_hal_bt_stop_scan(); +/** Check & switch C2 to given mode + * + * @param[in] mode mode to switch into + */ +bool furi_hal_bt_ensure_c2_mode(BleGlueC2Mode mode); + #ifdef __cplusplus } #endif diff --git a/firmware/targets/furi_hal_include/furi_hal_debug.h b/firmware/targets/furi_hal_include/furi_hal_debug.h new file mode 100644 index 00000000..88397bbb --- /dev/null +++ b/firmware/targets/furi_hal_include/furi_hal_debug.h @@ -0,0 +1,23 @@ +/** + * @file furi_hal_debug.h + * Debug HAL API + */ + +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** Enable MCU debug */ +void furi_hal_debug_enable(); + +/** Disable MCU debug */ +void furi_hal_debug_disable(); + +#ifdef __cplusplus +} +#endif diff --git a/firmware/targets/furi_hal_include/furi_hal_power.h b/firmware/targets/furi_hal_include/furi_hal_power.h index 717f6570..16af959c 100644 --- a/firmware/targets/furi_hal_include/furi_hal_power.h +++ b/firmware/targets/furi_hal_include/furi_hal_power.h @@ -85,6 +85,9 @@ uint8_t furi_hal_power_get_bat_health_pct(); */ bool furi_hal_power_is_charging(); +/** Switch MCU to SHUTDOWN */ +void furi_hal_power_shutdown(); + /** Poweroff device */ void furi_hal_power_off(); diff --git a/firmware/targets/furi_hal_include/furi_hal_rtc.h b/firmware/targets/furi_hal_include/furi_hal_rtc.h index 5b6a7780..21c69b24 100644 --- a/firmware/targets/furi_hal_include/furi_hal_rtc.h +++ b/firmware/targets/furi_hal_include/furi_hal_rtc.h @@ -28,6 +28,7 @@ typedef enum { FuriHalRtcFlagDebug = (1 << 0), FuriHalRtcFlagFactoryReset = (1 << 1), FuriHalRtcFlagLock = (1 << 2), + FuriHalRtcFlagC2Update = (1 << 3), } FuriHalRtcFlag; typedef enum { diff --git a/firmware/targets/furi_hal_include/furi_hal_vcp.h b/firmware/targets/furi_hal_include/furi_hal_vcp.h deleted file mode 100644 index ef5c18f9..00000000 --- a/firmware/targets/furi_hal_include/furi_hal_vcp.h +++ /dev/null @@ -1,63 +0,0 @@ -/** - * @file furi_hal_vcp.h - * VCP HAL API - */ - -#pragma once - -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** Init VCP HAL Allocates ring buffer and initializes state - */ -void furi_hal_vcp_init(); - -/** Disable VCP to make CDC interface usable by other application - */ -void furi_hal_vcp_disable(); - -/** Enable VCP - */ -void furi_hal_vcp_enable(); - -/** Recieve data from VCP Waits till some data arrives, never returns 0 - * - * @param buffer pointer to buffer - * @param size buffer size - * - * @return items copied in buffer, 0 if channel closed - */ -size_t furi_hal_vcp_rx(uint8_t* buffer, size_t size); - -/** Recieve data from VCP with timeout Waits till some data arrives during - * timeout - * - * @param buffer pointer to buffer - * @param size buffer size - * @param timeout rx timeout in ms - * - * @return items copied in buffer, 0 if channel closed or timeout occurs - */ -size_t furi_hal_vcp_rx_with_timeout(uint8_t* buffer, size_t size, uint32_t timeout); - -/** Transmit data to VCP - * - * @param buffer pointer to buffer - * @param size buffer size - */ -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/lib/STM32CubeWB b/lib/STM32CubeWB index 528461f8..a9e29b43 160000 --- a/lib/STM32CubeWB +++ b/lib/STM32CubeWB @@ -1 +1 @@ -Subproject commit 528461f8276f06783d46461bfb31d77aa8bac419 +Subproject commit a9e29b431f6dac95b6fc860a717834f35b7f78e5 diff --git a/lib/infrared/worker/infrared_worker.c b/lib/infrared/worker/infrared_worker.c index 4439a713..5711d2c0 100644 --- a/lib/infrared/worker/infrared_worker.c +++ b/lib/infrared/worker/infrared_worker.c @@ -177,7 +177,7 @@ static int32_t infrared_worker_rx_thread(void* thread_context) { notification_message(instance->notification, &sequence_blink_blue_10); } if(instance->signal.timings_cnt == 0) - notification_message(instance->notification, &sequence_display_on); + notification_message(instance->notification, &sequence_display_backlight_on); while(sizeof(LevelDuration) == xStreamBufferReceive( instance->stream, &level_duration, sizeof(LevelDuration), 0)) { diff --git a/lib/lib.mk b/lib/lib.mk index c1ee2cc0..1470c89f 100644 --- a/lib/lib.mk +++ b/lib/lib.mk @@ -97,7 +97,8 @@ CPP_SOURCES += $(wildcard $(LIB_DIR)/toolbox/*/*.cpp) # USB Stack CFLAGS += -I$(LIB_DIR)/libusb_stm32/inc -C_SOURCES += $(wildcard $(LIB_DIR)/libusb_stm32/src/*.c) +C_SOURCES += $(LIB_DIR)/libusb_stm32/src/usbd_stm32wb55_devfs.c +C_SOURCES += $(LIB_DIR)/libusb_stm32/src/usbd_core.c # protobuf CFLAGS += -I$(LIB_DIR)/nanopb diff --git a/lib/nfc_protocols/emv.c b/lib/nfc_protocols/emv.c index 6756981b..81a05e35 100644 --- a/lib/nfc_protocols/emv.c +++ b/lib/nfc_protocols/emv.c @@ -58,6 +58,16 @@ static const uint8_t pdol_ans[] = {0x77, 0x40, 0x82, 0x02, 0x20, 0x00, 0x57, 0x1 0xC9, 0x85, 0x9F, 0x27, 0x01, 0x00, 0x9F, 0x36, 0x02, 0x06, 0x0C, 0x9F, 0x6C, 0x02, 0x10, 0x00, 0x90, 0x00}; +static void emv_trace(FuriHalNfcTxRxContext* tx_rx, const char* message) { + if(furi_log_get_level() == FuriLogLevelTrace) { + FURI_LOG_T(TAG, "%s", message); + for(size_t i = 0; i < tx_rx->rx_bits / 8; i++) { + printf("%02X ", tx_rx->rx_data[i]); + } + printf("\r\n"); + } +} + static uint16_t emv_parse_TLV(uint8_t* dest, uint8_t* src, uint16_t* idx) { uint8_t len = src[*idx + 1]; memcpy(dest, &src[*idx + 2], len); @@ -113,6 +123,7 @@ bool emv_select_ppse(FuriHalNfcTxRxContext* tx_rx, EmvApplication* app) { FURI_LOG_D(TAG, "Send select PPSE"); if(furi_hal_nfc_tx_rx(tx_rx, 300)) { + emv_trace(tx_rx, "Select PPSE answer:"); if(emv_decode_ppse_response(tx_rx->rx_data, tx_rx->rx_bits / 8, app)) { app_aid_found = true; } else { @@ -169,6 +180,7 @@ bool emv_select_app(FuriHalNfcTxRxContext* tx_rx, EmvApplication* app) { FURI_LOG_D(TAG, "Start application"); if(furi_hal_nfc_tx_rx(tx_rx, 300)) { + emv_trace(tx_rx, "Start application answer:"); if(emv_decode_select_app_response(tx_rx->rx_data, tx_rx->rx_bits / 8, app)) { select_app_success = true; } else { @@ -251,6 +263,7 @@ static bool emv_get_processing_options(FuriHalNfcTxRxContext* tx_rx, EmvApplicat FURI_LOG_D(TAG, "Get proccessing options"); if(furi_hal_nfc_tx_rx(tx_rx, 300)) { + emv_trace(tx_rx, "Get processing options answer:"); if(emv_decode_get_proc_opt(tx_rx->rx_data, tx_rx->rx_bits / 8, app)) { card_num_read = true; } @@ -306,6 +319,7 @@ static bool emv_read_sfi_record( tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault; if(furi_hal_nfc_tx_rx(tx_rx, 300)) { + emv_trace(tx_rx, "SFI record:"); if(emv_decode_read_sfi_record(tx_rx->rx_data, tx_rx->rx_bits / 8, app)) { card_num_read = true; } diff --git a/lib/nfc_protocols/mifare_ultralight.c b/lib/nfc_protocols/mifare_ultralight.c index 31ac8e77..5b3d70b5 100644 --- a/lib/nfc_protocols/mifare_ultralight.c +++ b/lib/nfc_protocols/mifare_ultralight.c @@ -14,6 +14,8 @@ static void mf_ul_set_default_version(MfUltralightReader* reader, MfUltralightDa data->type = MfUltralightTypeUnknown; reader->pages_to_read = 16; reader->support_fast_read = false; + reader->support_tearing_flags = false; + reader->support_counters = false; } bool mf_ultralight_read_version( @@ -40,22 +42,32 @@ bool mf_ultralight_read_version( data->type = MfUltralightTypeUL11; reader->pages_to_read = 20; reader->support_fast_read = true; + reader->support_tearing_flags = true; + reader->support_counters = true; } else if(version->storage_size == 0x0E) { data->type = MfUltralightTypeUL21; reader->pages_to_read = 41; reader->support_fast_read = true; + reader->support_tearing_flags = true; + reader->support_counters = true; } else if(version->storage_size == 0x0F) { data->type = MfUltralightTypeNTAG213; reader->pages_to_read = 45; - reader->support_fast_read = false; + reader->support_fast_read = true; + reader->support_tearing_flags = false; + reader->support_counters = false; } else if(version->storage_size == 0x11) { data->type = MfUltralightTypeNTAG215; reader->pages_to_read = 135; - reader->support_fast_read = false; + reader->support_fast_read = true; + reader->support_tearing_flags = false; + reader->support_counters = false; } else if(version->storage_size == 0x13) { data->type = MfUltralightTypeNTAG216; reader->pages_to_read = 231; - reader->support_fast_read = false; + reader->support_fast_read = true; + reader->support_tearing_flags = false; + reader->support_counters = false; } else { mf_ul_set_default_version(reader, data); break; @@ -190,9 +202,10 @@ bool mf_ul_read_card( // Read Signature mf_ultralight_read_signature(tx_rx, data); } - // Read data blocks - if(reader->support_fast_read) { + if(reader->support_counters) { mf_ultralight_read_counters(tx_rx, data); + } + if(reader->support_tearing_flags) { mf_ultralight_read_tearing_flags(tx_rx, data); } card_read = mf_ultralight_read_pages(tx_rx, reader, data); @@ -230,11 +243,11 @@ void mf_ul_prepare_emulation(MfUltralightEmulator* emulator, MfUltralightData* d } else if(data->type == MfUltralightTypeUL21) { emulator->support_fast_read = true; } else if(data->type == MfUltralightTypeNTAG213) { - emulator->support_fast_read = false; + emulator->support_fast_read = true; } else if(data->type == MfUltralightTypeNTAG215) { - emulator->support_fast_read = false; + emulator->support_fast_read = true; } else if(data->type == MfUltralightTypeNTAG216) { - emulator->support_fast_read = false; + emulator->support_fast_read = true; } if(data->type >= MfUltralightTypeNTAG213) { diff --git a/lib/nfc_protocols/mifare_ultralight.h b/lib/nfc_protocols/mifare_ultralight.h index b554b75b..1947f10a 100644 --- a/lib/nfc_protocols/mifare_ultralight.h +++ b/lib/nfc_protocols/mifare_ultralight.h @@ -74,6 +74,8 @@ typedef struct { uint8_t pages_to_read; uint8_t pages_read; bool support_fast_read; + bool support_tearing_flags; + bool support_counters; } MfUltralightReader; typedef struct { diff --git a/lib/subghz/protocols/firefly.c b/lib/subghz/protocols/firefly.c new file mode 100644 index 00000000..e872d558 --- /dev/null +++ b/lib/subghz/protocols/firefly.c @@ -0,0 +1,343 @@ +#include "firefly.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +/* + * Help + * https://phreakerclub.com/447 + * + */ + +#define TAG "SubGhzProtocolFirefly" + +#define DIP_PATTERN "%c%c%c%c%c%c%c%c%c%c" +#define DATA_TO_DIP(dip) \ + (dip & 0x0200 ? '1' : '0'), (dip & 0x0100 ? '1' : '0'), (dip & 0x0080 ? '1' : '0'), \ + (dip & 0x0040 ? '1' : '0'), (dip & 0x0020 ? '1' : '0'), (dip & 0x0010 ? '1' : '0'), \ + (dip & 0x0008 ? '1' : '0'), (dip & 0x0004 ? '1' : '0'), (dip & 0x0002 ? '1' : '0'), \ + (dip & 0x0001 ? '1' : '0') + +static const SubGhzBlockConst subghz_protocol_firefly_const = { + .te_short = 500, + .te_long = 1500, + .te_delta = 150, + .min_count_bit_for_found = 10, +}; + +struct SubGhzProtocolDecoderFirefly { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; +}; + +struct SubGhzProtocolEncoderFirefly { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + FireflyDecoderStepReset = 0, + FireflyDecoderStepSaveDuration, + FireflyDecoderStepCheckDuration, +} FireflyDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_firefly_decoder = { + .alloc = subghz_protocol_decoder_firefly_alloc, + .free = subghz_protocol_decoder_firefly_free, + + .feed = subghz_protocol_decoder_firefly_feed, + .reset = subghz_protocol_decoder_firefly_reset, + + .get_hash_data = subghz_protocol_decoder_firefly_get_hash_data, + .serialize = subghz_protocol_decoder_firefly_serialize, + .deserialize = subghz_protocol_decoder_firefly_deserialize, + .get_string = subghz_protocol_decoder_firefly_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_firefly_encoder = { + .alloc = subghz_protocol_encoder_firefly_alloc, + .free = subghz_protocol_encoder_firefly_free, + + .deserialize = subghz_protocol_encoder_firefly_deserialize, + .stop = subghz_protocol_encoder_firefly_stop, + .yield = subghz_protocol_encoder_firefly_yield, +}; + +const SubGhzProtocol subghz_protocol_firefly = { + .name = SUBGHZ_PROTOCOL_FIREFLY_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_firefly_decoder, + .encoder = &subghz_protocol_firefly_encoder, +}; + +void* subghz_protocol_encoder_firefly_alloc(SubGhzEnvironment* environment) { + SubGhzProtocolEncoderFirefly* instance = malloc(sizeof(SubGhzProtocolEncoderFirefly)); + + instance->base.protocol = &subghz_protocol_firefly; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 28; //max 10bit*2 + 2 (start, stop) + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_runing = false; + return instance; +} + +void subghz_protocol_encoder_firefly_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderFirefly* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderFirefly instance + * @return true On success + */ +static bool subghz_protocol_encoder_firefly_get_upload(SubGhzProtocolEncoderFirefly* instance) { + furi_assert(instance); + size_t index = 0; + size_t size_upload = (instance->generic.data_count_bit * 2); + if(size_upload > instance->encoder.size_upload) { + FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer."); + return false; + } else { + instance->encoder.size_upload = size_upload; + } + + //Send key data + for(uint8_t i = instance->generic.data_count_bit; i > 1; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_firefly_const.te_short * 3); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_firefly_const.te_short); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_firefly_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_firefly_const.te_short * 3); + } + } + //Send end bit + if(bit_read(instance->generic.data, 0)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_firefly_const.te_short * 3); + //Send PT_GUARD + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_firefly_const.te_short * 42); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_firefly_const.te_short); + //Send PT_GUARD + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_firefly_const.te_short * 44); + } + + return true; +} + +bool subghz_protocol_encoder_firefly_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderFirefly* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + subghz_protocol_encoder_firefly_get_upload(instance); + instance->encoder.is_runing = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_firefly_stop(void* context) { + SubGhzProtocolEncoderFirefly* instance = context; + instance->encoder.is_runing = false; +} + +LevelDuration subghz_protocol_encoder_firefly_yield(void* context) { + SubGhzProtocolEncoderFirefly* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_runing) { + instance->encoder.is_runing = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +void* subghz_protocol_decoder_firefly_alloc(SubGhzEnvironment* environment) { + SubGhzProtocolDecoderFirefly* instance = malloc(sizeof(SubGhzProtocolDecoderFirefly)); + instance->base.protocol = &subghz_protocol_firefly; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_firefly_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderFirefly* instance = context; + free(instance); +} + +void subghz_protocol_decoder_firefly_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderFirefly* instance = context; + instance->decoder.parser_step = FireflyDecoderStepReset; +} + +void subghz_protocol_decoder_firefly_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderFirefly* instance = context; + switch(instance->decoder.parser_step) { + case FireflyDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_firefly_const.te_short * 42) < + subghz_protocol_firefly_const.te_delta * 20)) { + //Found header Firefly + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->decoder.parser_step = FireflyDecoderStepSaveDuration; + } + break; + case FireflyDecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = FireflyDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = FireflyDecoderStepReset; + } + break; + case FireflyDecoderStepCheckDuration: + if(!level) { //save interval + if(duration >= (subghz_protocol_firefly_const.te_short * 5)) { + instance->decoder.parser_step = FireflyDecoderStepReset; + //checking that the duration matches the guardtime + if((DURATION_DIFF(duration, subghz_protocol_firefly_const.te_short * 42) > + subghz_protocol_firefly_const.te_delta * 20)) { + break; + } + if(DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_firefly_const.te_short) < + subghz_protocol_firefly_const.te_delta) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + } else if( + DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_firefly_const.te_long) < + subghz_protocol_firefly_const.te_delta) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + } + if(instance->decoder.decode_count_bit == + subghz_protocol_firefly_const.min_count_bit_for_found) { + instance->generic.serial = 0x0; + instance->generic.btn = 0x0; + + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + break; + } + + if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_firefly_const.te_short) < + subghz_protocol_firefly_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_firefly_const.te_long) < + subghz_protocol_firefly_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = FireflyDecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_firefly_const.te_long) < + subghz_protocol_firefly_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_firefly_const.te_short) < + subghz_protocol_firefly_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = FireflyDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = FireflyDecoderStepReset; + } + + } else { + instance->decoder.parser_step = FireflyDecoderStepReset; + } + break; + } +} + +uint8_t subghz_protocol_decoder_firefly_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderFirefly* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_firefly_serialize( + void* context, + FlipperFormat* flipper_format, + uint32_t frequency, + FuriHalSubGhzPreset preset) { + furi_assert(context); + SubGhzProtocolDecoderFirefly* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, frequency, preset); +} + +bool subghz_protocol_decoder_firefly_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderFirefly* instance = context; + return subghz_block_generic_deserialize(&instance->generic, flipper_format); +} + +void subghz_protocol_decoder_firefly_get_string(void* context, string_t output) { + furi_assert(context); + SubGhzProtocolDecoderFirefly* instance = context; + + uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff; + + uint64_t code_found_reverse = subghz_protocol_blocks_reverse_key( + instance->generic.data, instance->generic.data_count_bit); + + uint32_t code_found_reverse_lo = code_found_reverse & 0x00000000ffffffff; + + string_cat_printf( + output, + "%s %dbit\r\n" + "Key:0x%08lX\r\n" + "Yek:0x%08lX\r\n" + "DIP:" DIP_PATTERN "\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + code_found_lo, + code_found_reverse_lo, + DATA_TO_DIP(code_found_lo)); +} diff --git a/lib/subghz/protocols/firefly.h b/lib/subghz/protocols/firefly.h new file mode 100644 index 00000000..abb4537c --- /dev/null +++ b/lib/subghz/protocols/firefly.h @@ -0,0 +1,109 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_FIREFLY_NAME "Firefly" + +typedef struct SubGhzProtocolDecoderFirefly SubGhzProtocolDecoderFirefly; +typedef struct SubGhzProtocolEncoderFirefly SubGhzProtocolEncoderFirefly; + +extern const SubGhzProtocolDecoder subghz_protocol_firefly_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_firefly_encoder; +extern const SubGhzProtocol subghz_protocol_firefly; + +/** + * Allocate SubGhzProtocolEncoderFirefly. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderFirefly* pointer to a SubGhzProtocolEncoderFirefly instance + */ +void* subghz_protocol_encoder_firefly_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderFirefly. + * @param context Pointer to a SubGhzProtocolEncoderFirefly instance + */ +void subghz_protocol_encoder_firefly_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderFirefly instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_firefly_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderFirefly instance + */ +void subghz_protocol_encoder_firefly_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderFirefly instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_firefly_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderFirefly. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderFirefly* pointer to a SubGhzProtocolDecoderFirefly instance + */ +void* subghz_protocol_decoder_firefly_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderFirefly. + * @param context Pointer to a SubGhzProtocolDecoderFirefly instance + */ +void subghz_protocol_decoder_firefly_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderFirefly. + * @param context Pointer to a SubGhzProtocolDecoderFirefly instance + */ +void subghz_protocol_decoder_firefly_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderFirefly instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_firefly_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderFirefly instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_firefly_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderFirefly. + * @param context Pointer to a SubGhzProtocolDecoderFirefly instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param frequency The frequency at which the signal was received, Hz + * @param preset The modulation on which the signal was received, FuriHalSubGhzPreset + * @return true On success + */ +bool subghz_protocol_decoder_firefly_serialize( + void* context, + FlipperFormat* flipper_format, + uint32_t frequency, + FuriHalSubGhzPreset preset); + +/** + * Deserialize data SubGhzProtocolDecoderFirefly. + * @param context Pointer to a SubGhzProtocolDecoderFirefly instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_firefly_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderFirefly instance + * @param output Resulting text + */ +void subghz_protocol_decoder_firefly_get_string(void* context, string_t output); diff --git a/lib/subghz/protocols/princeton.c b/lib/subghz/protocols/princeton.c index d3906465..4fa2b481 100644 --- a/lib/subghz/protocols/princeton.c +++ b/lib/subghz/protocols/princeton.c @@ -17,7 +17,7 @@ static const SubGhzBlockConst subghz_protocol_princeton_const = { .te_short = 400, .te_long = 1200, - .te_delta = 250, + .te_delta = 300, .min_count_bit_for_found = 24, }; diff --git a/lib/subghz/protocols/registry.c b/lib/subghz/protocols/registry.c index 6d15af56..c384eda1 100644 --- a/lib/subghz/protocols/registry.c +++ b/lib/subghz/protocols/registry.c @@ -7,7 +7,7 @@ const SubGhzProtocol* subghz_protocol_registry[] = { &subghz_protocol_nero_sketch, &subghz_protocol_ido, &subghz_protocol_kia, &subghz_protocol_hormann, &subghz_protocol_nero_radio, &subghz_protocol_somfy_telis, &subghz_protocol_somfy_keytis, &subghz_protocol_scher_khan, &subghz_protocol_gate_tx, - &subghz_protocol_raw, + &subghz_protocol_raw, &subghz_protocol_firefly, }; diff --git a/lib/subghz/protocols/registry.h b/lib/subghz/protocols/registry.h index cce48e02..531caee2 100644 --- a/lib/subghz/protocols/registry.h +++ b/lib/subghz/protocols/registry.h @@ -21,6 +21,7 @@ #include "scher_khan.h" #include "gate_tx.h" #include "raw.h" +#include "firefly.h" /** * Registration by name SubGhzProtocol. diff --git a/lib/toolbox/crc32_calc.c b/lib/toolbox/crc32_calc.c new file mode 100644 index 00000000..c8ae3524 --- /dev/null +++ b/lib/toolbox/crc32_calc.c @@ -0,0 +1,38 @@ +#include "crc32_calc.h" +#include + +#define CRC_DATA_BUFFER_MAX_LEN 512 + +uint32_t crc32_calc_buffer(uint32_t crc, const void* buffer, size_t size) { + // TODO: consider removing dependency on LFS + return ~lfs_crc(~crc, buffer, size); +} + +uint32_t crc32_calc_file(File* file, const FileCrcProgressCb progress_cb, void* context) { + furi_check(storage_file_is_open(file) && storage_file_seek(file, 0, true)); + + uint32_t file_crc = 0; + + uint8_t* data_buffer = malloc(CRC_DATA_BUFFER_MAX_LEN); + uint16_t data_buffer_valid_len; + + uint32_t file_size = storage_file_size(file); + + /* Feed file contents per sector into CRC calc */ + for(uint32_t fptr = 0; fptr < file_size;) { + data_buffer_valid_len = storage_file_read(file, data_buffer, CRC_DATA_BUFFER_MAX_LEN); + if(data_buffer_valid_len == 0) { + break; + } + fptr += data_buffer_valid_len; + + if(progress_cb && (fptr % CRC_DATA_BUFFER_MAX_LEN == 0)) { + progress_cb(fptr * 100 / file_size, context); + } + + file_crc = crc32_calc_buffer(file_crc, data_buffer, data_buffer_valid_len); + } + free(data_buffer); + + return file_crc; +} diff --git a/lib/toolbox/crc32_calc.h b/lib/toolbox/crc32_calc.h new file mode 100644 index 00000000..9455ade5 --- /dev/null +++ b/lib/toolbox/crc32_calc.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +uint32_t crc32_calc_buffer(uint32_t crc, const void* buffer, size_t size); + +typedef void (*FileCrcProgressCb)(const uint8_t progress, void* context); + +uint32_t crc32_calc_file(File* file, const FileCrcProgressCb progress_cb, void* context); + +#ifdef __cplusplus +} +#endif diff --git a/lib/toolbox/tar/tar_archive.c b/lib/toolbox/tar/tar_archive.c index e3cc1818..315497e1 100644 --- a/lib/toolbox/tar/tar_archive.c +++ b/lib/toolbox/tar/tar_archive.c @@ -38,6 +38,7 @@ static int mtar_storage_file_seek(void* stream, unsigned offset) { static int mtar_storage_file_close(void* stream) { if(stream) { storage_file_close(stream); + storage_file_free(stream); } return MTAR_ESUCCESS; } @@ -93,6 +94,7 @@ void tar_archive_free(TarArchive* archive) { if(mtar_is_open(&archive->tar)) { mtar_close(&archive->tar); } + free(archive); } void tar_archive_set_file_callback(TarArchive* archive, tar_unpack_file_cb callback, void* context) { @@ -204,12 +206,14 @@ static int archive_extract_foreach_cb(mtar_t* tar, const mtar_header_t* header, bool failed = false; uint8_t n_tries = FILE_OPEN_NTRIES; do { - while( - (n_tries-- > 0) && - !storage_file_open(out_file, string_get_cstr(fname), FSAM_WRITE, FSOM_CREATE_ALWAYS)) { + while(n_tries-- > 0) { + if(storage_file_open( + out_file, string_get_cstr(fname), FSAM_WRITE, FSOM_CREATE_ALWAYS)) { + break; + } FURI_LOG_W(TAG, "Failed to open '%s', reties: %d", string_get_cstr(fname), n_tries); + storage_file_close(out_file); osDelay(FILE_OPEN_RETRY_DELAY); - continue; } if(!storage_file_is_open(out_file)) { @@ -255,11 +259,13 @@ bool tar_archive_add_file( File* src_file = storage_file_alloc(archive->storage); uint8_t n_tries = FILE_OPEN_NTRIES; do { - while((n_tries-- > 0) && - !storage_file_open(src_file, fs_file_path, FSAM_READ, FSOM_OPEN_EXISTING)) { + while(n_tries-- > 0) { + if(storage_file_open(src_file, fs_file_path, FSAM_READ, FSOM_OPEN_EXISTING)) { + break; + } FURI_LOG_W(TAG, "Failed to open '%s', reties: %d", fs_file_path, n_tries); + storage_file_close(src_file); osDelay(FILE_OPEN_RETRY_DELAY); - continue; } if(!storage_file_is_open(src_file) || @@ -339,4 +345,4 @@ bool tar_archive_add_dir(TarArchive* archive, const char* fs_full_path, const ch free(name); storage_file_free(directory); return success; -} +} \ No newline at end of file diff --git a/lib/u8g2/u8g2_glue.c b/lib/u8g2/u8g2_glue.c index 550a4610..cc186663 100644 --- a/lib/u8g2/u8g2_glue.c +++ b/lib/u8g2/u8g2_glue.c @@ -17,7 +17,7 @@ uint8_t u8g2_gpio_and_delay_stm32(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, vo asm("nop"); break; case U8X8_MSG_GPIO_RESET: - furi_hal_gpio_write(&gpio_display_rst, arg_int); + furi_hal_gpio_write(&gpio_display_rst_n, arg_int); break; default: return 0; diff --git a/lib/update_util/dfu_file.c b/lib/update_util/dfu_file.c index 2fcf95ac..d6f31b60 100644 --- a/lib/update_util/dfu_file.c +++ b/lib/update_util/dfu_file.c @@ -1,42 +1,14 @@ #include "dfu_file.h" + #include +#include #define VALID_WHOLE_FILE_CRC 0xFFFFFFFF #define DFU_SUFFIX_VERSION 0x011A -#define DFU_DATA_BUFFER_MAX_LEN 512 #define DFU_SIGNATURE "DfuSe" bool dfu_file_validate_crc(File* dfuf, const DfuPageTaskProgressCb progress_cb, void* context) { - if(!storage_file_is_open(dfuf) || !storage_file_seek(dfuf, 0, true)) { - return false; - } - - furi_hal_crc_reset(); - - uint32_t file_crc = 0; - - uint8_t* data_buffer = malloc(DFU_DATA_BUFFER_MAX_LEN); - uint16_t data_buffer_valid_len; - - uint32_t file_size = storage_file_size(dfuf); - - /* Feed file contents per sector into CRC calc */ - furi_hal_crc_acquire(osWaitForever); - for(uint32_t fptr = 0; fptr < file_size;) { - data_buffer_valid_len = storage_file_read(dfuf, data_buffer, DFU_DATA_BUFFER_MAX_LEN); - if(data_buffer_valid_len == 0) { - break; - } - fptr += data_buffer_valid_len; - - if((fptr % DFU_DATA_BUFFER_MAX_LEN == 0)) { - progress_cb(fptr * 100 / file_size, context); - } - - file_crc = furi_hal_crc_feed(data_buffer, data_buffer_valid_len); - } - furi_hal_crc_reset(); - free(data_buffer); + uint32_t file_crc = crc32_calc_file(dfuf, progress_cb, context); /* Last 4 bytes of DFU file = CRC of previous file contents, inverted * If we calculate whole file CRC32, incl. embedded CRC, diff --git a/lib/update_util/update_manifest.c b/lib/update_util/update_manifest.c index 3d9ec9c3..3d05d0ba 100644 --- a/lib/update_util/update_manifest.c +++ b/lib/update_util/update_manifest.c @@ -14,6 +14,9 @@ #define MANIFEST_KEY_RADIO_VERSION "Radio version" #define MANIFEST_KEY_RADIO_CRC "Radio CRC" #define MANIFEST_KEY_ASSETS_FILE "Resources" +#define MANIFEST_KEY_OB_REFERENCE "OB reference" +#define MANIFEST_KEY_OB_MASK "OB mask" +#define MANIFEST_KEY_OB_WRITE_MASK "OB write mask" UpdateManifest* update_manifest_alloc() { UpdateManifest* update_manifest = malloc(sizeof(UpdateManifest)); @@ -23,6 +26,9 @@ UpdateManifest* update_manifest_alloc() { string_init(update_manifest->staged_loader_file); string_init(update_manifest->resource_bundle); update_manifest->target = 0; + memset(update_manifest->ob_reference.bytes, 0, FURI_HAL_FLASH_OB_RAW_SIZE_BYTES); + memset(update_manifest->ob_compare_mask.bytes, 0, FURI_HAL_FLASH_OB_RAW_SIZE_BYTES); + memset(update_manifest->ob_write_mask.bytes, 0, FURI_HAL_FLASH_OB_RAW_SIZE_BYTES); update_manifest->valid = false; return update_manifest; } @@ -75,8 +81,8 @@ static bool flipper_format_read_hex( flipper_file, MANIFEST_KEY_RADIO_VERSION, - (uint8_t*)&update_manifest->radio_version, - sizeof(uint32_t)); + update_manifest->radio_version.raw, + sizeof(UpdateManifestRadioVersion)); flipper_format_read_hex( flipper_file, MANIFEST_KEY_RADIO_CRC, @@ -85,6 +91,22 @@ static bool flipper_format_read_string( flipper_file, MANIFEST_KEY_ASSETS_FILE, update_manifest->resource_bundle); + flipper_format_read_hex( + flipper_file, + MANIFEST_KEY_OB_REFERENCE, + update_manifest->ob_reference.bytes, + FURI_HAL_FLASH_OB_RAW_SIZE_BYTES); + flipper_format_read_hex( + flipper_file, + MANIFEST_KEY_OB_MASK, + update_manifest->ob_compare_mask.bytes, + FURI_HAL_FLASH_OB_RAW_SIZE_BYTES); + flipper_format_read_hex( + flipper_file, + MANIFEST_KEY_OB_WRITE_MASK, + update_manifest->ob_write_mask.bytes, + FURI_HAL_FLASH_OB_RAW_SIZE_BYTES); + update_manifest->valid = (!string_empty_p(update_manifest->firmware_dfu_image) || !string_empty_p(update_manifest->radio_image) || @@ -94,6 +116,41 @@ static bool return update_manifest->valid; } +// Verifies that mask values are same for adjacent words (value & inverted) +static bool ob_data_check_mask_valid(const FuriHalFlashRawOptionByteData* mask) { + bool mask_valid = true; + for(size_t idx = 0; mask_valid && (idx < FURI_HAL_FLASH_OB_TOTAL_VALUES); ++idx) { + mask_valid &= mask->obs[idx].values.base == mask->obs[idx].values.complementary_value; + } + return mask_valid; +} + +// Verifies that all reference values have no unmasked bits +static bool ob_data_check_masked_values_valid( + const FuriHalFlashRawOptionByteData* data, + const FuriHalFlashRawOptionByteData* mask) { + bool valid = true; + for(size_t idx = 0; valid && (idx < FURI_HAL_FLASH_OB_TOTAL_VALUES); ++idx) { + valid &= (data->obs[idx]. dword & mask->obs[idx].dword) == + data->obs[idx].dword; + } + return valid; +} + +bool update_manifest_has_obdata(UpdateManifest* update_manifest) { + bool ob_data_valid = false; + // do we have at least 1 value? + for(size_t idx = 0; !ob_data_valid && (idx < FURI_HAL_FLASH_OB_RAW_SIZE_BYTES); ++idx) { + ob_data_valid |= update_manifest->ob_reference.bytes[idx] != 0; + } + // sanity checks + ob_data_valid &= ob_data_check_mask_valid(&update_manifest->ob_write_mask); + ob_data_valid &= ob_data_check_mask_valid(&update_manifest->ob_compare_mask); + ob_data_valid &= ob_data_check_masked_values_valid( + &update_manifest->ob_reference, &update_manifest->ob_compare_mask); + return ob_data_valid; +} + bool update_manifest_init(UpdateManifest* update_manifest, const char* manifest_filename) { Storage* storage = furi_record_open("storage"); FlipperFormat* flipper_file = flipper_format_file_alloc(storage); diff --git a/lib/update_util/update_manifest.h b/lib/update_util/update_manifest.h index 7d4757c4..d03c71d2 100644 --- a/lib/update_util/update_manifest.h +++ b/lib/update_util/update_manifest.h @@ -7,12 +7,26 @@ extern "C" { #include #include #include +#include /* Paths don't include /ext -- because at startup SD card is mounted as root */ #define UPDATE_DIR_DEFAULT_REL_PATH "/update" #define UPDATE_MANIFEST_DEFAULT_NAME "update.fuf" #define UPDATE_MAINFEST_DEFAULT_PATH UPDATE_DIR_DEFAULT_REL_PATH "/" UPDATE_MANIFEST_DEFAULT_NAME +typedef union { + uint8_t raw[6]; + struct { + uint8_t major; + uint8_t minor; + uint8_t sub; + uint8_t branch; + uint8_t release; + uint8_t type; + } version; +} UpdateManifestRadioVersion; +_Static_assert(sizeof(UpdateManifestRadioVersion) == 6, "UpdateManifestRadioVersion size error"); + typedef struct { string_t version; uint32_t target; @@ -21,9 +35,12 @@ typedef struct { string_t firmware_dfu_image; string_t radio_image; uint32_t radio_address; - uint32_t radio_version; + UpdateManifestRadioVersion radio_version; uint32_t radio_crc; string_t resource_bundle; + FuriHalFlashRawOptionByteData ob_reference; + FuriHalFlashRawOptionByteData ob_compare_mask; + FuriHalFlashRawOptionByteData ob_write_mask; bool valid; } UpdateManifest; @@ -38,6 +55,8 @@ bool update_manifest_init_mem( const uint8_t* manifest_data, const uint16_t length); +bool update_manifest_has_obdata(UpdateManifest* update_manifest); + #ifdef __cplusplus } #endif \ No newline at end of file diff --git a/lib/update_util/update_operation.c b/lib/update_util/update_operation.c index 2fd510b5..beea98d2 100644 --- a/lib/update_util/update_operation.c +++ b/lib/update_util/update_operation.c @@ -7,6 +7,7 @@ #include #include #include +#include static const char* UPDATE_ROOT_DIR = "/ext" UPDATE_DIR_DEFAULT_REL_PATH; static const char* UPDATE_PREFIX = "/ext" UPDATE_DIR_DEFAULT_REL_PATH "/"; @@ -156,8 +157,6 @@ UpdatePrepareResult update_operation_prepare(const char* manifest_file_path) { path_extract_dirname(manifest_file_path, stage_path); path_append(stage_path, string_get_cstr(manifest->staged_loader_file)); - const uint16_t READ_BLOCK = 0x1000; - uint8_t* read_buffer = malloc(READ_BLOCK); uint32_t crc = 0; do { if(!storage_file_open( @@ -166,19 +165,10 @@ UpdatePrepareResult update_operation_prepare(const char* manifest_file_path) { } result = UpdatePrepareResultStageIntegrityError; - furi_hal_crc_acquire(osWaitForever); - - uint16_t bytes_read = 0; - do { - bytes_read = storage_file_read(file, read_buffer, READ_BLOCK); - crc = furi_hal_crc_feed(read_buffer, bytes_read); - } while(bytes_read == READ_BLOCK); - - furi_hal_crc_reset(); + crc = crc32_calc_file(file, NULL, NULL); } while(false); string_clear(stage_path); - free(read_buffer); storage_file_free(file); if(crc == manifest->staged_loader_crc) { diff --git a/scripts/assets.py b/scripts/assets.py index bb7b493d..1363900f 100755 --- a/scripts/assets.py +++ b/scripts/assets.py @@ -50,6 +50,26 @@ class Main(App): self.parser_copro.add_argument("cube_dir", help="Path to Cube folder") self.parser_copro.add_argument("output_dir", help="Path to output folder") self.parser_copro.add_argument("mcu", help="MCU series as in copro folder") + self.parser_copro.add_argument( + "--cube_ver", dest="cube_ver", help="Cube version", required=True + ) + self.parser_copro.add_argument( + "--stack_type", dest="stack_type", help="Stack type", required=True + ) + self.parser_copro.add_argument( + "--stack_file", + dest="stack_file", + help="Stack file name in copro folder", + required=True, + ) + self.parser_copro.add_argument( + "--stack_addr", + dest="stack_addr", + help="Stack flash address, as per release_notes", + type=lambda x: int(x, 16), + default=0, + required=False, + ) self.parser_copro.set_defaults(func=self.copro) self.parser_dolphin = self.subparsers.add_parser( @@ -203,13 +223,15 @@ class Main(App): manifest_file = os.path.join(directory_path, "Manifest") old_manifest = Manifest() if os.path.exists(manifest_file): - self.logger.info("old manifest is present, loading for compare") + self.logger.info("Manifest is present, loading to compare") old_manifest.load(manifest_file) - self.logger.info(f'Creating new Manifest for directory "{directory_path}"') + self.logger.info( + f'Creating temporary Manifest for directory "{directory_path}"' + ) new_manifest = Manifest() new_manifest.create(directory_path) - self.logger.info(f"Comparing new manifest with old") + self.logger.info(f"Comparing new manifest with existing") only_in_old, changed, only_in_new = Manifest.compare(old_manifest, new_manifest) for record in only_in_old: self.logger.info(f"Only in old: {record}") @@ -233,9 +255,14 @@ class Main(App): self.logger.info(f"Bundling coprocessor binaries") copro = Copro(self.args.mcu) self.logger.info(f"Loading CUBE info") - copro.loadCubeInfo(self.args.cube_dir) + copro.loadCubeInfo(self.args.cube_dir, self.args.cube_ver) self.logger.info(f"Bundling") - copro.bundle(self.args.output_dir) + copro.bundle( + self.args.output_dir, + self.args.stack_file, + self.args.stack_type, + self.args.stack_addr, + ) self.logger.info(f"Complete") return 0 diff --git a/scripts/dist.py b/scripts/dist.py index de1a02c8..ccede12b 100755 --- a/scripts/dist.py +++ b/scripts/dist.py @@ -91,6 +91,7 @@ class Main(App): self.args.resources, ) ) + bundle_args.extend(self.other_args) self.logger.info( f"Use this directory to self-update your Flipper:\n\t{bundle_dir}" ) diff --git a/scripts/flash.py b/scripts/flash.py index cc5c5e17..fb05b8b0 100755 --- a/scripts/flash.py +++ b/scripts/flash.py @@ -7,6 +7,7 @@ import os from flipper.app import App from flipper.cube import CubeProgrammer +from flipper.assets.coprobin import CoproBinary STATEMENT = "AGREE_TO_LOSE_FLIPPER_FEATURES_THAT_USE_CRYPTO_ENCLAVE" @@ -68,10 +69,15 @@ class Main(App): ) self._addArgsSWD(self.parser_core2radio) self.parser_core2radio.add_argument( - "radio_address", type=str, help="Radio Stack Binary Address" + "radio", type=str, help="Radio Stack Binary" ) self.parser_core2radio.add_argument( - "radio", type=str, help="Radio Stack Binary" + "--addr", + dest="radio_address", + help="Radio Stack Binary Address, as per release_notes", + type=lambda x: int(x, 16), + default=0, + required=False, ) self.parser_core2radio.set_defaults(func=self.core2radio) @@ -144,14 +150,27 @@ class Main(App): return 0 def core2radio(self): - if int(self.args.radio_address, 16) > 0x080E0000: + stack_info = CoproBinary(self.args.radio) + if not stack_info.is_stack(): + self.logger.error("Not a Radio Stack") + return 1 + self.logger.info(f"Will flash {stack_info.img_sig.get_version()}") + + radio_address = self.args.radio_address + if not radio_address: + radio_address = stack_info.get_flash_load_addr() + self.logger.warning( + f"Radio address not provided, guessed as 0x{radio_address:X}" + ) + if radio_address > 0x080E0000: self.logger.error(f"I KNOW WHAT YOU DID LAST SUMMER") return 1 + cp = CubeProgrammer(self._getCubeParams()) self.logger.info(f"Removing Current Radio Stack") cp.deleteCore2RadioStack() self.logger.info(f"Flashing Radio Stack") - cp.flashCore2(self.args.radio_address, self.args.radio) + cp.flashCore2(radio_address, self.args.radio) self.logger.info(f"Complete") return 0 diff --git a/scripts/flipper/app.py b/scripts/flipper/app.py index ea62f674..bd0cbc37 100644 --- a/scripts/flipper/app.py +++ b/scripts/flipper/app.py @@ -16,7 +16,7 @@ class App: self.init() def __call__(self, args=None): - self.args, _ = self.parser.parse_known_args(args=args) + self.args, self.other_args = self.parser.parse_known_args(args=args) # configure log output self.log_level = logging.DEBUG if self.args.debug else logging.INFO self.logger.setLevel(self.log_level) diff --git a/scripts/flipper/assets/copro.py b/scripts/flipper/assets/copro.py index edf6bdd7..2c4c1a9f 100644 --- a/scripts/flipper/assets/copro.py +++ b/scripts/flipper/assets/copro.py @@ -2,9 +2,12 @@ import logging import datetime import shutil import json +from os.path import basename import xml.etree.ElementTree as ET from flipper.utils import * +from flipper.assets.coprobin import CoproBinary, get_stack_type + CUBE_COPRO_PATH = "Projects/STM32WB_Copro_Wireless_Binaries" @@ -13,14 +16,7 @@ MANIFEST_TEMPLATE = { "copro": { "fus": {"version": {"major": 1, "minor": 2, "sub": 0}, "files": []}, "radio": { - "version": { - "type": 3, - "major": 1, - "minor": 13, - "sub": 0, - "branch": 0, - "release": 5, - }, + "version": {}, "files": [], }, }, @@ -35,7 +31,7 @@ class Copro: self.mcu_copro = None self.logger = logging.getLogger(self.__class__.__name__) - def loadCubeInfo(self, cube_dir): + def loadCubeInfo(self, cube_dir, cube_version): if not os.path.isdir(cube_dir): raise Exception(f'"{cube_dir}" doesn\'t exists') self.cube_dir = cube_dir @@ -51,8 +47,8 @@ class Copro: if not cube_version or not cube_version.startswith("FW.WB"): raise Exception(f"Incorrect Cube package or version info") cube_version = cube_version.replace("FW.WB.", "", 1) - if cube_version != "1.13.1": - raise Exception(f"Unknonwn cube version") + if cube_version != cube_version: + raise Exception(f"Unsupported cube version") self.version = cube_version def addFile(self, array, filename, **kwargs): @@ -63,14 +59,32 @@ class Copro: {"name": filename, "sha256": file_sha256(destination_file), **kwargs} ) - def bundle(self, output_dir): + def bundle(self, output_dir, stack_file_name, stack_type, stack_addr=None): if not os.path.isdir(output_dir): raise Exception(f'"{output_dir}" doesn\'t exists') self.output_dir = output_dir + stack_file = os.path.join(self.mcu_copro, stack_file_name) manifest_file = os.path.join(self.output_dir, "Manifest.json") # Form Manifest manifest = dict(MANIFEST_TEMPLATE) manifest["manifest"]["timestamp"] = timestamp() + copro_bin = CoproBinary(stack_file) + self.logger.info(f"Bundling {copro_bin.img_sig.get_version()}") + stack_type_code = get_stack_type(stack_type) + manifest["copro"]["radio"]["version"].update( + { + "type": stack_type_code, + "major": copro_bin.img_sig.version_major, + "minor": copro_bin.img_sig.version_minor, + "sub": copro_bin.img_sig.version_sub, + "branch": copro_bin.img_sig.version_branch, + "release": copro_bin.img_sig.version_build, + } + ) + if not stack_addr: + stack_addr = copro_bin.get_flash_load_addr() + self.logger.info(f"Using guessed flash address 0x{stack_addr:x}") + # Old FUS Update self.addFile( manifest["copro"]["fus"]["files"], @@ -88,8 +102,8 @@ class Copro: # BLE Full Stack self.addFile( manifest["copro"]["radio"]["files"], - "stm32wb5x_BLE_Stack_light_fw.bin", - address="0x080D7000", + stack_file_name, + address=f"0x{stack_addr:X}", ) # Save manifest to json.dump(manifest, open(manifest_file, "w")) diff --git a/scripts/flipper/assets/coprobin.py b/scripts/flipper/assets/coprobin.py new file mode 100644 index 00000000..64f0b8c8 --- /dev/null +++ b/scripts/flipper/assets/coprobin.py @@ -0,0 +1,187 @@ +import struct +import math +import os, os.path +import sys + + +# From STM32CubeWB\Middlewares\ST\STM32_WPAN\interface\patterns\ble_thread\shci\shci.h +__STACK_TYPE_CODES = { + "BLE_FULL": 0x01, + "BLE_HCI": 0x02, + "BLE_LIGHT": 0x03, + "BLE_BEACON": 0x04, + "BLE_BASIC": 0x05, + "BLE_FULL_EXT_ADV": 0x06, + "BLE_HCI_EXT_ADV": 0x07, + "THREAD_FTD": 0x10, + "THREAD_MTD": 0x11, + "ZIGBEE_FFD": 0x30, + "ZIGBEE_RFD": 0x31, + "MAC": 0x40, + "BLE_THREAD_FTD_STATIC": 0x50, + "BLE_THREAD_FTD_DYAMIC": 0x51, + "802154_LLD_TESTS": 0x60, + "802154_PHY_VALID": 0x61, + "BLE_PHY_VALID": 0x62, + "BLE_LLD_TESTS": 0x63, + "BLE_RLV": 0x64, + "802154_RLV": 0x65, + "BLE_ZIGBEE_FFD_STATIC": 0x70, + "BLE_ZIGBEE_RFD_STATIC": 0x71, + "BLE_ZIGBEE_FFD_DYNAMIC": 0x78, + "BLE_ZIGBEE_RFD_DYNAMIC": 0x79, + "RLV": 0x80, + "BLE_MAC_STATIC": 0x90, +} + + +class CoproException(ValueError): + pass + + +# Formats based on AN5185 +class CoproFooterBase: + SIG_BIN_SIZE = 5 * 4 + _SIG_BIN_COMMON_SIZE = 2 * 4 + + def get_version(self): + return f"Version {self.version_major}.{self.version_minor}.{self.version_sub}, branch {self.version_branch}, build {self.version_build} (magic {self.magic:X})" + + def get_details(self): + raise CoproException("Not implemented") + + def __init__(self, raw: bytes): + if len(raw) != self.SIG_BIN_SIZE: + raise CoproException("Invalid footer size") + sig_common_part = raw[-self._SIG_BIN_COMMON_SIZE :] + parts = struct.unpack("BBBBI", sig_common_part) + self.version_major = parts[3] + self.version_minor = parts[2] + self.version_sub = parts[1] + # AN5185 mismatch: swapping byte halves + self.version_build = parts[0] & 0x0F + self.version_branch = (parts[0] & 0xF0) >> 4 + self.magic = parts[4] + + +class CoproFusFooter(CoproFooterBase): + FUS_MAGIC_IMG_STACK = 0x23372991 + FUS_MAGIC_IMG_FUS = 0x32279221 + FUS_MAGIC_IMG_OTHER = 0x42769811 + + FUS_BASE = 0x80F4000 + FLASH_PAGE_SIZE = 4 * 1024 + + def __init__(self, raw: bytes): + super().__init__(raw) + if self.magic not in ( + self.FUS_MAGIC_IMG_OTHER, + self.FUS_MAGIC_IMG_FUS, + self.FUS_MAGIC_IMG_STACK, + ): + raise CoproException(f"Invalid FUS img magic {self.magic:x}") + own_data = raw[: -self._SIG_BIN_COMMON_SIZE] + parts = struct.unpack("IIBBBB", own_data) + self.info1 = parts[0] + self.info2 = parts[1] + self.sram2b_1ks = parts[5] + self.sram2a_1ks = parts[4] + self.flash_4ks = parts[2] + + def get_details(self): + return f"SRAM2b={self.sram2b_1ks}k SRAM2a={self.sram2a_1ks}k flash={self.flash_4ks}p" + + def is_stack(self): + return self.magic == self.FUS_MAGIC_IMG_STACK + + def get_flash_pages(self, fullsize): + return math.ceil(fullsize / self.FLASH_PAGE_SIZE) + + def get_flash_base(self, fullsize): + if not self.is_stack(): + raise CoproException("Not a stack image") + return self.FUS_BASE - self.get_flash_pages(fullsize) * self.FLASH_PAGE_SIZE + + +class CoproSigFooter(CoproFooterBase): + SIG_MAGIC_ST = 0xD3A12C5E + SIG_MAGIC_CUSTOMER = 0xE2B51D4A + + def __init__(self, raw: bytes): + super().__init__(raw) + if self.magic not in (self.SIG_MAGIC_ST, self.SIG_MAGIC_CUSTOMER): + raise CoproException(f"Invalid FUS img magic {self.magic:x}") + own_data = raw[: -self._SIG_BIN_COMMON_SIZE] + parts = struct.unpack("IIBBH", own_data) + self.reserved_1 = parts[0] + self.reserved_2 = parts[1] + self.size = parts[2] + self.source = parts[3] + self.reserved_34 = parts[4] + + def get_details(self): + return f"Signature Src {self.source:x} size {self.size:x}" + + +class CoproBinary: + def __init__(self, binary_path): + self.binary_path = binary_path + self.img_sig_footer = None + self.img_sig = None + self.binary_size = -1 + self._load() + + def _load(self): + with open(self.binary_path, "rb") as fin: + whole_file = fin.read() + self.binary_size = len(whole_file) + + img_sig_footer_bin = whole_file[-CoproFooterBase.SIG_BIN_SIZE :] + self.img_sig_footer = CoproSigFooter(img_sig_footer_bin) + img_sig_size = self.img_sig_footer.size + CoproSigFooter.SIG_BIN_SIZE + img_sig_bin = whole_file[ + -(img_sig_size + CoproFusFooter.SIG_BIN_SIZE) : -img_sig_size + ] + self.img_sig = CoproFusFooter(img_sig_bin) + + def is_valid(self): + return self.img_sig_footer is not None and self.img_sig is not None + + def is_stack(self): + return self.img_sig and self.img_sig.is_stack() + + def get_flash_load_addr(self): + if not self.is_stack(): + raise CoproException("Not a stack image") + return self.img_sig.get_flash_base(self.binary_size) + + +def get_stack_type(typestr: str): + stack_code = __STACK_TYPE_CODES.get(typestr.upper(), None) + if stack_code is None: + raise CoproException(f"Unknown stack type {typestr}. See shci.h") + return stack_code + + +def _load_bin(binary_path: str): + print(binary_path) + copro_bin = CoproBinary(binary_path) + print(copro_bin.img_sig.get_version()) + if copro_bin.img_sig.is_stack(): + print(f"\t>> FLASH AT {copro_bin.get_flash_load_addr():X}\n") + + +def main(): + coprodir = ( + sys.argv[1] + if len(sys.argv) > 1 + else "../../../lib/STM32CubeWB/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x" + ) + for fn in os.listdir(coprodir): + if not fn.endswith(".bin"): + continue + _load_bin(os.path.join(coprodir, fn)) + + +if __name__ == "__main__": + main() diff --git a/scripts/flipper/assets/manifest.py b/scripts/flipper/assets/manifest.py index 103f07b6..f7526ac7 100644 --- a/scripts/flipper/assets/manifest.py +++ b/scripts/flipper/assets/manifest.py @@ -135,6 +135,8 @@ class Manifest: def create(self, directory_path, ignore_files=["Manifest"]): for root, dirs, files in os.walk(directory_path): + dirs.sort() + files.sort() relative_root = root.replace(directory_path, "", 1) if relative_root.startswith("/"): relative_root = relative_root[1:] diff --git a/scripts/flipper/assets/obdata.py b/scripts/flipper/assets/obdata.py new file mode 100644 index 00000000..0f7f5c19 --- /dev/null +++ b/scripts/flipper/assets/obdata.py @@ -0,0 +1,208 @@ +#!/usr/bin/env python3 + +import logging +import struct + +from enum import Enum +from dataclasses import dataclass +from typing import Tuple +from array import array + + +class OBException(ValueError): + pass + + +@dataclass +class OBParams: + word_idx: int + bits: Tuple[int, int] + name: str + + +_OBS_descr = ( + OBParams(0, (0, 8), "RDP"), + OBParams(0, (8, 9), "ESE"), + OBParams(0, (9, 12), "BOR_LEV"), + OBParams(0, (12, 13), "nRST_STOP"), + OBParams(0, (13, 14), "nRST_STDBY"), + OBParams(0, (14, 15), "nRSTSHDW"), + OBParams(0, (15, 16), "UNUSED1"), + OBParams(0, (16, 17), "IWDGSW"), + OBParams(0, (17, 18), "IWDGSTOP"), + OBParams(0, (18, 19), "IWGDSTDBY"), # ST's typo: IWDGSTDBY + OBParams(0, (18, 19), "IWDGSTDBY"), # ST's typo: IWDGSTDBY + OBParams(0, (19, 20), "WWDGSW"), + OBParams(0, (20, 23), "UNUSED2"), + OBParams(0, (23, 24), "nBOOT1"), + OBParams(0, (24, 25), "SRAM2PE"), + OBParams(0, (25, 26), "SRAM2RST"), + OBParams(0, (26, 27), "nSWBOOT0"), + OBParams(0, (27, 28), "nBOOT0"), + OBParams(0, (28, 29), "UNUSED3"), + OBParams(0, (29, 32), "AGC_TRIM"), + OBParams(1, (0, 9), "PCROP1A_STRT"), + OBParams(1, (9, 32), "UNUSED"), + OBParams(2, (0, 9), "PCROP1A_END"), + OBParams(2, (9, 31), "UNUSED"), + OBParams(2, (31, 32), "PCROP_RDP"), + OBParams(3, (0, 8), "WRP1A_STRT"), + OBParams(3, (8, 16), "UNUSED1"), + OBParams(3, (16, 24), "WRP1A_END"), + OBParams(3, (24, 32), "UNUSED2"), + OBParams(4, (0, 8), "WRP1B_STRT"), + OBParams(4, (8, 16), "UNUSED1"), + OBParams(4, (16, 24), "WRP1B_END"), + OBParams(4, (24, 32), "UNUSED2"), + OBParams(5, (0, 9), "PCROP1B_STRT"), + OBParams(5, (9, 32), "UNUSED"), + OBParams(6, (0, 9), "PCROP1B_END"), + OBParams(6, (9, 32), "UNUSED"), + OBParams(13, (0, 14), "IPCCDBA"), + OBParams(13, (14, 32), "UNUSED"), + OBParams(14, (0, 8), "SFSA"), + OBParams(14, (8, 9), "FSD"), + OBParams(14, (9, 12), "UNUSED1"), + OBParams(14, (12, 13), "DDS"), + OBParams(14, (13, 32), "UNUSED2"), + OBParams(15, (0, 18), "SBRV"), + OBParams(15, (18, 23), "SBRSA"), + OBParams(15, (23, 24), "BRSD"), + OBParams(15, (24, 25), "UNUSED1"), + OBParams(15, (25, 30), "SNBRSA"), + OBParams(15, (30, 31), "NBRSD"), + OBParams(15, (31, 32), "C2OPT"), +) + + +_OBS = dict((param.name, param) for param in _OBS_descr) + + +@dataclass +class EncodedOBValue: + value: int + mask: int + params: OBParams + + +class OptionByte: + class OBMode(Enum): + IGNORE = 0 + READ = 1 + READ_WRITE = 2 + + @classmethod + def from_str(cls, value): + if value == "r": + return cls.READ + elif value == "rw": + return cls.READ_WRITE + else: + raise OBException(f"Unknown OB check mode '{value}'") + + def __init__(self, obstr): + parts = obstr.split(":") + if len(parts) != 3: + raise OBException(f"Invalid OB value definition {obstr}") + self.name = parts[0] + self.value = int(parts[1], 16) + self.mode = OptionByte.OBMode.from_str(parts[2].strip()) + self.descr = _OBS.get(self.name, None) + if self.descr is None: + raise OBException(f"Missing OB descriptor for {self.name}") + + def encode(self): + startbit, endbit = self.descr.bits + value_mask = 2 ** (endbit - startbit) - 1 + value_corrected = self.value & value_mask + + value_shifted = value_corrected << startbit + value_mask_shifted = value_mask << startbit + return EncodedOBValue(value_shifted, value_mask_shifted, self) + + def __repr__(self): + return f"" + + +@dataclass +class ObReferenceValues: + reference: bytes + compare_mask: bytes + write_mask: bytes + + +class ObReferenceValuesGenerator: + def __init__(self): + self.compare_mask = array("I", [0] * 16) + self.write_mask = array("I", [0] * 16) + self.ref_values = array("I", [0] * 16) + + def __repr__(self): + return ( + f"