diff --git a/.github/ISSUE_TEMPLATE/03_feature_request.yml b/.github/ISSUE_TEMPLATE/03_feature_request.yml index 2af11454..052cd340 100644 --- a/.github/ISSUE_TEMPLATE/03_feature_request.yml +++ b/.github/ISSUE_TEMPLATE/03_feature_request.yml @@ -14,7 +14,7 @@ body: description: | Please describe your feature request in as many details as possible. - Describe what it should do. - - Note whetever it is to extend existing functionality or introduce new functionality. + - Note whether it is to extend existing functionality or introduce new functionality. validations: required: true - type: textarea diff --git a/.github/assets/dark_theme_banner.png b/.github/assets/dark_theme_banner.png new file mode 100644 index 00000000..10a4e8c3 Binary files /dev/null and b/.github/assets/dark_theme_banner.png differ diff --git a/.github/assets/light_theme_banner.png b/.github/assets/light_theme_banner.png new file mode 100644 index 00000000..6c6b3b70 Binary files /dev/null and b/.github/assets/light_theme_banner.png differ diff --git a/.github/workflows/amap_analyse.yml b/.github/workflows/amap_analyse.yml deleted file mode 100644 index 1340e4cd..00000000 --- a/.github/workflows/amap_analyse.yml +++ /dev/null @@ -1,103 +0,0 @@ -name: 'Analyze .map file with Amap' - -on: - push: - branches: - - dev - - "release*" - tags: - - '*' - pull_request: - -env: - TARGETS: f7 - FBT_TOOLCHAIN_PATH: /opt - -jobs: - amap_analyse: - if: ${{ !github.event.pull_request.head.repo.fork }} - runs-on: [self-hosted,FlipperZeroMacShell] - timeout-minutes: 15 - steps: - - name: 'Wait Build workflow' - uses: fountainhead/action-wait-for-check@v1.0.0 - id: wait-for-build - with: - token: ${{ secrets.GITHUB_TOKEN }} - checkName: 'main' - ref: ${{ github.event.pull_request.head.sha || github.sha }} - intervalSeconds: 20 - - - name: 'Check Build workflow status' - if: steps.wait-for-build.outputs.conclusion == 'failure' - run: | - exit 1 - - - name: 'Decontaminate previous build leftovers' - run: | - if [ -d .git ]; then - git submodule status || git checkout "$(git rev-list --max-parents=0 HEAD | tail -n 1)" - fi - - - name: 'Checkout code' - uses: actions/checkout@v3 - with: - fetch-depth: 0 - ref: ${{ github.event.pull_request.head.sha }} - - - name: 'Get commit details' - run: | - if [[ ${{ github.event_name }} == 'pull_request' ]]; then - TYPE="pull" - elif [[ "${{ github.ref }}" == "refs/tags/"* ]]; then - TYPE="tag" - else - TYPE="other" - fi - python3 scripts/get_env.py "--event_file=${{ github.event_path }}" "--type=$TYPE" - - - name: 'Make artifacts directory' - run: | - rm -rf artifacts - mkdir artifacts - - - name: 'Download build artifacts' - run: | - mkdir -p ~/.ssh - ssh-keyscan -p ${{ secrets.RSYNC_DEPLOY_PORT }} -H ${{ secrets.RSYNC_DEPLOY_HOST }} > ~/.ssh/known_hosts - echo "${{ secrets.RSYNC_DEPLOY_KEY }}" > deploy_key; - chmod 600 ./deploy_key; - rsync -avzP \ - -e 'ssh -p ${{ secrets.RSYNC_DEPLOY_PORT }} -i ./deploy_key' \ - ${{ secrets.RSYNC_DEPLOY_USER }}@${{ secrets.RSYNC_DEPLOY_HOST }}:"${{ secrets.RSYNC_DEPLOY_BASE_PATH }}${BRANCH_NAME}/" artifacts/; - rm ./deploy_key; - - - name: 'Make .map file analyze' - run: | - cd artifacts/ - /Applications/amap/Contents/MacOS/amap -f "flipper-z-f7-firmware-${SUFFIX}.elf.map" - - - name: 'Upload report to DB' - run: | - source scripts/toolchain/fbtenv.sh - get_size() - { - SECTION="$1"; - arm-none-eabi-size \ - -A artifacts/flipper-z-f7-firmware-$SUFFIX.elf \ - | grep "^$SECTION" | awk '{print $2}' - } - export BSS_SIZE="$(get_size ".bss")" - export TEXT_SIZE="$(get_size ".text")" - export RODATA_SIZE="$(get_size ".rodata")" - export DATA_SIZE="$(get_size ".data")" - export FREE_FLASH_SIZE="$(get_size ".free_flash")" - python3 -m pip install mariadb==1.1.4 - python3 scripts/amap_mariadb_insert.py \ - ${{ secrets.AMAP_MARIADB_USER }} \ - ${{ secrets.AMAP_MARIADB_PASSWORD }} \ - ${{ secrets.AMAP_MARIADB_HOST }} \ - ${{ secrets.AMAP_MARIADB_PORT }} \ - ${{ secrets.AMAP_MARIADB_DATABASE }} \ - artifacts/flipper-z-f7-firmware-$SUFFIX.elf.map.all - diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2de0e57c..4f7233e4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -30,11 +30,6 @@ jobs: fetch-depth: 0 ref: ${{ github.event.pull_request.head.sha }} - - name: 'Make artifacts directory' - run: | - rm -rf artifacts - mkdir artifacts - - name: 'Get commit details' id: names run: | @@ -46,6 +41,15 @@ jobs: TYPE="other" fi python3 scripts/get_env.py "--event_file=${{ github.event_path }}" "--type=$TYPE" + echo random_hash=$(openssl rand -base64 40 | shasum -a 256 | awk '{print $1}') >> $GITHUB_OUTPUT + echo "event_type=$TYPE" >> $GITHUB_OUTPUT + + - name: 'Make artifacts directory' + run: | + rm -rf artifacts + rm -rf map_analyser_files + mkdir artifacts + mkdir map_analyser_files - name: 'Bundle scripts' if: ${{ !github.event.pull_request.head.repo.fork }} @@ -82,9 +86,33 @@ jobs: run: | cp build/core2_firmware.tgz "artifacts/flipper-z-any-core2_firmware-${SUFFIX}.tgz" - - name: 'Copy .map file' + - name: 'Copy map analyser files' + if: ${{ !github.event.pull_request.head.repo.fork }} run: | - cp build/f7-firmware-*/firmware.elf.map "artifacts/flipper-z-f7-firmware-${SUFFIX}.elf.map" + cp build/f7-firmware-*/firmware.elf.map map_analyser_files/firmware.elf.map + cp build/f7-firmware-*/firmware.elf map_analyser_files/firmware.elf + cp ${{ github.event_path }} map_analyser_files/event.json + + - name: 'Upload map analyser files to storage' + if: ${{ !github.event.pull_request.head.repo.fork }} + uses: keithweaver/aws-s3-github-action@v1.0.0 + with: + source: map_analyser_files/ + destination: "s3://${{ secrets.MAP_REPORT_AWS_BUCKET }}/${{steps.names.outputs.random_hash}}" + aws_access_key_id: "${{ secrets.MAP_REPORT_AWS_ACCESS_KEY }}" + aws_secret_access_key: "${{ secrets.MAP_REPORT_AWS_SECRET_KEY }}" + aws_region: "${{ secrets.MAP_REPORT_AWS_REGION }}" + flags: --recursive + + - name: 'Trigger map file reporter' + if: ${{ !github.event.pull_request.head.repo.fork }} + uses: peter-evans/repository-dispatch@v2 + with: + repository: flipperdevices/flipper-map-reporter + token: ${{ secrets.REPOSITORY_DISPATCH_TOKEN }} + event-type: map-file-analyse + client-payload: '{"random_hash": "${{steps.names.outputs.random_hash}}", "event_type": "${{steps.names.outputs.event_type}}"}' + - name: 'Upload artifacts to update server' if: ${{ !github.event.pull_request.head.repo.fork }} diff --git a/.github/workflows/pvs_studio.yml b/.github/workflows/pvs_studio.yml index 5bb04afc..46ee8801 100644 --- a/.github/workflows/pvs_studio.yml +++ b/.github/workflows/pvs_studio.yml @@ -50,7 +50,7 @@ jobs: - name: 'Generate compile_comands.json' run: | - ./fbt COMPACT=1 version_json proto_ver icons firmware_cdb dolphin_internal dolphin_blocking _fap_icons + ./fbt COMPACT=1 version_json proto_ver icons firmware_cdb dolphin_internal dolphin_blocking _fap_icons api_syms - name: 'Static code analysis' run: | @@ -58,15 +58,25 @@ jobs: pvs-studio-analyzer credentials ${{ secrets.PVS_STUDIO_CREDENTIALS }} pvs-studio-analyzer analyze \ @.pvsoptions \ + -C gccarm \ -j$(grep -c processor /proc/cpuinfo) \ -f build/f7-firmware-DC/compile_commands.json \ -o PVS-Studio.log - - name: 'Convert PVS-Studio output to html page' - run: plog-converter -a GA:1,2,3 -t fullhtml PVS-Studio.log -o reports/${DEFAULT_TARGET}-${SUFFIX} + - name: 'Convert PVS-Studio output to html and detect warnings' + id: pvs-warn + run: | + WARNINGS=0 + plog-converter \ + -a GA:1,2,3 \ + -t fullhtml \ + --indicate-warnings \ + PVS-Studio.log \ + -o reports/${DEFAULT_TARGET}-${SUFFIX} || WARNINGS=1 + echo "warnings=${WARNINGS}" >> $GITHUB_OUTPUT - name: 'Upload artifacts to update server' - if: ${{ !github.event.pull_request.head.repo.fork }} + if: ${{ !github.event.pull_request.head.repo.fork && (steps.pvs-warn.outputs.warnings != 0) }} run: | mkdir -p ~/.ssh ssh-keyscan -p ${{ secrets.RSYNC_DEPLOY_PORT }} -H ${{ secrets.RSYNC_DEPLOY_HOST }} > ~/.ssh/known_hosts @@ -78,8 +88,8 @@ jobs: rm ./deploy_key; - name: 'Find Previous Comment' - if: ${{ !github.event.pull_request.head.repo.fork && github.event.pull_request }} - uses: peter-evans/find-comment@v1 + if: ${{ !github.event.pull_request.head.repo.fork && github.event.pull_request && (steps.pvs-warn.outputs.warnings != 0) }} + uses: peter-evans/find-comment@v2 id: fc with: issue-number: ${{ github.event.pull_request.number }} @@ -87,7 +97,7 @@ jobs: body-includes: 'PVS-Studio report for commit' - name: 'Create or update 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 && (steps.pvs-warn.outputs.warnings != 0) }} uses: peter-evans/create-or-update-comment@v1 with: comment-id: ${{ steps.fc.outputs.comment-id }} @@ -96,3 +106,10 @@ jobs: **PVS-Studio report for commit `${{steps.names.outputs.commit_sha}}`:** - [Report](https://update.flipperzero.one/builds/firmware-pvs-studio-report/${{steps.names.outputs.branch_name}}/${{steps.names.outputs.default_target}}-${{steps.names.outputs.suffix}}/index.html) edit-mode: replace + + - name: 'Raise exception' + if: ${{ steps.pvs-warn.outputs.warnings != 0 }} + run: | + echo "Please fix all PVS varnings before merge" + exit 1 + diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index eb687b6c..ac3fc368 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -9,7 +9,7 @@ env: FBT_TOOLCHAIN_PATH: /opt jobs: - run_units_on_test_bench: + run_units_on_bench: runs-on: [self-hosted, FlipperZeroTest] steps: - name: 'Decontaminate previous build leftovers' @@ -29,81 +29,38 @@ jobs: run: | echo "flipper=/dev/ttyACM0" >> $GITHUB_OUTPUT - - name: 'Flashing target firmware' - id: first_full_flash - run: | - ./fbt flash_usb_full PORT=${{steps.device.outputs.flipper}} FORCE=1 - source scripts/toolchain/fbtenv.sh - python3 scripts/testing/await_flipper.py ${{steps.device.outputs.flipper}} - - - name: 'Validating updater' - id: second_full_flash - if: success() - run: | - ./fbt flash_usb_full PORT=${{steps.device.outputs.flipper}} FORCE=1 - source scripts/toolchain/fbtenv.sh - python3 scripts/testing/await_flipper.py ${{steps.device.outputs.flipper}} - - name: 'Flash unit tests firmware' id: flashing if: success() - run: | + run: | ./fbt flash OPENOCD_ADAPTER_SERIAL=2A0906016415303030303032 FIRMWARE_APP_SET=unit_tests FORCE=1 - - name: 'Wait for flipper to finish updating' - id: connect + - name: 'Wait for flipper and format ext' + id: format_ext if: steps.flashing.outcome == 'success' run: | source scripts/toolchain/fbtenv.sh python3 scripts/testing/await_flipper.py ${{steps.device.outputs.flipper}} + python3 scripts/storage.py -p ${{steps.device.outputs.flipper}} format_ext - - name: 'Copy assets and unit tests data to flipper' + - name: 'Copy assets and unit data, reboot and wait for flipper' id: copy - if: steps.connect.outcome == 'success' + if: steps.format_ext.outcome == 'success' run: | source scripts/toolchain/fbtenv.sh + python3 scripts/storage.py -p ${{steps.device.outputs.flipper}} -f send assets/resources /ext python3 scripts/storage.py -p ${{steps.device.outputs.flipper}} -f send assets/unit_tests /ext/unit_tests + python3 scripts/power.py -p ${{steps.device.outputs.flipper}} reboot + python3 scripts/testing/await_flipper.py ${{steps.device.outputs.flipper}} - name: 'Run units and validate results' + id: run_units if: steps.copy.outcome == 'success' run: | source scripts/toolchain/fbtenv.sh python3 scripts/testing/units.py ${{steps.device.outputs.flipper}} - - name: 'Get last release tag' - id: release_tag - if: always() + - name: 'Check GDB output' + if: failure() run: | - echo "tag=$(git tag -l --sort=-version:refname | grep -v "rc\|RC" | head -1)" >> $GITHUB_OUTPUT - - - name: 'Decontaminate previous build leftovers' - if: always() - run: | - if [ -d .git ]; then - git submodule status || git checkout "$(git rev-list --max-parents=0 HEAD | tail -n 1)" - fi - - - name: 'Checkout latest release' - uses: actions/checkout@v3 - if: always() - with: - fetch-depth: 0 - ref: ${{ steps.release_tag.outputs.tag }} - - - name: 'Flash last release' - if: always() - run: | - ./fbt flash OPENOCD_ADAPTER_SERIAL=2A0906016415303030303032 FIRMWARE_APP_SET=unit_tests FORCE=1 - - - name: 'Wait for flipper to finish updating' - if: always() - run: | - source scripts/toolchain/fbtenv.sh - python3 scripts/testing/await_flipper.py ${{steps.device.outputs.flipper}} - - - name: 'Format flipper SD card' - id: format - if: always() - run: | - source scripts/toolchain/fbtenv.sh - python3 scripts/storage.py -p ${{steps.device.outputs.flipper}} format_ext + ./fbt gdb_trace_all OPENOCD_ADAPTER_SERIAL=2A0906016415303030303032 FIRMWARE_APP_SET=unit_tests FORCE=1 diff --git a/.github/workflows/updater_test.yml b/.github/workflows/updater_test.yml new file mode 100644 index 00000000..d4ca56fa --- /dev/null +++ b/.github/workflows/updater_test.yml @@ -0,0 +1,77 @@ +name: 'Updater test' + +on: + pull_request: + +env: + TARGETS: f7 + DEFAULT_TARGET: f7 + FBT_TOOLCHAIN_PATH: /opt + +jobs: + test_updater_on_bench: + runs-on: [self-hosted, FlipperZeroTest] # currently on same bench as units, needs different bench + steps: + - name: 'Decontaminate previous build leftovers' + run: | + if [ -d .git ]; then + git submodule status || git checkout "$(git rev-list --max-parents=0 HEAD | tail -n 1)" + fi + + - name: Checkout code + uses: actions/checkout@v3 + with: + fetch-depth: 0 + ref: ${{ github.event.pull_request.head.sha }} + + - name: 'Get flipper from device manager (mock)' + id: device + run: | + echo "flipper=/dev/ttyACM0" >> $GITHUB_OUTPUT + + - name: 'Flashing target firmware' + id: first_full_flash + run: | + source scripts/toolchain/fbtenv.sh + ./fbt flash_usb_full PORT=${{steps.device.outputs.flipper}} FORCE=1 + python3 scripts/testing/await_flipper.py ${{steps.device.outputs.flipper}} + + - name: 'Validating updater' + id: second_full_flash + if: success() + run: | + source scripts/toolchain/fbtenv.sh + ./fbt flash_usb PORT=${{steps.device.outputs.flipper}} FORCE=1 + python3 scripts/testing/await_flipper.py ${{steps.device.outputs.flipper}} + + - name: 'Get last release tag' + id: release_tag + if: failure() + run: | + echo "tag=$(git tag -l --sort=-version:refname | grep -v "rc\|RC" | head -1)" >> $GITHUB_OUTPUT + + - name: 'Decontaminate previous build leftovers' + if: failure() + run: | + if [ -d .git ]; then + git submodule status || git checkout "$(git rev-list --max-parents=0 HEAD | tail -n 1)" + fi + + - name: 'Checkout latest release' + uses: actions/checkout@v3 + if: failure() + with: + fetch-depth: 0 + ref: ${{ steps.release_tag.outputs.tag }} + + - name: 'Flash last release' + if: failure() + run: | + ./fbt flash OPENOCD_ADAPTER_SERIAL=2A0906016415303030303032 FORCE=1 + + - name: 'Wait for flipper and format ext' + if: failure() + run: | + source scripts/toolchain/fbtenv.sh + python3 scripts/testing/await_flipper.py ${{steps.device.outputs.flipper}} + python3 scripts/storage.py -p ${{steps.device.outputs.flipper}} format_ext diff --git a/.gitignore b/.gitignore index 61c594ed..d54ed4a1 100644 --- a/.gitignore +++ b/.gitignore @@ -30,6 +30,10 @@ Brewfile.lock.json # Visual Studio Code .vscode/ +# Kate +.kateproject +.kateconfig + # legendary cmake's build CMakeLists.txt diff --git a/.pvsconfig b/.pvsconfig index 5f1ffb7c..a9ab9c9f 100644 --- a/.pvsconfig +++ b/.pvsconfig @@ -1,4 +1,5 @@ # MLib macros we can't do much about. +//-V:M_LET:1048,1044 //-V:M_EACH:1048,1044 //-V:ARRAY_DEF:760,747,568,776,729,712,654 //-V:LIST_DEF:760,747,568,712,729,654,776 @@ -16,8 +17,30 @@ # Potentially null argument warnings //-V:memset:575 //-V:memcpy:575 +//-V:memcmp:575 +//-V:strlen:575 //-V:strcpy:575 +//-V:strncpy:575 //-V:strchr:575 # For loop warning on M_FOREACH //-V:for:1044 + +# Bitwise OR +//-V:bit:792 + +# Do not complain about similar code +//-V::525 + +# Common embedded development pointer operations +//-V::566 +//-V::1032 + +# Warnings about length mismatch +//-V:property_value_out:666 + +# Model-related warnings +//-V:with_view_model:1044,1048 + +# Functions that always return the same error code +//-V:picopass_device_decrypt:1048 diff --git a/.pvsoptions b/.pvsoptions index 4c80ab66..31bc4b80 100644 --- a/.pvsoptions +++ b/.pvsoptions @@ -1 +1 @@ ---rules-config .pvsconfig -e lib/fatfs -e lib/fnv1a-hash -e lib/FreeRTOS-Kernel -e lib/heatshrink -e lib/libusb_stm32 -e lib/littlefs -e lib/mbedtls -e lib/micro-ecc -e lib/microtar -e lib/mlib -e lib/qrcode -e lib/ST25RFAL002 -e lib/STM32CubeWB -e lib/u8g2 -e */arm-none-eabi/* +--rules-config .pvsconfig -e lib/fatfs -e lib/fnv1a-hash -e lib/FreeRTOS-Kernel -e lib/heatshrink -e lib/libusb_stm32 -e lib/littlefs -e lib/mbedtls -e lib/micro-ecc -e lib/microtar -e lib/mlib -e lib/qrcode -e lib/ST25RFAL002 -e lib/STM32CubeWB -e lib/u8g2 -e lib/nanopb -e */arm-none-eabi/* -e applications/plugins/dap_link/lib/free-dap diff --git a/Brewfile b/Brewfile deleted file mode 100644 index d9ba70e0..00000000 --- a/Brewfile +++ /dev/null @@ -1,6 +0,0 @@ -cask "gcc-arm-embedded" -brew "protobuf" -brew "gdb" -brew "open-ocd" -brew "clang-format" -brew "dfu-util" diff --git a/CODING_STYLE.md b/CODING_STYLE.md index 31426b94..6c7d6d79 100644 --- a/CODING_STYLE.md +++ b/CODING_STYLE.md @@ -3,15 +3,15 @@ Nice to see you reading this document, we really appreciate it. As all documents of this kind it's unable to cover everything. -But it will cover general rules that we enforcing on PR review. +But it will cover general rules that we are enforcing on PR review. -Also we already have automatic rules checking and formatting, -but it got it's limitations and this guide is still mandatory. +Also, we already have automatic rules checking and formatting, +but it got its limitations and this guide is still mandatory. -Some part of this project do have it's own naming and coding guides. +Some part of this project do have its own naming and coding guides. For example: assets. Take a look into `ReadMe.md` in assets folder for more details. -Also 3rd party libraries are none of our concern. +Also, 3rd party libraries are none of our concern. And yes, this set is not final and we are open to discussion. If you want to add/remove/change something here please feel free to open new ticket. @@ -30,7 +30,7 @@ Our guide is inspired by, but not claiming to be compatible with: Code we write is intended to be public. Avoid one-liners from hell and keep code complexity under control. -Try to make code self explanatory and add comments if needed. +Try to make code self-explanatory and add comments if needed. Leave references to standards that you are implementing. Use project wiki to document new/reverse engineered standards. @@ -89,7 +89,7 @@ Enforced by linter. Suffixes: - `alloc` - allocate and init instance. C style constructor. Returns pointer to instance. -- `free` - deinit and release instance. C style destructor. Takes pointer to instance. +- `free` - de-init and release instance. C style destructor. Takes pointer to instance. # C++ coding style diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index dc1d41e2..3f485311 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -23,8 +23,8 @@ Before writing code and creating PR make sure that it aligns with our mission an - PR that contains code intended to commit crimes is not going to be accepted. - Your PR must comply with our [Coding Style](CODING_STYLE.md) - Your PR must contain code compatible with project [LICENSE](LICENSE). -- PR will only be merged if it pass CI/CD. -- PR will only be merged if it pass review by code owner. +- PR will only be merged if it passes CI/CD. +- PR will only be merged if it passes review by code owner. Feel free to ask questions in issues if you're not sure. @@ -59,7 +59,7 @@ Commit the changes once you are happy with them. Make sure that code compilation ### Pull Request When you're done making the changes, open a pull request, often referred to as a PR. -- Fill out the "Ready for review" template so we can review your PR. This template helps reviewers understand your changes and the purpose of your pull request. +- Fill out the "Ready for review" template, so we can review your PR. This template helps reviewers understand your changes and the purpose of your pull request. - Don't forget to [link PR to issue](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue) if you are solving one. - Enable the checkbox to [allow maintainer edits](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/allowing-changes-to-a-pull-request-branch-created-from-a-fork) so the branch can be updated for a merge. Once you submit your PR, a Docs team member will review your proposal. We may ask questions or request for additional information. diff --git a/Makefile b/Makefile deleted file mode 100644 index ff701a42..00000000 --- a/Makefile +++ /dev/null @@ -1,21 +0,0 @@ -$(info +-------------------------------------------------+) -$(info | |) -$(info | Hello, this is Flipper team speaking! |) -$(info | |) -$(info | We've migrated to new build system |) -$(info | It's nice and based on scons |) -$(info | |) -$(info | Crash course: |) -$(info | |) -$(info | `./fbt` |) -$(info | `./fbt flash` |) -$(info | `./fbt debug` |) -$(info | |) -$(info | More details in documentation/fbt.md |) -$(info | |) -$(info | Also Please leave your feedback here: |) -$(info | https://flipp.dev/4RDu |) -$(info | or |) -$(info | https://flipp.dev/2XM8 |) -$(info | |) -$(info +-------------------------------------------------+) \ No newline at end of file diff --git a/ReadMe.md b/ReadMe.md index c091d7e3..411f5b41 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -1,134 +1,115 @@ + + + + A pixel art of a Dophin with text: Flipper Zero Official Repo + + # Flipper Zero Firmware -[![Discord](https://img.shields.io/discord/740930220399525928.svg?label=&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](http://flipperzero.one/discord) +- [Flipper Zero Official Website](https://flipperzero.one). A simple way to explain to your friends what Flipper Zero can do. +- [Flipper Zero Firmware Update](https://update.flipperzero.one). Improvements for your dolphin: latest firmware releases, upgrade tools for PC and mobile devices. +- [User Documentation](https://docs.flipperzero.one). Learn more about your dolphin: specs, usage guides, and anything you want to ask. -![Show me the code](https://habrastorage.org/webt/eo/m0/e4/eom0e4btudte7nrhnyic-laiog0.png) +# Contributing -Welcome to [Flipper Zero](https://flipperzero.one/)'s Firmware repo! -Our goal is to create nice and clean code with good documentation, to make it a pleasure for everyone to work with. +Our main goal is to build a healthy and sustainable community around Flipper, so we're open to any new ideas and contributions. We also have some rules and taboos here, so please read this page and our [Code of Conduct](/CODE_OF_CONDUCT.md) carefully. -# Clone the Repository +## I need help + +The best place to search for answers is our [User Documentation](https://docs.flipperzero.one). If you can't find the answer there, check our [Discord Server](https://flipp.dev/discord) or our [Forum](https://forum.flipperzero.one/). + +## I want to report an issue + +If you've found an issue and want to report it, please check our [Issues](https://github.com/flipperdevices/flipperzero-firmware/issues) page. Make sure the description contains information about the firmware version you're using, your platform, and a clear explanation of the steps to reproduce the issue. + +## I want to contribute code + +Before opening a PR, please confirm that your changes must be contained in the firmware. Many ideas can easily be implemented as external applications and published in the Flipper Application Catalog (coming soon). If you are unsure, reach out to us on the [Discord Server](https://flipp.dev/discord) or the [Issues](https://github.com/flipperdevices/flipperzero-firmware/issues) page, and we'll help you find the right place for your code. + +Also, please read our [Contribution Guide](/CONTRIBUTING.md) and our [Coding Style](/CODING_STYLE.md), and make sure your code is compatible with our [Project License](/LICENSE). + +Finally, open a [Pull Request](https://github.com/flipperdevices/flipperzero-firmware/pulls) and make sure that CI/CD statuses are all green. + +# Development + +Flipper Zero Firmware is written in C, with some bits and pieces written in C++ and armv7m assembly languages. An intermediate level of C knowledge is recommended for comfortable programming. C, C++, and armv7m assembly languages are supported for Flipper applications. + +## Requirements + +Supported development platforms: + +- Windows 10+ with PowerShell and Git (x86_64) +- macOS 12+ with Command Line tools (x86_64, arm64) +- Ubuntu 20.04+ with build-essential and Git (x86_64) + +Supported in-circuit debuggers (optional but highly recommended): + +- [Flipper Zero Wi-Fi Development Board](https://shop.flipperzero.one/products/wifi-devboard) +- ST-Link +- J-Link + +Flipper Build System will take care of all the other dependencies. + +## Cloning source code + +Make sure you have enough space and clone the source code: -You should clone with ```shell -$ git clone --recursive https://github.com/flipperdevices/flipperzero-firmware.git +git clone --recursive https://github.com/flipperdevices/flipperzero-firmware.git ``` -# Read the Docs +## Building -Check out details on [how to build firmware](documentation/fbt.md), [write applications](documentation/AppsOnSDCard.md), [un-brick your device](documentation/KeyCombo.md) and more in `documentation` folder. +Build firmware using Flipper Build Tool: -# Update firmware - -[Get Latest Firmware from Update Server](https://update.flipperzero.one/) - -Flipper Zero's firmware consists of two components: - -- Core2 firmware set - proprietary components by ST: FUS + radio stack. FUS is flashed at factory, and you should never update it. -- Core1 Firmware - HAL + OS + Drivers + Applications. - -They both must be flashed in the order described. - -## With offline update package - -With Flipper attached over USB: - -`./fbt flash_usb` - -Just building the package: - -`./fbt updater_package` - -To update, copy the resulting directory to Flipper's SD card and navigate to `update.fuf` file in Archive app. - -## With STLink - -### Core1 Firmware - -Prerequisites: - -- Linux / macOS -- Terminal -- [arm-gcc-none-eabi](https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm/downloads) -- openocd - -One-liner: `./fbt firmware_flash` - -## With USB DFU - -1. Download latest [Firmware](https://update.flipperzero.one) - -2. Reboot Flipper to Bootloader - - Press and hold `← Left` + `↩ Back` for reset - - Release `↩ Back` and keep holding `← Left` until blue LED lights up - - Release `← Left` - -3. Run `dfu-util -D full.dfu -a 0` - -# Build on Linux/macOS - -Check out `documentation/fbt.md` for details on building and flashing firmware. - -## macOS Prerequisites - -Make sure you have [brew](https://brew.sh) and install all the dependencies: -```sh -brew bundle --verbose -``` - -## Linux Prerequisites - -The FBT tool handles everything, only `git` is required. - -### Optional dependencies - -- openocd (debugging/flashing over SWD) -- heatshrink (compiling image assets) -- clang-format (code formatting) -- dfu-util (flashing over USB DFU) -- protobuf (compiling proto sources) - -For example, to install them on Debian, use: -```sh -apt update -apt install openocd clang-format-13 dfu-util protobuf-compiler -``` - -heatshrink has to be compiled [from sources](https://github.com/atomicobject/heatshrink). - -## Compile everything - -```sh +```shell ./fbt ``` -Check `dist/` for build outputs. +## Flashing firmware using an in-circuit debugger -Use **`flipper-z-{target}-full-{suffix}.dfu`** to flash your device. +Connect your in-circuit debugger to your Flipper and flash firmware using Flipper Build Tool: -## Flash everything - -Connect your device via ST-Link and run: -```sh -./fbt firmware_flash +```shell +./fbt flash ``` -# Links +## Flashing firmware using USB -* Discord: [flipp.dev/discord](https://flipp.dev/discord) -* Website: [flipperzero.one](https://flipperzero.one) -* Kickstarter page: [kickstarter.com](https://www.kickstarter.com/projects/flipper-devices/flipper-zero-tamagochi-for-hackers) -* Forum: [forum.flipperzero.one](https://forum.flipperzero.one/) +Make sure your Flipper is on, and your firmware is functioning. Connect your Flipper with a USB cable and flash firmware using Flipper Build Tool: + +```shell +./fbt flash_usb +``` + +## Documentation + +- [Flipper Build Tool](/documentation/fbt.md) - building, flashing, and debugging Flipper software +- [Applications](/documentation/AppsOnSDCard.md), [Application Manifest](/documentation/AppManifests.md) - developing, building, deploying, and debugging Flipper applications +- [Hardware combos and Un-bricking](/documentation/KeyCombo.md) - recovering your Flipper from the most nasty situations +- [Flipper File Formats](/documentation/file_formats) - everything about how Flipper stores your data and how you can work with it +- [Universal Remotes](/documentation/UniversalRemotes.md) - contributing your infrared remote to the universal remote database +- [Firmware Roadmap](/documentation/RoadMap.md) +- And much more in the [documentation](/documentation) folder # Project structure -- `applications` - Applications and services used in firmware -- `assets` - Assets used by applications and services -- `furi` - Furi Core: os level primitives and helpers -- `debug` - Debug tool: GDB-plugins, SVD-file and etc -- `documentation` - Documentation generation system configs and input files -- `firmware` - Firmware source code -- `lib` - Our and 3rd party libraries, drivers, etc. -- `scripts` - Supplementary scripts and python libraries home +- `applications` - applications and services used in firmware +- `assets` - assets used by applications and services +- `furi` - Furi Core: OS-level primitives and helpers +- `debug` - debug tool: GDB plugins, an SVD file, etc. +- `documentation` - documentation generation system configs and input files +- `firmware` - firmware source code +- `lib` - our and 3rd party libraries, drivers, etc. +- `scripts` - supplementary scripts and python libraries home -Also pay attention to `ReadMe.md` files inside those directories. +Also, see `ReadMe.md` files inside those directories for further details. + +# Links + +- Discord: [flipp.dev/discord](https://flipp.dev/discord) +- Website: [flipperzero.one](https://flipperzero.one) +- Forum: [forum.flipperzero.one](https://forum.flipperzero.one/) +- Kickstarter: [kickstarter.com](https://www.kickstarter.com/projects/flipper-devices/flipper-zero-tamagochi-for-hackers) diff --git a/RoadMap.md b/RoadMap.md deleted file mode 100644 index 32ed860f..00000000 --- a/RoadMap.md +++ /dev/null @@ -1,51 +0,0 @@ -# RoadMap - -# Where we are (0.x.x branch) - -Our goal for 0.x.x branch is to build stable usable apps and API. -First public release that we support in this branch is 0.43.1. Your device most likely came with this version. -You can develop applications but keep in mind that API is not final yet. - -## What's already implemented - -**Applications** - -- SubGhz: all most common protocols, reading RAW for everything else -- 125kHz RFID: all most common protocols -- NFC: reading/emulating Mifare Ultralight, reading MiFare Classic and DESFire, basic EMV, basic NFC-B,F,V -- Infrared: all most common RC protocols, RAW format for everything else -- GPIO: UART bridge, basic GPIO controls -- iButton: DS1990, Cyfral, Metacom -- Bad USB: Full USB Rubber Ducky support, some extras for windows alt codes -- U2F: Full U2F specification support - -**Extras** - -- BLE Keyboard -- Snake game - -**System and HAL** - -- Furi Core -- Furi HAL - -# Where we're going (Version 1) - -Main goal for 1.0.0 is to provide first stable version for both Users and Developers. - -## What we're planning to implement in 1.0.0 - -- Loading applications from SD (tested as PoC, work scheduled for Q2) -- More protocols (gathering feedback) -- User documentation (work in progress) -- FuriCore: get rid of CMSIS API, replace hard real time timers, improve stability and performance (work in progress) -- FuriHal: deep sleep mode, stable API, examples, documentation (work in progress) -- Application improvements (a ton of things that we want to add and improve that are too numerous to list here) - -## When will it happen and where I can see the progress? - -Release 1.0.0 will most likely happen around the end of Q3 - -Development progress can be tracked in our public Miro board: - -https://miro.com/app/board/uXjVO_3D6xU=/?moveToWidget=3458764522498020058&cot=14 diff --git a/SConstruct b/SConstruct index 474175c1..138b52d9 100644 --- a/SConstruct +++ b/SConstruct @@ -194,6 +194,20 @@ firmware_bm_flash = distenv.PhonyTarget( ], ) +gdb_backtrace_all_threads = distenv.PhonyTarget( + "gdb_trace_all", + "$GDB $GDBOPTS $SOURCES $GDBFLASH", + source=firmware_env["FW_ELF"], + GDBOPTS="${GDBOPTS_BASE}", + GDBREMOTE="${OPENOCD_GDB_PIPE}", + GDBFLASH=[ + "-ex", + "thread apply all bt", + "-ex", + "quit", + ], +) + # Debugging firmware firmware_debug = distenv.PhonyTarget( "debug", diff --git a/applications/debug/direct_draw/application.fam b/applications/debug/direct_draw/application.fam new file mode 100644 index 00000000..11b3bc6b --- /dev/null +++ b/applications/debug/direct_draw/application.fam @@ -0,0 +1,10 @@ +App( + appid="direct_draw", + name="Direct Draw", + apptype=FlipperAppType.DEBUG, + entry_point="direct_draw_app", + requires=["gui", "input"], + stack_size=2 * 1024, + order=70, + fap_category="Debug", +) diff --git a/applications/debug/direct_draw/direct_draw.c b/applications/debug/direct_draw/direct_draw.c new file mode 100644 index 00000000..71c65eab --- /dev/null +++ b/applications/debug/direct_draw/direct_draw.c @@ -0,0 +1,112 @@ +#include +#include +#include +#include + +#define BUFFER_SIZE (32U) + +typedef struct { + FuriPubSub* input; + FuriPubSubSubscription* input_subscription; + Gui* gui; + Canvas* canvas; + bool stop; + uint32_t counter; +} DirectDraw; + +static void gui_input_events_callback(const void* value, void* ctx) { + furi_assert(value); + furi_assert(ctx); + + DirectDraw* instance = ctx; + const InputEvent* event = value; + + if(event->key == InputKeyBack && event->type == InputTypeShort) { + instance->stop = true; + } +} + +static DirectDraw* direct_draw_alloc() { + DirectDraw* instance = malloc(sizeof(DirectDraw)); + + instance->input = furi_record_open(RECORD_INPUT_EVENTS); + instance->gui = furi_record_open(RECORD_GUI); + instance->canvas = gui_direct_draw_acquire(instance->gui); + + instance->input_subscription = + furi_pubsub_subscribe(instance->input, gui_input_events_callback, instance); + + return instance; +} + +static void direct_draw_free(DirectDraw* instance) { + furi_pubsub_unsubscribe(instance->input, instance->input_subscription); + + instance->canvas = NULL; + gui_direct_draw_release(instance->gui); + furi_record_close(RECORD_GUI); + furi_record_close(RECORD_INPUT_EVENTS); +} + +static void direct_draw_block(Canvas* canvas, uint32_t size, uint32_t counter) { + size += 16; + uint8_t width = canvas_width(canvas) - size; + uint8_t height = canvas_height(canvas) - size; + + uint8_t x = counter % width; + if((counter / width) % 2) { + x = width - x; + } + + uint8_t y = counter % height; + if((counter / height) % 2) { + y = height - y; + } + + canvas_draw_box(canvas, x, y, size, size); +} + +static void direct_draw_run(DirectDraw* instance) { + size_t start = DWT->CYCCNT; + size_t counter = 0; + float fps = 0; + + vTaskPrioritySet(furi_thread_get_current_id(), FuriThreadPriorityIdle); + + do { + size_t elapsed = DWT->CYCCNT - start; + char buffer[BUFFER_SIZE] = {0}; + + if(elapsed >= 64000000) { + fps = (float)counter / ((float)elapsed / 64000000.0f); + + start = DWT->CYCCNT; + counter = 0; + } + snprintf(buffer, BUFFER_SIZE, "FPS: %.1f", (double)fps); + + canvas_reset(instance->canvas); + canvas_set_color(instance->canvas, ColorXOR); + direct_draw_block(instance->canvas, instance->counter % 16, instance->counter); + direct_draw_block(instance->canvas, instance->counter * 2 % 16, instance->counter * 2); + direct_draw_block(instance->canvas, instance->counter * 3 % 16, instance->counter * 3); + direct_draw_block(instance->canvas, instance->counter * 4 % 16, instance->counter * 4); + direct_draw_block(instance->canvas, instance->counter * 5 % 16, instance->counter * 5); + canvas_draw_str(instance->canvas, 10, 10, buffer); + canvas_commit(instance->canvas); + + counter++; + instance->counter++; + furi_thread_yield(); + } while(!instance->stop); +} + +int32_t direct_draw_app(void* p) { + UNUSED(p); + + DirectDraw* instance = direct_draw_alloc(); + direct_draw_run(instance); + direct_draw_free(instance); + + return 0; +} diff --git a/applications/debug/unit_tests/float_tools/float_tools_test.c b/applications/debug/unit_tests/float_tools/float_tools_test.c new file mode 100644 index 00000000..fc5b4ecf --- /dev/null +++ b/applications/debug/unit_tests/float_tools/float_tools_test.c @@ -0,0 +1,60 @@ +#include +#include + +#include "../minunit.h" + +MU_TEST(float_tools_equal_test) { + mu_check(float_is_equal(FLT_MAX, FLT_MAX)); + mu_check(float_is_equal(FLT_MIN, FLT_MIN)); + mu_check(float_is_equal(-FLT_MAX, -FLT_MAX)); + mu_check(float_is_equal(-FLT_MIN, -FLT_MIN)); + + mu_check(!float_is_equal(FLT_MIN, FLT_MAX)); + mu_check(!float_is_equal(-FLT_MIN, FLT_MAX)); + mu_check(!float_is_equal(FLT_MIN, -FLT_MAX)); + mu_check(!float_is_equal(-FLT_MIN, -FLT_MAX)); + + const float pi = 3.14159f; + mu_check(float_is_equal(pi, pi)); + mu_check(float_is_equal(-pi, -pi)); + mu_check(!float_is_equal(pi, -pi)); + mu_check(!float_is_equal(-pi, pi)); + + const float one_third = 1.f / 3.f; + const float one_third_dec = 0.3333333f; + mu_check(one_third != one_third_dec); + mu_check(float_is_equal(one_third, one_third_dec)); + + const float big_num = 1.e12f; + const float med_num = 95.389f; + const float smol_num = 1.e-12f; + mu_check(float_is_equal(big_num, big_num)); + mu_check(float_is_equal(med_num, med_num)); + mu_check(float_is_equal(smol_num, smol_num)); + mu_check(!float_is_equal(smol_num, big_num)); + mu_check(!float_is_equal(med_num, smol_num)); + mu_check(!float_is_equal(big_num, med_num)); + + const float more_than_one = 1.f + FLT_EPSILON; + const float less_than_one = 1.f - FLT_EPSILON; + mu_check(!float_is_equal(more_than_one, less_than_one)); + mu_check(!float_is_equal(more_than_one, -less_than_one)); + mu_check(!float_is_equal(-more_than_one, less_than_one)); + mu_check(!float_is_equal(-more_than_one, -less_than_one)); + + const float slightly_more_than_one = 1.f + FLT_EPSILON / 2.f; + const float slightly_less_than_one = 1.f - FLT_EPSILON / 2.f; + mu_check(float_is_equal(slightly_more_than_one, slightly_less_than_one)); + mu_check(float_is_equal(-slightly_more_than_one, -slightly_less_than_one)); + mu_check(!float_is_equal(slightly_more_than_one, -slightly_less_than_one)); + mu_check(!float_is_equal(-slightly_more_than_one, slightly_less_than_one)); +} + +MU_TEST_SUITE(float_tools_suite) { + MU_RUN_TEST(float_tools_equal_test); +} + +int run_minunit_test_float_tools() { + MU_RUN_SUITE(float_tools_suite); + return MU_EXIT_CODE; +} diff --git a/applications/debug/unit_tests/furi/furi_memmgr_test.c b/applications/debug/unit_tests/furi/furi_memmgr_test.c index cf384874..f7bc234a 100644 --- a/applications/debug/unit_tests/furi/furi_memmgr_test.c +++ b/applications/debug/unit_tests/furi/furi_memmgr_test.c @@ -3,98 +3,37 @@ #include #include -// this test is not accurate, but gives a basic understanding -// that memory management is working fine - -// do not include memmgr.h here -// we also test that we are linking against stdlib -extern size_t memmgr_get_free_heap(void); -extern size_t memmgr_get_minimum_free_heap(void); - -// current heap management realization consume: -// X bytes after allocate and 0 bytes after allocate and free, -// where X = sizeof(void*) + sizeof(size_t), look to BlockLink_t -const size_t heap_overhead_max_size = sizeof(void*) + sizeof(size_t); - -bool heap_equal(size_t heap_size, size_t heap_size_old) { - // heap borders with overhead - const size_t heap_low = heap_size_old - heap_overhead_max_size; - const size_t heap_high = heap_size_old + heap_overhead_max_size; - - // not exact, so we must test it against bigger numbers than "overhead size" - const bool result = ((heap_size >= heap_low) && (heap_size <= heap_high)); - - // debug allocation info - if(!result) { - printf("\n(hl: %zu) <= (p: %zu) <= (hh: %zu)\n", heap_low, heap_size, heap_high); - } - - return result; -} - void test_furi_memmgr() { - size_t heap_size = 0; - size_t heap_size_old = 0; - const int alloc_size = 128; - - void* ptr = NULL; - void* original_ptr = NULL; - - // do not include furi memmgr.h case -#ifdef FURI_MEMMGR_GUARD - mu_fail("do not link against furi memmgr.h"); -#endif + void* ptr; // allocate memory case - heap_size_old = memmgr_get_free_heap(); - ptr = malloc(alloc_size); - heap_size = memmgr_get_free_heap(); - mu_assert_pointers_not_eq(ptr, NULL); - mu_assert(heap_equal(heap_size, heap_size_old - alloc_size), "allocate failed"); - - // free memory case - heap_size_old = memmgr_get_free_heap(); + ptr = malloc(100); + mu_check(ptr != NULL); + // test that memory is zero-initialized after allocation + for(int i = 0; i < 100; i++) { + mu_assert_int_eq(0, ((uint8_t*)ptr)[i]); + } free(ptr); - ptr = NULL; - heap_size = memmgr_get_free_heap(); - mu_assert(heap_equal(heap_size, heap_size_old + alloc_size), "free failed"); // reallocate memory case + ptr = malloc(100); + memset(ptr, 66, 100); + ptr = realloc(ptr, 200); + mu_check(ptr != NULL); - // get filled array with some data - original_ptr = malloc(alloc_size); - mu_assert_pointers_not_eq(original_ptr, NULL); - for(int i = 0; i < alloc_size; i++) { - *(unsigned char*)(original_ptr + i) = i; + // test that memory is really reallocated + for(int i = 0; i < 100; i++) { + mu_assert_int_eq(66, ((uint8_t*)ptr)[i]); } - // malloc array and copy data - ptr = malloc(alloc_size); - mu_assert_pointers_not_eq(ptr, NULL); - memcpy(ptr, original_ptr, alloc_size); - - // reallocate array - heap_size_old = memmgr_get_free_heap(); - ptr = realloc(ptr, alloc_size * 2); - heap_size = memmgr_get_free_heap(); - mu_assert(heap_equal(heap_size, heap_size_old - alloc_size), "reallocate failed"); - mu_assert_int_eq(memcmp(original_ptr, ptr, alloc_size), 0); - free(original_ptr); + // TODO: fix realloc to copy only old size, and write testcase that leftover of reallocated memory is zero-initialized free(ptr); // allocate and zero-initialize array (calloc) - original_ptr = malloc(alloc_size); - mu_assert_pointers_not_eq(original_ptr, NULL); - - for(int i = 0; i < alloc_size; i++) { - *(unsigned char*)(original_ptr + i) = 0; + ptr = calloc(100, 2); + mu_check(ptr != NULL); + for(int i = 0; i < 100 * 2; i++) { + mu_assert_int_eq(0, ((uint8_t*)ptr)[i]); } - heap_size_old = memmgr_get_free_heap(); - ptr = calloc(1, alloc_size); - heap_size = memmgr_get_free_heap(); - mu_assert(heap_equal(heap_size, heap_size_old - alloc_size), "callocate failed"); - mu_assert_int_eq(memcmp(original_ptr, ptr, alloc_size), 0); - - free(original_ptr); free(ptr); } diff --git a/applications/debug/unit_tests/manifest/manifest.c b/applications/debug/unit_tests/manifest/manifest.c new file mode 100644 index 00000000..0b24ad1e --- /dev/null +++ b/applications/debug/unit_tests/manifest/manifest.c @@ -0,0 +1,75 @@ +#include +#include "../minunit.h" +#include + +#define TAG "Manifest" + +MU_TEST(manifest_type_test) { + mu_assert(ResourceManifestEntryTypeUnknown == 0, "ResourceManifestEntryTypeUnknown != 0\r\n"); + mu_assert(ResourceManifestEntryTypeVersion == 1, "ResourceManifestEntryTypeVersion != 1\r\n"); + mu_assert( + ResourceManifestEntryTypeTimestamp == 2, "ResourceManifestEntryTypeTimestamp != 2\r\n"); + mu_assert( + ResourceManifestEntryTypeDirectory == 3, "ResourceManifestEntryTypeDirectory != 3\r\n"); + mu_assert(ResourceManifestEntryTypeFile == 4, "ResourceManifestEntryTypeFile != 4\r\n"); +} + +MU_TEST(manifest_iteration_test) { + bool result = true; + size_t counters[5] = {0}; + + Storage* storage = furi_record_open(RECORD_STORAGE); + ResourceManifestReader* manifest_reader = resource_manifest_reader_alloc(storage); + do { + // Open manifest file + if(!resource_manifest_reader_open(manifest_reader, EXT_PATH("unit_tests/Manifest"))) { + result = false; + break; + } + + // Iterate forward + ResourceManifestEntry* entry_ptr = NULL; + while((entry_ptr = resource_manifest_reader_next(manifest_reader))) { + FURI_LOG_D(TAG, "F:%u:%s", entry_ptr->type, furi_string_get_cstr(entry_ptr->name)); + if(entry_ptr->type > 4) { + mu_fail("entry_ptr->type > 4\r\n"); + result = false; + break; + } + counters[entry_ptr->type]++; + } + if(!result) break; + + // Iterate backward + while((entry_ptr = resource_manifest_reader_previous(manifest_reader))) { + FURI_LOG_D(TAG, "B:%u:%s", entry_ptr->type, furi_string_get_cstr(entry_ptr->name)); + if(entry_ptr->type > 4) { + mu_fail("entry_ptr->type > 4\r\n"); + result = false; + break; + } + counters[entry_ptr->type]--; + } + } while(false); + + resource_manifest_reader_free(manifest_reader); + furi_record_close(RECORD_STORAGE); + + mu_assert(counters[ResourceManifestEntryTypeUnknown] == 0, "Unknown counter != 0\r\n"); + mu_assert(counters[ResourceManifestEntryTypeVersion] == 0, "Version counter != 0\r\n"); + mu_assert(counters[ResourceManifestEntryTypeTimestamp] == 0, "Timestamp counter != 0\r\n"); + mu_assert(counters[ResourceManifestEntryTypeDirectory] == 0, "Directory counter != 0\r\n"); + mu_assert(counters[ResourceManifestEntryTypeFile] == 0, "File counter != 0\r\n"); + + mu_assert(result, "Manifest forward iterate failed\r\n"); +} + +MU_TEST_SUITE(manifest_suite) { + MU_RUN_TEST(manifest_type_test); + MU_RUN_TEST(manifest_iteration_test); +} + +int run_minunit_test_manifest() { + MU_RUN_SUITE(manifest_suite); + return MU_EXIT_CODE; +} \ No newline at end of file diff --git a/applications/debug/unit_tests/stream/stream_test.c b/applications/debug/unit_tests/stream/stream_test.c index 802e3402..2fa3b21a 100644 --- a/applications/debug/unit_tests/stream/stream_test.c +++ b/applications/debug/unit_tests/stream/stream_test.c @@ -72,8 +72,32 @@ MU_TEST_1(stream_composite_subtest, Stream* stream) { mu_check(stream_seek(stream, -3, StreamOffsetFromEnd)); mu_check(stream_tell(stream) == 4); - // write string with replacemet + // test seeks to char. content: '1337_69' + stream_rewind(stream); + mu_check(stream_seek_to_char(stream, '3', StreamDirectionForward)); + mu_check(stream_tell(stream) == 1); + mu_check(stream_seek_to_char(stream, '3', StreamDirectionForward)); + mu_check(stream_tell(stream) == 2); + mu_check(stream_seek_to_char(stream, '_', StreamDirectionForward)); + mu_check(stream_tell(stream) == 4); + mu_check(stream_seek_to_char(stream, '9', StreamDirectionForward)); + mu_check(stream_tell(stream) == 6); + mu_check(!stream_seek_to_char(stream, '9', StreamDirectionForward)); + mu_check(stream_tell(stream) == 6); + mu_check(stream_seek_to_char(stream, '_', StreamDirectionBackward)); + mu_check(stream_tell(stream) == 4); + mu_check(stream_seek_to_char(stream, '3', StreamDirectionBackward)); + mu_check(stream_tell(stream) == 2); + mu_check(stream_seek_to_char(stream, '3', StreamDirectionBackward)); + mu_check(stream_tell(stream) == 1); + mu_check(!stream_seek_to_char(stream, '3', StreamDirectionBackward)); + mu_check(stream_tell(stream) == 1); + mu_check(stream_seek_to_char(stream, '1', StreamDirectionBackward)); + mu_check(stream_tell(stream) == 0); + + // write string with replacement // "1337_69" -> "1337lee" + mu_check(stream_seek(stream, 4, StreamOffsetFromStart)); mu_check(stream_write_string(stream, string_lee) == 3); mu_check(stream_size(stream) == 7); mu_check(stream_tell(stream) == 7); diff --git a/applications/debug/unit_tests/subghz/subghz_test.c b/applications/debug/unit_tests/subghz/subghz_test.c index d954ddd9..1dee1d59 100644 --- a/applications/debug/unit_tests/subghz/subghz_test.c +++ b/applications/debug/unit_tests/subghz/subghz_test.c @@ -13,7 +13,7 @@ #define CAME_ATOMO_DIR_NAME EXT_PATH("subghz/assets/came_atomo") #define NICE_FLOR_S_DIR_NAME EXT_PATH("subghz/assets/nice_flor_s") #define TEST_RANDOM_DIR_NAME EXT_PATH("unit_tests/subghz/test_random_raw.sub") -#define TEST_RANDOM_COUNT_PARSE 253 +#define TEST_RANDOM_COUNT_PARSE 273 #define TEST_TIMEOUT 10000 static SubGhzEnvironment* environment_handler; @@ -318,7 +318,10 @@ bool subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestType type) { furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok650Async); furi_hal_subghz_set_frequency_and_path(433920000); - furi_hal_subghz_start_async_tx(subghz_hal_async_tx_test_yield, &test); + if(!furi_hal_subghz_start_async_tx(subghz_hal_async_tx_test_yield, &test)) { + return false; + } + while(!furi_hal_subghz_is_async_tx_complete()) { furi_delay_ms(10); } @@ -594,6 +597,13 @@ MU_TEST(subghz_decoder_smc5326_test) { "Test decoder " SUBGHZ_PROTOCOL_SMC5326_NAME " error\r\n"); } +MU_TEST(subghz_decoder_holtek_ht12x_test) { + mu_assert( + subghz_decoder_test( + EXT_PATH("unit_tests/subghz/holtek_ht12x_raw.sub"), SUBGHZ_PROTOCOL_HOLTEK_HT12X_NAME), + "Test decoder " SUBGHZ_PROTOCOL_HOLTEK_HT12X_NAME " error\r\n"); +} + //test encoders MU_TEST(subghz_encoder_princeton_test) { mu_assert( @@ -727,6 +737,12 @@ MU_TEST(subghz_encoder_smc5326_test) { "Test encoder " SUBGHZ_PROTOCOL_SMC5326_NAME " error\r\n"); } +MU_TEST(subghz_encoder_holtek_ht12x_test) { + mu_assert( + subghz_encoder_test(EXT_PATH("unit_tests/subghz/holtek_ht12x.sub")), + "Test encoder " SUBGHZ_PROTOCOL_HOLTEK_HT12X_NAME " error\r\n"); +} + MU_TEST(subghz_random_test) { mu_assert(subghz_decode_random_test(TEST_RANDOM_DIR_NAME), "Random test error\r\n"); } @@ -771,6 +787,7 @@ MU_TEST_SUITE(subghz) { MU_RUN_TEST(subghz_decoder_clemsa_test); MU_RUN_TEST(subghz_decoder_ansonic_test); MU_RUN_TEST(subghz_decoder_smc5326_test); + MU_RUN_TEST(subghz_decoder_holtek_ht12x_test); MU_RUN_TEST(subghz_encoder_princeton_test); MU_RUN_TEST(subghz_encoder_came_test); @@ -794,6 +811,7 @@ MU_TEST_SUITE(subghz) { MU_RUN_TEST(subghz_encoder_clemsa_test); MU_RUN_TEST(subghz_encoder_ansonic_test); MU_RUN_TEST(subghz_encoder_smc5326_test); + MU_RUN_TEST(subghz_encoder_holtek_ht12x_test); MU_RUN_TEST(subghz_random_test); subghz_test_deinit(); diff --git a/applications/debug/unit_tests/test_index.c b/applications/debug/unit_tests/test_index.c index 36c2b4ae..2bb9c423 100644 --- a/applications/debug/unit_tests/test_index.c +++ b/applications/debug/unit_tests/test_index.c @@ -13,6 +13,7 @@ int run_minunit_test_furi_hal(); int run_minunit_test_furi_string(); int run_minunit_test_infrared(); int run_minunit_test_rpc(); +int run_minunit_test_manifest(); int run_minunit_test_flipper_format(); int run_minunit_test_flipper_format_string(); int run_minunit_test_stream(); @@ -24,6 +25,7 @@ int run_minunit_test_protocol_dict(); int run_minunit_test_lfrfid_protocols(); int run_minunit_test_nfc(); int run_minunit_test_bit_lib(); +int run_minunit_test_float_tools(); int run_minunit_test_bt(); typedef int (*UnitTestEntry)(); @@ -40,6 +42,7 @@ const UnitTest unit_tests[] = { {.name = "storage", .entry = run_minunit_test_storage}, {.name = "stream", .entry = run_minunit_test_stream}, {.name = "dirwalk", .entry = run_minunit_test_dirwalk}, + {.name = "manifest", .entry = run_minunit_test_manifest}, {.name = "flipper_format", .entry = run_minunit_test_flipper_format}, {.name = "flipper_format_string", .entry = run_minunit_test_flipper_format_string}, {.name = "rpc", .entry = run_minunit_test_rpc}, @@ -50,6 +53,7 @@ const UnitTest unit_tests[] = { {.name = "protocol_dict", .entry = run_minunit_test_protocol_dict}, {.name = "lfrfid", .entry = run_minunit_test_lfrfid_protocols}, {.name = "bit_lib", .entry = run_minunit_test_bit_lib}, + {.name = "float_tools", .entry = run_minunit_test_float_tools}, {.name = "bt", .entry = run_minunit_test_bt}, }; @@ -73,7 +77,6 @@ void unit_tests_cli(Cli* cli, FuriString* args, void* context) { UNUSED(cli); UNUSED(args); UNUSED(context); - uint32_t failed_tests = 0; minunit_run = 0; minunit_assert = 0; minunit_fail = 0; @@ -99,32 +102,35 @@ void unit_tests_cli(Cli* cli, FuriString* args, void* context) { if(furi_string_size(args)) { if(furi_string_cmp_str(args, unit_tests[i].name) == 0) { - failed_tests += unit_tests[i].entry(); + unit_tests[i].entry(); } else { printf("Skipping %s\r\n", unit_tests[i].name); } } else { - failed_tests += unit_tests[i].entry(); + unit_tests[i].entry(); } } - printf("\r\nFailed tests: %lu\r\n", failed_tests); - // Time report - cycle_counter = (furi_get_tick() - cycle_counter); - printf("Consumed: %lu ms\r\n", cycle_counter); + if(minunit_run != 0) { + printf("\r\nFailed tests: %u\r\n", minunit_fail); - // Wait for tested services and apps to deallocate memory - furi_delay_ms(200); - uint32_t heap_after = memmgr_get_free_heap(); - printf("Leaked: %ld\r\n", heap_before - heap_after); + // Time report + cycle_counter = (furi_get_tick() - cycle_counter); + printf("Consumed: %lu ms\r\n", cycle_counter); - // Final Report - if(failed_tests == 0) { - notification_message(notification, &sequence_success); - printf("Status: PASSED\r\n"); - } else { - notification_message(notification, &sequence_error); - printf("Status: FAILED\r\n"); + // Wait for tested services and apps to deallocate memory + furi_delay_ms(200); + uint32_t heap_after = memmgr_get_free_heap(); + printf("Leaked: %ld\r\n", heap_before - heap_after); + + // Final Report + if(minunit_fail == 0) { + notification_message(notification, &sequence_success); + printf("Status: PASSED\r\n"); + } else { + notification_message(notification, &sequence_error); + printf("Status: FAILED\r\n"); + } } } diff --git a/applications/main/archive/helpers/archive_apps.c b/applications/main/archive/helpers/archive_apps.c index 72084f11..c8ad6762 100644 --- a/applications/main/archive/helpers/archive_apps.c +++ b/applications/main/archive/helpers/archive_apps.c @@ -13,7 +13,7 @@ ArchiveAppTypeEnum archive_get_app_type(const char* path) { } app_name++; - for(size_t i = 0; i < COUNT_OF(known_apps); i++) { + for(size_t i = 0; i < COUNT_OF(known_apps); i++) { //-V1008 if(strncmp(app_name, known_apps[i], strlen(known_apps[i])) == 0) { return i; } diff --git a/applications/main/archive/helpers/archive_favorites.c b/applications/main/archive/helpers/archive_favorites.c index 86a294f7..8bbcb521 100644 --- a/applications/main/archive/helpers/archive_favorites.c +++ b/applications/main/archive/helpers/archive_favorites.c @@ -177,7 +177,7 @@ bool archive_favorites_read(void* context) { archive_set_item_count(browser, file_count); - if(need_refresh) { + if(need_refresh) { //-V547 archive_favourites_rescan(); } diff --git a/applications/main/archive/scenes/archive_scene_browser.c b/applications/main/archive/scenes/archive_scene_browser.c index 2f469354..c28f91f5 100644 --- a/applications/main/archive/scenes/archive_scene_browser.c +++ b/applications/main/archive/scenes/archive_scene_browser.c @@ -116,7 +116,7 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) { case ArchiveBrowserEventFileMenuPin: { const char* name = archive_get_name(browser); if(favorites) { - archive_favorites_delete(name); + archive_favorites_delete("%s", name); archive_file_array_rm_selected(browser); archive_show_file_menu(browser, false); } else if(archive_is_known_app(selected->type)) { diff --git a/applications/main/bad_usb/bad_usb_script.c b/applications/main/bad_usb/bad_usb_script.c index aa465351..92c7466f 100644 --- a/applications/main/bad_usb/bad_usb_script.c +++ b/applications/main/bad_usb/bad_usb_script.c @@ -218,8 +218,8 @@ static bool ducky_string(const char* param) { } static uint16_t ducky_get_keycode(const char* param, bool accept_chars) { - for(uint8_t i = 0; i < (sizeof(ducky_keys) / sizeof(ducky_keys[0])); i++) { - uint8_t key_cmd_len = strlen(ducky_keys[i].name); + for(size_t i = 0; i < (sizeof(ducky_keys) / sizeof(ducky_keys[0])); i++) { + size_t key_cmd_len = strlen(ducky_keys[i].name); if((strncmp(param, ducky_keys[i].name, key_cmd_len) == 0) && (ducky_is_line_end(param[key_cmd_len]))) { return ducky_keys[i].keycode; @@ -417,7 +417,7 @@ static int32_t ducky_script_execute_next(BadUsbScript* bad_usb, File* script_fil return 0; } else if(delay_val < 0) { // Script error bad_usb->st.error_line = bad_usb->st.line_cur - 1; - FURI_LOG_E(WORKER_TAG, "Unknown command at line %u", bad_usb->st.line_cur - 1); + FURI_LOG_E(WORKER_TAG, "Unknown command at line %u", bad_usb->st.line_cur - 1U); return SCRIPT_STATE_ERROR; } else { return (delay_val + bad_usb->defdelay); @@ -596,7 +596,9 @@ static int32_t bad_usb_worker(void* context) { } bad_usb->st.state = worker_state; continue; - } else if((flags == FuriFlagErrorTimeout) || (flags == FuriFlagErrorResource)) { + } else if( + (flags == (unsigned)FuriFlagErrorTimeout) || + (flags == (unsigned)FuriFlagErrorResource)) { if(delay_val > 0) { bad_usb->st.delay_remain--; continue; @@ -650,7 +652,7 @@ static int32_t bad_usb_worker(void* context) { BadUsbScript* bad_usb_script_open(FuriString* file_path) { furi_assert(file_path); - BadUsbScript* bad_usb = malloc(sizeof(BadUsbScript)); //-V773 + BadUsbScript* bad_usb = malloc(sizeof(BadUsbScript)); bad_usb->file_path = furi_string_alloc(); furi_string_set(bad_usb->file_path, file_path); @@ -660,7 +662,7 @@ BadUsbScript* bad_usb_script_open(FuriString* file_path) { bad_usb->thread = furi_thread_alloc_ex("BadUsbWorker", 2048, bad_usb_worker, bad_usb); furi_thread_start(bad_usb->thread); return bad_usb; -} +} //-V773 void bad_usb_script_close(BadUsbScript* bad_usb) { furi_assert(bad_usb); diff --git a/applications/main/fap_loader/fap_loader_app.c b/applications/main/fap_loader/fap_loader_app.c index 90186674..7911aa06 100644 --- a/applications/main/fap_loader/fap_loader_app.c +++ b/applications/main/fap_loader/fap_loader_app.c @@ -156,7 +156,7 @@ static bool fap_loader_select_app(FapLoader* loader) { } static FapLoader* fap_loader_alloc(const char* path) { - FapLoader* loader = malloc(sizeof(FapLoader)); //-V773 + FapLoader* loader = malloc(sizeof(FapLoader)); //-V799 loader->fap_path = furi_string_alloc_set(path); loader->storage = furi_record_open(RECORD_STORAGE); loader->dialogs = furi_record_open(RECORD_DIALOGS); @@ -167,7 +167,7 @@ static FapLoader* fap_loader_alloc(const char* path) { loader->view_dispatcher, loader->gui, ViewDispatcherTypeFullscreen); view_dispatcher_add_view(loader->view_dispatcher, 0, loading_get_view(loader->loading)); return loader; -} +} //-V773 static void fap_loader_free(FapLoader* loader) { view_dispatcher_remove_view(loader->view_dispatcher, 0); diff --git a/applications/main/ibutton/ibutton.c b/applications/main/ibutton/ibutton.c index b7c8223b..85212f42 100644 --- a/applications/main/ibutton/ibutton.c +++ b/applications/main/ibutton/ibutton.c @@ -278,7 +278,7 @@ bool ibutton_save_key(iButton* ibutton, const char* key_name) { flipper_format_free(file); - if(!result) { + if(!result) { //-V547 dialog_message_show_storage_error(ibutton->dialogs, "Cannot save\nkey file"); } @@ -302,7 +302,7 @@ void ibutton_text_store_set(iButton* ibutton, const char* text, ...) { } void ibutton_text_store_clear(iButton* ibutton) { - memset(ibutton->text_store, 0, IBUTTON_TEXT_STORE_SIZE); + memset(ibutton->text_store, 0, IBUTTON_TEXT_STORE_SIZE + 1); } void ibutton_notification_message(iButton* ibutton, uint32_t message) { @@ -343,7 +343,7 @@ int32_t ibutton_app(void* p) { } else { view_dispatcher_attach_to_gui( ibutton->view_dispatcher, ibutton->gui, ViewDispatcherTypeFullscreen); - if(key_loaded) { + if(key_loaded) { //-V547 scene_manager_next_scene(ibutton->scene_manager, iButtonSceneEmulate); DOLPHIN_DEED(DolphinDeedIbuttonEmulate); } else { diff --git a/applications/main/infrared/infrared.c b/applications/main/infrared/infrared.c index f62db14c..9d78a09b 100644 --- a/applications/main/infrared/infrared.c +++ b/applications/main/infrared/infrared.c @@ -360,7 +360,7 @@ void infrared_text_store_set(Infrared* infrared, uint32_t bank, const char* text } void infrared_text_store_clear(Infrared* infrared, uint32_t bank) { - memset(infrared->text_store[bank], 0, INFRARED_TEXT_STORE_SIZE); + memset(infrared->text_store[bank], 0, INFRARED_TEXT_STORE_SIZE + 1); } void infrared_play_notification_message(Infrared* infrared, uint32_t message) { @@ -455,7 +455,7 @@ int32_t infrared_app(void* p) { } else { view_dispatcher_attach_to_gui( infrared->view_dispatcher, infrared->gui, ViewDispatcherTypeFullscreen); - if(is_remote_loaded) { + if(is_remote_loaded) { //-V547 scene_manager_next_scene(infrared->scene_manager, InfraredSceneRemote); } else { scene_manager_next_scene(infrared->scene_manager, InfraredSceneStart); diff --git a/applications/main/infrared/infrared_brute_force.c b/applications/main/infrared/infrared_brute_force.c index 3f426f1d..3ca5c409 100644 --- a/applications/main/infrared/infrared_brute_force.c +++ b/applications/main/infrared/infrared_brute_force.c @@ -65,7 +65,7 @@ bool infrared_brute_force_calculate_messages(InfraredBruteForce* brute_force) { while(flipper_format_read_string(ff, "name", signal_name)) { InfraredBruteForceRecord* record = InfraredBruteForceRecordDict_get(brute_force->records, signal_name); - if(record) { + if(record) { //-V547 ++(record->count); } } diff --git a/applications/main/infrared/infrared_cli.c b/applications/main/infrared/infrared_cli.c index 8f35a8fd..5f5e2d4b 100644 --- a/applications/main/infrared/infrared_cli.c +++ b/applications/main/infrared/infrared_cli.c @@ -55,7 +55,7 @@ static void signal_received_callback(void* context, InfraredWorkerSignal* receiv size_t timings_cnt; infrared_worker_get_raw_signal(received_signal, &timings, &timings_cnt); - buf_cnt = snprintf(buf, sizeof(buf), "RAW, %d samples:\r\n", timings_cnt); + buf_cnt = snprintf(buf, sizeof(buf), "RAW, %zu samples:\r\n", timings_cnt); cli_write(cli, (uint8_t*)buf, buf_cnt); for(size_t i = 0; i < timings_cnt; ++i) { buf_cnt = snprintf(buf, sizeof(buf), "%lu ", timings[i]); @@ -276,7 +276,9 @@ static bool infrared_cli_decode_file(FlipperFormat* input_file, FlipperFormat* o } InfraredRawSignal* raw_signal = infrared_signal_get_raw_signal(signal); printf( - "Raw signal: %s, %u samples\r\n", furi_string_get_cstr(tmp), raw_signal->timings_size); + "Raw signal: %s, %zu samples\r\n", + furi_string_get_cstr(tmp), + raw_signal->timings_size); if(!infrared_cli_decode_raw_signal( raw_signal, decoder, output_file, furi_string_get_cstr(tmp))) break; @@ -382,7 +384,7 @@ static void infrared_cli_list_remote_signals(FuriString* remote_name) { while(flipper_format_read_string(ff, "name", signal_name)) { furi_string_set_str(key, furi_string_get_cstr(signal_name)); int* v = dict_signals_get(signals_dict, key); - if(v != NULL) { + if(v != NULL) { //-V547 (*v)++; max = M_MAX(*v, max); } else { @@ -436,7 +438,7 @@ static void break; } - printf("Sending %ld signal(s)...\r\n", record_count); + printf("Sending %lu signal(s)...\r\n", record_count); printf("Press Ctrl-C to stop.\r\n"); int records_sent = 0; diff --git a/applications/main/infrared/infrared_remote.c b/applications/main/infrared/infrared_remote.c index 3a528a65..d3dfc2cc 100644 --- a/applications/main/infrared/infrared_remote.c +++ b/applications/main/infrared/infrared_remote.c @@ -145,15 +145,14 @@ bool infrared_remote_load(InfraredRemote* remote, FuriString* path) { buf = furi_string_alloc(); FURI_LOG_I(TAG, "load file: \'%s\'", furi_string_get_cstr(path)); - bool success = flipper_format_buffered_file_open_existing(ff, furi_string_get_cstr(path)); + bool success = false; - if(success) { + do { + if(!flipper_format_buffered_file_open_existing(ff, furi_string_get_cstr(path))) break; uint32_t version; - success = flipper_format_read_header(ff, buf, &version) && - !furi_string_cmp(buf, "IR signals file") && (version == 1); - } + if(!flipper_format_read_header(ff, buf, &version)) break; + if(!furi_string_equal(buf, "IR signals file") || (version != 1)) break; - if(success) { path_extract_filename(path, buf, true); infrared_remote_clear_buttons(remote); infrared_remote_set_name(remote, furi_string_get_cstr(buf)); @@ -169,7 +168,8 @@ bool infrared_remote_load(InfraredRemote* remote, FuriString* path) { infrared_remote_button_free(button); } } - } + success = true; + } while(false); furi_string_free(buf); flipper_format_free(ff); diff --git a/applications/main/infrared/infrared_signal.c b/applications/main/infrared/infrared_signal.c index d399b958..9154dfbf 100644 --- a/applications/main/infrared/infrared_signal.c +++ b/applications/main/infrared/infrared_signal.c @@ -74,7 +74,7 @@ static bool infrared_signal_is_raw_valid(InfraredRawSignal* raw) { } else if((raw->timings_size <= 0) || (raw->timings_size > MAX_TIMINGS_AMOUNT)) { FURI_LOG_E( TAG, - "Timings amount is out of range (0 - %X): %X", + "Timings amount is out of range (0 - %X): %zX", MAX_TIMINGS_AMOUNT, raw->timings_size); return false; @@ -275,8 +275,8 @@ bool infrared_signal_search_and_read( is_name_found = furi_string_equal(name, tmp); if(is_name_found) break; } - if(!is_name_found) break; - if(!infrared_signal_read_body(signal, ff)) break; + if(!is_name_found) break; //-V547 + if(!infrared_signal_read_body(signal, ff)) break; //-V779 success = true; } while(false); diff --git a/applications/main/infrared/scenes/infrared_scene_debug.c b/applications/main/infrared/scenes/infrared_scene_debug.c index dd0609b5..20497869 100644 --- a/applications/main/infrared/scenes/infrared_scene_debug.c +++ b/applications/main/infrared/scenes/infrared_scene_debug.c @@ -26,7 +26,7 @@ bool infrared_scene_debug_on_event(void* context, SceneManagerEvent event) { InfraredRawSignal* raw = infrared_signal_get_raw_signal(signal); infrared_debug_view_set_text(debug_view, "RAW\n%d samples\n", raw->timings_size); - printf("RAW, %d samples:\r\n", raw->timings_size); + printf("RAW, %zu samples:\r\n", raw->timings_size); for(size_t i = 0; i < raw->timings_size; ++i) { printf("%lu ", raw->timings[i]); } diff --git a/applications/main/lfrfid/lfrfid.c b/applications/main/lfrfid/lfrfid.c index 2207e7e0..85a00eea 100644 --- a/applications/main/lfrfid/lfrfid.c +++ b/applications/main/lfrfid/lfrfid.c @@ -32,7 +32,7 @@ static void rpc_command_callback(RpcAppSystemEvent rpc_event, void* context) { } static LfRfid* lfrfid_alloc() { - LfRfid* lfrfid = malloc(sizeof(LfRfid)); //-V773 + LfRfid* lfrfid = malloc(sizeof(LfRfid)); lfrfid->storage = furi_record_open(RECORD_STORAGE); lfrfid->dialogs = furi_record_open(RECORD_DIALOGS); @@ -100,7 +100,7 @@ static LfRfid* lfrfid_alloc() { lfrfid->view_dispatcher, LfRfidViewRead, lfrfid_view_read_get_view(lfrfid->read_view)); return lfrfid; -} +} //-V773 static void lfrfid_free(LfRfid* lfrfid) { furi_assert(lfrfid); diff --git a/applications/main/lfrfid/lfrfid_cli.c b/applications/main/lfrfid/lfrfid_cli.c index 64027452..ce3e987e 100644 --- a/applications/main/lfrfid/lfrfid_cli.c +++ b/applications/main/lfrfid/lfrfid_cli.c @@ -87,7 +87,7 @@ static void lfrfid_cli_read(Cli* cli, FuriString* args) { uint32_t flags = furi_event_flag_wait(context.event, available_flags, FuriFlagWaitAny, 100); - if(flags != FuriFlagErrorTimeout) { + if(flags != (unsigned)FuriFlagErrorTimeout) { if(FURI_BIT(flags, LFRFIDWorkerReadDone)) { break; } @@ -153,7 +153,7 @@ static bool lfrfid_cli_parse_args(FuriString* args, ProtocolDict* dict, Protocol for(ProtocolId i = 0; i < LFRFIDProtocolMax; i++) { printf( - "\t%s, %d bytes long\r\n", + "\t%s, %zu bytes long\r\n", protocol_dict_get_name(dict, i), protocol_dict_get_data_size(dict, i)); } @@ -165,7 +165,7 @@ static bool lfrfid_cli_parse_args(FuriString* args, ProtocolDict* dict, Protocol // check data arg if(!args_read_hex_bytes(data_text, data, data_size)) { printf( - "%s data needs to be %d bytes long\r\n", + "%s data needs to be %zu bytes long\r\n", protocol_dict_get_name(dict, *protocol), data_size); break; @@ -211,7 +211,7 @@ static void lfrfid_cli_write(Cli* cli, FuriString* args) { while(!cli_cmd_interrupt_received(cli)) { uint32_t flags = furi_event_flag_wait(event, available_flags, FuriFlagWaitAny, 100); - if(flags != FuriFlagErrorTimeout) { + if(flags != (unsigned)FuriFlagErrorTimeout) { if(FURI_BIT(flags, LFRFIDWorkerWriteOK)) { printf("Written!\r\n"); break; @@ -309,9 +309,9 @@ static void lfrfid_cli_raw_analyze(Cli* cli, FuriString* args) { warn = true; } - furi_string_printf(info_string, "[%ld %ld]", pulse, duration); + furi_string_printf(info_string, "[%lu %lu]", pulse, duration); printf("%-16s", furi_string_get_cstr(info_string)); - furi_string_printf(info_string, "[%ld %ld]", pulse, duration - pulse); + furi_string_printf(info_string, "[%lu %lu]", pulse, duration - pulse); printf("%-16s", furi_string_get_cstr(info_string)); if(warn) { @@ -335,7 +335,7 @@ static void lfrfid_cli_raw_analyze(Cli* cli, FuriString* args) { total_pulse += pulse; total_duration += duration; - if(total_protocol != PROTOCOL_NO) { + if(total_protocol != PROTOCOL_NO) { //-V1051 break; } } else { @@ -346,9 +346,9 @@ static void lfrfid_cli_raw_analyze(Cli* cli, FuriString* args) { printf(" Frequency: %f\r\n", (double)frequency); printf(" Duty Cycle: %f\r\n", (double)duty_cycle); - printf(" Warns: %ld\r\n", total_warns); - printf(" Pulse sum: %ld\r\n", total_pulse); - printf("Duration sum: %ld\r\n", total_duration); + printf(" Warns: %lu\r\n", total_warns); + printf(" Pulse sum: %lu\r\n", total_pulse); + printf("Duration sum: %lu\r\n", total_duration); printf(" Average: %f\r\n", (double)((float)total_pulse / (float)total_duration)); printf(" Protocol: "); @@ -435,7 +435,7 @@ static void lfrfid_cli_raw_read(Cli* cli, FuriString* args) { while(true) { uint32_t flags = furi_event_flag_wait(event, available_flags, FuriFlagWaitAny, 100); - if(flags != FuriFlagErrorTimeout) { + if(flags != (unsigned)FuriFlagErrorTimeout) { if(FURI_BIT(flags, LFRFIDWorkerReadRawFileError)) { printf("File is not RFID raw file\r\n"); break; @@ -510,7 +510,7 @@ static void lfrfid_cli_raw_emulate(Cli* cli, FuriString* args) { while(true) { uint32_t flags = furi_event_flag_wait(event, available_flags, FuriFlagWaitAny, 100); - if(flags != FuriFlagErrorTimeout) { + if(flags != (unsigned)FuriFlagErrorTimeout) { if(FURI_BIT(flags, LFRFIDWorkerEmulateRawFileError)) { printf("File is not RFID raw file\r\n"); break; @@ -573,4 +573,4 @@ static void lfrfid_cli(Cli* cli, FuriString* args, void* context) { } furi_string_free(cmd); -} \ No newline at end of file +} diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_save_type.c b/applications/main/lfrfid/scenes/lfrfid_scene_save_type.c index 0ba51f06..dd20ae48 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_save_type.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_save_type.c @@ -1,7 +1,6 @@ #include "../lfrfid_i.h" typedef struct { - FuriString* menu_item_name[LFRFIDProtocolMax]; uint32_t line_sel; } SaveTypeCtx; @@ -16,25 +15,27 @@ void lfrfid_scene_save_type_on_enter(void* context) { Submenu* submenu = app->submenu; SaveTypeCtx* state = malloc(sizeof(SaveTypeCtx)); + FuriString* protocol_string = furi_string_alloc(); for(uint8_t i = 0; i < LFRFIDProtocolMax; i++) { if(strcmp( protocol_dict_get_manufacturer(app->dict, i), protocol_dict_get_name(app->dict, i)) != 0) { - state->menu_item_name[i] = furi_string_alloc_printf( + furi_string_printf( + protocol_string, "%s %s", protocol_dict_get_manufacturer(app->dict, i), protocol_dict_get_name(app->dict, i)); } else { - state->menu_item_name[i] = - furi_string_alloc_printf("%s", protocol_dict_get_name(app->dict, i)); + furi_string_printf(protocol_string, "%s", protocol_dict_get_name(app->dict, i)); } submenu_add_item( submenu, - furi_string_get_cstr(state->menu_item_name[i]), + furi_string_get_cstr(protocol_string), i, lfrfid_scene_save_type_submenu_callback, app); } + furi_string_free(protocol_string); submenu_set_selected_item( submenu, scene_manager_get_scene_state(app->scene_manager, LfRfidSceneSaveType)); @@ -73,13 +74,7 @@ void lfrfid_scene_save_type_on_exit(void* context) { submenu_reset(app->submenu); - for(uint8_t i = 0; i < LFRFIDProtocolMax; i++) { - furi_string_free(state->menu_item_name[i]); - } - uint32_t line_sel = state->line_sel; - free(state); - scene_manager_set_scene_state(app->scene_manager, LfRfidSceneSaveType, line_sel); } diff --git a/applications/main/nfc/scenes/nfc_scene_emulate_uid.c b/applications/main/nfc/scenes/nfc_scene_emulate_uid.c index f9019767..7316eebe 100644 --- a/applications/main/nfc/scenes/nfc_scene_emulate_uid.c +++ b/applications/main/nfc/scenes/nfc_scene_emulate_uid.c @@ -39,7 +39,7 @@ static void nfc_scene_emulate_uid_widget_config(Nfc* nfc, bool data_received) { widget_add_icon_element(widget, 0, 3, &I_NFC_dolphin_emulation_47x61); widget_add_string_element(widget, 57, 13, AlignLeft, AlignTop, FontPrimary, "Emulating UID"); - if(strcmp(nfc->dev->dev_name, "")) { + if(strcmp(nfc->dev->dev_name, "") != 0) { furi_string_printf(info_str, "%s", nfc->dev->dev_name); } else { for(uint8_t i = 0; i < data->uid_len; i++) { diff --git a/applications/main/nfc/scenes/nfc_scene_extra_actions.c b/applications/main/nfc/scenes/nfc_scene_extra_actions.c index fc6021d7..717e8efc 100644 --- a/applications/main/nfc/scenes/nfc_scene_extra_actions.c +++ b/applications/main/nfc/scenes/nfc_scene_extra_actions.c @@ -43,7 +43,7 @@ bool nfc_scene_extra_actions_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == SubmenuIndexMfClassicKeys) { - if(mf_classic_dict_check_presence(MfClassicDictTypeFlipper)) { + if(mf_classic_dict_check_presence(MfClassicDictTypeSystem)) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicKeys); } else { scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound); diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c index acb5b783..b82bf552 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c @@ -53,10 +53,10 @@ static void nfc_scene_mf_classic_dict_attack_prepare_view(Nfc* nfc, DictAttackSt // Setup view if(state == DictAttackStateUserDictInProgress) { worker_state = NfcWorkerStateMfClassicDictAttack; - dict_attack_set_header(nfc->dict_attack, "Mf Classic User Dict."); + dict_attack_set_header(nfc->dict_attack, "MF Classic User Dictionary"); dict = mf_classic_dict_alloc(MfClassicDictTypeUser); - // If failed to load user dictionary - try flipper dictionary + // If failed to load user dictionary - try the system dictionary if(!dict) { FURI_LOG_E(TAG, "User dictionary not found"); state = DictAttackStateFlipperDictInProgress; @@ -64,11 +64,11 @@ static void nfc_scene_mf_classic_dict_attack_prepare_view(Nfc* nfc, DictAttackSt } if(state == DictAttackStateFlipperDictInProgress) { worker_state = NfcWorkerStateMfClassicDictAttack; - dict_attack_set_header(nfc->dict_attack, "Mf Classic Flipper Dict."); - dict = mf_classic_dict_alloc(MfClassicDictTypeFlipper); + dict_attack_set_header(nfc->dict_attack, "MF Classic System Dictionary"); + dict = mf_classic_dict_alloc(MfClassicDictTypeSystem); if(!dict) { FURI_LOG_E(TAG, "Flipper dictionary not found"); - // Pass through to let worker handle the failure + // Pass through to let the worker handle the failure } } // Free previous dictionary @@ -153,6 +153,15 @@ bool nfc_scene_mf_classic_dict_attack_on_event(void* context, SceneManagerEvent nfc_worker_stop(nfc->worker); consumed = true; } + } else if(event.event == NfcWorkerEventKeyAttackStart) { + dict_attack_set_key_attack( + nfc->dict_attack, + true, + nfc->dev->dev_data.mf_classic_dict_attack_data.current_sector); + } else if(event.event == NfcWorkerEventKeyAttackStop) { + dict_attack_set_key_attack(nfc->dict_attack, false, 0); + } else if(event.event == NfcWorkerEventKeyAttackNextSector) { + dict_attack_inc_key_attack_current_sector(nfc->dict_attack); } } else if(event.type == SceneManagerEventTypeBack) { scene_manager_next_scene(nfc->scene_manager, NfcSceneExitConfirm); diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_emulate.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_emulate.c index 1bd9a85a..8c0f493e 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_emulate.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_emulate.c @@ -18,7 +18,7 @@ void nfc_scene_mf_classic_emulate_on_enter(void* context) { // Setup view Popup* popup = nfc->popup; popup_set_header(popup, "Emulating", 67, 13, AlignLeft, AlignTop); - if(strcmp(nfc->dev->dev_name, "")) { + if(strcmp(nfc->dev->dev_name, "") != 0) { nfc_text_store_set(nfc, "%s", nfc->dev->dev_name); } else { nfc_text_store_set(nfc, "MIFARE\nClassic"); diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys.c index 54cc18d3..8a7dc2c1 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys.c @@ -12,7 +12,7 @@ void nfc_scene_mf_classic_keys_on_enter(void* context) { // Load flipper dict keys total uint32_t flipper_dict_keys_total = 0; - MfClassicDict* dict = mf_classic_dict_alloc(MfClassicDictTypeFlipper); + MfClassicDict* dict = mf_classic_dict_alloc(MfClassicDictTypeSystem); if(dict) { flipper_dict_keys_total = mf_classic_dict_get_total_keys(dict); mf_classic_dict_free(dict); @@ -26,11 +26,11 @@ void nfc_scene_mf_classic_keys_on_enter(void* context) { } widget_add_string_element( - nfc->widget, 0, 0, AlignLeft, AlignTop, FontPrimary, "Mifare Classic Keys"); + nfc->widget, 0, 0, AlignLeft, AlignTop, FontPrimary, "MIFARE Classic Keys"); char temp_str[32]; - snprintf(temp_str, sizeof(temp_str), "Flipper list: %ld", flipper_dict_keys_total); + snprintf(temp_str, sizeof(temp_str), "System dict: %lu", flipper_dict_keys_total); widget_add_string_element(nfc->widget, 0, 20, AlignLeft, AlignTop, FontSecondary, temp_str); - snprintf(temp_str, sizeof(temp_str), "User list: %ld", user_dict_keys_total); + snprintf(temp_str, sizeof(temp_str), "User dict: %lu", user_dict_keys_total); widget_add_string_element(nfc->widget, 0, 32, AlignLeft, AlignTop, FontSecondary, temp_str); widget_add_button_element( nfc->widget, GuiButtonTypeCenter, "Add", nfc_scene_mf_classic_keys_widget_callback, nfc); diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_list.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_list.c index 19d2f556..57f9fe65 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_list.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_list.c @@ -27,7 +27,7 @@ void nfc_scene_mf_classic_keys_list_prepare(Nfc* nfc, MfClassicDict* dict) { char* current_key = (char*)malloc(sizeof(char) * 13); strncpy(current_key, furi_string_get_cstr(temp_key), 12); MfClassicUserKeys_push_back(nfc->mfc_key_strs, current_key); - FURI_LOG_D("ListKeys", "Key %ld: %s", index, current_key); + FURI_LOG_D("ListKeys", "Key %lu: %s", index, current_key); submenu_add_item( submenu, current_key, index++, nfc_scene_mf_classic_keys_list_submenu_callback, nfc); } diff --git a/applications/main/nfc/scenes/nfc_scene_mf_desfire_app.c b/applications/main/nfc/scenes/nfc_scene_mf_desfire_app.c index afc5f0de..882dc5fe 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_desfire_app.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_desfire_app.c @@ -51,23 +51,18 @@ void nfc_scene_mf_desfire_app_on_enter(void* context) { nfc_scene_mf_desfire_app_submenu_callback, nfc); - uint16_t cap = NFC_TEXT_STORE_SIZE; - char* buf = nfc->text_store; + FuriString* label = furi_string_alloc(); int idx = SubmenuIndexDynamic; for(MifareDesfireFile* file = app->file_head; file; file = file->next) { - int size = snprintf(buf, cap, "File %d", file->id); - if(size < 0 || size >= cap) { - FURI_LOG_W( - TAG, - "Exceeded NFC_TEXT_STORE_SIZE when preparing file id strings; menu truncated"); - break; - } - char* label = buf; - cap -= size + 1; - buf += size + 1; + furi_string_printf(label, "File %d", file->id); submenu_add_item( - nfc->submenu, label, idx++, nfc_scene_mf_desfire_app_submenu_callback, nfc); + nfc->submenu, + furi_string_get_cstr(label), + idx++, + nfc_scene_mf_desfire_app_submenu_callback, + nfc); } + furi_string_free(label); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); } diff --git a/applications/main/nfc/scenes/nfc_scene_mf_desfire_data.c b/applications/main/nfc/scenes/nfc_scene_mf_desfire_data.c index e619d037..c7caee8d 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_desfire_data.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_desfire_data.c @@ -33,21 +33,18 @@ void nfc_scene_mf_desfire_data_on_enter(void* context) { nfc_scene_mf_desfire_data_submenu_callback, nfc); - uint16_t cap = NFC_TEXT_STORE_SIZE; - char* buf = nfc->text_store; + FuriString* label = furi_string_alloc(); int idx = SubmenuIndexDynamic; for(MifareDesfireApplication* app = data->app_head; app; app = app->next) { - int size = snprintf(buf, cap, "App %02x%02x%02x", app->id[0], app->id[1], app->id[2]); - if(size < 0 || size >= cap) { - FURI_LOG_W( - TAG, "Exceeded NFC_TEXT_STORE_SIZE when preparing app id strings; menu truncated"); - break; - } - char* label = buf; - cap -= size + 1; - buf += size + 1; - submenu_add_item(submenu, label, idx++, nfc_scene_mf_desfire_data_submenu_callback, nfc); + furi_string_printf(label, "App %02x%02x%02x", app->id[0], app->id[1], app->id[2]); + submenu_add_item( + submenu, + furi_string_get_cstr(label), + idx++, + nfc_scene_mf_desfire_data_submenu_callback, + nfc); } + furi_string_free(label); if(state >= MifareDesfireDataStateItem) { submenu_set_selected_item( diff --git a/applications/main/nfc/scenes/nfc_scene_mf_desfire_read_success.c b/applications/main/nfc/scenes/nfc_scene_mf_desfire_read_success.c index 2ab0355c..39030397 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_desfire_read_success.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_desfire_read_success.c @@ -26,13 +26,13 @@ void nfc_scene_mf_desfire_read_success_on_enter(void* context) { furi_string_cat_printf(temp_str, " %02X", nfc_data->uid[i]); } - uint32_t bytes_total = 1 << (data->version.sw_storage >> 1); + uint32_t bytes_total = 1UL << (data->version.sw_storage >> 1); uint32_t bytes_free = data->free_memory ? data->free_memory->bytes : 0; - furi_string_cat_printf(temp_str, "\n%ld", bytes_total); + furi_string_cat_printf(temp_str, "\n%lu", bytes_total); if(data->version.sw_storage & 1) { furi_string_push_back(temp_str, '+'); } - furi_string_cat_printf(temp_str, " bytes, %ld bytes free\n", bytes_free); + furi_string_cat_printf(temp_str, " bytes, %lu bytes free\n", bytes_free); uint16_t n_apps = 0; uint16_t n_files = 0; diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_emulate.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_emulate.c index c9c617cb..9d8f17f9 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_emulate.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_emulate.c @@ -21,7 +21,7 @@ void nfc_scene_mf_ultralight_emulate_on_enter(void* context) { (type == MfUltralightTypeUnknown); Popup* popup = nfc->popup; popup_set_header(popup, "Emulating", 67, 13, AlignLeft, AlignTop); - if(strcmp(nfc->dev->dev_name, "")) { + if(strcmp(nfc->dev->dev_name, "") != 0) { nfc_text_store_set(nfc, "%s", nfc->dev->dev_name); } else if(is_ultralight) { nfc_text_store_set(nfc, "MIFARE\nUltralight"); diff --git a/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c b/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c index d1767a45..b44ab782 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c +++ b/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c @@ -57,13 +57,13 @@ void nfc_scene_nfc_data_info_on_enter(void* context) { // Set application specific data if(protocol == NfcDeviceProtocolMifareDesfire) { MifareDesfireData* data = &dev_data->mf_df_data; - uint32_t bytes_total = 1 << (data->version.sw_storage >> 1); + uint32_t bytes_total = 1UL << (data->version.sw_storage >> 1); uint32_t bytes_free = data->free_memory ? data->free_memory->bytes : 0; - furi_string_cat_printf(temp_str, "\n%ld", bytes_total); + furi_string_cat_printf(temp_str, "\n%lu", bytes_total); if(data->version.sw_storage & 1) { furi_string_push_back(temp_str, '+'); } - furi_string_cat_printf(temp_str, " bytes, %ld bytes free\n", bytes_free); + furi_string_cat_printf(temp_str, " bytes, %lu bytes free\n", bytes_free); uint16_t n_apps = 0; uint16_t n_files = 0; @@ -147,4 +147,4 @@ void nfc_scene_nfc_data_info_on_exit(void* context) { Nfc* nfc = context; widget_reset(nfc->widget); -} \ No newline at end of file +} diff --git a/applications/main/nfc/scenes/nfc_scene_read.c b/applications/main/nfc/scenes/nfc_scene_read.c index a64d4d00..4252883b 100644 --- a/applications/main/nfc/scenes/nfc_scene_read.c +++ b/applications/main/nfc/scenes/nfc_scene_read.c @@ -71,7 +71,7 @@ bool nfc_scene_read_on_event(void* context, SceneManagerEvent event) { } else if(event.event == NfcWorkerEventReadMfUltralight) { notification_message(nfc->notifications, &sequence_success); // Set unlock password input to 0xFFFFFFFF only on fresh read - memset(nfc->byte_input_store, 0xFF, 4); + memset(nfc->byte_input_store, 0xFF, sizeof(nfc->byte_input_store)); scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadSuccess); DOLPHIN_DEED(DolphinDeedNfcReadSuccess); consumed = true; @@ -91,7 +91,7 @@ bool nfc_scene_read_on_event(void* context, SceneManagerEvent event) { DOLPHIN_DEED(DolphinDeedNfcReadSuccess); consumed = true; } else if(event.event == NfcWorkerEventReadMfClassicDictAttackRequired) { - if(mf_classic_dict_check_presence(MfClassicDictTypeFlipper)) { + if(mf_classic_dict_check_presence(MfClassicDictTypeSystem)) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicDictAttack); } else { scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound); diff --git a/applications/main/nfc/scenes/nfc_scene_save_name.c b/applications/main/nfc/scenes/nfc_scene_save_name.c index ca4b1a35..00727422 100644 --- a/applications/main/nfc/scenes/nfc_scene_save_name.c +++ b/applications/main/nfc/scenes/nfc_scene_save_name.c @@ -55,7 +55,7 @@ bool nfc_scene_save_name_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == NfcCustomEventTextInputDone) { - if(strcmp(nfc->dev->dev_name, "")) { + if(strcmp(nfc->dev->dev_name, "") != 0) { nfc_device_delete(nfc->dev, true); } if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetUid)) { diff --git a/applications/main/nfc/scenes/nfc_scene_start.c b/applications/main/nfc/scenes/nfc_scene_start.c index f8b9f52c..2a116fe0 100644 --- a/applications/main/nfc/scenes/nfc_scene_start.c +++ b/applications/main/nfc/scenes/nfc_scene_start.c @@ -7,7 +7,7 @@ enum SubmenuIndex { SubmenuIndexDetectReader, SubmenuIndexSaved, SubmenuIndexExtraAction, - SubmenuIndexAddManualy, + SubmenuIndexAddManually, SubmenuIndexDebug, }; @@ -28,7 +28,7 @@ void nfc_scene_start_on_enter(void* context) { submenu_add_item( submenu, "Extra Actions", SubmenuIndexExtraAction, nfc_scene_start_submenu_callback, nfc); submenu_add_item( - submenu, "Add Manually", SubmenuIndexAddManualy, nfc_scene_start_submenu_callback, nfc); + submenu, "Add Manually", SubmenuIndexAddManually, nfc_scene_start_submenu_callback, nfc); if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { submenu_add_item( @@ -68,7 +68,7 @@ bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) { } else if(event.event == SubmenuIndexExtraAction) { scene_manager_next_scene(nfc->scene_manager, NfcSceneExtraActions); consumed = true; - } else if(event.event == SubmenuIndexAddManualy) { + } else if(event.event == SubmenuIndexAddManually) { scene_manager_next_scene(nfc->scene_manager, NfcSceneSetType); consumed = true; } else if(event.event == SubmenuIndexDebug) { diff --git a/applications/main/nfc/views/dict_attack.c b/applications/main/nfc/views/dict_attack.c index c5f55ae7..a539e514 100644 --- a/applications/main/nfc/views/dict_attack.c +++ b/applications/main/nfc/views/dict_attack.c @@ -24,6 +24,8 @@ typedef struct { uint8_t keys_found; uint16_t dict_keys_total; uint16_t dict_keys_current; + bool is_key_attack; + uint8_t key_attack_current_sector; } DictAttackViewModel; static void dict_attack_draw_callback(Canvas* canvas, void* model) { @@ -36,10 +38,19 @@ static void dict_attack_draw_callback(Canvas* canvas, void* model) { canvas, 64, 23, AlignCenter, AlignTop, "Make sure the tag is\npositioned correctly."); } else if(m->state == DictAttackStateRead) { char draw_str[32] = {}; - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned( - canvas, 64, 2, AlignCenter, AlignTop, furi_string_get_cstr(m->header)); canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned( + canvas, 64, 0, AlignCenter, AlignTop, furi_string_get_cstr(m->header)); + if(m->is_key_attack) { + snprintf( + draw_str, + sizeof(draw_str), + "Reuse key check for sector: %d", + m->key_attack_current_sector); + } else { + snprintf(draw_str, sizeof(draw_str), "Unlocking sector: %d", m->sector_current); + } + canvas_draw_str_aligned(canvas, 0, 10, AlignLeft, AlignTop, draw_str); float dict_progress = m->dict_keys_total == 0 ? 0 : (float)(m->dict_keys_current) / (float)(m->dict_keys_total); @@ -49,13 +60,20 @@ static void dict_attack_draw_callback(Canvas* canvas, void* model) { if(progress > 1.0) { progress = 1.0; } - elements_progress_bar(canvas, 5, 15, 120, progress); + if(m->dict_keys_current == 0) { + // Cause when people see 0 they think it's broken + snprintf(draw_str, sizeof(draw_str), "%d/%d", 1, m->dict_keys_total); + } else { + snprintf( + draw_str, sizeof(draw_str), "%d/%d", m->dict_keys_current, m->dict_keys_total); + } + elements_progress_bar_with_text(canvas, 0, 20, 128, dict_progress, draw_str); canvas_set_font(canvas, FontSecondary); snprintf(draw_str, sizeof(draw_str), "Keys found: %d/%d", m->keys_found, m->keys_total); - canvas_draw_str_aligned(canvas, 1, 28, AlignLeft, AlignTop, draw_str); + canvas_draw_str_aligned(canvas, 0, 33, AlignLeft, AlignTop, draw_str); snprintf( draw_str, sizeof(draw_str), "Sectors Read: %d/%d", m->sectors_read, m->sectors_total); - canvas_draw_str_aligned(canvas, 1, 40, AlignLeft, AlignTop, draw_str); + canvas_draw_str_aligned(canvas, 0, 43, AlignLeft, AlignTop, draw_str); } elements_button_center(canvas, "Skip"); } @@ -113,6 +131,7 @@ void dict_attack_reset(DictAttack* dict_attack) { model->keys_found = 0; model->dict_keys_total = 0; model->dict_keys_current = 0; + model->is_key_attack = false; furi_string_reset(model->header); }, false); @@ -235,3 +254,28 @@ void dict_attack_inc_current_dict_key(DictAttack* dict_attack, uint16_t keys_tri }, true); } + +void dict_attack_set_key_attack(DictAttack* dict_attack, bool is_key_attack, uint8_t sector) { + furi_assert(dict_attack); + with_view_model( + dict_attack->view, + DictAttackViewModel * model, + { + model->is_key_attack = is_key_attack; + model->key_attack_current_sector = sector; + }, + true); +} + +void dict_attack_inc_key_attack_current_sector(DictAttack* dict_attack) { + furi_assert(dict_attack); + with_view_model( + dict_attack->view, + DictAttackViewModel * model, + { + if(model->key_attack_current_sector < model->sectors_total) { + model->key_attack_current_sector++; + } + }, + true); +} diff --git a/applications/main/nfc/views/dict_attack.h b/applications/main/nfc/views/dict_attack.h index 684f17f0..2839534a 100644 --- a/applications/main/nfc/views/dict_attack.h +++ b/applications/main/nfc/views/dict_attack.h @@ -38,3 +38,7 @@ void dict_attack_inc_keys_found(DictAttack* dict_attack); void dict_attack_set_total_dict_keys(DictAttack* dict_attack, uint16_t dict_keys_total); void dict_attack_inc_current_dict_key(DictAttack* dict_attack, uint16_t keys_tried); + +void dict_attack_set_key_attack(DictAttack* dict_attack, bool is_key_attack, uint8_t sector); + +void dict_attack_inc_key_attack_current_sector(DictAttack* dict_attack); \ No newline at end of file diff --git a/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c b/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c index 7463cfaf..5d1a80a3 100644 --- a/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c +++ b/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c @@ -2,6 +2,7 @@ #include #include +#include #define TAG "SubghzFrequencyAnalyzerWorker" @@ -197,7 +198,7 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) { rssi_temp = (rssi_temp + frequency_rssi.rssi_fine) / 2; frequency_temp = frequency_rssi.frequency_fine; - if(instance->filVal) { + if(!float_is_equal(instance->filVal, 0.f)) { frequency_rssi.frequency_fine = subghz_frequency_analyzer_worker_expRunningAverageAdaptive( instance, frequency_rssi.frequency_fine); @@ -219,7 +220,7 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) { instance->sample_hold_counter = 20; rssi_temp = (rssi_temp + frequency_rssi.rssi_coarse) / 2; frequency_temp = frequency_rssi.frequency_coarse; - if(instance->filVal) { + if(!float_is_equal(instance->filVal, 0.f)) { frequency_rssi.frequency_coarse = subghz_frequency_analyzer_worker_expRunningAverageAdaptive( instance, frequency_rssi.frequency_coarse); diff --git a/applications/main/subghz/scenes/subghz_scene_config.h b/applications/main/subghz/scenes/subghz_scene_config.h index fd1271c8..86a30731 100644 --- a/applications/main/subghz/scenes/subghz_scene_config.h +++ b/applications/main/subghz/scenes/subghz_scene_config.h @@ -23,3 +23,4 @@ ADD_SCENE(subghz, more_raw, MoreRAW) ADD_SCENE(subghz, delete_raw, DeleteRAW) ADD_SCENE(subghz, need_saving, NeedSaving) ADD_SCENE(subghz, rpc, Rpc) +ADD_SCENE(subghz, region_info, RegionInfo) diff --git a/applications/main/subghz/scenes/subghz_scene_read_raw.c b/applications/main/subghz/scenes/subghz_scene_read_raw.c index b270dd48..6f95c416 100644 --- a/applications/main/subghz/scenes/subghz_scene_read_raw.c +++ b/applications/main/subghz/scenes/subghz_scene_read_raw.c @@ -3,6 +3,7 @@ #include #include #include +#include #define RAW_FILE_NAME "Raw_signal_" #define TAG "SubGhzSceneReadRAW" @@ -358,7 +359,7 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { float rssi = furi_hal_subghz_get_rssi(); - if(subghz->txrx->raw_threshold_rssi == SUBGHZ_RAW_TRESHOLD_MIN) { + if(float_is_equal(subghz->txrx->raw_threshold_rssi, SUBGHZ_RAW_TRESHOLD_MIN)) { subghz_read_raw_add_data_rssi(subghz->subghz_read_raw, rssi, true); subghz_protocol_raw_save_to_file_pause( (SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result, false); diff --git a/applications/main/subghz/scenes/subghz_scene_region_info.c b/applications/main/subghz/scenes/subghz_scene_region_info.c new file mode 100644 index 00000000..82486314 --- /dev/null +++ b/applications/main/subghz/scenes/subghz_scene_region_info.c @@ -0,0 +1,39 @@ +#include "../subghz_i.h" + +#include + +void subghz_scene_region_info_on_enter(void* context) { + SubGhz* subghz = context; + const FuriHalRegion* const region = furi_hal_region_get(); + FuriString* buffer; + buffer = furi_string_alloc(); + if(region) { + furi_string_cat_printf(buffer, "Region: %s, bands:\n", region->country_code); + for(uint16_t i = 0; i < region->bands_count; ++i) { + furi_string_cat_printf( + buffer, + " %lu-%lu kHz\n", + region->bands[i].start / 1000, + region->bands[i].end / 1000); + } + } else { + furi_string_cat_printf(buffer, "Region: N/A\n"); + } + + widget_add_string_multiline_element( + subghz->widget, 0, 0, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(buffer)); + + furi_string_free(buffer); + view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdWidget); +} + +bool subghz_scene_region_info_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void subghz_scene_region_info_on_exit(void* context) { + SubGhz* subghz = context; + widget_reset(subghz->widget); +} diff --git a/applications/main/subghz/scenes/subghz_scene_save_name.c b/applications/main/subghz/scenes/subghz_scene_save_name.c index 33846c28..255ba228 100644 --- a/applications/main/subghz/scenes/subghz_scene_save_name.c +++ b/applications/main/subghz/scenes/subghz_scene_save_name.c @@ -94,7 +94,7 @@ bool subghz_scene_save_name_on_event(void* context, SceneManagerEvent event) { return true; } else if(event.type == SceneManagerEventTypeCustom) { if(event.event == SubGhzCustomEventSceneSaveName) { - if(strcmp(subghz->file_name_tmp, "")) { + if(strcmp(subghz->file_name_tmp, "") != 0) { furi_string_cat_printf( subghz->file_path, "/%s%s", subghz->file_name_tmp, SUBGHZ_APP_EXTENSION); if(subghz_path_is_file(subghz->file_path_tmp)) { diff --git a/applications/main/subghz/scenes/subghz_scene_set_type.c b/applications/main/subghz/scenes/subghz_scene_set_type.c index 2ed53719..eaa3ccef 100644 --- a/applications/main/subghz/scenes/subghz_scene_set_type.c +++ b/applications/main/subghz/scenes/subghz_scene_set_type.c @@ -46,7 +46,7 @@ bool subghz_scene_set_type_submenu_gen_data_protocol( uint8_t key_data[sizeof(uint64_t)] = {0}; for(size_t i = 0; i < sizeof(uint64_t); i++) { - key_data[sizeof(uint64_t) - i - 1] = (key >> i * 8) & 0xFF; + key_data[sizeof(uint64_t) - i - 1] = (key >> (i * 8)) & 0xFF; } if(!flipper_format_update_hex(subghz->txrx->fff_data, "Key", key_data, sizeof(uint64_t))) { FURI_LOG_E(TAG, "Unable to update Key"); diff --git a/applications/main/subghz/scenes/subghz_scene_start.c b/applications/main/subghz/scenes/subghz_scene_start.c index 0b1c3c15..a50f73a8 100644 --- a/applications/main/subghz/scenes/subghz_scene_start.c +++ b/applications/main/subghz/scenes/subghz_scene_start.c @@ -5,9 +5,10 @@ enum SubmenuIndex { SubmenuIndexRead = 10, SubmenuIndexSaved, SubmenuIndexTest, - SubmenuIndexAddManualy, + SubmenuIndexAddManually, SubmenuIndexFrequencyAnalyzer, SubmenuIndexReadRAW, + SubmenuIndexShowRegionInfo }; void subghz_scene_start_submenu_callback(void* context, uint32_t index) { @@ -33,7 +34,7 @@ void subghz_scene_start_on_enter(void* context) { submenu_add_item( subghz->submenu, "Add Manually", - SubmenuIndexAddManualy, + SubmenuIndexAddManually, subghz_scene_start_submenu_callback, subghz); submenu_add_item( @@ -42,6 +43,12 @@ void subghz_scene_start_on_enter(void* context) { SubmenuIndexFrequencyAnalyzer, subghz_scene_start_submenu_callback, subghz); + submenu_add_item( + subghz->submenu, + "Region Information", + SubmenuIndexShowRegionInfo, + subghz_scene_start_submenu_callback, + subghz); if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { submenu_add_item( subghz->submenu, "Test", SubmenuIndexTest, subghz_scene_start_submenu_callback, subghz); @@ -76,9 +83,9 @@ bool subghz_scene_start_on_event(void* context, SceneManagerEvent event) { subghz->scene_manager, SubGhzSceneStart, SubmenuIndexSaved); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaved); return true; - } else if(event.event == SubmenuIndexAddManualy) { + } else if(event.event == SubmenuIndexAddManually) { scene_manager_set_scene_state( - subghz->scene_manager, SubGhzSceneStart, SubmenuIndexAddManualy); + subghz->scene_manager, SubGhzSceneStart, SubmenuIndexAddManually); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSetType); return true; } else if(event.event == SubmenuIndexFrequencyAnalyzer) { @@ -92,6 +99,11 @@ bool subghz_scene_start_on_event(void* context, SceneManagerEvent event) { subghz->scene_manager, SubGhzSceneStart, SubmenuIndexTest); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneTest); return true; + } else if(event.event == SubmenuIndexShowRegionInfo) { + scene_manager_set_scene_state( + subghz->scene_manager, SubGhzSceneStart, SubmenuIndexShowRegionInfo); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneRegionInfo); + return true; } } return false; diff --git a/applications/main/subghz/subghz_cli.c b/applications/main/subghz/subghz_cli.c index b6471b33..536cb535 100644 --- a/applications/main/subghz/subghz_cli.c +++ b/applications/main/subghz/subghz_cli.c @@ -152,11 +152,11 @@ void subghz_cli_command_tx(Cli* cli, FuriString* args, void* context) { "Protocol: Princeton\n" "Bit: 24\n" "Key: 00 00 00 00 00 %02X %02X %02X\n" - "TE: %ld\n" - "Repeat: %ld\n", - (uint8_t)((key >> 16) & 0xFF), - (uint8_t)((key >> 8) & 0xFF), - (uint8_t)(key & 0xFF), + "TE: %lu\n" + "Repeat: %lu\n", + (uint8_t)((key >> 16) & 0xFFU), + (uint8_t)((key >> 8) & 0xFFU), + (uint8_t)(key & 0xFFU), te, repeat); FlipperFormat* flipper_format = flipper_format_string_alloc(); @@ -300,7 +300,7 @@ void subghz_cli_command_rx(Cli* cli, FuriString* args, void* context) { furi_hal_power_suppress_charge_exit(); - printf("\r\nPackets received %u\r\n", instance->packet_count); + printf("\r\nPackets received %zu\r\n", instance->packet_count); // Cleanup subghz_receiver_free(receiver); @@ -787,8 +787,9 @@ static bool subghz_on_system_start_istream_decode_band( } region->bands_count += 1; - region = - realloc(region, sizeof(FuriHalRegion) + sizeof(FuriHalRegionBand) * region->bands_count); + region = realloc( //-V701 + region, + sizeof(FuriHalRegion) + sizeof(FuriHalRegionBand) * region->bands_count); size_t pos = region->bands_count - 1; region->bands[pos].start = band.start; region->bands[pos].end = band.end; @@ -798,7 +799,7 @@ static bool subghz_on_system_start_istream_decode_band( FURI_LOG_I( "SubGhzOnStart", - "Add allowed band: start %ldHz, stop %ldHz, power_limit %ddBm, duty_cycle %d%%", + "Add allowed band: start %luHz, stop %luHz, power_limit %ddBm, duty_cycle %u%%", band.start, band.end, band.power_limit, diff --git a/applications/main/subghz/subghz_i.c b/applications/main/subghz/subghz_i.c index 0bcd7006..7de020a5 100644 --- a/applications/main/subghz/subghz_i.c +++ b/applications/main/subghz/subghz_i.c @@ -164,7 +164,7 @@ bool subghz_tx_start(SubGhz* subghz, FlipperFormat* flipper_format) { if(subghz->txrx->transmitter) { if(subghz_transmitter_deserialize(subghz->txrx->transmitter, flipper_format)) { - if(strcmp(furi_string_get_cstr(subghz->txrx->preset->name), "")) { + if(strcmp(furi_string_get_cstr(subghz->txrx->preset->name), "") != 0) { subghz_begin( subghz, subghz_setting_get_preset_data_by_name( @@ -544,11 +544,8 @@ void subghz_hopper_update(SubGhz* subghz) { switch(subghz->txrx->hopper_state) { case SubGhzHopperStateOFF: - return; - break; case SubGhzHopperStatePause: return; - break; case SubGhzHopperStateRSSITimeOut: if(subghz->txrx->hopper_timeout != 0) { subghz->txrx->hopper_timeout--; diff --git a/applications/main/subghz/views/subghz_frequency_analyzer.c b/applications/main/subghz/views/subghz_frequency_analyzer.c index 12989840..94419084 100644 --- a/applications/main/subghz/views/subghz_frequency_analyzer.c +++ b/applications/main/subghz/views/subghz_frequency_analyzer.c @@ -11,6 +11,7 @@ #include "../helpers/subghz_frequency_analyzer_log_item_array.h" #include +#include #define LOG_FREQUENCY_MAX_ITEMS 60 // uint8_t (limited by 'seq' of SubGhzFrequencyAnalyzerLogItem) @@ -47,7 +48,8 @@ typedef struct { } SubGhzFrequencyAnalyzerModel; static inline uint8_t rssi_sanitize(float rssi) { - return (rssi ? (uint8_t)(rssi - SUBGHZ_FREQUENCY_ANALYZER_THRESHOLD) : 0); + return ( + !float_is_equal(rssi, 0.f) ? (uint8_t)(rssi - SUBGHZ_FREQUENCY_ANALYZER_THRESHOLD) : 0); } void subghz_frequency_analyzer_set_callback( @@ -294,9 +296,6 @@ static bool subghz_frequency_analyzer_log_frequency_insert(SubGhzFrequencyAnalyz if(items_count < LOG_FREQUENCY_MAX_ITEMS) { SubGhzFrequencyAnalyzerLogItem_t* item = SubGhzFrequencyAnalyzerLogItemArray_push_new(model->log_frequency); - if(item == NULL) { - return false; - } (*item)->frequency = model->frequency; (*item)->count = 1; (*item)->rssi_max = model->rssi; @@ -340,7 +339,7 @@ void subghz_frequency_analyzer_pair_callback( float rssi, bool signal) { SubGhzFrequencyAnalyzer* instance = context; - if((rssi == 0.f) && (instance->locked)) { + if(float_is_equal(rssi, 0.f) && instance->locked) { if(instance->callback) { instance->callback(SubGhzCustomEventSceneAnalyzerUnlock, instance->context); } @@ -355,13 +354,13 @@ void subghz_frequency_analyzer_pair_callback( model->history_frequency[0] = model->frequency; }, false); - } else if((rssi != 0.f) && (!instance->locked)) { + } else if(!float_is_equal(rssi, 0.f) && !instance->locked) { if(instance->callback) { instance->callback(SubGhzCustomEventSceneAnalyzerLock, instance->context); } } - instance->locked = (rssi != 0.f); + instance->locked = !float_is_equal(rssi, 0.f); with_view_model( instance->view, SubGhzFrequencyAnalyzerModel * model, diff --git a/applications/main/subghz/views/subghz_read_raw.c b/applications/main/subghz/views/subghz_read_raw.c index 6120a210..87c8a308 100644 --- a/applications/main/subghz/views/subghz_read_raw.c +++ b/applications/main/subghz/views/subghz_read_raw.c @@ -91,7 +91,7 @@ void subghz_read_raw_update_sample_write(SubGhzReadRAW* instance, size_t sample) with_view_model( instance->view, SubGhzReadRAWModel * model, - { furi_string_printf(model->sample_write, "%d spl.", sample); }, + { furi_string_printf(model->sample_write, "%zu spl.", sample); }, false); } @@ -161,7 +161,7 @@ void subghz_read_raw_draw_sin(Canvas* canvas, SubGhzReadRAWModel* model) { canvas_draw_line( canvas, i + 1, - 32 - subghz_read_raw_tab_sin((i + model->ind_sin * 16)) / SUBGHZ_RAW_SIN_AMPLITUDE, + 32 - subghz_read_raw_tab_sin(i + model->ind_sin * 16) / SUBGHZ_RAW_SIN_AMPLITUDE, i + 2, 32 + subghz_read_raw_tab_sin((i + model->ind_sin * 16 + 1) * 2) / SUBGHZ_RAW_SIN_AMPLITUDE); diff --git a/applications/main/subghz/views/subghz_test_packet.c b/applications/main/subghz/views/subghz_test_packet.c index a42898f7..43502180 100644 --- a/applications/main/subghz/views/subghz_test_packet.c +++ b/applications/main/subghz/views/subghz_test_packet.c @@ -114,7 +114,7 @@ static void subghz_test_packet_draw(Canvas* canvas, SubGhzTestPacketModel* model snprintf(buffer, sizeof(buffer), "Path: %d - %s", model->path, path_name); canvas_draw_str(canvas, 0, 31, buffer); - snprintf(buffer, sizeof(buffer), "Packets: %d", model->packets); + snprintf(buffer, sizeof(buffer), "Packets: %zu", model->packets); canvas_draw_str(canvas, 0, 42, buffer); if(model->status == SubGhzTestPacketModelStatusRx) { diff --git a/applications/main/u2f/scenes/u2f_scene_main.c b/applications/main/u2f/scenes/u2f_scene_main.c index 60ed71c7..af7f1159 100644 --- a/applications/main/u2f/scenes/u2f_scene_main.c +++ b/applications/main/u2f/scenes/u2f_scene_main.c @@ -58,7 +58,7 @@ bool u2f_scene_main_on_event(void* context, SceneManagerEvent event) { app->event_cur = event.event; if(event.event == U2fCustomEventRegister) u2f_view_set_state(app->u2f_view, U2fMsgRegister); - else if(event.event == U2fCustomEventAuth) + else if(event.event == U2fCustomEventAuth) //-V547 u2f_view_set_state(app->u2f_view, U2fMsgAuth); notification_message(app->notifications, &sequence_display_backlight_on); notification_message(app->notifications, &sequence_single_vibro); diff --git a/applications/main/u2f/u2f_data.c b/applications/main/u2f/u2f_data.c index 900af462..66604d16 100644 --- a/applications/main/u2f/u2f_data.c +++ b/applications/main/u2f/u2f_data.c @@ -402,9 +402,9 @@ bool u2f_data_cnt_read(uint32_t* cnt_val) { FURI_LOG_E(TAG, "Unable to load encryption key"); break; } - memset(&cnt, 0, 32); - if(!furi_hal_crypto_decrypt(cnt_encr, (uint8_t*)&cnt, 32)) { - memset(&cnt, 0, 32); + memset(&cnt, 0, sizeof(U2fCounterData)); + if(!furi_hal_crypto_decrypt(cnt_encr, (uint8_t*)&cnt, sizeof(U2fCounterData))) { + memset(&cnt, 0, sizeof(U2fCounterData)); FURI_LOG_E(TAG, "Decryption failed"); break; } diff --git a/applications/main/u2f/u2f_hid.c b/applications/main/u2f/u2f_hid.c index 6e1a51f3..9b625c1f 100644 --- a/applications/main/u2f/u2f_hid.c +++ b/applications/main/u2f/u2f_hid.c @@ -94,7 +94,7 @@ static void u2f_hid_send_response(U2fHid* u2f_hid) { uint16_t data_ptr = 0; memset(packet_buf, 0, HID_U2F_PACKET_LEN); - memcpy(packet_buf, &(u2f_hid->packet.cid), 4); + memcpy(packet_buf, &(u2f_hid->packet.cid), sizeof(uint32_t)); //-V1086 // Init packet packet_buf[4] = u2f_hid->packet.cmd; @@ -166,7 +166,7 @@ static bool u2f_hid_parse_request(U2fHid* u2f_hid) { return false; u2f_hid->packet.len = 17; uint32_t random_cid = furi_hal_random_get(); - memcpy(&(u2f_hid->packet.payload[8]), &random_cid, 4); + memcpy(&(u2f_hid->packet.payload[8]), &random_cid, sizeof(uint32_t)); //-V1086 u2f_hid->packet.payload[12] = 2; // Protocol version u2f_hid->packet.payload[13] = 1; // Device version major u2f_hid->packet.payload[14] = 0; // Device version minor @@ -177,7 +177,7 @@ static bool u2f_hid_parse_request(U2fHid* u2f_hid) { } else if(u2f_hid->packet.cmd == U2F_HID_WINK) { // WINK - notify user if(u2f_hid->packet.len != 0) return false; u2f_wink(u2f_hid->u2f_instance); - u2f_hid->packet.len = 0; + u2f_hid->packet.len = 0; //-V1048 u2f_hid_send_response(u2f_hid); } else return false; diff --git a/applications/plugins/dap_link/usb/dap_v2_usb.c b/applications/plugins/dap_link/usb/dap_v2_usb.c index b42df283..cba78664 100644 --- a/applications/plugins/dap_link/usb/dap_v2_usb.c +++ b/applications/plugins/dap_link/usb/dap_v2_usb.c @@ -568,12 +568,12 @@ void dap_common_usb_set_state_callback(DapStateCallback callback) { static void* dap_usb_alloc_string_descr(const char* str) { furi_assert(str); - uint8_t len = strlen(str); - uint8_t wlen = (len + 1) * sizeof(uint16_t); + size_t len = strlen(str); + size_t wlen = (len + 1) * sizeof(uint16_t); struct usb_string_descriptor* dev_str_desc = malloc(wlen); dev_str_desc->bLength = wlen; dev_str_desc->bDescriptorType = USB_DTYPE_STRING; - for(uint8_t i = 0; i < len; i++) { + for(size_t i = 0; i < len; i++) { dev_str_desc->wString[i] = str[i]; } @@ -974,4 +974,4 @@ static usbd_respond hid_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_cal } return usbd_fail; -} \ No newline at end of file +} diff --git a/applications/plugins/hid_app/assets/Ble_disconnected_15x15.png b/applications/plugins/hid_app/assets/Ble_disconnected_15x15.png index 0858bb93..bd54646d 100644 Binary files a/applications/plugins/hid_app/assets/Ble_disconnected_15x15.png and b/applications/plugins/hid_app/assets/Ble_disconnected_15x15.png differ diff --git a/applications/plugins/hid_app/hid.c b/applications/plugins/hid_app/hid.c index 1d2235e0..7f63f0cc 100644 --- a/applications/plugins/hid_app/hid.c +++ b/applications/plugins/hid_app/hid.c @@ -42,10 +42,12 @@ static void bt_hid_connection_status_changed_callback(BtStatus status, void* con furi_assert(context); Hid* hid = context; bool connected = (status == BtStatusConnected); - if(connected) { - notification_internal_message(hid->notifications, &sequence_set_blue_255); - } else { - notification_internal_message(hid->notifications, &sequence_reset_blue); + if(hid->transport == HidTransportBle) { + if(connected) { + notification_internal_message(hid->notifications, &sequence_set_blue_255); + } else { + notification_internal_message(hid->notifications, &sequence_reset_blue); + } } hid_keynote_set_connected_status(hid->hid_keynote, connected); hid_keyboard_set_connected_status(hid->hid_keyboard, connected); @@ -186,7 +188,9 @@ void hid_free(Hid* app) { furi_assert(app); // Reset notification - notification_internal_message(app->notifications, &sequence_reset_blue); + if(app->transport == HidTransportBle) { + notification_internal_message(app->notifications, &sequence_reset_blue); + } // Free views view_dispatcher_remove_view(app->view_dispatcher, HidViewSubmenu); diff --git a/applications/plugins/hid_app/views/hid_keyboard.c b/applications/plugins/hid_app/views/hid_keyboard.c index dff4a7df..8b12e8fd 100644 --- a/applications/plugins/hid_app/views/hid_keyboard.c +++ b/applications/plugins/hid_app/views/hid_keyboard.c @@ -25,6 +25,7 @@ typedef struct { bool back_pressed; bool connected; char key_string[5]; + HidTransport transport; } HidKeyboardModel; typedef struct { @@ -207,7 +208,7 @@ static void hid_keyboard_draw_callback(Canvas* canvas, void* context) { HidKeyboardModel* model = context; // Header - if(!model->connected) { + if((!model->connected) && (model->transport == HidTransportBle)) { canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15); canvas_set_font(canvas, FontPrimary); elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Keyboard"); @@ -249,30 +250,19 @@ static void hid_keyboard_draw_callback(Canvas* canvas, void* context) { static uint8_t hid_keyboard_get_selected_key(HidKeyboardModel* model) { HidKeyboardKey key = hid_keyboard_keyset[model->y][model->x]; - // Use upper case if shift is toggled - bool useUppercase = model->shift; - // Check if the key has an upper case version - bool hasUppercase = key.shift_key != 0; - if(useUppercase && hasUppercase) - return key.value; - else - return key.value; + return key.value; } static void hid_keyboard_get_select_key(HidKeyboardModel* model, HidKeyboardPoint delta) { // Keep going until a valid spot is found, this allows for nulls and zero width keys in the map do { - if(((int8_t)model->y) + delta.y < 0) - model->y = ROW_COUNT - 1; - else - model->y = (model->y + delta.y) % ROW_COUNT; + const int delta_sum = model->y + delta.y; + model->y = delta_sum < 0 ? ROW_COUNT - 1 : delta_sum % ROW_COUNT; } while(delta.y != 0 && hid_keyboard_keyset[model->y][model->x].value == 0); do { - if(((int8_t)model->x) + delta.x < 0) - model->x = COLUMN_COUNT - 1; - else - model->x = (model->x + delta.x) % COLUMN_COUNT; + const int delta_sum = model->x + delta.x; + model->x = delta_sum < 0 ? COLUMN_COUNT - 1 : delta_sum % COLUMN_COUNT; } while(delta.x != 0 && hid_keyboard_keyset[model->y][model->x].width == 0); // Skip zero width keys, pretend they are one key } @@ -372,6 +362,12 @@ HidKeyboard* hid_keyboard_alloc(Hid* bt_hid) { view_set_draw_callback(hid_keyboard->view, hid_keyboard_draw_callback); view_set_input_callback(hid_keyboard->view, hid_keyboard_input_callback); + with_view_model( + hid_keyboard->view, + HidKeyboardModel * model, + { model->transport = bt_hid->transport; }, + true); + return hid_keyboard; } diff --git a/applications/plugins/hid_app/views/hid_keynote.c b/applications/plugins/hid_app/views/hid_keynote.c index c95f4278..5e5eeb79 100644 --- a/applications/plugins/hid_app/views/hid_keynote.c +++ b/applications/plugins/hid_app/views/hid_keynote.c @@ -19,6 +19,7 @@ typedef struct { bool ok_pressed; bool back_pressed; bool connected; + HidTransport transport; } HidKeynoteModel; static void hid_keynote_draw_arrow(Canvas* canvas, uint8_t x, uint8_t y, CanvasDirection dir) { @@ -39,11 +40,14 @@ static void hid_keynote_draw_callback(Canvas* canvas, void* context) { HidKeynoteModel* model = context; // Header - if(model->connected) { - canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15); - } else { - canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15); + if(model->transport == HidTransportBle) { + if(model->connected) { + canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15); + } else { + canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15); + } } + canvas_set_font(canvas, FontPrimary); elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Keynote"); @@ -186,6 +190,9 @@ HidKeynote* hid_keynote_alloc(Hid* hid) { view_set_draw_callback(hid_keynote->view, hid_keynote_draw_callback); view_set_input_callback(hid_keynote->view, hid_keynote_input_callback); + with_view_model( + hid_keynote->view, HidKeynoteModel * model, { model->transport = hid->transport; }, true); + return hid_keynote; } diff --git a/applications/plugins/hid_app/views/hid_media.c b/applications/plugins/hid_app/views/hid_media.c index 5d764f73..468529d5 100644 --- a/applications/plugins/hid_app/views/hid_media.c +++ b/applications/plugins/hid_app/views/hid_media.c @@ -21,6 +21,7 @@ typedef struct { bool down_pressed; bool ok_pressed; bool connected; + HidTransport transport; } HidMediaModel; static void hid_media_draw_arrow(Canvas* canvas, uint8_t x, uint8_t y, CanvasDirection dir) { @@ -41,11 +42,14 @@ static void hid_media_draw_callback(Canvas* canvas, void* context) { HidMediaModel* model = context; // Header - if(model->connected) { - canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15); - } else { - canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15); + if(model->transport == HidTransportBle) { + if(model->connected) { + canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15); + } else { + canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15); + } } + canvas_set_font(canvas, FontPrimary); elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Media"); canvas_set_font(canvas, FontSecondary); @@ -190,6 +194,9 @@ HidMedia* hid_media_alloc(Hid* hid) { view_set_draw_callback(hid_media->view, hid_media_draw_callback); view_set_input_callback(hid_media->view, hid_media_input_callback); + with_view_model( + hid_media->view, HidMediaModel * model, { model->transport = hid->transport; }, true); + return hid_media; } diff --git a/applications/plugins/hid_app/views/hid_mouse.c b/applications/plugins/hid_app/views/hid_mouse.c index d1d76c15..30a9d9d0 100644 --- a/applications/plugins/hid_app/views/hid_mouse.c +++ b/applications/plugins/hid_app/views/hid_mouse.c @@ -20,6 +20,7 @@ typedef struct { bool left_mouse_held; bool right_mouse_pressed; bool connected; + HidTransport transport; } HidMouseModel; static void hid_mouse_draw_callback(Canvas* canvas, void* context) { @@ -27,11 +28,14 @@ static void hid_mouse_draw_callback(Canvas* canvas, void* context) { HidMouseModel* model = context; // Header - if(model->connected) { - canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15); - } else { - canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15); + if(model->transport == HidTransportBle) { + if(model->connected) { + canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15); + } else { + canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15); + } } + canvas_set_font(canvas, FontPrimary); elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Mouse"); canvas_set_font(canvas, FontSecondary); @@ -198,6 +202,9 @@ HidMouse* hid_mouse_alloc(Hid* hid) { view_set_draw_callback(hid_mouse->view, hid_mouse_draw_callback); view_set_input_callback(hid_mouse->view, hid_mouse_input_callback); + with_view_model( + hid_mouse->view, HidMouseModel * model, { model->transport = hid->transport; }, true); + return hid_mouse; } diff --git a/applications/plugins/hid_app/views/hid_mouse_jiggler.c b/applications/plugins/hid_app/views/hid_mouse_jiggler.c index a2b07c7a..d8f1f892 100644 --- a/applications/plugins/hid_app/views/hid_mouse_jiggler.c +++ b/applications/plugins/hid_app/views/hid_mouse_jiggler.c @@ -16,6 +16,7 @@ typedef struct { bool connected; bool running; uint8_t counter; + HidTransport transport; } HidMouseJigglerModel; static void hid_mouse_jiggler_draw_callback(Canvas* canvas, void* context) { @@ -23,11 +24,14 @@ static void hid_mouse_jiggler_draw_callback(Canvas* canvas, void* context) { HidMouseJigglerModel* model = context; // Header - if(model->connected) { - canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15); - } else { - canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15); + if(model->transport == HidTransportBle) { + if(model->connected) { + canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15); + } else { + canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15); + } } + canvas_set_font(canvas, FontPrimary); elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Mouse Jiggler"); @@ -120,6 +124,12 @@ HidMouseJiggler* hid_mouse_jiggler_alloc(Hid* hid) { hid_mouse_jiggler->timer = furi_timer_alloc( hid_mouse_jiggler_timer_callback, FuriTimerTypePeriodic, hid_mouse_jiggler); + with_view_model( + hid_mouse_jiggler->view, + HidMouseJigglerModel * model, + { model->transport = hid->transport; }, + true); + return hid_mouse_jiggler; } diff --git a/applications/plugins/hid_app/views/hid_tiktok.c b/applications/plugins/hid_app/views/hid_tiktok.c index 42abf414..e1f9f4be 100644 --- a/applications/plugins/hid_app/views/hid_tiktok.c +++ b/applications/plugins/hid_app/views/hid_tiktok.c @@ -19,6 +19,7 @@ typedef struct { bool ok_pressed; bool connected; bool is_cursor_set; + HidTransport transport; } HidTikTokModel; static void hid_tiktok_draw_callback(Canvas* canvas, void* context) { @@ -26,11 +27,14 @@ static void hid_tiktok_draw_callback(Canvas* canvas, void* context) { HidTikTokModel* model = context; // Header - if(model->connected) { - canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15); - } else { - canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15); + if(model->transport == HidTransportBle) { + if(model->connected) { + canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15); + } else { + canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15); + } } + canvas_set_font(canvas, FontPrimary); elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "TikTok"); canvas_set_font(canvas, FontSecondary); @@ -207,6 +211,9 @@ HidTikTok* hid_tiktok_alloc(Hid* bt_hid) { view_set_draw_callback(hid_tiktok->view, hid_tiktok_draw_callback); view_set_input_callback(hid_tiktok->view, hid_tiktok_input_callback); + with_view_model( + hid_tiktok->view, HidTikTokModel * model, { model->transport = bt_hid->transport; }, true); + return hid_tiktok; } diff --git a/applications/plugins/music_player/music_player.c b/applications/plugins/music_player/music_player.c index 07d4e2df..28127a57 100644 --- a/applications/plugins/music_player/music_player.c +++ b/applications/plugins/music_player/music_player.c @@ -180,7 +180,7 @@ static void render_callback(Canvas* canvas, void* ctx) { // note stack view_port x_pos = 73; - y_pos = 0; + y_pos = 0; //-V1048 canvas_set_color(canvas, ColorBlack); canvas_set_font(canvas, FontPrimary); canvas_draw_frame(canvas, x_pos, y_pos, 49, 64); diff --git a/applications/plugins/picopass/lib/loclass/optimized_cipher.c b/applications/plugins/picopass/lib/loclass/optimized_cipher.c index eba95538..94df07ba 100644 --- a/applications/plugins/picopass/lib/loclass/optimized_cipher.c +++ b/applications/plugins/picopass/lib/loclass/optimized_cipher.c @@ -111,9 +111,9 @@ static void init_opt_select_LUT(void) { ***********************************************************************************/ #define loclass_opt__select(x, y, r) \ - (4 & (((r & (r << 2)) >> 5) ^ ((r & ~(r << 2)) >> 4) ^ ((r | r << 2) >> 3))) | \ - (2 & (((r | r << 2) >> 6) ^ ((r | r << 2) >> 1) ^ (r >> 5) ^ r ^ ((x ^ y) << 1))) | \ - (1 & (((r & ~(r << 2)) >> 4) ^ ((r & (r << 2)) >> 3) ^ r ^ x)) + (4 & ((((r) & ((r) << 2)) >> 5) ^ (((r) & ~((r) << 2)) >> 4) ^ (((r) | (r) << 2) >> 3))) | \ + (2 & ((((r) | (r) << 2) >> 6) ^ (((r) | (r) << 2) >> 1) ^ ((r) >> 5) ^ (r) ^ (((x) ^ (y)) << 1))) | \ + (1 & ((((r) & ~((r) << 2)) >> 4) ^ (((r) & ((r) << 2)) >> 3) ^ (r) ^ (x))) static void loclass_opt_successor(const uint8_t* k, LoclassState_t* s, uint8_t y) { uint16_t Tt = s->t & 0xc533; @@ -149,30 +149,11 @@ static void loclass_opt_suc( uint8_t length, bool add32Zeroes) { for(int i = 0; i < length; i++) { - uint8_t head; - head = in[i]; - loclass_opt_successor(k, s, head); - - head >>= 1; - loclass_opt_successor(k, s, head); - - head >>= 1; - loclass_opt_successor(k, s, head); - - head >>= 1; - loclass_opt_successor(k, s, head); - - head >>= 1; - loclass_opt_successor(k, s, head); - - head >>= 1; - loclass_opt_successor(k, s, head); - - head >>= 1; - loclass_opt_successor(k, s, head); - - head >>= 1; - loclass_opt_successor(k, s, head); + uint8_t head = in[i]; + for(int j = 0; j < 8; j++) { + loclass_opt_successor(k, s, head); + head >>= 1; + } } //For tag MAC, an additional 32 zeroes if(add32Zeroes) { diff --git a/applications/plugins/picopass/picopass_device.h b/applications/plugins/picopass/picopass_device.h index 26f21594..150b095a 100644 --- a/applications/plugins/picopass/picopass_device.h +++ b/applications/plugins/picopass/picopass_device.h @@ -18,6 +18,9 @@ #define PICOPASS_CSN_BLOCK_INDEX 0 #define PICOPASS_CONFIG_BLOCK_INDEX 1 +#define PICOPASS_EPURSE_BLOCK_INDEX 2 +#define PICOPASS_KD_BLOCK_INDEX 3 +#define PICOPASS_KC_BLOCK_INDEX 4 #define PICOPASS_AIA_BLOCK_INDEX 5 #define PICOPASS_APP_FOLDER ANY_PATH("picopass") diff --git a/applications/plugins/picopass/picopass_worker.c b/applications/plugins/picopass/picopass_worker.c index c2a32cb6..1ee814aa 100644 --- a/applications/plugins/picopass/picopass_worker.c +++ b/applications/plugins/picopass/picopass_worker.c @@ -143,7 +143,7 @@ ReturnCode picopass_read_preauth(PicopassBlock* AA1) { AA1[PICOPASS_CSN_BLOCK_INDEX].data[7]); rfalPicoPassReadBlockRes cfg = {0}; - err = rfalPicoPassPollerReadBlock(PICOPASS_CONFIG_BLOCK_INDEX, &cfg); + rfalPicoPassPollerReadBlock(PICOPASS_CONFIG_BLOCK_INDEX, &cfg); memcpy(AA1[PICOPASS_CONFIG_BLOCK_INDEX].data, cfg.data, sizeof(cfg.data)); FURI_LOG_D( TAG, @@ -158,7 +158,7 @@ ReturnCode picopass_read_preauth(PicopassBlock* AA1) { AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[7]); rfalPicoPassReadBlockRes aia; - err = rfalPicoPassPollerReadBlock(PICOPASS_AIA_BLOCK_INDEX, &aia); + rfalPicoPassPollerReadBlock(PICOPASS_AIA_BLOCK_INDEX, &aia); memcpy(AA1[PICOPASS_AIA_BLOCK_INDEX].data, aia.data, sizeof(aia.data)); FURI_LOG_D( TAG, @@ -175,13 +175,12 @@ ReturnCode picopass_read_preauth(PicopassBlock* AA1) { return ERR_NONE; } -ReturnCode picopass_auth(PicopassBlock* AA1, PicopassPacs* pacs) { +static ReturnCode picopass_auth_standard(uint8_t* csn, uint8_t* div_key) { rfalPicoPassReadCheckRes rcRes; rfalPicoPassCheckRes chkRes; ReturnCode err; - uint8_t div_key[8] = {0}; uint8_t mac[4] = {0}; uint8_t ccnr[12] = {0}; @@ -192,26 +191,34 @@ ReturnCode picopass_auth(PicopassBlock* AA1, PicopassPacs* pacs) { } memcpy(ccnr, rcRes.CCNR, sizeof(rcRes.CCNR)); // last 4 bytes left 0 - loclass_diversifyKey(AA1[PICOPASS_CSN_BLOCK_INDEX].data, picopass_iclass_key, div_key); + loclass_diversifyKey(csn, picopass_iclass_key, div_key); loclass_opt_doReaderMAC(ccnr, div_key, mac); - err = rfalPicoPassPollerCheck(mac, &chkRes); - if(err == ERR_NONE) { - return ERR_NONE; - } - FURI_LOG_E(TAG, "rfalPicoPassPollerCheck error %d", err); + return rfalPicoPassPollerCheck(mac, &chkRes); +} - FURI_LOG_E(TAG, "Starting dictionary attack"); +static ReturnCode picopass_auth_dict( + uint8_t* csn, + PicopassPacs* pacs, + uint8_t* div_key, + IclassEliteDictType dict_type) { + rfalPicoPassReadCheckRes rcRes; + rfalPicoPassCheckRes chkRes; + + ReturnCode err = ERR_PARAM; + + uint8_t mac[4] = {0}; + uint8_t ccnr[12] = {0}; size_t index = 0; uint8_t key[PICOPASS_BLOCK_LEN] = {0}; - if(!iclass_elite_dict_check_presence(IclassEliteDictTypeFlipper)) { + if(!iclass_elite_dict_check_presence(dict_type)) { FURI_LOG_E(TAG, "Dictionary not found"); return ERR_PARAM; } - IclassEliteDict* dict = iclass_elite_dict_alloc(IclassEliteDictTypeFlipper); + IclassEliteDict* dict = iclass_elite_dict_alloc(dict_type); if(!dict) { FURI_LOG_E(TAG, "Dictionary not allocated"); return ERR_PARAM; @@ -221,7 +228,7 @@ ReturnCode picopass_auth(PicopassBlock* AA1, PicopassPacs* pacs) { while(iclass_elite_dict_get_next_key(dict, key)) { FURI_LOG_D( TAG, - "Try to auth with key %d %02x%02x%02x%02x%02x%02x%02x%02x", + "Try to auth with key %zu %02x%02x%02x%02x%02x%02x%02x%02x", index++, key[0], key[1], @@ -235,11 +242,11 @@ ReturnCode picopass_auth(PicopassBlock* AA1, PicopassPacs* pacs) { err = rfalPicoPassPollerReadCheck(&rcRes); if(err != ERR_NONE) { FURI_LOG_E(TAG, "rfalPicoPassPollerReadCheck error %d", err); - return err; + break; } memcpy(ccnr, rcRes.CCNR, sizeof(rcRes.CCNR)); // last 4 bytes left 0 - loclass_iclass_calc_div_key(AA1[PICOPASS_CSN_BLOCK_INDEX].data, key, div_key, true); + loclass_iclass_calc_div_key(csn, key, div_key, true); loclass_opt_doReaderMAC(ccnr, div_key, mac); err = rfalPicoPassPollerCheck(mac, &chkRes); @@ -249,8 +256,39 @@ ReturnCode picopass_auth(PicopassBlock* AA1, PicopassPacs* pacs) { } } - if(dict) { - iclass_elite_dict_free(dict); + iclass_elite_dict_free(dict); + + return err; +} + +ReturnCode picopass_auth(PicopassBlock* AA1, PicopassPacs* pacs) { + ReturnCode err; + + FURI_LOG_E(TAG, "Trying standard legacy key"); + err = picopass_auth_standard( + AA1[PICOPASS_CSN_BLOCK_INDEX].data, AA1[PICOPASS_KD_BLOCK_INDEX].data); + if(err == ERR_NONE) { + return ERR_NONE; + } + + FURI_LOG_E(TAG, "Starting user dictionary attack"); + err = picopass_auth_dict( + AA1[PICOPASS_CSN_BLOCK_INDEX].data, + pacs, + AA1[PICOPASS_KD_BLOCK_INDEX].data, + IclassEliteDictTypeUser); + if(err == ERR_NONE) { + return ERR_NONE; + } + + FURI_LOG_E(TAG, "Starting in-built dictionary attack"); + err = picopass_auth_dict( + AA1[PICOPASS_CSN_BLOCK_INDEX].data, + pacs, + AA1[PICOPASS_KD_BLOCK_INDEX].data, + IclassEliteDictTypeFlipper); + if(err == ERR_NONE) { + return ERR_NONE; } return err; @@ -264,6 +302,11 @@ ReturnCode picopass_read_card(PicopassBlock* AA1) { PICOPASS_MAX_APP_LIMIT; for(size_t i = 2; i < app_limit; i++) { + if(i == PICOPASS_KD_BLOCK_INDEX) { + // Skip over Kd block which is populated earlier (READ of Kd returns all FF's) + continue; + } + rfalPicoPassReadBlockRes block; err = rfalPicoPassPollerReadBlock(i, &block); if(err != ERR_NONE) { diff --git a/applications/plugins/picopass/scenes/picopass_scene_save_name.c b/applications/plugins/picopass/scenes/picopass_scene_save_name.c index 17ad5927..59f33c79 100644 --- a/applications/plugins/picopass/scenes/picopass_scene_save_name.c +++ b/applications/plugins/picopass/scenes/picopass_scene_save_name.c @@ -54,7 +54,7 @@ bool picopass_scene_save_name_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == PicopassCustomEventTextInputDone) { - if(strcmp(picopass->dev->dev_name, "")) { + if(strcmp(picopass->dev->dev_name, "") != 0) { // picopass_device_delete(picopass->dev, true); } strlcpy( diff --git a/applications/plugins/picopass/scenes/picopass_scene_start.c b/applications/plugins/picopass/scenes/picopass_scene_start.c index 76c18a22..6d1aeedc 100644 --- a/applications/plugins/picopass/scenes/picopass_scene_start.c +++ b/applications/plugins/picopass/scenes/picopass_scene_start.c @@ -3,7 +3,7 @@ enum SubmenuIndex { SubmenuIndexRead, SubmenuIndexRunScript, SubmenuIndexSaved, - SubmenuIndexAddManualy, + SubmenuIndexAddManually, SubmenuIndexDebug, }; diff --git a/applications/plugins/signal_generator/scenes/signal_gen_scene_pwm.c b/applications/plugins/signal_generator/scenes/signal_gen_scene_pwm.c index f302c023..7ac3fadd 100644 --- a/applications/plugins/signal_generator/scenes/signal_gen_scene_pwm.c +++ b/applications/plugins/signal_generator/scenes/signal_gen_scene_pwm.c @@ -15,12 +15,12 @@ static void app->pwm_freq = freq; app->pwm_duty = duty; - if(app->pwm_ch != pwm_ch_id[channel_id]) { + if(app->pwm_ch != pwm_ch_id[channel_id]) { //-V1051 app->pwm_ch_prev = app->pwm_ch; app->pwm_ch = pwm_ch_id[channel_id]; view_dispatcher_send_custom_event(app->view_dispatcher, SignalGenPwmEventChannelChange); } else { - app->pwm_ch = pwm_ch_id[channel_id]; + app->pwm_ch = pwm_ch_id[channel_id]; //-V1048 view_dispatcher_send_custom_event(app->view_dispatcher, SignalGenPwmEventUpdate); } } diff --git a/applications/plugins/signal_generator/views/signal_gen_pwm.c b/applications/plugins/signal_generator/views/signal_gen_pwm.c index 8e618f8a..b6ba47ab 100644 --- a/applications/plugins/signal_generator/views/signal_gen_pwm.c +++ b/applications/plugins/signal_generator/views/signal_gen_pwm.c @@ -127,12 +127,12 @@ static void signal_gen_pwm_draw_callback(Canvas* canvas, void* _model) { char* line_label = NULL; char val_text[16]; - for(uint8_t line = 0; line < LineIndexTotalCount; line++) { + for(size_t line = 0; line < LineIndexTotalCount; line++) { if(line == LineIndexChannel) { line_label = "GPIO Pin"; } else if(line == LineIndexFrequency) { line_label = "Frequency"; - } else if(line == LineIndexDuty) { + } else if(line == LineIndexDuty) { //-V547 line_label = "Pulse width"; } @@ -169,7 +169,7 @@ static void signal_gen_pwm_draw_callback(Canvas* canvas, void* _model) { canvas_draw_icon(canvas, icon_x, text_y - 9, &I_SmallArrowUp_3x5); canvas_draw_icon(canvas, icon_x, text_y + 5, &I_SmallArrowDown_3x5); } - } else if(line == LineIndexDuty) { + } else if(line == LineIndexDuty) { //-V547 snprintf(val_text, sizeof(val_text), "%d%%", model->duty); canvas_draw_str_aligned(canvas, VALUE_X, text_y, AlignCenter, AlignCenter, val_text); if(model->duty != 0) { diff --git a/applications/plugins/snake_game/snake_game.c b/applications/plugins/snake_game/snake_game.c index ef4ae2ee..f9b4d30a 100644 --- a/applications/plugins/snake_game/snake_game.c +++ b/applications/plugins/snake_game/snake_game.c @@ -130,7 +130,7 @@ static void snake_game_render_callback(Canvas* const canvas, void* ctx) { canvas_set_font(canvas, FontSecondary); char buffer[12]; - snprintf(buffer, sizeof(buffer), "Score: %u", snake_state->len - 7); + snprintf(buffer, sizeof(buffer), "Score: %u", snake_state->len - 7U); canvas_draw_str_aligned(canvas, 64, 41, AlignCenter, AlignBottom, buffer); } @@ -153,7 +153,7 @@ static void snake_game_update_timer_callback(FuriMessageQueue* event_queue) { static void snake_game_init_game(SnakeState* const snake_state) { Point p[] = {{8, 6}, {7, 6}, {6, 6}, {5, 6}, {4, 6}, {3, 6}, {2, 6}}; - memcpy(snake_state->points, p, sizeof(p)); + memcpy(snake_state->points, p, sizeof(p)); //-V1086 snake_state->len = 7; diff --git a/applications/plugins/weather_station/protocols/oregon2.c b/applications/plugins/weather_station/protocols/oregon2.c index 8779e959..8ca80bbe 100644 --- a/applications/plugins/weather_station/protocols/oregon2.c +++ b/applications/plugins/weather_station/protocols/oregon2.c @@ -343,7 +343,7 @@ bool ws_protocol_decoder_oregon2_deserialize(void* context, FlipperFormat* flipp flipper_format, "VarData", (uint8_t*)&instance->var_data, - sizeof(instance->var_data))) { + sizeof(instance->var_data))) { //-V1051 FURI_LOG_E(TAG, "Missing VarData"); break; } diff --git a/applications/plugins/weather_station/protocols/oregon_v1.c b/applications/plugins/weather_station/protocols/oregon_v1.c index d1cc4c7a..1ed9da20 100644 --- a/applications/plugins/weather_station/protocols/oregon_v1.c +++ b/applications/plugins/weather_station/protocols/oregon_v1.c @@ -147,7 +147,7 @@ static void ws_protocol_oregon_v1_remote_controller(WSBlockGeneric* instance) { instance->temp = -temp_raw; } - instance->battery_low = !(instance->data >> 23) & 1; + instance->battery_low = !((instance->data >> 23) & 1ULL); instance->btn = WS_NO_BTN; instance->humidity = WS_NO_HUMIDITY; diff --git a/applications/plugins/weather_station/protocols/ws_generic.c b/applications/plugins/weather_station/protocols/ws_generic.c index dcacda2e..8a88ed52 100644 --- a/applications/plugins/weather_station/protocols/ws_generic.c +++ b/applications/plugins/weather_station/protocols/ws_generic.c @@ -79,7 +79,7 @@ bool ws_block_generic_serialize( uint8_t key_data[sizeof(uint64_t)] = {0}; for(size_t i = 0; i < sizeof(uint64_t); i++) { - key_data[sizeof(uint64_t) - i - 1] = (instance->data >> i * 8) & 0xFF; + key_data[sizeof(uint64_t) - i - 1] = (instance->data >> (i * 8)) & 0xFF; } if(!flipper_format_write_hex(flipper_format, "Data", key_data, sizeof(uint64_t))) { @@ -208,4 +208,4 @@ bool ws_block_generic_deserialize(WSBlockGeneric* instance, FlipperFormat* flipp } while(0); return res; -} +} \ No newline at end of file diff --git a/applications/plugins/weather_station/views/weather_station_receiver_info.c b/applications/plugins/weather_station/views/weather_station_receiver_info.c index 55d239aa..b3b3f219 100644 --- a/applications/plugins/weather_station/views/weather_station_receiver_info.c +++ b/applications/plugins/weather_station/views/weather_station_receiver_info.c @@ -4,8 +4,7 @@ #include "../protocols/ws_generic.h" #include #include - -#define abs(x) ((x) > 0 ? (x) : -(x)) +#include struct WSReceiverInfo { View* view; @@ -79,7 +78,7 @@ void ws_view_receiver_info_draw(Canvas* canvas, WSReceiverInfoModel* model) { elements_bold_rounded_frame(canvas, 0, 38, 127, 25); canvas_set_font(canvas, FontPrimary); - if(model->generic->temp != WS_NO_TEMPERATURE) { + if(!float_is_equal(model->generic->temp, WS_NO_TEMPERATURE)) { canvas_draw_icon(canvas, 6, 43, &I_Therm_7x16); uint8_t temp_x1 = 0; diff --git a/applications/plugins/weather_station/weather_station_app_i.c b/applications/plugins/weather_station/weather_station_app_i.c index 052bb853..712634a2 100644 --- a/applications/plugins/weather_station/weather_station_app_i.c +++ b/applications/plugins/weather_station/weather_station_app_i.c @@ -111,11 +111,8 @@ void ws_hopper_update(WeatherStationApp* app) { switch(app->txrx->hopper_state) { case WSHopperStateOFF: - return; - break; case WSHopperStatePause: return; - break; case WSHopperStateRSSITimeOut: if(app->txrx->hopper_timeout != 0) { app->txrx->hopper_timeout--; diff --git a/applications/plugins/weather_station/weather_station_history.c b/applications/plugins/weather_station/weather_station_history.c index b37009c4..9adff39c 100644 --- a/applications/plugins/weather_station/weather_station_history.c +++ b/applications/plugins/weather_station/weather_station_history.c @@ -186,7 +186,7 @@ WSHistoryStateAddKey } // or add new record - if(!sensor_found) { + if(!sensor_found) { //-V547 WSHistoryItem* item = WSHistoryItemArray_push_raw(instance->history->data); item->preset = malloc(sizeof(SubGhzRadioPreset)); item->type = decoder_base->protocol->type; diff --git a/applications/services/bt/bt_service/bt.c b/applications/services/bt/bt_service/bt.c index 024cb6e5..9e578269 100644 --- a/applications/services/bt/bt_service/bt.c +++ b/applications/services/bt/bt_service/bt.c @@ -36,7 +36,7 @@ static void bt_pin_code_view_port_draw_callback(Canvas* canvas, void* context) { Bt* bt = context; char pin_code_info[24]; canvas_draw_icon(canvas, 0, 0, &I_BLE_Pairing_128x64); - snprintf(pin_code_info, sizeof(pin_code_info), "Pairing code\n%06ld", bt->pin_code); + snprintf(pin_code_info, sizeof(pin_code_info), "Pairing code\n%06lu", bt->pin_code); elements_multiline_text_aligned(canvas, 64, 4, AlignCenter, AlignTop, pin_code_info); elements_button_left(canvas, "Quit"); } @@ -78,7 +78,7 @@ static bool bt_pin_code_verify_event_handler(Bt* bt, uint32_t pin) { notification_message(bt->notification, &sequence_display_backlight_on); FuriString* pin_str; dialog_message_set_icon(bt->dialog_message, &I_BLE_Pairing_128x64, 0, 0); - pin_str = furi_string_alloc_printf("Verify code\n%06ld", pin); + pin_str = furi_string_alloc_printf("Verify code\n%06lu", pin); dialog_message_set_text( bt->dialog_message, furi_string_get_cstr(pin_str), 64, 4, AlignCenter, AlignTop); dialog_message_set_buttons(bt->dialog_message, "Cancel", "OK", NULL); @@ -163,7 +163,7 @@ static uint16_t bt_serial_event_callback(SerialServiceEvent event, void* context rpc_session_feed(bt->rpc_session, event.data.buffer, event.data.size, 1000); if(bytes_processed != event.data.size) { FURI_LOG_E( - TAG, "Only %d of %d bytes processed by RPC", bytes_processed, event.data.size); + TAG, "Only %zu of %u bytes processed by RPC", bytes_processed, event.data.size); } ret = rpc_session_get_available_size(bt->rpc_session); } else if(event.event == SerialServiceEventTypeDataSent) { diff --git a/applications/services/bt/bt_service/bt_keys_storage.c b/applications/services/bt/bt_service/bt_keys_storage.c index 7cff9994..215f19a8 100644 --- a/applications/services/bt/bt_service/bt_keys_storage.c +++ b/applications/services/bt/bt_service/bt_keys_storage.c @@ -115,7 +115,7 @@ bool bt_keys_storage_update(BtKeysStorage* instance, uint8_t* start_addr, uint32 FURI_LOG_I( TAG, - "Base address: %p. Start update address: %p. Size changed: %ld", + "Base address: %p. Start update address: %p. Size changed: %lu", (void*)instance->nvm_sram_buff, start_addr, size); diff --git a/applications/services/cli/cli.c b/applications/services/cli/cli.c index f29dca9c..384d1780 100644 --- a/applications/services/cli/cli.c +++ b/applications/services/cli/cli.c @@ -225,7 +225,7 @@ static void cli_handle_enter(Cli* cli) { furi_check(furi_mutex_acquire(cli->mutex, FuriWaitForever) == FuriStatusOk); CliCommand* cli_command_ptr = CliCommandTree_get(cli->commands, command); - if(cli_command_ptr) { + if(cli_command_ptr) { //-V547 CliCommand cli_command; memcpy(&cli_command, cli_command_ptr, sizeof(CliCommand)); furi_check(furi_mutex_release(cli->mutex) == FuriStatusOk); @@ -353,7 +353,7 @@ void cli_process_input(Cli* cli) { cli_handle_backspace(cli); } else if(in_chr == CliSymbolAsciiCR) { cli_handle_enter(cli); - } else if(in_chr >= 0x20 && in_chr < 0x7F) { + } else if(in_chr >= 0x20 && in_chr < 0x7F) { //-V560 if(cli->cursor_position == furi_string_size(cli->line)) { furi_string_push_back(cli->line, in_chr); cli_putc(cli, in_chr); diff --git a/applications/services/cli/cli_command_gpio.c b/applications/services/cli/cli_command_gpio.c index 0b29f485..f0d487be 100644 --- a/applications/services/cli/cli_command_gpio.c +++ b/applications/services/cli/cli_command_gpio.c @@ -1,5 +1,6 @@ #include "cli_command_gpio.h" +#include "core/string.h" #include #include #include @@ -36,26 +37,24 @@ void cli_command_gpio_print_usage() { } static bool pin_name_to_int(FuriString* pin_name, size_t* result) { - bool found = false; - bool debug = furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug); + bool is_debug_mode = furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug); for(size_t i = 0; i < COUNT_OF(cli_command_gpio_pins); i++) { - if(!furi_string_cmp(pin_name, cli_command_gpio_pins[i].name)) { - if(!cli_command_gpio_pins[i].debug || debug) { + if(furi_string_equal(pin_name, cli_command_gpio_pins[i].name)) { + if(!cli_command_gpio_pins[i].debug || is_debug_mode) { *result = i; - found = true; - break; + return true; } } } - return found; + return false; } static void gpio_print_pins(void) { printf("Wrong pin name. Available pins: "); - bool debug = furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug); + bool is_debug_mode = furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug); for(size_t i = 0; i < COUNT_OF(cli_command_gpio_pins); i++) { - if(!cli_command_gpio_pins[i].debug || debug) { + if(!cli_command_gpio_pins[i].debug || is_debug_mode) { printf("%s ", cli_command_gpio_pins[i].name); } } @@ -69,34 +68,29 @@ typedef enum { } GpioParseReturn; static GpioParseReturn gpio_command_parse(FuriString* args, size_t* pin_num, uint8_t* value) { - FuriString* pin_name; - pin_name = furi_string_alloc(); + GpioParseReturn ret = GpioParseReturnOk; + FuriString* pin_name = furi_string_alloc(); - size_t ws = furi_string_search_char(args, ' '); - if(ws == FURI_STRING_FAILURE) { - return GpioParseReturnCmdSyntaxError; - } + do { + if(!args_read_string_and_trim(args, pin_name)) { + ret = GpioParseReturnCmdSyntaxError; + break; + } else if(!pin_name_to_int(pin_name, pin_num)) { + ret = GpioParseReturnPinError; + break; + } - furi_string_set_n(pin_name, args, 0, ws); - furi_string_right(args, ws); - furi_string_trim(args); + int pin_mode; //-V779 + if(!args_read_int_and_trim(args, &pin_mode) || pin_mode < 0 || pin_mode > 1) { + ret = GpioParseReturnValueError; + break; + } - if(!pin_name_to_int(pin_name, pin_num)) { - furi_string_free(pin_name); - return GpioParseReturnPinError; - } + *value = pin_mode; + } while(false); furi_string_free(pin_name); - - if(!furi_string_cmp(args, "0")) { - *value = 0; - } else if(!furi_string_cmp(args, "1")) { - *value = 1; - } else { - return GpioParseReturnValueError; - } - - return GpioParseReturnOk; + return ret; } void cli_command_gpio_mode(Cli* cli, FuriString* args, void* context) { @@ -111,7 +105,7 @@ void cli_command_gpio_mode(Cli* cli, FuriString* args, void* context) { if(err == GpioParseReturnCmdSyntaxError) { cli_print_usage("gpio mode", " <0|1>", furi_string_get_cstr(args)); return; - } else if(err == GpioParseReturnPinError) { + } else if(err == GpioParseReturnPinError) { //-V547 gpio_print_pins(); return; } else if(err == GpioParseReturnValueError) { @@ -119,7 +113,7 @@ void cli_command_gpio_mode(Cli* cli, FuriString* args, void* context) { return; } - if(cli_command_gpio_pins[num].debug) { + if(cli_command_gpio_pins[num].debug) { //-V779 printf( "Changing this pin mode may damage hardware. Are you sure you want to continue? (y/n)?\r\n"); char c = cli_getc(cli); @@ -149,7 +143,7 @@ void cli_command_gpio_read(Cli* cli, FuriString* args, void* context) { return; } - if(LL_GPIO_MODE_INPUT != + if(LL_GPIO_MODE_INPUT != //-V779 LL_GPIO_GetPinMode( cli_command_gpio_pins[num].pin->port, cli_command_gpio_pins[num].pin->pin)) { printf("Err: pin %s is not set as an input.", cli_command_gpio_pins[num].name); @@ -171,7 +165,7 @@ void cli_command_gpio_set(Cli* cli, FuriString* args, void* context) { if(err == GpioParseReturnCmdSyntaxError) { cli_print_usage("gpio set", " <0|1>", furi_string_get_cstr(args)); return; - } else if(err == GpioParseReturnPinError) { + } else if(err == GpioParseReturnPinError) { //-V547 gpio_print_pins(); return; } else if(err == GpioParseReturnValueError) { @@ -179,7 +173,7 @@ void cli_command_gpio_set(Cli* cli, FuriString* args, void* context) { return; } - if(LL_GPIO_MODE_OUTPUT != + if(LL_GPIO_MODE_OUTPUT != //-V779 LL_GPIO_GetPinMode( cli_command_gpio_pins[num].pin->port, cli_command_gpio_pins[num].pin->pin)) { printf("Err: pin %s is not set as an output.", cli_command_gpio_pins[num].name); diff --git a/applications/services/cli/cli_commands.c b/applications/services/cli/cli_commands.c index 3534a541..4414d365 100644 --- a/applications/services/cli/cli_commands.c +++ b/applications/services/cli/cli_commands.c @@ -354,7 +354,7 @@ void cli_command_ps(Cli* cli, FuriString* args, void* context) { for(uint8_t i = 0; i < thread_num; i++) { TaskControlBlock* tcb = (TaskControlBlock*)threads_ids[i]; printf( - "%-20s 0x%-12lx %-8d %-8ld %-8ld\r\n", + "%-20s 0x%-12lx %-8zu %-8lu %-8lu\r\n", furi_thread_get_name(threads_ids[i]), (uint32_t)tcb->pxStack, memmgr_heap_get_thread_memory(threads_ids[i]), @@ -369,13 +369,13 @@ void cli_command_free(Cli* cli, FuriString* args, void* context) { UNUSED(args); UNUSED(context); - printf("Free heap size: %d\r\n", memmgr_get_free_heap()); - printf("Total heap size: %d\r\n", memmgr_get_total_heap()); - printf("Minimum heap size: %d\r\n", memmgr_get_minimum_free_heap()); - printf("Maximum heap block: %d\r\n", memmgr_heap_get_max_free_block()); + printf("Free heap size: %zu\r\n", memmgr_get_free_heap()); + printf("Total heap size: %zu\r\n", memmgr_get_total_heap()); + printf("Minimum heap size: %zu\r\n", memmgr_get_minimum_free_heap()); + printf("Maximum heap block: %zu\r\n", memmgr_heap_get_max_free_block()); - printf("Pool free: %d\r\n", memmgr_pool_get_free()); - printf("Maximum pool block: %d\r\n", memmgr_pool_get_max_block()); + printf("Pool free: %zu\r\n", memmgr_pool_get_free()); + printf("Maximum pool block: %zu\r\n", memmgr_pool_get_max_block()); } void cli_command_free_blocks(Cli* cli, FuriString* args, void* context) { diff --git a/applications/services/crypto/crypto_cli.c b/applications/services/crypto/crypto_cli.c index 1b26ba9f..a286f445 100644 --- a/applications/services/crypto/crypto_cli.c +++ b/applications/services/crypto/crypto_cli.c @@ -142,7 +142,7 @@ void crypto_cli_decrypt(Cli* cli, FuriString* args) { if(args_read_hex_bytes(hex_input, input, size)) { if(furi_hal_crypto_decrypt(input, output, size)) { printf("Decrypted data:\r\n"); - printf("%s\r\n", output); + printf("%s\r\n", output); //-V576 } else { printf("Failed to decrypt\r\n"); } diff --git a/applications/services/desktop/animations/animation_manager.c b/applications/services/desktop/animations/animation_manager.c index 9c22d131..f4c8f17a 100644 --- a/applications/services/desktop/animations/animation_manager.c +++ b/applications/services/desktop/animations/animation_manager.c @@ -244,10 +244,8 @@ static bool animation_manager_check_blocking(AnimationManager* animation_manager furi_record_close(RECORD_DOLPHIN); if(!blocking_animation && stats.level_up_is_pending) { blocking_animation = animation_storage_find_animation(NEW_MAIL_ANIMATION_NAME); - furi_assert(blocking_animation); - if(blocking_animation) { - animation_manager->levelup_pending = true; - } + furi_check(blocking_animation); + animation_manager->levelup_pending = true; } if(blocking_animation) { @@ -448,7 +446,7 @@ void animation_manager_unload_and_stall_animation(AnimationManager* animation_ma if(animation_manager->state == AnimationManagerStateBlocked) { animation_manager->state = AnimationManagerStateFreezedBlocked; - } else if(animation_manager->state == AnimationManagerStateIdle) { + } else if(animation_manager->state == AnimationManagerStateIdle) { //-V547 animation_manager->state = AnimationManagerStateFreezedIdle; animation_manager->freezed_animation_time_left = @@ -491,7 +489,7 @@ void animation_manager_load_and_continue_animation(AnimationManager* animation_m furi_assert(restore_animation); animation_manager_replace_current_animation(animation_manager, restore_animation); animation_manager->state = AnimationManagerStateBlocked; - } else if(animation_manager->state == AnimationManagerStateFreezedIdle) { + } else if(animation_manager->state == AnimationManagerStateFreezedIdle) { //-V547 /* check if we missed some system notifications, and set current_animation */ bool blocked = animation_manager_check_blocking(animation_manager); if(!blocked) { diff --git a/applications/services/desktop/animations/animation_storage.c b/applications/services/desktop/animations/animation_storage.c index 0727fd6a..2c16cf72 100644 --- a/applications/services/desktop/animations/animation_storage.c +++ b/applications/services/desktop/animations/animation_storage.c @@ -360,7 +360,6 @@ static bool animation_storage_load_bubbles(BubbleAnimation* animation, FlipperFo if(u32value > 20) break; animation->frame_bubble_sequences_count = u32value; if(animation->frame_bubble_sequences_count == 0) { - animation->frame_bubble_sequences = NULL; success = true; break; } @@ -481,7 +480,7 @@ static BubbleAnimation* animation_storage_load_animation(const char* name) { if(!animation_storage_load_frames(storage, name, animation, u32array, width, height)) break; - if(!flipper_format_read_uint32(ff, "Active cycles", &u32value, 1)) break; + if(!flipper_format_read_uint32(ff, "Active cycles", &u32value, 1)) break; //-V779 animation->active_cycles = u32value; if(!flipper_format_read_uint32(ff, "Frame rate", &u32value, 1)) break; FURI_CONST_ASSIGN(animation->icon_animation.frame_rate, u32value); @@ -500,7 +499,7 @@ static BubbleAnimation* animation_storage_load_animation(const char* name) { free(u32array); } - if(!success) { + if(!success) { //-V547 if(animation->frame_order) { free((void*)animation->frame_order); } diff --git a/applications/services/desktop/helpers/slideshow.c b/applications/services/desktop/helpers/slideshow.c index b4d85cb9..a8e13277 100644 --- a/applications/services/desktop/helpers/slideshow.c +++ b/applications/services/desktop/helpers/slideshow.c @@ -41,7 +41,7 @@ Slideshow* slideshow_alloc() { void slideshow_free(Slideshow* slideshow) { Icon* icon = &slideshow->icon; - if(icon) { + if(icon) { //-V547 for(int frame_idx = 0; frame_idx < icon->frame_count; ++frame_idx) { uint8_t* frame_data = (uint8_t*)icon->frames[frame_idx]; free(frame_data); diff --git a/applications/services/desktop/views/desktop_view_debug.c b/applications/services/desktop/views/desktop_view_debug.c index f9c8aedc..e679cf63 100644 --- a/applications/services/desktop/views/desktop_view_debug.c +++ b/applications/services/desktop/views/desktop_view_debug.c @@ -52,7 +52,7 @@ void desktop_debug_render(Canvas* canvas, void* model) { #ifdef SRV_BT c2_ver = ble_glue_get_c2_info(); #endif - if(!ver) { + if(!ver) { //-V1051 canvas_draw_str(canvas, 0, 30 + STATUS_BAR_Y_SHIFT, "No info"); return; } @@ -88,19 +88,19 @@ void desktop_debug_render(Canvas* canvas, void* model) { uint32_t remaining = dolphin_state_xp_to_levelup(m->icounter); canvas_set_font(canvas, FontSecondary); - snprintf(buffer, sizeof(buffer), "Icounter: %ld Butthurt %ld", m->icounter, m->butthurt); + snprintf(buffer, sizeof(buffer), "Icounter: %lu Butthurt %lu", m->icounter, m->butthurt); canvas_draw_str(canvas, 5, 19 + STATUS_BAR_Y_SHIFT, buffer); snprintf( buffer, sizeof(buffer), - "Level: %ld To level up: %ld", + "Level: %lu To level up: %lu", current_lvl, (remaining == (uint32_t)(-1) ? remaining : 0)); canvas_draw_str(canvas, 5, 29 + STATUS_BAR_Y_SHIFT, buffer); // even if timestamp is uint64_t, it's safe to cast it to uint32_t, because furi_hal_rtc_datetime_to_timestamp only returns uint32_t - snprintf(buffer, sizeof(buffer), "%ld", (uint32_t)m->timestamp); + snprintf(buffer, sizeof(buffer), "%lu", (uint32_t)m->timestamp); canvas_draw_str(canvas, 5, 39 + STATUS_BAR_Y_SHIFT, buffer); canvas_draw_str(canvas, 0, 49 + STATUS_BAR_Y_SHIFT, "[< >] icounter value [ok] save"); diff --git a/applications/services/desktop/views/desktop_view_lock_menu.c b/applications/services/desktop/views/desktop_view_lock_menu.c index 486be23b..52570f8c 100644 --- a/applications/services/desktop/views/desktop_view_lock_menu.c +++ b/applications/services/desktop/views/desktop_view_lock_menu.c @@ -53,7 +53,7 @@ void desktop_lock_menu_draw_callback(Canvas* canvas, void* model) { canvas_draw_icon(canvas, 116, 0 + STATUS_BAR_Y_SHIFT, &I_DoorRight_70x55); canvas_set_font(canvas, FontSecondary); - for(uint8_t i = 0; i < DesktopLockMenuIndexTotalCount; ++i) { + for(size_t i = 0; i < DesktopLockMenuIndexTotalCount; ++i) { const char* str = NULL; if(i == DesktopLockMenuIndexLock) { @@ -64,7 +64,7 @@ void desktop_lock_menu_draw_callback(Canvas* canvas, void* model) { } else { str = "Set PIN"; } - } else if(i == DesktopLockMenuIndexDummy) { + } else if(i == DesktopLockMenuIndexDummy) { //-V547 if(m->dummy_mode) { str = "Brainiac Mode"; } else { @@ -72,7 +72,7 @@ void desktop_lock_menu_draw_callback(Canvas* canvas, void* model) { } } - if(str) + if(str) //-V547 canvas_draw_str_aligned( canvas, 64, 9 + (i * 17) + STATUS_BAR_Y_SHIFT, AlignCenter, AlignCenter, str); diff --git a/applications/services/desktop/views/desktop_view_main.c b/applications/services/desktop/views/desktop_view_main.c index cbf4a20f..7d956489 100644 --- a/applications/services/desktop/views/desktop_view_main.c +++ b/applications/services/desktop/views/desktop_view_main.c @@ -102,7 +102,6 @@ DesktopMainView* desktop_main_alloc() { DesktopMainView* main_view = malloc(sizeof(DesktopMainView)); main_view->view = view_alloc(); - view_allocate_model(main_view->view, ViewModelTypeLockFree, 1); view_set_context(main_view->view, main_view); view_set_input_callback(main_view->view, desktop_main_input_callback); diff --git a/applications/services/desktop/views/desktop_view_pin_setup_done.c b/applications/services/desktop/views/desktop_view_pin_setup_done.c index 1d82aeaa..561b1286 100644 --- a/applications/services/desktop/views/desktop_view_pin_setup_done.c +++ b/applications/services/desktop/views/desktop_view_pin_setup_done.c @@ -18,7 +18,7 @@ struct DesktopViewPinSetupDone { static void desktop_view_pin_done_draw(Canvas* canvas, void* model) { furi_assert(canvas); - furi_assert(model); + UNUSED(model); canvas_set_font(canvas, FontPrimary); elements_multiline_text_aligned( @@ -59,7 +59,6 @@ void desktop_view_pin_done_set_callback( DesktopViewPinSetupDone* desktop_view_pin_done_alloc() { DesktopViewPinSetupDone* view = malloc(sizeof(DesktopViewPinSetupDone)); view->view = view_alloc(); - view_allocate_model(view->view, ViewModelTypeLockFree, 1); view_set_context(view->view, view); view_set_draw_callback(view->view, desktop_view_pin_done_draw); view_set_input_callback(view->view, desktop_view_pin_done_input); diff --git a/applications/services/desktop/views/desktop_view_pin_timeout.c b/applications/services/desktop/views/desktop_view_pin_timeout.c index 6e1e807f..e64c264f 100644 --- a/applications/services/desktop/views/desktop_view_pin_timeout.c +++ b/applications/services/desktop/views/desktop_view_pin_timeout.c @@ -67,7 +67,7 @@ static void desktop_view_pin_timeout_draw(Canvas* canvas, void* _model) { canvas_set_font(canvas, FontSecondary); char str[30] = {0}; - snprintf(str, sizeof(str), "Timeout: %lds", model->time_left); + snprintf(str, sizeof(str), "Timeout: %lus", model->time_left); canvas_draw_str_aligned(canvas, 64, 38, AlignCenter, AlignCenter, str); } diff --git a/applications/services/dolphin/helpers/dolphin_state.c b/applications/services/dolphin/helpers/dolphin_state.c index 10cb85c2..14f08046 100644 --- a/applications/services/dolphin/helpers/dolphin_state.c +++ b/applications/services/dolphin/helpers/dolphin_state.c @@ -171,7 +171,7 @@ void dolphin_state_on_deed(DolphinState* dolphin_state, DolphinDeed deed) { FURI_LOG_D( TAG, - "icounter %ld, butthurt %ld", + "icounter %lu, butthurt %ld", dolphin_state->data.icounter, dolphin_state->data.butthurt); } diff --git a/applications/services/gui/canvas.h b/applications/services/gui/canvas.h index a3df5adc..0b0c7e65 100644 --- a/applications/services/gui/canvas.h +++ b/applications/services/gui/canvas.h @@ -17,6 +17,7 @@ extern "C" { typedef enum { ColorWhite = 0x00, ColorBlack = 0x01, + ColorXOR = 0x02, } Color; /** Fonts enumeration */ @@ -66,6 +67,18 @@ typedef struct { /** Canvas anonymous structure */ typedef struct Canvas Canvas; +/** Reset canvas drawing tools configuration + * + * @param canvas Canvas instance + */ +void canvas_reset(Canvas* canvas); + +/** Commit canvas. Send buffer to display + * + * @param canvas Canvas instance + */ +void canvas_commit(Canvas* canvas); + /** Get Canvas width * * @param canvas Canvas instance diff --git a/applications/services/gui/canvas_i.h b/applications/services/gui/canvas_i.h index d5594c24..2b684923 100644 --- a/applications/services/gui/canvas_i.h +++ b/applications/services/gui/canvas_i.h @@ -31,18 +31,6 @@ Canvas* canvas_init(); */ void canvas_free(Canvas* canvas); -/** Reset canvas drawing tools configuration - * - * @param canvas Canvas instance - */ -void canvas_reset(Canvas* canvas); - -/** Commit canvas. Send buffer to display - * - * @param canvas Canvas instance - */ -void canvas_commit(Canvas* canvas); - /** Get canvas buffer. * * @param canvas Canvas instance diff --git a/applications/services/gui/elements.c b/applications/services/gui/elements.c index 6b796ed5..e22889bf 100644 --- a/applications/services/gui/elements.c +++ b/applications/services/gui/elements.c @@ -41,6 +41,31 @@ void elements_progress_bar(Canvas* canvas, uint8_t x, uint8_t y, uint8_t width, canvas_draw_box(canvas, x + 1, y + 1, progress_length, height - 2); } +void elements_progress_bar_with_text( + Canvas* canvas, + uint8_t x, + uint8_t y, + uint8_t width, + float progress, + const char* text) { + furi_assert(canvas); + furi_assert((progress >= 0.0f) && (progress <= 1.0f)); + uint8_t height = 11; + + uint8_t progress_length = roundf(progress * (width - 2)); + + canvas_set_color(canvas, ColorWhite); + canvas_draw_box(canvas, x + 1, y + 1, width - 2, height - 2); + canvas_set_color(canvas, ColorBlack); + canvas_draw_rframe(canvas, x, y, width, height, 3); + + canvas_draw_box(canvas, x + 1, y + 1, progress_length, height - 2); + + canvas_set_color(canvas, ColorXOR); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, x + width / 2, y + 2, AlignCenter, AlignTop, text); +} + void elements_scrollbar_pos( Canvas* canvas, uint8_t x, @@ -291,11 +316,11 @@ void elements_multiline_text(Canvas* canvas, uint8_t x, uint8_t y, const char* t end = strchr(start, '\n'); if(end) { furi_string_set_strn(str, start, end - start); + start = end + 1; } else { furi_string_set(str, start); } canvas_draw_str(canvas, x, y, furi_string_get_cstr(str)); - start = end + 1; y += font_height; } while(end && y < 64); furi_string_free(str); diff --git a/applications/services/gui/elements.h b/applications/services/gui/elements.h index 162f0d41..48533513 100644 --- a/applications/services/gui/elements.h +++ b/applications/services/gui/elements.h @@ -31,6 +31,23 @@ extern "C" { */ void elements_progress_bar(Canvas* canvas, uint8_t x, uint8_t y, uint8_t width, float progress); +/** Draw progress bar with text. + * + * @param canvas Canvas instance + * @param x progress bar position on X axis + * @param y progress bar position on Y axis + * @param width progress bar width + * @param progress progress (0.0 - 1.0) + * @param text text to draw + */ +void elements_progress_bar_with_text( + Canvas* canvas, + uint8_t x, + uint8_t y, + uint8_t width, + float progress, + const char* text); + /** Draw scrollbar on canvas at specific position. * * @param canvas Canvas instance diff --git a/applications/services/gui/gui.c b/applications/services/gui/gui.c index 9f6ebcd7..b0903e02 100644 --- a/applications/services/gui/gui.c +++ b/applications/services/gui/gui.c @@ -20,7 +20,7 @@ ViewPort* gui_view_port_find_enabled(ViewPortArray_t array) { void gui_update(Gui* gui) { furi_assert(gui); - furi_thread_flags_set(gui->thread_id, GUI_THREAD_FLAG_DRAW); + if(!gui->direct_draw) furi_thread_flags_set(gui->thread_id, GUI_THREAD_FLAG_DRAW); } void gui_input_events_callback(const void* value, void* ctx) { @@ -34,7 +34,7 @@ void gui_input_events_callback(const void* value, void* ctx) { } // Only Fullscreen supports vertical display for now -bool gui_redraw_fs(Gui* gui) { +static bool gui_redraw_fs(Gui* gui) { canvas_set_orientation(gui->canvas, CanvasOrientationHorizontal); canvas_frame_set(gui->canvas, 0, 0, GUI_DISPLAY_WIDTH, GUI_DISPLAY_HEIGHT); ViewPort* view_port = gui_view_port_find_enabled(gui->layers[GuiLayerFullscreen]); @@ -192,7 +192,7 @@ static void gui_redraw_status_bar(Gui* gui, bool need_attention) { } } -bool gui_redraw_window(Gui* gui) { +static bool gui_redraw_window(Gui* gui) { canvas_set_orientation(gui->canvas, CanvasOrientationHorizontal); canvas_frame_set(gui->canvas, GUI_WINDOW_X, GUI_WINDOW_Y, GUI_WINDOW_WIDTH, GUI_WINDOW_HEIGHT); ViewPort* view_port = gui_view_port_find_enabled(gui->layers[GuiLayerWindow]); @@ -203,7 +203,7 @@ bool gui_redraw_window(Gui* gui) { return false; } -bool gui_redraw_desktop(Gui* gui) { +static bool gui_redraw_desktop(Gui* gui) { canvas_set_orientation(gui->canvas, CanvasOrientationHorizontal); canvas_frame_set(gui->canvas, 0, 0, GUI_DISPLAY_WIDTH, GUI_DISPLAY_HEIGHT); ViewPort* view_port = gui_view_port_find_enabled(gui->layers[GuiLayerDesktop]); @@ -215,37 +215,44 @@ bool gui_redraw_desktop(Gui* gui) { return false; } -void gui_redraw(Gui* gui) { +static void gui_redraw(Gui* gui) { furi_assert(gui); gui_lock(gui); - canvas_reset(gui->canvas); + do { + if(gui->direct_draw) break; - if(gui->lockdown) { - gui_redraw_desktop(gui); - bool need_attention = - (gui_view_port_find_enabled(gui->layers[GuiLayerWindow]) != 0 || - gui_view_port_find_enabled(gui->layers[GuiLayerFullscreen]) != 0); - gui_redraw_status_bar(gui, need_attention); - } else { - if(!gui_redraw_fs(gui)) { - if(!gui_redraw_window(gui)) { - gui_redraw_desktop(gui); + canvas_reset(gui->canvas); + + if(gui->lockdown) { + gui_redraw_desktop(gui); + bool need_attention = + (gui_view_port_find_enabled(gui->layers[GuiLayerWindow]) != 0 || + gui_view_port_find_enabled(gui->layers[GuiLayerFullscreen]) != 0); + gui_redraw_status_bar(gui, need_attention); + } else { + if(!gui_redraw_fs(gui)) { + if(!gui_redraw_window(gui)) { + gui_redraw_desktop(gui); + } + gui_redraw_status_bar(gui, false); } - gui_redraw_status_bar(gui, false); } - } - canvas_commit(gui->canvas); - for - M_EACH(p, gui->canvas_callback_pair, CanvasCallbackPairArray_t) { - p->callback( - canvas_get_buffer(gui->canvas), canvas_get_buffer_size(gui->canvas), p->context); - } + canvas_commit(gui->canvas); + for + M_EACH(p, gui->canvas_callback_pair, CanvasCallbackPairArray_t) { + p->callback( + canvas_get_buffer(gui->canvas), + canvas_get_buffer_size(gui->canvas), + p->context); + } + } while(false); + gui_unlock(gui); } -void gui_input(Gui* gui, InputEvent* input_event) { +static void gui_input(Gui* gui, InputEvent* input_event) { furi_assert(gui); furi_assert(input_event); @@ -267,42 +274,48 @@ void gui_input(Gui* gui, InputEvent* input_event) { gui_lock(gui); - ViewPort* view_port = NULL; + do { + if(gui->direct_draw && !gui->ongoing_input_view_port) { + break; + } - if(gui->lockdown) { - view_port = gui_view_port_find_enabled(gui->layers[GuiLayerDesktop]); - } else { - view_port = gui_view_port_find_enabled(gui->layers[GuiLayerFullscreen]); - if(!view_port) view_port = gui_view_port_find_enabled(gui->layers[GuiLayerWindow]); - if(!view_port) view_port = gui_view_port_find_enabled(gui->layers[GuiLayerDesktop]); - } + ViewPort* view_port = NULL; - if(!(gui->ongoing_input & ~key_bit) && input_event->type == InputTypePress) { - gui->ongoing_input_view_port = view_port; - } + if(gui->lockdown) { + view_port = gui_view_port_find_enabled(gui->layers[GuiLayerDesktop]); + } else { + view_port = gui_view_port_find_enabled(gui->layers[GuiLayerFullscreen]); + if(!view_port) view_port = gui_view_port_find_enabled(gui->layers[GuiLayerWindow]); + if(!view_port) view_port = gui_view_port_find_enabled(gui->layers[GuiLayerDesktop]); + } - if(view_port && view_port == gui->ongoing_input_view_port) { - view_port_input(view_port, input_event); - } else if(gui->ongoing_input_view_port && input_event->type == InputTypeRelease) { - FURI_LOG_D( - TAG, - "ViewPort changed while key press %p -> %p. Sending key: %s, type: %s, sequence: %p to previous view port", - gui->ongoing_input_view_port, - view_port, - input_get_key_name(input_event->key), - input_get_type_name(input_event->type), - (void*)input_event->sequence); - view_port_input(gui->ongoing_input_view_port, input_event); - } else { - FURI_LOG_D( - TAG, - "ViewPort changed while key press %p -> %p. Discarding key: %s, type: %s, sequence: %p", - gui->ongoing_input_view_port, - view_port, - input_get_key_name(input_event->key), - input_get_type_name(input_event->type), - (void*)input_event->sequence); - } + if(!(gui->ongoing_input & ~key_bit) && input_event->type == InputTypePress) { + gui->ongoing_input_view_port = view_port; + } + + if(view_port && view_port == gui->ongoing_input_view_port) { + view_port_input(view_port, input_event); + } else if(gui->ongoing_input_view_port && input_event->type == InputTypeRelease) { + FURI_LOG_D( + TAG, + "ViewPort changed while key press %p -> %p. Sending key: %s, type: %s, sequence: %p to previous view port", + gui->ongoing_input_view_port, + view_port, + input_get_key_name(input_event->key), + input_get_type_name(input_event->type), + (void*)input_event->sequence); + view_port_input(gui->ongoing_input_view_port, input_event); + } else { + FURI_LOG_D( + TAG, + "ViewPort changed while key press %p -> %p. Discarding key: %s, type: %s, sequence: %p", + gui->ongoing_input_view_port, + view_port, + input_get_key_name(input_event->key), + input_get_type_name(input_event->type), + (void*)input_event->sequence); + } + } while(false); gui_unlock(gui); } @@ -471,6 +484,31 @@ void gui_set_lockdown(Gui* gui, bool lockdown) { gui_update(gui); } +Canvas* gui_direct_draw_acquire(Gui* gui) { + furi_assert(gui); + gui_lock(gui); + gui->direct_draw = true; + gui_unlock(gui); + + canvas_reset(gui->canvas); + canvas_commit(gui->canvas); + + return gui->canvas; +} + +void gui_direct_draw_release(Gui* gui) { + furi_assert(gui); + + canvas_reset(gui->canvas); + canvas_commit(gui->canvas); + + gui_lock(gui); + gui->direct_draw = false; + gui_unlock(gui); + + gui_update(gui); +} + Gui* gui_alloc() { Gui* gui = malloc(sizeof(Gui)); // Thread ID diff --git a/applications/services/gui/gui.h b/applications/services/gui/gui.h index f4886758..4e7fc2e4 100644 --- a/applications/services/gui/gui.h +++ b/applications/services/gui/gui.h @@ -106,6 +106,28 @@ size_t gui_get_framebuffer_size(Gui* gui); */ void gui_set_lockdown(Gui* gui, bool lockdown); +/** Acquire Direct Draw lock and get Canvas instance + * + * This method return Canvas instance for use in monopoly mode. Direct draw lock + * disables input and draw call dispatch functions in GUI service. No other + * applications or services will be able to draw until gui_direct_draw_release + * call. + * + * @param gui The graphical user interface + * + * @return Canvas instance + */ +Canvas* gui_direct_draw_acquire(Gui* gui); + +/** Release Direct Draw Lock + * + * Release Direct Draw Lock, enables Input and Draw call processing. Canvas + * acquired in gui_direct_draw_acquire will become invalid after this call. + * + * @param gui Gui instance + */ +void gui_direct_draw_release(Gui* gui); + #ifdef __cplusplus } #endif diff --git a/applications/services/gui/gui_i.h b/applications/services/gui/gui_i.h index b3fd0aa7..45061bd5 100644 --- a/applications/services/gui/gui_i.h +++ b/applications/services/gui/gui_i.h @@ -62,6 +62,7 @@ struct Gui { // Layers and Canvas bool lockdown; + bool direct_draw; ViewPortArray_t layers[GuiLayerMAX]; Canvas* canvas; CanvasCallbackPairArray_t canvas_callback_pair; diff --git a/applications/services/gui/modules/button_panel.c b/applications/services/gui/modules/button_panel.c index 47b6ed48..8f29c654 100644 --- a/applications/services/gui/modules/button_panel.c +++ b/applications/services/gui/modules/button_panel.c @@ -172,7 +172,7 @@ void button_panel_add_item( void* callback_context) { furi_assert(button_panel); - with_view_model( + with_view_model( //-V773 button_panel->view, ButtonPanelModel * model, { diff --git a/applications/services/gui/modules/byte_input.c b/applications/services/gui/modules/byte_input.c index bc19f0ee..82de129f 100644 --- a/applications/services/gui/modules/byte_input.c +++ b/applications/services/gui/modules/byte_input.c @@ -71,11 +71,13 @@ static uint8_t byte_input_get_row_size(uint8_t row_index) { switch(row_index + 1) { case 1: - row_size = sizeof(keyboard_keys_row_1) / sizeof(ByteInputKey); + row_size = COUNT_OF(keyboard_keys_row_1); break; case 2: - row_size = sizeof(keyboard_keys_row_2) / sizeof(ByteInputKey); + row_size = COUNT_OF(keyboard_keys_row_2); break; + default: + furi_crash(NULL); } return row_size; @@ -97,6 +99,8 @@ static const ByteInputKey* byte_input_get_row(uint8_t row_index) { case 2: row = keyboard_keys_row_2; break; + default: + furi_crash(NULL); } return row; @@ -383,6 +387,7 @@ static void byte_input_dec_selected_byte(ByteInputModel* model) { if(model->selected_byte > 0) { model->selected_byte -= 1; + furi_assert(model->selected_byte >= model->first_visible_byte); if(model->selected_byte - model->first_visible_byte < 1) { if(model->first_visible_byte > 0) { model->first_visible_byte--; diff --git a/applications/services/gui/modules/dialog_ex.c b/applications/services/gui/modules/dialog_ex.c index 1cb46723..7c3ef9b4 100644 --- a/applications/services/gui/modules/dialog_ex.c +++ b/applications/services/gui/modules/dialog_ex.c @@ -147,7 +147,7 @@ DialogEx* dialog_ex_alloc() { DialogEx* dialog_ex = malloc(sizeof(DialogEx)); dialog_ex->view = view_alloc(); view_set_context(dialog_ex->view, dialog_ex); - view_allocate_model(dialog_ex->view, ViewModelTypeLockFree, sizeof(DialogExModel)); + view_allocate_model(dialog_ex->view, ViewModelTypeLocking, sizeof(DialogExModel)); view_set_draw_callback(dialog_ex->view, dialog_ex_view_draw_callback); view_set_input_callback(dialog_ex->view, dialog_ex_view_input_callback); with_view_model( diff --git a/applications/services/gui/modules/file_browser.c b/applications/services/gui/modules/file_browser.c index 57e0018e..e03a032c 100644 --- a/applications/services/gui/modules/file_browser.c +++ b/applications/services/gui/modules/file_browser.c @@ -514,7 +514,7 @@ static void browser_draw_list(Canvas* canvas, FileBrowserModel* model) { scroll_counter = 0; } - if(custom_icon_data) { + if(custom_icon_data) { //-V547 // Currently only 10*10 icons are supported canvas_draw_bitmap( canvas, 2, Y_OFFSET + 1 + i * FRAME_HEIGHT, 10, 10, custom_icon_data); @@ -544,6 +544,18 @@ static void browser_draw_list(Canvas* canvas, FileBrowserModel* model) { model->item_cnt); } + uint32_t folder_item_cnt = (model->is_root) ? (model->item_cnt) : (model->item_cnt - 1); + if(folder_item_cnt == 0) { + canvas_set_color(canvas, ColorBlack); + canvas_draw_str_aligned( + canvas, + canvas_width(canvas) / 2, + canvas_height(canvas) / 2, + AlignCenter, + AlignCenter, + ""); + } + furi_string_free(filename); } @@ -657,9 +669,7 @@ static bool file_browser_view_input_callback(InputEvent* event, void* context) { if(!is_root && !file_browser_worker_is_in_start_folder(browser->worker)) { consumed = true; - if(!is_root) { - file_browser_worker_folder_exit(browser->worker); - } + file_browser_worker_folder_exit(browser->worker); } } } diff --git a/applications/services/gui/modules/file_browser_worker.c b/applications/services/gui/modules/file_browser_worker.c index d8b515d0..a97a4d71 100644 --- a/applications/services/gui/modules/file_browser_worker.c +++ b/applications/services/gui/modules/file_browser_worker.c @@ -375,7 +375,7 @@ BrowserWorker* file_browser_worker_alloc( const char* filter_ext, bool skip_assets, bool hide_dot_files) { - BrowserWorker* browser = malloc(sizeof(BrowserWorker)); //-V773 + BrowserWorker* browser = malloc(sizeof(BrowserWorker)); idx_last_array_init(browser->idx_last); @@ -395,7 +395,7 @@ BrowserWorker* file_browser_worker_alloc( furi_thread_start(browser->thread); return browser; -} +} //-V773 void file_browser_worker_free(BrowserWorker* browser) { furi_assert(browser); diff --git a/applications/services/gui/modules/popup.c b/applications/services/gui/modules/popup.c index 08e8d8c2..d75abb95 100644 --- a/applications/services/gui/modules/popup.c +++ b/applications/services/gui/modules/popup.c @@ -117,7 +117,7 @@ Popup* popup_alloc() { popup->timer_enabled = false; view_set_context(popup->view, popup); - view_allocate_model(popup->view, ViewModelTypeLockFree, sizeof(PopupModel)); + view_allocate_model(popup->view, ViewModelTypeLocking, sizeof(PopupModel)); view_set_draw_callback(popup->view, popup_view_draw_callback); view_set_input_callback(popup->view, popup_view_input_callback); view_set_enter_callback(popup->view, popup_start_timer); diff --git a/applications/services/gui/modules/submenu.c b/applications/services/gui/modules/submenu.c index b7152a3d..72626c58 100644 --- a/applications/services/gui/modules/submenu.c +++ b/applications/services/gui/modules/submenu.c @@ -9,17 +9,48 @@ struct Submenu { }; typedef struct { - const char* label; + FuriString* label; uint32_t index; SubmenuItemCallback callback; void* callback_context; } SubmenuItem; -ARRAY_DEF(SubmenuItemArray, SubmenuItem, M_POD_OPLIST); +static void SubmenuItem_init(SubmenuItem* item) { + item->label = furi_string_alloc(); + item->index = 0; + item->callback = NULL; + item->callback_context = NULL; +} + +static void SubmenuItem_init_set(SubmenuItem* item, const SubmenuItem* src) { + item->label = furi_string_alloc_set(src->label); + item->index = src->index; + item->callback = src->callback; + item->callback_context = src->callback_context; +} + +static void SubmenuItem_set(SubmenuItem* item, const SubmenuItem* src) { + furi_string_set(item->label, src->label); + item->index = src->index; + item->callback = src->callback; + item->callback_context = src->callback_context; +} + +static void SubmenuItem_clear(SubmenuItem* item) { + furi_string_free(item->label); +} + +ARRAY_DEF( + SubmenuItemArray, + SubmenuItem, + (INIT(API_2(SubmenuItem_init)), + SET(API_6(SubmenuItem_set)), + INIT_SET(API_6(SubmenuItem_init_set)), + CLEAR(API_2(SubmenuItem_clear)))) typedef struct { SubmenuItemArray_t items; - const char* header; + FuriString* header; size_t position; size_t window_position; } SubmenuModel; @@ -36,9 +67,9 @@ static void submenu_view_draw_callback(Canvas* canvas, void* _model) { canvas_clear(canvas); - if(model->header) { + if(!furi_string_empty(model->header)) { canvas_set_font(canvas, FontPrimary); - canvas_draw_str(canvas, 4, 11, model->header); + canvas_draw_str(canvas, 4, 11, furi_string_get_cstr(model->header)); } canvas_set_font(canvas, FontSecondary); @@ -48,8 +79,8 @@ static void submenu_view_draw_callback(Canvas* canvas, void* _model) { for(SubmenuItemArray_it(it, model->items); !SubmenuItemArray_end_p(it); SubmenuItemArray_next(it)) { const size_t item_position = position - model->window_position; - const size_t items_on_screen = model->header ? 3 : 4; - uint8_t y_offset = model->header ? 16 : 0; + const size_t items_on_screen = furi_string_empty(model->header) ? 4 : 3; + uint8_t y_offset = furi_string_empty(model->header) ? 0 : 16; if(item_position < items_on_screen) { if(position == model->position) { @@ -134,7 +165,7 @@ Submenu* submenu_alloc() { SubmenuItemArray_init(model->items); model->position = 0; model->window_position = 0; - model->header = NULL; + model->header = furi_string_alloc(); }, true); @@ -145,7 +176,13 @@ void submenu_free(Submenu* submenu) { furi_assert(submenu); with_view_model( - submenu->view, SubmenuModel * model, { SubmenuItemArray_clear(model->items); }, true); + submenu->view, + SubmenuModel * model, + { + furi_string_free(model->header); + SubmenuItemArray_clear(model->items); + }, + true); view_free(submenu->view); free(submenu); } @@ -170,7 +207,7 @@ void submenu_add_item( SubmenuModel * model, { item = SubmenuItemArray_push_new(model->items); - item->label = label; + furi_string_set_str(item->label, label); item->index = index; item->callback = callback; item->callback_context = callback_context; @@ -188,7 +225,7 @@ void submenu_reset(Submenu* submenu) { SubmenuItemArray_reset(model->items); model->position = 0; model->window_position = 0; - model->header = NULL; + furi_string_reset(model->header); }, true); } @@ -221,12 +258,15 @@ void submenu_set_selected_item(Submenu* submenu, uint32_t index) { model->window_position -= 1; } - const size_t items_on_screen = model->header ? 3 : 4; + const size_t items_on_screen = furi_string_empty(model->header) ? 4 : 3; if(items_size <= items_on_screen) { model->window_position = 0; - } else if(model->window_position >= items_size - items_on_screen) { - model->window_position = items_size - items_on_screen; + } else { + const size_t pos = items_size - items_on_screen; + if(model->window_position > pos) { + model->window_position = pos; + } } }, true); @@ -237,13 +277,12 @@ void submenu_process_up(Submenu* submenu) { submenu->view, SubmenuModel * model, { - const size_t items_on_screen = model->header ? 3 : 4; + const size_t items_on_screen = furi_string_empty(model->header) ? 4 : 3; const size_t items_size = SubmenuItemArray_size(model->items); if(model->position > 0) { model->position--; - if((model->position - model->window_position < 1) && - (model->window_position > 0)) { + if((model->position == model->window_position) && (model->window_position > 0)) { model->window_position--; } } else { @@ -261,7 +300,7 @@ void submenu_process_down(Submenu* submenu) { submenu->view, SubmenuModel * model, { - const size_t items_on_screen = model->header ? 3 : 4; + const size_t items_on_screen = furi_string_empty(model->header) ? 4 : 3; const size_t items_size = SubmenuItemArray_size(model->items); if(model->position < items_size - 1) { @@ -301,5 +340,14 @@ void submenu_set_header(Submenu* submenu, const char* header) { furi_assert(submenu); with_view_model( - submenu->view, SubmenuModel * model, { model->header = header; }, true); + submenu->view, + SubmenuModel * model, + { + if(header == NULL) { + furi_string_reset(model->header); + } else { + furi_string_set_str(model->header, header); + } + }, + true); } diff --git a/applications/services/gui/modules/text_input.c b/applications/services/gui/modules/text_input.c index 540e4b7c..7c419d96 100644 --- a/applications/services/gui/modules/text_input.c +++ b/applications/services/gui/modules/text_input.c @@ -92,14 +92,16 @@ static uint8_t get_row_size(uint8_t row_index) { switch(row_index + 1) { case 1: - row_size = sizeof(keyboard_keys_row_1) / sizeof(TextInputKey); + row_size = COUNT_OF(keyboard_keys_row_1); break; case 2: - row_size = sizeof(keyboard_keys_row_2) / sizeof(TextInputKey); + row_size = COUNT_OF(keyboard_keys_row_2); break; case 3: - row_size = sizeof(keyboard_keys_row_3) / sizeof(TextInputKey); + row_size = COUNT_OF(keyboard_keys_row_3); break; + default: + furi_crash(NULL); } return row_size; @@ -118,6 +120,8 @@ static const TextInputKey* get_row(uint8_t row_index) { case 3: row = keyboard_keys_row_3; break; + default: + furi_crash(NULL); } return row; @@ -184,7 +188,7 @@ static void text_input_view_draw_callback(Canvas* canvas, void* _model) { canvas_set_font(canvas, FontKeyboard); - for(uint8_t row = 0; row <= keyboard_row_count; row++) { + for(uint8_t row = 0; row < keyboard_row_count; row++) { const uint8_t column_count = get_row_size(row); const TextInputKey* keys = get_row(row); @@ -303,7 +307,7 @@ static void text_input_handle_right(TextInput* text_input, TextInputModel* model static void text_input_handle_ok(TextInput* text_input, TextInputModel* model, bool shift) { char selected = get_selected_char(model); - uint8_t text_length = strlen(model->text_buffer); + size_t text_length = strlen(model->text_buffer); if(shift) { selected = char_to_uppercase(selected); @@ -481,7 +485,6 @@ void text_input_reset(TextInput* text_input) { text_input->view, TextInputModel * model, { - model->text_buffer_size = 0; model->header = ""; model->selected_row = 0; model->selected_column = 0; diff --git a/applications/services/gui/modules/validators.c b/applications/services/gui/modules/validators.c index 0463b1c2..9c5d0be8 100644 --- a/applications/services/gui/modules/validators.c +++ b/applications/services/gui/modules/validators.c @@ -18,15 +18,12 @@ bool validator_is_file_callback(const char* text, FuriString* error, void* conte } } - bool ret = true; FuriString* path = furi_string_alloc_printf( "%s/%s%s", instance->app_path_folder, text, instance->app_extension); Storage* storage = furi_record_open(RECORD_STORAGE); - if(storage_common_stat(storage, furi_string_get_cstr(path), NULL) == FSE_OK) { - ret = false; + const bool ret = storage_common_stat(storage, furi_string_get_cstr(path), NULL) != FSE_OK; + if(!ret) { furi_string_printf(error, "This name\nexists!\nChoose\nanother one."); - } else { - ret = true; } furi_string_free(path); furi_record_close(RECORD_STORAGE); diff --git a/applications/services/gui/modules/variable_item_list.c b/applications/services/gui/modules/variable_item_list.c index a9b89d63..5060985e 100644 --- a/applications/services/gui/modules/variable_item_list.c +++ b/applications/services/gui/modules/variable_item_list.c @@ -188,8 +188,8 @@ void variable_item_list_process_up(VariableItemList* variable_item_list) { uint8_t items_on_screen = 4; if(model->position > 0) { model->position--; - if(((model->position - model->window_position) < 1) && - model->window_position > 0) { + + if((model->position == model->window_position) && (model->window_position > 0)) { model->window_position--; } } else { diff --git a/applications/services/gui/modules/widget_elements/widget_element_button.c b/applications/services/gui/modules/widget_elements/widget_element_button.c index e3267058..3bfae9df 100644 --- a/applications/services/gui/modules/widget_elements/widget_element_button.c +++ b/applications/services/gui/modules/widget_elements/widget_element_button.c @@ -60,7 +60,7 @@ WidgetElement* widget_element_button_create( ButtonCallback callback, void* context) { // Allocate and init model - GuiButtonModel* model = malloc(sizeof(GuiButtonModel)); //-V773 + GuiButtonModel* model = malloc(sizeof(GuiButtonModel)); model->button_type = button_type; model->callback = callback; model->context = context; @@ -75,4 +75,4 @@ WidgetElement* widget_element_button_create( gui_button->model = model; return gui_button; -} +} //-V773 diff --git a/applications/services/gui/modules/widget_elements/widget_element_string.c b/applications/services/gui/modules/widget_elements/widget_element_string.c index feb22ad1..4bf7dd69 100644 --- a/applications/services/gui/modules/widget_elements/widget_element_string.c +++ b/applications/services/gui/modules/widget_elements/widget_element_string.c @@ -62,4 +62,4 @@ WidgetElement* widget_element_string_create( gui_string->model = model; return gui_string; -} +} //-V773 diff --git a/applications/services/gui/modules/widget_elements/widget_element_string_multiline.c b/applications/services/gui/modules/widget_elements/widget_element_string_multiline.c index 9ad2a1a8..3fc6b309 100644 --- a/applications/services/gui/modules/widget_elements/widget_element_string_multiline.c +++ b/applications/services/gui/modules/widget_elements/widget_element_string_multiline.c @@ -63,4 +63,4 @@ WidgetElement* widget_element_string_multiline_create( gui_string->model = model; return gui_string; -} +} //-V773 diff --git a/applications/services/gui/modules/widget_elements/widget_element_text_box.c b/applications/services/gui/modules/widget_elements/widget_element_text_box.c index 2c694820..98f8e83d 100644 --- a/applications/services/gui/modules/widget_elements/widget_element_text_box.c +++ b/applications/services/gui/modules/widget_elements/widget_element_text_box.c @@ -71,4 +71,4 @@ WidgetElement* widget_element_text_box_create( gui_string->model = model; return gui_string; -} +} //-V773 diff --git a/applications/services/gui/modules/widget_elements/widget_element_text_scroll.c b/applications/services/gui/modules/widget_elements/widget_element_text_scroll.c index a4d76638..d8fc1131 100644 --- a/applications/services/gui/modules/widget_elements/widget_element_text_scroll.c +++ b/applications/services/gui/modules/widget_elements/widget_element_text_scroll.c @@ -241,4 +241,4 @@ WidgetElement* widget_element_text_scroll_create( text_scroll->model_mutex = furi_mutex_alloc(FuriMutexTypeNormal); return text_scroll; -} +} //-V773 diff --git a/applications/services/gui/view.c b/applications/services/gui/view.c index 7ab6d15b..50c05a40 100644 --- a/applications/services/gui/view.c +++ b/applications/services/gui/view.c @@ -86,7 +86,7 @@ void view_allocate_model(View* view, ViewModelType type, size_t size) { model->data = malloc(size); view->model = model; } else { - furi_assert(false); + furi_crash(NULL); } } @@ -103,7 +103,7 @@ void view_free_model(View* view) { free(model); view->model = NULL; } else { - furi_assert(false); + furi_crash(NULL); } } diff --git a/applications/services/input/input.c b/applications/services/input/input.c index 1d02df1e..e1e581c9 100644 --- a/applications/services/input/input.c +++ b/applications/services/input/input.c @@ -80,9 +80,7 @@ int32_t input_srv(void* p) { #ifdef SRV_CLI input->cli = furi_record_open(RECORD_CLI); - if(input->cli) { - cli_add_command(input->cli, "input", CliCommandFlagParallelSafe, input_cli, input); - } + cli_add_command(input->cli, "input", CliCommandFlagParallelSafe, input_cli, input); #endif input->pin_states = malloc(input_pins_count * sizeof(InputPinState)); diff --git a/applications/services/input/input_cli.c b/applications/services/input/input_cli.c index d9a8eaeb..f7e904b1 100644 --- a/applications/services/input/input_cli.c +++ b/applications/services/input/input_cli.c @@ -89,7 +89,7 @@ static void input_cli_send(Cli* cli, FuriString* args, Input* input) { parsed = true; } while(false); - if(parsed) { + if(parsed) { //-V547 furi_pubsub_publish(input->event_pubsub, &event); } else { input_cli_send_print_usage(); diff --git a/applications/services/loader/loader.c b/applications/services/loader/loader.c index 93171972..97d1e6e4 100644 --- a/applications/services/loader/loader.c +++ b/applications/services/loader/loader.c @@ -280,7 +280,7 @@ static void loader_thread_state_callback(FuriThreadState thread_state, void* con furi_hal_power_insomnia_enter(); } } else if(thread_state == FuriThreadStateStopped) { - FURI_LOG_I(TAG, "Application stopped. Free heap: %d", memmgr_get_free_heap()); + FURI_LOG_I(TAG, "Application stopped. Free heap: %zu", memmgr_get_free_heap()); if(loader_instance->application_arguments) { free(loader_instance->application_arguments); diff --git a/applications/services/power/power_service/views/power_off.c b/applications/services/power/power_service/views/power_off.c index f14a18d7..3a1addba 100644 --- a/applications/services/power/power_service/views/power_off.c +++ b/applications/services/power/power_service/views/power_off.c @@ -26,7 +26,7 @@ static void power_off_draw_callback(Canvas* canvas, void* _model) { canvas_set_font(canvas, FontSecondary); if(model->response == PowerOffResponseDefault) { - snprintf(buff, sizeof(buff), "Charge me!\nOff in %lds!", model->time_left_sec); + snprintf(buff, sizeof(buff), "Charge me!\nOff in %lus!", model->time_left_sec); elements_multiline_text_aligned(canvas, 70, 23, AlignLeft, AlignTop, buff); elements_button_left(canvas, "Cancel"); diff --git a/applications/services/rpc/rpc_app.c b/applications/services/rpc/rpc_app.c index b96f043a..cc18b6ce 100644 --- a/applications/services/rpc/rpc_app.c +++ b/applications/services/rpc/rpc_app.c @@ -39,9 +39,9 @@ static void rpc_system_app_start_process(const PB_Main* request, void* context) furi_assert(!rpc_app->last_id); furi_assert(!rpc_app->last_data); - FURI_LOG_D(TAG, "StartProcess: id %ld", request->command_id); + FURI_LOG_D(TAG, "StartProcess: id %lu", request->command_id); - PB_CommandStatus result = PB_CommandStatus_ERROR_APP_CANT_START; + PB_CommandStatus result; Loader* loader = furi_record_open(RECORD_LOADER); const char* app_name = request->content.app_start_request.name; @@ -62,7 +62,7 @@ static void rpc_system_app_start_process(const PB_Main* request, void* context) } else if(status == LoaderStatusOk) { result = PB_CommandStatus_OK; } else { - furi_crash("Programming Error"); + furi_crash(NULL); } } else { result = PB_CommandStatus_ERROR_INVALID_PARAMETERS; @@ -70,7 +70,7 @@ static void rpc_system_app_start_process(const PB_Main* request, void* context) furi_record_close(RECORD_LOADER); - FURI_LOG_D(TAG, "StartProcess: response id %ld, result %d", request->command_id, result); + FURI_LOG_D(TAG, "StartProcess: response id %lu, result %d", request->command_id, result); rpc_send_and_release_empty(session, request->command_id, result); } @@ -117,7 +117,7 @@ static void rpc_system_app_exit_request(const PB_Main* request, void* context) { PB_CommandStatus status; if(rpc_app->app_callback) { - FURI_LOG_D(TAG, "ExitRequest: id %ld", request->command_id); + FURI_LOG_D(TAG, "ExitRequest: id %lu", request->command_id); furi_assert(!rpc_app->last_id); furi_assert(!rpc_app->last_data); rpc_app->last_id = request->command_id; @@ -125,7 +125,7 @@ static void rpc_system_app_exit_request(const PB_Main* request, void* context) { } else { status = PB_CommandStatus_ERROR_APP_NOT_RUNNING; FURI_LOG_E( - TAG, "ExitRequest: APP_NOT_RUNNING, id %ld, status: %d", request->command_id, status); + TAG, "ExitRequest: APP_NOT_RUNNING, id %lu, status: %d", request->command_id, status); rpc_send_and_release_empty(session, request->command_id, status); } } @@ -142,7 +142,7 @@ static void rpc_system_app_load_file(const PB_Main* request, void* context) { PB_CommandStatus status; if(rpc_app->app_callback) { - FURI_LOG_D(TAG, "LoadFile: id %ld", request->command_id); + FURI_LOG_D(TAG, "LoadFile: id %lu", request->command_id); furi_assert(!rpc_app->last_id); furi_assert(!rpc_app->last_data); rpc_app->last_id = request->command_id; @@ -151,7 +151,7 @@ static void rpc_system_app_load_file(const PB_Main* request, void* context) { } else { status = PB_CommandStatus_ERROR_APP_NOT_RUNNING; FURI_LOG_E( - TAG, "LoadFile: APP_NOT_RUNNING, id %ld, status: %d", request->command_id, status); + TAG, "LoadFile: APP_NOT_RUNNING, id %lu, status: %d", request->command_id, status); rpc_send_and_release_empty(session, request->command_id, status); } } @@ -177,7 +177,7 @@ static void rpc_system_app_button_press(const PB_Main* request, void* context) { } else { status = PB_CommandStatus_ERROR_APP_NOT_RUNNING; FURI_LOG_E( - TAG, "ButtonPress: APP_NOT_RUNNING, id %ld, status: %d", request->command_id, status); + TAG, "ButtonPress: APP_NOT_RUNNING, id %lu, status: %d", request->command_id, status); rpc_send_and_release_empty(session, request->command_id, status); } } @@ -202,7 +202,7 @@ static void rpc_system_app_button_release(const PB_Main* request, void* context) } else { status = PB_CommandStatus_ERROR_APP_NOT_RUNNING; FURI_LOG_E( - TAG, "ButtonRelease: APP_NOT_RUNNING, id %ld, status: %d", request->command_id, status); + TAG, "ButtonRelease: APP_NOT_RUNNING, id %lu, status: %d", request->command_id, status); rpc_send_and_release_empty(session, request->command_id, status); } } @@ -300,7 +300,7 @@ void rpc_system_app_confirm(RpcAppSystem* rpc_app, RpcAppSystemEvent event, bool free(rpc_app->last_data); rpc_app->last_data = NULL; } - FURI_LOG_D(TAG, "AppConfirm: event %d last_id %ld status %d", event, last_id, status); + FURI_LOG_D(TAG, "AppConfirm: event %d last_id %lu status %d", event, last_id, status); rpc_send_and_release_empty(session, last_id, status); break; default: diff --git a/applications/services/rpc/rpc_cli.c b/applications/services/rpc/rpc_cli.c index 82023e31..d14b8eee 100644 --- a/applications/services/rpc/rpc_cli.c +++ b/applications/services/rpc/rpc_cli.c @@ -44,7 +44,7 @@ void rpc_cli_command_start_session(Cli* cli, FuriString* args, void* context) { Rpc* rpc = context; uint32_t mem_before = memmgr_get_free_heap(); - FURI_LOG_D(TAG, "Free memory %ld", mem_before); + FURI_LOG_D(TAG, "Free memory %lu", mem_before); furi_hal_usb_lock(); RpcSession* rpc_session = rpc_session_open(rpc); diff --git a/applications/services/rpc/rpc_debug.c b/applications/services/rpc/rpc_debug.c index 8eb81dec..edc2b002 100644 --- a/applications/services/rpc/rpc_debug.c +++ b/applications/services/rpc/rpc_debug.c @@ -10,7 +10,7 @@ static size_t rpc_debug_print_file_msg( for(size_t i = 0; i < msg_files_size; ++i, ++msg_file) { furi_string_cat_printf( str, - "%s[%c] size: %5ld", + "%s[%c] size: %5lu", prefix, msg_file->type == PB_Storage_File_FileType_DIR ? 'd' : 'f', msg_file->size); @@ -40,7 +40,7 @@ void rpc_debug_print_data(const char* prefix, uint8_t* buffer, size_t size) { str = furi_string_alloc(); furi_string_reserve(str, 100 + size * 5); - furi_string_cat_printf(str, "\r\n%s DEC(%d): {", prefix, size); + furi_string_cat_printf(str, "\r\n%s DEC(%zu): {", prefix, size); for(size_t i = 0; i < size; ++i) { furi_string_cat_printf(str, "%d, ", buffer[i]); } @@ -50,7 +50,7 @@ void rpc_debug_print_data(const char* prefix, uint8_t* buffer, size_t size) { furi_string_reset(str); furi_string_reserve(str, 100 + size * 3); - furi_string_cat_printf(str, "%s HEX(%d): {", prefix, size); + furi_string_cat_printf(str, "%s HEX(%zu): {", prefix, size); for(size_t i = 0; i < size; ++i) { furi_string_cat_printf(str, "%02X", buffer[i]); } @@ -66,7 +66,7 @@ void rpc_debug_print_message(const PB_Main* message) { furi_string_cat_printf( str, - "PB_Main: {\r\n\tresult: %d cmd_id: %ld (%s)\r\n", + "PB_Main: {\r\n\tresult: %d cmd_id: %lu (%s)\r\n", message->command_status, message->command_id, message->has_next ? "has_next" : "last"); @@ -110,9 +110,9 @@ void rpc_debug_print_message(const PB_Main* message) { } case PB_Main_storage_md5sum_response_tag: { furi_string_cat_printf(str, "\tmd5sum_response {\r\n"); - const char* path = message->content.storage_md5sum_response.md5sum; - if(path) { - furi_string_cat_printf(str, "\t\tmd5sum: %s\r\n", path); + const char* md5sum = message->content.storage_md5sum_response.md5sum; + if(md5sum) { //-V547 + furi_string_cat_printf(str, "\t\tmd5sum: %s\r\n", md5sum); } break; } diff --git a/applications/services/rpc/rpc_storage.c b/applications/services/rpc/rpc_storage.c index 3c6ff7f9..c4493cc7 100644 --- a/applications/services/rpc/rpc_storage.c +++ b/applications/services/rpc/rpc_storage.c @@ -597,7 +597,7 @@ static void rpc_system_storage_md5sum_process(const PB_Main* request, void* cont char* md5sum = response.content.storage_md5sum_response.md5sum; size_t md5sum_size = sizeof(response.content.storage_md5sum_response.md5sum); (void)md5sum_size; - furi_assert(hash_size <= ((md5sum_size - 1) / 2)); + furi_assert(hash_size <= ((md5sum_size - 1) / 2)); //-V547 for(uint8_t i = 0; i < hash_size; i++) { md5sum += snprintf(md5sum, md5sum_size, "%02x", hash[i]); } diff --git a/applications/services/rpc/rpc_system.c b/applications/services/rpc/rpc_system.c index a17be7d2..77dca4a1 100644 --- a/applications/services/rpc/rpc_system.c +++ b/applications/services/rpc/rpc_system.c @@ -30,7 +30,6 @@ static void rpc_system_system_ping_process(const PB_Main* request, void* context } PB_Main response = PB_Main_init_default; - response.has_next = false; response.command_status = PB_CommandStatus_OK; response.command_id = request->command_id; response.which_content = PB_Main_system_ping_response_tag; diff --git a/applications/services/storage/storage_external_api.c b/applications/services/storage/storage_external_api.c index 2c3a7bfc..6929a9cb 100644 --- a/applications/services/storage/storage_external_api.c +++ b/applications/services/storage/storage_external_api.c @@ -385,9 +385,7 @@ FS_Error storage_common_remove(Storage* storage, const char* path) { FS_Error storage_common_rename(Storage* storage, const char* old_path, const char* new_path) { FS_Error error = storage_common_copy(storage, old_path, new_path); if(error == FSE_OK) { - if(storage_simply_remove_recursive(storage, old_path)) { - error = FSE_OK; - } else { + if(!storage_simply_remove_recursive(storage, old_path)) { error = FSE_INTERNAL; } } @@ -743,7 +741,7 @@ bool storage_simply_remove_recursive(Storage* storage, const char* path) { return true; } - char* name = malloc(MAX_NAME_LENGTH + 1); + char* name = malloc(MAX_NAME_LENGTH + 1); //-V799 File* dir = storage_file_alloc(storage); cur_dir = furi_string_alloc_set(path); bool go_deeper = false; @@ -790,7 +788,7 @@ bool storage_simply_remove_recursive(Storage* storage, const char* path) { furi_string_free(cur_dir); free(name); return result; -} +} //-V773 bool storage_simply_remove(Storage* storage, const char* path) { FS_Error result; diff --git a/applications/services/storage/storage_glue.c b/applications/services/storage/storage_glue.c index c6ff08bd..22f2e3df 100644 --- a/applications/services/storage/storage_glue.c +++ b/applications/services/storage/storage_glue.c @@ -17,7 +17,7 @@ void storage_file_init_set(StorageFile* obj, const StorageFile* src) { obj->path = furi_string_alloc_set(src->path); } -void storage_file_set(StorageFile* obj, const StorageFile* src) { +void storage_file_set(StorageFile* obj, const StorageFile* src) { //-V524 obj->file = src->file; obj->type = src->type; obj->file_data = src->file_data; @@ -172,7 +172,6 @@ void storage_push_storage_file( StorageType type, StorageData* storage) { StorageFile* storage_file = StorageFileList_push_new(storage->files); - furi_check(storage_file != NULL); file->file_id = (uint32_t)storage_file; storage_file->file = file; diff --git a/applications/services/storage/storages/storage_int.c b/applications/services/storage/storages/storage_int.c index 4fa5d130..2534d47a 100644 --- a/applications/services/storage/storages/storage_int.c +++ b/applications/services/storage/storages/storage_int.c @@ -77,7 +77,7 @@ static int storage_int_device_read( FURI_LOG_T( TAG, - "Device read: block %ld, off %ld, buffer: %p, size %ld, translated address: %p", + "Device read: block %lu, off %lu, buffer: %p, size %lu, translated address: %p", block, off, buffer, @@ -100,7 +100,7 @@ static int storage_int_device_prog( FURI_LOG_T( TAG, - "Device prog: block %ld, off %ld, buffer: %p, size %ld, translated address: %p", + "Device prog: block %lu, off %lu, buffer: %p, size %lu, translated address: %p", block, off, buffer, @@ -122,7 +122,7 @@ static int storage_int_device_erase(const struct lfs_config* c, lfs_block_t bloc LFSData* lfs_data = c->context; size_t page = lfs_data->start_page + block; - FURI_LOG_D(TAG, "Device erase: page %ld, translated page: %x", block, page); + FURI_LOG_D(TAG, "Device erase: page %lu, translated page: %zx", block, page); furi_hal_flash_erase(page); return 0; @@ -240,56 +240,38 @@ static void storage_int_lfs_mount(LFSData* lfs_data, StorageData* storage) { /****************** Common Functions ******************/ static FS_Error storage_int_parse_error(int error) { - FS_Error result = FSE_INTERNAL; + FS_Error result; if(error >= LFS_ERR_OK) { result = FSE_OK; } else { switch(error) { - case LFS_ERR_IO: - result = FSE_INTERNAL; - break; - case LFS_ERR_CORRUPT: - result = FSE_INTERNAL; - break; case LFS_ERR_NOENT: result = FSE_NOT_EXIST; break; case LFS_ERR_EXIST: result = FSE_EXIST; break; - case LFS_ERR_NOTDIR: - result = FSE_INVALID_NAME; - break; - case LFS_ERR_ISDIR: - result = FSE_INVALID_NAME; - break; case LFS_ERR_NOTEMPTY: result = FSE_DENIED; break; - case LFS_ERR_BADF: - result = FSE_INVALID_NAME; - break; - case LFS_ERR_FBIG: - result = FSE_INTERNAL; - break; case LFS_ERR_INVAL: - result = FSE_INVALID_PARAMETER; - break; - case LFS_ERR_NOSPC: - result = FSE_INTERNAL; - break; - case LFS_ERR_NOMEM: - result = FSE_INTERNAL; - break; case LFS_ERR_NOATTR: result = FSE_INVALID_PARAMETER; break; + case LFS_ERR_BADF: + case LFS_ERR_ISDIR: + case LFS_ERR_NOTDIR: case LFS_ERR_NAMETOOLONG: result = FSE_INVALID_NAME; break; + case LFS_ERR_IO: + case LFS_ERR_FBIG: + case LFS_ERR_NOSPC: + case LFS_ERR_NOMEM: + case LFS_ERR_CORRUPT: default: - break; + result = FSE_INTERNAL; } } @@ -740,7 +722,7 @@ void storage_int_init(StorageData* storage) { LFSData* lfs_data = storage_int_lfs_data_alloc(); FURI_LOG_I( TAG, - "Config: start %p, read %ld, write %ld, page size: %ld, page count: %ld, cycles: %ld", + "Config: start %p, read %lu, write %lu, page size: %lu, page count: %lu, cycles: %ld", (void*)lfs_data->start_address, lfs_data->config.read_size, lfs_data->config.prog_size, diff --git a/applications/settings/about/about.c b/applications/settings/about/about.c index 1719e188..560a683c 100644 --- a/applications/settings/about/about.c +++ b/applications/settings/about/about.c @@ -119,7 +119,7 @@ static DialogMessageButton fw_version_screen(DialogsApp* dialogs, DialogMessage* c2_ver = ble_glue_get_c2_info(); #endif - if(!ver) { + if(!ver) { //-V1051 furi_string_cat_printf(buffer, "No info\n"); } else { furi_string_cat_printf( @@ -208,4 +208,4 @@ int32_t about_settings_app(void* p) { furi_record_close(RECORD_GUI); return 0; -} \ No newline at end of file +} diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_favorite.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_favorite.c index cf474c54..94c5ee9f 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_favorite.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_favorite.c @@ -84,7 +84,7 @@ bool desktop_settings_scene_favorite_on_event(void* context, SceneManagerEvent e scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneFavorite); if(event.type == SceneManagerEventTypeCustom) { - if(strcmp(FLIPPER_APPS[event.event].name, FAP_LOADER_APP_NAME)) { + if(strcmp(FLIPPER_APPS[event.event].name, FAP_LOADER_APP_NAME) != 0) { if(primary_favorite) { app->settings.favorite_primary.is_external = false; strncpy( diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup_howto.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup_howto.c index b8d630f2..ec128246 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup_howto.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup_howto.c @@ -32,9 +32,7 @@ bool desktop_settings_scene_pin_setup_howto_on_event(void* context, SceneManager consumed = true; break; default: - furi_assert(0); - consumed = true; - break; + furi_crash(NULL); } } return consumed; diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup_howto2.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup_howto2.c index 477d1f27..44b8e1bf 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup_howto2.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup_howto2.c @@ -52,9 +52,7 @@ bool desktop_settings_scene_pin_setup_howto2_on_event(void* context, SceneManage break; } default: - furi_assert(0); - consumed = true; - break; + furi_crash(NULL); } } return consumed; diff --git a/applications/settings/desktop_settings/views/desktop_settings_view_pin_setup_howto.c b/applications/settings/desktop_settings/views/desktop_settings_view_pin_setup_howto.c index 3831be8c..26aa7c3e 100644 --- a/applications/settings/desktop_settings/views/desktop_settings_view_pin_setup_howto.c +++ b/applications/settings/desktop_settings/views/desktop_settings_view_pin_setup_howto.c @@ -17,7 +17,7 @@ struct DesktopSettingsViewPinSetupHowto { static void desktop_settings_view_pin_setup_howto_draw(Canvas* canvas, void* model) { furi_assert(canvas); - furi_assert(model); + UNUSED(model); canvas_draw_icon(canvas, 16, 18, &I_Pin_attention_dpad_29x29); elements_button_right(canvas, "Next"); @@ -57,7 +57,6 @@ void desktop_settings_view_pin_setup_howto_set_callback( DesktopSettingsViewPinSetupHowto* desktop_settings_view_pin_setup_howto_alloc() { DesktopSettingsViewPinSetupHowto* view = malloc(sizeof(DesktopSettingsViewPinSetupHowto)); view->view = view_alloc(); - view_allocate_model(view->view, ViewModelTypeLockFree, 1); view_set_context(view->view, view); view_set_draw_callback(view->view, desktop_settings_view_pin_setup_howto_draw); view_set_input_callback(view->view, desktop_settings_view_pin_setup_howto_input); diff --git a/applications/settings/desktop_settings/views/desktop_settings_view_pin_setup_howto2.c b/applications/settings/desktop_settings/views/desktop_settings_view_pin_setup_howto2.c index ab1fa238..c28826e6 100644 --- a/applications/settings/desktop_settings/views/desktop_settings_view_pin_setup_howto2.c +++ b/applications/settings/desktop_settings/views/desktop_settings_view_pin_setup_howto2.c @@ -18,7 +18,7 @@ struct DesktopSettingsViewPinSetupHowto2 { static void desktop_settings_view_pin_setup_howto2_draw(Canvas* canvas, void* model) { furi_assert(canvas); - furi_assert(model); + UNUSED(model); canvas_set_font(canvas, FontSecondary); elements_multiline_text_aligned( @@ -79,7 +79,6 @@ void desktop_settings_view_pin_setup_howto2_set_ok_callback( DesktopSettingsViewPinSetupHowto2* desktop_settings_view_pin_setup_howto2_alloc() { DesktopSettingsViewPinSetupHowto2* view = malloc(sizeof(DesktopSettingsViewPinSetupHowto2)); view->view = view_alloc(); - view_allocate_model(view->view, ViewModelTypeLockFree, 1); view_set_context(view->view, view); view_set_draw_callback(view->view, desktop_settings_view_pin_setup_howto2_draw); view_set_input_callback(view->view, desktop_settings_view_pin_setup_howto2_input); diff --git a/applications/settings/power_settings_app/views/battery_info.c b/applications/settings/power_settings_app/views/battery_info.c index d760164b..5353a2e2 100644 --- a/applications/settings/power_settings_app/views/battery_info.c +++ b/applications/settings/power_settings_app/views/battery_info.c @@ -49,7 +49,7 @@ static void draw_battery(Canvas* canvas, BatteryInfoModel* data, int x, int y) { snprintf( value, sizeof(value), - "%ld.%ldV %ldmA", + "%lu.%luV %lumA", (uint32_t)(data->vbus_voltage), (uint32_t)(data->vbus_voltage * 10) % 10, charge_current); @@ -75,7 +75,7 @@ static void draw_battery(Canvas* canvas, BatteryInfoModel* data, int x, int y) { snprintf( value, sizeof(value), - "%ld.%ldV", + "%lu.%luV", (uint32_t)(data->charging_voltage), (uint32_t)(data->charging_voltage * 10) % 10); } else { @@ -100,14 +100,14 @@ static void battery_info_draw_callback(Canvas* canvas, void* context) { char voltage[10]; char health[10]; - snprintf(batt_level, sizeof(batt_level), "%ld%%", (uint32_t)model->charge); - snprintf(temperature, sizeof(temperature), "%ld C", (uint32_t)model->gauge_temperature); + snprintf(batt_level, sizeof(batt_level), "%lu%%", (uint32_t)model->charge); + snprintf(temperature, sizeof(temperature), "%lu C", (uint32_t)model->gauge_temperature); snprintf( voltage, sizeof(voltage), - "%ld.%01ld V", + "%lu.%01lu V", (uint32_t)model->gauge_voltage, - (uint32_t)(model->gauge_voltage * 10) % 10); + (uint32_t)(model->gauge_voltage * 10) % 10UL); snprintf(health, sizeof(health), "%d%%", model->health); draw_stat(canvas, 8, 42, &I_Battery_16x16, batt_level); diff --git a/applications/settings/storage_settings/scenes/storage_settings_scene_format_confirm.c b/applications/settings/storage_settings/scenes/storage_settings_scene_format_confirm.c index 261ef199..8af065bf 100644 --- a/applications/settings/storage_settings/scenes/storage_settings_scene_format_confirm.c +++ b/applications/settings/storage_settings/scenes/storage_settings_scene_format_confirm.c @@ -40,8 +40,6 @@ bool storage_settings_scene_format_confirm_on_event(void* context, SceneManagerE if(event.type == SceneManagerEventTypeCustom) { switch(event.event) { case DialogExResultLeft: - consumed = scene_manager_previous_scene(app->scene_manager); - break; case DialogExResultCenter: consumed = scene_manager_previous_scene(app->scene_manager); break; diff --git a/applications/settings/storage_settings/scenes/storage_settings_scene_sd_info.c b/applications/settings/storage_settings/scenes/storage_settings_scene_sd_info.c index ede610d0..0c398ed5 100644 --- a/applications/settings/storage_settings/scenes/storage_settings_scene_sd_info.c +++ b/applications/settings/storage_settings/scenes/storage_settings_scene_sd_info.c @@ -47,8 +47,6 @@ bool storage_settings_scene_sd_info_on_event(void* context, SceneManagerEvent ev if(event.type == SceneManagerEventTypeCustom) { switch(event.event) { case DialogExResultLeft: - consumed = scene_manager_previous_scene(app->scene_manager); - break; case DialogExResultCenter: consumed = scene_manager_previous_scene(app->scene_manager); break; diff --git a/applications/settings/storage_settings/scenes/storage_settings_scene_unmount_confirm.c b/applications/settings/storage_settings/scenes/storage_settings_scene_unmount_confirm.c index 2b485b7f..0c15116b 100644 --- a/applications/settings/storage_settings/scenes/storage_settings_scene_unmount_confirm.c +++ b/applications/settings/storage_settings/scenes/storage_settings_scene_unmount_confirm.c @@ -41,8 +41,6 @@ bool storage_settings_scene_unmount_confirm_on_event(void* context, SceneManager if(event.type == SceneManagerEventTypeCustom) { switch(event.event) { case DialogExResultCenter: - consumed = scene_manager_previous_scene(app->scene_manager); - break; case DialogExResultLeft: consumed = scene_manager_previous_scene(app->scene_manager); break; diff --git a/applications/system/updater/util/update_task_worker_backup.c b/applications/system/updater/util/update_task_worker_backup.c index ce62da2a..78040106 100644 --- a/applications/system/updater/util/update_task_worker_backup.c +++ b/applications/system/updater/util/update_task_worker_backup.c @@ -79,8 +79,8 @@ static void update_task_set_progress( update_task, UpdateTaskStageProgress, - /* For this stage, first 30% of progress = cleanup */ - (n_processed_files++ * 30) / (n_approx_file_entries + 1)); + /* For this stage, first 20% of progress = cleanup files */ + (n_processed_files++ * 20) / (n_approx_file_entries + 1)); FuriString* file_path = furi_string_alloc(); path_concat( @@ -90,6 +90,46 @@ static void furi_string_free(file_path); } } + + while((entry_ptr = resource_manifest_reader_previous(manifest_reader))) { + if(entry_ptr->type == ResourceManifestEntryTypeDirectory) { + update_task_set_progress( + update_task, + UpdateTaskStageProgress, + /* For this stage, second 10% of progress = cleanup directories */ + (n_processed_files++ * 10) / (n_approx_file_entries + 1)); + + FuriString* folder_path = furi_string_alloc(); + File* folder_file = storage_file_alloc(update_task->storage); + + do { + path_concat( + STORAGE_EXT_PATH_PREFIX, + furi_string_get_cstr(entry_ptr->name), + folder_path); + + FURI_LOG_D(TAG, "Removing folder %s", furi_string_get_cstr(folder_path)); + if(!storage_dir_open(folder_file, furi_string_get_cstr(folder_path))) { + FURI_LOG_W( + TAG, + "%s can't be opened, skipping", + furi_string_get_cstr(folder_path)); + break; + } + + if(storage_dir_read(folder_file, NULL, NULL, 0)) { + FURI_LOG_I( + TAG, "%s is not empty, skipping", furi_string_get_cstr(folder_path)); + break; + } + + storage_simply_remove(update_task->storage, furi_string_get_cstr(folder_path)); + } while(false); + + storage_file_free(folder_file); + furi_string_free(folder_path); + } + } } while(false); resource_manifest_reader_free(manifest_reader); } @@ -176,7 +216,7 @@ int32_t update_task_worker_backup_restore(void* context) { if(boot_mode == FuriHalRtcBootModePreUpdate) { success = update_task_pre_update(update_task); - } else if(boot_mode == FuriHalRtcBootModePostUpdate) { + } else if(boot_mode == FuriHalRtcBootModePostUpdate) { //-V547 success = update_task_post_update(update_task); if(success) { update_operation_disarm(); diff --git a/applications/system/updater/util/update_task_worker_flasher.c b/applications/system/updater/util/update_task_worker_flasher.c index 7358a633..63024ced 100644 --- a/applications/system/updater/util/update_task_worker_flasher.c +++ b/applications/system/updater/util/update_task_worker_flasher.c @@ -143,7 +143,6 @@ static void update_task_wait_for_restart(UpdateTask* update_task) { } static bool update_task_write_stack(UpdateTask* update_task) { - bool success = false; UpdateManifest* manifest = update_task->manifest; do { FURI_LOG_W(TAG, "Writing stack"); @@ -162,13 +161,11 @@ static bool update_task_write_stack(UpdateTask* update_task) { update_task_set_progress(update_task, UpdateTaskStageRadioInstall, 100); /* ...system will restart here. */ update_task_wait_for_restart(update_task); - success = true; } while(false); - return success; + return false; /* will return only in the case of failure */ } static bool update_task_remove_stack(UpdateTask* update_task) { - bool success = false; do { FURI_LOG_W(TAG, "Removing stack"); update_task_set_progress(update_task, UpdateTaskStageRadioErase, 30); @@ -178,9 +175,8 @@ static bool update_task_remove_stack(UpdateTask* update_task) { update_task_set_progress(update_task, UpdateTaskStageRadioErase, 100); /* ...system will restart here. */ update_task_wait_for_restart(update_task); - success = true; } while(false); - return success; + return false; /* will return only in the case of failure */ } static bool update_task_manage_radiostack(UpdateTask* update_task) { diff --git a/assets/dolphin/ReadMe.md b/assets/dolphin/ReadMe.md index 643086c2..e7572571 100644 --- a/assets/dolphin/ReadMe.md +++ b/assets/dolphin/ReadMe.md @@ -26,7 +26,7 @@ Version: 1 - `Name` - name of animation. Must be exact animation directory name. - `Min butthurt`, `Max butthurt` - range of dolphin's butthurt for this animation. - `Min level`, `Max level` - range of dolphin's level for this animation. If 0, this animation doesn't participate in random idle animation selection and can only be selected by exact name. -- `Weight` - chance of this animation to be choosen at random animation selection. +- `Weight` - chance of this animation to be chosen at random animation selection. Some animations can be excluded from participation in random animation selection, such as `L1_NoSd_128x49`. diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_0.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_0.png new file mode 100644 index 00000000..340ceb47 Binary files /dev/null and b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_0.png differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_1.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_1.png new file mode 100644 index 00000000..f401d60a Binary files /dev/null and b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_1.png differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_10.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_10.png new file mode 100644 index 00000000..890ea5d7 Binary files /dev/null and b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_10.png differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_11.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_11.png new file mode 100644 index 00000000..0b331e4b Binary files /dev/null and b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_11.png differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_12.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_12.png new file mode 100644 index 00000000..145eaa31 Binary files /dev/null and b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_12.png differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_13.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_13.png new file mode 100644 index 00000000..6eda42b5 Binary files /dev/null and b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_13.png differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_14.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_14.png new file mode 100644 index 00000000..b1eb38da Binary files /dev/null and b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_14.png differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_15.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_15.png new file mode 100644 index 00000000..554a177a Binary files /dev/null and b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_15.png differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_16.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_16.png new file mode 100644 index 00000000..ad032f41 Binary files /dev/null and b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_16.png differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_17.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_17.png new file mode 100644 index 00000000..971e8f55 Binary files /dev/null and b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_17.png differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_18.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_18.png new file mode 100644 index 00000000..4026dc37 Binary files /dev/null and b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_18.png differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_19.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_19.png new file mode 100644 index 00000000..b8cfa50c Binary files /dev/null and b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_19.png differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_2.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_2.png new file mode 100644 index 00000000..0c380ca4 Binary files /dev/null and b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_2.png differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_20.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_20.png new file mode 100644 index 00000000..59ec9627 Binary files /dev/null and b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_20.png differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_21.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_21.png new file mode 100644 index 00000000..d0e44937 Binary files /dev/null and b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_21.png differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_22.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_22.png new file mode 100644 index 00000000..fd800b8b Binary files /dev/null and b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_22.png differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_23.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_23.png new file mode 100644 index 00000000..41615b61 Binary files /dev/null and b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_23.png differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_24.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_24.png new file mode 100644 index 00000000..05c5e969 Binary files /dev/null and b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_24.png differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_25.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_25.png new file mode 100644 index 00000000..79fa4575 Binary files /dev/null and b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_25.png differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_26.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_26.png new file mode 100644 index 00000000..edb03866 Binary files /dev/null and b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_26.png differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_27.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_27.png new file mode 100644 index 00000000..7696f9ce Binary files /dev/null and b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_27.png differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_28.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_28.png new file mode 100644 index 00000000..052ff284 Binary files /dev/null and b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_28.png differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_29.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_29.png new file mode 100644 index 00000000..c6caf88d Binary files /dev/null and b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_29.png differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_3.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_3.png new file mode 100644 index 00000000..c69ac801 Binary files /dev/null and b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_3.png differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_30.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_30.png new file mode 100644 index 00000000..8710e1a0 Binary files /dev/null and b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_30.png differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_31.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_31.png new file mode 100644 index 00000000..7abad6b3 Binary files /dev/null and b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_31.png differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_32.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_32.png new file mode 100644 index 00000000..0a0882a9 Binary files /dev/null and b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_32.png differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_33.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_33.png new file mode 100644 index 00000000..94b79d2e Binary files /dev/null and b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_33.png differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_34.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_34.png new file mode 100644 index 00000000..ce0e210f Binary files /dev/null and b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_34.png differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_35.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_35.png new file mode 100644 index 00000000..aba999f6 Binary files /dev/null and b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_35.png differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_36.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_36.png new file mode 100644 index 00000000..1aac8558 Binary files /dev/null and b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_36.png differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_4.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_4.png new file mode 100644 index 00000000..ef5e2fc5 Binary files /dev/null and b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_4.png differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_5.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_5.png new file mode 100644 index 00000000..44d566b1 Binary files /dev/null and b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_5.png differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_6.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_6.png new file mode 100644 index 00000000..789eebdf Binary files /dev/null and b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_6.png differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_7.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_7.png new file mode 100644 index 00000000..cc4f5cbf Binary files /dev/null and b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_7.png differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_8.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_8.png new file mode 100644 index 00000000..1a1fe5d2 Binary files /dev/null and b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_8.png differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_9.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_9.png new file mode 100644 index 00000000..a19da738 Binary files /dev/null and b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_9.png differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/meta.txt b/assets/dolphin/external/L1_Sleigh_ride_128x64/meta.txt new file mode 100644 index 00000000..3e31e1d6 --- /dev/null +++ b/assets/dolphin/external/L1_Sleigh_ride_128x64/meta.txt @@ -0,0 +1,23 @@ +Filetype: Flipper Animation +Version: 1 + +Width: 128 +Height: 64 +Passive frames: 18 +Active frames: 19 +Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 +Active cycles: 1 +Frame rate: 2 +Duration: 3600 +Active cooldown: 7 + +Bubble slots: 1 + +Slot: 0 +X: 21 +Y: 25 +Text: AAAAaAAAAHHh!! +AlignH: Right +AlignV: Bottom +StartFrame: 30 +EndFrame: 32 diff --git a/assets/dolphin/external/manifest.txt b/assets/dolphin/external/manifest.txt index 362fba6a..df84ed7b 100644 --- a/assets/dolphin/external/manifest.txt +++ b/assets/dolphin/external/manifest.txt @@ -140,3 +140,10 @@ Max butthurt: 10 Min level: 3 Max level: 3 Weight: 3 + +Name: L1_Sleigh_ride_128x64 +Min butthurt: 0 +Max butthurt: 14 +Min level: 1 +Max level: 3 +Weight: 4 \ No newline at end of file diff --git a/assets/resources/badusb/demo_macos.txt b/assets/resources/badusb/demo_macos.txt index 6da30590..3c21a4df 100644 --- a/assets/resources/badusb/demo_macos.txt +++ b/assets/resources/badusb/demo_macos.txt @@ -34,43 +34,43 @@ ENTER STRING _.-------.._ -, ENTER HOME -STRING .-"```"--..,,_/ /`-, -, \ +STRING .-"'''"--..,,_/ /'-, -, \ ENTER HOME -STRING .:" /:/ /'\ \ ,_..., `. | | +STRING .:" /:/ /'\ \ ,_..., '. | | ENTER HOME -STRING / ,----/:/ /`\ _\~`_-"` _; +STRING / ,----/:/ /'\ _\~'_-"' _; ENTER HOME -STRING ' / /`"""'\ \ \.~`_-' ,-"'/ +STRING ' / /'"""'\ \ \.~'_-' ,-"'/ ENTER HOME -STRING | | | 0 | | .-' ,/` / +STRING | | | 0 | | .-' ,/' / ENTER HOME -STRING | ,..\ \ ,.-"` ,/` / +STRING | ,..\ \ ,.-"' ,/' / ENTER HOME -STRING ; : `/`""\` ,/--==,/-----, +STRING ; : '/'""\' ,/--==,/-----, ENTER HOME -STRING | `-...| -.___-Z:_______J...---; +STRING | '-...| -.___-Z:_______J...---; ENTER HOME -STRING : ` _-' +STRING : ' _-' ENTER HOME -STRING _L_ _ ___ ___ ___ ___ ____--"` +STRING _L_ _ ___ ___ ___ ___ ____--"' ENTER HOME -STRING | __|| | |_ _|| _ \| _ \| __|| _ \ +STRING | __|| | |_ _|| _ \| _ \| __|| _ \ ENTER HOME -STRING | _| | |__ | | | _/| _/| _| | / +STRING | _| | |__ | | | _/| _/| _| | / ENTER HOME -STRING |_| |____||___||_| |_| |___||_|_\ +STRING |_| |____||___||_| |_| |___||_|_\ ENTER HOME ENTER diff --git a/assets/resources/infrared/assets/ac.ir b/assets/resources/infrared/assets/ac.ir index af776706..1f9c2145 100644 --- a/assets/resources/infrared/assets/ac.ir +++ b/assets/resources/infrared/assets/ac.ir @@ -185,4 +185,115 @@ type: raw frequency: 38000 duty_cycle: 0.330000 data: 4403 4333 563 1615 563 528 561 1618 560 1618 560 531 558 561 538 1614 564 556 533 558 541 1611 557 562 537 555 534 1619 559 1618 560 558 541 1613 565 551 538 1614 564 1614 564 1614 564 1613 565 528 561 1617 561 1619 559 1617 561 557 532 535 564 528 561 533 566 1611 557 562 537 558 531 1619 559 1619 559 1619 559 559 540 553 536 557 532 561 538 557 532 559 540 553 536 557 532 1620 558 1620 558 1620 558 1620 558 1618 560 5188 4398 4346 561 1618 560 558 531 1621 557 1621 557 561 538 555 534 1619 559 561 538 553 536 1616 562 556 533 560 539 1614 564 1613 565 554 535 1619 559 557 532 1621 557 1620 558 1620 558 1620 558 560 539 1613 565 1615 563 1613 565 553 536 557 532 535 564 554 535 1618 560 558 531 564 535 1615 563 1615 563 1614 564 555 534 559 540 552 537 530 559 536 563 528 561 532 557 562 537 1615 563 1615 563 1614 564 1614 564 1616 562 - +# +# Model: Sharp AH-X9VEW. Doesn't have heat function. +name: Dh +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 307 125509 3831 1867 489 482 491 1392 461 468 485 1397 456 489 464 1411 463 466 487 1397 456 488 465 1390 463 482 481 1402 462 1402 483 471 461 1412 462 468 485 1400 464 1401 484 1397 488 1395 458 471 461 485 457 1408 487 1393 460 485 457 473 459 486 456 474 489 1394 459 471 461 485 457 473 459 488 465 466 455 490 463 467 465 480 462 468 464 482 460 470 483 1399 465 464 457 488 465 465 488 1393 460 484 458 471 461 485 457 1415 459 1405 511 408 482 489 464 466 487 1377 456 489 464 481 461 469 463 482 460 470 462 483 459 470 462 484 458 472 460 485 457 473 459 487 455 474 489 1395 458 471 461 485 457 472 460 486 456 473 459 486 456 474 458 487 455 474 458 488 465 466 487 1396 457 487 434 496 457 488 433 496 457 489 432 497 466 479 432 498 465 480 431 499 464 482 439 490 463 482 460 1394 491 1389 485 1395 490 1392 461 468 464 482 460 469 484 1399 465 1399 465 480 462 483 491 77962 300 +# +name: Cool_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 345 2685 478 1170 176 29680 3832 1891 465 481 482 1400 464 465 488 1395 458 470 483 1396 457 487 466 1406 458 471 492 1389 464 481 461 1411 463 1408 456 489 464 1407 457 473 490 1392 482 1383 481 1399 486 1396 458 462 459 497 456 1409 486 1394 459 459 483 473 459 487 455 474 489 1392 461 468 464 481 461 469 463 483 459 471 482 1398 487 1379 464 481 461 485 457 1416 458 487 466 1407 457 472 460 460 482 474 489 1393 460 469 463 483 459 471 461 485 457 1408 456 488 465 481 461 469 484 1381 462 483 459 486 456 473 459 487 455 474 458 487 455 475 457 489 464 467 465 481 461 1410 485 1380 484 1397 488 1392 461 483 459 471 461 485 457 473 459 486 456 474 458 488 454 476 456 490 463 467 465 481 461 1411 464 481 440 491 462 484 437 492 461 485 436 494 459 487 434 495 458 488 433 497 456 489 432 498 486 450 482 1408 467 1389 485 1394 480 1384 459 486 456 489 464 466 487 1393 460 485 457 1415 459 1406 510 +# +name: Cool_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 181 11813 3819 1899 457 488 486 1421 433 472 491 1416 438 481 461 1417 437 482 492 1416 437 467 486 1419 435 485 457 1439 435 1403 461 483 480 1408 435 484 490 1416 438 1401 484 1395 490 1392 461 467 465 480 462 1427 458 1396 457 486 456 473 459 486 456 472 481 1426 438 466 466 480 462 467 465 480 462 467 465 479 463 1426 438 481 461 484 458 1438 436 1409 486 1378 465 480 462 483 459 471 482 1425 428 475 457 487 455 474 458 487 455 1434 430 489 464 481 461 468 485 1404 439 480 462 483 459 470 462 483 459 469 463 482 460 470 462 483 459 470 462 483 459 1437 458 1381 483 1395 490 1390 463 481 461 469 463 482 460 468 464 481 461 468 464 481 461 469 463 482 460 469 463 481 461 1434 430 474 458 486 456 473 459 486 456 473 459 486 456 473 459 486 456 473 459 485 457 472 460 485 489 449 462 1434 461 1378 486 1393 481 1397 519 400 479 476 456 489 464 1431 433 486 435 495 458 487 487 +# +name: Off +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3826 1866 490 481 482 1382 461 484 490 1392 461 467 486 1396 457 471 482 1400 464 479 463 1390 463 481 482 1399 465 1398 518 401 510 1379 464 480 483 1398 456 1408 487 1392 482 1399 517 386 483 487 466 1399 486 1392 461 483 459 470 462 482 460 469 484 1397 456 473 459 459 483 472 460 484 458 471 461 483 459 1411 463 480 462 467 486 1377 487 1392 482 1396 457 486 456 472 460 486 456 473 480 1400 464 465 456 488 454 475 488 1393 460 468 464 480 462 467 486 1395 458 470 462 483 459 485 436 493 460 469 463 481 461 486 435 500 463 463 458 486 456 1397 488 1390 484 1394 480 1400 464 465 456 488 465 464 457 487 455 474 458 487 455 473 459 485 457 472 460 485 457 472 481 1399 517 401 458 497 456 473 459 485 457 487 434 495 458 471 461 484 458 488 433 502 430 507 435 500 432 498 455 1409 486 1392 482 1399 454 1409 455 488 465 480 462 467 465 480 462 1408 456 473 459 486 488 +# +# Model: Electrolux ESV09CRO-B21. Doesn't have heat function. +name: Dh +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3092 3057 3092 4438 579 1675 545 534 576 1650 571 535 575 531 569 1656 575 1652 579 527 573 533 577 1648 572 1655 576 1651 580 526 573 532 578 1647 573 532 578 1647 573 1654 577 529 571 535 575 530 570 535 575 529 571 534 576 529 571 534 576 528 571 534 576 528 572 533 577 528 572 533 577 527 573 1651 580 526 573 532 578 527 572 532 568 537 573 531 568 536 574 1650 571 535 575 531 569 536 574 530 569 535 575 529 571 534 576 529 571 533 577 528 572 533 577 527 572 532 568 536 574 531 569 1655 576 530 570 535 575 530 570 535 575 529 571 534 576 528 572 533 577 527 573 532 578 527 572 531 569 536 574 531 569 535 575 530 570 534 576 529 571 534 576 528 571 533 577 527 573 532 567 537 573 531 569 536 574 530 570 535 575 529 571 534 576 528 571 533 577 527 572 532 578 526 573 531 569 536 574 530 570 535 575 529 571 534 576 528 572 533 577 1646 574 532 578 1646 574 1652 579 527 572 533 577 1647 573 1653 578 1675 545 534 576 1649 571 +# +name: Cool_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 315 101050 3094 3056 3093 4437 580 1648 572 534 576 1649 582 525 574 530 580 1646 574 1653 578 529 570 534 576 529 571 534 576 529 570 1655 576 1651 580 527 572 532 578 1647 573 1654 577 1651 580 526 573 531 579 526 573 531 579 526 573 531 579 526 573 531 579 526 573 531 579 525 574 531 579 525 574 531 579 1646 574 532 578 526 573 531 579 526 573 531 579 526 573 1652 579 527 572 1653 578 528 571 534 576 528 571 533 577 528 571 533 577 528 572 533 577 528 572 532 578 527 572 532 578 527 572 532 578 526 573 1652 579 527 572 532 578 527 572 532 578 527 572 532 578 526 573 531 579 526 573 531 579 526 573 531 579 525 574 530 580 525 574 530 580 525 574 530 580 524 575 529 581 524 575 529 571 534 576 528 571 533 577 528 571 533 577 528 571 533 577 527 572 532 578 527 572 532 578 526 573 531 579 526 573 531 579 525 574 531 579 525 574 530 580 525 574 1650 581 525 574 1651 580 1647 573 533 577 527 572 1653 578 528 572 1654 577 1650 581 1646 574 71637 254 +# +name: Cool_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 284 19161 3098 3053 3096 4435 572 1656 575 532 578 1648 572 534 576 530 570 1682 549 1652 579 527 572 534 576 1649 571 1656 575 1652 579 1649 571 1656 575 531 579 527 572 1653 578 1649 571 1656 575 531 579 527 572 532 578 527 572 533 577 527 572 533 577 527 573 532 578 527 572 532 578 527 573 532 578 527 572 1652 579 527 572 533 577 528 571 533 577 528 571 533 577 1648 572 533 577 1649 571 535 575 530 569 536 574 531 569 536 574 530 569 536 574 530 570 535 575 530 570 535 575 530 569 535 575 530 569 535 575 1649 571 535 575 531 568 536 574 531 568 536 574 531 568 536 574 531 569 536 574 530 569 536 574 530 569 535 575 530 569 535 575 530 569 535 575 530 570 535 575 529 570 534 576 529 570 534 576 529 570 534 576 528 571 534 576 528 571 534 576 528 571 534 576 528 571 534 576 528 571 533 577 528 571 533 577 528 572 533 577 528 571 533 577 528 572 1652 579 527 572 1653 578 529 570 534 576 529 570 535 575 529 570 1654 577 1677 554 1673 547 +# +name: Off +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3093 3058 3090 4441 576 1652 579 528 571 1654 577 531 579 526 573 1652 579 1649 582 525 574 1652 579 528 571 1654 577 1651 580 527 572 533 577 528 571 533 577 1649 582 1646 574 1653 578 529 581 525 574 530 580 525 574 530 580 525 574 531 579 526 573 531 579 526 573 531 579 526 573 531 579 526 573 531 579 526 573 531 579 526 573 531 579 526 573 532 578 1647 573 533 577 1648 572 535 575 530 569 535 575 530 580 525 574 531 579 525 574 531 579 526 573 531 579 526 573 531 579 526 573 531 579 526 573 1651 580 527 572 533 577 528 571 533 577 528 571 533 577 528 571 533 577 528 571 534 576 528 571 534 576 529 570 534 576 529 570 534 576 529 570 534 576 529 570 534 576 529 570 534 576 529 570 534 576 529 570 534 576 529 570 534 576 529 570 534 576 529 570 534 576 529 570 534 576 529 570 534 576 529 570 534 576 529 570 534 576 529 570 534 576 1649 582 525 574 1650 581 1647 573 1654 577 1651 580 1647 573 1654 577 531 579 1646 574 1653 578 +# +# Model: Daikin FTE35KV1. Doesn't have heat function. +name: Dh +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 5045 2158 335 1768 358 690 357 723 335 716 331 1771 355 694 364 686 361 720 327 723 335 1767 359 690 357 1775 362 1770 356 692 366 1767 359 1772 354 1777 360 1771 355 1776 361 687 360 690 357 1776 361 687 360 690 357 693 365 716 331 719 328 692 366 1767 359 1772 354 1777 360 1771 355 1776 361 687 360 1773 364 1767 360 689 358 692 366 685 362 688 359 721 326 724 334 717 330 720 327 723 335 715 332 718 329 721 326 1777 360 1771 355 1776 361 1770 356 692 366 715 332 718 329 721 326 29460 5042 2161 332 1770 356 692 366 685 362 688 359 1774 363 685 362 688 359 721 326 694 364 1769 357 691 367 1767 360 1771 355 693 365 1769 358 1773 364 1768 359 1772 365 1766 361 688 359 691 367 1767 360 689 358 692 366 684 363 717 330 720 327 693 365 686 361 689 358 722 336 684 363 1770 357 1774 363 686 361 689 358 1774 363 686 361 1771 356 1776 361 1770 357 692 366 685 362 688 359 690 357 1776 361 687 360 690 357 1776 361 688 359 691 356 694 364 716 331 689 358 1775 362 686 361 689 358 692 366 685 362 688 359 691 356 724 334 716 331 689 358 722 336 685 362 688 359 721 326 693 365 716 331 689 358 692 366 684 363 718 329 690 357 693 365 716 331 689 358 722 336 1767 360 689 358 1774 363 686 361 1771 356 693 365 686 361 689 358 722 336 684 363 717 330 720 327 1776 361 687 360 690 357 693 365 716 331 1771 355 693 365 686 361 1772 355 1776 361 688 359 1773 364 1768 359 +# +name: Cool_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 5038 2165 328 1772 365 686 361 689 358 692 366 1765 362 719 328 692 366 684 363 717 330 1771 366 684 363 1768 359 1774 363 686 361 1771 355 1776 361 1770 357 1775 362 1769 358 691 356 694 364 1767 360 691 356 694 364 686 361 689 358 692 366 685 362 1769 358 1775 362 1769 358 1774 363 1768 359 690 357 1776 361 1770 357 692 366 684 363 687 360 690 357 693 365 686 361 689 358 692 366 684 363 687 360 690 357 693 365 1766 361 1773 364 1767 360 1772 355 694 364 686 361 689 358 692 366 25151 319 3980 5041 2131 362 1769 358 693 365 686 361 689 358 1772 365 686 361 689 358 692 366 684 363 1768 359 692 366 1765 361 1772 354 694 364 1769 358 1774 363 1768 359 1773 364 1767 359 719 328 692 366 1796 331 719 328 692 366 685 362 688 359 691 356 694 364 686 361 689 358 692 366 685 362 1768 359 1775 362 686 361 689 358 1773 364 686 361 1770 357 1777 360 1771 355 693 365 686 361 689 358 1772 365 1769 358 690 357 694 364 1767 360 691 356 694 364 686 361 689 358 723 335 1766 361 690 357 693 365 685 362 688 359 691 356 694 364 687 360 690 357 693 365 685 362 688 359 691 356 694 364 687 360 690 357 693 365 685 362 688 359 691 367 684 363 687 360 1771 366 684 363 687 360 690 357 693 365 1767 360 690 357 1804 333 687 360 690 357 693 365 686 361 689 358 692 366 685 362 1768 359 692 366 685 362 688 359 690 357 1774 363 688 359 691 356 1774 363 1770 356 1775 362 1769 358 691 356 +# +name: Cool_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 301 132136 5036 2167 337 1766 361 689 358 692 366 684 363 1770 357 692 366 684 363 718 329 690 357 1776 361 687 360 1773 364 1767 360 689 358 1775 362 1769 357 1774 363 1768 359 1773 364 684 363 718 329 1773 364 684 363 718 329 691 356 694 364 716 331 719 328 1775 362 1769 358 1774 363 1768 359 1772 365 714 333 1770 357 1774 363 716 331 719 328 722 336 715 332 718 329 721 326 724 334 716 331 719 328 722 336 715 332 718 329 1773 364 1767 360 1772 354 1777 360 719 328 721 326 725 333 717 330 29455 5036 2139 354 1777 360 688 359 691 367 714 333 1770 356 692 366 684 363 687 360 690 357 1776 361 688 359 1773 364 1768 359 689 358 1775 362 1769 357 1774 363 1768 359 1773 364 684 363 687 360 1773 364 685 362 688 359 691 356 694 364 686 361 689 358 692 366 685 362 688 359 691 356 1777 360 1771 355 693 365 685 362 1771 355 693 365 1768 359 1773 364 1767 360 689 358 692 366 685 362 1771 355 1775 362 687 360 690 357 1775 362 687 360 690 357 693 365 716 331 689 358 1774 363 686 361 689 358 692 366 685 362 688 359 691 356 694 364 686 361 689 358 692 366 685 362 688 359 691 356 694 364 686 361 689 358 692 366 685 362 1771 355 693 365 1768 358 1773 364 684 363 687 360 690 357 693 365 1768 359 690 357 1776 361 688 359 691 356 694 364 686 361 689 358 692 366 685 362 1770 356 693 365 685 362 688 359 691 356 1777 360 1771 355 693 365 686 361 689 358 692 366 685 362 1770 356 +# +name: Off +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 5043 2132 361 1770 356 723 335 715 332 718 329 1774 363 715 332 719 328 722 336 714 333 1770 356 722 336 1767 360 1772 354 724 334 1769 357 1774 363 1768 358 1773 364 1767 359 720 327 723 335 1768 359 720 327 723 335 716 331 719 328 722 336 714 333 1770 356 1774 363 1769 357 1773 364 1767 360 720 327 1775 362 1769 357 721 326 725 333 717 330 720 327 723 335 716 331 719 328 722 336 714 333 717 330 720 327 723 335 1768 359 1773 364 1767 360 1772 354 724 334 717 330 720 327 723 335 29451 5041 2134 359 1772 354 724 334 717 330 720 327 1775 362 717 330 720 327 723 335 715 332 1771 355 723 335 1768 358 1773 364 715 332 1770 357 1775 362 1769 357 1774 363 1768 359 720 327 723 335 1768 359 720 327 724 334 716 331 719 328 722 336 715 332 718 329 720 327 723 335 716 331 1771 355 1776 361 718 329 721 326 1776 361 718 329 1773 364 1767 360 720 327 723 335 715 332 718 329 1774 363 1768 359 720 327 723 335 1768 358 721 326 724 334 716 331 719 328 722 336 1767 360 719 328 722 336 715 332 718 329 721 326 724 334 717 330 720 327 723 335 715 332 719 328 722 325 725 333 717 330 720 327 723 335 716 331 719 328 1774 363 716 331 1771 355 1776 361 718 329 721 326 724 334 717 330 1772 365 714 333 1770 356 722 336 715 332 718 329 721 326 724 334 717 330 719 328 1775 362 717 330 720 327 723 335 715 332 718 329 1774 363 715 332 718 329 721 326 725 333 717 330 1772 365 +# +# Model: Dyson Purifier Hot+Cool +name: Off +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 2309 665 781 672 803 672 803 695 832 643 833 1355 804 694 836 640 781 695 804 1409 778 697 777 702 797 678 798 677 799 678 799 701 803 674 802 1412 801 674 801 674 801 674 802 674 801 51317 2284 670 775 1413 802 51252 2283 670 801 1412 775 51275 2258 673 798 1414 802 51248 2284 670 802 1412 774 51246 2259 695 775 1413 801 +# +name: Heat_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 2316 610 806 671 781 695 806 695 781 695 782 1405 782 694 808 694 780 693 808 1381 779 697 802 1412 776 1438 800 1437 776 1438 775 700 775 1412 776 700 801 701 775 700 776 1438 776 700 776 51695 2258 695 776 1437 776 51248 2258 672 798 1439 776 51240 2258 670 801 1436 776 +# +name: Heat_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 2342 612 781 695 805 668 810 666 810 665 811 1402 811 666 811 692 781 670 781 1432 781 696 778 1436 802 1412 802 1412 802 1413 801 1412 802 1412 801 1412 777 1411 776 1463 776 1412 800 1414 801 51041 2257 697 802 1411 777 51240 2283 671 776 1437 801 51209 2255 672 799 1412 801 +# +name: Cool_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 2317 637 832 644 830 669 805 670 805 672 803 1411 803 673 802 674 802 673 803 1411 803 674 801 1411 802 675 775 1415 800 701 774 1440 801 1412 775 702 799 1414 774 1413 801 701 801 675 800 51681 2257 695 803 1411 801 51226 2283 671 799 1412 803 51246 2257 696 803 1411 775 51255 2282 668 803 1410 802 51243 2258 695 802 1387 798 +# +name: Cool_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 2315 618 853 643 832 644 834 641 833 643 833 1356 805 695 835 640 808 667 809 1404 808 668 806 1409 803 674 801 1412 802 1388 799 677 799 701 775 701 801 1389 799 677 799 676 800 1439 802 51426 2283 671 800 1412 802 51251 2258 697 801 1387 800 51248 2283 669 802 1411 802 51230 2258 696 799 1387 801 51225 2283 670 801 1411 801 51200 2280 695 775 1411 802 51227 2258 696 802 1411 775 51204 2281 669 801 1411 800 +# +name: Dh +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 2320 634 837 637 838 637 838 640 835 642 832 1378 836 645 826 670 809 667 808 1406 806 672 803 674 802 1412 802 1412 800 676 801 675 802 1412 802 674 802 1413 801 1412 801 1413 802 1412 802 50937 2285 671 801 1411 802 51225 2280 696 775 1412 801 51212 2283 671 775 1412 802 diff --git a/assets/unit_tests/Manifest b/assets/unit_tests/Manifest new file mode 100644 index 00000000..db2979ee --- /dev/null +++ b/assets/unit_tests/Manifest @@ -0,0 +1,76 @@ +V:0 +T:1672935435 +D:infrared +D:nfc +D:subghz +F:4bff70f2a2ae771f81de5cfb090b3d74:3952:infrared/test_kaseikyo.irtest +F:8556d32d7c54e66771d9da78d007d379:21463:infrared/test_nec.irtest +F:860c0c475573878842180a6cb50c85c7:2012:infrared/test_nec42.irtest +F:2b3cbf3fe7d3642190dfb8362dcc0ed6:3522:infrared/test_nec42ext.irtest +F:c74bbd7f885ab8fbc3b3363598041bc1:18976:infrared/test_necext.irtest +F:cab5e604abcb233bcb27903baec24462:7460:infrared/test_rc5.irtest +F:3d22b3ec2531bb8f4842c9c0c6a8d97c:547:infrared/test_rc5x.irtest +F:c9cb9fa4decbdd077741acb845f21343:8608:infrared/test_rc6.irtest +F:97de943385bc6ad1c4a58fc4fedb5244:16975:infrared/test_samsung32.irtest +F:4eb36c62d4f2e737a3e4a64b5ff0a8e7:41623:infrared/test_sirc.irtest +F:e4ec3299cbe1f528fb1b9b45aac53556:4182:nfc/nfc_nfca_signal_long.nfc +F:af4d10974834c2703ad29e859eea78c2:1020:nfc/nfc_nfca_signal_short.nfc +F:224d12457a26774d8d2aa0d4b3a15652:160:subghz/ansonic.sub +F:ce9fc98dc01230387a340332316774f1:13642:subghz/ansonic_raw.sub +F:f958927b656d0804036c28b4a31ff856:157:subghz/bett.sub +F:b4b17b2603fa3a144dbea4d9ede9f61d:5913:subghz/bett_raw.sub +F:370a0c62be967b420da5e60ffcdc078b:157:subghz/came.sub +F:0156915c656d8c038c6d555d34349a36:6877:subghz/came_atomo_raw.sub +F:111a8b796661f3cbd6f49f756cf91107:8614:subghz/came_raw.sub +F:2101b0a5a72c87f9dce77223b2885aa7:162:subghz/came_twee.sub +F:c608b78b8e4646eeb94db37644623254:10924:subghz/came_twee_raw.sub +F:c4a55acddb68fc3111d592c9292022a8:21703:subghz/cenmax_raw.sub +F:51d6bd600345954b9c84a5bc6e999313:159:subghz/clemsa.sub +F:14fa0d5931a32674bfb2ddf288f3842b:21499:subghz/clemsa_raw.sub +F:f38b6dfa0920199200887b2cd5c0a385:161:subghz/doitrand.sub +F:c7e53da8e3588a2c0721aa794699ccd4:24292:subghz/doitrand_raw.sub +F:cc73b6f4d05bfe30c67a0d18b63e58d9:159:subghz/doorhan.sub +F:22fec89c5cc43504ad4391e61e12c7e0:10457:subghz/doorhan_raw.sub +F:3a97d8bd32ddaff42932b4c3033ee2d2:12732:subghz/faac_slh_raw.sub +F:06d3226f5330665f48d41c49e34fed15:159:subghz/gate_tx.sub +F:8b150a8d38ac7c4f7063ee0d42050399:13827:subghz/gate_tx_raw.sub +F:a7904e17b0c18c083ae1acbefc330c7a:159:subghz/holtek.sub +F:72bb528255ef1c135cb3f436414897d3:173:subghz/holtek_ht12x.sub +F:54ceacb8c156f9534fc7ee0a0911f4da:11380:subghz/holtek_ht12x_raw.sub +F:4a9567c1543cf3e7bb5350b635d9076f:31238:subghz/holtek_raw.sub +F:ca86c0d78364d704ff62b0698093d396:162:subghz/honeywell_wdb.sub +F:f606548c935adc8d8bc804326ef67543:38415:subghz/honeywell_wdb_raw.sub +F:20bba4b0aec006ced7e82513f9459e31:15532:subghz/hormann_hsm_raw.sub +F:3392f2db6aa7777e937db619b86203bb:10637:subghz/ido_117_111_raw.sub +F:cc5c7968527cc233ef11a08986e31bf2:167:subghz/intertechno_v3.sub +F:70bceb941739260ab9f6162cfdeb0347:18211:subghz/intertechno_v3_raw.sub +F:bc9a4622f3e22fd7f82eb3f26e61f59b:44952:subghz/kia_seed_raw.sub +F:6b6e95fc70ea481dc6184d291466d16a:159:subghz/linear.sub +F:77aaa9005db54c0357451ced081857b2:14619:subghz/linear_raw.sub +F:1a618e21e6ffa9984d465012e704c450:161:subghz/magellan.sub +F:bf43cb85d79e20644323d6acad87e028:5808:subghz/magellan_raw.sub +F:4ef17320f936ee88e92582a9308b2faa:161:subghz/marantec.sub +F:507a8413a1603ad348eea945123fb7cc:21155:subghz/marantec_raw.sub +F:22b69dc490d5425488342b5c5a838d55:161:subghz/megacode.sub +F:4f8fe9bef8bdd9c52f3f77e829f8986f:6205:subghz/megacode_raw.sub +F:b39f62cb108c2fa9916e0a466596ab87:18655:subghz/nero_radio_raw.sub +F:d0d70f8183032096805a41e1808c093b:26436:subghz/nero_sketch_raw.sub +F:c6999bd0eefd0fccf34820e17bcbc8ba:161:subghz/nice_flo.sub +F:9b1200600b9ec2a73166797ff243fbfc:3375:subghz/nice_flo_raw.sub +F:b52bafb098282676d1c7163bfb0d6e73:8773:subghz/nice_flor_s_raw.sub +F:e4df94dfdee2efadf2ed9a1e9664f8b2:163:subghz/phoenix_v2.sub +F:8ec066976df93fba6335b3f6dc47014c:8548:subghz/phoenix_v2_raw.sub +F:2b1192e4898aaf274caebbb493b9f96e:164:subghz/power_smart.sub +F:8b8195cab1d9022fe38e802383fb923a:3648:subghz/power_smart_raw.sub +F:1ccf1289533e0486a1d010d934ad7b06:170:subghz/princeton.sub +F:8bccc506a61705ec429aecb879e5d7ce:7344:subghz/princeton_raw.sub +F:0bda91d783e464165190c3b3d16666a7:38724:subghz/scher_khan_magic_code.sub +F:116d7e1a532a0c9e00ffeee105f7138b:166:subghz/security_pls_1_0.sub +F:441fc7fc6fa11ce0068fde3f6145177b:69413:subghz/security_pls_1_0_raw.sub +F:e5e33c24c5e55f592ca892b5aa8fa31f:208:subghz/security_pls_2_0.sub +F:2614f0aef367042f8623719d765bf2c0:62287:subghz/security_pls_2_0_raw.sub +F:8eb533544c4c02986800c90e935184ff:168:subghz/smc5326.sub +F:fc67a4fe7e0b3bc81a1c8da8caca7658:4750:subghz/smc5326_raw.sub +F:24196a4c4af1eb03404a2ee434c864bf:4096:subghz/somfy_keytis_raw.sub +F:6a5ece145a5694e543d99bf1b970baf0:9741:subghz/somfy_telis_raw.sub +F:0ad046bfa9ec872e92141a69bbf03d92:382605:subghz/test_random_raw.sub diff --git a/assets/unit_tests/subghz/holtek_ht12x.sub b/assets/unit_tests/subghz/holtek_ht12x.sub new file mode 100644 index 00000000..09e20b13 --- /dev/null +++ b/assets/unit_tests/subghz/holtek_ht12x.sub @@ -0,0 +1,8 @@ +Filetype: Flipper SubGhz Key File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: Holtek_HT12X +Bit: 12 +Key: 00 00 00 00 00 00 0F FB +TE: 205 diff --git a/assets/unit_tests/subghz/holtek_ht12x_raw.sub b/assets/unit_tests/subghz/holtek_ht12x_raw.sub new file mode 100644 index 00000000..1aeedac3 --- /dev/null +++ b/assets/unit_tests/subghz/holtek_ht12x_raw.sub @@ -0,0 +1,10 @@ +Filetype: Flipper SubGhz RAW File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: RAW +RAW_Data: 97 -264 65 -1890 231 -366 165 -232 99 -166 297 -68 401 -100 97 -100 759 -264 823 -132 919 -98 65 -230 367 -98 1055 -52 51 -104 101 -126 173 -148 151 -100 153 -222 77 -288 53 -280 331 -212 81 -108 246 -156 267 -268 183 -1260 53 -622 51 -468 183 -264 105 -990 77 -421 75 -52 53 -214 185 -504 75 -563 131 -338 165 -82 133 -80 53 -236 231 -208 282 -836 187 -406 81 -102 181 -54 107 -415 81 -54 109 -298 55 -406 323 -136 299 -136 437 -52 133 -294 53 -110 133 -420 333 -402 269 -574 79 -270 163 -594 124 -52 51 -320 79 -324 109 -78 105 -236 79 -292 53 -432 181 -110 187 -268 107 -78 79 -188 79 -78 75 -130 75 -128 103 -628 79 -322 79 -136 81 -196 85 -296 51 -349 159 -325 131 -80 131 -80 402 -110 55 -194 109 -108 53 -108 135 -224 81 -162 57 -228 161 -357 105 -54 103 -347 279 -294 422 -152 51 -104 99 -146 147 -205 77 -216 107 -52 181 -52 107 -158 105 -52 133 -134 139 -418 291 -102 103 -106 105 -584 103 -540 53 -52 153 -208 227 -128 307 -184 105 -328 109 -862 129 -196 55 -244 51 -1501 55 -82 81 -326 79 -500 103 -406 207 -130 75 -126 155 -202 53 -376 135 -158 53 -428 107 -467 167 -138 81 -106 79 -346 157 -84 143 -144 85 -136 53 -308 189 -248 79 -711 109 -592 107 -220 107 -104 77 -583 131 -158 53 -110 135 -191 107 -748 105 -478 77 -398 107 -138 83 -410 127 -477 103 -162 119 -72 103 -442 129 -237 107 -664 175 -74 147 -406 53 -78 127 -178 51 -102 101 -259 125 -202 257 -210 77 -52 77 -130 251 -230 77 -76 77 -178 155 -98 97 -100 153 -54 185 -82 79 -82 135 -398 241 -130 77 -76 311 -210 53 -238 181 -404 241 -352 189 -270 265 -164 215 -998 157 -82 77 -106 51 -54 75 -100 328 -178 103 -224 253 -341 147 -334 446 -76 125 -100 75 -126 173 -690 75 -250 51 -554 81 -300 53 -188 53 -106 265 -1024 105 -646 221 -82 191 -362 75 -234 131 -128 83 -224 55 -240 77 -376 157 -78 187 -358 389 -76 103 -128 199 -513 83 -492 129 -618 123 -52 129 -52 79 -376 101 -688 105 -110 55 -84 179 -152 51 -486 358 -276 99 -74 73 -679 51 -210 113 -196 365 -104 223 -104 53 -430 55 -140 55 -84 153 -202 181 -358 149 -820 103 -80 215 -108 309 -368 105 -538 154 -732 233 -588 53 -253 155 -288 55 -136 55715 -102 407 -100 259 -78 157 -130 206 -52 258 -78 441 -104 77 -80 492 -104 182 -104 79 -155 +RAW_Data: 287 -78 807 -104 1588 -52 2952 -78 1018 -240 341 -262 361 -258 335 -280 309 -100 509 -280 307 -302 307 -6968 321 -290 293 -298 323 -294 289 -314 285 -334 285 -330 257 -330 281 -328 293 -310 281 -124 479 -330 281 -326 267 -6994 295 -324 267 -320 287 -340 259 -332 257 -358 257 -354 257 -328 257 -352 257 -352 255 -150 455 -360 255 -326 255 -7024 253 -352 255 -352 255 -354 267 -326 269 -352 239 -346 261 -362 233 -358 257 -356 255 -152 455 -334 255 -354 255 -7006 267 -342 267 -352 241 -350 263 -342 259 -362 233 -356 257 -356 231 -378 231 -380 231 -150 455 -358 231 -378 229 -7050 229 -378 229 -352 253 -352 239 -372 239 -380 243 -348 235 -368 231 -388 231 -356 233 -176 431 -384 229 -380 231 -7028 239 -368 241 -372 241 -350 239 -372 233 -366 233 -384 231 -358 231 -380 231 -378 231 -176 429 -384 205 -402 205 -7064 213 -380 243 -378 215 -374 233 -368 233 -386 231 -382 205 -382 231 -380 229 -380 231 -176 429 -386 203 -378 231 -7062 229 -378 229 -378 229 -378 229 -378 213 -396 203 -404 203 -404 203 -404 203 -404 203 -198 431 -376 215 -376 239 -7064 235 -364 231 -386 231 -384 205 -408 205 -380 231 -380 231 -380 231 -378 207 -404 205 -200 431 -384 205 -404 205 -7118 205 -408 205 -406 205 -408 205 -408 205 -408 205 -436 179 -440 283 -460 75 -1486 105 -54 103 -784 107 -218 113 -824 125 -2140 85 -583 51 -1291 53 -722 113 -622 107 -1176 51 -52 75 -152 113 -56 111 -784 105 -436 81 -112 85 -272 53 -406 81 -1345 105 -3008 53 -1418 105 -254 107 -874 131 -818 53 -595 77 -130 123 -1088 213 -724 131 -793 99 -428 75 -288 51 -334 101 -1471 81 -274 53 -164 107 -2618 129 -532 55 -366 297 -160 83 -166 184 -208 57 -196 328 -1640 235 -1743 79 -594 51 -76 77 -270 163 -728 51 -408 55 -354 101 -126 153 -184 133 -392 53 -604 77 -804 127 -965 51 -78 103 -668 262 -210 237 -439 75 -340 77 -106 105 -1775 79 -284 51 -3100 157 -433 55 -1355 107 -198 165 -82 79 -770 79 -1587 81 -638 79 -530 103 -703 51 -396 71 -956 151 -248 53 -553 103 -154 75 -806 75 -660 53 -698 127 -1210 53 -3175 79 -608 245 -1590 207 -164 107 -232 51 -1094 99 -695 135 -955 53 -804 217 -587 55 -1452 163 -1232 79 -968 79 -720 81 -1110 129 -1194 105 -1736 79 -386 79 -184 77 -652 75 -2442 103 -555 129 -125 75 -1115 81 -108 135 -980 53 -808 105 -1001 155 -1068 131 -850 99 -268 51 -1106 159 -2408 79 -612 51 -1191 +RAW_Data: 135 -212 77 -2816 137 -980 51 -1921 79 -1554 53 -906 83 -1042 99 -5161 51 -290 75 -558 51 -260 77 -738 99 -2128 105 -1384 53 -108 79 -188 55 -830 77 -5690 109 -2652 51 -1374 53 -2469 79 -2163 203 -152 71 -1459 79 -2741 105 -262 75 -1194 75 -228 153 -210 107 -3236 107 -1500 77 -3562 79 -170 55 -540 53 -1270 51 -304 51 -2071 137 -134 105 -266 101 -834 51 -8117 73 -3292 81 -732 181 -1539 53 -132 109 -1375 77 -132 81 -1061 77 -1620 79 -1373 105 -643 183 -190 53 -620 77 -1633 55 -931 51 -398 135 -924 111 -1122 51 -128 125 -2361 97 -2571 51 -3563 51 -794 77 -844 81 -788 81 -2040 77 -232 77 -1000 131 -322 73 -848 55 -336 107 -1546 81 -602 79 -954 107 -1824 73 -1131 240 -1048 197 -2442 73 -720 127 -78 77 -574 79 -624 101 -5754 103 -1829 149 -719 77 -982 53 -250 53 -634 161 -2201 75 -3483 149 -356 73 -98 123 -176 75 -702 231 -1633 121 -704 53 -1163 51 -1227 73 -368 123 -258 103 -530 279 -628 53 -884 55 -82 101 -102 135 -162 107 -1264 159 -3370 77 -3538 131 -130 81 -2460 129 -588 51 -514 77 -376 53 -616 53 -956 51 -1508 53 -1044 53 -1160 261 -442 53 -640 83 -300 79 -1723 75 -124 125 -532 103 -500 79 -764 53 -1220 279 -458 77 -548 159 -450 223 -718 55 -716 51 -716 203 -732 79 -6974 53 -1086 51 -1794 79 -882 53 -4221 53 -1070 77 -1126 75 -2307 155 -1233 131 -156 111 -1352 51 -478 51 -154 75 -724 51 -635 103 -652 77 -294 75 -268 81 -1046 177 -1778 51 -298 109 -336 77 -80 53 -192 133 -556 99 -394 81 -654 51 -106 79 -1893 55 -240 55 -136 55 -2680 77 -136 81 -164 53 -576 135 -3304 131 -654 103 -788 53 -224 187 -354 79 -1872 81 -290 207 -410 77 -1132 55 -112 57 -512 51 -2736 123 -404 239 -1283 51 -1526 213 -720 133 -354 109 -396 77 -1518 105 -632 53 -164 53 -1090 55 -730 79 -1705 75 -370 75 -378 79 -2022 81 -2480 103 -1108 79 -106 53 -1784 105 -512 219 -669 179 -795 105 -376 105 -514 53 -402 53 -282 83 -554 133 -448 105 -972 79 -490 81 -1184 77 -216 109 -132 51 -164 135 -1570 51 -1329 79 -1218 53 -2450 75 -1246 51 -3825 77 -1114 53 -1718 51 -846 79 -826 165 -306 75 -3821 51 -606 55 -880 107 -56 55 -5505 53 -504 79 -1356 77 -569 51 -2268 149 -358 53 -1979 51 -106 103 -176 221 -508 51 -110 53 -3896 127 -234 79 -1012 79 -84 243 -2860 53 -1010 73 -286 133 -2198 151 -370 129 -1942 101 -154 109 -702 79 -696 51 -302 +RAW_Data: 51 -744 83 -496 79 -672 77 -170 111 -264 103 -472 51 -524 75 -1172 51 -240 51963 -254 333 -278 333 -276 333 -278 305 -302 307 -76 531 -280 331 -276 331 -6938 347 -268 319 -272 319 -292 311 -312 283 -306 309 -304 283 -328 293 -310 281 -326 293 -110 479 -322 295 -322 291 -6964 283 -334 283 -304 283 -328 283 -328 281 -328 255 -354 265 -338 255 -352 255 -352 255 -124 481 -338 277 -328 279 -6984 269 -350 265 -320 287 -340 257 -332 257 -358 255 -356 255 -328 257 -354 257 -352 255 -150 455 -360 229 -376 231 -7040 239 -348 267 -350 239 -346 261 -364 231 -360 257 -356 231 -380 231 -378 231 -352 257 -150 453 -360 229 -378 229 -7032 239 -372 227 -378 227 -380 241 -352 241 -374 233 -366 233 -384 233 -356 231 -380 231 -176 431 -384 229 -354 255 -7030 231 -376 231 -376 229 -378 229 -378 229 -352 255 -352 255 -352 229 -378 229 -378 229 -174 427 -384 229 -376 241 -7040 241 -350 235 -368 233 -388 231 -358 233 -380 231 -380 229 -380 231 -380 229 -380 211 -192 421 -366 227 -380 227 -7068 239 -378 215 -402 209 -394 207 -388 231 -386 231 -384 205 -408 205 -408 205 -408 205 -202 409 -412 207 -1400 105 -1002 51 -9495 53 -250 57 -1093 155 -124 73 -344 75 -2759 83 -468 53 -738 77 -134 53 -1581 51 -106 127 -1209 121 -956 51 -918 83 -276 85 -1696 125 -618 81 -1666 51 -152 101 -1324 107 -54 141 -586 75 -784 55 -1828 51 -4052 81 -480 53 -218 141 -1346 105 -1152 127 -776 53 -426 135 -390 105 -939 81 -887 71 -492 107 -1311 105 -1844 101 -1340 77 -2586 51 -2637 51 -1626 105 -54 53 -1672 151 -2830 57 -3143 51 -1859 79 -929 179 -78 77 -890 73 -894 79 -80 79 -1184 53 -323 53 -1344 79 -636 53 -1808 55 -3048 79 -2287 53 -572 51 -822 51 -608 77 -1772 75 -2521 79 -162 81 -664 163 -110 83 -524 53 -930 53 -1816 79 -1305 51 -816 53 -1358 55 -822 55 -594 81 -2230 55 -234 77 -600 201 -3174 151 -2534 71 -122 51 -1370 81 -3130 127 -236 79 -728 101 -1472 53 -800 127 -528 51 -802 77 -52 99 -3144 77 -6346 51 -1090 81 -588 79 -292 169 -2345 107 -370 187 -1218 81 -296 75 -696 51 -516 77 -2154 75 -558 75 -816 103 -2200 125 -2766 229 -376 151 -3375 79 -1466 53 -535 167 -524 217 -54 131 -3408 51 -54 109 -1886 77 -732 83 -536 99 -3128 103 -168 57 -1852 51 -574 79 -296 155 -844 99 -767 147 -2406 99 -1014 81 -4460 175 -226 195 -454 73 -2236 53 -818 155 -352 83 -752 79 -5083 51 -1716 +RAW_Data: 77 -1925 51 -1760 81 -162 53 -1469 79 -362 53 -471 103 -750 53 -562 127 -238 79 -250 107 -52 53 -210 83 -504 79 -1566 107 -82 79 -968 53 -458 131 -1944 301 -594 79 -382 51 -1196 79 -3963 183 -670 111 -360 105 -1990 53 -1084 79 -658 73 -301 73 -264 75 -1335 79 -1110 81 -1856 159 -3986 75 -1262 53 -1189 53 -158 53 -903 81 -1135 133 -350 107 -178 151 -1096 73 -2734 81 -1280 107 -950 79 -52 53 -5912 53 -1653 111 -1601 79 -742 51 -1104 55 -1254 51 -867 53 -136 109 -813 79 -1082 53 -395 53 -54 109 -504 79 -218 109 -114 57 -1620 157 -3003 79 -656 53 -510 209 -1933 107 -1197 159 -5508 79 -1164 77 -1466 77 -742 101 -124 71 -2049 85 -144 109 -1304 105 -2310 55 -381 83 -114 113 -944 103 -184 83 -558 55 -2064 109 -760 75 -1036 77 -574 51 -134 131 -224 57 -104 53 -440 53 -1262 183 -2454 51 -1966 73 -1950 125 -1095 51 -480 121 -1994 57 -1930 103 -786 79 -2272 105 -3312 51 -746 127 -144 133 -1608 77 -692 51 -1136 53 -164 55 -573 55 -3110 53 -1558 105 -6248 53 -1051 111 -886 105 -2234 103 -106 53 -1256 101 -1446 111 -974 79 -851 81 -136 193 -3392 83 -582 103 -1197 111 -196 55 -906 51 -742 77 -2038 71 -686 53 -1943 51 -134 51 -852 51 -1658 133 -2050 161 -388 77 -326 81 -412 55 -1137 81 -3256 55 -1516 53 -1414 117 -372 51 -1144 199 -3087 51 -430 75 -1856 151 -128 95 -192 398 -1207 77 -280 51 -2716 51 -808 53 -78 77 -1524 109 -54 79 -410 79 -132 53 -770 109 -2066 185 -368 131 -2102 125 -1037 75 -780 127 -128 51 -176 53 -1982 77 -140 111 -1046 109 -3166 169 -1956 77 -4040 79 -2778 73 -204 129 -1546 82945 -150 359 -252 333 -76 533 -280 319 -286 305 -6960 319 -300 323 -270 317 -290 313 -308 283 -306 309 -304 283 -328 291 -310 281 -326 281 -124 481 -334 279 -302 305 -6970 293 -318 301 -304 279 -328 279 -328 277 -330 277 -330 293 -298 295 -320 287 -314 283 -128 489 -334 255 -330 281 -7012 265 -340 265 -344 251 -354 253 -354 269 -322 293 -322 263 -344 259 -336 257 -360 257 -152 457 -358 231 -354 257 -7060 237 -364 255 -354 237 -366 255 -354 255 -354 267 -352 241 -352 265 -348 261 -342 259 -154 439 -388 231 -358 255 -7114 237 -370 235 -394 233 -362 233 -386 233 -386 233 -386 233 -386 231 -388 233 -414 233 -180 441 -392 233 -1832 53 -564 53 -370 289 -867 201 -78 103 -352 213 -586 103 -1226 165 -112 55 -300 105 -975 107 -358 77 -410 55 -1777 51 -973 51 -828 diff --git a/assets/unit_tests/subghz/test_random_raw.sub b/assets/unit_tests/subghz/test_random_raw.sub index 900b2620..be635f04 100644 --- a/assets/unit_tests/subghz/test_random_raw.sub +++ b/assets/unit_tests/subghz/test_random_raw.sub @@ -168,3 +168,8 @@ RAW_Data: 285 -96 627 -362 53 -84 201 -374 113 -202 115 -202 421 -316 85 -58 139 RAW_Data: 535 -142 57 -58 55 -116 115 -432 85 -172 259 -192 167 -120 117 -72 119 -240 334 -72 71 -267 285 -144 119 -374 85 -88 85 -114 143 -202 229 -58 143 -202 115 -202 171 -86 71 -144 87 -56 173 -373 143 -116 113 -462 169 -80 215 -148 115 -336 85 -230 163 -432 85 -374 639 -174 85 -58 57 -82 295 -352 269 -532 414 -322 95 -287 263 -268 115 -56 259 -76 85 -282 401 -305 516 -114 115 -202 171 -86 451 -110 85 -346 201 -274 149 -202 85 -364 366 -258 57 -114 259 -172 142 -144 85 -116 85 -480 171 -144 57 -352 115 -116 535 -404 315 -202 163 -158 517 -316 215 -98 85 -346 85 -144 87 -86 257 -82 167 -58 85 -116 113 -894 233 -186 77 -266 147 -72 71 -82 57 -86 171 -58 57 -86 201 -364 143 -202 115 -114 85 -88 113 -86 87 -230 57 -76 613 -72 85 -96 209 -346 458 -58 547 -490 201 -315 315 -116 75 -168 359 -335 95 -384 93 -120 71 -312 251 -366 233 -96 189 -240 263 -192 271 -58 115 -58 229 -346 459 -174 113 -144 173 -144 218 -224 57 -116 215 -72 103 -202 513 -210 433 -116 113 -174 650 -273 147 -450 375 -86 115 -172 536 -84 85 -230 85 -58 195 -468 287 -110 551 -214 167 -311 213 -250 85 -58 85 -355 113 -230 115 -144 117 -288 195 -202 57 -376 123 -144 236 -168 553 -284 119 -72 143 -188 161 -120 93 -312 335 -58 55 -260 105 -244 143 -120 381 -268 173 -268 635 -168 453 -318 71 -167 71 -406 191 -172 215 -408 119 -144 93 -120 97 -130 143 -192 308 -122 147 -550 313 -96 139 -162 167 -96 431 -80 83 -112 201 -86 287 -86 229 -116 57 -288 113 -174 143 -116 113 -144 115 -518 57 -230 57 -172 231 -86 113 -314 183 -144 119 -72 165 -446 81 -86 135 -190 143 -96 71 -72 411 -96 143 -120 69 -216 349 -72 95 -96 517 -646 163 -86 113 -116 171 -116 143 -116 113 -287 259 -114 517 -168 141 -116 105 -72 95 -96 311 -118 159 -310 191 -54 143 -258 115 -450 219 -54 339 -372 239 -72 167 -174 113 -58 57 -144 259 -172 143 -336 113 -174 85 -230 83 -668 85 -202 113 -144 57 -116 373 -316 719 -288 115 -58 75 -120 139 -144 229 -144 57 -144 171 -192 391 -202 403 -58 315 -188 259 -56 115 -144 85 -404 57 -58 105 -102 429 -406 81 -172 57 -144 287 -230 287 -220 317 -458 283 -58 113 -86 269 -72 281 -58 85 -202 113 -52 421 -58 229 -480 259 -58 143 -660 155 -638 123 -86 57 -86 143 -346 143 -144 57 -144 RAW_Data: 2442 -312 275 -972 949 -310 941 -322 923 -342 921 -352 923 -334 281 -954 945 -350 279 -958 907 -354 289 -980 909 -352 281 -962 907 -330 311 -964 913 -350 317 -930 933 -344 921 -352 893 -330 311 -954 943 -318 315 -958 909 -324 947 -7854 953 -322 289 -948 939 -354 927 -332 911 -324 943 -344 917 -318 317 -964 905 -344 303 -942 947 -312 319 -960 913 -348 281 -958 941 -322 295 -978 905 -350 279 -962 931 -328 947 -324 939 -346 267 -964 935 -348 283 -938 953 -318 931 -7868 935 -346 269 -968 953 -310 941 -322 921 -330 935 -342 931 -318 311 -962 939 -290 337 -950 909 -352 317 -924 943 -324 313 -938 941 -318 317 -932 939 -344 301 -938 933 -350 921 -322 959 -310 301 -942 933 -352 317 -926 957 -314 919 -7868 943 -314 317 -958 909 -322 951 -344 919 -352 921 -324 937 -326 281 -964 941 -318 317 -930 939 -344 301 -938 933 -352 281 -962 953 -314 317 -922 933 -330 315 -954 943 -318 921 -342 943 -320 291 -980 909 -354 281 -962 943 -296 967 -7836 943 -332 309 -950 935 -318 929 -340 943 -320 921 -344 921 -354 283 -960 943 -296 309 -964 945 -318 279 -964 941 -322 333 -944 939 -314 279 -992 903 -342 319 -932 933 -330 931 -340 929 -348 281 -964 935 -334 281 -970 927 -346 921 -7862 951 -314 319 -922 953 -320 923 -346 921 -320 965 -298 943 -324 313 -942 941 -320 317 -930 941 -344 303 -940 945 -312 321 -940 953 -314 303 -960 933 -348 287 -962 911 -352 917 -350 905 -324 333 -918 971 -322 317 -924 945 -324 937 -7872 919 -324 317 -942 941 -318 933 -330 943 -324 943 -310 951 -318 317 -930 939 -344 301 -938 933 -352 317 -926 953 -314 319 -924 939 -324 331 -950 907 -354 315 -926 945 -324 939 -312 953 -318 317 -930 937 -344 301 -940 947 -348 909 -7864 949 -310 319 -956 915 -350 919 -348 905 -322 963 -296 935 -348 317 -922 951 -322 295 -976 939 -314 281 -996 915 -326 307 -940 959 -310 301 -966 935 -346 285 -958 915 -348 921 -348 903 -354 303 -948 911 -350 315 -926 945 -324 941 -7874 943 -290 319 -942 973 -318 929 -314 937 -328 941 -324 939 -310 303 -962 933 -352 285 -962 949 -314 319 -924 951 -320 293 -948 941 -354 283 -962 943 -294 309 -966 943 -320 931 -328 943 -326 311 -940 939 -320 309 -958 933 -338 943 -7840 933 -352 277 -964 941 -322 923 -344 923 -350 931 -310 955 -320 291 -974 907 -350 281 -958 963 -298 313 -956 945 -314 311 -960 937 -312 311 -966 909 -324 319 -944 941 -354 929 -298 945 -324 315 -940 RAW_Data: 943 -354 281 -964 905 -330 933 -7868 951 -324 315 -938 943 -354 893 -330 943 -324 943 -344 919 -318 317 -962 903 -344 301 -974 903 -350 317 -932 931 -342 269 -972 949 -346 285 -938 955 -310 301 -964 935 -348 921 -320 921 -344 301 -940 935 -350 317 -930 929 -318 937 -7872 939 -344 301 -940 947 -346 917 -322 921 -344 923 -352 927 -334 281 -970 925 -334 277 -982 943 -318 317 -932 931 -344 301 -936 935 -350 281 -960 957 -312 303 -960 935 -346 907 -322 929 -344 301 -942 935 -350 317 -924 955 -312 951 -7858 919 -342 309 -940 949 -348 909 -322 923 -344 923 -352 923 -336 317 -924 945 -312 311 -966 921 -340 317 -924 947 -350 281 -958 941 -322 291 -976 905 -350 279 -960 935 -342 943 -320 919 -330 311 -958 943 -320 315 -932 935 -344 919 -7866 957 -312 303 -964 917 -342 945 -320 923 -344 923 -354 929 -298 315 -956 941 -318 315 -960 911 -324 317 -942 939 -354 281 -964 941 -294 311 -968 943 -318 317 -932 937 -330 931 -350 919 -348 283 -960 917 -350 317 -922 939 -322 965 -7864 921 -324 329 -950 909 -354 923 -336 913 -322 947 -344 919 -354 281 -962 941 -294 311 -960 935 -354 281 -962 939 -294 311 -964 937 -354 281 -964 941 -296 309 -964 939 -318 931 -330 945 -324 315 -940 939 -354 281 -964 909 -344 921 -7862 963 -304 307 -976 933 -320 929 -328 941 -324 939 -348 915 -320 317 -930 939 -344 301 -940 965 -320 319 -926 953 -312 303 -960 933 -312 321 -960 913 -348 319 -924 943 -320 959 -310 921 -354 319 -924 943 -324 311 -938 941 -318 957 -7862 943 -318 317 -932 933 -344 925 -352 897 -332 943 -324 943 -346 267 -966 951 -310 321 -960 911 -350 281 -958 949 -320 291 -978 937 -316 279 -964 949 -326 309 -944 943 -314 959 -318 933 -336 317 -934 933 -344 267 -964 937 -350 905 -7896 943 -318 319 -926 955 -314 919 -350 935 -324 941 -294 967 -312 303 -962 933 -348 285 -960 917 -348 317 -922 941 -322 329 -950 907 -354 315 -926 943 -326 313 -940 941 -352 893 -332 949 -324 315 -938 941 -352 283 -962 943 -310 925 -7890 931 -344 269 -968 949 -310 943 -320 923 -350 937 -310 955 -318 317 -930 935 -344 301 -942 947 -346 285 -958 915 -346 317 -924 951 -322 295 -982 905 -352 317 -924 945 -324 941 -346 917 -318 317 -962 905 -330 311 -956 937 -352 897 -7878 939 -354 283 -960 941 -294 965 -312 953 -318 385 -201512 165 -198 265 -526 229 -298 755 -164 61687 -17310 131 -1056 99 -296 195 -296 65 -66 1617 +RAW_Data: 77 -76 131 -244 81 -210 55 -1428 53 -344 53 -238 51 -448 51 -804 125 -1490 51 -452 79 -1816 51 -176 197 -700 133 -563 51 -386 79 -474 109 -626 55 -266 103 -616 283 -1932 51 -1034 51 -2809 75 -244 83 -5339 77 -260 105 -839 107 -1806 53 -1408 81 -810 135 -488 187 -1469 73 -2596 75 -74 51 -726 113 -136 83 -406 55 -194 133 -606 55 -1018 55 -1774 51 -1954 75 -910 51 -944 137 -1337 51 -1606 101 -566 75 -584 51 -1470 133 -242 159 -2798 51 -1568 97 -100 71 -556 77 -1234 53 -320 53 -274 68337 -252 333 -278 333 -74 533 -280 307 -276 331 -6948 347 -262 329 -278 329 -278 329 -278 303 -304 303 -302 303 -304 303 -278 329 -278 329 -74 533 -294 325 -270 317 -6944 335 -282 309 -304 309 -304 281 -328 293 -310 281 -326 281 -326 281 -326 279 -302 305 -100 503 -306 305 -302 293 -6992 295 -294 291 -314 285 -336 283 -334 257 -328 283 -328 291 -310 281 -328 279 -328 279 -124 479 -332 279 -328 255 -7012 295 -324 271 -330 267 -346 261 -342 259 -334 257 -358 255 -356 257 -354 231 -354 255 -152 453 -356 257 -352 255 -7024 255 -352 257 -352 255 -354 255 -326 257 -352 255 -352 255 -354 255 -352 253 -352 255 -150 453 -356 255 -354 253 -7030 255 -352 267 -344 251 -354 253 -354 253 -354 267 -322 267 -350 261 -344 257 -364 231 -154 459 -360 257 -354 231 -7062 231 -380 231 -380 231 -352 257 -352 257 -352 257 -352 257 -352 257 -352 255 -354 231 -174 457 -360 229 -378 229 -7084 239 -364 239 -366 229 -380 229 -378 229 -380 229 -378 255 -354 255 -354 255 -352 255 -150 457 -364 253 -354 255 -9542 101 -3126 53 -814 109 -406 51 -162 109 -2219 183 -496 103 -1369 81 -603 99 -2172 79 -1103 75 -676 77 -560 103 -378 51 -654 95 -888 155 -1322 111 -1626 53 -182 51 -166 83 -52 181 -182 71 -2132 77 -2839 103 -4022 79 -362 81 -466 75 -970 203 -998 51 -2085 51 -1853 99 -328 75 -346 55 -1949 79 -2648 79 -434 75 -6757 51 -1920 109 -306 51 -612 101 -996 77 -764 81 -790 125 -1489 99 -430 77 -4142 165 -372 101 -198 71 -1688 51 -1636 99 -434 81 -794 135 -1973 79 -188 109 -2678 81 -196 109 -2099 51 -504 77 -1854 51 -910 107 -948 75 -122 131 -78 79 -1781 103 -3344 111 -406 79 -184 51 -408 103 -54 79 -1474 127 -1789 213 -683 131 -348 161 -5237 53 -2675 101 -52 105 -474 103 -1336 99 -3548 105 -1724 161 -2180 107 -2514 97 -3784 51 -910 77 -505 71 -494 131 -1154 79 -2295 75 -350 161 -274 81 -222 +RAW_Data: 107 -1501 77 -1518 53 -704 113 -390 107 -650 73 -932 51 -3641 169 -704 187 -574 79 -332 51 -3765 51 -1042 75 -1413 103 -2163 75 -218 73 -4118 73 -716 51 -1720 51 -176 145 -817 79 -602 55 -1270 53 -2290 81 -346 79 -1840 53 -596 97 -1135 155 -1672 157 -1150 53 -52 101 -2753 153 -1546 158 -698 79 -2962 215 -490 161 -766 51 -2170 55 -811 51 -694 51 -1461 103 -2590 149 -3785 130 -54 103 -1108 103 -3978 51 -1626 81 -1825 109 -452 129 -1000 79 -1651 157 -276 53 -104 81 -2440 81 -1780 53 -1554 51 -512 131 -2508 99 -176 73 -914 51 -76 81 -202 111 -690 109 -790 109 -584 53 -244 79 -706 73 -550 129 -142 127 -546 77 -296 53 -874 105 -2623 51 -1004 77 -3131 79 -552 81 -2008 187 -1168 55 -1173 51 -2146 99 -700 51 -1580 101 -252 75 -474 77 -569 73 -5817 77 -614 77 -712 149 -228 73 -562 201 -274 127 -648 77 -578 75 -1810 109 -2106 171 -5996 81 -366 159 -274 55 -1228 77 -3386 53 -106 81 -1024 133 -2331 53 -2636 53 -780 149 -842 79 -2288 53 -2807 107 -1410 51 -620 75 -428 71 -272 75 -1140 103 -4912 55 -2261 53 -716 53 -3093 109 -502 111 -1492 53 -4317 51 -500 83 -338 129 -698 105 -1565 103 -1874 105 -344 77 -546 79 -2826 105 -260 75 -616 103 -1254 113 -2687 77 -977 73 -246 97 -1054 109 -4681 67101 -252 357 -252 333 -276 333 -252 355 -254 333 -276 331 -6932 347 -272 353 -242 343 -268 339 -286 309 -282 309 -306 307 -304 283 -328 281 -328 281 -102 505 -308 291 -314 279 -6996 281 -326 279 -328 277 -328 277 -328 279 -328 277 -328 279 -328 279 -328 277 -330 277 -124 483 -322 297 -298 293 -6972 283 -336 281 -332 257 -330 281 -328 283 -328 281 -330 265 -338 255 -352 253 -352 255 -150 455 -336 279 -328 279 -6992 295 -322 265 -348 261 -342 259 -334 259 -356 257 -356 255 -330 257 -354 257 -352 255 -152 455 -358 239 -368 253 -7026 243 -352 243 -350 265 -344 261 -362 231 -358 255 -356 257 -354 231 -380 229 -354 255 -150 455 -360 231 -376 231 -7050 229 -378 229 -378 229 -378 229 -378 229 -378 229 -378 229 -352 255 -352 253 -352 255 -150 455 -362 229 -378 227 -7050 253 -354 229 -378 229 -378 229 -378 227 -378 229 -378 229 -378 229 -378 229 -376 229 -174 431 -374 243 -378 241 -7032 261 -370 233 -362 233 -384 233 -356 233 -380 231 -380 231 -380 231 -380 231 -378 231 -176 431 -384 231 -380 211 -7114 231 -384 231 -384 205 -408 207 -408 207 -408 231 -384 231 -412 207 -412 181 -530 77 -1144 +RAW_Data: 79 -4798 53 -918 83 -4847 51 -755 103 -732 81 -388 55 -1026 77 -1506 101 -242 107 -469 51 -2026 79 -686 77 -348 51 -104 131 -860 129 -148 73 -446 75 -440 97 -306 99 -600 51 -626 105 -1350 95 -674 83 -230 119 -1714 135 -396 155 -1111 109 -652 111 -482 51 -506 55 -1715 103 -968 207 -1156 81 -164 57 -404 99 -508 205 -126 75 -1417 51 -186 77 -588 53 -54 103 -2854 73 -1010 53 -800 51 -2494 53 -106 105 -52 51 -104 79 -1116 51 -654 103 -220 77 -162 71 -5385 137 -2232 79 -1159 79 -250 57 -108 79 -164 107 -1660 79 -3927 129 -992 73 -1913 51 -1430 51 -1498 55 -514 103 -586 81 -386 53 -2402 175 -1994 85 -3431 53 -3209 99 -372 79 -78 53 -1338 75 -682 97 -680 51 -206 101 -1708 101 -452 131 -1397 161 -2272 53 -456 77 -1413 193 -270 109 -466 53 -2432 77 -222 189 -474 107 -774 171 -192 79 -1327 75 -2141 51 -908 135 -3866 75 -804 129 -468 101 -1040 79 -1470 55 -869 77 -1448 105 -160 55 -1916 240 -588 79 -1587 53 -922 79 -2292 181 -1448 51 -552 77 -2189 75 -2545 77 -384 300 -2478 101 -1092 73 -558 79 -132 105 -884 103 -1177 109 -880 79 -2431 109 -1006 105 -468 53 -1378 235 -684 75 -285 73 -604 129 -528 77 -1582 51 -1240 105 -2750 75 -252 51 -1024 95 -1891 51 -864 107 -326 83 -887 159 -1058 163 -322 105 -722 83 -388 81 -936 155 -880 55 -220 83 -2123 135 -2100 73 -1926 103 -1633 149 -526 51 -324 51 -1538 103 -164 137 -964 81 -152 111 -781 225 -655 53 -2888 105 -151 131 -454 53 -4109 77 -1052 53 -178 163 -910 51 -733 207 -2070 53 -474 79 -54 53 -818 51 -1228 53 -2262 79 -788 79 -480 73 -2747 83 -316 183 -1880 105 -862 53 -662 53 -2287 153 -1630 51 -817 243 -806 55 -510 51 -1389 75 -986 135 -498 109 -532 131 -5521 99 -2948 209 -764 75 -1168 75 -886 83 -2065 53 -710 51 -596 77 -374 73 -628 99 -732 51 -202 73 -632 53 -222 55 -511 79 -4884 53 -1826 81 -1266 107 -356 55 -110 113 -280 83 -756 169 -252 81 -1854 51 -1556 157 -258 75 -748 53 -1438 291 -244 71 -1092 77 -1220 229 -1055 181 -1182 71 -1284 77 -864 79 -138 53 -160 53 -952 81 -80 127 -1272 51 -590 103 -502 77 -634 101 -74 51 -224 101 -912 77 -562 51 -164 83 -396 105 -4643 111 -3293 133 -1395 107 -3047 137 -2353 53 -298 83 -54 81 -80 53 -162 83 -392 105 -606 107 -787 53 -928 51 -2800 161 -1146 51 -182 103 -536 103 -994 81 -2044 83 -732 133 -1881 133 -2160 75 -178 +RAW_Data: 75 -1694 101 -122 73 -864 51 -250 129 -406 77 -630 77 -610 101 -781 125 -128 51 -5075 77 -1992 83 -1272 176 -2100 53 -2044 53 -1234 79 -1704 157 -519 99 -2374 101 -100 103 -202 51 -360 77 -1962 103 -2153 77 -1820 191 -164 167 -1320 77 -1718 127 -1374 81 -1047 53 -54 79 -632 53 -656 51 -128 81 -216 51 -755 79 -2692 103 -1478 125 -452 51 -896 157 -3679 135 -632 105 -134 55 -112 77 -588 79 -188 55 -1118 79 -1152 51 -1950 109 -1858 103 -1104 81 -580 131 -226 255 -2932 77 -1536 51 -1044 159 -2135 67667 -252 333 -278 333 -276 333 -74 533 -280 307 -276 345 -6930 331 -276 329 -278 329 -278 327 -278 349 -270 325 -270 317 -290 313 -308 283 -306 309 -100 509 -306 283 -328 281 -6972 307 -302 281 -326 281 -326 281 -328 279 -328 281 -326 279 -328 279 -326 281 -328 279 -124 481 -308 281 -326 279 -6998 281 -326 279 -328 279 -328 277 -330 277 -328 279 -328 279 -328 277 -304 303 -302 303 -100 503 -306 295 -322 293 -6968 287 -342 259 -336 283 -332 257 -356 255 -328 283 -328 257 -352 257 -352 257 -352 255 -150 455 -334 281 -326 281 -6996 265 -342 253 -354 253 -354 253 -354 253 -354 267 -326 269 -350 263 -342 257 -336 259 -152 459 -360 257 -354 231 -7038 267 -338 255 -352 253 -354 253 -354 239 -354 269 -352 239 -372 233 -366 233 -358 257 -154 457 -360 231 -378 231 -7050 231 -380 231 -378 231 -380 231 -378 231 -354 255 -352 257 -352 255 -354 231 -378 229 -176 453 -358 229 -378 231 -7076 231 -378 231 -380 231 -380 229 -354 255 -354 257 -352 257 -352 257 -352 257 -354 255 -150 455 -358 265 -364 229 -4941 101 -1058 153 -670 157 -532 124 -1396 133 -82 165 -162 153 -258 207 -156 131 -1582 85 -714 53 -774 103 -396 274 -110 131 -1965 55 -402 159 -1026 79 -590 77 -3531 57 -500 51 -4770 109 -722 77 -186 53 -298 79 -502 165 -808 77 -438 53 -382 101 -1914 75 -504 77 -1969 135 -5517 99 -576 51 -608 243 -684 53 -2058 315 -1384 79 -1079 77 -232 79 -212 155 -1500 137 -258 75 -975 204 -752 83 -2542 51 -484 103 -78 77 -210 53 -922 157 -1900 107 -2173 83 -384 101 -80 128 -814 183 -978 127 -772 105 -2073 51 -708 53 -300 83 -739 237 -884 131 -3412 157 -1752 81 -164 83 -3373 53 -1406 105 -3809 79 -432 51 -724 77 -548 53 -1955 79 -807 81 -2096 103 -490 105 -1196 109 -108 79 -394 71 -1159 129 -126 143 -340 107 -556 81 -2390 135 -106 133 -690 133 -4347 189 -290 51 -110 53 -78 103 -1101 51 -1362 +RAW_Data: 83 -320 81 -4648 101 -3726 173 -1418 85 -348 53 -2994 79 -1390 51 -1656 107 -764 53 -134 79 -1619 131 -932 55 -2810 107 -3218 79 -765 107 -654 103 -1498 77 -228 51 -134 247 -1526 51 -3903 103 -1495 179 -282 77 -392 53 -1756 105 -368 111 -486 51 -298 53 -216 113 -358 51 -266 187 -1059 81 -780 105 -238 51 -482 53 -791 109 -2169 77 -5304 53 -398 79 -650 51 -54 51 -1789 73 -198 101 -1580 101 -746 97 -4518 53 -744 51 -1064 101 -928 111 -392 185 -869 103 -320 133 -704 81 -244 53 -1628 75 -634 79 -666 183 -1276 83 -218 107 -1163 55 -1276 127 -1144 73 -1400 81 -266 77 -568 129 -806 121 -1420 103 -848 77 -982 103 -2132 81 -1610 101 -1218 55 -2208 75 -2735 53 -921 53 -724 51 -472 83 -3164 185 -400 77 -812 81 -306 215 -2167 53 -130 53 -272 81 -400 79 -1272 81 -418 51 -1381 73 -340 101 -2169 81 -2330 137 -2698 99 -2340 99 -126 51 -1714 55 -488 81 -3500 51 -404 77 -1422 77 -856 215 -80 51 -2308 53 -134 77 -2036 75 -5175 129 -946 239 -638 53 -244 55 -564 105 -826 71 -1632 77 -106 129 -246 135 -366 79 -724 79 -1535 57 -1085 113 -1320 79 -3111 127 -1578 75 -324 75 -102 173 -364 79 -1374 53 -1508 107 -622 51 -526 109 -584 187 -2648 51 -106 79 -380 103 -604 51 -1244 73 -5766 107 -1934 177 -702 51 -1277 53 -1643 79 -1446 81 -4098 75 -574 103 -432 189 -1436 107 -454 79 -132 105 -136 81 -112 113 -942 239 -1238 79 -952 157 -340 51 -314 191 -456 53 -3368 101 -150 99 -464 51 -718 73 -770 101 -150 73 -2132 75 -557 77 -680 81 -3512 151 -760 75 -332 75 -1212 131 -1468 79 -1955 101 -541 75 -344 79 -2146 53 -2299 97 -720 79 -2518 79 -3807 51 -1272 75 -352 77 -52 75 -586 53 -1142 79 -82 81 -2400 157 -324 81 -268 103 -1154 81 -1175 79 -1191 51 -1074 53 -2566 137 -854 75 -1497 51 -4533 51 -2290 51 -344 77 -348 55 -1182 77 -897 135 -874 51 -1064 51 -208 55 -140 55 -1334 133 -1238 157 -1669 113 -2128 75 -848 85 -510 83590 -126 333 -280 331 -252 331 -6946 331 -276 331 -276 329 -278 329 -276 331 -276 331 -276 331 -276 347 -238 351 -254 353 -268 323 -270 345 -6924 335 -282 307 -304 307 -304 281 -304 307 -302 307 -302 305 -302 307 -302 305 -302 281 -124 507 -282 305 -302 305 -6984 279 -328 277 -328 279 -328 277 -330 277 -304 303 -302 305 -302 305 -302 303 -304 303 -100 507 -314 295 -298 293 -6986 283 -334 281 -306 283 -328 283 -328 281 -328 283 -328 255 -352 diff --git a/debug/gdbinit b/debug/gdbinit new file mode 100644 index 00000000..cba5d6b1 --- /dev/null +++ b/debug/gdbinit @@ -0,0 +1,10 @@ +set confirm off +set pagination off +set print pretty on +set print object on +set print static-members on +set print vtbl on +set print demangle on +set demangle-style gnu-v3 +set print sevenbit-strings off + diff --git a/documentation/AppManifests.md b/documentation/AppManifests.md index d70a12f9..195fe925 100644 --- a/documentation/AppManifests.md +++ b/documentation/AppManifests.md @@ -1,63 +1,63 @@ # Flipper Application Manifests (.fam) -All components of Flipper Zero firmware — services, user applications, system settings — are developed independently. Each component has a build system manifest file, named `application.fam`, which defines basic properties of that component and its relations to other parts of the system. +All components of Flipper Zero firmware — services, user applications, and system settings — are developed independently. Each component has a build system manifest file named `application.fam`, which defines the basic properties of that component and its relations to other parts of the system. -When building firmware, **`fbt`** collects all application manifests and processes their dependencies. Then it builds only those components that are referenced in the current build configuration. See [fbt docs](./fbt.md#firmware-application-set) for details on build configurations. +When building firmware, **`fbt`** collects all application manifests and processes their dependencies. Then it builds only those components referenced in the current build configuration. See [FBT docs](./fbt.md#firmware-application-set) for details on build configurations. ## Application definition -Properties of a firmware component are declared in a form of a Python code snippet, forming a call to App() function with various parameters. +A firmware component's properties are declared in a Python code snippet, forming a call to the `App()` function with various parameters. -Only 2 parameters are mandatory: ***appid*** and ***apptype***, others are optional and may only be meaningful for certain application types. +Only two parameters are mandatory: **_appid_** and **_apptype_**. Others are optional and may only be meaningful for certain application types. ### Parameters -* **appid**: string, application id within the build system. Used for specifying which applications to include in build configuration and to resolve dependencies and conflicts. +- **appid**: string, application ID within the build system. It is used to specify which applications to include in the build configuration and resolve dependencies and conflicts. -* **apptype**: member of FlipperAppType.* enumeration. Valid values are: +- **apptype**: member of FlipperAppType.\* enumeration. Valid values are: -| Enum member | Firmware component type | -|--------------|--------------------------| -| SERVICE | System service, created at early startup | -| SYSTEM | Application not being shown in any menus. Can be started by other apps or from CLI | -| APP | Regular application for main menu | -| PLUGIN | Application to be built as a part of firmware an to be placed in Plugins menu | -| DEBUG | Application only visible in Debug menu with debug mode enabled | -| ARCHIVE | One and only Archive app | -| SETTINGS | Application to be placed in System settings menu | -| STARTUP | Callback function to run at system startup. Does not define a separate app | -| EXTERNAL | Application to be built as .fap plugin | -| METAPACKAGE | Does not define any code to be run, used for declaring dependencies and application bundles | - -* **name**: Name that is displayed in menus. -* **entry_point**: C function to be used as application's entry point. Note that C++ function names are mangled, so you need to wrap them in `extern "C"` in order to use them as entry points. -* **flags**: Internal flags for system apps. Do not use. -* **cdefines**: C preprocessor definitions to declare globally for other apps when current application is included in active build configuration. -* **requires**: List of application IDs to also include in build configuration, when current application is referenced in list of applications to build. -* **conflicts**: List of application IDs that current application conflicts with. If any of them is found in constructed application list, **`fbt`** will abort firmware build process. -* **provides**: Functionally identical to ***requires*** field. -* **stack_size**: Stack size, in bytes, to allocate for application on its startup. Note that allocating a stack that is too small for an app to run will cause system crash due to stack overflow, and allocating too much stack space will reduce usable heap memory size for apps to process data. *Note: you can use `ps` and `free` CLI commands to profile your app's memory usage.* -* **icon**: Animated icon name from built-in assets to be used when building app as a part of firmware. -* **order**: Order of an application within its group when sorting entries in it. The lower the order is, the closer to the start of the list the item is placed. *Used for ordering startup hooks and menu entries.* -* **sdk_headers**: List of C header files from this app's code to include in API definitions for external applications. -* **targets**: list of strings, target names, which this application is compatible with. If not specified, application is built for all targets. Default value is `["all"]`. +| Enum member | Firmware component type | +| ----------- | ------------------------------------------------------------------------------------------- | +| SERVICE | System service, created at early startup | +| SYSTEM | Application is not being shown in any menus. It can be started by other apps or from CLI | +| APP | Regular application for the main menu | +| PLUGIN | Application to be built as a part of the firmware and to be placed in the Plugins menu | +| DEBUG | Application only visible in Debug menu with debug mode enabled | +| ARCHIVE | One and only Archive app | +| SETTINGS | Application to be placed in the system settings menu | +| STARTUP | Callback function to run at system startup. Does not define a separate app | +| EXTERNAL | Application to be built as `.fap` plugin | +| METAPACKAGE | Does not define any code to be run, used for declaring dependencies and application bundles | +- **name**: name displayed in menus. +- **entry_point**: C function to be used as the application's entry point. Note that C++ function names are mangled, so you need to wrap them in `extern "C"` to use them as entry points. +- **flags**: internal flags for system apps. Do not use. +- **cdefines**: C preprocessor definitions to declare globally for other apps when the current application is included in the active build configuration. +- **requires**: list of application IDs to include in the build configuration when the current application is referenced in the list of applications to build. +- **conflicts**: list of application IDs with which the current application conflicts. If any of them is found in the constructed application list, **`fbt`** will abort the firmware build process. +- **provides**: functionally identical to **_requires_** field. +- **stack_size**: stack size in bytes to allocate for an application on its startup. Note that allocating a stack too small for an app to run will cause a system crash due to stack overflow, and allocating too much stack space will reduce usable heap memory size for apps to process data. _Note: you can use `ps` and `free` CLI commands to profile your app's memory usage._ +- **icon**: animated icon name from built-in assets to be used when building the app as a part of the firmware. +- **order**: order of an application within its group when sorting entries in it. The lower the order is, the closer to the start of the list the item is placed. _Used for ordering startup hooks and menu entries._ +- **sdk_headers**: list of C header files from this app's code to include in API definitions for external applications. +- **targets**: list of strings and target names with which this application is compatible. If not specified, the application is built for all targets. The default value is `["all"]`. #### Parameters for external applications The following parameters are used only for [FAPs](./AppsOnSDCard.md): -* **sources**: list of strings, file name masks, used for gathering sources within app folder. Default value of `["*.c*"]` includes C and C++ source files. Application cannot use `"lib"` folder for their own source code, as it is reserved for **fap_private_libs**. -* **fap_version**: tuple, 2 numbers in form of (x,y): application version to be embedded within .fap file. Default value is (0,1), meaning version "0.1". -* **fap_icon**: name of a .png file, 1-bit color depth, 10x10px, to be embedded within .fap file. -* **fap_libs**: list of extra libraries to link application against. Provides access to extra functions that are not exported as a part of main firmware at expense of increased .fap file size and RAM consumption. -* **fap_category**: string, may be empty. App subcategory, also works as path of FAP within apps folder in the file system. -* **fap_description**: string, may be empty. Short application description. -* **fap_author**: string, may be empty. Application's author. -* **fap_weburl**: string, may be empty. Application's homepage. -* **fap_icon_assets**: string. If present, defines a folder name to be used for gathering image assets for this application. These images will be preprocessed and built alongside the application. See [FAP assets](./AppsOnSDCard.md#fap-assets) for details. -* **fap_extbuild**: provides support for parts of application sources to be built by external tools. Contains a list of `ExtFile(path="file name", command="shell command")` definitions. **`fbt`** will run the specified command for each file in the list. -Note that commands are executed at the firmware root folder's root, and all intermediate files must be placed in a application's temporary build folder. For that, you can use pattern expansion by **`fbt`**: `${FAP_WORK_DIR}` will be replaced with the path to the application's temporary build folder, and `${FAP_SRC_DIR}` will be replaced with the path to the application's source folder. You can also use other variables defined internally by **`fbt`**. +- **sources**: list of strings, file name masks used for gathering sources within the app folder. The default value of `["*.c*"]` includes C and C++ source files. Applications cannot use the `"lib"` folder for their own source code, as it is reserved for **fap_private_libs**. +- **fap_version**: tuple, 2 numbers in the form of (x,y): application version to be embedded within .fap file. The default value is (0,1), meaning version "0.1". +- **fap_icon**: name of a `.png` file, 1-bit color depth, 10x10px, to be embedded within `.fap` file. +- **fap_libs**: list of extra libraries to link the application against. Provides access to extra functions that are not exported as a part of main firmware at the expense of increased `.fap` file size and RAM consumption. +- **fap_category**: string, may be empty. App subcategory, also determines the path of the FAP within the apps folder in the file system. +- **fap_description**: string, may be empty. Short application description. +- **fap_author**: string, may be empty. Application's author. +- **fap_weburl**: string, may be empty. Application's homepage. +- **fap_icon_assets**: string. If present, it defines a folder name to be used for gathering image assets for this application. These images will be preprocessed and built alongside the application. See [FAP assets](./AppsOnSDCard.md#fap-assets) for details. +- **fap_extbuild**: provides support for parts of application sources to be built by external tools. Contains a list of `ExtFile(path="file name", command="shell command")` definitions. **`fbt`** will run the specified command for each file in the list. + +Note that commands are executed at the firmware root folder, and all intermediate files must be placed in an application's temporary build folder. For that, you can use pattern expansion by **`fbt`**: `${FAP_WORK_DIR}` will be replaced with the path to the application's temporary build folder, and `${FAP_SRC_DIR}` will be replaced with the path to the application's source folder. You can also use other variables defined internally by **`fbt`**. Example for building an app from Rust sources: @@ -71,16 +71,16 @@ Example for building an app from Rust sources: ), ``` -* **fap_private_libs**: list of additional libraries that are distributed as sources alongside the application. These libraries will be built as a part of the application build process. -Library sources must be placed in a subfolder of "`lib`" folder within the application's source folder. -Each library is defined as a call to `Lib()` function, accepting the following parameters: +- **fap_private_libs**: list of additional libraries distributed as sources alongside the application. These libraries will be built as a part of the application build process. + Library sources must be placed in a subfolder of the `lib` folder within the application's source folder. + Each library is defined as a call to the `Lib()` function, accepting the following parameters: - - **name**: name of library's folder. Required. - - **fap_include_paths**: list of library's relative paths to add to parent fap's include path list. Default value is `["."]` meaning library's source root. - - **sources**: list of filename masks to be used for gathering include files for this library. Paths are relative to library's source root. Default value is `["*.c*"]`. - - **cflags**: list of additional compiler flags to be used for building this library. Default value is `[]`. - - **cdefines**: list of additional preprocessor definitions to be used for building this library. Default value is `[]`. - - **cincludes**: list of additional include paths to be used for building this library. Paths are relative to application's root. Can be used for providing external search paths for this library's code - for configuration headers. Default value is `[]`. + - **name**: name of the library's folder. Required. + - **fap_include_paths**: list of the library's relative paths to add to the parent fap's include path list. The default value is `["."]`, meaning the library's source root. + - **sources**: list of filename masks to be used for gathering include files for this library. Paths are relative to the library's source root. The default value is `["*.c*"]`. + - **cflags**: list of additional compiler flags to be used for building this library. The default value is `[]`. + - **cdefines**: list of additional preprocessor definitions to be used for building this library. The default value is `[]`. + - **cincludes**: list of additional include paths to be used for building this library. Paths are relative to the application's root. This can be used for providing external search paths for this library's code — for configuration headers. The default value is `[]`. Example for building an app with a private library: @@ -103,15 +103,14 @@ Example for building an app with a private library: ], ``` -For that snippet, **`fbt`** will build 2 libraries: one from sources in `lib/mbedtls` folder, and another from sources in `lib/loclass` folder. For `mbedtls` library, **`fbt`** will add `lib/mbedtls/include` to the list of include paths for the application and compile only the files specified in `sources` list. Additionally, **`fbt`** will enable `MBEDTLS_ERROR_C` preprocessor definition for `mbedtls` sources. -For `loclass` library, **`fbt`** will add `lib/loclass` to the list of include paths for the application and build all sources in that folder. Also **`fbt`** will disable treating compiler warnings as errors for `loclass` library specifically - that can be useful when compiling large 3rd-party codebases. +For that snippet, **`fbt`** will build 2 libraries: one from sources in `lib/mbedtls` folder and another from sources in the `lib/loclass` folder. For the `mbedtls` library, **`fbt`** will add `lib/mbedtls/include` to the list of include paths for the application and compile only the files specified in the `sources` list. Additionally, **`fbt`** will enable `MBEDTLS_ERROR_C` preprocessor definition for `mbedtls` sources. +For the `loclass` library, **`fbt`** will add `lib/loclass` to the list of the include paths for the application and build all sources in that folder. Also, **`fbt`** will disable treating compiler warnings as errors for the `loclass` library, which can be useful when compiling large 3rd-party codebases. -Both libraries will be linked into the application. +Both libraries will be linked with the application. +## `.fam` file contents -## .fam file contents - -.fam file contains one or more Application definitions. For example, here's a part of `applications/service/bt/application.fam`: +The `.fam` file contains one or more application definitions. For example, here's a part of `applications/service/bt/application.fam`: ```python App( @@ -135,4 +134,4 @@ App( ) ``` -For more examples, see .fam files from various firmware parts. +For more examples, see `.fam` files from various firmware parts. diff --git a/documentation/AppsOnSDCard.md b/documentation/AppsOnSDCard.md index 4acb3ec3..9ab7e9b2 100644 --- a/documentation/AppsOnSDCard.md +++ b/documentation/AppsOnSDCard.md @@ -1,83 +1,80 @@ # FAP (Flipper Application Package) -[fbt](./fbt.md) has support for building applications as FAP files. FAP are essentially .elf executables with extra metadata and resources bundled in. +[fbt](./fbt.md) supports building applications as FAP files. FAPs are essentially `.elf` executables with extra metadata and resources bundled in. -FAPs are built with `faps` target. They can also be deployed to `dist` folder with `fap_dist` target. +FAPs are built with the `faps` target. They can also be deployed to the `dist` folder with the `fap_dist` target. FAPs do not depend on being run on a specific firmware version. Compatibility is determined by the FAP's metadata, which includes the required [API version](#api-versioning). - ## How to set up an application to be built as a FAP -FAPs are created and developed the same way as internal applications that are part of the firmware. +FAPs are created and developed the same way as internal applications that are part of the firmware. -To build your application as a FAP, just create a folder with your app's source code in `applications_user`, then write its code the way you'd do when creating a regular built-in application. Then configure its `application.fam` manifest — and set its *apptype* to FlipperAppType.EXTERNAL. See [Application Manifests](./AppManifests.md#application-definition) for more details. - - * To build your application, run `./fbt fap_{APPID}`, where APPID is your application's ID in its manifest. - * To build your app, then upload it over USB & run it on Flipper, use `./fbt launch_app APPSRC=applications/path/to/app`. This command is configured in default [VSCode profile](../.vscode/ReadMe.md) as "Launch App on Flipper" build action (Ctrl+Shift+B menu). - * To build all FAPs, run `./fbt faps` or `./fbt fap_dist`. +To build your application as a FAP, create a folder with your app's source code in `applications_user`, then write its code the way you'd do when creating a regular built-in application. Then configure its `application.fam` manifest, and set its _apptype_ to FlipperAppType.EXTERNAL. See [Application Manifests](./AppManifests.md#application-definition) for more details. +- To build your application, run `./fbt fap_{APPID}`, where APPID is your application's ID in its manifest. +- To build your app and upload it over USB to run on Flipper, use `./fbt launch_app APPSRC=applications/path/to/app`. This command is configured in the default [VS Code profile](../.vscode/ReadMe.md) as a "Launch App on Flipper" build action (Ctrl+Shift+B menu). +- To build all FAPs, run `./fbt faps` or `./fbt fap_dist`. ## FAP assets FAPs can include static and animated images as private assets. They will be automatically compiled alongside application sources and can be referenced the same way as assets from the main firmware. -To use that feature, put your images in a subfolder inside your application's folder, then reference that folder in your application's manifest in `fap_icon_assets` field. See [Application Manifests](./AppManifests.md#application-definition) for more details. +To use that feature, put your images in a subfolder inside your application's folder, then reference that folder in your application's manifest in the `fap_icon_assets` field. See [Application Manifests](./AppManifests.md#application-definition) for more details. To use these assets in your application, put `#include "{APPID}_icons.h"` in your application's source code, where `{APPID}` is the `appid` value field from your application's manifest. Then you can use all icons from your application's assets the same way as if they were a part of `assets_icons.h` of the main firmware. -Images and animated icons must follow the same [naming convention](../assets/ReadMe.md#asset-naming-rules) as those from the main firmware. - +Images and animated icons should follow the same [naming convention](../assets/ReadMe.md#asset-naming-rules) as those from the main firmware. ## Debugging FAPs -**`fbt`** includes a script for gdb-py to provide debugging support for FAPs, `debug/flipperapps.py`. It is loaded in default debugging configurations by **`fbt`** and stock VSCode configurations. +**`fbt`** includes a script for gdb-py to provide debugging support for FAPs, `debug/flipperapps.py`. It is loaded in default debugging configurations by **`fbt`** and stock VS Code configurations. -With it, you can debug FAPs as if they were a part of main firmware — inspect variables, set breakpoints, step through the code, etc. +With it, you can debug FAPs as if they were a part of the main firmware — inspect variables, set breakpoints, step through the code, etc. ### Setting up debugging environment -Debugging support script looks up debugging information in latest firmware build dir (`build/latest`). That directory is symlinked by fbt to the latest firmware configuration (Debug or Release) build dir, when you run `./fbt` for chosen configuration. See [fbt docs](./fbt.md#nb) for details. +The debugging support script looks up debugging information in the latest firmware build directory (`build/latest`). That directory is symlinked by `fbt` to the latest firmware configuration (Debug or Release) build directory when you run `./fbt` for the chosen configuration. See [fbt docs](./fbt.md#nb) for details. + +To debug FAPs, do the following: -So, to debug FAPs, do the following: 1. Build firmware with `./fbt` 2. Flash it with `./fbt flash` -3. [Build your FAP](#how-to-set-up-an-application-to-be-built-as-a-fap) and run it on Flipper. +3. [Build your FAP](#how-to-set-up-an-application-to-be-built-as-a-fap) and run it on Flipper -After that, you can attach with `./fbt debug` or VSCode and use all debug features. +After that, you can attach with `./fbt debug` or VS Code and use all debug features. -It is **important** that firmware and application build type (debug/release) match, and that matching firmware folder is linked as `build/latest`. Otherwise, debugging will not work. +It is **important** that firmware and application build type (debug/release) match and that the matching firmware folder is linked as `build/latest`. Otherwise, debugging will not work. +## How Flipper runs an application from an SD card -## How Flipper runs an application from SD card +Flipper's MCU cannot run code directly from external storage, so it needs to be copied to RAM first. That is done by the App Loader application responsible for loading the FAP from the SD card, verifying its integrity and compatibility, copying it to RAM, and adjusting it for its new location. -Flipper's MCU cannot run code directly from external storage, so it needs to be copied to RAM first. That is done by the App Loader application, which is responsible for loading the FAP from SD card, verifying its integrity and compatibility, copying it to RAM and adjusting it for its new location. +Since FAP has to be loaded to RAM to be executed, the amount of RAM available for allocations from heap is reduced compared to running the same app from flash, as a part of the firmware. Note that the amount of occupied RAM is less than the total FAP file size since only code and data sections are allocated, while the FAP file includes extra information only used at app load time. -Since FAP has to be loaded to RAM to be executed, the amount of RAM available for allocations from heap is reduced compared to running the same app from flash, as a part of firmware. Note that the amount of occupied RAM is less than total FAP file size, since only code and data sections are allocated, while FAP file includes extra information only used at app load time. +Applications are built for a specific API version. It is a part of the hardware target's definition and contains a major and minor version number. The App Loader checks if the application's major API version matches the firmware's major API version. -Applications are built for a specific API version. It is a part of the hardware target's definition and contains a major and minor version number. Application loader checks if the application's major API version matches firmware's major API version. +The App Loader allocates memory for the application and copies it to RAM, processing relocations and providing concrete addresses for imported symbols using the [symbol table](#symbol-table). Then it starts the application. -App loader allocates memory for the application and copies it to RAM, processing relocations and providing concrete addresses for imported symbols using the [symbol table](#symbol-table). Then it starts the application. +## API versioning +Not all parts of firmware are available for external applications. A subset of available functions and variables is defined in the "api_symbols.csv" file, which is a part of the firmware target definition in the `firmware/targets/` directory. -## API Versioning - -Not all parts of firmware are available for external applications. A subset of available functions and variables is defined in "api_symbols.csv" file, which is a part of firmware target definition in `firmware/targets/` directory. - -**`fbt`** uses semantic versioning for API. Major version is incremented when there are breaking changes in the API, minor version is incremented when new features are added. +**`fbt`** uses semantic versioning for the API. The major version is incremented when there are breaking changes in the API. The minor version is incremented when new features are added. Breaking changes include: -- removal of a function or a global variable; -- changing the signature of a function. -API versioning is mostly automated by **`fbt`**. When rebuilding the firmware, **`fbt`** checks if there are any changes in the API exposed by headers gathered from `SDK_HEADERS`. If so, it stops the build, adjusts the API version and asks the user to go through the changes in .csv file. New entries are marked with "`?`" mark, and the user is supposed to change the mark to "`+`" for the entry to be exposed for FAPs, "`-`" for it to be unavailable. +- Removing a function or a global variable +- Changing the signature of a function + +API versioning is mostly automated by **`fbt`**. When rebuilding the firmware, **`fbt`** checks if there are any changes in the API exposed by headers gathered from `SDK_HEADERS`. If so, it stops the build, adjusts the API version, and asks the user to go through the changes in the `.csv` file. New entries are marked with a "`?`" mark, and the user is supposed to change the mark to "`+`" for the entry to be exposed for FAPs, or to "`-`" for it to be unavailable. **`fbt`** will not allow building a firmware until all "`?`" entries are changed to "`+`" or "`-`". -**NB:** **`fbt`** automatically manages the API version. The only case where manually incrementing the major API version is allowed (and required) is when existing "`+`" entries are to be changed to "`-`". +**NB:** **`fbt`** automatically manages the API version. The only case where manually incrementing the major API version is allowed (and required) is when existing "`+`" entries are to be changed to "`-`". -### Symbol Table +### Symbol table The symbol table is a list of symbols exported by firmware and available for external applications. It is generated by **`fbt`** from the API symbols file and is used by the App Loader to resolve addresses of imported symbols. It is build as a part of the `fap_loader` application. -**`fbt`** also checks if all imported symbols are present in the symbol table. If there are any missing symbols, it will issue a warning listing them. Such application won't be able to run on the device until all requires symbols are provided in the symbol table. +**`fbt`** also checks if all imported symbols are present in the symbol table. If there are any missing symbols, it will issue a warning listing them. The application won't be able to run on the device until all required symbols are provided in the symbol table. diff --git a/documentation/KeyCombo.md b/documentation/KeyCombo.md index e6f55dc5..6db5b411 100644 --- a/documentation/KeyCombo.md +++ b/documentation/KeyCombo.md @@ -1,134 +1,119 @@ # Key Combos -There are times when your flipper feels blue and doesn't respond to your commands. -In that case, you may find this guide useful. +There are times when your Flipper feels blue and doesn't respond to any of your commands due to a software issue. This guide will help you solve this problem. +## Basic combos -## Basic Combos - - -### Hardware Reset +### Hardware reset - Press `LEFT` and `BACK` and hold for a couple of seconds - Release `LEFT` and `BACK` -This combo performs hardware reset by pulling MCU reset line down. -Main components involved: Keys -> DD8(NC7SZ32M5X, OR-gate) -> DD1(STM32WB55, MCU) +This combo performs a hardware reset by pulling the MCU reset line down. +Main components involved: Keys -> DD8(NC7SZ32M5X, OR-gate) -> DD1(STM32WB55, MCU). -There is 1 case when it's not working: - -- MCU debug block is active and holding reset line from inside. +It won't work only in one case: +- The MCU debug block is active and holding the reset line from inside. ### Hardware Power Reset -- Disconnect USB and any external power supplies -- Disconnect USB once again -- Make sure that you've disconnected USB and any external power supplies -- Press `BACK` and hold for 30 seconds (Only will work with USB Disconnected) -- If you have not disconnected USB, then disconnect USB and repeat previous step -- Release `BACK` key +- Disconnect the USB cable and any external power supplies +- Disconnect the USB once again +- Make sure you've disconnected the USB and any external power supplies +- Press `BACK` and hold for 30 seconds (this will only work with the USB disconnected) +- If you haven't disconnected the USB, then disconnect it and repeat the previous step +- Release the `BACK` key This combo performs a reset by switching SYS power line off and then on. -Main components involved: Keys -> DD6(bq25896, charger) +Main components involved: Keys -> DD6(bq25896, charger). -There is 1 case when it's not working: +It won't work only in one case: - Power supply is connected to USB or 5V_ext - ### Software DFU -- Press `LEFT` on boot to enter DFU with flipper boot-loader +- Press `LEFT` on boot to enter DFU with Flipper boot-loader -There is 1 case when it's not working: - -- Flipper Boot-loader is damaged or absent +It won't work only in one case: +- Flipper boot-loader is damaged or absent ### Hardware DFU - Press `OK` on boot to enter DFU with ST boot-loader -There is 1 case when it's not working: +It won't work only in one case: -- Option Bytes are damaged or set to ignore `OK` key - - -## DFU Combos +- Option Bytes are damaged or set to ignore the `OK` key +## DFU combos ### Hardware Reset + Software DFU - Press `LEFT` and `BACK` and hold for a couple of seconds - Release `BACK` -- Device will enter DFU with indication (Blue LED + DFU Screen) +- Device will enter DFU with an indication (Blue LED + DFU Screen) - Release `LEFT` -This combo performs hardware reset by pulling MCU reset line down. -Then `LEFT` key indicates to boot-loader that DFU mode is requested. +This combo performs a hardware reset by pulling the MCU reset line down. Then, the `LEFT` key indicates to the boot-loader that DFU mode is requested. -There are 2 cases when it's not working: - -- MCU debug block is active and holding reset line from inside -- Flipper Boot-loader is damaged or absent +It won't work in two cases: +- The MCU debug block is active and holding the reset line from inside +- Flipper boot-loader is damaged or absent ### Hardware Reset + Hardware DFU -- Press `LEFT` and `BACK` and `OK` and hold for a couple of seconds +- Press `LEFT`, `BACK` and `OK` and hold for a couple of seconds - Release `BACK` and `LEFT` -- Device will enter DFU without indication +- The device will enter DFU without an indication -This combo performs hardware reset by pulling MCU reset line down. -Then `OK` key forces MCU to load internal boot-loader. +This combo performs a hardware reset by pulling the MCU reset line down. Then, the `OK` key forces MCU to load the internal boot-loader. -There are 2 cases when it's not working: - -- MCU debug block is active and holding reset line from inside -- Option Bytes are damaged or set to ignore `OK` key +It won't work in two cases: +- The MCU debug block is active and holding the reset line from inside +- Option Bytes are damaged or set to ignore the `OK` key ### Hardware Power Reset + Software DFU -- Disconnect USB and any external power supplies +- Disconnect the USB and any external power supplies - Press `BACK` and `LEFT` for 30 seconds - Release `BACK` -- Device will enter DFU with indication (Blue LED + DFU Screen) +- The device will enter DFU with an indication (Blue LED + DFU Screen) - Release `LEFT` -- Plug USB +- Plug in the USB -This combo performs reset by switching SYS power line off and then on. -Then `LEFT` key indicates to boot-loader that DFU mode requested. +This combo performs a reset by switching the SYS power line off and then on. Next, the `LEFT` key indicates to the boot-loader that DFU mode is requested. -There are 2 cases when it's not working: +It won't work in two cases: - Power supply is connected to USB or 5V_ext -- Flipper Boot-loader is damaged or absent - +- Flipper boot-loader is damaged or absent ### Hardware Power Reset + Hardware DFU -- Disconnect USB and any external power supplies +- Disconnect the USB and any external power supplies - Press `BACK` and `OK` and hold for 30 seconds - Release `BACK` and `OK` -- Device will enter DFU without indication -- Plug USB +- The device will enter DFU without indication +- Plug in the USB -This combo performs reset by switching SYS power line off and then on. -Then `OK` key forces MCU to load internal boot-loader. +This combo performs a reset by switching the SYS power line off and then on. Next, the `OK` key forces MCU to load the internal boot-loader. -There are 2 cases when it's not working: +It won't work in two cases: - Power supply is connected to USB or 5V_ext -- Option Bytes are damaged or set to ignore `OK` key +- Option Bytes are damaged or set to ignore the `OK` key # Alternative ways to recover your device -If none of the described methods were useful: +If none of the described methods helped you: -- Ensure the battery charged -- Disconnect the battery and connect again (Requires disassembly) -- Try to Flash device with ST-Link or other programmer that supports SWD +- Make sure the battery charged +- Disconnect the battery and connect again (requires disassembly) +- Try to flash the device with ST-Link or another programmer that supports SWD -If you still here and your device is not working: it's not a software issue. +If you're still here and your device is not working: it's not a software issue. diff --git a/documentation/OTA.md b/documentation/OTA.md index 836bc1b7..9d09c0f7 100644 --- a/documentation/OTA.md +++ b/documentation/OTA.md @@ -1,84 +1,79 @@ # Executing code from RAM -In Flipper firmware, we have a special boot mode that loads a specially crafted system image into RAM and transfers control to it. System image executing in RAM has full write access to whole Flipper's flash memory — something that's not possible when running main code from the same flash. +In Flipper firmware, we have a special boot mode that loads a specially crafted system image into RAM and transfers control to it. System image executing in RAM has full write access to Flipper's entire flash memory — something that's not possible when running main code from the same flash. We leverage that boot mode to perform OTA firmware updates, including operations on a radio stack running on the second MCU core. - # How does Flipper OTA work? Installation of OTA updates goes through 3 stages: -## 1. Backing up internal storage (`/int/`) +## 1. Backing up internal storage (`/int`) -It is a special partition of Flipper's flash memory, taking up all available space not used by firmware code. Newer versions of firmware may be of different size, and simply installing them would cause flash repartitioning and data loss. - -So, before taking any action upon the firmware, we back up current configuration from `/int/` into a plain tar archive on SD card. +It is a special partition of Flipper's flash memory, taking up all available space not used by the firmware code. Newer versions of firmware may be of different size, and simply installing them would cause flash repartitioning and data loss. +So, before taking any action on the firmware, we back up the current configuration from `/int` into a plain tar archive on the SD card. ## 2. Performing device update -For that, main firmware loads an updater image - a customized build of main Flipper firmware — into RAM and runs it. Updater performs operations on system flash that are described by an Update manifest file. +The main firmware loads an updater image — a customized build of the main Flipper firmware — into RAM and runs it. Updater performs operations on system flash as described by an Update manifest file. -First, if there's a Radio stack image bundled with the update, updater compares its version with currently installed one. If they don't match, updater performs stack deinstallation followed by writing and installing a new one. The installation itself is performed by proprietary software, FUS, running on Core2, and leads to a series of system restarts. +First, if there's a Radio stack image bundled with the update, updater compares its version with the currently installed one. If they don't match, updater performs stack deinstallation followed by writing and installing a new one. The installation itself is performed by proprietary software FUS running on Core2, and leads to a series of system restarts. Then, updater validates and corrects Option Bytes — a special memory region containing low-level configuration for Flipper's MCU. After that, updater loads a `.dfu` file with firmware to be flashed, checks its integrity using CRC32, writes it to system flash and validates written data. - ## 3. Restoring internal storage and updating resources After performing operations on flash memory, the system restarts into newly flashed firmware. Then it performs restoration of previously backed up `/int` contents. -If the update package contains an additional resources archive, it is extracted onto SD card. - +If the update package contains an additional resources archive, it is extracted onto the SD card. # Update manifest -Update packages come with a manifest that contains a description of its contents. The manifest is in Flipper File Format — a simple text file, comprised of key-value pairs. +An update package comes with a manifest that contains a description of its contents. The manifest is in Flipper File Format — a simple text file, comprised of key-value pairs. ## Mandatory fields -An update manifest must contain the following keys in given order: +An update manifest must contain the following keys in the given order: -* __Filetype__: a constant string, "Flipper firmware upgrade configuration"; +- **Filetype**: a constant string, "Flipper firmware upgrade configuration". -* __Version__: manifest version. Current value is 2; +- **Version**: manifest version. The current value is 2. -* __Info__: arbitrary string, describing package contents; +- **Info**: arbitrary string, describing package contents. -* __Target__: hardware revision the package is built for; +- **Target**: hardware revision for which the package is built. -* __Loader__: file name of stage 2 loader that is executed from RAM; +- **Loader**: file name of stage 2 loader that is executed from RAM. -* __Loader CRC__: CRC32 of loader file. Note that it is represented in little-endian hex. +- **Loader CRC**: CRC32 of loader file. Note that it is represented in little-endian hex. ## Optional fields -Other fields may have empty values, is such case, updater skips all operations related to such values. +Other fields may have empty values. In this case, updater skips all operations related to these values. -* __Radio__: file name of radio stack image, provided by STM; +- **Radio**: file name of radio stack image, provided by STM. -* __Radio address__: address to install the radio stack at. It is specified in Release Notes by STM; +- **Radio address**: address to install the radio stack at. It is specified in Release Notes by STM. -* __Radio version__: Radio major, minor and sub versions followed by branch, release, and stack type packed into 6 hex-encoded bytes; +- **Radio version**: radio major, minor and sub versions followed by branch, release and stack type packed into 6 hex-encoded bytes. -* __Radio CRC__: CRC32 of radio image; +- **Radio CRC**: CRC32 of radio image. -* __Resources__: file name of TAR archive with resources to be extracted on SD card; - -* __OB reference__, __OB mask__, __OB write mask__: reference values for validating and correcting option bytes. +- **Resources**: file name of TAR archive with resources to be extracted onto the SD card. +- **OB reference**, **OB mask**, **OB write mask**: reference values for validating and correcting option bytes. # OTA update error codes -We designed the OTA update process to be as fail-safe as possible. We don't start any risky operation before validating all related pieces of data to ensure we don't leave the device in a partially updated, or bricked, state. +We designed the OTA update process to be as fail-safe as possible. We don't start any risky operations before validating all related pieces of data to ensure we don't leave the device in a partially updated, or bricked, state. -Even if something goes wrong, Updater gives you an option to retry failed operations, and reports its state with an error code. These error codes have an `[XX-YY]` format, where `XX` encodes an operation that failed, and `YY` contains extra details on its progress where the error occurred. +Even if something goes wrong, updater allows you to retry failed operations and reports its state with an error code. These error codes have an `[XX-YY]` format, where `XX` encodes the failed operation, and `YY` contains extra details on its progress where the error occurred. | Stage description | Code | Progress | Description | -|:-----------------------:|-------:|------------|--------------------------------------------| +| :---------------------: | -----: | ---------- | ------------------------------------------ | | Loading update manifest | **1** | **13** | Updater reported hardware version mismatch | | | | **20** | Failed to get saved manifest path | | | | **30** | Failed to load manifest | @@ -86,61 +81,60 @@ Even if something goes wrong, Updater gives you an option to retry failed operat | | | **50** | Package has mismatching HW target | | | | **60** | Missing DFU file | | | | **80** | Missing radio firmware file | -| Backing up LFS | **2** | **0-100** | FS read/write error | -| Checking radio FW | **3** | **0-99** | Error reading radio firmware file | +| Backing up LFS | **2** | **0-100** | FS read/write error | +| Checking radio FW | **3** | **0-99** | Error reading radio firmware file | | | | **100** | CRC mismatch | -| Uninstalling radio FW | **4** | **0** | SHCI Delete command error | +| Uninstalling radio FW | **4** | **0** | SHCI Delete command error | | | | **80** | Error awaiting command status | -| Writing radio FW | **5** | **0-100** | Block read/write error | -| Installing radio FW | **6** | **0** | SHCI Install command error | +| Writing radio FW | **5** | **0-100** | Block read/write error | +| Installing radio FW | **6** | **0** | SHCI Install command error | | | | **80** | Error awaiting command status | -| Radio is updating | **7** | **10** | Error waiting for operation completion | -| Validating opt. bytes | **8** | **yy** | Option byte code | -| Checking DFU file | **9** | **0** | Error opening DFU file | +| Radio is updating | **7** | **10** | Error waiting for operation completion | +| Validating opt. bytes | **8** | **yy** | Option byte code | +| Checking DFU file | **9** | **0** | Error opening DFU file | | | | **1-98** | Error reading DFU file | | | | **99-100** | Corrupted DFU file | -| Writing flash | **10** | **0-100** | Block read/write error | -| Validating flash | **11** | **0-100** | Block read/write error | -| Restoring LFS | **12** | **0-100** | FS read/write error | -| Updating resources | **13** | **0-100** | SD card read/write error | - +| Writing flash | **10** | **0-100** | Block read/write error | +| Validating flash | **11** | **0-100** | Block read/write error | +| Restoring LFS | **12** | **0-100** | FS read/write error | +| Updating resources | **13** | **0-100** | SD card read/write error | # Building update packages - ## Full package -To build full update package, including firmware, radio stack and resources for SD card, run `./fbt COMPACT=1 DEBUG=0 updater_package` +To build a full update package, including firmware, radio stack and resources for the SD card, run: +`./fbt COMPACT=1 DEBUG=0 updater_package` ## Minimal package -To build minimal update package, including only firmware, run `./fbt COMPACT=1 DEBUG=0 updater_minpackage` +To build a minimal update package, including only firmware, run: +`./fbt COMPACT=1 DEBUG=0 updater_minpackage` ## Customizing update bundles -Default update packages are built with Bluetooth Light stack. -You can pick a different stack, if your firmware version supports it, and build a bundle with it passing stack type and binary name to `fbt`: +Default update packages are built with Bluetooth Light stack. +You can pick a different stack if your firmware version supports it, and build a bundle with it by passing the stack type and binary name to `fbt`: -`./fbt updater_package COMPACT=1 DEBUG=0 COPRO_OB_DATA=scripts/ob_custradio.data COPRO_STACK_BIN=stm32wb5x_BLE_Stack_full_fw.bin COPRO_STACK_TYPE=ble_full` +`./fbt updater_package COMPACT=1 DEBUG=0 COPRO_OB_DATA=scripts/ob_custradio.data COPRO_STACK_BIN=stm32wb5x_BLE_Stack_full_fw.bin COPRO_STACK_TYPE=ble_full` -Note that `COPRO_OB_DATA` must point to a valid file in `scripts` folder containing reference Option Byte data matching to your radio stack type. +Note that `COPRO_OB_DATA` must point to a valid file in the `scripts` folder containing reference Option Byte data matching your radio stack type. In certain cases, you might have to confirm your intentions by adding `COPRO_DISCLAIMER=...` to the build command line. - ## Building partial update packages -You can customize package contents by calling `scripts/update.py` directly. +You can customize package contents by calling `scripts/update.py` directly. For example, to build a package only for installing BLE FULL stack: ```shell scripts/update.py generate \ - -t f7 -d r13.3_full -v "BLE FULL 13.3" \ - --stage dist/f7/flipper-z-f7-updater-*.bin \ - --radio lib/STM32CubeWB/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x/stm32wb5x_BLE_Stack_full_fw.bin \ - --radiotype ble_full + -t f7 -d r13.3_full -v "BLE FULL 13.3" \ + --stage dist/f7/flipper-z-f7-updater-*.bin \ + --radio lib/STM32CubeWB/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x/stm32wb5x_BLE_Stack_full_fw.bin \ + --radiotype ble_full ``` -For full list of options, check `scripts/update.py generate` help. +For the full list of options, check `scripts/update.py generate` help. diff --git a/documentation/RoadMap.md b/documentation/RoadMap.md new file mode 100644 index 00000000..658bb208 --- /dev/null +++ b/documentation/RoadMap.md @@ -0,0 +1,46 @@ +# RoadMap + +# Where we are now (0.x.x branch) + +Our goal for the 0.x.x branch is to build an API and apps that are stable and usable. The first public release in this branch is 0.43.1. + +## What's already implemented + +**System and HAL** + +- Furi Core +- Furi HAL +- Loading applications from SD + +**Applications** + +- SubGhz: all most common protocols, reading RAW for everything else +- 125kHz RFID: all most common protocols +- NFC: reading/emulating MIFARE Ultralight, reading MIFARE Classic and DESFire, basic EMV, and basic NFC-B/F/V +- Infrared: all most common RC protocols, RAW format for everything else +- GPIO: UART bridge, basic GPIO controls +- iButton: DS1990, Cyfral, Metakom +- Bad USB: full USB Rubber Ducky support, some extras for Windows Alt codes +- U2F: full U2F specification support + +**External applications** + +- Bluetooth +- Snake game + +# Where we're going (Version 1) + +The main goal for 1.0.0 is to provide the first stable version for both Users and Developers. + +## What we're planning to implement in 1.0.0 + +- More protocols (gathering feedback) +- User documentation (work in progress) +- FuriHal: deep sleep mode, stable API, examples, documentation (work in progress) +- Application improvements (a ton of things that we want to add and improve that are too numerous to list here) + +## When will it happen, and where can I see the progress? + +Release 1.0.0 will likely happen around the end of 2023Q1. + +You can track the development progress in our public Miro board: https://miro.com/app/board/uXjVO_3D6xU=/?moveToWidget=3458764522498020058&cot=14 diff --git a/documentation/UnitTests.md b/documentation/UnitTests.md index 89642656..d38d4c4b 100644 --- a/documentation/UnitTests.md +++ b/documentation/UnitTests.md @@ -1,49 +1,65 @@ # Unit tests + ## Intro -Unit tests are special pieces of code that apply known inputs to the feature code and check the results to see if they were correct. + +Unit tests are special pieces of code that apply known inputs to the feature code and check the results to see if they are correct. They are crucial for writing robust, bug-free code. Flipper Zero firmware includes a separate application called [unit_tests](/applications/debug/unit_tests). -It is run directly on the Flipper Zero in order to employ its hardware features and to rule out any platform-related differences. +It is run directly on Flipper devices in order to employ their hardware features and rule out any platform-related differences. -When contributing code to the Flipper Zero firmware, it is highly desirable to supply unit tests along with the proposed features. +When contributing code to the Flipper Zero firmware, it is highly desirable to supply unit tests along with the proposed features. Running existing unit tests is useful to ensure that the new code doesn't introduce any regressions. ## Running unit tests -In order to run the unit tests, follow these steps: + +To run the unit tests, follow these steps: + 1. Compile the firmware with the tests enabled: `./fbt FIRMWARE_APP_SET=unit_tests`. 2. Flash the firmware using your preferred method. 3. Copy the [assets/unit_tests](assets/unit_tests) folder to the root of your Flipper Zero's SD card. 4. Launch the CLI session and run the `unit_tests` command. -**NOTE:** To run a particular test (and skip all others), specify its name as the command argument. +**NOTE:** To run a particular test (and skip all others), specify its name as the command argument. See [test_index.c](applications/debug/unit_tests/test_index.c) for the complete list of test names. ## Adding unit tests + ### General + #### Entry point + The common entry point for all tests is the [unit_tests](applications/debug/unit_tests) application. Test-specific code is placed into an arbitrarily named subdirectory and is then called from the [test_index.c](applications/debug/unit_tests/test_index.c) source file. + #### Test assets -Some unit tests require external data in order to function. These files (commonly called assets) reside in the [assets/unit_tests](/assets/unit_tests) directory in their respective subdirectories. Asset files can be of any type (plain text, FlipperFormat(FFF), binary, etc). + +Some unit tests require external data in order to function. These files (commonly called assets) reside in the [assets/unit_tests](/assets/unit_tests) directory in their respective subdirectories. Asset files can be of any type (plain text, FlipperFormat (FFF), binary, etc.). + ### Application-specific + #### Infrared + Each infrared protocol has a corresponding set of unit tests, so it makes sense to implement one when adding support for a new protocol. -In order to add unit tests for your protocol, follow these steps: +To add unit tests for your protocol, follow these steps: + 1. Create a file named `test_.irtest` in the [assets](assets/unit_tests/infrared) directory. 2. Fill it with the test data (more on it below). 3. Add the test code to [infrared_test.c](applications/debug/unit_tests/infrared/infrared_test.c). 4. Update the [assets](assets/unit_tests/infrared) on your Flipper Zero and run the tests to see if they pass. ##### Test data format -Each unit test has 3 sections: -1. `decoder` - takes in raw signal and outputs decoded messages. + +Each unit test has three sections: + +1. `decoder` - takes in a raw signal and outputs decoded messages. 2. `encoder` - takes in decoded messages and outputs a raw signal. -3. `encoder_decoder` - takes in decoded messages, turns them into a raw signal, and then decodes again. +3. `encoder_decoder` - takes in decoded messages, turns them into a raw signal, and then decodes again. Infrared test asset files have an `.irtest` extension and are regular `.ir` files with a few additions. -Decoder input data has signal names `decoder_input_N`, where N is a test sequence number. Expected data goes under the name `decoder_expected_N`. When testing the encoder these two are switched. +Decoder input data has signal names `decoder_input_N`, where N is a test sequence number. Expected data goes under the name `decoder_expected_N`. When testing the encoder, these two are switched. -Decoded data is represented in arrays (since a single raw signal may decode to several messages). If there is only one signal, then it has to be an array of size 1. Use the existing files as syntax examples. +Decoded data is represented in arrays (since a single raw signal may be decoded into several messages). If there is only one signal, then it has to be an array of size 1. Use the existing files as syntax examples. ##### Getting raw signals + Recording raw IR signals are possible using the Flipper Zero. Launch the CLI session, run `ir rx raw`, then point the remote towards Flipper's receiver and send the signals. The raw signal data will be printed to the console in a convenient format. diff --git a/documentation/UniversalRemotes.md b/documentation/UniversalRemotes.md index 186a0e65..264829e1 100644 --- a/documentation/UniversalRemotes.md +++ b/documentation/UniversalRemotes.md @@ -1,62 +1,69 @@ # Universal Remotes + ## Televisions -Adding your TV set to the universal remote is quite straightforward. Up to 6 signals can be recorded: `Power`, `Mute`, `Vol_up`, `Vol_dn`, `Ch_next`, `Ch_prev`. Any of them can be omitted if not supported by the TV. + +Adding your TV set to the universal remote is quite straightforward. Up to 6 signals can be recorded: `Power`, `Mute`, `Vol_up`, `Vol_dn`, `Ch_next`, and `Ch_prev`. Any of them can be omitted if not supported by your TV. Each signal is recorded using the following algorithm: + 1. Get the remote and point it to Flipper's IR receiver. 2. Start learning a new remote if it's the first button or press `+` to add a new button otherwise. 3. Press a remote button and save it under a corresponding name. 4. Repeat steps 2-3 until all required signals are saved. -The signal names are self-explanatory. Don't forget to make sure that every recorded signal does what it's supposed to. +The signal names are self-explanatory. Remember to make sure that every recorded signal does what it's supposed to. If everything checks out, append these signals **to the end** of the [TV universal remote file](/assets/resources/infrared/assets/tv.ir). -## Audio Players -Adding your audio player to the universal remote is done in the same manner as described above. Up to 8 signals can be recorded: `Power`, `Play`, `Pause`, `Vol_up`, `Vol_dn`, `Next`, `Prev`, `Mute`. Any of them can be omitted if not supported by the player. +## Audio players -The signal names are self-explanatory. -On many remotes, the `Play` button doubles as `Pause`. In this case record it as `Play` omitting the `Pause`. +Adding your audio player to the universal remote is done in the same manner as described above. Up to 8 signals can be recorded: `Power`, `Play`, `Pause`, `Vol_up`, `Vol_dn`, `Next`, `Prev`, and `Mute`. Any of them can be omitted if not supported by the player. + +The signal names are self-explanatory. +On many remotes, the `Play` button doubles as `Pause`. In this case, record it as `Play` omitting the `Pause`. Make sure that every signal does what it's supposed to. -If everything checks out, append these signals **to the end** of the [Audio players universal remote file](/assets/resources/infrared/assets/audio.ir). +If everything checks out, append these signals **to the end** of the [audio player universal remote file](/assets/resources/infrared/assets/audio.ir). + +## Air conditioners -## Air Conditioners Air conditioners differ from most other infrared-controlled devices because their state is tracked by the remote. -The majority of A/C remotes have a small display which shows current mode, temperature and other settings. +The majority of A/C remotes have a small display that shows the current mode, temperature, and other settings. When the user presses a button, a whole set of parameters is transmitted to the device, which must be recorded and used as a whole. -In order to add a particular air conditioner to the universal remote, 6 signals must be recorded: `Off`, `Dh`, `Cool_hi`, `Cool_lo`, `Heat_hi`, `Heat_lo`. +In order to add a particular air conditioner to the universal remote, 6 signals must be recorded: `Off`, `Dh`, `Cool_hi`, `Cool_lo`, `Heat_hi`, and `Heat_lo`. Each signal (except `Off`) is recorded using the following algorithm: 1. Get the remote and press the **Power Button** so that the display shows that A/C is ON. -2. Set the A/C to the corresponding mode (see table below), while leaving other parameters such as fan speed or vane on **AUTO** (if applicable). +2. Set the A/C to the corresponding mode (see table below), leaving other parameters such as fan speed or vane on **AUTO** (if applicable). 3. Press the **POWER** button to switch the A/C off. 4. Start learning a new remote on Flipper if it's the first button or press `+` to add a new button otherwise. 5. Point the remote to Flipper's IR receiver as directed and press **POWER** button once again. 6. Save the resulting signal under the specified name. -7. Repeat the steps 2-6 for each signal from the table below. +7. Repeat steps 2-6 for each signal from the table below. -| Signal | Mode | Temperature | Note | +| Signal | Mode | Temperature | Note | | :-----: | :--------: | :---------: | ----------------------------------- | -| Dh | Dehumidify | N/A | | -| Cool_hi | Cooling | See note | Lowest temperature in cooling mode | -| Cool_lo | Cooling | 23°C | | -| Heat_hi | Heating | See note | Highest temperature in heating mode | -| Heat_lo | Heating | 23°C | | +| Dh | Dehumidify | N/A | | +| Cool_hi | Cooling | See note | Lowest temperature in cooling mode | +| Cool_lo | Cooling | 23°C | | +| Heat_hi | Heating | See note | Highest temperature in heating mode | +| Heat_lo | Heating | 23°C | | Finally, record the `Off` signal: -1. Make sure the display shows that A/C is ON. + +1. Make sure the display shows that the A/C is ON. 2. Start learning a new signal on Flipper and point the remote towards the IR receiver. 3. Press the **POWER** button so that the remote shows the OFF state. 4. Save the resulting signal under the name `Off`. -The resulting remote file should now contain 6 signals. Any of them can be omitted, but that will mean that this functionality will not be used. +The resulting remote file should now contain 6 signals. You can omit any of them, but you then won't be able to use their functionality. Test the file against the actual device. Make sure that every signal does what it's supposed to. If everything checks out, append these signals **to the end** of the [A/C universal remote file](/assets/resources/infrared/assets/ac.ir). ## Final steps -The order of signals is not important, but they must be preceded by a following comment: `# Model: ` in order to keep the library organised. + +The order of signals is not important, but they should be preceded by the following comment: `# Model: ` in order to keep the library organized. When done, open a pull request containing the changed file. diff --git a/documentation/fbt.md b/documentation/fbt.md index 4726268d..7b1aa8b4 100644 --- a/documentation/fbt.md +++ b/documentation/fbt.md @@ -5,102 +5,99 @@ It is invoked by `./fbt` in the firmware project root directory. Internally, it ## Requirements -Please install Python packages required by assets build scripts: `pip3 install -r scripts/requirements.txt` +Install Python packages required by assets build scripts: `pip3 install -r scripts/requirements.txt` ## NB -* `fbt` constructs all referenced environments & their targets' dependency trees on startup. So, to keep startup time as low as possible, we're hiding construction of certain targets behind command-line options. -* `fbt` always performs `git submodule update --init` on start, unless you set `FBT_NO_SYNC=1` in environment: - * On Windows, that's `set "FBT_NO_SYNC=1"` in the shell you're running `fbt` from - * On \*nix, it's `$ FBT_NO_SYNC=1 ./fbt ...` -* `fbt` builds updater & firmware in separate subdirectories in `build`, with their names depending on optimization settings (`COMPACT` & `DEBUG` options). However, for ease of integration with IDEs, latest built variant's directory is always linked as `built/latest`. Additionally, `compile_commands.json` is generated in that folder, which is used for code completion support in IDE. +- `fbt` constructs all referenced environments and their targets' dependency trees on startup. So, to keep startup time as low as possible, we're hiding the construction of certain targets behind command-line options. +- `fbt` always performs `git submodule update --init` on start, unless you set `FBT_NO_SYNC=1` in the environment: + - On Windows, it's `set "FBT_NO_SYNC=1"` in the shell you're running `fbt` from + - On \*nix, it's `$ FBT_NO_SYNC=1 ./fbt ...` +- `fbt` builds updater & firmware in separate subdirectories in `build`, and their names depend on optimization settings (`COMPACT` & `DEBUG` options). However, for ease of integration with IDEs, the latest built variant's directory is always linked as `built/latest`. Additionally, `compile_commands.json` is generated in that folder (used for code completion support in IDE). ## Invoking FBT -To build with FBT, call it specifying configuration options & targets to build. For example, +To build with FBT, call it and specify configuration options & targets to build. For example: `./fbt COMPACT=1 DEBUG=0 VERBOSE=1 updater_package copro_dist` -To run cleanup (think of `make clean`) for specified targets, add `-c` option. +To run cleanup (think of `make clean`) for specified targets, add the `-c` option. ## VSCode integration -`fbt` includes basic development environment configuration for VSCode. To deploy it, run `./fbt vscode_dist`. That will copy initial environment configuration to `.vscode` folder. After that, you can use that configuration by starting VSCode and choosing firmware root folder in "File > Open Folder" menu. - - * On first start, you'll be prompted to install recommended plug-ins. Please install them for best development experience. _You can find a list of them in `.vscode/extensions.json`._ - * Basic build tasks are invoked in Ctrl+Shift+B menu. - * Debugging requires a supported probe. That includes: - * Wi-Fi devboard with stock firmware (blackmagic), - * ST-Link and compatible devices, - * J-Link for flashing and debugging (in VSCode only). _Note that J-Link tools are not included with our toolchain and you have to [download](https://www.segger.com/downloads/jlink/) them yourself and put on your system's PATH._ - * Without a supported probe, you can install firmware on Flipper using USB installation method. +`fbt` includes basic development environment configuration for VS Code. Run `./fbt vscode_dist` to deploy it. That will copy the initial environment configuration to the `.vscode` folder. After that, you can use that configuration by starting VS Code and choosing the firmware root folder in the "File > Open Folder" menu. +- On the first start, you'll be prompted to install recommended plugins. We highly recommend installing them for the best development experience. _You can find a list of them in `.vscode/extensions.json`._ +- Basic build tasks are invoked in the Ctrl+Shift+B menu. +- Debugging requires a supported probe. That includes: + - Wi-Fi devboard with stock firmware (blackmagic). + - ST-Link and compatible devices. + - J-Link for flashing and debugging (in VSCode only). _Note that J-Link tools are not included with our toolchain and you have to [download](https://www.segger.com/downloads/jlink/) them yourself and put them on your system's PATH._ +- Without a supported probe, you can install firmware on Flipper using the USB installation method. ## FBT targets **`fbt`** keeps track of internal dependencies, so you only need to build the highest-level target you need, and **`fbt`** will make sure everything they depend on is up-to-date. ### High-level (what you most likely need) - -- `fw_dist` - build & publish firmware to `dist` folder. This is a default target, when no other are specified -- `fap_dist` - build external plugins & publish to `dist` folder -- `updater_package`, `updater_minpackage` - build self-update package. Minimal version only includes firmware's DFU file; full version also includes radio stack & resources for SD card -- `copro_dist` - bundle Core2 FUS+stack binaries for qFlipper -- `flash` - flash attached device with OpenOCD over ST-Link -- `flash_usb`, `flash_usb_full` - build, upload and install update package to device over USB. See details on `updater_package`, `updater_minpackage` -- `debug` - build and flash firmware, then attach with gdb with firmware's .elf loaded -- `debug_other`, `debug_other_blackmagic` - attach gdb without loading any .elf. Allows to manually add external elf files with `add-symbol-file` in gdb -- `updater_debug` - attach gdb with updater's .elf loaded -- `blackmagic` - debug firmware with Blackmagic probe (WiFi dev board) -- `openocd` - just start OpenOCD -- `get_blackmagic` - output blackmagic address in gdb remote format. Useful for IDE integration + +- `fw_dist` - build & publish firmware to the `dist` folder. This is a default target when no others are specified. +- `fap_dist` - build external plugins & publish to the `dist` folder. +- `updater_package`, `updater_minpackage` - build a self-update package. The minimal version only includes the firmware's DFU file; the full version also includes a radio stack & resources for the SD card. +- `copro_dist` - bundle Core2 FUS+stack binaries for qFlipper. +- `flash` - flash the attached device with OpenOCD over ST-Link. +- `flash_usb`, `flash_usb_full` - build, upload and install the update package to the device over USB. See details on `updater_package` and `updater_minpackage`. +- `debug` - build and flash firmware, then attach with gdb with firmware's .elf loaded. +- `debug_other`, `debug_other_blackmagic` - attach GDB without loading any `.elf`. It will allow you to manually add external `.elf` files with `add-symbol-file` in GDB. +- `updater_debug` - attach GDB with the updater's `.elf` loaded. +- `blackmagic` - debug firmware with Blackmagic probe (WiFi dev board). +- `openocd` - just start OpenOCD. +- `get_blackmagic` - output the blackmagic address in the GDB remote format. Useful for IDE integration. - `get_stlink` - output serial numbers for attached STLink probes. Used for specifying an adapter with `OPENOCD_ADAPTER_SERIAL=...`. -- `lint`, `format` - run clang-format on C source code to check and reformat it according to `.clang-format` specs -- `lint_py`, `format_py` - run [black](https://black.readthedocs.io/en/stable/index.html) on Python source code, build system files & application manifests -- `cli` - start Flipper CLI session over USB +- `lint`, `format` - run clang-format on the C source code to check and reformat it according to the `.clang-format` specs. +- `lint_py`, `format_py` - run [black](https://black.readthedocs.io/en/stable/index.html) on the Python source code, build system files & application manifests. +- `cli` - start a Flipper CLI session over USB. ### Firmware targets -- `faps` - build all external & plugin apps as [.faps](./AppsOnSDCard.md#fap-flipper-application-package). +- `faps` - build all external & plugin apps as [`.faps`](./AppsOnSDCard.md#fap-flipper-application-package). - **`fbt`** also defines per-app targets. For example, for an app with `appid=snake_game` target names are: - - `fap_snake_game`, etc - build single app as .fap by its application ID. - - Check out [`--extra-ext-apps`](#command-line-parameters) for force adding extra apps to external build - - `fap_snake_game_list`, etc - generate source + assembler listing for app's .fap -- `flash`, `firmware_flash` - flash current version to attached device with OpenOCD over ST-Link -- `jflash` - flash current version to attached device with JFlash using J-Link probe. JFlash executable must be on your $PATH -- `flash_blackmagic` - flash current version to attached device with Blackmagic probe -- `firmware_all`, `updater_all` - build basic set of binaries -- `firmware_list`, `updater_list` - generate source + assembler listing -- `firmware_cdb`, `updater_cdb` - generate `compilation_database.json` file for external tools and IDEs. It can be created without actually building the firmware. + - `fap_snake_game`, etc. - build single app as `.fap` by its application ID. + - Check out [`--extra-ext-apps`](#command-line-parameters) for force adding extra apps to external build. + - `fap_snake_game_list`, etc - generate source + assembler listing for app's `.fap`. +- `flash`, `firmware_flash` - flash the current version to the attached device with OpenOCD over ST-Link. +- `jflash` - flash the current version to the attached device with JFlash using a J-Link probe. The JFlash executable must be on your `$PATH`. +- `flash_blackmagic` - flash the current version to the attached device with a Blackmagic probe. +- `firmware_all`, `updater_all` - build a basic set of binaries. +- `firmware_list`, `updater_list` - generate source + assembler listing. +- `firmware_cdb`, `updater_cdb` - generate a `compilation_database.json` file for external tools and IDEs. It can be created without actually building the firmware. ### Assets -- `resources` - build resources and their Manifest - - `dolphin_ext` - process dolphin animations for SD card -- `icons` - generate .c+.h for icons from png assets -- `proto` - generate .pb.c+.pb.h for .proto sources -- `proto_ver` - generate .h with protobuf version -- `dolphin_internal`, `dolphin_blocking` - generate .c+.h for corresponding dolphin assets - +- `resources` - build resources and their manifest files + - `dolphin_ext` - process dolphin animations for the SD card +- `icons` - generate `.c+.h` for icons from PNG assets +- `proto` - generate `.pb.c+.pb.h` for `.proto` sources +- `proto_ver` - generate `.h` with a protobuf version +- `dolphin_internal`, `dolphin_blocking` - generate `.c+.h` for corresponding dolphin assets ## Command-line parameters -- `--options optionfile.py` (default value `fbt_options.py`) - load file with multiple configuration values -- `--extra-int-apps=app1,app2,appN` - forces listed apps to be built as internal with `firmware` target -- `--extra-ext-apps=app1,app2,appN` - forces listed apps to be built as external with `firmware_extapps` target -- `--proxy-env=VAR1,VAR2` - additional environment variables to expose to subprocesses spawned by `fbt`. By default, `fbt` sanitizes execution environment and doesn't forward all inherited environment variables. You can find list of variables that are always forwarded in `environ.scons` file. +- `--options optionfile.py` (default value `fbt_options.py`) - load a file with multiple configuration values +- `--extra-int-apps=app1,app2,appN` - force listed apps to be built as internal with the `firmware` target +- `--extra-ext-apps=app1,app2,appN` - force listed apps to be built as external with the `firmware_extapps` target +- `--proxy-env=VAR1,VAR2` - additional environment variables to expose to subprocesses spawned by `fbt`. By default, `fbt` sanitizes the execution environment and doesn't forward all inherited environment variables. You can find the list of variables that are always forwarded in the `environ.scons` file. +## Configuration -## Configuration - -Default configuration variables are set in the configuration file `fbt_options.py`. -Values set on command-line have higher precedence over configuration file. +Default configuration variables are set in the configuration file: `fbt_options.py`. +Values set in the command line have higher precedence over the configuration file. You can find out available options with `./fbt -h`. ### Firmware application set -You can create customized firmware builds by modifying the application list to be included in the build. Application presets are configured with the `FIRMWARE_APPS` option, which is a map(configuration_name:str -> application_list:tuple(str)). To specify application set to use in a build, set `FIRMWARE_APP_SET` to its name. +You can create customized firmware builds by modifying the list of applications to be included in the build. Application presets are configured with the `FIRMWARE_APPS` option, which is a `map(configuration_name:str -> application_list:tuple(str))`. To specify an application set to use in the build, set `FIRMWARE_APP_SET` to its name. For example, to build a firmware image with unit tests, run `./fbt FIRMWARE_APP_SET=unit_tests`. Check out `fbt_options.py` for details. diff --git a/documentation/file_formats/BadUsbScriptFormat.md b/documentation/file_formats/BadUsbScriptFormat.md index 321eff24..fa503874 100644 --- a/documentation/file_formats/BadUsbScriptFormat.md +++ b/documentation/file_formats/BadUsbScriptFormat.md @@ -1,16 +1,23 @@ # Command syntax -BadUsb app uses extended Duckyscript syntax. It is compatible with classic USB Rubber Ducky 1.0 scripts, but provides some additional commands and features, such as custom USB ID, ALT+Numpad input method, SYSRQ command and more fuctional keys. + +BadUsb app uses extended Duckyscript syntax. It is compatible with classic USB Rubber Ducky 1.0 scripts but provides some additional commands and features, such as custom USB ID, ALT+Numpad input method, SYSRQ command, and more functional keys. + # Script file format -BadUsb app can execute only text scrips from .txt files, no compilation is required. Both `\n` and `\r\n` line endings are supported. Empty lines are allowed. You can use spaces ore tabs for line indentation. + +BadUsb app can execute only text scrips from `.txt` files, no compilation is required. Both `\n` and `\r\n` line endings are supported. Empty lines are allowed. You can use spaces or tabs for line indentation. + # Command set + ## Comment line -Just a single comment line. All text after REM command will be ignored by interpreter + +Just a single comment line. The interpreter will ignore all text after the REM command. |Command|Parameters|Notes| |-|-|-| |REM|Comment text|| ## Delay -Pause script execution by defined time + +Pause script execution by a defined time. |Command|Parameters|Notes| |-|-|-| |DELAY|Delay value in ms|Single delay| @@ -18,35 +25,37 @@ Pause script execution by defined time |DEFAULTDELAY|Delay value in ms|Same as DEFAULT_DELAY| ## Special keys -|Command|Notes| -|-|-| -|DOWNARROW / DOWN|| -|LEFTARROW / LEFT|| -|RIGHTARROW / RIGHT|| -|UPARROW / UP|| -|ENTER|| -|DELETE|| -|BACKSPACE|| -|END|| -|HOME|| -|ESCAPE / ESC|| -|INSERT|| -|PAGEUP|| -|PAGEDOWN|| -|CAPSLOCK|| -|NUMLOCK|| -|SCROLLLOCK|| -|PRINTSCREEN|| -|BREAK|Pause/Break key| -|PAUSE|Pause/Break key| -|SPACE|| -|TAB|| -|MENU|Context menu key| -|APP|Same as MENU| -|Fx|F1-F12 keys| + +| Command | Notes | +| ------------------ | ---------------- | +| DOWNARROW / DOWN | | +| LEFTARROW / LEFT | | +| RIGHTARROW / RIGHT | | +| UPARROW / UP | | +| ENTER | | +| DELETE | | +| BACKSPACE | | +| END | | +| HOME | | +| ESCAPE / ESC | | +| INSERT | | +| PAGEUP | | +| PAGEDOWN | | +| CAPSLOCK | | +| NUMLOCK | | +| SCROLLLOCK | | +| PRINTSCREEN | | +| BREAK | Pause/Break key | +| PAUSE | Pause/Break key | +| SPACE | | +| TAB | | +| MENU | Context menu key | +| APP | Same as MENU | +| Fx | F1-F12 keys | ## Modifier keys -Can be combined with special key command or single character + +Can be combined with a special key command or a single character. |Command|Notes| |-|-| |CONTROL / CTRL|| @@ -58,35 +67,44 @@ Can be combined with special key command or single character |ALT-SHIFT|ALT+SHIFT| |ALT-GUI|ALT+WIN| |GUI-SHIFT|WIN+SHIFT| + ## String -|Command|Parameters|Notes| -|-|-|-| -|STRING|Text string|Print text string| + +| Command | Parameters | Notes | +| ------- | ----------- | ----------------- | +| STRING | Text string | Print text string | + ## Repeat -|Command|Parameters|Notes| -|-|-|-| -|REPEAT|Number of additional repeats|Repeat previous command| + +| Command | Parameters | Notes | +| ------- | ---------------------------- | ----------------------- | +| REPEAT | Number of additional repeats | Repeat previous command | + ## ALT+Numpad input -On Windows and some Linux systems you can print character by pressing ALT key and entering its code on numpad + +On Windows and some Linux systems, you can print characters by pressing `ALT` key and entering its code on Numpad. |Command|Parameters|Notes| |-|-|-| |ALTCHAR|Character code|Print single character| |ALTSTRING|Text string|Print text string using ALT+Numpad method| |ALTCODE|Text string|Same as ALTSTRING, presents in some Duckyscript implementations| + ## SysRq + Send [SysRq command](https://en.wikipedia.org/wiki/Magic_SysRq_key) |Command|Parameters|Notes| |-|-|-| |SYSRQ|Single character|| -## USB device ID -You can set custom ID of Flipper USB HID device. ID command should be in the **first line** of script, it is executed before script run. -|Command|Parameters|Notes| -|-|-|-| -|ID|VID:PID Manufacturer:Product|| +## USB device ID + +You can set the custom ID of the Flipper USB HID device. ID command should be in the **first line** of script, it is executed before script run. + +| Command | Parameters | Notes | +| ------- | ---------------------------- | ----- | +| ID | VID:PID Manufacturer:Product | | Example: `ID 1234:abcd Flipper Devices:Flipper Zero` -VID and PID are hex codes and are mandatory, Manufacturer and Product are text strings and are optional. - +VID and PID are hex codes and are mandatory. Manufacturer and Product are text strings and are optional. diff --git a/documentation/file_formats/InfraredFileFormats.md b/documentation/file_formats/InfraredFileFormats.md index a6d6c276..3c0acdcb 100644 --- a/documentation/file_formats/InfraredFileFormats.md +++ b/documentation/file_formats/InfraredFileFormats.md @@ -1,11 +1,12 @@ # Infrared Flipper File Formats ## Infrared Remote File Format + ### Example Filetype: IR signals file Version: 1 - # + # name: Button_1 type: parsed protocol: NECext @@ -25,48 +26,59 @@ command: 15 00 00 00 ### Description + Filename extension: `.ir` -This file format is used to store an infrared remote that consists of an arbitrary number of buttons. +This file format is used to store an infrared remote that consists of an arbitrary number of buttons. Each button is separated from others by a comment character (`#`) for better readability. -Known protocols are represented in the `parsed` form, whereas non-recognised signals may be saved and re-transmitted as `raw` data. +Known protocols are represented in the `parsed` form, whereas non-recognized signals may be saved and re-transmitted as `raw` data. #### Version history: + 1. Initial version. #### Format fields -| Name | Use | Type | Description | -| ---------- | ------- | ------ |------------ | -| name | both | string | Name of the button. Only printable ASCII characters are allowed. | -| type | both | string | Type of the signal. Must be `parsed` or `raw`. | -| protocol | parsed | string | Name of the infrared protocol. Refer to `ir` console command for the complete list of supported protocols. | -| address | parsed | hex | Payload address. Must be 4 bytes long. | -| command | parsed | hex | Payload command. Must be 4 bytes long. | -| frequency | raw | uint32 | Carrier frequency, in Hertz, usually 38000 Hz. | -| duty_cycle | raw | float | Carrier duty cycle, usually 0.33. | -| data | raw | uint32 | Raw signal timings, in microseconds between logic level changes. Individual elements must be space-separated. Maximum timings amount is 1024. | + +| Name | Use | Type | Description | +| ---------- | ------ | ------ | --------------------------------------------------------------------------------------------------------------------------------------------- | +| name | both | string | Name of the button. Only printable ASCII characters are allowed. | +| type | both | string | Type of the signal. Must be `parsed` or `raw`. | +| protocol | parsed | string | Name of the infrared protocol. Refer to `ir` console command for the complete list of supported protocols. | +| address | parsed | hex | Payload address. Must be 4 bytes long. | +| command | parsed | hex | Payload command. Must be 4 bytes long. | +| frequency | raw | uint32 | Carrier frequency, in Hertz, usually 38000 Hz. | +| duty_cycle | raw | float | Carrier duty cycle, usually 0.33. | +| data | raw | uint32 | Raw signal timings, in microseconds between logic level changes. Individual elements must be space-separated. Maximum timings amount is 1024. | ## Infrared Library File Format + ### Examples + - [TV Universal Library](/assets/resources/infrared/assets/tv.ir) - [A/C Universal Library](/assets/resources/infrared/assets/ac.ir) - [Audio Universal Library](/assets/resources/infrared/assets/audio.ir) ### Description + Filename extension: `.ir` This file format is used to store universal remote libraries. It is identical to the previous format, differing only in the `Filetype` field.\ -It also has predefined button names for each universal library type, so that the universal remote application could understand them. +It also has predefined button names for each universal library type, so that the universal remote application can understand them. See [Universal Remotes](/documentation/UniversalRemotes.md) for more information. ### Version history: + 1. Initial version. ## Infrared Test File Format + ### Examples + See [Infrared Unit Tests](/assets/unit_tests/infrared/) for various examples. + ### Description + Filename extension: `.irtest` This file format is used to store technical test data that is too large to keep directly in the firmware. @@ -78,34 +90,38 @@ Known protocols are represented in the `parsed_array` form, whereas raw data has Note: a single parsed signal must be represented as an array of size 1. ### Version history: + 1. Initial version. #### Format fields -| Name | Use | Type | Description | -| ---------- | ------------ | ------ |------------ | + +| Name | Use | Type | Description | +| ---------- | ------------ | ------ | ---------------------------------------------------------------- | | name | both | string | Name of the signal. Only printable ASCII characters are allowed. | -| type | both | string | Type of the signal. Must be `parsed_array` or `raw`. | -| count | parsed_array | uint32 | The number of parsed signals in an array. Must be at least 1. | -| protocol | parsed_array | string | Same as in previous formats. | -| address | parsed_array | hex | Ditto. | -| command | parsed_array | hex | Ditto. | -| repeat | parsed_array | bool | Indicates whether the signal is a repeated button press. | -| frequency | raw | uint32 | Same as in previous formats. | -| duty_cycle | raw | float | Ditto. | -| data | raw | uint32 | Ditto. | +| type | both | string | Type of the signal. Must be `parsed_array` or `raw`. | +| count | parsed_array | uint32 | The number of parsed signals in an array. Must be at least 1. | +| protocol | parsed_array | string | Same as in previous formats. | +| address | parsed_array | hex | Ditto. | +| command | parsed_array | hex | Ditto. | +| repeat | parsed_array | bool | Indicates whether the signal is a repeated button press. | +| frequency | raw | uint32 | Same as in previous formats. | +| duty_cycle | raw | float | Ditto. | +| data | raw | uint32 | Ditto. | #### Signal names -The signal names in an `.irtest` file folow a convention ``, where the name is one of: + +The signal names in an `.irtest` file follow a convention ``, where the name is one of: + - decoder_input - decoder_expected - encoder_decoder_input, -and the number is a sequential integer: 1, 2, 3...etc, which produces names like `decoder_input1`, `encoder_decoder_input3`, and so on. +and the number is a sequential integer: 1, 2, 3, etc., which produces names like `decoder_input1`, `encoder_decoder_input3`, and so on. -| Name | Type | Description | -| --------------------- | ------------ | ----------- | -| decoder_input | raw | A raw signal contaning the decoder input. Is also used as the expected encoder output. | -| decoder_expected | parsed_array | An array of parsed signals containing the expected decoder output. Is also used as the encoder input. | -| encoder_decoder_input | parsed_array | An array of parsed signals containing both the encoder-decoder input and expected output. | +| Name | Type | Description | +| --------------------- | ------------ | ----------------------------------------------------------------------------------------------------- | +| decoder_input | raw | A raw signal containing the decoder input. Also used as the expected encoder output. | +| decoder_expected | parsed_array | An array of parsed signals containing the expected decoder output. Also used as the encoder input. | +| encoder_decoder_input | parsed_array | An array of parsed signals containing both the encoder-decoder input and expected output. | See [Unit Tests](/documentation/UnitTests.md#infrared) for more info. diff --git a/documentation/file_formats/LfRfidFileFormat.md b/documentation/file_formats/LfRfidFileFormat.md index 715c49f6..5143d8bc 100644 --- a/documentation/file_formats/LfRfidFileFormat.md +++ b/documentation/file_formats/LfRfidFileFormat.md @@ -1,17 +1,19 @@ # LF RFID key file format ## Example + ``` Filetype: Flipper RFID key Version: 1 Key type: EM4100 Data: 01 23 45 67 89 ``` + ## Description Filename extension: `.rfid` -The file stores single RFID key of type defined by `Key type` parameter +The file stores a single RFID key of the type defined by the `Key type` parameter. ### Version history @@ -19,29 +21,29 @@ The file stores single RFID key of type defined by `Key type` parameter ### Format fields -|Name|Description| -|-|-| -|Key type|Key protocol type| -|Data|Key data (HEX values)| +| Name | Description | +| -------- | --------------------- | +| Key type | Key protocol type | +| Data | Key data (HEX values) | ### Supported key types -|Type|Full name| -|-|-| -|EM4100|EM-Micro EM4100| -|H10301|HID H10301| -|Idteck|IDTECK| -|Indala26|Motorola Indala26| -|IOProxXSF|Kantech IOProxXSF| -|AWID|AWID| -|FDX-A|FECAVA FDX-A| -|FDX-B|ISO FDX-B| -|HIDProx|Generic HIDProx| -|HIDExt|Generic HIDExt| -|Pyramid|Farpointe Pyramid| -|Viking|Viking| -|Jablotron|Jablotron| -|Paradox|Paradox| -|PAC/Stanley|PAC/Stanley| -|Keri|Keri| -|Gallagher|Gallagher| \ No newline at end of file +| Type | Full name | +| ----------- | ----------------- | +| EM4100 | EM-Micro EM4100 | +| H10301 | HID H10301 | +| Idteck | IDTECK | +| Indala26 | Motorola Indala26 | +| IOProxXSF | Kantech IOProxXSF | +| AWID | AWID | +| FDX-A | FECAVA FDX-A | +| FDX-B | ISO FDX-B | +| HIDProx | Generic HIDProx | +| HIDExt | Generic HIDExt | +| Pyramid | Farpointe Pyramid | +| Viking | Viking | +| Jablotron | Jablotron | +| Paradox | Paradox | +| PAC/Stanley | PAC/Stanley | +| Keri | Keri | +| Gallagher | Gallagher | diff --git a/documentation/file_formats/NfcFileFormats.md b/documentation/file_formats/NfcFileFormats.md index 90dcaea1..78c6420e 100644 --- a/documentation/file_formats/NfcFileFormats.md +++ b/documentation/file_formats/NfcFileFormats.md @@ -19,9 +19,9 @@ This file format is used to store the UID, SAK and ATQA of a NFC-A device. It do Version differences: - 1. Initial version, deprecated - 2. LSB ATQA (e.g. 4400 instead of 0044) - 3. MSB ATQA (current version) +1. Initial version, deprecated +2. LSB ATQA (e.g. 4400 instead of 0044) +3. MSB ATQA (current version) UID can be either 4 or 7 bytes long. ATQA is 2 bytes long. SAK is 1 byte long. @@ -68,13 +68,13 @@ This file format is used to store the UID, SAK and ATQA of a Mifare Ultralight/N The "Signature" field contains the reply of the tag to the READ_SIG command. More on that can be found here: (page 31) -The "Mifare version" field is not related to the file format version, but to the Mifare Ultralight version. It contains the responce of the tag to the GET_VERSION command. More on that can be found here: (page 21) +The "Mifare version" field is not related to the file format version but to the Mifare Ultralight version. It contains the response of the tag to the GET_VERSION command. More on that can be found here: (page 21) -Other fields are the direct representation of the card's internal state, more on them can be found in the same datasheet. +Other fields are the direct representation of the card's internal state. Learn more about them in the same datasheet. Version differences: - 1. Current version +1. Current version ## Mifare Classic @@ -126,7 +126,7 @@ This file format is used to store the NFC-A and Mifare Classic specific data of Version differences: - 1. Initial version, has Key A and Key B masks instead of marking unknown data with '??'. +1. Initial version, has Key A and Key B masks instead of marking unknown data with '??'. Example: @@ -137,8 +137,8 @@ Example: Key B map: 000000000000FFFF # Mifare Classic blocks ... - - 2. Current version + +2. Current version ## Mifare DESFire @@ -200,7 +200,7 @@ This file format is used to store the NFC-A and Mifare DESFire specific data of hf mfdes write --aid 123456 --fid 01 -d 1337 Version differences: - None, there are no versions yet. +None, there are no versions yet. ## Mifare Classic Dictionary @@ -252,4 +252,4 @@ This file stores a list of EMV currency codes, country codes, or AIDs and their Version differences: - 1. Initial version +1. Initial version diff --git a/documentation/file_formats/SubGhzFileFormats.md b/documentation/file_formats/SubGhzFileFormats.md index ae80e6e0..26863f56 100644 --- a/documentation/file_formats/SubGhzFileFormats.md +++ b/documentation/file_formats/SubGhzFileFormats.md @@ -2,7 +2,7 @@ ## `.sub` File Format -Flipper uses `.sub` files to store SubGhz transmissions. They are text files in Flipper File Format. `.sub` files can contain either a SubGhz Key with a certain protocol or SubGhz RAW data. +Flipper uses `.sub` files to store SubGhz transmissions. These are text files in Flipper File Format. `.sub` files can contain either a SubGhz Key with a certain protocol or SubGhz RAW data. A `.sub` files consist of 3 parts: @@ -10,36 +10,36 @@ A `.sub` files consist of 3 parts: - **preset information**: preset type and, in case of a custom preset, transceiver configuration data - **protocol and its data**: contains protocol name and its specific data, such as key, bit length, etc., or RAW data -Flipper's SubGhz subsystem uses presets to configure radio transceiver. Presets are used to configure modulation, bandwidth, filters, etc. There are several presets available in stock firmware, and there is a way to create custom presets. See [SubGhz Presets](#adding-a-custom-preset) for more details. +Flipper's SubGhz subsystem uses presets to configure the radio transceiver. Presets are used to configure modulation, bandwidth, filters, etc. There are several presets available in stock firmware, and there is a way to create custom presets. See [SubGhz Presets](#adding-a-custom-preset) for more details. -## Header Format +## Header format Header is a mandatory part of `.sub` file. It contains file type, version, and frequency. -| Field | Type | Description | -| --- | --- | --- | -| `Filetype` | string | Filetype of subghz file format, must be `Flipper SubGhz Key File` | -| `Version` | uint | Version of subghz file format, current version is 1 | -| `Frequency` | uint | Frequency in Hertz | +| Field | Type | Description | +| ----------- | ------ | ----------------------------------------------------------------- | +| `Filetype` | string | Filetype of subghz file format, must be `Flipper SubGhz Key File` | +| `Version` | uint | Version of subghz file format, current version is 1 | +| `Frequency` | uint | Frequency in Hertz | -## Preset Information +## Preset information -Preset information is a mandatory part of `.sub` file. It contains preset type and, in case of custom preset, transceiver configuration data. +Preset information is a mandatory part for `.sub` files. It contains preset type and, in case of custom preset, transceiver configuration data. -When using one of the standard presets, only `Preset` field is required. When using custom preset, `Custom_preset_module` and `Custom_preset_data` fields are required. +When using one of the standard presets, only `Preset` field is required. When using a custom preset, `Custom_preset_module` and `Custom_preset_data` fields are required. -| Field | Description | -| --- | --- | -| `Preset` | Radio preset name (configures modulation, bandwidth, filters, etc.). When using a custom preset, must be `FuriHalSubGhzPresetCustom` | -| `Custom_preset_module` | Transceiver identifier, `CC1101` for Flipper Zero | -| `Custom_preset_data` | Transceiver configuration data | +| Field | Description | +| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | +| `Preset` | Radio preset name (configures modulation, bandwidth, filters, etc.). When using a custom preset, must be `FuriHalSubGhzPresetCustom` | +| `Custom_preset_module` | Transceiver identifier, `CC1101` for Flipper Zero | +| `Custom_preset_data` | Transceiver configuration data | Built-in presets: -- `FuriHalSubGhzPresetOok270Async` - On/Off Keying, 270kHz bandwidth, async(IO throw GP0) -- `FuriHalSubGhzPresetOok650Async` - On/Off Keying, 650kHz bandwidth, async(IO throw GP0) -- `FuriHalSubGhzPreset2FSKDev238Async` - 2 Frequency Shift Keying, deviation 2kHz, 270kHz bandwidth, async(IO throw GP0) -- `FuriHalSubGhzPreset2FSKDev476Async` - 2 Frequency Shift Keying, deviation 47kHz, 270kHz bandwidth, async(IO throw GP0) +- `FuriHalSubGhzPresetOok270Async` — On/Off Keying, 270kHz bandwidth, async(IO throw GP0) +- `FuriHalSubGhzPresetOok650Async` — On/Off Keying, 650kHz bandwidth, async(IO throw GP0) +- `FuriHalSubGhzPreset2FSKDev238Async` — 2 Frequency Shift Keying, deviation 2kHz, 270kHz bandwidth, async(IO throw GP0) +- `FuriHalSubGhzPreset2FSKDev476Async` — 2 Frequency Shift Keying, deviation 47kHz, 270kHz bandwidth, async(IO throw GP0) ### Transceiver Configuration Data @@ -50,7 +50,7 @@ Transceiver configuration data is a string of bytes, encoded in hex format, sepa - 00 00: marks register block end, - `ZZ ZZ ZZ ZZ ZZ ZZ ZZ ZZ`: 8 byte PA table (Power amplifier ramp table). -More details can be found in [CC1101 datasheet](https://www.ti.com/lit/ds/symlink/cc1101.pdf) and `furi_hal_subghz` code. +You can find more details in the [CC1101 datasheet](https://www.ti.com/lit/ds/symlink/cc1101.pdf) and `furi_hal_subghz` code. ## File Data @@ -59,9 +59,9 @@ More details can be found in [CC1101 datasheet](https://www.ti.com/lit/ds/symlin ### Key Files `.sub` files with key data files contain protocol name and its specific data, such as key value, bit length, etc. -Check out protocol registry for full list of supported protocol names. +Check out the protocol registry for the full list of supported protocol names. -Example of key data block in Princeton format: +Example of a key data block in Princeton format: ``` ... @@ -74,7 +74,7 @@ TE: 400 Protocol-specific fields in this example: | Field | Description | -| --- | --- | +| ----- | --------------------------------- | | `Bit` | Princeton payload length, in bits | | `Key` | Princeton payload data | | `TE` | Princeton quantization interval | @@ -83,25 +83,23 @@ This file may contain additional fields, more details on available fields can be ### RAW Files -RAW `.sub` files contain raw signal data that is not processed through protocol-specific decoding. These files are useful for testing purposes, or for sending data that is not supported by any known protocol. +RAW `.sub` files contain raw signal data that is not processed through protocol-specific decoding. These files are useful for testing or sending data not supported by any known protocol. For RAW files, 2 fields are required: - * `Protocol`, must be `RAW` - * `RAW_Data`, contains an array of timings, specified in micro seconds. Values must be non-zero, start with a positive number, and interleaved (change sign with each value). Up to 512 values per line. Can be specified multiple times to store multiple lines of data. +- `Protocol`, must be `RAW` +- `RAW_Data`, contains an array of timings, specified in microseconds Values must be non-zero, start with a positive number, and interleaved (change sign with each value). Up to 512 values per line. Can be specified multiple times to store multiple lines of data. Example of RAW data: Protocol: RAW RAW_Data: 29262 361 -68 2635 -66 24113 -66 11 ... +Long payload not fitting into internal memory buffer and consisting of short duration timings (< 10us) may not be read fast enough from the SD card. That might cause the signal transmission to stop before reaching the end of the payload. Ensure that your SD Card has good performance before transmitting long or complex RAW payloads. -Long payload not fitting into internal memory buffer and consisting of short duration timings (<10us) may not be read fast enough from SD Card. That might cause signal transmission to stop before reaching the end of the payload. Ensure that your SD Card has good performance before transmitting long or complex RAW payloads. +## File examples - -## File Examples - -### Key File, Standard Preset +### Key file, standard preset Filetype: Flipper SubGhz Key File Version: 1 @@ -112,8 +110,7 @@ Long payload not fitting into internal memory buffer and consisting of short dur Key: 00 00 00 00 00 95 D5 D4 TE: 400 - -### Key File, Custom Preset +### Key file, custom preset Filetype: Flipper SubGhz Key File Version: 1 @@ -126,7 +123,7 @@ Long payload not fitting into internal memory buffer and consisting of short dur Key: 00 00 00 00 00 95 D5 D4 TE: 400 -### RAW File, Standard Preset +### RAW file, standard preset Filetype: Flipper SubGhz RAW File Version: 1 @@ -137,7 +134,7 @@ Long payload not fitting into internal memory buffer and consisting of short dur RAW_Data: -424 205 -412 159 -412 381 -240 181 ... RAW_Data: -1448 361 -17056 131 -134 233 -1462 131 -166 953 -100 ... -### RAW File, Custom Preset +### RAW file, custom preset Filetype: Flipper SubGhz RAW File Version: 1 @@ -150,26 +147,26 @@ Long payload not fitting into internal memory buffer and consisting of short dur RAW_Data: -424 205 -412 159 -412 381 -240 181 ... RAW_Data: -1448 361 -17056 131 -134 233 -1462 131 -166 953 -100 ... -# SubGhz Configuration Files +# SubGhz configuration files SubGhz application provides support for adding extra radio presets and additional keys for decoding transmissions in certain protocols. -## SubGhz `keeloq_mfcodes_user` File +## SubGhz `keeloq_mfcodes_user` file This file contains additional manufacturer keys for Keeloq protocol. It is used to decode Keeloq transmissions. This file is loaded at subghz application start and is located at path `/ext/subghz/assets/keeloq_mfcodes_user`. -### File Format +### File format File contains a header and a list of manufacturer keys. File header format: -| Field | Type | Description | -| --- | | --- | -| `Filetype` | string | SubGhz Keystore file format, always `Flipper SubGhz Keystore File` | -| `Version` | uint | File format version, 0 | -| `Encryption` | uint | File encryption: for user-provided file, set to 0 (disabled) | +| Field | Type | Description | +| ------------ | ------ | ------------------------------------------------------------------ | +| `Filetype` | string | SubGhz Keystore file format, always `Flipper SubGhz Keystore File` | +| `Version` | uint | File format version, 0 | +| `Encryption` | uint | File encryption: for user-provided file, set to 0 (disabled) | Following the header, file contains a list of user-provided manufacture keys, one key per line. For each key, a name and encryption method must be specified, according to comment in file header. More information can be found in keeloq decoder source code. @@ -186,7 +183,7 @@ For each key, a name and encryption method must be specified, according to comme # - 2 - Normal_Learning # - 3 - Secure_Learning # - 4 - Magic_xor_type1 Learning - # + # # NAME - name (string without spaces) max 64 characters long Filetype: Flipper SubGhz Keystore File Version: 0 @@ -194,45 +191,44 @@ For each key, a name and encryption method must be specified, according to comme AABBCCDDEEFFAABB:1:Test1 AABBCCDDEEFFAABB:1:Test2 +## SubGhz `setting_user` file -## SubGhz `setting_user` File +This file contains additional radio presets and frequencies for SubGhz application. It is used to add new presets and frequencies for existing presets. This file is being loaded on subghz application start and is located at path `/ext/subghz/assets/setting_user`. -This file contains additional radio presets and frequencies for SubGhz application. It is used to add new presets and frequencies for existing presets. This file is be loaded on subghz application start and is located at path `/ext/subghz/assets/setting_user`. - -### File Format +### File format File contains a header, basic options, and optional lists of presets and frequencies. -Header must contain following fields: +Header must contain the following fields: - `Filetype`: SubGhz setting file format, must be `Flipper SubGhz Setting File`. - `Version`: file format version, current is `1`. -#### Basic Settings +#### Basic settings - `Add_standard_frequencies`: bool, flag indicating whether to load standard frequencies shipped with firmware. If set to `false`, only frequencies specified in this file will be used. - `Default_frequency`: uint, default frequency used in SubGhz application. -#### Adding More Frequencies +#### Adding more frequencies - `Frequency`: uint — additional frequency for the subghz application frequency list. Used in Read and Read RAW. You can specify multiple frequencies, one per line. -#### Adding More Hopper Frequencies +#### Adding more hopper frequencies - `Hopper_frequency`: uint — additional frequency for subghz application frequency hopping. Used in Frequency Analyzer. You can specify multiple frequencies, one per line. -Repeating same frequency will cause Flipper to listen on this frequency more often. +Repeating the same frequency will cause Flipper to listen to this frequency more often. #### Adding a Custom Preset You can have as many presets as you want. Presets are embedded into `.sub` files, so another Flipper can load them directly from that file. -Each preset is defined by following fields: +Each preset is defined by the following fields: -| Field | Description | -| --- | --- | -| `Custom_preset_name` | string, preset name that will be shown in SubGHz application | -| `Custom_preset_module` | string, transceiver identifier. Set to `CC1101` for Flipper Zero | -| `Custom_preset_data` | transceiver configuration data. See [Transceiver Configuration Data](#transceiver-configuration-data) for details. | +| Field | Description | +| ---------------------- | ------------------------------------------------------------------------------------------------------------------ | +| `Custom_preset_name` | string, preset name that will be shown in SubGHz application | +| `Custom_preset_module` | string, transceiver identifier. Set to `CC1101` for Flipper Zero | +| `Custom_preset_data` | transceiver configuration data. See [Transceiver Configuration Data](#transceiver-configuration-data) for details. | ### Example @@ -252,7 +248,7 @@ Frequency: 300000000 Frequency: 310000000 Frequency: 320000000 -# Frequencies used for hopping mode (keep this list small or flipper will miss signal) +# Frequencies used for hopping mode (keep this list small or Flipper will miss the signal) Hopper_frequency: 300000000 Hopper_frequency: 310000000 Hopper_frequency: 310000000 diff --git a/documentation/file_formats/iButtonFileFormat.md b/documentation/file_formats/iButtonFileFormat.md index a5d41b49..c0458619 100644 --- a/documentation/file_formats/iButtonFileFormat.md +++ b/documentation/file_formats/iButtonFileFormat.md @@ -1,6 +1,7 @@ # iButton key file format ## Example + ``` Filetype: Flipper iButton key Version: 1 @@ -9,11 +10,12 @@ Key type: Dallas # Data size for Cyfral is 2, for Metakom is 4, for Dallas is 8 Data: 12 34 56 78 9A BC DE F0 ``` + ## Description Filename extension: `.ibtn` -The file stores single iButton key of type defined by `Key type` parameter +The file stores a single iButton key of the type defined by the `Key type` parameter. ### Version history @@ -21,7 +23,7 @@ The file stores single iButton key of type defined by `Key type` parameter ### Format fields -|Name|Description| -|-|-| -|Key type|Currently supported: Cyfral, Dallas, Metakom| -|Data|Key data (HEX values)| \ No newline at end of file +| Name | Description | +| -------- | -------------------------------------------- | +| Key type | Currently supported: Cyfral, Dallas, Metakom | +| Data | Key data (HEX values) | diff --git a/firmware/ReadMe.md b/firmware/ReadMe.md index d2baedfd..c39f8c4a 100644 --- a/firmware/ReadMe.md +++ b/firmware/ReadMe.md @@ -14,9 +14,9 @@ What does it do? |-----------|-------------------|-----------------------|-----------------------| | f7 | 0x08000000 | L+Back, release both | L+Back, release Back | -Also there is a "hardware" ST bootloader combo available even on a bricked or empty device: L+Ok+Back, release Back, Left. +Also, there is a "hardware" ST bootloader combo available even on a bricked or empty device: L+Ok+Back, release Back, Left. Target independent code and headers in `target/include` folders. More details in `documentation/KeyCombo.md` # Building -Check out `documentation/fbt.md` on how to build and flash firmware. \ No newline at end of file +Check out `documentation/fbt.md` on how to build and flash firmware. diff --git a/firmware/targets/f7/Src/update.c b/firmware/targets/f7/Src/update.c index 722a7b61..a68a8b7a 100644 --- a/firmware/targets/f7/Src/update.c +++ b/firmware/targets/f7/Src/update.c @@ -149,7 +149,7 @@ static UpdateManifest* flipper_update_process_manifest(const FuriString* manifes do { uint16_t size_read = 0; - if(f_read(&file, manifest_data + bytes_read, MAX_READ, &size_read) != FR_OK) { + if(f_read(&file, manifest_data + bytes_read, MAX_READ, &size_read) != FR_OK) { //-V769 break; } bytes_read += size_read; diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 8503a838..ac7d1a0c 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,11.2,, +Version,+,11.6,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -174,6 +174,7 @@ Header,+,lib/subghz/transmitter.h,, Header,+,lib/toolbox/args.h,, Header,+,lib/toolbox/crc32_calc.h,, Header,+,lib/toolbox/dir_walk.h,, +Header,+,lib/toolbox/float_tools.h,, Header,+,lib/toolbox/hmac_sha256.h,, Header,+,lib/toolbox/manchester_decoder.h,, Header,+,lib/toolbox/manchester_encoder.h,, @@ -562,8 +563,8 @@ Function,+,ble_glue_wait_for_c2_start,_Bool,int32_t Function,-,bsearch,void*,"const void*, const void*, size_t, size_t, __compar_fn_t" Function,+,bt_disconnect,void,Bt* Function,+,bt_forget_bonded_devices,void,Bt* -Function,+,bt_keys_storage_set_storage_path,void,"Bt*, const char*" Function,+,bt_keys_storage_set_default_path,void,Bt* +Function,+,bt_keys_storage_set_storage_path,void,"Bt*, const char*" Function,+,bt_set_profile,_Bool,"Bt*, BtProfile" Function,+,bt_set_status_changed_callback,void,"Bt*, BtStatusChangedCallback, void*" Function,+,buffered_file_stream_alloc,Stream*,Storage* @@ -593,6 +594,7 @@ Function,+,byte_input_set_result_callback,void,"ByteInput*, ByteInputCallback, B Function,-,bzero,void,"void*, size_t" Function,-,calloc,void*,"size_t, size_t" Function,+,canvas_clear,void,Canvas* +Function,+,canvas_commit,void,Canvas* Function,+,canvas_current_font_height,uint8_t,Canvas* Function,+,canvas_draw_bitmap,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, const uint8_t*" Function,+,canvas_draw_box,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t" @@ -614,6 +616,7 @@ Function,+,canvas_get_font_params,CanvasFontParameters*,"Canvas*, Font" Function,+,canvas_glyph_width,uint8_t,"Canvas*, char" Function,+,canvas_height,uint8_t,Canvas* Function,+,canvas_invert_color,void,Canvas* +Function,+,canvas_reset,void,Canvas* Function,+,canvas_set_bitmap_mode,void,"Canvas*, _Bool" Function,+,canvas_set_color,void,"Canvas*, Color" Function,+,canvas_set_font,void,"Canvas*, Font" @@ -754,6 +757,7 @@ Function,+,elements_multiline_text,void,"Canvas*, uint8_t, uint8_t, const char*" Function,+,elements_multiline_text_aligned,void,"Canvas*, uint8_t, uint8_t, Align, Align, const char*" Function,+,elements_multiline_text_framed,void,"Canvas*, uint8_t, uint8_t, const char*" Function,+,elements_progress_bar,void,"Canvas*, uint8_t, uint8_t, uint8_t, float" +Function,+,elements_progress_bar_with_text,void,"Canvas*, uint8_t, uint8_t, uint8_t, float, const char*" Function,+,elements_scrollable_text_line,void,"Canvas*, uint8_t, uint8_t, uint8_t, FuriString*, size_t, _Bool" Function,+,elements_scrollbar,void,"Canvas*, uint16_t, uint16_t" Function,+,elements_scrollbar_pos,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint16_t, uint16_t" @@ -905,6 +909,7 @@ Function,+,flipper_format_write_int32,_Bool,"FlipperFormat*, const char*, const Function,+,flipper_format_write_string,_Bool,"FlipperFormat*, const char*, FuriString*" Function,+,flipper_format_write_string_cstr,_Bool,"FlipperFormat*, const char*, const char*" Function,+,flipper_format_write_uint32,_Bool,"FlipperFormat*, const char*, const uint32_t*, const uint16_t" +Function,+,float_is_equal,_Bool,"float, float" Function,-,flockfile,void,FILE* Function,-,floor,double,double Function,-,floorf,float,float @@ -1553,6 +1558,8 @@ Function,-,gmtime,tm*,const time_t* Function,-,gmtime_r,tm*,"const time_t*, tm*" Function,+,gui_add_framebuffer_callback,void,"Gui*, GuiCanvasCommitCallback, void*" Function,+,gui_add_view_port,void,"Gui*, ViewPort*, GuiLayer" +Function,+,gui_direct_draw_acquire,Canvas*,Gui* +Function,+,gui_direct_draw_release,void,Gui* Function,+,gui_get_framebuffer_size,size_t,Gui* Function,+,gui_remove_framebuffer_callback,void,"Gui*, GuiCanvasCommitCallback, void*" Function,+,gui_remove_view_port,void,"Gui*, ViewPort*" @@ -1853,6 +1860,7 @@ Function,+,menu_set_selected_item,void,"Menu*, uint32_t" Function,-,mf_classic_auth_attempt,_Bool,"FuriHalNfcTxRxContext*, MfClassicAuthContext*, uint64_t" Function,-,mf_classic_auth_init_context,void,"MfClassicAuthContext*, uint8_t" Function,-,mf_classic_authenticate,_Bool,"FuriHalNfcTxRxContext*, uint8_t, uint64_t, MfClassicKey" +Function,-,mf_classic_authenticate_skip_activate,_Bool,"FuriHalNfcTxRxContext*, uint8_t, uint64_t, MfClassicKey, _Bool, uint32_t" Function,-,mf_classic_check_card_type,_Bool,"uint8_t, uint8_t, uint8_t" Function,-,mf_classic_dict_add_key,_Bool,"MfClassicDict*, uint8_t*" Function,-,mf_classic_dict_add_key_str,_Bool,"MfClassicDict*, FuriString*" @@ -2472,6 +2480,7 @@ Function,+,stream_read_line,_Bool,"Stream*, FuriString*" Function,+,stream_rewind,_Bool,Stream* Function,+,stream_save_to_file,size_t,"Stream*, Storage*, const char*, FS_OpenMode" Function,+,stream_seek,_Bool,"Stream*, int32_t, StreamOffset" +Function,+,stream_seek_to_char,_Bool,"Stream*, char, StreamDirection" Function,+,stream_size,size_t,Stream* Function,+,stream_split,_Bool,"Stream*, Stream*, Stream*" Function,+,stream_tell,size_t,Stream* diff --git a/firmware/targets/f7/ble_glue/ble_glue.c b/firmware/targets/f7/ble_glue/ble_glue.c index b3752f17..83562c73 100644 --- a/firmware/targets/f7/ble_glue/ble_glue.c +++ b/firmware/targets/f7/ble_glue/ble_glue.c @@ -456,17 +456,15 @@ BleGlueCommandResult ble_glue_fus_get_status() { BleGlueCommandResult ble_glue_fus_wait_operation() { furi_check(ble_glue->c2_info.mode == BleGlueC2ModeFUS); - bool wip; - do { - BleGlueCommandResult fus_status = ble_glue_fus_get_status(); - if(fus_status == BleGlueCommandResultError) { - return BleGlueCommandResultError; - } - wip = fus_status == BleGlueCommandResultOperationOngoing; - if(wip) { - furi_delay_ms(20); - } - } while(wip); - return BleGlueCommandResultOK; + while(true) { + BleGlueCommandResult fus_status = ble_glue_fus_get_status(); + if(fus_status == BleGlueCommandResultOperationOngoing) { + furi_delay_ms(20); + } else if(fus_status == BleGlueCommandResultError) { + return BleGlueCommandResultError; + } else { + return BleGlueCommandResultOK; + } + } } diff --git a/firmware/targets/f7/ble_glue/gap.c b/firmware/targets/f7/ble_glue/gap.c index 3e29527e..8ef037d6 100644 --- a/firmware/targets/f7/ble_glue/gap.c +++ b/firmware/targets/f7/ble_glue/gap.c @@ -227,7 +227,7 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { case EVT_BLUE_GAP_NUMERIC_COMPARISON_VALUE: { uint32_t pin = ((aci_gap_numeric_comparison_value_event_rp0*)(blue_evt->data))->Numeric_Value; - FURI_LOG_I(TAG, "Verify numeric comparison: %06ld", pin); + FURI_LOG_I(TAG, "Verify numeric comparison: %06lu", pin); GapEvent event = {.type = GapEventTypePinCodeVerify, .data.pin_code = pin}; bool result = gap->on_event_cb(event, gap->context); aci_gap_numeric_comparison_value_confirm_yesno(gap->service.connection_handle, result); @@ -245,7 +245,7 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { } else { FURI_LOG_I(TAG, "Pairing complete"); GapEvent event = {.type = GapEventTypeConnected}; - gap->on_event_cb(event, gap->context); + gap->on_event_cb(event, gap->context); //-V595 } break; diff --git a/firmware/targets/f7/ble_glue/hid_service.c b/firmware/targets/f7/ble_glue/hid_service.c index d0ca9685..47d242d4 100644 --- a/firmware/targets/f7/ble_glue/hid_service.c +++ b/firmware/targets/f7/ble_glue/hid_service.c @@ -87,7 +87,7 @@ void hid_svc_start() { #if(HID_SVC_REPORT_COUNT != 0) for(uint8_t i = 0; i < HID_SVC_REPORT_COUNT; i++) { - if(i < HID_SVC_INPUT_REPORT_COUNT) { + if(i < HID_SVC_INPUT_REPORT_COUNT) { //-V547 uint8_t buf[2] = {i + 1, 1}; // 1 input char_uuid.Char_UUID_16 = REPORT_CHAR_UUID; status = aci_gatt_add_char( diff --git a/firmware/targets/f7/ble_glue/hw_ipcc.c b/firmware/targets/f7/ble_glue/hw_ipcc.c index 64dd9ef9..7c84df09 100644 --- a/firmware/targets/f7/ble_glue/hw_ipcc.c +++ b/firmware/targets/f7/ble_glue/hw_ipcc.c @@ -24,9 +24,9 @@ /* Global variables ---------------------------------------------------------*/ /* Private defines -----------------------------------------------------------*/ #define HW_IPCC_TX_PENDING(channel) \ - (!(LL_C1_IPCC_IsActiveFlag_CHx(IPCC, channel))) && (((~(IPCC->C1MR)) & (channel << 16U))) + (!(LL_C1_IPCC_IsActiveFlag_CHx(IPCC, channel))) && (((~(IPCC->C1MR)) & ((channel) << 16U))) #define HW_IPCC_RX_PENDING(channel) \ - (LL_C2_IPCC_IsActiveFlag_CHx(IPCC, channel)) && (((~(IPCC->C1MR)) & (channel << 0U))) + (LL_C2_IPCC_IsActiveFlag_CHx(IPCC, channel)) && (((~(IPCC->C1MR)) & ((channel) << 0U))) /* Private macros ------------------------------------------------------------*/ /* Private typedef -----------------------------------------------------------*/ diff --git a/firmware/targets/f7/fatfs/sector_cache.c b/firmware/targets/f7/fatfs/sector_cache.c index 5a4f1b97..d23c1d5a 100644 --- a/firmware/targets/f7/fatfs/sector_cache.c +++ b/firmware/targets/f7/fatfs/sector_cache.c @@ -8,6 +8,7 @@ #define SECTOR_SIZE 512 #define N_SECTORS 8 +#define TAG "SDCache" typedef struct { uint32_t itr; @@ -19,14 +20,15 @@ static SectorCache* cache = NULL; void sector_cache_init() { if(cache == NULL) { - cache = furi_hal_memory_alloc(sizeof(SectorCache)); + // TODO: tuneup allocation order, to place cache in mem pool (MEM2) + cache = memmgr_alloc_from_pool(sizeof(SectorCache)); } if(cache != NULL) { - FURI_LOG_I("SectorCache", "Initializing sector cache"); + FURI_LOG_I(TAG, "Init"); memset(cache, 0, sizeof(SectorCache)); } else { - FURI_LOG_E("SectorCache", "Cannot enable sector cache"); + FURI_LOG_E(TAG, "Init failed"); } } diff --git a/firmware/targets/f7/fatfs/stm32_adafruit_sd.c b/firmware/targets/f7/fatfs/stm32_adafruit_sd.c index 07cae31f..b9b65f06 100644 --- a/firmware/targets/f7/fatfs/stm32_adafruit_sd.c +++ b/firmware/targets/f7/fatfs/stm32_adafruit_sd.c @@ -405,9 +405,9 @@ uint8_t BSP_SD_GetCardInfo(SD_CardInfo* pCardInfo) { pCardInfo->LogBlockNbr = (pCardInfo->CardCapacity) / (pCardInfo->LogBlockSize); } else { pCardInfo->CardCapacity = (pCardInfo->Csd.version.v1.DeviceSize + 1); - pCardInfo->CardCapacity *= (1 << (pCardInfo->Csd.version.v1.DeviceSizeMul + 2)); + pCardInfo->CardCapacity *= (1UL << (pCardInfo->Csd.version.v1.DeviceSizeMul + 2)); pCardInfo->LogBlockSize = 512; - pCardInfo->CardBlockSize = 1 << (pCardInfo->Csd.RdBlockLen); + pCardInfo->CardBlockSize = 1UL << (pCardInfo->Csd.RdBlockLen); pCardInfo->CardCapacity *= pCardInfo->CardBlockSize; pCardInfo->LogBlockNbr = (pCardInfo->CardCapacity) / (pCardInfo->LogBlockSize); } @@ -982,7 +982,8 @@ uint8_t SD_GoIdleState(void) { SD_IO_WriteByte(SD_DUMMY_BYTE); /* Send ACMD41 (SD_CMD_SD_APP_OP_COND) to initialize SDHC or SDXC cards: R1 response (0x00: no errors) */ - response = SD_SendCmd(SD_CMD_SD_APP_OP_COND, 0x00000000, 0xFF, SD_ANSWER_R1_EXPECTED); + response = //-V519 + SD_SendCmd(SD_CMD_SD_APP_OP_COND, 0x00000000, 0xFF, SD_ANSWER_R1_EXPECTED); SD_IO_CSState(1); SD_IO_WriteByte(SD_DUMMY_BYTE); if(counter >= SD_MAX_TRY) { @@ -1001,7 +1002,8 @@ uint8_t SD_GoIdleState(void) { SD_IO_WriteByte(SD_DUMMY_BYTE); /* Send ACMD41 (SD_CMD_SD_APP_OP_COND) to initialize SDHC or SDXC cards: R1 response (0x00: no errors) */ - response = SD_SendCmd(SD_CMD_SD_APP_OP_COND, 0x40000000, 0xFF, SD_ANSWER_R1_EXPECTED); + response = //-V519 + SD_SendCmd(SD_CMD_SD_APP_OP_COND, 0x40000000, 0xFF, SD_ANSWER_R1_EXPECTED); SD_IO_CSState(1); SD_IO_WriteByte(SD_DUMMY_BYTE); if(counter >= SD_MAX_TRY) { diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt.c b/firmware/targets/f7/furi_hal/furi_hal_bt.c index fc1c25c7..0857fe4e 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_bt.c +++ b/firmware/targets/f7/furi_hal/furi_hal_bt.c @@ -415,7 +415,7 @@ float furi_hal_bt_get_rssi() { val += 6.0; rssi >>= 1; } - val += (417 * rssi + 18080) >> 10; + val += (float)((417 * rssi + 18080) >> 10); } return val; } diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c b/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c index 22415199..ab3855f4 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c +++ b/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c @@ -216,7 +216,7 @@ bool furi_hal_bt_hid_kb_release_all() { bool furi_hal_bt_hid_consumer_key_press(uint16_t button) { furi_assert(consumer_report); - for(uint8_t i = 0; i < FURI_HAL_BT_HID_CONSUMER_MAX_KEYS; i++) { + for(uint8_t i = 0; i < FURI_HAL_BT_HID_CONSUMER_MAX_KEYS; i++) { //-V1008 if(consumer_report->key[i] == 0) { consumer_report->key[i] = button; break; @@ -228,7 +228,7 @@ bool furi_hal_bt_hid_consumer_key_press(uint16_t button) { bool furi_hal_bt_hid_consumer_key_release(uint16_t button) { furi_assert(consumer_report); - for(uint8_t i = 0; i < FURI_HAL_BT_HID_CONSUMER_MAX_KEYS; i++) { + for(uint8_t i = 0; i < FURI_HAL_BT_HID_CONSUMER_MAX_KEYS; i++) { //-V1008 if(consumer_report->key[i] == button) { consumer_report->key[i] = 0; break; @@ -240,7 +240,7 @@ bool furi_hal_bt_hid_consumer_key_release(uint16_t button) { bool furi_hal_bt_hid_consumer_key_release_all() { furi_assert(consumer_report); - for(uint8_t i = 0; i < FURI_HAL_BT_HID_CONSUMER_MAX_KEYS; i++) { + for(uint8_t i = 0; i < FURI_HAL_BT_HID_CONSUMER_MAX_KEYS; i++) { //-V1008 consumer_report->key[i] = 0; } return hid_svc_update_input_report( diff --git a/firmware/targets/f7/furi_hal/furi_hal_flash.c b/firmware/targets/f7/furi_hal/furi_hal_flash.c index e49cd5f2..fc021d96 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_flash.c +++ b/firmware/targets/f7/furi_hal/furi_hal_flash.c @@ -488,7 +488,7 @@ static const FuriHalFlashObMapping furi_hal_flash_ob_reg_map[FURI_HAL_FLASH_OB_T OB_REG_DEF(FuriHalFlashObInvalid, (NULL)), OB_REG_DEF(FuriHalFlashObInvalid, (NULL)), - OB_REG_DEF(FuriHalFlashObRegisterIPCCMail, (NULL)), + OB_REG_DEF(FuriHalFlashObRegisterIPCCMail, (&FLASH->IPCCBR)), OB_REG_DEF(FuriHalFlashObRegisterSecureFlash, (NULL)), OB_REG_DEF(FuriHalFlashObRegisterC2Opts, (NULL)), }; diff --git a/firmware/targets/f7/furi_hal/furi_hal_i2c_config.c b/firmware/targets/f7/furi_hal/furi_hal_i2c_config.c index d832c4f6..678eb296 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_i2c_config.c +++ b/firmware/targets/f7/furi_hal/furi_hal_i2c_config.c @@ -108,7 +108,7 @@ void furi_hal_i2c_bus_handle_power_event( GpioSpeedLow, GpioAltFn4I2C1); - LL_I2C_InitTypeDef I2C_InitStruct = {0}; + LL_I2C_InitTypeDef I2C_InitStruct; I2C_InitStruct.PeripheralMode = LL_I2C_MODE_I2C; I2C_InitStruct.AnalogFilter = LL_I2C_ANALOGFILTER_ENABLE; I2C_InitStruct.DigitalFilter = 0; @@ -152,7 +152,7 @@ void furi_hal_i2c_bus_handle_external_event( furi_hal_gpio_init_ex( &gpio_ext_pc1, GpioModeAltFunctionOpenDrain, GpioPullNo, GpioSpeedLow, GpioAltFn4I2C3); - LL_I2C_InitTypeDef I2C_InitStruct = {0}; + LL_I2C_InitTypeDef I2C_InitStruct; I2C_InitStruct.PeripheralMode = LL_I2C_MODE_I2C; I2C_InitStruct.AnalogFilter = LL_I2C_ANALOGFILTER_ENABLE; I2C_InitStruct.DigitalFilter = 0; diff --git a/firmware/targets/f7/furi_hal/furi_hal_memory.c b/firmware/targets/f7/furi_hal/furi_hal_memory.c index ec71e666..9716f1e5 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_memory.c +++ b/firmware/targets/f7/furi_hal/furi_hal_memory.c @@ -55,9 +55,9 @@ void furi_hal_memory_init() { memory->region[SRAM_B].size = sram2b_unprotected_size; FURI_LOG_I( - TAG, "SRAM2A: 0x%p, %ld", memory->region[SRAM_A].start, memory->region[SRAM_A].size); + TAG, "SRAM2A: 0x%p, %lu", memory->region[SRAM_A].start, memory->region[SRAM_A].size); FURI_LOG_I( - TAG, "SRAM2B: 0x%p, %ld", memory->region[SRAM_B].start, memory->region[SRAM_B].size); + TAG, "SRAM2B: 0x%p, %lu", memory->region[SRAM_B].start, memory->region[SRAM_B].size); if((memory->region[SRAM_A].size > 0) || (memory->region[SRAM_B].size > 0)) { if((memory->region[SRAM_A].size > 0)) { @@ -120,4 +120,4 @@ size_t furi_hal_memory_max_pool_block() { } } return max; -} \ No newline at end of file +} diff --git a/firmware/targets/f7/furi_hal/furi_hal_nfc.c b/firmware/targets/f7/furi_hal/furi_hal_nfc.c index 75c695af..6381d1a9 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_nfc.c +++ b/firmware/targets/f7/furi_hal/furi_hal_nfc.c @@ -425,7 +425,7 @@ bool furi_hal_nfc_emulate_nfca( buff_rx_len = 0; buff_tx_len = 0; uint32_t flag = furi_event_flag_wait(event, EVENT_FLAG_ALL, FuriFlagWaitAny, timeout); - if(flag == FuriFlagErrorTimeout || flag == EVENT_FLAG_STOP) { + if(flag == (unsigned)FuriFlagErrorTimeout || flag == EVENT_FLAG_STOP) { break; } bool data_received = false; @@ -609,9 +609,9 @@ static uint16_t furi_hal_nfc_data_and_parity_to_bitstream( out[curr_bit_pos / 8] = next_par_bit; curr_bit_pos++; } else { - out[curr_bit_pos / 8] |= data[i] << curr_bit_pos % 8; + out[curr_bit_pos / 8] |= data[i] << (curr_bit_pos % 8); out[curr_bit_pos / 8 + 1] = data[i] >> (8 - curr_bit_pos % 8); - out[curr_bit_pos / 8 + 1] |= next_par_bit << curr_bit_pos % 8; + out[curr_bit_pos / 8 + 1] |= next_par_bit << (curr_bit_pos % 8); curr_bit_pos += 9; } } @@ -635,7 +635,7 @@ uint16_t furi_hal_nfc_bitstream_to_data_and_parity( uint16_t bit_processed = 0; memset(out_parity, 0, in_buff_bits / 9); while(bit_processed < in_buff_bits) { - out_data[curr_byte] = in_buff[bit_processed / 8] >> bit_processed % 8; + out_data[curr_byte] = in_buff[bit_processed / 8] >> (bit_processed % 8); out_data[curr_byte] |= in_buff[bit_processed / 8 + 1] << (8 - bit_processed % 8); out_parity[curr_byte / 8] |= FURI_BIT(in_buff[bit_processed / 8 + 1], bit_processed % 8) << (7 - curr_byte % 8); @@ -802,4 +802,4 @@ FuriHalNfcReturn furi_hal_nfc_ll_txrx_bits( void furi_hal_nfc_ll_poll() { rfalWorker(); -} \ No newline at end of file +} diff --git a/firmware/targets/f7/furi_hal/furi_hal_os.c b/firmware/targets/f7/furi_hal/furi_hal_os.c index 97d022c9..ee9743e6 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_os.c +++ b/firmware/targets/f7/furi_hal/furi_hal_os.c @@ -15,8 +15,8 @@ #define FURI_HAL_IDLE_TIMER_CLK_HZ 32768 #define FURI_HAL_OS_TICK_HZ configTICK_RATE_HZ -#define FURI_HAL_OS_IDLE_CNT_TO_TICKS(x) ((x * FURI_HAL_OS_TICK_HZ) / FURI_HAL_IDLE_TIMER_CLK_HZ) -#define FURI_HAL_OS_TICKS_TO_IDLE_CNT(x) ((x * FURI_HAL_IDLE_TIMER_CLK_HZ) / FURI_HAL_OS_TICK_HZ) +#define FURI_HAL_OS_IDLE_CNT_TO_TICKS(x) (((x)*FURI_HAL_OS_TICK_HZ) / FURI_HAL_IDLE_TIMER_CLK_HZ) +#define FURI_HAL_OS_TICKS_TO_IDLE_CNT(x) (((x)*FURI_HAL_IDLE_TIMER_CLK_HZ) / FURI_HAL_OS_TICK_HZ) #define FURI_HAL_IDLE_TIMER_TICK_PER_EPOCH (FURI_HAL_OS_IDLE_CNT_TO_TICKS(FURI_HAL_IDLE_TIMER_MAX)) #define FURI_HAL_OS_MAX_SLEEP (FURI_HAL_IDLE_TIMER_TICK_PER_EPOCH - 1) diff --git a/firmware/targets/f7/furi_hal/furi_hal_pwm.c b/firmware/targets/f7/furi_hal/furi_hal_pwm.c index e484808d..e47f752a 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_pwm.c +++ b/firmware/targets/f7/furi_hal/furi_hal_pwm.c @@ -110,7 +110,7 @@ void furi_hal_pwm_set_params(FuriHalPwmOutputId channel, uint32_t freq, uint8_t bool clock_lse = false; do { - period = freq_div / (1 << prescaler); + period = freq_div / (1UL << prescaler); if(period <= 0xFFFF) { break; } diff --git a/firmware/targets/f7/furi_hal/furi_hal_rtc.c b/firmware/targets/f7/furi_hal/furi_hal_rtc.c index e5fa8c76..e011406a 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_rtc.c +++ b/firmware/targets/f7/furi_hal/furi_hal_rtc.c @@ -154,7 +154,7 @@ void furi_hal_rtc_deinit_early() { } void furi_hal_rtc_init() { - LL_RTC_InitTypeDef RTC_InitStruct = {0}; + LL_RTC_InitTypeDef RTC_InitStruct; RTC_InitStruct.HourFormat = LL_RTC_HOURFORMAT_24HOUR; RTC_InitStruct.AsynchPrescaler = 127; RTC_InitStruct.SynchPrescaler = 255; diff --git a/firmware/targets/f7/furi_hal/furi_hal_uart.c b/firmware/targets/f7/furi_hal/furi_hal_uart.c index cb44b6d1..54232e67 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_uart.c +++ b/firmware/targets/f7/furi_hal/furi_hal_uart.c @@ -26,7 +26,7 @@ static void furi_hal_usart_init(uint32_t baud) { GpioSpeedVeryHigh, GpioAltFn7USART1); - LL_USART_InitTypeDef USART_InitStruct = {0}; + LL_USART_InitTypeDef USART_InitStruct; USART_InitStruct.PrescalerValue = LL_USART_PRESCALER_DIV1; USART_InitStruct.BaudRate = baud; USART_InitStruct.DataWidth = LL_USART_DATAWIDTH_8B; @@ -62,7 +62,7 @@ static void furi_hal_lpuart_init(uint32_t baud) { GpioSpeedVeryHigh, GpioAltFn8LPUART1); - LL_LPUART_InitTypeDef LPUART_InitStruct = {0}; + LL_LPUART_InitTypeDef LPUART_InitStruct; LPUART_InitStruct.PrescalerValue = LL_LPUART_PRESCALER_DIV1; LPUART_InitStruct.BaudRate = 115200; LPUART_InitStruct.DataWidth = LL_LPUART_DATAWIDTH_8B; diff --git a/firmware/targets/f7/furi_hal/furi_hal_usb_hid.c b/firmware/targets/f7/furi_hal/furi_hal_usb_hid.c index a7253223..fc1ce024 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_usb_hid.c +++ b/firmware/targets/f7/furi_hal/furi_hal_usb_hid.c @@ -360,11 +360,11 @@ bool furi_hal_hid_consumer_key_release(uint16_t button) { static void* hid_set_string_descr(char* str) { furi_assert(str); - uint8_t len = strlen(str); + size_t len = strlen(str); struct usb_string_descriptor* dev_str_desc = malloc(len * 2 + 2); dev_str_desc->bLength = len * 2 + 2; dev_str_desc->bDescriptorType = USB_DTYPE_STRING; - for(uint8_t i = 0; i < len; i++) dev_str_desc->wString[i] = str[i]; + for(size_t i = 0; i < len; i++) dev_str_desc->wString[i] = str[i]; return dev_str_desc; } diff --git a/firmware/targets/f7/furi_hal/furi_hal_version.c b/firmware/targets/f7/furi_hal/furi_hal_version.c index 697d6593..b7827ac7 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_version.c +++ b/firmware/targets/f7/furi_hal/furi_hal_version.c @@ -174,8 +174,6 @@ static void furi_hal_version_load_otp_v2() { void furi_hal_version_init() { switch(furi_hal_version_get_otp_version()) { case FuriHalVersionOtpVersionUnknown: - furi_hal_version_load_otp_default(); - break; case FuriHalVersionOtpVersionEmpty: furi_hal_version_load_otp_default(); break; diff --git a/furi/core/check.c b/furi/core/check.c index 1c2a005f..910527ce 100644 --- a/furi/core/check.c +++ b/furi/core/check.c @@ -117,7 +117,7 @@ FURI_NORETURN void __furi_crash() { if(debug) { furi_hal_console_puts("\r\nSystem halted. Connect debugger for more info\r\n"); furi_hal_console_puts("\033[0m\r\n"); - RESTORE_REGISTERS_AND_HALT_MCU(debug); + RESTORE_REGISTERS_AND_HALT_MCU(true); } else { furi_hal_rtc_set_fault_data((uint32_t)__furi_check_message); furi_hal_console_puts("\r\nRebooting system.\r\n"); diff --git a/furi/core/core_defines.h b/furi/core/core_defines.h index 487fe2ca..a0f50aff 100644 --- a/furi/core/core_defines.h +++ b/furi/core/core_defines.h @@ -93,7 +93,7 @@ extern "C" { #endif #ifndef FURI_BIT_CLEAR -#define FURI_BIT_CLEAR(x, n) ((x) &= ~(1 << (n))) +#define FURI_BIT_CLEAR(x, n) ((x) &= ~(1UL << (n))) #endif #define FURI_SW_MEMBARRIER() asm volatile("" : : : "memory") diff --git a/furi/core/event_flag.c b/furi/core/event_flag.c index 07dd30a1..87de65f2 100644 --- a/furi/core/event_flag.c +++ b/furi/core/event_flag.c @@ -9,7 +9,11 @@ FuriEventFlag* furi_event_flag_alloc() { furi_assert(!FURI_IS_IRQ_MODE()); - return ((FuriEventFlag*)xEventGroupCreate()); + + EventGroupHandle_t handle = xEventGroupCreate(); + furi_check(handle); + + return ((FuriEventFlag*)handle); } void furi_event_flag_free(FuriEventFlag* instance) { diff --git a/furi/core/memmgr_heap.c b/furi/core/memmgr_heap.c index 01153fe5..ca206cd3 100644 --- a/furi/core/memmgr_heap.c +++ b/furi/core/memmgr_heap.c @@ -212,7 +212,8 @@ static inline void traceFREE(void* pointer, size_t size) { MemmgrHeapThreadDict_get(memmgr_heap_thread_dict, (uint32_t)thread_id); if(alloc_dict) { // In some cases thread may want to release memory that was not allocated by it - (void)MemmgrHeapAllocDict_erase(*alloc_dict, (uint32_t)pointer); + const bool res = MemmgrHeapAllocDict_erase(*alloc_dict, (uint32_t)pointer); + UNUSED(res); } memmgr_heap_thread_trace_depth--; } @@ -520,8 +521,8 @@ void vPortFree(void* pv) { { furi_assert((size_t)pv >= SRAM_BASE); furi_assert((size_t)pv < SRAM_BASE + 1024 * 256); + furi_assert(pxLink->xBlockSize >= xHeapStructSize); furi_assert((pxLink->xBlockSize - xHeapStructSize) < 1024 * 256); - furi_assert((int32_t)(pxLink->xBlockSize - xHeapStructSize) >= 0); /* Add this block to the list of free blocks. */ xFreeBytesRemaining += pxLink->xBlockSize; diff --git a/furi/core/message_queue.c b/furi/core/message_queue.c index 2658d6ff..9a41f877 100644 --- a/furi/core/message_queue.c +++ b/furi/core/message_queue.c @@ -7,7 +7,10 @@ FuriMessageQueue* furi_message_queue_alloc(uint32_t msg_count, uint32_t msg_size) { furi_assert((furi_is_irq_context() == 0U) && (msg_count > 0U) && (msg_size > 0U)); - return ((FuriMessageQueue*)xQueueCreate(msg_count, msg_size)); + QueueHandle_t handle = xQueueCreate(msg_count, msg_size); + furi_check(handle); + + return ((FuriMessageQueue*)handle); } void furi_message_queue_free(FuriMessageQueue* instance) { diff --git a/furi/core/mutex.c b/furi/core/mutex.c index ab66b0f1..9fb964a1 100644 --- a/furi/core/mutex.c +++ b/furi/core/mutex.c @@ -30,6 +30,8 @@ FuriMutex* furi_mutex_alloc(FuriMutexType type) { void furi_mutex_free(FuriMutex* instance) { furi_assert(!FURI_IS_IRQ_MODE()); + furi_assert(instance); + vSemaphoreDelete((SemaphoreHandle_t)((uint32_t)instance & ~1U)); } diff --git a/furi/core/stream_buffer.c b/furi/core/stream_buffer.c index 2df84fa5..bf483948 100644 --- a/furi/core/stream_buffer.c +++ b/furi/core/stream_buffer.c @@ -1,18 +1,26 @@ #include "base.h" +#include "check.h" #include "stream_buffer.h" #include "common_defines.h" #include #include FuriStreamBuffer* furi_stream_buffer_alloc(size_t size, size_t trigger_level) { - return xStreamBufferCreate(size, trigger_level); + furi_assert(size != 0); + + StreamBufferHandle_t handle = xStreamBufferCreate(size, trigger_level); + furi_check(handle); + + return handle; }; void furi_stream_buffer_free(FuriStreamBuffer* stream_buffer) { + furi_assert(stream_buffer); vStreamBufferDelete(stream_buffer); }; bool furi_stream_set_trigger_level(FuriStreamBuffer* stream_buffer, size_t trigger_level) { + furi_assert(stream_buffer); return xStreamBufferSetTriggerLevel(stream_buffer, trigger_level) == pdTRUE; }; diff --git a/furi/core/string.c b/furi/core/string.c index 901b1f62..4384fe06 100644 --- a/furi/core/string.c +++ b/furi/core/string.c @@ -29,16 +29,16 @@ FuriString* furi_string_alloc() { } FuriString* furi_string_alloc_set(const FuriString* s) { - FuriString* string = malloc(sizeof(FuriString)); //-V773 + FuriString* string = malloc(sizeof(FuriString)); //-V799 string_init_set(string->string, s->string); return string; -} +} //-V773 FuriString* furi_string_alloc_set_str(const char cstr[]) { - FuriString* string = malloc(sizeof(FuriString)); //-V773 + FuriString* string = malloc(sizeof(FuriString)); //-V799 string_init_set(string->string, cstr); return string; -} +} //-V773 FuriString* furi_string_alloc_printf(const char format[], ...) { va_list args; @@ -299,4 +299,4 @@ void furi_string_utf8_decode(char c, FuriStringUTF8State* state, FuriStringUnico m_str1ng_utf8_state_e m_state = furi_state_to_state(*state); m_str1ng_utf8_decode(c, &m_state, unicode); *state = state_to_furi_state(m_state); -} \ No newline at end of file +} diff --git a/furi/core/thread.c b/furi/core/thread.c index 34cc7d98..c966dd57 100644 --- a/furi/core/thread.c +++ b/furi/core/thread.c @@ -49,9 +49,9 @@ static size_t __furi_thread_stdout_write(FuriThread* thread, const char* data, s static int32_t __furi_thread_stdout_flush(FuriThread* thread); /** Catch threads that are trying to exit wrong way */ -__attribute__((__noreturn__)) void furi_thread_catch() { +__attribute__((__noreturn__)) void furi_thread_catch() { //-V1082 asm volatile("nop"); // extra magic - furi_crash("You are doing it wrong"); + furi_crash("You are doing it wrong"); //-V779 __builtin_unreachable(); } @@ -84,10 +84,10 @@ static void furi_thread_body(void* context) { if(thread->heap_trace_enabled == true) { furi_delay_ms(33); thread->heap_size = memmgr_heap_get_thread_memory((FuriThreadId)task_handle); - furi_log_print_format( + furi_log_print_format( //-V576 thread->heap_size ? FuriLogLevelError : FuriLogLevelInfo, TAG, - "%s allocation balance: %d", + "%s allocation balance: %u", thread->name ? thread->name : "Thread", thread->heap_size); memmgr_heap_disable_thread_trace((FuriThreadId)task_handle); diff --git a/furi/core/timer.c b/furi/core/timer.c index c42b0c2a..be7efebe 100644 --- a/furi/core/timer.c +++ b/furi/core/timer.c @@ -32,44 +32,28 @@ FuriTimer* furi_timer_alloc(FuriTimerCallback func, FuriTimerType type, void* co TimerHandle_t hTimer; TimerCallback_t* callb; UBaseType_t reload; - uint32_t callb_dyn; hTimer = NULL; - callb = NULL; - callb_dyn = 0U; /* Dynamic memory allocation is available: if memory for callback and */ /* its context is not provided, allocate it from dynamic memory pool */ - if(callb == NULL) { - callb = (TimerCallback_t*)malloc(sizeof(TimerCallback_t)); + callb = (TimerCallback_t*)malloc(sizeof(TimerCallback_t)); - if(callb != NULL) { - /* Callback memory was allocated from dynamic pool, set flag */ - callb_dyn = 1U; - } + callb->func = func; + callb->context = context; + + if(type == FuriTimerTypeOnce) { + reload = pdFALSE; + } else { + reload = pdTRUE; } - if(callb != NULL) { - callb->func = func; - callb->context = context; - - if(type == FuriTimerTypeOnce) { - reload = pdFALSE; - } else { - reload = pdTRUE; - } - - /* Store callback memory dynamic allocation flag */ - callb = (TimerCallback_t*)((uint32_t)callb | callb_dyn); - // TimerCallback function is always provided as a callback and is used to call application - // specified function with its context both stored in structure callb. - hTimer = xTimerCreate(NULL, 1, reload, callb, TimerCallback); - if((hTimer == NULL) && (callb != NULL) && (callb_dyn == 1U)) { - /* Failed to create a timer, release allocated resources */ - callb = (TimerCallback_t*)((uint32_t)callb & ~1U); - free(callb); - } - } + /* Store callback memory dynamic allocation flag */ + callb = (TimerCallback_t*)((uint32_t)callb | 1U); + // TimerCallback function is always provided as a callback and is used to call application + // specified function with its context both stored in structure callb. + hTimer = xTimerCreate(NULL, 1, reload, callb, TimerCallback); + furi_check(hTimer); /* Return timer ID */ return ((FuriTimer*)hTimer); diff --git a/lib/ReadMe.md b/lib/ReadMe.md index 9cd846a0..82dcb74c 100644 --- a/lib/ReadMe.md +++ b/lib/ReadMe.md @@ -11,7 +11,7 @@ - `infrared` - Infrared library - `libusb_stm32` - STM32 USB library - `littlefs` - Internal storage file system -- `micro-ecc` - Elliptic Curve Crpytography library +- `micro-ecc` - Elliptic Curve Crytography library - `microtar` - TAR archive support library - `mlib` - Algorithms and containers - `nanopb` - Nano Protobuf library diff --git a/lib/ST25RFAL002/include/rfal_isoDep.h b/lib/ST25RFAL002/include/rfal_isoDep.h index f4ebdac5..34bd6172 100644 --- a/lib/ST25RFAL002/include/rfal_isoDep.h +++ b/lib/ST25RFAL002/include/rfal_isoDep.h @@ -857,7 +857,7 @@ ReturnCode rfalIsoDepATTRIB( * \brief Deselects PICC * * This function sends a deselect command to PICC and waits for it`s - * responce in a blocking way + * response in a blocking way * * \return ERR_NONE : Deselect successfully sent and acknowledged by PICC * \return ERR_TIMEOUT: No response rcvd from PICC diff --git a/lib/ST25RFAL002/include/rfal_t4t.h b/lib/ST25RFAL002/include/rfal_t4t.h index ff026e1a..edee1cd8 100644 --- a/lib/ST25RFAL002/include/rfal_t4t.h +++ b/lib/ST25RFAL002/include/rfal_t4t.h @@ -88,7 +88,7 @@ #define RFAL_T4T_ISO7816_P2_SELECT_RETURN_FCI_TEMPLATE \ 0x00U /*!< b4b3 P2 value for Return FCI template */ #define RFAL_T4T_ISO7816_P2_SELECT_NO_RESPONSE_DATA \ - 0x0CU /*!< b4b3 P2 value for No responce data */ + 0x0CU /*!< b4b3 P2 value for No response data */ #define RFAL_T4T_ISO7816_STATUS_COMPLETE \ 0x9000U /*!< Command completed \ Normal processing - No further qualification*/ diff --git a/lib/app-scened-template/view_modules/popup_vm.cpp b/lib/app-scened-template/view_modules/popup_vm.cpp index c378d9cc..e2c8732e 100644 --- a/lib/app-scened-template/view_modules/popup_vm.cpp +++ b/lib/app-scened-template/view_modules/popup_vm.cpp @@ -1,4 +1,5 @@ #include "popup_vm.h" +#include "gui/modules/popup.h" PopupVM::PopupVM() { popup = popup_alloc(); } @@ -50,5 +51,5 @@ void PopupVM::enable_timeout() { } void PopupVM::disable_timeout() { - popup_enable_timeout(popup); + popup_disable_timeout(popup); } diff --git a/lib/drivers/cc1101.c b/lib/drivers/cc1101.c index f28165e8..d563c30c 100644 --- a/lib/drivers/cc1101.c +++ b/lib/drivers/cc1101.c @@ -121,7 +121,7 @@ uint32_t cc1101_set_intermediate_frequency(FuriHalSpiBusHandle* handle, uint32_t } void cc1101_set_pa_table(FuriHalSpiBusHandle* handle, const uint8_t value[8]) { - uint8_t tx[9] = {CC1101_PATABLE | CC1101_BURST}; + uint8_t tx[9] = {CC1101_PATABLE | CC1101_BURST}; //-V1009 CC1101Status rx[9] = {0}; memcpy(&tx[1], &value[0], 8); diff --git a/lib/drivers/lp5562.c b/lib/drivers/lp5562.c index f5d765fa..e755d2d6 100644 --- a/lib/drivers/lp5562.c +++ b/lib/drivers/lp5562.c @@ -105,7 +105,7 @@ void lp5562_set_channel_src(FuriHalI2cBusHandle* handle, LP5562Channel channel, reg_val &= ~(0x3 << bit_offset); reg_val |= ((src & 0x03) << bit_offset); furi_hal_i2c_write_reg_8(handle, LP5562_ADDRESS, 0x70, reg_val, LP5562_I2C_TIMEOUT); - } while(channel); + } while(channel != 0); } void lp5562_execute_program( @@ -166,7 +166,7 @@ void lp5562_stop_program(FuriHalI2cBusHandle* handle, LP5562Engine eng) { bit_offset = (3 - eng) * 2; furi_hal_i2c_read_reg_8(handle, LP5562_ADDRESS, 0x01, ®_val, LP5562_I2C_TIMEOUT); reg_val &= ~(0x3 << bit_offset); - reg_val |= (0x00 << bit_offset); // Disabled + // Not setting lowest 2 bits here furi_hal_i2c_write_reg_8(handle, LP5562_ADDRESS, 0x01, reg_val, LP5562_I2C_TIMEOUT); } diff --git a/lib/fatfs/ff.c b/lib/fatfs/ff.c index 85ab9736..d3908957 100644 --- a/lib/fatfs/ff.c +++ b/lib/fatfs/ff.c @@ -3975,7 +3975,7 @@ FRESULT f_getcwd ( #endif if (i == len) { /* Root-directory */ *tp++ = '/'; - } else { /* Sub-directroy */ + } else { /* Sub-directory */ do /* Add stacked path str */ *tp++ = buff[i++]; while (i < len); @@ -4673,7 +4673,7 @@ FRESULT f_mkdir ( } } if (res == FR_OK) { - res = dir_register(&dj); /* Register the object to the directoy */ + res = dir_register(&dj); /* Register the object to the directory */ } if (res == FR_OK) { #if _FS_EXFAT diff --git a/lib/flipper_application/elf/elf_file.c b/lib/flipper_application/elf/elf_file.c index 2082e550..bf98650a 100644 --- a/lib/flipper_application/elf/elf_file.c +++ b/lib/flipper_application/elf/elf_file.c @@ -6,8 +6,8 @@ #define TAG "elf" #define ELF_NAME_BUFFER_LEN 32 -#define SECTION_OFFSET(e, n) (e->section_table + n * sizeof(Elf32_Shdr)) -#define IS_FLAGS_SET(v, m) ((v & m) == m) +#define SECTION_OFFSET(e, n) ((e)->section_table + (n) * sizeof(Elf32_Shdr)) +#define IS_FLAGS_SET(v, m) (((v) & (m)) == (m)) #define RESOLVER_THREAD_YIELD_STEP 30 // #define ELF_DEBUG_LOG 1 @@ -758,15 +758,13 @@ ELFFileLoadStatus elf_file_load_sections(ELFFile* elf) { AddressCache_init(elf->relocation_cache); - if(status == ELFFileLoadStatusSuccess) { - for(ELFSectionDict_it(it, elf->sections); !ELFSectionDict_end_p(it); - ELFSectionDict_next(it)) { - ELFSectionDict_itref_t* itref = ELFSectionDict_ref(it); - FURI_LOG_D(TAG, "Relocating section '%s'", itref->key); - if(!elf_relocate_section(elf, &itref->value)) { - FURI_LOG_E(TAG, "Error relocating section '%s'", itref->key); - status = ELFFileLoadStatusMissingImports; - } + for(ELFSectionDict_it(it, elf->sections); !ELFSectionDict_end_p(it); + ELFSectionDict_next(it)) { + ELFSectionDict_itref_t* itref = ELFSectionDict_ref(it); + FURI_LOG_D(TAG, "Relocating section '%s'", itref->key); + if(!elf_relocate_section(elf, &itref->value)) { + FURI_LOG_E(TAG, "Error relocating section '%s'", itref->key); + status = ELFFileLoadStatusMissingImports; } } @@ -793,7 +791,7 @@ ELFFileLoadStatus elf_file_load_sections(ELFFile* elf) { ELFSectionDict_itref_t* itref = ELFSectionDict_ref(it); total_size += itref->value.size; } - FURI_LOG_I(TAG, "Total size of loaded sections: %u", total_size); + FURI_LOG_I(TAG, "Total size of loaded sections: %u", total_size); //-V576 } return status; diff --git a/lib/flipper_format/flipper_format_stream.c b/lib/flipper_format/flipper_format_stream.c index 9cce95d4..405b819a 100644 --- a/lib/flipper_format/flipper_format_stream.c +++ b/lib/flipper_format/flipper_format_stream.c @@ -298,7 +298,7 @@ bool flipper_format_stream_write_value_line(Stream* stream, FlipperStreamWriteDa }; break; case FlipperStreamValueUint32: { const uint32_t* data = write_data->data; - furi_string_printf(value, "%" PRId32, data[i]); + furi_string_printf(value, "%" PRIu32, data[i]); }; break; case FlipperStreamValueHexUint64: { const uint64_t* data = write_data->data; @@ -396,7 +396,7 @@ bool flipper_format_stream_read_value_line( }; break; case FlipperStreamValueUint32: { uint32_t* data = _data; - scan_values = sscanf(furi_string_get_cstr(value), "%" PRId32, &data[i]); + scan_values = sscanf(furi_string_get_cstr(value), "%" PRIu32, &data[i]); }; break; case FlipperStreamValueHexUint64: { uint64_t* data = _data; diff --git a/lib/infrared/encoder_decoder/common/infrared_common_decoder.c b/lib/infrared/encoder_decoder/common/infrared_common_decoder.c index 7f1c3a4f..8acb6751 100644 --- a/lib/infrared/encoder_decoder/common/infrared_common_decoder.c +++ b/lib/infrared/encoder_decoder/common/infrared_common_decoder.c @@ -107,7 +107,7 @@ static InfraredStatus infrared_common_decode_bits(InfraredCommonDecoder* decoder decoder->timings_cnt = consume_samples(decoder->timings, decoder->timings_cnt, 1); /* check if largest protocol version can be decoded */ - if(level && (decoder->protocol->databit_len[0] == decoder->databit_cnt) && + if(level && (decoder->protocol->databit_len[0] == decoder->databit_cnt) && //-V1051 !timings->min_split_time) { status = InfraredStatusReady; break; diff --git a/lib/infrared/encoder_decoder/common/infrared_common_encoder.c b/lib/infrared/encoder_decoder/common/infrared_common_encoder.c index dcf63acc..9c774617 100644 --- a/lib/infrared/encoder_decoder/common/infrared_common_encoder.c +++ b/lib/infrared/encoder_decoder/common/infrared_common_encoder.c @@ -94,7 +94,6 @@ InfraredStatus case InfraredCommonEncoderStateSilence: *duration = encoder->protocol->timings.silence_time; *level = false; - status = InfraredStatusOk; encoder->state = InfraredCommonEncoderStatePreamble; ++encoder->timings_encoded; encoder->timings_sum = 0; diff --git a/lib/infrared/encoder_decoder/common/infrared_common_i.h b/lib/infrared/encoder_decoder/common/infrared_common_i.h index 20dbeb79..f34e81ed 100644 --- a/lib/infrared/encoder_decoder/common/infrared_common_i.h +++ b/lib/infrared/encoder_decoder/common/infrared_common_i.h @@ -4,7 +4,7 @@ #include "infrared.h" #include "infrared_i.h" -#define MATCH_TIMING(x, v, delta) (((x) < (v + delta)) && ((x) > (v - delta))) +#define MATCH_TIMING(x, v, delta) (((x) < ((v) + (delta))) && ((x) > ((v) - (delta)))) typedef struct InfraredCommonDecoder InfraredCommonDecoder; typedef struct InfraredCommonEncoder InfraredCommonEncoder; diff --git a/lib/infrared/encoder_decoder/infrared.h b/lib/infrared/encoder_decoder/infrared.h index 086950f1..2c76645f 100644 --- a/lib/infrared/encoder_decoder/infrared.h +++ b/lib/infrared/encoder_decoder/infrared.h @@ -10,7 +10,7 @@ extern "C" { #define INFRARED_COMMON_CARRIER_FREQUENCY ((uint32_t)38000) #define INFRARED_COMMON_DUTY_CYCLE ((float)0.33) -/* if we want to see splitted raw signals during brutforce, +/* if we want to see split raw signals during bruteforce, * we have to have RX raw timing delay less than TX */ #define INFRARED_RAW_RX_TIMING_DELAY_US 150000 #define INFRARED_RAW_TX_TIMING_DELAY_US 180000 diff --git a/lib/infrared/worker/infrared_transmit.c b/lib/infrared/worker/infrared_transmit.c index 695be8d1..1a508301 100644 --- a/lib/infrared/worker/infrared_transmit.c +++ b/lib/infrared/worker/infrared_transmit.c @@ -67,7 +67,7 @@ void infrared_send_raw(const uint32_t timings[], uint32_t timings_cnt, bool star FuriHalInfraredTxGetDataState infrared_get_data_callback(void* context, uint32_t* duration, bool* level) { - FuriHalInfraredTxGetDataState state = FuriHalInfraredTxGetDataStateLastDone; + FuriHalInfraredTxGetDataState state; InfraredEncoderHandler* handler = context; InfraredStatus status = InfraredStatusError; @@ -82,9 +82,10 @@ FuriHalInfraredTxGetDataState } else if(status == InfraredStatusOk) { state = FuriHalInfraredTxGetDataStateOk; } else if(status == InfraredStatusDone) { - state = FuriHalInfraredTxGetDataStateDone; if(--infrared_tx_number_of_transmissions == 0) { state = FuriHalInfraredTxGetDataStateLastDone; + } else { + state = FuriHalInfraredTxGetDataStateDone; } } else { furi_crash(NULL); diff --git a/lib/infrared/worker/infrared_worker.c b/lib/infrared/worker/infrared_worker.c index 57288f3c..033dba52 100644 --- a/lib/infrared/worker/infrared_worker.c +++ b/lib/infrared/worker/infrared_worker.c @@ -7,6 +7,7 @@ #include #include #include +#include #include @@ -397,8 +398,9 @@ static bool infrared_get_new_signal(InfraredWorker* instance) { } instance->tx.tx_raw_cnt = 0; - instance->tx.need_reinitialization = (new_tx_frequency != instance->tx.frequency) || - (new_tx_duty_cycle != instance->tx.duty_cycle); + instance->tx.need_reinitialization = + (new_tx_frequency != instance->tx.frequency) || + !float_is_equal(new_tx_duty_cycle, instance->tx.duty_cycle); instance->tx.frequency = new_tx_frequency; instance->tx.duty_cycle = new_tx_duty_cycle; if(instance->signal.decoded) { diff --git a/lib/infrared/worker/infrared_worker.h b/lib/infrared/worker/infrared_worker.h index 26919c4f..1a8cd9a7 100644 --- a/lib/infrared/worker/infrared_worker.h +++ b/lib/infrared/worker/infrared_worker.h @@ -7,7 +7,7 @@ extern "C" { #endif -#define MAX_TIMINGS_AMOUNT 1024 +#define MAX_TIMINGS_AMOUNT 1024U /** Interface struct of infrared worker */ typedef struct InfraredWorker InfraredWorker; diff --git a/lib/lfrfid/lfrfid_raw_worker.c b/lib/lfrfid/lfrfid_raw_worker.c index 8c69aced..22c0bbd0 100644 --- a/lib/lfrfid/lfrfid_raw_worker.c +++ b/lib/lfrfid/lfrfid_raw_worker.c @@ -114,7 +114,6 @@ void lfrfid_raw_worker_stop(LFRFIDRawWorker* worker) { worker->emulate_callback = NULL; worker->context = NULL; worker->read_callback = NULL; - worker->context = NULL; furi_event_flag_set(worker->events, 1 << LFRFIDRawWorkerEventStop); furi_thread_join(worker->thread); } @@ -335,7 +334,7 @@ static int32_t lfrfid_raw_emulate_worker_thread(void* thread_context) { } if(data->ctx.overrun_count) { - FURI_LOG_E(TAG_EMULATE, "overruns: %u", data->ctx.overrun_count); + FURI_LOG_E(TAG_EMULATE, "overruns: %zu", data->ctx.overrun_count); } furi_stream_buffer_free(data->ctx.stream); @@ -344,4 +343,4 @@ static int32_t lfrfid_raw_emulate_worker_thread(void* thread_context) { free(data); return 0; -} \ No newline at end of file +} diff --git a/lib/lfrfid/lfrfid_worker.c b/lib/lfrfid/lfrfid_worker.c index f33c1aed..1e491c6b 100644 --- a/lib/lfrfid/lfrfid_worker.c +++ b/lib/lfrfid/lfrfid_worker.c @@ -139,7 +139,7 @@ static int32_t lfrfid_worker_thread(void* thread_context) { while(true) { uint32_t flags = furi_thread_flags_wait(LFRFIDEventAll, FuriFlagWaitAny, FuriWaitForever); - if(flags != FuriFlagErrorTimeout) { + if(flags != (unsigned)FuriFlagErrorTimeout) { // stop thread if(flags & LFRFIDEventStopThread) break; @@ -161,4 +161,4 @@ static int32_t lfrfid_worker_thread(void* thread_context) { } return 0; -} \ No newline at end of file +} diff --git a/lib/lfrfid/lfrfid_worker_modes.c b/lib/lfrfid/lfrfid_worker_modes.c index 1fbae04c..9b6f16eb 100644 --- a/lib/lfrfid/lfrfid_worker_modes.c +++ b/lib/lfrfid/lfrfid_worker_modes.c @@ -276,7 +276,7 @@ static LFRFIDWorkerReadState lfrfid_worker_read_internal( FURI_LOG_D( TAG, - "%s, %d, [%s]", + "%s, %zu, [%s]", protocol_dict_get_name(worker->protocols, protocol), last_read_count, furi_string_get_cstr(string_info)); @@ -335,9 +335,9 @@ static LFRFIDWorkerReadState lfrfid_worker_read_internal( } static void lfrfid_worker_mode_read_process(LFRFIDWorker* worker) { - LFRFIDFeature feature = LFRFIDFeatureASK; ProtocolId read_result = PROTOCOL_NO; LFRFIDWorkerReadState state; + LFRFIDFeature feature; if(worker->read_type == LFRFIDWorkerReadTypePSKOnly) { feature = LFRFIDFeaturePSK; @@ -635,4 +635,4 @@ const LFRFIDWorkerModeType lfrfid_worker_modes[] = { [LFRFIDWorkerEmulate] = {.process = lfrfid_worker_mode_emulate_process}, [LFRFIDWorkerReadRaw] = {.process = lfrfid_worker_mode_read_raw_process}, [LFRFIDWorkerEmulateRaw] = {.process = lfrfid_worker_mode_emulate_raw_process}, -}; \ No newline at end of file +}; diff --git a/lib/lfrfid/protocols/protocol_indala26.c b/lib/lfrfid/protocols/protocol_indala26.c index cafc5848..8319f0a9 100644 --- a/lib/lfrfid/protocols/protocol_indala26.c +++ b/lib/lfrfid/protocols/protocol_indala26.c @@ -353,4 +353,4 @@ const ProtocolBase protocol_indala26 = { .render_data = (ProtocolRenderData)protocol_indala26_render_data, .render_brief_data = (ProtocolRenderData)protocol_indala26_render_brief_data, .write_data = (ProtocolWriteData)protocol_indala26_write_data, -}; \ No newline at end of file +}; diff --git a/lib/lfrfid/protocols/protocol_pac_stanley.c b/lib/lfrfid/protocols/protocol_pac_stanley.c index 59aaf1e6..11c64240 100644 --- a/lib/lfrfid/protocols/protocol_pac_stanley.c +++ b/lib/lfrfid/protocols/protocol_pac_stanley.c @@ -12,7 +12,7 @@ #define PAC_STANLEY_ENCODED_BYTE_FULL_SIZE \ (PAC_STANLEY_ENCODED_BYTE_SIZE + PAC_STANLEY_PREAMBLE_BYTE_SIZE) #define PAC_STANLEY_BYTE_LENGTH (10) // start bit, 7 data bits, parity bit, stop bit -#define PAC_STANLEY_DATA_START_INDEX 8 + (3 * PAC_STANLEY_BYTE_LENGTH) + 1 +#define PAC_STANLEY_DATA_START_INDEX (8 + (3 * PAC_STANLEY_BYTE_LENGTH) + 1) #define PAC_STANLEY_DECODED_DATA_SIZE (4) #define PAC_STANLEY_ENCODED_DATA_SIZE (sizeof(ProtocolPACStanley)) @@ -128,7 +128,7 @@ bool protocol_pac_stanley_decoder_feed(ProtocolPACStanley* protocol, bool level, } bool protocol_pac_stanley_encoder_start(ProtocolPACStanley* protocol) { - memset(protocol->encoded_data, 0, PAC_STANLEY_ENCODED_BYTE_SIZE); + memset(protocol->encoded_data, 0, sizeof(protocol->encoded_data)); uint8_t idbytes[10]; idbytes[0] = '2'; @@ -137,7 +137,7 @@ bool protocol_pac_stanley_encoder_start(ProtocolPACStanley* protocol) { uint8_to_hex_chars(protocol->data, &idbytes[2], 8); // insert start and stop bits - for(size_t i = 0; i < 16; i++) protocol->encoded_data[i] = 0x40 >> (i + 3) % 5 * 2; + for(size_t i = 0; i < 16; i++) protocol->encoded_data[i] = 0x40 >> ((i + 3) % 5 * 2); protocol->encoded_data[0] = 0xFF; // mark + stop protocol->encoded_data[1] = 0x20; // start + reflect8(STX) @@ -228,4 +228,4 @@ const ProtocolBase protocol_pac_stanley = { .render_data = (ProtocolRenderData)protocol_pac_stanley_render_data, .render_brief_data = (ProtocolRenderData)protocol_pac_stanley_render_data, .write_data = (ProtocolWriteData)protocol_pac_stanley_write_data, -}; \ No newline at end of file +}; diff --git a/lib/lfrfid/protocols/protocol_pyramid.c b/lib/lfrfid/protocols/protocol_pyramid.c index 974bb6da..d794bb46 100644 --- a/lib/lfrfid/protocols/protocol_pyramid.c +++ b/lib/lfrfid/protocols/protocol_pyramid.c @@ -88,7 +88,7 @@ static bool protocol_pyramid_can_be_decoded(uint8_t* data) { } uint8_t fmt_len = 105 - j; - // Only suppport 26bit format for now + // Only support 26bit format for now if(fmt_len != 26) return false; return true; diff --git a/lib/lfrfid/tools/bit_lib.c b/lib/lfrfid/tools/bit_lib.c index c84f4b7e..54decb3e 100644 --- a/lib/lfrfid/tools/bit_lib.c +++ b/lib/lfrfid/tools/bit_lib.c @@ -25,7 +25,7 @@ void bit_lib_set_bits(uint8_t* data, size_t position, uint8_t byte, uint8_t leng for(uint8_t i = 0; i < length; ++i) { uint8_t shift = (length - 1) - i; - bit_lib_set_bit(data, position + i, (byte >> shift) & 1); + bit_lib_set_bit(data, position + i, (byte >> shift) & 1); //-V610 } } @@ -69,9 +69,9 @@ uint32_t bit_lib_get_bits_32(const uint8_t* data, size_t position, uint8_t lengt value |= bit_lib_get_bits(data, position + 8, 8) << (length - 16); value |= bit_lib_get_bits(data, position + 16, length - 16); } else { - value = bit_lib_get_bits(data, position, 8) << (length - 8); - value |= bit_lib_get_bits(data, position + 8, 8) << (length - 16); - value |= bit_lib_get_bits(data, position + 16, 8) << (length - 24); + value = (uint32_t)bit_lib_get_bits(data, position, 8) << (length - 8); + value |= (uint32_t)bit_lib_get_bits(data, position + 8, 8) << (length - 16); + value |= (uint32_t)bit_lib_get_bits(data, position + 16, 8) << (length - 24); value |= bit_lib_get_bits(data, position + 24, length - 24); } @@ -364,4 +364,4 @@ uint16_t bit_lib_crc16( crc ^= xor_out; return crc; -} \ No newline at end of file +} diff --git a/lib/lfrfid/tools/bit_lib.h b/lib/lfrfid/tools/bit_lib.h index 1b048db3..bae95462 100644 --- a/lib/lfrfid/tools/bit_lib.h +++ b/lib/lfrfid/tools/bit_lib.h @@ -7,7 +7,7 @@ extern "C" { #endif -#define TOPBIT(X) (1 << (X - 1)) +#define TOPBIT(X) (1 << ((X)-1)) typedef enum { BitLibParityEven, @@ -26,13 +26,13 @@ typedef enum { * @param data value to test * @param index bit index to test */ -#define bit_lib_bit_is_set(data, index) ((data & (1 << (index))) != 0) +#define bit_lib_bit_is_set(data, index) (((data) & (1 << (index))) != 0) /** @brief Test if a bit is not set. * @param data value to test * @param index bit index to test */ -#define bit_lib_bit_is_not_set(data, index) ((data & (1 << (index))) == 0) +#define bit_lib_bit_is_not_set(data, index) (((data) & (1 << (index))) == 0) /** @brief Push a bit into a byte array. * @param data array to push bit into @@ -269,4 +269,4 @@ uint16_t bit_lib_crc16( #ifdef __cplusplus } -#endif \ No newline at end of file +#endif diff --git a/lib/lfrfid/tools/varint_pair.c b/lib/lfrfid/tools/varint_pair.c index c59ba55b..1e6c82ee 100644 --- a/lib/lfrfid/tools/varint_pair.c +++ b/lib/lfrfid/tools/varint_pair.c @@ -28,11 +28,9 @@ bool varint_pair_pack(VarintPair* pair, bool first, uint32_t value) { pair->data_length = 0; } } else { - if(pair->data_length > 0) { + if(pair->data_length != 0) { pair->data_length += varint_uint32_pack(value, pair->data + pair->data_length); result = true; - } else { - pair->data_length = 0; } } diff --git a/lib/nfc/helpers/mf_classic_dict.c b/lib/nfc/helpers/mf_classic_dict.c index 690bba61..93098d40 100644 --- a/lib/nfc/helpers/mf_classic_dict.c +++ b/lib/nfc/helpers/mf_classic_dict.c @@ -20,7 +20,7 @@ bool mf_classic_dict_check_presence(MfClassicDictType dict_type) { Storage* storage = furi_record_open(RECORD_STORAGE); bool dict_present = false; - if(dict_type == MfClassicDictTypeFlipper) { + if(dict_type == MfClassicDictTypeSystem) { dict_present = storage_common_stat(storage, MF_CLASSIC_DICT_FLIPPER_PATH, NULL) == FSE_OK; } else if(dict_type == MfClassicDictTypeUser) { dict_present = storage_common_stat(storage, MF_CLASSIC_DICT_USER_PATH, NULL) == FSE_OK; @@ -42,7 +42,7 @@ MfClassicDict* mf_classic_dict_alloc(MfClassicDictType dict_type) { bool dict_loaded = false; do { - if(dict_type == MfClassicDictTypeFlipper) { + if(dict_type == MfClassicDictTypeSystem) { if(!buffered_file_stream_open( dict->stream, MF_CLASSIC_DICT_FLIPPER_PATH, @@ -90,7 +90,7 @@ MfClassicDict* mf_classic_dict_alloc(MfClassicDictType dict_type) { } FURI_LOG_T( TAG, - "Read line: %s, len: %d", + "Read line: %s, len: %zu", furi_string_get_cstr(next_line), furi_string_size(next_line)); if(furi_string_get_char(next_line, 0) == '#') continue; @@ -101,7 +101,7 @@ MfClassicDict* mf_classic_dict_alloc(MfClassicDictType dict_type) { stream_rewind(dict->stream); dict_loaded = true; - FURI_LOG_I(TAG, "Loaded dictionary with %ld keys", dict->total_keys); + FURI_LOG_I(TAG, "Loaded dictionary with %lu keys", dict->total_keys); } while(false); if(!dict_loaded) { @@ -136,7 +136,7 @@ static void mf_classic_dict_str_to_int(FuriString* key_str, uint64_t* key_int) { for(uint8_t i = 0; i < 12; i += 2) { args_char_to_hex( furi_string_get_char(key_str, i), furi_string_get_char(key_str, i + 1), &key_byte_tmp); - *key_int |= (uint64_t)key_byte_tmp << 8 * (5 - i / 2); + *key_int |= (uint64_t)key_byte_tmp << (8 * (5 - i / 2)); } } @@ -193,7 +193,7 @@ bool mf_classic_dict_is_key_present_str(MfClassicDict* dict, FuriString* key) { bool key_found = false; stream_rewind(dict->stream); - while(!key_found) { + while(!key_found) { //-V654 if(!stream_read_line(dict->stream, next_line)) break; if(furi_string_get_char(next_line, 0) == '#') continue; if(furi_string_size(next_line) != NFC_MF_CLASSIC_KEY_LEN) continue; @@ -294,7 +294,7 @@ bool mf_classic_dict_find_index_str(MfClassicDict* dict, FuriString* key, uint32 bool key_found = false; uint32_t index = 0; stream_rewind(dict->stream); - while(!key_found) { + while(!key_found) { //-V654 if(!stream_read_line(dict->stream, next_line)) break; if(furi_string_get_char(next_line, 0) == '#') continue; if(furi_string_size(next_line) != NFC_MF_CLASSIC_KEY_LEN) continue; diff --git a/lib/nfc/helpers/mf_classic_dict.h b/lib/nfc/helpers/mf_classic_dict.h index 5b0ee312..3b2d560a 100644 --- a/lib/nfc/helpers/mf_classic_dict.h +++ b/lib/nfc/helpers/mf_classic_dict.h @@ -8,7 +8,7 @@ typedef enum { MfClassicDictTypeUser, - MfClassicDictTypeFlipper, + MfClassicDictTypeSystem, MfClassicDictTypeUnitTest, } MfClassicDictType; diff --git a/lib/nfc/helpers/reader_analyzer.c b/lib/nfc/helpers/reader_analyzer.c index 73b4b125..af4869ca 100644 --- a/lib/nfc/helpers/reader_analyzer.c +++ b/lib/nfc/helpers/reader_analyzer.c @@ -221,11 +221,11 @@ static void reader_analyzer_write( data_sent = furi_stream_buffer_send( instance->stream, &header, sizeof(ReaderAnalyzerHeader), FuriWaitForever); if(data_sent != sizeof(ReaderAnalyzerHeader)) { - FURI_LOG_W(TAG, "Sent %d out of %d bytes", data_sent, sizeof(ReaderAnalyzerHeader)); + FURI_LOG_W(TAG, "Sent %zu out of %zu bytes", data_sent, sizeof(ReaderAnalyzerHeader)); } data_sent = furi_stream_buffer_send(instance->stream, data, len, FuriWaitForever); if(data_sent != len) { - FURI_LOG_W(TAG, "Sent %d out of %d bytes", data_sent, len); + FURI_LOG_W(TAG, "Sent %zu out of %u bytes", data_sent, len); } } diff --git a/lib/nfc/nfc_device.c b/lib/nfc/nfc_device.c index 49eebc37..d10eaa0e 100644 --- a/lib/nfc/nfc_device.c +++ b/lib/nfc/nfc_device.c @@ -576,7 +576,7 @@ static bool nfc_device_save_mifare_df_data(FlipperFormat* file, NfcDevice* dev) tmp = malloc(n_apps * 3); int i = 0; for(MifareDesfireApplication* app = data->app_head; app; app = app->next) { - memcpy(tmp + i, app->id, 3); + memcpy(tmp + i, app->id, 3); //-V769 i += 3; } if(!flipper_format_write_hex(file, "Application IDs", tmp, n_apps * 3)) break; @@ -856,7 +856,7 @@ static bool nfc_device_load_mifare_classic_data(FlipperFormat* file, NfcDevice* bool old_format = false; // Read Mifare Classic format version if(!flipper_format_read_uint32(file, "Data format version", &data_format_version, 1)) { - // Load unread sectors with zero keys access for backward compatability + // Load unread sectors with zero keys access for backward compatibility if(!flipper_format_rewind(file)) break; old_format = true; } else { @@ -1085,7 +1085,7 @@ bool nfc_device_save(NfcDevice* dev, const char* dev_name) { saved = true; } while(0); - if(!saved) { + if(!saved) { //-V547 dialog_message_show_storage_error(dev->dialogs, "Can not save\nkey file"); } furi_string_free(temp_str); @@ -1125,7 +1125,7 @@ static bool nfc_device_load_data(NfcDevice* dev, FuriString* path, bool show_dia } do { - // Check existance of shadow file + // Check existence of shadow file nfc_device_get_shadow_path(path, temp_str); dev->shadow_file_exist = storage_common_stat(dev->storage, furi_string_get_cstr(temp_str), NULL) == FSE_OK; diff --git a/lib/nfc/nfc_device.h b/lib/nfc/nfc_device.h index 55ee4ac4..8b2e6e5b 100644 --- a/lib/nfc/nfc_device.h +++ b/lib/nfc/nfc_device.h @@ -18,7 +18,7 @@ extern "C" { #define NFC_DEV_NAME_MAX_LEN 22 #define NFC_READER_DATA_MAX_SIZE 64 -#define NFC_DICT_KEY_BATCH_SIZE 50 +#define NFC_DICT_KEY_BATCH_SIZE 10 #define NFC_APP_EXTENSION ".nfc" #define NFC_APP_SHADOW_EXTENSION ".shd" @@ -48,6 +48,7 @@ typedef struct { typedef struct { MfClassicDict* dict; + uint8_t current_sector; } NfcMfClassicDictAttackData; typedef enum { diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index 450428a1..a652e088 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -453,11 +453,11 @@ void nfc_worker_read_type(NfcWorker* nfc_worker) { event = NfcWorkerEventReadUidNfcA; break; } - } else { - if(!card_not_detected_notified) { - nfc_worker->callback(NfcWorkerEventNoCardDetected, nfc_worker->context); - card_not_detected_notified = true; - } + } + } else { + if(!card_not_detected_notified) { + nfc_worker->callback(NfcWorkerEventNoCardDetected, nfc_worker->context); + card_not_detected_notified = true; } } furi_hal_nfc_sleep(); @@ -509,7 +509,7 @@ void nfc_worker_emulate_apdu(NfcWorker* nfc_worker) { reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog); } - while(nfc_worker->state == NfcWorkerStateEmulateApdu) { + while(nfc_worker->state == NfcWorkerStateEmulateApdu) { //-V1044 if(furi_hal_nfc_listen(params.uid, params.uid_len, params.atqa, params.sak, false, 300)) { FURI_LOG_D(TAG, "POS terminal detected"); if(emv_card_emulation(&tx_rx)) { @@ -573,17 +573,24 @@ static void nfc_worker_mf_classic_key_attack( FuriHalNfcTxRxContext* tx_rx, uint16_t start_sector) { furi_assert(nfc_worker); + furi_assert(nfc_worker->callback); bool card_found_notified = true; bool card_removed_notified = false; MfClassicData* data = &nfc_worker->dev_data->mf_classic_data; + NfcMfClassicDictAttackData* dict_attack_data = + &nfc_worker->dev_data->mf_classic_dict_attack_data; uint32_t total_sectors = mf_classic_get_total_sectors_num(data->type); furi_assert(start_sector < total_sectors); + nfc_worker->callback(NfcWorkerEventKeyAttackStart, nfc_worker->context); + // Check every sector's A and B keys with the given key for(size_t i = start_sector; i < total_sectors; i++) { + nfc_worker->callback(NfcWorkerEventKeyAttackNextSector, nfc_worker->context); + dict_attack_data->current_sector = i; furi_hal_nfc_sleep(); if(furi_hal_nfc_activate_nfca(200, NULL)) { furi_hal_nfc_sleep(); @@ -632,6 +639,7 @@ static void nfc_worker_mf_classic_key_attack( } if(nfc_worker->state != NfcWorkerStateMfClassicDictAttack) break; } + nfc_worker->callback(NfcWorkerEventKeyAttackStop, nfc_worker->context); } void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) { @@ -657,7 +665,7 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) { } FURI_LOG_D( - TAG, "Start Dictionary attack, Key Count %ld", mf_classic_dict_get_total_keys(dict)); + TAG, "Start Dictionary attack, Key Count %lu", mf_classic_dict_get_total_keys(dict)); for(size_t i = 0; i < total_sectors; i++) { FURI_LOG_I(TAG, "Sector %d", i); nfc_worker->callback(NfcWorkerEventNewSector, nfc_worker->context); @@ -672,13 +680,15 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) { nfc_worker->callback(NfcWorkerEventNewDictKeyBatch, nfc_worker->context); } furi_hal_nfc_sleep(); - if(furi_hal_nfc_activate_nfca(200, NULL)) { - furi_hal_nfc_sleep(); + uint32_t cuid; + if(furi_hal_nfc_activate_nfca(200, &cuid)) { + bool deactivated = false; if(!card_found_notified) { nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context); card_found_notified = true; card_removed_notified = false; nfc_worker_mf_classic_key_attack(nfc_worker, prev_key, &tx_rx, i); + deactivated = true; } FURI_LOG_D( TAG, @@ -688,22 +698,26 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) { (uint32_t)key); if(!is_key_a_found) { is_key_a_found = mf_classic_is_key_found(data, i, MfClassicKeyA); - if(mf_classic_authenticate(&tx_rx, block_num, key, MfClassicKeyA)) { + if(mf_classic_authenticate_skip_activate( + &tx_rx, block_num, key, MfClassicKeyA, !deactivated, cuid)) { mf_classic_set_key_found(data, i, MfClassicKeyA, key); FURI_LOG_D(TAG, "Key found"); nfc_worker->callback(NfcWorkerEventFoundKeyA, nfc_worker->context); nfc_worker_mf_classic_key_attack(nfc_worker, key, &tx_rx, i + 1); } furi_hal_nfc_sleep(); + deactivated = true; } if(!is_key_b_found) { is_key_b_found = mf_classic_is_key_found(data, i, MfClassicKeyB); - if(mf_classic_authenticate(&tx_rx, block_num, key, MfClassicKeyB)) { + if(mf_classic_authenticate_skip_activate( + &tx_rx, block_num, key, MfClassicKeyB, !deactivated, cuid)) { FURI_LOG_D(TAG, "Key found"); mf_classic_set_key_found(data, i, MfClassicKeyB, key); nfc_worker->callback(NfcWorkerEventFoundKeyB, nfc_worker->context); nfc_worker_mf_classic_key_attack(nfc_worker, key, &tx_rx, i + 1); } + deactivated = true; } if(is_key_a_found && is_key_b_found) break; if(nfc_worker->state != NfcWorkerStateMfClassicDictAttack) break; @@ -742,7 +756,7 @@ void nfc_worker_emulate_mf_classic(NfcWorker* nfc_worker) { rfal_platform_spi_acquire(); furi_hal_nfc_listen_start(nfc_data); - while(nfc_worker->state == NfcWorkerStateMfClassicEmulate) { + while(nfc_worker->state == NfcWorkerStateMfClassicEmulate) { //-V1044 if(furi_hal_nfc_listen_rx(&tx_rx, 300)) { mf_classic_emulator(&emulator, &tx_rx); } @@ -776,7 +790,8 @@ void nfc_worker_write_mf_classic(NfcWorker* nfc_worker) { furi_hal_nfc_sleep(); FURI_LOG_I(TAG, "Check low level nfc data"); - if(memcmp(&nfc_data, &nfc_worker->dev_data->nfc_data, sizeof(FuriHalNfcDevData))) { + if(memcmp(&nfc_data, &nfc_worker->dev_data->nfc_data, sizeof(FuriHalNfcDevData)) != + 0) { FURI_LOG_E(TAG, "Wrong card"); nfc_worker->callback(NfcWorkerEventWrongCard, nfc_worker->context); break; @@ -848,7 +863,8 @@ void nfc_worker_update_mf_classic(NfcWorker* nfc_worker) { furi_hal_nfc_sleep(); FURI_LOG_I(TAG, "Check low level nfc data"); - if(memcmp(&nfc_data, &nfc_worker->dev_data->nfc_data, sizeof(FuriHalNfcDevData))) { + if(memcmp(&nfc_data, &nfc_worker->dev_data->nfc_data, sizeof(FuriHalNfcDevData)) != + 0) { FURI_LOG_E(TAG, "Low level nfc data mismatch"); nfc_worker->callback(NfcWorkerEventWrongCard, nfc_worker->context); break; diff --git a/lib/nfc/nfc_worker.h b/lib/nfc/nfc_worker.h index fdcaa72f..ce542828 100644 --- a/lib/nfc/nfc_worker.h +++ b/lib/nfc/nfc_worker.h @@ -55,6 +55,9 @@ typedef enum { NfcWorkerEventNewDictKeyBatch, NfcWorkerEventFoundKeyA, NfcWorkerEventFoundKeyB, + NfcWorkerEventKeyAttackStart, + NfcWorkerEventKeyAttackStop, + NfcWorkerEventKeyAttackNextSector, // Write Mifare Classic events NfcWorkerEventWrongCard, diff --git a/lib/nfc/parsers/all_in_one.c b/lib/nfc/parsers/all_in_one.c index 05cfe576..c02710a2 100644 --- a/lib/nfc/parsers/all_in_one.c +++ b/lib/nfc/parsers/all_in_one.c @@ -73,24 +73,14 @@ bool all_in_one_parser_parse(NfcDeviceData* dev_data) { return false; } - // If the layout is a then the ride count is stored in the first byte of page 8 uint8_t ride_count = 0; uint32_t serial = 0; if(all_in_one_get_layout(dev_data) == ALL_IN_ONE_LAYOUT_A) { + // If the layout is A then the ride count is stored in the first byte of page 8 ride_count = dev_data->mf_ul_data.data[4 * 8]; } else if(all_in_one_get_layout(dev_data) == ALL_IN_ONE_LAYOUT_D) { // If the layout is D, the ride count is stored in the second byte of page 9 ride_count = dev_data->mf_ul_data.data[4 * 9 + 1]; - // I hate this with a burning passion. - - // The number starts at the second half of the third byte on page 4, and is 32 bits long - // So we get the second half of the third byte, then bytes 4-6, and then the first half of the 7th byte - // B8 17 A2 A4 BD becomes 81 7A 2A 4B - serial = (dev_data->mf_ul_data.data[4 * 4 + 2] & 0x0F) << 28 | - dev_data->mf_ul_data.data[4 * 4 + 3] << 20 | - dev_data->mf_ul_data.data[4 * 4 + 4] << 12 | - dev_data->mf_ul_data.data[4 * 4 + 5] << 4 | - (dev_data->mf_ul_data.data[4 * 4 + 6] >> 4); } else { FURI_LOG_I("all_in_one", "Unknown layout: %d", all_in_one_get_layout(dev_data)); ride_count = 137; @@ -110,4 +100,4 @@ bool all_in_one_parser_parse(NfcDeviceData* dev_data) { furi_string_printf( dev_data->parsed_data, "\e#All-In-One\nNumber: %lu\nRides left: %u", serial, ride_count); return true; -} \ No newline at end of file +} diff --git a/lib/nfc/parsers/plantain_4k_parser.c b/lib/nfc/parsers/plantain_4k_parser.c index 348b5a64..e636bee0 100644 --- a/lib/nfc/parsers/plantain_4k_parser.c +++ b/lib/nfc/parsers/plantain_4k_parser.c @@ -106,7 +106,7 @@ bool plantain_4k_parser_parse(NfcDeviceData* dev_data) { // Point to block 0 of sector 0, value 0 temp_ptr = &data->block[0 * 4].value[0]; // Read first 7 bytes of block 0 of sector 0 from last to first and convert them to uint64_t - // 80 5C 23 8A 16 31 04 becomes 04 31 16 8A 23 5C 80, and equals to 36130104729284868 decimal + // 04 31 16 8A 23 5C 80 becomes 80 5C 23 8A 16 31 04, and equals to 36130104729284868 decimal uint8_t card_number_arr[7]; for(size_t i = 0; i < 7; i++) { card_number_arr[i] = temp_ptr[6 - i]; @@ -116,26 +116,9 @@ bool plantain_4k_parser_parse(NfcDeviceData* dev_data) { for(size_t i = 0; i < 7; i++) { card_number = (card_number << 8) | card_number_arr[i]; } - // Convert card number to string - FuriString* card_number_str; - card_number_str = furi_string_alloc(); - // Should look like "361301047292848684" - furi_string_printf(card_number_str, "%llu", card_number); - // Add suffix with luhn checksum (1 digit) to the card number string - FuriString* card_number_suffix; - card_number_suffix = furi_string_alloc(); - - furi_string_cat_printf(card_number_suffix, "-"); - furi_string_cat_printf(card_number_str, furi_string_get_cstr(card_number_suffix)); - // Free all not needed strings - furi_string_free(card_number_suffix); furi_string_printf( - dev_data->parsed_data, - "\e#Plantain\nN:%s\nBalance:%ld\n", - furi_string_get_cstr(card_number_str), - balance); - furi_string_free(card_number_str); + dev_data->parsed_data, "\e#Plantain\nN:%llu-\nBalance:%ld\n", card_number, balance); return true; } diff --git a/lib/nfc/parsers/plantain_parser.c b/lib/nfc/parsers/plantain_parser.c index 5328b5c4..c0e2a094 100644 --- a/lib/nfc/parsers/plantain_parser.c +++ b/lib/nfc/parsers/plantain_parser.c @@ -79,7 +79,7 @@ bool plantain_parser_parse(NfcDeviceData* dev_data) { // Point to block 0 of sector 0, value 0 temp_ptr = &data->block[0 * 4].value[0]; // Read first 7 bytes of block 0 of sector 0 from last to first and convert them to uint64_t - // 80 5C 23 8A 16 31 04 becomes 04 31 16 8A 23 5C 80, and equals to 36130104729284868 decimal + // 04 31 16 8A 23 5C 80 becomes 80 5C 23 8A 16 31 04, and equals to 36130104729284868 decimal uint8_t card_number_arr[7]; for(size_t i = 0; i < 7; i++) { card_number_arr[i] = temp_ptr[6 - i]; @@ -89,26 +89,9 @@ bool plantain_parser_parse(NfcDeviceData* dev_data) { for(size_t i = 0; i < 7; i++) { card_number = (card_number << 8) | card_number_arr[i]; } - // Convert card number to string - FuriString* card_number_str; - card_number_str = furi_string_alloc(); - // Should look like "361301047292848684" - furi_string_printf(card_number_str, "%llu", card_number); - // Add suffix with luhn checksum (1 digit) to the card number string - FuriString* card_number_suffix; - card_number_suffix = furi_string_alloc(); - - furi_string_cat_printf(card_number_suffix, "-"); - furi_string_cat_printf(card_number_str, furi_string_get_cstr(card_number_suffix)); - // Free all not needed strings - furi_string_free(card_number_suffix); furi_string_printf( - dev_data->parsed_data, - "\e#Plantain\nN:%s\nBalance:%ld\n", - furi_string_get_cstr(card_number_str), - balance); - furi_string_free(card_number_str); + dev_data->parsed_data, "\e#Plantain\nN:%llu-\nBalance:%ld\n", card_number, balance); return true; } diff --git a/lib/nfc/parsers/two_cities.c b/lib/nfc/parsers/two_cities.c index 2c6184a7..335248b2 100644 --- a/lib/nfc/parsers/two_cities.c +++ b/lib/nfc/parsers/two_cities.c @@ -107,7 +107,7 @@ bool two_cities_parser_parse(NfcDeviceData* dev_data) { // Point to block 0 of sector 0, value 0 temp_ptr = &data->block[0 * 4].value[0]; // Read first 7 bytes of block 0 of sector 0 from last to first and convert them to uint64_t - // 80 5C 23 8A 16 31 04 becomes 04 31 16 8A 23 5C 80, and equals to 36130104729284868 decimal + // 04 31 16 8A 23 5C 80 becomes 80 5C 23 8A 16 31 04, and equals to 36130104729284868 decimal uint8_t card_number_arr[7]; for(size_t i = 0; i < 7; i++) { card_number_arr[i] = temp_ptr[6 - i]; @@ -117,19 +117,6 @@ bool two_cities_parser_parse(NfcDeviceData* dev_data) { for(size_t i = 0; i < 7; i++) { card_number = (card_number << 8) | card_number_arr[i]; } - // Convert card number to string - FuriString* card_number_str; - card_number_str = furi_string_alloc(); - // Should look like "361301047292848684" - furi_string_printf(card_number_str, "%llu", card_number); - // Add suffix with luhn checksum (1 digit) to the card number string - FuriString* card_number_suffix; - card_number_suffix = furi_string_alloc(); - - furi_string_cat_printf(card_number_suffix, "-"); - furi_string_cat_printf(card_number_str, furi_string_get_cstr(card_number_suffix)); - // Free all not needed strings - furi_string_free(card_number_suffix); // ===== // --PLANTAIN-- @@ -149,12 +136,11 @@ bool two_cities_parser_parse(NfcDeviceData* dev_data) { furi_string_printf( dev_data->parsed_data, - "\e#Troika+Plantain\nPN: %s\nPB: %ld rur.\nTN: %ld\nTB: %d rur.\n", - furi_string_get_cstr(card_number_str), + "\e#Troika+Plantain\nPN: %llu-\nPB: %ld rur.\nTN: %ld\nTB: %d rur.\n", + card_number, balance, troika_number, troika_balance); - furi_string_free(card_number_str); return true; } diff --git a/lib/nfc/protocols/crypto1.c b/lib/nfc/protocols/crypto1.c index 2ac0ff08..f59651cf 100644 --- a/lib/nfc/protocols/crypto1.c +++ b/lib/nfc/protocols/crypto1.c @@ -4,7 +4,8 @@ // Algorithm from https://github.com/RfidResearchGroup/proxmark3.git -#define SWAPENDIAN(x) (x = (x >> 8 & 0xff00ff) | (x & 0xff00ff) << 8, x = x >> 16 | x << 16) +#define SWAPENDIAN(x) \ + ((x) = ((x) >> 8 & 0xff00ff) | ((x)&0xff00ff) << 8, (x) = (x) >> 16 | (x) << 16) #define LF_POLY_ODD (0x29CE5C) #define LF_POLY_EVEN (0x870804) diff --git a/lib/nfc/protocols/mifare_classic.c b/lib/nfc/protocols/mifare_classic.c index f4c7353a..c91e9c60 100644 --- a/lib/nfc/protocols/mifare_classic.c +++ b/lib/nfc/protocols/mifare_classic.c @@ -245,7 +245,8 @@ bool mf_classic_is_allowed_access_sector_trailer( case MfClassicActionKeyARead: { return false; } - case MfClassicActionKeyAWrite: { + case MfClassicActionKeyAWrite: + case MfClassicActionKeyBWrite: { return ( (key == MfClassicKeyA && (AC == 0x00 || AC == 0x01)) || (key == MfClassicKeyB && (AC == 0x04 || AC == 0x03))); @@ -253,11 +254,6 @@ bool mf_classic_is_allowed_access_sector_trailer( case MfClassicActionKeyBRead: { return (key == MfClassicKeyA && (AC == 0x00 || AC == 0x02 || AC == 0x01)); } - case MfClassicActionKeyBWrite: { - return ( - (key == MfClassicKeyA && (AC == 0x00 || AC == 0x01)) || - (key == MfClassicKeyB && (AC == 0x04 || AC == 0x03))); - } case MfClassicActionACRead: { return ( (key == MfClassicKeyA) || @@ -407,15 +403,16 @@ static bool mf_classic_auth( uint32_t block, uint64_t key, MfClassicKey key_type, - Crypto1* crypto) { + Crypto1* crypto, + bool skip_activate, + uint32_t cuid) { bool auth_success = false; - uint32_t cuid = 0; memset(tx_rx->tx_data, 0, sizeof(tx_rx->tx_data)); memset(tx_rx->tx_parity, 0, sizeof(tx_rx->tx_parity)); tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault; do { - if(!furi_hal_nfc_activate_nfca(200, &cuid)) break; + if(!skip_activate && !furi_hal_nfc_activate_nfca(200, &cuid)) break; if(key_type == MfClassicKeyA) { tx_rx->tx_data[0] = MF_CLASSIC_AUTH_KEY_A_CMD; } else { @@ -464,7 +461,23 @@ bool mf_classic_authenticate( furi_assert(tx_rx); Crypto1 crypto = {}; - bool key_found = mf_classic_auth(tx_rx, block_num, key, key_type, &crypto); + bool key_found = mf_classic_auth(tx_rx, block_num, key, key_type, &crypto, false, 0); + furi_hal_nfc_sleep(); + return key_found; +} + +bool mf_classic_authenticate_skip_activate( + FuriHalNfcTxRxContext* tx_rx, + uint8_t block_num, + uint64_t key, + MfClassicKey key_type, + bool skip_activate, + uint32_t cuid) { + furi_assert(tx_rx); + + Crypto1 crypto = {}; + bool key_found = + mf_classic_auth(tx_rx, block_num, key, key_type, &crypto, skip_activate, cuid); furi_hal_nfc_sleep(); return key_found; } @@ -487,7 +500,9 @@ bool mf_classic_auth_attempt( mf_classic_get_first_block_num_of_sector(auth_ctx->sector), key, MfClassicKeyA, - &crypto)) { + &crypto, + false, + 0)) { auth_ctx->key_a = key; found_key = true; } @@ -504,7 +519,9 @@ bool mf_classic_auth_attempt( mf_classic_get_first_block_num_of_sector(auth_ctx->sector), key, MfClassicKeyB, - &crypto)) { + &crypto, + false, + 0)) { auth_ctx->key_b = key; found_key = true; } @@ -572,7 +589,7 @@ void mf_classic_read_sector(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data, u if(!key_a_found) break; FURI_LOG_D(TAG, "Try to read blocks with key A"); key = nfc_util_bytes2num(sec_tr->key_a, sizeof(sec_tr->key_a)); - if(!mf_classic_auth(tx_rx, start_block, key, MfClassicKeyA, &crypto)) break; + if(!mf_classic_auth(tx_rx, start_block, key, MfClassicKeyA, &crypto, false, 0)) break; for(size_t i = start_block; i < start_block + total_blocks; i++) { if(!mf_classic_is_block_read(data, i)) { if(mf_classic_read_block(tx_rx, &crypto, i, &block_tmp)) { @@ -591,7 +608,7 @@ void mf_classic_read_sector(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data, u FURI_LOG_D(TAG, "Try to read blocks with key B"); key = nfc_util_bytes2num(sec_tr->key_b, sizeof(sec_tr->key_b)); furi_hal_nfc_sleep(); - if(!mf_classic_auth(tx_rx, start_block, key, MfClassicKeyB, &crypto)) break; + if(!mf_classic_auth(tx_rx, start_block, key, MfClassicKeyB, &crypto, false, 0)) break; for(size_t i = start_block; i < start_block + total_blocks; i++) { if(!mf_classic_is_block_read(data, i)) { if(mf_classic_read_block(tx_rx, &crypto, i, &block_tmp)) { @@ -635,7 +652,7 @@ static bool mf_classic_read_sector_with_reader( } // Auth to first block in sector - if(!mf_classic_auth(tx_rx, first_block, key, key_type, crypto)) { + if(!mf_classic_auth(tx_rx, first_block, key, key_type, crypto, false, 0)) { // Set key to MF_CLASSIC_NO_KEY to prevent further attempts if(key_type == MfClassicKeyA) { sector_reader->key_a = MF_CLASSIC_NO_KEY; @@ -734,7 +751,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ MfClassicKey access_key = MfClassicKeyA; // Read command - while(!command_processed) { + while(!command_processed) { //-V654 if(!is_encrypted) { crypto1_reset(&emulator->crypto); memcpy(plain_data, tx_rx->rx_data, tx_rx->rx_bits / 8); @@ -850,7 +867,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ if(mf_classic_is_sector_trailer(block)) { if(!mf_classic_is_allowed_access( emulator, block, access_key, MfClassicActionKeyARead)) { - memset(block_data, 0, 6); + memset(block_data, 0, 6); //-V1086 } if(!mf_classic_is_allowed_access( emulator, block, access_key, MfClassicActionKeyBRead)) { @@ -860,22 +877,16 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ emulator, block, access_key, MfClassicActionACRead)) { memset(&block_data[6], 0, 4); } - } else { - if(!mf_classic_is_allowed_access( - emulator, block, access_key, MfClassicActionDataRead)) { - // Send NACK - uint8_t nack = 0x04; - if(is_encrypted) { - crypto1_encrypt( - &emulator->crypto, NULL, &nack, 4, tx_rx->tx_data, tx_rx->tx_parity); - } else { - tx_rx->tx_data[0] = nack; - } - tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent; - tx_rx->tx_bits = 4; - furi_hal_nfc_tx_rx(tx_rx, 300); - break; - } + } else if(!mf_classic_is_allowed_access( + emulator, block, access_key, MfClassicActionDataRead)) { + // Send NACK + uint8_t nack = 0x04; + crypto1_encrypt( + &emulator->crypto, NULL, &nack, 4, tx_rx->tx_data, tx_rx->tx_parity); + tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent; + tx_rx->tx_bits = 4; + furi_hal_nfc_tx_rx(tx_rx, 300); + break; } nfca_append_crc16(block_data, 16); @@ -908,7 +919,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ if(mf_classic_is_sector_trailer(block)) { if(mf_classic_is_allowed_access( emulator, block, access_key, MfClassicActionKeyAWrite)) { - memcpy(block_data, plain_data, 6); + memcpy(block_data, plain_data, 6); //-V1086 } if(mf_classic_is_allowed_access( emulator, block, access_key, MfClassicActionKeyBWrite)) { @@ -924,7 +935,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ memcpy(block_data, plain_data, MF_CLASSIC_BLOCK_SIZE); } } - if(memcmp(block_data, emulator->data.block[block].value, MF_CLASSIC_BLOCK_SIZE)) { + if(memcmp(block_data, emulator->data.block[block].value, MF_CLASSIC_BLOCK_SIZE) != 0) { memcpy(emulator->data.block[block].value, block_data, MF_CLASSIC_BLOCK_SIZE); emulator->data_changed = true; } @@ -971,7 +982,7 @@ bool mf_classic_write_block( do { furi_hal_nfc_sleep(); - if(!mf_classic_auth(tx_rx, block_num, key, key_type, &crypto)) { + if(!mf_classic_auth(tx_rx, block_num, key, key_type, &crypto, false, 0)) { FURI_LOG_D(TAG, "Auth fail"); break; } @@ -1060,7 +1071,8 @@ bool mf_classic_write_sector( bool write_success = true; for(size_t i = first_block; i < first_block + total_blocks; i++) { // Compare blocks - if(memcmp(dest_data->block[i].value, src_data->block[i].value, MF_CLASSIC_BLOCK_SIZE)) { + if(memcmp(dest_data->block[i].value, src_data->block[i].value, MF_CLASSIC_BLOCK_SIZE) != + 0) { bool key_a_write_allowed = mf_classic_is_allowed_access_data_block( dest_data, i, MfClassicKeyA, MfClassicActionDataWrite); bool key_b_write_allowed = mf_classic_is_allowed_access_data_block( diff --git a/lib/nfc/protocols/mifare_classic.h b/lib/nfc/protocols/mifare_classic.h index 9a0bb579..74beceb6 100644 --- a/lib/nfc/protocols/mifare_classic.h +++ b/lib/nfc/protocols/mifare_classic.h @@ -156,6 +156,14 @@ bool mf_classic_authenticate( uint64_t key, MfClassicKey key_type); +bool mf_classic_authenticate_skip_activate( + FuriHalNfcTxRxContext* tx_rx, + uint8_t block_num, + uint64_t key, + MfClassicKey key_type, + bool skip_activate, + uint32_t cuid); + bool mf_classic_auth_attempt( FuriHalNfcTxRxContext* tx_rx, MfClassicAuthContext* auth_ctx, diff --git a/lib/nfc/protocols/mifare_desfire.c b/lib/nfc/protocols/mifare_desfire.c index b2247bf2..23308ae9 100644 --- a/lib/nfc/protocols/mifare_desfire.c +++ b/lib/nfc/protocols/mifare_desfire.c @@ -108,7 +108,7 @@ void mf_df_cat_version(MifareDesfireVersion* version, FuriString* out) { } void mf_df_cat_free_mem(MifareDesfireFreeMemory* free_mem, FuriString* out) { - furi_string_cat_printf(out, "freeMem %ld\n", free_mem->bytes); + furi_string_cat_printf(out, "freeMem %lu\n", free_mem->bytes); } void mf_df_cat_key_settings(MifareDesfireKeySettings* ks, FuriString* out) { @@ -191,10 +191,10 @@ void mf_df_cat_file(MifareDesfireFile* file, FuriString* out) { case MifareDesfireFileTypeValue: size = 4; furi_string_cat_printf( - out, "lo %ld hi %ld\n", file->settings.value.lo_limit, file->settings.value.hi_limit); + out, "lo %lu hi %lu\n", file->settings.value.lo_limit, file->settings.value.hi_limit); furi_string_cat_printf( out, - "limit %ld enabled %d\n", + "limit %lu enabled %d\n", file->settings.value.limited_credit_value, file->settings.value.limited_credit_enabled); break; @@ -203,7 +203,7 @@ void mf_df_cat_file(MifareDesfireFile* file, FuriString* out) { size = file->settings.record.size; num = file->settings.record.cur; furi_string_cat_printf(out, "size %d\n", size); - furi_string_cat_printf(out, "num %d max %ld\n", num, file->settings.record.max); + furi_string_cat_printf(out, "num %d max %lu\n", num, file->settings.record.max); break; } uint8_t* data = file->contents; @@ -220,8 +220,9 @@ void mf_df_cat_file(MifareDesfireFile* file, FuriString* out) { } } for(int i = 0; i < 4 && ch + i < size; i++) { - if(isprint(data[rec * size + ch + i])) { - furi_string_cat_printf(out, "%c", data[rec * size + ch + i]); + const size_t data_index = rec * size + ch + i; + if(isprint(data[data_index])) { + furi_string_cat_printf(out, "%c", data[data_index]); } else { furi_string_cat_printf(out, "."); } @@ -547,7 +548,8 @@ bool mf_df_read_card(FuriHalNfcTxRxContext* tx_rx, MifareDesfireData* data) { for(MifareDesfireApplication* app = data->app_head; app; app = app->next) { tx_rx->tx_bits = 8 * mf_df_prepare_select_application(tx_rx->tx_data, app->id); if(!furi_hal_nfc_tx_rx_full(tx_rx) || - !mf_df_parse_select_application_response(tx_rx->rx_data, tx_rx->rx_bits / 8)) { + !mf_df_parse_select_application_response( + tx_rx->rx_data, tx_rx->rx_bits / 8)) { //-V1051 FURI_LOG_W(TAG, "Bad exchange selecting application"); continue; } diff --git a/lib/nfc/protocols/mifare_ultralight.c b/lib/nfc/protocols/mifare_ultralight.c index 85e234bd..d642e290 100644 --- a/lib/nfc/protocols/mifare_ultralight.c +++ b/lib/nfc/protocols/mifare_ultralight.c @@ -170,6 +170,7 @@ bool mf_ultralight_read_version( } bool mf_ultralight_authenticate(FuriHalNfcTxRxContext* tx_rx, uint32_t key, uint16_t* pack) { + furi_assert(pack); bool authenticated = false; do { @@ -189,9 +190,7 @@ bool mf_ultralight_authenticate(FuriHalNfcTxRxContext* tx_rx, uint32_t key, uint break; } - if(pack != NULL) { - *pack = (tx_rx->rx_data[1] << 8) | tx_rx->rx_data[0]; - } + *pack = (tx_rx->rx_data[1] << 8) | tx_rx->rx_data[0]; FURI_LOG_I(TAG, "Auth success. Password: %08lX. PACK: %04X", key, *pack); authenticated = true; @@ -492,7 +491,7 @@ MfUltralightConfigPages* mf_ultralight_get_config_pages(MfUltralightData* data) } else if( data->type >= MfUltralightTypeNTAGI2CPlus1K && data->type <= MfUltralightTypeNTAGI2CPlus2K) { - return (MfUltralightConfigPages*)&data->data[0xe3 * 4]; + return (MfUltralightConfigPages*)&data->data[0xe3 * 4]; //-V641 } else { return NULL; } @@ -561,7 +560,7 @@ bool mf_ultralight_read_pages_direct( FURI_LOG_D(TAG, "Failed to read pages %d - %d", start_index, start_index + 3); return false; } - memcpy(data, tx_rx->rx_data, 16); + memcpy(data, tx_rx->rx_data, 16); //-V1086 return true; } @@ -584,7 +583,8 @@ bool mf_ultralight_read_pages( curr_sector_index = tag_sector; } - FURI_LOG_D(TAG, "Reading pages %d - %d", i, i + (valid_pages > 4 ? 4 : valid_pages) - 1); + FURI_LOG_D( + TAG, "Reading pages %zu - %zu", i, i + (valid_pages > 4 ? 4 : valid_pages) - 1U); tx_rx->tx_data[0] = MF_UL_READ_CMD; tx_rx->tx_data[1] = tag_page; tx_rx->tx_bits = 16; @@ -593,9 +593,9 @@ bool mf_ultralight_read_pages( if(!furi_hal_nfc_tx_rx(tx_rx, 50) || tx_rx->rx_bits < 16 * 8) { FURI_LOG_D( TAG, - "Failed to read pages %d - %d", + "Failed to read pages %zu - %zu", i, - i + (valid_pages > 4 ? 4 : valid_pages) - 1); + i + (valid_pages > 4 ? 4 : valid_pages) - 1U); break; } @@ -857,7 +857,7 @@ static void mf_ul_ntag_i2c_fill_cross_area_read( } if(apply) { - while(tx_page_offset < 0 && page_length > 0) { + while(tx_page_offset < 0 && page_length > 0) { //-V614 ++tx_page_offset; ++data_page_offset; --page_length; @@ -987,9 +987,9 @@ static bool mf_ul_check_lock(MfUltralightEmulator* emulator, int16_t write_page) switch(emulator->data.type) { // low byte LSB range, MSB range case MfUltralightTypeNTAG203: - if(write_page >= 16 && write_page <= 27) + if(write_page >= 16 && write_page <= 27) //-V560 shift = (write_page - 16) / 4 + 1; - else if(write_page >= 28 && write_page <= 39) + else if(write_page >= 28 && write_page <= 39) //-V560 shift = (write_page - 28) / 4 + 5; else if(write_page == 41) shift = 12; @@ -1216,7 +1216,7 @@ static void mf_ul_emulate_write( page_buff[0] = new_locks & 0xff; page_buff[1] = new_locks >> 8; page_buff[2] = new_block_locks; - if(emulator->data.type >= MfUltralightTypeUL21 && + if(emulator->data.type >= MfUltralightTypeUL21 && //-V1016 emulator->data.type <= MfUltralightTypeNTAG216) page_buff[3] = MF_UL_TEARING_FLAG_DEFAULT; else diff --git a/lib/one_wire/ibutton/ibutton_key.c b/lib/one_wire/ibutton/ibutton_key.c index 2c0f7fa2..7b7571a2 100644 --- a/lib/one_wire/ibutton/ibutton_key.c +++ b/lib/one_wire/ibutton/ibutton_key.c @@ -62,8 +62,6 @@ const char* ibutton_key_get_string_by_type(iButtonKeyType key_type) { break; default: furi_crash("Invalid iButton type"); - return ""; - break; } } diff --git a/lib/one_wire/ibutton/ibutton_worker_modes.c b/lib/one_wire/ibutton/ibutton_worker_modes.c index b1e5904c..b284940e 100644 --- a/lib/one_wire/ibutton/ibutton_worker_modes.c +++ b/lib/one_wire/ibutton/ibutton_worker_modes.c @@ -130,7 +130,6 @@ bool ibutton_worker_read_comparator(iButtonWorker* worker) { ibutton_key_set_data(worker->key_p, worker->key_data, ibutton_key_get_max_size()); result = true; break; - break; default: break; } diff --git a/lib/one_wire/ibutton/ibutton_writer.c b/lib/one_wire/ibutton/ibutton_writer.c index 203c4fc0..84d12249 100644 --- a/lib/one_wire/ibutton/ibutton_writer.c +++ b/lib/one_wire/ibutton/ibutton_writer.c @@ -72,7 +72,7 @@ static bool writer_write_TM2004(iButtonWriter* writer, iButtonKey* key) { writer_write_one_bit(writer, 1, 50000); // read written key byte - answer = onewire_host_read(writer->host); + answer = onewire_host_read(writer->host); //-V519 // check that written and read are same if(ibutton_key_get_data_p(key)[i] != answer) { diff --git a/lib/one_wire/ibutton/protocols/protocol_cyfral.c b/lib/one_wire/ibutton/protocols/protocol_cyfral.c index 51c42824..a5f459bc 100644 --- a/lib/one_wire/ibutton/protocols/protocol_cyfral.c +++ b/lib/one_wire/ibutton/protocols/protocol_cyfral.c @@ -181,7 +181,7 @@ static bool protocol_cyfral_decoder_feed(ProtocolCyfral* proto, bool level, uint cyfral->index++; } - // succefully read 8 nibbles + // successfully read 8 nibbles if(cyfral->index == 8) { cyfral->state = CYFRAL_READ_STOP_NIBBLE; } @@ -270,10 +270,10 @@ static LevelDuration protocol_cyfral_encoder_yield(ProtocolCyfral* proto) { // start word (0b0001) switch(proto->encoder.index) { case 0: - result = level_duration_make(false, CYFRAL_0_LOW); + result = level_duration_make(false, CYFRAL_0_LOW); //-V1037 break; case 1: - result = level_duration_make(true, CYFRAL_0_HI); + result = level_duration_make(true, CYFRAL_0_HI); //-V1037 break; case 2: result = level_duration_make(false, CYFRAL_0_LOW); @@ -341,4 +341,4 @@ const ProtocolBase protocol_cyfral = { .start = (ProtocolEncoderStart)protocol_cyfral_encoder_start, .yield = (ProtocolEncoderYield)protocol_cyfral_encoder_yield, }, -}; \ No newline at end of file +}; diff --git a/lib/one_wire/ibutton/protocols/protocol_metakom.c b/lib/one_wire/ibutton/protocols/protocol_metakom.c index 00f16e45..ff65c667 100644 --- a/lib/one_wire/ibutton/protocols/protocol_metakom.c +++ b/lib/one_wire/ibutton/protocols/protocol_metakom.c @@ -248,14 +248,14 @@ static LevelDuration protocol_metakom_encoder_yield(ProtocolMetakom* proto) { if(proto->encoder.index == 0) { // sync bit result = level_duration_make(false, METAKOM_PERIOD); - } else if(proto->encoder.index >= 1 && proto->encoder.index <= 6) { + } else if(proto->encoder.index <= 6) { // start word (0b010) switch(proto->encoder.index) { case 1: - result = level_duration_make(true, METAKOM_0_LOW); + result = level_duration_make(true, METAKOM_0_LOW); //-V1037 break; case 2: - result = level_duration_make(false, METAKOM_0_HI); + result = level_duration_make(false, METAKOM_0_HI); //-V1037 break; case 3: result = level_duration_make(true, METAKOM_1_LOW); @@ -317,4 +317,4 @@ const ProtocolBase protocol_metakom = { .start = (ProtocolEncoderStart)protocol_metakom_encoder_start, .yield = (ProtocolEncoderYield)protocol_metakom_encoder_yield, }, -}; \ No newline at end of file +}; diff --git a/lib/one_wire/one_wire_slave.c b/lib/one_wire/one_wire_slave.c index af04cfda..ad9c34b1 100644 --- a/lib/one_wire/one_wire_slave.c +++ b/lib/one_wire/one_wire_slave.c @@ -41,7 +41,7 @@ uint32_t onewire_slave_wait_while_gpio_is(OneWireSlave* bus, uint32_t time, cons uint32_t time_ticks = time * furi_hal_cortex_instructions_per_microsecond(); uint32_t time_captured; - do { + do { //-V1044 time_captured = DWT->CYCCNT; if(furi_hal_ibutton_pin_get_level() != pin_value) { uint32_t remaining_time = time_ticks - (time_captured - start); @@ -155,8 +155,10 @@ bool onewire_slave_receive_and_process_cmd(OneWireSlave* bus) { uint8_t cmd; onewire_slave_receive(bus, &cmd, 1); - if(bus->error == RESET_IN_PROGRESS) return true; - if(bus->error != NO_ERROR) return false; + if(bus->error == RESET_IN_PROGRESS) + return true; + else if(bus->error != NO_ERROR) + return false; switch(cmd) { case 0xF0: @@ -172,10 +174,8 @@ bool onewire_slave_receive_and_process_cmd(OneWireSlave* bus) { default: // Unknown command bus->error = INCORRECT_ONEWIRE_CMD; + return false; } - - if(bus->error == RESET_IN_PROGRESS) return true; - return (bus->error == NO_ERROR); } bool onewire_slave_bus_start(OneWireSlave* bus) { diff --git a/lib/print/printf_tiny.c b/lib/print/printf_tiny.c index 6e47f652..54f192a6 100644 --- a/lib/print/printf_tiny.c +++ b/lib/print/printf_tiny.c @@ -541,7 +541,7 @@ static size_t _etoa( exp2 = (int)(expval * 3.321928094887362 + 0.5); const double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453; const double z2 = z * z; - conv.U = ((uint64_t)exp2 + 1023) << 52U; + conv.U = ((uint64_t)exp2 + 1023) << 52U; //-V519 // compute exp(z) using continued fractions, see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex conv.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14))))); // correct for rounding errors diff --git a/lib/subghz/blocks/generic.c b/lib/subghz/blocks/generic.c index 1bad5f0a..94114676 100644 --- a/lib/subghz/blocks/generic.c +++ b/lib/subghz/blocks/generic.c @@ -71,7 +71,7 @@ bool subghz_block_generic_serialize( uint8_t key_data[sizeof(uint64_t)] = {0}; for(size_t i = 0; i < sizeof(uint64_t); i++) { - key_data[sizeof(uint64_t) - i - 1] = (instance->data >> i * 8) & 0xFF; + key_data[sizeof(uint64_t) - i - 1] = (instance->data >> (i * 8)) & 0xFF; } if(!flipper_format_write_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) { diff --git a/lib/subghz/blocks/math.h b/lib/subghz/blocks/math.h index a4f04271..87c209f7 100644 --- a/lib/subghz/blocks/math.h +++ b/lib/subghz/blocks/math.h @@ -8,8 +8,7 @@ #define bit_set(value, bit) ((value) |= (1UL << (bit))) #define bit_clear(value, bit) ((value) &= ~(1UL << (bit))) #define bit_write(value, bit, bitvalue) (bitvalue ? bit_set(value, bit) : bit_clear(value, bit)) -#define DURATION_DIFF(x, y) ((x < y) ? (y - x) : (x - y)) -#define abs(x) ((x) > 0 ? (x) : -(x)) +#define DURATION_DIFF(x, y) (((x) < (y)) ? ((y) - (x)) : ((x) - (y))) #ifdef __cplusplus extern "C" { diff --git a/lib/subghz/environment.h b/lib/subghz/environment.h index 5f8fcf1f..e994f7c9 100644 --- a/lib/subghz/environment.h +++ b/lib/subghz/environment.h @@ -26,7 +26,7 @@ void subghz_environment_free(SubGhzEnvironment* instance); * Downloading the manufacture key file. * @param instance Pointer to a SubGhzEnvironment instance * @param filename Full path to the file - * @return true On succes + * @return true On success */ bool subghz_environment_load_keystore(SubGhzEnvironment* instance, const char* filename); diff --git a/lib/subghz/protocols/bett.c b/lib/subghz/protocols/bett.c index 2dd39af9..644d80fd 100644 --- a/lib/subghz/protocols/bett.c +++ b/lib/subghz/protocols/bett.c @@ -242,7 +242,6 @@ void subghz_protocol_decoder_bett_feed(void* context, bool level, uint32_t durat if(!level) { if(DURATION_DIFF(duration, subghz_protocol_bett_const.te_short * 44) < (subghz_protocol_bett_const.te_delta * 15)) { - instance->decoder.parser_step = BETTDecoderStepSaveDuration; if(instance->decoder.decode_count_bit == subghz_protocol_bett_const.min_count_bit_for_found) { instance->generic.data = instance->decoder.decode_data; diff --git a/lib/subghz/protocols/came.c b/lib/subghz/protocols/came.c index 1ac4ec05..bed26d7d 100644 --- a/lib/subghz/protocols/came.c +++ b/lib/subghz/protocols/came.c @@ -119,7 +119,9 @@ static bool subghz_protocol_encoder_came_get_upload(SubGhzProtocolEncoderCame* i //Send header instance->encoder.upload[index++] = level_duration_make( false, - ((instance->generic.data_count_bit == CAME_24_COUNT_BIT) ? + (((instance->generic.data_count_bit == CAME_24_COUNT_BIT) || + (instance->generic.data_count_bit == + subghz_protocol_came_const.min_count_bit_for_found)) ? (uint32_t)subghz_protocol_came_const.te_short * 76 : (uint32_t)subghz_protocol_came_const.te_short * 39)); //Send start bit diff --git a/lib/subghz/protocols/holtek.c b/lib/subghz/protocols/holtek.c index 39e27bbf..8aaad3b7 100644 --- a/lib/subghz/protocols/holtek.c +++ b/lib/subghz/protocols/holtek.c @@ -240,7 +240,6 @@ void subghz_protocol_decoder_holtek_feed(void* context, bool level, uint32_t dur if(!level) { if(duration >= ((uint32_t)subghz_protocol_holtek_const.te_short * 10 + subghz_protocol_holtek_const.te_delta)) { - instance->decoder.parser_step = HoltekDecoderStepSaveDuration; if(instance->decoder.decode_count_bit == subghz_protocol_holtek_const.min_count_bit_for_found) { if((instance->decoder.decode_data & HOLTEK_HEADER_MASK) == HOLTEK_HEADER) { diff --git a/lib/subghz/protocols/holtek_ht12x.c b/lib/subghz/protocols/holtek_ht12x.c new file mode 100644 index 00000000..169387de --- /dev/null +++ b/lib/subghz/protocols/holtek_ht12x.c @@ -0,0 +1,400 @@ +#include "holtek_ht12x.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +/* + * Help + * https://www.holtek.com/documents/10179/116711/HT12A_Ev130.pdf + * + */ + +#define TAG "SubGhzProtocolHoltek_HT12X" + +#define DIP_PATTERN "%c%c%c%c%c%c%c%c" +#define CNT_TO_DIP(dip) \ + (dip & 0x0080 ? '0' : '1'), (dip & 0x0040 ? '0' : '1'), (dip & 0x0020 ? '0' : '1'), \ + (dip & 0x0010 ? '0' : '1'), (dip & 0x0008 ? '0' : '1'), (dip & 0x0004 ? '0' : '1'), \ + (dip & 0x0002 ? '0' : '1'), (dip & 0x0001 ? '0' : '1') + +static const SubGhzBlockConst subghz_protocol_holtek_th12x_const = { + .te_short = 320, + .te_long = 640, + .te_delta = 200, + .min_count_bit_for_found = 12, +}; + +struct SubGhzProtocolDecoderHoltek_HT12X { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + + uint32_t te; + uint32_t last_data; +}; + +struct SubGhzProtocolEncoderHoltek_HT12X { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; + + uint32_t te; +}; + +typedef enum { + Holtek_HT12XDecoderStepReset = 0, + Holtek_HT12XDecoderStepFoundStartBit, + Holtek_HT12XDecoderStepSaveDuration, + Holtek_HT12XDecoderStepCheckDuration, +} Holtek_HT12XDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_holtek_th12x_decoder = { + .alloc = subghz_protocol_decoder_holtek_th12x_alloc, + .free = subghz_protocol_decoder_holtek_th12x_free, + + .feed = subghz_protocol_decoder_holtek_th12x_feed, + .reset = subghz_protocol_decoder_holtek_th12x_reset, + + .get_hash_data = subghz_protocol_decoder_holtek_th12x_get_hash_data, + .serialize = subghz_protocol_decoder_holtek_th12x_serialize, + .deserialize = subghz_protocol_decoder_holtek_th12x_deserialize, + .get_string = subghz_protocol_decoder_holtek_th12x_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_holtek_th12x_encoder = { + .alloc = subghz_protocol_encoder_holtek_th12x_alloc, + .free = subghz_protocol_encoder_holtek_th12x_free, + + .deserialize = subghz_protocol_encoder_holtek_th12x_deserialize, + .stop = subghz_protocol_encoder_holtek_th12x_stop, + .yield = subghz_protocol_encoder_holtek_th12x_yield, +}; + +const SubGhzProtocol subghz_protocol_holtek_th12x = { + .name = SUBGHZ_PROTOCOL_HOLTEK_HT12X_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_315 | + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_holtek_th12x_decoder, + .encoder = &subghz_protocol_holtek_th12x_encoder, +}; + +void* subghz_protocol_encoder_holtek_th12x_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderHoltek_HT12X* instance = + malloc(sizeof(SubGhzProtocolEncoderHoltek_HT12X)); + + instance->base.protocol = &subghz_protocol_holtek_th12x; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 128; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_holtek_th12x_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderHoltek_HT12X* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderHoltek_HT12X instance + * @return true On success + */ +static bool + subghz_protocol_encoder_holtek_th12x_get_upload(SubGhzProtocolEncoderHoltek_HT12X* instance) { + furi_assert(instance); + + size_t index = 0; + size_t size_upload = (instance->generic.data_count_bit * 2) + 2; + if(size_upload > instance->encoder.size_upload) { + FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer."); + return false; + } else { + instance->encoder.size_upload = size_upload; + } + + //Send header + instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)instance->te * 36); + //Send start bit + instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)instance->te); + //Send key data + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)instance->te * 2); + instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)instance->te); + } else { + //send bit 0 + instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)instance->te); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)instance->te * 2); + } + } + return true; +} + +bool subghz_protocol_encoder_holtek_th12x_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderHoltek_HT12X* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + if(!flipper_format_read_uint32(flipper_format, "TE", (uint32_t*)&instance->te, 1)) { + FURI_LOG_E(TAG, "Missing TE"); + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_holtek_th12x_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + if(!subghz_protocol_encoder_holtek_th12x_get_upload(instance)) break; + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_holtek_th12x_stop(void* context) { + SubGhzProtocolEncoderHoltek_HT12X* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_holtek_th12x_yield(void* context) { + SubGhzProtocolEncoderHoltek_HT12X* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +void* subghz_protocol_decoder_holtek_th12x_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderHoltek_HT12X* instance = + malloc(sizeof(SubGhzProtocolDecoderHoltek_HT12X)); + instance->base.protocol = &subghz_protocol_holtek_th12x; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_holtek_th12x_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderHoltek_HT12X* instance = context; + free(instance); +} + +void subghz_protocol_decoder_holtek_th12x_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderHoltek_HT12X* instance = context; + instance->decoder.parser_step = Holtek_HT12XDecoderStepReset; +} + +void subghz_protocol_decoder_holtek_th12x_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderHoltek_HT12X* instance = context; + + switch(instance->decoder.parser_step) { + case Holtek_HT12XDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_holtek_th12x_const.te_short * 36) < + subghz_protocol_holtek_th12x_const.te_delta * 36)) { + //Found Preambula + instance->decoder.parser_step = Holtek_HT12XDecoderStepFoundStartBit; + } + break; + case Holtek_HT12XDecoderStepFoundStartBit: + if((level) && (DURATION_DIFF(duration, subghz_protocol_holtek_th12x_const.te_short) < + subghz_protocol_holtek_th12x_const.te_delta)) { + //Found StartBit + instance->decoder.parser_step = Holtek_HT12XDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->te = duration; + } else { + instance->decoder.parser_step = Holtek_HT12XDecoderStepReset; + } + break; + case Holtek_HT12XDecoderStepSaveDuration: + //save duration + if(!level) { + if(duration >= ((uint32_t)subghz_protocol_holtek_th12x_const.te_short * 10 + + subghz_protocol_holtek_th12x_const.te_delta)) { + if(instance->decoder.decode_count_bit == + subghz_protocol_holtek_th12x_const.min_count_bit_for_found) { + if((instance->last_data == instance->decoder.decode_data) && + instance->last_data) { + instance->te /= (instance->decoder.decode_count_bit * 3 + 1); + + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + instance->last_data = instance->decoder.decode_data; + } + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->te = 0; + instance->decoder.parser_step = Holtek_HT12XDecoderStepFoundStartBit; + break; + } else { + instance->decoder.te_last = duration; + instance->te += duration; + instance->decoder.parser_step = Holtek_HT12XDecoderStepCheckDuration; + } + } else { + instance->decoder.parser_step = Holtek_HT12XDecoderStepReset; + } + break; + case Holtek_HT12XDecoderStepCheckDuration: + if(level) { + instance->te += duration; + if((DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_holtek_th12x_const.te_long) < + subghz_protocol_holtek_th12x_const.te_delta * 2) && + (DURATION_DIFF(duration, subghz_protocol_holtek_th12x_const.te_short) < + subghz_protocol_holtek_th12x_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = Holtek_HT12XDecoderStepSaveDuration; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_holtek_th12x_const.te_short) < + subghz_protocol_holtek_th12x_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_holtek_th12x_const.te_long) < + subghz_protocol_holtek_th12x_const.te_delta * 2)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = Holtek_HT12XDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = Holtek_HT12XDecoderStepReset; + } + } else { + instance->decoder.parser_step = Holtek_HT12XDecoderStepReset; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_holtek_th12x_check_remote_controller(SubGhzBlockGeneric* instance) { + instance->btn = instance->data & 0x0F; + instance->cnt = (instance->data >> 4) & 0xFF; +} + +uint8_t subghz_protocol_decoder_holtek_th12x_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderHoltek_HT12X* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_holtek_th12x_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderHoltek_HT12X* instance = context; + bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + if(res && !flipper_format_write_uint32(flipper_format, "TE", &instance->te, 1)) { + FURI_LOG_E(TAG, "Unable to add TE"); + res = false; + } + return res; +} + +bool subghz_protocol_decoder_holtek_th12x_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderHoltek_HT12X* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_holtek_th12x_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + if(!flipper_format_read_uint32(flipper_format, "TE", (uint32_t*)&instance->te, 1)) { + FURI_LOG_E(TAG, "Missing TE"); + break; + } + ret = true; + } while(false); + return ret; +} + +static void subghz_protocol_holtek_th12x_event_serialize(uint8_t event, FuriString* output) { + furi_string_cat_printf( + output, + "%s%s%s%s\r\n", + (((event >> 3) & 0x1) == 0x0 ? "B1 " : ""), + (((event >> 2) & 0x1) == 0x0 ? "B2 " : ""), + (((event >> 1) & 0x1) == 0x0 ? "B3 " : ""), + (((event >> 0) & 0x1) == 0x0 ? "B4 " : "")); +} + +void subghz_protocol_decoder_holtek_th12x_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderHoltek_HT12X* instance = context; + subghz_protocol_holtek_th12x_check_remote_controller(&instance->generic); + + furi_string_cat_printf( + output, + "%s %db\r\n" + "Key:0x%03lX\r\n" + "Btn: ", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data & 0xFFF)); + subghz_protocol_holtek_th12x_event_serialize(instance->generic.btn, output); + furi_string_cat_printf( + output, + "DIP:" DIP_PATTERN "\r\n" + "Te:%luus\r\n", + CNT_TO_DIP(instance->generic.cnt), + instance->te); +} diff --git a/lib/subghz/protocols/holtek_ht12x.h b/lib/subghz/protocols/holtek_ht12x.h new file mode 100644 index 00000000..7b5c31dd --- /dev/null +++ b/lib/subghz/protocols/holtek_ht12x.h @@ -0,0 +1,107 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_HOLTEK_HT12X_NAME "Holtek_HT12X" + +typedef struct SubGhzProtocolDecoderHoltek_HT12X SubGhzProtocolDecoderHoltek_HT12X; +typedef struct SubGhzProtocolEncoderHoltek_HT12X SubGhzProtocolEncoderHoltek_HT12X; + +extern const SubGhzProtocolDecoder subghz_protocol_holtek_th12x_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_holtek_th12x_encoder; +extern const SubGhzProtocol subghz_protocol_holtek_th12x; + +/** + * Allocate SubGhzProtocolEncoderHoltek_HT12X. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderHoltek_HT12X* pointer to a SubGhzProtocolEncoderHoltek_HT12X instance + */ +void* subghz_protocol_encoder_holtek_th12x_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderHoltek_HT12X. + * @param context Pointer to a SubGhzProtocolEncoderHoltek_HT12X instance + */ +void subghz_protocol_encoder_holtek_th12x_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderHoltek_HT12X instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_holtek_th12x_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderHoltek_HT12X instance + */ +void subghz_protocol_encoder_holtek_th12x_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderHoltek_HT12X instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_holtek_th12x_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderHoltek_HT12X. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderHoltek_HT12X* pointer to a SubGhzProtocolDecoderHoltek_HT12X instance + */ +void* subghz_protocol_decoder_holtek_th12x_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderHoltek_HT12X. + * @param context Pointer to a SubGhzProtocolDecoderHoltek_HT12X instance + */ +void subghz_protocol_decoder_holtek_th12x_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderHoltek_HT12X. + * @param context Pointer to a SubGhzProtocolDecoderHoltek_HT12X instance + */ +void subghz_protocol_decoder_holtek_th12x_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderHoltek_HT12X instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_holtek_th12x_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderHoltek_HT12X instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_holtek_th12x_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderHoltek_HT12X. + * @param context Pointer to a SubGhzProtocolDecoderHoltek_HT12X instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_holtek_th12x_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderHoltek_HT12X. + * @param context Pointer to a SubGhzProtocolDecoderHoltek_HT12X instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_holtek_th12x_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderHoltek_HT12X instance + * @param output Resulting text + */ +void subghz_protocol_decoder_holtek_th12x_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/keeloq.c b/lib/subghz/protocols/keeloq.c index eef1d093..6a9c3468 100644 --- a/lib/subghz/protocols/keeloq.c +++ b/lib/subghz/protocols/keeloq.c @@ -119,8 +119,8 @@ void subghz_protocol_encoder_keeloq_free(void* context) { */ static bool subghz_protocol_keeloq_gen_data(SubGhzProtocolEncoderKeeloq* instance, uint8_t btn) { instance->generic.cnt++; - uint32_t fix = btn << 28 | instance->generic.serial; - uint32_t decrypt = btn << 28 | + uint32_t fix = (uint32_t)btn << 28 | instance->generic.serial; + uint32_t decrypt = (uint32_t)btn << 28 | (instance->generic.serial & 0x3FF) << 16 | //ToDo in some protocols the discriminator is 0 instance->generic.cnt; @@ -271,7 +271,8 @@ bool subghz_protocol_encoder_keeloq_deserialize(void* context, FlipperFormat* fl subghz_protocol_keeloq_check_remote_controller( &instance->generic, instance->keystore, &instance->manufacture_name); - if(strcmp(instance->manufacture_name, "DoorHan")) { + if(strcmp(instance->manufacture_name, "DoorHan") != 0) { + FURI_LOG_E(TAG, "Wrong manufacturer name"); break; } @@ -287,7 +288,7 @@ bool subghz_protocol_encoder_keeloq_deserialize(void* context, FlipperFormat* fl } uint8_t key_data[sizeof(uint64_t)] = {0}; for(size_t i = 0; i < sizeof(uint64_t); i++) { - key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> i * 8) & 0xFF; + key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> (i * 8)) & 0xFF; } if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) { FURI_LOG_E(TAG, "Unable to add Key"); diff --git a/lib/subghz/protocols/kia.c b/lib/subghz/protocols/kia.c index 997f8e1d..a5d9e37e 100644 --- a/lib/subghz/protocols/kia.c +++ b/lib/subghz/protocols/kia.c @@ -142,7 +142,7 @@ void subghz_protocol_decoder_kia_feed(void* context, bool level, uint32_t durati case KIADecoderStepSaveDuration: if(level) { if(duration >= - (uint32_t)(subghz_protocol_kia_const.te_long + subghz_protocol_kia_const.te_delta * 2)) { + (subghz_protocol_kia_const.te_long + subghz_protocol_kia_const.te_delta * 2UL)) { //Found stop bit instance->decoder.parser_step = KIADecoderStepReset; if(instance->decoder.decode_count_bit == diff --git a/lib/subghz/protocols/megacode.c b/lib/subghz/protocols/megacode.c index 1b871a0c..05b5b689 100644 --- a/lib/subghz/protocols/megacode.c +++ b/lib/subghz/protocols/megacode.c @@ -417,7 +417,7 @@ void subghz_protocol_decoder_megacode_get_string(void* context, FuriString* outp output, "%s %dbit\r\n" "Key:0x%06lX\r\n" - "Sn:0x%04lX - %ld\r\n" + "Sn:0x%04lX - %lu\r\n" "Facility:%lX Btn:%X\r\n", instance->generic.protocol_name, instance->generic.data_count_bit, diff --git a/lib/subghz/protocols/nero_radio.c b/lib/subghz/protocols/nero_radio.c index 5fffaa19..c8126b1e 100644 --- a/lib/subghz/protocols/nero_radio.c +++ b/lib/subghz/protocols/nero_radio.c @@ -308,7 +308,7 @@ void subghz_protocol_decoder_nero_radio_feed(void* context, bool level, uint32_t } instance->decoder.decode_data = 0; instance->decoder.decode_count_bit = 0; - instance->decoder.parser_step = NeroRadioDecoderStepReset; + instance->decoder.parser_step = NeroRadioDecoderStepReset; //-V1048 break; } else if( (DURATION_DIFF( diff --git a/lib/subghz/protocols/princeton.c b/lib/subghz/protocols/princeton.c index ab1c5876..7fc8f652 100644 --- a/lib/subghz/protocols/princeton.c +++ b/lib/subghz/protocols/princeton.c @@ -363,7 +363,7 @@ void subghz_protocol_decoder_princeton_get_string(void* context, FuriString* out "Key:0x%08lX\r\n" "Yek:0x%08lX\r\n" "Sn:0x%05lX Btn:%01X\r\n" - "Te:%ldus\r\n", + "Te:%luus\r\n", instance->generic.protocol_name, instance->generic.data_count_bit, (uint32_t)(instance->generic.data & 0xFFFFFF), diff --git a/lib/subghz/protocols/princeton_for_testing.c b/lib/subghz/protocols/princeton_for_testing.c index 0987e0ad..fa561602 100644 --- a/lib/subghz/protocols/princeton_for_testing.c +++ b/lib/subghz/protocols/princeton_for_testing.c @@ -94,12 +94,12 @@ void subghz_encoder_princeton_for_testing_print_log(void* context) { ((float)instance->time_high / (instance->time_high + instance->time_low)) * 100; FURI_LOG_I( TAG "Encoder", - "Radio tx_time=%ldus ON=%ldus, OFF=%ldus, DutyCycle=%ld,%ld%%", + "Radio tx_time=%luus ON=%luus, OFF=%luus, DutyCycle=%lu,%lu%%", instance->time_high + instance->time_low, instance->time_high, instance->time_low, (uint32_t)duty_cycle, - (uint32_t)((duty_cycle - (uint32_t)duty_cycle) * 100)); + (uint32_t)((duty_cycle - (uint32_t)duty_cycle) * 100UL)); } LevelDuration subghz_encoder_princeton_for_testing_yield(void* context) { diff --git a/lib/subghz/protocols/protocol_items.c b/lib/subghz/protocols/protocol_items.c index ed46f5c9..2022e9c4 100644 --- a/lib/subghz/protocols/protocol_items.c +++ b/lib/subghz/protocols/protocol_items.c @@ -13,6 +13,7 @@ const SubGhzProtocol* subghz_protocol_registry_items[] = { &subghz_protocol_bett, &subghz_protocol_doitrand, &subghz_protocol_phoenix_v2, &subghz_protocol_honeywell_wdb, &subghz_protocol_magellan, &subghz_protocol_intertechno_v3, &subghz_protocol_clemsa, &subghz_protocol_ansonic, &subghz_protocol_smc5326, + &subghz_protocol_holtek_th12x, }; const SubGhzProtocolRegistry subghz_protocol_registry = { diff --git a/lib/subghz/protocols/protocol_items.h b/lib/subghz/protocols/protocol_items.h index 9f446739..998fb56b 100644 --- a/lib/subghz/protocols/protocol_items.h +++ b/lib/subghz/protocols/protocol_items.h @@ -37,5 +37,6 @@ #include "clemsa.h" #include "ansonic.h" #include "smc5326.h" +#include "holtek_ht12x.h" extern const SubGhzProtocolRegistry subghz_protocol_registry; diff --git a/lib/subghz/protocols/scher_khan.c b/lib/subghz/protocols/scher_khan.c index a9a3078e..955104bc 100644 --- a/lib/subghz/protocols/scher_khan.c +++ b/lib/subghz/protocols/scher_khan.c @@ -151,8 +151,8 @@ void subghz_protocol_decoder_scher_khan_feed(void* context, bool level, uint32_t break; case ScherKhanDecoderStepSaveDuration: if(level) { - if(duration >= (uint32_t)(subghz_protocol_scher_khan_const.te_long + - subghz_protocol_scher_khan_const.te_delta * 2)) { + if(duration >= (subghz_protocol_scher_khan_const.te_delta * 2UL + + subghz_protocol_scher_khan_const.te_long)) { //Found stop bit instance->decoder.parser_step = ScherKhanDecoderStepReset; if(instance->decoder.decode_count_bit >= diff --git a/lib/subghz/protocols/secplus_v1.c b/lib/subghz/protocols/secplus_v1.c index 7bd0b5b7..75a44a26 100644 --- a/lib/subghz/protocols/secplus_v1.c +++ b/lib/subghz/protocols/secplus_v1.c @@ -291,7 +291,7 @@ bool subghz_protocol_encoder_secplus_v1_deserialize(void* context, FlipperFormat uint8_t key_data[sizeof(uint64_t)] = {0}; for(size_t i = 0; i < sizeof(uint64_t); i++) { - key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> i * 8) & 0xFF; + key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> (i * 8)) & 0xFF; } if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) { FURI_LOG_E(TAG, "Unable to add Key"); @@ -550,7 +550,7 @@ bool subghz_protocol_secplus_v1_check_fixed(uint32_t fixed) { do { if(id1 == 0) return false; - if(!(btn == 0 || btn == 1 || btn == 2)) return false; + if(!(btn == 0 || btn == 1 || btn == 2)) return false; //-V560 } while(false); return true; } @@ -588,7 +588,7 @@ void subghz_protocol_decoder_secplus_v1_get_string(void* context, FuriString* ou if(pin <= 9999) { furi_string_cat_printf(output, " pin:%d", pin); - } else if(10000 <= pin && pin <= 11029) { + } else if(pin <= 11029) { furi_string_cat_printf(output, " pin:enter"); } @@ -618,7 +618,7 @@ void subghz_protocol_decoder_secplus_v1_get_string(void* context, FuriString* ou furi_string_cat_printf(output, " Btn:left\r\n"); } else if(instance->generic.btn == 0) { furi_string_cat_printf(output, " Btn:middle\r\n"); - } else if(instance->generic.btn == 2) { + } else if(instance->generic.btn == 2) { //-V547 furi_string_cat_printf(output, " Btn:right\r\n"); } diff --git a/lib/subghz/protocols/secplus_v2.c b/lib/subghz/protocols/secplus_v2.c index 90cc805a..7b79892b 100644 --- a/lib/subghz/protocols/secplus_v2.c +++ b/lib/subghz/protocols/secplus_v2.c @@ -151,7 +151,7 @@ static bool subghz_protocol_secplus_v2_mix_order_decode(uint8_t order, uint16_t case 0x06: // 0b0110 2, 1, 0], case 0x09: // 0b1001 2, 1, 0], p[2] = a; - p[1] = b; + // p[1]: no change p[0] = c; break; case 0x08: // 0b1000 1, 2, 0], @@ -166,20 +166,18 @@ static bool subghz_protocol_secplus_v2_mix_order_decode(uint8_t order, uint16_t p[1] = c; break; case 0x00: // 0b0000 0, 2, 1], - p[0] = a; + // p[0]: no change p[2] = b; p[1] = c; break; case 0x05: // 0b0101 1, 0, 2], p[1] = a; p[0] = b; - p[2] = c; + // p[2]: no change break; case 0x02: // 0b0010 0, 1, 2], case 0x0A: // 0b1010 0, 1, 2], - p[0] = a; - p[1] = b; - p[2] = c; + // no reordering break; default: FURI_LOG_E(TAG, "Order FAIL"); @@ -539,7 +537,7 @@ bool subghz_protocol_encoder_secplus_v2_deserialize(void* context, FlipperFormat //update data for(size_t i = 0; i < sizeof(uint64_t); i++) { - key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> i * 8) & 0xFF; + key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> (i * 8)) & 0xFF; } if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) { FURI_LOG_E(TAG, "Unable to add Key"); @@ -547,7 +545,7 @@ bool subghz_protocol_encoder_secplus_v2_deserialize(void* context, FlipperFormat } for(size_t i = 0; i < sizeof(uint64_t); i++) { - key_data[sizeof(uint64_t) - i - 1] = (instance->secplus_packet_1 >> i * 8) & 0xFF; + key_data[sizeof(uint64_t) - i - 1] = (instance->secplus_packet_1 >> (i * 8)) & 0xFF; } if(!flipper_format_update_hex( flipper_format, "Secplus_packet_1", key_data, sizeof(uint64_t))) { @@ -605,7 +603,7 @@ bool subghz_protocol_secplus_v2_create_data( uint8_t key_data[sizeof(uint64_t)] = {0}; for(size_t i = 0; i < sizeof(uint64_t); i++) { - key_data[sizeof(uint64_t) - i - 1] = (instance->secplus_packet_1 >> i * 8) & 0xFF; + key_data[sizeof(uint64_t) - i - 1] = (instance->secplus_packet_1 >> (i * 8)) & 0xFF; } if(res && @@ -691,7 +689,7 @@ void subghz_protocol_decoder_secplus_v2_feed(void* context, bool level, uint32_t subghz_protocol_secplus_v2_const.te_delta) { event = ManchesterEventLongLow; } else if( - duration >= (uint32_t)(subghz_protocol_secplus_v2_const.te_long * 2 + + duration >= (subghz_protocol_secplus_v2_const.te_long * 2UL + subghz_protocol_secplus_v2_const.te_delta)) { if(instance->decoder.decode_count_bit == subghz_protocol_secplus_v2_const.min_count_bit_for_found) { @@ -766,7 +764,7 @@ bool subghz_protocol_decoder_secplus_v2_serialize( uint8_t key_data[sizeof(uint64_t)] = {0}; for(size_t i = 0; i < sizeof(uint64_t); i++) { - key_data[sizeof(uint64_t) - i - 1] = (instance->secplus_packet_1 >> i * 8) & 0xFF; + key_data[sizeof(uint64_t) - i - 1] = (instance->secplus_packet_1 >> (i * 8)) & 0xFF; } if(res && diff --git a/lib/subghz/protocols/smc5326.c b/lib/subghz/protocols/smc5326.c index 889e39f0..9c9b5d4f 100644 --- a/lib/subghz/protocols/smc5326.c +++ b/lib/subghz/protocols/smc5326.c @@ -372,8 +372,8 @@ void subghz_protocol_decoder_smc5326_get_string(void* context, FuriString* outpu furi_string_cat_printf( output, - "%s %dbit\r\n" - "Key:%07lX Te:%ldus\r\n" + "%s %ubit\r\n" + "Key:%07lX Te:%luus\r\n" " +: " DIP_PATTERN "\r\n" " o: " DIP_PATTERN " ", instance->generic.protocol_name, diff --git a/lib/subghz/subghz_file_encoder_worker.c b/lib/subghz/subghz_file_encoder_worker.c index abc33188..5c4d36f7 100644 --- a/lib/subghz/subghz_file_encoder_worker.c +++ b/lib/subghz/subghz_file_encoder_worker.c @@ -90,7 +90,7 @@ LevelDuration subghz_file_encoder_worker_get_level_duration(void* context) { level_duration = level_duration_make(false, -duration); } else if(duration > 0) { level_duration = level_duration_make(true, duration); - } else if(duration == 0) { + } else if(duration == 0) { //-V547 level_duration = level_duration_reset(); FURI_LOG_I(TAG, "Stop transmission"); instance->worker_stoping = true; diff --git a/lib/subghz/subghz_keystore.c b/lib/subghz/subghz_keystore.c index e06bd979..e0b1cf6c 100644 --- a/lib/subghz/subghz_keystore.c +++ b/lib/subghz/subghz_keystore.c @@ -189,7 +189,7 @@ bool subghz_keystore_load(SubGhzKeystore* instance, const char* file_name) { bool result = false; uint8_t iv[16]; uint32_t version; - SubGhzKeystoreEncryption encryption; + uint32_t encryption; FuriString* filetype; filetype = furi_string_alloc(); @@ -324,9 +324,9 @@ bool subghz_keystore_save(SubGhzKeystore* instance, const char* file_name, uint8 size_t total_keys = SubGhzKeyArray_size(instance->data); result = encrypted_line_count == total_keys; if(result) { - FURI_LOG_I(TAG, "Success. Encrypted: %d of %d", encrypted_line_count, total_keys); + FURI_LOG_I(TAG, "Success. Encrypted: %zu of %zu", encrypted_line_count, total_keys); } else { - FURI_LOG_E(TAG, "Failure. Encrypted: %d of %d", encrypted_line_count, total_keys); + FURI_LOG_E(TAG, "Failure. Encrypted: %zu of %zu", encrypted_line_count, total_keys); } } while(0); flipper_format_free(flipper_format); @@ -349,9 +349,9 @@ bool subghz_keystore_raw_encrypted_save( uint8_t* iv) { bool encrypted = false; uint32_t version; + uint32_t encryption; FuriString* filetype; filetype = furi_string_alloc(); - SubGhzKeystoreEncryption encryption; Storage* storage = furi_record_open(RECORD_STORAGE); @@ -464,7 +464,7 @@ bool subghz_keystore_raw_encrypted_save( } stream_write_cstring(output_stream, encrypted_line); - } while(result); + } while(true); flipper_format_free(output_flipper_format); @@ -488,7 +488,7 @@ bool subghz_keystore_raw_get_data(const char* file_name, size_t offset, uint8_t* bool result = false; uint8_t iv[16]; uint32_t version; - SubGhzKeystoreEncryption encryption; + uint32_t encryption; FuriString* str_temp; str_temp = furi_string_alloc(); diff --git a/lib/subghz/subghz_setting.c b/lib/subghz/subghz_setting.c index c5ec5db7..57e23c38 100644 --- a/lib/subghz/subghz_setting.c +++ b/lib/subghz/subghz_setting.c @@ -532,9 +532,8 @@ uint8_t* subghz_setting_get_preset_data_by_name(SubGhzSetting* instance, const c uint32_t subghz_setting_get_frequency(SubGhzSetting* instance, size_t idx) { furi_assert(instance); - uint32_t* ret = FrequencyList_get(instance->frequencies, idx); - if(ret) { - return (*ret) & FREQUENCY_MASK; + if(idx < FrequencyList_size(instance->frequencies)) { + return (*FrequencyList_get(instance->frequencies, idx)) & FREQUENCY_MASK; } else { return 0; } @@ -542,9 +541,8 @@ uint32_t subghz_setting_get_frequency(SubGhzSetting* instance, size_t idx) { uint32_t subghz_setting_get_hopper_frequency(SubGhzSetting* instance, size_t idx) { furi_assert(instance); - uint32_t* ret = FrequencyList_get(instance->hopper_frequencies, idx); - if(ret) { - return *ret; + if(idx < FrequencyList_size(instance->frequencies)) { + return *FrequencyList_get(instance->hopper_frequencies, idx); } else { return 0; } diff --git a/lib/toolbox/SConscript b/lib/toolbox/SConscript index 015a8ed1..724d25af 100644 --- a/lib/toolbox/SConscript +++ b/lib/toolbox/SConscript @@ -19,6 +19,7 @@ env.Append( File("args.h"), File("saved_struct.h"), File("version.h"), + File("float_tools.h"), File("tar/tar_archive.h"), File("stream/stream.h"), File("stream/file_stream.h"), diff --git a/lib/toolbox/dir_walk.c b/lib/toolbox/dir_walk.c index b5e2cb52..e5a3cf32 100644 --- a/lib/toolbox/dir_walk.c +++ b/lib/toolbox/dir_walk.c @@ -69,8 +69,11 @@ static DirWalkResult if(dir_walk_filter(dir_walk, name, &info)) { if(return_path != NULL) { - furi_string_printf( - return_path, "%s/%s", furi_string_get_cstr(dir_walk->path), name); + furi_string_printf( //-V576 + return_path, + "%s/%s", + furi_string_get_cstr(dir_walk->path), + name); } if(fileinfo != NULL) { diff --git a/lib/toolbox/float_tools.c b/lib/toolbox/float_tools.c new file mode 100644 index 00000000..9c0fe871 --- /dev/null +++ b/lib/toolbox/float_tools.c @@ -0,0 +1,8 @@ +#include "float_tools.h" + +#include +#include + +bool float_is_equal(float a, float b) { + return fabsf(a - b) <= FLT_EPSILON * fmaxf(fabsf(a), fabsf(b)); +} diff --git a/lib/toolbox/float_tools.h b/lib/toolbox/float_tools.h new file mode 100644 index 00000000..0b758e9f --- /dev/null +++ b/lib/toolbox/float_tools.h @@ -0,0 +1,19 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/** Compare two floating point numbers + * @param a First number to compare + * @param b Second number to compare + * + * @return bool true if a equals b, false otherwise + */ +bool float_is_equal(float a, float b); + +#ifdef __cplusplus +} +#endif diff --git a/lib/toolbox/hex.c b/lib/toolbox/hex.c index 7b2719b7..25dcb095 100644 --- a/lib/toolbox/hex.c +++ b/lib/toolbox/hex.c @@ -1,14 +1,14 @@ #include "hex.h" bool hex_char_to_hex_nibble(char c, uint8_t* nibble) { - if((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f')) { - if((c >= '0' && c <= '9')) { - *nibble = c - '0'; - } else if((c >= 'A' && c <= 'F')) { - *nibble = c - 'A' + 10; - } else { - *nibble = c - 'a' + 10; - } + if((c >= '0' && c <= '9')) { + *nibble = c - '0'; + return true; + } else if((c >= 'A' && c <= 'F')) { + *nibble = c - 'A' + 10; + return true; + } else if(c >= 'a' && c <= 'f') { + *nibble = c - 'a' + 10; return true; } else { return false; diff --git a/lib/toolbox/md5.c b/lib/toolbox/md5.c index 3cf7cf05..a907d52e 100644 --- a/lib/toolbox/md5.c +++ b/lib/toolbox/md5.c @@ -115,7 +115,7 @@ void md5_process(md5_context* ctx, const unsigned char data[64]) { GET_UINT32_LE(X[14], data, 56); GET_UINT32_LE(X[15], data, 60); -#define S(x, n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n))) +#define S(x, n) (((x) << (n)) | (((x)&0xFFFFFFFF) >> (32 - (n)))) #define P(a, b, c, d, k, s, t) \ { \ @@ -128,7 +128,7 @@ void md5_process(md5_context* ctx, const unsigned char data[64]) { C = ctx->state[2]; D = ctx->state[3]; -#define F(x, y, z) (z ^ (x & (y ^ z))) +#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) P(A, B, C, D, 0, 7, 0xD76AA478); P(D, A, B, C, 1, 12, 0xE8C7B756); @@ -149,7 +149,7 @@ void md5_process(md5_context* ctx, const unsigned char data[64]) { #undef F -#define F(x, y, z) (y ^ (z & (x ^ y))) +#define F(x, y, z) ((y) ^ ((z) & ((x) ^ (y)))) P(A, B, C, D, 1, 5, 0xF61E2562); P(D, A, B, C, 6, 9, 0xC040B340); @@ -170,7 +170,7 @@ void md5_process(md5_context* ctx, const unsigned char data[64]) { #undef F -#define F(x, y, z) (x ^ y ^ z) +#define F(x, y, z) ((x) ^ (y) ^ (z)) P(A, B, C, D, 5, 4, 0xFFFA3942); P(D, A, B, C, 8, 11, 0x8771F681); @@ -191,7 +191,7 @@ void md5_process(md5_context* ctx, const unsigned char data[64]) { #undef F -#define F(x, y, z) (y ^ (x | ~z)) +#define F(x, y, z) ((y) ^ ((x) | ~(z))) P(A, B, C, D, 0, 6, 0xF4292244); P(D, A, B, C, 7, 10, 0x432AFF97); @@ -295,5 +295,5 @@ void md5(const unsigned char* input, size_t ilen, unsigned char output[16]) { md5_update(&ctx, input, ilen); md5_finish(&ctx, output); - memset(&ctx, 0, sizeof(md5_context)); + memset(&ctx, 0, sizeof(md5_context)); //-V597 } diff --git a/lib/toolbox/sha256.c b/lib/toolbox/sha256.c index ece77955..ff498443 100644 --- a/lib/toolbox/sha256.c +++ b/lib/toolbox/sha256.c @@ -62,15 +62,15 @@ static void memcpy_output_bswap32(unsigned char* dst, const uint32_t* p) { } } -#define rotr32(x, n) (((x) >> n) | ((x) << (32 - n))) +#define rotr32(x, n) (((x) >> n) | ((x) << (32 - (n)))) #define ch(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) #define maj(x, y, z) (((x) & (y)) | ((z) & ((x) ^ (y)))) /* round transforms for SHA256 compression functions */ -#define vf(n, i) v[(n - i) & 7] +#define vf(n, i) v[((n) - (i)) & 7] -#define hf(i) (p[i & 15] += g_1(p[(i + 14) & 15]) + p[(i + 9) & 15] + g_0(p[(i + 1) & 15])) +#define hf(i) (p[(i)&15] += g_1(p[((i) + 14) & 15]) + p[((i) + 9) & 15] + g_0(p[((i) + 1) & 15])) #define v_cycle0(i) \ p[i] = __builtin_bswap32(p[i]); \ @@ -176,8 +176,8 @@ void sha256_finish(sha256_context* ctx, unsigned char output[32]) { uint32_t last = (ctx->total[0] & SHA256_MASK); ctx->wbuf[last >> 2] = __builtin_bswap32(ctx->wbuf[last >> 2]); - ctx->wbuf[last >> 2] &= 0xffffff80 << (8 * (~last & 3)); - ctx->wbuf[last >> 2] |= 0x00000080 << (8 * (~last & 3)); + ctx->wbuf[last >> 2] &= 0xffffff80UL << (8 * (~last & 3)); + ctx->wbuf[last >> 2] |= 0x00000080UL << (8 * (~last & 3)); ctx->wbuf[last >> 2] = __builtin_bswap32(ctx->wbuf[last >> 2]); if(last > SHA256_BLOCK_SIZE - 9) { diff --git a/lib/toolbox/stream/stream.c b/lib/toolbox/stream/stream.c index 86d35c95..407da0f2 100644 --- a/lib/toolbox/stream/stream.c +++ b/lib/toolbox/stream/stream.c @@ -4,6 +4,8 @@ #include #include +#define STREAM_BUFFER_SIZE (32U) + void stream_free(Stream* stream) { furi_assert(stream); stream->vtable->free(stream); @@ -24,6 +26,82 @@ bool stream_seek(Stream* stream, int32_t offset, StreamOffset offset_type) { return stream->vtable->seek(stream, offset, offset_type); } +static bool stream_seek_to_char_forward(Stream* stream, char c) { + // Search is starting from seconds character + if(!stream_seek(stream, 1, StreamOffsetFromCurrent)) { + return false; + } + + // Search character in a stream + bool result = false; + while(!result) { + uint8_t buffer[STREAM_BUFFER_SIZE] = {0}; + size_t ret = stream_read(stream, buffer, STREAM_BUFFER_SIZE); + for(size_t i = 0; i < ret; i++) { + if(buffer[i] == c) { + stream_seek(stream, (int32_t)i - ret, StreamOffsetFromCurrent); + result = true; + break; + } + } + if(ret != STREAM_BUFFER_SIZE) break; + } + return result; +} + +static bool stream_seek_to_char_backward(Stream* stream, char c) { + size_t anchor = stream_tell(stream); + + // Special case, no previous characters + if(anchor == 0) { + return false; + } + + bool result = false; + while(!result) { + // Seek back + uint8_t buffer[STREAM_BUFFER_SIZE] = {0}; + size_t to_read = STREAM_BUFFER_SIZE; + if(to_read > anchor) { + to_read = anchor; + } + + anchor -= to_read; + furi_check(stream_seek(stream, anchor, StreamOffsetFromStart)); + + size_t ret = stream_read(stream, buffer, to_read); + for(size_t i = 0; i < ret; i++) { + size_t cursor = ret - i - 1; + if(buffer[cursor] == c) { + result = true; + furi_check(stream_seek(stream, anchor + cursor, StreamOffsetFromStart)); + break; + } else { + } + } + if(ret != STREAM_BUFFER_SIZE) break; + } + return result; +} + +bool stream_seek_to_char(Stream* stream, char c, StreamDirection direction) { + const size_t old_position = stream_tell(stream); + + bool result = false; + if(direction == StreamDirectionForward) { + result = stream_seek_to_char_forward(stream, c); + } else if(direction == StreamDirectionBackward) { + result = stream_seek_to_char_backward(stream, c); + } + + // Rollback + if(!result) { + stream_seek(stream, old_position, StreamOffsetFromStart); + } + + return result; +} + size_t stream_tell(Stream* stream) { furi_assert(stream); return stream->vtable->tell(stream); @@ -69,11 +147,10 @@ static bool stream_write_struct(Stream* stream, const void* context) { bool stream_read_line(Stream* stream, FuriString* str_result) { furi_string_reset(str_result); - const uint8_t buffer_size = 32; - uint8_t buffer[buffer_size]; + uint8_t buffer[STREAM_BUFFER_SIZE]; do { - uint16_t bytes_were_read = stream_read(stream, buffer, buffer_size); + uint16_t bytes_were_read = stream_read(stream, buffer, STREAM_BUFFER_SIZE); if(bytes_were_read == 0) break; bool result = false; @@ -315,8 +392,8 @@ void stream_dump_data(Stream* stream) { size_t size = stream_size(stream); size_t tell = stream_tell(stream); printf("stream %p\r\n", stream); - printf("size = %u\r\n", size); - printf("tell = %u\r\n", tell); + printf("size = %zu\r\n", size); + printf("tell = %zu\r\n", tell); printf("DATA START\r\n"); uint8_t* data = malloc(STREAM_CACHE_SIZE); stream_rewind(stream); diff --git a/lib/toolbox/stream/stream.h b/lib/toolbox/stream/stream.h index fc385510..84b4f0eb 100644 --- a/lib/toolbox/stream/stream.h +++ b/lib/toolbox/stream/stream.h @@ -16,6 +16,11 @@ typedef enum { StreamOffsetFromEnd, } StreamOffset; +typedef enum { + StreamDirectionForward, + StreamDirectionBackward, +} StreamDirection; + typedef bool (*StreamWriteCB)(Stream* stream, const void* context); /** @@ -31,15 +36,15 @@ void stream_free(Stream* stream); void stream_clean(Stream* stream); /** - * Indicates that the rw pointer is at the end of the stream + * Indicates that the RW pointer is at the end of the stream * @param stream Stream instance - * @return true if rw pointer is at the end of the stream - * @return false if rw pointer is not at the end of the stream + * @return true if RW pointer is at the end of the stream + * @return false if RW pointer is not at the end of the stream */ bool stream_eof(Stream* stream); /** - * Moves the rw pointer. + * Moves the RW pointer. * @param stream Stream instance * @param offset how much to move the pointer * @param offset_type starting from what @@ -48,10 +53,20 @@ bool stream_eof(Stream* stream); */ bool stream_seek(Stream* stream, int32_t offset, StreamOffset offset_type); +/** Seek to next occurrence of the character + * + * @param stream Pointer to the stream instance + * @param[in] c The Character + * @param[in] direction The Direction + * + * @return true on success + */ +bool stream_seek_to_char(Stream* stream, char c, StreamDirection direction); + /** - * Gets the value of the rw pointer + * Gets the value of the RW pointer * @param stream Stream instance - * @return size_t value of the rw pointer + * @return size_t value of the RW pointer */ size_t stream_tell(Stream* stream); @@ -101,13 +116,13 @@ bool stream_delete_and_insert( * Read line from a stream (supports LF and CRLF line endings) * @param stream * @param str_result - * @return true if line lenght is not zero + * @return true if line length is not zero * @return false otherwise */ bool stream_read_line(Stream* stream, FuriString* str_result); /** - * Moves the rw pointer to the start + * Moves the RW pointer to the start * @param stream Stream instance */ bool stream_rewind(Stream* stream); @@ -157,7 +172,7 @@ size_t stream_write_vaformat(Stream* stream, const char* format, va_list args); /** * Insert N chars to the stream, starting at the current pointer. - * Data will be inserted, not overwritteт, so the stream will be increased in size. + * Data will be inserted, not overwritten, so the stream will be increased in size. * @param stream Stream instance * @param data data to be inserted * @param size size of data to be inserted @@ -273,7 +288,7 @@ bool stream_delete_and_insert_vaformat( /** * Remove N chars from the stream, starting at the current pointer. - * The size may be larger than stream size, the stream will be cleared from current rw pointer to the end. + * The size may be larger than stream size, the stream will be cleared from current RW pointer to the end. * @param stream Stream instance * @param size how many chars need to be deleted * @return true if the operation was successful @@ -282,7 +297,7 @@ bool stream_delete_and_insert_vaformat( bool stream_delete(Stream* stream, size_t size); /** - * Copy data from one stream to another. Data will be copied from current rw pointer and to current rw pointer. + * Copy data from one stream to another. Data will be copied from current RW pointer and to current RW pointer. * @param stream_from * @param stream_to * @param size @@ -328,7 +343,7 @@ size_t stream_load_from_file(Stream* stream, Storage* storage, const char* path) size_t stream_save_to_file(Stream* stream, Storage* storage, const char* path, FS_OpenMode mode); /** - * Dump stream inner data (size, RW positiot, content) + * Dump stream inner data (size, RW position, content) * @param stream Stream instance */ void stream_dump_data(Stream* stream); diff --git a/lib/toolbox/tar/tar_archive.c b/lib/toolbox/tar/tar_archive.c index e8b44729..fd0d175e 100644 --- a/lib/toolbox/tar/tar_archive.c +++ b/lib/toolbox/tar/tar_archive.c @@ -236,7 +236,7 @@ static int archive_extract_foreach_cb(mtar_t* tar, const mtar_header_t* header, return 0; } - FURI_LOG_D(TAG, "Extracting %d bytes to '%s'", header->size, header->name); + FURI_LOG_D(TAG, "Extracting %u bytes to '%s'", header->size, header->name); FuriString* converted_fname = furi_string_alloc_set(header->name); if(op_params->converter) { @@ -382,4 +382,4 @@ bool tar_archive_unpack_file( return false; } return archive_extract_current_file(archive, destination); -} \ No newline at end of file +} diff --git a/lib/toolbox/varint.c b/lib/toolbox/varint.c index ee2f5c3a..79777c4b 100644 --- a/lib/toolbox/varint.c +++ b/lib/toolbox/varint.c @@ -15,7 +15,7 @@ size_t varint_uint32_unpack(uint32_t* value, const uint8_t* input, size_t input_ uint32_t parsed = 0; for(i = 0; i < input_size; i++) { - parsed |= (input[i] & 0x7F) << (7 * i); + parsed |= (input[i] & 0x7FUL) << (7 * i); if(!(input[i] & 0x80)) { break; @@ -73,4 +73,4 @@ size_t varint_int32_length(int32_t value) { } return varint_uint32_length(v); -} \ No newline at end of file +} diff --git a/lib/update_util/dfu_file.c b/lib/update_util/dfu_file.c index d6f31b60..62b139e8 100644 --- a/lib/update_util/dfu_file.c +++ b/lib/update_util/dfu_file.c @@ -35,7 +35,7 @@ uint8_t dfu_file_validate_headers(File* dfuf, const DfuValidationParams* referen return 0; } - if(memcmp(dfu_prefix.szSignature, DFU_SIGNATURE, sizeof(dfu_prefix.szSignature))) { + if(memcmp(dfu_prefix.szSignature, DFU_SIGNATURE, sizeof(dfu_prefix.szSignature)) != 0) { return 0; } diff --git a/lib/update_util/resources/manifest.c b/lib/update_util/resources/manifest.c index 8b6a1b33..5a818a0a 100644 --- a/lib/update_util/resources/manifest.c +++ b/lib/update_util/resources/manifest.c @@ -60,6 +60,12 @@ ResourceManifestEntry* resource_manifest_reader_next(ResourceManifestReader* res char type_code = furi_string_get_char(resource_manifest->linebuf, 0); switch(type_code) { + case 'V': + resource_manifest->entry.type = ResourceManifestEntryTypeVersion; + break; + case 'T': + resource_manifest->entry.type = ResourceManifestEntryTypeTimestamp; + break; case 'F': resource_manifest->entry.type = ResourceManifestEntryTypeFile; break; @@ -98,9 +104,9 @@ ResourceManifestEntry* resource_manifest_reader_next(ResourceManifestReader* res furi_string_right(resource_manifest->linebuf, offs + 1); furi_string_set(resource_manifest->entry.name, resource_manifest->linebuf); - } else if(resource_manifest->entry.type == ResourceManifestEntryTypeDirectory) { - /* Parse directory entry - D: */ + } else { //-V547 + /* Everything else is plain key value. Parse version, timestamp or directory entry + : */ /* Remove entry type code */ furi_string_right(resource_manifest->linebuf, 2); @@ -113,3 +119,45 @@ ResourceManifestEntry* resource_manifest_reader_next(ResourceManifestReader* res return NULL; } + +ResourceManifestEntry* + resource_manifest_reader_previous(ResourceManifestReader* resource_manifest) { + furi_assert(resource_manifest); + + // Snapshot position for rollback + const size_t previous_position = stream_tell(resource_manifest->stream); + + // We need to jump 2 lines back + size_t jumps = 2; + // Special case: end of the file. + const bool was_eof = stream_eof(resource_manifest->stream); + if(was_eof) { + jumps = 1; + } + while(jumps) { + if(!stream_seek_to_char(resource_manifest->stream, '\n', StreamDirectionBackward)) { + break; + } + if(stream_tell(resource_manifest->stream) < (previous_position - 1)) { + jumps--; + } + } + + // Special case: first line. Force seek to zero + if(jumps == 1) { + jumps = 0; + stream_seek(resource_manifest->stream, 0, StreamOffsetFromStart); + } + + if(jumps == 0) { + ResourceManifestEntry* entry = resource_manifest_reader_next(resource_manifest); + // Special case: was end of the file, prevent loop + if(was_eof) { + stream_seek(resource_manifest->stream, -1, StreamOffsetFromCurrent); + } + return entry; + } else { + stream_seek(resource_manifest->stream, previous_position, StreamOffsetFromStart); + return NULL; + } +} diff --git a/lib/update_util/resources/manifest.h b/lib/update_util/resources/manifest.h index 8baa1613..ddceb5ff 100644 --- a/lib/update_util/resources/manifest.h +++ b/lib/update_util/resources/manifest.h @@ -11,6 +11,8 @@ extern "C" { typedef enum { ResourceManifestEntryTypeUnknown = 0, + ResourceManifestEntryTypeVersion, + ResourceManifestEntryTypeTimestamp, ResourceManifestEntryTypeDirectory, ResourceManifestEntryTypeFile, } ResourceManifestEntryType; @@ -52,6 +54,18 @@ bool resource_manifest_reader_open(ResourceManifestReader* resource_manifest, co */ ResourceManifestEntry* resource_manifest_reader_next(ResourceManifestReader* resource_manifest); +/** Read previous file/dir entry from manifest + * + * You must be at the end of the manifest to use this function. + * Intended to be used after reaching end with resource_manifest_reader_next + * + * @param resource_manifest Pointer to the ResourceManifestReader instance + * + * @return entry or NULL if end of file + */ +ResourceManifestEntry* + resource_manifest_reader_previous(ResourceManifestReader* resource_manifest); + #ifdef __cplusplus } // extern "C" #endif \ No newline at end of file diff --git a/lib/update_util/update_operation.c b/lib/update_util/update_operation.c index 3a44605e..c6a9ccc5 100644 --- a/lib/update_util/update_operation.c +++ b/lib/update_util/update_operation.c @@ -11,7 +11,7 @@ #define UPDATE_ROOT_DIR EXT_PATH("update") /* Need at least 4 free LFS pages before update */ -#define UPDATE_MIN_INT_FREE_SPACE 2 * 4 * 1024 +#define UPDATE_MIN_INT_FREE_SPACE (2 * 4 * 1024) static const char* update_prepare_result_descr[] = { [UpdatePrepareResultOK] = "OK", @@ -110,7 +110,7 @@ bool update_operation_get_current_package_manifest_path(Storage* storage, FuriSt } static bool update_operation_persist_manifest_path(Storage* storage, const char* manifest_path) { - const uint16_t manifest_path_len = strlen(manifest_path); + const size_t manifest_path_len = strlen(manifest_path); furi_check(manifest_path && manifest_path_len); bool success = false; File* file = storage_file_alloc(storage); diff --git a/scripts/ReadMe.md b/scripts/ReadMe.md index d37e67c9..a9feba11 100644 --- a/scripts/ReadMe.md +++ b/scripts/ReadMe.md @@ -27,14 +27,14 @@ Also display type, region and etc... ## Core1 and Core2 firmware flashing Core2 goes first, then Core1. -Never flash FUS or you will loose your job, girlfriend and keys in secure enclave. +Never flash FUS or you will lose your job, girlfriend and keys in secure enclave. ## Option Bytes !!! Setting incorrect Option Bytes may brick your MCU !!! Defaults are mostly OK, but there are couple things that we'd like to tune. -Also OB may be damaged, so we've made couple scripts to check and set option bytes. +Also, OB may be damaged, so we've made couple scripts to check and set option bytes. !!! Setting incorrect Option Bytes may brick your MCU !!! @@ -69,4 +69,4 @@ Then run python scripts/slideshow.py -i assets/slideshow/my_show/ -o assets/slideshow/my_show/.slideshow ``` -Upload generated .slideshow file to Flipper's internal storage and restart it. \ No newline at end of file +Upload generated .slideshow file to Flipper's internal storage and restart it. diff --git a/scripts/amap_mariadb_insert.py b/scripts/amap_mariadb_insert.py deleted file mode 100755 index 6ff1b3bf..00000000 --- a/scripts/amap_mariadb_insert.py +++ /dev/null @@ -1,136 +0,0 @@ -#!/usr/bin/env python3 - -from datetime import datetime -import argparse -import mariadb -import sys -import os - - -def parseArgs(): - parser = argparse.ArgumentParser() - parser.add_argument("db_user", help="MariaDB user") - parser.add_argument("db_pass", help="MariaDB password") - parser.add_argument("db_host", help="MariaDB hostname") - parser.add_argument("db_port", type=int, help="MariaDB port") - parser.add_argument("db_name", help="MariaDB database") - parser.add_argument("report_file", help="Report file(.map.all)") - args = parser.parse_args() - return args - - -def mariadbConnect(args): - try: - conn = mariadb.connect( - user=args.db_user, - password=args.db_pass, - host=args.db_host, - port=args.db_port, - database=args.db_name, - ) - except mariadb.Error as e: - print(f"Error connecting to MariaDB: {e}") - sys.exit(1) - return conn - - -def parseEnv(): - outArr = [] - outArr.append(datetime.now().strftime("%Y-%m-%d %H:%M:%S")) - outArr.append(os.getenv("COMMIT_HASH", default=None)) - outArr.append(os.getenv("COMMIT_MSG", default=None)) - outArr.append(os.getenv("BRANCH_NAME", default=None)) - outArr.append(os.getenv("BSS_SIZE", default=None)) - outArr.append(os.getenv("TEXT_SIZE", default=None)) - outArr.append(os.getenv("RODATA_SIZE", default=None)) - outArr.append(os.getenv("DATA_SIZE", default=None)) - outArr.append(os.getenv("FREE_FLASH_SIZE", default=None)) - outArr.append(os.getenv("PULL_ID", default=None)) - outArr.append(os.getenv("PULL_NAME", default=None)) - return outArr - - -def createTables(cur, conn): - headerTable = "CREATE TABLE IF NOT EXISTS `header` ( \ - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, \ - `datetime` datetime NOT NULL, \ - `commit` varchar(40) NOT NULL, \ - `commit_msg` text NOT NULL, \ - `branch_name` text NOT NULL, \ - `bss_size` int(10) unsigned NOT NULL, \ - `text_size` int(10) unsigned NOT NULL, \ - `rodata_size` int(10) unsigned NOT NULL, \ - `data_size` int(10) unsigned NOT NULL, \ - `free_flash_size` int(10) unsigned NOT NULL, \ - `pullrequest_id` int(10) unsigned DEFAULT NULL, \ - `pullrequest_name` text DEFAULT NULL, \ - PRIMARY KEY (`id`), \ - KEY `header_id_index` (`id`) )" - dataTable = "CREATE TABLE IF NOT EXISTS `data` ( \ - `header_id` int(10) unsigned NOT NULL, \ - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, \ - `section` text NOT NULL, \ - `address` text NOT NULL, \ - `size` int(10) unsigned NOT NULL, \ - `name` text NOT NULL, \ - `lib` text NOT NULL, \ - `obj_name` text NOT NULL, \ - PRIMARY KEY (`id`), \ - KEY `data_id_index` (`id`), \ - KEY `data_header_id_index` (`header_id`), \ - CONSTRAINT `data_header_id_foreign` FOREIGN KEY (`header_id`) REFERENCES `header` (`id`) )" - cur.execute(headerTable) - cur.execute(dataTable) - conn.commit() - - -def insertHeader(data, cur, conn): - query = "INSERT INTO `header` ( \ - datetime, commit, commit_msg, branch_name, bss_size, text_size, \ - rodata_size, data_size, free_flash_size, pullrequest_id, pullrequest_name) \ - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" - cur.execute(query, data) - conn.commit() - return cur.lastrowid - - -def parseFile(fileObj, headerID): - arr = [] - fileLines = fileObj.readlines() - for line in fileLines: - lineArr = [] - tempLineArr = line.split("\t") - lineArr.append(headerID) - lineArr.append(tempLineArr[0]) # section - lineArr.append(int(tempLineArr[2], 16)) # address hex - lineArr.append(int(tempLineArr[3])) # size - lineArr.append(tempLineArr[4]) # name - lineArr.append(tempLineArr[5]) # lib - lineArr.append(tempLineArr[6]) # obj_name - arr.append(tuple(lineArr)) - return arr - - -def insertData(data, cur, conn): - query = "INSERT INTO `data` ( \ - header_id, section, address, size, \ - name, lib, obj_name) \ - VALUES (?, ?, ?, ?, ? ,?, ?)" - cur.executemany(query, data) - conn.commit() - - -def main(): - args = parseArgs() - dbConn = mariadbConnect(args) - reportFile = open(args.report_file) - dbCurs = dbConn.cursor() - createTables(dbCurs, dbConn) - headerID = insertHeader(parseEnv(), dbCurs, dbConn) - insertData(parseFile(reportFile, headerID), dbCurs, dbConn) - reportFile.close() - dbCurs.close() - - -if __name__ == "__main__": - main() diff --git a/scripts/fbt_tools/fbt_debugopts.py b/scripts/fbt_tools/fbt_debugopts.py index 9ff05cb7..f4b021c2 100644 --- a/scripts/fbt_tools/fbt_debugopts.py +++ b/scripts/fbt_tools/fbt_debugopts.py @@ -41,12 +41,10 @@ def generate(env, **kw): "|openocd -c 'gdb_port pipe; log_output ${FBT_DEBUG_DIR}/openocd.log' ${[SINGLEQUOTEFUNC(OPENOCD_OPTS)]}" ], GDBOPTS_BASE=[ - "-ex", - "set pagination off", "-ex", "target extended-remote ${GDBREMOTE}", "-ex", - "set confirm off", + "source ${FBT_DEBUG_DIR}/gdbinit", ], GDBOPTS_BLACKMAGIC=[ "-ex", diff --git a/scripts/flipper/assets/icon.py b/scripts/flipper/assets/icon.py index 235af7b0..ed85b024 100644 --- a/scripts/flipper/assets/icon.py +++ b/scripts/flipper/assets/icon.py @@ -104,7 +104,7 @@ def file2image(file): data_enc = bytearray(data_encoded_str) data_enc = bytearray([len(data_enc) & 0xFF, len(data_enc) >> 8]) + data_enc - # Use encoded data only if its lenght less than original, including header + # Use encoded data only if its length less than original, including header if len(data_enc) < len(data_bin) + 1: data = b"\x01\x00" + data_enc else: diff --git a/scripts/flipper/utils/openocd.py b/scripts/flipper/utils/openocd.py new file mode 100644 index 00000000..1309055b --- /dev/null +++ b/scripts/flipper/utils/openocd.py @@ -0,0 +1,173 @@ +import socket +import subprocess +import logging + + +class OpenOCD: + """OpenOCD cli wrapper""" + + COMMAND_TOKEN = "\x1a" + + def __init__(self, config: dict = {}) -> None: + assert isinstance(config, dict) + + # Params base + self.params = [] + + self.gdb_port = 3333 + self.telnet_port = 4444 + self.tcl_port = 6666 + + # Port + if port_base := config.get("port_base", None): + self.gdb_port = port_base + self.tcl_port = port_base + 1 + self.telnet_port = port_base + 2 + + self._add_command(f"gdb_port {self.gdb_port}") + self._add_command(f"tcl_port {self.tcl_port}") + self._add_command(f"telnet_port {self.telnet_port}") + + # Config files + + if interface := config.get("interface", None): + pass + else: + interface = "interface/stlink.cfg" + + if target := config.get("target", None): + pass + else: + target = "target/stm32wbx.cfg" + + self._add_file(interface) + self._add_file(target) + + # Programmer settings + if serial := config.get("serial", None): + self._add_command(f"{serial}") + + # Other params + if "params" in config: + self.params += config["params"] + + # logging + self.logger = logging.getLogger() + + def _add_command(self, command: str): + self.params.append("-c") + self.params.append(command) + + def _add_file(self, file: str): + self.params.append("-f") + self.params.append(file) + + def start(self, args: list[str] = []): + """Start OpenOCD process""" + + params = ["openocd", *self.params, *args] + self.logger.debug(f"_execute: {params}") + self.process = subprocess.Popen( + params, stderr=subprocess.PIPE, stdout=subprocess.PIPE + ) + + self._wait_for_openocd_tcl() + + self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.socket.connect(("127.0.0.1", self.tcl_port)) + + def _wait_for_openocd_tcl(self): + """Wait for OpenOCD to start""" + # TODO: timeout + while True: + stderr = self.process.stderr + if not stderr: + break + line = stderr.readline() + if not line: + break + line = line.decode("utf-8").strip() + self.logger.debug(f"OpenOCD: {line}") + if "Listening on port" in line and "for tcl connections" in line: + break + + def stop(self): + self.send_tcl("exit") + self.send_tcl("shutdown") + self.socket.close() + try: + self.process.wait(timeout=10) + except subprocess.TimeoutExpired as e: + self.process.kill() + self.logger.error("Failed to stop OpenOCD") + self.logger.exception(e) + self.postmortem() + + def send_tcl(self, cmd) -> str: + """Send a command string to TCL RPC. Return the result that was read.""" + + try: + data = (cmd + OpenOCD.COMMAND_TOKEN).encode("utf-8") + self.logger.debug(f"<- {data}") + + self.socket.send(data) + except Exception as e: + self.logger.error("Failed to send command to OpenOCD") + self.logger.exception(e) + self.postmortem() + raise + + try: + data = self._recv() + return data + except Exception as e: + self.logger.error("Failed to receive response from OpenOCD") + self.logger.exception(e) + self.postmortem() + raise + + def _recv(self): + """Read from the stream until the token (\x1a) was received.""" + # TODO: timeout + data = bytes() + while True: + chunk = self.socket.recv(4096) + data += chunk + if bytes(OpenOCD.COMMAND_TOKEN, encoding="utf-8") in chunk: + break + + self.logger.debug(f"-> {data}") + + data = data.decode("utf-8").strip() + data = data[:-1] # strip trailing \x1a + + return data + + def postmortem(self) -> None: + """Postmortem analysis of the OpenOCD process""" + stdout, stderr = self.process.communicate() + + log = self.logger.error + if self.process.returncode == 0: + log = self.logger.debug + log("OpenOCD exited normally") + else: + log("OpenOCD exited with error") + + log(f"Exit code: {self.process.returncode}") + for line in stdout.decode("utf-8").splitlines(): + log(f"Stdout: {line}") + + for line in stderr.decode("utf-8").splitlines(): + log(f"Stderr: {line}") + + def read_32(self, addr: int) -> int: + """Read 32-bit value from memory""" + data = self.send_tcl(f"mdw {addr}").strip() + data = data.split(": ")[-1] + data = int(data, 16) + return data + + def write_32(self, addr: int, value: int) -> None: + """Write 32-bit value to memory""" + self.send_tcl(f"mww {addr} {value}") diff --git a/scripts/flipper/utils/programmer.py b/scripts/flipper/utils/programmer.py new file mode 100644 index 00000000..84452d15 --- /dev/null +++ b/scripts/flipper/utils/programmer.py @@ -0,0 +1,31 @@ +from abc import ABC, abstractmethod +from enum import Enum + + +class Programmer(ABC): + def __init__(self): + pass + + class RunMode(Enum): + Run = "run" + Stop = "stop" + + @abstractmethod + def reset(self, mode: RunMode = RunMode.Run) -> bool: + pass + + @abstractmethod + def flash(self, address: int, file_path: str, verify: bool = True) -> bool: + pass + + @abstractmethod + def option_bytes_validate(self, file_path: str) -> bool: + pass + + @abstractmethod + def option_bytes_set(self, file_path: str) -> bool: + pass + + @abstractmethod + def otp_write(self, address: int, file_path: str) -> bool: + pass diff --git a/scripts/flipper/utils/programmer_openocd.py b/scripts/flipper/utils/programmer_openocd.py new file mode 100644 index 00000000..b3340610 --- /dev/null +++ b/scripts/flipper/utils/programmer_openocd.py @@ -0,0 +1,281 @@ +import logging +import os +import typing + +from flipper.utils.programmer import Programmer +from flipper.utils.openocd import OpenOCD +from flipper.utils.stm32wb55 import STM32WB55 +from flipper.assets.obdata import OptionBytesData + + +class OpenOCDProgrammer(Programmer): + def __init__( + self, + interface: str = "interface/cmsis-dap.cfg", + port_base: typing.Union[int, None] = None, + serial: typing.Union[str, None] = None, + ): + super().__init__() + + config = {} + + config["interface"] = interface + config["target"] = "target/stm32wbx.cfg" + + if not serial is None: + if interface == "interface/cmsis-dap.cfg": + config["serial"] = f"cmsis_dap_serial {serial}" + elif "stlink" in interface: + config["serial"] = f"stlink_serial {serial}" + + if not port_base is None: + config["port_base"] = port_base + + self.openocd = OpenOCD(config) + self.logger = logging.getLogger() + + def reset(self, mode: Programmer.RunMode = Programmer.RunMode.Run) -> bool: + stm32 = STM32WB55() + if mode == Programmer.RunMode.Run: + stm32.reset(self.openocd, stm32.RunMode.Run) + elif mode == Programmer.RunMode.Stop: + stm32.reset(self.openocd, stm32.RunMode.Init) + else: + raise Exception("Unknown mode") + + return True + + def flash(self, address: int, file_path: str, verify: bool = True) -> bool: + if not os.path.exists(file_path): + raise Exception(f"File {file_path} not found") + + self.openocd.start() + self.openocd.send_tcl(f"init") + self.openocd.send_tcl( + f"program {file_path} 0x{address:08x}{' verify' if verify else ''} reset exit" + ) + self.openocd.stop() + + return True + + def _ob_print_diff_table(self, ob_reference: bytes, ob_read: bytes, print_fn): + print_fn( + f'{"Reference": <20} {"Device": <20} {"Diff Reference": <20} {"Diff Device": <20}' + ) + + # Split into 8 byte, word + word + for i in range(0, len(ob_reference), 8): + ref = ob_reference[i : i + 8] + read = ob_read[i : i + 8] + + diff_str1 = "" + diff_str2 = "" + for j in range(0, len(ref.hex()), 2): + byte_str_1 = ref.hex()[j : j + 2] + byte_str_2 = read.hex()[j : j + 2] + + if byte_str_1 == byte_str_2: + diff_str1 += "__" + diff_str2 += "__" + else: + diff_str1 += byte_str_1 + diff_str2 += byte_str_2 + + print_fn( + f"{ref.hex(): <20} {read.hex(): <20} {diff_str1: <20} {diff_str2: <20}" + ) + + def option_bytes_validate(self, file_path: str) -> bool: + # Registers + stm32 = STM32WB55() + + # OpenOCD + self.openocd.start() + stm32.reset(self.openocd, stm32.RunMode.Init) + + # Generate Option Bytes data + ob_data = OptionBytesData(file_path) + ob_values = ob_data.gen_values().export() + ob_reference = ob_values.reference + ob_compare_mask = ob_values.compare_mask + ob_length = len(ob_reference) + ob_words = int(ob_length / 4) + + # Read Option Bytes + ob_read = bytes() + for i in range(ob_words): + addr = stm32.OPTION_BYTE_BASE + i * 4 + value = self.openocd.read_32(addr) + ob_read += value.to_bytes(4, "little") + + # Compare Option Bytes with reference by mask + ob_compare = bytes() + for i in range(ob_length): + ob_compare += bytes([ob_read[i] & ob_compare_mask[i]]) + + # Compare Option Bytes + return_code = False + + if ob_reference == ob_compare: + self.logger.info("Option Bytes are valid") + return_code = True + else: + self.logger.error("Option Bytes are invalid") + self._ob_print_diff_table(ob_reference, ob_compare, self.logger.error) + + # Stop OpenOCD + stm32.reset(self.openocd, stm32.RunMode.Run) + self.openocd.stop() + + return return_code + + def _unpack_u32(self, data: bytes, offset: int): + return int.from_bytes(data[offset : offset + 4], "little") + + def option_bytes_set(self, file_path: str) -> bool: + # Registers + stm32 = STM32WB55() + + # OpenOCD + self.openocd.start() + stm32.reset(self.openocd, stm32.RunMode.Init) + + # Generate Option Bytes data + ob_data = OptionBytesData(file_path) + ob_values = ob_data.gen_values().export() + ob_reference_bytes = ob_values.reference + ob_compare_mask_bytes = ob_values.compare_mask + ob_write_mask_bytes = ob_values.write_mask + ob_length = len(ob_reference_bytes) + ob_dwords = int(ob_length / 8) + + # Clear flash errors + stm32.clear_flash_errors(self.openocd) + + # Unlock Flash and Option Bytes + stm32.flash_unlock(self.openocd) + stm32.option_bytes_unlock(self.openocd) + + ob_need_to_apply = False + + for i in range(ob_dwords): + device_addr = stm32.OPTION_BYTE_BASE + i * 8 + device_value = self.openocd.read_32(device_addr) + ob_write_mask = self._unpack_u32(ob_write_mask_bytes, i * 8) + ob_compare_mask = self._unpack_u32(ob_compare_mask_bytes, i * 8) + ob_value_ref = self._unpack_u32(ob_reference_bytes, i * 8) + ob_value_masked = device_value & ob_compare_mask + + need_patch = ((ob_value_masked ^ ob_value_ref) & ob_write_mask) != 0 + if need_patch: + ob_need_to_apply = True + + self.logger.info( + f"Need to patch: {device_addr:08X}: {ob_value_masked:08X} != {ob_value_ref:08X}, REG[{i}]" + ) + + # Check if this option byte (dword) is mapped to a register + device_reg_addr = stm32.option_bytes_id_to_address(i) + + # Construct new value for the OB register + ob_value = device_value & (~ob_write_mask) + ob_value |= ob_value_ref & ob_write_mask + + self.logger.info(f"Writing {ob_value:08X} to {device_reg_addr:08X}") + self.openocd.write_32(device_reg_addr, ob_value) + + if ob_need_to_apply: + stm32.option_bytes_apply(self.openocd) + else: + self.logger.info(f"Option Bytes are already correct") + + # Load Option Bytes + # That will reset and also lock the Option Bytes and the Flash + stm32.option_bytes_load(self.openocd) + + # Stop OpenOCD + stm32.reset(self.openocd, stm32.RunMode.Run) + self.openocd.stop() + + return True + + def otp_write(self, address: int, file_path: str) -> bool: + # Open file, check that it aligned to 8 bytes + with open(file_path, "rb") as f: + data = f.read() + if len(data) % 8 != 0: + self.logger.error(f"File {file_path} is not aligned to 8 bytes") + return False + + # Check that address is aligned to 8 bytes + if address % 8 != 0: + self.logger.error(f"Address {address} is not aligned to 8 bytes") + return False + + # Get size of data + data_size = len(data) + + # Check that data size is aligned to 8 bytes + if data_size % 8 != 0: + self.logger.error(f"Data size {data_size} is not aligned to 8 bytes") + return False + + self.logger.debug(f"Writing {data_size} bytes to OTP at {address:08X}") + self.logger.debug(f"Data: {data.hex().upper()}") + + # Start OpenOCD + oocd = self.openocd + oocd.start() + + # Registers + stm32 = STM32WB55() + + try: + # Check that OTP is empty for the given address + # Also check that data is already written + already_written = True + for i in range(0, data_size, 4): + file_word = int.from_bytes(data[i : i + 4], "little") + device_word = oocd.read_32(address + i) + if device_word != 0xFFFFFFFF and device_word != file_word: + self.logger.error( + f"OTP memory at {address + i:08X} is not empty: {device_word:08X}" + ) + raise Exception("OTP memory is not empty") + + if device_word != file_word: + already_written = False + + if already_written: + self.logger.info(f"OTP memory is already written with the given data") + return True + + self.reset(self.RunMode.Stop) + stm32.clear_flash_errors(oocd) + + # Write OTP memory by 8 bytes + for i in range(0, data_size, 8): + word_1 = int.from_bytes(data[i : i + 4], "little") + word_2 = int.from_bytes(data[i + 4 : i + 8], "little") + self.logger.debug( + f"Writing {word_1:08X} {word_2:08X} to {address + i:08X}" + ) + stm32.write_flash_64(oocd, address + i, word_1, word_2) + + # Validate OTP memory + validation_result = True + + for i in range(0, data_size, 4): + file_word = int.from_bytes(data[i : i + 4], "little") + device_word = oocd.read_32(address + i) + if file_word != device_word: + self.logger.error( + f"Validation failed: {file_word:08X} != {device_word:08X} at {address + i:08X}" + ) + validation_result = False + finally: + # Stop OpenOCD + stm32.reset(oocd, stm32.RunMode.Run) + oocd.stop() + + return validation_result diff --git a/scripts/flipper/utils/register.py b/scripts/flipper/utils/register.py new file mode 100644 index 00000000..26d66730 --- /dev/null +++ b/scripts/flipper/utils/register.py @@ -0,0 +1,95 @@ +from dataclasses import dataclass +from flipper.utils.openocd import OpenOCD + + +@dataclass +class RegisterBitDefinition: + name: str + offset: int + size: int + value: int = 0 + + +class Register32: + def __init__(self, address: int, definition_list: list[RegisterBitDefinition]): + self.__dict__["names"] = [definition.name for definition in definition_list] + self.names = [definition.name for definition in definition_list] # typecheck + self.address = address + self.definition_list = definition_list + + # Validate that the definitions are not overlapping + for i in range(len(definition_list)): + for j in range(i + 1, len(definition_list)): + if self._is_overlapping(definition_list[i], definition_list[j]): + raise ValueError("Register definitions are overlapping") + + self.freezed = True + + def _is_overlapping( + self, a: RegisterBitDefinition, b: RegisterBitDefinition + ) -> bool: + if a.offset + a.size <= b.offset: + return False + if b.offset + b.size <= a.offset: + return False + return True + + def _get_definition(self, name: str) -> RegisterBitDefinition: + for definition in self.definition_list: + if definition.name == name: + return definition + raise ValueError(f"Register definition '{name}' not found") + + def get_definition_list(self) -> list[RegisterBitDefinition]: + return self.definition_list + + def get_address(self) -> int: + return self.address + + def set_reg_value(self, name: str, value: int): + definition = self._get_definition(name) + if value > (1 << definition.size) - 1: + raise ValueError( + f"Value {value} is too large for register definition '{name}'" + ) + definition.value = value + + def get_reg_value(self, name: str) -> int: + definition = self._get_definition(name) + return definition.value + + def __getattr__(self, attr): + if str(attr) in self.names: + return self.get_reg_value(str(attr)) + else: + return self.__dict__[attr] + + def __setattr__(self, attr, value): + if str(attr) in self.names: + self.set_reg_value(str(attr), value) + else: + if attr in self.__dict__ or "freezed" not in self.__dict__: + self.__dict__[attr] = value + else: + raise AttributeError(f"Attribute '{attr}' not found") + + def __dir__(self): + return self.names + + def set(self, value: int): + for definition in self.definition_list: + definition.value = (value >> definition.offset) & ( + (1 << definition.size) - 1 + ) + + def get(self) -> int: + value = 0 + for definition in self.definition_list: + value |= definition.value << definition.offset + return value + + def load(self, openocd: OpenOCD): + self.set(openocd.read_32(self.address)) + + def store(self, openocd: OpenOCD): + openocd.write_32(self.address, self.get()) diff --git a/scripts/flipper/utils/stm32wb55.py b/scripts/flipper/utils/stm32wb55.py new file mode 100644 index 00000000..910b0d7d --- /dev/null +++ b/scripts/flipper/utils/stm32wb55.py @@ -0,0 +1,352 @@ +import logging +from enum import Enum + +from flipper.utils.openocd import OpenOCD +from flipper.utils.register import Register32, RegisterBitDefinition + + +class STM32WB55: + # Address of OTP memory in flash + OTP_BASE = 0x1FFF7000 + + # Address of Option byte in flash + OPTION_BYTE_BASE = 0x1FFF8000 + + # Flash base address + FLASH_BASE = 0x58004000 + + # Flash unlock register + FLASH_KEYR = FLASH_BASE + 0x08 + + # Option byte unlock register + FLASH_OPTKEYR = FLASH_BASE + 0x0C + + # Flash unlock keys + FLASH_UNLOCK_KEY1 = 0x45670123 + FLASH_UNLOCK_KEY2 = 0xCDEF89AB + + # Option byte unlock keys + FLASH_UNLOCK_OPTKEY1 = 0x08192A3B + FLASH_UNLOCK_OPTKEY2 = 0x4C5D6E7F + + # Flash control register + FLASH_CR = Register32( + FLASH_BASE + 0x14, + [ + RegisterBitDefinition("PG", 0, 1), + RegisterBitDefinition("PER", 1, 1), + RegisterBitDefinition("MER", 2, 1), + RegisterBitDefinition("PNB", 3, 8), + RegisterBitDefinition("_", 11, 5), + RegisterBitDefinition("STRT", 16, 1), + RegisterBitDefinition("OPT_STRT", 17, 1), + RegisterBitDefinition("FSTPG", 18, 1), + RegisterBitDefinition("_", 19, 5), + RegisterBitDefinition("EOPIE", 24, 1), + RegisterBitDefinition("ERRIE", 25, 1), + RegisterBitDefinition("RD_ERRIE", 26, 1), + RegisterBitDefinition("OBL_LAUNCH", 27, 1), + RegisterBitDefinition("_", 28, 2), + RegisterBitDefinition("OPT_LOCK", 30, 1), + RegisterBitDefinition("LOCK", 31, 1), + ], + ) + + # Flash status register + FLASH_SR = Register32( + FLASH_BASE + 0x10, + [ + RegisterBitDefinition("EOP", 0, 1), + RegisterBitDefinition("OP_ERR", 1, 1), + RegisterBitDefinition("_", 2, 1), + RegisterBitDefinition("PROG_ERR", 3, 1), + RegisterBitDefinition("WRP_ERR", 4, 1), + RegisterBitDefinition("PGA_ERR", 5, 1), + RegisterBitDefinition("SIZE_ERR", 6, 1), + RegisterBitDefinition("PGS_ERR", 7, 1), + RegisterBitDefinition("MISS_ERR", 8, 1), + RegisterBitDefinition("FAST_ERR", 9, 1), + RegisterBitDefinition("_", 10, 3), + RegisterBitDefinition("OPTNV", 13, 1), + RegisterBitDefinition("RD_ERR", 14, 1), + RegisterBitDefinition("OPTV_ERR", 15, 1), + RegisterBitDefinition("BSY", 16, 1), + RegisterBitDefinition("_", 17, 1), + RegisterBitDefinition("CFGBSY", 18, 1), + RegisterBitDefinition("PESD", 19, 1), + RegisterBitDefinition("_", 20, 12), + ], + ) + + # Option byte registers + FLASH_OPTR = FLASH_BASE + 0x20 + FLASH_PCROP1ASR = FLASH_BASE + 0x24 + FLASH_PCROP1AER = FLASH_BASE + 0x28 + FLASH_WRP1AR = FLASH_BASE + 0x2C + FLASH_WRP1BR = FLASH_BASE + 0x30 + FLASH_PCROP1BSR = FLASH_BASE + 0x34 + FLASH_PCROP1BER = FLASH_BASE + 0x38 + FLASH_IPCCBR = FLASH_BASE + 0x3C + + # Map option byte dword index to register address + OPTION_BYTE_MAP_TO_REGS = { + 0: FLASH_OPTR, + 1: FLASH_PCROP1ASR, + 2: FLASH_PCROP1AER, + 3: FLASH_WRP1AR, + 4: FLASH_WRP1BR, + 5: FLASH_PCROP1BSR, + 6: FLASH_PCROP1BER, + 7: None, # Invalid Options + 8: None, # Invalid Options + 9: None, # Invalid Options + 10: None, # Invalid Options + 11: None, # Invalid Options + 12: None, # Invalid Options + 13: FLASH_IPCCBR, + 14: None, # Secure Flash + 15: None, # Core 2 Options + } + + def __init__(self): + self.logger = logging.getLogger("STM32WB55") + + class RunMode(Enum): + Init = "init" + Run = "run" + Halt = "halt" + + def reset(self, oocd: OpenOCD, mode: RunMode): + self.logger.debug("Resetting device") + oocd.send_tcl(f"reset {mode.value}") + + def clear_flash_errors(self, oocd: OpenOCD): + # Errata 2.2.9: Flash OPTVERR flag is always set after system reset + # And also clear all other flash error flags + self.logger.debug(f"Resetting flash errors") + self.FLASH_SR.load(oocd) + self.FLASH_SR.OP_ERR = 1 + self.FLASH_SR.PROG_ERR = 1 + self.FLASH_SR.WRP_ERR = 1 + self.FLASH_SR.PGA_ERR = 1 + self.FLASH_SR.SIZE_ERR = 1 + self.FLASH_SR.PGS_ERR = 1 + self.FLASH_SR.MISS_ERR = 1 + self.FLASH_SR.FAST_ERR = 1 + self.FLASH_SR.RD_ERR = 1 + self.FLASH_SR.OPTV_ERR = 1 + self.FLASH_SR.store(oocd) + + def flash_unlock(self, oocd: OpenOCD): + # Check if flash is already unlocked + self.FLASH_CR.load(oocd) + if self.FLASH_CR.LOCK == 0: + self.logger.debug("Flash is already unlocked") + return + + # Unlock flash + self.logger.debug("Unlocking Flash") + oocd.write_32(self.FLASH_KEYR, self.FLASH_UNLOCK_KEY1) + oocd.write_32(self.FLASH_KEYR, self.FLASH_UNLOCK_KEY2) + + # Check if flash is unlocked + self.FLASH_CR.load(oocd) + if self.FLASH_CR.LOCK == 0: + self.logger.debug("Flash unlocked") + else: + self.logger.error("Flash unlock failed") + raise Exception("Flash unlock failed") + + def option_bytes_unlock(self, oocd: OpenOCD): + # Check if options is already unlocked + self.FLASH_CR.load(oocd) + if self.FLASH_CR.OPT_LOCK == 0: + self.logger.debug("Options is already unlocked") + return + + # Unlock options + self.logger.debug("Unlocking Options") + oocd.write_32(self.FLASH_OPTKEYR, self.FLASH_UNLOCK_OPTKEY1) + oocd.write_32(self.FLASH_OPTKEYR, self.FLASH_UNLOCK_OPTKEY2) + + # Check if options is unlocked + self.FLASH_CR.load(oocd) + if self.FLASH_CR.OPT_LOCK == 0: + self.logger.debug("Options unlocked") + else: + self.logger.error("Options unlock failed") + raise Exception("Options unlock failed") + + def option_bytes_lock(self, oocd: OpenOCD): + # Check if options is already locked + self.FLASH_CR.load(oocd) + if self.FLASH_CR.OPT_LOCK == 1: + self.logger.debug("Options is already locked") + return + + # Lock options + self.logger.debug("Locking Options") + self.FLASH_CR.OPT_LOCK = 1 + self.FLASH_CR.store(oocd) + + # Check if options is locked + self.FLASH_CR.load(oocd) + if self.FLASH_CR.OPT_LOCK == 1: + self.logger.debug("Options locked") + else: + self.logger.error("Options lock failed") + raise Exception("Options lock failed") + + def flash_lock(self, oocd: OpenOCD): + # Check if flash is already locked + self.FLASH_CR.load(oocd) + if self.FLASH_CR.LOCK == 1: + self.logger.debug("Flash is already locked") + return + + # Lock flash + self.logger.debug("Locking Flash") + self.FLASH_CR.LOCK = 1 + self.FLASH_CR.store(oocd) + + # Check if flash is locked + self.FLASH_CR.load(oocd) + if self.FLASH_CR.LOCK == 1: + self.logger.debug("Flash locked") + else: + self.logger.error("Flash lock failed") + raise Exception("Flash lock failed") + + def option_bytes_apply(self, oocd: OpenOCD): + self.logger.debug(f"Applying Option Bytes") + + self.FLASH_CR.load(oocd) + self.FLASH_CR.OPT_STRT = 1 + self.FLASH_CR.store(oocd) + + # Wait for Option Bytes to be applied + self.flash_wait_for_operation(oocd) + + def option_bytes_load(self, oocd: OpenOCD): + self.logger.debug(f"Loading Option Bytes") + self.FLASH_CR.load(oocd) + self.FLASH_CR.OBL_LAUNCH = 1 + self.FLASH_CR.store(oocd) + + def option_bytes_id_to_address(self, id: int) -> int: + # Check if this option byte (dword) is mapped to a register + device_reg_addr = self.OPTION_BYTE_MAP_TO_REGS.get(id, None) + if device_reg_addr is None: + raise Exception(f"Option Byte {id} is not mapped to a register") + + return device_reg_addr + + def flash_wait_for_operation(self, oocd: OpenOCD): + # Wait for flash operation to complete + # TODO: timeout + while True: + self.FLASH_SR.load(oocd) + if self.FLASH_SR.BSY == 0: + break + + def flash_dump_status_register(self, oocd: OpenOCD): + self.FLASH_SR.load(oocd) + self.logger.info(f"FLASH_SR: {self.FLASH_SR.get():08x}") + if self.FLASH_SR.EOP: + self.logger.info(" End of operation") + if self.FLASH_SR.OP_ERR: + self.logger.error(" Operation error") + if self.FLASH_SR.PROG_ERR: + self.logger.error(" Programming error") + if self.FLASH_SR.WRP_ERR: + self.logger.error(" Write protection error") + if self.FLASH_SR.PGA_ERR: + self.logger.error(" Programming alignment error") + if self.FLASH_SR.SIZE_ERR: + self.logger.error(" Size error") + if self.FLASH_SR.PGS_ERR: + self.logger.error(" Programming sequence error") + if self.FLASH_SR.MISS_ERR: + self.logger.error(" Fast programming data miss error") + if self.FLASH_SR.FAST_ERR: + self.logger.error(" Fast programming error") + if self.FLASH_SR.OPTNV: + self.logger.info(" User option OPTVAL indication") + if self.FLASH_SR.RD_ERR: + self.logger.info(" PCROP read error") + if self.FLASH_SR.OPTV_ERR: + self.logger.info(" Option and Engineering bits loading validity error") + if self.FLASH_SR.BSY: + self.logger.info(" Busy") + if self.FLASH_SR.CFGBSY: + self.logger.info(" Programming or erase configuration busy") + if self.FLASH_SR.PESD: + self.logger.info(" Programming / erase operation suspended.") + + def write_flash_64(self, oocd: OpenOCD, address: int, word_1: int, word_2: int): + self.logger.debug(f"Writing flash at address {address:08x}") + + if address % 8 != 0: + self.logger.error("Address must be aligned to 8 bytes") + raise Exception("Address must be aligned to 8 bytes") + + if word_1 == oocd.read_32(address) and word_2 == oocd.read_32(address + 4): + self.logger.debug("Data is already programmed") + return + + self.flash_unlock(oocd) + + # Check that no flash main memory operation is ongoing by checking the BSY bit + self.FLASH_SR.load(oocd) + if self.FLASH_SR.BSY: + self.logger.error("Flash is busy") + self.flash_dump_status_register(oocd) + raise Exception("Flash is busy") + + # Enable end of operation interrupts and error interrupts + self.FLASH_CR.load(oocd) + self.FLASH_CR.EOPIE = 1 + self.FLASH_CR.ERRIE = 1 + self.FLASH_CR.store(oocd) + + # Check that flash memory program and erase operations are allowed + if self.FLASH_SR.PESD: + self.logger.error("Flash operations are not allowed") + self.flash_dump_status_register(oocd) + raise Exception("Flash operations are not allowed") + + # Check and clear all error programming flags due to a previous programming. + self.clear_flash_errors(oocd) + + # Set the PG bit in the Flash memory control register (FLASH_CR) + self.FLASH_CR.load(oocd) + self.FLASH_CR.PG = 1 + self.FLASH_CR.store(oocd) + + # Perform the data write operation at the desired memory address, only double word (64 bits) can be programmed. + # Write the first word + oocd.send_tcl(f"mww 0x{address:08x} 0x{word_1:08x}") + # Write the second word + oocd.send_tcl(f"mww 0x{(address + 4):08x} 0x{word_2:08x}") + + # Wait for the BSY bit to be cleared + self.flash_wait_for_operation(oocd) + + # Check that EOP flag is set in the FLASH_SR register + self.FLASH_SR.load(oocd) + if not self.FLASH_SR.EOP: + self.logger.error("Flash operation failed") + self.flash_dump_status_register(oocd) + raise Exception("Flash operation failed") + + # Clear the EOP flag + self.FLASH_SR.load(oocd) + self.FLASH_SR.EOP = 1 + self.FLASH_SR.store(oocd) + + # Clear the PG bit in the FLASH_CR register + self.FLASH_CR.load(oocd) + self.FLASH_CR.PG = 0 + self.FLASH_CR.store(oocd) + + self.flash_lock(oocd) diff --git a/scripts/ob.py b/scripts/ob.py index 722549d6..178fe16a 100755 --- a/scripts/ob.py +++ b/scripts/ob.py @@ -1,69 +1,79 @@ #!/usr/bin/env python3 -import logging -import argparse -import subprocess -import sys -import os +from os import path from flipper.app import App -from flipper.cube import CubeProgrammer +from flipper.utils.programmer_openocd import OpenOCDProgrammer class Main(App): def init(self): + # Subparsers self.subparsers = self.parser.add_subparsers(help="sub-command help") + + # Check command self.parser_check = self.subparsers.add_parser( "check", help="Check Option Bytes" ) - self._addArgsSWD(self.parser_check) + self._add_args(self.parser_check) self.parser_check.set_defaults(func=self.check) + # Set command self.parser_set = self.subparsers.add_parser("set", help="Set Option Bytes") - self._addArgsSWD(self.parser_set) + self._add_args(self.parser_set) self.parser_set.set_defaults(func=self.set) - # OB - self.ob = {} - def _addArgsSWD(self, parser): + def _add_args(self, parser): parser.add_argument( - "--port", type=str, help="Port to connect: swd or usb1", default="swd" + "--port-base", type=int, help="OpenOCD port base", default=3333 + ) + parser.add_argument( + "--interface", + type=str, + help="OpenOCD interface", + default="interface/cmsis-dap.cfg", + ) + parser.add_argument( + "--serial", type=str, help="OpenOCD interface serial number" + ) + parser.add_argument( + "--ob-path", + type=str, + help="Option bytes file", + default=path.join(path.dirname(__file__), "ob.data"), ) - parser.add_argument("--serial", type=str, help="ST-Link Serial Number") - - def _getCubeParams(self): - return { - "port": self.args.port, - "serial": self.args.serial, - } - - def before(self): - self.logger.info(f"Loading Option Bytes data") - file_path = os.path.join(os.path.dirname(sys.argv[0]), "ob.data") - with open(file_path, "r") as file: - for line in file.readlines(): - k, v, o = line.split(":") - self.ob[k.strip()] = v.strip(), o.strip() def check(self): self.logger.info(f"Checking Option Bytes") - cp = CubeProgrammer(self._getCubeParams()) - if cp.checkOptionBytes(self.ob): - self.logger.info(f"OB Check OK") - return 0 - else: - self.logger.error(f"OB Check FAIL") - return 255 + + # OpenOCD + openocd = OpenOCDProgrammer( + self.args.interface, + self.args.port_base, + self.args.serial, + ) + + return_code = 1 + if openocd.option_bytes_validate(self.args.ob_path): + return_code = 0 + + return return_code def set(self): self.logger.info(f"Setting Option Bytes") - cp = CubeProgrammer(self._getCubeParams()) - if cp.setOptionBytes(self.ob): - self.logger.info(f"OB Set OK") - return 0 - else: - self.logger.error(f"OB Set FAIL") - return 255 + + # OpenOCD + openocd = OpenOCDProgrammer( + self.args.interface, + self.args.port_base, + self.args.serial, + ) + + return_code = 1 + if openocd.option_bytes_set(self.args.ob_path): + return_code = 0 + + return return_code if __name__ == "__main__": diff --git a/scripts/otp.py b/scripts/otp.py index 48056fd2..3bfe30d2 100755 --- a/scripts/otp.py +++ b/scripts/otp.py @@ -35,6 +35,7 @@ OTP_DISPLAYS = { from flipper.app import App from flipper.cube import CubeProgrammer +from flipper.utils.programmer_openocd import OpenOCDProgrammer class Main(App): @@ -53,21 +54,21 @@ class Main(App): self.parser_flash_first = self.subparsers.add_parser( "flash_first", help="Flash first block of OTP to device" ) - self._addArgsSWD(self.parser_flash_first) + self._addArgsOpenOCD(self.parser_flash_first) self._addFirstArgs(self.parser_flash_first) self.parser_flash_first.set_defaults(func=self.flash_first) # Flash Second self.parser_flash_second = self.subparsers.add_parser( "flash_second", help="Flash second block of OTP to device" ) - self._addArgsSWD(self.parser_flash_second) + self._addArgsOpenOCD(self.parser_flash_second) self._addSecondArgs(self.parser_flash_second) self.parser_flash_second.set_defaults(func=self.flash_second) # Flash All self.parser_flash_all = self.subparsers.add_parser( "flash_all", help="Flash OTP to device" ) - self._addArgsSWD(self.parser_flash_all) + self._addArgsOpenOCD(self.parser_flash_all) self._addFirstArgs(self.parser_flash_all) self._addSecondArgs(self.parser_flash_all) self.parser_flash_all.set_defaults(func=self.flash_all) @@ -75,17 +76,19 @@ class Main(App): self.logger = logging.getLogger() self.timestamp = datetime.datetime.now().timestamp() - def _addArgsSWD(self, parser): + def _addArgsOpenOCD(self, parser): parser.add_argument( - "--port", type=str, help="Port to connect: swd or usb1", default="swd" + "--port-base", type=int, help="OpenOCD port base", default=3333 + ) + parser.add_argument( + "--interface", + type=str, + help="OpenOCD interface", + default="interface/cmsis-dap.cfg", + ) + parser.add_argument( + "--serial", type=str, help="OpenOCD interface serial number" ) - parser.add_argument("--serial", type=str, help="ST-Link Serial Number") - - def _getCubeParams(self): - return { - "port": self.args.port, - "serial": self.args.serial, - } def _addFirstArgs(self, parser): parser.add_argument("--version", type=int, help="Version", required=True) @@ -173,14 +176,22 @@ class Main(App): file.write(self._packFirst()) self.logger.info(f"Flashing OTP") - cp = CubeProgrammer(self._getCubeParams()) - cp.flashBin("0x1FFF7000", filename) - cp.resetTarget() + + openocd = OpenOCDProgrammer( + self.args.interface, + self.args.port_base, + self.args.serial, + ) + + if not openocd.otp_write(0x1FFF7000, filename): + raise Exception("Failed to flash OTP") + self.logger.info(f"Flashed Successfully") - os.remove(filename) except Exception as e: self.logger.exception(e) return 1 + finally: + os.remove(filename) return 0 @@ -197,14 +208,22 @@ class Main(App): file.write(self._packSecond()) self.logger.info(f"Flashing OTP") - cp = CubeProgrammer(self._getCubeParams()) - cp.flashBin("0x1FFF7010", filename) - cp.resetTarget() + + openocd = OpenOCDProgrammer( + self.args.interface, + self.args.port_base, + self.args.serial, + ) + + if not openocd.otp_write(0x1FFF7010, filename): + raise Exception("Failed to flash OTP") + self.logger.info(f"Flashed Successfully") - os.remove(filename) except Exception as e: self.logger.exception(e) return 1 + finally: + os.remove(filename) return 0 @@ -223,14 +242,22 @@ class Main(App): file.write(self._packSecond()) self.logger.info(f"Flashing OTP") - cp = CubeProgrammer(self._getCubeParams()) - cp.flashBin("0x1FFF7000", filename) - cp.resetTarget() + + openocd = OpenOCDProgrammer( + self.args.interface, + self.args.port_base, + self.args.serial, + ) + + if not openocd.otp_write(0x1FFF7000, filename): + raise Exception("Failed to flash OTP") + self.logger.info(f"Flashed Successfully") - os.remove(filename) except Exception as e: self.logger.exception(e) return 1 + finally: + os.remove(filename) return 0 diff --git a/scripts/power.py b/scripts/power.py new file mode 100755 index 00000000..45a130c5 --- /dev/null +++ b/scripts/power.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python3 + +from flipper.app import App +from flipper.storage import FlipperStorage +from flipper.utils.cdc import resolve_port + + +class Main(App): + # this is basic use without sub-commands, simply to reboot flipper / power it off, not meant as a full CLI wrapper + def init(self): + self.parser.add_argument("-p", "--port", help="CDC Port", default="auto") + + self.subparsers = self.parser.add_subparsers(help="sub-command help") + + self.parser_power_off = self.subparsers.add_parser( + "power_off", help="Power off command, won't return to CLI" + ) + self.parser_power_off.set_defaults(func=self.power_off) + + self.parser_reboot = self.subparsers.add_parser( + "reboot", help="Reboot command help" + ) + self.parser_reboot.set_defaults(func=self.reboot) + + self.parser_reboot2dfu = self.subparsers.add_parser( + "reboot2dfu", help="Reboot to DFU, won't return to CLI" + ) + self.parser_reboot2dfu.set_defaults(func=self.reboot2dfu) + + def _get_flipper(self): + if not (port := resolve_port(self.logger, self.args.port)): + return None + + flipper = FlipperStorage(port) + flipper.start() + return flipper + + def power_off(self): + if not (flipper := self._get_flipper()): + return 1 + + self.logger.debug("Powering off") + flipper.send("power off" + "\r") + flipper.stop() + return 0 + + def reboot(self): + if not (flipper := self._get_flipper()): + return 1 + + self.logger.debug("Rebooting") + flipper.send("power reboot" + "\r") + flipper.stop() + return 0 + + def reboot2dfu(self): + if not (flipper := self._get_flipper()): + return 1 + + self.logger.debug("Rebooting to DFU") + flipper.send("power reboot2dfu" + "\r") + flipper.stop() + + return 0 + + +if __name__ == "__main__": + Main()() diff --git a/scripts/testing/await_flipper.py b/scripts/testing/await_flipper.py index efae6765..2b4c8b4c 100755 --- a/scripts/testing/await_flipper.py +++ b/scripts/testing/await_flipper.py @@ -1,6 +1,8 @@ #!/usr/bin/env python3 - -import sys, os, time +import logging +import os +import sys +import time def flp_serial_by_name(flp_name): @@ -24,13 +26,19 @@ def flp_serial_by_name(flp_name): return "" -UPDATE_TIMEOUT = 60 +UPDATE_TIMEOUT = 60 * 4 # 4 minutes def main(): flipper_name = sys.argv[1] elapsed = 0 flipper = flp_serial_by_name(flipper_name) + logging.basicConfig( + format="%(asctime)s %(levelname)-8s %(message)s", + level=logging.INFO, + datefmt="%Y-%m-%d %H:%M:%S", + ) + logging.info("Waiting for Flipper to be ready...") while flipper == "" and elapsed < UPDATE_TIMEOUT: elapsed += 1 @@ -38,9 +46,11 @@ def main(): flipper = flp_serial_by_name(flipper_name) if flipper == "": - print(f"Cannot find {flipper_name} flipper. Guess your flipper swam away") + logging.error("Flipper not found!") sys.exit(1) + logging.info(f"Found Flipper at {flipper}") + sys.exit(0) diff --git a/scripts/testing/units.py b/scripts/testing/units.py index 83b07899..5083bcd4 100755 --- a/scripts/testing/units.py +++ b/scripts/testing/units.py @@ -1,28 +1,32 @@ #!/usr/bin/env python3 - -import sys, os -import serial +import logging import re +import sys +import serial from await_flipper import flp_serial_by_name -LEAK_THRESHOLD = 3000 # added until units are fixed - - def main(): + logging.basicConfig( + format="%(asctime)s %(levelname)-8s %(message)s", + level=logging.INFO, + datefmt="%Y-%m-%d %H:%M:%S", + ) + logging.info("Trying to run units on flipper") flp_serial = flp_serial_by_name(sys.argv[1]) if flp_serial == "": - print("Name or serial port is invalid") + logging.error("Flipper not found!") sys.exit(1) with serial.Serial(flp_serial, timeout=1) as flipper: + logging.info(f"Found Flipper at {flp_serial}") flipper.baudrate = 230400 flipper.flushOutput() flipper.flushInput() - flipper.timeout = 300 + flipper.timeout = 180 flipper.read_until(b">: ").decode("utf-8") flipper.write(b"unit_tests\r") @@ -41,9 +45,13 @@ def main(): status_pattern = re.compile(status_re) tests, time, leak, status = None, None, None, None + total = 0 for line in lines: - print(line) + logging.info(line) + if "()" in line: + total += 1 + if not tests: tests = re.match(tests_pattern, line) if not time: @@ -53,8 +61,8 @@ def main(): if not status: status = re.match(status_pattern, line) - if leak is None or time is None or leak is None or status is None: - print("Failed to get data. Or output is corrupt") + if None in (tests, time, leak, status): + logging.error(f"Failed to parse output: {leak} {time} {leak} {status}") sys.exit(1) leak = int(re.findall(r"[- ]\d+", leak.group(0))[0]) @@ -62,16 +70,18 @@ def main(): tests = int(re.findall(r"\d+", tests.group(0))[0]) time = int(re.findall(r"\d+", time.group(0))[0]) - if tests > 0 or leak > LEAK_THRESHOLD or status != "PASSED": - print(f"Got {tests} failed tests.") - print(f"Leaked {leak} bytes.") - print(f"Status by flipper: {status}") - print(f"Time elapsed {time/1000} seconds.") + if tests > 0 or status != "PASSED": + logging.error(f"Got {tests} failed tests.") + logging.error(f"Leaked (not failing on this stat): {leak}") + logging.error(f"Status: {status}") + logging.error(f"Time: {time/1000} seconds") sys.exit(1) - print( - f"Tests ran successfully! Time elapsed {time/1000} seconds. Passed {tests} tests." + logging.info(f"Leaked (not failing on this stat): {leak}") + logging.info( + f"Tests ran successfully! Time elapsed {time/1000} seconds. Passed {total} tests." ) + sys.exit(0)