Просмотр исходного кода

[FL-2263] Flasher service & RAM exec (#1006)

* WIP on stripping fw
* Compact FW build - use RAM_EXEC=1 COMPACT=1 DEBUG=0
* Fixed uninitialized storage struct; small fixes to compact fw
* Flasher srv w/mocked flash ops
* Fixed typos & accomodated FFF changes
* Alternative fw startup branch
* Working load & jmp to RAM fw
* +manifest processing for stage loader; + crc verification for stage payload
* Fixed questionable code & potential leaks
* Lowered screen update rate; added radio stack update stubs; working dfu write
* Console EP with manifest & stage validation
* Added microtar lib; minor ui fixes for updater
* Removed microtar
* Removed mtar #2
* Added a better version of microtar
* TAR archive api; LFS backup & restore core
* Recursive backup/restore
* LFS worker thread
* Added system apps to loader - not visible in UI; full update process with restarts
* Typo fix
* Dropped BL & f6; tooling for updater WIP
* Minor py fixes
* Minor fixes to make it build after merge
* Ported flash workaround from BL + fixed visuals
* Minor cleanup
* Chmod + loader app search fix
* Python linter fix
* Removed usb stuff & float read support for staged loader == -10% of binary size
* Added backup/restore & update pb requests
* Added stub impl to RPC for backup/restore/update commands
* Reworked TAR to use borrowed Storage api; slightly reduced build size by removing `static string`; hidden update-related RPC behind defines
* Moved backup&restore to storage
* Fixed new message types
* Backup/restore/update RPC impl
* Moved furi_hal_crc to LL; minor fixes
* CRC HAL rework to LL
* Purging STM HAL
* Brought back minimal DFU boot mode (no gui); additional crc state checks
* Added splash screen, BROKEN usb function
* Clock init rework WIP
* Stripped graphics from DFU mode
* Temp fix for unused static fun
* WIP update picker - broken!
* Fixed UI
* Bumping version
* Fixed RTC setup
* Backup to update folder instead of ext root
* Removed unused scenes & more usb remnants from staged loader
* CI updates
* Fixed update bundle name
* Temporary restored USB handler
* Attempt to prevent .text corruption
* Comments on how I spent this Saturday
* Added update file icon
* Documentation updates
* Moved common code to lib folder
* Storage: more unit tests
* Storage: blocking dir open, differentiate file and dir when freed.
* Major refactoring; added input processing to updater to allow retrying on failures (not very useful prob). Added API for extraction of thread return value
* Removed re-init check for manifest
* Changed low-level path manipulation to toolbox/path.h; makefile cleanup; tiny fix in lint.py
* Increased update worker stack size
* Text fixes in backup CLI
* Displaying number of update stages to run; removed timeout in handling errors
* Bumping version
* Added thread cleanup for spawner thread
* Updated build targets to exclude firmware bundle from 'ALL'
* Fixed makefile for update_package; skipping VCP init for update mode (ugly)
* Switched github build from ALL to update_package
* Added +x for dist_update.sh
* Cli: add total heap size to "free" command
* Moved (RAM) suffix to build version instead of git commit no.
* DFU comment
* Some fixes suggested by clang-tidy
* Fixed recursive PREFIX macro
* Makefile: gather all new rules in updater namespace. FuriHal: rename bootloader to boot, isr safe delays
* Github: correct build target name in firmware build
* FuriHal: move target switch to boot
* Makefile: fix firmware flash
* Furi, FuriHal: move kernel start to furi, early init
* Drop bootloader related stuff
* Drop cube. Drop bootloader linker script.
* Renamed update_hl, moved constants to #defines
* Moved update-related boot mode to separate bitfield
* Reworked updater cli to single entry point; fixed crash on tar cleanup
* Added Python replacement for dist shell scripts
* Linter fixes for dist.py +x
* Fixes for environment suffix
* Dropped bash scripts
* Added dirty build flag to version structure & interfaces
* Version string escapes
* Fixed flag logic in dist.py; added support for App instances being imported and not terminating the whole program
* Fixed fw address in ReadMe.md
* Rpc: fix crash on double screen start
* Return back original boot behavior and fix jump to system bootloader
* Cleanup code, add error sequence for RTC
* Update firmware readme
* FuriHal: drop boot, restructure RTC registers usage and add header register check
* Furi goes first
* Toolchain: add ccache support
* Renamed update bundle dir

Co-authored-by: DrZlo13 <who.just.the.doctor@gmail.com>
Co-authored-by: あく <alleteam@gmail.com>
hedger 3 лет назад
Родитель
Сommit
e02040107b
100 измененных файлов с 1803 добавлено и 3368 удалено
  1. 1 0
      .github/CODEOWNERS
  2. 18 7
      .github/workflows/build.yml
  3. 3 0
      .gitmodules
  4. 27 25
      Makefile
  5. 2 4
      ReadMe.md
  6. 1 0
      applications/ReadMe.md
  7. 3 33
      applications/about/about.c
  8. 28 1
      applications/applications.c
  9. 7 1
      applications/applications.h
  10. 29 2
      applications/applications.mk
  11. 1 0
      applications/archive/helpers/archive_browser.h
  12. 1 0
      applications/archive/helpers/archive_files.h
  13. 1 0
      applications/archive/scenes/archive_scene_browser.c
  14. 4 2
      applications/archive/views/archive_browser_view.c
  15. 3 1
      applications/bt/bt_cli.c
  16. 2 1
      applications/cli/cli.c
  17. 1 0
      applications/cli/cli_commands.c
  18. 3 1
      applications/crypto/crypto_cli.c
  19. 4 4
      applications/desktop/views/desktop_view_debug.c
  20. 0 1
      applications/desktop/views/desktop_view_debug.h
  21. 1 1
      applications/gui/elements.c
  22. 8 5
      applications/ibutton/ibutton_cli.c
  23. 2 0
      applications/infrared/cli/infrared_cli.cpp
  24. 1 1
      applications/infrared/infrared_app_remote_manager.cpp
  25. 1 1
      applications/input/input_cli.c
  26. 8 6
      applications/lfrfid/lfrfid_cli.cpp
  27. 29 24
      applications/loader/loader.c
  28. 2 0
      applications/nfc/nfc_cli.c
  29. 2 0
      applications/power/power_cli.c
  30. 3 4
      applications/power/power_service/power_api.c
  31. 27 23
      applications/rpc/rpc_gui.c
  32. 57 2
      applications/rpc/rpc_storage.c
  33. 29 1
      applications/rpc/rpc_system.c
  34. 2 0
      applications/storage/storage.c
  35. 17 1
      applications/storage/storage.h
  36. 3 1
      applications/storage/storage_cli.c
  37. 22 0
      applications/storage/storage_internal_api.c
  38. 4 0
      applications/storage/storage_processing.c
  39. 46 2
      applications/storage/storages/storage_ext.c
  40. 3 1
      applications/subghz/subghz_cli.c
  41. 134 0
      applications/updater/cli/updater_cli.c
  42. 30 0
      applications/updater/scenes/updater_scene.c
  43. 29 0
      applications/updater/scenes/updater_scene.h
  44. 5 0
      applications/updater/scenes/updater_scene_config.h
  45. 65 0
      applications/updater/scenes/updater_scene_error.c
  46. 105 0
      applications/updater/scenes/updater_scene_loadcfg.c
  47. 106 0
      applications/updater/scenes/updater_scene_main.c
  48. 132 0
      applications/updater/updater.c
  49. 65 0
      applications/updater/updater_i.h
  50. 226 0
      applications/updater/util/update_task.c
  51. 66 0
      applications/updater/util/update_task.h
  52. 24 0
      applications/updater/util/update_task_i.h
  53. 148 0
      applications/updater/util/update_task_workers.c
  54. 151 0
      applications/updater/views/updater_main.c
  55. 28 0
      applications/updater/views/updater_main.h
  56. 1 1
      assets/assets.mk
  57. 3 0
      assets/compiled/assets_icons.c
  58. 1 0
      assets/compiled/assets_icons.h
  59. BIN
      assets/icons/Archive/update_10px.png
  60. 0 44
      bootloader/Makefile
  61. 0 50
      bootloader/ReadMe.md
  62. 0 14
      bootloader/src/main.c
  63. 0 21
      bootloader/targets/f7/furi_hal/furi_hal.c
  64. 0 214
      bootloader/targets/f7/furi_hal/furi_hal_gpio.c
  65. 0 264
      bootloader/targets/f7/furi_hal/furi_hal_gpio.h
  66. 0 205
      bootloader/targets/f7/furi_hal/furi_hal_i2c.c
  67. 0 195
      bootloader/targets/f7/furi_hal/furi_hal_i2c.h
  68. 0 151
      bootloader/targets/f7/furi_hal/furi_hal_i2c_config.c
  69. 0 31
      bootloader/targets/f7/furi_hal/furi_hal_i2c_config.h
  70. 0 51
      bootloader/targets/f7/furi_hal/furi_hal_i2c_types.h
  71. 0 49
      bootloader/targets/f7/furi_hal/furi_hal_light.c
  72. 0 17
      bootloader/targets/f7/furi_hal/furi_hal_light.h
  73. 0 43
      bootloader/targets/f7/furi_hal/furi_hal_resources.c
  74. 0 73
      bootloader/targets/f7/furi_hal/furi_hal_resources.h
  75. 0 151
      bootloader/targets/f7/furi_hal/furi_hal_spi.c
  76. 0 290
      bootloader/targets/f7/furi_hal/furi_hal_spi_config.c
  77. 0 61
      bootloader/targets/f7/furi_hal/furi_hal_spi_config.h
  78. 0 64
      bootloader/targets/f7/furi_hal/furi_hal_spi_types.h
  79. 0 268
      bootloader/targets/f7/furi_hal/furi_hal_version.c
  80. 0 108
      bootloader/targets/f7/furi_hal/main.h
  81. 0 187
      bootloader/targets/f7/stm32wb55xx_flash_cm4.ld
  82. 0 264
      bootloader/targets/f7/target.c
  83. 0 50
      bootloader/targets/f7/target.mk
  84. 0 15
      bootloader/targets/furi_hal_include/furi_hal.h
  85. 0 102
      bootloader/targets/furi_hal_include/furi_hal_spi.h
  86. 0 173
      bootloader/targets/furi_hal_include/furi_hal_version.h
  87. 0 25
      bootloader/targets/furi_hal_include/target.h
  88. 3 8
      core/flipper.c
  89. 6 0
      core/furi.c
  90. 2 0
      core/furi.h
  91. 8 6
      core/furi/check.c
  92. 6 3
      core/furi/check.h
  93. 14 10
      core/furi/common_defines.h
  94. 5 0
      core/furi/memmgr.c
  95. 6 0
      core/furi/memmgr.h
  96. 5 0
      core/furi/memmgr_heap.c
  97. 1 1
      core/furi/record.h
  98. 7 1
      core/furi/thread.c
  99. 9 1
      core/furi/thread.h
  100. 6 1
      firmware/Makefile

+ 1 - 0
.github/CODEOWNERS

@@ -25,6 +25,7 @@ applications/sd-filesystem/** @skotopes @DrZlo13
 applications/subghz/** @skotopes @DrZlo13
 applications/subghz/** @skotopes @DrZlo13
 applications/template/** @skotopes @DrZlo13
 applications/template/** @skotopes @DrZlo13
 applications/tests/** @skotopes @DrZlo13
 applications/tests/** @skotopes @DrZlo13
+applications/updater/** @skotopes @DrZlo13 @hedger
 
 
 # Assets and asset generator
 # Assets and asset generator
 assets/** @skotopes @DrZlo13
 assets/** @skotopes @DrZlo13

+ 18 - 7
.github/workflows/build.yml

@@ -42,7 +42,7 @@ jobs:
         run: |
         run: |
           test -d artifacts && rm -rf artifacts || true
           test -d artifacts && rm -rf artifacts || true
           mkdir artifacts
           mkdir artifacts
-      
+
       - name: 'Generate suffix and folder name'
       - name: 'Generate suffix and folder name'
         id: names
         id: names
         run: |
         run: |
@@ -52,13 +52,13 @@ jobs:
           fi
           fi
           BRANCH_OR_TAG=${REF#refs/*/}
           BRANCH_OR_TAG=${REF#refs/*/}
           SHA=$(git rev-parse --short HEAD)
           SHA=$(git rev-parse --short HEAD)
-          
+
           if [[ "${{ github.ref }}" == "refs/tags/"* ]]; then
           if [[ "${{ github.ref }}" == "refs/tags/"* ]]; then
             SUFFIX=${BRANCH_OR_TAG//\//_}
             SUFFIX=${BRANCH_OR_TAG//\//_}
           else
           else
             SUFFIX=${BRANCH_OR_TAG//\//_}-$(date +'%d%m%Y')-${SHA}
             SUFFIX=${BRANCH_OR_TAG//\//_}-$(date +'%d%m%Y')-${SHA}
           fi
           fi
-          
+
           echo "WORKFLOW_BRANCH_OR_TAG=${BRANCH_OR_TAG}" >> $GITHUB_ENV
           echo "WORKFLOW_BRANCH_OR_TAG=${BRANCH_OR_TAG}" >> $GITHUB_ENV
           echo "DIST_SUFFIX=${SUFFIX}" >> $GITHUB_ENV
           echo "DIST_SUFFIX=${SUFFIX}" >> $GITHUB_ENV
           echo "::set-output name=artifacts-path::${BRANCH_OR_TAG}"
           echo "::set-output name=artifacts-path::${BRANCH_OR_TAG}"
@@ -86,7 +86,7 @@ jobs:
             set -e
             set -e
             for TARGET in ${TARGETS}
             for TARGET in ${TARGETS}
             do
             do
-              make TARGET=${TARGET} ${{ startsWith(github.ref, 'refs/tags') && 'DEBUG=0 COMPACT=1' || '' }}
+              make updater_package TARGET=${TARGET} ${{ startsWith(github.ref, 'refs/tags') && 'DEBUG=0 COMPACT=1' || '' }}
             done
             done
 
 
       - name: 'Move upload files'
       - name: 'Move upload files'
@@ -100,6 +100,17 @@ jobs:
               mv dist/${TARGET}/* artifacts/
               mv dist/${TARGET}/* artifacts/
             done
             done
 
 
+      - name: 'Bundle self-update package'
+        if: ${{ !github.event.pull_request.head.repo.fork }}
+        run: |
+          set -e
+          for UPDATEBUNDLE in artifacts/*/
+          do
+            BUNDLE_NAME=`echo $UPDATEBUNDLE | cut -d'/' -f2`
+            echo Packaging ${BUNDLE_NAME}
+            tar czpf artifacts/flipper-z-${BUNDLE_NAME}.tgz -C artifacts ${BUNDLE_NAME}
+          done
+
       - name: 'Bundle resources'
       - name: 'Bundle resources'
         if: ${{ !github.event.pull_request.head.repo.fork }}
         if: ${{ !github.event.pull_request.head.repo.fork }}
         run: |
         run: |
@@ -175,7 +186,7 @@ jobs:
 
 
       - name: 'Build docker image'
       - name: 'Build docker image'
         uses: ./.github/actions/docker
         uses: ./.github/actions/docker
-      
+
       - name: 'Generate suffix and folder name'
       - name: 'Generate suffix and folder name'
         id: names
         id: names
         run: |
         run: |
@@ -185,13 +196,13 @@ jobs:
           fi
           fi
           BRANCH_OR_TAG=${REF#refs/*/}
           BRANCH_OR_TAG=${REF#refs/*/}
           SHA=$(git rev-parse --short HEAD)
           SHA=$(git rev-parse --short HEAD)
-          
+
           if [[ "${{ github.ref }}" == "refs/tags/"* ]]; then
           if [[ "${{ github.ref }}" == "refs/tags/"* ]]; then
             SUFFIX=${BRANCH_OR_TAG//\//_}
             SUFFIX=${BRANCH_OR_TAG//\//_}
           else
           else
             SUFFIX=${BRANCH_OR_TAG//\//_}-$(date +'%d%m%Y')-${SHA}
             SUFFIX=${BRANCH_OR_TAG//\//_}-$(date +'%d%m%Y')-${SHA}
           fi
           fi
-          
+
           echo "WORKFLOW_BRANCH_OR_TAG=${BRANCH_OR_TAG}" >> $GITHUB_ENV
           echo "WORKFLOW_BRANCH_OR_TAG=${BRANCH_OR_TAG}" >> $GITHUB_ENV
           echo "DIST_SUFFIX=${SUFFIX}" >> $GITHUB_ENV
           echo "DIST_SUFFIX=${SUFFIX}" >> $GITHUB_ENV
 
 

+ 3 - 0
.gitmodules

@@ -19,3 +19,6 @@
 [submodule "lib/FreeRTOS-Kernel"]
 [submodule "lib/FreeRTOS-Kernel"]
 	path = lib/FreeRTOS-Kernel
 	path = lib/FreeRTOS-Kernel
 	url = https://github.com/FreeRTOS/FreeRTOS-Kernel.git
 	url = https://github.com/FreeRTOS/FreeRTOS-Kernel.git
+[submodule "lib/microtar"]
+	path = lib/microtar
+	url = https://github.com/amachronic/microtar.git

+ 27 - 25
Makefile

@@ -1,16 +1,16 @@
 PROJECT_ROOT := $(abspath $(dir $(abspath $(firstword $(MAKEFILE_LIST)))))
 PROJECT_ROOT := $(abspath $(dir $(abspath $(firstword $(MAKEFILE_LIST)))))
+
+include			$(PROJECT_ROOT)/make/git.mk
+
 COPRO_DIR := $(PROJECT_ROOT)/lib/STM32CubeWB/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x
 COPRO_DIR := $(PROJECT_ROOT)/lib/STM32CubeWB/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x
 
 
 PROJECT_SOURCE_DIRECTORIES := \
 PROJECT_SOURCE_DIRECTORIES := \
 	$(PROJECT_ROOT)/applications \
 	$(PROJECT_ROOT)/applications \
-	$(PROJECT_ROOT)/bootloader/src \
-	$(PROJECT_ROOT)/bootloader/targets \
 	$(PROJECT_ROOT)/core \
 	$(PROJECT_ROOT)/core \
 	$(PROJECT_ROOT)/firmware/targets \
 	$(PROJECT_ROOT)/firmware/targets \
 	$(PROJECT_ROOT)/lib/app-template \
 	$(PROJECT_ROOT)/lib/app-template \
 	$(PROJECT_ROOT)/lib/app-scened-template \
 	$(PROJECT_ROOT)/lib/app-scened-template \
 	$(PROJECT_ROOT)/lib/common-api \
 	$(PROJECT_ROOT)/lib/common-api \
-	$(PROJECT_ROOT)/lib/cyfral \
 	$(PROJECT_ROOT)/lib/drivers \
 	$(PROJECT_ROOT)/lib/drivers \
 	$(PROJECT_ROOT)/lib/flipper_file \
 	$(PROJECT_ROOT)/lib/flipper_file \
 	$(PROJECT_ROOT)/lib/infrared \
 	$(PROJECT_ROOT)/lib/infrared \
@@ -34,18 +34,18 @@ endif
 include	$(PROJECT_ROOT)/make/defaults.mk
 include	$(PROJECT_ROOT)/make/defaults.mk
 
 
 .PHONY: all
 .PHONY: all
-all: bootloader_all firmware_all
-	@$(PROJECT_ROOT)/scripts/dist.sh
+all: firmware_all
+	@$(PROJECT_ROOT)/scripts/dist.py copy -t $(TARGET) -p firmware -s $(DIST_SUFFIX)
 
 
 .PHONY: whole
 .PHONY: whole
-whole: flash_radio bootloader_flash firmware_flash
+whole: flash_radio firmware_flash
 
 
 .PHONY: clean
 .PHONY: clean
-clean: bootloader_clean firmware_clean
+clean: firmware_clean updater_clean
 	@rm -rf $(PROJECT_ROOT)/dist/$(TARGET)
 	@rm -rf $(PROJECT_ROOT)/dist/$(TARGET)
 
 
 .PHONY: flash
 .PHONY: flash
-flash: bootloader_flash firmware_flash
+flash: firmware_flash
 
 
 .PHONY: debug
 .PHONY: debug
 debug:
 debug:
@@ -60,36 +60,38 @@ wipe:
 	@$(PROJECT_ROOT)/scripts/flash.py wipe
 	@$(PROJECT_ROOT)/scripts/flash.py wipe
 	@$(PROJECT_ROOT)/scripts/ob.py set
 	@$(PROJECT_ROOT)/scripts/ob.py set
 
 
-.PHONY: bootloader_all
-bootloader_all:
-	@$(MAKE) -C $(PROJECT_ROOT)/bootloader -j$(NPROCS) all
-
 .PHONY: firmware_all
 .PHONY: firmware_all
 firmware_all:
 firmware_all:
 	@$(MAKE) -C $(PROJECT_ROOT)/firmware -j$(NPROCS) all
 	@$(MAKE) -C $(PROJECT_ROOT)/firmware -j$(NPROCS) all
 
 
-.PHONY: bootloader_clean
-bootloader_clean:
-	@$(MAKE) -C $(PROJECT_ROOT)/bootloader -j$(NPROCS) clean
-
 .PHONY: firmware_clean
 .PHONY: firmware_clean
 firmware_clean:
 firmware_clean:
 	@$(MAKE) -C $(PROJECT_ROOT)/firmware -j$(NPROCS) clean
 	@$(MAKE) -C $(PROJECT_ROOT)/firmware -j$(NPROCS) clean
 
 
-.PHONY: bootloader_flash
-bootloader_flash:
-ifeq ($(FORCE), 1)
-	@rm $(PROJECT_ROOT)/bootloader/.obj/f*/flash || true
-endif
-	@$(MAKE) -C $(PROJECT_ROOT)/bootloader -j$(NPROCS) flash
-
 .PHONY: firmware_flash
 .PHONY: firmware_flash
 firmware_flash:
 firmware_flash:
 ifeq ($(FORCE), 1)
 ifeq ($(FORCE), 1)
-	@rm $(PROJECT_ROOT)/firmware/.obj/f*/flash || true
+	@rm $(PROJECT_ROOT)/firmware/.obj/f*-firmware/flash || true
 endif
 endif
 	@$(MAKE) -C $(PROJECT_ROOT)/firmware -j$(NPROCS) flash
 	@$(MAKE) -C $(PROJECT_ROOT)/firmware -j$(NPROCS) flash
 
 
+
+.PHONY: updater
+updater:
+	@$(MAKE) -C $(PROJECT_ROOT)/firmware -j$(NPROCS) RAM_EXEC=1 all
+
+.PHONY: updater_clean
+updater_clean:
+	@$(MAKE) -C $(PROJECT_ROOT)/firmware -j$(NPROCS) RAM_EXEC=1 clean
+
+.PHONY: updater_debug
+updater_debug:
+	@$(MAKE) -C $(PROJECT_ROOT)/firmware -j$(NPROCS) RAM_EXEC=1 debug
+
+.PHONY: updater_package
+updater_package: firmware_all updater
+	@$(PROJECT_ROOT)/scripts/dist.py copy -t $(TARGET) -p firmware updater -s $(DIST_SUFFIX) --bundlever "$(VERSION_STRING)"
+
 .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 0x080D7000 $(COPRO_DIR)/stm32wb5x_BLE_Stack_light_fw.bin
@@ -100,7 +102,7 @@ flash_radio_fus:
 	@echo
 	@echo
 	@echo "================   DON'T DO IT    ================"
 	@echo "================   DON'T DO IT    ================"
 	@echo "= Flashing FUS is going to erase secure enclave  ="
 	@echo "= Flashing FUS is going to erase secure enclave  ="
-	@echo "= You will loose ability to use encrypted assets ="
+	@echo "= You will lose ability to use encrypted assets  ="
 	@echo "=       type 'find / -exec rm -rf {} \;'         ="
 	@echo "=       type 'find / -exec rm -rf {} \;'         ="
 	@echo "=     In case if you still want to continue      ="
 	@echo "=     In case if you still want to continue      ="
 	@echo "================    JUST DON'T    ================"
 	@echo "================    JUST DON'T    ================"

+ 2 - 4
ReadMe.md

@@ -11,17 +11,16 @@ Our goal is to create nice and clean code with good documentation, to make it a
 
 
 [Get Latest Firmware from Update Server](https://update.flipperzero.one/)
 [Get Latest Firmware from Update Server](https://update.flipperzero.one/)
 
 
-Flipper Zero's firmware consists of three components:
+Flipper Zero's firmware consists of two components:
 
 
 - Core2 firmware set - proprietary components by ST: FUS + radio stack. FUS is flashed at factory and you should never update it.
 - Core2 firmware set - proprietary components by ST: FUS + radio stack. FUS is flashed at factory and you should never update it.
-- Core1 Bootloader - controls basic hardware initialization and loads firmware.
 - Core1 Firmware - HAL + OS + Drivers + Applications.
 - Core1 Firmware - HAL + OS + Drivers + Applications.
 
 
 All 3 of them must be flashed in order described.
 All 3 of them must be flashed in order described.
 
 
 ## With STLink
 ## With STLink
 
 
-### Core1 Bootloader + Firmware
+### Core1 Firmware
 
 
 Prerequisites:
 Prerequisites:
 
 
@@ -144,7 +143,6 @@ make whole
 
 
 - `applications`    - Applications and services used in firmware
 - `applications`    - Applications and services used in firmware
 - `assets`          - Assets used by applications and services
 - `assets`          - Assets used by applications and services
-- `bootloader`      - Bootloader source code
 - `core`            - Furi Core: os level primitives and helpers
 - `core`            - Furi Core: os level primitives and helpers
 - `debug`           - Debug tool: GDB-plugins, SVD-file and etc
 - `debug`           - Debug tool: GDB-plugins, SVD-file and etc
 - `docker`          - Docker image sources (used for firmware build automation)
 - `docker`          - Docker image sources (used for firmware build automation)

+ 1 - 0
applications/ReadMe.md

@@ -34,6 +34,7 @@
 - `system`              - System settings, tools and API
 - `system`              - System settings, tools and API
 - `tests`               - Unit tests and etc
 - `tests`               - Unit tests and etc
 - `u2f`                 - U2F Application
 - `u2f`                 - U2F Application
+- `updater`             - Update service & application
 
 
 - `application.c`       - Firmware application list source
 - `application.c`       - Firmware application list source
 - `application.h`       - Firmware application list header
 - `application.h`       - Firmware application list header

+ 3 - 33
applications/about/about.c

@@ -116,9 +116,10 @@ static DialogMessageButton fw_version_screen(DialogsApp* dialogs, DialogMessage*
     } else {
     } else {
         string_cat_printf(
         string_cat_printf(
             buffer,
             buffer,
-            "%s [%s]\n%s [%s]\n[%d] %s",
+            "%s [%s]\n%s%s [%s]\n[%d] %s",
             version_get_version(ver),
             version_get_version(ver),
             version_get_builddate(ver),
             version_get_builddate(ver),
+            version_get_dirty_flag(ver) ? "[!] " : "",
             version_get_githash(ver),
             version_get_githash(ver),
             version_get_gitbranchnum(ver),
             version_get_gitbranchnum(ver),
             version_get_target(ver),
             version_get_target(ver),
@@ -135,36 +136,6 @@ static DialogMessageButton fw_version_screen(DialogsApp* dialogs, DialogMessage*
     return result;
     return result;
 }
 }
 
 
-static DialogMessageButton bootloader_version_screen(DialogsApp* dialogs, DialogMessage* message) {
-    DialogMessageButton result;
-    string_t buffer;
-    string_init(buffer);
-    const Version* ver = furi_hal_version_get_bootloader_version();
-
-    if(!ver) {
-        string_cat_printf(buffer, "No info\n");
-    } else {
-        string_cat_printf(
-            buffer,
-            "%s [%s]\n%s [%s]\n[%d] %s",
-            version_get_version(ver),
-            version_get_builddate(ver),
-            version_get_githash(ver),
-            version_get_gitbranchnum(ver),
-            version_get_target(ver),
-            version_get_gitbranch(ver));
-    }
-
-    dialog_message_set_header(message, "Boot Version info:", 0, 0, AlignLeft, AlignTop);
-    dialog_message_set_text(message, string_get_cstr(buffer), 0, 13, AlignLeft, AlignTop);
-    result = dialog_message_show(dialogs, message);
-    dialog_message_set_text(message, NULL, 0, 0, AlignLeft, AlignTop);
-    dialog_message_set_header(message, NULL, 0, 0, AlignLeft, AlignTop);
-    string_clear(buffer);
-
-    return result;
-}
-
 const AboutDialogScreen about_screens[] = {
 const AboutDialogScreen about_screens[] = {
     product_screen,
     product_screen,
     compliance_screen,
     compliance_screen,
@@ -172,8 +143,7 @@ const AboutDialogScreen about_screens[] = {
     icon1_screen,
     icon1_screen,
     icon2_screen,
     icon2_screen,
     hw_version_screen,
     hw_version_screen,
-    fw_version_screen,
-    bootloader_version_screen};
+    fw_version_screen};
 
 
 const size_t about_screens_count = sizeof(about_screens) / sizeof(AboutDialogScreen);
 const size_t about_screens_count = sizeof(about_screens) / sizeof(AboutDialogScreen);
 
 

+ 28 - 1
applications/applications.c

@@ -14,6 +14,7 @@ extern int32_t notification_srv(void* p);
 extern int32_t power_srv(void* p);
 extern int32_t power_srv(void* p);
 extern int32_t storage_srv(void* p);
 extern int32_t storage_srv(void* p);
 extern int32_t desktop_srv(void* p);
 extern int32_t desktop_srv(void* p);
+extern int32_t updater_srv(void* p);
 
 
 // Apps
 // Apps
 extern int32_t accessor_app(void* p);
 extern int32_t accessor_app(void* p);
@@ -58,6 +59,7 @@ extern void storage_on_system_start();
 extern void subghz_on_system_start();
 extern void subghz_on_system_start();
 extern void power_on_system_start();
 extern void power_on_system_start();
 extern void unit_tests_on_system_start();
 extern void unit_tests_on_system_start();
+extern void updater_on_system_start();
 
 
 // Settings
 // Settings
 extern int32_t notification_settings_app(void* p);
 extern int32_t notification_settings_app(void* p);
@@ -91,6 +93,9 @@ const FlipperApplication FLIPPER_SERVICES[] = {
 #endif
 #endif
 
 
 #ifdef SRV_DESKTOP
 #ifdef SRV_DESKTOP
+#ifdef SRV_UPDATER
+#error SRV_UPDATER and SRV_DESKTOP are mutually exclusive!
+#endif
     {.app = desktop_srv, .name = "DesktopSrv", .stack_size = 2048, .icon = NULL},
     {.app = desktop_srv, .name = "DesktopSrv", .stack_size = 2048, .icon = NULL},
 #endif
 #endif
 
 
@@ -117,10 +122,28 @@ const FlipperApplication FLIPPER_SERVICES[] = {
 #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},
 #endif
 #endif
+
+#ifdef SRV_UPDATER
+#ifdef SRV_DESKTOP
+#error SRV_UPDATER and SRV_DESKTOP are mutually exclusive!
+#endif
+    {.app = updater_srv, .name = "UpdaterSrv", .stack_size = 2048, .icon = NULL},
+#endif
 };
 };
 
 
 const size_t FLIPPER_SERVICES_COUNT = sizeof(FLIPPER_SERVICES) / sizeof(FlipperApplication);
 const size_t FLIPPER_SERVICES_COUNT = sizeof(FLIPPER_SERVICES) / sizeof(FlipperApplication);
 
 
+const FlipperApplication FLIPPER_SYSTEM_APPS[] = {
+#ifdef APP_UPDATER
+#ifdef SRV_UPDATER
+#error APP_UPDATER and SRV_UPDATER are mutually exclusive!
+#endif
+    {.app = updater_srv, .name = "UpdaterApp", .stack_size = 2048, .icon = NULL},
+#endif
+};
+
+const size_t FLIPPER_SYSTEM_APPS_COUNT = sizeof(FLIPPER_SERVICES) / sizeof(FlipperApplication);
+
 // Main menu APP
 // Main menu APP
 const FlipperApplication FLIPPER_APPS[] = {
 const FlipperApplication FLIPPER_APPS[] = {
 
 
@@ -199,6 +222,10 @@ const FlipperOnStartHook FLIPPER_ON_SYSTEM_START[] = {
 #ifdef APP_UNIT_TESTS
 #ifdef APP_UNIT_TESTS
     unit_tests_on_system_start,
     unit_tests_on_system_start,
 #endif
 #endif
+
+#ifdef APP_UPDATER
+    updater_on_system_start,
+#endif
 };
 };
 
 
 const size_t FLIPPER_ON_SYSTEM_START_COUNT =
 const size_t FLIPPER_ON_SYSTEM_START_COUNT =
@@ -326,4 +353,4 @@ const FlipperApplication FLIPPER_SETTINGS_APPS[] = {
 };
 };
 
 
 const size_t FLIPPER_SETTINGS_APPS_COUNT =
 const size_t FLIPPER_SETTINGS_APPS_COUNT =
-    sizeof(FLIPPER_SETTINGS_APPS) / sizeof(FlipperApplication);
+    sizeof(FLIPPER_SETTINGS_APPS) / sizeof(FlipperApplication);

+ 7 - 1
applications/applications.h

@@ -42,6 +42,12 @@ extern const size_t FLIPPER_PLUGINS_COUNT;
 extern const FlipperApplication FLIPPER_DEBUG_APPS[];
 extern const FlipperApplication FLIPPER_DEBUG_APPS[];
 extern const size_t FLIPPER_DEBUG_APPS_COUNT;
 extern const size_t FLIPPER_DEBUG_APPS_COUNT;
 
 
+/* System apps
+ * Can only be spawned by loader by name
+ */
+extern const FlipperApplication FLIPPER_SYSTEM_APPS[];
+extern const size_t FLIPPER_SYSTEM_APPS_COUNT;
+
 /* Seperate scene app holder
 /* Seperate scene app holder
  * Spawned by loader
  * Spawned by loader
  */
  */
@@ -55,4 +61,4 @@ extern const FlipperApplication FLIPPER_ARCHIVE;
  * Spawned by loader
  * Spawned by loader
  */
  */
 extern const FlipperApplication FLIPPER_SETTINGS_APPS[];
 extern const FlipperApplication FLIPPER_SETTINGS_APPS[];
-extern const size_t FLIPPER_SETTINGS_APPS_COUNT;
+extern const size_t FLIPPER_SETTINGS_APPS_COUNT;

+ 29 - 2
applications/applications.mk

@@ -5,6 +5,16 @@ CFLAGS		+= -I$(APP_DIR)
 C_SOURCES	+= $(shell find $(APP_DIR) -name "*.c")
 C_SOURCES	+= $(shell find $(APP_DIR) -name "*.c")
 CPP_SOURCES	+= $(shell find $(APP_DIR) -name "*.cpp")
 CPP_SOURCES	+= $(shell find $(APP_DIR) -name "*.cpp")
 
 
+RAM_EXEC ?= 0
+ifeq ($(RAM_EXEC), 1)
+APP_RELEASE	= 0
+SRV_GUI 	= 1
+SRV_INPUT 	= 1
+SRV_NOTIFICATION	= 1
+SRV_STORAGE	= 1
+SRV_UPDATER	= 1
+APP_UPDATER	= 0
+endif
 
 
 APP_RELEASE ?= 1
 APP_RELEASE ?= 1
 ifeq ($(APP_RELEASE), 1)
 ifeq ($(APP_RELEASE), 1)
@@ -18,13 +28,13 @@ SRV_INPUT	= 1
 SRV_LOADER	= 1
 SRV_LOADER	= 1
 SRV_NOTIFICATION = 1
 SRV_NOTIFICATION = 1
 SRV_POWER	= 1
 SRV_POWER	= 1
-SRV_RPC = 1
+SRV_RPC 	= 1
 SRV_STORAGE	= 1
 SRV_STORAGE	= 1
 
 
 # Apps
 # Apps
 SRV_DESKTOP	= 1
 SRV_DESKTOP	= 1
 APP_ARCHIVE	= 1
 APP_ARCHIVE	= 1
-APP_GPIO = 1
+APP_GPIO 	= 1
 APP_IBUTTON	= 1
 APP_IBUTTON	= 1
 APP_INFRARED	= 1
 APP_INFRARED	= 1
 APP_LF_RFID	= 1
 APP_LF_RFID	= 1
@@ -32,6 +42,7 @@ APP_NFC		= 1
 APP_SUBGHZ	= 1
 APP_SUBGHZ	= 1
 APP_ABOUT	= 1
 APP_ABOUT	= 1
 APP_PASSPORT = 1
 APP_PASSPORT = 1
+APP_UPDATER = 1
 
 
 # Plugins
 # Plugins
 APP_MUSIC_PLAYER = 1
 APP_MUSIC_PLAYER = 1
@@ -223,6 +234,14 @@ CFLAGS		+= -DAPP_IBUTTON
 SRV_GUI		= 1
 SRV_GUI		= 1
 endif
 endif
 
 
+APP_UPDATER ?= 0
+ifeq ($(APP_UPDATER), 1)
+CFLAGS		+= -DAPP_UPDATER
+SRV_GUI		= 1
+SRV_STORAGE = 1
+SRV_NOTIFICATION = 1
+SRV_INPUT   = 1
+endif
 
 
 # Services
 # Services
 # that will start with OS
 # that will start with OS
@@ -245,6 +264,14 @@ SRV_GUI		= 1
 endif
 endif
 
 
 
 
+SRV_UPDATER ?= 0
+ifeq ($(SRV_UPDATER), 1)
+CFLAGS		+= -DSRV_UPDATER
+SRV_STORAGE	= 1
+SRV_GUI		= 1
+endif
+
+
 SRV_DOLPHIN ?= 0
 SRV_DOLPHIN ?= 0
 ifeq ($(SRV_DOLPHIN), 1)
 ifeq ($(SRV_DOLPHIN), 1)
 CFLAGS		+= -DSRV_DOLPHIN
 CFLAGS		+= -DSRV_DOLPHIN

+ 1 - 0
applications/archive/helpers/archive_browser.h

@@ -25,6 +25,7 @@ static const char* known_ext[] = {
     [ArchiveFileTypeInfrared] = ".ir",
     [ArchiveFileTypeInfrared] = ".ir",
     [ArchiveFileTypeBadUsb] = ".txt",
     [ArchiveFileTypeBadUsb] = ".txt",
     [ArchiveFileTypeU2f] = "?",
     [ArchiveFileTypeU2f] = "?",
+    [ArchiveFileTypeUpdateManifest] = ".fuf",
     [ArchiveFileTypeFolder] = "?",
     [ArchiveFileTypeFolder] = "?",
     [ArchiveFileTypeUnknown] = "*",
     [ArchiveFileTypeUnknown] = "*",
 };
 };

+ 1 - 0
applications/archive/helpers/archive_files.h

@@ -10,6 +10,7 @@ typedef enum {
     ArchiveFileTypeInfrared,
     ArchiveFileTypeInfrared,
     ArchiveFileTypeBadUsb,
     ArchiveFileTypeBadUsb,
     ArchiveFileTypeU2f,
     ArchiveFileTypeU2f,
+    ArchiveFileTypeUpdateManifest,
     ArchiveFileTypeFolder,
     ArchiveFileTypeFolder,
     ArchiveFileTypeUnknown,
     ArchiveFileTypeUnknown,
     ArchiveFileTypeLoading,
     ArchiveFileTypeLoading,

+ 1 - 0
applications/archive/scenes/archive_scene_browser.c

@@ -15,6 +15,7 @@ static const char* flipper_app_name[] = {
     [ArchiveFileTypeInfrared] = "Infrared",
     [ArchiveFileTypeInfrared] = "Infrared",
     [ArchiveFileTypeBadUsb] = "Bad USB",
     [ArchiveFileTypeBadUsb] = "Bad USB",
     [ArchiveFileTypeU2f] = "U2F",
     [ArchiveFileTypeU2f] = "U2F",
+    [ArchiveFileTypeUpdateManifest] = "UpdaterApp",
 };
 };
 
 
 static void archive_run_in_app(ArchiveBrowserView* browser, ArchiveFile_t* selected) {
 static void archive_run_in_app(ArchiveBrowserView* browser, ArchiveFile_t* selected) {

+ 4 - 2
applications/archive/views/archive_browser_view.c

@@ -12,7 +12,8 @@ static const char* ArchiveTabNames[] = {
     [ArchiveTabInfrared] = "Infrared",
     [ArchiveTabInfrared] = "Infrared",
     [ArchiveTabBadUsb] = "Bad USB",
     [ArchiveTabBadUsb] = "Bad USB",
     [ArchiveTabU2f] = "U2F",
     [ArchiveTabU2f] = "U2F",
-    [ArchiveTabBrowser] = "Browser"};
+    [ArchiveTabBrowser] = "Browser",
+};
 
 
 static const Icon* ArchiveItemIcons[] = {
 static const Icon* ArchiveItemIcons[] = {
     [ArchiveFileTypeIButton] = &I_ibutt_10px,
     [ArchiveFileTypeIButton] = &I_ibutt_10px,
@@ -22,6 +23,7 @@ static const Icon* ArchiveItemIcons[] = {
     [ArchiveFileTypeInfrared] = &I_ir_10px,
     [ArchiveFileTypeInfrared] = &I_ir_10px,
     [ArchiveFileTypeBadUsb] = &I_badusb_10px,
     [ArchiveFileTypeBadUsb] = &I_badusb_10px,
     [ArchiveFileTypeU2f] = &I_u2f_10px,
     [ArchiveFileTypeU2f] = &I_u2f_10px,
+    [ArchiveFileTypeUpdateManifest] = &I_update_10px,
     [ArchiveFileTypeFolder] = &I_dir_10px,
     [ArchiveFileTypeFolder] = &I_dir_10px,
     [ArchiveFileTypeUnknown] = &I_unknown_10px,
     [ArchiveFileTypeUnknown] = &I_unknown_10px,
     [ArchiveFileTypeLoading] = &I_unknown_10px, // TODO loading icon
     [ArchiveFileTypeLoading] = &I_unknown_10px, // TODO loading icon
@@ -374,4 +376,4 @@ void browser_free(ArchiveBrowserView* browser) {
 
 
     view_free(browser->view);
     view_free(browser->view);
     free(browser);
     free(browser);
-}
+}

+ 3 - 1
applications/bt/bt_cli.c

@@ -244,5 +244,7 @@ void bt_on_system_start() {
     cli_add_command(cli, "bt", CliCommandFlagDefault, bt_cli, NULL);
     cli_add_command(cli, "bt", CliCommandFlagDefault, bt_cli, NULL);
     furi_record_close("bt");
     furi_record_close("bt");
     furi_record_close("cli");
     furi_record_close("cli");
+#else
+    UNUSED(bt_cli);
 #endif
 #endif
-}
+}

+ 2 - 1
applications/cli/cli.c

@@ -95,10 +95,11 @@ void cli_motd() {
     const Version* firmware_version = furi_hal_version_get_firmware_version();
     const Version* firmware_version = furi_hal_version_get_firmware_version();
     if(firmware_version) {
     if(firmware_version) {
         printf(
         printf(
-            "Firmware version: %s %s (%s built on %s)\r\n",
+            "Firmware version: %s %s (%s%s built on %s)\r\n",
             version_get_gitbranch(firmware_version),
             version_get_gitbranch(firmware_version),
             version_get_version(firmware_version),
             version_get_version(firmware_version),
             version_get_githash(firmware_version),
             version_get_githash(firmware_version),
+            version_get_dirty_flag(firmware_version) ? "-dirty" : "",
             version_get_builddate(firmware_version));
             version_get_builddate(firmware_version));
     }
     }
 }
 }

+ 1 - 0
applications/cli/cli_commands.c

@@ -327,6 +327,7 @@ void cli_command_ps(Cli* cli, string_t args, void* context) {
 
 
 void cli_command_free(Cli* cli, string_t args, void* context) {
 void cli_command_free(Cli* cli, string_t args, void* context) {
     printf("Free heap size: %d\r\n", memmgr_get_free_heap());
     printf("Free heap size: %d\r\n", memmgr_get_free_heap());
+    printf("Total heap size: %d\r\n", memmgr_get_total_heap());
     printf("Minimum heap size: %d\r\n", memmgr_get_minimum_free_heap());
     printf("Minimum heap size: %d\r\n", memmgr_get_minimum_free_heap());
     printf("Maximum heap block: %d\r\n", memmgr_heap_get_max_free_block());
     printf("Maximum heap block: %d\r\n", memmgr_heap_get_max_free_block());
 }
 }

+ 3 - 1
applications/crypto/crypto_cli.c

@@ -276,7 +276,7 @@ void crypto_cli_store_key(Cli* cli, string_t args) {
     string_clear(key_type);
     string_clear(key_type);
 }
 }
 
 
-void crypto_cli(Cli* cli, string_t args, void* context) {
+static void crypto_cli(Cli* cli, string_t args, void* context) {
     string_t cmd;
     string_t cmd;
     string_init(cmd);
     string_init(cmd);
 
 
@@ -317,5 +317,7 @@ void crypto_on_system_start() {
     Cli* cli = furi_record_open("cli");
     Cli* cli = furi_record_open("cli");
     cli_add_command(cli, "crypto", CliCommandFlagDefault, crypto_cli, NULL);
     cli_add_command(cli, "crypto", CliCommandFlagDefault, crypto_cli, NULL);
     furi_record_close("cli");
     furi_record_close("cli");
+#else
+    UNUSED(crypto_cli);
 #endif
 #endif
 }
 }

+ 4 - 4
applications/desktop/views/desktop_view_debug.c

@@ -23,7 +23,7 @@ void desktop_debug_render(Canvas* canvas, void* model) {
     const Version* ver;
     const Version* ver;
     char buffer[64];
     char buffer[64];
 
 
-    static const char* headers[] = {"FW Version info:", "Boot Version info:", "Dolphin info:"};
+    static const char* headers[] = {"FW Version info:", "Dolphin info:"};
 
 
     canvas_set_color(canvas, ColorBlack);
     canvas_set_color(canvas, ColorBlack);
     canvas_set_font(canvas, FontPrimary);
     canvas_set_font(canvas, FontPrimary);
@@ -44,8 +44,7 @@ void desktop_debug_render(Canvas* canvas, void* model) {
             my_name ? my_name : "Unknown");
             my_name ? my_name : "Unknown");
         canvas_draw_str(canvas, 5, 19 + STATUS_BAR_Y_SHIFT, buffer);
         canvas_draw_str(canvas, 5, 19 + STATUS_BAR_Y_SHIFT, buffer);
 
 
-        ver = m->screen == DesktopViewStatsBoot ? furi_hal_version_get_bootloader_version() :
-                                                  furi_hal_version_get_firmware_version();
+        ver = furi_hal_version_get_firmware_version();
 
 
         if(!ver) {
         if(!ver) {
             canvas_draw_str(canvas, 5, 29 + STATUS_BAR_Y_SHIFT, "No info");
             canvas_draw_str(canvas, 5, 29 + STATUS_BAR_Y_SHIFT, "No info");
@@ -63,7 +62,8 @@ void desktop_debug_render(Canvas* canvas, void* model) {
         snprintf(
         snprintf(
             buffer,
             buffer,
             sizeof(buffer),
             sizeof(buffer),
-            "%s [%s]",
+            "%s%s [%s]",
+            version_get_dirty_flag(ver) ? "[!] " : "",
             version_get_githash(ver),
             version_get_githash(ver),
             version_get_gitbranchnum(ver));
             version_get_gitbranchnum(ver));
         canvas_draw_str(canvas, 5, 39 + STATUS_BAR_Y_SHIFT, buffer);
         canvas_draw_str(canvas, 5, 39 + STATUS_BAR_Y_SHIFT, buffer);

+ 0 - 1
applications/desktop/views/desktop_view_debug.h

@@ -11,7 +11,6 @@ typedef void (*DesktopDebugViewCallback)(DesktopEvent event, void* context);
 // Debug info
 // Debug info
 typedef enum {
 typedef enum {
     DesktopViewStatsFw,
     DesktopViewStatsFw,
-    DesktopViewStatsBoot,
     DesktopViewStatsMeta,
     DesktopViewStatsMeta,
     DesktopViewStatsTotalCount,
     DesktopViewStatsTotalCount,
 } DesktopViewStatsScreens;
 } DesktopViewStatsScreens;

+ 1 - 1
applications/gui/elements.c

@@ -739,4 +739,4 @@ void elements_text_box(
         }
         }
     }
     }
     canvas_set_font(canvas, FontSecondary);
     canvas_set_font(canvas, FontSecondary);
-}
+}

+ 8 - 5
applications/ibutton/ibutton_cli.c

@@ -6,8 +6,8 @@
 #include <one_wire/ibutton/ibutton_worker.h>
 #include <one_wire/ibutton/ibutton_worker.h>
 #include <one_wire/one_wire_host.h>
 #include <one_wire/one_wire_host.h>
 
 
-void ibutton_cli(Cli* cli, string_t args, void* context);
-void onewire_cli(Cli* cli, string_t args, void* context);
+static void ibutton_cli(Cli* cli, string_t args, void* context);
+static void onewire_cli(Cli* cli, string_t args, void* context);
 
 
 // app cli function
 // app cli function
 void ibutton_on_system_start() {
 void ibutton_on_system_start() {
@@ -16,6 +16,9 @@ void ibutton_on_system_start() {
     cli_add_command(cli, "ikey", CliCommandFlagDefault, ibutton_cli, cli);
     cli_add_command(cli, "ikey", CliCommandFlagDefault, ibutton_cli, cli);
     cli_add_command(cli, "onewire", CliCommandFlagDefault, onewire_cli, cli);
     cli_add_command(cli, "onewire", CliCommandFlagDefault, onewire_cli, cli);
     furi_record_close("cli");
     furi_record_close("cli");
+#else
+    UNUSED(ibutton_cli);
+    UNUSED(onewire_cli);
 #endif
 #endif
 }
 }
 
 
@@ -236,7 +239,7 @@ void ibutton_cli_emulate(Cli* cli, string_t args) {
     ibutton_key_free(key);
     ibutton_key_free(key);
 };
 };
 
 
-void ibutton_cli(Cli* cli, string_t args, void* context) {
+static void ibutton_cli(Cli* cli, string_t args, void* context) {
     string_t cmd;
     string_t cmd;
     string_init(cmd);
     string_init(cmd);
 
 
@@ -264,7 +267,7 @@ void onewire_cli_print_usage() {
     printf("onewire search\r\n");
     printf("onewire search\r\n");
 };
 };
 
 
-void onewire_cli_search(Cli* cli) {
+static void onewire_cli_search(Cli* cli) {
     OneWireHost* onewire = onewire_host_alloc();
     OneWireHost* onewire = onewire_host_alloc();
     uint8_t address[8];
     uint8_t address[8];
     bool done = false;
     bool done = false;
@@ -308,4 +311,4 @@ void onewire_cli(Cli* cli, string_t args, void* context) {
     }
     }
 
 
     string_clear(cmd);
     string_clear(cmd);
-}
+}

+ 2 - 0
applications/infrared/cli/infrared_cli.cpp

@@ -192,5 +192,7 @@ extern "C" void infrared_on_system_start() {
     Cli* cli = (Cli*)furi_record_open("cli");
     Cli* cli = (Cli*)furi_record_open("cli");
     cli_add_command(cli, "ir", CliCommandFlagDefault, infrared_cli_start_ir, NULL);
     cli_add_command(cli, "ir", CliCommandFlagDefault, infrared_cli_start_ir, NULL);
     furi_record_close("cli");
     furi_record_close("cli");
+#else
+    UNUSED(infrared_cli_start_ir);
 #endif
 #endif
 }
 }

+ 1 - 1
applications/infrared/infrared_app_remote_manager.cpp

@@ -13,7 +13,7 @@
 #include <storage/storage.h>
 #include <storage/storage.h>
 #include "infrared_app.h"
 #include "infrared_app.h"
 
 
-static const std::string default_remote_name = "remote";
+static const char* default_remote_name = "remote";
 
 
 std::string InfraredAppRemoteManager::make_full_name(
 std::string InfraredAppRemoteManager::make_full_name(
     const std::string& path,
     const std::string& path,

+ 1 - 1
applications/input/input_cli.c

@@ -50,7 +50,7 @@ static void input_cli_send_print_usage() {
     printf("\t\t <type>\t - one of 'press', 'release', 'short', 'long'\r\n");
     printf("\t\t <type>\t - one of 'press', 'release', 'short', 'long'\r\n");
 }
 }
 
 
-void input_cli_send(Cli* cli, string_t args, Input* input) {
+static void input_cli_send(Cli* cli, string_t args, Input* input) {
     InputEvent event;
     InputEvent event;
     string_t key_str;
     string_t key_str;
     string_init(key_str);
     string_init(key_str);

+ 8 - 6
applications/lfrfid/lfrfid_cli.cpp

@@ -7,7 +7,7 @@
 #include "helpers/rfid_reader.h"
 #include "helpers/rfid_reader.h"
 #include "helpers/rfid_timer_emulator.h"
 #include "helpers/rfid_timer_emulator.h"
 
 
-void lfrfid_cli(Cli* cli, string_t args, void* context);
+static void lfrfid_cli(Cli* cli, string_t args, void* context);
 
 
 // app cli function
 // app cli function
 extern "C" void lfrfid_on_system_start() {
 extern "C" void lfrfid_on_system_start() {
@@ -15,6 +15,8 @@ extern "C" void lfrfid_on_system_start() {
     Cli* cli = static_cast<Cli*>(furi_record_open("cli"));
     Cli* cli = static_cast<Cli*>(furi_record_open("cli"));
     cli_add_command(cli, "rfid", CliCommandFlagDefault, lfrfid_cli, NULL);
     cli_add_command(cli, "rfid", CliCommandFlagDefault, lfrfid_cli, NULL);
     furi_record_close("cli");
     furi_record_close("cli");
+#else
+    UNUSED(lfrfid_cli);
 #endif
 #endif
 }
 }
 
 
@@ -29,7 +31,7 @@ void lfrfid_cli_print_usage() {
     printf("\t<key_data> are hex-formatted\r\n");
     printf("\t<key_data> are hex-formatted\r\n");
 };
 };
 
 
-bool lfrfid_cli_get_key_type(string_t data, LfrfidKeyType* type) {
+static bool lfrfid_cli_get_key_type(string_t data, LfrfidKeyType* type) {
     bool result = false;
     bool result = false;
 
 
     if(string_cmp_str(data, "EM4100") == 0 || string_cmp_str(data, "EM-Marin") == 0) {
     if(string_cmp_str(data, "EM4100") == 0 || string_cmp_str(data, "EM-Marin") == 0) {
@@ -46,7 +48,7 @@ bool lfrfid_cli_get_key_type(string_t data, LfrfidKeyType* type) {
     return result;
     return result;
 }
 }
 
 
-void lfrfid_cli_read(Cli* cli, string_t args) {
+static void lfrfid_cli_read(Cli* cli, string_t args) {
     RfidReader reader;
     RfidReader reader;
     string_t type_string;
     string_t type_string;
     string_init(type_string);
     string_init(type_string);
@@ -97,12 +99,12 @@ void lfrfid_cli_read(Cli* cli, string_t args) {
     string_clear(type_string);
     string_clear(type_string);
 }
 }
 
 
-void lfrfid_cli_write(Cli* cli, string_t args) {
+static void lfrfid_cli_write(Cli* cli, string_t args) {
     // TODO implement rfid write
     // TODO implement rfid write
     printf("Not implemented :(\r\n");
     printf("Not implemented :(\r\n");
 }
 }
 
 
-void lfrfid_cli_emulate(Cli* cli, string_t args) {
+static void lfrfid_cli_emulate(Cli* cli, string_t args) {
     string_t data;
     string_t data;
     string_init(data);
     string_init(data);
     RfidTimerEmulator emulator;
     RfidTimerEmulator emulator;
@@ -144,7 +146,7 @@ void lfrfid_cli_emulate(Cli* cli, string_t args) {
     string_clear(data);
     string_clear(data);
 }
 }
 
 
-void lfrfid_cli(Cli* cli, string_t args, void* context) {
+static void lfrfid_cli(Cli* cli, string_t args, void* context) {
     string_t cmd;
     string_t cmd;
     string_init(cmd);
     string_init(cmd);
 
 

+ 29 - 24
applications/loader/loader.c

@@ -65,33 +65,36 @@ static void loader_cli_print_usage() {
     printf("\topen <Application Name:string>\t - Open application by name\r\n");
     printf("\topen <Application Name:string>\t - Open application by name\r\n");
 }
 }
 
 
-const FlipperApplication* loader_find_application_by_name(const char* name) {
-    const FlipperApplication* application = NULL;
-
-    for(size_t i = 0; i < FLIPPER_APPS_COUNT; i++) {
-        if(strcmp(name, FLIPPER_APPS[i].name) == 0) {
-            application = &FLIPPER_APPS[i];
+static FlipperApplication const* loader_find_application_by_name_in_list(
+    const char* name,
+    const FlipperApplication* list,
+    const uint32_t n_apps) {
+    for(size_t i = 0; i < n_apps; i++) {
+        if(strcmp(name, list[i].name) == 0) {
+            return &list[i];
         }
         }
     }
     }
+    return NULL;
+}
 
 
-    for(size_t i = 0; i < FLIPPER_PLUGINS_COUNT; i++) {
-        if(strcmp(name, FLIPPER_PLUGINS[i].name) == 0) {
-            application = &FLIPPER_PLUGINS[i];
-        }
+const FlipperApplication* loader_find_application_by_name(const char* name) {
+    const FlipperApplication* application = NULL;
+    application = loader_find_application_by_name_in_list(name, FLIPPER_APPS, FLIPPER_APPS_COUNT);
+    if(!application) {
+        application =
+            loader_find_application_by_name_in_list(name, FLIPPER_PLUGINS, FLIPPER_PLUGINS_COUNT);
     }
     }
-
-    for(size_t i = 0; i < FLIPPER_SETTINGS_APPS_COUNT; i++) {
-        if(strcmp(name, FLIPPER_SETTINGS_APPS[i].name) == 0) {
-            application = &FLIPPER_SETTINGS_APPS[i];
-        }
+    if(!application) {
+        application = loader_find_application_by_name_in_list(
+            name, FLIPPER_SETTINGS_APPS, FLIPPER_SETTINGS_APPS_COUNT);
     }
     }
-
-    if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
-        for(size_t i = 0; i < FLIPPER_DEBUG_APPS_COUNT; i++) {
-            if(strcmp(name, FLIPPER_DEBUG_APPS[i].name) == 0) {
-                application = &FLIPPER_DEBUG_APPS[i];
-            }
-        }
+    if(!application) {
+        application = loader_find_application_by_name_in_list(
+            name, FLIPPER_SYSTEM_APPS, FLIPPER_SYSTEM_APPS_COUNT);
+    }
+    if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug) && !application) {
+        application = loader_find_application_by_name_in_list(
+            name, FLIPPER_DEBUG_APPS, FLIPPER_DEBUG_APPS_COUNT);
     }
     }
 
 
     return application;
     return application;
@@ -149,7 +152,7 @@ void loader_cli_list(Cli* cli, string_t args, Loader* instance) {
     }
     }
 }
 }
 
 
-void loader_cli(Cli* cli, string_t args, void* _ctx) {
+static void loader_cli(Cli* cli, string_t args, void* _ctx) {
     furi_assert(_ctx);
     furi_assert(_ctx);
     Loader* instance = _ctx;
     Loader* instance = _ctx;
 
 
@@ -290,6 +293,8 @@ static Loader* loader_alloc() {
 #ifdef SRV_CLI
 #ifdef SRV_CLI
     instance->cli = furi_record_open("cli");
     instance->cli = furi_record_open("cli");
     cli_add_command(instance->cli, "loader", CliCommandFlagDefault, loader_cli, instance);
     cli_add_command(instance->cli, "loader", CliCommandFlagDefault, loader_cli, instance);
+#else
+    UNUSED(loader_cli);
 #endif
 #endif
 
 
     instance->loader_thread = osThreadGetId();
     instance->loader_thread = osThreadGetId();
@@ -483,4 +488,4 @@ int32_t loader_srv(void* p) {
 
 
 FuriPubSub* loader_get_pubsub(Loader* instance) {
 FuriPubSub* loader_get_pubsub(Loader* instance) {
     return instance->pubsub;
     return instance->pubsub;
-}
+}

+ 2 - 0
applications/nfc/nfc_cli.c

@@ -136,5 +136,7 @@ void nfc_on_system_start() {
     Cli* cli = furi_record_open("cli");
     Cli* cli = furi_record_open("cli");
     cli_add_command(cli, "nfc", CliCommandFlagDefault, nfc_cli, NULL);
     cli_add_command(cli, "nfc", CliCommandFlagDefault, nfc_cli, NULL);
     furi_record_close("cli");
     furi_record_close("cli");
+#else
+    UNUSED(nfc_cli);
 #endif
 #endif
 }
 }

+ 2 - 0
applications/power/power_cli.c

@@ -128,5 +128,7 @@ void power_on_system_start() {
     cli_add_command(cli, "power", CliCommandFlagParallelSafe, power_cli, NULL);
     cli_add_command(cli, "power", CliCommandFlagParallelSafe, power_cli, NULL);
 
 
     furi_record_close("cli");
     furi_record_close("cli");
+#else
+    UNUSED(power_cli);
 #endif
 #endif
 }
 }

+ 3 - 4
applications/power/power_service/power_api.c

@@ -1,8 +1,7 @@
 #include "power_i.h"
 #include "power_i.h"
 
 
 #include <furi.h>
 #include <furi.h>
-#include "furi_hal_power.h"
-#include "furi_hal_bootloader.h"
+#include <furi_hal.h>
 
 
 void power_off(Power* power) {
 void power_off(Power* power) {
     furi_hal_power_off();
     furi_hal_power_off();
@@ -15,9 +14,9 @@ void power_off(Power* power) {
 
 
 void power_reboot(PowerBootMode mode) {
 void power_reboot(PowerBootMode mode) {
     if(mode == PowerBootModeNormal) {
     if(mode == PowerBootModeNormal) {
-        furi_hal_bootloader_set_mode(FuriHalBootloaderModeNormal);
+        furi_hal_rtc_set_boot_mode(FuriHalRtcBootModeNormal);
     } else if(mode == PowerBootModeDfu) {
     } else if(mode == PowerBootModeDfu) {
-        furi_hal_bootloader_set_mode(FuriHalBootloaderModeDFU);
+        furi_hal_rtc_set_boot_mode(FuriHalRtcBootModeDfu);
     }
     }
     furi_hal_power_reset();
     furi_hal_power_reset();
 }
 }

+ 27 - 23
applications/rpc/rpc_gui.c

@@ -69,30 +69,34 @@ static void rpc_system_gui_start_screen_stream_process(const PB_Main* request, v
 
 
     RpcSession* session = rpc_gui->session;
     RpcSession* session = rpc_gui->session;
     furi_assert(session);
     furi_assert(session);
-    furi_assert(!rpc_gui->is_streaming);
 
 
-    rpc_send_and_release_empty(session, request->command_id, PB_CommandStatus_OK);
-
-    rpc_gui->is_streaming = true;
-    size_t framebuffer_size = gui_get_framebuffer_size(rpc_gui->gui);
-    // Reusable Frame
-    rpc_gui->transmit_frame = malloc(sizeof(PB_Main));
-    rpc_gui->transmit_frame->which_content = PB_Main_gui_screen_frame_tag;
-    rpc_gui->transmit_frame->command_status = PB_CommandStatus_OK;
-    rpc_gui->transmit_frame->content.gui_screen_frame.data =
-        malloc(PB_BYTES_ARRAY_T_ALLOCSIZE(framebuffer_size));
-    rpc_gui->transmit_frame->content.gui_screen_frame.data->size = framebuffer_size;
-    // Transmission thread for async TX
-    rpc_gui->transmit_thread = furi_thread_alloc();
-    furi_thread_set_name(rpc_gui->transmit_thread, "GuiRpcWorker");
-    furi_thread_set_callback(
-        rpc_gui->transmit_thread, rpc_system_gui_screen_stream_frame_transmit_thread);
-    furi_thread_set_context(rpc_gui->transmit_thread, rpc_gui);
-    furi_thread_set_stack_size(rpc_gui->transmit_thread, 1024);
-    furi_thread_start(rpc_gui->transmit_thread);
-    // GUI framebuffer callback
-    gui_add_framebuffer_callback(
-        rpc_gui->gui, rpc_system_gui_screen_stream_frame_callback, context);
+    if(rpc_gui->is_streaming) {
+        rpc_send_and_release_empty(
+            session, request->command_id, PB_CommandStatus_ERROR_VIRTUAL_DISPLAY_ALREADY_STARTED);
+    } else {
+        rpc_send_and_release_empty(session, request->command_id, PB_CommandStatus_OK);
+
+        rpc_gui->is_streaming = true;
+        size_t framebuffer_size = gui_get_framebuffer_size(rpc_gui->gui);
+        // Reusable Frame
+        rpc_gui->transmit_frame = malloc(sizeof(PB_Main));
+        rpc_gui->transmit_frame->which_content = PB_Main_gui_screen_frame_tag;
+        rpc_gui->transmit_frame->command_status = PB_CommandStatus_OK;
+        rpc_gui->transmit_frame->content.gui_screen_frame.data =
+            malloc(PB_BYTES_ARRAY_T_ALLOCSIZE(framebuffer_size));
+        rpc_gui->transmit_frame->content.gui_screen_frame.data->size = framebuffer_size;
+        // Transmission thread for async TX
+        rpc_gui->transmit_thread = furi_thread_alloc();
+        furi_thread_set_name(rpc_gui->transmit_thread, "GuiRpcWorker");
+        furi_thread_set_callback(
+            rpc_gui->transmit_thread, rpc_system_gui_screen_stream_frame_transmit_thread);
+        furi_thread_set_context(rpc_gui->transmit_thread, rpc_gui);
+        furi_thread_set_stack_size(rpc_gui->transmit_thread, 1024);
+        furi_thread_start(rpc_gui->transmit_thread);
+        // GUI framebuffer callback
+        gui_add_framebuffer_callback(
+            rpc_gui->gui, rpc_system_gui_screen_stream_frame_callback, context);
+    }
 }
 }
 
 
 static void rpc_system_gui_stop_screen_stream_process(const PB_Main* request, void* context) {
 static void rpc_system_gui_stop_screen_stream_process(const PB_Main* request, void* context) {

+ 57 - 2
applications/rpc/rpc_storage.c

@@ -10,10 +10,12 @@
 #include "storage/storage.h"
 #include "storage/storage.h"
 #include <stdint.h>
 #include <stdint.h>
 #include <lib/toolbox/md5.h>
 #include <lib/toolbox/md5.h>
+#include <update_util/lfs_backup.h>
 
 
 #define RPC_TAG "RPC_STORAGE"
 #define RPC_TAG "RPC_STORAGE"
 #define MAX_NAME_LENGTH 255
 #define MAX_NAME_LENGTH 255
-#define MAX_DATA_SIZE 512
+
+static const size_t MAX_DATA_SIZE = 512;
 
 
 typedef enum {
 typedef enum {
     RpcStorageStateIdle = 0,
     RpcStorageStateIdle = 0,
@@ -185,7 +187,7 @@ static void rpc_system_storage_list_root(const PB_Main* request, void* context)
     };
     };
     furi_assert(COUNT_OF(hard_coded_dirs) < COUNT_OF(response.content.storage_list_response.file));
     furi_assert(COUNT_OF(hard_coded_dirs) < COUNT_OF(response.content.storage_list_response.file));
 
 
-    for(int i = 0; i < COUNT_OF(hard_coded_dirs); ++i) {
+    for(uint32_t i = 0; i < COUNT_OF(hard_coded_dirs); ++i) {
         ++response.content.storage_list_response.file_count;
         ++response.content.storage_list_response.file_count;
         response.content.storage_list_response.file[i].data = NULL;
         response.content.storage_list_response.file[i].data = NULL;
         response.content.storage_list_response.file[i].size = 0;
         response.content.storage_list_response.file[i].size = 0;
@@ -538,6 +540,53 @@ static void rpc_system_storage_rename_process(const PB_Main* request, void* cont
     rpc_send_and_release_empty(session, request->command_id, status);
     rpc_send_and_release_empty(session, request->command_id, status);
 }
 }
 
 
+static void rpc_system_storage_backup_create_process(const PB_Main* request, void* context) {
+    furi_assert(request);
+    furi_assert(request->which_content == PB_Main_storage_backup_create_request_tag);
+
+    RpcSession* session = (RpcSession*)context;
+    furi_assert(session);
+
+    PB_Main* response = malloc(sizeof(PB_Main));
+    response->command_id = request->command_id;
+    response->has_next = false;
+
+    Storage* fs_api = furi_record_open("storage");
+
+    bool backup_ok =
+        lfs_backup_create(fs_api, request->content.storage_backup_create_request.archive_path);
+    response->command_status = backup_ok ? PB_CommandStatus_OK : PB_CommandStatus_ERROR;
+
+    furi_record_close("storage");
+
+    rpc_send_and_release(session, response);
+    free(response);
+}
+
+static void rpc_system_storage_backup_restore_process(const PB_Main* request, void* context) {
+    furi_assert(request);
+    furi_assert(request->which_content == PB_Main_storage_backup_restore_request_tag);
+
+    RpcSession* session = (RpcSession*)context;
+    furi_assert(session);
+
+    PB_Main* response = malloc(sizeof(PB_Main));
+    response->command_id = request->command_id;
+    response->has_next = false;
+    response->command_status = PB_CommandStatus_OK;
+
+    Storage* fs_api = furi_record_open("storage");
+
+    bool backup_ok =
+        lfs_backup_unpack(fs_api, request->content.storage_backup_restore_request.archive_path);
+    response->command_status = backup_ok ? PB_CommandStatus_OK : PB_CommandStatus_ERROR;
+
+    furi_record_close("storage");
+
+    rpc_send_and_release(session, response);
+    free(response);
+}
+
 void* rpc_system_storage_alloc(RpcSession* session) {
 void* rpc_system_storage_alloc(RpcSession* session) {
     furi_assert(session);
     furi_assert(session);
 
 
@@ -579,6 +628,12 @@ void* rpc_system_storage_alloc(RpcSession* session) {
     rpc_handler.message_handler = rpc_system_storage_rename_process;
     rpc_handler.message_handler = rpc_system_storage_rename_process;
     rpc_add_handler(session, PB_Main_storage_rename_request_tag, &rpc_handler);
     rpc_add_handler(session, PB_Main_storage_rename_request_tag, &rpc_handler);
 
 
+    rpc_handler.message_handler = rpc_system_storage_backup_create_process;
+    rpc_add_handler(session, PB_Main_storage_backup_create_request_tag, &rpc_handler);
+
+    rpc_handler.message_handler = rpc_system_storage_backup_restore_process;
+    rpc_add_handler(session, PB_Main_storage_backup_restore_request_tag, &rpc_handler);
+
     return rpc_storage;
     return rpc_storage;
 }
 }
 
 

+ 29 - 1
applications/rpc/rpc_system.c

@@ -3,6 +3,7 @@
 #include <power/power_service/power.h>
 #include <power/power_service/power.h>
 #include <notification/notification_messages.h>
 #include <notification/notification_messages.h>
 #include <protobuf_version.h>
 #include <protobuf_version.h>
+#include <update_util/update_operation.h>
 
 
 #include "rpc_i.h"
 #include "rpc_i.h"
 
 
@@ -242,6 +243,28 @@ static void rpc_system_system_get_power_info_process(const PB_Main* request, voi
     free(response);
     free(response);
 }
 }
 
 
+#ifdef APP_UPDATER
+static void rpc_system_system_update_request_process(const PB_Main* request, void* context) {
+    furi_assert(request);
+    furi_assert(request->which_content == PB_Main_system_update_request_tag);
+
+    RpcSession* session = (RpcSession*)context;
+    furi_assert(session);
+
+    bool update_prepare_result =
+        update_operation_prepare(request->content.system_update_request.update_folder) ==
+        UpdatePrepareResultOK;
+
+    PB_Main* response = malloc(sizeof(PB_Main));
+    response->command_id = request->command_id;
+    response->has_next = false;
+    response->command_status = update_prepare_result ? PB_CommandStatus_OK :
+                                                       PB_CommandStatus_ERROR_INVALID_PARAMETERS;
+    rpc_send_and_release(session, response);
+    free(response);
+}
+#endif
+
 void* rpc_system_system_alloc(RpcSession* session) {
 void* rpc_system_system_alloc(RpcSession* session) {
     RpcHandler rpc_handler = {
     RpcHandler rpc_handler = {
         .message_handler = NULL,
         .message_handler = NULL,
@@ -276,5 +299,10 @@ void* rpc_system_system_alloc(RpcSession* session) {
     rpc_handler.message_handler = rpc_system_system_get_power_info_process;
     rpc_handler.message_handler = rpc_system_system_get_power_info_process;
     rpc_add_handler(session, PB_Main_system_power_info_request_tag, &rpc_handler);
     rpc_add_handler(session, PB_Main_system_power_info_request_tag, &rpc_handler);
 
 
+#ifdef APP_UPDATER
+    rpc_handler.message_handler = rpc_system_system_update_request_process;
+    rpc_add_handler(session, PB_Main_system_update_request_tag, &rpc_handler);
+#endif
+
     return NULL;
     return NULL;
-}
+}

+ 2 - 0
applications/storage/storage.c

@@ -40,7 +40,9 @@ Storage* storage_app_alloc() {
         storage_data_init(&app->storage[i]);
         storage_data_init(&app->storage[i]);
     }
     }
 
 
+#ifndef FURI_RAM_EXEC
     storage_int_init(&app->storage[ST_INT]);
     storage_int_init(&app->storage[ST_INT]);
+#endif
     storage_ext_init(&app->storage[ST_EXT]);
     storage_ext_init(&app->storage[ST_EXT]);
 
 
     // sd icon gui
     // sd icon gui

+ 17 - 1
applications/storage/storage.h

@@ -263,6 +263,22 @@ FS_Error storage_sd_info(Storage* api, SDInfo* info);
  */
  */
 FS_Error storage_sd_status(Storage* api);
 FS_Error storage_sd_status(Storage* api);
 
 
+/******************* Internal LFS Functions *******************/
+
+/** Backs up internal storage to a tar archive
+ * @param api pointer to the api
+ * @param dstmane destination archive path
+ * @return FS_Error operation result
+ */
+FS_Error storage_int_backup(Storage* api, const char* dstname);
+
+/** Restores internal storage from a tar archive
+ * @param api pointer to the api
+ * @param dstmane archive path
+ * @return FS_Error operation result
+ */
+FS_Error storage_int_restore(Storage* api, const char* dstname);
+
 /***************** Simplified Functions ******************/
 /***************** Simplified Functions ******************/
 
 
 /**
 /**
@@ -309,4 +325,4 @@ void storage_get_next_filename(
 
 
 #ifdef __cplusplus
 #ifdef __cplusplus
 }
 }
-#endif
+#endif

+ 3 - 1
applications/storage/storage_cli.c

@@ -521,7 +521,7 @@ void storage_cli(Cli* cli, string_t args, void* context) {
     string_clear(cmd);
     string_clear(cmd);
 }
 }
 
 
-void storage_cli_factory_reset(Cli* cli, string_t args, void* context) {
+static void storage_cli_factory_reset(Cli* cli, string_t args, void* context) {
     printf("All data will be lost. Are you sure (y/n)?\r\n");
     printf("All data will be lost. Are you sure (y/n)?\r\n");
     char c = cli_getc(cli);
     char c = cli_getc(cli);
     if(c == 'y' || c == 'Y') {
     if(c == 'y' || c == 'Y') {
@@ -540,5 +540,7 @@ void storage_on_system_start() {
     cli_add_command(
     cli_add_command(
         cli, "factory_reset", CliCommandFlagParallelSafe, storage_cli_factory_reset, NULL);
         cli, "factory_reset", CliCommandFlagParallelSafe, storage_cli_factory_reset, NULL);
     furi_record_close("cli");
     furi_record_close("cli");
+#else
+    UNUSED(storage_cli_factory_reset);
 #endif
 #endif
 }
 }

+ 22 - 0
applications/storage/storage_internal_api.c

@@ -0,0 +1,22 @@
+#include <furi/record.h>
+#include <m-string.h>
+#include "storage.h"
+#include <toolbox/tar/tar_archive.h>
+
+#define INT_PATH "/int"
+
+FS_Error storage_int_backup(Storage* api, const char* dstname) {
+    TarArchive* archive = tar_archive_alloc(api);
+    bool success = tar_archive_open(archive, dstname, TAR_OPEN_MODE_WRITE) &&
+                   tar_archive_add_dir(archive, INT_PATH, "") && tar_archive_finalize(archive);
+    tar_archive_free(archive);
+    return success ? FSE_OK : FSE_INTERNAL;
+}
+
+FS_Error storage_int_restore(Storage* api, const char* srcname) {
+    TarArchive* archive = tar_archive_alloc(api);
+    bool success = tar_archive_open(archive, srcname, TAR_OPEN_MODE_READ) &&
+                   tar_archive_unpack_to(archive, INT_PATH);
+    tar_archive_free(archive);
+    return success ? FSE_OK : FSE_INTERNAL;
+}

+ 4 - 0
applications/storage/storage_processing.c

@@ -20,7 +20,11 @@ static StorageData* storage_get_storage_by_type(Storage* app, StorageType type)
 }
 }
 
 
 static bool storage_type_is_not_valid(StorageType type) {
 static bool storage_type_is_not_valid(StorageType type) {
+#ifdef FURI_RAM_EXEC
+    return type != ST_EXT;
+#else
     return type >= ST_ERROR;
     return type >= ST_ERROR;
+#endif
 }
 }
 
 
 static StorageData* get_storage_by_file(File* file, StorageData* storages) {
 static StorageData* get_storage_by_file(File* file, StorageData* storages) {

+ 46 - 2
applications/storage/storages/storage_ext.c

@@ -54,10 +54,12 @@ static bool sd_mount_card(StorageData* storage, bool notify) {
             SDError status = f_mount(sd_data->fs, sd_data->path, 1);
             SDError status = f_mount(sd_data->fs, sd_data->path, 1);
 
 
             if(status == FR_OK || status == FR_NO_FILESYSTEM) {
             if(status == FR_OK || status == FR_NO_FILESYSTEM) {
+#ifndef FURI_RAM_EXEC
                 FATFS* fs;
                 FATFS* fs;
                 uint32_t free_clusters;
                 uint32_t free_clusters;
 
 
                 status = f_getfree(sd_data->path, &free_clusters, &fs);
                 status = f_getfree(sd_data->path, &free_clusters, &fs);
+#endif
 
 
                 if(status == FR_OK || status == FR_NO_FILESYSTEM) {
                 if(status == FR_OK || status == FR_NO_FILESYSTEM) {
                     result = true;
                     result = true;
@@ -110,6 +112,9 @@ FS_Error sd_unmount_card(StorageData* storage) {
 }
 }
 
 
 FS_Error sd_format_card(StorageData* storage) {
 FS_Error sd_format_card(StorageData* storage) {
+#ifdef FURI_RAM_EXEC
+    return FSE_NOT_READY;
+#else
     uint8_t* work_area;
     uint8_t* work_area;
     SDData* sd_data = storage->data;
     SDData* sd_data = storage->data;
     SDError error;
     SDError error;
@@ -135,11 +140,14 @@ FS_Error sd_format_card(StorageData* storage) {
     storage_data_unlock(storage);
     storage_data_unlock(storage);
 
 
     return storage_ext_parse_error(error);
     return storage_ext_parse_error(error);
+#endif
 }
 }
 
 
 FS_Error sd_card_info(StorageData* storage, SDInfo* sd_info) {
 FS_Error sd_card_info(StorageData* storage, SDInfo* sd_info) {
+#ifndef FURI_RAM_EXEC
     uint32_t free_clusters, free_sectors, total_sectors;
     uint32_t free_clusters, free_sectors, total_sectors;
     FATFS* fs;
     FATFS* fs;
+#endif
     SDData* sd_data = storage->data;
     SDData* sd_data = storage->data;
     SDError error;
     SDError error;
 
 
@@ -150,20 +158,32 @@ FS_Error sd_card_info(StorageData* storage, SDInfo* sd_info) {
     storage_data_lock(storage);
     storage_data_lock(storage);
     error = f_getlabel(sd_data->path, sd_info->label, NULL);
     error = f_getlabel(sd_data->path, sd_info->label, NULL);
     if(error == FR_OK) {
     if(error == FR_OK) {
+#ifndef FURI_RAM_EXEC
         error = f_getfree(sd_data->path, &free_clusters, &fs);
         error = f_getfree(sd_data->path, &free_clusters, &fs);
+#endif
     }
     }
     storage_data_unlock(storage);
     storage_data_unlock(storage);
 
 
     if(error == FR_OK) {
     if(error == FR_OK) {
         // calculate size
         // calculate size
+#ifndef FURI_RAM_EXEC
         total_sectors = (fs->n_fatent - 2) * fs->csize;
         total_sectors = (fs->n_fatent - 2) * fs->csize;
         free_sectors = free_clusters * fs->csize;
         free_sectors = free_clusters * fs->csize;
+#endif
 
 
         uint16_t sector_size = _MAX_SS;
         uint16_t sector_size = _MAX_SS;
 #if _MAX_SS != _MIN_SS
 #if _MAX_SS != _MIN_SS
         sector_size = fs->ssize;
         sector_size = fs->ssize;
 #endif
 #endif
 
 
+#ifdef FURI_RAM_EXEC
+        sd_info->fs_type = 0;
+        sd_info->kb_total = 0;
+        sd_info->kb_free = 0;
+        sd_info->cluster_size = 512;
+        sd_info->sector_size = sector_size;
+#else
+        sd_info->fs_type = fs->fs_type;
         switch(fs->fs_type) {
         switch(fs->fs_type) {
         case FS_FAT12:
         case FS_FAT12:
             sd_info->fs_type = FST_FAT12;
             sd_info->fs_type = FST_FAT12;
@@ -177,7 +197,6 @@ FS_Error sd_card_info(StorageData* storage, SDInfo* sd_info) {
         case FS_EXFAT:
         case FS_EXFAT:
             sd_info->fs_type = FST_EXFAT;
             sd_info->fs_type = FST_EXFAT;
             break;
             break;
-
         default:
         default:
             sd_info->fs_type = FST_UNKNOWN;
             sd_info->fs_type = FST_UNKNOWN;
             break;
             break;
@@ -187,6 +206,7 @@ FS_Error sd_card_info(StorageData* storage, SDInfo* sd_info) {
         sd_info->kb_free = free_sectors / 1024 * sector_size;
         sd_info->kb_free = free_sectors / 1024 * sector_size;
         sd_info->cluster_size = fs->csize;
         sd_info->cluster_size = fs->csize;
         sd_info->sector_size = sector_size;
         sd_info->sector_size = sector_size;
+#endif
     }
     }
 
 
     return storage_ext_parse_error(error);
     return storage_ext_parse_error(error);
@@ -328,12 +348,16 @@ static uint16_t
 
 
 static uint16_t
 static uint16_t
     storage_ext_file_write(void* ctx, File* file, const void* buff, uint16_t const bytes_to_write) {
     storage_ext_file_write(void* ctx, File* file, const void* buff, uint16_t const bytes_to_write) {
+#ifdef FURI_RAM_EXEC
+    return FSE_NOT_READY;
+#else
     StorageData* storage = ctx;
     StorageData* storage = ctx;
     SDFile* file_data = storage_get_storage_file_data(file, storage);
     SDFile* file_data = storage_get_storage_file_data(file, storage);
     uint16_t bytes_written = 0;
     uint16_t bytes_written = 0;
     file->internal_error_id = f_write(file_data, buff, bytes_to_write, &bytes_written);
     file->internal_error_id = f_write(file_data, buff, bytes_to_write, &bytes_written);
     file->error_id = storage_ext_parse_error(file->internal_error_id);
     file->error_id = storage_ext_parse_error(file->internal_error_id);
     return bytes_written;
     return bytes_written;
+#endif
 }
 }
 
 
 static bool
 static bool
@@ -364,21 +388,29 @@ static uint64_t storage_ext_file_tell(void* ctx, File* file) {
 }
 }
 
 
 static bool storage_ext_file_truncate(void* ctx, File* file) {
 static bool storage_ext_file_truncate(void* ctx, File* file) {
+#ifdef FURI_RAM_EXEC
+    return FSE_NOT_READY;
+#else
     StorageData* storage = ctx;
     StorageData* storage = ctx;
     SDFile* file_data = storage_get_storage_file_data(file, storage);
     SDFile* file_data = storage_get_storage_file_data(file, storage);
 
 
     file->internal_error_id = f_truncate(file_data);
     file->internal_error_id = f_truncate(file_data);
     file->error_id = storage_ext_parse_error(file->internal_error_id);
     file->error_id = storage_ext_parse_error(file->internal_error_id);
     return (file->error_id == FSE_OK);
     return (file->error_id == FSE_OK);
+#endif
 }
 }
 
 
 static bool storage_ext_file_sync(void* ctx, File* file) {
 static bool storage_ext_file_sync(void* ctx, File* file) {
+#ifdef FURI_RAM_EXEC
+    return FSE_NOT_READY;
+#else
     StorageData* storage = ctx;
     StorageData* storage = ctx;
     SDFile* file_data = storage_get_storage_file_data(file, storage);
     SDFile* file_data = storage_get_storage_file_data(file, storage);
 
 
     file->internal_error_id = f_sync(file_data);
     file->internal_error_id = f_sync(file_data);
     file->error_id = storage_ext_parse_error(file->internal_error_id);
     file->error_id = storage_ext_parse_error(file->internal_error_id);
     return (file->error_id == FSE_OK);
     return (file->error_id == FSE_OK);
+#endif
 }
 }
 
 
 static uint64_t storage_ext_file_size(void* ctx, File* file) {
 static uint64_t storage_ext_file_size(void* ctx, File* file) {
@@ -479,13 +511,21 @@ static FS_Error storage_ext_common_stat(void* ctx, const char* path, FileInfo* f
 }
 }
 
 
 static FS_Error storage_ext_common_remove(void* ctx, const char* path) {
 static FS_Error storage_ext_common_remove(void* ctx, const char* path) {
+#ifdef FURI_RAM_EXEC
+    return FSE_NOT_READY;
+#else
     SDError result = f_unlink(path);
     SDError result = f_unlink(path);
     return storage_ext_parse_error(result);
     return storage_ext_parse_error(result);
+#endif
 }
 }
 
 
 static FS_Error storage_ext_common_mkdir(void* ctx, const char* path) {
 static FS_Error storage_ext_common_mkdir(void* ctx, const char* path) {
+#ifdef FURI_RAM_EXEC
+    return FSE_NOT_READY;
+#else
     SDError result = f_mkdir(path);
     SDError result = f_mkdir(path);
     return storage_ext_parse_error(result);
     return storage_ext_parse_error(result);
+#endif
 }
 }
 
 
 static FS_Error storage_ext_common_fs_info(
 static FS_Error storage_ext_common_fs_info(
@@ -493,6 +533,9 @@ static FS_Error storage_ext_common_fs_info(
     const char* fs_path,
     const char* fs_path,
     uint64_t* total_space,
     uint64_t* total_space,
     uint64_t* free_space) {
     uint64_t* free_space) {
+#ifdef FURI_RAM_EXEC
+    return FSE_NOT_READY;
+#else
     StorageData* storage = ctx;
     StorageData* storage = ctx;
     SDData* sd_data = storage->data;
     SDData* sd_data = storage->data;
 
 
@@ -519,6 +562,7 @@ static FS_Error storage_ext_common_fs_info(
     }
     }
 
 
     return storage_ext_parse_error(fresult);
     return storage_ext_parse_error(fresult);
+#endif
 }
 }
 
 
 /******************* Init Storage *******************/
 /******************* Init Storage *******************/
@@ -566,4 +610,4 @@ void storage_ext_init(StorageData* storage) {
 
 
     // do not notify on first launch, notifications app is waiting for our thread to read settings
     // do not notify on first launch, notifications app is waiting for our thread to read settings
     storage_ext_tick_internal(storage, false);
     storage_ext_tick_internal(storage, false);
-}
+}

+ 3 - 1
applications/subghz/subghz_cli.c

@@ -571,7 +571,7 @@ static void subghz_cli_command_chat(Cli* cli, string_t args) {
     printf("\r\nExit chat\r\n");
     printf("\r\nExit chat\r\n");
 }
 }
 
 
-void subghz_cli_command(Cli* cli, string_t args, void* context) {
+static void subghz_cli_command(Cli* cli, string_t args, void* context) {
     string_t cmd;
     string_t cmd;
     string_init(cmd);
     string_init(cmd);
 
 
@@ -630,5 +630,7 @@ void subghz_on_system_start() {
     cli_add_command(cli, "subghz", CliCommandFlagDefault, subghz_cli_command, NULL);
     cli_add_command(cli, "subghz", CliCommandFlagDefault, subghz_cli_command, NULL);
 
 
     furi_record_close("cli");
     furi_record_close("cli");
+#else
+    UNUSED(subghz_cli_command);
 #endif
 #endif
 }
 }

+ 134 - 0
applications/updater/cli/updater_cli.c

@@ -0,0 +1,134 @@
+
+#include <furi.h>
+#include <furi_hal.h>
+#include <m-string.h>
+#include <cli/cli.h>
+#include <storage/storage.h>
+#include <loader/loader.h>
+#include <toolbox/path.h>
+#include <toolbox/tar/tar_archive.h>
+#include <toolbox/args.h>
+#include <update_util/update_manifest.h>
+#include <update_util/lfs_backup.h>
+#include <update_util/update_operation.h>
+
+typedef void (*cmd_handler)(string_t args);
+typedef struct {
+    const char* command;
+    const cmd_handler handler;
+} CliSubcommand;
+
+static void updater_cli_install(string_t manifest_path) {
+    printf("Verifying update package at '%s'\r\n", string_get_cstr(manifest_path));
+
+    UpdatePrepareResult result = update_operation_prepare(string_get_cstr(manifest_path));
+    if(result != UpdatePrepareResultOK) {
+        printf(
+            "Error: %s. Stopping update.\r\n",
+            update_operation_describe_preparation_result(result));
+        return;
+    }
+    printf("OK.\r\nRestarting to apply update. BRB\r\n");
+    osDelay(100);
+    furi_hal_power_reset();
+}
+
+static void updater_cli_backup(string_t args) {
+    printf("Backup /int to '%s'\r\n", string_get_cstr(args));
+    Storage* storage = furi_record_open("storage");
+    bool success = lfs_backup_create(storage, string_get_cstr(args));
+    furi_record_close("storage");
+    printf("Result: %s\r\n", success ? "OK" : "FAIL");
+}
+
+static void updater_cli_restore(string_t args) {
+    printf("Restore /int from '%s'\r\n", string_get_cstr(args));
+    Storage* storage = furi_record_open("storage");
+    bool success = lfs_backup_unpack(storage, string_get_cstr(args));
+    furi_record_close("storage");
+    printf("Result: %s\r\n", success ? "OK" : "FAIL");
+}
+
+static void updater_cli_help(string_t args) {
+    UNUSED(args);
+    printf("Commands:\r\n"
+           "\tinstall /ext/update/PACKAGE/update.fuf - verify & apply update package\r\n"
+           "\tbackup /ext/path/to/backup.tar - create internal storage backup\r\n"
+           "\trestore /ext/path/to/backup.tar - restore internal storage backup\r\n");
+}
+
+static const CliSubcommand update_cli_subcommands[] = {
+    {.command = "install", .handler = updater_cli_install},
+    {.command = "backup", .handler = updater_cli_backup},
+    {.command = "restore", .handler = updater_cli_restore},
+    {.command = "help", .handler = updater_cli_help},
+};
+
+static void updater_cli_ep(Cli* cli, string_t args, void* context) {
+    string_t subcommand;
+    string_init(subcommand);
+    if(!args_read_string_and_trim(args, subcommand) || string_empty_p(args)) {
+        updater_cli_help(args);
+        string_clear(subcommand);
+        return;
+    }
+    for(size_t idx = 0; idx < COUNT_OF(update_cli_subcommands); ++idx) {
+        const CliSubcommand* subcmd_def = &update_cli_subcommands[idx];
+        if(string_cmp_str(subcommand, subcmd_def->command) == 0) {
+            string_clear(subcommand);
+            subcmd_def->handler(args);
+            return;
+        }
+    }
+    string_clear(subcommand);
+    updater_cli_help(args);
+}
+
+static int32_t updater_spawner_thread_worker(void* arg) {
+    Loader* loader = furi_record_open("loader");
+    loader_start(loader, "UpdaterApp", NULL);
+    furi_record_close("loader");
+    return 0;
+}
+
+static void updater_spawner_thread_cleanup(FuriThreadState state, void* context) {
+    FuriThread* thread = context;
+    if(state == FuriThreadStateStopped) {
+        furi_thread_free(thread);
+    }
+}
+
+static void updater_start_app() {
+    FuriHalRtcBootMode mode = furi_hal_rtc_get_boot_mode();
+    if((mode != FuriHalRtcBootModePreUpdate) && (mode != FuriHalRtcBootModePostUpdate)) {
+        return;
+    }
+
+    /* We need to spawn a separate thread, because these callbacks are executed 
+     * inside loader process, at startup. 
+     * So, accessing its record would cause a deadlock 
+     */
+    FuriThread* thread = furi_thread_alloc();
+
+    furi_thread_set_name(thread, "UpdateAppSpawner");
+    furi_thread_set_stack_size(thread, 768);
+    furi_thread_set_callback(thread, updater_spawner_thread_worker);
+    furi_thread_set_state_callback(thread, updater_spawner_thread_cleanup);
+    furi_thread_set_state_context(thread, thread);
+    furi_thread_start(thread);
+}
+
+void updater_on_system_start() {
+#ifdef SRV_CLI
+    Cli* cli = (Cli*)furi_record_open("cli");
+    cli_add_command(cli, "update", CliCommandFlagDefault, updater_cli_ep, NULL);
+    furi_record_close("cli");
+#else
+    UNUSED(updater_cli_ep);
+#endif
+#ifndef FURI_RAM_EXEC
+    updater_start_app();
+#else
+    UNUSED(updater_start_app);
+#endif
+}

+ 30 - 0
applications/updater/scenes/updater_scene.c

@@ -0,0 +1,30 @@
+#include "updater_scene.h"
+
+// Generate scene on_enter handlers array
+#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,
+void (*const updater_on_enter_handlers[])(void*) = {
+#include "updater_scene_config.h"
+};
+#undef ADD_SCENE
+
+// Generate scene on_event handlers array
+#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,
+bool (*const updater_on_event_handlers[])(void* context, SceneManagerEvent event) = {
+#include "updater_scene_config.h"
+};
+#undef ADD_SCENE
+
+// Generate scene on_exit handlers array
+#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,
+void (*const updater_on_exit_handlers[])(void* context) = {
+#include "updater_scene_config.h"
+};
+#undef ADD_SCENE
+
+// Initialize scene handlers configuration structure
+const SceneManagerHandlers updater_scene_handlers = {
+    .on_enter_handlers = updater_on_enter_handlers,
+    .on_event_handlers = updater_on_event_handlers,
+    .on_exit_handlers = updater_on_exit_handlers,
+    .scene_num = UpdaterSceneNum,
+};

+ 29 - 0
applications/updater/scenes/updater_scene.h

@@ -0,0 +1,29 @@
+#pragma once
+
+#include <gui/scene_manager.h>
+
+// Generate scene id and total number
+#define ADD_SCENE(prefix, name, id) UpdaterScene##id,
+typedef enum {
+#include "updater_scene_config.h"
+    UpdaterSceneNum,
+} UpdaterScene;
+#undef ADD_SCENE
+
+extern const SceneManagerHandlers updater_scene_handlers;
+
+// Generate scene on_enter handlers declaration
+#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);
+#include "updater_scene_config.h"
+#undef ADD_SCENE
+
+// Generate scene on_event handlers declaration
+#define ADD_SCENE(prefix, name, id) \
+    bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event);
+#include "updater_scene_config.h"
+#undef ADD_SCENE
+
+// Generate scene on_exit handlers declaration
+#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context);
+#include "updater_scene_config.h"
+#undef ADD_SCENE

+ 5 - 0
applications/updater/scenes/updater_scene_config.h

@@ -0,0 +1,5 @@
+ADD_SCENE(updater, main, Main)
+#ifndef FURI_RAM_EXEC
+ADD_SCENE(updater, loadcfg, LoadCfg)
+ADD_SCENE(updater, error, Error)
+#endif

+ 65 - 0
applications/updater/scenes/updater_scene_error.c

@@ -0,0 +1,65 @@
+#include "updater/updater_i.h"
+#include "updater_scene.h"
+#include <update_util/update_operation.h>
+
+void updater_scene_error_callback(GuiButtonType result, InputType type, void* context) {
+    furi_assert(context);
+    Updater* updater = context;
+    if(type != InputTypeShort) {
+        return;
+    }
+
+    if(result == GuiButtonTypeLeft) {
+        view_dispatcher_send_custom_event(
+            updater->view_dispatcher, UpdaterCustomEventCancelUpdate);
+    }
+}
+
+void updater_scene_error_on_enter(void* context) {
+    Updater* updater = (Updater*)context;
+
+    widget_add_button_element(
+        updater->widget, GuiButtonTypeLeft, "Exit", updater_scene_error_callback, updater);
+
+    widget_add_string_multiline_element(
+        updater->widget, 64, 13, AlignCenter, AlignCenter, FontPrimary, "Error");
+
+    widget_add_string_multiline_element(
+        updater->widget,
+        64,
+        33,
+        AlignCenter,
+        AlignCenter,
+        FontPrimary,
+        update_operation_describe_preparation_result(updater->preparation_result));
+
+    view_dispatcher_switch_to_view(updater->view_dispatcher, UpdaterViewWidget);
+}
+
+bool updater_scene_error_on_event(void* context, SceneManagerEvent event) {
+    Updater* updater = (Updater*)context;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeBack) {
+        view_dispatcher_stop(updater->view_dispatcher);
+        consumed = true;
+    } else if(event.type == SceneManagerEventTypeCustom) {
+        switch(event.event) {
+        case UpdaterCustomEventCancelUpdate:
+            view_dispatcher_stop(updater->view_dispatcher);
+            consumed = true;
+            break;
+        default:
+            break;
+        }
+    }
+
+    return consumed;
+}
+
+void updater_scene_error_on_exit(void* context) {
+    Updater* updater = (Updater*)context;
+
+    widget_reset(updater->widget);
+    free(updater->pending_update);
+}

+ 105 - 0
applications/updater/scenes/updater_scene_loadcfg.c

@@ -0,0 +1,105 @@
+#include "updater/updater_i.h"
+#include "updater_scene.h"
+
+#include <update_util/update_operation.h>
+#include <furi_hal.h>
+
+void updater_scene_loadcfg_apply_callback(GuiButtonType result, InputType type, void* context) {
+    furi_assert(context);
+    Updater* updater = context;
+    if(type != InputTypeShort) {
+        return;
+    }
+
+    if(result == GuiButtonTypeRight) {
+        view_dispatcher_send_custom_event(updater->view_dispatcher, UpdaterCustomEventStartUpdate);
+    } else if(result == GuiButtonTypeLeft) {
+        view_dispatcher_send_custom_event(
+            updater->view_dispatcher, UpdaterCustomEventCancelUpdate);
+    }
+}
+
+void updater_scene_loadcfg_on_enter(void* context) {
+    Updater* updater = (Updater*)context;
+    UpdaterManifestProcessingState* pending_upd = updater->pending_update =
+        malloc(sizeof(UpdaterManifestProcessingState));
+    pending_upd->manifest = update_manifest_alloc();
+
+    if(update_manifest_init(pending_upd->manifest, string_get_cstr(updater->startup_arg))) {
+        widget_add_string_element(
+            updater->widget, 64, 12, AlignCenter, AlignCenter, FontPrimary, "Update");
+
+        widget_add_string_multiline_element(
+            updater->widget,
+            64,
+            32,
+            AlignCenter,
+            AlignCenter,
+            FontSecondary,
+            string_get_cstr(pending_upd->manifest->version));
+
+        widget_add_button_element(
+            updater->widget,
+            GuiButtonTypeRight,
+            "Install",
+            updater_scene_loadcfg_apply_callback,
+            updater);
+    } else {
+        widget_add_string_element(
+            updater->widget, 64, 24, AlignCenter, AlignCenter, FontPrimary, "Invalid manifest");
+    }
+
+    widget_add_button_element(
+        updater->widget,
+        GuiButtonTypeLeft,
+        "Cancel",
+        updater_scene_loadcfg_apply_callback,
+        updater);
+
+    view_dispatcher_switch_to_view(updater->view_dispatcher, UpdaterViewWidget);
+}
+
+bool updater_scene_loadcfg_on_event(void* context, SceneManagerEvent event) {
+    Updater* updater = (Updater*)context;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeBack) {
+        view_dispatcher_stop(updater->view_dispatcher);
+        consumed = true;
+    } else if(event.type == SceneManagerEventTypeCustom) {
+        switch(event.event) {
+        case UpdaterCustomEventStartUpdate:
+            updater->preparation_result =
+                update_operation_prepare(string_get_cstr(updater->startup_arg));
+            if(updater->preparation_result == UpdatePrepareResultOK) {
+                furi_hal_power_reset();
+            } else {
+#ifndef FURI_RAM_EXEC
+                scene_manager_next_scene(updater->scene_manager, UpdaterSceneError);
+#endif
+            }
+            consumed = true;
+            break;
+        case UpdaterCustomEventCancelUpdate:
+            view_dispatcher_stop(updater->view_dispatcher);
+            consumed = true;
+            break;
+        default:
+            break;
+        }
+    }
+
+    return consumed;
+}
+
+void updater_scene_loadcfg_on_exit(void* context) {
+    Updater* updater = (Updater*)context;
+
+    if(updater->pending_update) {
+        update_manifest_free(updater->pending_update->manifest);
+        string_clear(updater->pending_update->message);
+    }
+
+    widget_reset(updater->widget);
+    free(updater->pending_update);
+}

+ 106 - 0
applications/updater/scenes/updater_scene_main.c

@@ -0,0 +1,106 @@
+#include <furi.h>
+#include <furi_hal.h>
+#include <applications.h>
+#include <storage/storage.h>
+
+#include "updater/updater_i.h"
+#include "updater/views/updater_main.h"
+#include "updater_scene.h"
+
+static void sd_mount_callback(const void* message, void* context) {
+    Updater* updater = context;
+    const StorageEvent* new_event = message;
+
+    switch(new_event->type) {
+    case StorageEventTypeCardMount:
+        view_dispatcher_send_custom_event(updater->view_dispatcher, UpdaterCustomEventStartUpdate);
+        break;
+    case StorageEventTypeCardUnmount:
+        view_dispatcher_send_custom_event(updater->view_dispatcher, UpdaterCustomEventSdUnmounted);
+        break;
+    default:
+        break;
+    }
+}
+
+void updater_scene_main_on_enter(void* context) {
+    Updater* updater = (Updater*)context;
+    UpdaterMainView* main_view = updater->main_view;
+
+    FuriPubSubSubscription* sub =
+        furi_pubsub_subscribe(storage_get_pubsub(updater->storage), &sd_mount_callback, updater);
+    updater_main_set_storage_pubsub(main_view, sub);
+
+    /* FIXME: there's a misbehavior in storage subsystem. If we produce heavy load on it before it
+    * fires an SD card event, it'll never do that until the load is lifted. Meanwhile SD card icon
+    * will be missing from UI, however, /ext will be fully operational. So, until it's fixed, this
+    * should remain commented out. */
+    // If (somehow) we started after SD card is mounted, initiate update immediately
+    //if(storage_sd_status(updater->storage) == FSE_OK) {
+    //    view_dispatcher_send_custom_event(updater->view_dispatcher, UpdaterCustomEventStartUpdate);
+    //}
+
+    updater_main_set_view_dispatcher(main_view, updater->view_dispatcher);
+    view_dispatcher_switch_to_view(updater->view_dispatcher, UpdaterViewMain);
+}
+
+static void updater_scene_restart_to_postupdate() {
+    furi_hal_rtc_set_boot_mode(FuriHalRtcBootModePostUpdate);
+    furi_hal_power_reset();
+}
+
+bool updater_scene_main_on_event(void* context, SceneManagerEvent event) {
+    Updater* updater = (Updater*)context;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeTick) {
+        if(!update_task_is_running(updater->update_task)) {
+            if(updater->idle_ticks++ >= (UPDATE_DELAY_OPERATION_ERROR / UPDATER_APP_TICK)) {
+                updater_scene_restart_to_postupdate();
+            }
+        } else {
+            updater->idle_ticks = 0;
+        }
+    } else if(event.type == SceneManagerEventTypeCustom) {
+        switch(event.event) {
+        case UpdaterCustomEventStartUpdate:
+            if(!update_task_is_running(updater->update_task) &&
+               update_task_init(updater->update_task)) {
+                update_task_start(updater->update_task);
+            }
+            consumed = true;
+            break;
+
+        case UpdaterCustomEventRetryUpdate:
+            if(!update_task_is_running(updater->update_task) &&
+               (update_task_get_state(updater->update_task)->stage != UpdateTaskStageComplete))
+                update_task_start(updater->update_task);
+            consumed = true;
+            break;
+
+        case UpdaterCustomEventCancelUpdate:
+            if(!update_task_is_running(updater->update_task)) {
+                updater_scene_restart_to_postupdate();
+            }
+            consumed = true;
+            break;
+
+        case UpdaterCustomEventSdUnmounted:
+            // TODO: error out, stop worker (it's probably dead actually)
+            break;
+        default:
+            break;
+        }
+    }
+
+    return consumed;
+}
+
+void updater_scene_main_on_exit(void* context) {
+    Updater* updater = (Updater*)context;
+
+    furi_pubsub_unsubscribe(
+        storage_get_pubsub(updater->storage), updater_main_get_storage_pubsub(updater->main_view));
+
+    scene_manager_set_scene_state(updater->scene_manager, UpdaterSceneMain, 0);
+}

+ 132 - 0
applications/updater/updater.c

@@ -0,0 +1,132 @@
+#include "scenes/updater_scene.h"
+#include "updater_i.h"
+
+#include <storage/storage.h>
+#include <gui/view_dispatcher.h>
+#include <furi.h>
+#include <furi_hal.h>
+#include <portmacro.h>
+#include <stdint.h>
+
+static bool updater_custom_event_callback(void* context, uint32_t event) {
+    furi_assert(context);
+    Updater* updater = (Updater*)context;
+    return scene_manager_handle_custom_event(updater->scene_manager, event);
+}
+
+static void updater_tick_event_callback(void* context) {
+    furi_assert(context);
+    Updater* app = context;
+    scene_manager_handle_tick_event(app->scene_manager);
+}
+
+static bool updater_back_event_callback(void* context) {
+    furi_assert(context);
+    Updater* updater = (Updater*)context;
+    return scene_manager_handle_back_event(updater->scene_manager);
+}
+
+static void status_update_cb(
+    const char* message,
+    const uint8_t progress,
+    const uint8_t idx_stage,
+    const uint8_t total_stages,
+    bool failed,
+    void* context) {
+    UpdaterMainView* main_view = context;
+    updater_main_model_set_state(main_view, message, progress, idx_stage, total_stages, failed);
+}
+
+Updater* updater_alloc(const char* arg) {
+    Updater* updater = malloc(sizeof(Updater));
+    if(arg) {
+        string_init_set_str(updater->startup_arg, arg);
+        string_replace_str(updater->startup_arg, "/any/", "/ext/");
+    } else {
+        string_init(updater->startup_arg);
+    }
+
+    updater->storage = furi_record_open("storage");
+
+    updater->gui = furi_record_open("gui");
+    updater->view_dispatcher = view_dispatcher_alloc();
+    updater->scene_manager = scene_manager_alloc(&updater_scene_handlers, updater);
+
+    view_dispatcher_enable_queue(updater->view_dispatcher);
+
+    view_dispatcher_set_event_callback_context(updater->view_dispatcher, updater);
+    view_dispatcher_set_custom_event_callback(
+        updater->view_dispatcher, updater_custom_event_callback);
+    view_dispatcher_set_navigation_event_callback(
+        updater->view_dispatcher, updater_back_event_callback);
+    view_dispatcher_set_tick_event_callback(
+        updater->view_dispatcher, updater_tick_event_callback, UPDATER_APP_TICK);
+
+    view_dispatcher_attach_to_gui(
+        updater->view_dispatcher,
+        updater->gui,
+        arg ? ViewDispatcherTypeFullscreen : ViewDispatcherTypeWindow);
+
+    updater->main_view = updater_main_alloc();
+    view_dispatcher_add_view(
+        updater->view_dispatcher, UpdaterViewMain, updater_main_get_view(updater->main_view));
+
+#ifndef FURI_RAM_EXEC
+    updater->widget = widget_alloc();
+    view_dispatcher_add_view(
+        updater->view_dispatcher, UpdaterViewWidget, widget_get_view(updater->widget));
+#endif
+
+#ifdef FURI_RAM_EXEC
+    if(true) {
+#else
+    if(!arg) {
+#endif
+        updater->update_task = update_task_alloc();
+        update_task_set_progress_cb(updater->update_task, status_update_cb, updater->main_view);
+
+        scene_manager_next_scene(updater->scene_manager, UpdaterSceneMain);
+    } else {
+#ifndef FURI_RAM_EXEC
+        scene_manager_next_scene(updater->scene_manager, UpdaterSceneLoadCfg);
+#endif
+    }
+
+    return updater;
+}
+
+void updater_free(Updater* updater) {
+    furi_assert(updater);
+
+    string_clear(updater->startup_arg);
+    if(updater->update_task) {
+        update_task_set_progress_cb(updater->update_task, NULL, NULL);
+        update_task_free(updater->update_task);
+    }
+
+    view_dispatcher_remove_view(updater->view_dispatcher, UpdaterViewMain);
+    updater_main_free(updater->main_view);
+
+#ifndef FURI_RAM_EXEC
+    view_dispatcher_remove_view(updater->view_dispatcher, UpdaterViewWidget);
+    widget_free(updater->widget);
+#endif
+
+    view_dispatcher_free(updater->view_dispatcher);
+    scene_manager_free(updater->scene_manager);
+
+    furi_record_close("gui");
+    furi_record_close("storage");
+
+    free(updater);
+}
+
+int32_t updater_srv(void* p) {
+    const char* cfgpath = p;
+
+    Updater* updater = updater_alloc(cfgpath);
+    view_dispatcher_run(updater->view_dispatcher);
+    updater_free(updater);
+
+    return 0;
+}

+ 65 - 0
applications/updater/updater_i.h

@@ -0,0 +1,65 @@
+#pragma once
+
+#include "views/updater_main.h"
+#include "util/update_task.h"
+
+#include <furi.h>
+#include <gui/gui.h>
+#include <gui/view_stack.h>
+#include <gui/view_dispatcher.h>
+#include <gui/modules/popup.h>
+#include <gui/scene_manager.h>
+#include <gui/modules/widget.h>
+#include <storage/storage.h>
+#include <update_util/update_operation.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define UPDATER_APP_TICK 500
+
+typedef enum {
+    UpdaterViewMain,
+    UpdaterViewWidget,
+} UpdaterViewEnum;
+
+typedef enum {
+    UpdaterCustomEventUnknown,
+    UpdaterCustomEventStartUpdate,
+    UpdaterCustomEventRetryUpdate,
+    UpdaterCustomEventCancelUpdate,
+    UpdaterCustomEventSdUnmounted,
+} UpdaterCustomEvent;
+
+typedef struct UpdaterManifestProcessingState {
+    UpdateManifest* manifest;
+    string_t message;
+    bool ready_to_be_applied;
+} UpdaterManifestProcessingState;
+
+typedef struct {
+    // GUI
+    Gui* gui;
+    SceneManager* scene_manager;
+    ViewDispatcher* view_dispatcher;
+    Storage* storage;
+
+    UpdaterMainView* main_view;
+
+    UpdaterManifestProcessingState* pending_update;
+    UpdatePrepareResult preparation_result;
+
+    UpdateTask* update_task;
+    Widget* widget;
+    string_t startup_arg;
+    int32_t idle_ticks;
+} Updater;
+
+Updater* updater_alloc(const char* arg);
+
+void updater_free(Updater* updater);
+
+#ifdef __cplusplus
+}
+#endif

+ 226 - 0
applications/updater/util/update_task.c

@@ -0,0 +1,226 @@
+#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>
+
+static const char* update_task_stage_descr[] = {
+    [UpdateTaskStageProgress] = "...",
+    [UpdateTaskStageReadManifest] = "Loading update manifest",
+    [UpdateTaskStageValidateDFUImage] = "Checking DFU file",
+    [UpdateTaskStageFlashWrite] = "Writing flash",
+    [UpdateTaskStageFlashValidate] = "Validating",
+    [UpdateTaskStageRadioWrite] = "Writing radio stack",
+    [UpdateTaskStageRadioCommit] = "Applying radio stack",
+    [UpdateTaskStageLfsBackup] = "Backing up LFS",
+    [UpdateTaskStageLfsRestore] = "Restoring LFS",
+    [UpdateTaskStageComplete] = "Complete",
+    [UpdateTaskStageError] = "Error",
+};
+
+static void update_task_set_status(UpdateTask* update_task, const char* status) {
+    if(!status) {
+        if(update_task->state.stage >= COUNT_OF(update_task_stage_descr)) {
+            status = "...";
+        } else {
+            status = update_task_stage_descr[update_task->state.stage];
+        }
+    }
+    string_set_str(update_task->state.status, status);
+}
+
+void update_task_set_progress(UpdateTask* update_task, UpdateTaskStage stage, uint8_t progress) {
+    if(stage != UpdateTaskStageProgress) {
+        update_task->state.stage = stage;
+        update_task->state.current_stage_idx++;
+        update_task_set_status(update_task, NULL);
+    }
+
+    if(progress > 100) {
+        progress = 100;
+    }
+
+    update_task->state.progress = progress;
+    if(update_task->status_change_cb) {
+        (update_task->status_change_cb)(
+            string_get_cstr(update_task->state.status),
+            progress,
+            update_task->state.current_stage_idx,
+            update_task->state.total_stages,
+            update_task->state.stage == UpdateTaskStageError,
+            update_task->status_change_cb_state);
+    }
+}
+
+static void update_task_close_file(UpdateTask* update_task) {
+    furi_assert(update_task);
+    if(!storage_file_is_open(update_task->file)) {
+        return;
+    }
+
+    storage_file_close(update_task->file);
+}
+
+static bool update_task_check_file_exists(UpdateTask* update_task, string_t filename) {
+    furi_assert(update_task);
+    string_t tmp_path;
+    string_init_set(tmp_path, update_task->update_path);
+    path_append(tmp_path, string_get_cstr(filename));
+    bool exists =
+        (storage_common_stat(update_task->storage, string_get_cstr(tmp_path), NULL) == FSE_OK);
+    string_clear(tmp_path);
+    return exists;
+}
+
+bool update_task_open_file(UpdateTask* update_task, string_t filename) {
+    furi_assert(update_task);
+    update_task_close_file(update_task);
+
+    string_t tmp_path;
+    string_init_set(tmp_path, update_task->update_path);
+    path_append(tmp_path, string_get_cstr(filename));
+    bool open_success = storage_file_open(
+        update_task->file, string_get_cstr(tmp_path), FSAM_READ, FSOM_OPEN_EXISTING);
+    string_clear(tmp_path);
+    return open_success;
+}
+
+static void update_task_worker_thread_cb(FuriThreadState state, void* context) {
+    UpdateTask* update_task = context;
+
+    if(state != FuriThreadStateStopped) {
+        return;
+    }
+
+    int32_t op_result = furi_thread_get_return_code(update_task->thread);
+    if(op_result == UPDATE_TASK_NOERR) {
+        osDelay(UPDATE_DELAY_OPERATION_OK);
+        furi_hal_power_reset();
+    }
+}
+
+UpdateTask* update_task_alloc() {
+    UpdateTask* update_task = malloc(sizeof(UpdateTask));
+
+    update_task->state.stage = UpdateTaskStageProgress;
+    update_task->state.progress = 0;
+    string_init(update_task->state.status);
+
+    update_task->manifest = update_manifest_alloc();
+    update_task->storage = furi_record_open("storage");
+    update_task->file = storage_file_alloc(update_task->storage);
+    update_task->status_change_cb = NULL;
+
+    FuriThread* thread = update_task->thread = furi_thread_alloc();
+
+    furi_thread_set_name(thread, "UpdateWorker");
+    furi_thread_set_stack_size(thread, 5120);
+    furi_thread_set_context(thread, update_task);
+
+    furi_thread_set_state_callback(thread, update_task_worker_thread_cb);
+    furi_thread_set_state_context(thread, update_task);
+#ifdef FURI_RAM_EXEC
+    UNUSED(update_task_worker_backup_restore);
+    furi_thread_set_callback(thread, update_task_worker_flash_writer);
+#else
+    UNUSED(update_task_worker_flash_writer);
+    furi_thread_set_callback(thread, update_task_worker_backup_restore);
+#endif
+
+    return update_task;
+}
+
+void update_task_free(UpdateTask* update_task) {
+    furi_assert(update_task);
+
+    furi_thread_join(update_task->thread);
+
+    furi_thread_free(update_task->thread);
+    update_task_close_file(update_task);
+    storage_file_free(update_task->file);
+    update_manifest_free(update_task->manifest);
+
+    furi_record_close("storage");
+    string_clear(update_task->update_path);
+
+    free(update_task);
+}
+
+bool update_task_init(UpdateTask* update_task) {
+    furi_assert(update_task);
+    string_init(update_task->update_path);
+    return true;
+}
+
+bool update_task_parse_manifest(UpdateTask* update_task) {
+    furi_assert(update_task);
+    update_task_set_progress(update_task, UpdateTaskStageReadManifest, 0);
+    bool result = false;
+    string_t manifest_path;
+    string_init(manifest_path);
+
+    do {
+        update_task_set_progress(update_task, UpdateTaskStageProgress, 10);
+        if(!update_operation_get_current_package_path(
+               update_task->storage, update_task->update_path)) {
+            break;
+        }
+
+        path_concat(
+            string_get_cstr(update_task->update_path),
+            UPDATE_MANIFEST_DEFAULT_NAME,
+            manifest_path);
+        update_task_set_progress(update_task, UpdateTaskStageProgress, 30);
+        if(!update_manifest_init(update_task->manifest, string_get_cstr(manifest_path))) {
+            break;
+        }
+
+        update_task_set_progress(update_task, UpdateTaskStageProgress, 50);
+        if(!string_empty_p(update_task->manifest->firmware_dfu_image) &&
+           !update_task_check_file_exists(update_task, update_task->manifest->firmware_dfu_image)) {
+            break;
+        }
+
+        update_task_set_progress(update_task, UpdateTaskStageProgress, 70);
+        if(!string_empty_p(update_task->manifest->radio_image) &&
+           !update_task_check_file_exists(update_task, update_task->manifest->radio_image)) {
+            break;
+        }
+
+        update_task_set_progress(update_task, UpdateTaskStageProgress, 100);
+        result = true;
+    } while(false);
+
+    string_clear(manifest_path);
+    return result;
+}
+
+void update_task_set_progress_cb(UpdateTask* update_task, updateProgressCb cb, void* state) {
+    update_task->status_change_cb = cb;
+    update_task->status_change_cb_state = state;
+}
+
+bool update_task_start(UpdateTask* update_task) {
+    furi_assert(update_task);
+    return furi_thread_start(update_task->thread);
+}
+
+bool update_task_is_running(UpdateTask* update_task) {
+    furi_assert(update_task);
+    return furi_thread_get_state(update_task->thread) == FuriThreadStateRunning;
+}
+
+UpdateTaskState const* update_task_get_state(UpdateTask* update_task) {
+    furi_assert(update_task);
+    return &update_task->state;
+}
+
+UpdateManifest const* update_task_get_manifest(UpdateTask* update_task) {
+    furi_assert(update_task);
+    return update_task->manifest;
+}

+ 66 - 0
applications/updater/util/update_task.h

@@ -0,0 +1,66 @@
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <update_util/update_manifest.h>
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <m-string.h>
+
+#define UPDATE_DELAY_OPERATION_OK 600
+#define UPDATE_DELAY_OPERATION_ERROR INT_MAX
+
+typedef enum {
+    UpdateTaskStageProgress,
+    UpdateTaskStageReadManifest,
+    UpdateTaskStageValidateDFUImage,
+    UpdateTaskStageFlashWrite,
+    UpdateTaskStageFlashValidate,
+    UpdateTaskStageRadioWrite,
+    UpdateTaskStageRadioCommit,
+    UpdateTaskStageLfsBackup,
+    UpdateTaskStageLfsRestore,
+    UpdateTaskStageComplete,
+    UpdateTaskStageError,
+} UpdateTaskStage;
+
+typedef struct {
+    UpdateTaskStage stage;
+    uint8_t progress;
+    uint8_t current_stage_idx;
+    uint8_t total_stages;
+    string_t status;
+} UpdateTaskState;
+
+typedef struct UpdateTask UpdateTask;
+
+typedef void (*updateProgressCb)(
+    const char* status,
+    const uint8_t stage_pct,
+    const uint8_t idx_stage,
+    const uint8_t total_stages,
+    bool failed,
+    void* state);
+
+UpdateTask* update_task_alloc();
+
+void update_task_free(UpdateTask* update_task);
+
+bool update_task_init(UpdateTask* update_task);
+
+void update_task_set_progress_cb(UpdateTask* update_task, updateProgressCb cb, void* state);
+
+bool update_task_start(UpdateTask* update_task);
+
+bool update_task_is_running(UpdateTask* update_task);
+
+UpdateTaskState const* update_task_get_state(UpdateTask* update_task);
+
+UpdateManifest const* update_task_get_manifest(UpdateTask* update_task);
+
+#ifdef __cplusplus
+}
+#endif

+ 24 - 0
applications/updater/util/update_task_i.h

@@ -0,0 +1,24 @@
+#pragma once
+
+#include <storage/storage.h>
+
+#define UPDATE_TASK_NOERR 0
+#define UPDATE_TASK_FAILED -1
+
+typedef struct UpdateTask {
+    UpdateTaskState state;
+    string_t update_path;
+    UpdateManifest* manifest;
+    FuriThread* thread;
+    Storage* storage;
+    File* file;
+    updateProgressCb status_change_cb;
+    void* status_change_cb_state;
+} UpdateTask;
+
+void update_task_set_progress(UpdateTask* update_task, UpdateTaskStage stage, uint8_t progress);
+bool update_task_parse_manifest(UpdateTask* update_task);
+bool update_task_open_file(UpdateTask* update_task, string_t filename);
+
+int32_t update_task_worker_flash_writer(void* context);
+int32_t update_task_worker_backup_restore(void* context);

+ 148 - 0
applications/updater/util/update_task_workers.c

@@ -0,0 +1,148 @@
+#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>
+
+#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
+
+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, UpdateTaskStageComplete, 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;
+}
+
+int32_t update_task_worker_backup_restore(void* context) {
+    furi_assert(context);
+    UpdateTask* update_task = context;
+    bool success = false;
+
+    FuriHalRtcBootMode boot_mode = furi_hal_rtc_get_boot_mode();
+    if((boot_mode != FuriHalRtcBootModePreUpdate) && (boot_mode != FuriHalRtcBootModePostUpdate)) {
+        // no idea how we got here. Clear to normal boot
+        furi_hal_rtc_set_boot_mode(FuriHalRtcBootModeNormal);
+        return UPDATE_TASK_NOERR;
+    }
+
+    update_task->state.current_stage_idx = 0;
+    update_task->state.total_stages = 1;
+
+    if(!update_operation_get_current_package_path(update_task->storage, update_task->update_path)) {
+        return UPDATE_TASK_FAILED;
+    }
+
+    string_t backup_file_path;
+    string_init(backup_file_path);
+    path_concat(
+        string_get_cstr(update_task->update_path), LFS_BACKUP_DEFAULT_FILENAME, backup_file_path);
+
+    if(boot_mode == FuriHalRtcBootModePreUpdate) {
+        update_task_set_progress(update_task, UpdateTaskStageLfsBackup, 0);
+        furi_hal_rtc_set_boot_mode(FuriHalRtcBootModeNormal); // to avoid bootloops
+        if((success =
+                lfs_backup_create(update_task->storage, string_get_cstr(backup_file_path)))) {
+            furi_hal_rtc_set_boot_mode(FuriHalRtcBootModeUpdate);
+        }
+    } else if(boot_mode == FuriHalRtcBootModePostUpdate) {
+        update_task_set_progress(update_task, UpdateTaskStageLfsRestore, 0);
+        furi_hal_rtc_set_boot_mode(FuriHalRtcBootModeNormal);
+        success = lfs_backup_unpack(update_task->storage, string_get_cstr(backup_file_path));
+    }
+
+    if(success) {
+        update_task_set_progress(update_task, UpdateTaskStageComplete, 100);
+    } else {
+        update_task_set_progress(update_task, UpdateTaskStageError, update_task->state.progress);
+    }
+
+    string_clear(backup_file_path);
+
+    return success ? UPDATE_TASK_NOERR : UPDATE_TASK_FAILED;
+}

+ 151 - 0
applications/updater/views/updater_main.c

@@ -0,0 +1,151 @@
+#include <gui/gui_i.h>
+#include <gui/view.h>
+#include <gui/elements.h>
+#include <gui/canvas.h>
+#include <furi.h>
+#include <input/input.h>
+
+#include "../updater_i.h"
+#include "updater_main.h"
+
+struct UpdaterMainView {
+    View* view;
+    ViewDispatcher* view_dispatcher;
+    FuriPubSubSubscription* subscription;
+    void* context;
+};
+
+static const uint8_t PROGRESS_RENDER_STEP = 3; /* percent, to limit rendering rate */
+
+typedef struct {
+    string_t status;
+    uint8_t progress, rendered_progress;
+    uint8_t idx_stage, total_stages;
+    bool failed;
+} UpdaterProgressModel;
+
+void updater_main_model_set_state(
+    UpdaterMainView* main_view,
+    const char* message,
+    uint8_t progress,
+    uint8_t idx_stage,
+    uint8_t total_stages,
+    bool failed) {
+    with_view_model(
+        main_view->view, (UpdaterProgressModel * model) {
+            model->failed = failed;
+            model->idx_stage = idx_stage;
+            model->total_stages = total_stages;
+            model->progress = progress;
+            if(string_cmp_str(model->status, message)) {
+                string_set(model->status, message);
+                model->rendered_progress = progress;
+                return true;
+            }
+            if((model->rendered_progress > progress) ||
+               ((progress - model->rendered_progress) > PROGRESS_RENDER_STEP)) {
+                model->rendered_progress = progress;
+                return true;
+            }
+            return false;
+        });
+}
+
+View* updater_main_get_view(UpdaterMainView* main_view) {
+    furi_assert(main_view);
+    return main_view->view;
+}
+
+bool updater_main_input(InputEvent* event, void* context) {
+    furi_assert(event);
+    furi_assert(context);
+
+    UpdaterMainView* main_view = context;
+    if(!main_view->view_dispatcher) {
+        return true;
+    }
+
+    if(event->type != InputTypeShort) {
+        return true;
+    }
+
+    if(event->key == InputKeyOk) {
+        view_dispatcher_send_custom_event(
+            main_view->view_dispatcher, UpdaterCustomEventRetryUpdate);
+    } else if(event->key == InputKeyBack) {
+        view_dispatcher_send_custom_event(
+            main_view->view_dispatcher, UpdaterCustomEventCancelUpdate);
+    }
+
+    return true;
+}
+
+static void updater_main_draw_callback(Canvas* canvas, void* _model) {
+    UpdaterProgressModel* model = _model;
+
+    canvas_set_font(canvas, FontPrimary);
+
+    uint16_t y_offset = model->failed ? 5 : 13;
+    string_t status_text;
+    if(!model->failed && (model->idx_stage != 0) && (model->idx_stage <= model->total_stages)) {
+        string_init_printf(
+            status_text,
+            "[%d/%d] %s",
+            model->idx_stage,
+            model->total_stages,
+            string_get_cstr(model->status));
+    } else {
+        string_init_set(status_text, model->status);
+    }
+    canvas_draw_str_aligned(
+        canvas, 128 / 2, y_offset, AlignCenter, AlignTop, string_get_cstr(status_text));
+    string_clear(status_text);
+    if(model->failed) {
+        canvas_set_font(canvas, FontSecondary);
+        canvas_draw_str_aligned(
+            canvas, 128 / 2, 20, AlignCenter, AlignTop, "[OK] to retry, [Back] to abort");
+    }
+    elements_progress_bar(canvas, 14, 35, 100, (float)model->progress / 100);
+}
+
+UpdaterMainView* updater_main_alloc() {
+    UpdaterMainView* main_view = malloc(sizeof(UpdaterMainView));
+
+    main_view->view = view_alloc();
+    view_allocate_model(main_view->view, ViewModelTypeLocking, sizeof(UpdaterProgressModel));
+
+    with_view_model(
+        main_view->view, (UpdaterProgressModel * model) {
+            string_init_set(model->status, "Waiting for storage");
+            return true;
+        });
+
+    view_set_context(main_view->view, main_view);
+    view_set_input_callback(main_view->view, updater_main_input);
+    view_set_draw_callback(main_view->view, updater_main_draw_callback);
+
+    return main_view;
+}
+
+void updater_main_free(UpdaterMainView* main_view) {
+    furi_assert(main_view);
+    with_view_model(
+        main_view->view, (UpdaterProgressModel * model) {
+            string_clear(model->status);
+            return false;
+        });
+    view_free(main_view->view);
+    free(main_view);
+}
+
+void updater_main_set_storage_pubsub(UpdaterMainView* main_view, FuriPubSubSubscription* sub) {
+    main_view->subscription = sub;
+}
+
+FuriPubSubSubscription* updater_main_get_storage_pubsub(UpdaterMainView* main_view) {
+    return main_view->subscription;
+}
+
+void updater_main_set_view_dispatcher(UpdaterMainView* main_view, ViewDispatcher* view_dispatcher) {
+    main_view->view_dispatcher = view_dispatcher;
+}

+ 28 - 0
applications/updater/views/updater_main.h

@@ -0,0 +1,28 @@
+#pragma once
+
+#include <gui/view.h>
+
+typedef struct UpdaterMainView UpdaterMainView;
+typedef struct FuriPubSubSubscription FuriPubSubSubscription;
+typedef struct ViewDispatcher ViewDispatcher;
+typedef void (*UpdaterMainInputCallback)(InputType type, void* context);
+
+View* updater_main_get_view(UpdaterMainView* main_view);
+
+UpdaterMainView* updater_main_alloc();
+
+void updater_main_free(UpdaterMainView* main_view);
+
+void updater_main_model_set_state(
+    UpdaterMainView* main_view,
+    const char* message,
+    uint8_t progress,
+    uint8_t idx_stage,
+    uint8_t total_stages,
+    bool failed);
+
+void updater_main_set_storage_pubsub(UpdaterMainView* main_view, FuriPubSubSubscription* sub);
+
+FuriPubSubSubscription* updater_main_get_storage_pubsub(UpdaterMainView* main_view);
+
+void updater_main_set_view_dispatcher(UpdaterMainView* main_view, ViewDispatcher* view_dispatcher);

+ 1 - 1
assets/assets.mk

@@ -16,7 +16,7 @@ PROTOBUF_COMPILED_DIR	:= $(ASSETS_COMPILED_DIR)
 PROTOBUF_SOURCES		:= $(shell find $(PROTOBUF_SOURCE_DIR) -type f -iname '*.proto')
 PROTOBUF_SOURCES		:= $(shell find $(PROTOBUF_SOURCE_DIR) -type f -iname '*.proto')
 PROTOBUF_FILENAMES		:= $(notdir $(addsuffix .pb.c,$(basename $(PROTOBUF_SOURCES))))
 PROTOBUF_FILENAMES		:= $(notdir $(addsuffix .pb.c,$(basename $(PROTOBUF_SOURCES))))
 PROTOBUF				:= $(addprefix $(PROTOBUF_COMPILED_DIR)/,$(PROTOBUF_FILENAMES)) $(PROTOBUF_COMPILED_DIR)/protobuf_version.h
 PROTOBUF				:= $(addprefix $(PROTOBUF_COMPILED_DIR)/,$(PROTOBUF_FILENAMES)) $(PROTOBUF_COMPILED_DIR)/protobuf_version.h
-PROTOBUF_VERSION		:= $(shell git -C $(PROTOBUF_SOURCE_DIR) fetch --tags 2>/dev/null && git -C $(PROTOBUF_SOURCE_DIR) describe --tags --abbrev=0 2>/dev/null || echo 'unknown')
+PROTOBUF_VERSION		:= $(shell git -C $(PROTOBUF_SOURCE_DIR) fetch --tags 2>/dev/null ; git -C $(PROTOBUF_SOURCE_DIR) describe --tags --abbrev=0 2>/dev/null || echo 'unknown')
 PROTOBUF_MAJOR_VERSION	:= $(word 1,$(subst ., ,$(PROTOBUF_VERSION)))
 PROTOBUF_MAJOR_VERSION	:= $(word 1,$(subst ., ,$(PROTOBUF_VERSION)))
 PROTOBUF_MINOR_VERSION	:= $(word 2,$(subst ., ,$(PROTOBUF_VERSION)))
 PROTOBUF_MINOR_VERSION	:= $(word 2,$(subst ., ,$(PROTOBUF_VERSION)))
 $(if $(PROTOBUF_MAJOR_VERSION),,$(error "Protobuf major version is not specified, $$PROTOBUF_VERSION=$(PROTOBUF_VERSION), please perform git fetch in assets/protobuf directory"))
 $(if $(PROTOBUF_MAJOR_VERSION),,$(error "Protobuf major version is not specified, $$PROTOBUF_VERSION=$(PROTOBUF_VERSION), please perform git fetch in assets/protobuf directory"))

Разница между файлами не показана из-за своего большого размера
+ 3 - 0
assets/compiled/assets_icons.c


+ 1 - 0
assets/compiled/assets_icons.h

@@ -15,6 +15,7 @@ extern const Icon I_ir_10px;
 extern const Icon I_sub1_10px;
 extern const Icon I_sub1_10px;
 extern const Icon I_u2f_10px;
 extern const Icon I_u2f_10px;
 extern const Icon I_unknown_10px;
 extern const Icon I_unknown_10px;
+extern const Icon I_update_10px;
 extern const Icon I_BLE_Pairing_128x64;
 extern const Icon I_BLE_Pairing_128x64;
 extern const Icon I_Ble_connected_38x34;
 extern const Icon I_Ble_connected_38x34;
 extern const Icon I_Ble_disconnected_24x34;
 extern const Icon I_Ble_disconnected_24x34;

BIN
assets/icons/Archive/update_10px.png


+ 0 - 44
bootloader/Makefile

@@ -1,44 +0,0 @@
-MAKEFILE_DIR	:= $(dir $(abspath $(firstword $(MAKEFILE_LIST))))
-PROJECT_ROOT	:= $(abspath $(MAKEFILE_DIR)/..)
-PROJECT			:= bootloader
-
-include 		$(PROJECT_ROOT)/make/base.mk
-
-CFLAGS			+= -I$(PROJECT_ROOT) -Itargets/furi_hal_include
-ASM_SOURCES		+= $(wildcard src/*.s)
-C_SOURCES		+= $(wildcard src/*.c)
-CPP_SOURCES		+= $(wildcard src/*.cpp)
-
-include			$(PROJECT_ROOT)/make/defaults.mk
-TARGET_DIR		= targets/$(TARGET)
-include			$(TARGET_DIR)/target.mk
-
-LIB_DIR			= $(PROJECT_ROOT)/lib
-
-# U8G2 display library
-U8G2_DIR		= $(LIB_DIR)/u8g2
-CFLAGS			+= -I$(U8G2_DIR)
-C_SOURCES		+= $(U8G2_DIR)/u8g2_glue.c
-C_SOURCES		+= $(U8G2_DIR)/u8g2_intersection.c
-C_SOURCES		+= $(U8G2_DIR)/u8g2_setup.c
-C_SOURCES		+= $(U8G2_DIR)/u8g2_d_memory.c
-C_SOURCES		+= $(U8G2_DIR)/u8x8_cad.c
-C_SOURCES		+= $(U8G2_DIR)/u8x8_byte.c
-C_SOURCES		+= $(U8G2_DIR)/u8x8_gpio.c
-C_SOURCES		+= $(U8G2_DIR)/u8x8_display.c
-C_SOURCES		+= $(U8G2_DIR)/u8x8_setup.c
-C_SOURCES		+= $(U8G2_DIR)/u8g2_hvline.c
-C_SOURCES		+= $(U8G2_DIR)/u8g2_line.c
-C_SOURCES		+= $(U8G2_DIR)/u8g2_ll_hvline.c
-C_SOURCES		+= $(U8G2_DIR)/u8g2_circle.c
-C_SOURCES		+= $(U8G2_DIR)/u8g2_box.c
-C_SOURCES		+= $(U8G2_DIR)/u8g2_buffer.c
-C_SOURCES		+= $(U8G2_DIR)/u8g2_font.c
-C_SOURCES		+= $(U8G2_DIR)/u8g2_fonts.c
-C_SOURCES		+= $(U8G2_DIR)/u8x8_8x8.c
-C_SOURCES		+= $(U8G2_DIR)/u8g2_bitmap.c
-
-
-include			$(PROJECT_ROOT)/make/git.mk
-include			$(PROJECT_ROOT)/make/toolchain.mk
-include			$(PROJECT_ROOT)/make/rules.mk

+ 0 - 50
bootloader/ReadMe.md

@@ -1,50 +0,0 @@
-# Flipper bootloader
-
-What it does?
-
-- [x] Hardware initialization
-- [x] Boot process LED indicators
-- [x] Firmware update
-- [x] Errata crutches
-- [ ] Recovery mode
-
-# Targets
-
-| Name      | Bootloader    | Firmware      | Reset     | DFU               |
-|           | Address       | Address       | Combo     | Combo             |
------------------------------------------------------------------------------
-| f7        | 0x08000000    | 0x00008000    | L+Back    | L+Back, hold L    |
-
-Also there is a ST bootloader combo available on empty device: L+Ok+Back, release Back,Left.
-Target independent code and headers in `src` and `target/include` folders.
-
-# Building
-
-## With dev docker image:
-
-`docker-compose exec dev make -C bootloader`
-
-## With toolchain installed in path:
-
-`make -C bootloader `
-
-## Build Options
-
-- `DEBUG` - 0/1 - enable or disable debug build. Default is 1.
-- `TARGET` - string - target to build. Default is `f4`.
-
-# Flashing 
-
-Using SWD (STLink):
-
-`make -C bootloader flash`
-
-Or use DFU (USB):
-
-`make -C bootloader upload`
-
-# Debug
-
-Using SWD (STLink):
-
-`make -C bootloader debug`

+ 0 - 14
bootloader/src/main.c

@@ -1,14 +0,0 @@
-#include "target.h"
-
-int main() {
-    // Initialize hardware
-    target_init();
-    // Check if dfu requested
-    if(target_is_dfu_requested()) {
-        target_switch2dfu();
-    }
-    // Switch to OS
-    target_switch2os();
-    // Never should get here
-    return 0;
-}

+ 0 - 21
bootloader/targets/f7/furi_hal/furi_hal.c

@@ -1,21 +0,0 @@
-#include <furi_hal.h>
-#include <stm32wbxx_ll_utils.h>
-
-void furi_hal_init() {
-    furi_hal_i2c_init();
-    furi_hal_light_init();
-    furi_hal_spi_init();
-    furi_hal_version_init();
-}
-
-void furi_hal_delay_ms(float milliseconds) {
-    LL_mDelay((uint32_t)milliseconds);
-}
-
-void furi_hal_delay_us(float microseconds) {
-    microseconds = microseconds / 1000;
-    if(microseconds < 1) {
-        microseconds = 1;
-    }
-    LL_mDelay((uint32_t)microseconds);
-}

+ 0 - 214
bootloader/targets/f7/furi_hal/furi_hal_gpio.c

@@ -1,214 +0,0 @@
-#include <furi_hal_gpio.h>
-#include <stddef.h>
-#include <assert.h>
-
-#define GET_SYSCFG_EXTI_PORT(gpio)                \
-    (((gpio) == (GPIOA)) ? LL_SYSCFG_EXTI_PORTA : \
-     ((gpio) == (GPIOB)) ? LL_SYSCFG_EXTI_PORTB : \
-     ((gpio) == (GPIOC)) ? LL_SYSCFG_EXTI_PORTC : \
-     ((gpio) == (GPIOD)) ? LL_SYSCFG_EXTI_PORTD : \
-     ((gpio) == (GPIOE)) ? LL_SYSCFG_EXTI_PORTE : \
-                           LL_SYSCFG_EXTI_PORTH)
-
-#define GPIO_PIN_MAP(pin, prefix)               \
-    (((pin) == (LL_GPIO_PIN_0))  ? prefix##0 :  \
-     ((pin) == (LL_GPIO_PIN_1))  ? prefix##1 :  \
-     ((pin) == (LL_GPIO_PIN_2))  ? prefix##2 :  \
-     ((pin) == (LL_GPIO_PIN_3))  ? prefix##3 :  \
-     ((pin) == (LL_GPIO_PIN_4))  ? prefix##4 :  \
-     ((pin) == (LL_GPIO_PIN_5))  ? prefix##5 :  \
-     ((pin) == (LL_GPIO_PIN_6))  ? prefix##6 :  \
-     ((pin) == (LL_GPIO_PIN_7))  ? prefix##7 :  \
-     ((pin) == (LL_GPIO_PIN_8))  ? prefix##8 :  \
-     ((pin) == (LL_GPIO_PIN_9))  ? prefix##9 :  \
-     ((pin) == (LL_GPIO_PIN_10)) ? prefix##10 : \
-     ((pin) == (LL_GPIO_PIN_11)) ? prefix##11 : \
-     ((pin) == (LL_GPIO_PIN_12)) ? prefix##12 : \
-     ((pin) == (LL_GPIO_PIN_13)) ? prefix##13 : \
-     ((pin) == (LL_GPIO_PIN_14)) ? prefix##14 : \
-                                   prefix##15)
-
-#define GET_SYSCFG_EXTI_LINE(pin) GPIO_PIN_MAP(pin, LL_SYSCFG_EXTI_LINE)
-#define GET_EXTI_LINE(pin) GPIO_PIN_MAP(pin, LL_EXTI_LINE_)
-
-static volatile GpioInterrupt gpio_interrupt[GPIO_NUMBER];
-
-static uint8_t furi_hal_gpio_get_pin_num(const GpioPin* gpio) {
-    uint8_t pin_num = 0;
-    for(pin_num = 0; pin_num < GPIO_NUMBER; pin_num++) {
-        if(gpio->pin & (1 << pin_num)) break;
-    }
-    return pin_num;
-}
-
-void furi_hal_gpio_init_simple(const GpioPin* gpio, const GpioMode mode) {
-    furi_hal_gpio_init(gpio, mode, GpioPullNo, GpioSpeedLow);
-}
-
-void furi_hal_gpio_init(
-    const GpioPin* gpio,
-    const GpioMode mode,
-    const GpioPull pull,
-    const GpioSpeed speed) {
-    // we cannot set alternate mode in this function
-    assert(mode != GpioModeAltFunctionPushPull);
-    assert(mode != GpioModeAltFunctionOpenDrain);
-
-    furi_hal_gpio_init_ex(gpio, mode, pull, speed, GpioAltFnUnused);
-}
-
-void furi_hal_gpio_init_ex(
-    const GpioPin* gpio,
-    const GpioMode mode,
-    const GpioPull pull,
-    const GpioSpeed speed,
-    const GpioAltFn alt_fn) {
-    uint32_t sys_exti_port = GET_SYSCFG_EXTI_PORT(gpio->port);
-    uint32_t sys_exti_line = GET_SYSCFG_EXTI_LINE(gpio->pin);
-    uint32_t exti_line = GET_EXTI_LINE(gpio->pin);
-
-    // Configure gpio with interrupts disabled
-    __disable_irq();
-
-    // Set gpio speed
-    switch(speed) {
-    case GpioSpeedLow:
-        LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_LOW);
-        break;
-    case GpioSpeedMedium:
-        LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_MEDIUM);
-        break;
-    case GpioSpeedHigh:
-        LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_HIGH);
-        break;
-    case GpioSpeedVeryHigh:
-        LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_VERY_HIGH);
-        break;
-    }
-
-    // Set gpio pull mode
-    switch(pull) {
-    case GpioPullNo:
-        LL_GPIO_SetPinPull(gpio->port, gpio->pin, LL_GPIO_PULL_NO);
-        break;
-    case GpioPullUp:
-        LL_GPIO_SetPinPull(gpio->port, gpio->pin, LL_GPIO_PULL_UP);
-        break;
-    case GpioPullDown:
-        LL_GPIO_SetPinPull(gpio->port, gpio->pin, LL_GPIO_PULL_DOWN);
-        break;
-    }
-
-    // Set gpio mode
-    if(mode >= GpioModeInterruptRise) {
-        // Set pin in interrupt mode
-        LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_INPUT);
-        LL_SYSCFG_SetEXTISource(sys_exti_port, sys_exti_line);
-        if(mode == GpioModeInterruptRise || mode == GpioModeInterruptRiseFall) {
-            LL_EXTI_EnableIT_0_31(exti_line);
-            LL_EXTI_EnableRisingTrig_0_31(exti_line);
-        }
-        if(mode == GpioModeInterruptFall || mode == GpioModeInterruptRiseFall) {
-            LL_EXTI_EnableIT_0_31(exti_line);
-            LL_EXTI_EnableFallingTrig_0_31(exti_line);
-        }
-        if(mode == GpioModeEventRise || mode == GpioModeEventRiseFall) {
-            LL_EXTI_EnableEvent_0_31(exti_line);
-            LL_EXTI_EnableRisingTrig_0_31(exti_line);
-        }
-        if(mode == GpioModeEventFall || mode == GpioModeEventRiseFall) {
-            LL_EXTI_EnableEvent_0_31(exti_line);
-            LL_EXTI_EnableFallingTrig_0_31(exti_line);
-        }
-    } else {
-        // Disable interrupts if set
-        if(LL_SYSCFG_GetEXTISource(sys_exti_line) == sys_exti_port &&
-           LL_EXTI_IsEnabledIT_0_31(exti_line)) {
-            LL_EXTI_DisableIT_0_31(exti_line);
-            LL_EXTI_DisableRisingTrig_0_31(exti_line);
-            LL_EXTI_DisableFallingTrig_0_31(exti_line);
-        }
-
-        // Prepare alternative part if any
-        if(mode == GpioModeAltFunctionPushPull || mode == GpioModeAltFunctionOpenDrain) {
-            // set alternate function
-            if(furi_hal_gpio_get_pin_num(gpio) < 8) {
-                LL_GPIO_SetAFPin_0_7(gpio->port, gpio->pin, alt_fn);
-            } else {
-                LL_GPIO_SetAFPin_8_15(gpio->port, gpio->pin, alt_fn);
-            }
-        }
-
-        // Set not interrupt pin modes
-        switch(mode) {
-        case GpioModeInput:
-            LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_INPUT);
-            break;
-        case GpioModeOutputPushPull:
-            LL_GPIO_SetPinOutputType(gpio->port, gpio->pin, LL_GPIO_OUTPUT_PUSHPULL);
-            LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_OUTPUT);
-            break;
-        case GpioModeAltFunctionPushPull:
-            LL_GPIO_SetPinOutputType(gpio->port, gpio->pin, LL_GPIO_OUTPUT_PUSHPULL);
-            LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_ALTERNATE);
-            break;
-        case GpioModeOutputOpenDrain:
-            LL_GPIO_SetPinOutputType(gpio->port, gpio->pin, LL_GPIO_OUTPUT_OPENDRAIN);
-            LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_OUTPUT);
-            break;
-        case GpioModeAltFunctionOpenDrain:
-            LL_GPIO_SetPinOutputType(gpio->port, gpio->pin, LL_GPIO_OUTPUT_OPENDRAIN);
-            LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_ALTERNATE);
-            break;
-        case GpioModeAnalog:
-            LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_ANALOG);
-            break;
-        default:
-            break;
-        }
-    }
-    __enable_irq();
-}
-
-void furi_hal_gpio_add_int_callback(const GpioPin* gpio, GpioExtiCallback cb, void* ctx) {
-    assert(gpio);
-    assert(cb);
-
-    __disable_irq();
-    uint8_t pin_num = furi_hal_gpio_get_pin_num(gpio);
-    gpio_interrupt[pin_num].callback = cb;
-    gpio_interrupt[pin_num].context = ctx;
-    gpio_interrupt[pin_num].ready = true;
-    __enable_irq();
-}
-
-void furi_hal_gpio_enable_int_callback(const GpioPin* gpio) {
-    assert(gpio);
-
-    __disable_irq();
-    uint8_t pin_num = furi_hal_gpio_get_pin_num(gpio);
-    if(gpio_interrupt[pin_num].callback) {
-        gpio_interrupt[pin_num].ready = true;
-    }
-    __enable_irq();
-}
-
-void furi_hal_gpio_disable_int_callback(const GpioPin* gpio) {
-    assert(gpio);
-
-    __disable_irq();
-    uint8_t pin_num = furi_hal_gpio_get_pin_num(gpio);
-    gpio_interrupt[pin_num].ready = false;
-    __enable_irq();
-}
-
-void furi_hal_gpio_remove_int_callback(const GpioPin* gpio) {
-    assert(gpio);
-
-    __disable_irq();
-    uint8_t pin_num = furi_hal_gpio_get_pin_num(gpio);
-    gpio_interrupt[pin_num].callback = NULL;
-    gpio_interrupt[pin_num].context = NULL;
-    gpio_interrupt[pin_num].ready = false;
-    __enable_irq();
-}

+ 0 - 264
bootloader/targets/f7/furi_hal/furi_hal_gpio.h

@@ -1,264 +0,0 @@
-#pragma once
-#include "stdbool.h"
-#include "main.h"
-#include <stm32wbxx_ll_gpio.h>
-#include <stm32wbxx_ll_system.h>
-#include <stm32wbxx_ll_exti.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- * Number of gpio on one port
- */
-#define GPIO_NUMBER (16U)
-
-/**
- * Interrupt callback prototype
- */
-typedef void (*GpioExtiCallback)(void* ctx);
-
-/**
- * Gpio interrupt type
- */
-typedef struct {
-    GpioExtiCallback callback;
-    void* context;
-    volatile bool ready;
-} GpioInterrupt;
-
-/**
- * Gpio modes
- */
-typedef enum {
-    GpioModeInput,
-    GpioModeOutputPushPull,
-    GpioModeOutputOpenDrain,
-    GpioModeAltFunctionPushPull,
-    GpioModeAltFunctionOpenDrain,
-    GpioModeAnalog,
-    GpioModeInterruptRise,
-    GpioModeInterruptFall,
-    GpioModeInterruptRiseFall,
-    GpioModeEventRise,
-    GpioModeEventFall,
-    GpioModeEventRiseFall,
-} GpioMode;
-
-/**
- * Gpio pull modes
- */
-typedef enum {
-    GpioPullNo,
-    GpioPullUp,
-    GpioPullDown,
-} GpioPull;
-
-/**
- * Gpio speed modes
- */
-typedef enum {
-    GpioSpeedLow,
-    GpioSpeedMedium,
-    GpioSpeedHigh,
-    GpioSpeedVeryHigh,
-} GpioSpeed;
-
-/**
- * Gpio alternate functions
- */
-typedef enum {
-    GpioAltFn0MCO = 0, /*!< MCO Alternate Function mapping */
-    GpioAltFn0LSCO = 0, /*!< LSCO Alternate Function mapping */
-    GpioAltFn0JTMS_SWDIO = 0, /*!< JTMS-SWDIO Alternate Function mapping */
-    GpioAltFn0JTCK_SWCLK = 0, /*!< JTCK-SWCLK Alternate Function mapping */
-    GpioAltFn0JTDI = 0, /*!< JTDI Alternate Function mapping */
-    GpioAltFn0RTC_OUT = 0, /*!< RCT_OUT Alternate Function mapping */
-    GpioAltFn0JTD_TRACE = 0, /*!< JTDO-TRACESWO Alternate Function mapping */
-    GpioAltFn0NJTRST = 0, /*!< NJTRST Alternate Function mapping */
-    GpioAltFn0RTC_REFIN = 0, /*!< RTC_REFIN Alternate Function mapping */
-    GpioAltFn0TRACED0 = 0, /*!< TRACED0 Alternate Function mapping */
-    GpioAltFn0TRACED1 = 0, /*!< TRACED1 Alternate Function mapping */
-    GpioAltFn0TRACED2 = 0, /*!< TRACED2 Alternate Function mapping */
-    GpioAltFn0TRACED3 = 0, /*!< TRACED3 Alternate Function mapping */
-    GpioAltFn0TRIG_INOUT = 0, /*!< TRIG_INOUT Alternate Function mapping */
-    GpioAltFn0TRACECK = 0, /*!< TRACECK Alternate Function mapping */
-    GpioAltFn0SYS = 0, /*!< System Function mapping */
-
-    GpioAltFn1TIM1 = 1, /*!< TIM1 Alternate Function mapping */
-    GpioAltFn1TIM2 = 1, /*!< TIM2 Alternate Function mapping */
-    GpioAltFn1LPTIM1 = 1, /*!< LPTIM1 Alternate Function mapping */
-
-    GpioAltFn2TIM2 = 2, /*!< TIM2 Alternate Function mapping */
-    GpioAltFn2TIM1 = 2, /*!< TIM1 Alternate Function mapping */
-
-    GpioAltFn3SAI1 = 3, /*!< SAI1_CK1 Alternate Function mapping */
-    GpioAltFn3SPI2 = 3, /*!< SPI2 Alternate Function mapping */
-    GpioAltFn3TIM1 = 3, /*!< TIM1 Alternate Function mapping */
-
-    GpioAltFn4I2C1 = 4, /*!< I2C1 Alternate Function mapping */
-    GpioAltFn4I2C3 = 4, /*!< I2C3 Alternate Function mapping */
-
-    GpioAltFn5SPI1 = 5, /*!< SPI1 Alternate Function mapping */
-    GpioAltFn5SPI2 = 5, /*!< SPI2 Alternate Function mapping */
-
-    GpioAltFn6MCO = 6, /*!< MCO Alternate Function mapping */
-    GpioAltFn6LSCO = 6, /*!< LSCO Alternate Function mapping */
-    GpioAltFn6RF_DTB0 = 6, /*!< RF_DTB0 Alternate Function mapping */
-    GpioAltFn6RF_DTB1 = 6, /*!< RF_DTB1 Alternate Function mapping */
-    GpioAltFn6RF_DTB2 = 6, /*!< RF_DTB2 Alternate Function mapping */
-    GpioAltFn6RF_DTB3 = 6, /*!< RF_DTB3 Alternate Function mapping */
-    GpioAltFn6RF_DTB4 = 6, /*!< RF_DTB4 Alternate Function mapping */
-    GpioAltFn6RF_DTB5 = 6, /*!< RF_DTB5 Alternate Function mapping */
-    GpioAltFn6RF_DTB6 = 6, /*!< RF_DTB6 Alternate Function mapping */
-    GpioAltFn6RF_DTB7 = 6, /*!< RF_DTB7 Alternate Function mapping */
-    GpioAltFn6RF_DTB8 = 6, /*!< RF_DTB8 Alternate Function mapping */
-    GpioAltFn6RF_DTB9 = 6, /*!< RF_DTB9 Alternate Function mapping */
-    GpioAltFn6RF_DTB10 = 6, /*!< RF_DTB10 Alternate Function mapping */
-    GpioAltFn6RF_DTB11 = 6, /*!< RF_DTB11 Alternate Function mapping */
-    GpioAltFn6RF_DTB12 = 6, /*!< RF_DTB12 Alternate Function mapping */
-    GpioAltFn6RF_DTB13 = 6, /*!< RF_DTB13 Alternate Function mapping */
-    GpioAltFn6RF_DTB14 = 6, /*!< RF_DTB14 Alternate Function mapping */
-    GpioAltFn6RF_DTB15 = 6, /*!< RF_DTB15 Alternate Function mapping */
-    GpioAltFn6RF_DTB16 = 6, /*!< RF_DTB16 Alternate Function mapping */
-    GpioAltFn6RF_DTB17 = 6, /*!< RF_DTB17 Alternate Function mapping */
-    GpioAltFn6RF_DTB18 = 6, /*!< RF_DTB18 Alternate Function mapping */
-    GpioAltFn6RF_MISO = 6, /*!< RF_MISO Alternate Function mapping */
-    GpioAltFn6RF_MOSI = 6, /*!< RF_MOSI Alternate Function mapping */
-    GpioAltFn6RF_SCK = 6, /*!< RF_SCK Alternate Function mapping */
-    GpioAltFn6RF_NSS = 6, /*!< RF_NSS Alternate Function mapping */
-
-    GpioAltFn7USART1 = 7, /*!< USART1 Alternate Function mapping */
-
-    GpioAltFn8LPUART1 = 8, /*!< LPUART1 Alternate Function mapping */
-    GpioAltFn8IR = 8, /*!< IR Alternate Function mapping */
-
-    GpioAltFn9TSC = 9, /*!< TSC Alternate Function mapping */
-
-    GpioAltFn10QUADSPI = 10, /*!< QUADSPI Alternate Function mapping */
-    GpioAltFn10USB = 10, /*!< USB Alternate Function mapping */
-
-    GpioAltFn11LCD = 11, /*!< LCD Alternate Function mapping */
-
-    GpioAltFn12COMP1 = 12, /*!< COMP1 Alternate Function mapping */
-    GpioAltFn12COMP2 = 12, /*!< COMP2 Alternate Function mapping */
-    GpioAltFn12TIM1 = 12, /*!< TIM1 Alternate Function mapping */
-
-    GpioAltFn13SAI1 = 13, /*!< SAI1 Alternate Function mapping */
-
-    GpioAltFn14TIM2 = 14, /*!< TIM2 Alternate Function mapping */
-    GpioAltFn14TIM16 = 14, /*!< TIM16 Alternate Function mapping */
-    GpioAltFn14TIM17 = 14, /*!< TIM17 Alternate Function mapping */
-    GpioAltFn14LPTIM2 = 14, /*!< LPTIM2 Alternate Function mapping */
-
-    GpioAltFn15EVENTOUT = 15, /*!< EVENTOUT Alternate Function mapping */
-
-    GpioAltFnUnused = 16, /*!< just dummy value */
-} GpioAltFn;
-
-/**
- * Gpio structure
- */
-typedef struct {
-    GPIO_TypeDef* port;
-    uint16_t pin;
-} GpioPin;
-
-/**
- * GPIO initialization function, simple version
- * @param gpio  GpioPin
- * @param mode  GpioMode
- */
-void furi_hal_gpio_init_simple(const GpioPin* gpio, const GpioMode mode);
-
-/**
- * GPIO initialization function, normal version
- * @param gpio  GpioPin
- * @param mode  GpioMode
- * @param pull  GpioPull
- * @param speed GpioSpeed
- */
-void furi_hal_gpio_init(
-    const GpioPin* gpio,
-    const GpioMode mode,
-    const GpioPull pull,
-    const GpioSpeed speed);
-
-/**
- * GPIO initialization function, extended version
- * @param gpio  GpioPin
- * @param mode  GpioMode
- * @param pull  GpioPull
- * @param speed GpioSpeed
- * @param alt_fn GpioAltFn
- */
-void furi_hal_gpio_init_ex(
-    const GpioPin* gpio,
-    const GpioMode mode,
-    const GpioPull pull,
-    const GpioSpeed speed,
-    const GpioAltFn alt_fn);
-
-/**
- * Add and enable interrupt
- * @param gpio GpioPin
- * @param cb   GpioExtiCallback
- * @param ctx  context for callback
- */
-void furi_hal_gpio_add_int_callback(const GpioPin* gpio, GpioExtiCallback cb, void* ctx);
-
-/**
- * Enable interrupt
- * @param gpio GpioPin
- */
-void furi_hal_gpio_enable_int_callback(const GpioPin* gpio);
-
-/**
- * Disable interrupt
- * @param gpio GpioPin
- */
-void furi_hal_gpio_disable_int_callback(const GpioPin* gpio);
-
-/**
- * Remove interrupt
- * @param gpio GpioPin
- */
-void furi_hal_gpio_remove_int_callback(const GpioPin* gpio);
-
-/**
- * GPIO write pin
- * @param gpio  GpioPin
- * @param state true / false
- */
-static inline void furi_hal_gpio_write(const GpioPin* gpio, const bool state) {
-    // writing to BSSR is an atomic operation
-    if(state == true) {
-        gpio->port->BSRR = gpio->pin;
-    } else {
-        gpio->port->BSRR = (uint32_t)gpio->pin << GPIO_NUMBER;
-    }
-}
-
-/**
- * GPIO read pin
- * @param gpio GpioPin
- * @return true / false
- */
-static inline bool furi_hal_gpio_read(const GpioPin* gpio) {
-    if((gpio->port->IDR & gpio->pin) != 0x00U) {
-        return true;
-    } else {
-        return false;
-    }
-}
-
-/**
- * Get RFID IN level
- * @return false = LOW, true = HIGH
- */
-bool furi_hal_gpio_get_rfid_in_level();
-
-#ifdef __cplusplus
-}
-#endif

+ 0 - 205
bootloader/targets/f7/furi_hal/furi_hal_i2c.c

@@ -1,205 +0,0 @@
-#include <furi_hal_i2c.h>
-#include <furi_hal_version.h>
-
-#include <stm32wbxx_ll_i2c.h>
-#include <stm32wbxx_ll_gpio.h>
-#include <stm32wbxx_ll_cortex.h>
-
-#include <assert.h>
-
-void furi_hal_i2c_init() {
-    furi_hal_i2c_bus_power.callback(&furi_hal_i2c_bus_power, FuriHalI2cBusEventInit);
-}
-
-void furi_hal_i2c_acquire(FuriHalI2cBusHandle* handle) {
-    handle->bus->callback(handle->bus, FuriHalI2cBusEventLock);
-
-    assert(handle->bus->current_handle == NULL);
-
-    handle->bus->current_handle = handle;
-
-    handle->bus->callback(handle->bus, FuriHalI2cBusEventActivate);
-
-    handle->callback(handle, FuriHalI2cBusHandleEventActivate);
-}
-
-void furi_hal_i2c_release(FuriHalI2cBusHandle* handle) {
-    assert(handle->bus->current_handle == handle);
-
-    handle->callback(handle, FuriHalI2cBusHandleEventDeactivate);
-
-    handle->bus->callback(handle->bus, FuriHalI2cBusEventDeactivate);
-
-    handle->bus->current_handle = NULL;
-
-    handle->bus->callback(handle->bus, FuriHalI2cBusEventUnlock);
-}
-
-bool furi_hal_i2c_tx(
-    FuriHalI2cBusHandle* handle,
-    uint8_t address,
-    const uint8_t* data,
-    uint8_t size,
-    uint32_t timeout) {
-    assert(handle->bus->current_handle == handle);
-    uint32_t time_left = timeout;
-    bool ret = true;
-
-    while(LL_I2C_IsActiveFlag_BUSY(handle->bus->i2c))
-        ;
-
-    LL_I2C_HandleTransfer(
-        handle->bus->i2c,
-        address,
-        LL_I2C_ADDRSLAVE_7BIT,
-        size,
-        LL_I2C_MODE_AUTOEND,
-        LL_I2C_GENERATE_START_WRITE);
-
-    while(!LL_I2C_IsActiveFlag_STOP(handle->bus->i2c) || size > 0) {
-        if(LL_I2C_IsActiveFlag_TXIS(handle->bus->i2c)) {
-            LL_I2C_TransmitData8(handle->bus->i2c, (*data));
-            data++;
-            size--;
-            time_left = timeout;
-        }
-
-        if(LL_SYSTICK_IsActiveCounterFlag()) {
-            if(--time_left == 0) {
-                ret = false;
-                break;
-            }
-        }
-    }
-
-    LL_I2C_ClearFlag_STOP(handle->bus->i2c);
-
-    return ret;
-}
-
-bool furi_hal_i2c_rx(
-    FuriHalI2cBusHandle* handle,
-    uint8_t address,
-    uint8_t* data,
-    uint8_t size,
-    uint32_t timeout) {
-    assert(handle->bus->current_handle == handle);
-    uint32_t time_left = timeout;
-    bool ret = true;
-
-    while(LL_I2C_IsActiveFlag_BUSY(handle->bus->i2c))
-        ;
-
-    LL_I2C_HandleTransfer(
-        handle->bus->i2c,
-        address,
-        LL_I2C_ADDRSLAVE_7BIT,
-        size,
-        LL_I2C_MODE_AUTOEND,
-        LL_I2C_GENERATE_START_READ);
-
-    while(!LL_I2C_IsActiveFlag_STOP(handle->bus->i2c) || size > 0) {
-        if(LL_I2C_IsActiveFlag_RXNE(handle->bus->i2c)) {
-            *data = LL_I2C_ReceiveData8(handle->bus->i2c);
-            data++;
-            size--;
-            time_left = timeout;
-        }
-
-        if(LL_SYSTICK_IsActiveCounterFlag()) {
-            if(--time_left == 0) {
-                ret = false;
-                break;
-            }
-        }
-    }
-
-    LL_I2C_ClearFlag_STOP(handle->bus->i2c);
-
-    return ret;
-}
-
-bool furi_hal_i2c_trx(
-    FuriHalI2cBusHandle* handle,
-    uint8_t address,
-    const uint8_t* tx_data,
-    uint8_t tx_size,
-    uint8_t* rx_data,
-    uint8_t rx_size,
-    uint32_t timeout) {
-    if(furi_hal_i2c_tx(handle, address, tx_data, tx_size, timeout) &&
-       furi_hal_i2c_rx(handle, address, rx_data, rx_size, timeout)) {
-        return true;
-    } else {
-        return false;
-    }
-}
-
-bool furi_hal_i2c_read_reg_8(
-    FuriHalI2cBusHandle* handle,
-    uint8_t i2c_addr,
-    uint8_t reg_addr,
-    uint8_t* data,
-    uint32_t timeout) {
-    assert(handle);
-
-    return furi_hal_i2c_trx(handle, i2c_addr, &reg_addr, 1, data, 1, timeout);
-}
-
-bool furi_hal_i2c_read_reg_16(
-    FuriHalI2cBusHandle* handle,
-    uint8_t i2c_addr,
-    uint8_t reg_addr,
-    uint16_t* data,
-    uint32_t timeout) {
-    assert(handle);
-
-    uint8_t reg_data[2];
-    bool ret = furi_hal_i2c_trx(handle, i2c_addr, &reg_addr, 1, reg_data, 2, timeout);
-    *data = (reg_data[0] << 8) | (reg_data[1]);
-
-    return ret;
-}
-
-bool furi_hal_i2c_read_mem(
-    FuriHalI2cBusHandle* handle,
-    uint8_t i2c_addr,
-    uint8_t mem_addr,
-    uint8_t* data,
-    uint8_t len,
-    uint32_t timeout) {
-    assert(handle);
-
-    return furi_hal_i2c_trx(handle, i2c_addr, &mem_addr, 1, data, len, timeout);
-}
-
-bool furi_hal_i2c_write_reg_8(
-    FuriHalI2cBusHandle* handle,
-    uint8_t i2c_addr,
-    uint8_t reg_addr,
-    uint8_t data,
-    uint32_t timeout) {
-    assert(handle);
-
-    uint8_t tx_data[2];
-    tx_data[0] = reg_addr;
-    tx_data[1] = data;
-
-    return furi_hal_i2c_tx(handle, i2c_addr, (const uint8_t*)&tx_data, 2, timeout);
-}
-
-bool furi_hal_i2c_write_reg_16(
-    FuriHalI2cBusHandle* handle,
-    uint8_t i2c_addr,
-    uint8_t reg_addr,
-    uint16_t data,
-    uint32_t timeout) {
-    assert(handle);
-
-    uint8_t tx_data[3];
-    tx_data[0] = reg_addr;
-    tx_data[1] = (data >> 8) & 0xFF;
-    tx_data[2] = data & 0xFF;
-
-    return furi_hal_i2c_tx(handle, i2c_addr, (const uint8_t*)&tx_data, 3, timeout);
-}

+ 0 - 195
bootloader/targets/f7/furi_hal/furi_hal_i2c.h

@@ -1,195 +0,0 @@
-/**
- * @file furi_hal_i2c.h
- * I2C HAL API
- */
-
-#pragma once
-
-#include <stdbool.h>
-#include <stdint.h>
-#include <furi_hal_i2c_config.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/** Init I2C
- */
-void furi_hal_i2c_init();
-
-/** Acquire i2c bus handle
- *
- * @return     Instance of FuriHalI2cBus
- */
-void furi_hal_i2c_acquire(FuriHalI2cBusHandle* handle);
-
-/** Release i2c bus handle
- *
- * @param      bus   instance of FuriHalI2cBus aquired in `furi_hal_i2c_acquire`
- */
-void furi_hal_i2c_release(FuriHalI2cBusHandle* handle);
-
-/** Perform I2C tx transfer
- *
- * @param      handle   pointer to FuriHalI2cBusHandle instance
- * @param      address  I2C slave address
- * @param      data     pointer to data buffer
- * @param      size     size of data buffer
- * @param      timeout  timeout in ticks
- *
- * @return     true on successful transfer, false otherwise
- */
-bool furi_hal_i2c_tx(
-    FuriHalI2cBusHandle* handle,
-    const uint8_t address,
-    const uint8_t* data,
-    const uint8_t size,
-    uint32_t timeout);
-
-/** Perform I2C rx transfer
- *
- * @param      handle   pointer to FuriHalI2cBusHandle instance
- * @param      address  I2C slave address
- * @param      data     pointer to data buffer
- * @param      size     size of data buffer
- * @param      timeout  timeout in ticks
- *
- * @return     true on successful transfer, false otherwise
- */
-bool furi_hal_i2c_rx(
-    FuriHalI2cBusHandle* handle,
-    const uint8_t address,
-    uint8_t* data,
-    const uint8_t size,
-    uint32_t timeout);
-
-/** Perform I2C tx and rx transfers
- *
- * @param      handle   pointer to FuriHalI2cBusHandle instance
- * @param      address  I2C slave address
- * @param      tx_data  pointer to tx data buffer
- * @param      tx_size  size of tx data buffer
- * @param      rx_data  pointer to rx data buffer
- * @param      rx_size  size of rx data buffer
- * @param      timeout  timeout in ticks
- *
- * @return     true on successful transfer, false otherwise
- */
-bool furi_hal_i2c_trx(
-    FuriHalI2cBusHandle* handle,
-    const uint8_t address,
-    const uint8_t* tx_data,
-    const uint8_t tx_size,
-    uint8_t* rx_data,
-    const uint8_t rx_size,
-    uint32_t timeout);
-
-/** Perform I2C device register read (8-bit)
- *
- * @param      handle   pointer to FuriHalI2cBusHandle instance
- * @param      i2c_addr I2C slave address
- * @param      reg_addr register address
- * @param      data     pointer to register value
- * @param      timeout  timeout in ticks
- *
- * @return     true on successful transfer, false otherwise
- */
-bool furi_hal_i2c_read_reg_8(
-    FuriHalI2cBusHandle* handle,
-    uint8_t i2c_addr,
-    uint8_t reg_addr,
-    uint8_t* data,
-    uint32_t timeout);
-
-/** Perform I2C device register read (16-bit)
- *
- * @param      handle   pointer to FuriHalI2cBusHandle instance
- * @param      i2c_addr I2C slave address
- * @param      reg_addr register address
- * @param      data     pointer to register value
- * @param      timeout  timeout in ticks
- *
- * @return     true on successful transfer, false otherwise
- */
-bool furi_hal_i2c_read_reg_16(
-    FuriHalI2cBusHandle* handle,
-    uint8_t i2c_addr,
-    uint8_t reg_addr,
-    uint16_t* data,
-    uint32_t timeout);
-
-/** Perform I2C device memory read
- *
- * @param      handle   pointer to FuriHalI2cBusHandle instance
- * @param      i2c_addr I2C slave address
- * @param      mem_addr memory start address
- * @param      data     pointer to data buffer
- * @param      len      size of data buffer
- * @param      timeout  timeout in ticks
- *
- * @return     true on successful transfer, false otherwise
- */
-bool furi_hal_i2c_read_mem(
-    FuriHalI2cBusHandle* handle,
-    uint8_t i2c_addr,
-    uint8_t mem_addr,
-    uint8_t* data,
-    uint8_t len,
-    uint32_t timeout);
-
-/** Perform I2C device register write (8-bit)
- *
- * @param      handle   pointer to FuriHalI2cBusHandle instance
- * @param      i2c_addr I2C slave address
- * @param      reg_addr register address
- * @param      data     register value
- * @param      timeout  timeout in ticks
- *
- * @return     true on successful transfer, false otherwise
- */
-bool furi_hal_i2c_write_reg_8(
-    FuriHalI2cBusHandle* handle,
-    uint8_t i2c_addr,
-    uint8_t reg_addr,
-    uint8_t data,
-    uint32_t timeout);
-
-/** Perform I2C device register write (16-bit)
- *
- * @param      handle   pointer to FuriHalI2cBusHandle instance
- * @param      i2c_addr I2C slave address
- * @param      reg_addr register address
- * @param      data     register value
- * @param      timeout  timeout in ticks
- *
- * @return     true on successful transfer, false otherwise
- */
-bool furi_hal_i2c_write_reg_16(
-    FuriHalI2cBusHandle* handle,
-    uint8_t i2c_addr,
-    uint8_t reg_addr,
-    uint16_t data,
-    uint32_t timeout);
-
-/** Perform I2C device memory
- *
- * @param      handle   pointer to FuriHalI2cBusHandle instance
- * @param      i2c_addr I2C slave address
- * @param      mem_addr memory start address
- * @param      data     pointer to data buffer
- * @param      len      size of data buffer
- * @param      timeout  timeout in ticks
- *
- * @return     true on successful transfer, false otherwise
- */
-bool furi_hal_i2c_write_mem(
-    FuriHalI2cBusHandle* handle,
-    uint8_t i2c_addr,
-    uint8_t mem_addr,
-    uint8_t* data,
-    uint8_t len,
-    uint32_t timeout);
-
-#ifdef __cplusplus
-}
-#endif

+ 0 - 151
bootloader/targets/f7/furi_hal/furi_hal_i2c_config.c

@@ -1,151 +0,0 @@
-#include "furi_hal_i2c_config.h"
-#include <furi_hal_resources.h>
-#include <furi_hal_version.h>
-
-#include <stm32wbxx_ll_rcc.h>
-#include <stm32wbxx_ll_bus.h>
-
-/** Timing register value is computed with the STM32CubeMX Tool,
-  * Standard Mode @100kHz with I2CCLK = 64 MHz,
-  * rise time = 0ns, fall time = 0ns
-  */
-#define FURI_HAL_I2C_CONFIG_POWER_I2C_TIMINGS_100 0x10707DBC
-
-/** Timing register value is computed with the STM32CubeMX Tool,
-  * Fast Mode @400kHz with I2CCLK = 64 MHz,
-  * rise time = 0ns, fall time = 0ns
-  */
-#define FURI_HAL_I2C_CONFIG_POWER_I2C_TIMINGS_400 0x00602173
-
-static void furi_hal_i2c_bus_power_event(FuriHalI2cBus* bus, FuriHalI2cBusEvent event) {
-    if(event == FuriHalI2cBusEventInit) {
-        LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_I2C1);
-        LL_RCC_SetI2CClockSource(LL_RCC_I2C1_CLKSOURCE_PCLK1);
-        LL_APB1_GRP1_ForceReset(LL_APB1_GRP1_PERIPH_I2C1);
-        bus->current_handle = NULL;
-    } else if(event == FuriHalI2cBusEventDeinit) {
-    } else if(event == FuriHalI2cBusEventLock) {
-    } else if(event == FuriHalI2cBusEventUnlock) {
-    } else if(event == FuriHalI2cBusEventActivate) {
-        LL_APB1_GRP1_ReleaseReset(LL_APB1_GRP1_PERIPH_I2C1);
-    } else if(event == FuriHalI2cBusEventDeactivate) {
-        LL_APB1_GRP1_ForceReset(LL_APB1_GRP1_PERIPH_I2C1);
-    }
-}
-
-FuriHalI2cBus furi_hal_i2c_bus_power = {
-    .i2c = I2C1,
-    .current_handle = NULL,
-    .callback = furi_hal_i2c_bus_power_event,
-};
-
-static void furi_hal_i2c_bus_external_event(FuriHalI2cBus* bus, FuriHalI2cBusEvent event) {
-    if(event == FuriHalI2cBusEventActivate) {
-        LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_I2C3);
-        LL_RCC_SetI2CClockSource(LL_RCC_I2C3_CLKSOURCE_PCLK1);
-        LL_APB1_GRP1_ReleaseReset(LL_APB1_GRP1_PERIPH_I2C3);
-    } else if(event == FuriHalI2cBusEventDeactivate) {
-        LL_APB1_GRP1_ForceReset(LL_APB1_GRP1_PERIPH_I2C3);
-    }
-}
-
-FuriHalI2cBus furi_hal_i2c_bus_external = {
-    .i2c = I2C3,
-    .current_handle = NULL,
-    .callback = furi_hal_i2c_bus_external_event,
-};
-
-void furi_hal_i2c_bus_handle_power_event(
-    FuriHalI2cBusHandle* handle,
-    FuriHalI2cBusHandleEvent event) {
-    if(event == FuriHalI2cBusHandleEventActivate) {
-        LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOA);
-        furi_hal_gpio_init_ex(
-            &gpio_i2c_power_sda,
-            GpioModeAltFunctionOpenDrain,
-            GpioPullNo,
-            GpioSpeedLow,
-            GpioAltFn4I2C1);
-        furi_hal_gpio_init_ex(
-            &gpio_i2c_power_scl,
-            GpioModeAltFunctionOpenDrain,
-            GpioPullNo,
-            GpioSpeedLow,
-            GpioAltFn4I2C1);
-
-        LL_I2C_InitTypeDef I2C_InitStruct = {0};
-        I2C_InitStruct.PeripheralMode = LL_I2C_MODE_I2C;
-        I2C_InitStruct.AnalogFilter = LL_I2C_ANALOGFILTER_ENABLE;
-        I2C_InitStruct.DigitalFilter = 0;
-        I2C_InitStruct.OwnAddress1 = 0;
-        I2C_InitStruct.TypeAcknowledge = LL_I2C_ACK;
-        I2C_InitStruct.OwnAddrSize = LL_I2C_OWNADDRESS1_7BIT;
-        if(furi_hal_version_get_hw_version() > 10) {
-            I2C_InitStruct.Timing = FURI_HAL_I2C_CONFIG_POWER_I2C_TIMINGS_400;
-        } else {
-            I2C_InitStruct.Timing = FURI_HAL_I2C_CONFIG_POWER_I2C_TIMINGS_100;
-        }
-        LL_I2C_Init(handle->bus->i2c, &I2C_InitStruct);
-
-        LL_I2C_EnableAutoEndMode(handle->bus->i2c);
-        LL_I2C_SetOwnAddress2(handle->bus->i2c, 0, LL_I2C_OWNADDRESS2_NOMASK);
-        LL_I2C_DisableOwnAddress2(handle->bus->i2c);
-        LL_I2C_DisableGeneralCall(handle->bus->i2c);
-        LL_I2C_EnableClockStretching(handle->bus->i2c);
-        LL_I2C_Enable(handle->bus->i2c);
-    } else if(event == FuriHalI2cBusHandleEventDeactivate) {
-        LL_I2C_Disable(handle->bus->i2c);
-        furi_hal_gpio_write(&gpio_i2c_power_sda, 1);
-        furi_hal_gpio_write(&gpio_i2c_power_scl, 1);
-        furi_hal_gpio_init_ex(
-            &gpio_i2c_power_sda, GpioModeAnalog, GpioPullNo, GpioSpeedLow, GpioAltFnUnused);
-        furi_hal_gpio_init_ex(
-            &gpio_i2c_power_scl, GpioModeAnalog, GpioPullNo, GpioSpeedLow, GpioAltFnUnused);
-    }
-}
-
-FuriHalI2cBusHandle furi_hal_i2c_handle_power = {
-    .bus = &furi_hal_i2c_bus_power,
-    .callback = furi_hal_i2c_bus_handle_power_event,
-};
-
-void furi_hal_i2c_bus_handle_external_event(
-    FuriHalI2cBusHandle* handle,
-    FuriHalI2cBusHandleEvent event) {
-    if(event == FuriHalI2cBusHandleEventActivate) {
-        furi_hal_gpio_init_ex(
-            &gpio_ext_pc0, GpioModeAltFunctionOpenDrain, GpioPullNo, GpioSpeedLow, GpioAltFn4I2C3);
-        furi_hal_gpio_init_ex(
-            &gpio_ext_pc1, GpioModeAltFunctionOpenDrain, GpioPullNo, GpioSpeedLow, GpioAltFn4I2C3);
-
-        LL_I2C_InitTypeDef I2C_InitStruct = {0};
-        I2C_InitStruct.PeripheralMode = LL_I2C_MODE_I2C;
-        I2C_InitStruct.AnalogFilter = LL_I2C_ANALOGFILTER_ENABLE;
-        I2C_InitStruct.DigitalFilter = 0;
-        I2C_InitStruct.OwnAddress1 = 0;
-        I2C_InitStruct.TypeAcknowledge = LL_I2C_ACK;
-        I2C_InitStruct.OwnAddrSize = LL_I2C_OWNADDRESS1_7BIT;
-        I2C_InitStruct.Timing = FURI_HAL_I2C_CONFIG_POWER_I2C_TIMINGS_100;
-        LL_I2C_Init(handle->bus->i2c, &I2C_InitStruct);
-
-        LL_I2C_EnableAutoEndMode(handle->bus->i2c);
-        LL_I2C_SetOwnAddress2(handle->bus->i2c, 0, LL_I2C_OWNADDRESS2_NOMASK);
-        LL_I2C_DisableOwnAddress2(handle->bus->i2c);
-        LL_I2C_DisableGeneralCall(handle->bus->i2c);
-        LL_I2C_EnableClockStretching(handle->bus->i2c);
-        LL_I2C_Enable(handle->bus->i2c);
-    } else if(event == FuriHalI2cBusHandleEventDeactivate) {
-        LL_I2C_Disable(handle->bus->i2c);
-        furi_hal_gpio_write(&gpio_ext_pc0, 1);
-        furi_hal_gpio_write(&gpio_ext_pc1, 1);
-        furi_hal_gpio_init_ex(
-            &gpio_ext_pc0, GpioModeAnalog, GpioPullNo, GpioSpeedLow, GpioAltFnUnused);
-        furi_hal_gpio_init_ex(
-            &gpio_ext_pc1, GpioModeAnalog, GpioPullNo, GpioSpeedLow, GpioAltFnUnused);
-    }
-}
-
-FuriHalI2cBusHandle furi_hal_i2c_handle_external = {
-    .bus = &furi_hal_i2c_bus_external,
-    .callback = furi_hal_i2c_bus_handle_external_event,
-};

+ 0 - 31
bootloader/targets/f7/furi_hal/furi_hal_i2c_config.h

@@ -1,31 +0,0 @@
-#pragma once
-
-#include <furi_hal_i2c_types.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/** Internal(power) i2c bus, I2C1, under reset when not used */
-extern FuriHalI2cBus furi_hal_i2c_bus_power;
-
-/** External i2c bus, I2C3, under reset when not used */
-extern FuriHalI2cBus furi_hal_i2c_bus_external;
-
-/** Handle for internal(power) i2c bus
- * Bus: furi_hal_i2c_bus_external
- * Pins: PA9(SCL) / PA10(SDA), float on release
- * Params: 400khz
- */
-extern FuriHalI2cBusHandle furi_hal_i2c_handle_power;
-
-/** Handle for external i2c bus
- * Bus: furi_hal_i2c_bus_external
- * Pins: PC0(SCL) / PC1(SDA), float on release
- * Params: 100khz
- */
-extern FuriHalI2cBusHandle furi_hal_i2c_handle_external;
-
-#ifdef __cplusplus
-}
-#endif

+ 0 - 51
bootloader/targets/f7/furi_hal/furi_hal_i2c_types.h

@@ -1,51 +0,0 @@
-#pragma once
-
-#include <stm32wbxx_ll_i2c.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef struct FuriHalI2cBus FuriHalI2cBus;
-typedef struct FuriHalI2cBusHandle FuriHalI2cBusHandle;
-
-/** FuriHal i2c bus states */
-typedef enum {
-    FuriHalI2cBusEventInit, /**< Bus initialization event, called on system start */
-    FuriHalI2cBusEventDeinit, /**< Bus deinitialization event, called on system stop */
-    FuriHalI2cBusEventLock, /**< Bus lock event, called before activation */
-    FuriHalI2cBusEventUnlock, /**< Bus unlock event, called after deactivation */
-    FuriHalI2cBusEventActivate, /**< Bus activation event, called before handle activation */
-    FuriHalI2cBusEventDeactivate, /**< Bus deactivation event, called after handle deactivation  */
-} FuriHalI2cBusEvent;
-
-/** FuriHal i2c bus event callback */
-typedef void (*FuriHalI2cBusEventCallback)(FuriHalI2cBus* bus, FuriHalI2cBusEvent event);
-
-/** FuriHal i2c bus */
-struct FuriHalI2cBus {
-    I2C_TypeDef* i2c;
-    FuriHalI2cBusHandle* current_handle;
-    FuriHalI2cBusEventCallback callback;
-};
-
-/** FuriHal i2c handle states */
-typedef enum {
-    FuriHalI2cBusHandleEventActivate, /**< Handle activate: connect gpio and apply bus config */
-    FuriHalI2cBusHandleEventDeactivate, /**< Handle deactivate: disconnect gpio and reset bus config */
-} FuriHalI2cBusHandleEvent;
-
-/** FuriHal i2c handle event callback */
-typedef void (*FuriHalI2cBusHandleEventCallback)(
-    FuriHalI2cBusHandle* handle,
-    FuriHalI2cBusHandleEvent event);
-
-/** FuriHal i2c handle */
-struct FuriHalI2cBusHandle {
-    FuriHalI2cBus* bus;
-    FuriHalI2cBusHandleEventCallback callback;
-};
-
-#ifdef __cplusplus
-}
-#endif

+ 0 - 49
bootloader/targets/f7/furi_hal/furi_hal_light.c

@@ -1,49 +0,0 @@
-#include <furi_hal_light.h>
-#include <lp5562.h>
-
-#define LED_CURRENT_RED 50
-#define LED_CURRENT_GREEN 50
-#define LED_CURRENT_BLUE 50
-#define LED_CURRENT_WHITE 150
-
-void furi_hal_light_init() {
-    furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
-
-    lp5562_reset(&furi_hal_i2c_handle_power);
-
-    lp5562_set_channel_current(&furi_hal_i2c_handle_power, LP5562ChannelRed, LED_CURRENT_RED);
-    lp5562_set_channel_current(&furi_hal_i2c_handle_power, LP5562ChannelGreen, LED_CURRENT_GREEN);
-    lp5562_set_channel_current(&furi_hal_i2c_handle_power, LP5562ChannelBlue, LED_CURRENT_BLUE);
-    lp5562_set_channel_current(&furi_hal_i2c_handle_power, LP5562ChannelWhite, LED_CURRENT_WHITE);
-
-    lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelRed, 0x00);
-    lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelGreen, 0x00);
-    lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelBlue, 0x00);
-    lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelWhite, 0x00);
-
-    lp5562_enable(&furi_hal_i2c_handle_power);
-    lp5562_configure(&furi_hal_i2c_handle_power);
-
-    furi_hal_i2c_release(&furi_hal_i2c_handle_power);
-}
-
-void furi_hal_light_set(Light light, uint8_t value) {
-    furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
-    switch(light) {
-    case LightRed:
-        lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelRed, value);
-        break;
-    case LightGreen:
-        lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelGreen, value);
-        break;
-    case LightBlue:
-        lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelBlue, value);
-        break;
-    case LightBacklight:
-        lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelWhite, value);
-        break;
-    default:
-        break;
-    }
-    furi_hal_i2c_release(&furi_hal_i2c_handle_power);
-}

+ 0 - 17
bootloader/targets/f7/furi_hal/furi_hal_light.h

@@ -1,17 +0,0 @@
-#pragma once
-
-#include <stdbool.h>
-#include <stdint.h>
-#include <furi_hal_resources.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-void furi_hal_light_init();
-
-void furi_hal_light_set(Light light, uint8_t value);
-
-#ifdef __cplusplus
-}
-#endif

+ 0 - 43
bootloader/targets/f7/furi_hal/furi_hal_resources.c

@@ -1,43 +0,0 @@
-#include "furi_hal_resources.h"
-
-const GpioPin vibro_gpio = {.port = VIBRO_GPIO_Port, .pin = VIBRO_Pin};
-const GpioPin ibutton_gpio = {.port = iBTN_GPIO_Port, .pin = iBTN_Pin};
-
-const GpioPin gpio_cc1101_g0 = {.port = CC1101_G0_GPIO_Port, .pin = CC1101_G0_Pin};
-const GpioPin gpio_rf_sw_0 = {.port = RF_SW_0_GPIO_Port, .pin = RF_SW_0_Pin};
-
-const GpioPin gpio_subghz_cs = {.port = CC1101_CS_GPIO_Port, .pin = CC1101_CS_Pin};
-const GpioPin gpio_display_cs = {.port = DISPLAY_CS_GPIO_Port, .pin = DISPLAY_CS_Pin};
-const GpioPin gpio_display_rst = {.port = DISPLAY_RST_GPIO_Port, .pin = DISPLAY_RST_Pin};
-const GpioPin gpio_display_di = {.port = DISPLAY_DI_GPIO_Port, .pin = DISPLAY_DI_Pin};
-const GpioPin gpio_sdcard_cs = {.port = SD_CS_GPIO_Port, .pin = SD_CS_Pin};
-const GpioPin gpio_nfc_cs = {.port = NFC_CS_GPIO_Port, .pin = NFC_CS_Pin};
-
-const GpioPin gpio_spi_d_miso = {.port = SPI_D_MISO_GPIO_Port, .pin = SPI_D_MISO_Pin};
-const GpioPin gpio_spi_d_mosi = {.port = SPI_D_MOSI_GPIO_Port, .pin = SPI_D_MOSI_Pin};
-const GpioPin gpio_spi_d_sck = {.port = SPI_D_SCK_GPIO_Port, .pin = SPI_D_SCK_Pin};
-const GpioPin gpio_spi_r_miso = {.port = SPI_R_MISO_GPIO_Port, .pin = SPI_R_MISO_Pin};
-const GpioPin gpio_spi_r_mosi = {.port = SPI_R_MOSI_GPIO_Port, .pin = SPI_R_MOSI_Pin};
-const GpioPin gpio_spi_r_sck = {.port = SPI_R_SCK_GPIO_Port, .pin = SPI_R_SCK_Pin};
-
-const GpioPin gpio_ext_pc0 = {.port = GPIOC, .pin = LL_GPIO_PIN_0};
-const GpioPin gpio_ext_pc1 = {.port = GPIOC, .pin = LL_GPIO_PIN_1};
-const GpioPin gpio_ext_pc3 = {.port = GPIOC, .pin = LL_GPIO_PIN_3};
-const GpioPin gpio_ext_pb2 = {.port = GPIOB, .pin = LL_GPIO_PIN_2};
-const GpioPin gpio_ext_pb3 = {.port = GPIOB, .pin = LL_GPIO_PIN_3};
-const GpioPin gpio_ext_pa4 = {.port = GPIOA, .pin = LL_GPIO_PIN_4};
-const GpioPin gpio_ext_pa6 = {.port = GPIOA, .pin = LL_GPIO_PIN_6};
-const GpioPin gpio_ext_pa7 = {.port = GPIOA, .pin = LL_GPIO_PIN_7};
-
-const GpioPin gpio_rfid_pull = {.port = RFID_PULL_GPIO_Port, .pin = RFID_PULL_Pin};
-const GpioPin gpio_rfid_carrier_out = {.port = RFID_OUT_GPIO_Port, .pin = RFID_OUT_Pin};
-const GpioPin gpio_rfid_data_in = {.port = RFID_RF_IN_GPIO_Port, .pin = RFID_RF_IN_Pin};
-
-const GpioPin gpio_infrared_rx = {.port = IR_RX_GPIO_Port, .pin = IR_RX_Pin};
-const GpioPin gpio_infrared_tx = {.port = IR_TX_GPIO_Port, .pin = IR_TX_Pin};
-
-const GpioPin gpio_usart_tx = {.port = USART1_TX_Port, .pin = USART1_TX_Pin};
-const GpioPin gpio_usart_rx = {.port = USART1_RX_Port, .pin = USART1_RX_Pin};
-
-const GpioPin gpio_i2c_power_sda = {.port = GPIOA, .pin = LL_GPIO_PIN_10};
-const GpioPin gpio_i2c_power_scl = {.port = GPIOA, .pin = LL_GPIO_PIN_9};

+ 0 - 73
bootloader/targets/f7/furi_hal/furi_hal_resources.h

@@ -1,73 +0,0 @@
-#pragma once
-
-#include <stm32wbxx.h>
-#include <stm32wbxx_ll_gpio.h>
-#include <furi_hal_gpio.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* Input Keys */
-typedef enum {
-    InputKeyUp,
-    InputKeyDown,
-    InputKeyRight,
-    InputKeyLeft,
-    InputKeyOk,
-    InputKeyBack,
-} InputKey;
-
-/* Light */
-typedef enum {
-    LightRed,
-    LightGreen,
-    LightBlue,
-    LightBacklight,
-} Light;
-
-extern const GpioPin vibro_gpio;
-extern const GpioPin ibutton_gpio;
-
-extern const GpioPin gpio_cc1101_g0;
-extern const GpioPin gpio_rf_sw_0;
-
-extern const GpioPin gpio_subghz_cs;
-extern const GpioPin gpio_display_cs;
-extern const GpioPin gpio_display_rst;
-extern const GpioPin gpio_display_di;
-extern const GpioPin gpio_sdcard_cs;
-extern const GpioPin gpio_nfc_cs;
-
-extern const GpioPin gpio_spi_d_miso;
-extern const GpioPin gpio_spi_d_mosi;
-extern const GpioPin gpio_spi_d_sck;
-extern const GpioPin gpio_spi_r_miso;
-extern const GpioPin gpio_spi_r_mosi;
-extern const GpioPin gpio_spi_r_sck;
-
-extern const GpioPin gpio_ext_pc0;
-extern const GpioPin gpio_ext_pc1;
-extern const GpioPin gpio_ext_pc3;
-extern const GpioPin gpio_ext_pb2;
-extern const GpioPin gpio_ext_pb3;
-extern const GpioPin gpio_ext_pa4;
-extern const GpioPin gpio_ext_pa6;
-extern const GpioPin gpio_ext_pa7;
-
-extern const GpioPin gpio_rfid_pull;
-extern const GpioPin gpio_rfid_carrier_out;
-extern const GpioPin gpio_rfid_data_in;
-
-extern const GpioPin gpio_infrared_rx;
-extern const GpioPin gpio_infrared_tx;
-
-extern const GpioPin gpio_usart_tx;
-extern const GpioPin gpio_usart_rx;
-
-extern const GpioPin gpio_i2c_power_sda;
-extern const GpioPin gpio_i2c_power_scl;
-
-#ifdef __cplusplus
-}
-#endif

+ 0 - 151
bootloader/targets/f7/furi_hal/furi_hal_spi.c

@@ -1,151 +0,0 @@
-#include "furi_hal_spi.h"
-#include "furi_hal_resources.h"
-
-#include <stdbool.h>
-#include <string.h>
-#include <assert.h>
-
-#include <stm32wbxx_ll_spi.h>
-#include <stm32wbxx_ll_utils.h>
-#include <stm32wbxx_ll_cortex.h>
-
-void furi_hal_spi_init() {
-    furi_hal_spi_bus_init(&furi_hal_spi_bus_r);
-    furi_hal_spi_bus_init(&furi_hal_spi_bus_d);
-
-    furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_subghz);
-    furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_nfc);
-    furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_display);
-    furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_sd_fast);
-    furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_sd_slow);
-}
-
-void furi_hal_spi_bus_init(FuriHalSpiBus* bus) {
-    assert(bus);
-    bus->callback(bus, FuriHalSpiBusEventInit);
-}
-
-void furi_hal_spi_bus_deinit(FuriHalSpiBus* bus) {
-    assert(bus);
-    bus->callback(bus, FuriHalSpiBusEventDeinit);
-}
-
-void furi_hal_spi_bus_handle_init(FuriHalSpiBusHandle* handle) {
-    assert(handle);
-    handle->callback(handle, FuriHalSpiBusHandleEventInit);
-}
-
-void furi_hal_spi_bus_handle_deinit(FuriHalSpiBusHandle* handle) {
-    assert(handle);
-    handle->callback(handle, FuriHalSpiBusHandleEventDeinit);
-}
-
-void furi_hal_spi_acquire(FuriHalSpiBusHandle* handle) {
-    assert(handle);
-
-    handle->bus->callback(handle->bus, FuriHalSpiBusEventLock);
-    handle->bus->callback(handle->bus, FuriHalSpiBusEventActivate);
-
-    assert(handle->bus->current_handle == NULL);
-
-    handle->bus->current_handle = handle;
-    handle->callback(handle, FuriHalSpiBusHandleEventActivate);
-}
-
-void furi_hal_spi_release(FuriHalSpiBusHandle* handle) {
-    assert(handle);
-    assert(handle->bus->current_handle == handle);
-
-    // Handle event and unset handle
-    handle->callback(handle, FuriHalSpiBusHandleEventDeactivate);
-    handle->bus->current_handle = NULL;
-
-    // Bus events
-    handle->bus->callback(handle->bus, FuriHalSpiBusEventDeactivate);
-    handle->bus->callback(handle->bus, FuriHalSpiBusEventUnlock);
-}
-
-static void furi_hal_spi_bus_end_txrx(FuriHalSpiBusHandle* handle, uint32_t timeout) {
-    while(LL_SPI_GetTxFIFOLevel(handle->bus->spi) != LL_SPI_TX_FIFO_EMPTY)
-        ;
-    while(LL_SPI_IsActiveFlag_BSY(handle->bus->spi))
-        ;
-    while(LL_SPI_GetRxFIFOLevel(handle->bus->spi) != LL_SPI_RX_FIFO_EMPTY) {
-        LL_SPI_ReceiveData8(handle->bus->spi);
-    }
-}
-
-bool furi_hal_spi_bus_rx(
-    FuriHalSpiBusHandle* handle,
-    uint8_t* buffer,
-    size_t size,
-    uint32_t timeout) {
-    assert(handle);
-    assert(handle->bus->current_handle == handle);
-    assert(buffer);
-    assert(size > 0);
-
-    return furi_hal_spi_bus_trx(handle, buffer, buffer, size, timeout);
-}
-
-bool furi_hal_spi_bus_tx(
-    FuriHalSpiBusHandle* handle,
-    uint8_t* buffer,
-    size_t size,
-    uint32_t timeout) {
-    assert(handle);
-    assert(handle->bus->current_handle == handle);
-    assert(buffer);
-    assert(size > 0);
-    bool ret = true;
-
-    while(size > 0) {
-        if(LL_SPI_IsActiveFlag_TXE(handle->bus->spi)) {
-            LL_SPI_TransmitData8(handle->bus->spi, *buffer);
-            buffer++;
-            size--;
-        }
-    }
-
-    furi_hal_spi_bus_end_txrx(handle, timeout);
-    LL_SPI_ClearFlag_OVR(handle->bus->spi);
-
-    return ret;
-}
-
-bool furi_hal_spi_bus_trx(
-    FuriHalSpiBusHandle* handle,
-    uint8_t* tx_buffer,
-    uint8_t* rx_buffer,
-    size_t size,
-    uint32_t timeout) {
-    assert(handle);
-    assert(handle->bus->current_handle == handle);
-    assert(tx_buffer);
-    assert(rx_buffer);
-    assert(size > 0);
-
-    bool ret = true;
-    size_t tx_size = size;
-    bool tx_allowed = true;
-
-    while(size > 0) {
-        if(tx_size > 0 && LL_SPI_IsActiveFlag_TXE(handle->bus->spi) && tx_allowed) {
-            LL_SPI_TransmitData8(handle->bus->spi, *tx_buffer);
-            tx_buffer++;
-            tx_size--;
-            tx_allowed = false;
-        }
-
-        if(LL_SPI_IsActiveFlag_RXNE(handle->bus->spi)) {
-            *rx_buffer = LL_SPI_ReceiveData8(handle->bus->spi);
-            rx_buffer++;
-            size--;
-            tx_allowed = true;
-        }
-    }
-
-    furi_hal_spi_bus_end_txrx(handle, timeout);
-
-    return ret;
-}

+ 0 - 290
bootloader/targets/f7/furi_hal/furi_hal_spi_config.c

@@ -1,290 +0,0 @@
-#include <furi_hal_spi_config.h>
-#include <furi_hal_resources.h>
-
-/* SPI Presets */
-
-const LL_SPI_InitTypeDef furi_hal_spi_preset_2edge_low_8m = {
-    .Mode = LL_SPI_MODE_MASTER,
-    .TransferDirection = LL_SPI_FULL_DUPLEX,
-    .DataWidth = LL_SPI_DATAWIDTH_8BIT,
-    .ClockPolarity = LL_SPI_POLARITY_LOW,
-    .ClockPhase = LL_SPI_PHASE_2EDGE,
-    .NSS = LL_SPI_NSS_SOFT,
-    .BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV8,
-    .BitOrder = LL_SPI_MSB_FIRST,
-    .CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE,
-    .CRCPoly = 7,
-};
-
-const LL_SPI_InitTypeDef furi_hal_spi_preset_1edge_low_8m = {
-    .Mode = LL_SPI_MODE_MASTER,
-    .TransferDirection = LL_SPI_FULL_DUPLEX,
-    .DataWidth = LL_SPI_DATAWIDTH_8BIT,
-    .ClockPolarity = LL_SPI_POLARITY_LOW,
-    .ClockPhase = LL_SPI_PHASE_1EDGE,
-    .NSS = LL_SPI_NSS_SOFT,
-    .BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV8,
-    .BitOrder = LL_SPI_MSB_FIRST,
-    .CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE,
-    .CRCPoly = 7,
-};
-
-const LL_SPI_InitTypeDef furi_hal_spi_preset_1edge_low_4m = {
-    .Mode = LL_SPI_MODE_MASTER,
-    .TransferDirection = LL_SPI_FULL_DUPLEX,
-    .DataWidth = LL_SPI_DATAWIDTH_8BIT,
-    .ClockPolarity = LL_SPI_POLARITY_LOW,
-    .ClockPhase = LL_SPI_PHASE_1EDGE,
-    .NSS = LL_SPI_NSS_SOFT,
-    .BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV16,
-    .BitOrder = LL_SPI_MSB_FIRST,
-    .CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE,
-    .CRCPoly = 7,
-};
-
-const LL_SPI_InitTypeDef furi_hal_spi_preset_1edge_low_16m = {
-    .Mode = LL_SPI_MODE_MASTER,
-    .TransferDirection = LL_SPI_FULL_DUPLEX,
-    .DataWidth = LL_SPI_DATAWIDTH_8BIT,
-    .ClockPolarity = LL_SPI_POLARITY_LOW,
-    .ClockPhase = LL_SPI_PHASE_1EDGE,
-    .NSS = LL_SPI_NSS_SOFT,
-    .BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV2,
-    .BitOrder = LL_SPI_MSB_FIRST,
-    .CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE,
-    .CRCPoly = 7,
-};
-
-const LL_SPI_InitTypeDef furi_hal_spi_preset_1edge_low_2m = {
-    .Mode = LL_SPI_MODE_MASTER,
-    .TransferDirection = LL_SPI_FULL_DUPLEX,
-    .DataWidth = LL_SPI_DATAWIDTH_8BIT,
-    .ClockPolarity = LL_SPI_POLARITY_LOW,
-    .ClockPhase = LL_SPI_PHASE_1EDGE,
-    .NSS = LL_SPI_NSS_SOFT,
-    .BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV32,
-    .BitOrder = LL_SPI_MSB_FIRST,
-    .CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE,
-    .CRCPoly = 7,
-};
-
-/* SPI Buses */
-
-static void furi_hal_spi_bus_r_event_callback(FuriHalSpiBus* bus, FuriHalSpiBusEvent event) {
-    if(event == FuriHalSpiBusEventInit) {
-        LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SPI1);
-        LL_APB2_GRP1_ForceReset(LL_APB2_GRP1_PERIPH_SPI1);
-        bus->current_handle = NULL;
-    } else if(event == FuriHalSpiBusEventDeinit) {
-    } else if(event == FuriHalSpiBusEventLock) {
-    } else if(event == FuriHalSpiBusEventUnlock) {
-    } else if(event == FuriHalSpiBusEventActivate) {
-        LL_APB2_GRP1_ReleaseReset(LL_APB2_GRP1_PERIPH_SPI1);
-    } else if(event == FuriHalSpiBusEventDeactivate) {
-        LL_APB2_GRP1_ForceReset(LL_APB2_GRP1_PERIPH_SPI1);
-    }
-}
-
-FuriHalSpiBus furi_hal_spi_bus_r = {
-    .spi = SPI1,
-    .callback = furi_hal_spi_bus_r_event_callback,
-};
-
-static void furi_hal_spi_bus_d_event_callback(FuriHalSpiBus* bus, FuriHalSpiBusEvent event) {
-    if(event == FuriHalSpiBusEventInit) {
-        LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_SPI2);
-        LL_APB1_GRP1_ForceReset(LL_APB1_GRP1_PERIPH_SPI2);
-        bus->current_handle = NULL;
-    } else if(event == FuriHalSpiBusEventDeinit) {
-    } else if(event == FuriHalSpiBusEventLock) {
-    } else if(event == FuriHalSpiBusEventUnlock) {
-    } else if(event == FuriHalSpiBusEventActivate) {
-        LL_APB1_GRP1_ReleaseReset(LL_APB1_GRP1_PERIPH_SPI2);
-    } else if(event == FuriHalSpiBusEventDeactivate) {
-        LL_APB1_GRP1_ForceReset(LL_APB1_GRP1_PERIPH_SPI2);
-    }
-}
-
-FuriHalSpiBus furi_hal_spi_bus_d = {
-    .spi = SPI2,
-    .callback = furi_hal_spi_bus_d_event_callback,
-};
-
-/* SPI Bus Handles */
-
-inline static void furi_hal_spi_bus_r_handle_event_callback(
-    FuriHalSpiBusHandle* handle,
-    FuriHalSpiBusHandleEvent event,
-    const LL_SPI_InitTypeDef* preset) {
-    if(event == FuriHalSpiBusHandleEventInit) {
-        furi_hal_gpio_write(handle->cs, true);
-        furi_hal_gpio_init(handle->cs, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
-    } else if(event == FuriHalSpiBusHandleEventDeinit) {
-        furi_hal_gpio_write(handle->cs, true);
-        furi_hal_gpio_init(handle->cs, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
-    } else if(event == FuriHalSpiBusHandleEventActivate) {
-        LL_SPI_Init(handle->bus->spi, (LL_SPI_InitTypeDef*)preset);
-        LL_SPI_SetRxFIFOThreshold(handle->bus->spi, LL_SPI_RX_FIFO_TH_QUARTER);
-        LL_SPI_Enable(handle->bus->spi);
-
-        furi_hal_gpio_init_ex(
-            handle->miso,
-            GpioModeAltFunctionPushPull,
-            GpioPullNo,
-            GpioSpeedVeryHigh,
-            GpioAltFn5SPI1);
-        furi_hal_gpio_init_ex(
-            handle->mosi,
-            GpioModeAltFunctionPushPull,
-            GpioPullNo,
-            GpioSpeedVeryHigh,
-            GpioAltFn5SPI1);
-        furi_hal_gpio_init_ex(
-            handle->sck,
-            GpioModeAltFunctionPushPull,
-            GpioPullNo,
-            GpioSpeedVeryHigh,
-            GpioAltFn5SPI1);
-
-        furi_hal_gpio_write(handle->cs, false);
-    } else if(event == FuriHalSpiBusHandleEventDeactivate) {
-        furi_hal_gpio_write(handle->cs, true);
-
-        furi_hal_gpio_init(handle->miso, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
-        furi_hal_gpio_init(handle->mosi, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
-        furi_hal_gpio_init(handle->sck, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
-
-        LL_SPI_Disable(handle->bus->spi);
-    }
-}
-
-static void furi_hal_spi_bus_handle_subghz_event_callback(
-    FuriHalSpiBusHandle* handle,
-    FuriHalSpiBusHandleEvent event) {
-    furi_hal_spi_bus_r_handle_event_callback(handle, event, &furi_hal_spi_preset_1edge_low_8m);
-}
-
-FuriHalSpiBusHandle furi_hal_spi_bus_handle_subghz = {
-    .bus = &furi_hal_spi_bus_r,
-    .callback = furi_hal_spi_bus_handle_subghz_event_callback,
-    .miso = &gpio_spi_r_miso,
-    .mosi = &gpio_spi_r_mosi,
-    .sck = &gpio_spi_r_sck,
-    .cs = &gpio_subghz_cs,
-};
-
-static void furi_hal_spi_bus_handle_nfc_event_callback(
-    FuriHalSpiBusHandle* handle,
-    FuriHalSpiBusHandleEvent event) {
-    furi_hal_spi_bus_r_handle_event_callback(handle, event, &furi_hal_spi_preset_2edge_low_8m);
-}
-
-FuriHalSpiBusHandle furi_hal_spi_bus_handle_nfc = {
-    .bus = &furi_hal_spi_bus_r,
-    .callback = furi_hal_spi_bus_handle_nfc_event_callback,
-    .miso = &gpio_spi_r_miso,
-    .mosi = &gpio_spi_r_mosi,
-    .sck = &gpio_spi_r_sck,
-    .cs = &gpio_nfc_cs,
-};
-
-static void furi_hal_spi_bus_handle_external_event_callback(
-    FuriHalSpiBusHandle* handle,
-    FuriHalSpiBusHandleEvent event) {
-    furi_hal_spi_bus_r_handle_event_callback(handle, event, &furi_hal_spi_preset_1edge_low_2m);
-}
-
-FuriHalSpiBusHandle furi_hal_spi_bus_handle_external = {
-    .bus = &furi_hal_spi_bus_r,
-    .callback = furi_hal_spi_bus_handle_external_event_callback,
-    .miso = &gpio_ext_pa6,
-    .mosi = &gpio_ext_pa7,
-    .sck = &gpio_ext_pb3,
-    .cs = &gpio_ext_pa4,
-};
-
-inline static void furi_hal_spi_bus_d_handle_event_callback(
-    FuriHalSpiBusHandle* handle,
-    FuriHalSpiBusHandleEvent event,
-    const LL_SPI_InitTypeDef* preset) {
-    if(event == FuriHalSpiBusHandleEventInit) {
-        furi_hal_gpio_write(handle->cs, true);
-        furi_hal_gpio_init(handle->cs, GpioModeOutputPushPull, GpioPullUp, GpioSpeedVeryHigh);
-
-        furi_hal_gpio_init_ex(
-            handle->miso,
-            GpioModeAltFunctionPushPull,
-            GpioPullNo,
-            GpioSpeedVeryHigh,
-            GpioAltFn5SPI2);
-        furi_hal_gpio_init_ex(
-            handle->mosi,
-            GpioModeAltFunctionPushPull,
-            GpioPullNo,
-            GpioSpeedVeryHigh,
-            GpioAltFn5SPI2);
-        furi_hal_gpio_init_ex(
-            handle->sck,
-            GpioModeAltFunctionPushPull,
-            GpioPullNo,
-            GpioSpeedVeryHigh,
-            GpioAltFn5SPI2);
-
-    } else if(event == FuriHalSpiBusHandleEventDeinit) {
-        furi_hal_gpio_write(handle->cs, true);
-        furi_hal_gpio_init(handle->cs, GpioModeAnalog, GpioPullUp, GpioSpeedLow);
-    } else if(event == FuriHalSpiBusHandleEventActivate) {
-        LL_SPI_Init(handle->bus->spi, (LL_SPI_InitTypeDef*)preset);
-        LL_SPI_SetRxFIFOThreshold(handle->bus->spi, LL_SPI_RX_FIFO_TH_QUARTER);
-        LL_SPI_Enable(handle->bus->spi);
-        furi_hal_gpio_write(handle->cs, false);
-    } else if(event == FuriHalSpiBusHandleEventDeactivate) {
-        furi_hal_gpio_write(handle->cs, true);
-        LL_SPI_Disable(handle->bus->spi);
-    }
-}
-
-static void furi_hal_spi_bus_handle_display_event_callback(
-    FuriHalSpiBusHandle* handle,
-    FuriHalSpiBusHandleEvent event) {
-    furi_hal_spi_bus_d_handle_event_callback(handle, event, &furi_hal_spi_preset_1edge_low_4m);
-}
-
-FuriHalSpiBusHandle furi_hal_spi_bus_handle_display = {
-    .bus = &furi_hal_spi_bus_d,
-    .callback = furi_hal_spi_bus_handle_display_event_callback,
-    .miso = &gpio_spi_d_miso,
-    .mosi = &gpio_spi_d_mosi,
-    .sck = &gpio_spi_d_sck,
-    .cs = &gpio_display_cs,
-};
-
-static void furi_hal_spi_bus_handle_sd_fast_event_callback(
-    FuriHalSpiBusHandle* handle,
-    FuriHalSpiBusHandleEvent event) {
-    furi_hal_spi_bus_d_handle_event_callback(handle, event, &furi_hal_spi_preset_1edge_low_16m);
-}
-
-FuriHalSpiBusHandle furi_hal_spi_bus_handle_sd_fast = {
-    .bus = &furi_hal_spi_bus_d,
-    .callback = furi_hal_spi_bus_handle_sd_fast_event_callback,
-    .miso = &gpio_spi_d_miso,
-    .mosi = &gpio_spi_d_mosi,
-    .sck = &gpio_spi_d_sck,
-    .cs = &gpio_sdcard_cs,
-};
-
-static void furi_hal_spi_bus_handle_sd_slow_event_callback(
-    FuriHalSpiBusHandle* handle,
-    FuriHalSpiBusHandleEvent event) {
-    furi_hal_spi_bus_d_handle_event_callback(handle, event, &furi_hal_spi_preset_1edge_low_2m);
-}
-
-FuriHalSpiBusHandle furi_hal_spi_bus_handle_sd_slow = {
-    .bus = &furi_hal_spi_bus_d,
-    .callback = furi_hal_spi_bus_handle_sd_slow_event_callback,
-    .miso = &gpio_spi_d_miso,
-    .mosi = &gpio_spi_d_mosi,
-    .sck = &gpio_spi_d_sck,
-    .cs = &gpio_sdcard_cs,
-};

+ 0 - 61
bootloader/targets/f7/furi_hal/furi_hal_spi_config.h

@@ -1,61 +0,0 @@
-#pragma once
-
-#include <furi_hal_spi_types.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/** Preset for ST25R916 */
-extern const LL_SPI_InitTypeDef furi_hal_spi_preset_2edge_low_8m;
-
-/** Preset for CC1101 */
-extern const LL_SPI_InitTypeDef furi_hal_spi_preset_1edge_low_8m;
-
-/** Preset for ST7567 (Display) */
-extern const LL_SPI_InitTypeDef furi_hal_spi_preset_1edge_low_4m;
-
-/** Preset for SdCard in fast mode */
-extern const LL_SPI_InitTypeDef furi_hal_spi_preset_1edge_low_16m;
-
-/** Preset for SdCard in slow mode */
-extern const LL_SPI_InitTypeDef furi_hal_spi_preset_1edge_low_2m;
-
-/** Furi Hal Spi Bus R (Radio: CC1101, Nfc, External)*/
-extern FuriHalSpiBus furi_hal_spi_bus_r;
-
-/** Furi Hal Spi Bus D (Display, SdCard) */
-extern FuriHalSpiBus furi_hal_spi_bus_d;
-
-/** CC1101 on `furi_hal_spi_bus_r` */
-extern FuriHalSpiBusHandle furi_hal_spi_bus_handle_subghz;
-
-/** ST25R3916 on `furi_hal_spi_bus_r` */
-extern FuriHalSpiBusHandle furi_hal_spi_bus_handle_nfc;
-
-/** External on `furi_hal_spi_bus_r`
- * Preset: `furi_hal_spi_preset_1edge_low_2m`
- * 
- * miso: pa6
- * mosi: pa7
- * sck: pb3
- * cs:  pa4 (software controlled)
- * 
- * @warning not initialized by default, call `furi_hal_spi_bus_handle_init` to initialize
- * Bus pins are floating on inactive state, CS high after initialization
- * 
- */
-extern FuriHalSpiBusHandle furi_hal_spi_bus_handle_external;
-
-/** ST7567(Display) on `furi_hal_spi_bus_d` */
-extern FuriHalSpiBusHandle furi_hal_spi_bus_handle_display;
-
-/** SdCard in fast mode on `furi_hal_spi_bus_d` */
-extern FuriHalSpiBusHandle furi_hal_spi_bus_handle_sd_fast;
-
-/** SdCard in slow mode on `furi_hal_spi_bus_d` */
-extern FuriHalSpiBusHandle furi_hal_spi_bus_handle_sd_slow;
-
-#ifdef __cplusplus
-}
-#endif

+ 0 - 64
bootloader/targets/f7/furi_hal/furi_hal_spi_types.h

@@ -1,64 +0,0 @@
-#pragma once
-
-#include <stdint.h>
-#include <stddef.h>
-
-#include <furi_hal_gpio.h>
-
-#include <stm32wbxx_ll_spi.h>
-#include <stm32wbxx_ll_rcc.h>
-#include <stm32wbxx_ll_bus.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef struct FuriHalSpiBus FuriHalSpiBus;
-typedef struct FuriHalSpiBusHandle FuriHalSpiBusHandle;
-
-/** FuriHal spi bus states */
-typedef enum {
-    FuriHalSpiBusEventInit, /**< Bus initialization event, called on system start */
-    FuriHalSpiBusEventDeinit, /**< Bus deinitialization event, called on system stop */
-    FuriHalSpiBusEventLock, /**< Bus lock event, called before activation */
-    FuriHalSpiBusEventUnlock, /**< Bus unlock event, called after deactivation */
-    FuriHalSpiBusEventActivate, /**< Bus activation event, called before handle activation */
-    FuriHalSpiBusEventDeactivate, /**< Bus deactivation event, called after handle deactivation  */
-} FuriHalSpiBusEvent;
-
-/** FuriHal spi bus event callback */
-typedef void (*FuriHalSpiBusEventCallback)(FuriHalSpiBus* bus, FuriHalSpiBusEvent event);
-
-/** FuriHal spi bus */
-struct FuriHalSpiBus {
-    SPI_TypeDef* spi;
-    FuriHalSpiBusEventCallback callback;
-    FuriHalSpiBusHandle* current_handle;
-};
-
-/** FuriHal spi handle states */
-typedef enum {
-    FuriHalSpiBusHandleEventInit, /**< Handle init, called on system start, initialize gpio for idle state */
-    FuriHalSpiBusHandleEventDeinit, /**< Handle deinit, called on system stop, deinitialize gpio for default state */
-    FuriHalSpiBusHandleEventActivate, /**< Handle activate: connect gpio and apply bus config */
-    FuriHalSpiBusHandleEventDeactivate, /**< Handle deactivate: disconnect gpio and reset bus config */
-} FuriHalSpiBusHandleEvent;
-
-/** FuriHal spi handle event callback */
-typedef void (*FuriHalSpiBusHandleEventCallback)(
-    FuriHalSpiBusHandle* handle,
-    FuriHalSpiBusHandleEvent event);
-
-/** FuriHal spi handle */
-struct FuriHalSpiBusHandle {
-    FuriHalSpiBus* bus;
-    FuriHalSpiBusHandleEventCallback callback;
-    const GpioPin* miso;
-    const GpioPin* mosi;
-    const GpioPin* sck;
-    const GpioPin* cs;
-};
-
-#ifdef __cplusplus
-}
-#endif

+ 0 - 268
bootloader/targets/f7/furi_hal/furi_hal_version.c

@@ -1,268 +0,0 @@
-#include <furi_hal_version.h>
-
-#include <stm32wbxx.h>
-#include <stm32wbxx_ll_rtc.h>
-#include <stm32wbxx_ll_system.h>
-
-#include <stdio.h>
-
-#define FURI_HAL_VERSION_OTP_HEADER_MAGIC 0xBABE
-#define FURI_HAL_VERSION_OTP_ADDRESS OTP_AREA_BASE
-
-/** OTP V0 Structure: prototypes and early EVT */
-typedef struct {
-    uint8_t board_version;
-    uint8_t board_target;
-    uint8_t board_body;
-    uint8_t board_connect;
-    uint32_t header_timestamp;
-    char name[FURI_HAL_VERSION_NAME_LENGTH];
-} FuriHalVersionOTPv0;
-
-/** OTP V1 Structure: late EVT, DVT */
-typedef struct {
-    /* First 64 bits: header */
-    uint16_t header_magic;
-    uint8_t header_version;
-    uint8_t header_reserved;
-    uint32_t header_timestamp;
-
-    /* Second 64 bits: board info */
-    uint8_t board_version; /** Board version */
-    uint8_t board_target; /** Board target firmware */
-    uint8_t board_body; /** Board body */
-    uint8_t board_connect; /** Board interconnect */
-    uint8_t board_color; /** Board color */
-    uint8_t board_region; /** Board region */
-    uint16_t board_reserved; /** Reserved for future use, 0x0000 */
-
-    /* Third 64 bits: Unique Device Name */
-    char name[FURI_HAL_VERSION_NAME_LENGTH]; /** Unique Device Name */
-} FuriHalVersionOTPv1;
-
-/** OTP V2 Structure: DVT2, PVT, Production */
-typedef struct {
-    /* Early First 64 bits: header */
-    uint16_t header_magic;
-    uint8_t header_version;
-    uint8_t header_reserved;
-    uint32_t header_timestamp;
-
-    /* Early Second 64 bits: board info */
-    uint8_t board_version; /** Board version */
-    uint8_t board_target; /** Board target firmware */
-    uint8_t board_body; /** Board body */
-    uint8_t board_connect; /** Board interconnect */
-    uint8_t board_display; /** Board display */
-    uint8_t board_reserved2_0; /** Reserved for future use, 0x00 */
-    uint16_t board_reserved2_1; /** Reserved for future use, 0x0000 */
-
-    /* Late Third 64 bits: device info */
-    uint8_t board_color; /** Board color */
-    uint8_t board_region; /** Board region */
-    uint16_t board_reserved3_0; /** Reserved for future use, 0x0000 */
-    uint32_t board_reserved3_1; /** Reserved for future use, 0x00000000 */
-
-    /* Late Fourth 64 bits: Unique Device Name */
-    char name[FURI_HAL_VERSION_NAME_LENGTH]; /** Unique Device Name */
-} FuriHalVersionOTPv2;
-
-/** Represenation Model: */
-typedef struct {
-    uint32_t timestamp;
-
-    uint8_t board_version; /** Board version */
-    uint8_t board_target; /** Board target firmware */
-    uint8_t board_body; /** Board body */
-    uint8_t board_connect; /** Board interconnect */
-    uint8_t board_color; /** Board color */
-    uint8_t board_region; /** Board region */
-    uint8_t board_display; /** Board display */
-
-    char name[FURI_HAL_VERSION_ARRAY_NAME_LENGTH]; /** \0 terminated name */
-    char device_name[FURI_HAL_VERSION_DEVICE_NAME_LENGTH]; /** device name for special needs */
-    uint8_t ble_mac[6];
-} FuriHalVersion;
-
-static FuriHalVersion furi_hal_version = {0};
-
-static void furi_hal_version_set_name(const char* name) {
-    furi_hal_version.device_name[0] = 0;
-}
-
-static void furi_hal_version_load_otp_default() {
-    furi_hal_version_set_name(NULL);
-}
-
-static void furi_hal_version_load_otp_v0() {
-    const FuriHalVersionOTPv0* otp = (FuriHalVersionOTPv0*)FURI_HAL_VERSION_OTP_ADDRESS;
-
-    furi_hal_version.timestamp = otp->header_timestamp;
-    furi_hal_version.board_version = otp->board_version;
-    furi_hal_version.board_target = otp->board_target;
-    furi_hal_version.board_body = otp->board_body;
-    furi_hal_version.board_connect = otp->board_connect;
-
-    furi_hal_version_set_name(otp->name);
-}
-
-static void furi_hal_version_load_otp_v1() {
-    const FuriHalVersionOTPv1* otp = (FuriHalVersionOTPv1*)FURI_HAL_VERSION_OTP_ADDRESS;
-
-    furi_hal_version.timestamp = otp->header_timestamp;
-    furi_hal_version.board_version = otp->board_version;
-    furi_hal_version.board_target = otp->board_target;
-    furi_hal_version.board_body = otp->board_body;
-    furi_hal_version.board_connect = otp->board_connect;
-    furi_hal_version.board_color = otp->board_color;
-    furi_hal_version.board_region = otp->board_region;
-
-    furi_hal_version_set_name(otp->name);
-}
-
-static void furi_hal_version_load_otp_v2() {
-    const FuriHalVersionOTPv2* otp = (FuriHalVersionOTPv2*)FURI_HAL_VERSION_OTP_ADDRESS;
-
-    // 1st block, programmed afer baking
-    furi_hal_version.timestamp = otp->header_timestamp;
-
-    // 2nd block, programmed afer baking
-    furi_hal_version.board_version = otp->board_version;
-    furi_hal_version.board_target = otp->board_target;
-    furi_hal_version.board_body = otp->board_body;
-    furi_hal_version.board_connect = otp->board_connect;
-    furi_hal_version.board_display = otp->board_display;
-
-    // 3rd and 4th blocks, programmed on FATP stage
-    if(otp->board_color != 0xFF) {
-        furi_hal_version.board_color = otp->board_color;
-        furi_hal_version.board_region = otp->board_region;
-        furi_hal_version_set_name(otp->name);
-    } else {
-        furi_hal_version.board_color = 0;
-        furi_hal_version.board_region = 0;
-        furi_hal_version_set_name(NULL);
-    }
-}
-
-void furi_hal_version_init() {
-    switch(furi_hal_version_get_otp_version()) {
-    case FuriHalVersionOtpVersionUnknown:
-        furi_hal_version_load_otp_default();
-        break;
-    case FuriHalVersionOtpVersionEmpty:
-        furi_hal_version_load_otp_default();
-        break;
-    case FuriHalVersionOtpVersion0:
-        furi_hal_version_load_otp_v0();
-        break;
-    case FuriHalVersionOtpVersion1:
-        furi_hal_version_load_otp_v1();
-        break;
-    case FuriHalVersionOtpVersion2:
-        furi_hal_version_load_otp_v2();
-        break;
-    default:
-        furi_hal_version_load_otp_default();
-    }
-}
-
-bool furi_hal_version_do_i_belong_here() {
-    return furi_hal_version_get_hw_target() == 7;
-}
-
-const char* furi_hal_version_get_model_name() {
-    return "Flipper Zero";
-}
-
-const FuriHalVersionOtpVersion furi_hal_version_get_otp_version() {
-    if(*(uint64_t*)FURI_HAL_VERSION_OTP_ADDRESS == 0xFFFFFFFF) {
-        return FuriHalVersionOtpVersionEmpty;
-    } else {
-        if(((FuriHalVersionOTPv1*)FURI_HAL_VERSION_OTP_ADDRESS)->header_magic ==
-           FURI_HAL_VERSION_OTP_HEADER_MAGIC) {
-            // Version 1+
-            uint8_t version = ((FuriHalVersionOTPv1*)FURI_HAL_VERSION_OTP_ADDRESS)->header_version;
-            if(version >= FuriHalVersionOtpVersion1 && version <= FuriHalVersionOtpVersion2) {
-                return version;
-            } else {
-                return FuriHalVersionOtpVersionUnknown;
-            }
-        } else if(((FuriHalVersionOTPv0*)FURI_HAL_VERSION_OTP_ADDRESS)->board_version <= 10) {
-            // Version 0
-            return FuriHalVersionOtpVersion0;
-        } else {
-            // Version Unknown
-            return FuriHalVersionOtpVersionUnknown;
-        }
-    }
-}
-
-const uint8_t furi_hal_version_get_hw_version() {
-    return furi_hal_version.board_version;
-}
-
-const uint8_t furi_hal_version_get_hw_target() {
-    return furi_hal_version.board_target;
-}
-
-const uint8_t furi_hal_version_get_hw_body() {
-    return furi_hal_version.board_body;
-}
-
-const FuriHalVersionColor furi_hal_version_get_hw_color() {
-    return furi_hal_version.board_color;
-}
-
-const uint8_t furi_hal_version_get_hw_connect() {
-    return furi_hal_version.board_connect;
-}
-
-const FuriHalVersionRegion furi_hal_version_get_hw_region() {
-    return furi_hal_version.board_region;
-}
-
-const FuriHalVersionDisplay furi_hal_version_get_hw_display() {
-    return furi_hal_version.board_display;
-}
-
-const uint32_t furi_hal_version_get_hw_timestamp() {
-    return furi_hal_version.timestamp;
-}
-
-const char* furi_hal_version_get_name_ptr() {
-    return *furi_hal_version.name == 0x00 ? NULL : furi_hal_version.name;
-}
-
-const char* furi_hal_version_get_device_name_ptr() {
-    return furi_hal_version.device_name + 1;
-}
-
-const char* furi_hal_version_get_ble_local_device_name_ptr() {
-    return furi_hal_version.device_name;
-}
-
-const uint8_t* furi_hal_version_get_ble_mac() {
-    return furi_hal_version.ble_mac;
-}
-
-const struct Version* furi_hal_version_get_firmware_version(void) {
-    return version_get();
-}
-
-const struct Version* furi_hal_version_get_bootloader_version(void) {
-#ifdef NO_BOOTLOADER
-    return 0;
-#else
-    /* Backup register which points to structure in flash memory */
-    return (const struct Version*)LL_RTC_BAK_GetRegister(RTC, LL_RTC_BKP_DR1);
-#endif
-}
-
-size_t furi_hal_version_uid_size() {
-    return 64 / 8;
-}
-
-const uint8_t* furi_hal_version_uid() {
-    return (const uint8_t*)UID64_BASE;
-}

+ 0 - 108
bootloader/targets/f7/furi_hal/main.h

@@ -1,108 +0,0 @@
-#pragma once
-
-#include <stm32wbxx.h>
-#include <stm32wbxx_ll_gpio.h>
-#include <stm32wbxx_ll_spi.h>
-
-#define BUTTON_BACK_GPIO_Port GPIOC
-#define BUTTON_BACK_Pin LL_GPIO_PIN_13
-#define BUTTON_DOWN_GPIO_Port GPIOC
-#define BUTTON_DOWN_Pin LL_GPIO_PIN_6
-#define BUTTON_LEFT_GPIO_Port GPIOB
-#define BUTTON_LEFT_Pin LL_GPIO_PIN_11
-#define BUTTON_OK_GPIO_Port GPIOH
-#define BUTTON_OK_Pin LL_GPIO_PIN_3
-#define BUTTON_RIGHT_GPIO_Port GPIOB
-#define BUTTON_RIGHT_Pin LL_GPIO_PIN_12
-#define BUTTON_UP_GPIO_Port GPIOB
-#define BUTTON_UP_Pin LL_GPIO_PIN_10
-
-#define CC1101_CS_GPIO_Port GPIOD
-#define CC1101_CS_Pin LL_GPIO_PIN_0
-#define CC1101_G0_GPIO_Port GPIOA
-#define CC1101_G0_Pin LL_GPIO_PIN_1
-
-#define DISPLAY_CS_GPIO_Port GPIOC
-#define DISPLAY_CS_Pin LL_GPIO_PIN_11
-#define DISPLAY_DI_GPIO_Port GPIOB
-#define DISPLAY_DI_Pin LL_GPIO_PIN_1
-#define DISPLAY_RST_GPIO_Port GPIOB
-#define DISPLAY_RST_Pin LL_GPIO_PIN_0
-
-#define IR_RX_GPIO_Port GPIOA
-#define IR_RX_Pin LL_GPIO_PIN_0
-#define IR_TX_GPIO_Port GPIOB
-#define IR_TX_Pin LL_GPIO_PIN_9
-
-#define NFC_CS_GPIO_Port GPIOE
-#define NFC_CS_Pin LL_GPIO_PIN_4
-
-#define PA4_GPIO_Port GPIOA
-#define PA4_Pin LL_GPIO_PIN_4
-#define PA6_GPIO_Port GPIOA
-#define PA6_Pin LL_GPIO_PIN_6
-#define PA7_GPIO_Port GPIOA
-#define PA7_Pin LL_GPIO_PIN_7
-#define PB2_GPIO_Port GPIOB
-#define PB2_Pin LL_GPIO_PIN_2
-#define PB3_GPIO_Port GPIOB
-#define PB3_Pin LL_GPIO_PIN_3
-#define PC0_GPIO_Port GPIOC
-#define PC0_Pin LL_GPIO_PIN_0
-#define PC1_GPIO_Port GPIOC
-#define PC1_Pin LL_GPIO_PIN_1
-#define PC3_GPIO_Port GPIOC
-#define PC3_Pin LL_GPIO_PIN_3
-
-#define PERIPH_POWER_GPIO_Port GPIOA
-#define PERIPH_POWER_Pin LL_GPIO_PIN_3
-
-#define QUARTZ_32MHZ_IN_GPIO_Port GPIOC
-#define QUARTZ_32MHZ_IN_Pin LL_GPIO_PIN_14
-#define QUARTZ_32MHZ_OUT_GPIO_Port GPIOC
-#define QUARTZ_32MHZ_OUT_Pin LL_GPIO_PIN_15
-
-#define RFID_OUT_GPIO_Port GPIOB
-#define RFID_OUT_Pin LL_GPIO_PIN_13
-#define RFID_PULL_GPIO_Port GPIOA
-#define RFID_PULL_Pin LL_GPIO_PIN_2
-#define RFID_RF_IN_GPIO_Port GPIOC
-#define RFID_RF_IN_Pin LL_GPIO_PIN_5
-#define RFID_TUNE_GPIO_Port GPIOA
-#define RFID_TUNE_Pin LL_GPIO_PIN_8
-
-#define RF_SW_0_GPIO_Port GPIOC
-#define RF_SW_0_Pin LL_GPIO_PIN_4
-
-#define SD_CD_GPIO_Port GPIOC
-#define SD_CD_Pin LL_GPIO_PIN_10
-#define SD_CS_GPIO_Port GPIOC
-#define SD_CS_Pin LL_GPIO_PIN_12
-
-#define SPEAKER_GPIO_Port GPIOB
-#define SPEAKER_Pin LL_GPIO_PIN_8
-
-#define VIBRO_GPIO_Port GPIOA
-#define VIBRO_Pin LL_GPIO_PIN_15
-
-#define iBTN_GPIO_Port GPIOB
-#define iBTN_Pin LL_GPIO_PIN_14
-
-#define USART1_TX_Pin LL_GPIO_PIN_6
-#define USART1_TX_Port GPIOB
-#define USART1_RX_Pin LL_GPIO_PIN_7
-#define USART1_RX_Port GPIOB
-
-#define SPI_D_MISO_GPIO_Port GPIOC
-#define SPI_D_MISO_Pin LL_GPIO_PIN_2
-#define SPI_D_MOSI_GPIO_Port GPIOB
-#define SPI_D_MOSI_Pin LL_GPIO_PIN_15
-#define SPI_D_SCK_GPIO_Port GPIOD
-#define SPI_D_SCK_Pin LL_GPIO_PIN_1
-
-#define SPI_R_MISO_GPIO_Port GPIOB
-#define SPI_R_MISO_Pin LL_GPIO_PIN_4
-#define SPI_R_MOSI_GPIO_Port GPIOB
-#define SPI_R_MOSI_Pin LL_GPIO_PIN_5
-#define SPI_R_SCK_GPIO_Port GPIOA
-#define SPI_R_SCK_Pin LL_GPIO_PIN_5

+ 0 - 187
bootloader/targets/f7/stm32wb55xx_flash_cm4.ld

@@ -1,187 +0,0 @@
-/**
-*****************************************************************************
-**
-**  File        : stm32wb55xx_flash_cm4.ld
-**
-**  Abstract    : System Workbench Minimal System calls file
-**
-** 		          For more information about which c-functions
-**                need which of these lowlevel functions
-**                please consult the Newlib libc-manual
-**
-**  Environment : System Workbench for MCU
-**
-**  Distribution: The file is distributed “as is,” without any warranty
-**                of any kind.
-**
-*****************************************************************************
-**
-** <h2><center>&copy; COPYRIGHT(c) 2019 Ac6</center></h2>
-**
-** Redistribution and use in source and binary forms, with or without modification,
-** are permitted provided that the following conditions are met:
-**   1. Redistributions of source code must retain the above copyright notice,
-**      this list of conditions and the following disclaimer.
-**   2. Redistributions in binary form must reproduce the above copyright notice,
-**      this list of conditions and the following disclaimer in the documentation
-**      and/or other materials provided with the distribution.
-**   3. Neither the name of Ac6 nor the names of its contributors
-**      may be used to endorse or promote products derived from this software
-**      without specific prior written permission.
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-** AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-** DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-** OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-**
-*****************************************************************************
-*/
-
-/* Entry Point */
-ENTRY(Reset_Handler)
-
-/* Highest address of the user mode stack */
-_estack = 0x20030000;    /* end of RAM */
-/* Generate a link error if heap and stack don't fit into RAM */
-_Min_Heap_Size = 0x200;      /* required amount of heap  */
-_Min_Stack_Size = 0x400; /* required amount of stack */
-
-/* Specify the memory areas */
-MEMORY
-{
-FLASH (rx)                 : ORIGIN = 0x08000000, LENGTH = 32K
-RAM1 (xrw)                 : ORIGIN = 0x20000008, LENGTH = 0x2FFF8
-RAM_SHARED (xrw)           : ORIGIN = 0x20030000, LENGTH = 10K
-}
-
-/* Define output sections */
-SECTIONS
-{
-  /* The startup code goes first into FLASH */
-  .isr_vector :
-  {
-    . = ALIGN(4);
-    KEEP(*(.isr_vector)) /* Startup code */
-    . = ALIGN(4);
-  } >FLASH
-
-  /* The program code and other data goes into FLASH */
-  .text :
-  {
-    . = ALIGN(4);
-    *(.text)           /* .text sections (code) */
-    *(.text*)          /* .text* sections (code) */
-    *(.glue_7)         /* glue arm to thumb code */
-    *(.glue_7t)        /* glue thumb to arm code */
-    *(.eh_frame)
-
-    KEEP (*(.init))
-    KEEP (*(.fini))
-
-    . = ALIGN(4);
-    _etext = .;        /* define a global symbols at end of code */
-  } >FLASH
-
-  /* Constant data goes into FLASH */
-  .rodata :
-  {
-    . = ALIGN(4);
-    *(.rodata)         /* .rodata sections (constants, strings, etc.) */
-    *(.rodata*)        /* .rodata* sections (constants, strings, etc.) */
-    . = ALIGN(4);
-  } >FLASH
-
-  .ARM.extab   : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH
-  .ARM : {
-    __exidx_start = .;
-    *(.ARM.exidx*)
-    __exidx_end = .;
-  } >FLASH
-
-  .preinit_array     :
-  {
-    PROVIDE_HIDDEN (__preinit_array_start = .);
-    KEEP (*(.preinit_array*))
-    PROVIDE_HIDDEN (__preinit_array_end = .);
-  } >FLASH
-  .init_array :
-  {
-    PROVIDE_HIDDEN (__init_array_start = .);
-    KEEP (*(SORT(.init_array.*)))
-    KEEP (*(.init_array*))
-    PROVIDE_HIDDEN (__init_array_end = .);
-  } >FLASH
-  .fini_array :
-  {
-    PROVIDE_HIDDEN (__fini_array_start = .);
-    KEEP (*(SORT(.fini_array.*)))
-    KEEP (*(.fini_array*))
-    PROVIDE_HIDDEN (__fini_array_end = .);
-  } >FLASH
-
-  /* used by the startup to initialize data */
-  _sidata = LOADADDR(.data);
-
-  /* Initialized data sections goes into RAM, load LMA copy after code */
-  .data : 
-  {
-    . = ALIGN(4);
-    _sdata = .;        /* create a global symbol at data start */
-    *(.data)           /* .data sections */
-    *(.data*)          /* .data* sections */
-
-    . = ALIGN(4);
-    _edata = .;        /* define a global symbol at data end */
-  } >RAM1 AT> FLASH
-
-  
-  /* Uninitialized data section */
-  . = ALIGN(4);
-  .bss :
-  {
-    /* This is used by the startup in order to initialize the .bss secion */
-    _sbss = .;         /* define a global symbol at bss start */
-    __bss_start__ = _sbss;
-    *(.bss)
-    *(.bss*)
-    *(COMMON)
-
-    . = ALIGN(4);
-    _ebss = .;         /* define a global symbol at bss end */
-    __bss_end__ = _ebss;
-  } >RAM1
-
-  /* User_heap_stack section, used to check that there is enough RAM left */
-  ._user_heap_stack :
-  {
-    . = ALIGN(8);
-    PROVIDE ( end = . );
-    PROVIDE ( _end = . );
-    . = . + _Min_Heap_Size;
-    . = . + _Min_Stack_Size;
-    . = ALIGN(8);
-  } >RAM1
-
-  
-
-  /* Remove information from the standard libraries */
-  /DISCARD/ :
-  {
-    libc.a ( * )
-    libm.a ( * )
-    libgcc.a ( * )
-  }
-
-  .ARM.attributes 0       : { *(.ARM.attributes) }
-   MAPPING_TABLE (NOLOAD) : { *(MAPPING_TABLE) } >RAM_SHARED
-   MB_MEM1 (NOLOAD)       : { *(MB_MEM1) } >RAM_SHARED
-   MB_MEM2 (NOLOAD)       : { _sMB_MEM2 = . ; *(MB_MEM2) ; _eMB_MEM2 = . ; } >RAM_SHARED
-}
-
-

+ 0 - 264
bootloader/targets/f7/target.c

@@ -1,264 +0,0 @@
-#include <target.h>
-#include <stm32wbxx.h>
-#include <stm32wbxx_ll_system.h>
-#include <stm32wbxx_ll_bus.h>
-#include <stm32wbxx_ll_utils.h>
-#include <stm32wbxx_ll_rcc.h>
-#include <stm32wbxx_ll_rtc.h>
-#include <stm32wbxx_ll_pwr.h>
-#include <stm32wbxx_ll_gpio.h>
-#include <stm32wbxx_hal_flash.h>
-
-#include <lib/toolbox/version.h>
-#include <furi_hal.h>
-
-#include <u8g2.h>
-#include <u8g2_glue.h>
-
-const uint8_t I_DFU_128x50[] = {
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x07, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x38, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0xC0, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8F, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x7F, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x75, 0x00, 0x00, 0xF0, 0x3F, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x0A, 0x00, 0x00, 0x0F, 0x60, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0xE0, 0x0F, 0x00, 0xC0, 0xE0, 0x4F, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x18, 0x00, 0x30, 0x1E, 0x90, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x18, 0x00, 0x8C, 0x01, 0xA0, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x81, 0xFF, 0x19, 0x00, 0x63, 0x00, 0xC0, 0xF0, 0x07,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x60, 0x5E, 0x1F, 0x80, 0x18, 0x00, 0xE0, 0x0E, 0x18,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x18, 0xAF, 0x0F, 0x40, 0x06, 0x00, 0xF8, 0x01, 0x20,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x06, 0x57, 0x01, 0x20, 0x01, 0x00, 0x78, 0x00, 0x3E,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0x81, 0xAF, 0x02, 0x90, 0x00, 0x00, 0x38, 0x80, 0x41,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x80, 0x57, 0x01, 0x48, 0x00, 0x00, 0x10, 0x60, 0x40,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x10, 0x80, 0xAB, 0x00, 0x24, 0x00, 0x00, 0x08, 0x10, 0x40,
-    0x3F, 0x00, 0x00, 0x00, 0x00, 0x38, 0x0C, 0xC0, 0x57, 0x01, 0x12, 0x00, 0x00, 0x04, 0x08, 0x40,
-    0xC0, 0x0F, 0x00, 0x00, 0xC0, 0x07, 0x03, 0xF0, 0xAB, 0x00, 0x0A, 0x00, 0x00, 0x02, 0x04, 0x40,
-    0x00, 0xF0, 0x1F, 0x80, 0x3F, 0xC0, 0x00, 0xFC, 0x55, 0x01, 0x05, 0xE0, 0x00, 0x01, 0x04, 0x40,
-    0x00, 0x00, 0xE0, 0x7F, 0x00, 0x30, 0x00, 0xFF, 0xAB, 0x00, 0x05, 0xE0, 0x80, 0x00, 0x02, 0x40,
-    0x0F, 0x00, 0x00, 0x00, 0x80, 0x0F, 0xE0, 0xCF, 0x55, 0x81, 0x02, 0xF0, 0x40, 0x00, 0x02, 0x40,
-    0xF0, 0x0F, 0x00, 0x00, 0x7F, 0x00, 0xFE, 0xC3, 0xAB, 0x80, 0x02, 0x78, 0x20, 0x00, 0x01, 0x40,
-    0x00, 0xF0, 0xFF, 0xFF, 0x00, 0xF0, 0xFF, 0xC0, 0xD5, 0x81, 0x01, 0x7E, 0x10, 0x80, 0x00, 0x20,
-    0x00, 0x00, 0x00, 0x00, 0xC0, 0xFF, 0x0F, 0xE0, 0xFA, 0x83, 0xC1, 0x3F, 0x08, 0x80, 0x00, 0x20,
-    0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x01, 0xD8, 0x07, 0x83, 0xF1, 0x1F, 0x04, 0x40, 0x00, 0x20,
-    0x00, 0xE0, 0xFF, 0xFF, 0xFF, 0x0F, 0x80, 0xC7, 0x01, 0x83, 0xF1, 0x0F, 0x00, 0x20, 0x00, 0x10,
-    0xE0, 0xFF, 0xFF, 0xFF, 0x3F, 0xC0, 0x7F, 0x40, 0x80, 0x83, 0xE1, 0x01, 0x00, 0x20, 0x00, 0x18,
-    0xFC, 0xFF, 0xFF, 0xFF, 0x03, 0x3F, 0x00, 0x20, 0xFC, 0x83, 0x01, 0x00, 0x00, 0x10, 0x00, 0x18,
-    0xFF, 0xFF, 0xFF, 0x3F, 0xF0, 0x00, 0x00, 0x10, 0xD7, 0x01, 0x03, 0x00, 0x00, 0x08, 0x00, 0x1C,
-    0xFF, 0xFF, 0x01, 0x00, 0x0F, 0x00, 0x00, 0x88, 0xAB, 0x02, 0xE3, 0x01, 0x00, 0x08, 0x00, 0x0C,
-    0xFF, 0x07, 0x00, 0xE0, 0x00, 0x00, 0x00, 0xC4, 0x55, 0x05, 0x1E, 0x00, 0x00, 0x04, 0x00, 0x0E,
-    0x7F, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0xA3, 0xAB, 0x02, 0x06, 0x00, 0x00, 0x02, 0x00, 0x0F,
-    0x0F, 0x00, 0x80, 0x03, 0x00, 0x00, 0xC0, 0x10, 0x57, 0x05, 0x02, 0x00, 0x00, 0x01, 0x80, 0x07,
-    0x03, 0x00, 0x70, 0x00, 0x00, 0x00, 0x30, 0x08, 0xAB, 0x0A, 0x02, 0x00, 0xC0, 0x00, 0xC0, 0x07,
-    0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x84, 0x57, 0x15, 0x01, 0x00, 0x30, 0x00, 0xE0, 0x07,
-    0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0xC3, 0xFF, 0x2A, 0x01, 0x00, 0x0C, 0x00, 0xF0, 0x0F,
-    0x00, 0xC0, 0x00, 0x00, 0x00, 0xE0, 0xC0, 0xE0, 0xFE, 0x55, 0x01, 0x82, 0x03, 0x00, 0xF8, 0x15,
-    0x00, 0x30, 0x00, 0x00, 0x00, 0x1C, 0x30, 0x78, 0xFE, 0xAA, 0x01, 0x7C, 0x00, 0x00, 0xFC, 0x23,
-    0x00, 0x0E, 0x00, 0x00, 0xC0, 0x03, 0x0C, 0x3C, 0x7F, 0x5D, 0x01, 0x00, 0x00, 0x00, 0xFF, 0x45,
-    0xC0, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x02, 0x8F, 0xBF, 0xAE, 0x03, 0x00, 0x00, 0xC0, 0xFF, 0x82,
-    0x30, 0x00, 0x00, 0xC0, 0x01, 0x80, 0xC1, 0x43, 0xFE, 0x5D, 0x01, 0x00, 0x00, 0xF0, 0xFF, 0x05,
-    0x0F, 0x00, 0x80, 0x3F, 0x00, 0x60, 0xF0, 0x31, 0xF6, 0xAE, 0x03, 0x00, 0x00, 0xFA, 0xAF, 0x02,
-    0xFC, 0xFF, 0x7F, 0x00, 0x00, 0x18, 0x7C, 0x08, 0x23, 0xFF, 0x05, 0x00, 0x00, 0xFD, 0x55, 0x01,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0x1F, 0x84, 0x30, 0xFE, 0x0A, 0x00, 0x00, 0xAA, 0xAA, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x80, 0xF1, 0x07, 0x43, 0x18, 0xFF, 0x15, 0x00, 0x00, 0x54, 0x15, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x80, 0x20, 0x8C, 0xFF, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00,
-};
-
-// Boot request enum
-#define BOOT_REQUEST_TAINTED 0x00000000
-#define BOOT_REQUEST_CLEAN 0xDADEDADE
-#define BOOT_REQUEST_DFU 0xDF00B000
-// Boot to DFU pin
-#define BOOT_DFU_PORT GPIOB
-#define BOOT_DFU_PIN LL_GPIO_PIN_11
-// USB pins
-#define BOOT_USB_PORT GPIOA
-#define BOOT_USB_DM_PIN LL_GPIO_PIN_11
-#define BOOT_USB_DP_PIN LL_GPIO_PIN_12
-#define BOOT_USB_PIN (BOOT_USB_DM_PIN | BOOT_USB_DP_PIN)
-
-#define RTC_CLOCK_IS_READY() (LL_RCC_LSE_IsReady() && LL_RCC_LSI1_IsReady())
-
-void target_led_control(char* c) {
-    furi_hal_light_set(LightRed, 0x00);
-    furi_hal_light_set(LightGreen, 0x00);
-    furi_hal_light_set(LightBlue, 0x00);
-    do {
-        if(*c == 'R') {
-            furi_hal_light_set(LightRed, 0xFF);
-        } else if(*c == 'G') {
-            furi_hal_light_set(LightGreen, 0xFF);
-        } else if(*c == 'B') {
-            furi_hal_light_set(LightBlue, 0xFF);
-        } else if(*c == '.') {
-            LL_mDelay(125);
-            furi_hal_light_set(LightRed, 0x00);
-            furi_hal_light_set(LightGreen, 0x00);
-            furi_hal_light_set(LightBlue, 0x00);
-            LL_mDelay(125);
-        } else if(*c == '-') {
-            LL_mDelay(250);
-            furi_hal_light_set(LightRed, 0x00);
-            furi_hal_light_set(LightGreen, 0x00);
-            furi_hal_light_set(LightBlue, 0x00);
-            LL_mDelay(250);
-        } else if(*c == '|') {
-            furi_hal_light_set(LightRed, 0x00);
-            furi_hal_light_set(LightGreen, 0x00);
-            furi_hal_light_set(LightBlue, 0x00);
-        }
-        c++;
-    } while(*c != 0);
-}
-
-void target_clock_init() {
-    LL_Init1msTick(4000000);
-    LL_SetSystemCoreClock(4000000);
-
-    LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOA);
-    LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOB);
-    LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOC);
-    LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOD);
-    LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOE);
-    LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOH);
-
-    LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SPI1);
-    LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_SPI2);
-}
-
-void target_gpio_init() {
-    // USB D+
-    LL_GPIO_SetPinMode(BOOT_USB_PORT, BOOT_USB_DP_PIN, LL_GPIO_MODE_OUTPUT);
-    LL_GPIO_SetPinSpeed(BOOT_USB_PORT, BOOT_USB_DP_PIN, LL_GPIO_SPEED_FREQ_VERY_HIGH);
-    LL_GPIO_SetPinOutputType(BOOT_USB_PORT, BOOT_USB_DP_PIN, LL_GPIO_OUTPUT_OPENDRAIN);
-    // USB D-
-    LL_GPIO_SetPinMode(BOOT_USB_PORT, BOOT_USB_DM_PIN, LL_GPIO_MODE_OUTPUT);
-    LL_GPIO_SetPinSpeed(BOOT_USB_PORT, BOOT_USB_DM_PIN, LL_GPIO_SPEED_FREQ_VERY_HIGH);
-    LL_GPIO_SetPinOutputType(BOOT_USB_PORT, BOOT_USB_DM_PIN, LL_GPIO_OUTPUT_OPENDRAIN);
-    // Button: back
-    LL_GPIO_SetPinMode(BOOT_DFU_PORT, BOOT_DFU_PIN, LL_GPIO_MODE_INPUT);
-    LL_GPIO_SetPinPull(BOOT_DFU_PORT, BOOT_DFU_PIN, LL_GPIO_PULL_UP);
-}
-
-void target_rtc_init() {
-    // LSE and RTC
-    LL_PWR_EnableBkUpAccess();
-    if(!RTC_CLOCK_IS_READY()) {
-        // Start LSI1 needed for CSS
-        LL_RCC_LSI1_Enable();
-        // Try to start LSE normal way
-        LL_RCC_LSE_SetDriveCapability(LL_RCC_LSEDRIVE_HIGH);
-        LL_RCC_LSE_Enable();
-        uint32_t c = 0;
-        while(!RTC_CLOCK_IS_READY() && c < 200) {
-            LL_mDelay(10);
-            c++;
-        }
-        // Plan B: reset backup domain
-        if(!RTC_CLOCK_IS_READY()) {
-            target_led_control("-R.R.R.");
-            LL_RCC_ForceBackupDomainReset();
-            LL_RCC_ReleaseBackupDomainReset();
-            NVIC_SystemReset();
-        }
-        // Set RTC domain clock to LSE
-        LL_RCC_SetRTCClockSource(LL_RCC_RTC_CLKSOURCE_LSE);
-        // Enable LSE CSS
-        LL_RCC_LSE_EnableCSS();
-    }
-    // Enable clocking
-    LL_RCC_EnableRTC();
-    LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_RTCAPB);
-}
-
-void target_version_save(void) {
-    LL_RTC_BAK_SetRegister(RTC, LL_RTC_BKP_DR1, (uint32_t)version_get());
-}
-
-void target_usb_wire_reset() {
-    LL_GPIO_ResetOutputPin(BOOT_USB_PORT, BOOT_USB_PIN);
-}
-
-void target_display_init() {
-    // Prepare gpio
-    furi_hal_gpio_init_simple(&gpio_display_rst, GpioModeOutputPushPull);
-    furi_hal_gpio_init_simple(&gpio_display_di, GpioModeOutputPushPull);
-    // Initialize
-    u8g2_t fb;
-    u8g2_Setup_st756x_flipper(&fb, U8G2_R0, u8x8_hw_spi_stm32, u8g2_gpio_and_delay_stm32);
-    u8g2_InitDisplay(&fb);
-    // Create payload
-    u8g2_ClearBuffer(&fb);
-    u8g2_SetDrawColor(&fb, 0x01);
-    u8g2_DrawXBM(&fb, 0, 64 - 50, 128, 50, I_DFU_128x50);
-#ifndef SLIM_BOOTLOADER
-    u8g2_SetFont(&fb, u8g2_font_helvB08_tf);
-    u8g2_DrawStr(&fb, 2, 8, "Update & Recovery Mode");
-    u8g2_DrawStr(&fb, 2, 21, "DFU started");
-#endif
-    // Send buffer
-    u8g2_SetPowerSave(&fb, 0);
-    u8g2_SendBuffer(&fb);
-}
-
-void target_init() {
-    target_clock_init();
-    target_gpio_init();
-    furi_hal_init();
-    target_led_control("RGB");
-    target_rtc_init();
-    target_version_save();
-    target_usb_wire_reset();
-
-    // Errata 2.2.9, Flash OPTVERR flag is always set after system reset
-    __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_ALL_ERRORS);
-}
-
-int target_is_dfu_requested() {
-    if(LL_RTC_BAK_GetRegister(RTC, LL_RTC_BKP_DR0) == BOOT_REQUEST_TAINTED) {
-        // Default system state is tainted
-        // We must ensure that MCU is cleanly booted
-        LL_RTC_BAK_SetRegister(RTC, LL_RTC_BKP_DR0, BOOT_REQUEST_CLEAN);
-        NVIC_SystemReset();
-    } else if(LL_RTC_BAK_GetRegister(RTC, LL_RTC_BKP_DR0) == BOOT_REQUEST_DFU) {
-        return 1;
-    }
-    LL_mDelay(100);
-    if(!LL_GPIO_IsInputPinSet(BOOT_DFU_PORT, BOOT_DFU_PIN)) {
-        return 1;
-    }
-
-    return 0;
-}
-
-void target_switch(void* offset) {
-    asm volatile("ldr    r3, [%0]    \n"
-                 "msr    msp, r3     \n"
-                 "ldr    r3, [%1]    \n"
-                 "mov    pc, r3      \n"
-                 :
-                 : "r"(offset), "r"(offset + 0x4)
-                 : "r3");
-}
-
-void target_switch2dfu() {
-    target_led_control("B");
-    furi_hal_light_set(LightBacklight, 0xFF);
-    target_display_init();
-    // Mark system as tainted, it will be soon
-    LL_RTC_BAK_SetRegister(RTC, LL_RTC_BKP_DR0, BOOT_REQUEST_TAINTED);
-    // Remap memory to system bootloader
-    LL_SYSCFG_SetRemapMemory(LL_SYSCFG_REMAP_SYSTEMFLASH);
-    // Jump
-    target_switch(0x0);
-}
-
-void target_switch2os() {
-    target_led_control("G");
-    SCB->VTOR = OS_OFFSET;
-    target_switch((void*)(BOOT_ADDRESS + OS_OFFSET));
-}

+ 0 - 50
bootloader/targets/f7/target.mk

@@ -1,50 +0,0 @@
-TOOLCHAIN = arm
-
-BOOT_ADDRESS	= 0x08000000
-FW_ADDRESS		= 0x08008000
-OS_OFFSET		= 0x00008000
-FLASH_ADDRESS	= 0x08000000
-
-OPENOCD_OPTS	= -f interface/stlink.cfg -c "transport select hla_swd" -f ../debug/stm32wbx.cfg -c "init"
-BOOT_CFLAGS		= -DBOOT_ADDRESS=$(BOOT_ADDRESS) -DFW_ADDRESS=$(FW_ADDRESS) -DOS_OFFSET=$(OS_OFFSET)
-MCU_FLAGS		= -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard
-
-CFLAGS			+= $(MCU_FLAGS) $(BOOT_CFLAGS) -DSTM32WB55xx -Wall -fdata-sections -ffunction-sections
-LDFLAGS			+= $(MCU_FLAGS) -specs=nosys.specs -specs=nano.specs 
-
-HARDWARE_TARGET = 7
-
-CUBE_DIR		= $(PROJECT_ROOT)/lib/STM32CubeWB
-
-# ST HAL
-CFLAGS			+=  -DUSE_FULL_LL_DRIVER
-ASM_SOURCES		+= $(CUBE_DIR)/Drivers/CMSIS/Device/ST/STM32WBxx/Source/Templates/gcc/startup_stm32wb55xx_cm4.s
-C_SOURCES		+= $(CUBE_DIR)/Drivers/CMSIS/Device/ST/STM32WBxx/Source/Templates/system_stm32wbxx.c
-C_SOURCES		+= $(CUBE_DIR)/Drivers/STM32WBxx_HAL_Driver/Src/stm32wbxx_ll_utils.c
-C_SOURCES		+= $(CUBE_DIR)/Drivers/STM32WBxx_HAL_Driver/Src/stm32wbxx_ll_gpio.c
-C_SOURCES		+= $(CUBE_DIR)/Drivers/STM32WBxx_HAL_Driver/Src/stm32wbxx_ll_i2c.c
-C_SOURCES		+= $(CUBE_DIR)/Drivers/STM32WBxx_HAL_Driver/Src/stm32wbxx_ll_spi.c
-
-CFLAGS			+= -I$(CUBE_DIR)/Drivers/CMSIS/Include
-CFLAGS			+= -I$(CUBE_DIR)/Drivers/CMSIS/Device/ST/STM32WBxx/Include
-CFLAGS			+= -I$(CUBE_DIR)/Drivers/STM32WBxx_HAL_Driver/Inc
-
-LDFLAGS			+= -T$(TARGET_DIR)/stm32wb55xx_flash_cm4.ld
-
-# Drivers
-DRIVERS_DIR		= $(PROJECT_ROOT)//lib/drivers
-CFLAGS			+= -I$(DRIVERS_DIR)
-C_SOURCES		+= $(DRIVERS_DIR)/lp5562.c
-
-# API-HAL
-CFLAGS			+= -I$(TARGET_DIR)/furi_hal
-C_SOURCES		+= $(wildcard $(TARGET_DIR)/furi_hal/*.c)
-
-# Version generation
-C_SOURCES		+= $(PROJECT_ROOT)/lib/toolbox/version.c
-
-ASM_SOURCES		+= $(wildcard $(TARGET_DIR)/*.s)
-C_SOURCES		+= $(wildcard $(TARGET_DIR)/*.c)
-CPP_SOURCES		+= $(wildcard $(TARGET_DIR)/*.cpp)
-
-SVD_FILE		= $(PROJECT_ROOT)/debug/STM32WB55_CM4.svd

+ 0 - 15
bootloader/targets/furi_hal_include/furi_hal.h

@@ -1,15 +0,0 @@
-#pragma once
-
-#include <furi_hal_i2c.h>
-#include <furi_hal_light.h>
-#include <furi_hal_resources.h>
-#include <furi_hal_spi.h>
-#include <furi_hal_version.h>
-
-#define furi_assert(value) (void)(value)
-
-void furi_hal_init();
-
-void furi_hal_delay_ms(float milliseconds);
-
-void furi_hal_delay_us(float microseconds);

+ 0 - 102
bootloader/targets/furi_hal_include/furi_hal_spi.h

@@ -1,102 +0,0 @@
-#pragma once
-
-#include <furi_hal_spi_config.h>
-#include <stdbool.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/** Initialize SPI HAL */
-void furi_hal_spi_init();
-
-/** Initialize SPI Bus
- *
- * @param      handle  pointer to FuriHalSpiBus instance
- */
-void furi_hal_spi_bus_init(FuriHalSpiBus* bus);
-
-/** Deinitialize SPI Bus
- *
- * @param      handle  pointer to FuriHalSpiBus instance
- */
-void furi_hal_spi_bus_deinit(FuriHalSpiBus* bus);
-
-/** Initialize SPI Bus Handle
- *
- * @param      handle  pointer to FuriHalSpiBusHandle instance
- */
-void furi_hal_spi_bus_handle_init(FuriHalSpiBusHandle* handle);
-
-/** Deinitialize SPI Bus Handle
- *
- * @param      handle  pointer to FuriHalSpiBusHandle instance
- */
-void furi_hal_spi_bus_handle_deinit(FuriHalSpiBusHandle* handle);
-
-/** Acquire SPI bus
- *
- * @warning blocking, calls `furi_crash` on programming error, CS transition is up to handler event routine
- *
- * @param      handle  pointer to FuriHalSpiBusHandle instance
- */
-void furi_hal_spi_acquire(FuriHalSpiBusHandle* handle);
-
-/** Release SPI bus
- *
- * @warning calls `furi_crash` on programming error, CS transition is up to handler event routine
- * 
- * @param      handle  pointer to FuriHalSpiBusHandle instance
- */
-void furi_hal_spi_release(FuriHalSpiBusHandle* handle);
-
-/** SPI Receive
- *
- * @param      handle   pointer to FuriHalSpiBusHandle instance
- * @param      buffer   receive buffer
- * @param      size     transaction size (buffer size)
- * @param      timeout  operation timeout in ms
- *
- * @return     true on sucess
- */
-bool furi_hal_spi_bus_rx(
-    FuriHalSpiBusHandle* handle,
-    uint8_t* buffer,
-    size_t size,
-    uint32_t timeout);
-
-/** SPI Transmit
- *
- * @param      handle   pointer to FuriHalSpiBusHandle instance
- * @param      buffer   transmit buffer
- * @param      size     transaction size (buffer size)
- * @param      timeout  operation timeout in ms
- *
- * @return     true on success
- */
-bool furi_hal_spi_bus_tx(
-    FuriHalSpiBusHandle* handle,
-    uint8_t* buffer,
-    size_t size,
-    uint32_t timeout);
-
-/** SPI Transmit and Receive
- *
- * @param      handle     pointer to FuriHalSpiBusHandle instance
- * @param      tx_buffer  pointer to tx buffer
- * @param      rx_buffer  pointer to rx buffer
- * @param      size       transaction size (buffer size)
- * @param      timeout    operation timeout in ms
- *
- * @return     true on success
- */
-bool furi_hal_spi_bus_trx(
-    FuriHalSpiBusHandle* handle,
-    uint8_t* tx_buffer,
-    uint8_t* rx_buffer,
-    size_t size,
-    uint32_t timeout);
-
-#ifdef __cplusplus
-}
-#endif

+ 0 - 173
bootloader/targets/furi_hal_include/furi_hal_version.h

@@ -1,173 +0,0 @@
-/**
- * @file furi_hal_version.h
- * Version HAL API
- */
-
-#pragma once
-
-#include <stdbool.h>
-#include <stdint.h>
-#include <string.h>
-#include <lib/toolbox/version.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define FURI_HAL_VERSION_NAME_LENGTH 8
-#define FURI_HAL_VERSION_ARRAY_NAME_LENGTH (FURI_HAL_VERSION_NAME_LENGTH + 1)
-/** BLE symbol + "Flipper " + name */
-#define FURI_HAL_VERSION_DEVICE_NAME_LENGTH (1 + 8 + FURI_HAL_VERSION_ARRAY_NAME_LENGTH)
-
-/** OTP Versions enum */
-typedef enum {
-    FuriHalVersionOtpVersion0 = 0x00,
-    FuriHalVersionOtpVersion1 = 0x01,
-    FuriHalVersionOtpVersion2 = 0x02,
-    FuriHalVersionOtpVersionEmpty = 0xFFFFFFFE,
-    FuriHalVersionOtpVersionUnknown = 0xFFFFFFFF,
-} FuriHalVersionOtpVersion;
-
-/** Device Colors */
-typedef enum {
-    FuriHalVersionColorUnknown = 0x00,
-    FuriHalVersionColorBlack = 0x01,
-    FuriHalVersionColorWhite = 0x02,
-} FuriHalVersionColor;
-
-/** Device Regions */
-typedef enum {
-    FuriHalVersionRegionUnknown = 0x00,
-    FuriHalVersionRegionEuRu = 0x01,
-    FuriHalVersionRegionUsCaAu = 0x02,
-    FuriHalVersionRegionJp = 0x03,
-} FuriHalVersionRegion;
-
-/** Device Display */
-typedef enum {
-    FuriHalVersionDisplayUnknown = 0x00,
-    FuriHalVersionDisplayErc = 0x01,
-    FuriHalVersionDisplayMgg = 0x02,
-} FuriHalVersionDisplay;
-
-/** Init flipper version
- */
-void furi_hal_version_init();
-
-/** Check target firmware version
- *
- * @return     true if target and real matches
- */
-bool furi_hal_version_do_i_belong_here();
-
-/** Get model name
- *
- * @return     model name C-string
- */
-const char* furi_hal_version_get_model_name();
-
-/** Get OTP version
- *
- * @return     OTP Version
- */
-const FuriHalVersionOtpVersion furi_hal_version_get_otp_version();
-
-/** Get hardware version
- *
- * @return     Hardware Version
- */
-const uint8_t furi_hal_version_get_hw_version();
-
-/** Get hardware target
- *
- * @return     Hardware Target
- */
-const uint8_t furi_hal_version_get_hw_target();
-
-/** Get hardware body
- *
- * @return     Hardware Body
- */
-const uint8_t furi_hal_version_get_hw_body();
-
-/** Get hardware body color
- *
- * @return     Hardware Color
- */
-const FuriHalVersionColor furi_hal_version_get_hw_color();
-
-/** Get hardware connect
- *
- * @return     Hardware Interconnect
- */
-const uint8_t furi_hal_version_get_hw_connect();
-
-/** Get hardware region
- *
- * @return     Hardware Region
- */
-const FuriHalVersionRegion furi_hal_version_get_hw_region();
-
-/** Get hardware display id
- *
- * @return     Display id
- */
-const FuriHalVersionDisplay furi_hal_version_get_hw_display();
-
-/** Get hardware timestamp
- *
- * @return     Hardware Manufacture timestamp
- */
-const uint32_t furi_hal_version_get_hw_timestamp();
-
-/** Get pointer to target name
- *
- * @return     Hardware Name C-string
- */
-const char* furi_hal_version_get_name_ptr();
-
-/** Get pointer to target device name
- *
- * @return     Hardware Device Name C-string
- */
-const char* furi_hal_version_get_device_name_ptr();
-
-/** Get pointer to target ble local device name
- *
- * @return     Ble Device Name C-string
- */
-const char* furi_hal_version_get_ble_local_device_name_ptr();
-
-/** Get BLE MAC address
- *
- * @return     pointer to BLE MAC address
- */
-const uint8_t* furi_hal_version_get_ble_mac();
-
-/** Get address of version structure of bootloader, stored in chip flash.
- *
- * @return     Address of boot version structure.
- */
-const struct Version* furi_hal_version_get_bootloader_version();
-
-/** Get address of version structure of firmware.
- *
- * @return     Address of firmware version structure.
- */
-const struct Version* furi_hal_version_get_firmware_version();
-
-/** Get platform UID size in bytes
- *
- * @return     UID size in bytes
- */
-size_t furi_hal_version_uid_size();
-
-/** Get const pointer to UID
- *
- * @return     pointer to UID
- */
-const uint8_t* furi_hal_version_uid();
-
-#ifdef __cplusplus
-}
-#endif

+ 0 - 25
bootloader/targets/furi_hal_include/target.h

@@ -1,25 +0,0 @@
-#ifndef TARGET_H
-#define TARGET_H
-
-/**
- * Initialize hardware
-*/
-void target_init();
-
-/**
- * Check if dfu mode requested
- * @return 1 if dfu mode requested, 0 if not
- */
-int target_is_dfu_requested();
-
-/**
- * Switch to dfu mode
- */
-void target_switch2dfu();
-
-/**
- * Switch to OS
- */
-void target_switch2os();
-
-#endif

+ 3 - 8
core/flipper.c

@@ -11,13 +11,14 @@ static void flipper_print_version(const char* target, const Version* version) {
             TAG,
             TAG,
             "\r\n\t%s version:\t%s\r\n"
             "\r\n\t%s version:\t%s\r\n"
             "\tBuild date:\t\t%s\r\n"
             "\tBuild date:\t\t%s\r\n"
-            "\tGit Commit:\t\t%s (%s)\r\n"
+            "\tGit Commit:\t\t%s (%s)%s\r\n"
             "\tGit Branch:\t\t%s",
             "\tGit Branch:\t\t%s",
             target,
             target,
             version_get_version(version),
             version_get_version(version),
             version_get_builddate(version),
             version_get_builddate(version),
             version_get_githash(version),
             version_get_githash(version),
             version_get_gitbranchnum(version),
             version_get_gitbranchnum(version),
+            version_get_dirty_flag(version) ? " (dirty)" : "",
             version_get_gitbranch(version));
             version_get_gitbranch(version));
     } else {
     } else {
         FURI_LOG_I(TAG, "No build info for %s", target);
         FURI_LOG_I(TAG, "No build info for %s", target);
@@ -25,13 +26,7 @@ static void flipper_print_version(const char* target, const Version* version) {
 }
 }
 
 
 void flipper_init() {
 void flipper_init() {
-    const Version* version;
-
-    version = (const Version*)furi_hal_version_get_bootloader_version();
-    flipper_print_version("Bootloader", version);
-
-    version = (const Version*)furi_hal_version_get_firmware_version();
-    flipper_print_version("Firmware", version);
+    flipper_print_version("Firmware", furi_hal_version_get_firmware_version());
 
 
     FURI_LOG_I(TAG, "starting services");
     FURI_LOG_I(TAG, "starting services");
 
 

+ 6 - 0
core/furi.c

@@ -1,7 +1,13 @@
 #include "furi.h"
 #include "furi.h"
 
 
 void furi_init() {
 void furi_init() {
+    osKernelInitialize();
+
     furi_log_init();
     furi_log_init();
     furi_record_init();
     furi_record_init();
     furi_stdglue_init();
     furi_stdglue_init();
 }
 }
+
+void furi_run() {
+    osKernelStart();
+}

+ 2 - 0
core/furi.h

@@ -27,6 +27,8 @@ extern "C" {
 
 
 void furi_init();
 void furi_init();
 
 
+void furi_run();
+
 #ifdef __cplusplus
 #ifdef __cplusplus
 }
 }
 #endif
 #endif

+ 8 - 6
core/furi/check.c

@@ -21,20 +21,21 @@ void __furi_print_name() {
     }
     }
 }
 }
 
 
-void __furi_halt() {
+static FURI_NORETURN void __furi_halt() {
     asm volatile(
     asm volatile(
 #ifdef FURI_DEBUG
 #ifdef FURI_DEBUG
         "bkpt 0x00  \n"
         "bkpt 0x00  \n"
 #endif
 #endif
-        "loop:      \n"
+        "loop%=:    \n"
         "wfi        \n"
         "wfi        \n"
-        "b loop     \n"
+        "b loop%=   \n"
         :
         :
         :
         :
         : "memory");
         : "memory");
+    __builtin_unreachable();
 }
 }
 
 
-void furi_crash(const char* message) {
+FURI_NORETURN void furi_crash(const char* message) {
     __disable_irq();
     __disable_irq();
 
 
     if(message == NULL) {
     if(message == NULL) {
@@ -54,9 +55,10 @@ void furi_crash(const char* message) {
     furi_hal_console_puts("\033[0m\r\n");
     furi_hal_console_puts("\033[0m\r\n");
     furi_hal_power_reset();
     furi_hal_power_reset();
 #endif
 #endif
+    __builtin_unreachable();
 }
 }
 
 
-void furi_halt(const char* message) {
+FURI_NORETURN void furi_halt(const char* message) {
     __disable_irq();
     __disable_irq();
 
 
     if(message == NULL) {
     if(message == NULL) {
@@ -69,4 +71,4 @@ void furi_halt(const char* message) {
     furi_hal_console_puts("\r\nSystem halted. Bye-bye!\r\n");
     furi_hal_console_puts("\r\nSystem halted. Bye-bye!\r\n");
     furi_hal_console_puts("\033[0m\r\n");
     furi_hal_console_puts("\033[0m\r\n");
     __furi_halt();
     __furi_halt();
-}
+}

+ 6 - 3
core/furi/check.h

@@ -1,7 +1,10 @@
 #pragma once
 #pragma once
-
 #ifdef __cplusplus
 #ifdef __cplusplus
 extern "C" {
 extern "C" {
+#define FURI_NORETURN [[noreturn]]
+#else
+#include <stdnoreturn.h>
+#define FURI_NORETURN noreturn
 #endif
 #endif
 
 
 /** Check condition and crash if check failed */
 /** Check condition and crash if check failed */
@@ -15,10 +18,10 @@ extern "C" {
 #endif
 #endif
 
 
 /** Crash system */
 /** Crash system */
-void furi_crash(const char* message);
+FURI_NORETURN void furi_crash(const char* message);
 
 
 /** Halt system */
 /** Halt system */
-void furi_halt(const char* message);
+FURI_NORETURN void furi_halt(const char* message);
 
 
 #ifdef __cplusplus
 #ifdef __cplusplus
 }
 }

+ 14 - 10
core/furi/common_defines.h

@@ -101,18 +101,20 @@ extern "C" {
 #endif
 #endif
 
 
 #ifndef FURI_IS_ISR
 #ifndef FURI_IS_ISR
-#define FURI_IS_ISR() \
-    (FURI_IS_IRQ_MODE() || (FURI_IS_IRQ_MASKED() && (osKernelGetState() == osKernelRunning)))
+#define FURI_IS_ISR() (FURI_IS_IRQ_MODE() || FURI_IS_IRQ_MASKED())
 #endif
 #endif
 
 
 #ifndef FURI_CRITICAL_ENTER
 #ifndef FURI_CRITICAL_ENTER
-#define FURI_CRITICAL_ENTER()                   \
-    uint32_t __isrm = 0;                        \
-    bool __from_isr = FURI_IS_ISR();            \
-    if(__from_isr) {                            \
-        __isrm = taskENTER_CRITICAL_FROM_ISR(); \
-    } else {                                    \
-        taskENTER_CRITICAL();                   \
+#define FURI_CRITICAL_ENTER()                                        \
+    uint32_t __isrm = 0;                                             \
+    bool __from_isr = FURI_IS_ISR();                                 \
+    bool __kernel_running = (osKernelGetState() == osKernelRunning); \
+    if(__from_isr) {                                                 \
+        __isrm = taskENTER_CRITICAL_FROM_ISR();                      \
+    } else if(__kernel_running) {                                    \
+        taskENTER_CRITICAL();                                        \
+    } else {                                                         \
+        __disable_irq();                                             \
     }
     }
 #endif
 #endif
 
 
@@ -120,8 +122,10 @@ extern "C" {
 #define FURI_CRITICAL_EXIT()                \
 #define FURI_CRITICAL_EXIT()                \
     if(__from_isr) {                        \
     if(__from_isr) {                        \
         taskEXIT_CRITICAL_FROM_ISR(__isrm); \
         taskEXIT_CRITICAL_FROM_ISR(__isrm); \
-    } else {                                \
+    } else if(__kernel_running) {           \
         taskEXIT_CRITICAL();                \
         taskEXIT_CRITICAL();                \
+    } else {                                \
+        __enable_irq();                     \
     }
     }
 #endif
 #endif
 
 

+ 5 - 0
core/furi/memmgr.c

@@ -4,6 +4,7 @@
 extern void* pvPortMalloc(size_t xSize);
 extern void* pvPortMalloc(size_t xSize);
 extern void vPortFree(void* pv);
 extern void vPortFree(void* pv);
 extern size_t xPortGetFreeHeapSize(void);
 extern size_t xPortGetFreeHeapSize(void);
+extern size_t xPortGetTotalHeapSize(void);
 extern size_t xPortGetMinimumEverFreeHeapSize(void);
 extern size_t xPortGetMinimumEverFreeHeapSize(void);
 
 
 void* malloc(size_t size) {
 void* malloc(size_t size) {
@@ -50,6 +51,10 @@ size_t memmgr_get_free_heap(void) {
     return xPortGetFreeHeapSize();
     return xPortGetFreeHeapSize();
 }
 }
 
 
+size_t memmgr_get_total_heap(void) {
+    return xPortGetTotalHeapSize();
+}
+
 size_t memmgr_get_minimum_free_heap(void) {
 size_t memmgr_get_minimum_free_heap(void) {
     return xPortGetMinimumEverFreeHeapSize();
     return xPortGetMinimumEverFreeHeapSize();
 }
 }

+ 6 - 0
core/furi/memmgr.h

@@ -23,6 +23,12 @@ extern "C" {
  */
  */
 size_t memmgr_get_free_heap(void);
 size_t memmgr_get_free_heap(void);
 
 
+/** Get total heap size
+ *
+ * @return     total heap size in bytes
+ */
+size_t memmgr_get_total_heap(void);
+
 /** Get heap watermark
 /** Get heap watermark
  *
  *
  * @return     minimum heap in bytes
  * @return     minimum heap in bytes

+ 5 - 0
core/furi/memmgr_heap.c

@@ -526,6 +526,11 @@ void vPortFree(void* pv) {
 }
 }
 /*-----------------------------------------------------------*/
 /*-----------------------------------------------------------*/
 
 
+size_t xPortGetTotalHeapSize(void) {
+    return (size_t)&__heap_end__ - (size_t)&__heap_start__;
+}
+/*-----------------------------------------------------------*/
+
 size_t xPortGetFreeHeapSize(void) {
 size_t xPortGetFreeHeapSize(void) {
     return xFreeBytesRemaining;
     return xFreeBytesRemaining;
 }
 }

+ 1 - 1
core/furi/record.h

@@ -49,7 +49,7 @@ bool furi_record_destroy(const char* name);
  *
  *
  * @return     pointer to the record
  * @return     pointer to the record
  * @note       Thread safe. Open and close must be executed from the same
  * @note       Thread safe. Open and close must be executed from the same
- *             thread. Suspends caller thread till record appear
+ *             thread. Suspends caller thread till record is available
  */
  */
 void* furi_record_open(const char* name);
 void* furi_record_open(const char* name);
 
 

+ 7 - 1
core/furi/thread.c

@@ -30,7 +30,7 @@ void furi_thread_set_state(FuriThread* thread, FuriThreadState state) {
     }
     }
 }
 }
 
 
-void furi_thread_body(void* context) {
+static void furi_thread_body(void* context) {
     furi_assert(context);
     furi_assert(context);
     FuriThread* thread = context;
     FuriThread* thread = context;
 
 
@@ -167,3 +167,9 @@ size_t furi_thread_get_heap_size(FuriThread* thread) {
     furi_assert(thread->heap_trace_enabled == true);
     furi_assert(thread->heap_trace_enabled == true);
     return thread->heap_size;
     return thread->heap_size;
 }
 }
+
+int32_t furi_thread_get_return_code(FuriThread* thread) {
+    furi_assert(thread);
+    furi_assert(thread->state == FuriThreadStateStopped);
+    return thread->ret;
+}

+ 9 - 1
core/furi/thread.h

@@ -150,6 +150,14 @@ void furi_thread_disable_heap_trace(FuriThread* thread);
  */
  */
 size_t furi_thread_get_heap_size(FuriThread* thread);
 size_t furi_thread_get_heap_size(FuriThread* thread);
 
 
+/** Get thread return code
+ *
+ * @param      thread  FuriThread instance
+ *
+ * @return     return code
+ */
+int32_t furi_thread_get_return_code(FuriThread* thread);
+
 #ifdef __cplusplus
 #ifdef __cplusplus
 }
 }
-#endif
+#endif

+ 6 - 1
firmware/Makefile

@@ -1,6 +1,5 @@
 MAKEFILE_DIR	:= $(dir $(abspath $(firstword $(MAKEFILE_LIST))))
 MAKEFILE_DIR	:= $(dir $(abspath $(firstword $(MAKEFILE_LIST))))
 PROJECT_ROOT	:= $(abspath $(MAKEFILE_DIR)/..)
 PROJECT_ROOT	:= $(abspath $(MAKEFILE_DIR)/..)
-PROJECT			:= firmware
 
 
 include 		$(PROJECT_ROOT)/make/base.mk
 include 		$(PROJECT_ROOT)/make/base.mk
 include 		$(PROJECT_ROOT)/make/freertos-heap.mk
 include 		$(PROJECT_ROOT)/make/freertos-heap.mk
@@ -17,6 +16,12 @@ include			$(PROJECT_ROOT)/make/defaults.mk
 TARGET_DIR		= targets/$(TARGET)
 TARGET_DIR		= targets/$(TARGET)
 include			$(TARGET_DIR)/target.mk
 include			$(TARGET_DIR)/target.mk
 
 
+ifeq ($(RAM_EXEC), 0)
+PROJECT			:= firmware
+else
+PROJECT			:= updater
+endif
+
 include			$(PROJECT_ROOT)/make/git.mk
 include			$(PROJECT_ROOT)/make/git.mk
 include			$(PROJECT_ROOT)/make/toolchain.mk
 include			$(PROJECT_ROOT)/make/toolchain.mk
 include			$(PROJECT_ROOT)/make/rules.mk
 include			$(PROJECT_ROOT)/make/rules.mk

Некоторые файлы не были показаны из-за большого количества измененных файлов