Merge branch 'dev' into release-candidate
This commit is contained in:
		
						commit
						b501d47943
					
				
							
								
								
									
										14
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										14
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							| @ -121,11 +121,11 @@ jobs: | |||||||
| 
 | 
 | ||||||
|       - name: 'Bundle core2 firmware' |       - name: 'Bundle core2 firmware' | ||||||
|         if: ${{ !github.event.pull_request.head.repo.fork }} |         if: ${{ !github.event.pull_request.head.repo.fork }} | ||||||
|  |         uses: ./.github/actions/docker | ||||||
|  |         with: | ||||||
|           run: | |           run: | | ||||||
|           test -d core2_firmware && rm -rf core2_firmware || true |             make -C assets copro_bundle | ||||||
|           mkdir core2_firmware |             tar czpf artifacts/flipper-z-any-core2_firmware-${{steps.names.outputs.suffix}}.tgz -C assets 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 |  | ||||||
| 
 | 
 | ||||||
|       - name: 'Upload artifacts to update server' |       - name: 'Upload artifacts to update server' | ||||||
|         if: ${{ !github.event.pull_request.head.repo.fork }} |         if: ${{ !github.event.pull_request.head.repo.fork }} | ||||||
| @ -161,7 +161,7 @@ jobs: | |||||||
|           comment-id: ${{ steps.fc.outputs.comment-id }} |           comment-id: ${{ steps.fc.outputs.comment-id }} | ||||||
|           issue-number: ${{ github.event.pull_request.number }} |           issue-number: ${{ github.event.pull_request.number }} | ||||||
|           body: | |           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 |           edit-mode: replace | ||||||
| 
 | 
 | ||||||
|   compact: |   compact: | ||||||
| @ -213,8 +213,8 @@ jobs: | |||||||
|         with: |         with: | ||||||
|           run: | |           run: | | ||||||
|             set -e |             set -e | ||||||
|             make -C assets clean |             make assets_rebuild assets_manifest | ||||||
|             make -C assets |             git diff --quiet || ( echo "Assets recompilation required."; exit 255 ) | ||||||
| 
 | 
 | ||||||
|       - name: 'Build the firmware in docker' |       - name: 'Build the firmware in docker' | ||||||
|         uses: ./.github/actions/docker |         uses: ./.github/actions/docker | ||||||
|  | |||||||
							
								
								
									
										17
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								Makefile
									
									
									
									
									
								
							| @ -1,8 +1,7 @@ | |||||||
| PROJECT_ROOT := $(abspath $(dir $(abspath $(firstword $(MAKEFILE_LIST))))) | PROJECT_ROOT := $(abspath $(dir $(abspath $(firstword $(MAKEFILE_LIST))))) | ||||||
| 
 | 
 | ||||||
| include			$(PROJECT_ROOT)/make/git.mk | include			$(PROJECT_ROOT)/make/git.mk | ||||||
| 
 | include			$(PROJECT_ROOT)/assets/copro.mk | ||||||
| COPRO_DIR := $(PROJECT_ROOT)/lib/STM32CubeWB/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x |  | ||||||
| 
 | 
 | ||||||
| PROJECT_SOURCE_DIRECTORIES := \
 | PROJECT_SOURCE_DIRECTORIES := \
 | ||||||
| 	$(PROJECT_ROOT)/applications \
 | 	$(PROJECT_ROOT)/applications \
 | ||||||
| @ -97,7 +96,13 @@ updater_package_bin: firmware_all updater | |||||||
| 
 | 
 | ||||||
| .PHONY: updater_package | .PHONY: updater_package | ||||||
| updater_package: firmware_all updater assets_manifest | 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 | .PHONY: assets_manifest | ||||||
| assets_manifest: | assets_manifest: | ||||||
| @ -109,7 +114,7 @@ assets_rebuild: | |||||||
| 
 | 
 | ||||||
| .PHONY: flash_radio | .PHONY: flash_radio | ||||||
| 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 | 	@$(PROJECT_ROOT)/scripts/ob.py set | ||||||
| 
 | 
 | ||||||
| .PHONY: flash_radio_fus | .PHONY: flash_radio_fus | ||||||
| @ -125,8 +130,8 @@ flash_radio_fus: | |||||||
| 
 | 
 | ||||||
| .PHONY: flash_radio_fus_please_i_m_not_going_to_complain | .PHONY: flash_radio_fus_please_i_m_not_going_to_complain | ||||||
| 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_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_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.bin | ||||||
| 	@$(PROJECT_ROOT)/scripts/ob.py set | 	@$(PROJECT_ROOT)/scripts/ob.py set | ||||||
| 
 | 
 | ||||||
| .PHONY: lint | .PHONY: lint | ||||||
|  | |||||||
| @ -32,7 +32,6 @@ void AccessorApp::run(void) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| AccessorApp::AccessorApp() { | AccessorApp::AccessorApp() { | ||||||
|     furi_hal_power_insomnia_enter(); |  | ||||||
|     notification = static_cast<NotificationApp*>(furi_record_open("notification")); |     notification = static_cast<NotificationApp*>(furi_record_open("notification")); | ||||||
|     onewire_host = onewire_host_alloc(); |     onewire_host = onewire_host_alloc(); | ||||||
|     furi_hal_power_enable_otg(); |     furi_hal_power_enable_otg(); | ||||||
| @ -42,7 +41,6 @@ AccessorApp::~AccessorApp() { | |||||||
|     furi_hal_power_disable_otg(); |     furi_hal_power_disable_otg(); | ||||||
|     furi_record_close("notification"); |     furi_record_close("notification"); | ||||||
|     onewire_host_free(onewire_host); |     onewire_host_free(onewire_host); | ||||||
|     furi_hal_power_insomnia_exit(); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| AccessorAppViewManager* AccessorApp::get_view_manager() { | AccessorAppViewManager* AccessorApp::get_view_manager() { | ||||||
|  | |||||||
| @ -74,61 +74,113 @@ extern int32_t system_settings_app(void* p); | |||||||
| const FlipperApplication FLIPPER_SERVICES[] = { | const FlipperApplication FLIPPER_SERVICES[] = { | ||||||
| /* Services */ | /* Services */ | ||||||
| #ifdef SRV_RPC | #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 | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef SRV_BT | #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 | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef SRV_CLI | #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 | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef SRV_DIALOGS | #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 | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef SRV_DOLPHIN | #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 | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef SRV_DESKTOP | #ifdef SRV_DESKTOP | ||||||
| #ifdef SRV_UPDATER | #ifdef SRV_UPDATER | ||||||
| #error SRV_UPDATER and SRV_DESKTOP are mutually exclusive! | #error SRV_UPDATER and SRV_DESKTOP are mutually exclusive! | ||||||
| #endif | #endif | ||||||
|     {.app = desktop_srv, .name = "DesktopSrv", .stack_size = 2048, .icon = NULL}, |     {.app = desktop_srv, | ||||||
|  |      .name = "DesktopSrv", | ||||||
|  |      .stack_size = 2048, | ||||||
|  |      .icon = NULL, | ||||||
|  |      .flags = FlipperApplicationFlagDefault}, | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef SRV_GUI | #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 | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef SRV_INPUT | #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 | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef SRV_LOADER | #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 | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef SRV_NOTIFICATION | #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 | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef SRV_POWER | #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 | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef SRV_STORAGE | #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 | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef SRV_UPDATER | #ifdef SRV_UPDATER | ||||||
| #ifdef SRV_DESKTOP | #ifdef SRV_DESKTOP | ||||||
| #error SRV_UPDATER and SRV_DESKTOP are mutually exclusive! | #error SRV_UPDATER and SRV_DESKTOP are mutually exclusive! | ||||||
| #endif | #endif | ||||||
|     {.app = updater_srv, .name = "UpdaterSrv", .stack_size = 2048, .icon = NULL}, |     {.app = updater_srv, | ||||||
|  |      .name = "UpdaterSrv", | ||||||
|  |      .stack_size = 2048, | ||||||
|  |      .icon = NULL, | ||||||
|  |      .flags = FlipperApplicationFlagDefault}, | ||||||
| #endif | #endif | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| @ -139,7 +191,11 @@ const FlipperApplication FLIPPER_SYSTEM_APPS[] = { | |||||||
| #ifdef SRV_UPDATER | #ifdef SRV_UPDATER | ||||||
| #error APP_UPDATER and SRV_UPDATER are mutually exclusive! | #error APP_UPDATER and SRV_UPDATER are mutually exclusive! | ||||||
| #endif | #endif | ||||||
|     {.app = updater_srv, .name = "UpdaterApp", .stack_size = 2048, .icon = NULL}, |     {.app = updater_srv, | ||||||
|  |      .name = "UpdaterApp", | ||||||
|  |      .stack_size = 2048, | ||||||
|  |      .icon = NULL, | ||||||
|  |      .flags = FlipperApplicationFlagDefault}, | ||||||
| #endif | #endif | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| @ -149,35 +205,67 @@ const size_t FLIPPER_SYSTEM_APPS_COUNT = COUNT_OF(FLIPPER_SYSTEM_APPS); | |||||||
| const FlipperApplication FLIPPER_APPS[] = { | const FlipperApplication FLIPPER_APPS[] = { | ||||||
| 
 | 
 | ||||||
| #ifdef APP_SUBGHZ | #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 | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef APP_LF_RFID | #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 | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef APP_NFC | #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 | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef APP_INFRARED | #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 | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef APP_GPIO | #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 | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef APP_IBUTTON | #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 | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef APP_BAD_USB | #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 | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef APP_U2F | #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 | #endif | ||||||
| 
 | 
 | ||||||
| }; | }; | ||||||
| @ -234,15 +322,27 @@ const size_t FLIPPER_ON_SYSTEM_START_COUNT = COUNT_OF(FLIPPER_ON_SYSTEM_START); | |||||||
| // Plugin menu
 | // Plugin menu
 | ||||||
| const FlipperApplication FLIPPER_PLUGINS[] = { | const FlipperApplication FLIPPER_PLUGINS[] = { | ||||||
| #ifdef APP_BLE_HID | #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 | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef APP_MUSIC_PLAYER | #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 | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef APP_SNAKE_GAME | #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 | #endif | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| @ -251,108 +351,201 @@ const size_t FLIPPER_PLUGINS_COUNT = COUNT_OF(FLIPPER_PLUGINS); | |||||||
| // Plugin menu
 | // Plugin menu
 | ||||||
| const FlipperApplication FLIPPER_DEBUG_APPS[] = { | const FlipperApplication FLIPPER_DEBUG_APPS[] = { | ||||||
| #ifdef APP_BLINK | #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 | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef APP_VIBRO_TEST | #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 | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef APP_KEYPAD_TEST | #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 | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef APP_ACCESSOR | #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 | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef APP_USB_TEST | #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 | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef APP_USB_MOUSE | #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 | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef APP_UART_ECHO | #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 | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef APP_INFRARED_MONITOR | #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 | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef APP_SCENED | #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 | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef APP_LF_RFID | #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 | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef SRV_BT | #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 | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef APP_UNIT_TESTS | #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 | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef APP_DISPLAY_TEST | #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 | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef APP_BATTERY_TEST | #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 | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef APP_TEXT_BOX_TEST | #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 | #endif | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const size_t FLIPPER_DEBUG_APPS_COUNT = COUNT_OF(FLIPPER_DEBUG_APPS); | const size_t FLIPPER_DEBUG_APPS_COUNT = COUNT_OF(FLIPPER_DEBUG_APPS); | ||||||
| 
 | 
 | ||||||
| #ifdef APP_ARCHIVE | #ifdef APP_ARCHIVE | ||||||
| const FlipperApplication FLIPPER_ARCHIVE = | const FlipperApplication FLIPPER_ARCHIVE = { | ||||||
|     {.app = archive_app, .name = "Archive", .stack_size = 4096, .icon = &A_FileManager_14}; |     .app = archive_app, | ||||||
|  |     .name = "Archive", | ||||||
|  |     .stack_size = 4096, | ||||||
|  |     .icon = &A_FileManager_14, | ||||||
|  |     .flags = FlipperApplicationFlagDefault}; | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| // Settings menu
 | // Settings menu
 | ||||||
| const FlipperApplication FLIPPER_SETTINGS_APPS[] = { | const FlipperApplication FLIPPER_SETTINGS_APPS[] = { | ||||||
| #ifdef SRV_BT | #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 | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef SRV_NOTIFICATION | #ifdef SRV_NOTIFICATION | ||||||
|     {.app = notification_settings_app, |     {.app = notification_settings_app, | ||||||
|      .name = "LCD and Notifications", |      .name = "LCD and Notifications", | ||||||
|      .stack_size = 1024, |      .stack_size = 1024, | ||||||
|      .icon = NULL}, |      .icon = NULL, | ||||||
|  |      .flags = FlipperApplicationFlagDefault}, | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef SRV_STORAGE | #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 | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef SRV_POWER | #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 | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef SRV_DESKTOP | #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 | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef APP_PASSPORT | #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 | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef SRV_GUI | #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 | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef APP_ABOUT | #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 | #endif | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -3,11 +3,17 @@ | |||||||
| #include <furi.h> | #include <furi.h> | ||||||
| #include <gui/icon.h> | #include <gui/icon.h> | ||||||
| 
 | 
 | ||||||
|  | typedef enum { | ||||||
|  |     FlipperApplicationFlagDefault = 0, | ||||||
|  |     FlipperApplicationFlagInsomniaSafe = (1 << 0), | ||||||
|  | } FlipperApplicationFlag; | ||||||
|  | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
|     const FuriThreadCallback app; |     const FuriThreadCallback app; | ||||||
|     const char* name; |     const char* name; | ||||||
|     const size_t stack_size; |     const size_t stack_size; | ||||||
|     const Icon* icon; |     const Icon* icon; | ||||||
|  |     const FlipperApplicationFlag flags; | ||||||
| } FlipperApplication; | } FlipperApplication; | ||||||
| 
 | 
 | ||||||
| typedef void (*FlipperOnStartHook)(void); | typedef void (*FlipperOnStartHook)(void); | ||||||
|  | |||||||
| @ -27,6 +27,7 @@ struct ArchiveApp { | |||||||
|     ArchiveBrowserView* browser; |     ArchiveBrowserView* browser; | ||||||
|     TextInput* text_input; |     TextInput* text_input; | ||||||
|     Widget* widget; |     Widget* widget; | ||||||
|  |     FuriPubSubSubscription* loader_stop_subscription; | ||||||
|     char text_store[MAX_NAME_LEN]; |     char text_store[MAX_NAME_LEN]; | ||||||
|     char file_extension[MAX_EXT_LEN + 1]; |     char file_extension[MAX_EXT_LEN + 1]; | ||||||
| }; | }; | ||||||
|  | |||||||
| @ -77,6 +77,7 @@ void archive_set_item_count(ArchiveBrowserView* browser, uint32_t count) { | |||||||
|     with_view_model( |     with_view_model( | ||||||
|         browser->view, (ArchiveBrowserViewModel * model) { |         browser->view, (ArchiveBrowserViewModel * model) { | ||||||
|             model->item_cnt = count; |             model->item_cnt = count; | ||||||
|  |             model->item_idx = CLAMP(model->item_idx, model->item_cnt - 1, 0); | ||||||
|             return false; |             return false; | ||||||
|         }); |         }); | ||||||
| } | } | ||||||
| @ -397,6 +398,7 @@ void archive_enter_dir(ArchiveBrowserView* browser, string_t name) { | |||||||
| 
 | 
 | ||||||
|     archive_dir_count_items(browser, string_get_cstr(name)); |     archive_dir_count_items(browser, string_get_cstr(name)); | ||||||
| 
 | 
 | ||||||
|  |     if(string_cmp(browser->path, name) != 0) { | ||||||
|         with_view_model( |         with_view_model( | ||||||
|             browser->view, (ArchiveBrowserViewModel * model) { |             browser->view, (ArchiveBrowserViewModel * model) { | ||||||
|                 idx_last_array_push_back(model->idx_last, model->item_idx); |                 idx_last_array_push_back(model->idx_last, model->item_idx); | ||||||
| @ -406,6 +408,7 @@ void archive_enter_dir(ArchiveBrowserView* browser, string_t name) { | |||||||
|             }); |             }); | ||||||
| 
 | 
 | ||||||
|         string_set(browser->path, name); |         string_set(browser->path, name); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     archive_switch_dir(browser, string_get_cstr(browser->path)); |     archive_switch_dir(browser, string_get_cstr(browser->path)); | ||||||
| } | } | ||||||
|  | |||||||
| @ -18,6 +18,18 @@ static const char* flipper_app_name[] = { | |||||||
|     [ArchiveFileTypeUpdateManifest] = "UpdaterApp", |     [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) { | static void archive_run_in_app(ArchiveBrowserView* browser, ArchiveFile_t* selected) { | ||||||
|     Loader* loader = furi_record_open("loader"); |     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_browser_set_callback(browser, archive_scene_browser_callback, archive); | ||||||
|     archive_update_focus(browser, archive->text_store); |     archive_update_focus(browser, archive->text_store); | ||||||
|     view_dispatcher_switch_to_view(archive->view_dispatcher, ArchiveViewBrowser); |     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) { | 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); |             archive_file_array_load(archive->browser, 1); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|             break; |             break; | ||||||
|  |         case ArchiveBrowserEventLoaderAppExit: | ||||||
|  |             if(!favorites) { | ||||||
|  |                 archive_enter_dir(browser, browser->path); | ||||||
|  |             } else { | ||||||
|  |                 archive_favorites_read(browser); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             consumed = true; | ||||||
|  |             break; | ||||||
| 
 | 
 | ||||||
|         case ArchiveBrowserEventExit: |         case ArchiveBrowserEventExit: | ||||||
|             if(archive_get_depth(browser)) { |             if(archive_get_depth(browser)) { | ||||||
|                 archive_leave_dir(browser); |                 archive_leave_dir(browser); | ||||||
|             } else { |             } 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); |                 view_dispatcher_stop(archive->view_dispatcher); | ||||||
|             } |             } | ||||||
|             consumed = true; |             consumed = true; | ||||||
| @ -165,5 +196,9 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void archive_scene_browser_on_exit(void* context) { | 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"); | ||||||
| } | } | ||||||
|  | |||||||
| @ -48,6 +48,8 @@ typedef enum { | |||||||
|     ArchiveBrowserEventLoadPrevItems, |     ArchiveBrowserEventLoadPrevItems, | ||||||
|     ArchiveBrowserEventLoadNextItems, |     ArchiveBrowserEventLoadNextItems, | ||||||
| 
 | 
 | ||||||
|  |     ArchiveBrowserEventLoaderAppExit, | ||||||
|  | 
 | ||||||
|     ArchiveBrowserEventExit, |     ArchiveBrowserEventExit, | ||||||
| } ArchiveBrowserEvent; | } ArchiveBrowserEvent; | ||||||
| 
 | 
 | ||||||
| @ -72,7 +74,6 @@ struct ArchiveBrowserView { | |||||||
|     View* view; |     View* view; | ||||||
|     ArchiveBrowserViewCallback callback; |     ArchiveBrowserViewCallback callback; | ||||||
|     void* context; |     void* context; | ||||||
| 
 |  | ||||||
|     string_t path; |     string_t path; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -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) { | static void bt_cli_scan_callback(GapAddress address, void* context) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     osMessageQueueId_t queue = 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) { | static void bt_cli_command_scan(Cli* cli, string_t args, void* context) { | ||||||
|  | |||||||
| @ -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) { | static void bt_pin_code_show(Bt* bt, uint32_t pin_code) { | ||||||
|     bt->pin_code = 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); |     gui_view_port_send_to_front(bt->gui, bt->pin_code_view_port); | ||||||
|     view_port_enabled_set(bt->pin_code_view_port, true); |     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) { | static bool bt_pin_code_verify_event_handler(Bt* bt, uint32_t pin) { | ||||||
|     furi_assert(bt); |     furi_assert(bt); | ||||||
|     notification_message(bt->notification, &sequence_display_on); |     notification_message(bt->notification, &sequence_display_backlight_on); | ||||||
|     string_t pin_str; |     string_t pin_str; | ||||||
|     dialog_message_set_icon(bt->dialog_message, &I_BLE_Pairing_128x64, 0, 0); |     dialog_message_set_icon(bt->dialog_message, &I_BLE_Pairing_128x64, 0, 0); | ||||||
|     string_init_printf(pin_str, "Verify code\n%06d", pin); |     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() { | int32_t bt_srv() { | ||||||
|     Bt* bt = bt_alloc(); |     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
 |     // Read keys
 | ||||||
|     if(!bt_keys_storage_load(bt)) { |     if(!bt_keys_storage_load(bt)) { | ||||||
|         FURI_LOG_W(TAG, "Failed to load bonding keys"); |         FURI_LOG_W(TAG, "Failed to load bonding keys"); | ||||||
|  | |||||||
| @ -1,9 +1,11 @@ | |||||||
| #include "cli_i.h" | #include "cli_i.h" | ||||||
| #include "cli_commands.h" | #include "cli_commands.h" | ||||||
| 
 | #include "cli_vcp.h" | ||||||
| #include <furi_hal_version.h> | #include <furi_hal_version.h> | ||||||
| #include <loader/loader.h> | #include <loader/loader.h> | ||||||
| 
 | 
 | ||||||
|  | #define TAG "CliSrv" | ||||||
|  | 
 | ||||||
| Cli* cli_alloc() { | Cli* cli_alloc() { | ||||||
|     Cli* cli = malloc(sizeof(Cli)); |     Cli* cli = malloc(sizeof(Cli)); | ||||||
| 
 | 
 | ||||||
| @ -12,55 +14,78 @@ Cli* cli_alloc() { | |||||||
|     string_init(cli->last_line); |     string_init(cli->last_line); | ||||||
|     string_init(cli->line); |     string_init(cli->line); | ||||||
| 
 | 
 | ||||||
|  |     cli->session = NULL; | ||||||
|  | 
 | ||||||
|     cli->mutex = osMutexNew(NULL); |     cli->mutex = osMutexNew(NULL); | ||||||
|     furi_check(cli->mutex); |     furi_check(cli->mutex); | ||||||
| 
 | 
 | ||||||
|  |     cli->idle_sem = osSemaphoreNew(1, 0, NULL); | ||||||
|  | 
 | ||||||
|     return cli; |     return cli; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void cli_free(Cli* cli) { | void cli_putc(Cli* cli, char c) { | ||||||
|     furi_assert(cli); |     furi_assert(cli); | ||||||
| 
 |     if(cli->session != NULL) { | ||||||
|     string_clear(cli->last_line); |         cli->session->tx((uint8_t*)&c, 1); | ||||||
|     string_clear(cli->line); |  | ||||||
| 
 |  | ||||||
|     CliCommandTree_clear(cli->commands); |  | ||||||
| 
 |  | ||||||
|     free(cli); |  | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
| void cli_putc(char c) { |  | ||||||
|     furi_hal_vcp_tx((uint8_t*)&c, 1); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| char cli_getc(Cli* cli) { | char cli_getc(Cli* cli) { | ||||||
|     furi_assert(cli); |     furi_assert(cli); | ||||||
|     char c; |     char c = 0; | ||||||
|     if(furi_hal_vcp_rx((uint8_t*)&c, 1) == 0) { |     if(cli->session != NULL) { | ||||||
|  |         if(cli->session->rx((uint8_t*)&c, 1, osWaitForever) == 0) { | ||||||
|  |             cli_reset(cli); | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|         cli_reset(cli); |         cli_reset(cli); | ||||||
|     } |     } | ||||||
|     return c; |     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) { | 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) { | 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) { | bool cli_cmd_interrupt_received(Cli* cli) { | ||||||
|  |     furi_assert(cli); | ||||||
|     char c = '\0'; |     char c = '\0'; | ||||||
|     if(furi_hal_vcp_rx_with_timeout((uint8_t*)&c, 1, 0) == 1) { |     if(cli->session != NULL) { | ||||||
|  |         if(cli->session->rx((uint8_t*)&c, 1, 0) == 1) { | ||||||
|             return c == CliSymbolAsciiETX; |             return c == CliSymbolAsciiETX; | ||||||
|     } else { |         } | ||||||
|  |     } | ||||||
|     return false; |     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) { | 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--; |         cli->cursor_position--; | ||||||
|     } else { |     } else { | ||||||
|         cli_putc(CliSymbolAsciiBell); |         cli_putc(cli, CliSymbolAsciiBell); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -210,7 +235,7 @@ static void cli_handle_enter(Cli* cli) { | |||||||
|         printf( |         printf( | ||||||
|             "`%s` command not found, use `help` or `?` to list all available commands", |             "`%s` command not found, use `help` or `?` to list all available commands", | ||||||
|             string_get_cstr(command)); |             string_get_cstr(command)); | ||||||
|         cli_putc(CliSymbolAsciiBell); |         cli_putc(cli, CliSymbolAsciiBell); | ||||||
|     } |     } | ||||||
|     furi_check(osMutexRelease(cli->mutex) == osOK); |     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) { | void cli_process_input(Cli* cli) { | ||||||
|     char c = cli_getc(cli); |     char in_chr = cli_getc(cli); | ||||||
|     size_t r; |     size_t rx_len; | ||||||
| 
 | 
 | ||||||
|     if(c == CliSymbolAsciiTab) { |     if(in_chr == CliSymbolAsciiTab) { | ||||||
|         cli_handle_autocomplete(cli); |         cli_handle_autocomplete(cli); | ||||||
|     } else if(c == CliSymbolAsciiSOH) { |     } else if(in_chr == CliSymbolAsciiSOH) { | ||||||
|         osDelay(33); // We are too fast, Minicom is not ready yet
 |         osDelay(33); // We are too fast, Minicom is not ready yet
 | ||||||
|         cli_motd(); |         cli_motd(); | ||||||
|         cli_prompt(cli); |         cli_prompt(cli); | ||||||
|     } else if(c == CliSymbolAsciiETX) { |     } else if(in_chr == CliSymbolAsciiETX) { | ||||||
|         cli_reset(cli); |         cli_reset(cli); | ||||||
|         cli_prompt(cli); |         cli_prompt(cli); | ||||||
|     } else if(c == CliSymbolAsciiEOT) { |     } else if(in_chr == CliSymbolAsciiEOT) { | ||||||
|         cli_reset(cli); |         cli_reset(cli); | ||||||
|     } else if(c == CliSymbolAsciiEsc) { |     } else if(in_chr == CliSymbolAsciiEsc) { | ||||||
|         r = furi_hal_vcp_rx((uint8_t*)&c, 1); |         rx_len = cli_read(cli, (uint8_t*)&in_chr, 1); | ||||||
|         if(r && c == '[') { |         if((rx_len > 0) && (in_chr == '[')) { | ||||||
|             furi_hal_vcp_rx((uint8_t*)&c, 1); |             cli_read(cli, (uint8_t*)&in_chr, 1); | ||||||
|             cli_handle_escape(cli, c); |             cli_handle_escape(cli, in_chr); | ||||||
|         } else { |         } 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); |         cli_handle_backspace(cli); | ||||||
|     } else if(c == CliSymbolAsciiCR) { |     } else if(in_chr == CliSymbolAsciiCR) { | ||||||
|         cli_handle_enter(cli); |         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)) { |         if(cli->cursor_position == string_size(cli->line)) { | ||||||
|             string_push_back(cli->line, c); |             string_push_back(cli->line, in_chr); | ||||||
|             cli_putc(c); |             cli_putc(cli, in_chr); | ||||||
|         } else { |         } else { | ||||||
|             // ToDo: better way?
 |             // ToDo: better way?
 | ||||||
|             string_t temp; |             string_t temp; | ||||||
|             string_init(temp); |             string_init(temp); | ||||||
|             string_reserve(temp, string_size(cli->line) + 1); |             string_reserve(temp, string_size(cli->line) + 1); | ||||||
|             string_set_strn(temp, string_get_cstr(cli->line), cli->cursor_position); |             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); |             string_cat_str(temp, string_get_cstr(cli->line) + cli->cursor_position); | ||||||
| 
 | 
 | ||||||
|             // cli->line is cleared and temp's buffer moved to cli->line
 |             // 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
 |             // NO MEMORY LEAK, STOP REPORTING IT
 | ||||||
| 
 | 
 | ||||||
|             // Print character in replace mode
 |             // Print character in replace mode
 | ||||||
|             printf("\e[4h%c\e[4l", c); |             printf("\e[4h%c\e[4l", in_chr); | ||||||
|             fflush(stdout); |             fflush(stdout); | ||||||
|         } |         } | ||||||
|         cli->cursor_position++; |         cli->cursor_position++; | ||||||
|     } else { |     } 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); |     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) { | int32_t cli_srv(void* p) { | ||||||
|     Cli* cli = cli_alloc(); |     Cli* cli = cli_alloc(); | ||||||
| 
 | 
 | ||||||
|     furi_hal_vcp_init(); |  | ||||||
| 
 |  | ||||||
|     // Init basic cli commands
 |     // Init basic cli commands
 | ||||||
|     cli_commands_init(cli); |     cli_commands_init(cli); | ||||||
| 
 | 
 | ||||||
|     furi_record_create("cli", 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) { |     while(1) { | ||||||
|  |         if(cli->session != NULL) { | ||||||
|             cli_process_input(cli); |             cli_process_input(cli); | ||||||
|  |         } else { | ||||||
|  |             furi_check(osSemaphoreAcquire(cli->idle_sem, osWaitForever) == osOK); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return 0; |     return 0; | ||||||
|  | |||||||
| @ -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); | 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      cli     Cli instance | ||||||
|  * @param      buffer  pointer to buffer |  * @param      buffer  pointer to buffer | ||||||
|  * @param      size    size of buffer in bytes |  * @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); | 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 |  * @param      cli   Cli instance | ||||||
|  * |  * | ||||||
| @ -111,6 +122,12 @@ char cli_getc(Cli* cli); | |||||||
|  */ |  */ | ||||||
| void cli_nl(); | 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 | #ifdef __cplusplus | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  | |||||||
| @ -202,7 +202,7 @@ void cli_command_led(Cli* cli, string_t args, void* context) { | |||||||
|     } else if(!string_cmp(light_name, "b")) { |     } else if(!string_cmp(light_name, "b")) { | ||||||
|         notification_led_message.type = NotificationMessageTypeLedBlue; |         notification_led_message.type = NotificationMessageTypeLedBlue; | ||||||
|     } else if(!string_cmp(light_name, "bl")) { |     } else if(!string_cmp(light_name, "bl")) { | ||||||
|         notification_led_message.type = NotificationMessageTypeLedDisplay; |         notification_led_message.type = NotificationMessageTypeLedDisplayBacklight; | ||||||
|     } else { |     } else { | ||||||
|         cli_print_usage("led", "<r|g|b|bl> <0-255>", string_get_cstr(args)); |         cli_print_usage("led", "<r|g|b|bl> <0-255>", string_get_cstr(args)); | ||||||
|         string_clear(light_name); |         string_clear(light_name); | ||||||
|  | |||||||
| @ -18,6 +18,17 @@ typedef struct { | |||||||
|     uint32_t flags; |     uint32_t flags; | ||||||
| } CliCommand; | } 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( | BPTREE_DEF2( | ||||||
|     CliCommandTree, |     CliCommandTree, | ||||||
|     CLI_COMMANDS_TREE_RANK, |     CLI_COMMANDS_TREE_RANK, | ||||||
| @ -31,18 +42,18 @@ BPTREE_DEF2( | |||||||
| struct Cli { | struct Cli { | ||||||
|     CliCommandTree_t commands; |     CliCommandTree_t commands; | ||||||
|     osMutexId_t mutex; |     osMutexId_t mutex; | ||||||
|  |     osSemaphoreId_t idle_sem; | ||||||
|     string_t last_line; |     string_t last_line; | ||||||
|     string_t line; |     string_t line; | ||||||
|  |     CliSession* session; | ||||||
| 
 | 
 | ||||||
|     size_t cursor_position; |     size_t cursor_position; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| Cli* cli_alloc(); | Cli* cli_alloc(); | ||||||
| 
 | 
 | ||||||
| void cli_free(Cli* cli); |  | ||||||
| 
 |  | ||||||
| void cli_reset(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); | void cli_stdout_callback(void* _cookie, const char* data, size_t size); | ||||||
|  | |||||||
| @ -2,8 +2,9 @@ | |||||||
| #include <furi_hal.h> | #include <furi_hal.h> | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
| #include <stream_buffer.h> | #include <stream_buffer.h> | ||||||
|  | #include "cli_i.h" | ||||||
| 
 | 
 | ||||||
| #define TAG "FuriHalVcp" | #define TAG "CliVcp" | ||||||
| 
 | 
 | ||||||
| #define USB_CDC_PKT_LEN CDC_DATA_SZ | #define USB_CDC_PKT_LEN CDC_DATA_SZ | ||||||
| #define VCP_RX_BUF_SIZE (USB_CDC_PKT_LEN * 3) | #define VCP_RX_BUF_SIZE (USB_CDC_PKT_LEN * 3) | ||||||
| @ -12,19 +13,18 @@ | |||||||
| #define VCP_IF_NUM 0 | #define VCP_IF_NUM 0 | ||||||
| 
 | 
 | ||||||
| typedef enum { | typedef enum { | ||||||
|     VcpEvtEnable = (1 << 0), |     VcpEvtStop = (1 << 0), | ||||||
|     VcpEvtDisable = (1 << 1), |     VcpEvtConnect = (1 << 1), | ||||||
|     VcpEvtConnect = (1 << 2), |     VcpEvtDisconnect = (1 << 2), | ||||||
|     VcpEvtDisconnect = (1 << 3), |     VcpEvtStreamRx = (1 << 3), | ||||||
|     VcpEvtStreamRx = (1 << 4), |     VcpEvtRx = (1 << 4), | ||||||
|     VcpEvtRx = (1 << 5), |     VcpEvtStreamTx = (1 << 5), | ||||||
|     VcpEvtStreamTx = (1 << 6), |     VcpEvtTx = (1 << 6), | ||||||
|     VcpEvtTx = (1 << 7), |  | ||||||
| } WorkerEvtFlags; | } WorkerEvtFlags; | ||||||
| 
 | 
 | ||||||
| #define VCP_THREAD_FLAG_ALL                                                                 \ | #define VCP_THREAD_FLAG_ALL                                                                 \ | ||||||
|     (VcpEvtEnable | VcpEvtDisable | VcpEvtConnect | VcpEvtDisconnect | VcpEvtRx | VcpEvtTx | \ |     (VcpEvtStop | VcpEvtConnect | VcpEvtDisconnect | VcpEvtRx | VcpEvtTx | VcpEvtStreamRx | \ | ||||||
|      VcpEvtStreamRx | VcpEvtStreamTx) |      VcpEvtStreamTx) | ||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
|     FuriThread* thread; |     FuriThread* thread; | ||||||
| @ -33,9 +33,12 @@ typedef struct { | |||||||
|     StreamBufferHandle_t rx_stream; |     StreamBufferHandle_t rx_stream; | ||||||
| 
 | 
 | ||||||
|     volatile bool connected; |     volatile bool connected; | ||||||
|  |     volatile bool running; | ||||||
|  | 
 | ||||||
|  |     FuriHalUsbInterface* usb_if_prev; | ||||||
| 
 | 
 | ||||||
|     uint8_t data_buffer[USB_CDC_PKT_LEN]; |     uint8_t data_buffer[USB_CDC_PKT_LEN]; | ||||||
| } FuriHalVcp; | } CliVcp; | ||||||
| 
 | 
 | ||||||
| static int32_t vcp_worker(void* context); | static int32_t vcp_worker(void* context); | ||||||
| static void vcp_on_cdc_tx_complete(void* context); | static void vcp_on_cdc_tx_complete(void* context); | ||||||
| @ -51,25 +54,23 @@ static CdcCallbacks cdc_cb = { | |||||||
|     NULL, |     NULL, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static FuriHalVcp* vcp = NULL; | static CliVcp* vcp = NULL; | ||||||
| 
 | 
 | ||||||
| static const uint8_t ascii_soh = 0x01; | static const uint8_t ascii_soh = 0x01; | ||||||
| static const uint8_t ascii_eot = 0x04; | static const uint8_t ascii_eot = 0x04; | ||||||
| 
 | 
 | ||||||
| void furi_hal_vcp_init() { | static void cli_vcp_init() { | ||||||
|     vcp = malloc(sizeof(FuriHalVcp)); |     if(vcp == NULL) { | ||||||
|     vcp->connected = false; |         vcp = malloc(sizeof(CliVcp)); | ||||||
| 
 |  | ||||||
|         vcp->tx_stream = xStreamBufferCreate(VCP_TX_BUF_SIZE, 1); |         vcp->tx_stream = xStreamBufferCreate(VCP_TX_BUF_SIZE, 1); | ||||||
|         vcp->rx_stream = xStreamBufferCreate(VCP_RX_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; |  | ||||||
|     } |     } | ||||||
|  |     furi_assert(vcp->thread == NULL); | ||||||
|  | 
 | ||||||
|  |     vcp->connected = false; | ||||||
| 
 | 
 | ||||||
|     vcp->thread = furi_thread_alloc(); |     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_stack_size(vcp->thread, 1024); | ||||||
|     furi_thread_set_callback(vcp->thread, vcp_worker); |     furi_thread_set_callback(vcp->thread, vcp_worker); | ||||||
|     furi_thread_start(vcp->thread); |     furi_thread_start(vcp->thread); | ||||||
| @ -77,48 +78,35 @@ void furi_hal_vcp_init() { | |||||||
|     FURI_LOG_I(TAG, "Init OK"); |     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) { | static int32_t vcp_worker(void* context) { | ||||||
|     bool enabled = true; |     bool tx_idle = true; | ||||||
|     bool tx_idle = false; |  | ||||||
|     size_t missed_rx = 0; |     size_t missed_rx = 0; | ||||||
|     uint8_t last_tx_pkt_len = 0; |     uint8_t last_tx_pkt_len = 0; | ||||||
| 
 | 
 | ||||||
|  |     // 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_usb_set_config(&usb_cdc_single, NULL); | ||||||
|  |     } | ||||||
|     furi_hal_cdc_set_callbacks(VCP_IF_NUM, &cdc_cb, NULL); |     furi_hal_cdc_set_callbacks(VCP_IF_NUM, &cdc_cb, NULL); | ||||||
| 
 | 
 | ||||||
|  |     FURI_LOG_D(TAG, "Start"); | ||||||
|  |     vcp->running = true; | ||||||
|  | 
 | ||||||
|     while(1) { |     while(1) { | ||||||
|         uint32_t flags = osThreadFlagsWait(VCP_THREAD_FLAG_ALL, osFlagsWaitAny, osWaitForever); |         uint32_t flags = osThreadFlagsWait(VCP_THREAD_FLAG_ALL, osFlagsWaitAny, osWaitForever); | ||||||
|         furi_assert((flags & osFlagsError) == 0); |         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
 |         // VCP session opened
 | ||||||
|         if((flags & VcpEvtConnect) && enabled) { |         if(flags & VcpEvtConnect) { | ||||||
| #ifdef FURI_HAL_USB_VCP_DEBUG | #ifdef CLI_VCP_DEBUG | ||||||
|             FURI_LOG_D(TAG, "Connect"); |             FURI_LOG_D(TAG, "Connect"); | ||||||
| #endif | #endif | ||||||
|             if(vcp->connected == false) { |             if(vcp->connected == false) { | ||||||
| @ -128,8 +116,8 @@ static int32_t vcp_worker(void* context) { | |||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         // VCP session closed
 |         // VCP session closed
 | ||||||
|         if((flags & VcpEvtDisconnect) && enabled) { |         if(flags & VcpEvtDisconnect) { | ||||||
| #ifdef FURI_HAL_USB_VCP_DEBUG | #ifdef CLI_VCP_DEBUG | ||||||
|             FURI_LOG_D(TAG, "Disconnect"); |             FURI_LOG_D(TAG, "Disconnect"); | ||||||
| #endif | #endif | ||||||
|             if(vcp->connected == true) { |             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?
 |         // Rx buffer was read, maybe there is enough space for new data?
 | ||||||
|         if((flags & VcpEvtStreamRx) && enabled && missed_rx > 0) { |         if((flags & VcpEvtStreamRx) && (missed_rx > 0)) { | ||||||
| #ifdef FURI_HAL_USB_VCP_DEBUG | #ifdef CLI_VCP_DEBUG | ||||||
|             FURI_LOG_D(TAG, "StreamRx"); |             FURI_LOG_D(TAG, "StreamRx"); | ||||||
| #endif | #endif | ||||||
|             if(xStreamBufferSpacesAvailable(vcp->rx_stream) >= USB_CDC_PKT_LEN) { |             if(xStreamBufferSpacesAvailable(vcp->rx_stream) >= USB_CDC_PKT_LEN) { | ||||||
| @ -151,10 +139,10 @@ static int32_t vcp_worker(void* context) { | |||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         // New data received
 |         // New data received
 | ||||||
|         if((flags & VcpEvtRx)) { |         if(flags & VcpEvtRx) { | ||||||
|             if(xStreamBufferSpacesAvailable(vcp->rx_stream) >= USB_CDC_PKT_LEN) { |             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); |                 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); |                 FURI_LOG_D(TAG, "Rx %d", len); | ||||||
| #endif | #endif | ||||||
|                 if(len > 0) { |                 if(len > 0) { | ||||||
| @ -163,7 +151,7 @@ static int32_t vcp_worker(void* context) { | |||||||
|                         len); |                         len); | ||||||
|                 } |                 } | ||||||
|             } else { |             } else { | ||||||
| #ifdef FURI_HAL_USB_VCP_DEBUG | #ifdef CLI_VCP_DEBUG | ||||||
|                 FURI_LOG_D(TAG, "Rx missed"); |                 FURI_LOG_D(TAG, "Rx missed"); | ||||||
| #endif | #endif | ||||||
|                 missed_rx++; |                 missed_rx++; | ||||||
| @ -171,8 +159,8 @@ static int32_t vcp_worker(void* context) { | |||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         // New data in Tx buffer
 |         // New data in Tx buffer
 | ||||||
|         if((flags & VcpEvtStreamTx) && enabled) { |         if(flags & VcpEvtStreamTx) { | ||||||
| #ifdef FURI_HAL_USB_VCP_DEBUG | #ifdef CLI_VCP_DEBUG | ||||||
|             FURI_LOG_D(TAG, "StreamTx"); |             FURI_LOG_D(TAG, "StreamTx"); | ||||||
| #endif | #endif | ||||||
|             if(tx_idle) { |             if(tx_idle) { | ||||||
| @ -181,10 +169,10 @@ static int32_t vcp_worker(void* context) { | |||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         // CDC write transfer done
 |         // CDC write transfer done
 | ||||||
|         if((flags & VcpEvtTx) && enabled) { |         if(flags & VcpEvtTx) { | ||||||
|             size_t len = |             size_t len = | ||||||
|                 xStreamBufferReceive(vcp->tx_stream, vcp->data_buffer, USB_CDC_PKT_LEN, 0); |                 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); |             FURI_LOG_D(TAG, "Tx %d", len); | ||||||
| #endif | #endif | ||||||
|             if(len > 0) { // Some data left in Tx buffer. Sending it now
 |             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; |                 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; |     return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void furi_hal_vcp_enable() { | static size_t cli_vcp_rx(uint8_t* buffer, size_t size, uint32_t timeout) { | ||||||
|     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) { |  | ||||||
|     furi_assert(vcp); |     furi_assert(vcp); | ||||||
|     furi_assert(buffer); |     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); |     FURI_LOG_D(TAG, "rx %u start", size); | ||||||
| #endif | #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; |         if(batch_size > VCP_RX_BUF_SIZE) batch_size = VCP_RX_BUF_SIZE; | ||||||
| 
 | 
 | ||||||
|         size_t len = xStreamBufferReceive(vcp->rx_stream, buffer, batch_size, timeout); |         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); |         FURI_LOG_D(TAG, "rx %u ", batch_size); | ||||||
| #endif | #endif | ||||||
|         if(len == 0) break; |         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; |         rx_cnt += len; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| #ifdef FURI_HAL_USB_VCP_DEBUG | #ifdef CLI_VCP_DEBUG | ||||||
|     FURI_LOG_D(TAG, "rx %u end", size); |     FURI_LOG_D(TAG, "rx %u end", size); | ||||||
| #endif | #endif | ||||||
|     return rx_cnt; |     return rx_cnt; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| size_t furi_hal_vcp_rx(uint8_t* buffer, size_t size) { | static void cli_vcp_tx(const 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) { |  | ||||||
|     furi_assert(vcp); |     furi_assert(vcp); | ||||||
|     furi_assert(buffer); |     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); |     FURI_LOG_D(TAG, "tx %u start", size); | ||||||
| #endif | #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); |         xStreamBufferSend(vcp->tx_stream, buffer, batch_size, osWaitForever); | ||||||
|         osThreadFlagsSet(furi_thread_get_thread_id(vcp->thread), VcpEvtStreamTx); |         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); |         FURI_LOG_D(TAG, "tx %u", batch_size); | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| @ -272,11 +269,15 @@ void furi_hal_vcp_tx(const uint8_t* buffer, size_t size) { | |||||||
|         buffer += batch_size; |         buffer += batch_size; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| #ifdef FURI_HAL_USB_VCP_DEBUG | #ifdef CLI_VCP_DEBUG | ||||||
|     FURI_LOG_D(TAG, "tx %u end", size); |     FURI_LOG_D(TAG, "tx %u end", size); | ||||||
| #endif | #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) { | static void vcp_state_callback(void* context, uint8_t state) { | ||||||
|     if(state == 0) { |     if(state == 0) { | ||||||
|         osThreadFlagsSet(furi_thread_get_thread_id(vcp->thread), VcpEvtDisconnect); |         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); |     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); |     furi_assert(vcp); | ||||||
|     return vcp->connected; |     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, | ||||||
|  | }; | ||||||
							
								
								
									
										18
									
								
								applications/cli/cli_vcp.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								applications/cli/cli_vcp.h
									
									
									
									
									
										Normal file
									
								
							| @ -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 | ||||||
| @ -45,7 +45,7 @@ typedef enum { | |||||||
| #define WORKER_EVENTS_MASK (WorkerEventStop | WorkerEventRx) | #define WORKER_EVENTS_MASK (WorkerEventStop | WorkerEventRx) | ||||||
| 
 | 
 | ||||||
| const NotificationSequence sequence_notification = { | const NotificationSequence sequence_notification = { | ||||||
|     &message_display_on, |     &message_display_backlight_on, | ||||||
|     &message_green_255, |     &message_green_255, | ||||||
|     &message_delay_10, |     &message_delay_10, | ||||||
|     NULL, |     NULL, | ||||||
|  | |||||||
| @ -14,7 +14,7 @@ | |||||||
| #include "desktop/views/desktop_view_pin_input.h" | #include "desktop/views/desktop_view_pin_input.h" | ||||||
| #include "desktop/views/desktop_view_pin_timeout.h" | #include "desktop/views/desktop_view_pin_timeout.h" | ||||||
| #include "desktop_i.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_arm(Desktop*); | ||||||
| static void desktop_auto_lock_inhibit(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) { | void desktop_lock(Desktop* desktop) { | ||||||
|     furi_hal_rtc_set_pin_fails(0); |  | ||||||
|     desktop_auto_lock_inhibit(desktop); |     desktop_auto_lock_inhibit(desktop); | ||||||
|     scene_manager_set_scene_state( |     scene_manager_set_scene_state( | ||||||
|         desktop->scene_manager, DesktopSceneLocked, SCENE_LOCKED_FIRST_ENTER); |         desktop->scene_manager, DesktopSceneLocked, SCENE_LOCKED_FIRST_ENTER); | ||||||
|     scene_manager_next_scene(desktop->scene_manager, DesktopSceneLocked); |     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) { | void desktop_unlock(Desktop* desktop) { | ||||||
|     furi_hal_rtc_set_pin_fails(0); |     view_port_enabled_set(desktop->lock_viewport, false); | ||||||
|     desktop_helpers_unlock_system(desktop); |     Gui* gui = furi_record_open("gui"); | ||||||
|  |     gui_set_lockdown(gui, false); | ||||||
|  |     furi_record_close("gui"); | ||||||
|     desktop_view_locked_unlock(desktop->locked_view); |     desktop_view_locked_unlock(desktop->locked_view); | ||||||
|     scene_manager_search_and_switch_to_previous_scene(desktop->scene_manager, DesktopSceneMain); |     scene_manager_search_and_switch_to_previous_scene(desktop->scene_manager, DesktopSceneMain); | ||||||
|     desktop_auto_lock_arm(desktop); |     desktop_auto_lock_arm(desktop); | ||||||
| @ -301,18 +302,15 @@ int32_t desktop_srv(void* p) { | |||||||
| 
 | 
 | ||||||
|     bool loaded = LOAD_DESKTOP_SETTINGS(&desktop->settings); |     bool loaded = LOAD_DESKTOP_SETTINGS(&desktop->settings); | ||||||
|     if(!loaded) { |     if(!loaded) { | ||||||
|         furi_hal_rtc_reset_flag(FuriHalRtcFlagLock); |  | ||||||
|         memset(&desktop->settings, 0, sizeof(desktop->settings)); |         memset(&desktop->settings, 0, sizeof(desktop->settings)); | ||||||
|         SAVE_DESKTOP_SETTINGS(&desktop->settings); |         SAVE_DESKTOP_SETTINGS(&desktop->settings); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain); |     scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain); | ||||||
| 
 | 
 | ||||||
|     if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagLock) && !desktop->settings.pin_code.length) { |     desktop_pin_lock_init(&desktop->settings); | ||||||
|         furi_hal_rtc_reset_flag(FuriHalRtcFlagLock); |  | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     if(!furi_hal_rtc_is_flag_set(FuriHalRtcFlagLock)) { |     if(!desktop_pin_lock_is_locked()) { | ||||||
|         if(!loader_is_locked(desktop->loader)) { |         if(!loader_is_locked(desktop->loader)) { | ||||||
|             desktop_auto_lock_arm(desktop); |             desktop_auto_lock_arm(desktop); | ||||||
|         } |         } | ||||||
|  | |||||||
| @ -1,82 +0,0 @@ | |||||||
| #include <notification/notification.h> |  | ||||||
| #include <notification/notification_messages.h> |  | ||||||
| #include <stddef.h> |  | ||||||
| #include <furi.h> |  | ||||||
| #include <furi_hal.h> |  | ||||||
| #include <gui/gui.h> |  | ||||||
| 
 |  | ||||||
| #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; |  | ||||||
| } |  | ||||||
| @ -1,9 +0,0 @@ | |||||||
| #pragma once |  | ||||||
| #include <stdbool.h> |  | ||||||
| #include <stdint.h> |  | ||||||
| #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); |  | ||||||
| @ -5,7 +5,7 @@ | |||||||
| #include <stdbool.h> | #include <stdbool.h> | ||||||
| #include <toolbox/saved_struct.h> | #include <toolbox/saved_struct.h> | ||||||
| 
 | 
 | ||||||
| #define DESKTOP_SETTINGS_VER (2) | #define DESKTOP_SETTINGS_VER (3) | ||||||
| #define DESKTOP_SETTINGS_PATH "/int/desktop.settings" | #define DESKTOP_SETTINGS_PATH "/int/desktop.settings" | ||||||
| #define DESKTOP_SETTINGS_MAGIC (0x17) | #define DESKTOP_SETTINGS_MAGIC (0x17) | ||||||
| #define PIN_MAX_LENGTH 12 | #define PIN_MAX_LENGTH 12 | ||||||
| @ -39,17 +39,6 @@ typedef struct { | |||||||
| typedef struct { | typedef struct { | ||||||
|     uint16_t favorite; |     uint16_t favorite; | ||||||
|     PinCode pin_code; |     PinCode pin_code; | ||||||
|  |     uint8_t is_locked; | ||||||
|     uint32_t auto_lock_delay_ms; |     uint32_t auto_lock_delay_ms; | ||||||
| } DesktopSettings; | } 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; |  | ||||||
| } |  | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| #include <stdint.h> | #include <stdint.h> | ||||||
| #include <furi/check.h> | #include <furi/check.h> | ||||||
| #include <gui/scene_manager.h> | #include <gui/scene_manager.h> | ||||||
| 
 | #include "../../helpers/pin_lock.h" | ||||||
| #include "../desktop_settings_app.h" | #include "../desktop_settings_app.h" | ||||||
| #include "desktop/desktop_settings/desktop_settings.h" | #include "desktop/desktop_settings/desktop_settings.h" | ||||||
| #include "desktop/views/desktop_view_pin_input.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; |     DesktopSettingsApp* app = context; | ||||||
| 
 | 
 | ||||||
|     app->pincode_buffer = *pin_code; |     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); |         view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_PINS_EQUAL); | ||||||
|     } else { |     } else { | ||||||
|         view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_PINS_DIFFERENT); |         view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_PINS_DIFFERENT); | ||||||
|  | |||||||
| @ -6,7 +6,7 @@ | |||||||
| #include "desktop/views/desktop_view_pin_input.h" | #include "desktop/views/desktop_view_pin_input.h" | ||||||
| #include "desktop_settings_scene.h" | #include "desktop_settings_scene.h" | ||||||
| #include "desktop_settings_scene_i.h" | #include "desktop_settings_scene_i.h" | ||||||
| #include "../../desktop_helpers.h" | #include "../../helpers/pin_lock.h" | ||||||
| #include "../desktop_settings_app.h" | #include "../desktop_settings_app.h" | ||||||
| 
 | 
 | ||||||
| #define SCENE_EVENT_EXIT (0U) | #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) { | void desktop_settings_scene_pin_error_on_enter(void* context) { | ||||||
|     DesktopSettingsApp* app = 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_context(app->pin_input_view, app); | ||||||
|     desktop_view_pin_input_set_back_callback(app->pin_input_view, pin_error_back_callback); |     desktop_view_pin_input_set_back_callback(app->pin_input_view, pin_error_back_callback); | ||||||
|  | |||||||
| @ -7,6 +7,7 @@ | |||||||
| #include "desktop/views/desktop_view_pin_input.h" | #include "desktop/views/desktop_view_pin_input.h" | ||||||
| #include "desktop_settings_scene.h" | #include "desktop_settings_scene.h" | ||||||
| #include "desktop_settings_scene_i.h" | #include "desktop_settings_scene_i.h" | ||||||
|  | #include "../../helpers/pin_lock.h" | ||||||
| 
 | 
 | ||||||
| #define SCENE_EVENT_EXIT (0U) | #define SCENE_EVENT_EXIT (0U) | ||||||
| #define SCENE_EVENT_1ST_PIN_ENTERED (1U) | #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); |         view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_1ST_PIN_ENTERED); | ||||||
|     } else { |     } else { | ||||||
|         app->pincode_buffer_filled = false; |         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); |             view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_PINS_EQUAL); | ||||||
|         } else { |         } else { | ||||||
|             view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_PINS_DIFFERENT); |             view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_PINS_DIFFERENT); | ||||||
|  | |||||||
							
								
								
									
										138
									
								
								applications/desktop/helpers/pin_lock.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										138
									
								
								applications/desktop/helpers/pin_lock.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,138 @@ | |||||||
|  | 
 | ||||||
|  | #include <notification/notification.h> | ||||||
|  | #include <notification/notification_messages.h> | ||||||
|  | #include <stddef.h> | ||||||
|  | #include <furi.h> | ||||||
|  | #include <furi_hal.h> | ||||||
|  | #include <gui/gui.h> | ||||||
|  | 
 | ||||||
|  | #include "../helpers/pin_lock.h" | ||||||
|  | #include "../desktop_i.h" | ||||||
|  | #include <cli/cli_vcp.h> | ||||||
|  | 
 | ||||||
|  | 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; | ||||||
|  | } | ||||||
							
								
								
									
										21
									
								
								applications/desktop/helpers/pin_lock.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								applications/desktop/helpers/pin_lock.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,21 @@ | |||||||
|  | #pragma once | ||||||
|  | #include <stdbool.h> | ||||||
|  | #include <stdint.h> | ||||||
|  | #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); | ||||||
| @ -10,6 +10,7 @@ | |||||||
| #include "../views/desktop_view_lock_menu.h" | #include "../views/desktop_view_lock_menu.h" | ||||||
| #include "desktop_scene_i.h" | #include "desktop_scene_i.h" | ||||||
| #include "desktop_scene.h" | #include "desktop_scene.h" | ||||||
|  | #include "../helpers/pin_lock.h" | ||||||
| 
 | 
 | ||||||
| #define TAG "DesktopSceneLock" | #define TAG "DesktopSceneLock" | ||||||
| 
 | 
 | ||||||
| @ -53,7 +54,7 @@ bool desktop_scene_lock_menu_on_event(void* context, SceneManagerEvent event) { | |||||||
|             break; |             break; | ||||||
|         case DesktopLockMenuEventPinLock: |         case DesktopLockMenuEventPinLock: | ||||||
|             if(desktop->settings.pin_code.length > 0) { |             if(desktop->settings.pin_code.length > 0) { | ||||||
|                 furi_hal_rtc_set_flag(FuriHalRtcFlagLock); |                 desktop_pin_lock(&desktop->settings); | ||||||
|                 desktop_lock(desktop); |                 desktop_lock(desktop); | ||||||
|             } else { |             } else { | ||||||
|                 LoaderStatus status = |                 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"); |                     FURI_LOG_E(TAG, "Unable to start desktop settings"); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
| 
 |  | ||||||
|             consumed = true; |             consumed = true; | ||||||
|             break; |             break; | ||||||
|         case DesktopLockMenuEventExit: |         case DesktopLockMenuEventExit: | ||||||
|  | |||||||
| @ -7,7 +7,7 @@ | |||||||
| 
 | 
 | ||||||
| #include "../desktop.h" | #include "../desktop.h" | ||||||
| #include "../desktop_i.h" | #include "../desktop_i.h" | ||||||
| #include "../desktop_helpers.h" | #include "../helpers/pin_lock.h" | ||||||
| #include "../animations/animation_manager.h" | #include "../animations/animation_manager.h" | ||||||
| #include "../views/desktop_events.h" | #include "../views/desktop_events.h" | ||||||
| #include "../views/desktop_view_pin_input.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; |     bool switch_to_timeout_scene = false; | ||||||
|     uint32_t state = scene_manager_get_scene_state(desktop->scene_manager, DesktopSceneLocked); |     uint32_t state = scene_manager_get_scene_state(desktop->scene_manager, DesktopSceneLocked); | ||||||
|     if(state == SCENE_LOCKED_FIRST_ENTER) { |     if(state == SCENE_LOCKED_FIRST_ENTER) { | ||||||
|         bool pin_locked = furi_hal_rtc_is_flag_set(FuriHalRtcFlagLock); |         bool pin_locked = desktop_pin_lock_is_locked(); | ||||||
|         desktop_helpers_lock_system(desktop, pin_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) { |         if(pin_locked) { | ||||||
|             LOAD_DESKTOP_SETTINGS(&desktop->settings); |             LOAD_DESKTOP_SETTINGS(&desktop->settings); | ||||||
|             desktop_view_locked_lock(desktop->locked_view, true); |             desktop_view_locked_lock(desktop->locked_view, true); | ||||||
|             uint32_t pin_fails = furi_hal_rtc_get_pin_fails(); |             uint32_t pin_timeout = desktop_pin_lock_get_fail_timeout(); | ||||||
|             uint32_t pin_timeout = desktop_helpers_get_pin_fail_timeout(pin_fails); |             if(pin_timeout > 0) { | ||||||
|             if(pin_timeout) { |  | ||||||
|                 scene_manager_set_scene_state( |                 scene_manager_set_scene_state( | ||||||
|                     desktop->scene_manager, DesktopScenePinTimeout, pin_timeout); |                     desktop->scene_manager, DesktopScenePinTimeout, pin_timeout); | ||||||
|                 switch_to_timeout_scene = true; |                 switch_to_timeout_scene = true; | ||||||
| @ -86,7 +89,7 @@ bool desktop_scene_locked_on_event(void* context, SceneManagerEvent event) { | |||||||
|             break; |             break; | ||||||
|         case DesktopLockedEventUpdate: |         case DesktopLockedEventUpdate: | ||||||
|             if(desktop_view_locked_is_locked_hint_visible(desktop->locked_view)) { |             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); |             desktop_view_locked_update(desktop->locked_view); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|  | |||||||
| @ -12,7 +12,7 @@ | |||||||
| #include "../animations/animation_manager.h" | #include "../animations/animation_manager.h" | ||||||
| #include "../views/desktop_events.h" | #include "../views/desktop_events.h" | ||||||
| #include "../views/desktop_view_pin_input.h" | #include "../views/desktop_view_pin_input.h" | ||||||
| #include "../desktop_helpers.h" | #include "../helpers/pin_lock.h" | ||||||
| #include "desktop_scene.h" | #include "desktop_scene.h" | ||||||
| #include "desktop_scene_i.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) { | static void desktop_scene_pin_input_done_callback(const PinCode* pin_code, void* context) { | ||||||
|     Desktop* desktop = (Desktop*)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); |         view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopPinInputEventUnlocked); | ||||||
|     } else { |     } else { | ||||||
|         view_dispatcher_send_custom_event( |         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) { | bool desktop_scene_pin_input_on_event(void* context, SceneManagerEvent event) { | ||||||
|     Desktop* desktop = (Desktop*)context; |     Desktop* desktop = (Desktop*)context; | ||||||
|     bool consumed = false; |     bool consumed = false; | ||||||
|     uint32_t pin_fails = 0; |     uint32_t pin_timeout = 0; | ||||||
| 
 | 
 | ||||||
|     if(event.type == SceneManagerEventTypeCustom) { |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|         switch(event.event) { |         switch(event.event) { | ||||||
|         case DesktopPinInputEventUnlockFailed: |         case DesktopPinInputEventUnlockFailed: | ||||||
|             pin_fails = furi_hal_rtc_get_pin_fails(); |             pin_timeout = desktop_pin_lock_get_fail_timeout(); | ||||||
|             pin_fails++; |  | ||||||
|             furi_hal_rtc_set_pin_fails(pin_fails); |  | ||||||
|             uint32_t pin_timeout = desktop_helpers_get_pin_fail_timeout(pin_fails); |  | ||||||
|             if(pin_timeout > 0) { |             if(pin_timeout > 0) { | ||||||
|                 desktop_helpers_emit_error_notification(); |                 desktop_pin_lock_error_notify(); | ||||||
|                 scene_manager_set_scene_state( |                 scene_manager_set_scene_state( | ||||||
|                     desktop->scene_manager, DesktopScenePinTimeout, pin_timeout); |                     desktop->scene_manager, DesktopScenePinTimeout, pin_timeout); | ||||||
|                 scene_manager_next_scene(desktop->scene_manager, DesktopScenePinTimeout); |                 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; |             consumed = true; | ||||||
|             break; |             break; | ||||||
|         case DesktopPinInputEventUnlocked: |         case DesktopPinInputEventUnlocked: | ||||||
|  |             desktop_pin_unlock(&desktop->settings); | ||||||
|             desktop_unlock(desktop); |             desktop_unlock(desktop); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|             break; |             break; | ||||||
|         case DesktopPinInputEventBack: |         case DesktopPinInputEventBack: | ||||||
|             scene_manager_search_and_switch_to_previous_scene( |             scene_manager_search_and_switch_to_previous_scene( | ||||||
|                 desktop->scene_manager, DesktopSceneLocked); |                 desktop->scene_manager, DesktopSceneLocked); | ||||||
|             notification_message(desktop->notification, &sequence_display_off); |             notification_message(desktop->notification, &sequence_display_backlight_off); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|  | |||||||
| @ -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]; |         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 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) { |     if(xp_to_levelup) { | ||||||
|         deed_weight = MIN(xp_to_levelup, deed_weight); |         deed_weight = MIN(xp_to_levelup, deed_weight); | ||||||
|         dolphin_state->data.icounter += deed_weight; |         dolphin_state->data.icounter += deed_weight; | ||||||
|  | |||||||
| @ -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); |     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); |     scene_manager_set_scene_state(app->scene_manager, GpioSceneUsbUart, 0); | ||||||
|     view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewUsbUart); |     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) { | 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); |         usb_uart_disable(app->usb_uart_bridge); | ||||||
|         free(scene_usb_uart); |         free(scene_usb_uart); | ||||||
|     } |     } | ||||||
|     notification_message(app->notifications, &sequence_display_unlock); |     notification_message(app->notifications, &sequence_display_backlight_enforce_auto); | ||||||
| } | } | ||||||
|  | |||||||
| @ -3,6 +3,8 @@ | |||||||
| #include <stream_buffer.h> | #include <stream_buffer.h> | ||||||
| #include <furi_hal_usb_cdc_i.h> | #include <furi_hal_usb_cdc_i.h> | ||||||
| #include "usb_cdc.h" | #include "usb_cdc.h" | ||||||
|  | #include "cli/cli_vcp.h" | ||||||
|  | #include "cli/cli.h" | ||||||
| 
 | 
 | ||||||
| #define USB_CDC_PKT_LEN CDC_DATA_SZ | #define USB_CDC_PKT_LEN CDC_DATA_SZ | ||||||
| #define USB_UART_RX_BUF_SIZE (USB_CDC_PKT_LEN * 5) | #define USB_UART_RX_BUF_SIZE (USB_CDC_PKT_LEN * 5) | ||||||
| @ -16,17 +18,16 @@ static const GpioPin* flow_pins[][2] = { | |||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| typedef enum { | typedef enum { | ||||||
|     WorkerEvtReserved = (1 << 0), // Reserved for StreamBuffer internal event
 |     WorkerEvtStop = (1 << 0), | ||||||
|     WorkerEvtStop = (1 << 1), |     WorkerEvtRxDone = (1 << 1), | ||||||
|     WorkerEvtRxDone = (1 << 2), |  | ||||||
| 
 | 
 | ||||||
|     WorkerEvtTxStop = (1 << 3), |     WorkerEvtTxStop = (1 << 2), | ||||||
|     WorkerEvtCdcRx = (1 << 4), |     WorkerEvtCdcRx = (1 << 3), | ||||||
| 
 | 
 | ||||||
|     WorkerEvtCfgChange = (1 << 5), |     WorkerEvtCfgChange = (1 << 4), | ||||||
| 
 | 
 | ||||||
|     WorkerEvtLineCfgSet = (1 << 6), |     WorkerEvtLineCfgSet = (1 << 5), | ||||||
|     WorkerEvtCtrlLineSet = (1 << 7), |     WorkerEvtCtrlLineSet = (1 << 6), | ||||||
| 
 | 
 | ||||||
| } WorkerEvtFlags; | } 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) { | static void usb_uart_vcp_init(UsbUartBridge* usb_uart, uint8_t vcp_ch) { | ||||||
|     furi_hal_usb_unlock(); |     furi_hal_usb_unlock(); | ||||||
|  |     FURI_LOG_I("", "Init %d", vcp_ch); | ||||||
|     if(vcp_ch == 0) { |     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_check(furi_hal_usb_set_config(&usb_cdc_single, NULL) == true); | ||||||
|         furi_hal_vcp_disable(); |  | ||||||
|     } else { |     } else { | ||||||
|         furi_check(furi_hal_usb_set_config(&usb_cdc_dual, NULL) == true); |         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); |     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) { | static void usb_uart_vcp_deinit(UsbUartBridge* usb_uart, uint8_t vcp_ch) { | ||||||
|     furi_hal_cdc_set_callbacks(vcp_ch, NULL, NULL); |     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) { | 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_context(usb_uart->tx_thread, usb_uart); | ||||||
|     furi_thread_set_callback(usb_uart->tx_thread, usb_uart_tx_thread); |     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_vcp_init(usb_uart, usb_uart->cfg.vcp_ch); | ||||||
|     usb_uart_serial_init(usb_uart, usb_uart->cfg.uart_ch); |     usb_uart_serial_init(usb_uart, usb_uart->cfg.uart_ch); | ||||||
|     usb_uart_set_baudrate(usb_uart, usb_uart->cfg.baudrate); |     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_update_ctrl_lines(usb_uart); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     usb_uart_vcp_deinit(usb_uart, usb_uart->cfg.vcp_ch); |     usb_uart_vcp_deinit(usb_uart, usb_uart->cfg.vcp_ch); | ||||||
|     usb_uart_serial_deinit(usb_uart, usb_uart->cfg.uart_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) { |     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][0], GpioModeAnalog); | ||||||
|         furi_hal_gpio_init_simple(flow_pins[usb_uart->cfg.flow_pins - 1][1], 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); |     osMutexDelete(usb_uart->usb_mutex); | ||||||
|     osSemaphoreDelete(usb_uart->tx_sem); |     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; |     return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -17,8 +17,6 @@ const CanvasFontParameters canvas_font_params[FontTotalNumber] = { | |||||||
| Canvas* canvas_init() { | Canvas* canvas_init() { | ||||||
|     Canvas* canvas = malloc(sizeof(Canvas)); |     Canvas* canvas = malloc(sizeof(Canvas)); | ||||||
| 
 | 
 | ||||||
|     furi_hal_power_insomnia_enter(); |  | ||||||
| 
 |  | ||||||
|     // Setup u8g2
 |     // Setup u8g2
 | ||||||
|     u8g2_Setup_st756x_flipper(&canvas->fb, U8G2_R0, u8x8_hw_spi_stm32, u8g2_gpio_and_delay_stm32); |     u8g2_Setup_st756x_flipper(&canvas->fb, U8G2_R0, u8x8_hw_spi_stm32, u8g2_gpio_and_delay_stm32); | ||||||
|     canvas->orientation = CanvasOrientationHorizontal; |     canvas->orientation = CanvasOrientationHorizontal; | ||||||
| @ -31,8 +29,6 @@ Canvas* canvas_init() { | |||||||
|     canvas_clear(canvas); |     canvas_clear(canvas); | ||||||
|     canvas_commit(canvas); |     canvas_commit(canvas); | ||||||
| 
 | 
 | ||||||
|     furi_hal_power_insomnia_exit(); |  | ||||||
| 
 |  | ||||||
|     return canvas; |     return canvas; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -41,7 +41,6 @@ iButtonApp::iButtonApp() | |||||||
|     : notification{"notification"} |     : notification{"notification"} | ||||||
|     , storage{"storage"} |     , storage{"storage"} | ||||||
|     , dialogs{"dialogs"} { |     , dialogs{"dialogs"} { | ||||||
|     furi_hal_power_insomnia_enter(); |  | ||||||
|     key = ibutton_key_alloc(); |     key = ibutton_key_alloc(); | ||||||
|     key_worker = ibutton_worker_alloc(); |     key_worker = ibutton_worker_alloc(); | ||||||
|     ibutton_worker_start_thread(key_worker); |     ibutton_worker_start_thread(key_worker); | ||||||
| @ -56,8 +55,6 @@ iButtonApp::~iButtonApp() { | |||||||
|     ibutton_worker_stop_thread(key_worker); |     ibutton_worker_stop_thread(key_worker); | ||||||
|     ibutton_worker_free(key_worker); |     ibutton_worker_free(key_worker); | ||||||
|     ibutton_key_free(key); |     ibutton_key_free(key); | ||||||
| 
 |  | ||||||
|     furi_hal_power_insomnia_exit(); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| iButtonAppViewManager* iButtonApp::get_view_manager() { | iButtonAppViewManager* iButtonApp::get_view_manager() { | ||||||
| @ -133,18 +130,18 @@ uint8_t iButtonApp::get_file_name_size() { | |||||||
|     return file_name_size; |     return file_name_size; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void iButtonApp::notify_green_blink() { | void iButtonApp::notify_read() { | ||||||
|     notification_message(notification, &sequence_blink_green_10); |     notification_message(notification, &sequence_blink_cyan_10); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void iButtonApp::notify_emulate() { | ||||||
|  |     notification_message(notification, &sequence_blink_magenta_10); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void iButtonApp::notify_yellow_blink() { | void iButtonApp::notify_yellow_blink() { | ||||||
|     notification_message(notification, &sequence_blink_yellow_10); |     notification_message(notification, &sequence_blink_yellow_10); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void iButtonApp::notify_red_blink() { |  | ||||||
|     notification_message(notification, &sequence_blink_red_10); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void iButtonApp::notify_error() { | void iButtonApp::notify_error() { | ||||||
|     notification_message(notification, &sequence_error); |     notification_message(notification, &sequence_error); | ||||||
| } | } | ||||||
|  | |||||||
| @ -75,9 +75,9 @@ public: | |||||||
|     iButtonWorker* get_key_worker(); |     iButtonWorker* get_key_worker(); | ||||||
|     iButtonKey* get_key(); |     iButtonKey* get_key(); | ||||||
| 
 | 
 | ||||||
|     void notify_green_blink(); |     void notify_read(); | ||||||
|     void notify_yellow_blink(); |     void notify_yellow_blink(); | ||||||
|     void notify_red_blink(); |     void notify_emulate(); | ||||||
| 
 | 
 | ||||||
|     void notify_error(); |     void notify_error(); | ||||||
|     void notify_success(); |     void notify_success(); | ||||||
|  | |||||||
| @ -83,7 +83,7 @@ bool iButtonSceneEmulate::on_event(iButtonApp* app, iButtonEvent* event) { | |||||||
|         app->notify_yellow_blink(); |         app->notify_yellow_blink(); | ||||||
|         consumed = true; |         consumed = true; | ||||||
|     } else if(event->type == iButtonEvent::Type::EventTypeTick) { |     } else if(event->type == iButtonEvent::Type::EventTypeTick) { | ||||||
|         app->notify_red_blink(); |         app->notify_emulate(); | ||||||
|         consumed = true; |         consumed = true; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -54,7 +54,7 @@ bool iButtonSceneRead::on_event(iButtonApp* app, iButtonEvent* event) { | |||||||
|         } |         } | ||||||
|     } else if(event->type == iButtonEvent::Type::EventTypeTick) { |     } else if(event->type == iButtonEvent::Type::EventTypeTick) { | ||||||
|         consumed = true; |         consumed = true; | ||||||
|         app->notify_red_blink(); |         app->notify_read(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return consumed; |     return consumed; | ||||||
|  | |||||||
| @ -95,7 +95,7 @@ bool iButtonSceneWrite::on_event(iButtonApp* app, iButtonEvent* event) { | |||||||
|         if(blink_yellow) { |         if(blink_yellow) { | ||||||
|             app->notify_yellow_blink(); |             app->notify_yellow_blink(); | ||||||
|         } else { |         } else { | ||||||
|             app->notify_red_blink(); |             app->notify_emulate(); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -211,46 +211,12 @@ void InfraredApp::notify_success() { | |||||||
|     notification_message(notification, &sequence_success); |     notification_message(notification, &sequence_success); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void InfraredApp::notify_red_blink() { | void InfraredApp::notify_blink_read() { | ||||||
|     notification_message(notification, &sequence_blink_red_10); |     notification_message(notification, &sequence_blink_cyan_10); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void InfraredApp::notify_click() { | void InfraredApp::notify_blink_send() { | ||||||
|     static const NotificationSequence sequence = { |     notification_message(notification, &sequence_blink_magenta_10); | ||||||
|         &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); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| DialogsApp* InfraredApp::get_dialogs() { | DialogsApp* InfraredApp::get_dialogs() { | ||||||
| @ -279,5 +245,5 @@ void InfraredApp::set_received_signal(const InfraredAppSignal& signal) { | |||||||
| 
 | 
 | ||||||
| void InfraredApp::signal_sent_callback(void* context) { | void InfraredApp::signal_sent_callback(void* context) { | ||||||
|     InfraredApp* app = static_cast<InfraredApp*>(context); |     InfraredApp* app = static_cast<InfraredApp*>(context); | ||||||
|     app->notify_blink_green(); |     app->notify_blink_send(); | ||||||
| } | } | ||||||
|  | |||||||
| @ -217,17 +217,13 @@ public: | |||||||
|     /** Play success notification */ |     /** Play success notification */ | ||||||
|     void notify_success(); |     void notify_success(); | ||||||
|     /** Play red blink notification */ |     /** Play red blink notification */ | ||||||
|     void notify_red_blink(); |     void notify_blink_read(); | ||||||
|     /** Light green */ |     /** Light green */ | ||||||
|     void notify_green_on(); |     void notify_green_on(); | ||||||
|     /** Disable green light */ |     /** Disable green light */ | ||||||
|     void notify_green_off(); |     void notify_green_off(); | ||||||
|     /** Play click sound */ |     /** Blink on send */ | ||||||
|     void notify_click(); |     void notify_blink_send(); | ||||||
|     /** Play click and green notification */ |  | ||||||
|     void notify_click_and_green_blink(); |  | ||||||
|     /** Blink green light */ |  | ||||||
|     void notify_blink_green(); |  | ||||||
| 
 | 
 | ||||||
|     /** Get Dialogs instance */ |     /** Get Dialogs instance */ | ||||||
|     DialogsApp* get_dialogs(); |     DialogsApp* get_dialogs(); | ||||||
|  | |||||||
| @ -51,7 +51,7 @@ bool InfraredAppSceneLearn::on_event(InfraredApp* app, InfraredAppEvent* event) | |||||||
|     switch(event->type) { |     switch(event->type) { | ||||||
|     case InfraredAppEvent::Type::Tick: |     case InfraredAppEvent::Type::Tick: | ||||||
|         consumed = true; |         consumed = true; | ||||||
|         app->notify_red_blink(); |         app->notify_blink_read(); | ||||||
|         break; |         break; | ||||||
|     case InfraredAppEvent::Type::InfraredMessageReceived: |     case InfraredAppEvent::Type::InfraredMessageReceived: | ||||||
|         app->notify_success(); |         app->notify_success(); | ||||||
|  | |||||||
| @ -95,7 +95,6 @@ bool InfraredAppSceneLearnSuccess::on_event(InfraredApp* app, InfraredAppEvent* | |||||||
|         case DialogExPressCenter: |         case DialogExPressCenter: | ||||||
|             if(!button_pressed) { |             if(!button_pressed) { | ||||||
|                 button_pressed = true; |                 button_pressed = true; | ||||||
|                 app->notify_click_and_green_blink(); |  | ||||||
| 
 | 
 | ||||||
|                 auto signal = app->get_received_signal(); |                 auto signal = app->get_received_signal(); | ||||||
|                 if(signal.is_raw()) { |                 if(signal.is_raw()) { | ||||||
|  | |||||||
| @ -72,14 +72,12 @@ bool InfraredAppSceneRemote::on_event(InfraredApp* app, InfraredAppEvent* event) | |||||||
|         switch(event->payload.menu_index) { |         switch(event->payload.menu_index) { | ||||||
|         case ButtonIndexPlus: |         case ButtonIndexPlus: | ||||||
|             furi_assert(event->type == InfraredAppEvent::Type::MenuSelected); |             furi_assert(event->type == InfraredAppEvent::Type::MenuSelected); | ||||||
|             app->notify_click(); |  | ||||||
|             buttonmenu_item_selected = event->payload.menu_index; |             buttonmenu_item_selected = event->payload.menu_index; | ||||||
|             app->set_learn_new_remote(false); |             app->set_learn_new_remote(false); | ||||||
|             app->switch_to_next_scene(InfraredApp::Scene::Learn); |             app->switch_to_next_scene(InfraredApp::Scene::Learn); | ||||||
|             break; |             break; | ||||||
|         case ButtonIndexEdit: |         case ButtonIndexEdit: | ||||||
|             furi_assert(event->type == InfraredAppEvent::Type::MenuSelected); |             furi_assert(event->type == InfraredAppEvent::Type::MenuSelected); | ||||||
|             app->notify_click(); |  | ||||||
|             buttonmenu_item_selected = event->payload.menu_index; |             buttonmenu_item_selected = event->payload.menu_index; | ||||||
|             app->switch_to_next_scene(InfraredApp::Scene::Edit); |             app->switch_to_next_scene(InfraredApp::Scene::Edit); | ||||||
|             break; |             break; | ||||||
| @ -89,7 +87,6 @@ bool InfraredAppSceneRemote::on_event(InfraredApp* app, InfraredAppEvent* event) | |||||||
| 
 | 
 | ||||||
|             if(pressed && !button_pressed) { |             if(pressed && !button_pressed) { | ||||||
|                 button_pressed = true; |                 button_pressed = true; | ||||||
|                 app->notify_click_and_green_blink(); |  | ||||||
| 
 | 
 | ||||||
|                 auto button_signal = |                 auto button_signal = | ||||||
|                     app->get_remote_manager()->get_button_data(event->payload.menu_index); |                     app->get_remote_manager()->get_button_data(event->payload.menu_index); | ||||||
|  | |||||||
| @ -56,6 +56,7 @@ bool InfraredAppSceneUniversalCommon::on_event(InfraredApp* app, InfraredAppEven | |||||||
|     if(brute_force_started) { |     if(brute_force_started) { | ||||||
|         if(event->type == InfraredAppEvent::Type::Tick) { |         if(event->type == InfraredAppEvent::Type::Tick) { | ||||||
|             auto view_manager = app->get_view_manager(); |             auto view_manager = app->get_view_manager(); | ||||||
|  |             app->notify_blink_send(); | ||||||
|             InfraredAppEvent tick_event = {.type = InfraredAppEvent::Type::Tick}; |             InfraredAppEvent tick_event = {.type = InfraredAppEvent::Type::Tick}; | ||||||
|             view_manager->send_event(&tick_event); |             view_manager->send_event(&tick_event); | ||||||
|             bool result = brute_force.send_next_bruteforce(); |             bool result = brute_force.send_next_bruteforce(); | ||||||
| @ -81,6 +82,7 @@ bool InfraredAppSceneUniversalCommon::on_event(InfraredApp* app, InfraredAppEven | |||||||
|                 DOLPHIN_DEED(DolphinDeedIrBruteForce); |                 DOLPHIN_DEED(DolphinDeedIrBruteForce); | ||||||
|                 brute_force_started = true; |                 brute_force_started = true; | ||||||
|                 show_popup(app, record_amount); |                 show_popup(app, record_amount); | ||||||
|  |                 app->notify_blink_send(); | ||||||
|             } else { |             } else { | ||||||
|                 app->switch_to_previous_scene(); |                 app->switch_to_previous_scene(); | ||||||
|             } |             } | ||||||
|  | |||||||
| @ -31,11 +31,9 @@ LfRfidApp::LfRfidApp() | |||||||
|     , storage{"storage"} |     , storage{"storage"} | ||||||
|     , dialogs{"dialogs"} |     , dialogs{"dialogs"} | ||||||
|     , text_store(40) { |     , text_store(40) { | ||||||
|     furi_hal_power_insomnia_enter(); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| LfRfidApp::~LfRfidApp() { | LfRfidApp::~LfRfidApp() { | ||||||
|     furi_hal_power_insomnia_exit(); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void LfRfidApp::run(void* _args) { | void LfRfidApp::run(void* _args) { | ||||||
|  | |||||||
| @ -29,7 +29,7 @@ bool LfRfidAppSceneEmulate::on_event(LfRfidApp* app, LfRfidApp::Event* event) { | |||||||
|     bool consumed = false; |     bool consumed = false; | ||||||
| 
 | 
 | ||||||
|     if(event->type == LfRfidApp::EventType::Tick) { |     if(event->type == LfRfidApp::EventType::Tick) { | ||||||
|         notification_message(app->notification, &sequence_blink_cyan_10); |         notification_message(app->notification, &sequence_blink_magenta_10); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return consumed; |     return consumed; | ||||||
|  | |||||||
| @ -24,9 +24,9 @@ bool LfRfidAppSceneRead::on_event(LfRfidApp* app, LfRfidApp::Event* event) { | |||||||
|             if(app->worker.any_read()) { |             if(app->worker.any_read()) { | ||||||
|                 notification_message(app->notification, &sequence_blink_green_10); |                 notification_message(app->notification, &sequence_blink_green_10); | ||||||
|             } else if(app->worker.detect()) { |             } else if(app->worker.detect()) { | ||||||
|                 notification_message(app->notification, &sequence_blink_blue_10); |                 notification_message(app->notification, &sequence_blink_cyan_10); | ||||||
|             } else { |             } else { | ||||||
|                 notification_message(app->notification, &sequence_blink_red_10); |                 notification_message(app->notification, &sequence_blink_cyan_10); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -32,7 +32,7 @@ bool LfRfidAppSceneWrite::on_event(LfRfidApp* app, LfRfidApp::Event* event) { | |||||||
| 
 | 
 | ||||||
|         switch(result) { |         switch(result) { | ||||||
|         case RfidWorker::WriteResult::Nothing: |         case RfidWorker::WriteResult::Nothing: | ||||||
|             notification_message(app->notification, &sequence_blink_yellow_10); |             notification_message(app->notification, &sequence_blink_magenta_10); | ||||||
|             break; |             break; | ||||||
|         case RfidWorker::WriteResult::Ok: |         case RfidWorker::WriteResult::Ok: | ||||||
|             notification_message(app->notification, &sequence_success); |             notification_message(app->notification, &sequence_success); | ||||||
| @ -51,7 +51,7 @@ bool LfRfidAppSceneWrite::on_event(LfRfidApp* app, LfRfidApp::Event* event) { | |||||||
|                     AlignTop); |                     AlignTop); | ||||||
|                 card_not_supported = true; |                 card_not_supported = true; | ||||||
|             } |             } | ||||||
|             notification_message(app->notification, &sequence_blink_red_10); |             notification_message(app->notification, &sequence_blink_yellow_10); | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -238,7 +238,10 @@ static void loader_thread_state_callback(FuriThreadState thread_state, void* con | |||||||
|     if(thread_state == FuriThreadStateRunning) { |     if(thread_state == FuriThreadStateRunning) { | ||||||
|         event.type = LoaderEventTypeApplicationStarted; |         event.type = LoaderEventTypeApplicationStarted; | ||||||
|         furi_pubsub_publish(loader_instance->pubsub, &event); |         furi_pubsub_publish(loader_instance->pubsub, &event); | ||||||
|  | 
 | ||||||
|  |         if(!loader_instance->application->flags & FlipperApplicationFlagInsomniaSafe) { | ||||||
|             furi_hal_power_insomnia_enter(); |             furi_hal_power_insomnia_enter(); | ||||||
|  |         } | ||||||
|     } else if(thread_state == FuriThreadStateStopped) { |     } else if(thread_state == FuriThreadStateStopped) { | ||||||
|         FURI_LOG_I( |         FURI_LOG_I( | ||||||
|             TAG, |             TAG, | ||||||
| @ -251,7 +254,9 @@ static void loader_thread_state_callback(FuriThreadState thread_state, void* con | |||||||
|             loader_instance->application_arguments = NULL; |             loader_instance->application_arguments = NULL; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         if(!loader_instance->application->flags & FlipperApplicationFlagInsomniaSafe) { | ||||||
|             furi_hal_power_insomnia_exit(); |             furi_hal_power_insomnia_exit(); | ||||||
|  |         } | ||||||
|         loader_unlock(instance); |         loader_unlock(instance); | ||||||
| 
 | 
 | ||||||
|         event.type = LoaderEventTypeApplicationStopped; |         event.type = LoaderEventTypeApplicationStopped; | ||||||
|  | |||||||
| @ -262,6 +262,7 @@ static bool nfc_device_save_mifare_df_app(FlipperFormat* file, MifareDesfireAppl | |||||||
|                    file, app->key_settings, string_get_cstr(prefix))) |                    file, app->key_settings, string_get_cstr(prefix))) | ||||||
|                 break; |                 break; | ||||||
|         } |         } | ||||||
|  |         if(!app->file_head) break; | ||||||
|         uint32_t n_files = 0; |         uint32_t n_files = 0; | ||||||
|         for(MifareDesfireFile* f = app->file_head; f; f = f->next) { |         for(MifareDesfireFile* f = app->file_head; f; f = f->next) { | ||||||
|             n_files++; |             n_files++; | ||||||
| @ -477,6 +478,7 @@ static bool nfc_device_save_mifare_df_data(FlipperFormat* file, NfcDevice* dev) | |||||||
|             n_apps++; |             n_apps++; | ||||||
|         } |         } | ||||||
|         if(!flipper_format_write_uint32(file, "Application Count", &n_apps, 1)) break; |         if(!flipper_format_write_uint32(file, "Application Count", &n_apps, 1)) break; | ||||||
|  |         if(n_apps == 0) break; | ||||||
|         tmp = malloc(n_apps * 3); |         tmp = malloc(n_apps * 3); | ||||||
|         int i = 0; |         int i = 0; | ||||||
|         for(MifareDesfireApplication* app = data->app_head; app; app = app->next) { |         for(MifareDesfireApplication* app = data->app_head; app; app = app->next) { | ||||||
|  | |||||||
| @ -86,7 +86,6 @@ void nfc_worker_change_state(NfcWorker* nfc_worker, NfcWorkerState state) { | |||||||
| int32_t nfc_worker_task(void* context) { | int32_t nfc_worker_task(void* context) { | ||||||
|     NfcWorker* nfc_worker = context; |     NfcWorker* nfc_worker = context; | ||||||
| 
 | 
 | ||||||
|     furi_hal_power_insomnia_enter(); |  | ||||||
|     furi_hal_nfc_exit_sleep(); |     furi_hal_nfc_exit_sleep(); | ||||||
| 
 | 
 | ||||||
|     if(nfc_worker->state == NfcWorkerStateDetect) { |     if(nfc_worker->state == NfcWorkerStateDetect) { | ||||||
| @ -110,7 +109,6 @@ int32_t nfc_worker_task(void* context) { | |||||||
|     } |     } | ||||||
|     furi_hal_nfc_sleep(); |     furi_hal_nfc_sleep(); | ||||||
|     nfc_worker_change_state(nfc_worker, NfcWorkerStateReady); |     nfc_worker_change_state(nfc_worker, NfcWorkerStateReady); | ||||||
|     furi_hal_power_insomnia_exit(); |  | ||||||
| 
 | 
 | ||||||
|     return 0; |     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"); |                 FURI_LOG_W(TAG, "Bad DESFire GET_KEY_SETTINGS response"); | ||||||
|                 free(data->master_key_settings); |                 free(data->master_key_settings); | ||||||
|                 data->master_key_settings = NULL; |                 data->master_key_settings = NULL; | ||||||
|  |                 continue; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             MifareDesfireKeyVersion** key_version_head = |             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"); |                     FURI_LOG_W(TAG, "Bad DESFire GET_KEY_SETTINGS response"); | ||||||
|                     free(app->key_settings); |                     free(app->key_settings); | ||||||
|                     app->key_settings = NULL; |                     app->key_settings = NULL; | ||||||
|  |                     continue; | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 MifareDesfireKeyVersion** key_version_head = &app->key_settings->key_version_head; |                 MifareDesfireKeyVersion** key_version_head = &app->key_settings->key_version_head; | ||||||
|  | |||||||
| @ -50,9 +50,9 @@ typedef enum { | |||||||
| 
 | 
 | ||||||
|     NotificationMessageTypeDelay, |     NotificationMessageTypeDelay, | ||||||
| 
 | 
 | ||||||
|     NotificationMessageTypeLedDisplay, |     NotificationMessageTypeLedDisplayBacklight, | ||||||
|     NotificationMessageTypeLedDisplayLock, |     NotificationMessageTypeLedDisplayBacklightEnforceOn, | ||||||
|     NotificationMessageTypeLedDisplayUnlock, |     NotificationMessageTypeLedDisplayBacklightEnforceAuto, | ||||||
| 
 | 
 | ||||||
|     NotificationMessageTypeDoNotReset, |     NotificationMessageTypeDoNotReset, | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -152,7 +152,7 @@ void notification_sound_off() { | |||||||
| static void notification_display_timer(void* ctx) { | static void notification_display_timer(void* ctx) { | ||||||
|     furi_assert(ctx); |     furi_assert(ctx); | ||||||
|     NotificationApp* app = ctx; |     NotificationApp* app = ctx; | ||||||
|     notification_message(app, &sequence_display_off); |     notification_message(app, &sequence_display_backlight_off); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // message processing
 | // message processing
 | ||||||
| @ -174,7 +174,7 @@ void notification_process_notification_message( | |||||||
| 
 | 
 | ||||||
|     while(notification_message != NULL) { |     while(notification_message != NULL) { | ||||||
|         switch(notification_message->type) { |         switch(notification_message->type) { | ||||||
|         case NotificationMessageTypeLedDisplay: |         case NotificationMessageTypeLedDisplayBacklight: | ||||||
|             // if on - switch on and start timer
 |             // if on - switch on and start timer
 | ||||||
|             // if off - switch off and stop timer
 |             // if off - switch off and stop timer
 | ||||||
|             // on timer - switch off
 |             // on timer - switch off
 | ||||||
| @ -190,7 +190,7 @@ void notification_process_notification_message( | |||||||
|             } |             } | ||||||
|             reset_mask |= reset_display_mask; |             reset_mask |= reset_display_mask; | ||||||
|             break; |             break; | ||||||
|         case NotificationMessageTypeLedDisplayLock: |         case NotificationMessageTypeLedDisplayBacklightEnforceOn: | ||||||
|             furi_assert(app->display_led_lock < UINT8_MAX); |             furi_assert(app->display_led_lock < UINT8_MAX); | ||||||
|             app->display_led_lock++; |             app->display_led_lock++; | ||||||
|             if(app->display_led_lock == 1) { |             if(app->display_led_lock == 1) { | ||||||
| @ -199,7 +199,7 @@ void notification_process_notification_message( | |||||||
|                     notification_message->data.led.value * display_brightness_setting); |                     notification_message->data.led.value * display_brightness_setting); | ||||||
|             } |             } | ||||||
|             break; |             break; | ||||||
|         case NotificationMessageTypeLedDisplayUnlock: |         case NotificationMessageTypeLedDisplayBacklightEnforceAuto: | ||||||
|             furi_assert(app->display_led_lock > 0); |             furi_assert(app->display_led_lock > 0); | ||||||
|             app->display_led_lock--; |             app->display_led_lock--; | ||||||
|             if(app->display_led_lock == 0) { |             if(app->display_led_lock == 0) { | ||||||
| @ -322,7 +322,7 @@ void notification_process_internal_message(NotificationApp* app, NotificationApp | |||||||
| 
 | 
 | ||||||
|     while(notification_message != NULL) { |     while(notification_message != NULL) { | ||||||
|         switch(notification_message->type) { |         switch(notification_message->type) { | ||||||
|         case NotificationMessageTypeLedDisplay: |         case NotificationMessageTypeLedDisplayBacklight: | ||||||
|             notification_apply_internal_led_layer( |             notification_apply_internal_led_layer( | ||||||
|                 &app->display, |                 &app->display, | ||||||
|                 notification_settings_get_display_brightness( |                 notification_settings_get_display_brightness( | ||||||
| @ -442,7 +442,7 @@ static void input_event_callback(const void* value, void* context) { | |||||||
|     furi_assert(value); |     furi_assert(value); | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     NotificationApp* app = context; |     NotificationApp* app = context; | ||||||
|     notification_message(app, &sequence_display_on); |     notification_message(app, &sequence_display_backlight_on); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // App alloc
 | // App alloc
 | ||||||
| @ -482,7 +482,7 @@ static NotificationApp* notification_app_alloc() { | |||||||
|     // display backlight control
 |     // display backlight control
 | ||||||
|     app->event_record = furi_record_open("input_events"); |     app->event_record = furi_record_open("input_events"); | ||||||
|     furi_pubsub_subscribe(app->event_record, input_event_callback, app); |     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; |     return app; | ||||||
| }; | }; | ||||||
|  | |||||||
| @ -4,24 +4,27 @@ | |||||||
| 
 | 
 | ||||||
| /*********************************** Messages **********************************/ | /*********************************** Messages **********************************/ | ||||||
| 
 | 
 | ||||||
| // Display
 | /** Display: backlight wakeup */ | ||||||
| const NotificationMessage message_display_on = { | const NotificationMessage message_display_backlight_on = { | ||||||
|     .type = NotificationMessageTypeLedDisplay, |     .type = NotificationMessageTypeLedDisplayBacklight, | ||||||
|     .data.led.value = 0xFF, |     .data.led.value = 0xFF, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const NotificationMessage message_display_off = { | /** Display: backlight force off */ | ||||||
|     .type = NotificationMessageTypeLedDisplay, | const NotificationMessage message_display_backlight_off = { | ||||||
|  |     .type = NotificationMessageTypeLedDisplayBacklight, | ||||||
|     .data.led.value = 0x00, |     .data.led.value = 0x00, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const NotificationMessage message_display_lock = { | /** Display: backlight always on */ | ||||||
|     .type = NotificationMessageTypeLedDisplayLock, | const NotificationMessage message_display_backlight_enforce_on = { | ||||||
|  |     .type = NotificationMessageTypeLedDisplayBacklightEnforceOn, | ||||||
|     .data.led.value = 0xFF, |     .data.led.value = 0xFF, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const NotificationMessage message_display_unlock = { | /** Display: automatic backlight management, with configured timeout */ | ||||||
|     .type = NotificationMessageTypeLedDisplayUnlock, | const NotificationMessage message_display_backlight_enforce_auto = { | ||||||
|  |     .type = NotificationMessageTypeLedDisplayBacklightEnforceAuto, | ||||||
|     .data.led.value = 0x00, |     .data.led.value = 0x00, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| @ -166,7 +169,7 @@ const NotificationSequence sequence_reset_rgb = { | |||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const NotificationSequence sequence_reset_display = { | const NotificationSequence sequence_reset_display = { | ||||||
|     &message_display_off, |     &message_display_backlight_off, | ||||||
|     NULL, |     NULL, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| @ -188,29 +191,31 @@ const NotificationSequence sequence_set_vibro_on = { | |||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| // Display
 | // Display
 | ||||||
| const NotificationSequence sequence_display_on = { | const NotificationSequence sequence_display_backlight_on = { | ||||||
|     &message_display_on, |     &message_display_backlight_on, | ||||||
|     NULL, |     NULL, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const NotificationSequence sequence_display_off = { | const NotificationSequence sequence_display_backlight_off = { | ||||||
|     &message_display_off, |     &message_display_backlight_off, | ||||||
|     NULL, |     NULL, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const NotificationSequence sequence_display_lock = { | /** Display: backlight always on lock */ | ||||||
|     &message_display_lock, | const NotificationSequence sequence_display_backlight_enforce_on = { | ||||||
|  |     &message_display_backlight_enforce_on, | ||||||
|     NULL, |     NULL, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const NotificationSequence sequence_display_unlock = { | /** Display: backlight always on unlock */ | ||||||
|     &message_display_unlock, | const NotificationSequence sequence_display_backlight_enforce_auto = { | ||||||
|  |     &message_display_backlight_enforce_auto, | ||||||
|     NULL, |     NULL, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const NotificationSequence sequence_display_off_delay_1000 = { | const NotificationSequence sequence_display_backlight_off_delay_1000 = { | ||||||
|     &message_delay_1000, |     &message_delay_1000, | ||||||
|     &message_display_off, |     &message_display_backlight_off, | ||||||
|     NULL, |     NULL, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| @ -309,6 +314,13 @@ const NotificationSequence sequence_blink_cyan_10 = { | |||||||
|     NULL, |     NULL, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | const NotificationSequence sequence_blink_magenta_10 = { | ||||||
|  |     &message_red_255, | ||||||
|  |     &message_blue_255, | ||||||
|  |     &message_delay_10, | ||||||
|  |     NULL, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| const NotificationSequence sequence_blink_red_100 = { | const NotificationSequence sequence_blink_red_100 = { | ||||||
|     &message_red_255, |     &message_red_255, | ||||||
|     &message_delay_100, |     &message_delay_100, | ||||||
| @ -376,7 +388,7 @@ const NotificationSequence sequence_double_vibro = { | |||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const NotificationSequence sequence_success = { | const NotificationSequence sequence_success = { | ||||||
|     &message_display_on, |     &message_display_backlight_on, | ||||||
|     &message_green_255, |     &message_green_255, | ||||||
|     &message_vibro_on, |     &message_vibro_on, | ||||||
|     &message_note_c5, |     &message_note_c5, | ||||||
| @ -393,7 +405,7 @@ const NotificationSequence sequence_success = { | |||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const NotificationSequence sequence_error = { | const NotificationSequence sequence_error = { | ||||||
|     &message_display_on, |     &message_display_backlight_on, | ||||||
|     &message_red_255, |     &message_red_255, | ||||||
|     &message_vibro_on, |     &message_vibro_on, | ||||||
|     &message_note_c5, |     &message_note_c5, | ||||||
| @ -415,27 +427,27 @@ const NotificationSequence sequence_audiovisual_alert = { | |||||||
|     &message_force_display_brightness_setting_1f, |     &message_force_display_brightness_setting_1f, | ||||||
|     &message_vibro_on, |     &message_vibro_on, | ||||||
| 
 | 
 | ||||||
|     &message_display_on, |     &message_display_backlight_on, | ||||||
|     &message_note_c7, |     &message_note_c7, | ||||||
|     &message_delay_250, |     &message_delay_250, | ||||||
| 
 | 
 | ||||||
|     &message_display_off, |     &message_display_backlight_off, | ||||||
|     &message_note_c4, |     &message_note_c4, | ||||||
|     &message_delay_250, |     &message_delay_250, | ||||||
| 
 | 
 | ||||||
|     &message_display_on, |     &message_display_backlight_on, | ||||||
|     &message_note_c7, |     &message_note_c7, | ||||||
|     &message_delay_250, |     &message_delay_250, | ||||||
| 
 | 
 | ||||||
|     &message_display_off, |     &message_display_backlight_off, | ||||||
|     &message_note_c4, |     &message_note_c4, | ||||||
|     &message_delay_250, |     &message_delay_250, | ||||||
| 
 | 
 | ||||||
|     &message_display_on, |     &message_display_backlight_on, | ||||||
|     &message_note_c7, |     &message_note_c7, | ||||||
|     &message_delay_250, |     &message_delay_250, | ||||||
| 
 | 
 | ||||||
|     &message_display_off, |     &message_display_backlight_off, | ||||||
|     &message_note_c4, |     &message_note_c4, | ||||||
|     &message_delay_250, |     &message_delay_250, | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -9,15 +9,10 @@ extern "C" { | |||||||
| /*********************************** Messages **********************************/ | /*********************************** Messages **********************************/ | ||||||
| 
 | 
 | ||||||
| // Display
 | // Display
 | ||||||
| 
 | extern const NotificationMessage message_display_backlight_on; | ||||||
| /** Display: backlight wakeup */ | extern const NotificationMessage message_display_backlight_off; | ||||||
| extern const NotificationMessage message_display_on; | extern const NotificationMessage message_display_backlight_enforce_on; | ||||||
| /** Display: backlight force off */ | extern const NotificationMessage message_display_backlight_enforce_auto; | ||||||
| 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; |  | ||||||
| 
 | 
 | ||||||
| // Led ON
 | // Led ON
 | ||||||
| extern const NotificationMessage message_red_255; | extern const NotificationMessage message_red_255; | ||||||
| @ -71,15 +66,16 @@ extern const NotificationSequence sequence_set_vibro_on; | |||||||
| 
 | 
 | ||||||
| // Display
 | // Display
 | ||||||
| /** Display: backlight wakeup */ | /** Display: backlight wakeup */ | ||||||
| extern const NotificationSequence sequence_display_on; | extern const NotificationSequence sequence_display_backlight_on; | ||||||
| /** Display: backlight force off */ | /** Display: backlight force off */ | ||||||
| extern const NotificationSequence sequence_display_off; | extern const NotificationSequence sequence_display_backlight_off; | ||||||
| /** Display: backlight always on lock */ |  | ||||||
| extern const NotificationSequence sequence_display_lock; |  | ||||||
| /** Display: backlight always on unlock */ |  | ||||||
| extern const NotificationSequence sequence_display_unlock; |  | ||||||
| /** Display: backlight force off after a delay of 1000ms */ | /** 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
 | // Charging
 | ||||||
| extern const NotificationSequence sequence_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_green_10; | ||||||
| extern const NotificationSequence sequence_blink_yellow_10; | extern const NotificationSequence sequence_blink_yellow_10; | ||||||
| extern const NotificationSequence sequence_blink_cyan_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_red_100; | ||||||
| extern const NotificationSequence sequence_blink_green_100; | extern const NotificationSequence sequence_blink_green_100; | ||||||
|  | |||||||
| @ -70,7 +70,7 @@ static void backlight_changed(VariableItem* item) { | |||||||
| 
 | 
 | ||||||
|     variable_item_set_current_value_text(item, backlight_text[index]); |     variable_item_set_current_value_text(item, backlight_text[index]); | ||||||
|     app->notification->settings.display_brightness = backlight_value[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) { | 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]); |     variable_item_set_current_value_text(item, delay_text[index]); | ||||||
|     app->notification->settings.display_off_delay_ms = delay_value[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 = { | const NotificationMessage apply_message = { | ||||||
|  | |||||||
| @ -28,7 +28,7 @@ static void battery_test_battery_info_update_model(void* context) { | |||||||
|         .health = app->info.health, |         .health = app->info.health, | ||||||
|     }; |     }; | ||||||
|     battery_info_set_data(app->batery_info, &battery_info_data); |     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() { | BatteryTestApp* battery_test_alloc() { | ||||||
|  | |||||||
| @ -62,8 +62,8 @@ void rpc_cli_command_start_session(Cli* cli, string_t args, void* context) { | |||||||
|     size_t size_received = 0; |     size_t size_received = 0; | ||||||
| 
 | 
 | ||||||
|     while(1) { |     while(1) { | ||||||
|         size_received = furi_hal_vcp_rx_with_timeout(buffer, CLI_READ_BUFFER_SIZE, 50); |         size_received = cli_read_timeout(cli_rpc.cli, buffer, CLI_READ_BUFFER_SIZE, 50); | ||||||
|         if(!furi_hal_vcp_is_connected() || cli_rpc.session_close_request) { |         if(!cli_is_connected(cli_rpc.cli) || cli_rpc.session_close_request) { | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -6,9 +6,17 @@ | |||||||
| extern "C" { | extern "C" { | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|  | /** File type */ | ||||||
|  | typedef enum { | ||||||
|  |     FileTypeClosed, /**< Closed file */ | ||||||
|  |     FileTypeOpenDir, /**< Open dir */ | ||||||
|  |     FileTypeOpenFile, /**< Open file */ | ||||||
|  | } FileType; | ||||||
|  | 
 | ||||||
| /** Structure that hold file index and returned api errors */ | /** Structure that hold file index and returned api errors */ | ||||||
| struct File { | struct File { | ||||||
|     uint32_t file_id; /**< File ID for internal references */ |     uint32_t file_id; /**< File ID for internal references */ | ||||||
|  |     FileType type; | ||||||
|     FS_Error error_id; /**< Standart API error from FS_Error enum */ |     FS_Error error_id; /**< Standart API error from FS_Error enum */ | ||||||
|     int32_t internal_error_id; /**< Internal API error value */ |     int32_t internal_error_id; /**< Internal API error value */ | ||||||
|     void* storage; |     void* storage; | ||||||
|  | |||||||
| @ -7,6 +7,8 @@ | |||||||
| 
 | 
 | ||||||
| #define MAX_NAME_LENGTH 256 | #define MAX_NAME_LENGTH 256 | ||||||
| 
 | 
 | ||||||
|  | #define TAG "StorageAPI" | ||||||
|  | 
 | ||||||
| #define S_API_PROLOGUE                                      \ | #define S_API_PROLOGUE                                      \ | ||||||
|     osSemaphoreId_t semaphore = osSemaphoreNew(1, 0, NULL); \ |     osSemaphoreId_t semaphore = osSemaphoreNew(1, 0, NULL); \ | ||||||
|     furi_check(semaphore != NULL); |     furi_check(semaphore != NULL); | ||||||
| @ -47,10 +49,6 @@ | |||||||
| #define S_RETURN_ERROR (return_data.error_value); | #define S_RETURN_ERROR (return_data.error_value); | ||||||
| #define S_RETURN_CSTRING (return_data.cstring_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 { | typedef enum { | ||||||
|     StorageEventFlagFileClose = (1 << 0), |     StorageEventFlagFileClose = (1 << 0), | ||||||
| } StorageEventFlag; | } StorageEventFlag; | ||||||
| @ -72,7 +70,7 @@ static bool storage_file_open_internal( | |||||||
|             .open_mode = open_mode, |             .open_mode = open_mode, | ||||||
|         }}; |         }}; | ||||||
| 
 | 
 | ||||||
|     file->file_id = FILE_OPENED_FILE; |     file->type = FileTypeOpenFile; | ||||||
| 
 | 
 | ||||||
|     S_API_MESSAGE(StorageCommandFileOpen); |     S_API_MESSAGE(StorageCommandFileOpen); | ||||||
|     S_API_EPILOGUE; |     S_API_EPILOGUE; | ||||||
| @ -113,6 +111,10 @@ bool storage_file_open( | |||||||
| 
 | 
 | ||||||
|     furi_pubsub_unsubscribe(storage_get_pubsub(file->storage), subscription); |     furi_pubsub_unsubscribe(storage_get_pubsub(file->storage), subscription); | ||||||
|     osEventFlagsDelete(event); |     osEventFlagsDelete(event); | ||||||
|  | 
 | ||||||
|  |     FURI_LOG_T( | ||||||
|  |         TAG, "File %p - %p open (%s)", (uint32_t)file - SRAM_BASE, file->file_id - SRAM_BASE, path); | ||||||
|  | 
 | ||||||
|     return result; |     return result; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -124,7 +126,8 @@ bool storage_file_close(File* file) { | |||||||
|     S_API_MESSAGE(StorageCommandFileClose); |     S_API_MESSAGE(StorageCommandFileClose); | ||||||
|     S_API_EPILOGUE; |     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; |     return S_RETURN_BOOL; | ||||||
| } | } | ||||||
| @ -234,7 +237,7 @@ static bool storage_dir_open_internal(File* file, const char* path) { | |||||||
|             .path = path, |             .path = path, | ||||||
|         }}; |         }}; | ||||||
| 
 | 
 | ||||||
|     file->file_id = FILE_OPENED_DIR; |     file->type = FileTypeOpenDir; | ||||||
| 
 | 
 | ||||||
|     S_API_MESSAGE(StorageCommandDirOpen); |     S_API_MESSAGE(StorageCommandDirOpen); | ||||||
|     S_API_EPILOGUE; |     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); |     furi_pubsub_unsubscribe(storage_get_pubsub(file->storage), subscription); | ||||||
|     osEventFlagsDelete(event); |     osEventFlagsDelete(event); | ||||||
|  | 
 | ||||||
|  |     FURI_LOG_T( | ||||||
|  |         TAG, "Dir %p - %p open (%s)", (uint32_t)file - SRAM_BASE, file->file_id - SRAM_BASE, path); | ||||||
|  | 
 | ||||||
|     return result; |     return result; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -269,7 +276,9 @@ bool storage_dir_close(File* file) { | |||||||
|     S_API_MESSAGE(StorageCommandDirClose); |     S_API_MESSAGE(StorageCommandDirClose); | ||||||
|     S_API_EPILOGUE; |     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; |     return S_RETURN_BOOL; | ||||||
| } | } | ||||||
| @ -448,18 +457,20 @@ FS_Error storage_sd_status(Storage* storage) { | |||||||
| 
 | 
 | ||||||
| File* storage_file_alloc(Storage* storage) { | File* storage_file_alloc(Storage* storage) { | ||||||
|     File* file = malloc(sizeof(File)); |     File* file = malloc(sizeof(File)); | ||||||
|     file->file_id = FILE_CLOSED; |     file->type = FileTypeClosed; | ||||||
|     file->storage = storage; |     file->storage = storage; | ||||||
| 
 | 
 | ||||||
|  |     FURI_LOG_T(TAG, "File/Dir %p alloc", (uint32_t)file - SRAM_BASE); | ||||||
|  | 
 | ||||||
|     return file; |     return file; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool storage_file_is_open(File* 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) { | bool storage_file_is_dir(File* file) { | ||||||
|     return (file->file_id == FILE_OPENED_DIR); |     return (file->type == FileTypeOpenDir); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void storage_file_free(File* file) { | 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); |     free(file); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -39,12 +39,10 @@ void storage_data_init(StorageData* storage) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool storage_data_lock(StorageData* storage) { | bool storage_data_lock(StorageData* storage) { | ||||||
|     furi_hal_power_insomnia_enter(); |  | ||||||
|     return (osMutexAcquire(storage->mutex, osWaitForever) == osOK); |     return (osMutexAcquire(storage->mutex, osWaitForever) == osOK); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool storage_data_unlock(StorageData* storage) { | bool storage_data_unlock(StorageData* storage) { | ||||||
|     furi_hal_power_insomnia_exit(); |  | ||||||
|     return (osMutexRelease(storage->mutex) == osOK); |     return (osMutexRelease(storage->mutex) == osOK); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -12,6 +12,8 @@ struct SubGhzChatWorker { | |||||||
|     volatile bool worker_stoping; |     volatile bool worker_stoping; | ||||||
|     osMessageQueueId_t event_queue; |     osMessageQueueId_t event_queue; | ||||||
|     uint32_t last_time_rx_data; |     uint32_t last_time_rx_data; | ||||||
|  | 
 | ||||||
|  |     Cli* cli; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /** Worker thread
 | /** Worker thread
 | ||||||
| @ -27,7 +29,7 @@ static int32_t subghz_chat_worker_thread(void* context) { | |||||||
|     event.event = SubGhzChatEventUserEntrance; |     event.event = SubGhzChatEventUserEntrance; | ||||||
|     osMessageQueuePut(instance->event_queue, &event, 0, 0); |     osMessageQueuePut(instance->event_queue, &event, 0, 0); | ||||||
|     while(instance->worker_running) { |     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.event = SubGhzChatEventInputData; | ||||||
|             event.c = c; |             event.c = c; | ||||||
|             osMessageQueuePut(instance->event_queue, &event, 0, osWaitForever); |             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); |     osMessageQueuePut(instance->event_queue, &event, 0, osWaitForever); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| SubGhzChatWorker* subghz_chat_worker_alloc() { | SubGhzChatWorker* subghz_chat_worker_alloc(Cli* cli) { | ||||||
|     SubGhzChatWorker* instance = malloc(sizeof(SubGhzChatWorker)); |     SubGhzChatWorker* instance = malloc(sizeof(SubGhzChatWorker)); | ||||||
| 
 | 
 | ||||||
|  |     instance->cli = cli; | ||||||
|  | 
 | ||||||
|     instance->thread = furi_thread_alloc(); |     instance->thread = furi_thread_alloc(); | ||||||
|     furi_thread_set_name(instance->thread, "SubGhzChat"); |     furi_thread_set_name(instance->thread, "SubGhzChat"); | ||||||
|     furi_thread_set_stack_size(instance->thread, 2048); |     furi_thread_set_stack_size(instance->thread, 2048); | ||||||
|  | |||||||
| @ -1,5 +1,6 @@ | |||||||
| #pragma once | #pragma once | ||||||
| #include "../subghz_i.h" | #include "../subghz_i.h" | ||||||
|  | #include <cli/cli.h> | ||||||
| 
 | 
 | ||||||
| typedef struct SubGhzChatWorker SubGhzChatWorker; | typedef struct SubGhzChatWorker SubGhzChatWorker; | ||||||
| 
 | 
 | ||||||
| @ -17,7 +18,7 @@ typedef struct { | |||||||
|     char c; |     char c; | ||||||
| } SubGhzChatEvent; | } SubGhzChatEvent; | ||||||
| 
 | 
 | ||||||
| SubGhzChatWorker* subghz_chat_worker_alloc(); | SubGhzChatWorker* subghz_chat_worker_alloc(Cli* cli); | ||||||
| void subghz_chat_worker_free(SubGhzChatWorker* instance); | void subghz_chat_worker_free(SubGhzChatWorker* instance); | ||||||
| bool subghz_chat_worker_start(SubGhzChatWorker* instance, uint32_t frequency); | bool subghz_chat_worker_start(SubGhzChatWorker* instance, uint32_t frequency); | ||||||
| void subghz_chat_worker_stop(SubGhzChatWorker* instance); | void subghz_chat_worker_stop(SubGhzChatWorker* instance); | ||||||
|  | |||||||
| @ -19,6 +19,8 @@ typedef enum { | |||||||
|     SubGhzCustomEventSceneShowErrorOk, |     SubGhzCustomEventSceneShowErrorOk, | ||||||
|     SubGhzCustomEventSceneShowErrorSub, |     SubGhzCustomEventSceneShowErrorSub, | ||||||
|     SubGhzCustomEventSceneShowOnlyRX, |     SubGhzCustomEventSceneShowOnlyRX, | ||||||
|  |     SubGhzCustomEventSceneAnalyzerLock, | ||||||
|  |     SubGhzCustomEventSceneAnalyzerUnlock, | ||||||
| 
 | 
 | ||||||
|     SubGhzCustomEventSceneExit, |     SubGhzCustomEventSceneExit, | ||||||
|     SubGhzCustomEventSceneStay, |     SubGhzCustomEventSceneStay, | ||||||
| @ -35,7 +37,6 @@ typedef enum { | |||||||
|     SubGhzCustomEventViewReadRAWSendStart, |     SubGhzCustomEventViewReadRAWSendStart, | ||||||
|     SubGhzCustomEventViewReadRAWSendStop, |     SubGhzCustomEventViewReadRAWSendStop, | ||||||
|     SubGhzCustomEventViewReadRAWSave, |     SubGhzCustomEventViewReadRAWSave, | ||||||
|     SubGhzCustomEventViewReadRAWVibro, |  | ||||||
|     SubGhzCustomEventViewReadRAWTXRXStop, |     SubGhzCustomEventViewReadRAWTXRXStop, | ||||||
|     SubGhzCustomEventViewReadRAWMore, |     SubGhzCustomEventViewReadRAWMore, | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -17,10 +17,20 @@ void subghz_scene_frequency_analyzer_on_enter(void* context) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool subghz_scene_frequency_analyzer_on_event(void* context, SceneManagerEvent event) { | 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; |     return false; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void subghz_scene_frequency_analyzer_on_exit(void* context) { | void subghz_scene_frequency_analyzer_on_exit(void* context) { | ||||||
|     // SubGhz* subghz = context;
 |     SubGhz* subghz = context; | ||||||
|  |     notification_message(subghz->notifications, &sequence_reset_rgb); | ||||||
| } | } | ||||||
|  | |||||||
| @ -168,11 +168,7 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { | |||||||
| 
 | 
 | ||||||
|         case SubGhzCustomEventViewReadRAWErase: |         case SubGhzCustomEventViewReadRAWErase: | ||||||
|             subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; |             subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; | ||||||
|             return true; |             notification_message(subghz->notifications, &sequence_reset_rgb); | ||||||
|             break; |  | ||||||
| 
 |  | ||||||
|         case SubGhzCustomEventViewReadRAWVibro: |  | ||||||
|             notification_message(subghz->notifications, &sequence_single_vibro); |  | ||||||
|             return true; |             return true; | ||||||
|             break; |             break; | ||||||
| 
 | 
 | ||||||
| @ -209,7 +205,7 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { | |||||||
|                             (SubGhzProtocolEncoderRAW*)subghz->txrx->transmitter->protocol_instance, |                             (SubGhzProtocolEncoderRAW*)subghz->txrx->transmitter->protocol_instance, | ||||||
|                             subghz_scene_read_raw_callback_end_tx, |                             subghz_scene_read_raw_callback_end_tx, | ||||||
|                             subghz); |                             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_rx_end(subghz); | ||||||
|                 subghz_sleep(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( |             subghz_protocol_raw_save_to_file_stop( | ||||||
|                 (SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result); |                 (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)); |             subghz_protocol_raw_gen_fff_data(subghz->txrx->fff_data, string_get_cstr(temp_str)); | ||||||
|             string_clear(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->state_notifications = SubGhzNotificationStateIDLE; | ||||||
|             subghz->txrx->rx_key_state = SubGhzRxKeyStateAddKey; |             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_begin(subghz, subghz->txrx->preset); | ||||||
|                         subghz_rx(subghz, subghz->txrx->frequency); |                         subghz_rx(subghz, subghz->txrx->frequency); | ||||||
|                     } |                     } | ||||||
|                     subghz->state_notifications = SubGhzNotificationStateRX; |                     subghz->state_notifications = SubGhzNotificationStateRx; | ||||||
|                     subghz->txrx->rx_key_state = SubGhzRxKeyStateAddKey; |                     subghz->txrx->rx_key_state = SubGhzRxKeyStateAddKey; | ||||||
|                 } else { |                 } else { | ||||||
|                     string_set(subghz->error_str, "Function requires\nan SD card."); |                     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) { |     } else if(event.type == SceneManagerEventTypeTick) { | ||||||
|         switch(subghz->state_notifications) { |         switch(subghz->state_notifications) { | ||||||
|         case SubGhzNotificationStateRX: |         case SubGhzNotificationStateRx: | ||||||
|             notification_message(subghz->notifications, &sequence_blink_blue_10); |             notification_message(subghz->notifications, &sequence_blink_cyan_10); | ||||||
|             subghz_read_raw_update_sample_write( |             subghz_read_raw_update_sample_write( | ||||||
|                 subghz->subghz_read_raw, |                 subghz->subghz_read_raw, | ||||||
|                 subghz_protocol_raw_get_sample_write( |                 subghz_protocol_raw_get_sample_write( | ||||||
|                     (SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result)); |                     (SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result)); | ||||||
|             subghz_read_raw_add_data_rssi(subghz->subghz_read_raw, furi_hal_subghz_get_rssi()); |             subghz_read_raw_add_data_rssi(subghz->subghz_read_raw, furi_hal_subghz_get_rssi()); | ||||||
|             break; |             break; | ||||||
|         case SubGhzNotificationStateTX: |         case SubGhzNotificationStateTx: | ||||||
|             notification_message(subghz->notifications, &sequence_blink_green_10); |             notification_message(subghz->notifications, &sequence_blink_magenta_10); | ||||||
|             subghz_read_raw_update_sin(subghz->subghz_read_raw); |             subghz_read_raw_update_sin(subghz->subghz_read_raw); | ||||||
|             break; |             break; | ||||||
|         default: |         default: | ||||||
| @ -316,6 +322,7 @@ void subghz_scene_read_raw_on_exit(void* context) { | |||||||
|         subghz_sleep(subghz); |         subghz_sleep(subghz); | ||||||
|     }; |     }; | ||||||
|     subghz->state_notifications = SubGhzNotificationStateIDLE; |     subghz->state_notifications = SubGhzNotificationStateIDLE; | ||||||
|  |     notification_message(subghz->notifications, &sequence_reset_rgb); | ||||||
| 
 | 
 | ||||||
|     //filter restoration
 |     //filter restoration
 | ||||||
|     subghz_receiver_set_filter(subghz->txrx->receiver, SubGhzProtocolFlag_Decodable); |     subghz_receiver_set_filter(subghz->txrx->receiver, SubGhzProtocolFlag_Decodable); | ||||||
|  | |||||||
| @ -1,6 +1,19 @@ | |||||||
| #include "../subghz_i.h" | #include "../subghz_i.h" | ||||||
| #include "../views/receiver.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) { | static void subghz_scene_receiver_update_statusbar(void* context) { | ||||||
|     SubGhz* subghz = context; |     SubGhz* subghz = context; | ||||||
|     string_t history_stat_str; |     string_t history_stat_str; | ||||||
| @ -50,6 +63,8 @@ static void subghz_scene_add_to_history_callback( | |||||||
|         subghz_receiver_reset(receiver); |         subghz_receiver_reset(receiver); | ||||||
|         string_reset(str_buff); |         string_reset(str_buff); | ||||||
| 
 | 
 | ||||||
|  |         subghz->state_notifications = SubGhzNotificationStateRxDone; | ||||||
|  | 
 | ||||||
|         subghz_history_get_text_item_menu( |         subghz_history_get_text_item_menu( | ||||||
|             subghz->txrx->history, str_buff, subghz_history_get_item(subghz->txrx->history) - 1); |             subghz->txrx->history, str_buff, subghz_history_get_item(subghz->txrx->history) - 1); | ||||||
|         subghz_view_receiver_add_item_to_menu( |         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_receiver_set_rx_callback( | ||||||
|         subghz->txrx->receiver, subghz_scene_add_to_history_callback, subghz); |         subghz->txrx->receiver, subghz_scene_add_to_history_callback, subghz); | ||||||
| 
 | 
 | ||||||
|     subghz->state_notifications = SubGhzNotificationStateRX; |     subghz->state_notifications = SubGhzNotificationStateRx; | ||||||
|     if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { |     if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { | ||||||
|         subghz_rx_end(subghz); |         subghz_rx_end(subghz); | ||||||
|     }; |     }; | ||||||
| @ -161,8 +176,12 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) { | |||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         switch(subghz->state_notifications) { |         switch(subghz->state_notifications) { | ||||||
|         case SubGhzNotificationStateRX: |         case SubGhzNotificationStateRx: | ||||||
|             notification_message(subghz->notifications, &sequence_blink_blue_10); |             notification_message(subghz->notifications, &sequence_blink_cyan_10); | ||||||
|  |             break; | ||||||
|  |         case SubGhzNotificationStateRxDone: | ||||||
|  |             notification_message(subghz->notifications, &subghs_sequence_rx); | ||||||
|  |             subghz->state_notifications = SubGhzNotificationStateRx; | ||||||
|             break; |             break; | ||||||
|         default: |         default: | ||||||
|             break; |             break; | ||||||
|  | |||||||
| @ -126,7 +126,7 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event) | |||||||
|                            subghz->txrx->history, subghz->txrx->idx_menu_chosen))) { |                            subghz->txrx->history, subghz->txrx->idx_menu_chosen))) { | ||||||
|                     scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx); |                     scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx); | ||||||
|                 } else { |                 } else { | ||||||
|                     subghz->state_notifications = SubGhzNotificationStateTX; |                     subghz->state_notifications = SubGhzNotificationStateTx; | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|             return true; |             return true; | ||||||
| @ -143,7 +143,7 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event) | |||||||
|             if(subghz->txrx->hopper_state == SubGhzHopperStatePause) { |             if(subghz->txrx->hopper_state == SubGhzHopperStatePause) { | ||||||
|                 subghz->txrx->hopper_state = SubGhzHopperStateRunnig; |                 subghz->txrx->hopper_state = SubGhzHopperStateRunnig; | ||||||
|             } |             } | ||||||
|             subghz->state_notifications = SubGhzNotificationStateRX; |             subghz->state_notifications = SubGhzNotificationStateRx; | ||||||
|             return true; |             return true; | ||||||
|         } else if(event.event == SubGhzCustomEventSceneReceiverInfoSave) { |         } else if(event.event == SubGhzCustomEventSceneReceiverInfoSave) { | ||||||
|             //CC1101 Stop RX -> Save
 |             //CC1101 Stop RX -> Save
 | ||||||
| @ -171,11 +171,15 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event) | |||||||
|             subghz_hopper_update(subghz); |             subghz_hopper_update(subghz); | ||||||
|         } |         } | ||||||
|         switch(subghz->state_notifications) { |         switch(subghz->state_notifications) { | ||||||
|         case SubGhzNotificationStateTX: |         case SubGhzNotificationStateTx: | ||||||
|             notification_message(subghz->notifications, &sequence_blink_red_10); |             notification_message(subghz->notifications, &sequence_blink_magenta_10); | ||||||
|             break; |             break; | ||||||
|         case SubGhzNotificationStateRX: |         case SubGhzNotificationStateRx: | ||||||
|             notification_message(subghz->notifications, &sequence_blink_blue_10); |             notification_message(subghz->notifications, &sequence_blink_cyan_10); | ||||||
|  |             break; | ||||||
|  |         case SubGhzNotificationStateRxDone: | ||||||
|  |             notification_message(subghz->notifications, &sequence_blink_green_100); | ||||||
|  |             subghz->state_notifications = SubGhzNotificationStateRx; | ||||||
|             break; |             break; | ||||||
|         default: |         default: | ||||||
|             break; |             break; | ||||||
|  | |||||||
| @ -4,6 +4,7 @@ | |||||||
| #include <dolphin/dolphin.h> | #include <dolphin/dolphin.h> | ||||||
| #include <flipper_format/flipper_format_i.h> | #include <flipper_format/flipper_format_i.h> | ||||||
| #include <lib/toolbox/stream/stream.h> | #include <lib/toolbox/stream/stream.h> | ||||||
|  | #include <lib/subghz/protocols/registry.h> | ||||||
| 
 | 
 | ||||||
| #define TAG "SubGhzSetType" | #define TAG "SubGhzSetType" | ||||||
| 
 | 
 | ||||||
| @ -19,13 +20,16 @@ enum SubmenuIndex { | |||||||
|     SubmenuIndexGateTX, |     SubmenuIndexGateTX, | ||||||
|     SubmenuIndexDoorHan_315_00, |     SubmenuIndexDoorHan_315_00, | ||||||
|     SubmenuIndexDoorHan_433_92, |     SubmenuIndexDoorHan_433_92, | ||||||
|  |     SubmenuIndexFirefly_300_00, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| bool subghz_scene_set_type_submenu_gen_data_protocol( | bool subghz_scene_set_type_submenu_gen_data_protocol( | ||||||
|     void* context, |     void* context, | ||||||
|     const char* protocol_name, |     const char* protocol_name, | ||||||
|     uint64_t key, |     uint64_t key, | ||||||
|     uint32_t bit) { |     uint32_t bit, | ||||||
|  |     uint32_t frequency, | ||||||
|  |     FuriHalSubGhzPreset preset) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     SubGhz* subghz = 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* fff_data_stream = flipper_format_get_raw_stream(subghz->txrx->fff_data); | ||||||
|         stream_clean(fff_data_stream); |         stream_clean(fff_data_stream); | ||||||
|         if(!subghz_protocol_decoder_base_serialize( |         if(!subghz_protocol_decoder_base_serialize( | ||||||
|                subghz->txrx->decoder_result, |                subghz->txrx->decoder_result, subghz->txrx->fff_data, frequency, preset)) { | ||||||
|                subghz->txrx->fff_data, |  | ||||||
|                subghz_setting_get_default_frequency(subghz->setting), |  | ||||||
|                FuriHalSubGhzPresetOok650Async)) { |  | ||||||
|             FURI_LOG_E(TAG, "Unable to serialize"); |             FURI_LOG_E(TAG, "Unable to serialize"); | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
| @ -107,6 +108,12 @@ void subghz_scene_set_type_on_enter(void* context) { | |||||||
|         SubmenuIndexCAME24bit, |         SubmenuIndexCAME24bit, | ||||||
|         subghz_scene_set_type_submenu_callback, |         subghz_scene_set_type_submenu_callback, | ||||||
|         subghz); |         subghz); | ||||||
|  |     submenu_add_item( | ||||||
|  |         subghz->submenu, | ||||||
|  |         "Firefly_300", | ||||||
|  |         SubmenuIndexFirefly_300_00, | ||||||
|  |         subghz_scene_set_type_submenu_callback, | ||||||
|  |         subghz); | ||||||
|     submenu_add_item( |     submenu_add_item( | ||||||
|         subghz->submenu, |         subghz->submenu, | ||||||
|         "CAME TWEE", |         "CAME TWEE", | ||||||
| @ -152,7 +159,13 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { | |||||||
|         switch(event.event) { |         switch(event.event) { | ||||||
|         case SubmenuIndexPricenton: |         case SubmenuIndexPricenton: | ||||||
|             key = (key & 0x00FFFFF0) | 0x4; //btn 0x1, 0x2, 0x4, 0x8
 |             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; |                 uint32_t te = 400; | ||||||
|                 flipper_format_update_uint32(subghz->txrx->fff_data, "TE", (uint32_t*)&te, 1); |                 flipper_format_update_uint32(subghz->txrx->fff_data, "TE", (uint32_t*)&te, 1); | ||||||
|                 generated_protocol = true; |                 generated_protocol = true; | ||||||
| @ -160,32 +173,74 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { | |||||||
|             break; |             break; | ||||||
|         case SubmenuIndexNiceFlo12bit: |         case SubmenuIndexNiceFlo12bit: | ||||||
|             key = (key & 0x0000FFF0) | 0x1; //btn 0x1, 0x2, 0x4
 |             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; |                 generated_protocol = true; | ||||||
|             } |             } | ||||||
|             break; |             break; | ||||||
|         case SubmenuIndexNiceFlo24bit: |         case SubmenuIndexNiceFlo24bit: | ||||||
|             key = (key & 0x00FFFFF0) | 0x4; //btn 0x1, 0x2, 0x4, 0x8
 |             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; |                 generated_protocol = true; | ||||||
|             } |             } | ||||||
|             break; |             break; | ||||||
|         case SubmenuIndexCAME12bit: |         case SubmenuIndexCAME12bit: | ||||||
|             key = (key & 0x0000FFF0) | 0x1; //btn 0x1, 0x2, 0x4
 |             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; |                 generated_protocol = true; | ||||||
|             } |             } | ||||||
|             break; |             break; | ||||||
|         case SubmenuIndexCAME24bit: |         case SubmenuIndexCAME24bit: | ||||||
|             key = (key & 0x00FFFFF0) | 0x4; //btn 0x1, 0x2, 0x4, 0x8
 |             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; |                 generated_protocol = true; | ||||||
|             } |             } | ||||||
|             break; |             break; | ||||||
|         case SubmenuIndexCAMETwee: |         case SubmenuIndexCAMETwee: | ||||||
|             key = (key & 0x0FFFFFF0); |             key = (key & 0x0FFFFFF0); | ||||||
|             key = 0x003FFF7200000000 | (key ^ 0xE0E0E0EE); |             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; |                 generated_protocol = true; | ||||||
|             } |             } | ||||||
|             break; |             break; | ||||||
| @ -198,13 +253,19 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { | |||||||
|         case SubmenuIndexGateTX: |         case SubmenuIndexGateTX: | ||||||
|             key = (key & 0x00F0FF00) | 0xF << 16 | 0x40; //btn 0xF, 0xC, 0xA, 0x6 (?)
 |             key = (key & 0x00F0FF00) | 0xF << 16 | 0x40; //btn 0xF, 0xC, 0xA, 0x6 (?)
 | ||||||
|             uint64_t rev_key = subghz_protocol_blocks_reverse_key(key, 24); |             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; |                 generated_protocol = true; | ||||||
|             } |             } | ||||||
|             break; |             break; | ||||||
|         case SubmenuIndexDoorHan_433_92: |         case SubmenuIndexDoorHan_433_92: | ||||||
|             subghz->txrx->transmitter = |             subghz->txrx->transmitter = subghz_transmitter_alloc_init( | ||||||
|                 subghz_transmitter_alloc_init(subghz->txrx->environment, "KeeLoq"); |                 subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME); | ||||||
|             if(subghz->txrx->transmitter) { |             if(subghz->txrx->transmitter) { | ||||||
|                 subghz_protocol_keeloq_create_data( |                 subghz_protocol_keeloq_create_data( | ||||||
|                     subghz->txrx->transmitter->protocol_instance, |                     subghz->txrx->transmitter->protocol_instance, | ||||||
| @ -227,8 +288,8 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { | |||||||
|             } |             } | ||||||
|             break; |             break; | ||||||
|         case SubmenuIndexDoorHan_315_00: |         case SubmenuIndexDoorHan_315_00: | ||||||
|             subghz->txrx->transmitter = |             subghz->txrx->transmitter = subghz_transmitter_alloc_init( | ||||||
|                 subghz_transmitter_alloc_init(subghz->txrx->environment, "KeeLoq"); |                 subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME); | ||||||
|             if(subghz->txrx->transmitter) { |             if(subghz->txrx->transmitter) { | ||||||
|                 subghz_protocol_keeloq_create_data( |                 subghz_protocol_keeloq_create_data( | ||||||
|                     subghz->txrx->transmitter->protocol_instance, |                     subghz->txrx->transmitter->protocol_instance, | ||||||
|  | |||||||
| @ -1,6 +1,13 @@ | |||||||
| #include "../subghz_i.h" | #include "../subghz_i.h" | ||||||
| #include "../helpers/subghz_custom_event.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) { | void subghz_scene_show_error_callback(GuiButtonType result, InputType type, void* context) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     SubGhz* subghz = context; |     SubGhz* subghz = context; | ||||||
| @ -31,6 +38,8 @@ void subghz_scene_show_error_on_enter(void* context) { | |||||||
|        SubGhzCustomEventManagerSet) { |        SubGhzCustomEventManagerSet) { | ||||||
|         widget_add_button_element( |         widget_add_button_element( | ||||||
|             subghz->widget, GuiButtonTypeRight, "Ok", subghz_scene_show_error_callback, subghz); |             subghz->widget, GuiButtonTypeRight, "Ok", subghz_scene_show_error_callback, subghz); | ||||||
|  |     } else { | ||||||
|  |         notification_message(subghz->notifications, &subghs_sequence_sd_error); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     widget_add_button_element( |     widget_add_button_element( | ||||||
| @ -81,4 +90,5 @@ void subghz_scene_show_error_on_exit(void* context) { | |||||||
|         subghz->scene_manager, SubGhzSceneShowError, SubGhzCustomEventManagerNoSet); |         subghz->scene_manager, SubGhzSceneShowError, SubGhzCustomEventManagerNoSet); | ||||||
|     widget_reset(subghz->widget); |     widget_reset(subghz->widget); | ||||||
|     string_reset(subghz->error_str); |     string_reset(subghz->error_str); | ||||||
|  |     notification_message(subghz->notifications, &sequence_reset_rgb); | ||||||
| } | } | ||||||
|  | |||||||
| @ -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_set_callback(popup, subghz_scene_show_error_sub_popup_callback); | ||||||
|     popup_enable_timeout(popup); |     popup_enable_timeout(popup); | ||||||
|     view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdPopup); |     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) { | 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_set_timeout(popup, 0); | ||||||
|     popup_disable_timeout(popup); |     popup_disable_timeout(popup); | ||||||
|     string_reset(subghz->error_str); |     string_reset(subghz->error_str); | ||||||
|  | 
 | ||||||
|  |     notification_message(subghz->notifications, &sequence_reset_rgb); | ||||||
| } | } | ||||||
|  | |||||||
| @ -76,7 +76,7 @@ bool subghz_scene_transmitter_on_event(void* context, SceneManagerEvent event) { | |||||||
|                 if(!subghz_tx_start(subghz, subghz->txrx->fff_data)) { |                 if(!subghz_tx_start(subghz, subghz->txrx->fff_data)) { | ||||||
|                     scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx); |                     scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx); | ||||||
|                 } else { |                 } else { | ||||||
|                     subghz->state_notifications = SubGhzNotificationStateTX; |                     subghz->state_notifications = SubGhzNotificationStateTx; | ||||||
|                     subghz_scene_transmitter_update_data_show(subghz); |                     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); |             scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowErrorSub); | ||||||
|         } |         } | ||||||
|     } else if(event.type == SceneManagerEventTypeTick) { |     } else if(event.type == SceneManagerEventTypeTick) { | ||||||
|         if(subghz->state_notifications == SubGhzNotificationStateTX) { |         if(subghz->state_notifications == SubGhzNotificationStateTx) { | ||||||
|             notification_message(subghz->notifications, &sequence_blink_red_10); |             notification_message(subghz->notifications, &sequence_blink_magenta_10); | ||||||
|         } |         } | ||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -525,7 +525,7 @@ static void subghz_cli_command_chat(Cli* cli, string_t args) { | |||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     SubGhzChatWorker* subghz_chat = subghz_chat_worker_alloc(); |     SubGhzChatWorker* subghz_chat = subghz_chat_worker_alloc(cli); | ||||||
|     if(!subghz_chat_worker_start(subghz_chat, frequency)) { |     if(!subghz_chat_worker_start(subghz_chat, frequency)) { | ||||||
|         printf("Startup error SubGhzChatWorker\r\n"); |         printf("Startup error SubGhzChatWorker\r\n"); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -41,8 +41,9 @@ | |||||||
| typedef enum { | typedef enum { | ||||||
|     SubGhzNotificationStateStarting, |     SubGhzNotificationStateStarting, | ||||||
|     SubGhzNotificationStateIDLE, |     SubGhzNotificationStateIDLE, | ||||||
|     SubGhzNotificationStateTX, |     SubGhzNotificationStateTx, | ||||||
|     SubGhzNotificationStateRX, |     SubGhzNotificationStateRx, | ||||||
|  |     SubGhzNotificationStateRxDone, | ||||||
| } SubGhzNotificationState; | } SubGhzNotificationState; | ||||||
| 
 | 
 | ||||||
| /** SubGhzTxRx state */ | /** SubGhzTxRx state */ | ||||||
|  | |||||||
| @ -19,6 +19,7 @@ struct SubGhzFrequencyAnalyzer { | |||||||
|     SubGhzFrequencyAnalyzerWorker* worker; |     SubGhzFrequencyAnalyzerWorker* worker; | ||||||
|     SubGhzFrequencyAnalyzerCallback callback; |     SubGhzFrequencyAnalyzerCallback callback; | ||||||
|     void* context; |     void* context; | ||||||
|  |     bool locked; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| typedef struct { | 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) { | void subghz_frequency_analyzer_pair_callback(void* context, uint32_t frequency, float rssi) { | ||||||
|     SubGhzFrequencyAnalyzer* instance = context; |     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( |     with_view_model( | ||||||
|         instance->view, (SubGhzFrequencyAnalyzerModel * model) { |         instance->view, (SubGhzFrequencyAnalyzerModel * model) { | ||||||
|             model->rssi = rssi; |             model->rssi = rssi; | ||||||
|  | |||||||
| @ -293,7 +293,6 @@ bool subghz_read_raw_input(InputEvent* event, void* context) { | |||||||
|                 case SubGhzReadRAWStatusIDLE: |                 case SubGhzReadRAWStatusIDLE: | ||||||
|                     // Start TX
 |                     // Start TX
 | ||||||
|                     instance->callback(SubGhzCustomEventViewReadRAWSendStart, instance->context); |                     instance->callback(SubGhzCustomEventViewReadRAWSendStart, instance->context); | ||||||
|                     instance->callback(SubGhzCustomEventViewReadRAWVibro, instance->context); |  | ||||||
|                     model->satus = SubGhzReadRAWStatusTXRepeat; |                     model->satus = SubGhzReadRAWStatusTXRepeat; | ||||||
|                     ret = true; |                     ret = true; | ||||||
|                     break; |                     break; | ||||||
| @ -304,7 +303,6 @@ bool subghz_read_raw_input(InputEvent* event, void* context) { | |||||||
|                 case SubGhzReadRAWStatusLoadKeyIDLE: |                 case SubGhzReadRAWStatusLoadKeyIDLE: | ||||||
|                     // Start Load Key TX
 |                     // Start Load Key TX
 | ||||||
|                     instance->callback(SubGhzCustomEventViewReadRAWSendStart, instance->context); |                     instance->callback(SubGhzCustomEventViewReadRAWSendStart, instance->context); | ||||||
|                     instance->callback(SubGhzCustomEventViewReadRAWVibro, instance->context); |  | ||||||
|                     model->satus = SubGhzReadRAWStatusLoadKeyTXRepeat; |                     model->satus = SubGhzReadRAWStatusLoadKeyTXRepeat; | ||||||
|                     ret = true; |                     ret = true; | ||||||
|                     break; |                     break; | ||||||
|  | |||||||
| @ -59,18 +59,20 @@ bool u2f_scene_main_on_event(void* context, SceneManagerEvent event) { | |||||||
|                     u2f_view_set_state(app->u2f_view, U2fMsgRegister); |                     u2f_view_set_state(app->u2f_view, U2fMsgRegister); | ||||||
|                 else if(event.event == U2fCustomEventAuth) |                 else if(event.event == U2fCustomEventAuth) | ||||||
|                     u2f_view_set_state(app->u2f_view, U2fMsgAuth); |                     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_single_vibro); | ||||||
|             } |             } | ||||||
|             notification_message(app->notifications, &sequence_blink_blue_10); |             notification_message(app->notifications, &sequence_blink_magenta_10); | ||||||
|         } else if(event.event == U2fCustomEventWink) { |         } 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) { |         } else if(event.event == U2fCustomEventAuthSuccess) { | ||||||
|  |             notification_message_block(app->notifications, &sequence_set_green_255); | ||||||
|             DOLPHIN_DEED(DolphinDeedU2fAuthorized); |             DOLPHIN_DEED(DolphinDeedU2fAuthorized); | ||||||
|             osTimerStart(app->timer, U2F_SUCCESS_TIMEOUT); |             osTimerStart(app->timer, U2F_SUCCESS_TIMEOUT); | ||||||
|             app->event_cur = U2fCustomEventNone; |             app->event_cur = U2fCustomEventNone; | ||||||
|             u2f_view_set_state(app->u2f_view, U2fMsgSuccess); |             u2f_view_set_state(app->u2f_view, U2fMsgSuccess); | ||||||
|         } else if(event.event == U2fCustomEventTimeout) { |         } else if(event.event == U2fCustomEventTimeout) { | ||||||
|  |             notification_message_block(app->notifications, &sequence_reset_rgb); | ||||||
|             app->event_cur = U2fCustomEventNone; |             app->event_cur = U2fCustomEventNone; | ||||||
|             u2f_view_set_state(app->u2f_view, U2fMsgIdle); |             u2f_view_set_state(app->u2f_view, U2fMsgIdle); | ||||||
|         } else if(event.event == U2fCustomEventConfirm) { |         } 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); |                 u2f_confirm_user_present(app->u2f_instance); | ||||||
|             } |             } | ||||||
|         } else if(event.event == U2fCustomEventDataError) { |         } else if(event.event == U2fCustomEventDataError) { | ||||||
|  |             notification_message(app->notifications, &sequence_set_red_255); | ||||||
|             osTimerStop(app->timer); |             osTimerStop(app->timer); | ||||||
|             u2f_view_set_state(app->u2f_view, U2fMsgError); |             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) { | void u2f_scene_main_on_exit(void* context) { | ||||||
|     U2fApp* app = context; |     U2fApp* app = context; | ||||||
|  |     notification_message_block(app->notifications, &sequence_reset_rgb); | ||||||
|     osTimerStop(app->timer); |     osTimerStop(app->timer); | ||||||
|     osTimerDelete(app->timer); |     osTimerDelete(app->timer); | ||||||
|     if(app->u2f_ready == true) { |     if(app->u2f_ready == true) { | ||||||
|  | |||||||
| @ -25,6 +25,7 @@ static void sd_mount_callback(const void* message, void* context) { | |||||||
| 
 | 
 | ||||||
| void updater_scene_main_on_enter(void* context) { | void updater_scene_main_on_enter(void* context) { | ||||||
|     Updater* updater = (Updater*)context; |     Updater* updater = (Updater*)context; | ||||||
|  |     notification_message(updater->notification, &sequence_display_backlight_enforce_on); | ||||||
|     UpdaterMainView* main_view = updater->main_view; |     UpdaterMainView* main_view = updater->main_view; | ||||||
| 
 | 
 | ||||||
|     FuriPubSubSubscription* sub = |     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 |     * will be missing from UI, however, /ext will be fully operational. So, until it's fixed, this | ||||||
|     * should remain commented out. */ |     * should remain commented out. */ | ||||||
|     // If (somehow) we started after SD card is mounted, initiate update immediately
 |     // If (somehow) we started after SD card is mounted, initiate update immediately
 | ||||||
|     //if(storage_sd_status(updater->storage) == FSE_OK) {
 |     if(storage_sd_status(updater->storage) == FSE_OK) { | ||||||
|     //    view_dispatcher_send_custom_event(updater->view_dispatcher, UpdaterCustomEventStartUpdate);
 |         view_dispatcher_send_custom_event(updater->view_dispatcher, UpdaterCustomEventStartUpdate); | ||||||
|     //}
 |     } | ||||||
| 
 | 
 | ||||||
|     updater_main_set_view_dispatcher(main_view, updater->view_dispatcher); |     updater_main_set_view_dispatcher(main_view, updater->view_dispatcher); | ||||||
|     view_dispatcher_switch_to_view(updater->view_dispatcher, UpdaterViewMain); |     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) { |     } else if(event.type == SceneManagerEventTypeCustom) { | ||||||
|         switch(event.event) { |         switch(event.event) { | ||||||
|         case UpdaterCustomEventStartUpdate: |         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: |         case UpdaterCustomEventRetryUpdate: | ||||||
|             if(!update_task_is_running(updater->update_task) && |             if(!update_task_is_running(updater->update_task) && | ||||||
|                (update_task_get_state(updater->update_task)->stage != UpdateTaskStageCompleted)) |                (update_task_get_state(updater->update_task)->stage != UpdateTaskStageCompleted)) | ||||||
| @ -99,6 +93,7 @@ bool updater_scene_main_on_event(void* context, SceneManagerEvent event) { | |||||||
| void updater_scene_main_on_exit(void* context) { | void updater_scene_main_on_exit(void* context) { | ||||||
|     Updater* updater = (Updater*)context; |     Updater* updater = (Updater*)context; | ||||||
| 
 | 
 | ||||||
|  |     notification_message(updater->notification, &sequence_display_backlight_enforce_auto); | ||||||
|     furi_pubsub_unsubscribe( |     furi_pubsub_unsubscribe( | ||||||
|         storage_get_pubsub(updater->storage), updater_main_get_storage_pubsub(updater->main_view)); |         storage_get_pubsub(updater->storage), updater_main_get_storage_pubsub(updater->main_view)); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -47,6 +47,7 @@ Updater* updater_alloc(const char* arg) { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     updater->storage = furi_record_open("storage"); |     updater->storage = furi_record_open("storage"); | ||||||
|  |     updater->notification = furi_record_open("notification"); | ||||||
| 
 | 
 | ||||||
|     updater->gui = furi_record_open("gui"); |     updater->gui = furi_record_open("gui"); | ||||||
|     updater->view_dispatcher = view_dispatcher_alloc(); |     updater->view_dispatcher = view_dispatcher_alloc(); | ||||||
| @ -119,6 +120,7 @@ void updater_free(Updater* updater) { | |||||||
| 
 | 
 | ||||||
|     furi_record_close("gui"); |     furi_record_close("gui"); | ||||||
|     furi_record_close("storage"); |     furi_record_close("storage"); | ||||||
|  |     furi_record_close("notification"); | ||||||
| 
 | 
 | ||||||
|     free(updater); |     free(updater); | ||||||
| } | } | ||||||
|  | |||||||
| @ -11,6 +11,7 @@ | |||||||
| #include <gui/scene_manager.h> | #include <gui/scene_manager.h> | ||||||
| #include <gui/modules/widget.h> | #include <gui/modules/widget.h> | ||||||
| #include <storage/storage.h> | #include <storage/storage.h> | ||||||
|  | #include <notification/notification_app.h> | ||||||
| #include <update_util/update_operation.h> | #include <update_util/update_operation.h> | ||||||
| 
 | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| @ -41,6 +42,7 @@ typedef struct UpdaterManifestProcessingState { | |||||||
| typedef struct { | typedef struct { | ||||||
|     // GUI
 |     // GUI
 | ||||||
|     Gui* gui; |     Gui* gui; | ||||||
|  |     NotificationApp* notification; | ||||||
|     SceneManager* scene_manager; |     SceneManager* scene_manager; | ||||||
|     ViewDispatcher* view_dispatcher; |     ViewDispatcher* view_dispatcher; | ||||||
|     Storage* storage; |     Storage* storage; | ||||||
|  | |||||||
| @ -15,13 +15,18 @@ static const char* update_task_stage_descr[] = { | |||||||
|     [UpdateTaskStageValidateDFUImage] = "Checking DFU file", |     [UpdateTaskStageValidateDFUImage] = "Checking DFU file", | ||||||
|     [UpdateTaskStageFlashWrite] = "Writing flash", |     [UpdateTaskStageFlashWrite] = "Writing flash", | ||||||
|     [UpdateTaskStageFlashValidate] = "Validating", |     [UpdateTaskStageFlashValidate] = "Validating", | ||||||
|  |     [UpdateTaskStageRadioImageValidate] = "Checking radio image", | ||||||
|  |     [UpdateTaskStageRadioErase] = "Removing radio stack", | ||||||
|     [UpdateTaskStageRadioWrite] = "Writing 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", |     [UpdateTaskStageLfsBackup] = "Backing up LFS", | ||||||
|     [UpdateTaskStageLfsRestore] = "Restoring LFS", |     [UpdateTaskStageLfsRestore] = "Restoring LFS", | ||||||
|     [UpdateTaskStageResourcesUpdate] = "Updating resources", |     [UpdateTaskStageResourcesUpdate] = "Updating resources", | ||||||
|     [UpdateTaskStageCompleted] = "Completed!", |     [UpdateTaskStageCompleted] = "Completed!", | ||||||
|     [UpdateTaskStageError] = "Error", |     [UpdateTaskStageError] = "Error", | ||||||
|  |     [UpdateTaskStageOBError] = "OB error, pls report", | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static void update_task_set_status(UpdateTask* update_task, const char* status) { | 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) { | void update_task_set_progress(UpdateTask* update_task, UpdateTaskStage stage, uint8_t progress) { | ||||||
|     if(stage != UpdateTaskStageProgress) { |     if(stage != UpdateTaskStageProgress) { | ||||||
|  |         // do not override more specific error states
 | ||||||
|  |         if((update_task->state.stage < UpdateTaskStageError) || (stage < UpdateTaskStageError)) { | ||||||
|             update_task->state.stage = stage; |             update_task->state.stage = stage; | ||||||
|  |         } | ||||||
|         update_task->state.current_stage_idx++; |         update_task->state.current_stage_idx++; | ||||||
|         update_task_set_status(update_task, NULL); |         update_task_set_status(update_task, NULL); | ||||||
|     } |     } | ||||||
| @ -53,7 +61,7 @@ void update_task_set_progress(UpdateTask* update_task, UpdateTaskStage stage, ui | |||||||
|             progress, |             progress, | ||||||
|             update_task->state.current_stage_idx, |             update_task->state.current_stage_idx, | ||||||
|             update_task->state.total_stages, |             update_task->state.total_stages, | ||||||
|             update_task->state.stage == UpdateTaskStageError, |             update_task->state.stage >= UpdateTaskStageError, | ||||||
|             update_task->status_change_cb_state); |             update_task->status_change_cb_state); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @ -116,6 +124,7 @@ UpdateTask* update_task_alloc() { | |||||||
|     update_task->storage = furi_record_open("storage"); |     update_task->storage = furi_record_open("storage"); | ||||||
|     update_task->file = storage_file_alloc(update_task->storage); |     update_task->file = storage_file_alloc(update_task->storage); | ||||||
|     update_task->status_change_cb = NULL; |     update_task->status_change_cb = NULL; | ||||||
|  |     string_init(update_task->update_path); | ||||||
| 
 | 
 | ||||||
|     FuriThread* thread = update_task->thread = furi_thread_alloc(); |     FuriThread* thread = update_task->thread = furi_thread_alloc(); | ||||||
| 
 | 
 | ||||||
| @ -152,12 +161,6 @@ void update_task_free(UpdateTask* update_task) { | |||||||
|     free(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) { | bool update_task_parse_manifest(UpdateTask* update_task) { | ||||||
|     furi_assert(update_task); |     furi_assert(update_task); | ||||||
|     update_task_set_progress(update_task, UpdateTaskStageReadManifest, 0); |     update_task_set_progress(update_task, UpdateTaskStageReadManifest, 0); | ||||||
|  | |||||||
| @ -19,13 +19,18 @@ typedef enum { | |||||||
|     UpdateTaskStageValidateDFUImage, |     UpdateTaskStageValidateDFUImage, | ||||||
|     UpdateTaskStageFlashWrite, |     UpdateTaskStageFlashWrite, | ||||||
|     UpdateTaskStageFlashValidate, |     UpdateTaskStageFlashValidate, | ||||||
|  |     UpdateTaskStageRadioImageValidate, | ||||||
|  |     UpdateTaskStageRadioErase, | ||||||
|     UpdateTaskStageRadioWrite, |     UpdateTaskStageRadioWrite, | ||||||
|     UpdateTaskStageRadioCommit, |     UpdateTaskStageRadioInstall, | ||||||
|  |     UpdateTaskStageRadioBusy, | ||||||
|  |     UpdateTaskStageOBValidation, | ||||||
|     UpdateTaskStageLfsBackup, |     UpdateTaskStageLfsBackup, | ||||||
|     UpdateTaskStageLfsRestore, |     UpdateTaskStageLfsRestore, | ||||||
|     UpdateTaskStageResourcesUpdate, |     UpdateTaskStageResourcesUpdate, | ||||||
|     UpdateTaskStageCompleted, |     UpdateTaskStageCompleted, | ||||||
|     UpdateTaskStageError, |     UpdateTaskStageError, | ||||||
|  |     UpdateTaskStageOBError | ||||||
| } UpdateTaskStage; | } UpdateTaskStage; | ||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
| @ -50,8 +55,6 @@ UpdateTask* update_task_alloc(); | |||||||
| 
 | 
 | ||||||
| void update_task_free(UpdateTask* update_task); | 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); | void update_task_set_progress_cb(UpdateTask* update_task, updateProgressCb cb, void* state); | ||||||
| 
 | 
 | ||||||
| bool update_task_start(UpdateTask* update_task); | bool update_task_start(UpdateTask* update_task); | ||||||
|  | |||||||
| @ -9,99 +9,17 @@ | |||||||
| #include <update_util/lfs_backup.h> | #include <update_util/lfs_backup.h> | ||||||
| #include <update_util/update_operation.h> | #include <update_util/update_operation.h> | ||||||
| #include <toolbox/tar/tar_archive.h> | #include <toolbox/tar/tar_archive.h> | ||||||
|  | #include <toolbox/crc32_calc.h> | ||||||
|  | 
 | ||||||
|  | #define TAG "UpdWorkerBackup" | ||||||
| 
 | 
 | ||||||
| #define CHECK_RESULT(x) \ | #define CHECK_RESULT(x) \ | ||||||
|     if(!(x)) {          \ |     if(!(x)) {          \ | ||||||
|         break;          \ |         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" | #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) { | static bool update_task_pre_update(UpdateTask* update_task) { | ||||||
|     bool success = false; |     bool success = false; | ||||||
|     string_t backup_file_path; |     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->state.total_stages = 1; | ||||||
|     update_task_set_progress(update_task, UpdateTaskStageLfsBackup, 0); |     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)))) { |     if((success = lfs_backup_create(update_task->storage, string_get_cstr(backup_file_path)))) { | ||||||
|         furi_hal_rtc_set_boot_mode(FuriHalRtcBootModeUpdate); |         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_t file_path; | ||||||
|     string_init(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 { |     do { | ||||||
|         CHECK_RESULT(update_task_parse_manifest(update_task)); |         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); |             string_get_cstr(update_task->update_path), LFS_BACKUP_DEFAULT_FILENAME, file_path); | ||||||
| 
 | 
 | ||||||
|         bool unpack_resources = !string_empty_p(update_task->manifest->resource_bundle); |         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); |         update_task_set_progress(update_task, UpdateTaskStageLfsRestore, 0); | ||||||
|         furi_hal_rtc_set_boot_mode(FuriHalRtcBootModeNormal); |         furi_hal_rtc_set_boot_mode(FuriHalRtcBootModeNormal); | ||||||
| @ -184,6 +101,7 @@ static bool update_task_post_update(UpdateTask* update_task) { | |||||||
|             } |             } | ||||||
|             tar_archive_free(archive); |             tar_archive_free(archive); | ||||||
|         } |         } | ||||||
|  |         success = true; | ||||||
|     } while(false); |     } while(false); | ||||||
| 
 | 
 | ||||||
|     string_clear(file_path); |     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(); |     FuriHalRtcBootMode boot_mode = furi_hal_rtc_get_boot_mode(); | ||||||
|     if((boot_mode != FuriHalRtcBootModePreUpdate) && (boot_mode != FuriHalRtcBootModePostUpdate)) { |     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); |         furi_hal_rtc_set_boot_mode(FuriHalRtcBootModeNormal); | ||||||
|         return UPDATE_TASK_NOERR; |         return UPDATE_TASK_NOERR; | ||||||
|     } |     } | ||||||
| @ -208,12 +126,17 @@ int32_t update_task_worker_backup_restore(void* context) { | |||||||
|         return UPDATE_TASK_FAILED; |         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) { |     if(boot_mode == FuriHalRtcBootModePreUpdate) { | ||||||
|         success = update_task_pre_update(update_task); |         success = update_task_pre_update(update_task); | ||||||
|     } else if(boot_mode == FuriHalRtcBootModePostUpdate) { |     } else if(boot_mode == FuriHalRtcBootModePostUpdate) { | ||||||
|         success = update_task_post_update(update_task); |         success = update_task_post_update(update_task); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     furi_record_close("bt"); | ||||||
|  | 
 | ||||||
|     if(success) { |     if(success) { | ||||||
|         update_task_set_progress(update_task, UpdateTaskStageCompleted, 100); |         update_task_set_progress(update_task, UpdateTaskStageCompleted, 100); | ||||||
|     } else { |     } else { | ||||||
							
								
								
									
										363
									
								
								applications/updater/util/update_task_worker_flasher.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										363
									
								
								applications/updater/util/update_task_worker_flasher.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,363 @@ | |||||||
|  | #include "update_task.h" | ||||||
|  | #include "update_task_i.h" | ||||||
|  | 
 | ||||||
|  | #include <furi.h> | ||||||
|  | #include <furi_hal.h> | ||||||
|  | #include <storage/storage.h> | ||||||
|  | #include <toolbox/path.h> | ||||||
|  | #include <update_util/dfu_file.h> | ||||||
|  | #include <update_util/lfs_backup.h> | ||||||
|  | #include <update_util/update_operation.h> | ||||||
|  | #include <toolbox/tar/tar_archive.h> | ||||||
|  | #include <toolbox/crc32_calc.h> | ||||||
|  | 
 | ||||||
|  | #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; | ||||||
|  | } | ||||||
							
								
								
									
										1
									
								
								assets/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								assets/.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -1 +1,2 @@ | |||||||
| /headers | /headers | ||||||
|  | /core2_firmware | ||||||
|  | |||||||
| @ -1,6 +1,7 @@ | |||||||
| PROJECT_ROOT		= $(abspath $(dir $(abspath $(firstword $(MAKEFILE_LIST))))..) | PROJECT_ROOT		= $(abspath $(dir $(abspath $(firstword $(MAKEFILE_LIST))))..) | ||||||
| 
 | 
 | ||||||
| include				$(PROJECT_ROOT)/assets/assets.mk | include				$(PROJECT_ROOT)/assets/assets.mk | ||||||
|  | include				$(PROJECT_ROOT)/assets/copro.mk | ||||||
| 
 | 
 | ||||||
| .PHONY: all | .PHONY: all | ||||||
| all: icons protobuf dolphin manifest | all: icons protobuf dolphin manifest | ||||||
| @ -35,7 +36,13 @@ manifest: | |||||||
| .PHONY: dolphin | .PHONY: dolphin | ||||||
| dolphin: $(DOLPHIN_EXTERNAL_OUTPUT_DIR) | 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: | clean: | ||||||
| 	@echo "\tCLEAN\t" | 	@echo "\tCLEAN\t" | ||||||
| 	@$(RM) $(ASSETS_COMPILED_DIR)/* | 	@$(RM) $(ASSETS_COMPILED_DIR)/* | ||||||
|  | 	@$(RM) -rf $(COPRO_BUNDLE_DIR) | ||||||
| 	@$(RM) -rf $(DOLPHIN_EXTERNAL_OUTPUT_DIR) | 	@$(RM) -rf $(DOLPHIN_EXTERNAL_OUTPUT_DIR) | ||||||
|  | |||||||
| @ -10,6 +10,13 @@ | |||||||
| make all | 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 | # Asset naming rules | ||||||
| 
 | 
 | ||||||
| ## Images and Animations | ## Images and Animations | ||||||
|  | |||||||
							
								
								
									
										12
									
								
								assets/copro.mk
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								assets/copro.mk
									
									
									
									
									
										Normal file
									
								
							| @ -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) | ||||||
| @ -1,5 +1,5 @@ | |||||||
| V:0 | V:0 | ||||||
| T:1650389893 | T:1651076680 | ||||||
| D:badusb | D:badusb | ||||||
| D:dolphin | D:dolphin | ||||||
| D:infrared | 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:f267f0654781049ca323b11bb4375519:581:dolphin/L3_Lab_research_128x54/frame_9.bm | ||||||
| F:41106c0cbc5144f151b2b2d3daaa0527:727:dolphin/L3_Lab_research_128x54/meta.txt | F:41106c0cbc5144f151b2b2d3daaa0527:727:dolphin/L3_Lab_research_128x54/meta.txt | ||||||
| D:infrared/assets | D:infrared/assets | ||||||
| F:5b16e1a59daf3ef1d0fc95b3b5596d67:74300:infrared/assets/tv.ir | F:d895fda2f48c6cc4c55e8a398ff52e43:74300:infrared/assets/tv.ir | ||||||
| D:nfc/assets | D:nfc/assets | ||||||
| F:c6826a621d081d68309e4be424d3d974:4715:nfc/assets/aid.nfc | F:c6826a621d081d68309e4be424d3d974:4715:nfc/assets/aid.nfc | ||||||
| F:86efbebdf41bb6bf15cc51ef88f069d5:2565:nfc/assets/country_code.nfc | F:86efbebdf41bb6bf15cc51ef88f069d5:2565:nfc/assets/country_code.nfc | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| Filetype: IR signals file | Filetype: IR library file | ||||||
| Version: 1 | Version: 1 | ||||||
| #  | #  | ||||||
| name: POWER | name: POWER | ||||||
|  | |||||||
| @ -7,7 +7,8 @@ | |||||||
| #include <flipper_format/flipper_format.h> | #include <flipper_format/flipper_format.h> | ||||||
| 
 | 
 | ||||||
| #include <update_util/update_manifest.h> | #include <update_util/update_manifest.h> | ||||||
| #include <lib/toolbox/path.h> | #include <toolbox/path.h> | ||||||
|  | #include <toolbox/crc32_calc.h> | ||||||
| 
 | 
 | ||||||
| static FATFS* pfs = NULL; | static FATFS* pfs = NULL; | ||||||
| 
 | 
 | ||||||
| @ -27,7 +28,6 @@ static bool flipper_update_init() { | |||||||
|     furi_hal_delay_init(); |     furi_hal_delay_init(); | ||||||
| 
 | 
 | ||||||
|     furi_hal_spi_init(); |     furi_hal_spi_init(); | ||||||
|     furi_hal_crc_init(false); |  | ||||||
| 
 | 
 | ||||||
|     MX_FATFS_Init(); |     MX_FATFS_Init(); | ||||||
|     if(!hal_sd_detect()) { |     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; |     uint32_t bytes_read = 0; | ||||||
|     const uint16_t MAX_READ = 0xFFFF; |     const uint16_t MAX_READ = 0xFFFF; | ||||||
| 
 | 
 | ||||||
|     furi_hal_crc_reset(); |  | ||||||
|     uint32_t crc = 0; |     uint32_t crc = 0; | ||||||
|     do { |     do { | ||||||
|         uint16_t size_read = 0; |         uint16_t size_read = 0; | ||||||
|         if(f_read(&file, img + bytes_read, MAX_READ, &size_read) != FR_OK) { |         if(f_read(&file, img + bytes_read, MAX_READ, &size_read) != FR_OK) { | ||||||
|             break; |             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; |         bytes_read += size_read; | ||||||
|     } while(bytes_read == MAX_READ); |     } while(bytes_read == MAX_READ); | ||||||
|     furi_hal_crc_reset(); |  | ||||||
| 
 | 
 | ||||||
|     do { |     do { | ||||||
|         if((bytes_read != stat.fsize) || (crc != manifest->staged_loader_crc)) { |         if((bytes_read != stat.fsize) || (crc != manifest->staged_loader_crc)) { | ||||||
|  | |||||||
| @ -396,7 +396,7 @@ typedef enum { | |||||||
|  * keep debugger enabled while in any low power mode when set to 1 |  * keep debugger enabled while in any low power mode when set to 1 | ||||||
|  * should be set to 0 in production |  * 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 |  * When set to 1, the traces are enabled in the BLE services | ||||||
|  | |||||||
| @ -1,25 +1,3 @@ | |||||||
| /* USER CODE BEGIN Header */ |  | ||||||
| /**
 |  | ||||||
|  ****************************************************************************** |  | ||||||
|   * File Name          : app_debug.c |  | ||||||
|   * Description        : Debug capabilities source file for STM32WPAN Middleware |  | ||||||
|  ****************************************************************************** |  | ||||||
|   * @attention |  | ||||||
|   * |  | ||||||
|   * <h2><center>© Copyright (c) 2020 STMicroelectronics. |  | ||||||
|   * All rights reserved.</center></h2> |  | ||||||
|   * |  | ||||||
|   * 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 "utilities_common.h" | ||||||
| 
 | 
 | ||||||
| #include "app_common.h" | #include "app_common.h" | ||||||
| @ -28,10 +6,7 @@ | |||||||
| #include "tl.h" | #include "tl.h" | ||||||
| #include "dbg_trace.h" | #include "dbg_trace.h" | ||||||
| #include <furi_hal.h> | #include <furi_hal.h> | ||||||
| /* USER CODE END Includes */ |  | ||||||
| 
 | 
 | ||||||
| /* Private typedef -----------------------------------------------------------*/ |  | ||||||
| /* USER CODE BEGIN PTD */ |  | ||||||
| typedef PACKED_STRUCT { | typedef PACKED_STRUCT { | ||||||
|     GPIO_TypeDef* port; |     GPIO_TypeDef* port; | ||||||
|     uint16_t pin; |     uint16_t pin; | ||||||
| @ -39,10 +14,7 @@ typedef PACKED_STRUCT { | |||||||
|     uint8_t reserved; |     uint8_t reserved; | ||||||
| } | } | ||||||
| APPD_GpioConfig_t; | APPD_GpioConfig_t; | ||||||
| /* USER CODE END PTD */ |  | ||||||
| 
 | 
 | ||||||
| /* Private defines -----------------------------------------------------------*/ |  | ||||||
| /* USER CODE BEGIN PD */ |  | ||||||
| #define GPIO_NBR_OF_RF_SIGNALS 9 | #define GPIO_NBR_OF_RF_SIGNALS 9 | ||||||
| #define GPIO_CFG_NBR_OF_FEATURES 34 | #define GPIO_CFG_NBR_OF_FEATURES 34 | ||||||
| #define NBR_OF_TRACES_CONFIG_PARAMETERS 4 | #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 |  * 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) | #define SYS_DBG_CFG1 (SHCI_C2_DEBUG_OPTIONS_IPCORE_LP | SHCI_C2_DEBUG_OPTIONS_CPU2_STOP_EN) | ||||||
| /* USER CODE END PD */ |  | ||||||
| 
 | 
 | ||||||
| /* Private variables ---------------------------------------------------------*/ | /* Private variables ---------------------------------------------------------*/ | ||||||
| /* USER CODE BEGIN PV */ |  | ||||||
| PLACE_IN_SECTION("MB_MEM2") | PLACE_IN_SECTION("MB_MEM2") | ||||||
| ALIGN(4) static SHCI_C2_DEBUG_TracesConfig_t APPD_TracesConfig = {0, 0, 0, 0}; | ALIGN(4) static SHCI_C2_DEBUG_TracesConfig_t APPD_TracesConfig = {0, 0, 0, 0}; | ||||||
| PLACE_IN_SECTION("MB_MEM2") | 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_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 */ |     {GPIOA, LL_GPIO_PIN_6, 1, 0}, /* HARD_FAULT - Set on Entry / Reset on Exit */ | ||||||
|     /* From v1.1.1 */ |     /* 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 */ |     /* 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}, /* END_OF_CONNECTION_EVENT - Set on Entry / Reset on Exit */ | ||||||
|     {GPIOA, LL_GPIO_PIN_0, 0, 0}, /* TIMER_SERVER_CALLBACK - Toggle on Entry */ |     {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 */ |     {GPIOB, LL_GPIO_PIN_10, 0, 0}, /* DTB18 - FSM4 */ | ||||||
| }; | }; | ||||||
| #endif | #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_SetCPU2GpioConfig(void); | ||||||
| static void APPD_BleDtbCfg(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) | #if(CFG_DEBUG_TRACE != 0) | ||||||
|     DbgTraceInit(); |     DbgTraceInit(); | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|     APPD_SetCPU2GpioConfig(); |     APPD_SetCPU2GpioConfig(); | ||||||
|     APPD_BleDtbCfg(); |     APPD_BleDtbCfg(); | ||||||
| 
 |  | ||||||
|     /* USER CODE END APPD_Init */ |  | ||||||
|     return; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void APPD_EnableCPU2(void) { | void APPD_EnableCPU2(void) { | ||||||
|     /* USER CODE BEGIN APPD_EnableCPU2 */ |  | ||||||
|     SHCI_C2_DEBUG_Init_Cmd_Packet_t DebugCmdPacket = { |     SHCI_C2_DEBUG_Init_Cmd_Packet_t DebugCmdPacket = { | ||||||
|         {{0, 0, 0}}, /**< Does not need to be initialized */ |         {{0, 0, 0}}, /**< Does not need to be initialized */ | ||||||
|         {(uint8_t*)aGpioConfigList, |         {(uint8_t*)aGpioConfigList, | ||||||
| @ -204,6 +130,7 @@ void APPD_EnableCPU2(void) { | |||||||
|     /** GPIO DEBUG Initialization */ |     /** GPIO DEBUG Initialization */ | ||||||
|     SHCI_C2_DEBUG_Init(&DebugCmdPacket); |     SHCI_C2_DEBUG_Init(&DebugCmdPacket); | ||||||
| 
 | 
 | ||||||
|  |     // We don't need External Power Amplifier
 | ||||||
|     // LL_GPIO_InitTypeDef  gpio_config;
 |     // LL_GPIO_InitTypeDef  gpio_config;
 | ||||||
|     // gpio_config.Pull = GPIO_NOPULL;
 |     // gpio_config.Pull = GPIO_NOPULL;
 | ||||||
|     // gpio_config.Mode = GPIO_MODE_OUTPUT_PP;
 |     // gpio_config.Mode = GPIO_MODE_OUTPUT_PP;
 | ||||||
| @ -212,17 +139,10 @@ void APPD_EnableCPU2(void) { | |||||||
|     // HAL_GPIO_Init(GPIOC, &gpio_config);
 |     // HAL_GPIO_Init(GPIOC, &gpio_config);
 | ||||||
|     // SHCI_C2_ExtpaConfig((uint32_t)GPIOC, LL_GPIO_PIN_3, EXT_PA_ENABLED_LOW, EXT_PA_ENABLED);
 |     // SHCI_C2_ExtpaConfig((uint32_t)GPIOC, LL_GPIO_PIN_3, EXT_PA_ENABLED_LOW, EXT_PA_ENABLED);
 | ||||||
| 
 | 
 | ||||||
|     /* USER CODE END APPD_EnableCPU2 */ |  | ||||||
|     return; |     return; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /*************************************************************
 |  | ||||||
|  * |  | ||||||
|  * LOCAL FUNCTIONS |  | ||||||
|  * |  | ||||||
|  *************************************************************/ |  | ||||||
| static void APPD_SetCPU2GpioConfig(void) { | static void APPD_SetCPU2GpioConfig(void) { | ||||||
|     /* USER CODE BEGIN APPD_SetCPU2GpioConfig */ |  | ||||||
|     LL_GPIO_InitTypeDef gpio_config = {0}; |     LL_GPIO_InitTypeDef gpio_config = {0}; | ||||||
|     uint8_t local_loop; |     uint8_t local_loop; | ||||||
|     uint16_t gpioa_pin_list; |     uint16_t gpioa_pin_list; | ||||||
| @ -259,8 +179,9 @@ static void APPD_SetCPU2GpioConfig(void) { | |||||||
|     gpio_config.OutputType = LL_GPIO_OUTPUT_PUSHPULL; |     gpio_config.OutputType = LL_GPIO_OUTPUT_PUSHPULL; | ||||||
|     gpio_config.Pull = LL_GPIO_PULL_NO; |     gpio_config.Pull = LL_GPIO_PULL_NO; | ||||||
| 
 | 
 | ||||||
|     gpio_config.Pin = LL_GPIO_PIN_15 | LL_GPIO_PIN_14 | LL_GPIO_PIN_13; |     // Never disable SWD, why would you?
 | ||||||
|     LL_GPIO_Init(GPIOA, &gpio_config); |     // 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) { |     if(gpioa_pin_list != 0) { | ||||||
|         gpio_config.Pin = gpioa_pin_list; |         gpio_config.Pin = gpioa_pin_list; | ||||||
| @ -282,13 +203,9 @@ static void APPD_SetCPU2GpioConfig(void) { | |||||||
|         LL_GPIO_Init(GPIOC, &gpio_config); |         LL_GPIO_Init(GPIOC, &gpio_config); | ||||||
|         LL_GPIO_ResetOutputPin(GPIOC, gpioa_pin_list); |         LL_GPIO_ResetOutputPin(GPIOC, gpioa_pin_list); | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     /* USER CODE END APPD_SetCPU2GpioConfig */ |  | ||||||
|     return; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void APPD_BleDtbCfg(void) { | static void APPD_BleDtbCfg(void) { | ||||||
| /* USER CODE BEGIN APPD_BleDtbCfg */ |  | ||||||
| #if(BLE_DTB_CFG != 0) | #if(BLE_DTB_CFG != 0) | ||||||
|     LL_GPIO_InitTypeDef gpio_config = {0}; |     LL_GPIO_InitTypeDef gpio_config = {0}; | ||||||
|     uint8_t local_loop; |     uint8_t local_loop; | ||||||
| @ -304,11 +221,9 @@ static void APPD_BleDtbCfg(void) { | |||||||
|             case(uint32_t)GPIOA: |             case(uint32_t)GPIOA: | ||||||
|                 gpioa_pin_list |= aRfConfigList[local_loop].pin; |                 gpioa_pin_list |= aRfConfigList[local_loop].pin; | ||||||
|                 break; |                 break; | ||||||
| 
 |  | ||||||
|             case(uint32_t)GPIOB: |             case(uint32_t)GPIOB: | ||||||
|                 gpiob_pin_list |= aRfConfigList[local_loop].pin; |                 gpiob_pin_list |= aRfConfigList[local_loop].pin; | ||||||
|                 break; |                 break; | ||||||
| 
 |  | ||||||
|             default: |             default: | ||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
| @ -334,16 +249,8 @@ static void APPD_BleDtbCfg(void) { | |||||||
|         LL_GPIO_Init(GPIOB, &gpio_config); |         LL_GPIO_Init(GPIOB, &gpio_config); | ||||||
|     } |     } | ||||||
| #endif | #endif | ||||||
| 
 |  | ||||||
|     /* USER CODE END APPD_BleDtbCfg */ |  | ||||||
|     return; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /*************************************************************
 |  | ||||||
|  * |  | ||||||
|  * WRAP FUNCTIONS |  | ||||||
|  * |  | ||||||
| *************************************************************/ |  | ||||||
| #if(CFG_DEBUG_TRACE != 0) | #if(CFG_DEBUG_TRACE != 0) | ||||||
| void DbgOutputInit(void) { | void DbgOutputInit(void) { | ||||||
| } | } | ||||||
| @ -353,5 +260,3 @@ void DbgOutputTraces(uint8_t* p_data, uint16_t size, void (*cb)(void)) { | |||||||
|     cb(); |     cb(); | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
| 
 |  | ||||||
| /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ |  | ||||||
|  | |||||||
| @ -6,7 +6,9 @@ | |||||||
| #include "shci.h" | #include "shci.h" | ||||||
| #include "shci_tl.h" | #include "shci_tl.h" | ||||||
| #include "app_debug.h" | #include "app_debug.h" | ||||||
|  | 
 | ||||||
| #include <furi_hal.h> | #include <furi_hal.h> | ||||||
|  | #include <shci/shci.h> | ||||||
| 
 | 
 | ||||||
| #define TAG "Core2" | #define TAG "Core2" | ||||||
| 
 | 
 | ||||||
| @ -27,22 +29,13 @@ PLACE_IN_SECTION("MB_MEM2") | |||||||
| ALIGN(4) | ALIGN(4) | ||||||
| static uint8_t ble_glue_ble_spare_event_buff[sizeof(TL_PacketHeader_t) + TL_EVT_HDR_SIZE + 255]; | 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 { | typedef struct { | ||||||
|     osMutexId_t shci_mtx; |     osMutexId_t shci_mtx; | ||||||
|     osSemaphoreId_t shci_sem; |     osSemaphoreId_t shci_sem; | ||||||
|     FuriThread* thread; |     FuriThread* thread; | ||||||
|     BleGlueStatus status; |     BleGlueStatus status; | ||||||
|     BleGlueKeyStorageChangedCallback callback; |     BleGlueKeyStorageChangedCallback callback; | ||||||
|  |     BleGlueC2Info c2_info; | ||||||
|     void* context; |     void* context; | ||||||
| } BleGlue; | } BleGlue; | ||||||
| 
 | 
 | ||||||
| @ -70,9 +63,10 @@ void ble_glue_init() { | |||||||
|     LL_RCC_SetClkAfterWakeFromStop(LL_RCC_STOP_WAKEUPCLOCK_HSI); |     LL_RCC_SetClkAfterWakeFromStop(LL_RCC_STOP_WAKEUPCLOCK_HSI); | ||||||
|     /* Initialize the CPU2 reset value before starting CPU2 with C2BOOT */ |     /* Initialize the CPU2 reset value before starting CPU2 with C2BOOT */ | ||||||
|     LL_C2_PWR_SetPowerMode(LL_PWR_MODE_SHUTDOWN); |     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
 |     // Initialize all transport layers
 | ||||||
|     TL_MM_Config_t tl_mm_config; |     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) { | const BleGlueC2Info* ble_glue_get_c2_info() { | ||||||
|     bool ret = false; |     return &ble_glue->c2_info; | ||||||
| 
 |  | ||||||
|     size_t countdown = 1000; |  | ||||||
|     while(countdown > 0) { |  | ||||||
|         if(ble_glue->status == BleGlueStatusFusStarted) { |  | ||||||
|             ret = true; |  | ||||||
|             break; |  | ||||||
| } | } | ||||||
|         countdown--; | 
 | ||||||
|  | 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); |             osDelay(1); | ||||||
|         } |         } | ||||||
|  |     } while(!started && (timeout > 0)); | ||||||
| 
 | 
 | ||||||
|     if(ble_glue->status == BleGlueStatusFusStarted) { |     if(started) { | ||||||
|         SHCI_GetWirelessFwInfo(info); |         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 { |     } else { | ||||||
|         FURI_LOG_E(TAG, "Failed to start FUS"); |         FURI_LOG_E(TAG, "C2 startup failed"); | ||||||
|         ble_glue->status = BleGlueStatusBroken; |         ble_glue->status = BleGlueStatusBroken; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return ret; |     return started; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool ble_glue_start() { | bool ble_glue_start() { | ||||||
|     furi_assert(ble_glue); |     furi_assert(ble_glue); | ||||||
| 
 | 
 | ||||||
|     if(ble_glue->status != BleGlueStatusFusStarted) { |     if(ble_glue->status != BleGlueStatusC2Started) { | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     bool ret = false; |     bool ret = false; | ||||||
|     furi_hal_power_insomnia_enter(); |  | ||||||
|     if(ble_app_init()) { |     if(ble_app_init()) { | ||||||
|         FURI_LOG_I(TAG, "Radio stack started"); |         FURI_LOG_I(TAG, "Radio stack started"); | ||||||
|         ble_glue->status = BleGlueStatusRadioStackStarted; |         ble_glue->status = BleGlueStatusRadioStackRunning; | ||||||
|         ret = true; |         ret = true; | ||||||
|         if(SHCI_C2_SetFlashActivityControl(FLASH_ACTIVITY_CONTROL_SEM7) == SHCI_Success) { |         if(SHCI_C2_SetFlashActivityControl(FLASH_ACTIVITY_CONTROL_SEM7) == SHCI_Success) { | ||||||
|             FURI_LOG_I(TAG, "Flash activity control switched to SEM7"); |             FURI_LOG_I(TAG, "Flash activity control switched to SEM7"); | ||||||
| @ -157,7 +213,6 @@ bool ble_glue_start() { | |||||||
|         ble_glue->status = BleGlueStatusRadioStackMissing; |         ble_glue->status = BleGlueStatusRadioStackMissing; | ||||||
|         ble_app_thread_stop(); |         ble_app_thread_stop(); | ||||||
|     } |     } | ||||||
|     furi_hal_power_insomnia_exit(); |  | ||||||
| 
 | 
 | ||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
| @ -167,7 +222,7 @@ bool ble_glue_is_alive() { | |||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return ble_glue->status >= BleGlueStatusFusStarted; |     return ble_glue->status >= BleGlueStatusC2Started; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool ble_glue_is_radio_stack_ready() { | bool ble_glue_is_radio_stack_ready() { | ||||||
| @ -175,26 +230,42 @@ bool ble_glue_is_radio_stack_ready() { | |||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return ble_glue->status == BleGlueStatusRadioStackStarted; |     return ble_glue->status == BleGlueStatusRadioStackRunning; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool ble_glue_radio_stack_fw_launch_started() { | BleGlueCommandResult ble_glue_force_c2_mode(BleGlueC2Mode desired_mode) { | ||||||
|     bool ret = false; |     furi_check(desired_mode > BleGlueC2ModeUnknown); | ||||||
|     // Get FUS status
 | 
 | ||||||
|     SHCI_FUS_GetState_ErrorCode_t err_code = 0; |     if(desired_mode == ble_glue->c2_info.mode) { | ||||||
|     uint8_t state = SHCI_C2_FUS_GetState(&err_code); |         return BleGlueCommandResultOK; | ||||||
|     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
 |     if((ble_glue->c2_info.mode == BleGlueC2ModeFUS) && (desired_mode == BleGlueC2ModeStack)) { | ||||||
|         FURI_LOG_W(TAG, "FUS is running. Restart to launch Radio Stack"); |         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(); |         SHCI_CmdStatus_t status = SHCI_C2_FUS_StartWs(); | ||||||
|         if(status) { |         if(status) { | ||||||
|             FURI_LOG_E(TAG, "Failed to start Radio Stack with status: %02X", status); |             FURI_LOG_E(TAG, "Failed to start Radio Stack with status: %02X", status); | ||||||
|         } else { |             return BleGlueCommandResultError; | ||||||
|             ret = true; |  | ||||||
|         } |         } | ||||||
|  |         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) { | 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) { | static void ble_glue_sys_user_event_callback(void* pPayload) { | ||||||
|     UNUSED(pPayload); |     UNUSED(pPayload); | ||||||
|     /* Traces channel initialization */ | 
 | ||||||
|     // APPD_EnableCPU2( );
 | #ifdef BLE_GLUE_DEBUG | ||||||
|  |     APPD_EnableCPU2(); | ||||||
|  | #endif | ||||||
| 
 | 
 | ||||||
|     TL_AsynchEvt_t* p_sys_event = |     TL_AsynchEvt_t* p_sys_event = | ||||||
|         (TL_AsynchEvt_t*)(((tSHCI_UserEvtRxParam*)pPayload)->pckt->evtserial.evt.payload); |         (TL_AsynchEvt_t*)(((tSHCI_UserEvtRxParam*)pPayload)->pckt->evtserial.evt.payload); | ||||||
| 
 | 
 | ||||||
|     if(p_sys_event->subevtcode == SHCI_SUB_EVT_CODE_READY) { |     if(p_sys_event->subevtcode == SHCI_SUB_EVT_CODE_READY) { | ||||||
|         FURI_LOG_I(TAG, "Fus started"); |         FURI_LOG_I(TAG, "Core2 started"); | ||||||
|         ble_glue->status = BleGlueStatusFusStarted; |         SHCI_C2_Ready_Evt_t* p_c2_ready_evt = (SHCI_C2_Ready_Evt_t*)p_sys_event->payload; | ||||||
|         furi_hal_power_insomnia_exit(); |         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) { |     } else if(p_sys_event->subevtcode == SHCI_SUB_EVT_ERROR_NOTIF) { | ||||||
|         FURI_LOG_E(TAG, "Error during initialization"); |         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) { |     } 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_ble_nvm_ram_update_event = | ||||||
|             (SHCI_C2_BleNvmRamUpdate_Evt_t*)p_sys_event->payload; |             (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); |         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; | ||||||
|  | } | ||||||
|  | |||||||
| @ -2,12 +2,53 @@ | |||||||
| 
 | 
 | ||||||
| #include <stdint.h> | #include <stdint.h> | ||||||
| #include <stdbool.h> | #include <stdbool.h> | ||||||
| #include <shci/shci.h> |  | ||||||
| 
 | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| extern "C" { | extern "C" { | ||||||
| #endif | #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 ( | typedef void ( | ||||||
|     *BleGlueKeyStorageChangedCallback)(uint8_t* change_addr_start, uint16_t size, void* context); |     *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_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
 | /** Is core2 radio stack present and ready
 | ||||||
|  * |  * | ||||||
| @ -46,11 +95,29 @@ void ble_glue_set_key_storage_changed_callback( | |||||||
| /** Stop SHCI thread */ | /** Stop SHCI thread */ | ||||||
| void ble_glue_thread_stop(); | 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
 | /** Restart MCU to launch radio stack firmware if necessary
 | ||||||
|  * |  * | ||||||
|  * @return      true on radio stack start command |  * @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 | #ifdef __cplusplus | ||||||
| } | } | ||||||
|  | |||||||
| @ -115,7 +115,6 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { | |||||||
|         if(gap->enable_adv) { |         if(gap->enable_adv) { | ||||||
|             // Restart advertising
 |             // Restart advertising
 | ||||||
|             gap_advertise_start(GapStateAdvFast); |             gap_advertise_start(GapStateAdvFast); | ||||||
|             furi_hal_power_insomnia_exit(); |  | ||||||
|         } |         } | ||||||
|         GapEvent event = {.type = GapEventTypeDisconnected}; |         GapEvent event = {.type = GapEventTypeDisconnected}; | ||||||
|         gap->on_event_cb(event, gap->context); |         gap->on_event_cb(event, gap->context); | ||||||
| @ -151,8 +150,7 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { | |||||||
|             } |             } | ||||||
|             break; |             break; | ||||||
| 
 | 
 | ||||||
|         case EVT_LE_CONN_COMPLETE: |         case EVT_LE_CONN_COMPLETE: { | ||||||
|             furi_hal_power_insomnia_enter(); |  | ||||||
|             hci_le_connection_complete_event_rp0* event = |             hci_le_connection_complete_event_rp0* event = | ||||||
|                 (hci_le_connection_complete_event_rp0*)meta_evt->data; |                 (hci_le_connection_complete_event_rp0*)meta_evt->data; | ||||||
|             gap->connection_params.conn_interval = event->Conn_Interval; |             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); |             gap_verify_connection_parameters(gap); | ||||||
|             // Start pairing by sending security request
 |             // Start pairing by sending security request
 | ||||||
|             aci_gap_slave_security_req(event->Connection_Handle); |             aci_gap_slave_security_req(event->Connection_Handle); | ||||||
|             break; |         } break; | ||||||
| 
 | 
 | ||||||
|         case EVT_LE_ADVERTISING_REPORT: { |         case EVT_LE_ADVERTISING_REPORT: { | ||||||
|             if(gap_scan) { |             if(gap_scan) { | ||||||
| @ -414,7 +412,9 @@ static void gap_advertise_start(GapState new_state) { | |||||||
|         // Stop advertising
 |         // Stop advertising
 | ||||||
|         status = aci_gap_set_non_discoverable(); |         status = aci_gap_set_non_discoverable(); | ||||||
|         if(status) { |         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
 |     // Configure advertising
 | ||||||
| @ -431,7 +431,7 @@ static void gap_advertise_start(GapState new_state) { | |||||||
|         0, |         0, | ||||||
|         0); |         0); | ||||||
|     if(status) { |     if(status) { | ||||||
|         FURI_LOG_E(TAG, "Set discoverable err: %d", status); |         FURI_LOG_E(TAG, "set_discoverable failed %d", status); | ||||||
|     } |     } | ||||||
|     gap->state = new_state; |     gap->state = new_state; | ||||||
|     GapEvent event = {.type = GapEventTypeStartAdvertising}; |     GapEvent event = {.type = GapEventTypeStartAdvertising}; | ||||||
| @ -440,14 +440,25 @@ static void gap_advertise_start(GapState new_state) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void gap_advertise_stop() { | static void gap_advertise_stop() { | ||||||
|  |     tBleStatus ret; | ||||||
|     if(gap->state > GapStateIdle) { |     if(gap->state > GapStateIdle) { | ||||||
|         if(gap->state == GapStateConnected) { |         if(gap->state == GapStateConnected) { | ||||||
|             // Terminate connection
 |             // 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
 |         // Stop advertising
 | ||||||
|         osTimerStop(gap->advertise_timer); |         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; |         gap->state = GapStateIdle; | ||||||
|     } |     } | ||||||
|     GapEvent event = {.type = GapEventTypeStopAdvertising}; |     GapEvent event = {.type = GapEventTypeStopAdvertising}; | ||||||
|  | |||||||
| @ -10,10 +10,10 @@ void furi_hal_init_early() { | |||||||
|     furi_hal_clock_init_early(); |     furi_hal_clock_init_early(); | ||||||
|     furi_hal_delay_init(); |     furi_hal_delay_init(); | ||||||
| 
 | 
 | ||||||
|     furi_hal_os_init(); |  | ||||||
| 
 |  | ||||||
|     furi_hal_resources_init_early(); |     furi_hal_resources_init_early(); | ||||||
| 
 | 
 | ||||||
|  |     furi_hal_os_init(); | ||||||
|  | 
 | ||||||
|     furi_hal_spi_init_early(); |     furi_hal_spi_init_early(); | ||||||
| 
 | 
 | ||||||
|     furi_hal_i2c_init_early(); |     furi_hal_i2c_init_early(); | ||||||
| @ -55,7 +55,6 @@ void furi_hal_init() { | |||||||
|     FURI_LOG_I(TAG, "Speaker OK"); |     FURI_LOG_I(TAG, "Speaker OK"); | ||||||
| 
 | 
 | ||||||
|     furi_hal_crypto_init(); |     furi_hal_crypto_init(); | ||||||
|     furi_hal_crc_init(true); |  | ||||||
| 
 | 
 | ||||||
|     // USB
 |     // USB
 | ||||||
| #ifndef FURI_RAM_EXEC | #ifndef FURI_RAM_EXEC | ||||||
|  | |||||||
| @ -16,6 +16,9 @@ | |||||||
| #define FURI_HAL_BT_DEFAULT_MAC_ADDR \ | #define FURI_HAL_BT_DEFAULT_MAC_ADDR \ | ||||||
|     { 0x6c, 0x7a, 0xd8, 0xac, 0x57, 0x72 } |     { 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; | osMutexId_t furi_hal_bt_core2_mtx = NULL; | ||||||
| static FuriHalBtStack furi_hal_bt_stack = FuriHalBtStackUnknown; | 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); |     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; |     bool supported = false; | ||||||
|     if(info->StackType == INFO_STACK_TYPE_BLE_HCI) { |     if(info->StackType == INFO_STACK_TYPE_BLE_HCI) { | ||||||
|         furi_hal_bt_stack = FuriHalBtStackHciLayer; |         furi_hal_bt_stack = FuriHalBtStackHciLayer; | ||||||
| @ -128,21 +131,21 @@ bool furi_hal_bt_start_radio_stack() { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     do { |     do { | ||||||
|         // Wait until FUS is started or timeout
 |         // Wait until C2 is started or timeout
 | ||||||
|         WirelessFwInfo_t info = {}; |         if(!ble_glue_wait_for_c2_start(FURI_HAL_BT_C2_START_TIMEOUT)) { | ||||||
|         if(!ble_glue_wait_for_fus_start(&info)) { |             FURI_LOG_E(TAG, "Core2 start failed"); | ||||||
|             FURI_LOG_E(TAG, "FUS start failed"); |  | ||||||
|             LL_C2_PWR_SetPowerMode(LL_PWR_MODE_SHUTDOWN); |  | ||||||
|             ble_glue_thread_stop(); |             ble_glue_thread_stop(); | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|         // If FUS is running, start radio stack fw
 | 
 | ||||||
|         if(ble_glue_radio_stack_fw_launch_started()) { |         // If C2 is running, start radio stack fw
 | ||||||
|             // If FUS is running do nothing and wait for system reset
 |         if(!furi_hal_bt_ensure_c2_mode(BleGlueC2ModeStack)) { | ||||||
|             furi_crash("Waiting for FUS to launch radio stack firmware"); |             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"); |             FURI_LOG_E(TAG, "Unsupported radio stack"); | ||||||
|             // Don't stop SHCI for crypto enclave support
 |             // Don't stop SHCI for crypto enclave support
 | ||||||
|             break; |             break; | ||||||
| @ -150,7 +153,6 @@ bool furi_hal_bt_start_radio_stack() { | |||||||
|         // Starting radio stack
 |         // Starting radio stack
 | ||||||
|         if(!ble_glue_start()) { |         if(!ble_glue_start()) { | ||||||
|             FURI_LOG_E(TAG, "Failed to start radio stack"); |             FURI_LOG_E(TAG, "Failed to start radio stack"); | ||||||
|             LL_C2_PWR_SetPowerMode(LL_PWR_MODE_SHUTDOWN); |  | ||||||
|             ble_glue_thread_stop(); |             ble_glue_thread_stop(); | ||||||
|             ble_app_thread_stop(); |             ble_app_thread_stop(); | ||||||
|             break; |             break; | ||||||
| @ -217,27 +219,39 @@ bool furi_hal_bt_start_app(FuriHalBtProfile profile, GapEventCallback event_cb, | |||||||
|     return ret; |     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) { | bool furi_hal_bt_change_app(FuriHalBtProfile profile, GapEventCallback event_cb, void* context) { | ||||||
|     furi_assert(event_cb); |     furi_assert(event_cb); | ||||||
|     furi_assert(profile < FuriHalBtProfileNumber); |     furi_assert(profile < FuriHalBtProfileNumber); | ||||||
|     bool ret = true; |     bool ret = true; | ||||||
| 
 | 
 | ||||||
|     FURI_LOG_I(TAG, "Stop current profile services"); |     furi_hal_bt_reinit(); | ||||||
|     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(); |  | ||||||
|     ret = furi_hal_bt_start_app(profile, event_cb, context); |     ret = furi_hal_bt_start_app(profile, event_cb, context); | ||||||
|     if(ret) { |     if(ret) { | ||||||
|         current_profile = &profile_config[profile]; |         current_profile = &profile_config[profile]; | ||||||
| @ -404,3 +418,18 @@ void furi_hal_bt_stop_scan() { | |||||||
|         gap_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; | ||||||
|  | } | ||||||
|  | |||||||
| @ -204,6 +204,8 @@ void furi_hal_clock_switch_to_hsi() { | |||||||
| 
 | 
 | ||||||
|     while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_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() { | void furi_hal_clock_switch_to_pll() { | ||||||
| @ -215,6 +217,8 @@ void furi_hal_clock_switch_to_pll() { | |||||||
|     while(!LL_RCC_PLL_IsReady()) |     while(!LL_RCC_PLL_IsReady()) | ||||||
|         ; |         ; | ||||||
| 
 | 
 | ||||||
|  |     LL_FLASH_SetLatency(LL_FLASH_LATENCY_3); | ||||||
|  | 
 | ||||||
|     LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_PLL); |     LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_PLL); | ||||||
|     LL_RCC_SetSMPSClockSource(LL_RCC_SMPS_CLKSOURCE_HSE); |     LL_RCC_SetSMPSClockSource(LL_RCC_SMPS_CLKSOURCE_HSE); | ||||||
| 
 | 
 | ||||||
|  | |||||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Aleksandr Kutuzov
						Aleksandr Kutuzov