Merge remote-tracking branch 'origin/dev' into release-candidate
							
								
								
									
										115
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						| @ -15,11 +15,8 @@ env: | |||||||
| 
 | 
 | ||||||
| jobs: | jobs: | ||||||
|   main: |   main: | ||||||
|     runs-on: [self-hosted,FlipperZero] |     runs-on: [self-hosted,FlipperZeroShell] | ||||||
|     steps: |     steps: | ||||||
|       - name: 'Cleanup workspace' |  | ||||||
|         uses: AutoModality/action-clean@v1 |  | ||||||
| 
 |  | ||||||
|       - name: 'Decontaminate previous build leftovers' |       - name: 'Decontaminate previous build leftovers' | ||||||
|         run: | |         run: | | ||||||
|           if [ -d .git ] |           if [ -d .git ] | ||||||
| @ -32,12 +29,8 @@ jobs: | |||||||
|         uses: actions/checkout@v2 |         uses: actions/checkout@v2 | ||||||
|         with: |         with: | ||||||
|           fetch-depth: 0 |           fetch-depth: 0 | ||||||
|           submodules: true |  | ||||||
|           ref: ${{ github.event.pull_request.head.sha }} |           ref: ${{ github.event.pull_request.head.sha }} | ||||||
| 
 | 
 | ||||||
|       - name: 'Build docker image' |  | ||||||
|         uses: ./.github/actions/docker |  | ||||||
| 
 |  | ||||||
|       - name: 'Make artifacts directory' |       - name: 'Make artifacts directory' | ||||||
|         run: | |         run: | | ||||||
|           test -d artifacts && rm -rf artifacts || true |           test -d artifacts && rm -rf artifacts || true | ||||||
| @ -71,40 +64,38 @@ jobs: | |||||||
|         run: | |         run: | | ||||||
|           tar czpf artifacts/flipper-z-any-scripts-${{steps.names.outputs.suffix}}.tgz scripts |           tar czpf artifacts/flipper-z-any-scripts-${{steps.names.outputs.suffix}}.tgz scripts | ||||||
| 
 | 
 | ||||||
|       - name: 'Build the firmware in docker' |       - name: 'Build the firmware' | ||||||
|         uses: ./.github/actions/docker |         run: | | ||||||
|         with: |           set -e | ||||||
|           run: | |           for TARGET in ${TARGETS} | ||||||
|             set -e |           do | ||||||
|             for TARGET in ${TARGETS} |             FBT_TOOLCHAIN_PATH=/opt ./fbt TARGET_HW=`echo ${TARGET} | sed 's/f//'` updater_package ${{ startsWith(github.ref, 'refs/tags') && 'DEBUG=0 COMPACT=1' || '' }} | ||||||
|             do |           done | ||||||
|               ./fbt TARGET_HW=`echo ${TARGET} | sed 's/f//'` --with-updater updater_package ${{ startsWith(github.ref, 'refs/tags') && 'DEBUG=0 COMPACT=1' || '' }} |  | ||||||
|             done |  | ||||||
| 
 | 
 | ||||||
|       - name: 'Move upload files' |       - name: 'Move upload files' | ||||||
|         if: ${{ !github.event.pull_request.head.repo.fork }} |         if: ${{ !github.event.pull_request.head.repo.fork }} | ||||||
|         uses: ./.github/actions/docker |         run: | | ||||||
|         with: |           set -e | ||||||
|           run: | |           for TARGET in ${TARGETS} | ||||||
|             set -e |           do | ||||||
|             for TARGET in ${TARGETS} |             mv dist/${TARGET}-*/* artifacts/ | ||||||
|             do |           done | ||||||
|               mv dist/${TARGET}-*/* artifacts/ |  | ||||||
|             done |  | ||||||
| 
 | 
 | ||||||
|       - name: 'Bundle self-update package' |       - name: 'Bundle self-update package' | ||||||
|         if: ${{ !github.event.pull_request.head.repo.fork }} |         if: ${{ !github.event.pull_request.head.repo.fork }} | ||||||
|         uses: ./.github/actions/docker |         run: | | ||||||
|         with: |           set -e | ||||||
|           run: | |           for UPDATEBUNDLE in artifacts/*/ | ||||||
|             set -e |           do | ||||||
|             for UPDATEBUNDLE in artifacts/*/ |             BUNDLE_NAME=`echo $UPDATEBUNDLE | cut -d'/' -f2` | ||||||
|             do |             echo Packaging ${BUNDLE_NAME} | ||||||
|               BUNDLE_NAME=`echo $UPDATEBUNDLE | cut -d'/' -f2` |             tar czpf artifacts/flipper-z-${BUNDLE_NAME}.tgz -C artifacts ${BUNDLE_NAME} | ||||||
|               echo Packaging ${BUNDLE_NAME} |             rm -rf artifacts/${BUNDLE_NAME} | ||||||
|               tar czpf artifacts/flipper-z-${BUNDLE_NAME}.tgz -C artifacts ${BUNDLE_NAME} |           done | ||||||
|               rm -rf artifacts/${BUNDLE_NAME} | 
 | ||||||
|             done |       - name: "Check for uncommited changes" | ||||||
|  |         run: | | ||||||
|  |           git diff --exit-code | ||||||
| 
 | 
 | ||||||
|       - name: 'Bundle resources' |       - name: 'Bundle resources' | ||||||
|         if: ${{ !github.event.pull_request.head.repo.fork }} |         if: ${{ !github.event.pull_request.head.repo.fork }} | ||||||
| @ -113,29 +104,23 @@ jobs: | |||||||
| 
 | 
 | ||||||
|       - name: 'Bundle core2 firmware' |       - name: 'Bundle core2 firmware' | ||||||
|         if: ${{ !github.event.pull_request.head.repo.fork }} |         if: ${{ !github.event.pull_request.head.repo.fork }} | ||||||
|         uses: ./.github/actions/docker |         run: | | ||||||
|         with: |           FBT_TOOLCHAIN_PATH=/opt ./fbt copro_dist | ||||||
|           run: | |           tar czpf artifacts/flipper-z-any-core2_firmware-${{steps.names.outputs.suffix}}.tgz -C assets core2_firmware | ||||||
|             ./fbt copro_dist |  | ||||||
|             tar czpf artifacts/flipper-z-any-core2_firmware-${{steps.names.outputs.suffix}}.tgz -C assets core2_firmware |  | ||||||
| 
 | 
 | ||||||
|       - name: 'Upload artifacts to update server' |       - name: 'Upload artifacts to update server' | ||||||
|         if: ${{ !github.event.pull_request.head.repo.fork }} |         if: ${{ !github.event.pull_request.head.repo.fork }} | ||||||
|         uses: burnett01/rsync-deployments@5.1 |         run: | | ||||||
|         with: |           echo "${{ secrets.RSYNC_DEPLOY_KEY }}" > deploy_key; | ||||||
|           switches: -avzP --delete --mkpath |           chmod 600 ./deploy_key; | ||||||
|           path: artifacts/ |           rsync -avzP --mkpath \ | ||||||
|           remote_path: "${{ secrets.RSYNC_DEPLOY_BASE_PATH }}${{steps.names.outputs.artifacts-path}}/" |               -e 'ssh -p ${{ secrets.RSYNC_DEPLOY_PORT }} -i ./deploy_key' \ | ||||||
|           remote_host: ${{ secrets.RSYNC_DEPLOY_HOST }} |               artifacts/ ${{ secrets.RSYNC_DEPLOY_USER }}@${{ secrets.RSYNC_DEPLOY_HOST }}:"${{ secrets.RSYNC_DEPLOY_BASE_PATH }}${{steps.names.outputs.artifacts-path}}/"; | ||||||
|           remote_port: ${{ secrets.RSYNC_DEPLOY_PORT }} |           rm ./deploy_key; | ||||||
|           remote_user: ${{ secrets.RSYNC_DEPLOY_USER }} |  | ||||||
|           remote_key: ${{ secrets.RSYNC_DEPLOY_KEY }} |  | ||||||
| 
 | 
 | ||||||
|       - name: 'Trigger update server reindex' |       - name: 'Trigger update server reindex' | ||||||
|         if: ${{ !github.event.pull_request.head.repo.fork }} |         if: ${{ !github.event.pull_request.head.repo.fork }} | ||||||
|         uses: wei/curl@master |         run: curl -X POST -F 'key=${{ secrets.REINDEX_KEY }}' ${{ secrets.REINDEX_URL }} | ||||||
|         with: |  | ||||||
|           args: -X POST -F 'key=${{ secrets.REINDEX_KEY }}' ${{ secrets.REINDEX_URL }} |  | ||||||
| 
 | 
 | ||||||
|       - name: 'Find Previous Comment' |       - name: 'Find Previous Comment' | ||||||
|         if: ${{ !github.event.pull_request.head.repo.fork && github.event.pull_request }} |         if: ${{ !github.event.pull_request.head.repo.fork && github.event.pull_request }} | ||||||
| @ -161,11 +146,8 @@ jobs: | |||||||
| 
 | 
 | ||||||
|   compact: |   compact: | ||||||
|     if: ${{ !startsWith(github.ref, 'refs/tags') }} |     if: ${{ !startsWith(github.ref, 'refs/tags') }} | ||||||
|     runs-on: [self-hosted,FlipperZero] |     runs-on: [self-hosted,FlipperZeroShell] | ||||||
|     steps: |     steps: | ||||||
|       - name: 'Cleanup workspace' |  | ||||||
|         uses: AutoModality/action-clean@v1 |  | ||||||
| 
 |  | ||||||
|       - name: 'Decontaminate previous build leftovers' |       - name: 'Decontaminate previous build leftovers' | ||||||
|         run: | |         run: | | ||||||
|           if [ -d .git ] |           if [ -d .git ] | ||||||
| @ -181,9 +163,6 @@ jobs: | |||||||
|           submodules: true |           submodules: true | ||||||
|           ref: ${{ github.event.pull_request.head.sha }} |           ref: ${{ github.event.pull_request.head.sha }} | ||||||
| 
 | 
 | ||||||
|       - name: 'Build docker image' |  | ||||||
|         uses: ./.github/actions/docker |  | ||||||
| 
 |  | ||||||
|       - name: 'Generate suffix and folder name' |       - name: 'Generate suffix and folder name' | ||||||
|         id: names |         id: names | ||||||
|         run: | |         run: | | ||||||
| @ -203,12 +182,10 @@ jobs: | |||||||
|           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 | ||||||
| 
 | 
 | ||||||
|       - name: 'Build the firmware in docker' |       - name: 'Build the firmware' | ||||||
|         uses: ./.github/actions/docker |         run: | | ||||||
|         with: |           set -e | ||||||
|           run: | |           for TARGET in ${TARGETS} | ||||||
|             set -e |           do | ||||||
|             for TARGET in ${TARGETS} |             FBT_TOOLCHAIN_PATH=/opt ./fbt TARGET_HW=`echo ${TARGET} | sed 's/f//'` updater_package DEBUG=0 COMPACT=1 | ||||||
|             do |           done | ||||||
|               ./fbt TARGET_HW=`echo ${TARGET} | sed 's/f//'` --with-updater updater_package DEBUG=0 COMPACT=1 |  | ||||||
|             done |  | ||||||
|  | |||||||
							
								
								
									
										46
									
								
								.github/workflows/build_toolchain.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						| @ -1,46 +0,0 @@ | |||||||
| name: 'Build toolchain Docker image' |  | ||||||
| 
 |  | ||||||
| on: |  | ||||||
|   push: |  | ||||||
|     branches: |  | ||||||
|       - dev |  | ||||||
|     tags: |  | ||||||
|       - '*' |  | ||||||
| 
 |  | ||||||
| jobs: |  | ||||||
|   build: |  | ||||||
|     runs-on: ubuntu-latest |  | ||||||
|     steps: |  | ||||||
|       - name: Checkout code |  | ||||||
|         uses: actions/checkout@v2 |  | ||||||
|         with: |  | ||||||
|           fetch-depth: 0 |  | ||||||
|       - name: Docker meta |  | ||||||
|         id: meta |  | ||||||
|         uses: docker/metadata-action@v3 |  | ||||||
|         with: |  | ||||||
|           images: flipperdevices/flipperzero-toolchain |  | ||||||
|           flavor: latest=${{ startsWith(github.ref, 'refs/tags/') && !endsWith(github.ref, 'rc')}} |  | ||||||
|           tags: | |  | ||||||
|             type=ref,event=branch |  | ||||||
|             type=ref,event=tag |  | ||||||
|       - name: Set up QEMU |  | ||||||
|         uses: docker/setup-qemu-action@v1 |  | ||||||
|       - name: Set up Docker Buildx |  | ||||||
|         uses: docker/setup-buildx-action@v1 |  | ||||||
|       - name: Login to DockerHub |  | ||||||
|         uses: docker/login-action@v1 |  | ||||||
|         with: |  | ||||||
|           username: ${{ secrets.DOCKERHUB_USERNAME }} |  | ||||||
|           password: ${{ secrets.DOCKERHUB_TOKEN }} |  | ||||||
|       - name: Build and push |  | ||||||
|         id: docker_build |  | ||||||
|         uses: docker/build-push-action@v2 |  | ||||||
|         with: |  | ||||||
|           context: docker/ |  | ||||||
|           push: true |  | ||||||
|           tags: ${{ steps.meta.outputs.tags }} |  | ||||||
|           labels: ${{ steps.meta.outputs.labels }} |  | ||||||
|           platforms: linux/amd64,linux/arm64 |  | ||||||
|           cache-from: type=registry,ref=flipperdevices/flipperzero-toolchain:buildcache |  | ||||||
|           cache-to: type=registry,ref=flipperdevices/flipperzero-toolchain:buildcache,mode=max |  | ||||||
							
								
								
									
										52
									
								
								.github/workflows/check_submodules.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						| @ -1,17 +1,47 @@ | |||||||
| name: 'Check submodules' | name: 'Check submodules branch' | ||||||
| 
 | 
 | ||||||
| on: | on: | ||||||
|  |   push: | ||||||
|  |     branches: | ||||||
|  |       - dev | ||||||
|  |       - "release*" | ||||||
|  |     tags: | ||||||
|  |       - '*' | ||||||
|   pull_request: |   pull_request: | ||||||
| 
 | 
 | ||||||
| jobs: | jobs: | ||||||
|   protobuf: |   check_protobuf: | ||||||
|     runs-on: ubuntu-latest |     runs-on: [self-hosted, FlipperZeroShell] | ||||||
|     steps: |     steps: | ||||||
|     - name: 'Checkout code' |       - name: 'Decontaminate previous build leftovers' | ||||||
|       uses: actions/checkout@v2 |         run: | | ||||||
|     - name: 'Check submodule commit branch' |           if [ -d .git ] | ||||||
|       uses: jtmullen/submodule-branch-check-action@v1 |           then | ||||||
|       with: |             git submodule status \ | ||||||
|         path: assets/protobuf |               || git checkout `git rev-list --max-parents=0 HEAD | tail -n 1` | ||||||
|         branch: dev |           fi | ||||||
|         fetch_depth: 50 | 
 | ||||||
|  |       - name: 'Checkout code' | ||||||
|  |         uses: actions/checkout@v2 | ||||||
|  |         with: | ||||||
|  |           fetch-depth: 0 | ||||||
|  | 
 | ||||||
|  |       - name: 'Check protobuf branch' | ||||||
|  |         run: | | ||||||
|  |           SUB_PATH="assets/protobuf"; | ||||||
|  |           SUB_BRANCH="dev"; | ||||||
|  |           SUB_COMMITS_MIN=40; | ||||||
|  |           cd "$SUB_PATH"; | ||||||
|  |           SUBMODULE_HASH="$(git rev-parse HEAD)"; | ||||||
|  |           BRANCHES=$(git branch -r --contains "$SUBMODULE_HASH"); | ||||||
|  |           COMMITS_IN_BRANCH="$(git rev-list --count dev)"; | ||||||
|  |           if [ $COMMITS_IN_BRANCH -lt $SUB_COMMITS_MIN ]; then | ||||||
|  |             echo "::set-output name=fails::error"; | ||||||
|  |             echo "::error::Error: Too low commits in $SUB_BRANCH of submodule $SUB_PATH: $COMMITS_IN_BRANCH(expected $SUB_COMMITS_MIN+)"; | ||||||
|  |             exit 1; | ||||||
|  |           fi | ||||||
|  |           if ! grep -q "/$SUB_BRANCH" <<< "$BRANCHES"; then | ||||||
|  |             echo "::set-output name=fails::error"; | ||||||
|  |             echo "::error::Error: Submodule $SUB_PATH is not on branch $SUB_BRANCH"; | ||||||
|  |             exit 1; | ||||||
|  |           fi | ||||||
|  | |||||||
							
								
								
									
										24
									
								
								.github/workflows/lint_c.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						| @ -1,6 +1,6 @@ | |||||||
| name: 'Lint C/C++ with clang-format' | name: 'Lint C/C++ with clang-format' | ||||||
| 
 | 
 | ||||||
| on:  | on: | ||||||
|   push: |   push: | ||||||
|     branches: |     branches: | ||||||
|       - dev |       - dev | ||||||
| @ -14,11 +14,8 @@ env: | |||||||
| 
 | 
 | ||||||
| jobs: | jobs: | ||||||
|   lint_c_cpp: |   lint_c_cpp: | ||||||
|     runs-on: [self-hosted,FlipperZero] |     runs-on: [self-hosted,FlipperZeroShell] | ||||||
|     steps: |     steps: | ||||||
|       - name: 'Cleanup workspace' |  | ||||||
|         uses: AutoModality/action-clean@v1 |  | ||||||
| 
 |  | ||||||
|       - name: 'Decontaminate previous build leftovers' |       - name: 'Decontaminate previous build leftovers' | ||||||
|         run: | |         run: | | ||||||
|           if [ -d .git ] |           if [ -d .git ] | ||||||
| @ -31,23 +28,10 @@ jobs: | |||||||
|         uses: actions/checkout@v2 |         uses: actions/checkout@v2 | ||||||
|         with: |         with: | ||||||
|           fetch-depth: 0 |           fetch-depth: 0 | ||||||
|           submodules: true |  | ||||||
| 
 |  | ||||||
|       - name: 'Docker cache' |  | ||||||
|         uses: satackey/action-docker-layer-caching@v0.0.11 |  | ||||||
|         continue-on-error: true |  | ||||||
|         with: |  | ||||||
|           key: docker-cache-${{ hashFiles('docker/**') }}-{hash} |  | ||||||
|           restore-keys: docker-cache-${{ hashFiles('docker/**') }}- |  | ||||||
| 
 |  | ||||||
|       - name: 'Build docker image' |  | ||||||
|         uses: ./.github/actions/docker |  | ||||||
| 
 | 
 | ||||||
|       - name: 'Check code formatting' |       - name: 'Check code formatting' | ||||||
|         id: syntax_check |         id: syntax_check | ||||||
|         uses: ./.github/actions/docker |         run: SET_GH_OUTPUT=1 FBT_TOOLCHAIN_PATH=/opt ./fbt lint | ||||||
|         with: |  | ||||||
|           run: SET_GH_OUTPUT=1 ./fbt lint |  | ||||||
| 
 | 
 | ||||||
|       - name: Report code formatting errors |       - name: Report code formatting errors | ||||||
|         if: failure() && steps.syntax_check.outputs.errors && github.event.pull_request |         if: failure() && steps.syntax_check.outputs.errors && github.event.pull_request | ||||||
| @ -59,4 +43,4 @@ jobs: | |||||||
|             ``` |             ``` | ||||||
|             ${{ steps.syntax_check.outputs.errors }} |             ${{ steps.syntax_check.outputs.errors }} | ||||||
|             ``` |             ``` | ||||||
|             You might want to run `docker compose exec dev make format` for an auto-fix. |             You might want to run `./fbt format` for an auto-fix. | ||||||
|  | |||||||
							
								
								
									
										14
									
								
								.github/workflows/lint_python.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						| @ -1,6 +1,6 @@ | |||||||
| name: 'Python Lint' | name: 'Python Lint' | ||||||
| 
 | 
 | ||||||
| on:  | on: | ||||||
|   push: |   push: | ||||||
|     branches: |     branches: | ||||||
|       - dev |       - dev | ||||||
| @ -11,11 +11,8 @@ on: | |||||||
| 
 | 
 | ||||||
| jobs: | jobs: | ||||||
|   lint_python: |   lint_python: | ||||||
|     runs-on: ubuntu-latest |     runs-on: [self-hosted,FlipperZeroShell] | ||||||
|     steps: |     steps: | ||||||
|       - name: 'Cleanup workspace' |  | ||||||
|         uses: AutoModality/action-clean@v1 |  | ||||||
| 
 |  | ||||||
|       - name: 'Decontaminate previous build leftovers' |       - name: 'Decontaminate previous build leftovers' | ||||||
|         run: | |         run: | | ||||||
|           if [ -d .git ] |           if [ -d .git ] | ||||||
| @ -29,8 +26,5 @@ jobs: | |||||||
|         with: |         with: | ||||||
|           fetch-depth: 0 |           fetch-depth: 0 | ||||||
| 
 | 
 | ||||||
|       - name: 'Setup python' |       - name: 'Check code formatting' | ||||||
|         uses: actions/setup-python@v2 |         run: SET_GH_OUTPUT=1 FBT_TOOLCHAIN_PATH=/opt ./fbt lint_py | ||||||
| 
 |  | ||||||
|       - name: 'Check python code with black' |  | ||||||
|         uses: psf/black@20.8b1 |  | ||||||
|  | |||||||
							
								
								
									
										7
									
								
								.github/workflows/reindex.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						| @ -7,9 +7,8 @@ on: | |||||||
| jobs: | jobs: | ||||||
|   reindex: |   reindex: | ||||||
|     name: 'Reindex updates' |     name: 'Reindex updates' | ||||||
|     runs-on: [self-hosted,FlipperZero] |     runs-on: [self-hosted,FlipperZeroShell] | ||||||
|     steps: |     steps: | ||||||
|       - name: Trigger reindex |       - name: Trigger reindex | ||||||
|         uses: wei/curl@master |         run: | | ||||||
|         with: |           curl -X POST -F 'key=${{ secrets.REINDEX_KEY }}' ${{ secrets.REINDEX_URL }} | ||||||
|           args: -X POST -F 'key=${{ secrets.REINDEX_KEY }}' ${{ secrets.REINDEX_URL }} |  | ||||||
|  | |||||||
| @ -29,11 +29,11 @@ They both must be flashed in the order described. | |||||||
| 
 | 
 | ||||||
| With Flipper attached over USB: | With Flipper attached over USB: | ||||||
| 
 | 
 | ||||||
| `./fbt --with-updater flash_usb` | `./fbt flash_usb` | ||||||
| 
 | 
 | ||||||
| Just building the package: | Just building the package: | ||||||
| 
 | 
 | ||||||
| `./fbt --with-updater updater_package` | `./fbt updater_package` | ||||||
| 
 | 
 | ||||||
| To update, copy the resulting directory to Flipper's SD card and navigate to `update.fuf` file in Archive app.  | To update, copy the resulting directory to Flipper's SD card and navigate to `update.fuf` file in Archive app.  | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										107
									
								
								SConstruct
									
									
									
									
									
								
							
							
						
						| @ -7,6 +7,7 @@ | |||||||
| # construction of certain targets behind command-line options. | # construction of certain targets behind command-line options. | ||||||
| 
 | 
 | ||||||
| import os | import os | ||||||
|  | import subprocess | ||||||
| 
 | 
 | ||||||
| EnsurePythonVersion(3, 8) | EnsurePythonVersion(3, 8) | ||||||
| 
 | 
 | ||||||
| @ -33,8 +34,10 @@ coreenv["ROOT_DIR"] = Dir(".") | |||||||
| 
 | 
 | ||||||
| # Create a separate "dist" environment and add construction envs to it | # Create a separate "dist" environment and add construction envs to it | ||||||
| distenv = coreenv.Clone( | distenv = coreenv.Clone( | ||||||
|     tools=["fbt_dist", "openocd", "blackmagic"], |     tools=["fbt_dist", "openocd", "blackmagic", "jflash"], | ||||||
|     OPENOCD_GDB_PIPE=["|openocd -c 'gdb_port pipe; log_output debug/openocd.log' ${[SINGLEQUOTEFUNC(OPENOCD_OPTS)]}"], |     OPENOCD_GDB_PIPE=[ | ||||||
|  |         "|openocd -c 'gdb_port pipe; log_output debug/openocd.log' ${[SINGLEQUOTEFUNC(OPENOCD_OPTS)]}" | ||||||
|  |     ], | ||||||
|     GDBOPTS_BASE=[ |     GDBOPTS_BASE=[ | ||||||
|         "-ex", |         "-ex", | ||||||
|         "target extended-remote ${GDBREMOTE}", |         "target extended-remote ${GDBREMOTE}", | ||||||
| @ -61,6 +64,7 @@ distenv = coreenv.Clone( | |||||||
|         "-ex", |         "-ex", | ||||||
|         "compare-sections", |         "compare-sections", | ||||||
|     ], |     ], | ||||||
|  |     JFLASHPROJECT="${ROOT_DIR.abspath}/debug/fw.jflash", | ||||||
|     ENV=os.environ, |     ENV=os.environ, | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| @ -71,7 +75,9 @@ firmware_env = distenv.AddFwProject( | |||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| # If enabled, initialize updater-related targets | # If enabled, initialize updater-related targets | ||||||
| if GetOption("fullenv"): | if GetOption("fullenv") or any( | ||||||
|  |     filter(lambda target: "updater" in target or "flash_usb" in target, BUILD_TARGETS) | ||||||
|  | ): | ||||||
|     updater_env = distenv.AddFwProject( |     updater_env = distenv.AddFwProject( | ||||||
|         base_env=coreenv, |         base_env=coreenv, | ||||||
|         fw_type="updater", |         fw_type="updater", | ||||||
| @ -79,11 +85,11 @@ if GetOption("fullenv"): | |||||||
|     ) |     ) | ||||||
| 
 | 
 | ||||||
|     # Target for self-update package |     # Target for self-update package | ||||||
|     dist_arguments = [ |     dist_basic_arguments = [ | ||||||
|         "-r", |  | ||||||
|         '"${ROOT_DIR.abspath}/assets/resources"', |  | ||||||
|         "--bundlever", |         "--bundlever", | ||||||
|         '"${UPDATE_VERSION_STRING}"', |         '"${UPDATE_VERSION_STRING}"', | ||||||
|  |     ] | ||||||
|  |     dist_radio_arguments = [ | ||||||
|         "--radio", |         "--radio", | ||||||
|         '"${ROOT_DIR.abspath}/${COPRO_STACK_BIN_DIR}/${COPRO_STACK_BIN}"', |         '"${ROOT_DIR.abspath}/${COPRO_STACK_BIN_DIR}/${COPRO_STACK_BIN}"', | ||||||
|         "--radiotype", |         "--radiotype", | ||||||
| @ -92,16 +98,34 @@ if GetOption("fullenv"): | |||||||
|         "--obdata", |         "--obdata", | ||||||
|         '"${ROOT_DIR.abspath}/${COPRO_OB_DATA}"', |         '"${ROOT_DIR.abspath}/${COPRO_OB_DATA}"', | ||||||
|     ] |     ] | ||||||
|     if distenv["UPDATE_SPLASH"]: |     dist_resource_arguments = [ | ||||||
|         dist_arguments += [ |         "-r", | ||||||
|  |         '"${ROOT_DIR.abspath}/assets/resources"', | ||||||
|  |     ] | ||||||
|  |     dist_splash_arguments = ( | ||||||
|  |         [ | ||||||
|             "--splash", |             "--splash", | ||||||
|             distenv.subst("assets/slideshow/$UPDATE_SPLASH"), |             distenv.subst("assets/slideshow/$UPDATE_SPLASH"), | ||||||
|         ] |         ] | ||||||
|  |         if distenv["UPDATE_SPLASH"] | ||||||
|  |         else [] | ||||||
|  |     ) | ||||||
| 
 | 
 | ||||||
|     selfupdate_dist = distenv.DistCommand( |     selfupdate_dist = distenv.DistCommand( | ||||||
|         "updater_package", |         "updater_package", | ||||||
|         (distenv["DIST_DEPENDS"], firmware_env["FW_RESOURCES"]), |         (distenv["DIST_DEPENDS"], firmware_env["FW_RESOURCES"]), | ||||||
|         DIST_EXTRA=dist_arguments, |         DIST_EXTRA=[ | ||||||
|  |             *dist_basic_arguments, | ||||||
|  |             *dist_radio_arguments, | ||||||
|  |             *dist_resource_arguments, | ||||||
|  |             *dist_splash_arguments, | ||||||
|  |         ], | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     selfupdate_min_dist = distenv.DistCommand( | ||||||
|  |         "updater_minpackage", | ||||||
|  |         distenv["DIST_DEPENDS"], | ||||||
|  |         DIST_EXTRA=dist_basic_arguments, | ||||||
|     ) |     ) | ||||||
| 
 | 
 | ||||||
|     # Updater debug |     # Updater debug | ||||||
| @ -121,18 +145,16 @@ if GetOption("fullenv"): | |||||||
|     ) |     ) | ||||||
| 
 | 
 | ||||||
|     # Installation over USB & CLI |     # Installation over USB & CLI | ||||||
|     usb_update_package = distenv.UsbInstall( |     usb_update_package = distenv.AddUsbFlashTarget( | ||||||
|         "#build/usbinstall.flag", |         "#build/usbinstall.flag", (firmware_env["FW_RESOURCES"], selfupdate_dist) | ||||||
|         ( |  | ||||||
|             distenv["DIST_DEPENDS"], |  | ||||||
|             firmware_env["FW_RESOURCES"], |  | ||||||
|             selfupdate_dist, |  | ||||||
|         ), |  | ||||||
|     ) |     ) | ||||||
|     if distenv["FORCE"]: |     distenv.Alias("flash_usb_full", usb_update_package) | ||||||
|         distenv.AlwaysBuild(usb_update_package) | 
 | ||||||
|     distenv.Depends(usb_update_package, selfupdate_dist) |     usb_minupdate_package = distenv.AddUsbFlashTarget( | ||||||
|     distenv.Alias("flash_usb", usb_update_package) |         "#build/minusbinstall.flag", (selfupdate_min_dist,) | ||||||
|  |     ) | ||||||
|  |     distenv.Alias("flash_usb", usb_minupdate_package) | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| # Target for copying & renaming binaries to dist folder | # Target for copying & renaming binaries to dist folder | ||||||
| basic_dist = distenv.DistCommand("fw_dist", distenv["DIST_DEPENDS"]) | basic_dist = distenv.DistCommand("fw_dist", distenv["DIST_DEPENDS"]) | ||||||
| @ -147,8 +169,9 @@ distenv.Alias("copro_dist", copro_dist) | |||||||
| 
 | 
 | ||||||
| firmware_flash = distenv.AddOpenOCDFlashTarget(firmware_env) | firmware_flash = distenv.AddOpenOCDFlashTarget(firmware_env) | ||||||
| distenv.Alias("flash", firmware_flash) | distenv.Alias("flash", firmware_flash) | ||||||
| if distenv["FORCE"]: | 
 | ||||||
|     distenv.AlwaysBuild(firmware_flash) | firmware_jflash = distenv.AddJFlashTarget(firmware_env) | ||||||
|  | distenv.Alias("jflash", firmware_jflash) | ||||||
| 
 | 
 | ||||||
| firmware_bm_flash = distenv.PhonyTarget( | firmware_bm_flash = distenv.PhonyTarget( | ||||||
|     "flash_blackmagic", |     "flash_blackmagic", | ||||||
| @ -209,6 +232,46 @@ distenv.PhonyTarget( | |||||||
|     LINT_SOURCES=firmware_env["LINT_SOURCES"], |     LINT_SOURCES=firmware_env["LINT_SOURCES"], | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | # PY_LINT_SOURCES contains recursively-built modules' SConscript files + application manifests | ||||||
|  | # Here we add additional Python files residing in repo root | ||||||
|  | firmware_env.Append( | ||||||
|  |     PY_LINT_SOURCES=[ | ||||||
|  |         # Py code folders | ||||||
|  |         "site_scons", | ||||||
|  |         "scripts", | ||||||
|  |         # Extra files | ||||||
|  |         "applications/extapps.scons", | ||||||
|  |         "SConstruct", | ||||||
|  |         "firmware.scons", | ||||||
|  |         "fbt_options.py", | ||||||
|  |     ] | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | black_commandline = "@${PYTHON3} -m black ${PY_BLACK_ARGS} ${PY_LINT_SOURCES}" | ||||||
|  | black_base_args = ["--include", '"\\.scons|\\.py|SConscript|SConstruct"'] | ||||||
|  | 
 | ||||||
|  | distenv.PhonyTarget( | ||||||
|  |     "lint_py", | ||||||
|  |     black_commandline, | ||||||
|  |     PY_BLACK_ARGS=[ | ||||||
|  |         "--check", | ||||||
|  |         "--diff", | ||||||
|  |         *black_base_args, | ||||||
|  |     ], | ||||||
|  |     PY_LINT_SOURCES=firmware_env["PY_LINT_SOURCES"], | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | distenv.PhonyTarget( | ||||||
|  |     "format_py", | ||||||
|  |     black_commandline, | ||||||
|  |     PY_BLACK_ARGS=black_base_args, | ||||||
|  |     PY_LINT_SOURCES=firmware_env["PY_LINT_SOURCES"], | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | # Start Flipper CLI via PySerial's miniterm | ||||||
|  | distenv.PhonyTarget("cli", "${PYTHON3} scripts/serial_cli.py") | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| # Find blackmagic probe | # Find blackmagic probe | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -38,6 +38,8 @@ appenv.AppendUnique( | |||||||
|         "-Wl,--no-export-dynamic", |         "-Wl,--no-export-dynamic", | ||||||
|         "-fvisibility=hidden", |         "-fvisibility=hidden", | ||||||
|         "-Wl,-e${APP_ENTRY}", |         "-Wl,-e${APP_ENTRY}", | ||||||
|  |         "-Xlinker", | ||||||
|  |         "-Map=${TARGET}.map", | ||||||
|     ], |     ], | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -5,7 +5,7 @@ | |||||||
| #include "m-string.h" | #include "m-string.h" | ||||||
| #include <toolbox/path.h> | #include <toolbox/path.h> | ||||||
| #include <flipper_format/flipper_format.h> | #include <flipper_format/flipper_format.h> | ||||||
| #include "rpc/rpc_app.h" | #include <rpc/rpc_app.h> | ||||||
| 
 | 
 | ||||||
| #define TAG "iButtonApp" | #define TAG "iButtonApp" | ||||||
| 
 | 
 | ||||||
| @ -58,7 +58,7 @@ static void ibutton_make_app_folder(iButton* ibutton) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static bool ibutton_load_key_data(iButton* ibutton, string_t key_path, bool show_dialog) { | bool ibutton_load_key_data(iButton* ibutton, string_t key_path, bool show_dialog) { | ||||||
|     FlipperFormat* file = flipper_format_file_alloc(ibutton->storage); |     FlipperFormat* file = flipper_format_file_alloc(ibutton->storage); | ||||||
|     bool result = false; |     bool result = false; | ||||||
|     string_t data; |     string_t data; | ||||||
| @ -99,33 +99,20 @@ static bool ibutton_load_key_data(iButton* ibutton, string_t key_path, bool show | |||||||
|     return result; |     return result; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static bool ibutton_rpc_command_callback(RpcAppSystemEvent event, const char* arg, void* context) { | static void ibutton_rpc_command_callback(RpcAppSystemEvent event, void* context) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     iButton* ibutton = context; |     iButton* ibutton = context; | ||||||
| 
 | 
 | ||||||
|     bool result = false; |  | ||||||
| 
 |  | ||||||
|     if(event == RpcAppEventSessionClose) { |     if(event == RpcAppEventSessionClose) { | ||||||
|         rpc_system_app_set_callback(ibutton->rpc_ctx, NULL, NULL); |         view_dispatcher_send_custom_event( | ||||||
|         ibutton->rpc_ctx = NULL; |             ibutton->view_dispatcher, iButtonCustomEventRpcSessionClose); | ||||||
|         view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventRpcExit); |  | ||||||
|         result = true; |  | ||||||
|     } else if(event == RpcAppEventAppExit) { |     } else if(event == RpcAppEventAppExit) { | ||||||
|         view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventRpcExit); |         view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventRpcExit); | ||||||
|         result = true; |  | ||||||
|     } else if(event == RpcAppEventLoadFile) { |     } else if(event == RpcAppEventLoadFile) { | ||||||
|         if(arg) { |         view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventRpcLoad); | ||||||
|             string_set_str(ibutton->file_path, arg); |     } else { | ||||||
|             if(ibutton_load_key_data(ibutton, ibutton->file_path, false)) { |         rpc_system_app_confirm(ibutton->rpc_ctx, event, false); | ||||||
|                 ibutton_worker_emulate_start(ibutton->key_worker, ibutton->key); |  | ||||||
|                 view_dispatcher_send_custom_event( |  | ||||||
|                     ibutton->view_dispatcher, iButtonCustomEventRpcLoad); |  | ||||||
|                 result = true; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     return result; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool ibutton_custom_event_callback(void* context, uint32_t event) { | bool ibutton_custom_event_callback(void* context, uint32_t event) { | ||||||
|  | |||||||
| @ -12,4 +12,5 @@ enum iButtonCustomEvent { | |||||||
| 
 | 
 | ||||||
|     iButtonCustomEventRpcLoad, |     iButtonCustomEventRpcLoad, | ||||||
|     iButtonCustomEventRpcExit, |     iButtonCustomEventRpcExit, | ||||||
|  |     iButtonCustomEventRpcSessionClose, | ||||||
| }; | }; | ||||||
|  | |||||||
| @ -78,6 +78,7 @@ typedef enum { | |||||||
| } iButtonNotificationMessage; | } iButtonNotificationMessage; | ||||||
| 
 | 
 | ||||||
| bool ibutton_file_select(iButton* ibutton); | bool ibutton_file_select(iButton* ibutton); | ||||||
|  | bool ibutton_load_key_data(iButton* ibutton, string_t key_path, bool show_dialog); | ||||||
| bool ibutton_save_key(iButton* ibutton, const char* key_name); | bool ibutton_save_key(iButton* ibutton, const char* key_name); | ||||||
| bool ibutton_delete_key(iButton* ibutton); | bool ibutton_delete_key(iButton* ibutton); | ||||||
| void ibutton_text_store_set(iButton* ibutton, const char* text, ...); | void ibutton_text_store_set(iButton* ibutton, const char* text, ...); | ||||||
|  | |||||||
| @ -1,5 +1,6 @@ | |||||||
| #include "../ibutton_i.h" | #include "../ibutton_i.h" | ||||||
| #include <toolbox/path.h> | #include <toolbox/path.h> | ||||||
|  | #include <rpc/rpc_app.h> | ||||||
| 
 | 
 | ||||||
| void ibutton_scene_rpc_on_enter(void* context) { | void ibutton_scene_rpc_on_enter(void* context) { | ||||||
|     iButton* ibutton = context; |     iButton* ibutton = context; | ||||||
| @ -26,23 +27,40 @@ bool ibutton_scene_rpc_on_event(void* context, SceneManagerEvent event) { | |||||||
|     if(event.type == SceneManagerEventTypeCustom) { |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|         consumed = true; |         consumed = true; | ||||||
|         if(event.event == iButtonCustomEventRpcLoad) { |         if(event.event == iButtonCustomEventRpcLoad) { | ||||||
|             string_t key_name; |             const char* arg = rpc_system_app_get_data(ibutton->rpc_ctx); | ||||||
|             string_init(key_name); |             bool result = false; | ||||||
|             if(string_end_with_str_p(ibutton->file_path, IBUTTON_APP_EXTENSION)) { |             if(arg) { | ||||||
|                 path_extract_filename(ibutton->file_path, key_name, true); |                 string_set_str(ibutton->file_path, arg); | ||||||
|  |                 if(ibutton_load_key_data(ibutton, ibutton->file_path, false)) { | ||||||
|  |                     ibutton_worker_emulate_start(ibutton->key_worker, ibutton->key); | ||||||
|  |                     string_t key_name; | ||||||
|  |                     string_init(key_name); | ||||||
|  |                     if(string_end_with_str_p(ibutton->file_path, IBUTTON_APP_EXTENSION)) { | ||||||
|  |                         path_extract_filename(ibutton->file_path, key_name, true); | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     if(!string_empty_p(key_name)) { | ||||||
|  |                         ibutton_text_store_set( | ||||||
|  |                             ibutton, "emulating\n%s", string_get_cstr(key_name)); | ||||||
|  |                     } else { | ||||||
|  |                         ibutton_text_store_set(ibutton, "emulating"); | ||||||
|  |                     } | ||||||
|  |                     popup_set_text(popup, ibutton->text_store, 82, 32, AlignCenter, AlignTop); | ||||||
|  | 
 | ||||||
|  |                     ibutton_notification_message(ibutton, iButtonNotificationMessageEmulateStart); | ||||||
|  | 
 | ||||||
|  |                     string_clear(key_name); | ||||||
|  |                     result = true; | ||||||
|  |                 } | ||||||
|             } |             } | ||||||
| 
 |             rpc_system_app_confirm(ibutton->rpc_ctx, RpcAppEventLoadFile, result); | ||||||
|             if(!string_empty_p(key_name)) { |  | ||||||
|                 ibutton_text_store_set(ibutton, "emulating\n%s", string_get_cstr(key_name)); |  | ||||||
|             } else { |  | ||||||
|                 ibutton_text_store_set(ibutton, "emulating"); |  | ||||||
|             } |  | ||||||
|             popup_set_text(popup, ibutton->text_store, 82, 32, AlignCenter, AlignTop); |  | ||||||
| 
 |  | ||||||
|             ibutton_notification_message(ibutton, iButtonNotificationMessageEmulateStart); |  | ||||||
| 
 |  | ||||||
|             string_clear(key_name); |  | ||||||
|         } else if(event.event == iButtonCustomEventRpcExit) { |         } else if(event.event == iButtonCustomEventRpcExit) { | ||||||
|  |             rpc_system_app_confirm(ibutton->rpc_ctx, RpcAppEventAppExit, true); | ||||||
|  |             ibutton_notification_message(ibutton, iButtonNotificationMessageBlinkStop); | ||||||
|  |             view_dispatcher_stop(ibutton->view_dispatcher); | ||||||
|  |         } else if(event.event == iButtonCustomEventRpcSessionClose) { | ||||||
|  |             rpc_system_app_set_callback(ibutton->rpc_ctx, NULL, NULL); | ||||||
|  |             ibutton->rpc_ctx = NULL; | ||||||
|             ibutton_notification_message(ibutton, iButtonNotificationMessageBlinkStop); |             ibutton_notification_message(ibutton, iButtonNotificationMessageBlinkStop); | ||||||
|             view_dispatcher_stop(ibutton->view_dispatcher); |             view_dispatcher_stop(ibutton->view_dispatcher); | ||||||
|         } |         } | ||||||
|  | |||||||
| @ -36,52 +36,29 @@ static void infrared_tick_event_callback(void* context) { | |||||||
|     scene_manager_handle_tick_event(infrared->scene_manager); |     scene_manager_handle_tick_event(infrared->scene_manager); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static bool | static void infrared_rpc_command_callback(RpcAppSystemEvent event, void* context) { | ||||||
|     infrared_rpc_command_callback(RpcAppSystemEvent event, const char* arg, void* context) { |  | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     Infrared* infrared = context; |     Infrared* infrared = context; | ||||||
| 
 |     furi_assert(infrared->rpc_ctx); | ||||||
|     if(!infrared->rpc_ctx) { |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     bool result = false; |  | ||||||
| 
 | 
 | ||||||
|     if(event == RpcAppEventSessionClose) { |     if(event == RpcAppEventSessionClose) { | ||||||
|         rpc_system_app_set_callback(infrared->rpc_ctx, NULL, NULL); |  | ||||||
|         infrared->rpc_ctx = NULL; |  | ||||||
|         view_dispatcher_send_custom_event( |         view_dispatcher_send_custom_event( | ||||||
|             infrared->view_dispatcher, InfraredCustomEventTypeBackPressed); |             infrared->view_dispatcher, InfraredCustomEventTypeRpcSessionClose); | ||||||
|         result = true; |  | ||||||
|     } else if(event == RpcAppEventAppExit) { |     } else if(event == RpcAppEventAppExit) { | ||||||
|         view_dispatcher_send_custom_event( |         view_dispatcher_send_custom_event( | ||||||
|             infrared->view_dispatcher, InfraredCustomEventTypeBackPressed); |             infrared->view_dispatcher, InfraredCustomEventTypeRpcExit); | ||||||
|         result = true; |  | ||||||
|     } else if(event == RpcAppEventLoadFile) { |     } else if(event == RpcAppEventLoadFile) { | ||||||
|         if(arg) { |         view_dispatcher_send_custom_event( | ||||||
|             string_set_str(infrared->file_path, arg); |             infrared->view_dispatcher, InfraredCustomEventTypeRpcLoad); | ||||||
|             result = infrared_remote_load(infrared->remote, infrared->file_path); |  | ||||||
|             infrared_worker_tx_set_get_signal_callback( |  | ||||||
|                 infrared->worker, infrared_worker_tx_get_signal_steady_callback, infrared); |  | ||||||
|             infrared_worker_tx_set_signal_sent_callback( |  | ||||||
|                 infrared->worker, infrared_signal_sent_callback, infrared); |  | ||||||
|             view_dispatcher_send_custom_event( |  | ||||||
|                 infrared->view_dispatcher, InfraredCustomEventTypeRpcLoaded); |  | ||||||
|         } |  | ||||||
|     } else if(event == RpcAppEventButtonPress) { |     } else if(event == RpcAppEventButtonPress) { | ||||||
|         if(arg) { |         view_dispatcher_send_custom_event( | ||||||
|             size_t button_index = 0; |             infrared->view_dispatcher, InfraredCustomEventTypeRpcButtonPress); | ||||||
|             if(infrared_remote_find_button_by_name(infrared->remote, arg, &button_index)) { |  | ||||||
|                 infrared_tx_start_button_index(infrared, button_index); |  | ||||||
|                 result = true; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } else if(event == RpcAppEventButtonRelease) { |     } else if(event == RpcAppEventButtonRelease) { | ||||||
|         infrared_tx_stop(infrared); |         view_dispatcher_send_custom_event( | ||||||
|         result = true; |             infrared->view_dispatcher, InfraredCustomEventTypeRpcButtonRelease); | ||||||
|  |     } else { | ||||||
|  |         rpc_system_app_confirm(infrared->rpc_ctx, event, false); | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     return result; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void infrared_find_vacant_remote_name(string_t name, const char* path) { | static void infrared_find_vacant_remote_name(string_t name, const char* path) { | ||||||
|  | |||||||
| @ -14,7 +14,12 @@ enum InfraredCustomEventType { | |||||||
|     InfraredCustomEventTypePopupClosed, |     InfraredCustomEventTypePopupClosed, | ||||||
|     InfraredCustomEventTypeButtonSelected, |     InfraredCustomEventTypeButtonSelected, | ||||||
|     InfraredCustomEventTypeBackPressed, |     InfraredCustomEventTypeBackPressed, | ||||||
|     InfraredCustomEventTypeRpcLoaded, | 
 | ||||||
|  |     InfraredCustomEventTypeRpcLoad, | ||||||
|  |     InfraredCustomEventTypeRpcExit, | ||||||
|  |     InfraredCustomEventTypeRpcButtonPress, | ||||||
|  |     InfraredCustomEventTypeRpcButtonRelease, | ||||||
|  |     InfraredCustomEventTypeRpcSessionClose, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| #pragma pack(push, 1) | #pragma pack(push, 1) | ||||||
|  | |||||||
| @ -28,12 +28,45 @@ bool infrared_scene_rpc_on_event(void* context, SceneManagerEvent event) { | |||||||
|             view_dispatcher_stop(infrared->view_dispatcher); |             view_dispatcher_stop(infrared->view_dispatcher); | ||||||
|         } else if(event.event == InfraredCustomEventTypePopupClosed) { |         } else if(event.event == InfraredCustomEventTypePopupClosed) { | ||||||
|             view_dispatcher_stop(infrared->view_dispatcher); |             view_dispatcher_stop(infrared->view_dispatcher); | ||||||
|         } else if(event.event == InfraredCustomEventTypeRpcLoaded) { |         } else if(event.event == InfraredCustomEventTypeRpcLoad) { | ||||||
|  |             bool result = false; | ||||||
|  |             const char* arg = rpc_system_app_get_data(infrared->rpc_ctx); | ||||||
|  |             if(arg) { | ||||||
|  |                 string_set_str(infrared->file_path, arg); | ||||||
|  |                 result = infrared_remote_load(infrared->remote, infrared->file_path); | ||||||
|  |                 infrared_worker_tx_set_get_signal_callback( | ||||||
|  |                     infrared->worker, infrared_worker_tx_get_signal_steady_callback, infrared); | ||||||
|  |                 infrared_worker_tx_set_signal_sent_callback( | ||||||
|  |                     infrared->worker, infrared_signal_sent_callback, infrared); | ||||||
|  |             } | ||||||
|             const char* remote_name = infrared_remote_get_name(infrared->remote); |             const char* remote_name = infrared_remote_get_name(infrared->remote); | ||||||
| 
 | 
 | ||||||
|             infrared_text_store_set(infrared, 0, "loaded\n%s", remote_name); |             infrared_text_store_set(infrared, 0, "loaded\n%s", remote_name); | ||||||
|             popup_set_text( |             popup_set_text( | ||||||
|                 infrared->popup, infrared->text_store[0], 82, 32, AlignCenter, AlignTop); |                 infrared->popup, infrared->text_store[0], 82, 32, AlignCenter, AlignTop); | ||||||
|  | 
 | ||||||
|  |             rpc_system_app_confirm(infrared->rpc_ctx, RpcAppEventLoadFile, result); | ||||||
|  |         } else if(event.event == InfraredCustomEventTypeRpcButtonPress) { | ||||||
|  |             bool result = false; | ||||||
|  |             const char* arg = rpc_system_app_get_data(infrared->rpc_ctx); | ||||||
|  |             if(arg) { | ||||||
|  |                 size_t button_index = 0; | ||||||
|  |                 if(infrared_remote_find_button_by_name(infrared->remote, arg, &button_index)) { | ||||||
|  |                     infrared_tx_start_button_index(infrared, button_index); | ||||||
|  |                     result = true; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             rpc_system_app_confirm(infrared->rpc_ctx, RpcAppEventButtonRelease, result); | ||||||
|  |         } else if(event.event == InfraredCustomEventTypeRpcButtonRelease) { | ||||||
|  |             infrared_tx_stop(infrared); | ||||||
|  |             rpc_system_app_confirm(infrared->rpc_ctx, RpcAppEventButtonRelease, true); | ||||||
|  |         } else if(event.event == InfraredCustomEventTypeRpcExit) { | ||||||
|  |             view_dispatcher_stop(infrared->view_dispatcher); | ||||||
|  |             rpc_system_app_confirm(infrared->rpc_ctx, RpcAppEventAppExit, true); | ||||||
|  |         } else if(event.event == InfraredCustomEventTypeRpcSessionClose) { | ||||||
|  |             rpc_system_app_set_callback(infrared->rpc_ctx, NULL, NULL); | ||||||
|  |             infrared->rpc_ctx = NULL; | ||||||
|  |             view_dispatcher_stop(infrared->view_dispatcher); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     return consumed; |     return consumed; | ||||||
|  | |||||||
| @ -25,7 +25,7 @@ | |||||||
| #include <toolbox/path.h> | #include <toolbox/path.h> | ||||||
| #include <flipper_format/flipper_format.h> | #include <flipper_format/flipper_format.h> | ||||||
| 
 | 
 | ||||||
| #include "rpc/rpc_app.h" | #include <rpc/rpc_app.h> | ||||||
| 
 | 
 | ||||||
| const char* LfRfidApp::app_folder = ANY_PATH("lfrfid"); | const char* LfRfidApp::app_folder = ANY_PATH("lfrfid"); | ||||||
| const char* LfRfidApp::app_extension = ".rfid"; | const char* LfRfidApp::app_extension = ".rfid"; | ||||||
| @ -48,38 +48,25 @@ LfRfidApp::~LfRfidApp() { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static bool rpc_command_callback(RpcAppSystemEvent event, const char* arg, void* context) { | static void rpc_command_callback(RpcAppSystemEvent rpc_event, void* context) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     LfRfidApp* app = static_cast<LfRfidApp*>(context); |     LfRfidApp* app = static_cast<LfRfidApp*>(context); | ||||||
| 
 | 
 | ||||||
|     bool result = false; |     if(rpc_event == RpcAppEventSessionClose) { | ||||||
| 
 |         LfRfidApp::Event event; | ||||||
|     if(event == RpcAppEventSessionClose) { |         event.type = LfRfidApp::EventType::RpcSessionClose; | ||||||
|         rpc_system_app_set_callback(app->rpc_ctx, NULL, NULL); |         app->view_controller.send_event(&event); | ||||||
|         app->rpc_ctx = NULL; |     } else if(rpc_event == RpcAppEventAppExit) { | ||||||
|         LfRfidApp::Event event; |         LfRfidApp::Event event; | ||||||
|         event.type = LfRfidApp::EventType::Exit; |         event.type = LfRfidApp::EventType::Exit; | ||||||
|         app->view_controller.send_event(&event); |         app->view_controller.send_event(&event); | ||||||
|         result = true; |     } else if(rpc_event == RpcAppEventLoadFile) { | ||||||
|     } else if(event == RpcAppEventAppExit) { |  | ||||||
|         LfRfidApp::Event event; |         LfRfidApp::Event event; | ||||||
|         event.type = LfRfidApp::EventType::Exit; |         event.type = LfRfidApp::EventType::RpcLoadFile; | ||||||
|         app->view_controller.send_event(&event); |         app->view_controller.send_event(&event); | ||||||
|         result = true; |     } else { | ||||||
|     } else if(event == RpcAppEventLoadFile) { |         rpc_system_app_confirm(app->rpc_ctx, rpc_event, false); | ||||||
|         if(arg) { |  | ||||||
|             string_set_str(app->file_path, arg); |  | ||||||
|             if(app->load_key_data(app->file_path, &(app->worker.key), false)) { |  | ||||||
|                 LfRfidApp::Event event; |  | ||||||
|                 event.type = LfRfidApp::EventType::EmulateStart; |  | ||||||
|                 app->view_controller.send_event(&event); |  | ||||||
|                 app->worker.start_emulate(); |  | ||||||
|                 result = true; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     return result; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void LfRfidApp::run(void* _args) { | void LfRfidApp::run(void* _args) { | ||||||
|  | |||||||
| @ -33,6 +33,8 @@ public: | |||||||
|         Retry, |         Retry, | ||||||
|         Exit, |         Exit, | ||||||
|         EmulateStart, |         EmulateStart, | ||||||
|  |         RpcLoadFile, | ||||||
|  |         RpcSessionClose, | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     enum class SceneType : uint8_t { |     enum class SceneType : uint8_t { | ||||||
|  | |||||||
| @ -1,6 +1,7 @@ | |||||||
| #include "lfrfid_app_scene_rpc.h" | #include "lfrfid_app_scene_rpc.h" | ||||||
| #include <core/common_defines.h> | #include <core/common_defines.h> | ||||||
| #include <dolphin/dolphin.h> | #include <dolphin/dolphin.h> | ||||||
|  | #include <rpc/rpc_app.h> | ||||||
| 
 | 
 | ||||||
| static const NotificationSequence sequence_blink_start_magenta = { | static const NotificationSequence sequence_blink_start_magenta = { | ||||||
|     &message_blink_start_10, |     &message_blink_start_10, | ||||||
| @ -36,6 +37,16 @@ bool LfRfidAppSceneRpc::on_event(LfRfidApp* app, LfRfidApp::Event* event) { | |||||||
|         LfRfidApp::Event view_event; |         LfRfidApp::Event view_event; | ||||||
|         view_event.type = LfRfidApp::EventType::Back; |         view_event.type = LfRfidApp::EventType::Back; | ||||||
|         app->view_controller.send_event(&view_event); |         app->view_controller.send_event(&view_event); | ||||||
|  |         rpc_system_app_confirm(app->rpc_ctx, RpcAppEventAppExit, true); | ||||||
|  |     } else if(event->type == LfRfidApp::EventType::RpcSessionClose) { | ||||||
|  |         // Detach RPC
 | ||||||
|  |         rpc_system_app_set_callback(app->rpc_ctx, NULL, NULL); | ||||||
|  |         app->rpc_ctx = NULL; | ||||||
|  | 
 | ||||||
|  |         consumed = true; | ||||||
|  |         LfRfidApp::Event view_event; | ||||||
|  |         view_event.type = LfRfidApp::EventType::Back; | ||||||
|  |         app->view_controller.send_event(&view_event); | ||||||
|     } else if(event->type == LfRfidApp::EventType::EmulateStart) { |     } else if(event->type == LfRfidApp::EventType::EmulateStart) { | ||||||
|         auto popup = app->view_controller.get<PopupVM>(); |         auto popup = app->view_controller.get<PopupVM>(); | ||||||
|         consumed = true; |         consumed = true; | ||||||
| @ -45,7 +56,22 @@ bool LfRfidAppSceneRpc::on_event(LfRfidApp* app, LfRfidApp::Event* event) { | |||||||
|         popup->set_text(app->text_store.text, 89, 43, AlignCenter, AlignTop); |         popup->set_text(app->text_store.text, 89, 43, AlignCenter, AlignTop); | ||||||
| 
 | 
 | ||||||
|         notification_message(app->notification, &sequence_blink_start_magenta); |         notification_message(app->notification, &sequence_blink_start_magenta); | ||||||
|  |     } else if(event->type == LfRfidApp::EventType::RpcLoadFile) { | ||||||
|  |         const char* arg = rpc_system_app_get_data(app->rpc_ctx); | ||||||
|  |         bool result = false; | ||||||
|  |         if(arg) { | ||||||
|  |             string_set_str(app->file_path, arg); | ||||||
|  |             if(app->load_key_data(app->file_path, &(app->worker.key), false)) { | ||||||
|  |                 LfRfidApp::Event event; | ||||||
|  |                 event.type = LfRfidApp::EventType::EmulateStart; | ||||||
|  |                 app->view_controller.send_event(&event); | ||||||
|  |                 app->worker.start_emulate(); | ||||||
|  |                 result = true; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         rpc_system_app_confirm(app->rpc_ctx, RpcAppEventLoadFile, result); | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|     return consumed; |     return consumed; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -11,4 +11,5 @@ enum NfcCustomEvent { | |||||||
|     NfcCustomEventDictAttackDone, |     NfcCustomEventDictAttackDone, | ||||||
|     NfcCustomEventDictAttackSkip, |     NfcCustomEventDictAttackSkip, | ||||||
|     NfcCustomEventRpcLoad, |     NfcCustomEventRpcLoad, | ||||||
|  |     NfcCustomEventRpcSessionClose, | ||||||
| }; | }; | ||||||
|  | |||||||
| @ -13,78 +13,21 @@ bool nfc_back_event_callback(void* context) { | |||||||
|     return scene_manager_handle_back_event(nfc->scene_manager); |     return scene_manager_handle_back_event(nfc->scene_manager); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void nfc_rpc_exit_callback(Nfc* nfc) { | static void nfc_rpc_command_callback(RpcAppSystemEvent event, void* context) { | ||||||
|     if(nfc->rpc_state == NfcRpcStateEmulating) { |  | ||||||
|         // Stop worker
 |  | ||||||
|         nfc_worker_stop(nfc->worker); |  | ||||||
|     } else if(nfc->rpc_state == NfcRpcStateEmulated) { |  | ||||||
|         // Stop worker
 |  | ||||||
|         nfc_worker_stop(nfc->worker); |  | ||||||
|         // Save data in shadow file
 |  | ||||||
|         nfc_device_save_shadow(nfc->dev, nfc->dev->dev_name); |  | ||||||
|     } |  | ||||||
|     if(nfc->rpc_ctx) { |  | ||||||
|         rpc_system_app_set_callback(nfc->rpc_ctx, NULL, NULL); |  | ||||||
|         rpc_system_app_send_exited(nfc->rpc_ctx); |  | ||||||
|         nfc->rpc_ctx = NULL; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static bool nfc_rpc_emulate_callback(NfcWorkerEvent event, void* context) { |  | ||||||
|     UNUSED(event); |  | ||||||
|     Nfc* nfc = context; |  | ||||||
| 
 |  | ||||||
|     nfc->rpc_state = NfcRpcStateEmulated; |  | ||||||
|     return true; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static bool nfc_rpc_command_callback(RpcAppSystemEvent event, const char* arg, void* context) { |  | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     Nfc* nfc = context; |     Nfc* nfc = context; | ||||||
| 
 | 
 | ||||||
|     if(!nfc->rpc_ctx) { |     furi_assert(nfc->rpc_ctx); | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     bool result = false; |  | ||||||
| 
 | 
 | ||||||
|     if(event == RpcAppEventSessionClose) { |     if(event == RpcAppEventSessionClose) { | ||||||
|         rpc_system_app_set_callback(nfc->rpc_ctx, NULL, NULL); |         view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventRpcSessionClose); | ||||||
|         nfc->rpc_ctx = NULL; |  | ||||||
|         view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); |  | ||||||
|         result = true; |  | ||||||
|     } else if(event == RpcAppEventAppExit) { |     } else if(event == RpcAppEventAppExit) { | ||||||
|         view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); |         view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); | ||||||
|         result = true; |  | ||||||
|     } else if(event == RpcAppEventLoadFile) { |     } else if(event == RpcAppEventLoadFile) { | ||||||
|         if((arg) && (nfc->rpc_state == NfcRpcStateIdle)) { |         view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventRpcLoad); | ||||||
|             if(nfc_device_load(nfc->dev, arg, false)) { |     } else { | ||||||
|                 if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) { |         rpc_system_app_confirm(nfc->rpc_ctx, event, false); | ||||||
|                     nfc_worker_start( |  | ||||||
|                         nfc->worker, |  | ||||||
|                         NfcWorkerStateMfUltralightEmulate, |  | ||||||
|                         &nfc->dev->dev_data, |  | ||||||
|                         nfc_rpc_emulate_callback, |  | ||||||
|                         nfc); |  | ||||||
|                 } else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { |  | ||||||
|                     nfc_worker_start( |  | ||||||
|                         nfc->worker, |  | ||||||
|                         NfcWorkerStateMfClassicEmulate, |  | ||||||
|                         &nfc->dev->dev_data, |  | ||||||
|                         nfc_rpc_emulate_callback, |  | ||||||
|                         nfc); |  | ||||||
|                 } else { |  | ||||||
|                     nfc_worker_start( |  | ||||||
|                         nfc->worker, NfcWorkerStateUidEmulate, &nfc->dev->dev_data, NULL, nfc); |  | ||||||
|                 } |  | ||||||
|                 nfc->rpc_state = NfcRpcStateEmulating; |  | ||||||
|                 view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventRpcLoad); |  | ||||||
|                 result = true; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     return result; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| Nfc* nfc_alloc() { | Nfc* nfc_alloc() { | ||||||
| @ -163,6 +106,21 @@ Nfc* nfc_alloc() { | |||||||
| void nfc_free(Nfc* nfc) { | void nfc_free(Nfc* nfc) { | ||||||
|     furi_assert(nfc); |     furi_assert(nfc); | ||||||
| 
 | 
 | ||||||
|  |     if(nfc->rpc_state == NfcRpcStateEmulating) { | ||||||
|  |         // Stop worker
 | ||||||
|  |         nfc_worker_stop(nfc->worker); | ||||||
|  |     } else if(nfc->rpc_state == NfcRpcStateEmulated) { | ||||||
|  |         // Stop worker
 | ||||||
|  |         nfc_worker_stop(nfc->worker); | ||||||
|  |         // Save data in shadow file
 | ||||||
|  |         nfc_device_save_shadow(nfc->dev, nfc->dev->dev_name); | ||||||
|  |     } | ||||||
|  |     if(nfc->rpc_ctx) { | ||||||
|  |         rpc_system_app_send_exited(nfc->rpc_ctx); | ||||||
|  |         rpc_system_app_set_callback(nfc->rpc_ctx, NULL, NULL); | ||||||
|  |         nfc->rpc_ctx = NULL; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     // Nfc device
 |     // Nfc device
 | ||||||
|     nfc_device_free(nfc->dev); |     nfc_device_free(nfc->dev); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -102,5 +102,3 @@ void nfc_blink_start(Nfc* nfc); | |||||||
| void nfc_blink_stop(Nfc* nfc); | void nfc_blink_stop(Nfc* nfc); | ||||||
| 
 | 
 | ||||||
| void nfc_show_loading_popup(void* context, bool show); | void nfc_show_loading_popup(void* context, bool show); | ||||||
| 
 |  | ||||||
| void nfc_rpc_exit_callback(Nfc* nfc); |  | ||||||
|  | |||||||
| @ -14,6 +14,14 @@ void nfc_scene_rpc_on_enter(void* context) { | |||||||
|     notification_message(nfc->notifications, &sequence_display_backlight_on); |     notification_message(nfc->notifications, &sequence_display_backlight_on); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static bool nfc_scene_rpc_emulate_callback(NfcWorkerEvent event, void* context) { | ||||||
|  |     UNUSED(event); | ||||||
|  |     Nfc* nfc = context; | ||||||
|  | 
 | ||||||
|  |     nfc->rpc_state = NfcRpcStateEmulated; | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| bool nfc_scene_rpc_on_event(void* context, SceneManagerEvent event) { | bool nfc_scene_rpc_on_event(void* context, SceneManagerEvent event) { | ||||||
|     Nfc* nfc = context; |     Nfc* nfc = context; | ||||||
|     Popup* popup = nfc->popup; |     Popup* popup = nfc->popup; | ||||||
| @ -22,13 +30,47 @@ bool nfc_scene_rpc_on_event(void* context, SceneManagerEvent event) { | |||||||
|     if(event.type == SceneManagerEventTypeCustom) { |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|         consumed = true; |         consumed = true; | ||||||
|         if(event.event == NfcCustomEventViewExit) { |         if(event.event == NfcCustomEventViewExit) { | ||||||
|  |             rpc_system_app_confirm(nfc->rpc_ctx, RpcAppEventAppExit, true); | ||||||
|  |             view_dispatcher_stop(nfc->view_dispatcher); | ||||||
|  |             nfc_blink_stop(nfc); | ||||||
|  |         } else if(event.event == NfcCustomEventRpcSessionClose) { | ||||||
|  |             rpc_system_app_set_callback(nfc->rpc_ctx, NULL, NULL); | ||||||
|  |             nfc->rpc_ctx = NULL; | ||||||
|             view_dispatcher_stop(nfc->view_dispatcher); |             view_dispatcher_stop(nfc->view_dispatcher); | ||||||
|             nfc_blink_stop(nfc); |             nfc_blink_stop(nfc); | ||||||
|         } else if(event.event == NfcCustomEventRpcLoad) { |         } else if(event.event == NfcCustomEventRpcLoad) { | ||||||
|             nfc_blink_start(nfc); |             bool result = false; | ||||||
|  |             const char* arg = rpc_system_app_get_data(nfc->rpc_ctx); | ||||||
|  |             if((arg) && (nfc->rpc_state == NfcRpcStateIdle)) { | ||||||
|  |                 if(nfc_device_load(nfc->dev, arg, false)) { | ||||||
|  |                     if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) { | ||||||
|  |                         nfc_worker_start( | ||||||
|  |                             nfc->worker, | ||||||
|  |                             NfcWorkerStateMfUltralightEmulate, | ||||||
|  |                             &nfc->dev->dev_data, | ||||||
|  |                             nfc_scene_rpc_emulate_callback, | ||||||
|  |                             nfc); | ||||||
|  |                     } else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { | ||||||
|  |                         nfc_worker_start( | ||||||
|  |                             nfc->worker, | ||||||
|  |                             NfcWorkerStateMfClassicEmulate, | ||||||
|  |                             &nfc->dev->dev_data, | ||||||
|  |                             nfc_scene_rpc_emulate_callback, | ||||||
|  |                             nfc); | ||||||
|  |                     } else { | ||||||
|  |                         nfc_worker_start( | ||||||
|  |                             nfc->worker, NfcWorkerStateUidEmulate, &nfc->dev->dev_data, NULL, nfc); | ||||||
|  |                     } | ||||||
|  |                     nfc->rpc_state = NfcRpcStateEmulating; | ||||||
|  |                     result = true; | ||||||
| 
 | 
 | ||||||
|             nfc_text_store_set(nfc, "emulating\n%s", nfc->dev->dev_name); |                     nfc_blink_start(nfc); | ||||||
|             popup_set_text(popup, nfc->text_store, 82, 32, AlignCenter, AlignTop); |                     nfc_text_store_set(nfc, "emulating\n%s", nfc->dev->dev_name); | ||||||
|  |                     popup_set_text(popup, nfc->text_store, 82, 32, AlignCenter, AlignTop); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             rpc_system_app_confirm(nfc->rpc_ctx, RpcAppEventLoadFile, result); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     return consumed; |     return consumed; | ||||||
| @ -38,7 +80,6 @@ void nfc_scene_rpc_on_exit(void* context) { | |||||||
|     Nfc* nfc = context; |     Nfc* nfc = context; | ||||||
|     Popup* popup = nfc->popup; |     Popup* popup = nfc->popup; | ||||||
| 
 | 
 | ||||||
|     nfc_rpc_exit_callback(nfc); |  | ||||||
|     nfc_blink_stop(nfc); |     nfc_blink_stop(nfc); | ||||||
| 
 | 
 | ||||||
|     popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom); |     popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom); | ||||||
|  | |||||||
| @ -6,24 +6,18 @@ | |||||||
| #include "rpc_app.h" | #include "rpc_app.h" | ||||||
| 
 | 
 | ||||||
| #define TAG "RpcSystemApp" | #define TAG "RpcSystemApp" | ||||||
| #define APP_BUTTON_TIMEOUT 1000 |  | ||||||
| 
 | 
 | ||||||
| struct RpcAppSystem { | struct RpcAppSystem { | ||||||
|     RpcSession* session; |     RpcSession* session; | ||||||
|     RpcAppSystemCallback app_callback; |     RpcAppSystemCallback app_callback; | ||||||
|     void* app_context; |     void* app_context; | ||||||
|     PB_Main* state_msg; |     PB_Main* state_msg; | ||||||
|     FuriTimer* timer; | 
 | ||||||
|  |     uint32_t last_id; | ||||||
|  |     char* last_data; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static void rpc_system_app_timer_callback(void* context) { | #define RPC_SYSTEM_APP_TEMP_ARGS_SIZE 16 | ||||||
|     furi_assert(context); |  | ||||||
|     RpcAppSystem* rpc_app = context; |  | ||||||
| 
 |  | ||||||
|     if(rpc_app->app_callback) { |  | ||||||
|         rpc_app->app_callback(RpcAppEventButtonRelease, NULL, rpc_app->app_context); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| static void rpc_system_app_start_process(const PB_Main* request, void* context) { | static void rpc_system_app_start_process(const PB_Main* request, void* context) { | ||||||
|     furi_assert(request); |     furi_assert(request); | ||||||
| @ -33,9 +27,12 @@ static void rpc_system_app_start_process(const PB_Main* request, void* context) | |||||||
|     RpcAppSystem* rpc_app = context; |     RpcAppSystem* rpc_app = context; | ||||||
|     RpcSession* session = rpc_app->session; |     RpcSession* session = rpc_app->session; | ||||||
|     furi_assert(session); |     furi_assert(session); | ||||||
|     char args_temp[16]; |     char args_temp[RPC_SYSTEM_APP_TEMP_ARGS_SIZE]; | ||||||
| 
 | 
 | ||||||
|     FURI_LOG_D(TAG, "Start"); |     furi_assert(!rpc_app->last_id); | ||||||
|  |     furi_assert(!rpc_app->last_data); | ||||||
|  | 
 | ||||||
|  |     FURI_LOG_D(TAG, "StartProcess: id %d", request->command_id); | ||||||
| 
 | 
 | ||||||
|     PB_CommandStatus result = PB_CommandStatus_ERROR_APP_CANT_START; |     PB_CommandStatus result = PB_CommandStatus_ERROR_APP_CANT_START; | ||||||
| 
 | 
 | ||||||
| @ -43,9 +40,9 @@ static void rpc_system_app_start_process(const PB_Main* request, void* context) | |||||||
|     const char* app_name = request->content.app_start_request.name; |     const char* app_name = request->content.app_start_request.name; | ||||||
|     if(app_name) { |     if(app_name) { | ||||||
|         const char* app_args = request->content.app_start_request.args; |         const char* app_args = request->content.app_start_request.args; | ||||||
|         if(strcmp(app_args, "RPC") == 0) { |         if(app_args && strcmp(app_args, "RPC") == 0) { | ||||||
|             // If app is being started in RPC mode - pass RPC context via args string
 |             // If app is being started in RPC mode - pass RPC context via args string
 | ||||||
|             snprintf(args_temp, 16, "RPC %08lX", (uint32_t)rpc_app); |             snprintf(args_temp, RPC_SYSTEM_APP_TEMP_ARGS_SIZE, "RPC %08lX", (uint32_t)rpc_app); | ||||||
|             app_args = args_temp; |             app_args = args_temp; | ||||||
|         } |         } | ||||||
|         LoaderStatus status = loader_start(loader, app_name, app_args); |         LoaderStatus status = loader_start(loader, app_name, app_args); | ||||||
| @ -58,7 +55,7 @@ static void rpc_system_app_start_process(const PB_Main* request, void* context) | |||||||
|         } else if(status == LoaderStatusOk) { |         } else if(status == LoaderStatusOk) { | ||||||
|             result = PB_CommandStatus_OK; |             result = PB_CommandStatus_OK; | ||||||
|         } else { |         } else { | ||||||
|             furi_assert(0); |             furi_crash("Programming Error"); | ||||||
|         } |         } | ||||||
|     } else { |     } else { | ||||||
|         result = PB_CommandStatus_ERROR_INVALID_PARAMETERS; |         result = PB_CommandStatus_ERROR_INVALID_PARAMETERS; | ||||||
| @ -66,6 +63,7 @@ static void rpc_system_app_start_process(const PB_Main* request, void* context) | |||||||
| 
 | 
 | ||||||
|     furi_record_close(RECORD_LOADER); |     furi_record_close(RECORD_LOADER); | ||||||
| 
 | 
 | ||||||
|  |     FURI_LOG_D(TAG, "StartProcess: response id %d, result %d", request->command_id, result); | ||||||
|     rpc_send_and_release_empty(session, request->command_id, result); |     rpc_send_and_release_empty(session, request->command_id, result); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -93,6 +91,7 @@ static void rpc_system_app_lock_status_process(const PB_Main* request, void* con | |||||||
| 
 | 
 | ||||||
|     furi_record_close(RECORD_LOADER); |     furi_record_close(RECORD_LOADER); | ||||||
| 
 | 
 | ||||||
|  |     FURI_LOG_D(TAG, "LockStatus: response"); | ||||||
|     rpc_send_and_release(session, &response); |     rpc_send_and_release(session, &response); | ||||||
|     pb_release(&PB_Main_msg, &response); |     pb_release(&PB_Main_msg, &response); | ||||||
| } | } | ||||||
| @ -109,17 +108,17 @@ static void rpc_system_app_exit_request(const PB_Main* request, void* context) { | |||||||
|     PB_CommandStatus status; |     PB_CommandStatus status; | ||||||
| 
 | 
 | ||||||
|     if(rpc_app->app_callback) { |     if(rpc_app->app_callback) { | ||||||
|         if(rpc_app->app_callback(RpcAppEventAppExit, NULL, rpc_app->app_context)) { |         FURI_LOG_D(TAG, "ExitRequest: id %d", request->command_id); | ||||||
|             status = PB_CommandStatus_OK; |         furi_assert(!rpc_app->last_id); | ||||||
|             furi_timer_stop(rpc_app->timer); |         furi_assert(!rpc_app->last_data); | ||||||
|         } else { |         rpc_app->last_id = request->command_id; | ||||||
|             status = PB_CommandStatus_ERROR_APP_CMD_ERROR; |         rpc_app->app_callback(RpcAppEventAppExit, rpc_app->app_context); | ||||||
|         } |  | ||||||
|     } else { |     } else { | ||||||
|         status = PB_CommandStatus_ERROR_APP_NOT_RUNNING; |         status = PB_CommandStatus_ERROR_APP_NOT_RUNNING; | ||||||
|  |         FURI_LOG_E( | ||||||
|  |             TAG, "ExitRequest: APP_NOT_RUNNING, id %d, status: %d", request->command_id, status); | ||||||
|  |         rpc_send_and_release_empty(session, request->command_id, status); | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     rpc_send_and_release_empty(session, request->command_id, status); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void rpc_system_app_load_file(const PB_Main* request, void* context) { | static void rpc_system_app_load_file(const PB_Main* request, void* context) { | ||||||
| @ -133,17 +132,18 @@ static void rpc_system_app_load_file(const PB_Main* request, void* context) { | |||||||
| 
 | 
 | ||||||
|     PB_CommandStatus status; |     PB_CommandStatus status; | ||||||
|     if(rpc_app->app_callback) { |     if(rpc_app->app_callback) { | ||||||
|         const char* file_path = request->content.app_load_file_request.path; |         FURI_LOG_D(TAG, "LoadFile: id %d", request->command_id); | ||||||
|         if(rpc_app->app_callback(RpcAppEventLoadFile, file_path, rpc_app->app_context)) { |         furi_assert(!rpc_app->last_id); | ||||||
|             status = PB_CommandStatus_OK; |         furi_assert(!rpc_app->last_data); | ||||||
|         } else { |         rpc_app->last_id = request->command_id; | ||||||
|             status = PB_CommandStatus_ERROR_APP_CMD_ERROR; |         rpc_app->last_data = strdup(request->content.app_load_file_request.path); | ||||||
|         } |         rpc_app->app_callback(RpcAppEventLoadFile, rpc_app->app_context); | ||||||
|     } else { |     } else { | ||||||
|         status = PB_CommandStatus_ERROR_APP_NOT_RUNNING; |         status = PB_CommandStatus_ERROR_APP_NOT_RUNNING; | ||||||
|  |         FURI_LOG_E( | ||||||
|  |             TAG, "LoadFile: APP_NOT_RUNNING, id %d, status: %d", request->command_id, status); | ||||||
|  |         rpc_send_and_release_empty(session, request->command_id, status); | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     rpc_send_and_release_empty(session, request->command_id, status); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void rpc_system_app_button_press(const PB_Main* request, void* context) { | static void rpc_system_app_button_press(const PB_Main* request, void* context) { | ||||||
| @ -157,18 +157,18 @@ static void rpc_system_app_button_press(const PB_Main* request, void* context) { | |||||||
| 
 | 
 | ||||||
|     PB_CommandStatus status; |     PB_CommandStatus status; | ||||||
|     if(rpc_app->app_callback) { |     if(rpc_app->app_callback) { | ||||||
|         const char* args = request->content.app_button_press_request.args; |         FURI_LOG_D(TAG, "ButtonPress"); | ||||||
|         if(rpc_app->app_callback(RpcAppEventButtonPress, args, rpc_app->app_context)) { |         furi_assert(!rpc_app->last_id); | ||||||
|             status = PB_CommandStatus_OK; |         furi_assert(!rpc_app->last_data); | ||||||
|             furi_timer_start(rpc_app->timer, APP_BUTTON_TIMEOUT); |         rpc_app->last_id = request->command_id; | ||||||
|         } else { |         rpc_app->last_data = strdup(request->content.app_button_press_request.args); | ||||||
|             status = PB_CommandStatus_ERROR_APP_CMD_ERROR; |         rpc_app->app_callback(RpcAppEventButtonPress, rpc_app->app_context); | ||||||
|         } |  | ||||||
|     } else { |     } else { | ||||||
|         status = PB_CommandStatus_ERROR_APP_NOT_RUNNING; |         status = PB_CommandStatus_ERROR_APP_NOT_RUNNING; | ||||||
|  |         FURI_LOG_E( | ||||||
|  |             TAG, "ButtonPress: APP_NOT_RUNNING, id %d, status: %d", request->command_id, status); | ||||||
|  |         rpc_send_and_release_empty(session, request->command_id, status); | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     rpc_send_and_release_empty(session, request->command_id, status); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void rpc_system_app_button_release(const PB_Main* request, void* context) { | static void rpc_system_app_button_release(const PB_Main* request, void* context) { | ||||||
| @ -182,17 +182,17 @@ static void rpc_system_app_button_release(const PB_Main* request, void* context) | |||||||
| 
 | 
 | ||||||
|     PB_CommandStatus status; |     PB_CommandStatus status; | ||||||
|     if(rpc_app->app_callback) { |     if(rpc_app->app_callback) { | ||||||
|         if(rpc_app->app_callback(RpcAppEventButtonRelease, NULL, rpc_app->app_context)) { |         FURI_LOG_D(TAG, "ButtonRelease"); | ||||||
|             status = PB_CommandStatus_OK; |         furi_assert(!rpc_app->last_id); | ||||||
|             furi_timer_stop(rpc_app->timer); |         furi_assert(!rpc_app->last_data); | ||||||
|         } else { |         rpc_app->last_id = request->command_id; | ||||||
|             status = PB_CommandStatus_ERROR_APP_CMD_ERROR; |         rpc_app->app_callback(RpcAppEventButtonRelease, rpc_app->app_context); | ||||||
|         } |  | ||||||
|     } else { |     } else { | ||||||
|         status = PB_CommandStatus_ERROR_APP_NOT_RUNNING; |         status = PB_CommandStatus_ERROR_APP_NOT_RUNNING; | ||||||
|  |         FURI_LOG_E( | ||||||
|  |             TAG, "ButtonRelease: APP_NOT_RUNNING, id %d, status: %d", request->command_id, status); | ||||||
|  |         rpc_send_and_release_empty(session, request->command_id, status); | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     rpc_send_and_release_empty(session, request->command_id, status); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void rpc_system_app_send_started(RpcAppSystem* rpc_app) { | void rpc_system_app_send_started(RpcAppSystem* rpc_app) { | ||||||
| @ -201,6 +201,8 @@ void rpc_system_app_send_started(RpcAppSystem* rpc_app) { | |||||||
|     furi_assert(session); |     furi_assert(session); | ||||||
| 
 | 
 | ||||||
|     rpc_app->state_msg->content.app_state_response.state = PB_App_AppState_APP_STARTED; |     rpc_app->state_msg->content.app_state_response.state = PB_App_AppState_APP_STARTED; | ||||||
|  | 
 | ||||||
|  |     FURI_LOG_D(TAG, "SendStarted"); | ||||||
|     rpc_send(session, rpc_app->state_msg); |     rpc_send(session, rpc_app->state_msg); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -210,9 +212,46 @@ void rpc_system_app_send_exited(RpcAppSystem* rpc_app) { | |||||||
|     furi_assert(session); |     furi_assert(session); | ||||||
| 
 | 
 | ||||||
|     rpc_app->state_msg->content.app_state_response.state = PB_App_AppState_APP_CLOSED; |     rpc_app->state_msg->content.app_state_response.state = PB_App_AppState_APP_CLOSED; | ||||||
|  | 
 | ||||||
|  |     FURI_LOG_D(TAG, "SendExit"); | ||||||
|     rpc_send(session, rpc_app->state_msg); |     rpc_send(session, rpc_app->state_msg); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | const char* rpc_system_app_get_data(RpcAppSystem* rpc_app) { | ||||||
|  |     furi_assert(rpc_app); | ||||||
|  |     furi_assert(rpc_app->last_data); | ||||||
|  |     return rpc_app->last_data; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void rpc_system_app_confirm(RpcAppSystem* rpc_app, RpcAppSystemEvent event, bool result) { | ||||||
|  |     furi_assert(rpc_app); | ||||||
|  |     RpcSession* session = rpc_app->session; | ||||||
|  |     furi_assert(session); | ||||||
|  |     furi_assert(rpc_app->last_id); | ||||||
|  | 
 | ||||||
|  |     PB_CommandStatus status = result ? PB_CommandStatus_OK : PB_CommandStatus_ERROR_APP_CMD_ERROR; | ||||||
|  | 
 | ||||||
|  |     uint32_t last_id = 0; | ||||||
|  |     switch(event) { | ||||||
|  |     case RpcAppEventAppExit: | ||||||
|  |     case RpcAppEventLoadFile: | ||||||
|  |     case RpcAppEventButtonPress: | ||||||
|  |     case RpcAppEventButtonRelease: | ||||||
|  |         last_id = rpc_app->last_id; | ||||||
|  |         rpc_app->last_id = 0; | ||||||
|  |         if(rpc_app->last_data) { | ||||||
|  |             free(rpc_app->last_data); | ||||||
|  |             rpc_app->last_data = NULL; | ||||||
|  |         } | ||||||
|  |         FURI_LOG_D(TAG, "AppConfirm: event %d last_id %d status %d", event, last_id, status); | ||||||
|  |         rpc_send_and_release_empty(session, last_id, status); | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         furi_crash("RPC App state programming Error"); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void rpc_system_app_set_callback(RpcAppSystem* rpc_app, RpcAppSystemCallback callback, void* ctx) { | void rpc_system_app_set_callback(RpcAppSystem* rpc_app, RpcAppSystemCallback callback, void* ctx) { | ||||||
|     furi_assert(rpc_app); |     furi_assert(rpc_app); | ||||||
| 
 | 
 | ||||||
| @ -226,8 +265,6 @@ void* rpc_system_app_alloc(RpcSession* session) { | |||||||
|     RpcAppSystem* rpc_app = malloc(sizeof(RpcAppSystem)); |     RpcAppSystem* rpc_app = malloc(sizeof(RpcAppSystem)); | ||||||
|     rpc_app->session = session; |     rpc_app->session = session; | ||||||
| 
 | 
 | ||||||
|     rpc_app->timer = furi_timer_alloc(rpc_system_app_timer_callback, FuriTimerTypeOnce, rpc_app); |  | ||||||
| 
 |  | ||||||
|     // App exit message
 |     // App exit message
 | ||||||
|     rpc_app->state_msg = malloc(sizeof(PB_Main)); |     rpc_app->state_msg = malloc(sizeof(PB_Main)); | ||||||
|     rpc_app->state_msg->which_content = PB_Main_app_state_response_tag; |     rpc_app->state_msg->which_content = PB_Main_app_state_response_tag; | ||||||
| @ -265,12 +302,16 @@ void rpc_system_app_free(void* context) { | |||||||
|     RpcSession* session = rpc_app->session; |     RpcSession* session = rpc_app->session; | ||||||
|     furi_assert(session); |     furi_assert(session); | ||||||
| 
 | 
 | ||||||
|     furi_timer_free(rpc_app->timer); |  | ||||||
| 
 |  | ||||||
|     if(rpc_app->app_callback) { |     if(rpc_app->app_callback) { | ||||||
|         rpc_app->app_callback(RpcAppEventSessionClose, NULL, rpc_app->app_context); |         rpc_app->app_callback(RpcAppEventSessionClose, rpc_app->app_context); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     while(rpc_app->app_callback) { | ||||||
|  |         furi_delay_tick(1); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if(rpc_app->last_data) free(rpc_app->last_data); | ||||||
|  | 
 | ||||||
|     free(rpc_app->state_msg); |     free(rpc_app->state_msg); | ||||||
|     free(rpc_app); |     free(rpc_app); | ||||||
| } | } | ||||||
|  | |||||||
| @ -13,7 +13,7 @@ typedef enum { | |||||||
|     RpcAppEventButtonRelease, |     RpcAppEventButtonRelease, | ||||||
| } RpcAppSystemEvent; | } RpcAppSystemEvent; | ||||||
| 
 | 
 | ||||||
| typedef bool (*RpcAppSystemCallback)(RpcAppSystemEvent event, const char* arg, void* context); | typedef void (*RpcAppSystemCallback)(RpcAppSystemEvent event, void* context); | ||||||
| 
 | 
 | ||||||
| typedef struct RpcAppSystem RpcAppSystem; | typedef struct RpcAppSystem RpcAppSystem; | ||||||
| 
 | 
 | ||||||
| @ -23,6 +23,10 @@ void rpc_system_app_send_started(RpcAppSystem* rpc_app); | |||||||
| 
 | 
 | ||||||
| void rpc_system_app_send_exited(RpcAppSystem* rpc_app); | void rpc_system_app_send_exited(RpcAppSystem* rpc_app); | ||||||
| 
 | 
 | ||||||
|  | const char* rpc_system_app_get_data(RpcAppSystem* rpc_app); | ||||||
|  | 
 | ||||||
|  | void rpc_system_app_confirm(RpcAppSystem* rpc_app, RpcAppSystemEvent event, bool result); | ||||||
|  | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  | |||||||
| @ -594,23 +594,19 @@ static void rpc_system_storage_backup_create_process(const PB_Main* request, voi | |||||||
| 
 | 
 | ||||||
|     FURI_LOG_D(TAG, "BackupCreate"); |     FURI_LOG_D(TAG, "BackupCreate"); | ||||||
| 
 | 
 | ||||||
|     RpcSession* session = (RpcSession*)context; |     RpcStorageSystem* rpc_storage = context; | ||||||
|  |     RpcSession* session = rpc_storage->session; | ||||||
|     furi_assert(session); |     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(RECORD_STORAGE); |     Storage* fs_api = furi_record_open(RECORD_STORAGE); | ||||||
| 
 | 
 | ||||||
|     bool backup_ok = |     bool backup_ok = | ||||||
|         lfs_backup_create(fs_api, request->content.storage_backup_create_request.archive_path); |         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(RECORD_STORAGE); |     furi_record_close(RECORD_STORAGE); | ||||||
| 
 | 
 | ||||||
|     rpc_send_and_release(session, response); |     rpc_send_and_release_empty( | ||||||
|     free(response); |         session, request->command_id, backup_ok ? PB_CommandStatus_OK : PB_CommandStatus_ERROR); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void rpc_system_storage_backup_restore_process(const PB_Main* request, void* context) { | static void rpc_system_storage_backup_restore_process(const PB_Main* request, void* context) { | ||||||
| @ -619,24 +615,19 @@ static void rpc_system_storage_backup_restore_process(const PB_Main* request, vo | |||||||
| 
 | 
 | ||||||
|     FURI_LOG_D(TAG, "BackupRestore"); |     FURI_LOG_D(TAG, "BackupRestore"); | ||||||
| 
 | 
 | ||||||
|     RpcSession* session = (RpcSession*)context; |     RpcStorageSystem* rpc_storage = context; | ||||||
|  |     RpcSession* session = rpc_storage->session; | ||||||
|     furi_assert(session); |     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(RECORD_STORAGE); |     Storage* fs_api = furi_record_open(RECORD_STORAGE); | ||||||
| 
 | 
 | ||||||
|     bool backup_ok = |     bool backup_ok = | ||||||
|         lfs_backup_unpack(fs_api, request->content.storage_backup_restore_request.archive_path); |         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(RECORD_STORAGE); |     furi_record_close(RECORD_STORAGE); | ||||||
| 
 | 
 | ||||||
|     rpc_send_and_release(session, response); |     rpc_send_and_release_empty( | ||||||
|     free(response); |         session, request->command_id, backup_ok ? PB_CommandStatus_OK : PB_CommandStatus_ERROR); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void* rpc_system_storage_alloc(RpcSession* session) { | void* rpc_system_storage_alloc(RpcSession* session) { | ||||||
|  | |||||||
| @ -26,8 +26,7 @@ static FS_Error storage_ext_parse_error(SDError error); | |||||||
| 
 | 
 | ||||||
| static bool sd_mount_card(StorageData* storage, bool notify) { | static bool sd_mount_card(StorageData* storage, bool notify) { | ||||||
|     bool result = false; |     bool result = false; | ||||||
|     const uint8_t max_init_counts = 10; |     uint8_t counter = BSP_SD_MaxMountRetryCount(); | ||||||
|     uint8_t counter = max_init_counts; |  | ||||||
|     uint8_t bsp_result; |     uint8_t bsp_result; | ||||||
|     SDData* sd_data = storage->data; |     SDData* sd_data = storage->data; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -47,6 +47,9 @@ typedef enum { | |||||||
|     SubGhzCustomEventSceneStay, |     SubGhzCustomEventSceneStay, | ||||||
| 
 | 
 | ||||||
|     SubGhzCustomEventSceneRpcLoad, |     SubGhzCustomEventSceneRpcLoad, | ||||||
|  |     SubGhzCustomEventSceneRpcButtonPress, | ||||||
|  |     SubGhzCustomEventSceneRpcButtonRelease, | ||||||
|  |     SubGhzCustomEventSceneRpcSessionClose, | ||||||
| 
 | 
 | ||||||
|     SubGhzCustomEventViewReceiverOK, |     SubGhzCustomEventViewReceiverOK, | ||||||
|     SubGhzCustomEventViewReceiverConfig, |     SubGhzCustomEventViewReceiverConfig, | ||||||
|  | |||||||
| @ -72,11 +72,11 @@ typedef enum { | |||||||
|     SubGhzViewIdTestPacket, |     SubGhzViewIdTestPacket, | ||||||
| } SubGhzViewId; | } SubGhzViewId; | ||||||
| 
 | 
 | ||||||
| struct SubGhzPesetDefinition { | struct SubGhzPresetDefinition { | ||||||
|     string_t name; |     string_t name; | ||||||
|     uint32_t frequency; |     uint32_t frequency; | ||||||
|     uint8_t* data; |     uint8_t* data; | ||||||
|     size_t data_size; |     size_t data_size; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| typedef struct SubGhzPesetDefinition SubGhzPesetDefinition; | typedef struct SubGhzPresetDefinition SubGhzPresetDefinition; | ||||||
|  | |||||||
| @ -28,8 +28,8 @@ static bool subghz_scene_receiver_info_update_parser(void* context) { | |||||||
|             subghz->txrx->decoder_result, |             subghz->txrx->decoder_result, | ||||||
|             subghz_history_get_raw_data(subghz->txrx->history, subghz->txrx->idx_menu_chosen)); |             subghz_history_get_raw_data(subghz->txrx->history, subghz->txrx->idx_menu_chosen)); | ||||||
| 
 | 
 | ||||||
|         SubGhzPesetDefinition* preset = |         SubGhzPresetDefinition* preset = | ||||||
|             subghz_history_get_presset(subghz->txrx->history, subghz->txrx->idx_menu_chosen); |             subghz_history_get_preset_def(subghz->txrx->history, subghz->txrx->idx_menu_chosen); | ||||||
|         subghz_preset_init( |         subghz_preset_init( | ||||||
|             subghz, |             subghz, | ||||||
|             string_get_cstr(preset->name), |             string_get_cstr(preset->name), | ||||||
|  | |||||||
| @ -22,20 +22,60 @@ bool subghz_scene_rpc_on_event(void* context, SceneManagerEvent event) { | |||||||
|     if(event.type == SceneManagerEventTypeCustom) { |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|         consumed = true; |         consumed = true; | ||||||
|         if(event.event == SubGhzCustomEventSceneExit) { |         if(event.event == SubGhzCustomEventSceneExit) { | ||||||
|  |             if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { | ||||||
|  |                 subghz_tx_stop(subghz); | ||||||
|  |                 subghz_sleep(subghz); | ||||||
|  |             } | ||||||
|             view_dispatcher_stop(subghz->view_dispatcher); |             view_dispatcher_stop(subghz->view_dispatcher); | ||||||
|  |             rpc_system_app_confirm(subghz->rpc_ctx, RpcAppEventAppExit, true); | ||||||
|  |         } else if(event.event == SubGhzCustomEventSceneRpcSessionClose) { | ||||||
|  |             rpc_system_app_set_callback(subghz->rpc_ctx, NULL, NULL); | ||||||
|  |             subghz->rpc_ctx = NULL; | ||||||
|  |             subghz_blink_stop(subghz); | ||||||
|  |             if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { | ||||||
|  |                 subghz_tx_stop(subghz); | ||||||
|  |                 subghz_sleep(subghz); | ||||||
|  |             } | ||||||
|  |             view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventSceneExit); | ||||||
|  |         } else if(event.event == SubGhzCustomEventSceneRpcButtonPress) { | ||||||
|  |             bool result = false; | ||||||
|  |             if(subghz->txrx->txrx_state == SubGhzTxRxStateSleep) { | ||||||
|  |                 subghz_blink_start(subghz); | ||||||
|  |                 result = subghz_tx_start(subghz, subghz->txrx->fff_data); | ||||||
|  |                 result = true; | ||||||
|  |             } | ||||||
|  |             rpc_system_app_confirm(subghz->rpc_ctx, RpcAppEventButtonPress, result); | ||||||
|  |         } else if(event.event == SubGhzCustomEventSceneRpcButtonRelease) { | ||||||
|  |             bool result = false; | ||||||
|  |             if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { | ||||||
|  |                 subghz_blink_stop(subghz); | ||||||
|  |                 subghz_tx_stop(subghz); | ||||||
|  |                 subghz_sleep(subghz); | ||||||
|  |                 result = true; | ||||||
|  |             } | ||||||
|  |             rpc_system_app_confirm(subghz->rpc_ctx, RpcAppEventButtonRelease, result); | ||||||
|         } else if(event.event == SubGhzCustomEventSceneRpcLoad) { |         } else if(event.event == SubGhzCustomEventSceneRpcLoad) { | ||||||
|             string_t file_name; |             bool result = false; | ||||||
|             string_init(file_name); |             const char* arg = rpc_system_app_get_data(subghz->rpc_ctx); | ||||||
|             path_extract_filename(subghz->file_path, file_name, true); |             if(arg) { | ||||||
|  |                 if(subghz_key_load(subghz, arg, false)) { | ||||||
|  |                     string_set_str(subghz->file_path, arg); | ||||||
|  |                     result = true; | ||||||
|  |                     string_t file_name; | ||||||
|  |                     string_init(file_name); | ||||||
|  |                     path_extract_filename(subghz->file_path, file_name, true); | ||||||
| 
 | 
 | ||||||
|             snprintf( |                     snprintf( | ||||||
|                 subghz->file_name_tmp, |                         subghz->file_name_tmp, | ||||||
|                 SUBGHZ_MAX_LEN_NAME, |                         SUBGHZ_MAX_LEN_NAME, | ||||||
|                 "loaded\n%s", |                         "loaded\n%s", | ||||||
|                 string_get_cstr(file_name)); |                         string_get_cstr(file_name)); | ||||||
|             popup_set_text(popup, subghz->file_name_tmp, 82, 32, AlignCenter, AlignTop); |                     popup_set_text(popup, subghz->file_name_tmp, 82, 32, AlignCenter, AlignTop); | ||||||
| 
 | 
 | ||||||
|             string_clear(file_name); |                     string_clear(file_name); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             rpc_system_app_confirm(subghz->rpc_ctx, RpcAppEventLoadFile, result); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     return consumed; |     return consumed; | ||||||
|  | |||||||
| @ -35,57 +35,38 @@ void subghz_tick_event_callback(void* context) { | |||||||
|     scene_manager_handle_tick_event(subghz->scene_manager); |     scene_manager_handle_tick_event(subghz->scene_manager); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static bool subghz_rpc_command_callback(RpcAppSystemEvent event, const char* arg, void* context) { | static void subghz_rpc_command_callback(RpcAppSystemEvent event, void* context) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     SubGhz* subghz = context; |     SubGhz* subghz = context; | ||||||
| 
 | 
 | ||||||
|     if(!subghz->rpc_ctx) { |     furi_assert(subghz->rpc_ctx); | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     bool result = false; |  | ||||||
| 
 | 
 | ||||||
|     if(event == RpcAppEventSessionClose) { |     if(event == RpcAppEventSessionClose) { | ||||||
|         rpc_system_app_set_callback(subghz->rpc_ctx, NULL, NULL); |         view_dispatcher_send_custom_event( | ||||||
|         subghz->rpc_ctx = NULL; |             subghz->view_dispatcher, SubGhzCustomEventSceneRpcSessionClose); | ||||||
|         notification_message(subghz->notifications, &sequence_blink_stop); |  | ||||||
|         view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventSceneExit); |  | ||||||
|         if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { |  | ||||||
|             subghz_tx_stop(subghz); |  | ||||||
|             subghz_sleep(subghz); |  | ||||||
|         } |  | ||||||
|         result = true; |  | ||||||
|     } else if(event == RpcAppEventAppExit) { |     } else if(event == RpcAppEventAppExit) { | ||||||
|         view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventSceneExit); |         view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventSceneExit); | ||||||
|         if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { |  | ||||||
|             subghz_tx_stop(subghz); |  | ||||||
|             subghz_sleep(subghz); |  | ||||||
|         } |  | ||||||
|         result = true; |  | ||||||
|     } else if(event == RpcAppEventLoadFile) { |     } else if(event == RpcAppEventLoadFile) { | ||||||
|         if(arg) { |         view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventSceneRpcLoad); | ||||||
|             if(subghz_key_load(subghz, arg, false)) { |  | ||||||
|                 string_set_str(subghz->file_path, arg); |  | ||||||
|                 view_dispatcher_send_custom_event( |  | ||||||
|                     subghz->view_dispatcher, SubGhzCustomEventSceneRpcLoad); |  | ||||||
|                 result = true; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } else if(event == RpcAppEventButtonPress) { |     } else if(event == RpcAppEventButtonPress) { | ||||||
|         if(subghz->txrx->txrx_state == SubGhzTxRxStateSleep) { |         view_dispatcher_send_custom_event( | ||||||
|             notification_message(subghz->notifications, &sequence_blink_start_magenta); |             subghz->view_dispatcher, SubGhzCustomEventSceneRpcButtonPress); | ||||||
|             result = subghz_tx_start(subghz, subghz->txrx->fff_data); |  | ||||||
|         } |  | ||||||
|     } else if(event == RpcAppEventButtonRelease) { |     } else if(event == RpcAppEventButtonRelease) { | ||||||
|         if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { |         view_dispatcher_send_custom_event( | ||||||
|             notification_message(subghz->notifications, &sequence_blink_stop); |             subghz->view_dispatcher, SubGhzCustomEventSceneRpcButtonRelease); | ||||||
|             subghz_tx_stop(subghz); |     } else { | ||||||
|             subghz_sleep(subghz); |         rpc_system_app_confirm(subghz->rpc_ctx, event, false); | ||||||
|             result = true; |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
|     return result; | void subghz_blink_start(SubGhz* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     notification_message(instance->notifications, &sequence_blink_start_magenta); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void subghz_blink_stop(SubGhz* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     notification_message(instance->notifications, &sequence_blink_stop); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| SubGhz* subghz_alloc() { | SubGhz* subghz_alloc() { | ||||||
| @ -199,7 +180,7 @@ SubGhz* subghz_alloc() { | |||||||
|     //init Worker & Protocol & History & KeyBoard
 |     //init Worker & Protocol & History & KeyBoard
 | ||||||
|     subghz->lock = SubGhzLockOff; |     subghz->lock = SubGhzLockOff; | ||||||
|     subghz->txrx = malloc(sizeof(SubGhzTxRx)); |     subghz->txrx = malloc(sizeof(SubGhzTxRx)); | ||||||
|     subghz->txrx->preset = malloc(sizeof(SubGhzPesetDefinition)); |     subghz->txrx->preset = malloc(sizeof(SubGhzPresetDefinition)); | ||||||
|     string_init(subghz->txrx->preset->name); |     string_init(subghz->txrx->preset->name); | ||||||
|     subghz_preset_init( |     subghz_preset_init( | ||||||
|         subghz, "AM650", subghz_setting_get_default_frequency(subghz->setting), NULL, 0); |         subghz, "AM650", subghz_setting_get_default_frequency(subghz->setting), NULL, 0); | ||||||
| @ -237,7 +218,7 @@ void subghz_free(SubGhz* subghz) { | |||||||
|     if(subghz->rpc_ctx) { |     if(subghz->rpc_ctx) { | ||||||
|         rpc_system_app_set_callback(subghz->rpc_ctx, NULL, NULL); |         rpc_system_app_set_callback(subghz->rpc_ctx, NULL, NULL); | ||||||
|         rpc_system_app_send_exited(subghz->rpc_ctx); |         rpc_system_app_send_exited(subghz->rpc_ctx); | ||||||
|         notification_message(subghz->notifications, &sequence_blink_stop); |         subghz_blink_stop(subghz); | ||||||
|         subghz->rpc_ctx = NULL; |         subghz->rpc_ctx = NULL; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -12,7 +12,7 @@ typedef struct { | |||||||
|     string_t item_str; |     string_t item_str; | ||||||
|     FlipperFormat* flipper_string; |     FlipperFormat* flipper_string; | ||||||
|     uint8_t type; |     uint8_t type; | ||||||
|     SubGhzPesetDefinition* preset; |     SubGhzPresetDefinition* preset; | ||||||
| } SubGhzHistoryItem; | } SubGhzHistoryItem; | ||||||
| 
 | 
 | ||||||
| ARRAY_DEF(SubGhzHistoryItemArray, SubGhzHistoryItem, M_POD_OPLIST) | ARRAY_DEF(SubGhzHistoryItemArray, SubGhzHistoryItem, M_POD_OPLIST) | ||||||
| @ -61,7 +61,7 @@ uint32_t subghz_history_get_frequency(SubGhzHistory* instance, uint16_t idx) { | |||||||
|     return item->preset->frequency; |     return item->preset->frequency; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| SubGhzPesetDefinition* subghz_history_get_presset(SubGhzHistory* instance, uint16_t idx) { | SubGhzPresetDefinition* subghz_history_get_preset_def(SubGhzHistory* instance, uint16_t idx) { | ||||||
|     furi_assert(instance); |     furi_assert(instance); | ||||||
|     SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx); |     SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx); | ||||||
|     return item->preset; |     return item->preset; | ||||||
| @ -139,7 +139,7 @@ void subghz_history_get_text_item_menu(SubGhzHistory* instance, string_t output, | |||||||
| bool subghz_history_add_to_history( | bool subghz_history_add_to_history( | ||||||
|     SubGhzHistory* instance, |     SubGhzHistory* instance, | ||||||
|     void* context, |     void* context, | ||||||
|     SubGhzPesetDefinition* preset) { |     SubGhzPresetDefinition* preset) { | ||||||
|     furi_assert(instance); |     furi_assert(instance); | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
| 
 | 
 | ||||||
| @ -159,7 +159,7 @@ bool subghz_history_add_to_history( | |||||||
|     string_t text; |     string_t text; | ||||||
|     string_init(text); |     string_init(text); | ||||||
|     SubGhzHistoryItem* item = SubGhzHistoryItemArray_push_raw(instance->history->data); |     SubGhzHistoryItem* item = SubGhzHistoryItemArray_push_raw(instance->history->data); | ||||||
|     item->preset = malloc(sizeof(SubGhzPesetDefinition)); |     item->preset = malloc(sizeof(SubGhzPresetDefinition)); | ||||||
|     item->type = decoder_base->protocol->type; |     item->type = decoder_base->protocol->type; | ||||||
|     item->preset->frequency = preset->frequency; |     item->preset->frequency = preset->frequency; | ||||||
|     string_init(item->preset->name); |     string_init(item->preset->name); | ||||||
|  | |||||||
| @ -35,7 +35,7 @@ void subghz_history_reset(SubGhzHistory* instance); | |||||||
|  */ |  */ | ||||||
| uint32_t subghz_history_get_frequency(SubGhzHistory* instance, uint16_t idx); | uint32_t subghz_history_get_frequency(SubGhzHistory* instance, uint16_t idx); | ||||||
| 
 | 
 | ||||||
| SubGhzPesetDefinition* subghz_history_get_presset(SubGhzHistory* instance, uint16_t idx); | SubGhzPresetDefinition* subghz_history_get_preset_def(SubGhzHistory* instance, uint16_t idx); | ||||||
| 
 | 
 | ||||||
| /** Get preset to history[idx]
 | /** Get preset to history[idx]
 | ||||||
|  *  |  *  | ||||||
| @ -88,13 +88,13 @@ bool subghz_history_get_text_space_left(SubGhzHistory* instance, string_t output | |||||||
|  *  |  *  | ||||||
|  * @param instance  - SubGhzHistory instance |  * @param instance  - SubGhzHistory instance | ||||||
|  * @param context    - SubGhzProtocolCommon context |  * @param context    - SubGhzProtocolCommon context | ||||||
|  * @param preset    - SubGhzPesetDefinition preset |  * @param preset    - SubGhzPresetDefinition preset | ||||||
|  * @return bool; |  * @return bool; | ||||||
|  */ |  */ | ||||||
| bool subghz_history_add_to_history( | bool subghz_history_add_to_history( | ||||||
|     SubGhzHistory* instance, |     SubGhzHistory* instance, | ||||||
|     void* context, |     void* context, | ||||||
|     SubGhzPesetDefinition* preset); |     SubGhzPresetDefinition* preset); | ||||||
| 
 | 
 | ||||||
| /** Get SubGhzProtocolCommonLoad to load into the protocol decoder bin data
 | /** Get SubGhzProtocolCommonLoad to load into the protocol decoder bin data
 | ||||||
|  *  |  *  | ||||||
|  | |||||||
| @ -331,8 +331,10 @@ bool subghz_key_load(SubGhz* subghz, const char* file_path, bool show_dialog) { | |||||||
|         subghz->txrx->decoder_result = subghz_receiver_search_decoder_base_by_name( |         subghz->txrx->decoder_result = subghz_receiver_search_decoder_base_by_name( | ||||||
|             subghz->txrx->receiver, string_get_cstr(temp_str)); |             subghz->txrx->receiver, string_get_cstr(temp_str)); | ||||||
|         if(subghz->txrx->decoder_result) { |         if(subghz->txrx->decoder_result) { | ||||||
|             subghz_protocol_decoder_base_deserialize( |             if(!subghz_protocol_decoder_base_deserialize( | ||||||
|                 subghz->txrx->decoder_result, subghz->txrx->fff_data); |                    subghz->txrx->decoder_result, subghz->txrx->fff_data)) { | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|         } else { |         } else { | ||||||
|             FURI_LOG_E(TAG, "Protocol not found"); |             FURI_LOG_E(TAG, "Protocol not found"); | ||||||
|             break; |             break; | ||||||
|  | |||||||
| @ -49,7 +49,7 @@ struct SubGhzTxRx { | |||||||
|     SubGhzProtocolDecoderBase* decoder_result; |     SubGhzProtocolDecoderBase* decoder_result; | ||||||
|     FlipperFormat* fff_data; |     FlipperFormat* fff_data; | ||||||
| 
 | 
 | ||||||
|     SubGhzPesetDefinition* preset; |     SubGhzPresetDefinition* preset; | ||||||
|     SubGhzHistory* history; |     SubGhzHistory* history; | ||||||
|     uint16_t idx_menu_chosen; |     uint16_t idx_menu_chosen; | ||||||
|     SubGhzTxRxState txrx_state; |     SubGhzTxRxState txrx_state; | ||||||
| @ -108,6 +108,10 @@ void subghz_begin(SubGhz* subghz, uint8_t* preset_data); | |||||||
| uint32_t subghz_rx(SubGhz* subghz, uint32_t frequency); | uint32_t subghz_rx(SubGhz* subghz, uint32_t frequency); | ||||||
| void subghz_rx_end(SubGhz* subghz); | void subghz_rx_end(SubGhz* subghz); | ||||||
| void subghz_sleep(SubGhz* subghz); | void subghz_sleep(SubGhz* subghz); | ||||||
|  | 
 | ||||||
|  | void subghz_blink_start(SubGhz* instance); | ||||||
|  | void subghz_blink_stop(SubGhz* instance); | ||||||
|  | 
 | ||||||
| bool subghz_tx_start(SubGhz* subghz, FlipperFormat* flipper_format); | bool subghz_tx_start(SubGhz* subghz, FlipperFormat* flipper_format); | ||||||
| void subghz_tx_stop(SubGhz* subghz); | void subghz_tx_stop(SubGhz* subghz); | ||||||
| void subghz_dialog_message_show_only_rx(SubGhz* subghz); | void subghz_dialog_message_show_only_rx(SubGhz* subghz); | ||||||
|  | |||||||
| @ -216,8 +216,8 @@ void subghz_read_raw_draw(Canvas* canvas, SubGhzReadRAWModel* model) { | |||||||
|     uint8_t graphics_mode = 1; |     uint8_t graphics_mode = 1; | ||||||
|     canvas_set_color(canvas, ColorBlack); |     canvas_set_color(canvas, ColorBlack); | ||||||
|     canvas_set_font(canvas, FontSecondary); |     canvas_set_font(canvas, FontSecondary); | ||||||
|     canvas_draw_str(canvas, 5, 8, string_get_cstr(model->frequency_str)); |     canvas_draw_str(canvas, 5, 7, string_get_cstr(model->frequency_str)); | ||||||
|     canvas_draw_str(canvas, 40, 8, string_get_cstr(model->preset_str)); |     canvas_draw_str(canvas, 40, 7, string_get_cstr(model->preset_str)); | ||||||
|     canvas_draw_str_aligned( |     canvas_draw_str_aligned( | ||||||
|         canvas, 126, 0, AlignRight, AlignTop, string_get_cstr(model->sample_write)); |         canvas, 126, 0, AlignRight, AlignTop, string_get_cstr(model->sample_write)); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -387,6 +387,34 @@ MU_TEST(stream_split_test) { | |||||||
|     furi_record_close(RECORD_STORAGE); |     furi_record_close(RECORD_STORAGE); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | MU_TEST(stream_buffered_write_after_read_test) { | ||||||
|  |     const char* prefix = "I write "; | ||||||
|  |     const char* substr = "Hello there"; | ||||||
|  | 
 | ||||||
|  |     const size_t substr_len = strlen(substr); | ||||||
|  |     const size_t prefix_len = strlen(prefix); | ||||||
|  |     const size_t buf_size = substr_len + 1; | ||||||
|  | 
 | ||||||
|  |     char buf[buf_size]; | ||||||
|  |     memset(buf, 0, buf_size); | ||||||
|  | 
 | ||||||
|  |     Storage* storage = furi_record_open(RECORD_STORAGE); | ||||||
|  |     Stream* stream = buffered_file_stream_alloc(storage); | ||||||
|  |     mu_check(buffered_file_stream_open( | ||||||
|  |         stream, EXT_PATH("filestream.str"), FSAM_READ_WRITE, FSOM_CREATE_ALWAYS)); | ||||||
|  |     mu_assert_int_eq(strlen(stream_test_data), stream_write_cstring(stream, stream_test_data)); | ||||||
|  |     mu_check(stream_rewind(stream)); | ||||||
|  |     mu_assert_int_eq(prefix_len, stream_read(stream, (uint8_t*)buf, prefix_len)); | ||||||
|  |     mu_assert_string_eq(prefix, buf); | ||||||
|  |     mu_assert_int_eq(substr_len, stream_write(stream, (uint8_t*)substr, substr_len)); | ||||||
|  |     mu_check(stream_seek(stream, prefix_len, StreamOffsetFromStart)); | ||||||
|  |     mu_assert_int_eq(substr_len, stream_read(stream, (uint8_t*)buf, substr_len)); | ||||||
|  |     mu_assert_string_eq(substr, buf); | ||||||
|  | 
 | ||||||
|  |     stream_free(stream); | ||||||
|  |     furi_record_close(RECORD_STORAGE); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| MU_TEST(stream_buffered_large_file_test) { | MU_TEST(stream_buffered_large_file_test) { | ||||||
|     string_t input_data; |     string_t input_data; | ||||||
|     string_t output_data; |     string_t output_data; | ||||||
| @ -470,6 +498,7 @@ MU_TEST_SUITE(stream_suite) { | |||||||
|     MU_RUN_TEST(stream_write_read_save_load_test); |     MU_RUN_TEST(stream_write_read_save_load_test); | ||||||
|     MU_RUN_TEST(stream_composite_test); |     MU_RUN_TEST(stream_composite_test); | ||||||
|     MU_RUN_TEST(stream_split_test); |     MU_RUN_TEST(stream_split_test); | ||||||
|  |     MU_RUN_TEST(stream_buffered_write_after_read_test); | ||||||
|     MU_RUN_TEST(stream_buffered_large_file_test); |     MU_RUN_TEST(stream_buffered_large_file_test); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -103,8 +103,7 @@ if assetsenv["IS_BASE_FIRMWARE"]: | |||||||
|     ) |     ) | ||||||
|     assetsenv.Precious(resources) |     assetsenv.Precious(resources) | ||||||
|     assetsenv.NoClean(resources) |     assetsenv.NoClean(resources) | ||||||
|     if assetsenv["FORCE"]: |     assetsenv.AlwaysBuild(resources) | ||||||
|         assetsenv.AlwaysBuild(resources) |  | ||||||
| 
 | 
 | ||||||
|     # Exporting resources node to external environment |     # Exporting resources node to external environment | ||||||
|     env["FW_RESOURCES"] = resources |     env["FW_RESOURCES"] = resources | ||||||
|  | |||||||
| Before Width: | Height: | Size: 301 B | 
| Before Width: | Height: | Size: 3.6 KiB | 
| Before Width: | Height: | Size: 3.6 KiB | 
| Before Width: | Height: | Size: 624 B | 
| Before Width: | Height: | Size: 558 B | 
| Before Width: | Height: | Size: 574 B | 
| Before Width: | Height: | Size: 527 B | 
| Before Width: | Height: | Size: 556 B | 
| Before Width: | Height: | Size: 1.7 KiB | 
| Before Width: | Height: | Size: 553 B | 
| Before Width: | Height: | Size: 423 B | 
| Before Width: | Height: | Size: 4.1 KiB | 
| Before Width: | Height: | Size: 3.9 KiB | 
| Before Width: | Height: | Size: 4.1 KiB | 
| Before Width: | Height: | Size: 372 B | 
| Before Width: | Height: | Size: 364 B | 
| Before Width: | Height: | Size: 298 B | 
| Before Width: | Height: | Size: 100 B | 
| Before Width: | Height: | Size: 99 B | 
| Before Width: | Height: | Size: 99 B | 
| Before Width: | Height: | Size: 96 B | 
| Before Width: | Height: | Size: 97 B | 
| Before Width: | Height: | Size: 99 B | 
| @ -1 +0,0 @@ | |||||||
| 3 |  | ||||||
| Before Width: | Height: | Size: 96 B | 
| Before Width: | Height: | Size: 99 B | 
| Before Width: | Height: | Size: 105 B | 
| Before Width: | Height: | Size: 104 B | 
| Before Width: | Height: | Size: 105 B | 
| Before Width: | Height: | Size: 102 B | 
| Before Width: | Height: | Size: 101 B | 
| Before Width: | Height: | Size: 102 B | 
| Before Width: | Height: | Size: 100 B | 
| @ -1 +0,0 @@ | |||||||
| 3 |  | ||||||
| Before Width: | Height: | Size: 98 B | 
| Before Width: | Height: | Size: 81 B | 
| Before Width: | Height: | Size: 86 B | 
| Before Width: | Height: | Size: 89 B | 
| Before Width: | Height: | Size: 89 B | 
| Before Width: | Height: | Size: 94 B | 
| Before Width: | Height: | Size: 97 B | 
| Before Width: | Height: | Size: 97 B | 
| Before Width: | Height: | Size: 99 B | 
| Before Width: | Height: | Size: 98 B | 
| @ -1 +0,0 @@ | |||||||
| 3 |  | ||||||
| Before Width: | Height: | Size: 326 B | 
| @ -1 +0,0 @@ | |||||||
| 3 |  | ||||||
| Before Width: | Height: | Size: 106 B | 
| Before Width: | Height: | Size: 106 B | 
| Before Width: | Height: | Size: 106 B | 
| Before Width: | Height: | Size: 106 B | 
| Before Width: | Height: | Size: 106 B | 
| Before Width: | Height: | Size: 106 B | 
| @ -1 +0,0 @@ | |||||||
| 3 |  | ||||||
| Before Width: | Height: | Size: 3.5 KiB | 
| Before Width: | Height: | Size: 83 B | 
| Before Width: | Height: | Size: 81 B | 
| Before Width: | Height: | Size: 312 B | 
| Before Width: | Height: | Size: 318 B | 
| @ -1,5 +1,5 @@ | |||||||
| V:0 | V:0 | ||||||
| T:1655152832 | T:1658906571 | ||||||
| D:badusb | D:badusb | ||||||
| D:dolphin | D:dolphin | ||||||
| D:infrared | D:infrared | ||||||
| @ -232,10 +232,10 @@ F:41b4f08774249014cb8d3dffa5f5c07d:1757:nfc/assets/currency_code.nfc | |||||||
| F:c60e862919731b0bd538a1001bbc1098:17453:nfc/assets/mf_classic_dict.nfc | F:c60e862919731b0bd538a1001bbc1098:17453:nfc/assets/mf_classic_dict.nfc | ||||||
| D:subghz/assets | D:subghz/assets | ||||||
| F:dda1ef895b8a25fde57c874feaaef997:650:subghz/assets/came_atomo | F:dda1ef895b8a25fde57c874feaaef997:650:subghz/assets/came_atomo | ||||||
| F:610a0ffa2479a874f2060eb2348104c5:2712:subghz/assets/keeloq_mfcodes | F:788eef2cc74e29f3388463d6607dab0d:3264:subghz/assets/keeloq_mfcodes | ||||||
| F:9214f9c10463b746a27e82ce0b96e040:465:subghz/assets/keeloq_mfcodes_user | F:9214f9c10463b746a27e82ce0b96e040:465:subghz/assets/keeloq_mfcodes_user | ||||||
| F:653bd8d349055a41e1152e557d4a52d3:202:subghz/assets/nice_flor_s | F:653bd8d349055a41e1152e557d4a52d3:202:subghz/assets/nice_flor_s | ||||||
| F:c6ec4374275cd20f482ecd46de9f53e3:528:subghz/assets/setting_user | F:c1c63fbd5f5aa3ea504027014652191f:1150:subghz/assets/setting_user | ||||||
| D:u2f/assets | D:u2f/assets | ||||||
| F:7e11e688e39034bbb9d88410044795e1:365:u2f/assets/cert.der | F:7e11e688e39034bbb9d88410044795e1:365:u2f/assets/cert.der | ||||||
| F:f60b88c20ed479ed9684e249f7134618:264:u2f/assets/cert_key.u2f | F:f60b88c20ed479ed9684e249f7134618:264:u2f/assets/cert_key.u2f | ||||||
|  | |||||||
							
								
								
									
										90
									
								
								debug/fw.jflash
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,90 @@ | |||||||
|  |   AppVersion = 76803 | ||||||
|  |   FileVersion = 2 | ||||||
|  | [GENERAL] | ||||||
|  |   aATEModuleSel[24] = 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 | ||||||
|  |   ConnectMode = 0 | ||||||
|  |   CurrentFile = "..\build\latest\firmware.bin" | ||||||
|  |   DataFileSAddr = 0x08000000 | ||||||
|  |   GUIMode = 0 | ||||||
|  |   HostName = "" | ||||||
|  |   TargetIF = 1 | ||||||
|  |   USBPort = 0 | ||||||
|  |   USBSerialNo = 0x00000000 | ||||||
|  |   UseATEModuleSelection = 0 | ||||||
|  | [JTAG] | ||||||
|  |   IRLen = 0 | ||||||
|  |   MultipleTargets = 0 | ||||||
|  |   NumDevices = 0 | ||||||
|  |   Speed0 = 8000 | ||||||
|  |   Speed1 = 8000 | ||||||
|  |   TAP_Number = 0 | ||||||
|  |   UseAdaptive0 = 0 | ||||||
|  |   UseAdaptive1 = 0 | ||||||
|  |   UseMaxSpeed0 = 0 | ||||||
|  |   UseMaxSpeed1 = 0 | ||||||
|  | [CPU] | ||||||
|  |   NumInitSteps = 2 | ||||||
|  |   InitStep0_Action = "Reset" | ||||||
|  |   InitStep0_Value0 = 0x00000000 | ||||||
|  |   InitStep0_Value1 = 0x00000000 | ||||||
|  |   InitStep0_Comment = "" | ||||||
|  |   InitStep1_Action = "Halt" | ||||||
|  |   InitStep1_Value0 = 0xFFFFFFFF | ||||||
|  |   InitStep1_Value1 = 0xFFFFFFFF | ||||||
|  |   InitStep1_Comment = "" | ||||||
|  |   NumExitSteps = 1 | ||||||
|  |   ExitStep0_Action = "Reset" | ||||||
|  |   ExitStep0_Value0 = 0x00000005 | ||||||
|  |   ExitStep0_Value1 = 0x00000032 | ||||||
|  |   ExitStep0_Comment = "" | ||||||
|  |   UseScriptFile = 0 | ||||||
|  |   ScriptFile = "" | ||||||
|  |   UseRAM = 1 | ||||||
|  |   RAMAddr = 0x20000000 | ||||||
|  |   RAMSize = 0x00030000 | ||||||
|  |   CheckCoreID = 1 | ||||||
|  |   CoreID = 0x6BA02477 | ||||||
|  |   CoreIDMask = 0x0F000FFF | ||||||
|  |   UseAutoSpeed = 0x00000001 | ||||||
|  |   ClockSpeed = 0x00000000 | ||||||
|  |   EndianMode = 0 | ||||||
|  |   ChipName = "ST STM32WB55RG" | ||||||
|  | [FLASH] | ||||||
|  |   aRangeSel[1] = 0-255 | ||||||
|  |   BankName = "Internal flash" | ||||||
|  |   BankSelMode = 1 | ||||||
|  |   BaseAddr = 0x08000000 | ||||||
|  |   NumBanks = 1 | ||||||
|  | [PRODUCTION] | ||||||
|  |   AutoPerformsDisconnect = 0 | ||||||
|  |   AutoPerformsErase = 1 | ||||||
|  |   AutoPerformsProgram = 1 | ||||||
|  |   AutoPerformsSecure = 0 | ||||||
|  |   AutoPerformsStartApp = 1 | ||||||
|  |   AutoPerformsUnsecure = 0 | ||||||
|  |   AutoPerformsVerify = 0 | ||||||
|  |   EnableFixedVTref = 0 | ||||||
|  |   EnableTargetPower = 0 | ||||||
|  |   EraseType = 1 | ||||||
|  |   FixedVTref = 0x00000CE4 | ||||||
|  |   MonitorVTref = 0 | ||||||
|  |   MonitorVTrefMax = 0x0000157C | ||||||
|  |   MonitorVTrefMin = 0x000003E8 | ||||||
|  |   OverrideTimeouts = 0 | ||||||
|  |   ProgramSN = 0 | ||||||
|  |   SerialFile = "" | ||||||
|  |   SNAddr = 0x00000000 | ||||||
|  |   SNInc = 0x00000001 | ||||||
|  |   SNLen = 0x00000004 | ||||||
|  |   SNListFile = "" | ||||||
|  |   SNValue = 0x00000001 | ||||||
|  |   StartAppType = 1 | ||||||
|  |   TargetPowerDelay = 0x00000014 | ||||||
|  |   TimeoutErase = 0x00003A98 | ||||||
|  |   TimeoutProgram = 0x00002710 | ||||||
|  |   TimeoutVerify = 0x00002710 | ||||||
|  |   VerifyType = 1 | ||||||
|  | [PERFORMANCE] | ||||||
|  |   DisableSkipBlankDataOnProgram = 0x00000000 | ||||||
|  |   PerfromBlankCheckPriorEraseChip = 0x00000001 | ||||||
|  |   PerfromBlankCheckPriorEraseSelectedSectors = 0x00000001 | ||||||
 Aleksandr Kutuzov
						Aleksandr Kutuzov