diff --git a/.github/assets/dark_theme_banner.png b/.github/assets/dark_theme_banner.png deleted file mode 100644 index 10a4e8c30..000000000 Binary files a/.github/assets/dark_theme_banner.png and /dev/null differ diff --git a/.github/assets/light_theme_banner.png b/.github/assets/light_theme_banner.png deleted file mode 100644 index 6c6b3b708..000000000 Binary files a/.github/assets/light_theme_banner.png and /dev/null differ diff --git a/.github/workflow_data/release.md b/.github/workflow_data/release.md new file mode 100644 index 000000000..1dcbf31ee --- /dev/null +++ b/.github/workflow_data/release.md @@ -0,0 +1,22 @@ +## ⬇️ Download +>### [🐬 qFlipper Package (.tgz)](https://github.com/ClaraCrazy/Flipper-Xtreme/releases/download/{release_tag}/{release_tag}.tgz) [recommended] + +>### [📦 Zipped Archive (.zip)](https://github.com/ClaraCrazy/Flipper-Xtreme/releases/download/{release_tag}/{release_tag}.zip) + +>### [🖥️ Web Updater (chrome)](https://lab.flipper.net/?url={webupdater_url}&channel=XFW-Updater&version={release_tag}) [not recommended] + +**Remember to delete your `apps` (and `update` if using "Install from file...") folders before updating!**\ +**Check the [install guide](https://github.com/ClaraCrazy/Flipper-Xtreme#install) if you're not sure, or [join our Discord](https://discord.gg/flipper-xtreme) if you have questions!** + +## 🚀 Changelog +{changelog} + +## ❤️ Support +If you like what you're seeing, **please consider donating to us**. We won't ever put this behind a paywall, but we'd still appreciate a few bucks! + +- **[Direct transfer to my bank](https://bunq.me/ClaraK)**: No account needed, they have a convenient online pay thingy (that hates americans, sowwy) +- **[Patreon](https://patreon.com/CynthiaLabs)** +- **[Paypal](https://paypal.me/RdX2020)** +- **Monero**: 41kyWeeoVdK4quzQ4M9ikVGs6tCQCLfdx8jLExTNsAu2SF1QAyDqRdjfGM6EL8L9NpXwt89HJeAoGf1aoArk7nDr4AMMV4T + +**Thanks for all your support <3** diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b7eee574a..715070b83 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,28 +1,22 @@ -name: 'Build' +name: 'Build artifact' on: push: branches: - dev - - "release*" + - main tags: - '*' pull_request: env: - TARGETS: f7 f18 + TARGETS: f7 DEFAULT_TARGET: f7 - FBT_TOOLCHAIN_PATH: /home/runner/work jobs: - main: + build: runs-on: ubuntu-latest 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 @@ -30,70 +24,24 @@ jobs: fetch-depth: 0 ref: ${{ github.event.pull_request.head.sha }} - - name: 'Get commit details' - id: names - 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" - 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 - mkdir artifacts - - - name: 'Bundle scripts' - run: | - tar czpf artifacts/flipper-z-any-scripts-${SUFFIX}.tgz scripts debug - - name: 'Build the firmware' run: | set -e for TARGET in ${TARGETS}; do TARGET="$(echo "${TARGET}" | sed 's/f//')"; \ - ./fbt TARGET_HW=$TARGET copro_dist updater_package \ - ${{ startsWith(github.ref, 'refs/tags') && 'DEBUG=0 COMPACT=1' || '' }} - done - - - name: 'Move upload files' - run: | - set -e - for TARGET in ${TARGETS}; do - mv dist/${TARGET}-*/* artifacts/ + ./fbt TARGET_HW=$TARGET updater_package done - name: "Check for uncommitted changes" run: | git diff --exit-code - - name: 'Bundle resources' - run: | - tar czpf "artifacts/flipper-z-any-resources-${SUFFIX}.tgz" -C assets resources - - - name: 'Bundle core2 firmware' - run: | - cp build/core2_firmware.tgz "artifacts/flipper-z-any-core2_firmware-${SUFFIX}.tgz" - - name: 'Updater artifact' uses: actions/upload-artifact@v3 with: name: updater path: | - artifacts/f7-* - - - name: 'Firmware artifact' - uses: actions/upload-artifact@v3 - with: - name: firmware - path: | - artifacts + dist/${{ env.DEFAULT_TARGET }}-* # - name: 'Find Previous Comment' # if: ${{ github.event.pull_request }} @@ -102,7 +50,7 @@ jobs: # with: # issue-number: ${{ github.event.pull_request.number }} # comment-author: 'github-actions[bot]' - # body-includes: 'Compiled firmware for commit' + # body-includes: 'Compiled firmware:' # - name: Artifact info # id: artifact-info @@ -117,44 +65,6 @@ jobs: # comment-id: ${{ steps.fc.outputs.comment-id }} # issue-number: ${{ github.event.pull_request.number }} # body: | - # **Compiled firmware for commit `${{steps.names.outputs.commit_sha}}`:** + # **Compiled firmware:** # - [📦 Update package](${{steps.artifact-info.outputs.artifacts[0].archive_download_url}}) # edit-mode: replace - - compact: - if: ${{ !startsWith(github.ref, 'refs/tags') }} - runs-on: ubuntu-latest - 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 - submodules: true - 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: 'Build the firmware' - run: | - set -e - for TARGET in ${TARGETS}; do - TARGET="$(echo "${TARGET}" | sed 's/f//')"; \ - ./fbt TARGET_HW=$TARGET DEBUG=0 COMPACT=1 updater_package - done diff --git a/.github/workflows/check_submodules.yml b/.github/workflows/check_submodules.yml index d1a1a64c3..ad63f711b 100644 --- a/.github/workflows/check_submodules.yml +++ b/.github/workflows/check_submodules.yml @@ -1,10 +1,10 @@ -name: 'Check submodules branch' +name: 'Check submodules' on: push: branches: - dev - - "release*" + - main tags: - '*' pull_request: @@ -13,11 +13,6 @@ jobs: check_protobuf: runs-on: ubuntu-latest 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 diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 000000000..818aa7d84 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,27 @@ +name: 'Lint formatting' + +on: + push: + branches: + - dev + - main + tags: + - '*' + pull_request: + +env: + SET_GH_OUTPUT: 1 + +jobs: + lint: + runs-on: ubuntu-latest + steps: + + - name: 'Checkout code' + uses: actions/checkout@v3 + with: + fetch-depth: 0 + ref: ${{ github.event.pull_request.head.sha }} + + - name: 'Check code formatting' + run: ./fbt lint lint_py diff --git a/.github/workflows/lint_c.yml b/.github/workflows/lint_c.yml deleted file mode 100644 index 232e3c689..000000000 --- a/.github/workflows/lint_c.yml +++ /dev/null @@ -1,46 +0,0 @@ -name: 'Lint C/C++ with clang-format' - -on: - push: - branches: - - dev - - "release*" - tags: - - '*' - pull_request: - -env: - TARGETS: f7 - SET_GH_OUTPUT: 1 - -jobs: - lint_c_cpp: - runs-on: ubuntu-latest - 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: 'Check code formatting' - id: syntax_check - run: ./fbt lint - - - name: Report code formatting errors - if: failure() && steps.syntax_check.outputs.errors && github.event.pull_request - uses: peter-evans/create-or-update-comment@v1 - with: - issue-number: ${{ github.event.pull_request.number }} - body: | - Please fix following code formatting errors: - ``` - ${{ steps.syntax_check.outputs.errors }} - ``` - You might want to run `./fbt format` for an auto-fix. diff --git a/.github/workflows/pvs_studio.yml b/.github/workflows/pvs_studio.yml deleted file mode 100644 index 65a8b6150..000000000 --- a/.github/workflows/pvs_studio.yml +++ /dev/null @@ -1,93 +0,0 @@ -name: 'Static C/C++ analysis with PVS-Studio' - -on: - push: - branches: - - dev - - "release*" - tags: - - '*' - pull_request: - -env: - TARGETS: f7 - DEFAULT_TARGET: f7 - FBT_TOOLCHAIN_PATH: /runner/_work - -jobs: - analyse_c_cpp: - if: ${{ !github.event.pull_request.head.repo.fork }} - runs-on: [self-hosted, FlipperZeroShell] - 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 commit details' - id: names - 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: 'Supply PVS credentials' - run: | - pvs-studio-analyzer credentials ${{ secrets.PVS_STUDIO_CREDENTIALS }} - - - name: 'Convert PVS-Studio output to html and detect warnings' - id: pvs-warn - run: | - WARNINGS=0 - ./fbt COMPACT=1 PVSNOBROWSER=1 firmware_pvs || WARNINGS=1 - echo "warnings=${WARNINGS}" >> $GITHUB_OUTPUT - - - name: 'Upload report' - if: ${{ !github.event.pull_request.head.repo.fork && (steps.pvs-warn.outputs.warnings != 0) }} - uses: prewk/s3-cp-action@v2 - with: - aws_s3_endpoint: "${{ secrets.PVS_AWS_ENDPOINT }}" - aws_access_key_id: "${{ secrets.PVS_AWS_ACCESS_KEY }}" - aws_secret_access_key: "${{ secrets.PVS_AWS_SECRET_KEY }}" - source: "./build/f7-firmware-DC/pvsreport" - dest: "s3://${{ secrets.PVS_AWS_BUCKET }}/${{steps.names.outputs.branch_name}}/${{steps.names.outputs.default_target}}-${{steps.names.outputs.suffix}}/" - flags: "--recursive --acl public-read" - - - name: 'Find Previous Comment' - 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 }} - comment-author: 'github-actions[bot]' - body-includes: 'PVS-Studio report for commit' - - - name: 'Create or update comment' - 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 }} - issue-number: ${{ github.event.pull_request.number }} - body: | - **PVS-Studio report for commit `${{steps.names.outputs.commit_sha}}`:** - - [Report](https://pvs.flipp.dev/${{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 warnings before merge" - exit 1 - diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..dc6ead78d --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,98 @@ +name: "Release integration" + +on: + pull_request_review: + types: [submitted] + +env: + TARGETS: f7 + DEFAULT_TARGET: f7 + +jobs: + release: + if: | + github.event.pull_request.head.repo.full_name == github.event.pull_request.base.repo.full_name && + endsWith(github.event.pull_request.title, ' Release Candidate Changes') && + github.event.review.author_association == 'OWNER' && + startsWith(github.event.pull_request.title, 'V') && + github.event.pull_request.base.ref == 'main' && + github.event.pull_request.head.ref == 'dev' && + github.event.pull_request.state == 'open' && + github.event.pull_request.draft == false && + github.event.review.state == 'APPROVED' + runs-on: ubuntu-latest + permissions: + contents: write + steps: + + - name: 'Checkout code' + uses: actions/checkout@v3 + with: + fetch-depth: 0 + repository: ${{ github.event.pull_request.head.repo.full_name }} + ref: ${{ github.event.pull_request.head.ref }} + + - name: "Update version number" + run: | + VERSION="$(python -c "print('%04d' % int('${{ github.event.pull_request.title }}'.removeprefix('V').removesuffix(' Release Candidate Changes')), end='')")" + DATE="$(python -c "from datetime import date;print(date.today().strftime('%d%m%Y'), end='')")" + RELEASE_NAME="XFW-${VERSION}" + RELEASE_TAG="${RELEASE_NAME}_${DATE}" + echo "RELEASE_TAG=${RELEASE_TAG}" >> $GITHUB_ENV + + sed "s/VERSION = \"XFW-[[:digit:]]\{4\}\"/VERSION = \"${RELEASE_NAME}\"/g" ./scripts/version.py > ./scripts/version.py.new + rm ./scripts/version.py + mv ./scripts/version.py.new ./scripts/version.py + + sed "s/DIST_SUFFIX = \"XFW-[[:digit:]]\{4\}_[[:digit:]]\{8\}\"/DIST_SUFFIX = \"${RELEASE_TAG}\"/g" ./fbt_options.py > ./fbt_options.py.new + rm ./fbt_options.py + mv ./fbt_options.py.new ./fbt_options.py + + - name: 'Build the firmware' + run: | + set -e + for TARGET in ${TARGETS}; do + TARGET="$(echo "${TARGET}" | sed 's/f//')"; \ + ./fbt TARGET_HW=$TARGET updater_package + done + + - name: "Make tgz, zip and webupdater" + run: | + cd ./dist/${DEFAULT_TARGET}-* + mv flipper-z-${DEFAULT_TARGET}-update-*.tgz ${RELEASE_TAG}.tgz + cd ./${DEFAULT_TARGET}-update-* + 7z a ../${RELEASE_TAG}.zip . + cd .. + python -m pip install pyncclient + WEBUPDATER_URL="$(python -c "import nextcloud_client as n;c = n.Client('https://cloud.cynthialabs.net/');c.login('${{ secrets.NC_USER }}', '${{ secrets.NC_PASS }}');c.put_file('XFW-Updater/${RELEASE_TAG}.tgz', '${RELEASE_TAG}.tgz');print(c.share_file_with_link('XFW-Updater/${RELEASE_TAG}.tgz').get_link().rstrip('/') + '/download/${RELEASE_TAG}.tgz', end='')")" + echo "WEBUPDATER_URL=${WEBUPDATER_URL}" >> $GITHUB_ENV + + - name: "Update release notes" + run: | + python -c "import json, os;f = '.github/workflow_data/release.md';c = json.load(open(os.environ['GITHUB_EVENT_PATH']))['pull_request']['body'];f_ = open(f);n = f_.read().format(release_tag='${RELEASE_TAG}', webupdater_url='${WEBUPDATER_URL}', changelog=c);f_.close();f_ = open(f, 'w');f_.write(n);f_.close()" + + - name: "Commit new version number" + uses: EndBug/add-and-commit@v9 + with: + default_author: github_actions + message: Update version number + add: './fbt_options.py ./scripts/version.py' + + - name: "Merge pull request" + uses: "pascalgn/automerge-action@v0.15.6" + env: + MERGE_LABELS: "" + GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" + + - name: "Make release" + uses: softprops/action-gh-release@v1 + with: + body_path: ".github/workflow_data/release.md" + draft: false + prerelease: false + files: | + ${{ env.RELEASE_TAG }}.tgz + ${{ env.RELEASE_TAG }}.zip + name: "${{ env.RELEASE_TAG }}" + tag_name: "${{ env.RELEASE_TAG }}" + target_commitish: main diff --git a/.github/workflows/sonarqube.yaml b/.github/workflows/sonarqube.yaml index 5d0ee1eef..7f3d311b0 100644 --- a/.github/workflows/sonarqube.yaml +++ b/.github/workflows/sonarqube.yaml @@ -1,33 +1,36 @@ -name: SonarCloud +name: 'SonarCloud analysis' + on: workflow_dispatch: pull_request: types: [opened, synchronize, reopened] + +env: + TARGETS: f7 + DEFAULT_TARGET: f7 + jobs: - build: - name: Build and analyze + analyze: runs-on: ubuntu-latest env: SONAR_SCANNER_VERSION: 4.7.0.2747 SONAR_SERVER_URL: "https://sonarcloud.io" BUILD_WRAPPER_OUT_DIR: "$HOME/.sonar/build_wrapper_output" # Directory where build-wrapper output will be placed FBT_NO_SYNC: "true" - TARGETS: f7 - DEFAULT_TARGET: f7 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 - - uses: actions/checkout@v2 + + - name: 'Checkout code' + uses: actions/checkout@v3 with: + submodules: 'recursive' # FBT_NO_SYNC is on, get submodules now fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis - submodules: 'recursive' + ref: ${{ github.event.pull_request.head.sha }} + - name: Set up JDK 11 uses: actions/setup-java@v1 with: java-version: 11 + - name: Download and set up sonar-scanner env: SONAR_SCANNER_DOWNLOAD_URL: https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-${{ env.SONAR_SCANNER_VERSION }}-linux.zip @@ -36,6 +39,7 @@ jobs: curl -sSLo $HOME/.sonar/sonar-scanner.zip ${{ env.SONAR_SCANNER_DOWNLOAD_URL }} unzip -o $HOME/.sonar/sonar-scanner.zip -d $HOME/.sonar/ echo "$HOME/.sonar/sonar-scanner-${{ env.SONAR_SCANNER_VERSION }}-linux/bin" >> $GITHUB_PATH + - name: Download and set up build-wrapper env: BUILD_WRAPPER_DOWNLOAD_URL: ${{ env.SONAR_SERVER_URL }}/static/cpp/build-wrapper-linux-x86.zip @@ -43,10 +47,16 @@ jobs: curl -sSLo $HOME/.sonar/build-wrapper-linux-x86.zip ${{ env.BUILD_WRAPPER_DOWNLOAD_URL }} unzip -o $HOME/.sonar/build-wrapper-linux-x86.zip -d $HOME/.sonar/ echo "$HOME/.sonar/build-wrapper-linux-x86" >> $GITHUB_PATH + - name: Run build-wrapper run: | - mkdir $HOME/.sonar/build_wrapper_output - build-wrapper-linux-x86-64 --out-dir ${{ env.BUILD_WRAPPER_OUT_DIR }} ./sonar-build + mkdir ${{ env.BUILD_WRAPPER_OUT_DIR }} + set -e + for TARGET in ${TARGETS}; do + TARGET="$(echo "${TARGET}" | sed 's/f//')"; \ + build-wrapper-linux-x86-64 --out-dir ${{ env.BUILD_WRAPPER_OUT_DIR }} ./sonar-build "./fbt TARGET_HW=$TARGET updater_package" + done + - name: Run sonar-scanner env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index 025246faa..f816372fa 100644 --- a/.gitignore +++ b/.gitignore @@ -34,6 +34,10 @@ Brewfile.lock.json # Visual Studio .vs/ +# Kate +.kateproject +.kateconfig + # legendary cmake's build CMakeLists.txt @@ -68,9 +72,8 @@ PVS-Studio.log # Automate files, etc automate.py deployments/ -fbt_options.py commitnotes.md -lib/STM32CubeWB +fbt_options.py # Asset packs assets/dolphin/custom/* diff --git a/.gitmodules b/.gitmodules index a97e0933a..de580c3c0 100644 --- a/.gitmodules +++ b/.gitmodules @@ -28,6 +28,9 @@ [submodule "lib/cxxheaderparser"] path = lib/cxxheaderparser url = https://github.com/robotpy/cxxheaderparser.git +[submodule "applications/plugins/subbrute"] + path = applications/plugins/subbrute + url = https://github.com/derskythe/flipperzero-subbrute.git [submodule "applications/plugins/dap_link/lib/free-dap"] path = applications/plugins/dap_link/lib/free-dap url = https://github.com/ataradov/free-dap.git diff --git a/.pvsconfig b/.pvsconfig index a9ab9c9f6..49c63ad73 100644 --- a/.pvsconfig +++ b/.pvsconfig @@ -44,3 +44,6 @@ # Functions that always return the same error code //-V:picopass_device_decrypt:1048 + +# Examples +//V_EXCLUDE_PATH applications/examples/ \ No newline at end of file diff --git a/.vscode/example/launch.json b/.vscode/example/launch.json index 5c46d3979..f7a9f8269 100644 --- a/.vscode/example/launch.json +++ b/.vscode/example/launch.json @@ -11,9 +11,10 @@ "args": { "useSingleResult": true, "env": { - "PATH": "${workspaceFolder};${env:PATH}" + "PATH": "${workspaceFolder};${env:PATH}", + "FBT_QUIET": 1 }, - "command": "./fbt get_blackmagic", + "command": "fbt get_blackmagic", "description": "Get Blackmagic device", } } diff --git a/ReadMe.md b/ReadMe.md index 6ff014479..bfce45ed6 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -1,13 +1,14 @@

XFW - Xtreme Firmware for the Flipper Zero

+

Feature-rich, stable, customizable

-[Intro](https://github.com/ClaraCrazy/Flipper-Xtreme#What-makes-it-special) | [Animations](https://github.com/ClaraCrazy/Flipper-Xtreme#Animations--Asset-Packs) | [Docs](https://github.com/ClaraCrazy/Flipper-Xtreme/wiki) | [Changelog](https://github.com/ClaraCrazy/Flipper-Xtreme#list-of-changes) | [Known bugs](https://github.com/ClaraCrazy/Flipper-Xtreme/issues?q=is%3Aissue+is%3Aopen+label%3Arelease-pending) | [Install](https://github.com/ClaraCrazy/Flipper-Xtreme#Install) | [Build](https://github.com/ClaraCrazy/Flipper-Xtreme#build-it-yourself) | [Discord](https://discord.gg/flipper-xtreme) +[Intro](https://github.com/ClaraCrazy/Flipper-Xtreme#What-makes-it-special) | [Animations](https://github.com/ClaraCrazy/Flipper-Xtreme#Animations--Asset-Packs) | [Wiki](https://github.com/ClaraCrazy/Flipper-Xtreme/wiki) | [Changelog](https://github.com/ClaraCrazy/Flipper-Xtreme#list-of-changes) | [Known bugs](https://github.com/ClaraCrazy/Flipper-Xtreme/issues?q=is%3Aissue+is%3Aopen+label%3Arelease-pending) | [Install](https://github.com/ClaraCrazy/Flipper-Xtreme#Install) | [Build](https://github.com/ClaraCrazy/Flipper-Xtreme#build-it-yourself) | [Discord](https://discord.gg/flipper-xtreme) ----- -This firmware is a complete overhaul of the [Official Firmware](https://github.com/flipperdevices/flipperzero-firmware), it also features lots of awesome code-bits from [Unleashed](https://github.com/DarkFlippers/unleashed-firmware). +This firmware is a complete overhaul of the [Official Firmware](https://github.com/flipperdevices/flipperzero-firmware), and also features lots of awesome code-bits from [Unleashed](https://github.com/DarkFlippers/unleashed-firmware). -----
@@ -17,35 +18,29 @@ We have spent many hours perfecting this code even further, and getting the most The goal of this Firmware is to regularly bring out amazing updates based on what the community wants, with an actual understanding of whats going on. Fixing bugs that are regularly talked about, removing unstable / broken applications (.FAP) and actually using the level system that just sits abandoned everywhere else.

-- The focus of this firmware is functionality & stability: If an App doesnt work, its gone +-

Feature-rich: We include all commonly found apps in the firmware, as long as they work.

-- Asset Packs: Are you tired of having to remake your custom animations after every update, switching manifests and having a hard time sharing them, especially once you modify scanning assets too? Those times are over. Scroll down to learn more - -- Giving the level system a purpose: Right now, each level unlocks a new wallpaper. More on that below - -- Clean upgraded code: Some people wrote updates to certain files. These are... painful, to say the least. Here its all built with perfection in mind and integrated in a mostly clean way. I invite you all to compare the code with theirs. - -- Up2Date: This firmware receives updates from a few repositories, not just from its Upstream. If there are functional, but yet un-merged Pull requests on another flipper firmware that are good, they will be in here! +-

Stable: Many hours have been spent rewriting core parts of the Flippers firmware as well as some of its apps to ensure stability. A task that was long needed on all Firmware, so we tackled it right away.

+-

Customizable: Dont like the Animations, want to turn on/off the Home screen icons (battery, SD card etc), change the flippers name or anything like that? You absolutely can. No need to mess with code or deal with weird manifest files. Its all done with an App.

-----

Xtreme Settings:

- We wrote a powerful yet easy-to-use application specifically for our Firmware, that gives you easy-access to all the fancy things we implemented: - + -Base Graphics: Change the fallback assets used. Its either SFW (default) or NSFW -
Asset Pack: Allows you to easily customize your firmware, more on that below -
Anim Speed: Speed in which the animations play -
Cycle Anims: Duration of how long animations are played before switching to next -
Unlock Anims: Custom setting just for NSFW fallback animations. Figure it out ;) -
Battery Icon: Classic Firmware battery style toggle, just at a more convenient place -
XP Level: Changes your Flippers level -
SubGhz Extend: Allows you to extend the subghz range beyond what FZ devs planned -
SubGhz Bypass: Allows you to bypass the subghz region locks of the Flipper +
Graphics:Change the animation package (more on that below), the play speed of them, cycle duration and bypass level-bassed animations
+ +
Statusbar:Modify the design of the statusbar seen on the main Screen. Toggle Icons, their background, the top line and modify the battery icon to your liking.
+ +
Protocols:Here you can cycle between USB & Bluetooth mode for our Bad-Keyboard app, and toggle Subghz settings.
+ +
Dolphin:Two simple yet sought after features: Simply change the level of your Flipper and disable / change the "Butthurt timer", aka. the time it takes for the Flipper to get sad when its not used.
+ +
Misc:All the other options that dont fit elsewhere. Toggles for our custom dark mode & left-handed mode (yes, we thought about you :3 ), an option to change the Flippers name and a switch for file sorting.

@@ -56,7 +51,7 @@ We wrote a powerful yet easy-to-use application specifically for our Firmware, t We created our own, new & improved Animation / Asset system, that we can finally reveal. It lets you to create and cycle through your own `Asset Packs` with only a few button presses, allowing you to easily load custom Animations and Icons like never before. -You can easily create your own pack, or find some user made ones in the discord channel. Check here for a tutorial on creating your own. Essentially, we got our own Anims & Icons folders, inside each Asset Pack. +You can easily create your own pack, or find some user made ones in the discord channel. Check here for a tutorial on creating your own. Essentially, we got our own Anims & Icons folders, inside each Asset Pack.
@@ -71,10 +66,20 @@ Once you have some packs, upload them to your Flipper in SD/dolphin_custom
-After installing the packs to Flipper, hit the Arrow UP button on the main menu and go to Xtreme Settings. Here choose which pack you want and tweak the other settings how you prefer, then press back to reboot and enjoy your new assets & animations! +After installing the packs to Flipper, hit the Arrow UP button on the main menu and go to Xtreme Settings. Here choose which pack you want and tweak the other settings how you prefer, then press back to reboot and enjoy your new assets for all apps (e.g. Subghz scanning asset) & animations!
+----- +
+

Bad Keyboard:

+ + + +BadUSB is a wonderful app, but it lacks bluetooth capabilities. Now some might argue that its useless as you will always need authentication from both sides, but what if I told you that we found a solution to this problem? +

+Bad-KB allows you to toggle between USB and Bluetooth mode for your attacks. In Bluetooth mode it allows you to spoof the name & MAC of the device to whatever you want. Being a JBL speaker or a wireless razer keyboard is easily doable, allowing you to trick people so you can run your payloads without needing a cable at hand. + -----

Levels:

@@ -86,7 +91,7 @@ With this new system in place, it allows for some cool stuff like locking animat
Our example -In our case, this is used with the NSFW animations. Dont worry, these are disabled by default because I know not everyone likes to see my / anime tits and thats fine. Anyways.. each level gives a brand new background animation, they also become more and more lewd over time. (Funfact for those reading.. thats why the repository has this warning. Github doesnt like my tits :c) +In our example case, this is used with the NSFW animation pack you can select in the Xtreme app. Dont worry, this is not used by default because I know not everyone likes to see my / anime tits and thats fine. Anyways.. each level gives a brand new background animation, they also become more and more lewd over time. | Level | Animations | | ------------- | ------------- | @@ -108,6 +113,7 @@ Note: This repo is always updated with OFW & Unleashed. No need to mention all t - Xtreme App - Asset Packs - More UI options +- Bad-Keyboard App - A new battery display-type - Scrolling view for long file names in browser - NSFW Animations tied to the level system. Read more above @@ -117,7 +123,6 @@ Note: This repo is always updated with OFW & Unleashed. No need to mention all t - Multiple NFC protocols - Multiple Sub-Ghz protocols | Merged from Unleashed, thanks @xMasterX - Subghz and IR signal replication via gpio | Credits to @ankris812 -- Honda Keys (CVE-2022-27254) & Ford blockers - New API Routes for Locale settings ``` @@ -129,12 +134,13 @@ Note: This repo is always updated with OFW & Unleashed. No need to mention all t - Tons of apps - File browser - Massive compiler re-do -- About 1k files to speed things up a lot +- About 4k files to speed things up a lot - Applications to now use the new Locale setting ``` ```txt [Fixed] +- Keyboard issues on first char - Passport crash on high level - SFW / Dummy_mode getting you XP - Leveling system @@ -152,17 +158,21 @@ Note: This repo is always updated with OFW & Unleashed. No need to mention all t

Install:

-**NOTE: If you are coming from a different FW, it is recommended to delete / clear your "apps" folder on the SD card prior to updating. This folder houses all the .fap files, which do not update to the correct API versions by default if old ones are present (Thanks flipper devs). This does `NOT` remove any of your saved files!** +**This is the recommended install procedure. Please follow these steps EXACTLY and CAREFULLY to ensure you install correctly.** +**This process will NOT delete any saved files and simply ensures the install goes smoothly.**

-- Download the latest release (.zip) from [The releases tab](https://github.com/ClaraCrazy/Flipper-Xtreme/releases/latest) -- Extract the archive. This is now your new Firmware folder -- Open [qFlipper](https://flipperzero.one/update), head to `SD/Update` and simply move the firmware folder there -- On the Flipper, hit the `Arrow Down` button, this will get you to the file menu. Hit `Arrow Left` once, and then simply search for your updates folder -- Inside that folder, select the Firmware you just moved onto it, and run the file thats simply called `Update` +- Download the latest release (`.tgz`) from [the release page](https://github.com/ClaraCrazy/Flipper-Xtreme/releases/latest) +- Open [qFlipper](https://flipperzero.one/update) and connect your Flipper Zero +- Go to the files tab, into SD Card, and DELETE THE `apps` AND `update` FOLDERS +- Go back to the device tab +- Click `Install from file...` and select the downloaded file (`.tgz`) - Enjoy! +**If you have issues or crashes with that process, you can try to use `Settings > Storage > Factory Reset` then retry the install.** +**Doing that will NOT remove your saved files, it will only forget some settings and paired devices.** + ----

Build it yourself:

diff --git a/SConstruct b/SConstruct index 62e37dfdc..81ff67790 100644 --- a/SConstruct +++ b/SConstruct @@ -139,7 +139,7 @@ if GetOption("fullenv") or any( basic_dist = distenv.DistCommand("fw_dist", distenv["DIST_DEPENDS"]) distenv.Default(basic_dist) -dist_dir = distenv.GetProjetDirName() +dist_dir = distenv.GetProjectDirName() fap_dist = [ distenv.Install( distenv.Dir(f"#/dist/{dist_dir}/apps/debug_elf"), diff --git a/applications/ReadMe.md b/applications/ReadMe.md index efc9afd86..ddbe5d65b 100644 --- a/applications/ReadMe.md +++ b/applications/ReadMe.md @@ -86,4 +86,4 @@ Small applications providing configuration for basic firmware and its services. Utility apps not visible in other menus. - `storage_move_to_sd` - Data migration tool for internal storage -- `updater` - updater service & application +- `updater` - Update service & application diff --git a/applications/debug/accessor/accessor_app.cpp b/applications/debug/accessor/accessor_app.cpp index 9d3708ebe..337437d0e 100644 --- a/applications/debug/accessor/accessor_app.cpp +++ b/applications/debug/accessor/accessor_app.cpp @@ -34,7 +34,7 @@ void AccessorApp::run(void) { AccessorApp::AccessorApp() : text_store{0} { notification = static_cast(furi_record_open(RECORD_NOTIFICATION)); - onewire_host = onewire_host_alloc(); + onewire_host = onewire_host_alloc(&ibutton_gpio); furi_hal_power_enable_otg(); } diff --git a/applications/debug/keypad_test/keypad_test.c b/applications/debug/keypad_test/keypad_test.c index 2470baf8d..9e8881def 100644 --- a/applications/debug/keypad_test/keypad_test.c +++ b/applications/debug/keypad_test/keypad_test.c @@ -11,6 +11,7 @@ typedef struct { uint16_t left; uint16_t right; uint16_t ok; + FuriMutex* mutex; } KeypadTestState; static void keypad_test_reset_state(KeypadTestState* state) { @@ -22,7 +23,8 @@ static void keypad_test_reset_state(KeypadTestState* state) { } static void keypad_test_render_callback(Canvas* canvas, void* ctx) { - KeypadTestState* state = (KeypadTestState*)acquire_mutex((ValueMutex*)ctx, 25); + KeypadTestState* state = ctx; + furi_mutex_acquire(state->mutex, FuriWaitForever); canvas_clear(canvas); char strings[5][20]; @@ -51,7 +53,7 @@ static void keypad_test_render_callback(Canvas* canvas, void* ctx) { canvas_draw_str(canvas, 10, 63, "[back] - reset, hold to exit"); - release_mutex((ValueMutex*)ctx, state); + furi_mutex_release(state->mutex); } static void keypad_test_input_callback(InputEvent* input_event, void* ctx) { @@ -64,17 +66,17 @@ int32_t keypad_test_app(void* p) { FuriMessageQueue* event_queue = furi_message_queue_alloc(32, sizeof(InputEvent)); furi_check(event_queue); - KeypadTestState _state = {{false, false, false, false, false}, 0, 0, 0, 0, 0}; + KeypadTestState state = {{false, false, false, false, false}, 0, 0, 0, 0, 0, NULL}; + state.mutex = furi_mutex_alloc(FuriMutexTypeNormal); - ValueMutex state_mutex; - if(!init_mutex(&state_mutex, &_state, sizeof(KeypadTestState))) { + if(!state.mutex) { FURI_LOG_E(TAG, "cannot create mutex"); return 0; } ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, keypad_test_render_callback, &state_mutex); + view_port_draw_callback_set(view_port, keypad_test_render_callback, &state); view_port_input_callback_set(view_port, keypad_test_input_callback, event_queue); // Open GUI and register view_port @@ -83,7 +85,7 @@ int32_t keypad_test_app(void* p) { InputEvent event; while(furi_message_queue_get(event_queue, &event, FuriWaitForever) == FuriStatusOk) { - KeypadTestState* state = (KeypadTestState*)acquire_mutex_block(&state_mutex); + furi_mutex_acquire(state.mutex, FuriWaitForever); FURI_LOG_I( TAG, "key: %s type: %s", @@ -92,54 +94,54 @@ int32_t keypad_test_app(void* p) { if(event.key == InputKeyRight) { if(event.type == InputTypePress) { - state->press[0] = true; + state.press[0] = true; } else if(event.type == InputTypeRelease) { - state->press[0] = false; + state.press[0] = false; } else if(event.type == InputTypeShort) { - ++state->right; + ++state.right; } } else if(event.key == InputKeyLeft) { if(event.type == InputTypePress) { - state->press[1] = true; + state.press[1] = true; } else if(event.type == InputTypeRelease) { - state->press[1] = false; + state.press[1] = false; } else if(event.type == InputTypeShort) { - ++state->left; + ++state.left; } } else if(event.key == InputKeyUp) { if(event.type == InputTypePress) { - state->press[2] = true; + state.press[2] = true; } else if(event.type == InputTypeRelease) { - state->press[2] = false; + state.press[2] = false; } else if(event.type == InputTypeShort) { - ++state->up; + ++state.up; } } else if(event.key == InputKeyDown) { if(event.type == InputTypePress) { - state->press[3] = true; + state.press[3] = true; } else if(event.type == InputTypeRelease) { - state->press[3] = false; + state.press[3] = false; } else if(event.type == InputTypeShort) { - ++state->down; + ++state.down; } } else if(event.key == InputKeyOk) { if(event.type == InputTypePress) { - state->press[4] = true; + state.press[4] = true; } else if(event.type == InputTypeRelease) { - state->press[4] = false; + state.press[4] = false; } else if(event.type == InputTypeShort) { - ++state->ok; + ++state.ok; } } else if(event.key == InputKeyBack) { if(event.type == InputTypeLong) { - release_mutex(&state_mutex, state); + furi_mutex_release(state.mutex); break; } else if(event.type == InputTypeShort) { - keypad_test_reset_state(state); + keypad_test_reset_state(&state); } } - release_mutex(&state_mutex, state); + furi_mutex_release(state.mutex); view_port_update(view_port); } @@ -147,7 +149,7 @@ int32_t keypad_test_app(void* p) { gui_remove_view_port(gui, view_port); view_port_free(view_port); furi_message_queue_free(event_queue); - delete_mutex(&state_mutex); + furi_mutex_free(state.mutex); furi_record_close(RECORD_GUI); diff --git a/applications/debug/locale_test/locale_test.c b/applications/debug/locale_test/locale_test.c index 46248cf3c..003df55dc 100644 --- a/applications/debug/locale_test/locale_test.c +++ b/applications/debug/locale_test/locale_test.c @@ -99,4 +99,4 @@ int32_t locale_test_app(void* p) { view_dispatcher_run(app->view_dispatcher); locale_test_free(app); return 0; -} \ No newline at end of file +} diff --git a/applications/debug/text_box_test/text_box_test.c b/applications/debug/text_box_test/text_box_test.c index d7194ffee..b980f686e 100644 --- a/applications/debug/text_box_test/text_box_test.c +++ b/applications/debug/text_box_test/text_box_test.c @@ -53,15 +53,17 @@ static void (*text_box_test_render[])(Canvas* canvas) = { typedef struct { uint32_t idx; + FuriMutex* mutex; } TextBoxTestState; static void text_box_test_render_callback(Canvas* canvas, void* ctx) { - TextBoxTestState* state = acquire_mutex((ValueMutex*)ctx, 25); + TextBoxTestState* state = ctx; + furi_mutex_acquire(state->mutex, FuriWaitForever); canvas_clear(canvas); text_box_test_render[state->idx](canvas); - release_mutex((ValueMutex*)ctx, state); + furi_mutex_release(state->mutex); } static void text_box_test_input_callback(InputEvent* input_event, void* ctx) { @@ -74,17 +76,17 @@ int32_t text_box_test_app(void* p) { FuriMessageQueue* event_queue = furi_message_queue_alloc(32, sizeof(InputEvent)); furi_check(event_queue); - TextBoxTestState _state = {.idx = 0}; + TextBoxTestState state = {.idx = 0, .mutex = NULL}; + state.mutex = furi_mutex_alloc(FuriMutexTypeNormal); - ValueMutex state_mutex; - if(!init_mutex(&state_mutex, &_state, sizeof(TextBoxTestState))) { + if(!state.mutex) { FURI_LOG_E(TAG, "Cannot create mutex"); return 0; } ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, text_box_test_render_callback, &state_mutex); + view_port_draw_callback_set(view_port, text_box_test_render_callback, &state); view_port_input_callback_set(view_port, text_box_test_input_callback, event_queue); // Open GUI and register view_port @@ -94,24 +96,24 @@ int32_t text_box_test_app(void* p) { uint32_t test_renders_num = COUNT_OF(text_box_test_render); InputEvent event; while(furi_message_queue_get(event_queue, &event, FuriWaitForever) == FuriStatusOk) { - TextBoxTestState* state = acquire_mutex_block(&state_mutex); + furi_mutex_acquire(state.mutex, FuriWaitForever); if(event.type == InputTypeShort) { if(event.key == InputKeyRight) { - if(state->idx < test_renders_num - 1) { - state->idx++; + if(state.idx < test_renders_num - 1) { + state.idx++; } } else if(event.key == InputKeyLeft) { - if(state->idx > 0) { - state->idx--; + if(state.idx > 0) { + state.idx--; } } else if(event.key == InputKeyBack) { - release_mutex(&state_mutex, state); + furi_mutex_release(state.mutex); break; } } - release_mutex(&state_mutex, state); + furi_mutex_release(state.mutex); view_port_update(view_port); } @@ -119,7 +121,7 @@ int32_t text_box_test_app(void* p) { gui_remove_view_port(gui, view_port); view_port_free(view_port); furi_message_queue_free(event_queue); - delete_mutex(&state_mutex); + furi_mutex_free(state.mutex); furi_record_close(RECORD_GUI); diff --git a/applications/debug/uart_echo/application.fam b/applications/debug/uart_echo/application.fam index a16c611bd..b9766254c 100644 --- a/applications/debug/uart_echo/application.fam +++ b/applications/debug/uart_echo/application.fam @@ -1,6 +1,6 @@ App( appid="UART_Echo", - name="UART Echo", + name="[GPIO] UART Echo", apptype=FlipperAppType.PLUGIN, entry_point="uart_echo_app", cdefines=["APP_UART_ECHO"], @@ -8,5 +8,5 @@ App( stack_size=2 * 1024, order=70, fap_icon="uart_10px.png", - fap_category="Misc", + fap_category="GPIO", ) diff --git a/applications/debug/unit_tests/furi/furi_test.c b/applications/debug/unit_tests/furi/furi_test.c index eed9e4205..33ec5fd01 100644 --- a/applications/debug/unit_tests/furi/furi_test.c +++ b/applications/debug/unit_tests/furi/furi_test.c @@ -5,7 +5,6 @@ // v2 tests void test_furi_create_open(); -void test_furi_valuemutex(); void test_furi_concurrent_access(); void test_furi_pubsub(); @@ -30,10 +29,6 @@ MU_TEST(mu_test_furi_create_open) { test_furi_create_open(); } -MU_TEST(mu_test_furi_valuemutex) { - test_furi_valuemutex(); -} - MU_TEST(mu_test_furi_pubsub) { test_furi_pubsub(); } @@ -51,7 +46,6 @@ MU_TEST_SUITE(test_suite) { // v2 tests MU_RUN_TEST(mu_test_furi_create_open); - MU_RUN_TEST(mu_test_furi_valuemutex); MU_RUN_TEST(mu_test_furi_pubsub); MU_RUN_TEST(mu_test_furi_memmgr); } diff --git a/applications/debug/unit_tests/furi/furi_valuemutex_test.c b/applications/debug/unit_tests/furi/furi_valuemutex_test.c deleted file mode 100644 index 02fd47eeb..000000000 --- a/applications/debug/unit_tests/furi/furi_valuemutex_test.c +++ /dev/null @@ -1,41 +0,0 @@ -#include -#include -#include - -#include "../minunit.h" - -void test_furi_valuemutex() { - const int init_value = 0xdeadbeef; - const int changed_value = 0x12345678; - - int value = init_value; - bool result; - ValueMutex valuemutex; - - // init mutex case - result = init_mutex(&valuemutex, &value, sizeof(value)); - mu_assert(result, "init mutex failed"); - - // acquire mutex case - int* value_pointer = acquire_mutex(&valuemutex, 100); - mu_assert_pointers_eq(value_pointer, &value); - - // second acquire mutex case - int* value_pointer_second = acquire_mutex(&valuemutex, 100); - mu_assert_pointers_eq(value_pointer_second, NULL); - - // change value case - *value_pointer = changed_value; - mu_assert_int_eq(value, changed_value); - - // release mutex case - result = release_mutex(&valuemutex, &value); - mu_assert(result, "release mutex failed"); - - // TODO - //acquire mutex blocking case - //write mutex blocking case - //read mutex blocking case - - mu_check(delete_mutex(&valuemutex)); -} diff --git a/applications/debug/unit_tests/power/power_test.c b/applications/debug/unit_tests/power/power_test.c index ce2c7aad7..a9b66b221 100644 --- a/applications/debug/unit_tests/power/power_test.c +++ b/applications/debug/unit_tests/power/power_test.c @@ -3,56 +3,63 @@ #include "../minunit.h" static void power_test_deinit(void) { - // Try to reset to default charging voltage - furi_hal_power_set_battery_charging_voltage(4.208f); + // Try to reset to default charge voltage limit + furi_hal_power_set_battery_charge_voltage_limit(4.208f); } -MU_TEST(test_power_charge_voltage_exact) { - // Power of 16mV charge voltages get applied exactly +MU_TEST(test_power_charge_voltage_limit_exact) { + // Power of 16mV charge voltage limits get applied exactly // (bq25896 charge controller works in 16mV increments) // // This test may need adapted if other charge controllers are used in the future. for(uint16_t charge_mv = 3840; charge_mv <= 4208; charge_mv += 16) { float charge_volt = (float)charge_mv / 1000.0f; - furi_hal_power_set_battery_charging_voltage(charge_volt); - mu_assert_double_eq(charge_volt, furi_hal_power_get_battery_charging_voltage()); + furi_hal_power_set_battery_charge_voltage_limit(charge_volt); + mu_assert_double_eq(charge_volt, furi_hal_power_get_battery_charge_voltage_limit()); } } -MU_TEST(test_power_charge_voltage_floating_imprecision) { +MU_TEST(test_power_charge_voltage_limit_floating_imprecision) { // 4.016f should act as 4.016 V, even with floating point imprecision - furi_hal_power_set_battery_charging_voltage(4.016f); - mu_assert_double_eq(4.016f, furi_hal_power_get_battery_charging_voltage()); + furi_hal_power_set_battery_charge_voltage_limit(4.016f); + mu_assert_double_eq(4.016f, furi_hal_power_get_battery_charge_voltage_limit()); } -MU_TEST(test_power_charge_voltage_inexact) { - // Charge voltages that are not power of 16mV get truncated down - furi_hal_power_set_battery_charging_voltage(3.841f); - mu_assert_double_eq(3.840, furi_hal_power_get_battery_charging_voltage()); +MU_TEST(test_power_charge_voltage_limit_inexact) { + // Charge voltage limits that are not power of 16mV get truncated down + furi_hal_power_set_battery_charge_voltage_limit(3.841f); + mu_assert_double_eq(3.840, furi_hal_power_get_battery_charge_voltage_limit()); - furi_hal_power_set_battery_charging_voltage(3.900f); - mu_assert_double_eq(3.888, furi_hal_power_get_battery_charging_voltage()); + furi_hal_power_set_battery_charge_voltage_limit(3.900f); + mu_assert_double_eq(3.888, furi_hal_power_get_battery_charge_voltage_limit()); - furi_hal_power_set_battery_charging_voltage(4.200f); - mu_assert_double_eq(4.192, furi_hal_power_get_battery_charging_voltage()); + furi_hal_power_set_battery_charge_voltage_limit(4.200f); + mu_assert_double_eq(4.192, furi_hal_power_get_battery_charge_voltage_limit()); } -MU_TEST(test_power_charge_voltage_invalid_clamped) { - // Out-of-range charge voltages get clamped to 3.840 V and 4.208 V - furi_hal_power_set_battery_charging_voltage(3.808f); - mu_assert_double_eq(3.840, furi_hal_power_get_battery_charging_voltage()); +MU_TEST(test_power_charge_voltage_limit_invalid_clamped) { + // Out-of-range charge voltage limits get clamped to 3.840 V and 4.208 V + furi_hal_power_set_battery_charge_voltage_limit(3.808f); + mu_assert_double_eq(3.840, furi_hal_power_get_battery_charge_voltage_limit()); + furi_hal_power_set_battery_charge_voltage_limit(1.0f); + mu_assert_double_eq(3.840, furi_hal_power_get_battery_charge_voltage_limit()); // NOTE: Intentionally picking a small increment above 4.208 V to reduce the risk of an // unhappy battery if this fails. - furi_hal_power_set_battery_charging_voltage(4.240f); - mu_assert_double_eq(4.208, furi_hal_power_get_battery_charging_voltage()); + furi_hal_power_set_battery_charge_voltage_limit(4.240f); + mu_assert_double_eq(4.208, furi_hal_power_get_battery_charge_voltage_limit()); + // Likewise, picking a number that the uint8_t wraparound in the driver would result in a + // VREG value under 23 if this test fails. + // E.g. (uint8_t)((8105-3840)/16) -> 10 + furi_hal_power_set_battery_charge_voltage_limit(8.105f); + mu_assert_double_eq(4.208, furi_hal_power_get_battery_charge_voltage_limit()); } MU_TEST_SUITE(test_power_suite) { - MU_RUN_TEST(test_power_charge_voltage_exact); - MU_RUN_TEST(test_power_charge_voltage_floating_imprecision); - MU_RUN_TEST(test_power_charge_voltage_inexact); - MU_RUN_TEST(test_power_charge_voltage_invalid_clamped); + MU_RUN_TEST(test_power_charge_voltage_limit_exact); + MU_RUN_TEST(test_power_charge_voltage_limit_floating_imprecision); + MU_RUN_TEST(test_power_charge_voltage_limit_inexact); + MU_RUN_TEST(test_power_charge_voltage_limit_invalid_clamped); power_test_deinit(); } diff --git a/applications/debug/unit_tests/rpc/rpc_test.c b/applications/debug/unit_tests/rpc/rpc_test.c index 5b52df2fa..329f3b741 100644 --- a/applications/debug/unit_tests/rpc/rpc_test.c +++ b/applications/debug/unit_tests/rpc/rpc_test.c @@ -89,7 +89,7 @@ static void test_rpc_setup(void) { } furi_check(rpc_session[0].session); - rpc_session[0].output_stream = furi_stream_buffer_alloc(1000, 1); + rpc_session[0].output_stream = furi_stream_buffer_alloc(4096, 1); rpc_session_set_send_bytes_callback(rpc_session[0].session, output_bytes_callback); rpc_session[0].close_session_semaphore = xSemaphoreCreateBinary(); rpc_session[0].terminate_semaphore = xSemaphoreCreateBinary(); @@ -191,7 +191,7 @@ static void clean_directory(Storage* fs_api, const char* clean_dir) { size_t size = strlen(clean_dir) + strlen(name) + 1 + 1; char* fullname = malloc(size); snprintf(fullname, size, "%s/%s", clean_dir, name); - if(fileinfo.flags & FSF_DIRECTORY) { + if(file_info_is_dir(&fileinfo)) { clean_directory(fs_api, fullname); } FS_Error error = storage_common_remove(fs_api, fullname); @@ -608,9 +608,8 @@ static void test_rpc_storage_list_create_expected_list( } if(path_contains_only_ascii(name)) { - list->file[i].type = (fileinfo.flags & FSF_DIRECTORY) ? - PB_Storage_File_FileType_DIR : - PB_Storage_File_FileType_FILE; + list->file[i].type = file_info_is_dir(&fileinfo) ? PB_Storage_File_FileType_DIR : + PB_Storage_File_FileType_FILE; list->file[i].size = fileinfo.size; list->file[i].data = NULL; /* memory free inside rpc_encode_and_send() -> pb_release() */ @@ -873,7 +872,7 @@ static void test_rpc_storage_stat_run(const char* path, uint32_t command_id) { if(error == FSE_OK) { response->which_content = PB_Main_storage_stat_response_tag; response->content.storage_stat_response.has_file = true; - response->content.storage_stat_response.file.type = (fileinfo.flags & FSF_DIRECTORY) ? + response->content.storage_stat_response.file.type = file_info_is_dir(&fileinfo) ? PB_Storage_File_FileType_DIR : PB_Storage_File_FileType_FILE; response->content.storage_stat_response.file.size = fileinfo.size; diff --git a/applications/debug/unit_tests/storage/dirwalk_test.c b/applications/debug/unit_tests/storage/dirwalk_test.c index 97aaa3580..e0842a7a4 100644 --- a/applications/debug/unit_tests/storage/dirwalk_test.c +++ b/applications/debug/unit_tests/storage/dirwalk_test.c @@ -179,7 +179,7 @@ MU_TEST_1(test_dirwalk_full, Storage* storage) { while(dir_walk_read(dir_walk, path, &fileinfo) == DirWalkOK) { furi_string_right(path, strlen(EXT_PATH("dirwalk/"))); - mu_check(storage_test_paths_mark(paths, path, (fileinfo.flags & FSF_DIRECTORY))); + mu_check(storage_test_paths_mark(paths, path, file_info_is_dir(&fileinfo))); } dir_walk_free(dir_walk); @@ -204,7 +204,7 @@ MU_TEST_1(test_dirwalk_no_recursive, Storage* storage) { while(dir_walk_read(dir_walk, path, &fileinfo) == DirWalkOK) { furi_string_right(path, strlen(EXT_PATH("dirwalk/"))); - mu_check(storage_test_paths_mark(paths, path, (fileinfo.flags & FSF_DIRECTORY))); + mu_check(storage_test_paths_mark(paths, path, file_info_is_dir(&fileinfo))); } dir_walk_free(dir_walk); @@ -219,7 +219,7 @@ static bool test_dirwalk_filter_no_folder_ext(const char* name, FileInfo* filein UNUSED(ctx); // only files - if(!(fileinfo->flags & FSF_DIRECTORY)) { + if(!file_info_is_dir(fileinfo)) { // with ".test" in name if(strstr(name, ".test") != NULL) { return true; @@ -243,7 +243,7 @@ MU_TEST_1(test_dirwalk_filter, Storage* storage) { while(dir_walk_read(dir_walk, path, &fileinfo) == DirWalkOK) { furi_string_right(path, strlen(EXT_PATH("dirwalk/"))); - mu_check(storage_test_paths_mark(paths, path, (fileinfo.flags & FSF_DIRECTORY))); + mu_check(storage_test_paths_mark(paths, path, file_info_is_dir(&fileinfo))); } dir_walk_free(dir_walk); diff --git a/applications/debug/unit_tests/storage/storage_test.c b/applications/debug/unit_tests/storage/storage_test.c index 115009701..582be7902 100644 --- a/applications/debug/unit_tests/storage/storage_test.c +++ b/applications/debug/unit_tests/storage/storage_test.c @@ -2,9 +2,40 @@ #include #include +// DO NOT USE THIS IN PRODUCTION CODE +// This is a hack to access internal storage functions and definitions +#include + +#define UNIT_TESTS_PATH(path) EXT_PATH("unit_tests/" path) + #define STORAGE_LOCKED_FILE EXT_PATH("locked_file.test") #define STORAGE_LOCKED_DIR STORAGE_INT_PATH_PREFIX +#define STORAGE_TEST_DIR UNIT_TESTS_PATH("test_dir") + +static bool storage_file_create(Storage* storage, const char* path, const char* data) { + File* file = storage_file_alloc(storage); + bool result = false; + do { + if(!storage_file_open(file, path, FSAM_WRITE, FSOM_CREATE_NEW)) { + break; + } + + if(storage_file_write(file, data, strlen(data)) != strlen(data)) { + break; + } + + if(!storage_file_close(file)) { + break; + } + + result = true; + } while(0); + + storage_file_free(file); + return result; +} + static void storage_file_open_lock_setup() { Storage* storage = furi_record_open(RECORD_STORAGE); File* file = storage_file_alloc(storage); @@ -115,7 +146,7 @@ static int32_t storage_dir_locker(void* ctx) { File* file = storage_file_alloc(storage); furi_check(storage_dir_open(file, STORAGE_LOCKED_DIR)); furi_semaphore_release(semaphore); - furi_delay_ms(1000); + furi_delay_ms(100); furi_check(storage_dir_close(file)); furi_record_close(RECORD_STORAGE); @@ -152,9 +183,21 @@ MU_TEST(storage_dir_open_lock) { mu_assert(result, "cannot open locked dir"); } +MU_TEST(storage_dir_exists_test) { + Storage* storage = furi_record_open(RECORD_STORAGE); + + mu_check(!storage_dir_exists(storage, STORAGE_TEST_DIR)); + mu_assert_int_eq(FSE_OK, storage_common_mkdir(storage, STORAGE_TEST_DIR)); + mu_check(storage_dir_exists(storage, STORAGE_TEST_DIR)); + mu_assert_int_eq(FSE_OK, storage_common_remove(storage, STORAGE_TEST_DIR)); + + furi_record_close(RECORD_STORAGE); +} + MU_TEST_SUITE(storage_dir) { MU_RUN_TEST(storage_dir_open_close); MU_RUN_TEST(storage_dir_open_lock); + MU_RUN_TEST(storage_dir_exists_test); } static const char* const storage_copy_test_paths[] = { @@ -303,9 +346,256 @@ MU_TEST_SUITE(storage_rename) { furi_record_close(RECORD_STORAGE); } +#define APPSDATA_APP_PATH(path) APPS_DATA_PATH "/" path + +static const char* storage_test_apps[] = { + "-_twilight_-", + "-_rainbow_-", + "-_pinkie_-", + "-_apple_-", + "-_flutter_-", + "-_rare_-", +}; + +static size_t storage_test_apps_count = COUNT_OF(storage_test_apps); + +static int32_t storage_test_app(void* arg) { + UNUSED(arg); + Storage* storage = furi_record_open(RECORD_STORAGE); + storage_common_remove(storage, "/app/test"); + int32_t ret = storage_file_create(storage, "/app/test", "test"); + furi_record_close(RECORD_STORAGE); + return ret; +} + +MU_TEST(test_storage_data_path_apps) { + for(size_t i = 0; i < storage_test_apps_count; i++) { + FuriThread* thread = + furi_thread_alloc_ex(storage_test_apps[i], 1024, storage_test_app, NULL); + furi_thread_set_appid(thread, storage_test_apps[i]); + furi_thread_start(thread); + furi_thread_join(thread); + + mu_assert_int_eq(true, furi_thread_get_return_code(thread)); + + // Check if app data dir and file exists + Storage* storage = furi_record_open(RECORD_STORAGE); + FuriString* expected = furi_string_alloc(); + furi_string_printf(expected, APPSDATA_APP_PATH("%s"), storage_test_apps[i]); + + mu_check(storage_dir_exists(storage, furi_string_get_cstr(expected))); + furi_string_cat(expected, "/test"); + mu_check(storage_file_exists(storage, furi_string_get_cstr(expected))); + + furi_string_printf(expected, APPSDATA_APP_PATH("%s"), storage_test_apps[i]); + storage_simply_remove_recursive(storage, furi_string_get_cstr(expected)); + + furi_record_close(RECORD_STORAGE); + + furi_string_free(expected); + furi_thread_free(thread); + } +} + +MU_TEST(test_storage_data_path) { + Storage* storage = furi_record_open(RECORD_STORAGE); + + File* file = storage_file_alloc(storage); + mu_check(storage_dir_open(file, "/app")); + mu_check(storage_dir_close(file)); + storage_file_free(file); + + // check that appsdata folder exists + mu_check(storage_dir_exists(storage, APPS_DATA_PATH)); + + // check that cli folder exists + mu_check(storage_dir_exists(storage, APPSDATA_APP_PATH("cli"))); + + storage_simply_remove(storage, APPSDATA_APP_PATH("cli")); + + furi_record_close(RECORD_STORAGE); +} + +MU_TEST(test_storage_common_migrate) { + Storage* storage = furi_record_open(RECORD_STORAGE); + + // Setup test folders + storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_old")); + storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_new")); + + // Test migration from non existing + mu_assert_int_eq( + FSE_OK, + storage_common_migrate( + storage, UNIT_TESTS_PATH("migrate_old"), UNIT_TESTS_PATH("migrate_new"))); + + // Test migration from existing folder to non existing + mu_assert_int_eq(FSE_OK, storage_common_mkdir(storage, UNIT_TESTS_PATH("migrate_old"))); + mu_check(storage_file_create(storage, UNIT_TESTS_PATH("migrate_old/file1"), "test1")); + mu_check(storage_file_create(storage, UNIT_TESTS_PATH("migrate_old/file2.ext"), "test2")); + mu_check(storage_file_create(storage, UNIT_TESTS_PATH("migrate_old/file3.ext.ext"), "test3")); + mu_assert_int_eq( + FSE_OK, + storage_common_migrate( + storage, UNIT_TESTS_PATH("migrate_old"), UNIT_TESTS_PATH("migrate_new"))); + + mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new/file1"))); + mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new/file2.ext"))); + mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new/file3.ext.ext"))); + mu_check(storage_dir_exists(storage, UNIT_TESTS_PATH("migrate_new"))); + mu_check(!storage_dir_exists(storage, UNIT_TESTS_PATH("migrate_old"))); + + // Test migration from existing folder to existing folder + mu_assert_int_eq(FSE_OK, storage_common_mkdir(storage, UNIT_TESTS_PATH("migrate_old"))); + mu_check(storage_file_create(storage, UNIT_TESTS_PATH("migrate_old/file1"), "test1")); + mu_check(storage_file_create(storage, UNIT_TESTS_PATH("migrate_old/file2.ext"), "test2")); + mu_check(storage_file_create(storage, UNIT_TESTS_PATH("migrate_old/file3.ext.ext"), "test3")); + + mu_assert_int_eq( + FSE_OK, + storage_common_migrate( + storage, UNIT_TESTS_PATH("migrate_old"), UNIT_TESTS_PATH("migrate_new"))); + + mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new/file1"))); + mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new/file2.ext"))); + mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new/file3.ext.ext"))); + mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new/file11"))); + mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new/file21.ext"))); + mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new/file3.ext1.ext"))); + mu_check(storage_dir_exists(storage, UNIT_TESTS_PATH("migrate_new"))); + mu_check(!storage_dir_exists(storage, UNIT_TESTS_PATH("migrate_old"))); + + storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_old")); + storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_new")); + + // Test migration from empty folder to existing file + // Expected result: FSE_OK, folder removed, file untouched + mu_assert_int_eq(FSE_OK, storage_common_mkdir(storage, UNIT_TESTS_PATH("migrate_old"))); + mu_check(storage_file_create(storage, UNIT_TESTS_PATH("migrate_new"), "test1")); + + mu_assert_int_eq( + FSE_OK, + storage_common_migrate( + storage, UNIT_TESTS_PATH("migrate_old"), UNIT_TESTS_PATH("migrate_new"))); + + mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new"))); + mu_check(!storage_dir_exists(storage, UNIT_TESTS_PATH("migrate_old"))); + + storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_old")); + storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_new")); + + // Test migration from empty folder to existing folder + // Expected result: FSE_OK, old folder removed, new folder untouched + mu_assert_int_eq(FSE_OK, storage_common_mkdir(storage, UNIT_TESTS_PATH("migrate_old"))); + mu_assert_int_eq(FSE_OK, storage_common_mkdir(storage, UNIT_TESTS_PATH("migrate_new"))); + + mu_assert_int_eq( + FSE_OK, + storage_common_migrate( + storage, UNIT_TESTS_PATH("migrate_old"), UNIT_TESTS_PATH("migrate_new"))); + + mu_check(storage_dir_exists(storage, UNIT_TESTS_PATH("migrate_new"))); + mu_check(!storage_dir_exists(storage, UNIT_TESTS_PATH("migrate_old"))); + + storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_old")); + storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_new")); + + // Test migration from existing file to non existing, no extension + mu_check(storage_file_create(storage, UNIT_TESTS_PATH("migrate_old"), "test1")); + + mu_assert_int_eq( + FSE_OK, + storage_common_migrate( + storage, UNIT_TESTS_PATH("migrate_old"), UNIT_TESTS_PATH("migrate_new"))); + + mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new"))); + mu_check(!storage_file_exists(storage, UNIT_TESTS_PATH("migrate_old"))); + + storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_old")); + storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_new")); + + // Test migration from existing file to non existing, with extension + mu_check(storage_file_create(storage, UNIT_TESTS_PATH("migrate_old.file"), "test1")); + + mu_assert_int_eq( + FSE_OK, + storage_common_migrate( + storage, UNIT_TESTS_PATH("migrate_old.file"), UNIT_TESTS_PATH("migrate_new.file"))); + + mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new.file"))); + mu_check(!storage_file_exists(storage, UNIT_TESTS_PATH("migrate_old.file"))); + + storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_old.file")); + storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_new.file")); + + // Test migration from existing file to existing file, no extension + mu_check(storage_file_create(storage, UNIT_TESTS_PATH("migrate_old"), "test1")); + mu_check(storage_file_create(storage, UNIT_TESTS_PATH("migrate_new"), "test2")); + + mu_assert_int_eq( + FSE_OK, + storage_common_migrate( + storage, UNIT_TESTS_PATH("migrate_old"), UNIT_TESTS_PATH("migrate_new"))); + + mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new"))); + mu_check(!storage_file_exists(storage, UNIT_TESTS_PATH("migrate_old"))); + mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new1"))); + + storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_old")); + storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_new")); + storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_new1")); + + // Test migration from existing file to existing file, with extension + mu_check(storage_file_create(storage, UNIT_TESTS_PATH("migrate_old.file"), "test1")); + mu_check(storage_file_create(storage, UNIT_TESTS_PATH("migrate_new.file"), "test2")); + + mu_assert_int_eq( + FSE_OK, + storage_common_migrate( + storage, UNIT_TESTS_PATH("migrate_old.file"), UNIT_TESTS_PATH("migrate_new.file"))); + + mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new.file"))); + mu_check(!storage_file_exists(storage, UNIT_TESTS_PATH("migrate_old.file"))); + mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new1.file"))); + + storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_old.file")); + storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_new.file")); + storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_new1.file")); + + // Test migration from existing file to existing folder + mu_check(storage_file_create(storage, UNIT_TESTS_PATH("migrate_old"), "test1")); + mu_assert_int_eq(FSE_OK, storage_common_mkdir(storage, UNIT_TESTS_PATH("migrate_new"))); + + mu_assert_int_eq( + FSE_OK, + storage_common_migrate( + storage, UNIT_TESTS_PATH("migrate_old"), UNIT_TESTS_PATH("migrate_new"))); + + mu_check(storage_dir_exists(storage, UNIT_TESTS_PATH("migrate_new"))); + mu_check(!storage_file_exists(storage, UNIT_TESTS_PATH("migrate_old"))); + mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new1"))); + + storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_old")); + storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_new")); + storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_new1")); + + furi_record_close(RECORD_STORAGE); +} + +MU_TEST_SUITE(test_data_path) { + MU_RUN_TEST(test_storage_data_path); + MU_RUN_TEST(test_storage_data_path_apps); +} + +MU_TEST_SUITE(test_storage_common) { + MU_RUN_TEST(test_storage_common_migrate); +} + int run_minunit_test_storage() { MU_RUN_SUITE(storage_file); MU_RUN_SUITE(storage_dir); MU_RUN_SUITE(storage_rename); + MU_RUN_SUITE(test_data_path); + MU_RUN_SUITE(test_storage_common); return MU_EXIT_CODE; } diff --git a/applications/debug/unit_tests/subghz/subghz_test.c b/applications/debug/unit_tests/subghz/subghz_test.c index fcd3dbded..c7e9c96f1 100644 --- a/applications/debug/unit_tests/subghz/subghz_test.c +++ b/applications/debug/unit_tests/subghz/subghz_test.c @@ -12,8 +12,9 @@ #define KEYSTORE_DIR_NAME EXT_PATH("subghz/assets/keeloq_mfcodes") #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 ALUTECH_AT_4N_DIR_NAME EXT_PATH("subghz/assets/alutech_at_4n") #define TEST_RANDOM_DIR_NAME EXT_PATH("unit_tests/subghz/test_random_raw.sub") -#define TEST_RANDOM_COUNT_PARSE 273 +#define TEST_RANDOM_COUNT_PARSE 329 #define TEST_TIMEOUT 10000 static SubGhzEnvironment* environment_handler; @@ -43,6 +44,8 @@ static void subghz_test_init(void) { environment_handler, CAME_ATOMO_DIR_NAME); subghz_environment_set_nice_flor_s_rainbow_table_file_name( environment_handler, NICE_FLOR_S_DIR_NAME); + subghz_environment_set_alutech_at_4n_rainbow_table_file_name( + environment_handler, ALUTECH_AT_4N_DIR_NAME); subghz_environment_set_protocol_registry( environment_handler, (void*)&subghz_protocol_registry); @@ -489,6 +492,14 @@ MU_TEST(subghz_decoder_linear_test) { "Test decoder " SUBGHZ_PROTOCOL_LINEAR_NAME " error\r\n"); } +MU_TEST(subghz_decoder_linear_delta3_test) { + mu_assert( + subghz_decoder_test( + EXT_PATH("unit_tests/subghz/linear_delta3_raw.sub"), + SUBGHZ_PROTOCOL_LINEAR_DELTA3_NAME), + "Test decoder " SUBGHZ_PROTOCOL_LINEAR_DELTA3_NAME " error\r\n"); +} + MU_TEST(subghz_decoder_megacode_test) { mu_assert( subghz_decoder_test( @@ -590,12 +601,6 @@ MU_TEST(subghz_decoder_ansonic_test) { "Test decoder " SUBGHZ_PROTOCOL_ANSONIC_NAME " error\r\n"); } -MU_TEST(subghz_decoder_pocsag_test) { - mu_assert( - subghz_decoder_test(EXT_PATH("unit_tests/subghz/pocsag.sub"), SUBGHZ_PROTOCOL_POCSAG_NAME), - "Test decoder " SUBGHZ_PROTOCOL_POCSAG_NAME " error\r\n"); -} - MU_TEST(subghz_decoder_smc5326_test) { mu_assert( subghz_decoder_test( @@ -610,6 +615,36 @@ MU_TEST(subghz_decoder_holtek_ht12x_test) { "Test decoder " SUBGHZ_PROTOCOL_HOLTEK_HT12X_NAME " error\r\n"); } +MU_TEST(subghz_decoder_dooya_test) { + mu_assert( + subghz_decoder_test( + EXT_PATH("unit_tests/subghz/dooya_raw.sub"), SUBGHZ_PROTOCOL_DOOYA_NAME), + "Test decoder " SUBGHZ_PROTOCOL_DOOYA_NAME " error\r\n"); +} + +MU_TEST(subghz_decoder_alutech_at_4n_test) { + mu_assert( + subghz_decoder_test( + EXT_PATH("unit_tests/subghz/alutech_at_4n_raw.sub"), + SUBGHZ_PROTOCOL_ALUTECH_AT_4N_NAME), + "Test decoder " SUBGHZ_PROTOCOL_ALUTECH_AT_4N_NAME " error\r\n"); +} + +MU_TEST(subghz_decoder_nice_one_test) { + mu_assert( + subghz_decoder_test( + EXT_PATH("unit_tests/subghz/nice_one_raw.sub"), SUBGHZ_PROTOCOL_NICE_FLOR_S_NAME), + "Test decoder " SUBGHZ_PROTOCOL_NICE_FLOR_S_NAME " error\r\n"); +} + +MU_TEST(subghz_decoder_kinggates_stylo4k_test) { + mu_assert( + subghz_decoder_test( + EXT_PATH("unit_tests/subghz/kinggates_stylo4k_raw.sub"), + SUBGHZ_PROTOCOL_KINGGATES_STYLO_4K_NAME), + "Test decoder " SUBGHZ_PROTOCOL_KINGGATES_STYLO_4K_NAME " error\r\n"); +} + //test encoders MU_TEST(subghz_encoder_princeton_test) { mu_assert( @@ -653,6 +688,12 @@ MU_TEST(subghz_encoder_linear_test) { "Test encoder " SUBGHZ_PROTOCOL_LINEAR_NAME " error\r\n"); } +MU_TEST(subghz_encoder_linear_delta3_test) { + mu_assert( + subghz_encoder_test(EXT_PATH("unit_tests/subghz/linear_delta3.sub")), + "Test encoder " SUBGHZ_PROTOCOL_LINEAR_DELTA3_NAME " error\r\n"); +} + MU_TEST(subghz_encoder_megacode_test) { mu_assert( subghz_encoder_test(EXT_PATH("unit_tests/subghz/megacode.sub")), @@ -749,6 +790,12 @@ MU_TEST(subghz_encoder_holtek_ht12x_test) { "Test encoder " SUBGHZ_PROTOCOL_HOLTEK_HT12X_NAME " error\r\n"); } +MU_TEST(subghz_encoder_dooya_test) { + mu_assert( + subghz_encoder_test(EXT_PATH("unit_tests/subghz/dooya.sub")), + "Test encoder " SUBGHZ_PROTOCOL_DOOYA_NAME " error\r\n"); +} + MU_TEST(subghz_random_test) { mu_assert(subghz_decode_random_test(TEST_RANDOM_DIR_NAME), "Random test error\r\n"); } @@ -778,6 +825,7 @@ MU_TEST_SUITE(subghz) { MU_RUN_TEST(subghz_decoder_somfy_telis_test); MU_RUN_TEST(subghz_decoder_star_line_test); MU_RUN_TEST(subghz_decoder_linear_test); + MU_RUN_TEST(subghz_decoder_linear_delta3_test); MU_RUN_TEST(subghz_decoder_megacode_test); MU_RUN_TEST(subghz_decoder_secplus_v1_test); MU_RUN_TEST(subghz_decoder_secplus_v2_test); @@ -792,9 +840,12 @@ MU_TEST_SUITE(subghz) { MU_RUN_TEST(subghz_decoder_intertechno_v3_test); MU_RUN_TEST(subghz_decoder_clemsa_test); MU_RUN_TEST(subghz_decoder_ansonic_test); - MU_RUN_TEST(subghz_decoder_pocsag_test); MU_RUN_TEST(subghz_decoder_smc5326_test); MU_RUN_TEST(subghz_decoder_holtek_ht12x_test); + MU_RUN_TEST(subghz_decoder_dooya_test); + MU_RUN_TEST(subghz_decoder_alutech_at_4n_test); + MU_RUN_TEST(subghz_decoder_nice_one_test); + MU_RUN_TEST(subghz_decoder_kinggates_stylo4k_test); MU_RUN_TEST(subghz_encoder_princeton_test); MU_RUN_TEST(subghz_encoder_came_test); @@ -803,6 +854,7 @@ MU_TEST_SUITE(subghz) { MU_RUN_TEST(subghz_encoder_nice_flo_test); MU_RUN_TEST(subghz_encoder_keelog_test); MU_RUN_TEST(subghz_encoder_linear_test); + MU_RUN_TEST(subghz_encoder_linear_delta3_test); MU_RUN_TEST(subghz_encoder_megacode_test); MU_RUN_TEST(subghz_encoder_holtek_test); MU_RUN_TEST(subghz_encoder_secplus_v1_test); @@ -819,6 +871,7 @@ MU_TEST_SUITE(subghz) { 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_encoder_dooya_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 2bb9c423f..ac71ca397 100644 --- a/applications/debug/unit_tests/test_index.c +++ b/applications/debug/unit_tests/test_index.c @@ -70,7 +70,7 @@ void minunit_print_progress() { } void minunit_print_fail(const char* str) { - printf(FURI_LOG_CLR_E "%s\r\n" FURI_LOG_CLR_RESET, str); + printf(_FURI_LOG_CLR_E "%s\r\n" _FURI_LOG_CLR_RESET, str); } void unit_tests_cli(Cli* cli, FuriString* args, void* context) { diff --git a/applications/examples/example_apps_data/README.md b/applications/examples/example_apps_data/README.md new file mode 100644 index 000000000..fd8666077 --- /dev/null +++ b/applications/examples/example_apps_data/README.md @@ -0,0 +1,18 @@ +# Apps Data folder Example + +This example demonstrates how to utilize the Apps Data folder to store data that is not part of the app itself, such as user data, configuration files, and so forth. + +## What is the Apps Data Folder? + +The **Apps Data** folder is a folder used to store data for external apps that are not part of the main firmware. + +The path to the current application folder is related to the `appid` of the app. The `appid` is used to identify the app in the app store and is stored in the `application.fam` file. +The Apps Data folder is located only on the external storage, the SD card. + +For example, if the `appid` of the app is `snake_game`, the path to the Apps Data folder will be `/ext/apps_data/snake_game`. But using raw paths is not recommended, because the path to the Apps Data folder can change in the future. Use the `/app` alias instead. + +## How to get the path to the Apps Data folder? + +You can use `/app` alias to get the path to the current application data folder. For example, if you want to open a file `config.txt` in the Apps Data folder, you can use the next path: `/app/config.txt`. But this way is not recommended, because even the `/app` alias can change in the future. + +We recommend to use the `APP_DATA_PATH` macro to get the path to the Apps Data folder. For example, if you want to open a file `config.txt` in the Apps Data folder, you can use the next path: `APP_DATA_PATH("config.txt")`. \ No newline at end of file diff --git a/applications/examples/example_apps_data/application.fam b/applications/examples/example_apps_data/application.fam new file mode 100644 index 000000000..f44dca97d --- /dev/null +++ b/applications/examples/example_apps_data/application.fam @@ -0,0 +1,9 @@ +App( + appid="example_apps_data", + name="Example: Apps Data", + apptype=FlipperAppType.EXTERNAL, + entry_point="example_apps_data_main", + requires=["gui"], + stack_size=1 * 1024, + fap_category="Examples", +) diff --git a/applications/examples/example_apps_data/example_apps_data.c b/applications/examples/example_apps_data/example_apps_data.c new file mode 100644 index 000000000..d6104c137 --- /dev/null +++ b/applications/examples/example_apps_data/example_apps_data.c @@ -0,0 +1,40 @@ +#include +#include + +// Define log tag +#define TAG "example_apps_data" + +// Application entry point +int32_t example_apps_data_main(void* p) { + // Mark argument as unused + UNUSED(p); + + // Open storage + Storage* storage = furi_record_open(RECORD_STORAGE); + + // Allocate file + File* file = storage_file_alloc(storage); + + // Get the path to the current application data folder + // That is: /ext/apps_data/ + // And it will create folders in the path if they don't exist + // In this example it will create /ext/apps_data/example_apps_data + // And file will be /ext/apps_data/example_apps_data/test.txt + + // Open file, write data and close it + if(!storage_file_open(file, APP_DATA_PATH("test.txt"), FSAM_WRITE, FSOM_CREATE_ALWAYS)) { + FURI_LOG_E(TAG, "Failed to open file"); + } + if(!storage_file_write(file, "Hello World!", strlen("Hello World!"))) { + FURI_LOG_E(TAG, "Failed to write to file"); + } + storage_file_close(file); + + // Deallocate file + storage_file_free(file); + + // Close storage + furi_record_close(RECORD_STORAGE); + + return 0; +} diff --git a/applications/main/application.fam b/applications/main/application.fam index eefb801b3..459a878ed 100644 --- a/applications/main/application.fam +++ b/applications/main/application.fam @@ -3,36 +3,16 @@ App( name="Basic applications for main menu", apptype=FlipperAppType.METAPACKAGE, provides=[ + "fap_loader", + "subghz", + "subghz_remote", + "lfrfid", + "nfc", + "infrared", "gpio", "ibutton", - "infrared", - "lfrfid", - "nfc", - "subghz", "bad_kb", "u2f", - "fap_loader", - "sub_playlist", - "archive", - "clock", - "unirfremix", - ], -) - -App( - appid="main_apps_default", - name="Basic applications for main menu", - apptype=FlipperAppType.METAPACKAGE, - provides=[ - "gpio", - # "ibutton", - "infrared", - "lfrfid", - "nfc", - "subghz", - # "bad_kb", - # "u2f", - "fap_loader", "archive", ], ) diff --git a/applications/main/archive/helpers/archive_browser.c b/applications/main/archive/helpers/archive_browser.c index 78b010a78..f93d9694d 100644 --- a/applications/main/archive/helpers/archive_browser.c +++ b/applications/main/archive/helpers/archive_browser.c @@ -8,6 +8,7 @@ #include #include #include +#include static void archive_folder_open_cb(void* context, uint32_t item_cnt, int32_t file_idx, bool is_root) { @@ -450,7 +451,7 @@ static bool archive_is_dir_exists(FuriString* path) { FileInfo file_info; Storage* storage = furi_record_open(RECORD_STORAGE); if(storage_common_stat(storage, furi_string_get_cstr(path), &file_info) == FSE_OK) { - if(file_info.flags & FSF_DIRECTORY) { + if(file_info_is_dir(&file_info)) { state = true; } } @@ -464,14 +465,17 @@ void archive_switch_tab(ArchiveBrowserView* browser, InputKey key) { browser->last_tab_switch_dir = key; - for(int i = 0; i < 2; i++) { + if(key == InputKeyLeft) { + tab = ((tab - 1) + ArchiveTabTotal) % ArchiveTabTotal; + } else { + tab = (tab + 1) % ArchiveTabTotal; + } + if(tab == ArchiveTabInternal && !furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { if(key == InputKeyLeft) { tab = ((tab - 1) + ArchiveTabTotal) % ArchiveTabTotal; } else { tab = (tab + 1) % ArchiveTabTotal; } - if(tab == ArchiveTabInternal && !furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) continue; - break; } browser->is_root = true; @@ -528,12 +532,16 @@ void archive_enter_dir(ArchiveBrowserView* browser, FuriString* path) { browser->view, ArchiveBrowserViewModel * model, { idx_temp = model->item_idx; }, false); furi_string_set(browser->path, path); + file_browser_worker_folder_enter(browser->worker, path, idx_temp); } void archive_leave_dir(ArchiveBrowserView* browser) { furi_assert(browser); + size_t dirname_start = furi_string_search_rchar(browser->path, '/'); + furi_string_left(browser->path, dirname_start); + file_browser_worker_folder_exit(browser->worker); } diff --git a/applications/main/archive/helpers/archive_favorites.c b/applications/main/archive/helpers/archive_favorites.c index 8bbcb5213..f395ee5a1 100644 --- a/applications/main/archive/helpers/archive_favorites.c +++ b/applications/main/archive/helpers/archive_favorites.c @@ -160,7 +160,7 @@ bool archive_favorites_read(void* context) { if(storage_file_exists(storage, furi_string_get_cstr(buffer))) { storage_common_stat(storage, furi_string_get_cstr(buffer), &file_info); archive_add_file_item( - browser, (file_info.flags & FSF_DIRECTORY), furi_string_get_cstr(buffer)); + browser, file_info_is_dir(&file_info), furi_string_get_cstr(buffer)); file_count++; } else { need_refresh = true; diff --git a/applications/main/archive/helpers/archive_files.c b/applications/main/archive/helpers/archive_files.c index 83eb2a845..5f5d13fc3 100644 --- a/applications/main/archive/helpers/archive_files.c +++ b/applications/main/archive/helpers/archive_files.c @@ -96,7 +96,7 @@ void archive_delete_file(void* context, const char* format, ...) { bool res = false; - if(fileinfo.flags & FSF_DIRECTORY) { + if(file_info_is_dir(&fileinfo)) { res = storage_simply_remove_recursive(fs_api, furi_string_get_cstr(filename)); } else { res = (storage_common_remove(fs_api, furi_string_get_cstr(filename)) == FSE_OK); diff --git a/applications/main/archive/helpers/archive_files.h b/applications/main/archive/helpers/archive_files.h index 863a8e7da..431c701b3 100644 --- a/applications/main/archive/helpers/archive_files.h +++ b/applications/main/archive/helpers/archive_files.h @@ -84,7 +84,7 @@ static void ArchiveFile_t_clear(ArchiveFile_t* obj) { } static int ArchiveFile_t_cmp(const ArchiveFile_t* a, const ArchiveFile_t* b) { - if(!XTREME_SETTINGS()->sort_ignore_dirs) { + if(XTREME_SETTINGS()->sort_dirs_first) { if(a->type == ArchiveFileTypeFolder && b->type != ArchiveFileTypeFolder) { return -1; } diff --git a/applications/main/bad_kb/bad_kb_app.c b/applications/main/bad_kb/bad_kb_app.c index dfce5acbf..9f2a3145a 100644 --- a/applications/main/bad_kb/bad_kb_app.c +++ b/applications/main/bad_kb/bad_kb_app.c @@ -37,9 +37,26 @@ static void bad_kb_load_settings(BadKbApp* app) { !storage_file_eof(settings_file) && !isspace(chr)) { furi_string_push_back(app->keyboard_layout, chr); } + } else { + furi_string_reset(app->keyboard_layout); } storage_file_close(settings_file); storage_file_free(settings_file); + + if(!furi_string_empty(app->keyboard_layout)) { + Storage* fs_api = furi_record_open(RECORD_STORAGE); + FileInfo layout_file_info; + FS_Error file_check_err = storage_common_stat( + fs_api, furi_string_get_cstr(app->keyboard_layout), &layout_file_info); + furi_record_close(RECORD_STORAGE); + if(file_check_err != FSE_OK) { + furi_string_reset(app->keyboard_layout); + return; + } + if(layout_file_info.size != 256) { + furi_string_reset(app->keyboard_layout); + } + } } static void bad_kb_save_settings(BadKbApp* app) { @@ -55,17 +72,6 @@ static void bad_kb_save_settings(BadKbApp* app) { storage_file_free(settings_file); } -void bad_kb_set_name(BadKbApp* app, const char* fmt, ...) { - furi_assert(app); - - va_list args; - va_start(args, fmt); - - vsnprintf(app->name, BAD_KB_ADV_NAME_MAX_LEN, fmt, args); - - va_end(args); -} - BadKbApp* bad_kb_app_alloc(char* arg) { BadKbApp* app = malloc(sizeof(BadKbApp)); @@ -77,6 +83,18 @@ BadKbApp* bad_kb_app_alloc(char* arg) { furi_string_set(app->file_path, arg); } + Storage* storage = furi_record_open(RECORD_STORAGE); + // Remove old pre-included files to avoid duplicates on migrate + storage_simply_remove(storage, EXT_PATH("badusb/layouts")); + storage_simply_remove(storage, EXT_PATH("badusb/.badusb.settings")); + storage_simply_remove(storage, EXT_PATH("badusb/Kiosk-Evasion-Bruteforce.txt")); + storage_simply_remove(storage, EXT_PATH("badusb/Wifi-Stealer_ORG.txt")); + storage_simply_remove(storage, EXT_PATH("badusb/demo_macos.txt")); + storage_simply_remove(storage, EXT_PATH("badusb/demo_windows.txt")); + storage_common_migrate(storage, EXT_PATH("badusb"), BAD_KB_APP_BASE_FOLDER); + storage_simply_mkdir(storage, BAD_KB_APP_BASE_FOLDER); + furi_record_close(RECORD_STORAGE); + bad_kb_load_settings(app); app->gui = furi_record_open(RECORD_GUI); @@ -96,14 +114,19 @@ BadKbApp* bad_kb_app_alloc(char* arg) { view_dispatcher_set_navigation_event_callback( app->view_dispatcher, bad_kb_app_back_event_callback); + app->connection_init = false; + Bt* bt = furi_record_open(RECORD_BT); app->bt = bt; + app->bt->suppress_pin_screen = true; app->is_bt = XTREME_SETTINGS()->bad_bt; - const char* adv_name = bt_get_profile_adv_name(bt); + app->bt_remember = XTREME_SETTINGS()->bad_bt_remember; + const char* adv_name = furi_hal_bt_get_profile_adv_name(FuriHalBtProfileHidKeyboard); memcpy(app->name, adv_name, BAD_KB_ADV_NAME_MAX_LEN); memcpy(app->bt_old_config.name, adv_name, BAD_KB_ADV_NAME_MAX_LEN); - const uint8_t* mac_addr = bt_get_profile_mac_address(bt); + // need to be done before bt init (where mac address get modified if bounding is activated) + const uint8_t* mac_addr = furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard); memcpy(app->mac, mac_addr, BAD_KB_MAC_ADDRESS_LEN); memcpy(app->bt_old_config.mac, mac_addr, BAD_KB_MAC_ADDRESS_LEN); @@ -139,8 +162,12 @@ BadKbApp* bad_kb_app_alloc(char* arg) { if(furi_hal_usb_is_locked()) { app->error = BadKbAppErrorCloseRpc; + app->conn_init_thread = NULL; scene_manager_next_scene(app->scene_manager, BadKbSceneError); } else { + app->conn_init_thread = furi_thread_alloc_ex( + "BadKbConnInit", 512, (FuriThreadCallback)bad_kb_connection_init, app); + furi_thread_start(app->conn_init_thread); if(!furi_string_empty(app->file_path)) { app->bad_kb_script = bad_kb_script_open(app->file_path, app->is_bt ? app->bt : NULL); bad_kb_script_set_keyboard_layout(app->bad_kb_script, app->keyboard_layout); @@ -188,16 +215,16 @@ void bad_kb_app_free(BadKbApp* app) { view_dispatcher_free(app->view_dispatcher); scene_manager_free(app->scene_manager); - // restores bt config - // BtProfile have already been switched to the previous one - // so we directly modify the right profile - bad_kb_connection_deinit(app->bt); + // Restore bt config + // BtProfile has already been switched to the previous one + // So we directly modify the right profile if(strcmp(app->bt_old_config.name, app->name) != 0) { furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, app->bt_old_config.name); } if(memcmp(app->bt_old_config.mac, app->mac, BAD_KB_MAC_ADDRESS_LEN) != 0) { furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileHidKeyboard, app->bt_old_config.mac); } + app->bt->suppress_pin_screen = false; // Close records furi_record_close(RECORD_GUI); @@ -210,6 +237,12 @@ void bad_kb_app_free(BadKbApp* app) { furi_string_free(app->file_path); furi_string_free(app->keyboard_layout); + if(app->conn_init_thread) { + furi_thread_join(app->conn_init_thread); + furi_thread_free(app->conn_init_thread); + } + bad_kb_connection_deinit(app); + free(app); } diff --git a/applications/main/bad_kb/bad_kb_app.h b/applications/main/bad_kb/bad_kb_app.h index e75a94651..af64be253 100644 --- a/applications/main/bad_kb/bad_kb_app.h +++ b/applications/main/bad_kb/bad_kb_app.h @@ -6,8 +6,6 @@ extern "C" { typedef struct BadKbApp BadKbApp; -void bad_kb_set_name(BadKbApp* app, const char* fmt, ...); - #ifdef __cplusplus } #endif diff --git a/applications/main/bad_kb/bad_kb_app_i.h b/applications/main/bad_kb/bad_kb_app_i.h index 913830e72..a55875676 100644 --- a/applications/main/bad_kb/bad_kb_app_i.h +++ b/applications/main/bad_kb/bad_kb_app_i.h @@ -16,14 +16,19 @@ #include #include "views/bad_kb_view.h" -#define BAD_KB_APP_BASE_FOLDER ANY_PATH("badkb") -#define BAD_KB_APP_PATH_LAYOUT_FOLDER BAD_KB_APP_BASE_FOLDER "/layouts" +#define BAD_KB_APP_BASE_FOLDER EXT_PATH("badkb") +#define BAD_KB_APP_PATH_LAYOUT_FOLDER BAD_KB_APP_BASE_FOLDER "/assets/layouts" +#define BAD_KB_APP_PATH_BOUND_KEYS_FOLDER EXT_PATH("badkb/.bt_keys") +#define BAD_KB_APP_PATH_BOUND_KEYS_FILE BAD_KB_APP_PATH_BOUND_KEYS_FOLDER "/.devices.keys" #define BAD_KB_APP_SCRIPT_EXTENSION ".txt" #define BAD_KB_APP_LAYOUT_EXTENSION ".kl" #define BAD_KB_MAC_ADDRESS_LEN 6 // need replace with MAC size maccro #define BAD_KB_ADV_NAME_MAX_LEN 18 +// this is the MAC address used when we do not forget paired device (BOUND STATE) +#define BAD_KB_BOUND_MAC_ADDRESS {0x41, 0x4a, 0xef, 0xb6, 0xa9, 0xd4}; + typedef enum { BadKbAppErrorNoFiles, BadKbAppErrorCloseRpc, @@ -36,12 +41,9 @@ typedef enum BadKbCustomEvent { } BadKbCustomEvent; typedef struct { + //uint8_t bounded_mac[BAD_KB_MAC_ADDRESS_LEN]; uint8_t mac[BAD_KB_MAC_ADDRESS_LEN]; char name[BAD_KB_ADV_NAME_MAX_LEN + 1]; - - // number of bt keys before starting the app (all keys added in - // the bt keys file then will be removed) - uint16_t n_keys; } BadKbBtConfig; struct BadKbApp { @@ -59,6 +61,7 @@ struct BadKbApp { ByteInput* byte_input; uint8_t mac[BAD_KB_MAC_ADDRESS_LEN]; char name[BAD_KB_ADV_NAME_MAX_LEN + 1]; + bool bt_remember; // weither we remember paired devices or not BadKbBtConfig bt_old_config; BadKbAppError error; @@ -68,6 +71,12 @@ struct BadKbApp { BadKbScript* bad_kb_script; bool is_bt; + + bool connection_init; + FuriHalUsbInterface* usb_prev_mode; + GapPairing bt_prev_mode; + + FuriThread* conn_init_thread; }; typedef enum { diff --git a/applications/main/bad_kb/bad_kb_script.c b/applications/main/bad_kb/bad_kb_script.c index e2fd464ff..b91f36b9f 100644 --- a/applications/main/bad_kb/bad_kb_script.c +++ b/applications/main/bad_kb/bad_kb_script.c @@ -11,6 +11,8 @@ #include +#include "bad_kb_app_i.h" + #define HID_BT_KEYS_STORAGE_PATH EXT_PATH("apps/Tools/.bt_hid.keys") #define TAG "BadKB" @@ -41,12 +43,6 @@ typedef enum { LevelRssiError = 0xFF, } LevelRssiRange; -typedef enum { - BadKbConnectionModeNone, - BadKbConnectionModeUsb, - BadKbConnectionModeBt, -} BadKbConnectionMode; - /** * Delays for waiting between HID key press and key release */ @@ -65,6 +61,7 @@ struct BadKbScript { FuriString* keyboard_layout; uint32_t defdelay; uint16_t layout[128]; + uint32_t stringdelay; FuriThread* thread; uint8_t file_buf[FILE_BUFFER_LEN + 1]; uint8_t buf_start; @@ -148,6 +145,8 @@ static const char ducky_cmd_delay[] = {"DELAY "}; static const char ducky_cmd_string[] = {"STRING "}; static const char ducky_cmd_defdelay_1[] = {"DEFAULT_DELAY "}; static const char ducky_cmd_defdelay_2[] = {"DEFAULTDELAY "}; +static const char ducky_cmd_stringdelay_1[] = {"STRINGDELAY "}; +static const char ducky_cmd_stringdelay_2[] = {"STRING_DELAY "}; static const char ducky_cmd_repeat[] = {"REPEAT "}; static const char ducky_cmd_sysrq[] = {"SYSRQ "}; @@ -170,27 +169,22 @@ static const uint8_t numpad_keys[10] = { HID_KEYPAD_9, }; -BadKbConnectionMode connection_mode = BadKbConnectionModeNone; -FuriHalUsbInterface* usb_mode_prev = NULL; -GapPairing bt_mode_prev = GapPairingNone; -bool bt_connected = false; -bool usb_connected = false; uint8_t bt_timeout = 0; static LevelRssiRange bt_remote_rssi_range(Bt* bt) { - BtRssi rssi_data = {0}; + uint8_t rssi; - if(!bt_remote_rssi(bt, &rssi_data)) return LevelRssiError; + if(!bt_remote_rssi(bt, &rssi)) return LevelRssiError; - if(rssi_data.rssi <= 39) + if(rssi <= 39) return LevelRssi39_0; - else if(rssi_data.rssi <= 59) + else if(rssi <= 59) return LevelRssi59_40; - else if(rssi_data.rssi <= 79) + else if(rssi <= 79) return LevelRssi79_60; - else if(rssi_data.rssi <= 99) + else if(rssi <= 99) return LevelRssi99_80; - else if(rssi_data.rssi <= 122) + else if(rssi <= 122) return LevelRssi122_100; return LevelRssiError; @@ -304,6 +298,7 @@ static bool ducky_altstring(BadKbScript* bad_kb, const char* param) { static bool ducky_string(BadKbScript* bad_kb, const char* param) { uint32_t i = 0; + while(param[i] != '\0') { uint16_t keycode = BADKB_ASCII_TO_KEY(bad_kb, param[i]); if(keycode != HID_KEYBOARD_NONE) { @@ -315,9 +310,13 @@ static bool ducky_string(BadKbScript* bad_kb, const char* param) { furi_hal_hid_kb_press(keycode); furi_hal_hid_kb_release(keycode); } + if(bad_kb->stringdelay > 0) { + furi_delay_ms(bad_kb->stringdelay); + } } i++; } + bad_kb->stringdelay = 0; return true; } @@ -379,6 +378,19 @@ static int32_t snprintf(error, error_len, "Invalid number %s", line_tmp); } return (state) ? (0) : SCRIPT_STATE_ERROR; + } else if( + (strncmp(line_tmp, ducky_cmd_stringdelay_1, strlen(ducky_cmd_stringdelay_1)) == 0) || + (strncmp(line_tmp, ducky_cmd_stringdelay_2, strlen(ducky_cmd_stringdelay_2)) == 0)) { + //STRINGDELAY, finally it's here + line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; + state = ducky_get_number(line_tmp, &bad_kb->stringdelay); + if((state) && (bad_kb->stringdelay > 0)) { + return state; + } + if(error != NULL) { + snprintf(error, error_len, "Invalid number %s", line_tmp); + } + return SCRIPT_STATE_ERROR; } else if(strncmp(line_tmp, ducky_cmd_string, strlen(ducky_cmd_string)) == 0) { // STRING line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; @@ -607,10 +619,8 @@ static void bad_kb_bt_hid_state_callback(BtStatus status, void* context) { if(r != LevelRssiError) { bt_timeout = bt_hid_delays[r]; } - bt_connected = true; furi_thread_flags_set(furi_thread_get_id(bad_kb->thread), WorkerEvtConnect); } else { - bt_connected = false; furi_thread_flags_set(furi_thread_get_id(bad_kb->thread), WorkerEvtDisconnect); } } @@ -620,83 +630,116 @@ static void bad_kb_usb_hid_state_callback(bool state, void* context) { BadKbScript* bad_kb = context; if(state == true) { - usb_connected = true; furi_thread_flags_set(furi_thread_get_id(bad_kb->thread), WorkerEvtConnect); } else { - usb_connected = false; furi_thread_flags_set(furi_thread_get_id(bad_kb->thread), WorkerEvtDisconnect); } } -void bad_kb_bt_init(Bt* bt) { - bt_timeout = bt_hid_delays[LevelRssi39_0]; - bt_disconnect(bt); - furi_delay_ms(200); - bt_keys_storage_set_storage_path(bt, HID_BT_KEYS_STORAGE_PATH); - furi_assert(bt_set_profile(bt, BtProfileHidKeyboard)); - bt_mode_prev = bt_get_profile_pairing_method(bt); - bt_set_profile_pairing_method(bt, GapPairingNone); - furi_hal_bt_start_advertising(); - // disable peer key adding to bt SRAM storage - bt_disable_peer_key_update(bt); - - connection_mode = BadKbConnectionModeBt; +void bad_kb_reload_worker(BadKbApp* app) { + bad_kb_script_close(app->bad_kb_script); + app->bad_kb_script = bad_kb_script_open(app->file_path, app->is_bt ? app->bt : NULL); + bad_kb_script_set_keyboard_layout(app->bad_kb_script, app->keyboard_layout); } -void bad_kb_bt_deinit(Bt* bt) { - // release all keys - // bt_hid_hold_while_keyboard_buffer_full(6, 3000); +void bad_kb_config_switch_mode(BadKbApp* app) { + scene_manager_previous_scene(app->scene_manager); + if(app->is_bt) { + furi_hal_bt_start_advertising(); + scene_manager_next_scene(app->scene_manager, BadKbSceneConfigBt); + } else { + furi_hal_bt_stop_advertising(); + scene_manager_next_scene(app->scene_manager, BadKbSceneConfigUsb); + } + bad_kb_reload_worker(app); +} - // stop ble - bt_disconnect(bt); +void bad_kb_config_switch_remember_mode(BadKbApp* app) { + if(app->bt_remember) { + // set bouding mac + uint8_t mac[6] = BAD_KB_BOUND_MAC_ADDRESS; + furi_hal_bt_set_profile_pairing_method( + FuriHalBtProfileHidKeyboard, GapPairingPinCodeVerifyYesNo); + bt_set_profile_mac_address(app->bt, mac); // this also restart bt + // enable keys storage + bt_enable_peer_key_update(app->bt); + } else { + // set back user defined mac address + furi_hal_bt_set_profile_pairing_method(FuriHalBtProfileHidKeyboard, GapPairingNone); + bt_set_profile_mac_address(app->bt, app->mac); + // disable key storage + bt_disable_peer_key_update(app->bt); + } + bad_kb_reload_worker(app); +} - // Wait 2nd core to update nvm storage - furi_delay_ms(200); +int32_t bad_kb_connection_init(BadKbApp* app) { + app->usb_prev_mode = furi_hal_usb_get_config(); + furi_hal_usb_set_config(NULL, NULL); - bt_keys_storage_set_default_path(bt); + bt_timeout = bt_hid_delays[LevelRssi39_0]; + bt_disconnect(app->bt); + // furi_delay_ms(200); + bt_keys_storage_set_storage_path(app->bt, BAD_KB_APP_PATH_BOUND_KEYS_FILE); + app->bt_prev_mode = furi_hal_bt_get_profile_pairing_method(FuriHalBtProfileHidKeyboard); + if(app->bt_remember) { + uint8_t mac[6] = BAD_KB_BOUND_MAC_ADDRESS; + furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileHidKeyboard, mac); + // using GapPairingNone breaks bounding between devices + furi_hal_bt_set_profile_pairing_method( + FuriHalBtProfileHidKeyboard, GapPairingPinCodeVerifyYesNo); + } else { + furi_hal_bt_set_profile_pairing_method(FuriHalBtProfileHidKeyboard, GapPairingNone); + } - bt_set_profile_pairing_method(bt, bt_mode_prev); + bt_set_profile(app->bt, BtProfileHidKeyboard); + if(app->is_bt) { + furi_hal_bt_start_advertising(); + if(app->bt_remember) { + bt_enable_peer_key_update(app->bt); + } else { + bt_disable_peer_key_update(app->bt); // disable peer key adding to bt SRAM storage + } + } else { + furi_hal_bt_stop_advertising(); + } + app->connection_init = true; + + return 0; +} + +void bad_kb_connection_deinit(BadKbApp* app) { + if(!app->connection_init) return; + + furi_hal_usb_set_config(app->usb_prev_mode, NULL); + + // bt_hid_hold_while_keyboard_buffer_full(6, 3000); // release all keys + bt_disconnect(app->bt); // stop ble + // furi_delay_ms(200); // Wait 2nd core to update nvm storage + bt_keys_storage_set_default_path(app->bt); + if(app->bt_remember) { + // hal primitives doesn't restarts ble, that's what we want cuz we are shutting down + furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileHidKeyboard, app->mac); + } + bt_enable_peer_key_update(app->bt); // starts saving peer keys (bounded devices) // fails if ble radio stack isn't ready when switching profile // if it happens, maybe we should increase the delay after bt_disconnect - bt_set_profile(bt, BtProfileSerial); - - // starts saving peer keys (bounded devices) - bt_enable_peer_key_update(bt); - - connection_mode = BadKbConnectionModeNone; + bt_set_profile(app->bt, BtProfileSerial); + furi_hal_bt_set_profile_pairing_method(FuriHalBtProfileHidKeyboard, app->bt_prev_mode); } -void bad_kb_usb_init() { - usb_mode_prev = furi_hal_usb_get_config(); - - connection_mode = BadKbConnectionModeUsb; -} - -void bad_kb_usb_deinit() { - furi_hal_usb_set_config(usb_mode_prev, NULL); - - connection_mode = BadKbConnectionModeNone; -} - -void bad_kb_connection_init(Bt* bt) { - if(connection_mode != BadKbConnectionModeNone) return; - - if(bt) { - bad_kb_bt_init(bt); +static uint32_t bad_kb_flags_get(uint32_t flags_mask, uint32_t timeout) { + uint32_t flags = furi_thread_flags_get(); + furi_check((flags & FuriFlagError) == 0); + if(flags == 0) { + flags = furi_thread_flags_wait(flags_mask, FuriFlagWaitAny, timeout); + furi_check(((flags & FuriFlagError) == 0) || (flags == (unsigned)FuriFlagErrorTimeout)); } else { - bad_kb_usb_init(); - } -} - -void bad_kb_connection_deinit(Bt* bt) { - if(connection_mode == BadKbConnectionModeNone) return; - - if(connection_mode == BadKbConnectionModeBt) { - bad_kb_bt_deinit(bt); - } else { - bad_kb_usb_deinit(); + uint32_t state = furi_thread_flags_clear(flags); + furi_check((state & FuriFlagError) == 0); } + return flags; } static int32_t bad_kb_worker(void* context) { @@ -705,8 +748,6 @@ static int32_t bad_kb_worker(void* context) { BadKbWorkerState worker_state = BadKbStateInit; int32_t delay_val = 0; - bad_kb_connection_init(bad_kb->bt); - if(bad_kb->bt) { bt_set_status_changed_callback(bad_kb->bt, bad_kb_bt_hid_state_callback, bad_kb); } else { @@ -727,7 +768,11 @@ static int32_t bad_kb_worker(void* context) { FSOM_OPEN_EXISTING)) { if((ducky_script_preload(bad_kb, script_file)) && (bad_kb->st.line_nb > 0)) { if(bad_kb->bt) { - worker_state = BadKbStateNotConnected; // Ready to run + if(furi_hal_bt_is_connected()) { + worker_state = BadKbStateIdle; // Ready to run + } else { + worker_state = BadKbStateNotConnected; // Not connected + } } else { if(furi_hal_hid_is_connected()) { worker_state = BadKbStateIdle; // Ready to run @@ -745,30 +790,20 @@ static int32_t bad_kb_worker(void* context) { bad_kb->st.state = worker_state; } else if(worker_state == BadKbStateNotConnected) { // State: Not connected - if((bad_kb->bt && bt_connected) || (!bad_kb->bt && usb_connected)) { + uint32_t flags = bad_kb_flags_get( + WorkerEvtEnd | WorkerEvtConnect | WorkerEvtToggle, FuriWaitForever); + if(flags & WorkerEvtEnd) { + break; + } else if(flags & WorkerEvtConnect) { worker_state = BadKbStateIdle; // Ready to run - } else { - uint32_t flags = furi_thread_flags_wait( - WorkerEvtEnd | WorkerEvtConnect | WorkerEvtToggle, - FuriFlagWaitAny, - FuriWaitForever); - furi_check((flags & FuriFlagError) == 0); - if(flags & WorkerEvtEnd) { - break; - } else if(flags & WorkerEvtConnect) { - worker_state = BadKbStateIdle; // Ready to run - } else if(flags & WorkerEvtToggle) { - worker_state = BadKbStateWillRun; // Will run when connected - } + } else if(flags & WorkerEvtToggle) { + worker_state = BadKbStateWillRun; // Will run when connected } bad_kb->st.state = worker_state; } else if(worker_state == BadKbStateIdle) { // State: ready to start - uint32_t flags = furi_thread_flags_wait( - WorkerEvtEnd | WorkerEvtToggle | WorkerEvtDisconnect, - FuriFlagWaitAny, - FuriWaitForever); - furi_check((flags & FuriFlagError) == 0); + uint32_t flags = bad_kb_flags_get( + WorkerEvtEnd | WorkerEvtToggle | WorkerEvtDisconnect, FuriWaitForever); if(flags & WorkerEvtEnd) { break; } else if(flags & WorkerEvtToggle) { // Start executing script @@ -777,6 +812,7 @@ static int32_t bad_kb_worker(void* context) { bad_kb->buf_len = 0; bad_kb->st.line_cur = 0; bad_kb->defdelay = 0; + bad_kb->stringdelay = 0; bad_kb->repeat_cnt = 0; bad_kb->file_end = false; storage_file_seek(script_file, 0, true); @@ -788,11 +824,8 @@ static int32_t bad_kb_worker(void* context) { bad_kb->st.state = worker_state; } else if(worker_state == BadKbStateWillRun) { // State: start on connection - uint32_t flags = furi_thread_flags_wait( - WorkerEvtEnd | WorkerEvtConnect | WorkerEvtToggle, - FuriFlagWaitAny, - FuriWaitForever); - furi_check((flags & FuriFlagError) == 0); + uint32_t flags = bad_kb_flags_get( + WorkerEvtEnd | WorkerEvtConnect | WorkerEvtToggle, FuriWaitForever); if(flags & WorkerEvtEnd) { break; } else if(flags & WorkerEvtConnect) { // Start executing script @@ -801,16 +834,26 @@ static int32_t bad_kb_worker(void* context) { bad_kb->buf_len = 0; bad_kb->st.line_cur = 0; bad_kb->defdelay = 0; + bad_kb->stringdelay = 0; bad_kb->repeat_cnt = 0; bad_kb->file_end = false; storage_file_seek(script_file, 0, true); // extra time for PC to recognize Flipper as keyboard - furi_thread_flags_wait(0, FuriFlagWaitAny, 1500); + flags = furi_thread_flags_wait( + WorkerEvtEnd | WorkerEvtDisconnect | WorkerEvtToggle, + FuriFlagWaitAny | FuriFlagNoClear, + 1500); + if(flags == (unsigned)FuriFlagErrorTimeout) { + // If nothing happened - start script execution + worker_state = BadKbStateRunning; + } else if(flags & WorkerEvtToggle) { + worker_state = BadKbStateIdle; + furi_thread_flags_clear(WorkerEvtToggle); + } if(bad_kb->bt) { update_bt_timeout(bad_kb->bt); } bad_kb_script_set_keyboard_layout(bad_kb, bad_kb->keyboard_layout); - worker_state = BadKbStateRunning; } else if(flags & WorkerEvtToggle) { // Cancel scheduled execution worker_state = BadKbStateNotConnected; } @@ -875,9 +918,8 @@ static int32_t bad_kb_worker(void* context) { } else if( (worker_state == BadKbStateFileError) || (worker_state == BadKbStateScriptError)) { // State: error - uint32_t flags = furi_thread_flags_wait( - WorkerEvtEnd, FuriFlagWaitAny, FuriWaitForever); // Waiting for exit command - furi_check((flags & FuriFlagError) == 0); + uint32_t flags = + bad_kb_flags_get(WorkerEvtEnd, FuriWaitForever); // Waiting for exit command if(flags & WorkerEvtEnd) { break; } @@ -921,6 +963,7 @@ BadKbScript* bad_kb_script_open(FuriString* file_path, Bt* bt) { bad_kb->st.state = BadKbStateInit; bad_kb->st.error[0] = '\0'; + bad_kb->st.is_bt = !!bt; bad_kb->bt = bt; @@ -949,7 +992,7 @@ void bad_kb_script_set_keyboard_layout(BadKbScript* bad_kb, FuriString* layout_p } File* layout_file = storage_file_alloc(furi_record_open(RECORD_STORAGE)); - if(!furi_string_empty(layout_path)) { + if(!furi_string_empty(layout_path)) { //-V1051 furi_string_set(bad_kb->keyboard_layout, layout_path); if(storage_file_open( layout_file, furi_string_get_cstr(layout_path), FSAM_READ, FSOM_OPEN_EXISTING)) { diff --git a/applications/main/bad_kb/bad_kb_script.h b/applications/main/bad_kb/bad_kb_script.h index 0ea701eb8..a724971ec 100644 --- a/applications/main/bad_kb/bad_kb_script.h +++ b/applications/main/bad_kb/bad_kb_script.h @@ -7,6 +7,8 @@ extern "C" { #include #include +typedef struct BadKbApp BadKbApp; + typedef struct BadKbScript BadKbScript; typedef enum { @@ -23,6 +25,8 @@ typedef enum { typedef struct { BadKbWorkerState state; + bool is_bt; + uint32_t pin; uint16_t line_cur; uint16_t line_nb; uint32_t delay_remain; @@ -30,9 +34,13 @@ typedef struct { char error[64]; } BadKbState; -void bad_kb_connection_init(Bt* bt); +void bad_kb_config_switch_mode(BadKbApp* app); -void bad_kb_connection_deinit(Bt* bt); +void bad_kb_config_switch_remember_mode(BadKbApp* app); + +int32_t bad_kb_connection_init(BadKbApp* app); + +void bad_kb_connection_deinit(BadKbApp* app); BadKbScript* bad_kb_script_open(FuriString* file_path, Bt* bt); diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config_bt.c b/applications/main/bad_kb/scenes/bad_kb_scene_config_bt.c index 4412f0796..70dde9e16 100644 --- a/applications/main/bad_kb/scenes/bad_kb_scene_config_bt.c +++ b/applications/main/bad_kb/scenes/bad_kb_scene_config_bt.c @@ -5,6 +5,7 @@ enum VarItemListIndex { VarItemListIndexConnection, + VarItemListIndexRemember, VarItemListIndexKeyboardLayout, VarItemListIndexAdvertisementName, VarItemListIndexMacAddress, @@ -19,6 +20,15 @@ void bad_kb_scene_config_bt_connection_callback(VariableItem* item) { view_dispatcher_send_custom_event(bad_kb->view_dispatcher, VarItemListIndexConnection); } +void bad_kb_scene_config_bt_bounding_callback(VariableItem* item) { + BadKbApp* bad_kb = variable_item_get_context(item); + bad_kb->bt_remember = variable_item_get_current_value_index(item); + XTREME_SETTINGS()->bad_bt_remember = bad_kb->bt_remember; + XTREME_SETTINGS_SAVE(); + variable_item_set_current_value_text(item, bad_kb->bt_remember ? "ON" : "OFF"); + view_dispatcher_send_custom_event(bad_kb->view_dispatcher, VarItemListIndexRemember); +} + void bad_kb_scene_config_bt_var_item_list_callback(void* context, uint32_t index) { BadKbApp* bad_kb = context; view_dispatcher_send_custom_event(bad_kb->view_dispatcher, index); @@ -34,11 +44,19 @@ void bad_kb_scene_config_bt_on_enter(void* context) { variable_item_set_current_value_index(item, bad_kb->is_bt); variable_item_set_current_value_text(item, bad_kb->is_bt ? "BT" : "USB"); + item = variable_item_list_add( + var_item_list, "Remember", 2, bad_kb_scene_config_bt_bounding_callback, bad_kb); + variable_item_set_current_value_index(item, bad_kb->bt_remember); + variable_item_set_current_value_text(item, bad_kb->bt_remember ? "ON" : "OFF"); + item = variable_item_list_add(var_item_list, "Keyboard layout", 0, NULL, bad_kb); item = variable_item_list_add(var_item_list, "BT device name", 0, NULL, bad_kb); - item = variable_item_list_add(var_item_list, "BT MAC address", 0, NULL, bad_kb); + // this doesn't update instantly when toggling between Bounding modes + if(!bad_kb->bt_remember) { + item = variable_item_list_add(var_item_list, "BT MAC address", 0, NULL, bad_kb); + } variable_item_list_set_enter_callback( var_item_list, bad_kb_scene_config_bt_var_item_list_callback, bad_kb); @@ -56,23 +74,15 @@ bool bad_kb_scene_config_bt_on_event(void* context, SceneManagerEvent event) { if(event.event == VarItemListIndexKeyboardLayout) { scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigLayout); } else if(event.event == VarItemListIndexConnection) { - bad_kb_script_close(bad_kb->bad_kb_script); - bad_kb_connection_deinit(bad_kb->bt); - bad_kb->bad_kb_script = - bad_kb_script_open(bad_kb->file_path, bad_kb->is_bt ? bad_kb->bt : NULL); - bad_kb_script_set_keyboard_layout(bad_kb->bad_kb_script, bad_kb->keyboard_layout); + bad_kb_config_switch_mode(bad_kb); + } else if(event.event == VarItemListIndexRemember) { + bad_kb_config_switch_remember_mode(bad_kb); scene_manager_previous_scene(bad_kb->scene_manager); - if(bad_kb->is_bt) { - scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigBt); - } else { - scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigUsb); - } + scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigBt); } else if(event.event == VarItemListIndexAdvertisementName) { scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigName); } else if(event.event == VarItemListIndexMacAddress) { scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigMac); - // } else { - // furi_crash("Unknown key type"); } } diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config_layout.c b/applications/main/bad_kb/scenes/bad_kb_scene_config_layout.c index 3842c59fa..a73822fcc 100644 --- a/applications/main/bad_kb/scenes/bad_kb_scene_config_layout.c +++ b/applications/main/bad_kb/scenes/bad_kb_scene_config_layout.c @@ -3,8 +3,6 @@ #include "furi_hal_usb.h" #include -#define KEYBOARD_FOLDER "/ext/badkb/layouts" - static bool bad_kb_layout_select(BadKbApp* bad_kb) { furi_assert(bad_kb); @@ -19,7 +17,8 @@ static bool bad_kb_layout_select(BadKbApp* bad_kb) { DialogsFileBrowserOptions browser_options; dialog_file_browser_set_basic_options( &browser_options, BAD_KB_APP_LAYOUT_EXTENSION, &I_keyboard_10px); - browser_options.base_path = KEYBOARD_FOLDER; + browser_options.base_path = BAD_KB_APP_PATH_LAYOUT_FOLDER; + browser_options.skip_assets = false; // Input events and views are managed by file_browser bool res = dialog_file_browser_show( diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config_mac.c b/applications/main/bad_kb/scenes/bad_kb_scene_config_mac.c index 0dc4be10a..d7027537a 100644 --- a/applications/main/bad_kb/scenes/bad_kb_scene_config_mac.c +++ b/applications/main/bad_kb/scenes/bad_kb_scene_config_mac.c @@ -2,14 +2,14 @@ #define TAG "BadKbConfigMac" -static uint8_t* reverse_mac_addr(uint8_t* mac) { +static uint8_t* reverse_mac_addr(uint8_t* mac, uint8_t* out) { uint8_t tmp; for(int i = 0; i < 3; i++) { tmp = mac[i]; - mac[i] = mac[5 - i]; - mac[5 - i] = tmp; + out[i] = mac[5 - i]; + out[5 - i] = tmp; } - return mac; + return out; } void bad_kb_scene_config_mac_byte_input_callback(void* context) { @@ -23,13 +23,13 @@ void bad_kb_scene_config_mac_on_enter(void* context) { // Setup view ByteInput* byte_input = bad_kb->byte_input; - byte_input_set_header_text(byte_input, "Enter new MAC address"); + byte_input_set_header_text(byte_input, "Set BT MAC address"); byte_input_set_result_callback( byte_input, bad_kb_scene_config_mac_byte_input_callback, NULL, bad_kb, - reverse_mac_addr(bad_kb->mac), + reverse_mac_addr(bad_kb->mac, bad_kb->mac), GAP_MAC_ADDR_SIZE); view_dispatcher_switch_to_view(bad_kb->view_dispatcher, BadKbAppViewConfigMac); } @@ -40,7 +40,8 @@ bool bad_kb_scene_config_mac_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == BadKbAppCustomEventByteInputDone) { - bt_set_profile_mac_address(bad_kb->bt, reverse_mac_addr(bad_kb->mac)); + uint8_t mac[GAP_MAC_ADDR_SIZE]; + bt_set_profile_mac_address(bad_kb->bt, reverse_mac_addr(bad_kb->mac, mac)); scene_manager_previous_scene(bad_kb->scene_manager); consumed = true; } @@ -54,4 +55,7 @@ void bad_kb_scene_config_mac_on_exit(void* context) { // Clear view byte_input_set_result_callback(bad_kb->byte_input, NULL, NULL, NULL, NULL, 0); byte_input_set_header_text(bad_kb->byte_input, ""); + + // reverse back addr (in case it didn't get modified) + reverse_mac_addr(bad_kb->mac, bad_kb->mac); } diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config_name.c b/applications/main/bad_kb/scenes/bad_kb_scene_config_name.c index 82dd7a850..59574c334 100644 --- a/applications/main/bad_kb/scenes/bad_kb_scene_config_name.c +++ b/applications/main/bad_kb/scenes/bad_kb_scene_config_name.c @@ -10,7 +10,7 @@ void bad_kb_scene_config_name_on_enter(void* context) { BadKbApp* bad_kb = context; TextInput* text_input = bad_kb->text_input; - text_input_set_header_text(text_input, "Set BLE adv name"); + text_input_set_header_text(text_input, "Set BT device name"); text_input_set_result_callback( text_input, diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config_usb.c b/applications/main/bad_kb/scenes/bad_kb_scene_config_usb.c index 232ef8796..d3ff35fc8 100644 --- a/applications/main/bad_kb/scenes/bad_kb_scene_config_usb.c +++ b/applications/main/bad_kb/scenes/bad_kb_scene_config_usb.c @@ -50,19 +50,7 @@ bool bad_kb_scene_config_usb_on_event(void* context, SceneManagerEvent event) { if(event.event == VarItemListIndexKeyboardLayout) { scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigLayout); } else if(event.event == VarItemListIndexConnection) { - bad_kb_script_close(bad_kb->bad_kb_script); - bad_kb_connection_deinit(bad_kb->bt); - bad_kb->bad_kb_script = - bad_kb_script_open(bad_kb->file_path, bad_kb->is_bt ? bad_kb->bt : NULL); - bad_kb_script_set_keyboard_layout(bad_kb->bad_kb_script, bad_kb->keyboard_layout); - scene_manager_previous_scene(bad_kb->scene_manager); - if(bad_kb->is_bt) { - scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigBt); - } else { - scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigUsb); - } - // } else { - // furi_crash("Unknown key type"); + bad_kb_config_switch_mode(bad_kb); } } diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_file_select.c b/applications/main/bad_kb/scenes/bad_kb_scene_file_select.c index bb2ddf6ca..ba238f390 100644 --- a/applications/main/bad_kb/scenes/bad_kb_scene_file_select.c +++ b/applications/main/bad_kb/scenes/bad_kb_scene_file_select.c @@ -22,7 +22,6 @@ static bool bad_kb_file_select(BadKbApp* bad_kb) { void bad_kb_scene_file_select_on_enter(void* context) { BadKbApp* bad_kb = context; - furi_hal_usb_disable(); if(bad_kb->bad_kb_script) { bad_kb_script_close(bad_kb->bad_kb_script); bad_kb->bad_kb_script = NULL; @@ -35,7 +34,6 @@ void bad_kb_scene_file_select_on_enter(void* context) { scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneWork); } else { - furi_hal_usb_enable(); view_dispatcher_stop(bad_kb->view_dispatcher); } } diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_work.c b/applications/main/bad_kb/scenes/bad_kb_scene_work.c index 165ee0289..e962f9132 100644 --- a/applications/main/bad_kb/scenes/bad_kb_scene_work.c +++ b/applications/main/bad_kb/scenes/bad_kb_scene_work.c @@ -16,10 +16,12 @@ bool bad_kb_scene_work_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == InputKeyLeft) { - if(app->is_bt) { - scene_manager_next_scene(app->scene_manager, BadKbSceneConfigBt); - } else { - scene_manager_next_scene(app->scene_manager, BadKbSceneConfigUsb); + if(bad_kb_is_idle_state(app->bad_kb_view)) { + if(app->is_bt) { + scene_manager_next_scene(app->scene_manager, BadKbSceneConfigBt); + } else { + scene_manager_next_scene(app->scene_manager, BadKbSceneConfigUsb); + } } consumed = true; } else if(event.event == InputKeyOk) { diff --git a/applications/main/bad_kb/views/bad_kb_view.c b/applications/main/bad_kb/views/bad_kb_view.c index 1ffe58e9c..26ec90809 100644 --- a/applications/main/bad_kb/views/bad_kb_view.c +++ b/applications/main/bad_kb/views/bad_kb_view.c @@ -1,5 +1,6 @@ #include "bad_kb_view.h" #include "../bad_kb_script.h" +#include "../bad_kb_app_i.h" #include #include #include @@ -24,7 +25,8 @@ static void bad_kb_draw_callback(Canvas* canvas, void* _model) { BadKbModel* model = _model; FuriString* disp_str; - disp_str = furi_string_alloc_set(model->file_name); + disp_str = furi_string_alloc_set(model->state.is_bt ? "(BT) " : "(USB) "); + furi_string_cat_str(disp_str, model->file_name); elements_string_fit_width(canvas, disp_str, 128 - 2); canvas_set_font(canvas, FontSecondary); canvas_draw_str(canvas, 2, 8, furi_string_get_cstr(disp_str)); @@ -38,6 +40,9 @@ static void bad_kb_draw_callback(Canvas* canvas, void* _model) { furi_string_push_back(disp_str, model->layout[i]); furi_string_push_back(disp_str, ')'); } + if(model->state.pin) { + furi_string_cat_printf(disp_str, " PIN: %ld", model->state.pin); + } elements_string_fit_width(canvas, disp_str, 128 - 2); canvas_draw_str( canvas, 2, 8 + canvas_current_font_height(canvas), furi_string_get_cstr(disp_str)); @@ -51,19 +56,15 @@ static void bad_kb_draw_callback(Canvas* canvas, void* _model) { if(XTREME_ASSETS()->is_nsfw) { elements_button_center(canvas, "Cum"); } else { - elements_button_center(canvas, "Start"); + elements_button_center(canvas, "Run"); } + elements_button_left(canvas, "Config"); } else if((model->state.state == BadKbStateRunning) || (model->state.state == BadKbStateDelay)) { elements_button_center(canvas, "Stop"); } else if(model->state.state == BadKbStateWillRun) { elements_button_center(canvas, "Cancel"); } - if((model->state.state == BadKbStateNotConnected) || (model->state.state == BadKbStateIdle) || - (model->state.state == BadKbStateDone)) { - elements_button_left(canvas, "Config"); - } - if(model->state.state == BadKbStateNotConnected) { canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18); canvas_set_font(canvas, FontPrimary); @@ -214,6 +215,14 @@ void bad_kb_set_layout(BadKb* bad_kb, const char* layout) { void bad_kb_set_state(BadKb* bad_kb, BadKbState* st) { furi_assert(st); + uint32_t pin = 0; + if(bad_kb->context != NULL) { + BadKbApp* app = bad_kb->context; + if(app->bt != NULL) { + pin = app->bt->pin; + } + } + st->pin = pin; with_view_model( bad_kb->view, BadKbModel * model, @@ -223,3 +232,18 @@ void bad_kb_set_state(BadKb* bad_kb, BadKbState* st) { }, true); } + +bool bad_kb_is_idle_state(BadKb* bad_kb) { + bool is_idle = false; + with_view_model( + bad_kb->view, + BadKbModel * model, + { + if((model->state.state == BadKbStateIdle) || (model->state.state == BadKbStateDone) || + (model->state.state == BadKbStateNotConnected)) { + is_idle = true; + } + }, + false); + return is_idle; +} diff --git a/applications/main/bad_kb/views/bad_kb_view.h b/applications/main/bad_kb/views/bad_kb_view.h index 24fdf4792..f16814bb4 100644 --- a/applications/main/bad_kb/views/bad_kb_view.h +++ b/applications/main/bad_kb/views/bad_kb_view.h @@ -19,3 +19,5 @@ void bad_kb_set_file_name(BadKb* bad_kb, const char* name); void bad_kb_set_layout(BadKb* bad_kb, const char* layout); void bad_kb_set_state(BadKb* bad_kb, BadKbState* st); + +bool bad_kb_is_idle_state(BadKb* bad_kb); diff --git a/applications/main/fap_loader/fap_loader_app.c b/applications/main/fap_loader/fap_loader_app.c index 7911aa068..559463988 100644 --- a/applications/main/fap_loader/fap_loader_app.c +++ b/applications/main/fap_loader/fap_loader_app.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include "elf_cpp/elf_hashtable.h" #include "fap_loader_app.h" @@ -35,7 +36,7 @@ bool fap_loader_load_name_and_icon( if(preload_res == FlipperApplicationPreloadStatusSuccess) { const FlipperApplicationManifest* manifest = flipper_application_get_manifest(app); - if(manifest->has_icon) { + if(manifest->has_icon && icon_ptr != NULL) { memcpy(*icon_ptr, manifest->icon, FAP_MANIFEST_MAX_ICON_SIZE); } furi_string_set(item_name, manifest->name); @@ -105,6 +106,12 @@ static bool fap_loader_run_selected_app(FapLoader* loader) { FURI_LOG_I(TAG, "FAP Loader is starting app"); FuriThread* thread = flipper_application_spawn(loader->app, NULL); + + FuriString* app_name = furi_string_alloc(); + path_extract_filename_no_ext(furi_string_get_cstr(loader->fap_path), app_name); + furi_thread_set_appid(thread, furi_string_get_cstr(app_name)); + furi_string_free(app_name); + furi_thread_start(thread); furi_thread_join(thread); @@ -146,6 +153,7 @@ static bool fap_loader_select_app(FapLoader* loader) { .skip_assets = true, .icon = &I_unknown_10px, .hide_ext = true, + .hide_dot_files = true, .item_loader_callback = fap_loader_item_callback, .item_loader_context = loader, .base_path = EXT_PATH("apps"), diff --git a/applications/main/gpio/scenes/gpio_scene_start.c b/applications/main/gpio/scenes/gpio_scene_start.c index 1c1a665c7..2d8f7adbc 100644 --- a/applications/main/gpio/scenes/gpio_scene_start.c +++ b/applications/main/gpio/scenes/gpio_scene_start.c @@ -107,6 +107,7 @@ bool gpio_scene_start_on_event(void* context, SceneManagerEvent event) { } else if(event.event == GpioStartEventUsbUart) { scene_manager_set_scene_state(app->scene_manager, GpioSceneStart, GpioItemUsbUart); if(!furi_hal_usb_is_locked()) { + DOLPHIN_DEED(DolphinDeedGpioUartBridge); scene_manager_next_scene(app->scene_manager, GpioSceneUsbUart); } else { scene_manager_next_scene(app->scene_manager, GpioSceneUsbUartCloseRpc); diff --git a/applications/main/ibutton/ibutton.c b/applications/main/ibutton/ibutton.c index 85212f42b..79999adb2 100644 --- a/applications/main/ibutton/ibutton.c +++ b/applications/main/ibutton/ibutton.c @@ -1,10 +1,6 @@ -#include "ibutton.h" -#include "assets_icons.h" #include "ibutton_i.h" -#include "ibutton/scenes/ibutton_scene.h" + #include -#include -#include #include #define TAG "iButtonApp" @@ -34,50 +30,13 @@ static const NotificationSequence* ibutton_notification_sequences[] = { }; static void ibutton_make_app_folder(iButton* ibutton) { - if(!storage_simply_mkdir(ibutton->storage, IBUTTON_APP_FOLDER)) { + Storage* storage = furi_record_open(RECORD_STORAGE); + + if(!storage_simply_mkdir(storage, IBUTTON_APP_FOLDER)) { dialog_message_show_storage_error(ibutton->dialogs, "Cannot create\napp folder"); } -} -bool ibutton_load_key_data(iButton* ibutton, FuriString* key_path, bool show_dialog) { - FlipperFormat* file = flipper_format_file_alloc(ibutton->storage); - bool result = false; - FuriString* data; - data = furi_string_alloc(); - - do { - if(!flipper_format_file_open_existing(file, furi_string_get_cstr(key_path))) break; - - // header - uint32_t version; - if(!flipper_format_read_header(file, data, &version)) break; - if(furi_string_cmp_str(data, IBUTTON_APP_FILE_TYPE) != 0) break; - if(version != 1) break; - - // key type - iButtonKeyType type; - if(!flipper_format_read_string(file, "Key type", data)) break; - if(!ibutton_key_get_type_by_string(furi_string_get_cstr(data), &type)) break; - - // key data - uint8_t key_data[IBUTTON_KEY_DATA_SIZE] = {0}; - if(!flipper_format_read_hex(file, "Data", key_data, ibutton_key_get_size_by_type(type))) - break; - - ibutton_key_set_type(ibutton->key, type); - ibutton_key_set_data(ibutton->key, key_data, IBUTTON_KEY_DATA_SIZE); - - result = true; - } while(false); - - flipper_format_free(file); - furi_string_free(data); - - if((!result) && (show_dialog)) { - dialog_message_show_storage_error(ibutton->dialogs, "Cannot load\nkey file"); - } - - return result; + furi_record_close(RECORD_STORAGE); } static void ibutton_rpc_command_callback(RpcAppSystemEvent event, void* context) { @@ -87,14 +46,14 @@ static void ibutton_rpc_command_callback(RpcAppSystemEvent event, void* context) if(event == RpcAppEventSessionClose) { view_dispatcher_send_custom_event( ibutton->view_dispatcher, iButtonCustomEventRpcSessionClose); - rpc_system_app_set_callback(ibutton->rpc_ctx, NULL, NULL); - ibutton->rpc_ctx = NULL; + rpc_system_app_set_callback(ibutton->rpc, NULL, NULL); + ibutton->rpc = NULL; } else if(event == RpcAppEventAppExit) { view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventRpcExit); } else if(event == RpcAppEventLoadFile) { view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventRpcLoad); } else { - rpc_system_app_confirm(ibutton->rpc_ctx, event, false); + rpc_system_app_confirm(ibutton->rpc, event, false); } } @@ -135,13 +94,13 @@ iButton* ibutton_alloc() { ibutton->gui = furi_record_open(RECORD_GUI); - ibutton->storage = furi_record_open(RECORD_STORAGE); ibutton->dialogs = furi_record_open(RECORD_DIALOGS); ibutton->notifications = furi_record_open(RECORD_NOTIFICATION); - ibutton->key = ibutton_key_alloc(); - ibutton->key_worker = ibutton_worker_alloc(); - ibutton_worker_start_thread(ibutton->key_worker); + ibutton->protocols = ibutton_protocols_alloc(); + ibutton->key = ibutton_key_alloc(ibutton_protocols_get_max_data_size(ibutton->protocols)); + ibutton->worker = ibutton_worker_alloc(ibutton->protocols); + ibutton_worker_start_thread(ibutton->worker); ibutton->submenu = submenu_alloc(); view_dispatcher_add_view( @@ -163,9 +122,9 @@ iButton* ibutton_alloc() { view_dispatcher_add_view( ibutton->view_dispatcher, iButtonViewWidget, widget_get_view(ibutton->widget)); - ibutton->dialog_ex = dialog_ex_alloc(); + ibutton->loading = loading_alloc(); view_dispatcher_add_view( - ibutton->view_dispatcher, iButtonViewDialogEx, dialog_ex_get_view(ibutton->dialog_ex)); + ibutton->view_dispatcher, iButtonViewLoading, loading_get_view(ibutton->loading)); return ibutton; } @@ -173,8 +132,8 @@ iButton* ibutton_alloc() { void ibutton_free(iButton* ibutton) { furi_assert(ibutton); - view_dispatcher_remove_view(ibutton->view_dispatcher, iButtonViewDialogEx); - dialog_ex_free(ibutton->dialog_ex); + view_dispatcher_remove_view(ibutton->view_dispatcher, iButtonViewLoading); + loading_free(ibutton->loading); view_dispatcher_remove_view(ibutton->view_dispatcher, iButtonViewWidget); widget_free(ibutton->widget); @@ -194,9 +153,6 @@ void ibutton_free(iButton* ibutton) { view_dispatcher_free(ibutton->view_dispatcher); scene_manager_free(ibutton->scene_manager); - furi_record_close(RECORD_STORAGE); - ibutton->storage = NULL; - furi_record_close(RECORD_NOTIFICATION); ibutton->notifications = NULL; @@ -206,103 +162,83 @@ void ibutton_free(iButton* ibutton) { furi_record_close(RECORD_GUI); ibutton->gui = NULL; - ibutton_worker_stop_thread(ibutton->key_worker); - ibutton_worker_free(ibutton->key_worker); + ibutton_worker_stop_thread(ibutton->worker); + ibutton_worker_free(ibutton->worker); ibutton_key_free(ibutton->key); + ibutton_protocols_free(ibutton->protocols); furi_string_free(ibutton->file_path); free(ibutton); } -bool ibutton_file_select(iButton* ibutton) { - DialogsFileBrowserOptions browser_options; - dialog_file_browser_set_basic_options(&browser_options, IBUTTON_APP_EXTENSION, &I_ibutt_10px); - browser_options.base_path = IBUTTON_APP_FOLDER; +bool ibutton_load_key(iButton* ibutton) { + view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewLoading); - bool success = dialog_file_browser_show( - ibutton->dialogs, ibutton->file_path, ibutton->file_path, &browser_options); + const bool success = ibutton_protocols_load( + ibutton->protocols, ibutton->key, furi_string_get_cstr(ibutton->file_path)); - if(success) { - success = ibutton_load_key_data(ibutton, ibutton->file_path, true); + if(!success) { + dialog_message_show_storage_error(ibutton->dialogs, "Cannot load\nkey file"); + + } else { + FuriString* tmp = furi_string_alloc(); + + path_extract_filename(ibutton->file_path, tmp, true); + strncpy(ibutton->key_name, furi_string_get_cstr(tmp), IBUTTON_KEY_NAME_SIZE); + + furi_string_free(tmp); } return success; } -bool ibutton_save_key(iButton* ibutton, const char* key_name) { - // Create ibutton directory if necessary +bool ibutton_select_and_load_key(iButton* ibutton) { + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options(&browser_options, IBUTTON_APP_EXTENSION, &I_ibutt_10px); + browser_options.base_path = IBUTTON_APP_FOLDER; + + if(furi_string_empty(ibutton->file_path)) { + furi_string_set(ibutton->file_path, browser_options.base_path); + } + + return dialog_file_browser_show( + ibutton->dialogs, ibutton->file_path, ibutton->file_path, &browser_options) && + ibutton_load_key(ibutton); +} + +bool ibutton_save_key(iButton* ibutton) { + view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewLoading); + ibutton_make_app_folder(ibutton); - FlipperFormat* file = flipper_format_file_alloc(ibutton->storage); iButtonKey* key = ibutton->key; + const bool success = + ibutton_protocols_save(ibutton->protocols, key, furi_string_get_cstr(ibutton->file_path)); - bool result = false; - - do { - // Check if we has old key - if(furi_string_end_with(ibutton->file_path, IBUTTON_APP_EXTENSION)) { - // First remove old key - ibutton_delete_key(ibutton); - - // Remove old key name from path - size_t filename_start = furi_string_search_rchar(ibutton->file_path, '/'); - furi_string_left(ibutton->file_path, filename_start); - } - - furi_string_cat_printf(ibutton->file_path, "/%s%s", key_name, IBUTTON_APP_EXTENSION); - - // Open file for write - if(!flipper_format_file_open_always(file, furi_string_get_cstr(ibutton->file_path))) break; - - // Write header - if(!flipper_format_write_header_cstr(file, IBUTTON_APP_FILE_TYPE, 1)) break; - - // Write key type - if(!flipper_format_write_comment_cstr(file, "Key type can be Cyfral, Dallas or Metakom")) - break; - const char* key_type = ibutton_key_get_string_by_type(ibutton_key_get_type(key)); - if(!flipper_format_write_string_cstr(file, "Key type", key_type)) break; - - // Write data - if(!flipper_format_write_comment_cstr( - file, "Data size for Cyfral is 2, for Metakom is 4, for Dallas is 8")) - break; - - if(!flipper_format_write_hex( - file, "Data", ibutton_key_get_data_p(key), ibutton_key_get_data_size(key))) - break; - result = true; - - } while(false); - - flipper_format_free(file); - - if(!result) { //-V547 + if(!success) { dialog_message_show_storage_error(ibutton->dialogs, "Cannot save\nkey file"); } - return result; + return success; } bool ibutton_delete_key(iButton* ibutton) { bool result = false; - result = storage_simply_remove(ibutton->storage, furi_string_get_cstr(ibutton->file_path)); + + Storage* storage = furi_record_open(RECORD_STORAGE); + result = storage_simply_remove(storage, furi_string_get_cstr(ibutton->file_path)); + furi_record_close(RECORD_STORAGE); + + ibutton_reset_key(ibutton); return result; } -void ibutton_text_store_set(iButton* ibutton, const char* text, ...) { - va_list args; - va_start(args, text); - - vsnprintf(ibutton->text_store, IBUTTON_TEXT_STORE_SIZE, text, args); - - va_end(args); -} - -void ibutton_text_store_clear(iButton* ibutton) { - memset(ibutton->text_store, 0, IBUTTON_TEXT_STORE_SIZE + 1); +void ibutton_reset_key(iButton* ibutton) { + memset(ibutton->key_name, 0, IBUTTON_KEY_NAME_SIZE + 1); + furi_string_reset(ibutton->file_path); + ibutton_key_reset(ibutton->key); } void ibutton_notification_message(iButton* ibutton, uint32_t message) { @@ -310,36 +246,44 @@ void ibutton_notification_message(iButton* ibutton, uint32_t message) { notification_message(ibutton->notifications, ibutton_notification_sequences[message]); } -int32_t ibutton_app(void* p) { +void ibutton_submenu_callback(void* context, uint32_t index) { + iButton* ibutton = context; + view_dispatcher_send_custom_event(ibutton->view_dispatcher, index); +} + +void ibutton_widget_callback(GuiButtonType result, InputType type, void* context) { + iButton* ibutton = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(ibutton->view_dispatcher, result); + } +} + +int32_t ibutton_app(void* arg) { iButton* ibutton = ibutton_alloc(); ibutton_make_app_folder(ibutton); bool key_loaded = false; - bool rpc_mode = false; - if(p && strlen(p)) { - uint32_t rpc_ctx = 0; - if(sscanf(p, "RPC %lX", &rpc_ctx) == 1) { + if((arg != NULL) && (strlen(arg) != 0)) { + if(sscanf(arg, "RPC %lX", (uint32_t*)&ibutton->rpc) == 1) { FURI_LOG_D(TAG, "Running in RPC mode"); - ibutton->rpc_ctx = (void*)rpc_ctx; - rpc_mode = true; - rpc_system_app_set_callback(ibutton->rpc_ctx, ibutton_rpc_command_callback, ibutton); - rpc_system_app_send_started(ibutton->rpc_ctx); + + rpc_system_app_set_callback(ibutton->rpc, ibutton_rpc_command_callback, ibutton); + rpc_system_app_send_started(ibutton->rpc); + } else { - furi_string_set(ibutton->file_path, (const char*)p); - if(ibutton_load_key_data(ibutton, ibutton->file_path, true)) { - key_loaded = true; - // TODO: Display an error if the key from p could not be loaded - } + furi_string_set(ibutton->file_path, (const char*)arg); + key_loaded = ibutton_load_key(ibutton); } } - if(rpc_mode) { + if(ibutton->rpc != NULL) { view_dispatcher_attach_to_gui( ibutton->view_dispatcher, ibutton->gui, ViewDispatcherTypeDesktop); scene_manager_next_scene(ibutton->scene_manager, iButtonSceneRpc); DOLPHIN_DEED(DolphinDeedIbuttonEmulate); + } else { view_dispatcher_attach_to_gui( ibutton->view_dispatcher, ibutton->gui, ViewDispatcherTypeFullscreen); @@ -353,9 +297,9 @@ int32_t ibutton_app(void* p) { view_dispatcher_run(ibutton->view_dispatcher); - if(ibutton->rpc_ctx) { - rpc_system_app_set_callback(ibutton->rpc_ctx, NULL, NULL); - rpc_system_app_send_exited(ibutton->rpc_ctx); + if(ibutton->rpc) { + rpc_system_app_set_callback(ibutton->rpc, NULL, NULL); + rpc_system_app_send_exited(ibutton->rpc); } ibutton_free(ibutton); return 0; diff --git a/applications/main/ibutton/ibutton_cli.c b/applications/main/ibutton/ibutton_cli.c index fab1ccf05..2b88b2007 100644 --- a/applications/main/ibutton/ibutton_cli.c +++ b/applications/main/ibutton/ibutton_cli.c @@ -1,11 +1,15 @@ #include #include -#include + #include -#include -#include +#include + #include +#include +#include +#include + static void ibutton_cli(Cli* cli, FuriString* args, void* context); static void onewire_cli(Cli* cli, FuriString* args, void* context); @@ -22,7 +26,7 @@ void ibutton_on_system_start() { #endif } -void ibutton_cli_print_usage() { +static void ibutton_cli_print_usage() { printf("Usage:\r\n"); printf("ikey read\r\n"); printf("ikey emulate \r\n"); @@ -34,30 +38,52 @@ void ibutton_cli_print_usage() { printf("\t are hex-formatted\r\n"); }; -bool ibutton_cli_get_key_type(FuriString* data, iButtonKeyType* type) { +static bool ibutton_cli_parse_key(iButtonProtocols* protocols, iButtonKey* key, FuriString* args) { bool result = false; + FuriString* name = furi_string_alloc(); - if(furi_string_cmp_str(data, "Dallas") == 0 || furi_string_cmp_str(data, "dallas") == 0) { - result = true; - *type = iButtonKeyDS1990; - } else if(furi_string_cmp_str(data, "Cyfral") == 0 || furi_string_cmp_str(data, "cyfral") == 0) { - result = true; - *type = iButtonKeyCyfral; - } else if(furi_string_cmp_str(data, "Metakom") == 0 || furi_string_cmp_str(data, "metakom") == 0) { - result = true; - *type = iButtonKeyMetakom; - } + do { + // Read protocol name + if(!args_read_string_and_trim(args, name)) break; + // Make the protocol name uppercase + const char first = furi_string_get_char(name, 0); + furi_string_set_char(name, 0, toupper((int)first)); + + const iButtonProtocolId id = + ibutton_protocols_get_id_by_name(protocols, furi_string_get_cstr(name)); + if(id == iButtonProtocolIdInvalid) break; + + ibutton_key_set_protocol_id(key, id); + + // Get the data pointer + iButtonEditableData data; + ibutton_protocols_get_editable_data(protocols, key, &data); + + // Read data + if(!args_read_hex_bytes(args, data.ptr, data.size)) break; + + result = true; + } while(false); + + furi_string_free(name); return result; } -void ibutton_cli_print_key_data(iButtonKey* key) { - const uint8_t* key_data = ibutton_key_get_data_p(key); - iButtonKeyType type = ibutton_key_get_type(key); +static void ibutton_cli_print_key(iButtonProtocols* protocols, iButtonKey* key) { + const char* name = ibutton_protocols_get_name(protocols, ibutton_key_get_protocol_id(key)); - printf("%s ", ibutton_key_get_string_by_type(type)); - for(size_t i = 0; i < ibutton_key_get_size_by_type(type); i++) { - printf("%02X", key_data[i]); + if(strncmp(name, "DS", 2) == 0) { + name = "Dallas"; + } + + printf("%s ", name); + + iButtonEditableData data; + ibutton_protocols_get_editable_data(protocols, key, &data); + + for(size_t i = 0; i < data.size; i++) { + printf("%02X", data.ptr[i]); } printf("\r\n"); @@ -71,9 +97,10 @@ static void ibutton_cli_worker_read_cb(void* context) { furi_event_flag_set(event, EVENT_FLAG_IBUTTON_COMPLETE); } -void ibutton_cli_read(Cli* cli) { - iButtonKey* key = ibutton_key_alloc(); - iButtonWorker* worker = ibutton_worker_alloc(); +static void ibutton_cli_read(Cli* cli) { + iButtonProtocols* protocols = ibutton_protocols_alloc(); + iButtonWorker* worker = ibutton_worker_alloc(protocols); + iButtonKey* key = ibutton_key_alloc(ibutton_protocols_get_max_data_size(protocols)); FuriEventFlag* event = furi_event_flag_alloc(); ibutton_worker_start_thread(worker); @@ -81,32 +108,25 @@ void ibutton_cli_read(Cli* cli) { printf("Reading iButton...\r\nPress Ctrl+C to abort\r\n"); ibutton_worker_read_start(worker, key); + while(true) { uint32_t flags = furi_event_flag_wait(event, EVENT_FLAG_IBUTTON_COMPLETE, FuriFlagWaitAny, 100); if(flags & EVENT_FLAG_IBUTTON_COMPLETE) { - ibutton_cli_print_key_data(key); - - if(ibutton_key_get_type(key) == iButtonKeyDS1990) { - if(!ibutton_key_dallas_crc_is_valid(key)) { - printf("Warning: invalid CRC\r\n"); - } - - if(!ibutton_key_dallas_is_1990_key(key)) { - printf("Warning: not a key\r\n"); - } - } + ibutton_cli_print_key(protocols, key); break; } if(cli_cmd_interrupt_received(cli)) break; } - ibutton_worker_stop(worker); + ibutton_worker_stop(worker); ibutton_worker_stop_thread(worker); - ibutton_worker_free(worker); + ibutton_key_free(key); + ibutton_worker_free(worker); + ibutton_protocols_free(protocols); furi_event_flag_free(event); }; @@ -124,48 +144,33 @@ static void ibutton_cli_worker_write_cb(void* context, iButtonWorkerWriteResult } void ibutton_cli_write(Cli* cli, FuriString* args) { - iButtonKey* key = ibutton_key_alloc(); - iButtonWorker* worker = ibutton_worker_alloc(); - iButtonKeyType type; - iButtonWriteContext write_context; - uint8_t key_data[IBUTTON_KEY_DATA_SIZE]; - FuriString* data; + iButtonProtocols* protocols = ibutton_protocols_alloc(); + iButtonWorker* worker = ibutton_worker_alloc(protocols); + iButtonKey* key = ibutton_key_alloc(ibutton_protocols_get_max_data_size(protocols)); + iButtonWriteContext write_context; write_context.event = furi_event_flag_alloc(); - data = furi_string_alloc(); ibutton_worker_start_thread(worker); ibutton_worker_write_set_callback(worker, ibutton_cli_worker_write_cb, &write_context); do { - if(!args_read_string_and_trim(args, data)) { + if(!ibutton_cli_parse_key(protocols, key, args)) { ibutton_cli_print_usage(); break; } - if(!ibutton_cli_get_key_type(data, &type)) { + if(!(ibutton_protocols_get_features(protocols, ibutton_key_get_protocol_id(key)) & + iButtonProtocolFeatureWriteBlank)) { ibutton_cli_print_usage(); break; } - if(type != iButtonKeyDS1990) { - ibutton_cli_print_usage(); - break; - } - - if(!args_read_hex_bytes(args, key_data, ibutton_key_get_size_by_type(type))) { - ibutton_cli_print_usage(); - break; - } - - ibutton_key_set_type(key, type); - ibutton_key_set_data(key, key_data, ibutton_key_get_size_by_type(type)); - printf("Writing key "); - ibutton_cli_print_key_data(key); + ibutton_cli_print_key(protocols, key); printf("Press Ctrl+C to abort\r\n"); - ibutton_worker_write_start(worker, key); + ibutton_worker_write_blank_start(worker, key); while(true) { uint32_t flags = furi_event_flag_wait( write_context.event, EVENT_FLAG_IBUTTON_COMPLETE, FuriFlagWaitAny, 100); @@ -183,64 +188,53 @@ void ibutton_cli_write(Cli* cli, FuriString* args) { if(cli_cmd_interrupt_received(cli)) break; } - ibutton_worker_stop(worker); } while(false); - furi_string_free(data); + ibutton_worker_stop(worker); ibutton_worker_stop_thread(worker); - ibutton_worker_free(worker); + ibutton_key_free(key); + ibutton_worker_free(worker); + ibutton_protocols_free(protocols); furi_event_flag_free(write_context.event); -}; +} void ibutton_cli_emulate(Cli* cli, FuriString* args) { - iButtonKey* key = ibutton_key_alloc(); - iButtonWorker* worker = ibutton_worker_alloc(); - iButtonKeyType type; - uint8_t key_data[IBUTTON_KEY_DATA_SIZE]; - FuriString* data; + iButtonProtocols* protocols = ibutton_protocols_alloc(); + iButtonWorker* worker = ibutton_worker_alloc(protocols); + iButtonKey* key = ibutton_key_alloc(ibutton_protocols_get_max_data_size(protocols)); - data = furi_string_alloc(); ibutton_worker_start_thread(worker); do { - if(!args_read_string_and_trim(args, data)) { + if(!ibutton_cli_parse_key(protocols, key, args)) { ibutton_cli_print_usage(); break; } - if(!ibutton_cli_get_key_type(data, &type)) { - ibutton_cli_print_usage(); - break; - } - - if(!args_read_hex_bytes(args, key_data, ibutton_key_get_size_by_type(type))) { - ibutton_cli_print_usage(); - break; - } - - ibutton_key_set_type(key, type); - ibutton_key_set_data(key, key_data, ibutton_key_get_size_by_type(type)); - printf("Emulating key "); - ibutton_cli_print_key_data(key); + ibutton_cli_print_key(protocols, key); printf("Press Ctrl+C to abort\r\n"); ibutton_worker_emulate_start(worker, key); + while(!cli_cmd_interrupt_received(cli)) { furi_delay_ms(100); }; - ibutton_worker_stop(worker); + } while(false); - furi_string_free(data); + ibutton_worker_stop(worker); ibutton_worker_stop_thread(worker); - ibutton_worker_free(worker); + ibutton_key_free(key); + ibutton_worker_free(worker); + ibutton_protocols_free(protocols); }; -static void ibutton_cli(Cli* cli, FuriString* args, void* context) { +void ibutton_cli(Cli* cli, FuriString* args, void* context) { + UNUSED(cli); UNUSED(context); FuriString* cmd; cmd = furi_string_alloc(); @@ -264,14 +258,14 @@ static void ibutton_cli(Cli* cli, FuriString* args, void* context) { furi_string_free(cmd); } -void onewire_cli_print_usage() { +static void onewire_cli_print_usage() { printf("Usage:\r\n"); printf("onewire search\r\n"); }; static void onewire_cli_search(Cli* cli) { UNUSED(cli); - OneWireHost* onewire = onewire_host_alloc(); + OneWireHost* onewire = onewire_host_alloc(&ibutton_gpio); uint8_t address[8]; bool done = false; @@ -281,7 +275,7 @@ static void onewire_cli_search(Cli* cli) { furi_hal_power_enable_otg(); while(!done) { - if(onewire_host_search(onewire, address, NORMAL_SEARCH) != 1) { + if(onewire_host_search(onewire, address, OneWireHostSearchModeNormal) != 1) { printf("Search finished\r\n"); onewire_host_reset_search(onewire); done = true; diff --git a/applications/main/ibutton/ibutton_custom_event.h b/applications/main/ibutton/ibutton_custom_event.h index 1e2f0300d..28bcb94a0 100644 --- a/applications/main/ibutton/ibutton_custom_event.h +++ b/applications/main/ibutton/ibutton_custom_event.h @@ -6,6 +6,7 @@ enum iButtonCustomEvent { iButtonCustomEventBack, iButtonCustomEventTextEditResult, + iButtonCustomEventByteEditChanged, iButtonCustomEventByteEditResult, iButtonCustomEventWorkerEmulated, iButtonCustomEventWorkerRead, diff --git a/applications/main/ibutton/ibutton_i.h b/applications/main/ibutton/ibutton_i.h index 0a8099351..8ad0b90e4 100644 --- a/applications/main/ibutton/ibutton_i.h +++ b/applications/main/ibutton/ibutton_i.h @@ -4,31 +4,40 @@ #include #include -#include -#include #include -#include +#include #include +#include + +#include #include #include +#include +#include #include #include -#include #include #include #include +#include + +#include #include "ibutton_custom_event.h" #include "scenes/ibutton_scene.h" -#define IBUTTON_FILE_NAME_SIZE 100 -#define IBUTTON_TEXT_STORE_SIZE 128 - #define IBUTTON_APP_FOLDER ANY_PATH("ibutton") #define IBUTTON_APP_EXTENSION ".ibtn" -#define IBUTTON_APP_FILE_TYPE "Flipper iButton key" + +#define IBUTTON_KEY_NAME_SIZE 22 + +typedef enum { + iButtonWriteModeInvalid, + iButtonWriteModeBlank, + iButtonWriteModeCopy, +} iButtonWriteMode; struct iButton { SceneManager* scene_manager; @@ -38,21 +47,22 @@ struct iButton { Storage* storage; DialogsApp* dialogs; NotificationApp* notifications; + RpcAppSystem* rpc; - iButtonWorker* key_worker; iButtonKey* key; + iButtonWorker* worker; + iButtonProtocols* protocols; + iButtonWriteMode write_mode; FuriString* file_path; - char text_store[IBUTTON_TEXT_STORE_SIZE + 1]; + char key_name[IBUTTON_KEY_NAME_SIZE + 1]; Submenu* submenu; ByteInput* byte_input; TextInput* text_input; Popup* popup; Widget* widget; - DialogEx* dialog_ex; - - void* rpc_ctx; + Loading* loading; }; typedef enum { @@ -61,7 +71,7 @@ typedef enum { iButtonViewTextInput, iButtonViewPopup, iButtonViewWidget, - iButtonViewDialogEx, + iButtonViewLoading, } iButtonView; typedef enum { @@ -78,10 +88,12 @@ typedef enum { iButtonNotificationMessageBlinkStop, } iButtonNotificationMessage; -bool ibutton_file_select(iButton* ibutton); -bool ibutton_load_key_data(iButton* ibutton, FuriString* key_path, bool show_dialog); -bool ibutton_save_key(iButton* ibutton, const char* key_name); +bool ibutton_select_and_load_key(iButton* ibutton); +bool ibutton_load_key(iButton* ibutton); +bool ibutton_save_key(iButton* ibutton); bool ibutton_delete_key(iButton* ibutton); -void ibutton_text_store_set(iButton* ibutton, const char* text, ...); -void ibutton_text_store_clear(iButton* ibutton); +void ibutton_reset_key(iButton* ibutton); void ibutton_notification_message(iButton* ibutton, uint32_t message); + +void ibutton_submenu_callback(void* context, uint32_t index); +void ibutton_widget_callback(GuiButtonType result, InputType type, void* context); diff --git a/applications/main/ibutton/scenes/ibutton_scene_add_type.c b/applications/main/ibutton/scenes/ibutton_scene_add_type.c index 38373999c..55e05f20d 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_add_type.c +++ b/applications/main/ibutton/scenes/ibutton_scene_add_type.c @@ -1,54 +1,57 @@ #include "../ibutton_i.h" -enum SubmenuIndex { - SubmenuIndexCyfral, - SubmenuIndexDallas, - SubmenuIndexMetakom, -}; - -void ibutton_scene_add_type_submenu_callback(void* context, uint32_t index) { - iButton* ibutton = context; - view_dispatcher_send_custom_event(ibutton->view_dispatcher, index); -} - void ibutton_scene_add_type_on_enter(void* context) { iButton* ibutton = context; Submenu* submenu = ibutton->submenu; - submenu_add_item( - submenu, "Cyfral", SubmenuIndexCyfral, ibutton_scene_add_type_submenu_callback, ibutton); - submenu_add_item( - submenu, "Dallas", SubmenuIndexDallas, ibutton_scene_add_type_submenu_callback, ibutton); - submenu_add_item( - submenu, "Metakom", SubmenuIndexMetakom, ibutton_scene_add_type_submenu_callback, ibutton); + FuriString* tmp = furi_string_alloc(); - submenu_set_selected_item( - submenu, scene_manager_get_scene_state(ibutton->scene_manager, iButtonSceneAddType)); + for(uint32_t protocol_id = 0; protocol_id < ibutton_protocols_get_protocol_count(); + ++protocol_id) { + if((strcmp( + ibutton_protocols_get_manufacturer(ibutton->protocols, protocol_id), + ibutton_protocols_get_name(ibutton->protocols, protocol_id)) != 0) && + (strcmp(ibutton_protocols_get_manufacturer(ibutton->protocols, protocol_id), "N/A") != + 0)) { + furi_string_printf( + tmp, + "%s %s", + ibutton_protocols_get_manufacturer(ibutton->protocols, protocol_id), + ibutton_protocols_get_name(ibutton->protocols, protocol_id)); + } else { + furi_string_printf( + tmp, "%s", ibutton_protocols_get_name(ibutton->protocols, protocol_id)); + } + + submenu_add_item( + submenu, furi_string_get_cstr(tmp), protocol_id, ibutton_submenu_callback, context); + } + + const uint32_t prev_protocol_id = + scene_manager_get_scene_state(ibutton->scene_manager, iButtonSceneAddType); + submenu_set_selected_item(submenu, prev_protocol_id); view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewSubmenu); + furi_string_free(tmp); } bool ibutton_scene_add_type_on_event(void* context, SceneManagerEvent event) { iButton* ibutton = context; iButtonKey* key = ibutton->key; + bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { - scene_manager_set_scene_state(ibutton->scene_manager, iButtonSceneAddType, event.event); - consumed = true; - if(event.event == SubmenuIndexCyfral) { - ibutton_key_set_type(key, iButtonKeyCyfral); - } else if(event.event == SubmenuIndexDallas) { - ibutton_key_set_type(key, iButtonKeyDS1990); - } else if(event.event == SubmenuIndexMetakom) { - ibutton_key_set_type(key, iButtonKeyMetakom); - } else { - furi_crash("Unknown key type"); - } + const iButtonProtocolId protocol_id = event.event; - furi_string_set(ibutton->file_path, IBUTTON_APP_FOLDER); - ibutton_key_clear_data(key); + ibutton_key_reset(key); + ibutton_key_set_protocol_id(key, protocol_id); + ibutton_protocols_apply_edits(ibutton->protocols, key); + + scene_manager_set_scene_state(ibutton->scene_manager, iButtonSceneAddType, protocol_id); scene_manager_next_scene(ibutton->scene_manager, iButtonSceneAddValue); + + consumed = true; } return consumed; diff --git a/applications/main/ibutton/scenes/ibutton_scene_add_value.c b/applications/main/ibutton/scenes/ibutton_scene_add_value.c index ccac76121..dc340771b 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_add_value.c +++ b/applications/main/ibutton/scenes/ibutton_scene_add_value.c @@ -1,42 +1,52 @@ #include "../ibutton_i.h" -void ibutton_scene_add_type_byte_input_callback(void* context) { +static void ibutton_scene_add_type_byte_input_callback(void* context) { iButton* ibutton = context; view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventByteEditResult); } +static void ibutton_scene_add_type_byte_changed_callback(void* context) { + iButton* ibutton = context; + view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventByteEditChanged); +} + void ibutton_scene_add_value_on_enter(void* context) { iButton* ibutton = context; - iButtonKey* key = ibutton->key; - uint8_t* new_key_data = malloc(IBUTTON_KEY_DATA_SIZE); + byte_input_set_header_text(ibutton->byte_input, "Enter the key"); - scene_manager_set_scene_state( - ibutton->scene_manager, iButtonSceneAddValue, (uint32_t)new_key_data); - memcpy(new_key_data, ibutton_key_get_data_p(key), ibutton_key_get_data_size(key)); + iButtonEditableData editable_data; + ibutton_protocols_get_editable_data(ibutton->protocols, ibutton->key, &editable_data); byte_input_set_result_callback( ibutton->byte_input, ibutton_scene_add_type_byte_input_callback, - NULL, - ibutton, - new_key_data, - ibutton_key_get_data_size(key)); + ibutton_scene_add_type_byte_changed_callback, + context, + editable_data.ptr, + editable_data.size); - byte_input_set_header_text(ibutton->byte_input, "Enter the key"); view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewByteInput); } bool ibutton_scene_add_value_on_event(void* context, SceneManagerEvent event) { iButton* ibutton = context; - uint8_t* new_key_data = - (uint8_t*)scene_manager_get_scene_state(ibutton->scene_manager, iButtonSceneAddValue); + SceneManager* scene_manager = ibutton->scene_manager; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { consumed = true; if(event.event == iButtonCustomEventByteEditResult) { - ibutton_key_set_data(ibutton->key, new_key_data, IBUTTON_KEY_DATA_SIZE); - scene_manager_next_scene(ibutton->scene_manager, iButtonSceneSaveName); + scene_manager_next_scene(scene_manager, iButtonSceneSaveName); + } else if(event.event == iButtonCustomEventByteEditChanged) { + ibutton_protocols_apply_edits(ibutton->protocols, ibutton->key); + } + } else if(event.type == SceneManagerEventTypeBack) { + // User cancelled editing, reload the key from storage + if(scene_manager_has_previous_scene(scene_manager, iButtonSceneSavedKeyMenu)) { + if(!ibutton_load_key(ibutton)) { + consumed = scene_manager_search_and_switch_to_previous_scene( + scene_manager, iButtonSceneStart); + } } } @@ -45,10 +55,7 @@ bool ibutton_scene_add_value_on_event(void* context, SceneManagerEvent event) { void ibutton_scene_add_value_on_exit(void* context) { iButton* ibutton = context; - uint8_t* new_key_data = - (uint8_t*)scene_manager_get_scene_state(ibutton->scene_manager, iButtonSceneAddValue); byte_input_set_result_callback(ibutton->byte_input, NULL, NULL, NULL, NULL, 0); byte_input_set_header_text(ibutton->byte_input, NULL); - free(new_key_data); } diff --git a/applications/main/ibutton/scenes/ibutton_scene_config.h b/applications/main/ibutton/scenes/ibutton_scene_config.h index 87fa1a036..79f6791b3 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_config.h +++ b/applications/main/ibutton/scenes/ibutton_scene_config.h @@ -6,8 +6,7 @@ ADD_SCENE(ibutton, info, Info) ADD_SCENE(ibutton, read, Read) ADD_SCENE(ibutton, read_key_menu, ReadKeyMenu) ADD_SCENE(ibutton, read_success, ReadSuccess) -ADD_SCENE(ibutton, read_crc_error, ReadCRCError) -ADD_SCENE(ibutton, read_not_key_error, ReadNotKeyError) +ADD_SCENE(ibutton, read_error, ReadError) ADD_SCENE(ibutton, select_key, SelectKey) ADD_SCENE(ibutton, add_type, AddType) ADD_SCENE(ibutton, add_value, AddValue) @@ -18,4 +17,5 @@ ADD_SCENE(ibutton, delete_confirm, DeleteConfirm) ADD_SCENE(ibutton, delete_success, DeleteSuccess) ADD_SCENE(ibutton, retry_confirm, RetryConfirm) ADD_SCENE(ibutton, exit_confirm, ExitConfirm) +ADD_SCENE(ibutton, view_data, ViewData) ADD_SCENE(ibutton, rpc, Rpc) diff --git a/applications/main/ibutton/scenes/ibutton_scene_delete_confirm.c b/applications/main/ibutton/scenes/ibutton_scene_delete_confirm.c index 3d609e833..587cb748c 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_delete_confirm.c +++ b/applications/main/ibutton/scenes/ibutton_scene_delete_confirm.c @@ -1,74 +1,29 @@ #include "../ibutton_i.h" #include -static void ibutton_scene_delete_confirm_widget_callback( - GuiButtonType result, - InputType type, - void* context) { - iButton* ibutton = context; - if(type == InputTypeShort) { - view_dispatcher_send_custom_event(ibutton->view_dispatcher, result); - } -} - void ibutton_scene_delete_confirm_on_enter(void* context) { iButton* ibutton = context; - Widget* widget = ibutton->widget; iButtonKey* key = ibutton->key; - const uint8_t* key_data = ibutton_key_get_data_p(key); + Widget* widget = ibutton->widget; - FuriString* key_name; - key_name = furi_string_alloc(); - path_extract_filename(ibutton->file_path, key_name, true); + FuriString* tmp = furi_string_alloc(); - ibutton_text_store_set(ibutton, "\e#Delete %s?\e#", furi_string_get_cstr(key_name)); - widget_add_text_box_element( - widget, 0, 0, 128, 27, AlignCenter, AlignCenter, ibutton->text_store, true); + widget_add_button_element(widget, GuiButtonTypeLeft, "Back", ibutton_widget_callback, context); widget_add_button_element( - widget, GuiButtonTypeLeft, "Cancel", ibutton_scene_delete_confirm_widget_callback, ibutton); - widget_add_button_element( - widget, - GuiButtonTypeRight, - "Delete", - ibutton_scene_delete_confirm_widget_callback, - ibutton); + widget, GuiButtonTypeRight, "Delete", ibutton_widget_callback, context); - switch(ibutton_key_get_type(key)) { - case iButtonKeyDS1990: - ibutton_text_store_set( - ibutton, - "%02X %02X %02X %02X %02X %02X %02X %02X", - key_data[0], - key_data[1], - key_data[2], - key_data[3], - key_data[4], - key_data[5], - key_data[6], - key_data[7]); - widget_add_string_element( - widget, 64, 34, AlignCenter, AlignBottom, FontSecondary, "Dallas"); - break; - - case iButtonKeyCyfral: - ibutton_text_store_set(ibutton, "%02X %02X", key_data[0], key_data[1]); - widget_add_string_element( - widget, 64, 34, AlignCenter, AlignBottom, FontSecondary, "Cyfral"); - break; - - case iButtonKeyMetakom: - ibutton_text_store_set( - ibutton, "%02X %02X %02X %02X", key_data[0], key_data[1], key_data[2], key_data[3]); - widget_add_string_element( - widget, 64, 34, AlignCenter, AlignBottom, FontSecondary, "Metakom"); - break; - } + furi_string_printf(tmp, "Delete %s?", ibutton->key_name); widget_add_string_element( - widget, 64, 46, AlignCenter, AlignBottom, FontSecondary, ibutton->text_store); + widget, 128 / 2, 0, AlignCenter, AlignTop, FontPrimary, furi_string_get_cstr(tmp)); + + furi_string_reset(tmp); + ibutton_protocols_render_brief_data(ibutton->protocols, key, tmp); + + widget_add_string_multiline_element( + widget, 128 / 2, 16, AlignCenter, AlignTop, FontSecondary, furi_string_get_cstr(tmp)); view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget); - - furi_string_free(key_name); + furi_string_free(tmp); } bool ibutton_scene_delete_confirm_on_event(void* context, SceneManagerEvent event) { @@ -81,8 +36,10 @@ bool ibutton_scene_delete_confirm_on_event(void* context, SceneManagerEvent even if(event.event == GuiButtonTypeRight) { if(ibutton_delete_key(ibutton)) { scene_manager_next_scene(scene_manager, iButtonSceneDeleteSuccess); + } else { + dialog_message_show_storage_error(ibutton->dialogs, "Cannot delete\nkey file"); + scene_manager_previous_scene(scene_manager); } - //TODO: What if the key could not be deleted? } else if(event.event == GuiButtonTypeLeft) { scene_manager_previous_scene(scene_manager); } @@ -93,6 +50,5 @@ bool ibutton_scene_delete_confirm_on_event(void* context, SceneManagerEvent even void ibutton_scene_delete_confirm_on_exit(void* context) { iButton* ibutton = context; - ibutton_text_store_clear(ibutton); widget_reset(ibutton->widget); } diff --git a/applications/main/ibutton/scenes/ibutton_scene_emulate.c b/applications/main/ibutton/scenes/ibutton_scene_emulate.c index 6f6ffcf57..713b8331c 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_emulate.c +++ b/applications/main/ibutton/scenes/ibutton_scene_emulate.c @@ -14,61 +14,32 @@ static void ibutton_scene_emulate_callback(void* context, bool emulated) { void ibutton_scene_emulate_on_enter(void* context) { iButton* ibutton = context; - Widget* widget = ibutton->widget; iButtonKey* key = ibutton->key; - const uint8_t* key_data = ibutton_key_get_data_p(key); + Widget* widget = ibutton->widget; + FuriString* tmp = furi_string_alloc(); - FuriString* key_name; - key_name = furi_string_alloc(); - if(furi_string_end_with(ibutton->file_path, IBUTTON_APP_EXTENSION)) { - path_extract_filename(ibutton->file_path, key_name, true); - } + widget_add_icon_element(widget, 3, 10, &I_iButtonKey_49x44); - // check that stored key has name - if(!furi_string_empty(key_name)) { - ibutton_text_store_set(ibutton, "%s", furi_string_get_cstr(key_name)); - } else { - // if not, show key data - switch(ibutton_key_get_type(key)) { - case iButtonKeyDS1990: - ibutton_text_store_set( - ibutton, - "%02X %02X %02X %02X\n%02X %02X %02X %02X", - key_data[0], - key_data[1], - key_data[2], - key_data[3], - key_data[4], - key_data[5], - key_data[6], - key_data[7]); - break; - case iButtonKeyCyfral: - ibutton_text_store_set(ibutton, "%02X %02X", key_data[0], key_data[1]); - break; - case iButtonKeyMetakom: - ibutton_text_store_set( - ibutton, "%02X %02X %02X %02X", key_data[0], key_data[1], key_data[2], key_data[3]); - break; - } - } + furi_string_printf( + tmp, + "%s\n[%s]", + furi_string_empty(ibutton->file_path) ? "Unsaved Key" : ibutton->key_name, + ibutton_protocols_get_name(ibutton->protocols, ibutton_key_get_protocol_id(key))); + + widget_add_text_box_element( + widget, 52, 38, 75, 26, AlignCenter, AlignCenter, furi_string_get_cstr(tmp), true); widget_add_string_multiline_element( - widget, 90, 10, AlignCenter, AlignTop, FontPrimary, "iButton\nemulating"); - widget_add_icon_element(widget, 3, 10, &I_iButtonKey_49x44); - widget_add_text_box_element( - widget, 54, 39, 75, 22, AlignCenter, AlignCenter, ibutton->text_store, true); + widget, 88, 10, AlignCenter, AlignTop, FontPrimary, "iButton\nemulating"); - view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget); - - ibutton_worker_emulate_set_callback( - ibutton->key_worker, ibutton_scene_emulate_callback, ibutton); - ibutton_worker_emulate_start(ibutton->key_worker, key); - - furi_string_free(key_name); + ibutton_worker_emulate_set_callback(ibutton->worker, ibutton_scene_emulate_callback, ibutton); + ibutton_worker_emulate_start(ibutton->worker, key); ibutton_notification_message(ibutton, iButtonNotificationMessageEmulateStart); + view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget); + + furi_string_free(tmp); } bool ibutton_scene_emulate_on_event(void* context, SceneManagerEvent event) { @@ -78,8 +49,7 @@ bool ibutton_scene_emulate_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeTick) { uint32_t cnt = scene_manager_get_scene_state(ibutton->scene_manager, iButtonSceneEmulate); if(cnt > 0) { - cnt--; - if(cnt == 0) { + if(--cnt == 0) { ibutton_notification_message(ibutton, iButtonNotificationMessageEmulateBlink); } scene_manager_set_scene_state(ibutton->scene_manager, iButtonSceneEmulate, cnt); @@ -101,7 +71,7 @@ bool ibutton_scene_emulate_on_event(void* context, SceneManagerEvent event) { void ibutton_scene_emulate_on_exit(void* context) { iButton* ibutton = context; - ibutton_worker_stop(ibutton->key_worker); + ibutton_worker_stop(ibutton->worker); widget_reset(ibutton->widget); ibutton_notification_message(ibutton, iButtonNotificationMessageBlinkStop); } diff --git a/applications/main/ibutton/scenes/ibutton_scene_exit_confirm.c b/applications/main/ibutton/scenes/ibutton_scene_exit_confirm.c index 2367e1217..9029a4b7b 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_exit_confirm.c +++ b/applications/main/ibutton/scenes/ibutton_scene_exit_confirm.c @@ -19,7 +19,7 @@ void ibutton_scene_exit_confirm_on_enter(void* context) { widget_add_button_element( widget, GuiButtonTypeRight, "Stay", ibutton_scene_exit_confirm_widget_callback, ibutton); widget_add_string_element( - widget, 64, 19, AlignCenter, AlignBottom, FontPrimary, "Exit to iButton menu?"); + widget, 64, 19, AlignCenter, AlignBottom, FontPrimary, "Exit to iButton Menu?"); widget_add_string_element( widget, 64, 31, AlignCenter, AlignBottom, FontSecondary, "All unsaved data will be lost!"); diff --git a/applications/main/ibutton/scenes/ibutton_scene_info.c b/applications/main/ibutton/scenes/ibutton_scene_info.c index 15648f6f2..cf44d6a86 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_info.c +++ b/applications/main/ibutton/scenes/ibutton_scene_info.c @@ -1,66 +1,54 @@ #include "../ibutton_i.h" -#include void ibutton_scene_info_on_enter(void* context) { iButton* ibutton = context; - Widget* widget = ibutton->widget; iButtonKey* key = ibutton->key; + Widget* widget = ibutton->widget; - const uint8_t* key_data = ibutton_key_get_data_p(key); + const iButtonProtocolId protocol_id = ibutton_key_get_protocol_id(key); - FuriString* key_name; - key_name = furi_string_alloc(); - path_extract_filename(ibutton->file_path, key_name, true); + FuriString* tmp = furi_string_alloc(); + + furi_string_printf( + tmp, + "\e#%s [%s]\e#", + ibutton->key_name, + ibutton_protocols_get_name(ibutton->protocols, protocol_id)); - ibutton_text_store_set(ibutton, "%s", furi_string_get_cstr(key_name)); widget_add_text_box_element( - widget, 0, 0, 128, 23, AlignCenter, AlignCenter, ibutton->text_store, true); + widget, 0, 2, 128, 12, AlignLeft, AlignTop, furi_string_get_cstr(tmp), true); - switch(ibutton_key_get_type(key)) { - case iButtonKeyDS1990: - ibutton_text_store_set( - ibutton, - "%02X %02X %02X %02X %02X %02X %02X %02X", - key_data[0], - key_data[1], - key_data[2], - key_data[3], - key_data[4], - key_data[5], - key_data[6], - key_data[7]); - widget_add_string_element(widget, 64, 36, AlignCenter, AlignBottom, FontPrimary, "Dallas"); - break; + furi_string_reset(tmp); + ibutton_protocols_render_brief_data(ibutton->protocols, key, tmp); - case iButtonKeyMetakom: - ibutton_text_store_set( - ibutton, "%02X %02X %02X %02X", key_data[0], key_data[1], key_data[2], key_data[3]); - widget_add_string_element( - widget, 64, 36, AlignCenter, AlignBottom, FontPrimary, "Metakom"); - break; + widget_add_string_multiline_element( + widget, 0, 16, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(tmp)); - case iButtonKeyCyfral: - ibutton_text_store_set(ibutton, "%02X %02X", key_data[0], key_data[1]); - widget_add_string_element(widget, 64, 36, AlignCenter, AlignBottom, FontPrimary, "Cyfral"); - break; + if(ibutton_protocols_get_features(ibutton->protocols, protocol_id) & + iButtonProtocolFeatureExtData) { + widget_add_button_element( + widget, GuiButtonTypeRight, "More", ibutton_widget_callback, context); } - widget_add_string_element( - widget, 64, 50, AlignCenter, AlignBottom, FontSecondary, ibutton->text_store); - view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget); - - furi_string_free(key_name); + furi_string_free(tmp); } bool ibutton_scene_info_on_event(void* context, SceneManagerEvent event) { - UNUSED(context); - UNUSED(event); - return false; + iButton* ibutton = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + if(event.event == GuiButtonTypeRight) { + scene_manager_next_scene(ibutton->scene_manager, iButtonSceneViewData); + } + } + + return consumed; } void ibutton_scene_info_on_exit(void* context) { iButton* ibutton = context; - ibutton_text_store_clear(ibutton); widget_reset(ibutton->widget); } diff --git a/applications/main/ibutton/scenes/ibutton_scene_read.c b/applications/main/ibutton/scenes/ibutton_scene_read.c index 1ccd2562b..a2b3b53e4 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_read.c +++ b/applications/main/ibutton/scenes/ibutton_scene_read.c @@ -11,14 +11,13 @@ void ibutton_scene_read_on_enter(void* context) { iButton* ibutton = context; Popup* popup = ibutton->popup; iButtonKey* key = ibutton->key; - iButtonWorker* worker = ibutton->key_worker; + iButtonWorker* worker = ibutton->worker; popup_set_header(popup, "iButton", 95, 26, AlignCenter, AlignBottom); - popup_set_text(popup, "Waiting\nfor key ...", 95, 30, AlignCenter, AlignTop); + popup_set_text(popup, "Apply key to\nFlipper's back", 95, 30, AlignCenter, AlignTop); popup_set_icon(popup, 0, 5, XTREME_ASSETS()->I_DolphinWait_61x59); view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewPopup); - furi_string_set(ibutton->file_path, IBUTTON_APP_FOLDER); ibutton_worker_read_set_callback(worker, ibutton_scene_read_callback, ibutton); ibutton_worker_read_start(worker, key); @@ -36,25 +35,14 @@ bool ibutton_scene_read_on_event(void* context, SceneManagerEvent event) { } else if(event.type == SceneManagerEventTypeCustom) { consumed = true; if(event.event == iButtonCustomEventWorkerRead) { - bool success = false; - iButtonKey* key = ibutton->key; - - if(ibutton_key_get_type(key) == iButtonKeyDS1990) { - if(!ibutton_key_dallas_crc_is_valid(key)) { - scene_manager_next_scene(scene_manager, iButtonSceneReadCRCError); - } else if(!ibutton_key_dallas_is_1990_key(key)) { - scene_manager_next_scene(scene_manager, iButtonSceneReadNotKeyError); - } else { - success = true; - } - } else { - success = true; - } - - if(success) { + if(ibutton_protocols_is_valid(ibutton->protocols, ibutton->key)) { ibutton_notification_message(ibutton, iButtonNotificationMessageSuccess); scene_manager_next_scene(scene_manager, iButtonSceneReadSuccess); + DOLPHIN_DEED(DolphinDeedIbuttonReadSuccess); + + } else { + scene_manager_next_scene(scene_manager, iButtonSceneReadError); } } } @@ -65,7 +53,7 @@ bool ibutton_scene_read_on_event(void* context, SceneManagerEvent event) { void ibutton_scene_read_on_exit(void* context) { iButton* ibutton = context; Popup* popup = ibutton->popup; - ibutton_worker_stop(ibutton->key_worker); + ibutton_worker_stop(ibutton->worker); popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom); popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); popup_set_icon(popup, 0, 0, NULL); diff --git a/applications/main/ibutton/scenes/ibutton_scene_read_crc_error.c b/applications/main/ibutton/scenes/ibutton_scene_read_crc_error.c deleted file mode 100644 index f822ff6a2..000000000 --- a/applications/main/ibutton/scenes/ibutton_scene_read_crc_error.c +++ /dev/null @@ -1,70 +0,0 @@ -#include "../ibutton_i.h" -#include - -static void ibutton_scene_read_crc_error_dialog_ex_callback(DialogExResult result, void* context) { - iButton* ibutton = context; - view_dispatcher_send_custom_event(ibutton->view_dispatcher, result); -} - -void ibutton_scene_read_crc_error_on_enter(void* context) { - iButton* ibutton = context; - DialogEx* dialog_ex = ibutton->dialog_ex; - iButtonKey* key = ibutton->key; - const uint8_t* key_data = ibutton_key_get_data_p(key); - - ibutton_text_store_set( - ibutton, - "%02X %02X %02X %02X %02X %02X %02X %02X\nExpected CRC: %X", - key_data[0], - key_data[1], - key_data[2], - key_data[3], - key_data[4], - key_data[5], - key_data[6], - key_data[7], - maxim_crc8(key_data, 7, MAXIM_CRC8_INIT)); - - dialog_ex_set_header(dialog_ex, "CRC ERROR", 64, 10, AlignCenter, AlignCenter); - dialog_ex_set_text(dialog_ex, ibutton->text_store, 64, 19, AlignCenter, AlignTop); - dialog_ex_set_left_button_text(dialog_ex, "Retry"); - dialog_ex_set_right_button_text(dialog_ex, "More"); - dialog_ex_set_result_callback(dialog_ex, ibutton_scene_read_crc_error_dialog_ex_callback); - dialog_ex_set_context(dialog_ex, ibutton); - - view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewDialogEx); - - ibutton_notification_message(ibutton, iButtonNotificationMessageError); - ibutton_notification_message(ibutton, iButtonNotificationMessageRedOn); -} - -bool ibutton_scene_read_crc_error_on_event(void* context, SceneManagerEvent event) { - iButton* ibutton = context; - SceneManager* scene_manager = ibutton->scene_manager; - bool consumed = false; - - if(event.type == SceneManagerEventTypeBack) { - consumed = true; - scene_manager_next_scene(scene_manager, iButtonSceneExitConfirm); - } else if(event.type == SceneManagerEventTypeCustom) { - consumed = true; - if(event.event == DialogExResultRight) { - scene_manager_next_scene(scene_manager, iButtonSceneReadKeyMenu); - } else if(event.event == DialogExResultLeft) { - scene_manager_previous_scene(scene_manager); - } - } - - return consumed; -} - -void ibutton_scene_read_crc_error_on_exit(void* context) { - iButton* ibutton = context; - DialogEx* dialog_ex = ibutton->dialog_ex; - - ibutton_text_store_clear(ibutton); - - dialog_ex_reset(dialog_ex); - - ibutton_notification_message(ibutton, iButtonNotificationMessageRedOff); -} diff --git a/applications/main/ibutton/scenes/ibutton_scene_read_error.c b/applications/main/ibutton/scenes/ibutton_scene_read_error.c new file mode 100644 index 000000000..e966384bf --- /dev/null +++ b/applications/main/ibutton/scenes/ibutton_scene_read_error.c @@ -0,0 +1,58 @@ +#include "../ibutton_i.h" +#include + +void ibutton_scene_read_error_on_enter(void* context) { + iButton* ibutton = context; + iButtonKey* key = ibutton->key; + + Widget* widget = ibutton->widget; + + FuriString* tmp = furi_string_alloc(); + + widget_add_button_element( + widget, GuiButtonTypeLeft, "Retry", ibutton_widget_callback, context); + widget_add_button_element( + widget, GuiButtonTypeRight, "More", ibutton_widget_callback, context); + + widget_add_string_element( + widget, 128 / 2, 2, AlignCenter, AlignTop, FontPrimary, "Read Error"); + + ibutton_protocols_render_error(ibutton->protocols, key, tmp); + + widget_add_string_multiline_element( + widget, 128 / 2, 16, AlignCenter, AlignTop, FontSecondary, furi_string_get_cstr(tmp)); + + ibutton_notification_message(ibutton, iButtonNotificationMessageError); + ibutton_notification_message(ibutton, iButtonNotificationMessageRedOn); + + view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget); + furi_string_free(tmp); +} + +bool ibutton_scene_read_error_on_event(void* context, SceneManagerEvent event) { + iButton* ibutton = context; + SceneManager* scene_manager = ibutton->scene_manager; + bool consumed = false; + + if(event.type == SceneManagerEventTypeBack) { + consumed = true; + scene_manager_next_scene(scene_manager, iButtonSceneExitConfirm); + + } else if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + if(event.event == GuiButtonTypeLeft) { + scene_manager_previous_scene(scene_manager); + } else if(event.event == GuiButtonTypeRight) { + scene_manager_next_scene(scene_manager, iButtonSceneReadKeyMenu); + } + } + + return consumed; +} + +void ibutton_scene_read_error_on_exit(void* context) { + iButton* ibutton = context; + + ibutton_notification_message(ibutton, iButtonNotificationMessageRedOff); + widget_reset(ibutton->widget); +} diff --git a/applications/main/ibutton/scenes/ibutton_scene_read_key_menu.c b/applications/main/ibutton/scenes/ibutton_scene_read_key_menu.c index 0a8ecfa55..716f72c7d 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_read_key_menu.c +++ b/applications/main/ibutton/scenes/ibutton_scene_read_key_menu.c @@ -4,7 +4,9 @@ typedef enum { SubmenuIndexSave, SubmenuIndexEmulate, - SubmenuIndexWrite, + SubmenuIndexViewData, + SubmenuIndexWriteBlank, + SubmenuIndexWriteCopy, } SubmenuIndex; void ibutton_scene_read_key_menu_submenu_callback(void* context, uint32_t index) { @@ -16,6 +18,9 @@ void ibutton_scene_read_key_menu_on_enter(void* context) { iButton* ibutton = context; Submenu* submenu = ibutton->submenu; + const iButtonProtocolId protocol_id = ibutton_key_get_protocol_id(ibutton->key); + const uint32_t features = ibutton_protocols_get_features(ibutton->protocols, protocol_id); + submenu_add_item( submenu, "Save", SubmenuIndexSave, ibutton_scene_read_key_menu_submenu_callback, ibutton); submenu_add_item( @@ -24,36 +29,66 @@ void ibutton_scene_read_key_menu_on_enter(void* context) { SubmenuIndexEmulate, ibutton_scene_read_key_menu_submenu_callback, ibutton); - if(ibutton_key_get_type(ibutton->key) == iButtonKeyDS1990) { + + if(features & iButtonProtocolFeatureExtData) { submenu_add_item( submenu, - "Write", - SubmenuIndexWrite, + "View Data", + SubmenuIndexViewData, ibutton_scene_read_key_menu_submenu_callback, ibutton); } + + if(features & iButtonProtocolFeatureWriteBlank) { + submenu_add_item( + submenu, + "Write Blank", + SubmenuIndexWriteBlank, + ibutton_scene_read_key_menu_submenu_callback, + ibutton); + } + + if(features & iButtonProtocolFeatureWriteCopy) { + submenu_add_item( + submenu, + "Write Copy", + SubmenuIndexWriteCopy, + ibutton_scene_read_key_menu_submenu_callback, + ibutton); + } + submenu_set_selected_item( submenu, scene_manager_get_scene_state(ibutton->scene_manager, iButtonSceneReadKeyMenu)); - view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewSubmenu); } bool ibutton_scene_read_key_menu_on_event(void* context, SceneManagerEvent event) { iButton* ibutton = context; + SceneManager* scene_manager = ibutton->scene_manager; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { - scene_manager_set_scene_state( - ibutton->scene_manager, iButtonSceneReadKeyMenu, event.event); + scene_manager_set_scene_state(scene_manager, iButtonSceneReadKeyMenu, event.event); consumed = true; + if(event.event == SubmenuIndexSave) { - scene_manager_next_scene(ibutton->scene_manager, iButtonSceneSaveName); + scene_manager_next_scene(scene_manager, iButtonSceneSaveName); } else if(event.event == SubmenuIndexEmulate) { - scene_manager_next_scene(ibutton->scene_manager, iButtonSceneEmulate); + scene_manager_next_scene(scene_manager, iButtonSceneEmulate); DOLPHIN_DEED(DolphinDeedIbuttonEmulate); - } else if(event.event == SubmenuIndexWrite) { - scene_manager_next_scene(ibutton->scene_manager, iButtonSceneWrite); + } else if(event.event == SubmenuIndexViewData) { + scene_manager_next_scene(scene_manager, iButtonSceneViewData); + } else if(event.event == SubmenuIndexWriteBlank) { + ibutton->write_mode = iButtonWriteModeBlank; + scene_manager_next_scene(scene_manager, iButtonSceneWrite); + } else if(event.event == SubmenuIndexWriteCopy) { + ibutton->write_mode = iButtonWriteModeCopy; + scene_manager_next_scene(scene_manager, iButtonSceneWrite); } + } else if(event.event == SceneManagerEventTypeBack) { + scene_manager_set_scene_state( + ibutton->scene_manager, iButtonSceneReadKeyMenu, SubmenuIndexSave); + // Event is not consumed } return consumed; diff --git a/applications/main/ibutton/scenes/ibutton_scene_read_not_key_error.c b/applications/main/ibutton/scenes/ibutton_scene_read_not_key_error.c deleted file mode 100644 index 8a7528031..000000000 --- a/applications/main/ibutton/scenes/ibutton_scene_read_not_key_error.c +++ /dev/null @@ -1,71 +0,0 @@ -#include "../ibutton_i.h" -#include - -static void - ibutton_scene_read_not_key_error_dialog_ex_callback(DialogExResult result, void* context) { - iButton* ibutton = context; - view_dispatcher_send_custom_event(ibutton->view_dispatcher, result); -} - -void ibutton_scene_read_not_key_error_on_enter(void* context) { - iButton* ibutton = context; - DialogEx* dialog_ex = ibutton->dialog_ex; - iButtonKey* key = ibutton->key; - const uint8_t* key_data = ibutton_key_get_data_p(key); - - ibutton_text_store_set( - ibutton, - "THIS IS NOT A KEY\n%02X %02X %02X %02X %02X %02X %02X %02X", - key_data[0], - key_data[1], - key_data[2], - key_data[3], - key_data[4], - key_data[5], - key_data[6], - key_data[7], - maxim_crc8(key_data, 7, MAXIM_CRC8_INIT)); - - dialog_ex_set_header(dialog_ex, "CRC ERROR", 64, 10, AlignCenter, AlignCenter); - dialog_ex_set_text(dialog_ex, ibutton->text_store, 64, 19, AlignCenter, AlignTop); - dialog_ex_set_left_button_text(dialog_ex, "Retry"); - dialog_ex_set_right_button_text(dialog_ex, "More"); - dialog_ex_set_result_callback(dialog_ex, ibutton_scene_read_not_key_error_dialog_ex_callback); - dialog_ex_set_context(dialog_ex, ibutton); - - view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewDialogEx); - - ibutton_notification_message(ibutton, iButtonNotificationMessageError); - ibutton_notification_message(ibutton, iButtonNotificationMessageRedOn); -} - -bool ibutton_scene_read_not_key_error_on_event(void* context, SceneManagerEvent event) { - iButton* ibutton = context; - SceneManager* scene_manager = ibutton->scene_manager; - bool consumed = false; - - if(event.type == SceneManagerEventTypeBack) { - consumed = true; - scene_manager_next_scene(scene_manager, iButtonSceneExitConfirm); - } else if(event.type == SceneManagerEventTypeCustom) { - consumed = true; - if(event.event == DialogExResultRight) { - scene_manager_next_scene(scene_manager, iButtonSceneReadKeyMenu); - } else if(event.event == DialogExResultLeft) { - scene_manager_previous_scene(scene_manager); - } - } - - return consumed; -} - -void ibutton_scene_read_not_key_error_on_exit(void* context) { - iButton* ibutton = context; - DialogEx* dialog_ex = ibutton->dialog_ex; - - ibutton_text_store_clear(ibutton); - - dialog_ex_reset(dialog_ex); - - ibutton_notification_message(ibutton, iButtonNotificationMessageRedOff); -} diff --git a/applications/main/ibutton/scenes/ibutton_scene_read_success.c b/applications/main/ibutton/scenes/ibutton_scene_read_success.c index 749e7af37..2e50bc996 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_read_success.c +++ b/applications/main/ibutton/scenes/ibutton_scene_read_success.c @@ -1,55 +1,40 @@ #include "../ibutton_i.h" -#include -static void ibutton_scene_read_success_dialog_ex_callback(DialogExResult result, void* context) { - iButton* ibutton = context; - view_dispatcher_send_custom_event(ibutton->view_dispatcher, result); -} +#include void ibutton_scene_read_success_on_enter(void* context) { iButton* ibutton = context; - DialogEx* dialog_ex = ibutton->dialog_ex; iButtonKey* key = ibutton->key; - const uint8_t* key_data = ibutton_key_get_data_p(key); + Widget* widget = ibutton->widget; - switch(ibutton_key_get_type(key)) { - case iButtonKeyDS1990: - ibutton_text_store_set( - ibutton, - "Dallas\n%02X %02X %02X %02X\n%02X %02X %02X %02X", - key_data[0], - key_data[1], - key_data[2], - key_data[3], - key_data[4], - key_data[5], - key_data[6], - key_data[7]); - break; - case iButtonKeyCyfral: - ibutton_text_store_set(ibutton, "Cyfral\n%02X %02X", key_data[0], key_data[1]); - break; - case iButtonKeyMetakom: - ibutton_text_store_set( - ibutton, - "Metakom\n%02X %02X %02X %02X", - key_data[0], - key_data[1], - key_data[2], - key_data[3]); - break; - } + FuriString* tmp = furi_string_alloc(); - dialog_ex_set_text(dialog_ex, ibutton->text_store, 95, 30, AlignCenter, AlignCenter); - dialog_ex_set_left_button_text(dialog_ex, "Retry"); - dialog_ex_set_right_button_text(dialog_ex, "More"); - dialog_ex_set_icon(dialog_ex, 0, 1, &I_DolphinReadingSuccess_59x63); - dialog_ex_set_result_callback(dialog_ex, ibutton_scene_read_success_dialog_ex_callback); - dialog_ex_set_context(dialog_ex, ibutton); + const iButtonProtocolId protocol_id = ibutton_key_get_protocol_id(key); - view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewDialogEx); + widget_add_button_element( + widget, GuiButtonTypeLeft, "Retry", ibutton_widget_callback, context); + widget_add_button_element( + widget, GuiButtonTypeRight, "More", ibutton_widget_callback, context); + furi_string_printf( + tmp, + "%s[%s]", + ibutton_protocols_get_name(ibutton->protocols, protocol_id), + ibutton_protocols_get_manufacturer(ibutton->protocols, protocol_id)); + + widget_add_string_element( + widget, 0, 2, AlignLeft, AlignTop, FontPrimary, furi_string_get_cstr(tmp)); + + furi_string_reset(tmp); + ibutton_protocols_render_brief_data(ibutton->protocols, key, tmp); + + widget_add_string_multiline_element( + widget, 0, 16, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(tmp)); + + view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget); ibutton_notification_message(ibutton, iButtonNotificationMessageGreenOn); + + furi_string_free(tmp); } bool ibutton_scene_read_success_on_event(void* context, SceneManagerEvent event) { @@ -62,9 +47,9 @@ bool ibutton_scene_read_success_on_event(void* context, SceneManagerEvent event) scene_manager_next_scene(scene_manager, iButtonSceneExitConfirm); } else if(event.type == SceneManagerEventTypeCustom) { consumed = true; - if(event.event == DialogExResultRight) { + if(event.event == GuiButtonTypeRight) { scene_manager_next_scene(scene_manager, iButtonSceneReadKeyMenu); - } else if(event.event == DialogExResultLeft) { + } else if(event.event == GuiButtonTypeLeft) { scene_manager_next_scene(scene_manager, iButtonSceneRetryConfirm); } } @@ -74,11 +59,8 @@ bool ibutton_scene_read_success_on_event(void* context, SceneManagerEvent event) void ibutton_scene_read_success_on_exit(void* context) { iButton* ibutton = context; - DialogEx* dialog_ex = ibutton->dialog_ex; - ibutton_text_store_clear(ibutton); - - dialog_ex_reset(dialog_ex); + widget_reset(ibutton->widget); ibutton_notification_message(ibutton, iButtonNotificationMessageGreenOff); } diff --git a/applications/main/ibutton/scenes/ibutton_scene_retry_confirm.c b/applications/main/ibutton/scenes/ibutton_scene_retry_confirm.c index 7f8c95b1e..34de5b877 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_retry_confirm.c +++ b/applications/main/ibutton/scenes/ibutton_scene_retry_confirm.c @@ -19,7 +19,7 @@ void ibutton_scene_retry_confirm_on_enter(void* context) { widget_add_button_element( widget, GuiButtonTypeRight, "Stay", ibutton_scene_retry_confirm_widget_callback, ibutton); widget_add_string_element( - widget, 64, 19, AlignCenter, AlignBottom, FontPrimary, "Return to reading?"); + widget, 64, 19, AlignCenter, AlignBottom, FontPrimary, "Retry Reading?"); widget_add_string_element( widget, 64, 29, AlignCenter, AlignBottom, FontSecondary, "All unsaved data will be lost!"); diff --git a/applications/main/ibutton/scenes/ibutton_scene_rpc.c b/applications/main/ibutton/scenes/ibutton_scene_rpc.c index b25b1b8dd..9205eb4b4 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_rpc.c +++ b/applications/main/ibutton/scenes/ibutton_scene_rpc.c @@ -1,6 +1,4 @@ #include "../ibutton_i.h" -#include -#include void ibutton_scene_rpc_on_enter(void* context) { iButton* ibutton = context; @@ -17,8 +15,6 @@ void ibutton_scene_rpc_on_enter(void* context) { } bool ibutton_scene_rpc_on_event(void* context, SceneManagerEvent event) { - UNUSED(context); - UNUSED(event); iButton* ibutton = context; Popup* popup = ibutton->popup; @@ -26,40 +22,32 @@ bool ibutton_scene_rpc_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { consumed = true; - if(event.event == iButtonCustomEventRpcLoad) { - const char* arg = rpc_system_app_get_data(ibutton->rpc_ctx); - bool result = false; - if(arg && (furi_string_empty(ibutton->file_path))) { - furi_string_set(ibutton->file_path, arg); - if(ibutton_load_key_data(ibutton, ibutton->file_path, false)) { - ibutton_worker_emulate_start(ibutton->key_worker, ibutton->key); - FuriString* key_name; - key_name = furi_string_alloc(); - if(furi_string_end_with(ibutton->file_path, IBUTTON_APP_EXTENSION)) { - path_extract_filename(ibutton->file_path, key_name, true); - } - if(!furi_string_empty(key_name)) { - ibutton_text_store_set( - ibutton, "emulating\n%s", furi_string_get_cstr(key_name)); - } else { - ibutton_text_store_set(ibutton, "emulating"); - } - popup_set_text(popup, ibutton->text_store, 82, 32, AlignCenter, AlignTop); + if(event.event == iButtonCustomEventRpcLoad) { + bool result = false; + const char* file_path = rpc_system_app_get_data(ibutton->rpc); + + if(file_path && (furi_string_empty(ibutton->file_path))) { + furi_string_set(ibutton->file_path, file_path); + + if(ibutton_load_key(ibutton)) { + popup_set_text(popup, ibutton->key_name, 82, 32, AlignCenter, AlignTop); + view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewPopup); ibutton_notification_message(ibutton, iButtonNotificationMessageEmulateStart); + ibutton_worker_emulate_start(ibutton->worker, ibutton->key); - furi_string_free(key_name); result = true; - } else { - furi_string_reset(ibutton->file_path); } } - rpc_system_app_confirm(ibutton->rpc_ctx, RpcAppEventLoadFile, result); + + rpc_system_app_confirm(ibutton->rpc, RpcAppEventLoadFile, result); + } else if(event.event == iButtonCustomEventRpcExit) { - rpc_system_app_confirm(ibutton->rpc_ctx, RpcAppEventAppExit, true); + rpc_system_app_confirm(ibutton->rpc, RpcAppEventAppExit, true); scene_manager_stop(ibutton->scene_manager); view_dispatcher_stop(ibutton->view_dispatcher); + } else if(event.event == iButtonCustomEventRpcSessionClose) { scene_manager_stop(ibutton->scene_manager); view_dispatcher_stop(ibutton->view_dispatcher); diff --git a/applications/main/ibutton/scenes/ibutton_scene_save_name.c b/applications/main/ibutton/scenes/ibutton_scene_save_name.c index 5f25a0002..4ad0315e5 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_save_name.c +++ b/applications/main/ibutton/scenes/ibutton_scene_save_name.c @@ -1,6 +1,8 @@ #include "../ibutton_i.h" -#include + +#include #include + #include static void ibutton_scene_save_name_text_input_callback(void* context) { @@ -12,17 +14,10 @@ void ibutton_scene_save_name_on_enter(void* context) { iButton* ibutton = context; TextInput* text_input = ibutton->text_input; - FuriString* key_name; - key_name = furi_string_alloc(); - if(furi_string_end_with(ibutton->file_path, IBUTTON_APP_EXTENSION)) { - path_extract_filename(ibutton->file_path, key_name, true); - } + const bool is_new_file = furi_string_empty(ibutton->file_path); - const bool key_name_is_empty = furi_string_empty(key_name); - if(key_name_is_empty) { - set_random_name(ibutton->text_store, IBUTTON_TEXT_STORE_SIZE); - } else { - ibutton_text_store_set(ibutton, "%s", furi_string_get_cstr(key_name)); + if(is_new_file) { + set_random_name(ibutton->key_name, IBUTTON_KEY_NAME_SIZE); } text_input_set_header_text(text_input, "Name the key"); @@ -30,23 +25,15 @@ void ibutton_scene_save_name_on_enter(void* context) { text_input, ibutton_scene_save_name_text_input_callback, ibutton, - ibutton->text_store, + ibutton->key_name, IBUTTON_KEY_NAME_SIZE, - key_name_is_empty); + is_new_file); - FuriString* folder_path; - folder_path = furi_string_alloc(); - - path_extract_dirname(furi_string_get_cstr(ibutton->file_path), folder_path); - - ValidatorIsFile* validator_is_file = validator_is_file_alloc_init( - furi_string_get_cstr(folder_path), IBUTTON_APP_EXTENSION, furi_string_get_cstr(key_name)); + ValidatorIsFile* validator_is_file = + validator_is_file_alloc_init(IBUTTON_APP_FOLDER, IBUTTON_APP_EXTENSION, ibutton->key_name); text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewTextInput); - - furi_string_free(key_name); - furi_string_free(folder_path); } bool ibutton_scene_save_name_on_event(void* context, SceneManagerEvent event) { @@ -56,8 +43,16 @@ bool ibutton_scene_save_name_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { consumed = true; if(event.event == iButtonCustomEventTextEditResult) { - if(ibutton_save_key(ibutton, ibutton->text_store)) { + furi_string_printf( + ibutton->file_path, + "%s/%s%s", + IBUTTON_APP_FOLDER, + ibutton->key_name, + IBUTTON_APP_EXTENSION); + + if(ibutton_save_key(ibutton)) { scene_manager_next_scene(ibutton->scene_manager, iButtonSceneSaveSuccess); + if(scene_manager_has_previous_scene( ibutton->scene_manager, iButtonSceneSavedKeyMenu)) { // Nothing, do not count editing as saving @@ -67,6 +62,7 @@ bool ibutton_scene_save_name_on_event(void* context, SceneManagerEvent event) { } else { DOLPHIN_DEED(DolphinDeedIbuttonSave); } + } else { const uint32_t possible_scenes[] = { iButtonSceneReadKeyMenu, iButtonSceneSavedKeyMenu, iButtonSceneAddType}; diff --git a/applications/main/ibutton/scenes/ibutton_scene_saved_key_menu.c b/applications/main/ibutton/scenes/ibutton_scene_saved_key_menu.c index e4c9c350a..80fca28b5 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_saved_key_menu.c +++ b/applications/main/ibutton/scenes/ibutton_scene_saved_key_menu.c @@ -3,72 +3,70 @@ enum SubmenuIndex { SubmenuIndexEmulate, - SubmenuIndexWrite, + SubmenuIndexWriteBlank, + SubmenuIndexWriteCopy, SubmenuIndexEdit, SubmenuIndexDelete, SubmenuIndexInfo, }; -void ibutton_scene_saved_key_menu_submenu_callback(void* context, uint32_t index) { - iButton* ibutton = context; - view_dispatcher_send_custom_event(ibutton->view_dispatcher, index); -} - void ibutton_scene_saved_key_menu_on_enter(void* context) { iButton* ibutton = context; Submenu* submenu = ibutton->submenu; - submenu_add_item( - submenu, - "Emulate", - SubmenuIndexEmulate, - ibutton_scene_saved_key_menu_submenu_callback, - ibutton); - if(ibutton_key_get_type(ibutton->key) == iButtonKeyDS1990) { + const uint32_t features = ibutton_protocols_get_features( + ibutton->protocols, ibutton_key_get_protocol_id(ibutton->key)); + + submenu_add_item(submenu, "Emulate", SubmenuIndexEmulate, ibutton_submenu_callback, ibutton); + + if(features & iButtonProtocolFeatureWriteBlank) { submenu_add_item( - submenu, - "Write", - SubmenuIndexWrite, - ibutton_scene_saved_key_menu_submenu_callback, - ibutton); + submenu, "Write Blank", SubmenuIndexWriteBlank, ibutton_submenu_callback, ibutton); } - submenu_add_item( - submenu, "Edit", SubmenuIndexEdit, ibutton_scene_saved_key_menu_submenu_callback, ibutton); - submenu_add_item( - submenu, - "Delete", - SubmenuIndexDelete, - ibutton_scene_saved_key_menu_submenu_callback, - ibutton); - submenu_add_item( - submenu, "Info", SubmenuIndexInfo, ibutton_scene_saved_key_menu_submenu_callback, ibutton); + + if(features & iButtonProtocolFeatureWriteCopy) { + submenu_add_item( + submenu, "Write Copy", SubmenuIndexWriteCopy, ibutton_submenu_callback, ibutton); + } + + submenu_add_item(submenu, "Edit", SubmenuIndexEdit, ibutton_submenu_callback, ibutton); + submenu_add_item(submenu, "Delete", SubmenuIndexDelete, ibutton_submenu_callback, ibutton); + submenu_add_item(submenu, "Info", SubmenuIndexInfo, ibutton_submenu_callback, ibutton); submenu_set_selected_item( submenu, scene_manager_get_scene_state(ibutton->scene_manager, iButtonSceneSavedKeyMenu)); - view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewSubmenu); } bool ibutton_scene_saved_key_menu_on_event(void* context, SceneManagerEvent event) { iButton* ibutton = context; + SceneManager* scene_manager = ibutton->scene_manager; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { - scene_manager_set_scene_state( - ibutton->scene_manager, iButtonSceneSavedKeyMenu, event.event); + scene_manager_set_scene_state(scene_manager, iButtonSceneSavedKeyMenu, event.event); consumed = true; if(event.event == SubmenuIndexEmulate) { - scene_manager_next_scene(ibutton->scene_manager, iButtonSceneEmulate); + scene_manager_next_scene(scene_manager, iButtonSceneEmulate); DOLPHIN_DEED(DolphinDeedIbuttonEmulate); - } else if(event.event == SubmenuIndexWrite) { - scene_manager_next_scene(ibutton->scene_manager, iButtonSceneWrite); + } else if(event.event == SubmenuIndexWriteBlank) { + ibutton->write_mode = iButtonWriteModeBlank; + scene_manager_next_scene(scene_manager, iButtonSceneWrite); + } else if(event.event == SubmenuIndexWriteCopy) { + ibutton->write_mode = iButtonWriteModeCopy; + scene_manager_next_scene(scene_manager, iButtonSceneWrite); } else if(event.event == SubmenuIndexEdit) { - scene_manager_next_scene(ibutton->scene_manager, iButtonSceneAddValue); + scene_manager_next_scene(scene_manager, iButtonSceneAddValue); } else if(event.event == SubmenuIndexDelete) { - scene_manager_next_scene(ibutton->scene_manager, iButtonSceneDeleteConfirm); + scene_manager_next_scene(scene_manager, iButtonSceneDeleteConfirm); } else if(event.event == SubmenuIndexInfo) { - scene_manager_next_scene(ibutton->scene_manager, iButtonSceneInfo); + scene_manager_next_scene(scene_manager, iButtonSceneInfo); } + + } else if(event.type == SceneManagerEventTypeBack) { + scene_manager_set_scene_state( + scene_manager, iButtonSceneSavedKeyMenu, SubmenuIndexEmulate); + // Event is not consumed } return consumed; diff --git a/applications/main/ibutton/scenes/ibutton_scene_select_key.c b/applications/main/ibutton/scenes/ibutton_scene_select_key.c index 32169a9c0..ebd504b1f 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_select_key.c +++ b/applications/main/ibutton/scenes/ibutton_scene_select_key.c @@ -3,11 +3,11 @@ void ibutton_scene_select_key_on_enter(void* context) { iButton* ibutton = context; - if(!ibutton_file_select(ibutton)) { + if(ibutton_select_and_load_key(ibutton)) { + scene_manager_next_scene(ibutton->scene_manager, iButtonSceneSavedKeyMenu); + } else { scene_manager_search_and_switch_to_previous_scene( ibutton->scene_manager, iButtonSceneStart); - } else { - scene_manager_next_scene(ibutton->scene_manager, iButtonSceneSavedKeyMenu); } } diff --git a/applications/main/ibutton/scenes/ibutton_scene_start.c b/applications/main/ibutton/scenes/ibutton_scene_start.c index 28807faa8..37bf96f39 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_start.c +++ b/applications/main/ibutton/scenes/ibutton_scene_start.c @@ -8,21 +8,15 @@ enum SubmenuIndex { SubmenuIndexAdd, }; -void ibutton_scene_start_submenu_callback(void* context, uint32_t index) { - iButton* ibutton = context; - view_dispatcher_send_custom_event(ibutton->view_dispatcher, index); -} - void ibutton_scene_start_on_enter(void* context) { iButton* ibutton = context; Submenu* submenu = ibutton->submenu; - submenu_add_item( - submenu, "Read", SubmenuIndexRead, ibutton_scene_start_submenu_callback, ibutton); - submenu_add_item( - submenu, "Saved", SubmenuIndexSaved, ibutton_scene_start_submenu_callback, ibutton); - submenu_add_item( - submenu, "Add Manually", SubmenuIndexAdd, ibutton_scene_start_submenu_callback, ibutton); + ibutton_reset_key(ibutton); + + submenu_add_item(submenu, "Read", SubmenuIndexRead, ibutton_submenu_callback, ibutton); + submenu_add_item(submenu, "Saved", SubmenuIndexSaved, ibutton_submenu_callback, ibutton); + submenu_add_item(submenu, "Add Manually", SubmenuIndexAdd, ibutton_submenu_callback, ibutton); submenu_set_selected_item( submenu, scene_manager_get_scene_state(ibutton->scene_manager, iButtonSceneStart)); @@ -39,9 +33,8 @@ bool ibutton_scene_start_on_event(void* context, SceneManagerEvent event) { consumed = true; if(event.event == SubmenuIndexRead) { scene_manager_next_scene(ibutton->scene_manager, iButtonSceneRead); - //DOLPHIN_DEED(DolphinDeedIbuttonRead); + DOLPHIN_DEED(DolphinDeedIbuttonRead); } else if(event.event == SubmenuIndexSaved) { - furi_string_set(ibutton->file_path, IBUTTON_APP_FOLDER); scene_manager_next_scene(ibutton->scene_manager, iButtonSceneSelectKey); } else if(event.event == SubmenuIndexAdd) { scene_manager_next_scene(ibutton->scene_manager, iButtonSceneAddType); diff --git a/applications/main/ibutton/scenes/ibutton_scene_view_data.c b/applications/main/ibutton/scenes/ibutton_scene_view_data.c new file mode 100644 index 000000000..7e063d7ec --- /dev/null +++ b/applications/main/ibutton/scenes/ibutton_scene_view_data.c @@ -0,0 +1,26 @@ +#include "../ibutton_i.h" + +void ibutton_scene_view_data_on_enter(void* context) { + iButton* ibutton = context; + iButtonKey* key = ibutton->key; + Widget* widget = ibutton->widget; + + FuriString* tmp = furi_string_alloc(); + ibutton_protocols_render_data(ibutton->protocols, key, tmp); + + widget_add_text_scroll_element(widget, 0, 0, 128, 64, furi_string_get_cstr(tmp)); + + view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget); + furi_string_free(tmp); +} + +bool ibutton_scene_view_data_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void ibutton_scene_view_data_on_exit(void* context) { + iButton* ibutton = context; + widget_reset(ibutton->widget); +} diff --git a/applications/main/ibutton/scenes/ibutton_scene_write.c b/applications/main/ibutton/scenes/ibutton_scene_write.c index cdea04db3..541aa1c52 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_write.c +++ b/applications/main/ibutton/scenes/ibutton_scene_write.c @@ -1,5 +1,4 @@ #include "../ibutton_i.h" -#include "toolbox/path.h" typedef enum { iButtonSceneWriteStateDefault, @@ -13,61 +12,46 @@ static void ibutton_scene_write_callback(void* context, iButtonWorkerWriteResult void ibutton_scene_write_on_enter(void* context) { iButton* ibutton = context; + furi_assert(ibutton->write_mode != iButtonWriteModeInvalid); + iButtonKey* key = ibutton->key; + iButtonWorker* worker = ibutton->worker; + const iButtonProtocolId protocol_id = ibutton_key_get_protocol_id(key); + Widget* widget = ibutton->widget; - iButtonWorker* worker = ibutton->key_worker; + FuriString* tmp = furi_string_alloc(); - const uint8_t* key_data = ibutton_key_get_data_p(key); + widget_add_icon_element(widget, 3, 10, &I_iButtonKey_49x44); - FuriString* key_name; - key_name = furi_string_alloc(); - if(furi_string_end_with(ibutton->file_path, IBUTTON_APP_EXTENSION)) { - path_extract_filename(ibutton->file_path, key_name, true); - } + furi_string_printf( + tmp, + "%s\n[%s]", + ibutton->key_name, + ibutton_protocols_get_name(ibutton->protocols, protocol_id)); - // check that stored key has name - if(!furi_string_empty(key_name)) { - ibutton_text_store_set(ibutton, "%s", furi_string_get_cstr(key_name)); - } else { - // if not, show key data - switch(ibutton_key_get_type(key)) { - case iButtonKeyDS1990: - ibutton_text_store_set( - ibutton, - "%02X %02X %02X %02X\n%02X %02X %02X %02X", - key_data[0], - key_data[1], - key_data[2], - key_data[3], - key_data[4], - key_data[5], - key_data[6], - key_data[7]); - break; - case iButtonKeyCyfral: - ibutton_text_store_set(ibutton, "%02X %02X", key_data[0], key_data[1]); - break; - case iButtonKeyMetakom: - ibutton_text_store_set( - ibutton, "%02X %02X %02X %02X", key_data[0], key_data[1], key_data[2], key_data[3]); - break; - } + widget_add_text_box_element( + widget, 52, 38, 75, 26, AlignCenter, AlignCenter, furi_string_get_cstr(tmp), true); + + ibutton_worker_write_set_callback(worker, ibutton_scene_write_callback, ibutton); + + furi_string_set(tmp, "iButton\nwriting "); + + if(ibutton->write_mode == iButtonWriteModeBlank) { + furi_string_cat(tmp, "Blank"); + ibutton_worker_write_blank_start(worker, key); + + } else if(ibutton->write_mode == iButtonWriteModeCopy) { + furi_string_cat(tmp, "Copy"); + ibutton_worker_write_copy_start(worker, key); } widget_add_string_multiline_element( - widget, 90, 10, AlignCenter, AlignTop, FontPrimary, "iButton\nwriting"); - widget_add_icon_element(widget, 3, 10, &I_iButtonKey_49x44); - widget_add_text_box_element( - widget, 54, 39, 75, 22, AlignCenter, AlignCenter, ibutton->text_store, true); - - view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget); - - ibutton_worker_write_set_callback(worker, ibutton_scene_write_callback, ibutton); - ibutton_worker_write_start(worker, key); - - furi_string_free(key_name); + widget, 88, 10, AlignCenter, AlignTop, FontPrimary, furi_string_get_cstr(tmp)); ibutton_notification_message(ibutton, iButtonNotificationMessageEmulateStart); + view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget); + + furi_string_free(tmp); } bool ibutton_scene_write_on_event(void* context, SceneManagerEvent event) { @@ -94,7 +78,9 @@ bool ibutton_scene_write_on_event(void* context, SceneManagerEvent event) { void ibutton_scene_write_on_exit(void* context) { iButton* ibutton = context; - ibutton_worker_stop(ibutton->key_worker); + ibutton->write_mode = iButtonWriteModeInvalid; + + ibutton_worker_stop(ibutton->worker); widget_reset(ibutton->widget); ibutton_notification_message(ibutton, iButtonNotificationMessageBlinkStop); diff --git a/applications/main/infrared/infrared.c b/applications/main/infrared/infrared.c index 9d78a09b6..a88306cc5 100644 --- a/applications/main/infrared/infrared.c +++ b/applications/main/infrared/infrared.c @@ -3,6 +3,8 @@ #include #include +#define INFRARED_TX_MIN_INTERVAL_MS 50U + static const NotificationSequence* infrared_notification_sequences[] = { &sequence_success, &sequence_set_only_green_255, @@ -148,6 +150,12 @@ static Infrared* infrared_alloc() { view_dispatcher_add_view( view_dispatcher, InfraredViewTextInput, text_input_get_view(infrared->text_input)); + infrared->variable_item_list = variable_item_list_alloc(); + view_dispatcher_add_view( + infrared->view_dispatcher, + InfraredViewVariableItemList, + variable_item_list_get_view(infrared->variable_item_list)); + infrared->dialog_ex = dialog_ex_alloc(); view_dispatcher_add_view( view_dispatcher, InfraredViewDialogEx, dialog_ex_get_view(infrared->dialog_ex)); @@ -195,6 +203,9 @@ static void infrared_free(Infrared* infrared) { view_dispatcher_remove_view(view_dispatcher, InfraredViewTextInput); text_input_free(infrared->text_input); + view_dispatcher_remove_view(infrared->view_dispatcher, InfraredViewVariableItemList); + variable_item_list_free(infrared->variable_item_list); + view_dispatcher_remove_view(view_dispatcher, InfraredViewDialogEx); dialog_ex_free(infrared->dialog_ex); @@ -299,10 +310,13 @@ bool infrared_rename_current_remote(Infrared* infrared, const char* name) { void infrared_tx_start_signal(Infrared* infrared, InfraredSignal* signal) { if(infrared->app_state.is_transmitting) { - FURI_LOG_D(INFRARED_LOG_TAG, "Transmitter is already active"); return; - } else { - infrared->app_state.is_transmitting = true; + } + + const uint32_t time_elapsed = furi_get_tick() - infrared->app_state.last_transmit_time; + + if(time_elapsed < INFRARED_TX_MIN_INTERVAL_MS) { + return; } if(infrared_signal_is_raw(signal)) { @@ -319,6 +333,8 @@ void infrared_tx_start_signal(Infrared* infrared, InfraredSignal* signal) { infrared_worker_tx_set_get_signal_callback( infrared->worker, infrared_worker_tx_get_signal_steady_callback, infrared); infrared_worker_tx_start(infrared->worker); + + infrared->app_state.is_transmitting = true; } void infrared_tx_start_button_index(Infrared* infrared, size_t button_index) { @@ -328,26 +344,24 @@ void infrared_tx_start_button_index(Infrared* infrared, size_t button_index) { InfraredSignal* signal = infrared_remote_button_get_signal(button); infrared_tx_start_signal(infrared, signal); - infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStartSend); } void infrared_tx_start_received(Infrared* infrared) { infrared_tx_start_signal(infrared, infrared->received_signal); - infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStartSend); } void infrared_tx_stop(Infrared* infrared) { if(!infrared->app_state.is_transmitting) { - FURI_LOG_D(INFRARED_LOG_TAG, "Transmitter is already stopped"); return; - } else { - infrared->app_state.is_transmitting = false; } infrared_worker_tx_stop(infrared->worker); infrared_worker_tx_set_get_signal_callback(infrared->worker, NULL, NULL); infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStop); + + infrared->app_state.is_transmitting = false; + infrared->app_state.last_transmit_time = furi_get_tick(); } void infrared_text_store_set(Infrared* infrared, uint32_t bank, const char* text, ...) { diff --git a/applications/main/infrared/infrared_cli.c b/applications/main/infrared/infrared_cli.c index 5f5e2d4bb..3fa99cb02 100644 --- a/applications/main/infrared/infrared_cli.c +++ b/applications/main/infrared/infrared_cli.c @@ -86,7 +86,7 @@ static void infrared_cli_print_usage(void) { printf("\tir universal \r\n"); printf("\tir universal list \r\n"); // TODO: Do not hardcode universal remote names - printf("\tAvailable universal remotes: tv audio ac\r\n"); + printf("\tAvailable universal remotes: tv audio ac projector\r\n"); } static void infrared_cli_start_ir_rx(Cli* cli, FuriString* args) { diff --git a/applications/main/infrared/infrared_i.h b/applications/main/infrared/infrared_i.h index 6972d53b4..55b70fc5e 100644 --- a/applications/main/infrared/infrared_i.h +++ b/applications/main/infrared/infrared_i.h @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -9,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -32,8 +34,6 @@ #include "rpc/rpc_app.h" -#include - #define INFRARED_FILE_NAME_SIZE 100 #define INFRARED_TEXT_STORE_NUM 2 #define INFRARED_TEXT_STORE_SIZE 128 @@ -70,6 +70,7 @@ typedef struct { InfraredEditTarget edit_target : 8; InfraredEditMode edit_mode : 8; int32_t current_button_index; + uint32_t last_transmit_time; } InfraredAppState; struct Infrared { @@ -87,6 +88,7 @@ struct Infrared { Submenu* submenu; TextInput* text_input; + VariableItemList* variable_item_list; DialogEx* dialog_ex; ButtonMenu* button_menu; Popup* popup; @@ -108,6 +110,7 @@ struct Infrared { typedef enum { InfraredViewSubmenu, InfraredViewTextInput, + InfraredViewVariableItemList, InfraredViewDialogEx, InfraredViewButtonMenu, InfraredViewPopup, diff --git a/applications/main/infrared/scenes/infrared_scene_ask_back.c b/applications/main/infrared/scenes/infrared_scene_ask_back.c index 493458ade..77fc97f98 100644 --- a/applications/main/infrared/scenes/infrared_scene_ask_back.c +++ b/applications/main/infrared/scenes/infrared_scene_ask_back.c @@ -10,13 +10,13 @@ void infrared_scene_ask_back_on_enter(void* context) { DialogEx* dialog_ex = infrared->dialog_ex; if(infrared->app_state.is_learning_new_remote) { - dialog_ex_set_header(dialog_ex, "Exit to Infrared Menu?", 64, 0, AlignCenter, AlignTop); + dialog_ex_set_header(dialog_ex, "Exit to Infrared Menu?", 64, 11, AlignCenter, AlignTop); } else { - dialog_ex_set_header(dialog_ex, "Exit to Remote Menu?", 64, 0, AlignCenter, AlignTop); + dialog_ex_set_header(dialog_ex, "Exit to Remote Menu?", 64, 11, AlignCenter, AlignTop); } dialog_ex_set_text( - dialog_ex, "All unsaved data\nwill be lost!", 64, 31, AlignCenter, AlignCenter); + dialog_ex, "All unsaved data\nwill be lost!", 64, 25, AlignCenter, AlignTop); dialog_ex_set_icon(dialog_ex, 0, 0, NULL); dialog_ex_set_left_button_text(dialog_ex, "Exit"); dialog_ex_set_center_button_text(dialog_ex, NULL); diff --git a/applications/main/infrared/scenes/infrared_scene_ask_retry.c b/applications/main/infrared/scenes/infrared_scene_ask_retry.c index c87d9e6d3..602e470c7 100644 --- a/applications/main/infrared/scenes/infrared_scene_ask_retry.c +++ b/applications/main/infrared/scenes/infrared_scene_ask_retry.c @@ -9,9 +9,9 @@ void infrared_scene_ask_retry_on_enter(void* context) { Infrared* infrared = context; DialogEx* dialog_ex = infrared->dialog_ex; - dialog_ex_set_header(dialog_ex, "Return to Reading?", 64, 0, AlignCenter, AlignTop); + dialog_ex_set_header(dialog_ex, "Retry Reading?", 64, 11, AlignCenter, AlignTop); dialog_ex_set_text( - dialog_ex, "All unsaved data\nwill be lost!", 64, 31, AlignCenter, AlignCenter); + dialog_ex, "All unsaved data\nwill be lost!", 64, 25, AlignCenter, AlignTop); dialog_ex_set_icon(dialog_ex, 0, 0, NULL); dialog_ex_set_left_button_text(dialog_ex, "Exit"); dialog_ex_set_center_button_text(dialog_ex, NULL); diff --git a/applications/main/infrared/scenes/infrared_scene_config.h b/applications/main/infrared/scenes/infrared_scene_config.h index 551485295..b15974941 100644 --- a/applications/main/infrared/scenes/infrared_scene_config.h +++ b/applications/main/infrared/scenes/infrared_scene_config.h @@ -21,4 +21,5 @@ ADD_SCENE(infrared, universal_audio, UniversalAudio) ADD_SCENE(infrared, universal_projector, UniversalProjector) ADD_SCENE(infrared, debug, Debug) ADD_SCENE(infrared, error_databases, ErrorDatabases) +ADD_SCENE(infrared, debug_settings, DebugSettings) ADD_SCENE(infrared, rpc, Rpc) diff --git a/applications/main/infrared/scenes/infrared_scene_debug_settings.c b/applications/main/infrared/scenes/infrared_scene_debug_settings.c new file mode 100644 index 000000000..0bc830788 --- /dev/null +++ b/applications/main/infrared/scenes/infrared_scene_debug_settings.c @@ -0,0 +1,59 @@ +#include "../infrared_i.h" +#include + +uint8_t value_index_ir; + +#define DEB_PINS_COUNT (sizeof(infrared_debug_cfg_variables_text) / sizeof(char* const)) +const char* const infrared_debug_cfg_variables_text[] = { + "Internal", + "2 (A7)", +}; + +static void infrared_scene_debug_settings_changed(VariableItem* item) { + Infrared* infrared = variable_item_get_context(item); + value_index_ir = variable_item_get_current_value_index(item); + UNUSED(infrared); + + variable_item_set_current_value_text(item, infrared_debug_cfg_variables_text[value_index_ir]); + + furi_hal_infrared_set_debug_out(value_index_ir); +} +static void infrared_debug_settings_start_var_list_enter_callback(void* context, uint32_t index) { + Infrared* infrared = context; + view_dispatcher_send_custom_event(infrared->view_dispatcher, index); +} + +void infrared_scene_debug_settings_on_enter(void* context) { + Infrared* infrared = context; + + VariableItemList* variable_item_list = infrared->variable_item_list; + + value_index_ir = furi_hal_infrared_get_debug_out_status(); + VariableItem* item = variable_item_list_add( + variable_item_list, + "Send signal to", + DEB_PINS_COUNT, + infrared_scene_debug_settings_changed, + infrared); + + variable_item_list_set_enter_callback( + variable_item_list, infrared_debug_settings_start_var_list_enter_callback, infrared); + + variable_item_set_current_value_index(item, value_index_ir); + variable_item_set_current_value_text(item, infrared_debug_cfg_variables_text[value_index_ir]); + + view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewVariableItemList); +} + +bool infrared_scene_debug_settings_on_event(void* context, SceneManagerEvent event) { + Infrared* infrared = context; + UNUSED(infrared); + UNUSED(event); + + return false; +} + +void infrared_scene_debug_settings_on_exit(void* context) { + Infrared* infrared = context; + variable_item_list_reset(infrared->variable_item_list); +} diff --git a/applications/main/infrared/scenes/infrared_scene_start.c b/applications/main/infrared/scenes/infrared_scene_start.c index 4d13d6e0e..0403e40f8 100644 --- a/applications/main/infrared/scenes/infrared_scene_start.c +++ b/applications/main/infrared/scenes/infrared_scene_start.c @@ -5,7 +5,8 @@ enum SubmenuIndex { SubmenuIndexLearnNewRemote, SubmenuIndexLearnNewRemoteRaw, SubmenuIndexSavedRemotes, - SubmenuIndexDebug + SubmenuIndexDebug, + SubmenuIndexDebugSettings }; static void infrared_scene_start_submenu_callback(void* context, uint32_t index) { @@ -45,7 +46,17 @@ void infrared_scene_start_on_enter(void* context) { infrared_scene_start_submenu_callback, infrared); submenu_add_item( - submenu, "Debug", SubmenuIndexDebug, infrared_scene_start_submenu_callback, infrared); + submenu, + "Debug RX", + SubmenuIndexDebug, + infrared_scene_start_submenu_callback, + infrared); + submenu_add_item( + submenu, + "Debug Settings", + SubmenuIndexDebugSettings, + infrared_scene_start_submenu_callback, + infrared); } const uint32_t submenu_index = @@ -85,6 +96,9 @@ bool infrared_scene_start_on_event(void* context, SceneManagerEvent event) { } else if(submenu_index == SubmenuIndexDebug) { scene_manager_next_scene(scene_manager, InfraredSceneDebug); consumed = true; + } else if(submenu_index == SubmenuIndexDebugSettings) { + scene_manager_next_scene(scene_manager, InfraredSceneDebugSettings); + consumed = true; } } diff --git a/applications/main/infrared/scenes/infrared_scene_universal.c b/applications/main/infrared/scenes/infrared_scene_universal.c index 09b77b1f7..b6c1ac983 100644 --- a/applications/main/infrared/scenes/infrared_scene_universal.c +++ b/applications/main/infrared/scenes/infrared_scene_universal.c @@ -52,7 +52,8 @@ void infrared_scene_universal_on_enter(void* context) { infrared_scene_universal_submenu_callback, context); - submenu_set_selected_item(submenu, 0); + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(infrared->scene_manager, InfraredSceneUniversal)); view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewSubmenu); } @@ -79,6 +80,7 @@ bool infrared_scene_universal_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(scene_manager, InfraredSceneUniversalAC); consumed = true; } + scene_manager_set_scene_state(scene_manager, InfraredSceneUniversal, event.event); } return consumed; diff --git a/applications/main/infrared/views/infrared_progress_view.h b/applications/main/infrared/views/infrared_progress_view.h index e8f76ba1f..4f3c3a875 100644 --- a/applications/main/infrared/views/infrared_progress_view.h +++ b/applications/main/infrared/views/infrared_progress_view.h @@ -10,7 +10,7 @@ extern "C" { #endif -/** Anonumous instance */ +/** Anonymous instance */ typedef struct InfraredProgressView InfraredProgressView; /** Callback for back button handling */ diff --git a/applications/main/lfrfid/lfrfid_i.h b/applications/main/lfrfid/lfrfid_i.h index 201caa4f6..72b061930 100644 --- a/applications/main/lfrfid/lfrfid_i.h +++ b/applications/main/lfrfid/lfrfid_i.h @@ -5,8 +5,10 @@ #include #include +#include #include #include +#include #include #include @@ -32,8 +34,6 @@ #include #include -#include -// #include #define LFRFID_KEY_NAME_SIZE 22 #define LFRFID_TEXT_STORE_SIZE 40 diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_clear_t5577.c b/applications/main/lfrfid/scenes/lfrfid_scene_clear_t5577.c index 888288a31..a950de708 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_clear_t5577.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_clear_t5577.c @@ -16,7 +16,7 @@ static void lfrfid_clear_t5577_password_and_config_to_EM(LfRfid* app) { Popup* popup = app->popup; char curr_buf[32] = {}; - //TODO: use .txt file in resourses for passwords. + //TODO: use .txt file in resources for passwords. const uint32_t default_passwords[] = { 0x51243648, 0x000D8787, 0x19920427, 0x50524F58, 0xF9DCEBA0, 0x65857569, 0x05D73B9F, 0x89A69E60, 0x314159E0, 0xAA55BBBB, 0xA5B4C3D2, 0x1C0B5848, 0x00434343, 0x444E4752, diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c b/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c index b1130d191..efa319c1e 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c @@ -20,7 +20,7 @@ void lfrfid_scene_extra_actions_on_enter(void* context) { submenu_add_item( submenu, - "Read ASK (Animal, FDX)", + "Read ASK (FDX,Regular)", SubmenuIndexASK, lfrfid_scene_extra_actions_submenu_callback, app); diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_retry_confirm.c b/applications/main/lfrfid/scenes/lfrfid_scene_retry_confirm.c index f639f0ae1..ddac3e8ba 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_retry_confirm.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_retry_confirm.c @@ -7,7 +7,7 @@ void lfrfid_scene_retry_confirm_on_enter(void* context) { widget_add_button_element(widget, GuiButtonTypeLeft, "Exit", lfrfid_widget_callback, app); widget_add_button_element(widget, GuiButtonTypeRight, "Stay", lfrfid_widget_callback, app); widget_add_string_element( - widget, 64, 19, AlignCenter, AlignBottom, FontPrimary, "Return to reading?"); + widget, 64, 19, AlignCenter, AlignBottom, FontPrimary, "Retry Reading?"); widget_add_string_element( widget, 64, 29, AlignCenter, AlignBottom, FontSecondary, "All unsaved data will be lost!"); diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_start.c b/applications/main/lfrfid/scenes/lfrfid_scene_start.c index 8e1c92dbb..2d83ba53b 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_start.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_start.c @@ -47,21 +47,28 @@ bool lfrfid_scene_start_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == SubmenuIndexRead) { + scene_manager_set_scene_state(app->scene_manager, LfRfidSceneStart, SubmenuIndexRead); scene_manager_next_scene(app->scene_manager, LfRfidSceneRead); DOLPHIN_DEED(DolphinDeedRfidRead); consumed = true; } else if(event.event == SubmenuIndexSaved) { + // Like in the other apps, explicitly save the scene state + // in each branch in case the user cancels loading a file. + scene_manager_set_scene_state(app->scene_manager, LfRfidSceneStart, SubmenuIndexSaved); furi_string_set(app->file_path, LFRFID_APP_FOLDER); scene_manager_next_scene(app->scene_manager, LfRfidSceneSelectKey); consumed = true; } else if(event.event == SubmenuIndexAddManually) { + scene_manager_set_scene_state( + app->scene_manager, LfRfidSceneStart, SubmenuIndexAddManually); scene_manager_next_scene(app->scene_manager, LfRfidSceneSaveType); consumed = true; } else if(event.event == SubmenuIndexExtraActions) { + scene_manager_set_scene_state( + app->scene_manager, LfRfidSceneStart, SubmenuIndexExtraActions); scene_manager_next_scene(app->scene_manager, LfRfidSceneExtraActions); consumed = true; } - scene_manager_set_scene_state(app->scene_manager, LfRfidSceneStart, event.event); } return consumed; diff --git a/applications/main/nfc/helpers/nfc_emv_parser.h b/applications/main/nfc/helpers/nfc_emv_parser.h index abe57f470..c636ca77d 100644 --- a/applications/main/nfc/helpers/nfc_emv_parser.h +++ b/applications/main/nfc/helpers/nfc_emv_parser.h @@ -9,7 +9,7 @@ * @param aid - AID number array * @param aid_len - AID length * @param aid_name - string to keep AID name - * @return - true if AID found, false otherwies + * @return - true if AID found, false otherwise */ bool nfc_emv_parser_get_aid_name( Storage* storage, @@ -21,7 +21,7 @@ bool nfc_emv_parser_get_aid_name( * @param storage Storage instance * @param country_code - ISO 3166 country code * @param country_name - string to keep country name - * @return - true if country found, false otherwies + * @return - true if country found, false otherwise */ bool nfc_emv_parser_get_country_name( Storage* storage, @@ -32,7 +32,7 @@ bool nfc_emv_parser_get_country_name( * @param storage Storage instance * @param currency_code - ISO 3166 currency code * @param currency_name - string to keep currency name - * @return - true if currency found, false otherwies + * @return - true if currency found, false otherwise */ bool nfc_emv_parser_get_currency_name( Storage* storage, diff --git a/applications/main/nfc/nfc_cli.c b/applications/main/nfc/nfc_cli.c index a6475ca68..23335e299 100644 --- a/applications/main/nfc/nfc_cli.c +++ b/applications/main/nfc/nfc_cli.c @@ -32,7 +32,7 @@ static void nfc_cli_detect(Cli* cli, FuriString* args) { while(!cmd_exit) { cmd_exit |= cli_cmd_interrupt_received(cli); if(furi_hal_nfc_detect(&dev_data, 400)) { - printf("found: %s ", nfc_get_dev_type(dev_data.type)); + printf("Found: %s ", nfc_get_dev_type(dev_data.type)); printf("UID length: %d, UID:", dev_data.uid_len); for(size_t i = 0; i < dev_data.uid_len; i++) { printf("%02X", dev_data.uid[i]); diff --git a/applications/main/nfc/scenes/nfc_scene_config.h b/applications/main/nfc/scenes/nfc_scene_config.h index 045080fea..ef257b76e 100644 --- a/applications/main/nfc/scenes/nfc_scene_config.h +++ b/applications/main/nfc/scenes/nfc_scene_config.h @@ -34,6 +34,7 @@ ADD_SCENE(nfc, mf_desfire_menu, MfDesfireMenu) ADD_SCENE(nfc, mf_desfire_data, MfDesfireData) ADD_SCENE(nfc, mf_desfire_app, MfDesfireApp) ADD_SCENE(nfc, mf_classic_read_success, MfClassicReadSuccess) +ADD_SCENE(nfc, mf_classic_data, MfClassicData) ADD_SCENE(nfc, mf_classic_menu, MfClassicMenu) ADD_SCENE(nfc, mf_classic_emulate, MfClassicEmulate) ADD_SCENE(nfc, mf_classic_keys, MfClassicKeys) diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_data.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_data.c new file mode 100644 index 000000000..dcb02d364 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_data.c @@ -0,0 +1,106 @@ +#include "../nfc_i.h" + +void nfc_scene_mf_classic_data_on_enter(void* context) { + Nfc* nfc = context; + MfClassicType type = nfc->dev->dev_data.mf_classic_data.type; + MfClassicData* data = &nfc->dev->dev_data.mf_classic_data; + TextBox* text_box = nfc->text_box; + + text_box_set_font(text_box, TextBoxFontHex); + + int card_blocks = 0; + if(type == MfClassicType1k) { + card_blocks = MF_CLASSIC_1K_TOTAL_SECTORS_NUM * 4; + } else if(type == MfClassicType4k) { + // 16 sectors of 4 blocks each plus 8 sectors of 16 blocks each + card_blocks = MF_CLASSIC_1K_TOTAL_SECTORS_NUM * 4 + 8 * 16; + } else if(type == MfClassicTypeMini) { + card_blocks = MF_MINI_TOTAL_SECTORS_NUM * 4; + } + + int bytes_written = 0; + for(int block_num = 0; block_num < card_blocks; block_num++) { + bool is_sec_trailer = mf_classic_is_sector_trailer(block_num); + if(is_sec_trailer) { + uint8_t sector_num = mf_classic_get_sector_by_block(block_num); + MfClassicSectorTrailer* sec_tr = + mf_classic_get_sector_trailer_by_sector(data, sector_num); + // Key A + for(size_t i = 0; i < sizeof(sec_tr->key_a); i += 2) { + if((bytes_written % 8 == 0) && (bytes_written != 0)) { + furi_string_push_back(nfc->text_box_store, '\n'); + } + if(mf_classic_is_key_found(data, sector_num, MfClassicKeyA)) { + furi_string_cat_printf( + nfc->text_box_store, "%02X%02X ", sec_tr->key_a[i], sec_tr->key_a[i + 1]); + } else { + furi_string_cat_printf(nfc->text_box_store, "???? "); + } + bytes_written += 2; + } + // Access bytes + for(size_t i = 0; i < MF_CLASSIC_ACCESS_BYTES_SIZE; i += 2) { + if((bytes_written % 8 == 0) && (bytes_written != 0)) { + furi_string_push_back(nfc->text_box_store, '\n'); + } + if(mf_classic_is_block_read(data, block_num)) { + furi_string_cat_printf( + nfc->text_box_store, + "%02X%02X ", + sec_tr->access_bits[i], + sec_tr->access_bits[i + 1]); + } else { + furi_string_cat_printf(nfc->text_box_store, "???? "); + } + bytes_written += 2; + } + // Key B + for(size_t i = 0; i < sizeof(sec_tr->key_b); i += 2) { + if((bytes_written % 8 == 0) && (bytes_written != 0)) { + furi_string_push_back(nfc->text_box_store, '\n'); + } + if(mf_classic_is_key_found(data, sector_num, MfClassicKeyB)) { + furi_string_cat_printf( + nfc->text_box_store, "%02X%02X ", sec_tr->key_b[i], sec_tr->key_b[i + 1]); + } else { + furi_string_cat_printf(nfc->text_box_store, "???? "); + } + bytes_written += 2; + } + } else { + // Write data block + for(size_t i = 0; i < MF_CLASSIC_BLOCK_SIZE; i += 2) { + if((bytes_written % 8 == 0) && (bytes_written != 0)) { + furi_string_push_back(nfc->text_box_store, '\n'); + } + if(mf_classic_is_block_read(data, block_num)) { + furi_string_cat_printf( + nfc->text_box_store, + "%02X%02X ", + data->block[block_num].value[i], + data->block[block_num].value[i + 1]); + } else { + furi_string_cat_printf(nfc->text_box_store, "???? "); + } + bytes_written += 2; + } + } + } + text_box_set_text(text_box, furi_string_get_cstr(nfc->text_box_store)); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox); +} + +bool nfc_scene_mf_classic_data_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void nfc_scene_mf_classic_data_on_exit(void* context) { + Nfc* nfc = context; + + // Clean view + text_box_reset(nfc->text_box); + furi_string_reset(nfc->text_box_store); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_menu.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_menu.c index 5fbdabe30..67b2a8530 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_menu.c @@ -25,7 +25,7 @@ void nfc_scene_mf_classic_menu_on_enter(void* context) { if(!mf_classic_is_card_read(&nfc->dev->dev_data.mf_classic_data)) { submenu_add_item( submenu, - "Detect reader", + "Detect Reader", SubmenuIndexDetectReader, nfc_scene_mf_classic_menu_submenu_callback, nfc); 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 372da8860..d6cb48047 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c +++ b/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c @@ -26,7 +26,7 @@ void nfc_scene_nfc_data_info_on_enter(void* context) { NfcProtocol protocol = dev_data->protocol; uint8_t text_scroll_height = 0; if((protocol == NfcDeviceProtocolMifareDesfire) || (protocol == NfcDeviceProtocolMifareUl) || - (protocol == NfcDeviceProtocolNfcV)) { + (protocol == NfcDeviceProtocolNfcV) || (protocol == NfcDeviceProtocolMifareClassic)) { widget_add_button_element( widget, GuiButtonTypeRight, "More", nfc_scene_nfc_data_info_widget_callback, nfc); text_scroll_height = 52; @@ -290,6 +290,9 @@ bool nfc_scene_nfc_data_info_on_event(void* context, SceneManagerEvent event) { } else if(protocol == NfcDeviceProtocolNfcV) { scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVMenu); consumed = true; + } else if(protocol == NfcDeviceProtocolMifareClassic) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicData); + consumed = true; } } } diff --git a/applications/main/nfc/scenes/nfc_scene_read_card_success.c b/applications/main/nfc/scenes/nfc_scene_read_card_success.c index 9b2a2188e..ee80ee768 100644 --- a/applications/main/nfc/scenes/nfc_scene_read_card_success.c +++ b/applications/main/nfc/scenes/nfc_scene_read_card_success.c @@ -46,6 +46,9 @@ bool nfc_scene_read_card_success_on_event(void* context, SceneManagerEvent event if(event.event == GuiButtonTypeLeft) { consumed = scene_manager_previous_scene(nfc->scene_manager); } + } else if(event.type == SceneManagerEventTypeBack) { + consumed = + scene_manager_search_and_switch_to_previous_scene(nfc->scene_manager, NfcSceneStart); } return consumed; } diff --git a/applications/main/nfc/scenes/nfc_scene_retry_confirm.c b/applications/main/nfc/scenes/nfc_scene_retry_confirm.c index 366582ea8..5f4f7985e 100644 --- a/applications/main/nfc/scenes/nfc_scene_retry_confirm.c +++ b/applications/main/nfc/scenes/nfc_scene_retry_confirm.c @@ -14,7 +14,7 @@ void nfc_scene_retry_confirm_on_enter(void* context) { dialog_ex_set_right_button_text(dialog_ex, "Stay"); dialog_ex_set_header(dialog_ex, "Retry Reading?", 64, 11, AlignCenter, AlignTop); dialog_ex_set_text( - dialog_ex, "All unsaved data will be\nlost!", 64, 25, AlignCenter, AlignTop); + dialog_ex, "All unsaved data\nwill be lost!", 64, 25, AlignCenter, AlignTop); dialog_ex_set_context(dialog_ex, nfc); dialog_ex_set_result_callback(dialog_ex, nfc_scene_retry_confirm_dialog_callback); diff --git a/applications/main/nfc/scenes/nfc_scene_saved_menu.c b/applications/main/nfc/scenes/nfc_scene_saved_menu.c index 616dc0ef7..dddaa47b3 100644 --- a/applications/main/nfc/scenes/nfc_scene_saved_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_saved_menu.c @@ -52,20 +52,20 @@ void nfc_scene_saved_menu_on_enter(void* context) { if(!mf_classic_is_card_read(&nfc->dev->dev_data.mf_classic_data)) { submenu_add_item( submenu, - "Detect reader", + "Detect Reader", SubmenuIndexDetectReader, nfc_scene_saved_menu_submenu_callback, nfc); } submenu_add_item( submenu, - "Write To Initial Card", + "Write to Initial Card", SubmenuIndexWrite, nfc_scene_saved_menu_submenu_callback, nfc); submenu_add_item( submenu, - "Update From Initial Card", + "Update from Initial Card", SubmenuIndexUpdate, nfc_scene_saved_menu_submenu_callback, nfc); @@ -76,13 +76,13 @@ void nfc_scene_saved_menu_on_enter(void* context) { !mf_ul_is_full_capture(&nfc->dev->dev_data.mf_ul_data)) { submenu_add_item( submenu, - "Unlock With Reader", + "Unlock with Reader", SubmenuIndexMfUlUnlockByReader, nfc_scene_saved_menu_submenu_callback, nfc); submenu_add_item( submenu, - "Unlock With Password", + "Unlock with Password", SubmenuIndexMfUlUnlockByPassword, nfc_scene_saved_menu_submenu_callback, nfc); @@ -153,6 +153,8 @@ bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) { application_info_present = nfc_supported_card_verify_and_parse(dev_data); } + FURI_LOG_I("nfc", "application_info_present: %d", application_info_present); + if(application_info_present) { scene_manager_next_scene(nfc->scene_manager, NfcSceneDeviceInfo); } else { diff --git a/applications/main/sub_playlist/playlist_10px.png b/applications/main/sub_playlist/playlist_10px.png deleted file mode 100644 index 94ad885c3..000000000 Binary files a/applications/main/sub_playlist/playlist_10px.png and /dev/null differ diff --git a/applications/main/subghz/helpers/subghz_chat.c b/applications/main/subghz/helpers/subghz_chat.c index dbf34c970..b589ba5d5 100644 --- a/applications/main/subghz/helpers/subghz_chat.c +++ b/applications/main/subghz/helpers/subghz_chat.c @@ -9,7 +9,7 @@ struct SubGhzChatWorker { SubGhzTxRxWorker* subghz_txrx; volatile bool worker_running; - volatile bool worker_stoping; + volatile bool worker_stopping; FuriMessageQueue* event_queue; uint32_t last_time_rx_data; diff --git a/applications/main/subghz/helpers/subghz_custom_event.h b/applications/main/subghz/helpers/subghz_custom_event.h index 350e68ee6..4475045ee 100644 --- a/applications/main/subghz/helpers/subghz_custom_event.h +++ b/applications/main/subghz/helpers/subghz_custom_event.h @@ -8,11 +8,14 @@ typedef enum { //SubmenuIndex SubmenuIndexFaacSLH_433, SubmenuIndexFaacSLH_868, - SubmenuIndexBFT, + SubmenuIndexBFTClone, + SubmenuIndexBFTMitto, + SubmenuIndexSomfyTelis, SubmenuIndexPricenton, SubmenuIndexNiceFlo12bit, SubmenuIndexNiceFlo24bit, SubmenuIndexNiceFlorS_433_92, + SubmenuIndexNiceOne_433_92, SubmenuIndexNiceSmilo_433_92, SubmenuIndexCAME12bit, SubmenuIndexCAME24bit, @@ -64,6 +67,7 @@ typedef enum { SubGhzCustomEventViewReceiverBack, SubGhzCustomEventViewReceiverOffDisplay, SubGhzCustomEventViewReceiverUnlock, + SubGhzCustomEventViewReceiverDeleteItem, SubGhzCustomEventViewReadRAWBack, SubGhzCustomEventViewReadRAWIDLE, diff --git a/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c b/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c index 6452792a6..ab90547cb 100644 --- a/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c +++ b/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c @@ -117,16 +117,17 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) { // First stage: coarse scan for(size_t i = 0; i < subghz_setting_get_frequency_count(instance->setting); i++) { - if(furi_hal_subghz_is_frequency_valid( - subghz_setting_get_frequency(instance->setting, i)) && + uint32_t current_frequency = subghz_setting_get_frequency(instance->setting, i); + if(furi_hal_subghz_is_frequency_valid(current_frequency) && + (current_frequency != 467750000) && (current_frequency != 464000000) && !((furi_hal_subghz.radio_type == SubGhzRadioExternal) && - (subghz_setting_get_frequency(instance->setting, i) >= 311900000 && - subghz_setting_get_frequency(instance->setting, i) <= 312200000))) { + ((current_frequency == 390000000) || (current_frequency == 312000000) || + (current_frequency == 312100000) || (current_frequency == 312200000) || + (current_frequency == 440175000)))) { furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle); cc1101_switch_to_idle(furi_hal_subghz.spi_bus_handle); - frequency = cc1101_set_frequency( - furi_hal_subghz.spi_bus_handle, - subghz_setting_get_frequency(instance->setting, i)); + frequency = + cc1101_set_frequency(furi_hal_subghz.spi_bus_handle, current_frequency); cc1101_calibrate(furi_hal_subghz.spi_bus_handle); do { @@ -331,4 +332,4 @@ void subghz_frequency_analyzer_worker_set_trigger_level( float subghz_frequency_analyzer_worker_get_trigger_level(SubGhzFrequencyAnalyzerWorker* instance) { return instance->trigger_level; -} \ No newline at end of file +} diff --git a/applications/main/subghz/helpers/subghz_types.h b/applications/main/subghz/helpers/subghz_types.h index 7d63ba6b9..3c5982427 100644 --- a/applications/main/subghz/helpers/subghz_types.h +++ b/applications/main/subghz/helpers/subghz_types.h @@ -23,7 +23,7 @@ typedef enum { /** SubGhzHopperState state */ typedef enum { SubGhzHopperStateOFF, - SubGhzHopperStateRunnig, + SubGhzHopperStateRunning, SubGhzHopperStatePause, SubGhzHopperStateRSSITimeOut, } SubGhzHopperState; @@ -54,6 +54,7 @@ typedef enum { SubGhzLoadKeyStateOK, SubGhzLoadKeyStateParseErr, SubGhzLoadKeyStateOnlyRx, + SubGhzLoadKeyStateProtocolDescriptionErr, } SubGhzLoadKeyState; /** SubGhzLock */ diff --git a/applications/main/subghz/scenes/subghz_scene_ext_module_settings.c b/applications/main/subghz/scenes/subghz_scene_ext_module_settings.c index 7d7a505cb..c58661332 100644 --- a/applications/main/subghz/scenes/subghz_scene_ext_module_settings.c +++ b/applications/main/subghz/scenes/subghz_scene_ext_module_settings.c @@ -1,8 +1,10 @@ #include "../subghz_i.h" #include "../helpers/subghz_custom_event.h" -uint8_t value_index; -uint8_t value_index2; +uint8_t value_index_exm; +uint8_t value_index_dpin; +uint8_t value_index_cnt; +uint8_t value_index_pwr; #define EXT_MODULES_COUNT (sizeof(radio_modules_variables_text) / sizeof(char* const)) const char* const radio_modules_variables_text[] = { @@ -10,18 +12,34 @@ const char* const radio_modules_variables_text[] = { "External", }; +#define EXT_MOD_POWER_COUNT 2 +const char* const ext_mod_power_text[EXT_MOD_POWER_COUNT] = { + "ON", + "OFF", +}; + #define DEBUG_P_COUNT 2 const char* const debug_pin_text[DEBUG_P_COUNT] = { "OFF", - "A7", + "17(1W)", +}; + +#define DEBUG_COUNTER_COUNT 6 +const char* const debug_counter_text[DEBUG_COUNTER_COUNT] = { + "+1", + "+2", + "+3", + "+4", + "+5", + "+10", }; static void subghz_scene_ext_module_changed(VariableItem* item) { SubGhz* subghz = variable_item_get_context(item); - value_index = variable_item_get_current_value_index(item); + value_index_exm = variable_item_get_current_value_index(item); UNUSED(subghz); - variable_item_set_current_value_text(item, radio_modules_variables_text[value_index]); + variable_item_set_current_value_text(item, radio_modules_variables_text[value_index_exm]); } static void subghz_ext_module_start_var_list_enter_callback(void* context, uint32_t index) { SubGhz* subghz = context; @@ -37,31 +55,114 @@ static void subghz_scene_receiver_config_set_debug_pin(VariableItem* item) { subghz->txrx->debug_pin_state = index == 1; } +static void subghz_scene_receiver_config_set_debug_counter(VariableItem* item) { + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, debug_counter_text[index]); + + switch(index) { + case 0: + furi_hal_subghz_set_rolling_counter_mult(1); + break; + case 1: + furi_hal_subghz_set_rolling_counter_mult(2); + break; + case 2: + furi_hal_subghz_set_rolling_counter_mult(3); + break; + case 3: + furi_hal_subghz_set_rolling_counter_mult(4); + break; + case 4: + furi_hal_subghz_set_rolling_counter_mult(5); + break; + case 5: + furi_hal_subghz_set_rolling_counter_mult(10); + break; + default: + break; + } +} + +static void subghz_scene_receiver_config_set_ext_mod_power(VariableItem* item) { + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, ext_mod_power_text[index]); + + furi_hal_subghz_set_external_power_disable(index == 1); + if(index == 1) { + furi_hal_subghz_disable_ext_power(); + } else { + furi_hal_subghz_enable_ext_power(); + } +} + void subghz_scene_ext_module_settings_on_enter(void* context) { SubGhz* subghz = context; VariableItemList* variable_item_list = subghz->variable_item_list; - value_index = furi_hal_subghz.radio_type; + value_index_exm = furi_hal_subghz.radio_type; VariableItem* item = variable_item_list_add( variable_item_list, "Module", EXT_MODULES_COUNT, subghz_scene_ext_module_changed, subghz); variable_item_list_set_enter_callback( variable_item_list, subghz_ext_module_start_var_list_enter_callback, subghz); - variable_item_set_current_value_index(item, value_index); - variable_item_set_current_value_text(item, radio_modules_variables_text[value_index]); + variable_item_set_current_value_index(item, value_index_exm); + variable_item_set_current_value_text(item, radio_modules_variables_text[value_index_exm]); + + item = variable_item_list_add( + subghz->variable_item_list, + "Ext Radio 5v", + EXT_MOD_POWER_COUNT, + subghz_scene_receiver_config_set_ext_mod_power, + subghz); + value_index_pwr = furi_hal_subghz_get_external_power_disable(); + variable_item_set_current_value_index(item, value_index_pwr); + variable_item_set_current_value_text(item, ext_mod_power_text[value_index_pwr]); if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { item = variable_item_list_add( subghz->variable_item_list, - "Debug Pin:", + "Debug Pin", DEBUG_P_COUNT, subghz_scene_receiver_config_set_debug_pin, subghz); - value_index2 = subghz->txrx->debug_pin_state; - variable_item_set_current_value_index(item, value_index2); - variable_item_set_current_value_text(item, debug_pin_text[value_index2]); + value_index_dpin = subghz->txrx->debug_pin_state; + variable_item_set_current_value_index(item, value_index_dpin); + variable_item_set_current_value_text(item, debug_pin_text[value_index_dpin]); + + item = variable_item_list_add( + subghz->variable_item_list, + "Counter incr.", + DEBUG_COUNTER_COUNT, + subghz_scene_receiver_config_set_debug_counter, + subghz); + switch(furi_hal_subghz_get_rolling_counter_mult()) { + case 1: + value_index_cnt = 0; + break; + case 2: + value_index_cnt = 1; + break; + case 3: + value_index_cnt = 2; + break; + case 4: + value_index_cnt = 3; + break; + case 5: + value_index_cnt = 4; + break; + case 10: + value_index_cnt = 5; + break; + default: + break; + } + variable_item_set_current_value_index(item, value_index_cnt); + variable_item_set_current_value_text(item, debug_counter_text[value_index_cnt]); } view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdVariableItemList); @@ -73,12 +174,14 @@ bool subghz_scene_ext_module_settings_on_event(void* context, SceneManagerEvent UNUSED(event); // Set selected radio module - furi_hal_subghz_set_radio_type(value_index); + furi_hal_subghz_set_radio_type(value_index_exm); + + furi_hal_subghz_enable_ext_power(); // Check if module is present, if no -> show error if(!furi_hal_subghz_check_radio()) { - value_index = 0; - furi_hal_subghz_set_radio_type(value_index); + value_index_exm = 0; + furi_hal_subghz_set_radio_type(value_index_exm); furi_string_set(subghz->error_str, "Please connect\nexternal radio"); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowErrorSub); } diff --git a/applications/main/subghz/scenes/subghz_scene_need_saving.c b/applications/main/subghz/scenes/subghz_scene_need_saving.c index 2cfe060c7..5c6b9cc39 100644 --- a/applications/main/subghz/scenes/subghz_scene_need_saving.c +++ b/applications/main/subghz/scenes/subghz_scene_need_saving.c @@ -16,7 +16,7 @@ void subghz_scene_need_saving_on_enter(void* context) { SubGhz* subghz = context; widget_add_string_multiline_element( - subghz->widget, 64, 13, AlignCenter, AlignCenter, FontPrimary, "Exit to Sub-GHz menu?"); + subghz->widget, 64, 13, AlignCenter, AlignCenter, FontPrimary, "Exit to Sub-GHz Menu?"); widget_add_string_multiline_element( subghz->widget, 64, @@ -24,7 +24,7 @@ void subghz_scene_need_saving_on_enter(void* context) { AlignCenter, AlignCenter, FontSecondary, - "All unsaved will be\nlost."); + "All unsaved data\nwill be lost!"); widget_add_button_element( subghz->widget, GuiButtonTypeRight, "Stay", subghz_scene_need_saving_callback, subghz); diff --git a/applications/main/subghz/scenes/subghz_scene_read_raw.c b/applications/main/subghz/scenes/subghz_scene_read_raw.c index e396527cc..1e3d6a9c0 100644 --- a/applications/main/subghz/scenes/subghz_scene_read_raw.c +++ b/applications/main/subghz/scenes/subghz_scene_read_raw.c @@ -368,7 +368,7 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { float rssi = furi_hal_subghz_get_rssi(); - if(float_is_equal(subghz->txrx->raw_threshold_rssi, SUBGHZ_RAW_TRESHOLD_MIN)) { + if(float_is_equal(subghz->txrx->raw_threshold_rssi, SUBGHZ_RAW_THRESHOLD_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); @@ -421,4 +421,4 @@ void subghz_scene_read_raw_on_exit(void* context) { //filter restoration subghz_receiver_set_filter(subghz->txrx->receiver, subghz->txrx->filter); -} \ No newline at end of file +} diff --git a/applications/main/subghz/scenes/subghz_scene_receiver.c b/applications/main/subghz/scenes/subghz_scene_receiver.c index e1ea08497..c0112199c 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver.c @@ -204,6 +204,16 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) { DOLPHIN_DEED(DolphinDeedSubGhzReceiverInfo); consumed = true; break; + case SubGhzCustomEventViewReceiverDeleteItem: + subghz->txrx->idx_menu_chosen = + subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); + + subghz_history_delete_item(subghz->txrx->history, subghz->txrx->idx_menu_chosen); + subghz_view_receiver_delete_element_callback(subghz->subghz_receiver); + + subghz_scene_receiver_update_statusbar(subghz); + consumed = true; + break; case SubGhzCustomEventViewReceiverConfig: subghz->state_notifications = SubGhzNotificationStateIDLE; subghz->txrx->idx_menu_chosen = diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_config.c b/applications/main/subghz/scenes/subghz_scene_receiver_config.c index af4c6aca3..ff51e2f4d 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver_config.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver_config.c @@ -8,11 +8,11 @@ enum SubGhzSettingIndex { SubGhzSettingIndexBinRAW, SubGhzSettingIndexSound, SubGhzSettingIndexLock, - SubGhzSettingIndexRAWThesholdRSSI, + SubGhzSettingIndexRAWThresholdRSSI, }; #define RAW_THRESHOLD_RSSI_COUNT 11 -const char* const raw_theshold_rssi_text[RAW_THRESHOLD_RSSI_COUNT] = { +const char* const raw_threshold_rssi_text[RAW_THRESHOLD_RSSI_COUNT] = { "-----", "-85.0", "-80.0", @@ -26,7 +26,7 @@ const char* const raw_theshold_rssi_text[RAW_THRESHOLD_RSSI_COUNT] = { "-40.0", }; -const float raw_theshold_rssi_value[RAW_THRESHOLD_RSSI_COUNT] = { +const float raw_threshold_rssi_value[RAW_THRESHOLD_RSSI_COUNT] = { -90.0f, -85.0f, -80.0f, @@ -47,7 +47,7 @@ const char* const hopping_text[HOPPING_COUNT] = { }; const uint32_t hopping_value[HOPPING_COUNT] = { SubGhzHopperStateOFF, - SubGhzHopperStateRunnig, + SubGhzHopperStateRunning, }; #define SPEAKER_COUNT 2 @@ -213,8 +213,8 @@ static void subghz_scene_receiver_config_set_raw_threshold_rssi(VariableItem* it SubGhz* subghz = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); - variable_item_set_current_value_text(item, raw_theshold_rssi_text[index]); - subghz->txrx->raw_threshold_rssi = raw_theshold_rssi_value[index]; + variable_item_set_current_value_text(item, raw_threshold_rssi_text[index]); + subghz->txrx->raw_threshold_rssi = raw_threshold_rssi_value[index]; } static void subghz_scene_receiver_config_var_list_enter_callback(void* context, uint32_t index) { @@ -320,9 +320,9 @@ void subghz_scene_receiver_config_on_enter(void* context) { subghz_scene_receiver_config_set_raw_threshold_rssi, subghz); value_index = value_index_float( - subghz->txrx->raw_threshold_rssi, raw_theshold_rssi_value, RAW_THRESHOLD_RSSI_COUNT); + subghz->txrx->raw_threshold_rssi, raw_threshold_rssi_value, RAW_THRESHOLD_RSSI_COUNT); variable_item_set_current_value_index(item, value_index); - variable_item_set_current_value_text(item, raw_theshold_rssi_text[value_index]); + variable_item_set_current_value_text(item, raw_threshold_rssi_text[value_index]); } view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdVariableItemList); } diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_info.c b/applications/main/subghz/scenes/subghz_scene_receiver_info.c index 0b265cca2..90046eb9d 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver_info.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver_info.c @@ -2,6 +2,9 @@ #include "../helpers/subghz_custom_event.h" #include #include +#include +#include +#include void subghz_scene_receiver_info_callback(GuiButtonType result, InputType type, void* context) { furi_assert(context); @@ -24,8 +27,9 @@ static bool subghz_scene_receiver_info_update_parser(void* context) { subghz->txrx->decoder_result = subghz_receiver_search_decoder_base_by_name( subghz->txrx->receiver, subghz_history_get_protocol_name(subghz->txrx->history, subghz->txrx->idx_menu_chosen)); + if(subghz->txrx->decoder_result) { - // In this case flipper format was changed to short file content + //todo we are trying to deserialize without checking for errors, since it is assumed that we just received this chignal subghz_protocol_decoder_base_deserialize( subghz->txrx->decoder_result, subghz_history_get_raw_data(subghz->txrx->history, subghz->txrx->idx_menu_chosen)); @@ -135,7 +139,6 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event) subghz, subghz_history_get_raw_data( subghz->txrx->history, subghz->txrx->idx_menu_chosen))) { - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx); if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { subghz_tx_stop(subghz); } @@ -148,7 +151,7 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event) subghz_rx(subghz, subghz->txrx->preset->frequency); } if(subghz->txrx->hopper_state == SubGhzHopperStatePause) { - subghz->txrx->hopper_state = SubGhzHopperStateRunnig; + subghz->txrx->hopper_state = SubGhzHopperStateRunning; } subghz->state_notifications = SubGhzNotificationStateRx; } else { @@ -175,7 +178,7 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event) subghz_rx(subghz, subghz->txrx->preset->frequency); } if(subghz->txrx->hopper_state == SubGhzHopperStatePause) { - subghz->txrx->hopper_state = SubGhzHopperStateRunnig; + subghz->txrx->hopper_state = SubGhzHopperStateRunning; } subghz->state_notifications = SubGhzNotificationStateRx; } @@ -233,6 +236,10 @@ void subghz_scene_receiver_info_on_exit(void* context) { widget_reset(subghz->widget); keeloq_reset_mfname(); keeloq_reset_kl_type(); + keeloq_reset_original_btn(); + alutech_reset_original_btn(); + nice_flors_reset_original_btn(); + somfy_telis_reset_original_btn(); star_line_reset_mfname(); star_line_reset_kl_type(); } diff --git a/applications/main/subghz/scenes/subghz_scene_rpc.c b/applications/main/subghz/scenes/subghz_scene_rpc.c index 609b4a71d..b9585ec36 100644 --- a/applications/main/subghz/scenes/subghz_scene_rpc.c +++ b/applications/main/subghz/scenes/subghz_scene_rpc.c @@ -1,6 +1,10 @@ #include "../subghz_i.h" #include #include +#include +#include +#include + #include "xtreme/assets.h" typedef enum { @@ -110,6 +114,10 @@ void subghz_scene_rpc_on_exit(void* context) { keeloq_reset_mfname(); keeloq_reset_kl_type(); + keeloq_reset_original_btn(); + alutech_reset_original_btn(); + nice_flors_reset_original_btn(); + somfy_telis_reset_original_btn(); star_line_reset_mfname(); star_line_reset_kl_type(); } diff --git a/applications/main/subghz/scenes/subghz_scene_set_seed_bft.c b/applications/main/subghz/scenes/subghz_scene_set_seed_bft.c index 1d6f9d70b..2c48462ef 100644 --- a/applications/main/subghz/scenes/subghz_scene_set_seed_bft.c +++ b/applications/main/subghz/scenes/subghz_scene_set_seed_bft.c @@ -14,7 +14,7 @@ void subghz_scene_set_seed_bft_on_enter(void* context) { SubGhz* subghz = context; // Setup view - // roguemaster don't steal!!! + // RogueMaster don't steal!!! ByteInput* byte_input = subghz->byte_input; byte_input_set_header_text(byte_input, "Enter SEED in hex"); byte_input_set_result_callback( diff --git a/applications/main/subghz/scenes/subghz_scene_set_seed_faac.c b/applications/main/subghz/scenes/subghz_scene_set_seed_faac.c index 8e955e4b7..55387a0a5 100644 --- a/applications/main/subghz/scenes/subghz_scene_set_seed_faac.c +++ b/applications/main/subghz/scenes/subghz_scene_set_seed_faac.c @@ -65,7 +65,7 @@ bool subghz_scene_set_seed_faac_on_event(void* context, SceneManagerEvent event) seed, "FAAC_SLH", subghz->txrx->preset); - // rogueemaster dont steal! + // RogueMaster dont steal! uint8_t seed_data[sizeof(uint32_t)] = {0}; for(size_t i = 0; i < sizeof(uint32_t); i++) { seed_data[sizeof(uint32_t) - i - 1] = (seed >> i * 8) & 0xFF; diff --git a/applications/main/subghz/scenes/subghz_scene_set_type.c b/applications/main/subghz/scenes/subghz_scene_set_type.c index 95cb1ec03..bc76ee146 100644 --- a/applications/main/subghz/scenes/subghz_scene_set_type.c +++ b/applications/main/subghz/scenes/subghz_scene_set_type.c @@ -36,8 +36,9 @@ bool subghz_scene_set_type_submenu_gen_data_protocol( do { Stream* fff_data_stream = flipper_format_get_raw_stream(subghz->txrx->fff_data); stream_clean(fff_data_stream); - if(!subghz_protocol_decoder_base_serialize( - subghz->txrx->decoder_result, subghz->txrx->fff_data, subghz->txrx->preset)) { + if(subghz_protocol_decoder_base_serialize( + subghz->txrx->decoder_result, subghz->txrx->fff_data, subghz->txrx->preset) != + SubGhzProtocolStatusOk) { FURI_LOG_E(TAG, "Unable to serialize"); break; } @@ -79,10 +80,22 @@ void subghz_scene_set_type_on_enter(void* context) { SubmenuIndexFaacSLH_433, subghz_scene_set_type_submenu_callback, subghz); + submenu_add_item( + subghz->submenu, + "BFT [Manual] 433MHz", + SubmenuIndexBFTClone, + subghz_scene_set_type_submenu_callback, + subghz); submenu_add_item( subghz->submenu, "BFT Mitto 433MHz", - SubmenuIndexBFT, + SubmenuIndexBFTMitto, + subghz_scene_set_type_submenu_callback, + subghz); + submenu_add_item( + subghz->submenu, + "Somfy Telis 433MHz", + SubmenuIndexSomfyTelis, subghz_scene_set_type_submenu_callback, subghz); submenu_add_item( @@ -115,6 +128,12 @@ void subghz_scene_set_type_on_enter(void* context) { SubmenuIndexNiceFlorS_433_92, subghz_scene_set_type_submenu_callback, subghz); + submenu_add_item( + subghz->submenu, + "Nice One 433MHz", + SubmenuIndexNiceOne_433_92, + subghz_scene_set_type_submenu_callback, + subghz); submenu_add_item( subghz->submenu, "CAME 12bit 433MHz", @@ -230,7 +249,7 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { case SubmenuIndexFaacSLH_433: scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSetFixFaac); break; - case SubmenuIndexBFT: + case SubmenuIndexBFTClone: scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSetFixBft); break; case SubmenuIndexPricenton: @@ -306,11 +325,69 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { generated_protocol = true; } break; + case SubmenuIndexBFTMitto: + subghz->txrx->transmitter = subghz_transmitter_alloc_init( + subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME); + subghz_preset_init(subghz, "AM650", 433920000, NULL, 0); + if(subghz->txrx->transmitter) { + subghz_protocol_keeloq_bft_create_data( + subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter), + subghz->txrx->fff_data, + key & 0x000FFFFF, + 0x2, + 0x0002, + key & 0x000FFFFF, + "BFT", + subghz->txrx->preset); + + uint8_t seed_data[sizeof(uint32_t)] = {0}; + for(size_t i = 0; i < sizeof(uint32_t); i++) { + seed_data[sizeof(uint32_t) - i - 1] = ((key & 0x000FFFFF) >> i * 8) & 0xFF; + } + + flipper_format_write_hex( + subghz->txrx->fff_data, "Seed", seed_data, sizeof(uint32_t)); + + flipper_format_write_string_cstr(subghz->txrx->fff_data, "Manufacture", "BFT"); + + generated_protocol = true; + } else { + generated_protocol = false; + } + subghz_transmitter_free(subghz->txrx->transmitter); + if(!generated_protocol) { + furi_string_set( + subghz->error_str, "Function requires\nan SD card with\nfresh databases."); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); + } + break; + case SubmenuIndexSomfyTelis: + subghz->txrx->transmitter = subghz_transmitter_alloc_init( + subghz->txrx->environment, SUBGHZ_PROTOCOL_SOMFY_TELIS_NAME); + subghz_preset_init(subghz, "AM650", 433920000, NULL, 0); + if(subghz->txrx->transmitter) { + subghz_protocol_somfy_telis_create_data( + subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter), + subghz->txrx->fff_data, + key & 0x00FFFFFF, + 0x2, + 0x0003, + subghz->txrx->preset); + generated_protocol = true; + } else { + generated_protocol = false; + } + subghz_transmitter_free(subghz->txrx->transmitter); + if(!generated_protocol) { + furi_string_set( + subghz->error_str, "Function requires\nan SD card with\nfresh databases."); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); + } + break; case SubmenuIndexDoorHan_433_92: subghz->txrx->transmitter = subghz_transmitter_alloc_init( subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME); - subghz_preset_init( - subghz, "AM650", subghz_setting_get_default_frequency(subghz->setting), NULL, 0); + subghz_preset_init(subghz, "AM650", 433920000, NULL, 0); if(subghz->txrx->transmitter) { subghz_protocol_keeloq_create_data( subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter), @@ -358,8 +435,7 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { case SubmenuIndexNiceFlorS_433_92: subghz->txrx->transmitter = subghz_transmitter_alloc_init( subghz->txrx->environment, SUBGHZ_PROTOCOL_NICE_FLOR_S_NAME); - subghz_preset_init( - subghz, "AM650", subghz_setting_get_default_frequency(subghz->setting), NULL, 0); + subghz_preset_init(subghz, "AM650", 433920000, NULL, 0); if(subghz->txrx->transmitter) { subghz_protocol_nice_flor_s_create_data( subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter), @@ -367,7 +443,32 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { key & 0x0FFFFFFF, 0x1, 0x0003, - subghz->txrx->preset); + subghz->txrx->preset, + false); + generated_protocol = true; + } else { + generated_protocol = false; + } + subghz_transmitter_free(subghz->txrx->transmitter); + if(!generated_protocol) { + furi_string_set( + subghz->error_str, "Function requires\nan SD card with\nfresh databases."); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); + } + break; + case SubmenuIndexNiceOne_433_92: + subghz->txrx->transmitter = subghz_transmitter_alloc_init( + subghz->txrx->environment, SUBGHZ_PROTOCOL_NICE_FLOR_S_NAME); + subghz_preset_init(subghz, "AM650", 433920000, NULL, 0); + if(subghz->txrx->transmitter) { + subghz_protocol_nice_flor_s_create_data( + subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter), + subghz->txrx->fff_data, + key & 0x0FFFFFFF, + 0x1, + 0x0003, + subghz->txrx->preset, + true); generated_protocol = true; } else { generated_protocol = false; @@ -382,8 +483,7 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { case SubmenuIndexNiceSmilo_433_92: subghz->txrx->transmitter = subghz_transmitter_alloc_init( subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME); - subghz_preset_init( - subghz, "AM650", subghz_setting_get_default_frequency(subghz->setting), NULL, 0); + subghz_preset_init(subghz, "AM650", 433920000, NULL, 0); if(subghz->txrx->transmitter) { subghz_protocol_keeloq_create_data( subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter), diff --git a/applications/main/subghz/scenes/subghz_scene_start.c b/applications/main/subghz/scenes/subghz_scene_start.c index 69e6cbea7..fd31353e0 100644 --- a/applications/main/subghz/scenes/subghz_scene_start.c +++ b/applications/main/subghz/scenes/subghz_scene_start.c @@ -75,43 +75,46 @@ bool subghz_scene_start_on_event(void* context, SceneManagerEvent event) { subghz->scene_manager, SubGhzSceneStart, SubmenuIndexExtSettings); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneExtModuleSettings); return true; - - } else if(!furi_hal_subghz_check_radio()) { - furi_string_set(subghz->error_str, "Please connect\nexternal radio"); - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowErrorSub); - return true; - } else if(event.event == SubmenuIndexReadRAW) { - scene_manager_set_scene_state( - subghz->scene_manager, SubGhzSceneStart, SubmenuIndexReadRAW); - subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReadRAW); - return true; - } else if(event.event == SubmenuIndexRead) { - scene_manager_set_scene_state( - subghz->scene_manager, SubGhzSceneStart, SubmenuIndexRead); - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiver); - return true; - } else if(event.event == SubmenuIndexSaved) { - scene_manager_set_scene_state( - subghz->scene_manager, SubGhzSceneStart, SubmenuIndexSaved); - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaved); - return true; } else if(event.event == SubmenuIndexAddManually) { scene_manager_set_scene_state( subghz->scene_manager, SubGhzSceneStart, SubmenuIndexAddManually); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSetType); return true; - } else if(event.event == SubmenuIndexFrequencyAnalyzer) { - scene_manager_set_scene_state( - subghz->scene_manager, SubGhzSceneStart, SubmenuIndexFrequencyAnalyzer); - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneFrequencyAnalyzer); - DOLPHIN_DEED(DolphinDeedSubGhzFrequencyAnalyzer); - return true; - } else if(event.event == SubmenuIndexTest) { - scene_manager_set_scene_state( - subghz->scene_manager, SubGhzSceneStart, SubmenuIndexTest); - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneTest); - return true; + } else { + furi_hal_subghz_enable_ext_power(); + + if(!furi_hal_subghz_check_radio()) { + furi_string_set(subghz->error_str, "Please connect\nexternal radio"); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowErrorSub); + return true; + } else if(event.event == SubmenuIndexReadRAW) { + scene_manager_set_scene_state( + subghz->scene_manager, SubGhzSceneStart, SubmenuIndexReadRAW); + subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReadRAW); + return true; + } else if(event.event == SubmenuIndexRead) { + scene_manager_set_scene_state( + subghz->scene_manager, SubGhzSceneStart, SubmenuIndexRead); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiver); + return true; + } else if(event.event == SubmenuIndexSaved) { + scene_manager_set_scene_state( + subghz->scene_manager, SubGhzSceneStart, SubmenuIndexSaved); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaved); + return true; + } else if(event.event == SubmenuIndexFrequencyAnalyzer) { + scene_manager_set_scene_state( + subghz->scene_manager, SubGhzSceneStart, SubmenuIndexFrequencyAnalyzer); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneFrequencyAnalyzer); + DOLPHIN_DEED(DolphinDeedSubGhzFrequencyAnalyzer); + return true; + } else if(event.event == SubmenuIndexTest) { + scene_manager_set_scene_state( + subghz->scene_manager, SubGhzSceneStart, SubmenuIndexTest); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneTest); + return true; + } } } return false; diff --git a/applications/main/subghz/scenes/subghz_scene_transmitter.c b/applications/main/subghz/scenes/subghz_scene_transmitter.c index dff4f8384..34685a8e5 100644 --- a/applications/main/subghz/scenes/subghz_scene_transmitter.c +++ b/applications/main/subghz/scenes/subghz_scene_transmitter.c @@ -2,7 +2,10 @@ #include "../views/transmitter.h" #include #include +#include #include +#include +#include void subghz_scene_transmitter_callback(SubGhzCustomEvent event, void* context) { furi_assert(context); @@ -11,9 +14,8 @@ void subghz_scene_transmitter_callback(SubGhzCustomEvent event, void* context) { } bool subghz_scene_transmitter_update_data_show(void* context) { - //ToDo Fix SubGhz* subghz = context; - + bool ret = false; if(subghz->txrx->decoder_result) { FuriString* key_str; FuriString* frequency_str; @@ -24,30 +26,29 @@ bool subghz_scene_transmitter_update_data_show(void* context) { modulation_str = furi_string_alloc(); uint8_t show_button = 0; - subghz_protocol_decoder_base_deserialize( - subghz->txrx->decoder_result, subghz->txrx->fff_data); - subghz_protocol_decoder_base_get_string(subghz->txrx->decoder_result, key_str); + if(subghz_protocol_decoder_base_deserialize( + subghz->txrx->decoder_result, subghz->txrx->fff_data) == SubGhzProtocolStatusOk) { + subghz_protocol_decoder_base_get_string(subghz->txrx->decoder_result, key_str); - if((subghz->txrx->decoder_result->protocol->flag & SubGhzProtocolFlag_Send) == - SubGhzProtocolFlag_Send) { - show_button = 1; + if((subghz->txrx->decoder_result->protocol->flag & SubGhzProtocolFlag_Send) == + SubGhzProtocolFlag_Send) { + show_button = 1; + } + + subghz_get_frequency_modulation(subghz, frequency_str, modulation_str); + subghz_view_transmitter_add_data_to_show( + subghz->subghz_transmitter, + furi_string_get_cstr(key_str), + furi_string_get_cstr(frequency_str), + furi_string_get_cstr(modulation_str), + show_button); + ret = true; } - - subghz_get_frequency_modulation(subghz, frequency_str, modulation_str); - subghz_view_transmitter_add_data_to_show( - subghz->subghz_transmitter, - furi_string_get_cstr(key_str), - furi_string_get_cstr(frequency_str), - furi_string_get_cstr(modulation_str), - show_button); - furi_string_free(frequency_str); furi_string_free(modulation_str); furi_string_free(key_str); - - return true; } - return false; + return ret; } void subghz_scene_transmitter_on_enter(void* context) { @@ -89,6 +90,27 @@ bool subghz_scene_transmitter_on_event(void* context, SceneManagerEvent event) { subghz_tx_stop(subghz); subghz_sleep(subghz); } + if(keeloq_get_custom_btn() != 0) { + keeloq_set_btn(0); + alutech_set_btn(0); + nice_flors_set_btn(0); + somfy_telis_set_btn(0); + uint8_t tmp_counter = furi_hal_subghz_get_rolling_counter_mult(); + furi_hal_subghz_set_rolling_counter_mult(0); + // Calling restore! + if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { + subghz_rx_end(subghz); + } + if((subghz->txrx->txrx_state == SubGhzTxRxStateIDLE) || + (subghz->txrx->txrx_state == SubGhzTxRxStateSleep)) { + if(!subghz_tx_start(subghz, subghz->txrx->fff_data)) { + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx); + } + } + subghz_tx_stop(subghz); + subghz_sleep(subghz); + furi_hal_subghz_set_rolling_counter_mult(tmp_counter); + } return true; } else if(event.event == SubGhzCustomEventViewTransmitterBack) { subghz->state_notifications = SubGhzNotificationStateIDLE; @@ -113,6 +135,10 @@ void subghz_scene_transmitter_on_exit(void* context) { subghz->state_notifications = SubGhzNotificationStateIDLE; keeloq_reset_mfname(); keeloq_reset_kl_type(); + keeloq_reset_original_btn(); + alutech_reset_original_btn(); + nice_flors_reset_original_btn(); + somfy_telis_reset_original_btn(); star_line_reset_mfname(); star_line_reset_kl_type(); } diff --git a/applications/main/subghz/subghz.c b/applications/main/subghz/subghz.c index 78295f08c..e2b484120 100644 --- a/applications/main/subghz/subghz.c +++ b/applications/main/subghz/subghz.c @@ -275,7 +275,7 @@ SubGhz* subghz_alloc(bool alloc_for_tx_only) { subghz->txrx->history = subghz_history_alloc(); } - subghz->txrx->raw_threshold_rssi = SUBGHZ_RAW_TRESHOLD_MIN; + subghz->txrx->raw_threshold_rssi = SUBGHZ_RAW_THRESHOLD_MIN; subghz->txrx->worker = subghz_worker_alloc(); subghz->txrx->fff_data = flipper_format_string_alloc(); @@ -421,6 +421,9 @@ void subghz_free(SubGhz* subghz, bool alloc_for_tx_only) { // The rest free(subghz); + + // Disable power for External CC1101 if it was enabled and module is connected + furi_hal_subghz_disable_ext_power(); } int32_t subghz_app(void* p) { diff --git a/applications/main/subghz/subghz_history.c b/applications/main/subghz/subghz_history.c index 184146698..f6530238e 100644 --- a/applications/main/subghz/subghz_history.c +++ b/applications/main/subghz/subghz_history.c @@ -88,6 +88,28 @@ void subghz_history_reset(SubGhzHistory* instance) { instance->code_last_hash_data = 0; } +void subghz_history_delete_item(SubGhzHistory* instance, uint16_t item_id) { + furi_assert(instance); + + SubGhzHistoryItemArray_it_t it; + //SubGhzHistoryItem* target_item = SubGhzHistoryItemArray_get(instance->history->data, item_id); + SubGhzHistoryItemArray_it_last(it, instance->history->data); + while(!SubGhzHistoryItemArray_end_p(it)) { + SubGhzHistoryItem* item = SubGhzHistoryItemArray_ref(it); + + if(it->index == (size_t)(item_id)) { + furi_string_free(item->item_str); + furi_string_free(item->preset->name); + free(item->preset); + flipper_format_free(item->flipper_string); + item->type = 0; + SubGhzHistoryItemArray_remove(instance->history->data, it); + } + SubGhzHistoryItemArray_previous(it); + } + instance->last_index_write--; +} + uint16_t subghz_history_get_item(SubGhzHistory* instance) { furi_assert(instance); return instance->last_index_write; diff --git a/applications/main/subghz/subghz_history.h b/applications/main/subghz/subghz_history.h index 607dbeae2..1f2f8d246 100644 --- a/applications/main/subghz/subghz_history.h +++ b/applications/main/subghz/subghz_history.h @@ -27,6 +27,8 @@ void subghz_history_free(SubGhzHistory* instance); */ void subghz_history_reset(SubGhzHistory* instance); +void subghz_history_delete_item(SubGhzHistory* instance, uint16_t item_id); + /** Get frequency to history[idx] * * @param instance - SubGhzHistory instance @@ -80,7 +82,7 @@ void subghz_history_get_text_item_menu(SubGhzHistory* instance, FuriString* outp * * @param instance - SubGhzHistory instance * @param output - FuriString* output - * @return bool - is FUUL + * @return bool - is FULL */ bool subghz_history_get_text_space_left(SubGhzHistory* instance, FuriString* output); diff --git a/applications/main/subghz/subghz_history_private.h b/applications/main/subghz/subghz_history_private.h index 7c554621b..12ce28fff 100644 --- a/applications/main/subghz/subghz_history_private.h +++ b/applications/main/subghz/subghz_history_private.h @@ -92,7 +92,7 @@ uint32_t subghz_history_rand_range(uint32_t min, uint32_t max); * * @param file - Stream* * @param is_negative_start - first value is negative or positive? - * @param current_position - 0 if started from begining + * @param current_position - 0 if started from beginning * @param empty_line - add RAW_Data to this line * @return true * @return false @@ -160,4 +160,4 @@ bool subghz_history_stream_seek_to_key(Stream* stream, const char* key, bool str * @return true * @return false */ -bool subghz_history_stream_read_value(Stream* stream, FuriString* value, bool* last); \ No newline at end of file +bool subghz_history_stream_read_value(Stream* stream, FuriString* value, bool* last); diff --git a/applications/main/subghz/subghz_i.c b/applications/main/subghz/subghz_i.c index 1fbe662ed..579a16520 100644 --- a/applications/main/subghz/subghz_i.c +++ b/applications/main/subghz/subghz_i.c @@ -154,7 +154,6 @@ bool subghz_tx_start(SubGhz* subghz, FlipperFormat* flipper_format) { FURI_LOG_E(TAG, "Missing Protocol"); break; } - //ToDo FIX if(!flipper_format_insert_or_update_uint32(flipper_format, "Repeat", &repeat, 1)) { FURI_LOG_E(TAG, "Unable Repeat"); break; @@ -164,7 +163,8 @@ bool subghz_tx_start(SubGhz* subghz, FlipperFormat* flipper_format) { subghz->txrx->environment, furi_string_get_cstr(temp_str)); if(subghz->txrx->transmitter) { - if(subghz_transmitter_deserialize(subghz->txrx->transmitter, flipper_format)) { + if(subghz_transmitter_deserialize(subghz->txrx->transmitter, flipper_format) == + SubGhzProtocolStatusOk) { if(strcmp(furi_string_get_cstr(subghz->txrx->preset->name), "") != 0) { subghz_begin( subghz, @@ -187,7 +187,12 @@ bool subghz_tx_start(SubGhz* subghz, FlipperFormat* flipper_format) { //Start TX furi_hal_subghz_start_async_tx( subghz_transmitter_yield, subghz->txrx->transmitter); + } else { + subghz_dialog_message_show_only_rx(subghz); } + } else { + dialog_message_show_storage_error( + subghz->dialogs, "Error in protocol\nparameters\ndescription"); } } if(!ret) { @@ -335,8 +340,10 @@ bool subghz_key_load(SubGhz* subghz, const char* file_path, bool show_dialog) { subghz->txrx->decoder_result = subghz_receiver_search_decoder_base_by_name( subghz->txrx->receiver, furi_string_get_cstr(temp_str)); if(subghz->txrx->decoder_result) { - if(!subghz_protocol_decoder_base_deserialize( - subghz->txrx->decoder_result, subghz->txrx->fff_data)) { + SubGhzProtocolStatus status = subghz_protocol_decoder_base_deserialize( + subghz->txrx->decoder_result, subghz->txrx->fff_data); + if(status != SubGhzProtocolStatusOk) { + load_key_state = SubGhzLoadKeyStateProtocolDescriptionErr; break; } } else { @@ -357,6 +364,12 @@ bool subghz_key_load(SubGhz* subghz, const char* file_path, bool show_dialog) { dialog_message_show_storage_error(subghz->dialogs, "Cannot parse\nfile"); } return false; + case SubGhzLoadKeyStateProtocolDescriptionErr: + if(show_dialog) { + dialog_message_show_storage_error( + subghz->dialogs, "Error in protocol\nparameters\ndescription"); + } + return false; case SubGhzLoadKeyStateOnlyRx: if(show_dialog) { @@ -575,7 +588,7 @@ void subghz_hopper_update(SubGhz* subghz) { return; } } else { - subghz->txrx->hopper_state = SubGhzHopperStateRunnig; + subghz->txrx->hopper_state = SubGhzHopperStateRunning; } // Select next frequency if(subghz->txrx->hopper_idx_frequency < @@ -598,7 +611,7 @@ void subghz_hopper_update(SubGhz* subghz) { void subghz_speaker_on(SubGhz* subghz) { if(subghz->txrx->debug_pin_state) { - furi_hal_subghz_set_async_mirror_pin(&gpio_ext_pa7); + furi_hal_subghz_set_async_mirror_pin(&ibutton_gpio); } if(subghz->txrx->speaker_state == SubGhzSpeakerStateEnable) { @@ -643,7 +656,7 @@ void subghz_speaker_mute(SubGhz* subghz) { void subghz_speaker_unmute(SubGhz* subghz) { if(subghz->txrx->debug_pin_state) { - furi_hal_subghz_set_async_mirror_pin(&gpio_ext_pa7); + furi_hal_subghz_set_async_mirror_pin(&ibutton_gpio); } if(subghz->txrx->speaker_state == SubGhzSpeakerStateEnable) { if(furi_hal_speaker_is_mine()) { diff --git a/applications/main/subghz/views/receiver.c b/applications/main/subghz/views/receiver.c index fa3569245..2a432deb5 100644 --- a/applications/main/subghz/views/receiver.c +++ b/applications/main/subghz/views/receiver.c @@ -14,7 +14,7 @@ #define MENU_ITEMS 4u #define UNLOCK_CNT 3 -#define SUBGHZ_RAW_TRESHOLD_MIN -90.0f +#define SUBGHZ_RAW_THRESHOLD_MIN -90.0f typedef struct { FuriString* item_str; @@ -82,10 +82,10 @@ void subghz_receiver_rssi(SubGhzViewReceiver* instance, float rssi) { instance->view, SubGhzViewReceiverModel * model, { - if(rssi < SUBGHZ_RAW_TRESHOLD_MIN) { + if(rssi < SUBGHZ_RAW_THRESHOLD_MIN) { model->u_rssi = 0; } else { - model->u_rssi = (uint8_t)(rssi - SUBGHZ_RAW_TRESHOLD_MIN); + model->u_rssi = (uint8_t)(rssi - SUBGHZ_RAW_THRESHOLD_MIN); } }, true); @@ -261,13 +261,23 @@ void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) { if(model->history_item == 0) { if(model->mode == SubGhzViewReceiverModeLive) { - canvas_draw_icon(canvas, 0, 0, XTREME_ASSETS()->I_Scanning_123x52); + canvas_draw_icon( + canvas, + 0, + 0, + furi_hal_subghz_get_radio_type() ? XTREME_ASSETS()->I_Fishing_123x52 : + XTREME_ASSETS()->I_Scanning_123x52); canvas_set_font(canvas, FontPrimary); canvas_draw_str(canvas, 63, 46, "Scanning..."); //canvas_draw_line(canvas, 46, 51, 125, 51); canvas_set_font(canvas, FontSecondary); } else { - canvas_draw_icon(canvas, 0, 0, XTREME_ASSETS()->I_Scanning_123x52); + canvas_draw_icon( + canvas, + 0, + 0, + furi_hal_subghz_get_radio_type() ? XTREME_ASSETS()->I_Fishing_123x52 : + XTREME_ASSETS()->I_Scanning_123x52); canvas_set_font(canvas, FontPrimary); canvas_draw_str(canvas, 63, 46, "Decoding..."); canvas_set_font(canvas, FontSecondary); @@ -421,6 +431,34 @@ bool subghz_view_receiver_input(InputEvent* event, void* context) { true); } else if(event->key == InputKeyLeft && event->type == InputTypeShort) { subghz_receiver->callback(SubGhzCustomEventViewReceiverConfig, subghz_receiver->context); + } else if(event->key == InputKeyRight && event->type == InputTypeLong) { + with_view_model( + subghz_receiver->view, + SubGhzViewReceiverModel * model, + { + if(model->history_item != 0) { + SubGhzReceiverMenuItemArray_it_t it; + // SubGhzReceiverMenuItem* target_item = + // SubGhzReceiverMenuItemArray_get(model->history->data, model->idx); + SubGhzReceiverMenuItemArray_it_last(it, model->history->data); + while(!SubGhzReceiverMenuItemArray_end_p(it)) { + SubGhzReceiverMenuItem* item = SubGhzReceiverMenuItemArray_ref(it); + + if(it->index == (size_t)(model->idx)) { + furi_string_free(item->item_str); + item->type = 0; + SubGhzReceiverMenuItemArray_remove(model->history->data, it); + } + + SubGhzReceiverMenuItemArray_previous(it); + } + + // Callback + subghz_receiver->callback( + SubGhzCustomEventViewReceiverDeleteItem, subghz_receiver->context); + } + }, + true); } else if(event->key == InputKeyOk && event->type == InputTypeShort) { with_view_model( subghz_receiver->view, @@ -534,12 +572,34 @@ View* subghz_view_receiver_get_view(SubGhzViewReceiver* subghz_receiver) { uint16_t subghz_view_receiver_get_idx_menu(SubGhzViewReceiver* subghz_receiver) { furi_assert(subghz_receiver); - uint32_t idx = 0; + uint16_t idx = 0; with_view_model( subghz_receiver->view, SubGhzViewReceiverModel * model, { idx = model->idx; }, false); return idx; } +void subghz_view_receiver_delete_element_callback(SubGhzViewReceiver* subghz_receiver) { + furi_assert(subghz_receiver); + + with_view_model( + subghz_receiver->view, + SubGhzViewReceiverModel * model, + { + if(model->history_item == 5) { + if(model->idx >= 2) { + model->idx = model->history_item - 1; + } + } + model->history_item--; + + if(model->idx != 0) { + model->idx--; + } + }, + true); + furi_delay_ms(200); +} + void subghz_view_receiver_set_idx_menu(SubGhzViewReceiver* subghz_receiver, uint16_t idx) { furi_assert(subghz_receiver); with_view_model( diff --git a/applications/main/subghz/views/receiver.h b/applications/main/subghz/views/receiver.h index 37eb473de..ad8c31eda 100644 --- a/applications/main/subghz/views/receiver.h +++ b/applications/main/subghz/views/receiver.h @@ -46,4 +46,6 @@ uint16_t subghz_view_receiver_get_idx_menu(SubGhzViewReceiver* subghz_receiver); void subghz_view_receiver_set_idx_menu(SubGhzViewReceiver* subghz_receiver, uint16_t idx); +void subghz_view_receiver_delete_element_callback(SubGhzViewReceiver* subghz_receiver); + void subghz_view_receiver_exit(void* context); diff --git a/applications/main/subghz/views/subghz_frequency_analyzer.c b/applications/main/subghz/views/subghz_frequency_analyzer.c index ce2e34297..3b8c37d73 100644 --- a/applications/main/subghz/views/subghz_frequency_analyzer.c +++ b/applications/main/subghz/views/subghz_frequency_analyzer.c @@ -21,13 +21,12 @@ #define MAX_HISTORY 4 static const uint32_t subghz_frequency_list[] = { - 300000000, 302757000, 303875000, 304250000, 307000000, 307500000, 307800000, - 309000000, 310000000, 312000000, 312100000, 313000000, 313850000, 314000000, - 314350000, 314980000, 315000000, 318000000, 330000000, 345000000, 348000000, - 350000000, 387000000, 390000000, 418000000, 433075000, 433220000, 433420000, - 433657070, 433889000, 433920000, 434075000, 434176948, 434390000, 434420000, - 434775000, 438900000, 440175000, 464000000, 467750000, 779000000, 868350000, - 868400000, 868800000, 868950000, 906400000, 915000000, 925000000, 928000000}; + 300000000, 302757000, 303875000, 304250000, 307000000, 307500000, 307800000, 309000000, + 310000000, 312000000, 312100000, 313000000, 313850000, 314000000, 314350000, 314980000, + 315000000, 318000000, 330000000, 345000000, 348000000, 350000000, 387000000, 390000000, + 418000000, 433075000, 433220000, 433420000, 433657070, 433889000, 433920000, 434075000, + 434176948, 434390000, 434420000, 434775000, 438900000, 440175000, 464000000, 779000000, + 868350000, 868400000, 868800000, 868950000, 906400000, 915000000, 925000000, 928000000}; typedef enum { SubGhzFrequencyAnalyzerStatusIDLE, diff --git a/applications/main/subghz/views/subghz_read_raw.c b/applications/main/subghz/views/subghz_read_raw.c index 7ba2f4434..fcc077efa 100644 --- a/applications/main/subghz/views/subghz_read_raw.c +++ b/applications/main/subghz/views/subghz_read_raw.c @@ -23,7 +23,7 @@ typedef struct { FuriString* sample_write; FuriString* file_name; uint8_t* rssi_history; - uint8_t rssi_curret; + uint8_t rssi_current; bool rssi_history_end; uint8_t ind_write; uint8_t ind_sin; @@ -62,17 +62,17 @@ void subghz_read_raw_add_data_rssi(SubGhzReadRAW* instance, float rssi, bool tra furi_assert(instance); uint8_t u_rssi = 0; - if(rssi < SUBGHZ_RAW_TRESHOLD_MIN) { + if(rssi < SUBGHZ_RAW_THRESHOLD_MIN) { u_rssi = 0; } else { - u_rssi = (uint8_t)((rssi - SUBGHZ_RAW_TRESHOLD_MIN) / 2.7); + u_rssi = (uint8_t)((rssi - SUBGHZ_RAW_THRESHOLD_MIN) / 2.7); } with_view_model( instance->view, SubGhzReadRAWModel * model, { - model->rssi_curret = u_rssi; + model->rssi_current = u_rssi; if(trace) { model->rssi_history[model->ind_write++] = u_rssi; } else { @@ -206,10 +206,10 @@ void subghz_read_raw_draw_rssi(Canvas* canvas, SubGhzReadRAWModel* model) { canvas_draw_line(canvas, i, 47, i, 47 - model->rssi_history[i]); } canvas_draw_line( - canvas, model->ind_write + 1, 47, model->ind_write + 1, 47 - model->rssi_curret); + canvas, model->ind_write + 1, 47, model->ind_write + 1, 47 - model->rssi_current); if(model->ind_write > 3) { canvas_draw_line( - canvas, model->ind_write - 1, 47, model->ind_write - 1, 47 - model->rssi_curret); + canvas, model->ind_write - 1, 47, model->ind_write - 1, 47 - model->rssi_current); for(uint8_t i = 13; i < 47; i += width * 2) { canvas_draw_line(canvas, model->ind_write, i, model->ind_write, i + width); @@ -231,13 +231,13 @@ void subghz_read_raw_draw_rssi(Canvas* canvas, SubGhzReadRAWModel* model) { SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE - 1, 47, SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE - 1, - 47 - model->rssi_curret); + 47 - model->rssi_current); canvas_draw_line( canvas, SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE + 1, 47, SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE + 1, - 47 - model->rssi_curret); + 47 - model->rssi_current); for(uint8_t i = 13; i < 47; i += width * 2) { canvas_draw_line( @@ -266,9 +266,9 @@ void subghz_read_raw_draw_threshold_rssi(Canvas* canvas, SubGhzReadRAWModel* mod uint8_t x = 118; uint8_t y = 48; - if(model->raw_threshold_rssi > SUBGHZ_RAW_TRESHOLD_MIN) { + if(model->raw_threshold_rssi > SUBGHZ_RAW_THRESHOLD_MIN) { uint8_t x = 118; - y -= (uint8_t)((model->raw_threshold_rssi - SUBGHZ_RAW_TRESHOLD_MIN) / 2.7); + y -= (uint8_t)((model->raw_threshold_rssi - SUBGHZ_RAW_THRESHOLD_MIN) / 2.7); uint8_t width = 3; for(uint8_t i = 0; i < x; i += width * 2) { diff --git a/applications/main/subghz/views/subghz_read_raw.h b/applications/main/subghz/views/subghz_read_raw.h index 43afac427..9d63870d5 100644 --- a/applications/main/subghz/views/subghz_read_raw.h +++ b/applications/main/subghz/views/subghz_read_raw.h @@ -3,7 +3,7 @@ #include #include "../helpers/subghz_custom_event.h" -#define SUBGHZ_RAW_TRESHOLD_MIN -90.0f +#define SUBGHZ_RAW_THRESHOLD_MIN -90.0f typedef struct SubGhzReadRAW SubGhzReadRAW; diff --git a/applications/main/subghz/views/transmitter.c b/applications/main/subghz/views/transmitter.c index 102639924..e6ed16d81 100644 --- a/applications/main/subghz/views/transmitter.c +++ b/applications/main/subghz/views/transmitter.c @@ -4,6 +4,11 @@ #include #include +#include +#include +#include +#include + struct SubGhzViewTransmitter { View* view; SubGhzViewTransmitterCallback callback; @@ -15,6 +20,8 @@ typedef struct { FuriString* preset_str; FuriString* key_str; uint8_t show_button; + FuriString* temp_button_id; + bool draw_temp_button; } SubGhzViewTransmitterModel; void subghz_view_transmitter_set_callback( @@ -89,6 +96,12 @@ void subghz_view_transmitter_draw(Canvas* canvas, SubGhzViewTransmitterModel* mo canvas_draw_str(canvas, 78, 7, furi_string_get_cstr(model->frequency_str)); canvas_draw_str(canvas, 113, 7, furi_string_get_cstr(model->preset_str)); + if(model->draw_temp_button) { + canvas_set_font(canvas, FontBatteryPercent); + canvas_draw_str(canvas, 117, 40, furi_string_get_cstr(model->temp_button_id)); + canvas_set_font(canvas, FontSecondary); + } + if(model->show_button) { canvas_draw_str(canvas, 58, 62, furi_hal_subghz_get_radio_type() ? "R: Ext" : "R: Int"); subghz_view_transmitter_button_right(canvas, "Send"); @@ -108,7 +121,9 @@ bool subghz_view_transmitter_input(InputEvent* event, void* context) { furi_string_reset(model->frequency_str); furi_string_reset(model->preset_str); furi_string_reset(model->key_str); + furi_string_reset(model->temp_button_id); model->show_button = 0; + model->draw_temp_button = false; }, false); return false; @@ -125,6 +140,14 @@ bool subghz_view_transmitter_input(InputEvent* event, void* context) { true); if(can_be_sent && event->key == InputKeyOk && event->type == InputTypePress) { + with_view_model( + subghz_transmitter->view, + SubGhzViewTransmitterModel * model, + { + furi_string_reset(model->temp_button_id); + model->draw_temp_button = false; + }, + true); subghz_transmitter->callback( SubGhzCustomEventViewTransmitterSendStart, subghz_transmitter->context); return true; @@ -134,6 +157,141 @@ bool subghz_view_transmitter_input(InputEvent* event, void* context) { return true; } + // Temp Buttons (UP) + if(can_be_sent && event->key == InputKeyUp && event->type == InputTypePress) { + keeloq_set_btn(1); + alutech_set_btn(1); + nice_flors_set_btn(1); + somfy_telis_set_btn(1); + with_view_model( + subghz_transmitter->view, + SubGhzViewTransmitterModel * model, + { + furi_string_reset(model->temp_button_id); + if(keeloq_get_original_btn() != 0) { + furi_string_printf(model->temp_button_id, "%01X", keeloq_get_original_btn()); + model->draw_temp_button = true; + } else if(alutech_get_original_btn() != 0) { + furi_string_printf(model->temp_button_id, "%01X", alutech_get_original_btn()); + model->draw_temp_button = true; + } else if(nice_flors_get_original_btn() != 0) { + furi_string_printf( + model->temp_button_id, "%01X", nice_flors_get_original_btn()); + model->draw_temp_button = true; + } else if(somfy_telis_get_original_btn() != 0) { + furi_string_printf( + model->temp_button_id, "%01X", somfy_telis_get_original_btn()); + model->draw_temp_button = true; + } + }, + true); + subghz_transmitter->callback( + SubGhzCustomEventViewTransmitterSendStart, subghz_transmitter->context); + return true; + } else if(can_be_sent && event->key == InputKeyUp && event->type == InputTypeRelease) { + subghz_transmitter->callback( + SubGhzCustomEventViewTransmitterSendStop, subghz_transmitter->context); + return true; + } + // Down + if(can_be_sent && event->key == InputKeyDown && event->type == InputTypePress) { + keeloq_set_btn(2); + alutech_set_btn(2); + nice_flors_set_btn(2); + somfy_telis_set_btn(2); + with_view_model( + subghz_transmitter->view, + SubGhzViewTransmitterModel * model, + { + furi_string_reset(model->temp_button_id); + if(keeloq_get_original_btn() != 0) { + furi_string_printf(model->temp_button_id, "%01X", keeloq_get_original_btn()); + model->draw_temp_button = true; + } else if(alutech_get_original_btn() != 0) { + furi_string_printf(model->temp_button_id, "%01X", alutech_get_original_btn()); + model->draw_temp_button = true; + } else if(nice_flors_get_original_btn() != 0) { + furi_string_printf( + model->temp_button_id, "%01X", nice_flors_get_original_btn()); + model->draw_temp_button = true; + } else if(somfy_telis_get_original_btn() != 0) { + furi_string_printf( + model->temp_button_id, "%01X", somfy_telis_get_original_btn()); + model->draw_temp_button = true; + } + }, + true); + subghz_transmitter->callback( + SubGhzCustomEventViewTransmitterSendStart, subghz_transmitter->context); + return true; + } else if(can_be_sent && event->key == InputKeyDown && event->type == InputTypeRelease) { + subghz_transmitter->callback( + SubGhzCustomEventViewTransmitterSendStop, subghz_transmitter->context); + return true; + } + // Left + if(can_be_sent && event->key == InputKeyLeft && event->type == InputTypePress) { + keeloq_set_btn(3); + alutech_set_btn(3); + nice_flors_set_btn(3); + somfy_telis_set_btn(3); + with_view_model( + subghz_transmitter->view, + SubGhzViewTransmitterModel * model, + { + furi_string_reset(model->temp_button_id); + if(keeloq_get_original_btn() != 0) { + furi_string_printf(model->temp_button_id, "%01X", keeloq_get_original_btn()); + model->draw_temp_button = true; + } else if(alutech_get_original_btn() != 0) { + furi_string_printf(model->temp_button_id, "%01X", alutech_get_original_btn()); + model->draw_temp_button = true; + } else if(nice_flors_get_original_btn() != 0) { + furi_string_printf( + model->temp_button_id, "%01X", nice_flors_get_original_btn()); + model->draw_temp_button = true; + } else if(somfy_telis_get_original_btn() != 0) { + furi_string_printf( + model->temp_button_id, "%01X", somfy_telis_get_original_btn()); + model->draw_temp_button = true; + } + }, + true); + subghz_transmitter->callback( + SubGhzCustomEventViewTransmitterSendStart, subghz_transmitter->context); + return true; + } else if(can_be_sent && event->key == InputKeyLeft && event->type == InputTypeRelease) { + subghz_transmitter->callback( + SubGhzCustomEventViewTransmitterSendStop, subghz_transmitter->context); + return true; + } + // Right + if(can_be_sent && event->key == InputKeyRight && event->type == InputTypePress) { + keeloq_set_btn(4); + alutech_set_btn(4); + with_view_model( + subghz_transmitter->view, + SubGhzViewTransmitterModel * model, + { + furi_string_reset(model->temp_button_id); + if(keeloq_get_original_btn() != 0) { + furi_string_printf(model->temp_button_id, "%01X", keeloq_get_original_btn()); + model->draw_temp_button = true; + } else if(alutech_get_original_btn() != 0) { + furi_string_printf(model->temp_button_id, "%01X", alutech_get_original_btn()); + model->draw_temp_button = true; + } + }, + true); + subghz_transmitter->callback( + SubGhzCustomEventViewTransmitterSendStart, subghz_transmitter->context); + return true; + } else if(can_be_sent && event->key == InputKeyRight && event->type == InputTypeRelease) { + subghz_transmitter->callback( + SubGhzCustomEventViewTransmitterSendStop, subghz_transmitter->context); + return true; + } + return true; } @@ -166,6 +324,7 @@ SubGhzViewTransmitter* subghz_view_transmitter_alloc() { model->frequency_str = furi_string_alloc(); model->preset_str = furi_string_alloc(); model->key_str = furi_string_alloc(); + model->temp_button_id = furi_string_alloc(); }, true); return subghz_transmitter; @@ -181,6 +340,7 @@ void subghz_view_transmitter_free(SubGhzViewTransmitter* subghz_transmitter) { furi_string_free(model->frequency_str); furi_string_free(model->preset_str); furi_string_free(model->key_str); + furi_string_free(model->temp_button_id); }, true); view_free(subghz_transmitter->view); diff --git a/applications/main/unirfremix/application.fam b/applications/main/subghz_remote/application.fam similarity index 55% rename from applications/main/unirfremix/application.fam rename to applications/main/subghz_remote/application.fam index fd3553947..e09f8500f 100644 --- a/applications/main/unirfremix/application.fam +++ b/applications/main/subghz_remote/application.fam @@ -1,14 +1,14 @@ App( - appid="unirfremix", + appid="subghz_remote", name="Sub-GHz Remote", apptype=FlipperAppType.APP, - entry_point="unirfremix_app", - cdefines=["APP_UNIRFREMIX"], + entry_point="subghz_remote_app", + cdefines=["APP_SUBGHZREMOTE"], requires=[ "gui", "dialogs", ], - icon="A_UniRFRemix_14", + icon="A_SubGHzRemote_14", stack_size=4 * 1024, order=11, ) diff --git a/applications/main/unirfremix/unirfremix_app.c b/applications/main/subghz_remote/subghz_remote_app.c similarity index 87% rename from applications/main/unirfremix/unirfremix_app.c rename to applications/main/subghz_remote/subghz_remote_app.c index 2b12a12b2..941cdb889 100644 --- a/applications/main/unirfremix/unirfremix_app.c +++ b/applications/main/subghz_remote/subghz_remote_app.c @@ -19,11 +19,14 @@ #include #include #include +#include +#include +#include -#define UNIRFMAP_FOLDER "/ext/subghz/unirf" -#define UNIRFMAP_EXTENSION ".txt" +#define SUBREMOTEMAP_FOLDER "/ext/subghz/remote" +#define SUBREMOTEMAP_EXTENSION ".txt" -#define TAG "UniRF Remix" +#define TAG "SubGHzRemote" typedef struct { uint32_t frequency; @@ -36,7 +39,7 @@ typedef struct { size_t data_size; SubGhzProtocolDecoderBase* decoder; -} UniRFPreset; +} SubRemotePreset; typedef struct { FuriMutex* model_mutex; @@ -50,7 +53,7 @@ typedef struct { SubGhzEnvironment* environment; SubGhzReceiver* subghz_receiver; NotificationApp* notification; - UniRFPreset* txpreset; + SubRemotePreset* txpreset; FuriString* up_file; FuriString* down_file; @@ -85,17 +88,17 @@ typedef struct { bool tx_not_allowed; FuriString* signal; -} UniRFRemix; +} SubGHzRemote; -UniRFPreset* unirfremix_preset_alloc(void) { - UniRFPreset* preset = malloc(sizeof(UniRFPreset)); +SubRemotePreset* subghz_remote_preset_alloc(void) { + SubRemotePreset* preset = malloc(sizeof(SubRemotePreset)); preset->name = furi_string_alloc(); preset->protocol = furi_string_alloc(); preset->repeat = 200; return preset; } -void unirfremix_preset_free(UniRFPreset* preset) { +void subghz_remote_preset_free(SubRemotePreset* preset) { furi_string_free(preset->name); furi_string_free(preset->protocol); free(preset); @@ -167,7 +170,7 @@ static void cfg_read_file_label( * set error flag if missing map file */ -void unirfremix_cfg_set_check(UniRFRemix* app, FuriString* file_name) { +void subghz_remote_cfg_set_check(SubGHzRemote* app, FuriString* file_name) { Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* fff_data_file = flipper_format_file_alloc(storage); @@ -310,11 +313,11 @@ void unirfremix_cfg_set_check(UniRFRemix* app, FuriString* file_name) { } } -static void unirfremix_end_send(UniRFRemix* app) { +static void subghz_remote_end_send(SubGHzRemote* app) { app->processing = 0; } -bool unirfremix_set_preset(UniRFPreset* p, const char* preset) { +bool subghz_remote_set_preset(SubRemotePreset* p, const char* preset) { if(!strcmp(preset, "FuriHalSubGhzPresetOok270Async")) { furi_string_set(p->name, "AM270"); } else if(!strcmp(preset, "FuriHalSubGhzPresetOok650Async")) { @@ -334,8 +337,8 @@ bool unirfremix_set_preset(UniRFPreset* p, const char* preset) { return true; } -bool unirfremix_key_load( - UniRFPreset* preset, +bool subghz_remote_key_load( + SubRemotePreset* preset, FlipperFormat* fff_file, FlipperFormat* fff_data, SubGhzSetting* setting, @@ -364,7 +367,7 @@ bool unirfremix_key_load( FURI_LOG_W(TAG, "Could not read Preset. Defaulting to Ook650Async"); furi_string_set(temp_str, "FuriHalSubGhzPresetOok650Async"); } - if(!unirfremix_set_preset(preset, furi_string_get_cstr(temp_str))) { + if(!subghz_remote_set_preset(preset, furi_string_get_cstr(temp_str))) { FURI_LOG_E(TAG, "Could not set preset"); break; } @@ -399,7 +402,10 @@ bool unirfremix_key_load( preset->decoder = subghz_receiver_search_decoder_base_by_name( receiver, furi_string_get_cstr(preset->protocol)); if(preset->decoder) { - if(!subghz_protocol_decoder_base_deserialize(preset->decoder, fff_data)) { + SubGhzProtocolStatus status = + subghz_protocol_decoder_base_deserialize(preset->decoder, fff_data); + if(status != SubGhzProtocolStatusOk) { + FURI_LOG_E(TAG, "Protocol deserialize failed, status = %d", status); break; } } else { @@ -416,7 +422,7 @@ bool unirfremix_key_load( // method modified from subghz_i.c // https://github.com/flipperdevices/flipperzero-firmware/blob/b0daa601ad5b87427a45f9089c8b403a01f72c2a/applications/subghz/subghz_i.c#L417-L456 -bool unirfremix_save_protocol_to_file(FlipperFormat* fff_file, const char* dev_file_name) { +bool subghz_remote_save_protocol_to_file(FlipperFormat* fff_file, const char* dev_file_name) { furi_assert(fff_file); furi_assert(dev_file_name); @@ -453,7 +459,7 @@ bool unirfremix_save_protocol_to_file(FlipperFormat* fff_file, const char* dev_f return saved; } -void unirfremix_tx_stop(UniRFRemix* app) { +void subghz_remote_tx_stop(SubGHzRemote* app) { if(app->processing == 0) { return; } @@ -477,10 +483,14 @@ void unirfremix_tx_stop(UniRFRemix* app) { FURI_LOG_D(TAG, "Protocol-TYPE %d", proto->type); if(proto && proto->type == SubGhzProtocolTypeDynamic) { FURI_LOG_D(TAG, "Protocol is dynamic. Saving key"); - unirfremix_save_protocol_to_file(app->tx_fff_data, app->tx_file_path); + subghz_remote_save_protocol_to_file(app->tx_fff_data, app->tx_file_path); keeloq_reset_mfname(); keeloq_reset_kl_type(); + keeloq_reset_original_btn(); + alutech_reset_original_btn(); + nice_flors_reset_original_btn(); + somfy_telis_reset_original_btn(); star_line_reset_mfname(); star_line_reset_kl_type(); } @@ -490,22 +500,22 @@ void unirfremix_tx_stop(UniRFRemix* app) { notification_message(app->notification, &sequence_blink_stop); - unirfremix_preset_free(app->txpreset); + subghz_remote_preset_free(app->txpreset); flipper_format_free(app->tx_fff_data); - unirfremix_end_send(app); + subghz_remote_end_send(app); } -static bool unirfremix_send_sub(UniRFRemix* app, FlipperFormat* fff_data) { +static bool subghz_remote_send_sub(SubGHzRemote* app, FlipperFormat* fff_data) { // bool res = false; do { if(!furi_hal_subghz_is_tx_allowed(app->txpreset->frequency)) { printf( "In your settings, only reception on this frequency (%lu) is allowed,\r\n" - "the actual operation of the unirf app is not possible\r\n ", + "the actual operation of the subghz remote app is not possible\r\n ", app->txpreset->frequency); app->tx_not_allowed = true; - unirfremix_end_send(app); + subghz_remote_end_send(app); break; } else { app->tx_not_allowed = false; @@ -522,13 +532,14 @@ static bool unirfremix_send_sub(UniRFRemix* app, FlipperFormat* fff_data) { furi_hal_subghz_reset(); furi_hal_subghz_idle(); furi_hal_subghz_load_custom_preset(app->txpreset->data); - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(furi_hal_subghz.cc1101_g0_pin, GpioModeInput, GpioPullNo, GpioSpeedLow); furi_hal_subghz_idle(); furi_hal_subghz_set_frequency_and_path(app->txpreset->frequency); - furi_hal_gpio_write(&gpio_cc1101_g0, false); - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_write(furi_hal_subghz.cc1101_g0_pin, false); + furi_hal_gpio_init( + furi_hal_subghz.cc1101_g0_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); if(!furi_hal_subghz_tx()) { FURI_LOG_E(TAG, "Sending not allowed"); @@ -546,14 +557,14 @@ static bool unirfremix_send_sub(UniRFRemix* app, FlipperFormat* fff_data) { return res; } -static void unirfremix_send_signal(UniRFRemix* app, Storage* storage, const char* path) { +static void subghz_remote_send_signal(SubGHzRemote* app, Storage* storage, const char* path) { FURI_LOG_D(TAG, "Sending: %s", path); app->tx_file_path = path; app->tx_fff_data = flipper_format_string_alloc(); - app->txpreset = unirfremix_preset_alloc(); + app->txpreset = subghz_remote_preset_alloc(); // load settings/stream from .sub file FlipperFormat* fff_file = flipper_format_file_alloc(storage); @@ -563,7 +574,7 @@ static void unirfremix_send_signal(UniRFRemix* app, Storage* storage, const char FURI_LOG_E(TAG, "Could not open file %s", path); break; } - if(!unirfremix_key_load( + if(!subghz_remote_key_load( app->txpreset, fff_file, app->tx_fff_data, @@ -581,25 +592,25 @@ static void unirfremix_send_signal(UniRFRemix* app, Storage* storage, const char return; } - unirfremix_send_sub(app, app->tx_fff_data); + subghz_remote_send_sub(app, app->tx_fff_data); } -static void unirfremix_process_signal(UniRFRemix* app, FuriString* signal) { +static void subghz_remote_process_signal(SubGHzRemote* app, FuriString* signal) { view_port_update(app->view_port); FURI_LOG_D(TAG, "signal = %s", furi_string_get_cstr(signal)); if(strlen(furi_string_get_cstr(signal)) > 12) { Storage* storage = furi_record_open(RECORD_STORAGE); - unirfremix_send_signal(app, storage, furi_string_get_cstr(signal)); + subghz_remote_send_signal(app, storage, furi_string_get_cstr(signal)); furi_record_close(RECORD_STORAGE); } else if(strlen(furi_string_get_cstr(signal)) < 10) { - unirfremix_end_send(app); + subghz_remote_end_send(app); } } static void render_callback(Canvas* canvas, void* ctx) { - UniRFRemix* app = ctx; + SubGHzRemote* app = ctx; furi_check(furi_mutex_acquire(app->model_mutex, FuriWaitForever) == FuriStatusOk); //setup different canvas settings @@ -639,10 +650,10 @@ static void render_callback(Canvas* canvas, void* ctx) { //canvas_draw_str(canvas, 0, 40, "D: "); //canvas_draw_str(canvas, 0, 50, "Ok: "); - //PNGs are located in assets/icons/UniRFRemix before compiliation + //PNGs are located in assets/icons/SubGHzRemote before compilation //Icons for Labels - //canvas_draw_icon(canvas, 0, 0, &I_UniRFRemix_LeftAlignedButtons_9x64); + //canvas_draw_icon(canvas, 0, 0, &I_SubGHzRemote_LeftAlignedButtons_9x64); canvas_draw_icon(canvas, 1, 5, &I_ButtonUp_7x4); canvas_draw_icon(canvas, 1, 15, &I_ButtonDown_7x4); canvas_draw_icon(canvas, 2, 23, &I_ButtonLeft_4x7); @@ -691,7 +702,7 @@ static void render_callback(Canvas* canvas, void* ctx) { //Repeat indicator //canvas_draw_str_aligned(canvas, 125, 40, AlignRight, AlignBottom, "Repeat:"); - //canvas_draw_icon(canvas, 115, 39, &I_UniRFRemix_Repeat_12x14); + //canvas_draw_icon(canvas, 115, 39, &I_SubGHzRemote_Repeat_12x14); //canvas_draw_str_aligned(canvas, 125, 62, AlignRight, AlignBottom, int_to_char(app->repeat)); } @@ -699,14 +710,14 @@ static void render_callback(Canvas* canvas, void* ctx) { } static void input_callback(InputEvent* input_event, void* ctx) { - UniRFRemix* app = ctx; + SubGHzRemote* app = ctx; furi_message_queue_put(app->input_queue, input_event, 0); } -void unirfremix_subghz_alloc(UniRFRemix* app) { +void subghz_remote_subghz_alloc(SubGHzRemote* app) { // load subghz presets app->setting = subghz_setting_alloc(); - subghz_setting_load(app->setting, EXT_PATH("subghz/assets/setting_user.txt")); + subghz_setting_load(app->setting, EXT_PATH("subghz/assets/setting_user")); // load mfcodes app->environment = subghz_environment_alloc(); @@ -722,8 +733,11 @@ void unirfremix_subghz_alloc(UniRFRemix* app) { app->subghz_receiver = subghz_receiver_alloc_init(app->environment); } -UniRFRemix* unirfremix_alloc(void) { - UniRFRemix* app = malloc(sizeof(UniRFRemix)); +SubGHzRemote* subghz_remote_alloc(void) { + SubGHzRemote* app = malloc(sizeof(SubGHzRemote)); + + // Enable power for External CC1101 if it is connected + furi_hal_subghz_enable_ext_power(); furi_hal_power_suppress_charge_enter(); @@ -744,9 +758,12 @@ UniRFRemix* unirfremix_alloc(void) { return app; } -void unirfremix_free(UniRFRemix* app, bool with_subghz) { +void subghz_remote_free(SubGHzRemote* app, bool with_subghz) { furi_hal_power_suppress_charge_exit(); + // Disable power for External CC1101 if it was enabled and module is connected + furi_hal_subghz_disable_ext_power(); + furi_string_free(app->up_file); furi_string_free(app->down_file); furi_string_free(app->left_file); @@ -778,9 +795,9 @@ void unirfremix_free(UniRFRemix* app, bool with_subghz) { free(app); } -int32_t unirfremix_app(void* p) { +int32_t subghz_remote_app(void* p) { UNUSED(p); - UniRFRemix* app = unirfremix_alloc(); + SubGHzRemote* app = subghz_remote_alloc(); app->file_path = furi_string_alloc(); app->signal = furi_string_alloc(); @@ -795,33 +812,40 @@ int32_t unirfremix_app(void* p) { app->file_result = 3; Storage* storage = furi_record_open(RECORD_STORAGE); - if(!storage_simply_mkdir(storage, UNIRFMAP_FOLDER)) { - FURI_LOG_E(TAG, "Could not create folder %s", UNIRFMAP_FOLDER); + // Remove old pre-included files to avoid duplicates on migrate + storage_simply_remove(storage, EXT_PATH("subghz/unirf/CVS_Walgreens.txt")); + storage_simply_remove(storage, EXT_PATH("subghz/unirf/Gas_Sign_Edit.txt")); + storage_simply_remove(storage, EXT_PATH("subghz/unirf/Ridin_Dirty.txt")); + storage_simply_remove(storage, EXT_PATH("subghz/unirf/Tesla_Handicap.txt")); + storage_common_migrate(storage, EXT_PATH("subghz/unirf"), SUBREMOTEMAP_FOLDER); + + if(!storage_simply_mkdir(storage, SUBREMOTEMAP_FOLDER)) { + FURI_LOG_E(TAG, "Could not create folder %s", SUBREMOTEMAP_FOLDER); } furi_record_close(RECORD_STORAGE); - furi_string_set(app->file_path, UNIRFMAP_FOLDER); + furi_string_set(app->file_path, SUBREMOTEMAP_FOLDER); DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); DialogsFileBrowserOptions browser_options; - dialog_file_browser_set_basic_options(&browser_options, UNIRFMAP_EXTENSION, &I_sub1_10px); - browser_options.base_path = UNIRFMAP_FOLDER; + dialog_file_browser_set_basic_options(&browser_options, SUBREMOTEMAP_EXTENSION, &I_sub1_10px); + browser_options.base_path = SUBREMOTEMAP_FOLDER; bool res = dialog_file_browser_show(dialogs, app->file_path, app->file_path, &browser_options); furi_record_close(RECORD_DIALOGS); if(!res) { FURI_LOG_E(TAG, "No file selected"); - unirfremix_free(app, false); + subghz_remote_free(app, false); return 255; } else { //check map and population variables - unirfremix_cfg_set_check(app, app->file_path); + subghz_remote_cfg_set_check(app, app->file_path); } // init subghz stuff - unirfremix_subghz_alloc(app); + subghz_remote_subghz_alloc(app); bool exit_loop = false; @@ -871,7 +895,7 @@ int32_t unirfremix_app(void* p) { } if(input.type == InputTypeRelease) { if(app->up_enabled) { - unirfremix_tx_stop(app); + subghz_remote_tx_stop(app); } } break; @@ -889,7 +913,7 @@ int32_t unirfremix_app(void* p) { } if(input.type == InputTypeRelease) { if(app->down_enabled) { - unirfremix_tx_stop(app); + subghz_remote_tx_stop(app); } } break; @@ -907,7 +931,7 @@ int32_t unirfremix_app(void* p) { } if(input.type == InputTypeRelease) { if(app->right_enabled) { - unirfremix_tx_stop(app); + subghz_remote_tx_stop(app); } } break; @@ -925,7 +949,7 @@ int32_t unirfremix_app(void* p) { } if(input.type == InputTypeRelease) { if(app->left_enabled) { - unirfremix_tx_stop(app); + subghz_remote_tx_stop(app); } } break; @@ -943,13 +967,13 @@ int32_t unirfremix_app(void* p) { } if(input.type == InputTypeRelease) { if(app->ok_enabled) { - unirfremix_tx_stop(app); + subghz_remote_tx_stop(app); } } break; case InputKeyBack: - unirfremix_tx_stop(app); + subghz_remote_tx_stop(app); exit_loop = true; break; default: @@ -986,7 +1010,7 @@ int32_t unirfremix_app(void* p) { app->processing = 2; - unirfremix_process_signal(app, app->signal); + subghz_remote_process_signal(app, app->signal); } if(exit_loop == true) { @@ -1042,7 +1066,7 @@ int32_t unirfremix_app(void* p) { } // remove & free all stuff created by app - unirfremix_free(app, true); + subghz_remote_free(app, true); return 0; } diff --git a/applications/main/u2f/u2f_data.c b/applications/main/u2f/u2f_data.c index 66604d166..217733c94 100644 --- a/applications/main/u2f/u2f_data.c +++ b/applications/main/u2f/u2f_data.c @@ -178,7 +178,7 @@ bool u2f_data_cert_key_load(uint8_t* cert_key) { uint8_t key_slot = 0; uint32_t version = 0; - // Check if unique key exists in secure eclave and generate it if missing + // Check if unique key exists in secure eclave(typo?) and generate it if missing if(!furi_hal_crypto_verify_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE)) return false; FuriString* filetype; diff --git a/applications/main/xtreme_app/application.fam b/applications/main/xtreme_app/application.fam new file mode 100644 index 000000000..266350521 --- /dev/null +++ b/applications/main/xtreme_app/application.fam @@ -0,0 +1,16 @@ +App( + appid="xtreme_app", + name="Xtreme Settings", + apptype=FlipperAppType.EXTERNAL, + entry_point="xtreme_app", + cdefines=["APP_XTREME"], + requires=[ + "gui", + "dolphin", + "xtreme", + ], + stack_size=2 * 1024, + # icon="A_Xtreme_14", + order=90, + fap_category=".Main", +) diff --git a/applications/settings/xtreme_app/scenes/xtreme_app_scene.c b/applications/main/xtreme_app/scenes/xtreme_app_scene.c similarity index 100% rename from applications/settings/xtreme_app/scenes/xtreme_app_scene.c rename to applications/main/xtreme_app/scenes/xtreme_app_scene.c diff --git a/applications/settings/xtreme_app/scenes/xtreme_app_scene.h b/applications/main/xtreme_app/scenes/xtreme_app_scene.h similarity index 100% rename from applications/settings/xtreme_app/scenes/xtreme_app_scene.h rename to applications/main/xtreme_app/scenes/xtreme_app_scene.h diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_config.h b/applications/main/xtreme_app/scenes/xtreme_app_scene_config.h new file mode 100644 index 000000000..a7370f9b4 --- /dev/null +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_config.h @@ -0,0 +1,11 @@ +ADD_SCENE(xtreme_app, start, Start) +ADD_SCENE(xtreme_app, graphics, Graphics) +ADD_SCENE(xtreme_app, statusbar, Statusbar) +ADD_SCENE(xtreme_app, protocols, Protocols) +ADD_SCENE(xtreme_app, protocols_frequencies, ProtocolsFrequencies) +ADD_SCENE(xtreme_app, protocols_frequencies_static, ProtocolsFrequenciesStatic) +ADD_SCENE(xtreme_app, protocols_frequencies_hopper, ProtocolsFrequenciesHopper) +ADD_SCENE(xtreme_app, protocols_frequencies_add, ProtocolsFrequenciesAdd) +ADD_SCENE(xtreme_app, dolphin, Dolphin) +ADD_SCENE(xtreme_app, misc, Misc) +ADD_SCENE(xtreme_app, misc_rename, MiscRename) diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_dolphin.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_dolphin.c new file mode 100644 index 000000000..a157b269a --- /dev/null +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_dolphin.c @@ -0,0 +1,92 @@ +#include "../xtreme_app.h" + +enum VarItemListIndex { + VarItemListIndexXpLevel, + VarItemListIndexButthurtTimer, +}; + +void xtreme_app_scene_dolphin_var_item_list_callback(void* context, uint32_t index) { + XtremeApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +static void xtreme_app_scene_dolphin_xp_level_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); + app->dolphin_level = variable_item_get_current_value_index(item) + 1; + char level_str[4]; + snprintf(level_str, 4, "%i", app->dolphin_level); + variable_item_set_current_value_text(item, level_str); + app->save_level = true; +} + +const char* const butthurt_timer_names[] = + {"OFF", "30 M", "1 H", "2 H", "4 H", "6 H", "8 H", "12 H", "24 H", "48 H"}; +const int32_t butthurt_timer_values[COUNT_OF(butthurt_timer_names)] = + {-1, 1800, 3600, 7200, 14400, 21600, 28800, 43200, 86400, 172800}; +static void xtreme_app_scene_dolphin_butthurt_timer_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, butthurt_timer_names[index]); + XTREME_SETTINGS()->butthurt_timer = butthurt_timer_values[index]; + app->save_settings = true; + app->require_reboot = true; +} + +void xtreme_app_scene_dolphin_on_enter(void* context) { + XtremeApp* app = context; + XtremeSettings* xtreme_settings = XTREME_SETTINGS(); + VariableItemList* var_item_list = app->var_item_list; + VariableItem* item; + uint8_t value_index; + + char level_str[4]; + snprintf(level_str, 4, "%i", app->dolphin_level); + item = variable_item_list_add( + var_item_list, + "XP Level", + DOLPHIN_LEVEL_COUNT + 1, + xtreme_app_scene_dolphin_xp_level_changed, + app); + variable_item_set_current_value_index(item, app->dolphin_level - 1); + variable_item_set_current_value_text(item, level_str); + + item = variable_item_list_add( + var_item_list, + "Butthurt Timer", + COUNT_OF(butthurt_timer_names), + xtreme_app_scene_dolphin_butthurt_timer_changed, + app); + value_index = value_index_int32( + xtreme_settings->butthurt_timer, butthurt_timer_values, COUNT_OF(butthurt_timer_names)); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, butthurt_timer_names[value_index]); + + variable_item_list_set_enter_callback( + var_item_list, xtreme_app_scene_dolphin_var_item_list_callback, app); + + variable_item_list_set_selected_item( + var_item_list, scene_manager_get_scene_state(app->scene_manager, XtremeAppSceneDolphin)); + + view_dispatcher_switch_to_view(app->view_dispatcher, XtremeAppViewVarItemList); +} + +bool xtreme_app_scene_dolphin_on_event(void* context, SceneManagerEvent event) { + XtremeApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + scene_manager_set_scene_state(app->scene_manager, XtremeAppSceneDolphin, event.event); + consumed = true; + switch(event.event) { + default: + break; + } + } + + return consumed; +} + +void xtreme_app_scene_dolphin_on_exit(void* context) { + XtremeApp* app = context; + variable_item_list_reset(app->var_item_list); +} diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_graphics.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_graphics.c new file mode 100644 index 000000000..99a302a44 --- /dev/null +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_graphics.c @@ -0,0 +1,146 @@ +#include "../xtreme_app.h" + +enum VarItemListIndex { + VarItemListIndexAssetPack, + VarItemListIndexAnimSpeed, + VarItemListIndexCycleAnims, + VarItemListIndexUnlockAnims, +}; + +void xtreme_app_scene_graphics_var_item_list_callback(void* context, uint32_t index) { + XtremeApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +static void xtreme_app_scene_graphics_asset_pack_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text( + item, index == 0 ? "SFW" : *asset_packs_get(app->asset_packs, index - 1)); + strlcpy( + XTREME_SETTINGS()->asset_pack, + index == 0 ? "" : *asset_packs_get(app->asset_packs, index - 1), + MAX_PACK_NAME_LEN); + app->asset_pack = index; + app->save_settings = true; + app->require_reboot = true; +} + +const char* const anim_speed_names[] = + {"25%", "50%", "75%", "100%", "125%", "150%", "175%", "200%", "225%", "250%", "275%", "300%"}; +const int32_t anim_speed_values[COUNT_OF(anim_speed_names)] = + {25, 50, 75, 100, 125, 150, 175, 200, 225, 250, 275, 300}; +static void xtreme_app_scene_graphics_anim_speed_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, anim_speed_names[index]); + XTREME_SETTINGS()->anim_speed = anim_speed_values[index]; + app->save_settings = true; +} + +const char* const cycle_anims_names[] = { + "OFF", + "Meta.txt", + "30 S", + "1 M", + "5 M", + "10 M", + "15 M", + "30 M", + "1 H", + "2 H", + "6 H", + "12 H", + "24 H"}; +const int32_t cycle_anims_values[COUNT_OF(cycle_anims_names)] = + {-1, 0, 30, 60, 300, 600, 900, 1800, 3600, 7200, 21600, 43200, 86400}; +static void xtreme_app_scene_graphics_cycle_anims_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, cycle_anims_names[index]); + XTREME_SETTINGS()->cycle_anims = cycle_anims_values[index]; + app->save_settings = true; +} + +static void xtreme_app_scene_graphics_unlock_anims_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); + bool value = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, value ? "ON" : "OFF"); + XTREME_SETTINGS()->unlock_anims = value; + app->save_settings = true; +} + +void xtreme_app_scene_graphics_on_enter(void* context) { + XtremeApp* app = context; + XtremeSettings* xtreme_settings = XTREME_SETTINGS(); + VariableItemList* var_item_list = app->var_item_list; + VariableItem* item; + uint8_t value_index; + + item = variable_item_list_add( + var_item_list, + "Asset Pack", + asset_packs_size(app->asset_packs) + 1, + xtreme_app_scene_graphics_asset_pack_changed, + app); + variable_item_set_current_value_index(item, app->asset_pack); + variable_item_set_current_value_text( + item, + app->asset_pack == 0 ? "SFW" : *asset_packs_get(app->asset_packs, app->asset_pack - 1)); + + item = variable_item_list_add( + var_item_list, + "Anim Speed", + COUNT_OF(anim_speed_names), + xtreme_app_scene_graphics_anim_speed_changed, + app); + value_index = value_index_int32( + xtreme_settings->anim_speed, anim_speed_values, COUNT_OF(anim_speed_names)); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, anim_speed_names[value_index]); + + item = variable_item_list_add( + var_item_list, + "Cycle Anims", + COUNT_OF(cycle_anims_names), + xtreme_app_scene_graphics_cycle_anims_changed, + app); + value_index = value_index_int32( + xtreme_settings->cycle_anims, cycle_anims_values, COUNT_OF(cycle_anims_names)); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, cycle_anims_names[value_index]); + + item = variable_item_list_add( + var_item_list, "Unlock Anims", 2, xtreme_app_scene_graphics_unlock_anims_changed, app); + variable_item_set_current_value_index(item, xtreme_settings->unlock_anims); + variable_item_set_current_value_text(item, xtreme_settings->unlock_anims ? "ON" : "OFF"); + + variable_item_list_set_enter_callback( + var_item_list, xtreme_app_scene_graphics_var_item_list_callback, app); + + variable_item_list_set_selected_item( + var_item_list, scene_manager_get_scene_state(app->scene_manager, XtremeAppSceneGraphics)); + + view_dispatcher_switch_to_view(app->view_dispatcher, XtremeAppViewVarItemList); +} + +bool xtreme_app_scene_graphics_on_event(void* context, SceneManagerEvent event) { + XtremeApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + scene_manager_set_scene_state(app->scene_manager, XtremeAppSceneGraphics, event.event); + consumed = true; + switch(event.event) { + default: + break; + } + } + + return consumed; +} + +void xtreme_app_scene_graphics_on_exit(void* context) { + XtremeApp* app = context; + variable_item_list_reset(app->var_item_list); +} diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_misc.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_misc.c new file mode 100644 index 000000000..030256be0 --- /dev/null +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_misc.c @@ -0,0 +1,96 @@ +#include "../xtreme_app.h" + +enum VarItemListIndex { + VarItemListIndexSortDirsFirst, + VarItemListIndexChangeDeviceName, + VarItemListIndexExperimentalOptions, + VarItemListIndexDarkMode, + VarItemListIndexLeftHanded, +}; + +void xtreme_app_scene_misc_var_item_list_callback(void* context, uint32_t index) { + XtremeApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +static void xtreme_app_scene_misc_sort_dirs_first_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); + bool value = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, value ? "ON" : "OFF"); + XTREME_SETTINGS()->sort_dirs_first = value; + app->save_settings = true; +} + +static void xtreme_app_scene_misc_dark_mode_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); + bool value = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, value ? "ON" : "OFF"); + XTREME_SETTINGS()->dark_mode = value; + app->save_settings = true; +} + +static void xtreme_app_scene_misc_left_handed_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); + bool value = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, value ? "ON" : "OFF"); + XTREME_SETTINGS()->left_handed = value; + app->save_settings = true; +} + +void xtreme_app_scene_misc_on_enter(void* context) { + XtremeApp* app = context; + XtremeSettings* xtreme_settings = XTREME_SETTINGS(); + VariableItemList* var_item_list = app->var_item_list; + VariableItem* item; + + item = variable_item_list_add( + var_item_list, "Sort Dirs First", 2, xtreme_app_scene_misc_sort_dirs_first_changed, app); + variable_item_set_current_value_index(item, xtreme_settings->sort_dirs_first); + variable_item_set_current_value_text(item, xtreme_settings->sort_dirs_first ? "ON" : "OFF"); + + variable_item_list_add(var_item_list, "Change Device Name", 0, NULL, app); + + variable_item_list_add(var_item_list, " Experimental Options:", 0, NULL, app); + + item = variable_item_list_add( + var_item_list, "Dark Mode", 2, xtreme_app_scene_misc_dark_mode_changed, app); + variable_item_set_current_value_index(item, xtreme_settings->dark_mode); + variable_item_set_current_value_text(item, xtreme_settings->dark_mode ? "ON" : "OFF"); + + item = variable_item_list_add( + var_item_list, "Left Handed", 2, xtreme_app_scene_misc_left_handed_changed, app); + variable_item_set_current_value_index(item, xtreme_settings->left_handed); + variable_item_set_current_value_text(item, xtreme_settings->left_handed ? "ON" : "OFF"); + + variable_item_list_set_enter_callback( + var_item_list, xtreme_app_scene_misc_var_item_list_callback, app); + + variable_item_list_set_selected_item( + var_item_list, scene_manager_get_scene_state(app->scene_manager, XtremeAppSceneMisc)); + + view_dispatcher_switch_to_view(app->view_dispatcher, XtremeAppViewVarItemList); +} + +bool xtreme_app_scene_misc_on_event(void* context, SceneManagerEvent event) { + XtremeApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + scene_manager_set_scene_state(app->scene_manager, XtremeAppSceneMisc, event.event); + consumed = true; + switch(event.event) { + case VarItemListIndexChangeDeviceName: + scene_manager_next_scene(app->scene_manager, XtremeAppSceneMiscRename); + break; + default: + break; + } + } + + return consumed; +} + +void xtreme_app_scene_misc_on_exit(void* context) { + XtremeApp* app = context; + variable_item_list_reset(app->var_item_list); +} diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_misc_rename.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_misc_rename.c new file mode 100644 index 000000000..a5f5dd8c5 --- /dev/null +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_misc_rename.c @@ -0,0 +1,53 @@ +#include "../xtreme_app.h" + +enum TextInputIndex { + TextInputResultOk, +}; + +static void xtreme_app_scene_misc_rename_text_input_callback(void* context) { + XtremeApp* app = context; + + app->save_name = true; + app->require_reboot = true; + view_dispatcher_send_custom_event(app->view_dispatcher, TextInputResultOk); +} + +void xtreme_app_scene_misc_rename_on_enter(void* context) { + XtremeApp* app = context; + TextInput* text_input = app->text_input; + + text_input_set_header_text(text_input, "Leave empty for default"); + + text_input_set_result_callback( + text_input, + xtreme_app_scene_misc_rename_text_input_callback, + app, + app->device_name, + NAMECHANGER_TEXT_STORE_SIZE, + true); + + view_dispatcher_switch_to_view(app->view_dispatcher, XtremeAppViewTextInput); +} + +bool xtreme_app_scene_misc_rename_on_event(void* context, SceneManagerEvent event) { + XtremeApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + switch(event.event) { + case TextInputResultOk: + scene_manager_previous_scene(app->scene_manager); + break; + default: + break; + } + } + + return consumed; +} + +void xtreme_app_scene_misc_rename_on_exit(void* context) { + XtremeApp* app = context; + text_input_reset(app->text_input); +} diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols.c new file mode 100644 index 000000000..eb8285e34 --- /dev/null +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols.c @@ -0,0 +1,105 @@ +#include "../xtreme_app.h" + +enum VarItemListIndex { + VarItemListIndexBadkbMode, + VarItemListIndexBadbtRemember, + VarItemListIndexSubghzFrequencies, + VarItemListIndexSubghzExtend, + VarItemListIndexSubghzBypass, +}; + +void xtreme_app_scene_protocols_var_item_list_callback(void* context, uint32_t index) { + XtremeApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +static void xtreme_app_scene_protocols_badkb_mode_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); + bool value = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, value ? "BT" : "USB"); + XTREME_SETTINGS()->bad_bt = value; + app->save_settings = true; +} + +static void xtreme_app_scene_protocols_badbt_remember_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); + bool value = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, value ? "ON" : "OFF"); + XTREME_SETTINGS()->bad_bt_remember = value; + app->save_settings = true; +} + +static void xtreme_app_scene_protocols_subghz_extend_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); + app->subghz_extend = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, app->subghz_extend ? "ON" : "OFF"); + app->save_subghz = true; +} + +static void xtreme_app_scene_protocols_subghz_bypass_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); + app->subghz_bypass = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, app->subghz_bypass ? "ON" : "OFF"); + app->save_subghz = true; +} + +void xtreme_app_scene_protocols_on_enter(void* context) { + XtremeApp* app = context; + XtremeSettings* xtreme_settings = XTREME_SETTINGS(); + VariableItemList* var_item_list = app->var_item_list; + VariableItem* item; + + item = variable_item_list_add( + var_item_list, "BadKB Mode", 2, xtreme_app_scene_protocols_badkb_mode_changed, app); + variable_item_set_current_value_index(item, xtreme_settings->bad_bt); + variable_item_set_current_value_text(item, xtreme_settings->bad_bt ? "BT" : "USB"); + + item = variable_item_list_add( + var_item_list, "BadBT Rmembr", 2, xtreme_app_scene_protocols_badbt_remember_changed, app); + variable_item_set_current_value_index(item, xtreme_settings->bad_bt_remember); + variable_item_set_current_value_text(item, xtreme_settings->bad_bt_remember ? "ON" : "OFF"); + + variable_item_list_add(var_item_list, "SubGHz Frequencies", 0, NULL, app); + + item = variable_item_list_add( + var_item_list, "SubGHz Extend", 2, xtreme_app_scene_protocols_subghz_extend_changed, app); + variable_item_set_current_value_index(item, app->subghz_extend); + variable_item_set_current_value_text(item, app->subghz_extend ? "ON" : "OFF"); + + item = variable_item_list_add( + var_item_list, "SubGHz Bypass", 2, xtreme_app_scene_protocols_subghz_bypass_changed, app); + variable_item_set_current_value_index(item, app->subghz_bypass); + variable_item_set_current_value_text(item, app->subghz_bypass ? "ON" : "OFF"); + + variable_item_list_set_enter_callback( + var_item_list, xtreme_app_scene_protocols_var_item_list_callback, app); + + variable_item_list_set_selected_item( + var_item_list, scene_manager_get_scene_state(app->scene_manager, XtremeAppSceneProtocols)); + + view_dispatcher_switch_to_view(app->view_dispatcher, XtremeAppViewVarItemList); +} + +bool xtreme_app_scene_protocols_on_event(void* context, SceneManagerEvent event) { + XtremeApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + scene_manager_set_scene_state(app->scene_manager, XtremeAppSceneProtocols, event.event); + consumed = true; + switch(event.event) { + case VarItemListIndexSubghzFrequencies: + scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocolsFrequencies); + break; + default: + break; + } + } + + return consumed; +} + +void xtreme_app_scene_protocols_on_exit(void* context) { + XtremeApp* app = context; + variable_item_list_reset(app->var_item_list); +} diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies.c new file mode 100644 index 000000000..6b92d1eb0 --- /dev/null +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies.c @@ -0,0 +1,76 @@ +#include "../xtreme_app.h" + +enum VarItemListIndex { + VarItemListIndexUseDefaults, + VarItemListIndexStaticFrequencies, + VarItemListIndexHopperFrequencies, +}; + +void xtreme_app_scene_protocols_frequencies_var_item_list_callback(void* context, uint32_t index) { + XtremeApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +static void xtreme_app_scene_protocols_frequencies_use_defaults_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); + bool value = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, value ? "ON" : "OFF"); + app->subghz_use_defaults = value; + app->save_subghz_frequencies = true; +} + +void xtreme_app_scene_protocols_frequencies_on_enter(void* context) { + XtremeApp* app = context; + VariableItemList* var_item_list = app->var_item_list; + VariableItem* item; + + item = variable_item_list_add( + var_item_list, + "Use Defaults", + 2, + xtreme_app_scene_protocols_frequencies_use_defaults_changed, + app); + variable_item_set_current_value_index(item, app->subghz_use_defaults); + variable_item_set_current_value_text(item, app->subghz_use_defaults ? "ON" : "OFF"); + + variable_item_list_add(var_item_list, "Static Frequencies", 0, NULL, app); + + variable_item_list_add(var_item_list, "Hopper Frequencies", 0, NULL, app); + + variable_item_list_set_enter_callback( + var_item_list, xtreme_app_scene_protocols_frequencies_var_item_list_callback, app); + + variable_item_list_set_selected_item( + var_item_list, + scene_manager_get_scene_state(app->scene_manager, XtremeAppSceneProtocolsFrequencies)); + + view_dispatcher_switch_to_view(app->view_dispatcher, XtremeAppViewVarItemList); +} + +bool xtreme_app_scene_protocols_frequencies_on_event(void* context, SceneManagerEvent event) { + XtremeApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + scene_manager_set_scene_state( + app->scene_manager, XtremeAppSceneProtocolsFrequencies, event.event); + consumed = true; + switch(event.event) { + case VarItemListIndexStaticFrequencies: + scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocolsFrequenciesStatic); + break; + case VarItemListIndexHopperFrequencies: + scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocolsFrequenciesHopper); + break; + default: + break; + } + } + + return consumed; +} + +void xtreme_app_scene_protocols_frequencies_on_exit(void* context) { + XtremeApp* app = context; + variable_item_list_reset(app->var_item_list); +} diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies_add.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies_add.c new file mode 100644 index 000000000..21b29e295 --- /dev/null +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies_add.c @@ -0,0 +1,83 @@ +#include "../xtreme_app.h" + +enum TextInputResult { + TextInputResultOk, + TextInputResultError, +}; + +static void xtreme_app_scene_protocols_frequencies_add_text_input_callback(void* context) { + XtremeApp* app = context; + + char* end; + uint32_t value = strtol(app->subghz_freq_buffer, &end, 0) * 10000; + if(*end || !furi_hal_subghz_is_frequency_valid(value)) { + view_dispatcher_send_custom_event(app->view_dispatcher, TextInputResultError); + return; + } + bool is_hopper = + scene_manager_get_scene_state(app->scene_manager, XtremeAppSceneProtocolsFrequenciesAdd); + if(is_hopper) { + FrequencyList_push_back(app->subghz_hopper_frequencies, value); + } else { + FrequencyList_push_back(app->subghz_static_frequencies, value); + } + app->save_subghz_frequencies = true; + view_dispatcher_send_custom_event(app->view_dispatcher, TextInputResultOk); +} + +void xtreme_app_scene_protocols_frequencies_add_on_enter(void* context) { + XtremeApp* app = context; + TextInput* text_input = app->text_input; + + text_input_set_header_text(text_input, "Format: 12356"); + + strlcpy(app->subghz_freq_buffer, "", XTREME_SUBGHZ_FREQ_BUFFER_SIZE); + + text_input_set_result_callback( + text_input, + xtreme_app_scene_protocols_frequencies_add_text_input_callback, + app, + app->subghz_freq_buffer, + XTREME_SUBGHZ_FREQ_BUFFER_SIZE, + true); + + view_dispatcher_switch_to_view(app->view_dispatcher, XtremeAppViewTextInput); +} + +void callback_return(void* context) { + XtremeApp* app = context; + scene_manager_previous_scene(app->scene_manager); +} + +bool xtreme_app_scene_protocols_frequencies_add_on_event(void* context, SceneManagerEvent event) { + XtremeApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + switch(event.event) { + case TextInputResultOk: + scene_manager_previous_scene(app->scene_manager); + break; + case TextInputResultError: + popup_set_header(app->popup, "Invalid value!", 64, 26, AlignCenter, AlignCenter); + popup_set_text( + app->popup, "Frequency was not added...", 64, 40, AlignCenter, AlignCenter); + popup_set_callback(app->popup, callback_return); + popup_set_context(app->popup, app); + popup_set_timeout(app->popup, 1000); + popup_enable_timeout(app->popup); + view_dispatcher_switch_to_view(app->view_dispatcher, XtremeAppViewPopup); + break; + default: + break; + } + } + + return consumed; +} + +void xtreme_app_scene_protocols_frequencies_add_on_exit(void* context) { + XtremeApp* app = context; + text_input_reset(app->text_input); +} diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies_hopper.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies_hopper.c new file mode 100644 index 000000000..24c7fe3cc --- /dev/null +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies_hopper.c @@ -0,0 +1,105 @@ +#include "../xtreme_app.h" + +enum VarItemListIndex { + VarItemListIndexHopperFrequency, + VarItemListIndexDeleteHopperFreq, + VarItemListIndexAddHopperFreq, +}; + +void xtreme_app_scene_protocols_frequencies_hopper_var_item_list_callback( + void* context, + uint32_t index) { + XtremeApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +static void xtreme_app_scene_protocols_frequencies_hopper_frequency_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); + app->subghz_hopper_index = variable_item_get_current_value_index(item); + uint32_t value = *FrequencyList_get(app->subghz_hopper_frequencies, app->subghz_hopper_index); + char text[10] = {0}; + snprintf(text, sizeof(text), "%lu.%02lu", value / 1000000, (value % 1000000) / 10000); + variable_item_set_current_value_text(item, text); +} + +void xtreme_app_scene_protocols_frequencies_hopper_on_enter(void* context) { + XtremeApp* app = context; + VariableItemList* var_item_list = app->var_item_list; + VariableItem* item; + + item = variable_item_list_add( + var_item_list, + "Hopper Freq", + FrequencyList_size(app->subghz_hopper_frequencies), + xtreme_app_scene_protocols_frequencies_hopper_frequency_changed, + app); + app->subghz_hopper_index = 0; + variable_item_set_current_value_index(item, app->subghz_hopper_index); + if(FrequencyList_size(app->subghz_hopper_frequencies)) { + uint32_t value = + *FrequencyList_get(app->subghz_hopper_frequencies, app->subghz_hopper_index); + char text[10] = {0}; + snprintf(text, sizeof(text), "%lu.%02lu", value / 1000000, (value % 1000000) / 10000); + variable_item_set_current_value_text(item, text); + } else { + variable_item_set_current_value_text(item, "None"); + } + + variable_item_list_add(var_item_list, "Delete Hopper Freq", 0, NULL, app); + + variable_item_list_add(var_item_list, "Add Hopper Freq", 0, NULL, app); + + variable_item_list_set_enter_callback( + var_item_list, xtreme_app_scene_protocols_frequencies_hopper_var_item_list_callback, app); + + variable_item_list_set_selected_item( + var_item_list, + scene_manager_get_scene_state( + app->scene_manager, XtremeAppSceneProtocolsFrequenciesHopper)); + + view_dispatcher_switch_to_view(app->view_dispatcher, XtremeAppViewVarItemList); +} + +bool xtreme_app_scene_protocols_frequencies_hopper_on_event(void* context, SceneManagerEvent event) { + XtremeApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + scene_manager_set_scene_state( + app->scene_manager, XtremeAppSceneProtocolsFrequenciesHopper, event.event); + consumed = true; + switch(event.event) { + case VarItemListIndexDeleteHopperFreq: + if(!FrequencyList_size(app->subghz_hopper_frequencies)) break; + uint32_t value = + *FrequencyList_get(app->subghz_hopper_frequencies, app->subghz_hopper_index); + FrequencyList_it_t it; + FrequencyList_it(it, app->subghz_hopper_frequencies); + while(!FrequencyList_end_p(it)) { + if(*FrequencyList_ref(it) == value) { + FrequencyList_remove(app->subghz_hopper_frequencies, it); + } else { + FrequencyList_next(it); + } + } + app->save_subghz_frequencies = true; + scene_manager_previous_scene(app->scene_manager); + scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocolsFrequenciesHopper); + break; + case VarItemListIndexAddHopperFreq: + scene_manager_set_scene_state( + app->scene_manager, XtremeAppSceneProtocolsFrequenciesAdd, true); + scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocolsFrequenciesAdd); + break; + default: + break; + } + } + + return consumed; +} + +void xtreme_app_scene_protocols_frequencies_hopper_on_exit(void* context) { + XtremeApp* app = context; + variable_item_list_reset(app->var_item_list); +} diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies_static.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies_static.c new file mode 100644 index 000000000..4d70a1a58 --- /dev/null +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies_static.c @@ -0,0 +1,105 @@ +#include "../xtreme_app.h" + +enum VarItemListIndex { + VarItemListIndexStaticFrequency, + VarItemListIndexDeleteStaticFreq, + VarItemListIndexAddStaticFreq, +}; + +void xtreme_app_scene_protocols_frequencies_static_var_item_list_callback( + void* context, + uint32_t index) { + XtremeApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +static void xtreme_app_scene_protocols_frequencies_static_frequency_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); + app->subghz_static_index = variable_item_get_current_value_index(item); + uint32_t value = *FrequencyList_get(app->subghz_static_frequencies, app->subghz_static_index); + char text[10] = {0}; + snprintf(text, sizeof(text), "%lu.%02lu", value / 1000000, (value % 1000000) / 10000); + variable_item_set_current_value_text(item, text); +} + +void xtreme_app_scene_protocols_frequencies_static_on_enter(void* context) { + XtremeApp* app = context; + VariableItemList* var_item_list = app->var_item_list; + VariableItem* item; + + item = variable_item_list_add( + var_item_list, + "Static Freq", + FrequencyList_size(app->subghz_static_frequencies), + xtreme_app_scene_protocols_frequencies_static_frequency_changed, + app); + app->subghz_static_index = 0; + variable_item_set_current_value_index(item, app->subghz_static_index); + if(FrequencyList_size(app->subghz_static_frequencies)) { + uint32_t value = + *FrequencyList_get(app->subghz_static_frequencies, app->subghz_static_index); + char text[10] = {0}; + snprintf(text, sizeof(text), "%lu.%02lu", value / 1000000, (value % 1000000) / 10000); + variable_item_set_current_value_text(item, text); + } else { + variable_item_set_current_value_text(item, "None"); + } + + variable_item_list_add(var_item_list, "Delete Static Freq", 0, NULL, app); + + variable_item_list_add(var_item_list, "Add Static Freq", 0, NULL, app); + + variable_item_list_set_enter_callback( + var_item_list, xtreme_app_scene_protocols_frequencies_static_var_item_list_callback, app); + + variable_item_list_set_selected_item( + var_item_list, + scene_manager_get_scene_state( + app->scene_manager, XtremeAppSceneProtocolsFrequenciesStatic)); + + view_dispatcher_switch_to_view(app->view_dispatcher, XtremeAppViewVarItemList); +} + +bool xtreme_app_scene_protocols_frequencies_static_on_event(void* context, SceneManagerEvent event) { + XtremeApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + scene_manager_set_scene_state( + app->scene_manager, XtremeAppSceneProtocolsFrequenciesStatic, event.event); + consumed = true; + switch(event.event) { + case VarItemListIndexDeleteStaticFreq: + if(!FrequencyList_size(app->subghz_static_frequencies)) break; + uint32_t value = + *FrequencyList_get(app->subghz_static_frequencies, app->subghz_static_index); + FrequencyList_it_t it; + FrequencyList_it(it, app->subghz_static_frequencies); + while(!FrequencyList_end_p(it)) { + if(*FrequencyList_ref(it) == value) { + FrequencyList_remove(app->subghz_static_frequencies, it); + } else { + FrequencyList_next(it); + } + } + app->save_subghz_frequencies = true; + scene_manager_previous_scene(app->scene_manager); + scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocolsFrequenciesStatic); + break; + case VarItemListIndexAddStaticFreq: + scene_manager_set_scene_state( + app->scene_manager, XtremeAppSceneProtocolsFrequenciesAdd, false); + scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocolsFrequenciesAdd); + break; + default: + break; + } + } + + return consumed; +} + +void xtreme_app_scene_protocols_frequencies_static_on_exit(void* context) { + XtremeApp* app = context; + variable_item_list_reset(app->var_item_list); +} diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_start.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_start.c new file mode 100644 index 000000000..c1c7659fc --- /dev/null +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_start.c @@ -0,0 +1,71 @@ +#include "../xtreme_app.h" + +enum VarItemListIndex { + VarItemListIndexGraphics, + VarItemListIndexStatusbar, + VarItemListIndexProtocols, + VarItemListIndexDolphin, + VarItemListIndexMisc, +}; + +void xtreme_app_scene_start_var_item_list_callback(void* context, uint32_t index) { + XtremeApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void xtreme_app_scene_start_on_enter(void* context) { + XtremeApp* app = context; + VariableItemList* var_item_list = app->var_item_list; + + variable_item_list_add(var_item_list, "Graphics", 0, NULL, app); + variable_item_list_add(var_item_list, "Statusbar", 0, NULL, app); + variable_item_list_add(var_item_list, "Protocols", 0, NULL, app); + variable_item_list_add(var_item_list, "Dolphin", 0, NULL, app); + variable_item_list_add(var_item_list, "Misc", 0, NULL, app); + + variable_item_list_add(var_item_list, furi_string_get_cstr(app->version_tag), 0, NULL, app); + + variable_item_list_set_enter_callback( + var_item_list, xtreme_app_scene_start_var_item_list_callback, app); + + variable_item_list_set_selected_item( + var_item_list, scene_manager_get_scene_state(app->scene_manager, XtremeAppSceneStart)); + + view_dispatcher_switch_to_view(app->view_dispatcher, XtremeAppViewVarItemList); +} + +bool xtreme_app_scene_start_on_event(void* context, SceneManagerEvent event) { + XtremeApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + scene_manager_set_scene_state(app->scene_manager, XtremeAppSceneStart, event.event); + consumed = true; + switch(event.event) { + case VarItemListIndexGraphics: + scene_manager_next_scene(app->scene_manager, XtremeAppSceneGraphics); + break; + case VarItemListIndexStatusbar: + scene_manager_next_scene(app->scene_manager, XtremeAppSceneStatusbar); + break; + case VarItemListIndexProtocols: + scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocols); + break; + case VarItemListIndexDolphin: + scene_manager_next_scene(app->scene_manager, XtremeAppSceneDolphin); + break; + case VarItemListIndexMisc: + scene_manager_next_scene(app->scene_manager, XtremeAppSceneMisc); + break; + default: + break; + } + } + + return consumed; +} + +void xtreme_app_scene_start_on_exit(void* context) { + XtremeApp* app = context; + variable_item_list_reset(app->var_item_list); +} diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_statusbar.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_statusbar.c new file mode 100644 index 000000000..48d7e6484 --- /dev/null +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_statusbar.c @@ -0,0 +1,107 @@ +#include "../xtreme_app.h" + +enum VarItemListIndex { + VarItemListIndexBatteryIcon, + VarItemListIndexStatusIcons, + VarItemListIndexBarBorders, + VarItemListIndexbarBackground, +}; + +void xtreme_app_scene_statusbar_var_item_list_callback(void* context, uint32_t index) { + XtremeApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +const char* const battery_icon_names[] = + {"OFF", "Bar", "%", "Inv. %", "Retro 3", "Retro 5", "Bar %"}; +static void xtreme_app_scene_statusbar_battery_icon_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, battery_icon_names[index]); + XTREME_SETTINGS()->battery_icon = index; + app->save_settings = true; +} + +static void xtreme_app_scene_statusbar_status_icons_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); + bool value = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, value ? "ON" : "OFF"); + XTREME_SETTINGS()->status_icons = value; + app->save_settings = true; +} + +static void xtreme_app_scene_statusbar_bar_borders_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); + bool value = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, value ? "ON" : "OFF"); + XTREME_SETTINGS()->bar_borders = value; + app->save_settings = true; +} + +static void xtreme_app_scene_statusbar_bar_background_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); + bool value = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, value ? "ON" : "OFF"); + XTREME_SETTINGS()->bar_background = value; + app->save_settings = true; +} + +void xtreme_app_scene_statusbar_on_enter(void* context) { + XtremeApp* app = context; + XtremeSettings* xtreme_settings = XTREME_SETTINGS(); + VariableItemList* var_item_list = app->var_item_list; + VariableItem* item; + + item = variable_item_list_add( + var_item_list, + "Battery Icon", + BatteryIconCount, + xtreme_app_scene_statusbar_battery_icon_changed, + app); + variable_item_set_current_value_index(item, xtreme_settings->battery_icon); + variable_item_set_current_value_text(item, battery_icon_names[xtreme_settings->battery_icon]); + + item = variable_item_list_add( + var_item_list, "Status Icons", 2, xtreme_app_scene_statusbar_status_icons_changed, app); + variable_item_set_current_value_index(item, xtreme_settings->status_icons); + variable_item_set_current_value_text(item, xtreme_settings->status_icons ? "ON" : "OFF"); + + item = variable_item_list_add( + var_item_list, "Bar Borders", 2, xtreme_app_scene_statusbar_bar_borders_changed, app); + variable_item_set_current_value_index(item, xtreme_settings->bar_borders); + variable_item_set_current_value_text(item, xtreme_settings->bar_borders ? "ON" : "OFF"); + + item = variable_item_list_add( + var_item_list, "Bar Background", 2, xtreme_app_scene_statusbar_bar_background_changed, app); + variable_item_set_current_value_index(item, xtreme_settings->bar_background); + variable_item_set_current_value_text(item, xtreme_settings->bar_background ? "ON" : "OFF"); + + variable_item_list_set_enter_callback( + var_item_list, xtreme_app_scene_statusbar_var_item_list_callback, app); + + variable_item_list_set_selected_item( + var_item_list, scene_manager_get_scene_state(app->scene_manager, XtremeAppSceneStatusbar)); + + view_dispatcher_switch_to_view(app->view_dispatcher, XtremeAppViewVarItemList); +} + +bool xtreme_app_scene_statusbar_on_event(void* context, SceneManagerEvent event) { + XtremeApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + scene_manager_set_scene_state(app->scene_manager, XtremeAppSceneStatusbar, event.event); + consumed = true; + switch(event.event) { + default: + break; + } + } + + return consumed; +} + +void xtreme_app_scene_statusbar_on_exit(void* context) { + XtremeApp* app = context; + variable_item_list_reset(app->var_item_list); +} diff --git a/applications/main/xtreme_app/xtreme_app.c b/applications/main/xtreme_app/xtreme_app.c new file mode 100644 index 000000000..fa66a8d52 --- /dev/null +++ b/applications/main/xtreme_app/xtreme_app.c @@ -0,0 +1,274 @@ +#include "xtreme_app.h" + +static bool xtreme_app_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + XtremeApp* app = context; + return scene_manager_handle_custom_event(app->scene_manager, event); +} + +void callback_reboot(void* context) { + UNUSED(context); + power_reboot(PowerBootModeNormal); +} + +static bool xtreme_app_back_event_callback(void* context) { + furi_assert(context); + XtremeApp* app = context; + + if(!scene_manager_has_previous_scene(app->scene_manager, XtremeAppSceneStart)) { + Storage* storage = furi_record_open(RECORD_STORAGE); + + if(app->save_subghz) { + furi_hal_subghz_set_extend_settings(app->subghz_extend, app->subghz_bypass); + } + + if(app->save_subghz_frequencies) { + FlipperFormat* file = flipper_format_file_alloc(storage); + do { + FrequencyList_it_t it; + if(!flipper_format_file_open_always(file, EXT_PATH("subghz/assets/setting_user"))) + break; + + if(!flipper_format_write_header_cstr( + file, SUBGHZ_SETTING_FILE_TYPE, SUBGHZ_SETTING_FILE_VERSION)) + break; + + while(flipper_format_delete_key(file, "Add_standard_frequencies")) + ; + flipper_format_write_bool( + file, "Add_standard_frequencies", &app->subghz_use_defaults, 1); + + if(!flipper_format_rewind(file)) break; + while(flipper_format_delete_key(file, "Frequency")) + ; + FrequencyList_it(it, app->subghz_static_frequencies); + for(uint i = 0; i < FrequencyList_size(app->subghz_static_frequencies); i++) { + flipper_format_write_uint32( + file, "Frequency", FrequencyList_get(app->subghz_static_frequencies, i), 1); + } + + if(!flipper_format_rewind(file)) break; + while(flipper_format_delete_key(file, "Hopper_frequency")) + ; + for(uint i = 0; i < FrequencyList_size(app->subghz_hopper_frequencies); i++) { + flipper_format_write_uint32( + file, + "Hopper_frequency", + FrequencyList_get(app->subghz_hopper_frequencies, i), + 1); + } + } while(false); + flipper_format_free(file); + } + + if(app->save_level) { + Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN); + int xp = app->dolphin_level > 1 ? dolphin_get_levels()[app->dolphin_level - 2] : 0; + dolphin->state->data.icounter = xp + 1; + dolphin->state->dirty = true; + dolphin_state_save(dolphin->state); + furi_record_close(RECORD_DOLPHIN); + } + + if(app->save_name) { + if(strcmp(app->device_name, "") == 0) { + storage_simply_remove(storage, NAMECHANGER_PATH); + } else { + FlipperFormat* file = flipper_format_file_alloc(storage); + + do { + if(!flipper_format_file_open_always(file, NAMECHANGER_PATH)) break; + + if(!flipper_format_write_header_cstr(file, NAMECHANGER_HEADER, 1)) break; + + if(!flipper_format_write_comment_cstr( + file, + "Changing the value below will change your FlipperZero device name.")) + break; + if(!flipper_format_write_comment_cstr( + file, + "Note: This is limited to 8 characters using the following: a-z, A-Z, 0-9, and _")) + break; + if(!flipper_format_write_comment_cstr( + file, "It cannot contain any other characters.")) + break; + + if(!flipper_format_write_string_cstr(file, "Name", app->device_name)) break; + + } while(0); + + flipper_format_free(file); + } + } + + if(app->save_settings) { + XTREME_SETTINGS_SAVE(); + } + + if(app->require_reboot) { + popup_set_header(app->popup, "Rebooting...", 64, 26, AlignCenter, AlignCenter); + popup_set_text(app->popup, "Applying changes...", 64, 40, AlignCenter, AlignCenter); + popup_set_callback(app->popup, callback_reboot); + popup_set_context(app->popup, app); + popup_set_timeout(app->popup, 1000); + popup_enable_timeout(app->popup); + view_dispatcher_switch_to_view(app->view_dispatcher, XtremeAppViewPopup); + return true; + } + + furi_record_close(RECORD_STORAGE); + } + + return scene_manager_handle_back_event(app->scene_manager); +} + +XtremeApp* xtreme_app_alloc() { + XtremeApp* app = malloc(sizeof(XtremeApp)); + app->gui = furi_record_open(RECORD_GUI); + + // View Dispatcher and Scene Manager + app->view_dispatcher = view_dispatcher_alloc(); + app->scene_manager = scene_manager_alloc(&xtreme_app_scene_handlers, app); + view_dispatcher_enable_queue(app->view_dispatcher); + view_dispatcher_set_event_callback_context(app->view_dispatcher, app); + + view_dispatcher_set_custom_event_callback( + app->view_dispatcher, xtreme_app_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + app->view_dispatcher, xtreme_app_back_event_callback); + + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + + // Gui Modules + app->var_item_list = variable_item_list_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, + XtremeAppViewVarItemList, + variable_item_list_get_view(app->var_item_list)); + + app->text_input = text_input_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, XtremeAppViewTextInput, text_input_get_view(app->text_input)); + + app->popup = popup_alloc(); + view_dispatcher_add_view(app->view_dispatcher, XtremeAppViewPopup, popup_get_view(app->popup)); + + // Settings init + + XtremeSettings* xtreme_settings = XTREME_SETTINGS(); + + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* file = flipper_format_file_alloc(storage); + FrequencyList_init(app->subghz_static_frequencies); + FrequencyList_init(app->subghz_hopper_frequencies); + app->subghz_use_defaults = true; + do { + uint32_t temp; + if(!flipper_format_file_open_existing(file, EXT_PATH("subghz/assets/setting_user"))) break; + + flipper_format_read_bool(file, "Add_standard_frequencies", &app->subghz_use_defaults, 1); + + if(!flipper_format_rewind(file)) break; + while(flipper_format_read_uint32(file, "Frequency", &temp, 1)) { + if(furi_hal_subghz_is_frequency_valid(temp)) { + FrequencyList_push_back(app->subghz_static_frequencies, temp); + } + } + + if(!flipper_format_rewind(file)) break; + while(flipper_format_read_uint32(file, "Hopper_frequency", &temp, 1)) { + if(furi_hal_subghz_is_frequency_valid(temp)) { + FrequencyList_push_back(app->subghz_hopper_frequencies, temp); + } + } + } while(false); + flipper_format_free(file); + + furi_hal_subghz_get_extend_settings(&app->subghz_extend, &app->subghz_bypass); + + Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN); + DolphinStats stats = dolphin_stats(dolphin); + app->dolphin_level = stats.level; + furi_record_close(RECORD_DOLPHIN); + + strlcpy(app->device_name, furi_hal_version_get_name_ptr(), NAMECHANGER_TEXT_STORE_SIZE); + + app->asset_pack = 0; + asset_packs_init(app->asset_packs); + File* folder = storage_file_alloc(storage); + FileInfo info; + char* name = malloc(MAX_PACK_NAME_LEN); + if(storage_dir_open(folder, PACKS_DIR)) { + while(storage_dir_read(folder, &info, name, MAX_PACK_NAME_LEN)) { + if(info.flags & FSF_DIRECTORY) { + char* copy = malloc(MAX_PACK_NAME_LEN); + strlcpy(copy, name, MAX_PACK_NAME_LEN); + uint idx = 0; + if(strcmp(copy, "NSFW") != 0) { + for(; idx < asset_packs_size(app->asset_packs); idx++) { + char* comp = *asset_packs_get(app->asset_packs, idx); + if(strcasecmp(copy, comp) < 0 && strcmp(comp, "NSFW") != 0) { + break; + } + } + } + asset_packs_push_at(app->asset_packs, idx, copy); + if(app->asset_pack != 0) { + if(idx < app->asset_pack) app->asset_pack++; + } else { + if(strcmp(copy, xtreme_settings->asset_pack) == 0) app->asset_pack = idx + 1; + } + } + } + } + free(name); + storage_file_free(folder); + furi_record_close(RECORD_STORAGE); + + app->version_tag = + furi_string_alloc_printf("%s %s", version_get_version(NULL), version_get_builddate(NULL)); + + return app; +} + +void xtreme_app_free(XtremeApp* app) { + furi_assert(app); + + // Gui modules + view_dispatcher_remove_view(app->view_dispatcher, XtremeAppViewVarItemList); + variable_item_list_free(app->var_item_list); + view_dispatcher_remove_view(app->view_dispatcher, XtremeAppViewTextInput); + text_input_free(app->text_input); + view_dispatcher_remove_view(app->view_dispatcher, XtremeAppViewPopup); + popup_free(app->popup); + + // View Dispatcher and Scene Manager + view_dispatcher_free(app->view_dispatcher); + scene_manager_free(app->scene_manager); + + // Settings deinit + + FrequencyList_clear(app->subghz_static_frequencies); + FrequencyList_clear(app->subghz_hopper_frequencies); + + asset_packs_it_t it; + for(asset_packs_it(it, app->asset_packs); !asset_packs_end_p(it); asset_packs_next(it)) { + free(*asset_packs_cref(it)); + } + asset_packs_clear(app->asset_packs); + + furi_string_free(app->version_tag); + + // Records + furi_record_close(RECORD_GUI); + free(app); +} + +extern int32_t xtreme_app(void* p) { + UNUSED(p); + XtremeApp* app = xtreme_app_alloc(); + scene_manager_next_scene(app->scene_manager, XtremeAppSceneStart); + view_dispatcher_run(app->view_dispatcher); + xtreme_app_free(app); + return 0; +} diff --git a/applications/settings/xtreme_app/xtreme_app.h b/applications/main/xtreme_app/xtreme_app.h similarity index 54% rename from applications/settings/xtreme_app/xtreme_app.h rename to applications/main/xtreme_app/xtreme_app.h index 23adbf5ac..d3af90b6e 100644 --- a/applications/settings/xtreme_app/xtreme_app.h +++ b/applications/main/xtreme_app/xtreme_app.h @@ -7,16 +7,22 @@ #include #include #include +#include #include +#include +#include #include "scenes/xtreme_app_scene.h" #include "dolphin/helpers/dolphin_state.h" #include "dolphin/dolphin.h" #include "dolphin/dolphin_i.h" #include +#include #include #include "xtreme/settings.h" #include "xtreme/assets.h" +#define XTREME_SUBGHZ_FREQ_BUFFER_SIZE 6 + ARRAY_DEF(asset_packs, char*) typedef struct { @@ -24,18 +30,31 @@ typedef struct { SceneManager* scene_manager; ViewDispatcher* view_dispatcher; VariableItemList* var_item_list; + TextInput* text_input; Popup* popup; - int dolphin_level; + bool subghz_use_defaults; + FrequencyList_t subghz_static_frequencies; + uint8_t subghz_static_index; + FrequencyList_t subghz_hopper_frequencies; + uint8_t subghz_hopper_index; + char subghz_freq_buffer[XTREME_SUBGHZ_FREQ_BUFFER_SIZE]; bool subghz_extend; bool subghz_bypass; - bool settings_changed; - bool assets_changed; - bool subghz_changed; - bool level_changed; + int dolphin_level; + char device_name[NAMECHANGER_TEXT_STORE_SIZE]; + uint asset_pack; asset_packs_t asset_packs; + FuriString* version_tag; + bool save_subghz; + bool save_subghz_frequencies; + bool save_level; + bool save_name; + bool save_settings; + bool require_reboot; } XtremeApp; typedef enum { XtremeAppViewVarItemList, + XtremeAppViewTextInput, XtremeAppViewPopup, } XtremeAppView; diff --git a/applications/plugins/airmouse/README.md b/applications/plugins/airmouse/README.md index 9df0d69b0..04e346e4b 100644 --- a/applications/plugins/airmouse/README.md +++ b/applications/plugins/airmouse/README.md @@ -19,6 +19,7 @@ Using it is really simple: * Up button for Left mouse click; * Down button for Right mouse click; * Center button for Middle mouse click; + * Left and Right buttons for scrolling; * Use calibration menu option if you notice significant drift (place your Flipper onto a level surface, make sure it doesn't move, run this option, wait 2 seconds, done). See early prototype [in action](https://www.youtube.com/watch?v=DdxAmmsYfMA). @@ -41,10 +42,9 @@ Reality: ![What it looks like](https://github.com/ginkage/FlippAirMouse/blob/main/schematic/flipper.jpg) - ## Software -The code is based on the original Bosch [driver](https://github.com/BoschSensortec/BMI160_driver/) and an orientation tracking implementation from the [Cardboard](https://github.com/googlevr/cardboard/tree/master/sdk/sensors) project +The code is based on the original Bosch [driver](https://github.com/BoschSensortec/BMI160_driver/) and an orientation tracking implementation from the Google [Cardboard](https://github.com/googlevr/cardboard/tree/master/sdk/sensors) project If you're familiar with Flipper applications, start in the [firmware](https://github.com/flipperdevices/flipperzero-firmware) checkout folder and do the following: ``` @@ -54,3 +54,7 @@ cd ../.. ./fbt fap_air_mouse ``` If you're not familiar with those, just grab a `fap` file from Releases. + +## License + +TL;DR: Use the code however you want, give credit where it's due, no warranty of any kind is provided. diff --git a/applications/plugins/airmouse/application.fam b/applications/plugins/airmouse/application.fam index c646ee7e1..abc3f55bb 100644 --- a/applications/plugins/airmouse/application.fam +++ b/applications/plugins/airmouse/application.fam @@ -1,5 +1,5 @@ App( - appid="BMI160_Air_Mouse", + appid="Air_Mouse", name="[BMI160] Air Mouse", apptype=FlipperAppType.EXTERNAL, entry_point="air_mouse_app", diff --git a/applications/plugins/airmouse/views/bt_mouse.c b/applications/plugins/airmouse/views/bt_mouse.c index e6e0ae45a..7d9c0e6db 100644 --- a/applications/plugins/airmouse/views/bt_mouse.c +++ b/applications/plugins/airmouse/views/bt_mouse.c @@ -44,8 +44,7 @@ struct BtMouse { #define BT_MOUSE_FLAG_KILL_THREAD (1UL << 1) #define BT_MOUSE_FLAG_ALL (BT_MOUSE_FLAG_INPUT_EVENT | BT_MOUSE_FLAG_KILL_THREAD) -#define MOUSE_MOVE_SHORT 5 -#define MOUSE_MOVE_LONG 20 +#define MOUSE_SCROLL 2 static void bt_mouse_notify_event(BtMouse* bt_mouse) { FuriThreadId thread_id = furi_thread_get_id(bt_mouse->thread); @@ -100,6 +99,14 @@ static void bt_mouse_process(BtMouse* bt_mouse, InputEvent* event) { } else if(event->type == InputTypeRelease) { bt_mouse_button_state(bt_mouse, HID_MOUSE_BTN_WHEEL, false); } + } else if(event->key == InputKeyRight) { + if(event->type == InputTypePress || event->type == InputTypeRepeat) { + bt_mouse->wheel = MOUSE_SCROLL; + } + } else if(event->key == InputKeyLeft) { + if(event->type == InputTypePress || event->type == InputTypeRepeat) { + bt_mouse->wheel = -MOUSE_SCROLL; + } } }, true); diff --git a/applications/plugins/airmouse/views/usb_mouse.c b/applications/plugins/airmouse/views/usb_mouse.c index 5d9ab4352..09075b566 100644 --- a/applications/plugins/airmouse/views/usb_mouse.c +++ b/applications/plugins/airmouse/views/usb_mouse.c @@ -21,6 +21,8 @@ static void usb_mouse_draw_callback(Canvas* canvas, void* context) { canvas_draw_str(canvas, 0, 63, "Hold [back] to exit"); } +#define MOUSE_SCROLL 2 + static void usb_mouse_process(UsbMouse* usb_mouse, InputEvent* event) { with_view_model( usb_mouse->view, @@ -45,6 +47,14 @@ static void usb_mouse_process(UsbMouse* usb_mouse, InputEvent* event) { } else if(event->type == InputTypeRelease) { furi_hal_hid_mouse_release(HID_MOUSE_BTN_WHEEL); } + } else if(event->key == InputKeyRight) { + if(event->type == InputTypePress || event->type == InputTypeRepeat) { + furi_hal_hid_mouse_scroll(MOUSE_SCROLL); + } + } else if(event->key == InputKeyLeft) { + if(event->type == InputTypePress || event->type == InputTypeRepeat) { + furi_hal_hid_mouse_scroll(-MOUSE_SCROLL); + } } }, true); diff --git a/applications/plugins/application.fam b/applications/plugins/application.fam index 3331888f2..5d5824e29 100644 --- a/applications/plugins/application.fam +++ b/applications/plugins/application.fam @@ -6,6 +6,5 @@ App( "music_player", "music_beeper", "snake_game", - "bt_hid", ], ) diff --git a/applications/plugins/arkanoid/arkanoid_game.c b/applications/plugins/arkanoid/arkanoid_game.c index af9976c98..f6d8c1a3d 100644 --- a/applications/plugins/arkanoid/arkanoid_game.c +++ b/applications/plugins/arkanoid/arkanoid_game.c @@ -37,6 +37,7 @@ typedef struct { } BallState; typedef struct { + FuriMutex* mutex; BallState ball_state; BrickState brick_state; NotificationApp* notify; @@ -309,10 +310,9 @@ static void arkanoid_state_init(ArkanoidState* arkanoid_state) { } static void arkanoid_draw_callback(Canvas* const canvas, void* ctx) { - ArkanoidState* arkanoid_state = acquire_mutex((ValueMutex*)ctx, 25); - if(arkanoid_state == NULL) { - return; - } + furi_assert(ctx); + ArkanoidState* arkanoid_state = ctx; + furi_mutex_acquire(arkanoid_state->mutex, FuriWaitForever); //Initial level draw if(!arkanoid_state->initialDraw) { @@ -351,7 +351,7 @@ static void arkanoid_draw_callback(Canvas* const canvas, void* ctx) { arkanoid_state->score = 0; } - release_mutex((ValueMutex*)ctx, arkanoid_state); + furi_mutex_release(arkanoid_state->mutex); } static void arkanoid_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { @@ -377,8 +377,8 @@ int32_t arkanoid_game_app(void* p) { ArkanoidState* arkanoid_state = malloc(sizeof(ArkanoidState)); arkanoid_state_init(arkanoid_state); - ValueMutex state_mutex; - if(!init_mutex(&state_mutex, arkanoid_state, sizeof(ArkanoidState))) { + arkanoid_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!arkanoid_state->mutex) { FURI_LOG_E(TAG, "Cannot create mutex\r\n"); return_code = 255; goto free_and_exit; @@ -386,7 +386,7 @@ int32_t arkanoid_game_app(void* p) { // Set system callbacks ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, arkanoid_draw_callback, &state_mutex); + view_port_draw_callback_set(view_port, arkanoid_draw_callback, arkanoid_state); view_port_input_callback_set(view_port, arkanoid_input_callback, event_queue); FuriTimer* timer = @@ -400,7 +400,7 @@ int32_t arkanoid_game_app(void* p) { GameEvent event; for(bool processing = true; processing;) { FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); - ArkanoidState* arkanoid_state = (ArkanoidState*)acquire_mutex_block(&state_mutex); + furi_mutex_acquire(arkanoid_state->mutex, FuriWaitForever); if(event_status == FuriStatusOk) { // Key events @@ -457,7 +457,7 @@ int32_t arkanoid_game_app(void* p) { } view_port_update(view_port); - release_mutex(&state_mutex, arkanoid_state); + furi_mutex_release(arkanoid_state->mutex); } furi_timer_free(timer); view_port_enabled_set(view_port, false); @@ -465,7 +465,7 @@ int32_t arkanoid_game_app(void* p) { furi_record_close(RECORD_GUI); furi_record_close(RECORD_NOTIFICATION); view_port_free(view_port); - delete_mutex(&state_mutex); + furi_mutex_free(arkanoid_state->mutex); free_and_exit: free(arkanoid_state); diff --git a/applications/plugins/asteroids/LICENSE b/applications/plugins/asteroids/LICENSE new file mode 100644 index 000000000..2d8a8a74d --- /dev/null +++ b/applications/plugins/asteroids/LICENSE @@ -0,0 +1,24 @@ +Copyright (c) 2022-2023 Salvatore Sanfilippo + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/applications/plugins/asteroids/app.c b/applications/plugins/asteroids/app.c index 9aee8e9f0..495542a24 100644 --- a/applications/plugins/asteroids/app.c +++ b/applications/plugins/asteroids/app.c @@ -13,23 +13,51 @@ #include #include #include +#include #define TAG "Asteroids" // Used for logging -#define DEBUG_MSG 1 +#define DEBUG_MSG 0 #define SCREEN_XRES 128 #define SCREEN_YRES 64 #define GAME_START_LIVES 3 +#define MAXLIVES 5 /* Max bonus lives allowed. */ #define TTLBUL 30 /* Bullet time to live, in ticks. */ -#define MAXBUL 5 /* Max bullets on the screen. */ +#define MAXBUL 50 /* Max bullets on the screen. */ +//@todo MAX Asteroids #define MAXAST 32 /* Max asteroids on the screen. */ +#define MAXPOWERUPS 3 /* Max powerups allowed on screen */ +#define POWERUPSTTL 400 /* Max powerup time to live, in ticks. */ #define SHIP_HIT_ANIMATION_LEN 15 -#define SAVING_DIRECTORY "/ext/apps_data/asteroids" +#define SAVING_DIRECTORY "/ext/apps/Games" #define SAVING_FILENAME SAVING_DIRECTORY "/game_asteroids.save" #ifndef PI #define PI 3.14159265358979f #endif /* ============================ Data structures ============================= */ +typedef enum PowerUpType { + PowerUpTypeShield, // Shield + PowerUpTypeLife, // Extra life + PowerUpTypeFirePower, // Burst Fire power + // PowerUpTypeRadialFire, // Radial Fire power + PowerUpTypeNuke, // Nuke power + // PowerUpTypeClone, // Clone ship + // PowerUpTypeAssist, // Secondary ship + Number_of_PowerUps // Used to count the number of powerups +} PowerUpType; + +// struct PowerUp +typedef struct PowerUp { + float x, y, vx, vy; /* Fields like in ship. */ + // rot, /* Fields like ship. */ + // rot_speed, /* Angular velocity (rot speed and sense). */ + float size; /* Power Up size */ + + uint32_t ttl; /* Time to live, in ticks. */ + uint32_t display_ttl; /* How long to display the powerup before it disappears */ + enum PowerUpType powerUpType; /* PowerUp type */ + bool isPowerUpActive; /* Is the powerup active? */ +} PowerUp; typedef struct Ship { float x, /* Ship x position. */ @@ -51,6 +79,7 @@ typedef struct Asteroid { uint8_t shape_seed; /* Seed to give random shape. */ } Asteroid; +// @todo AsteroidsApp typedef struct AsteroidsApp { /* GUI */ Gui* gui; @@ -61,6 +90,7 @@ typedef struct AsteroidsApp { /* Game state. */ int running; /* Once false exists the app. */ bool gameover; /* Gameover status. */ + bool paused; /* Game paused status. */ uint32_t ticks; /* Game ticks. Increments at each refresh. */ uint32_t score; /* Game score. */ uint32_t highscore; /* Highscore. Shown on Game Over Screen */ @@ -73,11 +103,14 @@ typedef struct AsteroidsApp { /* Ship state. */ struct Ship ship; + struct PowerUp powerUps[MAXPOWERUPS]; /* Each powerup state. */ + int powerUps_num; /* Active powerups. */ /* Bullets state. */ struct Bullet bullets[MAXBUL]; /* Each bullet state. */ int bullets_num; /* Active bullets. */ uint32_t last_bullet_tick; /* Tick the last bullet was fired. */ + uint32_t bullet_min_period; /* Minimum time between bullets in ms. */ /* Asteroids state. */ Asteroid asteroids[MAXAST]; /* Each asteroid state. */ @@ -133,9 +166,63 @@ const NotificationSequence sequence_bullet_fired = { NULL, }; +const NotificationSequence sequence_powerup_collected = { + &message_vibro_on, + &message_delay_1, + &message_delay_1, + &message_delay_1, + &message_delay_1, + &message_delay_1, + &message_vibro_off, + NULL, +}; + +const NotificationSequence sequence_nuke = { + &message_blink_set_color_red, + &message_blink_start_100, + + &message_vibro_on, + &message_delay_10, + &message_vibro_off, + + &message_vibro_on, + &message_delay_10, + &message_vibro_off, + + &message_vibro_on, + &message_delay_10, + &message_vibro_off, + &message_red_0, + + &message_vibro_on, + &message_delay_10, + &message_delay_1, + &message_delay_1, + &message_vibro_off, + + &message_vibro_on, + &message_delay_10, + &message_delay_1, + &message_delay_1, + &message_vibro_off, + + &message_vibro_on, + &message_delay_10, + &message_delay_1, + &message_delay_1, + &message_vibro_off, + + &message_blink_stop, + &message_vibro_off, + &message_sound_off, + NULL, +}; + /* ============================== Prototyeps ================================ */ // Only functions called before their definition are here. +bool isPowerUpActive(AsteroidsApp* app, enum PowerUpType powerUpType); +bool isPowerUpAlreadyExists(AsteroidsApp* app, enum PowerUpType powerUpType); bool load_game(AsteroidsApp* app); void save_game(AsteroidsApp* app); void restart_game_after_gameover(AsteroidsApp* app); @@ -183,6 +270,8 @@ void lfsr_next(unsigned char* prev) { *prev ^= *prev << 7; /* Mix things a bit more. */ } +/* ================================ Render ================================ */ + /* Render the polygon 'poly' at x,y, rotated by the specified angle. */ void draw_poly(Canvas* const canvas, Poly* poly, uint8_t x, uint8_t y, float a) { Poly rot; @@ -249,20 +338,127 @@ void draw_left_lives(Canvas* const canvas, AsteroidsApp* app) { } } -/* Given the current position, update it according to the velocity and - * wrap it back to the other side if the object went over the screen. */ -void update_pos_by_velocity(float* x, float* y, float vx, float vy) { - /* Return back from one side to the other of the screen. */ - *x += vx; - *y += vy; - if(*x >= SCREEN_XRES) - *x = 0; - else if(*x < 0) - *x = SCREEN_XRES - 1; - if(*y >= SCREEN_YRES) - *y = 0; - else if(*y < 0) - *y = SCREEN_YRES - 1; +bool should_draw_powerUp(PowerUp* const p) { + // Just return if power up has already been picked up + if(p->display_ttl == 0) return false; + + // Begin flashing power up when it is about to expire + if(p->display_ttl < 100) { + return p->display_ttl % 8 > 0; + } + + return true; +} + +void draw_powerUp_RemainingLife(Canvas* canvas, PowerUp* const p, int y_offset) { + if(!p->isPowerUpActive) return; + + /* + Here we generate a reverse progress bar. The bar is 24 pixels wide and 1 pixel tall. + Calculate the percentage of hitpoints left: hitpoints / total hitpoints + Multiply the percentage by the width of the bar (in pixels): percentage * bar width + Subtract the result from the width of the bar to get the filled portion of the bar: bar width - (percentage * bar width) + Round the result to the nearest integer to get the final result. + + 400 / 400 = 1.0 + 1.0 * 24 = 24 + 24 - 24 = 0 + Round(0) = 0 + */ + int progress_bar_width = SCREEN_XRES / 4; + + if(p->ttl > 0) { + canvas_set_color(canvas, ColorBlack); + int remaining = ceil(((float)p->ttl / (float)POWERUPSTTL) * (float)progress_bar_width); + + if(remaining > 0) { + canvas_draw_line( + canvas, + (SCREEN_XRES / 2) - remaining, // x1 + 3 + y_offset, //y1 + (SCREEN_XRES / 2) + remaining, //x2 + 3 + y_offset); // y2 + } + } +} + +void draw_powerUps(Canvas* const canvas, PowerUp* const p) { + /* + + * * * * * * * * * * + * * + * * + * * + * F * + * * + * * + * * + * * + * * * * * * * * * * + + BOX_SIZE = 10 + Box_Width = BOX_SIZE + BOX_HEIGHT = BOX_SIZE + BOX_X_POS = x - BOX_WIDTH/2 + BOX_Y_POS = y - BOX_HEIGHT/2 + POS_F_X = WIDTH/2 + POS_F_Y = HEIGHT/2 + + */ + + //@todo render_callback + + // Just return if power up has already been picked up + // FURI_LOG_I(TAG, "[draw_powerUps] Display TTL: %lu", p->display_ttl); + if(p->display_ttl == 0) return; + if(!should_draw_powerUp(p)) return; + + canvas_set_color(canvas, ColorXOR); + + // Display power up to be picked up + switch(p->powerUpType) { + case PowerUpTypeFirePower: + canvas_draw_icon(canvas, p->x, p->y, &I_firepower_shifted_9x10); + break; + case PowerUpTypeShield: + canvas_draw_icon(canvas, p->x, p->y, &I_shield_frame); + break; + case PowerUpTypeLife: + // Draw a heart + canvas_draw_icon(canvas, p->x, p->y, &I_heart_10x10); + break; + case PowerUpTypeNuke: + // canvas_draw_disc(canvas, p->x, p->y, p->size); + // canvas_draw_str(canvas, p->x, p->y, "N"); + canvas_draw_icon(canvas, p->x, p->y, &I_nuke_10x10); + break; + // case PowerUpTypeRadialFire: + // // Draw box with letter R inside + // canvas_draw_str(canvas, p->x, p->y, "R"); + // break; + // case PowerUpTypeAssist: + // // Draw box with letter A inside + // canvas_draw_str(canvas, p->x, p->y, "A"); + // break; + // case PowerUpTypeClone: + // // Draw box with letter C inside + // canvas_draw_str(canvas, p->x, p->y, "C"); + // break; + default: + //@todo Uknown Power Up Type Detected + // Draw box with letter U inside + canvas_draw_str(canvas, p->x, p->y, "?"); + FURI_LOG_E(TAG, "Unexpected Power Up Type Detected: %i", p->powerUpType); + break; + } +} + +void draw_shield(Canvas* const canvas, AsteroidsApp* app) { + if(isPowerUpActive(app, PowerUpTypeShield) == false) return; + + canvas_set_color(canvas, ColorXOR); + // canvas_draw_disc(canvas, app->ship.x, app->ship.y, 4); + canvas_draw_circle(canvas, app->ship.x, app->ship.y, 8); } /* Render the current game screen. */ @@ -286,6 +482,9 @@ void render_callback(Canvas* const canvas, void* ctx) { /* Draw ship, asteroids, bullets. */ draw_poly(canvas, &ShipPoly, app->ship.x, app->ship.y, app->ship.rot); + /* Draw shield if active. */ + draw_shield(canvas, app); + if(key_pressed_time(app, InputKeyUp) > 0) { notification_message(furi_record_open(RECORD_NOTIFICATION), &sequence_thrusters); draw_poly(canvas, &ShipFirePoly, app->ship.x, app->ship.y, app->ship.rot); @@ -295,6 +494,20 @@ void render_callback(Canvas* const canvas, void* ctx) { for(int j = 0; j < app->asteroids_num; j++) draw_asteroid(canvas, &app->asteroids[j]); + for(int j = 0; j < app->powerUps_num; j++) { + draw_powerUps(canvas, &app->powerUps[j]); + draw_powerUp_RemainingLife(canvas, &app->powerUps[j], j); + } + + if(app->paused) { + canvas_set_color(canvas, ColorXOR); + canvas_set_font(canvas, FontPrimary); + canvas_draw_rbox(canvas, 0, 0, SCREEN_XRES, SCREEN_YRES, 4); + canvas_draw_str_aligned( + canvas, SCREEN_XRES / 2, SCREEN_YRES / 2, AlignCenter, AlignCenter, "Paused"); + return; + } + /* Game over text. */ if(app->gameover) { canvas_set_color(canvas, ColorBlack); @@ -331,6 +544,22 @@ void render_callback(Canvas* const canvas, void* ctx) { /* ============================ Game logic ================================== */ +/* Given the current position, update it according to the velocity and + * wrap it back to the other side if the object went over the screen. */ +void update_pos_by_velocity(float* x, float* y, float vx, float vy) { + /* Return back from one side to the other of the screen. */ + *x += vx; + *y += vy; + if(*x >= SCREEN_XRES) + *x = 0; + else if(*x < 0) + *x = SCREEN_XRES - 1; + if(*y >= SCREEN_YRES) + *y = 0; + else if(*y < 0) + *y = SCREEN_YRES - 1; +} + float distance(float x1, float y1, float x2, float y2) { float dx = x1 - x2; float dy = y1 - y2; @@ -363,9 +592,16 @@ bool objects_are_colliding(float x1, float y1, float r1, float x2, float y2, flo return dx * dx + dy * dy < rsum * rsum; } +/* ================================ Bullets ================================ */ +//@todo ship_fire_bullet /* Create a new bullet headed in the same direction of the ship. */ void ship_fire_bullet(AsteroidsApp* app) { - if(app->bullets_num == MAXBUL) return; + // No power ups, only 5 bullets allowed + if(isPowerUpActive(app, PowerUpTypeFirePower) == false && app->bullets_num >= 5) return; + + // Double the Fire Power + if(isPowerUpActive(app, PowerUpTypeFirePower) && (app->bullets_num >= (MAXBUL))) return; + notification_message(furi_record_open(RECORD_NOTIFICATION), &sequence_bullet_fired); Bullet* b = &app->bullets[app->bullets_num]; b->x = app->ship.x; @@ -401,6 +637,7 @@ void remove_bullet(AsteroidsApp* app, int bid) { if(n && bid != n) app->bullets[bid] = app->bullets[n]; } +/* ================================ Asteroids ================================ */ /* Create a new asteroid, away from the ship. Return the * pointer to the asteroid object, so that the caller can change * certain things of the asteroid if needed. */ @@ -466,6 +703,159 @@ void asteroid_was_hit(AsteroidsApp* app, int id) { } } +/* ================================ Power Up ================================ */ +bool isPowerUpCollidingWithEachOther(AsteroidsApp* app, float x, float y, float size) { + for(int i = 0; i < app->powerUps_num; i++) { + PowerUp* p2 = &app->powerUps[i]; + if(objects_are_colliding(x, y, size, p2->x, p2->y, p2->size, 1)) return true; + } + return false; +} + +bool should_trigger_rare_powerUp(PowerUpType selected_powerUpType) { + switch(selected_powerUpType) { + case PowerUpTypeLife: // Make extra life power up more rare + return rand() % 10 != 0; + case PowerUpTypeShield: // Make shield power up more rare + return rand() % 4 != 0; + default: + return true; + } +} + +//@todo Add PowerUp +PowerUp* add_powerUp(AsteroidsApp* app) { + FURI_LOG_I(TAG, "add_powerUp: %i", app->powerUps_num); + if(app->powerUps_num == MAXPOWERUPS) return NULL; // Max Power Ups reached + if(app->lives == MAXLIVES) return NULL; // Max Lives reached + + // Randomly select power up for display + PowerUpType selected_powerUpType = rand() % Number_of_PowerUps; + FURI_LOG_I(TAG, "[add_powerUp] Power Up Selected: %i", selected_powerUpType); + + // Don't add already existing power ups + if(isPowerUpAlreadyExists(app, selected_powerUpType)) { + FURI_LOG_D(TAG, "[add_powerUp] Power Up %i already active", selected_powerUpType); + return NULL; + } + + // Make some power ups more rare + if(!should_trigger_rare_powerUp(selected_powerUpType)) { + FURI_LOG_D(TAG, "[add_powerUp] Power Up %i not triggered", selected_powerUpType); + return NULL; + } + + float size = 10; + float min_distance = 20; + float x, y; + do { + //Make sure power up is not spawned on the edge of the screen + x = rand() % (SCREEN_XRES - (int)size); + y = rand() % (SCREEN_YRES - (int)size); + + //Also keep it away from the lives and score at the top of screen + if(y < size) y = size; + } while( + ((distance(app->ship.x, app->ship.y, x, y) < min_distance + size) || + isPowerUpCollidingWithEachOther(app, x, y, size))); + + PowerUp* p = &app->powerUps[app->powerUps_num++]; + p->x = x; + p->y = y; + //@todo Disable Velocity + p->vx = 0; //2 * (-.5 + ((float)rand() / RAND_MAX)); + p->vy = 0; //2 * (-.5 + ((float)rand() / RAND_MAX)); + p->display_ttl = 200; + p->ttl = POWERUPSTTL; + p->size = size; + // p->size = size; + // p->rot = 0; + // p->rot_speed = ((float)rand() / RAND_MAX) / 10; + // if(app->ticks & 1) p->rot_speed = -(p->rot_speed); + + //@todo add powerup type, for now hardcoding to firepower + p->powerUpType = selected_powerUpType; + p->isPowerUpActive = false; + FURI_LOG_I(TAG, "[add_powerUp] Power Up Added: %i", p->powerUpType); + return p; +} + +bool isPowerUpActive(AsteroidsApp* const app, PowerUpType const powerUpType) { + for(int i = 0; i < app->powerUps_num; i++) { + // PowerUp* p = &app->powerUps[i]; + // if(p->powerUpType == powerUpType && p->ttl > 0 && p->display_ttl == 0) return true; + if(app->powerUps[i].isPowerUpActive && app->powerUps[i].powerUpType == powerUpType) { + return true; + } + } + return false; +} + +bool isPowerUpAlreadyExists(AsteroidsApp* const app, PowerUpType const powerUpType) { + for(int i = 0; i < app->powerUps_num; i++) { + if(app->powerUps[i].powerUpType == powerUpType) return true; + } + return false; +} + +//@todo remove_powerUp +void remove_powerUp(AsteroidsApp* app, int id) { + FURI_LOG_I(TAG, "remove_powerUp: %i", id); + // Invalid ID, ignore + if(id < 0) { + FURI_LOG_E(TAG, "remove_powerUp: Invalid ID: %i", id); + return; + } + // TODO: Break this out into object types that set the game state + // Return the bullet period to normal + if(app->powerUps[id].powerUpType == PowerUpTypeFirePower) { + app->bullet_min_period = 200; + } + + /* Replace the top powerUp with the empty space left + * by the removal of this one. This way we always take the + * array dense, which is an advantage when looping. */ + int n = --app->powerUps_num; + if(n && id != n) app->powerUps[id] = app->powerUps[n]; +} + +void remove_all_astroids_and_bullets(AsteroidsApp* app) { + app->score += app->asteroids_num; + app->asteroids_num = 0; + app->bullets_num = 0; +} + +//@todo powerUp_was_hit +void powerUp_was_hit(AsteroidsApp* app, int id) { + PowerUp* p = &app->powerUps[id]; + if(p->display_ttl == 0) return; // Don't collect if already collected or expired + + // Vibrate to indicate power up was collected + notification_message(furi_record_open(RECORD_NOTIFICATION), &sequence_powerup_collected); + + switch(p->powerUpType) { + case PowerUpTypeLife: + if(app->lives < MAXLIVES) app->lives++; + remove_powerUp(app, id); + break; + case PowerUpTypeFirePower: + p->ttl = POWERUPSTTL / 2; + app->bullet_min_period = 100; + break; + case PowerUpTypeNuke: + //TODO: Animate explosion + notification_message(furi_record_open(RECORD_NOTIFICATION), &sequence_nuke); + // Simulate nuke explosion + remove_all_astroids_and_bullets(app); + break; + default: + break; + } + p->display_ttl = 0; + p->isPowerUpActive = true; +} + +/* ================================ Game States ================================ */ /* Set gameover state. When in game-over mode, the game displays a gameover * text with a background of many asteroids floating around. */ void game_over(AsteroidsApp* app) { @@ -494,7 +884,9 @@ void restart_game(AsteroidsApp* app) { app->ship.vx = 0; app->ship.vy = 0; app->bullets_num = 0; + app->powerUps_num = 0; app->last_bullet_tick = 0; + app->bullet_min_period = 200; app->asteroids_num = 0; app->ship_hit = 0; } @@ -510,6 +902,7 @@ void restart_game_after_gameover(AsteroidsApp* app) { restart_game(app); } +/* ================================ Position & Status ================================ */ /* Move bullets. */ void update_bullets_position(AsteroidsApp* app) { for(int j = 0; j < app->bullets_num; j++) { @@ -536,6 +929,68 @@ void update_asteroids_position(AsteroidsApp* app) { } } +bool should_add_powerUp(AsteroidsApp* app) { + srand(furi_get_tick()); + int random_number = rand() % 100 + 1; + + // The chance of spawning a power-up decreases as the game goes on + int threshold = 100 - (app->score * 5); + + // Make sure the threshold doesn't go below 10 + threshold = (threshold < 10) ? 10 : threshold; + // FURI_LOG_I( + // TAG, + // "Random number: %d, threshold: %d Bool: %d", + // random_number, + // threshold, + // random_number <= threshold); + return random_number <= threshold; +} + +void update_powerUps_position(AsteroidsApp* app) { + for(int j = 0; j < app->powerUps_num; j++) { + // @todo update_powerUps_position + if(app->powerUps[j].display_ttl > 0) { + update_pos_by_velocity( + &app->powerUps[j].x, &app->powerUps[j].y, app->powerUps[j].vx, app->powerUps[j].vy); + } + } +} + +// @todo update_powerUp_status +/* This updates the state of each power up collected and removes them if they have expired. */ +void update_powerUp_status(AsteroidsApp* app) { + for(int j = 0; j < app->powerUps_num; j++) { + if(app->powerUps[j].ttl > 0 && app->powerUps[j].isPowerUpActive) { + // Only decrement ttl if we actually picked up power up + app->powerUps[j].ttl--; + } else if(app->powerUps[j].display_ttl > 0) { + app->powerUps[j].display_ttl--; + } else if(app->powerUps[j].ttl == 0 || app->powerUps[j].display_ttl == 0) { + FURI_LOG_I( + TAG, + "[update_powerUp_status] Power up expired!, ttl: %lu, display_ttl: %lu id: %d", + app->powerUps[j].ttl, + app->powerUps[j].display_ttl, + j); + // we've reached the end of life of the power up + // Time to remove it + app->powerUps[j].isPowerUpActive = false; + remove_powerUp(app, j); + j--; /* Process this power up index again: the removal will + fill it with the top power up to take the array dense. */ + } else { + FURI_LOG_E( + TAG, + "[update_powerUp_status] Power up error! Invalid Index: %d ttl: %lu display_ttl: %lu PowerUp_Num: %d", + j, + app->powerUps[j].ttl, + app->powerUps[j].display_ttl, + app->powerUps_num); + } + } +} + /* Collision detection and game state update based on collisions. */ void detect_collisions(AsteroidsApp* app) { /* Detect collision between bullet and asteroid. */ @@ -560,8 +1015,26 @@ void detect_collisions(AsteroidsApp* app) { for(int j = 0; j < app->asteroids_num; j++) { Asteroid* a = &app->asteroids[j]; if(objects_are_colliding(a->x, a->y, a->size, app->ship.x, app->ship.y, 4, 1)) { - ship_was_hit(app); - break; + if(isPowerUpActive(app, PowerUpTypeShield)) { + // Asteroid was hit with shield + notification_message( + furi_record_open(RECORD_NOTIFICATION), &sequence_bullet_fired); + asteroid_was_hit(app, j); + j--; /* Scan this j value again. */ + } else { + // No sheild active, take damage + ship_was_hit(app); + break; + } + } + } + + /* Detect collision between ship and powerUp. */ + for(int j = 0; j < app->powerUps_num; j++) { + PowerUp* p = &app->powerUps[j]; + if(objects_are_colliding(p->x, p->y, p->size, app->ship.x, app->ship.y, 4, 1)) { + powerUp_was_hit(app, j); + // break; } } } @@ -599,6 +1072,12 @@ void game_tick(void* ctx) { update_asteroids_position(app); view_port_update(app->view_port); return; + } else if(app->paused) { + if(key_pressed_time(app, InputKeyBack) > 100 || key_pressed_time(app, InputKeyOk) > 100) { + app->paused = false; + } + view_port_update(app->view_port); + return; } /* Handle keypresses. */ @@ -617,19 +1096,32 @@ void game_tick(void* ctx) { * asteroids_update_keypress_state() since depends on exact * pressure timing. */ if(app->fire) { - uint32_t bullet_min_period = 200; // In milliseconds uint32_t now = furi_get_tick(); - if(now - app->last_bullet_tick >= bullet_min_period) { + if(now - app->last_bullet_tick >= app->bullet_min_period) { ship_fire_bullet(app); app->last_bullet_tick = now; } app->fire = false; } + // DEBUG: Show Power Up Status + // for(int j = 0; j < app->powerUps_num; j++) { + // PowerUp* p = &app->powerUps[j]; + // FURI_LOG_I( + // TAG, + // "Power Up Type: %d TTL: %lu Display_TTL: %lu PowerUpNum: %i", + // p->powerUpType, + // p->ttl, + // p->display_ttl, + // app->powerUps_num); + // } + /* Update positions and detect collisions. */ update_pos_by_velocity(&app->ship.x, &app->ship.y, app->ship.vx, app->ship.vy); update_bullets_position(app); update_asteroids_position(app); + update_powerUp_status(app); //@todo update_powerUp_status + update_powerUps_position(app); detect_collisions(app); /* From time to time, create a new asteroid. The more asteroids @@ -639,6 +1131,13 @@ void game_tick(void* ctx) { add_asteroid(app); } + /* From time to time add a random power up */ + //@todo game tick + // if(app->powerUps_num == 0 || random() % (500 + (100 * (int)app->score)) <= app->powerUps_num) { + if(should_add_powerUp(app)) { + add_powerUp(app); + } + app->ticks++; view_port_update(app->view_port); } @@ -760,8 +1259,17 @@ int32_t asteroids_app_entry(void* p) { while(app->running) { FuriStatus qstat = furi_message_queue_get(app->event_queue, &input, 100); if(qstat == FuriStatusOk) { - if(DEBUG_MSG) - FURI_LOG_E(TAG, "Main Loop - Input: type %d key %u", input.type, input.key); + // if(DEBUG_MSG) + // FURI_LOG_E(TAG, "Main Loop - Input: type %d key %u", input.type, input.key); + /* Handle Pause */ + if(input.type == InputTypeShort && input.key == InputKeyBack) { + app->paused = !app->paused; + if(app->paused) { + furi_timer_stop(timer); + } else { + furi_timer_start(timer, furi_kernel_get_tick_frequency() / 10); + } + } /* Handle navigation here. Then handle view-specific inputs * in the view specific handling function. */ @@ -775,11 +1283,11 @@ int32_t asteroids_app_entry(void* p) { } else { /* Useful to understand if the app is still alive when it * does not respond because of bugs. */ - if(DEBUG_MSG) { - static int c = 0; - c++; - if(!(c % 20)) FURI_LOG_E(TAG, "Loop timeout"); - } + // if(DEBUG_MSG) { + // static int c = 0; + // c++; + // if(!(c % 20)) FURI_LOG_E(TAG, "Loop timeout"); + // } } } diff --git a/applications/plugins/asteroids/application.fam b/applications/plugins/asteroids/application.fam index f5ad2cdb9..5eb43a6e5 100644 --- a/applications/plugins/asteroids/application.fam +++ b/applications/plugins/asteroids/application.fam @@ -3,10 +3,14 @@ App( name="Asteroids", apptype=FlipperAppType.EXTERNAL, entry_point="asteroids_app_entry", - cdefines=["APP_PROTOVIEW"], + cdefines=["APP_ASTEROIDS"], requires=["gui"], stack_size=8 * 1024, order=50, fap_icon="appicon.png", fap_category="Games", + fap_icon_assets="assets", # Image assets to compile for this application + fap_description="An implementation of the classic arcade game Asteroids", + fap_author="antirez, SimplyMinimal", + fap_weburl="https://github.com/SimplyMinimal/FlipperZero-Asteroids", ) diff --git a/applications/plugins/asteroids/assets/ammo_10x10.png b/applications/plugins/asteroids/assets/ammo_10x10.png new file mode 100644 index 000000000..b112a1a7f Binary files /dev/null and b/applications/plugins/asteroids/assets/ammo_10x10.png differ diff --git a/applications/plugins/asteroids/assets/ammo_11x11.png b/applications/plugins/asteroids/assets/ammo_11x11.png new file mode 100644 index 000000000..55e59f858 Binary files /dev/null and b/applications/plugins/asteroids/assets/ammo_11x11.png differ diff --git a/applications/plugins/asteroids/assets/firepower_12x12.png b/applications/plugins/asteroids/assets/firepower_12x12.png new file mode 100644 index 000000000..711a29200 Binary files /dev/null and b/applications/plugins/asteroids/assets/firepower_12x12.png differ diff --git a/applications/plugins/asteroids/assets/firepower_9x10.png b/applications/plugins/asteroids/assets/firepower_9x10.png new file mode 100644 index 000000000..4070b6c88 Binary files /dev/null and b/applications/plugins/asteroids/assets/firepower_9x10.png differ diff --git a/applications/plugins/asteroids/assets/firepower_shifted_9x10.png b/applications/plugins/asteroids/assets/firepower_shifted_9x10.png new file mode 100644 index 000000000..9c8506d18 Binary files /dev/null and b/applications/plugins/asteroids/assets/firepower_shifted_9x10.png differ diff --git a/applications/plugins/asteroids/assets/heart_10x10.png b/applications/plugins/asteroids/assets/heart_10x10.png new file mode 100644 index 000000000..0d66b49ee Binary files /dev/null and b/applications/plugins/asteroids/assets/heart_10x10.png differ diff --git a/applications/plugins/asteroids/assets/heart_12x12.png b/applications/plugins/asteroids/assets/heart_12x12.png new file mode 100644 index 000000000..b1cfdcdfe Binary files /dev/null and b/applications/plugins/asteroids/assets/heart_12x12.png differ diff --git a/applications/plugins/asteroids/assets/nuke_10x10.png b/applications/plugins/asteroids/assets/nuke_10x10.png new file mode 100644 index 000000000..8b49fc98e Binary files /dev/null and b/applications/plugins/asteroids/assets/nuke_10x10.png differ diff --git a/applications/plugins/asteroids/assets/shield-frame.png b/applications/plugins/asteroids/assets/shield-frame.png new file mode 100644 index 000000000..60a670f0e Binary files /dev/null and b/applications/plugins/asteroids/assets/shield-frame.png differ diff --git a/applications/plugins/asteroids/assets/shield_clean.png b/applications/plugins/asteroids/assets/shield_clean.png new file mode 100644 index 000000000..59eefb717 Binary files /dev/null and b/applications/plugins/asteroids/assets/shield_clean.png differ diff --git a/applications/plugins/asteroids/assets/split_shield_10x10.png b/applications/plugins/asteroids/assets/split_shield_10x10.png new file mode 100644 index 000000000..bff879ca1 Binary files /dev/null and b/applications/plugins/asteroids/assets/split_shield_10x10.png differ diff --git a/applications/plugins/barcode_generator/application.fam b/applications/plugins/barcode_generator/application.fam index 97dc9acef..a02255334 100644 --- a/applications/plugins/barcode_generator/application.fam +++ b/applications/plugins/barcode_generator/application.fam @@ -9,7 +9,7 @@ App( "dialogs", ], stack_size=1 * 1024, - order=250, + order=50, fap_icon="barcode_10px.png", fap_category="Misc", ) diff --git a/applications/plugins/barcode_generator/barcode_generator.c b/applications/plugins/barcode_generator/barcode_generator.c index 4aa54e7d4..2645bbcea 100644 --- a/applications/plugins/barcode_generator/barcode_generator.c +++ b/applications/plugins/barcode_generator/barcode_generator.c @@ -1,8 +1,3 @@ -#include -#include -#include -#include - #include "barcode_generator.h" static BarcodeType* barcodeTypes[NUMBER_OF_BARCODE_TYPES]; @@ -103,9 +98,9 @@ int get_menu_text_location(int index) { } int get_barcode_max_index(PluginState* plugin_state) { - return plugin_state->doParityCalculation ? - barcodeTypes[plugin_state->barcodeTypeIndex]->numberOfDigits - 1 : - barcodeTypes[plugin_state->barcodeTypeIndex]->numberOfDigits; + return plugin_state->barcode_state.doParityCalculation ? + barcodeTypes[plugin_state->barcode_state.barcodeTypeIndex]->numberOfDigits - 1 : + barcodeTypes[plugin_state->barcode_state.barcodeTypeIndex]->numberOfDigits; } int calculate_check_digit(PluginState* plugin_state, BarcodeType* type) { @@ -114,12 +109,12 @@ int calculate_check_digit(PluginState* plugin_state, BarcodeType* type) { int checkDigitEven = 0; //add all odd positions. Confusing because 0index for(int i = 0; i < type->numberOfDigits - 1; i += 2) { - checkDigitOdd += plugin_state->barcodeNumeral[i]; + checkDigitOdd += plugin_state->barcode_state.barcodeNumeral[i]; } //add all even positions to above. Confusing because 0index for(int i = 1; i < type->numberOfDigits - 1; i += 2) { - checkDigitEven += plugin_state->barcodeNumeral[i]; + checkDigitEven += plugin_state->barcode_state.barcodeNumeral[i]; } if(type->bartype == BarTypeEAN13) { @@ -135,10 +130,9 @@ int calculate_check_digit(PluginState* plugin_state, BarcodeType* type) { } static void render_callback(Canvas* const canvas, void* ctx) { - PluginState* plugin_state = acquire_mutex((ValueMutex*)ctx, 25); - if(plugin_state == NULL) { - return; - } + furi_assert(ctx); + PluginState* plugin_state = ctx; + furi_mutex_acquire(plugin_state->mutex, FuriWaitForever); if(plugin_state->mode == MenuMode) { canvas_set_color(canvas, ColorBlack); @@ -152,7 +146,7 @@ static void render_callback(Canvas* const canvas, void* ctx) { canvas, 64, get_menu_text_location(2), AlignCenter, AlignCenter, "Parity?"); canvas_draw_frame(canvas, 83, get_menu_text_location(2) - 3, 6, 6); - if(plugin_state->doParityCalculation == true) { + if(plugin_state->barcode_state.doParityCalculation == true) { canvas_draw_box(canvas, 85, get_menu_text_location(2) - 1, 2, 2); } canvas_draw_str_aligned( @@ -161,14 +155,14 @@ static void render_callback(Canvas* const canvas, void* ctx) { get_menu_text_location(3), AlignCenter, AlignCenter, - (barcodeTypes[plugin_state->barcodeTypeIndex])->name); + (barcodeTypes[plugin_state->barcode_state.barcodeTypeIndex])->name); canvas_draw_disc( canvas, 40, get_menu_text_location(plugin_state->menuIndex) - 1, 2); //draw menu cursor } else { - BarcodeType* type = barcodeTypes[plugin_state->barcodeTypeIndex]; + BarcodeType* type = barcodeTypes[plugin_state->barcode_state.barcodeTypeIndex]; //start saftey canvas_set_color(canvas, ColorBlack); @@ -181,13 +175,13 @@ static void render_callback(Canvas* const canvas, void* ctx) { startpos++; draw_digit( canvas, - plugin_state->barcodeNumeral[0], + plugin_state->barcode_state.barcodeNumeral[0], BarEncodingTypeRight, - get_digit_position(0, barcodeTypes[plugin_state->barcodeTypeIndex]), + get_digit_position(0, barcodeTypes[plugin_state->barcode_state.barcodeTypeIndex]), false); } - if(plugin_state->doParityCalculation) { //calculate the check digit - plugin_state->barcodeNumeral[type->numberOfDigits - 1] = + if(plugin_state->barcode_state.doParityCalculation) { //calculate the check digit + plugin_state->barcode_state.barcodeNumeral[type->numberOfDigits - 1] = calculate_check_digit(plugin_state, type); } for(int index = startpos; index < endpos; index++) { @@ -197,7 +191,9 @@ static void render_callback(Canvas* const canvas, void* ctx) { barEncodingType = BarEncodingTypeRight; } else { barEncodingType = - (FURI_BIT(EAN13ENCODE[plugin_state->barcodeNumeral[0]], index - 1)) ? + (FURI_BIT( + EAN13ENCODE[plugin_state->barcode_state.barcodeNumeral[0]], + index - 1)) ? BarEncodingTypeG : BarEncodingTypeLeft; } @@ -207,10 +203,14 @@ static void render_callback(Canvas* const canvas, void* ctx) { } } - int digitPosition = - get_digit_position(index, barcodeTypes[plugin_state->barcodeTypeIndex]); + int digitPosition = get_digit_position( + index, barcodeTypes[plugin_state->barcode_state.barcodeTypeIndex]); draw_digit( - canvas, plugin_state->barcodeNumeral[index], barEncodingType, digitPosition, true); + canvas, + plugin_state->barcode_state.barcodeNumeral[index], + barEncodingType, + digitPosition, + true); } //central separator @@ -223,7 +223,8 @@ static void render_callback(Canvas* const canvas, void* ctx) { canvas_draw_box( canvas, get_digit_position( - plugin_state->editingIndex, barcodeTypes[plugin_state->barcodeTypeIndex]) - + plugin_state->editingIndex, + barcodeTypes[plugin_state->barcode_state.barcodeTypeIndex]) - 1, 63, 7, @@ -237,7 +238,7 @@ static void render_callback(Canvas* const canvas, void* ctx) { canvas_draw_box(canvas, (endSafetyPosition + 2), BARCODE_Y_START, 1, BARCODE_HEIGHT + 2); } - release_mutex((ValueMutex*)ctx, plugin_state); + furi_mutex_release(plugin_state->mutex); } static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { @@ -247,15 +248,17 @@ static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queu furi_message_queue_put(event_queue, &event, FuriWaitForever); } -static void barcode_generator_state_init(PluginState* const plugin_state) { - for(int i = 0; i < BARCODE_MAX_LENS; ++i) { - plugin_state->barcodeNumeral[i] = i % 10; - } +static void barcode_generator_state_init(PluginState* plugin_state) { plugin_state->editingIndex = 0; plugin_state->mode = ViewMode; - plugin_state->doParityCalculation = true; plugin_state->menuIndex = MENU_INDEX_VIEW; - plugin_state->barcodeTypeIndex = 0; + if(!LOAD_BARCODE_SETTINGS(&plugin_state->barcode_state)) { + for(int i = 0; i < BARCODE_MAX_LENS; ++i) { + plugin_state->barcode_state.barcodeNumeral[i] = i % 10; + } + plugin_state->barcode_state.doParityCalculation = true; + plugin_state->barcode_state.barcodeTypeIndex = 0; + } } static bool handle_key_press_view(InputKey key, PluginState* plugin_state) { @@ -277,15 +280,15 @@ static bool handle_key_press_edit(InputKey key, PluginState* plugin_state) { switch(key) { case InputKeyUp: - plugin_state->barcodeNumeral[plugin_state->editingIndex] = - (plugin_state->barcodeNumeral[plugin_state->editingIndex] + 1) % 10; + plugin_state->barcode_state.barcodeNumeral[plugin_state->editingIndex] = + (plugin_state->barcode_state.barcodeNumeral[plugin_state->editingIndex] + 1) % 10; break; case InputKeyDown: - plugin_state->barcodeNumeral[plugin_state->editingIndex] = - (plugin_state->barcodeNumeral[plugin_state->editingIndex] == 0) ? + plugin_state->barcode_state.barcodeNumeral[plugin_state->editingIndex] = + (plugin_state->barcode_state.barcodeNumeral[plugin_state->editingIndex] == 0) ? 9 : - plugin_state->barcodeNumeral[plugin_state->editingIndex] - 1; + plugin_state->barcode_state.barcodeNumeral[plugin_state->editingIndex] - 1; break; case InputKeyRight: @@ -324,21 +327,24 @@ static bool handle_key_press_menu(InputKey key, PluginState* plugin_state) { case InputKeyRight: if(plugin_state->menuIndex == MENU_INDEX_TYPE) { - plugin_state->barcodeTypeIndex = - (plugin_state->barcodeTypeIndex == NUMBER_OF_BARCODE_TYPES - 1) ? + plugin_state->barcode_state.barcodeTypeIndex = + (plugin_state->barcode_state.barcodeTypeIndex == NUMBER_OF_BARCODE_TYPES - 1) ? 0 : - plugin_state->barcodeTypeIndex + 1; + plugin_state->barcode_state.barcodeTypeIndex + 1; } else if(plugin_state->menuIndex == MENU_INDEX_PARITY) { - plugin_state->doParityCalculation = !plugin_state->doParityCalculation; + plugin_state->barcode_state.doParityCalculation = + !plugin_state->barcode_state.doParityCalculation; } break; case InputKeyLeft: if(plugin_state->menuIndex == MENU_INDEX_TYPE) { - plugin_state->barcodeTypeIndex = (plugin_state->barcodeTypeIndex == 0) ? - NUMBER_OF_BARCODE_TYPES - 1 : - plugin_state->barcodeTypeIndex - 1; + plugin_state->barcode_state.barcodeTypeIndex = + (plugin_state->barcode_state.barcodeTypeIndex == 0) ? + NUMBER_OF_BARCODE_TYPES - 1 : + plugin_state->barcode_state.barcodeTypeIndex - 1; } else if(plugin_state->menuIndex == MENU_INDEX_PARITY) { - plugin_state->doParityCalculation = !plugin_state->doParityCalculation; + plugin_state->barcode_state.doParityCalculation = + !plugin_state->barcode_state.doParityCalculation; } break; @@ -348,12 +354,13 @@ static bool handle_key_press_menu(InputKey key, PluginState* plugin_state) { } else if(plugin_state->menuIndex == MENU_INDEX_EDIT) { plugin_state->mode = EditMode; } else if(plugin_state->menuIndex == MENU_INDEX_PARITY) { - plugin_state->doParityCalculation = !plugin_state->doParityCalculation; + plugin_state->barcode_state.doParityCalculation = + !plugin_state->barcode_state.doParityCalculation; } else if(plugin_state->menuIndex == MENU_INDEX_TYPE) { - plugin_state->barcodeTypeIndex = - (plugin_state->barcodeTypeIndex == NUMBER_OF_BARCODE_TYPES - 1) ? + plugin_state->barcode_state.barcodeTypeIndex = + (plugin_state->barcode_state.barcodeTypeIndex == NUMBER_OF_BARCODE_TYPES - 1) ? 0 : - plugin_state->barcodeTypeIndex + 1; + plugin_state->barcode_state.barcodeTypeIndex + 1; } break; @@ -379,8 +386,9 @@ int32_t barcode_generator_app(void* p) { PluginState* plugin_state = malloc(sizeof(PluginState)); barcode_generator_state_init(plugin_state); - ValueMutex state_mutex; - if(!init_mutex(&state_mutex, plugin_state, sizeof(PluginState))) { + + plugin_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!plugin_state->mutex) { FURI_LOG_E("barcode_generator", "cannot create mutex\r\n"); furi_message_queue_free(event_queue); free(plugin_state); @@ -389,7 +397,7 @@ int32_t barcode_generator_app(void* p) { // Set system callbacks ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, render_callback, &state_mutex); + view_port_draw_callback_set(view_port, render_callback, plugin_state); view_port_input_callback_set(view_port, input_callback, event_queue); // Open GUI and register view_port @@ -399,7 +407,7 @@ int32_t barcode_generator_app(void* p) { PluginEvent event; for(bool processing = true; processing;) { FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); - PluginState* plugin_state = (PluginState*)acquire_mutex_block(&state_mutex); + furi_mutex_acquire(plugin_state->mutex, FuriWaitForever); if(event_status == FuriStatusOk) { // press events @@ -422,7 +430,7 @@ int32_t barcode_generator_app(void* p) { } view_port_update(view_port); - release_mutex(&state_mutex, plugin_state); + furi_mutex_release(plugin_state->mutex); } view_port_enabled_set(view_port, false); @@ -430,6 +438,10 @@ int32_t barcode_generator_app(void* p) { furi_record_close(RECORD_GUI); view_port_free(view_port); furi_message_queue_free(event_queue); + furi_mutex_free(plugin_state->mutex); + // save settings + SAVE_BARCODE_SETTINGS(&plugin_state->barcode_state); + free(plugin_state); return 0; -} \ No newline at end of file +} diff --git a/applications/plugins/barcode_generator/barcode_generator.h b/applications/plugins/barcode_generator/barcode_generator.h index 1b5ff9e15..5d2c8307e 100644 --- a/applications/plugins/barcode_generator/barcode_generator.h +++ b/applications/plugins/barcode_generator/barcode_generator.h @@ -1,3 +1,34 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#define BARCODE_SETTINGS_FILE_NAME "apps/Misc/barcodegen.save" + +#define BARCODE_SETTINGS_VER (1) +#define BARCODE_SETTINGS_PATH EXT_PATH(BARCODE_SETTINGS_FILE_NAME) +#define BARCODE_SETTINGS_MAGIC (0xC2) + +#define SAVE_BARCODE_SETTINGS(x) \ + saved_struct_save( \ + BARCODE_SETTINGS_PATH, \ + (x), \ + sizeof(BarcodeState), \ + BARCODE_SETTINGS_MAGIC, \ + BARCODE_SETTINGS_VER) + +#define LOAD_BARCODE_SETTINGS(x) \ + saved_struct_load( \ + BARCODE_SETTINGS_PATH, \ + (x), \ + sizeof(BarcodeState), \ + BARCODE_SETTINGS_MAGIC, \ + BARCODE_SETTINGS_VER) + #define BARCODE_HEIGHT 50 #define BARCODE_Y_START 3 #define BARCODE_TEXT_OFFSET 9 @@ -45,11 +76,16 @@ typedef struct { typedef struct { int barcodeNumeral[BARCODE_MAX_LENS]; //The current barcode number + bool doParityCalculation; //Should do parity check? + int barcodeTypeIndex; +} BarcodeState; + +typedef struct { + FuriMutex* mutex; + BarcodeState barcode_state; int editingIndex; //The index of the editing symbol int menuIndex; //The index of the menu cursor Mode mode; //View, edit or menu - bool doParityCalculation; //Should do parity check? - int barcodeTypeIndex; } PluginState; static const int DIGITS[10][4] = { diff --git a/applications/plugins/blackjack/application.fam b/applications/plugins/blackjack/application.fam index 489ce2aeb..6ca8add37 100644 --- a/applications/plugins/blackjack/application.fam +++ b/applications/plugins/blackjack/application.fam @@ -1,5 +1,5 @@ App( - appid="BlackJack", + appid="Blackjack", name="BlackJack", apptype=FlipperAppType.EXTERNAL, entry_point="blackjack_app", diff --git a/applications/plugins/blackjack/blackjack.c b/applications/plugins/blackjack/blackjack.c index 73d393f8b..8b81829c3 100644 --- a/applications/plugins/blackjack/blackjack.c +++ b/applications/plugins/blackjack/blackjack.c @@ -1,7 +1,6 @@ #include #include -#include #include #include @@ -14,7 +13,7 @@ #include "util.h" #include "ui.h" -#include "BlackJack_icons.h" +#include "Blackjack_icons.h" #define DEALER_MAX 17 @@ -33,11 +32,9 @@ static void draw_ui(Canvas* const canvas, const GameState* game_state) { } static void render_callback(Canvas* const canvas, void* ctx) { - const GameState* game_state = acquire_mutex((ValueMutex*)ctx, 25); - - if(game_state == NULL) { - return; - } + furi_assert(ctx); + const GameState* game_state = ctx; + furi_mutex_acquire(game_state->mutex, FuriWaitForever); canvas_set_color(canvas, ColorBlack); canvas_draw_frame(canvas, 0, 0, 128, 64); @@ -60,7 +57,7 @@ static void render_callback(Canvas* const canvas, void* ctx) { settings_page(canvas, game_state); } - release_mutex((ValueMutex*)ctx, game_state); + furi_mutex_release(game_state->mutex); } //region card draw @@ -552,68 +549,65 @@ int32_t blackjack_app(void* p) { game_state->state = GameStateStart; - ValueMutex state_mutex; - if(!init_mutex(&state_mutex, game_state, sizeof(GameState))) { + game_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!game_state->mutex) { FURI_LOG_E(APP_NAME, "cannot create mutex\r\n"); return_code = 255; goto free_and_exit; } ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, render_callback, &state_mutex); + view_port_draw_callback_set(view_port, render_callback, game_state); view_port_input_callback_set(view_port, input_callback, event_queue); FuriTimer* timer = furi_timer_alloc(update_timer_callback, FuriTimerTypePeriodic, event_queue); furi_timer_start(timer, furi_kernel_get_tick_frequency() / 25); - Gui* gui = furi_record_open("gui"); + Gui* gui = furi_record_open(RECORD_GUI); gui_add_view_port(gui, view_port, GuiLayerFullscreen); AppEvent event; for(bool processing = true; processing;) { FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); - GameState* localstate = (GameState*)acquire_mutex_block(&state_mutex); + furi_mutex_acquire(game_state->mutex, FuriWaitForever); if(event_status == FuriStatusOk) { if(event.type == EventTypeKey) { if(event.input.type == InputTypePress) { switch(event.input.key) { case InputKeyUp: - localstate->selectDirection = DirectionUp; + game_state->selectDirection = DirectionUp; break; case InputKeyDown: - localstate->selectDirection = DirectionDown; + game_state->selectDirection = DirectionDown; break; case InputKeyRight: - localstate->selectDirection = DirectionRight; + game_state->selectDirection = DirectionRight; break; case InputKeyLeft: - localstate->selectDirection = DirectionLeft; + game_state->selectDirection = DirectionLeft; break; case InputKeyBack: - if(localstate->state == GameStateSettings) { - localstate->state = GameStateStart; - save_settings(localstate->settings); + if(game_state->state == GameStateSettings) { + game_state->state = GameStateStart; + save_settings(game_state->settings); } else processing = false; break; case InputKeyOk: - localstate->selectDirection = Select; + game_state->selectDirection = Select; break; default: break; } } } else if(event.type == EventTypeTick) { - tick(localstate); - processing = localstate->processing; + tick(game_state); + processing = game_state->processing; } - } else { - //FURI_LOG_D(APP_NAME, "osMessageQueue: event timeout"); - // event timeout } view_port_update(view_port); - release_mutex(&state_mutex, localstate); + furi_mutex_release(game_state->mutex); } furi_timer_free(timer); @@ -621,7 +615,7 @@ int32_t blackjack_app(void* p) { gui_remove_view_port(gui, view_port); furi_record_close(RECORD_GUI); view_port_free(view_port); - delete_mutex(&state_mutex); + furi_mutex_free(game_state->mutex); free_and_exit: free(game_state->deck.cards); @@ -631,4 +625,4 @@ free_and_exit: furi_message_queue_free(event_queue); return return_code; -} \ No newline at end of file +} diff --git a/applications/plugins/blackjack/common/card.c b/applications/plugins/blackjack/common/card.c index 199135bb5..88228fda4 100644 --- a/applications/plugins/blackjack/common/card.c +++ b/applications/plugins/blackjack/common/card.c @@ -173,7 +173,7 @@ uint8_t hand_count(const Card* cards, uint8_t count) { } for(uint8_t i = 0; i < aceCount; i++) { - if((score + 11) <= 21) + if((score + 11 + (aceCount - 1)) <= 21) score += 11; else score++; @@ -350,4 +350,4 @@ void add_hand_region(Hand* to, Hand* from) { add_to_hand(to, from->cards[i]); } } -} \ No newline at end of file +} diff --git a/applications/plugins/blackjack/defines.h b/applications/plugins/blackjack/defines.h index b400badfb..0a3fdf53e 100644 --- a/applications/plugins/blackjack/defines.h +++ b/applications/plugins/blackjack/defines.h @@ -54,6 +54,7 @@ typedef enum { } Direction; typedef struct { + FuriMutex* mutex; Card player_cards[21]; Card dealer_cards[21]; uint8_t player_card_count; diff --git a/applications/plugins/bpmtapper/README.md b/applications/plugins/bpmtapper/README.md index 224d2cb5b..8e88863ee 100644 --- a/applications/plugins/bpmtapper/README.md +++ b/applications/plugins/bpmtapper/README.md @@ -9,6 +9,6 @@ Hit any button other than back repeatedly. Calculates based on the average of th ## Compiling ``` -./fbt fap_bpm_tapper +./fbt firmware_bpm_tapper ``` diff --git a/applications/plugins/bpmtapper/bpm.c b/applications/plugins/bpmtapper/bpm.c index 323a898a4..5720ac4d2 100644 --- a/applications/plugins/bpmtapper/bpm.c +++ b/applications/plugins/bpmtapper/bpm.c @@ -3,7 +3,6 @@ #include #include #include -#include #include #include "BPM_Tapper_icons.h" @@ -91,6 +90,7 @@ static float queue_avg(queue* q) { //} // typedef struct { + FuriMutex* mutex; int taps; double bpm; uint32_t last_stamp; @@ -127,42 +127,43 @@ static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queu } static void render_callback(Canvas* const canvas, void* ctx) { - string_t tempStr; + furi_assert(ctx); + const BPMTapper* bpm_state = ctx; + furi_mutex_acquire(bpm_state->mutex, FuriWaitForever); - const BPMTapper* bpm_state = acquire_mutex((ValueMutex*)ctx, 25); - if(bpm_state == NULL) { - return; - } + FuriString* tempStr; // border //canvas_draw_frame(canvas, 0, 0, 128, 64); canvas_set_font(canvas, FontPrimary); - string_init(tempStr); + tempStr = furi_string_alloc(); - string_printf(tempStr, "Taps: %d", bpm_state->taps); - canvas_draw_str_aligned(canvas, 5, 10, AlignLeft, AlignBottom, string_get_cstr(tempStr)); - string_reset(tempStr); + furi_string_printf(tempStr, "Taps: %d", bpm_state->taps); + canvas_draw_str_aligned(canvas, 5, 10, AlignLeft, AlignBottom, furi_string_get_cstr(tempStr)); + furi_string_reset(tempStr); - string_printf(tempStr, "Queue: %d", bpm_state->tap_queue->size); - canvas_draw_str_aligned(canvas, 70, 10, AlignLeft, AlignBottom, string_get_cstr(tempStr)); - string_reset(tempStr); + furi_string_printf(tempStr, "Queue: %d", bpm_state->tap_queue->size); + canvas_draw_str_aligned(canvas, 70, 10, AlignLeft, AlignBottom, furi_string_get_cstr(tempStr)); + furi_string_reset(tempStr); - string_printf(tempStr, "Interval: %dms", bpm_state->interval); - canvas_draw_str_aligned(canvas, 5, 20, AlignLeft, AlignBottom, string_get_cstr(tempStr)); - string_reset(tempStr); + furi_string_printf(tempStr, "Interval: %ldms", bpm_state->interval); + canvas_draw_str_aligned(canvas, 5, 20, AlignLeft, AlignBottom, furi_string_get_cstr(tempStr)); + furi_string_reset(tempStr); - string_printf(tempStr, "x2 %.2f /2 %.2f", bpm_state->bpm * 2, bpm_state->bpm / 2); - canvas_draw_str_aligned(canvas, 64, 60, AlignCenter, AlignCenter, string_get_cstr(tempStr)); - string_reset(tempStr); + furi_string_printf(tempStr, "x2 %.2f /2 %.2f", bpm_state->bpm * 2, bpm_state->bpm / 2); + canvas_draw_str_aligned( + canvas, 64, 60, AlignCenter, AlignCenter, furi_string_get_cstr(tempStr)); + furi_string_reset(tempStr); - string_printf(tempStr, "%.2f", bpm_state->bpm); + furi_string_printf(tempStr, "%.2f", bpm_state->bpm); canvas_set_font(canvas, FontBigNumbers); - canvas_draw_str_aligned(canvas, 64, 40, AlignCenter, AlignCenter, string_get_cstr(tempStr)); - string_reset(tempStr); + canvas_draw_str_aligned( + canvas, 64, 40, AlignCenter, AlignCenter, furi_string_get_cstr(tempStr)); + furi_string_reset(tempStr); - string_clear(tempStr); + furi_string_free(tempStr); - release_mutex((ValueMutex*)ctx, bpm_state); + furi_mutex_release(bpm_state->mutex); } static void bpm_state_init(BPMTapper* const plugin_state) { @@ -185,8 +186,8 @@ int32_t bpm_tapper_app(void* p) { // setup bpm_state_init(bpm_state); - ValueMutex state_mutex; - if(!init_mutex(&state_mutex, bpm_state, sizeof(bpm_state))) { + bpm_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!bpm_state->mutex) { FURI_LOG_E("BPM-Tapper", "cannot create mutex\r\n"); free(bpm_state); return 255; @@ -197,7 +198,7 @@ int32_t bpm_tapper_app(void* p) { // Set system callbacks ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, render_callback, &state_mutex); + view_port_draw_callback_set(view_port, render_callback, bpm_state); view_port_input_callback_set(view_port, input_callback, event_queue); // Open GUI and register view_port @@ -207,7 +208,7 @@ int32_t bpm_tapper_app(void* p) { PluginEvent event; for(bool processing = true; processing;) { FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); - BPMTapper* bpm_state = (BPMTapper*)acquire_mutex_block(&state_mutex); + furi_mutex_acquire(bpm_state->mutex, FuriWaitForever); if(event_status == FuriStatusOk) { // press events if(event.type == EventTypeKey) { @@ -240,19 +241,16 @@ int32_t bpm_tapper_app(void* p) { } } } - } else { - FURI_LOG_D("BPM-Tapper", "FuriMessageQueue: event timeout"); - // event timeout } view_port_update(view_port); - release_mutex(&state_mutex, bpm_state); + furi_mutex_release(bpm_state->mutex); } view_port_enabled_set(view_port, false); gui_remove_view_port(gui, view_port); furi_record_close("gui"); view_port_free(view_port); furi_message_queue_free(event_queue); - delete_mutex(&state_mutex); + furi_mutex_free(bpm_state->mutex); queue* q = bpm_state->tap_queue; free(q); free(bpm_state); diff --git a/applications/plugins/bpmtapper/images/DolphinCommon_56x48.png b/applications/plugins/bpmtapper/images/DolphinCommon_56x48.png new file mode 100644 index 000000000..089aaed83 Binary files /dev/null and b/applications/plugins/bpmtapper/images/DolphinCommon_56x48.png differ diff --git a/applications/plugins/brainfuck/application.fam b/applications/plugins/brainfuck/application.fam index 6e2b6d1f9..716c44a6a 100644 --- a/applications/plugins/brainfuck/application.fam +++ b/applications/plugins/brainfuck/application.fam @@ -11,5 +11,4 @@ App( fap_icon="bfico.png", fap_category="Misc", fap_icon_assets="icons", - fap_icon_assets_symbol="brainfuck", ) diff --git a/applications/plugins/brainfuck/brainfuck_i.h b/applications/plugins/brainfuck/brainfuck_i.h index d3d27dcbd..3940cecad 100644 --- a/applications/plugins/brainfuck/brainfuck_i.h +++ b/applications/plugins/brainfuck/brainfuck_i.h @@ -29,7 +29,7 @@ typedef unsigned char byte; #include #include -#include +#include #include #include diff --git a/applications/plugins/brainfuck/views/bf_dev_env.c b/applications/plugins/brainfuck/views/bf_dev_env.c index c5f194500..241a4ebc3 100644 --- a/applications/plugins/brainfuck/views/bf_dev_env.c +++ b/applications/plugins/brainfuck/views/bf_dev_env.c @@ -35,9 +35,9 @@ int selectedButton = 0; int saveNotifyCountdown = 0; int execCountdown = 0; -char dspLine0[25] = {}; -char dspLine1[25] = {}; -char dspLine2[25] = {}; +char dspLine0[25] = {0x00}; +char dspLine1[25] = {0x00}; +char dspLine2[25] = {0x00}; static bMapping buttonMappings[12] = { {8, 8, 7, 1}, //0 @@ -361,6 +361,10 @@ static void bf_dev_enter_callback(void* context) { //read into the buffer appDev->dataSize = stream_size(stream); + if(appDev->dataSize > 2000) { + return; //BF file is too large + } + stream_read(stream, (uint8_t*)appDev->dataBuffer, appDev->dataSize); buffered_file_stream_close(stream); diff --git a/applications/plugins/brainfuck/worker.c b/applications/plugins/brainfuck/worker.c index 1b05ac3fd..584bb9fb8 100644 --- a/applications/plugins/brainfuck/worker.c +++ b/applications/plugins/brainfuck/worker.c @@ -1,4 +1,6 @@ #include "worker.h" +#include +#include bool killswitch = false; @@ -207,27 +209,40 @@ static const NotificationSequence led_on = { }; static const NotificationSequence led_off = { - &message_green_0, + &message_blue_0, NULL, }; +void input_kill(void* _ctx) { + UNUSED(_ctx); + killswitch = true; +} + void beginWorker() { status = 1; + + //redefined from furi_hal_resources.c + const GpioPin gpio_button_back = {.port = GPIOC, .pin = LL_GPIO_PIN_13}; + while(inst[instPtr] != 0x00) { if(runOpCount % 500 == 0) { text_box_set_text(wrkrApp->text_box, workerGetOutput()); notification_message(wrkrApp->notifications, &led_on); } + //status 2 indicates failure if(status == 2) { status = 0; break; } - if(killswitch) { + + //read back button directly to avoid weirdness in furi + if(killswitch || !furi_hal_gpio_read(&gpio_button_back)) { status = 0; killswitch = false; break; } + switch(inst[instPtr]) { case '>': rShift(); diff --git a/applications/plugins/caesarcipher/application.fam b/applications/plugins/caesarcipher/application.fam index 4f438d2b3..652585de2 100644 --- a/applications/plugins/caesarcipher/application.fam +++ b/applications/plugins/caesarcipher/application.fam @@ -1,7 +1,7 @@ App( appid="Caesar_Cipher", name="Caesar Cipher", - apptype=FlipperAppType.EXTERNAL, + apptype=FlipperAppType.PLUGIN, entry_point="caesar_cipher_app", cdefines=["APP_CAESAR_CIPHER"], requires=[ diff --git a/applications/plugins/caesarcipher/caesar_cipher.c b/applications/plugins/caesarcipher/caesar_cipher.c index 9eb93e925..d5c4b2b65 100644 --- a/applications/plugins/caesarcipher/caesar_cipher.c +++ b/applications/plugins/caesarcipher/caesar_cipher.c @@ -21,6 +21,7 @@ typedef struct { } PluginEvent; typedef struct { + FuriMutex* mutex; ViewDispatcher* view_dispatcher; TextInput* text_input; TextBox* text_box; @@ -58,7 +59,10 @@ static void build_output(char* input, char* output) { } static void text_input_callback(void* ctx) { - CaesarState* caesar_state = acquire_mutex((ValueMutex*)ctx, 25); + furi_assert(ctx); + CaesarState* caesar_state = ctx; + furi_mutex_acquire(caesar_state->mutex, FuriWaitForever); + FURI_LOG_D("caesar_cipher", "Input text: %s", caesar_state->input); // this is where we build the output. string_to_uppercase(caesar_state->input); @@ -67,13 +71,14 @@ static void text_input_callback(void* ctx) { text_box_set_text(caesar_state->text_box, caesar_state->output); view_dispatcher_switch_to_view(caesar_state->view_dispatcher, 1); - release_mutex((ValueMutex*)ctx, caesar_state); + furi_mutex_release(caesar_state->mutex); } static bool back_event_callback(void* ctx) { - const CaesarState* caesar_state = acquire_mutex((ValueMutex*)ctx, 25); + const CaesarState* caesar_state = ctx; + furi_mutex_acquire(caesar_state->mutex, FuriWaitForever); view_dispatcher_stop(caesar_state->view_dispatcher); - release_mutex((ValueMutex*)ctx, caesar_state); + furi_mutex_release(caesar_state->mutex); return true; } @@ -99,8 +104,8 @@ int32_t caesar_cipher_app() { FURI_LOG_D("caesar_cipher", "Running caesar_cipher_state_init"); caesar_cipher_state_init(caesar_state); - ValueMutex state_mutex; - if(!init_mutex(&state_mutex, caesar_state, sizeof(CaesarState))) { + caesar_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!caesar_state->mutex) { FURI_LOG_E("caesar_cipher", "cannot create mutex\r\n"); free(caesar_state); return 255; @@ -110,7 +115,7 @@ int32_t caesar_cipher_app() { text_input_set_result_callback( caesar_state->text_input, text_input_callback, - &state_mutex, + caesar_state, caesar_state->input, TEXT_BUFFER_SIZE, //clear default text @@ -135,12 +140,12 @@ int32_t caesar_cipher_app() { FURI_LOG_D("ceasar_cipher", "starting view dispatcher"); view_dispatcher_set_navigation_event_callback( caesar_state->view_dispatcher, back_event_callback); - view_dispatcher_set_event_callback_context(caesar_state->view_dispatcher, &state_mutex); + view_dispatcher_set_event_callback_context(caesar_state->view_dispatcher, caesar_state); view_dispatcher_switch_to_view(caesar_state->view_dispatcher, 0); view_dispatcher_run(caesar_state->view_dispatcher); furi_record_close("gui"); - delete_mutex(&state_mutex); + furi_mutex_free(caesar_state->mutex); caesar_cipher_state_free(caesar_state); return 0; diff --git a/applications/plugins/calculator/calculator.c b/applications/plugins/calculator/calculator.c index b121641b0..1ca1d3a86 100644 --- a/applications/plugins/calculator/calculator.c +++ b/applications/plugins/calculator/calculator.c @@ -19,6 +19,7 @@ typedef struct { } selectedPosition; typedef struct { + FuriMutex* mutex; selectedPosition position; //string with the inputted calculator text char text[20]; @@ -201,8 +202,10 @@ void generate_calculator_layout(Canvas* canvas) { }; void calculator_draw_callback(Canvas* canvas, void* ctx) { - const Calculator* calculator_state = acquire_mutex((ValueMutex*)ctx, 25); - UNUSED(ctx); + furi_assert(ctx); + const Calculator* calculator_state = ctx; + furi_mutex_acquire(calculator_state->mutex, FuriWaitForever); + canvas_clear(canvas); //show selected button @@ -240,7 +243,7 @@ void calculator_draw_callback(Canvas* canvas, void* ctx) { //draw cursor canvas_draw_box(canvas, stringWidth + 5, 29, 5, 1); - release_mutex((ValueMutex*)ctx, calculator_state); + furi_mutex_release(calculator_state->mutex); } void calculator_input_callback(InputEvent* input_event, void* ctx) { @@ -315,8 +318,8 @@ int32_t calculator_app(void* p) { FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); Calculator* calculator_state = malloc(sizeof(Calculator)); - ValueMutex calculator_state_mutex; - if(!init_mutex(&calculator_state_mutex, calculator_state, sizeof(Calculator))) { + calculator_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!calculator_state->mutex) { //FURI_LOG_E("calculator", "cannot create mutex\r\n"); free(calculator_state); return -1; @@ -324,7 +327,7 @@ int32_t calculator_app(void* p) { // Configure view port ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, calculator_draw_callback, &calculator_state_mutex); + view_port_draw_callback_set(view_port, calculator_draw_callback, calculator_state); view_port_input_callback_set(view_port, calculator_input_callback, event_queue); view_port_set_orientation(view_port, ViewPortOrientationVertical); @@ -444,10 +447,12 @@ int32_t calculator_app(void* p) { } gui_remove_view_port(gui, view_port); view_port_free(view_port); + furi_mutex_free(calculator_state->mutex); furi_message_queue_free(event_queue); furi_record_close(RECORD_NOTIFICATION); furi_record_close(RECORD_GUI); + free(calculator_state); return 0; } diff --git a/applications/plugins/cli_bridge/README.md b/applications/plugins/cli_bridge/README.md index 446f3b8a9..b6d35fa7f 100644 --- a/applications/plugins/cli_bridge/README.md +++ b/applications/plugins/cli_bridge/README.md @@ -13,12 +13,12 @@ git clone https://github.com/ranchordo/flipperzero-cli-bridge ./applications_use # If everything went well, the built .fap file can be found in ./dist/f7-D/apps/apps/Tools/cli_gui.fap ``` # Usage -On the flipperzero, you should be able to find a new application (CLI-GUI Bridge) under Applications->Tools. Opening it will result in a text prompt - the prompt for the command line. Enter a suitable command (quickly pressing the back button will input a space) such as `subghz chat [freq in hz, e.g. 310000000]`, etc, then navigate to and press the SAVE key. You should then see the command window. Use Up and Down to scroll, and use Left or Center to get back to the text input prompt. A quick tap of the back key while viewing the console output sends a Ctrl-C to the console. +On the flipperzero, you should be able to find a new application (CLI-GUI Bridge) under Applications->Tools. Opening it will result in a text prompt - the prompt for the command line. Enter a suitable command (quickly pressing the back button or holding `_` on the keyboard will input a space) such as `subghz chat [freq in hz, e.g. 310000000]`, etc, then navigate to and press the SAVE key. You should then see the command window. Use Up and Down to scroll, and use Left or Center to get back to the text input prompt. A quick tap of the back key while viewing the console output sends a Ctrl-C to the console, and a long press of the left or right keys during text input will navigate back to the console output without executing. ## Exiting the app Holding and then releasing the back key for at least a second or so (long press) will exit the app normally, meaning that the inner terminal will send Ctrl-C and close. Any sessions will be disconnected. -Holding and then releasing the OK key for at least a second or so (long press) will exit the app while keeping the terminal open. Terminal output will be cleared the next time you launch the app, but whatever command or session was running previously will be resumed. This is especially handy with subghz chat - exiting the app while keeping the terminal open will not disconnect you from the chat, and the flipper will still vibrate briefly whenever a new message comes in (even if the app is closed). +Holding and then releasing the OK key while focusing on the console output for at least a second or so (long press) will exit the app while keeping the terminal open. Terminal output will be cleared the next time you launch the app, but whatever command or session was running previously will be resumed. This is especially handy with subghz chat - exiting the app while keeping the terminal open will not disconnect you from the chat, and the flipper will still vibrate briefly whenever a new message comes in (even if the app is closed). NOTE: USB functionality (qFlipper, normal USB CLI) may not work after running the app (especially after exiting without closing the terminal), simply restart your flipper and all USB functionality will return to normal. diff --git a/applications/plugins/cli_bridge/cligui_main.c b/applications/plugins/cli_bridge/cligui_main.c index 137b4a08b..4e7987c89 100644 --- a/applications/plugins/cli_bridge/cligui_main.c +++ b/applications/plugins/cli_bridge/cligui_main.c @@ -50,8 +50,10 @@ static void input_callback_wrapper(InputEvent* event, void* context) { view_dispatcher_stop(app->view_dispatcher); } if(event->type == InputTypeLong && event->key == InputKeyOk) { - persistent_exit = true; - view_dispatcher_stop(app->view_dispatcher); + if(app->data->state == ViewConsoleOutput) { + persistent_exit = true; + view_dispatcher_stop(app->view_dispatcher); + } } if(app->data->state == ViewTextInput) { text_input_input_handler(app, event); diff --git a/applications/plugins/cli_bridge/text_input.c b/applications/plugins/cli_bridge/text_input.c index 295e7629d..568b8ebcd 100644 --- a/applications/plugins/cli_bridge/text_input.c +++ b/applications/plugins/cli_bridge/text_input.c @@ -19,8 +19,6 @@ void text_input_result_callback(void* ctx) { } void text_input_input_handler(CliguiApp* app, InputEvent* event) { - UNUSED(app); - UNUSED(event); if(event->type == InputTypeShort && event->key == InputKeyBack) { // view_dispatcher_switch_to_view(app->view_dispatcher, ViewConsoleOutput); // app->data->state = ViewConsoleOutput; @@ -28,4 +26,9 @@ void text_input_input_handler(CliguiApp* app, InputEvent* event) { app->text_input_store[len] = ' '; app->text_input_store[len + 1] = 0; } + if(event->type == InputTypeLong && + (event->key == InputKeyLeft || event->key == InputKeyRight)) { + view_dispatcher_switch_to_view(app->view_dispatcher, ViewConsoleOutput); + app->data->state = ViewConsoleOutput; + } } \ No newline at end of file diff --git a/applications/plugins/clock_app/application.fam b/applications/plugins/clock_app/application.fam deleted file mode 100644 index a6a2eff3e..000000000 --- a/applications/plugins/clock_app/application.fam +++ /dev/null @@ -1,10 +0,0 @@ -App( - appid="clock", - name="Clock", - apptype=FlipperAppType.EXTERNAL, - entry_point="clock_app", - requires=["gui"], - stack_size=2 * 1024, - fap_icon="clock.png", - fap_category="Tools", -) diff --git a/applications/plugins/clock_app/clock.png b/applications/plugins/clock_app/clock.png deleted file mode 100644 index 0d96df102..000000000 Binary files a/applications/plugins/clock_app/clock.png and /dev/null differ diff --git a/applications/plugins/clock_app/clock_app.c b/applications/plugins/clock_app/clock_app.c deleted file mode 100644 index d2c178903..000000000 --- a/applications/plugins/clock_app/clock_app.c +++ /dev/null @@ -1,241 +0,0 @@ -#include -#include - -#include -#include - -#include "clock_app.h" - -static void clock_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { - furi_assert(event_queue); - PluginEvent event = {.type = EventTypeKey, .input = *input_event}; - furi_message_queue_put(event_queue, &event, FuriWaitForever); -} - -static void clock_render_callback(Canvas* const canvas, void* ctx) { - //canvas_clear(canvas); - //canvas_set_color(canvas, ColorBlack); - - ClockState* state = ctx; - if(furi_mutex_acquire(state->mutex, 200) != FuriStatusOk) { - //FURI_LOG_D(TAG, "Can't obtain mutex, requeue render"); - PluginEvent event = {.type = EventTypeTick}; - furi_message_queue_put(state->event_queue, &event, 0); - return; - } - - FuriHalRtcDateTime curr_dt; - furi_hal_rtc_get_datetime(&curr_dt); - uint32_t curr_ts = furi_hal_rtc_datetime_to_timestamp(&curr_dt); - - char time_string[TIME_LEN]; - char date_string[DATE_LEN]; - char meridian_string[MERIDIAN_LEN]; - char timer_string[20]; - - if(state->time_format == LocaleTimeFormat24h) { - snprintf( - time_string, TIME_LEN, CLOCK_TIME_FORMAT, curr_dt.hour, curr_dt.minute, curr_dt.second); - } else { - bool pm = curr_dt.hour > 12; - bool pm12 = curr_dt.hour >= 12; - bool am12 = curr_dt.hour == 0; - snprintf( - time_string, - TIME_LEN, - CLOCK_TIME_FORMAT, - pm ? curr_dt.hour - 12 : (am12 ? 12 : curr_dt.hour), - curr_dt.minute, - curr_dt.second); - - snprintf( - meridian_string, - MERIDIAN_LEN, - MERIDIAN_FORMAT, - pm12 ? MERIDIAN_STRING_PM : MERIDIAN_STRING_AM); - } - - if(state->date_format == LocaleDateFormatYMD) { - snprintf( - date_string, DATE_LEN, CLOCK_ISO_DATE_FORMAT, curr_dt.year, curr_dt.month, curr_dt.day); - } else if(state->date_format == LocaleDateFormatMDY) { - snprintf( - date_string, DATE_LEN, CLOCK_RFC_DATE_FORMAT, curr_dt.month, curr_dt.day, curr_dt.year); - } else { - snprintf( - date_string, DATE_LEN, CLOCK_RFC_DATE_FORMAT, curr_dt.day, curr_dt.month, curr_dt.year); - } - - bool timer_running = state->timer_running; - uint32_t timer_start_timestamp = state->timer_start_timestamp; - uint32_t timer_stopped_seconds = state->timer_stopped_seconds; - - furi_mutex_release(state->mutex); - - canvas_set_font(canvas, FontBigNumbers); - - if(timer_start_timestamp != 0) { - int32_t elapsed_secs = timer_running ? (curr_ts - timer_start_timestamp) : - timer_stopped_seconds; - snprintf(timer_string, 20, "%.2ld:%.2ld", elapsed_secs / 60, elapsed_secs % 60); - canvas_draw_str_aligned(canvas, 64, 8, AlignCenter, AlignCenter, time_string); // DRAW TIME - canvas_draw_str_aligned(canvas, 64, 32, AlignCenter, AlignTop, timer_string); // DRAW TIMER - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned(canvas, 64, 20, AlignCenter, AlignTop, date_string); // DRAW DATE - elements_button_left(canvas, "Reset"); - } else { - canvas_draw_str_aligned(canvas, 64, 28, AlignCenter, AlignCenter, time_string); - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned(canvas, 64, 42, AlignCenter, AlignTop, date_string); - - if(state->time_format == LocaleTimeFormat12h) - canvas_draw_str_aligned(canvas, 65, 12, AlignCenter, AlignCenter, meridian_string); - } - if(timer_running) { - elements_button_center(canvas, "Stop"); - } else if(timer_start_timestamp != 0 && !timer_running) { - elements_button_center(canvas, "Start"); - } -} - -static void clock_state_init(ClockState* const state) { - state->time_format = locale_get_time_format(); - - state->date_format = locale_get_date_format(); - - //FURI_LOG_D(TAG, "Time format: %s", state->settings.time_format == H12 ? "12h" : "24h"); - //FURI_LOG_D(TAG, "Date format: %s", state->settings.date_format == Iso ? "ISO 8601" : "RFC 5322"); - //furi_hal_rtc_get_datetime(&state->datetime); -} - -// Runs every 1000ms by default -static void clock_tick(void* ctx) { - furi_assert(ctx); - FuriMessageQueue* event_queue = ctx; - PluginEvent event = {.type = EventTypeTick}; - // It's OK to loose this event if system overloaded - furi_message_queue_put(event_queue, &event, 0); -} - -int32_t clock_app(void* p) { - UNUSED(p); - ClockState* plugin_state = malloc(sizeof(ClockState)); - - plugin_state->event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent)); - if(plugin_state->event_queue == NULL) { - FURI_LOG_E(TAG, "Cannot create event queue"); - free(plugin_state); - return 255; - } - //FURI_LOG_D(TAG, "Event queue created"); - - plugin_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); - if(plugin_state->mutex == NULL) { - FURI_LOG_E(TAG, "Cannot create mutex"); - furi_message_queue_free(plugin_state->event_queue); - free(plugin_state); - return 255; - } - //FURI_LOG_D(TAG, "Mutex created"); - - clock_state_init(plugin_state); - - // Set system callbacks - ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, clock_render_callback, plugin_state); - view_port_input_callback_set(view_port, clock_input_callback, plugin_state->event_queue); - - FuriTimer* timer = - furi_timer_alloc(clock_tick, FuriTimerTypePeriodic, plugin_state->event_queue); - - if(timer == NULL) { - FURI_LOG_E(TAG, "Cannot create timer"); - furi_mutex_free(plugin_state->mutex); - furi_message_queue_free(plugin_state->event_queue); - free(plugin_state); - return 255; - } - //FURI_LOG_D(TAG, "Timer created"); - - // Open GUI and register view_port - Gui* gui = furi_record_open(RECORD_GUI); - gui_add_view_port(gui, view_port, GuiLayerFullscreen); - - furi_timer_start(timer, furi_kernel_get_tick_frequency()); - //FURI_LOG_D(TAG, "Timer started"); - - // Main loop - PluginEvent event; - for(bool processing = true; processing;) { - FuriStatus event_status = furi_message_queue_get(plugin_state->event_queue, &event, 100); - - if(event_status != FuriStatusOk) continue; - - if(furi_mutex_acquire(plugin_state->mutex, FuriWaitForever) != FuriStatusOk) continue; - // press events - if(event.type == EventTypeKey) { - if(event.input.type == InputTypeShort || event.input.type == InputTypeRepeat) { - switch(event.input.key) { - case InputKeyUp: - case InputKeyDown: - case InputKeyRight: - break; - case InputKeyLeft: - if(plugin_state->timer_start_timestamp != 0) { - // Reset seconds - plugin_state->timer_running = false; - plugin_state->timer_start_timestamp = 0; - plugin_state->timer_stopped_seconds = 0; - } - break; - case InputKeyOk:; - // START/STOP TIMER - - FuriHalRtcDateTime curr_dt; - furi_hal_rtc_get_datetime(&curr_dt); - uint32_t curr_ts = furi_hal_rtc_datetime_to_timestamp(&curr_dt); - - if(plugin_state->timer_running) { - // Update stopped seconds - plugin_state->timer_stopped_seconds = - curr_ts - plugin_state->timer_start_timestamp; - } else { - if(plugin_state->timer_start_timestamp == 0) { - // Set starting timestamp if this is first time - plugin_state->timer_start_timestamp = curr_ts; - } else { - // Timer was already running, need to slightly readjust so we don't - // count the intervening time - plugin_state->timer_start_timestamp = - curr_ts - plugin_state->timer_stopped_seconds; - } - } - plugin_state->timer_running = !plugin_state->timer_running; - break; - case InputKeyBack: - // Exit the plugin - processing = false; - break; - default: - break; - } - } - } /*else if(event.type == EventTypeTick) { - furi_hal_rtc_get_datetime(&plugin_state->datetime); - }*/ - - view_port_update(view_port); - furi_mutex_release(plugin_state->mutex); - } - - furi_timer_free(timer); - view_port_enabled_set(view_port, false); - gui_remove_view_port(gui, view_port); - furi_record_close(RECORD_GUI); - view_port_free(view_port); - furi_message_queue_free(plugin_state->event_queue); - furi_mutex_free(plugin_state->mutex); - free(plugin_state); - - return 0; -} diff --git a/applications/plugins/clock_app/clock_app.h b/applications/plugins/clock_app/clock_app.h deleted file mode 100644 index 693bdfac0..000000000 --- a/applications/plugins/clock_app/clock_app.h +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -#include -#include - -#define TAG "Clock" - -#define CLOCK_ISO_DATE_FORMAT "%.4d-%.2d-%.2d" -#define CLOCK_RFC_DATE_FORMAT "%.2d-%.2d-%.4d" -#define CLOCK_TIME_FORMAT "%.2d:%.2d:%.2d" - -#define MERIDIAN_FORMAT "%s" -#define MERIDIAN_STRING_AM "AM" -#define MERIDIAN_STRING_PM "PM" - -#define TIME_LEN 12 -#define DATE_LEN 14 -#define MERIDIAN_LEN 3 - -typedef enum { - EventTypeTick, - EventTypeKey, -} EventType; - -typedef struct { - EventType type; - InputEvent input; -} PluginEvent; - -typedef struct { - LocaleDateFormat date_format; - LocaleTimeFormat time_format; - FuriHalRtcDateTime datetime; - FuriMutex* mutex; - FuriMessageQueue* event_queue; - uint32_t timer_start_timestamp; - uint32_t timer_stopped_seconds; - bool timer_running; -} ClockState; diff --git a/applications/plugins/cntdown_timer/application.fam b/applications/plugins/cntdown_timer/application.fam index f40ccaa51..ba22fd4bd 100644 --- a/applications/plugins/cntdown_timer/application.fam +++ b/applications/plugins/cntdown_timer/application.fam @@ -1,5 +1,7 @@ +# qv. https://github.com/flipperdevices/flipperzero-firmware/blob/dev/documentation/AppManifests.md + App( - appid="Count_Down_Timer", + appid="cntdown_tim", name="Count Down Timer", apptype=FlipperAppType.EXTERNAL, entry_point="app_main", @@ -10,5 +12,5 @@ App( stack_size=2 * 1024, order=20, fap_icon="cntdown_timer.png", - fap_category="Tools", + fap_category="Misc", ) diff --git a/applications/plugins/counter/application.fam b/applications/plugins/counter/application.fam index 5d164f20c..8f0147b09 100644 --- a/applications/plugins/counter/application.fam +++ b/applications/plugins/counter/application.fam @@ -1,7 +1,7 @@ App( - appid="Counter", + appid="counter", name="Counter", - apptype=FlipperAppType.EXTERNAL, + apptype=FlipperAppType.PLUGIN, entry_point="counterapp", requires=[ "gui", diff --git a/applications/plugins/counter/counter.c b/applications/plugins/counter/counter.c index 886ad3398..22fa8cd80 100644 --- a/applications/plugins/counter/counter.c +++ b/applications/plugins/counter/counter.c @@ -2,7 +2,7 @@ #include #include #include -#include +#include #define MAX_COUNT 99 #define BOXTIME 2 diff --git a/applications/plugins/counter/media/preview.gif b/applications/plugins/counter/media/preview.gif deleted file mode 100644 index 87098b733..000000000 Binary files a/applications/plugins/counter/media/preview.gif and /dev/null differ diff --git a/applications/plugins/dap_link/application.fam b/applications/plugins/dap_link/application.fam index 5489c3b19..4dd2e531e 100644 --- a/applications/plugins/dap_link/application.fam +++ b/applications/plugins/dap_link/application.fam @@ -1,7 +1,7 @@ App( - appid="DAP_Link", - name="DAP Link", - apptype=FlipperAppType.EXTERNAL, + appid="dap_link", + name="[GPIO] DAP Link", + apptype=FlipperAppType.PLUGIN, entry_point="dap_link_app", requires=[ "gui", @@ -10,7 +10,7 @@ App( stack_size=4 * 1024, order=20, fap_icon="dap_link.png", - fap_category="Tools", + fap_category="GPIO", fap_private_libs=[ Lib( name="free-dap", diff --git a/applications/plugins/dap_link/dap_link.c b/applications/plugins/dap_link/dap_link.c index dd684810a..eafb435e7 100644 --- a/applications/plugins/dap_link/dap_link.c +++ b/applications/plugins/dap_link/dap_link.c @@ -14,7 +14,7 @@ #include "gui/dap_gui.h" #include "usb/dap_v2_usb.h" #include -#include "DAP_Link_icons.h" +#include "dap_link_icons.h" /***************************************************************************/ /****************************** DAP COMMON *********************************/ @@ -524,4 +524,4 @@ int32_t dap_link_app(void* p) { dap_app_free(app); return 0; -} +} \ No newline at end of file diff --git a/applications/plugins/dap_link/gui/views/dap_main_view.c b/applications/plugins/dap_link/gui/views/dap_main_view.c index 58bfcf316..c5c8f9dff 100644 --- a/applications/plugins/dap_link/gui/views/dap_main_view.c +++ b/applications/plugins/dap_link/gui/views/dap_main_view.c @@ -1,5 +1,5 @@ #include "dap_main_view.h" -#include "DAP_Link_icons.h" +#include "dap_link_icons.h" #include // extern const Icon I_ArrowDownEmpty_12x18; diff --git a/applications/plugins/doom/doom.c b/applications/plugins/doom/doom.c index 78a06055c..b6d08536d 100644 --- a/applications/plugins/doom/doom.c +++ b/applications/plugins/doom/doom.c @@ -37,6 +37,7 @@ typedef struct { } PluginEvent; typedef struct { + FuriMutex* mutex; Player player; Entity entity[MAX_ENTITIES]; StaticEntity static_entity[MAX_STATIC_ENTITIES]; @@ -770,10 +771,9 @@ void loopIntro(Canvas* const canvas) { } static void render_callback(Canvas* const canvas, void* ctx) { - PluginState* plugin_state = acquire_mutex((ValueMutex*)ctx, 25); - if(plugin_state == NULL) { - return; - } + furi_assert(ctx); + PluginState* plugin_state = ctx; + furi_mutex_acquire(plugin_state->mutex, FuriWaitForever); if(plugin_state->init) setupDisplay(canvas); canvas_set_font(canvas, FontPrimary); @@ -797,7 +797,7 @@ static void render_callback(Canvas* const canvas, void* ctx) { break; } } - release_mutex((ValueMutex*)ctx, plugin_state); + furi_mutex_release(plugin_state->mutex); } static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { @@ -967,8 +967,8 @@ int32_t doom_app() { FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent)); PluginState* plugin_state = malloc(sizeof(PluginState)); doom_state_init(plugin_state); - ValueMutex state_mutex; - if(!init_mutex(&state_mutex, plugin_state, sizeof(PluginState))) { + plugin_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!plugin_state->mutex) { FURI_LOG_E("Doom_game", "cannot create mutex\r\n"); furi_record_close(RECORD_NOTIFICATION); furi_message_queue_free(event_queue); @@ -980,11 +980,11 @@ int32_t doom_app() { furi_timer_start(timer, furi_kernel_get_tick_frequency() / 12); // Set system callbacks ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, render_callback, &state_mutex); + view_port_draw_callback_set(view_port, render_callback, plugin_state); view_port_input_callback_set(view_port, input_callback, event_queue); // Open GUI and register view_port - Gui* gui = furi_record_open("gui"); + Gui* gui = furi_record_open(RECORD_GUI); gui_add_view_port(gui, view_port, GuiLayerFullscreen); ////////////////////////////////// @@ -997,7 +997,7 @@ int32_t doom_app() { #endif for(bool processing = true; processing;) { FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); - PluginState* plugin_state = (PluginState*)acquire_mutex_block(&state_mutex); + furi_mutex_acquire(plugin_state->mutex, FuriWaitForever); #ifdef SOUND furi_check( furi_mutex_acquire(plugin_state->music_instance->model_mutex, FuriWaitForever) == @@ -1087,7 +1087,7 @@ int32_t doom_app() { furi_mutex_release(plugin_state->music_instance->model_mutex); #endif view_port_update(view_port); - release_mutex(&state_mutex, plugin_state); + furi_mutex_release(plugin_state->mutex); } #ifdef SOUND music_player_worker_free(plugin_state->music_instance->worker); @@ -1099,8 +1099,9 @@ int32_t doom_app() { furi_timer_free(timer); view_port_enabled_set(view_port, false); gui_remove_view_port(gui, view_port); - furi_record_close("gui"); + furi_record_close(RECORD_GUI); view_port_free(view_port); + furi_mutex_free(plugin_state->mutex); furi_message_queue_free(event_queue); free(plugin_state); return 0; diff --git a/applications/plugins/dtmf_dolphin/README.md b/applications/plugins/dtmf_dolphin/README.md index 5c9561f4b..9a65a382a 100644 --- a/applications/plugins/dtmf_dolphin/README.md +++ b/applications/plugins/dtmf_dolphin/README.md @@ -1,4 +1,6 @@ -![Image](assets/dialer.jpg) +![Image](pics/dialer.jpg) + +[Original Link](https://github.com/litui/dtmf_dolphin) ## DTMF Dolphin diff --git a/applications/plugins/dtmf_dolphin/application.fam b/applications/plugins/dtmf_dolphin/application.fam index 98fbe7363..0727f5f52 100644 --- a/applications/plugins/dtmf_dolphin/application.fam +++ b/applications/plugins/dtmf_dolphin/application.fam @@ -1,5 +1,5 @@ App( - appid="DTMF_Dolphin", + appid="dtmf_dolphin", name="DTMF Dolphin", apptype=FlipperAppType.EXTERNAL, entry_point="dtmf_dolphin_app", diff --git a/applications/plugins/dtmf_dolphin/assets/dialer.jpg b/applications/plugins/dtmf_dolphin/pics/dialer.jpg similarity index 100% rename from applications/plugins/dtmf_dolphin/assets/dialer.jpg rename to applications/plugins/dtmf_dolphin/pics/dialer.jpg diff --git a/applications/plugins/wifi_deauther_v1/FlipperZeroWiFiDeauthModuleDefines.h b/applications/plugins/esp8266_deauth/FlipperZeroWiFiDeauthModuleDefines.h similarity index 100% rename from applications/plugins/wifi_deauther_v1/FlipperZeroWiFiDeauthModuleDefines.h rename to applications/plugins/esp8266_deauth/FlipperZeroWiFiDeauthModuleDefines.h diff --git a/applications/plugins/wifi_deauther_v1/application.fam b/applications/plugins/esp8266_deauth/application.fam similarity index 91% rename from applications/plugins/wifi_deauther_v1/application.fam rename to applications/plugins/esp8266_deauth/application.fam index 6ce0f4435..b289bc88e 100644 --- a/applications/plugins/wifi_deauther_v1/application.fam +++ b/applications/plugins/esp8266_deauth/application.fam @@ -8,5 +8,5 @@ App( stack_size=2 * 1024, order=20, fap_icon="wifi_10px.png", - fap_category="GPIO", + fap_category="WiFi", ) diff --git a/applications/plugins/wifi_deauther_v1/esp8266_deauth.c b/applications/plugins/esp8266_deauth/esp8266_deauth.c similarity index 95% rename from applications/plugins/wifi_deauther_v1/esp8266_deauth.c rename to applications/plugins/esp8266_deauth/esp8266_deauth.c index 3cc61a588..d32ca4c18 100644 --- a/applications/plugins/wifi_deauther_v1/esp8266_deauth.c +++ b/applications/plugins/esp8266_deauth/esp8266_deauth.c @@ -62,6 +62,7 @@ typedef struct SGpioButtons { } SGpioButtons; typedef struct SWiFiDeauthApp { + FuriMutex* mutex; Gui* m_gui; FuriThread* m_worker_thread; //NotificationApp* m_notification; @@ -121,10 +122,9 @@ static void esp8266_deauth_app_init(SWiFiDeauthApp* const app) { } static void esp8266_deauth_module_render_callback(Canvas* const canvas, void* ctx) { - SWiFiDeauthApp* app = acquire_mutex((ValueMutex*)ctx, 25); - if(app == NULL) { - return; - } + furi_assert(ctx); + SWiFiDeauthApp* app = ctx; + furi_mutex_acquire(app->mutex, FuriWaitForever); //if(app->m_needUpdateGUI) //{ @@ -206,7 +206,7 @@ static void esp8266_deauth_module_render_callback(Canvas* const canvas, void* ct break; } - release_mutex((ValueMutex*)ctx, app); + furi_mutex_release(app->mutex); } static void @@ -235,14 +235,15 @@ static int32_t uart_worker(void* context) { furi_assert(context); DEAUTH_APP_LOG_I("[UART] Worker thread init"); - SWiFiDeauthApp* app = acquire_mutex((ValueMutex*)context, 25); + SWiFiDeauthApp* app = context; + furi_mutex_acquire(app->mutex, FuriWaitForever); if(app == NULL) { return 1; } FuriStreamBuffer* rx_stream = app->m_rx_stream; - release_mutex((ValueMutex*)context, app); + furi_mutex_release(app->mutex); #if ENABLE_MODULE_POWER bool initialized = false; @@ -259,7 +260,8 @@ static int32_t uart_worker(void* context) { if(events & WorkerEventStop) break; if(events & WorkerEventRx) { DEAUTH_APP_LOG_I("[UART] Received data"); - SWiFiDeauthApp* app = acquire_mutex((ValueMutex*)context, 25); + SWiFiDeauthApp* app = context; + furi_mutex_acquire(app->mutex, FuriWaitForever); if(app == NULL) { return 1; } @@ -307,7 +309,7 @@ static int32_t uart_worker(void* context) { } #endif // ENABLE_MODULE_POWER - release_mutex((ValueMutex*)context, app); + furi_mutex_release(app->mutex); } } @@ -356,8 +358,8 @@ int32_t esp8266_deauth_app(void* p) { #endif #endif // ENABLE_MODULE_DETECTION - ValueMutex app_data_mutex; - if(!init_mutex(&app_data_mutex, app, sizeof(SWiFiDeauthApp))) { + app->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!app->mutex) { DEAUTH_APP_LOG_E("cannot create mutex\r\n"); free(app); return 255; @@ -365,10 +367,10 @@ int32_t esp8266_deauth_app(void* p) { DEAUTH_APP_LOG_I("Mutex created"); - //app->m_notification = furi_record_open("notification"); + //app->m_notification = furi_record_open(RECORD_NOTIFICATION); ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, esp8266_deauth_module_render_callback, &app_data_mutex); + view_port_draw_callback_set(view_port, esp8266_deauth_module_render_callback, app); view_port_input_callback_set(view_port, esp8266_deauth_module_input_callback, event_queue); // Open GUI and register view_port @@ -382,7 +384,7 @@ int32_t esp8266_deauth_app(void* p) { app->m_worker_thread = furi_thread_alloc(); furi_thread_set_name(app->m_worker_thread, "WiFiDeauthModuleUARTWorker"); furi_thread_set_stack_size(app->m_worker_thread, 1 * 1024); - furi_thread_set_context(app->m_worker_thread, &app_data_mutex); + furi_thread_set_context(app->m_worker_thread, app); furi_thread_set_callback(app->m_worker_thread, uart_worker); furi_thread_start(app->m_worker_thread); DEAUTH_APP_LOG_I("UART thread allocated"); @@ -398,7 +400,7 @@ int32_t esp8266_deauth_app(void* p) { SPluginEvent event; for(bool processing = true; processing;) { FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); - SWiFiDeauthApp* app = (SWiFiDeauthApp*)acquire_mutex_block(&app_data_mutex); + furi_mutex_acquire(app->mutex, FuriWaitForever); #if ENABLE_MODULE_DETECTION if(!app->m_wifiDeauthModuleAttached) { @@ -484,7 +486,7 @@ int32_t esp8266_deauth_app(void* p) { #endif view_port_update(view_port); - release_mutex(&app_data_mutex, app); + furi_mutex_release(app->mutex); } DEAUTH_APP_LOG_I("Start exit app"); @@ -514,7 +516,7 @@ int32_t esp8266_deauth_app(void* p) { // Close gui record furi_record_close(RECORD_GUI); - furi_record_close("notification"); + //furi_record_close(RECORD_NOTIFICATION); app->m_gui = NULL; view_port_free(view_port); @@ -523,7 +525,7 @@ int32_t esp8266_deauth_app(void* p) { furi_stream_buffer_free(app->m_rx_stream); - delete_mutex(&app_data_mutex); + furi_mutex_free(app->mutex); // Free rest free(app); diff --git a/applications/plugins/wifi_deauther_v1/wifi_10px.png b/applications/plugins/esp8266_deauth/wifi_10px.png similarity index 100% rename from applications/plugins/wifi_deauther_v1/wifi_10px.png rename to applications/plugins/esp8266_deauth/wifi_10px.png diff --git a/applications/plugins/flappy_bird/flappy_bird.c b/applications/plugins/flappy_bird/flappy_bird.c index a86ddb2bb..0c5cd462e 100644 --- a/applications/plugins/flappy_bird/flappy_bird.c +++ b/applications/plugins/flappy_bird/flappy_bird.c @@ -1,5 +1,4 @@ #include -#include #include #include @@ -60,6 +59,7 @@ typedef struct { PILAR pilars[FLAPPY_PILAR_MAX]; bool debug; State state; + FuriMutex* mutex; } GameState; typedef struct { @@ -175,10 +175,9 @@ static void flappy_game_flap(GameState* const game_state) { } static void flappy_game_render_callback(Canvas* const canvas, void* ctx) { - const GameState* game_state = acquire_mutex((ValueMutex*)ctx, 25); - if(game_state == NULL) { - return; - } + furi_assert(ctx); + const GameState* game_state = ctx; + furi_mutex_acquire(game_state->mutex, FuriWaitForever); canvas_draw_frame(canvas, 0, 0, 128, 64); @@ -256,17 +255,13 @@ static void flappy_game_render_callback(Canvas* const canvas, void* ctx) { canvas_set_font(canvas, FontPrimary); canvas_draw_str(canvas, 37, 31, "Game Over"); - if(game_state->points != 0 && game_state->points % 5 == 0) { - DOLPHIN_DEED(getRandomDeed()); - } - canvas_set_font(canvas, FontSecondary); char buffer[12]; snprintf(buffer, sizeof(buffer), "Score: %u", game_state->points); canvas_draw_str_aligned(canvas, 64, 41, AlignCenter, AlignBottom, buffer); } - release_mutex((ValueMutex*)ctx, game_state); + furi_mutex_release(game_state->mutex); } static void flappy_game_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { @@ -292,8 +287,8 @@ int32_t flappy_game_app(void* p) { GameState* game_state = malloc(sizeof(GameState)); flappy_game_state_init(game_state); - ValueMutex state_mutex; - if(!init_mutex(&state_mutex, game_state, sizeof(GameState))) { + game_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!game_state->mutex) { FURI_LOG_E(TAG, "cannot create mutex\r\n"); return_code = 255; goto free_and_exit; @@ -301,7 +296,7 @@ int32_t flappy_game_app(void* p) { // Set system callbacks ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, flappy_game_render_callback, &state_mutex); + view_port_draw_callback_set(view_port, flappy_game_render_callback, game_state); view_port_input_callback_set(view_port, flappy_game_input_callback, event_queue); FuriTimer* timer = @@ -315,7 +310,7 @@ int32_t flappy_game_app(void* p) { GameEvent event; for(bool processing = true; processing;) { FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); - GameState* game_state = (GameState*)acquire_mutex_block(&state_mutex); + furi_mutex_acquire(game_state->mutex, FuriWaitForever); if(event_status == FuriStatusOk) { // press events @@ -357,7 +352,7 @@ int32_t flappy_game_app(void* p) { } view_port_update(view_port); - release_mutex(&state_mutex, game_state); + furi_mutex_release(game_state->mutex); } furi_timer_free(timer); @@ -365,7 +360,7 @@ int32_t flappy_game_app(void* p) { gui_remove_view_port(gui, view_port); furi_record_close(RECORD_GUI); view_port_free(view_port); - delete_mutex(&state_mutex); + furi_mutex_free(game_state->mutex); free_and_exit: flappy_game_state_free(game_state); diff --git a/applications/plugins/flashlight/application.fam b/applications/plugins/flashlight/application.fam index 6d70a036f..ea8eb03d1 100644 --- a/applications/plugins/flashlight/application.fam +++ b/applications/plugins/flashlight/application.fam @@ -1,5 +1,5 @@ App( - appid="Flashlight", + appid="flashlight", name="[GPIO] Flashlight", apptype=FlipperAppType.EXTERNAL, entry_point="flashlight_app", diff --git a/applications/plugins/flashlight/flashlight.c b/applications/plugins/flashlight/flashlight.c index 534d48fdb..251c596ef 100644 --- a/applications/plugins/flashlight/flashlight.c +++ b/applications/plugins/flashlight/flashlight.c @@ -18,14 +18,14 @@ typedef struct { } PluginEvent; typedef struct { + FuriMutex* mutex; bool is_on; } PluginState; static void render_callback(Canvas* const canvas, void* ctx) { - const PluginState* plugin_state = acquire_mutex((ValueMutex*)ctx, 25); - if(plugin_state == NULL) { - return; - } + furi_assert(ctx); + const PluginState* plugin_state = ctx; + furi_mutex_acquire(plugin_state->mutex, FuriWaitForever); canvas_set_font(canvas, FontPrimary); elements_multiline_text_aligned(canvas, 64, 2, AlignCenter, AlignTop, "Flashlight"); @@ -41,7 +41,7 @@ static void render_callback(Canvas* const canvas, void* ctx) { canvas, 64, 40, AlignCenter, AlignTop, "Press OK button to off"); } - release_mutex((ValueMutex*)ctx, plugin_state); + furi_mutex_release(plugin_state->mutex); } static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { @@ -69,8 +69,8 @@ int32_t flashlight_app() { PluginState* plugin_state = malloc(sizeof(PluginState)); - ValueMutex state_mutex; - if(!init_mutex(&state_mutex, plugin_state, sizeof(PluginState))) { + plugin_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!plugin_state->mutex) { FURI_LOG_E("flashlight", "cannot create mutex\r\n"); furi_message_queue_free(event_queue); free(plugin_state); @@ -79,7 +79,7 @@ int32_t flashlight_app() { // Set system callbacks ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, render_callback, &state_mutex); + view_port_draw_callback_set(view_port, render_callback, plugin_state); view_port_input_callback_set(view_port, input_callback, event_queue); // Open GUI and register view_port @@ -90,7 +90,7 @@ int32_t flashlight_app() { for(bool processing = true; processing;) { FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); - PluginState* plugin_state = (PluginState*)acquire_mutex_block(&state_mutex); + furi_mutex_acquire(plugin_state->mutex, FuriWaitForever); if(event_status == FuriStatusOk) { // press events @@ -116,7 +116,7 @@ int32_t flashlight_app() { } view_port_update(view_port); - release_mutex(&state_mutex, plugin_state); + furi_mutex_release(plugin_state->mutex); } view_port_enabled_set(view_port, false); @@ -124,7 +124,7 @@ int32_t flashlight_app() { furi_record_close(RECORD_GUI); view_port_free(view_port); furi_message_queue_free(event_queue); - delete_mutex(&state_mutex); + furi_mutex_free(plugin_state->mutex); return 0; } diff --git a/applications/plugins/flipfrid/flipfrid.c b/applications/plugins/flipfrid/flipfrid.c index c2831f2f6..4f28be7b1 100644 --- a/applications/plugins/flipfrid/flipfrid.c +++ b/applications/plugins/flipfrid/flipfrid.c @@ -9,11 +9,9 @@ #define RFIDFUZZER_APP_FOLDER "/ext/lrfid/rfidfuzzer" static void flipfrid_draw_callback(Canvas* const canvas, void* ctx) { - FlipFridState* flipfrid_state = (FlipFridState*)acquire_mutex((ValueMutex*)ctx, 100); - - if(flipfrid_state == NULL) { - return; - } + furi_assert(ctx); + FlipFridState* flipfrid_state = ctx; + furi_mutex_acquire(flipfrid_state->mutex, FuriWaitForever); // Draw correct Canvas switch(flipfrid_state->current_scene) { @@ -37,7 +35,7 @@ static void flipfrid_draw_callback(Canvas* const canvas, void* ctx) { break; } - release_mutex((ValueMutex*)ctx, flipfrid_state); + furi_mutex_release(flipfrid_state->mutex); } void flipfrid_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { @@ -118,11 +116,9 @@ int32_t flipfrid_start(void* p) { FURI_LOG_I(TAG, "Initializing input"); FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(FlipFridEvent)); FlipFridState* flipfrid_state = flipfrid_alloc(); - ValueMutex flipfrid_state_mutex; - // Mutex - FURI_LOG_I(TAG, "Initializing flipfrid mutex"); - if(!init_mutex(&flipfrid_state_mutex, flipfrid_state, sizeof(FlipFridState))) { + flipfrid_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!flipfrid_state->mutex) { FURI_LOG_E(TAG, "cannot create mutex\r\n"); furi_message_queue_free(event_queue); furi_record_close(RECORD_NOTIFICATION); @@ -139,7 +135,7 @@ int32_t flipfrid_start(void* p) { // Configure view port FURI_LOG_I(TAG, "Initializing viewport"); ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, flipfrid_draw_callback, &flipfrid_state_mutex); + view_port_draw_callback_set(view_port, flipfrid_draw_callback, flipfrid_state); view_port_input_callback_set(view_port, flipfrid_input_callback, event_queue); // Configure timer @@ -268,6 +264,7 @@ int32_t flipfrid_start(void* p) { furi_message_queue_free(event_queue); furi_record_close(RECORD_GUI); furi_record_close(RECORD_NOTIFICATION); + furi_mutex_free(flipfrid_state->mutex); flipfrid_free(flipfrid_state); return 0; diff --git a/applications/plugins/flipfrid/flipfrid.h b/applications/plugins/flipfrid/flipfrid.h index 6b8662e65..e4122054b 100644 --- a/applications/plugins/flipfrid/flipfrid.h +++ b/applications/plugins/flipfrid/flipfrid.h @@ -58,6 +58,7 @@ typedef struct { // STRUCTS typedef struct { + FuriMutex* mutex; bool is_running; bool is_attacking; FlipFridScene current_scene; diff --git a/applications/plugins/flipfrid/scene/flipfrid_scene_load_custom_uids.c b/applications/plugins/flipfrid/scene/flipfrid_scene_load_custom_uids.c index e2e8b76a2..32157556b 100644 --- a/applications/plugins/flipfrid/scene/flipfrid_scene_load_custom_uids.c +++ b/applications/plugins/flipfrid/scene/flipfrid_scene_load_custom_uids.c @@ -1,7 +1,6 @@ #include "flipfrid_scene_load_custom_uids.h" #include "flipfrid_scene_run_attack.h" #include "flipfrid_scene_entrypoint.h" -#include "RFID_Fuzzer_icons.h" #define LFRFID_UIDS_EXTENSION ".txt" #define RFIDFUZZER_APP_PATH_FOLDER "/ext/rfidfuzzer" diff --git a/applications/plugins/flipfrid/scene/flipfrid_scene_load_file.c b/applications/plugins/flipfrid/scene/flipfrid_scene_load_file.c index ce6a40b11..5f3f1a31b 100644 --- a/applications/plugins/flipfrid/scene/flipfrid_scene_load_file.c +++ b/applications/plugins/flipfrid/scene/flipfrid_scene_load_file.c @@ -1,6 +1,5 @@ #include "flipfrid_scene_load_file.h" #include "flipfrid_scene_entrypoint.h" -#include "RFID_Fuzzer_icons.h" #define LFRFID_APP_EXTENSION ".rfid" #define LFRFID_APP_PATH_FOLDER "/ext/lfrfid" diff --git a/applications/plugins/flipper_i2ctools/.gitignore b/applications/plugins/flipper_i2ctools/.gitignore deleted file mode 100644 index e69de29bb..000000000 diff --git a/applications/plugins/flipper_i2ctools/README.md b/applications/plugins/flipper_i2ctools/README.md index 8ecf47c8c..5ee1f881e 100644 --- a/applications/plugins/flipper_i2ctools/README.md +++ b/applications/plugins/flipper_i2ctools/README.md @@ -1,5 +1,6 @@ # flipperzero-i2ctools +[Original link](https://github.com/NaejEL/flipperzero-i2ctools) Set of i2c tools for Flipper Zero ![Preview](i2ctools.gif) diff --git a/applications/plugins/flipper_i2ctools/application.fam b/applications/plugins/flipper_i2ctools/application.fam index f6522a86e..44138691d 100644 --- a/applications/plugins/flipper_i2ctools/application.fam +++ b/applications/plugins/flipper_i2ctools/application.fam @@ -1,5 +1,5 @@ App( - appid="I2C_Tools", + appid="i2cTools", name="[GPIO] i2c Tools", apptype=FlipperAppType.EXTERNAL, entry_point="i2ctools_app", diff --git a/applications/plugins/flipper_i2ctools/i2ctools.c b/applications/plugins/flipper_i2ctools/i2ctools.c index 9d73a73b8..6d4cc739f 100644 --- a/applications/plugins/flipper_i2ctools/i2ctools.c +++ b/applications/plugins/flipper_i2ctools/i2ctools.c @@ -1,7 +1,9 @@ #include "i2ctools_i.h" void i2ctools_draw_callback(Canvas* canvas, void* ctx) { - i2cTools* i2ctools = acquire_mutex((ValueMutex*)ctx, 25); + furi_assert(ctx); + i2cTools* i2ctools = ctx; + furi_mutex_acquire(i2ctools->mutex, FuriWaitForever); switch(i2ctools->main_view->current_view) { case MAIN_VIEW: @@ -23,7 +25,7 @@ void i2ctools_draw_callback(Canvas* canvas, void* ctx) { default: break; } - release_mutex((ValueMutex*)ctx, i2ctools); + furi_mutex_release(i2ctools->mutex); } void i2ctools_input_callback(InputEvent* input_event, void* ctx) { @@ -38,8 +40,8 @@ int32_t i2ctools_app(void* p) { // Alloc i2ctools i2cTools* i2ctools = malloc(sizeof(i2cTools)); - ValueMutex i2ctools_mutex; - if(!init_mutex(&i2ctools_mutex, i2ctools, sizeof(i2cTools))) { + i2ctools->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!i2ctools->mutex) { FURI_LOG_E(APP_NAME, "cannot create mutex\r\n"); free(i2ctools); return -1; @@ -47,7 +49,7 @@ int32_t i2ctools_app(void* p) { // Alloc viewport i2ctools->view_port = view_port_alloc(); - view_port_draw_callback_set(i2ctools->view_port, i2ctools_draw_callback, &i2ctools_mutex); + view_port_draw_callback_set(i2ctools->view_port, i2ctools_draw_callback, i2ctools); view_port_input_callback_set(i2ctools->view_port, i2ctools_input_callback, event_queue); // Register view port in GUI @@ -216,6 +218,7 @@ int32_t i2ctools_app(void* p) { i2c_scanner_free(i2ctools->scanner); i2c_sender_free(i2ctools->sender); i2c_main_view_free(i2ctools->main_view); + furi_mutex_free(i2ctools->mutex); free(i2ctools); furi_record_close(RECORD_GUI); return 0; diff --git a/applications/plugins/flipper_i2ctools/i2ctools_i.h b/applications/plugins/flipper_i2ctools/i2ctools_i.h index 33917dc34..f31e42478 100644 --- a/applications/plugins/flipper_i2ctools/i2ctools_i.h +++ b/applications/plugins/flipper_i2ctools/i2ctools_i.h @@ -13,6 +13,7 @@ // App datas typedef struct { + FuriMutex* mutex; ViewPort* view_port; i2cMainView* main_view; diff --git a/applications/plugins/flipper_i2ctools/views/main_view.h b/applications/plugins/flipper_i2ctools/views/main_view.h index dc9379190..050e41130 100644 --- a/applications/plugins/flipper_i2ctools/views/main_view.h +++ b/applications/plugins/flipper_i2ctools/views/main_view.h @@ -1,7 +1,7 @@ #include #include #include -#include +#include #define APP_NAME "I2C Tools" #define SCAN_MENU_TEXT "Scan" diff --git a/applications/plugins/flipper_i2ctools/views/scanner_view.h b/applications/plugins/flipper_i2ctools/views/scanner_view.h index bd591a740..02bc8fb1c 100644 --- a/applications/plugins/flipper_i2ctools/views/scanner_view.h +++ b/applications/plugins/flipper_i2ctools/views/scanner_view.h @@ -1,7 +1,7 @@ #include #include #include -#include +#include #include "../i2cscanner.h" #define SCAN_TEXT "SCAN" diff --git a/applications/plugins/flipper_i2ctools/views/sender_view.h b/applications/plugins/flipper_i2ctools/views/sender_view.h index 8a21912dc..5f48081dd 100644 --- a/applications/plugins/flipper_i2ctools/views/sender_view.h +++ b/applications/plugins/flipper_i2ctools/views/sender_view.h @@ -1,7 +1,7 @@ #include #include #include -#include +#include #include "../i2csender.h" #define SEND_TEXT "SEND" diff --git a/applications/plugins/flipper_i2ctools/views/sniffer_view.h b/applications/plugins/flipper_i2ctools/views/sniffer_view.h index d5cc373d1..80c92f7fc 100644 --- a/applications/plugins/flipper_i2ctools/views/sniffer_view.h +++ b/applications/plugins/flipper_i2ctools/views/sniffer_view.h @@ -1,7 +1,7 @@ #include #include #include -#include +#include #include "../i2csniffer.h" #define SNIFF_TEXT "SNIFF" diff --git a/applications/plugins/game15/application.fam b/applications/plugins/game15/application.fam index dc3a0da0b..ab00316c1 100644 --- a/applications/plugins/game15/application.fam +++ b/applications/plugins/game15/application.fam @@ -1,5 +1,5 @@ App( - appid="Game15", + appid="game15", name="Game 15", apptype=FlipperAppType.EXTERNAL, entry_point="game15_app", diff --git a/applications/plugins/game15/game15.c b/applications/plugins/game15/game15.c index d9b059466..8faea4380 100644 --- a/applications/plugins/game15/game15.c +++ b/applications/plugins/game15/game15.c @@ -461,6 +461,7 @@ int32_t game15_app() { sandbox_init( FPS, (SandboxRenderCallback)render_callback, (SandboxEventHandler)game_event_handler); + sandbox_loop(); sandbox_free(); game_free(); diff --git a/applications/plugins/game_2048/application.fam b/applications/plugins/game_2048/application.fam index f2456668e..0440d0864 100644 --- a/applications/plugins/game_2048/application.fam +++ b/applications/plugins/game_2048/application.fam @@ -1,6 +1,6 @@ # PLUGIN BY eugene-kirzhanov App( - appid="2048_improved", + appid="game_2048", name="2048", apptype=FlipperAppType.EXTERNAL, entry_point="game_2048_app", diff --git a/applications/plugins/game_2048/game_2048.c b/applications/plugins/game_2048/game_2048.c index e0c4a4cec..cf8bba8f1 100644 --- a/applications/plugins/game_2048/game_2048.c +++ b/applications/plugins/game_2048/game_2048.c @@ -33,6 +33,7 @@ typedef enum { } State; typedef struct { + FuriMutex* mutex; State state; uint8_t table[CELLS_COUNT][CELLS_COUNT]; uint32_t score; @@ -103,8 +104,9 @@ static void gray_canvas(Canvas* const canvas) { } static void draw_callback(Canvas* const canvas, void* ctx) { - const GameState* game_state = acquire_mutex((ValueMutex*)ctx, 25); - if(game_state == NULL) return; + furi_assert(ctx); + const GameState* game_state = ctx; + furi_mutex_acquire(game_state->mutex, FuriWaitForever); canvas_clear(canvas); @@ -180,7 +182,7 @@ static void draw_callback(Canvas* const canvas, void* ctx) { canvas_draw_str_aligned(canvas, 64, 48, AlignCenter, AlignBottom, buf); } - release_mutex((ValueMutex*)ctx, game_state); + furi_mutex_release(game_state->mutex); } void calculate_move_to_left(uint8_t arr[], MoveResult* const move_result) { @@ -380,9 +382,9 @@ int32_t game_2048_app() { MoveResult* move_result = malloc(sizeof(MoveResult)); - ValueMutex state_mutex; - if(!init_mutex(&state_mutex, game_state, sizeof(GameState))) { - FURI_LOG_E("SnakeGame", "cannot create mutex\r\n"); + game_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!game_state->mutex) { + FURI_LOG_E("2048Game", "cannot create mutex\r\n"); free(game_state); return 255; } @@ -391,7 +393,7 @@ int32_t game_2048_app() { FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, draw_callback, &state_mutex); + view_port_draw_callback_set(view_port, draw_callback, game_state); view_port_input_callback_set(view_port, input_callback, event_queue); Gui* gui = furi_record_open(RECORD_GUI); @@ -404,7 +406,7 @@ int32_t game_2048_app() { // handle only press event, ignore repeat/release events if(input.type != InputTypePress) continue; - GameState* game_state = (GameState*)acquire_mutex_block(&state_mutex); + furi_mutex_acquire(game_state->mutex, FuriWaitForever); switch(game_state->state) { case GameStateMenu: @@ -489,7 +491,7 @@ int32_t game_2048_app() { } view_port_update(view_port); - release_mutex(&state_mutex, game_state); + furi_mutex_release(game_state->mutex); } } @@ -500,7 +502,7 @@ int32_t game_2048_app() { furi_message_queue_free(event_queue); - delete_mutex(&state_mutex); + furi_mutex_free(game_state->mutex); free(game_state); free(move_result); diff --git a/applications/plugins/game_of_life/game_of_life.c b/applications/plugins/game_of_life/game_of_life.c index 65be8cb72..8629062b8 100644 --- a/applications/plugins/game_of_life/game_of_life.c +++ b/applications/plugins/game_of_life/game_of_life.c @@ -21,6 +21,7 @@ typedef struct { typedef struct { bool revive; int evo; + FuriMutex* mutex; } State; unsigned char new[TOTAL_PIXELS] = {}; @@ -92,7 +93,10 @@ static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queu } static void render_callback(Canvas* canvas, void* ctx) { - State* state = (State*)acquire_mutex((ValueMutex*)ctx, 25); + //furi_assert(ctx); + State* state = ctx; + furi_mutex_acquire(state->mutex, FuriWaitForever); + canvas_clear(canvas); for(int i = 0; i < TOTAL_PIXELS; ++i) { @@ -100,7 +104,7 @@ static void render_callback(Canvas* canvas, void* ctx) { int y = (int)(i / SCREEN_WIDTH); if(fields[current][i] == 1) canvas_draw_dot(canvas, x, y); } - release_mutex((ValueMutex*)ctx, state); + furi_mutex_release(state->mutex); } int32_t game_of_life_app(void* p) { @@ -112,8 +116,8 @@ int32_t game_of_life_app(void* p) { State* _state = malloc(sizeof(State)); - ValueMutex state_mutex; - if(!init_mutex(&state_mutex, _state, sizeof(State))) { + _state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!_state->mutex) { printf("cannot create mutex\r\n"); furi_message_queue_free(event_queue); free(_state); @@ -121,7 +125,7 @@ int32_t game_of_life_app(void* p) { } ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, render_callback, &state_mutex); + view_port_draw_callback_set(view_port, render_callback, _state); view_port_input_callback_set(view_port, input_callback, event_queue); Gui* gui = furi_record_open(RECORD_GUI); @@ -129,23 +133,23 @@ int32_t game_of_life_app(void* p) { AppEvent event; for(bool processing = true; processing;) { - State* state = (State*)acquire_mutex_block(&state_mutex); FuriStatus event_status = furi_message_queue_get(event_queue, &event, 25); + furi_mutex_acquire(_state->mutex, FuriWaitForever); if(event_status == FuriStatusOk && event.type == EventTypeKey && event.input.type == InputTypePress) { if(event.input.key == InputKeyBack) { // furiac_exit(NULL); processing = false; - release_mutex(&state_mutex, state); + furi_mutex_release(_state->mutex); break; } } - update_field(state); + update_field(_state); view_port_update(view_port); - release_mutex(&state_mutex, state); + furi_mutex_release(_state->mutex); } view_port_enabled_set(view_port, false); @@ -153,7 +157,7 @@ int32_t game_of_life_app(void* p) { furi_record_close(RECORD_GUI); view_port_free(view_port); furi_message_queue_free(event_queue); - delete_mutex(&state_mutex); + furi_mutex_free(_state->mutex); free(_state); return 0; diff --git a/applications/plugins/geigercounter/application.fam b/applications/plugins/geigercounter/application.fam index 71be5a161..ea5e1fa85 100644 --- a/applications/plugins/geigercounter/application.fam +++ b/applications/plugins/geigercounter/application.fam @@ -1,5 +1,5 @@ App( - appid="Geiger_Coutner", + appid="flipper_geiger", name="[GPIO] Geiger Counter", apptype=FlipperAppType.EXTERNAL, entry_point="flipper_geiger_app", diff --git a/applications/plugins/geigercounter/flipper_geiger.c b/applications/plugins/geigercounter/flipper_geiger.c index 709db9a26..a5503eb90 100644 --- a/applications/plugins/geigercounter/flipper_geiger.c +++ b/applications/plugins/geigercounter/flipper_geiger.c @@ -29,6 +29,7 @@ typedef struct { } EventApp; typedef struct { + FuriMutex* mutex; uint32_t cps, cpm; uint32_t line[SCREEN_SIZE_X / 2]; float coef; @@ -36,12 +37,13 @@ typedef struct { } mutexStruct; static void draw_callback(Canvas* canvas, void* ctx) { - UNUSED(ctx); + furi_assert(ctx); mutexStruct displayStruct; - mutexStruct* geigerMutex = (mutexStruct*)acquire_mutex_block((ValueMutex*)ctx); + mutexStruct* geigerMutex = ctx; + furi_mutex_acquire(geigerMutex->mutex, FuriWaitForever); memcpy(&displayStruct, geigerMutex, sizeof(mutexStruct)); - release_mutex((ValueMutex*)ctx, geigerMutex); + furi_mutex_release(geigerMutex->mutex); char buffer[32]; if(displayStruct.data == 0) @@ -101,7 +103,8 @@ static void gpiocallback(void* ctx) { furi_message_queue_put(queue, &event, 0); } -int32_t flipper_geiger_app() { +int32_t flipper_geiger_app(void* p) { + UNUSED(p); EventApp event; FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(EventApp)); @@ -117,11 +120,14 @@ int32_t flipper_geiger_app() { uint32_t counter = 0; - ValueMutex state_mutex; - init_mutex(&state_mutex, &mutexVal, sizeof(mutexVal)); + mutexVal.mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!mutexVal.mutex) { + furi_message_queue_free(event_queue); + return 255; + } ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, draw_callback, &state_mutex); + view_port_draw_callback_set(view_port, draw_callback, &mutexVal); view_port_input_callback_set(view_port, input_callback, event_queue); furi_hal_gpio_add_int_callback(&gpio_ext_pa7, gpiocallback, event_queue); @@ -146,62 +152,62 @@ int32_t flipper_geiger_app() { break; } else if(event.input.key == InputKeyOk && event.input.type == InputTypeShort) { counter = 0; - mutexStruct* geigerMutex = (mutexStruct*)acquire_mutex_block(&state_mutex); + furi_mutex_acquire(mutexVal.mutex, FuriWaitForever); - geigerMutex->cps = 0; - geigerMutex->cpm = 0; - for(int i = 0; i < SCREEN_SIZE_X / 2; i++) geigerMutex->line[i] = 0; + mutexVal.cps = 0; + mutexVal.cpm = 0; + for(int i = 0; i < SCREEN_SIZE_X / 2; i++) mutexVal.line[i] = 0; screenRefresh = 1; - release_mutex(&state_mutex, geigerMutex); + furi_mutex_release(mutexVal.mutex); } else if((event.input.key == InputKeyLeft && event.input.type == InputTypeShort)) { - mutexStruct* geigerMutex = (mutexStruct*)acquire_mutex_block(&state_mutex); + furi_mutex_acquire(mutexVal.mutex, FuriWaitForever); - if(geigerMutex->data != 0) - geigerMutex->data--; + if(mutexVal.data != 0) + mutexVal.data--; else - geigerMutex->data = 2; + mutexVal.data = 2; screenRefresh = 1; - release_mutex(&state_mutex, geigerMutex); + furi_mutex_release(mutexVal.mutex); } else if((event.input.key == InputKeyRight && event.input.type == InputTypeShort)) { - mutexStruct* geigerMutex = (mutexStruct*)acquire_mutex_block(&state_mutex); + furi_mutex_acquire(mutexVal.mutex, FuriWaitForever); - if(geigerMutex->data != 2) - geigerMutex->data++; + if(mutexVal.data != 2) + mutexVal.data++; else - geigerMutex->data = 0; + mutexVal.data = 0; screenRefresh = 1; - release_mutex(&state_mutex, geigerMutex); + furi_mutex_release(mutexVal.mutex); } } else if(event.type == ClockEventTypeTick) { - mutexStruct* geigerMutex = (mutexStruct*)acquire_mutex_block(&state_mutex); + furi_mutex_acquire(mutexVal.mutex, FuriWaitForever); for(int i = 0; i < SCREEN_SIZE_X / 2 - 1; i++) - geigerMutex->line[SCREEN_SIZE_X / 2 - 1 - i] = - geigerMutex->line[SCREEN_SIZE_X / 2 - 2 - i]; + mutexVal.line[SCREEN_SIZE_X / 2 - 1 - i] = + mutexVal.line[SCREEN_SIZE_X / 2 - 2 - i]; - geigerMutex->line[0] = counter; - geigerMutex->cps = counter; + mutexVal.line[0] = counter; + mutexVal.cps = counter; counter = 0; - geigerMutex->cpm = geigerMutex->line[0]; - uint32_t max = geigerMutex->line[0]; + mutexVal.cpm = mutexVal.line[0]; + uint32_t max = mutexVal.line[0]; for(int i = 1; i < SCREEN_SIZE_X / 2; i++) { - if(i < 60) geigerMutex->cpm += geigerMutex->line[i]; - if(geigerMutex->line[i] > max) max = geigerMutex->line[i]; + if(i < 60) mutexVal.cpm += mutexVal.line[i]; + if(mutexVal.line[i] > max) max = mutexVal.line[i]; } if(max > 0) - geigerMutex->coef = ((float)(SCREEN_SIZE_Y - 15)) / ((float)max); + mutexVal.coef = ((float)(SCREEN_SIZE_Y - 15)) / ((float)max); else - geigerMutex->coef = 1; + mutexVal.coef = 1; screenRefresh = 1; - release_mutex(&state_mutex, geigerMutex); + furi_mutex_release(mutexVal.mutex); } else if(event.type == EventGPIO) { counter++; } @@ -217,7 +223,7 @@ int32_t flipper_geiger_app() { furi_hal_pwm_stop(FuriHalPwmOutputIdLptim2PA4); furi_message_queue_free(event_queue); - delete_mutex(&state_mutex); + furi_mutex_free(mutexVal.mutex); gui_remove_view_port(gui, view_port); view_port_free(view_port); furi_timer_free(timer); diff --git a/applications/plugins/gpioreader2/GPIO_reader.c b/applications/plugins/gpio_reader_a/GPIO_reader.c similarity index 88% rename from applications/plugins/gpioreader2/GPIO_reader.c rename to applications/plugins/gpio_reader_a/GPIO_reader.c index be333cd7e..eb5f95e67 100644 --- a/applications/plugins/gpioreader2/GPIO_reader.c +++ b/applications/plugins/gpio_reader_a/GPIO_reader.c @@ -17,10 +17,13 @@ typedef struct { typedef struct { int pin; int pullMode; + FuriMutex* mutex; } PluginState; static void render_callback(Canvas* const canvas, void* ctx) { - const PluginState* plugin_state = acquire_mutex((ValueMutex*)ctx, 25); + furi_assert(ctx); + const PluginState* plugin_state = ctx; + furi_mutex_acquire(plugin_state->mutex, FuriWaitForever); canvas_set_font(canvas, FontPrimary); canvas_draw_str_aligned( @@ -57,7 +60,7 @@ static void render_callback(Canvas* const canvas, void* ctx) { AlignCenter, gpio_item_get_pin_level(plugin_state->pin)); - release_mutex((ValueMutex*)ctx, plugin_state); + furi_mutex_release(plugin_state->mutex); } static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { @@ -79,8 +82,8 @@ int32_t GPIO_reader_app(void* p) { PluginState* plugin_state = malloc(sizeof(PluginState)); GPIO_reader_state_init(plugin_state); - ValueMutex state_mutex; - if(!init_mutex(&state_mutex, plugin_state, sizeof(PluginState))) { + plugin_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!plugin_state->mutex) { FURI_LOG_E("GPIO_reader", "cannot create mutex\r\n"); free(plugin_state); return 255; @@ -88,7 +91,7 @@ int32_t GPIO_reader_app(void* p) { // Set system callbacks ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, render_callback, &state_mutex); + view_port_draw_callback_set(view_port, render_callback, plugin_state); view_port_input_callback_set(view_port, input_callback, event_queue); // Open GUI and register view_port @@ -98,7 +101,7 @@ int32_t GPIO_reader_app(void* p) { PluginEvent event; for(bool processing = true; processing;) { FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); - PluginState* plugin_state = (PluginState*)acquire_mutex_block(&state_mutex); + furi_mutex_acquire(plugin_state->mutex, FuriWaitForever); if(event_status == FuriStatusOk) { // press events @@ -131,20 +134,19 @@ int32_t GPIO_reader_app(void* p) { } } } - } else { - FURI_LOG_D("GPIO_reader", "FuriMessageQueue: event timeout"); - // event timeout } view_port_update(view_port); - release_mutex(&state_mutex, plugin_state); + furi_mutex_release(plugin_state->mutex); } view_port_enabled_set(view_port, false); gui_remove_view_port(gui, view_port); furi_record_close("gui"); view_port_free(view_port); + furi_mutex_free(plugin_state->mutex); furi_message_queue_free(event_queue); + free(plugin_state); return 0; } \ No newline at end of file diff --git a/applications/plugins/gpioreader2/GPIO_reader_item.c b/applications/plugins/gpio_reader_a/GPIO_reader_item.c similarity index 100% rename from applications/plugins/gpioreader2/GPIO_reader_item.c rename to applications/plugins/gpio_reader_a/GPIO_reader_item.c diff --git a/applications/plugins/gpioreader2/GPIO_reader_item.h b/applications/plugins/gpio_reader_a/GPIO_reader_item.h similarity index 100% rename from applications/plugins/gpioreader2/GPIO_reader_item.h rename to applications/plugins/gpio_reader_a/GPIO_reader_item.h diff --git a/applications/plugins/gpioreader2/README.md b/applications/plugins/gpio_reader_a/README.md similarity index 100% rename from applications/plugins/gpioreader2/README.md rename to applications/plugins/gpio_reader_a/README.md diff --git a/applications/plugins/gpioreader2/application.fam b/applications/plugins/gpio_reader_a/application.fam similarity index 89% rename from applications/plugins/gpioreader2/application.fam rename to applications/plugins/gpio_reader_a/application.fam index bf25c323d..4ec90df6a 100644 --- a/applications/plugins/gpioreader2/application.fam +++ b/applications/plugins/gpio_reader_a/application.fam @@ -1,5 +1,5 @@ App( - appid="GPIO_Reader_A", + appid="gpio_reader_a", name="[GPIO] Reader (Aurelilc)", apptype=FlipperAppType.EXTERNAL, entry_point="GPIO_reader_app", diff --git a/applications/plugins/gpioreader/icon.png b/applications/plugins/gpio_reader_a/icon.png similarity index 100% rename from applications/plugins/gpioreader/icon.png rename to applications/plugins/gpio_reader_a/icon.png diff --git a/applications/plugins/gpioreader/LICENSE b/applications/plugins/gpio_reader_b/LICENSE similarity index 100% rename from applications/plugins/gpioreader/LICENSE rename to applications/plugins/gpio_reader_b/LICENSE diff --git a/applications/plugins/gpioreader/README.md b/applications/plugins/gpio_reader_b/README.md similarity index 100% rename from applications/plugins/gpioreader/README.md rename to applications/plugins/gpio_reader_b/README.md diff --git a/applications/plugins/gpioreader/application.fam b/applications/plugins/gpio_reader_b/application.fam similarity index 91% rename from applications/plugins/gpioreader/application.fam rename to applications/plugins/gpio_reader_b/application.fam index a7f297d47..aa26060aa 100644 --- a/applications/plugins/gpioreader/application.fam +++ b/applications/plugins/gpio_reader_b/application.fam @@ -1,5 +1,5 @@ App( - appid="GPIO_Reader_B", + appid="gpio_reader_b", name="[GPIO] Reader (biotinker)", apptype=FlipperAppType.EXTERNAL, entry_point="gpio_app", diff --git a/applications/plugins/gpioreader/gpio_app.c b/applications/plugins/gpio_reader_b/gpio_app.c similarity index 100% rename from applications/plugins/gpioreader/gpio_app.c rename to applications/plugins/gpio_reader_b/gpio_app.c diff --git a/applications/plugins/gpioreader/gpio_app.h b/applications/plugins/gpio_reader_b/gpio_app.h similarity index 100% rename from applications/plugins/gpioreader/gpio_app.h rename to applications/plugins/gpio_reader_b/gpio_app.h diff --git a/applications/plugins/gpioreader/gpio_app_i.h b/applications/plugins/gpio_reader_b/gpio_app_i.h similarity index 100% rename from applications/plugins/gpioreader/gpio_app_i.h rename to applications/plugins/gpio_reader_b/gpio_app_i.h diff --git a/applications/plugins/gpioreader/gpio_custom_event.h b/applications/plugins/gpio_reader_b/gpio_custom_event.h similarity index 100% rename from applications/plugins/gpioreader/gpio_custom_event.h rename to applications/plugins/gpio_reader_b/gpio_custom_event.h diff --git a/applications/plugins/gpioreader/gpio_item.c b/applications/plugins/gpio_reader_b/gpio_item.c similarity index 100% rename from applications/plugins/gpioreader/gpio_item.c rename to applications/plugins/gpio_reader_b/gpio_item.c diff --git a/applications/plugins/gpioreader/gpio_item.h b/applications/plugins/gpio_reader_b/gpio_item.h similarity index 100% rename from applications/plugins/gpioreader/gpio_item.h rename to applications/plugins/gpio_reader_b/gpio_item.h diff --git a/applications/plugins/gpioreader/gpioreader.png b/applications/plugins/gpio_reader_b/gpioreader.png similarity index 100% rename from applications/plugins/gpioreader/gpioreader.png rename to applications/plugins/gpio_reader_b/gpioreader.png diff --git a/applications/plugins/gpioreader2/icon.png b/applications/plugins/gpio_reader_b/icon.png similarity index 100% rename from applications/plugins/gpioreader2/icon.png rename to applications/plugins/gpio_reader_b/icon.png diff --git a/applications/plugins/gpioreader/scenes/gpio_scene.c b/applications/plugins/gpio_reader_b/scenes/gpio_scene.c similarity index 100% rename from applications/plugins/gpioreader/scenes/gpio_scene.c rename to applications/plugins/gpio_reader_b/scenes/gpio_scene.c diff --git a/applications/plugins/gpioreader/scenes/gpio_scene.h b/applications/plugins/gpio_reader_b/scenes/gpio_scene.h similarity index 100% rename from applications/plugins/gpioreader/scenes/gpio_scene.h rename to applications/plugins/gpio_reader_b/scenes/gpio_scene.h diff --git a/applications/plugins/gpioreader/scenes/gpio_scene_config.h b/applications/plugins/gpio_reader_b/scenes/gpio_scene_config.h similarity index 100% rename from applications/plugins/gpioreader/scenes/gpio_scene_config.h rename to applications/plugins/gpio_reader_b/scenes/gpio_scene_config.h diff --git a/applications/plugins/gpioreader/scenes/gpio_scene_reader.c b/applications/plugins/gpio_reader_b/scenes/gpio_scene_reader.c similarity index 100% rename from applications/plugins/gpioreader/scenes/gpio_scene_reader.c rename to applications/plugins/gpio_reader_b/scenes/gpio_scene_reader.c diff --git a/applications/plugins/gpioreader/scenes/gpio_scene_start.c b/applications/plugins/gpio_reader_b/scenes/gpio_scene_start.c similarity index 100% rename from applications/plugins/gpioreader/scenes/gpio_scene_start.c rename to applications/plugins/gpio_reader_b/scenes/gpio_scene_start.c diff --git a/applications/plugins/gpioreader/scenes/gpio_scene_test.c b/applications/plugins/gpio_reader_b/scenes/gpio_scene_test.c similarity index 100% rename from applications/plugins/gpioreader/scenes/gpio_scene_test.c rename to applications/plugins/gpio_reader_b/scenes/gpio_scene_test.c diff --git a/applications/plugins/gpioreader/scenes/gpio_scene_usb_uart.c b/applications/plugins/gpio_reader_b/scenes/gpio_scene_usb_uart.c similarity index 100% rename from applications/plugins/gpioreader/scenes/gpio_scene_usb_uart.c rename to applications/plugins/gpio_reader_b/scenes/gpio_scene_usb_uart.c diff --git a/applications/plugins/gpioreader/scenes/gpio_scene_usb_uart_close_rpc.c b/applications/plugins/gpio_reader_b/scenes/gpio_scene_usb_uart_close_rpc.c similarity index 100% rename from applications/plugins/gpioreader/scenes/gpio_scene_usb_uart_close_rpc.c rename to applications/plugins/gpio_reader_b/scenes/gpio_scene_usb_uart_close_rpc.c diff --git a/applications/plugins/gpioreader/scenes/gpio_scene_usb_uart_config.c b/applications/plugins/gpio_reader_b/scenes/gpio_scene_usb_uart_config.c similarity index 100% rename from applications/plugins/gpioreader/scenes/gpio_scene_usb_uart_config.c rename to applications/plugins/gpio_reader_b/scenes/gpio_scene_usb_uart_config.c diff --git a/applications/plugins/gpioreader/usb_uart_bridge.c b/applications/plugins/gpio_reader_b/usb_uart_bridge.c similarity index 100% rename from applications/plugins/gpioreader/usb_uart_bridge.c rename to applications/plugins/gpio_reader_b/usb_uart_bridge.c diff --git a/applications/plugins/gpioreader/usb_uart_bridge.h b/applications/plugins/gpio_reader_b/usb_uart_bridge.h similarity index 100% rename from applications/plugins/gpioreader/usb_uart_bridge.h rename to applications/plugins/gpio_reader_b/usb_uart_bridge.h diff --git a/applications/plugins/gpioreader/views/gpio_reader.c b/applications/plugins/gpio_reader_b/views/gpio_reader.c similarity index 100% rename from applications/plugins/gpioreader/views/gpio_reader.c rename to applications/plugins/gpio_reader_b/views/gpio_reader.c diff --git a/applications/plugins/gpioreader/views/gpio_reader.h b/applications/plugins/gpio_reader_b/views/gpio_reader.h similarity index 100% rename from applications/plugins/gpioreader/views/gpio_reader.h rename to applications/plugins/gpio_reader_b/views/gpio_reader.h diff --git a/applications/plugins/gpioreader/views/gpio_test.c b/applications/plugins/gpio_reader_b/views/gpio_test.c similarity index 100% rename from applications/plugins/gpioreader/views/gpio_test.c rename to applications/plugins/gpio_reader_b/views/gpio_test.c diff --git a/applications/plugins/gpioreader/views/gpio_test.h b/applications/plugins/gpio_reader_b/views/gpio_test.h similarity index 100% rename from applications/plugins/gpioreader/views/gpio_test.h rename to applications/plugins/gpio_reader_b/views/gpio_test.h diff --git a/applications/plugins/gpioreader/views/gpio_usb_uart.c b/applications/plugins/gpio_reader_b/views/gpio_usb_uart.c similarity index 100% rename from applications/plugins/gpioreader/views/gpio_usb_uart.c rename to applications/plugins/gpio_reader_b/views/gpio_usb_uart.c diff --git a/applications/plugins/gpioreader/views/gpio_usb_uart.h b/applications/plugins/gpio_reader_b/views/gpio_usb_uart.h similarity index 100% rename from applications/plugins/gpioreader/views/gpio_usb_uart.h rename to applications/plugins/gpio_reader_b/views/gpio_usb_uart.h diff --git a/applications/plugins/gps_nmea_uart/README.md b/applications/plugins/gps_nmea_uart/README.md index 3f7a5ef3d..7af0fa3b3 100644 --- a/applications/plugins/gps_nmea_uart/README.md +++ b/applications/plugins/gps_nmea_uart/README.md @@ -1,12 +1,9 @@ # GPS for Flipper Zero -A simple Flipper Zero application for NMEA 0183 serial GPS modules, such as the - [Original link](https://github.com/ezod/flipperzero-gps) + [Adafruit Ultimate GPS Breakout]. -[Original link](https://github.com/ezod/flipperzero-gps) - ![ui](ui.png) Heavy lifting (NMEA parsing) provided by [minmea], which is included in this @@ -35,4 +32,3 @@ baud rate, may be useful for other GPS modules. [Adafruit Ultimate GPS Breakout]: https://www.adafruit.com/product/746 [minmea]: https://github.com/kosma/minmea [flipperzero-firmware]: https://github.com/flipperdevices/flipperzero-firmware -[qFlipper]: https://flipperzero.one/update diff --git a/applications/plugins/gps_nmea_uart/application.fam b/applications/plugins/gps_nmea_uart/application.fam index 138fb3f29..a5fdb4360 100644 --- a/applications/plugins/gps_nmea_uart/application.fam +++ b/applications/plugins/gps_nmea_uart/application.fam @@ -1,5 +1,5 @@ App( - appid="NMEA_GPS", + appid="gps_nmea", name="[NMEA] GPS", apptype=FlipperAppType.EXTERNAL, entry_point="gps_app", diff --git a/applications/plugins/gps_nmea_uart/gps.c b/applications/plugins/gps_nmea_uart/gps.c index 62053cede..b36fd080b 100644 --- a/applications/plugins/gps_nmea_uart/gps.c +++ b/applications/plugins/gps_nmea_uart/gps.c @@ -15,10 +15,9 @@ typedef struct { } PluginEvent; static void render_callback(Canvas* const canvas, void* context) { - const GpsUart* gps_uart = acquire_mutex((ValueMutex*)context, 25); - if(gps_uart == NULL) { - return; - } + furi_assert(context); + const GpsUart* gps_uart = context; + furi_mutex_acquire(gps_uart->mutex, FuriWaitForever); canvas_set_font(canvas, FontPrimary); canvas_draw_str_aligned(canvas, 32, 8, AlignCenter, AlignBottom, "Latitude"); @@ -57,7 +56,7 @@ static void render_callback(Canvas* const canvas, void* context) { gps_uart->status.time_seconds); canvas_draw_str_aligned(canvas, 96, 62, AlignCenter, AlignBottom, buffer); - release_mutex((ValueMutex*)context, gps_uart); + furi_mutex_release(gps_uart->mutex); } static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { @@ -74,8 +73,8 @@ int32_t gps_app(void* p) { GpsUart* gps_uart = gps_uart_enable(); - ValueMutex gps_uart_mutex; - if(!init_mutex(&gps_uart_mutex, gps_uart, sizeof(GpsUart))) { + gps_uart->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!gps_uart->mutex) { FURI_LOG_E("GPS", "cannot create mutex\r\n"); free(gps_uart); return 255; @@ -83,18 +82,18 @@ int32_t gps_app(void* p) { // set system callbacks ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, render_callback, &gps_uart_mutex); + view_port_draw_callback_set(view_port, render_callback, gps_uart); view_port_input_callback_set(view_port, input_callback, event_queue); // open GUI and register view_port - Gui* gui = furi_record_open("gui"); + Gui* gui = furi_record_open(RECORD_GUI); gui_add_view_port(gui, view_port, GuiLayerFullscreen); PluginEvent event; for(bool processing = true; processing;) { FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); - GpsUart* gps_uart = (GpsUart*)acquire_mutex_block(&gps_uart_mutex); + furi_mutex_acquire(gps_uart->mutex, FuriWaitForever); if(event_status == FuriStatusOk) { // press events @@ -118,15 +117,15 @@ int32_t gps_app(void* p) { } view_port_update(view_port); - release_mutex(&gps_uart_mutex, gps_uart); + furi_mutex_release(gps_uart->mutex); } view_port_enabled_set(view_port, false); gui_remove_view_port(gui, view_port); - furi_record_close("gui"); + furi_record_close(RECORD_GUI); view_port_free(view_port); furi_message_queue_free(event_queue); - delete_mutex(&gps_uart_mutex); + furi_mutex_free(gps_uart->mutex); gps_uart_disable(gps_uart); return 0; diff --git a/applications/plugins/gps_nmea_uart/gps_uart.h b/applications/plugins/gps_nmea_uart/gps_uart.h index d6aafae9f..fd33eb245 100644 --- a/applications/plugins/gps_nmea_uart/gps_uart.h +++ b/applications/plugins/gps_nmea_uart/gps_uart.h @@ -22,6 +22,7 @@ typedef struct { } GpsStatus; typedef struct { + FuriMutex* mutex; FuriThread* thread; FuriStreamBuffer* rx_stream; uint8_t rx_buf[RX_BUF_SIZE]; diff --git a/applications/plugins/hc_sr04/README.md b/applications/plugins/hc_sr04/README.md deleted file mode 100644 index 2fb3b8ce4..000000000 --- a/applications/plugins/hc_sr04/README.md +++ /dev/null @@ -1 +0,0 @@ -## (5V -> VCC) / (GND -> GND) / (13|TX -> Trig) / (14|RX -> Echo) \ No newline at end of file diff --git a/applications/plugins/hc_sr04/application.fam b/applications/plugins/hc_sr04/application.fam index bfbb2daa8..85b6da2df 100644 --- a/applications/plugins/hc_sr04/application.fam +++ b/applications/plugins/hc_sr04/application.fam @@ -1,5 +1,5 @@ App( - appid="HC_SR04_Dist_Sensor", + appid="hc_sr04", name="[HC-SR] Dist. Sensor", apptype=FlipperAppType.EXTERNAL, entry_point="hc_sr04_app", diff --git a/applications/plugins/hc_sr04/hc_sr04.c b/applications/plugins/hc_sr04/hc_sr04.c index dbbf4f3ec..66ffdad30 100644 --- a/applications/plugins/hc_sr04/hc_sr04.c +++ b/applications/plugins/hc_sr04/hc_sr04.c @@ -3,6 +3,7 @@ // Ported and modified by @xMasterX #include +#include #include #include #include @@ -23,10 +24,11 @@ typedef struct { } PluginEvent; typedef struct { + FuriMutex* mutex; NotificationApp* notification; bool have_5v; bool measurement_made; - uint32_t echo; // ms + uint32_t echo; // us float distance; // meters } PluginState; @@ -40,10 +42,10 @@ const NotificationSequence sequence_done = { }; static void render_callback(Canvas* const canvas, void* ctx) { - const PluginState* plugin_state = acquire_mutex((ValueMutex*)ctx, 25); - if(plugin_state == NULL) { - return; - } + furi_assert(ctx); + const PluginState* plugin_state = ctx; + furi_mutex_acquire(plugin_state->mutex, FuriWaitForever); + // border around the edge of the screen // canvas_draw_frame(canvas, 0, 0, 128, 64); @@ -72,7 +74,7 @@ static void render_callback(Canvas* const canvas, void* ctx) { FuriString* str_buf; str_buf = furi_string_alloc(); - furi_string_printf(str_buf, "Echo: %ld ms", plugin_state->echo); + furi_string_printf(str_buf, "Echo: %ld us", plugin_state->echo); canvas_draw_str_aligned( canvas, 8, 38, AlignLeft, AlignTop, furi_string_get_cstr(str_buf)); @@ -84,7 +86,7 @@ static void render_callback(Canvas* const canvas, void* ctx) { } } - release_mutex((ValueMutex*)ctx, plugin_state); + furi_mutex_release(plugin_state->mutex); } static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { @@ -110,9 +112,11 @@ static void hc_sr04_state_init(PluginState* const plugin_state) { } } -float hc_sr04_ms_to_m(uint32_t ms) { - const float speed_sound_m_per_s = 343.0f; - const float time_s = ms / 1e3f; +float hc_sr04_us_to_m(uint32_t us) { + //speed of sound for 20°C, 50% relative humidity + //331.3 + 20 * 0.606 + 50 * 0.0124 = 0.034404 + const float speed_sound_m_per_s = 344.04f; + const float time_s = us / 1e6f; const float total_dist = time_s * speed_sound_m_per_s; return total_dist / 2.0f; } @@ -147,10 +151,6 @@ static void hc_sr04_measure(PluginState* const plugin_state) { furi_delay_ms(10); furi_hal_gpio_write(&gpio_usart_tx, false); - // TODO change from furi_get_tick(), which returns ms, - // to DWT->CYCCNT, which is a more precise counter with - // us precision (see furi_hal_cortex_delay_us) - const uint32_t start = furi_get_tick(); while(furi_get_tick() - start < timeout_ms && furi_hal_gpio_read(&gpio_usart_rx)) @@ -158,16 +158,17 @@ static void hc_sr04_measure(PluginState* const plugin_state) { while(furi_get_tick() - start < timeout_ms && !furi_hal_gpio_read(&gpio_usart_rx)) ; - const uint32_t pulse_start = furi_get_tick(); + const uint32_t pulse_start = DWT->CYCCNT; while(furi_get_tick() - start < timeout_ms && furi_hal_gpio_read(&gpio_usart_rx)) ; + const uint32_t pulse_end = DWT->CYCCNT; - const uint32_t pulse_end = furi_get_tick(); //FURI_CRITICAL_EXIT(); - plugin_state->echo = pulse_end - pulse_start; - plugin_state->distance = hc_sr04_ms_to_m(pulse_end - pulse_start); + plugin_state->echo = + (pulse_end - pulse_start) / furi_hal_cortex_instructions_per_microsecond(); + plugin_state->distance = hc_sr04_us_to_m(plugin_state->echo); plugin_state->measurement_made = true; //furi_hal_light_set(LightRed, 0x00); @@ -184,8 +185,8 @@ int32_t hc_sr04_app() { furi_hal_console_disable(); - ValueMutex state_mutex; - if(!init_mutex(&state_mutex, plugin_state, sizeof(PluginState))) { + plugin_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!plugin_state->mutex) { FURI_LOG_E("hc_sr04", "cannot create mutex\r\n"); if(furi_hal_power_is_otg_enabled()) { furi_hal_power_disable_otg(); @@ -201,7 +202,7 @@ int32_t hc_sr04_app() { // Set system callbacks ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, render_callback, &state_mutex); + view_port_draw_callback_set(view_port, render_callback, plugin_state); view_port_input_callback_set(view_port, input_callback, event_queue); // Open GUI and register view_port @@ -212,7 +213,7 @@ int32_t hc_sr04_app() { for(bool processing = true; processing;) { FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); - PluginState* plugin_state = (PluginState*)acquire_mutex_block(&state_mutex); + furi_mutex_acquire(plugin_state->mutex, FuriWaitForever); if(event_status == FuriStatusOk) { // press events @@ -238,7 +239,7 @@ int32_t hc_sr04_app() { } view_port_update(view_port); - release_mutex(&state_mutex, plugin_state); + furi_mutex_release(plugin_state->mutex); } if(furi_hal_power_is_otg_enabled()) { @@ -267,7 +268,8 @@ int32_t hc_sr04_app() { furi_record_close(RECORD_NOTIFICATION); view_port_free(view_port); furi_message_queue_free(event_queue); - delete_mutex(&state_mutex); + furi_mutex_free(plugin_state->mutex); + free(plugin_state); return 0; -} \ No newline at end of file +} diff --git a/applications/plugins/heap_defence_game/application.fam b/applications/plugins/heap_defence_game/application.fam index bedd27dbc..b132531d1 100644 --- a/applications/plugins/heap_defence_game/application.fam +++ b/applications/plugins/heap_defence_game/application.fam @@ -1,5 +1,5 @@ App( - appid="Heap_Defence", + appid="heap_defence", name="Heap Defence", apptype=FlipperAppType.EXTERNAL, entry_point="heap_defence_app", diff --git a/applications/plugins/heap_defence_game/heap_defence.c b/applications/plugins/heap_defence_game/heap_defence.c index a22a8db22..f7d6e4ef7 100644 --- a/applications/plugins/heap_defence_game/heap_defence.c +++ b/applications/plugins/heap_defence_game/heap_defence.c @@ -7,7 +7,7 @@ #include #include "hede_assets.h" -#include "Heap_Defence_icons.h" +#include "heap_defence_icons.h" #include #include @@ -86,6 +86,7 @@ typedef struct { Person* person; Animations animation; GameStatuses game_status; + FuriMutex* mutex; } GameState; typedef Box** Field; @@ -432,15 +433,15 @@ static void draw_box(Canvas* canvas, Box* box, int x, int y) { static void heap_defense_render_callback(Canvas* const canvas, void* mutex) { furi_assert(mutex); - - const GameState* game = acquire_mutex((ValueMutex*)mutex, 25); + const GameState* game = mutex; + furi_mutex_acquire(game->mutex, FuriWaitForever); ///Draw GameOver or Pause if(!(game->game_status & GameStatusInProgress)) { FURI_LOG_W(TAG, "[DAED_DRAW]func: [%s] line: %d ", __FUNCTION__, __LINE__); canvas_draw_icon_animation(canvas, 0, 0, animations[game->animation]); - release_mutex((ValueMutex*)mutex, game); + furi_mutex_release(game->mutex); return; } @@ -480,7 +481,7 @@ static void heap_defense_render_callback(Canvas* const canvas, void* mutex) { } } - release_mutex((ValueMutex*)mutex, game); + furi_mutex_release(game->mutex); } static void heap_defense_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { @@ -505,25 +506,25 @@ int32_t heap_defence_app(void* p) { FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(GameEvent)); GameState* game = allocGameState(); - ValueMutex state_mutex; - if(!init_mutex(&state_mutex, game, sizeof(GameState))) { + game->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!game->mutex) { game_destroy(game); return 1; } assets_load(); ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, heap_defense_render_callback, &state_mutex); + view_port_draw_callback_set(view_port, heap_defense_render_callback, game); view_port_input_callback_set(view_port, heap_defense_input_callback, event_queue); FuriTimer* timer = furi_timer_alloc(heap_defense_timer_callback, FuriTimerTypePeriodic, event_queue); furi_timer_start(timer, furi_kernel_get_tick_frequency() / TIMER_UPDATE_FREQ); - Gui* gui = furi_record_open("gui"); + Gui* gui = furi_record_open(RECORD_GUI); gui_add_view_port(gui, view_port, GuiLayerFullscreen); - NotificationApp* notification = furi_record_open("notification"); + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); memset(game->field[Y_LAST], 128, ROW_BYTE_SIZE); game->person->p.y -= 2; @@ -536,7 +537,7 @@ int32_t heap_defence_app(void* p) { continue; } - game = (GameState*)acquire_mutex_block(&state_mutex); + furi_mutex_acquire(game->mutex, FuriWaitForever); //unset vibration if(game->game_status & GameStatusVibro) { @@ -573,7 +574,7 @@ int32_t heap_defence_app(void* p) { notification_message(notification, &sequence_error); } } - release_mutex(&state_mutex, game); + furi_mutex_release(game->mutex); view_port_update(view_port); } @@ -581,11 +582,11 @@ int32_t heap_defence_app(void* p) { view_port_enabled_set(view_port, false); gui_remove_view_port(gui, view_port); view_port_free(view_port); - furi_record_close("gui"); - furi_record_close("notification"); + furi_record_close(RECORD_GUI); + furi_record_close(RECORD_NOTIFICATION); furi_message_queue_free(event_queue); assets_clear(); - delete_mutex(&state_mutex); + furi_mutex_free(game->mutex); game_destroy(game); return 0; diff --git a/applications/plugins/hex_viewer/README.md b/applications/plugins/hex_viewer/README.md deleted file mode 100644 index e830eef8e..000000000 --- a/applications/plugins/hex_viewer/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# flipper-zero-hex-viewer - -Hex Viewer application for Flipper Zero! - -![Hex Viewer app!](https://habrastorage.org/r/w1560/getpro/habr/upload_files/46e/28a/d97/46e28ad973d144b123a4ce513c895d18.png) - -[Link to FAP](https://nightly.link/QtRoS/flipper-zero-hex-viewer/actions/artifacts/448677581.zip) (firmware version 0.72) diff --git a/applications/plugins/hex_viewer/application.fam b/applications/plugins/hex_viewer/application.fam index cfddabb0d..30c31ba76 100644 --- a/applications/plugins/hex_viewer/application.fam +++ b/applications/plugins/hex_viewer/application.fam @@ -1,6 +1,6 @@ App( - appid="HEX_Viewer", - name="HEX Viewer", + appid="hex_viewer", + name="Hex Viewer", apptype=FlipperAppType.EXTERNAL, entry_point="hex_viewer_app", cdefines=["APP_HEX_VIEWER"], @@ -13,5 +13,4 @@ App( fap_icon="icons/hex_10px.png", fap_category="Misc", fap_icon_assets="icons", - fap_icon_assets_symbol="hex_viewer", ) diff --git a/applications/plugins/hid_app/application.fam b/applications/plugins/hid_app/application.fam index 86c65c2dd..b8c13e353 100644 --- a/applications/plugins/hid_app/application.fam +++ b/applications/plugins/hid_app/application.fam @@ -1,5 +1,5 @@ App( - appid="USB_Remote", + appid="hid_usb", name="USB Remote", apptype=FlipperAppType.PLUGIN, entry_point="hid_usb_app", @@ -10,8 +10,9 @@ App( fap_icon_assets_symbol="hid", ) + App( - appid="Bluetooth_Remote", + appid="hid_ble", name="Bluetooth Remote", apptype=FlipperAppType.PLUGIN, entry_point="hid_ble_app", diff --git a/applications/plugins/hid_app/hid.c b/applications/plugins/hid_app/hid.c index 7f63f0cc6..949ff63b3 100644 --- a/applications/plugins/hid_app/hid.c +++ b/applications/plugins/hid_app/hid.c @@ -376,7 +376,17 @@ int32_t hid_ble_app(void* p) { // Wait 2nd core to update nvm storage furi_delay_ms(200); - bt_keys_storage_set_storage_path(app->bt, HID_BT_KEYS_STORAGE_PATH); + // Migrate data from old sd-card folder + Storage* storage = furi_record_open(RECORD_STORAGE); + + storage_common_migrate( + storage, + EXT_PATH("apps/Tools/" HID_BT_KEYS_STORAGE_NAME), + APP_DATA_PATH(HID_BT_KEYS_STORAGE_NAME)); + + bt_keys_storage_set_storage_path(app->bt, APP_DATA_PATH(HID_BT_KEYS_STORAGE_NAME)); + + furi_record_close(RECORD_STORAGE); if(!bt_set_profile(app->bt, BtProfileHidKeyboard)) { FURI_LOG_E(TAG, "Failed to switch to HID profile"); diff --git a/applications/plugins/hid_app/hid.h b/applications/plugins/hid_app/hid.h index fe32a199b..8ed1664a3 100644 --- a/applications/plugins/hid_app/hid.h +++ b/applications/plugins/hid_app/hid.h @@ -23,7 +23,7 @@ #include "views/hid_mouse_jiggler.h" #include "views/hid_tiktok.h" -#define HID_BT_KEYS_STORAGE_PATH EXT_PATH("apps/Tools/.bt_hid.keys") +#define HID_BT_KEYS_STORAGE_NAME ".bt_hid.keys" typedef enum { HidTransportUsb, diff --git a/applications/plugins/hid_app/hid_usb_10px.png b/applications/plugins/hid_app/hid_usb_10px.png index 415de7d23..7649138eb 100644 Binary files a/applications/plugins/hid_app/hid_usb_10px.png and b/applications/plugins/hid_app/hid_usb_10px.png differ diff --git a/applications/plugins/hid_app/views/hid_keyboard.c b/applications/plugins/hid_app/views/hid_keyboard.c index 82e772b15..9d29ccf5e 100644 --- a/applications/plugins/hid_app/views/hid_keyboard.c +++ b/applications/plugins/hid_app/views/hid_keyboard.c @@ -140,18 +140,18 @@ const HidKeyboardKey hid_keyboard_keyset[ROW_COUNT][COLUMN_COUNT] = { {.width = 1, .icon = &I_ButtonRight_4x7, .value = HID_KEYBOARD_RIGHT_ARROW}, }, { - {.width = 3, .icon = NULL, .key = "Ctrl", .value = HID_KEYBOARD_L_CTRL}, + {.width = 2, .icon = NULL, .key = "Ctl", .value = HID_KEYBOARD_L_CTRL}, {.width = 0, .icon = NULL, .value = HID_KEYBOARD_L_CTRL}, - {.width = 0, .icon = NULL, .value = HID_KEYBOARD_L_CTRL}, - {.width = 3, .icon = NULL, .key = "Alt", .value = HID_KEYBOARD_L_ALT}, + {.width = 2, .icon = NULL, .key = "Alt", .value = HID_KEYBOARD_L_ALT}, {.width = 0, .icon = NULL, .value = HID_KEYBOARD_L_ALT}, - {.width = 0, .icon = NULL, .value = HID_KEYBOARD_L_ALT}, - {.width = 3, .icon = NULL, .key = "Cmd", .value = HID_KEYBOARD_L_GUI}, + {.width = 2, .icon = NULL, .key = "Cmd", .value = HID_KEYBOARD_L_GUI}, {.width = 0, .icon = NULL, .value = HID_KEYBOARD_L_GUI}, - {.width = 0, .icon = NULL, .value = HID_KEYBOARD_L_GUI}, - {.width = 3, .icon = NULL, .key = "Tab", .value = HID_KEYBOARD_TAB}, - {.width = 0, .icon = NULL, .value = HID_KEYBOARD_TAB}, + {.width = 2, .icon = NULL, .key = "Tab", .value = HID_KEYBOARD_TAB}, {.width = 0, .icon = NULL, .value = HID_KEYBOARD_TAB}, + {.width = 2, .icon = NULL, .key = "Esc", .value = HID_KEYBOARD_ESCAPE}, + {.width = 0, .icon = NULL, .value = HID_KEYBOARD_ESCAPE}, + {.width = 2, .icon = NULL, .key = "Del", .value = HID_KEYBOARD_DELETE_FORWARD}, + {.width = 0, .icon = NULL, .value = HID_KEYBOARD_DELETE_FORWARD}, }, }; diff --git a/applications/plugins/hid_app/views/hid_mouse_jiggler.c b/applications/plugins/hid_app/views/hid_mouse_jiggler.c index d8f1f8928..120d5bc34 100644 --- a/applications/plugins/hid_app/views/hid_mouse_jiggler.c +++ b/applications/plugins/hid_app/views/hid_mouse_jiggler.c @@ -6,6 +6,8 @@ #define TAG "HidMouseJiggler" +#define LENGTH(x) (int)(sizeof(x) / sizeof((x)[0])) + struct HidMouseJiggler { View* view; Hid* hid; @@ -15,10 +17,13 @@ struct HidMouseJiggler { typedef struct { bool connected; bool running; + int interval_idx; uint8_t counter; HidTransport transport; } HidMouseJigglerModel; +const int intervals[6] = {500, 2000, 5000, 10000, 30000, 60000}; + static void hid_mouse_jiggler_draw_callback(Canvas* canvas, void* context) { furi_assert(context); HidMouseJigglerModel* model = context; @@ -33,29 +38,39 @@ static void hid_mouse_jiggler_draw_callback(Canvas* canvas, void* context) { } canvas_set_font(canvas, FontPrimary); - elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Mouse Jiggler"); + elements_multiline_text_aligned(canvas, 17, 2, AlignLeft, AlignTop, "Mouse Jiggler"); + + // Timeout + elements_multiline_text(canvas, AlignLeft, 26, "Interval (ms):"); + canvas_set_font(canvas, FontSecondary); + if(model->interval_idx != 0) canvas_draw_icon(canvas, 74, 19, &I_ButtonLeft_4x7); + if(model->interval_idx != LENGTH(intervals) - 1) + canvas_draw_icon(canvas, 80, 19, &I_ButtonRight_4x7); + FuriString* interval_str = furi_string_alloc_printf("%d", intervals[model->interval_idx]); + elements_multiline_text(canvas, 91, 26, furi_string_get_cstr(interval_str)); + furi_string_free(interval_str); canvas_set_font(canvas, FontPrimary); - elements_multiline_text(canvas, AlignLeft, 35, "Press Start\nto jiggle"); + elements_multiline_text(canvas, AlignLeft, 40, "Press Start\nto jiggle"); canvas_set_font(canvas, FontSecondary); // Ok - canvas_draw_icon(canvas, 63, 25, &I_Space_65x18); + canvas_draw_icon(canvas, 63, 30, &I_Space_65x18); if(model->running) { - elements_slightly_rounded_box(canvas, 66, 27, 60, 13); + elements_slightly_rounded_box(canvas, 66, 32, 60, 13); canvas_set_color(canvas, ColorWhite); } - canvas_draw_icon(canvas, 74, 29, &I_Ok_btn_9x9); + canvas_draw_icon(canvas, 74, 34, &I_Ok_btn_9x9); if(model->running) { - elements_multiline_text_aligned(canvas, 91, 36, AlignLeft, AlignBottom, "Stop"); + elements_multiline_text_aligned(canvas, 91, 41, AlignLeft, AlignBottom, "Stop"); } else { - elements_multiline_text_aligned(canvas, 91, 36, AlignLeft, AlignBottom, "Start"); + elements_multiline_text_aligned(canvas, 91, 41, AlignLeft, AlignBottom, "Start"); } canvas_set_color(canvas, ColorBlack); // Back - canvas_draw_icon(canvas, 74, 49, &I_Pin_back_arrow_10x8); - elements_multiline_text_aligned(canvas, 91, 57, AlignLeft, AlignBottom, "Quit"); + canvas_draw_icon(canvas, 74, 54, &I_Pin_back_arrow_10x8); + elements_multiline_text_aligned(canvas, 91, 62, AlignLeft, AlignBottom, "Quit"); } static void hid_mouse_jiggler_timer_callback(void* context) { @@ -76,13 +91,6 @@ static void hid_mouse_jiggler_timer_callback(void* context) { false); } -static void hid_mouse_jiggler_enter_callback(void* context) { - furi_assert(context); - HidMouseJiggler* hid_mouse_jiggler = context; - - furi_timer_start(hid_mouse_jiggler->timer, 500); -} - static void hid_mouse_jiggler_exit_callback(void* context) { furi_assert(context); HidMouseJiggler* hid_mouse_jiggler = context; @@ -95,14 +103,30 @@ static bool hid_mouse_jiggler_input_callback(InputEvent* event, void* context) { bool consumed = false; - if(event->key == InputKeyOk) { - with_view_model( - hid_mouse_jiggler->view, - HidMouseJigglerModel * model, - { model->running = !model->running; }, - true); - consumed = true; - } + with_view_model( + hid_mouse_jiggler->view, + HidMouseJigglerModel * model, + { + if(event->type == InputTypePress && event->key == InputKeyOk) { + model->running = !model->running; + if(model->running) { + furi_timer_stop(hid_mouse_jiggler->timer); + furi_timer_start(hid_mouse_jiggler->timer, intervals[model->interval_idx]); + }; + consumed = true; + } + if(event->type == InputTypePress && event->key == InputKeyRight && !model->running && + model->interval_idx < LENGTH(intervals) - 1) { + model->interval_idx++; + consumed = true; + } + if(event->type == InputTypePress && event->key == InputKeyLeft && !model->running && + model->interval_idx > 0) { + model->interval_idx--; + consumed = true; + } + }, + true); return consumed; } @@ -116,7 +140,6 @@ HidMouseJiggler* hid_mouse_jiggler_alloc(Hid* hid) { hid_mouse_jiggler->view, ViewModelTypeLocking, sizeof(HidMouseJigglerModel)); view_set_draw_callback(hid_mouse_jiggler->view, hid_mouse_jiggler_draw_callback); view_set_input_callback(hid_mouse_jiggler->view, hid_mouse_jiggler_input_callback); - view_set_enter_callback(hid_mouse_jiggler->view, hid_mouse_jiggler_enter_callback); view_set_exit_callback(hid_mouse_jiggler->view, hid_mouse_jiggler_exit_callback); hid_mouse_jiggler->hid = hid; @@ -127,7 +150,10 @@ HidMouseJiggler* hid_mouse_jiggler_alloc(Hid* hid) { with_view_model( hid_mouse_jiggler->view, HidMouseJigglerModel * model, - { model->transport = hid->transport; }, + { + model->transport = hid->transport; + model->interval_idx = 2; + }, true); return hid_mouse_jiggler; diff --git a/applications/plugins/htu21d_temp_sensor/Readme.md b/applications/plugins/htu21d_temp_sensor/Readme.md deleted file mode 100644 index 45c332306..000000000 --- a/applications/plugins/htu21d_temp_sensor/Readme.md +++ /dev/null @@ -1,65 +0,0 @@ -[Original link](https://github.com/Mywk/FlipperTemperatureSensor) - -# Flipper Temperature Sensor - -## Supported sensors - -> HTU2xD, SHT2x, SI702x, SI700x, SI701x, AM2320 - -## What is this? - -A small app for the [Flipper Zero](https://flipperzero.one) that reads the [I2C](https://en.wikipedia.org/wiki/I%C2%B2C) signal from a few temperature sensors and displays the current temperature and humidity. - -I'm using a [Sparkfun HTU21D sensor](https://learn.sparkfun.com/tutorials/htu21d-humidity-sensor-hookup-guide), also tested with a clone and with the Si7021 variant. - -![Flipper Temperature Sensor](images/Flipper.png) - -![App](images/App.png) - -
- -# How to Connect the HTU21D sensor -![Connection](images/Connection.png) - - -# How to install - -If you have the FAP loader, just copy the fap file from the Releases into your Flipper apps folder and you should be able to launch it from the menu. - -If you don't have the FAP loader you will have to bake this application together with your firmware (aka compile it all together). - -# FAQ - -## The app says the sensor is not found! - -1- Are the four connectors correctly soldered? - -2- Are the SCL and SDA connections correct? Re-check the "How to Connect the sensor" above. - -3- For the HTU21D, on the sensor board, there should be three contacts in the center, for it to work correctly they must be soldered together (basically drop a blob of solder to connect the three of them). Without the solder it looks like this: - -![Sensor](images/Sensor.png) - -## Which Flipper versions was this app tested on? - -Version 1.2 -- RM11221439-0.71.2 -- unlshd-015 -- 0.71.1 - -Version 1.1 -- RM10302252-0.70.1 -- unlshd-012 -- 0.68.2-1007-RM -- 0.68.1 -- 0.67.2 - -## I can't build the app together with the firmware? - -In the *application.fam*, don't forget to change the apptype, it should not be EXTERNAL but APP. - -# How to compile - -Place the temperature_sensor folder in the applications_user folder and compile using FBT. - -Please refer to the [Flipper Build Tool documentation](https://github.com/flipperdevices/flipperzero-firmware/blob/dev/documentation/fbt.md). \ No newline at end of file diff --git a/applications/plugins/htu21d_temp_sensor/application.fam b/applications/plugins/htu21d_temp_sensor/application.fam deleted file mode 100644 index 724f55cd5..000000000 --- a/applications/plugins/htu21d_temp_sensor/application.fam +++ /dev/null @@ -1,14 +0,0 @@ -App( - appid="HTU_Temperature_Sensor", - name="[HTU/+] Temperature Sensor", - apptype=FlipperAppType.EXTERNAL, - entry_point="temperature_sensor_app", - cdefines=["APP_TEMPERATURE_SENSOR"], - requires=[ - "gui", - ], - stack_size=2 * 1024, - order=90, - fap_icon="temperature_sensor.png", - fap_category="GPIO", -) diff --git a/applications/plugins/htu21d_temp_sensor/docs/App.png b/applications/plugins/htu21d_temp_sensor/docs/App.png deleted file mode 100644 index a0373bdbd..000000000 Binary files a/applications/plugins/htu21d_temp_sensor/docs/App.png and /dev/null differ diff --git a/applications/plugins/htu21d_temp_sensor/docs/Connection.png b/applications/plugins/htu21d_temp_sensor/docs/Connection.png deleted file mode 100644 index b38f5c250..000000000 Binary files a/applications/plugins/htu21d_temp_sensor/docs/Connection.png and /dev/null differ diff --git a/applications/plugins/htu21d_temp_sensor/docs/Flipper.png b/applications/plugins/htu21d_temp_sensor/docs/Flipper.png deleted file mode 100644 index c85319593..000000000 Binary files a/applications/plugins/htu21d_temp_sensor/docs/Flipper.png and /dev/null differ diff --git a/applications/plugins/htu21d_temp_sensor/temperature_sensor.c b/applications/plugins/htu21d_temp_sensor/temperature_sensor.c deleted file mode 100644 index b04e40a7d..000000000 --- a/applications/plugins/htu21d_temp_sensor/temperature_sensor.c +++ /dev/null @@ -1,387 +0,0 @@ -/* Flipper App to read the values from a HTU2XD, SHT2X, SI702X, SI700X, SI701X or AM2320 Sensor */ -/* Created by Mywk - https://github.com/Mywk - https://mywk.net */ -#include -#include -#include -#include - -#include -#include - -#include - -#include - -#define TS_DEFAULT_VALUE 0xFFFF - -#define TS_AVAILABLE_SENSORS 2 - -// HTU2XD, SHT2X, SI702X, SI700X address -#define HTU2XD_SHT2X_SI702X_SI700X_ADDRESS (0x40 << 1) -// SI701X ADDRESS -#define SI701X_ADDRESS (0x41 << 1) - -// HTU2XD, SHT2X, SI702X, SI700X commands -#define HTU21D_CMD_TEMPERATURE 0xE3 -#define HTU21D_CMD_HUMIDITY 0xE5 - -// AM2320 address -#define AM2320_ADDRESS (0x5C << 1) - -// Used for the temperature and humidity buffers -#define DATA_BUFFER_SIZE 8 - -// External I2C BUS -#define I2C_BUS &furi_hal_i2c_handle_external - -// Typedef enums to make everything easier to read - -typedef enum { TSSCmdNone, TSSCmdTemperature, TSSCmdHumidity } TSSCmdType; - -typedef enum { - TSSInitializing, - TSSNoSensor, - TSSPendingUpdate, -} TSStatus; - -typedef enum { - TSEventTypeTick, - TSEventTypeInput, -} TSEventType; - -typedef struct { - TSEventType type; - InputEvent input; -} TSEvent; - -// Possible return values for sensor_cmd -typedef enum { - TSCmdRet_Error, - TSCmdRet_HTU2XD_SHT2X_SI702X_SI700X, - TSCmdRet_SI701X, - TSCmdRet_AM2320, -} TSCmdRet; - -// External NotificationSequence RGB -extern const NotificationSequence sequence_blink_red_100; -extern const NotificationSequence sequence_blink_green_100; -extern const NotificationSequence sequence_blink_blue_100; - -// Current status of the temperature sensor app -static TSStatus temperature_sensor_current_status = TSSInitializing; - -// We keep track of the last cmd return -static TSCmdRet temperature_sensor_last_cmd_ret = TSCmdRet_Error; - -// Temperature and Humidity data buffers, ready to print -char ts_data_buffer_temperature_c[DATA_BUFFER_SIZE]; -char ts_data_buffer_temperature_f[DATA_BUFFER_SIZE]; -char ts_data_buffer_relative_humidity[DATA_BUFFER_SIZE]; -char ts_data_buffer_absolute_humidity[DATA_BUFFER_SIZE]; - -// -// Executes an I2C cmd (trx) -// -// -// CRC -// -// -// true if fetch was successful, false otherwise -// -static TSCmdRet temperature_sensor_cmd(TSSCmdType cmd, uint8_t* buffer) { - uint32_t timeout = furi_ms_to_ticks(100); - TSCmdRet ret = TSCmdRet_Error; - - // Aquire I2C and check if device is ready, then release - furi_hal_i2c_acquire(I2C_BUS); - - // Check if HTU2XD, SHT2X, SI702X, SI700X sensor is available - uint8_t isAddress40 = - furi_hal_i2c_is_device_ready(I2C_BUS, HTU2XD_SHT2X_SI702X_SI700X_ADDRESS, timeout); - uint8_t isAddress41 = 0; - - // Check if SI701X sensor is available if necessary - if(!isAddress40) isAddress41 = furi_hal_i2c_is_device_ready(I2C_BUS, SI701X_ADDRESS, timeout); - - if(isAddress40 || isAddress41) { - uint8_t address = isAddress40 ? HTU2XD_SHT2X_SI702X_SI700X_ADDRESS : SI701X_ADDRESS; - - // Better safe than sorry delay - furi_delay_ms(15); - - // Extra delay for the SI70XX - if(isAddress41) furi_delay_ms(50); - - // Transmit either the temperature or the humidity command depending on TSSCmdType - uint8_t c = (cmd == TSSCmdTemperature) ? HTU21D_CMD_TEMPERATURE : HTU21D_CMD_HUMIDITY; - if(furi_hal_i2c_tx(I2C_BUS, address, &c, 1, timeout)) { - // Receive data (2 bytes) - if(furi_hal_i2c_rx(I2C_BUS, address, buffer, 2, timeout + 50)) - ret = isAddress40 ? TSCmdRet_HTU2XD_SHT2X_SI702X_SI700X : TSCmdRet_SI701X; - } - } else { - // The AM2320 goes to sleep after a period of inactivity, wake it up (check AM2320 datasheet for more info) - furi_hal_i2c_is_device_ready(I2C_BUS, AM2320_ADDRESS, timeout); - furi_delay_ms(30); - - // Check if it's really available - if(furi_hal_i2c_is_device_ready(I2C_BUS, AM2320_ADDRESS, timeout)) { - // {Address, Register, Len} - const uint8_t request[3] = {0x03, 0x00, 0x04}; - - if(furi_hal_i2c_tx(I2C_BUS, AM2320_ADDRESS, request, 3, timeout)) { - // 6 bytes - usually 8 but we currently don't check the CRC - if(furi_hal_i2c_rx(I2C_BUS, (uint8_t)AM2320_ADDRESS, buffer, 6, timeout)) - ret = TSCmdRet_AM2320; - } - } - } - - furi_hal_i2c_release(I2C_BUS); - - temperature_sensor_last_cmd_ret = ret; - return ret; -} - -// -// Fetches temperature and humidity from sensor -// -// -// temperature in C -// humidity in relative humidity -// -// -// Temperature and humidity must be preallocated -// -// -// CRC -// -// -// true if fetch was successful, false otherwise -// -static bool temperature_sensor_fetch_data(double* temperature, double* humidity) { - bool ret = false; - - uint16_t adc_raw; - - uint8_t buffer[DATA_BUFFER_SIZE] = {0x00}; - - // Check if the sensor is the HTU21D by attempting to fetch the temperature - TSCmdRet cmdRet = temperature_sensor_cmd(TSSCmdTemperature, buffer); - if(cmdRet == TSCmdRet_HTU2XD_SHT2X_SI702X_SI700X || cmdRet == TSCmdRet_SI701X) { - // Calculate temperature - adc_raw = ((uint16_t)(buffer[0] << 8) | (buffer[1])); - *temperature = (float)(adc_raw * 175.72 / 65536.00) - 46.85; - - // Fetch humidity - if(temperature_sensor_cmd(TSSCmdHumidity, buffer)) { - // Calculate humidity - adc_raw = ((uint16_t)(buffer[0] << 8) | (buffer[1])); - *humidity = (float)(adc_raw * 125.0 / 65536.00) - 6.0; - - ret = true; - } - } else if(cmdRet == TSCmdRet_AM2320) { - // The AM2320 returns all the data immediately so we just process it all - // Note: CRC isn't currently present in the buffer - - // Temperature - float temp = (((buffer[4] & 0x7F) << 8) + buffer[5]) / 10; - *temperature = ((buffer[4] & 0x80) >> 7) == 1 ? temp * (-1) : temp; - - // Humidity - temp = ((buffer[2] << 8) + buffer[3]) / 10; - *humidity = temp; - - ret = true; - } - - return ret; -} - -// -// Draw callback -// -static void temperature_sensor_draw_callback(Canvas* canvas, void* ctx) { - UNUSED(ctx); - - canvas_clear(canvas); - canvas_set_font(canvas, FontPrimary); - - // Update title accordingly (this could be improved by checking the hardware id) - switch(temperature_sensor_last_cmd_ret) { - case TSCmdRet_Error: - canvas_draw_str(canvas, 2, 10, "Temperature Sensor"); - break; - - case TSCmdRet_HTU2XD_SHT2X_SI702X_SI700X: - canvas_draw_str(canvas, 2, 10, "HTU/SHT/SI70 Sensor"); - break; - - case TSCmdRet_SI701X: - canvas_draw_str(canvas, 2, 10, "SI701X Sensor"); - break; - - case TSCmdRet_AM2320: - canvas_draw_str(canvas, 2, 10, "AM2320 Sensor"); - break; - - default: - break; - } - - canvas_set_font(canvas, FontSecondary); - canvas_draw_str(canvas, 2, 62, "Press back to exit."); - - switch(temperature_sensor_current_status) { - case TSSInitializing: - canvas_draw_str(canvas, 2, 30, "Initializing.."); - break; - case TSSNoSensor: - canvas_draw_str(canvas, 2, 30, "No sensor found!"); - break; - case TSSPendingUpdate: { - canvas_draw_str(canvas, 3, 24, "Temperature"); - canvas_draw_str(canvas, 68, 24, "Humidity"); - - // Draw vertical lines - canvas_draw_line(canvas, 61, 16, 61, 50); - canvas_draw_line(canvas, 62, 16, 62, 50); - - // Draw horizontal line - canvas_draw_line(canvas, 2, 27, 122, 27); - - // Draw temperature and humidity values - canvas_draw_str(canvas, 8, 38, ts_data_buffer_temperature_c); - canvas_draw_str(canvas, 42, 38, "C"); - canvas_draw_str(canvas, 8, 48, ts_data_buffer_temperature_f); - canvas_draw_str(canvas, 42, 48, "F"); - canvas_draw_str(canvas, 68, 38, ts_data_buffer_relative_humidity); - canvas_draw_str(canvas, 100, 38, "%"); - canvas_draw_str(canvas, 68, 48, ts_data_buffer_absolute_humidity); - canvas_draw_str(canvas, 100, 48, "g/m3"); - } break; - default: - break; - } -} - -// -// Input callback -// -static void temperature_sensor_input_callback(InputEvent* input_event, void* ctx) { - furi_assert(ctx); - FuriMessageQueue* event_queue = ctx; - - TSEvent event = {.type = TSEventTypeInput, .input = *input_event}; - furi_message_queue_put(event_queue, &event, FuriWaitForever); -} - -// -// Timer callback -// -static void temperature_sensor_timer_callback(FuriMessageQueue* event_queue) { - furi_assert(event_queue); - - TSEvent event = {.type = TSEventTypeTick}; - furi_message_queue_put(event_queue, &event, 0); -} - -// -// App entry point -// -int32_t temperature_sensor_app(void* p) { - UNUSED(p); - - // Declare our variables and assign variables a default value - TSEvent tsEvent; - bool sensorFound = false; - double celsius, fahrenheit, rel_humidity, abs_humidity = TS_DEFAULT_VALUE; - - // Used for absolute humidity calculation - double vapour_pressure = 0; - - FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(TSEvent)); - - // Register callbacks - ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, temperature_sensor_draw_callback, NULL); - view_port_input_callback_set(view_port, temperature_sensor_input_callback, event_queue); - - // Create timer and register its callback - FuriTimer* timer = - furi_timer_alloc(temperature_sensor_timer_callback, FuriTimerTypePeriodic, event_queue); - furi_timer_start(timer, furi_kernel_get_tick_frequency()); - - // Register viewport - Gui* gui = furi_record_open(RECORD_GUI); - gui_add_view_port(gui, view_port, GuiLayerFullscreen); - - // Used to notify the user by blinking red (error) or blue (fetch successful) - NotificationApp* notifications = furi_record_open(RECORD_NOTIFICATION); - - while(1) { - furi_check(furi_message_queue_get(event_queue, &tsEvent, FuriWaitForever) == FuriStatusOk); - - // Handle events - if(tsEvent.type == TSEventTypeInput) { - // Exit on back key - if(tsEvent.input.key == - InputKeyBack) // We dont check for type here, we can check the type of keypress like: (event.input.type == InputTypeShort) - break; - } else if(tsEvent.type == TSEventTypeTick) { - // Update sensor data - // Fetch data and set the sensor current status accordingly - sensorFound = temperature_sensor_fetch_data(&celsius, &rel_humidity); - temperature_sensor_current_status = (sensorFound ? TSSPendingUpdate : TSSNoSensor); - - if(sensorFound) { - // Blink blue - notification_message(notifications, &sequence_blink_blue_100); - - if(celsius != TS_DEFAULT_VALUE && rel_humidity != TS_DEFAULT_VALUE) { - // Convert celsius to fahrenheit - fahrenheit = (celsius * 9 / 5) + 32; - - // Calculate absolute humidity - For more info refer to https://github.com/Mywk/FlipperTemperatureSensor/issues/1 - // Calculate saturation vapour pressure first - vapour_pressure = - (double)6.11 * - pow(10, (double)(((double)7.5 * celsius) / ((double)237.3 + celsius))); - // Then the vapour pressure in Pa - vapour_pressure = vapour_pressure * rel_humidity; - // Calculate absolute humidity - abs_humidity = - (double)2.16679 * (double)(vapour_pressure / ((double)273.15 + celsius)); - - // Fill our buffers here, not on the canvas draw callback - snprintf(ts_data_buffer_temperature_c, DATA_BUFFER_SIZE, "%.2f", celsius); - snprintf(ts_data_buffer_temperature_f, DATA_BUFFER_SIZE, "%.2f", fahrenheit); - snprintf( - ts_data_buffer_relative_humidity, DATA_BUFFER_SIZE, "%.2f", rel_humidity); - snprintf( - ts_data_buffer_absolute_humidity, DATA_BUFFER_SIZE, "%.2f", abs_humidity); - } - } else { - // Reset our variables to their default values - celsius = fahrenheit = rel_humidity = abs_humidity = TS_DEFAULT_VALUE; - - // Blink red - notification_message(notifications, &sequence_blink_red_100); - } - } - - furi_delay_ms(!sensorFound ? 100 : 500); - } - - // Dobby is freee (free our variables, Flipper will crash if we don't do this!) - furi_timer_free(timer); - gui_remove_view_port(gui, view_port); - view_port_free(view_port); - furi_message_queue_free(event_queue); - - furi_record_close(RECORD_NOTIFICATION); - furi_record_close(RECORD_GUI); - - return 0; -} \ No newline at end of file diff --git a/applications/plugins/htu21d_temp_sensor/temperature_sensor.png b/applications/plugins/htu21d_temp_sensor/temperature_sensor.png deleted file mode 100644 index b6fe6d7fe..000000000 Binary files a/applications/plugins/htu21d_temp_sensor/temperature_sensor.png and /dev/null differ diff --git a/applications/plugins/ibtn_fuzzer/README.md b/applications/plugins/ibtn_fuzzer/README.md deleted file mode 100644 index 6b93488c8..000000000 --- a/applications/plugins/ibtn_fuzzer/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# iButton Fuzzer -Plugin for Flipper Zero - -Made for [Unleashed](https://github.com/DarkFlippers/unleashed-firmware) firmware, but will work on original firmware too - -Supported protocols: -- DS1990 -- Metakom -- Cyfral \ No newline at end of file diff --git a/applications/plugins/ibtn_fuzzer/application.fam b/applications/plugins/ibtn_fuzzer/application.fam index 93a9bad23..b27f47ba9 100644 --- a/applications/plugins/ibtn_fuzzer/application.fam +++ b/applications/plugins/ibtn_fuzzer/application.fam @@ -1,5 +1,5 @@ App( - appid="IBtn_Fuzzer", + appid="iBtn_Fuzzer", name="iButton Fuzzer", apptype=FlipperAppType.EXTERNAL, entry_point="ibtnfuzzer_start", diff --git a/applications/plugins/ibtn_fuzzer/ibtnfuzzer.c b/applications/plugins/ibtn_fuzzer/ibtnfuzzer.c index f02da3b82..c5f2a5f7c 100644 --- a/applications/plugins/ibtn_fuzzer/ibtnfuzzer.c +++ b/applications/plugins/ibtn_fuzzer/ibtnfuzzer.c @@ -9,11 +9,9 @@ #define IBTNFUZZER_APP_FOLDER "/ext/ibtnfuzzer" static void ibtnfuzzer_draw_callback(Canvas* const canvas, void* ctx) { - iBtnFuzzerState* ibtnfuzzer_state = (iBtnFuzzerState*)acquire_mutex((ValueMutex*)ctx, 100); - - if(ibtnfuzzer_state == NULL) { - return; - } + furi_assert(ctx); + iBtnFuzzerState* ibtnfuzzer_state = ctx; + furi_mutex_acquire(ibtnfuzzer_state->mutex, FuriWaitForever); // Draw correct Canvas switch(ibtnfuzzer_state->current_scene) { @@ -35,7 +33,7 @@ static void ibtnfuzzer_draw_callback(Canvas* const canvas, void* ctx) { break; } - release_mutex((ValueMutex*)ctx, ibtnfuzzer_state); + furi_mutex_release(ibtnfuzzer_state->mutex); } void ibtnfuzzer_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { @@ -120,11 +118,9 @@ int32_t ibtnfuzzer_start(void* p) { FURI_LOG_I(TAG, "Initializing input"); FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(iBtnFuzzerEvent)); iBtnFuzzerState* ibtnfuzzer_state = ibtnfuzzer_alloc(); - ValueMutex ibtnfuzzer_state_mutex; - // Mutex - FURI_LOG_I(TAG, "Initializing ibtnfuzzer mutex"); - if(!init_mutex(&ibtnfuzzer_state_mutex, ibtnfuzzer_state, sizeof(iBtnFuzzerState))) { + ibtnfuzzer_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!ibtnfuzzer_state->mutex) { FURI_LOG_E(TAG, "cannot create mutex\r\n"); furi_message_queue_free(event_queue); furi_record_close(RECORD_NOTIFICATION); @@ -141,7 +137,7 @@ int32_t ibtnfuzzer_start(void* p) { // Configure view port FURI_LOG_I(TAG, "Initializing viewport"); ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, ibtnfuzzer_draw_callback, &ibtnfuzzer_state_mutex); + view_port_draw_callback_set(view_port, ibtnfuzzer_draw_callback, ibtnfuzzer_state); view_port_input_callback_set(view_port, ibtnfuzzer_input_callback, event_queue); // Configure timer @@ -160,6 +156,7 @@ int32_t ibtnfuzzer_start(void* p) { while(ibtnfuzzer_state->is_running) { // Get next event FuriStatus event_status = furi_message_queue_get(event_queue, &event, 25); + //furi_mutex_acquire(ibtnfuzzer_state->mutex, FuriWaitForever); if(event_status == FuriStatusOk) { if(event.evt_type == EventTypeKey) { //Handle event key @@ -250,6 +247,7 @@ int32_t ibtnfuzzer_start(void* p) { view_port_update(view_port); } } + //furi_mutex_release(ibtnfuzzer_state->mutex); } // Cleanup @@ -262,6 +260,7 @@ int32_t ibtnfuzzer_start(void* p) { furi_message_queue_free(event_queue); furi_record_close(RECORD_GUI); furi_record_close(RECORD_NOTIFICATION); + furi_mutex_free(ibtnfuzzer_state->mutex); ibtnfuzzer_free(ibtnfuzzer_state); return 0; diff --git a/applications/plugins/ibtn_fuzzer/ibtnfuzzer.h b/applications/plugins/ibtn_fuzzer/ibtnfuzzer.h index 1db527f65..91a9c6b0c 100644 --- a/applications/plugins/ibtn_fuzzer/ibtnfuzzer.h +++ b/applications/plugins/ibtn_fuzzer/ibtnfuzzer.h @@ -15,7 +15,7 @@ #include #include -#include +#include #include #include @@ -56,6 +56,7 @@ typedef struct { // STRUCTS typedef struct { + FuriMutex* mutex; bool is_running; bool is_attacking; iBtnFuzzerScene current_scene; @@ -78,7 +79,8 @@ typedef struct { uint8_t key_index; iButtonWorker* worker; iButtonKey* key; - iButtonKeyType keytype; + iButtonProtocolId keytype; + iButtonProtocols* protocols; bool workr_rund; bool enter_rerun; bool attack_stop_called; diff --git a/applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.c b/applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.c index e45035d6b..1cab8b04e 100644 --- a/applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.c +++ b/applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.c @@ -72,14 +72,15 @@ void ibtnfuzzer_scene_run_attack_on_enter(iBtnFuzzerState* context) { context->time_between_cards = 8; context->attack_step = 0; context->attack_stop_called = false; - context->key = ibutton_key_alloc(); - context->worker = ibutton_worker_alloc(); + context->protocols = ibutton_protocols_alloc(); + context->key = ibutton_key_alloc(ibutton_protocols_get_max_data_size(context->protocols)); + context->worker = ibutton_worker_alloc(context->protocols); if(context->proto == Metakom) { - context->keytype = iButtonKeyMetakom; + context->keytype = ibutton_protocols_get_id_by_name(context->protocols, "Metakom"); } else if(context->proto == Cyfral) { - context->keytype = iButtonKeyCyfral; + context->keytype = ibutton_protocols_get_id_by_name(context->protocols, "Cyfral"); } else { - context->keytype = iButtonKeyDS1990; + context->keytype = ibutton_protocols_get_id_by_name(context->protocols, "DS1990"); } context->workr_rund = false; } @@ -90,8 +91,9 @@ void ibtnfuzzer_scene_run_attack_on_exit(iBtnFuzzerState* context) { ibutton_worker_stop_thread(context->worker); context->workr_rund = false; } - ibutton_worker_free(context->worker); ibutton_key_free(context->key); + ibutton_worker_free(context->worker); + ibutton_protocols_free(context->protocols); notification_message(context->notify, &sequence_blink_stop); } @@ -99,9 +101,14 @@ void ibtnfuzzer_scene_run_attack_on_tick(iBtnFuzzerState* context) { if(context->is_attacking) { if(1 == counter) { ibutton_worker_start_thread(context->worker); - ibutton_key_set_type(context->key, context->keytype); - ibutton_key_set_data( - context->key, context->payload, ibutton_key_get_size_by_type(context->keytype)); + ibutton_key_set_protocol_id(context->key, context->keytype); + iButtonEditableData data; + ibutton_protocols_get_editable_data(context->protocols, context->key, &data); + data.size = sizeof(context->payload); + for(size_t i = 0; i < data.size; i++) { + data.ptr[i] = context->payload[i]; + } + ibutton_worker_emulate_start(context->worker, context->key); context->workr_rund = true; } else if(0 == counter) { diff --git a/applications/plugins/ifttt/application.fam b/applications/plugins/ifttt/application.fam index 75e5beb67..6a3c4986e 100644 --- a/applications/plugins/ifttt/application.fam +++ b/applications/plugins/ifttt/application.fam @@ -10,5 +10,5 @@ App( stack_size=2 * 1024, order=20, fap_icon="icon.png", - fap_category="GPIO", + fap_category="WiFi", ) diff --git a/applications/plugins/lightmeter/.clang-format b/applications/plugins/lightmeter/.clang-format deleted file mode 100644 index 4b76f7fa4..000000000 --- a/applications/plugins/lightmeter/.clang-format +++ /dev/null @@ -1,191 +0,0 @@ ---- -Language: Cpp -AccessModifierOffset: -4 -AlignAfterOpenBracket: AlwaysBreak -AlignArrayOfStructures: None -AlignConsecutiveMacros: None -AlignConsecutiveAssignments: None -AlignConsecutiveBitFields: None -AlignConsecutiveDeclarations: None -AlignEscapedNewlines: Left -AlignOperands: Align -AlignTrailingComments: false -AllowAllArgumentsOnNextLine: true -AllowAllParametersOfDeclarationOnNextLine: false -AllowShortEnumsOnASingleLine: true -AllowShortBlocksOnASingleLine: Never -AllowShortCaseLabelsOnASingleLine: false -AllowShortFunctionsOnASingleLine: None -AllowShortLambdasOnASingleLine: All -AllowShortIfStatementsOnASingleLine: WithoutElse -AllowShortLoopsOnASingleLine: true -AlwaysBreakAfterDefinitionReturnType: None -AlwaysBreakAfterReturnType: None -AlwaysBreakBeforeMultilineStrings: false -AlwaysBreakTemplateDeclarations: Yes -AttributeMacros: - - __capability -BinPackArguments: false -BinPackParameters: false -BraceWrapping: - AfterCaseLabel: false - AfterClass: false - AfterControlStatement: Never - AfterEnum: false - AfterFunction: false - AfterNamespace: false - AfterObjCDeclaration: false - AfterStruct: false - AfterUnion: false - AfterExternBlock: false - BeforeCatch: false - BeforeElse: false - BeforeLambdaBody: false - BeforeWhile: false - IndentBraces: false - SplitEmptyFunction: true - SplitEmptyRecord: true - SplitEmptyNamespace: true -BreakBeforeBinaryOperators: None -BreakBeforeConceptDeclarations: true -BreakBeforeBraces: Attach -BreakBeforeInheritanceComma: false -BreakInheritanceList: BeforeColon -BreakBeforeTernaryOperators: false -BreakConstructorInitializersBeforeComma: false -BreakConstructorInitializers: BeforeComma -BreakAfterJavaFieldAnnotations: false -BreakStringLiterals: false -ColumnLimit: 99 -CommentPragmas: '^ IWYU pragma:' -QualifierAlignment: Leave -CompactNamespaces: false -ConstructorInitializerIndentWidth: 4 -ContinuationIndentWidth: 4 -Cpp11BracedListStyle: true -DeriveLineEnding: true -DerivePointerAlignment: false -DisableFormat: false -EmptyLineAfterAccessModifier: Never -EmptyLineBeforeAccessModifier: LogicalBlock -ExperimentalAutoDetectBinPacking: false -PackConstructorInitializers: BinPack -BasedOnStyle: '' -ConstructorInitializerAllOnOneLineOrOnePerLine: false -AllowAllConstructorInitializersOnNextLine: true -FixNamespaceComments: false -ForEachMacros: - - foreach - - Q_FOREACH - - BOOST_FOREACH -IfMacros: - - KJ_IF_MAYBE -IncludeBlocks: Preserve -IncludeCategories: - - Regex: '.*' - Priority: 1 - SortPriority: 0 - CaseSensitive: false - - Regex: '^(<|"(gtest|gmock|isl|json)/)' - Priority: 3 - SortPriority: 0 - CaseSensitive: false - - Regex: '.*' - Priority: 1 - SortPriority: 0 - CaseSensitive: false -IncludeIsMainRegex: '(Test)?$' -IncludeIsMainSourceRegex: '' -IndentAccessModifiers: false -IndentCaseLabels: false -IndentCaseBlocks: false -IndentGotoLabels: true -IndentPPDirectives: None -IndentExternBlock: AfterExternBlock -IndentRequires: false -IndentWidth: 4 -IndentWrappedFunctionNames: true -InsertTrailingCommas: None -JavaScriptQuotes: Leave -JavaScriptWrapImports: true -KeepEmptyLinesAtTheStartOfBlocks: false -LambdaBodyIndentation: Signature -MacroBlockBegin: '' -MacroBlockEnd: '' -MaxEmptyLinesToKeep: 1 -NamespaceIndentation: None -ObjCBinPackProtocolList: Auto -ObjCBlockIndentWidth: 4 -ObjCBreakBeforeNestedBlockParam: true -ObjCSpaceAfterProperty: true -ObjCSpaceBeforeProtocolList: true -PenaltyBreakAssignment: 10 -PenaltyBreakBeforeFirstCallParameter: 30 -PenaltyBreakComment: 10 -PenaltyBreakFirstLessLess: 0 -PenaltyBreakOpenParenthesis: 0 -PenaltyBreakString: 10 -PenaltyBreakTemplateDeclaration: 10 -PenaltyExcessCharacter: 100 -PenaltyReturnTypeOnItsOwnLine: 60 -PenaltyIndentedWhitespace: 0 -PointerAlignment: Left -PPIndentWidth: -1 -ReferenceAlignment: Pointer -ReflowComments: false -RemoveBracesLLVM: false -SeparateDefinitionBlocks: Leave -ShortNamespaceLines: 1 -SortIncludes: Never -SortJavaStaticImport: Before -SortUsingDeclarations: false -SpaceAfterCStyleCast: false -SpaceAfterLogicalNot: false -SpaceAfterTemplateKeyword: true -SpaceBeforeAssignmentOperators: true -SpaceBeforeCaseColon: false -SpaceBeforeCpp11BracedList: false -SpaceBeforeCtorInitializerColon: true -SpaceBeforeInheritanceColon: true -SpaceBeforeParens: Never -SpaceBeforeParensOptions: - AfterControlStatements: false - AfterForeachMacros: false - AfterFunctionDefinitionName: false - AfterFunctionDeclarationName: false - AfterIfMacros: false - AfterOverloadedOperator: false - BeforeNonEmptyParentheses: false -SpaceAroundPointerQualifiers: Default -SpaceBeforeRangeBasedForLoopColon: true -SpaceInEmptyBlock: false -SpaceInEmptyParentheses: false -SpacesBeforeTrailingComments: 1 -SpacesInAngles: Never -SpacesInConditionalStatement: false -SpacesInContainerLiterals: false -SpacesInCStyleCastParentheses: false -SpacesInLineCommentPrefix: - Minimum: 1 - Maximum: -1 -SpacesInParentheses: false -SpacesInSquareBrackets: false -SpaceBeforeSquareBrackets: false -BitFieldColonSpacing: Both -Standard: c++03 -StatementAttributeLikeMacros: - - Q_EMIT -StatementMacros: - - Q_UNUSED - - QT_REQUIRE_VERSION -TabWidth: 4 -UseCRLF: false -UseTab: Never -WhitespaceSensitiveMacros: - - STRINGIZE - - PP_STRINGIZE - - BOOST_PP_STRINGIZE - - NS_SWIFT_NAME - - CF_SWIFT_NAME -... - diff --git a/applications/plugins/lightmeter/README.md b/applications/plugins/lightmeter/README.md index dc6c6ffd5..d9c071e67 100644 --- a/applications/plugins/lightmeter/README.md +++ b/applications/plugins/lightmeter/README.md @@ -1,10 +1,9 @@ -# flipperzero-lightmeter +# flipperzero-lightmeter -[![Build FAP](https://github.com/oleksiikutuzov/flipperzero-lightmeter/actions/workflows/build.yml/badge.svg?branch=main)](https://github.com/oleksiikutuzov/flipperzero-lightmeter/actions/workflows/build.yml) +[Original link](https://github.com/oleksiikutuzov/flipperzero-lightmeter) - - + ## Wiring @@ -15,19 +14,6 @@ SCL -> C0 SDA -> C1 ``` -## Sensor module - - - -### If you want to build this module, you'll need (it's quite over-engineered, sorry :D) -1. [Module PCB](https://github.com/oleksiikutuzov/flipperzero-lightmeter/blob/main/module/module_v2_gerber.zip) -2. [Enclosure](https://github.com/oleksiikutuzov/flipperzero-lightmeter/blob/main/module/module_v2_enclosure.stl) -3. 4-pin female header -4. 10-pin male header -5. 2x M3 threaded inserts (max diameter 5.3 mm, max height 4 mm) -6. 2x M3x5 screws - - ## TODO - [ ] Save settings to sd card diff --git a/applications/plugins/lightmeter/application.fam b/applications/plugins/lightmeter/application.fam index b1de62641..8cd90ee26 100644 --- a/applications/plugins/lightmeter/application.fam +++ b/applications/plugins/lightmeter/application.fam @@ -1,5 +1,5 @@ App( - appid="BH1750_Lightmeter", + appid="lightmeter", name="[BH1750] Lightmeter", apptype=FlipperAppType.EXTERNAL, entry_point="lightmeter_app", @@ -9,7 +9,6 @@ App( ], stack_size=1 * 1024, order=90, - fap_version=(0, 7), fap_icon="lightmeter.png", fap_category="GPIO", fap_private_libs=[ @@ -22,7 +21,4 @@ App( ), ], fap_icon_assets="icons", - fap_description="Lightmeter app for photography based on BH1750 sensor", - fap_author="Oleksii Kutuzov", - fap_weburl="https://github.com/oleksiikutuzov/flipperzero-lightmeter", ) diff --git a/applications/plugins/lightmeter/gui/views/main_view.c b/applications/plugins/lightmeter/gui/views/main_view.c index fcbafbff4..8b3e2989f 100644 --- a/applications/plugins/lightmeter/gui/views/main_view.c +++ b/applications/plugins/lightmeter/gui/views/main_view.c @@ -62,11 +62,11 @@ const float aperture_numbers[] = { const float speed_numbers[] = { [SPEED_8000] = 1.0 / 8000, [SPEED_4000] = 1.0 / 4000, [SPEED_2000] = 1.0 / 2000, [SPEED_1000] = 1.0 / 1000, [SPEED_500] = 1.0 / 500, [SPEED_250] = 1.0 / 250, - [SPEED_125] = 1.0 / 125, [SPEED_60] = 1.0 / 60, [SPEED_30] = 1.0 / 30, - [SPEED_15] = 1.0 / 15, [SPEED_8] = 1.0 / 8, [SPEED_4] = 1.0 / 4, - [SPEED_2] = 1.0 / 2, [SPEED_1S] = 1.0, [SPEED_2S] = 2.0, - [SPEED_4S] = 4.0, [SPEED_8S] = 8.0, [SPEED_15S] = 15.0, - [SPEED_30S] = 30.0, + [SPEED_125] = 1.0 / 125, [SPEED_60] = 1.0 / 60, [SPEED_48] = 1.0 / 48, + [SPEED_30] = 1.0 / 30, [SPEED_15] = 1.0 / 15, [SPEED_8] = 1.0 / 8, + [SPEED_4] = 1.0 / 4, [SPEED_2] = 1.0 / 2, [SPEED_1S] = 1.0, + [SPEED_2S] = 2.0, [SPEED_4S] = 4.0, [SPEED_8S] = 8.0, + [SPEED_15S] = 15.0, [SPEED_30S] = 30.0, }; struct MainView { diff --git a/applications/plugins/lightmeter/gui/views/main_view.h b/applications/plugins/lightmeter/gui/views/main_view.h index 42d2d1877..038cd3065 100644 --- a/applications/plugins/lightmeter/gui/views/main_view.h +++ b/applications/plugins/lightmeter/gui/views/main_view.h @@ -1,7 +1,7 @@ #pragma once #include -#include "BH1750_Lightmeter_icons.h" +#include "lightmeter_icons.h" #include "../../lightmeter_config.h" typedef struct MainView MainView; diff --git a/applications/plugins/lightmeter/images/framed_gui_config.png b/applications/plugins/lightmeter/images/framed_gui_config.png deleted file mode 100644 index b87c3bd5c..000000000 Binary files a/applications/plugins/lightmeter/images/framed_gui_config.png and /dev/null differ diff --git a/applications/plugins/lightmeter/images/framed_gui_lux_meter.png b/applications/plugins/lightmeter/images/framed_gui_lux_meter.png deleted file mode 100644 index 6ab0cf191..000000000 Binary files a/applications/plugins/lightmeter/images/framed_gui_lux_meter.png and /dev/null differ diff --git a/applications/plugins/lightmeter/images/framed_gui_main.png b/applications/plugins/lightmeter/images/framed_gui_main.png deleted file mode 100644 index 23dc4a2cc..000000000 Binary files a/applications/plugins/lightmeter/images/framed_gui_main.png and /dev/null differ diff --git a/applications/plugins/lightmeter/images/gui_config.png b/applications/plugins/lightmeter/images/gui_config.png deleted file mode 100644 index 95fdcb167..000000000 Binary files a/applications/plugins/lightmeter/images/gui_config.png and /dev/null differ diff --git a/applications/plugins/lightmeter/images/gui_lux_meter.png b/applications/plugins/lightmeter/images/gui_lux_meter.png deleted file mode 100644 index f7159e671..000000000 Binary files a/applications/plugins/lightmeter/images/gui_lux_meter.png and /dev/null differ diff --git a/applications/plugins/lightmeter/images/gui_main.png b/applications/plugins/lightmeter/images/gui_main.png deleted file mode 100644 index ba4c3f75f..000000000 Binary files a/applications/plugins/lightmeter/images/gui_main.png and /dev/null differ diff --git a/applications/plugins/lightmeter/lib/BH1750/BH1750.c b/applications/plugins/lightmeter/lib/BH1750/BH1750.c index 28616e040..9e6a50758 100644 --- a/applications/plugins/lightmeter/lib/BH1750/BH1750.c +++ b/applications/plugins/lightmeter/lib/BH1750/BH1750.c @@ -15,6 +15,7 @@ BH1750_mode bh1750_mode = BH1750_DEFAULT_MODE; // Current sensor mode uint8_t bh1750_mt_reg = BH1750_DEFAULT_MTREG; // Current MT register value +uint8_t bh1750_addr = BH1750_ADDRESS; BH1750_STATUS bh1750_init() { if(BH1750_OK == bh1750_reset()) { @@ -25,12 +26,17 @@ BH1750_STATUS bh1750_init() { return BH1750_ERROR; } +BH1750_STATUS bh1750_init_with_addr(uint8_t addr) { + bh1750_addr = (addr << 1); + return bh1750_init(); +} + BH1750_STATUS bh1750_reset() { uint8_t command = 0x07; bool status; furi_hal_i2c_acquire(I2C_BUS); - status = furi_hal_i2c_tx(I2C_BUS, BH1750_ADDRESS, &command, 1, I2C_TIMEOUT); + status = furi_hal_i2c_tx(I2C_BUS, bh1750_addr, &command, 1, I2C_TIMEOUT); furi_hal_i2c_release(I2C_BUS); if(status) { @@ -45,7 +51,7 @@ BH1750_STATUS bh1750_set_power_state(uint8_t PowerOn) { bool status; furi_hal_i2c_acquire(I2C_BUS); - status = furi_hal_i2c_tx(I2C_BUS, BH1750_ADDRESS, &PowerOn, 1, I2C_TIMEOUT); + status = furi_hal_i2c_tx(I2C_BUS, bh1750_addr, &PowerOn, 1, I2C_TIMEOUT); furi_hal_i2c_release(I2C_BUS); if(status) { @@ -69,7 +75,7 @@ BH1750_STATUS bh1750_set_mode(BH1750_mode mode) { bh1750_mode = mode; furi_hal_i2c_acquire(I2C_BUS); - status = furi_hal_i2c_tx(I2C_BUS, BH1750_ADDRESS, &mode, 1, I2C_TIMEOUT); + status = furi_hal_i2c_tx(I2C_BUS, bh1750_addr, &mode, 1, I2C_TIMEOUT); furi_hal_i2c_release(I2C_BUS); if(status) { @@ -93,14 +99,14 @@ BH1750_STATUS bh1750_set_mt_reg(uint8_t mt_reg) { tmp[1] = (0x60 | (mt_reg & 0x1F)); furi_hal_i2c_acquire(I2C_BUS); - status = furi_hal_i2c_tx(I2C_BUS, BH1750_ADDRESS, &tmp[0], 1, I2C_TIMEOUT); + status = furi_hal_i2c_tx(I2C_BUS, bh1750_addr, &tmp[0], 1, I2C_TIMEOUT); furi_hal_i2c_release(I2C_BUS); if(!status) { return BH1750_ERROR; } furi_hal_i2c_acquire(I2C_BUS); - status = furi_hal_i2c_tx(I2C_BUS, BH1750_ADDRESS, &tmp[1], 1, I2C_TIMEOUT); + status = furi_hal_i2c_tx(I2C_BUS, bh1750_addr, &tmp[1], 1, I2C_TIMEOUT); furi_hal_i2c_release(I2C_BUS); if(status) { return BH1750_OK; @@ -122,7 +128,7 @@ BH1750_STATUS bh1750_read_light(float* result) { bool status; furi_hal_i2c_acquire(I2C_BUS); - status = furi_hal_i2c_rx(I2C_BUS, BH1750_ADDRESS, rcv, 2, I2C_TIMEOUT); + status = furi_hal_i2c_rx(I2C_BUS, bh1750_addr, rcv, 2, I2C_TIMEOUT); furi_hal_i2c_release(I2C_BUS); if(status) { diff --git a/applications/plugins/lightmeter/lib/BH1750/BH1750.h b/applications/plugins/lightmeter/lib/BH1750/BH1750.h index 991350f7f..46649c33e 100644 --- a/applications/plugins/lightmeter/lib/BH1750/BH1750.h +++ b/applications/plugins/lightmeter/lib/BH1750/BH1750.h @@ -49,6 +49,13 @@ typedef enum { */ BH1750_STATUS bh1750_init(); +/** + * @brief Change the I2C device address and then initialize the sensor. + * + * @return BH1750_STATUS + */ +BH1750_STATUS bh1750_init_with_addr(uint8_t addr); + /** * @brief Reset all registers to the default value. * diff --git a/applications/plugins/lightmeter/lightmeter_config.h b/applications/plugins/lightmeter/lightmeter_config.h index 4f7e11e0d..5edbdce0a 100644 --- a/applications/plugins/lightmeter/lightmeter_config.h +++ b/applications/plugins/lightmeter/lightmeter_config.h @@ -79,6 +79,7 @@ typedef enum { SPEED_250, SPEED_125, SPEED_60, + SPEED_48, SPEED_30, SPEED_15, SPEED_8, diff --git a/applications/plugins/lightmeter/module/back.jpg b/applications/plugins/lightmeter/module/back.jpg deleted file mode 100644 index 366d7a852..000000000 Binary files a/applications/plugins/lightmeter/module/back.jpg and /dev/null differ diff --git a/applications/plugins/lightmeter/module/front.jpg b/applications/plugins/lightmeter/module/front.jpg deleted file mode 100644 index b38fd4150..000000000 Binary files a/applications/plugins/lightmeter/module/front.jpg and /dev/null differ diff --git a/applications/plugins/lightmeter/module/module.jpg b/applications/plugins/lightmeter/module/module.jpg deleted file mode 100644 index 53f481338..000000000 Binary files a/applications/plugins/lightmeter/module/module.jpg and /dev/null differ diff --git a/applications/plugins/lightmeter/module/module_v2_enclosure.stl b/applications/plugins/lightmeter/module/module_v2_enclosure.stl deleted file mode 100644 index ee2be93fd..000000000 Binary files a/applications/plugins/lightmeter/module/module_v2_enclosure.stl and /dev/null differ diff --git a/applications/plugins/lightmeter/module/module_v2_gerber.zip b/applications/plugins/lightmeter/module/module_v2_gerber.zip deleted file mode 100644 index 02887f4bf..000000000 Binary files a/applications/plugins/lightmeter/module/module_v2_gerber.zip and /dev/null differ diff --git a/applications/plugins/mandelbrot/mandelbrot.c b/applications/plugins/mandelbrot/mandelbrot.c index bfddc6a97..8bfe36c77 100644 --- a/applications/plugins/mandelbrot/mandelbrot.c +++ b/applications/plugins/mandelbrot/mandelbrot.c @@ -15,6 +15,7 @@ typedef struct { } PluginEvent; typedef struct { + FuriMutex* mutex; float xZoom; float yZoom; float xOffset; @@ -52,10 +53,9 @@ bool mandelbrot_pixel(int x, int y, float xZoom, float yZoom, float xOffset, flo } static void render_callback(Canvas* const canvas, void* ctx) { - const PluginState* plugin_state = acquire_mutex((ValueMutex*)ctx, 25); - if(plugin_state == NULL) { - return; - } + furi_assert(ctx); + const PluginState* plugin_state = ctx; + furi_mutex_acquire(plugin_state->mutex, FuriWaitForever); // border around the edge of the screen canvas_draw_frame(canvas, 0, 0, 128, 64); @@ -75,7 +75,7 @@ static void render_callback(Canvas* const canvas, void* ctx) { } } - release_mutex((ValueMutex*)ctx, plugin_state); + furi_mutex_release(plugin_state->mutex); } static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { @@ -100,8 +100,8 @@ int32_t mandelbrot_app(void* p) { PluginState* plugin_state = malloc(sizeof(PluginState)); mandelbrot_state_init(plugin_state); - ValueMutex state_mutex; - if(!init_mutex(&state_mutex, plugin_state, sizeof(PluginState))) { + plugin_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!plugin_state->mutex) { FURI_LOG_E("mandelbrot", "cannot create mutex\r\n"); furi_message_queue_free(event_queue); free(plugin_state); @@ -110,7 +110,7 @@ int32_t mandelbrot_app(void* p) { // Set system callbacks ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, render_callback, &state_mutex); + view_port_draw_callback_set(view_port, render_callback, plugin_state); view_port_input_callback_set(view_port, input_callback, event_queue); // Open GUI and register view_port @@ -120,7 +120,7 @@ int32_t mandelbrot_app(void* p) { PluginEvent event; for(bool processing = true; processing;) { FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); - PluginState* plugin_state = (PluginState*)acquire_mutex_block(&state_mutex); + furi_mutex_acquire(plugin_state->mutex, FuriWaitForever); if(event_status == FuriStatusOk) { // press events @@ -154,12 +154,9 @@ int32_t mandelbrot_app(void* p) { } } } - } else { - FURI_LOG_D("mandelbrot", "osMessageQueue: event timeout"); - // event timeout } view_port_update(view_port); - release_mutex(&state_mutex, plugin_state); + furi_mutex_release(plugin_state->mutex); } view_port_enabled_set(view_port, false); @@ -167,6 +164,8 @@ int32_t mandelbrot_app(void* p) { furi_record_close(RECORD_GUI); view_port_free(view_port); furi_message_queue_free(event_queue); + furi_mutex_free(plugin_state->mutex); + free(plugin_state); return 0; } \ No newline at end of file diff --git a/applications/plugins/metronome/README.md b/applications/plugins/metronome/README.md index 79134c350..4b6cd3122 100644 --- a/applications/plugins/metronome/README.md +++ b/applications/plugins/metronome/README.md @@ -1,5 +1,7 @@ # Metronome +[Original link](https://github.com/panki27/Metronome) + A metronome for the [Flipper Zero](https://flipperzero.one/) device. Goes along perfectly with my [BPM tapper](https://github.com/panki27/bpm-tapper). ![screenshot](img/screenshot.png) @@ -17,5 +19,5 @@ A metronome for the [Flipper Zero](https://flipperzero.one/) device. Goes along ## Compiling ``` -./fbt fap_metronome +./fbt firmware_metronome ``` diff --git a/applications/plugins/metronome/application.fam b/applications/plugins/metronome/application.fam index b40809638..32588d06e 100644 --- a/applications/plugins/metronome/application.fam +++ b/applications/plugins/metronome/application.fam @@ -8,8 +8,8 @@ App( "gui", ], fap_icon="metronome_icon.png", - fap_icon_assets="icons", fap_category="Music", + fap_icon_assets="images", stack_size=2 * 1024, order=20, ) diff --git a/applications/plugins/metronome/gui_extensions.c b/applications/plugins/metronome/gui_extensions.c index a7661ec28..458eb137b 100644 --- a/applications/plugins/metronome/gui_extensions.c +++ b/applications/plugins/metronome/gui_extensions.c @@ -1,6 +1,6 @@ #include #include -#include "Metronome_icons.h" +#include //lib can only do bottom left/right void elements_button_top_left(Canvas* canvas, const char* str) { diff --git a/applications/plugins/metronome/icons/ButtonUp_7x4.png b/applications/plugins/metronome/images/ButtonUp_7x4.png similarity index 100% rename from applications/plugins/metronome/icons/ButtonUp_7x4.png rename to applications/plugins/metronome/images/ButtonUp_7x4.png diff --git a/applications/plugins/metronome/metronome.c b/applications/plugins/metronome/metronome.c index 4c374aa75..a01f4418d 100644 --- a/applications/plugins/metronome/metronome.c +++ b/applications/plugins/metronome/metronome.c @@ -1,7 +1,6 @@ #include #include #include -#include #include #include @@ -50,31 +49,33 @@ typedef struct { enum OutputMode output_mode; FuriTimer* timer; NotificationApp* notifications; + FuriMutex* mutex; } MetronomeState; static void render_callback(Canvas* const canvas, void* ctx) { - const MetronomeState* metronome_state = acquire_mutex((ValueMutex*)ctx, 25); - if(metronome_state == NULL) { - return; - } + furi_assert(ctx); + const MetronomeState* metronome_state = ctx; + furi_mutex_acquire(metronome_state->mutex, FuriWaitForever); - string_t tempStr; - string_init(tempStr); + FuriString* tempStr = furi_string_alloc(); canvas_draw_frame(canvas, 0, 0, 128, 64); canvas_set_font(canvas, FontPrimary); // draw bars/beat - string_printf(tempStr, "%d/%d", metronome_state->beats_per_bar, metronome_state->note_length); - canvas_draw_str_aligned(canvas, 64, 8, AlignCenter, AlignCenter, string_get_cstr(tempStr)); - string_reset(tempStr); + furi_string_printf( + tempStr, "%d/%d", metronome_state->beats_per_bar, metronome_state->note_length); + canvas_draw_str_aligned( + canvas, 64, 8, AlignCenter, AlignCenter, furi_string_get_cstr(tempStr)); + furi_string_reset(tempStr); // draw BPM value - string_printf(tempStr, "%.2f", metronome_state->bpm); + furi_string_printf(tempStr, "%.2f", metronome_state->bpm); canvas_set_font(canvas, FontBigNumbers); - canvas_draw_str_aligned(canvas, 64, 24, AlignCenter, AlignCenter, string_get_cstr(tempStr)); - string_reset(tempStr); + canvas_draw_str_aligned( + canvas, 64, 24, AlignCenter, AlignCenter, furi_string_get_cstr(tempStr)); + furi_string_reset(tempStr); // draw volume indicator // always draw first waves @@ -126,8 +127,8 @@ static void render_callback(Canvas* const canvas, void* ctx) { canvas, 8, 36, 112, (float)metronome_state->current_beat / metronome_state->beats_per_bar); // cleanup - string_clear(tempStr); - release_mutex((ValueMutex*)ctx, metronome_state); + furi_string_free(tempStr); + furi_mutex_release(metronome_state->mutex); } static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { @@ -139,7 +140,10 @@ static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queu static void timer_callback(void* ctx) { // this is where we go BEEP! - MetronomeState* metronome_state = acquire_mutex((ValueMutex*)ctx, 25); + furi_assert(ctx); + MetronomeState* metronome_state = ctx; + furi_mutex_acquire(metronome_state->mutex, FuriWaitForever); + metronome_state->current_beat++; if(metronome_state->current_beat > metronome_state->beats_per_bar) { metronome_state->current_beat = 1; @@ -200,7 +204,7 @@ static void timer_callback(void* ctx) { } notification_message(metronome_state->notifications, &sequence_reset_rgb); - release_mutex((ValueMutex*)ctx, metronome_state); + furi_mutex_release(metronome_state->mutex); } static uint32_t state_to_sleep_ticks(MetronomeState* metronome_state) { @@ -273,8 +277,8 @@ int32_t metronome_app() { MetronomeState* metronome_state = malloc(sizeof(MetronomeState)); metronome_state_init(metronome_state); - ValueMutex state_mutex; - if(!init_mutex(&state_mutex, metronome_state, sizeof(MetronomeState))) { + metronome_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!metronome_state->mutex) { FURI_LOG_E("Metronome", "cannot create mutex\r\n"); free(metronome_state); return 255; @@ -282,20 +286,20 @@ int32_t metronome_app() { // Set system callbacks ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, render_callback, &state_mutex); + view_port_draw_callback_set(view_port, render_callback, metronome_state); view_port_input_callback_set(view_port, input_callback, event_queue); - metronome_state->timer = furi_timer_alloc(timer_callback, FuriTimerTypePeriodic, &state_mutex); + metronome_state->timer = + furi_timer_alloc(timer_callback, FuriTimerTypePeriodic, metronome_state); // Open GUI and register view_port - // - Gui* gui = furi_record_open("gui"); + Gui* gui = furi_record_open(RECORD_GUI); gui_add_view_port(gui, view_port, GuiLayerFullscreen); PluginEvent event; for(bool processing = true; processing;) { FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); - MetronomeState* metronome_state = (MetronomeState*)acquire_mutex_block(&state_mutex); + furi_mutex_acquire(metronome_state->mutex, FuriWaitForever); if(event_status == FuriStatusOk) { if(event.type == EventTypeKey) { @@ -326,7 +330,7 @@ int32_t metronome_app() { case InputKeyBack: processing = false; break; - case InputKeyMAX: + default: break; } } else if(event.input.type == InputTypeLong) { @@ -348,7 +352,7 @@ int32_t metronome_app() { case InputKeyBack: processing = false; break; - case InputKeyMAX: + default: break; } } else if(event.input.type == InputTypeRepeat) { @@ -369,26 +373,23 @@ int32_t metronome_app() { case InputKeyBack: processing = false; break; - case InputKeyMAX: + default: break; } } } - } else { - FURI_LOG_D("Metronome", "FuriMessageQueue: event timeout"); - // event timeout } view_port_update(view_port); - release_mutex(&state_mutex, metronome_state); + furi_mutex_release(metronome_state->mutex); } view_port_enabled_set(view_port, false); gui_remove_view_port(gui, view_port); - furi_record_close("gui"); + furi_record_close(RECORD_GUI); view_port_free(view_port); furi_message_queue_free(event_queue); - delete_mutex(&state_mutex); + furi_mutex_free(metronome_state->mutex); furi_timer_free(metronome_state->timer); furi_record_close(RECORD_NOTIFICATION); free(metronome_state); diff --git a/applications/plugins/minesweeper/README.md b/applications/plugins/minesweeper/README.md index eb1ebcad2..28176bd5e 100644 --- a/applications/plugins/minesweeper/README.md +++ b/applications/plugins/minesweeper/README.md @@ -1,5 +1,7 @@ # Minesweeper +[Original Link](https://github.com/panki27/minesweeper) + This is a Minesweeper implementation for the Flipper Zero device. ![screenshot](img/screenshot.png) diff --git a/applications/plugins/minesweeper/minesweeper.c b/applications/plugins/minesweeper/minesweeper.c index 75fb32e56..43e8f027e 100644 --- a/applications/plugins/minesweeper/minesweeper.c +++ b/applications/plugins/minesweeper/minesweeper.c @@ -55,6 +55,7 @@ typedef struct { int flags_set; bool game_started; uint32_t game_started_tick; + FuriMutex* mutex; } Minesweeper; static void timer_callback(void* ctx) { @@ -72,10 +73,10 @@ static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queu } static void render_callback(Canvas* const canvas, void* ctx) { - const Minesweeper* minesweeper_state = acquire_mutex((ValueMutex*)ctx, 25); - if(minesweeper_state == NULL) { - return; - } + furi_assert(ctx); + const Minesweeper* minesweeper_state = ctx; + furi_mutex_acquire(minesweeper_state->mutex, FuriWaitForever); + FuriString* mineStr; FuriString* timeStr; mineStr = furi_string_alloc(); @@ -160,7 +161,7 @@ static void render_callback(Canvas* const canvas, void* ctx) { furi_string_free(mineStr); furi_string_free(timeStr); - release_mutex((ValueMutex*)ctx, minesweeper_state); + furi_mutex_release(minesweeper_state->mutex); } static void setup_playfield(Minesweeper* minesweeper_state) { @@ -217,6 +218,10 @@ static bool game_lost(Minesweeper* minesweeper_state) { dialog_message_set_icon(message, NULL, 0, 10); + // Set cursor to initial position + minesweeper_state->cursor_x = 0; + minesweeper_state->cursor_y = 0; + NotificationApp* notifications = furi_record_open(RECORD_NOTIFICATION); notification_message(notifications, &sequence_set_vibro_on); furi_record_close(RECORD_NOTIFICATION); @@ -387,8 +392,8 @@ int32_t minesweeper_app(void* p) { // setup minesweeper_state_init(minesweeper_state); - ValueMutex state_mutex; - if(!init_mutex(&state_mutex, minesweeper_state, sizeof(minesweeper_state))) { + minesweeper_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!minesweeper_state->mutex) { FURI_LOG_E("Minesweeper", "cannot create mutex\r\n"); free(minesweeper_state); return 255; @@ -397,18 +402,19 @@ int32_t minesweeper_app(void* p) { // Set system callbacks ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, render_callback, &state_mutex); + view_port_draw_callback_set(view_port, render_callback, minesweeper_state); view_port_input_callback_set(view_port, input_callback, event_queue); - minesweeper_state->timer = furi_timer_alloc(timer_callback, FuriTimerTypeOnce, &state_mutex); + minesweeper_state->timer = + furi_timer_alloc(timer_callback, FuriTimerTypeOnce, minesweeper_state); // Open GUI and register view_port - Gui* gui = furi_record_open("gui"); + Gui* gui = furi_record_open(RECORD_GUI); gui_add_view_port(gui, view_port, GuiLayerFullscreen); PluginEvent event; for(bool processing = true; processing;) { FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); - Minesweeper* minesweeper_state = (Minesweeper*)acquire_mutex_block(&state_mutex); + furi_mutex_acquire(minesweeper_state->mutex, FuriWaitForever); if(event_status == FuriStatusOk) { // press events if(event.type == EventTypeKey) { @@ -472,7 +478,7 @@ int32_t minesweeper_app(void* p) { // Exit the plugin processing = false; break; - case InputKeyMAX: + default: break; } } else if(event.input.type == InputTypeLong) { @@ -491,24 +497,21 @@ int32_t minesweeper_app(void* p) { case InputKeyBack: processing = false; break; - case InputKeyMAX: + default: break; } } } - } else { - // event timeout - ; } view_port_update(view_port); - release_mutex(&state_mutex, minesweeper_state); + furi_mutex_release(minesweeper_state->mutex); } view_port_enabled_set(view_port, false); gui_remove_view_port(gui, view_port); - furi_record_close("gui"); + furi_record_close(RECORD_GUI); view_port_free(view_port); furi_message_queue_free(event_queue); - delete_mutex(&state_mutex); + furi_mutex_free(minesweeper_state->mutex); furi_timer_free(minesweeper_state->timer); free(minesweeper_state); diff --git a/applications/plugins/morse_code/application.fam b/applications/plugins/morse_code/application.fam index 73ad2ba81..d5a2e6c9a 100644 --- a/applications/plugins/morse_code/application.fam +++ b/applications/plugins/morse_code/application.fam @@ -1,5 +1,5 @@ App( - appid="Morse_Code", + appid="morse_code", name="Morse Code", apptype=FlipperAppType.EXTERNAL, entry_point="morse_code_app", @@ -10,5 +10,5 @@ App( stack_size=1 * 1024, order=20, fap_icon="morse_code_10px.png", - fap_category="Music", + fap_category="Misc", ) diff --git a/applications/plugins/morse_code/morse_code.c b/applications/plugins/morse_code/morse_code.c index beb661222..3f96969e7 100644 --- a/applications/plugins/morse_code/morse_code.c +++ b/applications/plugins/morse_code/morse_code.c @@ -44,15 +44,10 @@ static void render_callback(Canvas* const canvas, void* ctx) { canvas_draw_frame(canvas, vol_bar_x_pos, vol_bar_y_pos, 4, 64); canvas_draw_box(canvas, vol_bar_x_pos, vol_bar_y_pos + (64 - volume_h), 4, volume_h); - //dit bpm - canvas_draw_str_aligned( - canvas, - 0, - 10, - AlignLeft, - AlignCenter, - furi_string_get_cstr( - furi_string_alloc_printf("Dit: %ld ms", morse_code->model->dit_delta))); + //dit bpms + FuriString* ditbpm = furi_string_alloc_printf("Dit: %ld ms", morse_code->model->dit_delta); + canvas_draw_str_aligned(canvas, 0, 10, AlignLeft, AlignCenter, furi_string_get_cstr(ditbpm)); + furi_string_free(ditbpm); //button info elements_button_center(canvas, "Press/Hold"); @@ -67,7 +62,7 @@ static void input_callback(InputEvent* input_event, void* ctx) { static void morse_code_worker_callback(FuriString* words, void* context) { MorseCode* morse_code = context; furi_check(furi_mutex_acquire(morse_code->model_mutex, FuriWaitForever) == FuriStatusOk); - morse_code->model->words = words; + furi_string_set(morse_code->model->words, words); furi_mutex_release(morse_code->model_mutex); view_port_update(morse_code->view_port); } @@ -109,6 +104,7 @@ void morse_code_free(MorseCode* instance) { furi_mutex_free(instance->model_mutex); + furi_string_free(instance->model->words); free(instance->model); free(instance); } @@ -116,10 +112,12 @@ void morse_code_free(MorseCode* instance) { int32_t morse_code_app() { MorseCode* morse_code = morse_code_alloc(); InputEvent input; + morse_code_worker_start(morse_code->worker); morse_code_worker_set_volume( morse_code->worker, MORSE_CODE_VOLUMES[morse_code->model->volume]); morse_code_worker_set_dit_delta(morse_code->worker, morse_code->model->dit_delta); + while(furi_message_queue_get(morse_code->input_queue, &input, FuriWaitForever) == FuriStatusOk) { furi_check(furi_mutex_acquire(morse_code->model_mutex, FuriWaitForever) == FuriStatusOk); @@ -128,6 +126,7 @@ int32_t morse_code_app() { break; } else if(input.key == InputKeyBack && input.type == InputTypeShort) { morse_code_worker_reset_text(morse_code->worker); + furi_string_reset(morse_code->model->words); } else if(input.key == InputKeyOk) { if(input.type == InputTypePress) morse_code_worker_play(morse_code->worker, true); @@ -160,6 +159,7 @@ int32_t morse_code_app() { furi_mutex_release(morse_code->model_mutex); view_port_update(morse_code->view_port); } + morse_code_worker_stop(morse_code->worker); morse_code_free(morse_code); return 0; diff --git a/applications/plugins/morse_code/morse_code_worker.c b/applications/plugins/morse_code/morse_code_worker.c index 142b427b6..47896b91a 100644 --- a/applications/plugins/morse_code/morse_code_worker.c +++ b/applications/plugins/morse_code/morse_code_worker.c @@ -35,6 +35,8 @@ void morse_code_worker_fill_buffer(MorseCodeWorker* instance, uint32_t duration) furi_string_push_back(instance->buffer, *DOT); else if(duration <= (instance->dit_delta * 3)) furi_string_push_back(instance->buffer, *LINE); + else + furi_string_reset(instance->buffer); if(furi_string_size(instance->buffer) > 5) furi_string_reset(instance->buffer); FURI_LOG_D("MorseCode: Buffer", "%s", furi_string_get_cstr(instance->buffer)); } @@ -87,9 +89,13 @@ static int32_t morse_code_worker_thread_callback(void* context) { if(!pushed) { if(end_tick + (instance->dit_delta * 3) < furi_get_tick()) { //NEW LETTER - morse_code_worker_fill_letter(instance); - if(instance->callback) - instance->callback(instance->words, instance->callback_context); + if(!furi_string_empty(instance->buffer)) { + morse_code_worker_fill_letter(instance); + if(instance->callback) + instance->callback(instance->words, instance->callback_context); + } else { + spaced = true; + } pushed = true; } } @@ -170,6 +176,7 @@ void morse_code_worker_start(MorseCodeWorker* instance) { void morse_code_worker_stop(MorseCodeWorker* instance) { furi_assert(instance); furi_assert(instance->is_running == true); + instance->play = false; instance->is_running = false; furi_thread_join(instance->thread); FURI_LOG_D("MorseCode: Stop", "Stop"); diff --git a/applications/plugins/mouse_jiggler/application.fam b/applications/plugins/mouse_jiggler/application.fam deleted file mode 100644 index 6115315f5..000000000 --- a/applications/plugins/mouse_jiggler/application.fam +++ /dev/null @@ -1,12 +0,0 @@ -App( - appid="MouseJiggler", - name="Mouse Jiggler", - apptype=FlipperAppType.EXTERNAL, - entry_point="mouse_jiggler_app", - cdefines=["APP_MOUSE_JIGGLER"], - requires=["gui"], - stack_size=1 * 1024, - order=150, - fap_icon="mouse_10px.png", - fap_category="Misc", -) diff --git a/applications/plugins/mouse_jiggler/mouse_10px.png b/applications/plugins/mouse_jiggler/mouse_10px.png deleted file mode 100644 index 94c3a7a14..000000000 Binary files a/applications/plugins/mouse_jiggler/mouse_10px.png and /dev/null differ diff --git a/applications/plugins/mouse_jiggler/mouse_jiggler.c b/applications/plugins/mouse_jiggler/mouse_jiggler.c deleted file mode 100644 index 868082eea..000000000 --- a/applications/plugins/mouse_jiggler/mouse_jiggler.c +++ /dev/null @@ -1,141 +0,0 @@ -#include -#include -#include -#include - -#define MOUSE_MOVE_SHORT 5 -#define MOUSE_MOVE_LONG 20 - -typedef enum { - EventTypeInput, - EventTypeKey, -} EventType; - -typedef struct { - EventType type; - InputEvent input; -} UsbMouseEvent; - -typedef struct { - bool running; -} MouseJigglerState; - -static void mouse_jiggler_render_callback(Canvas* canvas, void* ctx) { - const MouseJigglerState* plugin_state = acquire_mutex((ValueMutex*)ctx, 25); - if(plugin_state == NULL) { - return; - } - - canvas_set_font(canvas, FontPrimary); - canvas_draw_str(canvas, 2, 12, "USB Mouse Jiggler"); - if(!plugin_state->running) { - canvas_set_font(canvas, FontSecondary); - canvas_draw_str(canvas, 2, 27, " -> STOPPED"); - canvas_draw_str(canvas, 2, 51, "Press [ok] to start"); - canvas_draw_str(canvas, 2, 63, "Press [back] to exit"); - } else { - canvas_set_font(canvas, FontSecondary); - canvas_draw_str(canvas, 2, 27, " -> RUNNING"); - canvas_draw_str(canvas, 2, 51, "Press [back] to stop"); - } - - release_mutex((ValueMutex*)ctx, plugin_state); -} - -static void mouse_jiggler_input_callback(InputEvent* input_event, void* ctx) { - FuriMessageQueue* event_queue = ctx; - furi_assert(event_queue); - - UsbMouseEvent event = {.type = EventTypeKey, .input = *input_event}; - furi_message_queue_put(event_queue, &event, FuriWaitForever); -} - -static void mouse_jiggler_state_init(MouseJigglerState* const plugin_state) { - plugin_state->running = false; -} - -int32_t mouse_jiggler_app(void* p) { - UNUSED(p); - - FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(UsbMouseEvent)); - - MouseJigglerState* plugin_state = malloc(sizeof(MouseJigglerState)); - if(plugin_state == NULL) { - FURI_LOG_E("MouseJiggler", "MouseJigglerState: malloc error\r\n"); - return 255; - } - mouse_jiggler_state_init(plugin_state); - - ValueMutex state_mutex; - if(!init_mutex(&state_mutex, plugin_state, sizeof(MouseJigglerState))) { - FURI_LOG_E("MouseJiggler", "cannot create mutex\r\n"); - furi_message_queue_free(event_queue); - free(plugin_state); - return 255; - } - - ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, mouse_jiggler_render_callback, &state_mutex); - view_port_input_callback_set(view_port, mouse_jiggler_input_callback, event_queue); - - FuriHalUsbInterface* usb_mode_prev = furi_hal_usb_get_config(); - furi_hal_usb_set_config(&usb_hid, NULL); - - // Open GUI and register view_port - Gui* gui = furi_record_open(RECORD_GUI); - gui_add_view_port(gui, view_port, GuiLayerFullscreen); - - UsbMouseEvent event; - //bool status = 0; - - for(bool processing = true; processing;) { - FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); - - MouseJigglerState* plugin_state = (MouseJigglerState*)acquire_mutex_block(&state_mutex); - - if(event_status == FuriStatusOk) { - if(event.type == EventTypeKey) { - if(event.input.type == InputTypePress) { - switch(event.input.key) { - case InputKeyOk: - if(!plugin_state->running) { - plugin_state->running = true; - } - break; - case InputKeyBack: - if(!plugin_state->running) { - processing = false; - } else { - plugin_state->running = false; - } - break; - default: - break; - } - } - } - } - - if(plugin_state->running) { - furi_hal_hid_mouse_move(MOUSE_MOVE_SHORT, 0); - furi_delay_ms(500); - furi_hal_hid_mouse_move(-MOUSE_MOVE_SHORT, 0); - furi_delay_ms(500); - } - - view_port_update(view_port); - release_mutex(&state_mutex, plugin_state); - } - - furi_hal_usb_set_config(usb_mode_prev, NULL); - - // remove & free all stuff created by app - view_port_enabled_set(view_port, false); - gui_remove_view_port(gui, view_port); - furi_record_close(RECORD_GUI); - view_port_free(view_port); - furi_message_queue_free(event_queue); - delete_mutex(&state_mutex); - - return 0; -} diff --git a/applications/plugins/mousejacker/README.md b/applications/plugins/mousejacker/README.md deleted file mode 100644 index ad5fdecdc..000000000 --- a/applications/plugins/mousejacker/README.md +++ /dev/null @@ -1,62 +0,0 @@ -# flipperzero-nrf24 - -An [NRF24](https://www.sparkfun.com/datasheets/Components/SMD/nRF24L01Pluss_Preliminary_Product_Specification_v1_0.pdf) driver for the [Flipper Zero](https://flipperzero.one/) device. The NRF24 is a popular line of 2.4GHz radio transceivers from Nordic Semiconductors. This library is not currently complete, but functional. - -## Warning -This repo contains two Flipper Zero apps that utilize the NRF24 driver to sniff for NRF24 addresses and perform mousejack attacks. These apps are for **educational purposes** only. Please use this code responsibly and only use these apps on your own equipment. - -## Acknowledgments -The NRF24 sniffing technique was discovered and shared by Travis Goodspeed in [his blog](http://travisgoodspeed.blogspot.com/2011/02/promiscuity-is-nrf24l01s-duty.html). - -The mousejack vulnerabilities were discovered and reported by Marc Newlin, see [the blog](https://www.bastille.net/research/vulnerabilities/mousejack/technical-details) for technical details. - -Much of the driver code was inspired by [RadioHead's Arduino library](https://www.airspayce.com/mikem/arduino/RadioHead/classRH__NRF24.html). -Much of the mousejack code was inspired by the [Jackit project](https://github.com/insecurityofthings/jackit). - - -## PinOut from from NoComp/Frog - - -# Mousejack / NRF24 pinout by UberGuidoZ -2/A7 on FZ goes to MOSI/6 on nrf24l01
-3/A6 on FZ goes to MISO/7 on nrf24l01
-4/A4 on FZ goes to CSN/4 on nrf24l01
-5/B3 on FZ goes to SCK/5 on nrf24l01
-6/B2 on FZ goes to CE/3 on nrf24l01
-8/GND on FZ goes to GND/1 on nrf24l01
-9/3V3 on FZ goes to VCC/2 on nrf24l01
-IRQ/8 is left disconnected on nrf24l01 - -![NRF_Pins](https://user-images.githubusercontent.com/57457139/178093717-39effd5c-ebe2-4253-b13c-70517d7902f9.png) - -If the nRF module is acting a bit flakey, try adding a capacitor to the vcc/gnd lines! I've not tried the Plus model so it may have a bigger need for a cap. Otherwise, I haven't had any major issues. Anything from a 3.3 uF to 10 uF should do. (Watch your positive/negative placement! Negative to ground.) I learned if you wanna get fancy, include a 0.1 uF cap in parallel. The 3.3 uF to 10 uF will respond to slow freq changes while the 0.1 uF will respond to the high freq switching spikes that the larger one cannot. That said, a single 10 uF will likely suffice for the Mousejack attack. ¯\\\_(ツ)_/¯ - -![NRF_Capacitor](https://user-images.githubusercontent.com/57457139/178169959-d030f9a6-d2ac-46af-af8b-470ff092c8a7.jpg) - -Selfmade NRF24 breakoutboard: -![NRF_soldered](https://user-images.githubusercontent.com/22019133/183419103-9c40b28b-8152-4212-98e0-9a3358f69518.jpeg) -![NRF_soldered2](https://user-images.githubusercontent.com/22019133/183419176-26c0359b-0ecb-4015-8f8b-3a75209502ba.jpeg) - -## Practical hints from einstein2150 -If you are not successfull with the NRF Sniff Plugin you can try to get the MAC of the device with the crazyradio pa USB dongle. Have a look here: https://github.com/SySS-Research/nrf24-playset - - -A sample output of the NRF-Research script could be like -``` -[2022-08-05 13:28:56.366] Found nRF24 device with address 38:24:93:C0:07 on channel 75\ -[2022-08-05 13:28:56.371] Checking communication\ -[2022-08-05 13:28:59.088] Scan for nRF24 device\ -[2022-08-05 13:28:59.097] Actively searching for address 07:C0:93:24:38\ -``` - -Be carefull with the byte-order using in mousejacker! The correct byte order is the reverse-byteorder. In my example the reversed one is ```07:C0:93:24:38``` for my Logitech R400 presenter. -The next thing which could make trouble is the datarate. In my case the Logitech-device is only responding at 2Mbit. Commands at 1Mbit are not detected. - -Now its time to write all the relevant information in the config file. In my case you are creating a file ```addresses.txt``` in the SD directory of ```/nrfsniff```. The file content corresponding to the results of the research is ```07C0932438,2``` representing the reversed byte order of the MAC and the comma-separated datarate. - -Start the Mouse Jacker plugin and select the prepared ```addresses.txt```. If everything is alright you are starting with your MAC ready for attack: - -![mousejacker start-screen](https://user-images.githubusercontent.com/22019133/183419533-ef5c8df1-e328-45e3-b85d-376513d69c82.jpeg) - -If you have troube you can check if a datarate of 1 Mbit will help. Change ```07C0932438,2``` to ```07C0932438,1``` in this case. Another problem in the practical use is electromagnetical noise. In my case the external USB-3.1-Hub creating massive noise in the 2,4 Ghz-Frequency around a distance of 5 cm. Try using a long USB connection-cable for the receiver. In my case the response of the attack raises significant because the signals of the NRF-breakoutboard is no more covered with noise. - diff --git a/applications/plugins/mousejacker/application.fam b/applications/plugins/mousejacker/application.fam index 05835700b..725e81ae8 100644 --- a/applications/plugins/mousejacker/application.fam +++ b/applications/plugins/mousejacker/application.fam @@ -9,7 +9,7 @@ App( "dialogs", ], stack_size=2 * 1024, - order=50, + order=60, fap_icon="mouse_10px.png", fap_category="GPIO", fap_icon_assets="images", diff --git a/applications/plugins/mousejacker/icons/sub1_10px.png b/applications/plugins/mousejacker/icons/sub1_10px.png deleted file mode 100644 index 5a25fdf4e..000000000 Binary files a/applications/plugins/mousejacker/icons/sub1_10px.png and /dev/null differ diff --git a/applications/plugins/mousejacker/images/badkb_10px.png b/applications/plugins/mousejacker/images/badkb_10px.png deleted file mode 100644 index 037474aa3..000000000 Binary files a/applications/plugins/mousejacker/images/badkb_10px.png and /dev/null differ diff --git a/applications/plugins/mousejacker/icons/badkb_10px.png b/applications/plugins/mousejacker/images/badusb_10px.png similarity index 100% rename from applications/plugins/mousejacker/icons/badkb_10px.png rename to applications/plugins/mousejacker/images/badusb_10px.png diff --git a/applications/plugins/mousejacker/mousejacker.c b/applications/plugins/mousejacker/mousejacker.c index 3bd772889..9ef23983f 100644 --- a/applications/plugins/mousejacker/mousejacker.c +++ b/applications/plugins/mousejacker/mousejacker.c @@ -10,8 +10,7 @@ #include #include #include "mousejacker_ducky.h" -#include -#include "NRF24_Mouse_Jacker_icons.h" +#include #define TAG "mousejacker" #define LOGITECH_MAX_CHANNEL 85 @@ -41,10 +40,10 @@ char target_address_str[12] = "None"; char target_text[30]; static void render_callback(Canvas* const canvas, void* ctx) { - const PluginState* plugin_state = acquire_mutex((ValueMutex*)ctx, 25); - if(plugin_state == NULL) { - return; - } + furi_assert(ctx); + const PluginState* plugin_state = ctx; + furi_mutex_acquire(plugin_state->mutex, FuriWaitForever); + // border around the edge of the screen canvas_draw_frame(canvas, 0, 0, 128, 64); @@ -83,7 +82,7 @@ static void render_callback(Canvas* const canvas, void* ctx) { canvas_draw_str_aligned(canvas, 3, 30, AlignLeft, AlignBottom, "to exit"); } - release_mutex((ValueMutex*)ctx, plugin_state); + furi_mutex_release(plugin_state->mutex); } static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { @@ -112,7 +111,7 @@ static bool open_ducky_script(Stream* stream, PluginState* plugin_state) { DialogsFileBrowserOptions browser_options; dialog_file_browser_set_basic_options( - &browser_options, MOUSEJACKER_APP_PATH_EXTENSION, &I_badkb_10px); + &browser_options, MOUSEJACKER_APP_PATH_EXTENSION, &I_badusb_10px); browser_options.hide_ext = false; bool ret = dialog_file_browser_show(dialogs, path, path, &browser_options); @@ -184,7 +183,6 @@ static bool process_ducky_file( mj_process_ducky_script( nrf24_HANDLE, addr, addr_size, rate, (char*)file_buf, plugin_state); FURI_LOG_D(TAG, "finished execution"); - DOLPHIN_DEED(getRandomDeed()); loaded = true; } else { FURI_LOG_D(TAG, "load failed. file size: %d", file_size); @@ -291,8 +289,8 @@ int32_t mousejacker_app(void* p) { PluginState* plugin_state = malloc(sizeof(PluginState)); mousejacker_state_init(plugin_state); - ValueMutex state_mutex; - if(!init_mutex(&state_mutex, plugin_state, sizeof(PluginState))) { + plugin_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!plugin_state->mutex) { FURI_LOG_E("mousejacker", "cannot create mutex\r\n"); furi_message_queue_free(event_queue); free(plugin_state); @@ -301,7 +299,7 @@ int32_t mousejacker_app(void* p) { // Set system callbacks ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, render_callback, &state_mutex); + view_port_draw_callback_set(view_port, render_callback, plugin_state); view_port_input_callback_set(view_port, input_callback, event_queue); // Open GUI and register view_port @@ -332,7 +330,7 @@ int32_t mousejacker_app(void* p) { PluginEvent event; for(bool processing = true; processing;) { FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); - PluginState* plugin_state = (PluginState*)acquire_mutex_block(&state_mutex); + furi_mutex_acquire(plugin_state->mutex, FuriWaitForever); if(event_status == FuriStatusOk) { // press events @@ -382,7 +380,7 @@ int32_t mousejacker_app(void* p) { } view_port_update(view_port); - release_mutex(&state_mutex, plugin_state); + furi_mutex_release(plugin_state->mutex); } furi_thread_free(plugin_state->mjthread); @@ -393,6 +391,7 @@ int32_t mousejacker_app(void* p) { furi_record_close(RECORD_STORAGE); view_port_free(view_port); furi_message_queue_free(event_queue); + furi_mutex_free(plugin_state->mutex); free(plugin_state); return 0; diff --git a/applications/plugins/mousejacker/mousejacker_ducky.c b/applications/plugins/mousejacker/mousejacker_ducky.c index b3b04d836..04b0bfaca 100644 --- a/applications/plugins/mousejacker/mousejacker_ducky.c +++ b/applications/plugins/mousejacker/mousejacker_ducky.c @@ -3,6 +3,7 @@ static const char ducky_cmd_comment[] = {"REM"}; static const char ducky_cmd_delay[] = {"DELAY "}; static const char ducky_cmd_string[] = {"STRING "}; +static const char ducky_cmd_altstring[] = {"ALTSTRING "}; static const char ducky_cmd_repeat[] = {"REPEAT "}; static uint8_t LOGITECH_HID_TEMPLATE[] = @@ -11,6 +12,10 @@ static uint8_t LOGITECH_HELLO[] = {0x00, 0x4F, 0x00, 0x04, 0xB0, 0x10, 0x00, 0x0 static uint8_t LOGITECH_KEEPALIVE[] = {0x00, 0x40, 0x00, 0x55, 0x6B}; uint8_t prev_hid = 0; +static bool holding_ctrl = false; +static bool holding_shift = false; +static bool holding_alt = false; +static bool holding_gui = false; #define RT_THRESHOLD 50 #define LOGITECH_MIN_CHANNEL 2 @@ -65,7 +70,10 @@ MJDuckyKey mj_ducky_keys[] = {{" ", 44, 0}, {"!", 30, 2}, {"\"" {"LEFTARROW", 80, 0}, {"RIGHTARROW", 79, 0}, {"PAGEDOWN", 78, 0}, {"PAUSE", 72, 0}, {"SPACE", 44, 0}, {"UPARROW", 82, 0}, {"F11", 68, 0}, {"F7", 64, 0}, {"UP", 82, 0}, - {"LEFT", 80, 0}}; + {"LEFT", 80, 0}, {"NUM 1", 89, 0}, {"NUM 2", 90, 0}, + {"NUM 3", 91, 0}, {"NUM 4", 92, 0}, {"NUM 5", 93, 0}, + {"NUM 6", 94, 0}, {"NUM 7", 95, 0}, {"NUM 8", 96, 0}, + {"NUM 9", 97, 0}, {"NUM 0", 98, 0}}; /* static bool mj_ducky_get_number(const char* param, uint32_t* val) { @@ -89,7 +97,8 @@ static uint32_t mj_ducky_get_command_len(const char* line) { static bool mj_get_ducky_key(char* key, size_t keylen, MJDuckyKey* dk) { //FURI_LOG_D(TAG, "looking up key %s with length %d", key, keylen); for(uint i = 0; i < sizeof(mj_ducky_keys) / sizeof(MJDuckyKey); i++) { - if(!strncmp(mj_ducky_keys[i].name, key, keylen)) { + if(strlen(mj_ducky_keys[i].name) == keylen && + !strncmp(mj_ducky_keys[i].name, key, keylen)) { memcpy(dk, &mj_ducky_keys[i], sizeof(MJDuckyKey)); return true; } @@ -152,6 +161,30 @@ static void build_hid_packet(uint8_t mod, uint8_t hid, uint8_t* payload) { checksum(payload, LOGITECH_HID_TEMPLATE_SIZE); } +static void release_key( + FuriHalSpiBusHandle* handle, + uint8_t* addr, + uint8_t addr_size, + uint8_t rate, + PluginState* plugin_state) { + // This function release keys currently pressed, but keep pressing special keys + // if holding mod keys variable are set to true + + uint8_t hid_payload[LOGITECH_HID_TEMPLATE_SIZE] = {0}; + build_hid_packet( + 0 | holding_ctrl | holding_shift << 1 | holding_alt << 2 | holding_gui << 3, + 0, + hid_payload); + inject_packet( + handle, + addr, + addr_size, + rate, + hid_payload, + LOGITECH_HID_TEMPLATE_SIZE, + plugin_state); // empty hid packet +} + static void send_hid_packet( FuriHalSpiBusHandle* handle, uint8_t* addr, @@ -161,24 +194,22 @@ static void send_hid_packet( uint8_t hid, PluginState* plugin_state) { uint8_t hid_payload[LOGITECH_HID_TEMPLATE_SIZE] = {0}; - build_hid_packet(0, 0, hid_payload); - if(hid == prev_hid) - inject_packet( - handle, - addr, - addr_size, - rate, - hid_payload, - LOGITECH_HID_TEMPLATE_SIZE, - plugin_state); // empty hid packet + if(hid == prev_hid) release_key(handle, addr, addr_size, rate, plugin_state); prev_hid = hid; - build_hid_packet(mod, hid, hid_payload); + build_hid_packet( + mod | holding_ctrl | holding_shift << 1 | holding_alt << 2 | holding_gui << 3, + hid, + hid_payload); inject_packet( handle, addr, addr_size, rate, hid_payload, LOGITECH_HID_TEMPLATE_SIZE, plugin_state); furi_delay_ms(12); } +static bool ducky_end_line(const char chr) { + return ((chr == ' ') || (chr == '\0') || (chr == '\r') || (chr == '\n')); +} + // returns false if there was an error processing script line static bool mj_process_ducky_line( FuriHalSpiBusHandle* handle, @@ -251,6 +282,32 @@ static bool mj_process_ducky_line( send_hid_packet(handle, addr, addr_size, rate, dk.mod, dk.hid, plugin_state); } + return true; + } else if(strncmp(line_tmp, ducky_cmd_altstring, strlen(ducky_cmd_altstring)) == 0) { + // ALTSTRING + line_tmp = &line_tmp[mj_ducky_get_command_len(line_tmp) + 1]; + for(size_t i = 0; i < strlen(line_tmp); i++) { + if((line_tmp[i] < ' ') || (line_tmp[i] > '~')) { + continue; // Skip non-printable chars + } + + char alt_code[4]; + // Getting altcode of the char + snprintf(alt_code, 4, "%u", line_tmp[i]); + + uint8_t j = 0; + while(!ducky_end_line(alt_code[j])) { + char pad_num[5] = {'N', 'U', 'M', ' ', alt_code[j]}; + if(!mj_get_ducky_key(pad_num, 5, &dk)) return false; + holding_alt = true; + FURI_LOG_D(TAG, "Sending %s", pad_num); + send_hid_packet(handle, addr, addr_size, rate, dk.mod, dk.hid, plugin_state); + j++; + } + holding_alt = false; + release_key(handle, addr, addr_size, rate, plugin_state); + } + return true; } else if(strncmp(line_tmp, ducky_cmd_repeat, strlen(ducky_cmd_repeat)) == 0) { // REPEAT @@ -269,7 +326,9 @@ static bool mj_process_ducky_line( } else if(strncmp(line_tmp, "ALT", strlen("ALT")) == 0) { line_tmp = &line_tmp[mj_ducky_get_command_len(line_tmp) + 1]; if(!mj_get_ducky_key(line_tmp, strlen(line_tmp), &dk)) return false; - send_hid_packet(handle, addr, addr_size, rate, dk.mod | 4, dk.hid, plugin_state); + holding_alt = true; + send_hid_packet(handle, addr, addr_size, rate, dk.mod, dk.hid, plugin_state); + holding_alt = false; return true; } else if( strncmp(line_tmp, "GUI", strlen("GUI")) == 0 || @@ -277,33 +336,47 @@ static bool mj_process_ducky_line( strncmp(line_tmp, "COMMAND", strlen("COMMAND")) == 0) { line_tmp = &line_tmp[mj_ducky_get_command_len(line_tmp) + 1]; if(!mj_get_ducky_key(line_tmp, strlen(line_tmp), &dk)) return false; - send_hid_packet(handle, addr, addr_size, rate, dk.mod | 8, dk.hid, plugin_state); + holding_gui = true; + send_hid_packet(handle, addr, addr_size, rate, dk.mod, dk.hid, plugin_state); + holding_gui = false; return true; } else if( strncmp(line_tmp, "CTRL-ALT", strlen("CTRL-ALT")) == 0 || strncmp(line_tmp, "CONTROL-ALT", strlen("CONTROL-ALT")) == 0) { line_tmp = &line_tmp[mj_ducky_get_command_len(line_tmp) + 1]; if(!mj_get_ducky_key(line_tmp, strlen(line_tmp), &dk)) return false; - send_hid_packet(handle, addr, addr_size, rate, dk.mod | 4 | 1, dk.hid, plugin_state); + holding_ctrl = true; + holding_alt = true; + send_hid_packet(handle, addr, addr_size, rate, dk.mod, dk.hid, plugin_state); + holding_ctrl = false; + holding_alt = false; return true; } else if( strncmp(line_tmp, "CTRL-SHIFT", strlen("CTRL-SHIFT")) == 0 || strncmp(line_tmp, "CONTROL-SHIFT", strlen("CONTROL-SHIFT")) == 0) { line_tmp = &line_tmp[mj_ducky_get_command_len(line_tmp) + 1]; if(!mj_get_ducky_key(line_tmp, strlen(line_tmp), &dk)) return false; - send_hid_packet(handle, addr, addr_size, rate, dk.mod | 1 | 2, dk.hid, plugin_state); + holding_ctrl = true; + holding_shift = true; + send_hid_packet(handle, addr, addr_size, rate, dk.mod, dk.hid, plugin_state); + holding_ctrl = false; + holding_shift = false; return true; } else if( strncmp(line_tmp, "CTRL", strlen("CTRL")) == 0 || strncmp(line_tmp, "CONTROL", strlen("CONTROL")) == 0) { line_tmp = &line_tmp[mj_ducky_get_command_len(line_tmp) + 1]; if(!mj_get_ducky_key(line_tmp, strlen(line_tmp), &dk)) return false; - send_hid_packet(handle, addr, addr_size, rate, dk.mod | 1, dk.hid, plugin_state); + holding_ctrl = true; + send_hid_packet(handle, addr, addr_size, rate, dk.mod, dk.hid, plugin_state); + holding_ctrl = false; return true; } else if(strncmp(line_tmp, "SHIFT", strlen("SHIFT")) == 0) { line_tmp = &line_tmp[mj_ducky_get_command_len(line_tmp) + 1]; if(!mj_get_ducky_key(line_tmp, strlen(line_tmp), &dk)) return false; - send_hid_packet(handle, addr, addr_size, rate, dk.mod | 2, dk.hid, plugin_state); + holding_shift = true; + send_hid_packet(handle, addr, addr_size, rate, dk.mod, dk.hid, plugin_state); + holding_shift = false; return true; } else if( strncmp(line_tmp, "ESC", strlen("ESC")) == 0 || @@ -344,6 +417,10 @@ static bool mj_process_ducky_line( if(!mj_get_ducky_key("SPACE", 5, &dk)) return false; send_hid_packet(handle, addr, addr_size, rate, dk.mod, dk.hid, plugin_state); return true; + } else if(strncmp(line_tmp, "TAB", strlen("TAB")) == 0) { + if(!mj_get_ducky_key("TAB", 3, &dk)) return false; + send_hid_packet(handle, addr, addr_size, rate, dk.mod, dk.hid, plugin_state); + return true; } return false; diff --git a/applications/plugins/mousejacker/mousejacker_ducky.h b/applications/plugins/mousejacker/mousejacker_ducky.h index b4922ed38..e1a422ea7 100644 --- a/applications/plugins/mousejacker/mousejacker_ducky.h +++ b/applications/plugins/mousejacker/mousejacker_ducky.h @@ -21,6 +21,7 @@ typedef struct { } MJDuckyKey; typedef struct { + FuriMutex* mutex; bool ducky_err; bool addr_err; bool is_thread_running; diff --git a/applications/plugins/multi_converter/multi_converter.c b/applications/plugins/multi_converter/multi_converter.c index 590730357..bd2b62587 100644 --- a/applications/plugins/multi_converter/multi_converter.c +++ b/applications/plugins/multi_converter/multi_converter.c @@ -8,10 +8,9 @@ #include "multi_converter_mode_select.h" static void multi_converter_render_callback(Canvas* const canvas, void* ctx) { - const MultiConverterState* multi_converter_state = acquire_mutex((ValueMutex*)ctx, 25); - if(multi_converter_state == NULL) { - return; - } + furi_assert(ctx); + const MultiConverterState* multi_converter_state = ctx; + furi_mutex_acquire(multi_converter_state->mutex, FuriWaitForever); if(multi_converter_state->mode == ModeDisplay) { multi_converter_mode_display_draw(canvas, multi_converter_state); @@ -19,7 +18,7 @@ static void multi_converter_render_callback(Canvas* const canvas, void* ctx) { multi_converter_mode_select_draw(canvas, multi_converter_state); } - release_mutex((ValueMutex*)ctx, multi_converter_state); + furi_mutex_release(multi_converter_state->mutex); } static void @@ -62,8 +61,8 @@ int32_t multi_converter_app(void* p) { MultiConverterState* multi_converter_state = malloc(sizeof(MultiConverterState)); // set mutex for plugin state (different threads can access it) - ValueMutex state_mutex; - if(!init_mutex(&state_mutex, multi_converter_state, sizeof(multi_converter_state))) { + multi_converter_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!multi_converter_state->mutex) { FURI_LOG_E("MultiConverter", "cannot create mutex\r\n"); furi_message_queue_free(event_queue); free(multi_converter_state); @@ -72,11 +71,11 @@ int32_t multi_converter_app(void* p) { // register callbacks for drawing and input processing ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, multi_converter_render_callback, &state_mutex); + view_port_draw_callback_set(view_port, multi_converter_render_callback, multi_converter_state); view_port_input_callback_set(view_port, multi_converter_input_callback, event_queue); // open GUI and register view_port - Gui* gui = furi_record_open("gui"); + Gui* gui = furi_record_open(RECORD_GUI); gui_add_view_port(gui, view_port, GuiLayerFullscreen); multi_converter_init(multi_converter_state); @@ -85,8 +84,7 @@ int32_t multi_converter_app(void* p) { MultiConverterEvent event; for(bool processing = true; processing;) { FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); - MultiConverterState* multi_converter_state = - (MultiConverterState*)acquire_mutex_block(&state_mutex); + furi_mutex_acquire(multi_converter_state->mutex, FuriWaitForever); if(event_status == FuriStatusOk) { // press events @@ -148,20 +146,18 @@ int32_t multi_converter_app(void* p) { } else if(multi_converter_state->keyboard_lock) { multi_converter_state->keyboard_lock = 0; } - } else { - // event timeout } view_port_update(view_port); - release_mutex(&state_mutex, multi_converter_state); + furi_mutex_release(multi_converter_state->mutex); } view_port_enabled_set(view_port, false); gui_remove_view_port(gui, view_port); - furi_record_close("gui"); + furi_record_close(RECORD_GUI); view_port_free(view_port); furi_message_queue_free(event_queue); - delete_mutex(&state_mutex); + furi_mutex_free(multi_converter_state->mutex); free(multi_converter_state); return 0; diff --git a/applications/plugins/multi_converter/multi_converter_definitions.h b/applications/plugins/multi_converter/multi_converter_definitions.h index 3bed192a0..2f79bc7d9 100644 --- a/applications/plugins/multi_converter/multi_converter_definitions.h +++ b/applications/plugins/multi_converter/multi_converter_definitions.h @@ -70,6 +70,7 @@ struct MultiConverterUnit { }; struct MultiConverterState { + FuriMutex* mutex; char buffer_orig[MULTI_CONVERTER_NUMBER_DIGITS + 1]; char buffer_dest[MULTI_CONVERTER_NUMBER_DIGITS + 1]; MultiConverterUnitType unit_type_orig; diff --git a/applications/plugins/dice/application.fam b/applications/plugins/multi_dice/application.fam similarity index 90% rename from applications/plugins/dice/application.fam rename to applications/plugins/multi_dice/application.fam index bd836c48c..15f5fe820 100644 --- a/applications/plugins/dice/application.fam +++ b/applications/plugins/multi_dice/application.fam @@ -1,5 +1,5 @@ App( - appid="Dice", + appid="multi_dice", name="Multi-Dice", apptype=FlipperAppType.EXTERNAL, entry_point="dice_app", diff --git a/applications/plugins/dice/dice.c b/applications/plugins/multi_dice/dice.c similarity index 98% rename from applications/plugins/dice/dice.c rename to applications/plugins/multi_dice/dice.c index dc748b68f..2de407cf0 100644 --- a/applications/plugins/dice/dice.c +++ b/applications/plugins/multi_dice/dice.c @@ -4,7 +4,6 @@ #include #include #include -#include #define TAG "Dice Roller" @@ -290,10 +289,6 @@ static void dice_render_callback(Canvas* const canvas, void* ctx) { state->diceQty, state->diceType[0], state->rollTime[0]); - if(state->diceSelect >= 20 && state->diceRoll == state->diceSelect) - DOLPHIN_DEED(getRandomDeed()); - if(state->diceSelect >= 20 && state->diceRoll == state->diceSelect - 1) - DOLPHIN_DEED(getRandomDeed()); if(state->diceQty == 1) { snprintf(state->strings[1], sizeof(state->strings[1]), "%d", state->diceRoll); } else if(state->diceQty == 2) { diff --git a/applications/plugins/dice/dice.png b/applications/plugins/multi_dice/dice.png similarity index 100% rename from applications/plugins/dice/dice.png rename to applications/plugins/multi_dice/dice.png diff --git a/applications/plugins/music_beeper/application.fam b/applications/plugins/music_beeper/application.fam index 2a83867bf..39b9babba 100644 --- a/applications/plugins/music_beeper/application.fam +++ b/applications/plugins/music_beeper/application.fam @@ -11,9 +11,9 @@ App( provides=["music_beeper_start"], stack_size=2 * 1024, order=45, - fap_icon="icons/music_10px.png", - fap_category="Music", + fap_icon="music_10px.png", fap_icon_assets="icons", + fap_category="Music", ) App( diff --git a/applications/plugins/music_beeper/music_10px.png b/applications/plugins/music_beeper/music_10px.png new file mode 100644 index 000000000..d41eb0db8 Binary files /dev/null and b/applications/plugins/music_beeper/music_10px.png differ diff --git a/applications/plugins/music_beeper/music_beeper_worker.c b/applications/plugins/music_beeper/music_beeper_worker.c index 4523b806e..e06e77447 100644 --- a/applications/plugins/music_beeper/music_beeper_worker.c +++ b/applications/plugins/music_beeper/music_beeper_worker.c @@ -101,8 +101,11 @@ MusicBeeperWorker* music_beeper_worker_alloc() { NoteBlockArray_init(instance->notes); - instance->thread = furi_thread_alloc_ex( - "MusicBeeperWorker", 1024, music_beeper_worker_thread_callback, instance); + instance->thread = furi_thread_alloc(); + furi_thread_set_name(instance->thread, "MusicBeeperWorker"); + furi_thread_set_stack_size(instance->thread, 1024); + furi_thread_set_context(instance->thread, instance); + furi_thread_set_callback(instance->thread, music_beeper_worker_thread_callback); instance->volume = 1.0f; diff --git a/applications/plugins/music_player/application.fam b/applications/plugins/music_player/application.fam index 1072f1524..07f489072 100644 --- a/applications/plugins/music_player/application.fam +++ b/applications/plugins/music_player/application.fam @@ -1,7 +1,7 @@ App( appid="Music_Player", name="Music Player", - apptype=FlipperAppType.EXTERNAL, + apptype=FlipperAppType.PLUGIN, entry_point="music_player_app", cdefines=["APP_MUSIC_PLAYER"], requires=[ diff --git a/applications/plugins/music_player/music_player.c b/applications/plugins/music_player/music_player.c index 919a7bfab..71840ff92 100644 --- a/applications/plugins/music_player/music_player.c +++ b/applications/plugins/music_player/music_player.c @@ -258,7 +258,7 @@ MusicPlayer* music_player_alloc() { MusicPlayer* instance = malloc(sizeof(MusicPlayer)); instance->model = malloc(sizeof(MusicPlayerModel)); - instance->model->volume = 4; + instance->model->volume = 3; instance->model_mutex = furi_mutex_alloc(FuriMutexTypeNormal); diff --git a/applications/plugins/musictracker/application.fam b/applications/plugins/musictracker/application.fam index 51a0e839d..c0f7edca6 100644 --- a/applications/plugins/musictracker/application.fam +++ b/applications/plugins/musictracker/application.fam @@ -1,7 +1,7 @@ App( - appid="Zero_Tracker", + appid="zero_tracker", name="Zero Tracker", - apptype=FlipperAppType.EXTERNAL, + apptype=FlipperAppType.PLUGIN, entry_point="zero_tracker_app", requires=[ "gui", diff --git a/applications/plugins/musictracker/tracker_engine/speaker_hal.c b/applications/plugins/musictracker/tracker_engine/speaker_hal.c index 14a9c4f85..94489f1b6 100644 --- a/applications/plugins/musictracker/tracker_engine/speaker_hal.c +++ b/applications/plugins/musictracker/tracker_engine/speaker_hal.c @@ -40,7 +40,7 @@ void tracker_speaker_stop() { } void tracker_speaker_init() { - if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(30)) { + if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(1000)) { furi_hal_speaker_start(200.0f, 0.01f); tracker_speaker_stop(); } diff --git a/applications/plugins/namechanger/application.fam b/applications/plugins/namechanger/application.fam deleted file mode 100644 index 704b643c5..000000000 --- a/applications/plugins/namechanger/application.fam +++ /dev/null @@ -1,13 +0,0 @@ -App( - appid="NameChanger", - name="Name Changer", - apptype=FlipperAppType.EXTERNAL, - entry_point="namechanger_app", - cdefines=["APP_NAMECHANGER"], - requires=["gui", "storage"], - stack_size=2 * 1024, - order=90, - fap_icon="namechanger_10px.png", - fap_category="Tools", - fap_icon_assets="icons", -) diff --git a/applications/plugins/namechanger/icons/DolphinNice_96x59.png b/applications/plugins/namechanger/icons/DolphinNice_96x59.png deleted file mode 100644 index a299d3630..000000000 Binary files a/applications/plugins/namechanger/icons/DolphinNice_96x59.png and /dev/null differ diff --git a/applications/plugins/namechanger/icons/MarioBlock.png b/applications/plugins/namechanger/icons/MarioBlock.png deleted file mode 100644 index 86f159966..000000000 Binary files a/applications/plugins/namechanger/icons/MarioBlock.png and /dev/null differ diff --git a/applications/plugins/namechanger/icons/namechanger_10px.png b/applications/plugins/namechanger/icons/namechanger_10px.png deleted file mode 100644 index 60facf25e..000000000 Binary files a/applications/plugins/namechanger/icons/namechanger_10px.png and /dev/null differ diff --git a/applications/plugins/namechanger/namechanger.c b/applications/plugins/namechanger/namechanger.c deleted file mode 100644 index 17abcea72..000000000 --- a/applications/plugins/namechanger/namechanger.c +++ /dev/null @@ -1,162 +0,0 @@ -#include "namechanger.h" -#include "scenes/namechanger_scene.h" - -#include -#include - -bool namechanger_custom_event_callback(void* context, uint32_t event) { - furi_assert(context); - NameChanger* namechanger = context; - return scene_manager_handle_custom_event(namechanger->scene_manager, event); -} - -bool namechanger_back_event_callback(void* context) { - furi_assert(context); - NameChanger* namechanger = context; - return scene_manager_handle_back_event(namechanger->scene_manager); -} - -NameChanger* namechanger_alloc() { - NameChanger* namechanger = malloc(sizeof(NameChanger)); - - namechanger->scene_manager = scene_manager_alloc(&namechanger_scene_handlers, namechanger); - - namechanger->view_dispatcher = view_dispatcher_alloc(); - view_dispatcher_enable_queue(namechanger->view_dispatcher); - view_dispatcher_set_event_callback_context(namechanger->view_dispatcher, namechanger); - view_dispatcher_set_custom_event_callback( - namechanger->view_dispatcher, namechanger_custom_event_callback); - view_dispatcher_set_navigation_event_callback( - namechanger->view_dispatcher, namechanger_back_event_callback); - - namechanger->gui = furi_record_open(RECORD_GUI); - namechanger->storage = furi_record_open(RECORD_STORAGE); - - namechanger->submenu = submenu_alloc(); - view_dispatcher_add_view( - namechanger->view_dispatcher, - NameChangerViewSubmenu, - submenu_get_view(namechanger->submenu)); - - namechanger->text_input = text_input_alloc(); - view_dispatcher_add_view( - namechanger->view_dispatcher, - NameChangerViewTextInput, - text_input_get_view(namechanger->text_input)); - - namechanger->popup = popup_alloc(); - view_dispatcher_add_view( - namechanger->view_dispatcher, NameChangerViewPopup, popup_get_view(namechanger->popup)); - - namechanger->widget = widget_alloc(); - view_dispatcher_add_view( - namechanger->view_dispatcher, NameChangerViewWidget, widget_get_view(namechanger->widget)); - - return namechanger; -} - -void namechanger_free(NameChanger* namechanger) { - furi_assert(namechanger); - - view_dispatcher_remove_view(namechanger->view_dispatcher, NameChangerViewWidget); - widget_free(namechanger->widget); - view_dispatcher_remove_view(namechanger->view_dispatcher, NameChangerViewPopup); - popup_free(namechanger->popup); - - view_dispatcher_remove_view(namechanger->view_dispatcher, NameChangerViewTextInput); - text_input_free(namechanger->text_input); - - view_dispatcher_remove_view(namechanger->view_dispatcher, NameChangerViewSubmenu); - submenu_free(namechanger->submenu); - - view_dispatcher_free(namechanger->view_dispatcher); - scene_manager_free(namechanger->scene_manager); - - furi_record_close(RECORD_STORAGE); - - furi_record_close(RECORD_GUI); - - free(namechanger); -} - -void namechanger_text_store_set(NameChanger* namechanger, const char* text, ...) { - va_list args; - va_start(args, text); - - vsnprintf(namechanger->text_store, NAMECHANGER_TEXT_STORE_SIZE, text, args); - - va_end(args); -} - -int32_t namechanger_app() { - NameChanger* namechanger = namechanger_alloc(); - - view_dispatcher_attach_to_gui( - namechanger->view_dispatcher, namechanger->gui, ViewDispatcherTypeFullscreen); - scene_manager_next_scene(namechanger->scene_manager, NameChangerSceneStart); - - view_dispatcher_run(namechanger->view_dispatcher); - - namechanger_free(namechanger); - return 0; -} - -bool namechanger_name_write(NameChanger* namechanger, char* name) { - FuriString* file_path = furi_string_alloc(); - furi_string_set(file_path, "/ext/dolphin/name.txt"); - - bool result = false; - - //If name is not "eraseerase" (set by Revert) then write name to file - //otherwise, remove name.txt - - if(strcmp(name, "eraseerase") != 0) { - //save - FlipperFormat* file = flipper_format_file_alloc(namechanger->storage); - - do { - // Open file for write - if(!flipper_format_file_open_always(file, furi_string_get_cstr(file_path))) { - break; - } - - // Write header - if(!flipper_format_write_header_cstr(file, NAMECHANGER_HEADER, 1)) { - break; - } - - // Write comments - if(!flipper_format_write_comment_cstr( - file, "Changing the value below will change your FlipperZero device name.")) { - break; - } - - if(!flipper_format_write_comment_cstr( - file, - "Note: This is limited to 8 characters using the following: a-z, A-Z, 0-9, and _")) { - break; - } - - if(!flipper_format_write_comment_cstr( - file, "It cannot contain any other characters.")) { - break; - } - - if(!flipper_format_write_string_cstr(file, "Name", name)) { - break; - } - - result = true; - } while(false); - - flipper_format_free(file); - - if(!result) { - FURI_LOG_E(TAG, "Cannot save name file."); - } - } else { - result = storage_simply_remove(namechanger->storage, furi_string_get_cstr(file_path)); - } - - return result; -} \ No newline at end of file diff --git a/applications/plugins/namechanger/namechanger.h b/applications/plugins/namechanger/namechanger.h deleted file mode 100644 index e3355db1d..000000000 --- a/applications/plugins/namechanger/namechanger.h +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include - -#include "namechanger_custom_event.h" -#include "scenes/namechanger_scene.h" - -#define NAMECHANGER_TEXT_STORE_SIZE 9 -#define NAMECHANGER_HEADER "Flipper Name File" - -#define TAG "NameChanger" - -typedef struct { - SceneManager* scene_manager; - ViewDispatcher* view_dispatcher; - - Gui* gui; - Storage* storage; - - char text_store[NAMECHANGER_TEXT_STORE_SIZE + 1]; - - Submenu* submenu; - TextInput* text_input; - Popup* popup; - Widget* widget; -} NameChanger; - -typedef enum { - NameChangerViewSubmenu, - NameChangerViewTextInput, - NameChangerViewPopup, - NameChangerViewWidget, -} NameChangerView; - -bool namechanger_make_app_folder(NameChanger* namechanger); -bool namechanger_name_write(NameChanger* namechanger, char* name); -void namechanger_text_store_set(NameChanger* namechanger, const char* text, ...); diff --git a/applications/plugins/namechanger/namechanger_10px.png b/applications/plugins/namechanger/namechanger_10px.png deleted file mode 100644 index 60facf25e..000000000 Binary files a/applications/plugins/namechanger/namechanger_10px.png and /dev/null differ diff --git a/applications/plugins/namechanger/namechanger_custom_event.h b/applications/plugins/namechanger/namechanger_custom_event.h deleted file mode 100644 index 3485c870b..000000000 --- a/applications/plugins/namechanger/namechanger_custom_event.h +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once - -enum NameChangerCustomEvent { - NameChangerCustomEventBack, - NameChangerCustomEventTextEditResult, -}; diff --git a/applications/plugins/namechanger/scenes/namechanger_scene.c b/applications/plugins/namechanger/scenes/namechanger_scene.c deleted file mode 100644 index 82f96e466..000000000 --- a/applications/plugins/namechanger/scenes/namechanger_scene.c +++ /dev/null @@ -1,30 +0,0 @@ -#include "namechanger_scene.h" - -// Generate scene on_enter handlers array -#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, -void (*const namechanger_on_enter_handlers[])(void*) = { -#include "namechanger_scene_config.h" -}; -#undef ADD_SCENE - -// Generate scene on_event handlers array -#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, -bool (*const namechanger_on_event_handlers[])(void* context, SceneManagerEvent event) = { -#include "namechanger_scene_config.h" -}; -#undef ADD_SCENE - -// Generate scene on_exit handlers array -#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, -void (*const namechanger_on_exit_handlers[])(void* context) = { -#include "namechanger_scene_config.h" -}; -#undef ADD_SCENE - -// Initialize scene handlers configuration structure -const SceneManagerHandlers namechanger_scene_handlers = { - .on_enter_handlers = namechanger_on_enter_handlers, - .on_event_handlers = namechanger_on_event_handlers, - .on_exit_handlers = namechanger_on_exit_handlers, - .scene_num = NameChangerSceneNum, -}; diff --git a/applications/plugins/namechanger/scenes/namechanger_scene_change.c b/applications/plugins/namechanger/scenes/namechanger_scene_change.c deleted file mode 100644 index 32977d51a..000000000 --- a/applications/plugins/namechanger/scenes/namechanger_scene_change.c +++ /dev/null @@ -1,56 +0,0 @@ -#include "../namechanger.h" - -static void namechanger_scene_change_text_input_callback(void* context) { - NameChanger* namechanger = context; - - view_dispatcher_send_custom_event( - namechanger->view_dispatcher, NameChangerCustomEventTextEditResult); -} - -void namechanger_scene_change_on_enter(void* context) { - NameChanger* namechanger = context; - TextInput* text_input = namechanger->text_input; - - namechanger_text_store_set(namechanger, "%s", furi_hal_version_get_name_ptr()); - - text_input_set_header_text(text_input, "Set Flipper Name"); - - text_input_set_result_callback( - text_input, - namechanger_scene_change_text_input_callback, - namechanger, - namechanger->text_store, - NAMECHANGER_TEXT_STORE_SIZE, - true); - - view_dispatcher_switch_to_view(namechanger->view_dispatcher, NameChangerViewTextInput); -} - -bool namechanger_scene_change_on_event(void* context, SceneManagerEvent event) { - NameChanger* namechanger = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - consumed = true; - if(event.event == NameChangerCustomEventTextEditResult) { - if(namechanger_name_write(namechanger, namechanger->text_store)) { - scene_manager_next_scene( - namechanger->scene_manager, NameChangerSceneChangeSuccess); - } else { - scene_manager_search_and_switch_to_previous_scene( - namechanger->scene_manager, NameChangerSceneStart); - } - } else { - scene_manager_search_and_switch_to_previous_scene( - namechanger->scene_manager, NameChangerSceneStart); - } - } - return consumed; -} - -void namechanger_scene_change_on_exit(void* context) { - NameChanger* namechanger = context; - TextInput* text_input = namechanger->text_input; - - text_input_reset(text_input); -} diff --git a/applications/plugins/namechanger/scenes/namechanger_scene_change_success.c b/applications/plugins/namechanger/scenes/namechanger_scene_change_success.c deleted file mode 100644 index 7574c3a0e..000000000 --- a/applications/plugins/namechanger/scenes/namechanger_scene_change_success.c +++ /dev/null @@ -1,50 +0,0 @@ -#include "../namechanger.h" - -static void namechanger_scene_change_success_popup_callback(void* context) { - NameChanger* namechanger = context; - view_dispatcher_send_custom_event(namechanger->view_dispatcher, NameChangerCustomEventBack); -} - -void namechanger_scene_change_success_on_enter(void* context) { - NameChanger* namechanger = context; - Popup* popup = namechanger->popup; - - popup_set_header(popup, "Saved!", 5, 5, AlignLeft, AlignTop); - popup_set_text(popup, "Rebooting...", 5, 17, AlignLeft, AlignTop); - - popup_set_callback(popup, namechanger_scene_change_success_popup_callback); - popup_set_context(popup, namechanger); - popup_set_timeout(popup, 5000); - popup_enable_timeout(popup); - - view_dispatcher_switch_to_view(namechanger->view_dispatcher, NameChangerViewPopup); -} - -bool namechanger_scene_change_success_on_event(void* context, SceneManagerEvent event) { - NameChanger* namechanger = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - consumed = true; - if(event.event == NameChangerCustomEventBack) { - scene_manager_search_and_switch_to_previous_scene( - namechanger->scene_manager, NameChangerSceneChange); - } - } - - return consumed; -} - -void namechanger_scene_change_success_on_exit(void* context) { - NameChanger* namechanger = context; - Popup* popup = namechanger->popup; - - popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); - popup_set_icon(popup, 0, 0, NULL); - - popup_disable_timeout(popup); - popup_set_context(popup, NULL); - popup_set_callback(popup, NULL); - - furi_hal_power_reset(); -} diff --git a/applications/plugins/namechanger/scenes/namechanger_scene_config.h b/applications/plugins/namechanger/scenes/namechanger_scene_config.h deleted file mode 100644 index 41bbaef6b..000000000 --- a/applications/plugins/namechanger/scenes/namechanger_scene_config.h +++ /dev/null @@ -1,5 +0,0 @@ -ADD_SCENE(namechanger, start, Start) -ADD_SCENE(namechanger, change, Change) -ADD_SCENE(namechanger, change_success, ChangeSuccess) -ADD_SCENE(namechanger, revert, Revert) -ADD_SCENE(namechanger, revert_success, RevertSuccess) \ No newline at end of file diff --git a/applications/plugins/namechanger/scenes/namechanger_scene_revert.c b/applications/plugins/namechanger/scenes/namechanger_scene_revert.c deleted file mode 100644 index 156817651..000000000 --- a/applications/plugins/namechanger/scenes/namechanger_scene_revert.c +++ /dev/null @@ -1,53 +0,0 @@ -#include "../namechanger.h" - -static void - namechanger_scene_revert_widget_callback(GuiButtonType result, InputType type, void* context) { - NameChanger* namechanger = context; - if(type == InputTypeShort) { - view_dispatcher_send_custom_event(namechanger->view_dispatcher, result); - } -} - -void namechanger_scene_revert_on_enter(void* context) { - NameChanger* namechanger = context; - Widget* widget = namechanger->widget; - widget_add_text_box_element( - widget, 0, 0, 128, 25, AlignCenter, AlignCenter, "\e#Revert Name?\e#", false); - widget_add_button_element( - widget, GuiButtonTypeLeft, "Cancel", namechanger_scene_revert_widget_callback, namechanger); - widget_add_button_element( - widget, - GuiButtonTypeRight, - "Revert", - namechanger_scene_revert_widget_callback, - namechanger); - view_dispatcher_switch_to_view(namechanger->view_dispatcher, NameChangerViewWidget); -} - -bool namechanger_scene_revert_on_event(void* context, SceneManagerEvent event) { - NameChanger* namechanger = context; - bool consumed = false; - if(event.type == SceneManagerEventTypeBack) { - consumed = true; - } else if(event.type == SceneManagerEventTypeCustom) { - consumed = true; - if(event.event == GuiButtonTypeRight) { - if(namechanger_name_write(namechanger, "eraseerase")) { - scene_manager_next_scene( - namechanger->scene_manager, NameChangerSceneRevertSuccess); - } else { - scene_manager_search_and_switch_to_previous_scene( - namechanger->scene_manager, NameChangerSceneStart); - } - } else if(event.event == GuiButtonTypeLeft) { - scene_manager_search_and_switch_to_previous_scene( - namechanger->scene_manager, NameChangerSceneStart); - } - } - return consumed; -} - -void namechanger_scene_revert_on_exit(void* context) { - NameChanger* namechanger = context; - widget_reset(namechanger->widget); -} diff --git a/applications/plugins/namechanger/scenes/namechanger_scene_revert_success.c b/applications/plugins/namechanger/scenes/namechanger_scene_revert_success.c deleted file mode 100644 index 354a6eadf..000000000 --- a/applications/plugins/namechanger/scenes/namechanger_scene_revert_success.c +++ /dev/null @@ -1,54 +0,0 @@ -#include "../namechanger.h" - -static void namechanger_scene_revert_success_popup_callback(void* context) { - NameChanger* namechanger = context; - view_dispatcher_send_custom_event(namechanger->view_dispatcher, NameChangerCustomEventBack); -} - -void namechanger_scene_revert_success_on_enter(void* context) { - NameChanger* namechanger = context; - Popup* popup = namechanger->popup; - - popup_set_header(popup, "Reverted!", 70, 5, AlignLeft, AlignTop); - popup_set_text(popup, "Rebooting...", 70, 16, AlignLeft, AlignTop); - - popup_set_callback(popup, namechanger_scene_revert_success_popup_callback); - popup_set_context(popup, namechanger); - popup_set_timeout(popup, 5000); - popup_enable_timeout(popup); - - view_dispatcher_switch_to_view(namechanger->view_dispatcher, NameChangerViewPopup); -} - -bool namechanger_scene_revert_success_on_event(void* context, SceneManagerEvent event) { - NameChanger* namechanger = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeBack) { - consumed = true; - scene_manager_search_and_switch_to_previous_scene( - namechanger->scene_manager, NameChangerSceneStart); - } else if(event.type == SceneManagerEventTypeCustom) { - consumed = true; - if(event.event == NameChangerCustomEventBack) { - scene_manager_search_and_switch_to_previous_scene( - namechanger->scene_manager, NameChangerSceneStart); - } - } - - return consumed; -} - -void namechanger_scene_revert_success_on_exit(void* context) { - NameChanger* namechanger = context; - Popup* popup = namechanger->popup; - - popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); - popup_set_icon(popup, 0, 0, NULL); - - popup_disable_timeout(popup); - popup_set_context(popup, NULL); - popup_set_callback(popup, NULL); - - furi_hal_power_reset(); -} \ No newline at end of file diff --git a/applications/plugins/namechanger/scenes/namechanger_scene_start.c b/applications/plugins/namechanger/scenes/namechanger_scene_start.c deleted file mode 100644 index 3fe93f5a2..000000000 --- a/applications/plugins/namechanger/scenes/namechanger_scene_start.c +++ /dev/null @@ -1,58 +0,0 @@ -#include "../namechanger.h" - -enum SubmenuIndex { - SubmenuIndexChange, - SubmenuIndexRevert, -}; - -void namechanger_scene_start_submenu_callback(void* context, uint32_t index) { - NameChanger* namechanger = context; - view_dispatcher_send_custom_event(namechanger->view_dispatcher, index); -} - -void namechanger_scene_start_on_enter(void* context) { - NameChanger* namechanger = context; - Submenu* submenu = namechanger->submenu; - - submenu_add_item( - submenu, - "Change", - SubmenuIndexChange, - namechanger_scene_start_submenu_callback, - namechanger); - - submenu_add_item( - submenu, - "Revert", - SubmenuIndexRevert, - namechanger_scene_start_submenu_callback, - namechanger); - - submenu_set_selected_item( - submenu, scene_manager_get_scene_state(namechanger->scene_manager, NameChangerSceneStart)); - - view_dispatcher_switch_to_view(namechanger->view_dispatcher, NameChangerViewSubmenu); -} - -bool namechanger_scene_start_on_event(void* context, SceneManagerEvent event) { - NameChanger* namechanger = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - scene_manager_set_scene_state( - namechanger->scene_manager, NameChangerSceneStart, event.event); - consumed = true; - if(event.event == SubmenuIndexChange) { - scene_manager_next_scene(namechanger->scene_manager, NameChangerSceneChange); - } - if(event.event == SubmenuIndexRevert) { - scene_manager_next_scene(namechanger->scene_manager, NameChangerSceneRevert); - } - } - return consumed; -} - -void namechanger_scene_start_on_exit(void* context) { - NameChanger* namechanger = context; - submenu_reset(namechanger->submenu); -} diff --git a/applications/plugins/nfc_magic/application.fam b/applications/plugins/nfc_magic/application.fam index 427524051..db0af81d4 100644 --- a/applications/plugins/nfc_magic/application.fam +++ b/applications/plugins/nfc_magic/application.fam @@ -1,5 +1,5 @@ App( - appid="NFC_Magic", + appid="nfc_magic", name="NFC Magic", apptype=FlipperAppType.EXTERNAL, targets=["f7"], diff --git a/applications/plugins/nfc_magic/nfc_magic.c b/applications/plugins/nfc_magic/nfc_magic.c index e4e0ffde0..1805f35ed 100644 --- a/applications/plugins/nfc_magic/nfc_magic.c +++ b/applications/plugins/nfc_magic/nfc_magic.c @@ -136,9 +136,9 @@ void nfc_magic_free(NfcMagic* nfc_magic) { free(nfc_magic); } -static const NotificationSequence nfc_magic_sequence_blink_start_blue = { +static const NotificationSequence nfc_magic_sequence_blink_start_cyan = { &message_blink_start_10, - &message_blink_set_color_blue, + &message_blink_set_color_cyan, &message_do_not_reset, NULL, }; @@ -149,7 +149,7 @@ static const NotificationSequence nfc_magic_sequence_blink_stop = { }; void nfc_magic_blink_start(NfcMagic* nfc_magic) { - notification_message(nfc_magic->notifications, &nfc_magic_sequence_blink_start_blue); + notification_message(nfc_magic->notifications, &nfc_magic_sequence_blink_start_cyan); } void nfc_magic_blink_stop(NfcMagic* nfc_magic) { diff --git a/applications/plugins/nfc_magic/nfc_magic_i.h b/applications/plugins/nfc_magic/nfc_magic_i.h index a6f9352b4..378912e5b 100644 --- a/applications/plugins/nfc_magic/nfc_magic_i.h +++ b/applications/plugins/nfc_magic/nfc_magic_i.h @@ -25,7 +25,7 @@ #include #include -#include "NFC_Magic_icons.h" +#include "nfc_magic_icons.h" #define NFC_APP_FOLDER ANY_PATH("nfc") diff --git a/applications/plugins/nfc_magic/nfc_magic_worker.c b/applications/plugins/nfc_magic/nfc_magic_worker.c index 523c794f7..32202f12d 100644 --- a/applications/plugins/nfc_magic/nfc_magic_worker.c +++ b/applications/plugins/nfc_magic/nfc_magic_worker.c @@ -49,6 +49,9 @@ void nfc_magic_worker_start( furi_assert(nfc_magic_worker); furi_assert(dev_data); + furi_hal_nfc_deinit(); + furi_hal_nfc_init(); + nfc_magic_worker->callback = callback; nfc_magic_worker->context = context; nfc_magic_worker->dev_data = dev_data; diff --git a/applications/plugins/nfc_magic/scenes/nfc_magic_scene_file_select.c b/applications/plugins/nfc_magic/scenes/nfc_magic_scene_file_select.c index a19237ed4..d78422eeb 100644 --- a/applications/plugins/nfc_magic/scenes/nfc_magic_scene_file_select.c +++ b/applications/plugins/nfc_magic/scenes/nfc_magic_scene_file_select.c @@ -11,6 +11,10 @@ void nfc_magic_scene_file_select_on_enter(void* context) { // Process file_select return nfc_device_set_loading_callback(nfc_magic->nfc_dev, nfc_magic_show_loading_popup, nfc_magic); + if(!furi_string_size(nfc_magic->nfc_dev->load_path)) { + furi_string_set_str(nfc_magic->nfc_dev->load_path, NFC_APP_FOLDER); + } + if(nfc_file_select(nfc_magic->nfc_dev)) { if(nfc_magic_scene_file_select_is_file_suitable(nfc_magic->nfc_dev)) { scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneWriteConfirm); diff --git a/applications/plugins/nfc_magic/scenes/nfc_magic_scene_start.c b/applications/plugins/nfc_magic/scenes/nfc_magic_scene_start.c index f2984443f..a70eb8acc 100644 --- a/applications/plugins/nfc_magic/scenes/nfc_magic_scene_start.c +++ b/applications/plugins/nfc_magic/scenes/nfc_magic_scene_start.c @@ -40,16 +40,24 @@ bool nfc_magic_scene_start_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == SubmenuIndexCheck) { + scene_manager_set_scene_state( + nfc_magic->scene_manager, NfcMagicSceneStart, SubmenuIndexCheck); scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneCheck); consumed = true; } else if(event.event == SubmenuIndexWriteGen1A) { + // Explicitly save state in each branch so that the + // correct option is reselected if the user cancels + // loading a file. + scene_manager_set_scene_state( + nfc_magic->scene_manager, NfcMagicSceneStart, SubmenuIndexWriteGen1A); scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneFileSelect); consumed = true; } else if(event.event == SubmenuIndexWipe) { + scene_manager_set_scene_state( + nfc_magic->scene_manager, NfcMagicSceneStart, SubmenuIndexWipe); scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneWipe); consumed = true; } - scene_manager_set_scene_state(nfc_magic->scene_manager, NfcMagicSceneStart, event.event); } return consumed; diff --git a/applications/plugins/nightstand/application.fam b/applications/plugins/nightstand/application.fam index 0b8cbf83a..ea28c7b77 100644 --- a/applications/plugins/nightstand/application.fam +++ b/applications/plugins/nightstand/application.fam @@ -1,12 +1,12 @@ App( - appid="Nightstand", + appid="nightstand", name="Nightstand Clock", apptype=FlipperAppType.EXTERNAL, entry_point="clock_app", requires=["gui"], icon="A_Clock_14", stack_size=2 * 1024, - fap_icon="ClockIcon.png", + fap_icon="clock.png", fap_category="Misc", order=81, ) diff --git a/applications/plugins/nightstand/ClockIcon.png b/applications/plugins/nightstand/clock.png similarity index 100% rename from applications/plugins/nightstand/ClockIcon.png rename to applications/plugins/nightstand/clock.png diff --git a/applications/plugins/nightstand/clock_app.c b/applications/plugins/nightstand/clock_app.c index d7a9f7faa..33dbb49a4 100644 --- a/applications/plugins/nightstand/clock_app.c +++ b/applications/plugins/nightstand/clock_app.c @@ -11,13 +11,16 @@ /* This is a modified version of the default clock app intended for use overnight - Up / Down control the displays brightness. Down at brightness 0 turns the notification LED on and off. + Up / Down controls the displays brightness. Down at brightness 0 turns the notification LED on and off. */ int brightness = 5; bool led = false; NotificationApp* notif = 0; +int dspBrightnessBarFrames = 0; +const int dspBrightnessBarDisplayFrames = 8; + const NotificationMessage message_red_dim = { .type = NotificationMessageTypeLedRed, .data.led.value = 0xFF / 16, @@ -51,6 +54,7 @@ void set_backlight_brightness(float brightness) { } void handle_up() { + dspBrightnessBarFrames = dspBrightnessBarDisplayFrames; if(brightness < 100) { led = false; notification_message(notif, &led_off); @@ -60,6 +64,7 @@ void handle_up() { } void handle_down() { + dspBrightnessBarFrames = dspBrightnessBarDisplayFrames; if(brightness > 0) { brightness -= 5; if(brightness == 0) { //trigger only on the first brightness 5 -> 0 transition @@ -83,6 +88,29 @@ static void clock_input_callback(InputEvent* input_event, FuriMessageQueue* even furi_message_queue_put(event_queue, &event, FuriWaitForever); } +//do you are have stupid? +void elements_progress_bar_vertical( + Canvas* canvas, + uint8_t x, + uint8_t y, + uint8_t height, + float progress) { + furi_assert(canvas); + furi_assert((progress >= 0) && (progress <= 1.0)); + uint8_t width = 9; + + uint8_t progress_length = roundf((1.f - progress) * (height - 2)); + + canvas_set_color(canvas, ColorBlack); + canvas_draw_box(canvas, x + 1, y + 1, width - 2, height - 2); + + canvas_set_color(canvas, ColorWhite); + canvas_draw_box(canvas, x + 1, y + 1, width - 2, progress_length); + + canvas_set_color(canvas, ColorBlack); + canvas_draw_rframe(canvas, x, y, width, height, 3); +} + static void clock_render_callback(Canvas* const canvas, void* ctx) { //canvas_clear(canvas); //canvas_set_color(canvas, ColorBlack); @@ -90,6 +118,11 @@ static void clock_render_callback(Canvas* const canvas, void* ctx) { //avoids a bug with the brightness being reverted after the backlight-off period set_backlight_brightness((float)(brightness / 100.f)); + if(dspBrightnessBarFrames > 0) { + elements_progress_bar_vertical(canvas, 119, 1, 62, (float)(brightness / 100.f)); + dspBrightnessBarFrames--; + } + ClockState* state = ctx; if(furi_mutex_acquire(state->mutex, 200) != FuriStatusOk) { //FURI_LOG_D(TAG, "Can't obtain mutex, requeue render"); @@ -270,7 +303,7 @@ int32_t clock_app(void* p) { notif = furi_record_open(RECORD_NOTIFICATION); float tmpBrightness = notif->settings.display_brightness; - brightness = tmpBrightness * 100; + brightness = tmpBrightness * 100; // Keep current brightness by default notification_message(notif, &sequence_display_backlight_enforce_on); notification_message(notif, &led_off); diff --git a/applications/plugins/nrf24scan/README.md b/applications/plugins/nrf24scan/README.md index a93ec913e..f2c50fde5 100644 --- a/applications/plugins/nrf24scan/README.md +++ b/applications/plugins/nrf24scan/README.md @@ -59,6 +59,21 @@ OK - вход в режим просмотра адресов и включен

+Схема подключения модуля nRF24l01:
+Big board:
+ +Gerber for full board (https://raw.githubusercontent.com/vad7/nrf24scan/master/Gerber_PCB_Flipper%20Zero%20nRF24%20board_v1_0.zip)
+Easyeda source (https://oshwlab.com/vad7/flipper-zero-nrf24-board). +

+Mini board:
+ +
+ +
+Gerber for mini board with 5V - 3.3V converter LM1117-3.3 (https://raw.githubusercontent.com/vad7/nrf24scan/master/Gerber_PCB_Flipper%20Zero%20nRF24%20board%20mini_v1_0.zip)
+Easyeda source (https://oshwlab.com/vad7/flipper-zero-nrf24-board_copy). +
+
_________________________________________________________________________________

diff --git a/applications/plugins/nrf24scan/nrf24scan.c b/applications/plugins/nrf24scan/nrf24scan.c index fafe37e8e..d66e7caf6 100644 --- a/applications/plugins/nrf24scan/nrf24scan.c +++ b/applications/plugins/nrf24scan/nrf24scan.c @@ -14,7 +14,7 @@ #include #define TAG "nrf24scan" -#define VERSION "2.1" +#define VERSION "2.2" #define MAX_CHANNEL 125 #define MAX_ADDR 6 @@ -71,6 +71,7 @@ uint8_t NRF_Payload = 32; // Payload len in bytes or Minimum payload in sniff mo uint8_t NRF_Payload_sniff_min = 0; uint8_t NRF_AA_OFF = 0; // Disable Auto Acknowledgement bool NRF_ERROR = 0; +bool NRF_BOARD_POWER_5V = false; struct ADDRS { uint8_t addr_P0[5]; // MSB first @@ -485,7 +486,13 @@ void check_add_addr(uint8_t* pkt) { static void prepare_nrf24(bool fsend_packet) { nrf24_write_reg(nrf24_HANDLE, REG_STATUS, 0x70); // clear interrupts - nrf24_write_reg(nrf24_HANDLE, REG_RF_SETUP, NRF_rate); + nrf24_write_reg( + nrf24_HANDLE, + REG_RF_SETUP, + (NRF_rate == 0 ? 0b00100000 : + NRF_rate == 1 ? 0 : + 0b00001000) | + 0b111); // +TX high power uint8_t erx_addr = (1 << 0); // Enable RX_P0 struct ADDRS* adr = what_to_do == 1 ? &addrs_sniff : &addrs; if(!fsend_packet) { @@ -517,14 +524,13 @@ static void prepare_nrf24(bool fsend_packet) { } if(what_to_do == 1) { // SNIFF payload = 32; - nrf24_write_reg(nrf24_HANDLE, REG_CONFIG, 0x70); // Mask all interrupts, NO CRC + nrf24_write_reg(nrf24_HANDLE, REG_CONFIG, 0x70); // Mask all interrupts nrf24_write_reg(nrf24_HANDLE, REG_SETUP_RETR, 0); // Automatic Retransmission nrf24_write_reg(nrf24_HANDLE, REG_EN_AA, 0); // Auto acknowledgement nrf24_write_reg( nrf24_HANDLE, REG_FEATURE, 0); // Enables the W_TX_PAYLOAD_NOACK command, Disable Payload with ACK, set Dynamic Payload - nrf24_write_reg(nrf24_HANDLE, REG_RF_CH, NRF_channel); } else if(setup_from_log) { // Scan nrf24_write_reg( nrf24_HANDLE, @@ -947,8 +953,10 @@ bool nrf24_send_packet() { } static void render_callback(Canvas* const canvas, void* ctx) { - const PluginState* plugin_state = acquire_mutex((ValueMutex*)ctx, 25); - if(plugin_state == NULL) return; + furi_assert(ctx); + const PluginState* plugin_state = ctx; + furi_mutex_acquire(plugin_state->mutex, FuriWaitForever); + //canvas_draw_frame(canvas, 0, 0, 128, 64); // border around the edge of the screen if(what_doing == 0) { canvas_set_font(canvas, FontSecondary); // 8x10 font, 6 lines @@ -1317,7 +1325,7 @@ static void render_callback(Canvas* const canvas, void* ctx) { canvas_draw_str(canvas, 0, 64, screen_buf); } } - release_mutex((ValueMutex*)ctx, plugin_state); + furi_mutex_release(plugin_state->mutex); } int32_t nrf24scan_app(void* p) { @@ -1325,8 +1333,8 @@ int32_t nrf24scan_app(void* p) { APP = malloc(sizeof(Nrf24Scan)); APP->event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent)); APP->plugin_state = malloc(sizeof(PluginState)); - ValueMutex state_mutex; - if(!init_mutex(&state_mutex, APP->plugin_state, sizeof(PluginState))) { + APP->plugin_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!APP->plugin_state->mutex) { furi_message_queue_free(APP->event_queue); FURI_LOG_E(TAG, "cannot create mutex"); free(APP->plugin_state); @@ -1346,11 +1354,16 @@ int32_t nrf24scan_app(void* p) { memset((uint8_t*)&addrs, 0, sizeof(addrs)); memset((uint8_t*)&addrs_sniff, 0, sizeof(addrs_sniff)); + if(!furi_hal_power_is_otg_enabled()) { + furi_hal_power_enable_otg(); + NRF_BOARD_POWER_5V = true; + furi_delay_ms(100); + } nrf24_init(); // Set system callbacks APP->view_port = view_port_alloc(); - view_port_draw_callback_set(APP->view_port, render_callback, &state_mutex); + view_port_draw_callback_set(APP->view_port, render_callback, APP->plugin_state); view_port_input_callback_set(APP->view_port, input_callback, APP->event_queue); // Open GUI and register view_port @@ -1391,7 +1404,7 @@ int32_t nrf24scan_app(void* p) { PluginEvent event; for(bool processing = true; processing;) { FuriStatus event_status = furi_message_queue_get(APP->event_queue, &event, 100); - PluginState* plugin_state = (PluginState*)acquire_mutex_block(&state_mutex); + furi_mutex_acquire(APP->plugin_state->mutex, FuriWaitForever); if(event_status == FuriStatusOk) { // press events @@ -1618,13 +1631,14 @@ int32_t nrf24scan_app(void* p) { } view_port_update(APP->view_port); - release_mutex(&state_mutex, plugin_state); + furi_mutex_release(APP->plugin_state->mutex); } nrf24_set_idle(nrf24_HANDLE); if(log_arr_idx && (log_to_file == 1 || log_to_file == 2)) { write_to_log_file(APP->storage, false); } nrf24_deinit(); + if(NRF_BOARD_POWER_5V) furi_hal_power_disable_otg(); view_port_enabled_set(APP->view_port, false); gui_remove_view_port(APP->gui, APP->view_port); @@ -1632,6 +1646,7 @@ int32_t nrf24scan_app(void* p) { furi_record_close(RECORD_NOTIFICATION); furi_record_close(RECORD_STORAGE); view_port_free(APP->view_port); + furi_mutex_free(APP->plugin_state->mutex); furi_message_queue_free(APP->event_queue); free(APP->plugin_state); if(APP->log_arr) free(APP->log_arr); diff --git a/applications/plugins/nrf24scan/nrf24scan.h b/applications/plugins/nrf24scan/nrf24scan.h index ea8d7ac04..cfc4806e7 100644 --- a/applications/plugins/nrf24scan/nrf24scan.h +++ b/applications/plugins/nrf24scan/nrf24scan.h @@ -19,8 +19,7 @@ typedef struct { } PluginEvent; typedef struct { - int x; - int y; + FuriMutex* mutex; } PluginState; struct FOUND { diff --git a/applications/plugins/nrfsniff/README.md b/applications/plugins/nrfsniff/README.md deleted file mode 100644 index 89cc401c3..000000000 --- a/applications/plugins/nrfsniff/README.md +++ /dev/null @@ -1,31 +0,0 @@ -# flipperzero-nrf24 - -An [NRF24](https://www.sparkfun.com/datasheets/Components/SMD/nRF24L01Pluss_Preliminary_Product_Specification_v1_0.pdf) driver for the [Flipper Zero](https://flipperzero.one/) device. The NRF24 is a popular line of 2.4GHz radio transceivers from Nordic Semiconductors. This library is not currently complete, but functional. - -## Warning -This repo contains two Flipper Zero apps that utilize the NRF24 driver to sniff for NRF24 addresses and perform mousejack attacks. These apps are for **educational purposes** only. Please use this code responsibly and only use these apps on your own equipment. - -## Acknowledgments -The NRF24 sniffing technique was discovered and shared by Travis Goodspeed in [his blog](http://travisgoodspeed.blogspot.com/2011/02/promiscuity-is-nrf24l01s-duty.html). - -The mousejack vulnerabilities were discovered and reported by Marc Newlin, see [the blog](https://www.bastille.net/research/vulnerabilities/mousejack/technical-details) for technical details. - -Much of the driver code was inspired by [RadioHead's Arduino library](https://www.airspayce.com/mikem/arduino/RadioHead/classRH__NRF24.html). -Much of the mousejack code was inspired by the [Jackit project](https://github.com/insecurityofthings/jackit). -# -## PinOut from from NoComp/Frog - - -# Mousejack / NRF24 pinout by UberGuidoZ -2/A7 on FZ goes to MOSI/6 on nrf24l01
-3/A6 on FZ goes to MISO/7 on nrf24l01
-4/A4 on FZ goes to CSN/4 on nrf24l01
-5/B3 on FZ goes to SCK/5 on nrf24l01
-6/B2 on FZ goes to CE/3 on nrf24l01
-8/GND on FZ goes to GND/1 on nrf24l01
-9/3V3 on FZ goes to VCC/2 on nrf24l01
-IRQ/8 is left disconnected on nrf24l01 -![NRF_Pins](https://user-images.githubusercontent.com/57457139/178093717-39effd5c-ebe2-4253-b13c-70517d7902f9.png) -If the nRF module is acting a bit flakey, try adding a capacitor to the vcc/gnd lines! I've not tried the Plus model so it may have a bigger need for a cap. Otherwise, I haven't had any major issues. Anything from a 3.3 uF to 10 uF should do. (Watch your positive/negative placement! Negative to ground.) I learned if you wanna get fancy, include a 0.1 uF cap in parallel. The 3.3 uF to 10 uF will respond to slow freq changes while the 0.1 uF will respond to the high freq switching spikes that the larger one cannot. That said, a single 10 uF will likely suffice for the Mousejack attack. ¯\\\_(ツ)_/¯ -![NRF_Capacitor](https://user-images.githubusercontent.com/57457139/178169959-d030f9a6-d2ac-46af-af8b-470ff092c8a7.jpg) - diff --git a/applications/plugins/nrfsniff/nrfsniff.c b/applications/plugins/nrfsniff/nrfsniff.c index ce2836152..56bc33d05 100644 --- a/applications/plugins/nrfsniff/nrfsniff.c +++ b/applications/plugins/nrfsniff/nrfsniff.c @@ -4,7 +4,7 @@ #include #include #include -#include + #include #include @@ -29,8 +29,7 @@ typedef struct { } PluginEvent; typedef struct { - int x; - int y; + FuriMutex* mutex; } PluginState; char rate_text_fmt[] = "Transfer rate: %dMbps"; @@ -96,13 +95,13 @@ static void insert_addr(uint8_t* addr, uint8_t addr_size) { } static void render_callback(Canvas* const canvas, void* ctx) { + furi_assert(ctx); + const PluginState* plugin_state = ctx; + furi_mutex_acquire(plugin_state->mutex, FuriWaitForever); + uint8_t rate = 2; char sniffing[] = "Yes"; - const PluginState* plugin_state = acquire_mutex((ValueMutex*)ctx, 25); - if(plugin_state == NULL) { - return; - } // border around the edge of the screen canvas_draw_frame(canvas, 0, 0, 128, 64); canvas_set_font(canvas, FontSecondary); @@ -126,7 +125,7 @@ static void render_callback(Canvas* const canvas, void* ctx) { canvas_draw_str_aligned(canvas, 30, 50, AlignLeft, AlignBottom, addresses_header_text); canvas_draw_str_aligned(canvas, 30, 60, AlignLeft, AlignBottom, sniffed_address); - release_mutex((ValueMutex*)ctx, plugin_state); + furi_mutex_release(plugin_state->mutex); } static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { @@ -291,7 +290,6 @@ static void wrap_up(Storage* storage, NotificationApp* notification) { hexlify(addr, 5, top_address); found_count++; save_addr_to_file(storage, addr, 5, notification); - DOLPHIN_DEED(getRandomDeed()); if(confirmed_idx < MAX_CONFIRMED) memcpy(confirmed[confirmed_idx++], addr, 5); break; } @@ -321,8 +319,8 @@ int32_t nrfsniff_app(void* p) { hexlify(address, 5, top_address); FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent)); PluginState* plugin_state = malloc(sizeof(PluginState)); - ValueMutex state_mutex; - if(!init_mutex(&state_mutex, plugin_state, sizeof(PluginState))) { + plugin_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!plugin_state->mutex) { furi_message_queue_free(event_queue); FURI_LOG_E(TAG, "cannot create mutex\r\n"); free(plugin_state); @@ -333,7 +331,7 @@ int32_t nrfsniff_app(void* p) { // Set system callbacks ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, render_callback, &state_mutex); + view_port_draw_callback_set(view_port, render_callback, plugin_state); view_port_input_callback_set(view_port, input_callback, event_queue); // Open GUI and register view_port @@ -348,7 +346,7 @@ int32_t nrfsniff_app(void* p) { PluginEvent event; for(bool processing = true; processing;) { FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); - PluginState* plugin_state = (PluginState*)acquire_mutex_block(&state_mutex); + furi_mutex_acquire(plugin_state->mutex, FuriWaitForever); if(event_status == FuriStatusOk) { // press events @@ -405,9 +403,6 @@ int32_t nrfsniff_app(void* p) { } } } - } else { - // FURI_LOG_D(TAG, "osMessageQueue: event timeout"); - // event timeout } if(sniffing_state) { @@ -439,7 +434,7 @@ int32_t nrfsniff_app(void* p) { } view_port_update(view_port); - release_mutex(&state_mutex, plugin_state); + furi_mutex_release(plugin_state->mutex); } clear_cache(); @@ -454,6 +449,8 @@ int32_t nrfsniff_app(void* p) { furi_record_close(RECORD_STORAGE); view_port_free(view_port); furi_message_queue_free(event_queue); + furi_mutex_free(plugin_state->mutex); + free(plugin_state); return 0; } diff --git a/applications/plugins/ocarina/ocarina.c b/applications/plugins/ocarina/ocarina.c index 80dc1fddc..7fdfce74c 100644 --- a/applications/plugins/ocarina/ocarina.c +++ b/applications/plugins/ocarina/ocarina.c @@ -88,27 +88,27 @@ int32_t ocarina_app(void* p) { if(event.type == InputTypePress) { switch(event.key) { case InputKeyUp: - if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(30)) { + if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(1000)) { furi_hal_speaker_start(NOTE_UP, volume); } break; case InputKeyDown: - if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(30)) { + if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(1000)) { furi_hal_speaker_start(NOTE_DOWN, volume); } break; case InputKeyLeft: - if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(30)) { + if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(1000)) { furi_hal_speaker_start(NOTE_LEFT, volume); } break; case InputKeyRight: - if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(30)) { + if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(1000)) { furi_hal_speaker_start(NOTE_RIGHT, volume); } break; case InputKeyOk: - if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(30)) { + if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(1000)) { furi_hal_speaker_start(NOTE_OK, volume); } break; diff --git a/applications/plugins/orgasmotron/orgasmotron.c b/applications/plugins/orgasmotron/orgasmotron.c index 684fc3d95..80c7d9e42 100644 --- a/applications/plugins/orgasmotron/orgasmotron.c +++ b/applications/plugins/orgasmotron/orgasmotron.c @@ -6,6 +6,7 @@ #include typedef struct { + FuriMutex* mutex; int mode; } PluginState; @@ -39,8 +40,8 @@ int32_t orgasmotron_app(void* p) { FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); PluginState* plugin_state = malloc(sizeof(PluginState)); - ValueMutex state_mutex; - if(!init_mutex(&state_mutex, plugin_state, sizeof(PluginState))) { + plugin_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!plugin_state->mutex) { FURI_LOG_E("Orgasmatron", "cannot create mutex\r\n"); free(plugin_state); return 255; @@ -63,7 +64,7 @@ int32_t orgasmotron_app(void* p) { //while(furi_message_queue_get(event_queue, &event, FuriWaitForever) == FuriStatusOk) { while(processing) { FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); - PluginState* plugin_state = (PluginState*)acquire_mutex_block(&state_mutex); + furi_mutex_acquire(plugin_state->mutex, FuriWaitForever); if(event_status == FuriStatusOk) { if(event.key == InputKeyBack && event.type == InputTypeShort) { //Exit Application @@ -132,15 +133,15 @@ int32_t orgasmotron_app(void* p) { delay(50); } } - release_mutex(&state_mutex, plugin_state); + furi_mutex_release(plugin_state->mutex); } gui_remove_view_port(gui, view_port); view_port_free(view_port); + furi_mutex_free(plugin_state->mutex); furi_message_queue_free(event_queue); furi_record_close(RECORD_NOTIFICATION); furi_record_close(RECORD_GUI); - delete_mutex(&state_mutex); return 0; -} \ No newline at end of file +} diff --git a/applications/plugins/paint/paint.c b/applications/plugins/paint/paint.c index 5cfe85155..0d4124b02 100644 --- a/applications/plugins/paint/paint.c +++ b/applications/plugins/paint/paint.c @@ -12,14 +12,17 @@ typedef struct selected_position { } selected_position; typedef struct { + FuriMutex* mutex; selected_position selected; bool board[32][16]; bool isDrawing; } PaintData; void paint_draw_callback(Canvas* canvas, void* ctx) { - const PaintData* paint_state = acquire_mutex((ValueMutex*)ctx, 25); - UNUSED(ctx); + furi_assert(ctx); + const PaintData* paint_state = ctx; + furi_mutex_acquire(paint_state->mutex, FuriWaitForever); + canvas_clear(canvas); canvas_set_color(canvas, ColorBlack); //draw the canvas(32x16) on screen(144x64) using 4x4 tiles @@ -39,7 +42,7 @@ void paint_draw_callback(Canvas* canvas, void* ctx) { canvas, paint_state->selected.x * 4 + 1, paint_state->selected.y * 4 + 1, 2, 2); //release the mutex - release_mutex((ValueMutex*)ctx, paint_state); + furi_mutex_release(paint_state->mutex); } void paint_input_callback(InputEvent* input_event, void* ctx) { @@ -53,8 +56,9 @@ int32_t paint_app(void* p) { FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); PaintData* paint_state = malloc(sizeof(PaintData)); - ValueMutex paint_state_mutex; - if(!init_mutex(&paint_state_mutex, paint_state, sizeof(PaintData))) { + + paint_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!paint_state->mutex) { FURI_LOG_E("paint", "cannot create mutex\r\n"); free(paint_state); return -1; @@ -62,7 +66,7 @@ int32_t paint_app(void* p) { // Configure view port ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, paint_draw_callback, &paint_state_mutex); + view_port_draw_callback_set(view_port, paint_draw_callback, paint_state); view_port_input_callback_set(view_port, paint_input_callback, event_queue); // Register view port in GUI @@ -140,9 +144,10 @@ int32_t paint_app(void* p) { gui_remove_view_port(gui, view_port); view_port_free(view_port); furi_message_queue_free(event_queue); - free(paint_state); + furi_mutex_free(paint_state->mutex); furi_record_close(RECORD_NOTIFICATION); furi_record_close(RECORD_GUI); + free(paint_state); return 0; } diff --git a/applications/plugins/passgen/application.fam b/applications/plugins/passgen/application.fam index 94005e716..b6c1ae8e0 100644 --- a/applications/plugins/passgen/application.fam +++ b/applications/plugins/passgen/application.fam @@ -1,7 +1,7 @@ App( - appid="Password_Generator", + appid="passgen", name="Password Generator", - apptype=FlipperAppType.EXTERNAL, + apptype=FlipperAppType.PLUGIN, entry_point="passgenapp", requires=[ "gui", diff --git a/applications/plugins/passgen/passgen.c b/applications/plugins/passgen/passgen.c index c0f9c6e59..12cdc10fb 100644 --- a/applications/plugins/passgen/passgen.c +++ b/applications/plugins/passgen/passgen.c @@ -4,7 +4,7 @@ #include #include #include -#include +#include #define PASSGEN_MAX_LENGTH 16 #define PASSGEN_CHARACTERS_LENGTH (26 * 4) diff --git a/applications/plugins/picopass/application.fam b/applications/plugins/picopass/application.fam index 5d331e708..48cbba316 100644 --- a/applications/plugins/picopass/application.fam +++ b/applications/plugins/picopass/application.fam @@ -1,6 +1,6 @@ App( appid="Picopass", - name="PicoPass Reader", + name="PicoPass", apptype=FlipperAppType.EXTERNAL, targets=["f7"], entry_point="picopass_app", @@ -9,7 +9,7 @@ App( "gui", ], stack_size=4 * 1024, - order=175, + order=30, fap_icon="125_10px.png", fap_category="Tools", fap_libs=["mbedtls"], diff --git a/applications/plugins/picopass/helpers/iclass_elite_dict.c b/applications/plugins/picopass/helpers/iclass_elite_dict.c index 455eb23c1..e8c13dd1d 100644 --- a/applications/plugins/picopass/helpers/iclass_elite_dict.c +++ b/applications/plugins/picopass/helpers/iclass_elite_dict.c @@ -3,8 +3,8 @@ #include #include -#define ICLASS_ELITE_DICT_FLIPPER_PATH EXT_PATH("picopass/assets/iclass_elite_dict.txt") -#define ICLASS_ELITE_DICT_USER_PATH EXT_PATH("picopass/assets/iclass_elite_dict_user.txt") +#define ICLASS_ELITE_DICT_FLIPPER_NAME APP_DATA_PATH("assets/iclass_elite_dict.txt") +#define ICLASS_ELITE_DICT_USER_NAME APP_DATA_PATH("assets/iclass_elite_dict_user.txt") #define TAG "IclassEliteDict" @@ -21,10 +21,10 @@ bool iclass_elite_dict_check_presence(IclassEliteDictType dict_type) { bool dict_present = false; if(dict_type == IclassEliteDictTypeFlipper) { - dict_present = storage_common_stat(storage, ICLASS_ELITE_DICT_FLIPPER_PATH, NULL) == - FSE_OK; + dict_present = + (storage_common_stat(storage, ICLASS_ELITE_DICT_FLIPPER_NAME, NULL) == FSE_OK); } else if(dict_type == IclassEliteDictTypeUser) { - dict_present = storage_common_stat(storage, ICLASS_ELITE_DICT_USER_PATH, NULL) == FSE_OK; + dict_present = (storage_common_stat(storage, ICLASS_ELITE_DICT_USER_NAME, NULL) == FSE_OK); } furi_record_close(RECORD_STORAGE); @@ -36,27 +36,26 @@ IclassEliteDict* iclass_elite_dict_alloc(IclassEliteDictType dict_type) { IclassEliteDict* dict = malloc(sizeof(IclassEliteDict)); Storage* storage = furi_record_open(RECORD_STORAGE); dict->stream = buffered_file_stream_alloc(storage); - furi_record_close(RECORD_STORAGE); FuriString* next_line = furi_string_alloc(); bool dict_loaded = false; do { if(dict_type == IclassEliteDictTypeFlipper) { if(!buffered_file_stream_open( - dict->stream, ICLASS_ELITE_DICT_FLIPPER_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) { + dict->stream, ICLASS_ELITE_DICT_FLIPPER_NAME, FSAM_READ, FSOM_OPEN_EXISTING)) { buffered_file_stream_close(dict->stream); break; } } else if(dict_type == IclassEliteDictTypeUser) { if(!buffered_file_stream_open( - dict->stream, ICLASS_ELITE_DICT_USER_PATH, FSAM_READ_WRITE, FSOM_OPEN_ALWAYS)) { + dict->stream, ICLASS_ELITE_DICT_USER_NAME, FSAM_READ_WRITE, FSOM_OPEN_ALWAYS)) { buffered_file_stream_close(dict->stream); break; } } // Read total amount of keys - while(true) { + while(true) { //-V547 if(!stream_read_line(dict->stream, next_line)) break; if(furi_string_get_char(next_line, 0) == '#') continue; if(furi_string_size(next_line) != ICLASS_ELITE_KEY_LINE_LEN) continue; @@ -69,12 +68,13 @@ IclassEliteDict* iclass_elite_dict_alloc(IclassEliteDictType dict_type) { FURI_LOG_I(TAG, "Loaded dictionary with %lu keys", dict->total_keys); } while(false); - if(!dict_loaded) { + if(!dict_loaded) { //-V547 buffered_file_stream_close(dict->stream); free(dict); dict = NULL; } + furi_record_close(RECORD_STORAGE); furi_string_free(next_line); return dict; diff --git a/applications/plugins/picopass/picopass.c b/applications/plugins/picopass/picopass.c index 805f6760e..c1428b2fb 100644 --- a/applications/plugins/picopass/picopass.c +++ b/applications/plugins/picopass/picopass.c @@ -137,9 +137,9 @@ void picopass_text_store_clear(Picopass* picopass) { memset(picopass->text_store, 0, sizeof(picopass->text_store)); } -static const NotificationSequence picopass_sequence_blink_start_blue = { +static const NotificationSequence picopass_sequence_blink_start_cyan = { &message_blink_start_10, - &message_blink_set_color_blue, + &message_blink_set_color_cyan, &message_do_not_reset, NULL, }; @@ -150,7 +150,7 @@ static const NotificationSequence picopass_sequence_blink_stop = { }; void picopass_blink_start(Picopass* picopass) { - notification_message(picopass->notifications, &picopass_sequence_blink_start_blue); + notification_message(picopass->notifications, &picopass_sequence_blink_start_cyan); } void picopass_blink_stop(Picopass* picopass) { @@ -171,8 +171,26 @@ void picopass_show_loading_popup(void* context, bool show) { } } +static void picopass_migrate_from_old_folder() { + Storage* storage = furi_record_open(RECORD_STORAGE); + storage_common_migrate(storage, "/ext/picopass", STORAGE_APP_DATA_PATH_PREFIX); + furi_record_close(RECORD_STORAGE); +} + +bool picopass_is_memset(const uint8_t* data, const uint8_t pattern, size_t size) { + bool result = size > 0; + while(size > 0) { + result &= (*data == pattern); + data++; + size--; + } + return result; +} + int32_t picopass_app(void* p) { UNUSED(p); + picopass_migrate_from_old_folder(); + Picopass* picopass = picopass_alloc(); scene_manager_next_scene(picopass->scene_manager, PicopassSceneStart); diff --git a/applications/plugins/picopass/picopass_device.c b/applications/plugins/picopass/picopass_device.c index de01834bc..b5ba8ba78 100644 --- a/applications/plugins/picopass/picopass_device.c +++ b/applications/plugins/picopass/picopass_device.c @@ -48,13 +48,9 @@ static bool picopass_device_save_file( if(use_load_path && !furi_string_empty(dev->load_path)) { // Get directory name path_extract_dirname(furi_string_get_cstr(dev->load_path), temp_str); - // Create picopass directory if necessary - if(!storage_simply_mkdir(dev->storage, furi_string_get_cstr(temp_str))) break; // Make path to file to save furi_string_cat_printf(temp_str, "/%s%s", dev_name, extension); } else { - // Create picopass directory if necessary - if(!storage_simply_mkdir(dev->storage, PICOPASS_APP_FOLDER)) break; // First remove picopass device file if it was saved furi_string_printf(temp_str, "%s/%s%s", folder, dev_name, extension); } @@ -126,10 +122,11 @@ static bool picopass_device_save_file( bool picopass_device_save(PicopassDevice* dev, const char* dev_name) { if(dev->format == PicopassDeviceSaveFormatHF) { return picopass_device_save_file( - dev, dev_name, PICOPASS_APP_FOLDER, PICOPASS_APP_EXTENSION, true); + dev, dev_name, STORAGE_APP_DATA_PATH_PREFIX, PICOPASS_APP_EXTENSION, true); } else if(dev->format == PicopassDeviceSaveFormatLF) { return picopass_device_save_file(dev, dev_name, ANY_PATH("lfrfid"), ".rfid", true); } + return false; } @@ -170,6 +167,8 @@ static bool picopass_device_load_data(PicopassDevice* dev, FuriString* path, boo } size_t app_limit = AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[0]; + // Fix for unpersonalized cards that have app_limit set to 0xFF + if(app_limit > PICOPASS_MAX_APP_LIMIT) app_limit = PICOPASS_MAX_APP_LIMIT; for(size_t i = 6; i < app_limit; i++) { furi_string_printf(temp_str, "Block %d", i); if(!flipper_format_read_hex( @@ -225,13 +224,12 @@ void picopass_device_free(PicopassDevice* picopass_dev) { bool picopass_file_select(PicopassDevice* dev) { furi_assert(dev); - // Input events and views are managed by file_browser FuriString* picopass_app_folder; - picopass_app_folder = furi_string_alloc_set(PICOPASS_APP_FOLDER); + picopass_app_folder = furi_string_alloc_set(STORAGE_APP_DATA_PATH_PREFIX); DialogsFileBrowserOptions browser_options; dialog_file_browser_set_basic_options(&browser_options, PICOPASS_APP_EXTENSION, &I_Nfc_10px); - browser_options.base_path = PICOPASS_APP_FOLDER; + browser_options.base_path = STORAGE_APP_DATA_PATH_PREFIX; bool res = dialog_file_browser_show( dev->dialogs, dev->load_path, picopass_app_folder, &browser_options); @@ -274,7 +272,7 @@ bool picopass_device_delete(PicopassDevice* dev, bool use_load_path) { furi_string_set(file_path, dev->load_path); } else { furi_string_printf( - file_path, "%s/%s%s", PICOPASS_APP_FOLDER, dev->dev_name, PICOPASS_APP_EXTENSION); + file_path, APP_DATA_PATH("%s%s"), dev->dev_name, PICOPASS_APP_EXTENSION); } if(!storage_simply_remove(dev->storage, furi_string_get_cstr(file_path))) break; deleted = true; @@ -368,7 +366,7 @@ ReturnCode picopass_device_parse_wiegand(uint8_t* data, PicopassWiegandRecord* r record->CardNumber = (bot >> 1) & 0xFFFF; record->FacilityCode = (bot >> 17) & 0xFF; - FURI_LOG_D(TAG, "FC:%u CN: %u\n", record->FacilityCode, record->CardNumber); + FURI_LOG_D(TAG, "FC: %u CN: %u", record->FacilityCode, record->CardNumber); record->valid = true; } else { record->CardNumber = 0; diff --git a/applications/plugins/picopass/picopass_device.h b/applications/plugins/picopass/picopass_device.h index 150b095a7..d7d0977df 100644 --- a/applications/plugins/picopass/picopass_device.h +++ b/applications/plugins/picopass/picopass_device.h @@ -22,8 +22,8 @@ #define PICOPASS_KD_BLOCK_INDEX 3 #define PICOPASS_KC_BLOCK_INDEX 4 #define PICOPASS_AIA_BLOCK_INDEX 5 +#define PICOPASS_PACS_CFG_BLOCK_INDEX 6 -#define PICOPASS_APP_FOLDER ANY_PATH("picopass") #define PICOPASS_APP_EXTENSION ".picopass" #define PICOPASS_APP_SHADOW_EXTENSION ".pas" @@ -80,7 +80,6 @@ typedef struct { PicopassDeviceSaveFormat format; PicopassLoadingCallback loading_cb; void* loading_cb_ctx; - } PicopassDevice; PicopassDevice* picopass_device_alloc(); diff --git a/applications/plugins/picopass/picopass_i.h b/applications/plugins/picopass/picopass_i.h index 8f954c83c..79c2a1af8 100644 --- a/applications/plugins/picopass/picopass_i.h +++ b/applications/plugins/picopass/picopass_i.h @@ -81,3 +81,15 @@ void picopass_blink_start(Picopass* picopass); void picopass_blink_stop(Picopass* picopass); void picopass_show_loading_popup(void* context, bool show); + +/** Check if memory is set to pattern + * + * @warning zero size will return false + * + * @param[in] data Pointer to the byte array + * @param[in] pattern The pattern + * @param[in] size The byte array size + * + * @return True if memory is set to pattern, false otherwise + */ +bool picopass_is_memset(const uint8_t* data, const uint8_t pattern, size_t size); diff --git a/applications/plugins/picopass/picopass_keys.c b/applications/plugins/picopass/picopass_keys.c new file mode 100644 index 000000000..43dfc6312 --- /dev/null +++ b/applications/plugins/picopass/picopass_keys.c @@ -0,0 +1,8 @@ +#include "picopass_keys.h" + +const uint8_t picopass_iclass_key[] = {0xaf, 0xa7, 0x85, 0xa7, 0xda, 0xb3, 0x33, 0x78}; +const uint8_t picopass_factory_credit_key[] = {0x76, 0x65, 0x54, 0x43, 0x32, 0x21, 0x10, 0x00}; +const uint8_t picopass_factory_debit_key[] = {0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87}; +const uint8_t picopass_xice_key[] = {0x20, 0x20, 0x66, 0x66, 0x66, 0x66, 0x88, 0x88}; +const uint8_t picopass_xicl_key[] = {0x20, 0x20, 0x66, 0x66, 0x66, 0x66, 0x88, 0x88}; +const uint8_t picopass_xics_key[] = {0x66, 0x66, 0x20, 0x20, 0x66, 0x66, 0x88, 0x88}; diff --git a/applications/plugins/picopass/picopass_keys.h b/applications/plugins/picopass/picopass_keys.h new file mode 100644 index 000000000..2b5dba661 --- /dev/null +++ b/applications/plugins/picopass/picopass_keys.h @@ -0,0 +1,10 @@ +#pragma once + +#include "picopass_device.h" + +extern const uint8_t picopass_iclass_key[PICOPASS_BLOCK_LEN]; +extern const uint8_t picopass_factory_credit_key[PICOPASS_BLOCK_LEN]; +extern const uint8_t picopass_factory_debit_key[PICOPASS_BLOCK_LEN]; +extern const uint8_t picopass_xice_key[PICOPASS_BLOCK_LEN]; +extern const uint8_t picopass_xicl_key[PICOPASS_BLOCK_LEN]; +extern const uint8_t picopass_xics_key[PICOPASS_BLOCK_LEN]; diff --git a/applications/plugins/picopass/picopass_worker.c b/applications/plugins/picopass/picopass_worker.c index e886deb33..d141bdf6b 100644 --- a/applications/plugins/picopass/picopass_worker.c +++ b/applications/plugins/picopass/picopass_worker.c @@ -4,9 +4,6 @@ #define TAG "PicopassWorker" -const uint8_t picopass_iclass_key[] = {0xaf, 0xa7, 0x85, 0xa7, 0xda, 0xb3, 0x33, 0x78}; -const uint8_t picopass_factory_key[] = {0x76, 0x65, 0x54, 0x43, 0x32, 0x21, 0x10, 0x00}; - static void picopass_worker_enable_field() { furi_hal_nfc_ll_txrx_on(); furi_hal_nfc_exit_sleep(); @@ -175,33 +172,12 @@ ReturnCode picopass_read_preauth(PicopassBlock* AA1) { return ERR_NONE; } -static ReturnCode picopass_auth_standard(uint8_t* csn, uint8_t* div_key) { - rfalPicoPassReadCheckRes rcRes; - rfalPicoPassCheckRes chkRes; - - ReturnCode err; - - uint8_t mac[4] = {0}; - uint8_t ccnr[12] = {0}; - - err = rfalPicoPassPollerReadCheck(&rcRes); - if(err != ERR_NONE) { - FURI_LOG_E(TAG, "rfalPicoPassPollerReadCheck error %d", err); - return err; - } - memcpy(ccnr, rcRes.CCNR, sizeof(rcRes.CCNR)); // last 4 bytes left 0 - - loclass_diversifyKey(csn, picopass_iclass_key, div_key); - loclass_opt_doReaderMAC(ccnr, div_key, mac); - - return rfalPicoPassPollerCheck(mac, &chkRes); -} - static ReturnCode picopass_auth_dict( uint8_t* csn, PicopassPacs* pacs, uint8_t* div_key, - IclassEliteDictType dict_type) { + IclassEliteDictType dict_type, + bool elite) { rfalPicoPassReadCheckRes rcRes; rfalPicoPassCheckRes chkRes; @@ -246,7 +222,7 @@ static ReturnCode picopass_auth_dict( } memcpy(ccnr, rcRes.CCNR, sizeof(rcRes.CCNR)); // last 4 bytes left 0 - loclass_iclass_calc_div_key(csn, key, div_key, true); + loclass_iclass_calc_div_key(csn, key, div_key, elite); loclass_opt_doReaderMAC(ccnr, div_key, mac); err = rfalPicoPassPollerCheck(mac, &chkRes); @@ -264,29 +240,35 @@ static ReturnCode picopass_auth_dict( 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"); + FURI_LOG_I(TAG, "Starting system dictionary attack [Standard KDF]"); err = picopass_auth_dict( AA1[PICOPASS_CSN_BLOCK_INDEX].data, pacs, AA1[PICOPASS_KD_BLOCK_INDEX].data, - IclassEliteDictTypeUser); + IclassEliteDictTypeFlipper, + false); if(err == ERR_NONE) { return ERR_NONE; } - FURI_LOG_E(TAG, "Starting in-built dictionary attack"); + FURI_LOG_I(TAG, "Starting user dictionary attack [Elite KDF]"); err = picopass_auth_dict( AA1[PICOPASS_CSN_BLOCK_INDEX].data, pacs, AA1[PICOPASS_KD_BLOCK_INDEX].data, - IclassEliteDictTypeFlipper); + IclassEliteDictTypeUser, + true); + if(err == ERR_NONE) { + return ERR_NONE; + } + + FURI_LOG_I(TAG, "Starting system dictionary attack [Elite KDF]"); + err = picopass_auth_dict( + AA1[PICOPASS_CSN_BLOCK_INDEX].data, + pacs, + AA1[PICOPASS_KD_BLOCK_INDEX].data, + IclassEliteDictTypeFlipper, + true); if(err == ERR_NONE) { return ERR_NONE; } @@ -364,7 +346,7 @@ ReturnCode picopass_write_card(PicopassBlock* AA1) { } memcpy(ccnr, rcRes.CCNR, sizeof(rcRes.CCNR)); // last 4 bytes left 0 - loclass_diversifyKey(selRes.CSN, picopass_iclass_key, div_key); + loclass_iclass_calc_div_key(selRes.CSN, (uint8_t*)picopass_iclass_key, div_key, false); loclass_opt_doReaderMAC(ccnr, div_key, mac); err = rfalPicoPassPollerCheck(mac, &chkRes); @@ -406,6 +388,86 @@ ReturnCode picopass_write_card(PicopassBlock* AA1) { return ERR_NONE; } +ReturnCode picopass_write_block(PicopassBlock* AA1, uint8_t blockNo, uint8_t* newBlock) { + rfalPicoPassIdentifyRes idRes; + rfalPicoPassSelectRes selRes; + rfalPicoPassReadCheckRes rcRes; + rfalPicoPassCheckRes chkRes; + + ReturnCode err; + + uint8_t mac[4] = {0}; + uint8_t ccnr[12] = {0}; + + err = rfalPicoPassPollerIdentify(&idRes); + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "rfalPicoPassPollerIdentify error %d", err); + return err; + } + + err = rfalPicoPassPollerSelect(idRes.CSN, &selRes); + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "rfalPicoPassPollerSelect error %d", err); + return err; + } + + err = rfalPicoPassPollerReadCheck(&rcRes); + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "rfalPicoPassPollerReadCheck error %d", err); + return err; + } + memcpy(ccnr, rcRes.CCNR, sizeof(rcRes.CCNR)); // last 4 bytes left 0 + + if(memcmp(selRes.CSN, AA1[PICOPASS_CSN_BLOCK_INDEX].data, PICOPASS_BLOCK_LEN) != 0) { + FURI_LOG_E(TAG, "Wrong CSN for write"); + return ERR_REQUEST; + } + + loclass_opt_doReaderMAC(ccnr, AA1[PICOPASS_KD_BLOCK_INDEX].data, mac); + err = rfalPicoPassPollerCheck(mac, &chkRes); + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "rfalPicoPassPollerCheck error %d", err); + return err; + } + + FURI_LOG_D(TAG, "rfalPicoPassPollerWriteBlock %d", blockNo); + uint8_t data[9] = { + blockNo, + newBlock[0], + newBlock[1], + newBlock[2], + newBlock[3], + newBlock[4], + newBlock[5], + newBlock[6], + newBlock[7]}; + loclass_doMAC_N(data, sizeof(data), AA1[PICOPASS_KD_BLOCK_INDEX].data, mac); + FURI_LOG_D( + TAG, + "loclass_doMAC_N %d %02x%02x%02x%02x%02x%02x%02x%02x %02x%02x%02x%02x", + blockNo, + data[1], + data[2], + data[3], + data[4], + data[5], + data[6], + data[7], + data[8], + mac[0], + mac[1], + mac[2], + mac[3]); + + err = rfalPicoPassPollerWriteBlock(data[0], data + 1, mac); + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "rfalPicoPassPollerWriteBlock error %d", err); + return err; + } + + return ERR_NONE; +} + int32_t picopass_worker_task(void* context) { PicopassWorker* picopass_worker = context; @@ -414,6 +476,8 @@ int32_t picopass_worker_task(void* context) { picopass_worker_detect(picopass_worker); } else if(picopass_worker->state == PicopassWorkerStateWrite) { picopass_worker_write(picopass_worker); + } else if(picopass_worker->state == PicopassWorkerStateWriteKey) { + picopass_worker_write_key(picopass_worker); } picopass_worker_disable_field(ERR_NONE); @@ -448,7 +512,7 @@ void picopass_worker_detect(PicopassWorker* picopass_worker) { } // Thank you proxmark! - pacs->legacy = (memcmp(AA1[5].data, "\xff\xff\xff\xff\xff\xff\xff\xff", 8) == 0); + pacs->legacy = picopass_is_memset(AA1[5].data, 0xFF, 8); pacs->se_enabled = (memcmp(AA1[5].data, "\xff\xff\xff\x00\x06\xff\xff\xff", 8) == 0); if(pacs->se_enabled) { FURI_LOG_D(TAG, "SE enabled"); @@ -520,3 +584,46 @@ void picopass_worker_write(PicopassWorker* picopass_worker) { furi_delay_ms(100); } } + +void picopass_worker_write_key(PicopassWorker* picopass_worker) { + PicopassDeviceData* dev_data = picopass_worker->dev_data; + PicopassBlock* AA1 = dev_data->AA1; + PicopassPacs* pacs = &dev_data->pacs; + ReturnCode err; + PicopassWorkerEvent nextState = PicopassWorkerEventSuccess; + + uint8_t* csn = AA1[PICOPASS_CSN_BLOCK_INDEX].data; + uint8_t* configBlock = AA1[PICOPASS_CONFIG_BLOCK_INDEX].data; + uint8_t fuses = configBlock[7]; + uint8_t* oldKey = AA1[PICOPASS_KD_BLOCK_INDEX].data; + + uint8_t newKey[PICOPASS_BLOCK_LEN] = {0}; + loclass_iclass_calc_div_key(csn, pacs->key, newKey, false); + + if((fuses & 0x80) == 0x80) { + FURI_LOG_D(TAG, "Plain write for personalized mode key change"); + } else { + FURI_LOG_D(TAG, "XOR write for application mode key change"); + // XOR when in application mode + for(size_t i = 0; i < PICOPASS_BLOCK_LEN; i++) { + newKey[i] ^= oldKey[i]; + } + } + + while(picopass_worker->state == PicopassWorkerStateWriteKey) { + if(picopass_detect_card(1000) == ERR_NONE) { + err = picopass_write_block(AA1, PICOPASS_KD_BLOCK_INDEX, newKey); + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "picopass_write_block error %d", err); + nextState = PicopassWorkerEventFail; + } + + // Notify caller and exit + if(picopass_worker->callback) { + picopass_worker->callback(nextState, picopass_worker->context); + } + break; + } + furi_delay_ms(100); + } +} diff --git a/applications/plugins/picopass/picopass_worker.h b/applications/plugins/picopass/picopass_worker.h index 29a890a18..f5e9f3039 100644 --- a/applications/plugins/picopass/picopass_worker.h +++ b/applications/plugins/picopass/picopass_worker.h @@ -1,6 +1,7 @@ #pragma once #include "picopass_device.h" +#include "picopass_keys.h" typedef struct PicopassWorker PicopassWorker; @@ -12,6 +13,7 @@ typedef enum { // Main worker states PicopassWorkerStateDetect, PicopassWorkerStateWrite, + PicopassWorkerStateWriteKey, // Transition PicopassWorkerStateStop, } PicopassWorkerState; diff --git a/applications/plugins/picopass/picopass_worker_i.h b/applications/plugins/picopass/picopass_worker_i.h index ded40e6c6..f41cfce45 100644 --- a/applications/plugins/picopass/picopass_worker_i.h +++ b/applications/plugins/picopass/picopass_worker_i.h @@ -31,3 +31,4 @@ int32_t picopass_worker_task(void* context); void picopass_worker_detect(PicopassWorker* picopass_worker); void picopass_worker_write(PicopassWorker* picopass_worker); +void picopass_worker_write_key(PicopassWorker* picopass_worker); diff --git a/applications/plugins/picopass/scenes/picopass_scene_card_menu.c b/applications/plugins/picopass/scenes/picopass_scene_card_menu.c index a424b919a..fe63f7c86 100644 --- a/applications/plugins/picopass/scenes/picopass_scene_card_menu.c +++ b/applications/plugins/picopass/scenes/picopass_scene_card_menu.c @@ -3,6 +3,7 @@ enum SubmenuIndex { SubmenuIndexSave, SubmenuIndexSaveAsLF, + SubmenuIndexChangeKey, }; void picopass_scene_card_menu_submenu_callback(void* context, uint32_t index) { @@ -25,6 +26,13 @@ void picopass_scene_card_menu_on_enter(void* context) { picopass_scene_card_menu_submenu_callback, picopass); } + submenu_add_item( + submenu, + "Change Key", + SubmenuIndexChangeKey, + picopass_scene_card_menu_submenu_callback, + picopass); + submenu_set_selected_item( picopass->submenu, scene_manager_get_scene_state(picopass->scene_manager, PicopassSceneCardMenu)); @@ -49,6 +57,11 @@ bool picopass_scene_card_menu_on_event(void* context, SceneManagerEvent event) { picopass->dev->format = PicopassDeviceSaveFormatLF; scene_manager_next_scene(picopass->scene_manager, PicopassSceneSaveName); consumed = true; + } else if(event.event == SubmenuIndexChangeKey) { + scene_manager_set_scene_state( + picopass->scene_manager, PicopassSceneCardMenu, SubmenuIndexChangeKey); + scene_manager_next_scene(picopass->scene_manager, PicopassSceneKeyMenu); + consumed = true; } } else if(event.type == SceneManagerEventTypeBack) { consumed = scene_manager_search_and_switch_to_previous_scene( diff --git a/applications/plugins/picopass/scenes/picopass_scene_config.h b/applications/plugins/picopass/scenes/picopass_scene_config.h index 27d6bbcd7..f5a90d46e 100644 --- a/applications/plugins/picopass/scenes/picopass_scene_config.h +++ b/applications/plugins/picopass/scenes/picopass_scene_config.h @@ -11,3 +11,6 @@ ADD_SCENE(picopass, delete, Delete) ADD_SCENE(picopass, delete_success, DeleteSuccess) ADD_SCENE(picopass, write_card, WriteCard) ADD_SCENE(picopass, write_card_success, WriteCardSuccess) +ADD_SCENE(picopass, read_factory_success, ReadFactorySuccess) +ADD_SCENE(picopass, write_key, WriteKey) +ADD_SCENE(picopass, key_menu, KeyMenu) diff --git a/applications/plugins/picopass/scenes/picopass_scene_key_menu.c b/applications/plugins/picopass/scenes/picopass_scene_key_menu.c new file mode 100644 index 000000000..8aac6cb24 --- /dev/null +++ b/applications/plugins/picopass/scenes/picopass_scene_key_menu.c @@ -0,0 +1,96 @@ +#include "../picopass_i.h" +#include "../picopass_keys.h" + +enum SubmenuIndex { + SubmenuIndexWriteStandard, + SubmenuIndexWriteiCE, + SubmenuIndexWriteiCL, + SubmenuIndexWriteiCS, + SubmenuIndexWriteCustom, //TODO: user input of key +}; + +void picopass_scene_key_menu_submenu_callback(void* context, uint32_t index) { + Picopass* picopass = context; + + view_dispatcher_send_custom_event(picopass->view_dispatcher, index); +} + +void picopass_scene_key_menu_on_enter(void* context) { + Picopass* picopass = context; + Submenu* submenu = picopass->submenu; + + submenu_add_item( + submenu, + "Write Standard", + SubmenuIndexWriteStandard, + picopass_scene_key_menu_submenu_callback, + picopass); + submenu_add_item( + submenu, + "Write iCE", + SubmenuIndexWriteiCE, + picopass_scene_key_menu_submenu_callback, + picopass); + submenu_add_item( + submenu, + "Write iCL", + SubmenuIndexWriteiCL, + picopass_scene_key_menu_submenu_callback, + picopass); + submenu_add_item( + submenu, + "Write iCS", + SubmenuIndexWriteiCS, + picopass_scene_key_menu_submenu_callback, + picopass); + + submenu_set_selected_item( + picopass->submenu, + scene_manager_get_scene_state(picopass->scene_manager, PicopassSceneKeyMenu)); + + view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewMenu); +} + +bool picopass_scene_key_menu_on_event(void* context, SceneManagerEvent event) { + Picopass* picopass = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexWriteStandard) { + scene_manager_set_scene_state( + picopass->scene_manager, PicopassSceneKeyMenu, SubmenuIndexWriteStandard); + memcpy(picopass->dev->dev_data.pacs.key, picopass_iclass_key, PICOPASS_BLOCK_LEN); + scene_manager_next_scene(picopass->scene_manager, PicopassSceneWriteKey); + consumed = true; + } else if(event.event == SubmenuIndexWriteiCE) { + scene_manager_set_scene_state( + picopass->scene_manager, PicopassSceneKeyMenu, SubmenuIndexWriteiCE); + memcpy(picopass->dev->dev_data.pacs.key, picopass_xice_key, PICOPASS_BLOCK_LEN); + scene_manager_next_scene(picopass->scene_manager, PicopassSceneWriteKey); + consumed = true; + } else if(event.event == SubmenuIndexWriteiCL) { + scene_manager_set_scene_state( + picopass->scene_manager, PicopassSceneKeyMenu, SubmenuIndexWriteiCE); + memcpy(picopass->dev->dev_data.pacs.key, picopass_xicl_key, PICOPASS_BLOCK_LEN); + scene_manager_next_scene(picopass->scene_manager, PicopassSceneWriteKey); + consumed = true; + } else if(event.event == SubmenuIndexWriteiCS) { + scene_manager_set_scene_state( + picopass->scene_manager, PicopassSceneKeyMenu, SubmenuIndexWriteiCE); + memcpy(picopass->dev->dev_data.pacs.key, picopass_xics_key, PICOPASS_BLOCK_LEN); + scene_manager_next_scene(picopass->scene_manager, PicopassSceneWriteKey); + consumed = true; + } + } else if(event.type == SceneManagerEventTypeBack) { + consumed = scene_manager_search_and_switch_to_previous_scene( + picopass->scene_manager, PicopassSceneStart); + } + + return consumed; +} + +void picopass_scene_key_menu_on_exit(void* context) { + Picopass* picopass = context; + + submenu_reset(picopass->submenu); +} diff --git a/applications/plugins/picopass/scenes/picopass_scene_read_card.c b/applications/plugins/picopass/scenes/picopass_scene_read_card.c index 8188207a2..96ec7c668 100644 --- a/applications/plugins/picopass/scenes/picopass_scene_read_card.c +++ b/applications/plugins/picopass/scenes/picopass_scene_read_card.c @@ -1,5 +1,6 @@ #include "../picopass_i.h" #include +#include "../picopass_keys.h" void picopass_read_card_worker_callback(PicopassWorkerEvent event, void* context) { UNUSED(event); @@ -34,7 +35,14 @@ bool picopass_scene_read_card_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == PicopassCustomEventWorkerExit) { - scene_manager_next_scene(picopass->scene_manager, PicopassSceneReadCardSuccess); + if(memcmp( + picopass->dev->dev_data.pacs.key, + picopass_factory_debit_key, + PICOPASS_BLOCK_LEN) == 0) { + scene_manager_next_scene(picopass->scene_manager, PicopassSceneReadFactorySuccess); + } else { + scene_manager_next_scene(picopass->scene_manager, PicopassSceneReadCardSuccess); + } consumed = true; } } diff --git a/applications/plugins/picopass/scenes/picopass_scene_read_card_success.c b/applications/plugins/picopass/scenes/picopass_scene_read_card_success.c index bb170ac45..f078d460a 100644 --- a/applications/plugins/picopass/scenes/picopass_scene_read_card_success.c +++ b/applications/plugins/picopass/scenes/picopass_scene_read_card_success.c @@ -15,6 +15,7 @@ void picopass_scene_read_card_success_widget_callback( void picopass_scene_read_card_success_on_enter(void* context) { Picopass* picopass = context; + FuriString* csn_str = furi_string_alloc_set("CSN:"); FuriString* credential_str = furi_string_alloc(); FuriString* wiegand_str = furi_string_alloc(); @@ -30,27 +31,31 @@ void picopass_scene_read_card_success_on_enter(void* context) { PicopassPacs* pacs = &picopass->dev->dev_data.pacs; Widget* widget = picopass->widget; - uint8_t csn[PICOPASS_BLOCK_LEN]; - memcpy(csn, &AA1->data[PICOPASS_CSN_BLOCK_INDEX], PICOPASS_BLOCK_LEN); + uint8_t csn[PICOPASS_BLOCK_LEN] = {0}; + memcpy(csn, AA1[PICOPASS_CSN_BLOCK_INDEX].data, PICOPASS_BLOCK_LEN); for(uint8_t i = 0; i < PICOPASS_BLOCK_LEN; i++) { - furi_string_cat_printf(csn_str, " %02X", csn[i]); + furi_string_cat_printf(csn_str, "%02X ", csn[i]); } - // Neither of these are valid. Indicates the block was all 0x00 or all 0xff - if(pacs->record.bitLength == 0 || pacs->record.bitLength == 255) { + bool no_key = picopass_is_memset(pacs->key, 0x00, PICOPASS_BLOCK_LEN); + bool empty = + picopass_is_memset(AA1[PICOPASS_PACS_CFG_BLOCK_INDEX].data, 0xFF, PICOPASS_BLOCK_LEN); + + if(no_key) { furi_string_cat_printf(wiegand_str, "Read Failed"); if(pacs->se_enabled) { furi_string_cat_printf(credential_str, "SE enabled"); } + } else if(empty) { + furi_string_cat_printf(wiegand_str, "Empty"); + } else if(pacs->record.bitLength == 0 || pacs->record.bitLength == 255) { + // Neither of these are valid. Indicates the block was all 0x00 or all 0xff + furi_string_cat_printf(wiegand_str, "Invalid PACS"); - widget_add_button_element( - widget, - GuiButtonTypeLeft, - "Retry", - picopass_scene_read_card_success_widget_callback, - picopass); - + if(pacs->se_enabled) { + furi_string_cat_printf(credential_str, "SE enabled"); + } } else { size_t bytesLength = 1 + pacs->record.bitLength / 8; furi_string_set(credential_str, ""); @@ -69,12 +74,18 @@ void picopass_scene_read_card_success_on_enter(void* context) { furi_string_cat_printf(sio_str, "+SIO"); } - widget_add_button_element( - widget, - GuiButtonTypeLeft, - "Retry", - picopass_scene_read_card_success_widget_callback, - picopass); + if(pacs->key) { + if(pacs->sio) { + furi_string_cat_printf(sio_str, " "); + } + furi_string_cat_printf(sio_str, "Key: "); + + uint8_t key[PICOPASS_BLOCK_LEN]; + memcpy(key, &pacs->key, PICOPASS_BLOCK_LEN); + for(uint8_t i = 0; i < PICOPASS_BLOCK_LEN; i++) { + furi_string_cat_printf(sio_str, "%02X", key[i]); + } + } widget_add_button_element( widget, @@ -84,6 +95,13 @@ void picopass_scene_read_card_success_on_enter(void* context) { picopass); } + widget_add_button_element( + widget, + GuiButtonTypeLeft, + "Retry", + picopass_scene_read_card_success_widget_callback, + picopass); + widget_add_string_element( widget, 64, 5, AlignCenter, AlignCenter, FontSecondary, furi_string_get_cstr(csn_str)); widget_add_string_element( diff --git a/applications/plugins/picopass/scenes/picopass_scene_read_factory_success.c b/applications/plugins/picopass/scenes/picopass_scene_read_factory_success.c new file mode 100644 index 000000000..bc07bb953 --- /dev/null +++ b/applications/plugins/picopass/scenes/picopass_scene_read_factory_success.c @@ -0,0 +1,80 @@ +#include "../picopass_i.h" +#include +#include "../picopass_keys.h" + +void picopass_scene_read_factory_success_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + furi_assert(context); + Picopass* picopass = context; + + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(picopass->view_dispatcher, result); + } +} + +void picopass_scene_read_factory_success_on_enter(void* context) { + Picopass* picopass = context; + FuriString* title = furi_string_alloc_set("Factory Default"); + FuriString* subtitle = furi_string_alloc_set(""); + + DOLPHIN_DEED(DolphinDeedNfcReadSuccess); + + // Send notification + notification_message(picopass->notifications, &sequence_success); + + // Setup view + Widget* widget = picopass->widget; + //PicopassPacs* pacs = &picopass->dev->dev_data.pacs; + PicopassBlock* AA1 = picopass->dev->dev_data.AA1; + + uint8_t* configBlock = AA1[PICOPASS_CONFIG_BLOCK_INDEX].data; + uint8_t fuses = configBlock[7]; + + if((fuses & 0x80) == 0x80) { + furi_string_cat_printf(subtitle, "Personalization mode"); + } else { + furi_string_cat_printf(subtitle, "Application mode"); + } + + widget_add_button_element( + widget, + GuiButtonTypeCenter, + "Write Standard iClass Key", + picopass_scene_read_factory_success_widget_callback, + picopass); + + widget_add_string_element( + widget, 64, 5, AlignCenter, AlignCenter, FontSecondary, furi_string_get_cstr(title)); + widget_add_string_element( + widget, 64, 20, AlignCenter, AlignCenter, FontPrimary, furi_string_get_cstr(subtitle)); + + furi_string_free(title); + furi_string_free(subtitle); + + view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewWidget); +} + +bool picopass_scene_read_factory_success_on_event(void* context, SceneManagerEvent event) { + Picopass* picopass = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeLeft) { + consumed = scene_manager_previous_scene(picopass->scene_manager); + } else if(event.event == GuiButtonTypeCenter) { + memcpy(picopass->dev->dev_data.pacs.key, picopass_iclass_key, PICOPASS_BLOCK_LEN); + scene_manager_next_scene(picopass->scene_manager, PicopassSceneWriteKey); + consumed = true; + } + } + return consumed; +} + +void picopass_scene_read_factory_success_on_exit(void* context) { + Picopass* picopass = context; + + // Clear view + widget_reset(picopass->widget); +} diff --git a/applications/plugins/picopass/scenes/picopass_scene_save_name.c b/applications/plugins/picopass/scenes/picopass_scene_save_name.c index 8239002d9..baf882b80 100644 --- a/applications/plugins/picopass/scenes/picopass_scene_save_name.c +++ b/applications/plugins/picopass/scenes/picopass_scene_save_name.c @@ -21,7 +21,7 @@ void picopass_scene_save_name_on_enter(void* context) { } else { picopass_text_store_set(picopass, picopass->dev->dev_name); } - text_input_set_header_text(text_input, "Name The Card"); + text_input_set_header_text(text_input, "Name the card"); text_input_set_result_callback( text_input, picopass_scene_save_name_text_input_callback, @@ -31,12 +31,10 @@ void picopass_scene_save_name_on_enter(void* context) { dev_name_empty); FuriString* folder_path; - folder_path = furi_string_alloc(); + folder_path = furi_string_alloc_set(STORAGE_APP_DATA_PATH_PREFIX); if(furi_string_end_with(picopass->dev->load_path, PICOPASS_APP_EXTENSION)) { path_extract_dirname(furi_string_get_cstr(picopass->dev->load_path), folder_path); - } else { - furi_string_set(folder_path, PICOPASS_APP_FOLDER); } ValidatorIsFile* validator_is_file = validator_is_file_alloc_init( diff --git a/applications/plugins/picopass/scenes/picopass_scene_start.c b/applications/plugins/picopass/scenes/picopass_scene_start.c index 6d1aeedcd..d33a1d264 100644 --- a/applications/plugins/picopass/scenes/picopass_scene_start.c +++ b/applications/plugins/picopass/scenes/picopass_scene_start.c @@ -32,13 +32,18 @@ bool picopass_scene_start_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == SubmenuIndexRead) { + scene_manager_set_scene_state( + picopass->scene_manager, PicopassSceneStart, SubmenuIndexRead); scene_manager_next_scene(picopass->scene_manager, PicopassSceneReadCard); consumed = true; } else if(event.event == SubmenuIndexSaved) { + // Explicitly save state so that the correct item is + // reselected if the user cancels loading a file. + scene_manager_set_scene_state( + picopass->scene_manager, PicopassSceneStart, SubmenuIndexSaved); scene_manager_next_scene(picopass->scene_manager, PicopassSceneFileSelect); consumed = true; } - scene_manager_set_scene_state(picopass->scene_manager, PicopassSceneStart, event.event); } return consumed; diff --git a/applications/plugins/picopass/scenes/picopass_scene_write_card_success.c b/applications/plugins/picopass/scenes/picopass_scene_write_card_success.c index 108e7d1ce..4bbca816a 100644 --- a/applications/plugins/picopass/scenes/picopass_scene_write_card_success.c +++ b/applications/plugins/picopass/scenes/picopass_scene_write_card_success.c @@ -16,6 +16,7 @@ void picopass_scene_write_card_success_widget_callback( void picopass_scene_write_card_success_on_enter(void* context) { Picopass* picopass = context; Widget* widget = picopass->widget; + FuriString* str = furi_string_alloc_set("Write Success!"); DOLPHIN_DEED(DolphinDeedNfcReadSuccess); @@ -29,6 +30,18 @@ void picopass_scene_write_card_success_on_enter(void* context) { picopass_scene_write_card_success_widget_callback, picopass); + widget_add_button_element( + widget, + GuiButtonTypeRight, + "Menu", + picopass_scene_write_card_success_widget_callback, + picopass); + + widget_add_string_element( + widget, 64, 5, AlignCenter, AlignCenter, FontSecondary, furi_string_get_cstr(str)); + + furi_string_free(str); + view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewWidget); } diff --git a/applications/plugins/picopass/scenes/picopass_scene_write_key.c b/applications/plugins/picopass/scenes/picopass_scene_write_key.c new file mode 100644 index 000000000..0f417e1c3 --- /dev/null +++ b/applications/plugins/picopass/scenes/picopass_scene_write_key.c @@ -0,0 +1,53 @@ +#include "../picopass_i.h" +#include + +void picopass_write_key_worker_callback(PicopassWorkerEvent event, void* context) { + UNUSED(event); + Picopass* picopass = context; + view_dispatcher_send_custom_event(picopass->view_dispatcher, PicopassCustomEventWorkerExit); +} + +void picopass_scene_write_key_on_enter(void* context) { + Picopass* picopass = context; + DOLPHIN_DEED(DolphinDeedNfcSave); + + // Setup view + Popup* popup = picopass->popup; + popup_set_header(popup, "Writing\niClass\nkey", 68, 30, AlignLeft, AlignTop); + popup_set_icon(popup, 0, 3, &I_RFIDDolphinSend_97x61); + + // Start worker + view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewPopup); + picopass_worker_start( + picopass->worker, + PicopassWorkerStateWriteKey, + &picopass->dev->dev_data, + picopass_write_key_worker_callback, + picopass); + + picopass_blink_start(picopass); +} + +bool picopass_scene_write_key_on_event(void* context, SceneManagerEvent event) { + Picopass* picopass = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == PicopassCustomEventWorkerExit) { + scene_manager_next_scene(picopass->scene_manager, PicopassSceneWriteCardSuccess); + consumed = true; + } + } + return consumed; +} + +void picopass_scene_write_key_on_exit(void* context) { + Picopass* picopass = context; + + // Stop worker + picopass_worker_stop(picopass->worker); + // Clear view + popup_reset(picopass->popup); + + picopass_blink_stop(picopass); +} diff --git a/applications/main/sub_playlist/application.fam b/applications/plugins/playlist/application.fam similarity index 60% rename from applications/main/sub_playlist/application.fam rename to applications/plugins/playlist/application.fam index 3a6de14da..06357e24b 100644 --- a/applications/main/sub_playlist/application.fam +++ b/applications/plugins/playlist/application.fam @@ -1,12 +1,13 @@ App( - appid="sub_playlist", + appid="SubGHz_Playlist", name="Sub-GHz Playlist", - apptype=FlipperAppType.APP, + apptype=FlipperAppType.EXTERNAL, entry_point="playlist_app", cdefines=["APP_PLAYLIST"], requires=["storage", "gui", "dialogs", "subghz"], stack_size=2 * 1024, - order=12, + order=14, fap_icon="playlist_10px.png", - icon="A_Sub_Playlist_14", + fap_category="Sub-GHz", + fap_icon_assets="images", ) diff --git a/applications/main/sub_playlist/canvas_helper.c b/applications/plugins/playlist/canvas_helper.c similarity index 100% rename from applications/main/sub_playlist/canvas_helper.c rename to applications/plugins/playlist/canvas_helper.c diff --git a/applications/main/sub_playlist/canvas_helper.h b/applications/plugins/playlist/canvas_helper.h similarity index 100% rename from applications/main/sub_playlist/canvas_helper.h rename to applications/plugins/playlist/canvas_helper.h diff --git a/applications/main/sub_playlist/images/ButtonRight_4x7.png b/applications/plugins/playlist/images/ButtonRight_4x7.png similarity index 100% rename from applications/main/sub_playlist/images/ButtonRight_4x7.png rename to applications/plugins/playlist/images/ButtonRight_4x7.png diff --git a/applications/main/sub_playlist/images/sub1_10px.png b/applications/plugins/playlist/images/sub1_10px.png similarity index 100% rename from applications/main/sub_playlist/images/sub1_10px.png rename to applications/plugins/playlist/images/sub1_10px.png diff --git a/applications/main/sub_playlist/playlist.c b/applications/plugins/playlist/playlist.c similarity index 89% rename from applications/main/sub_playlist/playlist.c rename to applications/plugins/playlist/playlist.c index 7bf433d09..1b16b1749 100644 --- a/applications/main/sub_playlist/playlist.c +++ b/applications/plugins/playlist/playlist.c @@ -6,7 +6,7 @@ #include #include -#include +#include #include #include @@ -61,6 +61,8 @@ typedef struct { bool ctl_request_exit; // can be set to true if the worker should exit bool ctl_pause; // can be set to true if the worker should pause + bool ctl_request_skip; // can be set to true if the worker should skip the current file + bool ctl_request_prev; // can be set to true if the worker should go to the previous file bool is_running; // indicates if the worker is running } PlaylistWorker; @@ -185,6 +187,18 @@ static int playlist_worker_process( status = 1; break; } + if(worker->ctl_request_skip) { + worker->ctl_request_skip = false; + FURI_LOG_D(TAG, " (TX) Requested to skip. Cancelling and resending..."); + status = 0; + break; + } + if(worker->ctl_request_prev) { + worker->ctl_request_prev = false; + FURI_LOG_D(TAG, " (TX) Requested to prev. Cancelling and resending..."); + status = 3; + break; + } furi_delay_ms(50); } @@ -213,6 +227,22 @@ static bool playlist_worker_wait_pause(PlaylistWorker* worker) { return true; } +void updatePlayListView(PlaylistWorker* worker, const char* str) { + furi_string_reset(worker->meta->prev_3_path); + furi_string_set(worker->meta->prev_3_path, furi_string_get_cstr(worker->meta->prev_2_path)); + + furi_string_reset(worker->meta->prev_2_path); + furi_string_set(worker->meta->prev_2_path, furi_string_get_cstr(worker->meta->prev_1_path)); + + furi_string_reset(worker->meta->prev_1_path); + furi_string_set(worker->meta->prev_1_path, furi_string_get_cstr(worker->meta->prev_0_path)); + + furi_string_reset(worker->meta->prev_0_path); + furi_string_set(worker->meta->prev_0_path, str); + + view_port_update(worker->meta->view_port); +} + static bool playlist_worker_play_playlist_once( PlaylistWorker* worker, Storage* storage, @@ -226,6 +256,7 @@ static bool playlist_worker_play_playlist_once( FURI_LOG_E(TAG, "Failed to rewind file"); return false; } + while(flipper_format_read_string(fff_head, "sub", data)) { if(!playlist_worker_wait_pause(worker)) { break; @@ -238,18 +269,7 @@ static bool playlist_worker_play_playlist_once( const char* str = furi_string_get_cstr(data); // it's not fancy, but it works for now :) - furi_string_reset(worker->meta->prev_3_path); - furi_string_set( - worker->meta->prev_3_path, furi_string_get_cstr(worker->meta->prev_2_path)); - furi_string_reset(worker->meta->prev_2_path); - furi_string_set( - worker->meta->prev_2_path, furi_string_get_cstr(worker->meta->prev_1_path)); - furi_string_reset(worker->meta->prev_1_path); - furi_string_set( - worker->meta->prev_1_path, furi_string_get_cstr(worker->meta->prev_0_path)); - furi_string_reset(worker->meta->prev_0_path); - furi_string_set(worker->meta->prev_0_path, str); - view_port_update(worker->meta->view_port); + updatePlayListView(worker, str); for(int i = 0; i < 1; i++) { if(!playlist_worker_wait_pause(worker)) { @@ -279,6 +299,23 @@ static bool playlist_worker_play_playlist_once( // exited, exit loop } else if(status == 2) { return false; + } else if(status == 3) { + //aqui rebobinamos y avanzamos de nuevo el fichero n-1 veces + //decrementamos el contador de ficheros enviados + worker->meta->current_count--; + if(worker->meta->current_count > 0) { + worker->meta->current_count--; + } + //rebobinamos el fichero + if(!flipper_format_rewind(fff_head)) { + FURI_LOG_E(TAG, "Failed to rewind file"); + return false; + } + //avanzamos el fichero n-1 veces + for(int j = 0; j < worker->meta->current_count; j++) { + flipper_format_read_string(fff_head, "sub", data); + } + break; } } } // end of loop @@ -674,6 +711,9 @@ int32_t playlist_app(void* p) { Playlist* app = playlist_alloc(meta); meta->view_port = app->view_port; + // Enable power for External CC1101 if it is connected + furi_hal_subghz_enable_ext_power(); + furi_hal_power_suppress_charge_enter(); // select playlist file @@ -683,9 +723,8 @@ int32_t playlist_app(void* p) { dialog_file_browser_set_basic_options(&browser_options, PLAYLIST_EXT, &I_sub1_10px); browser_options.base_path = PLAYLIST_FOLDER; - bool res = + const bool res = dialog_file_browser_show(dialogs, app->file_path, app->file_path, &browser_options); - furi_record_close(RECORD_DIALOGS); // check if a file was selected if(!res) { @@ -711,6 +750,10 @@ int32_t playlist_app(void* p) { if(input.type == InputTypeShort && app->meta->playlist_repetitions > 0) { --app->meta->playlist_repetitions; } + } else if(app->meta->state == STATE_SENDING) { + if(input.type == InputTypeShort) { + app->worker->ctl_request_prev = true; + } } break; @@ -719,6 +762,10 @@ int32_t playlist_app(void* p) { if(input.type == InputTypeShort) { ++app->meta->playlist_repetitions; } + } else if(app->meta->state == STATE_SENDING) { + if(input.type == InputTypeShort) { + app->worker->ctl_request_skip = true; + } } break; @@ -755,6 +802,8 @@ int32_t playlist_app(void* p) { exit_cleanup: furi_hal_power_suppress_charge_exit(); + // Disable power for External CC1101 if it was enabled and module is connected + furi_hal_subghz_disable_ext_power(); if(app->worker != NULL) { if(playlist_worker_running(app->worker)) { diff --git a/applications/plugins/subbrute/images/subbrute_10px.png b/applications/plugins/playlist/playlist_10px.png similarity index 55% rename from applications/plugins/subbrute/images/subbrute_10px.png rename to applications/plugins/playlist/playlist_10px.png index 57d6f6a45..fc33471f7 100644 Binary files a/applications/plugins/subbrute/images/subbrute_10px.png and b/applications/plugins/playlist/playlist_10px.png differ diff --git a/applications/main/sub_playlist/playlist_file.c b/applications/plugins/playlist/playlist_file.c similarity index 100% rename from applications/main/sub_playlist/playlist_file.c rename to applications/plugins/playlist/playlist_file.c diff --git a/applications/main/sub_playlist/playlist_file.h b/applications/plugins/playlist/playlist_file.h similarity index 100% rename from applications/main/sub_playlist/playlist_file.h rename to applications/plugins/playlist/playlist_file.h diff --git a/applications/plugins/pocsag_pager/application.fam b/applications/plugins/pocsag_pager/application.fam index 243acaab5..86f8d528b 100644 --- a/applications/plugins/pocsag_pager/application.fam +++ b/applications/plugins/pocsag_pager/application.fam @@ -1,5 +1,5 @@ App( - appid="POCSAG_Pager", + appid="pocsag_pager", name="POCSAG Pager", apptype=FlipperAppType.PLUGIN, entry_point="pocsag_pager_app", @@ -8,6 +8,6 @@ App( stack_size=4 * 1024, order=50, fap_icon="pocsag_pager_10px.png", - fap_category="Tools", + fap_category="Sub-GHz", fap_icon_assets="images", ) diff --git a/applications/plugins/pocsag_pager/pocsag_pager_app.c b/applications/plugins/pocsag_pager/pocsag_pager_app.c index 3ac242304..123b3ee9d 100644 --- a/applications/plugins/pocsag_pager/pocsag_pager_app.c +++ b/applications/plugins/pocsag_pager/pocsag_pager_app.c @@ -122,6 +122,9 @@ POCSAGPagerApp* pocsag_pager_app_alloc() { app->txrx->worker, (SubGhzWorkerPairCallback)subghz_receiver_decode); subghz_worker_set_context(app->txrx->worker, app->txrx->receiver); + // Enable power for External CC1101 if it is connected + furi_hal_subghz_enable_ext_power(); + furi_hal_power_suppress_charge_enter(); scene_manager_next_scene(app->scene_manager, POCSAGPagerSceneStart); @@ -135,6 +138,9 @@ void pocsag_pager_app_free(POCSAGPagerApp* app) { //CC1101 off pcsg_sleep(app); + // Disable power for External CC1101 if it was enabled and module is connected + furi_hal_subghz_disable_ext_power(); + // Submenu view_dispatcher_remove_view(app->view_dispatcher, POCSAGPagerViewSubmenu); submenu_free(app->submenu); diff --git a/applications/plugins/pocsag_pager/pocsag_pager_app_i.c b/applications/plugins/pocsag_pager/pocsag_pager_app_i.c index ba6e87c28..ff73ab50e 100644 --- a/applications/plugins/pocsag_pager/pocsag_pager_app_i.c +++ b/applications/plugins/pocsag_pager/pocsag_pager_app_i.c @@ -40,7 +40,7 @@ void pcsg_begin(POCSAGPagerApp* app, uint8_t* preset_data) { furi_hal_subghz_reset(); furi_hal_subghz_idle(); furi_hal_subghz_load_custom_preset(preset_data); - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(furi_hal_subghz.cc1101_g0_pin, GpioModeInput, GpioPullNo, GpioSpeedLow); app->txrx->txrx_state = PCSGTxRxStateIDLE; } @@ -54,7 +54,7 @@ uint32_t pcsg_rx(POCSAGPagerApp* app, uint32_t frequency) { furi_hal_subghz_idle(); uint32_t value = furi_hal_subghz_set_frequency_and_path(frequency); - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(furi_hal_subghz.cc1101_g0_pin, GpioModeInput, GpioPullNo, GpioSpeedLow); furi_hal_subghz_flush_rx(); furi_hal_subghz_rx(); diff --git a/applications/plugins/pocsag_pager/protocols/pcsg_generic.c b/applications/plugins/pocsag_pager/protocols/pcsg_generic.c index 890ed43d7..5425fd512 100644 --- a/applications/plugins/pocsag_pager/protocols/pcsg_generic.c +++ b/applications/plugins/pocsag_pager/protocols/pcsg_generic.c @@ -21,12 +21,12 @@ void pcsg_block_generic_get_preset_name(const char* preset_name, FuriString* pre furi_string_set(preset_str, preset_name_temp); } -bool pcsg_block_generic_serialize( +SubGhzProtocolStatus pcsg_block_generic_serialize( PCSGBlockGeneric* instance, FlipperFormat* flipper_format, SubGhzRadioPreset* preset) { furi_assert(instance); - bool res = false; + SubGhzProtocolStatus res = SubGhzProtocolStatusError; FuriString* temp_str; temp_str = furi_string_alloc(); do { @@ -75,15 +75,16 @@ bool pcsg_block_generic_serialize( break; } - res = true; + res = SubGhzProtocolStatusOk; } while(false); furi_string_free(temp_str); return res; } -bool pcsg_block_generic_deserialize(PCSGBlockGeneric* instance, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + pcsg_block_generic_deserialize(PCSGBlockGeneric* instance, FlipperFormat* flipper_format) { furi_assert(instance); - bool res = false; + SubGhzProtocolStatus res = SubGhzProtocolStatusError; FuriString* temp_data = furi_string_alloc(); FuriString* temp_data2 = furi_string_alloc(); @@ -113,7 +114,7 @@ bool pcsg_block_generic_deserialize(PCSGBlockGeneric* instance, FlipperFormat* f instance->result_msg = furi_string_alloc_set(temp_data); } - res = true; + res = SubGhzProtocolStatusOk; } while(0); furi_string_free(temp_data); diff --git a/applications/plugins/pocsag_pager/protocols/pcsg_generic.h b/applications/plugins/pocsag_pager/protocols/pcsg_generic.h index ff925b6c9..126b54ffe 100644 --- a/applications/plugins/pocsag_pager/protocols/pcsg_generic.h +++ b/applications/plugins/pocsag_pager/protocols/pcsg_generic.h @@ -35,7 +35,7 @@ void pcsg_block_generic_get_preset_name(const char* preset_name, FuriString* pre * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ -bool pcsg_block_generic_serialize( +SubGhzProtocolStatus pcsg_block_generic_serialize( PCSGBlockGeneric* instance, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); @@ -46,7 +46,8 @@ bool pcsg_block_generic_serialize( * @param flipper_format Pointer to a FlipperFormat instance * @return true On success */ -bool pcsg_block_generic_deserialize(PCSGBlockGeneric* instance, FlipperFormat* flipper_format); +SubGhzProtocolStatus + pcsg_block_generic_deserialize(PCSGBlockGeneric* instance, FlipperFormat* flipper_format); float pcsg_block_generic_fahrenheit_to_celsius(float fahrenheit); diff --git a/applications/plugins/pocsag_pager/protocols/pocsag.c b/applications/plugins/pocsag_pager/protocols/pocsag.c index 69d09d554..ca210c2a4 100644 --- a/applications/plugins/pocsag_pager/protocols/pocsag.c +++ b/applications/plugins/pocsag_pager/protocols/pocsag.c @@ -288,7 +288,7 @@ uint8_t subghz_protocol_decoder_pocsag_get_hash_data(void* context) { return hash; } -bool subghz_protocol_decoder_pocsag_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_pocsag_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset) { @@ -296,31 +296,35 @@ bool subghz_protocol_decoder_pocsag_serialize( SubGhzProtocolDecoderPocsag* instance = context; uint32_t msg_len; - if(!pcsg_block_generic_serialize(&instance->generic, flipper_format, preset)) return false; + if(SubGhzProtocolStatusOk != + pcsg_block_generic_serialize(&instance->generic, flipper_format, preset)) + return SubGhzProtocolStatusError; msg_len = furi_string_size(instance->done_msg); if(!flipper_format_write_uint32(flipper_format, "MsgLen", &msg_len, 1)) { FURI_LOG_E(TAG, "Error adding MsgLen"); - return false; + return SubGhzProtocolStatusError; } uint8_t* s = (uint8_t*)furi_string_get_cstr(instance->done_msg); if(!flipper_format_write_hex(flipper_format, "Msg", s, msg_len)) { FURI_LOG_E(TAG, "Error adding Msg"); - return false; + return SubGhzProtocolStatusError; } - return true; + return SubGhzProtocolStatusOk; } -bool subghz_protocol_decoder_pocsag_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_decoder_pocsag_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolDecoderPocsag* instance = context; - bool ret = false; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; uint32_t msg_len; uint8_t* buf; do { - if(!pcsg_block_generic_deserialize(&instance->generic, flipper_format)) { + if(SubGhzProtocolStatusOk != + pcsg_block_generic_deserialize(&instance->generic, flipper_format)) { break; } @@ -338,7 +342,7 @@ bool subghz_protocol_decoder_pocsag_deserialize(void* context, FlipperFormat* fl furi_string_set_strn(instance->done_msg, (const char*)buf, msg_len); free(buf); - ret = true; + ret = SubGhzProtocolStatusOk; } while(false); return ret; } diff --git a/applications/plugins/pocsag_pager/scenes/pocsag_pager_receiver.c b/applications/plugins/pocsag_pager/scenes/pocsag_pager_receiver.c index 3f41b9b5d..658b70fea 100644 --- a/applications/plugins/pocsag_pager/scenes/pocsag_pager_receiver.c +++ b/applications/plugins/pocsag_pager/scenes/pocsag_pager_receiver.c @@ -195,6 +195,10 @@ bool pocsag_pager_scene_receiver_on_event(void* context, SceneManagerEvent event pcsg_hopper_update(app); pocsag_pager_scene_receiver_update_statusbar(app); } + // Get current RSSI + float rssi = furi_hal_subghz_get_rssi(); + pcsg_receiver_rssi(app->pcsg_receiver, rssi); + if(app->txrx->txrx_state == PCSGTxRxStateRx) { notification_message(app->notifications, &sequence_blink_cyan_10); } diff --git a/applications/plugins/pocsag_pager/views/pocsag_pager_receiver.c b/applications/plugins/pocsag_pager/views/pocsag_pager_receiver.c index 972c8dafb..62a2b2968 100644 --- a/applications/plugins/pocsag_pager/views/pocsag_pager_receiver.c +++ b/applications/plugins/pocsag_pager/views/pocsag_pager_receiver.c @@ -1,6 +1,6 @@ #include "pocsag_pager_receiver.h" #include "../pocsag_pager_app_i.h" -#include +#include #include #include @@ -12,6 +12,8 @@ #define MENU_ITEMS 4u #define UNLOCK_CNT 3 +#define SUBGHZ_RAW_TRESHOLD_MIN -90.0f + typedef struct { FuriString* item_str; uint8_t type; @@ -58,8 +60,24 @@ typedef struct { uint16_t list_offset; uint16_t history_item; PCSGReceiverBarShow bar_show; + uint8_t u_rssi; } PCSGReceiverModel; +void pcsg_receiver_rssi(PCSGReceiver* instance, float rssi) { + furi_assert(instance); + with_view_model( + instance->view, + PCSGReceiverModel * model, + { + if(rssi < SUBGHZ_RAW_TRESHOLD_MIN) { + model->u_rssi = 0; + } else { + model->u_rssi = (uint8_t)(rssi - SUBGHZ_RAW_TRESHOLD_MIN); + } + }, + true); +} + void pcsg_view_receiver_set_lock(PCSGReceiver* pcsg_receiver, PCSGLock lock) { furi_assert(pcsg_receiver); pcsg_receiver->lock_count = 0; @@ -167,13 +185,23 @@ static void pcsg_view_receiver_draw_frame(Canvas* canvas, uint16_t idx, bool scr canvas_draw_dot(canvas, scrollbar ? 121 : 126, (0 + idx * FRAME_HEIGHT) + 11); } +static void pcsg_view_rssi_draw(Canvas* canvas, PCSGReceiverModel* model) { + for(uint8_t i = 1; i < model->u_rssi; i++) { + if(i % 5) { + canvas_draw_dot(canvas, 46 + i, 50); + canvas_draw_dot(canvas, 47 + i, 51); + canvas_draw_dot(canvas, 46 + i, 52); + } + } +} + void pcsg_view_receiver_draw(Canvas* canvas, PCSGReceiverModel* model) { canvas_clear(canvas); canvas_set_color(canvas, ColorBlack); canvas_set_font(canvas, FontSecondary); elements_button_left(canvas, "Config"); - canvas_draw_line(canvas, 46, 51, 125, 51); + //canvas_draw_line(canvas, 46, 51, 125, 51); bool scrollbar = model->history_item > 4; FuriString* str_buff; @@ -207,10 +235,13 @@ void pcsg_view_receiver_draw(Canvas* canvas, PCSGReceiverModel* model) { canvas_draw_icon(canvas, 0, 0, &I_Scanning_123x52); canvas_set_font(canvas, FontPrimary); canvas_draw_str(canvas, 63, 46, "Scanning..."); - canvas_draw_line(canvas, 46, 51, 125, 51); + //canvas_draw_line(canvas, 46, 51, 125, 51); canvas_set_font(canvas, FontSecondary); } + // Draw RSSI + pcsg_view_rssi_draw(canvas, model); + switch(model->bar_show) { case PCSGReceiverBarShowLock: canvas_draw_icon(canvas, 64, 55, &I_Lock_7x8); diff --git a/applications/plugins/pocsag_pager/views/pocsag_pager_receiver.h b/applications/plugins/pocsag_pager/views/pocsag_pager_receiver.h index 5ea2d4859..a13f5cdc7 100644 --- a/applications/plugins/pocsag_pager/views/pocsag_pager_receiver.h +++ b/applications/plugins/pocsag_pager/views/pocsag_pager_receiver.h @@ -8,6 +8,8 @@ typedef struct PCSGReceiver PCSGReceiver; typedef void (*PCSGReceiverCallback)(PCSGCustomEvent event, void* context); +void pcsg_receiver_rssi(PCSGReceiver* instance, float rssi); + void pcsg_view_receiver_set_lock(PCSGReceiver* pcsg_receiver, PCSGLock keyboard); void pcsg_view_receiver_set_callback( diff --git a/applications/plugins/pocsag_pager/views/pocsag_pager_receiver_info.c b/applications/plugins/pocsag_pager/views/pocsag_pager_receiver_info.c index 99bb1002a..4811f3902 100644 --- a/applications/plugins/pocsag_pager/views/pocsag_pager_receiver_info.c +++ b/applications/plugins/pocsag_pager/views/pocsag_pager_receiver_info.c @@ -1,6 +1,6 @@ #include "pocsag_pager_receiver.h" #include "../pocsag_pager_app_i.h" -#include "POCSAG_Pager_icons.h" +#include "pocsag_pager_icons.h" #include "../protocols/pcsg_generic.h" #include #include diff --git a/applications/plugins/pomodoro/application.fam b/applications/plugins/pomodoro/application.fam index ceb8cccc9..847d91606 100644 --- a/applications/plugins/pomodoro/application.fam +++ b/applications/plugins/pomodoro/application.fam @@ -1,6 +1,6 @@ App( - appid="Pomodoro", - name="Pomodoro", + appid="flipp_pomodoro", + name="Pomodoro Timer", apptype=FlipperAppType.EXTERNAL, entry_point="flipp_pomodoro_app", requires=["gui", "notification", "dolphin"], @@ -8,5 +8,4 @@ App( fap_category="Tools", fap_icon_assets="images", fap_icon="flipp_pomodoro_10.png", - fap_icon_assets_symbol="flipp_pomodoro", ) diff --git a/applications/plugins/pomodoro/flipp_pomodoro_app.c b/applications/plugins/pomodoro/flipp_pomodoro_app.c index ed5b8282b..5adca1edb 100644 --- a/applications/plugins/pomodoro/flipp_pomodoro_app.c +++ b/applications/plugins/pomodoro/flipp_pomodoro_app.c @@ -29,11 +29,6 @@ static bool flipp_pomodoro_app_custom_event_callback(void* ctx, uint32_t event) app->view_dispatcher, FlippPomodoroAppCustomEventStateUpdated); return CustomEventConsumed; case FlippPomodoroAppCustomEventStageComplete: - if(flipp_pomodoro__get_stage(app->state) == FlippPomodoroStageFocus) { - // REGISTER a deed on work stage complete to get an acheivement - DOLPHIN_DEED(DolphinDeedPluginGameWin); - }; - flipp_pomodoro__toggle_stage(app->state); notification_message( app->notification_app, @@ -83,7 +78,6 @@ void flipp_pomodoro_app_free(FlippPomodoroApp* app) { view_dispatcher_free(app->view_dispatcher); scene_manager_free(app->scene_manager); flipp_pomodoro_view_timer_free(app->timer_view); - flipp_pomodoro__destroy(app->state); free(app); furi_record_close(RECORD_GUI); furi_record_close(RECORD_NOTIFICATION); @@ -98,4 +92,4 @@ int32_t flipp_pomodoro_app(void* p) { flipp_pomodoro_app_free(app); return 0; -}; \ No newline at end of file +}; diff --git a/applications/plugins/pomodoro/flipp_pomodoro_app_i.h b/applications/plugins/pomodoro/flipp_pomodoro_app_i.h index 492ef9c2d..03c3dc894 100644 --- a/applications/plugins/pomodoro/flipp_pomodoro_app_i.h +++ b/applications/plugins/pomodoro/flipp_pomodoro_app_i.h @@ -15,7 +15,6 @@ #include #include #include -#include #include // App resource imports diff --git a/applications/plugins/pomodoro/modules/flipp_pomodoro.c b/applications/plugins/pomodoro/modules/flipp_pomodoro.c index 9915025fb..161e862f8 100644 --- a/applications/plugins/pomodoro/modules/flipp_pomodoro.c +++ b/applications/plugins/pomodoro/modules/flipp_pomodoro.c @@ -55,11 +55,6 @@ char* flipp_pomodoro__next_stage_label(FlippPomodoroState* state) { return next_stage_label[flipp_pomodoro__stage_by_index(state->current_stage_index + 1)]; }; -void flipp_pomodoro__destroy(FlippPomodoroState* state) { - furi_assert(state); - free(state); -}; - uint32_t flipp_pomodoro__current_stage_total_duration(FlippPomodoroState* state) { const int32_t stage_duration_seconds_map[] = { [FlippPomodoroStageFocus] = 25 * TIME_SECONDS_IN_MINUTE, diff --git a/applications/plugins/pong/flipper_pong.c b/applications/plugins/pong/flipper_pong.c index 0e80e6ff3..55b371ad5 100644 --- a/applications/plugins/pong/flipper_pong.c +++ b/applications/plugins/pong/flipper_pong.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #define SCREEN_SIZE_X 128 @@ -14,7 +15,8 @@ #define PAD_SIZE_X 3 #define PAD_SIZE_Y 8 -#define PLAYER1_PAD_SPEED 2 +#define PLAYER1_PAD_SPEED 4 + #define PLAYER2_PAD_SPEED 2 #define BALL_SIZE 4 @@ -29,14 +31,16 @@ typedef struct { } EventApp; typedef struct Players { + FuriMutex* mutex; uint8_t player1_X, player1_Y, player2_X, player2_Y; uint16_t player1_score, player2_score; uint8_t ball_X, ball_Y, ball_X_speed, ball_Y_speed, ball_X_direction, ball_Y_direction; } Players; static void draw_callback(Canvas* canvas, void* ctx) { - UNUSED(ctx); - Players* playersMutex = (Players*)acquire_mutex_block((ValueMutex*)ctx); + furi_assert(ctx); + Players* playersMutex = ctx; + furi_mutex_acquire(playersMutex->mutex, FuriWaitForever); canvas_draw_frame(canvas, 0, 0, 128, 64); canvas_draw_box( @@ -57,7 +61,7 @@ static void draw_callback(Canvas* canvas, void* ctx) { canvas_draw_str_aligned( canvas, SCREEN_SIZE_X / 2 + 15, SCREEN_SIZE_Y / 2 + 2, AlignCenter, AlignTop, buffer); - release_mutex((ValueMutex*)ctx, playersMutex); + furi_mutex_release(playersMutex->mutex); } static void input_callback(InputEvent* input_event, void* ctx) { @@ -97,7 +101,8 @@ uint8_t changeDirection() { return randomuint8[0]; } -int32_t flipper_pong_app() { +int32_t flipper_pong_app(void* p) { + UNUSED(p); EventApp event; FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(EventApp)); @@ -117,78 +122,83 @@ int32_t flipper_pong_app() { players.ball_X_direction = changeDirection(); players.ball_Y_direction = changeDirection(); - ValueMutex state_mutex; - init_mutex(&state_mutex, &players, sizeof(Players)); + players.mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!players.mutex) { + furi_message_queue_free(event_queue); + return 255; + } ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, draw_callback, &state_mutex); + view_port_draw_callback_set(view_port, draw_callback, &players); view_port_input_callback_set(view_port, input_callback, event_queue); Gui* gui = furi_record_open(RECORD_GUI); gui_add_view_port(gui, view_port, GuiLayerFullscreen); + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); + if(players.ball_X_direction == 0) + notification_message(notification, &sequence_set_only_red_255); + else + notification_message(notification, &sequence_set_only_blue_255); + FuriTimer* timer = furi_timer_alloc(clock_tick, FuriTimerTypePeriodic, event_queue); furi_timer_start(timer, 1000 / FPS); while(1) { FuriStatus event_status = furi_message_queue_get(event_queue, &event, FuriWaitForever); - Players* playersMutex = (Players*)acquire_mutex_block(&state_mutex); + furi_mutex_acquire(players.mutex, FuriWaitForever); if(event_status == FuriStatusOk) { if(event.type == EventTypeInput) { if(event.input.key == InputKeyBack) { - release_mutex(&state_mutex, playersMutex); + furi_mutex_release(players.mutex); + notification_message(notification, &sequence_set_only_green_255); break; } else if(event.input.key == InputKeyUp) { - if(playersMutex->player1_Y >= 1 + PLAYER1_PAD_SPEED) - playersMutex->player1_Y -= PLAYER1_PAD_SPEED; + if(players.player1_Y >= 1 + PLAYER1_PAD_SPEED) + players.player1_Y -= PLAYER1_PAD_SPEED; else - playersMutex->player1_Y = 1; + players.player1_Y = 1; } else if(event.input.key == InputKeyDown) { - if(playersMutex->player1_Y <= - SCREEN_SIZE_Y - PAD_SIZE_Y - PLAYER1_PAD_SPEED - 1) - playersMutex->player1_Y += PLAYER1_PAD_SPEED; + if(players.player1_Y <= SCREEN_SIZE_Y - PAD_SIZE_Y - PLAYER1_PAD_SPEED - 1) + players.player1_Y += PLAYER1_PAD_SPEED; else - playersMutex->player1_Y = SCREEN_SIZE_Y - PAD_SIZE_Y - 1; + players.player1_Y = SCREEN_SIZE_Y - PAD_SIZE_Y - 1; } } else if(event.type == ClockEventTypeTick) { - if(playersMutex->ball_X + BALL_SIZE / 2 <= SCREEN_SIZE_X * 0.35 && - playersMutex->ball_X_direction == 0) { - if(playersMutex->ball_Y + BALL_SIZE / 2 < - playersMutex->player2_Y + PAD_SIZE_Y / 2) { - if(playersMutex->player2_Y >= 1 + PLAYER2_PAD_SPEED) - playersMutex->player2_Y -= PLAYER2_PAD_SPEED; + if(players.ball_X + BALL_SIZE / 2 <= SCREEN_SIZE_X * 0.35 && + players.ball_X_direction == 0) { + if(players.ball_Y + BALL_SIZE / 2 < players.player2_Y + PAD_SIZE_Y / 2) { + if(players.player2_Y >= 1 + PLAYER2_PAD_SPEED) + players.player2_Y -= PLAYER2_PAD_SPEED; else - playersMutex->player2_Y = 1; - } else if( - playersMutex->ball_Y + BALL_SIZE / 2 > - playersMutex->player2_Y + PAD_SIZE_Y / 2) { - if(playersMutex->player2_Y <= - SCREEN_SIZE_Y - PAD_SIZE_Y - PLAYER2_PAD_SPEED - 1) - playersMutex->player2_Y += PLAYER2_PAD_SPEED; + players.player2_Y = 1; + } else if(players.ball_Y + BALL_SIZE / 2 > players.player2_Y + PAD_SIZE_Y / 2) { + if(players.player2_Y <= SCREEN_SIZE_Y - PAD_SIZE_Y - PLAYER2_PAD_SPEED - 1) + players.player2_Y += PLAYER2_PAD_SPEED; else - playersMutex->player2_Y = SCREEN_SIZE_Y - PAD_SIZE_Y - 1; + players.player2_Y = SCREEN_SIZE_Y - PAD_SIZE_Y - 1; } } uint8_t ball_corner_X[4] = { - playersMutex->ball_X, - playersMutex->ball_X + BALL_SIZE, - playersMutex->ball_X + BALL_SIZE, - playersMutex->ball_X}; + players.ball_X, + players.ball_X + BALL_SIZE, + players.ball_X + BALL_SIZE, + players.ball_X}; uint8_t ball_corner_Y[4] = { - playersMutex->ball_Y, - playersMutex->ball_Y, - playersMutex->ball_Y + BALL_SIZE, - playersMutex->ball_Y + BALL_SIZE}; + players.ball_Y, + players.ball_Y, + players.ball_Y + BALL_SIZE, + players.ball_Y + BALL_SIZE}; bool insidePlayer1 = false, insidePlayer2 = false; for(int i = 0; i < 4; i++) { if(insidePad( ball_corner_X[i], ball_corner_Y[i], - playersMutex->player1_X, - playersMutex->player1_Y) == true) { + players.player1_X, + players.player1_Y) == true) { insidePlayer1 = true; break; } @@ -196,83 +206,88 @@ int32_t flipper_pong_app() { if(insidePad( ball_corner_X[i], ball_corner_Y[i], - playersMutex->player2_X, - playersMutex->player2_Y) == true) { + players.player2_X, + players.player2_Y) == true) { insidePlayer2 = true; break; } } if(insidePlayer1 == true) { - playersMutex->ball_X_direction = 0; - playersMutex->ball_X -= playersMutex->ball_X_speed; - playersMutex->ball_X_speed = changeSpeed(); - playersMutex->ball_Y_speed = changeSpeed(); + players.ball_X_direction = 0; + players.ball_X -= players.ball_X_speed; + players.ball_X_speed = changeSpeed(); + players.ball_Y_speed = changeSpeed(); + notification_message(notification, &sequence_set_only_red_255); } else if(insidePlayer2 == true) { - playersMutex->ball_X_direction = 1; - playersMutex->ball_X += playersMutex->ball_X_speed; - playersMutex->ball_X_speed = changeSpeed(); - playersMutex->ball_Y_speed = changeSpeed(); + players.ball_X_direction = 1; + players.ball_X += players.ball_X_speed; + players.ball_X_speed = changeSpeed(); + players.ball_Y_speed = changeSpeed(); + notification_message(notification, &sequence_set_only_blue_255); } else { - if(playersMutex->ball_X_direction == 1) { - if(playersMutex->ball_X <= - SCREEN_SIZE_X - BALL_SIZE - 1 - playersMutex->ball_X_speed) { - playersMutex->ball_X += playersMutex->ball_X_speed; + if(players.ball_X_direction == 1) { + if(players.ball_X <= + SCREEN_SIZE_X - BALL_SIZE - 1 - players.ball_X_speed) { + players.ball_X += players.ball_X_speed; } else { - playersMutex->ball_X = SCREEN_SIZE_X / 2 - BALL_SIZE / 2; - playersMutex->ball_Y = SCREEN_SIZE_Y / 2 - BALL_SIZE / 2; - playersMutex->ball_X_speed = 1; - playersMutex->ball_Y_speed = 1; - playersMutex->ball_X_direction = 0; - playersMutex->player2_score++; + players.ball_X = SCREEN_SIZE_X / 2 - BALL_SIZE / 2; + players.ball_Y = SCREEN_SIZE_Y / 2 - BALL_SIZE / 2; + players.ball_X_speed = 1; + players.ball_Y_speed = 1; + players.ball_X_direction = 0; + players.player2_score++; + notification_message(notification, &sequence_set_only_red_255); } } else { - if(playersMutex->ball_X >= 1 + playersMutex->ball_X_speed) { - playersMutex->ball_X -= playersMutex->ball_X_speed; + if(players.ball_X >= 1 + players.ball_X_speed) { + players.ball_X -= players.ball_X_speed; } else { - playersMutex->ball_X = SCREEN_SIZE_X / 2 - BALL_SIZE / 2; - playersMutex->ball_Y = SCREEN_SIZE_Y / 2 - BALL_SIZE / 2; - playersMutex->ball_X_speed = 1; - playersMutex->ball_Y_speed = 1; - playersMutex->ball_X_direction = 1; - playersMutex->player1_score++; + players.ball_X = SCREEN_SIZE_X / 2 - BALL_SIZE / 2; + players.ball_Y = SCREEN_SIZE_Y / 2 - BALL_SIZE / 2; + players.ball_X_speed = 1; + players.ball_Y_speed = 1; + players.ball_X_direction = 1; + players.player1_score++; + notification_message(notification, &sequence_set_only_blue_255); } } } - if(playersMutex->ball_Y_direction == 1) { - if(playersMutex->ball_Y <= - SCREEN_SIZE_Y - BALL_SIZE - 1 - playersMutex->ball_Y_speed) { - playersMutex->ball_Y += playersMutex->ball_Y_speed; + if(players.ball_Y_direction == 1) { + if(players.ball_Y <= SCREEN_SIZE_Y - BALL_SIZE - 1 - players.ball_Y_speed) { + players.ball_Y += players.ball_Y_speed; } else { - playersMutex->ball_Y = SCREEN_SIZE_Y - BALL_SIZE - 1; - playersMutex->ball_X_speed = changeSpeed(); - playersMutex->ball_Y_speed = changeSpeed(); - playersMutex->ball_Y_direction = 0; + players.ball_Y = SCREEN_SIZE_Y - BALL_SIZE - 1; + players.ball_X_speed = changeSpeed(); + players.ball_Y_speed = changeSpeed(); + players.ball_Y_direction = 0; } } else { - if(playersMutex->ball_Y >= 1 + playersMutex->ball_Y_speed) { - playersMutex->ball_Y -= playersMutex->ball_Y_speed; + if(players.ball_Y >= 1 + players.ball_Y_speed) { + players.ball_Y -= players.ball_Y_speed; } else { - playersMutex->ball_Y = 1; - playersMutex->ball_X_speed = changeSpeed(); - playersMutex->ball_Y_speed = changeSpeed(); - playersMutex->ball_Y_direction = 1; + players.ball_Y = 1; + players.ball_X_speed = changeSpeed(); + players.ball_Y_speed = changeSpeed(); + players.ball_Y_direction = 1; } } } } - release_mutex(&state_mutex, playersMutex); + furi_mutex_release(players.mutex); view_port_update(view_port); } + notification_message(notification, &sequence_reset_rgb); furi_message_queue_free(event_queue); - delete_mutex(&state_mutex); + furi_mutex_free(players.mutex); gui_remove_view_port(gui, view_port); view_port_free(view_port); furi_timer_free(timer); furi_record_close(RECORD_GUI); + furi_record_close(RECORD_NOTIFICATION); return 0; -} +} \ No newline at end of file diff --git a/applications/plugins/protoview/README.md b/applications/plugins/protoview/README.md index 71083cdef..7523913ca 100644 --- a/applications/plugins/protoview/README.md +++ b/applications/plugins/protoview/README.md @@ -285,3 +285,5 @@ A big thank you to the RTL433 author, [Benjamin Larsson](https://github.com/merb * As a sourve of documentation for protocols. * As an awesome way to visualize and understand protocols, via [these great web tools](https://triq.org/). * To have tons of fun with RTLSDR in general, now and in the past. + +The application icon was designed by Stefano Liuzzo. diff --git a/applications/plugins/protoview/app.c b/applications/plugins/protoview/app.c index 4765da9e3..a4dab9f40 100644 --- a/applications/plugins/protoview/app.c +++ b/applications/plugins/protoview/app.c @@ -3,32 +3,6 @@ #include "app.h" -/* If this define is enabled, ProtoView is going to mess with the - * otherwise opaque SubGhzWorker structure in order to disable - * its filter for samples shorter than a given amount (30us at the - * time I'm writing this comment). - * - * This structure must be taken in sync with the one of the firmware. */ -#define PROTOVIEW_DISABLE_SUBGHZ_FILTER 0 - -#ifdef PROTOVIEW_DISABLE_SUBGHZ_FILTER -struct SubGhzWorker { - FuriThread* thread; - FuriStreamBuffer* stream; - - volatile bool running; - volatile bool overrun; - - LevelDuration filter_level_duration; - bool filter_running; - uint16_t filter_duration; - - SubGhzWorkerOverrunCallback overrun_callback; - SubGhzWorkerPairCallback pair_callback; - void* context; -}; -#endif - RawSamplesBuffer *RawSamples, *DetectedSamples; extern const SubGhzProtocolRegistry protoview_protocol_registry; @@ -42,6 +16,7 @@ extern const SubGhzProtocolRegistry protoview_protocol_registry; * and setting color to black. */ static void render_callback(Canvas* const canvas, void* ctx) { ProtoViewApp* app = ctx; + furi_mutex_acquire(app->view_updating_mutex, FuriWaitForever); /* Clear screen. */ canvas_set_color(canvas, ColorWhite); @@ -74,6 +49,7 @@ static void render_callback(Canvas* const canvas, void* ctx) { /* Draw the alert box if set. */ ui_draw_alert_if_needed(canvas, app); + furi_mutex_release(app->view_updating_mutex); } /* Here all we do is putting the events into the queue that will be handled @@ -91,6 +67,8 @@ static void input_callback(InputEvent* input_event, void* ctx) { * special views ViewGoNext and ViewGoPrev in order to move to * the logical next/prev view. */ static void app_switch_view(ProtoViewApp* app, ProtoViewCurrentView switchto) { + furi_mutex_acquire(app->view_updating_mutex, FuriWaitForever); + /* Switch to the specified view. */ ProtoViewCurrentView old = app->current_view; if(switchto == ViewGoNext) { @@ -106,22 +84,10 @@ static void app_switch_view(ProtoViewApp* app, ProtoViewCurrentView switchto) { } ProtoViewCurrentView new = app->current_view; - /* Set the current subview of the view we just left to zero. This is - * the main subview of the old view. When re re-enter the view we are - * lefting, we want to see the main thing again. */ - app->current_subview[old] = 0; - - /* Reset the view private data each time, before calling the enter/exit - * callbacks that may want to setup some state. */ - memset(app->view_privdata, 0, PROTOVIEW_VIEW_PRIVDATA_LEN); - - /* Call the enter/exit view callbacks if needed. */ + /* Call the exit view callbacks. */ if(old == ViewDirectSampling) view_exit_direct_sampling(app); - if(new == ViewDirectSampling) view_enter_direct_sampling(app); if(old == ViewBuildMessage) view_exit_build_message(app); - if(new == ViewBuildMessage) view_enter_build_message(app); if(old == ViewInfo) view_exit_info(app); - /* The frequency/modulation settings are actually a single view: * as long as the user stays between the two modes of this view we * don't need to call the exit-view callback. */ @@ -129,7 +95,24 @@ static void app_switch_view(ProtoViewApp* app, ProtoViewCurrentView switchto) { (old == ViewModulationSettings && new != ViewFrequencySettings)) view_exit_settings(app); + /* Reset the view private data each time, before calling the enter + * callbacks that may want to setup some state. */ + memset(app->view_privdata, 0, PROTOVIEW_VIEW_PRIVDATA_LEN); + + /* Call the enter view callbacks after all the exit callback + * of the old view was already executed. */ + if(new == ViewDirectSampling) view_enter_direct_sampling(app); + if(new == ViewBuildMessage) view_enter_build_message(app); + + /* Set the current subview of the view we just left to zero. This is + * the main subview of the old view. When we re-enter the view we are + * lefting, we want to see the main thing again. */ + app->current_subview[old] = 0; + + /* If there is an alert on screen, dismiss it: if the user is + * switching view she already read it. */ ui_dismiss_alert(app); + furi_mutex_release(app->view_updating_mutex); } /* Allocate the application state and initialize a number of stuff. @@ -143,7 +126,7 @@ ProtoViewApp* protoview_app_alloc() { //init setting app->setting = subghz_setting_alloc(); - subghz_setting_load(app->setting, EXT_PATH("subghz/assets/setting_user.txt")); + subghz_setting_load(app->setting, EXT_PATH("subghz/assets/setting_user")); // GUI app->gui = furi_record_open(RECORD_GUI); @@ -158,6 +141,7 @@ ProtoViewApp* protoview_app_alloc() { app->show_text_input = false; app->alert_dismiss_time = 0; app->current_view = ViewRawPulses; + app->view_updating_mutex = furi_mutex_alloc(FuriMutexTypeNormal); for(int j = 0; j < ViewLast; j++) app->current_subview[j] = 0; app->direct_sampling_enabled = false; app->view_privdata = malloc(PROTOVIEW_VIEW_PRIVDATA_LEN); @@ -174,29 +158,18 @@ ProtoViewApp* protoview_app_alloc() { // Init Worker & Protocol app->txrx = malloc(sizeof(ProtoViewTxRx)); - /* Setup rx worker and environment. */ + /* Setup rx state. */ app->txrx->freq_mod_changed = false; app->txrx->debug_timer_sampling = false; app->txrx->last_g0_change_time = DWT->CYCCNT; app->txrx->last_g0_value = false; - app->txrx->worker = subghz_worker_alloc(); -#ifdef PROTOVIEW_DISABLE_SUBGHZ_FILTER - app->txrx->worker->filter_running = 0; -#endif - app->txrx->environment = subghz_environment_alloc(); - subghz_environment_set_protocol_registry( - app->txrx->environment, (void*)&protoview_protocol_registry); - app->txrx->receiver = subghz_receiver_alloc_init(app->txrx->environment); - subghz_receiver_set_filter(app->txrx->receiver, SubGhzProtocolFlag_Decodable); - subghz_worker_set_overrun_callback( - app->txrx->worker, (SubGhzWorkerOverrunCallback)subghz_receiver_reset); - subghz_worker_set_pair_callback( - app->txrx->worker, (SubGhzWorkerPairCallback)subghz_receiver_decode); - subghz_worker_set_context(app->txrx->worker, app->txrx->receiver); app->frequency = subghz_setting_get_default_frequency(app->setting); app->modulation = 0; /* Defaults to ProtoViewModulations[0]. */ + // Enable power for External CC1101 if it is connected + furi_hal_subghz_enable_ext_power(); + furi_hal_power_suppress_charge_enter(); app->running = 1; @@ -212,6 +185,9 @@ void protoview_app_free(ProtoViewApp* app) { // Put CC1101 on sleep, this also restores charging. radio_sleep(app); + // Disable power for External CC1101 if it was enabled and module is connected + furi_hal_subghz_disable_ext_power(); + // View related. view_port_enabled_set(app->view_port, false); gui_remove_view_port(app->gui, app->view_port); @@ -219,17 +195,13 @@ void protoview_app_free(ProtoViewApp* app) { furi_record_close(RECORD_GUI); furi_record_close(RECORD_NOTIFICATION); furi_message_queue_free(app->event_queue); + furi_mutex_free(app->view_updating_mutex); app->gui = NULL; // Frequency setting. subghz_setting_free(app->setting); // Worker stuff. - if(!app->txrx->debug_timer_sampling) { - subghz_receiver_free(app->txrx->receiver); - subghz_environment_free(app->txrx->environment); - subghz_worker_free(app->txrx->worker); - } free(app->txrx); // Raw samples buffers. @@ -259,7 +231,7 @@ static void timer_callback(void* ctx) { } if(delta < RawSamples->total / 2) return; app->signal_last_scan_idx = RawSamples->idx; - scan_for_signal(app, RawSamples); + scan_for_signal(app, RawSamples, ProtoViewModulations[app->modulation].duration_filter); } /* This is the navigation callback we use in the view dispatcher used diff --git a/applications/plugins/protoview/app.h b/applications/plugins/protoview/app.h index 85007e345..624c118e2 100644 --- a/applications/plugins/protoview/app.h +++ b/applications/plugins/protoview/app.h @@ -17,9 +17,6 @@ #include #include #include -#include -#include -#include #include #include "raw_samples.h" @@ -28,7 +25,7 @@ #define BITMAP_SEEK_NOT_FOUND UINT32_MAX // Returned by function as sentinel #define PROTOVIEW_VIEW_PRIVDATA_LEN 64 // View specific private data len -#define DEBUG_MSG 1 +#define DEBUG_MSG 0 /* Forward declarations. */ @@ -69,8 +66,11 @@ typedef struct { const char* name; // Name to show to the user. const char* id; // Identifier in the Flipper API/file. FuriHalSubGhzPreset preset; // The preset ID. - uint8_t* custom; // If not null, a set of registers for - // the CC1101, specifying a custom preset. + uint8_t* custom; /* If not null, a set of registers for + the CC1101, specifying a custom preset.*/ + uint32_t duration_filter; /* Ignore pulses and gaps that are less + than the specified microseconds. This + depends on the data rate. */ } ProtoViewModulation; extern ProtoViewModulation ProtoViewModulations[]; /* In app_subghz.c */ @@ -82,9 +82,6 @@ struct ProtoViewTxRx { bool freq_mod_changed; /* The user changed frequency and/or modulation from the interface. There is to restart the radio with the right parameters. */ - SubGhzWorker* worker; /* Our background worker. */ - SubGhzEnvironment* environment; - SubGhzReceiver* receiver; TxRxState txrx_state; /* Receiving, idle or sleeping? */ /* Timer sampling mode state. */ @@ -108,6 +105,10 @@ struct ProtoViewApp { ViewPort* view_port; /* We just use a raw viewport and we render everything into the low level canvas. */ ProtoViewCurrentView current_view; /* Active left-right view ID. */ + FuriMutex* view_updating_mutex; /* The Flipper GUI calls the screen redraw + callback in a different thread. We + use this mutex to protect the redraw + from changes in app->view_privdata. */ int current_subview[ViewLast]; /* Active up-down subview ID. */ FuriMessageQueue* event_queue; /* Keypress events go here. */ @@ -238,7 +239,7 @@ typedef struct ProtoViewDecoder { extern RawSamplesBuffer *RawSamples, *DetectedSamples; -/* app_radio.c */ +/* app_subghz.c */ void radio_begin(ProtoViewApp* app); uint32_t radio_rx(ProtoViewApp* app); void radio_idle(ProtoViewApp* app); @@ -247,11 +248,12 @@ void radio_sleep(ProtoViewApp* app); void raw_sampling_worker_start(ProtoViewApp* app); void raw_sampling_worker_stop(ProtoViewApp* app); void radio_tx_signal(ProtoViewApp* app, FuriHalSubGhzAsyncTxCallback data_feeder, void* ctx); +void protoview_rx_callback(bool level, uint32_t duration, void* context); /* signal.c */ uint32_t duration_delta(uint32_t a, uint32_t b); void reset_current_signal(ProtoViewApp* app); -void scan_for_signal(ProtoViewApp* app, RawSamplesBuffer* source); +void scan_for_signal(ProtoViewApp* app, RawSamplesBuffer* source, uint32_t min_duration); bool bitmap_get(uint8_t* b, uint32_t blen, uint32_t bitpos); void bitmap_set(uint8_t* b, uint32_t blen, uint32_t bitpos, bool val); void bitmap_copy( @@ -271,6 +273,15 @@ uint32_t bitmap_seek_bits( uint32_t startpos, uint32_t maxbits, const char* bits); +bool bitmap_match_bitmap( + uint8_t* b1, + uint32_t b1len, + uint32_t b1off, + uint8_t* b2, + uint32_t b2len, + uint32_t b2off, + uint32_t cmplen); +void bitmap_to_string(char* dst, uint8_t* b, uint32_t blen, uint32_t off, uint32_t len); uint32_t convert_from_line_code( uint8_t* buf, uint64_t buflen, @@ -339,7 +350,7 @@ void fieldset_add_int(ProtoViewFieldSet* fs, const char* name, int64_t val, uint void fieldset_add_uint(ProtoViewFieldSet* fs, const char* name, uint64_t uval, uint8_t bits); void fieldset_add_hex(ProtoViewFieldSet* fs, const char* name, uint64_t uval, uint8_t bits); void fieldset_add_bin(ProtoViewFieldSet* fs, const char* name, uint64_t uval, uint8_t bits); -void fieldset_add_str(ProtoViewFieldSet* fs, const char* name, const char* s); +void fieldset_add_str(ProtoViewFieldSet* fs, const char* name, const char* s, size_t len); void fieldset_add_bytes( ProtoViewFieldSet* fs, const char* name, @@ -359,3 +370,5 @@ void field_set_from_field(ProtoViewField* dst, ProtoViewField* src); /* crc.c */ uint8_t crc8(const uint8_t* data, size_t len, uint8_t init, uint8_t poly); +uint8_t sum_bytes(const uint8_t* data, size_t len, uint8_t init); +uint8_t xor_bytes(const uint8_t* data, size_t len, uint8_t init); diff --git a/applications/plugins/protoview/app_subghz.c b/applications/plugins/protoview/app_subghz.c index 73e0e16ae..69197df5c 100644 --- a/applications/plugins/protoview/app_subghz.c +++ b/applications/plugins/protoview/app_subghz.c @@ -9,31 +9,32 @@ #include #include -void raw_sampling_worker_start(ProtoViewApp* app); -void raw_sampling_worker_stop(ProtoViewApp* app); +void raw_sampling_timer_start(ProtoViewApp* app); +void raw_sampling_timer_stop(ProtoViewApp* app); ProtoViewModulation ProtoViewModulations[] = { - {"OOK 650Khz", "FuriHalSubGhzPresetOok650Async", FuriHalSubGhzPresetOok650Async, NULL}, - {"OOK 270Khz", "FuriHalSubGhzPresetOok270Async", FuriHalSubGhzPresetOok270Async, NULL}, + {"OOK 650Khz", "FuriHalSubGhzPresetOok650Async", FuriHalSubGhzPresetOok650Async, NULL, 30}, + {"OOK 270Khz", "FuriHalSubGhzPresetOok270Async", FuriHalSubGhzPresetOok270Async, NULL, 30}, {"2FSK 2.38Khz", "FuriHalSubGhzPreset2FSKDev238Async", FuriHalSubGhzPreset2FSKDev238Async, - NULL}, + NULL, + 30}, {"2FSK 47.6Khz", "FuriHalSubGhzPreset2FSKDev476Async", FuriHalSubGhzPreset2FSKDev476Async, - NULL}, - {"TPMS 1 (FSK)", NULL, 0, (uint8_t*)protoview_subghz_tpms1_fsk_async_regs}, - {"TPMS 2 (OOK)", NULL, 0, (uint8_t*)protoview_subghz_tpms2_ook_async_regs}, - {"TPMS 3 (FSK)", NULL, 0, (uint8_t*)protoview_subghz_tpms3_fsk_async_regs}, - {"TPMS 4 (FSK)", NULL, 0, (uint8_t*)protoview_subghz_tpms4_fsk_async_regs}, - {NULL, NULL, 0, NULL} /* End of list sentinel. */ + NULL, + 30}, + {"TPMS 1 (FSK)", NULL, 0, (uint8_t*)protoview_subghz_tpms1_fsk_async_regs, 30}, + {"TPMS 2 (OOK)", NULL, 0, (uint8_t*)protoview_subghz_tpms2_ook_async_regs, 30}, + {"TPMS 3 (GFSK)", NULL, 0, (uint8_t*)protoview_subghz_tpms3_gfsk_async_regs, 30}, + {"OOK 40kBaud", NULL, 0, (uint8_t*)protoview_subghz_40k_ook_async_regs, 15}, + {"FSK 40kBaud", NULL, 0, (uint8_t*)protoview_subghz_40k_fsk_async_regs, 15}, + {NULL, NULL, 0, NULL, 0} /* End of list sentinel. */ }; /* Called after the application initialization in order to setup the - * subghz system and put it into idle state. If the user wants to start - * receiving we will call radio_rx() to start a receiving worker and - * associated thread. */ + * subghz system and put it into idle state. */ void radio_begin(ProtoViewApp* app) { furi_assert(app); furi_hal_subghz_reset(); @@ -51,13 +52,23 @@ void radio_begin(ProtoViewApp* app) { } else { furi_hal_subghz_load_custom_preset(ProtoViewModulations[app->modulation].custom); } - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(furi_hal_subghz.cc1101_g0_pin, GpioModeInput, GpioPullNo, GpioSpeedLow); app->txrx->txrx_state = TxRxStateIDLE; } /* ================================= Reception ============================== */ -/* Setup subghz to start receiving using a background worker. */ +/* We avoid the subghz provided abstractions and put the data in our + * simple abstraction: the RawSamples circular buffer. */ +void protoview_rx_callback(bool level, uint32_t duration, void* context) { + UNUSED(context); + /* Add data to the circular buffer. */ + raw_samples_add(RawSamples, level, duration); + // FURI_LOG_E(TAG, "FEED: %d %d", (int)level, (int)duration); + return; +} + +/* Setup the CC1101 to start receiving using a background worker. */ uint32_t radio_rx(ProtoViewApp* app) { furi_assert(app); if(!furi_hal_subghz_is_frequency_valid(app->frequency)) { @@ -69,12 +80,11 @@ uint32_t radio_rx(ProtoViewApp* app) { furi_hal_subghz_idle(); /* Put it into idle state in case it is sleeping. */ uint32_t value = furi_hal_subghz_set_frequency_and_path(app->frequency); FURI_LOG_E(TAG, "Switched to frequency: %lu", value); - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(furi_hal_subghz.cc1101_g0_pin, GpioModeInput, GpioPullNo, GpioSpeedLow); furi_hal_subghz_flush_rx(); furi_hal_subghz_rx(); if(!app->txrx->debug_timer_sampling) { - furi_hal_subghz_start_async_rx(subghz_worker_rx_callback, app->txrx->worker); - subghz_worker_start(app->txrx->worker); + furi_hal_subghz_start_async_rx(protoview_rx_callback, NULL); } else { raw_sampling_worker_start(app); } @@ -82,16 +92,13 @@ uint32_t radio_rx(ProtoViewApp* app) { return value; } -/* Stop subghz worker (if active), put radio on idle state. */ +/* Stop receiving (if active) and put the radio on idle state. */ void radio_rx_end(ProtoViewApp* app) { furi_assert(app); if(app->txrx->txrx_state == TxRxStateRx) { if(!app->txrx->debug_timer_sampling) { - if(subghz_worker_is_running(app->txrx->worker)) { - subghz_worker_stop(app->txrx->worker); - furi_hal_subghz_stop_async_rx(); - } + furi_hal_subghz_stop_async_rx(); } else { raw_sampling_worker_stop(app); } @@ -104,8 +111,8 @@ void radio_rx_end(ProtoViewApp* app) { void radio_sleep(ProtoViewApp* app) { furi_assert(app); if(app->txrx->txrx_state == TxRxStateRx) { - /* We can't go from having an active RX worker to sleeping. - * Stop the RX subsystems first. */ + /* Stop the asynchronous receiving system before putting the + * chip into sleep. */ radio_rx_end(app); } furi_hal_subghz_sleep(); @@ -127,8 +134,9 @@ void radio_tx_signal(ProtoViewApp* app, FuriHalSubGhzAsyncTxCallback data_feeder furi_hal_subghz_idle(); uint32_t value = furi_hal_subghz_set_frequency_and_path(app->frequency); FURI_LOG_E(TAG, "Switched to frequency: %lu", value); - furi_hal_gpio_write(&gpio_cc1101_g0, false); - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_write(furi_hal_subghz.cc1101_g0_pin, false); + furi_hal_gpio_init( + furi_hal_subghz.cc1101_g0_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); furi_hal_subghz_start_async_tx(data_feeder, ctx); while(!furi_hal_subghz_is_async_tx_complete()) furi_delay_ms(10); @@ -149,7 +157,7 @@ void radio_tx_signal(ProtoViewApp* app, FuriHalSubGhzAsyncTxCallback data_feeder void protoview_timer_isr(void* ctx) { ProtoViewApp* app = ctx; - bool level = furi_hal_gpio_read(&gpio_cc1101_g0); + bool level = furi_hal_gpio_read(furi_hal_subghz.cc1101_g0_pin); if(app->txrx->last_g0_value != level) { uint32_t now = DWT->CYCCNT; uint32_t dur = now - app->txrx->last_g0_change_time; diff --git a/applications/plugins/protoview/appicon.png b/applications/plugins/protoview/appicon.png index 7ce5c4eff..fa7122515 100644 Binary files a/applications/plugins/protoview/appicon.png and b/applications/plugins/protoview/appicon.png differ diff --git a/applications/plugins/protoview/application.fam b/applications/plugins/protoview/application.fam index d3614524c..22ad2d628 100644 --- a/applications/plugins/protoview/application.fam +++ b/applications/plugins/protoview/application.fam @@ -8,5 +8,5 @@ App( stack_size=8 * 1024, order=50, fap_icon="appicon.png", - fap_category="Tools", + fap_category="Sub-GHz", ) diff --git a/applications/plugins/protoview/crc.c b/applications/plugins/protoview/crc.c index 94d482972..e99786952 100644 --- a/applications/plugins/protoview/crc.c +++ b/applications/plugins/protoview/crc.c @@ -1,3 +1,6 @@ +/* Copyright (C) 2022-2023 Salvatore Sanfilippo -- All Rights Reserved + * See the LICENSE file for information about the license. */ + #include #include @@ -17,3 +20,17 @@ uint8_t crc8(const uint8_t* data, size_t len, uint8_t init, uint8_t poly) { } return crc; } + +/* Sum all the specified bytes modulo 256. + * Initialize the sum with 'init' (usually 0). */ +uint8_t sum_bytes(const uint8_t* data, size_t len, uint8_t init) { + for(size_t i = 0; i < len; i++) init += data[i]; + return init; +} + +/* Perform the bitwise xor of all the specified bytes. + * Initialize xor value with 'init' (usually 0). */ +uint8_t xor_bytes(const uint8_t* data, size_t len, uint8_t init) { + for(size_t i = 0; i < len; i++) init ^= data[i]; + return init; +} diff --git a/applications/plugins/protoview/custom_presets.h b/applications/plugins/protoview/custom_presets.h index 00aa49945..ef1c56a94 100644 --- a/applications/plugins/protoview/custom_presets.h +++ b/applications/plugins/protoview/custom_presets.h @@ -1,5 +1,4 @@ #include - /* ========================== DATA RATE SETTINGS =============================== * * This is how to configure registers MDMCFG3 and MDMCFG4. @@ -131,8 +130,8 @@ static const uint8_t protoview_subghz_tpms2_ook_async_regs[][2] = { {CC1101_MDMCFG0, 0x00}, // Channel spacing is 25kHz {CC1101_MDMCFG1, 0x00}, // Channel spacing is 25kHz {CC1101_MDMCFG2, 0x30}, // Format ASK/OOK, No preamble/sync - {CC1101_MDMCFG3, /*0x93*/ 0x32}, // Data rate is 10kBaud - {CC1101_MDMCFG4, /*0x18*/ 0x17}, // Rx BW filter is 650.000kHz + {CC1101_MDMCFG3, 0x93}, // Data rate is 10kBaud + {CC1101_MDMCFG4, 0x18}, // Rx BW filter is 650.000kHz /* Main Radio Control State Machine */ {CC1101_MCSM0, 0x18}, // Autocalibrate on idle-to-rx/tx, PO_TIMEOUT is 64 cycles(149-155us) @@ -164,8 +163,57 @@ static const uint8_t protoview_subghz_tpms2_ook_async_regs[][2] = { {0, 0}, {0, 0}}; +/* GFSK 19k dev, 325 Khz filter, 20kBaud. Different AGI settings. + * Works well with Toyota. */ +static uint8_t protoview_subghz_tpms3_gfsk_async_regs[][2] = { + /* GPIO GD0 */ + {CC1101_IOCFG0, 0x0D}, // GD0 as async serial data output/input + + /* Frequency Synthesizer Control */ + {CC1101_FSCTRL1, 0x06}, // IF = (26*10^6) / (2^10) * 0x06 = 152343.75Hz + + /* Packet engine */ + {CC1101_PKTCTRL0, 0x32}, // Async, continious, no whitening + {CC1101_PKTCTRL1, 0x04}, + + // // Modem Configuration + {CC1101_MDMCFG0, 0x00}, + {CC1101_MDMCFG1, 0x02}, // 2 is the channel spacing exponet: not used + {CC1101_MDMCFG2, 0x10}, // GFSK without any other check + {CC1101_MDMCFG3, 0x93}, // Data rate is 20kBaud + {CC1101_MDMCFG4, 0x59}, // Rx bandwidth filter is 325 kHz + {CC1101_DEVIATN, 0x34}, // Deviation 19.04 Khz. + + /* Main Radio Control State Machine */ + {CC1101_MCSM0, 0x18}, // Autocalibrate on idle-to-rx/tx, PO_TIMEOUT is 64 cycles(149-155us) + + /* Frequency Offset Compensation Configuration */ + {CC1101_FOCCFG, + 0x16}, // no frequency offset compensation, POST_K same as PRE_K, PRE_K is 4K, GATE is off + + /* Automatic Gain Control */ + {CC1101_AGCCTRL0, 0x80}, + {CC1101_AGCCTRL1, 0x58}, + {CC1101_AGCCTRL2, 0x87}, + + /* Wake on radio and timeouts control */ + {CC1101_WORCTRL, 0xFB}, // WOR_RES is 2^15 periods (0.91 - 0.94 s) 16.5 - 17.2 hours + + /* Frontend configuration */ + {CC1101_FREND0, 0x10}, // Adjusts current TX LO buffer + {CC1101_FREND1, 0x56}, + + /* End */ + {0, 0}, + + /* CC1101 2FSK PATABLE. */ + {0xC0, 0}, + {0, 0}, + {0, 0}, + {0, 0}}; + /* 40 KBaud, 2FSK, 28 kHz deviation, 270 Khz bandwidth filter. */ -static uint8_t protoview_subghz_tpms3_fsk_async_regs[][2] = { +static uint8_t protoview_subghz_40k_fsk_async_regs[][2] = { /* GPIO GD0 */ {CC1101_IOCFG0, 0x0D}, // GD0 as async serial data output/input @@ -215,50 +263,55 @@ static uint8_t protoview_subghz_tpms3_fsk_async_regs[][2] = { {0, 0}, {0, 0}}; -/* FSK 19k dev, 325 Khz filter, 20kBaud. Works well with Toyota. */ -static uint8_t protoview_subghz_tpms4_fsk_async_regs[][2] = { +/* This is like the default Flipper OOK 640Khz bandwidth preset, but + * the bandwidth is changed to 40kBaud, in order to receive signals + * with a pulse width ~25us/30us. */ +static const uint8_t protoview_subghz_40k_ook_async_regs[][2] = { /* GPIO GD0 */ {CC1101_IOCFG0, 0x0D}, // GD0 as async serial data output/input + /* FIFO and internals */ + {CC1101_FIFOTHR, 0x07}, // The only important bit is ADC_RETENTION + + /* Packet engine */ + {CC1101_PKTCTRL0, 0x32}, // Async, continious, no whitening + /* Frequency Synthesizer Control */ {CC1101_FSCTRL1, 0x06}, // IF = (26*10^6) / (2^10) * 0x06 = 152343.75Hz - /* Packet engine */ - {CC1101_PKTCTRL0, 0x32}, // Async, continious, no whitening - {CC1101_PKTCTRL1, 0x04}, - - // // Modem Configuration - {CC1101_MDMCFG0, 0x00}, - {CC1101_MDMCFG1, 0x02}, // 2 is the channel spacing exponet: not used - {CC1101_MDMCFG2, 0x10}, // GFSK without any other check - {CC1101_MDMCFG3, 0x93}, // Data rate is 20kBaud - {CC1101_MDMCFG4, 0x59}, // Rx bandwidth filter is 325 kHz - {CC1101_DEVIATN, 0x34}, // Deviation 19.04 Khz. + // Modem Configuration + {CC1101_MDMCFG0, 0x00}, // Channel spacing is 25kHz + {CC1101_MDMCFG1, 0x00}, // Channel spacing is 25kHz + {CC1101_MDMCFG2, 0x30}, // Format ASK/OOK, No preamble/sync + {CC1101_MDMCFG3, 0x93}, // Data rate is 40kBaud + {CC1101_MDMCFG4, 0x1A}, // Rx BW filter is 650.000kHz /* Main Radio Control State Machine */ {CC1101_MCSM0, 0x18}, // Autocalibrate on idle-to-rx/tx, PO_TIMEOUT is 64 cycles(149-155us) /* Frequency Offset Compensation Configuration */ {CC1101_FOCCFG, - 0x16}, // no frequency offset compensation, POST_K same as PRE_K, PRE_K is 4K, GATE is off + 0x18}, // no frequency offset compensation, POST_K same as PRE_K, PRE_K is 4K, GATE is off /* Automatic Gain Control */ - {CC1101_AGCCTRL0, 0x80}, - {CC1101_AGCCTRL1, 0x58}, - {CC1101_AGCCTRL2, 0x87}, + {CC1101_AGCCTRL0, + 0x91}, // 10 - Medium hysteresis, medium asymmetric dead zone, medium gain ; 01 - 16 samples agc; 00 - Normal AGC, 01 - 8dB boundary + {CC1101_AGCCTRL1, + 0x0}, // 0; 0 - LNA 2 gain is decreased to minimum before decreasing LNA gain; 00 - Relative carrier sense threshold disabled; 0000 - RSSI to MAIN_TARGET + {CC1101_AGCCTRL2, 0x07}, // 00 - DVGA all; 000 - MAX LNA+LNA2; 111 - MAIN_TARGET 42 dB /* Wake on radio and timeouts control */ {CC1101_WORCTRL, 0xFB}, // WOR_RES is 2^15 periods (0.91 - 0.94 s) 16.5 - 17.2 hours /* Frontend configuration */ - {CC1101_FREND0, 0x10}, // Adjusts current TX LO buffer - {CC1101_FREND1, 0x56}, + {CC1101_FREND0, 0x11}, // Adjusts current TX LO buffer + high is PATABLE[1] + {CC1101_FREND1, 0xB6}, // /* End */ {0, 0}, - /* CC1101 2FSK PATABLE. */ - {0xC0, 0}, + /* CC1101 OOK PATABLE. */ + {0, 0xC0}, {0, 0}, {0, 0}, {0, 0}}; diff --git a/applications/plugins/protoview/data_feed.c b/applications/plugins/protoview/data_feed.c deleted file mode 100644 index 81d1a8020..000000000 --- a/applications/plugins/protoview/data_feed.c +++ /dev/null @@ -1,115 +0,0 @@ -/* Copyright (C) 2022-2023 Salvatore Sanfilippo -- All Rights Reserved - * See the LICENSE file for information about the license. */ - -#include -#include -#include -#include -#include -#include "raw_samples.h" - -#define TAG "PROTOVIEW-protocol" - -const SubGhzProtocol subghz_protocol_protoview; - -/* The feed() method puts data in the RawSamples global (protected by - * a mutex). */ -extern RawSamplesBuffer* RawSamples; - -/* This is totally dummy: we just define the decoder base for the async - * system to work but we don't really use it if not to collect raw - * data via the feed() method. */ -typedef struct SubGhzProtocolDecoderprotoview { - SubGhzProtocolDecoderBase base; -} SubGhzProtocolDecoderprotoview; - -void* subghz_protocol_decoder_protoview_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - - SubGhzProtocolDecoderprotoview* instance = malloc(sizeof(SubGhzProtocolDecoderprotoview)); - instance->base.protocol = &subghz_protocol_protoview; - return instance; -} - -void subghz_protocol_decoder_protoview_free(void* context) { - furi_assert(context); - SubGhzProtocolDecoderprotoview* instance = context; - free(instance); -} - -void subghz_protocol_decoder_protoview_reset(void* context) { - furi_assert(context); -} - -/* That's the only thig we really use of the protocol decoder - * implementation. We avoid the subghz provided abstractions and put - * the data in our simple abstraction: the RawSamples circular buffer. */ -void subghz_protocol_decoder_protoview_feed(void* context, bool level, uint32_t duration) { - furi_assert(context); - UNUSED(context); - - /* Add data to the circular buffer. */ - raw_samples_add(RawSamples, level, duration); - // FURI_LOG_E(TAG, "FEED: %d %d", (int)level, (int)duration); - return; -} - -/* The only scope of this method is to avoid duplicated messages in the - * Subghz history, which we don't use. */ -uint8_t subghz_protocol_decoder_protoview_get_hash_data(void* context) { - furi_assert(context); - return 123; -} - -/* Not used. */ -bool subghz_protocol_decoder_protoview_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) { - UNUSED(context); - UNUSED(flipper_format); - UNUSED(preset); - return false; -} - -/* Not used. */ -bool subghz_protocol_decoder_protoview_deserialize(void* context, FlipperFormat* flipper_format) { - UNUSED(context); - UNUSED(flipper_format); - return false; -} - -void subhz_protocol_decoder_protoview_get_string(void* context, FuriString* output) { - furi_assert(context); - furi_string_cat_printf(output, "Protoview"); -} - -const SubGhzProtocolDecoder subghz_protocol_protoview_decoder = { - .alloc = subghz_protocol_decoder_protoview_alloc, - .free = subghz_protocol_decoder_protoview_free, - .reset = subghz_protocol_decoder_protoview_reset, - .feed = subghz_protocol_decoder_protoview_feed, - .get_hash_data = subghz_protocol_decoder_protoview_get_hash_data, - .serialize = subghz_protocol_decoder_protoview_serialize, - .deserialize = subghz_protocol_decoder_protoview_deserialize, - .get_string = subhz_protocol_decoder_protoview_get_string, -}; - -/* Well, we don't really target a specific protocol. So let's put flags - * that make sense. */ -const SubGhzProtocol subghz_protocol_protoview = { - .name = "Protoview", - .type = SubGhzProtocolTypeStatic, - .flag = SubGhzProtocolFlag_AM | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable, - .decoder = &subghz_protocol_protoview_decoder, -}; - -/* Our table has just the single dummy protocol we defined for the - * sake of data collection. */ -const SubGhzProtocol* protoview_protocol_registry_items[] = { - &subghz_protocol_protoview, -}; - -const SubGhzProtocolRegistry protoview_protocol_registry = { - .items = protoview_protocol_registry_items, - .size = COUNT_OF(protoview_protocol_registry_items)}; diff --git a/applications/plugins/protoview/fields.c b/applications/plugins/protoview/fields.c index 47d573f4f..18dee6502 100644 --- a/applications/plugins/protoview/fields.c +++ b/applications/plugins/protoview/fields.c @@ -314,11 +314,16 @@ void fieldset_add_bin(ProtoViewFieldSet* fs, const char* name, uint64_t uval, ui fieldset_add_field(fs, f); } -/* Allocate and append a string field. */ -void fieldset_add_str(ProtoViewFieldSet* fs, const char* name, const char* s) { +/* Allocate and append a string field. The string 's' does not need to point + * to a null terminated string, but must have at least 'len' valid bytes + * starting from the pointer. The field object will be correctly null + * terminated. */ +void fieldset_add_str(ProtoViewFieldSet* fs, const char* name, const char* s, size_t len) { ProtoViewField* f = field_new(FieldTypeStr, name); - f->str = strdup(s); - f->len = strlen(s); + f->len = len; + f->str = malloc(len + 1); + memcpy(f->str, s, len); + f->str[len] = 0; fieldset_add_field(fs, f); } diff --git a/applications/plugins/protoview/protocols/b4b1.c b/applications/plugins/protoview/protocols/b4b1.c index 52c59d24b..6d0b8237a 100644 --- a/applications/plugins/protoview/protocols/b4b1.c +++ b/applications/plugins/protoview/protocols/b4b1.c @@ -1,4 +1,7 @@ -/* PT/SC remotes. Usually 443.92 Mhz OOK. +/* Copyright (C) 2022-2023 Salvatore Sanfilippo -- All Rights Reserved + * See the LICENSE file for information about the license. + * + * PT/SC remotes. Usually 443.92 Mhz OOK. * * This line code is used in many remotes such as Princeton chips * named PT2262, Silian Microelectronics SC5262 and others. @@ -11,10 +14,15 @@ static bool decode(uint8_t* bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo* info) { if(numbits < 30) return false; - const char* sync_patterns[3] = { - "10000000000000000000000000000001", /* 30 zero bits. */ - "100000000000000000000000000000001", /* 31 zero bits. */ - "1000000000000000000000000000000001", /* 32 zero bits. */ + + /* Test different pulse + gap + first byte possibilities. */ + const char* sync_patterns[6] = { + "100000000000000000000000000000011101", /* 30 times gap + one. */ + "100000000000000000000000000000010001", /* 30 times gap + zero. */ + "1000000000000000000000000000000011101", /* 31 times gap + one. */ + "1000000000000000000000000000000010001", /* 31 times gap + zero. */ + "10000000000000000000000000000000011101", /* 32 times gap + one. */ + "10000000000000000000000000000000010001", /* 32 times gap + zero. */ }; uint32_t off; @@ -24,11 +32,11 @@ static bool decode(uint8_t* bits, uint32_t numbytes, uint32_t numbits, ProtoView if(off != BITMAP_SEEK_NOT_FOUND) break; } if(off == BITMAP_SEEK_NOT_FOUND) return false; - if(DEBUG_MSG) FURI_LOG_E(TAG, "B4B1 preamble at: %lu", off); + if(DEBUG_MSG) FURI_LOG_E(TAG, "B4B1 preamble id:%d at: %lu", j, off); info->start_off = off; - // Seek data setction. Why -1? Last bit is data. - off += strlen(sync_patterns[j]) - 1; + // Seek data setction. Why -5? Last 5 half-bit-times are data. + off += strlen(sync_patterns[j]) - 5; uint8_t d[3]; /* 24 bits of data. */ uint32_t decoded = convert_from_line_code(d, sizeof(d), bits, numbytes, off, "1000", "1110"); diff --git a/applications/plugins/protoview/protocols/keeloq.c b/applications/plugins/protoview/protocols/keeloq.c index 298c690d4..c09cc35fc 100644 --- a/applications/plugins/protoview/protocols/keeloq.c +++ b/applications/plugins/protoview/protocols/keeloq.c @@ -1,4 +1,7 @@ -/* Microchip HCS200/HCS300/HSC301 KeeLoq, rolling code remotes. +/* Copyright (C) 2022-2023 Salvatore Sanfilippo -- All Rights Reserved + * See the LICENSE file for information about the license. + * + * Microchip HCS200/HCS300/HSC301 KeeLoq, rolling code remotes. * * Usually 443.92 Mhz OOK, ~200us or ~400us pulse len, depending * on the configuration. diff --git a/applications/plugins/protoview/protocols/oregon2.c b/applications/plugins/protoview/protocols/oregon2.c index f67e85a2d..f7b123deb 100644 --- a/applications/plugins/protoview/protocols/oregon2.c +++ b/applications/plugins/protoview/protocols/oregon2.c @@ -1,4 +1,7 @@ -/* Oregon remote termometers. Usually 443.92 Mhz OOK. +/* Copyright (C) 2022-2023 Salvatore Sanfilippo -- All Rights Reserved + * See the LICENSE file for information about the license. + * + * Oregon remote termometers. Usually 443.92 Mhz OOK. * * The protocol is described here: * https://wmrx00.sourceforge.net/Arduino/OregonScientific-RF-Protocols.pdf diff --git a/applications/plugins/protoview/protocols/pvchat.c b/applications/plugins/protoview/protocols/pvchat.c new file mode 100644 index 000000000..779248c5b --- /dev/null +++ b/applications/plugins/protoview/protocols/pvchat.c @@ -0,0 +1,205 @@ +#include "../app.h" + +/* Copyright (C) 2022-2023 Salvatore Sanfilippo -- All Rights Reserved + * See the LICENSE file for information about the license. + * + * ---------------------------------------------------------- + * ProtoView chat protocol. This is just a fun test protocol + * that can be used between two Flippers in order to send + * and receive text messages. + * ---------------------------------------------------------- + * + * Protocol description + * ==================== + * + * The protocol works with different data rates. However here is defined + * to use a short pulse/gap duration of 300us and a long pulse/gap + * duration of 600us. Even with the Flipper hardware, the protocol works + * with 100/200us, but becomes less reliable and standard presets can't + * be used because of the higher data rate. + * + * In the following description we have that: + * + * "1" represents a pulse of one-third bit time (300us) + * "0" represents a gap of one-third bit time (300us) + * + * The message starts with a preamble + a sync pattern: + * + * preamble = 1010101010101010 x 3 + * sync = 1100110011001010 + * + * The a variable amount of bytes follow, where each bit + * is encoded in the following way: + * + * zero 100 (300 us pulse, 600 us gap) + * one 110 (600 us pulse, 300 us gap) + * + * Bytes are sent MSB first, so receiving, in sequence, bits + * 11100001, means byte E1. + * + * This is the data format: + * + * +--+------+-------+--+--+--+ + * |SL|Sender|Message|FF|AA|CS| + * +--+------+-------+--+--+--+ + * | | | + * | | \_ N bytes of message terminated by FF AA + 1 byte of checksum + * | | + * | \_ SL bytes of sender name + * \ + * \_ 1 byte of sender len, 8 bit unsigned integer. + * + * + * Checksum = sum of bytes modulo 256, with checksum set + * to 0 for the computation. + * + * Design notes + * ============ + * + * The protocol is designed in order to have certain properties: + * + * 1. Pulses and gaps can only be 100 or 200 microseconds, so the + * message can be described, encoded and decoded with only two + * fixed durations. + * + * 2. The preamble + sync is designed to have a well recognizable + * pattern that can't be reproduced just for accident inside + * the encoded pattern. There is no combinatio of encoded bits + * leading to the preamble+sync. Also the sync pattern final + * part can't be mistaken for actual bits of data, since it + * contains alternating short pulses/gaps at 100us. + * + * 3. Data encoding wastes some bandwidth in order to be more + * robust. Even so, with a 300us clock period, a single bit + * bit takes 900us, reaching a data transfer of 138 characters per + * second. More than enough for the simple chat we have here. + */ + +static bool decode(uint8_t* bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo* info) { + const char* sync_pattern = "1010101010101010" // Preamble + "1100110011001010"; // Sync + uint8_t sync_len = 32; + + /* This is a variable length message, however the minimum length + * requires a sender len byte (of value zero) and the terminator + * FF 00 plus checksum: a total of 4 bytes. */ + if(numbits - sync_len < 8 * 4) return false; + + uint64_t off = bitmap_seek_bits(bits, numbytes, 0, numbits, sync_pattern); + if(off == BITMAP_SEEK_NOT_FOUND) return false; + FURI_LOG_E(TAG, "Chat preamble+sync found"); + + /* If there is room on the left, let's mark the start of the message + * a bit before: we don't try to detect all the preamble, but only + * the first part, however it is likely present. */ + if(off >= 16) { + off -= 16; + sync_len += 16; + } + + info->start_off = off; + off += sync_len; /* Skip preamble and sync. */ + + uint8_t raw[64] = {(uint8_t)'.'}; + uint32_t decoded = + convert_from_line_code(raw, sizeof(raw), bits, numbytes, off, "100", "110"); /* PWM */ + FURI_LOG_E(TAG, "Chat decoded bits: %lu", decoded); + + if(decoded < 8 * 4) return false; /* Min message len. */ + + // The message needs to have a two bytes terminator before + // the checksum. + uint32_t j; + for(j = 0; j < sizeof(raw) - 1; j++) + if(raw[j] == 0xff && raw[j + 1] == 0xaa) break; + + if(j == sizeof(raw) - 1) { + FURI_LOG_E(TAG, "Chat: terminator not found"); + return false; // No terminator found. + } + + uint32_t datalen = j + 3; // If the terminator was found at j, then + // we need to sum three more bytes to have + // the len: FF itself, AA, checksum. + info->pulses_count = sync_len + 8 * 3 * datalen; + + // Check if the control sum matches. + if(sum_bytes(raw, datalen - 1, 0) != raw[datalen - 1]) { + FURI_LOG_E(TAG, "Chat: checksum mismatch"); + return false; + } + + // Check if the length of the sender looks sane + uint8_t senderlen = raw[0]; + if(senderlen >= sizeof(raw)) { + FURI_LOG_E(TAG, "Chat: invalid sender length"); + return false; // Overflow + } + + fieldset_add_str(info->fieldset, "sender", (char*)raw + 1, senderlen); + fieldset_add_str( + info->fieldset, "message", (char*)raw + 1 + senderlen, datalen - senderlen - 4); + return true; +} + +/* Give fields and defaults for the signal creator. */ +static void get_fields(ProtoViewFieldSet* fieldset) { + fieldset_add_str(fieldset, "sender", "Carol", 5); + fieldset_add_str(fieldset, "message", "Anyone hearing?", 15); +} + +/* Create a signal. */ +static void build_message(RawSamplesBuffer* samples, ProtoViewFieldSet* fs) { + uint32_t te = 300; /* Short pulse duration in microseconds. + Our protocol needs three symbol times to send + a bit, so 300 us per bit = 3.33 kBaud. */ + + // Preamble: 24 alternating 300us pulse/gap pairs. + for(int j = 0; j < 24; j++) { + raw_samples_add(samples, true, te); + raw_samples_add(samples, false, te); + } + + // Sync: 3 alternating 600 us pulse/gap pairs. + for(int j = 0; j < 3; j++) { + raw_samples_add(samples, true, te * 2); + raw_samples_add(samples, false, te * 2); + } + + // Sync: plus 2 alternating 300 us pluse/gap pairs. + for(int j = 0; j < 2; j++) { + raw_samples_add(samples, true, te); + raw_samples_add(samples, false, te); + } + + // Data: build the array. + uint32_t datalen = 1 + fs->fields[0]->len + // Userlen + Username + fs->fields[1]->len + 3; // Message + FF + 00 + CRC + uint8_t *data = malloc(datalen), *p = data; + *p++ = fs->fields[0]->len; + memcpy(p, fs->fields[0]->str, fs->fields[0]->len); + p += fs->fields[0]->len; + memcpy(p, fs->fields[1]->str, fs->fields[1]->len); + p += fs->fields[1]->len; + *p++ = 0xff; + *p++ = 0xaa; + *p = sum_bytes(data, datalen - 1, 0); + + // Emit bits + for(uint32_t j = 0; j < datalen * 8; j++) { + if(bitmap_get(data, datalen, j)) { + raw_samples_add(samples, true, te * 2); + raw_samples_add(samples, false, te); + } else { + raw_samples_add(samples, true, te); + raw_samples_add(samples, false, te * 2); + } + } + free(data); +} + +ProtoViewDecoder ProtoViewChatDecoder = { + .name = "ProtoView chat", + .decode = decode, + .get_fields = get_fields, + .build_message = build_message}; diff --git a/applications/plugins/protoview/protocols/tpms/citroen.c b/applications/plugins/protoview/protocols/tpms/citroen.c index ecd8fb983..a86e5f7d6 100644 --- a/applications/plugins/protoview/protocols/tpms/citroen.c +++ b/applications/plugins/protoview/protocols/tpms/citroen.c @@ -1,4 +1,7 @@ -/* Citroen TPMS. Usually 443.92 Mhz FSK. +/* Copyright (C) 2022-2023 Salvatore Sanfilippo -- All Rights Reserved + * See the LICENSE file for information about the license. + * + * Citroen TPMS. Usually 443.92 Mhz FSK. * * Preamble of ~14 high/low 52 us pulses * Sync of high 100us pulse then 50us low diff --git a/applications/plugins/protoview/protocols/tpms/ford.c b/applications/plugins/protoview/protocols/tpms/ford.c index 3816e72f9..066332590 100644 --- a/applications/plugins/protoview/protocols/tpms/ford.c +++ b/applications/plugins/protoview/protocols/tpms/ford.c @@ -1,4 +1,7 @@ -/* Ford tires TPMS. Usually 443.92 Mhz FSK (in Europe). +/* Copyright (C) 2022-2023 Salvatore Sanfilippo -- All Rights Reserved + * See the LICENSE file for information about the license. + * + * Ford tires TPMS. Usually 443.92 Mhz FSK (in Europe). * * 52 us short pules * Preamble: 0101010101010101010101010101 diff --git a/applications/plugins/protoview/protocols/tpms/renault.c b/applications/plugins/protoview/protocols/tpms/renault.c index 3d8fc43d5..0755a515f 100644 --- a/applications/plugins/protoview/protocols/tpms/renault.c +++ b/applications/plugins/protoview/protocols/tpms/renault.c @@ -1,4 +1,7 @@ -/* Renault tires TPMS. Usually 443.92 Mhz FSK. +/* Copyright (C) 2022-2023 Salvatore Sanfilippo -- All Rights Reserved + * See the LICENSE file for information about the license. + * + * Renault tires TPMS. Usually 443.92 Mhz FSK. * * Preamble + sync + Manchester bits. ~48us short pulse. * 9 Bytes in total not counting the preamble. */ diff --git a/applications/plugins/protoview/protocols/tpms/schrader.c b/applications/plugins/protoview/protocols/tpms/schrader.c index 7dc85a2cb..2b53d313c 100644 --- a/applications/plugins/protoview/protocols/tpms/schrader.c +++ b/applications/plugins/protoview/protocols/tpms/schrader.c @@ -1,4 +1,7 @@ -/* Schrader TPMS. Usually 443.92 Mhz OOK, 120us pulse len. +/* Copyright (C) 2022-2023 Salvatore Sanfilippo -- All Rights Reserved + * See the LICENSE file for information about the license. + * + * Schrader TPMS. Usually 443.92 Mhz OOK, 120us pulse len. * * 500us high pulse + Preamble + Manchester coded bits where: * 1 = 10 diff --git a/applications/plugins/protoview/protocols/tpms/schrader_eg53ma4.c b/applications/plugins/protoview/protocols/tpms/schrader_eg53ma4.c index 45accf1a1..b186f57b1 100644 --- a/applications/plugins/protoview/protocols/tpms/schrader_eg53ma4.c +++ b/applications/plugins/protoview/protocols/tpms/schrader_eg53ma4.c @@ -1,4 +1,7 @@ -/* Schrader variant EG53MA4 TPMS. +/* Copyright (C) 2022-2023 Salvatore Sanfilippo -- All Rights Reserved + * See the LICENSE file for information about the license. + * + * Schrader variant EG53MA4 TPMS. * Usually 443.92 Mhz OOK, 100us pulse len. * * Preamble: alternating pulse/gap, 100us. diff --git a/applications/plugins/protoview/protocols/tpms/toyota.c b/applications/plugins/protoview/protocols/tpms/toyota.c index b80af7647..3a02447b7 100644 --- a/applications/plugins/protoview/protocols/tpms/toyota.c +++ b/applications/plugins/protoview/protocols/tpms/toyota.c @@ -1,4 +1,7 @@ -/* Toyota tires TPMS. Usually 443.92 Mhz FSK (In Europe). +/* Copyright (C) 2022-2023 Salvatore Sanfilippo -- All Rights Reserved + * See the LICENSE file for information about the license. + * + * Toyota tires TPMS. Usually 443.92 Mhz FSK (In Europe). * * Preamble + sync + 64 bits of data. ~48us short pulse length. * diff --git a/applications/plugins/protoview/protocols/unknown.c b/applications/plugins/protoview/protocols/unknown.c new file mode 100644 index 000000000..9e2f4a893 --- /dev/null +++ b/applications/plugins/protoview/protocols/unknown.c @@ -0,0 +1,326 @@ +#include "../app.h" + +/* Copyright (C) 2023 Salvatore Sanfilippo -- All Rights Reserved + * See the LICENSE file for information about the license. + * + * ---------------------------------------------------------------------------- + * The "unknown" decoder fires as the last one, once we are sure no other + * decoder was able to identify the signal. The goal is to detect the + * preamble and line code used in the received signal, then turn the + * decoded bits into bytes. + * + * The techniques used for the detection are described in the comments + * below. + * ---------------------------------------------------------------------------- + */ + +/* Scan the signal bitmap looking for a PWM modulation. In this case + * for PWM we are referring to two exact patterns of high and low + * signal (each bit in the bitmap is worth the smallest gap/pulse duration + * we detected) that repeat each other in a given segment of the message. + * + * This modulation is quite common, for instance sometimes zero and + * one are rappresented by a 700us pulse followed by 350 gap, + * and 350us pulse followed by a 700us gap. So the signal bitmap received + * by the decoder would contain 110 and 100 symbols. + * + * The way this function work is commented inline. + * + * The function returns the number of consecutive symbols found, having + * a symbol length of 'symlen' (3 in the above example), and stores + * in *s1i the offset of the first symbol found, and in *s2i the offset + * of the second symbol. The function can't tell which is one and which + * zero. */ +static uint32_t find_pwm( + uint8_t* bits, + uint32_t numbytes, + uint32_t numbits, + uint32_t symlen, + uint32_t* s1i, + uint32_t* s2i) { + uint32_t best_count = 0; /* Max number of symbols found in this try. */ + uint32_t best_idx1 = 0; /* First symbol offset of longest sequence found. + * This is also the start sequence offset. */ + uint32_t best_idx2 = 0; /* Second symbol offset. */ + + /* Try all the possible symbol offsets that are less of our + * symbol len. This is likely not really useful but we take + * a conservative approach. Because if have have, for instance, + * repeating symbols "100" and "110", they will form a sequence + * that is choerent at different offsets, but out-of-sync. + * + * Anyway at the end of the function we try to fix the sync. */ + for(uint32_t off = 0; off < symlen; off++) { + uint32_t c = 0; // Number of contiguous symbols found. + uint32_t c1 = 0, c2 = 0; // Occurrences of first/second symbol. + *s1i = off; // Assume we start at one symbol boundaty. + *s2i = UINT32_MAX; // Second symbol first index still unknown. + uint32_t next = off; + + /* We scan the whole bitmap in one pass, resetting the state + * each time we find a pattern that is not one of the two + * symbols we found so far. */ + while(next < numbits - symlen) { + bool match1 = bitmap_match_bitmap(bits, numbytes, next, bits, numbytes, *s1i, symlen); + if(!match1 && *s2i == UINT32_MAX) { + /* It's not the first sybol. We don't know how the + * second look like. Assume we found an occurrence of + * the second symbol. */ + *s2i = next; + } + + bool match2 = bitmap_match_bitmap(bits, numbytes, next, bits, numbytes, *s2i, symlen); + + /* One or the other should match. */ + if(match1 || match2) { + c++; + if(match1) c1++; + if(match2) c2++; + if(c > best_count && c1 >= best_count / 5 && // Require enough presence of both + c2 >= best_count / 5) // zero and one. + { + best_count = c; + best_idx1 = *s1i; + best_idx2 = *s2i; + } + next += symlen; + } else { + /* No match. Continue resetting the signal info. */ + c = 0; // Start again to count contiguous symbols. + c1 = 0; + c2 = 0; + *s1i = next; // First symbol always at start. + *s2i = UINT32_MAX; // Second symbol unknown. + } + } + } + + /* We don't know if we are really synchronized with the bits at this point. + * For example if zero bit is 100 and one bit is 110 in a specific + * line code, our detector could randomly believe it's 001 and 101. + * However PWD line codes normally start with a pulse in both symbols. + * If that is the case, let's align. */ + uint32_t shift; + for(shift = 0; shift < symlen; shift++) { + if(bitmap_get(bits, numbytes, best_idx1 + shift) && + bitmap_get(bits, numbytes, best_idx2 + shift)) + break; + } + if(shift != symlen) { + best_idx1 += shift; + best_idx2 += shift; + } + + *s1i = best_idx1; + *s2i = best_idx2; + return best_count; +} + +/* Find the longest sequence that looks like Manchester coding. + * + * Manchester coding requires each pairs of bits to be either + * 01 or 10. We'll have to try odd and even offsets to be + * sure to find it. + * + * Note that this will also detect differential Manchester, but + * will report it as Manchester. I can't think of any way to + * distinguish between the two line codes, because shifting them + * one symbol will make one to look like the other. + * + * Only option could be to decode the message with both line + * codes and use statistical properties (common byte values) + * to determine what's more likely, but this looks very fragile. + * + * Fortunately differential Manchester is more rarely used, + * so we can assume Manchester most of the times. Yet we are left + * with the indetermination about zero being pulse-gap or gap-pulse + * or the other way around. + * + * If the 'only_raising' parameter is true, the function detects + * only sequences going from gap to pulse: this is useful in order + * to locate preambles of alternating gaps and pulses. */ +static uint32_t find_alternating_bits( + uint8_t* bits, + uint32_t numbytes, + uint32_t numbits, + uint32_t* start, + bool only_raising) { + uint32_t best_count = 0; // Max number of symbols found + uint32_t best_off = 0; // Max symbols start offset. + for(int odd = 0; odd < 2; odd++) { + uint32_t count = 0; // Symbols found so far + uint32_t start_off = odd; + uint32_t j = odd; + while(j < numbits - 1) { + bool bit1 = bitmap_get(bits, numbytes, j); + bool bit2 = bitmap_get(bits, numbytes, j + 1); + if((!only_raising && bit1 != bit2) || (only_raising && !bit1 && bit2)) { + count++; + if(count > best_count) { + best_count = count; + best_off = start_off; + } + } else { + /* End of sequence. Continue with the next + * part of the signal. */ + count = 0; + start_off = j + 2; + } + j += 2; + } + } + *start = best_off; + return best_count; +} + +/* Wrapper to find Manchester code. */ +static uint32_t + find_manchester(uint8_t* bits, uint32_t numbytes, uint32_t numbits, uint32_t* start) { + return find_alternating_bits(bits, numbytes, numbits, start, false); +} + +/* Wrapper to find preamble sections. */ +static uint32_t + find_preamble(uint8_t* bits, uint32_t numbytes, uint32_t numbits, uint32_t* start) { + return find_alternating_bits(bits, numbytes, numbits, start, true); +} + +typedef enum { + LineCodeNone, + LineCodeManchester, + LineCodePWM3, + LineCodePWM4, +} LineCodeGuess; + +static char* get_linecode_name(LineCodeGuess lc) { + switch(lc) { + case LineCodeNone: + return "none"; + case LineCodeManchester: + return "Manchester"; + case LineCodePWM3: + return "PWM3"; + case LineCodePWM4: + return "PWM4"; + } + return "unknown"; +} + +static bool decode(uint8_t* bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo* info) { + /* No decoder was able to detect this message. Let's try if we can + * find some structure. To start, we'll see if it looks like is + * manchester coded, or PWM with symbol len of 3 or 4. */ + + /* For PWM, start1 and start2 are the offsets at which the two + * sequences composing the message appear the first time. + * So start1 is also the message start offset. Start2 is not used + * for Manchester, that does not have two separated symbols like + * PWM. */ + uint32_t start1 = 0, start2 = 0; + uint32_t msgbits; // Number of message bits in the bitmap, so + // this will be the number of symbols, not actual + // bits after the message is decoded. + uint32_t tmp1, tmp2; // Temp vars to store the start. + uint32_t minbits = 16; // Less than that gets undetected. + uint32_t pwm_len; // Bits per symbol, in the case of PWM. + LineCodeGuess linecode = LineCodeNone; + + // Try PWM3 + uint32_t pwm3_bits = find_pwm(bits, numbytes, numbits, 3, &tmp1, &tmp2); + if(pwm3_bits >= minbits) { + linecode = LineCodePWM3; + start1 = tmp1; + start2 = tmp2; + pwm_len = 3; + msgbits = pwm3_bits * pwm_len; + } + + // Try PWM4 + uint32_t pwm4_bits = find_pwm(bits, numbytes, numbits, 4, &tmp1, &tmp2); + if(pwm4_bits >= minbits && pwm4_bits > pwm3_bits) { + linecode = LineCodePWM4; + start1 = tmp1; + start2 = tmp2; + pwm_len = 4; + msgbits = pwm3_bits * pwm_len; + } + + // Try Manchester + uint32_t manchester_bits = find_manchester(bits, numbytes, numbits, &tmp1); + if(manchester_bits > minbits && manchester_bits > pwm3_bits && manchester_bits > pwm4_bits) { + linecode = LineCodeManchester; + start1 = tmp1; + msgbits = manchester_bits * 2; + FURI_LOG_E(TAG, "MANCHESTER START: %lu", tmp1); + } + + if(linecode == LineCodeNone) return false; + + /* Often there is a preamble before the signal. We'll try to find + * it, and if it is not too far away from our signal, we'll claim + * our signal starts at the preamble. */ + uint32_t preamble_len = find_preamble(bits, numbytes, numbits, &tmp1); + uint32_t min_preamble_len = 10; + uint32_t max_preamble_distance = 32; + uint32_t preamble_start = 0; + bool preamble_found = false; + + /* Note that because of the following checks, if the Manchester detector + * detected the preamble bits as data, we are ok with that, since it + * means that the synchronization is not designed to "break" the bits + * flow. In this case we ignore the preamble and return all as data. */ + if(preamble_len >= min_preamble_len && // Not too short. + tmp1 < start1 && // Should be before the data. + start1 - tmp1 <= max_preamble_distance) // Not too far. + { + preamble_start = tmp1; + preamble_found = true; + } + + info->start_off = preamble_found ? preamble_start : start1; + info->pulses_count = (start1 + msgbits) - info->start_off; + info->pulses_count += 20; /* Add a few more, so that if the user resends + * the message, it is more likely we will + * transfer all that is needed, like a message + * terminator (that we don't detect). */ + + if(preamble_found) FURI_LOG_E(TAG, "PREAMBLE AT: %lu", preamble_start); + FURI_LOG_E(TAG, "START: %lu", info->start_off); + FURI_LOG_E(TAG, "MSGBITS: %lu", msgbits); + FURI_LOG_E(TAG, "DATASTART: %lu", start1); + FURI_LOG_E(TAG, "PULSES: %lu", info->pulses_count); + + /* We think there is a message and we know where it starts and the + * line code used. We can turn it into bits and bytes. */ + uint32_t decoded; + uint8_t data[32]; + uint32_t datalen; + + char symbol1[5], symbol2[5]; + if(linecode == LineCodePWM3 || linecode == LineCodePWM4) { + bitmap_to_string(symbol1, bits, numbytes, start1, pwm_len); + bitmap_to_string(symbol2, bits, numbytes, start2, pwm_len); + } else if(linecode == LineCodeManchester) { + memcpy(symbol1, "01", 3); + memcpy(symbol2, "10", 3); + } + + decoded = convert_from_line_code(data, sizeof(data), bits, numbytes, start1, symbol1, symbol2); + datalen = (decoded + 7) / 8; + + char* linecode_name = get_linecode_name(linecode); + fieldset_add_str(info->fieldset, "line code", linecode_name, strlen(linecode_name)); + fieldset_add_uint(info->fieldset, "data bits", decoded, 8); + if(preamble_found) fieldset_add_uint(info->fieldset, "preamble len", preamble_len, 8); + fieldset_add_str(info->fieldset, "first symbol", symbol1, strlen(symbol1)); + fieldset_add_str(info->fieldset, "second symbol", symbol2, strlen(symbol2)); + for(uint32_t j = 0; j < datalen; j++) { + char label[16]; + snprintf(label, sizeof(label), "data[%lu]", j); + fieldset_add_bytes(info->fieldset, label, data + j, 2); + } + return true; +} + +ProtoViewDecoder UnknownDecoder = + {.name = "Unknown", .decode = decode, .get_fields = NULL, .build_message = NULL}; diff --git a/applications/plugins/protoview/signal.c b/applications/plugins/protoview/signal.c index a1c4b2b8f..71fbe823f 100644 --- a/applications/plugins/protoview/signal.c +++ b/applications/plugins/protoview/signal.c @@ -5,6 +5,43 @@ bool decode_signal(RawSamplesBuffer* s, uint64_t len, ProtoViewMsgInfo* info); +/* ============================================================================= + * Protocols table. + * + * Supported protocols go here, with the relevant implementation inside + * protocols/.c + * ===========================================================================*/ + +extern ProtoViewDecoder Oregon2Decoder; +extern ProtoViewDecoder B4B1Decoder; +extern ProtoViewDecoder RenaultTPMSDecoder; +extern ProtoViewDecoder ToyotaTPMSDecoder; +extern ProtoViewDecoder SchraderTPMSDecoder; +extern ProtoViewDecoder SchraderEG53MA4TPMSDecoder; +extern ProtoViewDecoder CitroenTPMSDecoder; +extern ProtoViewDecoder FordTPMSDecoder; +extern ProtoViewDecoder KeeloqDecoder; +extern ProtoViewDecoder ProtoViewChatDecoder; +extern ProtoViewDecoder UnknownDecoder; + +ProtoViewDecoder* Decoders[] = { + &Oregon2Decoder, /* Oregon sensors v2.1 protocol. */ + &B4B1Decoder, /* PT, SC, ... 24 bits remotes. */ + &RenaultTPMSDecoder, /* Renault TPMS. */ + &ToyotaTPMSDecoder, /* Toyota TPMS. */ + &SchraderTPMSDecoder, /* Schrader TPMS. */ + &SchraderEG53MA4TPMSDecoder, /* Schrader EG53MA4 TPMS. */ + &CitroenTPMSDecoder, /* Citroen TPMS. */ + &FordTPMSDecoder, /* Ford TPMS. */ + &KeeloqDecoder, /* Keeloq remote. */ + &ProtoViewChatDecoder, /* Protoview simple text messages. */ + + /* Warning: the following decoder must stay at the end of the + * list. Otherwise would detect most signals and prevent the actaul + * decoders from handling them. */ + &UnknownDecoder, /* General protocol detector. */ + NULL}; + /* ============================================================================= * Raw signal detection * ===========================================================================*/ @@ -39,23 +76,28 @@ void reset_current_signal(ProtoViewApp* app) { * For instance Oregon2 sensors, in the case of protocol 2.1 will send * pulses of ~400us (RF on) VS ~580us (RF off). */ #define SEARCH_CLASSES 3 -uint32_t search_coherent_signal(RawSamplesBuffer* s, uint32_t idx) { +uint32_t search_coherent_signal(RawSamplesBuffer* s, uint32_t idx, uint32_t min_duration) { struct { uint32_t dur[2]; /* dur[0] = low, dur[1] = high */ uint32_t count[2]; /* Associated observed frequency. */ } classes[SEARCH_CLASSES]; memset(classes, 0, sizeof(classes)); - uint32_t minlen = 30, maxlen = 4000; /* Depends on data rate, here we - allow for high and low. */ + + // Set a min/max duration limit for samples to be considered part of a + // coherent signal. The maximum length is fixed while the minimum + // is passed as argument, as depends on the data rate and in general + // on the signal to analyze. + uint32_t max_duration = 4000; + uint32_t len = 0; /* Observed len of coherent samples. */ s->short_pulse_dur = 0; - for(uint32_t j = idx; j < idx + 500; j++) { + for(uint32_t j = idx; j < idx + s->total; j++) { bool level; uint32_t dur; raw_samples_get(s, j, &level, &dur); - if(dur < minlen || dur > maxlen) break; /* return. */ + if(dur < min_duration || dur > max_duration) break; /* return. */ /* Let's see if it matches a class we already have or if we * can populate a new (yet empty) class. */ @@ -142,7 +184,7 @@ void notify_signal_detected(ProtoViewApp* app, bool decoded) { * in order to find a coherent signal. If a signal that does not appear to * be just noise is found, it is set in DetectedSamples global signal * buffer, that is what is rendered on the screen. */ -void scan_for_signal(ProtoViewApp* app, RawSamplesBuffer* source) { +void scan_for_signal(ProtoViewApp* app, RawSamplesBuffer* source, uint32_t min_duration) { /* We need to work on a copy: the source buffer may be populated * by the background thread receiving data. */ RawSamplesBuffer* copy = raw_samples_alloc(); @@ -157,7 +199,7 @@ void scan_for_signal(ProtoViewApp* app, RawSamplesBuffer* source) { uint32_t i = 0; while(i < copy->total - 1) { - uint32_t thislen = search_coherent_signal(copy, i); + uint32_t thislen = search_coherent_signal(copy, i, min_duration); /* For messages that are long enough, attempt decoding. */ if(thislen > minlen) { @@ -179,8 +221,11 @@ void scan_for_signal(ProtoViewApp* app, RawSamplesBuffer* source) { /* Accept this signal as the new signal if either it's longer * than the previous undecoded one, or the previous one was * unknown and this is decoded. */ - if((thislen > app->signal_bestlen && app->signal_decoded == false) || - (app->signal_decoded == false && decoded)) { + bool oldsignal_not_decoded = app->signal_decoded == false || + app->msg_info->decoder == &UnknownDecoder; + + if(oldsignal_not_decoded && + (thislen > app->signal_bestlen || (decoded && info->decoder != &UnknownDecoder))) { free_msg_info(app->msg_info); app->msg_info = info; app->signal_bestlen = thislen; @@ -194,7 +239,7 @@ void scan_for_signal(ProtoViewApp* app, RawSamplesBuffer* source) { DetectedSamples->short_pulse_dur); adjust_raw_view_scale(app, DetectedSamples->short_pulse_dur); - notify_signal_detected(app, decoded); + if(app->msg_info->decoder != &UnknownDecoder) notify_signal_detected(app, decoded); } else { /* If the structure was not filled, discard it. Otherwise * now the owner is app->msg_info. */ @@ -387,6 +432,33 @@ uint32_t bitmap_seek_bits( return BITMAP_SEEK_NOT_FOUND; } +/* Compare bitmaps b1 and b2 (possibly overlapping or the same bitmap), + * at the specified offsets, for cmplen bits. Returns true if the + * exact same bits are found, otherwise false. */ +bool bitmap_match_bitmap( + uint8_t* b1, + uint32_t b1len, + uint32_t b1off, + uint8_t* b2, + uint32_t b2len, + uint32_t b2off, + uint32_t cmplen) { + for(uint32_t j = 0; j < cmplen; j++) { + bool bit1 = bitmap_get(b1, b1len, b1off + j); + bool bit2 = bitmap_get(b2, b2len, b2off + j); + if(bit1 != bit2) return false; + } + return true; +} + +/* Convert 'len' bitmap bits of the bitmap 'bitmap' into a null terminated + * string, stored at 'dst', that must have space at least for len+1 bytes. + * The bits are extracted from the specified offset. */ +void bitmap_to_string(char* dst, uint8_t* b, uint32_t blen, uint32_t off, uint32_t len) { + for(uint32_t j = 0; j < len; j++) dst[j] = bitmap_get(b, blen, off + j) ? '1' : '0'; + dst[len] = 0; +} + /* Set the pattern 'pat' into the bitmap 'b' of max length 'blen' bytes, * starting from the specified offset. * @@ -503,7 +575,7 @@ uint32_t convert_from_line_code( } /* Convert the differential Manchester code to bits. This is similar to - * convert_from_line_code() but specific for Manchester. The user must + * convert_from_line_code() but specific for diff-Manchester. The user must * supply the value of the previous symbol before this stream, since * in differential codings the next bits depend on the previous one. * @@ -528,31 +600,6 @@ uint32_t convert_from_diff_manchester( return decoded; } -/* Supported protocols go here, with the relevant implementation inside - * protocols/.c */ - -extern ProtoViewDecoder Oregon2Decoder; -extern ProtoViewDecoder B4B1Decoder; -extern ProtoViewDecoder RenaultTPMSDecoder; -extern ProtoViewDecoder ToyotaTPMSDecoder; -extern ProtoViewDecoder SchraderTPMSDecoder; -extern ProtoViewDecoder SchraderEG53MA4TPMSDecoder; -extern ProtoViewDecoder CitroenTPMSDecoder; -extern ProtoViewDecoder FordTPMSDecoder; -extern ProtoViewDecoder KeeloqDecoder; - -ProtoViewDecoder* Decoders[] = { - &Oregon2Decoder, /* Oregon sensors v2.1 protocol. */ - &B4B1Decoder, /* PT, SC, ... 24 bits remotes. */ - &RenaultTPMSDecoder, /* Renault TPMS. */ - &ToyotaTPMSDecoder, /* Toyota TPMS. */ - &SchraderTPMSDecoder, /* Schrader TPMS. */ - &SchraderEG53MA4TPMSDecoder, /* Schrader EG53MA4 TPMS. */ - &CitroenTPMSDecoder, /* Citroen TPMS. */ - &FordTPMSDecoder, /* Ford TPMS. */ - &KeeloqDecoder, /* Keeloq remote. */ - NULL}; - /* Free the message info and allocated data. */ void free_msg_info(ProtoViewMsgInfo* i) { if(i == NULL) return; diff --git a/applications/plugins/protoview/view_build.c b/applications/plugins/protoview/view_build.c index 955855902..57e6e4fbc 100644 --- a/applications/plugins/protoview/view_build.c +++ b/applications/plugins/protoview/view_build.c @@ -205,7 +205,7 @@ static void process_input_set_fields(ProtoViewApp* app, InputEvent input) { privdata->decoder->build_message(rs, privdata->fieldset); app->signal_decoded = false; // So that the new signal will be // accepted as the current signal. - scan_for_signal(app, rs); + scan_for_signal(app, rs, 5); raw_samples_free(rs); ui_show_alert(app, "Done: press back key", 3000); } diff --git a/applications/plugins/protoview/view_direct_sampling.c b/applications/plugins/protoview/view_direct_sampling.c index 1ab90f096..0268e5297 100644 --- a/applications/plugins/protoview/view_direct_sampling.c +++ b/applications/plugins/protoview/view_direct_sampling.c @@ -2,63 +2,161 @@ * See the LICENSE file for information about the license. */ #include "app.h" - #include +static void direct_sampling_timer_start(ProtoViewApp* app); +static void direct_sampling_timer_stop(ProtoViewApp* app); + +#define CAPTURED_BITMAP_BITS (128 * 64) +#define CAPTURED_BITMAP_BYTES (CAPTURED_BITMAP_BITS / 8) +#define DEFAULT_USEC_PER_PIXEL 50 +#define USEC_PER_PIXEL_SMALL_CHANGE 5 +#define USEC_PER_PIXEL_LARGE_CHANGE 25 +#define USEC_PER_PIXEL_MIN 5 +#define USEC_PER_PIXEL_MAX 300 +typedef struct { + uint8_t* captured; // Bitmap with the last captured screen. + uint32_t captured_idx; // Current index to write into the bitmap + uint32_t usec_per_pixel; // Number of useconds a pixel should represent + bool show_usage_info; +} DirectSamplingViewPrivData; + /* Read directly from the G0 CC1101 pin, and draw a black or white * dot depending on the level. */ void render_view_direct_sampling(Canvas* const canvas, ProtoViewApp* app) { - if(!app->direct_sampling_enabled) { + DirectSamplingViewPrivData* privdata = app->view_privdata; + + if(!app->direct_sampling_enabled && privdata->show_usage_info) { canvas_set_font(canvas, FontSecondary); - canvas_draw_str(canvas, 2, 9, "Direct sampling is a special"); - canvas_draw_str(canvas, 2, 18, "mode that displays the signal"); - canvas_draw_str(canvas, 2, 27, "captured in real time. Like in"); - canvas_draw_str(canvas, 2, 36, "a old CRT TV. It's very slow."); - canvas_draw_str(canvas, 2, 45, "Can crash your Flipper."); + canvas_draw_str(canvas, 2, 9, "Direct sampling displays the"); + canvas_draw_str(canvas, 2, 18, "the captured signal in real"); + canvas_draw_str(canvas, 2, 27, "time, like in a CRT TV set."); + canvas_draw_str(canvas, 2, 36, "Use UP/DOWN to change the"); + canvas_draw_str(canvas, 2, 45, "resolution (usec/pixel)."); canvas_set_font(canvas, FontPrimary); - canvas_draw_str(canvas, 14, 60, "To enable press OK"); + canvas_draw_str(canvas, 5, 60, "To start/stop, press OK"); return; } + privdata->show_usage_info = false; + /* Draw on screen. */ + int idx = 0; for(int y = 0; y < 64; y++) { for(int x = 0; x < 128; x++) { - bool level = furi_hal_gpio_read(&gpio_cc1101_g0); + bool level = bitmap_get(privdata->captured, CAPTURED_BITMAP_BYTES, idx++); if(level) canvas_draw_dot(canvas, x, y); - /* Busy loop: this is a terrible approach as it blocks - * everything else, but for now it's the best we can do - * to obtain direct data with some spacing. */ - uint32_t x = 250; - while(x--) - ; } } + + char buf[32]; + snprintf(buf, sizeof(buf), "%lu usec/px", privdata->usec_per_pixel); canvas_set_font(canvas, FontSecondary); - canvas_draw_str_with_border(canvas, 36, 60, "Direct sampling", ColorWhite, ColorBlack); + canvas_draw_str_with_border(canvas, 1, 60, buf, ColorWhite, ColorBlack); } /* Handle input */ void process_input_direct_sampling(ProtoViewApp* app, InputEvent input) { + DirectSamplingViewPrivData* privdata = app->view_privdata; + if(input.type == InputTypePress && input.key == InputKeyOk) { app->direct_sampling_enabled = !app->direct_sampling_enabled; } + + if((input.key == InputKeyUp || input.key == InputKeyDown) && + (input.type == InputTypePress || input.type == InputTypeRepeat)) { + uint32_t change = input.type == InputTypePress ? USEC_PER_PIXEL_SMALL_CHANGE : + USEC_PER_PIXEL_LARGE_CHANGE; + if(input.key == InputKeyUp) change = -change; + privdata->usec_per_pixel += change; + if(privdata->usec_per_pixel < USEC_PER_PIXEL_MIN) + privdata->usec_per_pixel = USEC_PER_PIXEL_MIN; + else if(privdata->usec_per_pixel > USEC_PER_PIXEL_MAX) + privdata->usec_per_pixel = USEC_PER_PIXEL_MAX; + /* Update the timer frequency. */ + direct_sampling_timer_stop(app); + direct_sampling_timer_start(app); + } } /* Enter view. Stop the subghz thread to prevent access as we read * the CC1101 data directly. */ void view_enter_direct_sampling(ProtoViewApp* app) { + /* Set view defaults. */ + DirectSamplingViewPrivData* privdata = app->view_privdata; + privdata->usec_per_pixel = DEFAULT_USEC_PER_PIXEL; + privdata->captured = malloc(CAPTURED_BITMAP_BYTES); + privdata->show_usage_info = true; + if(app->txrx->txrx_state == TxRxStateRx && !app->txrx->debug_timer_sampling) { - subghz_worker_stop(app->txrx->worker); + furi_hal_subghz_stop_async_rx(); + + /* To read data asynchronously directly from the view, we need + * to put the CC1101 back into reception mode (the previous call + * to stop the async RX will put it into idle) and configure the + * G0 pin for reading. */ + furi_hal_subghz_rx(); + furi_hal_gpio_init(furi_hal_subghz.cc1101_g0_pin, GpioModeInput, GpioPullNo, GpioSpeedLow); } else { raw_sampling_worker_stop(app); } + + // Start the timer to capture raw data + direct_sampling_timer_start(app); } /* Exit view. Restore the subghz thread. */ void view_exit_direct_sampling(ProtoViewApp* app) { + DirectSamplingViewPrivData* privdata = app->view_privdata; + if(privdata->captured) free(privdata->captured); + app->direct_sampling_enabled = false; + + direct_sampling_timer_stop(app); + + /* Restart normal data feeding. */ if(app->txrx->txrx_state == TxRxStateRx && !app->txrx->debug_timer_sampling) { - subghz_worker_start(app->txrx->worker); + furi_hal_subghz_start_async_rx(protoview_rx_callback, NULL); } else { raw_sampling_worker_start(app); } - app->direct_sampling_enabled = false; +} + +/* =========================== Timer implementation ========================= */ + +static void ds_timer_isr(void* ctx) { + ProtoViewApp* app = ctx; + DirectSamplingViewPrivData* privdata = app->view_privdata; + + if(app->direct_sampling_enabled) { + bool level = furi_hal_gpio_read(furi_hal_subghz.cc1101_g0_pin); + bitmap_set(privdata->captured, CAPTURED_BITMAP_BYTES, privdata->captured_idx, level); + privdata->captured_idx = (privdata->captured_idx + 1) % CAPTURED_BITMAP_BITS; + } + LL_TIM_ClearFlag_UPDATE(TIM2); +} + +static void direct_sampling_timer_start(ProtoViewApp* app) { + DirectSamplingViewPrivData* privdata = app->view_privdata; + + LL_TIM_InitTypeDef tim_init = { + .Prescaler = 63, /* CPU frequency is ~64Mhz. */ + .CounterMode = LL_TIM_COUNTERMODE_UP, + .Autoreload = privdata->usec_per_pixel}; + + LL_TIM_Init(TIM2, &tim_init); + LL_TIM_SetClockSource(TIM2, LL_TIM_CLOCKSOURCE_INTERNAL); + LL_TIM_DisableCounter(TIM2); + LL_TIM_SetCounter(TIM2, 0); + furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, ds_timer_isr, app); + LL_TIM_EnableIT_UPDATE(TIM2); + LL_TIM_EnableCounter(TIM2); +} + +static void direct_sampling_timer_stop(ProtoViewApp* app) { + UNUSED(app); + FURI_CRITICAL_ENTER(); + LL_TIM_DisableCounter(TIM2); + LL_TIM_DisableIT_UPDATE(TIM2); + furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, NULL, NULL); + LL_TIM_DeInit(TIM2); + FURI_CRITICAL_EXIT(); } diff --git a/applications/plugins/protoview/view_settings.c b/applications/plugins/protoview/view_settings.c index 94d80cfb5..09abf5a2a 100644 --- a/applications/plugins/protoview/view_settings.c +++ b/applications/plugins/protoview/view_settings.c @@ -88,7 +88,7 @@ void process_input_settings(ProtoViewApp* app, InputEvent input) { if(input.key == InputKeyUp) { modid = modid == 0 ? count - 1 : modid - 1; } else if(input.key == InputKeyDown) { - modid = (modid + 1) % (count ? count : 1); + modid = (modid + 1) % count; } else { return; } diff --git a/applications/plugins/qrcode/application.fam b/applications/plugins/qrcode/application.fam index 68a378fa4..3aac3f18d 100644 --- a/applications/plugins/qrcode/application.fam +++ b/applications/plugins/qrcode/application.fam @@ -1,7 +1,7 @@ App( - appid="QRCode", + appid="qrcode", name="QR Code", - fap_version=(1, 0), + fap_version=(1, 1), fap_description="Display qrcodes", fap_author="Bob Matcuk", fap_weburl="https://github.com/bmatcuk/flipperzero-qrcode", @@ -16,5 +16,4 @@ App( fap_category="Tools", fap_icon="icons/qrcode_10px.png", fap_icon_assets="icons", - fap_icon_assets_symbol="qrcode", ) diff --git a/applications/plugins/rc2014_coleco/application.fam b/applications/plugins/rc2014_coleco/application.fam index 6856b716b..fe3ee028b 100644 --- a/applications/plugins/rc2014_coleco/application.fam +++ b/applications/plugins/rc2014_coleco/application.fam @@ -1,5 +1,5 @@ App( - appid="RC2014_Coleco", + appid="coleco", name="[RC2014] ColecoVision", apptype=FlipperAppType.EXTERNAL, entry_point="coleco_app", diff --git a/applications/plugins/rc2014_coleco/coleco.c b/applications/plugins/rc2014_coleco/coleco.c index 6ae050633..d5b1b0d46 100644 --- a/applications/plugins/rc2014_coleco/coleco.c +++ b/applications/plugins/rc2014_coleco/coleco.c @@ -2,7 +2,7 @@ #include #include #include -#include "RC2014_Coleco_icons.h" +#include "coleco_icons.h" #define CODE_0 0x0A #define CODE_1 0x0D @@ -40,16 +40,16 @@ typedef struct { } PluginEvent; typedef struct { + FuriMutex* mutex; bool dpad; int row; int column; } Coleco; static void render_callback(Canvas* const canvas, void* context) { - Coleco* coleco = acquire_mutex((ValueMutex*)context, 25); - if(coleco == NULL) { - return; - } + furi_assert(context); + Coleco* coleco = context; + furi_mutex_acquire(coleco->mutex, FuriWaitForever); if(coleco->dpad) { canvas_draw_icon(canvas, 4, 16, &I_ColecoJoystick_sel_33x33); @@ -128,7 +128,7 @@ static void render_callback(Canvas* const canvas, void* context) { (coleco->row == 4 && coleco->column == 2) ? &I_ColecoPound_hvr_17x17 : &I_ColecoPound_17x17); - release_mutex((ValueMutex*)context, coleco); + furi_mutex_release(coleco->mutex); } static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { @@ -191,8 +191,8 @@ int32_t coleco_app(void* p) { Coleco* coleco = coleco_alloc(); - ValueMutex coleco_mutex; - if(!init_mutex(&coleco_mutex, coleco, sizeof(Coleco))) { + coleco->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!coleco->mutex) { FURI_LOG_E("Coleco", "cannot create mutex\r\n"); coleco_free(coleco); return 255; @@ -200,7 +200,7 @@ int32_t coleco_app(void* p) { // set system callbacks ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, render_callback, &coleco_mutex); + view_port_draw_callback_set(view_port, render_callback, coleco); view_port_input_callback_set(view_port, input_callback, event_queue); // open GUI and register view_port @@ -214,7 +214,7 @@ int32_t coleco_app(void* p) { for(bool processing = true; processing;) { FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); - Coleco* coleco = (Coleco*)acquire_mutex_block(&coleco_mutex); + furi_mutex_acquire(coleco->mutex, FuriWaitForever); if(event_status == FuriStatusOk) { // press events @@ -346,11 +346,9 @@ int32_t coleco_app(void* p) { view_port_update(view_port); } - } else { - FURI_LOG_D("Coleco", "FuriMessageQueue: event timeout"); } - release_mutex(&coleco_mutex, coleco); + furi_mutex_release(coleco->mutex); } furi_hal_power_disable_otg(); @@ -360,7 +358,7 @@ int32_t coleco_app(void* p) { furi_record_close("gui"); view_port_free(view_port); furi_message_queue_free(event_queue); - delete_mutex(&coleco_mutex); + furi_mutex_free(coleco->mutex); coleco_free(coleco); return 0; } diff --git a/applications/plugins/scrambler/LICENSE b/applications/plugins/rubiks_cube_scrambler/LICENSE similarity index 100% rename from applications/plugins/scrambler/LICENSE rename to applications/plugins/rubiks_cube_scrambler/LICENSE diff --git a/applications/plugins/rubiks_cube_scrambler/README.md b/applications/plugins/rubiks_cube_scrambler/README.md new file mode 100644 index 000000000..feddf06d6 --- /dev/null +++ b/applications/plugins/rubiks_cube_scrambler/README.md @@ -0,0 +1,13 @@ +# Rubik's Cube Scrambler FAP + +## Where to start? +Install the .fap file and put it in your apps folder + +## What does what? +The On/Off button toggles the vibration notification on and off. The "New" button generates a new scramble. The scramble letters correspond with the following moves: R = Right, L = Left, U = Up, D = Down, F = Front, B = Back. The number after the letter indicates how many times to turn that face. For example, R2 means to turn the right face twice. The ' symbol indicates a counter-clockwise turn. For example, R' means to turn the right face counter-clockwise once. + + + +# A special thanks to Tanish for their c scrambler example 🙏 +https://github.com/TanishBhongade/RubiksCubeScrambler-C/ + diff --git a/applications/plugins/scrambler/application.fam b/applications/plugins/rubiks_cube_scrambler/application.fam similarity index 93% rename from applications/plugins/scrambler/application.fam rename to applications/plugins/rubiks_cube_scrambler/application.fam index 4d48d7bb5..131da4de7 100644 --- a/applications/plugins/scrambler/application.fam +++ b/applications/plugins/rubiks_cube_scrambler/application.fam @@ -10,7 +10,7 @@ # ./fbt launch_app APPSRC=rubiks_cube_scrambler App( - appid="Rubiks_Cube_Scrambler", + appid="rubiks_cube_scrambler", name="Rubik's Cube Scrambler", apptype=FlipperAppType.EXTERNAL, entry_point="rubiks_cube_scrambler_main", diff --git a/applications/plugins/scrambler/cube.png b/applications/plugins/rubiks_cube_scrambler/cube.png similarity index 100% rename from applications/plugins/scrambler/cube.png rename to applications/plugins/rubiks_cube_scrambler/cube.png diff --git a/applications/plugins/scrambler/rubiks_cube_scrambler.c b/applications/plugins/rubiks_cube_scrambler/rubiks_cube_scrambler.c similarity index 100% rename from applications/plugins/scrambler/rubiks_cube_scrambler.c rename to applications/plugins/rubiks_cube_scrambler/rubiks_cube_scrambler.c diff --git a/applications/plugins/scrambler/scrambler.c b/applications/plugins/rubiks_cube_scrambler/scrambler.c similarity index 100% rename from applications/plugins/scrambler/scrambler.c rename to applications/plugins/rubiks_cube_scrambler/scrambler.c diff --git a/applications/plugins/scrambler/scrambler.h b/applications/plugins/rubiks_cube_scrambler/scrambler.h similarity index 100% rename from applications/plugins/scrambler/scrambler.h rename to applications/plugins/rubiks_cube_scrambler/scrambler.h diff --git a/applications/plugins/sam/application.fam b/applications/plugins/sam/application.fam index 100840482..1c49eb236 100644 --- a/applications/plugins/sam/application.fam +++ b/applications/plugins/sam/application.fam @@ -1,7 +1,7 @@ App( appid="SAM", name="SAM AYBABTU", - apptype=FlipperAppType.EXTERNAL, + apptype=FlipperAppType.PLUGIN, entry_point="sam_app", requires=[ "gui", @@ -9,7 +9,7 @@ App( ], stack_size=4 * 1024, order=20, - fap_icon="icons/music_10px.png", + fap_icon="music_10px.png", fap_category="Music", fap_icon_assets="icons", ) @@ -24,7 +24,7 @@ App( ], stack_size=4 * 1024, order=20, - fap_icon="icons/music_10px.png", + fap_icon="music_10px.png", fap_category="Music", fap_icon_assets="icons", ) @@ -39,7 +39,7 @@ App( ], stack_size=4 * 1024, order=20, - fap_icon="icons/music_10px.png", + fap_icon="music_10px.png", fap_category="Music", fap_icon_assets="icons", ) @@ -54,7 +54,7 @@ App( ], stack_size=4 * 1024, order=20, - fap_icon="icons/music_10px.png", + fap_icon="music_10px.png", fap_category="Music", fap_icon_assets="icons", ) diff --git a/applications/plugins/sam/music_10px.png b/applications/plugins/sam/music_10px.png new file mode 100644 index 000000000..d41eb0db8 Binary files /dev/null and b/applications/plugins/sam/music_10px.png differ diff --git a/applications/plugins/sam/sam_app.cpp b/applications/plugins/sam/sam_app.cpp index c46c6d3a2..81adf3093 100644 --- a/applications/plugins/sam/sam_app.cpp +++ b/applications/plugins/sam/sam_app.cpp @@ -6,7 +6,7 @@ STM32SAM voice; extern "C" int32_t sam_app(void* p) { UNUSED(p); - if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(30)) { + if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(1000)) { voice.begin(); voice.say( "All your base are belong to us. You have no chance to survive make your time. ha. ha. ha. GOOD BYE. "); @@ -17,7 +17,7 @@ extern "C" int32_t sam_app(void* p) { extern "C" int32_t sam_app_yes(void* p) { UNUSED(p); - if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(30)) { + if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(1000)) { voice.begin(); voice.say("Yes"); furi_hal_speaker_release(); @@ -27,7 +27,7 @@ extern "C" int32_t sam_app_yes(void* p) { extern "C" int32_t sam_app_no(void* p) { UNUSED(p); - if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(30)) { + if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(1000)) { voice.begin(); voice.say("No"); furi_hal_speaker_release(); @@ -37,10 +37,10 @@ extern "C" int32_t sam_app_no(void* p) { extern "C" int32_t sam_app_wtf(void* p) { UNUSED(p); - if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(30)) { + if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(1000)) { voice.begin(); voice.say("What The Fuck"); furi_hal_speaker_release(); } return 0; -} \ No newline at end of file +} diff --git a/applications/plugins/scrambler/README.md b/applications/plugins/scrambler/README.md deleted file mode 100644 index 7e4700bcd..000000000 --- a/applications/plugins/scrambler/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# Setting up the Rubik's Cube Scrambler - -## Installation -To install the Rubik's Cube Scrambler, simply add the `rubiks_cube_scrambler` folder to your `application_users` folder. - -## Cleaning the code and removing old files -Run `./fbt -c fap_rubiks_cube_scrambler` to clean the code and remove any old binaries or compilation artifacts. - -## Compiling the FAP -To compile the FAP, run `./fbt fap_rubiks_cube_scrambler`. - -## Launching the app -To run the Rubik's Cube Scrambler directly from the Flip.x0, use `./fbt launch_app APPSRC=rubiks_cube_scrambler`. - -# A special thanks to Tanish for their c scrambler example 🙏 -https://github.com/TanishBhongade/RubiksCubeScrambler-C/ diff --git a/applications/plugins/scrambler/assets/1.png b/applications/plugins/scrambler/assets/1.png deleted file mode 100644 index d2099ea34..000000000 Binary files a/applications/plugins/scrambler/assets/1.png and /dev/null differ diff --git a/applications/plugins/sentry_safe/README.md b/applications/plugins/sentry_safe/README.md deleted file mode 100644 index c4b00fefe..000000000 --- a/applications/plugins/sentry_safe/README.md +++ /dev/null @@ -1,26 +0,0 @@ -# flipperzero-sentry-safe-plugin - -Flipper zero exploiting vulnerability to open any Sentry Safe and Master Lock electronic safe without any pin code. - -[Vulnerability described here](https://github.com/H4ckd4ddy/bypass-sentry-safe) - -### Installation - -- Download [last release fap file](https://github.com/H4ckd4ddy/flipperzero-sentry-safe-plugin/releases/latest) -- Copy fap file to the apps folder of your flipper SD card - -### Usage - -- Start "Sentry Safe" plugin -- Place wires as described on the plugin screen -- Press enter -- Open safe - -### Build - -- Recursively clone your base firmware (official or not) -- Clone this repository in `applications_user` -- Build with `./fbt fap_dist APPSRC=applications_user/flipperzero-sentry-safe-plugin` -- Retreive builed fap in dist subfolders - -(More info about build tool [here](https://github.com/flipperdevices/flipperzero-firmware/blob/dev/documentation/fbt.md)) diff --git a/applications/plugins/sentry_safe/sentry_safe.c b/applications/plugins/sentry_safe/sentry_safe.c index 4d9c12fc7..789b43f2c 100644 --- a/applications/plugins/sentry_safe/sentry_safe.c +++ b/applications/plugins/sentry_safe/sentry_safe.c @@ -7,6 +7,7 @@ typedef struct { uint8_t status; + FuriMutex* mutex; } SentryState; typedef enum { @@ -22,10 +23,9 @@ typedef struct { const char* status_texts[3] = {"[Press OK to open safe]", "Sending...", "Done !"}; static void sentry_safe_render_callback(Canvas* const canvas, void* ctx) { - const SentryState* sentry_state = acquire_mutex((ValueMutex*)ctx, 25); - if(sentry_state == NULL) { - return; - } + furi_assert(ctx); + const SentryState* sentry_state = ctx; + furi_mutex_acquire(sentry_state->mutex, FuriWaitForever); // Before the function is called, the state is set with the canvas_reset(canvas) @@ -41,7 +41,7 @@ static void sentry_safe_render_callback(Canvas* const canvas, void* ctx) { canvas_draw_str_aligned( canvas, 64, 50, AlignCenter, AlignBottom, status_texts[sentry_state->status]); - release_mutex((ValueMutex*)ctx, sentry_state); + furi_mutex_release(sentry_state->mutex); } static void sentry_safe_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { @@ -89,8 +89,8 @@ int32_t sentry_safe_app(void* p) { sentry_state->status = 0; - ValueMutex state_mutex; - if(!init_mutex(&state_mutex, sentry_state, sizeof(SentryState))) { + sentry_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!sentry_state->mutex) { FURI_LOG_E("SentrySafe", "cannot create mutex\r\n"); furi_message_queue_free(event_queue); free(sentry_state); @@ -98,7 +98,7 @@ int32_t sentry_safe_app(void* p) { } ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, sentry_safe_render_callback, &state_mutex); + view_port_draw_callback_set(view_port, sentry_safe_render_callback, sentry_state); view_port_input_callback_set(view_port, sentry_safe_input_callback, event_queue); // Open GUI and register view_port @@ -109,7 +109,7 @@ int32_t sentry_safe_app(void* p) { for(bool processing = true; processing;) { FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); - SentryState* sentry_state = (SentryState*)acquire_mutex_block(&state_mutex); + furi_mutex_acquire(sentry_state->mutex, FuriWaitForever); if(event_status == FuriStatusOk) { // press events @@ -151,7 +151,7 @@ int32_t sentry_safe_app(void* p) { } view_port_update(view_port); - release_mutex(&state_mutex, sentry_state); + furi_mutex_release(sentry_state->mutex); } // Reset GPIO pins to default state @@ -162,7 +162,7 @@ int32_t sentry_safe_app(void* p) { furi_record_close(RECORD_GUI); view_port_free(view_port); furi_message_queue_free(event_queue); - delete_mutex(&state_mutex); + furi_mutex_free(sentry_state->mutex); free(sentry_state); return 0; diff --git a/applications/plugins/signal_generator/application.fam b/applications/plugins/signal_generator/application.fam index 133319cf4..8edc9221b 100644 --- a/applications/plugins/signal_generator/application.fam +++ b/applications/plugins/signal_generator/application.fam @@ -1,7 +1,7 @@ App( appid="Signal_Generator", name="[GPIO] Signal Generator", - apptype=FlipperAppType.EXTERNAL, + apptype=FlipperAppType.PLUGIN, entry_point="signal_gen_app", cdefines=["APP_SIGNAL_GEN"], requires=["gui"], diff --git a/applications/plugins/snake_game/application.fam b/applications/plugins/snake_game/application.fam index 6b2dee927..4e88e1941 100644 --- a/applications/plugins/snake_game/application.fam +++ b/applications/plugins/snake_game/application.fam @@ -1,15 +1,11 @@ App( appid="Snake", name="Snake Game", - apptype=FlipperAppType.EXTERNAL, + apptype=FlipperAppType.PLUGIN, entry_point="snake_game_app", cdefines=["APP_SNAKE_GAME"], - requires=[ - "gui", - "notification", - "storage", - ], - stack_size=2 * 1024, + requires=["gui"], + stack_size=1 * 1024, order=210, fap_icon="snake_10px.png", fap_category="Games", diff --git a/applications/plugins/snake_game/helpers/snake_file_handler.c b/applications/plugins/snake_game/helpers/snake_file_handler.c deleted file mode 100644 index 569bd8738..000000000 --- a/applications/plugins/snake_game/helpers/snake_file_handler.c +++ /dev/null @@ -1,181 +0,0 @@ -#include "snake_file_handler.h" - -#include -#include - -static void snake_game_close_file(FlipperFormat* file) { - if(file == NULL) { - furi_record_close(RECORD_STORAGE); - return; - } - flipper_format_file_close(file); - flipper_format_free(file); - furi_record_close(RECORD_STORAGE); -} - -static FlipperFormat* snake_game_open_file() { - Storage* storage = furi_record_open(RECORD_STORAGE); - FlipperFormat* file = flipper_format_file_alloc(storage); - - if(storage_common_stat(storage, SNAKE_GAME_FILE_PATH, NULL) == FSE_OK) { - if(!flipper_format_file_open_existing(file, SNAKE_GAME_FILE_PATH)) { - snake_game_close_file(file); - return NULL; - } - } else { - if(storage_common_stat(storage, APPS_DATA, NULL) == FSE_NOT_EXIST) { - if(!storage_simply_mkdir(storage, APPS_DATA)) { - return NULL; - } - } - if(storage_common_stat(storage, SNAKE_GAME_FILE_DIR_PATH, NULL) == FSE_NOT_EXIST) { - if(!storage_simply_mkdir(storage, SNAKE_GAME_FILE_DIR_PATH)) { - return NULL; - } - } - - if(!flipper_format_file_open_new(file, SNAKE_GAME_FILE_PATH)) { - snake_game_close_file(file); - return NULL; - } - - flipper_format_write_header_cstr( - file, SNAKE_GAME_FILE_HEADER, SNAKE_GAME_FILE_ACTUAL_VERSION); - flipper_format_rewind(file); - } - return file; -} - -void snake_game_save_score_to_file(int16_t highscore) { - FlipperFormat* file = snake_game_open_file(); - if(file != NULL) { - uint32_t temp = highscore; - if(!flipper_format_insert_or_update_uint32(file, SNAKE_GAME_CONFIG_HIGHSCORE, &temp, 1)) { - snake_game_close_file(file); - return; - } - snake_game_close_file(file); - } -} - -void snake_game_save_game_to_file(SnakeState* const snake_state) { - FlipperFormat* file = snake_game_open_file(); - - if(file != NULL) { - uint32_t temp = snake_state->len; - if(!flipper_format_insert_or_update_uint32(file, SNAKE_GAME_CONFIG_KEY_LEN, &temp, 1)) { - snake_game_close_file(file); - return; - } - - uint16_t array_size = snake_state->len * 2; - uint32_t temp_array[array_size]; - for(int16_t i = 0, a = 0; a < array_size && i < snake_state->len; i++) { - temp_array[a++] = snake_state->points[i].x; - temp_array[a++] = snake_state->points[i].y; - } - if(!flipper_format_insert_or_update_uint32( - file, SNAKE_GAME_CONFIG_KEY_POINTS, temp_array, array_size)) { - snake_game_close_file(file); - return; - } - - temp = snake_state->currentMovement; - if(!flipper_format_insert_or_update_uint32( - file, SNAKE_GAME_CONFIG_KEY_CURRENT_MOVEMENT, &temp, 1)) { - snake_game_close_file(file); - return; - } - - temp = snake_state->nextMovement; - if(!flipper_format_insert_or_update_uint32( - file, SNAKE_GAME_CONFIG_KEY_NEXT_MOVEMENT, &temp, 1)) { - snake_game_close_file(file); - return; - } - - array_size = 2; - uint32_t temp_point_array[array_size]; - temp_point_array[0] = snake_state->fruit.x; - temp_point_array[1] = snake_state->fruit.y; - if(!flipper_format_insert_or_update_uint32( - file, SNAKE_GAME_CONFIG_KEY_FRUIT_POINTS, temp_point_array, array_size)) { - snake_game_close_file(file); - return; - } - - snake_game_close_file(file); - } -} - -bool snake_game_init_game_from_file(SnakeState* const snake_state) { - FlipperFormat* file = snake_game_open_file(); - - if(file != NULL) { - FuriString* file_type = furi_string_alloc(); - uint32_t version = 1; - if(!flipper_format_read_header(file, file_type, &version)) { - furi_string_free(file_type); - snake_game_close_file(file); - return false; - } - furi_string_free(file_type); - - uint32_t temp; - snake_state->highscore = - (flipper_format_read_uint32(file, SNAKE_GAME_CONFIG_HIGHSCORE, &temp, 1)) ? temp : 0; - flipper_format_rewind(file); - - if(!flipper_format_read_uint32(file, SNAKE_GAME_CONFIG_KEY_LEN, &temp, 1)) { - snake_game_close_file(file); - return false; - } - snake_state->len = temp; - flipper_format_delete_key(file, SNAKE_GAME_CONFIG_KEY_LEN); - - uint16_t array_size = snake_state->len * 2; - uint32_t temp_array[array_size]; - if(!flipper_format_read_uint32( - file, SNAKE_GAME_CONFIG_KEY_POINTS, temp_array, array_size)) { - snake_game_close_file(file); - return false; - } - - for(int16_t i = 0, a = 0; a < array_size && i < snake_state->len; i++) { - snake_state->points[i].x = temp_array[a++]; - snake_state->points[i].y = temp_array[a++]; - } - flipper_format_delete_key(file, SNAKE_GAME_CONFIG_KEY_POINTS); - - if(!flipper_format_read_uint32(file, SNAKE_GAME_CONFIG_KEY_CURRENT_MOVEMENT, &temp, 1)) { - snake_game_close_file(file); - return false; - } - snake_state->currentMovement = temp; - flipper_format_delete_key(file, SNAKE_GAME_CONFIG_KEY_CURRENT_MOVEMENT); - - if(!flipper_format_read_uint32(file, SNAKE_GAME_CONFIG_KEY_NEXT_MOVEMENT, &temp, 1)) { - snake_game_close_file(file); - return false; - } - snake_state->nextMovement = temp; - flipper_format_delete_key(file, SNAKE_GAME_CONFIG_KEY_NEXT_MOVEMENT); - - array_size = 2; - uint32_t temp_point_array[array_size]; - if(!flipper_format_read_uint32( - file, SNAKE_GAME_CONFIG_KEY_FRUIT_POINTS, temp_point_array, array_size)) { - snake_game_close_file(file); - return false; - } - snake_state->fruit.x = temp_point_array[0]; - snake_state->fruit.y = temp_point_array[1]; - flipper_format_delete_key(file, SNAKE_GAME_CONFIG_KEY_FRUIT_POINTS); - - snake_game_close_file(file); - - return true; - } - - return false; -} diff --git a/applications/plugins/snake_game/helpers/snake_file_handler.h b/applications/plugins/snake_game/helpers/snake_file_handler.h deleted file mode 100644 index 178d5d8e0..000000000 --- a/applications/plugins/snake_game/helpers/snake_file_handler.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include "snake_types.h" -#include -#include - -#define APPS_DATA EXT_PATH("apps_data") -#define SNAKE_GAME_FILE_DIR_PATH APPS_DATA "/snake_game" -#define SNAKE_GAME_FILE_PATH SNAKE_GAME_FILE_DIR_PATH "/.snake" - -#define SNAKE_GAME_FILE_HEADER "Flipper Snake plugin run file" -#define SNAKE_GAME_FILE_ACTUAL_VERSION 1 - -#define SNAKE_GAME_CONFIG_KEY_POINTS "SnakePoints" -#define SNAKE_GAME_CONFIG_KEY_LEN "SnakeLen" -#define SNAKE_GAME_CONFIG_KEY_CURRENT_MOVEMENT "CurrentMovement" -#define SNAKE_GAME_CONFIG_KEY_NEXT_MOVEMENT "NextMovement" -#define SNAKE_GAME_CONFIG_KEY_FRUIT_POINTS "FruitPoints" -#define SNAKE_GAME_CONFIG_HIGHSCORE "Highscore" - -void snake_game_save_score_to_file(int16_t highscore); - -void snake_game_save_game_to_file(SnakeState* const snake_state); - -bool snake_game_init_game_from_file(SnakeState* const snake_state); diff --git a/applications/plugins/snake_game/helpers/snake_types.h b/applications/plugins/snake_game/helpers/snake_types.h deleted file mode 100644 index 08682f585..000000000 --- a/applications/plugins/snake_game/helpers/snake_types.h +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once - -#include - -typedef struct { - // +-----x - // | - // | - // y - uint8_t x; - uint8_t y; -} Point; - -typedef enum { - GameStateLife, - - // https://melmagazine.com/en-us/story/snake-nokia-6110-oral-history-taneli-armanto - // Armanto: While testing the early versions of the game, I noticed it was hard - // to control the snake upon getting close to and edge but not crashing — especially - // in the highest speed levels. I wanted the highest level to be as fast as I could - // possibly make the device "run," but on the other hand, I wanted to be friendly - // and help the player manage that level. Otherwise it might not be fun to play. So - // I implemented a little delay. A few milliseconds of extra time right before - // the player crashes, during which she can still change the directions. And if - // she does, the game continues. - GameStateLastChance, - - GameStateGameOver, -} GameState; - -// Note: do not change without purpose. Current values are used in smart -// orthogonality calculation in `snake_game_get_turn_snake`. -typedef enum { - DirectionUp, - DirectionRight, - DirectionDown, - DirectionLeft, -} Direction; - -#define MAX_SNAKE_LEN 253 - -typedef struct { - Point points[MAX_SNAKE_LEN]; - uint16_t len; - bool isNewHighscore; - int16_t highscore; - Direction currentMovement; - Direction nextMovement; // if backward of currentMovement, ignore - Point fruit; - GameState state; -} SnakeState; diff --git a/applications/plugins/snake_game/snake_game.c b/applications/plugins/snake_game/snake_game.c index 2815e2f37..1a0480355 100644 --- a/applications/plugins/snake_game/snake_game.c +++ b/applications/plugins/snake_game/snake_game.c @@ -2,7 +2,6 @@ #include #include #include -#include #include #include @@ -41,7 +40,7 @@ typedef enum { DirectionLeft, } Direction; -#define MAX_SNAKE_LEN 253 +#define MAX_SNAKE_LEN 128 * 64 / 4 typedef struct { Point points[MAX_SNAKE_LEN]; @@ -50,6 +49,7 @@ typedef struct { Direction nextMovement; // if backward of currentMovement, ignore Point fruit; GameState state; + FuriMutex* mutex; } SnakeState; typedef enum { @@ -92,12 +92,10 @@ const NotificationSequence sequence_eat = { }; static void snake_game_render_callback(Canvas* const canvas, void* ctx) { - const SnakeState* snake_state = acquire_mutex((ValueMutex*)ctx, 25); - if(snake_state == NULL) { - return; - } + furi_assert(ctx); + const SnakeState* snake_state = ctx; - // Before the function is called, the state is set with the canvas_reset(canvas) + furi_mutex_acquire(snake_state->mutex, FuriWaitForever); // Frame canvas_draw_frame(canvas, 0, 0, 128, 64); @@ -134,7 +132,7 @@ static void snake_game_render_callback(Canvas* const canvas, void* ctx) { canvas_draw_str_aligned(canvas, 64, 41, AlignCenter, AlignBottom, buffer); } - release_mutex((ValueMutex*)ctx, snake_state); + furi_mutex_release(snake_state->mutex); } static void snake_game_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { @@ -268,10 +266,7 @@ static void return; } - bool can_turn = (snake_state->points[0].x % 2 == 0) && (snake_state->points[0].y % 2 == 0); - if(can_turn) { - snake_state->currentMovement = snake_game_get_turn_snake(snake_state); - } + snake_state->currentMovement = snake_game_get_turn_snake(snake_state); Point next_step = snake_game_get_next_step(snake_state); @@ -324,15 +319,17 @@ int32_t snake_game_app(void* p) { SnakeState* snake_state = malloc(sizeof(SnakeState)); snake_game_init_game(snake_state); - ValueMutex state_mutex; - if(!init_mutex(&state_mutex, snake_state, sizeof(SnakeState))) { + snake_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + + if(!snake_state->mutex) { FURI_LOG_E("SnakeGame", "cannot create mutex\r\n"); + furi_message_queue_free(event_queue); free(snake_state); return 255; } ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, snake_game_render_callback, &state_mutex); + view_port_draw_callback_set(view_port, snake_game_render_callback, snake_state); view_port_input_callback_set(view_port, snake_game_input_callback, event_queue); FuriTimer* timer = @@ -346,13 +343,11 @@ int32_t snake_game_app(void* p) { notification_message_block(notification, &sequence_display_backlight_enforce_on); - DOLPHIN_DEED(DolphinDeedPluginGameStart); - SnakeEvent event; for(bool processing = true; processing;) { FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); - SnakeState* snake_state = (SnakeState*)acquire_mutex_block(&state_mutex); + furi_mutex_acquire(snake_state->mutex, FuriWaitForever); if(event_status == FuriStatusOk) { // press events @@ -391,7 +386,7 @@ int32_t snake_game_app(void* p) { } view_port_update(view_port); - release_mutex(&state_mutex, snake_state); + furi_mutex_release(snake_state->mutex); } // Return backlight to normal state @@ -404,31 +399,8 @@ int32_t snake_game_app(void* p) { furi_record_close(RECORD_NOTIFICATION); view_port_free(view_port); furi_message_queue_free(event_queue); - delete_mutex(&state_mutex); + furi_mutex_free(snake_state->mutex); free(snake_state); return 0; } - -// Screen is 128x64 px -// (4 + 4) * 16 - 4 + 2 + 2border == 128 -// (4 + 4) * 8 - 4 + 2 + 2border == 64 -// Game field from point{x: 0, y: 0} to point{x: 30, y: 14}. -// The snake turns only in even cells - intersections. -// ┌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┐ -// ╎ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ╎ -// ╎ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ╎ -// ╎ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ╎ -// ╎ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ╎ -// ╎ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ╎ -// ╎ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ╎ -// ╎ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ╎ -// ╎ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ╎ -// ╎ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ╎ -// ╎ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ╎ -// ╎ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ╎ -// ╎ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ╎ -// ╎ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ╎ -// ╎ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ╎ -// ╎ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ╎ -// └╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┘ diff --git a/applications/plugins/solitaire/defines.h b/applications/plugins/solitaire/defines.h index 1456f1964..8734395e8 100644 --- a/applications/plugins/solitaire/defines.h +++ b/applications/plugins/solitaire/defines.h @@ -53,4 +53,5 @@ typedef struct { int8_t selected_card; CardAnimation animation; uint8_t* buffer; + FuriMutex* mutex; } GameState; \ No newline at end of file diff --git a/applications/plugins/solitaire/solitaire.c b/applications/plugins/solitaire/solitaire.c index 8893b3986..4cbd694dc 100644 --- a/applications/plugins/solitaire/solitaire.c +++ b/applications/plugins/solitaire/solitaire.c @@ -1,5 +1,4 @@ #include -#include #include #include #include "defines.h" @@ -155,10 +154,9 @@ static void draw_animation(Canvas* const canvas, const GameState* game_state) { } static void render_callback(Canvas* const canvas, void* ctx) { - const GameState* game_state = acquire_mutex((ValueMutex*)ctx, 25); - if(game_state == NULL) { - return; - } + furi_assert(ctx); + const GameState* game_state = ctx; + furi_mutex_acquire(game_state->mutex, FuriWaitForever); switch(game_state->state) { case GameStateAnimate: @@ -174,7 +172,7 @@ static void render_callback(Canvas* const canvas, void* ctx) { break; } - release_mutex((ValueMutex*)ctx, game_state); + furi_mutex_release(game_state->mutex); } void remove_drag(GameState* game_state) { @@ -255,7 +253,9 @@ bool place_on_top(Card* where, Card what) { int8_t b_letter = (int8_t)what.character; if(a_letter == 12) a_letter = -1; if(b_letter == 12) b_letter = -1; + if(where->disabled && b_letter != -1) return false; + if((a_letter + 1) == b_letter) { where->disabled = what.disabled; where->pip = what.pip; @@ -467,8 +467,8 @@ int32_t solitaire_app(void* p) { game_state->state = GameStateStart; game_state->processing = true; - ValueMutex state_mutex; - if(!init_mutex(&state_mutex, game_state, sizeof(GameState))) { + game_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!game_state->mutex) { FURI_LOG_E(APP_NAME, "cannot create mutex\r\n"); return_code = 255; goto free_and_exit; @@ -478,19 +478,20 @@ int32_t solitaire_app(void* p) { notification_message_block(notification, &sequence_display_backlight_enforce_on); ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, render_callback, &state_mutex); + view_port_draw_callback_set(view_port, render_callback, game_state); view_port_input_callback_set(view_port, input_callback, event_queue); FuriTimer* timer = furi_timer_alloc(update_timer_callback, FuriTimerTypePeriodic, event_queue); furi_timer_start(timer, furi_kernel_get_tick_frequency() / 30); - Gui* gui = furi_record_open("gui"); + Gui* gui = furi_record_open(RECORD_GUI); gui_add_view_port(gui, view_port, GuiLayerFullscreen); AppEvent event; + for(bool processing = true; processing;) { FuriStatus event_status = furi_message_queue_get(event_queue, &event, 150); - GameState* localstate = (GameState*)acquire_mutex_block(&state_mutex); + furi_mutex_acquire(game_state->mutex, FuriWaitForever); bool hadChange = false; if(event_status == FuriStatusOk) { if(event.type == EventTypeKey) { @@ -502,7 +503,7 @@ int32_t solitaire_app(void* p) { case InputKeyRight: case InputKeyLeft: case InputKeyOk: - localstate->input = event.input.key; + game_state->input = event.input.key; break; case InputKeyBack: processing = false; @@ -518,12 +519,12 @@ int32_t solitaire_app(void* p) { case InputKeyRight: case InputKeyLeft: case InputKeyOk: - if(event.input.key == InputKeyOk && localstate->state == GameStateStart) { - localstate->state = GameStatePlay; + if(event.input.key == InputKeyOk && game_state->state == GameStateStart) { + game_state->state = GameStatePlay; init(game_state); } else { hadChange = true; - localstate->input = event.input.key; + game_state->input = event.input.key; } break; case InputKeyBack: @@ -536,16 +537,16 @@ int32_t solitaire_app(void* p) { } } } else if(event.type == EventTypeTick) { - tick(localstate, notification); - processing = localstate->processing; - localstate->input = InputKeyMAX; + tick(game_state, notification); + processing = game_state->processing; + game_state->input = InputKeyMAX; } } else { //FURI_LOG_W(APP_NAME, "osMessageQueue: event timeout"); // event timeout } if(hadChange || game_state->state == GameStateAnimate) view_port_update(view_port); - release_mutex(&state_mutex, localstate); + furi_mutex_release(game_state->mutex); } notification_message_block(notification, &sequence_display_backlight_enforce_auto); @@ -555,7 +556,7 @@ int32_t solitaire_app(void* p) { furi_record_close(RECORD_GUI); furi_record_close(RECORD_NOTIFICATION); view_port_free(view_port); - delete_mutex(&state_mutex); + furi_mutex_free(game_state->mutex); free_and_exit: free(game_state->animation.buffer); @@ -566,4 +567,4 @@ free_and_exit: free(game_state); furi_message_queue_free(event_queue); return return_code; -} \ No newline at end of file +} diff --git a/applications/plugins/spectrum_analyzer/application.fam b/applications/plugins/spectrum_analyzer/application.fam index 04bb946ee..344c2244f 100644 --- a/applications/plugins/spectrum_analyzer/application.fam +++ b/applications/plugins/spectrum_analyzer/application.fam @@ -8,5 +8,5 @@ App( stack_size=2 * 1024, order=12, fap_icon="spectrum_10px.png", - fap_category="Tools", + fap_category="Sub-GHz", ) diff --git a/applications/plugins/spectrum_analyzer/spectrum_analyzer.c b/applications/plugins/spectrum_analyzer/spectrum_analyzer.c index 8d68c5a1a..99c12adf7 100644 --- a/applications/plugins/spectrum_analyzer/spectrum_analyzer.c +++ b/applications/plugins/spectrum_analyzer/spectrum_analyzer.c @@ -392,6 +392,9 @@ void spectrum_analyzer_free(SpectrumAnalyzer* instance) { furi_hal_subghz_idle(); furi_hal_subghz_sleep(); + + // Disable power for External CC1101 if it was enabled and module is connected + furi_hal_subghz_disable_ext_power(); } int32_t spectrum_analyzer_app(void* p) { @@ -400,6 +403,9 @@ int32_t spectrum_analyzer_app(void* p) { SpectrumAnalyzer* spectrum_analyzer = spectrum_analyzer_alloc(); InputEvent input; + // Enable power for External CC1101 if it is connected + furi_hal_subghz_enable_ext_power(); + furi_hal_power_suppress_charge_enter(); FURI_LOG_D("Spectrum", "Main Loop - Starting worker"); diff --git a/applications/plugins/spi_mem_manager/application.fam b/applications/plugins/spi_mem_manager/application.fam index 09d801876..21e70980a 100644 --- a/applications/plugins/spi_mem_manager/application.fam +++ b/applications/plugins/spi_mem_manager/application.fam @@ -1,13 +1,13 @@ App( appid="spi_mem_manager", - name="SPI Mem Manager", + name="[SPI] Mem Manager", apptype=FlipperAppType.EXTERNAL, entry_point="spi_mem_app", requires=["gui"], stack_size=1 * 2048, order=30, fap_icon="images/Dip8_10px.png", - fap_category="Tools", + fap_category="GPIO", fap_icon_assets="images", fap_private_libs=[ Lib( diff --git a/applications/plugins/spi_mem_manager/lib/spi/spi_mem_tools.c b/applications/plugins/spi_mem_manager/lib/spi/spi_mem_tools.c index 7a788241b..3518ca25c 100644 --- a/applications/plugins/spi_mem_manager/lib/spi/spi_mem_tools.c +++ b/applications/plugins/spi_mem_manager/lib/spi/spi_mem_tools.c @@ -74,8 +74,8 @@ bool spi_mem_tools_read_chip_info(SPIMemChip* chip) { bool spi_mem_tools_check_chip_info(SPIMemChip* chip) { SPIMemChip new_chip_info; + spi_mem_tools_read_chip_info(&new_chip_info); do { - if(!spi_mem_tools_read_chip_info(&new_chip_info)) break; if(chip->vendor_id != new_chip_info.vendor_id) break; if(chip->type_id != new_chip_info.type_id) break; if(chip->capacity_id != new_chip_info.capacity_id) break; diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_about.c b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_about.c index 9e0f6f0c6..dc0cc4fe4 100644 --- a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_about.c +++ b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_about.c @@ -4,9 +4,8 @@ #define SPI_MEM_VERSION_APP "0.1.0" #define SPI_MEM_DEVELOPER "DrunkBatya" #define SPI_MEM_GITHUB "https://github.com/flipperdevices/flipperzero-firmware" -#define SPI_MEM_NAME "\e#\e! SPI Mem Manager \e!\n" // NOSONAR -#define SPI_MEM_BLANK_INV \ - "\e#\e! \e!\n" // NOSONAR +#define SPI_MEM_NAME "\e#\e! SPI Mem Manager \e!\n" +#define SPI_MEM_BLANK_INV "\e#\e! \e!\n" void spi_mem_scene_about_on_enter(void* context) { SPIMemApp* app = context; @@ -16,11 +15,11 @@ void spi_mem_scene_about_on_enter(void* context) { app->widget, 0, 0, 128, 14, AlignCenter, AlignBottom, SPI_MEM_BLANK_INV, false); widget_add_text_box_element( app->widget, 0, 2, 128, 14, AlignCenter, AlignBottom, SPI_MEM_NAME, false); - furi_string_printf(tmp_string, "\e#%s\n", "Information"); // NOSONAR + furi_string_printf(tmp_string, "\e#%s\n", "Information"); furi_string_cat_printf(tmp_string, "Version: %s\n", SPI_MEM_VERSION_APP); furi_string_cat_printf(tmp_string, "Developed by: %s\n", SPI_MEM_DEVELOPER); furi_string_cat_printf(tmp_string, "Github: %s\n\n", SPI_MEM_GITHUB); - furi_string_cat_printf(tmp_string, "\e#%s\n", "Description"); // NOSONAR + furi_string_cat_printf(tmp_string, "\e#%s\n", "Description"); furi_string_cat_printf( tmp_string, "SPI memory dumper\n" diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_delete_confirm.c b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_delete_confirm.c index 5d0c0b8d8..bb5142452 100644 --- a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_delete_confirm.c +++ b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_delete_confirm.c @@ -16,7 +16,7 @@ void spi_mem_scene_delete_confirm_on_enter(void* context) { FuriString* file_name = furi_string_alloc(); FuriString* message = furi_string_alloc(); path_extract_filename(app->file_path, file_name, true); - furi_string_printf(message, "\e#Delete %s?\e#", furi_string_get_cstr(file_name)); // NOSONAR + furi_string_printf(message, "\e#Delete %s?\e#", furi_string_get_cstr(file_name)); widget_add_text_box_element( app->widget, 0, 0, 128, 27, AlignCenter, AlignCenter, furi_string_get_cstr(message), true); widget_add_button_element( diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_start.c b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_start.c index b664df687..38d064a4d 100644 --- a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_start.c +++ b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_start.c @@ -60,7 +60,7 @@ bool spi_mem_scene_start_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(app->scene_manager, SPIMemSceneChipDetect); success = true; } else if(event.event == SPIMemSceneStartSubmenuIndexSaved) { - furi_string_set(app->file_path, SPI_MEM_FILE_FOLDER); + furi_string_set(app->file_path, STORAGE_APP_DATA_PATH_PREFIX); scene_manager_next_scene(app->scene_manager, SPIMemSceneSelectFile); success = true; } else if(event.event == SPIMemSceneStartSubmenuIndexErase) { diff --git a/applications/plugins/spi_mem_manager/spi_mem_app.c b/applications/plugins/spi_mem_manager/spi_mem_app.c index 63531b74c..96c3632d0 100644 --- a/applications/plugins/spi_mem_manager/spi_mem_app.c +++ b/applications/plugins/spi_mem_manager/spi_mem_app.c @@ -16,9 +16,9 @@ static bool spi_mem_back_event_callback(void* context) { } SPIMemApp* spi_mem_alloc(void) { - SPIMemApp* instance = malloc(sizeof(SPIMemApp)); + SPIMemApp* instance = malloc(sizeof(SPIMemApp)); //-V799 - instance->file_path = furi_string_alloc(); + instance->file_path = furi_string_alloc_set(STORAGE_APP_DATA_PATH_PREFIX); instance->gui = furi_record_open(RECORD_GUI); instance->notifications = furi_record_open(RECORD_NOTIFICATION); instance->view_dispatcher = view_dispatcher_alloc(); @@ -37,7 +37,8 @@ SPIMemApp* spi_mem_alloc(void) { instance->text_input = text_input_alloc(); instance->mode = SPIMemModeUnknown; - furi_string_set(instance->file_path, SPI_MEM_FILE_FOLDER); + // Migrate data from old sd-card folder + storage_common_migrate(instance->storage, EXT_PATH("spimem"), STORAGE_APP_DATA_PATH_PREFIX); view_dispatcher_enable_queue(instance->view_dispatcher); view_dispatcher_set_event_callback_context(instance->view_dispatcher, instance); @@ -70,7 +71,7 @@ SPIMemApp* spi_mem_alloc(void) { furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_external); scene_manager_next_scene(instance->scene_manager, SPIMemSceneStart); return instance; -} +} //-V773 void spi_mem_free(SPIMemApp* instance) { view_dispatcher_remove_view(instance->view_dispatcher, SPIMemViewSubmenu); @@ -105,7 +106,6 @@ void spi_mem_free(SPIMemApp* instance) { int32_t spi_mem_app(void* p) { UNUSED(p); SPIMemApp* instance = spi_mem_alloc(); - spi_mem_file_create_folder(instance); view_dispatcher_run(instance->view_dispatcher); spi_mem_free(instance); return 0; diff --git a/applications/plugins/spi_mem_manager/spi_mem_app_i.h b/applications/plugins/spi_mem_manager/spi_mem_app_i.h index 4ce056175..285ca66d2 100644 --- a/applications/plugins/spi_mem_manager/spi_mem_app_i.h +++ b/applications/plugins/spi_mem_manager/spi_mem_app_i.h @@ -24,7 +24,6 @@ #define TAG "SPIMem" #define SPI_MEM_FILE_EXTENSION ".bin" -#define SPI_MEM_FILE_FOLDER EXT_PATH("spimem") #define SPI_MEM_FILE_NAME_SIZE 100 #define SPI_MEM_TEXT_BUFFER_SIZE 128 diff --git a/applications/plugins/spi_mem_manager/spi_mem_files.c b/applications/plugins/spi_mem_manager/spi_mem_files.c index a7374da19..9b787bd7f 100644 --- a/applications/plugins/spi_mem_manager/spi_mem_files.c +++ b/applications/plugins/spi_mem_manager/spi_mem_files.c @@ -1,11 +1,5 @@ #include "spi_mem_app_i.h" -void spi_mem_file_create_folder(SPIMemApp* app) { - if(!storage_simply_mkdir(app->storage, SPI_MEM_FILE_FOLDER)) { - dialog_message_show_storage_error(app->dialogs, "Cannot create\napp folder"); - } -} - bool spi_mem_file_delete(SPIMemApp* app) { return (storage_simply_remove(app->storage, furi_string_get_cstr(app->file_path))); } @@ -13,7 +7,7 @@ bool spi_mem_file_delete(SPIMemApp* app) { bool spi_mem_file_select(SPIMemApp* app) { DialogsFileBrowserOptions browser_options; dialog_file_browser_set_basic_options(&browser_options, SPI_MEM_FILE_EXTENSION, &I_Dip8_10px); - browser_options.base_path = SPI_MEM_FILE_FOLDER; + browser_options.base_path = STORAGE_APP_DATA_PATH_PREFIX; bool success = dialog_file_browser_show(app->dialogs, app->file_path, app->file_path, &browser_options); return success; diff --git a/applications/plugins/spi_mem_manager/spi_mem_files.h b/applications/plugins/spi_mem_manager/spi_mem_files.h index 0e735d951..6a529d327 100644 --- a/applications/plugins/spi_mem_manager/spi_mem_files.h +++ b/applications/plugins/spi_mem_manager/spi_mem_files.h @@ -1,7 +1,6 @@ #pragma once #include "spi_mem_app.h" -void spi_mem_file_create_folder(SPIMemApp* app); bool spi_mem_file_select(SPIMemApp* app); bool spi_mem_file_create(SPIMemApp* app, const char* file_name); bool spi_mem_file_delete(SPIMemApp* app); diff --git a/applications/plugins/spi_mem_manager/tools/chiplist_convert.py b/applications/plugins/spi_mem_manager/tools/chiplist_convert.py old mode 100755 new mode 100644 diff --git a/applications/plugins/subbrute b/applications/plugins/subbrute new file mode 160000 index 000000000..19153c723 --- /dev/null +++ b/applications/plugins/subbrute @@ -0,0 +1 @@ +Subproject commit 19153c72395c7d62efac425325b4c4c646e1fd3f diff --git a/applications/plugins/subbrute/README.md b/applications/plugins/subbrute/README.md deleted file mode 100644 index 98c1503d0..000000000 --- a/applications/plugins/subbrute/README.md +++ /dev/null @@ -1,72 +0,0 @@ -# SubGHz Bruteforcer Plugin for Flipper Zero - -SubGhz Bruteforcer from [Unleashed Firmware](https://github.com/DarkFlippers/unleashed-firmware) - -### Disclaimer - -This software is for experimental purposes only and is not meant for any illegal activity/purposes. -We do not condone illegal activity and strongly encourage keeping transmissions to legal/valid uses allowed by law. - -### Supported Protocols: - -#### CAME - -- CAME 12bit 303MHz -- CAME 12bit 307MHz -- CAME 12bit 315MHz -- CAME 12bit 433MHz -- CAME 12bit 868MHz - -#### NICE - -- NICE 12bit 433MHz -- NICE 12bit 868MHz - -#### Ansonic - -- Ansonic 12bit 433.075MHz -- Ansonic 12bit 433.920MHz -- Ansonic 12bit 434.075MHz - -#### Holtek - -- Holtek HT12X 12bit 433.920MHz - -#### Chamberlain - -- Chamberlain 9bit 300MHz -- Chamberlain 9bit 315MHz -- Chamberlain 9bit 390MHz -- Chamberlain 9bit 433MHz -- Chamberlain 8bit 300MHz -- Chamberlain 8bit 315MHz -- Chamberlain 8bit 390MHz -- Chamberlain 7bit 300MHz -- Chamberlain 7bit 315MHz -- Chamberlain 7bit 390MHz - -#### Linear - -- Linear 10bit 300MHz -- Linear 10bit 310MHz - -#### UNILARM - -- UNILARM 25bit 330MHz -- UNILARM 25bit 433MHz - -#### SMC5326 - -- SMC5326 25bit 330MHz -- SMC5326 25bit 433MHz - -#### PT2260 - -- PT2260 24bit 315MHz -- PT2260 24bit 330MHz -- PT2260 24bit 390MHz -- PT2260 24bit 433MHz - -#### Additional - -- BF Existing dump works for most other static protocols supported by Flipper Zero diff --git a/applications/plugins/subbrute/application.fam b/applications/plugins/subbrute/application.fam deleted file mode 100644 index 20a5f5367..000000000 --- a/applications/plugins/subbrute/application.fam +++ /dev/null @@ -1,13 +0,0 @@ -App( - appid="SubGHz_Bruteforcer", - name="Sub-GHz Bruteforcer", - apptype=FlipperAppType.EXTERNAL, - entry_point="subbrute_app", - cdefines=["APP_SUB_BRUTE"], - requires=["gui", "dialogs"], - stack_size=2 * 1024, - order=11, - fap_icon="images/subbrute_10px.png", - fap_category="Tools", - fap_icon_assets="images", -) diff --git a/applications/plugins/subbrute/helpers/gui_top_buttons.c b/applications/plugins/subbrute/helpers/gui_top_buttons.c deleted file mode 100644 index 0415c5ae7..000000000 --- a/applications/plugins/subbrute/helpers/gui_top_buttons.c +++ /dev/null @@ -1,59 +0,0 @@ -#include "gui_top_buttons.h" - -void elements_button_top_left(Canvas* canvas, const char* str) { - const Icon* icon = &I_ButtonUp_7x4; - - const uint8_t button_height = 12; - const uint8_t vertical_offset = 3; - const uint8_t horizontal_offset = 3; - const uint8_t string_width = canvas_string_width(canvas, str); - const uint8_t icon_h_offset = 3; - const uint8_t icon_width_with_offset = icon_get_width(icon) + icon_h_offset; - const uint8_t icon_v_offset = icon_get_height(icon) + vertical_offset; - const uint8_t button_width = string_width + horizontal_offset * 2 + icon_width_with_offset; - - const uint8_t x = 0; - const uint8_t y = 0 + button_height; - - uint8_t line_x = x + button_width; - uint8_t line_y = y - button_height; - canvas_draw_box(canvas, x, line_y, button_width, button_height); - canvas_draw_line(canvas, line_x + 0, line_y, line_x + 0, y - 1); - canvas_draw_line(canvas, line_x + 1, line_y, line_x + 1, y - 2); - canvas_draw_line(canvas, line_x + 2, line_y, line_x + 2, y - 3); - - canvas_invert_color(canvas); - canvas_draw_icon(canvas, x + horizontal_offset, y - icon_v_offset, icon); - canvas_draw_str( - canvas, x + horizontal_offset + icon_width_with_offset, y - vertical_offset, str); - canvas_invert_color(canvas); -} - -void elements_button_top_right(Canvas* canvas, const char* str) { - const Icon* icon = &I_ButtonDown_7x4; - - const uint8_t button_height = 12; - const uint8_t vertical_offset = 3; - const uint8_t horizontal_offset = 3; - const uint8_t string_width = canvas_string_width(canvas, str); - const uint8_t icon_h_offset = 3; - const uint8_t icon_width_with_offset = icon_get_width(icon) + icon_h_offset; - const uint8_t icon_v_offset = icon_get_height(icon) + vertical_offset + 1; - const uint8_t button_width = string_width + horizontal_offset * 2 + icon_width_with_offset; - - const uint8_t x = canvas_width(canvas); - const uint8_t y = 0 + button_height; - - uint8_t line_x = x - button_width; - uint8_t line_y = y - button_height; - canvas_draw_box(canvas, line_x, line_y, button_width, button_height); - canvas_draw_line(canvas, line_x - 1, line_y, line_x - 1, y - 1); - canvas_draw_line(canvas, line_x - 2, line_y, line_x - 2, y - 2); - canvas_draw_line(canvas, line_x - 3, line_y, line_x - 3, y - 3); - - canvas_invert_color(canvas); - canvas_draw_str(canvas, x - button_width + horizontal_offset, y - vertical_offset, str); - canvas_draw_icon( - canvas, x - horizontal_offset - icon_get_width(icon), y - icon_v_offset, icon); - canvas_invert_color(canvas); -} \ No newline at end of file diff --git a/applications/plugins/subbrute/helpers/gui_top_buttons.h b/applications/plugins/subbrute/helpers/gui_top_buttons.h deleted file mode 100644 index b5ca507b7..000000000 --- a/applications/plugins/subbrute/helpers/gui_top_buttons.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -/** - * Thanks to the author of metronome - * @param canvas - * @param str - */ -void elements_button_top_left(Canvas* canvas, const char* str); - -/** - * Thanks to the author of metronome - * @param canvas - * @param str - */ -void elements_button_top_right(Canvas* canvas, const char* str); \ No newline at end of file diff --git a/applications/plugins/subbrute/helpers/subbrute_worker.c b/applications/plugins/subbrute/helpers/subbrute_worker.c deleted file mode 100644 index 118e63c65..000000000 --- a/applications/plugins/subbrute/helpers/subbrute_worker.c +++ /dev/null @@ -1,437 +0,0 @@ -#include "subbrute_worker_private.h" -#include -#include -#include -#include -#include - -#define TAG "SubBruteWorker" -#define SUBBRUTE_TX_TIMEOUT 5 -#define SUBBRUTE_MANUAL_TRANSMIT_INTERVAL 400 - -SubBruteWorker* subbrute_worker_alloc() { - SubBruteWorker* instance = malloc(sizeof(SubBruteWorker)); - - instance->state = SubBruteWorkerStateIDLE; - instance->step = 0; - instance->worker_running = false; - instance->initiated = false; - instance->last_time_tx_data = 0; - instance->load_index = 0; - - instance->thread = furi_thread_alloc(); - furi_thread_set_name(instance->thread, "SubBruteAttackWorker"); - furi_thread_set_stack_size(instance->thread, 2048); - furi_thread_set_context(instance->thread, instance); - furi_thread_set_callback(instance->thread, subbrute_worker_thread); - - instance->context = NULL; - instance->callback = NULL; - - instance->decoder_result = NULL; - instance->transmitter = NULL; - instance->environment = subghz_environment_alloc(); - subghz_environment_set_protocol_registry( - instance->environment, (void*)&subghz_protocol_registry); - - instance->transmit_mode = false; - - return instance; -} - -void subbrute_worker_free(SubBruteWorker* instance) { - furi_assert(instance); - - // I don't know how to free this - instance->decoder_result = NULL; - - if(instance->transmitter != NULL) { - subghz_transmitter_free(instance->transmitter); - instance->transmitter = NULL; - } - - subghz_environment_free(instance->environment); - instance->environment = NULL; - - furi_thread_free(instance->thread); - - free(instance); -} - -uint64_t subbrute_worker_get_step(SubBruteWorker* instance) { - return instance->step; -} - -bool subbrute_worker_set_step(SubBruteWorker* instance, uint64_t step) { - furi_assert(instance); - if(!subbrute_worker_can_manual_transmit(instance)) { - FURI_LOG_W(TAG, "Cannot set step during running mode"); - return false; - } - - instance->step = step; - - return true; -} - -bool subbrute_worker_init_default_attack( - SubBruteWorker* instance, - SubBruteAttacks attack_type, - uint64_t step, - const SubBruteProtocol* protocol, - uint8_t extra_repeats) { - furi_assert(instance); - - if(instance->worker_running) { - FURI_LOG_W(TAG, "Init Worker when it's running"); - subbrute_worker_stop(instance); - } - - instance->attack = attack_type; - instance->frequency = protocol->frequency; - instance->preset = protocol->preset; - instance->file = protocol->file; - instance->step = step; - instance->bits = protocol->bits; - instance->te = protocol->te; - instance->repeat = protocol->repeat + extra_repeats; - instance->load_index = 0; - instance->file_key = 0; - instance->two_bytes = false; - - instance->max_value = - subbrute_protocol_calc_max_value(instance->attack, instance->bits, instance->two_bytes); - - instance->initiated = true; - instance->state = SubBruteWorkerStateReady; - subbrute_worker_send_callback(instance); -#ifdef FURI_DEBUG - FURI_LOG_I( - TAG, - "subbrute_worker_init_default_attack: %s, bits: %d, preset: %s, file: %s, te: %ld, repeat: %d, max_value: %lld", - subbrute_protocol_name(instance->attack), - instance->bits, - subbrute_protocol_preset(instance->preset), - subbrute_protocol_file(instance->file), - instance->te, - instance->repeat, - instance->max_value); -#endif - - return true; -} - -bool subbrute_worker_init_file_attack( - SubBruteWorker* instance, - uint64_t step, - uint8_t load_index, - uint64_t file_key, - SubBruteProtocol* protocol, - uint8_t extra_repeats, - bool two_bytes) { - furi_assert(instance); - - if(instance->worker_running) { - FURI_LOG_W(TAG, "Init Worker when it's running"); - subbrute_worker_stop(instance); - } - - instance->attack = SubBruteAttackLoadFile; - instance->frequency = protocol->frequency; - instance->preset = protocol->preset; - instance->file = protocol->file; - instance->step = step; - instance->bits = protocol->bits; - instance->te = protocol->te; - instance->load_index = load_index; - instance->repeat = protocol->repeat + extra_repeats; - instance->file_key = file_key; - instance->two_bytes = two_bytes; - - instance->max_value = - subbrute_protocol_calc_max_value(instance->attack, instance->bits, instance->two_bytes); - - instance->initiated = true; - instance->state = SubBruteWorkerStateReady; - subbrute_worker_send_callback(instance); -#ifdef FURI_DEBUG - FURI_LOG_I( - TAG, - "subbrute_worker_init_file_attack: %s, bits: %d, preset: %s, file: %s, te: %ld, repeat: %d, max_value: %lld, key: %llX", - subbrute_protocol_name(instance->attack), - instance->bits, - subbrute_protocol_preset(instance->preset), - subbrute_protocol_file(instance->file), - instance->te, - instance->repeat, - instance->max_value, - instance->file_key); -#endif - - return true; -} - -bool subbrute_worker_start(SubBruteWorker* instance) { - furi_assert(instance); - - if(!instance->initiated) { - FURI_LOG_W(TAG, "Worker not init!"); - return false; - } - - if(instance->worker_running) { - FURI_LOG_W(TAG, "Worker is already running!"); - return false; - } - if(instance->state != SubBruteWorkerStateReady && - instance->state != SubBruteWorkerStateFinished) { - FURI_LOG_W(TAG, "Worker cannot start, invalid device state: %d", instance->state); - return false; - } - - instance->worker_running = true; - furi_thread_start(instance->thread); - - return true; -} - -void subbrute_worker_stop(SubBruteWorker* instance) { - furi_assert(instance); - - if(!instance->worker_running) { - return; - } - - instance->worker_running = false; - furi_thread_join(instance->thread); - - furi_hal_subghz_set_path(FuriHalSubGhzPathIsolate); - furi_hal_subghz_sleep(); -} - -bool subbrute_worker_transmit_current_key(SubBruteWorker* instance, uint64_t step) { - furi_assert(instance); - - if(!instance->initiated) { - FURI_LOG_W(TAG, "Worker not init!"); - return false; - } - if(instance->worker_running) { - FURI_LOG_W(TAG, "Worker in running state!"); - return false; - } - if(instance->state != SubBruteWorkerStateReady && - instance->state != SubBruteWorkerStateFinished) { - FURI_LOG_W(TAG, "Invalid state for running worker! State: %d", instance->state); - return false; - } - - uint32_t ticks = furi_get_tick(); - if((ticks - instance->last_time_tx_data) < SUBBRUTE_MANUAL_TRANSMIT_INTERVAL) { -#if FURI_DEBUG - FURI_LOG_D(TAG, "Need to wait, current: %ld", ticks - instance->last_time_tx_data); -#endif - return false; - } - - instance->last_time_tx_data = ticks; - instance->step = step; - - bool result; - instance->protocol_name = subbrute_protocol_file(instance->file); - FlipperFormat* flipper_format = flipper_format_string_alloc(); - Stream* stream = flipper_format_get_raw_stream(flipper_format); - - stream_clean(stream); - - if(instance->attack == SubBruteAttackLoadFile) { - subbrute_protocol_file_payload( - stream, - step, - instance->bits, - instance->te, - instance->repeat, - instance->load_index, - instance->file_key, - instance->two_bytes); - } else { - subbrute_protocol_default_payload( - stream, instance->file, step, instance->bits, instance->te, instance->repeat); - } - - // size_t written = stream_write_string(stream, payload); - // if(written <= 0) { - // FURI_LOG_W(TAG, "Error creating packet! EXIT"); - // result = false; - // } else { - subbrute_worker_subghz_transmit(instance, flipper_format); - - result = true; -#if FURI_DEBUG - FURI_LOG_D(TAG, "Manual transmit done"); -#endif - // } - - flipper_format_free(flipper_format); - // furi_string_free(payload); - - return result; -} - -bool subbrute_worker_is_running(SubBruteWorker* instance) { - return instance->worker_running; -} - -bool subbrute_worker_can_manual_transmit(SubBruteWorker* instance) { - furi_assert(instance); - - if(!instance->initiated) { - FURI_LOG_W(TAG, "Worker not init!"); - return false; - } - - return !instance->worker_running && instance->state != SubBruteWorkerStateIDLE && - instance->state != SubBruteWorkerStateTx && - ((furi_get_tick() - instance->last_time_tx_data) > SUBBRUTE_MANUAL_TRANSMIT_INTERVAL); -} - -void subbrute_worker_set_callback( - SubBruteWorker* instance, - SubBruteWorkerCallback callback, - void* context) { - furi_assert(instance); - - instance->callback = callback; - instance->context = context; -} - -void subbrute_worker_subghz_transmit(SubBruteWorker* instance, FlipperFormat* flipper_format) { - while(instance->transmit_mode) { - furi_delay_ms(SUBBRUTE_TX_TIMEOUT); - } - instance->transmit_mode = true; - if(instance->transmitter != NULL) { - subghz_transmitter_free(instance->transmitter); - instance->transmitter = NULL; - } - instance->transmitter = - subghz_transmitter_alloc_init(instance->environment, instance->protocol_name); - subghz_transmitter_deserialize(instance->transmitter, flipper_format); - furi_hal_subghz_reset(); - furi_hal_subghz_load_preset(instance->preset); - furi_hal_subghz_set_frequency_and_path(instance->frequency); - furi_hal_subghz_start_async_tx(subghz_transmitter_yield, instance->transmitter); - - while(!furi_hal_subghz_is_async_tx_complete()) { - furi_delay_ms(SUBBRUTE_TX_TIMEOUT); - } - furi_hal_subghz_stop_async_tx(); - - furi_hal_subghz_set_path(FuriHalSubGhzPathIsolate); - furi_hal_subghz_sleep(); - subghz_transmitter_free(instance->transmitter); - instance->transmitter = NULL; - - instance->transmit_mode = false; -} - -void subbrute_worker_send_callback(SubBruteWorker* instance) { - if(instance->callback != NULL) { - instance->callback(instance->context, instance->state); - } -} - -/** - * Entrypoint for worker - * - * @param context SubBruteWorker* - * @return 0 if ok - */ -int32_t subbrute_worker_thread(void* context) { - furi_assert(context); - SubBruteWorker* instance = (SubBruteWorker*)context; - - if(!instance->worker_running) { - FURI_LOG_W(TAG, "Worker is not set to running state!"); - return -1; - } - if(instance->state != SubBruteWorkerStateReady && - instance->state != SubBruteWorkerStateFinished) { - FURI_LOG_W(TAG, "Invalid state for running worker! State: %d", instance->state); - return -2; - } -#ifdef FURI_DEBUG - FURI_LOG_I(TAG, "Worker start"); -#endif - - SubBruteWorkerState local_state = instance->state = SubBruteWorkerStateTx; - subbrute_worker_send_callback(instance); - - instance->protocol_name = subbrute_protocol_file(instance->file); - - FlipperFormat* flipper_format = flipper_format_string_alloc(); - Stream* stream = flipper_format_get_raw_stream(flipper_format); - - while(instance->worker_running) { - stream_clean(stream); - if(instance->attack == SubBruteAttackLoadFile) { - subbrute_protocol_file_payload( - stream, - instance->step, - instance->bits, - instance->te, - instance->repeat, - instance->load_index, - instance->file_key, - instance->two_bytes); - } else { - subbrute_protocol_default_payload( - stream, - instance->file, - instance->step, - instance->bits, - instance->te, - instance->repeat); - } -#ifdef FURI_DEBUG - //FURI_LOG_I(TAG, "Payload: %s", furi_string_get_cstr(payload)); - //furi_delay_ms(SUBBRUTE_MANUAL_TRANSMIT_INTERVAL / 4); -#endif - - // size_t written = stream_write_stream_write_string(stream, payload); - // if(written <= 0) { - // FURI_LOG_W(TAG, "Error creating packet! BREAK"); - // instance->worker_running = false; - // local_state = SubBruteWorkerStateIDLE; - // furi_string_free(payload); - // break; - // } - - subbrute_worker_subghz_transmit(instance, flipper_format); - - if(instance->step + 1 > instance->max_value) { -#ifdef FURI_DEBUG - FURI_LOG_I(TAG, "Worker finished to end"); -#endif - local_state = SubBruteWorkerStateFinished; - // furi_string_free(payload); - break; - } - instance->step++; - - // furi_string_free(payload); - furi_delay_ms(SUBBRUTE_TX_TIMEOUT); - } - - flipper_format_free(flipper_format); - - instance->worker_running = false; // Because we have error states - instance->state = local_state == SubBruteWorkerStateTx ? SubBruteWorkerStateReady : - local_state; - subbrute_worker_send_callback(instance); - -#ifdef FURI_DEBUG - FURI_LOG_I(TAG, "Worker stop"); -#endif - return 0; -} diff --git a/applications/plugins/subbrute/helpers/subbrute_worker.h b/applications/plugins/subbrute/helpers/subbrute_worker.h deleted file mode 100644 index 4046f997c..000000000 --- a/applications/plugins/subbrute/helpers/subbrute_worker.h +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once - -#include "../subbrute_protocols.h" - -typedef enum { - SubBruteWorkerStateIDLE, - SubBruteWorkerStateReady, - SubBruteWorkerStateTx, - SubBruteWorkerStateFinished -} SubBruteWorkerState; - -typedef void (*SubBruteWorkerCallback)(void* context, SubBruteWorkerState state); - -typedef struct SubBruteWorker SubBruteWorker; - -SubBruteWorker* subbrute_worker_alloc(); -void subbrute_worker_free(SubBruteWorker* instance); -uint64_t subbrute_worker_get_step(SubBruteWorker* instance); -bool subbrute_worker_set_step(SubBruteWorker* instance, uint64_t step); -bool subbrute_worker_is_running(SubBruteWorker* instance); -bool subbrute_worker_init_default_attack( - SubBruteWorker* instance, - SubBruteAttacks attack_type, - uint64_t step, - const SubBruteProtocol* protocol, - uint8_t extra_repeats); -bool subbrute_worker_init_file_attack( - SubBruteWorker* instance, - uint64_t step, - uint8_t load_index, - uint64_t file_key, - SubBruteProtocol* protocol, - uint8_t extra_repeats, - bool two_bytes); -bool subbrute_worker_start(SubBruteWorker* instance); -void subbrute_worker_stop(SubBruteWorker* instance); -bool subbrute_worker_transmit_current_key(SubBruteWorker* instance, uint64_t step); -bool subbrute_worker_can_manual_transmit(SubBruteWorker* instance); -void subbrute_worker_set_callback( - SubBruteWorker* instance, - SubBruteWorkerCallback callback, - void* context); \ No newline at end of file diff --git a/applications/plugins/subbrute/helpers/subbrute_worker_private.h b/applications/plugins/subbrute/helpers/subbrute_worker_private.h deleted file mode 100644 index e38e77dc4..000000000 --- a/applications/plugins/subbrute/helpers/subbrute_worker_private.h +++ /dev/null @@ -1,48 +0,0 @@ -#pragma once - -#include "subbrute_worker.h" -#include -#include -#include -#include - -struct SubBruteWorker { - SubBruteWorkerState state; - volatile bool worker_running; - volatile bool initiated; - volatile bool transmit_mode; - - // Current step - uint64_t step; - - // SubGhz - FuriThread* thread; - SubGhzProtocolDecoderBase* decoder_result; - SubGhzEnvironment* environment; - SubGhzTransmitter* transmitter; - const char* protocol_name; - - // Initiated values - SubBruteAttacks attack; // Attack state - uint32_t frequency; - FuriHalSubGhzPreset preset; - SubBruteFileProtocol file; - uint8_t bits; - uint32_t te; - uint8_t repeat; - uint8_t load_index; // Index of group to bruteforce in loaded file - uint64_t file_key; - uint64_t max_value; // Max step - bool two_bytes; - - // Manual transmit - uint32_t last_time_tx_data; - - // Callback for changed states - SubBruteWorkerCallback callback; - void* context; -}; - -int32_t subbrute_worker_thread(void* context); -void subbrute_worker_subghz_transmit(SubBruteWorker* instance, FlipperFormat* flipper_format); -void subbrute_worker_send_callback(SubBruteWorker* instance); \ No newline at end of file diff --git a/applications/plugins/subbrute/images/DolphinNice_96x59.png b/applications/plugins/subbrute/images/DolphinNice_96x59.png deleted file mode 100644 index a299d3630..000000000 Binary files a/applications/plugins/subbrute/images/DolphinNice_96x59.png and /dev/null differ diff --git a/applications/plugins/subbrute/images/Sub1ghz_14/frame_01.png b/applications/plugins/subbrute/images/Sub1ghz_14/frame_01.png deleted file mode 100644 index 52dc4ad21..000000000 Binary files a/applications/plugins/subbrute/images/Sub1ghz_14/frame_01.png and /dev/null differ diff --git a/applications/plugins/subbrute/images/Sub1ghz_14/frame_02.png b/applications/plugins/subbrute/images/Sub1ghz_14/frame_02.png deleted file mode 100644 index 2dff1c031..000000000 Binary files a/applications/plugins/subbrute/images/Sub1ghz_14/frame_02.png and /dev/null differ diff --git a/applications/plugins/subbrute/images/Sub1ghz_14/frame_03.png b/applications/plugins/subbrute/images/Sub1ghz_14/frame_03.png deleted file mode 100644 index c1e438b01..000000000 Binary files a/applications/plugins/subbrute/images/Sub1ghz_14/frame_03.png and /dev/null differ diff --git a/applications/plugins/subbrute/images/Sub1ghz_14/frame_04.png b/applications/plugins/subbrute/images/Sub1ghz_14/frame_04.png deleted file mode 100644 index 169fb6147..000000000 Binary files a/applications/plugins/subbrute/images/Sub1ghz_14/frame_04.png and /dev/null differ diff --git a/applications/plugins/subbrute/images/Sub1ghz_14/frame_05.png b/applications/plugins/subbrute/images/Sub1ghz_14/frame_05.png deleted file mode 100644 index 79b2bc972..000000000 Binary files a/applications/plugins/subbrute/images/Sub1ghz_14/frame_05.png and /dev/null differ diff --git a/applications/plugins/subbrute/images/Sub1ghz_14/frame_06.png b/applications/plugins/subbrute/images/Sub1ghz_14/frame_06.png deleted file mode 100644 index 8fce0c44d..000000000 Binary files a/applications/plugins/subbrute/images/Sub1ghz_14/frame_06.png and /dev/null differ diff --git a/applications/plugins/subbrute/images/sub1_10px.png b/applications/plugins/subbrute/images/sub1_10px.png deleted file mode 100644 index 5a25fdf4e..000000000 Binary files a/applications/plugins/subbrute/images/sub1_10px.png and /dev/null differ diff --git a/applications/plugins/subbrute/scenes/subbrute_scene.h b/applications/plugins/subbrute/scenes/subbrute_scene.h deleted file mode 100644 index c048985e2..000000000 --- a/applications/plugins/subbrute/scenes/subbrute_scene.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include - -// Generate scene id and total number -#define ADD_SCENE(prefix, name, id) SubBruteScene##id, -typedef enum { -#include "subbrute_scene_config.h" - SubBruteSceneNum, -} SubBruteScene; -#undef ADD_SCENE - -extern const SceneManagerHandlers subbrute_scene_handlers; - -// Generate scene on_enter handlers declaration -#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); -#include "subbrute_scene_config.h" -#undef ADD_SCENE - -// Generate scene on_event handlers declaration -#define ADD_SCENE(prefix, name, id) \ - bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); -#include "subbrute_scene_config.h" -#undef ADD_SCENE - -// Generate scene on_exit handlers declaration -#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); -#include "subbrute_scene_config.h" -#undef ADD_SCENE diff --git a/applications/plugins/subbrute/scenes/subbrute_scene_config.h b/applications/plugins/subbrute/scenes/subbrute_scene_config.h deleted file mode 100644 index 3541df9ac..000000000 --- a/applications/plugins/subbrute/scenes/subbrute_scene_config.h +++ /dev/null @@ -1,7 +0,0 @@ -ADD_SCENE(subbrute, load_file, LoadFile) -ADD_SCENE(subbrute, load_select, LoadSelect) -ADD_SCENE(subbrute, run_attack, RunAttack) -ADD_SCENE(subbrute, save_name, SaveName) -ADD_SCENE(subbrute, save_success, SaveSuccess) -ADD_SCENE(subbrute, setup_attack, SetupAttack) -ADD_SCENE(subbrute, start, Start) \ No newline at end of file diff --git a/applications/plugins/subbrute/scenes/subbrute_scene_load_file.c b/applications/plugins/subbrute/scenes/subbrute_scene_load_file.c deleted file mode 100644 index 8aae1bcad..000000000 --- a/applications/plugins/subbrute/scenes/subbrute_scene_load_file.c +++ /dev/null @@ -1,91 +0,0 @@ -#include "../subbrute_i.h" -#include "subbrute_scene.h" - -#define TAG "SubBruteSceneLoadFile" - -void subbrute_scene_load_file_on_enter(void* context) { - furi_assert(context); - SubBruteState* instance = (SubBruteState*)context; - - // Input events and views are managed by file_browser - FuriString* app_folder; - FuriString* load_path; - load_path = furi_string_alloc(); - app_folder = furi_string_alloc_set(SUBBRUTE_PATH); - - DialogsFileBrowserOptions browser_options; - dialog_file_browser_set_basic_options(&browser_options, SUBBRUTE_FILE_EXT, &I_sub1_10px); - - SubBruteFileResult load_result = SubBruteFileResultUnknown; - // TODO: DELETE IT -#ifdef SUBBRUTE_FAST_TRACK - bool res = true; - furi_string_printf(load_path, "%s", "/ext/subghz/princeton.sub"); -#else - bool res = - dialog_file_browser_show(instance->dialogs, load_path, app_folder, &browser_options); -#endif -#ifdef FURI_DEBUG - FURI_LOG_D( - TAG, - "load_path: %s, app_folder: %s", - furi_string_get_cstr(load_path), - furi_string_get_cstr(app_folder)); -#endif - if(res) { - load_result = - subbrute_device_load_from_file(instance->device, furi_string_get_cstr(load_path)); - if(load_result == SubBruteFileResultOk) { - uint8_t extra_repeats = subbrute_main_view_get_extra_repeats(instance->view_main); - - load_result = subbrute_device_attack_set( - instance->device, SubBruteAttackLoadFile, extra_repeats); - if(load_result == SubBruteFileResultOk) { - if(!subbrute_worker_init_file_attack( - instance->worker, - instance->device->current_step, - instance->device->bit_index, - instance->device->key_from_file, - instance->device->file_protocol_info, - extra_repeats, - instance->device->two_bytes)) { - furi_crash("Invalid attack set!"); - } - // Ready to run! - FURI_LOG_I(TAG, "Ready to run"); - res = true; - } - } - - if(load_result == SubBruteFileResultOk) { - scene_manager_next_scene(instance->scene_manager, SubBruteSceneLoadSelect); - } else { - FURI_LOG_E(TAG, "Returned error: %d", load_result); - - FuriString* dialog_msg; - dialog_msg = furi_string_alloc(); - furi_string_cat_printf( - dialog_msg, "Cannot parse\nfile: %s", subbrute_device_error_get_desc(load_result)); - dialog_message_show_storage_error(instance->dialogs, furi_string_get_cstr(dialog_msg)); - furi_string_free(dialog_msg); - scene_manager_search_and_switch_to_previous_scene( - instance->scene_manager, SubBruteSceneStart); - } - } else { - scene_manager_search_and_switch_to_previous_scene( - instance->scene_manager, SubBruteSceneStart); - } - - furi_string_free(app_folder); - furi_string_free(load_path); -} - -void subbrute_scene_load_file_on_exit(void* context) { - UNUSED(context); -} - -bool subbrute_scene_load_file_on_event(void* context, SceneManagerEvent event) { - UNUSED(context); - UNUSED(event); - return false; -} \ No newline at end of file diff --git a/applications/plugins/subbrute/scenes/subbrute_scene_load_select.c b/applications/plugins/subbrute/scenes/subbrute_scene_load_select.c deleted file mode 100644 index d018e8b4d..000000000 --- a/applications/plugins/subbrute/scenes/subbrute_scene_load_select.c +++ /dev/null @@ -1,82 +0,0 @@ -#include "../subbrute_i.h" -#include "subbrute_scene.h" - -#define TAG "SubBruteSceneStart" - -void subbrute_scene_load_select_callback(SubBruteCustomEvent event, void* context) { - furi_assert(context); - - SubBruteState* instance = (SubBruteState*)context; - view_dispatcher_send_custom_event(instance->view_dispatcher, event); -} - -void subbrute_scene_load_select_on_enter(void* context) { - furi_assert(context); -#ifdef FURI_DEBUG - FURI_LOG_I(TAG, "subbrute_scene_load_select_on_enter"); -#endif - SubBruteState* instance = (SubBruteState*)context; - SubBruteMainView* view = instance->view_main; - - instance->current_view = SubBruteViewMain; - subbrute_main_view_set_callback(view, subbrute_scene_load_select_callback, instance); - subbrute_main_view_set_index( - view, 7, true, instance->device->two_bytes, instance->device->key_from_file); - - view_dispatcher_switch_to_view(instance->view_dispatcher, instance->current_view); -} - -void subbrute_scene_load_select_on_exit(void* context) { - UNUSED(context); -#ifdef FURI_DEBUG - FURI_LOG_I(TAG, "subbrute_scene_load_select_on_exit"); -#endif -} - -bool subbrute_scene_load_select_on_event(void* context, SceneManagerEvent event) { - SubBruteState* instance = (SubBruteState*)context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubBruteCustomEventTypeIndexSelected) { - /*#ifdef FURI_DEBUG && !SUBBRUTE_FAST_TRACK - view_dispatcher_stop(instance->view_dispatcher); - consumed = true; -#else*/ - instance->device->current_step = 0; - instance->device->bit_index = subbrute_main_view_get_index(instance->view_main); - instance->device->two_bytes = subbrute_main_view_get_two_bytes(instance->view_main); - uint8_t extra_repeats = subbrute_main_view_get_extra_repeats(instance->view_main); - instance->device->max_value = subbrute_protocol_calc_max_value( - instance->device->attack, - instance->device->bit_index, - instance->device->two_bytes); - - if(!subbrute_worker_init_file_attack( - instance->worker, - instance->device->current_step, - instance->device->bit_index, - instance->device->key_from_file, - instance->device->file_protocol_info, - extra_repeats, - instance->device->two_bytes)) { - furi_crash("Invalid attack set!"); - } - scene_manager_next_scene(instance->scene_manager, SubBruteSceneSetupAttack); - /*#endif*/ - consumed = true; - } /* else if(event.event == SubBruteCustomEventTypeChangeStepUp) { - instance->device->two_bytes = true; - } else if(event.event == SubBruteCustomEventTypeChangeStepDown) { - instance->device->two_bytes = false; - }*/ - } else if(event.type == SceneManagerEventTypeBack) { - if(!scene_manager_search_and_switch_to_previous_scene( - instance->scene_manager, SubBruteSceneStart)) { - scene_manager_next_scene(instance->scene_manager, SubBruteSceneStart); - } - consumed = true; - } - - return consumed; -} \ No newline at end of file diff --git a/applications/plugins/subbrute/scenes/subbrute_scene_run_attack.c b/applications/plugins/subbrute/scenes/subbrute_scene_run_attack.c deleted file mode 100644 index 2f22c25d4..000000000 --- a/applications/plugins/subbrute/scenes/subbrute_scene_run_attack.c +++ /dev/null @@ -1,104 +0,0 @@ -#include "../subbrute_i.h" -#include "subbrute_scene.h" - -#define TAG "SubBruteSceneRunAttack" - -static void subbrute_scene_run_attack_callback(SubBruteCustomEvent event, void* context) { - furi_assert(context); - - SubBruteState* instance = (SubBruteState*)context; - view_dispatcher_send_custom_event(instance->view_dispatcher, event); -} - -static void - subbrute_scene_run_attack_device_state_changed(void* context, SubBruteWorkerState state) { - furi_assert(context); - - SubBruteState* instance = (SubBruteState*)context; - - if(state == SubBruteWorkerStateIDLE) { - // Can't be IDLE on this step! - view_dispatcher_send_custom_event(instance->view_dispatcher, SubBruteCustomEventTypeError); - } else if(state == SubBruteWorkerStateFinished) { - view_dispatcher_send_custom_event( - instance->view_dispatcher, SubBruteCustomEventTypeTransmitFinished); - } -} -void subbrute_scene_run_attack_on_exit(void* context) { - furi_assert(context); - SubBruteState* instance = (SubBruteState*)context; - - notification_message(instance->notifications, &sequence_blink_stop); - subbrute_worker_stop(instance->worker); -} - -void subbrute_scene_run_attack_on_enter(void* context) { - furi_assert(context); - SubBruteState* instance = (SubBruteState*)context; - SubBruteAttackView* view = instance->view_attack; - - instance->current_view = SubBruteViewAttack; - subbrute_attack_view_set_callback(view, subbrute_scene_run_attack_callback, instance); - view_dispatcher_switch_to_view(instance->view_dispatcher, instance->current_view); - - subbrute_worker_set_callback( - instance->worker, subbrute_scene_run_attack_device_state_changed, instance); - - if(!subbrute_worker_is_running(instance->worker)) { - subbrute_worker_set_step(instance->worker, instance->device->current_step); - if(!subbrute_worker_start(instance->worker)) { - view_dispatcher_send_custom_event( - instance->view_dispatcher, SubBruteCustomEventTypeError); - } else { - notification_message(instance->notifications, &sequence_single_vibro); - notification_message(instance->notifications, &sequence_blink_start_yellow); - } - } -} - -bool subbrute_scene_run_attack_on_event(void* context, SceneManagerEvent event) { - SubBruteState* instance = (SubBruteState*)context; - SubBruteAttackView* view = instance->view_attack; - - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - uint64_t step = subbrute_worker_get_step(instance->worker); - instance->device->current_step = step; - subbrute_attack_view_set_current_step(view, step); - - if(event.event == SubBruteCustomEventTypeTransmitFinished) { - notification_message(instance->notifications, &sequence_display_backlight_on); - notification_message(instance->notifications, &sequence_double_vibro); - - scene_manager_next_scene(instance->scene_manager, SubBruteSceneSetupAttack); - } else if( - event.event == SubBruteCustomEventTypeTransmitNotStarted || - event.event == SubBruteCustomEventTypeBackPressed) { - if(subbrute_worker_is_running(instance->worker)) { - // Notify - notification_message(instance->notifications, &sequence_single_vibro); - } - // Stop transmit - scene_manager_search_and_switch_to_previous_scene( - instance->scene_manager, SubBruteSceneSetupAttack); - } else if(event.event == SubBruteCustomEventTypeError) { - notification_message(instance->notifications, &sequence_error); - - // Stop transmit - scene_manager_search_and_switch_to_previous_scene( - instance->scene_manager, SubBruteSceneSetupAttack); - } else if(event.event == SubBruteCustomEventTypeUpdateView) { - //subbrute_attack_view_set_current_step(view, instance->device->current_step); - } - consumed = true; - } else if(event.type == SceneManagerEventTypeTick) { - uint64_t step = subbrute_worker_get_step(instance->worker); - instance->device->current_step = step; - subbrute_attack_view_set_current_step(view, step); - - consumed = true; - } - - return consumed; -} diff --git a/applications/plugins/subbrute/scenes/subbrute_scene_save_name.c b/applications/plugins/subbrute/scenes/subbrute_scene_save_name.c deleted file mode 100644 index bb129e948..000000000 --- a/applications/plugins/subbrute/scenes/subbrute_scene_save_name.c +++ /dev/null @@ -1,84 +0,0 @@ -#include "../subbrute_i.h" -#include "subbrute_scene.h" -#include - -#define TAG "SubBruteSceneSaveFile" - -void subbrute_scene_save_name_on_enter(void* context) { - SubBruteState* instance = (SubBruteState*)context; - - // Setup view - TextInput* text_input = instance->text_input; - set_random_name(instance->text_store, sizeof(instance->text_store)); - - text_input_set_header_text(text_input, "Name of file"); - text_input_set_result_callback( - text_input, - subbrute_text_input_callback, - instance, - instance->text_store, - SUBBRUTE_MAX_LEN_NAME, - true); - - furi_string_reset(instance->file_path); - furi_string_set_str(instance->file_path, SUBBRUTE_PATH); - - ValidatorIsFile* validator_is_file = validator_is_file_alloc_init( - furi_string_get_cstr(instance->file_path), SUBBRUTE_FILE_EXT, ""); - text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); - - view_dispatcher_switch_to_view(instance->view_dispatcher, SubBruteViewTextInput); -} - -bool subbrute_scene_save_name_on_event(void* context, SceneManagerEvent event) { - SubBruteState* instance = (SubBruteState*)context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeBack) { - scene_manager_previous_scene(instance->scene_manager); - return true; - } else if( - event.type == SceneManagerEventTypeCustom && - event.event == SubBruteCustomEventTypeTextEditDone) { -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "Saving: %s", instance->text_store); -#endif - bool success = false; - if(strcmp(instance->text_store, "")) { - furi_string_reset(instance->file_path); - furi_string_cat_printf( - instance->file_path, - "%s/%s%s", - SUBBRUTE_PATH, - instance->text_store, - SUBBRUTE_FILE_EXT); - - if(subbrute_device_save_file( - instance->device, furi_string_get_cstr(instance->file_path))) { - scene_manager_next_scene(instance->scene_manager, SubBruteSceneSaveSuccess); - success = true; - consumed = true; - } - } - - if(!success) { - dialog_message_show_storage_error(instance->dialogs, "Error during saving!"); - consumed = scene_manager_search_and_switch_to_previous_scene( - instance->scene_manager, SubBruteSceneSetupAttack); - } - } - return consumed; -} - -void subbrute_scene_save_name_on_exit(void* context) { - SubBruteState* instance = (SubBruteState*)context; - - // Clear view - void* validator_context = text_input_get_validator_callback_context(instance->text_input); - text_input_set_validator(instance->text_input, NULL, NULL); - validator_is_file_free(validator_context); - - text_input_reset(instance->text_input); - - furi_string_reset(instance->file_path); -} diff --git a/applications/plugins/subbrute/scenes/subbrute_scene_save_success.c b/applications/plugins/subbrute/scenes/subbrute_scene_save_success.c deleted file mode 100644 index 20b1a0de4..000000000 --- a/applications/plugins/subbrute/scenes/subbrute_scene_save_success.c +++ /dev/null @@ -1,51 +0,0 @@ -#include "../subbrute_i.h" -#include "subbrute_scene.h" - -void subbrute_scene_save_success_on_enter(void* context) { - furi_assert(context); - SubBruteState* instance = context; - - // Setup view - Popup* popup = instance->popup; - popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); - popup_set_header(popup, "Saved!", 13, 22, AlignLeft, AlignBottom); - popup_set_timeout(popup, 1500); - popup_set_context(popup, instance); - popup_set_callback(popup, subbrute_popup_closed_callback); - popup_enable_timeout(popup); - view_dispatcher_switch_to_view(instance->view_dispatcher, SubBruteViewPopup); -} - -bool subbrute_scene_save_success_on_event(void* context, SceneManagerEvent event) { - furi_assert(context); - - SubBruteState* instance = (SubBruteState*)context; - //SubBruteMainView* view = instance->view_main; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubBruteCustomEventTypePopupClosed) { - if(!scene_manager_search_and_switch_to_previous_scene( - instance->scene_manager, SubBruteSceneSetupAttack)) { - scene_manager_next_scene(instance->scene_manager, SubBruteSceneStart); - } - return true; - } - } - return false; -} - -void subbrute_scene_save_success_on_exit(void* context) { - furi_assert(context); - - SubBruteState* instance = (SubBruteState*)context; - - // Clear view - Popup* popup = instance->popup; - popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom); - popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); - popup_set_icon(popup, 0, 0, NULL); - popup_set_callback(popup, NULL); - popup_set_context(popup, NULL); - popup_set_timeout(popup, 0); - popup_disable_timeout(popup); -} diff --git a/applications/plugins/subbrute/scenes/subbrute_scene_setup_attack.c b/applications/plugins/subbrute/scenes/subbrute_scene_setup_attack.c deleted file mode 100644 index c2877c7cb..000000000 --- a/applications/plugins/subbrute/scenes/subbrute_scene_setup_attack.c +++ /dev/null @@ -1,138 +0,0 @@ -#include "../subbrute_i.h" -#include "subbrute_scene.h" - -#define TAG "SubBruteSceneSetupAttack" - -static void subbrute_scene_setup_attack_callback(SubBruteCustomEvent event, void* context) { - furi_assert(context); - - SubBruteState* instance = (SubBruteState*)context; - view_dispatcher_send_custom_event(instance->view_dispatcher, event); -} - -static void - subbrute_scene_setup_attack_device_state_changed(void* context, SubBruteWorkerState state) { - furi_assert(context); - - SubBruteState* instance = (SubBruteState*)context; - - if(state == SubBruteWorkerStateIDLE) { - // Can't be IDLE on this step! - view_dispatcher_send_custom_event(instance->view_dispatcher, SubBruteCustomEventTypeError); - } -} - -void subbrute_scene_setup_attack_on_enter(void* context) { - furi_assert(context); - SubBruteState* instance = (SubBruteState*)context; - SubBruteAttackView* view = instance->view_attack; - - notification_message(instance->notifications, &sequence_reset_vibro); - -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "Enter Attack: %s", subbrute_protocol_name(instance->device->attack)); -#endif - - subbrute_worker_set_callback( - instance->worker, subbrute_scene_setup_attack_device_state_changed, context); - if(subbrute_worker_is_running(instance->worker)) { - subbrute_worker_stop(instance->worker); - instance->device->current_step = subbrute_worker_get_step(instance->worker); - } - - subbrute_attack_view_init_values( - view, - instance->device->attack, - instance->device->max_value, - instance->device->current_step, - false, - instance->device->extra_repeats); - - instance->current_view = SubBruteViewAttack; - subbrute_attack_view_set_callback(view, subbrute_scene_setup_attack_callback, instance); - view_dispatcher_switch_to_view(instance->view_dispatcher, instance->current_view); -} - -void subbrute_scene_setup_attack_on_exit(void* context) { - furi_assert(context); -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "subbrute_scene_setup_attack_on_exit"); -#endif - SubBruteState* instance = (SubBruteState*)context; - subbrute_worker_stop(instance->worker); - notification_message(instance->notifications, &sequence_blink_stop); - notification_message(instance->notifications, &sequence_reset_vibro); -} - -bool subbrute_scene_setup_attack_on_event(void* context, SceneManagerEvent event) { - SubBruteState* instance = (SubBruteState*)context; - SubBruteAttackView* view = instance->view_attack; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubBruteCustomEventTypeTransmitStarted) { - scene_manager_next_scene(instance->scene_manager, SubBruteSceneRunAttack); - } else if(event.event == SubBruteCustomEventTypeSaveFile) { - subbrute_attack_view_init_values( - view, - instance->device->attack, - instance->device->max_value, - instance->device->current_step, - false, - instance->device->extra_repeats); - scene_manager_next_scene(instance->scene_manager, SubBruteSceneSaveName); - } else if(event.event == SubBruteCustomEventTypeBackPressed) { - subbrute_attack_view_init_values( - view, - instance->device->attack, - instance->device->max_value, - instance->device->current_step, - false, - instance->device->extra_repeats); - scene_manager_next_scene(instance->scene_manager, SubBruteSceneStart); - } else if(event.event == SubBruteCustomEventTypeError) { - notification_message(instance->notifications, &sequence_error); - } else if(event.event == SubBruteCustomEventTypeTransmitCustom) { - // We can transmit only in not working states - if(subbrute_worker_can_manual_transmit(instance->worker)) { - // MANUAL Transmit! - // Blink - notification_message(instance->notifications, &sequence_blink_green_100); - subbrute_worker_transmit_current_key( - instance->worker, instance->device->current_step); - // Stop - notification_message(instance->notifications, &sequence_blink_stop); - } - } else if(event.event == SubBruteCustomEventTypeChangeStepUp) { - // +1 - uint64_t step = subbrute_device_add_step(instance->device, 1); - subbrute_worker_set_step(instance->worker, step); - subbrute_attack_view_set_current_step(view, step); - } else if(event.event == SubBruteCustomEventTypeChangeStepUpMore) { - // +50 - uint64_t step = subbrute_device_add_step(instance->device, 50); - subbrute_worker_set_step(instance->worker, step); - subbrute_attack_view_set_current_step(view, step); - } else if(event.event == SubBruteCustomEventTypeChangeStepDown) { - // -1 - uint64_t step = subbrute_device_add_step(instance->device, -1); - subbrute_worker_set_step(instance->worker, step); - subbrute_attack_view_set_current_step(view, step); - } else if(event.event == SubBruteCustomEventTypeChangeStepDownMore) { - // -50 - uint64_t step = subbrute_device_add_step(instance->device, -50); - subbrute_worker_set_step(instance->worker, step); - subbrute_attack_view_set_current_step(view, step); - } - - consumed = true; - } else if(event.type == SceneManagerEventTypeTick) { - if(subbrute_worker_is_running(instance->worker)) { - instance->device->current_step = subbrute_worker_get_step(instance->worker); - } - subbrute_attack_view_set_current_step(view, instance->device->current_step); - consumed = true; - } - - return consumed; -} diff --git a/applications/plugins/subbrute/scenes/subbrute_scene_start.c b/applications/plugins/subbrute/scenes/subbrute_scene_start.c deleted file mode 100644 index 256762d92..000000000 --- a/applications/plugins/subbrute/scenes/subbrute_scene_start.c +++ /dev/null @@ -1,89 +0,0 @@ -#include "../subbrute_i.h" -#include "subbrute_scene.h" - -#define TAG "SubBruteSceneStart" - -void subbrute_scene_start_callback(SubBruteCustomEvent event, void* context) { - furi_assert(context); - - SubBruteState* instance = (SubBruteState*)context; -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "subbrute_scene_start_callback"); -#endif - view_dispatcher_send_custom_event(instance->view_dispatcher, event); -} - -void subbrute_scene_start_on_enter(void* context) { - furi_assert(context); -#ifdef FURI_DEBUG - FURI_LOG_I(TAG, "subbrute_scene_start_on_enter"); -#endif - SubBruteState* instance = (SubBruteState*)context; - SubBruteMainView* view = instance->view_main; - - instance->current_view = SubBruteViewMain; - subbrute_main_view_set_callback(view, subbrute_scene_start_callback, instance); - subbrute_main_view_set_index( - view, instance->device->attack, false, instance->device->two_bytes, 0); - - view_dispatcher_switch_to_view(instance->view_dispatcher, instance->current_view); - - // TODO: DELETE IT -#ifdef SUBBRUTE_FAST_TRACK - scene_manager_next_scene(instance->scene_manager, SubBruteSceneLoadFile); -#endif -} - -void subbrute_scene_start_on_exit(void* context) { - UNUSED(context); -#ifdef FURI_DEBUG - FURI_LOG_I(TAG, "subbrute_scene_start_on_exit"); -#endif -} - -bool subbrute_scene_start_on_event(void* context, SceneManagerEvent event) { - SubBruteState* instance = (SubBruteState*)context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { -#ifdef FURI_DEBUG - FURI_LOG_D( - TAG, - "Event: %ld, SubBruteCustomEventTypeMenuSelected: %s, SubBruteCustomEventTypeLoadFile: %s", - event.event, - event.event == SubBruteCustomEventTypeMenuSelected ? "true" : "false", - event.event == SubBruteCustomEventTypeLoadFile ? "true" : "false"); -#endif - if(event.event == SubBruteCustomEventTypeMenuSelected) { - SubBruteAttacks attack = subbrute_main_view_get_index(instance->view_main); - uint8_t extra_repeats = subbrute_main_view_get_extra_repeats(instance->view_main); - - if((subbrute_device_attack_set(instance->device, attack, extra_repeats) != - SubBruteFileResultOk) || - (!subbrute_worker_init_default_attack( - instance->worker, - attack, - instance->device->current_step, - instance->device->protocol_info, - instance->device->extra_repeats))) { - furi_crash("Invalid attack set!"); - } - scene_manager_next_scene(instance->scene_manager, SubBruteSceneSetupAttack); - - consumed = true; - } else if(event.event == SubBruteCustomEventTypeLoadFile) { - //uint8_t extra_repeats = subbrute_main_view_get_extra_repeats(instance->view_main); - - //instance->device->extra_repeats = extra_repeats; - scene_manager_next_scene(instance->scene_manager, SubBruteSceneLoadFile); - consumed = true; - } - } else if(event.type == SceneManagerEventTypeBack) { - //exit app - scene_manager_stop(instance->scene_manager); - view_dispatcher_stop(instance->view_dispatcher); - consumed = true; - } - - return consumed; -} \ No newline at end of file diff --git a/applications/plugins/subbrute/scenes/subbute_scene.c b/applications/plugins/subbrute/scenes/subbute_scene.c deleted file mode 100644 index 6d9ba9799..000000000 --- a/applications/plugins/subbrute/scenes/subbute_scene.c +++ /dev/null @@ -1,30 +0,0 @@ -#include "subbrute_scene.h" - -// Generate scene on_enter handlers array -#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, -void (*const subbrute_on_enter_handlers[])(void*) = { -#include "subbrute_scene_config.h" -}; -#undef ADD_SCENE - -// Generate scene on_event handlers array -#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, -bool (*const subbrute_on_event_handlers[])(void* context, SceneManagerEvent event) = { -#include "subbrute_scene_config.h" -}; -#undef ADD_SCENE - -// Generate scene on_exit handlers array -#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, -void (*const subbrute_on_exit_handlers[])(void* context) = { -#include "subbrute_scene_config.h" -}; -#undef ADD_SCENE - -// Initialize scene handlers configuration structure -const SceneManagerHandlers subbrute_scene_handlers = { - .on_enter_handlers = subbrute_on_enter_handlers, - .on_event_handlers = subbrute_on_event_handlers, - .on_exit_handlers = subbrute_on_exit_handlers, - .scene_num = SubBruteSceneNum, -}; diff --git a/applications/plugins/subbrute/subbrute.c b/applications/plugins/subbrute/subbrute.c deleted file mode 100644 index aaab3aeb1..000000000 --- a/applications/plugins/subbrute/subbrute.c +++ /dev/null @@ -1,188 +0,0 @@ -#include "subbrute_i.h" -#include "subbrute_custom_event.h" -#include "scenes/subbrute_scene.h" - -#define TAG "SubBruteApp" - -static bool subbrute_custom_event_callback(void* context, uint32_t event) { - furi_assert(context); - SubBruteState* instance = context; - return scene_manager_handle_custom_event(instance->scene_manager, event); -} - -static bool subbrute_back_event_callback(void* context) { - furi_assert(context); - SubBruteState* instance = context; - return scene_manager_handle_back_event(instance->scene_manager); -} - -static void subbrute_tick_event_callback(void* context) { - furi_assert(context); - SubBruteState* instance = context; - scene_manager_handle_tick_event(instance->scene_manager); -} - -SubBruteState* subbrute_alloc() { - SubBruteState* instance = malloc(sizeof(SubBruteState)); - - memset(instance->text_store, 0, sizeof(instance->text_store)); - instance->file_path = furi_string_alloc(); - - instance->scene_manager = scene_manager_alloc(&subbrute_scene_handlers, instance); - instance->view_dispatcher = view_dispatcher_alloc(); - - instance->gui = furi_record_open(RECORD_GUI); - - view_dispatcher_enable_queue(instance->view_dispatcher); - view_dispatcher_set_event_callback_context(instance->view_dispatcher, instance); - view_dispatcher_set_custom_event_callback( - instance->view_dispatcher, subbrute_custom_event_callback); - view_dispatcher_set_navigation_event_callback( - instance->view_dispatcher, subbrute_back_event_callback); - view_dispatcher_set_tick_event_callback( - instance->view_dispatcher, subbrute_tick_event_callback, 100); - - //Dialog - instance->dialogs = furi_record_open(RECORD_DIALOGS); - - // Notifications - instance->notifications = furi_record_open(RECORD_NOTIFICATION); - - // Devices - instance->device = subbrute_device_alloc(); - - // SubBruteWorker - instance->worker = subbrute_worker_alloc(); - - // TextInput - instance->text_input = text_input_alloc(); - view_dispatcher_add_view( - instance->view_dispatcher, - SubBruteViewTextInput, - text_input_get_view(instance->text_input)); - - // Custom Widget - instance->widget = widget_alloc(); - view_dispatcher_add_view( - instance->view_dispatcher, SubBruteViewWidget, widget_get_view(instance->widget)); - - // Popup - instance->popup = popup_alloc(); - view_dispatcher_add_view( - instance->view_dispatcher, SubBruteViewPopup, popup_get_view(instance->popup)); - - // ViewStack - instance->view_stack = view_stack_alloc(); - view_dispatcher_add_view( - instance->view_dispatcher, SubBruteViewStack, view_stack_get_view(instance->view_stack)); - - // SubBruteMainView - instance->view_main = subbrute_main_view_alloc(); - view_dispatcher_add_view( - instance->view_dispatcher, - SubBruteViewMain, - subbrute_main_view_get_view(instance->view_main)); - - // SubBruteAttackView - instance->view_attack = subbrute_attack_view_alloc(); - view_dispatcher_add_view( - instance->view_dispatcher, - SubBruteViewAttack, - subbrute_attack_view_get_view(instance->view_attack)); - - //instance->flipper_format = flipper_format_string_alloc(); - //instance->environment = subghz_environment_alloc(); - - return instance; -} - -void subbrute_free(SubBruteState* instance) { - furi_assert(instance); - - // SubBruteWorker - subbrute_worker_stop(instance->worker); - subbrute_worker_free(instance->worker); - - // SubBruteDevice - subbrute_device_free(instance->device); - - // Notifications - notification_message(instance->notifications, &sequence_blink_stop); - furi_record_close(RECORD_NOTIFICATION); - instance->notifications = NULL; - - // View Main - view_dispatcher_remove_view(instance->view_dispatcher, SubBruteViewMain); - subbrute_main_view_free(instance->view_main); - - // View Attack - view_dispatcher_remove_view(instance->view_dispatcher, SubBruteViewAttack); - subbrute_attack_view_free(instance->view_attack); - - // TextInput - view_dispatcher_remove_view(instance->view_dispatcher, SubBruteViewTextInput); - text_input_free(instance->text_input); - - // Custom Widget - view_dispatcher_remove_view(instance->view_dispatcher, SubBruteViewWidget); - widget_free(instance->widget); - - // Popup - view_dispatcher_remove_view(instance->view_dispatcher, SubBruteViewPopup); - popup_free(instance->popup); - - // ViewStack - view_dispatcher_remove_view(instance->view_dispatcher, SubBruteViewStack); - view_stack_free(instance->view_stack); - - //Dialog - furi_record_close(RECORD_DIALOGS); - instance->dialogs = NULL; - - // Scene manager - scene_manager_free(instance->scene_manager); - - // View Dispatcher - view_dispatcher_free(instance->view_dispatcher); - - // GUI - furi_record_close(RECORD_GUI); - instance->gui = NULL; - - furi_string_free(instance->file_path); - - // The rest - free(instance); -} - -void subbrute_text_input_callback(void* context) { - furi_assert(context); - SubBruteState* instance = context; - view_dispatcher_send_custom_event( - instance->view_dispatcher, SubBruteCustomEventTypeTextEditDone); -} - -void subbrute_popup_closed_callback(void* context) { - furi_assert(context); - SubBruteState* instance = context; - view_dispatcher_send_custom_event( - instance->view_dispatcher, SubBruteCustomEventTypePopupClosed); -} - -// ENTRYPOINT -int32_t subbrute_app(void* p) { - UNUSED(p); - - SubBruteState* instance = subbrute_alloc(); - view_dispatcher_attach_to_gui( - instance->view_dispatcher, instance->gui, ViewDispatcherTypeFullscreen); - scene_manager_next_scene(instance->scene_manager, SubBruteSceneStart); - - furi_hal_power_suppress_charge_enter(); - notification_message(instance->notifications, &sequence_display_backlight_on); - view_dispatcher_run(instance->view_dispatcher); - furi_hal_power_suppress_charge_exit(); - subbrute_free(instance); - - return 0; -} \ No newline at end of file diff --git a/applications/plugins/subbrute/subbrute.h b/applications/plugins/subbrute/subbrute.h deleted file mode 100644 index 5fedb9158..000000000 --- a/applications/plugins/subbrute/subbrute.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -typedef struct SubBruteState SubBruteState; \ No newline at end of file diff --git a/applications/plugins/subbrute/subbrute_custom_event.h b/applications/plugins/subbrute/subbrute_custom_event.h deleted file mode 100644 index 2864f8934..000000000 --- a/applications/plugins/subbrute/subbrute_custom_event.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -typedef enum { - // Reserve first 100 events for button types and indexes, starting from 0 - SubBruteCustomEventTypeReserved = 100, - - SubBruteCustomEventTypeBackPressed, - SubBruteCustomEventTypeIndexSelected, - SubBruteCustomEventTypeTransmitStarted, - SubBruteCustomEventTypeError, - SubBruteCustomEventTypeTransmitFinished, - SubBruteCustomEventTypeTransmitNotStarted, - SubBruteCustomEventTypeTransmitCustom, - SubBruteCustomEventTypeSaveFile, - SubBruteCustomEventTypeUpdateView, - SubBruteCustomEventTypeChangeStepUp, - SubBruteCustomEventTypeChangeStepDown, - SubBruteCustomEventTypeChangeStepUpMore, - SubBruteCustomEventTypeChangeStepDownMore, - - SubBruteCustomEventTypeMenuSelected, - SubBruteCustomEventTypeTextEditDone, - SubBruteCustomEventTypePopupClosed, - - SubBruteCustomEventTypeLoadFile, -} SubBruteCustomEvent; \ No newline at end of file diff --git a/applications/plugins/subbrute/subbrute_device.c b/applications/plugins/subbrute/subbrute_device.c deleted file mode 100644 index 0971c380e..000000000 --- a/applications/plugins/subbrute/subbrute_device.c +++ /dev/null @@ -1,460 +0,0 @@ -#include "subbrute_device.h" - -#include -#include -#include -#include -#include -#include - -#define TAG "SubBruteDevice" - -SubBruteDevice* subbrute_device_alloc() { - SubBruteDevice* instance = malloc(sizeof(SubBruteDevice)); - - instance->current_step = 0; - - instance->protocol_info = NULL; - instance->file_protocol_info = NULL; - instance->decoder_result = NULL; - instance->receiver = NULL; - instance->environment = subghz_environment_alloc(); - subghz_environment_set_protocol_registry( - instance->environment, (void*)&subghz_protocol_registry); - -#ifdef FURI_DEBUG - subbrute_device_attack_set_default_values(instance, SubBruteAttackLoadFile); -#else - subbrute_device_attack_set_default_values(instance, SubBruteAttackCAME12bit433); -#endif - return instance; -} - -void subbrute_device_free(SubBruteDevice* instance) { - furi_assert(instance); - - // I don't know how to free this - instance->decoder_result = NULL; - - if(instance->receiver != NULL) { - subghz_receiver_free(instance->receiver); - instance->receiver = NULL; - } - - subghz_environment_free(instance->environment); - instance->environment = NULL; - - subbrute_device_free_protocol_info(instance); - - free(instance); -} - -uint64_t subbrute_device_add_step(SubBruteDevice* instance, int8_t step) { - if(step > 0) { - if((instance->current_step + step) - instance->max_value == 1) { - instance->current_step = 0x00; - } else { - uint64_t value = instance->current_step + step; - if(value == instance->max_value) { - instance->current_step = value; - } else { - instance->current_step = value % instance->max_value; - } - } - } else { - if(instance->current_step + step == 0) { - instance->current_step = 0x00; - } else if(instance->current_step == 0) { - instance->current_step = instance->max_value; - } else { - uint64_t value = ((instance->current_step + step) + instance->max_value); - if(value == instance->max_value) { - instance->current_step = value; - } else { - instance->current_step = value % instance->max_value; - } - } - } - - return instance->current_step; -} - -bool subbrute_device_save_file(SubBruteDevice* instance, const char* dev_file_name) { - furi_assert(instance); - -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "subbrute_device_save_file: %s", dev_file_name); -#endif - - Storage* storage = furi_record_open(RECORD_STORAGE); - FlipperFormat* file = flipper_format_file_alloc(storage); - bool result = false; - do { - if(!flipper_format_file_open_always(file, dev_file_name)) { - FURI_LOG_E(TAG, "Failed to open file: %s", dev_file_name); - break; - } - Stream* stream = flipper_format_get_raw_stream(file); - if(instance->attack == SubBruteAttackLoadFile) { - subbrute_protocol_file_generate_file( - stream, - instance->file_protocol_info->frequency, - instance->file_protocol_info->preset, - instance->file_protocol_info->file, - instance->current_step, - instance->file_protocol_info->bits, - instance->file_protocol_info->te, - instance->file_protocol_info->repeat, - instance->bit_index, - instance->key_from_file, - instance->two_bytes); - } else { - subbrute_protocol_default_generate_file( - stream, - instance->protocol_info->frequency, - instance->protocol_info->preset, - instance->protocol_info->file, - instance->current_step, - instance->protocol_info->bits, - instance->protocol_info->te, - instance->protocol_info->repeat); - } - - result = true; - } while(false); - - if(!result) { - FURI_LOG_E(TAG, "subbrute_device_save_file failed!"); - } - - flipper_format_file_close(file); - flipper_format_free(file); - furi_record_close(RECORD_STORAGE); - - return result; -} - -SubBruteFileResult subbrute_device_attack_set( - SubBruteDevice* instance, - SubBruteAttacks type, - uint8_t extra_repeats) { - furi_assert(instance); -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "subbrute_device_attack_set: %d, extra_repeats: %d", type, extra_repeats); -#endif - subbrute_device_attack_set_default_values(instance, type); - - if(type != SubBruteAttackLoadFile) { - subbrute_device_free_protocol_info(instance); - instance->protocol_info = subbrute_protocol(type); - } - - instance->extra_repeats = extra_repeats; - - // For non-file types we didn't set SubGhzProtocolDecoderBase - instance->receiver = subghz_receiver_alloc_init(instance->environment); - subghz_receiver_set_filter(instance->receiver, SubGhzProtocolFlag_Decodable); - furi_hal_subghz_reset(); - - uint8_t protocol_check_result = SubBruteFileResultProtocolNotFound; -#ifdef FURI_DEBUG - uint8_t bits; - uint32_t te; - uint8_t repeat; - FuriHalSubGhzPreset preset; - SubBruteFileProtocol file; -#endif - if(type != SubBruteAttackLoadFile) { - instance->decoder_result = subghz_receiver_search_decoder_base_by_name( - instance->receiver, subbrute_protocol_file(instance->protocol_info->file)); - - if(!instance->decoder_result || - instance->decoder_result->protocol->type == SubGhzProtocolTypeDynamic) { - FURI_LOG_E(TAG, "Can't load SubGhzProtocolDecoderBase in phase non-file decoder set"); - } else { - protocol_check_result = SubBruteFileResultOk; - - // Calc max value - instance->max_value = subbrute_protocol_calc_max_value( - instance->attack, instance->protocol_info->bits, instance->two_bytes); - } -#ifdef FURI_DEBUG - bits = instance->protocol_info->bits; - te = instance->protocol_info->te; - repeat = instance->protocol_info->repeat + instance->extra_repeats; - preset = instance->protocol_info->preset; - file = instance->protocol_info->file; -#endif - } else { - // And here we need to set preset enum - protocol_check_result = SubBruteFileResultOk; - - // Calc max value - instance->max_value = subbrute_protocol_calc_max_value( - instance->attack, instance->file_protocol_info->bits, instance->two_bytes); -#ifdef FURI_DEBUG - bits = instance->file_protocol_info->bits; - te = instance->file_protocol_info->te; - repeat = instance->file_protocol_info->repeat + instance->extra_repeats; - preset = instance->file_protocol_info->preset; - file = instance->file_protocol_info->file; -#endif - } - - subghz_receiver_free(instance->receiver); - instance->receiver = NULL; - - if(protocol_check_result != SubBruteFileResultOk) { - return SubBruteFileResultProtocolNotFound; - } - -#ifdef FURI_DEBUG - FURI_LOG_I( - TAG, - "subbrute_device_attack_set: %s, bits: %d, preset: %s, file: %s, te: %ld, repeat: %d, max_value: %lld", - subbrute_protocol_name(instance->attack), - bits, - subbrute_protocol_preset(preset), - subbrute_protocol_file(file), - te, - repeat, - instance->max_value); -#endif - - return SubBruteFileResultOk; -} - -uint8_t subbrute_device_load_from_file(SubBruteDevice* instance, const char* file_path) { - furi_assert(instance); -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "subbrute_device_load_from_file: %s", file_path); -#endif - SubBruteFileResult result = SubBruteFileResultUnknown; - - Storage* storage = furi_record_open(RECORD_STORAGE); - FlipperFormat* fff_data_file = flipper_format_file_alloc(storage); - - subbrute_device_free_protocol_info(instance); - instance->file_protocol_info = malloc(sizeof(SubBruteProtocol)); - - FuriString* temp_str; - temp_str = furi_string_alloc(); - uint32_t temp_data32; - - instance->receiver = subghz_receiver_alloc_init(instance->environment); - subghz_receiver_set_filter(instance->receiver, SubGhzProtocolFlag_Decodable); - furi_hal_subghz_reset(); - - do { - if(!flipper_format_file_open_existing(fff_data_file, file_path)) { - FURI_LOG_E(TAG, "Error open file %s", file_path); - result = SubBruteFileResultErrorOpenFile; - break; - } - if(!flipper_format_read_header(fff_data_file, temp_str, &temp_data32)) { - FURI_LOG_E(TAG, "Missing or incorrect header"); - result = SubBruteFileResultMissingOrIncorrectHeader; - break; - } - - // Frequency - if(!flipper_format_read_uint32(fff_data_file, "Frequency", &temp_data32, 1)) { - FURI_LOG_E(TAG, "Missing or incorrect Frequency"); - result = SubBruteFileResultMissingOrIncorrectFrequency; - break; - } - instance->file_protocol_info->frequency = temp_data32; - if(!furi_hal_subghz_is_tx_allowed(instance->file_protocol_info->frequency)) { - result = SubBruteFileResultFrequencyNotAllowed; - break; - } - - // Preset - if(!flipper_format_read_string(fff_data_file, "Preset", temp_str)) { - FURI_LOG_E(TAG, "Preset FAIL"); - result = SubBruteFileResultPresetInvalid; - break; - } - instance->file_protocol_info->preset = subbrute_protocol_convert_preset(temp_str); - - const char* protocol_file = NULL; - // Protocol - if(!flipper_format_read_string(fff_data_file, "Protocol", temp_str)) { - FURI_LOG_E(TAG, "Missing Protocol"); - result = SubBruteFileResultMissingProtocol; - break; - } - instance->file_protocol_info->file = subbrute_protocol_file_protocol_name(temp_str); - protocol_file = subbrute_protocol_file(instance->file_protocol_info->file); -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "Protocol: %s", protocol_file); -#endif - - instance->decoder_result = subghz_receiver_search_decoder_base_by_name( - instance->receiver, furi_string_get_cstr(temp_str)); - - if((!instance->decoder_result) || (strcmp(protocol_file, "RAW") == 0) || - (strcmp(protocol_file, "Unknown") == 0)) { - FURI_LOG_E(TAG, "Protocol unsupported"); - result = SubBruteFileResultProtocolNotSupported; - break; - } - - if(instance->decoder_result->protocol->type == SubGhzProtocolTypeDynamic) { - FURI_LOG_E(TAG, "Protocol is dynamic - not supported"); - result = SubBruteFileResultDynamicProtocolNotValid; - break; - } -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "Decoder: %s", instance->decoder_result->protocol->name); -#endif - - // Bit - if(!flipper_format_read_uint32(fff_data_file, "Bit", &temp_data32, 1)) { - FURI_LOG_E(TAG, "Missing or incorrect Bit"); - result = SubBruteFileResultMissingOrIncorrectBit; - break; - } - instance->file_protocol_info->bits = temp_data32; -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "Bit: %d", instance->file_protocol_info->bits); -#endif - - uint8_t key_data[sizeof(uint64_t)] = {0}; - if(!flipper_format_read_hex(fff_data_file, "Key", key_data, sizeof(uint64_t))) { - FURI_LOG_E(TAG, "Missing Key"); - result = SubBruteFileResultMissingOrIncorrectKey; - break; - } - uint64_t data = 0; - for(uint8_t i = 0; i < sizeof(uint64_t); i++) { - data = (data << 8) | key_data[i]; - } -#if FURI_DEBUG - FURI_LOG_D(TAG, "Key: %.16llX", data); -#endif - instance->key_from_file = data; - - // TE - if(!flipper_format_read_uint32(fff_data_file, "TE", &temp_data32, 1)) { - FURI_LOG_E(TAG, "Missing or incorrect TE"); - //result = SubBruteFileResultMissingOrIncorrectTe; - //break; - } else { - instance->file_protocol_info->te = temp_data32 != 0 ? temp_data32 : 0; - } - - // Repeat - if(flipper_format_read_uint32(fff_data_file, "Repeat", &temp_data32, 1)) { -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "Repeat: %ld", temp_data32); -#endif - instance->file_protocol_info->repeat = (uint8_t)temp_data32; - } else { -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "Repeat: 3 (default)"); -#endif - instance->file_protocol_info->repeat = 3; - } - - result = SubBruteFileResultOk; - } while(0); - - furi_string_free(temp_str); - flipper_format_file_close(fff_data_file); - flipper_format_free(fff_data_file); - furi_record_close(RECORD_STORAGE); - - subghz_receiver_free(instance->receiver); - - instance->decoder_result = NULL; - instance->receiver = NULL; - - if(result == SubBruteFileResultOk) { -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "Loaded successfully"); -#endif - } else { - subbrute_device_free_protocol_info(instance); - } - - return result; -} - -void subbrute_device_attack_set_default_values( - SubBruteDevice* instance, - SubBruteAttacks default_attack) { - furi_assert(instance); -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "subbrute_device_attack_set_default_values"); -#endif - instance->attack = default_attack; - instance->current_step = 0x00; - instance->bit_index = 0x00; - instance->extra_repeats = 0; - instance->two_bytes = false; - - if(default_attack != SubBruteAttackLoadFile) { - instance->max_value = subbrute_protocol_calc_max_value( - instance->attack, instance->bit_index, instance->two_bytes); - } -} - -const char* subbrute_device_error_get_desc(SubBruteFileResult error_id) { - const char* result; - switch(error_id) { - case(SubBruteFileResultOk): - result = "OK"; - break; - case(SubBruteFileResultErrorOpenFile): - result = "invalid name/path"; - break; - case(SubBruteFileResultMissingOrIncorrectHeader): - result = "Missing or incorrect header"; - break; - case(SubBruteFileResultFrequencyNotAllowed): - result = "Invalid frequency!"; - break; - case(SubBruteFileResultMissingOrIncorrectFrequency): - result = "Missing or incorrect Frequency"; - break; - case(SubBruteFileResultPresetInvalid): - result = "Preset FAIL"; - break; - case(SubBruteFileResultMissingProtocol): - result = "Missing Protocol"; - break; - case(SubBruteFileResultProtocolNotSupported): - result = "Protocol unsupported"; - break; - case(SubBruteFileResultDynamicProtocolNotValid): - result = "Dynamic protocol unsupported"; - break; - case(SubBruteFileResultProtocolNotFound): - result = "Protocol not found"; - break; - case(SubBruteFileResultMissingOrIncorrectBit): - result = "Missing or incorrect Bit"; - break; - case(SubBruteFileResultMissingOrIncorrectKey): - result = "Missing or incorrect Key"; - break; - case(SubBruteFileResultMissingOrIncorrectTe): - result = "Missing or incorrect TE"; - break; - case SubBruteFileResultUnknown: - default: - result = "Unknown error"; - break; - } - return result; -} - -void subbrute_device_free_protocol_info(SubBruteDevice* instance) { - furi_assert(instance); - instance->protocol_info = NULL; - if(instance->file_protocol_info) { - free(instance->file_protocol_info); - } - instance->file_protocol_info = NULL; -} \ No newline at end of file diff --git a/applications/plugins/subbrute/subbrute_device.h b/applications/plugins/subbrute/subbrute_device.h deleted file mode 100644 index 7ff650e93..000000000 --- a/applications/plugins/subbrute/subbrute_device.h +++ /dev/null @@ -1,75 +0,0 @@ -#pragma once - -#include "subbrute_protocols.h" -#include -#include -#include -#include - -#define SUBBRUTE_TEXT_STORE_SIZE 256 - -#define SUBBRUTE_MAX_LEN_NAME 64 -#define SUBBRUTE_PATH EXT_PATH("subghz") -#define SUBBRUTE_FILE_EXT ".sub" - -#define SUBBRUTE_PAYLOAD_SIZE 16 - -typedef enum { - SubBruteFileResultUnknown, - SubBruteFileResultOk, - SubBruteFileResultErrorOpenFile, - SubBruteFileResultMissingOrIncorrectHeader, - SubBruteFileResultFrequencyNotAllowed, - SubBruteFileResultMissingOrIncorrectFrequency, - SubBruteFileResultPresetInvalid, - SubBruteFileResultMissingProtocol, - SubBruteFileResultProtocolNotSupported, - SubBruteFileResultDynamicProtocolNotValid, - SubBruteFileResultProtocolNotFound, - SubBruteFileResultMissingOrIncorrectBit, - SubBruteFileResultMissingOrIncorrectKey, - SubBruteFileResultMissingOrIncorrectTe, -} SubBruteFileResult; - -typedef struct { - const SubBruteProtocol* protocol_info; - SubBruteProtocol* file_protocol_info; - - // Current step - uint64_t current_step; - - // SubGhz - SubGhzReceiver* receiver; - SubGhzProtocolDecoderBase* decoder_result; - SubGhzEnvironment* environment; - - // Attack state - SubBruteAttacks attack; - uint64_t max_value; - uint8_t extra_repeats; - - // Loaded info for attack type - uint64_t key_from_file; - uint64_t current_key_from_file; - bool two_bytes; - // Index of group to bruteforce in loaded file - uint8_t bit_index; -} SubBruteDevice; - -SubBruteDevice* subbrute_device_alloc(); -void subbrute_device_free(SubBruteDevice* instance); - -bool subbrute_device_save_file(SubBruteDevice* instance, const char* key_name); -const char* subbrute_device_error_get_desc(SubBruteFileResult error_id); -SubBruteFileResult subbrute_device_attack_set( - SubBruteDevice* context, - SubBruteAttacks type, - uint8_t extra_repeats); -uint8_t subbrute_device_load_from_file(SubBruteDevice* context, const char* file_path); - -uint64_t subbrute_device_add_step(SubBruteDevice* instance, int8_t step); - -void subbrute_device_free_protocol_info(SubBruteDevice* instance); -void subbrute_device_attack_set_default_values( - SubBruteDevice* context, - SubBruteAttacks default_attack); \ No newline at end of file diff --git a/applications/plugins/subbrute/subbrute_i.h b/applications/plugins/subbrute/subbrute_i.h deleted file mode 100644 index eff9a9075..000000000 --- a/applications/plugins/subbrute/subbrute_i.h +++ /dev/null @@ -1,80 +0,0 @@ -#pragma once - -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "SubGHz_Bruteforcer_icons.h" - -#include - -#include -#include - -#include "subbrute.h" -#include "subbrute_device.h" -#include "helpers/subbrute_worker.h" -#include "views/subbrute_attack_view.h" -#include "views/subbrute_main_view.h" - -#define SUBBRUTEFORCER_VER "Sub-GHz BruteForcer 3.4" - -#ifdef FURI_DEBUG -//#define SUBBRUTE_FAST_TRACK false -#endif - -typedef enum { - SubBruteViewNone, - SubBruteViewMain, - SubBruteViewAttack, - SubBruteViewTextInput, - SubBruteViewDialogEx, - SubBruteViewPopup, - SubBruteViewWidget, - SubBruteViewStack, -} SubBruteView; - -struct SubBruteState { - // GUI elements - NotificationApp* notifications; - Gui* gui; - ViewDispatcher* view_dispatcher; - ViewStack* view_stack; - TextInput* text_input; - Popup* popup; - Widget* widget; - DialogsApp* dialogs; - - // Text store - char text_store[SUBBRUTE_MAX_LEN_NAME]; - FuriString* file_path; - - // Views - SubBruteMainView* view_main; - SubBruteAttackView* view_attack; - SubBruteView current_view; - - // Scene - SceneManager* scene_manager; - - // SubBruteDevice - SubBruteDevice* device; - // SubBruteWorker - SubBruteWorker* worker; -}; - -void subbrute_show_loading_popup(void* context, bool show); -void subbrute_text_input_callback(void* context); -void subbrute_popup_closed_callback(void* context); \ No newline at end of file diff --git a/applications/plugins/subbrute/subbrute_protocols.c b/applications/plugins/subbrute/subbrute_protocols.c deleted file mode 100644 index d3a4826b8..000000000 --- a/applications/plugins/subbrute/subbrute_protocols.c +++ /dev/null @@ -1,815 +0,0 @@ -#include "subbrute_protocols.h" -#include "math.h" -#include - -#define TAG "SubBruteProtocols" - -/** - * CAME 12bit 303MHz - */ -const SubBruteProtocol subbrute_protocol_came_12bit_303 = { - .frequency = 303875000, - .bits = 12, - .te = 0, - .repeat = 3, - .preset = FuriHalSubGhzPresetOok650Async, - .file = CAMEFileProtocol}; - -/** - * CAME 12bit 307MHz - */ -const SubBruteProtocol subbrute_protocol_came_12bit_307 = { - .frequency = 307800000, - .bits = 12, - .te = 0, - .repeat = 3, - .preset = FuriHalSubGhzPresetOok650Async, - .file = CAMEFileProtocol}; - -/** - * CAME 12bit 315MHz - */ -const SubBruteProtocol subbrute_protocol_came_12bit_315 = { - .frequency = 315000000, - .bits = 12, - .te = 0, - .repeat = 3, - .preset = FuriHalSubGhzPresetOok650Async, - .file = CAMEFileProtocol}; - -/** - * CAME 12bit 433MHz - */ -const SubBruteProtocol subbrute_protocol_came_12bit_433 = { - .frequency = 433920000, - .bits = 12, - .te = 0, - .repeat = 3, - .preset = FuriHalSubGhzPresetOok650Async, - .file = CAMEFileProtocol}; - -/** - * CAME 12bit 868MHz - */ -const SubBruteProtocol subbrute_protocol_came_12bit_868 = { - .frequency = 868350000, - .bits = 12, - .te = 0, - .repeat = 3, - .preset = FuriHalSubGhzPresetOok650Async, - .file = CAMEFileProtocol}; - -/** - * NICE 12bit 433MHz - */ -const SubBruteProtocol subbrute_protocol_nice_12bit_433 = { - .frequency = 433920000, - .bits = 12, - .te = 0, - .repeat = 3, - .preset = FuriHalSubGhzPresetOok650Async, - .file = NICEFileProtocol}; - -/** - * NICE 12bit 868MHz - */ -const SubBruteProtocol subbrute_protocol_nice_12bit_868 = { - .frequency = 868350000, - .bits = 12, - .te = 0, - .repeat = 3, - .preset = FuriHalSubGhzPresetOok650Async, - .file = NICEFileProtocol}; - -/** - * Ansonic 12bit 433.075MHz - */ -const SubBruteProtocol subbrute_protocol_ansonic_12bit_433075 = { - .frequency = 433075000, - .bits = 12, - .te = 0, - .repeat = 3, - .preset = FuriHalSubGhzPreset2FSKDev238Async, - .file = AnsonicFileProtocol}; - -/** - * Ansonic 12bit 433.92MHz - */ -const SubBruteProtocol subbrute_protocol_ansonic_12bit_433 = { - .frequency = 433920000, - .bits = 12, - .te = 0, - .repeat = 3, - .preset = FuriHalSubGhzPreset2FSKDev238Async, - .file = AnsonicFileProtocol}; - -/** - * Ansonic 12bit 434.075MHz - */ -const SubBruteProtocol subbrute_protocol_ansonic_12bit_434 = { - .frequency = 434075000, - .bits = 12, - .te = 0, - .repeat = 3, - .preset = FuriHalSubGhzPreset2FSKDev238Async, - .file = AnsonicFileProtocol}; - -/** - * Chamberlain 9bit 300MHz - */ -const SubBruteProtocol subbrute_protocol_chamberlain_9bit_300 = { - .frequency = 300000000, - .bits = 9, - .te = 0, - .repeat = 3, - .preset = FuriHalSubGhzPresetOok650Async, - .file = ChamberlainFileProtocol}; - -/** - * Chamberlain 9bit 315MHz - */ -const SubBruteProtocol subbrute_protocol_chamberlain_9bit_315 = { - .frequency = 315000000, - .bits = 9, - .te = 0, - .repeat = 3, - .preset = FuriHalSubGhzPresetOok650Async, - .file = ChamberlainFileProtocol}; - -/** - * Chamberlain 9bit 390MHz - */ -const SubBruteProtocol subbrute_protocol_chamberlain_9bit_390 = { - .frequency = 390000000, - .bits = 9, - .te = 0, - .repeat = 3, - .preset = FuriHalSubGhzPresetOok650Async, - .file = ChamberlainFileProtocol}; - -/** - * Chamberlain 9bit 433MHz - */ -const SubBruteProtocol subbrute_protocol_chamberlain_9bit_433 = { - .frequency = 433920000, - .bits = 9, - .te = 0, - .repeat = 3, - .preset = FuriHalSubGhzPresetOok650Async, - .file = ChamberlainFileProtocol}; - -/** - * Chamberlain 8bit 300MHz - */ -const SubBruteProtocol subbrute_protocol_chamberlain_8bit_300 = { - .frequency = 300000000, - .bits = 8, - .te = 0, - .repeat = 3, - .preset = FuriHalSubGhzPresetOok650Async, - .file = ChamberlainFileProtocol}; - -/** - * Chamberlain 8bit 315MHz - */ -const SubBruteProtocol subbrute_protocol_chamberlain_8bit_315 = { - .frequency = 315000000, - .bits = 8, - .te = 0, - .repeat = 3, - .preset = FuriHalSubGhzPresetOok650Async, - .file = ChamberlainFileProtocol}; - -/** - * Chamberlain 8bit 390MHz - */ -const SubBruteProtocol subbrute_protocol_chamberlain_8bit_390 = { - .frequency = 390000000, - .bits = 8, - .te = 0, - .repeat = 3, - .preset = FuriHalSubGhzPresetOok650Async, - .file = ChamberlainFileProtocol}; - -/** - * Chamberlain 7bit 300MHz - */ -const SubBruteProtocol subbrute_protocol_chamberlain_7bit_300 = { - .frequency = 300000000, - .bits = 7, - .te = 0, - .repeat = 3, - .preset = FuriHalSubGhzPresetOok650Async, - .file = ChamberlainFileProtocol}; - -/** - * Chamberlain 7bit 315MHz - */ -const SubBruteProtocol subbrute_protocol_chamberlain_7bit_315 = { - .frequency = 315000000, - .bits = 7, - .te = 0, - .repeat = 3, - .preset = FuriHalSubGhzPresetOok650Async, - .file = ChamberlainFileProtocol}; - -/** - * Chamberlain 7bit 390MHz - */ -const SubBruteProtocol subbrute_protocol_chamberlain_7bit_390 = { - .frequency = 390000000, - .bits = 7, - .te = 0, - .repeat = 3, - .preset = FuriHalSubGhzPresetOok650Async, - .file = ChamberlainFileProtocol}; - -/** - * Linear 10bit 300MHz - */ -const SubBruteProtocol subbrute_protocol_linear_10bit_300 = { - .frequency = 300000000, - .bits = 10, - .te = 0, - .repeat = 5, - .preset = FuriHalSubGhzPresetOok650Async, - .file = LinearFileProtocol}; - -/** - * Linear 10bit 310MHz - */ -const SubBruteProtocol subbrute_protocol_linear_10bit_310 = { - .frequency = 310000000, - .bits = 10, - .te = 0, - .repeat = 5, - .preset = FuriHalSubGhzPresetOok650Async, - .file = LinearFileProtocol}; - -/** - * UNILARM 24bit 330MHz - */ -const SubBruteProtocol subbrute_protocol_unilarm_24bit_330 = { - .frequency = 330000000, - .bits = 25, - .te = 209, - .repeat = 5, - .preset = FuriHalSubGhzPresetOok650Async, - .file = UNILARMFileProtocol}; - -/** - * UNILARM 24bit 433MHz - */ -const SubBruteProtocol subbrute_protocol_unilarm_24bit_433 = { - .frequency = 433920000, - .bits = 25, - .te = 209, - .repeat = 5, - .preset = FuriHalSubGhzPresetOok650Async, - .file = UNILARMFileProtocol}; - -/** - * SMC5326 24bit 330MHz - */ -const SubBruteProtocol subbrute_protocol_smc5326_24bit_330 = { - .frequency = 330000000, - .bits = 25, - .te = 320, - .repeat = 5, - .preset = FuriHalSubGhzPresetOok650Async, - .file = SMC5326FileProtocol}; - -/** - * SMC5326 24bit 433MHz - */ -const SubBruteProtocol subbrute_protocol_smc5326_24bit_433 = { - .frequency = 433920000, - .bits = 25, - .te = 320, - .repeat = 5, - .preset = FuriHalSubGhzPresetOok650Async, - .file = SMC5326FileProtocol}; - -/** - * PT2260 (Princeton) 24bit 315MHz - */ -const SubBruteProtocol subbrute_protocol_pt2260_24bit_315 = { - .frequency = 315000000, - .bits = 24, - .te = 286, - .repeat = 5, - .preset = FuriHalSubGhzPresetOok650Async, - .file = PT2260FileProtocol}; - -/** - * PT2260 (Princeton) 24bit 330MHz - */ -const SubBruteProtocol subbrute_protocol_pt2260_24bit_330 = { - .frequency = 330000000, - .bits = 24, - .te = 286, - .repeat = 5, - .preset = FuriHalSubGhzPresetOok650Async, - .file = PT2260FileProtocol}; - -/** - * PT2260 (Princeton) 24bit 390MHz - */ -const SubBruteProtocol subbrute_protocol_pt2260_24bit_390 = { - .frequency = 390000000, - .bits = 24, - .te = 286, - .repeat = 5, - .preset = FuriHalSubGhzPresetOok650Async, - .file = PT2260FileProtocol}; - -/** - * PT2260 (Princeton) 24bit 433MHz - */ -const SubBruteProtocol subbrute_protocol_pt2260_24bit_433 = { - .frequency = 433920000, - .bits = 24, - .te = 286, - .repeat = 5, - .preset = FuriHalSubGhzPresetOok650Async, - .file = PT2260FileProtocol}; - -/** - * Holtek FM 12bit 433MHz - */ -const SubBruteProtocol subbrute_protocol_holtek_12bit_433 = { - .frequency = 433920000, - .bits = 12, - .te = 204, - .repeat = 4, - .preset = FuriHalSubGhzPreset2FSKDev476Async, - .file = HoltekFileProtocol}; - -/** - * BF existing dump - */ -const SubBruteProtocol subbrute_protocol_load_file = - {0, 0, 0, 3, FuriHalSubGhzPresetOok650Async, UnknownFileProtocol}; - -static const char* subbrute_protocol_names[] = { - [SubBruteAttackCAME12bit303] = "CAME 12bit 303MHz", - [SubBruteAttackCAME12bit307] = "CAME 12bit 307MHz", - [SubBruteAttackCAME12bit315] = "CAME 12bit 315MHz", - [SubBruteAttackCAME12bit433] = "CAME 12bit 433MHz", - [SubBruteAttackCAME12bit868] = "CAME 12bit 868MHz", - [SubBruteAttackNICE12bit433] = "NICE 12bit 433MHz", - [SubBruteAttackNICE12bit868] = "NICE 12bit 868MHz", - [SubBruteAttackAnsonic12bit433075] = "Ansonic 12bit 433.07MHz", - [SubBruteAttackAnsonic12bit433] = "Ansonic 12bit 433.92MHz", - [SubBruteAttackAnsonic12bit434] = "Ansonic 12bit 434.07MHz", - [SubBruteAttackHoltek12bit433] = "Holtek FM 12bit 433MHz", - [SubBruteAttackChamberlain9bit300] = "Chamberlain 9bit 300MHz", - [SubBruteAttackChamberlain9bit315] = "Chamberlain 9bit 315MHz", - [SubBruteAttackChamberlain9bit390] = "Chamberlain 9bit 390MHz", - [SubBruteAttackChamberlain9bit433] = "Chamberlain 9bit 433MHz", - [SubBruteAttackChamberlain8bit300] = "Chamberlain 8bit 300MHz", - [SubBruteAttackChamberlain8bit315] = "Chamberlain 8bit 315MHz", - [SubBruteAttackChamberlain8bit390] = "Chamberlain 8bit 390MHz", - [SubBruteAttackChamberlain7bit300] = "Chamberlain 7bit 300MHz", - [SubBruteAttackChamberlain7bit315] = "Chamberlain 7bit 315MHz", - [SubBruteAttackChamberlain7bit390] = "Chamberlain 7bit 390MHz", - [SubBruteAttackLinear10bit300] = "Linear 10bit 300MHz", - [SubBruteAttackLinear10bit310] = "Linear 10bit 310MHz", - [SubBruteAttackUNILARM24bit330] = "UNILARM 25bit 330MHz", - [SubBruteAttackUNILARM24bit433] = "UNILARM 25bit 433MHz", - [SubBruteAttackSMC532624bit330] = "SMC5326 25bit 330MHz", - [SubBruteAttackSMC532624bit433] = "SMC5326 25bit 433MHz", - [SubBruteAttackPT226024bit315] = "PT2260 24bit 315MHz", - [SubBruteAttackPT226024bit330] = "PT2260 24bit 330MHz", - [SubBruteAttackPT226024bit390] = "PT2260 24bit 390MHz", - [SubBruteAttackPT226024bit433] = "PT2260 24bit 433MHz", - [SubBruteAttackLoadFile] = "BF existing dump", - [SubBruteAttackTotalCount] = "Total Count", -}; - -static const char* subbrute_protocol_presets[] = { - [FuriHalSubGhzPresetIDLE] = "FuriHalSubGhzPresetIDLE", - [FuriHalSubGhzPresetOok270Async] = "FuriHalSubGhzPresetOok270Async", - [FuriHalSubGhzPresetOok650Async] = "FuriHalSubGhzPresetOok650Async", - [FuriHalSubGhzPreset2FSKDev238Async] = "FuriHalSubGhzPreset2FSKDev238Async", - [FuriHalSubGhzPreset2FSKDev476Async] = "FuriHalSubGhzPreset2FSKDev476Async", - [FuriHalSubGhzPresetMSK99_97KbAsync] = "FuriHalSubGhzPresetMSK99_97KbAsync", - [FuriHalSubGhzPresetGFSK9_99KbAsync] = "FuriHalSubGhzPresetGFSK9_99KbAsync", -}; - -const SubBruteProtocol* subbrute_protocol_registry[] = { - [SubBruteAttackCAME12bit303] = &subbrute_protocol_came_12bit_303, - [SubBruteAttackCAME12bit307] = &subbrute_protocol_came_12bit_307, - [SubBruteAttackCAME12bit315] = &subbrute_protocol_came_12bit_315, - [SubBruteAttackCAME12bit433] = &subbrute_protocol_came_12bit_433, - [SubBruteAttackCAME12bit868] = &subbrute_protocol_came_12bit_868, - [SubBruteAttackNICE12bit433] = &subbrute_protocol_nice_12bit_433, - [SubBruteAttackNICE12bit868] = &subbrute_protocol_nice_12bit_868, - [SubBruteAttackAnsonic12bit433075] = &subbrute_protocol_ansonic_12bit_433075, - [SubBruteAttackAnsonic12bit433] = &subbrute_protocol_ansonic_12bit_433, - [SubBruteAttackAnsonic12bit434] = &subbrute_protocol_ansonic_12bit_434, - [SubBruteAttackHoltek12bit433] = &subbrute_protocol_holtek_12bit_433, - [SubBruteAttackChamberlain9bit300] = &subbrute_protocol_chamberlain_9bit_300, - [SubBruteAttackChamberlain9bit315] = &subbrute_protocol_chamberlain_9bit_315, - [SubBruteAttackChamberlain9bit390] = &subbrute_protocol_chamberlain_9bit_390, - [SubBruteAttackChamberlain9bit433] = &subbrute_protocol_chamberlain_9bit_433, - [SubBruteAttackChamberlain8bit300] = &subbrute_protocol_chamberlain_8bit_300, - [SubBruteAttackChamberlain8bit315] = &subbrute_protocol_chamberlain_8bit_315, - [SubBruteAttackChamberlain8bit390] = &subbrute_protocol_chamberlain_8bit_390, - [SubBruteAttackChamberlain7bit300] = &subbrute_protocol_chamberlain_7bit_300, - [SubBruteAttackChamberlain7bit315] = &subbrute_protocol_chamberlain_7bit_315, - [SubBruteAttackChamberlain7bit390] = &subbrute_protocol_chamberlain_7bit_390, - [SubBruteAttackLinear10bit300] = &subbrute_protocol_linear_10bit_300, - [SubBruteAttackLinear10bit310] = &subbrute_protocol_linear_10bit_310, - [SubBruteAttackUNILARM24bit330] = &subbrute_protocol_unilarm_24bit_330, - [SubBruteAttackUNILARM24bit433] = &subbrute_protocol_unilarm_24bit_433, - [SubBruteAttackSMC532624bit330] = &subbrute_protocol_smc5326_24bit_330, - [SubBruteAttackSMC532624bit433] = &subbrute_protocol_smc5326_24bit_433, - [SubBruteAttackPT226024bit315] = &subbrute_protocol_pt2260_24bit_315, - [SubBruteAttackPT226024bit330] = &subbrute_protocol_pt2260_24bit_330, - [SubBruteAttackPT226024bit390] = &subbrute_protocol_pt2260_24bit_390, - [SubBruteAttackPT226024bit433] = &subbrute_protocol_pt2260_24bit_433, - [SubBruteAttackLoadFile] = &subbrute_protocol_load_file}; - -static const char* subbrute_protocol_file_types[] = { - [CAMEFileProtocol] = "CAME", - [NICEFileProtocol] = "Nice FLO", - [ChamberlainFileProtocol] = "Cham_Code", - [LinearFileProtocol] = "Linear", - [PrincetonFileProtocol] = "Princeton", - [RAWFileProtocol] = "RAW", - [BETTFileProtocol] = "BETT", - [ClemsaFileProtocol] = "Clemsa", - [DoitrandFileProtocol] = "Doitrand", - [GateTXFileProtocol] = "GateTX", - [MagellanFileProtocol] = "Magellan", - [IntertechnoV3FileProtocol] = "Intertechno_V3", - [AnsonicFileProtocol] = "Ansonic", - [SMC5326FileProtocol] = "SMC5326", - [UNILARMFileProtocol] = "SMC5326", - [PT2260FileProtocol] = "Princeton", - [HoneywellFileProtocol] = "Honeywell", - [HoltekFileProtocol] = "Holtek_HT12X", - [UnknownFileProtocol] = "Unknown"}; - -/** - * Values to not use less memory for packet parse operations - */ -static const char* subbrute_key_file_start_no_tail = - "Filetype: Flipper SubGhz Key File\nVersion: 1\nFrequency: %u\nPreset: %s\nProtocol: %s\nBit: %d\nKey: %s\nRepeat: %d\n"; -static const char* subbrute_key_file_start_with_tail = - "Filetype: Flipper SubGhz Key File\nVersion: 1\nFrequency: %u\nPreset: %s\nProtocol: %s\nBit: %d\nKey: %s\nTE: %d\nRepeat: %d\n"; -static const char* subbrute_key_small_no_tail = "Bit: %d\nKey: %s\nRepeat: %d\n"; -//static const char* subbrute_key_small_raw = -// "Filetype: Flipper SubGhz Key File\nVersion: 1\nFrequency: %u\nPreset: %s\nProtocol: %s\nBit: %d\n"; -static const char* subbrute_key_small_with_tail = "Bit: %d\nKey: %s\nTE: %d\nRepeat: %d\n"; - -const char* subbrute_protocol_name(SubBruteAttacks index) { - return subbrute_protocol_names[index]; -} - -const SubBruteProtocol* subbrute_protocol(SubBruteAttacks index) { - return subbrute_protocol_registry[index]; -} - -uint8_t subbrute_protocol_repeats_count(SubBruteAttacks index) { - return subbrute_protocol_registry[index]->repeat; -} - -const char* subbrute_protocol_preset(FuriHalSubGhzPreset preset) { - return subbrute_protocol_presets[preset]; -} - -const char* subbrute_protocol_file(SubBruteFileProtocol protocol) { - return subbrute_protocol_file_types[protocol]; -} - -FuriHalSubGhzPreset subbrute_protocol_convert_preset(FuriString* preset_name) { - for(size_t i = FuriHalSubGhzPresetIDLE; i < FuriHalSubGhzPresetCustom; i++) { - if(furi_string_cmp_str(preset_name, subbrute_protocol_presets[i]) == 0) { - return i; - } - } - - return FuriHalSubGhzPresetIDLE; -} - -SubBruteFileProtocol subbrute_protocol_file_protocol_name(FuriString* name) { - for(size_t i = CAMEFileProtocol; i < TotalFileProtocol - 1; i++) { - if(furi_string_cmp_str(name, subbrute_protocol_file_types[i]) == 0) { - return i; - } - } - - return UnknownFileProtocol; -} - -void subbrute_protocol_create_candidate_for_existing_file( - FuriString* candidate, - uint64_t step, - uint8_t bit_index, - uint64_t file_key, - bool two_bytes) { - uint8_t p[8]; - for(int i = 0; i < 8; i++) { - p[i] = (uint8_t)(file_key >> 8 * (7 - i)) & 0xFF; - } - uint8_t low_byte = step & (0xff); - uint8_t high_byte = (step >> 8) & 0xff; - - size_t size = sizeof(uint64_t); - for(uint8_t i = 0; i < size; i++) { - if(i == bit_index - 1 && two_bytes) { - furi_string_cat_printf(candidate, "%02X %02X", high_byte, low_byte); - i++; - } else if(i == bit_index) { - furi_string_cat_printf(candidate, "%02X", low_byte); - } else if(p[i] != 0) { - furi_string_cat_printf(candidate, "%02X", p[i]); - } else { - furi_string_cat_printf(candidate, "%s", "00"); - } - - if(i < size - 1) { - furi_string_push_back(candidate, ' '); - } - } - -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "file candidate: %s, step: %lld", furi_string_get_cstr(candidate), step); -#endif -} - -void subbrute_protocol_create_candidate_for_default( - FuriString* candidate, - SubBruteFileProtocol file, - uint64_t step) { - uint8_t p[8]; - if(file == SMC5326FileProtocol) { - const uint8_t lut[] = {0x00, 0x02, 0x03}; // 00, 10, 11 - const uint64_t gate1 = 0x01D5; // 111010101 - //const uint8_t gate2 = 0x0175; // 101110101 - - uint64_t total = 0; - for(size_t j = 0; j < 8; j++) { - total |= lut[step % 3] << (2 * j); - double sub_step = step / 3; - step = (uint64_t)floor(sub_step); - } - total <<= 9; - total |= gate1; - - for(int i = 0; i < 8; i++) { - p[i] = (uint8_t)(total >> 8 * (7 - i)) & 0xFF; - } - } else if(file == UNILARMFileProtocol) { - const uint8_t lut[] = {0x00, 0x02, 0x03}; // 00, 10, 11 - const uint64_t gate1 = 3 << 7; - //const uint8_t gate2 = 3 << 5; - - uint64_t total = 0; - for(size_t j = 0; j < 8; j++) { - total |= lut[step % 3] << (2 * j); - double sub_step = step / 3; - step = (uint64_t)floor(sub_step); - } - total <<= 9; - total |= gate1; - - for(int i = 0; i < 8; i++) { - p[i] = (uint8_t)(total >> 8 * (7 - i)) & 0xFF; - } - } else if(file == PT2260FileProtocol) { - const uint8_t lut[] = {0x00, 0x01, 0x03}; // 00, 01, 11 - const uint64_t button_open = 0x03; // 11 - //const uint8_t button_lock = 0x0C; // 1100 - //const uint8_t button_stop = 0x30; // 110000 - //const uint8_t button_close = 0xC0; // 11000000 - - uint64_t total = 0; - for(size_t j = 0; j < 8; j++) { - total |= lut[step % 3] << (2 * j); - double sub_step = step / 3; - step = (uint64_t)floor(sub_step); - } - total <<= 8; - total |= button_open; - - for(int i = 0; i < 8; i++) { - p[i] = (uint8_t)(total >> 8 * (7 - i)) & 0xFF; - } - } else { - for(int i = 0; i < 8; i++) { - p[i] = (uint8_t)(step >> 8 * (7 - i)) & 0xFF; - } - } - - size_t size = sizeof(uint64_t); - for(uint8_t i = 0; i < size; i++) { - if(p[i] != 0) { - furi_string_cat_printf(candidate, "%02X", p[i]); - } else { - furi_string_cat_printf(candidate, "%s", "00"); - } - - if(i < size - 1) { - furi_string_push_back(candidate, ' '); - } - } - -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "candidate: %s, step: %lld", furi_string_get_cstr(candidate), step); -#endif -} - -void subbrute_protocol_default_payload( - Stream* stream, - SubBruteFileProtocol file, - uint64_t step, - uint8_t bits, - uint32_t te, - uint8_t repeat) { - FuriString* candidate = furi_string_alloc(); - subbrute_protocol_create_candidate_for_default(candidate, file, step); - -#ifdef FURI_DEBUG - FURI_LOG_D( - TAG, - "candidate: %s, step: %lld, repeat: %d, te: %s", - furi_string_get_cstr(candidate), - step, - repeat, - te ? "true" : "false"); -#endif - stream_clean(stream); - if(te) { - stream_write_format( - stream, - subbrute_key_small_with_tail, - bits, - furi_string_get_cstr(candidate), - te, - repeat); - } else { - stream_write_format( - stream, subbrute_key_small_no_tail, bits, furi_string_get_cstr(candidate), repeat); - } - - furi_string_free(candidate); -} - -void subbrute_protocol_file_payload( - Stream* stream, - uint64_t step, - uint8_t bits, - uint32_t te, - uint8_t repeat, - uint8_t bit_index, - uint64_t file_key, - bool two_bytes) { - FuriString* candidate = furi_string_alloc(); - subbrute_protocol_create_candidate_for_existing_file( - candidate, step, bit_index, file_key, two_bytes); - -#ifdef FURI_DEBUG - FURI_LOG_D( - TAG, - "candidate: %s, step: %lld, repeat: %d, te: %s", - furi_string_get_cstr(candidate), - step, - repeat, - te ? "true" : "false"); -#endif - stream_clean(stream); - - if(te) { - stream_write_format( - stream, - subbrute_key_small_with_tail, - bits, - furi_string_get_cstr(candidate), - te, - repeat); - } else { - stream_write_format( - stream, subbrute_key_small_no_tail, bits, furi_string_get_cstr(candidate), repeat); - } - - furi_string_free(candidate); -} - -void subbrute_protocol_default_generate_file( - Stream* stream, - uint32_t frequency, - FuriHalSubGhzPreset preset, - SubBruteFileProtocol file, - uint64_t step, - uint8_t bits, - uint32_t te, - uint8_t repeat) { - FuriString* candidate = furi_string_alloc(); - subbrute_protocol_create_candidate_for_default(candidate, file, step); - -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "candidate: %s, step: %lld", furi_string_get_cstr(candidate), step); -#endif - stream_clean(stream); - - if(te) { - stream_write_format( - stream, - subbrute_key_file_start_with_tail, - frequency, - subbrute_protocol_preset(preset), - subbrute_protocol_file(file), - bits, - furi_string_get_cstr(candidate), - te, - repeat); - } else { - stream_write_format( - stream, - subbrute_key_file_start_no_tail, - frequency, - subbrute_protocol_preset(preset), - subbrute_protocol_file(file), - bits, - furi_string_get_cstr(candidate), - repeat); - } - - furi_string_free(candidate); -} - -void subbrute_protocol_file_generate_file( - Stream* stream, - uint32_t frequency, - FuriHalSubGhzPreset preset, - SubBruteFileProtocol file, - uint64_t step, - uint8_t bits, - uint32_t te, - uint8_t repeat, - uint8_t bit_index, - uint64_t file_key, - bool two_bytes) { - FuriString* candidate = furi_string_alloc(); - // char subbrute_payload_byte[8]; - //furi_string_set_str(candidate, file_key); - subbrute_protocol_create_candidate_for_existing_file( - candidate, step, bit_index, file_key, two_bytes); - - stream_clean(stream); - if(te) { - stream_write_format( - stream, - subbrute_key_file_start_with_tail, - frequency, - subbrute_protocol_preset(preset), - subbrute_protocol_file(file), - bits, - furi_string_get_cstr(candidate), - te, - repeat); - } else { - stream_write_format( - stream, - subbrute_key_file_start_no_tail, - frequency, - subbrute_protocol_preset(preset), - subbrute_protocol_file(file), - bits, - furi_string_get_cstr(candidate), - repeat); - } - - furi_string_free(candidate); -} - -uint64_t - subbrute_protocol_calc_max_value(SubBruteAttacks attack_type, uint8_t bits, bool two_bytes) { - uint64_t max_value; - if(attack_type == SubBruteAttackLoadFile) { - max_value = two_bytes ? 0xFFFF : 0xFF; - } else if( - attack_type == SubBruteAttackSMC532624bit330 || - attack_type == SubBruteAttackSMC532624bit433 || - attack_type == SubBruteAttackUNILARM24bit330 || - attack_type == SubBruteAttackUNILARM24bit433 || - attack_type == SubBruteAttackPT226024bit315 || - attack_type == SubBruteAttackPT226024bit330 || - attack_type == SubBruteAttackPT226024bit390 || - attack_type == SubBruteAttackPT226024bit433) { - max_value = 6561; - } else { - FuriString* max_value_s; - max_value_s = furi_string_alloc(); - for(uint8_t i = 0; i < bits; i++) { - furi_string_cat_printf(max_value_s, "1"); - } - max_value = (uint64_t)strtol(furi_string_get_cstr(max_value_s), NULL, 2); - furi_string_free(max_value_s); - } - - return max_value; -} \ No newline at end of file diff --git a/applications/plugins/subbrute/subbrute_protocols.h b/applications/plugins/subbrute/subbrute_protocols.h deleted file mode 100644 index 0aba8140f..000000000 --- a/applications/plugins/subbrute/subbrute_protocols.h +++ /dev/null @@ -1,122 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -typedef enum { - CAMEFileProtocol, - NICEFileProtocol, - ChamberlainFileProtocol, - LinearFileProtocol, - PrincetonFileProtocol, - RAWFileProtocol, - BETTFileProtocol, - ClemsaFileProtocol, - DoitrandFileProtocol, - GateTXFileProtocol, - MagellanFileProtocol, - IntertechnoV3FileProtocol, - AnsonicFileProtocol, - SMC5326FileProtocol, - UNILARMFileProtocol, - PT2260FileProtocol, - HoneywellFileProtocol, - HoltekFileProtocol, - UnknownFileProtocol, - TotalFileProtocol, -} SubBruteFileProtocol; - -typedef enum { - SubBruteAttackCAME12bit303, - SubBruteAttackCAME12bit307, - SubBruteAttackCAME12bit315, - SubBruteAttackCAME12bit433, - SubBruteAttackCAME12bit868, - SubBruteAttackNICE12bit433, - SubBruteAttackNICE12bit868, - SubBruteAttackAnsonic12bit433075, - SubBruteAttackAnsonic12bit433, - SubBruteAttackAnsonic12bit434, - SubBruteAttackHoltek12bit433, - SubBruteAttackChamberlain9bit300, - SubBruteAttackChamberlain9bit315, - SubBruteAttackChamberlain9bit390, - SubBruteAttackChamberlain9bit433, - SubBruteAttackChamberlain8bit300, - SubBruteAttackChamberlain8bit315, - SubBruteAttackChamberlain8bit390, - SubBruteAttackChamberlain7bit300, - SubBruteAttackChamberlain7bit315, - SubBruteAttackChamberlain7bit390, - SubBruteAttackLinear10bit300, - SubBruteAttackLinear10bit310, - SubBruteAttackUNILARM24bit330, - SubBruteAttackUNILARM24bit433, - SubBruteAttackSMC532624bit330, - SubBruteAttackSMC532624bit433, - SubBruteAttackPT226024bit315, - SubBruteAttackPT226024bit330, - SubBruteAttackPT226024bit390, - SubBruteAttackPT226024bit433, - SubBruteAttackLoadFile, - SubBruteAttackTotalCount, -} SubBruteAttacks; - -typedef struct { - uint32_t frequency; - uint8_t bits; - uint32_t te; - uint8_t repeat; - FuriHalSubGhzPreset preset; - SubBruteFileProtocol file; -} SubBruteProtocol; - -const SubBruteProtocol* subbrute_protocol(SubBruteAttacks index); -const char* subbrute_protocol_preset(FuriHalSubGhzPreset preset); -const char* subbrute_protocol_file(SubBruteFileProtocol protocol); -FuriHalSubGhzPreset subbrute_protocol_convert_preset(FuriString* preset_name); -SubBruteFileProtocol subbrute_protocol_file_protocol_name(FuriString* name); -uint8_t subbrute_protocol_repeats_count(SubBruteAttacks index); -const char* subbrute_protocol_name(SubBruteAttacks index); - -void subbrute_protocol_default_payload( - Stream* stream, - SubBruteFileProtocol file, - uint64_t step, - uint8_t bits, - uint32_t te, - uint8_t repeat); -void subbrute_protocol_file_payload( - Stream* stream, - uint64_t step, - uint8_t bits, - uint32_t te, - uint8_t repeat, - uint8_t bit_index, - uint64_t file_key, - bool two_bytes); -void subbrute_protocol_default_generate_file( - Stream* stream, - uint32_t frequency, - FuriHalSubGhzPreset preset, - SubBruteFileProtocol file, - uint64_t step, - uint8_t bits, - uint32_t te, - uint8_t repeat); -void subbrute_protocol_file_generate_file( - Stream* stream, - uint32_t frequency, - FuriHalSubGhzPreset preset, - SubBruteFileProtocol file, - uint64_t step, - uint8_t bits, - uint32_t te, - uint8_t repeat, - uint8_t bit_index, - uint64_t file_key, - bool two_bytes); -uint64_t - subbrute_protocol_calc_max_value(SubBruteAttacks attack_type, uint8_t bits, bool two_bytes); \ No newline at end of file diff --git a/applications/plugins/subbrute/views/subbrute_attack_view.c b/applications/plugins/subbrute/views/subbrute_attack_view.c deleted file mode 100644 index d7770bb44..000000000 --- a/applications/plugins/subbrute/views/subbrute_attack_view.c +++ /dev/null @@ -1,341 +0,0 @@ -#include "subbrute_attack_view.h" -#include "../subbrute_i.h" -#include "../subbrute_protocols.h" -#include "../helpers/gui_top_buttons.h" - -#include -#include -#include -#include -#include - -#define TAG "SubBruteAttackView" - -struct SubBruteAttackView { - View* view; - SubBruteAttackViewCallback callback; - void* context; - SubBruteAttacks attack_type; - uint64_t max_value; - uint64_t current_step; - bool is_attacking; - uint8_t extra_repeats; -}; - -typedef struct { - SubBruteAttacks attack_type; - uint64_t max_value; - uint64_t current_step; - uint8_t extra_repeats; - bool is_attacking; - IconAnimation* icon; -} SubBruteAttackViewModel; - -void subbrute_attack_view_set_callback( - SubBruteAttackView* instance, - SubBruteAttackViewCallback callback, - void* context) { - furi_assert(instance); - furi_assert(callback); - - instance->callback = callback; - instance->context = context; -} - -bool subbrute_attack_view_input(InputEvent* event, void* context) { - furi_assert(event); - furi_assert(context); - SubBruteAttackView* instance = context; -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "InputKey: %d", event->key); -#endif - - if(event->key == InputKeyBack && event->type == InputTypeShort) { - instance->is_attacking = false; - with_view_model( - instance->view, - SubBruteAttackViewModel * model, - { model->is_attacking = false; }, - true); - - instance->callback(SubBruteCustomEventTypeBackPressed, instance->context); - return true; - } - - bool update = false; - - if(!instance->is_attacking) { - if(event->type == InputTypeShort && event->key == InputKeyOk) { -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "InputKey: %d OK", event->key); -#endif - instance->is_attacking = true; - instance->callback(SubBruteCustomEventTypeTransmitStarted, instance->context); - update = true; - } else if(event->key == InputKeyUp) { - instance->callback(SubBruteCustomEventTypeSaveFile, instance->context); - update = true; - } else if(event->key == InputKeyDown) { - instance->callback(SubBruteCustomEventTypeTransmitCustom, instance->context); - update = true; - } else if(event->type == InputTypeShort) { - if(event->key == InputKeyLeft) { - instance->callback(SubBruteCustomEventTypeChangeStepDown, instance->context); - } else if(event->key == InputKeyRight) { - instance->callback(SubBruteCustomEventTypeChangeStepUp, instance->context); - } - update = true; - } else if(event->type == InputTypeRepeat) { - if(event->key == InputKeyLeft) { - instance->callback(SubBruteCustomEventTypeChangeStepDownMore, instance->context); - } else if(event->key == InputKeyRight) { - instance->callback(SubBruteCustomEventTypeChangeStepUpMore, instance->context); - } - update = true; - } - } else { - // ATTACK Mode! - if((event->type == InputTypeShort || event->type == InputTypeRepeat) && - (event->key == InputKeyOk || event->key == InputKeyBack)) { - instance->is_attacking = false; - instance->callback(SubBruteCustomEventTypeTransmitNotStarted, instance->context); - - update = true; - } - } - - if(update) { - with_view_model( - instance->view, - SubBruteAttackViewModel * model, - { - if(model->is_attacking != instance->is_attacking) { - if(instance->is_attacking) { - icon_animation_stop(model->icon); - icon_animation_start(model->icon); - } else { - icon_animation_stop(model->icon); - } - } - - model->attack_type = instance->attack_type; - model->max_value = instance->max_value; - model->current_step = instance->current_step; - model->is_attacking = instance->is_attacking; - }, - true); - } - - return true; -} - -SubBruteAttackView* subbrute_attack_view_alloc() { - SubBruteAttackView* instance = malloc(sizeof(SubBruteAttackView)); - - instance->view = view_alloc(); - view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(SubBruteAttackViewModel)); - view_set_context(instance->view, instance); - - with_view_model( - instance->view, - SubBruteAttackViewModel * model, - { - model->icon = icon_animation_alloc(&A_Sub1ghz_14); - view_tie_icon_animation(instance->view, model->icon); - }, - true); - - view_set_draw_callback(instance->view, (ViewDrawCallback)subbrute_attack_view_draw); - view_set_input_callback(instance->view, subbrute_attack_view_input); - view_set_enter_callback(instance->view, subbrute_attack_view_enter); - view_set_exit_callback(instance->view, subbrute_attack_view_exit); - - instance->attack_type = SubBruteAttackTotalCount; - instance->max_value = 0x00; - instance->current_step = 0; - instance->is_attacking = false; - - return instance; -} - -void subbrute_attack_view_enter(void* context) { - furi_assert(context); - -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "subbrute_attack_view_enter"); -#endif -} - -void subbrute_attack_view_free(SubBruteAttackView* instance) { - furi_assert(instance); - -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "subbrute_attack_view_free"); -#endif - - with_view_model( - instance->view, - SubBruteAttackViewModel * model, - { icon_animation_free(model->icon); }, - false); - - view_free(instance->view); - free(instance); -} - -View* subbrute_attack_view_get_view(SubBruteAttackView* instance) { - furi_assert(instance); - return instance->view; -} - -void subbrute_attack_view_set_current_step(SubBruteAttackView* instance, uint64_t current_step) { - furi_assert(instance); -#ifdef FURI_DEBUG - //FURI_LOG_D(TAG, "Set step: %d", current_step); -#endif - instance->current_step = current_step; - with_view_model( - instance->view, - SubBruteAttackViewModel * model, - { model->current_step = current_step; }, - true); -} - -// We need to call init every time, because not every time we calls enter -// normally, call enter only once -void subbrute_attack_view_init_values( - SubBruteAttackView* instance, - uint8_t index, - uint64_t max_value, - uint64_t current_step, - bool is_attacking, - uint8_t extra_repeats) { -#ifdef FURI_DEBUG - FURI_LOG_I( - TAG, - "INIT, attack_type: %d, max_value: %lld, current_step: %lld, extra_repeats: %d", - index, - max_value, - current_step, - extra_repeats); -#endif - instance->attack_type = index; - instance->max_value = max_value; - instance->current_step = current_step; - instance->is_attacking = is_attacking; - instance->extra_repeats = extra_repeats; - - with_view_model( - instance->view, - SubBruteAttackViewModel * model, - { - model->max_value = max_value; - model->attack_type = index; - model->current_step = current_step; - model->is_attacking = is_attacking; - model->extra_repeats = extra_repeats; - if(is_attacking) { - icon_animation_start(model->icon); - } else { - icon_animation_stop(model->icon); - } - }, - true); -} - -void subbrute_attack_view_exit(void* context) { - furi_assert(context); - SubBruteAttackView* instance = context; -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "subbrute_attack_view_exit"); -#endif - with_view_model( - instance->view, - SubBruteAttackViewModel * model, - { icon_animation_stop(model->icon); }, - false); -} - -void subbrute_attack_view_draw(Canvas* canvas, void* context) { - furi_assert(context); - SubBruteAttackViewModel* model = (SubBruteAttackViewModel*)context; - char buffer[64]; - - const char* attack_name = NULL; - attack_name = subbrute_protocol_name(model->attack_type); - - // Title - if(model->is_attacking) { - canvas_set_color(canvas, ColorBlack); - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned(canvas, 64, 2, AlignCenter, AlignTop, attack_name); - } - - // Current Step / Max value - const uint8_t y_frequency = 17; - if(model->max_value > 9999) { - canvas_set_font(canvas, FontBigNumbers); - snprintf(buffer, sizeof(buffer), "%05d/", (int)model->current_step); - canvas_draw_str_aligned(canvas, 5, y_frequency, AlignLeft, AlignTop, buffer); - - // Second part with another font, because BigNumber is out of screen bounds - canvas_set_font(canvas, FontPrimary); - snprintf(buffer, sizeof(buffer), "%05d", (int)model->max_value); - canvas_draw_str_aligned(canvas, 107, y_frequency + 13, AlignRight, AlignBottom, buffer); - } else if(model->max_value <= 0xFF) { - canvas_set_font(canvas, FontBigNumbers); - snprintf( - buffer, sizeof(buffer), "%03d/%03d", (int)model->current_step, (int)model->max_value); - canvas_draw_str_aligned(canvas, 64, y_frequency, AlignCenter, AlignTop, buffer); - } else { - canvas_set_font(canvas, FontBigNumbers); - snprintf( - buffer, sizeof(buffer), "%04d/%04d", (int)model->current_step, (int)model->max_value); - canvas_draw_str_aligned(canvas, 64, y_frequency, AlignCenter, AlignTop, buffer); - } - canvas_set_font(canvas, FontSecondary); - - memset(buffer, 0, sizeof(buffer)); - if(!model->is_attacking) { - canvas_set_color(canvas, ColorBlack); - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned(canvas, 64, 44, AlignCenter, AlignBottom, attack_name); - - snprintf( - buffer, - sizeof(buffer), - "x%d", - model->extra_repeats + subbrute_protocol_repeats_count(model->attack_type)); - canvas_draw_str_aligned(canvas, 60, 6, AlignCenter, AlignCenter, buffer); - - elements_button_left(canvas, "-1"); - elements_button_right(canvas, "+1"); - elements_button_center(canvas, "Start"); - elements_button_top_left(canvas, "Save"); - elements_button_top_right(canvas, "Resend"); - } else { - // canvas_draw_icon_animation - const uint8_t icon_h_offset = 0; - const uint8_t icon_width_with_offset = - icon_animation_get_width(model->icon) + icon_h_offset; - const uint8_t icon_v_offset = icon_animation_get_height(model->icon); // + vertical_offset; - const uint8_t x = canvas_width(canvas); - const uint8_t y = canvas_height(canvas); - canvas_draw_icon_animation( - canvas, x - icon_width_with_offset, y - icon_v_offset, model->icon); - // Progress bar - // Resolution: 128x64 px - float progress_value = (float)model->current_step / model->max_value; - elements_progress_bar(canvas, 8, 37, 110, progress_value > 1 ? 1 : progress_value); - - snprintf( - buffer, - sizeof(buffer), - "x%d", - model->extra_repeats + subbrute_protocol_repeats_count(model->attack_type)); - canvas_draw_str(canvas, 4, y - 8, buffer); - canvas_draw_str(canvas, 4, y - 1, "repeats"); - - elements_button_center(canvas, "Stop"); - } -} diff --git a/applications/plugins/subbrute/views/subbrute_attack_view.h b/applications/plugins/subbrute/views/subbrute_attack_view.h deleted file mode 100644 index 55e3a8222..000000000 --- a/applications/plugins/subbrute/views/subbrute_attack_view.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include "../subbrute_custom_event.h" -#include -#include -#include - -typedef void (*SubBruteAttackViewCallback)(SubBruteCustomEvent event, void* context); -typedef struct SubBruteAttackView SubBruteAttackView; - -void subbrute_attack_view_set_callback( - SubBruteAttackView* instance, - SubBruteAttackViewCallback callback, - void* context); -SubBruteAttackView* subbrute_attack_view_alloc(); -void subbrute_attack_view_free(SubBruteAttackView* instance); -View* subbrute_attack_view_get_view(SubBruteAttackView* instance); -void subbrute_attack_view_set_current_step(SubBruteAttackView* instance, uint64_t current_step); -void subbrute_attack_view_init_values( - SubBruteAttackView* instance, - uint8_t index, - uint64_t max_value, - uint64_t current_step, - bool is_attacking, - uint8_t extra_repeats); \ No newline at end of file diff --git a/applications/plugins/subbrute/views/subbrute_main_view.c b/applications/plugins/subbrute/views/subbrute_main_view.c deleted file mode 100644 index c21f2ea33..000000000 --- a/applications/plugins/subbrute/views/subbrute_main_view.c +++ /dev/null @@ -1,463 +0,0 @@ -#include "subbrute_main_view.h" -#include "../subbrute_i.h" -#include "../subbrute_protocols.h" -#include "../helpers/gui_top_buttons.h" - -#include -#include -#include - -#define STATUS_BAR_Y_SHIFT 14 -#define TAG "SubBruteMainView" - -#define ITEMS_ON_SCREEN 3 -#define ITEMS_INTERVAL 1 -#define ITEM_WIDTH 14 -#define ITEM_Y 27 -#define ITEM_HEIGHT 13 -#define TEXT_X 6 -#define TEXT_Y 37 -#define TEXT_INTERVAL 3 -#define TEXT_WIDTH 12 -#define ITEM_FRAME_RADIUS 2 - -struct SubBruteMainView { - View* view; - SubBruteMainViewCallback callback; - void* context; - uint8_t index; - bool is_select_byte; - bool two_bytes; - uint64_t key_from_file; - uint8_t extra_repeats; - uint8_t window_position; -}; - -typedef struct { - uint8_t index; - uint8_t extra_repeats; - uint8_t window_position; - bool is_select_byte; - bool two_bytes; - uint64_t key_from_file; -} SubBruteMainViewModel; - -void subbrute_main_view_set_callback( - SubBruteMainView* instance, - SubBruteMainViewCallback callback, - void* context) { - furi_assert(instance); - furi_assert(callback); - - instance->callback = callback; - instance->context = context; -} - -void subbrute_main_view_center_displayed_key( - Canvas* canvas, - uint64_t key, - uint8_t index, - bool two_bytes) { - uint8_t text_x = TEXT_X; - uint8_t item_x = TEXT_X - ITEMS_INTERVAL; - canvas_set_font(canvas, FontSecondary); - - for(int i = 0; i < 8; i++) { - char current_value[3] = {0}; - uint8_t byte_value = (uint8_t)(key >> 8 * (7 - i)) & 0xFF; - snprintf(current_value, sizeof(current_value), "%02X", byte_value); - - // For two bytes we need to select prev location - if(!two_bytes && i == index) { - canvas_set_color(canvas, ColorBlack); - canvas_draw_rbox( - canvas, item_x - 1, ITEM_Y, ITEM_WIDTH + 1, ITEM_HEIGHT, ITEM_FRAME_RADIUS); - canvas_set_color(canvas, ColorWhite); - canvas_draw_str(canvas, text_x, TEXT_Y, current_value); - } else if(two_bytes && (i == index || i == index - 1)) { - if(i == index) { - canvas_set_color(canvas, ColorBlack); - canvas_draw_rbox( - canvas, - item_x - ITEMS_INTERVAL - ITEM_WIDTH - 1, - ITEM_Y, - ITEM_WIDTH * 2 + ITEMS_INTERVAL * 2 + 1, - ITEM_HEIGHT, - ITEM_FRAME_RADIUS); - - canvas_set_color(canvas, ColorWhite); - canvas_draw_str(canvas, text_x, TEXT_Y, current_value); - - // Redraw prev element with white - memset(current_value, 0, sizeof(current_value)); - byte_value = (uint8_t)(key >> 8 * (7 - i + 1)) & 0xFF; - snprintf(current_value, sizeof(current_value), "%02X", byte_value); - canvas_draw_str( - canvas, text_x - (TEXT_WIDTH + TEXT_INTERVAL), TEXT_Y, current_value); - } else { - canvas_set_color(canvas, ColorWhite); - canvas_draw_str(canvas, text_x, TEXT_Y, current_value); - } - } else { - canvas_set_color(canvas, ColorBlack); - canvas_draw_str(canvas, text_x, TEXT_Y, current_value); - } - text_x = text_x + TEXT_WIDTH + TEXT_INTERVAL; - item_x = item_x + ITEM_WIDTH + ITEMS_INTERVAL; - } - - // Return normal color - canvas_set_color(canvas, ColorBlack); -} - -void subbrute_main_view_draw(Canvas* canvas, SubBruteMainViewModel* model) { - uint16_t screen_width = canvas_width(canvas); - uint16_t screen_height = canvas_height(canvas); - - if(model->is_select_byte) { -#ifdef FURI_DEBUG - //FURI_LOG_D(TAG, "key_from_file: %s", model->key_from_file); -#endif - //char msg_index[18]; - //snprintf(msg_index, sizeof(msg_index), "Field index: %d", model->index); - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned( - canvas, 64, 17, AlignCenter, AlignTop, "Please select values to calc:"); - - subbrute_main_view_center_displayed_key( - canvas, model->key_from_file, model->index, model->two_bytes); - //const char* line = furi_string_get_cstr(menu_items); - //canvas_set_font(canvas, FontSecondary); - //canvas_draw_str_aligned( - // canvas, 64, 37, AlignCenter, AlignTop, furi_string_get_cstr(menu_items)); - - elements_button_center(canvas, "Select"); - if(model->index > 0) { - elements_button_left(canvas, " "); - } - if(model->index < 7) { - elements_button_right(canvas, " "); - } - // Switch to another mode - if(model->two_bytes) { - elements_button_top_left(canvas, "One byte"); - } else { - elements_button_top_left(canvas, "Two bytes"); - } - } else { - // Title - canvas_set_font(canvas, FontPrimary); - canvas_draw_box(canvas, 0, 0, canvas_width(canvas), STATUS_BAR_Y_SHIFT); - canvas_invert_color(canvas); - canvas_draw_str_aligned(canvas, 64, 3, AlignCenter, AlignTop, SUBBRUTEFORCER_VER); - canvas_invert_color(canvas); - - // Menu - canvas_set_color(canvas, ColorBlack); - canvas_set_font(canvas, FontSecondary); - const uint8_t item_height = 16; - -#ifdef FURI_DEBUG - //FURI_LOG_D(TAG, "window_position: %d, index: %d", model->window_position, model->index); -#endif - for(uint8_t position = 0; position < SubBruteAttackTotalCount; ++position) { - uint8_t item_position = position - model->window_position; - - if(item_position < ITEMS_ON_SCREEN) { - if(model->index == position) { - canvas_draw_str_aligned( - canvas, - 4, - 9 + (item_position * item_height) + STATUS_BAR_Y_SHIFT, - AlignLeft, - AlignCenter, - subbrute_protocol_name(position)); - - if(model->extra_repeats > 0) { - canvas_set_font(canvas, FontBatteryPercent); - char buffer[10]; - snprintf( - buffer, - sizeof(buffer), - "x%d", - model->extra_repeats + subbrute_protocol_repeats_count(model->index)); - canvas_draw_str_aligned( - canvas, - screen_width - 15, - 9 + (item_position * item_height) + STATUS_BAR_Y_SHIFT, - AlignLeft, - AlignCenter, - buffer); - canvas_set_font(canvas, FontSecondary); - } - - elements_frame( - canvas, 1, 1 + (item_position * item_height) + STATUS_BAR_Y_SHIFT, 124, 15); - } else { - canvas_draw_str_aligned( - canvas, - 4, - 9 + (item_position * item_height) + STATUS_BAR_Y_SHIFT, - AlignLeft, - AlignCenter, - subbrute_protocol_name(position)); - } - } - } - - elements_scrollbar_pos( - canvas, - screen_width, - STATUS_BAR_Y_SHIFT + 2, - screen_height - STATUS_BAR_Y_SHIFT, - model->index, - SubBruteAttackTotalCount); - } -} - -bool subbrute_main_view_input(InputEvent* event, void* context) { - furi_assert(event); - furi_assert(context); - - if(event->key == InputKeyBack && event->type == InputTypeShort) { -#ifdef FURI_DEBUG - FURI_LOG_I(TAG, "InputKey: BACK"); -#endif - return false; - } - - SubBruteMainView* instance = context; -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "InputKey: %d, extra_repeats: %d", event->key, instance->extra_repeats); -#endif - const uint8_t min_value = 0; - const uint8_t correct_total = SubBruteAttackTotalCount - 1; - uint8_t max_repeats = 9 - subbrute_protocol_repeats_count(instance->index); - - bool updated = false; - bool consumed = false; - bool is_short = (event->type == InputTypeShort) || (event->type == InputTypeRepeat); - - if(!instance->is_select_byte) { - if(event->key == InputKeyUp && is_short) { - if(instance->index == min_value) { - instance->index = correct_total; - } else { - instance->index = CLAMP(instance->index - 1, correct_total, min_value); - } - instance->extra_repeats = 0; - updated = true; - consumed = true; - } else if(event->key == InputKeyDown && is_short) { - if(instance->index == correct_total) { - instance->index = min_value; - } else { - instance->index = CLAMP(instance->index + 1, correct_total, min_value); - } - instance->extra_repeats = 0; - updated = true; - consumed = true; - } else if(event->key == InputKeyLeft && is_short) { - instance->extra_repeats = CLAMP(instance->extra_repeats - 1, max_repeats, 0); - - updated = true; - consumed = true; - } else if(event->key == InputKeyRight && is_short) { - instance->extra_repeats = CLAMP(instance->extra_repeats + 1, max_repeats, 0); - - updated = true; - consumed = true; - } else if(event->key == InputKeyOk && is_short) { - if(instance->index == SubBruteAttackLoadFile) { - instance->callback(SubBruteCustomEventTypeLoadFile, instance->context); - } else { - instance->callback(SubBruteCustomEventTypeMenuSelected, instance->context); - } - consumed = true; - updated = true; - } - if(updated) { - instance->window_position = instance->index; - if(instance->window_position > 0) { - instance->window_position -= 1; - } - - if(SubBruteAttackTotalCount <= ITEMS_ON_SCREEN) { - instance->window_position = 0; - } else { - if(instance->window_position >= (SubBruteAttackTotalCount - ITEMS_ON_SCREEN)) { - instance->window_position = (SubBruteAttackTotalCount - ITEMS_ON_SCREEN); - } - } - } - } else if(is_short) { - if(event->key == InputKeyLeft) { - if((instance->index > 0 && !instance->two_bytes) || - (instance->two_bytes && instance->index > 1)) { - instance->index--; - } - updated = true; - consumed = true; - } else if(event->key == InputKeyRight) { - if(instance->index < 7) { - instance->index++; - } - updated = true; - consumed = true; - } else if(event->key == InputKeyUp) { - instance->two_bytes = !instance->two_bytes; - // Because index is changing - if(instance->two_bytes && instance->index < 7) { - instance->index++; - } - // instance->callback( - // instance->two_bytes ? SubBruteCustomEventTypeChangeStepUp : - // SubBruteCustomEventTypeChangeStepDown, - // instance->context); - - updated = true; - consumed = true; - } else if(event->key == InputKeyOk) { - instance->callback(SubBruteCustomEventTypeIndexSelected, instance->context); - consumed = true; - updated = true; - } - } - - if(updated) { - with_view_model( - instance->view, - SubBruteMainViewModel * model, - { - model->index = instance->index; - model->window_position = instance->window_position; - model->key_from_file = instance->key_from_file; - model->is_select_byte = instance->is_select_byte; - model->two_bytes = instance->two_bytes; - model->extra_repeats = instance->extra_repeats; - }, - true); - } - - return consumed; -} - -void subbrute_main_view_enter(void* context) { - furi_assert(context); - -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "subbrute_main_view_enter"); -#endif -} - -void subbrute_main_view_exit(void* context) { - furi_assert(context); - -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "subbrute_main_view_exit"); -#endif -} - -SubBruteMainView* subbrute_main_view_alloc() { - SubBruteMainView* instance = malloc(sizeof(SubBruteMainView)); - instance->view = view_alloc(); - view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(SubBruteMainViewModel)); - view_set_context(instance->view, instance); - view_set_draw_callback(instance->view, (ViewDrawCallback)subbrute_main_view_draw); - view_set_input_callback(instance->view, subbrute_main_view_input); - view_set_enter_callback(instance->view, subbrute_main_view_enter); - view_set_exit_callback(instance->view, subbrute_main_view_exit); - - instance->index = 0; - instance->window_position = 0; - instance->key_from_file = 0; - instance->is_select_byte = false; - instance->two_bytes = false; - instance->extra_repeats = 0; - with_view_model( - instance->view, - SubBruteMainViewModel * model, - { - model->index = instance->index; - model->window_position = instance->window_position; - model->key_from_file = instance->key_from_file; - model->is_select_byte = instance->is_select_byte; - model->two_bytes = instance->two_bytes; - model->extra_repeats = instance->extra_repeats; - }, - true); - - return instance; -} - -void subbrute_main_view_free(SubBruteMainView* instance) { - furi_assert(instance); - - view_free(instance->view); - free(instance); -} - -View* subbrute_main_view_get_view(SubBruteMainView* instance) { - furi_assert(instance); - return instance->view; -} - -void subbrute_main_view_set_index( - SubBruteMainView* instance, - uint8_t idx, - bool is_select_byte, - bool two_bytes, - uint64_t key_from_file) { - furi_assert(instance); - furi_assert(idx < SubBruteAttackTotalCount); -#ifdef FURI_DEBUG - FURI_LOG_I(TAG, "Set index: %d, is_select_byte: %d", idx, is_select_byte); -#endif - instance->is_select_byte = is_select_byte; - instance->two_bytes = two_bytes; - instance->key_from_file = key_from_file; - instance->index = idx; - instance->window_position = idx; - - if(!is_select_byte) { - if(instance->window_position > 0) { - instance->window_position -= 1; - } - - if(SubBruteAttackTotalCount <= ITEMS_ON_SCREEN) { - instance->window_position = 0; - } else { - if(instance->window_position >= (SubBruteAttackTotalCount - ITEMS_ON_SCREEN)) { - instance->window_position = (SubBruteAttackTotalCount - ITEMS_ON_SCREEN); - } - } - } - - with_view_model( - instance->view, - SubBruteMainViewModel * model, - { - model->index = instance->index; - model->window_position = instance->window_position; - model->key_from_file = instance->key_from_file; - model->is_select_byte = instance->is_select_byte; - model->two_bytes = instance->two_bytes; - model->extra_repeats = instance->extra_repeats; - }, - true); -} - -SubBruteAttacks subbrute_main_view_get_index(SubBruteMainView* instance) { - furi_assert(instance); - return instance->index; -} - -uint8_t subbrute_main_view_get_extra_repeats(SubBruteMainView* instance) { - furi_assert(instance); - return instance->extra_repeats; -} - -bool subbrute_main_view_get_two_bytes(SubBruteMainView* instance) { - furi_assert(instance); - return instance->two_bytes; -} \ No newline at end of file diff --git a/applications/plugins/subbrute/views/subbrute_main_view.h b/applications/plugins/subbrute/views/subbrute_main_view.h deleted file mode 100644 index 003cd9817..000000000 --- a/applications/plugins/subbrute/views/subbrute_main_view.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include "../subbrute_custom_event.h" -#include "../subbrute_protocols.h" -#include -#include -#include - -typedef void (*SubBruteMainViewCallback)(SubBruteCustomEvent event, void* context); -typedef struct SubBruteMainView SubBruteMainView; - -void subbrute_main_view_set_callback( - SubBruteMainView* instance, - SubBruteMainViewCallback callback, - void* context); - -SubBruteMainView* subbrute_main_view_alloc(); -void subbrute_main_view_free(SubBruteMainView* instance); -View* subbrute_main_view_get_view(SubBruteMainView* instance); -void subbrute_main_view_set_index( - SubBruteMainView* instance, - uint8_t idx, - bool is_select_byte, - bool two_bytes, - uint64_t file_key); -SubBruteAttacks subbrute_main_view_get_index(SubBruteMainView* instance); -uint8_t subbrute_main_view_get_extra_repeats(SubBruteMainView* instance); -bool subbrute_main_view_get_two_bytes(SubBruteMainView* instance); -void subbrute_attack_view_enter(void* context); -void subbrute_attack_view_exit(void* context); -bool subbrute_attack_view_input(InputEvent* event, void* context); -void subbrute_attack_view_draw(Canvas* canvas, void* context); \ No newline at end of file diff --git a/applications/plugins/text_viewer/.gitignore b/applications/plugins/swd_probe/.gitignore similarity index 100% rename from applications/plugins/text_viewer/.gitignore rename to applications/plugins/swd_probe/.gitignore diff --git a/applications/plugins/wifi_deauther_v2/LICENSE b/applications/plugins/swd_probe/LICENSE.txt similarity index 100% rename from applications/plugins/wifi_deauther_v2/LICENSE rename to applications/plugins/swd_probe/LICENSE.txt diff --git a/applications/plugins/swd_probe/README.md b/applications/plugins/swd_probe/README.md new file mode 100644 index 000000000..df4dadcd0 --- /dev/null +++ b/applications/plugins/swd_probe/README.md @@ -0,0 +1,17 @@ +# ARM SWD (Single Wire Debug) Probe + +Modern microcontrollers have support for the two wire debug interface SWD, which makes wiring a lot simpler. +When reverse engineering, finding these two pins is a los easier than with JTAG, where you had to wire up twice or more pins. However, finding the two pins is still a bit of work, which gets simplified even more with this application. + +This application tries to detect a valid SWD response on the wires you have picked and beeps when you have found the correct pins, showing the detected ID register and, more important, the SWD pinout. It doesn't matter which two pins you choose, just pick any two from the GPIOs on the breakout header. + +To achieve this, the application sends packets and scans the response on all pins and elaborates the pins within a few retries. Using some kind of bisect pattern reduces this number to a hand full of tries, yielding in a seemingly instant detection. + +For the user it is as simple as a continuity tester - wire up your two test needles (or accupuncture needles), connect the obvious GND pin and probe all test pads. +Now it depends on your bisect capabilities finding all pad combinations, how long it will take this time. + +https://cdn.discordapp.com/attachments/954430078882816021/1071603366741938176/20230205_022641.mp4 + +https://cdn.discordapp.com/attachments/1071712925171056690/1072306469057347594/qFlipper_2023-02-07_01-01-24.mp4 + +Discussion thread: https://discord.com/channels/740930220399525928/1071712925171056690 diff --git a/applications/plugins/swd_probe/adi.c b/applications/plugins/swd_probe/adi.c new file mode 100644 index 000000000..95456fcd0 --- /dev/null +++ b/applications/plugins/swd_probe/adi.c @@ -0,0 +1,1016 @@ + +#include +#include + +#include "adi.h" +#include "swd_probe_app.h" + +/* https://github.com/openocd-org/openocd/blob/master/src/target/arm_adi_v5.c */ + +/* +static const char* class_description[16] = { + [0x0] = "Generic verification component", + [0x1] = "(ROM Table)", + [0x2] = "Reserved", + [0x3] = "Reserved", + [0x4] = "Reserved", + [0x5] = "Reserved", + [0x6] = "Reserved", + [0x7] = "Reserved", + [0x8] = "Reserved", + [0x9] = "CoreSight component", + [0xA] = "Reserved", + [0xB] = "Peripheral Test Block", + [0xC] = "Reserved", + [0xD] = "OptimoDE DESS", + [0xE] = "Generic IP component", + [0xF] = "CoreLink, PrimeCell or System component", +}; +*/ + +static const struct { + uint32_t arch_id; + const char* description; +} class0x9_devarch[] = { + /* keep same unsorted order as in ARM IHI0029E */ + {ARCH_ID(ARM_ID, 0x0A00), "RAS architecture"}, + {ARCH_ID(ARM_ID, 0x1A01), "Instrumentation Trace Macrocell (ITM) architecture"}, + {ARCH_ID(ARM_ID, 0x1A02), "DWT architecture"}, + {ARCH_ID(ARM_ID, 0x1A03), "Flash Patch and Breakpoint unit (FPB) architecture"}, + {ARCH_ID(ARM_ID, 0x2A04), "Processor debug architecture (ARMv8-M)"}, + {ARCH_ID(ARM_ID, 0x6A05), "Processor debug architecture (ARMv8-R)"}, + {ARCH_ID(ARM_ID, 0x0A10), "PC sample-based profiling"}, + {ARCH_ID(ARM_ID, 0x4A13), "Embedded Trace Macrocell (ETM) architecture"}, + {ARCH_ID(ARM_ID, 0x1A14), "Cross Trigger Interface (CTI) architecture"}, + {ARCH_ID(ARM_ID, 0x6A15), "Processor debug architecture (v8.0-A)"}, + {ARCH_ID(ARM_ID, 0x7A15), "Processor debug architecture (v8.1-A)"}, + {ARCH_ID(ARM_ID, 0x8A15), "Processor debug architecture (v8.2-A)"}, + {ARCH_ID(ARM_ID, 0x2A16), "Processor Performance Monitor (PMU) architecture"}, + {ARCH_ID(ARM_ID, 0x0A17), "Memory Access Port v2 architecture"}, + {ARCH_ID(ARM_ID, 0x0A27), "JTAG Access Port v2 architecture"}, + {ARCH_ID(ARM_ID, 0x0A31), "Basic trace router"}, + {ARCH_ID(ARM_ID, 0x0A37), "Power requestor"}, + {ARCH_ID(ARM_ID, 0x0A47), "Unknown Access Port v2 architecture"}, + {ARCH_ID(ARM_ID, 0x0A50), "HSSTP architecture"}, + {ARCH_ID(ARM_ID, 0x0A63), "System Trace Macrocell (STM) architecture"}, + {ARCH_ID(ARM_ID, 0x0A75), "CoreSight ELA architecture"}, + {ARCH_ID(ARM_ID, 0x0AF7), "CoreSight ROM architecture"}, +}; + +/* Part number interpretations are from Cortex + * core specs, the CoreSight components TRM + * (ARM DDI 0314H), CoreSight System Design + * Guide (ARM DGI 0012D) and ETM specs; also + * from chip observation (e.g. TI SDTI). + */ + +static const struct dap_part_nums { + uint16_t designer_id; + uint16_t part_num; + const char* type; + const char* full; +} dap_part_nums[] = { + { + ARM_ID, + 0x000, + "Cortex-M3 SCS", + "(System Control Space)", + }, + { + ARM_ID, + 0x001, + "Cortex-M3 ITM", + "(Instrumentation Trace Module)", + }, + { + ARM_ID, + 0x002, + "Cortex-M3 DWT", + "(Data Watchpoint and Trace)", + }, + { + ARM_ID, + 0x003, + "Cortex-M3 FPB", + "(Flash Patch and Breakpoint)", + }, + { + ARM_ID, + 0x008, + "Cortex-M0 SCS", + "(System Control Space)", + }, + { + ARM_ID, + 0x00a, + "Cortex-M0 DWT", + "(Data Watchpoint and Trace)", + }, + { + ARM_ID, + 0x00b, + "Cortex-M0 BPU", + "(Breakpoint Unit)", + }, + { + ARM_ID, + 0x00c, + "Cortex-M4 SCS", + "(System Control Space)", + }, + { + ARM_ID, + 0x00d, + "CoreSight ETM11", + "(Embedded Trace)", + }, + { + ARM_ID, + 0x00e, + "Cortex-M7 FPB", + "(Flash Patch and Breakpoint)", + }, + { + ARM_ID, + 0x193, + "SoC-600 TSGEN", + "(Timestamp Generator)", + }, + { + ARM_ID, + 0x470, + "Cortex-M1 ROM", + "(ROM Table)", + }, + { + ARM_ID, + 0x471, + "Cortex-M0 ROM", + "(ROM Table)", + }, + { + ARM_ID, + 0x490, + "Cortex-A15 GIC", + "(Generic Interrupt Controller)", + }, + { + ARM_ID, + 0x492, + "Cortex-R52 GICD", + "(Distributor)", + }, + { + ARM_ID, + 0x493, + "Cortex-R52 GICR", + "(Redistributor)", + }, + { + ARM_ID, + 0x4a1, + "Cortex-A53 ROM", + "(v8 Memory Map ROM Table)", + }, + { + ARM_ID, + 0x4a2, + "Cortex-A57 ROM", + "(ROM Table)", + }, + { + ARM_ID, + 0x4a3, + "Cortex-A53 ROM", + "(v7 Memory Map ROM Table)", + }, + { + ARM_ID, + 0x4a4, + "Cortex-A72 ROM", + "(ROM Table)", + }, + { + ARM_ID, + 0x4a9, + "Cortex-A9 ROM", + "(ROM Table)", + }, + { + ARM_ID, + 0x4aa, + "Cortex-A35 ROM", + "(v8 Memory Map ROM Table)", + }, + { + ARM_ID, + 0x4af, + "Cortex-A15 ROM", + "(ROM Table)", + }, + { + ARM_ID, + 0x4b5, + "Cortex-R5 ROM", + "(ROM Table)", + }, + { + ARM_ID, + 0x4b8, + "Cortex-R52 ROM", + "(ROM Table)", + }, + { + ARM_ID, + 0x4c0, + "Cortex-M0+ ROM", + "(ROM Table)", + }, + { + ARM_ID, + 0x4c3, + "Cortex-M3 ROM", + "(ROM Table)", + }, + { + ARM_ID, + 0x4c4, + "Cortex-M4 ROM", + "(ROM Table)", + }, + { + ARM_ID, + 0x4c7, + "Cortex-M7 PPB ROM", + "(Private Peripheral Bus ROM Table)", + }, + { + ARM_ID, + 0x4c8, + "Cortex-M7 ROM", + "(ROM Table)", + }, + { + ARM_ID, + 0x4e0, + "Cortex-A35 ROM", + "(v7 Memory Map ROM Table)", + }, + { + ARM_ID, + 0x4e4, + "Cortex-A76 ROM", + "(ROM Table)", + }, + { + ARM_ID, + 0x906, + "CoreSight CTI", + "(Cross Trigger)", + }, + { + ARM_ID, + 0x907, + "CoreSight ETB", + "(Trace Buffer)", + }, + { + ARM_ID, + 0x908, + "CoreSight CSTF", + "(Trace Funnel)", + }, + { + ARM_ID, + 0x909, + "CoreSight ATBR", + "(Advanced Trace Bus Replicator)", + }, + { + ARM_ID, + 0x910, + "CoreSight ETM9", + "(Embedded Trace)", + }, + { + ARM_ID, + 0x912, + "CoreSight TPIU", + "(Trace Port Interface Unit)", + }, + { + ARM_ID, + 0x913, + "CoreSight ITM", + "(Instrumentation Trace Macrocell)", + }, + { + ARM_ID, + 0x914, + "CoreSight SWO", + "(Single Wire Output)", + }, + { + ARM_ID, + 0x917, + "CoreSight HTM", + "(AHB Trace Macrocell)", + }, + { + ARM_ID, + 0x920, + "CoreSight ETM11", + "(Embedded Trace)", + }, + { + ARM_ID, + 0x921, + "Cortex-A8 ETM", + "(Embedded Trace)", + }, + { + ARM_ID, + 0x922, + "Cortex-A8 CTI", + "(Cross Trigger)", + }, + { + ARM_ID, + 0x923, + "Cortex-M3 TPIU", + "(Trace Port Interface Unit)", + }, + { + ARM_ID, + 0x924, + "Cortex-M3 ETM", + "(Embedded Trace)", + }, + { + ARM_ID, + 0x925, + "Cortex-M4 ETM", + "(Embedded Trace)", + }, + { + ARM_ID, + 0x930, + "Cortex-R4 ETM", + "(Embedded Trace)", + }, + { + ARM_ID, + 0x931, + "Cortex-R5 ETM", + "(Embedded Trace)", + }, + { + ARM_ID, + 0x932, + "CoreSight MTB-M0+", + "(Micro Trace Buffer)", + }, + { + ARM_ID, + 0x941, + "CoreSight TPIU-Lite", + "(Trace Port Interface Unit)", + }, + { + ARM_ID, + 0x950, + "Cortex-A9 PTM", + "(Program Trace Macrocell)", + }, + { + ARM_ID, + 0x955, + "Cortex-A5 ETM", + "(Embedded Trace)", + }, + { + ARM_ID, + 0x95a, + "Cortex-A72 ETM", + "(Embedded Trace)", + }, + { + ARM_ID, + 0x95b, + "Cortex-A17 PTM", + "(Program Trace Macrocell)", + }, + { + ARM_ID, + 0x95d, + "Cortex-A53 ETM", + "(Embedded Trace)", + }, + { + ARM_ID, + 0x95e, + "Cortex-A57 ETM", + "(Embedded Trace)", + }, + { + ARM_ID, + 0x95f, + "Cortex-A15 PTM", + "(Program Trace Macrocell)", + }, + { + ARM_ID, + 0x961, + "CoreSight TMC", + "(Trace Memory Controller)", + }, + { + ARM_ID, + 0x962, + "CoreSight STM", + "(System Trace Macrocell)", + }, + { + ARM_ID, + 0x975, + "Cortex-M7 ETM", + "(Embedded Trace)", + }, + { + ARM_ID, + 0x9a0, + "CoreSight PMU", + "(Performance Monitoring Unit)", + }, + { + ARM_ID, + 0x9a1, + "Cortex-M4 TPIU", + "(Trace Port Interface Unit)", + }, + { + ARM_ID, + 0x9a4, + "CoreSight GPR", + "(Granular Power Requester)", + }, + { + ARM_ID, + 0x9a5, + "Cortex-A5 PMU", + "(Performance Monitor Unit)", + }, + { + ARM_ID, + 0x9a7, + "Cortex-A7 PMU", + "(Performance Monitor Unit)", + }, + { + ARM_ID, + 0x9a8, + "Cortex-A53 CTI", + "(Cross Trigger)", + }, + { + ARM_ID, + 0x9a9, + "Cortex-M7 TPIU", + "(Trace Port Interface Unit)", + }, + { + ARM_ID, + 0x9ae, + "Cortex-A17 PMU", + "(Performance Monitor Unit)", + }, + { + ARM_ID, + 0x9af, + "Cortex-A15 PMU", + "(Performance Monitor Unit)", + }, + { + ARM_ID, + 0x9b6, + "Cortex-R52 PMU/CTI/ETM", + "(Performance Monitor Unit/Cross Trigger/ETM)", + }, + { + ARM_ID, + 0x9b7, + "Cortex-R7 PMU", + "(Performance Monitor Unit)", + }, + { + ARM_ID, + 0x9d3, + "Cortex-A53 PMU", + "(Performance Monitor Unit)", + }, + { + ARM_ID, + 0x9d7, + "Cortex-A57 PMU", + "(Performance Monitor Unit)", + }, + { + ARM_ID, + 0x9d8, + "Cortex-A72 PMU", + "(Performance Monitor Unit)", + }, + { + ARM_ID, + 0x9da, + "Cortex-A35 PMU/CTI/ETM", + "(Performance Monitor Unit/Cross Trigger/ETM)", + }, + { + ARM_ID, + 0x9e2, + "SoC-600 APB-AP", + "(APB4 Memory Access Port)", + }, + { + ARM_ID, + 0x9e3, + "SoC-600 AHB-AP", + "(AHB5 Memory Access Port)", + }, + { + ARM_ID, + 0x9e4, + "SoC-600 AXI-AP", + "(AXI Memory Access Port)", + }, + { + ARM_ID, + 0x9e5, + "SoC-600 APv1 Adapter", + "(Access Port v1 Adapter)", + }, + { + ARM_ID, + 0x9e6, + "SoC-600 JTAG-AP", + "(JTAG Access Port)", + }, + { + ARM_ID, + 0x9e7, + "SoC-600 TPIU", + "(Trace Port Interface Unit)", + }, + { + ARM_ID, + 0x9e8, + "SoC-600 TMC ETR/ETS", + "(Embedded Trace Router/Streamer)", + }, + { + ARM_ID, + 0x9e9, + "SoC-600 TMC ETB", + "(Embedded Trace Buffer)", + }, + { + ARM_ID, + 0x9ea, + "SoC-600 TMC ETF", + "(Embedded Trace FIFO)", + }, + { + ARM_ID, + 0x9eb, + "SoC-600 ATB Funnel", + "(Trace Funnel)", + }, + { + ARM_ID, + 0x9ec, + "SoC-600 ATB Replicator", + "(Trace Replicator)", + }, + { + ARM_ID, + 0x9ed, + "SoC-600 CTI", + "(Cross Trigger)", + }, + { + ARM_ID, + 0x9ee, + "SoC-600 CATU", + "(Address Translation Unit)", + }, + { + ARM_ID, + 0xc05, + "Cortex-A5 Debug", + "(Debug Unit)", + }, + { + ARM_ID, + 0xc07, + "Cortex-A7 Debug", + "(Debug Unit)", + }, + { + ARM_ID, + 0xc08, + "Cortex-A8 Debug", + "(Debug Unit)", + }, + { + ARM_ID, + 0xc09, + "Cortex-A9 Debug", + "(Debug Unit)", + }, + { + ARM_ID, + 0xc0e, + "Cortex-A17 Debug", + "(Debug Unit)", + }, + { + ARM_ID, + 0xc0f, + "Cortex-A15 Debug", + "(Debug Unit)", + }, + { + ARM_ID, + 0xc14, + "Cortex-R4 Debug", + "(Debug Unit)", + }, + { + ARM_ID, + 0xc15, + "Cortex-R5 Debug", + "(Debug Unit)", + }, + { + ARM_ID, + 0xc17, + "Cortex-R7 Debug", + "(Debug Unit)", + }, + { + ARM_ID, + 0xd03, + "Cortex-A53 Debug", + "(Debug Unit)", + }, + { + ARM_ID, + 0xd04, + "Cortex-A35 Debug", + "(Debug Unit)", + }, + { + ARM_ID, + 0xd07, + "Cortex-A57 Debug", + "(Debug Unit)", + }, + { + ARM_ID, + 0xd08, + "Cortex-A72 Debug", + "(Debug Unit)", + }, + { + ARM_ID, + 0xd0b, + "Cortex-A76 Debug", + "(Debug Unit)", + }, + { + ARM_ID, + 0xd0c, + "Neoverse N1", + "(Debug Unit)", + }, + { + ARM_ID, + 0xd13, + "Cortex-R52 Debug", + "(Debug Unit)", + }, + { + ARM_ID, + 0xd49, + "Neoverse N2", + "(Debug Unit)", + }, + { + 0x017, + 0x120, + "TI SDTI", + "(System Debug Trace Interface)", + }, /* from OMAP3 memmap */ + { + 0x017, + 0x343, + "TI DAPCTL", + "", + }, /* from OMAP3 memmap */ + {0x017, 0x9af, "MSP432 ROM", "(ROM Table)"}, + {0x01f, 0xcd0, "Atmel CPU with DSU", "(CPU)"}, + {0x041, 0x1db, "XMC4500 ROM", "(ROM Table)"}, + {0x041, 0x1df, "XMC4700/4800 ROM", "(ROM Table)"}, + {0x041, 0x1ed, "XMC1000 ROM", "(ROM Table)"}, + { + 0x065, + 0x000, + "SHARC+/Blackfin+", + "", + }, + { + 0x070, + 0x440, + "Qualcomm QDSS Component v1", + "(Qualcomm Designed CoreSight Component v1)", + }, + { + 0x0bf, + 0x100, + "Brahma-B53 Debug", + "(Debug Unit)", + }, + { + 0x0bf, + 0x9d3, + "Brahma-B53 PMU", + "(Performance Monitor Unit)", + }, + { + 0x0bf, + 0x4a1, + "Brahma-B53 ROM", + "(ROM Table)", + }, + { + 0x0bf, + 0x721, + "Brahma-B53 ROM", + "(ROM Table)", + }, + { + 0x1eb, + 0x181, + "Tegra 186 ROM", + "(ROM Table)", + }, + { + 0x1eb, + 0x202, + "Denver ETM", + "(Denver Embedded Trace)", + }, + { + 0x1eb, + 0x211, + "Tegra 210 ROM", + "(ROM Table)", + }, + { + 0x1eb, + 0x302, + "Denver Debug", + "(Debug Unit)", + }, + { + 0x1eb, + 0x402, + "Denver PMU", + "(Performance Monitor Unit)", + }, + {0x20, 0x410, "STM32F10 (med)", "(ROM Table)"}, + {0x20, 0x411, "STM32F2", "(ROM Table)"}, + {0x20, 0x412, "STM32F10 (low)", "(ROM Table)"}, + {0x20, 0x413, "STM32F40/41", "(ROM Table)"}, + {0x20, 0x414, "STM32F10 (high)", "(ROM Table)"}, + {0x20, 0x415, "STM32L47/48", "(ROM Table)"}, + {0x20, 0x416, "STM32L1xxx6/8/B", "(ROM Table)"}, + {0x20, 0x417, "STM32L05/06", "(ROM Table)"}, + {0x20, 0x418, "STM32F105xx/107", "(ROM Table)"}, + {0x20, 0x419, "STM32F42/43", "(ROM Table)"}, + {0x20, 0x420, "STM32F10 (med)", "(ROM Table)"}, + {0x20, 0x421, "STM32F446xx", "(ROM Table)"}, + {0x20, 0x422, "STM32FF358/02/03", "(ROM Table)"}, + {0x20, 0x423, "STM32F401xB/C", "(ROM Table)"}, + {0x20, 0x425, "STM32L031/41", "(ROM Table)"}, + {0x20, 0x427, "STM32L1xxxC", "(ROM Table)"}, + {0x20, 0x428, "STM32F10 (high)", "(ROM Table)"}, + {0x20, 0x429, "STM32L1xxx6A/8A/BA", "(ROM Table)"}, + {0x20, 0x430, "STM32F10 (xl)", "(ROM Table)"}, + {0x20, 0x431, "STM32F411xx", "(ROM Table)"}, + {0x20, 0x432, "STM32F373/8", "(ROM Table)"}, + {0x20, 0x433, "STM32F401xD/E", "(ROM Table)"}, + {0x20, 0x434, "STM32F469/79", "(ROM Table)"}, + {0x20, 0x435, "STM32L43/44", "(ROM Table)"}, + {0x20, 0x436, "STM32L1xxxD", "(ROM Table)"}, + {0x20, 0x437, "STM32L1xxxE", "(ROM Table)"}, + {0x20, 0x438, "STM32F303/34/28", "(ROM Table)"}, + {0x20, 0x439, "STM32F301/02/18 ", "(ROM Table)"}, + {0x20, 0x440, "STM32F03/5", "(ROM Table)"}, + {0x20, 0x441, "STM32F412xx", "(ROM Table)"}, + {0x20, 0x442, "STM32F03/9", "(ROM Table)"}, + {0x20, 0x444, "STM32F03xx4", "(ROM Table)"}, + {0x20, 0x445, "STM32F04/7", "(ROM Table)"}, + {0x20, 0x446, "STM32F302/03/98", "(ROM Table)"}, + {0x20, 0x447, "STM32L07/08", "(ROM Table)"}, + {0x20, 0x448, "STM32F070/1/2", "(ROM Table)"}, + {0x20, 0x449, "STM32F74/5", "(ROM Table)"}, + {0x20, 0x450, "STM32H74/5", "(ROM Table)"}, + {0x20, 0x451, "STM32F76/7", "(ROM Table)"}, + {0x20, 0x452, "STM32F72/3", "(ROM Table)"}, + {0x20, 0x457, "STM32L01/2", "(ROM Table)"}, + {0x20, 0x458, "STM32F410xx", "(ROM Table)"}, + {0x20, 0x460, "STM32G07/8", "(ROM Table)"}, + {0x20, 0x461, "STM32L496/A6", "(ROM Table)"}, + {0x20, 0x462, "STM32L45/46", "(ROM Table)"}, + {0x20, 0x463, "STM32F413/23", "(ROM Table)"}, + {0x20, 0x464, "STM32L412/22", "(ROM Table)"}, + {0x20, 0x466, "STM32G03/04", "(ROM Table)"}, + {0x20, 0x468, "STM32G431/41", "(ROM Table)"}, + {0x20, 0x469, "STM32G47/48", "(ROM Table)"}, + {0x20, 0x470, "STM32L4R/S", "(ROM Table)"}, + {0x20, 0x471, "STM32L4P5/Q5", "(ROM Table)"}, + {0x20, 0x479, "STM32G491xx", "(ROM Table)"}, + {0x20, 0x480, "STM32H7A/B", "(ROM Table)"}, + {0x20, 0x495, "STM32WB50/55", "(ROM Table)"}, + {0x20, 0x497, "STM32WLE5xx", "(ROM Table)"}}; + +const char* adi_devarch_desc(uint32_t devarch) { + if(!(devarch & ARM_CS_C9_DEVARCH_PRESENT)) { + return "not present"; + } + + for(unsigned int i = 0; i < ARRAY_SIZE(class0x9_devarch); i++) { + if((devarch & DEVARCH_ID_MASK) == class0x9_devarch[i].arch_id) { + return class0x9_devarch[i].description; + } + } + + return "unknown"; +} + +const struct dap_part_nums* adi_part_num(unsigned int des, unsigned int part) { + static char buf[32]; + static struct dap_part_nums unknown = { + .type = "Unrecognized", + .full = "", + }; + + for(unsigned int i = 0; i < ARRAY_SIZE(dap_part_nums); i++) { + if(dap_part_nums[i].designer_id == des && dap_part_nums[i].part_num == part) { + return &dap_part_nums[i]; + } + } + + snprintf(buf, sizeof(buf), "D:%x P:%x", des, part); + unknown.full = buf; + + return &unknown; +} + +bool adi_get_pidr(AppFSM* const ctx, uint32_t base, pidr_data_t* data) { + uint32_t pidrs[7]; + uint32_t offsets[] = {0xFE0, 0xFE4, 0xFE8, 0xFEC, 0xFD0, 0xFD4, 0xFD8, 0xFDC}; + + furi_mutex_acquire(ctx->swd_mutex, FuriWaitForever); + for(size_t pos = 0; pos < COUNT(pidrs); pos++) { + uint8_t ret = swd_read_memory(ctx, ctx->ap_pos, base + offsets[pos], &pidrs[pos]); + if(ret != 1) { + DBGS("Read failed"); + furi_mutex_release(ctx->swd_mutex); + return false; + } + } + furi_mutex_release(ctx->swd_mutex); + + data->designer = ((pidrs[4] & 0x0F) << 7) | ((pidrs[2] & 0x07) << 4) | + ((pidrs[1] >> 4) & 0x0F); + data->part = (pidrs[0] & 0xFF) | ((pidrs[1] & 0x0F) << 8); + data->revand = ((pidrs[3] >> 4) & 0x0F); + data->cmod = (pidrs[3] & 0x0F); + data->revision = ((pidrs[2] >> 4) & 0x0F); + data->size = ((pidrs[2] >> 4) & 0x0F); + + return true; +} + +bool adi_get_class(AppFSM* const ctx, uint32_t base, uint8_t* class) { + uint32_t cidrs[4]; + uint32_t offsets[] = {0xFF0, 0xFF4, 0xFF8, 0xFFC}; + + furi_mutex_acquire(ctx->swd_mutex, FuriWaitForever); + for(size_t pos = 0; pos < COUNT(cidrs); pos++) { + uint8_t ret = swd_read_memory(ctx, ctx->ap_pos, base + offsets[pos], &cidrs[pos]); + if(ret != 1) { + DBGS("Read failed"); + furi_mutex_release(ctx->swd_mutex); + return false; + } + } + furi_mutex_release(ctx->swd_mutex); + + if((cidrs[0] & 0xFF) != 0x0D) { + return false; + } + if((cidrs[1] & 0x0F) != 0x00) { + return false; + } + if((cidrs[2] & 0xFF) != 0x05) { + return false; + } + if((cidrs[3] & 0xFF) != 0xB1) { + return false; + } + + *class = ((cidrs[1] >> 4) & 0x0F); + + return true; +} + +const char* adi_romtable_type(AppFSM* const ctx, uint32_t base) { + pidr_data_t data; + + if(!adi_get_pidr(ctx, base, &data)) { + return "fail"; + } + const struct dap_part_nums* info = adi_part_num(data.designer, data.part); + + return info->type; +} + +const char* adi_romtable_full(AppFSM* const ctx, uint32_t base) { + pidr_data_t data; + + if(!adi_get_pidr(ctx, base, &data)) { + return "fail"; + } + const struct dap_part_nums* info = adi_part_num(data.designer, data.part); + + return info->full; +} + +uint32_t adi_romtable_entry_count(AppFSM* const ctx, uint32_t base) { + uint32_t count = 0; + uint32_t entry = 0; + + furi_mutex_acquire(ctx->swd_mutex, FuriWaitForever); + for(size_t pos = 0; pos < 960; pos++) { + uint8_t ret = 0; + for(int tries = 0; tries < 10 && ret != 1; tries++) { + ret = swd_read_memory(ctx, ctx->ap_pos, base + pos * 4, &entry); + } + if(ret != 1) { + DBGS("Read failed"); + break; + } + if(!(entry & 1)) { + break; + } + if(entry & 0x00000FFC) { + break; + } + count++; + } + furi_mutex_release(ctx->swd_mutex); + return count; +} + +uint32_t adi_romtable_get(AppFSM* const ctx, uint32_t base, uint32_t pos) { + uint32_t entry = 0; + + furi_mutex_acquire(ctx->swd_mutex, FuriWaitForever); + uint8_t ret = swd_read_memory(ctx, ctx->ap_pos, base + pos * 4, &entry); + if(ret != 1) { + DBGS("Read failed"); + furi_mutex_release(ctx->swd_mutex); + return 0; + } + furi_mutex_release(ctx->swd_mutex); + + return base + (entry & 0xFFFFF000); +} + +bool adi_is_romtable(AppFSM* const ctx, uint32_t base) { + uint8_t class = 0; + + if(!adi_get_class(ctx, base, &class)) { + return false; + } + + if(class != CIDR_CLASS_ROMTABLE) { + return false; + } + + return true; +} diff --git a/applications/plugins/swd_probe/adi.h b/applications/plugins/swd_probe/adi.h new file mode 100644 index 000000000..bade7736a --- /dev/null +++ b/applications/plugins/swd_probe/adi.h @@ -0,0 +1,34 @@ + +#ifndef __ADI_H__ +#define __ADI_H__ + +#include "swd_probe_app.h" + +#define ARM_ID 0x23B + +#define ARCH_ID(architect, archid) ((architect) << 21) | (archid) + +#define BIT(nr) (1UL << (nr)) +#define ARM_CS_C9_DEVARCH_PRESENT BIT(20) +#define ARM_CS_C9_DEVARCH_ARCHITECT_MASK (0xFFE00000) +#define ARM_CS_C9_DEVARCH_ARCHID_MASK (0x0000FFFF) +#define DEVARCH_ID_MASK (ARM_CS_C9_DEVARCH_ARCHITECT_MASK | ARM_CS_C9_DEVARCH_ARCHID_MASK) + +typedef struct { + uint16_t designer; + uint16_t part; + uint8_t revision; + uint8_t cmod; + uint8_t revand; + uint8_t size; +} pidr_data_t; + +typedef enum { CIDR_CLASS_ROMTABLE = 0x01, CIDR_CLASS_CORESIGHT = 0x09 } cidr_classes_t; + +uint32_t adi_romtable_entry_count(AppFSM* const ctx, uint32_t base); +uint32_t adi_romtable_get(AppFSM* const ctx, uint32_t base, uint32_t pos); +bool adi_is_romtable(AppFSM* const ctx, uint32_t base); +const char* adi_romtable_type(AppFSM* const ctx, uint32_t base); +const char* adi_romtable_full(AppFSM* const ctx, uint32_t base); + +#endif diff --git a/applications/plugins/swd_probe/application.fam b/applications/plugins/swd_probe/application.fam new file mode 100644 index 000000000..de2708e41 --- /dev/null +++ b/applications/plugins/swd_probe/application.fam @@ -0,0 +1,13 @@ +App( + appid="swd_probe", + name="[SWD] Probe", + apptype=FlipperAppType.PLUGIN, + entry_point="swd_probe_app_main", + cdefines=["APP_SWD_PROBE"], + requires=["notification", "gui", "storage", "dialogs", "cli"], + stack_size=2 * 1024, + order=10, + fap_icon="icons/app.png", + fap_category="GPIO", + fap_icon_assets="icons", +) diff --git a/applications/plugins/subbrute/images/ButtonDown_7x4.png b/applications/plugins/swd_probe/icons/ButtonDown_7x4.png similarity index 100% rename from applications/plugins/subbrute/images/ButtonDown_7x4.png rename to applications/plugins/swd_probe/icons/ButtonDown_7x4.png diff --git a/applications/plugins/subbrute/images/ButtonUp_7x4.png b/applications/plugins/swd_probe/icons/ButtonUp_7x4.png similarity index 100% rename from applications/plugins/subbrute/images/ButtonUp_7x4.png rename to applications/plugins/swd_probe/icons/ButtonUp_7x4.png diff --git a/applications/plugins/swd_probe/icons/app.png b/applications/plugins/swd_probe/icons/app.png new file mode 100644 index 000000000..6949ce78d Binary files /dev/null and b/applications/plugins/swd_probe/icons/app.png differ diff --git a/applications/plugins/swd_probe/icons/swd.png b/applications/plugins/swd_probe/icons/swd.png new file mode 100644 index 000000000..c8cb5831b Binary files /dev/null and b/applications/plugins/swd_probe/icons/swd.png differ diff --git a/applications/plugins/swd_probe/jep106.c b/applications/plugins/swd_probe/jep106.c new file mode 100644 index 000000000..65df94dba --- /dev/null +++ b/applications/plugins/swd_probe/jep106.c @@ -0,0 +1,26 @@ +/* https://github.com/openocd-org/openocd/blob/master/src/helper/ */ +// SPDX-License-Identifier: GPL-2.0-or-later + +/*************************************************************************** + * Copyright (C) 2015 Andreas Fritiofson * + * andreas.fritiofson@gmail.com * + ***************************************************************************/ + +#include "jep106.h" + +static const char* const jep106[][126] = { +#include "jep106.inc" +}; + +const char* jep106_table_manufacturer(unsigned int bank, unsigned int id) { + if(id < 1 || id > 126) { + return ""; + } + + /* index is zero based */ + id--; + + if(bank >= 14 || jep106[bank][id] == 0) return ""; + + return jep106[bank][id]; +} diff --git a/applications/plugins/swd_probe/jep106.h b/applications/plugins/swd_probe/jep106.h new file mode 100644 index 000000000..17c87feaa --- /dev/null +++ b/applications/plugins/swd_probe/jep106.h @@ -0,0 +1,26 @@ +/* https://github.com/openocd-org/openocd/blob/master/src/helper/ */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/*************************************************************************** + * Copyright (C) 2015 Andreas Fritiofson * + * andreas.fritiofson@gmail.com * + ***************************************************************************/ + +#ifndef OPENOCD_HELPER_JEP106_H +#define OPENOCD_HELPER_JEP106_H + +/** + * Get the manufacturer name associated with a JEP106 ID. + * @param bank The bank (number of continuation codes) of the manufacturer ID. + * @param id The 7-bit manufacturer ID (i.e. with parity stripped). + * @return A pointer to static const storage containing the name of the + * manufacturer associated with bank and id, or one of the strings + * "" and "". + */ +const char* jep106_table_manufacturer(unsigned int bank, unsigned int id); + +static inline const char* jep106_manufacturer(unsigned int manufacturer) { + return jep106_table_manufacturer(manufacturer >> 7, manufacturer & 0x7f); +} + +#endif /* OPENOCD_HELPER_JEP106_H */ diff --git a/applications/plugins/swd_probe/jep106.inc b/applications/plugins/swd_probe/jep106.inc new file mode 100644 index 000000000..3adc131be --- /dev/null +++ b/applications/plugins/swd_probe/jep106.inc @@ -0,0 +1,1791 @@ +/* https://github.com/openocd-org/openocd/blob/master/src/helper/ */ + +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/* + * The manufacturer's standard identification code list appears in JEP106. + * Copyright (c) 2022 JEDEC. All rights reserved. + * + * JEP106 is regularly updated. For the current manufacturer's standard + * identification code list, please visit the JEDEC website at www.jedec.org . + */ + +/* This file is aligned to revision JEP106BF.01 October 2022. */ + +[0][0x01 - 1] = "AMD", +[0][0x02 - 1] = "AMI", +[0][0x03 - 1] = "Fairchild", +[0][0x04 - 1] = "Fujitsu", +[0][0x05 - 1] = "GTE", +[0][0x06 - 1] = "Harris", +[0][0x07 - 1] = "Hitachi", +[0][0x08 - 1] = "Inmos", +[0][0x09 - 1] = "Intel", +[0][0x0a - 1] = "I.T.T.", +[0][0x0b - 1] = "Intersil", +[0][0x0c - 1] = "Monolithic Memories", +[0][0x0d - 1] = "Mostek", +[0][0x0e - 1] = "Freescale (Motorola)", +[0][0x0f - 1] = "National", +[0][0x10 - 1] = "NEC", +[0][0x11 - 1] = "RCA", +[0][0x12 - 1] = "Raytheon", +[0][0x13 - 1] = "Conexant (Rockwell)", +[0][0x14 - 1] = "Seeq", +[0][0x15 - 1] = "NXP (Philips)", +[0][0x16 - 1] = "Synertek", +[0][0x17 - 1] = "Texas Instruments", +[0][0x18 - 1] = "Kioxia Corporation", +[0][0x19 - 1] = "Xicor", +[0][0x1a - 1] = "Zilog", +[0][0x1b - 1] = "Eurotechnique", +[0][0x1c - 1] = "Mitsubishi", +[0][0x1d - 1] = "Lucent (AT&T)", +[0][0x1e - 1] = "Exel", +[0][0x1f - 1] = "Atmel", +[0][0x20 - 1] = "STMicroelectronics", +[0][0x21 - 1] = "Lattice Semi.", +[0][0x22 - 1] = "NCR", +[0][0x23 - 1] = "Wafer Scale Integration", +[0][0x24 - 1] = "IBM", +[0][0x25 - 1] = "Tristar", +[0][0x26 - 1] = "Visic", +[0][0x27 - 1] = "Intl. CMOS Technology", +[0][0x28 - 1] = "SSSI", +[0][0x29 - 1] = "Microchip Technology", +[0][0x2a - 1] = "Ricoh Ltd", +[0][0x2b - 1] = "VLSI", +[0][0x2c - 1] = "Micron Technology", +[0][0x2d - 1] = "SK Hynix", +[0][0x2e - 1] = "OKI Semiconductor", +[0][0x2f - 1] = "ACTEL", +[0][0x30 - 1] = "Sharp", +[0][0x31 - 1] = "Catalyst", +[0][0x32 - 1] = "Panasonic", +[0][0x33 - 1] = "IDT", +[0][0x34 - 1] = "Cypress", +[0][0x35 - 1] = "DEC", +[0][0x36 - 1] = "LSI Logic", +[0][0x37 - 1] = "Zarlink (Plessey)", +[0][0x38 - 1] = "UTMC", +[0][0x39 - 1] = "Thinking Machine", +[0][0x3a - 1] = "Thomson CSF", +[0][0x3b - 1] = "Integrated CMOS (Vertex)", +[0][0x3c - 1] = "Honeywell", +[0][0x3d - 1] = "Tektronix", +[0][0x3e - 1] = "Oracle Corporation", +[0][0x3f - 1] = "Silicon Storage Technology", +[0][0x40 - 1] = "ProMos/Mosel Vitelic", +[0][0x41 - 1] = "Infineon (Siemens)", +[0][0x42 - 1] = "Macronix", +[0][0x43 - 1] = "Xerox", +[0][0x44 - 1] = "Plus Logic", +[0][0x45 - 1] = "Western Digital Technologies Inc", +[0][0x46 - 1] = "Elan Circuit Tech.", +[0][0x47 - 1] = "European Silicon Str.", +[0][0x48 - 1] = "Apple Computer", +[0][0x49 - 1] = "Xilinx", +[0][0x4a - 1] = "Compaq", +[0][0x4b - 1] = "Protocol Engines", +[0][0x4c - 1] = "SCI", +[0][0x4d - 1] = "Seiko Instruments", +[0][0x4e - 1] = "Samsung", +[0][0x4f - 1] = "I3 Design System", +[0][0x50 - 1] = "Klic", +[0][0x51 - 1] = "Crosspoint Solutions", +[0][0x52 - 1] = "Alliance Memory Inc", +[0][0x53 - 1] = "Tandem", +[0][0x54 - 1] = "Hewlett-Packard", +[0][0x55 - 1] = "Integrated Silicon Solutions", +[0][0x56 - 1] = "Brooktree", +[0][0x57 - 1] = "New Media", +[0][0x58 - 1] = "MHS Electronic", +[0][0x59 - 1] = "Performance Semi.", +[0][0x5a - 1] = "Winbond Electronic", +[0][0x5b - 1] = "Kawasaki Steel", +[0][0x5c - 1] = "Bright Micro", +[0][0x5d - 1] = "TECMAR", +[0][0x5e - 1] = "Exar", +[0][0x5f - 1] = "PCMCIA", +[0][0x60 - 1] = "LG Semi (Goldstar)", +[0][0x61 - 1] = "Northern Telecom", +[0][0x62 - 1] = "Sanyo", +[0][0x63 - 1] = "Array Microsystems", +[0][0x64 - 1] = "Crystal Semiconductor", +[0][0x65 - 1] = "Analog Devices", +[0][0x66 - 1] = "PMC-Sierra", +[0][0x67 - 1] = "Asparix", +[0][0x68 - 1] = "Convex Computer", +[0][0x69 - 1] = "Quality Semiconductor", +[0][0x6a - 1] = "Nimbus Technology", +[0][0x6b - 1] = "Transwitch", +[0][0x6c - 1] = "Micronas (ITT Intermetall)", +[0][0x6d - 1] = "Cannon", +[0][0x6e - 1] = "Altera", +[0][0x6f - 1] = "NEXCOM", +[0][0x70 - 1] = "Qualcomm", +[0][0x71 - 1] = "Sony", +[0][0x72 - 1] = "Cray Research", +[0][0x73 - 1] = "AMS(Austria Micro)", +[0][0x74 - 1] = "Vitesse", +[0][0x75 - 1] = "Aster Electronics", +[0][0x76 - 1] = "Bay Networks (Synoptic)", +[0][0x77 - 1] = "Zentrum/ZMD", +[0][0x78 - 1] = "TRW", +[0][0x79 - 1] = "Thesys", +[0][0x7a - 1] = "Solbourne Computer", +[0][0x7b - 1] = "Allied-Signal", +[0][0x7c - 1] = "Dialog Semiconductor", +[0][0x7d - 1] = "Media Vision", +[0][0x7e - 1] = "Numonyx Corporation", +[1][0x01 - 1] = "Cirrus Logic", +[1][0x02 - 1] = "National Instruments", +[1][0x03 - 1] = "ILC Data Device", +[1][0x04 - 1] = "Alcatel Mietec", +[1][0x05 - 1] = "Micro Linear", +[1][0x06 - 1] = "Univ. of NC", +[1][0x07 - 1] = "JTAG Technologies", +[1][0x08 - 1] = "BAE Systems (Loral)", +[1][0x09 - 1] = "Nchip", +[1][0x0a - 1] = "Galileo Tech", +[1][0x0b - 1] = "Bestlink Systems", +[1][0x0c - 1] = "Graychip", +[1][0x0d - 1] = "GENNUM", +[1][0x0e - 1] = "Imagination Technologies Limited", +[1][0x0f - 1] = "Robert Bosch", +[1][0x10 - 1] = "Chip Express", +[1][0x11 - 1] = "DATARAM", +[1][0x12 - 1] = "United Microelectronics Corp", +[1][0x13 - 1] = "TCSI", +[1][0x14 - 1] = "Smart Modular", +[1][0x15 - 1] = "Hughes Aircraft", +[1][0x16 - 1] = "Lanstar Semiconductor", +[1][0x17 - 1] = "Qlogic", +[1][0x18 - 1] = "Kingston", +[1][0x19 - 1] = "Music Semi", +[1][0x1a - 1] = "Ericsson Components", +[1][0x1b - 1] = "SpaSE", +[1][0x1c - 1] = "Eon Silicon Devices", +[1][0x1d - 1] = "Integrated Silicon Solution (ISSI)", +[1][0x1e - 1] = "DoD", +[1][0x1f - 1] = "Integ. Memories Tech.", +[1][0x20 - 1] = "Corollary Inc", +[1][0x21 - 1] = "Dallas Semiconductor", +[1][0x22 - 1] = "Omnivision", +[1][0x23 - 1] = "EIV(Switzerland)", +[1][0x24 - 1] = "Novatel Wireless", +[1][0x25 - 1] = "Zarlink (Mitel)", +[1][0x26 - 1] = "Clearpoint", +[1][0x27 - 1] = "Cabletron", +[1][0x28 - 1] = "STEC (Silicon Tech)", +[1][0x29 - 1] = "Vanguard", +[1][0x2a - 1] = "Hagiwara Sys-Com", +[1][0x2b - 1] = "Vantis", +[1][0x2c - 1] = "Celestica", +[1][0x2d - 1] = "Century", +[1][0x2e - 1] = "Hal Computers", +[1][0x2f - 1] = "Rohm Company Ltd", +[1][0x30 - 1] = "Juniper Networks", +[1][0x31 - 1] = "Libit Signal Processing", +[1][0x32 - 1] = "Mushkin Enhanced Memory", +[1][0x33 - 1] = "Tundra Semiconductor", +[1][0x34 - 1] = "Adaptec Inc", +[1][0x35 - 1] = "LightSpeed Semi.", +[1][0x36 - 1] = "ZSP Corp", +[1][0x37 - 1] = "AMIC Technology", +[1][0x38 - 1] = "Adobe Systems", +[1][0x39 - 1] = "Dynachip", +[1][0x3a - 1] = "PNY Technologies Inc", +[1][0x3b - 1] = "Newport Digital", +[1][0x3c - 1] = "MMC Networks", +[1][0x3d - 1] = "T Square", +[1][0x3e - 1] = "Seiko Epson", +[1][0x3f - 1] = "Broadcom", +[1][0x40 - 1] = "Viking Components", +[1][0x41 - 1] = "V3 Semiconductor", +[1][0x42 - 1] = "Flextronics (Orbit Semiconductor)", +[1][0x43 - 1] = "Suwa Electronics", +[1][0x44 - 1] = "Transmeta", +[1][0x45 - 1] = "Micron CMS", +[1][0x46 - 1] = "American Computer & Digital Components Inc", +[1][0x47 - 1] = "Enhance 3000 Inc", +[1][0x48 - 1] = "Tower Semiconductor", +[1][0x49 - 1] = "CPU Design", +[1][0x4a - 1] = "Price Point", +[1][0x4b - 1] = "Maxim Integrated Product", +[1][0x4c - 1] = "Tellabs", +[1][0x4d - 1] = "Centaur Technology", +[1][0x4e - 1] = "Unigen Corporation", +[1][0x4f - 1] = "Transcend Information", +[1][0x50 - 1] = "Memory Card Technology", +[1][0x51 - 1] = "CKD Corporation Ltd", +[1][0x52 - 1] = "Capital Instruments Inc", +[1][0x53 - 1] = "Aica Kogyo Ltd", +[1][0x54 - 1] = "Linvex Technology", +[1][0x55 - 1] = "MSC Vertriebs GmbH", +[1][0x56 - 1] = "AKM Company Ltd", +[1][0x57 - 1] = "Dynamem Inc", +[1][0x58 - 1] = "NERA ASA", +[1][0x59 - 1] = "GSI Technology", +[1][0x5a - 1] = "Dane-Elec (C Memory)", +[1][0x5b - 1] = "Acorn Computers", +[1][0x5c - 1] = "Lara Technology", +[1][0x5d - 1] = "Oak Technology Inc", +[1][0x5e - 1] = "Itec Memory", +[1][0x5f - 1] = "Tanisys Technology", +[1][0x60 - 1] = "Truevision", +[1][0x61 - 1] = "Wintec Industries", +[1][0x62 - 1] = "Super PC Memory", +[1][0x63 - 1] = "MGV Memory", +[1][0x64 - 1] = "Galvantech", +[1][0x65 - 1] = "Gadzoox Networks", +[1][0x66 - 1] = "Multi Dimensional Cons.", +[1][0x67 - 1] = "GateField", +[1][0x68 - 1] = "Integrated Memory System", +[1][0x69 - 1] = "Triscend", +[1][0x6a - 1] = "XaQti", +[1][0x6b - 1] = "Goldenram", +[1][0x6c - 1] = "Clear Logic", +[1][0x6d - 1] = "Cimaron Communications", +[1][0x6e - 1] = "Nippon Steel Semi. Corp", +[1][0x6f - 1] = "Advantage Memory", +[1][0x70 - 1] = "AMCC", +[1][0x71 - 1] = "LeCroy", +[1][0x72 - 1] = "Yamaha Corporation", +[1][0x73 - 1] = "Digital Microwave", +[1][0x74 - 1] = "NetLogic Microsystems", +[1][0x75 - 1] = "MIMOS Semiconductor", +[1][0x76 - 1] = "Advanced Fibre", +[1][0x77 - 1] = "BF Goodrich Data.", +[1][0x78 - 1] = "Epigram", +[1][0x79 - 1] = "Acbel Polytech Inc", +[1][0x7a - 1] = "Apacer Technology", +[1][0x7b - 1] = "Admor Memory", +[1][0x7c - 1] = "FOXCONN", +[1][0x7d - 1] = "Quadratics Superconductor", +[1][0x7e - 1] = "3COM", +[2][0x01 - 1] = "Camintonn Corporation", +[2][0x02 - 1] = "ISOA Incorporated", +[2][0x03 - 1] = "Agate Semiconductor", +[2][0x04 - 1] = "ADMtek Incorporated", +[2][0x05 - 1] = "HYPERTEC", +[2][0x06 - 1] = "Adhoc Technologies", +[2][0x07 - 1] = "MOSAID Technologies", +[2][0x08 - 1] = "Ardent Technologies", +[2][0x09 - 1] = "Switchcore", +[2][0x0a - 1] = "Cisco Systems Inc", +[2][0x0b - 1] = "Allayer Technologies", +[2][0x0c - 1] = "WorkX AG (Wichman)", +[2][0x0d - 1] = "Oasis Semiconductor", +[2][0x0e - 1] = "Novanet Semiconductor", +[2][0x0f - 1] = "E-M Solutions", +[2][0x10 - 1] = "Power General", +[2][0x11 - 1] = "Advanced Hardware Arch.", +[2][0x12 - 1] = "Inova Semiconductors GmbH", +[2][0x13 - 1] = "Telocity", +[2][0x14 - 1] = "Delkin Devices", +[2][0x15 - 1] = "Symagery Microsystems", +[2][0x16 - 1] = "C-Port Corporation", +[2][0x17 - 1] = "SiberCore Technologies", +[2][0x18 - 1] = "Southland Microsystems", +[2][0x19 - 1] = "Malleable Technologies", +[2][0x1a - 1] = "Kendin Communications", +[2][0x1b - 1] = "Great Technology Microcomputer", +[2][0x1c - 1] = "Sanmina Corporation", +[2][0x1d - 1] = "HADCO Corporation", +[2][0x1e - 1] = "Corsair", +[2][0x1f - 1] = "Actrans System Inc", +[2][0x20 - 1] = "ALPHA Technologies", +[2][0x21 - 1] = "Silicon Laboratories Inc (Cygnal)", +[2][0x22 - 1] = "Artesyn Technologies", +[2][0x23 - 1] = "Align Manufacturing", +[2][0x24 - 1] = "Peregrine Semiconductor", +[2][0x25 - 1] = "Chameleon Systems", +[2][0x26 - 1] = "Aplus Flash Technology", +[2][0x27 - 1] = "MIPS Technologies", +[2][0x28 - 1] = "Chrysalis ITS", +[2][0x29 - 1] = "ADTEC Corporation", +[2][0x2a - 1] = "Kentron Technologies", +[2][0x2b - 1] = "Win Technologies", +[2][0x2c - 1] = "Tezzaron Semiconductor", +[2][0x2d - 1] = "Extreme Packet Devices", +[2][0x2e - 1] = "RF Micro Devices", +[2][0x2f - 1] = "Siemens AG", +[2][0x30 - 1] = "Sarnoff Corporation", +[2][0x31 - 1] = "Itautec SA", +[2][0x32 - 1] = "Radiata Inc", +[2][0x33 - 1] = "Benchmark Elect. (AVEX)", +[2][0x34 - 1] = "Legend", +[2][0x35 - 1] = "SpecTek Incorporated", +[2][0x36 - 1] = "Hi/fn", +[2][0x37 - 1] = "Enikia Incorporated", +[2][0x38 - 1] = "SwitchOn Networks", +[2][0x39 - 1] = "AANetcom Incorporated", +[2][0x3a - 1] = "Micro Memory Bank", +[2][0x3b - 1] = "ESS Technology", +[2][0x3c - 1] = "Virata Corporation", +[2][0x3d - 1] = "Excess Bandwidth", +[2][0x3e - 1] = "West Bay Semiconductor", +[2][0x3f - 1] = "DSP Group", +[2][0x40 - 1] = "Newport Communications", +[2][0x41 - 1] = "Chip2Chip Incorporated", +[2][0x42 - 1] = "Phobos Corporation", +[2][0x43 - 1] = "Intellitech Corporation", +[2][0x44 - 1] = "Nordic VLSI ASA", +[2][0x45 - 1] = "Ishoni Networks", +[2][0x46 - 1] = "Silicon Spice", +[2][0x47 - 1] = "Alchemy Semiconductor", +[2][0x48 - 1] = "Agilent Technologies", +[2][0x49 - 1] = "Centillium Communications", +[2][0x4a - 1] = "W.L. Gore", +[2][0x4b - 1] = "HanBit Electronics", +[2][0x4c - 1] = "GlobeSpan", +[2][0x4d - 1] = "Element 14", +[2][0x4e - 1] = "Pycon", +[2][0x4f - 1] = "Saifun Semiconductors", +[2][0x50 - 1] = "Sibyte Incorporated", +[2][0x51 - 1] = "MetaLink Technologies", +[2][0x52 - 1] = "Feiya Technology", +[2][0x53 - 1] = "I & C Technology", +[2][0x54 - 1] = "Shikatronics", +[2][0x55 - 1] = "Elektrobit", +[2][0x56 - 1] = "Megic", +[2][0x57 - 1] = "Com-Tier", +[2][0x58 - 1] = "Malaysia Micro Solutions", +[2][0x59 - 1] = "Hyperchip", +[2][0x5a - 1] = "Gemstone Communications", +[2][0x5b - 1] = "Anadigm (Anadyne)", +[2][0x5c - 1] = "3ParData", +[2][0x5d - 1] = "Mellanox Technologies", +[2][0x5e - 1] = "Tenx Technologies", +[2][0x5f - 1] = "Helix AG", +[2][0x60 - 1] = "Domosys", +[2][0x61 - 1] = "Skyup Technology", +[2][0x62 - 1] = "HiNT Corporation", +[2][0x63 - 1] = "Chiaro", +[2][0x64 - 1] = "MDT Technologies GmbH", +[2][0x65 - 1] = "Exbit Technology A/S", +[2][0x66 - 1] = "Integrated Technology Express", +[2][0x67 - 1] = "AVED Memory", +[2][0x68 - 1] = "Legerity", +[2][0x69 - 1] = "Jasmine Networks", +[2][0x6a - 1] = "Caspian Networks", +[2][0x6b - 1] = "nCUBE", +[2][0x6c - 1] = "Silicon Access Networks", +[2][0x6d - 1] = "FDK Corporation", +[2][0x6e - 1] = "High Bandwidth Access", +[2][0x6f - 1] = "MultiLink Technology", +[2][0x70 - 1] = "BRECIS", +[2][0x71 - 1] = "World Wide Packets", +[2][0x72 - 1] = "APW", +[2][0x73 - 1] = "Chicory Systems", +[2][0x74 - 1] = "Xstream Logic", +[2][0x75 - 1] = "Fast-Chip", +[2][0x76 - 1] = "Zucotto Wireless", +[2][0x77 - 1] = "Realchip", +[2][0x78 - 1] = "Galaxy Power", +[2][0x79 - 1] = "eSilicon", +[2][0x7a - 1] = "Morphics Technology", +[2][0x7b - 1] = "Accelerant Networks", +[2][0x7c - 1] = "Silicon Wave", +[2][0x7d - 1] = "SandCraft", +[2][0x7e - 1] = "Elpida", +[3][0x01 - 1] = "Solectron", +[3][0x02 - 1] = "Optosys Technologies", +[3][0x03 - 1] = "Buffalo (Formerly Melco)", +[3][0x04 - 1] = "TriMedia Technologies", +[3][0x05 - 1] = "Cyan Technologies", +[3][0x06 - 1] = "Global Locate", +[3][0x07 - 1] = "Optillion", +[3][0x08 - 1] = "Terago Communications", +[3][0x09 - 1] = "Ikanos Communications", +[3][0x0a - 1] = "Princeton Technology", +[3][0x0b - 1] = "Nanya Technology", +[3][0x0c - 1] = "Elite Flash Storage", +[3][0x0d - 1] = "Mysticom", +[3][0x0e - 1] = "LightSand Communications", +[3][0x0f - 1] = "ATI Technologies", +[3][0x10 - 1] = "Agere Systems", +[3][0x11 - 1] = "NeoMagic", +[3][0x12 - 1] = "AuroraNetics", +[3][0x13 - 1] = "Golden Empire", +[3][0x14 - 1] = "Mushkin", +[3][0x15 - 1] = "Tioga Technologies", +[3][0x16 - 1] = "Netlist", +[3][0x17 - 1] = "TeraLogic", +[3][0x18 - 1] = "Cicada Semiconductor", +[3][0x19 - 1] = "Centon Electronics", +[3][0x1a - 1] = "Tyco Electronics", +[3][0x1b - 1] = "Magis Works", +[3][0x1c - 1] = "Zettacom", +[3][0x1d - 1] = "Cogency Semiconductor", +[3][0x1e - 1] = "Chipcon AS", +[3][0x1f - 1] = "Aspex Technology", +[3][0x20 - 1] = "F5 Networks", +[3][0x21 - 1] = "Programmable Silicon Solutions", +[3][0x22 - 1] = "ChipWrights", +[3][0x23 - 1] = "Acorn Networks", +[3][0x24 - 1] = "Quicklogic", +[3][0x25 - 1] = "Kingmax Semiconductor", +[3][0x26 - 1] = "BOPS", +[3][0x27 - 1] = "Flasys", +[3][0x28 - 1] = "BitBlitz Communications", +[3][0x29 - 1] = "eMemory Technology", +[3][0x2a - 1] = "Procket Networks", +[3][0x2b - 1] = "Purple Ray", +[3][0x2c - 1] = "Trebia Networks", +[3][0x2d - 1] = "Delta Electronics", +[3][0x2e - 1] = "Onex Communications", +[3][0x2f - 1] = "Ample Communications", +[3][0x30 - 1] = "Memory Experts Intl", +[3][0x31 - 1] = "Astute Networks", +[3][0x32 - 1] = "Azanda Network Devices", +[3][0x33 - 1] = "Dibcom", +[3][0x34 - 1] = "Tekmos", +[3][0x35 - 1] = "API NetWorks", +[3][0x36 - 1] = "Bay Microsystems", +[3][0x37 - 1] = "Firecron Ltd", +[3][0x38 - 1] = "Resonext Communications", +[3][0x39 - 1] = "Tachys Technologies", +[3][0x3a - 1] = "Equator Technology", +[3][0x3b - 1] = "Concept Computer", +[3][0x3c - 1] = "SILCOM", +[3][0x3d - 1] = "3Dlabs", +[3][0x3e - 1] = "c't Magazine", +[3][0x3f - 1] = "Sanera Systems", +[3][0x40 - 1] = "Silicon Packets", +[3][0x41 - 1] = "Viasystems Group", +[3][0x42 - 1] = "Simtek", +[3][0x43 - 1] = "Semicon Devices Singapore", +[3][0x44 - 1] = "Satron Handelsges", +[3][0x45 - 1] = "Improv Systems", +[3][0x46 - 1] = "INDUSYS GmbH", +[3][0x47 - 1] = "Corrent", +[3][0x48 - 1] = "Infrant Technologies", +[3][0x49 - 1] = "Ritek Corp", +[3][0x4a - 1] = "empowerTel Networks", +[3][0x4b - 1] = "Hypertec", +[3][0x4c - 1] = "Cavium Networks", +[3][0x4d - 1] = "PLX Technology", +[3][0x4e - 1] = "Massana Design", +[3][0x4f - 1] = "Intrinsity", +[3][0x50 - 1] = "Valence Semiconductor", +[3][0x51 - 1] = "Terawave Communications", +[3][0x52 - 1] = "IceFyre Semiconductor", +[3][0x53 - 1] = "Primarion", +[3][0x54 - 1] = "Picochip Designs Ltd", +[3][0x55 - 1] = "Silverback Systems", +[3][0x56 - 1] = "Jade Star Technologies", +[3][0x57 - 1] = "Pijnenburg Securealink", +[3][0x58 - 1] = "takeMS - Ultron AG", +[3][0x59 - 1] = "Cambridge Silicon Radio", +[3][0x5a - 1] = "Swissbit", +[3][0x5b - 1] = "Nazomi Communications", +[3][0x5c - 1] = "eWave System", +[3][0x5d - 1] = "Rockwell Collins", +[3][0x5e - 1] = "Picocel Co Ltd (Paion)", +[3][0x5f - 1] = "Alphamosaic Ltd", +[3][0x60 - 1] = "Sandburst", +[3][0x61 - 1] = "SiCon Video", +[3][0x62 - 1] = "NanoAmp Solutions", +[3][0x63 - 1] = "Ericsson Technology", +[3][0x64 - 1] = "PrairieComm", +[3][0x65 - 1] = "Mitac International", +[3][0x66 - 1] = "Layer N Networks", +[3][0x67 - 1] = "MtekVision (Atsana)", +[3][0x68 - 1] = "Allegro Networks", +[3][0x69 - 1] = "Marvell Semiconductors", +[3][0x6a - 1] = "Netergy Microelectronic", +[3][0x6b - 1] = "NVIDIA", +[3][0x6c - 1] = "Internet Machines", +[3][0x6d - 1] = "Memorysolution GmbH", +[3][0x6e - 1] = "Litchfield Communication", +[3][0x6f - 1] = "Accton Technology", +[3][0x70 - 1] = "Teradiant Networks", +[3][0x71 - 1] = "Scaleo Chip", +[3][0x72 - 1] = "Cortina Systems", +[3][0x73 - 1] = "RAM Components", +[3][0x74 - 1] = "Raqia Networks", +[3][0x75 - 1] = "ClearSpeed", +[3][0x76 - 1] = "Matsushita Battery", +[3][0x77 - 1] = "Xelerated", +[3][0x78 - 1] = "SimpleTech", +[3][0x79 - 1] = "Utron Technology", +[3][0x7a - 1] = "Astec International", +[3][0x7b - 1] = "AVM gmbH", +[3][0x7c - 1] = "Redux Communications", +[3][0x7d - 1] = "Dot Hill Systems", +[3][0x7e - 1] = "TeraChip", +[4][0x01 - 1] = "T-RAM Incorporated", +[4][0x02 - 1] = "Innovics Wireless", +[4][0x03 - 1] = "Teknovus", +[4][0x04 - 1] = "KeyEye Communications", +[4][0x05 - 1] = "Runcom Technologies", +[4][0x06 - 1] = "RedSwitch", +[4][0x07 - 1] = "Dotcast", +[4][0x08 - 1] = "Silicon Mountain Memory", +[4][0x09 - 1] = "Signia Technologies", +[4][0x0a - 1] = "Pixim", +[4][0x0b - 1] = "Galazar Networks", +[4][0x0c - 1] = "White Electronic Designs", +[4][0x0d - 1] = "Patriot Scientific", +[4][0x0e - 1] = "Neoaxiom Corporation", +[4][0x0f - 1] = "3Y Power Technology", +[4][0x10 - 1] = "Scaleo Chip", +[4][0x11 - 1] = "Potentia Power Systems", +[4][0x12 - 1] = "C-guys Incorporated", +[4][0x13 - 1] = "Digital Communications Technology Inc", +[4][0x14 - 1] = "Silicon-Based Technology", +[4][0x15 - 1] = "Fulcrum Microsystems", +[4][0x16 - 1] = "Positivo Informatica Ltd", +[4][0x17 - 1] = "XIOtech Corporation", +[4][0x18 - 1] = "PortalPlayer", +[4][0x19 - 1] = "Zhiying Software", +[4][0x1a - 1] = "ParkerVision Inc", +[4][0x1b - 1] = "Phonex Broadband", +[4][0x1c - 1] = "Skyworks Solutions", +[4][0x1d - 1] = "Entropic Communications", +[4][0x1e - 1] = "I'M Intelligent Memory Ltd", +[4][0x1f - 1] = "Zensys A/S", +[4][0x20 - 1] = "Legend Silicon Corp", +[4][0x21 - 1] = "Sci-worx GmbH", +[4][0x22 - 1] = "SMSC (Standard Microsystems)", +[4][0x23 - 1] = "Renesas Electronics", +[4][0x24 - 1] = "Raza Microelectronics", +[4][0x25 - 1] = "Phyworks", +[4][0x26 - 1] = "MediaTek", +[4][0x27 - 1] = "Non-cents Productions", +[4][0x28 - 1] = "US Modular", +[4][0x29 - 1] = "Wintegra Ltd", +[4][0x2a - 1] = "Mathstar", +[4][0x2b - 1] = "StarCore", +[4][0x2c - 1] = "Oplus Technologies", +[4][0x2d - 1] = "Mindspeed", +[4][0x2e - 1] = "Just Young Computer", +[4][0x2f - 1] = "Radia Communications", +[4][0x30 - 1] = "OCZ", +[4][0x31 - 1] = "Emuzed", +[4][0x32 - 1] = "LOGIC Devices", +[4][0x33 - 1] = "Inphi Corporation", +[4][0x34 - 1] = "Quake Technologies", +[4][0x35 - 1] = "Vixel", +[4][0x36 - 1] = "SolusTek", +[4][0x37 - 1] = "Kongsberg Maritime", +[4][0x38 - 1] = "Faraday Technology", +[4][0x39 - 1] = "Altium Ltd", +[4][0x3a - 1] = "Insyte", +[4][0x3b - 1] = "ARM Ltd", +[4][0x3c - 1] = "DigiVision", +[4][0x3d - 1] = "Vativ Technologies", +[4][0x3e - 1] = "Endicott Interconnect Technologies", +[4][0x3f - 1] = "Pericom", +[4][0x40 - 1] = "Bandspeed", +[4][0x41 - 1] = "LeWiz Communications", +[4][0x42 - 1] = "CPU Technology", +[4][0x43 - 1] = "Ramaxel Technology", +[4][0x44 - 1] = "DSP Group", +[4][0x45 - 1] = "Axis Communications", +[4][0x46 - 1] = "Legacy Electronics", +[4][0x47 - 1] = "Chrontel", +[4][0x48 - 1] = "Powerchip Semiconductor", +[4][0x49 - 1] = "MobilEye Technologies", +[4][0x4a - 1] = "Excel Semiconductor", +[4][0x4b - 1] = "A-DATA Technology", +[4][0x4c - 1] = "VirtualDigm", +[4][0x4d - 1] = "G Skill Intl", +[4][0x4e - 1] = "Quanta Computer", +[4][0x4f - 1] = "Yield Microelectronics", +[4][0x50 - 1] = "Afa Technologies", +[4][0x51 - 1] = "KINGBOX Technology Co Ltd", +[4][0x52 - 1] = "Ceva", +[4][0x53 - 1] = "iStor Networks", +[4][0x54 - 1] = "Advance Modules", +[4][0x55 - 1] = "Microsoft", +[4][0x56 - 1] = "Open-Silicon", +[4][0x57 - 1] = "Goal Semiconductor", +[4][0x58 - 1] = "ARC International", +[4][0x59 - 1] = "Simmtec", +[4][0x5a - 1] = "Metanoia", +[4][0x5b - 1] = "Key Stream", +[4][0x5c - 1] = "Lowrance Electronics", +[4][0x5d - 1] = "Adimos", +[4][0x5e - 1] = "SiGe Semiconductor", +[4][0x5f - 1] = "Fodus Communications", +[4][0x60 - 1] = "Credence Systems Corp", +[4][0x61 - 1] = "Genesis Microchip Inc", +[4][0x62 - 1] = "Vihana Inc", +[4][0x63 - 1] = "WIS Technologies", +[4][0x64 - 1] = "GateChange Technologies", +[4][0x65 - 1] = "High Density Devices AS", +[4][0x66 - 1] = "Synopsys", +[4][0x67 - 1] = "Gigaram", +[4][0x68 - 1] = "Enigma Semiconductor Inc", +[4][0x69 - 1] = "Century Micro Inc", +[4][0x6a - 1] = "Icera Semiconductor", +[4][0x6b - 1] = "Mediaworks Integrated Systems", +[4][0x6c - 1] = "O'Neil Product Development", +[4][0x6d - 1] = "Supreme Top Technology Ltd", +[4][0x6e - 1] = "MicroDisplay Corporation", +[4][0x6f - 1] = "Team Group Inc", +[4][0x70 - 1] = "Sinett Corporation", +[4][0x71 - 1] = "Toshiba Corporation", +[4][0x72 - 1] = "Tensilica", +[4][0x73 - 1] = "SiRF Technology", +[4][0x74 - 1] = "Bacoc Inc", +[4][0x75 - 1] = "SMaL Camera Technologies", +[4][0x76 - 1] = "Thomson SC", +[4][0x77 - 1] = "Airgo Networks", +[4][0x78 - 1] = "Wisair Ltd", +[4][0x79 - 1] = "SigmaTel", +[4][0x7a - 1] = "Arkados", +[4][0x7b - 1] = "Compete IT gmbH Co KG", +[4][0x7c - 1] = "Eudar Technology Inc", +[4][0x7d - 1] = "Focus Enhancements", +[4][0x7e - 1] = "Xyratex", +[5][0x01 - 1] = "Specular Networks", +[5][0x02 - 1] = "Patriot Memory (PDP Systems)", +[5][0x03 - 1] = "U-Chip Technology Corp", +[5][0x04 - 1] = "Silicon Optix", +[5][0x05 - 1] = "Greenfield Networks", +[5][0x06 - 1] = "CompuRAM GmbH", +[5][0x07 - 1] = "Stargen Inc", +[5][0x08 - 1] = "NetCell Corporation", +[5][0x09 - 1] = "Excalibrus Technologies Ltd", +[5][0x0a - 1] = "SCM Microsystems", +[5][0x0b - 1] = "Xsigo Systems Inc", +[5][0x0c - 1] = "CHIPS & Systems Inc", +[5][0x0d - 1] = "Tier 1 Multichip Solutions", +[5][0x0e - 1] = "CWRL Labs", +[5][0x0f - 1] = "Teradici", +[5][0x10 - 1] = "Gigaram Inc", +[5][0x11 - 1] = "g2 Microsystems", +[5][0x12 - 1] = "PowerFlash Semiconductor", +[5][0x13 - 1] = "P.A. Semi Inc", +[5][0x14 - 1] = "NovaTech Solutions S.A.", +[5][0x15 - 1] = "c2 Microsystems Inc", +[5][0x16 - 1] = "Level5 Networks", +[5][0x17 - 1] = "COS Memory AG", +[5][0x18 - 1] = "Innovasic Semiconductor", +[5][0x19 - 1] = "02IC Co Ltd", +[5][0x1a - 1] = "Tabula Inc", +[5][0x1b - 1] = "Crucial Technology", +[5][0x1c - 1] = "Chelsio Communications", +[5][0x1d - 1] = "Solarflare Communications", +[5][0x1e - 1] = "Xambala Inc", +[5][0x1f - 1] = "EADS Astrium", +[5][0x20 - 1] = "Terra Semiconductor Inc", +[5][0x21 - 1] = "Imaging Works Inc", +[5][0x22 - 1] = "Astute Networks Inc", +[5][0x23 - 1] = "Tzero", +[5][0x24 - 1] = "Emulex", +[5][0x25 - 1] = "Power-One", +[5][0x26 - 1] = "Pulse~LINK Inc", +[5][0x27 - 1] = "Hon Hai Precision Industry", +[5][0x28 - 1] = "White Rock Networks Inc", +[5][0x29 - 1] = "Telegent Systems USA Inc", +[5][0x2a - 1] = "Atrua Technologies Inc", +[5][0x2b - 1] = "Acbel Polytech Inc", +[5][0x2c - 1] = "eRide Inc", +[5][0x2d - 1] = "ULi Electronics Inc", +[5][0x2e - 1] = "Magnum Semiconductor Inc", +[5][0x2f - 1] = "neoOne Technology Inc", +[5][0x30 - 1] = "Connex Technology Inc", +[5][0x31 - 1] = "Stream Processors Inc", +[5][0x32 - 1] = "Focus Enhancements", +[5][0x33 - 1] = "Telecis Wireless Inc", +[5][0x34 - 1] = "uNav Microelectronics", +[5][0x35 - 1] = "Tarari Inc", +[5][0x36 - 1] = "Ambric Inc", +[5][0x37 - 1] = "Newport Media Inc", +[5][0x38 - 1] = "VMTS", +[5][0x39 - 1] = "Enuclia Semiconductor Inc", +[5][0x3a - 1] = "Virtium Technology Inc", +[5][0x3b - 1] = "Solid State System Co Ltd", +[5][0x3c - 1] = "Kian Tech LLC", +[5][0x3d - 1] = "Artimi", +[5][0x3e - 1] = "Power Quotient International", +[5][0x3f - 1] = "Avago Technologies", +[5][0x40 - 1] = "ADTechnology", +[5][0x41 - 1] = "Sigma Designs", +[5][0x42 - 1] = "SiCortex Inc", +[5][0x43 - 1] = "Ventura Technology Group", +[5][0x44 - 1] = "eASIC", +[5][0x45 - 1] = "M.H.S. SAS", +[5][0x46 - 1] = "Micro Star International", +[5][0x47 - 1] = "Rapport Inc", +[5][0x48 - 1] = "Makway International", +[5][0x49 - 1] = "Broad Reach Engineering Co", +[5][0x4a - 1] = "Semiconductor Mfg Intl Corp", +[5][0x4b - 1] = "SiConnect", +[5][0x4c - 1] = "FCI USA Inc", +[5][0x4d - 1] = "Validity Sensors", +[5][0x4e - 1] = "Coney Technology Co Ltd", +[5][0x4f - 1] = "Spans Logic", +[5][0x50 - 1] = "Neterion Inc", +[5][0x51 - 1] = "Qimonda", +[5][0x52 - 1] = "New Japan Radio Co Ltd", +[5][0x53 - 1] = "Velogix", +[5][0x54 - 1] = "Montalvo Systems", +[5][0x55 - 1] = "iVivity Inc", +[5][0x56 - 1] = "Walton Chaintech", +[5][0x57 - 1] = "AENEON", +[5][0x58 - 1] = "Lorom Industrial Co Ltd", +[5][0x59 - 1] = "Radiospire Networks", +[5][0x5a - 1] = "Sensio Technologies Inc", +[5][0x5b - 1] = "Nethra Imaging", +[5][0x5c - 1] = "Hexon Technology Pte Ltd", +[5][0x5d - 1] = "CompuStocx (CSX)", +[5][0x5e - 1] = "Methode Electronics Inc", +[5][0x5f - 1] = "Connect One Ltd", +[5][0x60 - 1] = "Opulan Technologies", +[5][0x61 - 1] = "Septentrio NV", +[5][0x62 - 1] = "Goldenmars Technology Inc", +[5][0x63 - 1] = "Kreton Corporation", +[5][0x64 - 1] = "Cochlear Ltd", +[5][0x65 - 1] = "Altair Semiconductor", +[5][0x66 - 1] = "NetEffect Inc", +[5][0x67 - 1] = "Spansion Inc", +[5][0x68 - 1] = "Taiwan Semiconductor Mfg", +[5][0x69 - 1] = "Emphany Systems Inc", +[5][0x6a - 1] = "ApaceWave Technologies", +[5][0x6b - 1] = "Mobilygen Corporation", +[5][0x6c - 1] = "Tego", +[5][0x6d - 1] = "Cswitch Corporation", +[5][0x6e - 1] = "Haier (Beijing) IC Design Co", +[5][0x6f - 1] = "MetaRAM", +[5][0x70 - 1] = "Axel Electronics Co Ltd", +[5][0x71 - 1] = "Tilera Corporation", +[5][0x72 - 1] = "Aquantia", +[5][0x73 - 1] = "Vivace Semiconductor", +[5][0x74 - 1] = "Redpine Signals", +[5][0x75 - 1] = "Octalica", +[5][0x76 - 1] = "InterDigital Communications", +[5][0x77 - 1] = "Avant Technology", +[5][0x78 - 1] = "Asrock Inc", +[5][0x79 - 1] = "Availink", +[5][0x7a - 1] = "Quartics Inc", +[5][0x7b - 1] = "Element CXI", +[5][0x7c - 1] = "Innovaciones Microelectronicas", +[5][0x7d - 1] = "VeriSilicon Microelectronics", +[5][0x7e - 1] = "W5 Networks", +[6][0x01 - 1] = "MOVEKING", +[6][0x02 - 1] = "Mavrix Technology Inc", +[6][0x03 - 1] = "CellGuide Ltd", +[6][0x04 - 1] = "Faraday Technology", +[6][0x05 - 1] = "Diablo Technologies Inc", +[6][0x06 - 1] = "Jennic", +[6][0x07 - 1] = "Octasic", +[6][0x08 - 1] = "Molex Incorporated", +[6][0x09 - 1] = "3Leaf Networks", +[6][0x0a - 1] = "Bright Micron Technology", +[6][0x0b - 1] = "Netxen", +[6][0x0c - 1] = "NextWave Broadband Inc", +[6][0x0d - 1] = "DisplayLink", +[6][0x0e - 1] = "ZMOS Technology", +[6][0x0f - 1] = "Tec-Hill", +[6][0x10 - 1] = "Multigig Inc", +[6][0x11 - 1] = "Amimon", +[6][0x12 - 1] = "Euphonic Technologies Inc", +[6][0x13 - 1] = "BRN Phoenix", +[6][0x14 - 1] = "InSilica", +[6][0x15 - 1] = "Ember Corporation", +[6][0x16 - 1] = "Avexir Technologies Corporation", +[6][0x17 - 1] = "Echelon Corporation", +[6][0x18 - 1] = "Edgewater Computer Systems", +[6][0x19 - 1] = "XMOS Semiconductor Ltd", +[6][0x1a - 1] = "GENUSION Inc", +[6][0x1b - 1] = "Memory Corp NV", +[6][0x1c - 1] = "SiliconBlue Technologies", +[6][0x1d - 1] = "Rambus Inc", +[6][0x1e - 1] = "Andes Technology Corporation", +[6][0x1f - 1] = "Coronis Systems", +[6][0x20 - 1] = "Achronix Semiconductor", +[6][0x21 - 1] = "Siano Mobile Silicon Ltd", +[6][0x22 - 1] = "Semtech Corporation", +[6][0x23 - 1] = "Pixelworks Inc", +[6][0x24 - 1] = "Gaisler Research AB", +[6][0x25 - 1] = "Teranetics", +[6][0x26 - 1] = "Toppan Printing Co Ltd", +[6][0x27 - 1] = "Kingxcon", +[6][0x28 - 1] = "Silicon Integrated Systems", +[6][0x29 - 1] = "I-O Data Device Inc", +[6][0x2a - 1] = "NDS Americas Inc", +[6][0x2b - 1] = "Solomon Systech Limited", +[6][0x2c - 1] = "On Demand Microelectronics", +[6][0x2d - 1] = "Amicus Wireless Inc", +[6][0x2e - 1] = "SMARDTV SNC", +[6][0x2f - 1] = "Comsys Communication Ltd", +[6][0x30 - 1] = "Movidia Ltd", +[6][0x31 - 1] = "Javad GNSS Inc", +[6][0x32 - 1] = "Montage Technology Group", +[6][0x33 - 1] = "Trident Microsystems", +[6][0x34 - 1] = "Super Talent", +[6][0x35 - 1] = "Optichron Inc", +[6][0x36 - 1] = "Future Waves UK Ltd", +[6][0x37 - 1] = "SiBEAM Inc", +[6][0x38 - 1] = "InicoreInc", +[6][0x39 - 1] = "Virident Systems", +[6][0x3a - 1] = "M2000 Inc", +[6][0x3b - 1] = "ZeroG Wireless Inc", +[6][0x3c - 1] = "Gingle Technology Co Ltd", +[6][0x3d - 1] = "Space Micro Inc", +[6][0x3e - 1] = "Wilocity", +[6][0x3f - 1] = "Novafora Inc", +[6][0x40 - 1] = "iKoa Corporation", +[6][0x41 - 1] = "ASint Technology", +[6][0x42 - 1] = "Ramtron", +[6][0x43 - 1] = "Plato Networks Inc", +[6][0x44 - 1] = "IPtronics AS", +[6][0x45 - 1] = "Infinite-Memories", +[6][0x46 - 1] = "Parade Technologies Inc", +[6][0x47 - 1] = "Dune Networks", +[6][0x48 - 1] = "GigaDevice Semiconductor", +[6][0x49 - 1] = "Modu Ltd", +[6][0x4a - 1] = "CEITEC", +[6][0x4b - 1] = "Northrop Grumman", +[6][0x4c - 1] = "XRONET Corporation", +[6][0x4d - 1] = "Sicon Semiconductor AB", +[6][0x4e - 1] = "Atla Electronics Co Ltd", +[6][0x4f - 1] = "TOPRAM Technology", +[6][0x50 - 1] = "Silego Technology Inc", +[6][0x51 - 1] = "Kinglife", +[6][0x52 - 1] = "Ability Industries Ltd", +[6][0x53 - 1] = "Silicon Power Computer & Communications", +[6][0x54 - 1] = "Augusta Technology Inc", +[6][0x55 - 1] = "Nantronics Semiconductors", +[6][0x56 - 1] = "Hilscher Gesellschaft", +[6][0x57 - 1] = "Quixant Ltd", +[6][0x58 - 1] = "Percello Ltd", +[6][0x59 - 1] = "NextIO Inc", +[6][0x5a - 1] = "Scanimetrics Inc", +[6][0x5b - 1] = "FS-Semi Company Ltd", +[6][0x5c - 1] = "Infinera Corporation", +[6][0x5d - 1] = "SandForce Inc", +[6][0x5e - 1] = "Lexar Media", +[6][0x5f - 1] = "Teradyne Inc", +[6][0x60 - 1] = "Memory Exchange Corp", +[6][0x61 - 1] = "Suzhou Smartek Electronics", +[6][0x62 - 1] = "Avantium Corporation", +[6][0x63 - 1] = "ATP Electronics Inc", +[6][0x64 - 1] = "Valens Semiconductor Ltd", +[6][0x65 - 1] = "Agate Logic Inc", +[6][0x66 - 1] = "Netronome", +[6][0x67 - 1] = "Zenverge Inc", +[6][0x68 - 1] = "N-trig Ltd", +[6][0x69 - 1] = "SanMax Technologies Inc", +[6][0x6a - 1] = "Contour Semiconductor Inc", +[6][0x6b - 1] = "TwinMOS", +[6][0x6c - 1] = "Silicon Systems Inc", +[6][0x6d - 1] = "V-Color Technology Inc", +[6][0x6e - 1] = "Certicom Corporation", +[6][0x6f - 1] = "JSC ICC Milandr", +[6][0x70 - 1] = "PhotoFast Global Inc", +[6][0x71 - 1] = "InnoDisk Corporation", +[6][0x72 - 1] = "Muscle Power", +[6][0x73 - 1] = "Energy Micro", +[6][0x74 - 1] = "Innofidei", +[6][0x75 - 1] = "CopperGate Communications", +[6][0x76 - 1] = "Holtek Semiconductor Inc", +[6][0x77 - 1] = "Myson Century Inc", +[6][0x78 - 1] = "FIDELIX", +[6][0x79 - 1] = "Red Digital Cinema", +[6][0x7a - 1] = "Densbits Technology", +[6][0x7b - 1] = "Zempro", +[6][0x7c - 1] = "MoSys", +[6][0x7d - 1] = "Provigent", +[6][0x7e - 1] = "Triad Semiconductor Inc", +[7][0x01 - 1] = "Siklu Communication Ltd", +[7][0x02 - 1] = "A Force Manufacturing Ltd", +[7][0x03 - 1] = "Strontium", +[7][0x04 - 1] = "ALi Corp (Abilis Systems)", +[7][0x05 - 1] = "Siglead Inc", +[7][0x06 - 1] = "Ubicom Inc", +[7][0x07 - 1] = "Unifosa Corporation", +[7][0x08 - 1] = "Stretch Inc", +[7][0x09 - 1] = "Lantiq Deutschland GmbH", +[7][0x0a - 1] = "Visipro.", +[7][0x0b - 1] = "EKMemory", +[7][0x0c - 1] = "Microelectronics Institute ZTE", +[7][0x0d - 1] = "u-blox AG", +[7][0x0e - 1] = "Carry Technology Co Ltd", +[7][0x0f - 1] = "Nokia", +[7][0x10 - 1] = "King Tiger Technology", +[7][0x11 - 1] = "Sierra Wireless", +[7][0x12 - 1] = "HT Micron", +[7][0x13 - 1] = "Albatron Technology Co Ltd", +[7][0x14 - 1] = "Leica Geosystems AG", +[7][0x15 - 1] = "BroadLight", +[7][0x16 - 1] = "AEXEA", +[7][0x17 - 1] = "ClariPhy Communications Inc", +[7][0x18 - 1] = "Green Plug", +[7][0x19 - 1] = "Design Art Networks", +[7][0x1a - 1] = "Mach Xtreme Technology Ltd", +[7][0x1b - 1] = "ATO Solutions Co Ltd", +[7][0x1c - 1] = "Ramsta", +[7][0x1d - 1] = "Greenliant Systems Ltd", +[7][0x1e - 1] = "Teikon", +[7][0x1f - 1] = "Antec Hadron", +[7][0x20 - 1] = "NavCom Technology Inc", +[7][0x21 - 1] = "Shanghai Fudan Microelectronics", +[7][0x22 - 1] = "Calxeda Inc", +[7][0x23 - 1] = "JSC EDC Electronics", +[7][0x24 - 1] = "Kandit Technology Co Ltd", +[7][0x25 - 1] = "Ramos Technology", +[7][0x26 - 1] = "Goldenmars Technology", +[7][0x27 - 1] = "XeL Technology Inc", +[7][0x28 - 1] = "Newzone Corporation", +[7][0x29 - 1] = "ShenZhen MercyPower Tech", +[7][0x2a - 1] = "Nanjing Yihuo Technology", +[7][0x2b - 1] = "Nethra Imaging Inc", +[7][0x2c - 1] = "SiTel Semiconductor BV", +[7][0x2d - 1] = "SolidGear Corporation", +[7][0x2e - 1] = "Topower Computer Ind Co Ltd", +[7][0x2f - 1] = "Wilocity", +[7][0x30 - 1] = "Profichip GmbH", +[7][0x31 - 1] = "Gerad Technologies", +[7][0x32 - 1] = "Ritek Corporation", +[7][0x33 - 1] = "Gomos Technology Limited", +[7][0x34 - 1] = "Memoright Corporation", +[7][0x35 - 1] = "D-Broad Inc", +[7][0x36 - 1] = "HiSilicon Technologies", +[7][0x37 - 1] = "Syndiant Inc.", +[7][0x38 - 1] = "Enverv Inc", +[7][0x39 - 1] = "Cognex", +[7][0x3a - 1] = "Xinnova Technology Inc", +[7][0x3b - 1] = "Ultron AG", +[7][0x3c - 1] = "Concord Idea Corporation", +[7][0x3d - 1] = "AIM Corporation", +[7][0x3e - 1] = "Lifetime Memory Products", +[7][0x3f - 1] = "Ramsway", +[7][0x40 - 1] = "Recore Systems B.V.", +[7][0x41 - 1] = "Haotian Jinshibo Science Tech", +[7][0x42 - 1] = "Being Advanced Memory", +[7][0x43 - 1] = "Adesto Technologies", +[7][0x44 - 1] = "Giantec Semiconductor Inc", +[7][0x45 - 1] = "HMD Electronics AG", +[7][0x46 - 1] = "Gloway International (HK)", +[7][0x47 - 1] = "Kingcore", +[7][0x48 - 1] = "Anucell Technology Holding", +[7][0x49 - 1] = "Accord Software & Systems Pvt. Ltd", +[7][0x4a - 1] = "Active-Semi Inc", +[7][0x4b - 1] = "Denso Corporation", +[7][0x4c - 1] = "TLSI Inc", +[7][0x4d - 1] = "Qidan", +[7][0x4e - 1] = "Mustang", +[7][0x4f - 1] = "Orca Systems", +[7][0x50 - 1] = "Passif Semiconductor", +[7][0x51 - 1] = "GigaDevice Semiconductor (Beijing) Inc", +[7][0x52 - 1] = "Memphis Electronic", +[7][0x53 - 1] = "Beckhoff Automation GmbH", +[7][0x54 - 1] = "Harmony Semiconductor Corp", +[7][0x55 - 1] = "Air Computers SRL", +[7][0x56 - 1] = "TMT Memory", +[7][0x57 - 1] = "Eorex Corporation", +[7][0x58 - 1] = "Xingtera", +[7][0x59 - 1] = "Netsol", +[7][0x5a - 1] = "Bestdon Technology Co Ltd", +[7][0x5b - 1] = "Baysand Inc", +[7][0x5c - 1] = "Uroad Technology Co Ltd", +[7][0x5d - 1] = "Wilk Elektronik S.A.", +[7][0x5e - 1] = "AAI", +[7][0x5f - 1] = "Harman", +[7][0x60 - 1] = "Berg Microelectronics Inc", +[7][0x61 - 1] = "ASSIA Inc", +[7][0x62 - 1] = "Visiontek Products LLC", +[7][0x63 - 1] = "OCMEMORY", +[7][0x64 - 1] = "Welink Solution Inc", +[7][0x65 - 1] = "Shark Gaming", +[7][0x66 - 1] = "Avalanche Technology", +[7][0x67 - 1] = "R&D Center ELVEES OJSC", +[7][0x68 - 1] = "KingboMars Technology Co Ltd", +[7][0x69 - 1] = "High Bridge Solutions Industria Eletronica", +[7][0x6a - 1] = "Transcend Technology Co Ltd", +[7][0x6b - 1] = "Everspin Technologies", +[7][0x6c - 1] = "Hon-Hai Precision", +[7][0x6d - 1] = "Smart Storage Systems", +[7][0x6e - 1] = "Toumaz Group", +[7][0x6f - 1] = "Zentel Electronics Corporation", +[7][0x70 - 1] = "Panram International Corporation", +[7][0x71 - 1] = "Silicon Space Technology", +[7][0x72 - 1] = "LITE-ON IT Corporation", +[7][0x73 - 1] = "Inuitive", +[7][0x74 - 1] = "HMicro", +[7][0x75 - 1] = "BittWare Inc", +[7][0x76 - 1] = "GLOBALFOUNDRIES", +[7][0x77 - 1] = "ACPI Digital Co Ltd", +[7][0x78 - 1] = "Annapurna Labs", +[7][0x79 - 1] = "AcSiP Technology Corporation", +[7][0x7a - 1] = "Idea! Electronic Systems", +[7][0x7b - 1] = "Gowe Technology Co Ltd", +[7][0x7c - 1] = "Hermes Testing Solutions Inc", +[7][0x7d - 1] = "Positivo BGH", +[7][0x7e - 1] = "Intelligence Silicon Technology", +[8][0x01 - 1] = "3D PLUS", +[8][0x02 - 1] = "Diehl Aerospace", +[8][0x03 - 1] = "Fairchild", +[8][0x04 - 1] = "Mercury Systems", +[8][0x05 - 1] = "Sonics Inc", +[8][0x06 - 1] = "Emerson Automation Solutions", +[8][0x07 - 1] = "Shenzhen Jinge Information Co Ltd", +[8][0x08 - 1] = "SCWW", +[8][0x09 - 1] = "Silicon Motion Inc", +[8][0x0a - 1] = "Anurag", +[8][0x0b - 1] = "King Kong", +[8][0x0c - 1] = "FROM30 Co Ltd", +[8][0x0d - 1] = "Gowin Semiconductor Corp", +[8][0x0e - 1] = "Fremont Micro Devices Ltd", +[8][0x0f - 1] = "Ericsson Modems", +[8][0x10 - 1] = "Exelis", +[8][0x11 - 1] = "Satixfy Ltd", +[8][0x12 - 1] = "Galaxy Microsystems Ltd", +[8][0x13 - 1] = "Gloway International Co Ltd", +[8][0x14 - 1] = "Lab", +[8][0x15 - 1] = "Smart Energy Instruments", +[8][0x16 - 1] = "Approved Memory Corporation", +[8][0x17 - 1] = "Axell Corporation", +[8][0x18 - 1] = "Essencore Limited", +[8][0x19 - 1] = "Phytium", +[8][0x1a - 1] = "Xi'an UniIC Semiconductors Co Ltd", +[8][0x1b - 1] = "Ambiq Micro", +[8][0x1c - 1] = "eveRAM Technology Inc", +[8][0x1d - 1] = "Infomax", +[8][0x1e - 1] = "Butterfly Network Inc", +[8][0x1f - 1] = "Shenzhen City Gcai Electronics", +[8][0x20 - 1] = "Stack Devices Corporation", +[8][0x21 - 1] = "ADK Media Group", +[8][0x22 - 1] = "TSP Global Co Ltd", +[8][0x23 - 1] = "HighX", +[8][0x24 - 1] = "Shenzhen Elicks Technology", +[8][0x25 - 1] = "XinKai/Silicon Kaiser", +[8][0x26 - 1] = "Google Inc", +[8][0x27 - 1] = "Dasima International Development", +[8][0x28 - 1] = "Leahkinn Technology Limited", +[8][0x29 - 1] = "HIMA Paul Hildebrandt GmbH Co KG", +[8][0x2a - 1] = "Keysight Technologies", +[8][0x2b - 1] = "Techcomp International (Fastable)", +[8][0x2c - 1] = "Ancore Technology Corporation", +[8][0x2d - 1] = "Nuvoton", +[8][0x2e - 1] = "Korea Uhbele International Group Ltd", +[8][0x2f - 1] = "Ikegami Tsushinki Co Ltd", +[8][0x30 - 1] = "RelChip Inc", +[8][0x31 - 1] = "Baikal Electronics", +[8][0x32 - 1] = "Nemostech Inc", +[8][0x33 - 1] = "Memorysolution GmbH", +[8][0x34 - 1] = "Silicon Integrated Systems Corporation", +[8][0x35 - 1] = "Xiede", +[8][0x36 - 1] = "BRC", +[8][0x37 - 1] = "Flash Chi", +[8][0x38 - 1] = "Jone", +[8][0x39 - 1] = "GCT Semiconductor Inc", +[8][0x3a - 1] = "Hong Kong Zetta Device Technology", +[8][0x3b - 1] = "Unimemory Technology(s) Pte Ltd", +[8][0x3c - 1] = "Cuso", +[8][0x3d - 1] = "Kuso", +[8][0x3e - 1] = "Uniquify Inc", +[8][0x3f - 1] = "Skymedi Corporation", +[8][0x40 - 1] = "Core Chance Co Ltd", +[8][0x41 - 1] = "Tekism Co Ltd", +[8][0x42 - 1] = "Seagate Technology PLC", +[8][0x43 - 1] = "Hong Kong Gaia Group Co Limited", +[8][0x44 - 1] = "Gigacom Semiconductor LLC", +[8][0x45 - 1] = "V2 Technologies", +[8][0x46 - 1] = "TLi", +[8][0x47 - 1] = "Neotion", +[8][0x48 - 1] = "Lenovo", +[8][0x49 - 1] = "Shenzhen Zhongteng Electronic Corp Ltd", +[8][0x4a - 1] = "Compound Photonics", +[8][0x4b - 1] = "in2H2 inc", +[8][0x4c - 1] = "Shenzhen Pango Microsystems Co Ltd", +[8][0x4d - 1] = "Vasekey", +[8][0x4e - 1] = "Cal-Comp Industria de Semicondutores", +[8][0x4f - 1] = "Eyenix Co Ltd", +[8][0x50 - 1] = "Heoriady", +[8][0x51 - 1] = "Accelerated Memory Production Inc", +[8][0x52 - 1] = "INVECAS Inc", +[8][0x53 - 1] = "AP Memory", +[8][0x54 - 1] = "Douqi Technology", +[8][0x55 - 1] = "Etron Technology Inc", +[8][0x56 - 1] = "Indie Semiconductor", +[8][0x57 - 1] = "Socionext Inc", +[8][0x58 - 1] = "HGST", +[8][0x59 - 1] = "EVGA", +[8][0x5a - 1] = "Audience Inc", +[8][0x5b - 1] = "EpicGear", +[8][0x5c - 1] = "Vitesse Enterprise Co", +[8][0x5d - 1] = "Foxtronn International Corporation", +[8][0x5e - 1] = "Bretelon Inc", +[8][0x5f - 1] = "Graphcore", +[8][0x60 - 1] = "Eoplex Inc", +[8][0x61 - 1] = "MaxLinear Inc", +[8][0x62 - 1] = "ETA Devices", +[8][0x63 - 1] = "LOKI", +[8][0x64 - 1] = "IMS Electronics Co Ltd", +[8][0x65 - 1] = "Dosilicon Co Ltd", +[8][0x66 - 1] = "Dolphin Integration", +[8][0x67 - 1] = "Shenzhen Mic Electronics Technolog", +[8][0x68 - 1] = "Boya Microelectronics Inc", +[8][0x69 - 1] = "Geniachip (Roche)", +[8][0x6a - 1] = "Axign", +[8][0x6b - 1] = "Kingred Electronic Technology Ltd", +[8][0x6c - 1] = "Chao Yue Zhuo Computer Business Dept.", +[8][0x6d - 1] = "Guangzhou Si Nuo Electronic Technology.", +[8][0x6e - 1] = "Crocus Technology Inc", +[8][0x6f - 1] = "Creative Chips GmbH", +[8][0x70 - 1] = "GE Aviation Systems LLC.", +[8][0x71 - 1] = "Asgard", +[8][0x72 - 1] = "Good Wealth Technology Ltd", +[8][0x73 - 1] = "TriCor Technologies", +[8][0x74 - 1] = "Nova-Systems GmbH", +[8][0x75 - 1] = "JUHOR", +[8][0x76 - 1] = "Zhuhai Douke Commerce Co Ltd", +[8][0x77 - 1] = "DSL Memory", +[8][0x78 - 1] = "Anvo-Systems Dresden GmbH", +[8][0x79 - 1] = "Realtek", +[8][0x7a - 1] = "AltoBeam", +[8][0x7b - 1] = "Wave Computing", +[8][0x7c - 1] = "Beijing TrustNet Technology Co Ltd", +[8][0x7d - 1] = "Innovium Inc", +[8][0x7e - 1] = "Starsway Technology Limited", +[9][0x01 - 1] = "Weltronics Co LTD", +[9][0x02 - 1] = "VMware Inc", +[9][0x03 - 1] = "Hewlett Packard Enterprise", +[9][0x04 - 1] = "INTENSO", +[9][0x05 - 1] = "Puya Semiconductor", +[9][0x06 - 1] = "MEMORFI", +[9][0x07 - 1] = "MSC Technologies GmbH", +[9][0x08 - 1] = "Txrui", +[9][0x09 - 1] = "SiFive Inc", +[9][0x0a - 1] = "Spreadtrum Communications", +[9][0x0b - 1] = "XTX Technology Limited", +[9][0x0c - 1] = "UMAX Technology", +[9][0x0d - 1] = "Shenzhen Yong Sheng Technology", +[9][0x0e - 1] = "SNOAMOO (Shenzhen Kai Zhuo Yue)", +[9][0x0f - 1] = "Daten Tecnologia LTDA", +[9][0x10 - 1] = "Shenzhen XinRuiYan Electronics", +[9][0x11 - 1] = "Eta Compute", +[9][0x12 - 1] = "Energous", +[9][0x13 - 1] = "Raspberry Pi Trading Ltd", +[9][0x14 - 1] = "Shenzhen Chixingzhe Tech Co Ltd", +[9][0x15 - 1] = "Silicon Mobility", +[9][0x16 - 1] = "IQ-Analog Corporation", +[9][0x17 - 1] = "Uhnder Inc", +[9][0x18 - 1] = "Impinj", +[9][0x19 - 1] = "DEPO Computers", +[9][0x1a - 1] = "Nespeed Sysems", +[9][0x1b - 1] = "Yangtze Memory Technologies Co Ltd", +[9][0x1c - 1] = "MemxPro Inc", +[9][0x1d - 1] = "Tammuz Co Ltd", +[9][0x1e - 1] = "Allwinner Technology", +[9][0x1f - 1] = "Shenzhen City Futian District Qing Xuan Tong Computer Trading Firm", +[9][0x20 - 1] = "XMC", +[9][0x21 - 1] = "Teclast", +[9][0x22 - 1] = "Maxsun", +[9][0x23 - 1] = "Haiguang Integrated Circuit Design", +[9][0x24 - 1] = "RamCENTER Technology", +[9][0x25 - 1] = "Phison Electronics Corporation", +[9][0x26 - 1] = "Guizhou Huaxintong Semi-Conductor", +[9][0x27 - 1] = "Network Intelligence", +[9][0x28 - 1] = "Continental Technology (Holdings)", +[9][0x29 - 1] = "Guangzhou Huayan Suning Electronic", +[9][0x2a - 1] = "Guangzhou Zhouji Electronic Co Ltd", +[9][0x2b - 1] = "Shenzhen Giant Hui Kang Tech Co Ltd", +[9][0x2c - 1] = "Shenzhen Yilong Innovative Co Ltd", +[9][0x2d - 1] = "Neo Forza", +[9][0x2e - 1] = "Lyontek Inc", +[9][0x2f - 1] = "Shanghai Kuxin Microelectronics Ltd", +[9][0x30 - 1] = "Shenzhen Larix Technology Co Ltd", +[9][0x31 - 1] = "Qbit Semiconductor Ltd", +[9][0x32 - 1] = "Insignis Technology Corporation", +[9][0x33 - 1] = "Lanson Memory Co Ltd", +[9][0x34 - 1] = "Shenzhen Superway Electronics Co Ltd", +[9][0x35 - 1] = "Canaan-Creative Co Ltd", +[9][0x36 - 1] = "Black Diamond Memory", +[9][0x37 - 1] = "Shenzhen City Parker Baking Electronics", +[9][0x38 - 1] = "Shenzhen Baihong Technology Co Ltd", +[9][0x39 - 1] = "GEO Semiconductors", +[9][0x3a - 1] = "OCPC", +[9][0x3b - 1] = "Artery Technology Co Ltd", +[9][0x3c - 1] = "Jinyu", +[9][0x3d - 1] = "ShenzhenYing Chi Technology Development", +[9][0x3e - 1] = "Shenzhen Pengcheng Xin Technology", +[9][0x3f - 1] = "Pegasus Semiconductor (Shanghai) Co", +[9][0x40 - 1] = "Mythic Inc", +[9][0x41 - 1] = "Elmos Semiconductor AG", +[9][0x42 - 1] = "Kllisre", +[9][0x43 - 1] = "Shenzhen Winconway Technology", +[9][0x44 - 1] = "Shenzhen Xingmem Technology Corp", +[9][0x45 - 1] = "Gold Key Technology Co Ltd", +[9][0x46 - 1] = "Habana Labs Ltd", +[9][0x47 - 1] = "Hoodisk Electronics Co Ltd", +[9][0x48 - 1] = "SemsoTai (SZ) Technology Co Ltd", +[9][0x49 - 1] = "OM Nanotech Pvt. Ltd", +[9][0x4a - 1] = "Shenzhen Zhifeng Weiye Technology", +[9][0x4b - 1] = "Xinshirui (Shenzhen) Electronics Co", +[9][0x4c - 1] = "Guangzhou Zhong Hao Tian Electronic", +[9][0x4d - 1] = "Shenzhen Longsys Electronics Co Ltd", +[9][0x4e - 1] = "Deciso B.V.", +[9][0x4f - 1] = "Puya Semiconductor (Shenzhen)", +[9][0x50 - 1] = "Shenzhen Veineda Technology Co Ltd", +[9][0x51 - 1] = "Antec Memory", +[9][0x52 - 1] = "Cortus SAS", +[9][0x53 - 1] = "Dust Leopard", +[9][0x54 - 1] = "MyWo AS", +[9][0x55 - 1] = "J&A Information Inc", +[9][0x56 - 1] = "Shenzhen JIEPEI Technology Co Ltd", +[9][0x57 - 1] = "Heidelberg University", +[9][0x58 - 1] = "Flexxon PTE Ltd", +[9][0x59 - 1] = "Wiliot", +[9][0x5a - 1] = "Raysun Electronics International Ltd", +[9][0x5b - 1] = "Aquarius Production Company LLC", +[9][0x5c - 1] = "MACNICA DHW LTDA", +[9][0x5d - 1] = "Intelimem", +[9][0x5e - 1] = "Zbit Semiconductor Inc", +[9][0x5f - 1] = "Shenzhen Technology Co Ltd", +[9][0x60 - 1] = "Signalchip", +[9][0x61 - 1] = "Shenzen Recadata Storage Technology", +[9][0x62 - 1] = "Hyundai Technology", +[9][0x63 - 1] = "Shanghai Fudi Investment Development", +[9][0x64 - 1] = "Aixi Technology", +[9][0x65 - 1] = "Tecon MT", +[9][0x66 - 1] = "Onda Electric Co Ltd", +[9][0x67 - 1] = "Jinshen", +[9][0x68 - 1] = "Kimtigo Semiconductor (HK) Limited", +[9][0x69 - 1] = "IIT Madras", +[9][0x6a - 1] = "Shenshan (Shenzhen) Electronic", +[9][0x6b - 1] = "Hefei Core Storage Electronic Limited", +[9][0x6c - 1] = "Colorful Technology Ltd", +[9][0x6d - 1] = "Visenta (Xiamen) Technology Co Ltd", +[9][0x6e - 1] = "Roa Logic BV", +[9][0x6f - 1] = "NSITEXE Inc", +[9][0x70 - 1] = "Hong Kong Hyunion Electronics", +[9][0x71 - 1] = "ASK Technology Group Limited", +[9][0x72 - 1] = "GIGA-BYTE Technology Co Ltd", +[9][0x73 - 1] = "Terabyte Co Ltd", +[9][0x74 - 1] = "Hyundai Inc", +[9][0x75 - 1] = "EXCELERAM", +[9][0x76 - 1] = "PsiKick", +[9][0x77 - 1] = "Netac Technology Co Ltd", +[9][0x78 - 1] = "PCCOOLER", +[9][0x79 - 1] = "Jiangsu Huacun Electronic Technology", +[9][0x7a - 1] = "Shenzhen Micro Innovation Industry", +[9][0x7b - 1] = "Beijing Tongfang Microelectronics Co", +[9][0x7c - 1] = "XZN Storage Technology", +[9][0x7d - 1] = "ChipCraft Sp. z.o.o.", +[9][0x7e - 1] = "ALLFLASH Technology Limited", +[10][0x01 - 1] = "Foerd Technology Co Ltd", +[10][0x02 - 1] = "KingSpec", +[10][0x03 - 1] = "Codasip GmbH", +[10][0x04 - 1] = "SL Link Co Ltd", +[10][0x05 - 1] = "Shenzhen Kefu Technology Co Limited", +[10][0x06 - 1] = "Shenzhen ZST Electronics Technology", +[10][0x07 - 1] = "Kyokuto Electronic Inc", +[10][0x08 - 1] = "Warrior Technology", +[10][0x09 - 1] = "TRINAMIC Motion Control GmbH & Co", +[10][0x0a - 1] = "PixelDisplay Inc", +[10][0x0b - 1] = "Shenzhen Futian District Bo Yueda Elec", +[10][0x0c - 1] = "Richtek Power", +[10][0x0d - 1] = "Shenzhen LianTeng Electronics Co Ltd", +[10][0x0e - 1] = "AITC Memory", +[10][0x0f - 1] = "UNIC Memory Technology Co Ltd", +[10][0x10 - 1] = "Shenzhen Huafeng Science Technology", +[10][0x11 - 1] = "CXMT", +[10][0x12 - 1] = "Guangzhou Xinyi Heng Computer Trading Firm", +[10][0x13 - 1] = "SambaNova Systems", +[10][0x14 - 1] = "V-GEN", +[10][0x15 - 1] = "Jump Trading", +[10][0x16 - 1] = "Ampere Computing", +[10][0x17 - 1] = "Shenzhen Zhongshi Technology Co Ltd", +[10][0x18 - 1] = "Shenzhen Zhongtian Bozhong Technology", +[10][0x19 - 1] = "Tri-Tech International", +[10][0x1a - 1] = "Silicon Intergrated Systems Corporation", +[10][0x1b - 1] = "Shenzhen HongDingChen Information", +[10][0x1c - 1] = "Plexton Holdings Limited", +[10][0x1d - 1] = "AMS (Jiangsu Advanced Memory Semi)", +[10][0x1e - 1] = "Wuhan Jing Tian Interconnected Tech Co", +[10][0x1f - 1] = "Axia Memory Technology", +[10][0x20 - 1] = "Chipset Technology Holding Limited", +[10][0x21 - 1] = "Shenzhen Xinshida Technology Co Ltd", +[10][0x22 - 1] = "Shenzhen Chuangshifeida Technology", +[10][0x23 - 1] = "Guangzhou MiaoYuanJi Technology", +[10][0x24 - 1] = "ADVAN Inc", +[10][0x25 - 1] = "Shenzhen Qianhai Weishengda Electronic Commerce Company Ltd", +[10][0x26 - 1] = "Guangzhou Guang Xie Cheng Trading", +[10][0x27 - 1] = "StarRam International Co Ltd", +[10][0x28 - 1] = "Shen Zhen XinShenHua Tech Co Ltd", +[10][0x29 - 1] = "UltraMemory Inc", +[10][0x2a - 1] = "New Coastline Global Tech Industry Co", +[10][0x2b - 1] = "Sinker", +[10][0x2c - 1] = "Diamond", +[10][0x2d - 1] = "PUSKILL", +[10][0x2e - 1] = "Guangzhou Hao Jia Ye Technology Co", +[10][0x2f - 1] = "Ming Xin Limited", +[10][0x30 - 1] = "Barefoot Networks", +[10][0x31 - 1] = "Biwin Semiconductor (HK) Co Ltd", +[10][0x32 - 1] = "UD INFO Corporation", +[10][0x33 - 1] = "Trek Technology (S) PTE Ltd", +[10][0x34 - 1] = "Xiamen Kingblaze Technology Co Ltd", +[10][0x35 - 1] = "Shenzhen Lomica Technology Co Ltd", +[10][0x36 - 1] = "Nuclei System Technology Co Ltd", +[10][0x37 - 1] = "Wuhan Xun Zhan Electronic Technology", +[10][0x38 - 1] = "Shenzhen Ingacom Semiconductor Ltd", +[10][0x39 - 1] = "Zotac Technology Ltd", +[10][0x3a - 1] = "Foxline", +[10][0x3b - 1] = "Shenzhen Farasia Science Technology", +[10][0x3c - 1] = "Efinix Inc", +[10][0x3d - 1] = "Hua Nan San Xian Technology Co Ltd", +[10][0x3e - 1] = "Goldtech Electronics Co Ltd", +[10][0x3f - 1] = "Shanghai Han Rong Microelectronics Co", +[10][0x40 - 1] = "Shenzhen Zhongguang Yunhe Trading", +[10][0x41 - 1] = "Smart Shine(QingDao) Microelectronics", +[10][0x42 - 1] = "Thermaltake Technology Co Ltd", +[10][0x43 - 1] = "Shenzhen O'Yang Maile Technology Ltd", +[10][0x44 - 1] = "UPMEM", +[10][0x45 - 1] = "Chun Well Technology Holding Limited", +[10][0x46 - 1] = "Astera Labs Inc", +[10][0x47 - 1] = "Winconway", +[10][0x48 - 1] = "Advantech Co Ltd", +[10][0x49 - 1] = "Chengdu Fengcai Electronic Technology", +[10][0x4a - 1] = "The Boeing Company", +[10][0x4b - 1] = "Blaize Inc", +[10][0x4c - 1] = "Ramonster Technology Co Ltd", +[10][0x4d - 1] = "Wuhan Naonongmai Technology Co Ltd", +[10][0x4e - 1] = "Shenzhen Hui ShingTong Technology", +[10][0x4f - 1] = "Yourlyon", +[10][0x50 - 1] = "Fabu Technology", +[10][0x51 - 1] = "Shenzhen Yikesheng Technology Co Ltd", +[10][0x52 - 1] = "NOR-MEM", +[10][0x53 - 1] = "Cervoz Co Ltd", +[10][0x54 - 1] = "Bitmain Technologies Inc.", +[10][0x55 - 1] = "Facebook Inc", +[10][0x56 - 1] = "Shenzhen Longsys Electronics Co Ltd", +[10][0x57 - 1] = "Guangzhou Siye Electronic Technology", +[10][0x58 - 1] = "Silergy", +[10][0x59 - 1] = "Adamway", +[10][0x5a - 1] = "PZG", +[10][0x5b - 1] = "Shenzhen King Power Electronics", +[10][0x5c - 1] = "Guangzhou ZiaoFu Tranding Co Ltd", +[10][0x5d - 1] = "Shenzhen SKIHOTAR Semiconductor", +[10][0x5e - 1] = "PulseRain Technology", +[10][0x5f - 1] = "Seeker Technology Limited", +[10][0x60 - 1] = "Shenzhen OSCOO Tech Co Ltd", +[10][0x61 - 1] = "Shenzhen Yze Technology Co Ltd", +[10][0x62 - 1] = "Shenzhen Jieshuo Electronic Commerce", +[10][0x63 - 1] = "Gazda", +[10][0x64 - 1] = "Hua Wei Technology Co Ltd", +[10][0x65 - 1] = "Esperanto Technologies", +[10][0x66 - 1] = "JinSheng Electronic (Shenzhen) Co Ltd", +[10][0x67 - 1] = "Shenzhen Shi Bolunshuai Technology", +[10][0x68 - 1] = "Shanghai Rei Zuan Information Tech", +[10][0x69 - 1] = "Fraunhofer IIS", +[10][0x6a - 1] = "Kandou Bus SA", +[10][0x6b - 1] = "Acer", +[10][0x6c - 1] = "Artmem Technology Co Ltd", +[10][0x6d - 1] = "Gstar Semiconductor Co Ltd", +[10][0x6e - 1] = "ShineDisk", +[10][0x6f - 1] = "Shenzhen CHN Technology Co Ltd", +[10][0x70 - 1] = "UnionChip Semiconductor Co Ltd", +[10][0x71 - 1] = "Tanbassh", +[10][0x72 - 1] = "Shenzhen Tianyu Jieyun Intl Logistics", +[10][0x73 - 1] = "MCLogic Inc", +[10][0x74 - 1] = "Eorex Corporation", +[10][0x75 - 1] = "Arm Technology (China) Co Ltd", +[10][0x76 - 1] = "Lexar Co Limited", +[10][0x77 - 1] = "QinetiQ Group plc", +[10][0x78 - 1] = "Exascend", +[10][0x79 - 1] = "Hong Kong Hyunion Electronics Co Ltd", +[10][0x7a - 1] = "Shenzhen Banghong Electronics Co Ltd", +[10][0x7b - 1] = "MBit Wireless Inc", +[10][0x7c - 1] = "Hex Five Security Inc", +[10][0x7d - 1] = "ShenZhen Juhor Precision Tech Co Ltd", +[10][0x7e - 1] = "Shenzhen Reeinno Technology Co Ltd", +[11][0x01 - 1] = "ABIT Electronics (Shenzhen) Co Ltd", +[11][0x02 - 1] = "Semidrive", +[11][0x03 - 1] = "MyTek Electronics Corp", +[11][0x04 - 1] = "Wxilicon Technology Co Ltd", +[11][0x05 - 1] = "Shenzhen Meixin Electronics Ltd", +[11][0x06 - 1] = "Ghost Wolf", +[11][0x07 - 1] = "LiSion Technologies Inc", +[11][0x08 - 1] = "Power Active Co Ltd", +[11][0x09 - 1] = "Pioneer High Fidelity Taiwan Co. Ltd", +[11][0x0a - 1] = "LuoSilk", +[11][0x0b - 1] = "Shenzhen Chuangshifeida Technology", +[11][0x0c - 1] = "Black Sesame Technologies Inc", +[11][0x0d - 1] = "Jiangsu Xinsheng Intelligent Technology", +[11][0x0e - 1] = "MLOONG", +[11][0x0f - 1] = "Quadratica LLC", +[11][0x10 - 1] = "Anpec Electronics", +[11][0x11 - 1] = "Xi'an Morebeck Semiconductor Tech Co", +[11][0x12 - 1] = "Kingbank Technology Co Ltd", +[11][0x13 - 1] = "ITRenew Inc", +[11][0x14 - 1] = "Shenzhen Eaget Innovation Tech Ltd", +[11][0x15 - 1] = "Jazer", +[11][0x16 - 1] = "Xiamen Semiconductor Investment Group", +[11][0x17 - 1] = "Guangzhou Longdao Network Tech Co", +[11][0x18 - 1] = "Shenzhen Futian SEC Electronic Market", +[11][0x19 - 1] = "Allegro Microsystems LLC", +[11][0x1a - 1] = "Hunan RunCore Innovation Technology", +[11][0x1b - 1] = "C-Corsa Technology", +[11][0x1c - 1] = "Zhuhai Chuangfeixin Technology Co Ltd", +[11][0x1d - 1] = "Beijing InnoMem Technologies Co Ltd", +[11][0x1e - 1] = "YooTin", +[11][0x1f - 1] = "Shenzhen Pengxiong Technology Co Ltd", +[11][0x20 - 1] = "Dongguan Yingbang Commercial Trading Co", +[11][0x21 - 1] = "Shenzhen Ronisys Electronics Co Ltd", +[11][0x22 - 1] = "Hongkong Xinlan Guangke Co Ltd", +[11][0x23 - 1] = "Apex Microelectronics Co Ltd", +[11][0x24 - 1] = "Beijing Hongda Jinming Technology Co Ltd", +[11][0x25 - 1] = "Ling Rui Technology (Shenzhen) Co Ltd", +[11][0x26 - 1] = "Hongkong Hyunion Electronics Co Ltd", +[11][0x27 - 1] = "Starsystems Inc", +[11][0x28 - 1] = "Shenzhen Yingjiaxun Industrial Co Ltd", +[11][0x29 - 1] = "Dongguan Crown Code Electronic Commerce", +[11][0x2a - 1] = "Monolithic Power Systems Inc", +[11][0x2b - 1] = "WuHan SenNaiBo E-Commerce Co Ltd", +[11][0x2c - 1] = "Hangzhou Hikstorage Technology Co", +[11][0x2d - 1] = "Shenzhen Goodix Technology Co Ltd", +[11][0x2e - 1] = "Aigo Electronic Technology Co Ltd", +[11][0x2f - 1] = "Hefei Konsemi Storage Technology Co Ltd", +[11][0x30 - 1] = "Cactus Technologies Limited", +[11][0x31 - 1] = "DSIN", +[11][0x32 - 1] = "Blu Wireless Technology", +[11][0x33 - 1] = "Nanjing UCUN Technology Inc", +[11][0x34 - 1] = "Acacia Communications", +[11][0x35 - 1] = "Beijinjinshengyihe Technology Co Ltd", +[11][0x36 - 1] = "Zyzyx", +[11][0x37 - 1] = "T-HEAD Semiconductor Co Ltd", +[11][0x38 - 1] = "Shenzhen Hystou Technology Co Ltd", +[11][0x39 - 1] = "Syzexion", +[11][0x3a - 1] = "Kembona", +[11][0x3b - 1] = "Qingdao Thunderobot Technology Co Ltd", +[11][0x3c - 1] = "Morse Micro", +[11][0x3d - 1] = "Shenzhen Envida Technology Co Ltd", +[11][0x3e - 1] = "UDStore Solution Limited", +[11][0x3f - 1] = "Shunlie", +[11][0x40 - 1] = "Shenzhen Xin Hong Rui Tech Ltd", +[11][0x41 - 1] = "Shenzhen Yze Technology Co Ltd", +[11][0x42 - 1] = "Shenzhen Huang Pu He Xin Technology", +[11][0x43 - 1] = "Xiamen Pengpai Microelectronics Co Ltd", +[11][0x44 - 1] = "JISHUN", +[11][0x45 - 1] = "Shenzhen WODPOSIT Technology Co", +[11][0x46 - 1] = "Unistar", +[11][0x47 - 1] = "UNICORE Electronic (Suzhou) Co Ltd", +[11][0x48 - 1] = "Axonne Inc", +[11][0x49 - 1] = "Shenzhen SOVERECA Technology Co", +[11][0x4a - 1] = "Dire Wolf", +[11][0x4b - 1] = "Whampoa Core Technology Co Ltd", +[11][0x4c - 1] = "CSI Halbleiter GmbH", +[11][0x4d - 1] = "ONE Semiconductor", +[11][0x4e - 1] = "SimpleMachines Inc", +[11][0x4f - 1] = "Shenzhen Chengyi Qingdian Electronic", +[11][0x50 - 1] = "Shenzhen Xinlianxin Network Technology", +[11][0x51 - 1] = "Vayyar Imaging Ltd", +[11][0x52 - 1] = "Paisen Network Technology Co Ltd", +[11][0x53 - 1] = "Shenzhen Fengwensi Technology Co Ltd", +[11][0x54 - 1] = "Caplink Technology Limited", +[11][0x55 - 1] = "JJT Solution Co Ltd", +[11][0x56 - 1] = "HOSIN Global Electronics Co Ltd", +[11][0x57 - 1] = "Shenzhen KingDisk Century Technology", +[11][0x58 - 1] = "SOYO", +[11][0x59 - 1] = "DIT Technology Co Ltd", +[11][0x5a - 1] = "iFound", +[11][0x5b - 1] = "Aril Computer Company", +[11][0x5c - 1] = "ASUS", +[11][0x5d - 1] = "Shenzhen Ruiyingtong Technology Co", +[11][0x5e - 1] = "HANA Micron", +[11][0x5f - 1] = "RANSOR", +[11][0x60 - 1] = "Axiado Corporation", +[11][0x61 - 1] = "Tesla Corporation", +[11][0x62 - 1] = "Pingtouge (Shanghai) Semiconductor Co", +[11][0x63 - 1] = "S3Plus Technologies SA", +[11][0x64 - 1] = "Integrated Silicon Solution Israel Ltd", +[11][0x65 - 1] = "GreenWaves Technologies", +[11][0x66 - 1] = "NUVIA Inc", +[11][0x67 - 1] = "Guangzhou Shuvrwine Technology Co", +[11][0x68 - 1] = "Shenzhen Hangshun Chip Technology", +[11][0x69 - 1] = "Chengboliwei Electronic Business", +[11][0x6a - 1] = "Kowin Technology HK Limited", +[11][0x6b - 1] = "Euronet Technology Inc", +[11][0x6c - 1] = "SCY", +[11][0x6d - 1] = "Shenzhen Xinhongyusheng Electrical", +[11][0x6e - 1] = "PICOCOM", +[11][0x6f - 1] = "Shenzhen Toooogo Memory Technology", +[11][0x70 - 1] = "VLSI Solution", +[11][0x71 - 1] = "Costar Electronics Inc", +[11][0x72 - 1] = "Shenzhen Huatop Technology Co Ltd", +[11][0x73 - 1] = "Inspur Electronic Information Industry", +[11][0x74 - 1] = "Shenzhen Boyuan Computer Technology", +[11][0x75 - 1] = "Beijing Welldisk Electronics Co Ltd", +[11][0x76 - 1] = "Suzhou EP Semicon Co Ltd", +[11][0x77 - 1] = "Zhejiang Dahua Memory Technology", +[11][0x78 - 1] = "Virtu Financial", +[11][0x79 - 1] = "Datotek International Co Ltd", +[11][0x7a - 1] = "Telecom and Microelectronics Industries", +[11][0x7b - 1] = "Echow Technology Ltd", +[11][0x7c - 1] = "APEX-INFO", +[11][0x7d - 1] = "Yingpark", +[11][0x7e - 1] = "Shenzhen Bigway Tech Co Ltd", +[12][0x01 - 1] = "Beijing Haawking Technology Co Ltd", +[12][0x02 - 1] = "Open HW Group", +[12][0x03 - 1] = "JHICC", +[12][0x04 - 1] = "ncoder AG", +[12][0x05 - 1] = "ThinkTech Information Technology Co", +[12][0x06 - 1] = "Shenzhen Chixingzhe Technology Co Ltd", +[12][0x07 - 1] = "Biao Ram Technology Co Ltd", +[12][0x08 - 1] = "Shenzhen Kaizhuoyue Electronics Co Ltd", +[12][0x09 - 1] = "Shenzhen YC Storage Technology Co Ltd", +[12][0x0a - 1] = "Shenzhen Chixingzhe Technology Co", +[12][0x0b - 1] = "Wink Semiconductor (Shenzhen) Co Ltd", +[12][0x0c - 1] = "AISTOR", +[12][0x0d - 1] = "Palma Ceia SemiDesign", +[12][0x0e - 1] = "EM Microelectronic-Marin SA", +[12][0x0f - 1] = "Shenzhen Monarch Memory Technology", +[12][0x10 - 1] = "Reliance Memory Inc", +[12][0x11 - 1] = "Jesis", +[12][0x12 - 1] = "Espressif Systems (Shanghai) Co Ltd", +[12][0x13 - 1] = "Shenzhen Sati Smart Technology Co Ltd", +[12][0x14 - 1] = "NeuMem Co Ltd", +[12][0x15 - 1] = "Lifelong", +[12][0x16 - 1] = "Beijing Oitech Technology Co Ltd", +[12][0x17 - 1] = "Groupe LDLC", +[12][0x18 - 1] = "Semidynamics Technology Services SLU", +[12][0x19 - 1] = "swordbill", +[12][0x1a - 1] = "YIREN", +[12][0x1b - 1] = "Shenzhen Yinxiang Technology Co Ltd", +[12][0x1c - 1] = "PoweV Electronic Technology Co Ltd", +[12][0x1d - 1] = "LEORICE", +[12][0x1e - 1] = "Waymo LLC", +[12][0x1f - 1] = "Ventana Micro Systems", +[12][0x20 - 1] = "Hefei Guangxin Microelectronics Co Ltd", +[12][0x21 - 1] = "Shenzhen Sooner Industrial Co Ltd", +[12][0x22 - 1] = "Horizon Robotics", +[12][0x23 - 1] = "Tangem AG", +[12][0x24 - 1] = "FuturePath Technology (Shenzhen) Co", +[12][0x25 - 1] = "RC Module", +[12][0x26 - 1] = "Timetec International Inc", +[12][0x27 - 1] = "ICMAX Technologies Co Limited", +[12][0x28 - 1] = "Lynxi Technologies Ltd Co", +[12][0x29 - 1] = "Guangzhou Taisupanke Computer Equipment", +[12][0x2a - 1] = "Ceremorphic Inc", +[12][0x2b - 1] = "Biwin Storage Technology Co Ltd", +[12][0x2c - 1] = "Beijing ESWIN Computing Technology", +[12][0x2d - 1] = "WeForce Co Ltd", +[12][0x2e - 1] = "Shenzhen Fanxiang Information Technology", +[12][0x2f - 1] = "Unisoc", +[12][0x30 - 1] = "YingChu", +[12][0x31 - 1] = "GUANCUN", +[12][0x32 - 1] = "IPASON", +[12][0x33 - 1] = "Ayar Labs", +[12][0x34 - 1] = "Amazon", +[12][0x35 - 1] = "Shenzhen Xinxinshun Technology Co", +[12][0x36 - 1] = "Galois Inc", +[12][0x37 - 1] = "Ubilite Inc", +[12][0x38 - 1] = "Shenzhen Quanxing Technology Co Ltd", +[12][0x39 - 1] = "Group RZX Technology LTDA", +[12][0x3a - 1] = "Yottac Technology (XI'AN) Cooperation", +[12][0x3b - 1] = "Shenzhen RuiRen Technology Co Ltd", +[12][0x3c - 1] = "Group Star Technology Co Ltd", +[12][0x3d - 1] = "RWA (Hong Kong) Ltd", +[12][0x3e - 1] = "Genesys Logic Inc", +[12][0x3f - 1] = "T3 Robotics Inc.", +[12][0x40 - 1] = "Biostar Microtech International Corp", +[12][0x41 - 1] = "Shenzhen SXmicro Technology Co Ltd", +[12][0x42 - 1] = "Shanghai Yili Computer Technology Co", +[12][0x43 - 1] = "Zhixin Semicoducotor Co Ltd", +[12][0x44 - 1] = "uFound", +[12][0x45 - 1] = "Aigo Data Security Technology Co. Ltd", +[12][0x46 - 1] = ".GXore Technologies", +[12][0x47 - 1] = "Shenzhen Pradeon Intelligent Technology", +[12][0x48 - 1] = "Power LSI", +[12][0x49 - 1] = "PRIME", +[12][0x4a - 1] = "Shenzhen Juyang Innovative Technology", +[12][0x4b - 1] = "CERVO", +[12][0x4c - 1] = "SiEngine Technology Co., Ltd.", +[12][0x4d - 1] = "Beijing Unigroup Tsingteng MicroSystem", +[12][0x4e - 1] = "Brainsao GmbH", +[12][0x4f - 1] = "Credo Technology Group Ltd", +[12][0x50 - 1] = "Shanghai Biren Technology Co Ltd", +[12][0x51 - 1] = "Nucleu Semiconductor", +[12][0x52 - 1] = "Shenzhen Guangshuo Electronics Co Ltd", +[12][0x53 - 1] = "ZhongsihangTechnology Co Ltd", +[12][0x54 - 1] = "Suzhou Mainshine Electronic Co Ltd.", +[12][0x55 - 1] = "Guangzhou Riss Electronic Technology", +[12][0x56 - 1] = "Shenzhen Cloud Security Storage Co", +[12][0x57 - 1] = "ROG", +[12][0x58 - 1] = "Perceive", +[12][0x59 - 1] = "e-peas", +[12][0x5a - 1] = "Fraunhofer IPMS", +[12][0x5b - 1] = "Shenzhen Daxinlang Electronic Tech Co", +[12][0x5c - 1] = "Abacus Peripherals Private Limited", +[12][0x5d - 1] = "OLOy Technology", +[12][0x5e - 1] = "Wuhan P&S Semiconductor Co Ltd", +[12][0x5f - 1] = "Sitrus Technology", +[12][0x60 - 1] = "AnHui Conner Storage Co Ltd", +[12][0x61 - 1] = "Rochester Electronics", +[12][0x62 - 1] = "Wuxi Petabyte Technologies Co Ltd", +[12][0x63 - 1] = "Star Memory", +[12][0x64 - 1] = "Agile Memory Technology Co Ltd", +[12][0x65 - 1] = "MEJEC", +[12][0x66 - 1] = "Rockchip Electronics Co Ltd", +[12][0x67 - 1] = "Dongguan Guanma e-commerce Co Ltd", +[12][0x68 - 1] = "Rayson Hi-Tech (SZ) Limited", +[12][0x69 - 1] = "MINRES Technologies GmbH", +[12][0x6a - 1] = "Himax Technologies Inc", +[12][0x6b - 1] = "Shenzhen Cwinner Technology Co Ltd", +[12][0x6c - 1] = "Tecmiyo", +[12][0x6d - 1] = "Shenzhen Suhuicun Technology Co Ltd", +[12][0x6e - 1] = "Vickter Electronics Co. Ltd.", +[12][0x6f - 1] = "lowRISC", +[12][0x70 - 1] = "EXEGate FZE", +[12][0x71 - 1] = "Shenzhen 9 Chapter Technologies Co", +[12][0x72 - 1] = "Addlink", +[12][0x73 - 1] = "Starsway", +[12][0x74 - 1] = "Pensando Systems Inc.", +[12][0x75 - 1] = "AirDisk", +[12][0x76 - 1] = "Shenzhen Speedmobile Technology Co", +[12][0x77 - 1] = "PEZY Computing", +[12][0x78 - 1] = "Extreme Engineering Solutions Inc", +[12][0x79 - 1] = "Shangxin Technology Co Ltd", +[12][0x7a - 1] = "Shanghai Zhaoxin Semiconductor Co", +[12][0x7b - 1] = "Xsight Labs Ltd", +[12][0x7c - 1] = "Hangzhou Hikstorage Technology Co", +[12][0x7d - 1] = "Dell Technologies", +[12][0x7e - 1] = "Guangdong StarFive Technology Co", +[13][0x01 - 1] = "TECOTON", +[13][0x02 - 1] = "Abko Co Ltd", +[13][0x03 - 1] = "Shenzhen Feisrike Technology Co Ltd", +[13][0x04 - 1] = "Shenzhen Sunhome Electronics Co Ltd", +[13][0x05 - 1] = "Global Mixed-mode Technology Inc", +[13][0x06 - 1] = "Shenzhen Weien Electronics Co. Ltd.", +[13][0x07 - 1] = "Shenzhen Cooyes Technology Co Ltd", +[13][0x08 - 1] = "Keymos Electronics Co., Limited", +[13][0x09 - 1] = "E-Rockic Technology Company Limited", +[13][0x0a - 1] = "Aerospace Science Memory Shenzhen", +[13][0x0b - 1] = "Shenzhen Quanji Technology Co Ltd", +[13][0x0c - 1] = "Dukosi", +[13][0x0d - 1] = "Maxell Corporation of America", +[13][0x0e - 1] = "Shenshen Xinxintao Electronics Co Ltd", +[13][0x0f - 1] = "Zhuhai Sanxia Semiconductor Co Ltd", +[13][0x10 - 1] = "Groq Inc", +[13][0x11 - 1] = "AstraTek", +[13][0x12 - 1] = "Shenzhen Xinyuze Technology Co Ltd", +[13][0x13 - 1] = "All Bit Semiconductor", +[13][0x14 - 1] = "ACFlow", +[13][0x15 - 1] = "Shenzhen Sipeed Technology Co Ltd", +[13][0x16 - 1] = "Linzhi Hong Kong Co Limited", +[13][0x17 - 1] = "Supreme Wise Limited", +[13][0x18 - 1] = "Blue Cheetah Analog Design Inc", +[13][0x19 - 1] = "Hefei Laiku Technology Co Ltd", +[13][0x1a - 1] = "Zord", +[13][0x1b - 1] = "SBO Hearing A/S", +[13][0x1c - 1] = "Regent Sharp International Limited", +[13][0x1d - 1] = "Permanent Potential Limited", +[13][0x1e - 1] = "Creative World International Limited", +[13][0x1f - 1] = "Base Creation International Limited", +[13][0x20 - 1] = "Shenzhen Zhixin Chuanglian Technology", +[13][0x21 - 1] = "Protected Logic Corporation", +[13][0x22 - 1] = "Sabrent", +[13][0x23 - 1] = "Union Memory", +[13][0x24 - 1] = "NEUCHIPS Corporation", +[13][0x25 - 1] = "Ingenic Semiconductor Co Ltd", +[13][0x26 - 1] = "SiPearl", +[13][0x27 - 1] = "Shenzhen Actseno Information Technology", +[13][0x28 - 1] = "RIVAI Technologies (Shenzhen) Co Ltd", +[13][0x29 - 1] = "Shenzhen Sunny Technology Co Ltd", +[13][0x2a - 1] = "Cott Electronics Ltd", +[13][0x2b - 1] = "Shanghai Synsense Technologies Co Ltd", +[13][0x2c - 1] = "Shenzhen Jintang Fuming Optoelectronics", +[13][0x2d - 1] = "CloudBEAR LLC", +[13][0x2e - 1] = "Emzior, LLC", +[13][0x2f - 1] = "Ehiway Microelectronic Science Tech Co", +[13][0x30 - 1] = "UNIM Innovation Technology (Wu XI)", +[13][0x31 - 1] = "GDRAMARS", +[13][0x32 - 1] = "Meminsights Technology", +[13][0x33 - 1] = "Zhuzhou Hongda Electronics Corp Ltd", +[13][0x34 - 1] = "Luminous Computing Inc", +[13][0x35 - 1] = "PROXMEM", +[13][0x36 - 1] = "Draper Labs", +[13][0x37 - 1] = "ORICO Technologies Co. Ltd.", +[13][0x38 - 1] = "Space Exploration Technologies Corp", +[13][0x39 - 1] = "AONDEVICES Inc", +[13][0x3a - 1] = "Shenzhen Netforward Micro Electronic", +[13][0x3b - 1] = "Syntacore Ltd", +[13][0x3c - 1] = "Shenzhen Secmem Microelectronics Co", +[13][0x3d - 1] = "ONiO As", +[13][0x3e - 1] = "Shenzhen Peladn Technology Co Ltd", +[13][0x3f - 1] = "O-Cubes Shanghai Microelectronics", +[13][0x40 - 1] = "ASTC", +[13][0x41 - 1] = "UMIS", +[13][0x42 - 1] = "Paradromics", +[13][0x43 - 1] = "Sinh Micro Co Ltd", +[13][0x44 - 1] = "Metorage Semiconductor Technology Co", +[13][0x45 - 1] = "Aeva Inc", +[13][0x46 - 1] = "HongKong Hyunion Electronics Co Ltd", +[13][0x47 - 1] = "China Flash Co Ltd", +[13][0x48 - 1] = "Sunplus Technology Co Ltd", +[13][0x49 - 1] = "Idaho Scientific", +[13][0x4a - 1] = "Suzhou SF Micro Electronics Co Ltd", +[13][0x4b - 1] = "IMEX Cap AG", +[13][0x4c - 1] = "Fitipower Integrated Technology Co Ltd", +[13][0x4d - 1] = "ShenzhenWooacme Technology Co Ltd", +[13][0x4e - 1] = "KeepData Original Chips", +[13][0x4f - 1] = "Rivos Inc", +[13][0x50 - 1] = "Big Innovation Company Limited", +[13][0x51 - 1] = "Wuhan YuXin Semiconductor Co Ltd", +[13][0x52 - 1] = "United Memory Technology (Jiangsu)", +[13][0x53 - 1] = "PQShield Ltd", +[13][0x54 - 1] = "ArchiTek Corporation", +[13][0x55 - 1] = "ShenZhen AZW Technology Co Ltd", +[13][0x56 - 1] = "Hengchi Zhixin (Dongguan) Technology", +[13][0x57 - 1] = "Eggtronic Engineering Spa", +[13][0x58 - 1] = "Fusontai Technology", +[13][0x59 - 1] = "PULP Platform", +[13][0x5a - 1] = "Koitek Electronic Technology (Shenzhen) Co", +[13][0x5b - 1] = "Shenzhen Jiteng Network Technology Co", +[13][0x5c - 1] = "Aviva Links Inc", +[13][0x5d - 1] = "Trilinear Technologies Inc", +[13][0x5e - 1] = "Shenzhen Developer Microelectronics Co", +[13][0x5f - 1] = "Guangdong OPPO Mobile Telecommunication", +[13][0x60 - 1] = "Akeana", +[13][0x61 - 1] = "Lyczar", +[13][0x62 - 1] = "Shenzhen Qiji Technology Co Ltd", +[13][0x63 - 1] = "Shenzhen Shangzhaoyuan Technology", +[13][0x64 - 1] = "Han Stor", +[13][0x65 - 1] = "China Micro Semicon Co., Ltd.", +[13][0x66 - 1] = "Shenzhen Zhuqin Technology Co Ltd", +[13][0x67 - 1] = "Shanghai Ningyuan Electronic Technology", +[13][0x68 - 1] = "Auradine", +[13][0x69 - 1] = "Suzhou Yishuo Electronics Co Ltd", +[13][0x6a - 1] = "Faurecia Clarion Electronics", +[13][0x6b - 1] = "SiMa Technologies", +[13][0x6c - 1] = "CFD Sales Inc", +[13][0x6d - 1] = "Suzhou Comay Information Co Ltd", +[13][0x6e - 1] = "Yentek", +[13][0x6f - 1] = "Qorvo Inc", +[13][0x70 - 1] = "Shenzhen Youzhi Computer Technology", +[13][0x71 - 1] = "Sychw Technology (Shenzhen) Co Ltd", +[13][0x72 - 1] = "MK Founder Technology Co Ltd", +[13][0x73 - 1] = "Siliconwaves Technologies Co Ltd", +[13][0x74 - 1] = "Hongkong Hyunion Electronics Co Ltd", +[13][0x75 - 1] = "Shenzhen Xinxinzhitao Electronics Business", +[13][0x76 - 1] = "Shenzhen HenQi Electronic Commerce Co", +[13][0x77 - 1] = "Shenzhen Jingyi Technology Co Ltd", +[13][0x78 - 1] = "Xiaohua Semiconductor Co. Ltd.", +[13][0x79 - 1] = "Shenzhen Dalu Semiconductor Technology", +[13][0x7a - 1] = "Shenzhen Ninespeed Electronics Co Ltd", +[13][0x7b - 1] = "ICYC Semiconductor Co Ltd", +[13][0x7c - 1] = "Shenzhen Jaguar Microsystems Co Ltd", +[13][0x7d - 1] = "Beijing EC-Founder Co Ltd", +[13][0x7e - 1] = "Shenzhen Taike Industrial Automation Co", +[14][0x01 - 1] = "Kalray SA", +[14][0x02 - 1] = "Shanghai Iluvatar CoreX Semiconductor Co", +[14][0x03 - 1] = "Fungible Inc", +[14][0x04 - 1] = "Song Industria E Comercio de Eletronicos", +[14][0x05 - 1] = "DreamBig Semiconductor Inc", +[14][0x06 - 1] = "ChampTek Electronics Corp", +[14][0x07 - 1] = "Fusontai Technology", +[14][0x08 - 1] = "Endress Hauser AG", +[14][0x09 - 1] = "altec ComputerSysteme GmbH", +[14][0x0a - 1] = "UltraRISC Technology (Shanghai) Co Ltd", +[14][0x0b - 1] = "Shenzhen Jing Da Kang Technology Co Ltd", +[14][0x0c - 1] = "Hangzhou Hongjun Microelectronics Co Ltd", +/* EOF */ diff --git a/applications/plugins/swd_probe/model/chip.ply b/applications/plugins/swd_probe/model/chip.ply new file mode 100644 index 000000000..7dc20abfa --- /dev/null +++ b/applications/plugins/swd_probe/model/chip.ply @@ -0,0 +1,216 @@ +ply +format ascii 1.0 +comment Created by Blender 3.3.1 - www.blender.org +element vertex 136 +property float x +property float y +property float z +element face 70 +property list uchar uint vertex_indices +end_header +1.000000 1.000000 0.152153 +-1.000000 1.000000 0.152153 +-1.000000 -1.000000 0.152153 +1.000000 -1.000000 0.152153 +1.000000 -1.000000 -0.185787 +-1.000000 -1.000000 -0.185787 +-1.000000 1.000000 -0.185787 +1.000000 1.000000 -0.185787 +-1.000043 -0.785071 -0.015780 +-1.155724 -0.785071 -0.015780 +-1.155724 -0.918718 -0.015780 +-1.000043 -0.918718 -0.015780 +-1.155724 -0.785071 0.127052 +-1.000043 -0.785071 0.127052 +-1.000043 -0.918718 0.127052 +-1.155724 -0.918718 0.127052 +-1.234192 -0.918846 -0.087021 +-1.234397 -0.785201 -0.086336 +-1.235319 -0.784943 -0.229143 +-1.235114 -0.918588 -0.229828 +-1.388133 -0.919573 -0.078673 +-1.389056 -0.919314 -0.221479 +-1.389261 -0.785669 -0.220795 +-1.388338 -0.785927 -0.077988 +-1.000043 -0.219627 -0.015780 +-1.155724 -0.219627 -0.015780 +-1.155724 -0.353273 -0.015780 +-1.000043 -0.353273 -0.015780 +-1.155724 -0.219627 0.127052 +-1.000043 -0.219627 0.127052 +-1.000043 -0.353273 0.127052 +-1.155724 -0.353273 0.127052 +-1.234192 -0.353402 -0.087021 +-1.234397 -0.219756 -0.086336 +-1.235319 -0.219498 -0.229143 +-1.235114 -0.353143 -0.229828 +-1.388133 -0.354128 -0.078673 +-1.389056 -0.353870 -0.221479 +-1.389261 -0.220224 -0.220795 +-1.388338 -0.220482 -0.077988 +-1.000043 0.345818 -0.015780 +-1.155724 0.345818 -0.015780 +-1.155724 0.212172 -0.015780 +-1.000043 0.212172 -0.015780 +-1.155724 0.345818 0.127052 +-1.000043 0.345818 0.127052 +-1.000043 0.212172 0.127052 +-1.155724 0.212172 0.127052 +-1.234192 0.212043 -0.087021 +-1.234397 0.345689 -0.086336 +-1.235319 0.345947 -0.229143 +-1.235114 0.212301 -0.229828 +-1.388133 0.211317 -0.078673 +-1.389056 0.211575 -0.221479 +-1.389261 0.345221 -0.220795 +-1.388338 0.344962 -0.077988 +-1.000043 0.911263 -0.015780 +-1.155724 0.911263 -0.015780 +-1.155724 0.777617 -0.015780 +-1.000043 0.777617 -0.015780 +-1.155724 0.911263 0.127052 +-1.000043 0.911263 0.127052 +-1.000043 0.777617 0.127052 +-1.155724 0.777617 0.127052 +-1.234192 0.777488 -0.087021 +-1.234397 0.911133 -0.086336 +-1.235319 0.911392 -0.229143 +-1.235114 0.777746 -0.229828 +-1.388133 0.776762 -0.078673 +-1.389056 0.777020 -0.221479 +-1.389261 0.910665 -0.220795 +-1.388338 0.910407 -0.077988 +1.000043 -0.785071 -0.015780 +1.000043 -0.918718 -0.015780 +1.155723 -0.918718 -0.015780 +1.155723 -0.785071 -0.015780 +1.155723 -0.785071 0.127052 +1.155723 -0.918718 0.127052 +1.000043 -0.918718 0.127052 +1.000043 -0.785071 0.127052 +1.234397 -0.785201 -0.086336 +1.234192 -0.918846 -0.087021 +1.235114 -0.918588 -0.229828 +1.235319 -0.784943 -0.229143 +1.388133 -0.919573 -0.078673 +1.388338 -0.785927 -0.077988 +1.389260 -0.785669 -0.220795 +1.389056 -0.919314 -0.221479 +1.000043 -0.219627 -0.015780 +1.000043 -0.353273 -0.015780 +1.155723 -0.353273 -0.015780 +1.155723 -0.219627 -0.015780 +1.155723 -0.219627 0.127052 +1.155723 -0.353273 0.127052 +1.000043 -0.353273 0.127052 +1.000043 -0.219627 0.127052 +1.234397 -0.219756 -0.086336 +1.234192 -0.353402 -0.087021 +1.235114 -0.353143 -0.229828 +1.235319 -0.219498 -0.229143 +1.388133 -0.354128 -0.078673 +1.388338 -0.220482 -0.077988 +1.389260 -0.220224 -0.220795 +1.389056 -0.353870 -0.221479 +1.000043 0.345818 -0.015780 +1.000043 0.212172 -0.015780 +1.155723 0.212172 -0.015780 +1.155723 0.345818 -0.015780 +1.155723 0.345818 0.127052 +1.155723 0.212172 0.127052 +1.000043 0.212172 0.127052 +1.000043 0.345818 0.127052 +1.234397 0.345689 -0.086336 +1.234192 0.212043 -0.087021 +1.235114 0.212301 -0.229828 +1.235319 0.345947 -0.229143 +1.388133 0.211317 -0.078673 +1.388338 0.344962 -0.077988 +1.389260 0.345221 -0.220795 +1.389056 0.211575 -0.221479 +1.000043 0.911263 -0.015780 +1.000043 0.777616 -0.015780 +1.155723 0.777616 -0.015780 +1.155723 0.911263 -0.015780 +1.155723 0.911263 0.127052 +1.155723 0.777616 0.127052 +1.000043 0.777616 0.127052 +1.000043 0.911263 0.127052 +1.234397 0.911133 -0.086336 +1.234192 0.777488 -0.087021 +1.235114 0.777746 -0.229828 +1.235319 0.911392 -0.229143 +1.388133 0.776762 -0.078673 +1.388338 0.910407 -0.077988 +1.389260 0.910665 -0.220795 +1.389056 0.777020 -0.221479 +4 0 1 2 3 +4 4 3 2 5 +4 5 2 1 6 +4 6 7 4 5 +4 7 0 3 4 +4 6 1 0 7 +4 8 9 10 11 +4 12 13 14 15 +4 13 8 11 14 +4 12 15 16 17 +4 10 9 18 19 +4 20 21 22 23 +4 17 16 20 23 +4 19 18 22 21 +4 24 25 26 27 +4 28 29 30 31 +4 29 24 27 30 +4 28 31 32 33 +4 26 25 34 35 +4 36 37 38 39 +4 33 32 36 39 +4 35 34 38 37 +4 40 41 42 43 +4 44 45 46 47 +4 45 40 43 46 +4 44 47 48 49 +4 42 41 50 51 +4 52 53 54 55 +4 49 48 52 55 +4 51 50 54 53 +4 56 57 58 59 +4 60 61 62 63 +4 61 56 59 62 +4 60 63 64 65 +4 58 57 66 67 +4 68 69 70 71 +4 65 64 68 71 +4 67 66 70 69 +4 72 73 74 75 +4 76 77 78 79 +4 79 78 73 72 +4 76 80 81 77 +4 74 82 83 75 +4 84 85 86 87 +4 80 85 84 81 +4 82 87 86 83 +4 88 89 90 91 +4 92 93 94 95 +4 95 94 89 88 +4 92 96 97 93 +4 90 98 99 91 +4 100 101 102 103 +4 96 101 100 97 +4 98 103 102 99 +4 104 105 106 107 +4 108 109 110 111 +4 111 110 105 104 +4 108 112 113 109 +4 106 114 115 107 +4 116 117 118 119 +4 112 117 116 113 +4 114 119 118 115 +4 120 121 122 123 +4 124 125 126 127 +4 127 126 121 120 +4 124 128 129 125 +4 122 130 131 123 +4 132 133 134 135 +4 128 133 132 129 +4 130 135 134 131 diff --git a/applications/plugins/swd_probe/model/convert.py b/applications/plugins/swd_probe/model/convert.py new file mode 100644 index 000000000..7c99ca215 --- /dev/null +++ b/applications/plugins/swd_probe/model/convert.py @@ -0,0 +1,39 @@ +#!/usr/bin/python + +import plyfile +import argparse + +parser = argparse.ArgumentParser(description='Convert a PLY file to C arrays.') +parser.add_argument('input_file', help='the input PLY file') +parser.add_argument('output_file', help='the output C file') +args = parser.parse_args() + +# Open the PLY file +plydata = plyfile.PlyData.read(args.input_file) + +# Extract the vertices +vertices = plydata['vertex'].data +num_vertices = len(vertices) + +with open(args.output_file, 'w') as f: + f.write('#define NUM_VERTICES %d\n' % num_vertices) + f.write('float vertexCoords[NUM_VERTICES][3] = {\n') + for i in range(num_vertices): + x, y, z = vertices[i][0], vertices[i][1], vertices[i][2] + f.write(' {%f, %f, %f},\n' % (x, y, z)) + f.write('};') + + # Extract the faces + faces = plydata['face'].data + num_faces = len(faces) + f.write('int edgeIndices[][3] = {\n') + for i in range(num_faces): + face = faces[i][0] + if len(face) == 3: + f.write(' {%d, %d, %d},\n' % (face[0], face[1], face[2])) + elif len(face) == 4: + # Convert 4-index face to 2-index edges + edges = [(face[0], face[1]), (face[1], face[2]), (face[2], face[3]), (face[3], face[0])] + for edge in edges: + f.write(' {%d, %d},\n' % (edge[0], edge[1])) + f.write('};\n') diff --git a/applications/plugins/swd_probe/model/model_chip.h b/applications/plugins/swd_probe/model/model_chip.h new file mode 100644 index 000000000..4061b8522 --- /dev/null +++ b/applications/plugins/swd_probe/model/model_chip.h @@ -0,0 +1,108 @@ +#define NUM_VERTICES 136 +float vertexCoords[NUM_VERTICES][3] = { + {1.000000, 1.000000, 0.152153}, {-1.000000, 1.000000, 0.152153}, + {-1.000000, -1.000000, 0.152153}, {1.000000, -1.000000, 0.152153}, + {1.000000, -1.000000, -0.185787}, {-1.000000, -1.000000, -0.185787}, + {-1.000000, 1.000000, -0.185787}, {1.000000, 1.000000, -0.185787}, + {-1.000043, -0.785071, -0.015780}, {-1.155724, -0.785071, -0.015780}, + {-1.155724, -0.918718, -0.015780}, {-1.000043, -0.918718, -0.015780}, + {-1.155724, -0.785071, 0.127052}, {-1.000043, -0.785071, 0.127052}, + {-1.000043, -0.918718, 0.127052}, {-1.155724, -0.918718, 0.127052}, + {-1.234192, -0.918846, -0.087021}, {-1.234397, -0.785201, -0.086336}, + {-1.235319, -0.784943, -0.229143}, {-1.235114, -0.918588, -0.229828}, + {-1.388133, -0.919573, -0.078673}, {-1.389056, -0.919314, -0.221479}, + {-1.389261, -0.785669, -0.220795}, {-1.388338, -0.785927, -0.077988}, + {-1.000043, -0.219627, -0.015780}, {-1.155724, -0.219627, -0.015780}, + {-1.155724, -0.353273, -0.015780}, {-1.000043, -0.353273, -0.015780}, + {-1.155724, -0.219627, 0.127052}, {-1.000043, -0.219627, 0.127052}, + {-1.000043, -0.353273, 0.127052}, {-1.155724, -0.353273, 0.127052}, + {-1.234192, -0.353402, -0.087021}, {-1.234397, -0.219756, -0.086336}, + {-1.235319, -0.219498, -0.229143}, {-1.235114, -0.353143, -0.229828}, + {-1.388133, -0.354128, -0.078673}, {-1.389056, -0.353870, -0.221479}, + {-1.389261, -0.220224, -0.220795}, {-1.388338, -0.220482, -0.077988}, + {-1.000043, 0.345818, -0.015780}, {-1.155724, 0.345818, -0.015780}, + {-1.155724, 0.212172, -0.015780}, {-1.000043, 0.212172, -0.015780}, + {-1.155724, 0.345818, 0.127052}, {-1.000043, 0.345818, 0.127052}, + {-1.000043, 0.212172, 0.127052}, {-1.155724, 0.212172, 0.127052}, + {-1.234192, 0.212043, -0.087021}, {-1.234397, 0.345689, -0.086336}, + {-1.235319, 0.345947, -0.229143}, {-1.235114, 0.212301, -0.229828}, + {-1.388133, 0.211317, -0.078673}, {-1.389056, 0.211575, -0.221479}, + {-1.389261, 0.345221, -0.220795}, {-1.388338, 0.344962, -0.077988}, + {-1.000043, 0.911263, -0.015780}, {-1.155724, 0.911263, -0.015780}, + {-1.155724, 0.777617, -0.015780}, {-1.000043, 0.777617, -0.015780}, + {-1.155724, 0.911263, 0.127052}, {-1.000043, 0.911263, 0.127052}, + {-1.000043, 0.777617, 0.127052}, {-1.155724, 0.777617, 0.127052}, + {-1.234192, 0.777488, -0.087021}, {-1.234397, 0.911133, -0.086336}, + {-1.235319, 0.911392, -0.229143}, {-1.235114, 0.777746, -0.229828}, + {-1.388133, 0.776762, -0.078673}, {-1.389056, 0.777020, -0.221479}, + {-1.389261, 0.910665, -0.220795}, {-1.388338, 0.910407, -0.077988}, + {1.000043, -0.785071, -0.015780}, {1.000043, -0.918718, -0.015780}, + {1.155723, -0.918718, -0.015780}, {1.155723, -0.785071, -0.015780}, + {1.155723, -0.785071, 0.127052}, {1.155723, -0.918718, 0.127052}, + {1.000043, -0.918718, 0.127052}, {1.000043, -0.785071, 0.127052}, + {1.234397, -0.785201, -0.086336}, {1.234192, -0.918846, -0.087021}, + {1.235114, -0.918588, -0.229828}, {1.235319, -0.784943, -0.229143}, + {1.388133, -0.919573, -0.078673}, {1.388338, -0.785927, -0.077988}, + {1.389260, -0.785669, -0.220795}, {1.389056, -0.919314, -0.221479}, + {1.000043, -0.219627, -0.015780}, {1.000043, -0.353273, -0.015780}, + {1.155723, -0.353273, -0.015780}, {1.155723, -0.219627, -0.015780}, + {1.155723, -0.219627, 0.127052}, {1.155723, -0.353273, 0.127052}, + {1.000043, -0.353273, 0.127052}, {1.000043, -0.219627, 0.127052}, + {1.234397, -0.219756, -0.086336}, {1.234192, -0.353402, -0.087021}, + {1.235114, -0.353143, -0.229828}, {1.235319, -0.219498, -0.229143}, + {1.388133, -0.354128, -0.078673}, {1.388338, -0.220482, -0.077988}, + {1.389260, -0.220224, -0.220795}, {1.389056, -0.353870, -0.221479}, + {1.000043, 0.345818, -0.015780}, {1.000043, 0.212172, -0.015780}, + {1.155723, 0.212172, -0.015780}, {1.155723, 0.345818, -0.015780}, + {1.155723, 0.345818, 0.127052}, {1.155723, 0.212172, 0.127052}, + {1.000043, 0.212172, 0.127052}, {1.000043, 0.345818, 0.127052}, + {1.234397, 0.345689, -0.086336}, {1.234192, 0.212043, -0.087021}, + {1.235114, 0.212301, -0.229828}, {1.235319, 0.345947, -0.229143}, + {1.388133, 0.211317, -0.078673}, {1.388338, 0.344962, -0.077988}, + {1.389260, 0.345221, -0.220795}, {1.389056, 0.211575, -0.221479}, + {1.000043, 0.911263, -0.015780}, {1.000043, 0.777616, -0.015780}, + {1.155723, 0.777616, -0.015780}, {1.155723, 0.911263, -0.015780}, + {1.155723, 0.911263, 0.127052}, {1.155723, 0.777616, 0.127052}, + {1.000043, 0.777616, 0.127052}, {1.000043, 0.911263, 0.127052}, + {1.234397, 0.911133, -0.086336}, {1.234192, 0.777488, -0.087021}, + {1.235114, 0.777746, -0.229828}, {1.235319, 0.911392, -0.229143}, + {1.388133, 0.776762, -0.078673}, {1.388338, 0.910407, -0.077988}, + {1.389260, 0.910665, -0.220795}, {1.389056, 0.777020, -0.221479}, +}; +int edgeIndices[][3] = { + {0, 1}, {1, 2}, {2, 3}, {3, 0}, {4, 3}, {3, 2}, {2, 5}, {5, 4}, + {5, 2}, {2, 1}, {1, 6}, {6, 5}, {6, 7}, {7, 4}, {4, 5}, {5, 6}, + {7, 0}, {0, 3}, {3, 4}, {4, 7}, {6, 1}, {1, 0}, {0, 7}, {7, 6}, + {8, 9}, {9, 10}, {10, 11}, {11, 8}, {12, 13}, {13, 14}, {14, 15}, {15, 12}, + {13, 8}, {8, 11}, {11, 14}, {14, 13}, {12, 15}, {15, 16}, {16, 17}, {17, 12}, + {10, 9}, {9, 18}, {18, 19}, {19, 10}, {20, 21}, {21, 22}, {22, 23}, {23, 20}, + {17, 16}, {16, 20}, {20, 23}, {23, 17}, {19, 18}, {18, 22}, {22, 21}, {21, 19}, + {24, 25}, {25, 26}, {26, 27}, {27, 24}, {28, 29}, {29, 30}, {30, 31}, {31, 28}, + {29, 24}, {24, 27}, {27, 30}, {30, 29}, {28, 31}, {31, 32}, {32, 33}, {33, 28}, + {26, 25}, {25, 34}, {34, 35}, {35, 26}, {36, 37}, {37, 38}, {38, 39}, {39, 36}, + {33, 32}, {32, 36}, {36, 39}, {39, 33}, {35, 34}, {34, 38}, {38, 37}, {37, 35}, + {40, 41}, {41, 42}, {42, 43}, {43, 40}, {44, 45}, {45, 46}, {46, 47}, {47, 44}, + {45, 40}, {40, 43}, {43, 46}, {46, 45}, {44, 47}, {47, 48}, {48, 49}, {49, 44}, + {42, 41}, {41, 50}, {50, 51}, {51, 42}, {52, 53}, {53, 54}, {54, 55}, {55, 52}, + {49, 48}, {48, 52}, {52, 55}, {55, 49}, {51, 50}, {50, 54}, {54, 53}, {53, 51}, + {56, 57}, {57, 58}, {58, 59}, {59, 56}, {60, 61}, {61, 62}, {62, 63}, {63, 60}, + {61, 56}, {56, 59}, {59, 62}, {62, 61}, {60, 63}, {63, 64}, {64, 65}, {65, 60}, + {58, 57}, {57, 66}, {66, 67}, {67, 58}, {68, 69}, {69, 70}, {70, 71}, {71, 68}, + {65, 64}, {64, 68}, {68, 71}, {71, 65}, {67, 66}, {66, 70}, {70, 69}, {69, 67}, + {72, 73}, {73, 74}, {74, 75}, {75, 72}, {76, 77}, {77, 78}, {78, 79}, {79, 76}, + {79, 78}, {78, 73}, {73, 72}, {72, 79}, {76, 80}, {80, 81}, {81, 77}, {77, 76}, + {74, 82}, {82, 83}, {83, 75}, {75, 74}, {84, 85}, {85, 86}, {86, 87}, {87, 84}, + {80, 85}, {85, 84}, {84, 81}, {81, 80}, {82, 87}, {87, 86}, {86, 83}, {83, 82}, + {88, 89}, {89, 90}, {90, 91}, {91, 88}, {92, 93}, {93, 94}, {94, 95}, {95, 92}, + {95, 94}, {94, 89}, {89, 88}, {88, 95}, {92, 96}, {96, 97}, {97, 93}, {93, 92}, + {90, 98}, {98, 99}, {99, 91}, {91, 90}, {100, 101}, {101, 102}, {102, 103}, {103, 100}, + {96, 101}, {101, 100}, {100, 97}, {97, 96}, {98, 103}, {103, 102}, {102, 99}, {99, 98}, + {104, 105}, {105, 106}, {106, 107}, {107, 104}, {108, 109}, {109, 110}, {110, 111}, {111, 108}, + {111, 110}, {110, 105}, {105, 104}, {104, 111}, {108, 112}, {112, 113}, {113, 109}, {109, 108}, + {106, 114}, {114, 115}, {115, 107}, {107, 106}, {116, 117}, {117, 118}, {118, 119}, {119, 116}, + {112, 117}, {117, 116}, {116, 113}, {113, 112}, {114, 119}, {119, 118}, {118, 115}, {115, 114}, + {120, 121}, {121, 122}, {122, 123}, {123, 120}, {124, 125}, {125, 126}, {126, 127}, {127, 124}, + {127, 126}, {126, 121}, {121, 120}, {120, 127}, {124, 128}, {128, 129}, {129, 125}, {125, 124}, + {122, 130}, {130, 131}, {131, 123}, {123, 122}, {132, 133}, {133, 134}, {134, 135}, {135, 132}, + {128, 133}, {133, 132}, {132, 129}, {129, 128}, {130, 135}, {135, 134}, {134, 131}, {131, 130}, +}; diff --git a/applications/plugins/swd_probe/swd_probe_app.c b/applications/plugins/swd_probe/swd_probe_app.c new file mode 100644 index 000000000..beaa871dc --- /dev/null +++ b/applications/plugins/swd_probe/swd_probe_app.c @@ -0,0 +1,3175 @@ + + +#include "swd_probe_app.h" +#include "swd_probe_icons.h" +#include "jep106.h" +#include "adi.h" + +static void render_callback(Canvas* const canvas, void* cb_ctx); +static bool swd_message_process(AppFSM* ctx); +static uint8_t swd_transfer(AppFSM* const ctx, bool ap, bool write, uint8_t a23, uint32_t* data); +static bool swd_execute_script(AppFSM* const ctx, const char* filename); + +static const GpioPin* gpios[] = { + &gpio_ext_pc0, + &gpio_ext_pc1, + &gpio_ext_pc3, + &gpio_ext_pb2, + &gpio_ext_pb3, + &gpio_ext_pa4, + &gpio_ext_pa6, + &gpio_ext_pa7}; + +static const char* gpio_names[] = {"PC0", "PC1", "PC3", "PB2", "PB3", "PA4", "PA6", "PA7"}; + +/* bit set: clock, else data */ +static const uint8_t gpio_direction_mask[6] = + {0b10101010, 0b01010101, 0b11001100, 0b00110011, 0b11110000, 0b00001111}; + +const NotificationSequence seq_c_minor = { + &message_note_c4, + &message_delay_100, + &message_sound_off, + &message_delay_10, + + &message_note_ds4, + &message_delay_100, + &message_sound_off, + &message_delay_10, + + &message_note_g4, + &message_delay_100, + &message_sound_off, + &message_delay_10, + + &message_vibro_on, + &message_delay_50, + &message_vibro_off, + NULL, +}; + +const NotificationSequence seq_error = { + + &message_vibro_on, + &message_delay_50, + &message_vibro_off, + + &message_note_g4, + &message_delay_100, + &message_sound_off, + &message_delay_10, + + &message_note_c4, + &message_delay_500, + &message_sound_off, + &message_delay_10, + NULL, +}; + +const NotificationSequence* seq_sounds[] = {&seq_c_minor, &seq_error}; + +static bool has_multiple_bits(uint8_t x) { + return (x & (x - 1)) != 0; +} + +static uint8_t get_bit_num(uint8_t x) { + return (uint8_t)__builtin_ctz(x); +} + +static const char* gpio_name(uint8_t mask) { + if(has_multiple_bits(mask)) { + return "Pxx"; + } + uint8_t io = get_bit_num(mask); + if(io >= COUNT(gpio_names)) { + return "Pxx"; + } + + return gpio_names[io]; +} + +static void swd_configure_pins(AppFSM* const ctx, bool output) { + if(ctx->mode_page > ModePageFound && ctx->io_num_swc < 8 && ctx->io_num_swd < 8) { + furi_hal_gpio_init( + gpios[ctx->io_num_swc], GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + if(!output) { + furi_hal_gpio_init( + gpios[ctx->io_num_swd], GpioModeInput, GpioPullUp, GpioSpeedVeryHigh); + } else { + furi_hal_gpio_init( + gpios[ctx->io_num_swd], GpioModeOutputOpenDrain, GpioPullUp, GpioSpeedVeryHigh); + } + return; + } + + for(int io = 0; io < 8; io++) { + uint8_t bitmask = 1 << io; + + /* if neither candidate for SWC nor SWD then skip */ + if(!(ctx->io_swc & bitmask) && !(ctx->io_swd & bitmask)) { + furi_hal_gpio_init(gpios[io], GpioModeInput, GpioPullUp, GpioSpeedVeryHigh); + continue; + } + + if(ctx->current_mask & bitmask) { + /* set for clock */ + furi_hal_gpio_init(gpios[io], GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + } else { + /* set for data */ + if(!output) { + furi_hal_gpio_init(gpios[io], GpioModeInput, GpioPullUp, GpioSpeedVeryHigh); + } else { + furi_hal_gpio_init( + gpios[io], GpioModeOutputOpenDrain, GpioPullUp, GpioSpeedVeryHigh); + } + } + } +} + +static void swd_set_clock(AppFSM* const ctx, const uint8_t level) { + if(ctx->mode_page > ModePageFound && ctx->io_num_swc < 8) { + furi_hal_gpio_write(gpios[ctx->io_num_swc], level); + return; + } + + for(int io = 0; io < 8; io++) { + uint8_t bitmask = 1 << io; + + /* if no candidate for SWC then skip */ + if(!(ctx->io_swc & bitmask)) { + continue; + } + + if(ctx->current_mask & bitmask) { + furi_hal_gpio_write(gpios[io], level); + } + } +} + +static void swd_set_data(AppFSM* const ctx, const uint8_t level) { + if(ctx->mode_page > ModePageFound && ctx->io_num_swd < 8) { + furi_hal_gpio_write(gpios[ctx->io_num_swd], level); + return; + } + + for(int io = 0; io < 8; io++) { + uint8_t bitmask = 1 << io; + + /* if no candidate for SWD then skip */ + if(!(ctx->io_swd & bitmask)) { + continue; + } + + if(!(ctx->current_mask & bitmask)) { + furi_hal_gpio_write(gpios[io], level); + } + } +} + +static uint8_t swd_get_data(AppFSM* const ctx) { + if(ctx->mode_page > ModePageFound && ctx->io_num_swd < 8) { + return furi_hal_gpio_read(gpios[ctx->io_num_swd]); + } + + uint8_t bits = 0; + for(int io = 0; io < 8; io++) { + uint8_t bitmask = 1 << io; + + /* if no candidate for SWD then skip */ + if(!(ctx->io_swd & bitmask)) { + continue; + } + bits |= furi_hal_gpio_read(gpios[io]) ? bitmask : 0; + } + return bits; +} + +static void swd_clock_delay(AppFSM* const ctx) { + if(ctx->swd_clock_delay) { + furi_delay_us(ctx->swd_clock_delay); + } +} + +static void swd_write_bit(AppFSM* const ctx, bool level) { + swd_set_clock(ctx, 0); + swd_set_data(ctx, level); + swd_clock_delay(ctx); + swd_set_clock(ctx, 1); + swd_clock_delay(ctx); + swd_set_clock(ctx, 0); +} + +static uint8_t swd_read_bit(AppFSM* const ctx) { + swd_set_clock(ctx, 1); + swd_clock_delay(ctx); + swd_set_clock(ctx, 0); + uint8_t bits = swd_get_data(ctx); + swd_clock_delay(ctx); + swd_set_clock(ctx, 1); + + return bits; +} + +/* send a byte or less LSB-first */ +static void swd_write_byte(AppFSM* const ctx, const uint8_t data, size_t bits) { + for(size_t pos = 0; pos < bits; pos++) { + swd_write_bit(ctx, data & (1 << pos)); + } +} + +/* send a sequence of bytes LSB-first */ +static void swd_write(AppFSM* const ctx, const uint8_t* data, size_t bits) { + size_t byte_pos = 0; + while(bits > 0) { + size_t remain = (bits > 8) ? 8 : bits; + swd_write_byte(ctx, data[byte_pos++], remain); + bits -= remain; + } +} + +static uint8_t swd_transfer(AppFSM* const ctx, bool ap, bool write, uint8_t a23, uint32_t* data) { + //notification_message(ctx->notification, &sequence_set_blue_255); + //notification_message(ctx->notification, &sequence_reset_red); + + swd_set_data(ctx, false); + swd_configure_pins(ctx, true); + + uint32_t idle = 0; + swd_write(ctx, (uint8_t*)&idle, ctx->swd_idle_bits); + + uint8_t request[] = {0}; + + request[0] |= 0x01; /* start bit*/ + request[0] |= ap ? 0x02 : 0; /* APnDP */ + request[0] |= write ? 0 : 0x04; /* operation */ + request[0] |= (a23 & 0x01) ? 0x08 : 0; /* A[2:3] */ + request[0] |= (a23 & 0x02) ? 0x10 : 0; /* A[2:3] */ + request[0] |= 0x80; /* park bit */ + request[0] |= __builtin_parity(request[0]) ? 0x20 : 0; /* parity */ + + swd_write(ctx, request, sizeof(request) * 8); + + /* turnaround cycle */ + swd_configure_pins(ctx, false); + + uint8_t ack = 0; + + /* receive 3 ACK bits */ + for(int pos = 0; pos < 3; pos++) { + ack >>= 1; + ack |= swd_read_bit(ctx) ? 0x04 : 0; + } + + /* force ABORT/CTRL to always work */ + if(!ap && a23 == 0) { + ack = 1; + } + + if(ack != 0x01) { + //notification_message(ctx->notification, &sequence_reset_blue); + //notification_message(ctx->notification, &sequence_set_red_255); + return ack; + } + + if(write) { + swd_write_bit(ctx, 0); + swd_configure_pins(ctx, true); + + /* send 32 WDATA bits */ + for(int pos = 0; pos < 32; pos++) { + swd_write_bit(ctx, *data & (1 << pos)); + } + + /* send parity bit */ + swd_write_bit(ctx, __builtin_parity(*data)); + } else { + *data = 0; + /* receive 32 RDATA bits */ + for(int pos = 0; pos < 32; pos++) { + *data >>= 1; + *data |= swd_read_bit(ctx) ? 0x80000000 : 0; + } + + /* receive parity bit */ + bool parity = swd_read_bit(ctx); + + if(parity != __builtin_parity(*data)) { + //notification_message(ctx->notification, &sequence_reset_blue); + //notification_message(ctx->notification, &sequence_set_red_255); + return 8; + } + } + swd_set_data(ctx, false); + swd_configure_pins(ctx, true); + //notification_message(ctx->notification, &sequence_reset_blue); + + return ack; +} + +/* A line reset is achieved by holding the data signal HIGH for at least 50 clock cycles, followed by at least two idle cycles. */ +static void swd_line_reset(AppFSM* const ctx) { + //notification_message(ctx->notification, &sequence_set_red_255); + for(int bitcount = 0; bitcount < 50; bitcount += 8) { + swd_write_byte(ctx, 0xFF, 8); + } + swd_write_byte(ctx, 0, 8); + ctx->dp_regs.select_ok = false; + //notification_message(ctx->notification, &sequence_reset_red); +} + +static void swd_abort(AppFSM* const ctx) { + uint32_t dpidr; + + /* first reset the line */ + swd_line_reset(ctx); + swd_transfer(ctx, false, false, 0, &dpidr); + uint32_t abort = 0x0E; + swd_transfer(ctx, false, true, 0, &abort); +} + +static void swd_abort_simple(AppFSM* const ctx) { + uint32_t abort = 0x0E; + swd_transfer(ctx, false, true, 0, &abort); + + uint32_t dpidr; + if(swd_transfer(ctx, false, false, 0, &dpidr) != 1) { + swd_abort(ctx); + } +} + +static uint8_t swd_select(AppFSM* const ctx, uint8_t ap_sel, uint8_t ap_bank, uint8_t dp_bank) { + uint32_t bank_reg = (ap_sel << 24) | ((ap_bank & 0x0F) << 4) | (dp_bank & 0x0F); + + if(ctx->dp_regs.select_ok && bank_reg == ctx->dp_regs.select) { + return 1; + } + + uint8_t ret = swd_transfer(ctx, false, true, REG_SELECT, &bank_reg); + if(ret != 1) { + ctx->dp_regs.select_ok = false; + DBG("failed: %d", ret); + return ret; + } + + ctx->dp_regs.select = bank_reg; + ctx->dp_regs.select_ok = true; + return ret; +} + +static uint8_t + swd_read_dpbank(AppFSM* const ctx, uint8_t dp_off, uint8_t dp_bank, uint32_t* data) { + uint8_t ret = 0; + + /* select target bank */ + if(dp_bank < 0x10) { + uint8_t ret = swd_select(ctx, 0, 0, dp_bank); + if(ret != 1) { + DBGS("swd_select failed"); + return ret; + } + } + + /* read data from it */ + *data = 0; + ret = swd_transfer(ctx, false, false, dp_off, data); + if(ret != 1) { + DBG("failed: %d", ret); + return ret; + } + return ret; +} + +static uint8_t + swd_write_dpbank(AppFSM* const ctx, uint8_t dp_off, uint8_t dp_bank, uint32_t* data) { + uint8_t ret = 0; + + /* select target bank */ + if(dp_bank < 0x10) { + ret = swd_select(ctx, 0, 0, dp_bank); + if(ret != 1) { + DBGS("swd_select failed"); + return ret; + } + } + + /* write it */ + ret = swd_transfer(ctx, false, true, dp_off, data); + if(ret != 1) { + DBG("failed: %d", ret); + return ret; + } + return ret; +} + +static uint8_t swd_read_ap(AppFSM* const ctx, uint8_t ap, uint8_t ap_off, uint32_t* data) { + /* select target bank */ + uint8_t ret = swd_select(ctx, ap, (ap_off >> 4) & 0x0F, 0); + if(ret != 1) { + DBGS("swd_select failed"); + return ret; + } + ret = swd_transfer(ctx, true, false, (ap_off >> 2) & 3, data); + *data = 0; + ret = swd_transfer(ctx, true, false, (ap_off >> 2) & 3, data); + if(ret != 1) { + DBG("failed: %d", ret); + return ret; + } + return ret; +} + +static uint8_t swd_read_ap_single(AppFSM* const ctx, uint8_t ap, uint8_t ap_off, uint32_t* data) { + uint8_t ret = swd_select(ctx, ap, (ap_off >> 4) & 0x0F, 0); + if(ret != 1) { + DBGS("swd_select failed"); + return ret; + } + *data = 0; + ret = swd_transfer(ctx, true, false, (ap_off >> 2) & 3, data); + if(ret != 1) { + DBG("failed: %d", ret); + return ret; + } + return ret; +} + +static uint8_t swd_write_ap(AppFSM* const ctx, uint8_t ap, uint8_t ap_off, uint32_t data) { + uint8_t ret = swd_select(ctx, ap, (ap_off >> 4) & 0x0F, 0); + if(ret != 1) { + DBGS("swd_select failed"); + return ret; + } + ret = swd_transfer(ctx, true, true, (ap_off >> 2) & 3, &data); + if(ret != 1) { + DBG("failed: %d", ret); + return ret; + } + return ret; +} + +static uint8_t swd_write_memory(AppFSM* const ctx, uint8_t ap, uint32_t address, uint32_t data) { + uint8_t ret = 0; + uint32_t csw = 0x23000002; + + ret |= swd_write_ap(ctx, ap, MEMAP_CSW, csw); + ret |= swd_write_ap(ctx, ap, MEMAP_TAR, address); + ret |= swd_write_ap(ctx, ap, MEMAP_DRW, data); + DBG("write 0x%08lX to 0x%08lX", data, address); + + if(ret != 1) { + swd_abort(ctx); + } + return ret; +} + +uint8_t swd_read_memory(AppFSM* const ctx, uint8_t ap, uint32_t address, uint32_t* data) { + uint8_t ret = 0; + uint32_t csw = 0x23000002; + + ret |= swd_write_ap(ctx, ap, MEMAP_CSW, csw); + ret |= swd_write_ap(ctx, ap, MEMAP_TAR, address); + ret |= swd_read_ap(ctx, ap, MEMAP_DRW, data); + + if(ret != 1) { + DBG("read from 0x%08lX failed", address); + swd_abort(ctx); + } else { + DBG("read 0x%08lX from 0x%08lX", *data, address); + } + return ret; +} + +static uint8_t swd_read_memory_block( + AppFSM* const ctx, + uint8_t ap, + uint32_t address, + uint8_t* buf, + uint32_t len) { + uint8_t ret = 0; + uint32_t data = 0; + uint32_t csw = 0x23000012; + + ret |= swd_write_ap(ctx, ap, MEMAP_CSW, csw); + ret |= swd_write_ap(ctx, ap, MEMAP_TAR, address); + ret |= swd_read_ap_single(ctx, ap, MEMAP_DRW, &data); + + for(size_t pos = 0; pos < len; pos += 4) { + data = 0xDEADBEEF; + ret |= swd_read_ap_single(ctx, ap, MEMAP_DRW, &data); + DBG("read %lX", data); + + memcpy(&buf[pos], &data, 4); + + if(ret != 1) { + swd_abort(ctx); + return ret; + } + } + return ret; +} + +static uint32_t swd_detect(AppFSM* const ctx) { + swd_set_data(ctx, false); + swd_configure_pins(ctx, true); + + uint8_t data[] = {0xA5}; + swd_write(ctx, data, sizeof(data) * 8); + + /* turnaround cycle */ + swd_configure_pins(ctx, false); + + uint8_t ack_bits[3]; + uint8_t rdata[32]; + + /* receive 3 ACK bits */ + for(int pos = 0; pos < 3; pos++) { + ack_bits[pos] = swd_read_bit(ctx); + } + + /* receive 32 RDATA bits */ + for(int pos = 0; pos < 32; pos++) { + rdata[pos] = swd_read_bit(ctx); + } + + /* receive parity bit */ + uint8_t parity = swd_read_bit(ctx); + + for(int io = 0; io < 8; io++) { + uint8_t bitmask = 1 << io; + + /* skip if it's a clock */ + if(ctx->current_mask & bitmask) { + continue; + } + + uint8_t ack = 0; + for(int pos = 0; pos < 3; pos++) { + ack >>= 1; + ack |= (ack_bits[pos] & bitmask) ? 4 : 0; + } + + uint32_t dpidr = 0; + for(int pos = 0; pos < 32; pos++) { + dpidr >>= 1; + dpidr |= (rdata[pos] & bitmask) ? 0x80000000 : 0; + } + + if(ack == 1 && dpidr != 0 && dpidr != 0xFFFFFFFF) { + bool received_parity = (parity & bitmask); + if(__builtin_parity(dpidr) == received_parity) { + ctx->dp_regs.dpidr = dpidr; + ctx->dp_regs.dpidr_ok = true; + ctx->detected = true; + ctx->io_swd = bitmask; + ctx->io_swc &= ctx->current_mask; + LOG("swd_detect: data: %08lX, io_swd %02X, io_swc %02X", + dpidr, + ctx->io_swd, + ctx->io_swc); + + if(!has_multiple_bits(ctx->io_swc)) { + ctx->io_num_swd = get_bit_num(ctx->io_swd); + ctx->io_num_swc = get_bit_num(ctx->io_swc); + } + } + } + } + swd_set_data(ctx, false); + swd_configure_pins(ctx, true); + + return 0; +} + +static void swd_scan(AppFSM* const ctx) { + /* To switch SWJ-DP from JTAG to SWD operation: + 1. Send at least 50 SWCLKTCK cycles with SWDIOTMS HIGH. This ensures that the current interface is in its reset state. The JTAG interface only detects the 16-bit JTAG-to-SWD sequence starting from the Test-Logic-Reset state. + 2. Send the 16-bit JTAG-to-SWD select sequence 0x79e7 on SWDIOTMS. + 3. Send at least 50 SWCLKTCK cycles with SWDIOTMS HIGH. This ensures that if SWJ-DP was already in SWD operation before sending the select sequence, the SWD interface enters line reset state. + */ + swd_configure_pins(ctx, true); + + /* reset JTAG interface */ + for(int bitcount = 0; bitcount < 50; bitcount += 8) { + swd_write_byte(ctx, 0xFF, 8); + } + + /* Send the 16-bit JTAG-to-SWD select sequence */ + swd_write_byte(ctx, 0x9E, 8); + swd_write_byte(ctx, 0xE7, 8); + + /* resynchronize SWD */ + swd_line_reset(ctx); + + swd_detect(ctx); +} + +static bool swd_ensure_powerup(AppFSM* const ctx) { + bool ret = true; + + if(!(ctx->dp_regs.ctrlstat & (CSYSPWRUPREQ | CDBGPWRUPREQ))) { + DBGS("no (CSYSPWRUPREQ | CDBGPWRUPREQ)"); + + /* fetch current CTRL/STAT */ + DBGS(" - Fetch CTRL/STAT"); + ctx->dp_regs.ctrlstat_ok = + swd_read_dpbank(ctx, REG_CTRLSTAT, REG_CTRLSTAT_BANK, &ctx->dp_regs.ctrlstat) == 1; + DBG(" %08lX %s", ctx->dp_regs.ctrlstat, ctx->dp_regs.ctrlstat_ok ? "OK" : "FAIL"); + /* enable requests */ + ctx->dp_regs.ctrlstat |= (CSYSPWRUPREQ | CDBGPWRUPREQ); + + swd_write_dpbank(ctx, REG_CTRLSTAT, REG_CTRLSTAT_BANK, &ctx->dp_regs.ctrlstat); + + ret = false; + } + if(!(ctx->dp_regs.ctrlstat & CDBGPWRUPACK)) { + DBGS("no CDBGPWRUPACK"); + /* fetch current CTRL/STAT */ + swd_read_dpbank(ctx, REG_CTRLSTAT, REG_CTRLSTAT_BANK, &ctx->dp_regs.ctrlstat); + ret = false; + } + + if(!ret) { + DBGS(" - Fetch CTRL/STAT"); + ctx->dp_regs.ctrlstat_ok = + swd_read_dpbank(ctx, REG_CTRLSTAT, REG_CTRLSTAT_BANK, &ctx->dp_regs.ctrlstat) == 1; + DBG(" %08lX %s", ctx->dp_regs.ctrlstat, ctx->dp_regs.ctrlstat_ok ? "OK" : "FAIL"); + } + + return ret; +} + +static void swd_apscan_reset(AppFSM* const ctx) { + for(size_t reset_ap = 0; reset_ap < COUNT(ctx->apidr_info); reset_ap++) { + ctx->apidr_info[reset_ap].tested = false; + } +} + +static bool swd_apscan_test(AppFSM* const ctx, uint32_t ap) { + furi_assert(ctx); + furi_assert(ap < sizeof(ctx->apidr_info)); + bool ret = true; + + ctx->apidr_info[ap].tested = true; + + uint32_t data = 0; + if(swd_read_ap(ctx, ap, AP_IDR, &data) != 1) { + swd_abort(ctx); + return false; + } + if(data == 0) { + return false; + } + DBG("AP%lu detected", ap); + ctx->apidr_info[ap].ok = true; + ctx->apidr_info[ap].revision = (data >> 24) & 0x0F; + ctx->apidr_info[ap].designer = (data >> 17) & 0x3FF; + ctx->apidr_info[ap].class = (data >> 13) & 0x0F; + ctx->apidr_info[ap].variant = (data >> 4) & 0x0F; + ctx->apidr_info[ap].type = (data >> 0) & 0x0F; + + if(swd_read_ap(ctx, ap, AP_BASE, &ctx->apidr_info[ap].base) != 1) { + swd_abort(ctx); + ret = false; + } + return ret; +} + +/************************** script helpers **************************/ + +static void swd_script_log(ScriptContext* ctx, FuriLogLevel level, const char* format, ...) { + bool commandline = false; + ScriptContext* cur = ctx; + char buffer[256]; + va_list argp; + va_start(argp, format); + + do { + if(cur == ctx->app->commandline) { + commandline = true; + } + cur = cur->parent; + } while(cur); + + if(commandline) { + const char* prefix = ""; + + switch(level) { + case FuriLogLevelWarn: + prefix = "Warning: "; + break; + case FuriLogLevelError: + prefix = "ERROR: "; + break; + default: + break; + } + + strcpy(buffer, prefix); + size_t pos = strlen(buffer); + vsnprintf(&buffer[pos], sizeof(buffer) - pos - 2, format, argp); + strcat(buffer, "\n"); + if(!usb_uart_tx_data(ctx->app->uart, (uint8_t*)buffer, strlen(buffer))) { + DBGS("Sending via USB failed"); + } + } else { + LOG(buffer); + } + va_end(argp); +} + +/* read characters until newline was read */ +static bool swd_script_seek_newline(ScriptContext* ctx) { + while(true) { + uint8_t ch = 0; + + if(ctx->script_file) { + if(storage_file_read(ctx->script_file, &ch, 1) != 1) { + return false; + } + } else { + ch = ctx->line_data[ctx->line_pos]; + if(ch == 0) { + return false; + } + ctx->line_pos++; + } + if(ch == '\n') { + return true; + } + } +} + +/* read whitespaces until the next character is read. + returns false if EOF or newline was read */ +static bool swd_script_skip_whitespace(ScriptContext* ctx) { + while(true) { + uint8_t ch = 0; + uint64_t start_pos = 0; + + if(ctx->script_file) { + start_pos = storage_file_tell(ctx->script_file); + + if(storage_file_read(ctx->script_file, &ch, 1) != 1) { + return false; + } + } else { + start_pos = ctx->line_pos; + ch = ctx->line_data[ctx->line_pos]; + + if(ch == 0) { + return false; + } + ctx->line_pos++; + } + if(ch == '\n') { + return false; + } + if(ch != ' ') { + if(ctx->script_file) { + storage_file_seek(ctx->script_file, start_pos, true); + } else { + ctx->line_pos = start_pos; + } + return true; + } + } +} + +static bool swd_script_get_string(ScriptContext* ctx, char* str, size_t max_length) { + bool quot = false; + size_t pos = 0; + + str[pos] = '\000'; + + while(true) { + char ch = 0; + uint64_t start_pos = 0; + + if(ctx->script_file) { + start_pos = storage_file_tell(ctx->script_file); + + if(storage_file_read(ctx->script_file, &ch, 1) != 1) { + DBGS("end reached"); + return false; + } + } else { + start_pos = ctx->line_pos; + ch = ctx->line_data[ctx->line_pos]; + + if(ch == 0) { + DBGS("end reached"); + return false; + } + ctx->line_pos++; + } + + if(ch == '"') { + quot = !quot; + continue; + } + if(!quot) { + if(ch == ' ') { + break; + } + if(ch == '\r' || ch == '\n') { + if(ctx->script_file) { + storage_file_seek(ctx->script_file, start_pos, true); + } else { + ctx->line_pos = start_pos; + } + break; + } + } + if(pos + 2 > max_length) { + DBGS("too long"); + return false; + } + str[pos++] = ch; + str[pos] = '\000'; + } + DBG("got '%s'", str); + + return true; +} + +static bool swd_script_get_number(ScriptContext* ctx, uint32_t* number) { + char str[16]; + + if(!swd_script_get_string(ctx, str, sizeof(str))) { + DBGS("could not get string"); + return false; + } + DBG("got '%s'", str); + + size_t pos = 0; + *number = 0; + + /* hex number? */ + if(!strncmp(str, "0x", 2)) { + pos += 2; + while(str[pos]) { + uint8_t ch = str[pos++]; + uint8_t ch_num = ch - '0'; + uint8_t ch_hex = (ch & ~0x20) - 'A'; + + *number <<= 4; + + if(ch_num <= 10) { + *number += ch_num; + } else if(ch_hex <= 5) { + *number += 10 + ch_hex; + } else { + return false; + } + } + } else { + while(str[pos]) { + uint8_t ch = str[pos++]; + uint8_t ch_num = ch - '0'; + + *number *= 10; + + if(ch_num < 10) { + *number += ch_num; + } else { + return false; + } + } + } + + return true; +} + +static void swd_script_gui_refresh(ScriptContext* ctx) { + if(furi_message_queue_get_count(ctx->app->event_queue) > 0) { + swd_message_process(ctx->app); + } + if(!ctx->status_ignore) { + DBG("Status: %s", ctx->app->state_string); + view_port_update(ctx->app->view_port); + } +} + +/************************** script functions **************************/ + +static bool swd_scriptfunc_comment(ScriptContext* ctx) { + DBGS("comment"); + + swd_script_seek_newline(ctx); + + return true; +} + +static bool swd_scriptfunc_label(ScriptContext* ctx) { + char label[256]; + DBGS("label"); + + swd_script_skip_whitespace(ctx); + if(!swd_script_get_string(ctx, label, sizeof(label))) { + swd_script_log(ctx, FuriLogLevelError, "failed to parse label"); + return false; + } + + if(!strcmp(label, ctx->goto_label)) { + ctx->goto_active = false; + DBG("matches '%s'", ctx->goto_label); + } + + swd_script_seek_newline(ctx); + + return true; +} + +static bool swd_scriptfunc_goto(ScriptContext* ctx) { + DBGS("goto"); + + swd_script_skip_whitespace(ctx); + + if(!swd_script_get_string(ctx, ctx->goto_label, sizeof(ctx->goto_label))) { + swd_script_log(ctx, FuriLogLevelError, "failed to parse target label"); + return false; + } + + /* start from beginning and rerun starting from label */ + ctx->goto_active = true; + ctx->restart = true; + + swd_script_seek_newline(ctx); + + return true; +} + +static bool swd_scriptfunc_call(ScriptContext* ctx) { + DBGS("call"); + + swd_script_skip_whitespace(ctx); + + /* fetch previous file directory */ + char filename[MAX_FILE_LENGTH]; + strncpy(filename, ctx->filename, sizeof(filename)); + char* path = strrchr(filename, '/'); + path[1] = '\000'; + + /* append filename */ + if(!swd_script_get_string(ctx, &path[1], sizeof(filename) - strlen(path))) { + swd_script_log(ctx, FuriLogLevelError, "failed to parse filename"); + return false; + } + + swd_script_seek_newline(ctx); + + /* append extension */ + if(strlen(filename) + 5 >= sizeof(filename)) { + swd_script_log(ctx, FuriLogLevelError, "name too long"); + return false; + } + + strcat(filename, ".swd"); + + bool ret = swd_execute_script(ctx->app, filename); + + if(!ret) { + swd_script_log(ctx, FuriLogLevelError, "failed to exec '%s'", filename); + return false; + } + + return true; +} + +static bool swd_scriptfunc_status(ScriptContext* ctx) { + uint32_t status = 1; + DBGS("status"); + + swd_script_skip_whitespace(ctx); + swd_script_get_number(ctx, &status); + + ctx->status_ignore = (status == 0); + + swd_script_seek_newline(ctx); + + return true; +} + +static bool swd_scriptfunc_errors(ScriptContext* ctx) { + char type[32]; + DBGS("errors"); + + swd_script_skip_whitespace(ctx); + + if(!swd_script_get_string(ctx, type, sizeof(type))) { + swd_script_log(ctx, FuriLogLevelError, "failed to parse"); + return false; + } + + if(!strcmp(type, "ignore")) { + ctx->errors_ignore = true; + } + if(!strcmp(type, "fail")) { + ctx->errors_ignore = false; + } + swd_script_seek_newline(ctx); + + return true; +} + +static bool swd_scriptfunc_beep(ScriptContext* ctx) { + uint32_t sound = 0; + DBGS("beep"); + + swd_script_skip_whitespace(ctx); + swd_script_get_number(ctx, &sound); + + notification_message_block(ctx->app->notification, seq_sounds[sound]); + + swd_script_seek_newline(ctx); + + return true; +} + +static bool swd_scriptfunc_message(ScriptContext* ctx) { + uint32_t wait_time = 0; + char message[256]; + char type[256]; + bool success = true; + bool show_dialog = false; + + if(!swd_script_skip_whitespace(ctx)) { + swd_script_log(ctx, FuriLogLevelError, "missing whitespace"); + return false; + } + + if(!swd_script_get_number(ctx, &wait_time)) { + swd_script_log(ctx, FuriLogLevelError, "failed to parse wait_time"); + return false; + } + + if(!swd_script_get_string(ctx, message, sizeof(message))) { + swd_script_log(ctx, FuriLogLevelError, "failed to parse message"); + return false; + } + + if(swd_script_get_string(ctx, type, sizeof(type))) { + if(!strcmp(type, "dialog")) { + show_dialog = true; + } + } + + if(wait_time <= 60 * 1000) { + strncpy(ctx->app->state_string, message, sizeof(ctx->app->state_string)); + swd_script_gui_refresh(ctx); + furi_delay_ms(wait_time); + if(show_dialog) { + DialogMessage* message = dialog_message_alloc(); + dialog_message_set_header(message, "SWD Probe", 16, 2, AlignLeft, AlignTop); + dialog_message_set_icon(message, &I_app, 3, 2); + dialog_message_set_text(message, ctx->app->state_string, 3, 16, AlignLeft, AlignTop); + dialog_message_set_buttons(message, "Abort", "Ok", NULL); + success = dialog_message_show(ctx->app->dialogs, message) == DialogMessageButtonCenter; + dialog_message_free(message); + } + } + + swd_script_seek_newline(ctx); + + return success; +} + +static bool swd_scriptfunc_swd_idle_bits(ScriptContext* ctx) { + uint32_t swd_idle_bits = 0; + + if(!swd_script_skip_whitespace(ctx)) { + swd_script_log(ctx, FuriLogLevelError, "missing whitespace"); + return false; + } + + if(!swd_script_get_number(ctx, &swd_idle_bits)) { + swd_script_log(ctx, FuriLogLevelError, "failed to parse"); + return false; + } + + if(swd_idle_bits <= 32) { + ctx->app->swd_idle_bits = swd_idle_bits; + } else { + swd_script_log(ctx, FuriLogLevelError, "value must be between 1 and 32"); + } + + swd_script_seek_newline(ctx); + + return true; +} + +static bool swd_scriptfunc_swd_clock_delay(ScriptContext* ctx) { + uint32_t swd_clock_delay = 0; + + if(!swd_script_skip_whitespace(ctx)) { + swd_script_log(ctx, FuriLogLevelError, "missing whitespace"); + return false; + } + + if(!swd_script_get_number(ctx, &swd_clock_delay)) { + swd_script_log(ctx, FuriLogLevelError, "failed to parse"); + return false; + } + + if(swd_clock_delay <= 1000000) { + ctx->app->swd_clock_delay = swd_clock_delay; + } else { + swd_script_log(ctx, FuriLogLevelError, "value must be between 1 and 1000000"); + } + + swd_script_seek_newline(ctx); + + return true; +} + +static bool swd_scriptfunc_maxtries(ScriptContext* ctx) { + uint32_t max_tries = 0; + + if(!swd_script_skip_whitespace(ctx)) { + swd_script_log(ctx, FuriLogLevelError, "missing whitespace"); + return false; + } + + if(!swd_script_get_number(ctx, &max_tries)) { + swd_script_log(ctx, FuriLogLevelError, "failed to parse"); + return false; + } + + if(max_tries >= 1 && max_tries <= 1024) { + ctx->max_tries = max_tries; + } else { + DBGS("value must be between 1 and 1024"); + } + + swd_script_seek_newline(ctx); + + return true; +} + +static bool swd_scriptfunc_blocksize(ScriptContext* ctx) { + uint32_t block_size = 0; + + if(!swd_script_skip_whitespace(ctx)) { + swd_script_log(ctx, FuriLogLevelError, "missing whitespace"); + return false; + } + + if(!swd_script_get_number(ctx, &block_size)) { + swd_script_log(ctx, FuriLogLevelError, "failed to parse"); + return false; + } + + if(block_size >= 4 && block_size <= 0x1000) { + ctx->block_size = block_size; + } else { + swd_script_log(ctx, FuriLogLevelError, "value must be between 4 and 4096"); + } + + swd_script_seek_newline(ctx); + + return true; +} + +static bool swd_scriptfunc_apselect(ScriptContext* ctx) { + uint32_t ap = 0; + + if(!swd_script_skip_whitespace(ctx)) { + swd_script_log(ctx, FuriLogLevelError, "missing whitespace"); + return false; + } + + if(!swd_script_get_number(ctx, &ap)) { + swd_script_log(ctx, FuriLogLevelError, "failed to parse AP"); + return false; + } + + if(!swd_apscan_test(ctx->app, ap)) { + swd_script_log(ctx, FuriLogLevelError, "no selected AP"); + return false; + } + + ctx->selected_ap = ap; + + swd_script_seek_newline(ctx); + + return true; +} + +static bool swd_scriptfunc_apscan(ScriptContext* ctx) { + DBGS("Scanning APs"); + for(uint32_t ap = 0; ap < 255; ap++) { + snprintf(ctx->app->state_string, sizeof(ctx->app->state_string), "Scan AP %lu", ap); + swd_script_gui_refresh(ctx); + if(swd_apscan_test(ctx->app, ap)) { + DBG(" AP%lu detected", ap); + } + } + swd_script_seek_newline(ctx); + + return true; +} + +static bool swd_scriptfunc_abort(ScriptContext* ctx) { + DBGS("Aborting"); + swd_abort(ctx->app); + swd_script_seek_newline(ctx); + + return true; +} + +static bool swd_scriptfunc_mem_dump(ScriptContext* ctx) { + char filename[MAX_FILE_LENGTH]; + uint32_t address = 0; + uint32_t length = 0; + uint32_t flags = 0; + bool success = true; + + /* get file */ + if(!swd_script_skip_whitespace(ctx)) { + swd_script_log(ctx, FuriLogLevelError, "missing whitespace"); + return false; + } + + if(!swd_script_get_string(ctx, filename, sizeof(filename))) { + swd_script_log(ctx, FuriLogLevelError, "failed to parse filename"); + return false; + } + /* get address */ + if(!swd_script_get_number(ctx, &address)) { + swd_script_log(ctx, FuriLogLevelError, "failed to parse address"); + return false; + } + + /* get length */ + if(!swd_script_get_number(ctx, &length)) { + swd_script_log(ctx, FuriLogLevelError, "failed to parse length"); + return false; + } + + /* get flags */ + if(swd_script_get_number(ctx, &flags)) { + DBGS("found extra flags"); + } + + LOG("would dump %08lX, len %08lX into %s", address, length, filename); + + File* dump = storage_file_alloc(ctx->app->storage); + + if(!storage_file_open(dump, filename, FSAM_WRITE, FSOM_CREATE_ALWAYS)) { + storage_file_free(dump); + snprintf(ctx->app->state_string, sizeof(ctx->app->state_string), "Failed to create file"); + swd_script_gui_refresh(ctx); + notification_message_block(ctx->app->notification, &seq_error); + return false; + } + + if(ctx->block_size == 0) { + ctx->block_size = 0x100; + } + if(ctx->block_size > 0x1000) { + ctx->block_size = 0x1000; + } + + uint8_t* buffer = malloc(ctx->block_size); + + furi_mutex_acquire(ctx->app->swd_mutex, FuriWaitForever); + + for(uint32_t pos = 0; pos < length; pos += ctx->block_size) { + if((pos & 0xFF) == 0) { + int pct = pos * 100 / length; + snprintf( + ctx->app->state_string, + sizeof(ctx->app->state_string), + "Dump %08lX (%d%%)", + pos, + pct); + swd_script_gui_refresh(ctx); + } + + bool read_ok = false; + + for(uint32_t tries = 0; tries < ctx->max_tries; tries++) { + if(ctx->abort) { + DBGS("aborting read"); + break; + } + uint32_t ret = 0; + + if(ctx->block_size > 4) { + ret = swd_read_memory_block( + ctx->app, ctx->selected_ap, address + pos, buffer, ctx->block_size); + } else { + ret = + swd_read_memory(ctx->app, ctx->selected_ap, address + pos, (uint32_t*)buffer); + } + read_ok = (ret == 1); + + if(!read_ok) { + snprintf( + ctx->app->state_string, + sizeof(ctx->app->state_string), + "Failed at 0x%08lX", + address + pos); + swd_script_gui_refresh(ctx); + furi_delay_ms(100); + } else { + break; + } + } + if(ctx->abort) { + DBGS("aborting"); + break; + } + + if(!read_ok) { + /* flags == 1: "continue reading even if it fails" */ + /* flags == 2: "its okay if cannot dump fully" */ + if(flags & 1) { + /* set all content to a known value as indication */ + for(size_t fill_pos = 0; fill_pos < ctx->block_size; fill_pos += 4) { + *((uint32_t*)&buffer[fill_pos]) = 0xDEADFACE; + } + } else if(flags & 2) { + success = (pos > 0); + break; + } else { + notification_message_block(ctx->app->notification, &seq_error); + success = false; + break; + } + } + storage_file_write(dump, buffer, ctx->block_size); + } + + furi_mutex_release(ctx->app->swd_mutex); + + storage_file_close(dump); + swd_script_seek_newline(ctx); + free(buffer); + + return success; +} + +static bool swd_scriptfunc_mem_write(ScriptContext* ctx) { + uint32_t address = 0; + uint32_t data = 0; + bool success = true; + + /* get file */ + if(!swd_script_skip_whitespace(ctx)) { + swd_script_log(ctx, FuriLogLevelError, "missing whitespace"); + return false; + } + + /* get address */ + if(!swd_script_get_number(ctx, &address)) { + swd_script_log(ctx, FuriLogLevelError, "failed to parse address"); + return false; + } + + /* get data */ + if(!swd_script_get_number(ctx, &data)) { + swd_script_log(ctx, FuriLogLevelError, "failed to parse data"); + return false; + } + + DBG("write %08lX to %08lX", data, address); + + bool access_ok = false; + for(uint32_t tries = 0; tries < ctx->max_tries; tries++) { + if(ctx->abort) { + DBGS("aborting"); + break; + } + + furi_mutex_acquire(ctx->app->swd_mutex, FuriWaitForever); + access_ok = swd_write_memory(ctx->app, ctx->selected_ap, address, data) == 1; + access_ok |= ctx->errors_ignore; + swd_read_memory(ctx->app, ctx->selected_ap, address, &data); + furi_mutex_release(ctx->app->swd_mutex); + + DBG("read %08lX from %08lX", data, address); + + if(!access_ok) { + snprintf( + ctx->app->state_string, + sizeof(ctx->app->state_string), + "Failed write 0x%08lX", + address); + swd_script_gui_refresh(ctx); + } else { + break; + } + } + + if(!access_ok) { + notification_message_block(ctx->app->notification, &seq_error); + success = false; + } + + swd_script_seek_newline(ctx); + + return success; +} + +static bool swd_scriptfunc_mem_read(ScriptContext* ctx) { + uint32_t address = 0; + bool success = true; + + /* get file */ + if(!swd_script_skip_whitespace(ctx)) { + swd_script_log(ctx, FuriLogLevelError, "missing whitespace"); + return false; + } + + /* get address */ + if(!swd_script_get_number(ctx, &address)) { + swd_script_log(ctx, FuriLogLevelError, "failed to parse address"); + return false; + } + + DBG("read from %08lX", address); + + uint32_t data = 0; + bool access_ok = false; + for(uint32_t tries = 0; tries < ctx->max_tries; tries++) { + if(ctx->abort) { + DBGS("aborting"); + break; + } + + furi_mutex_acquire(ctx->app->swd_mutex, FuriWaitForever); + access_ok = swd_read_memory(ctx->app, ctx->selected_ap, address, &data) == 1; + furi_mutex_release(ctx->app->swd_mutex); + + if(!access_ok) { + swd_script_log(ctx, FuriLogLevelError, "Failed to read from %08lX", address); + snprintf( + ctx->app->state_string, + sizeof(ctx->app->state_string), + "Failed read 0x%08lX", + address); + swd_script_gui_refresh(ctx); + } else { + swd_script_log(ctx, FuriLogLevelDefault, "%08lX", data); + break; + } + } + + if(!access_ok) { + notification_message_block(ctx->app->notification, &seq_error); + success = false; + } + + swd_script_seek_newline(ctx); + + return success; +} + +static bool swd_scriptfunc_mem_ldmst(ScriptContext* ctx) { + uint32_t address = 0; + uint32_t data = 0; + uint32_t mask = 0; + bool success = true; + + if(!swd_script_skip_whitespace(ctx)) { + swd_script_log(ctx, FuriLogLevelError, "missing whitespace"); + return false; + } + + /* get address */ + if(!swd_script_get_number(ctx, &address)) { + swd_script_log(ctx, FuriLogLevelError, "failed to parse address"); + return false; + } + + /* get data */ + if(!swd_script_get_number(ctx, &data)) { + swd_script_log(ctx, FuriLogLevelError, "failed to parse data"); + return false; + } + + /* get mask */ + if(!swd_script_get_number(ctx, &mask)) { + swd_script_log(ctx, FuriLogLevelError, "failed to parse mask"); + return false; + } + + LOG("write %08lX to %08lX, mask %08lX", data, address, mask); + + bool access_ok = false; + uint32_t modified = 0; + for(uint32_t tries = 0; tries < ctx->max_tries; tries++) { + if(ctx->abort) { + DBGS("aborting"); + break; + } + furi_mutex_acquire(ctx->app->swd_mutex, FuriWaitForever); + + access_ok = swd_read_memory(ctx->app, ctx->selected_ap, address, &modified) == 1; + modified = (modified & mask) | data; + access_ok &= swd_write_memory(ctx->app, ctx->selected_ap, address, modified) == 1; + + furi_mutex_release(ctx->app->swd_mutex); + access_ok |= ctx->errors_ignore; + + if(!access_ok) { + snprintf( + ctx->app->state_string, + sizeof(ctx->app->state_string), + "Failed access 0x%08lX", + address); + swd_script_gui_refresh(ctx); + } else { + break; + } + } + + if(!access_ok) { + notification_message_block(ctx->app->notification, &seq_error); + success = false; + } + + swd_script_seek_newline(ctx); + + return success; +} + +static bool swd_scriptfunc_dp_write(ScriptContext* ctx) { + uint32_t dp_bank = 0; + uint32_t dp_off = 0; + uint32_t data = 0; + bool success = true; + + if(!swd_script_skip_whitespace(ctx)) { + swd_script_log(ctx, FuriLogLevelError, "missing whitespace"); + return false; + } + + /* get data */ + if(!swd_script_get_number(ctx, &data)) { + swd_script_log(ctx, FuriLogLevelError, "failed to parse data"); + return false; + } + + /* get dp_off */ + if(!swd_script_get_number(ctx, &dp_off)) { + swd_script_log(ctx, FuriLogLevelError, "failed to parse DP offset"); + return false; + } + + /* get dp_bank */ + if(!swd_script_get_number(ctx, &dp_bank)) { + dp_bank = 0xFF; + } + + swd_script_log( + ctx, FuriLogLevelDefault, "write %08lX to reg %08lX / bank %08lX", data, dp_off, dp_bank); + + furi_mutex_acquire(ctx->app->swd_mutex, FuriWaitForever); + + uint8_t ret = swd_write_dpbank(ctx->app, dp_off, dp_bank, &data); + if(ret != 1) { + swd_script_log(ctx, FuriLogLevelError, "swd_write_dpbank failed"); + success = false; + } + + furi_mutex_release(ctx->app->swd_mutex); + + swd_script_seek_newline(ctx); + + return success; +} + +static bool swd_scriptfunc_dp_read(ScriptContext* ctx) { + uint32_t dp_bank = 0; + uint32_t dp_off = 0; + uint32_t data = 0; + bool success = true; + + if(!swd_script_skip_whitespace(ctx)) { + swd_script_log(ctx, FuriLogLevelError, "missing whitespace"); + return false; + } + + /* get dp_off */ + if(!swd_script_get_number(ctx, &dp_off)) { + swd_script_log(ctx, FuriLogLevelError, "failed to parse DP offset"); + return false; + } + + /* get dp_bank */ + if(!swd_script_get_number(ctx, &dp_bank)) { + dp_bank = 0xFF; + } + + swd_script_log(ctx, FuriLogLevelDefault, "read reg %02lX / bank %02lX", dp_off, dp_bank); + + furi_mutex_acquire(ctx->app->swd_mutex, FuriWaitForever); + + uint8_t ret = swd_read_dpbank(ctx->app, dp_off, dp_bank, &data); + if(ret != 1) { + swd_script_log(ctx, FuriLogLevelError, "swd_read_dpbank failed"); + success = false; + } else { + swd_script_log(ctx, FuriLogLevelDefault, "result: 0x%08lX", data); + } + furi_mutex_release(ctx->app->swd_mutex); + + swd_script_seek_newline(ctx); + + return success; +} + +static bool swd_scriptfunc_ap_write(ScriptContext* ctx) { + uint32_t ap_reg = 0; + uint32_t data = 0; + bool success = true; + + if(!swd_script_skip_whitespace(ctx)) { + swd_script_log(ctx, FuriLogLevelError, "missing whitespace"); + return false; + } + + /* get data */ + if(!swd_script_get_number(ctx, &data)) { + swd_script_log(ctx, FuriLogLevelError, "failed to parse data"); + return false; + } + + /* get ap_reg */ + if(!swd_script_get_number(ctx, &ap_reg)) { + swd_script_log(ctx, FuriLogLevelError, "failed to parse AP register"); + return false; + } + + swd_script_log( + ctx, FuriLogLevelDefault, "AP%d %08lX -> %02lX", ctx->selected_ap, data, ap_reg); + + furi_mutex_acquire(ctx->app->swd_mutex, FuriWaitForever); + + uint8_t ret = swd_write_ap(ctx->app, ctx->selected_ap, ap_reg, data); + if(ret != 1) { + swd_script_log(ctx, FuriLogLevelError, "swd_write_ap failed"); + success = false; + } + furi_mutex_release(ctx->app->swd_mutex); + + swd_script_seek_newline(ctx); + + return success; +} + +static bool swd_scriptfunc_ap_read(ScriptContext* ctx) { + uint32_t ap_reg = 0; + uint32_t data = 0; + bool success = true; + + if(!swd_script_skip_whitespace(ctx)) { + swd_script_log(ctx, FuriLogLevelError, "missing whitespace"); + return false; + } + + /* get ap_reg */ + if(!swd_script_get_number(ctx, &ap_reg)) { + swd_script_log(ctx, FuriLogLevelError, "failed to parse AP register"); + return false; + } + + furi_mutex_acquire(ctx->app->swd_mutex, FuriWaitForever); + + uint8_t ret = swd_read_ap(ctx->app, ctx->selected_ap, ap_reg, &data); + if(ret != 1) { + swd_script_log(ctx, FuriLogLevelError, "swd_read_ap failed"); + success = false; + } else { + swd_script_log( + ctx, FuriLogLevelDefault, "AP%d %02lX: %08lX", ctx->selected_ap, ap_reg, data); + } + furi_mutex_release(ctx->app->swd_mutex); + + swd_script_seek_newline(ctx); + + return success; +} + +static bool swd_scriptfunc_core_halt(ScriptContext* ctx) { + bool succ = false; + + furi_mutex_acquire(ctx->app->swd_mutex, FuriWaitForever); + uint32_t reg_dhcsr = SCS_DHCSR_KEY | SCS_DHCSR_C_HALT | SCS_DHCSR_C_DEBUGEN; + + succ = swd_write_memory(ctx->app, ctx->selected_ap, SCS_DHCSR, reg_dhcsr) == 1; + + if(!succ) { + swd_script_log(ctx, FuriLogLevelError, "swd_write_memory failed"); + } else { + swd_read_memory(ctx->app, ctx->selected_ap, SCS_DHCSR, ®_dhcsr); + + if(!(reg_dhcsr & SCS_DHCSR_S_HALT)) { + swd_script_log(ctx, FuriLogLevelError, "Core did not halt"); + succ = false; + } else { + swd_script_log(ctx, FuriLogLevelDefault, "Core halted"); + } + } + + furi_mutex_release(ctx->app->swd_mutex); + swd_script_seek_newline(ctx); + + return succ; +} + +static bool swd_scriptfunc_core_continue(ScriptContext* ctx) { + bool succ = false; + uint32_t data = 0; + + furi_mutex_acquire(ctx->app->swd_mutex, FuriWaitForever); + succ = swd_read_memory(ctx->app, ctx->selected_ap, SCS_DHCSR, &data) == 1; + + if(!(data & SCS_DHCSR_S_HALT)) { + swd_script_log(ctx, FuriLogLevelError, "Core is not in debug state"); + succ = false; + } else { + succ = swd_write_memory(ctx->app, ctx->selected_ap, SCS_DHCSR, SCS_DHCSR_KEY) == 1; + furi_mutex_release(ctx->app->swd_mutex); + } + + if(!succ) { + swd_script_log(ctx, FuriLogLevelError, "swd_write_memory failed"); + } else { + swd_script_log(ctx, FuriLogLevelDefault, "Core continued"); + } + + swd_script_seek_newline(ctx); + + return succ; +} + +static bool swd_scriptfunc_core_step(ScriptContext* ctx) { + bool succ = false; + uint32_t data = 0; + + furi_mutex_acquire(ctx->app->swd_mutex, FuriWaitForever); + succ = swd_read_memory(ctx->app, ctx->selected_ap, SCS_DHCSR, &data) == 1; + + if(!(data & SCS_DHCSR_S_HALT)) { + swd_script_log(ctx, FuriLogLevelError, "Core is not in debug state"); + succ = false; + } else { + succ = swd_write_memory( + ctx->app, + ctx->selected_ap, + SCS_DHCSR, + SCS_DHCSR_KEY | SCS_DHCSR_C_STEP | SCS_DHCSR_C_MASKINTS | + SCS_DHCSR_C_DEBUGEN) == 1; + } + furi_mutex_release(ctx->app->swd_mutex); + + if(!succ) { + swd_script_log(ctx, FuriLogLevelError, "swd_write_memory failed"); + } else { + swd_script_log(ctx, FuriLogLevelDefault, "Core stepped"); + } + + swd_script_seek_newline(ctx); + + return succ; +} + +static struct cpu_regs_type { + uint8_t regsel; + const char* desc; +} cpu_regs[] = { + {0x00, "R00"}, {0x01, "R01"}, {0x02, "R02"}, {0x03, "R03"}, {0x04, "R04"}, + {0x05, "R05"}, {0x06, "R06"}, {0x07, "R07"}, {0x08, "R08"}, {0x09, "R09"}, + {0x0A, "R10"}, {0x0B, "R11"}, {0x0C, "R12"}, {0x0D, "SP/R13"}, {0x0E, "LR/R14"}, + {0x0F, "PC/R15"}, {0x10, "xPSR"}, {0x11, "MSP"}, {0x12, "PSP"}, {0x14, "Flags"}, + {0x21, "FPCSR"}, {0x40, "FP S00"}, {0x41, "FP S01"}, {0x42, "FP S02"}, {0x43, "FP S03"}, + {0x44, "FP S04"}, {0x45, "FP S05"}, {0x46, "FP S06"}, {0x47, "FP S07"}, {0x48, "FP S08"}, + {0x49, "FP S09"}, {0x4A, "FP S10"}, {0x4B, "FP S11"}, {0x4C, "FP S12"}, {0x4D, "FP S13"}, + {0x4E, "FP S14"}, {0x4F, "FP S15"}, {0x50, "FP S16"}, {0x51, "FP S17"}, {0x52, "FP S18"}, + {0x53, "FP S19"}, {0x54, "FP S20"}, {0x55, "FP S21"}, {0x56, "FP S22"}, {0x57, "FP S23"}, + {0x58, "FP S24"}, {0x59, "FP S25"}, {0x5A, "FP S26"}, {0x5B, "FP S27"}, {0x5C, "FP S28"}, + {0x5D, "FP S29"}, {0x5E, "FP S30"}, {0x5F, "FP S31"}}; + +static bool swd_scriptfunc_core_regs(ScriptContext* ctx) { + bool succ = false; + + furi_mutex_acquire(ctx->app->swd_mutex, FuriWaitForever); + + uint32_t reg_dhcsr = 0; + uint32_t reg_cpacr = 0; + swd_read_memory(ctx->app, ctx->selected_ap, SCS_DHCSR, ®_dhcsr); + swd_read_memory(ctx->app, ctx->selected_ap, SCS_CPACR, ®_cpacr); + + /* when FPU is enabled/available, CP10 and CP11 are implemented */ + bool has_fpu = ((reg_cpacr >> 20) & 0x0F) != 0; + + if(!(reg_dhcsr & SCS_DHCSR_S_HALT)) { + swd_script_log(ctx, FuriLogLevelError, "Core is not in debug state"); + succ = false; + } else { + for(size_t pos = 0; pos < COUNT(cpu_regs); pos++) { + if(!has_fpu && (cpu_regs[pos].regsel >= 0x20)) { + continue; + } + uint32_t core_data = 0; + succ = + swd_write_memory( + ctx->app, ctx->selected_ap, SCS_DCRSR, SCS_DCRSR_RD | cpu_regs[pos].regsel) == + 1; + succ &= swd_read_memory(ctx->app, ctx->selected_ap, SCS_DCRDR, &core_data) == 1; + + if(!succ) { + swd_script_log(ctx, FuriLogLevelDefault, "%08s ----------", cpu_regs[pos].desc); + } else { + swd_script_log( + ctx, FuriLogLevelDefault, "%06s 0x%08X", cpu_regs[pos].desc, core_data); + } + } + } + furi_mutex_release(ctx->app->swd_mutex); + + swd_script_seek_newline(ctx); + + return true; +} + +static bool swd_scriptfunc_core_reg_get(ScriptContext* ctx) { + uint32_t core_reg = 0; + uint32_t core_data = 0; + bool succ = false; + + if(!swd_script_skip_whitespace(ctx)) { + swd_script_log(ctx, FuriLogLevelError, "missing whitespace"); + return false; + } + + if(!swd_script_get_number(ctx, &core_reg)) { + swd_script_log(ctx, FuriLogLevelError, "failed to parse register"); + return false; + } + + furi_mutex_acquire(ctx->app->swd_mutex, FuriWaitForever); + uint32_t reg_dhcsr = 0; + uint32_t reg_cpacr = 0; + swd_read_memory(ctx->app, ctx->selected_ap, SCS_DHCSR, ®_dhcsr); + swd_read_memory(ctx->app, ctx->selected_ap, SCS_CPACR, ®_cpacr); + + /* when FPU is enabled/available, CP10 and CP11 are implemented */ + bool has_fpu = ((reg_cpacr >> 20) & 0x0F) != 0; + + if(!(reg_dhcsr & SCS_DHCSR_S_HALT)) { + swd_script_log(ctx, FuriLogLevelError, "Core is not in debug state"); + succ = false; + } else { + if(!has_fpu && (core_reg >= 0x20)) { + swd_script_log(ctx, FuriLogLevelError, "Core has no FP extensions"); + succ = false; + } else { + succ = swd_write_memory( + ctx->app, ctx->selected_ap, SCS_DCRSR, SCS_DCRSR_RD | core_reg) == 1; + succ &= swd_read_memory(ctx->app, ctx->selected_ap, SCS_DCRDR, &core_data) == 1; + if(!succ) { + swd_script_log(ctx, FuriLogLevelError, "swd_write_memory failed"); + } + } + } + furi_mutex_release(ctx->app->swd_mutex); + + if(succ) { + swd_script_log(ctx, FuriLogLevelDefault, "0x%08X", core_data); + } + + swd_script_seek_newline(ctx); + + return succ; +} + +static bool swd_scriptfunc_core_reg_set(ScriptContext* ctx) { + uint32_t core_reg = 0; + uint32_t core_data = 0; + bool succ = false; + + if(!swd_script_skip_whitespace(ctx)) { + swd_script_log(ctx, FuriLogLevelError, "missing whitespace"); + return false; + } + + if(!swd_script_get_number(ctx, &core_reg)) { + swd_script_log(ctx, FuriLogLevelError, "failed to parse register"); + return false; + } + if(!swd_script_skip_whitespace(ctx)) { + swd_script_log(ctx, FuriLogLevelError, "missing whitespace"); + return false; + } + if(!swd_script_get_number(ctx, &core_data)) { + swd_script_log(ctx, FuriLogLevelError, "failed to parse data"); + return false; + } + + furi_mutex_acquire(ctx->app->swd_mutex, FuriWaitForever); + uint32_t reg_dhcsr = 0; + uint32_t reg_cpacr = 0; + + swd_read_memory(ctx->app, ctx->selected_ap, SCS_DHCSR, ®_dhcsr); + swd_read_memory(ctx->app, ctx->selected_ap, SCS_CPACR, ®_cpacr); + + /* when FPU is enabled/available, CP10 and CP11 are implemented */ + bool has_fpu = ((reg_cpacr >> 20) & 0x0F) != 0; + + if(!(reg_dhcsr & SCS_DHCSR_S_HALT)) { + swd_script_log(ctx, FuriLogLevelError, "Core is not in debug state"); + succ = false; + } else { + if(!has_fpu && (core_reg >= 0x20)) { + swd_script_log(ctx, FuriLogLevelError, "Core has no FP extensions"); + succ = false; + } else { + succ = swd_write_memory(ctx->app, ctx->selected_ap, SCS_DCRDR, core_data) == 1; + succ &= swd_write_memory( + ctx->app, ctx->selected_ap, SCS_DCRSR, SCS_DCRSR_WR | core_reg) == 1; + if(!succ) { + swd_script_log(ctx, FuriLogLevelError, "swd_write_memory failed"); + } + } + } + furi_mutex_release(ctx->app->swd_mutex); + + swd_script_seek_newline(ctx); + + return succ; +} + +static bool swd_scriptfunc_core_cpuid(ScriptContext* ctx) { + bool succ = false; + uint32_t reg_cpuid = 0; + + furi_mutex_acquire(ctx->app->swd_mutex, FuriWaitForever); + succ = swd_read_memory(ctx->app, ctx->selected_ap, SCS_CPUID, ®_cpuid) == 1; + furi_mutex_release(ctx->app->swd_mutex); + + if(!succ) { + swd_script_log(ctx, FuriLogLevelError, "swd_read_memory failed"); + } else { + swd_script_log(ctx, FuriLogLevelDefault, "0x%08X", reg_cpuid); + } + + swd_script_seek_newline(ctx); + + return succ; +} + +static const ScriptFunctionInfo script_funcs[] = { + {"#", &swd_scriptfunc_comment}, + {".label", &swd_scriptfunc_label}, + {"goto", &swd_scriptfunc_goto}, + {"call", &swd_scriptfunc_call}, + {"status", &swd_scriptfunc_status}, + {"errors", &swd_scriptfunc_errors}, + {"message", &swd_scriptfunc_message}, + {"beep", &swd_scriptfunc_beep}, + {"max_tries", &swd_scriptfunc_maxtries}, + {"swd_clock_delay", &swd_scriptfunc_swd_clock_delay}, + {"swd_idle_bits", &swd_scriptfunc_swd_idle_bits}, + {"block_size", &swd_scriptfunc_blocksize}, + {"abort", &swd_scriptfunc_abort}, + {"mem_dump", &swd_scriptfunc_mem_dump}, + {"mem_ldmst", &swd_scriptfunc_mem_ldmst}, + {"mem_write", &swd_scriptfunc_mem_write}, + {"mem_read", &swd_scriptfunc_mem_read}, + {"dp_write", &swd_scriptfunc_dp_write}, + {"dp_read", &swd_scriptfunc_dp_read}, + {"ap_scan", &swd_scriptfunc_apscan}, + {"ap_select", &swd_scriptfunc_apselect}, + {"ap_read", &swd_scriptfunc_ap_read}, + {"ap_write", &swd_scriptfunc_ap_write}, + {"core_halt", &swd_scriptfunc_core_halt}, + {"core_step", &swd_scriptfunc_core_step}, + {"core_continue", &swd_scriptfunc_core_continue}, + {"core_regs", &swd_scriptfunc_core_regs}, + {"core_reg_get", &swd_scriptfunc_core_reg_get}, + {"core_reg_set", &swd_scriptfunc_core_reg_set}, + {"core_cpuid", &swd_scriptfunc_core_cpuid}}; + +/************************** script main code **************************/ + +static bool swd_execute_script_line(ScriptContext* const ctx) { + char buffer[64]; + uint64_t start_pos = 0; + + if(ctx->script_file) { + start_pos = storage_file_tell(ctx->script_file); + uint16_t ret = storage_file_read(ctx->script_file, buffer, 2); + storage_file_seek(ctx->script_file, start_pos, true); + + if(ret < 2) { + return true; + } + } else { + start_pos = ctx->line_pos; + strncpy(buffer, ctx->line_data, 2); + + if(buffer[0] == 0 || buffer[1] == 0) { + return true; + } + } + + if(buffer[0] == '\n' || (buffer[0] == '\r' && buffer[1] == '\n')) { + swd_script_seek_newline(ctx); + return true; + } + + for(size_t entry = 0; entry < COUNT(script_funcs); entry++) { + if(ctx->abort) { + DBGS("aborting"); + break; + } + size_t expected = strlen(script_funcs[entry].prefix); + + if(ctx->script_file) { + storage_file_seek(ctx->script_file, start_pos, true); + + if(storage_file_read(ctx->script_file, buffer, expected) != expected) { + continue; + } + } else { + ctx->line_pos = start_pos; + + if(strlen(ctx->line_data) < expected) { + continue; + } + strncpy(buffer, ctx->line_data, expected); + ctx->line_pos += expected; + } + + buffer[expected] = '\000'; + if(strncmp(buffer, script_funcs[entry].prefix, expected)) { + continue; + } + bool success = true; + + if(ctx->goto_active) { + DBG("ignore: '%s'", script_funcs[entry].prefix); + + /* only execute label handlers */ + if(buffer[0] == '.') { + success = script_funcs[entry].func(ctx); + } else { + swd_script_seek_newline(ctx); + } + } else { + DBG("command: '%s'", script_funcs[entry].prefix); + + if(!ctx->status_ignore) { + snprintf( + ctx->app->state_string, + sizeof(ctx->app->state_string), + "CMD: %s", + script_funcs[entry].prefix); + } + swd_script_gui_refresh(ctx); + + /* function, execute */ + success = script_funcs[entry].func(ctx); + + if(!success && !ctx->errors_ignore) { + swd_script_log( + ctx, FuriLogLevelError, "Command failed: %s", script_funcs[entry].prefix); + snprintf( + ctx->app->state_string, + sizeof(ctx->app->state_string), + "Command failed: %s", + script_funcs[entry].prefix); + return false; + } + } + + return true; + } + swd_script_log(ctx, FuriLogLevelError, "unknown command '%s'", buffer); + + return false; +} + +static bool swd_execute_script(AppFSM* const ctx, const char* filename) { + bool success = true; + + /* fetch current script and set as parent */ + ScriptContext* parent = ctx->script; + + ctx->script = malloc(sizeof(ScriptContext)); + ctx->script->app = ctx; + ctx->script->max_tries = 1; + ctx->script->parent = parent; + strcpy(ctx->script->filename, filename); + + if(!storage_file_exists(ctx->storage, filename)) { + DBG("Does not exist '%s'", filename); + parent = ctx->script->parent; + free(ctx->script); + ctx->script = parent; + return false; + } + + /* first allocate a file object */ + ctx->script->script_file = storage_file_alloc(ctx->storage); + + /* then get our script opened */ + if(!storage_file_open(ctx->script->script_file, filename, FSAM_READ, FSOM_OPEN_EXISTING)) { + FURI_LOG_E(TAG, "open, %s", storage_file_get_error_desc(ctx->script->script_file)); + DBG("Failed to open '%s'", filename); + storage_file_free(ctx->script->script_file); + parent = ctx->script->parent; + free(ctx->script); + ctx->script = parent; + return false; + } + + do { + success = true; + ctx->script->restart = false; + + storage_file_seek(ctx->script->script_file, 0, true); + + uint32_t line = 1; + while(line < SCRIPT_MAX_LINES) { + if(ctx->script->abort) { + DBGS("Abort requested"); + break; + } + if(storage_file_eof(ctx->script->script_file)) { + break; + } + DBG("line %lu", line); + if(!swd_execute_script_line(ctx->script)) { + success = false; + break; + } + if(ctx->script->restart) { + break; + } + line++; + } + + if(ctx->script->restart) { + DBGS("Restarting"); + } else { + DBGS("Finished"); + } + + if(line >= SCRIPT_MAX_LINES) { + success = true; + char text_buf[128]; + + snprintf(text_buf, sizeof(text_buf), "aborting after %d lines", SCRIPT_MAX_LINES); + DialogMessage* message = dialog_message_alloc(); + dialog_message_set_header(message, "SWD Probe", 16, 2, AlignLeft, AlignTop); + dialog_message_set_icon(message, &I_app, 3, 2); + dialog_message_set_text(message, text_buf, 3, 16, AlignLeft, AlignTop); + dialog_message_set_buttons(message, "Back", NULL, NULL); + dialog_message_free(message); + + ctx->script->restart = false; + } + + if(!success) { + char text_buf[128]; + + snprintf(text_buf, sizeof(text_buf), "Line %lu failed:\n%s", line, ctx->state_string); + DialogMessage* message = dialog_message_alloc(); + dialog_message_set_header(message, "SWD Probe", 16, 2, AlignLeft, AlignTop); + dialog_message_set_icon(message, &I_app, 3, 2); + dialog_message_set_text(message, text_buf, 3, 16, AlignLeft, AlignTop); + dialog_message_set_buttons(message, "Back", "Retry", NULL); + if(dialog_message_show(ctx->dialogs, message) == DialogMessageButtonCenter) { + ctx->script->restart = true; + } + dialog_message_free(message); + } + } while(ctx->script->restart); + + storage_file_close(ctx->script->script_file); + storage_file_free(ctx->script->script_file); + + parent = ctx->script->parent; + free(ctx->script); + ctx->script = parent; + + return success; +} + +/************************** UI functions **************************/ + +#define CANVAS_WIDTH 128 +#define CANVAS_HEIGHT 64 + +#define COERCE(d, min, max) \ + do { \ + if(d < (min)) { \ + d = (min); \ + } \ + if(d > (max)) { \ + d = (max); \ + } \ + } while(0) + +#define COERCE_COORDS(x1, y1, x2, y2) \ + do { \ + COERCE(x1, 0, CANVAS_WIDTH); \ + COERCE(x2, 0, CANVAS_WIDTH); \ + COERCE(y1, 0, CANVAS_HEIGHT); \ + COERCE(y1, 0, CANVAS_HEIGHT); \ + } while(0) + +#include "model/model_chip.h" + +static int rotatedVertexCoords[NUM_VERTICES][3]; + +static void draw_model(Canvas* const canvas) { + static float xAngle = 0; + static float yAngle = 0; + static float zAngle = 0; + static float zoom = 0; + static float speed = 0.6f; + + float cosXAngle = cosf(xAngle); + float sinXAngle = sinf(xAngle); + float cosYAngle = cosf(yAngle); + float sinYAngle = sinf(yAngle); + float cosZAngle = cosf(zAngle); + float sinZAngle = sinf(zAngle); + float sinZoom = 1.2f + sinf(zoom) * 0.25f; + + int centerX = CANVAS_WIDTH / 2; + int centerY = CANVAS_HEIGHT / 2 + 5; + + for(int i = 0; i < NUM_VERTICES; i++) { + int x = vertexCoords[i][0] * sinZoom * 16; + int y = vertexCoords[i][1] * sinZoom * 16; + int z = vertexCoords[i][2] * sinZoom * 16; + + int y1 = y * cosXAngle - z * sinXAngle; + int z1 = y * sinXAngle + z * cosXAngle; + + int x2 = x * cosYAngle + z1 * sinYAngle; + int z2 = -x * sinYAngle + z1 * cosYAngle; + + int x3 = x2 * cosZAngle - y1 * sinZAngle; + int y3 = x2 * sinZAngle + y1 * cosZAngle; + + rotatedVertexCoords[i][0] = x3 + centerX; + rotatedVertexCoords[i][1] = y3 + centerY; + rotatedVertexCoords[i][2] = z2; + } + + for(size_t i = 0; i < COUNT(edgeIndices); i++) { + int v1Index = edgeIndices[i][0]; + int v2Index = edgeIndices[i][1]; + int x1 = rotatedVertexCoords[v1Index][0]; + int y1 = rotatedVertexCoords[v1Index][1]; + int x2 = rotatedVertexCoords[v2Index][0]; + int y2 = rotatedVertexCoords[v2Index][1]; + + COERCE_COORDS(x1, y1, x2, y2); + canvas_draw_line(canvas, x1, y1, x2, y2); + } + + xAngle += speed * 0.02 / sinZoom; + yAngle += speed * 0.023 / sinZoom; + zAngle += speed * 0.029 * sinZoom; + zoom += speed * 0.005; +} + +static void render_callback(Canvas* const canvas, void* ctx_in) { + furi_assert(canvas); + furi_assert(ctx_in); + + AppFSM* ctx = ctx_in; + furi_mutex_acquire(ctx->gui_mutex, FuriWaitForever); + + char buffer[64]; + int y = 10; + + canvas_draw_frame(canvas, 0, 0, 128, 64); + canvas_set_font(canvas, FontPrimary); + + if(!ctx->detected_device) { + ctx->mode_page = ModePageScan; + } else if(ctx->mode_page == ModePageScan) { + ctx->mode_page = ModePageFound; + } + + /* if seen less than a quarter second ago */ + switch(ctx->mode_page) { + case ModePageScan: { + draw_model(canvas); + + canvas_draw_str_aligned(canvas, 64, y, AlignCenter, AlignBottom, "Searching"); + y += 14; + + canvas_set_font(canvas, FontSecondary); + + bool info_page = (ctx->loop_count % 500) >= 250; + if(info_page) { + canvas_draw_str(canvas, 2, y, "Connect GND with target GND"); + y += 10; + canvas_draw_str(canvas, 2, y, "and any two GPIOs with pads"); + y += 10; + canvas_draw_str(canvas, 2, y, "you want to check for SWD"); + + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 111, 62, "2/2"); + } else { + const char* filename = ""; + if(strlen(ctx->script_detected) > 0) { + const char* slash = strrchr(ctx->script_detected, '/'); + if(slash) { + filename = &slash[1]; + } else { + filename = ctx->script_detected; + } + } + + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, 64, y, AlignCenter, AlignBottom, "Autoexec Script"); + y += 10; + canvas_set_font(canvas, FontKeyboard); + canvas_draw_str_aligned(canvas, 64, y, AlignCenter, AlignBottom, filename); + y += 16; + + canvas_set_font(canvas, FontSecondary); + canvas_draw_icon(canvas, 14, y - 5, &I_ButtonUp_7x4); + canvas_draw_icon(canvas, 78, y - 5, &I_ButtonDown_7x4); + canvas_draw_str(canvas, 23, y, "Clear"); + canvas_draw_str(canvas, 87, y, "Choose"); + + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 111, 62, "1/2"); + } + canvas_set_font(canvas, FontSecondary); + elements_button_left(canvas, "Script"); + break; + } + case ModePageFound: { + if((ctx->detected_timeout + TIMER_HZ / 4) >= TIMER_HZ * TIMEOUT) { + snprintf(buffer, sizeof(buffer), "FOUND!"); + } else { + /* if it was seen more than a quarter second ago, show countdown */ + snprintf( + buffer, sizeof(buffer), "FOUND! (%lus)", (ctx->detected_timeout / TIMER_HZ) + 1); + } + canvas_draw_str_aligned(canvas, 64, y, AlignCenter, AlignBottom, buffer); + y += 10; + canvas_set_font(canvas, FontKeyboard); + + snprintf( + buffer, + sizeof(buffer), + "SWC/SWD: %s/%s", + gpio_name(ctx->io_swc), + gpio_name(ctx->io_swd)); + canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer); + y += 10; + snprintf(buffer, sizeof(buffer), "DPIDR 0x%08lX", ctx->dp_regs.dpidr); + canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer); + y += 10; + + snprintf( + buffer, + sizeof(buffer), + "Part %02X Rev %X DAPv%d", + ctx->dpidr_info.partno, + ctx->dpidr_info.revision, + ctx->dpidr_info.version); + canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer); + y += 10; + + canvas_set_font(canvas, FontSecondary); + snprintf(buffer, sizeof(buffer), "%s", jep106_manufacturer(ctx->dpidr_info.designer)); + canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer); + y += 10; + + canvas_set_font(canvas, FontSecondary); + elements_button_left(canvas, "Script"); + elements_button_right(canvas, "DP Regs"); + + break; + } + case ModePageDPRegs: { + canvas_draw_str_aligned(canvas, 64, y, AlignCenter, AlignBottom, "DP Registers"); + y += 10; + canvas_set_font(canvas, FontKeyboard); + if(ctx->dp_regs.dpidr_ok) { + snprintf(buffer, sizeof(buffer), "DPIDR %08lX", ctx->dp_regs.dpidr); + canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer); + } + y += 10; + + if(ctx->dp_regs.ctrlstat_ok) { + snprintf(buffer, sizeof(buffer), "CTRL %08lX", ctx->dp_regs.ctrlstat); + canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer); + } + y += 10; + + if(ctx->dp_regs.targetid_ok) { + snprintf(buffer, sizeof(buffer), "TGTID %08lX", ctx->dp_regs.targetid); + canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer); + } + y += 10; + + if(ctx->dp_regs.eventstat_ok) { + snprintf(buffer, sizeof(buffer), "EVTST %08lX", ctx->dp_regs.eventstat); + canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer); + } + y += 10; + canvas_set_font(canvas, FontSecondary); + elements_button_left(canvas, "Scan"); + elements_button_right(canvas, "DPID"); + + break; + } + + case ModePageDPID: { + canvas_draw_str_aligned(canvas, 64, y, AlignCenter, AlignBottom, "DP ID Register"); + y += 10; + canvas_set_font(canvas, FontKeyboard); + if(ctx->dpidr_info.version != 2) { + snprintf(buffer, sizeof(buffer), "TARGETID not supported"); + canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer); + y += 10; + } else { + if(ctx->dp_regs.targetid_ok) { + snprintf(buffer, sizeof(buffer), "TGTID %08lX", ctx->dp_regs.targetid); + canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer); + y += 10; + + snprintf(buffer, sizeof(buffer), "Part No. %04X", ctx->targetid_info.partno); + canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer); + y += 10; + snprintf( + buffer, sizeof(buffer), "%s", jep106_manufacturer(ctx->targetid_info.designer)); + canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer); + y += 10; + } + } + canvas_set_font(canvas, FontSecondary); + elements_button_left(canvas, "DP Regs"); + elements_button_right(canvas, "APs"); + break; + } + + case ModePageAPID: { + canvas_draw_str_aligned(canvas, 64, y, AlignCenter, AlignBottom, "AP Menu"); + y += 10; + canvas_set_font(canvas, FontKeyboard); + + char state = ' '; + if(ctx->ap_pos >= ctx->ap_scanned && ctx->ap_pos <= ctx->ap_scanned + 10) { + state = '*'; + } + + if(!ctx->apidr_info[ctx->ap_pos].ok) { + snprintf(buffer, sizeof(buffer), "[%d]%c", ctx->ap_pos, state); + canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer); + y += 10; + + if(ctx->ap_pos == 0) { + for(size_t pos = 0; pos < COUNT(ctx->apidr_info); pos++) { + if(ctx->apidr_info[pos].ok) { + ctx->ap_pos = pos; + } + } + } + } else { + const char* class = ""; + + switch(ctx->apidr_info[ctx->ap_pos].class) { + case 0: + class = "und"; + break; + case 1: + class = "COM"; + break; + case 8: + class = "MEM"; + break; + default: + class = "unk"; + break; + } + + const char* types[] = { + "COM-AP", + "AHB3", + "APB2 or APB3", + "Type unknown", + "AXI3 or AXI4", + "AHB5", + "APB4 and APB5", + "AXI5", + "AHB5 enh.", + }; + const char* type = "Type unk"; + + if(ctx->apidr_info[ctx->ap_pos].type < COUNT(types)) { + type = types[ctx->apidr_info[ctx->ap_pos].type]; + } + + snprintf(buffer, sizeof(buffer), "[%d]%c%s, %s", ctx->ap_pos, state, class, type); + canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer); + y += 10; + + snprintf(buffer, sizeof(buffer), "Base 0x%08lX", ctx->apidr_info[ctx->ap_pos].base); + canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer); + y += 10; + + snprintf( + buffer, + sizeof(buffer), + "Rev %d Var %d", + ctx->apidr_info[ctx->ap_pos].revision, + ctx->apidr_info[ctx->ap_pos].variant); + canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer); + y += 10; + + snprintf( + buffer, + sizeof(buffer), + "%s", + jep106_manufacturer(ctx->apidr_info[ctx->ap_pos].designer)); + canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer); + y += 10; + + elements_button_center(canvas, "Show"); + } + canvas_set_font(canvas, FontSecondary); + elements_button_left(canvas, "DPID"); + elements_button_right(canvas, "CoreS."); + elements_scrollbar_pos(canvas, 4, 10, 40, ctx->ap_pos / 32, COUNT(ctx->apidr_info) / 32); + break; + } + + /* hex dump view */ + case ModePageHexDump: { + canvas_draw_str_aligned(canvas, 64, y, AlignCenter, AlignBottom, "Hex dump"); + y += 10; + canvas_set_font(canvas, FontKeyboard); + + canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, "Addr:"); + + snprintf(buffer, sizeof(buffer), "%08lX", ctx->hex_addr); + canvas_draw_str_aligned(canvas, 38, y, AlignLeft, AlignBottom, buffer); + uint32_t font_width = canvas_glyph_width(canvas, '0'); + uint32_t x = 37 + (7 - ctx->hex_select) * font_width; + + /* draw selection */ + canvas_draw_line(canvas, x, y + 1, x + font_width, y + 1); + y += 10; + + uint32_t byte_num = 0; + for(int line = 0; line < 4; line++) { + uint32_t x_pos = 5; + + for(int byte_pos = 0; byte_pos < 8; byte_pos++) { + if(ctx->hex_buffer_valid[byte_num / 4]) { + snprintf(buffer, sizeof(buffer), "%02X", ctx->hex_buffer[byte_num]); + } else { + snprintf(buffer, sizeof(buffer), "--"); + } + byte_num++; + canvas_draw_str_aligned(canvas, x_pos, y, AlignLeft, AlignBottom, buffer); + x_pos += font_width * 2 + font_width / 2; + } + y += 10; + } + break; + } + + case ModePageCoresight: { + canvas_draw_str_aligned(canvas, 64, y, AlignCenter, AlignBottom, "Coresight"); + y += 10; + canvas_set_font(canvas, FontSecondary); + + uint32_t base = ctx->coresight_bases[ctx->coresight_level]; + uint32_t base_next = adi_romtable_get(ctx, base, ctx->coresight_pos[ctx->coresight_level]); + + snprintf(buffer, sizeof(buffer), "Base: %08lX", base); + canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer); + y += 10; + snprintf(buffer, sizeof(buffer), "Type: %s", adi_romtable_type(ctx, base)); + canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer); + y += 10; + snprintf(buffer, sizeof(buffer), "Full: %s", adi_romtable_full(ctx, base)); + canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer); + y += 10; + + if(adi_is_romtable(ctx, base)) { + snprintf( + buffer, + sizeof(buffer), + "[%lu/%lu] -> %08lX", + ctx->coresight_pos[ctx->coresight_level] + 1, + ctx->coresight_count[ctx->coresight_level], + base_next); + canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer); + canvas_set_font(canvas, FontSecondary); + elements_button_center(canvas, "Enter"); + } + y += 10; + + canvas_set_font(canvas, FontSecondary); + + if(ctx->coresight_level) { + elements_button_left(canvas, "Prev"); + } else { + elements_button_left(canvas, "APs"); + } + elements_scrollbar_pos( + canvas, + 4, + 10, + 40, + ctx->coresight_pos[ctx->coresight_level], + ctx->coresight_count[ctx->coresight_level]); + + break; + } + + /* hex dump view */ + case ModePageScript: { + canvas_draw_str_aligned(canvas, 64, y, AlignCenter, AlignBottom, "Script"); + y += 10; + y += 10; + canvas_draw_str_aligned(canvas, 10, y, AlignLeft, AlignBottom, "Status:"); + y += 10; + canvas_set_font(canvas, FontKeyboard); + canvas_draw_str_aligned(canvas, 64, y, AlignCenter, AlignBottom, ctx->state_string); + y += 10; + break; + } + } + + furi_mutex_release(ctx->gui_mutex); +} + +static void input_callback(InputEvent* input_event, void* ctx_in) { + furi_assert(input_event); + furi_assert(ctx_in); + AppFSM* ctx = ctx_in; + + int entries = furi_message_queue_get_count(ctx->event_queue); + + /* better skip than sorry */ + if(entries < QUEUE_SIZE) { + AppEvent event = {.type = EventKeyPress, .input = *input_event}; + furi_message_queue_put(ctx->event_queue, &event, 0); + } +} + +static void app_init(AppFSM* const app) { + furi_assert(app); + + app->loop_count = 0; + app->current_mask_id = 0; + app->current_mask = gpio_direction_mask[app->current_mask_id]; + app->io_swd = 0xFF; + app->io_swc = 0xFF; + app->hex_addr = 0x40002800; + app->hex_addr = 0xE000EDF0; + app->swd_clock_delay = CLOCK_DELAY; + app->swd_idle_bits = IDLE_BITS; + + strcpy(app->state_string, "none"); + strcpy(app->script_detected, ""); +} + +static void app_deinit(AppFSM* const app) { + furi_assert(app); + + strcpy(app->state_string, "exiting"); +} + +static void swd_main_loop(AppFSM* ctx) { + furi_assert(ctx); + + ctx->loop_count++; + + switch(ctx->mode_page) { + case ModePageScan: + case ModePageFound: { + /* reset after timeout */ + if(ctx->detected_timeout > 0) { + ctx->detected_timeout--; + } else { + DBGS("Reset detected flag"); + ctx->detected_device = false; + ctx->io_swd = 0xFF; + ctx->io_swc = 0xFF; + ctx->io_num_swd = 0xFF; + ctx->io_num_swc = 0xFF; + ctx->ap_scanned = 0; + memset(&ctx->dp_regs, 0x00, sizeof(ctx->dp_regs)); + memset(&ctx->targetid_info, 0x00, sizeof(ctx->targetid_info)); + memset(&ctx->apidr_info, 0x00, sizeof(ctx->apidr_info)); + ctx->script_detected_executed = false; + } + + ctx->detected = false; + ctx->current_mask = gpio_direction_mask[ctx->current_mask_id]; + + /* when SWD was already detected, set it to data pin regardless of the mask */ + if(ctx->detected_device) { + ctx->current_mask &= ~ctx->io_swd; + } + + /* do the scan */ + furi_mutex_acquire(ctx->swd_mutex, FuriWaitForever); + swd_scan(ctx); + furi_mutex_release(ctx->swd_mutex); + + /* now when detected a device, set the timeout */ + if(ctx->detected) { + DBGS("Set detected flag"); + ctx->detected_device = true; + ctx->detected_timeout = TIMER_HZ * TIMEOUT; + + /* update DPIDR fields */ + ctx->dpidr_info.revision = (ctx->dp_regs.dpidr >> 28) & 0x0F; + ctx->dpidr_info.partno = (ctx->dp_regs.dpidr >> 20) & 0xFF; + ctx->dpidr_info.version = (ctx->dp_regs.dpidr >> 12) & 0x0F; + ctx->dpidr_info.designer = (ctx->dp_regs.dpidr >> 1) & 0x3FF; + + if(!has_multiple_bits(ctx->io_swc)) { + DBGS(" - Detected pins"); + DBGS(" - Resetting error"); + + furi_mutex_acquire(ctx->swd_mutex, FuriWaitForever); + /* reset error */ + /* first make sure we have the correct bank by invalidating the current select cache */ + ctx->dp_regs.select_ok = false; + uint8_t ack = + swd_read_dpbank(ctx, REG_CTRLSTAT, REG_CTRLSTAT_BANK, &ctx->dp_regs.ctrlstat); + + if(ack != 1 || (ctx->dp_regs.ctrlstat & STAT_ERROR_FLAGS)) { + DBGS(" - send ABORT"); + swd_abort(ctx); + } + DBGS(" - Fetch CTRL/STAT"); + ctx->dp_regs.ctrlstat_ok = + swd_read_dpbank( + ctx, REG_CTRLSTAT, REG_CTRLSTAT_BANK, &ctx->dp_regs.ctrlstat) == 1; + DBG(" %08lX %s", + ctx->dp_regs.ctrlstat, + ctx->dp_regs.ctrlstat_ok ? "OK" : "FAIL"); + + if(ctx->dpidr_info.version >= 1) { + DBGS(" - DAPv1, read DLCR"); + ctx->dp_regs.dlcr_ok = + swd_read_dpbank(ctx, REG_DLCR, REG_DLCR_BANK, &ctx->dp_regs.dlcr) == 1; + DBG(" %08lX %s", ctx->dp_regs.dlcr, ctx->dp_regs.dlcr_ok ? "OK" : "FAIL"); + } + + if(ctx->dpidr_info.version >= 2) { + DBGS(" - DAPv2, read TARGETID"); + ctx->dp_regs.targetid_ok = + swd_read_dpbank( + ctx, REG_TARGETID, REG_TARGETID_BANK, &ctx->dp_regs.targetid) == 1; + DBG(" %08lX %s", + ctx->dp_regs.targetid, + ctx->dp_regs.targetid_ok ? "OK" : "FAIL"); + DBGS(" - DAPv2, read EVENTSTAT"); + ctx->dp_regs.eventstat_ok = + swd_read_dpbank( + ctx, REG_EVENTSTAT, REG_EVENTSTAT_BANK, &ctx->dp_regs.eventstat) == 1; + DBG(" %08lX %s", + ctx->dp_regs.eventstat, + ctx->dp_regs.eventstat_ok ? "OK" : "FAIL"); + DBGS(" - DAPv2, read DLPIDR"); + ctx->dp_regs.dlpidr_ok = + swd_read_dpbank(ctx, REG_DLPIDR, REG_DLPIDR_BANK, &ctx->dp_regs.dlpidr) == + 1; + DBG(" %08lX %s", + ctx->dp_regs.dlpidr, + ctx->dp_regs.dlpidr_ok ? "OK" : "FAIL"); + } + + if(ctx->dp_regs.targetid_ok) { + ctx->targetid_info.revision = (ctx->dp_regs.targetid >> 28) & 0x0F; + ctx->targetid_info.partno = (ctx->dp_regs.targetid >> 12) & 0xFFFF; + ctx->targetid_info.designer = (ctx->dp_regs.targetid >> 1) & 0x3FF; + } + + if(!ctx->script_detected_executed && strlen(ctx->script_detected) > 0) { + DBG(" - Run script '%s'", ctx->script_detected); + + ctx->script_detected_executed = true; + + ctx->mode_page = ModePageScript; + swd_execute_script(ctx, ctx->script_detected); + ctx->mode_page = ModePageFound; + } + furi_mutex_release(ctx->swd_mutex); + } + } else { + if(!has_multiple_bits(ctx->io_swc)) { + DBGS(" - Lost device"); + } + } + + ctx->current_mask_id = (ctx->current_mask_id + 1) % COUNT(gpio_direction_mask); + break; + } + + case ModePageDPRegs: + case ModePageAPID: { + furi_mutex_acquire(ctx->swd_mutex, FuriWaitForever); + /* set debug enable request */ + if(!swd_ensure_powerup(ctx)) { + furi_mutex_release(ctx->swd_mutex); + break; + } + + /* only scan a few APs at once to stay responsive */ + for(int pos = 0; pos < 8; pos++) { + if(ctx->ap_scanned == 0) { + swd_apscan_reset(ctx); + } + + uint8_t ap = ctx->ap_scanned++; + + if(ctx->apidr_info[ap].tested) { + continue; + } + if(swd_apscan_test(ctx, ap)) { + break; + } + } + furi_mutex_release(ctx->swd_mutex); + break; + } + + case ModePageHexDump: { + furi_mutex_acquire(ctx->swd_mutex, FuriWaitForever); + + for(size_t byte_pos = 0; byte_pos < sizeof(ctx->hex_buffer); byte_pos += 4) { + uint32_t* data = (uint32_t*)&ctx->hex_buffer[byte_pos]; + bool ret = swd_read_memory(ctx, ctx->ap_pos, ctx->hex_addr + byte_pos, data) == 1; + + ctx->hex_buffer_valid[byte_pos / 4] = ret; + + if(!ret) { + swd_abort_simple(ctx); + } + } + furi_mutex_release(ctx->swd_mutex); + break; + } + + case ModePageDPID: + case ModePageCoresight: + furi_delay_ms(50); + break; + } +} + +static bool swd_message_process(AppFSM* ctx) { + bool processing = true; + AppEvent event; + + /* wait to make sure the OS can do its stuff */ + FuriStatus event_status = furi_message_queue_get(ctx->event_queue, &event, 1000 / TIMER_HZ); + + if(event_status != FuriStatusOk) { + return processing; + } + + if(event.type == EventKeyPress) { + if(event.input.type == InputTypePress) { + switch(event.input.key) { + case InputKeyUp: + switch(ctx->mode_page) { + default: + break; + + case ModePageScan: + case ModePageFound: { + strcpy(ctx->script_detected, ""); + break; + } + + case ModePageAPID: + if(ctx->ap_pos > 0) { + ctx->ap_pos--; + } + break; + + case ModePageHexDump: { + ctx->hex_addr += ((ctx->hex_select) ? 1 : 8) * (1 << (4 * ctx->hex_select)); + break; + } + + case ModePageCoresight: { + if(ctx->coresight_pos[ctx->coresight_level] > 0) { + ctx->coresight_pos[ctx->coresight_level]--; + } + break; + } + } + break; + + case InputKeyDown: { + switch(ctx->mode_page) { + default: + break; + + case ModePageScan: { + FuriString* result_path = furi_string_alloc_printf(ANY_PATH("swd_scripts")); + FuriString* preselected = furi_string_alloc_printf( + (strlen(ctx->script_detected) > 0) ? ctx->script_detected : + ANY_PATH("swd_scripts")); + DialogsFileBrowserOptions options; + + dialog_file_browser_set_basic_options(&options, "swd", &I_swd); + + if(dialog_file_browser_show(ctx->dialogs, result_path, preselected, &options)) { + const char* path = furi_string_get_cstr(result_path); + strcpy(ctx->script_detected, path); + } + + furi_string_free(result_path); + furi_string_free(preselected); + break; + } + + case ModePageAPID: + if(ctx->ap_pos + 1U < COUNT(ctx->apidr_info)) { + ctx->ap_pos++; + } + break; + + case ModePageHexDump: { + ctx->hex_addr -= ((ctx->hex_select) ? 1 : 8) * (1 << (4 * ctx->hex_select)); + break; + } + + case ModePageCoresight: { + if(ctx->coresight_pos[ctx->coresight_level] + 1 < + ctx->coresight_count[ctx->coresight_level]) { + ctx->coresight_pos[ctx->coresight_level]++; + } + break; + } + } + break; + } + + case InputKeyRight: + if(ctx->mode_page == ModePageHexDump) { + if(ctx->hex_select > 0) { + ctx->hex_select--; + } + } else if(ctx->mode_page == ModePageAPID && ctx->apidr_info[ctx->ap_pos].ok) { + ctx->mode_page = ModePageCoresight; + uint32_t base = ctx->apidr_info[ctx->ap_pos].base & 0xFFFFF000; + ctx->coresight_level = 0; + ctx->coresight_bases[ctx->coresight_level] = base; + ctx->coresight_pos[ctx->coresight_level] = 0; + ctx->coresight_count[ctx->coresight_level] = + adi_romtable_entry_count(ctx, base); + } else if(ctx->detected) { + if(ctx->mode_page + 1 < ModePageCount) { + ctx->mode_page++; + } + } + break; + + case InputKeyLeft: + if(ctx->mode_page == ModePageHexDump) { + if(ctx->hex_select < 7) { + ctx->hex_select++; + } + } else if(ctx->mode_page == ModePageCoresight) { + if(ctx->coresight_level > 0) { + ctx->coresight_level--; + } else { + ctx->mode_page = ModePageAPID; + } + } else if((ctx->mode_page == ModePageScan) || (ctx->mode_page == ModePageFound)) { + uint32_t mode_page = ctx->mode_page; + FuriString* result_path = furi_string_alloc_printf(ANY_PATH("swd_scripts")); + FuriString* preselected = furi_string_alloc_printf( + (strlen(ctx->script_detected) > 0) ? ctx->script_detected : + ANY_PATH("swd_scripts")); + DialogsFileBrowserOptions options; + + dialog_file_browser_set_basic_options(&options, "swd", &I_swd); + + if(dialog_file_browser_show(ctx->dialogs, result_path, preselected, &options)) { + const char* path = furi_string_get_cstr(result_path); + ctx->mode_page = ModePageScript; + swd_execute_script(ctx, path); + ctx->mode_page = mode_page; + } + + furi_string_free(result_path); + furi_string_free(preselected); + break; + } else { + if(ctx->mode_page > 0) { + ctx->mode_page--; + } + } + break; + + case InputKeyOk: + if(ctx->mode_page == ModePageAPID && ctx->apidr_info[ctx->ap_pos].ok) { + ctx->mode_page = ModePageHexDump; + } else if(ctx->mode_page == ModePageCoresight) { + uint32_t base = ctx->coresight_bases[ctx->coresight_level]; + + if(!adi_is_romtable(ctx, base)) { + break; + } + + uint32_t cur_pos = ctx->coresight_pos[ctx->coresight_level]; + uint32_t base_next = adi_romtable_get(ctx, base, cur_pos); + uint32_t new_count = adi_romtable_entry_count(ctx, base_next); + + ctx->coresight_level++; + ctx->coresight_pos[ctx->coresight_level] = 0; + ctx->coresight_count[ctx->coresight_level] = new_count; + ctx->coresight_bases[ctx->coresight_level] = base_next; + } + break; + + case InputKeyBack: + if(ctx->mode_page == ModePageHexDump) { + ctx->mode_page = ModePageAPID; + } else if(ctx->mode_page == ModePageScript) { + ctx->script->abort = true; + } else if(ctx->mode_page > ModePageFound) { + ctx->mode_page = ModePageScan; + } else if(ctx->mode_page == ModePageScan) { + processing = false; + } else if(ctx->mode_page == ModePageFound) { + processing = false; + } + break; + + default: + break; + } + } + } + return processing; +} + +size_t data_received(void* ctx, uint8_t* data, size_t length) { + AppFSM* app = (AppFSM*)ctx; + + strncpy(app->commandline->line_data, (const char*)data, length); + app->commandline->line_pos = 0; + + for(size_t pos = 0; pos < length; pos++) { + uint8_t ch = app->commandline->line_data[pos]; + + if((ch == '\r') || (ch == '\n')) { + app->commandline->line_data[pos++] = '\n'; + app->commandline->line_data[pos] = 0; + LOG("direct command '%s'", app->commandline->line_data); + swd_execute_script_line(app->commandline); + return pos; + } + } + + return 0; +} + +int32_t swd_probe_app_main(void* p) { + UNUSED(p); + + AppFSM* app = malloc(sizeof(AppFSM)); + + DBGS("App init"); + app_init(app); + + DBGS("furi_record_open"); + app->notification = furi_record_open(RECORD_NOTIFICATION); + app->gui = furi_record_open(RECORD_GUI); + app->dialogs = furi_record_open(RECORD_DIALOGS); + app->storage = furi_record_open(RECORD_STORAGE); + + DBGS("furi_mutex_alloc"); + app->swd_mutex = furi_mutex_alloc(FuriMutexTypeNormal); + app->gui_mutex = furi_mutex_alloc(FuriMutexTypeNormal); + app->event_queue = furi_message_queue_alloc(QUEUE_SIZE, sizeof(AppEvent)); + + DBGS("usb_uart_enable"); + UsbUartConfig uart_config; + uart_config.vcp_ch = 1; + uart_config.rx_data = &data_received; + uart_config.rx_data_ctx = app; + app->uart = usb_uart_enable(&uart_config); + + app->commandline = malloc(sizeof(ScriptContext)); + app->commandline->max_tries = 1; + app->commandline->app = app; + + DBGS("view_port_alloc"); + app->view_port = view_port_alloc(); + view_port_draw_callback_set(app->view_port, render_callback, app); + view_port_input_callback_set(app->view_port, input_callback, app); + gui_add_view_port(app->gui, app->view_port, GuiLayerFullscreen); + + DBGS("notification_message_block"); + notification_message(app->notification, &sequence_display_backlight_enforce_on); + + DBGS("swd_execute_script"); + swd_execute_script(app, ANY_PATH("swd_scripts/startup.swd")); + + DBGS("processing"); + for(bool processing = true; processing;) { + swd_main_loop(app); + view_port_update(app->view_port); + + processing = swd_message_process(app); + + bool beep = false; + + if(app->detected_device && !app->detected_notified) { + app->detected_notified = true; + beep = true; + } + if(!app->detected_device && app->detected_notified) { + app->detected_notified = false; + } + if(beep) { + notification_message_block(app->notification, &seq_c_minor); + } + } + + view_port_enabled_set(app->view_port, false); + gui_remove_view_port(app->gui, app->view_port); + view_port_free(app->view_port); + + app_deinit(app); + + notification_message(app->notification, &sequence_display_backlight_enforce_auto); + + usb_uart_disable(app->uart); + + furi_message_queue_free(app->event_queue); + furi_mutex_free(app->gui_mutex); + furi_mutex_free(app->swd_mutex); + free(app); + + furi_record_close(RECORD_GUI); + furi_record_close(RECORD_NOTIFICATION); + furi_record_close(RECORD_DIALOGS); + furi_record_close(RECORD_STORAGE); + + return 0; +} diff --git a/applications/plugins/swd_probe/swd_probe_app.h b/applications/plugins/swd_probe/swd_probe_app.h new file mode 100644 index 000000000..203da2b1f --- /dev/null +++ b/applications/plugins/swd_probe/swd_probe_app.h @@ -0,0 +1,247 @@ +#ifndef __ARHA_FLIPPERAPP_DEMO +#define __ARHA_FLIPPERAPP_DEMO + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "usb_uart.h" + +#define TAG "SWD" + +/* short debug message */ +#define DBGS(format) furi_log_print_format(FuriLogLevelDebug, TAG, "%s: " format, __FUNCTION__) +/* formatted debug message */ +#define DBG(format, ...) \ + furi_log_print_format(FuriLogLevelDebug, TAG, "%s: " format, __FUNCTION__, __VA_ARGS__) +/* log message*/ +#define LOG(...) furi_log_print_format(FuriLogLevelDefault, TAG, __VA_ARGS__) + +#define COUNT(x) ((size_t)(sizeof(x) / sizeof((x)[0]))) +#define ARRAY_SIZE(x) COUNT(x) + +#define SWD_DELAY_US 0 +#define TIMER_HZ 25 +#define TIMEOUT 3 +#define QUEUE_SIZE 8 +#define IDLE_BITS 8 +#define CLOCK_DELAY 0 + +#define MAX_FILE_LENGTH 128 +#define SCRIPT_MAX_LINES 1000 + +typedef enum { + ModePageScan = 0, + ModePageFound = 1, + ModePageDPRegs = 2, + ModePageDPID = 3, + ModePageAPID = 4, + ModePageCount = 5, + ModePageHexDump = 0x100, + ModePageScript = 0x101, + ModePageCoresight = 0x102, +} ModePages; + +#define CDBGPWRUPREQ (1 << 28) +#define CDBGPWRUPACK (1 << 29) +#define CSYSPWRUPREQ (1 << 30) +#define CSYSPWRUPACK (1 << 31) +#define WDATAERR (1 << 7) +#define STICKYERR (1 << 5) +#define STAT_ERROR_FLAGS (WDATAERR | STICKYERR) + +#define REG_IDCODE 0x00 +#define REG_CTRLSTAT 0x01 +#define REG_CTRLSTAT_BANK 0x00 +#define REG_DLCR 0x01 +#define REG_DLCR_BANK 0x01 +#define REG_TARGETID 0x01 +#define REG_TARGETID_BANK 0x02 +#define REG_DLPIDR 0x01 +#define REG_DLPIDR_BANK 0x03 +#define REG_EVENTSTAT 0x01 +#define REG_EVENTSTAT_BANK 0x04 + +#define REG_SELECT 0x02 + +#define MEMAP_CSW 0x00 +#define MEMAP_TAR 0x04 +#define MEMAP_DRW 0x0C +#define AP_IDR 0xFC +#define AP_BASE 0xF8 + +#define SCS_CPUID 0xE000ED00u +#define SCS_CPACR 0xE000ED88u +#define SCS_DHCSR 0xE000EDF0u +#define SCS_DHCSR_S_HALT (1u << 17) +#define SCS_DHCSR_C_MASKINTS (1u << 3) +#define SCS_DHCSR_C_STEP (1u << 2) +#define SCS_DHCSR_C_HALT (1u << 1) +#define SCS_DHCSR_C_DEBUGEN (1u << 0) +#define SCS_DHCSR_KEY 0xA05F0000u +#define SCS_DCRSR 0xE000EDF4u +#define SCS_DCRSR_RD 0x00000000u +#define SCS_DCRSR_WR 0x00010000u +#define SCS_DCRDR 0xE000EDF8u +#define SCS_DEMCR 0xE000EDFCu + +typedef enum { KeyNone, KeyUp, KeyRight, KeyDown, KeyLeft, KeyOK } KeyCode; + +typedef enum { + EventTimerTick, + EventKeyPress, +} EventType; + +typedef struct { + EventType type; + InputEvent input; +} AppEvent; + +typedef struct { + uint32_t ctrlstat; + bool ctrlstat_ok; + uint32_t dlcr; + bool dlcr_ok; + uint32_t dlpidr; + bool dlpidr_ok; + uint32_t dpidr; + bool dpidr_ok; + uint32_t eventstat; + bool eventstat_ok; + uint32_t select; + bool select_ok; + uint32_t targetid; + bool targetid_ok; +} swd_dpreg_t; + +typedef struct { + bool ok; + bool tested; + uint8_t revision; + uint16_t designer; + uint8_t class; + uint8_t variant; + uint8_t type; + uint32_t base; +} swd_apidr_info_t; + +typedef struct { + uint8_t revision; + uint8_t partno; + uint8_t version; + uint16_t designer; +} swd_dpidr_info_t; + +typedef struct { + uint8_t revision; + uint16_t partno; + uint16_t designer; +} swd_targetid_info_t; + +typedef struct sScriptContext ScriptContext; + +typedef struct { + Storage* storage; + Gui* gui; + DialogsApp* dialogs; + NotificationApp* notification; + + FuriTimer* timer; + UsbUart* uart; + ViewPort* view_port; + + FuriMessageQueue* event_queue; + FuriMutex* swd_mutex; + FuriMutex* gui_mutex; + + swd_targetid_info_t targetid_info; + swd_dpidr_info_t dpidr_info; + swd_dpreg_t dp_regs; + swd_apidr_info_t apidr_info[256]; + + ScriptContext* script; + ScriptContext* commandline; + + uint8_t timeout_overdue; + uint32_t loop_count; + uint8_t current_mask_id; + uint32_t current_mask; + uint8_t io_swc; + uint8_t io_swd; + uint8_t io_num_swc; + uint8_t io_num_swd; + int32_t detected_timeout; + uint32_t swd_clock_delay; + uint32_t swd_idle_bits; + bool detected; + bool detected_device; + bool detected_notified; + uint32_t mode_page; + uint8_t ap_pos; + uint8_t ap_scanned; + + uint32_t coresight_pos[16]; + uint32_t coresight_count[16]; + uint8_t coresight_level; + uint32_t coresight_bases[16]; + + uint32_t hex_addr; + uint8_t hex_select; + uint8_t hex_buffer[32]; + uint8_t hex_buffer_valid[8]; + + char state_string[64]; + char script_detected[MAX_FILE_LENGTH]; + bool script_detected_executed; +} AppFSM; + +struct sScriptContext { + AppFSM* app; + ScriptContext* parent; + char filename[MAX_FILE_LENGTH]; + + /* when used with string input */ + char line_data[128]; + uint64_t line_pos; + + /* when used with file input */ + File* script_file; + + uint64_t position; + uint32_t selected_ap; + uint32_t max_tries; + uint32_t block_size; + + bool abort; + bool restart; + bool errors_ignore; + bool status_ignore; + bool goto_active; + char goto_label[64]; +}; + +typedef struct { + const char* prefix; + bool (*func)(ScriptContext* ctx); +} ScriptFunctionInfo; + +uint8_t swd_read_memory(AppFSM* const ctx, uint8_t ap, uint32_t address, uint32_t* data); + +#endif diff --git a/applications/plugins/swd_probe/usb_uart.c b/applications/plugins/swd_probe/usb_uart.c new file mode 100644 index 000000000..9674541ec --- /dev/null +++ b/applications/plugins/swd_probe/usb_uart.c @@ -0,0 +1,229 @@ + +#include + +#include "usb_uart.h" +#include "furi_hal.h" +#include +#include "usb_cdc.h" +#include "cli/cli_vcp.h" +#include +#include "cli/cli.h" + +#define USB_CDC_PKT_LEN CDC_DATA_SZ +#define USB_UART_RX_BUF_SIZE (USB_CDC_PKT_LEN * 5) + +#define USB_CDC_BIT_DTR (1 << 0) +#define USB_CDC_BIT_RTS (1 << 1) + +typedef enum { + WorkerEvtStop = (1 << 0), + WorkerEvtCdcRx = (1 << 1), + WorkerEvtCfgChange = (1 << 2) + +} WorkerEvtFlags; + +#define WORKER_ALL_EVENTS (WorkerEvtStop | WorkerEvtCfgChange | WorkerEvtCdcRx) + +struct UsbUart { + UsbUartConfig cfg; + UsbUartConfig cfg_new; + + FuriThread* thread; + FuriMutex* usb_mutex; + FuriSemaphore* tx_sem; + UsbUartState st; + FuriApiLock cfg_lock; + + uint8_t rx_buf[USB_CDC_PKT_LEN]; +}; + +static void vcp_on_cdc_tx_complete(void* context); +static void vcp_on_cdc_rx(void* context); +static void vcp_state_callback(void* context, uint8_t state); +static void vcp_on_cdc_control_line(void* context, uint8_t state); +static void vcp_on_line_config(void* context, struct usb_cdc_line_coding* config); + +static const CdcCallbacks cdc_cb = { + .tx_ep_callback = &vcp_on_cdc_tx_complete, + .rx_ep_callback = &vcp_on_cdc_rx, + .state_callback = &vcp_state_callback, + .ctrl_line_callback = &vcp_on_cdc_control_line, + .config_callback = &vcp_on_line_config}; + +static void usb_uart_vcp_init(UsbUart* usb_uart, uint8_t vcp_ch) { + furi_hal_usb_unlock(); + + Cli* cli = furi_record_open(RECORD_CLI); + cli_session_close(cli); + + if(vcp_ch == 0) { + furi_check(furi_hal_usb_set_config(&usb_cdc_single, NULL) == true); + } else { + furi_check(furi_hal_usb_set_config(&usb_cdc_dual, NULL) == true); + cli_session_open(cli, &cli_vcp); + } + furi_record_close(RECORD_CLI); + furi_hal_cdc_set_callbacks(vcp_ch, (CdcCallbacks*)&cdc_cb, usb_uart); +} + +static void usb_uart_vcp_deinit(UsbUart* usb_uart, uint8_t vcp_ch) { + UNUSED(usb_uart); + furi_hal_cdc_set_callbacks(vcp_ch, NULL, NULL); + if(vcp_ch != 0) { + Cli* cli = furi_record_open(RECORD_CLI); + cli_session_close(cli); + furi_record_close(RECORD_CLI); + } +} + +bool usb_uart_tx_data(UsbUart* usb_uart, uint8_t* data, size_t length) { + uint32_t pos = 0; + while(pos < length) { + size_t pkt_size = length - pos; + + if(pkt_size > USB_CDC_PKT_LEN) { + pkt_size = USB_CDC_PKT_LEN; + } + + if(furi_semaphore_acquire(usb_uart->tx_sem, 100) != FuriStatusOk) { + return false; + } + if(furi_mutex_acquire(usb_uart->usb_mutex, 100) != FuriStatusOk) { + furi_semaphore_release(usb_uart->tx_sem); + return false; + } + furi_hal_cdc_send(usb_uart->cfg.vcp_ch, &data[pos], pkt_size); + furi_mutex_release(usb_uart->usb_mutex); + usb_uart->st.tx_cnt += pkt_size; + pos += pkt_size; + } + return true; +} + +static int32_t usb_uart_worker(void* context) { + UsbUart* usb_uart = (UsbUart*)context; + + memcpy(&usb_uart->cfg, &usb_uart->cfg_new, sizeof(UsbUartConfig)); + + usb_uart->tx_sem = furi_semaphore_alloc(1, 1); + usb_uart->usb_mutex = furi_mutex_alloc(FuriMutexTypeNormal); + + usb_uart_vcp_init(usb_uart, usb_uart->cfg.vcp_ch); + + uint8_t data[2 * USB_CDC_PKT_LEN]; + size_t remain = 0; + + while(1) { + uint32_t events = + furi_thread_flags_wait(WORKER_ALL_EVENTS, FuriFlagWaitAny, FuriWaitForever); + furi_check(!(events & FuriFlagError)); + + if(events & WorkerEvtStop) { + break; + } + + if(events & WorkerEvtCdcRx) { + size_t len = 0; + if(furi_mutex_acquire(usb_uart->usb_mutex, 100) == FuriStatusOk) { + len = furi_hal_cdc_receive(usb_uart->cfg.vcp_ch, &data[remain], USB_CDC_PKT_LEN); + furi_mutex_release(usb_uart->usb_mutex); + } + + if(len > 0) { + usb_uart->st.rx_cnt += len; + remain += len; + + size_t handled = usb_uart->cfg.rx_data(usb_uart->cfg.rx_data_ctx, data, remain); + + memcpy(data, &data[handled], remain - handled); + remain -= handled; + } + } + + if(events & WorkerEvtCfgChange) { + if(usb_uart->cfg.vcp_ch != usb_uart->cfg_new.vcp_ch) { + usb_uart_vcp_deinit(usb_uart, usb_uart->cfg.vcp_ch); + usb_uart_vcp_init(usb_uart, usb_uart->cfg_new.vcp_ch); + + usb_uart->cfg.vcp_ch = usb_uart->cfg_new.vcp_ch; + } + api_lock_unlock(usb_uart->cfg_lock); + } + } + usb_uart_vcp_deinit(usb_uart, usb_uart->cfg.vcp_ch); + + furi_mutex_free(usb_uart->usb_mutex); + furi_semaphore_free(usb_uart->tx_sem); + + furi_hal_usb_unlock(); + furi_check(furi_hal_usb_set_config(&usb_cdc_single, NULL) == true); + Cli* cli = furi_record_open(RECORD_CLI); + cli_session_open(cli, &cli_vcp); + furi_record_close(RECORD_CLI); + + return 0; +} + +/* VCP callbacks */ +static void vcp_on_cdc_tx_complete(void* context) { + UsbUart* usb_uart = (UsbUart*)context; + furi_semaphore_release(usb_uart->tx_sem); +} + +static void vcp_on_cdc_rx(void* context) { + UsbUart* usb_uart = (UsbUart*)context; + furi_thread_flags_set(furi_thread_get_id(usb_uart->thread), WorkerEvtCdcRx); +} + +static void vcp_state_callback(void* context, uint8_t state) { + UNUSED(context); + UNUSED(state); +} + +static void vcp_on_cdc_control_line(void* context, uint8_t state) { + UNUSED(context); + UNUSED(state); +} + +static void vcp_on_line_config(void* context, struct usb_cdc_line_coding* config) { + UNUSED(context); + UNUSED(config); +} + +UsbUart* usb_uart_enable(UsbUartConfig* cfg) { + UsbUart* usb_uart = malloc(sizeof(UsbUart)); + memcpy(&(usb_uart->cfg_new), cfg, sizeof(UsbUartConfig)); + + usb_uart->thread = furi_thread_alloc_ex("UsbUartWorker", 1024, usb_uart_worker, usb_uart); + furi_thread_start(usb_uart->thread); + return usb_uart; +} + +void usb_uart_disable(UsbUart* usb_uart) { + furi_assert(usb_uart); + furi_thread_flags_set(furi_thread_get_id(usb_uart->thread), WorkerEvtStop); + furi_thread_join(usb_uart->thread); + furi_thread_free(usb_uart->thread); + free(usb_uart); +} + +void usb_uart_set_config(UsbUart* usb_uart, UsbUartConfig* cfg) { + furi_assert(usb_uart); + furi_assert(cfg); + usb_uart->cfg_lock = api_lock_alloc_locked(); + memcpy(&(usb_uart->cfg_new), cfg, sizeof(UsbUartConfig)); + furi_thread_flags_set(furi_thread_get_id(usb_uart->thread), WorkerEvtCfgChange); + api_lock_wait_unlock_and_free(usb_uart->cfg_lock); +} + +void usb_uart_get_config(UsbUart* usb_uart, UsbUartConfig* cfg) { + furi_assert(usb_uart); + furi_assert(cfg); + memcpy(cfg, &(usb_uart->cfg_new), sizeof(UsbUartConfig)); +} + +void usb_uart_get_state(UsbUart* usb_uart, UsbUartState* st) { + furi_assert(usb_uart); + furi_assert(st); + memcpy(st, &(usb_uart->st), sizeof(UsbUartState)); +} diff --git a/applications/plugins/swd_probe/usb_uart.h b/applications/plugins/swd_probe/usb_uart.h new file mode 100644 index 000000000..325a4db98 --- /dev/null +++ b/applications/plugins/swd_probe/usb_uart.h @@ -0,0 +1,29 @@ +#pragma once + +#include +#include + +typedef struct UsbUart UsbUart; + +typedef struct { + uint8_t vcp_ch; + size_t (*rx_data)(void* ctx, uint8_t* data, size_t length); + void* rx_data_ctx; +} UsbUartConfig; + +typedef struct { + uint32_t rx_cnt; + uint32_t tx_cnt; +} UsbUartState; + +UsbUart* usb_uart_enable(UsbUartConfig* cfg); + +void usb_uart_disable(UsbUart* usb_uart); + +void usb_uart_set_config(UsbUart* usb_uart, UsbUartConfig* cfg); + +void usb_uart_get_config(UsbUart* usb_uart, UsbUartConfig* cfg); + +void usb_uart_get_state(UsbUart* usb_uart, UsbUartState* st); + +bool usb_uart_tx_data(UsbUart* usb_uart, uint8_t* data, size_t length); diff --git a/applications/plugins/tama_p1/README.md b/applications/plugins/tama_p1/README.md index 0c06eb712..cb58931cc 100644 --- a/applications/plugins/tama_p1/README.md +++ b/applications/plugins/tama_p1/README.md @@ -8,27 +8,32 @@ This is a tama P1 Emulator app for Flipper Zero, based on [TamaLIB](https://gith How to play ----------- Create a `tama_p1` folder in your microSD card, and put the ROM as `rom.bin`. -Use a search engine to find the Tamagotchi ROM. There is a file named `a`. +Use a search engine to find the Tamagotchi ROM. There is a file named `tama.b`. Rename this to `rom.bin`. +*Controls in portrait mode are the same as landscape mode, but turned 90 degrees.* - Left button is A. -- OK is B. +- Down or OK is B. - Right button is C. -- Holding the Up button functions the same as press both A and C, which mutes the volume. +- Up button takes you to the emulator menu. - Hold the Back button to save and exit. +![Alt Text](Screenshot1.png) +![Alt Text](Screenshot2.png) Building -------- -Move this folder into flippers applications/plugins/tama_p1. +Move this folder into flippers `applications/plugins/tama_p1`. Launching the app, directly from console to flipper: -`./fbt launch_app APPSRC=applications\plugins\tama_p1` +``` +./fbt launch_app APPSRC=applications/plugins/tama_p1 +``` Run the following to compile icons: ``` -scripts/assets.py icons applications/tama_p1/icons applications/tama_p1/compiled +scripts/assets.py icons applications/plugins/tama_p1/icons applications/plugins/tama_p1/compiled ``` Note: you may also need to add `-Wno-unused-parameter` to `CCFLAGS` in @@ -38,22 +43,34 @@ Debugging --------- Using the serial script from [FlipperScripts](https://github.com/DroomOne/FlipperScripts/blob/main/serial_logger.py) it is easy to add direct logging after running the application: +``` `python .\serial_logger.py` -`./fbt launch_app APPSRC=applications\plugins\tama_p1; python .\serial_logger.py` - +`./fbt launch_app APPSRC=applications/plugins/tama_p1; python .\serial_logger.py` +``` +Alternatively, follow the directions here: https://flipper.atmanos.com/docs/debugging/viewing/ Implemented ----------- -- Basic emulation -- Input -- Sound -- Saving/Loading emulator state (stored in `/ext/tama_p1/save.bin`) -- Mute button combo shortcut (Up = A+C) +- Menu options: + - Switch between portrait and landscape + - A+C shortcut (mute/change in-game time) + - Double / quadruple speed -To-do +![Alt Text](Screenshot3.png) + +To-Do ----- -- more than one save slot -- In-game reset -- Test mode? -- Volume adjustment +- Fix bugs: + - When not on 1x speed, after mashing buttons in quick succession, buttons stop responding for a few seconds. But the rom still runs. +- Stuff to do when bored: + - optimization and bug fixing (see above) + - add to this list + - portrait menu + - Add "loading bar" when saving + - "Advanced" settings + - saving and loading, multiple save states, with the date and time of of each save. + - Autosave and changing autosave frequency + - Save settings to /tama_p1/settings.txt + +![Alt Text](Screenshot4.png) diff --git a/applications/plugins/tama_p1/application.fam b/applications/plugins/tama_p1/application.fam index 2f089a54b..93ee53aa5 100644 --- a/applications/plugins/tama_p1/application.fam +++ b/applications/plugins/tama_p1/application.fam @@ -5,7 +5,7 @@ App( entry_point="tama_p1_app", cdefines=["APP_TAMA_P1"], requires=["gui", "storage"], - stack_size=1 * 1024, + stack_size=2 * 1024, order=215, fap_icon="tamaIcon.png", fap_category="Games", diff --git a/applications/plugins/tama_p1/tama_p1.c b/applications/plugins/tama_p1/tama_p1.c index 0e7686a06..1d7939a01 100644 --- a/applications/plugins/tama_p1/tama_p1.c +++ b/applications/plugins/tama_p1/tama_p1.c @@ -10,6 +10,21 @@ TamaApp* g_ctx; FuriMutex* g_state_mutex; +uint8_t layout_mode = 0; // 3: portrait => 4: portrait <= +// 0: landscape (small) 1: landscape (big) 2: landscape (full) +bool in_menu = false; + +uint8_t speed = 1; +const uint8_t speed_options[] = {1, 2, 4}; +const uint8_t min_speed = 1; +const uint8_t max_speed = 4; +const uint8_t speed_options_size = 3; +// = sizeof(speed_options) / sizeof(speed_options[0]); + +uint8_t menu_cursor = 3; // 0: layout mode; 1: speed; 2: A+C; +const uint8_t menu_items = 4; // 3: Close menu, Save & Exit +uint8_t sub_menu_buttons = 0; +uint8_t sub_menu_last = 0; static const Icon* icons_list[] = { &I_icon_0, @@ -22,6 +37,384 @@ static const Icon* icons_list[] = { &I_icon_7, }; +static InputKey m = InputKeyUp; +static InputKey a = InputKeyLeft; +static InputKey b = InputKeyDown; +static InputKey c = InputKeyRight; + +static void speed_up() { + switch(speed) { + case max_speed: + speed = speed_options[0]; + break; + default: + for(uint8_t i = 0; i < speed_options_size - 1; i++) { + if(speed == speed_options[i]) { + speed = speed_options[i + 1]; + break; + } + } + break; + } + tamalib_set_speed(speed); +} +static void speed_down() { + switch(speed) { + case min_speed: + speed = speed_options[speed_options_size - 1]; + break; + default: + for(uint8_t i = speed_options_size - 1; i > 0; i--) { + if(speed == speed_options[i]) { + speed = speed_options[i - 1]; + break; + } + } + break; + } + tamalib_set_speed(speed); +} + +// static void draw_landscape(Canvas* const canvas, void* cb_ctx) +static void draw_landscape(Canvas* const canvas, uint8_t scale) { + // FURI_LOG_D(TAG, "Drawing frame"); + // Calculate positioning + uint16_t canv_width = canvas_width(canvas); + uint16_t canv_height = canvas_height(canvas); + uint16_t lcd_matrix_scaled_width = 32 * scale; + uint16_t lcd_matrix_scaled_height = 16 * scale; + uint16_t lcd_matrix_top = (canv_height - lcd_matrix_scaled_height) / 2; // 0 + uint16_t lcd_matrix_left = (canv_width - lcd_matrix_scaled_width) / 2; + + // uint16_t lcd_icon_upper_top = lcd_matrix_top - TAMA_LCD_ICON_SIZE - TAMA_LCD_ICON_MARGIN; + // uint16_t lcd_icon_lower_top = lcd_matrix_top + lcd_matrix_scaled_height + TAMA_LCD_ICON_MARGIN; + uint16_t lcd_icon_upper_left = lcd_matrix_left; + uint16_t lcd_icon_lower_left = lcd_matrix_left; + uint16_t lcd_icon_spacing_horiz = + (lcd_matrix_scaled_width - (4 * TAMA_LCD_ICON_SIZE)) / 3 + TAMA_LCD_ICON_SIZE; + + uint16_t y = lcd_matrix_top; + for(uint8_t row = 0; row < 16; ++row) { + uint16_t x = lcd_matrix_left; + uint32_t row_pixels = g_ctx->framebuffer[row]; + for(uint8_t col = 0; col < 32; ++col) { + if(row_pixels & 1) { + canvas_draw_box(canvas, x, y, scale, scale); + } + x += scale; + row_pixels >>= 1; + } + y += scale; + } + + // Start drawing icons + uint8_t lcd_icons = g_ctx->icons; + + // Draw top icons + y = 0; + uint16_t x_ic = lcd_icon_upper_left; + for(uint8_t i = 0; i < 4; ++i) { + if(lcd_icons & 1) { + canvas_draw_icon(canvas, x_ic, y, icons_list[i]); + } + // x_ic += TAMA_LCD_ICON_SIZE + 4; + // if(scale == 3) { + // y += 16; + // } else { + x_ic += lcd_icon_spacing_horiz; + // } + lcd_icons >>= 1; + } + + // Draw bottom icons + y = 64 - TAMA_LCD_ICON_SIZE; + // if(scale == 3) { + // y = 0; + // x_ic = 128 - TAMA_LCD_ICON_SIZE; + // x_ic = 0; + // } else { + // y = 64 - TAMA_LCD_ICON_SIZE; + x_ic = lcd_icon_lower_left; + // } + for(uint8_t i = 4; i < 8; ++i) { + // canvas_draw_frame(canvas, x_ic, y, TAMA_LCD_ICON_SIZE, TAMA_LCD_ICON_SIZE); + if(lcd_icons & 1) { + canvas_draw_icon(canvas, x_ic, y, icons_list[i]); + } + // if(scale == 3) { + // y += 16; + // } else { + x_ic += lcd_icon_spacing_horiz; + // } + lcd_icons >>= 1; + } +} +// static void draw_portrait_right(Canvas* const canvas, void* cb_ctx) +static void draw_portrait_right(Canvas* const canvas, uint8_t scale) { + // FURI_LOG_D(TAG, "Drawing frame"); + // Calculate positioning + // uint16_t canv_width = canvas_width(canvas); + uint16_t canv_height = canvas_height(canvas); + uint16_t lcd_matrix_scaled_width = 32 * scale; + uint16_t lcd_matrix_scaled_height = 16 * scale; + // uint16_t lcd_matrix_top = 0; + uint16_t lcd_matrix_top = (canv_height - lcd_matrix_scaled_height) / 2; + // uint16_t lcd_matrix_left = (canv_width - lcd_matrix_scaled_width) / 2; + uint16_t lcd_matrix_left = 64 - TAMA_LCD_ICON_SIZE; + uint16_t lcd_icon_upper_left = lcd_matrix_left; + uint16_t lcd_icon_lower_left = lcd_matrix_left; + uint16_t lcd_icon_spacing_horiz = + (lcd_matrix_scaled_width - (4 * TAMA_LCD_ICON_SIZE)) / 3 + TAMA_LCD_ICON_SIZE; + + uint16_t y = lcd_matrix_top; // 64 + for(uint8_t row = 0; row < 16; ++row) { + uint16_t x = 128; // lcd_matrix_left + uint32_t row_pixels = g_ctx->framebuffer[row]; + for(uint8_t col = 0; col < 32; ++col) { + if(row_pixels & 1) { + canvas_draw_box(canvas, y + 32, x - 66, scale, scale); + } + x -= scale; + row_pixels >>= 1; + } + y += scale; + } + + // Start drawing icons + uint8_t lcd_icons = g_ctx->icons; + + // Draw top icons + // y = lcd_icon_upper_top; + y = 30; + // y = 64 - TAMA_LCD_ICON_SIZE; + uint16_t x_ic = lcd_icon_upper_left; + // uint16_t x_ic = 64 - TAMA_LCD_ICON_SIZE; + for(uint8_t i = 0; i < 4; ++i) { + if(lcd_icons & 1) { + canvas_draw_icon(canvas, y, x_ic, icons_list[i]); + } + x_ic -= lcd_icon_spacing_horiz; // TAMA_LCD_ICON_SIZE + 4; + lcd_icons >>= 1; + } + + // Draw bottom icons + y = 84; // lcd_icon_lower_top + x_ic = lcd_icon_lower_left; // 64 - TAMA_LCD_ICON_SIZE + for(uint8_t i = 4; i < 8; ++i) { + // canvas_draw_frame(canvas, x_ic, y, TAMA_LCD_ICON_SIZE, TAMA_LCD_ICON_SIZE); + if(lcd_icons & 1) { + canvas_draw_icon(canvas, y, x_ic, icons_list[i]); + } + x_ic -= lcd_icon_spacing_horiz; + lcd_icons >>= 1; + } +} +static void draw_portrait_left(Canvas* const canvas, uint8_t scale) { + // FURI_LOG_D(TAG, "Drawing frame"); + // Calculate positioning + // uint16_t canv_width = canvas_width(canvas); + // uint16_t canv_height = canvas_height(canvas); + uint16_t lcd_matrix_scaled_width = 32 * scale; + // uint16_t lcd_matrix_scaled_height = 16 * scale; + // uint16_t lcd_matrix_top = 0; + // uint16_t lcd_matrix_top = (canv_height - lcd_matrix_scaled_height) / 2; + // uint16_t lcd_matrix_left = (canv_width - lcd_matrix_scaled_width) / 2; + uint16_t lcd_matrix_left = 0; + uint16_t lcd_icon_upper_left = lcd_matrix_left; + uint16_t lcd_icon_lower_left = lcd_matrix_left; + uint16_t lcd_icon_spacing_horiz = + (lcd_matrix_scaled_width - (4 * TAMA_LCD_ICON_SIZE)) / 3 + TAMA_LCD_ICON_SIZE; + + // uint16_t y = 64 + lcd_matrix_top; + uint16_t y = 64; + for(uint8_t row = 0; row < 16; ++row) { + uint16_t x = 0; // lcd_matrix_left + uint32_t row_pixels = g_ctx->framebuffer[row]; + for(uint8_t col = 0; col < 32; ++col) { + if(row_pixels & 1) { + canvas_draw_box(canvas, y, x, scale, scale); + } + x += scale; + row_pixels >>= 1; + } + y -= scale; + } + + // Start drawing icons + uint8_t lcd_icons = g_ctx->icons; + + // Draw top icons + // y = lcd_icon_upper_top; + y = 70; + // y = 64 - TAMA_LCD_ICON_SIZE; + uint16_t x_ic = lcd_icon_upper_left; + // uint16_t x_ic = 64 - TAMA_LCD_ICON_SIZE; + for(uint8_t i = 0; i < 4; ++i) { + if(lcd_icons & 1) { + canvas_draw_icon(canvas, y, x_ic, icons_list[i]); + } + x_ic += lcd_icon_spacing_horiz; // TAMA_LCD_ICON_SIZE + 4; + lcd_icons >>= 1; + } + + // Draw bottom icons + y = 16; // lcd_icon_lower_top + x_ic = lcd_icon_lower_left; // 64 - TAMA_LCD_ICON_SIZE + for(uint8_t i = 4; i < 8; ++i) { + // canvas_draw_frame(canvas, x_ic, y, TAMA_LCD_ICON_SIZE, TAMA_LCD_ICON_SIZE); + if(lcd_icons & 1) { + canvas_draw_icon(canvas, y, x_ic, icons_list[i]); + } + x_ic += lcd_icon_spacing_horiz; + lcd_icons >>= 1; + } +} +// static void draw_mini(Canvas* const canvas, uint16_t inX, uint16_t inY) +static void draw_mini(Canvas* const canvas) { + // Calculate positioning + // uint16_t y = inY; + const uint16_t y = 34; + const uint16_t x = 84; + + uint16_t y1 = y; + for(uint8_t row = 0; row < 16; ++row) { + // uint16_t x = inX; + uint16_t x1 = x; + uint32_t row_pixels = g_ctx->framebuffer[row]; + for(uint8_t col = 0; col < 32; ++col) { + if(row_pixels & 1) { + canvas_draw_dot(canvas, x1, y1); + } + x1 += 1; + row_pixels >>= 1; + } + y1 += 1; + } + + // Start drawing icons + uint8_t lcd_icons = g_ctx->icons; + + // Draw top icons + uint16_t y2 = y - 2; + uint16_t x_ic = x; + for(uint8_t i = 0; i < 4; ++i) { + if(lcd_icons & 1) { + // canvas_draw_icon(canvas, x_ic, y, icons_list[i]); + canvas_draw_line(canvas, x_ic, y2, x_ic + 6, y2); + } + x_ic += 8; + lcd_icons >>= 1; + } + + // Draw bottom icons + y2 = y + 17; + x_ic = x; + for(uint8_t i = 4; i < 8; ++i) { + // canvas_draw_frame(canvas, x_ic, y, TAMA_LCD_ICON_SIZE, TAMA_LCD_ICON_SIZE); + if(lcd_icons & 1) { + // canvas_draw_icon(canvas, x_ic, y, icons_list[i]); + canvas_draw_line(canvas, x_ic, y2, x_ic + 6, y2); + } + x_ic += 8; + lcd_icons >>= 1; + } +} + +static void draw_menu(Canvas* const canvas) { + canvas_draw_frame(canvas, 0, 0, 128, 64); + canvas_draw_str_aligned(canvas, 64, 6, AlignCenter, AlignCenter, "Menu"); + canvas_draw_line(canvas, 0, 10, 128, 10); + draw_mini(canvas); + // draw_mini(canvas, 34, 84); + + switch(menu_cursor) { + case 0: + canvas_draw_triangle(canvas, 4, 16, 6, 6, CanvasDirectionLeftToRight); + break; + case 1: + canvas_draw_triangle(canvas, 4, 26, 6, 6, CanvasDirectionLeftToRight); + break; + case 2: + switch(sub_menu_buttons) { + case 0: + canvas_draw_triangle(canvas, 4, 36, 6, 6, CanvasDirectionLeftToRight); + break; + case 1: + canvas_draw_triangle(canvas, 47, 44, 6, 4, CanvasDirectionBottomToTop); + break; + case 2: + canvas_draw_triangle(canvas, 57, 44, 6, 4, CanvasDirectionBottomToTop); + break; + case 3: + canvas_draw_triangle(canvas, 67, 44, 6, 4, CanvasDirectionBottomToTop); + break; + default: + break; + } + break; + case menu_items - 1: + switch(sub_menu_last) { + case 0: + canvas_draw_triangle(canvas, 4, 56, 6, 6, CanvasDirectionLeftToRight); + break; + case 1: + canvas_draw_triangle(canvas, 36, 56, 6, 6, CanvasDirectionLeftToRight); + break; + case 2: + canvas_draw_triangle(canvas, 67, 56, 6, 6, CanvasDirectionLeftToRight); + break; + default: + break; + } + break; + } + switch(layout_mode) { + case 0: + canvas_draw_str(canvas, 12, 20, "Layout: Landscape (small)"); + break; + case 1: + canvas_draw_str(canvas, 12, 20, "Layout: Landscape (big)"); + break; + case 2: + canvas_draw_str(canvas, 12, 20, "Layout: Landscape (full)"); + break; + case 3: + canvas_draw_str(canvas, 12, 20, "Layout: Portrait =>"); + break; + case 4: + canvas_draw_str(canvas, 12, 20, "Layout: Portrait <="); + break; + default: + canvas_draw_str(canvas, 12, 20, "Layout: ???"); + break; + } + switch(speed) { // match with speed_options + // case 0: // freeze menu too + // canvas_draw_str(canvas, 12, 30, "Speed: 0x"); + // break; + case 1: + canvas_draw_str(canvas, 12, 30, "Speed: 1x"); + break; + case 2: + canvas_draw_str(canvas, 12, 30, "Speed: 2x"); + break; + case 4: + canvas_draw_str(canvas, 12, 30, "Speed: 4x (max)"); + break; + default: + canvas_draw_str(canvas, 12, 30, "Speed ??x"); + break; + } + canvas_draw_str(canvas, 12, 40, "A+C"); + canvas_draw_str(canvas, 45, 40, "A"); + canvas_draw_str(canvas, 55, 40, "B"); + canvas_draw_str(canvas, 65, 40, "C"); + + canvas_draw_str(canvas, 12, 60, "Close"); + canvas_draw_str(canvas, 44, 60, "Save"); + canvas_draw_str(canvas, 75, 60, "Save & Exit"); +} + static void tama_p1_draw_callback(Canvas* const canvas, void* cb_ctx) { furi_assert(cb_ctx); @@ -35,68 +428,32 @@ static void tama_p1_draw_callback(Canvas* const canvas, void* cb_ctx) { canvas_set_font(canvas, FontPrimary); canvas_draw_str(canvas, 30, 30, "Halted"); } else { - // FURI_LOG_D(TAG, "Drawing frame"); - // Calculate positioning - uint16_t canv_width = canvas_width(canvas); - uint16_t canv_height = canvas_height(canvas); - uint16_t lcd_matrix_scaled_width = 32 * TAMA_SCREEN_SCALE_FACTOR; - uint16_t lcd_matrix_scaled_height = 16 * TAMA_SCREEN_SCALE_FACTOR; - // uint16_t lcd_matrix_top = 0; - uint16_t lcd_matrix_top = (canv_height - lcd_matrix_scaled_height) / 2; - uint16_t lcd_matrix_left = (canv_width - lcd_matrix_scaled_width) / 2; - - uint16_t lcd_icon_upper_top = lcd_matrix_top - TAMA_LCD_ICON_SIZE - TAMA_LCD_ICON_MARGIN; - uint16_t lcd_icon_upper_left = lcd_matrix_left; - uint16_t lcd_icon_lower_top = - lcd_matrix_top + lcd_matrix_scaled_height + TAMA_LCD_ICON_MARGIN; - uint16_t lcd_icon_lower_left = lcd_matrix_left; - uint16_t lcd_icon_spacing_horiz = - (lcd_matrix_scaled_width - (4 * TAMA_LCD_ICON_SIZE)) / 3 + TAMA_LCD_ICON_SIZE; - - uint16_t y = lcd_matrix_top; - for(uint8_t row = 0; row < 16; ++row) { - uint16_t x = lcd_matrix_left; - uint32_t row_pixels = g_ctx->framebuffer[row]; - for(uint8_t col = 0; col < 32; ++col) { - if(row_pixels & 1) { - canvas_draw_box( - canvas, x, y, TAMA_SCREEN_SCALE_FACTOR, TAMA_SCREEN_SCALE_FACTOR); - } - x += TAMA_SCREEN_SCALE_FACTOR; - row_pixels >>= 1; + if(in_menu) { + // switch(layout_mode) + // draw_menu_landscape(canvas); + draw_menu(canvas); + } else { + switch(layout_mode) { + case 0: + draw_landscape(canvas, TAMA_SCREEN_SCALE_FACTOR); // 2 + break; + case 1: + draw_landscape(canvas, 3); + break; + case 2: + draw_landscape(canvas, 4); + break; + case 3: + draw_portrait_right(canvas, TAMA_SCREEN_SCALE_FACTOR); + break; + case 4: + draw_portrait_left(canvas, TAMA_SCREEN_SCALE_FACTOR); + break; + default: + break; } - y += TAMA_SCREEN_SCALE_FACTOR; - } - - // Start drawing icons - uint8_t lcd_icons = g_ctx->icons; - - // Draw top icons - y = lcd_icon_upper_top; - // y = 64 - TAMA_LCD_ICON_SIZE; - uint16_t x_ic = lcd_icon_upper_left; - for(uint8_t i = 0; i < 4; ++i) { - if(lcd_icons & 1) { - canvas_draw_icon(canvas, x_ic, y, icons_list[i]); - } - // x_ic += TAMA_LCD_ICON_SIZE + 4; - x_ic += lcd_icon_spacing_horiz; - lcd_icons >>= 1; - } - - // Draw bottom icons - y = lcd_icon_lower_top; - x_ic = lcd_icon_lower_left; - for(uint8_t i = 4; i < 8; ++i) { - // canvas_draw_frame(canvas, x_ic, y, TAMA_LCD_ICON_SIZE, TAMA_LCD_ICON_SIZE); - if(lcd_icons & 1) { - canvas_draw_icon(canvas, x_ic, y, icons_list[i]); - } - x_ic += lcd_icon_spacing_horiz; - lcd_icons >>= 1; } } - furi_mutex_release(mutex); } @@ -345,9 +702,9 @@ static int32_t tama_p1_worker(void* context) { if(furi_thread_flags_get()) { running = false; } else { - // FURI_LOG_D(TAG, "Stepping"); + // FURI_LOG_D(TAG, "Stepping"); // enabling this cause blank screen somehow // for (int i = 0; i < 100; ++i) - tamalib_step(); + tamalib_step(); // tamalib_mainloop(); } } LL_TIM_DisableCounter(TIM2); @@ -406,15 +763,15 @@ static void tama_p1_init(TamaApp* const ctx) { // Init TamaLIB tamalib_register_hal(&ctx->hal); tamalib_init((u12_t*)ctx->rom, NULL, 64000); - tamalib_set_speed(1); + tamalib_set_speed(speed); // TODO: implement fast forwarding - ctx->fast_forward_done = true; + // ctx->fast_forward_done = true; // Start stepping thread ctx->thread = furi_thread_alloc(); furi_thread_set_name(ctx->thread, "TamaLIB"); - furi_thread_set_stack_size(ctx->thread, 1024); + furi_thread_set_stack_size(ctx->thread, 2 * 1024); furi_thread_set_callback(ctx->thread, tama_p1_worker); furi_thread_set_context(ctx->thread, g_state_mutex); furi_thread_start(ctx->thread); @@ -449,6 +806,9 @@ int32_t tama_p1_app(void* p) { furi_timer_alloc(tama_p1_update_timer_callback, FuriTimerTypePeriodic, event_queue); furi_timer_start(timer, furi_kernel_get_tick_frequency() / 30); + // in_menu = false; + // menu_cursor = 2; + for(bool running = true; running;) { TamaEvent event; FuriStatus event_status = furi_message_queue_get(event_queue, &event, FuriWaitForever); @@ -466,51 +826,353 @@ int32_t tama_p1_app(void* p) { event.input.sequence, event.input.key, event.input.type); - InputType input_type = event.input.type; - if(input_type == InputTypePress || input_type == InputTypeRelease) { - btn_state_t tama_btn_state = 0; - if(input_type == InputTypePress) - tama_btn_state = BTN_STATE_PRESSED; - else if(input_type == InputTypeRelease) - tama_btn_state = BTN_STATE_RELEASED; + // InputType input_type = event.input.type; // idk why this is a variable + btn_state_t tama_btn_state = 0; // BTN_STATE_RELEASED is 0 - if(event.input.key == InputKeyLeft) { - tamalib_set_button(BTN_LEFT, tama_btn_state); + if(in_menu) { + // if(menu_cursor == 2 && + // (event.input.key == InputKeyUp || event.input.key == InputKeyDown)) { + // tama_btn_state = BTN_STATE_RELEASED; + // } + if(event.input.key == InputKeyBack) { + tama_btn_state = BTN_STATE_RELEASED; + in_menu = false; + } else if(event.input.key == InputKeyUp && event.input.type == InputTypePress) { + tama_btn_state = BTN_STATE_RELEASED; + if(menu_cursor > 0) { + menu_cursor -= 1; + } else { + menu_cursor = menu_items - 1; + } + if(menu_cursor >= menu_items - 2 && sub_menu_last > 0) { + sub_menu_buttons = 1; + sub_menu_last = 1; + } else { + sub_menu_buttons = 0; + sub_menu_last = 0; + } + } else if(event.input.key == InputKeyDown && event.input.type == InputTypePress) { + tama_btn_state = BTN_STATE_RELEASED; + if(menu_cursor < menu_items - 1) { + menu_cursor += 1; + } else { + menu_cursor = 0; + } + if(menu_cursor >= menu_items - 2 && sub_menu_buttons > 0) { + sub_menu_buttons = 1; + sub_menu_last = 1; + } else { + sub_menu_buttons = 0; + sub_menu_last = 0; + } + } else if(event.input.key == InputKeyLeft && event.input.type == InputTypePress) { + switch(menu_cursor) { + case 0: + switch(layout_mode) { + case 0: + layout_mode = 4; + m = InputKeyRight; + a = InputKeyUp; + b = InputKeyLeft; + c = InputKeyDown; + break; + case 1: + layout_mode -= 1; // 0 + m = InputKeyUp; + a = InputKeyLeft; + b = InputKeyDown; + c = InputKeyRight; + break; + case 2: + layout_mode -= 1; // 1 + m = InputKeyUp; + a = InputKeyLeft; + b = InputKeyDown; + c = InputKeyRight; + break; + case 3: + layout_mode -= 1; // 2 + m = InputKeyUp; + a = InputKeyLeft; + b = InputKeyDown; + c = InputKeyRight; + break; + case 4: + layout_mode -= 1; // 3 + m = InputKeyLeft; + a = InputKeyDown; + b = InputKeyRight; + c = InputKeyUp; + break; + } + break; + case 1: + speed_down(); + break; + case 2: + tama_btn_state = BTN_STATE_RELEASED; + switch(sub_menu_buttons) { + case 0: + sub_menu_buttons = 3; + break; + case 1: + case 2: + case 3: + sub_menu_buttons -= 1; + break; + default: + break; + } + break; + case menu_items - 1: + switch(sub_menu_last) { + case 0: + sub_menu_last = 2; + break; + case 1: + case 2: + sub_menu_last -= 1; + break; + default: + break; + } + break; + default: + break; + } + } else if(event.input.key == InputKeyRight && event.input.type == InputTypePress) { + switch(menu_cursor) { + case 0: + switch(layout_mode) { + case 0: + layout_mode += 1; // 1 + m = InputKeyUp; + a = InputKeyLeft; + b = InputKeyDown; + c = InputKeyRight; + break; + case 1: + layout_mode += 1; // 2 + m = InputKeyUp; + a = InputKeyLeft; + b = InputKeyDown; + c = InputKeyRight; + break; + case 2: + layout_mode += 1; // 3 + m = InputKeyLeft; + a = InputKeyDown; + b = InputKeyRight; + c = InputKeyUp; + break; + case 3: + layout_mode += 1; // 4 + m = InputKeyRight; + a = InputKeyUp; + b = InputKeyLeft; + c = InputKeyDown; + break; + case 4: + layout_mode = 0; + m = InputKeyUp; + a = InputKeyLeft; + b = InputKeyDown; + c = InputKeyRight; + break; + } + break; + case 1: + speed_up(); + break; + case 2: + tama_btn_state = BTN_STATE_RELEASED; + switch(sub_menu_buttons) { + case 0: + case 1: + case 2: + sub_menu_buttons += 1; + break; + case 3: + sub_menu_buttons = 0; + break; + default: + break; + } + break; + case menu_items - 1: + switch(sub_menu_last) { + case 0: + case 1: + sub_menu_last += 1; + break; + case 2: + sub_menu_last = 0; + break; + default: + break; + } + break; + default: + break; + } } else if(event.input.key == InputKeyOk) { - tamalib_set_button(BTN_MIDDLE, tama_btn_state); - } else if(event.input.key == InputKeyRight) { - tamalib_set_button(BTN_RIGHT, tama_btn_state); - } else if(event.input.key == InputKeyDown && event.input.type == InputTypeShort) { - // TODO: pause or fast-forward tamagotchi - tama_p1_save_state(); - } else if(event.input.key == InputKeyUp) { // mute tamagotchi - tamalib_set_button(BTN_LEFT, tama_btn_state); - tamalib_set_button(BTN_RIGHT, tama_btn_state); - } else if(event.input.key == InputKeyBack && event.input.type == InputTypeShort) { + switch(menu_cursor) { + case 0: + if(event.input.type == InputTypePress) { + switch(layout_mode) { + case 0: + layout_mode += 1; // 1 + m = InputKeyUp; + a = InputKeyLeft; + b = InputKeyDown; + c = InputKeyRight; + break; + case 1: + layout_mode += 1; // 2 + m = InputKeyUp; + a = InputKeyLeft; + b = InputKeyDown; + c = InputKeyRight; + break; + case 2: + layout_mode += 1; // 3 + m = InputKeyLeft; + a = InputKeyDown; + b = InputKeyRight; + c = InputKeyUp; + break; + case 3: + layout_mode += 1; // 4 + m = InputKeyRight; + a = InputKeyUp; + b = InputKeyLeft; + c = InputKeyDown; + break; + case 4: + layout_mode = 0; + m = InputKeyUp; + a = InputKeyLeft; + b = InputKeyDown; + c = InputKeyRight; + break; + } + } + break; + case 1: + if(event.input.type == InputTypePress) { + speed_up(); + } + break; + case 2: + if(event.input.type == InputTypePress) + tama_btn_state = BTN_STATE_PRESSED; + else if(event.input.type == InputTypeRelease) + tama_btn_state = BTN_STATE_RELEASED; + + switch(sub_menu_buttons) { + case 0: // A+C + tamalib_set_button(BTN_LEFT, tama_btn_state); + tamalib_set_button(BTN_RIGHT, tama_btn_state); + break; + case 1: // A + tamalib_set_button(BTN_LEFT, tama_btn_state); + break; + case 2: // B + tamalib_set_button(BTN_MIDDLE, tama_btn_state); + break; + case 3: // C + tamalib_set_button(BTN_RIGHT, tama_btn_state); + break; + default: + break; + } + break; + case menu_items - 1: + default: + if(event.input.type == InputTypePress) { + switch(sub_menu_last) { + case 0: // close menu + in_menu = false; + break; + case 1: // Save + if(speed != 1) { + uint8_t temp = speed; + speed = 1; + tamalib_set_speed(speed); + furi_timer_stop(timer); + tama_p1_save_state(); + furi_timer_start( + timer, furi_kernel_get_tick_frequency() / 30); + speed = temp; + tamalib_set_speed(speed); + } else { + furi_timer_stop(timer); + tama_p1_save_state(); + furi_timer_start( + timer, furi_kernel_get_tick_frequency() / 30); + } + break; + case 2: // Save & Exit + if(speed != 1) { + speed = 1; + tamalib_set_speed(speed); + } + furi_timer_stop(timer); + tama_p1_save_state(); + running = false; + break; + default: + break; + } + } + break; + } + } + } else { // out of menu // TODO: clean up code -.- + if(event.input.key == InputKeyBack && event.input.type == InputTypeLong) { + if(speed != 1) { + speed = 1; + tamalib_set_speed(speed); + } + furi_timer_stop(timer); tama_p1_save_state(); + running = false; + } else if( + event.input.type == InputTypePress || + event.input.type == InputTypeRelease) { + if(event.input.key != InputKeyBack && event.input.key != m) { + if(event.input.type == InputTypePress) + tama_btn_state = BTN_STATE_PRESSED; + else if(event.input.type == InputTypeRelease) + tama_btn_state = BTN_STATE_RELEASED; + } else { + tama_btn_state = BTN_STATE_RELEASED; + } + if(event.input.key == m && event.input.type == InputTypePress) { + in_menu = true; + } else if(event.input.key == a) { + tamalib_set_button(BTN_LEFT, tama_btn_state); + } else if(event.input.key == b) { + tamalib_set_button(BTN_MIDDLE, tama_btn_state); + } else if(event.input.key == c) { + tamalib_set_button(BTN_RIGHT, tama_btn_state); + } else if(event.input.key == InputKeyOk) { + tamalib_set_button(BTN_MIDDLE, tama_btn_state); + } } } - - if(event.input.key == InputKeyBack && event.input.type == InputTypeLong) { - furi_timer_stop(timer); - running = false; - - tama_p1_save_state(); - } } - furi_mutex_release(g_state_mutex); - } else { - // Timeout - // FURI_LOG_D(TAG, "Timed out"); } + // else { + // // Timeout + // FURI_LOG_D(TAG, "Timed out"); + // } } - if(ctx->rom != NULL) { furi_thread_flags_set(furi_thread_get_id(ctx->thread), 1); furi_thread_join(ctx->thread); } - furi_timer_free(timer); view_port_enabled_set(view_port, false); gui_remove_view_port(gui, view_port); @@ -520,6 +1182,5 @@ int32_t tama_p1_app(void* p) { furi_mutex_free(g_state_mutex); tama_p1_deinit(ctx); free(ctx); - return 0; } diff --git a/applications/plugins/tanksgame/tanks_game.c b/applications/plugins/tanksgame/tanks_game.c index 3d67dfbb0..14ea1ce52 100644 --- a/applications/plugins/tanksgame/tanks_game.c +++ b/applications/plugins/tanksgame/tanks_game.c @@ -105,6 +105,7 @@ typedef struct { uint8_t sent; PlayerState* p1; PlayerState* p2; + FuriMutex* mutex; } TanksState; typedef enum { @@ -388,10 +389,9 @@ void tanks_game_deserialize_and_render(unsigned char* data, Canvas* const canvas } static void tanks_game_render_callback(Canvas* const canvas, void* ctx) { - const TanksState* tanks_state = acquire_mutex((ValueMutex*)ctx, 25); - if(tanks_state == NULL) { - return; - } + furi_assert(ctx); + const TanksState* tanks_state = ctx; + furi_mutex_acquire(tanks_state->mutex, FuriWaitForever); // Before the function is called, the state is set with the canvas_reset(canvas) if(tanks_state->state == GameStateMenu) { @@ -415,7 +415,7 @@ static void tanks_game_render_callback(Canvas* const canvas, void* ctx) { canvas_draw_frame(canvas, 0, 0, 128, 64); - release_mutex((ValueMutex*)ctx, tanks_state); + furi_mutex_release(tanks_state->mutex); return; } @@ -432,7 +432,7 @@ static void tanks_game_render_callback(Canvas* const canvas, void* ctx) { tanks_game_render_constant_cells(canvas); - release_mutex((ValueMutex*)ctx, tanks_state); + furi_mutex_release(tanks_state->mutex); return; } @@ -615,7 +615,7 @@ static void tanks_game_render_callback(Canvas* const canvas, void* ctx) { tanks_game_deserialize_and_render(data, canvas); // TEST enf - release_mutex((ValueMutex*)ctx, tanks_state); + furi_mutex_release(tanks_state->mutex); } static void tanks_game_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { @@ -1188,8 +1188,8 @@ int32_t tanks_game_app(void* p) { tanks_state->state = GameStateMenu; tanks_state->menu_state = MenuStateSingleMode; - ValueMutex state_mutex; - if(!init_mutex(&state_mutex, tanks_state, sizeof(TanksState))) { + tanks_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!tanks_state->mutex) { FURI_LOG_E("Tanks", "cannot create mutex\r\n"); furi_message_queue_free(event_queue); free(tanks_state); @@ -1197,7 +1197,7 @@ int32_t tanks_game_app(void* p) { } ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, tanks_game_render_callback, &state_mutex); + view_port_draw_callback_set(view_port, tanks_game_render_callback, tanks_state); view_port_input_callback_set(view_port, tanks_game_input_callback, event_queue); FuriTimer* timer = @@ -1221,7 +1221,7 @@ int32_t tanks_game_app(void* p) { for(bool processing = true; processing;) { FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); - TanksState* tanks_state = (TanksState*)acquire_mutex_block(&state_mutex); + furi_mutex_acquire(tanks_state->mutex, FuriWaitForever); if(event_status == FuriStatusOk) { // press events @@ -1422,7 +1422,7 @@ int32_t tanks_game_app(void* p) { } view_port_update(view_port); - release_mutex(&state_mutex, tanks_state); + furi_mutex_release(tanks_state->mutex); furi_delay_ms(1); } @@ -1440,7 +1440,7 @@ int32_t tanks_game_app(void* p) { furi_record_close(RECORD_GUI); view_port_free(view_port); furi_message_queue_free(event_queue); - delete_mutex(&state_mutex); + furi_mutex_free(tanks_state->mutex); if(tanks_state->p1 != NULL) { free(tanks_state->p1); diff --git a/applications/plugins/tetris_game/tetris_game.c b/applications/plugins/tetris_game/tetris_game.c index a4a17d348..addfbaad5 100644 --- a/applications/plugins/tetris_game/tetris_game.c +++ b/applications/plugins/tetris_game/tetris_game.c @@ -6,7 +6,6 @@ #include #include #include -#include #define BORDER_OFFSET 1 #define MARGIN_OFFSET 3 @@ -16,6 +15,9 @@ #define FIELD_WIDTH 11 #define FIELD_HEIGHT 24 +#define MAX_FALL_SPEED 500 +#define MIN_FALL_SPEED 100 + typedef struct Point { // Also used for offset data, which is sometimes negative int8_t x, y; @@ -68,6 +70,7 @@ typedef struct { uint16_t fallSpeed; GameState gameState; FuriTimer* timer; + FuriMutex* mutex; } TetrisState; typedef enum { @@ -124,11 +127,9 @@ static void tetris_game_draw_playfield(Canvas* const canvas, const TetrisState* } static void tetris_game_render_callback(Canvas* const canvas, void* ctx) { - const TetrisState* tetris_state = acquire_mutex((ValueMutex*)ctx, 25); - if(tetris_state == NULL) { - FURI_LOG_E("TetrisGame", "it null"); - return; - } + furi_assert(ctx); + const TetrisState* tetris_state = ctx; + furi_mutex_acquire(tetris_state->mutex, FuriWaitForever); tetris_game_draw_border(canvas); tetris_game_draw_playfield(canvas, tetris_state); @@ -151,16 +152,12 @@ static void tetris_game_render_callback(Canvas* const canvas, void* ctx) { canvas_set_font(canvas, FontPrimary); canvas_draw_str(canvas, 4, 63, "Game Over"); - if(tetris_state->numLines % 8 == 0 && tetris_state->numLines != 0) { - DOLPHIN_DEED(getRandomDeed()); - } - char buffer[13]; snprintf(buffer, sizeof(buffer), "Lines: %u", tetris_state->numLines); canvas_set_font(canvas, FontSecondary); canvas_draw_str_aligned(canvas, 32, 73, AlignCenter, AlignBottom, buffer); } - release_mutex((ValueMutex*)ctx, tetris_state); + furi_mutex_release(tetris_state->mutex); } static void tetris_game_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { @@ -173,7 +170,7 @@ static void tetris_game_input_callback(InputEvent* input_event, FuriMessageQueue static void tetris_game_init_state(TetrisState* tetris_state) { tetris_state->gameState = GameStatePlaying; tetris_state->numLines = 0; - tetris_state->fallSpeed = 500; + tetris_state->fallSpeed = MAX_FALL_SPEED; memset(tetris_state->playField, 0, sizeof(tetris_state->playField)); memcpy(&tetris_state->currPiece, &shapes[rand() % 7], sizeof(tetris_state->currPiece)); @@ -307,6 +304,7 @@ static void tetris_game_render_curr_piece(tetris_state); uint8_t numLines = 0; uint8_t lines[] = {0, 0, 0, 0}; + uint16_t nextFallSpeed; tetris_game_check_for_lines(tetris_state, lines, &numLines); if(numLines > 0) { @@ -327,7 +325,11 @@ static void uint16_t oldNumLines = tetris_state->numLines; tetris_state->numLines += numLines; if((oldNumLines / 10) % 10 != (tetris_state->numLines / 10) % 10) { - tetris_state->fallSpeed -= 50; + nextFallSpeed = + tetris_state->fallSpeed - (100 / (tetris_state->numLines / 10)); + if(nextFallSpeed >= MIN_FALL_SPEED) { + tetris_state->fallSpeed = nextFallSpeed; + } } } @@ -354,8 +356,8 @@ int32_t tetris_game_app() { TetrisState* tetris_state = malloc(sizeof(TetrisState)); - ValueMutex state_mutex; - if(!init_mutex(&state_mutex, tetris_state, sizeof(TetrisState))) { + tetris_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!tetris_state->mutex) { FURI_LOG_E("TetrisGame", "cannot create mutex\r\n"); furi_message_queue_free(event_queue); free(tetris_state); @@ -372,7 +374,7 @@ int32_t tetris_game_app() { ViewPort* view_port = view_port_alloc(); view_port_set_orientation(view_port, ViewPortOrientationVertical); - view_port_draw_callback_set(view_port, tetris_game_render_callback, &state_mutex); + view_port_draw_callback_set(view_port, tetris_game_render_callback, tetris_state); view_port_input_callback_set(view_port, tetris_game_input_callback, event_queue); // Open GUI and register view_port @@ -392,7 +394,7 @@ int32_t tetris_game_app() { // This 10U implicitly sets the game loop speed. downRepeatCounter relies on this value FuriStatus event_status = furi_message_queue_get(event_queue, &event, 10U); - TetrisState* tetris_state = (TetrisState*)acquire_mutex_block(&state_mutex); + furi_mutex_acquire(tetris_state->mutex, FuriWaitForever); memcpy(newPiece, &tetris_state->currPiece, sizeof(tetris_state->currPiece)); bool wasDownMove = false; @@ -459,7 +461,7 @@ int32_t tetris_game_app() { tetris_game_process_step(tetris_state, newPiece, wasDownMove); view_port_update(view_port); - release_mutex(&state_mutex, tetris_state); + furi_mutex_release(tetris_state->mutex); } furi_timer_free(tetris_state->timer); @@ -468,10 +470,10 @@ int32_t tetris_game_app() { furi_record_close(RECORD_GUI); view_port_free(view_port); furi_message_queue_free(event_queue); - delete_mutex(&state_mutex); + furi_mutex_free(tetris_state->mutex); vTaskPrioritySet(timer_task, origTimerPrio); free(newPiece); free(tetris_state); return 0; -} \ No newline at end of file +} diff --git a/applications/plugins/text_viewer/application.fam b/applications/plugins/text_viewer/application.fam index 4f5182edf..dcd573c9d 100644 --- a/applications/plugins/text_viewer/application.fam +++ b/applications/plugins/text_viewer/application.fam @@ -2,7 +2,7 @@ App( appid="text_viewer", name="Text Viewer", apptype=FlipperAppType.EXTERNAL, - entry_point="hex_viewer_app", + entry_point="text_viewer_app", cdefines=["APP_TEXT_VIEWER"], requires=[ "gui", @@ -10,7 +10,7 @@ App( ], stack_size=2 * 1024, order=20, - fap_icon="icons/hex_10px.png", + fap_icon="icons/text_10px.png", fap_category="Misc", fap_icon_assets="icons", ) diff --git a/applications/plugins/text_viewer/icons/hex_10px.png b/applications/plugins/text_viewer/icons/hex_10px.png deleted file mode 100644 index 582e288c6..000000000 Binary files a/applications/plugins/text_viewer/icons/hex_10px.png and /dev/null differ diff --git a/applications/plugins/text_viewer/icons/text_10px.png b/applications/plugins/text_viewer/icons/text_10px.png new file mode 100644 index 000000000..8e8a6183d Binary files /dev/null and b/applications/plugins/text_viewer/icons/text_10px.png differ diff --git a/applications/plugins/text_viewer/hex_viewer.c b/applications/plugins/text_viewer/text_viewer.c similarity index 52% rename from applications/plugins/text_viewer/hex_viewer.c rename to applications/plugins/text_viewer/text_viewer.c index 3830a6602..59923adb9 100644 --- a/applications/plugins/text_viewer/hex_viewer.c +++ b/applications/plugins/text_viewer/text_viewer.c @@ -13,24 +13,24 @@ #define TAG "TextViewer" -#define HEX_VIEWER_APP_PATH_FOLDER "/any" -#define HEX_VIEWER_APP_EXTENSION "*" +#define TEXT_VIEWER_APP_PATH_FOLDER ANY_PATH("") +#define TEXT_VIEWER_APP_EXTENSION "*" -#define HEX_VIEWER_BYTES_PER_LINE 20u -#define HEX_VIEWER_LINES_ON_SCREEN 5u -#define HEX_VIEWER_BUF_SIZE (HEX_VIEWER_LINES_ON_SCREEN * HEX_VIEWER_BYTES_PER_LINE) +#define TEXT_VIEWER_BYTES_PER_LINE 20u +#define TEXT_VIEWER_LINES_ON_SCREEN 5u +#define TEXT_VIEWER_BUF_SIZE (TEXT_VIEWER_LINES_ON_SCREEN * TEXT_VIEWER_BYTES_PER_LINE) typedef struct { - uint8_t file_bytes[HEX_VIEWER_LINES_ON_SCREEN][HEX_VIEWER_BYTES_PER_LINE]; + uint8_t file_bytes[TEXT_VIEWER_LINES_ON_SCREEN][TEXT_VIEWER_BYTES_PER_LINE]; uint32_t file_offset; uint32_t file_read_bytes; uint32_t file_size; Stream* stream; bool mode; // Print address or content -} HexViewerModel; +} TextViewerModel; typedef struct { - HexViewerModel* model; + TextViewerModel* model; FuriMutex** mutex; FuriMessageQueue* input_queue; @@ -38,56 +38,56 @@ typedef struct { ViewPort* view_port; Gui* gui; Storage* storage; -} HexViewer; +} TextViewer; static void render_callback(Canvas* canvas, void* ctx) { - HexViewer* hex_viewer = ctx; - furi_check(furi_mutex_acquire(hex_viewer->mutex, FuriWaitForever) == FuriStatusOk); + TextViewer* text_viewer = ctx; + furi_check(furi_mutex_acquire(text_viewer->mutex, FuriWaitForever) == FuriStatusOk); canvas_clear(canvas); canvas_set_color(canvas, ColorBlack); - //elements_button_left(canvas, hex_viewer->model->mode ? "Addr" : "Text"); - hex_viewer->model->mode = 1; //text mode + //elements_button_left(canvas, text_viewer->model->mode ? "Addr" : "Text"); + text_viewer->model->mode = 1; //text mode //elements_button_right(canvas, "Info"); int ROW_HEIGHT = 12; int TOP_OFFSET = 10; int LEFT_OFFSET = 3; - uint32_t line_count = hex_viewer->model->file_size / HEX_VIEWER_BYTES_PER_LINE; - if(hex_viewer->model->file_size % HEX_VIEWER_BYTES_PER_LINE != 0) line_count += 1; - uint32_t first_line_on_screen = hex_viewer->model->file_offset / HEX_VIEWER_BYTES_PER_LINE; - if(line_count > HEX_VIEWER_LINES_ON_SCREEN) { + uint32_t line_count = text_viewer->model->file_size / TEXT_VIEWER_BYTES_PER_LINE; + if(text_viewer->model->file_size % TEXT_VIEWER_BYTES_PER_LINE != 0) line_count += 1; + uint32_t first_line_on_screen = text_viewer->model->file_offset / TEXT_VIEWER_BYTES_PER_LINE; + if(line_count > TEXT_VIEWER_LINES_ON_SCREEN) { uint8_t width = canvas_width(canvas); elements_scrollbar_pos( canvas, width, 0, - ROW_HEIGHT * HEX_VIEWER_LINES_ON_SCREEN, + ROW_HEIGHT * TEXT_VIEWER_LINES_ON_SCREEN, first_line_on_screen, // TODO - line_count - (HEX_VIEWER_LINES_ON_SCREEN - 1)); + line_count - (TEXT_VIEWER_LINES_ON_SCREEN - 1)); } char temp_buf[32]; - uint32_t row_iters = hex_viewer->model->file_read_bytes / HEX_VIEWER_BYTES_PER_LINE; - if(hex_viewer->model->file_read_bytes % HEX_VIEWER_BYTES_PER_LINE != 0) row_iters += 1; + uint32_t row_iters = text_viewer->model->file_read_bytes / TEXT_VIEWER_BYTES_PER_LINE; + if(text_viewer->model->file_read_bytes % TEXT_VIEWER_BYTES_PER_LINE != 0) row_iters += 1; for(uint32_t i = 0; i < row_iters; ++i) { uint32_t bytes_left_per_row = - hex_viewer->model->file_read_bytes - i * HEX_VIEWER_BYTES_PER_LINE; - bytes_left_per_row = MIN(bytes_left_per_row, HEX_VIEWER_BYTES_PER_LINE); + text_viewer->model->file_read_bytes - i * TEXT_VIEWER_BYTES_PER_LINE; + bytes_left_per_row = MIN(bytes_left_per_row, TEXT_VIEWER_BYTES_PER_LINE); - if(hex_viewer->model->mode) { - memcpy(temp_buf, hex_viewer->model->file_bytes[i], bytes_left_per_row); + if(text_viewer->model->mode) { + memcpy(temp_buf, text_viewer->model->file_bytes[i], bytes_left_per_row); temp_buf[bytes_left_per_row] = '\0'; for(uint32_t j = 0; j < bytes_left_per_row; ++j) - if(!isprint((int)temp_buf[j])) temp_buf[j] = '.'; + if(!isprint((int)temp_buf[j])) temp_buf[j] = ' '; canvas_set_font(canvas, FontKeyboard); canvas_draw_str(canvas, LEFT_OFFSET, TOP_OFFSET + i * ROW_HEIGHT, temp_buf); } else { - uint32_t addr = hex_viewer->model->file_offset + i * HEX_VIEWER_BYTES_PER_LINE; + uint32_t addr = text_viewer->model->file_offset + i * TEXT_VIEWER_BYTES_PER_LINE; snprintf(temp_buf, 32, "%04lX", addr); canvas_set_font(canvas, FontKeyboard); @@ -95,21 +95,21 @@ static void render_callback(Canvas* canvas, void* ctx) { } } - furi_mutex_release(hex_viewer->mutex); + furi_mutex_release(text_viewer->mutex); } static void input_callback(InputEvent* input_event, void* ctx) { - HexViewer* hex_viewer = ctx; + TextViewer* text_viewer = ctx; if(input_event->type == InputTypeShort || input_event->type == InputTypeRepeat) { - furi_message_queue_put(hex_viewer->input_queue, input_event, 0); + furi_message_queue_put(text_viewer->input_queue, input_event, 0); } } -static HexViewer* hex_viewer_alloc() { - HexViewer* instance = malloc(sizeof(HexViewer)); +static TextViewer* text_viewer_alloc() { + TextViewer* instance = malloc(sizeof(TextViewer)); - instance->model = malloc(sizeof(HexViewerModel)); - memset(instance->model, 0x0, sizeof(HexViewerModel)); + instance->model = malloc(sizeof(TextViewerModel)); + memset(instance->model, 0x0, sizeof(TextViewerModel)); instance->mutex = furi_mutex_alloc(FuriMutexTypeNormal); @@ -127,7 +127,7 @@ static HexViewer* hex_viewer_alloc() { return instance; } -static void hex_viewer_free(HexViewer* instance) { +static void text_viewer_free(TextViewer* instance) { furi_record_close(RECORD_STORAGE); gui_remove_view_port(instance->gui, instance->view_port); @@ -144,54 +144,54 @@ static void hex_viewer_free(HexViewer* instance) { free(instance); } -static bool hex_viewer_open_file(HexViewer* hex_viewer, const char* file_path) { - furi_assert(hex_viewer); +static bool text_viewer_open_file(TextViewer* text_viewer, const char* file_path) { + furi_assert(text_viewer); furi_assert(file_path); - hex_viewer->model->stream = buffered_file_stream_alloc(hex_viewer->storage); + text_viewer->model->stream = buffered_file_stream_alloc(text_viewer->storage); bool isOk = true; do { if(!buffered_file_stream_open( - hex_viewer->model->stream, file_path, FSAM_READ, FSOM_OPEN_EXISTING)) { + text_viewer->model->stream, file_path, FSAM_READ, FSOM_OPEN_EXISTING)) { FURI_LOG_E(TAG, "Unable to open stream: %s", file_path); isOk = false; break; }; - hex_viewer->model->file_size = stream_size(hex_viewer->model->stream); + text_viewer->model->file_size = stream_size(text_viewer->model->stream); } while(false); return isOk; } -static bool hex_viewer_read_file(HexViewer* hex_viewer) { - furi_assert(hex_viewer); - furi_assert(hex_viewer->model->stream); - furi_assert(hex_viewer->model->file_offset % HEX_VIEWER_BYTES_PER_LINE == 0); +static bool text_viewer_read_file(TextViewer* text_viewer) { + furi_assert(text_viewer); + furi_assert(text_viewer->model->stream); + furi_assert(text_viewer->model->file_offset % TEXT_VIEWER_BYTES_PER_LINE == 0); - memset(hex_viewer->model->file_bytes, 0x0, HEX_VIEWER_BUF_SIZE); + memset(text_viewer->model->file_bytes, 0x0, TEXT_VIEWER_BUF_SIZE); bool isOk = true; do { - uint32_t offset = hex_viewer->model->file_offset; - if(!stream_seek(hex_viewer->model->stream, offset, true)) { + uint32_t offset = text_viewer->model->file_offset; + if(!stream_seek(text_viewer->model->stream, offset, true)) { FURI_LOG_E(TAG, "Unable to seek stream"); isOk = false; break; } - hex_viewer->model->file_read_bytes = stream_read( - hex_viewer->model->stream, - (uint8_t*)hex_viewer->model->file_bytes, - HEX_VIEWER_BUF_SIZE); + text_viewer->model->file_read_bytes = stream_read( + text_viewer->model->stream, + (uint8_t*)text_viewer->model->file_bytes, + TEXT_VIEWER_BUF_SIZE); } while(false); return isOk; } -int32_t hex_viewer_app(void* p) { - HexViewer* hex_viewer = hex_viewer_alloc(); +int32_t text_viewer_app(void* p) { + TextViewer* text_viewer = text_viewer_alloc(); FuriString* file_path; file_path = furi_string_alloc(); @@ -200,11 +200,11 @@ int32_t hex_viewer_app(void* p) { if(p && strlen(p)) { furi_string_set(file_path, (const char*)p); } else { - furi_string_set(file_path, HEX_VIEWER_APP_PATH_FOLDER); + furi_string_set(file_path, TEXT_VIEWER_APP_PATH_FOLDER); DialogsFileBrowserOptions browser_options; dialog_file_browser_set_basic_options( - &browser_options, HEX_VIEWER_APP_EXTENSION, &I_hex_10px); + &browser_options, TEXT_VIEWER_APP_EXTENSION, &I_text_10px); browser_options.hide_ext = false; DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); @@ -219,35 +219,38 @@ int32_t hex_viewer_app(void* p) { FURI_LOG_I(TAG, "File selected: %s", furi_string_get_cstr(file_path)); - if(!hex_viewer_open_file(hex_viewer, furi_string_get_cstr(file_path))) break; - hex_viewer_read_file(hex_viewer); + if(!text_viewer_open_file(text_viewer, furi_string_get_cstr(file_path))) break; + text_viewer_read_file(text_viewer); InputEvent input; - while(furi_message_queue_get(hex_viewer->input_queue, &input, FuriWaitForever) == + while(furi_message_queue_get(text_viewer->input_queue, &input, FuriWaitForever) == FuriStatusOk) { if(input.key == InputKeyBack) { break; } else if(input.key == InputKeyUp) { - furi_check(furi_mutex_acquire(hex_viewer->mutex, FuriWaitForever) == FuriStatusOk); - if(hex_viewer->model->file_offset > 0) { - hex_viewer->model->file_offset -= HEX_VIEWER_BYTES_PER_LINE; - if(!hex_viewer_read_file(hex_viewer)) break; + furi_check( + furi_mutex_acquire(text_viewer->mutex, FuriWaitForever) == FuriStatusOk); + if(text_viewer->model->file_offset > 0) { + text_viewer->model->file_offset -= TEXT_VIEWER_BYTES_PER_LINE; + if(!text_viewer_read_file(text_viewer)) break; } - furi_mutex_release(hex_viewer->mutex); + furi_mutex_release(text_viewer->mutex); } else if(input.key == InputKeyDown) { - furi_check(furi_mutex_acquire(hex_viewer->mutex, FuriWaitForever) == FuriStatusOk); + furi_check( + furi_mutex_acquire(text_viewer->mutex, FuriWaitForever) == FuriStatusOk); uint32_t last_byte_on_screen = - hex_viewer->model->file_offset + hex_viewer->model->file_read_bytes; + text_viewer->model->file_offset + text_viewer->model->file_read_bytes; - if(hex_viewer->model->file_size > last_byte_on_screen) { - hex_viewer->model->file_offset += HEX_VIEWER_BYTES_PER_LINE; - if(!hex_viewer_read_file(hex_viewer)) break; + if(text_viewer->model->file_size > last_byte_on_screen) { + text_viewer->model->file_offset += TEXT_VIEWER_BYTES_PER_LINE; + if(!text_viewer_read_file(text_viewer)) break; } - furi_mutex_release(hex_viewer->mutex); + furi_mutex_release(text_viewer->mutex); } else if(input.key == InputKeyLeft) { - furi_check(furi_mutex_acquire(hex_viewer->mutex, FuriWaitForever) == FuriStatusOk); - hex_viewer->model->mode = !hex_viewer->model->mode; - furi_mutex_release(hex_viewer->mutex); + furi_check( + furi_mutex_acquire(text_viewer->mutex, FuriWaitForever) == FuriStatusOk); + text_viewer->model->mode = !text_viewer->model->mode; + furi_mutex_release(text_viewer->mutex); } else if(input.key == InputKeyRight) { FuriString* buffer; buffer = furi_string_alloc(); @@ -255,13 +258,13 @@ int32_t hex_viewer_app(void* p) { buffer, "File path: %s\nFile size: %lu (0x%lX)", furi_string_get_cstr(file_path), - hex_viewer->model->file_size, - hex_viewer->model->file_size); + text_viewer->model->file_size, + text_viewer->model->file_size); DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); DialogMessage* message = dialog_message_alloc(); dialog_message_set_header(message, "Text Viewer v1.1", 16, 2, AlignLeft, AlignTop); - dialog_message_set_icon(message, &I_hex_10px, 3, 2); + dialog_message_set_icon(message, &I_text_10px, 3, 2); dialog_message_set_text( message, furi_string_get_cstr(buffer), 3, 16, AlignLeft, AlignTop); dialog_message_set_buttons(message, NULL, NULL, "Back"); @@ -271,12 +274,12 @@ int32_t hex_viewer_app(void* p) { dialog_message_free(message); furi_record_close(RECORD_DIALOGS); } - view_port_update(hex_viewer->view_port); + view_port_update(text_viewer->view_port); } } while(false); furi_string_free(file_path); - hex_viewer_free(hex_viewer); + text_viewer_free(text_viewer); return 0; } \ No newline at end of file diff --git a/applications/plugins/tictactoe_game/tictactoe_game.c b/applications/plugins/tictactoe_game/tictactoe_game.c index fe57d7c62..b6c0747d6 100644 --- a/applications/plugins/tictactoe_game/tictactoe_game.c +++ b/applications/plugins/tictactoe_game/tictactoe_game.c @@ -9,6 +9,7 @@ typedef enum { EventTypeTick, EventTypeKey } EventType; typedef struct { + FuriMutex* mutex; FuriTimer* timer; uint8_t selBoxX; uint8_t selBoxY; @@ -172,10 +173,9 @@ static void tictactoe_state_init(TicTacToeState* tictactoe_state) { } static void tictactoe_draw_callback(Canvas* const canvas, void* ctx) { - TicTacToeState* ticst = acquire_mutex((ValueMutex*)ctx, 25); - if(ticst == NULL) { - return; - } + furi_assert(ctx); + TicTacToeState* ticst = ctx; + furi_mutex_acquire(ticst->mutex, FuriWaitForever); if(ticst->selX > 3) { ticst->selX = 3; @@ -284,7 +284,7 @@ static void tictactoe_draw_callback(Canvas* const canvas, void* ctx) { tictactoe_draw(canvas, ticst); - release_mutex((ValueMutex*)ctx, ticst); + furi_mutex_release(ticst->mutex); } static void tictactoe_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { @@ -307,8 +307,9 @@ int32_t tictactoe_game_app(void* p) { TicTacToeState* tictactoe_state = malloc(sizeof(TicTacToeState)); - ValueMutex state_mutex; - if(!init_mutex(&state_mutex, tictactoe_state, sizeof(TicTacToeState))) { + tictactoe_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + + if(!tictactoe_state->mutex) { FURI_LOG_E(TAG, "Cannot create mutex\r\n"); furi_message_queue_free(event_queue); free(tictactoe_state); @@ -317,7 +318,7 @@ int32_t tictactoe_game_app(void* p) { // Set system callbacks ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, tictactoe_draw_callback, &state_mutex); + view_port_draw_callback_set(view_port, tictactoe_draw_callback, tictactoe_state); view_port_input_callback_set(view_port, tictactoe_input_callback, event_queue); tictactoe_state->timer = @@ -333,7 +334,7 @@ int32_t tictactoe_game_app(void* p) { GameEvent event; for(bool processing = true; processing;) { FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); - TicTacToeState* tictactoe_state = (TicTacToeState*)acquire_mutex_block(&state_mutex); + furi_mutex_acquire(tictactoe_state->mutex, FuriWaitForever); if(event_status == FuriStatusOk) { // Key events @@ -366,7 +367,7 @@ int32_t tictactoe_game_app(void* p) { } view_port_update(view_port); - release_mutex(&state_mutex, tictactoe_state); + furi_mutex_release(tictactoe_state->mutex); } furi_timer_free(tictactoe_state->timer); @@ -375,8 +376,8 @@ int32_t tictactoe_game_app(void* p) { furi_record_close(RECORD_GUI); view_port_free(view_port); furi_message_queue_free(event_queue); - delete_mutex(&state_mutex); + furi_mutex_free(tictactoe_state->mutex); free(tictactoe_state); return 0; -} \ No newline at end of file +} diff --git a/applications/plugins/timelapse/README.md b/applications/plugins/timelapse/README.md index fe3ed171a..b10955ea1 100644 --- a/applications/plugins/timelapse/README.md +++ b/applications/plugins/timelapse/README.md @@ -5,6 +5,12 @@ english version [below](#eng) + +Blog: [theageoflove.ru](https://theageoflove.ru) + +TG: [t.me/scuko_bled](https://t.me/scuko_bled) + + ![zeitraffer for flipper zero](https://theageoflove.ru/uploads/2022/11/photo_2022-11-10_15-54-25.jpg) Видео работы: https://youtu.be/VPSpRLJXYAc diff --git a/applications/plugins/timelapse/application.fam b/applications/plugins/timelapse/application.fam index f5eaae551..ba5babc7e 100644 --- a/applications/plugins/timelapse/application.fam +++ b/applications/plugins/timelapse/application.fam @@ -1,6 +1,6 @@ App( - appid="GPIO_Intervalometer", - name="[GPIO] Intervalometer", + appid="GPIO_Timelapse", + name="[GPIO] Timelapse", apptype=FlipperAppType.EXTERNAL, entry_point="zeitraffer_app", cdefines=["APP_ZEITRAFFER"], diff --git a/applications/plugins/timelapse/zeitraffer.c b/applications/plugins/timelapse/zeitraffer.c index 4e31292c6..cbae4fa44 100644 --- a/applications/plugins/timelapse/zeitraffer.c +++ b/applications/plugins/timelapse/zeitraffer.c @@ -5,10 +5,10 @@ #include #include #include "gpio_item.h" -#include "GPIO_Intervalometer_icons.h" +#include "GPIO_Timelapse_icons.h" -#define CONFIG_FILE_DIRECTORY_PATH "/ext/apps_data/intravelometer" -#define CONFIG_FILE_PATH CONFIG_FILE_DIRECTORY_PATH "/intravelometer.conf" +#define CONFIG_FILE_DIRECTORY_PATH "/ext/apps_data/timelapse" +#define CONFIG_FILE_PATH CONFIG_FILE_DIRECTORY_PATH "/timelapse.conf" // Часть кода покрадена из https://github.com/zmactep/flipperzero-hello-world @@ -390,7 +390,7 @@ int32_t zeitraffer_app(void* p) { } if(!flipper_format_write_comment_cstr( save, - "Zeitraffer app settings: № of frames, interval time, backlight type, Delay")) { + "Zeitraffer app settings: n of frames, interval time, backlight type, Delay")) { notification_message(notifications, &sequence_error); break; } diff --git a/applications/plugins/totp/.clang-format b/applications/plugins/totp/.clang-format deleted file mode 100644 index 4b76f7fa4..000000000 --- a/applications/plugins/totp/.clang-format +++ /dev/null @@ -1,191 +0,0 @@ ---- -Language: Cpp -AccessModifierOffset: -4 -AlignAfterOpenBracket: AlwaysBreak -AlignArrayOfStructures: None -AlignConsecutiveMacros: None -AlignConsecutiveAssignments: None -AlignConsecutiveBitFields: None -AlignConsecutiveDeclarations: None -AlignEscapedNewlines: Left -AlignOperands: Align -AlignTrailingComments: false -AllowAllArgumentsOnNextLine: true -AllowAllParametersOfDeclarationOnNextLine: false -AllowShortEnumsOnASingleLine: true -AllowShortBlocksOnASingleLine: Never -AllowShortCaseLabelsOnASingleLine: false -AllowShortFunctionsOnASingleLine: None -AllowShortLambdasOnASingleLine: All -AllowShortIfStatementsOnASingleLine: WithoutElse -AllowShortLoopsOnASingleLine: true -AlwaysBreakAfterDefinitionReturnType: None -AlwaysBreakAfterReturnType: None -AlwaysBreakBeforeMultilineStrings: false -AlwaysBreakTemplateDeclarations: Yes -AttributeMacros: - - __capability -BinPackArguments: false -BinPackParameters: false -BraceWrapping: - AfterCaseLabel: false - AfterClass: false - AfterControlStatement: Never - AfterEnum: false - AfterFunction: false - AfterNamespace: false - AfterObjCDeclaration: false - AfterStruct: false - AfterUnion: false - AfterExternBlock: false - BeforeCatch: false - BeforeElse: false - BeforeLambdaBody: false - BeforeWhile: false - IndentBraces: false - SplitEmptyFunction: true - SplitEmptyRecord: true - SplitEmptyNamespace: true -BreakBeforeBinaryOperators: None -BreakBeforeConceptDeclarations: true -BreakBeforeBraces: Attach -BreakBeforeInheritanceComma: false -BreakInheritanceList: BeforeColon -BreakBeforeTernaryOperators: false -BreakConstructorInitializersBeforeComma: false -BreakConstructorInitializers: BeforeComma -BreakAfterJavaFieldAnnotations: false -BreakStringLiterals: false -ColumnLimit: 99 -CommentPragmas: '^ IWYU pragma:' -QualifierAlignment: Leave -CompactNamespaces: false -ConstructorInitializerIndentWidth: 4 -ContinuationIndentWidth: 4 -Cpp11BracedListStyle: true -DeriveLineEnding: true -DerivePointerAlignment: false -DisableFormat: false -EmptyLineAfterAccessModifier: Never -EmptyLineBeforeAccessModifier: LogicalBlock -ExperimentalAutoDetectBinPacking: false -PackConstructorInitializers: BinPack -BasedOnStyle: '' -ConstructorInitializerAllOnOneLineOrOnePerLine: false -AllowAllConstructorInitializersOnNextLine: true -FixNamespaceComments: false -ForEachMacros: - - foreach - - Q_FOREACH - - BOOST_FOREACH -IfMacros: - - KJ_IF_MAYBE -IncludeBlocks: Preserve -IncludeCategories: - - Regex: '.*' - Priority: 1 - SortPriority: 0 - CaseSensitive: false - - Regex: '^(<|"(gtest|gmock|isl|json)/)' - Priority: 3 - SortPriority: 0 - CaseSensitive: false - - Regex: '.*' - Priority: 1 - SortPriority: 0 - CaseSensitive: false -IncludeIsMainRegex: '(Test)?$' -IncludeIsMainSourceRegex: '' -IndentAccessModifiers: false -IndentCaseLabels: false -IndentCaseBlocks: false -IndentGotoLabels: true -IndentPPDirectives: None -IndentExternBlock: AfterExternBlock -IndentRequires: false -IndentWidth: 4 -IndentWrappedFunctionNames: true -InsertTrailingCommas: None -JavaScriptQuotes: Leave -JavaScriptWrapImports: true -KeepEmptyLinesAtTheStartOfBlocks: false -LambdaBodyIndentation: Signature -MacroBlockBegin: '' -MacroBlockEnd: '' -MaxEmptyLinesToKeep: 1 -NamespaceIndentation: None -ObjCBinPackProtocolList: Auto -ObjCBlockIndentWidth: 4 -ObjCBreakBeforeNestedBlockParam: true -ObjCSpaceAfterProperty: true -ObjCSpaceBeforeProtocolList: true -PenaltyBreakAssignment: 10 -PenaltyBreakBeforeFirstCallParameter: 30 -PenaltyBreakComment: 10 -PenaltyBreakFirstLessLess: 0 -PenaltyBreakOpenParenthesis: 0 -PenaltyBreakString: 10 -PenaltyBreakTemplateDeclaration: 10 -PenaltyExcessCharacter: 100 -PenaltyReturnTypeOnItsOwnLine: 60 -PenaltyIndentedWhitespace: 0 -PointerAlignment: Left -PPIndentWidth: -1 -ReferenceAlignment: Pointer -ReflowComments: false -RemoveBracesLLVM: false -SeparateDefinitionBlocks: Leave -ShortNamespaceLines: 1 -SortIncludes: Never -SortJavaStaticImport: Before -SortUsingDeclarations: false -SpaceAfterCStyleCast: false -SpaceAfterLogicalNot: false -SpaceAfterTemplateKeyword: true -SpaceBeforeAssignmentOperators: true -SpaceBeforeCaseColon: false -SpaceBeforeCpp11BracedList: false -SpaceBeforeCtorInitializerColon: true -SpaceBeforeInheritanceColon: true -SpaceBeforeParens: Never -SpaceBeforeParensOptions: - AfterControlStatements: false - AfterForeachMacros: false - AfterFunctionDefinitionName: false - AfterFunctionDeclarationName: false - AfterIfMacros: false - AfterOverloadedOperator: false - BeforeNonEmptyParentheses: false -SpaceAroundPointerQualifiers: Default -SpaceBeforeRangeBasedForLoopColon: true -SpaceInEmptyBlock: false -SpaceInEmptyParentheses: false -SpacesBeforeTrailingComments: 1 -SpacesInAngles: Never -SpacesInConditionalStatement: false -SpacesInContainerLiterals: false -SpacesInCStyleCastParentheses: false -SpacesInLineCommentPrefix: - Minimum: 1 - Maximum: -1 -SpacesInParentheses: false -SpacesInSquareBrackets: false -SpaceBeforeSquareBrackets: false -BitFieldColonSpacing: Both -Standard: c++03 -StatementAttributeLikeMacros: - - Q_EMIT -StatementMacros: - - Q_UNUSED - - QT_REQUIRE_VERSION -TabWidth: 4 -UseCRLF: false -UseTab: Never -WhitespaceSensitiveMacros: - - STRINGIZE - - PP_STRINGIZE - - BOOST_PP_STRINGIZE - - NS_SWIFT_NAME - - CF_SWIFT_NAME -... - diff --git a/applications/plugins/totp/LICENSE b/applications/plugins/totp/LICENSE new file mode 100644 index 000000000..65504e7b1 --- /dev/null +++ b/applications/plugins/totp/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Alexander Kopachov + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/applications/plugins/totp/application.fam b/applications/plugins/totp/application.fam index 81b0e22c3..f6a61f119 100644 --- a/applications/plugins/totp/application.fam +++ b/applications/plugins/totp/application.fam @@ -1,5 +1,5 @@ App( - appid="Authenticator", + appid="totp", name="Authenticator", apptype=FlipperAppType.EXTERNAL, entry_point="totp_app", diff --git a/applications/plugins/totp/cli/cli.c b/applications/plugins/totp/cli/cli.c index 8f4708f4d..28d173766 100644 --- a/applications/plugins/totp/cli/cli.c +++ b/applications/plugins/totp/cli/cli.c @@ -14,7 +14,7 @@ #include "commands/reset/reset.h" static void totp_cli_print_unknown_command(const FuriString* unknown_command) { - TOTP_CLI_PRINTF( + TOTP_CLI_PRINTF_ERROR( "Command \"%s\" is unknown. Use \"" TOTP_CLI_COMMAND_HELP "\" command to get list of available commands.", furi_string_get_cstr(unknown_command)); diff --git a/applications/plugins/totp/cli/cli_helpers.h b/applications/plugins/totp/cli/cli_helpers.h index 5027d7027..a35a2e599 100644 --- a/applications/plugins/totp/cli/cli_helpers.h +++ b/applications/plugins/totp/cli/cli_helpers.h @@ -22,6 +22,31 @@ _Pragma(STRINGIFY(GCC diagnostic pop)) \ } while(false) +#define TOTP_CLI_PRINTF_COLORFUL(color, format, ...) \ + do { \ + _Pragma(STRINGIFY(GCC diagnostic push)) \ + _Pragma(STRINGIFY(GCC diagnostic ignored "-Wdouble-promotion")) \ + printf("\e[%s", color); \ + printf(format, ##__VA_ARGS__); \ + printf("\e[0m"); \ + fflush(stdout); \ + _Pragma(STRINGIFY(GCC diagnostic pop)) \ + } while(false) + +#define TOTP_CLI_COLOR_ERROR "91m" +#define TOTP_CLI_COLOR_WARNING "93m" +#define TOTP_CLI_COLOR_SUCCESS "92m" +#define TOTP_CLI_COLOR_INFO "96m" + +#define TOTP_CLI_PRINTF_ERROR(format, ...) \ + TOTP_CLI_PRINTF_COLORFUL(TOTP_CLI_COLOR_ERROR, format, ##__VA_ARGS__) +#define TOTP_CLI_PRINTF_WARNING(format, ...) \ + TOTP_CLI_PRINTF_COLORFUL(TOTP_CLI_COLOR_WARNING, format, ##__VA_ARGS__) +#define TOTP_CLI_PRINTF_SUCCESS(format, ...) \ + TOTP_CLI_PRINTF_COLORFUL(TOTP_CLI_COLOR_SUCCESS, format, ##__VA_ARGS__) +#define TOTP_CLI_PRINTF_INFO(format, ...) \ + TOTP_CLI_PRINTF_COLORFUL(TOTP_CLI_COLOR_INFO, format, ##__VA_ARGS__) + #define TOTP_CLI_DELETE_LAST_LINE() \ TOTP_CLI_PRINTF("\033[A\33[2K\r"); \ fflush(stdout) @@ -35,11 +60,11 @@ fflush(stdout) #define TOTP_CLI_PRINT_INVALID_ARGUMENTS() \ - TOTP_CLI_PRINTF( \ + TOTP_CLI_PRINTF_ERROR( \ "Invalid command arguments. use \"help\" command to get list of available commands") #define TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE() \ - TOTP_CLI_PRINTF("An error has occurred during updating config file\r\n") + TOTP_CLI_PRINTF_ERROR("An error has occurred during updating config file\r\n") /** * @brief Checks whether user is authenticated and entered correct PIN. diff --git a/applications/plugins/totp/cli/commands/add/add.c b/applications/plugins/totp/cli/commands/add/add.c index 2183d5277..fbcd58530 100644 --- a/applications/plugins/totp/cli/commands/add/add.c +++ b/applications/plugins/totp/cli/commands/add/add.c @@ -115,10 +115,10 @@ void totp_cli_command_add_handle(PluginState* plugin_state, FuriString* args, Cl bool parsed = false; if(furi_string_cmpi_str(temp_str, TOTP_CLI_COMMAND_ADD_ARG_ALGO_PREFIX) == 0) { if(!args_read_string_and_trim(args, temp_str)) { - TOTP_CLI_PRINTF("Missed value for argument \"" TOTP_CLI_COMMAND_ADD_ARG_ALGO_PREFIX - "\"\r\n"); + TOTP_CLI_PRINTF_ERROR( + "Missed value for argument \"" TOTP_CLI_COMMAND_ADD_ARG_ALGO_PREFIX "\"\r\n"); } else if(!token_info_set_algo_from_str(token_info, temp_str)) { - TOTP_CLI_PRINTF( + TOTP_CLI_PRINTF_ERROR( "\"%s\" is incorrect value for argument \"" TOTP_CLI_COMMAND_ADD_ARG_ALGO_PREFIX "\"\r\n", furi_string_get_cstr(temp_str)); @@ -128,11 +128,11 @@ void totp_cli_command_add_handle(PluginState* plugin_state, FuriString* args, Cl } else if(furi_string_cmpi_str(temp_str, TOTP_CLI_COMMAND_ADD_ARG_DIGITS_PREFIX) == 0) { uint8_t digit_value; if(!args_read_uint8_and_trim(args, &digit_value)) { - TOTP_CLI_PRINTF( + TOTP_CLI_PRINTF_ERROR( "Missed or incorrect value for argument \"" TOTP_CLI_COMMAND_ADD_ARG_DIGITS_PREFIX "\"\r\n"); } else if(!token_info_set_digits_from_int(token_info, digit_value)) { - TOTP_CLI_PRINTF( + TOTP_CLI_PRINTF_ERROR( "\"%" PRIu8 "\" is incorrect value for argument \"" TOTP_CLI_COMMAND_ADD_ARG_DIGITS_PREFIX "\"\r\n", @@ -143,11 +143,11 @@ void totp_cli_command_add_handle(PluginState* plugin_state, FuriString* args, Cl } else if(furi_string_cmpi_str(temp_str, TOTP_CLI_COMMAND_ADD_ARG_DURATION_PREFIX) == 0) { uint8_t duration_value; if(!args_read_uint8_and_trim(args, &duration_value)) { - TOTP_CLI_PRINTF( + TOTP_CLI_PRINTF_ERROR( "Missed or incorrect value for argument \"" TOTP_CLI_COMMAND_ADD_ARG_DURATION_PREFIX "\"\r\n"); } else if(!token_info_set_duration_from_int(token_info, duration_value)) { - TOTP_CLI_PRINTF( + TOTP_CLI_PRINTF_ERROR( "\"%" PRIu8 "\" is incorrect value for argument \"" TOTP_CLI_COMMAND_ADD_ARG_DURATION_PREFIX "\"\r\n", @@ -159,7 +159,7 @@ void totp_cli_command_add_handle(PluginState* plugin_state, FuriString* args, Cl mask_user_input = false; parsed = true; } else { - TOTP_CLI_PRINTF("Unknown argument \"%s\"\r\n", furi_string_get_cstr(temp_str)); + TOTP_CLI_PRINTF_ERROR("Unknown argument \"%s\"\r\n", furi_string_get_cstr(temp_str)); } if(!parsed) { @@ -176,7 +176,7 @@ void totp_cli_command_add_handle(PluginState* plugin_state, FuriString* args, Cl if(!totp_cli_read_line(cli, temp_str, mask_user_input) || !totp_cli_ensure_authenticated(plugin_state, cli)) { TOTP_CLI_DELETE_LAST_LINE(); - TOTP_CLI_PRINTF("Cancelled by user\r\n"); + TOTP_CLI_PRINTF_INFO("Cancelled by user\r\n"); furi_string_secure_free(temp_str); token_info_free(token_info); return; @@ -189,7 +189,7 @@ void totp_cli_command_add_handle(PluginState* plugin_state, FuriString* args, Cl furi_string_get_cstr(temp_str), furi_string_size(temp_str), plugin_state->iv)) { - TOTP_CLI_PRINTF("Token secret seems to be invalid and can not be parsed\r\n"); + TOTP_CLI_PRINTF_ERROR("Token secret seems to be invalid and can not be parsed\r\n"); furi_string_secure_free(temp_str); token_info_free(token_info); return; @@ -206,7 +206,7 @@ void totp_cli_command_add_handle(PluginState* plugin_state, FuriString* args, Cl TOTP_LIST_INIT_OR_ADD(plugin_state->tokens_list, token_info, furi_check); plugin_state->tokens_count++; if(totp_config_file_save_new_token(token_info) == TotpConfigFileUpdateSuccess) { - TOTP_CLI_PRINTF("Token \"%s\" has been successfully added\r\n", token_info->name); + TOTP_CLI_PRINTF_SUCCESS("Token \"%s\" has been successfully added\r\n", token_info->name); } else { TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE(); } diff --git a/applications/plugins/totp/cli/commands/delete/delete.c b/applications/plugins/totp/cli/commands/delete/delete.c index 046341693..8fe72e220 100644 --- a/applications/plugins/totp/cli/commands/delete/delete.c +++ b/applications/plugins/totp/cli/commands/delete/delete.c @@ -64,11 +64,11 @@ void totp_cli_command_delete_handle(PluginState* plugin_state, FuriString* args, bool confirmed = !confirm_needed; if(confirm_needed) { - TOTP_CLI_PRINTF("WARNING!\r\n"); - TOTP_CLI_PRINTF( + TOTP_CLI_PRINTF_WARNING("WARNING!\r\n"); + TOTP_CLI_PRINTF_WARNING( "TOKEN \"%s\" WILL BE PERMANENTLY DELETED WITHOUT ABILITY TO RECOVER IT.\r\n", token_info->name); - TOTP_CLI_PRINTF("Confirm? [y/n]\r\n"); + TOTP_CLI_PRINTF_WARNING("Confirm? [y/n]\r\n"); fflush(stdout); char user_pick; do { @@ -94,7 +94,8 @@ void totp_cli_command_delete_handle(PluginState* plugin_state, FuriString* args, plugin_state->tokens_count--; if(totp_full_save_config_file(plugin_state) == TotpConfigFileUpdateSuccess) { - TOTP_CLI_PRINTF("Token \"%s\" has been successfully deleted\r\n", token_info->name); + TOTP_CLI_PRINTF_SUCCESS( + "Token \"%s\" has been successfully deleted\r\n", token_info->name); } else { TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE(); } @@ -105,6 +106,6 @@ void totp_cli_command_delete_handle(PluginState* plugin_state, FuriString* args, totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL); } } else { - TOTP_CLI_PRINTF("User not confirmed\r\n"); + TOTP_CLI_PRINTF_INFO("User has not confirmed\r\n"); } } \ No newline at end of file diff --git a/applications/plugins/totp/cli/commands/move/move.c b/applications/plugins/totp/cli/commands/move/move.c index 9d47134e5..1d5a23bbf 100644 --- a/applications/plugins/totp/cli/commands/move/move.c +++ b/applications/plugins/totp/cli/commands/move/move.c @@ -34,10 +34,10 @@ void totp_cli_command_move_docopt_usage() { void totp_cli_command_move_docopt_options() { TOTP_CLI_PRINTF(" " DOCOPT_OPTION( TOTP_CLI_COMMAND_MOVE_ARG_NEW_NAME_PREFIX, - DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_MOVE_ARG_NEW_NAME)) " New token name.\r\n"); + DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_MOVE_ARG_NEW_NAME)) " New token name\r\n"); TOTP_CLI_PRINTF(" " DOCOPT_OPTION( TOTP_CLI_COMMAND_MOVE_ARG_NEW_INDEX_PREFIX, - DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_MOVE_ARG_NEW_INDEX)) " New token index.\r\n"); + DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_MOVE_ARG_NEW_INDEX)) " New token index\r\n"); } void totp_cli_command_move_handle(PluginState* plugin_state, FuriString* args, Cli* cli) { @@ -65,7 +65,7 @@ void totp_cli_command_move_handle(PluginState* plugin_state, FuriString* args, C bool parsed = false; if(furi_string_cmpi_str(temp_str, TOTP_CLI_COMMAND_MOVE_ARG_NEW_NAME_PREFIX) == 0) { if(!args_read_string_and_trim(args, temp_str)) { - TOTP_CLI_PRINTF( + TOTP_CLI_PRINTF_ERROR( "Missed value for argument \"" TOTP_CLI_COMMAND_MOVE_ARG_NEW_NAME_PREFIX "\"\r\n"); } else { @@ -87,11 +87,11 @@ void totp_cli_command_move_handle(PluginState* plugin_state, FuriString* args, C } } else if(furi_string_cmpi_str(temp_str, TOTP_CLI_COMMAND_MOVE_ARG_NEW_INDEX_PREFIX) == 0) { if(!args_read_int_and_trim(args, &new_token_index)) { - TOTP_CLI_PRINTF( + TOTP_CLI_PRINTF_ERROR( "Missed value for argument \"" TOTP_CLI_COMMAND_MOVE_ARG_NEW_INDEX_PREFIX "\"\r\n"); } else if(new_token_index < 1 || new_token_index > plugin_state->tokens_count) { - TOTP_CLI_PRINTF( + TOTP_CLI_PRINTF_ERROR( "\"%" PRId16 "\" is incorrect value for argument \"" TOTP_CLI_COMMAND_MOVE_ARG_NEW_INDEX_PREFIX "\"\r\n", @@ -100,7 +100,7 @@ void totp_cli_command_move_handle(PluginState* plugin_state, FuriString* args, C parsed = true; } } else { - TOTP_CLI_PRINTF("Unknown argument \"%s\"\r\n", furi_string_get_cstr(temp_str)); + TOTP_CLI_PRINTF_ERROR("Unknown argument \"%s\"\r\n", furi_string_get_cstr(temp_str)); } if(!parsed) { @@ -148,7 +148,8 @@ void totp_cli_command_move_handle(PluginState* plugin_state, FuriString* args, C if(token_updated) { if(totp_full_save_config_file(plugin_state) == TotpConfigFileUpdateSuccess) { - TOTP_CLI_PRINTF("Token \"%s\" has been successfully updated\r\n", token_info->name); + TOTP_CLI_PRINTF_SUCCESS( + "Token \"%s\" has been successfully updated\r\n", token_info->name); } else { TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE(); } diff --git a/applications/plugins/totp/cli/commands/notification/notification.c b/applications/plugins/totp/cli/commands/notification/notification.c index 5b98a857b..bbbd52703 100644 --- a/applications/plugins/totp/cli/commands/notification/notification.c +++ b/applications/plugins/totp/cli/commands/notification/notification.c @@ -28,21 +28,21 @@ void totp_cli_command_notification_docopt_arguments() { ", " TOTP_CLI_COMMAND_NOTIFICATION_METHOD_VIBRO "]\r\n"); } -static void totp_cli_command_notification_print_method(NotificationMethod method) { +static void totp_cli_command_notification_print_method(NotificationMethod method, char* color) { bool has_previous_method = false; if(method & NotificationMethodSound) { - TOTP_CLI_PRINTF("\"" TOTP_CLI_COMMAND_NOTIFICATION_METHOD_SOUND "\""); + TOTP_CLI_PRINTF_COLORFUL(color, "\"" TOTP_CLI_COMMAND_NOTIFICATION_METHOD_SOUND "\""); has_previous_method = true; } if(method & NotificationMethodVibro) { if(has_previous_method) { - TOTP_CLI_PRINTF(" and "); + TOTP_CLI_PRINTF_COLORFUL(color, " and "); } - TOTP_CLI_PRINTF("\"" TOTP_CLI_COMMAND_NOTIFICATION_METHOD_VIBRO "\""); + TOTP_CLI_PRINTF_COLORFUL(color, "\"" TOTP_CLI_COMMAND_NOTIFICATION_METHOD_VIBRO "\""); } if(method == NotificationMethodNone) { - TOTP_CLI_PRINTF("\"" TOTP_CLI_COMMAND_NOTIFICATION_METHOD_NONE "\""); + TOTP_CLI_PRINTF_COLORFUL(color, "\"" TOTP_CLI_COMMAND_NOTIFICATION_METHOD_NONE "\""); } } @@ -88,8 +88,8 @@ void totp_cli_command_notification_handle(PluginState* plugin_state, FuriString* plugin_state->notification_method = new_method; if(totp_config_file_update_notification_method(new_method) == TotpConfigFileUpdateSuccess) { - TOTP_CLI_PRINTF("Notification method is set to "); - totp_cli_command_notification_print_method(new_method); + TOTP_CLI_PRINTF_SUCCESS("Notification method is set to "); + totp_cli_command_notification_print_method(new_method, TOTP_CLI_COLOR_SUCCESS); cli_nl(); } else { TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE(); @@ -99,8 +99,9 @@ void totp_cli_command_notification_handle(PluginState* plugin_state, FuriString* totp_scene_director_activate_scene(plugin_state, previous_scene, NULL); } } else { - TOTP_CLI_PRINTF("Current notification method is "); - totp_cli_command_notification_print_method(plugin_state->notification_method); + TOTP_CLI_PRINTF_INFO("Current notification method is "); + totp_cli_command_notification_print_method( + plugin_state->notification_method, TOTP_CLI_COLOR_INFO); cli_nl(); } } while(false); diff --git a/applications/plugins/totp/cli/commands/pin/pin.c b/applications/plugins/totp/cli/commands/pin/pin.c index 198324e27..b5b4943d6 100644 --- a/applications/plugins/totp/cli/commands/pin/pin.c +++ b/applications/plugins/totp/cli/commands/pin/pin.c @@ -65,7 +65,7 @@ static bool totp_cli_read_pin(Cli* cli, uint8_t* pin, uint8_t* pin_length) { } } else if(c == CliSymbolAsciiETX) { TOTP_CLI_DELETE_CURRENT_LINE(); - TOTP_CLI_PRINTF("Cancelled by user\r\n"); + TOTP_CLI_PRINTF_INFO("Cancelled by user\r\n"); return false; } else if(c == CliSymbolAsciiBackspace || c == CliSymbolAsciiDel) { if(*pin_length > 0) { @@ -162,9 +162,9 @@ void totp_cli_command_pin_handle(PluginState* plugin_state, FuriString* args, Cl if(totp_full_save_config_file(plugin_state) == TotpConfigFileUpdateSuccess) { if(do_change) { - TOTP_CLI_PRINTF("PIN has been successfully changed\r\n"); + TOTP_CLI_PRINTF_SUCCESS("PIN has been successfully changed\r\n"); } else if(do_remove) { - TOTP_CLI_PRINTF("PIN has been successfully removed\r\n"); + TOTP_CLI_PRINTF_SUCCESS("PIN has been successfully removed\r\n"); } } else { TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE(); diff --git a/applications/plugins/totp/cli/commands/reset/reset.c b/applications/plugins/totp/cli/commands/reset/reset.c index 3f20dc8ad..c7928db31 100644 --- a/applications/plugins/totp/cli/commands/reset/reset.c +++ b/applications/plugins/totp/cli/commands/reset/reset.c @@ -17,21 +17,21 @@ void totp_cli_command_reset_docopt_usage() { } void totp_cli_command_reset_handle(Cli* cli, FuriMessageQueue* event_queue) { - TOTP_CLI_PRINTF( + TOTP_CLI_PRINTF_WARNING( "As a result of reset all the settings and tokens will be permanently lost.\r\n"); - TOTP_CLI_PRINTF("Do you really want to reset application?\r\n"); - TOTP_CLI_PRINTF("Type \"" TOTP_CLI_RESET_CONFIRMATION_KEYWORD - "\" and hit to confirm:\r\n"); + TOTP_CLI_PRINTF_WARNING("Do you really want to reset application?\r\n"); + TOTP_CLI_PRINTF_WARNING("Type \"" TOTP_CLI_RESET_CONFIRMATION_KEYWORD + "\" and hit to confirm:\r\n"); FuriString* temp_str = furi_string_alloc(); bool is_confirmed = totp_cli_read_line(cli, temp_str, false) && furi_string_cmpi_str(temp_str, TOTP_CLI_RESET_CONFIRMATION_KEYWORD) == 0; furi_string_free(temp_str); if(is_confirmed) { totp_config_file_reset(); - TOTP_CLI_PRINTF("Application has been successfully reset to default.\r\n"); - TOTP_CLI_PRINTF("Now application will be closed to apply all the changes.\r\n"); + TOTP_CLI_PRINTF_SUCCESS("Application has been successfully reset to default.\r\n"); + TOTP_CLI_PRINTF_SUCCESS("Now application will be closed to apply all the changes.\r\n"); totp_cli_force_close_app(event_queue); } else { - TOTP_CLI_PRINTF("Action was not confirmed by user\r\n"); + TOTP_CLI_PRINTF_INFO("Action was not confirmed by user\r\n"); } } \ No newline at end of file diff --git a/applications/plugins/totp/cli/commands/timezone/timezone.c b/applications/plugins/totp/cli/commands/timezone/timezone.c index 537cf8a4a..9eb0cb5f6 100644 --- a/applications/plugins/totp/cli/commands/timezone/timezone.c +++ b/applications/plugins/totp/cli/commands/timezone/timezone.c @@ -20,7 +20,7 @@ void totp_cli_command_timezone_docopt_usage() { void totp_cli_command_timezone_docopt_arguments() { TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_TIMEZONE_ARG_TIMEZONE - " Timezone offset in hours to be set.\r\n"); + " Timezone offset in hours to be set\r\n"); } void totp_cli_command_timezone_handle(PluginState* plugin_state, FuriString* args, Cli* cli) { @@ -30,11 +30,12 @@ void totp_cli_command_timezone_handle(PluginState* plugin_state, FuriString* arg FuriString* temp_str = furi_string_alloc(); if(args_read_string_and_trim(args, temp_str)) { - float tz = strtof(furi_string_get_cstr(temp_str), NULL); - if(tz >= -12.75f && tz <= 12.75f) { + char* strtof_endptr; + float tz = strtof(furi_string_get_cstr(temp_str), &strtof_endptr); + if(*strtof_endptr == 0 && tz >= -12.75f && tz <= 12.75f) { plugin_state->timezone_offset = tz; if(totp_config_file_update_timezone_offset(tz) == TotpConfigFileUpdateSuccess) { - TOTP_CLI_PRINTF("Timezone is set to %f\r\n", tz); + TOTP_CLI_PRINTF_SUCCESS("Timezone is set to %f\r\n", tz); } else { TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE(); } @@ -46,10 +47,10 @@ void totp_cli_command_timezone_handle(PluginState* plugin_state, FuriString* arg totp_scene_director_activate_scene(plugin_state, TotpSceneAppSettings, NULL); } } else { - TOTP_CLI_PRINTF("Invalid timezone offset\r\n"); + TOTP_CLI_PRINTF_ERROR("Invalid timezone offset\r\n"); } } else { - TOTP_CLI_PRINTF("Current timezone offset is %f\r\n", plugin_state->timezone_offset); + TOTP_CLI_PRINTF_INFO("Current timezone offset is %f\r\n", plugin_state->timezone_offset); } furi_string_free(temp_str); } \ No newline at end of file diff --git a/applications/plugins/totp/services/config/config.c b/applications/plugins/totp/services/config/config.c index c5193ac2e..76c72707c 100644 --- a/applications/plugins/totp/services/config/config.c +++ b/applications/plugins/totp/services/config/config.c @@ -132,7 +132,7 @@ static TotpConfigFileOpenResult totp_open_config_file(Storage* storage, FlipperF flipper_format_write_comment_cstr(fff_data_file, " "); flipper_format_write_comment_cstr( fff_data_file, - "How to notify user when new token is generated or badkb mode is activated (possible values: 0 - do not notify, 1 - sound, 2 - vibro, 3 sound and vibro)"); + "How to notify user when new token is generated or badusb mode is activated (possible values: 0 - do not notify, 1 - sound, 2 - vibro, 3 sound and vibro)"); flipper_format_write_uint32( fff_data_file, TOTP_CONFIG_KEY_NOTIFICATION_METHOD, &tmp_uint32, 1); @@ -196,7 +196,7 @@ static TotpConfigFileOpenResult totp_open_config_file(Storage* storage, FlipperF return TotpConfigFileOpenSuccess; } -TotpConfigFileUpdateResult +static TotpConfigFileUpdateResult totp_config_file_save_new_token_i(FlipperFormat* file, const TokenInfo* token_info) { TotpConfigFileUpdateResult update_result; do { diff --git a/applications/plugins/totp/services/hmac/sha1.c b/applications/plugins/totp/services/hmac/sha1.c index 243a6dde8..ecf22fc97 100644 --- a/applications/plugins/totp/services/hmac/sha1.c +++ b/applications/plugins/totp/services/hmac/sha1.c @@ -174,16 +174,13 @@ void sha1_process_bytes(const void* buffer, size_t len, struct sha1_ctx* ctx) { /* --- Code below is the primary difference between md5.c and sha1.c --- */ /* SHA1 round constants */ -#define K1 0x5a827999 -#define K2 0x6ed9eba1 -#define K3 0x8f1bbcdc -#define K4 0xca62c1d6 +static const int sha1_round_constants[4] = {0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6}; /* Round functions. Note that F2 is the same as F4. */ #define F1(B, C, D) (D ^ (B & (C ^ D))) -#define F2(B, C, D) (B ^ C ^ D) +#define F2_4(B, C, D) (B ^ C ^ D) #define F3(B, C, D) ((B & C) | (D & (B | C))) -#define F4(B, C, D) (B ^ C ^ D) +#define FN(I, B, C, D) (I == 0 ? F1(B, C, D) : (I == 2 ? F3(B, C, D) : F2_4(B, C, D))) /* Process LEN bytes of BUFFER, accumulating context into CTX. It is assumed that LEN % 64 == 0. @@ -213,10 +210,10 @@ void sha1_process_block(const void* buffer, size_t len, struct sha1_ctx* ctx) { (tm = x[I & 0x0f] ^ x[(I - 14) & 0x0f] ^ x[(I - 8) & 0x0f] ^ x[(I - 3) & 0x0f], \ (x[I & 0x0f] = rol(tm, 1))) -#define R(A, B, C, D, E, F, K, M) \ - do { \ - E += rol(A, 5) + F(B, C, D) + K + M; \ - B = rol(B, 30); \ +#define R(A, B, C, D, E, F, K, M, KI) \ + do { \ + E += rol(A, 5) + F(KI, B, C, D) + K + M; \ + B = rol(B, 30); \ } while(0) while(words < endp) { @@ -227,24 +224,11 @@ void sha1_process_block(const void* buffer, size_t len, struct sha1_ctx* ctx) { words++; } - for(int i = 0; i < 80; i++) { - uint32_t xx = i < 16 ? x[i] : M(i); - uint32_t ki = i / 20; - switch(ki) { - case 0: - R(a, b, c, d, e, F1, K1, xx); - break; - case 1: - R(a, b, c, d, e, F2, K2, xx); - break; - case 2: - R(a, b, c, d, e, F3, K3, xx); - break; - default: - R(a, b, c, d, e, F4, K4, xx); - break; - } - + for(uint8_t i = 0; i < 80; i++) { + uint32_t m = i < 16 ? x[i] : M(i); + uint8_t ki = i / 20; + int k_const = sha1_round_constants[ki]; + R(a, b, c, d, e, FN, k_const, m, ki); uint32_t tt = a; a = e; e = d; diff --git a/applications/plugins/totp/totp_app.c b/applications/plugins/totp/totp_app.c index f10837814..966c9fb34 100644 --- a/applications/plugins/totp/totp_app.c +++ b/applications/plugins/totp/totp_app.c @@ -7,6 +7,7 @@ #include #include #include +#include #include "services/config/config.h" #include "types/plugin_state.h" #include "types/token_info.h" @@ -22,12 +23,12 @@ #define IDLE_TIMEOUT 60000 static void render_callback(Canvas* const canvas, void* ctx) { - PluginState* plugin_state = acquire_mutex((ValueMutex*)ctx, 25); - if(plugin_state != NULL) { + furi_assert(ctx); + PluginState* plugin_state = ctx; + if(furi_mutex_acquire(plugin_state->mutex, 25) == FuriStatusOk) { totp_scene_director_render(canvas, plugin_state); + furi_mutex_release(plugin_state->mutex); } - - release_mutex((ValueMutex*)ctx, plugin_state); } static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { @@ -101,6 +102,12 @@ static bool totp_plugin_state_init(PluginState* const plugin_state) { return false; } + plugin_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(plugin_state->mutex == NULL) { + FURI_LOG_E(LOGGING_TAG, "Cannot create mutex\r\n"); + return false; + } + return true; } @@ -122,6 +129,8 @@ static void totp_plugin_state_free(PluginState* plugin_state) { if(plugin_state->crypto_verify_data != NULL) { free(plugin_state->crypto_verify_data); } + + furi_mutex_free(plugin_state->mutex); free(plugin_state); } @@ -136,13 +145,6 @@ int32_t totp_app() { return 254; } - ValueMutex state_mutex; - if(!init_mutex(&state_mutex, plugin_state, sizeof(PluginState))) { - FURI_LOG_E(LOGGING_TAG, "Cannot create mutex\r\n"); - totp_plugin_state_free(plugin_state); - return 255; - } - TotpCliContext* cli_context = totp_cli_register_command_handler(plugin_state, event_queue); totp_scene_director_init_scenes(plugin_state); if(!totp_activate_initial_scene(plugin_state)) { @@ -151,9 +153,12 @@ int32_t totp_app() { return 253; } + // Affecting dolphin level + DOLPHIN_DEED(DolphinDeedPluginStart); + // Set system callbacks ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, render_callback, &state_mutex); + view_port_draw_callback_set(view_port, render_callback, plugin_state); view_port_input_callback_set(view_port, input_callback, event_queue); // Open GUI and register view_port @@ -165,26 +170,26 @@ int32_t totp_app() { while(processing) { FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); - PluginState* plugin_state_m = acquire_mutex_block(&state_mutex); + if(furi_mutex_acquire(plugin_state->mutex, FuriWaitForever) == FuriStatusOk) { + if(event_status == FuriStatusOk) { + if(event.type == EventTypeKey) { + last_user_interaction_time = furi_get_tick(); + } - if(event_status == FuriStatusOk) { - if(event.type == EventTypeKey) { - last_user_interaction_time = furi_get_tick(); + if(event.type == EventForceCloseApp) { + processing = false; + } else { + processing = totp_scene_director_handle_event(&event, plugin_state); + } + } else if( + plugin_state->pin_set && plugin_state->current_scene != TotpSceneAuthentication && + furi_get_tick() - last_user_interaction_time > IDLE_TIMEOUT) { + totp_scene_director_activate_scene(plugin_state, TotpSceneAuthentication, NULL); } - if(event.type == EventForceCloseApp) { - processing = false; - } else { - processing = totp_scene_director_handle_event(&event, plugin_state_m); - } - } else if( - plugin_state_m->pin_set && plugin_state_m->current_scene != TotpSceneAuthentication && - furi_get_tick() - last_user_interaction_time > IDLE_TIMEOUT) { - totp_scene_director_activate_scene(plugin_state_m, TotpSceneAuthentication, NULL); + view_port_update(view_port); + furi_mutex_release(plugin_state->mutex); } - - view_port_update(view_port); - release_mutex(&state_mutex, plugin_state_m); } totp_cli_unregister_command_handler(cli_context); @@ -195,7 +200,6 @@ int32_t totp_app() { gui_remove_view_port(plugin_state->gui, view_port); view_port_free(view_port); furi_message_queue_free(event_queue); - delete_mutex(&state_mutex); totp_plugin_state_free(plugin_state); return 0; } diff --git a/applications/plugins/totp/types/plugin_state.h b/applications/plugins/totp/types/plugin_state.h index 0aad2e125..dee500305 100644 --- a/applications/plugins/totp/types/plugin_state.h +++ b/applications/plugins/totp/types/plugin_state.h @@ -87,4 +87,9 @@ typedef struct { * @brief Notification method */ NotificationMethod notification_method; + + /** + * @brief Main rendering loop mutex + */ + FuriMutex* mutex; } PluginState; diff --git a/applications/plugins/totp/types/token_info.c b/applications/plugins/totp/types/token_info.c index 60d04245f..b69979cc7 100644 --- a/applications/plugins/totp/types/token_info.c +++ b/applications/plugins/totp/types/token_info.c @@ -12,6 +12,7 @@ TokenInfo* token_info_alloc() { furi_check(tokenInfo != NULL); tokenInfo->algo = SHA1; tokenInfo->digits = TOTP_6_DIGITS; + tokenInfo->duration = TOTP_TOKEN_DURATION_DEFAULT; return tokenInfo; } diff --git a/applications/plugins/totp/ui/scenes/app_settings/totp_app_settings.c b/applications/plugins/totp/ui/scenes/app_settings/totp_app_settings.c index fa4ba5411..b8936f395 100644 --- a/applications/plugins/totp/ui/scenes/app_settings/totp_app_settings.c +++ b/applications/plugins/totp/ui/scenes/app_settings/totp_app_settings.c @@ -1,6 +1,6 @@ #include "totp_app_settings.h" #include -#include +#include #include "../../ui_controls.h" #include "../../common_dialogs.h" #include "../../scene_director.h" @@ -247,4 +247,4 @@ void totp_scene_app_settings_deactivate(PluginState* plugin_state) { void totp_scene_app_settings_free(const PluginState* plugin_state) { UNUSED(plugin_state); -} +} \ No newline at end of file diff --git a/applications/plugins/totp/ui/scenes/authenticate/totp_scene_authenticate.c b/applications/plugins/totp/ui/scenes/authenticate/totp_scene_authenticate.c index d4977050f..17beb64c6 100644 --- a/applications/plugins/totp/ui/scenes/authenticate/totp_scene_authenticate.c +++ b/applications/plugins/totp/ui/scenes/authenticate/totp_scene_authenticate.c @@ -1,6 +1,6 @@ #include "totp_scene_authenticate.h" #include -#include +#include #include "../../../types/common.h" #include "../../constants.h" #include "../../../services/config/config.h" @@ -10,6 +10,8 @@ #include "../../../types/user_pin_codes.h" #define MAX_CODE_LENGTH TOTP_IV_SIZE +static const uint8_t PIN_ASTERISK_RADIUS = 3; +static const uint8_t PIN_ASTERISK_STEP = (PIN_ASTERISK_RADIUS << 1) + 2; typedef struct { TotpUserPinCode code_input[MAX_CODE_LENGTH]; @@ -61,8 +63,7 @@ void totp_scene_authenticate_render(Canvas* const canvas, PluginState* plugin_st AlignCenter, "Use arrow keys to enter PIN"); } - const uint8_t PIN_ASTERISK_RADIUS = 3; - const uint8_t PIN_ASTERISK_STEP = (PIN_ASTERISK_RADIUS << 1) + 2; + if(scene_state->code_length > 0) { uint8_t left_start_x = ((scene_state->code_length - 1) * PIN_ASTERISK_STEP) >> 1; for(uint8_t i = 0; i < scene_state->code_length; i++) { diff --git a/applications/plugins/totp/ui/scenes/generate_token/totp_scene_generate_token.c b/applications/plugins/totp/ui/scenes/generate_token/totp_scene_generate_token.c index 18d1791f4..a882ae78c 100644 --- a/applications/plugins/totp/ui/scenes/generate_token/totp_scene_generate_token.c +++ b/applications/plugins/totp/ui/scenes/generate_token/totp_scene_generate_token.c @@ -1,7 +1,7 @@ #include #include #include -#include +#include #include "totp_scene_generate_token.h" #include "../../../types/token_info.h" #include "../../../types/common.h" @@ -16,6 +16,9 @@ #include "../token_menu/totp_scene_token_menu.h" #include "../../../workers/type_code/type_code.h" +static const uint8_t PROGRESS_BAR_MARGIN = 3; +static const uint8_t PROGRESS_BAR_HEIGHT = 4; + typedef struct { uint16_t current_token_index; char last_code[TOTP_TOKEN_DIGITS_MAX_COUNT + 1]; @@ -24,7 +27,7 @@ typedef struct { uint32_t last_token_gen_time; TotpTypeCodeWorkerContext* type_code_worker_context; NotificationMessage const** notification_sequence_new_token; - NotificationMessage const** notification_sequence_badkb; + NotificationMessage const** notification_sequence_badusb; } SceneState; static const NotificationSequence* @@ -69,8 +72,8 @@ static const NotificationSequence* } static const NotificationSequence* - get_notification_sequence_badkb(const PluginState* plugin_state, SceneState* scene_state) { - if(scene_state->notification_sequence_badkb == NULL) { + get_notification_sequence_badusb(const PluginState* plugin_state, SceneState* scene_state) { + if(scene_state->notification_sequence_badusb == NULL) { uint8_t i = 0; uint8_t length = 3; if(plugin_state->notification_method & NotificationMethodVibro) { @@ -81,36 +84,36 @@ static const NotificationSequence* length += 6; } - scene_state->notification_sequence_badkb = malloc(sizeof(void*) * length); - furi_check(scene_state->notification_sequence_badkb != NULL); + scene_state->notification_sequence_badusb = malloc(sizeof(void*) * length); + furi_check(scene_state->notification_sequence_badusb != NULL); - scene_state->notification_sequence_badkb[i++] = &message_blue_255; + scene_state->notification_sequence_badusb[i++] = &message_blue_255; if(plugin_state->notification_method & NotificationMethodVibro) { - scene_state->notification_sequence_badkb[i++] = &message_vibro_on; + scene_state->notification_sequence_badusb[i++] = &message_vibro_on; } if(plugin_state->notification_method & NotificationMethodSound) { - scene_state->notification_sequence_badkb[i++] = &message_note_d5; //-V525 - scene_state->notification_sequence_badkb[i++] = &message_delay_50; - scene_state->notification_sequence_badkb[i++] = &message_note_e4; - scene_state->notification_sequence_badkb[i++] = &message_delay_50; - scene_state->notification_sequence_badkb[i++] = &message_note_f3; + scene_state->notification_sequence_badusb[i++] = &message_note_d5; //-V525 + scene_state->notification_sequence_badusb[i++] = &message_delay_50; + scene_state->notification_sequence_badusb[i++] = &message_note_e4; + scene_state->notification_sequence_badusb[i++] = &message_delay_50; + scene_state->notification_sequence_badusb[i++] = &message_note_f3; } - scene_state->notification_sequence_badkb[i++] = &message_delay_50; + scene_state->notification_sequence_badusb[i++] = &message_delay_50; if(plugin_state->notification_method & NotificationMethodVibro) { - scene_state->notification_sequence_badkb[i++] = &message_vibro_off; + scene_state->notification_sequence_badusb[i++] = &message_vibro_off; } if(plugin_state->notification_method & NotificationMethodSound) { - scene_state->notification_sequence_badkb[i++] = &message_sound_off; + scene_state->notification_sequence_badusb[i++] = &message_sound_off; } - scene_state->notification_sequence_badkb[i++] = NULL; + scene_state->notification_sequence_badusb[i++] = NULL; } - return (NotificationSequence*)scene_state->notification_sequence_badkb; + return (NotificationSequence*)scene_state->notification_sequence_badusb; } static void int_token_to_str(uint32_t i_token_code, char* str, TokenDigitsCount len) { @@ -306,14 +309,18 @@ void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_ AlignCenter, scene_state->last_code); - const uint8_t BAR_MARGIN = 3; - const uint8_t BAR_HEIGHT = 4; const uint8_t TOKEN_LIFETIME = scene_state->current_token->duration; float percentDone = (float)(TOKEN_LIFETIME - curr_ts % TOKEN_LIFETIME) / (float)TOKEN_LIFETIME; - uint8_t barWidth = (uint8_t)((float)(SCREEN_WIDTH - (BAR_MARGIN << 1)) * percentDone); - uint8_t barX = ((SCREEN_WIDTH - (BAR_MARGIN << 1) - barWidth) >> 1) + BAR_MARGIN; + uint8_t barWidth = (uint8_t)((float)(SCREEN_WIDTH - (PROGRESS_BAR_MARGIN << 1)) * percentDone); + uint8_t barX = + ((SCREEN_WIDTH - (PROGRESS_BAR_MARGIN << 1) - barWidth) >> 1) + PROGRESS_BAR_MARGIN; - canvas_draw_box(canvas, barX, SCREEN_HEIGHT - BAR_MARGIN - BAR_HEIGHT, barWidth, BAR_HEIGHT); + canvas_draw_box( + canvas, + barX, + SCREEN_HEIGHT - PROGRESS_BAR_MARGIN - PROGRESS_BAR_HEIGHT, + barWidth, + PROGRESS_BAR_HEIGHT); if(plugin_state->tokens_count > 1) { canvas_draw_icon(canvas, 0, SCREEN_HEIGHT_CENTER - 24, &I_totp_arrow_left_8x9); @@ -340,7 +347,7 @@ bool totp_scene_generate_token_handle_event( scene_state->type_code_worker_context, TotpTypeCodeWorkerEventType); notification_message( plugin_state->notification_app, - get_notification_sequence_badkb(plugin_state, scene_state)); + get_notification_sequence_badusb(plugin_state, scene_state)); return true; } @@ -399,8 +406,8 @@ void totp_scene_generate_token_deactivate(PluginState* plugin_state) { free(scene_state->notification_sequence_new_token); } - if(scene_state->notification_sequence_badkb != NULL) { - free(scene_state->notification_sequence_badkb); + if(scene_state->notification_sequence_badusb != NULL) { + free(scene_state->notification_sequence_badusb); } free(scene_state); diff --git a/applications/plugins/totp/ui/ui_controls.c b/applications/plugins/totp/ui/ui_controls.c index 7fc7f0111..af029dd9f 100644 --- a/applications/plugins/totp/ui/ui_controls.c +++ b/applications/plugins/totp/ui/ui_controls.c @@ -1,5 +1,5 @@ #include "ui_controls.h" -#include +#include #include "constants.h" #define TEXT_BOX_HEIGHT 13 diff --git a/applications/plugins/totp/workers/type_code/type_code.c b/applications/plugins/totp/workers/type_code/type_code.c index 3eb59047a..f2b4c9b9e 100644 --- a/applications/plugins/totp/workers/type_code/type_code.c +++ b/applications/plugins/totp/workers/type_code/type_code.c @@ -57,8 +57,8 @@ static void totp_type_code_worker_type_code(TotpTypeCodeWorkerContext* context) } static int32_t totp_type_code_worker_callback(void* context) { - ValueMutex context_mutex; - if(!init_mutex(&context_mutex, context, sizeof(TotpTypeCodeWorkerContext))) { + FuriMutex* context_mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(context_mutex == NULL) { return 251; } @@ -70,15 +70,16 @@ static int32_t totp_type_code_worker_callback(void* context) { furi_check((flags & FuriFlagError) == 0); //-V562 if(flags & TotpTypeCodeWorkerEventStop) break; - TotpTypeCodeWorkerContext* h_context = acquire_mutex_block(&context_mutex); - if(flags & TotpTypeCodeWorkerEventType) { - totp_type_code_worker_type_code(h_context); - } + if(furi_mutex_acquire(context_mutex, FuriWaitForever) == FuriStatusOk) { + if(flags & TotpTypeCodeWorkerEventType) { + totp_type_code_worker_type_code(context); + } - release_mutex(&context_mutex, h_context); + furi_mutex_release(context_mutex); + } } - delete_mutex(&context_mutex); + furi_mutex_free(context_mutex); return 0; } diff --git a/applications/plugins/tuning_fork/application.fam b/applications/plugins/tuning_fork/application.fam index 47cef5364..5979ddb2a 100644 --- a/applications/plugins/tuning_fork/application.fam +++ b/applications/plugins/tuning_fork/application.fam @@ -1,5 +1,5 @@ App( - appid="Tuning_Fork", + appid="tuning_fork", name="Tuning Fork", apptype=FlipperAppType.EXTERNAL, entry_point="tuning_fork_app", diff --git a/applications/plugins/tuning_fork/tuning_fork.c b/applications/plugins/tuning_fork/tuning_fork.c index 69a76029f..6912d1780 100644 --- a/applications/plugins/tuning_fork/tuning_fork.c +++ b/applications/plugins/tuning_fork/tuning_fork.c @@ -28,6 +28,7 @@ typedef struct { enum Page { Tunings, Notes }; typedef struct { + FuriMutex* mutex; bool playing; enum Page page; int current_tuning_note_index; @@ -114,7 +115,7 @@ static void decrease_volume(TuningForkState* tuning_fork_state) { } static void play(TuningForkState* tuning_fork_state) { - if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(30)) { + if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(1000)) { furi_hal_speaker_start( current_tuning_note_freq(tuning_fork_state), tuning_fork_state->volume); } @@ -133,10 +134,9 @@ static void replay(TuningForkState* tuning_fork_state) { } static void render_callback(Canvas* const canvas, void* ctx) { - TuningForkState* tuning_fork_state = acquire_mutex((ValueMutex*)ctx, 25); - if(tuning_fork_state == NULL) { - return; - } + furi_assert(ctx); + TuningForkState* tuning_fork_state = ctx; + furi_mutex_acquire(tuning_fork_state->mutex, FuriWaitForever); string_t tempStr; string_init(tempStr); @@ -185,7 +185,7 @@ static void render_callback(Canvas* const canvas, void* ctx) { } string_clear(tempStr); - release_mutex((ValueMutex*)ctx, tuning_fork_state); + furi_mutex_release(tuning_fork_state->mutex); } static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { @@ -210,8 +210,8 @@ int32_t tuning_fork_app() { TuningForkState* tuning_fork_state = malloc(sizeof(TuningForkState)); tuning_fork_state_init(tuning_fork_state); - ValueMutex state_mutex; - if(!init_mutex(&state_mutex, tuning_fork_state, sizeof(TuningForkState))) { + tuning_fork_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!tuning_fork_state->mutex) { FURI_LOG_E("TuningFork", "cannot create mutex\r\n"); free(tuning_fork_state); return 255; @@ -219,7 +219,7 @@ int32_t tuning_fork_app() { // Set system callbacks ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, render_callback, &state_mutex); + view_port_draw_callback_set(view_port, render_callback, tuning_fork_state); view_port_input_callback_set(view_port, input_callback, event_queue); Gui* gui = furi_record_open("gui"); @@ -229,7 +229,7 @@ int32_t tuning_fork_app() { for(bool processing = true; processing;) { FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); - TuningForkState* tuning_fork_state = (TuningForkState*)acquire_mutex_block(&state_mutex); + furi_mutex_acquire(tuning_fork_state->mutex, FuriWaitForever); if(event_status == FuriStatusOk) { if(event.type == EventTypeKey) { @@ -387,12 +387,10 @@ int32_t tuning_fork_app() { } } } - } else { - FURI_LOG_D("TuningFork", "FuriMessageQueue: event timeout"); } view_port_update(view_port); - release_mutex(&state_mutex, tuning_fork_state); + furi_mutex_release(tuning_fork_state->mutex); } view_port_enabled_set(view_port, false); @@ -400,7 +398,7 @@ int32_t tuning_fork_app() { furi_record_close("gui"); view_port_free(view_port); furi_message_queue_free(event_queue); - delete_mutex(&state_mutex); + furi_mutex_free(tuning_fork_state->mutex); furi_record_close(RECORD_NOTIFICATION); free(tuning_fork_state); diff --git a/applications/plugins/sentry_safe/LICENSE b/applications/plugins/uart_terminal/LICENSE similarity index 96% rename from applications/plugins/sentry_safe/LICENSE rename to applications/plugins/uart_terminal/LICENSE index c0cd6b598..c4613e7ea 100644 --- a/applications/plugins/sentry_safe/LICENSE +++ b/applications/plugins/uart_terminal/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2022 Etienne Sellan +Copyright (c) 2023 Malik cool4uma Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -19,3 +19,4 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + diff --git a/applications/plugins/uart_terminal/README.md b/applications/plugins/uart_terminal/README.md new file mode 100644 index 000000000..7e2cfd212 --- /dev/null +++ b/applications/plugins/uart_terminal/README.md @@ -0,0 +1,45 @@ +# UART Terminal for Flipper Zero +[Flipper Zero](https://flipperzero.one/) app to control various devices via UART interface. + +## Capabilities +- Read log and command output by uart +- Send commands by uart +- Set baud rate +- Fast commands + +## Connecting +| Flipper Zero pin | UART interface | +| ---------------- | --------------- | +| 13 TX | RX | +| 14 RX | TX | +|8, 18 GND | GND | + +Info: If possible, do not power your devices from 3V3 (pin 9) Flipper Zero. It does not support hot plugging. + +## Keyboard +UART_terminal uses its own special keyboard for work, which has all the symbols necessary for working in the console. + +To accommodate more characters on a small display, some characters are called up by holding. + +![kbf](https://user-images.githubusercontent.com/122148894/212286637-7063f1ee-c6ff-46b9-8dc5-79a5f367fab1.png) + + +## How to install +Copy the contents of the repository to the applications_user/uart_terminal folder Flipper Zero firmware and build app with the command ./fbt fap_uart_terminal. + +Or use the tool [uFBT](https://github.com/flipperdevices/flipperzero-ufbt) for building applications for Flipper Zero. + +## How it works + + +![1f](https://user-images.githubusercontent.com/122148894/211161450-6d177638-3bfa-42a8-9c73-0cf3af5e5ca7.jpg) + + +![2f](https://user-images.githubusercontent.com/122148894/211161456-4d2be15b-4a05-4450-a62e-edcaab3772fd.jpg) + + +![4f](https://user-images.githubusercontent.com/122148894/211161461-4507120b-42df-441f-9e01-e4517aa83537.jpg) + +## INFO: + +~70% of the source code is taken from the [Wifi Marauder](https://github.com/0xchocolate/flipperzero-firmware-with-wifi-marauder-companion) project. Many thanks to the developers of the Wifi Marauder project. diff --git a/applications/plugins/uart_terminal/application.fam b/applications/plugins/uart_terminal/application.fam new file mode 100644 index 000000000..a36960ea4 --- /dev/null +++ b/applications/plugins/uart_terminal/application.fam @@ -0,0 +1,13 @@ +App( + appid="uart_terminal", + name="[GPIO] UART Terminal", + apptype=FlipperAppType.EXTERNAL, + entry_point="uart_terminal_app", + cdefines=["APP_UART_TERMINAL"], + requires=["gui"], + stack_size=1 * 1024, + order=90, + fap_icon="uart_terminal.png", + fap_category="GPIO", + fap_icon_assets="assets", +) diff --git a/applications/plugins/uart_terminal/assets/KeyBackspaceSelected_16x9.png b/applications/plugins/uart_terminal/assets/KeyBackspaceSelected_16x9.png new file mode 100644 index 000000000..7cc0759a8 Binary files /dev/null and b/applications/plugins/uart_terminal/assets/KeyBackspaceSelected_16x9.png differ diff --git a/applications/plugins/uart_terminal/assets/KeyBackspace_16x9.png b/applications/plugins/uart_terminal/assets/KeyBackspace_16x9.png new file mode 100644 index 000000000..9946232d9 Binary files /dev/null and b/applications/plugins/uart_terminal/assets/KeyBackspace_16x9.png differ diff --git a/applications/plugins/uart_terminal/assets/KeySaveSelected_24x11.png b/applications/plugins/uart_terminal/assets/KeySaveSelected_24x11.png new file mode 100644 index 000000000..eeb3569d3 Binary files /dev/null and b/applications/plugins/uart_terminal/assets/KeySaveSelected_24x11.png differ diff --git a/applications/plugins/uart_terminal/assets/KeySave_24x11.png b/applications/plugins/uart_terminal/assets/KeySave_24x11.png new file mode 100644 index 000000000..e7dba987a Binary files /dev/null and b/applications/plugins/uart_terminal/assets/KeySave_24x11.png differ diff --git a/applications/plugins/uart_terminal/assets/WarningDolphin_45x42.png b/applications/plugins/uart_terminal/assets/WarningDolphin_45x42.png new file mode 100644 index 000000000..d766ffbb4 Binary files /dev/null and b/applications/plugins/uart_terminal/assets/WarningDolphin_45x42.png differ diff --git a/applications/plugins/uart_terminal/scenes/uart_terminal_scene.c b/applications/plugins/uart_terminal/scenes/uart_terminal_scene.c new file mode 100644 index 000000000..451c5d98b --- /dev/null +++ b/applications/plugins/uart_terminal/scenes/uart_terminal_scene.c @@ -0,0 +1,30 @@ +#include "uart_terminal_scene.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const uart_terminal_scene_on_enter_handlers[])(void*) = { +#include "uart_terminal_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_event handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, +bool (*const uart_terminal_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = { +#include "uart_terminal_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_exit handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, +void (*const uart_terminal_scene_on_exit_handlers[])(void* context) = { +#include "uart_terminal_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers uart_terminal_scene_handlers = { + .on_enter_handlers = uart_terminal_scene_on_enter_handlers, + .on_event_handlers = uart_terminal_scene_on_event_handlers, + .on_exit_handlers = uart_terminal_scene_on_exit_handlers, + .scene_num = UART_TerminalSceneNum, +}; diff --git a/applications/plugins/namechanger/scenes/namechanger_scene.h b/applications/plugins/uart_terminal/scenes/uart_terminal_scene.h similarity index 64% rename from applications/plugins/namechanger/scenes/namechanger_scene.h rename to applications/plugins/uart_terminal/scenes/uart_terminal_scene.h index 42071ab98..c6f4ed4b4 100644 --- a/applications/plugins/namechanger/scenes/namechanger_scene.h +++ b/applications/plugins/uart_terminal/scenes/uart_terminal_scene.h @@ -3,27 +3,27 @@ #include // Generate scene id and total number -#define ADD_SCENE(prefix, name, id) NameChangerScene##id, +#define ADD_SCENE(prefix, name, id) UART_TerminalScene##id, typedef enum { -#include "namechanger_scene_config.h" - NameChangerSceneNum, -} NameChangerScene; +#include "uart_terminal_scene_config.h" + UART_TerminalSceneNum, +} UART_TerminalScene; #undef ADD_SCENE -extern const SceneManagerHandlers namechanger_scene_handlers; +extern const SceneManagerHandlers uart_terminal_scene_handlers; // Generate scene on_enter handlers declaration #define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); -#include "namechanger_scene_config.h" +#include "uart_terminal_scene_config.h" #undef ADD_SCENE // Generate scene on_event handlers declaration #define ADD_SCENE(prefix, name, id) \ bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); -#include "namechanger_scene_config.h" +#include "uart_terminal_scene_config.h" #undef ADD_SCENE // Generate scene on_exit handlers declaration #define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); -#include "namechanger_scene_config.h" +#include "uart_terminal_scene_config.h" #undef ADD_SCENE diff --git a/applications/plugins/uart_terminal/scenes/uart_terminal_scene_config.h b/applications/plugins/uart_terminal/scenes/uart_terminal_scene_config.h new file mode 100644 index 000000000..febdce167 --- /dev/null +++ b/applications/plugins/uart_terminal/scenes/uart_terminal_scene_config.h @@ -0,0 +1,3 @@ +ADD_SCENE(uart_terminal, start, Start) +ADD_SCENE(uart_terminal, console_output, ConsoleOutput) +ADD_SCENE(uart_terminal, text_input, UART_TextInput) diff --git a/applications/plugins/uart_terminal/scenes/uart_terminal_scene_console_output.c b/applications/plugins/uart_terminal/scenes/uart_terminal_scene_console_output.c new file mode 100644 index 000000000..a9f998124 --- /dev/null +++ b/applications/plugins/uart_terminal/scenes/uart_terminal_scene_console_output.c @@ -0,0 +1,147 @@ +#include "../uart_terminal_app_i.h" + +void uart_terminal_console_output_handle_rx_data_cb(uint8_t* buf, size_t len, void* context) { + furi_assert(context); + UART_TerminalApp* app = context; + + // If text box store gets too big, then truncate it + app->text_box_store_strlen += len; + if(app->text_box_store_strlen >= UART_TERMINAL_TEXT_BOX_STORE_SIZE - 1) { + furi_string_right(app->text_box_store, app->text_box_store_strlen / 2); + app->text_box_store_strlen = furi_string_size(app->text_box_store) + len; + } + + // Null-terminate buf and append to text box store + buf[len] = '\0'; + furi_string_cat_printf(app->text_box_store, "%s", buf); + + view_dispatcher_send_custom_event( + app->view_dispatcher, UART_TerminalEventRefreshConsoleOutput); +} + +void uart_terminal_scene_console_output_on_enter(void* context) { + UART_TerminalApp* app = context; + + TextBox* text_box = app->text_box; + text_box_reset(app->text_box); + text_box_set_font(text_box, TextBoxFontText); + if(app->focus_console_start) { + text_box_set_focus(text_box, TextBoxFocusStart); + } else { + text_box_set_focus(text_box, TextBoxFocusEnd); + } + + //Change baudrate /////////////////////////////////////////////////////////////////////////// + if(0 == strncmp("2400", app->selected_tx_string, strlen("2400")) && app->BAUDRATE != 2400) { + uart_terminal_uart_free(app->uart); + app->BAUDRATE = 2400; + app->uart = uart_terminal_uart_init(app); + } + if(0 == strncmp("9600", app->selected_tx_string, strlen("9600")) && app->BAUDRATE != 9600) { + uart_terminal_uart_free(app->uart); + app->BAUDRATE = 9600; + app->uart = uart_terminal_uart_init(app); + } + if(0 == strncmp("19200", app->selected_tx_string, strlen("19200")) && app->BAUDRATE != 19200) { + uart_terminal_uart_free(app->uart); + app->BAUDRATE = 19200; + app->uart = uart_terminal_uart_init(app); + } + if(0 == strncmp("38400", app->selected_tx_string, strlen("38400")) && app->BAUDRATE != 38400) { + uart_terminal_uart_free(app->uart); + app->BAUDRATE = 38400; + app->uart = uart_terminal_uart_init(app); + } + if(0 == strncmp("57600", app->selected_tx_string, strlen("57600")) && app->BAUDRATE != 57600) { + uart_terminal_uart_free(app->uart); + app->BAUDRATE = 57600; + app->uart = uart_terminal_uart_init(app); + } + if(0 == strncmp("115200", app->selected_tx_string, strlen("115200")) && + app->BAUDRATE != 115200) { + uart_terminal_uart_free(app->uart); + app->BAUDRATE = 115200; + app->uart = uart_terminal_uart_init(app); + } + if(0 == strncmp("230400", app->selected_tx_string, strlen("230400")) && + app->BAUDRATE != 230400) { + uart_terminal_uart_free(app->uart); + app->BAUDRATE = 230400; + app->uart = uart_terminal_uart_init(app); + } + if(0 == strncmp("460800", app->selected_tx_string, strlen("460800")) && + app->BAUDRATE != 460800) { + uart_terminal_uart_free(app->uart); + app->BAUDRATE = 460800; + app->uart = uart_terminal_uart_init(app); + } + if(0 == strncmp("921600", app->selected_tx_string, strlen("921600")) && + app->BAUDRATE != 921600) { + uart_terminal_uart_free(app->uart); + app->BAUDRATE = 921600; + app->uart = uart_terminal_uart_init(app); + } + ///////////////////////////////////////////////////////////////////////////////////////////////////// + + if(app->is_command) { + furi_string_reset(app->text_box_store); + app->text_box_store_strlen = 0; + + if(0 == strncmp("help", app->selected_tx_string, strlen("help"))) { + const char* help_msg = + "UART terminal for Flipper\n\nI'm in github: cool4uma\n\nThis app is a modified\nWiFi Marauder companion,\nThanks 0xchocolate(github)\nfor great code and app.\n\n"; + furi_string_cat_str(app->text_box_store, help_msg); + app->text_box_store_strlen += strlen(help_msg); + } + + if(app->show_stopscan_tip) { + const char* help_msg = "Press BACK to return\n"; + furi_string_cat_str(app->text_box_store, help_msg); + app->text_box_store_strlen += strlen(help_msg); + } + } + + // Set starting text - for "View Log", this will just be what was already in the text box store + text_box_set_text(app->text_box, furi_string_get_cstr(app->text_box_store)); + + scene_manager_set_scene_state(app->scene_manager, UART_TerminalSceneConsoleOutput, 0); + view_dispatcher_switch_to_view(app->view_dispatcher, UART_TerminalAppViewConsoleOutput); + + // Register callback to receive data + uart_terminal_uart_set_handle_rx_data_cb( + app->uart, uart_terminal_console_output_handle_rx_data_cb); // setup callback for rx thread + + // Send command with newline '\n' + if(app->is_command && app->selected_tx_string) { + uart_terminal_uart_tx( + (uint8_t*)(app->selected_tx_string), strlen(app->selected_tx_string)); + uart_terminal_uart_tx((uint8_t*)("\n"), 1); + } +} + +bool uart_terminal_scene_console_output_on_event(void* context, SceneManagerEvent event) { + UART_TerminalApp* app = context; + + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + text_box_set_text(app->text_box, furi_string_get_cstr(app->text_box_store)); + consumed = true; + } else if(event.type == SceneManagerEventTypeTick) { + consumed = true; + } + + return consumed; +} + +void uart_terminal_scene_console_output_on_exit(void* context) { + UART_TerminalApp* app = context; + + // Unregister rx callback + uart_terminal_uart_set_handle_rx_data_cb(app->uart, NULL); + + // Automatically logut when exiting view + //if(app->is_command) { + // uart_terminal_uart_tx((uint8_t*)("exit\n"), strlen("exit\n")); + //} +} diff --git a/applications/plugins/uart_terminal/scenes/uart_terminal_scene_start.c b/applications/plugins/uart_terminal/scenes/uart_terminal_scene_start.c new file mode 100644 index 000000000..db783e9b2 --- /dev/null +++ b/applications/plugins/uart_terminal/scenes/uart_terminal_scene_start.c @@ -0,0 +1,137 @@ +#include "../uart_terminal_app_i.h" + +// For each command, define whether additional arguments are needed +// (enabling text input to fill them out), and whether the console +// text box should focus at the start of the output or the end +typedef enum { NO_ARGS = 0, INPUT_ARGS, TOGGLE_ARGS } InputArgs; + +typedef enum { FOCUS_CONSOLE_END = 0, FOCUS_CONSOLE_START, FOCUS_CONSOLE_TOGGLE } FocusConsole; + +#define SHOW_STOPSCAN_TIP (true) +#define NO_TIP (false) + +#define MAX_OPTIONS (9) +typedef struct { + const char* item_string; + const char* options_menu[MAX_OPTIONS]; + int num_options_menu; + const char* actual_commands[MAX_OPTIONS]; + InputArgs needs_keyboard; + FocusConsole focus_console; + bool show_stopscan_tip; +} UART_TerminalItem; + +// NUM_MENU_ITEMS defined in uart_terminal_app_i.h - if you add an entry here, increment it! +const UART_TerminalItem items[NUM_MENU_ITEMS] = { + {"Console", + {"115200", "2400", "9600", "19200", "38400", "57600", "230400", "460800", "921600"}, + 9, + {"115200", "2400", "9600", "19200", "38400", "57600", "230400", "460800", "921600"}, + NO_ARGS, + FOCUS_CONSOLE_TOGGLE, + NO_TIP}, + {"Send command", {""}, 1, {""}, INPUT_ARGS, FOCUS_CONSOLE_END, NO_TIP}, + {"Fast cmd", + {"help", "uptime", "date", "df -h", "ps", "dmesg", "reboot", "poweroff"}, + 8, + {"help", "uptime", "date", "df -h", "ps", "dmesg", "reboot", "poweroff"}, + INPUT_ARGS, + FOCUS_CONSOLE_END, + NO_TIP}, + {"Help", {""}, 1, {"help"}, NO_ARGS, FOCUS_CONSOLE_START, SHOW_STOPSCAN_TIP}, +}; + +static void uart_terminal_scene_start_var_list_enter_callback(void* context, uint32_t index) { + furi_assert(context); + UART_TerminalApp* app = context; + + furi_assert(index < NUM_MENU_ITEMS); + const UART_TerminalItem* item = &items[index]; + + const int selected_option_index = app->selected_option_index[index]; + furi_assert(selected_option_index < item->num_options_menu); + app->selected_tx_string = item->actual_commands[selected_option_index]; + app->is_command = (1 <= index); + app->is_custom_tx_string = false; + app->selected_menu_index = index; + app->focus_console_start = (item->focus_console == FOCUS_CONSOLE_TOGGLE) ? + (selected_option_index == 0) : + item->focus_console; + app->show_stopscan_tip = item->show_stopscan_tip; + + bool needs_keyboard = (item->needs_keyboard == TOGGLE_ARGS) ? (selected_option_index != 0) : + item->needs_keyboard; + if(needs_keyboard) { + view_dispatcher_send_custom_event(app->view_dispatcher, UART_TerminalEventStartKeyboard); + } else { + view_dispatcher_send_custom_event(app->view_dispatcher, UART_TerminalEventStartConsole); + } +} + +static void uart_terminal_scene_start_var_list_change_callback(VariableItem* item) { + furi_assert(item); + + UART_TerminalApp* app = variable_item_get_context(item); + furi_assert(app); + + const UART_TerminalItem* menu_item = &items[app->selected_menu_index]; + uint8_t item_index = variable_item_get_current_value_index(item); + furi_assert(item_index < menu_item->num_options_menu); + variable_item_set_current_value_text(item, menu_item->options_menu[item_index]); + app->selected_option_index[app->selected_menu_index] = item_index; +} + +void uart_terminal_scene_start_on_enter(void* context) { + UART_TerminalApp* app = context; + VariableItemList* var_item_list = app->var_item_list; + + variable_item_list_set_enter_callback( + var_item_list, uart_terminal_scene_start_var_list_enter_callback, app); + + VariableItem* item; + for(int i = 0; i < NUM_MENU_ITEMS; ++i) { + item = variable_item_list_add( + var_item_list, + items[i].item_string, + items[i].num_options_menu, + uart_terminal_scene_start_var_list_change_callback, + app); + variable_item_set_current_value_index(item, app->selected_option_index[i]); + variable_item_set_current_value_text( + item, items[i].options_menu[app->selected_option_index[i]]); + } + + variable_item_list_set_selected_item( + var_item_list, scene_manager_get_scene_state(app->scene_manager, UART_TerminalSceneStart)); + + view_dispatcher_switch_to_view(app->view_dispatcher, UART_TerminalAppViewVarItemList); +} + +bool uart_terminal_scene_start_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UART_TerminalApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == UART_TerminalEventStartKeyboard) { + scene_manager_set_scene_state( + app->scene_manager, UART_TerminalSceneStart, app->selected_menu_index); + scene_manager_next_scene(app->scene_manager, UART_TerminalAppViewTextInput); + } else if(event.event == UART_TerminalEventStartConsole) { + scene_manager_set_scene_state( + app->scene_manager, UART_TerminalSceneStart, app->selected_menu_index); + scene_manager_next_scene(app->scene_manager, UART_TerminalAppViewConsoleOutput); + } + consumed = true; + } else if(event.type == SceneManagerEventTypeTick) { + app->selected_menu_index = variable_item_list_get_selected_item_index(app->var_item_list); + consumed = true; + } + + return consumed; +} + +void uart_terminal_scene_start_on_exit(void* context) { + UART_TerminalApp* app = context; + variable_item_list_reset(app->var_item_list); +} diff --git a/applications/plugins/uart_terminal/scenes/uart_terminal_scene_text_input.c b/applications/plugins/uart_terminal/scenes/uart_terminal_scene_text_input.c new file mode 100644 index 000000000..62c0792d4 --- /dev/null +++ b/applications/plugins/uart_terminal/scenes/uart_terminal_scene_text_input.c @@ -0,0 +1,60 @@ +#include "../uart_terminal_app_i.h" + +void uart_terminal_scene_text_input_callback(void* context) { + UART_TerminalApp* app = context; + + view_dispatcher_send_custom_event(app->view_dispatcher, UART_TerminalEventStartConsole); +} + +void uart_terminal_scene_text_input_on_enter(void* context) { + UART_TerminalApp* app = context; + + if(false == app->is_custom_tx_string) { + // Fill text input with selected string so that user can add to it + size_t length = strlen(app->selected_tx_string); + furi_assert(length < UART_TERMINAL_TEXT_INPUT_STORE_SIZE); + bzero(app->text_input_store, UART_TERMINAL_TEXT_INPUT_STORE_SIZE); + strncpy(app->text_input_store, app->selected_tx_string, length); + + // Add space - because flipper keyboard currently doesn't have a space + //app->text_input_store[length] = ' '; + app->text_input_store[length + 1] = '\0'; + app->is_custom_tx_string = true; + } + + // Setup view + UART_TextInput* text_input = app->text_input; + // Add help message to header + uart_text_input_set_header_text(text_input, "Send command to UART"); + uart_text_input_set_result_callback( + text_input, + uart_terminal_scene_text_input_callback, + app, + app->text_input_store, + UART_TERMINAL_TEXT_INPUT_STORE_SIZE, + false); + + view_dispatcher_switch_to_view(app->view_dispatcher, UART_TerminalAppViewTextInput); +} + +bool uart_terminal_scene_text_input_on_event(void* context, SceneManagerEvent event) { + UART_TerminalApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == UART_TerminalEventStartConsole) { + // Point to custom string to send + app->selected_tx_string = app->text_input_store; + scene_manager_next_scene(app->scene_manager, UART_TerminalAppViewConsoleOutput); + consumed = true; + } + } + + return consumed; +} + +void uart_terminal_scene_text_input_on_exit(void* context) { + UART_TerminalApp* app = context; + + uart_text_input_reset(app->text_input); +} diff --git a/applications/plugins/uart_terminal/uart_terminal.png b/applications/plugins/uart_terminal/uart_terminal.png new file mode 100644 index 000000000..8420f5692 Binary files /dev/null and b/applications/plugins/uart_terminal/uart_terminal.png differ diff --git a/applications/plugins/uart_terminal/uart_terminal_app.c b/applications/plugins/uart_terminal/uart_terminal_app.c new file mode 100644 index 000000000..2c18c5bae --- /dev/null +++ b/applications/plugins/uart_terminal/uart_terminal_app.c @@ -0,0 +1,104 @@ +#include "uart_terminal_app_i.h" + +#include +#include + +static bool uart_terminal_app_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + UART_TerminalApp* app = context; + return scene_manager_handle_custom_event(app->scene_manager, event); +} + +static bool uart_terminal_app_back_event_callback(void* context) { + furi_assert(context); + UART_TerminalApp* app = context; + return scene_manager_handle_back_event(app->scene_manager); +} + +static void uart_terminal_app_tick_event_callback(void* context) { + furi_assert(context); + UART_TerminalApp* app = context; + scene_manager_handle_tick_event(app->scene_manager); +} + +UART_TerminalApp* uart_terminal_app_alloc() { + UART_TerminalApp* app = malloc(sizeof(UART_TerminalApp)); + + app->gui = furi_record_open(RECORD_GUI); + + app->view_dispatcher = view_dispatcher_alloc(); + app->scene_manager = scene_manager_alloc(&uart_terminal_scene_handlers, app); + view_dispatcher_enable_queue(app->view_dispatcher); + view_dispatcher_set_event_callback_context(app->view_dispatcher, app); + + view_dispatcher_set_custom_event_callback( + app->view_dispatcher, uart_terminal_app_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + app->view_dispatcher, uart_terminal_app_back_event_callback); + view_dispatcher_set_tick_event_callback( + app->view_dispatcher, uart_terminal_app_tick_event_callback, 100); + + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + + app->var_item_list = variable_item_list_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, + UART_TerminalAppViewVarItemList, + variable_item_list_get_view(app->var_item_list)); + + for(int i = 0; i < NUM_MENU_ITEMS; ++i) { + app->selected_option_index[i] = 0; + } + + app->text_box = text_box_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, UART_TerminalAppViewConsoleOutput, text_box_get_view(app->text_box)); + app->text_box_store = furi_string_alloc(); + furi_string_reserve(app->text_box_store, UART_TERMINAL_TEXT_BOX_STORE_SIZE); + + app->text_input = uart_text_input_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, + UART_TerminalAppViewTextInput, + uart_text_input_get_view(app->text_input)); + + scene_manager_next_scene(app->scene_manager, UART_TerminalSceneStart); + + return app; +} + +void uart_terminal_app_free(UART_TerminalApp* app) { + furi_assert(app); + + // Views + view_dispatcher_remove_view(app->view_dispatcher, UART_TerminalAppViewVarItemList); + view_dispatcher_remove_view(app->view_dispatcher, UART_TerminalAppViewConsoleOutput); + view_dispatcher_remove_view(app->view_dispatcher, UART_TerminalAppViewTextInput); + text_box_free(app->text_box); + furi_string_free(app->text_box_store); + uart_text_input_free(app->text_input); + + // View dispatcher + view_dispatcher_free(app->view_dispatcher); + scene_manager_free(app->scene_manager); + + uart_terminal_uart_free(app->uart); + + // Close records + furi_record_close(RECORD_GUI); + + free(app); +} + +int32_t uart_terminal_app(void* p) { + UNUSED(p); + UART_TerminalApp* uart_terminal_app = uart_terminal_app_alloc(); + + uart_terminal_app->uart = uart_terminal_uart_init(uart_terminal_app); + + view_dispatcher_run(uart_terminal_app->view_dispatcher); + + uart_terminal_app_free(uart_terminal_app); + + return 0; +} diff --git a/applications/plugins/uart_terminal/uart_terminal_app.h b/applications/plugins/uart_terminal/uart_terminal_app.h new file mode 100644 index 000000000..c859d828b --- /dev/null +++ b/applications/plugins/uart_terminal/uart_terminal_app.h @@ -0,0 +1,11 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct UART_TerminalApp UART_TerminalApp; + +#ifdef __cplusplus +} +#endif diff --git a/applications/plugins/uart_terminal/uart_terminal_app_i.h b/applications/plugins/uart_terminal/uart_terminal_app_i.h new file mode 100644 index 000000000..6b1996eb5 --- /dev/null +++ b/applications/plugins/uart_terminal/uart_terminal_app_i.h @@ -0,0 +1,49 @@ +#pragma once + +#include "uart_terminal_app.h" +#include "scenes/uart_terminal_scene.h" +#include "uart_terminal_custom_event.h" +#include "uart_terminal_uart.h" + +#include +#include +#include +#include +#include +#include "uart_text_input.h" + +#define NUM_MENU_ITEMS (4) + +#define UART_TERMINAL_TEXT_BOX_STORE_SIZE (4096) +#define UART_TERMINAL_TEXT_INPUT_STORE_SIZE (512) +#define UART_CH (FuriHalUartIdUSART1) + +struct UART_TerminalApp { + Gui* gui; + ViewDispatcher* view_dispatcher; + SceneManager* scene_manager; + + char text_input_store[UART_TERMINAL_TEXT_INPUT_STORE_SIZE + 1]; + FuriString* text_box_store; + size_t text_box_store_strlen; + TextBox* text_box; + UART_TextInput* text_input; + + VariableItemList* var_item_list; + + UART_TerminalUart* uart; + int selected_menu_index; + int selected_option_index[NUM_MENU_ITEMS]; + const char* selected_tx_string; + bool is_command; + bool is_custom_tx_string; + bool focus_console_start; + bool show_stopscan_tip; + int BAUDRATE; +}; + +typedef enum { + UART_TerminalAppViewVarItemList, + UART_TerminalAppViewConsoleOutput, + UART_TerminalAppViewTextInput, +} UART_TerminalAppView; diff --git a/applications/plugins/uart_terminal/uart_terminal_custom_event.h b/applications/plugins/uart_terminal/uart_terminal_custom_event.h new file mode 100644 index 000000000..d57d822d1 --- /dev/null +++ b/applications/plugins/uart_terminal/uart_terminal_custom_event.h @@ -0,0 +1,7 @@ +#pragma once + +typedef enum { + UART_TerminalEventRefreshConsoleOutput = 0, + UART_TerminalEventStartConsole, + UART_TerminalEventStartKeyboard, +} UART_TerminalCustomEvent; diff --git a/applications/plugins/uart_terminal/uart_terminal_uart.c b/applications/plugins/uart_terminal/uart_terminal_uart.c new file mode 100644 index 000000000..136bd5d38 --- /dev/null +++ b/applications/plugins/uart_terminal/uart_terminal_uart.c @@ -0,0 +1,97 @@ +#include "uart_terminal_app_i.h" +#include "uart_terminal_uart.h" + +//#define UART_CH (FuriHalUartIdUSART1) +//#define BAUDRATE (115200) + +struct UART_TerminalUart { + UART_TerminalApp* app; + FuriThread* rx_thread; + FuriStreamBuffer* rx_stream; + uint8_t rx_buf[RX_BUF_SIZE + 1]; + void (*handle_rx_data_cb)(uint8_t* buf, size_t len, void* context); +}; + +typedef enum { + WorkerEvtStop = (1 << 0), + WorkerEvtRxDone = (1 << 1), +} WorkerEvtFlags; + +void uart_terminal_uart_set_handle_rx_data_cb( + UART_TerminalUart* uart, + void (*handle_rx_data_cb)(uint8_t* buf, size_t len, void* context)) { + furi_assert(uart); + uart->handle_rx_data_cb = handle_rx_data_cb; +} + +#define WORKER_ALL_RX_EVENTS (WorkerEvtStop | WorkerEvtRxDone) + +void uart_terminal_uart_on_irq_cb(UartIrqEvent ev, uint8_t data, void* context) { + UART_TerminalUart* uart = (UART_TerminalUart*)context; + + if(ev == UartIrqEventRXNE) { + furi_stream_buffer_send(uart->rx_stream, &data, 1, 0); + furi_thread_flags_set(furi_thread_get_id(uart->rx_thread), WorkerEvtRxDone); + } +} + +static int32_t uart_worker(void* context) { + UART_TerminalUart* uart = (void*)context; + + uart->rx_stream = furi_stream_buffer_alloc(RX_BUF_SIZE, 1); + + while(1) { + uint32_t events = + furi_thread_flags_wait(WORKER_ALL_RX_EVENTS, FuriFlagWaitAny, FuriWaitForever); + furi_check((events & FuriFlagError) == 0); + if(events & WorkerEvtStop) break; + if(events & WorkerEvtRxDone) { + size_t len = furi_stream_buffer_receive(uart->rx_stream, uart->rx_buf, RX_BUF_SIZE, 0); + if(len > 0) { + if(uart->handle_rx_data_cb) uart->handle_rx_data_cb(uart->rx_buf, len, uart->app); + } + } + } + + furi_stream_buffer_free(uart->rx_stream); + + return 0; +} + +void uart_terminal_uart_tx(uint8_t* data, size_t len) { + furi_hal_uart_tx(UART_CH, data, len); +} + +UART_TerminalUart* uart_terminal_uart_init(UART_TerminalApp* app) { + UART_TerminalUart* uart = malloc(sizeof(UART_TerminalUart)); + + furi_hal_console_disable(); + if(app->BAUDRATE == 0) { + app->BAUDRATE = 115200; + } + furi_hal_uart_set_br(UART_CH, app->BAUDRATE); + furi_hal_uart_set_irq_cb(UART_CH, uart_terminal_uart_on_irq_cb, uart); + + uart->app = app; + uart->rx_thread = furi_thread_alloc(); + furi_thread_set_name(uart->rx_thread, "UART_TerminalUartRxThread"); + furi_thread_set_stack_size(uart->rx_thread, 1024); + furi_thread_set_context(uart->rx_thread, uart); + furi_thread_set_callback(uart->rx_thread, uart_worker); + + furi_thread_start(uart->rx_thread); + return uart; +} + +void uart_terminal_uart_free(UART_TerminalUart* uart) { + furi_assert(uart); + + furi_thread_flags_set(furi_thread_get_id(uart->rx_thread), WorkerEvtStop); + furi_thread_join(uart->rx_thread); + furi_thread_free(uart->rx_thread); + + furi_hal_uart_set_irq_cb(UART_CH, NULL, NULL); + furi_hal_console_enable(); + + free(uart); +} \ No newline at end of file diff --git a/applications/plugins/uart_terminal/uart_terminal_uart.h b/applications/plugins/uart_terminal/uart_terminal_uart.h new file mode 100644 index 000000000..ca95c92fb --- /dev/null +++ b/applications/plugins/uart_terminal/uart_terminal_uart.h @@ -0,0 +1,14 @@ +#pragma once + +#include "furi_hal.h" + +#define RX_BUF_SIZE (320) + +typedef struct UART_TerminalUart UART_TerminalUart; + +void uart_terminal_uart_set_handle_rx_data_cb( + UART_TerminalUart* uart, + void (*handle_rx_data_cb)(uint8_t* buf, size_t len, void* context)); +void uart_terminal_uart_tx(uint8_t* data, size_t len); +UART_TerminalUart* uart_terminal_uart_init(UART_TerminalApp* app); +void uart_terminal_uart_free(UART_TerminalUart* uart); diff --git a/applications/plugins/uart_terminal/uart_text_input.c b/applications/plugins/uart_terminal/uart_text_input.c new file mode 100644 index 000000000..4a571b127 --- /dev/null +++ b/applications/plugins/uart_terminal/uart_text_input.c @@ -0,0 +1,637 @@ +#include "uart_text_input.h" +#include +#include "uart_terminal_icons.h" +#include + +struct UART_TextInput { + View* view; + FuriTimer* timer; +}; + +typedef struct { + const char text; + const uint8_t x; + const uint8_t y; +} UART_TextInputKey; + +typedef struct { + const char* header; + char* text_buffer; + size_t text_buffer_size; + bool clear_default_text; + + UART_TextInputCallback callback; + void* callback_context; + + uint8_t selected_row; + uint8_t selected_column; + + UART_TextInputValidatorCallback validator_callback; + void* validator_callback_context; + FuriString* validator_text; + bool valadator_message_visible; +} UART_TextInputModel; + +static const uint8_t keyboard_origin_x = 1; +static const uint8_t keyboard_origin_y = 29; +static const uint8_t keyboard_row_count = 4; + +#define ENTER_KEY '\r' +#define BACKSPACE_KEY '\b' + +static const UART_TextInputKey keyboard_keys_row_1[] = { + {'{', 1, 0}, + {'(', 9, 0}, + {'[', 17, 0}, + {'|', 25, 0}, + {'@', 33, 0}, + {'&', 41, 0}, + {'#', 49, 0}, + {';', 57, 0}, + {'^', 65, 0}, + {'*', 73, 0}, + {'`', 81, 0}, + {'"', 89, 0}, + {'~', 97, 0}, + {'\'', 105, 0}, + {'.', 113, 0}, + {'/', 120, 0}, +}; + +static const UART_TextInputKey keyboard_keys_row_2[] = { + {'q', 1, 10}, + {'w', 9, 10}, + {'e', 17, 10}, + {'r', 25, 10}, + {'t', 33, 10}, + {'y', 41, 10}, + {'u', 49, 10}, + {'i', 57, 10}, + {'o', 65, 10}, + {'p', 73, 10}, + {'0', 81, 10}, + {'1', 89, 10}, + {'2', 97, 10}, + {'3', 105, 10}, + {'=', 113, 10}, + {'-', 120, 10}, +}; + +static const UART_TextInputKey keyboard_keys_row_3[] = { + {'a', 1, 21}, + {'s', 9, 21}, + {'d', 18, 21}, + {'f', 25, 21}, + {'g', 33, 21}, + {'h', 41, 21}, + {'j', 49, 21}, + {'k', 57, 21}, + {'l', 65, 21}, + {BACKSPACE_KEY, 72, 13}, + {'4', 89, 21}, + {'5', 97, 21}, + {'6', 105, 21}, + {'$', 113, 21}, + {'%', 120, 21}, + +}; + +static const UART_TextInputKey keyboard_keys_row_4[] = { + {'z', 1, 33}, + {'x', 9, 33}, + {'c', 18, 33}, + {'v', 25, 33}, + {'b', 33, 33}, + {'n', 41, 33}, + {'m', 49, 33}, + {'_', 57, 33}, + {ENTER_KEY, 64, 24}, + {'7', 89, 33}, + {'8', 97, 33}, + {'9', 105, 33}, + {'!', 113, 33}, + {'+', 120, 33}, +}; + +static uint8_t get_row_size(uint8_t row_index) { + uint8_t row_size = 0; + + switch(row_index + 1) { + case 1: + row_size = sizeof(keyboard_keys_row_1) / sizeof(UART_TextInputKey); + break; + case 2: + row_size = sizeof(keyboard_keys_row_2) / sizeof(UART_TextInputKey); + break; + case 3: + row_size = sizeof(keyboard_keys_row_3) / sizeof(UART_TextInputKey); + break; + case 4: + row_size = sizeof(keyboard_keys_row_4) / sizeof(UART_TextInputKey); + break; + } + + return row_size; +} + +static const UART_TextInputKey* get_row(uint8_t row_index) { + const UART_TextInputKey* row = NULL; + + switch(row_index + 1) { + case 1: + row = keyboard_keys_row_1; + break; + case 2: + row = keyboard_keys_row_2; + break; + case 3: + row = keyboard_keys_row_3; + break; + case 4: + row = keyboard_keys_row_4; + break; + } + + return row; +} + +static char get_selected_char(UART_TextInputModel* model) { + return get_row(model->selected_row)[model->selected_column].text; +} + +static bool char_is_lowercase(char letter) { + return (letter >= 0x61 && letter <= 0x7A); +} + +static char char_to_uppercase(const char letter) { + switch(letter) { + case '_': + return 0x20; + break; + case '(': + return 0x29; + break; + case '{': + return 0x7d; + break; + case '[': + return 0x5d; + break; + case '/': + return 0x5c; + break; + case ';': + return 0x3a; + break; + case '.': + return 0x2c; + break; + case '!': + return 0x3f; + break; + case '<': + return 0x3e; + break; + } + if(isalpha(letter)) { + return (letter - 0x20); + } else { + return letter; + } +} + +static void uart_text_input_backspace_cb(UART_TextInputModel* model) { + uint8_t text_length = model->clear_default_text ? 1 : strlen(model->text_buffer); + if(text_length > 0) { + model->text_buffer[text_length - 1] = 0; + } +} + +static void uart_text_input_view_draw_callback(Canvas* canvas, void* _model) { + UART_TextInputModel* model = _model; + uint8_t text_length = model->text_buffer ? strlen(model->text_buffer) : 0; + uint8_t needed_string_width = canvas_width(canvas) - 8; + uint8_t start_pos = 4; + + const char* text = model->text_buffer; + + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + + canvas_draw_str(canvas, 2, 7, model->header); + elements_slightly_rounded_frame(canvas, 1, 8, 126, 12); + + if(canvas_string_width(canvas, text) > needed_string_width) { + canvas_draw_str(canvas, start_pos, 17, "..."); + start_pos += 6; + needed_string_width -= 8; + } + + while(text != 0 && canvas_string_width(canvas, text) > needed_string_width) { + text++; + } + + if(model->clear_default_text) { + elements_slightly_rounded_box( + canvas, start_pos - 1, 14, canvas_string_width(canvas, text) + 2, 10); + canvas_set_color(canvas, ColorWhite); + } else { + canvas_draw_str(canvas, start_pos + canvas_string_width(canvas, text) + 1, 18, "|"); + canvas_draw_str(canvas, start_pos + canvas_string_width(canvas, text) + 2, 18, "|"); + } + canvas_draw_str(canvas, start_pos, 17, text); + + canvas_set_font(canvas, FontKeyboard); + + for(uint8_t row = 0; row <= keyboard_row_count; row++) { + const uint8_t column_count = get_row_size(row); + const UART_TextInputKey* keys = get_row(row); + + for(size_t column = 0; column < column_count; column++) { + if(keys[column].text == ENTER_KEY) { + canvas_set_color(canvas, ColorBlack); + if(model->selected_row == row && model->selected_column == column) { + canvas_draw_icon( + canvas, + keyboard_origin_x + keys[column].x, + keyboard_origin_y + keys[column].y, + &I_KeySaveSelected_24x11); + } else { + canvas_draw_icon( + canvas, + keyboard_origin_x + keys[column].x, + keyboard_origin_y + keys[column].y, + &I_KeySave_24x11); + } + } else if(keys[column].text == BACKSPACE_KEY) { + canvas_set_color(canvas, ColorBlack); + if(model->selected_row == row && model->selected_column == column) { + canvas_draw_icon( + canvas, + keyboard_origin_x + keys[column].x, + keyboard_origin_y + keys[column].y, + &I_KeyBackspaceSelected_16x9); + } else { + canvas_draw_icon( + canvas, + keyboard_origin_x + keys[column].x, + keyboard_origin_y + keys[column].y, + &I_KeyBackspace_16x9); + } + } else { + if(model->selected_row == row && model->selected_column == column) { + canvas_set_color(canvas, ColorBlack); + canvas_draw_box( + canvas, + keyboard_origin_x + keys[column].x - 1, + keyboard_origin_y + keys[column].y - 8, + 7, + 10); + canvas_set_color(canvas, ColorWhite); + } else { + canvas_set_color(canvas, ColorBlack); + } + + if(model->clear_default_text || + (text_length == 0 && char_is_lowercase(keys[column].text))) { + canvas_draw_glyph( + canvas, + keyboard_origin_x + keys[column].x, + keyboard_origin_y + keys[column].y, + //char_to_uppercase(keys[column].text)); + keys[column].text); + } else { + canvas_draw_glyph( + canvas, + keyboard_origin_x + keys[column].x, + keyboard_origin_y + keys[column].y, + keys[column].text); + } + } + } + } + if(model->valadator_message_visible) { + canvas_set_font(canvas, FontSecondary); + canvas_set_color(canvas, ColorWhite); + canvas_draw_box(canvas, 8, 10, 110, 48); + canvas_set_color(canvas, ColorBlack); + canvas_draw_icon(canvas, 10, 14, &I_WarningDolphin_45x42); + canvas_draw_rframe(canvas, 8, 8, 112, 50, 3); + canvas_draw_rframe(canvas, 9, 9, 110, 48, 2); + elements_multiline_text(canvas, 62, 20, furi_string_get_cstr(model->validator_text)); + canvas_set_font(canvas, FontKeyboard); + } +} + +static void + uart_text_input_handle_up(UART_TextInput* uart_text_input, UART_TextInputModel* model) { + UNUSED(uart_text_input); + if(model->selected_row > 0) { + model->selected_row--; + if(model->selected_column > get_row_size(model->selected_row) - 6) { + model->selected_column = model->selected_column + 1; + } + } +} + +static void + uart_text_input_handle_down(UART_TextInput* uart_text_input, UART_TextInputModel* model) { + UNUSED(uart_text_input); + if(model->selected_row < keyboard_row_count - 1) { + model->selected_row++; + if(model->selected_column > get_row_size(model->selected_row) - 4) { + model->selected_column = model->selected_column - 1; + } + } +} + +static void + uart_text_input_handle_left(UART_TextInput* uart_text_input, UART_TextInputModel* model) { + UNUSED(uart_text_input); + if(model->selected_column > 0) { + model->selected_column--; + } else { + model->selected_column = get_row_size(model->selected_row) - 1; + } +} + +static void + uart_text_input_handle_right(UART_TextInput* uart_text_input, UART_TextInputModel* model) { + UNUSED(uart_text_input); + if(model->selected_column < get_row_size(model->selected_row) - 1) { + model->selected_column++; + } else { + model->selected_column = 0; + } +} + +static void uart_text_input_handle_ok( + UART_TextInput* uart_text_input, + UART_TextInputModel* model, + bool shift) { + char selected = get_selected_char(model); + uint8_t text_length = strlen(model->text_buffer); + + if(shift) { + selected = char_to_uppercase(selected); + } + + if(selected == ENTER_KEY) { + if(model->validator_callback && + (!model->validator_callback( + model->text_buffer, model->validator_text, model->validator_callback_context))) { + model->valadator_message_visible = true; + furi_timer_start(uart_text_input->timer, furi_kernel_get_tick_frequency() * 4); + } else if(model->callback != 0 && text_length > 0) { + model->callback(model->callback_context); + } + } else if(selected == BACKSPACE_KEY) { + uart_text_input_backspace_cb(model); + } else { + if(model->clear_default_text) { + text_length = 0; + } + if(text_length < (model->text_buffer_size - 1)) { + if(text_length == 0 && char_is_lowercase(selected)) { + //selected = char_to_uppercase(selected); + } + model->text_buffer[text_length] = selected; + model->text_buffer[text_length + 1] = 0; + } + } + model->clear_default_text = false; +} + +static bool uart_text_input_view_input_callback(InputEvent* event, void* context) { + UART_TextInput* uart_text_input = context; + furi_assert(uart_text_input); + + bool consumed = false; + + // Acquire model + UART_TextInputModel* model = view_get_model(uart_text_input->view); + + if((!(event->type == InputTypePress) && !(event->type == InputTypeRelease)) && + model->valadator_message_visible) { + model->valadator_message_visible = false; + consumed = true; + } else if(event->type == InputTypeShort) { + consumed = true; + switch(event->key) { + case InputKeyUp: + uart_text_input_handle_up(uart_text_input, model); + break; + case InputKeyDown: + uart_text_input_handle_down(uart_text_input, model); + break; + case InputKeyLeft: + uart_text_input_handle_left(uart_text_input, model); + break; + case InputKeyRight: + uart_text_input_handle_right(uart_text_input, model); + break; + case InputKeyOk: + uart_text_input_handle_ok(uart_text_input, model, false); + break; + default: + consumed = false; + break; + } + } else if(event->type == InputTypeLong) { + consumed = true; + switch(event->key) { + case InputKeyUp: + uart_text_input_handle_up(uart_text_input, model); + break; + case InputKeyDown: + uart_text_input_handle_down(uart_text_input, model); + break; + case InputKeyLeft: + uart_text_input_handle_left(uart_text_input, model); + break; + case InputKeyRight: + uart_text_input_handle_right(uart_text_input, model); + break; + case InputKeyOk: + uart_text_input_handle_ok(uart_text_input, model, true); + break; + case InputKeyBack: + uart_text_input_backspace_cb(model); + break; + default: + consumed = false; + break; + } + } else if(event->type == InputTypeRepeat) { + consumed = true; + switch(event->key) { + case InputKeyUp: + uart_text_input_handle_up(uart_text_input, model); + break; + case InputKeyDown: + uart_text_input_handle_down(uart_text_input, model); + break; + case InputKeyLeft: + uart_text_input_handle_left(uart_text_input, model); + break; + case InputKeyRight: + uart_text_input_handle_right(uart_text_input, model); + break; + case InputKeyBack: + uart_text_input_backspace_cb(model); + break; + default: + consumed = false; + break; + } + } + + // Commit model + view_commit_model(uart_text_input->view, consumed); + + return consumed; +} + +void uart_text_input_timer_callback(void* context) { + furi_assert(context); + UART_TextInput* uart_text_input = context; + + with_view_model( + uart_text_input->view, + UART_TextInputModel * model, + { model->valadator_message_visible = false; }, + true); +} + +UART_TextInput* uart_text_input_alloc() { + UART_TextInput* uart_text_input = malloc(sizeof(UART_TextInput)); + uart_text_input->view = view_alloc(); + view_set_context(uart_text_input->view, uart_text_input); + view_allocate_model(uart_text_input->view, ViewModelTypeLocking, sizeof(UART_TextInputModel)); + view_set_draw_callback(uart_text_input->view, uart_text_input_view_draw_callback); + view_set_input_callback(uart_text_input->view, uart_text_input_view_input_callback); + + uart_text_input->timer = + furi_timer_alloc(uart_text_input_timer_callback, FuriTimerTypeOnce, uart_text_input); + + with_view_model( + uart_text_input->view, + UART_TextInputModel * model, + { model->validator_text = furi_string_alloc(); }, + false); + + uart_text_input_reset(uart_text_input); + + return uart_text_input; +} + +void uart_text_input_free(UART_TextInput* uart_text_input) { + furi_assert(uart_text_input); + with_view_model( + uart_text_input->view, + UART_TextInputModel * model, + { furi_string_free(model->validator_text); }, + false); + + // Send stop command + furi_timer_stop(uart_text_input->timer); + // Release allocated memory + furi_timer_free(uart_text_input->timer); + + view_free(uart_text_input->view); + + free(uart_text_input); +} + +void uart_text_input_reset(UART_TextInput* uart_text_input) { + furi_assert(uart_text_input); + with_view_model( + uart_text_input->view, + UART_TextInputModel * model, + { + model->text_buffer_size = 0; + model->header = ""; + model->selected_row = 0; + model->selected_column = 0; + model->clear_default_text = false; + model->text_buffer = NULL; + model->text_buffer_size = 0; + model->callback = NULL; + model->callback_context = NULL; + model->validator_callback = NULL; + model->validator_callback_context = NULL; + furi_string_reset(model->validator_text); + model->valadator_message_visible = false; + }, + true); +} + +View* uart_text_input_get_view(UART_TextInput* uart_text_input) { + furi_assert(uart_text_input); + return uart_text_input->view; +} + +void uart_text_input_set_result_callback( + UART_TextInput* uart_text_input, + UART_TextInputCallback callback, + void* callback_context, + char* text_buffer, + size_t text_buffer_size, + bool clear_default_text) { + with_view_model( + uart_text_input->view, + UART_TextInputModel * model, + { + model->callback = callback; + model->callback_context = callback_context; + model->text_buffer = text_buffer; + model->text_buffer_size = text_buffer_size; + model->clear_default_text = clear_default_text; + if(text_buffer && text_buffer[0] != '\0') { + // Set focus on Save + model->selected_row = 2; + model->selected_column = 8; + } + }, + true); +} + +void uart_text_input_set_validator( + UART_TextInput* uart_text_input, + UART_TextInputValidatorCallback callback, + void* callback_context) { + with_view_model( + uart_text_input->view, + UART_TextInputModel * model, + { + model->validator_callback = callback; + model->validator_callback_context = callback_context; + }, + true); +} + +UART_TextInputValidatorCallback + uart_text_input_get_validator_callback(UART_TextInput* uart_text_input) { + UART_TextInputValidatorCallback validator_callback = NULL; + with_view_model( + uart_text_input->view, + UART_TextInputModel * model, + { validator_callback = model->validator_callback; }, + false); + return validator_callback; +} + +void* uart_text_input_get_validator_callback_context(UART_TextInput* uart_text_input) { + void* validator_callback_context = NULL; + with_view_model( + uart_text_input->view, + UART_TextInputModel * model, + { validator_callback_context = model->validator_callback_context; }, + false); + return validator_callback_context; +} + +void uart_text_input_set_header_text(UART_TextInput* uart_text_input, const char* text) { + with_view_model( + uart_text_input->view, UART_TextInputModel * model, { model->header = text; }, true); +} diff --git a/applications/plugins/uart_terminal/uart_text_input.h b/applications/plugins/uart_terminal/uart_text_input.h new file mode 100644 index 000000000..f3703ed5a --- /dev/null +++ b/applications/plugins/uart_terminal/uart_text_input.h @@ -0,0 +1,82 @@ +#pragma once + +#include +#include "uart_validators.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Text input anonymous structure */ +typedef struct UART_TextInput UART_TextInput; +typedef void (*UART_TextInputCallback)(void* context); +typedef bool (*UART_TextInputValidatorCallback)(const char* text, FuriString* error, void* context); + +/** Allocate and initialize text input + * + * This text input is used to enter string + * + * @return UART_TextInput instance + */ +UART_TextInput* uart_text_input_alloc(); + +/** Deinitialize and free text input + * + * @param uart_text_input UART_TextInput instance + */ +void uart_text_input_free(UART_TextInput* uart_text_input); + +/** Clean text input view Note: this function does not free memory + * + * @param uart_text_input Text input instance + */ +void uart_text_input_reset(UART_TextInput* uart_text_input); + +/** Get text input view + * + * @param uart_text_input UART_TextInput instance + * + * @return View instance that can be used for embedding + */ +View* uart_text_input_get_view(UART_TextInput* uart_text_input); + +/** Set text input result callback + * + * @param uart_text_input UART_TextInput instance + * @param callback callback fn + * @param callback_context callback context + * @param text_buffer pointer to YOUR text buffer, that we going + * to modify + * @param text_buffer_size YOUR text buffer size in bytes. Max string + * length will be text_buffer_size-1. + * @param clear_default_text clear text from text_buffer on first OK + * event + */ +void uart_text_input_set_result_callback( + UART_TextInput* uart_text_input, + UART_TextInputCallback callback, + void* callback_context, + char* text_buffer, + size_t text_buffer_size, + bool clear_default_text); + +void uart_text_input_set_validator( + UART_TextInput* uart_text_input, + UART_TextInputValidatorCallback callback, + void* callback_context); + +UART_TextInputValidatorCallback + uart_text_input_get_validator_callback(UART_TextInput* uart_text_input); + +void* uart_text_input_get_validator_callback_context(UART_TextInput* uart_text_input); + +/** Set text input header text + * + * @param uart_text_input UART_TextInput instance + * @param text text to be shown + */ +void uart_text_input_set_header_text(UART_TextInput* uart_text_input, const char* text); + +#ifdef __cplusplus +} +#endif diff --git a/applications/plugins/uart_terminal/uart_validators.c b/applications/plugins/uart_terminal/uart_validators.c new file mode 100644 index 000000000..c87a6cc6e --- /dev/null +++ b/applications/plugins/uart_terminal/uart_validators.c @@ -0,0 +1,57 @@ +#include +#include "uart_validators.h" +#include + +struct ValidatorIsFile { + char* app_path_folder; + const char* app_extension; + char* current_name; +}; + +bool validator_is_file_callback(const char* text, FuriString* error, void* context) { + furi_assert(context); + ValidatorIsFile* instance = context; + + if(instance->current_name != NULL) { + if(strcmp(instance->current_name, text) == 0) { + return true; + } + } + + 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; + furi_string_printf(error, "This name\nexists!\nChoose\nanother one."); + } else { + ret = true; + } + furi_string_free(path); + furi_record_close(RECORD_STORAGE); + + return ret; +} + +ValidatorIsFile* validator_is_file_alloc_init( + const char* app_path_folder, + const char* app_extension, + const char* current_name) { + ValidatorIsFile* instance = malloc(sizeof(ValidatorIsFile)); + + instance->app_path_folder = strdup(app_path_folder); + instance->app_extension = app_extension; + if(current_name != NULL) { + instance->current_name = strdup(current_name); + } + + return instance; +} + +void validator_is_file_free(ValidatorIsFile* instance) { + furi_assert(instance); + free(instance->app_path_folder); + free(instance->current_name); + free(instance); +} diff --git a/applications/plugins/uart_terminal/uart_validators.h b/applications/plugins/uart_terminal/uart_validators.h new file mode 100644 index 000000000..d9200b6db --- /dev/null +++ b/applications/plugins/uart_terminal/uart_validators.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif +typedef struct ValidatorIsFile ValidatorIsFile; + +ValidatorIsFile* validator_is_file_alloc_init( + const char* app_path_folder, + const char* app_extension, + const char* current_name); + +void validator_is_file_free(ValidatorIsFile* instance); + +bool validator_is_file_callback(const char* text, FuriString* error, void* context); + +#ifdef __cplusplus +} +#endif diff --git a/applications/plugins/unitemp/README.md b/applications/plugins/unitemp/README.md index 436f3600e..2da78d2bd 100644 --- a/applications/plugins/unitemp/README.md +++ b/applications/plugins/unitemp/README.md @@ -13,7 +13,7 @@ 3) Open application on your Flipper: `Applications->GPIO->Temp sensors reader` Note: If you get the message "API version mismatch" after updating the firmware, download and install Unitemp again ## Need help? Discussions? -Join the discussion, ask a question or just send a photo of the flipper with sensors to [Discord](https://discord.com/channels/740930220399525928/1056727938747351060) +Join the discussion, ask a question or just send a photo of the flipper with sensors to [Discord](https://discord.com/channels/740930220399525928/1056727938747351060). [Invite link](https://discord.com/invite/flipper) ## Gratitudes Thanks to [@Svaarich](https://github.com/Svaarich) for the UI design and to the Unleashed firmware community for sensors testing and feedbacks. @@ -21,4 +21,4 @@ Thanks to [@Svaarich](https://github.com/Svaarich) for the UI design and to the ![image](https://user-images.githubusercontent.com/10090793/210120132-7ddbc937-0a6b-4472-bd1c-7fbc3ecdf2ad.png) ![image](https://user-images.githubusercontent.com/10090793/210120135-12fc5810-77ff-49db-b799-e9479e1f57a7.png) ![image](https://user-images.githubusercontent.com/10090793/210120143-a2bae3ce-4190-421f-8c4f-c7c744903bd6.png) -![image](https://user-images.githubusercontent.com/10090793/215224085-8099408e-b3de-4a0c-854e-fe4e4faa8ea3.png) \ No newline at end of file +![image](https://user-images.githubusercontent.com/10090793/215224085-8099408e-b3de-4a0c-854e-fe4e4faa8ea3.png) diff --git a/applications/plugins/unitemp/Sensors.c b/applications/plugins/unitemp/Sensors.c index d9304ab32..30419a929 100644 --- a/applications/plugins/unitemp/Sensors.c +++ b/applications/plugins/unitemp/Sensors.c @@ -149,8 +149,7 @@ uint8_t unitemp_gpio_getAviablePortsCount(const Interface* interface, const GPIO for(uint8_t i = 0; i < GPIO_ITEMS; i++) { //Проверка для one wire if(interface == &ONE_WIRE) { - if(((gpio_interfaces_list[i] == NULL || gpio_interfaces_list[i] == &ONE_WIRE) && - (i != 12)) || //Почему-то не работает на 17 порте + if(((gpio_interfaces_list[i] == NULL || gpio_interfaces_list[i] == &ONE_WIRE)) || (unitemp_gpio_getFromIndex(i) == extraport)) { aviable_ports_count++; } @@ -208,9 +207,7 @@ const GPIO* for(uint8_t i = 0; i < GPIO_ITEMS; i++) { //Проверка для one wire if(interface == &ONE_WIRE) { - //Почему-то не работает на 17 порте - if(((gpio_interfaces_list[i] == NULL || gpio_interfaces_list[i] == &ONE_WIRE) && - (i != 12)) || //Почему-то не работает на 17 порте + if(((gpio_interfaces_list[i] == NULL || gpio_interfaces_list[i] == &ONE_WIRE)) || (unitemp_gpio_getFromIndex(i) == extraport)) { if(aviable_index == index) { return unitemp_gpio_getFromIndex(i); diff --git a/applications/plugins/unitemp/interfaces/OneWireSensor.c b/applications/plugins/unitemp/interfaces/OneWireSensor.c index f4f3ebcdc..377eb0d08 100644 --- a/applications/plugins/unitemp/interfaces/OneWireSensor.c +++ b/applications/plugins/unitemp/interfaces/OneWireSensor.c @@ -20,7 +20,6 @@ #include "OneWireSensor.h" #include #include -#include const SensorType Dallas = { .typename = "Dallas", @@ -37,8 +36,6 @@ const SensorType Dallas = { // Переменные для хранения промежуточного результата сканирования шины // найденный восьмибайтовый адрес static uint8_t onewire_enum[8] = {0}; -// последний нулевой бит, где была неоднозначность (нумеруя с единицы) -static uint8_t onewire_enum_fork_bit = 65; OneWireBus* uintemp_onewire_bus_alloc(const GPIO* gpio) { if(gpio == NULL) { @@ -55,9 +52,11 @@ OneWireBus* uintemp_onewire_bus_alloc(const GPIO* gpio) { } OneWireBus* bus = malloc(sizeof(OneWireBus)); + bus->device_count = 0; bus->gpio = gpio; bus->powerMode = PWR_PASSIVE; + UNITEMP_DEBUG("one wire bus (port %d) allocated", gpio->num); return bus; @@ -69,6 +68,8 @@ bool unitemp_onewire_bus_init(OneWireBus* bus) { //Выход если шина уже была инициализирована if(bus->device_count > 1) return true; + bus->host = onewire_host_alloc(bus->gpio->pin); + unitemp_gpio_lock(bus->gpio, &ONE_WIRE); //Высокий уровень по умолчанию furi_hal_gpio_write(bus->gpio->pin, true); @@ -81,6 +82,7 @@ bool unitemp_onewire_bus_init(OneWireBus* bus) { return true; } + bool unitemp_onewire_bus_deinit(OneWireBus* bus) { UNITEMP_DEBUG("devices on wire %d: %d", bus->gpio->num, bus->device_count); bus->device_count--; @@ -100,84 +102,34 @@ bool unitemp_onewire_bus_deinit(OneWireBus* bus) { return false; } } -bool unitemp_onewire_bus_start(OneWireBus* bus) { - furi_hal_gpio_write(bus->gpio->pin, false); - furi_delay_us(500); - - furi_hal_gpio_write(bus->gpio->pin, true); - - //Ожидание подъёма шины - uint32_t t = furi_get_tick(); - while(!furi_hal_gpio_read(bus->gpio->pin)) { - //Выход если шина не поднялась - if(furi_get_tick() - t > 10) return false; - } - - furi_delay_us(100); - bool status = !furi_hal_gpio_read(bus->gpio->pin); - furi_delay_us(400); - return status; +inline bool unitemp_onewire_bus_start(OneWireBus* bus) { + return onewire_host_reset(bus->host); } -void unitemp_onewire_bus_send_bit(OneWireBus* bus, bool state) { - //Необходимо для стабильной работы при пассивном питании - if(bus->powerMode == PWR_PASSIVE) furi_delay_us(100); - - if(state) { - // write 1 - furi_hal_gpio_write(bus->gpio->pin, false); - furi_delay_us(1); - furi_hal_gpio_write(bus->gpio->pin, true); - furi_delay_us(90); - } else { - furi_hal_gpio_write(bus->gpio->pin, false); - furi_delay_us(90); - furi_hal_gpio_write(bus->gpio->pin, true); - //Ожидание подъёма шины - uint32_t t = furi_get_tick(); - while(!furi_hal_gpio_read(bus->gpio->pin)) { - //Выход если шина не поднялась - if(furi_get_tick() - t > 10) return; - } - } +inline void unitemp_onewire_bus_send_bit(OneWireBus* bus, bool state) { + onewire_host_write_bit(bus->host, state); } -void unitemp_onewire_bus_send_byte(OneWireBus* bus, uint8_t data) { - for(int i = 0; i < 8; i++) { - unitemp_onewire_bus_send_bit(bus, (data & (1 << i)) != 0); - } +inline void unitemp_onewire_bus_send_byte(OneWireBus* bus, uint8_t data) { + onewire_host_write(bus->host, data); } void unitemp_onewire_bus_send_byteArray(OneWireBus* bus, uint8_t* data, uint8_t len) { for(uint8_t i = 0; i < len; i++) { - unitemp_onewire_bus_send_byte(bus, data[i]); + onewire_host_write(bus->host, data[i]); } } -bool unitemp_onewire_bus_read_bit(OneWireBus* bus) { - furi_delay_ms(1); - furi_hal_gpio_write(bus->gpio->pin, false); - furi_delay_us(2); // Длительность низкого уровня, минимум 1 мкс - furi_hal_gpio_write(bus->gpio->pin, true); - furi_delay_us(8); // Пауза до момента сэмплирования, всего не более 15 мкс - bool r = furi_hal_gpio_read(bus->gpio->pin); - furi_delay_us(80); // Ожидание до следующего тайм-слота, минимум 60 мкс с начала низкого уровня - return r; +inline bool unitemp_onewire_bus_read_bit(OneWireBus* bus) { + return onewire_host_read_bit(bus->host); } -uint8_t unitemp_onewire_bus_read_byte(OneWireBus* bus) { - uint8_t r = 0; - for(uint8_t p = 8; p; p--) { - r >>= 1; - if(unitemp_onewire_bus_read_bit(bus)) r |= 0x80; - } - return r; +inline uint8_t unitemp_onewire_bus_read_byte(OneWireBus* bus) { + return onewire_host_read(bus->host); } void unitemp_onewire_bus_read_byteArray(OneWireBus* bus, uint8_t* data, uint8_t len) { - for(uint8_t i = 0; i < len; i++) { - data[i] = unitemp_onewire_bus_read_byte(bus); - } + onewire_host_read_bytes(bus->host, data, len); } static uint8_t onewire_CRC_update(uint8_t crc, uint8_t b) { @@ -222,77 +174,16 @@ bool unitemp_onewire_sensor_readID(OneWireSensor* instance) { return true; } -void unitemp_onewire_bus_enum_init(void) { - for(uint8_t p = 0; p < 8; p++) { - onewire_enum[p] = 0; - } - onewire_enum_fork_bit = 65; // правее правого +void unitemp_onewire_bus_enum_init(OneWireBus* bus) { + onewire_host_reset_search(bus->host); } uint8_t* unitemp_onewire_bus_enum_next(OneWireBus* bus) { - furi_delay_ms(10); - if(!onewire_enum_fork_bit) { // Если на предыдущем шаге уже не было разногласий - UNITEMP_DEBUG("All devices on wire %d is found", unitemp_gpio_toInt(bus->gpio)); - return 0; // то просто выходим ничего не возвращая + if(onewire_host_search(bus->host, onewire_enum, OneWireHostSearchModeNormal)) { + return onewire_enum; + } else { + return NULL; } - if(!unitemp_onewire_bus_start(bus)) { - UNITEMP_DEBUG("Wire %d is empty", unitemp_gpio_toInt(bus->gpio)); - return 0; - } - uint8_t bp = 8; - uint8_t* pprev = &onewire_enum[0]; - uint8_t prev = *pprev; - uint8_t next = 0; - - uint8_t p = 1; - unitemp_onewire_bus_send_byte(bus, 0xF0); - uint8_t newfork = 0; - for(;;) { - uint8_t not0 = unitemp_onewire_bus_read_bit(bus); - uint8_t not1 = unitemp_onewire_bus_read_bit(bus); - if(!not0) { // Если присутствует в адресах бит ноль - if(!not1) { // Но также присустствует бит 1 (вилка) - if(p < - onewire_enum_fork_bit) { // Если мы левее прошлого правого конфликтного бита, - if(prev & 1) { - next |= 0x80; // то копируем значение бита из прошлого прохода - } else { - newfork = p; // если ноль, то запомним конфликтное место - } - } else if(p == onewire_enum_fork_bit) { - next |= - 0x80; // если на этом месте в прошлый раз был правый конфликт с нулём, выведем 1 - } else { - newfork = p; // правее - передаём ноль и запоминаем конфликтное место - } - } // в противном случае идём, выбирая ноль в адресе - } else { - if(!not1) { // Присутствует единица - next |= 0x80; - } else { // Нет ни нулей ни единиц - ошибочная ситуация - - UNITEMP_DEBUG("Wrong wire %d situation", unitemp_gpio_toInt(bus->gpio)); - return 0; - } - } - unitemp_onewire_bus_send_bit(bus, next & 0x80); - bp--; - if(!bp) { - *pprev = next; - if(p >= 64) break; - next = 0; - pprev++; - prev = *pprev; - bp = 8; - } else { - if(p >= 64) break; - prev >>= 1; - next >>= 1; - } - p++; - } - onewire_enum_fork_bit = newfork; - return &onewire_enum[0]; } void unitemp_onewire_bus_select_sensor(OneWireSensor* instance) { @@ -364,7 +255,6 @@ bool unitemp_onewire_sensor_init(Sensor* sensor) { } unitemp_onewire_bus_init(instance->bus); - furi_delay_ms(1); if(instance->familyCode == FC_DS18B20 || instance->familyCode == FC_DS1822) { //Установка разрядности в 10 бит diff --git a/applications/plugins/unitemp/interfaces/OneWireSensor.h b/applications/plugins/unitemp/interfaces/OneWireSensor.h index ef94db820..cb031d161 100644 --- a/applications/plugins/unitemp/interfaces/OneWireSensor.h +++ b/applications/plugins/unitemp/interfaces/OneWireSensor.h @@ -19,6 +19,7 @@ #define UNITEMP_OneWire #include "../unitemp.h" +#include //Коды семейства устройств typedef enum DallasFamilyCode { @@ -42,6 +43,8 @@ typedef struct { int8_t device_count; //Режим питания датчиков на шине PowerMode powerMode; + + OneWireHost* host; } OneWireBus; //Инстанс датчика one wire @@ -155,7 +158,7 @@ bool unitemp_onewire_bus_read_bit(OneWireBus* bus); * * @param bus Указатель на шину one wire * @return Байт информации - */ + **/ uint8_t unitemp_onewire_bus_read_byte(OneWireBus* bus); /** @@ -201,7 +204,7 @@ void unitemp_onewire_bus_select_sensor(OneWireSensor* instance); /** * @brief Инициализация процесса поиска адресов на шине one wire */ -void unitemp_onewire_bus_enum_init(void); +void unitemp_onewire_bus_enum_init(OneWireBus* bus); /** * @brief Перечисляет устройства на шине one wire и получает очередной адрес diff --git a/applications/plugins/unitemp/unitemp.c b/applications/plugins/unitemp/unitemp.c index 181de0e78..fbf9eca55 100644 --- a/applications/plugins/unitemp/unitemp.c +++ b/applications/plugins/unitemp/unitemp.c @@ -36,6 +36,9 @@ void unitemp_pascalToMmHg(Sensor* sensor) { void unitemp_pascalToKPa(Sensor* sensor) { sensor->pressure = sensor->pressure / 1000.0f; } +void unitemp_pascalToHPa(Sensor* sensor) { + sensor->pressure = sensor->pressure / 100.0f; +} void unitemp_pascalToInHg(Sensor* sensor) { sensor->pressure = sensor->pressure * 0.0002953007; } @@ -306,4 +309,4 @@ int32_t unitemp_app() { unitemp_free(); //Выход return 0; -} \ No newline at end of file +} diff --git a/applications/plugins/unitemp/unitemp.h b/applications/plugins/unitemp/unitemp.h index bde8b0c17..4d184b2c3 100644 --- a/applications/plugins/unitemp/unitemp.h +++ b/applications/plugins/unitemp/unitemp.h @@ -67,6 +67,7 @@ typedef enum { UT_PRESSURE_MM_HG, UT_PRESSURE_IN_HG, UT_PRESSURE_KPA, + UT_PRESSURE_HPA, UT_PRESSURE_COUNT } pressureMeasureUnit; @@ -135,6 +136,12 @@ void unitemp_pascalToKPa(Sensor* sensor); * * @param sensor Указатель на датчик */ +void unitemp_pascalToHPa(Sensor* sensor); +/** + * + * Mod BySepa - linktr.ee/BySepa + * + */ void unitemp_pascalToInHg(Sensor* sensor); /** @@ -151,4 +158,4 @@ bool unitemp_saveSettings(void); bool unitemp_loadSettings(void); extern Unitemp* app; -#endif \ No newline at end of file +#endif diff --git a/applications/plugins/unitemp/views/General_view.c b/applications/plugins/unitemp/views/General_view.c index dcf8420d9..2c8d389bf 100644 --- a/applications/plugins/unitemp/views/General_view.c +++ b/applications/plugins/unitemp/views/General_view.c @@ -116,14 +116,20 @@ static void _draw_humidity(Canvas* canvas, Sensor* sensor, const uint8_t pos[2]) static void _draw_pressure(Canvas* canvas, Sensor* sensor) { const uint8_t x = 29, y = 39; //Рисование рамки - canvas_draw_rframe(canvas, x, y, 69, 20, 3); - canvas_draw_rframe(canvas, x, y, 69, 19, 3); + if(app->settings.pressure_unit == UT_PRESSURE_HPA) { + canvas_draw_rframe(canvas, x, y, 84, 20, 3); + canvas_draw_rframe(canvas, x, y, 84, 19, 3); + } else { + canvas_draw_rframe(canvas, x, y, 69, 20, 3); + canvas_draw_rframe(canvas, x, y, 69, 19, 3); + } //Рисование иконки canvas_draw_icon(canvas, x + 3, y + 4, &I_pressure_7x13); int16_t press_int = sensor->pressure; - int8_t press_dec = (int16_t)(sensor->temp * 10) % 10; + // Change Temp for Pressure + int8_t press_dec = (int16_t)(sensor->pressure * 10) % 10; //Целая часть давления snprintf(app->buff, BUFF_SIZE, "%d", press_int); @@ -136,15 +142,23 @@ static void _draw_pressure(Canvas* canvas, Sensor* sensor) { snprintf(app->buff, BUFF_SIZE, ".%d", press_dec); canvas_set_font(canvas, FontPrimary); canvas_draw_str(canvas, x + 27 + int_len / 2 + 2, y + 10 + 7, app->buff); + } else if(app->settings.pressure_unit == UT_PRESSURE_HPA) { + uint8_t int_len = canvas_string_width(canvas, app->buff); + snprintf(app->buff, BUFF_SIZE, ".%d", press_dec); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, x + 32 + int_len / 2 + 2, y + 10 + 7, app->buff); } canvas_set_font(canvas, FontSecondary); //Единица измерения + if(app->settings.pressure_unit == UT_PRESSURE_MM_HG) { canvas_draw_icon(canvas, x + 50, y + 2, &I_mm_hg_15x15); } else if(app->settings.pressure_unit == UT_PRESSURE_IN_HG) { canvas_draw_icon(canvas, x + 50, y + 2, &I_in_hg_15x15); } else if(app->settings.pressure_unit == UT_PRESSURE_KPA) { canvas_draw_str(canvas, x + 52, y + 13, "kPa"); + } else if(app->settings.pressure_unit == UT_PRESSURE_HPA) { + canvas_draw_str(canvas, x + 67, y + 13, "hPa"); } } diff --git a/applications/plugins/unitemp/views/SensorEdit_view.c b/applications/plugins/unitemp/views/SensorEdit_view.c index ccb07a48e..c1ac4c028 100644 --- a/applications/plugins/unitemp/views/SensorEdit_view.c +++ b/applications/plugins/unitemp/views/SensorEdit_view.c @@ -75,7 +75,7 @@ static void _onewire_scan(void) { } while(_onewire_id_exist(id)); if(id == NULL) { - unitemp_onewire_bus_enum_init(); + unitemp_onewire_bus_enum_init(ow_sensor->bus); id = unitemp_onewire_bus_enum_next(ow_sensor->bus); if(_onewire_id_exist(id)) { do { diff --git a/applications/plugins/unitemp/views/Settings_view.c b/applications/plugins/unitemp/views/Settings_view.c index bff169129..e61c6cad6 100644 --- a/applications/plugins/unitemp/views/Settings_view.c +++ b/applications/plugins/unitemp/views/Settings_view.c @@ -25,7 +25,7 @@ static VariableItemList* variable_item_list; static const char states[2][9] = {"Auto", "Infinity"}; static const char temp_units[UT_TEMP_COUNT][3] = {"*C", "*F"}; -static const char pressure_units[UT_PRESSURE_COUNT][6] = {"mm Hg", "in Hg", "kPa"}; +static const char pressure_units[UT_PRESSURE_COUNT][6] = {"mm Hg", "in Hg", "kPa", "hPA"}; //Элемент списка - бесконечная подсветка VariableItem* infinity_backlight_item; @@ -149,4 +149,4 @@ void unitemp_Settings_free(void) { view_free(view); //Удаление вида после обработки view_dispatcher_remove_view(app->view_dispatcher, VIEW_ID); -} \ No newline at end of file +} diff --git a/applications/plugins/usb_hid_autofire/CHANGELOG.md b/applications/plugins/usb_hid_autofire/CHANGELOG.md index 62929bc8f..d0924edd3 100644 --- a/applications/plugins/usb_hid_autofire/CHANGELOG.md +++ b/applications/plugins/usb_hid_autofire/CHANGELOG.md @@ -1,8 +1,5 @@ # Changelog -## 0.5 -- Fix compatibility with Flipper Zero firmware 0.74.2 - ## 0.4 - Show active/inactive state in primary font (bold) diff --git a/applications/plugins/usb_hid_autofire/version.h b/applications/plugins/usb_hid_autofire/version.h index 669b388a5..ac1f5d0fa 100644 --- a/applications/plugins/usb_hid_autofire/version.h +++ b/applications/plugins/usb_hid_autofire/version.h @@ -1 +1 @@ -#define VERSION "0.5" +#define VERSION "0.4" diff --git a/applications/plugins/usbkeyboard/LICENSE.md b/applications/plugins/usbkeyboard/LICENSE.md deleted file mode 100644 index c616efe70..000000000 --- a/applications/plugins/usbkeyboard/LICENSE.md +++ /dev/null @@ -1,24 +0,0 @@ -BSD 2-Clause License - -Copyright (c) 2022, Gabriel Cirlig - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/applications/plugins/usbkeyboard/README.md b/applications/plugins/usbkeyboard/README.md deleted file mode 100644 index b2e31f9f4..000000000 --- a/applications/plugins/usbkeyboard/README.md +++ /dev/null @@ -1,8 +0,0 @@ -# FlipperZeroUSBKeyboard -Turn your Flipper Zero into an USB keyboard. Works with the [latest Unleashed firmware](https://github.com/Eng1n33r/flipperzero-firmware). See the release on how to install the external app on the sdcard. For building instructions please refer the [official FAP guide](https://github.com/Eng1n33r/flipperzero-firmware/blob/dev/documentation/AppsOnSDCard.md). - -How it works: https://twitter.com/hookgab/status/1572537933210718211 - -Credits: - -All the good people that made the [BT HID remote](https://github.com/flipperdevices/flipperzero-firmware/tree/873e1f114b7ca55a72dc68bf1b1fa6d169e7c17e/applications/plugins/bt_hid_app) and the [USB Mouse](https://github.com/flipperdevices/flipperzero-firmware/tree/873e1f114b7ca55a72dc68bf1b1fa6d169e7c17e/applications/debug/usb_mouse). diff --git a/applications/plugins/usbkeyboard/application.fam b/applications/plugins/usbkeyboard/application.fam deleted file mode 100644 index 1e419f3fe..000000000 --- a/applications/plugins/usbkeyboard/application.fam +++ /dev/null @@ -1,15 +0,0 @@ -App( - appid="USB_Keyboard", - name="USB Keyboard & Mouse", - apptype=FlipperAppType.EXTERNAL, - entry_point="usb_hid_app", - stack_size=1 * 1024, - cdefines=["APP_USB_KEYBOARD"], - requires=[ - "gui", - ], - order=60, - fap_icon="usb_keyboard_10px.png", - fap_category="Misc", - fap_icon_assets="assets", -) diff --git a/applications/plugins/usbkeyboard/assets/Arr_dwn_7x9.png b/applications/plugins/usbkeyboard/assets/Arr_dwn_7x9.png deleted file mode 100644 index d4034efc4..000000000 Binary files a/applications/plugins/usbkeyboard/assets/Arr_dwn_7x9.png and /dev/null differ diff --git a/applications/plugins/usbkeyboard/assets/Arr_up_7x9.png b/applications/plugins/usbkeyboard/assets/Arr_up_7x9.png deleted file mode 100644 index 28b4236a2..000000000 Binary files a/applications/plugins/usbkeyboard/assets/Arr_up_7x9.png and /dev/null differ diff --git a/applications/plugins/usbkeyboard/assets/ButtonDown_7x4.png b/applications/plugins/usbkeyboard/assets/ButtonDown_7x4.png deleted file mode 100644 index 2954bb6a6..000000000 Binary files a/applications/plugins/usbkeyboard/assets/ButtonDown_7x4.png and /dev/null differ diff --git a/applications/plugins/usbkeyboard/assets/ButtonLeft_4x7.png b/applications/plugins/usbkeyboard/assets/ButtonLeft_4x7.png deleted file mode 100644 index 0b4655d43..000000000 Binary files a/applications/plugins/usbkeyboard/assets/ButtonLeft_4x7.png and /dev/null differ diff --git a/applications/plugins/usbkeyboard/assets/ButtonRight_4x7.png b/applications/plugins/usbkeyboard/assets/ButtonRight_4x7.png deleted file mode 100644 index 8e1c74c1c..000000000 Binary files a/applications/plugins/usbkeyboard/assets/ButtonRight_4x7.png and /dev/null differ diff --git a/applications/plugins/usbkeyboard/assets/ButtonUp_7x4.png b/applications/plugins/usbkeyboard/assets/ButtonUp_7x4.png deleted file mode 100644 index 1be79328b..000000000 Binary files a/applications/plugins/usbkeyboard/assets/ButtonUp_7x4.png and /dev/null differ diff --git a/applications/plugins/usbkeyboard/assets/Button_18x18.png b/applications/plugins/usbkeyboard/assets/Button_18x18.png deleted file mode 100644 index 30a5b4fab..000000000 Binary files a/applications/plugins/usbkeyboard/assets/Button_18x18.png and /dev/null differ diff --git a/applications/plugins/usbkeyboard/assets/Circles_47x47.png b/applications/plugins/usbkeyboard/assets/Circles_47x47.png deleted file mode 100644 index 6a16ebf7b..000000000 Binary files a/applications/plugins/usbkeyboard/assets/Circles_47x47.png and /dev/null differ diff --git a/applications/plugins/usbkeyboard/assets/Left_mouse_icon_9x9.png b/applications/plugins/usbkeyboard/assets/Left_mouse_icon_9x9.png deleted file mode 100644 index c533d8572..000000000 Binary files a/applications/plugins/usbkeyboard/assets/Left_mouse_icon_9x9.png and /dev/null differ diff --git a/applications/plugins/usbkeyboard/assets/Like_def_11x9.png b/applications/plugins/usbkeyboard/assets/Like_def_11x9.png deleted file mode 100644 index 555bea3d4..000000000 Binary files a/applications/plugins/usbkeyboard/assets/Like_def_11x9.png and /dev/null differ diff --git a/applications/plugins/usbkeyboard/assets/Like_pressed_17x17.png b/applications/plugins/usbkeyboard/assets/Like_pressed_17x17.png deleted file mode 100644 index f5bf276f3..000000000 Binary files a/applications/plugins/usbkeyboard/assets/Like_pressed_17x17.png and /dev/null differ diff --git a/applications/plugins/usbkeyboard/assets/Ok_btn_9x9.png b/applications/plugins/usbkeyboard/assets/Ok_btn_9x9.png deleted file mode 100644 index 9a1539da2..000000000 Binary files a/applications/plugins/usbkeyboard/assets/Ok_btn_9x9.png and /dev/null differ diff --git a/applications/plugins/usbkeyboard/assets/Ok_btn_pressed_13x13.png b/applications/plugins/usbkeyboard/assets/Ok_btn_pressed_13x13.png deleted file mode 100644 index 6b46ba3a8..000000000 Binary files a/applications/plugins/usbkeyboard/assets/Ok_btn_pressed_13x13.png and /dev/null differ diff --git a/applications/plugins/usbkeyboard/assets/Pin_arrow_down_7x9.png b/applications/plugins/usbkeyboard/assets/Pin_arrow_down_7x9.png deleted file mode 100644 index 9687397af..000000000 Binary files a/applications/plugins/usbkeyboard/assets/Pin_arrow_down_7x9.png and /dev/null differ diff --git a/applications/plugins/usbkeyboard/assets/Pin_arrow_left_9x7.png b/applications/plugins/usbkeyboard/assets/Pin_arrow_left_9x7.png deleted file mode 100644 index fb4ded78f..000000000 Binary files a/applications/plugins/usbkeyboard/assets/Pin_arrow_left_9x7.png and /dev/null differ diff --git a/applications/plugins/usbkeyboard/assets/Pin_arrow_right_9x7.png b/applications/plugins/usbkeyboard/assets/Pin_arrow_right_9x7.png deleted file mode 100644 index 97648d176..000000000 Binary files a/applications/plugins/usbkeyboard/assets/Pin_arrow_right_9x7.png and /dev/null differ diff --git a/applications/plugins/usbkeyboard/assets/Pin_arrow_up_7x9.png b/applications/plugins/usbkeyboard/assets/Pin_arrow_up_7x9.png deleted file mode 100644 index a91a6fd5e..000000000 Binary files a/applications/plugins/usbkeyboard/assets/Pin_arrow_up_7x9.png and /dev/null differ diff --git a/applications/plugins/usbkeyboard/assets/Pin_back_arrow_10x8.png b/applications/plugins/usbkeyboard/assets/Pin_back_arrow_10x8.png deleted file mode 100644 index 3bafabd14..000000000 Binary files a/applications/plugins/usbkeyboard/assets/Pin_back_arrow_10x8.png and /dev/null differ diff --git a/applications/plugins/usbkeyboard/assets/Pressed_Button_13x13.png b/applications/plugins/usbkeyboard/assets/Pressed_Button_13x13.png deleted file mode 100644 index 823926b84..000000000 Binary files a/applications/plugins/usbkeyboard/assets/Pressed_Button_13x13.png and /dev/null differ diff --git a/applications/plugins/usbkeyboard/assets/Right_mouse_icon_9x9.png b/applications/plugins/usbkeyboard/assets/Right_mouse_icon_9x9.png deleted file mode 100644 index 446d7176c..000000000 Binary files a/applications/plugins/usbkeyboard/assets/Right_mouse_icon_9x9.png and /dev/null differ diff --git a/applications/plugins/usbkeyboard/assets/Space_65x18.png b/applications/plugins/usbkeyboard/assets/Space_65x18.png deleted file mode 100644 index b60ae5097..000000000 Binary files a/applications/plugins/usbkeyboard/assets/Space_65x18.png and /dev/null differ diff --git a/applications/plugins/usbkeyboard/assets/Voldwn_6x6.png b/applications/plugins/usbkeyboard/assets/Voldwn_6x6.png deleted file mode 100644 index d7a82a2df..000000000 Binary files a/applications/plugins/usbkeyboard/assets/Voldwn_6x6.png and /dev/null differ diff --git a/applications/plugins/usbkeyboard/assets/Volup_8x6.png b/applications/plugins/usbkeyboard/assets/Volup_8x6.png deleted file mode 100644 index 4b7ec66d6..000000000 Binary files a/applications/plugins/usbkeyboard/assets/Volup_8x6.png and /dev/null differ diff --git a/applications/plugins/usbkeyboard/usb_hid.c b/applications/plugins/usbkeyboard/usb_hid.c deleted file mode 100644 index 744d6463e..000000000 --- a/applications/plugins/usbkeyboard/usb_hid.c +++ /dev/null @@ -1,198 +0,0 @@ -#include "usb_hid.h" -#include -#include -#include - -#define TAG "UsbHidApp" - -enum UsbDebugSubmenuIndex { - UsbHidSubmenuIndexDirpad, - UsbHidSubmenuIndexKeyboard, - UsbHidSubmenuIndexMedia, - UsbHidSubmenuIndexMouse, - UsbHidSubmenuIndexMouseJiggler, -}; - -void usb_hid_submenu_callback(void* context, uint32_t index) { - furi_assert(context); - UsbHid* app = context; - if(index == UsbHidSubmenuIndexDirpad) { - app->view_id = UsbHidViewDirpad; - view_dispatcher_switch_to_view(app->view_dispatcher, UsbHidViewDirpad); - } else if(index == UsbHidSubmenuIndexKeyboard) { - app->view_id = UsbHidViewKeyboard; - view_dispatcher_switch_to_view(app->view_dispatcher, UsbHidViewKeyboard); - } else if(index == UsbHidSubmenuIndexMedia) { - app->view_id = UsbHidViewMedia; - view_dispatcher_switch_to_view(app->view_dispatcher, UsbHidViewMedia); - } else if(index == UsbHidSubmenuIndexMouse) { - app->view_id = UsbHidViewMouse; - view_dispatcher_switch_to_view(app->view_dispatcher, UsbHidViewMouse); - } else if(index == UsbHidSubmenuIndexMouseJiggler) { - app->view_id = UsbHidViewMouseJiggler; - view_dispatcher_switch_to_view(app->view_dispatcher, UsbHidViewMouseJiggler); - } -} - -void usb_hid_dialog_callback(DialogExResult result, void* context) { - furi_assert(context); - UsbHid* app = context; - if(result == DialogExResultLeft) { - view_dispatcher_stop(app->view_dispatcher); - } else if(result == DialogExResultRight) { - view_dispatcher_switch_to_view(app->view_dispatcher, app->view_id); // Show last view - } else if(result == DialogExResultCenter) { - view_dispatcher_switch_to_view(app->view_dispatcher, UsbHidViewSubmenu); - } -} - -uint32_t usb_hid_exit_confirm_view(void* context) { - UNUSED(context); - return UsbHidViewExitConfirm; -} - -uint32_t usb_hid_exit(void* context) { - UNUSED(context); - return VIEW_NONE; -} - -UsbHid* usb_hid_app_alloc() { - UsbHid* app = malloc(sizeof(UsbHid)); - - // Gui - app->gui = furi_record_open(RECORD_GUI); - - // Notifications - app->notifications = furi_record_open(RECORD_NOTIFICATION); - - // View dispatcher - app->view_dispatcher = view_dispatcher_alloc(); - view_dispatcher_enable_queue(app->view_dispatcher); - view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); - - // Submenu view - app->submenu = submenu_alloc(); - submenu_add_item( - app->submenu, "Dirpad", UsbHidSubmenuIndexDirpad, usb_hid_submenu_callback, app); - submenu_add_item( - app->submenu, "Keyboard", UsbHidSubmenuIndexKeyboard, usb_hid_submenu_callback, app); - submenu_add_item( - app->submenu, "Media", UsbHidSubmenuIndexMedia, usb_hid_submenu_callback, app); - submenu_add_item( - app->submenu, "Mouse", UsbHidSubmenuIndexMouse, usb_hid_submenu_callback, app); - submenu_add_item( - app->submenu, - "Mouse Jiggler", - UsbHidSubmenuIndexMouseJiggler, - usb_hid_submenu_callback, - app); - view_set_previous_callback(submenu_get_view(app->submenu), usb_hid_exit); - view_dispatcher_add_view( - app->view_dispatcher, UsbHidViewSubmenu, submenu_get_view(app->submenu)); - - // Dialog view - app->dialog = dialog_ex_alloc(); - dialog_ex_set_result_callback(app->dialog, usb_hid_dialog_callback); - dialog_ex_set_context(app->dialog, app); - dialog_ex_set_left_button_text(app->dialog, "Exit"); - dialog_ex_set_right_button_text(app->dialog, "Stay"); - dialog_ex_set_center_button_text(app->dialog, "Menu"); - dialog_ex_set_header(app->dialog, "Close Current App?", 16, 12, AlignLeft, AlignTop); - view_dispatcher_add_view( - app->view_dispatcher, UsbHidViewExitConfirm, dialog_ex_get_view(app->dialog)); - - // Dirpad view - app->usb_hid_dirpad = usb_hid_dirpad_alloc(); - view_set_previous_callback( - usb_hid_dirpad_get_view(app->usb_hid_dirpad), usb_hid_exit_confirm_view); - view_dispatcher_add_view( - app->view_dispatcher, UsbHidViewDirpad, usb_hid_dirpad_get_view(app->usb_hid_dirpad)); - - // Keyboard view - app->usb_hid_keyboard = usb_hid_keyboard_alloc(); - view_set_previous_callback( - usb_hid_keyboard_get_view(app->usb_hid_keyboard), usb_hid_exit_confirm_view); - view_dispatcher_add_view( - app->view_dispatcher, - UsbHidViewKeyboard, - usb_hid_keyboard_get_view(app->usb_hid_keyboard)); - - // Media view - app->usb_hid_media = usb_hid_media_alloc(); - view_set_previous_callback( - usb_hid_media_get_view(app->usb_hid_media), usb_hid_exit_confirm_view); - view_dispatcher_add_view( - app->view_dispatcher, UsbHidViewMedia, usb_hid_media_get_view(app->usb_hid_media)); - - // Mouse view - app->usb_hid_mouse = usb_hid_mouse_alloc(); - view_set_previous_callback( - usb_hid_mouse_get_view(app->usb_hid_mouse), usb_hid_exit_confirm_view); - view_dispatcher_add_view( - app->view_dispatcher, UsbHidViewMouse, usb_hid_mouse_get_view(app->usb_hid_mouse)); - - // Mouse jiggler view - app->hid_mouse_jiggler = hid_mouse_jiggler_alloc(app); - view_set_previous_callback( - hid_mouse_jiggler_get_view(app->hid_mouse_jiggler), usb_hid_exit_confirm_view); - view_dispatcher_add_view( - app->view_dispatcher, - UsbHidViewMouseJiggler, - hid_mouse_jiggler_get_view(app->hid_mouse_jiggler)); - - // TODO switch to menu after Media is done - app->view_id = UsbHidViewSubmenu; - view_dispatcher_switch_to_view(app->view_dispatcher, app->view_id); - - return app; -} - -void usb_hid_app_free(UsbHid* app) { - furi_assert(app); - - // Reset notification - notification_internal_message(app->notifications, &sequence_reset_blue); - - // Free views - view_dispatcher_remove_view(app->view_dispatcher, UsbHidViewSubmenu); - submenu_free(app->submenu); - view_dispatcher_remove_view(app->view_dispatcher, UsbHidViewExitConfirm); - dialog_ex_free(app->dialog); - view_dispatcher_remove_view(app->view_dispatcher, UsbHidViewDirpad); - usb_hid_dirpad_free(app->usb_hid_dirpad); - view_dispatcher_remove_view(app->view_dispatcher, UsbHidViewKeyboard); - usb_hid_keyboard_free(app->usb_hid_keyboard); - view_dispatcher_remove_view(app->view_dispatcher, UsbHidViewMedia); - usb_hid_media_free(app->usb_hid_media); - view_dispatcher_remove_view(app->view_dispatcher, UsbHidViewMouse); - usb_hid_mouse_free(app->usb_hid_mouse); - view_dispatcher_remove_view(app->view_dispatcher, UsbHidViewMouseJiggler); - hid_mouse_jiggler_free(app->hid_mouse_jiggler); - view_dispatcher_free(app->view_dispatcher); - // Close records - furi_record_close(RECORD_GUI); - app->gui = NULL; - furi_record_close(RECORD_NOTIFICATION); - app->notifications = NULL; - - // Free rest - free(app); -} - -int32_t usb_hid_app(void* p) { - UNUSED(p); - // Switch profile to Hid - UsbHid* app = usb_hid_app_alloc(); - - FuriHalUsbInterface* usb_mode_prev = furi_hal_usb_get_config(); - furi_hal_usb_unlock(); - furi_check(furi_hal_usb_set_config(&usb_hid, NULL) == true); - - view_dispatcher_run(app->view_dispatcher); - - // Change back profile - furi_hal_usb_set_config(usb_mode_prev, NULL); - usb_hid_app_free(app); - - return 0; -} diff --git a/applications/plugins/usbkeyboard/usb_hid.h b/applications/plugins/usbkeyboard/usb_hid.h deleted file mode 100644 index 77b70e59f..000000000 --- a/applications/plugins/usbkeyboard/usb_hid.h +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -#include -#include -#include "views/usb_hid_dirpad.h" -#include "views/usb_hid_keyboard.h" -#include "views/usb_hid_media.h" -#include "views/usb_hid_mouse.h" -#include "views/usb_hid_mouse_jiggler.h" - -typedef struct { - Gui* gui; - NotificationApp* notifications; - ViewDispatcher* view_dispatcher; - Submenu* submenu; - DialogEx* dialog; - UsbHidDirpad* usb_hid_dirpad; - UsbHidKeyboard* usb_hid_keyboard; - UsbHidMedia* usb_hid_media; - UsbHidMouse* usb_hid_mouse; - HidMouseJiggler* hid_mouse_jiggler; - uint32_t view_id; -} UsbHid; - -typedef enum { - UsbHidViewSubmenu, - UsbHidViewDirpad, - UsbHidViewKeyboard, - UsbHidViewMedia, - UsbHidViewMouse, - UsbHidViewMouseJiggler, - UsbHidViewExitConfirm, -} UsbHidView; diff --git a/applications/plugins/usbkeyboard/usb_keyboard_10px.png b/applications/plugins/usbkeyboard/usb_keyboard_10px.png deleted file mode 100644 index 7649138eb..000000000 Binary files a/applications/plugins/usbkeyboard/usb_keyboard_10px.png and /dev/null differ diff --git a/applications/plugins/usbkeyboard/views/usb_hid_dirpad.c b/applications/plugins/usbkeyboard/views/usb_hid_dirpad.c deleted file mode 100644 index d6aec9ef2..000000000 --- a/applications/plugins/usbkeyboard/views/usb_hid_dirpad.c +++ /dev/null @@ -1,197 +0,0 @@ -#include "usb_hid_dirpad.h" -#include -#include -#include -#include - -struct UsbHidDirpad { - View* view; -}; - -typedef struct { - bool left_pressed; - bool up_pressed; - bool right_pressed; - bool down_pressed; - bool ok_pressed; - bool back_pressed; - bool connected; -} UsbHidDirpadModel; - -static void usb_hid_dirpad_draw_arrow(Canvas* canvas, uint8_t x, uint8_t y, CanvasDirection dir) { - canvas_draw_triangle(canvas, x, y, 5, 3, dir); - if(dir == CanvasDirectionBottomToTop) { - canvas_draw_line(canvas, x, y + 6, x, y - 1); - } else if(dir == CanvasDirectionTopToBottom) { - canvas_draw_line(canvas, x, y - 6, x, y + 1); - } else if(dir == CanvasDirectionRightToLeft) { - canvas_draw_line(canvas, x + 6, y, x - 1, y); - } else if(dir == CanvasDirectionLeftToRight) { - canvas_draw_line(canvas, x - 6, y, x + 1, y); - } -} - -static void usb_hid_dirpad_draw_callback(Canvas* canvas, void* context) { - furi_assert(context); - UsbHidDirpadModel* model = context; - - canvas_set_font(canvas, FontPrimary); - elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Dirpad"); - - canvas_draw_icon(canvas, 68, 2, &I_Pin_back_arrow_10x8); - canvas_set_font(canvas, FontSecondary); - elements_multiline_text_aligned(canvas, 127, 3, AlignRight, AlignTop, "Hold to exit"); - - // Up - canvas_draw_icon(canvas, 21, 24, &I_Button_18x18); - if(model->up_pressed) { - elements_slightly_rounded_box(canvas, 24, 26, 13, 13); - canvas_set_color(canvas, ColorWhite); - } - usb_hid_dirpad_draw_arrow(canvas, 30, 30, CanvasDirectionBottomToTop); - canvas_set_color(canvas, ColorBlack); - - // Down - canvas_draw_icon(canvas, 21, 45, &I_Button_18x18); - if(model->down_pressed) { - elements_slightly_rounded_box(canvas, 24, 47, 13, 13); - canvas_set_color(canvas, ColorWhite); - } - usb_hid_dirpad_draw_arrow(canvas, 30, 55, CanvasDirectionTopToBottom); - canvas_set_color(canvas, ColorBlack); - - // Left - canvas_draw_icon(canvas, 0, 45, &I_Button_18x18); - if(model->left_pressed) { - elements_slightly_rounded_box(canvas, 3, 47, 13, 13); - canvas_set_color(canvas, ColorWhite); - } - usb_hid_dirpad_draw_arrow(canvas, 7, 53, CanvasDirectionRightToLeft); - canvas_set_color(canvas, ColorBlack); - - // Right - canvas_draw_icon(canvas, 42, 45, &I_Button_18x18); - if(model->right_pressed) { - elements_slightly_rounded_box(canvas, 45, 47, 13, 13); - canvas_set_color(canvas, ColorWhite); - } - usb_hid_dirpad_draw_arrow(canvas, 53, 53, CanvasDirectionLeftToRight); - canvas_set_color(canvas, ColorBlack); - - // Ok - canvas_draw_icon(canvas, 63, 25, &I_Space_65x18); - if(model->ok_pressed) { - elements_slightly_rounded_box(canvas, 66, 27, 60, 13); - canvas_set_color(canvas, ColorWhite); - } - canvas_draw_icon(canvas, 74, 29, &I_Ok_btn_9x9); - elements_multiline_text_aligned(canvas, 91, 36, AlignLeft, AlignBottom, "Space"); - canvas_set_color(canvas, ColorBlack); - - // Back - canvas_draw_icon(canvas, 63, 45, &I_Space_65x18); - if(model->back_pressed) { - elements_slightly_rounded_box(canvas, 66, 47, 60, 13); - canvas_set_color(canvas, ColorWhite); - } - canvas_draw_icon(canvas, 74, 49, &I_Pin_back_arrow_10x8); - elements_multiline_text_aligned(canvas, 91, 57, AlignLeft, AlignBottom, "Back"); -} - -static void usb_hid_dirpad_process(UsbHidDirpad* usb_hid_dirpad, InputEvent* event) { - with_view_model( - usb_hid_dirpad->view, - UsbHidDirpadModel * model, - { - if(event->type == InputTypePress) { - if(event->key == InputKeyUp) { - model->up_pressed = true; - furi_hal_hid_kb_press(HID_KEYBOARD_UP_ARROW); - } else if(event->key == InputKeyDown) { - model->down_pressed = true; - furi_hal_hid_kb_press(HID_KEYBOARD_DOWN_ARROW); - } else if(event->key == InputKeyLeft) { - model->left_pressed = true; - furi_hal_hid_kb_press(HID_KEYBOARD_LEFT_ARROW); - } else if(event->key == InputKeyRight) { - model->right_pressed = true; - furi_hal_hid_kb_press(HID_KEYBOARD_RIGHT_ARROW); - } else if(event->key == InputKeyOk) { - model->ok_pressed = true; - furi_hal_hid_kb_press(HID_KEYBOARD_SPACEBAR); - } else if(event->key == InputKeyBack) { - model->back_pressed = true; - } - } else if(event->type == InputTypeRelease) { - if(event->key == InputKeyUp) { - model->up_pressed = false; - furi_hal_hid_kb_release(HID_KEYBOARD_UP_ARROW); - } else if(event->key == InputKeyDown) { - model->down_pressed = false; - furi_hal_hid_kb_release(HID_KEYBOARD_DOWN_ARROW); - } else if(event->key == InputKeyLeft) { - model->left_pressed = false; - furi_hal_hid_kb_release(HID_KEYBOARD_LEFT_ARROW); - } else if(event->key == InputKeyRight) { - model->right_pressed = false; - furi_hal_hid_kb_release(HID_KEYBOARD_RIGHT_ARROW); - } else if(event->key == InputKeyOk) { - model->ok_pressed = false; - furi_hal_hid_kb_release(HID_KEYBOARD_SPACEBAR); - } else if(event->key == InputKeyBack) { - model->back_pressed = false; - } - } else if(event->type == InputTypeShort) { - if(event->key == InputKeyBack) { - furi_hal_hid_kb_press(HID_KEYBOARD_DELETE); - furi_hal_hid_kb_release(HID_KEYBOARD_DELETE); - furi_hal_hid_consumer_key_press(HID_CONSUMER_AC_BACK); - furi_hal_hid_consumer_key_release(HID_CONSUMER_AC_BACK); - } - } - }, - true); -} - -static bool usb_hid_dirpad_input_callback(InputEvent* event, void* context) { - furi_assert(context); - UsbHidDirpad* usb_hid_dirpad = context; - bool consumed = false; - - if(event->type == InputTypeLong && event->key == InputKeyBack) { - furi_hal_hid_kb_release_all(); - } else { - usb_hid_dirpad_process(usb_hid_dirpad, event); - consumed = true; - } - - return consumed; -} - -UsbHidDirpad* usb_hid_dirpad_alloc() { - UsbHidDirpad* usb_hid_dirpad = malloc(sizeof(UsbHidDirpad)); - usb_hid_dirpad->view = view_alloc(); - view_set_context(usb_hid_dirpad->view, usb_hid_dirpad); - view_allocate_model(usb_hid_dirpad->view, ViewModelTypeLocking, sizeof(UsbHidDirpadModel)); - view_set_draw_callback(usb_hid_dirpad->view, usb_hid_dirpad_draw_callback); - view_set_input_callback(usb_hid_dirpad->view, usb_hid_dirpad_input_callback); - - return usb_hid_dirpad; -} - -void usb_hid_dirpad_free(UsbHidDirpad* usb_hid_dirpad) { - furi_assert(usb_hid_dirpad); - view_free(usb_hid_dirpad->view); - free(usb_hid_dirpad); -} - -View* usb_hid_dirpad_get_view(UsbHidDirpad* usb_hid_dirpad) { - furi_assert(usb_hid_dirpad); - return usb_hid_dirpad->view; -} - -void usb_hid_dirpad_set_connected_status(UsbHidDirpad* usb_hid_dirpad, bool connected) { - furi_assert(usb_hid_dirpad); - with_view_model( - usb_hid_dirpad->view, UsbHidDirpadModel * model, { model->connected = connected; }, true); -} diff --git a/applications/plugins/usbkeyboard/views/usb_hid_dirpad.h b/applications/plugins/usbkeyboard/views/usb_hid_dirpad.h deleted file mode 100644 index 305e051b4..000000000 --- a/applications/plugins/usbkeyboard/views/usb_hid_dirpad.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include - -typedef struct UsbHidDirpad UsbHidDirpad; - -UsbHidDirpad* usb_hid_dirpad_alloc(); - -void usb_hid_dirpad_free(UsbHidDirpad* usb_hid_dirpad); - -View* usb_hid_dirpad_get_view(UsbHidDirpad* usb_hid_dirpad); - -void usb_hid_dirpad_set_connected_status(UsbHidDirpad* usb_hid_dirpad, bool connected); diff --git a/applications/plugins/usbkeyboard/views/usb_hid_keyboard.c b/applications/plugins/usbkeyboard/views/usb_hid_keyboard.c deleted file mode 100644 index 61fcaab80..000000000 --- a/applications/plugins/usbkeyboard/views/usb_hid_keyboard.c +++ /dev/null @@ -1,387 +0,0 @@ -#include "usb_hid_keyboard.h" -#include -#include -#include -#include -#include - -struct UsbHidKeyboard { - View* view; -}; - -typedef struct { - bool shift; - bool alt; - bool ctrl; - bool gui; - uint8_t x; - uint8_t y; - uint8_t last_key_code; - uint16_t modifier_code; - bool ok_pressed; - bool back_pressed; - bool connected; - char key_string[5]; -} UsbHidKeyboardModel; - -typedef struct { - uint8_t width; - char* key; - const Icon* icon; - char* shift_key; - uint8_t value; -} UsbHidKeyboardKey; - -typedef struct { - int8_t x; - int8_t y; -} UsbHidKeyboardPoint; - -// 4 BY 12 -#define MARGIN_TOP 0 -#define MARGIN_LEFT 4 -#define KEY_WIDTH 9 -#define KEY_HEIGHT 12 -#define KEY_PADDING 1 -#define ROW_COUNT 7 -#define COLUMN_COUNT 12 - -// 0 width items are not drawn, but there value is used -const UsbHidKeyboardKey usb_hid_keyboard_keyset[ROW_COUNT][COLUMN_COUNT] = { - { - {.width = 1, .icon = NULL, .key = "1", .shift_key = "!", .value = HID_KEYBOARD_1}, - {.width = 1, .icon = NULL, .key = "2", .shift_key = "@", .value = HID_KEYBOARD_2}, - {.width = 1, .icon = NULL, .key = "3", .shift_key = "#", .value = HID_KEYBOARD_3}, - {.width = 1, .icon = NULL, .key = "4", .shift_key = "$", .value = HID_KEYBOARD_4}, - {.width = 1, .icon = NULL, .key = "5", .shift_key = "%", .value = HID_KEYBOARD_5}, - {.width = 1, .icon = NULL, .key = "6", .shift_key = "^", .value = HID_KEYBOARD_6}, - {.width = 1, .icon = NULL, .key = "7", .shift_key = "&", .value = HID_KEYBOARD_7}, - {.width = 1, .icon = NULL, .key = "8", .shift_key = "*", .value = HID_KEYBOARD_8}, - {.width = 1, .icon = NULL, .key = "9", .shift_key = "(", .value = HID_KEYBOARD_9}, - {.width = 1, .icon = NULL, .key = "0", .shift_key = ")", .value = HID_KEYBOARD_0}, - {.width = 2, .icon = &I_Pin_arrow_left_9x7, .value = HID_KEYBOARD_DELETE}, - {.width = 0, .value = HID_KEYBOARD_DELETE}, - }, - { - {.width = 1, .icon = NULL, .key = "q", .shift_key = "Q", .value = HID_KEYBOARD_Q}, - {.width = 1, .icon = NULL, .key = "w", .shift_key = "W", .value = HID_KEYBOARD_W}, - {.width = 1, .icon = NULL, .key = "e", .shift_key = "E", .value = HID_KEYBOARD_E}, - {.width = 1, .icon = NULL, .key = "r", .shift_key = "R", .value = HID_KEYBOARD_R}, - {.width = 1, .icon = NULL, .key = "t", .shift_key = "T", .value = HID_KEYBOARD_T}, - {.width = 1, .icon = NULL, .key = "y", .shift_key = "Y", .value = HID_KEYBOARD_Y}, - {.width = 1, .icon = NULL, .key = "u", .shift_key = "U", .value = HID_KEYBOARD_U}, - {.width = 1, .icon = NULL, .key = "i", .shift_key = "I", .value = HID_KEYBOARD_I}, - {.width = 1, .icon = NULL, .key = "o", .shift_key = "O", .value = HID_KEYBOARD_O}, - {.width = 1, .icon = NULL, .key = "p", .shift_key = "P", .value = HID_KEYBOARD_P}, - {.width = 1, .icon = NULL, .key = "[", .shift_key = "{", .value = HID_KEYBOARD_OPEN_BRACKET}, - {.width = 1, - .icon = NULL, - .key = "]", - .shift_key = "}", - .value = HID_KEYBOARD_CLOSE_BRACKET}, - }, - { - {.width = 1, .icon = NULL, .key = "a", .shift_key = "A", .value = HID_KEYBOARD_A}, - {.width = 1, .icon = NULL, .key = "s", .shift_key = "S", .value = HID_KEYBOARD_S}, - {.width = 1, .icon = NULL, .key = "d", .shift_key = "D", .value = HID_KEYBOARD_D}, - {.width = 1, .icon = NULL, .key = "f", .shift_key = "F", .value = HID_KEYBOARD_F}, - {.width = 1, .icon = NULL, .key = "g", .shift_key = "G", .value = HID_KEYBOARD_G}, - {.width = 1, .icon = NULL, .key = "h", .shift_key = "H", .value = HID_KEYBOARD_H}, - {.width = 1, .icon = NULL, .key = "j", .shift_key = "J", .value = HID_KEYBOARD_J}, - {.width = 1, .icon = NULL, .key = "k", .shift_key = "K", .value = HID_KEYBOARD_K}, - {.width = 1, .icon = NULL, .key = "l", .shift_key = "L", .value = HID_KEYBOARD_L}, - {.width = 1, .icon = NULL, .key = ";", .shift_key = ":", .value = HID_KEYBOARD_SEMICOLON}, - {.width = 2, .icon = &I_Pin_arrow_right_9x7, .value = HID_KEYBOARD_RETURN}, - {.width = 0, .value = HID_KEYBOARD_RETURN}, - }, - { - {.width = 1, .icon = NULL, .key = "z", .shift_key = "Z", .value = HID_KEYBOARD_Z}, - {.width = 1, .icon = NULL, .key = "x", .shift_key = "X", .value = HID_KEYBOARD_X}, - {.width = 1, .icon = NULL, .key = "c", .shift_key = "C", .value = HID_KEYBOARD_C}, - {.width = 1, .icon = NULL, .key = "v", .shift_key = "V", .value = HID_KEYBOARD_V}, - {.width = 1, .icon = NULL, .key = "b", .shift_key = "B", .value = HID_KEYBOARD_B}, - {.width = 1, .icon = NULL, .key = "n", .shift_key = "N", .value = HID_KEYBOARD_N}, - {.width = 1, .icon = NULL, .key = "m", .shift_key = "M", .value = HID_KEYBOARD_M}, - {.width = 1, .icon = NULL, .key = "/", .shift_key = "?", .value = HID_KEYBOARD_SLASH}, - {.width = 1, .icon = NULL, .key = "\\", .shift_key = "|", .value = HID_KEYBOARD_BACKSLASH}, - {.width = 1, .icon = NULL, .key = "`", .shift_key = "~", .value = HID_KEYBOARD_GRAVE_ACCENT}, - {.width = 1, .icon = &I_ButtonUp_7x4, .value = HID_KEYBOARD_UP_ARROW}, - {.width = 1, .icon = NULL, .key = "-", .shift_key = "_", .value = HID_KEYBOARD_MINUS}, - }, - { - {.width = 1, .icon = &I_Pin_arrow_up_7x9, .value = HID_KEYBOARD_L_SHIFT}, - {.width = 1, .icon = NULL, .key = ",", .shift_key = "<", .value = HID_KEYPAD_COMMA}, - {.width = 1, .icon = NULL, .key = ".", .shift_key = ">", .value = HID_KEYBOARD_DOT}, - {.width = 4, .icon = NULL, .key = " ", .value = HID_KEYBOARD_SPACEBAR}, - {.width = 0, .value = HID_KEYBOARD_SPACEBAR}, - {.width = 0, .value = HID_KEYBOARD_SPACEBAR}, - {.width = 0, .value = HID_KEYBOARD_SPACEBAR}, - {.width = 1, .icon = NULL, .key = "'", .shift_key = "\"", .value = HID_KEYBOARD_APOSTROPHE}, - {.width = 1, .icon = NULL, .key = "=", .shift_key = "+", .value = HID_KEYBOARD_EQUAL_SIGN}, - {.width = 1, .icon = &I_ButtonLeft_4x7, .value = HID_KEYBOARD_LEFT_ARROW}, - {.width = 1, .icon = &I_ButtonDown_7x4, .value = HID_KEYBOARD_DOWN_ARROW}, - {.width = 1, .icon = &I_ButtonRight_4x7, .value = HID_KEYBOARD_RIGHT_ARROW}, - }, - { - {.width = 2, .icon = NULL, .key = "Ctl", .value = HID_KEYBOARD_L_CTRL}, - {.width = 0, .icon = NULL, .value = HID_KEYBOARD_L_CTRL}, - {.width = 2, .icon = NULL, .key = "Alt", .value = HID_KEYBOARD_L_ALT}, - {.width = 0, .icon = NULL, .value = HID_KEYBOARD_L_ALT}, - {.width = 2, .icon = NULL, .key = "Cmd", .value = HID_KEYBOARD_L_GUI}, - {.width = 0, .icon = NULL, .value = HID_KEYBOARD_L_GUI}, - {.width = 2, .icon = NULL, .key = "Tab", .value = HID_KEYBOARD_TAB}, - {.width = 0, .icon = NULL, .value = HID_KEYBOARD_TAB}, - {.width = 2, .icon = NULL, .key = "Esc", .value = HID_KEYBOARD_ESCAPE}, - {.width = 0, .icon = NULL, .value = HID_KEYBOARD_ESCAPE}, - {.width = 2, .icon = NULL, .key = "Del", .value = HID_KEYBOARD_DELETE_FORWARD}, - {.width = 0, .icon = NULL, .value = HID_KEYBOARD_DELETE_FORWARD}, - }, - { - {.width = 1, .icon = NULL, .key = "1", .shift_key = "1", .value = HID_KEYBOARD_F1}, - {.width = 1, .icon = NULL, .key = "2", .shift_key = "2", .value = HID_KEYBOARD_F2}, - {.width = 1, .icon = NULL, .key = "3", .shift_key = "3", .value = HID_KEYBOARD_F3}, - {.width = 1, .icon = NULL, .key = "4", .shift_key = "4", .value = HID_KEYBOARD_F4}, - {.width = 1, .icon = NULL, .key = "5", .shift_key = "5", .value = HID_KEYBOARD_F5}, - {.width = 1, .icon = NULL, .key = "6", .shift_key = "6", .value = HID_KEYBOARD_F6}, - {.width = 1, .icon = NULL, .key = "7", .shift_key = "7", .value = HID_KEYBOARD_F7}, - {.width = 1, .icon = NULL, .key = "8", .shift_key = "8", .value = HID_KEYBOARD_F8}, - {.width = 1, .icon = NULL, .key = "9", .shift_key = "9", .value = HID_KEYBOARD_F9}, - {.width = 1, .icon = NULL, .key = "0", .shift_key = "0", .value = HID_KEYBOARD_F10}, - {.width = 1, .icon = NULL, .key = "1", .shift_key = "1", .value = HID_KEYBOARD_F11}, - {.width = 1, .icon = NULL, .key = "2", .shift_key = "2", .value = HID_KEYBOARD_F12}, - }}; - -static void usb_hid_keyboard_to_upper(char* str) { - while(*str) { - *str = toupper((unsigned char)*str); - str++; - } -} - -static void usb_hid_keyboard_draw_key( - Canvas* canvas, - UsbHidKeyboardModel* model, - uint8_t x, - uint8_t y, - UsbHidKeyboardKey key, - bool selected) { - if(!key.width) return; - - canvas_set_color(canvas, ColorBlack); - uint8_t keyWidth = KEY_WIDTH * key.width + KEY_PADDING * (key.width - 1); - if(selected) { - // Draw a filled box - elements_slightly_rounded_box( - canvas, - MARGIN_LEFT + x * (KEY_WIDTH + KEY_PADDING), - MARGIN_TOP + y * (KEY_HEIGHT + KEY_PADDING), - keyWidth, - KEY_HEIGHT); - canvas_set_color(canvas, ColorWhite); - } else { - // Draw a framed box - elements_slightly_rounded_frame( - canvas, - MARGIN_LEFT + x * (KEY_WIDTH + KEY_PADDING), - MARGIN_TOP + y * (KEY_HEIGHT + KEY_PADDING), - keyWidth, - KEY_HEIGHT); - } - if(key.icon != NULL) { - // Draw the icon centered on the button - canvas_draw_icon( - canvas, - MARGIN_LEFT + x * (KEY_WIDTH + KEY_PADDING) + keyWidth / 2 - key.icon->width / 2, - MARGIN_TOP + y * (KEY_HEIGHT + KEY_PADDING) + KEY_HEIGHT / 2 - key.icon->height / 2, - key.icon); - } else { - // If shift is toggled use the shift key when available - strcpy(model->key_string, (model->shift && key.shift_key != 0) ? key.shift_key : key.key); - // Upper case if ctrl or alt was toggled true - if((model->ctrl && key.value == HID_KEYBOARD_L_CTRL) || - (model->alt && key.value == HID_KEYBOARD_L_ALT) || - (model->gui && key.value == HID_KEYBOARD_L_GUI)) { - usb_hid_keyboard_to_upper(model->key_string); - } - canvas_draw_str_aligned( - canvas, - MARGIN_LEFT + x * (KEY_WIDTH + KEY_PADDING) + keyWidth / 2 + 1, - MARGIN_TOP + y * (KEY_HEIGHT + KEY_PADDING) + KEY_HEIGHT / 2, - AlignCenter, - AlignCenter, - model->key_string); - } -} - -static void usb_hid_keyboard_draw_callback(Canvas* canvas, void* context) { - furi_assert(context); - UsbHidKeyboardModel* model = context; - - canvas_set_font(canvas, FontKeyboard); - // Start shifting the all keys up if on the next row (Scrolling) - uint8_t initY = model->y - 4 > 0 ? model->y - 4 : 0; - for(uint8_t y = initY; y < ROW_COUNT; y++) { - const UsbHidKeyboardKey* keyboardKeyRow = usb_hid_keyboard_keyset[y]; - uint8_t x = 0; - for(uint8_t i = 0; i < COLUMN_COUNT; i++) { - UsbHidKeyboardKey key = keyboardKeyRow[i]; - // Select when the button is hovered - // Select if the button is hovered within its width - // Select if back is clicked and its the backspace key - // Deselect when the button clicked or not hovered - bool keySelected = (x <= model->x && model->x < (x + key.width)) && y == model->y; - // Revert selection for function keys - keySelected = y == ROW_COUNT - 1 ? !keySelected : keySelected; - bool backSelected = model->back_pressed && key.value == HID_KEYBOARD_DELETE; - usb_hid_keyboard_draw_key( - canvas, - model, - x, - y - initY, - key, - (!model->ok_pressed && keySelected) || backSelected); - x += key.width; - } - } -} - -static uint8_t usb_hid_keyboard_get_selected_key(UsbHidKeyboardModel* model) { - UsbHidKeyboardKey key = usb_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; -} - -static void - usb_hid_keyboard_get_select_key(UsbHidKeyboardModel* model, UsbHidKeyboardPoint 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; - } while(delta.y != 0 && usb_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; - } while(delta.x != 0 && usb_hid_keyboard_keyset[model->y][model->x].width == - 0); // Skip zero width keys, pretend they are one key -} - -static void usb_hid_keyboard_process(UsbHidKeyboard* usb_hid_keyboard, InputEvent* event) { - with_view_model( - usb_hid_keyboard->view, - UsbHidKeyboardModel * model, - { - if(event->key == InputKeyOk) { - if(event->type == InputTypePress) { - model->ok_pressed = true; - } else if(event->type == InputTypeLong || event->type == InputTypeShort) { - model->last_key_code = usb_hid_keyboard_get_selected_key(model); - - // Toggle the modifier key when clicked, and click the key - if(model->last_key_code == HID_KEYBOARD_L_SHIFT) { - model->shift = !model->shift; - if(model->shift) - model->modifier_code |= KEY_MOD_LEFT_SHIFT; - else - model->modifier_code &= ~KEY_MOD_LEFT_SHIFT; - } else if(model->last_key_code == HID_KEYBOARD_L_ALT) { - model->alt = !model->alt; - if(model->alt) - model->modifier_code |= KEY_MOD_LEFT_ALT; - else - model->modifier_code &= ~KEY_MOD_LEFT_ALT; - } else if(model->last_key_code == HID_KEYBOARD_L_CTRL) { - model->ctrl = !model->ctrl; - if(model->ctrl) - model->modifier_code |= KEY_MOD_LEFT_CTRL; - else - model->modifier_code &= ~KEY_MOD_LEFT_CTRL; - } else if(model->last_key_code == HID_KEYBOARD_L_GUI) { - model->gui = !model->gui; - if(model->gui) - model->modifier_code |= KEY_MOD_LEFT_GUI; - else - model->modifier_code &= ~KEY_MOD_LEFT_GUI; - } - furi_hal_hid_kb_press(model->modifier_code | model->last_key_code); - } else if(event->type == InputTypeRelease) { - // Release happens after short and long presses - furi_hal_hid_kb_release(model->modifier_code | model->last_key_code); - model->ok_pressed = false; - } - } else if(event->key == InputKeyBack) { - // If back is pressed for a short time, backspace - if(event->type == InputTypePress) { - model->back_pressed = true; - } else if(event->type == InputTypeShort) { - furi_hal_hid_kb_press(HID_KEYBOARD_DELETE); - furi_hal_hid_kb_release(HID_KEYBOARD_DELETE); - } else if(event->type == InputTypeRelease) { - model->back_pressed = false; - } - } else if(event->type == InputTypePress || event->type == InputTypeRepeat) { - // Cycle the selected keys - if(event->key == InputKeyUp) { - usb_hid_keyboard_get_select_key(model, (UsbHidKeyboardPoint){.x = 0, .y = -1}); - } else if(event->key == InputKeyDown) { - usb_hid_keyboard_get_select_key(model, (UsbHidKeyboardPoint){.x = 0, .y = 1}); - } else if(event->key == InputKeyLeft) { - usb_hid_keyboard_get_select_key(model, (UsbHidKeyboardPoint){.x = -1, .y = 0}); - } else if(event->key == InputKeyRight) { - usb_hid_keyboard_get_select_key(model, (UsbHidKeyboardPoint){.x = 1, .y = 0}); - } - } - }, - true); -} - -static bool usb_hid_keyboard_input_callback(InputEvent* event, void* context) { - furi_assert(context); - UsbHidKeyboard* usb_hid_keyboard = context; - bool consumed = false; - - if(event->type == InputTypeLong && event->key == InputKeyBack) { - furi_hal_hid_kb_release_all(); - } else { - usb_hid_keyboard_process(usb_hid_keyboard, event); - consumed = true; - } - - return consumed; -} - -UsbHidKeyboard* usb_hid_keyboard_alloc() { - UsbHidKeyboard* usb_hid_keyboard = malloc(sizeof(UsbHidKeyboard)); - - usb_hid_keyboard->view = view_alloc(); - view_set_context(usb_hid_keyboard->view, usb_hid_keyboard); - view_allocate_model(usb_hid_keyboard->view, ViewModelTypeLocking, sizeof(UsbHidKeyboardModel)); - view_set_draw_callback(usb_hid_keyboard->view, usb_hid_keyboard_draw_callback); - view_set_input_callback(usb_hid_keyboard->view, usb_hid_keyboard_input_callback); - - with_view_model( - usb_hid_keyboard->view, UsbHidKeyboardModel * model, { model->connected = true; }, true); - - return usb_hid_keyboard; -} - -void usb_hid_keyboard_free(UsbHidKeyboard* usb_hid_keyboard) { - furi_assert(usb_hid_keyboard); - view_free(usb_hid_keyboard->view); - free(usb_hid_keyboard); -} - -View* usb_hid_keyboard_get_view(UsbHidKeyboard* usb_hid_keyboard) { - furi_assert(usb_hid_keyboard); - return usb_hid_keyboard->view; -} \ No newline at end of file diff --git a/applications/plugins/usbkeyboard/views/usb_hid_keyboard.h b/applications/plugins/usbkeyboard/views/usb_hid_keyboard.h deleted file mode 100644 index 4dee5fbee..000000000 --- a/applications/plugins/usbkeyboard/views/usb_hid_keyboard.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include - -typedef struct UsbHidKeyboard UsbHidKeyboard; - -UsbHidKeyboard* usb_hid_keyboard_alloc(); - -void usb_hid_keyboard_free(UsbHidKeyboard* usb_hid_keyboard); - -View* usb_hid_keyboard_get_view(UsbHidKeyboard* usb_hid_keyboard); - -void usb_hid_keyboard_set_connected_status(UsbHidKeyboard* usb_hid_keyboard, bool connected); diff --git a/applications/plugins/usbkeyboard/views/usb_hid_media.c b/applications/plugins/usbkeyboard/views/usb_hid_media.c deleted file mode 100644 index 8d2188434..000000000 --- a/applications/plugins/usbkeyboard/views/usb_hid_media.c +++ /dev/null @@ -1,201 +0,0 @@ -#include "usb_hid_media.h" -#include -#include -#include -#include - -struct UsbHidMedia { - View* view; -}; - -typedef struct { - bool left_pressed; - bool up_pressed; - bool right_pressed; - bool down_pressed; - bool ok_pressed; - bool connected; -} UsbHidMediaModel; - -static void usb_hid_media_draw_arrow(Canvas* canvas, uint8_t x, uint8_t y, CanvasDirection dir) { - canvas_draw_triangle(canvas, x, y, 5, 3, dir); - if(dir == CanvasDirectionBottomToTop) { - canvas_draw_dot(canvas, x, y - 1); - } else if(dir == CanvasDirectionTopToBottom) { - canvas_draw_dot(canvas, x, y + 1); - } else if(dir == CanvasDirectionRightToLeft) { - canvas_draw_dot(canvas, x - 1, y); - } else if(dir == CanvasDirectionLeftToRight) { - canvas_draw_dot(canvas, x + 1, y); - } -} - -static void usb_hid_media_draw_callback(Canvas* canvas, void* context) { - furi_assert(context); - UsbHidMediaModel* model = context; - - canvas_set_font(canvas, FontPrimary); - elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Media"); - canvas_set_font(canvas, FontSecondary); - - // Keypad circles - canvas_draw_icon(canvas, 76, 8, &I_Circles_47x47); - - // Up - if(model->up_pressed) { - canvas_set_bitmap_mode(canvas, 1); - canvas_draw_icon(canvas, 93, 9, &I_Pressed_Button_13x13); - canvas_set_bitmap_mode(canvas, 0); - canvas_set_color(canvas, ColorWhite); - } - canvas_draw_icon(canvas, 96, 12, &I_Volup_8x6); - canvas_set_color(canvas, ColorBlack); - - // Down - if(model->down_pressed) { - canvas_set_bitmap_mode(canvas, 1); - canvas_draw_icon(canvas, 93, 41, &I_Pressed_Button_13x13); - canvas_set_bitmap_mode(canvas, 0); - canvas_set_color(canvas, ColorWhite); - } - canvas_draw_icon(canvas, 96, 45, &I_Voldwn_6x6); - canvas_set_color(canvas, ColorBlack); - - // Left - if(model->left_pressed) { - canvas_set_bitmap_mode(canvas, 1); - canvas_draw_icon(canvas, 77, 25, &I_Pressed_Button_13x13); - canvas_set_bitmap_mode(canvas, 0); - canvas_set_color(canvas, ColorWhite); - } - usb_hid_media_draw_arrow(canvas, 82, 31, CanvasDirectionRightToLeft); - usb_hid_media_draw_arrow(canvas, 86, 31, CanvasDirectionRightToLeft); - canvas_set_color(canvas, ColorBlack); - - // Right - if(model->right_pressed) { - canvas_set_bitmap_mode(canvas, 1); - canvas_draw_icon(canvas, 109, 25, &I_Pressed_Button_13x13); - canvas_set_bitmap_mode(canvas, 0); - canvas_set_color(canvas, ColorWhite); - } - usb_hid_media_draw_arrow(canvas, 112, 31, CanvasDirectionLeftToRight); - usb_hid_media_draw_arrow(canvas, 116, 31, CanvasDirectionLeftToRight); - canvas_set_color(canvas, ColorBlack); - - // Ok - if(model->ok_pressed) { - canvas_draw_icon(canvas, 93, 25, &I_Pressed_Button_13x13); - canvas_set_color(canvas, ColorWhite); - } - usb_hid_media_draw_arrow(canvas, 96, 31, CanvasDirectionLeftToRight); - canvas_draw_line(canvas, 100, 29, 100, 33); - canvas_draw_line(canvas, 102, 29, 102, 33); - canvas_set_color(canvas, ColorBlack); - - // Exit - canvas_draw_icon(canvas, 0, 54, &I_Pin_back_arrow_10x8); - canvas_set_font(canvas, FontSecondary); - elements_multiline_text_aligned(canvas, 13, 62, AlignLeft, AlignBottom, "Hold to exit"); -} - -static void usb_hid_media_process_press(UsbHidMedia* usb_hid_media, InputEvent* event) { - with_view_model( - usb_hid_media->view, - UsbHidMediaModel * model, - { - if(event->key == InputKeyUp) { - model->up_pressed = true; - furi_hal_hid_consumer_key_press(HID_CONSUMER_VOLUME_INCREMENT); - } else if(event->key == InputKeyDown) { - model->down_pressed = true; - furi_hal_hid_consumer_key_press(HID_CONSUMER_VOLUME_DECREMENT); - } else if(event->key == InputKeyLeft) { - model->left_pressed = true; - furi_hal_hid_consumer_key_press(HID_CONSUMER_SCAN_PREVIOUS_TRACK); - } else if(event->key == InputKeyRight) { - model->right_pressed = true; - furi_hal_hid_consumer_key_press(HID_CONSUMER_SCAN_NEXT_TRACK); - } else if(event->key == InputKeyOk) { - model->ok_pressed = true; - furi_hal_hid_consumer_key_press(HID_CONSUMER_PLAY_PAUSE); - } - }, - true); -} - -static void hid_media_process_release(UsbHidMedia* usb_hid_media, InputEvent* event) { - with_view_model( - usb_hid_media->view, - UsbHidMediaModel * model, - { - if(event->key == InputKeyUp) { - model->up_pressed = false; - furi_hal_hid_consumer_key_release(HID_CONSUMER_VOLUME_INCREMENT); - } else if(event->key == InputKeyDown) { - model->down_pressed = false; - furi_hal_hid_consumer_key_release(HID_CONSUMER_VOLUME_DECREMENT); - } else if(event->key == InputKeyLeft) { - model->left_pressed = false; - furi_hal_hid_consumer_key_release(HID_CONSUMER_SCAN_PREVIOUS_TRACK); - } else if(event->key == InputKeyRight) { - model->right_pressed = false; - furi_hal_hid_consumer_key_release(HID_CONSUMER_SCAN_NEXT_TRACK); - } else if(event->key == InputKeyOk) { - model->ok_pressed = false; - furi_hal_hid_consumer_key_release(HID_CONSUMER_PLAY_PAUSE); - } - }, - true); -} - -static bool usb_hid_media_input_callback(InputEvent* event, void* context) { - furi_assert(context); - UsbHidMedia* usb_hid_media = context; - bool consumed = false; - - if(event->type == InputTypePress) { - usb_hid_media_process_press(usb_hid_media, event); - consumed = true; - } else if(event->type == InputTypeRelease) { - hid_media_process_release(usb_hid_media, event); - consumed = true; - } else if(event->type == InputTypeShort) { - if(event->key == InputKeyBack) { - furi_hal_hid_kb_release_all(); - } - } - - return consumed; -} - -UsbHidMedia* usb_hid_media_alloc() { - UsbHidMedia* usb_hid_media = malloc(sizeof(UsbHidMedia)); - usb_hid_media->view = view_alloc(); - view_set_context(usb_hid_media->view, usb_hid_media); - view_allocate_model(usb_hid_media->view, ViewModelTypeLocking, sizeof(UsbHidMediaModel)); - view_set_draw_callback(usb_hid_media->view, usb_hid_media_draw_callback); - view_set_input_callback(usb_hid_media->view, usb_hid_media_input_callback); - - with_view_model( - usb_hid_media->view, UsbHidMediaModel * model, { model->connected = true; }, true); - - return usb_hid_media; -} - -void usb_hid_media_free(UsbHidMedia* usb_hid_media) { - furi_assert(usb_hid_media); - view_free(usb_hid_media->view); - free(usb_hid_media); -} - -View* usb_hid_media_get_view(UsbHidMedia* usb_hid_media) { - furi_assert(usb_hid_media); - return usb_hid_media->view; -} - -void usb_hid_media_set_connected_status(UsbHidMedia* usb_hid_media, bool connected) { - furi_assert(usb_hid_media); - with_view_model( - usb_hid_media->view, UsbHidMediaModel * model, { model->connected = connected; }, true); -} diff --git a/applications/plugins/usbkeyboard/views/usb_hid_media.h b/applications/plugins/usbkeyboard/views/usb_hid_media.h deleted file mode 100644 index 441e2bd6d..000000000 --- a/applications/plugins/usbkeyboard/views/usb_hid_media.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include - -typedef struct UsbHidMedia UsbHidMedia; - -UsbHidMedia* usb_hid_media_alloc(); - -void usb_hid_media_free(UsbHidMedia* usb_hid_media); - -View* usb_hid_media_get_view(UsbHidMedia* usb_hid_media); - -void usb_hid_media_set_connected_status(UsbHidMedia* usb_hid_media, bool connected); diff --git a/applications/plugins/usbkeyboard/views/usb_hid_mouse.c b/applications/plugins/usbkeyboard/views/usb_hid_mouse.c deleted file mode 100644 index 27f2ac105..000000000 --- a/applications/plugins/usbkeyboard/views/usb_hid_mouse.c +++ /dev/null @@ -1,215 +0,0 @@ -#include "usb_hid_mouse.h" -#include -#include -#include -#include - -struct UsbHidMouse { - View* view; -}; -#define MOUSE_MOVE_SHORT 5 -#define MOUSE_MOVE_LONG 20 - -typedef struct { - bool left_pressed; - bool up_pressed; - bool right_pressed; - bool down_pressed; - bool left_mouse_pressed; - bool left_mouse_held; - bool right_mouse_pressed; - bool connected; -} UsbHidMouseModel; - -static void usb_hid_mouse_draw_callback(Canvas* canvas, void* context) { - furi_assert(context); - UsbHidMouseModel* 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); - }*/ - canvas_set_font(canvas, FontPrimary); - elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Mouse"); - canvas_set_font(canvas, FontSecondary); - - if(model->left_mouse_held == true) { - elements_multiline_text_aligned(canvas, 0, 62, AlignLeft, AlignBottom, "Selecting..."); - } else { - canvas_draw_icon(canvas, 0, 54, &I_Pin_back_arrow_10x8); - canvas_set_font(canvas, FontSecondary); - elements_multiline_text_aligned(canvas, 13, 62, AlignLeft, AlignBottom, "Hold to exit"); - } - - // Keypad circles - canvas_draw_icon(canvas, 64, 8, &I_Circles_47x47); - - // Up - if(model->up_pressed) { - canvas_set_bitmap_mode(canvas, 1); - canvas_draw_icon(canvas, 81, 9, &I_Pressed_Button_13x13); - canvas_set_bitmap_mode(canvas, 0); - canvas_set_color(canvas, ColorWhite); - } - canvas_draw_icon(canvas, 84, 10, &I_Pin_arrow_up_7x9); - canvas_set_color(canvas, ColorBlack); - - // Down - if(model->down_pressed) { - canvas_set_bitmap_mode(canvas, 1); - canvas_draw_icon(canvas, 81, 41, &I_Pressed_Button_13x13); - canvas_set_bitmap_mode(canvas, 0); - canvas_set_color(canvas, ColorWhite); - } - canvas_draw_icon(canvas, 84, 43, &I_Pin_arrow_down_7x9); - canvas_set_color(canvas, ColorBlack); - - // Left - if(model->left_pressed) { - canvas_set_bitmap_mode(canvas, 1); - canvas_draw_icon(canvas, 65, 25, &I_Pressed_Button_13x13); - canvas_set_bitmap_mode(canvas, 0); - canvas_set_color(canvas, ColorWhite); - } - canvas_draw_icon(canvas, 67, 28, &I_Pin_arrow_left_9x7); - canvas_set_color(canvas, ColorBlack); - - // Right - if(model->right_pressed) { - canvas_set_bitmap_mode(canvas, 1); - canvas_draw_icon(canvas, 97, 25, &I_Pressed_Button_13x13); - canvas_set_bitmap_mode(canvas, 0); - canvas_set_color(canvas, ColorWhite); - } - canvas_draw_icon(canvas, 99, 28, &I_Pin_arrow_right_9x7); - canvas_set_color(canvas, ColorBlack); - - // Ok - if(model->left_mouse_pressed) { - canvas_draw_icon(canvas, 81, 25, &I_Ok_btn_pressed_13x13); - } else { - canvas_draw_icon(canvas, 83, 27, &I_Left_mouse_icon_9x9); - } - - // Back - if(model->right_mouse_pressed) { - canvas_draw_icon(canvas, 108, 48, &I_Ok_btn_pressed_13x13); - } else { - canvas_draw_icon(canvas, 110, 50, &I_Right_mouse_icon_9x9); - } -} - -static void usb_hid_mouse_process(UsbHidMouse* usb_hid_mouse, InputEvent* event) { - with_view_model( - usb_hid_mouse->view, - UsbHidMouseModel * model, - { - if(event->key == InputKeyBack) { - if(event->type == InputTypeShort) { - furi_hal_hid_mouse_press(HID_MOUSE_BTN_RIGHT); - furi_hal_hid_mouse_release(HID_MOUSE_BTN_RIGHT); - } else if(event->type == InputTypePress) { - model->right_mouse_pressed = true; - } else if(event->type == InputTypeRelease) { - model->right_mouse_pressed = false; - } - } else if(event->key == InputKeyOk) { - if(event->type == InputTypeShort) { - // Just release if it was being held before - if(!model->left_mouse_held) furi_hal_hid_mouse_press(HID_MOUSE_BTN_LEFT); - furi_hal_hid_mouse_release(HID_MOUSE_BTN_LEFT); - model->left_mouse_held = false; - } else if(event->type == InputTypeLong) { - furi_hal_hid_mouse_press(HID_MOUSE_BTN_LEFT); - model->left_mouse_held = true; - model->left_mouse_pressed = true; - } else if(event->type == InputTypePress) { - model->left_mouse_pressed = true; - } else if(event->type == InputTypeRelease) { - // Only release if it wasn't a long press - if(!model->left_mouse_held) model->left_mouse_pressed = false; - } - - } else if(event->key == InputKeyRight) { - if(event->type == InputTypePress) { - model->right_pressed = true; - furi_hal_hid_mouse_move(MOUSE_MOVE_SHORT, 0); - } else if(event->type == InputTypeRepeat) { - furi_hal_hid_mouse_move(MOUSE_MOVE_LONG, 0); - } else if(event->type == InputTypeRelease) { - model->right_pressed = false; - } - } else if(event->key == InputKeyLeft) { - if(event->type == InputTypePress) { - model->left_pressed = true; - furi_hal_hid_mouse_move(-MOUSE_MOVE_SHORT, 0); - } else if(event->type == InputTypeRepeat) { - furi_hal_hid_mouse_move(-MOUSE_MOVE_LONG, 0); - } else if(event->type == InputTypeRelease) { - model->left_pressed = false; - } - } else if(event->key == InputKeyDown) { - if(event->type == InputTypePress) { - model->down_pressed = true; - furi_hal_hid_mouse_move(0, MOUSE_MOVE_SHORT); - } else if(event->type == InputTypeRepeat) { - furi_hal_hid_mouse_move(0, MOUSE_MOVE_LONG); - } else if(event->type == InputTypeRelease) { - model->down_pressed = false; - } - } else if(event->key == InputKeyUp) { - if(event->type == InputTypePress) { - model->up_pressed = true; - furi_hal_hid_mouse_move(0, -MOUSE_MOVE_SHORT); - } else if(event->type == InputTypeRepeat) { - furi_hal_hid_mouse_move(0, -MOUSE_MOVE_LONG); - } else if(event->type == InputTypeRelease) { - model->up_pressed = false; - } - } - }, - true); -} - -static bool usb_hid_mouse_input_callback(InputEvent* event, void* context) { - furi_assert(context); - UsbHidMouse* usb_hid_mouse = context; - bool consumed = false; - - if(event->type == InputTypeLong && event->key == InputKeyBack) { - furi_hal_hid_mouse_release(HID_MOUSE_BTN_LEFT); - furi_hal_hid_mouse_release(HID_MOUSE_BTN_RIGHT); - } else { - usb_hid_mouse_process(usb_hid_mouse, event); - consumed = true; - } - - return consumed; -} - -UsbHidMouse* usb_hid_mouse_alloc() { - UsbHidMouse* usb_hid_mouse = malloc(sizeof(UsbHidMouse)); - usb_hid_mouse->view = view_alloc(); - view_set_context(usb_hid_mouse->view, usb_hid_mouse); - view_allocate_model(usb_hid_mouse->view, ViewModelTypeLocking, sizeof(UsbHidMouseModel)); - view_set_draw_callback(usb_hid_mouse->view, usb_hid_mouse_draw_callback); - view_set_input_callback(usb_hid_mouse->view, usb_hid_mouse_input_callback); - - with_view_model( - usb_hid_mouse->view, UsbHidMouseModel * model, { model->connected = true; }, true); - - return usb_hid_mouse; -} - -void usb_hid_mouse_free(UsbHidMouse* usb_hid_mouse) { - furi_assert(usb_hid_mouse); - view_free(usb_hid_mouse->view); - free(usb_hid_mouse); -} - -View* usb_hid_mouse_get_view(UsbHidMouse* usb_hid_mouse) { - furi_assert(usb_hid_mouse); - return usb_hid_mouse->view; -} diff --git a/applications/plugins/usbkeyboard/views/usb_hid_mouse.h b/applications/plugins/usbkeyboard/views/usb_hid_mouse.h deleted file mode 100644 index e7eec9224..000000000 --- a/applications/plugins/usbkeyboard/views/usb_hid_mouse.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include - -typedef struct UsbHidMouse UsbHidMouse; - -UsbHidMouse* usb_hid_mouse_alloc(); - -void usb_hid_mouse_free(UsbHidMouse* usb_hid_mouse); - -View* usb_hid_mouse_get_view(UsbHidMouse* usb_hid_mouse); - -void usb_hid_mouse_set_connected_status(UsbHidMouse* usb_hid_mouse, bool connected); diff --git a/applications/plugins/usbkeyboard/views/usb_hid_mouse_jiggler.c b/applications/plugins/usbkeyboard/views/usb_hid_mouse_jiggler.c deleted file mode 100644 index fe0c618d2..000000000 --- a/applications/plugins/usbkeyboard/views/usb_hid_mouse_jiggler.c +++ /dev/null @@ -1,130 +0,0 @@ -#include "usb_hid_mouse_jiggler.h" -#include -#include -#include - -#include - -#define TAG "HidMouseJiggler" - -struct HidMouseJiggler { - View* view; - FuriTimer* timer; -}; - -typedef struct { - bool running; - uint8_t counter; -} HidMouseJigglerModel; - -static void hid_mouse_jiggler_draw_callback(Canvas* canvas, void* context) { - furi_assert(context); - HidMouseJigglerModel* model = context; - - // Header - canvas_set_font(canvas, FontPrimary); - elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Mouse Jiggler"); - - canvas_set_font(canvas, FontPrimary); - elements_multiline_text(canvas, AlignLeft, 35, "Press Start\nto jiggle"); - canvas_set_font(canvas, FontSecondary); - - // Ok - canvas_draw_icon(canvas, 63, 25, &I_Space_65x18); - if(model->running) { - elements_slightly_rounded_box(canvas, 66, 27, 60, 13); - canvas_set_color(canvas, ColorWhite); - } - canvas_draw_icon(canvas, 74, 29, &I_Ok_btn_9x9); - if(model->running) { - elements_multiline_text_aligned(canvas, 91, 36, AlignLeft, AlignBottom, "Stop"); - } else { - elements_multiline_text_aligned(canvas, 91, 36, AlignLeft, AlignBottom, "Start"); - } - canvas_set_color(canvas, ColorBlack); - - // Back - canvas_draw_icon(canvas, 74, 49, &I_Pin_back_arrow_10x8); - elements_multiline_text_aligned(canvas, 91, 57, AlignLeft, AlignBottom, "Quit"); -} - -static void hid_mouse_jiggler_timer_callback(void* context) { - furi_assert(context); - HidMouseJiggler* hid_mouse_jiggler = context; - with_view_model( - hid_mouse_jiggler->view, - HidMouseJigglerModel * model, - { - if(model->running) { - model->counter++; - furi_hal_hid_mouse_move( - (model->counter % 2 == 0) ? MOUSE_MOVE_SHORT : -MOUSE_MOVE_SHORT, 0); - } - }, - false); -} - -static void hid_mouse_jiggler_enter_callback(void* context) { - furi_assert(context); - HidMouseJiggler* hid_mouse_jiggler = context; - - furi_timer_start(hid_mouse_jiggler->timer, 500); -} - -static void hid_mouse_jiggler_exit_callback(void* context) { - furi_assert(context); - HidMouseJiggler* hid_mouse_jiggler = context; - furi_timer_stop(hid_mouse_jiggler->timer); -} - -static bool hid_mouse_jiggler_input_callback(InputEvent* event, void* context) { - furi_assert(context); - HidMouseJiggler* hid_mouse_jiggler = context; - - bool consumed = false; - - if(event->key == InputKeyOk) { - with_view_model( - hid_mouse_jiggler->view, - HidMouseJigglerModel * model, - { model->running = !model->running; }, - true); - consumed = true; - } - - return consumed; -} - -HidMouseJiggler* hid_mouse_jiggler_alloc() { - HidMouseJiggler* hid_mouse_jiggler = malloc(sizeof(HidMouseJiggler)); - - hid_mouse_jiggler->view = view_alloc(); - view_set_context(hid_mouse_jiggler->view, hid_mouse_jiggler); - view_allocate_model( - hid_mouse_jiggler->view, ViewModelTypeLocking, sizeof(HidMouseJigglerModel)); - view_set_draw_callback(hid_mouse_jiggler->view, hid_mouse_jiggler_draw_callback); - view_set_input_callback(hid_mouse_jiggler->view, hid_mouse_jiggler_input_callback); - view_set_enter_callback(hid_mouse_jiggler->view, hid_mouse_jiggler_enter_callback); - view_set_exit_callback(hid_mouse_jiggler->view, hid_mouse_jiggler_exit_callback); - - hid_mouse_jiggler->timer = furi_timer_alloc( - hid_mouse_jiggler_timer_callback, FuriTimerTypePeriodic, hid_mouse_jiggler); - - return hid_mouse_jiggler; -} - -void hid_mouse_jiggler_free(HidMouseJiggler* hid_mouse_jiggler) { - furi_assert(hid_mouse_jiggler); - - furi_timer_stop(hid_mouse_jiggler->timer); - furi_timer_free(hid_mouse_jiggler->timer); - - view_free(hid_mouse_jiggler->view); - - free(hid_mouse_jiggler); -} - -View* hid_mouse_jiggler_get_view(HidMouseJiggler* hid_mouse_jiggler) { - furi_assert(hid_mouse_jiggler); - return hid_mouse_jiggler->view; -} diff --git a/applications/plugins/usbkeyboard/views/usb_hid_mouse_jiggler.h b/applications/plugins/usbkeyboard/views/usb_hid_mouse_jiggler.h deleted file mode 100644 index 24cf877a7..000000000 --- a/applications/plugins/usbkeyboard/views/usb_hid_mouse_jiggler.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include - -#define MOUSE_MOVE_SHORT 5 -#define MOUSE_MOVE_LONG 20 - -typedef struct HidMouseJiggler HidMouseJiggler; - -HidMouseJiggler* hid_mouse_jiggler_alloc(); - -void hid_mouse_jiggler_free(HidMouseJiggler* hid_mouse_jiggler); - -View* hid_mouse_jiggler_get_view(HidMouseJiggler* hid_mouse_jiggler); diff --git a/applications/plugins/wav_player/README.md b/applications/plugins/wav_player/README.md new file mode 100644 index 000000000..ef87e1e77 --- /dev/null +++ b/applications/plugins/wav_player/README.md @@ -0,0 +1,4 @@ +# WAV player + A Flipper Zero application for playing wav files. My fork adds support for correct playback speed (for files with different sample rates) and for mono files (original wav player only plays stereo). You still need to convert your file to unsigned 8-bit PCM format for it to played correctly on flipper. + +Original app by https://github.com/DrZlo13. diff --git a/applications/plugins/wav_player/application.fam b/applications/plugins/wav_player/application.fam index ec0c76291..4040ed159 100644 --- a/applications/plugins/wav_player/application.fam +++ b/applications/plugins/wav_player/application.fam @@ -5,7 +5,7 @@ App( entry_point="wav_player_app", cdefines=["APP_WAV_PLAYER"], stack_size=4 * 1024, - order=60, + order=46, fap_icon="wav_10px.png", fap_category="Music", fap_icon_assets="images", diff --git a/applications/plugins/wav_player/wav_parser.c b/applications/plugins/wav_player/wav_parser.c index c2897706c..1f534bacb 100644 --- a/applications/plugins/wav_player/wav_parser.c +++ b/applications/plugins/wav_player/wav_parser.c @@ -29,7 +29,7 @@ void wav_parser_free(WavParser* parser) { free(parser); } -bool wav_parser_parse(WavParser* parser, Stream* stream) { +bool wav_parser_parse(WavParser* parser, Stream* stream, WavPlayerApp* app) { stream_read(stream, (uint8_t*)&parser->header, sizeof(WavHeaderChunk)); stream_read(stream, (uint8_t*)&parser->format, sizeof(WavFormatChunk)); stream_read(stream, (uint8_t*)&parser->data, sizeof(WavDataChunk)); @@ -63,6 +63,10 @@ bool wav_parser_parse(WavParser* parser, Stream* stream) { parser->format.byte_per_sec, parser->format.bits_per_sample); + app->sample_rate = parser->format.sample_rate; + app->num_channels = parser->format.channels; + app->bits_per_sample = parser->format.bits_per_sample; + parser->wav_data_start = stream_tell(stream); parser->wav_data_end = parser->wav_data_start + parser->data.size; diff --git a/applications/plugins/wav_player/wav_parser.h b/applications/plugins/wav_player/wav_parser.h index f50c48b3f..8d4139865 100644 --- a/applications/plugins/wav_player/wav_parser.h +++ b/applications/plugins/wav_player/wav_parser.h @@ -1,6 +1,18 @@ #pragma once #include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wav_player_view.h" + #ifdef __cplusplus extern "C" { #endif @@ -34,11 +46,37 @@ typedef struct { typedef struct WavParser WavParser; +typedef struct { + Storage* storage; + Stream* stream; + WavParser* parser; + uint16_t* sample_buffer; + uint8_t* tmp_buffer; + + uint32_t sample_rate; + + uint16_t num_channels; + uint16_t bits_per_sample; + + size_t samples_count_half; + size_t samples_count; + + FuriMessageQueue* queue; + + float volume; + bool play; + + WavPlayerView* view; + ViewDispatcher* view_dispatcher; + Gui* gui; + NotificationApp* notification; +} WavPlayerApp; + WavParser* wav_parser_alloc(); void wav_parser_free(WavParser* parser); -bool wav_parser_parse(WavParser* parser, Stream* stream); +bool wav_parser_parse(WavParser* parser, Stream* stream, WavPlayerApp* app); size_t wav_parser_get_data_start(WavParser* parser); @@ -48,4 +86,4 @@ size_t wav_parser_get_data_len(WavParser* parser); #ifdef __cplusplus } -#endif \ No newline at end of file +#endif diff --git a/applications/plugins/wav_player/wav_player.c b/applications/plugins/wav_player/wav_player.c index 0a57162ff..3fb8b1ea5 100644 --- a/applications/plugins/wav_player/wav_player.c +++ b/applications/plugins/wav_player/wav_player.c @@ -11,7 +11,6 @@ #include "wav_parser.h" #include "wav_player_view.h" #include -#include #include @@ -80,27 +79,6 @@ static void wav_player_dma_isr(void* ctx) { } } -typedef struct { - Storage* storage; - Stream* stream; - WavParser* parser; - uint16_t* sample_buffer; - uint8_t* tmp_buffer; - - size_t samples_count_half; - size_t samples_count; - - FuriMessageQueue* queue; - - float volume; - bool play; - - WavPlayerView* view; - ViewDispatcher* view_dispatcher; - Gui* gui; - NotificationApp* notification; -} WavPlayerApp; - static WavPlayerApp* app_alloc() { WavPlayerApp* app = malloc(sizeof(WavPlayerApp)); app->samples_count_half = 1024 * 4; @@ -149,38 +127,83 @@ static void app_free(WavPlayerApp* app) { // TODO: that works only with 8-bit 2ch audio static bool fill_data(WavPlayerApp* app, size_t index) { - uint16_t* sample_buffer_start = &app->sample_buffer[index]; - size_t count = stream_read(app->stream, app->tmp_buffer, app->samples_count); + if(app->num_channels == 1) { + uint16_t* sample_buffer_start = &app->sample_buffer[index]; + size_t count = stream_read(app->stream, app->tmp_buffer, app->samples_count_half); - for(size_t i = count; i < app->samples_count; i++) { - app->tmp_buffer[i] = 0; - } - - for(size_t i = 0; i < app->samples_count; i += 2) { - float data = app->tmp_buffer[i]; - data -= UINT8_MAX / 2; // to signed - data /= UINT8_MAX / 2; // scale -1..1 - - data *= app->volume; // volume - data = tanhf(data); // hyperbolic tangent limiter - - data *= UINT8_MAX / 2; // scale -128..127 - data += UINT8_MAX / 2; // to unsigned - - if(data < 0) { - data = 0; + for(size_t i = count; i < app->samples_count_half; i++) { + app->tmp_buffer[i] = 0; } - if(data > 255) { - data = 255; + //for(size_t i = 0; i < app->samples_count; i += 2) + for(size_t i = 0; i < app->samples_count_half; i++) //now works with mono! + { + float data = app->tmp_buffer[i]; + data -= UINT8_MAX / 2; // to signed + data /= UINT8_MAX / 2; // scale -1..1 + + data *= app->volume; // volume + data = tanhf(data); // hyperbolic tangent limiter + + data *= UINT8_MAX / 2; // scale -128..127 + data += UINT8_MAX / 2; // to unsigned + + if(data < 0) { + data = 0; + } + + if(data > 255) { + data = 255; + } + + //uint8_t data = app->tmp_buffer[i]; + + sample_buffer_start[i] = data; } - sample_buffer_start[i / 2] = data; + wav_player_view_set_data(app->view, sample_buffer_start, app->samples_count_half); + + return count != app->samples_count_half; } - wav_player_view_set_data(app->view, sample_buffer_start, app->samples_count_half); + if(app->num_channels == 2) { + uint16_t* sample_buffer_start = &app->sample_buffer[index]; + size_t count = stream_read(app->stream, app->tmp_buffer, app->samples_count); - return count != app->samples_count; + for(size_t i = count; i < app->samples_count; i++) { + app->tmp_buffer[i] = 0; + } + + for(size_t i = 0; i < app->samples_count; i += 2) { + float data = (app->tmp_buffer[i] + app->tmp_buffer[i + 1]) / 2; // (L + R) / 2 + data -= UINT8_MAX / 2; // to signed + data /= UINT8_MAX / 2; // scale -1..1 + + data *= app->volume; // volume + data = tanhf(data); // hyperbolic tangent limiter + + data *= UINT8_MAX / 2; // scale -128..127 + data += UINT8_MAX / 2; // to unsigned + + if(data < 0) { + data = 0; + } + + if(data > 255) { + data = 255; + } + + //uint8_t data = app->tmp_buffer[i]; + + sample_buffer_start[i / 2] = data; + } + + wav_player_view_set_data(app->view, sample_buffer_start, app->samples_count_half); + + return count != app->samples_count; + } + + return true; } static void ctrl_callback(WavPlayerCtrl ctrl, void* ctx) { @@ -219,7 +242,7 @@ static void ctrl_callback(WavPlayerCtrl ctrl, void* ctx) { static void app_run(WavPlayerApp* app) { if(!open_wav_stream(app->stream)) return; - if(!wav_parser_parse(app->parser, app->stream)) return; + if(!wav_parser_parse(app->parser, app->stream, app)) return; wav_player_view_set_volume(app->view, app->volume); wav_player_view_set_start(app->view, wav_parser_get_data_start(app->parser)); @@ -233,7 +256,7 @@ static void app_run(WavPlayerApp* app) { bool eof = fill_data(app, 0); eof = fill_data(app, app->samples_count_half); - wav_player_speaker_init(); + wav_player_speaker_init(app->sample_rate); wav_player_dma_init((uint32_t)app->sample_buffer, app->samples_count); furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch1, wav_player_dma_isr, app->queue); diff --git a/applications/plugins/wav_player/wav_player_hal.c b/applications/plugins/wav_player/wav_player_hal.c index ad0c019be..e711eccb4 100644 --- a/applications/plugins/wav_player/wav_player_hal.c +++ b/applications/plugins/wav_player/wav_player_hal.c @@ -2,14 +2,26 @@ #include #include +#include +#include +#include +#include + +//#define FURI_HAL_SPEAKER_TIMER TIM16 + #define FURI_HAL_SPEAKER_TIMER TIM16 + +#define SAMPLE_RATE_TIMER TIM2 + #define FURI_HAL_SPEAKER_CHANNEL LL_TIM_CHANNEL_CH1 #define DMA_INSTANCE DMA1, LL_DMA_CHANNEL_1 -void wav_player_speaker_init() { +void wav_player_speaker_init(uint32_t sample_rate) { LL_TIM_InitTypeDef TIM_InitStruct = {0}; - TIM_InitStruct.Prescaler = 4; - TIM_InitStruct.Autoreload = 255; + //TIM_InitStruct.Prescaler = 4; + TIM_InitStruct.Prescaler = 1; + TIM_InitStruct.Autoreload = + 255; //in this fork used purely as PWM timer, the DMA now is triggered by SAMPLE_RATE_TIMER LL_TIM_Init(FURI_HAL_SPEAKER_TIMER, &TIM_InitStruct); LL_TIM_OC_InitTypeDef TIM_OC_InitStruct = {0}; @@ -17,16 +29,51 @@ void wav_player_speaker_init() { TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_ENABLE; TIM_OC_InitStruct.CompareValue = 127; LL_TIM_OC_Init(FURI_HAL_SPEAKER_TIMER, FURI_HAL_SPEAKER_CHANNEL, &TIM_OC_InitStruct); + + //====================================================== + + TIM_InitStruct.Prescaler = 0; + //TIM_InitStruct.Autoreload = 1451; //64 000 000 / 1451 ~= 44100 Hz + + TIM_InitStruct.Autoreload = 64000000 / sample_rate; //to support various sample rates + + LL_TIM_Init(SAMPLE_RATE_TIMER, &TIM_InitStruct); + + //LL_TIM_OC_InitTypeDef TIM_OC_InitStruct = {0}; + TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_PWM1; + TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_ENABLE; + TIM_OC_InitStruct.CompareValue = 127; + LL_TIM_OC_Init(SAMPLE_RATE_TIMER, FURI_HAL_SPEAKER_CHANNEL, &TIM_OC_InitStruct); + + //========================================================= + //configuring PA6 pin as TIM16 output + + //furi_hal_gpio_init_ex(&gpio_ext_pa6, (GpioMode)GpioPullNo, (GpioPull)GpioModeAltFunctionPushPull, GpioSpeedVeryHigh, GpioAltFn14TIM16); + //furi_hal_gpio_init_ex(&gpio_ext_pa6, (GpioMode)GpioPullNo, (GpioPull)GpioModeAltFunctionPushPull, GpioSpeedLow, GpioAltFn14TIM16); + furi_hal_gpio_init_ex( + &gpio_ext_pa6, + GpioModeAltFunctionPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFn14TIM16); + //furi_hal_gpio_init_simple(&gpio_ext_pa6, GpioModeOutputPushPull); + //furi_hal_gpio_write(&gpio_ext_pa6, false); } void wav_player_speaker_start() { LL_TIM_EnableAllOutputs(FURI_HAL_SPEAKER_TIMER); LL_TIM_EnableCounter(FURI_HAL_SPEAKER_TIMER); + + LL_TIM_EnableAllOutputs(SAMPLE_RATE_TIMER); + LL_TIM_EnableCounter(SAMPLE_RATE_TIMER); } void wav_player_speaker_stop() { LL_TIM_DisableAllOutputs(FURI_HAL_SPEAKER_TIMER); LL_TIM_DisableCounter(FURI_HAL_SPEAKER_TIMER); + + LL_TIM_DisableAllOutputs(SAMPLE_RATE_TIMER); + LL_TIM_DisableCounter(SAMPLE_RATE_TIMER); } void wav_player_dma_init(uint32_t address, size_t size) { @@ -35,7 +82,7 @@ void wav_player_dma_init(uint32_t address, size_t size) { LL_DMA_ConfigAddresses(DMA_INSTANCE, address, dma_dst, LL_DMA_DIRECTION_MEMORY_TO_PERIPH); LL_DMA_SetDataLength(DMA_INSTANCE, size); - LL_DMA_SetPeriphRequest(DMA_INSTANCE, LL_DMAMUX_REQ_TIM16_UP); + LL_DMA_SetPeriphRequest(DMA_INSTANCE, LL_DMAMUX_REQ_TIM2_UP); LL_DMA_SetDataTransferDirection(DMA_INSTANCE, LL_DMA_DIRECTION_MEMORY_TO_PERIPH); LL_DMA_SetChannelPriorityLevel(DMA_INSTANCE, LL_DMA_PRIORITY_VERYHIGH); LL_DMA_SetMode(DMA_INSTANCE, LL_DMA_MODE_CIRCULAR); @@ -50,9 +97,9 @@ void wav_player_dma_init(uint32_t address, size_t size) { void wav_player_dma_start() { LL_DMA_EnableChannel(DMA_INSTANCE); - LL_TIM_EnableDMAReq_UPDATE(FURI_HAL_SPEAKER_TIMER); + LL_TIM_EnableDMAReq_UPDATE(SAMPLE_RATE_TIMER); } void wav_player_dma_stop() { LL_DMA_DisableChannel(DMA_INSTANCE); -} \ No newline at end of file +} diff --git a/applications/plugins/wav_player/wav_player_view.c b/applications/plugins/wav_player/wav_player_view.c index fdf08cb21..eee39c902 100644 --- a/applications/plugins/wav_player/wav_player_view.c +++ b/applications/plugins/wav_player/wav_player_view.c @@ -1,22 +1,5 @@ #include "wav_player_view.h" -#define DATA_COUNT 116 - -struct WavPlayerView { - View* view; - WavPlayerCtrlCallback callback; - void* context; -}; - -typedef struct { - bool play; - float volume; - size_t start; - size_t end; - size_t current; - uint8_t data[DATA_COUNT]; -} WavPlayerViewModel; - float map(float x, float in_min, float in_max, float out_min, float out_max) { return (x - in_min) * (out_max - out_min + 1) / (in_max - in_min + 1) + out_min; } @@ -30,7 +13,7 @@ static void wav_player_view_draw_callback(Canvas* canvas, void* _model) { uint8_t y_pos = 0; // volume - x_pos = 124; + x_pos = 123; y_pos = 0; const float volume = (64 / 10.0f) * model->volume; canvas_draw_frame(canvas, x_pos, y_pos, 4, 64); @@ -198,4 +181,4 @@ void wav_player_view_set_ctrl_callback(WavPlayerView* wav_view, WavPlayerCtrlCal void wav_player_view_set_context(WavPlayerView* wav_view, void* context) { furi_assert(wav_view); wav_view->context = context; -} \ No newline at end of file +} diff --git a/applications/plugins/wav_player/wav_player_view.h b/applications/plugins/wav_player/wav_player_view.h index 246aeaf3e..d841b04c0 100644 --- a/applications/plugins/wav_player/wav_player_view.h +++ b/applications/plugins/wav_player/wav_player_view.h @@ -1,6 +1,16 @@ #pragma once #include +#include +#include +#include +#include +#include +#include +#include +#include +#include + #ifdef __cplusplus extern "C" { #endif @@ -18,6 +28,23 @@ typedef enum { typedef void (*WavPlayerCtrlCallback)(WavPlayerCtrl ctrl, void* context); +#define DATA_COUNT 116 + +struct WavPlayerView { + View* view; + WavPlayerCtrlCallback callback; + void* context; +}; + +typedef struct { + bool play; + float volume; + size_t start; + size_t end; + size_t current; + uint8_t data[DATA_COUNT]; +} WavPlayerViewModel; + WavPlayerView* wav_player_view_alloc(); void wav_player_view_free(WavPlayerView* wav_view); @@ -42,4 +69,4 @@ void wav_player_view_set_context(WavPlayerView* wav_view, void* context); #ifdef __cplusplus } -#endif \ No newline at end of file +#endif diff --git a/applications/plugins/weather_station/application.fam b/applications/plugins/weather_station/application.fam index d826f51ea..935f92573 100644 --- a/applications/plugins/weather_station/application.fam +++ b/applications/plugins/weather_station/application.fam @@ -1,5 +1,5 @@ App( - appid="Weather_Station", + appid="weather_station", name="Weather Station", apptype=FlipperAppType.PLUGIN, targets=["f7"], @@ -9,6 +9,6 @@ App( stack_size=4 * 1024, order=50, fap_icon="weather_station_10px.png", - fap_category="Tools", + fap_category="Sub-GHz", fap_icon_assets="images", ) diff --git a/applications/plugins/weather_station/helpers/weather_station_types.h b/applications/plugins/weather_station/helpers/weather_station_types.h index 1f5612e2e..111465978 100644 --- a/applications/plugins/weather_station/helpers/weather_station_types.h +++ b/applications/plugins/weather_station/helpers/weather_station_types.h @@ -3,7 +3,7 @@ #include #include -#define WS_VERSION_APP "0.7" +#define WS_VERSION_APP "0.8" #define WS_DEVELOPED "SkorP" #define WS_GITHUB "https://github.com/flipperdevices/flipperzero-firmware" diff --git a/applications/plugins/weather_station/protocols/acurite_592txr.c b/applications/plugins/weather_station/protocols/acurite_592txr.c index 5384a3c91..874f6dd33 100644 --- a/applications/plugins/weather_station/protocols/acurite_592txr.c +++ b/applications/plugins/weather_station/protocols/acurite_592txr.c @@ -258,7 +258,7 @@ uint8_t ws_protocol_decoder_acurite_592txr_get_hash_data(void* context) { &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } -bool ws_protocol_decoder_acurite_592txr_serialize( +SubGhzProtocolStatus ws_protocol_decoder_acurite_592txr_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset) { @@ -267,22 +267,14 @@ bool ws_protocol_decoder_acurite_592txr_serialize( return ws_block_generic_serialize(&instance->generic, flipper_format, preset); } -bool ws_protocol_decoder_acurite_592txr_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + ws_protocol_decoder_acurite_592txr_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); WSProtocolDecoderAcurite_592TXR* instance = context; - bool ret = false; - do { - if(!ws_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit != - ws_protocol_acurite_592txr_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); - break; - } - ret = true; - } while(false); - return ret; + return ws_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + ws_protocol_acurite_592txr_const.min_count_bit_for_found); } void ws_protocol_decoder_acurite_592txr_get_string(void* context, FuriString* output) { diff --git a/applications/plugins/weather_station/protocols/acurite_592txr.h b/applications/plugins/weather_station/protocols/acurite_592txr.h index ac0371d89..1071d1cf7 100644 --- a/applications/plugins/weather_station/protocols/acurite_592txr.h +++ b/applications/plugins/weather_station/protocols/acurite_592txr.h @@ -56,9 +56,9 @@ uint8_t ws_protocol_decoder_acurite_592txr_get_hash_data(void* context); * @param context Pointer to a WSProtocolDecoderAcurite_592TXR instance * @param flipper_format Pointer to a FlipperFormat instance * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success + * @return status */ -bool ws_protocol_decoder_acurite_592txr_serialize( +SubGhzProtocolStatus ws_protocol_decoder_acurite_592txr_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); @@ -67,9 +67,10 @@ bool ws_protocol_decoder_acurite_592txr_serialize( * Deserialize data WSProtocolDecoderAcurite_592TXR. * @param context Pointer to a WSProtocolDecoderAcurite_592TXR instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool ws_protocol_decoder_acurite_592txr_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + ws_protocol_decoder_acurite_592txr_deserialize(void* context, FlipperFormat* flipper_format); /** * Getting a textual representation of the received data. diff --git a/applications/plugins/weather_station/protocols/acurite_606tx.c b/applications/plugins/weather_station/protocols/acurite_606tx.c index 4cb5d18b8..e0d405c66 100644 --- a/applications/plugins/weather_station/protocols/acurite_606tx.c +++ b/applications/plugins/weather_station/protocols/acurite_606tx.c @@ -199,7 +199,7 @@ uint8_t ws_protocol_decoder_acurite_606tx_get_hash_data(void* context) { &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } -bool ws_protocol_decoder_acurite_606tx_serialize( +SubGhzProtocolStatus ws_protocol_decoder_acurite_606tx_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset) { @@ -208,22 +208,14 @@ bool ws_protocol_decoder_acurite_606tx_serialize( return ws_block_generic_serialize(&instance->generic, flipper_format, preset); } -bool ws_protocol_decoder_acurite_606tx_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + ws_protocol_decoder_acurite_606tx_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); WSProtocolDecoderAcurite_606TX* instance = context; - bool ret = false; - do { - if(!ws_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit != - ws_protocol_acurite_606tx_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); - break; - } - ret = true; - } while(false); - return ret; + return ws_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + ws_protocol_acurite_606tx_const.min_count_bit_for_found); } void ws_protocol_decoder_acurite_606tx_get_string(void* context, FuriString* output) { diff --git a/applications/plugins/weather_station/protocols/acurite_606tx.h b/applications/plugins/weather_station/protocols/acurite_606tx.h index 5bab3bcb7..15b6d1eb5 100644 --- a/applications/plugins/weather_station/protocols/acurite_606tx.h +++ b/applications/plugins/weather_station/protocols/acurite_606tx.h @@ -56,9 +56,9 @@ uint8_t ws_protocol_decoder_acurite_606tx_get_hash_data(void* context); * @param context Pointer to a WSProtocolDecoderAcurite_606TX instance * @param flipper_format Pointer to a FlipperFormat instance * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success + * @return status */ -bool ws_protocol_decoder_acurite_606tx_serialize( +SubGhzProtocolStatus ws_protocol_decoder_acurite_606tx_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); @@ -67,9 +67,10 @@ bool ws_protocol_decoder_acurite_606tx_serialize( * Deserialize data WSProtocolDecoderAcurite_606TX. * @param context Pointer to a WSProtocolDecoderAcurite_606TX instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool ws_protocol_decoder_acurite_606tx_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + ws_protocol_decoder_acurite_606tx_deserialize(void* context, FlipperFormat* flipper_format); /** * Getting a textual representation of the received data. diff --git a/applications/plugins/weather_station/protocols/acurite_609txc.c b/applications/plugins/weather_station/protocols/acurite_609txc.c index aeb0785eb..853b78446 100644 --- a/applications/plugins/weather_station/protocols/acurite_609txc.c +++ b/applications/plugins/weather_station/protocols/acurite_609txc.c @@ -199,7 +199,7 @@ uint8_t ws_protocol_decoder_acurite_609txc_get_hash_data(void* context) { &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } -bool ws_protocol_decoder_acurite_609txc_serialize( +SubGhzProtocolStatus ws_protocol_decoder_acurite_609txc_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset) { @@ -208,22 +208,14 @@ bool ws_protocol_decoder_acurite_609txc_serialize( return ws_block_generic_serialize(&instance->generic, flipper_format, preset); } -bool ws_protocol_decoder_acurite_609txc_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + ws_protocol_decoder_acurite_609txc_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); WSProtocolDecoderAcurite_609TXC* instance = context; - bool ret = false; - do { - if(!ws_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit != - ws_protocol_acurite_609txc_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); - break; - } - ret = true; - } while(false); - return ret; + return ws_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + ws_protocol_acurite_609txc_const.min_count_bit_for_found); } void ws_protocol_decoder_acurite_609txc_get_string(void* context, FuriString* output) { diff --git a/applications/plugins/weather_station/protocols/acurite_609txc.h b/applications/plugins/weather_station/protocols/acurite_609txc.h index f87c20e9b..3e3b9cee8 100644 --- a/applications/plugins/weather_station/protocols/acurite_609txc.h +++ b/applications/plugins/weather_station/protocols/acurite_609txc.h @@ -56,9 +56,9 @@ uint8_t ws_protocol_decoder_acurite_609txc_get_hash_data(void* context); * @param context Pointer to a WSProtocolDecoderAcurite_609TXC instance * @param flipper_format Pointer to a FlipperFormat instance * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success + * @return status */ -bool ws_protocol_decoder_acurite_609txc_serialize( +SubGhzProtocolStatus ws_protocol_decoder_acurite_609txc_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); @@ -67,9 +67,10 @@ bool ws_protocol_decoder_acurite_609txc_serialize( * Deserialize data WSProtocolDecoderAcurite_609TXC. * @param context Pointer to a WSProtocolDecoderAcurite_609TXC instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool ws_protocol_decoder_acurite_609txc_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + ws_protocol_decoder_acurite_609txc_deserialize(void* context, FlipperFormat* flipper_format); /** * Getting a textual representation of the received data. diff --git a/applications/plugins/weather_station/protocols/ambient_weather.c b/applications/plugins/weather_station/protocols/ambient_weather.c index e3c85f40b..588a7f82a 100644 --- a/applications/plugins/weather_station/protocols/ambient_weather.c +++ b/applications/plugins/weather_station/protocols/ambient_weather.c @@ -228,7 +228,7 @@ uint8_t ws_protocol_decoder_ambient_weather_get_hash_data(void* context) { &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } -bool ws_protocol_decoder_ambient_weather_serialize( +SubGhzProtocolStatus ws_protocol_decoder_ambient_weather_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset) { @@ -237,22 +237,14 @@ bool ws_protocol_decoder_ambient_weather_serialize( return ws_block_generic_serialize(&instance->generic, flipper_format, preset); } -bool ws_protocol_decoder_ambient_weather_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + ws_protocol_decoder_ambient_weather_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); WSProtocolDecoderAmbient_Weather* instance = context; - bool ret = false; - do { - if(!ws_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit != - ws_protocol_ambient_weather_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); - break; - } - ret = true; - } while(false); - return ret; + return ws_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + ws_protocol_ambient_weather_const.min_count_bit_for_found); } void ws_protocol_decoder_ambient_weather_get_string(void* context, FuriString* output) { diff --git a/applications/plugins/weather_station/protocols/ambient_weather.h b/applications/plugins/weather_station/protocols/ambient_weather.h index 04cc5819c..1694403cd 100644 --- a/applications/plugins/weather_station/protocols/ambient_weather.h +++ b/applications/plugins/weather_station/protocols/ambient_weather.h @@ -56,9 +56,9 @@ uint8_t ws_protocol_decoder_ambient_weather_get_hash_data(void* context); * @param context Pointer to a WSProtocolDecoderAmbient_Weather instance * @param flipper_format Pointer to a FlipperFormat instance * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success + * @return status */ -bool ws_protocol_decoder_ambient_weather_serialize( +SubGhzProtocolStatus ws_protocol_decoder_ambient_weather_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); @@ -67,9 +67,10 @@ bool ws_protocol_decoder_ambient_weather_serialize( * Deserialize data WSProtocolDecoderAmbient_Weather. * @param context Pointer to a WSProtocolDecoderAmbient_Weather instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool ws_protocol_decoder_ambient_weather_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + ws_protocol_decoder_ambient_weather_deserialize(void* context, FlipperFormat* flipper_format); /** * Getting a textual representation of the received data. diff --git a/applications/plugins/weather_station/protocols/auriol_hg0601a.c b/applications/plugins/weather_station/protocols/auriol_hg0601a.c index d5f89fc8b..813fe1526 100644 --- a/applications/plugins/weather_station/protocols/auriol_hg0601a.c +++ b/applications/plugins/weather_station/protocols/auriol_hg0601a.c @@ -210,7 +210,7 @@ uint8_t ws_protocol_decoder_auriol_th_get_hash_data(void* context) { &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } -bool ws_protocol_decoder_auriol_th_serialize( +SubGhzProtocolStatus ws_protocol_decoder_auriol_th_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset) { @@ -219,22 +219,12 @@ bool ws_protocol_decoder_auriol_th_serialize( return ws_block_generic_serialize(&instance->generic, flipper_format, preset); } -bool ws_protocol_decoder_auriol_th_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + ws_protocol_decoder_auriol_th_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); WSProtocolDecoderAuriol_TH* instance = context; - bool ret = false; - do { - if(!ws_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit != - ws_protocol_auriol_th_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); - break; - } - ret = true; - } while(false); - return ret; + return ws_block_generic_deserialize_check_count_bit( + &instance->generic, flipper_format, ws_protocol_auriol_th_const.min_count_bit_for_found); } void ws_protocol_decoder_auriol_th_get_string(void* context, FuriString* output) { diff --git a/applications/plugins/weather_station/protocols/auriol_hg0601a.h b/applications/plugins/weather_station/protocols/auriol_hg0601a.h index c23007c1a..155bb07fc 100644 --- a/applications/plugins/weather_station/protocols/auriol_hg0601a.h +++ b/applications/plugins/weather_station/protocols/auriol_hg0601a.h @@ -56,9 +56,9 @@ uint8_t ws_protocol_decoder_auriol_th_get_hash_data(void* context); * @param context Pointer to a WSProtocolDecoderAuriol_TH instance * @param flipper_format Pointer to a FlipperFormat instance * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success + * @return status */ -bool ws_protocol_decoder_auriol_th_serialize( +SubGhzProtocolStatus ws_protocol_decoder_auriol_th_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); @@ -67,9 +67,10 @@ bool ws_protocol_decoder_auriol_th_serialize( * Deserialize data WSProtocolDecoderAuriol_TH. * @param context Pointer to a WSProtocolDecoderAuriol_TH instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool ws_protocol_decoder_auriol_th_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + ws_protocol_decoder_auriol_th_deserialize(void* context, FlipperFormat* flipper_format); /** * Getting a textual representation of the received data. diff --git a/applications/plugins/weather_station/protocols/gt_wt_02.c b/applications/plugins/weather_station/protocols/gt_wt_02.c index cbe119192..d333164b4 100644 --- a/applications/plugins/weather_station/protocols/gt_wt_02.c +++ b/applications/plugins/weather_station/protocols/gt_wt_02.c @@ -217,7 +217,7 @@ uint8_t ws_protocol_decoder_gt_wt_02_get_hash_data(void* context) { &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } -bool ws_protocol_decoder_gt_wt_02_serialize( +SubGhzProtocolStatus ws_protocol_decoder_gt_wt_02_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset) { @@ -226,22 +226,12 @@ bool ws_protocol_decoder_gt_wt_02_serialize( return ws_block_generic_serialize(&instance->generic, flipper_format, preset); } -bool ws_protocol_decoder_gt_wt_02_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + ws_protocol_decoder_gt_wt_02_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); WSProtocolDecoderGT_WT02* instance = context; - bool ret = false; - do { - if(!ws_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit != - ws_protocol_gt_wt_02_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); - break; - } - ret = true; - } while(false); - return ret; + return ws_block_generic_deserialize_check_count_bit( + &instance->generic, flipper_format, ws_protocol_gt_wt_02_const.min_count_bit_for_found); } void ws_protocol_decoder_gt_wt_02_get_string(void* context, FuriString* output) { diff --git a/applications/plugins/weather_station/protocols/gt_wt_02.h b/applications/plugins/weather_station/protocols/gt_wt_02.h index f17d5baa0..e13576d21 100644 --- a/applications/plugins/weather_station/protocols/gt_wt_02.h +++ b/applications/plugins/weather_station/protocols/gt_wt_02.h @@ -56,9 +56,9 @@ uint8_t ws_protocol_decoder_gt_wt_02_get_hash_data(void* context); * @param context Pointer to a WSProtocolDecoderGT_WT02 instance * @param flipper_format Pointer to a FlipperFormat instance * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success + * @return status */ -bool ws_protocol_decoder_gt_wt_02_serialize( +SubGhzProtocolStatus ws_protocol_decoder_gt_wt_02_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); @@ -67,9 +67,10 @@ bool ws_protocol_decoder_gt_wt_02_serialize( * Deserialize data WSProtocolDecoderGT_WT02. * @param context Pointer to a WSProtocolDecoderGT_WT02 instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool ws_protocol_decoder_gt_wt_02_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + ws_protocol_decoder_gt_wt_02_deserialize(void* context, FlipperFormat* flipper_format); /** * Getting a textual representation of the received data. diff --git a/applications/plugins/weather_station/protocols/gt_wt_03.c b/applications/plugins/weather_station/protocols/gt_wt_03.c index 7831cf069..30430b839 100644 --- a/applications/plugins/weather_station/protocols/gt_wt_03.c +++ b/applications/plugins/weather_station/protocols/gt_wt_03.c @@ -292,7 +292,7 @@ uint8_t ws_protocol_decoder_gt_wt_03_get_hash_data(void* context) { &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } -bool ws_protocol_decoder_gt_wt_03_serialize( +SubGhzProtocolStatus ws_protocol_decoder_gt_wt_03_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset) { @@ -301,22 +301,12 @@ bool ws_protocol_decoder_gt_wt_03_serialize( return ws_block_generic_serialize(&instance->generic, flipper_format, preset); } -bool ws_protocol_decoder_gt_wt_03_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + ws_protocol_decoder_gt_wt_03_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); WSProtocolDecoderGT_WT03* instance = context; - bool ret = false; - do { - if(!ws_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit != - ws_protocol_gt_wt_03_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); - break; - } - ret = true; - } while(false); - return ret; + return ws_block_generic_deserialize_check_count_bit( + &instance->generic, flipper_format, ws_protocol_gt_wt_03_const.min_count_bit_for_found); } void ws_protocol_decoder_gt_wt_03_get_string(void* context, FuriString* output) { diff --git a/applications/plugins/weather_station/protocols/gt_wt_03.h b/applications/plugins/weather_station/protocols/gt_wt_03.h index fd9536e34..d566bb399 100644 --- a/applications/plugins/weather_station/protocols/gt_wt_03.h +++ b/applications/plugins/weather_station/protocols/gt_wt_03.h @@ -56,9 +56,9 @@ uint8_t ws_protocol_decoder_gt_wt_03_get_hash_data(void* context); * @param context Pointer to a WSProtocolDecoderGT_WT03 instance * @param flipper_format Pointer to a FlipperFormat instance * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success + * @return status */ -bool ws_protocol_decoder_gt_wt_03_serialize( +SubGhzProtocolStatus ws_protocol_decoder_gt_wt_03_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); @@ -67,9 +67,10 @@ bool ws_protocol_decoder_gt_wt_03_serialize( * Deserialize data WSProtocolDecoderGT_WT03. * @param context Pointer to a WSProtocolDecoderGT_WT03 instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool ws_protocol_decoder_gt_wt_03_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + ws_protocol_decoder_gt_wt_03_deserialize(void* context, FlipperFormat* flipper_format); /** * Getting a textual representation of the received data. diff --git a/applications/plugins/weather_station/protocols/infactory.c b/applications/plugins/weather_station/protocols/infactory.c index 53a656d73..4b0e6602a 100644 --- a/applications/plugins/weather_station/protocols/infactory.c +++ b/applications/plugins/weather_station/protocols/infactory.c @@ -248,7 +248,7 @@ uint8_t ws_protocol_decoder_infactory_get_hash_data(void* context) { &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } -bool ws_protocol_decoder_infactory_serialize( +SubGhzProtocolStatus ws_protocol_decoder_infactory_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset) { @@ -257,22 +257,12 @@ bool ws_protocol_decoder_infactory_serialize( return ws_block_generic_serialize(&instance->generic, flipper_format, preset); } -bool ws_protocol_decoder_infactory_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + ws_protocol_decoder_infactory_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); WSProtocolDecoderInfactory* instance = context; - bool ret = false; - do { - if(!ws_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit != - ws_protocol_infactory_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); - break; - } - ret = true; - } while(false); - return ret; + return ws_block_generic_deserialize_check_count_bit( + &instance->generic, flipper_format, ws_protocol_infactory_const.min_count_bit_for_found); } void ws_protocol_decoder_infactory_get_string(void* context, FuriString* output) { diff --git a/applications/plugins/weather_station/protocols/infactory.h b/applications/plugins/weather_station/protocols/infactory.h index 2792ab987..9431a067e 100644 --- a/applications/plugins/weather_station/protocols/infactory.h +++ b/applications/plugins/weather_station/protocols/infactory.h @@ -56,9 +56,9 @@ uint8_t ws_protocol_decoder_infactory_get_hash_data(void* context); * @param context Pointer to a WSProtocolDecoderInfactory instance * @param flipper_format Pointer to a FlipperFormat instance * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success + * @return status */ -bool ws_protocol_decoder_infactory_serialize( +SubGhzProtocolStatus ws_protocol_decoder_infactory_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); @@ -67,9 +67,10 @@ bool ws_protocol_decoder_infactory_serialize( * Deserialize data WSProtocolDecoderInfactory. * @param context Pointer to a WSProtocolDecoderInfactory instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool ws_protocol_decoder_infactory_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + ws_protocol_decoder_infactory_deserialize(void* context, FlipperFormat* flipper_format); /** * Getting a textual representation of the received data. diff --git a/applications/plugins/weather_station/protocols/lacrosse_tx.c b/applications/plugins/weather_station/protocols/lacrosse_tx.c index 8d8a24e24..f4b850d76 100644 --- a/applications/plugins/weather_station/protocols/lacrosse_tx.c +++ b/applications/plugins/weather_station/protocols/lacrosse_tx.c @@ -281,7 +281,7 @@ uint8_t ws_protocol_decoder_lacrosse_tx_get_hash_data(void* context) { &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } -bool ws_protocol_decoder_lacrosse_tx_serialize( +SubGhzProtocolStatus ws_protocol_decoder_lacrosse_tx_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset) { @@ -290,22 +290,12 @@ bool ws_protocol_decoder_lacrosse_tx_serialize( return ws_block_generic_serialize(&instance->generic, flipper_format, preset); } -bool ws_protocol_decoder_lacrosse_tx_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + ws_protocol_decoder_lacrosse_tx_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); WSProtocolDecoderLaCrosse_TX* instance = context; - bool ret = false; - do { - if(!ws_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit != - ws_protocol_lacrosse_tx_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); - break; - } - ret = true; - } while(false); - return ret; + return ws_block_generic_deserialize_check_count_bit( + &instance->generic, flipper_format, ws_protocol_lacrosse_tx_const.min_count_bit_for_found); } void ws_protocol_decoder_lacrosse_tx_get_string(void* context, FuriString* output) { diff --git a/applications/plugins/weather_station/protocols/lacrosse_tx.h b/applications/plugins/weather_station/protocols/lacrosse_tx.h index e88455689..151282b3a 100644 --- a/applications/plugins/weather_station/protocols/lacrosse_tx.h +++ b/applications/plugins/weather_station/protocols/lacrosse_tx.h @@ -56,9 +56,9 @@ uint8_t ws_protocol_decoder_lacrosse_tx_get_hash_data(void* context); * @param context Pointer to a WSProtocolDecoderLaCrosse_TX instance * @param flipper_format Pointer to a FlipperFormat instance * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success + * @return status */ -bool ws_protocol_decoder_lacrosse_tx_serialize( +SubGhzProtocolStatus ws_protocol_decoder_lacrosse_tx_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); @@ -67,9 +67,10 @@ bool ws_protocol_decoder_lacrosse_tx_serialize( * Deserialize data WSProtocolDecoderLaCrosse_TX. * @param context Pointer to a WSProtocolDecoderLaCrosse_TX instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool ws_protocol_decoder_lacrosse_tx_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + ws_protocol_decoder_lacrosse_tx_deserialize(void* context, FlipperFormat* flipper_format); /** * Getting a textual representation of the received data. diff --git a/applications/plugins/weather_station/protocols/lacrosse_tx141thbv2.c b/applications/plugins/weather_station/protocols/lacrosse_tx141thbv2.c index e4b612250..5d007b12f 100644 --- a/applications/plugins/weather_station/protocols/lacrosse_tx141thbv2.c +++ b/applications/plugins/weather_station/protocols/lacrosse_tx141thbv2.c @@ -247,7 +247,7 @@ uint8_t ws_protocol_decoder_lacrosse_tx141thbv2_get_hash_data(void* context) { &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } -bool ws_protocol_decoder_lacrosse_tx141thbv2_serialize( +SubGhzProtocolStatus ws_protocol_decoder_lacrosse_tx141thbv2_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset) { @@ -256,24 +256,15 @@ bool ws_protocol_decoder_lacrosse_tx141thbv2_serialize( return ws_block_generic_serialize(&instance->generic, flipper_format, preset); } -bool ws_protocol_decoder_lacrosse_tx141thbv2_deserialize( +SubGhzProtocolStatus ws_protocol_decoder_lacrosse_tx141thbv2_deserialize( void* context, FlipperFormat* flipper_format) { furi_assert(context); WSProtocolDecoderLaCrosse_TX141THBv2* instance = context; - bool ret = false; - do { - if(!ws_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit != - ws_protocol_lacrosse_tx141thbv2_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); - break; - } - ret = true; - } while(false); - return ret; + return ws_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + ws_protocol_lacrosse_tx141thbv2_const.min_count_bit_for_found); } void ws_protocol_decoder_lacrosse_tx141thbv2_get_string(void* context, FuriString* output) { diff --git a/applications/plugins/weather_station/protocols/lacrosse_tx141thbv2.h b/applications/plugins/weather_station/protocols/lacrosse_tx141thbv2.h index 941b01058..036db0548 100644 --- a/applications/plugins/weather_station/protocols/lacrosse_tx141thbv2.h +++ b/applications/plugins/weather_station/protocols/lacrosse_tx141thbv2.h @@ -56,9 +56,9 @@ uint8_t ws_protocol_decoder_lacrosse_tx141thbv2_get_hash_data(void* context); * @param context Pointer to a WSProtocolDecoderLaCrosse_TX141THBv2 instance * @param flipper_format Pointer to a FlipperFormat instance * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success + * @return status */ -bool ws_protocol_decoder_lacrosse_tx141thbv2_serialize( +SubGhzProtocolStatus ws_protocol_decoder_lacrosse_tx141thbv2_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); @@ -67,9 +67,9 @@ bool ws_protocol_decoder_lacrosse_tx141thbv2_serialize( * Deserialize data WSProtocolDecoderLaCrosse_TX141THBv2. * @param context Pointer to a WSProtocolDecoderLaCrosse_TX141THBv2 instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool ws_protocol_decoder_lacrosse_tx141thbv2_deserialize( +SubGhzProtocolStatus ws_protocol_decoder_lacrosse_tx141thbv2_deserialize( void* context, FlipperFormat* flipper_format); diff --git a/applications/plugins/weather_station/protocols/nexus_th.c b/applications/plugins/weather_station/protocols/nexus_th.c index 38f2fe895..14ba8b273 100644 --- a/applications/plugins/weather_station/protocols/nexus_th.c +++ b/applications/plugins/weather_station/protocols/nexus_th.c @@ -216,7 +216,7 @@ uint8_t ws_protocol_decoder_nexus_th_get_hash_data(void* context) { &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } -bool ws_protocol_decoder_nexus_th_serialize( +SubGhzProtocolStatus ws_protocol_decoder_nexus_th_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset) { @@ -225,22 +225,12 @@ bool ws_protocol_decoder_nexus_th_serialize( return ws_block_generic_serialize(&instance->generic, flipper_format, preset); } -bool ws_protocol_decoder_nexus_th_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + ws_protocol_decoder_nexus_th_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); WSProtocolDecoderNexus_TH* instance = context; - bool ret = false; - do { - if(!ws_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit != - ws_protocol_nexus_th_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); - break; - } - ret = true; - } while(false); - return ret; + return ws_block_generic_deserialize_check_count_bit( + &instance->generic, flipper_format, ws_protocol_nexus_th_const.min_count_bit_for_found); } void ws_protocol_decoder_nexus_th_get_string(void* context, FuriString* output) { diff --git a/applications/plugins/weather_station/protocols/nexus_th.h b/applications/plugins/weather_station/protocols/nexus_th.h index 5450f0040..6c2715b85 100644 --- a/applications/plugins/weather_station/protocols/nexus_th.h +++ b/applications/plugins/weather_station/protocols/nexus_th.h @@ -56,9 +56,9 @@ uint8_t ws_protocol_decoder_nexus_th_get_hash_data(void* context); * @param context Pointer to a WSProtocolDecoderNexus_TH instance * @param flipper_format Pointer to a FlipperFormat instance * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success + * @return status */ -bool ws_protocol_decoder_nexus_th_serialize( +SubGhzProtocolStatus ws_protocol_decoder_nexus_th_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); @@ -67,9 +67,10 @@ bool ws_protocol_decoder_nexus_th_serialize( * Deserialize data WSProtocolDecoderNexus_TH. * @param context Pointer to a WSProtocolDecoderNexus_TH instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool ws_protocol_decoder_nexus_th_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + ws_protocol_decoder_nexus_th_deserialize(void* context, FlipperFormat* flipper_format); /** * Getting a textual representation of the received data. diff --git a/applications/plugins/weather_station/protocols/oregon2.c b/applications/plugins/weather_station/protocols/oregon2.c index 8ca80bbe2..313748ccf 100644 --- a/applications/plugins/weather_station/protocols/oregon2.c +++ b/applications/plugins/weather_station/protocols/oregon2.c @@ -302,17 +302,19 @@ uint8_t ws_protocol_decoder_oregon2_get_hash_data(void* context) { &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } -bool ws_protocol_decoder_oregon2_serialize( +SubGhzProtocolStatus ws_protocol_decoder_oregon2_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset) { furi_assert(context); WSProtocolDecoderOregon2* instance = context; - if(!ws_block_generic_serialize(&instance->generic, flipper_format, preset)) return false; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; + ret = ws_block_generic_serialize(&instance->generic, flipper_format, preset); + if(ret != SubGhzProtocolStatusOk) return ret; uint32_t temp = instance->var_bits; if(!flipper_format_write_uint32(flipper_format, "VarBits", &temp, 1)) { FURI_LOG_E(TAG, "Error adding VarBits"); - return false; + return SubGhzProtocolStatusErrorParserOthers; } if(!flipper_format_write_hex( flipper_format, @@ -320,22 +322,25 @@ bool ws_protocol_decoder_oregon2_serialize( (const uint8_t*)&instance->var_data, sizeof(instance->var_data))) { FURI_LOG_E(TAG, "Error adding VarData"); - return false; + return SubGhzProtocolStatusErrorParserOthers; } - return true; + return ret; } -bool ws_protocol_decoder_oregon2_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + ws_protocol_decoder_oregon2_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); WSProtocolDecoderOregon2* instance = context; - bool ret = false; uint32_t temp_data; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; do { - if(!ws_block_generic_deserialize(&instance->generic, flipper_format)) { + ret = ws_block_generic_deserialize(&instance->generic, flipper_format); + if(ret != SubGhzProtocolStatusOk) { break; } if(!flipper_format_read_uint32(flipper_format, "VarBits", &temp_data, 1)) { FURI_LOG_E(TAG, "Missing VarLen"); + ret = SubGhzProtocolStatusErrorParserOthers; break; } instance->var_bits = (uint8_t)temp_data; @@ -345,13 +350,14 @@ bool ws_protocol_decoder_oregon2_deserialize(void* context, FlipperFormat* flipp (uint8_t*)&instance->var_data, sizeof(instance->var_data))) { //-V1051 FURI_LOG_E(TAG, "Missing VarData"); + ret = SubGhzProtocolStatusErrorParserOthers; break; } if(instance->generic.data_count_bit != ws_oregon2_const.min_count_bit_for_found) { FURI_LOG_E(TAG, "Wrong number of bits in key: %d", instance->generic.data_count_bit); + ret = SubGhzProtocolStatusErrorValueBitCount; break; } - ret = true; } while(false); return ret; } diff --git a/applications/plugins/weather_station/protocols/oregon_v1.c b/applications/plugins/weather_station/protocols/oregon_v1.c index 1ed9da205..03215bbf5 100644 --- a/applications/plugins/weather_station/protocols/oregon_v1.c +++ b/applications/plugins/weather_station/protocols/oregon_v1.c @@ -283,7 +283,7 @@ uint8_t ws_protocol_decoder_oregon_v1_get_hash_data(void* context) { &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } -bool ws_protocol_decoder_oregon_v1_serialize( +SubGhzProtocolStatus ws_protocol_decoder_oregon_v1_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset) { @@ -292,22 +292,12 @@ bool ws_protocol_decoder_oregon_v1_serialize( return ws_block_generic_serialize(&instance->generic, flipper_format, preset); } -bool ws_protocol_decoder_oregon_v1_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + ws_protocol_decoder_oregon_v1_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); WSProtocolDecoderOregon_V1* instance = context; - bool ret = false; - do { - if(!ws_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit != - ws_protocol_oregon_v1_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); - break; - } - ret = true; - } while(false); - return ret; + return ws_block_generic_deserialize_check_count_bit( + &instance->generic, flipper_format, ws_protocol_oregon_v1_const.min_count_bit_for_found); } void ws_protocol_decoder_oregon_v1_get_string(void* context, FuriString* output) { diff --git a/applications/plugins/weather_station/protocols/oregon_v1.h b/applications/plugins/weather_station/protocols/oregon_v1.h index c9aa5af48..48937601d 100644 --- a/applications/plugins/weather_station/protocols/oregon_v1.h +++ b/applications/plugins/weather_station/protocols/oregon_v1.h @@ -56,9 +56,9 @@ uint8_t ws_protocol_decoder_oregon_v1_get_hash_data(void* context); * @param context Pointer to a WSProtocolDecoderOregon_V1 instance * @param flipper_format Pointer to a FlipperFormat instance * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success + * @return status */ -bool ws_protocol_decoder_oregon_v1_serialize( +SubGhzProtocolStatus ws_protocol_decoder_oregon_v1_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); @@ -67,9 +67,10 @@ bool ws_protocol_decoder_oregon_v1_serialize( * Deserialize data WSProtocolDecoderOregon_V1. * @param context Pointer to a WSProtocolDecoderOregon_V1 instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool ws_protocol_decoder_oregon_v1_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + ws_protocol_decoder_oregon_v1_deserialize(void* context, FlipperFormat* flipper_format); /** * Getting a textual representation of the received data. diff --git a/applications/plugins/weather_station/protocols/thermopro_tx4.c b/applications/plugins/weather_station/protocols/thermopro_tx4.c index 0882bc33d..24e883e60 100644 --- a/applications/plugins/weather_station/protocols/thermopro_tx4.c +++ b/applications/plugins/weather_station/protocols/thermopro_tx4.c @@ -211,7 +211,7 @@ uint8_t ws_protocol_decoder_thermopro_tx4_get_hash_data(void* context) { &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } -bool ws_protocol_decoder_thermopro_tx4_serialize( +SubGhzProtocolStatus ws_protocol_decoder_thermopro_tx4_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset) { @@ -220,22 +220,14 @@ bool ws_protocol_decoder_thermopro_tx4_serialize( return ws_block_generic_serialize(&instance->generic, flipper_format, preset); } -bool ws_protocol_decoder_thermopro_tx4_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + ws_protocol_decoder_thermopro_tx4_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); WSProtocolDecoderThermoPRO_TX4* instance = context; - bool ret = false; - do { - if(!ws_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit != - ws_protocol_thermopro_tx4_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); - break; - } - ret = true; - } while(false); - return ret; + return ws_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + ws_protocol_thermopro_tx4_const.min_count_bit_for_found); } void ws_protocol_decoder_thermopro_tx4_get_string(void* context, FuriString* output) { diff --git a/applications/plugins/weather_station/protocols/thermopro_tx4.h b/applications/plugins/weather_station/protocols/thermopro_tx4.h index 1feae58d3..526648d1e 100644 --- a/applications/plugins/weather_station/protocols/thermopro_tx4.h +++ b/applications/plugins/weather_station/protocols/thermopro_tx4.h @@ -56,9 +56,9 @@ uint8_t ws_protocol_decoder_thermopro_tx4_get_hash_data(void* context); * @param context Pointer to a WSProtocolDecoderThermoPRO_TX4 instance * @param flipper_format Pointer to a FlipperFormat instance * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success + * @return status */ -bool ws_protocol_decoder_thermopro_tx4_serialize( +SubGhzProtocolStatus ws_protocol_decoder_thermopro_tx4_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); @@ -67,9 +67,10 @@ bool ws_protocol_decoder_thermopro_tx4_serialize( * Deserialize data WSProtocolDecoderThermoPRO_TX4. * @param context Pointer to a WSProtocolDecoderThermoPRO_TX4 instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool ws_protocol_decoder_thermopro_tx4_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + ws_protocol_decoder_thermopro_tx4_deserialize(void* context, FlipperFormat* flipper_format); /** * Getting a textual representation of the received data. diff --git a/applications/plugins/weather_station/protocols/tx_8300.c b/applications/plugins/weather_station/protocols/tx_8300.c index ee0412ba9..3a06ce49d 100644 --- a/applications/plugins/weather_station/protocols/tx_8300.c +++ b/applications/plugins/weather_station/protocols/tx_8300.c @@ -246,7 +246,7 @@ uint8_t ws_protocol_decoder_tx_8300_get_hash_data(void* context) { &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } -bool ws_protocol_decoder_tx_8300_serialize( +SubGhzProtocolStatus ws_protocol_decoder_tx_8300_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset) { @@ -255,21 +255,12 @@ bool ws_protocol_decoder_tx_8300_serialize( return ws_block_generic_serialize(&instance->generic, flipper_format, preset); } -bool ws_protocol_decoder_tx_8300_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + ws_protocol_decoder_tx_8300_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); WSProtocolDecoderTX_8300* instance = context; - bool ret = false; - do { - if(!ws_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit != ws_protocol_tx_8300_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); - break; - } - ret = true; - } while(false); - return ret; + return ws_block_generic_deserialize_check_count_bit( + &instance->generic, flipper_format, ws_protocol_tx_8300_const.min_count_bit_for_found); } void ws_protocol_decoder_tx_8300_get_string(void* context, FuriString* output) { diff --git a/applications/plugins/weather_station/protocols/tx_8300.h b/applications/plugins/weather_station/protocols/tx_8300.h index ec198e80f..088ccd7c0 100644 --- a/applications/plugins/weather_station/protocols/tx_8300.h +++ b/applications/plugins/weather_station/protocols/tx_8300.h @@ -56,9 +56,9 @@ uint8_t ws_protocol_decoder_tx_8300_get_hash_data(void* context); * @param context Pointer to a WSProtocolDecoderTX_8300 instance * @param flipper_format Pointer to a FlipperFormat instance * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success + * @return status */ -bool ws_protocol_decoder_tx_8300_serialize( +SubGhzProtocolStatus ws_protocol_decoder_tx_8300_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); @@ -67,9 +67,10 @@ bool ws_protocol_decoder_tx_8300_serialize( * Deserialize data WSProtocolDecoderTX_8300. * @param context Pointer to a WSProtocolDecoderTX_8300 instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool ws_protocol_decoder_tx_8300_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + ws_protocol_decoder_tx_8300_deserialize(void* context, FlipperFormat* flipper_format); /** * Getting a textual representation of the received data. diff --git a/applications/plugins/weather_station/protocols/ws_generic.c b/applications/plugins/weather_station/protocols/ws_generic.c index 8a88ed52f..026856a9e 100644 --- a/applications/plugins/weather_station/protocols/ws_generic.c +++ b/applications/plugins/weather_station/protocols/ws_generic.c @@ -21,12 +21,12 @@ void ws_block_generic_get_preset_name(const char* preset_name, FuriString* prese furi_string_set(preset_str, preset_name_temp); } -bool ws_block_generic_serialize( +SubGhzProtocolStatus ws_block_generic_serialize( WSBlockGeneric* instance, FlipperFormat* flipper_format, SubGhzRadioPreset* preset) { furi_assert(instance); - bool res = false; + SubGhzProtocolStatus res = SubGhzProtocolStatusError; FuriString* temp_str; temp_str = furi_string_alloc(); do { @@ -34,11 +34,13 @@ bool ws_block_generic_serialize( if(!flipper_format_write_header_cstr( flipper_format, WS_KEY_FILE_TYPE, WS_KEY_FILE_VERSION)) { FURI_LOG_E(TAG, "Unable to add header"); + res = SubGhzProtocolStatusErrorParserHeader; break; } if(!flipper_format_write_uint32(flipper_format, "Frequency", &preset->frequency, 1)) { FURI_LOG_E(TAG, "Unable to add Frequency"); + res = SubGhzProtocolStatusErrorParserFrequency; break; } @@ -46,34 +48,40 @@ bool ws_block_generic_serialize( if(!flipper_format_write_string_cstr( flipper_format, "Preset", furi_string_get_cstr(temp_str))) { FURI_LOG_E(TAG, "Unable to add Preset"); + res = SubGhzProtocolStatusErrorParserPreset; break; } if(!strcmp(furi_string_get_cstr(temp_str), "FuriHalSubGhzPresetCustom")) { if(!flipper_format_write_string_cstr( flipper_format, "Custom_preset_module", "CC1101")) { FURI_LOG_E(TAG, "Unable to add Custom_preset_module"); + res = SubGhzProtocolStatusErrorParserCustomPreset; break; } if(!flipper_format_write_hex( flipper_format, "Custom_preset_data", preset->data, preset->data_size)) { FURI_LOG_E(TAG, "Unable to add Custom_preset_data"); + res = SubGhzProtocolStatusErrorParserCustomPreset; break; } } if(!flipper_format_write_string_cstr(flipper_format, "Protocol", instance->protocol_name)) { FURI_LOG_E(TAG, "Unable to add Protocol"); + res = SubGhzProtocolStatusErrorParserProtocolName; break; } uint32_t temp_data = instance->id; if(!flipper_format_write_uint32(flipper_format, "Id", &temp_data, 1)) { FURI_LOG_E(TAG, "Unable to add Id"); + res = SubGhzProtocolStatusErrorParserOthers; break; } temp_data = instance->data_count_bit; if(!flipper_format_write_uint32(flipper_format, "Bit", &temp_data, 1)) { FURI_LOG_E(TAG, "Unable to add Bit"); + res = SubGhzProtocolStatusErrorParserBitCount; break; } @@ -84,18 +92,21 @@ bool ws_block_generic_serialize( if(!flipper_format_write_hex(flipper_format, "Data", key_data, sizeof(uint64_t))) { FURI_LOG_E(TAG, "Unable to add Data"); + res = SubGhzProtocolStatusErrorParserOthers; break; } temp_data = instance->battery_low; if(!flipper_format_write_uint32(flipper_format, "Batt", &temp_data, 1)) { FURI_LOG_E(TAG, "Unable to add Battery_low"); + res = SubGhzProtocolStatusErrorParserOthers; break; } temp_data = instance->humidity; if(!flipper_format_write_uint32(flipper_format, "Hum", &temp_data, 1)) { FURI_LOG_E(TAG, "Unable to add Humidity"); + res = SubGhzProtocolStatusErrorParserOthers; break; } @@ -107,52 +118,60 @@ bool ws_block_generic_serialize( temp_data = curr_ts; if(!flipper_format_write_uint32(flipper_format, "Ts", &temp_data, 1)) { FURI_LOG_E(TAG, "Unable to add timestamp"); + res = SubGhzProtocolStatusErrorParserOthers; break; } temp_data = instance->channel; if(!flipper_format_write_uint32(flipper_format, "Ch", &temp_data, 1)) { FURI_LOG_E(TAG, "Unable to add Channel"); + res = SubGhzProtocolStatusErrorParserOthers; break; } temp_data = instance->btn; if(!flipper_format_write_uint32(flipper_format, "Btn", &temp_data, 1)) { FURI_LOG_E(TAG, "Unable to add Btn"); + res = SubGhzProtocolStatusErrorParserOthers; break; } float temp = instance->temp; if(!flipper_format_write_float(flipper_format, "Temp", &temp, 1)) { FURI_LOG_E(TAG, "Unable to add Temperature"); + res = SubGhzProtocolStatusErrorParserOthers; break; } - res = true; + res = SubGhzProtocolStatusOk; } while(false); furi_string_free(temp_str); return res; } -bool ws_block_generic_deserialize(WSBlockGeneric* instance, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + ws_block_generic_deserialize(WSBlockGeneric* instance, FlipperFormat* flipper_format) { furi_assert(instance); - bool res = false; + SubGhzProtocolStatus res = SubGhzProtocolStatusError; uint32_t temp_data = 0; do { if(!flipper_format_rewind(flipper_format)) { FURI_LOG_E(TAG, "Rewind error"); + res = SubGhzProtocolStatusErrorParserOthers; break; } if(!flipper_format_read_uint32(flipper_format, "Id", (uint32_t*)&temp_data, 1)) { FURI_LOG_E(TAG, "Missing Id"); + res = SubGhzProtocolStatusErrorParserOthers; break; } instance->id = (uint32_t)temp_data; if(!flipper_format_read_uint32(flipper_format, "Bit", (uint32_t*)&temp_data, 1)) { FURI_LOG_E(TAG, "Missing Bit"); + res = SubGhzProtocolStatusErrorParserBitCount; break; } instance->data_count_bit = (uint8_t)temp_data; @@ -160,6 +179,7 @@ bool ws_block_generic_deserialize(WSBlockGeneric* instance, FlipperFormat* flipp uint8_t key_data[sizeof(uint64_t)] = {0}; if(!flipper_format_read_hex(flipper_format, "Data", key_data, sizeof(uint64_t))) { FURI_LOG_E(TAG, "Missing Data"); + res = SubGhzProtocolStatusErrorParserOthers; break; } @@ -169,30 +189,35 @@ bool ws_block_generic_deserialize(WSBlockGeneric* instance, FlipperFormat* flipp if(!flipper_format_read_uint32(flipper_format, "Batt", (uint32_t*)&temp_data, 1)) { FURI_LOG_E(TAG, "Missing Battery_low"); + res = SubGhzProtocolStatusErrorParserOthers; break; } instance->battery_low = (uint8_t)temp_data; if(!flipper_format_read_uint32(flipper_format, "Hum", (uint32_t*)&temp_data, 1)) { FURI_LOG_E(TAG, "Missing Humidity"); + res = SubGhzProtocolStatusErrorParserOthers; break; } instance->humidity = (uint8_t)temp_data; if(!flipper_format_read_uint32(flipper_format, "Ts", (uint32_t*)&temp_data, 1)) { FURI_LOG_E(TAG, "Missing timestamp"); + res = SubGhzProtocolStatusErrorParserOthers; break; } instance->timestamp = (uint32_t)temp_data; if(!flipper_format_read_uint32(flipper_format, "Ch", (uint32_t*)&temp_data, 1)) { FURI_LOG_E(TAG, "Missing Channel"); + res = SubGhzProtocolStatusErrorParserOthers; break; } instance->channel = (uint8_t)temp_data; if(!flipper_format_read_uint32(flipper_format, "Btn", (uint32_t*)&temp_data, 1)) { FURI_LOG_E(TAG, "Missing Btn"); + res = SubGhzProtocolStatusErrorParserOthers; break; } instance->btn = (uint8_t)temp_data; @@ -200,12 +225,32 @@ bool ws_block_generic_deserialize(WSBlockGeneric* instance, FlipperFormat* flipp float temp; if(!flipper_format_read_float(flipper_format, "Temp", (float*)&temp, 1)) { FURI_LOG_E(TAG, "Missing Temperature"); + res = SubGhzProtocolStatusErrorParserOthers; break; } instance->temp = temp; - res = true; + res = SubGhzProtocolStatusOk; } while(0); return res; +} + +SubGhzProtocolStatus ws_block_generic_deserialize_check_count_bit( + WSBlockGeneric* instance, + FlipperFormat* flipper_format, + uint16_t count_bit) { + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; + do { + ret = ws_block_generic_deserialize(instance, flipper_format); + if(ret != SubGhzProtocolStatusOk) { + break; + } + if(instance->data_count_bit != count_bit) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + ret = SubGhzProtocolStatusErrorValueBitCount; + break; + } + } while(false); + return ret; } \ No newline at end of file diff --git a/applications/plugins/weather_station/protocols/ws_generic.h b/applications/plugins/weather_station/protocols/ws_generic.h index 47cfa74b3..ff047fae6 100644 --- a/applications/plugins/weather_station/protocols/ws_generic.h +++ b/applications/plugins/weather_station/protocols/ws_generic.h @@ -48,9 +48,9 @@ void ws_block_generic_get_preset_name(const char* preset_name, FuriString* prese * @param instance Pointer to a WSBlockGeneric instance * @param flipper_format Pointer to a FlipperFormat instance * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success + * @return status */ -bool ws_block_generic_serialize( +SubGhzProtocolStatus ws_block_generic_serialize( WSBlockGeneric* instance, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); @@ -59,9 +59,22 @@ bool ws_block_generic_serialize( * Deserialize data WSBlockGeneric. * @param instance Pointer to a WSBlockGeneric instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool ws_block_generic_deserialize(WSBlockGeneric* instance, FlipperFormat* flipper_format); +SubGhzProtocolStatus + ws_block_generic_deserialize(WSBlockGeneric* instance, FlipperFormat* flipper_format); + +/** + * Deserialize data WSBlockGeneric. + * @param instance Pointer to a WSBlockGeneric instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param count_bit Count bit protocol + * @return status + */ +SubGhzProtocolStatus ws_block_generic_deserialize_check_count_bit( + WSBlockGeneric* instance, + FlipperFormat* flipper_format, + uint16_t count_bit); #ifdef __cplusplus } diff --git a/applications/plugins/weather_station/scenes/weather_station_receiver.c b/applications/plugins/weather_station/scenes/weather_station_receiver.c index 670c8c386..e76810430 100644 --- a/applications/plugins/weather_station/scenes/weather_station_receiver.c +++ b/applications/plugins/weather_station/scenes/weather_station_receiver.c @@ -195,6 +195,10 @@ bool weather_station_scene_receiver_on_event(void* context, SceneManagerEvent ev ws_hopper_update(app); weather_station_scene_receiver_update_statusbar(app); } + // Get current RSSI + float rssi = furi_hal_subghz_get_rssi(); + ws_view_receiver_set_rssi(app->ws_receiver, rssi); + if(app->txrx->txrx_state == WSTxRxStateRx) { notification_message(app->notifications, &sequence_blink_cyan_10); } diff --git a/applications/plugins/weather_station/views/weather_station_receiver.c b/applications/plugins/weather_station/views/weather_station_receiver.c index 42a90b22d..f8e2e3288 100644 --- a/applications/plugins/weather_station/views/weather_station_receiver.c +++ b/applications/plugins/weather_station/views/weather_station_receiver.c @@ -1,6 +1,6 @@ #include "weather_station_receiver.h" #include "../weather_station_app_i.h" -#include +#include #include #include @@ -12,6 +12,7 @@ #define MENU_ITEMS 4u #define UNLOCK_CNT 3 +#define SUBGHZ_RAW_TRESHOLD_MIN -90.0f typedef struct { FuriString* item_str; uint8_t type; @@ -59,8 +60,24 @@ typedef struct { uint16_t list_offset; uint16_t history_item; WSReceiverBarShow bar_show; + uint8_t u_rssi; } WSReceiverModel; +void ws_view_receiver_set_rssi(WSReceiver* instance, float rssi) { + furi_assert(instance); + with_view_model( + instance->view, + WSReceiverModel * model, + { + if(rssi < SUBGHZ_RAW_TRESHOLD_MIN) { + model->u_rssi = 0; + } else { + model->u_rssi = (uint8_t)(rssi - SUBGHZ_RAW_TRESHOLD_MIN); + } + }, + true); +} + void ws_view_receiver_set_lock(WSReceiver* ws_receiver, WSLock lock) { furi_assert(ws_receiver); ws_receiver->lock_count = 0; @@ -164,13 +181,22 @@ static void ws_view_receiver_draw_frame(Canvas* canvas, uint16_t idx, bool scrol canvas_draw_dot(canvas, scrollbar ? 121 : 126, (0 + idx * FRAME_HEIGHT) + 11); } +static void ws_view_rssi_draw(Canvas* canvas, WSReceiverModel* model) { + for(uint8_t i = 1; i < model->u_rssi; i++) { + if(i % 5) { + canvas_draw_dot(canvas, 46 + i, 50); + canvas_draw_dot(canvas, 47 + i, 51); + canvas_draw_dot(canvas, 46 + i, 52); + } + } +} + void ws_view_receiver_draw(Canvas* canvas, WSReceiverModel* model) { canvas_clear(canvas); canvas_set_color(canvas, ColorBlack); canvas_set_font(canvas, FontSecondary); elements_button_left(canvas, "Config"); - canvas_draw_line(canvas, 46, 51, 125, 51); bool scrollbar = model->history_item > 4; FuriString* str_buff; @@ -203,10 +229,12 @@ void ws_view_receiver_draw(Canvas* canvas, WSReceiverModel* model) { canvas_draw_icon(canvas, 0, 0, &I_Scanning_123x52); canvas_set_font(canvas, FontPrimary); canvas_draw_str(canvas, 63, 46, "Scanning..."); - canvas_draw_line(canvas, 46, 51, 125, 51); canvas_set_font(canvas, FontSecondary); } + // Draw RSSI + ws_view_rssi_draw(canvas, model); + switch(model->bar_show) { case WSReceiverBarShowLock: canvas_draw_icon(canvas, 64, 55, &I_Lock_7x8); diff --git a/applications/plugins/weather_station/views/weather_station_receiver.h b/applications/plugins/weather_station/views/weather_station_receiver.h index 30c6516d5..f81aa1f5e 100644 --- a/applications/plugins/weather_station/views/weather_station_receiver.h +++ b/applications/plugins/weather_station/views/weather_station_receiver.h @@ -8,6 +8,8 @@ typedef struct WSReceiver WSReceiver; typedef void (*WSReceiverCallback)(WSCustomEvent event, void* context); +void ws_view_receiver_set_rssi(WSReceiver* instance, float rssi); + void ws_view_receiver_set_lock(WSReceiver* ws_receiver, WSLock keyboard); void ws_view_receiver_set_callback( 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 d8df737e5..b3b3f2193 100644 --- a/applications/plugins/weather_station/views/weather_station_receiver_info.c +++ b/applications/plugins/weather_station/views/weather_station_receiver_info.c @@ -1,6 +1,6 @@ #include "weather_station_receiver.h" #include "../weather_station_app_i.h" -#include "Weather_Station_icons.h" +#include "weather_station_icons.h" #include "../protocols/ws_generic.h" #include #include diff --git a/applications/plugins/weather_station/weather_station_app.c b/applications/plugins/weather_station/weather_station_app.c index b17f2acfc..ffa569f20 100644 --- a/applications/plugins/weather_station/weather_station_app.c +++ b/applications/plugins/weather_station/weather_station_app.c @@ -105,6 +105,9 @@ WeatherStationApp* weather_station_app_alloc() { app->txrx->worker, (SubGhzWorkerPairCallback)subghz_receiver_decode); subghz_worker_set_context(app->txrx->worker, app->txrx->receiver); + // Enable power for External CC1101 if it is connected + furi_hal_subghz_enable_ext_power(); + furi_hal_power_suppress_charge_enter(); scene_manager_next_scene(app->scene_manager, WeatherStationSceneStart); @@ -118,6 +121,9 @@ void weather_station_app_free(WeatherStationApp* app) { //CC1101 off ws_sleep(app); + // Disable power for External CC1101 if it was enabled and module is connected + furi_hal_subghz_disable_ext_power(); + // Submenu view_dispatcher_remove_view(app->view_dispatcher, WeatherStationViewSubmenu); submenu_free(app->submenu); diff --git a/applications/plugins/weather_station/weather_station_app_i.c b/applications/plugins/weather_station/weather_station_app_i.c index 712634a2c..7236b6625 100644 --- a/applications/plugins/weather_station/weather_station_app_i.c +++ b/applications/plugins/weather_station/weather_station_app_i.c @@ -58,7 +58,7 @@ void ws_begin(WeatherStationApp* app, uint8_t* preset_data) { furi_hal_subghz_reset(); furi_hal_subghz_idle(); furi_hal_subghz_load_custom_preset(preset_data); - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(furi_hal_subghz.cc1101_g0_pin, GpioModeInput, GpioPullNo, GpioSpeedLow); app->txrx->txrx_state = WSTxRxStateIDLE; } @@ -72,7 +72,7 @@ uint32_t ws_rx(WeatherStationApp* app, uint32_t frequency) { furi_hal_subghz_idle(); uint32_t value = furi_hal_subghz_set_frequency_and_path(frequency); - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(furi_hal_subghz.cc1101_g0_pin, GpioModeInput, GpioPullNo, GpioSpeedLow); furi_hal_subghz_flush_rx(); furi_hal_subghz_rx(); diff --git a/applications/plugins/wifi_deauther/LICENSE b/applications/plugins/wifi_deauther/LICENSE new file mode 100644 index 000000000..f288702d2 --- /dev/null +++ b/applications/plugins/wifi_deauther/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/applications/plugins/wifi_deauther_v2/README.md b/applications/plugins/wifi_deauther/README.md similarity index 79% rename from applications/plugins/wifi_deauther_v2/README.md rename to applications/plugins/wifi_deauther/README.md index 40fff1324..87a419f90 100644 --- a/applications/plugins/wifi_deauther_v2/README.md +++ b/applications/plugins/wifi_deauther/README.md @@ -4,10 +4,6 @@ Flipper Zero esp8266 deauther app. Based off the WiFi Marauder App from 0xchocolate. -Thanks to Roguemaster for fixing some issues I had with the code and didnt get a chance to get to. I have now uploaded these changes into the source. - -I have also successfully built this with unleashed souce as well and included the FAP file here. unleashed version unlshd-020. - https://github.com/0xchocolate/flipperzero-firmware-with-wifi-marauder-companion https://github.com/RogueMaster/flipperzero-firmware-wPlugins/tree/unleashed/applications/wifi_marauder_companion @@ -36,13 +32,10 @@ TX--------U_RX -Video in action (old version). +Video in action. https://youtu.be/_RFzZyPkeR0 -New video and install instructions. -https://youtu.be/CKK7t0TaRVQ - -If you want to disable the built in WiFi access and web interface (only use flipper to send serial commands) then select "set webinterface false", "save settings" and "reboot". When it starts back up you wont see the pwned AP any more. +If you want to disable the built in WiFi access and web interface (only use flipper to serial send commands) then select "set webinterface false", "save settings" and "reboot". When it starts back up you wont see the pwned AP any more. I installed this into Roguemaster to test. diff --git a/applications/plugins/wifi_deauther_v2/application.fam b/applications/plugins/wifi_deauther/application.fam similarity index 67% rename from applications/plugins/wifi_deauther_v2/application.fam rename to applications/plugins/wifi_deauther/application.fam index 8fd4602c4..07ceb543b 100644 --- a/applications/plugins/wifi_deauther_v2/application.fam +++ b/applications/plugins/wifi_deauther/application.fam @@ -1,6 +1,6 @@ App( - appid="ESP8266_Wifi_Deauther", - name="[ESP8266] WiFi (Deauther) v2", + appid="ESP8266_Wifi_Deauther_v2", + name="[ESP8266] Deauther v2", apptype=FlipperAppType.EXTERNAL, entry_point="wifi_deauther_app", cdefines=["APP_WIFI_deauther"], @@ -8,5 +8,5 @@ App( stack_size=1 * 1024, order=30, fap_icon="wifi_10px.png", - fap_category="GPIO", + fap_category="WiFi", ) diff --git a/applications/plugins/wifi_deauther_v2/scenes/wifi_deauther_scene.c b/applications/plugins/wifi_deauther/scenes/wifi_deauther_scene.c similarity index 100% rename from applications/plugins/wifi_deauther_v2/scenes/wifi_deauther_scene.c rename to applications/plugins/wifi_deauther/scenes/wifi_deauther_scene.c diff --git a/applications/plugins/wifi_deauther_v2/scenes/wifi_deauther_scene.h b/applications/plugins/wifi_deauther/scenes/wifi_deauther_scene.h similarity index 100% rename from applications/plugins/wifi_deauther_v2/scenes/wifi_deauther_scene.h rename to applications/plugins/wifi_deauther/scenes/wifi_deauther_scene.h diff --git a/applications/plugins/wifi_deauther_v2/scenes/wifi_deauther_scene_config.h b/applications/plugins/wifi_deauther/scenes/wifi_deauther_scene_config.h similarity index 100% rename from applications/plugins/wifi_deauther_v2/scenes/wifi_deauther_scene_config.h rename to applications/plugins/wifi_deauther/scenes/wifi_deauther_scene_config.h diff --git a/applications/plugins/wifi_deauther_v2/scenes/wifi_deauther_scene_console_output.c b/applications/plugins/wifi_deauther/scenes/wifi_deauther_scene_console_output.c similarity index 84% rename from applications/plugins/wifi_deauther_v2/scenes/wifi_deauther_scene_console_output.c rename to applications/plugins/wifi_deauther/scenes/wifi_deauther_scene_console_output.c index a9b738b06..a3d03a0d1 100644 --- a/applications/plugins/wifi_deauther_v2/scenes/wifi_deauther_scene_console_output.c +++ b/applications/plugins/wifi_deauther/scenes/wifi_deauther_scene_console_output.c @@ -21,13 +21,6 @@ void wifi_deauther_console_output_handle_rx_data_cb(uint8_t* buf, size_t len, vo void wifi_deauther_scene_console_output_on_enter(void* context) { WifideautherApp* app = context; - // Register callback to receive data - wifi_deauther_uart_set_handle_rx_data_cb( - app->uart, wifi_deauther_console_output_handle_rx_data_cb); // setup callback for rx thread - - // Give a small delay to allow UART to settle. - furi_delay_ms(600); - TextBox* text_box = app->text_box; text_box_reset(app->text_box); text_box_set_font(text_box, TextBoxFontText); @@ -39,7 +32,17 @@ void wifi_deauther_scene_console_output_on_enter(void* context) { if(app->is_command) { furi_string_reset(app->text_box_store); app->text_box_store_strlen = 0; + if(0 == strncmp("help", app->selected_tx_string, strlen("help"))) { + const char* help_msg = "For app support/feedback,\nreach out to\n"; + furi_string_cat_str(app->text_box_store, help_msg); + app->text_box_store_strlen += strlen(help_msg); + } + if(app->show_stopscan_tip) { + const char* help_msg = "Press BACK to send stopscan\n"; + furi_string_cat_str(app->text_box_store, help_msg); + app->text_box_store_strlen += strlen(help_msg); + } } else { // "View Log" menu action text_box_set_text(app->text_box, furi_string_get_cstr(app->text_box_store)); } @@ -47,6 +50,10 @@ void wifi_deauther_scene_console_output_on_enter(void* context) { scene_manager_set_scene_state(app->scene_manager, WifideautherSceneConsoleOutput, 0); view_dispatcher_switch_to_view(app->view_dispatcher, WifideautherAppViewConsoleOutput); + // Register callback to receive data + wifi_deauther_uart_set_handle_rx_data_cb( + app->uart, wifi_deauther_console_output_handle_rx_data_cb); // setup callback for rx thread + // Send command with newline '\n' if(app->is_command && app->selected_tx_string) { wifi_deauther_uart_tx( diff --git a/applications/plugins/wifi_deauther_v2/scenes/wifi_deauther_scene_start.c b/applications/plugins/wifi_deauther/scenes/wifi_deauther_scene_start.c similarity index 100% rename from applications/plugins/wifi_deauther_v2/scenes/wifi_deauther_scene_start.c rename to applications/plugins/wifi_deauther/scenes/wifi_deauther_scene_start.c index 2519c9a07..92b80b2cf 100644 --- a/applications/plugins/wifi_deauther_v2/scenes/wifi_deauther_scene_start.c +++ b/applications/plugins/wifi_deauther/scenes/wifi_deauther_scene_start.c @@ -33,13 +33,6 @@ const WifideautherItem MenuItems[NUM_MENU_ITEMS] = { NO_ARGS, FOCUS_CONSOLE_END, NO_TIP}, - {"Show", - {"SSIDs", "Stations", "All", "Selected"}, - 4, - {"show ap", "show station", "show all", "show selected"}, - NO_ARGS, - FOCUS_CONSOLE_END, - NO_TIP}, {"Select", {"All", "SSIDs", "Stations"}, 3, @@ -54,6 +47,13 @@ const WifideautherItem MenuItems[NUM_MENU_ITEMS] = { INPUT_ARGS, FOCUS_CONSOLE_END, NO_TIP}, + {"Show", + {"SSIDs", "Stations", "All", "Selected"}, + 4, + {"show ap", "show station", "show all", "show selected"}, + NO_ARGS, + FOCUS_CONSOLE_END, + NO_TIP}, {"Attack", {"deauth", "deauthall", "beacon", "probe"}, 4, diff --git a/applications/plugins/wifi_deauther_v2/scenes/wifi_deauther_scene_text_input.c b/applications/plugins/wifi_deauther/scenes/wifi_deauther_scene_text_input.c similarity index 55% rename from applications/plugins/wifi_deauther_v2/scenes/wifi_deauther_scene_text_input.c rename to applications/plugins/wifi_deauther/scenes/wifi_deauther_scene_text_input.c index 2968f4de1..339b2f2a3 100644 --- a/applications/plugins/wifi_deauther_v2/scenes/wifi_deauther_scene_text_input.c +++ b/applications/plugins/wifi_deauther/scenes/wifi_deauther_scene_text_input.c @@ -25,32 +25,16 @@ void wifi_deauther_scene_text_input_on_enter(void* context) { // Setup view TextInput* text_input = app->text_input; // Add help message to header - if(0 == strncmp("select aps", app->selected_tx_string, strlen("select aps"))) { - text_input_set_header_text(text_input, "Enter SSID ID to attack"); - } else if(0 == strncmp("select stations", app->selected_tx_string, strlen("select stations"))) { - text_input_set_header_text(text_input, "Enter Station ID to attack"); - } - if(0 == strncmp("deselect aps", app->selected_tx_string, strlen("deselect aps"))) { - text_input_set_header_text(text_input, "Enter SSID ID to remove"); - } else if( - 0 == strncmp("deselect stations", app->selected_tx_string, strlen("deselect stations"))) { - text_input_set_header_text(text_input, "Enter Station ID to remove"); - } else if(0 == strncmp("get settings", app->selected_tx_string, strlen("get settings"))) { - text_input_set_header_text(text_input, "Get setting. Enter for all"); - } else if( - 0 == - strncmp( - "set webinterface false", app->selected_tx_string, strlen("set webinterface false"))) { - text_input_set_header_text(text_input, "Disable PWNED management AP"); - } else if(0 == strncmp("set ssid: pwned", app->selected_tx_string, strlen("set ssid: pwned"))) { - text_input_set_header_text(text_input, "Change management SSID"); - } else if( - 0 == - strncmp( - "set password: deauther", app->selected_tx_string, strlen("set password: deauther"))) { - text_input_set_header_text(text_input, "Change management PWD"); - } else if(0 == strncmp("save settings", app->selected_tx_string, strlen("save settings"))) { - text_input_set_header_text(text_input, "Save Settings"); + if(0 == strncmp("ssid -a -g", app->selected_tx_string, strlen("ssid -a -g"))) { + text_input_set_header_text(text_input, "Enter # SSIDs to generate"); + } else if(0 == strncmp("ssid -a -n", app->selected_tx_string, strlen("ssid -a -n"))) { + text_input_set_header_text(text_input, "Enter SSID name to add"); + } else if(0 == strncmp("ssid -r", app->selected_tx_string, strlen("ssid -r"))) { + text_input_set_header_text(text_input, "Remove target from SSID list"); + } else if(0 == strncmp("select -a", app->selected_tx_string, strlen("select -a"))) { + text_input_set_header_text(text_input, "Add target from AP list"); + } else if(0 == strncmp("select -s", app->selected_tx_string, strlen("select -s"))) { + text_input_set_header_text(text_input, "Add target from SSID list"); } else { text_input_set_header_text(text_input, "Add command arguments"); } @@ -65,7 +49,6 @@ void wifi_deauther_scene_text_input_on_enter(void* context) { view_dispatcher_switch_to_view(app->view_dispatcher, WifideautherAppViewTextInput); } -///* bool wifi_deauther_scene_text_input_on_event(void* context, SceneManagerEvent event) { WifideautherApp* app = context; bool consumed = false; @@ -87,4 +70,3 @@ void wifi_deauther_scene_text_input_on_exit(void* context) { text_input_reset(app->text_input); } -//*/ \ No newline at end of file diff --git a/applications/plugins/wifi_deauther_v2/wifi_10px.png b/applications/plugins/wifi_deauther/wifi_10px.png similarity index 100% rename from applications/plugins/wifi_deauther_v2/wifi_10px.png rename to applications/plugins/wifi_deauther/wifi_10px.png diff --git a/applications/plugins/wifi_deauther_v2/wifi_deauther_app.c b/applications/plugins/wifi_deauther/wifi_deauther_app.c similarity index 100% rename from applications/plugins/wifi_deauther_v2/wifi_deauther_app.c rename to applications/plugins/wifi_deauther/wifi_deauther_app.c diff --git a/applications/plugins/wifi_deauther_v2/wifi_deauther_app.h b/applications/plugins/wifi_deauther/wifi_deauther_app.h similarity index 100% rename from applications/plugins/wifi_deauther_v2/wifi_deauther_app.h rename to applications/plugins/wifi_deauther/wifi_deauther_app.h diff --git a/applications/plugins/wifi_deauther_v2/wifi_deauther_app_i.h b/applications/plugins/wifi_deauther/wifi_deauther_app_i.h similarity index 100% rename from applications/plugins/wifi_deauther_v2/wifi_deauther_app_i.h rename to applications/plugins/wifi_deauther/wifi_deauther_app_i.h diff --git a/applications/plugins/wifi_deauther_v2/wifi_deauther_custom_event.h b/applications/plugins/wifi_deauther/wifi_deauther_custom_event.h similarity index 100% rename from applications/plugins/wifi_deauther_v2/wifi_deauther_custom_event.h rename to applications/plugins/wifi_deauther/wifi_deauther_custom_event.h diff --git a/applications/plugins/wifi_deauther_v2/wifi_deauther_uart.c b/applications/plugins/wifi_deauther/wifi_deauther_uart.c similarity index 100% rename from applications/plugins/wifi_deauther_v2/wifi_deauther_uart.c rename to applications/plugins/wifi_deauther/wifi_deauther_uart.c index 10e73c323..434ccfd97 100644 --- a/applications/plugins/wifi_deauther_v2/wifi_deauther_uart.c +++ b/applications/plugins/wifi_deauther/wifi_deauther_uart.c @@ -42,8 +42,6 @@ void wifi_deauther_uart_on_irq_cb(UartIrqEvent ev, uint8_t data, void* context) static int32_t uart_worker(void* context) { WifideautherUart* uart = (void*)context; - uart->rx_stream = xStreamBufferCreate(RX_BUF_SIZE, 1); - while(1) { uint32_t events = furi_thread_flags_wait(WORKER_ALL_RX_EVENTS, FuriFlagWaitAny, FuriWaitForever); @@ -69,11 +67,8 @@ void wifi_deauther_uart_tx(uint8_t* data, size_t len) { WifideautherUart* wifi_deauther_uart_init(WifideautherApp* app) { WifideautherUart* uart = malloc(sizeof(WifideautherUart)); - furi_hal_console_disable(); - furi_hal_uart_set_br(UART_CH, BAUDRATE); - furi_hal_uart_set_irq_cb(UART_CH, wifi_deauther_uart_on_irq_cb, uart); - uart->app = app; + uart->rx_stream = xStreamBufferCreate(RX_BUF_SIZE, 1); uart->rx_thread = furi_thread_alloc(); furi_thread_set_name(uart->rx_thread, "WifideautherUartRxThread"); furi_thread_set_stack_size(uart->rx_thread, 1024); @@ -81,6 +76,11 @@ WifideautherUart* wifi_deauther_uart_init(WifideautherApp* app) { furi_thread_set_callback(uart->rx_thread, uart_worker); furi_thread_start(uart->rx_thread); + + furi_hal_console_disable(); + furi_hal_uart_set_br(UART_CH, BAUDRATE); + furi_hal_uart_set_irq_cb(UART_CH, wifi_deauther_uart_on_irq_cb, uart); + return uart; } diff --git a/applications/plugins/wifi_deauther_v2/wifi_deauther_uart.h b/applications/plugins/wifi_deauther/wifi_deauther_uart.h similarity index 100% rename from applications/plugins/wifi_deauther_v2/wifi_deauther_uart.h rename to applications/plugins/wifi_deauther/wifi_deauther_uart.h diff --git a/applications/plugins/wifi_marauder_companion/application.fam b/applications/plugins/wifi_marauder_companion/application.fam index e3185d50a..049d66045 100644 --- a/applications/plugins/wifi_marauder_companion/application.fam +++ b/applications/plugins/wifi_marauder_companion/application.fam @@ -8,5 +8,5 @@ App( stack_size=1 * 1024, order=90, fap_icon="wifi_10px.png", - fap_category="GPIO", + fap_category="WiFi", ) diff --git a/applications/plugins/wifi_marauder_companion/wifi_marauder_app.c b/applications/plugins/wifi_marauder_companion/wifi_marauder_app.c index 79d35a808..1deb4e6f2 100644 --- a/applications/plugins/wifi_marauder_companion/wifi_marauder_app.c +++ b/applications/plugins/wifi_marauder_companion/wifi_marauder_app.c @@ -92,6 +92,9 @@ void wifi_marauder_app_free(WifiMarauderApp* app) { int32_t wifi_marauder_app(void* p) { UNUSED(p); + furi_hal_power_enable_otg(); + furi_delay_ms(300); + WifiMarauderApp* wifi_marauder_app = wifi_marauder_app_alloc(); wifi_marauder_app->uart = wifi_marauder_uart_init(wifi_marauder_app); @@ -100,5 +103,7 @@ int32_t wifi_marauder_app(void* p) { wifi_marauder_app_free(wifi_marauder_app); + furi_hal_power_disable_otg(); + return 0; } diff --git a/applications/plugins/wifi_scanner/application.fam b/applications/plugins/wifi_scanner/application.fam index bc7d352eb..a46ecf3fb 100644 --- a/applications/plugins/wifi_scanner/application.fam +++ b/applications/plugins/wifi_scanner/application.fam @@ -8,5 +8,5 @@ App( stack_size=2 * 1024, order=70, fap_icon="wifi_10px.png", - fap_category="GPIO", + fap_category="WiFi", ) diff --git a/applications/plugins/wifi_scanner/wifi_scanner.c b/applications/plugins/wifi_scanner/wifi_scanner.c index 826048e26..3a79ce16e 100644 --- a/applications/plugins/wifi_scanner/wifi_scanner.c +++ b/applications/plugins/wifi_scanner/wifi_scanner.c @@ -83,6 +83,7 @@ typedef enum EWorkerEventFlags { } EWorkerEventFlags; typedef struct SWiFiScannerApp { + FuriMutex* mutex; Gui* m_gui; FuriThread* m_worker_thread; NotificationApp* m_notification; @@ -162,10 +163,9 @@ void DrawSignalStrengthBar(Canvas* canvas, int rssi, int x, int y, int width, in } static void wifi_module_render_callback(Canvas* const canvas, void* ctx) { - SWiFiScannerApp* app = acquire_mutex((ValueMutex*)ctx, 25); - if(app == NULL) { - return; - } + furi_assert(ctx); + SWiFiScannerApp* app = ctx; + furi_mutex_acquire(app->mutex, FuriWaitForever); canvas_clear(canvas); @@ -433,7 +433,7 @@ static void wifi_module_render_callback(Canvas* const canvas, void* ctx) { break; } } - release_mutex((ValueMutex*)ctx, app); + furi_mutex_release(app->mutex); } static void wifi_module_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { @@ -460,14 +460,15 @@ static void uart_on_irq_cb(UartIrqEvent ev, uint8_t data, void* context) { static int32_t uart_worker(void* context) { furi_assert(context); - SWiFiScannerApp* app = acquire_mutex((ValueMutex*)context, 25); + SWiFiScannerApp* app = context; + furi_mutex_acquire(app->mutex, FuriWaitForever); if(app == NULL) { return 1; } FuriStreamBuffer* rx_stream = app->m_rx_stream; - release_mutex((ValueMutex*)context, app); + furi_mutex_release(app->mutex); while(true) { uint32_t events = furi_thread_flags_wait( @@ -527,7 +528,8 @@ static int32_t uart_worker(void* context) { } while(end < stringSize); furi_string_free(chunk); - app = acquire_mutex((ValueMutex*)context, 25); + app = context; + furi_mutex_acquire(app->mutex, FuriWaitForever); if(app == NULL) { return 1; } @@ -584,7 +586,7 @@ static int32_t uart_worker(void* context) { } } - release_mutex((ValueMutex*)context, app); + furi_mutex_release(app->mutex); // Clear string array for(index = 0; index < EChunkArrayData_ENUM_MAX; ++index) { @@ -665,8 +667,9 @@ int32_t wifi_scanner_app(void* p) { #endif // ENABLE_MODULE_POWER #endif // ENABLE_MODULE_DETECTION - ValueMutex app_data_mutex; - if(!init_mutex(&app_data_mutex, app, sizeof(SWiFiScannerApp))) { + app->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + + if(!app->mutex) { WIFI_APP_LOG_E("cannot create mutex\r\n"); free(app); return 255; @@ -674,10 +677,10 @@ int32_t wifi_scanner_app(void* p) { WIFI_APP_LOG_I("Mutex created"); - app->m_notification = furi_record_open("notification"); + app->m_notification = furi_record_open(RECORD_NOTIFICATION); ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, wifi_module_render_callback, &app_data_mutex); + view_port_draw_callback_set(view_port, wifi_module_render_callback, app); view_port_input_callback_set(view_port, wifi_module_input_callback, event_queue); // Open GUI and register view_port @@ -691,7 +694,7 @@ int32_t wifi_scanner_app(void* p) { app->m_worker_thread = furi_thread_alloc(); furi_thread_set_name(app->m_worker_thread, "WiFiModuleUARTWorker"); furi_thread_set_stack_size(app->m_worker_thread, 1024); - furi_thread_set_context(app->m_worker_thread, &app_data_mutex); + furi_thread_set_context(app->m_worker_thread, app); furi_thread_set_callback(app->m_worker_thread, uart_worker); furi_thread_start(app->m_worker_thread); WIFI_APP_LOG_I("UART thread allocated"); @@ -710,7 +713,7 @@ int32_t wifi_scanner_app(void* p) { SPluginEvent event; for(bool processing = true; processing;) { FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); - SWiFiScannerApp* app = (SWiFiScannerApp*)acquire_mutex_block(&app_data_mutex); + furi_mutex_acquire(app->mutex, FuriWaitForever); #if ENABLE_MODULE_DETECTION if(!app->m_wifiModuleAttached) { @@ -807,7 +810,7 @@ int32_t wifi_scanner_app(void* p) { #endif view_port_update(view_port); - release_mutex(&app_data_mutex, app); + furi_mutex_release(app->mutex); } WIFI_APP_LOG_I("Start exit app"); @@ -831,7 +834,7 @@ int32_t wifi_scanner_app(void* p) { // Close gui record furi_record_close(RECORD_GUI); - furi_record_close("notification"); + furi_record_close(RECORD_NOTIFICATION); app->m_gui = NULL; view_port_free(view_port); @@ -840,7 +843,7 @@ int32_t wifi_scanner_app(void* p) { furi_stream_buffer_free(app->m_rx_stream); - delete_mutex(&app_data_mutex); + furi_mutex_free(app->mutex); // Free rest free(app); diff --git a/applications/plugins/subbrute/LICENSE b/applications/plugins/wii_ec_anal/LICENSE similarity index 97% rename from applications/plugins/subbrute/LICENSE rename to applications/plugins/wii_ec_anal/LICENSE index 06dcf7e87..95e544a06 100644 --- a/applications/plugins/subbrute/LICENSE +++ b/applications/plugins/wii_ec_anal/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2022 Der Skythe +Copyright (c) 2022 BlueChip Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/applications/plugins/wii_ec_anal/_image_tool/_convert.sh b/applications/plugins/wii_ec_anal/_image_tool/_convert.sh old mode 100644 new mode 100755 diff --git a/applications/plugins/wii_ec_anal/application.fam b/applications/plugins/wii_ec_anal/application.fam index 4dc251713..0f0ee08e0 100644 --- a/applications/plugins/wii_ec_anal/application.fam +++ b/applications/plugins/wii_ec_anal/application.fam @@ -3,7 +3,7 @@ App( # --- App Info appid="wii_ec_anal", - name="Wii EC Analyser", + name="[GPIO] Wii EC Analyser", # --- Entry point apptype=FlipperAppType.EXTERNAL, entry_point="wii_ec_anal", diff --git a/applications/plugins/wii_ec_anal/info.sh b/applications/plugins/wii_ec_anal/info.sh old mode 100644 new mode 100755 diff --git a/applications/plugins/wii_ec_anal/wii_anal.c b/applications/plugins/wii_ec_anal/wii_anal.c index f0af1c9c5..34b4f318b 100644 --- a/applications/plugins/wii_ec_anal/wii_anal.c +++ b/applications/plugins/wii_ec_anal/wii_anal.c @@ -94,10 +94,9 @@ static void cbDraw(Canvas* const canvas, void* ctx) { furi_assert(canvas); furi_assert(ctx); - state_t* state = NULL; - // Try to acquire the mutex for the plugin state variables, timeout = 25mS - if(!(state = (state_t*)acquire_mutex((ValueMutex*)ctx, 25))) return; + state_t* state = ctx; + furi_mutex_acquire(state->mutex, FuriWaitForever); switch(state->scene) { //--------------------------------------------------------------------- @@ -182,7 +181,7 @@ static void cbDraw(Canvas* const canvas, void* ctx) { } // Release the mutex - release_mutex((ValueMutex*)ctx, state); + furi_mutex_release(state->mutex); LEAVE; return; @@ -308,7 +307,6 @@ int32_t wii_ec_anal(void) { Gui* gui = NULL; ViewPort* vpp = NULL; state_t* state = NULL; - ValueMutex mutex = {0}; FuriMessageQueue* queue = NULL; const uint32_t queueSz = 20; // maximum messages in queue uint32_t tmo = (3.5f * 1000); // timeout splash screen after N seconds @@ -346,7 +344,8 @@ int32_t wii_ec_anal(void) { goto bail; } // 5. Create a mutex for (reading/writing) the plugin state variables - if(!init_mutex(&mutex, state, sizeof(state))) { + state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!state->mutex) { ERROR(wii_errs[(error = ERR_NO_MUTEX)]); goto bail; } @@ -360,7 +359,7 @@ int32_t wii_ec_anal(void) { // 7a. Register a callback for input events view_port_input_callback_set(vpp, cbInput, queue); // 7b. Register a callback for draw events - view_port_draw_callback_set(vpp, cbDraw, &mutex); + view_port_draw_callback_set(vpp, cbDraw, state); // ===== Start GUI Interface ===== // 8. Attach the viewport to the GUI @@ -435,10 +434,7 @@ int32_t wii_ec_anal(void) { // Read successful // *** Try to lock the plugin state variables *** - if(!(state = (state_t*)acquire_mutex_block(&mutex))) { - ERROR(wii_errs[(error = ERR_MUTEX_BLOCK)]); - goto bail; - } + furi_mutex_acquire(state->mutex, FuriWaitForever); // *** Handle events *** switch(msg.id) { @@ -476,10 +472,7 @@ int32_t wii_ec_anal(void) { if(redraw) view_port_update(vpp); // *** Try to release the plugin state variables *** - if(!release_mutex(&mutex, state)) { - ERROR(wii_errs[(error = ERR_MUTEX_RELEASE)]); - goto bail; - } + furi_mutex_release(state->mutex); } while(state->run); // ===== Game Over ===== @@ -514,10 +507,7 @@ bail: } // 5. Free the mutex - if(mutex.mutex) { - delete_mutex(&mutex); - mutex.mutex = NULL; - } + furi_mutex_free(state->mutex); // 4. Free up state pointer(s) // none diff --git a/applications/plugins/wii_ec_anal/wii_anal.h b/applications/plugins/wii_ec_anal/wii_anal.h index 3aae61fdc..d8997b030 100644 --- a/applications/plugins/wii_ec_anal/wii_anal.h +++ b/applications/plugins/wii_ec_anal/wii_anal.h @@ -56,6 +56,7 @@ typedef struct eventMsg { // Access to this memory is controlled by mutex // typedef struct state { + FuriMutex* mutex; bool run; // true : plugin is running bool timerEn; // controller scanning enabled diff --git a/applications/plugins/yatzee/application.fam b/applications/plugins/yatzee/application.fam index b30e90d23..1c59d634b 100644 --- a/applications/plugins/yatzee/application.fam +++ b/applications/plugins/yatzee/application.fam @@ -1,5 +1,5 @@ App( - appid="Yatzee", + appid="yatzee", name="Yatzee", apptype=FlipperAppType.EXTERNAL, entry_point="yatzee_main", diff --git a/applications/plugins/yatzee/yatzee.c b/applications/plugins/yatzee/yatzee.c index 304a604c1..fa114fde4 100644 --- a/applications/plugins/yatzee/yatzee.c +++ b/applications/plugins/yatzee/yatzee.c @@ -1,4 +1,4 @@ -#include "Yatzee_icons.h" +#include "yatzee_icons.h" #include #include diff --git a/applications/plugins/zombiez/zombiez.c b/applications/plugins/zombiez/zombiez.c index bd2a5c97d..590edf97d 100644 --- a/applications/plugins/zombiez/zombiez.c +++ b/applications/plugins/zombiez/zombiez.c @@ -51,6 +51,7 @@ typedef struct { } Projectile; typedef struct { + FuriMutex* mutex; GameState game_state; Player player; @@ -65,10 +66,9 @@ typedef struct { } PluginState; static void render_callback(Canvas* const canvas, void* ctx) { - const PluginState* plugin_state = acquire_mutex((ValueMutex*)ctx, 25); - if(plugin_state == NULL) { - return; - } + furi_assert(ctx); + const PluginState* plugin_state = ctx; + furi_mutex_acquire(plugin_state->mutex, FuriWaitForever); canvas_draw_frame(canvas, 0, 0, 128, 64); @@ -180,7 +180,7 @@ static void render_callback(Canvas* const canvas, void* ctx) { //canvas_draw_str_aligned(canvas, 32, 16, AlignLeft, AlignBottom, info); //free(info); - release_mutex((ValueMutex*)ctx, plugin_state); + furi_mutex_release(plugin_state->mutex); } static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { @@ -294,8 +294,8 @@ int32_t zombiez_game_app(void* p) { PluginState* plugin_state = malloc(sizeof(PluginState)); zombiez_state_init(plugin_state); - ValueMutex state_mutex; - if(!init_mutex(&state_mutex, plugin_state, sizeof(PluginState))) { + plugin_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!plugin_state->mutex) { FURI_LOG_E("Zombiez", "cannot create mutex\r\n"); return_code = 255; goto free_and_exit; @@ -303,7 +303,7 @@ int32_t zombiez_game_app(void* p) { // Set system callbacks ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, render_callback, &state_mutex); + view_port_draw_callback_set(view_port, render_callback, plugin_state); view_port_input_callback_set(view_port, input_callback, event_queue); FuriTimer* timer = furi_timer_alloc(timer_callback, FuriTimerTypePeriodic, event_queue); @@ -317,7 +317,7 @@ int32_t zombiez_game_app(void* p) { bool isRunning = true; while(isRunning) { FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); - PluginState* plugin_state = (PluginState*)acquire_mutex_block(&state_mutex); + furi_mutex_acquire(plugin_state->mutex, FuriWaitForever); if(event_status == FuriStatusOk) { if(event.type == EventTypeKey) { if(event.input.type == InputTypePress) { @@ -377,12 +377,10 @@ int32_t zombiez_game_app(void* p) { } else if(event.type == EventTypeTick) { tick(plugin_state); } - } else { - // event timeout } view_port_update(view_port); - release_mutex(&state_mutex, plugin_state); + furi_mutex_release(plugin_state->mutex); } furi_timer_free(timer); @@ -390,11 +388,11 @@ int32_t zombiez_game_app(void* p) { gui_remove_view_port(gui, view_port); furi_record_close(RECORD_GUI); view_port_free(view_port); - delete_mutex(&state_mutex); + furi_mutex_free(plugin_state->mutex); free_and_exit: free(plugin_state); furi_message_queue_free(event_queue); return return_code; -} \ No newline at end of file +} diff --git a/applications/services/application.fam b/applications/services/application.fam index 934b0b012..6c41160db 100644 --- a/applications/services/application.fam +++ b/applications/services/application.fam @@ -6,6 +6,7 @@ App( "crypto_start", "rpc_start", "bt", + "xtreme", "desktop", "loader", "power", diff --git a/applications/services/applications.h b/applications/services/applications.h index c0672f7da..871e9af54 100644 --- a/applications/services/applications.h +++ b/applications/services/applications.h @@ -11,10 +11,10 @@ typedef enum { typedef struct { const FuriThreadCallback app; const char* name; + const char* appid; const size_t stack_size; const Icon* icon; const FlipperApplicationFlag flags; - const char* link; } FlipperApplication; typedef void (*FlipperOnStartHook)(void); diff --git a/applications/services/bt/bt_service/bt.c b/applications/services/bt/bt_service/bt.c index 7abef0e68..31a43f637 100644 --- a/applications/services/bt/bt_service/bt.c +++ b/applications/services/bt/bt_service/bt.c @@ -61,8 +61,11 @@ static ViewPort* bt_pin_code_view_port_alloc(Bt* bt) { } static void bt_pin_code_show(Bt* bt, uint32_t pin_code) { + furi_assert(bt); bt->pin_code = pin_code; notification_message(bt->notification, &sequence_display_backlight_on); + if(bt->suppress_pin_screen) return; + gui_view_port_send_to_front(bt->gui, bt->pin_code_view_port); view_port_enabled_set(bt->pin_code_view_port, true); } @@ -76,10 +79,10 @@ static void bt_pin_code_hide(Bt* bt) { static bool bt_pin_code_verify_event_handler(Bt* bt, uint32_t pin) { furi_assert(bt); - - if(bt_get_profile_pairing_method(bt) == GapPairingNone) return true; - + bt->pin_code = pin; notification_message(bt->notification, &sequence_display_backlight_on); + if(bt->suppress_pin_screen) return true; + FuriString* pin_str; dialog_message_set_icon(bt->dialog_message, XTREME_ASSETS()->I_BLE_Pairing_128x64, 0, 0); pin_str = furi_string_alloc_printf("Verify code\n%06lu", pin); @@ -154,6 +157,8 @@ Bt* bt_alloc() { // API evnent bt->api_event = furi_event_flag_alloc(); + bt->pin = 0; + return bt; } @@ -219,6 +224,7 @@ static bool bt_on_gap_event_callback(GapEvent event, void* context) { furi_assert(context); Bt* bt = context; bool ret = false; + bt->pin = 0; if(event.type == GapEventTypeConnected) { // Update status bar @@ -275,12 +281,14 @@ static bool bt_on_gap_event_callback(GapEvent event, void* context) { furi_message_queue_put(bt->message_queue, &message, FuriWaitForever) == FuriStatusOk); ret = true; } else if(event.type == GapEventTypePinCodeShow) { + bt->pin = event.data.pin_code; BtMessage message = { .type = BtMessageTypePinCodeShow, .data.pin_code = event.data.pin_code}; furi_check( furi_message_queue_put(bt->message_queue, &message, FuriWaitForever) == FuriStatusOk); ret = true; } else if(event.type == GapEventTypePinCodeVerify) { + bt->pin = event.data.pin_code; ret = bt_pin_code_verify_event_handler(bt, event.data.pin_code); } else if(event.type == GapEventTypeUpdateMTU) { bt->max_packet_size = event.data.max_packet_size; @@ -419,17 +427,15 @@ const uint8_t* bt_get_profile_mac_address(Bt* bt) { return furi_hal_bt_get_profile_mac_addr(get_hal_bt_profile(bt->profile)); } -bool bt_remote_rssi(Bt* bt, BtRssi* rssi) { +bool bt_remote_rssi(Bt* bt, uint8_t* rssi) { furi_assert(bt); - UNUSED(rssi); uint8_t rssi_val; uint32_t since = furi_hal_bt_get_conn_rssi(&rssi_val); if(since == 0) return false; - rssi->rssi = rssi_val; - rssi->since = since; + *rssi = rssi_val; return true; } @@ -451,6 +457,7 @@ void bt_disable_peer_key_update(Bt* bt) { } void bt_enable_peer_key_update(Bt* bt) { + furi_assert(bt); furi_hal_bt_set_key_storage_change_callback(bt_on_key_storage_change_callback, bt); } @@ -459,7 +466,7 @@ int32_t bt_srv(void* p) { Bt* bt = bt_alloc(); if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) { - FURI_LOG_W(TAG, "Skipped BT init: device in special startup mode"); + FURI_LOG_W(TAG, "Skipping start in special boot mode"); ble_glue_wait_for_c2_start(FURI_HAL_BT_C2_START_TIMEOUT); furi_record_create(RECORD_BT, bt); return 0; diff --git a/applications/services/bt/bt_service/bt.h b/applications/services/bt/bt_service/bt.h index a79c227f7..eb762dbf4 100644 --- a/applications/services/bt/bt_service/bt.h +++ b/applications/services/bt/bt_service/bt.h @@ -47,7 +47,7 @@ const char* bt_get_profile_adv_name(Bt* bt); void bt_set_profile_mac_address(Bt* bt, const uint8_t mac[6]); const uint8_t* bt_get_profile_mac_address(Bt* bt); -bool bt_remote_rssi(Bt* bt, BtRssi* rssi); +bool bt_remote_rssi(Bt* bt, uint8_t* rssi); void bt_set_profile_pairing_method(Bt* bt, GapPairing pairing_method); GapPairing bt_get_profile_pairing_method(Bt* bt); diff --git a/applications/services/bt/bt_service/bt_api.c b/applications/services/bt/bt_service/bt_api.c index e3cf78cc7..2f56b50a3 100644 --- a/applications/services/bt/bt_service/bt_api.c +++ b/applications/services/bt/bt_service/bt_api.c @@ -45,7 +45,14 @@ void bt_keys_storage_set_storage_path(Bt* bt, const char* keys_storage_path) { furi_assert(bt->keys_storage); furi_assert(keys_storage_path); - bt_keys_storage_set_file_path(bt->keys_storage, keys_storage_path); + Storage* storage = furi_record_open(RECORD_STORAGE); + FuriString* path = furi_string_alloc_set(keys_storage_path); + storage_common_resolve_path_and_ensure_app_directory(storage, path); + + bt_keys_storage_set_file_path(bt->keys_storage, furi_string_get_cstr(path)); + + furi_string_free(path); + furi_record_close(RECORD_STORAGE); } void bt_keys_storage_set_default_path(Bt* bt) { diff --git a/applications/services/bt/bt_service/bt_i.h b/applications/services/bt/bt_service/bt_i.h index c8a0e9965..41e5bcd8c 100644 --- a/applications/services/bt/bt_service/bt_i.h +++ b/applications/services/bt/bt_service/bt_i.h @@ -76,4 +76,6 @@ struct Bt { FuriEventFlag* api_event; BtStatusChangedCallback status_changed_cb; void* status_changed_ctx; + uint32_t pin; + bool suppress_pin_screen; }; diff --git a/applications/services/cli/cli.c b/applications/services/cli/cli.c index 384d17808..b68505c51 100644 --- a/applications/services/cli/cli.c +++ b/applications/services/cli/cli.c @@ -461,7 +461,7 @@ int32_t cli_srv(void* p) { if(furi_hal_rtc_get_boot_mode() == FuriHalRtcBootModeNormal) { cli_session_open(cli, &cli_vcp); } else { - FURI_LOG_W(TAG, "Skipped CLI session open: device in special startup mode"); + FURI_LOG_W(TAG, "Skipping start in special boot mode"); } while(1) { diff --git a/applications/services/cli/cli_commands.c b/applications/services/cli/cli_commands.c index bb1f3ab7f..a8d1b7e67 100644 --- a/applications/services/cli/cli_commands.c +++ b/applications/services/cli/cli_commands.c @@ -12,26 +12,48 @@ // Close to ISO, `date +'%Y-%m-%d %H:%M:%S %u'` #define CLI_DATE_FORMAT "%.4d-%.2d-%.2d %.2d:%.2d:%.2d %d" -void cli_command_device_info_callback(const char* key, const char* value, bool last, void* context) { - UNUSED(context); +void cli_command_info_callback(const char* key, const char* value, bool last, void* context) { UNUSED(last); + UNUSED(context); printf("%-30s: %s\r\n", key, value); } -/* - * Device Info Command +/** Info Command + * * This command is intended to be used by humans + * + * Arguments: + * - device - print device info + * - power - print power info + * - power_debug - print power debug info + * + * @param cli The cli instance + * @param args The arguments + * @param context The context */ -void cli_command_device_info(Cli* cli, FuriString* args, void* context) { +void cli_command_info(Cli* cli, FuriString* args, void* context) { UNUSED(cli); - UNUSED(args); - furi_hal_info_get(cli_command_device_info_callback, '_', context); + + if(context) { + furi_hal_info_get(cli_command_info_callback, '_', NULL); + return; + } + + if(!furi_string_cmp(args, "device")) { + furi_hal_info_get(cli_command_info_callback, '.', NULL); + } else if(!furi_string_cmp(args, "power")) { + furi_hal_power_info_get(cli_command_info_callback, '.', NULL); + } else if(!furi_string_cmp(args, "power_debug")) { + furi_hal_power_debug_get(cli_command_info_callback, NULL); + } else { + cli_print_usage("info", "", furi_string_get_cstr(args)); + } } void cli_command_help(Cli* cli, FuriString* args, void* context) { UNUSED(args); UNUSED(context); - printf("Commands we have:"); + printf("Commands available:"); // Command count const size_t commands_count = CliCommandTree_size(cli->commands); @@ -61,9 +83,9 @@ void cli_command_help(Cli* cli, FuriString* args, void* context) { if(furi_string_size(args) > 0) { cli_nl(); - printf("Also I have no clue what '"); + printf("`"); printf("%s", furi_string_get_cstr(args)); - printf("' is."); + printf("` command not found"); } } @@ -137,7 +159,7 @@ void cli_command_date(Cli* cli, FuriString* args, void* context) { } void cli_command_src(Cli* cli, FuriString* args, void* context) { - // Quality of life feaature for people exploring CLI on lab.flipper.net/cli + // Quality of life feature for people exploring CLI on lab.flipper.net/cli // By Yousef AK UNUSED(cli); UNUSED(args); @@ -360,11 +382,18 @@ void cli_command_ps(Cli* cli, FuriString* args, void* context) { FuriThreadId threads_ids[threads_num_max]; uint8_t thread_num = furi_thread_enumerate(threads_ids, threads_num_max); printf( - "%-20s %-14s %-8s %-8s %s\r\n", "Name", "Stack start", "Heap", "Stack", "Stack min free"); + "%-20s %-20s %-14s %-8s %-8s %s\r\n", + "AppID", + "Name", + "Stack start", + "Heap", + "Stack", + "Stack min free"); for(uint8_t i = 0; i < thread_num; i++) { TaskControlBlock* tcb = (TaskControlBlock*)threads_ids[i]; printf( - "%-20s 0x%-12lx %-8zu %-8lu %-8lu\r\n", + "%-20s %-20s 0x%-12lx %-8zu %-8lu %-8lu\r\n", + furi_thread_get_appid(threads_ids[i]), furi_thread_get_name(threads_ids[i]), (uint32_t)tcb->pxStack, memmgr_heap_get_thread_memory(threads_ids[i]), @@ -420,8 +449,9 @@ void cli_command_i2c(Cli* cli, FuriString* args, void* context) { } void cli_commands_init(Cli* cli) { - cli_add_command(cli, "!", CliCommandFlagParallelSafe, cli_command_device_info, NULL); - cli_add_command(cli, "device_info", CliCommandFlagParallelSafe, cli_command_device_info, NULL); + cli_add_command(cli, "!", CliCommandFlagParallelSafe, cli_command_info, (void*)true); + cli_add_command(cli, "info", CliCommandFlagParallelSafe, cli_command_info, NULL); + cli_add_command(cli, "device_info", CliCommandFlagParallelSafe, cli_command_info, (void*)true); cli_add_command(cli, "src", CliCommandFlagParallelSafe, cli_command_src, NULL); cli_add_command(cli, "source", CliCommandFlagParallelSafe, cli_command_src, NULL); diff --git a/applications/services/crypto/crypto_cli.c b/applications/services/crypto/crypto_cli.c index a286f4457..511a9d2a8 100644 --- a/applications/services/crypto/crypto_cli.c +++ b/applications/services/crypto/crypto_cli.c @@ -14,7 +14,7 @@ void crypto_cli_print_usage() { "\tdecrypt \t - Using key from secure enclave and IV decrypt hex encoded encrypted with AES256CBC data to plain text\r\n"); printf("\thas_key \t - Check if secure enclave has key in slot\r\n"); printf( - "\tstore_key \t - Store key in secure enclave. !!! NON-REVERSABLE OPERATION - READ MANUAL FIRST !!!\r\n"); + "\tstore_key \t - Store key in secure enclave. !!! NON-REVERSIBLE OPERATION - READ MANUAL FIRST !!!\r\n"); }; void crypto_cli_encrypt(Cli* cli, FuriString* args) { diff --git a/applications/services/desktop/animations/animation_manager.c b/applications/services/desktop/animations/animation_manager.c index 2d100b2e1..ade83a3d8 100644 --- a/applications/services/desktop/animations/animation_manager.c +++ b/applications/services/desktop/animations/animation_manager.c @@ -415,7 +415,7 @@ static StorageAnimation* } } - uint32_t lucky_number = furi_hal_random_get() % (whole_weight != 0 ? whole_weight : 1); + uint32_t lucky_number = furi_hal_random_get() % whole_weight; uint32_t weight = 0; StorageAnimation* selected = NULL; diff --git a/applications/services/desktop/animations/animation_manager.h b/applications/services/desktop/animations/animation_manager.h index e1340a0b5..d5a6ba384 100644 --- a/applications/services/desktop/animations/animation_manager.h +++ b/applications/services/desktop/animations/animation_manager.h @@ -152,8 +152,8 @@ bool animation_manager_is_animation_loaded(AnimationManager* animation_manager); void animation_manager_unload_and_stall_animation(AnimationManager* animation_manager); /** - * Load and Contunue execution of animation manager. + * Load and Continue execution of animation manager. * * @animation_manager instance */ -void animation_manager_load_and_continue_animation(AnimationManager* animation_manager); \ No newline at end of file +void animation_manager_load_and_continue_animation(AnimationManager* animation_manager); diff --git a/applications/services/desktop/animations/animation_storage.c b/applications/services/desktop/animations/animation_storage.c index 0d09f18dd..d04871e62 100644 --- a/applications/services/desktop/animations/animation_storage.c +++ b/applications/services/desktop/animations/animation_storage.c @@ -325,7 +325,7 @@ static bool animation_storage_load_frames( FURI_CONST_ASSIGN(icon->width, width); icon->frames = malloc(sizeof(const uint8_t*) * icon->frame_count); - bool frames_ok = true; + bool frames_ok = false; File* file = storage_file_alloc(storage); FileInfo file_info; FuriString* filename; @@ -333,41 +333,34 @@ static bool animation_storage_load_frames( size_t max_filesize = ROUND_UP_TO(width, 8) * height + 2; for(int i = 0; i < icon->frame_count; ++i) { - FURI_CONST_ASSIGN_PTR(icon->frames[i], 0); - if(frames_ok) { - frames_ok = false; - furi_string_printf(filename, "%s/%s/frame_%d.bm", ANIMATION_DIR, name, i); - do { - if(storage_common_stat(storage, furi_string_get_cstr(filename), &file_info) != - FSE_OK) - break; - if(file_info.size > max_filesize) { - FURI_LOG_E( - TAG, - "Filesize %lld, max: %d (width %d, height %d)", - file_info.size, - max_filesize, - width, - height); - break; - } - if(!storage_file_open( - file, furi_string_get_cstr(filename), FSAM_READ, FSOM_OPEN_EXISTING)) { - FURI_LOG_E(TAG, "Can't open file \'%s\'", furi_string_get_cstr(filename)); - break; - } + frames_ok = false; + furi_string_printf(filename, "%s/%s/frame_%d.bm", ANIMATION_DIR, name, i); - FURI_CONST_ASSIGN_PTR(icon->frames[i], malloc(file_info.size)); - if(storage_file_read(file, (void*)icon->frames[i], file_info.size) != - file_info.size) { - FURI_LOG_E(TAG, "Read failed: \'%s\'", furi_string_get_cstr(filename)); - break; - } else { - frames_ok = true; - } - storage_file_close(file); - } while(0); + if(storage_common_stat(storage, furi_string_get_cstr(filename), &file_info) != FSE_OK) + break; + if(file_info.size > max_filesize) { + FURI_LOG_E( + TAG, + "Filesize %lld, max: %d (width %d, height %d)", + file_info.size, + max_filesize, + width, + height); + break; } + if(!storage_file_open( + file, furi_string_get_cstr(filename), FSAM_READ, FSOM_OPEN_EXISTING)) { + FURI_LOG_E(TAG, "Can't open file \'%s\'", furi_string_get_cstr(filename)); + break; + } + + FURI_CONST_ASSIGN_PTR(icon->frames[i], malloc(file_info.size)); + if(storage_file_read(file, (void*)icon->frames[i], file_info.size) != file_info.size) { + FURI_LOG_E(TAG, "Read failed: \'%s\'", furi_string_get_cstr(filename)); + break; + } + storage_file_close(file); + frames_ok = true; } if(!frames_ok) { @@ -528,7 +521,6 @@ static BubbleAnimation* animation_storage_load_animation(const char* name) { animation->active_cycles = u32value; if(!flipper_format_read_uint32(ff, "Frame rate", &u32value, 1)) break; uint16_t anim_speed = XTREME_SETTINGS()->anim_speed; - anim_speed = (anim_speed == 0 ? 100 : anim_speed); u32value = (u32value * anim_speed) / 100; FURI_CONST_ASSIGN(animation->icon_animation.frame_rate, u32value < 1 ? 1 : u32value); if(!flipper_format_read_uint32(ff, "Duration", &u32value, 1)) break; diff --git a/applications/services/desktop/desktop.c b/applications/services/desktop/desktop.c index 7840cd00a..1fe23bb1d 100644 --- a/applications/services/desktop/desktop.c +++ b/applications/services/desktop/desktop.c @@ -17,6 +17,8 @@ #include "helpers/pin_lock.h" #include "helpers/slideshow_filename.h" +#define TAG "Desktop" + static void desktop_auto_lock_arm(Desktop*); static void desktop_auto_lock_inhibit(Desktop*); static void desktop_start_auto_lock_timer(Desktop*); @@ -56,12 +58,10 @@ static bool desktop_custom_event_callback(void* context, uint32_t event) { return true; case DesktopGlobalAutoLock: if(!loader_is_locked(desktop->loader)) { - if(desktop->settings.pin_code.length > 0) { + if(desktop->settings.auto_lock_with_pin && desktop->settings.pin_code.length > 0) { desktop_pin_lock(&desktop->settings); - desktop_lock(desktop); - } else { - desktop_lock(desktop); } + desktop_lock(desktop); } return true; } @@ -306,43 +306,44 @@ int32_t desktop_srv(void* p) { UNUSED(p); if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) { - FURI_LOG_W("Desktop", "Desktop load skipped. Device is in special startup mode."); - } else { - Desktop* desktop = desktop_alloc(); - - bool loaded = DESKTOP_SETTINGS_LOAD(&desktop->settings); - if(!loaded) { - memset(&desktop->settings, 0, sizeof(desktop->settings)); - DESKTOP_SETTINGS_SAVE(&desktop->settings); - } - - scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain); - - desktop_pin_lock_init(&desktop->settings); - - if(!desktop_pin_lock_is_locked()) { - if(!loader_is_locked(desktop->loader)) { - desktop_auto_lock_arm(desktop); - } - } else { - desktop_lock(desktop); - } - - if(desktop_check_file_flag(SLIDESHOW_FS_PATH)) { - scene_manager_next_scene(desktop->scene_manager, DesktopSceneSlideshow); - } - - if(!furi_hal_version_do_i_belong_here()) { - scene_manager_next_scene(desktop->scene_manager, DesktopSceneHwMismatch); - } - - if(furi_hal_rtc_get_fault_data()) { - scene_manager_next_scene(desktop->scene_manager, DesktopSceneFault); - } - - view_dispatcher_run(desktop->view_dispatcher); - desktop_free(desktop); + FURI_LOG_W(TAG, "Skipping start in special boot mode"); + return 0; } + Desktop* desktop = desktop_alloc(); + + bool loaded = DESKTOP_SETTINGS_LOAD(&desktop->settings); + if(!loaded) { + memset(&desktop->settings, 0, sizeof(desktop->settings)); + DESKTOP_SETTINGS_SAVE(&desktop->settings); + } + + scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain); + + desktop_pin_lock_init(&desktop->settings); + + if(!desktop_pin_lock_is_locked()) { + if(!loader_is_locked(desktop->loader)) { + desktop_auto_lock_arm(desktop); + } + } else { + desktop_lock(desktop); + } + + if(desktop_check_file_flag(SLIDESHOW_FS_PATH)) { + scene_manager_next_scene(desktop->scene_manager, DesktopSceneSlideshow); + } + + if(!furi_hal_version_do_i_belong_here()) { + scene_manager_next_scene(desktop->scene_manager, DesktopSceneHwMismatch); + } + + if(furi_hal_rtc_get_fault_data()) { + scene_manager_next_scene(desktop->scene_manager, DesktopSceneFault); + } + + view_dispatcher_run(desktop->view_dispatcher); + desktop_free(desktop); + return 0; } diff --git a/applications/services/desktop/desktop_i.h b/applications/services/desktop/desktop_i.h index 2bb0d683f..822eecc76 100644 --- a/applications/services/desktop/desktop_i.h +++ b/applications/services/desktop/desktop_i.h @@ -68,6 +68,8 @@ struct Desktop { FuriPubSub* input_events_pubsub; FuriPubSubSubscription* input_events_subscription; FuriTimer* auto_lock_timer; + + bool in_transition; }; Desktop* desktop_alloc(); diff --git a/applications/services/desktop/desktop_settings.h b/applications/services/desktop/desktop_settings.h index 6285f9c90..0019f1ef8 100644 --- a/applications/services/desktop/desktop_settings.h +++ b/applications/services/desktop/desktop_settings.h @@ -7,8 +7,9 @@ #include #include #include +#include -#define DESKTOP_SETTINGS_VER (6) +#define DESKTOP_SETTINGS_VER (7) #define DESKTOP_SETTINGS_PATH INT_PATH(DESKTOP_SETTINGS_FILE_NAME) #define DESKTOP_SETTINGS_MAGIC (0x17) @@ -36,8 +37,6 @@ #define MIN_PIN_SIZE 4 #define MAX_APP_LENGTH 128 -#define FAP_LOADER_APP_NAME "Applications" - typedef struct { InputKey data[MAX_PIN_SIZE]; uint8_t length; @@ -54,4 +53,5 @@ typedef struct { PinCode pin_code; uint8_t is_locked; uint32_t auto_lock_delay_ms; + bool auto_lock_with_pin; } DesktopSettings; diff --git a/applications/services/desktop/scenes/desktop_scene_lock_menu.c b/applications/services/desktop/scenes/desktop_scene_lock_menu.c index a0a2c08e9..26e40e974 100644 --- a/applications/services/desktop/scenes/desktop_scene_lock_menu.c +++ b/applications/services/desktop/scenes/desktop_scene_lock_menu.c @@ -98,7 +98,8 @@ bool desktop_scene_lock_menu_on_event(void* context, SceneManagerEvent event) { break; case DesktopLockMenuEventXtremeSettings: - loader_start(desktop->loader, "Xtreme FW", NULL); + loader_start( + desktop->loader, FAP_LOADER_APP_NAME, EXT_PATH("apps/.Main/xtreme_app.fap")); break; default: break; diff --git a/applications/services/desktop/scenes/desktop_scene_main.c b/applications/services/desktop/scenes/desktop_scene_main.c index d7fc63578..a34eba444 100644 --- a/applications/services/desktop/scenes/desktop_scene_main.c +++ b/applications/services/desktop/scenes/desktop_scene_main.c @@ -1,6 +1,6 @@ #include #include -#include "applications/services/applications.h" +#include #include #include @@ -76,6 +76,7 @@ static void desktop_scene_main_open_app_or_profile(Desktop* desktop, const char* void desktop_scene_main_callback(DesktopEvent event, void* context) { Desktop* desktop = (Desktop*)context; + if(desktop->in_transition) return; view_dispatcher_send_custom_event(desktop->view_dispatcher, event); } @@ -119,13 +120,10 @@ bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) { case DesktopMainEventLock: if(desktop->settings.pin_code.length > 0) { - scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 1); desktop_pin_lock(&desktop->settings); - desktop_lock(desktop); - } else { scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 0); - desktop_lock(desktop); } + desktop_lock(desktop); consumed = true; break; diff --git a/applications/services/desktop/views/desktop_view_debug.c b/applications/services/desktop/views/desktop_view_debug.c index 016188874..8c72001b4 100644 --- a/applications/services/desktop/views/desktop_view_debug.c +++ b/applications/services/desktop/views/desktop_view_debug.c @@ -42,7 +42,7 @@ void desktop_debug_render(Canvas* canvas, void* model) { furi_hal_version_get_hw_target(), furi_hal_version_get_hw_body(), furi_hal_version_get_hw_connect(), - furi_hal_version_get_hw_region_name(), + furi_hal_version_get_hw_region_name_otp(), furi_hal_region_get_name(), my_name ? my_name : "Unknown"); canvas_draw_str(canvas, 0, 19 + STATUS_BAR_Y_SHIFT, buffer); @@ -75,7 +75,8 @@ void desktop_debug_render(Canvas* canvas, void* model) { c2_ver ? c2_ver->StackTypeString : ""); canvas_draw_str(canvas, 0, 40 + STATUS_BAR_Y_SHIFT, buffer); - snprintf(buffer, sizeof(buffer), "[%d] %s", version_get_target(ver), "dev"); + snprintf( + buffer, sizeof(buffer), "[%d] %s", version_get_target(ver), version_get_gitbranch(ver)); canvas_draw_str(canvas, 0, 50 + STATUS_BAR_Y_SHIFT, buffer); } else { diff --git a/applications/services/desktop/views/desktop_view_lock_menu.c b/applications/services/desktop/views/desktop_view_lock_menu.c index 9e02f2bdf..f9530fe84 100644 --- a/applications/services/desktop/views/desktop_view_lock_menu.c +++ b/applications/services/desktop/views/desktop_view_lock_menu.c @@ -1,13 +1,9 @@ #include #include -#include #include #include "../desktop_i.h" #include "desktop_view_lock_menu.h" -#include "xtreme/settings.h" - -#define LOCK_MENU_ITEMS_NB 5 typedef enum { DesktopLockMenuIndexLock, diff --git a/applications/services/desktop/views/desktop_view_main.c b/applications/services/desktop/views/desktop_view_main.c index 9149fd562..c1009fde1 100644 --- a/applications/services/desktop/views/desktop_view_main.c +++ b/applications/services/desktop/views/desktop_view_main.c @@ -16,7 +16,7 @@ struct DesktopMainView { TimerHandle_t poweroff_timer; }; -#define DESKTOP_MAIN_VIEW_POWEROFF_TIMEOUT 2000 +#define DESKTOP_MAIN_VIEW_POWEROFF_TIMEOUT 1300 static void desktop_main_poweroff_timer_callback(TimerHandle_t timer) { DesktopMainView* main_view = pvTimerGetTimerID(timer); @@ -85,7 +85,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_slideshow.c b/applications/services/desktop/views/desktop_view_slideshow.c index 002a16f68..e528d6878 100644 --- a/applications/services/desktop/views/desktop_view_slideshow.c +++ b/applications/services/desktop/views/desktop_view_slideshow.c @@ -61,9 +61,9 @@ static bool desktop_view_slideshow_input(InputEvent* event, void* context) { furi_timer_start(instance->timer, DESKTOP_SLIDESHOW_POWEROFF_SHORT); } else if(event->type == InputTypeRelease) { furi_timer_stop(instance->timer); - // if(!slideshow_is_one_page(model->slideshow)) { - // furi_timer_start(instance->timer, DESKTOP_SLIDESHOW_POWEROFF_LONG); - // } + /*if(!slideshow_is_one_page(model->slideshow)) { + furi_timer_start(instance->timer, DESKTOP_SLIDESHOW_POWEROFF_LONG); + }*/ } } view_commit_model(instance->view, update_view); diff --git a/applications/services/dialogs/dialogs_api.c b/applications/services/dialogs/dialogs_api.c index ca2435b9b..4723a1f91 100644 --- a/applications/services/dialogs/dialogs_api.c +++ b/applications/services/dialogs/dialogs_api.c @@ -2,6 +2,7 @@ #include "dialogs_i.h" #include #include +#include /****************** File browser ******************/ @@ -13,6 +14,22 @@ bool dialog_file_browser_show( FuriApiLock lock = api_lock_alloc_locked(); furi_check(lock != NULL); + Storage* storage = furi_record_open(RECORD_STORAGE); + FuriString* base_path = furi_string_alloc(); + + if(options && options->base_path) { + furi_string_set(base_path, options->base_path); + storage_common_resolve_path_and_ensure_app_directory(storage, base_path); + } + + if(result_path) { + storage_common_resolve_path_and_ensure_app_directory(storage, result_path); + } + + if(path) { + storage_common_resolve_path_and_ensure_app_directory(storage, path); + } + DialogsAppData data = { .file_browser = { .extension = options ? options->extension : "", @@ -24,7 +41,7 @@ bool dialog_file_browser_show( .preselected_filename = path, .item_callback = options ? options->item_loader_callback : NULL, .item_callback_context = options ? options->item_loader_context : NULL, - .base_path = options ? options->base_path : NULL, + .base_path = furi_string_get_cstr(base_path), }}; DialogsAppReturn return_data; @@ -39,6 +56,9 @@ bool dialog_file_browser_show( furi_message_queue_put(context->message_queue, &message, FuriWaitForever) == FuriStatusOk); api_lock_wait_unlock_and_free(lock); + furi_record_close(RECORD_STORAGE); + furi_string_free(base_path); + return return_data.bool_value; } diff --git a/applications/services/dolphin/application.fam b/applications/services/dolphin/application.fam index 78a097e67..5d275a7b7 100644 --- a/applications/services/dolphin/application.fam +++ b/applications/services/dolphin/application.fam @@ -6,5 +6,8 @@ App( cdefines=["SRV_DOLPHIN"], stack_size=1 * 1024, order=50, - sdk_headers=["dolphin.h"], + sdk_headers=[ + "dolphin.h", + "helpers/dolphin_state.h", + ], ) diff --git a/applications/services/dolphin/dolphin.c b/applications/services/dolphin/dolphin.c index e5b0ba52b..cf37c5cd9 100644 --- a/applications/services/dolphin/dolphin.c +++ b/applications/services/dolphin/dolphin.c @@ -6,7 +6,7 @@ #include #include #include -#include "furi_hal_random.h" +#include #define DOLPHIN_LOCK_EVENT_FLAG (0x1) #define TAG "Dolphin" @@ -22,18 +22,6 @@ void dolphin_deed(Dolphin* dolphin, DolphinDeed deed) { dolphin_event_send_async(dolphin, &event); } -DolphinDeed getRandomDeed() { - DolphinDeed returnGrp[14] = {1, 5, 8, 10, 12, 15, 17, 20, 21, 25, 26, 28, 29, 32}; - static bool rand_generator_inited = false; - if(!rand_generator_inited) { - srand(furi_get_tick()); - rand_generator_inited = true; - } - uint8_t diceRoll = (rand() % COUNT_OF(returnGrp)); // JUST TO GET IT GOING? AND FIX BUG - diceRoll = (rand() % COUNT_OF(returnGrp)); - return returnGrp[diceRoll]; -} - DolphinStats dolphin_stats(Dolphin* dolphin) { furi_assert(dolphin); @@ -92,8 +80,13 @@ Dolphin* dolphin_alloc() { dolphin->state = dolphin_state_alloc(); dolphin->event_queue = furi_message_queue_alloc(8, sizeof(DolphinEvent)); dolphin->pubsub = furi_pubsub_alloc(); + int32_t butthurt = XTREME_SETTINGS()->butthurt_timer; dolphin->butthurt_timer = xTimerCreate( - NULL, HOURS_IN_TICKS(2 * 24), pdTRUE, dolphin, dolphin_butthurt_timer_callback); + NULL, + (butthurt > 0) ? (butthurt * 1000) : -1, + pdTRUE, + dolphin, + dolphin_butthurt_timer_callback); dolphin->flush_timer = xTimerCreate(NULL, 30 * 1000, pdFALSE, dolphin, dolphin_flush_timer_callback); dolphin->clear_limits_timer = xTimerCreate( @@ -167,6 +160,12 @@ static void dolphin_update_clear_limits_timer_period(Dolphin* dolphin) { int32_t dolphin_srv(void* p) { UNUSED(p); + + if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) { + FURI_LOG_W(TAG, "Skipping start in special boot mode"); + return 0; + } + Dolphin* dolphin = dolphin_alloc(); furi_record_create(RECORD_DOLPHIN, dolphin); diff --git a/applications/services/dolphin/dolphin.h b/applications/services/dolphin/dolphin.h index e8fc12dd3..8757e2a37 100644 --- a/applications/services/dolphin/dolphin.h +++ b/applications/services/dolphin/dolphin.h @@ -44,10 +44,6 @@ void dolphin_deed(Dolphin* dolphin, DolphinDeed deed); */ DolphinStats dolphin_stats(Dolphin* dolphin); -/** GET RANDOM 3PT DEED - */ -DolphinDeed getRandomDeed(); - /** Flush dolphin queue and save state * Thread safe, blocking */ diff --git a/applications/services/dolphin/helpers/dolphin_deed.c b/applications/services/dolphin/helpers/dolphin_deed.c index 4b9f11599..2cbaf84a6 100644 --- a/applications/services/dolphin/helpers/dolphin_deed.c +++ b/applications/services/dolphin/helpers/dolphin_deed.c @@ -40,8 +40,6 @@ static const DolphinDeedWeight dolphin_deed_weights[] = { {1, DolphinAppPlugin}, // DolphinDeedGpioUartBridge {1, DolphinAppPlugin}, // DolphinDeedPluginStart - {1, DolphinAppPlugin}, // DolphinDeedPluginGameStart - {10, DolphinAppPlugin}, // DolphinDeedPluginGameWin }; static uint8_t dolphin_deed_limits[] = { diff --git a/applications/services/dolphin/helpers/dolphin_deed.h b/applications/services/dolphin/helpers/dolphin_deed.h index 51adf6b20..d81166417 100644 --- a/applications/services/dolphin/helpers/dolphin_deed.h +++ b/applications/services/dolphin/helpers/dolphin_deed.h @@ -56,8 +56,6 @@ typedef enum { DolphinDeedGpioUartBridge, DolphinDeedPluginStart, - DolphinDeedPluginGameStart, - DolphinDeedPluginGameWin, DolphinDeedMAX, diff --git a/applications/services/dolphin/helpers/dolphin_state.c b/applications/services/dolphin/helpers/dolphin_state.c index 419245a7c..31f9d4030 100644 --- a/applications/services/dolphin/helpers/dolphin_state.c +++ b/applications/services/dolphin/helpers/dolphin_state.c @@ -170,7 +170,7 @@ void dolphin_state_on_deed(DolphinState* dolphin_state, DolphinDeed deed) { int32_t new_butthurt = ((int32_t)dolphin_state->data.butthurt) - (butthurt_icounter_level_old != butthurt_icounter_level_new); new_butthurt = CLAMP(new_butthurt, BUTTHURT_MAX, BUTTHURT_MIN); - if(new_butthurt >= 7) new_butthurt = BUTTHURT_MIN; // FLIPPER STAYS HAPPY + dolphin_state->data.butthurt = new_butthurt; dolphin_state->data.timestamp = dolphin_state_timestamp(); dolphin_state->dirty = true; diff --git a/applications/services/dolphin/helpers/dolphin_state.h b/applications/services/dolphin/helpers/dolphin_state.h index e0c6af1ef..65cea35ac 100644 --- a/applications/services/dolphin/helpers/dolphin_state.h +++ b/applications/services/dolphin/helpers/dolphin_state.h @@ -5,6 +5,10 @@ #include #include +#ifdef __cplusplus +extern "C" { +#endif + #define DOLPHIN_LEVEL_COUNT 29 typedef struct DolphinState DolphinState; @@ -50,3 +54,7 @@ bool dolphin_state_is_levelup(int icounter); void dolphin_state_increase_level(DolphinState* dolphin_state); uint8_t dolphin_get_level(int icounter); + +#ifdef __cplusplus +} +#endif diff --git a/applications/services/gui/canvas.c b/applications/services/gui/canvas.c index ee472138e..eadbd4834 100644 --- a/applications/services/gui/canvas.c +++ b/applications/services/gui/canvas.c @@ -6,6 +6,7 @@ #include #include #include +#include const CanvasFontParameters canvas_font_params[FontTotalNumber] = { [FontPrimary] = {.leading_default = 12, .leading_min = 11, .height = 8, .descender = 2}, @@ -26,6 +27,8 @@ Canvas* canvas_init() { // Wake up display u8g2_SetPowerSave(&canvas->fb, 0); + canvas_set_orientation(canvas, CanvasOrientationHorizontal); + // Clear buffer and send to device canvas_clear(canvas); canvas_commit(canvas); @@ -58,7 +61,7 @@ uint8_t* canvas_get_buffer(Canvas* canvas) { return u8g2_GetBufferPtr(&canvas->fb); } -size_t canvas_get_buffer_size(Canvas* canvas) { +size_t canvas_get_buffer_size(const Canvas* canvas) { furi_assert(canvas); return u8g2_GetBufferTileWidth(&canvas->fb) * u8g2_GetBufferTileHeight(&canvas->fb) * 8; } @@ -76,17 +79,17 @@ void canvas_frame_set( canvas->height = height; } -uint8_t canvas_width(Canvas* canvas) { +uint8_t canvas_width(const Canvas* canvas) { furi_assert(canvas); return canvas->width; } -uint8_t canvas_height(Canvas* canvas) { +uint8_t canvas_height(const Canvas* canvas) { furi_assert(canvas); return canvas->height; } -uint8_t canvas_current_font_height(Canvas* canvas) { +uint8_t canvas_current_font_height(const Canvas* canvas) { furi_assert(canvas); uint8_t font_height = u8g2_GetMaxCharHeight(&canvas->fb); @@ -97,19 +100,30 @@ uint8_t canvas_current_font_height(Canvas* canvas) { return font_height; } -CanvasFontParameters* canvas_get_font_params(Canvas* canvas, Font font) { +const CanvasFontParameters* canvas_get_font_params(const Canvas* canvas, Font font) { furi_assert(canvas); furi_assert(font < FontTotalNumber); - return (CanvasFontParameters*)&canvas_font_params[font]; + return &canvas_font_params[font]; } void canvas_clear(Canvas* canvas) { furi_assert(canvas); - u8g2_ClearBuffer(&canvas->fb); + if(XTREME_SETTINGS()->dark_mode) { + u8g2_FillBuffer(&canvas->fb); + } else { + u8g2_ClearBuffer(&canvas->fb); + } } void canvas_set_color(Canvas* canvas, Color color) { furi_assert(canvas); + if(XTREME_SETTINGS()->dark_mode) { + if(color == ColorBlack) { + color = ColorWhite; + } else if(color == ColorWhite) { + color = ColorBlack; + } + } u8g2_SetDrawColor(&canvas->fb, color); } @@ -396,6 +410,13 @@ void canvas_set_bitmap_mode(Canvas* canvas, bool alpha) { void canvas_set_orientation(Canvas* canvas, CanvasOrientation orientation) { furi_assert(canvas); + if(XTREME_SETTINGS()->left_handed) { + if(orientation == CanvasOrientationHorizontal) { + orientation = CanvasOrientationHorizontalFlip; + } else if(orientation == CanvasOrientationHorizontalFlip) { + orientation = CanvasOrientationHorizontal; + } + } if(canvas->orientation != orientation) { switch(orientation) { case CanvasOrientationHorizontal: diff --git a/applications/services/gui/canvas.h b/applications/services/gui/canvas.h index 9fb554be2..79ad2bcc5 100644 --- a/applications/services/gui/canvas.h +++ b/applications/services/gui/canvas.h @@ -86,7 +86,7 @@ void canvas_commit(Canvas* canvas); * * @return width in pixels. */ -uint8_t canvas_width(Canvas* canvas); +uint8_t canvas_width(const Canvas* canvas); /** Get Canvas height * @@ -94,7 +94,7 @@ uint8_t canvas_width(Canvas* canvas); * * @return height in pixels. */ -uint8_t canvas_height(Canvas* canvas); +uint8_t canvas_height(const Canvas* canvas); /** Get current font height * @@ -102,7 +102,7 @@ uint8_t canvas_height(Canvas* canvas); * * @return height in pixels. */ -uint8_t canvas_current_font_height(Canvas* canvas); +uint8_t canvas_current_font_height(const Canvas* canvas); /** Get font parameters * @@ -111,7 +111,7 @@ uint8_t canvas_current_font_height(Canvas* canvas); * * @return pointer to CanvasFontParameters structure */ -CanvasFontParameters* canvas_get_font_params(Canvas* canvas, Font font); +const CanvasFontParameters* canvas_get_font_params(const Canvas* canvas, Font font); /** Clear canvas * @@ -363,7 +363,7 @@ void canvas_draw_rframe( uint8_t height, uint8_t radius); -/** Draw rounded-corner box of width, height at x,y, with round value raduis +/** Draw rounded-corner box of width, height at x,y, with round value radius * * @param canvas Canvas instance * @param x x coordinate diff --git a/applications/services/gui/canvas_i.h b/applications/services/gui/canvas_i.h index 3aa1b1d07..745599c95 100644 --- a/applications/services/gui/canvas_i.h +++ b/applications/services/gui/canvas_i.h @@ -49,7 +49,7 @@ uint8_t* canvas_get_buffer(Canvas* canvas); * * @return size of canvas in bytes */ -size_t canvas_get_buffer_size(Canvas* canvas); +size_t canvas_get_buffer_size(const Canvas* canvas); /** Set drawing region relative to real screen buffer * diff --git a/applications/services/gui/elements.c b/applications/services/gui/elements.c index 54c36af76..90682d68e 100644 --- a/applications/services/gui/elements.c +++ b/applications/services/gui/elements.c @@ -635,11 +635,11 @@ void elements_text_box( ElementTextBoxLine line[ELEMENTS_MAX_LINES_NUM]; bool bold = false; bool mono = false; - bool inversed = false; - bool inversed_present = false; + bool inverse = false; + bool inverse_present = false; Font current_font = FontSecondary; Font prev_font = FontSecondary; - CanvasFontParameters* font_params = canvas_get_font_params(canvas, current_font); + const CanvasFontParameters* font_params = canvas_get_font_params(canvas, current_font); // Fill line parameters uint8_t line_leading_min = font_params->leading_min; @@ -692,8 +692,8 @@ void elements_text_box( canvas_set_font(canvas, FontKeyboard); mono = !mono; } - if(text[i] == ELEMENTS_INVERSED_MARKER) { - inversed_present = true; + if(text[i] == ELEMENTS_INVERSE_MARKER) { + inverse_present = true; } continue; } @@ -709,10 +709,10 @@ void elements_text_box( if(text[i] == '\0') { full_text_processed = true; } - if(inversed_present) { + if(inverse_present) { line_leading_min += 1; line_leading_default += 1; - inversed_present = false; + inverse_present = false; } line[line_num].leading_min = line_leading_min; line[line_num].leading_default = line_leading_default; @@ -775,7 +775,7 @@ void elements_text_box( canvas_set_font(canvas, FontSecondary); bold = false; mono = false; - inversed = false; + inverse = false; for(uint8_t i = 0; i < line_num; i++) { for(uint8_t j = 0; j < line[i].len; j++) { // Process format symbols @@ -799,11 +799,11 @@ void elements_text_box( mono = !mono; continue; } - if(line[i].text[j] == ELEMENTS_INVERSED_MARKER) { - inversed = !inversed; + if(line[i].text[j] == ELEMENTS_INVERSE_MARKER) { + inverse = !inverse; continue; } - if(inversed) { + if(inverse) { canvas_draw_box( canvas, line[i].x - 1, diff --git a/applications/services/gui/elements.h b/applications/services/gui/elements.h index 485335131..04ca357b8 100644 --- a/applications/services/gui/elements.h +++ b/applications/services/gui/elements.h @@ -19,7 +19,7 @@ extern "C" { #define ELEMENTS_MAX_LINES_NUM (7) #define ELEMENTS_BOLD_MARKER '#' #define ELEMENTS_MONO_MARKER '*' -#define ELEMENTS_INVERSED_MARKER '!' +#define ELEMENTS_INVERSE_MARKER '!' /** Draw progress bar. * @@ -240,7 +240,7 @@ void elements_scrollable_text_line( * @param[in] text Formatted text. The following formats are available: * "\e#Bold text\e#" - bold font is used * "\e*Monospaced text\e*" - monospaced font is used - * "\e!Inversed text\e!" - white text on black background + * "\e!Inverted text\e!" - white text on black background * @param strip_to_dots Strip text to ... if does not fit to width */ void elements_text_box( diff --git a/applications/services/gui/gui.c b/applications/services/gui/gui.c index 3eadb41f9..835bca2c8 100644 --- a/applications/services/gui/gui.c +++ b/applications/services/gui/gui.c @@ -99,7 +99,11 @@ static void gui_redraw_status_bar(Gui* gui, bool need_attention) { if(xtreme_settings->bar_borders) { canvas_set_color(gui->canvas, ColorWhite); canvas_draw_box( - gui->canvas, -1, 0, canvas_width(gui->canvas) + 1, canvas_height(gui->canvas)); + gui->canvas, + -1, + 0, + canvas_width(gui->canvas) + 1, + canvas_height(gui->canvas)); } canvas_set_color(gui->canvas, ColorBlack); // ViewPort draw @@ -497,7 +501,7 @@ void gui_remove_framebuffer_callback(Gui* gui, GuiCanvasCommitCallback callback, gui_unlock(gui); } -size_t gui_get_framebuffer_size(Gui* gui) { +size_t gui_get_framebuffer_size(const Gui* gui) { furi_assert(gui); return canvas_get_buffer_size(gui->canvas); } diff --git a/applications/services/gui/gui.h b/applications/services/gui/gui.h index 4e7fc2e4d..d7d73f27b 100644 --- a/applications/services/gui/gui.h +++ b/applications/services/gui/gui.h @@ -94,7 +94,7 @@ void gui_remove_framebuffer_callback(Gui* gui, GuiCanvasCommitCallback callback, * @param gui Gui instance * @return size_t size of frame buffer in bytes */ -size_t gui_get_framebuffer_size(Gui* gui); +size_t gui_get_framebuffer_size(const Gui* gui); /** Set lockdown mode * diff --git a/applications/services/gui/icon_animation.c b/applications/services/gui/icon_animation.c index 48c862208..b63d233f3 100644 --- a/applications/services/gui/icon_animation.c +++ b/applications/services/gui/icon_animation.c @@ -29,7 +29,7 @@ void icon_animation_set_update_callback( instance->callback_context = context; } -const uint8_t* icon_animation_get_data(IconAnimation* instance) { +const uint8_t* icon_animation_get_data(const IconAnimation* instance) { return instance->icon->frames[instance->frame]; } @@ -51,12 +51,12 @@ void icon_animation_timer_callback(void* context) { } } -uint8_t icon_animation_get_width(IconAnimation* instance) { +uint8_t icon_animation_get_width(const IconAnimation* instance) { furi_assert(instance); return instance->icon->width; } -uint8_t icon_animation_get_height(IconAnimation* instance) { +uint8_t icon_animation_get_height(const IconAnimation* instance) { furi_assert(instance); return instance->icon->height; } @@ -83,7 +83,7 @@ void icon_animation_stop(IconAnimation* instance) { } } -bool icon_animation_is_last_frame(IconAnimation* instance) { +bool icon_animation_is_last_frame(const IconAnimation* instance) { furi_assert(instance); return instance->icon->frame_count - instance->frame <= 1; } diff --git a/applications/services/gui/icon_animation.h b/applications/services/gui/icon_animation.h index 684790353..6e58df31d 100644 --- a/applications/services/gui/icon_animation.h +++ b/applications/services/gui/icon_animation.h @@ -55,7 +55,7 @@ void icon_animation_set_update_callback( * * @return width in pixels */ -uint8_t icon_animation_get_width(IconAnimation* instance); +uint8_t icon_animation_get_width(const IconAnimation* instance); /** Get icon animation height * @@ -63,7 +63,7 @@ uint8_t icon_animation_get_width(IconAnimation* instance); * * @return height in pixels */ -uint8_t icon_animation_get_height(IconAnimation* instance); +uint8_t icon_animation_get_height(const IconAnimation* instance); /** Start icon animation * @@ -83,7 +83,7 @@ void icon_animation_stop(IconAnimation* instance); * * @return true if last frame */ -bool icon_animation_is_last_frame(IconAnimation* instance); +bool icon_animation_is_last_frame(const IconAnimation* instance); #ifdef __cplusplus } diff --git a/applications/services/gui/icon_animation_i.h b/applications/services/gui/icon_animation_i.h index 4053a120d..62858d288 100644 --- a/applications/services/gui/icon_animation_i.h +++ b/applications/services/gui/icon_animation_i.h @@ -24,7 +24,7 @@ struct IconAnimation { * * @return pointer to current frame XBM bitmap data */ -const uint8_t* icon_animation_get_data(IconAnimation* instance); +const uint8_t* icon_animation_get_data(const IconAnimation* instance); /** Advance to next frame * diff --git a/applications/services/gui/modules/button_panel.h b/applications/services/gui/modules/button_panel.h index e74f7bc3b..4733b4695 100644 --- a/applications/services/gui/modules/button_panel.h +++ b/applications/services/gui/modules/button_panel.h @@ -61,7 +61,7 @@ void button_panel_reserve(ButtonPanel* button_panel, size_t reserve_x, size_t re * @param matrix_place_x coordinates by x-axis on virtual grid, it * is only used for navigation * @param matrix_place_y coordinates by y-axis on virtual grid, it - * is only used for naviagation + * is only used for navigation * @param x x-coordinate to draw icon on * @param y y-coordinate to draw icon on * @param icon_name name of the icon to draw diff --git a/applications/services/gui/modules/file_browser.c b/applications/services/gui/modules/file_browser.c index 5477192a0..841afda50 100644 --- a/applications/services/gui/modules/file_browser.c +++ b/applications/services/gui/modules/file_browser.c @@ -91,7 +91,7 @@ static int BrowserItem_t_cmp(const BrowserItem_t* a, const BrowserItem_t* b) { if(b->type == BrowserItemTypeBack) { return 1; } - if(!XTREME_SETTINGS()->sort_ignore_dirs) { + if(XTREME_SETTINGS()->sort_dirs_first) { if(a->type == BrowserItemTypeFolder && b->type != BrowserItemTypeFolder) { return -1; } diff --git a/applications/services/gui/modules/file_browser_worker.c b/applications/services/gui/modules/file_browser_worker.c index 9d219429b..326040020 100644 --- a/applications/services/gui/modules/file_browser_worker.c +++ b/applications/services/gui/modules/file_browser_worker.c @@ -60,7 +60,7 @@ static bool browser_path_is_file(FuriString* path) { FileInfo file_info; Storage* storage = furi_record_open(RECORD_STORAGE); if(storage_common_stat(storage, furi_string_get_cstr(path), &file_info) == FSE_OK) { - if((file_info.flags & FSF_DIRECTORY) == 0) { + if(!file_info_is_dir(&file_info)) { state = true; } } @@ -119,7 +119,7 @@ static bool browser_folder_check_and_switch(FuriString* path) { while(1) { // Check if folder is existing and navigate back if not if(storage_common_stat(storage, furi_string_get_cstr(path), &file_info) == FSE_OK) { - if(file_info.flags & FSF_DIRECTORY) { + if(file_info_is_dir(&file_info)) { break; } } @@ -161,7 +161,7 @@ static bool browser_folder_init( if((storage_file_get_error(directory) == FSE_OK) && (name_temp[0] != '\0')) { total_files_cnt++; furi_string_set(name_str, name_temp); - if(browser_filter_by_name(browser, name_str, (file_info.flags & FSF_DIRECTORY))) { + if(browser_filter_by_name(browser, name_str, file_info_is_dir(&file_info))) { if(!furi_string_empty(filename)) { if(furi_string_cmp(name_str, filename) == 0) { *file_idx = *item_cnt; @@ -218,7 +218,7 @@ static bool browser_folder_load_chunked( } if(storage_file_get_error(directory) == FSE_OK) { furi_string_set(name_str, name_temp); - if(browser_filter_by_name(browser, name_str, (file_info.flags & FSF_DIRECTORY))) { + if(browser_filter_by_name(browser, name_str, file_info_is_dir(&file_info))) { items_cnt++; } } else { @@ -240,14 +240,14 @@ static bool browser_folder_load_chunked( } if(storage_file_get_error(directory) == FSE_OK) { furi_string_set(name_str, name_temp); - if(browser_filter_by_name(browser, name_str, (file_info.flags & FSF_DIRECTORY))) { + if(browser_filter_by_name(browser, name_str, file_info_is_dir(&file_info))) { furi_string_printf(name_str, "%s/%s", furi_string_get_cstr(path), name_temp); if(browser->list_item_cb) { browser->list_item_cb( browser->cb_ctx, name_str, items_cnt, - (file_info.flags & FSF_DIRECTORY), + file_info_is_dir(&file_info), false); } items_cnt++; @@ -295,15 +295,11 @@ static bool browser_folder_load_full(BrowserWorker* browser, FuriString* path) { while(storage_dir_read(directory, &file_info, name_temp, FILE_NAME_LEN_MAX) && storage_file_get_error(directory) == FSE_OK) { furi_string_set(name_str, name_temp); - if(browser_filter_by_name(browser, name_str, (file_info.flags & FSF_DIRECTORY))) { + if(browser_filter_by_name(browser, name_str, file_info_is_dir(&file_info))) { furi_string_printf(name_str, "%s/%s", furi_string_get_cstr(path), name_temp); if(browser->list_item_cb) { browser->list_item_cb( - browser->cb_ctx, - name_str, - items_cnt, - (file_info.flags & FSF_DIRECTORY), - false); + browser->cb_ctx, name_str, items_cnt, file_info_is_dir(&file_info), false); } items_cnt++; } diff --git a/applications/services/gui/modules/text_input.c b/applications/services/gui/modules/text_input.c index 9004304cb..f39adcd9c 100644 --- a/applications/services/gui/modules/text_input.c +++ b/applications/services/gui/modules/text_input.c @@ -14,6 +14,11 @@ typedef struct { const uint8_t y; } TextInputKey; +typedef struct { + const TextInputKey* rows[3]; + const uint8_t keyboard_index; +} Keyboard; + typedef struct { const char* header; char* text_buffer; @@ -25,6 +30,7 @@ typedef struct { uint8_t selected_row; uint8_t selected_column; + uint8_t selected_keyboard; TextInputValidatorCallback validator_callback; void* validator_callback_context; @@ -35,9 +41,11 @@ typedef struct { static const uint8_t keyboard_origin_x = 1; static const uint8_t keyboard_origin_y = 29; static const uint8_t keyboard_row_count = 3; +static const uint8_t keyboard_count = 2; #define ENTER_KEY '\r' #define BACKSPACE_KEY '\b' +#define SWITCH_KEYBOARD_KEY 0xfe static const TextInputKey keyboard_keys_row_1[] = { {'q', 1, 8}, @@ -73,54 +81,139 @@ static const TextInputKey keyboard_keys_row_2[] = { }; static const TextInputKey keyboard_keys_row_3[] = { - {'z', 1, 32}, - {'x', 10, 32}, - {'c', 19, 32}, - {'v', 28, 32}, - {'b', 37, 32}, - {'n', 46, 32}, - {'m', 55, 32}, - {'_', 64, 32}, + {SWITCH_KEYBOARD_KEY, 1, 23}, + {'z', 13, 32}, + {'x', 21, 32}, + {'c', 28, 32}, + {'v', 36, 32}, + {'b', 44, 32}, + {'n', 52, 32}, + {'m', 59, 32}, + {'_', 67, 32}, {ENTER_KEY, 74, 23}, {'7', 100, 32}, {'8', 110, 32}, {'9', 120, 32}, }; -static uint8_t get_row_size(uint8_t row_index) { - uint8_t row_size = 0; +static const TextInputKey symbol_keyboard_keys_row_1[] = { + {'!', 1, 8}, + {'"', 10, 8}, + {'#', 19, 8}, + {'$', 28, 8}, + {'%', 37, 8}, + {'&', 46, 8}, + {'/', 55, 8}, + {'(', 64, 8}, + {')', 73, 8}, + {'=', 82, 8}, + {'0', 91, 8}, + {'1', 100, 8}, + {'2', 110, 8}, + {'3', 120, 8}, +}; - switch(row_index + 1) { - case 1: - row_size = COUNT_OF(keyboard_keys_row_1); - break; - case 2: - row_size = COUNT_OF(keyboard_keys_row_2); - break; - case 3: - row_size = COUNT_OF(keyboard_keys_row_3); - break; - default: - furi_crash(NULL); +static const TextInputKey symbol_keyboard_keys_row_2[] = { + {'{', 1, 20}, + {'}', 10, 20}, + {'[', 19, 20}, + {']', 28, 20}, + {'<', 37, 20}, + {'>', 46, 20}, + {'\\', 55, 20}, + {'@', 64, 20}, + {'?', 73, 20}, + {BACKSPACE_KEY, 82, 12}, + {'4', 100, 20}, + {'5', 110, 20}, + {'6', 120, 20}, +}; + +static const TextInputKey symbol_keyboard_keys_row_3[] = { + {SWITCH_KEYBOARD_KEY, 1, 23}, + {'+', 13, 32}, + {'`', 21, 32}, + {'\'', 28, 32}, + {'^', 36, 32}, + {'*', 44, 32}, + {',', 52, 32}, + {'.', 59, 32}, + {'-', 67, 32}, + {ENTER_KEY, 74, 23}, + {'7', 100, 32}, + {'8', 110, 32}, + {'9', 120, 32}, +}; + +static const Keyboard keyboard = { + .rows = + { + keyboard_keys_row_1, + keyboard_keys_row_2, + keyboard_keys_row_3, + }, + .keyboard_index = 0, +}; + +static const Keyboard symbol_keyboard = { + .rows = + { + symbol_keyboard_keys_row_1, + symbol_keyboard_keys_row_2, + symbol_keyboard_keys_row_3, + }, + .keyboard_index = 1, +}; + +static const Keyboard* keyboards[] = { + &keyboard, + &symbol_keyboard, +}; + +static void switch_keyboard(TextInputModel* model) { + model->selected_keyboard = (model->selected_keyboard + 1) % keyboard_count; +} + +static uint8_t get_row_size(const Keyboard* keyboard, uint8_t row_index) { + uint8_t row_size = 0; + if(keyboard == &symbol_keyboard) { + switch(row_index + 1) { + case 1: + row_size = COUNT_OF(symbol_keyboard_keys_row_1); + break; + case 2: + row_size = COUNT_OF(symbol_keyboard_keys_row_2); + break; + case 3: + row_size = COUNT_OF(symbol_keyboard_keys_row_3); + break; + default: + furi_crash(NULL); + } + } else { + switch(row_index + 1) { + case 1: + row_size = COUNT_OF(keyboard_keys_row_1); + break; + case 2: + row_size = COUNT_OF(keyboard_keys_row_2); + break; + case 3: + row_size = COUNT_OF(keyboard_keys_row_3); + break; + default: + furi_crash(NULL); + } } return row_size; } -static const TextInputKey* get_row(uint8_t row_index) { +static const TextInputKey* get_row(const Keyboard* keyboard, uint8_t row_index) { const TextInputKey* row = NULL; - - switch(row_index + 1) { - case 1: - row = keyboard_keys_row_1; - break; - case 2: - row = keyboard_keys_row_2; - break; - case 3: - row = keyboard_keys_row_3; - break; - default: + if(row_index < 3) { + row = keyboard->rows[row_index]; + } else { furi_crash(NULL); } @@ -128,7 +221,9 @@ static const TextInputKey* get_row(uint8_t row_index) { } static char get_selected_char(TextInputModel* model) { - return get_row(model->selected_row)[model->selected_column].text; + return get_row( + keyboards[model->selected_keyboard], model->selected_row)[model->selected_column] + .text; } static bool char_is_lowercase(char letter) { @@ -138,7 +233,7 @@ static bool char_is_lowercase(char letter) { static char char_to_uppercase(const char letter) { if(letter == '_') { return 0x20; - } else if(isalpha(letter)) { + } else if(char_is_lowercase(letter)) { return (letter - 0x20); } else { return letter; @@ -189,8 +284,8 @@ 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++) { - const uint8_t column_count = get_row_size(row); - const TextInputKey* keys = get_row(row); + const uint8_t column_count = get_row_size(keyboards[model->selected_keyboard], row); + const TextInputKey* keys = get_row(keyboards[model->selected_keyboard], row); for(size_t column = 0; column < column_count; column++) { if(keys[column].text == ENTER_KEY) { @@ -208,6 +303,21 @@ static void text_input_view_draw_callback(Canvas* canvas, void* _model) { keyboard_origin_y + keys[column].y, &I_KeySave_24x11); } + } else if(keys[column].text == SWITCH_KEYBOARD_KEY) { + canvas_set_color(canvas, ColorBlack); + if(model->selected_row == row && model->selected_column == column) { + canvas_draw_icon( + canvas, + keyboard_origin_x + keys[column].x, + keyboard_origin_y + keys[column].y, + &I_KeyKeyboardSelected_10x11); + } else { + canvas_draw_icon( + canvas, + keyboard_origin_x + keys[column].x, + keyboard_origin_y + keys[column].y, + &I_KeyKeyboard_10x11); + } } else if(keys[column].text == BACKSPACE_KEY) { canvas_set_color(canvas, ColorBlack); if(model->selected_row == row && model->selected_column == column) { @@ -237,8 +347,7 @@ static void text_input_view_draw_callback(Canvas* canvas, void* _model) { canvas_set_color(canvas, ColorBlack); } - if(model->clear_default_text || - (text_length == 0 && char_is_lowercase(keys[column].text))) { + if(model->clear_default_text || text_length == 0) { canvas_draw_glyph( canvas, keyboard_origin_x + keys[column].x, @@ -271,7 +380,8 @@ static void text_input_handle_up(TextInput* text_input, TextInputModel* model) { UNUSED(text_input); if(model->selected_row > 0) { model->selected_row--; - if(model->selected_column > get_row_size(model->selected_row) - 6) { + if(model->selected_column > + get_row_size(keyboards[model->selected_keyboard], model->selected_row) - 6) { model->selected_column = model->selected_column + 1; } } @@ -281,7 +391,8 @@ static void text_input_handle_down(TextInput* text_input, TextInputModel* model) UNUSED(text_input); if(model->selected_row < keyboard_row_count - 1) { model->selected_row++; - if(model->selected_column > get_row_size(model->selected_row) - 4) { + if(model->selected_column > + get_row_size(keyboards[model->selected_keyboard], model->selected_row) - 4) { model->selected_column = model->selected_column - 1; } } @@ -292,27 +403,27 @@ static void text_input_handle_left(TextInput* text_input, TextInputModel* model) if(model->selected_column > 0) { model->selected_column--; } else { - model->selected_column = get_row_size(model->selected_row) - 1; + model->selected_column = + get_row_size(keyboards[model->selected_keyboard], model->selected_row) - 1; } } static void text_input_handle_right(TextInput* text_input, TextInputModel* model) { UNUSED(text_input); - if(model->selected_column < get_row_size(model->selected_row) - 1) { + if(model->selected_column < + get_row_size(keyboards[model->selected_keyboard], model->selected_row) - 1) { model->selected_column++; } else { model->selected_column = 0; } } -static void text_input_handle_ok(TextInput* text_input, TextInputModel* model, bool shift) { +static void text_input_handle_ok(TextInput* text_input, TextInputModel* model, InputType type) { + bool shift = type == InputTypeLong; + bool repeat = type == InputTypeRepeat; char selected = get_selected_char(model); size_t text_length = strlen(model->text_buffer); - if(shift) { - selected = char_to_uppercase(selected); - } - if(selected == ENTER_KEY) { if(model->validator_callback && (!model->validator_callback( @@ -322,21 +433,25 @@ static void text_input_handle_ok(TextInput* text_input, TextInputModel* model, b } else if(model->callback != 0 && text_length > 0) { model->callback(model->callback_context); } - } else if(selected == BACKSPACE_KEY) { - text_input_backspace_cb(model); + } else if(selected == SWITCH_KEYBOARD_KEY) { + switch_keyboard(model); } else { - if(model->clear_default_text) { - text_length = 0; - } - if(text_length < (model->text_buffer_size - 1)) { - if(text_length == 0 && char_is_lowercase(selected)) { - selected = char_to_uppercase(selected); + if(selected == BACKSPACE_KEY) { + text_input_backspace_cb(model); + } else if(!repeat) { + if(model->clear_default_text) { + text_length = 0; + } + if(text_length < (model->text_buffer_size - 1)) { + if(shift != (text_length == 0)) { + selected = char_to_uppercase(selected); + } + model->text_buffer[text_length] = selected; + model->text_buffer[text_length + 1] = 0; } - model->text_buffer[text_length] = selected; - model->text_buffer[text_length + 1] = 0; } + model->clear_default_text = false; } - model->clear_default_text = false; } static bool text_input_view_input_callback(InputEvent* event, void* context) { @@ -368,7 +483,7 @@ static bool text_input_view_input_callback(InputEvent* event, void* context) { text_input_handle_right(text_input, model); break; case InputKeyOk: - text_input_handle_ok(text_input, model, false); + text_input_handle_ok(text_input, model, event->type); break; default: consumed = false; @@ -390,7 +505,7 @@ static bool text_input_view_input_callback(InputEvent* event, void* context) { text_input_handle_right(text_input, model); break; case InputKeyOk: - text_input_handle_ok(text_input, model, true); + text_input_handle_ok(text_input, model, event->type); break; case InputKeyBack: text_input_backspace_cb(model); @@ -414,6 +529,9 @@ static bool text_input_view_input_callback(InputEvent* event, void* context) { case InputKeyRight: text_input_handle_right(text_input, model); break; + case InputKeyOk: + text_input_handle_ok(text_input, model, event->type); + break; case InputKeyBack: text_input_backspace_cb(model); break; @@ -488,6 +606,7 @@ void text_input_reset(TextInput* text_input) { model->header = ""; model->selected_row = 0; model->selected_column = 0; + model->selected_keyboard = 0; model->clear_default_text = false; model->text_buffer = NULL; model->text_buffer_size = 0; @@ -525,7 +644,8 @@ void text_input_set_result_callback( if(text_buffer && text_buffer[0] != '\0') { // Set focus on Save model->selected_row = 2; - model->selected_column = 8; + model->selected_column = 9; + model->selected_keyboard = 0; } }, true); @@ -568,4 +688,4 @@ void* text_input_get_validator_callback_context(TextInput* text_input) { void text_input_set_header_text(TextInput* text_input, const char* text) { with_view_model( text_input->view, TextInputModel * model, { model->header = text; }, true); -} \ No newline at end of file +} diff --git a/applications/services/gui/modules/widget.h b/applications/services/gui/modules/widget.h index 9076ce7f2..f86b14cc9 100644 --- a/applications/services/gui/modules/widget.h +++ b/applications/services/gui/modules/widget.h @@ -91,7 +91,7 @@ void widget_add_string_element( * @param[in] text Formatted text. The following formats are available: * "\e#Bold text\e#" - bold font is used * "\e*Monospaced text\e*" - monospaced font is used - * "\e!Inversed text\e!" - white text on black background + * "\e!Inverted text\e!" - white text on black background * @param strip_to_dots Strip text to ... if does not fit to width */ void widget_add_text_box_element( @@ -115,6 +115,7 @@ void widget_add_text_box_element( * @param[in] text Formatted text. Default format: align left, Secondary font. * The following formats are available: * "\e#Bold text" - sets bold font before until next '\n' symbol + * "\e*Monospaced text\e*" - sets monospaced font before until next '\n' symbol * "\ecCenter-aligned text" - sets center horizontal align until the next '\n' symbol * "\erRight-aligned text" - sets right horizontal align until the next '\n' symbol */ 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 d8fc11311..4c9c39dff 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 @@ -37,6 +37,8 @@ static bool line->horizontal = AlignRight; } else if(ctrl_symbol == '#') { line->font = FontPrimary; + } else if(ctrl_symbol == '*') { + line->font = FontKeyboard; } furi_string_right(text, 2); processed = true; @@ -74,7 +76,7 @@ static void widget_element_text_scroll_fill_lines(Canvas* canvas, WidgetElement* } // Set canvas font canvas_set_font(canvas, line_tmp.font); - CanvasFontParameters* params = canvas_get_font_params(canvas, line_tmp.font); + const CanvasFontParameters* params = canvas_get_font_params(canvas, line_tmp.font); total_height += params->height; if(total_height > model->height) { model->scroll_pos_total++; @@ -138,7 +140,7 @@ static void widget_element_text_scroll_draw(Canvas* canvas, WidgetElement* eleme TextScrollLineArray_next(it), curr_line++) { if(curr_line < model->scroll_pos_current) continue; TextScrollLineArray* line = TextScrollLineArray_ref(it); - CanvasFontParameters* params = canvas_get_font_params(canvas, line->font); + const CanvasFontParameters* params = canvas_get_font_params(canvas, line->font); if(y + params->descender > model->y + model->height) break; canvas_set_font(canvas, line->font); if(line->horizontal == AlignLeft) { diff --git a/applications/services/gui/scene_manager.c b/applications/services/gui/scene_manager.c index 590145e1e..4122b3bc3 100644 --- a/applications/services/gui/scene_manager.c +++ b/applications/services/gui/scene_manager.c @@ -10,7 +10,7 @@ SceneManager* scene_manager_alloc(const SceneManagerHandlers* app_scene_handlers scene_manager->scene_handlers = app_scene_handlers; // Allocate all scenes scene_manager->scene = malloc(sizeof(AppScene) * app_scene_handlers->scene_num); - // Initialize ScaneManager array for navigation + // Initialize SceneManager array for navigation SceneManagerIdStack_init(scene_manager->scene_id_stack); return scene_manager; @@ -19,7 +19,7 @@ SceneManager* scene_manager_alloc(const SceneManagerHandlers* app_scene_handlers void scene_manager_free(SceneManager* scene_manager) { furi_assert(scene_manager); - // Clear ScaneManager array + // Clear SceneManager array SceneManagerIdStack_clear(scene_manager->scene_id_stack); // Clear allocated scenes free(scene_manager->scene); @@ -34,7 +34,7 @@ void scene_manager_set_scene_state(SceneManager* scene_manager, uint32_t scene_i scene_manager->scene[scene_id].state = state; } -uint32_t scene_manager_get_scene_state(SceneManager* scene_manager, uint32_t scene_id) { +uint32_t scene_manager_get_scene_state(const SceneManager* scene_manager, uint32_t scene_id) { furi_assert(scene_manager); furi_assert(scene_id < scene_manager->scene_handlers->scene_num); @@ -184,7 +184,7 @@ bool scene_manager_search_and_switch_to_previous_scene_one_of( return scene_found; } -bool scene_manager_has_previous_scene(SceneManager* scene_manager, uint32_t scene_id) { +bool scene_manager_has_previous_scene(const SceneManager* scene_manager, uint32_t scene_id) { furi_assert(scene_manager); bool scene_found = false; diff --git a/applications/services/gui/scene_manager.h b/applications/services/gui/scene_manager.h index 5b833e5de..c349a12ce 100644 --- a/applications/services/gui/scene_manager.h +++ b/applications/services/gui/scene_manager.h @@ -63,7 +63,7 @@ void scene_manager_set_scene_state(SceneManager* scene_manager, uint32_t scene_i * * @return Scene state */ -uint32_t scene_manager_get_scene_state(SceneManager* scene_manager, uint32_t scene_id); +uint32_t scene_manager_get_scene_state(const SceneManager* scene_manager, uint32_t scene_id); /** Scene Manager allocation and configuration * @@ -134,7 +134,7 @@ bool scene_manager_previous_scene(SceneManager* scene_manager); * * @return true if previous scene was found, false otherwise */ -bool scene_manager_has_previous_scene(SceneManager* scene_manager, uint32_t scene_id); +bool scene_manager_has_previous_scene(const SceneManager* scene_manager, uint32_t scene_id); /** Search and switch to previous Scene * diff --git a/applications/services/gui/view_port.c b/applications/services/gui/view_port.c index ffd01450b..3e0889fbf 100644 --- a/applications/services/gui/view_port.c +++ b/applications/services/gui/view_port.c @@ -1,4 +1,5 @@ #include "view_port_i.h" +#include "xtreme/settings.h" #include @@ -51,6 +52,27 @@ static const InputKey view_port_input_mapping[ViewPortOrientationMAX][InputKeyMA // Remaps directional pad buttons on Flipper based on ViewPort orientation static void view_port_map_input(InputEvent* event, ViewPortOrientation orientation) { furi_assert(orientation < ViewPortOrientationMAX && event->key < InputKeyMAX); + if(orientation == ViewPortOrientationHorizontal || + orientation == ViewPortOrientationHorizontalFlip) { + if(XTREME_SETTINGS()->left_handed) { + switch(event->key) { + case InputKeyUp: + event->key = InputKeyDown; + break; + case InputKeyDown: + event->key = InputKeyUp; + break; + case InputKeyLeft: + event->key = InputKeyRight; + break; + case InputKeyRight: + event->key = InputKeyLeft; + break; + default: + break; + } + } + } event->key = view_port_input_mapping[orientation][event->key]; } @@ -89,7 +111,7 @@ void view_port_set_width(ViewPort* view_port, uint8_t width) { view_port->width = width; } -uint8_t view_port_get_width(ViewPort* view_port) { +uint8_t view_port_get_width(const ViewPort* view_port) { furi_assert(view_port); return view_port->width; } @@ -99,7 +121,7 @@ void view_port_set_height(ViewPort* view_port, uint8_t height) { view_port->height = height; } -uint8_t view_port_get_height(ViewPort* view_port) { +uint8_t view_port_get_height(const ViewPort* view_port) { furi_assert(view_port); return view_port->height; } @@ -112,7 +134,7 @@ void view_port_enabled_set(ViewPort* view_port, bool enabled) { } } -bool view_port_is_enabled(ViewPort* view_port) { +bool view_port_is_enabled(const ViewPort* view_port) { furi_assert(view_port); return view_port->is_enabled; } diff --git a/applications/services/gui/view_port.h b/applications/services/gui/view_port.h index 703e99248..752fc46ba 100644 --- a/applications/services/gui/view_port.h +++ b/applications/services/gui/view_port.h @@ -56,7 +56,7 @@ void view_port_free(ViewPort* view_port); * @param width wanted width, 0 - auto. */ void view_port_set_width(ViewPort* view_port, uint8_t width); -uint8_t view_port_get_width(ViewPort* view_port); +uint8_t view_port_get_width(const ViewPort* view_port); /** Set view_port height. * @@ -66,7 +66,7 @@ uint8_t view_port_get_width(ViewPort* view_port); * @param height wanted height, 0 - auto. */ void view_port_set_height(ViewPort* view_port, uint8_t height); -uint8_t view_port_get_height(ViewPort* view_port); +uint8_t view_port_get_height(const ViewPort* view_port); /** Enable or disable view_port rendering. * @@ -75,7 +75,7 @@ uint8_t view_port_get_height(ViewPort* view_port); * @warning automatically dispatches update event */ void view_port_enabled_set(ViewPort* view_port, bool enabled); -bool view_port_is_enabled(ViewPort* view_port); +bool view_port_is_enabled(const ViewPort* view_port); /** ViewPort event callbacks * diff --git a/applications/services/gui/view_stack.h b/applications/services/gui/view_stack.h index cd62b4f6a..f16f5febc 100644 --- a/applications/services/gui/view_stack.h +++ b/applications/services/gui/view_stack.h @@ -3,7 +3,7 @@ * GUI: ViewStack API * * ViewStack accumulates several Views in one stack. - * Draw callbacks are called sequenctially starting from + * Draw callbacks are called sequentially starting from * first added. Input callbacks are called in reverse order. * Consumed input is not passed on underlying layers. */ diff --git a/applications/services/loader/loader.c b/applications/services/loader/loader.c index f2edf0a33..bcbffcac2 100644 --- a/applications/services/loader/loader.c +++ b/applications/services/loader/loader.c @@ -1,8 +1,12 @@ #include "applications.h" #include +#include #include "loader/loader.h" #include "loader_i.h" -#include "applications/services/desktop/desktop_i.h" +#include "applications/main/fap_loader/fap_loader_app.h" +#include +#include +#include #define TAG "LoaderSrv" @@ -30,6 +34,8 @@ static bool } furi_thread_set_name(loader_instance->application_thread, loader_instance->application->name); + furi_thread_set_appid( + loader_instance->application_thread, loader_instance->application->appid); furi_thread_set_stack_size( loader_instance->application_thread, loader_instance->application->stack_size); furi_thread_set_context( @@ -42,41 +48,6 @@ static bool return true; } -static void loader_menu_callback(void* _ctx, uint32_t index) { - UNUSED(index); - const FlipperApplication* application = _ctx; - - furi_assert(application->app); - furi_assert(application->name); - furi_assert(application->link); - - if(strcmp(application->link, "NULL") != 0) { - loader_start(NULL, "Applications", application->link); - } else { - if(!loader_lock(loader_instance)) { - FURI_LOG_E(TAG, "Loader is locked"); - return; - } - - loader_start_application(application, NULL); - } -} - -static void loader_submenu_callback(void* context, uint32_t index) { - UNUSED(index); - uint32_t view_id = (uint32_t)context; - view_dispatcher_switch_to_view(loader_instance->view_dispatcher, view_id); -} - -static void loader_cli_print_usage() { - printf("Usage:\r\n"); - printf("loader \r\n"); - printf("Cmd list:\r\n"); - printf("\tlist\t - List available applications\r\n"); - printf("\topen \t - Open application by name\r\n"); - printf("\tinfo\t - Show loader state\r\n"); -} - static FlipperApplication const* loader_find_application_by_name_in_list( const char* name, const FlipperApplication* list, @@ -112,6 +83,52 @@ const FlipperApplication* loader_find_application_by_name(const char* name) { return application; } +static void loader_menu_callback(void* _ctx, uint32_t index) { + UNUSED(index); + const FlipperApplication* application = _ctx; + + furi_assert(application->app); + furi_assert(application->name); + + if(!loader_lock(loader_instance)) { + FURI_LOG_E(TAG, "Loader is locked"); + return; + } + + loader_start_application(application, NULL); +} + +static void loader_main_callback(void* _ctx, uint32_t index) { + UNUSED(index); + const char* path = _ctx; + const FlipperApplication* app = loader_find_application_by_name_in_list( + FAP_LOADER_APP_NAME, FLIPPER_APPS, FLIPPER_APPS_COUNT); + + furi_assert(path); + + if(!loader_lock(loader_instance)) { + FURI_LOG_E(TAG, "Loader is locked"); + return; + } + + loader_start_application(app, path); +} + +static void loader_submenu_callback(void* context, uint32_t index) { + UNUSED(index); + uint32_t view_id = (uint32_t)context; + view_dispatcher_switch_to_view(loader_instance->view_dispatcher, view_id); +} + +static void loader_cli_print_usage() { + printf("Usage:\r\n"); + printf("loader \r\n"); + printf("Cmd list:\r\n"); + printf("\tlist\t - List available applications\r\n"); + printf("\topen \t - Open application by name\r\n"); + printf("\tinfo\t - Show loader state\r\n"); +} + static void loader_cli_open(Cli* cli, FuriString* args, Loader* instance) { UNUSED(cli); if(loader_is_locked(instance)) { @@ -162,12 +179,7 @@ static void loader_cli_list(Cli* cli, FuriString* args, Loader* instance) { UNUSED(instance); printf("Applications:\r\n"); for(size_t i = 0; i < FLIPPER_APPS_COUNT; i++) { - if(strcmp(FLIPPER_APPS[i].link, "NULL") != 0) { - printf( - "\tFor %s, Use: Applications %s\r\n", FLIPPER_APPS[i].name, FLIPPER_APPS[i].link); - } else { - printf("\t%s\r\n", FLIPPER_APPS[i].name); - } + printf("\t%s\r\n", FLIPPER_APPS[i].name); } printf("Plugins:\r\n"); @@ -175,7 +187,7 @@ static void loader_cli_list(Cli* cli, FuriString* args, Loader* instance) { printf("\t%s\r\n", FLIPPER_PLUGINS[i].name); } - if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug) && FLIPPER_DEBUG_APPS_COUNT != 0) { + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { printf("Debug:\r\n"); for(size_t i = 0; i < FLIPPER_DEBUG_APPS_COUNT; i++) { printf("\t%s\r\n", FLIPPER_DEBUG_APPS[i].name); @@ -273,7 +285,7 @@ void loader_unlock(Loader* instance) { FURI_CRITICAL_EXIT(); } -bool loader_is_locked(Loader* instance) { +bool loader_is_locked(const Loader* instance) { return instance->lock_count > 0; } @@ -408,6 +420,14 @@ static void loader_free(Loader* instance) { instance = NULL; } +const Icon* loader_get_main_icon(char* name) { + // Temp solution, not sure how this could be easily improved + if(strcmp(name, "xtreme_app.fap") == 0) { + return &A_Xtreme_14; + } + return NULL; +} + static void loader_build_menu() { FURI_LOG_I(TAG, "Building main menu"); size_t i; @@ -420,6 +440,32 @@ static void loader_build_menu() { loader_menu_callback, (void*)&FLIPPER_APPS[i]); } + Storage* storage = furi_record_open(RECORD_STORAGE); + File* folder = storage_file_alloc(storage); + FileInfo info; + char* name = malloc(100); + if(storage_dir_open(folder, EXT_PATH("apps/.Main"))) { + FuriString* path = furi_string_alloc(); + FuriString* appname = furi_string_alloc(); + while(storage_dir_read(folder, &info, name, 100)) { + if(file_info_is_dir(&info)) continue; + furi_string_printf(path, EXT_PATH("apps/.Main/%s"), name); + if(!fap_loader_load_name_and_icon(path, storage, NULL, appname)) continue; + const Icon* icon = loader_get_main_icon(name); + menu_add_item( + loader_instance->primary_menu, + strdup(furi_string_get_cstr(appname)), + icon, + i++, + loader_main_callback, + (void*)strdup(furi_string_get_cstr(path))); + } + furi_string_free(appname); + furi_string_free(path); + } + free(name); + storage_file_free(folder); + furi_record_close(RECORD_STORAGE); if(FLIPPER_PLUGINS_COUNT != 0) { menu_add_item( loader_instance->primary_menu, @@ -429,7 +475,7 @@ static void loader_build_menu() { loader_submenu_callback, (void*)LoaderMenuViewPlugins); } - if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug) && FLIPPER_DEBUG_APPS_COUNT != 0) { + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug) && (FLIPPER_DEBUG_APPS_COUNT > 0)) { menu_add_item( loader_instance->primary_menu, "Debug Tools", @@ -448,9 +494,8 @@ static void loader_build_menu() { } static void loader_build_submenu() { - size_t i; - FURI_LOG_I(TAG, "Building plugins menu"); + size_t i; for(i = 0; i < FLIPPER_PLUGINS_COUNT; i++) { submenu_add_item( loader_instance->plugins_menu, @@ -531,4 +576,4 @@ int32_t loader_srv(void* p) { FuriPubSub* loader_get_pubsub(Loader* instance) { return instance->pubsub; -} \ No newline at end of file +} diff --git a/applications/services/loader/loader.h b/applications/services/loader/loader.h index 954e79c57..2f8203e98 100644 --- a/applications/services/loader/loader.h +++ b/applications/services/loader/loader.h @@ -9,6 +9,8 @@ extern "C" { #define RECORD_LOADER "loader" +#define FAP_LOADER_APP_NAME "Applications" + typedef struct Loader Loader; typedef enum { @@ -43,7 +45,7 @@ bool loader_lock(Loader* instance); void loader_unlock(Loader* instance); /** Get loader lock status */ -bool loader_is_locked(Loader* instance); +bool loader_is_locked(const Loader* instance); /** Show primary loader */ void loader_show_menu(); diff --git a/applications/services/locale/locale.h b/applications/services/locale/locale.h index 5f2a4d87f..61fb4c605 100644 --- a/applications/services/locale/locale.h +++ b/applications/services/locale/locale.h @@ -81,7 +81,7 @@ void locale_format_time( * * @return The Locale DateFormat. */ -LocaleDateFormat locale_get_date_format(); +LocaleDateFormat locale_get_date_format(void); /** Set Locale DateFormat * diff --git a/applications/services/namechangersrv/namechangersrv.c b/applications/services/namechangersrv/namechangersrv.c index e62b39c01..fe25906ae 100644 --- a/applications/services/namechangersrv/namechangersrv.c +++ b/applications/services/namechangersrv/namechangersrv.c @@ -13,16 +13,13 @@ void namechanger_on_system_start() { FuriString* NAMEHEADER; NAMEHEADER = furi_string_alloc_set("Flipper Name File"); - FuriString* filepath; - filepath = furi_string_alloc_set("/ext/dolphin/name.txt"); - bool result = false; FuriString* data; data = furi_string_alloc(); do { - if(!flipper_format_file_open_existing(file, furi_string_get_cstr(filepath))) { + if(!flipper_format_file_open_existing(file, NAMECHANGER_PATH)) { break; } @@ -59,7 +56,7 @@ void namechanger_on_system_start() { do { // Open file for write - if(!flipper_format_file_open_always(file, furi_string_get_cstr(filepath))) { + if(!flipper_format_file_open_always(file, NAMECHANGER_PATH)) { break; } @@ -109,7 +106,7 @@ void namechanger_on_system_start() { do { // Open file for write - if(!flipper_format_file_open_always(file, furi_string_get_cstr(filepath))) { + if(!flipper_format_file_open_always(file, NAMECHANGER_PATH)) { break; } @@ -161,7 +158,6 @@ void namechanger_on_system_start() { furi_string_free(data); - furi_string_free(filepath); furi_record_close(RECORD_STORAGE); } } diff --git a/applications/services/namechangersrv/namechangersrv.h b/applications/services/namechangersrv/namechangersrv.h index c5014355b..d440b19bc 100644 --- a/applications/services/namechangersrv/namechangersrv.h +++ b/applications/services/namechangersrv/namechangersrv.h @@ -5,5 +5,6 @@ #define NAMECHANGER_TEXT_STORE_SIZE 9 #define NAMECHANGER_HEADER "Flipper Name File" +#define NAMECHANGER_PATH EXT_PATH("dolphin/name.txt") #define TAG "NameChangerSRV" diff --git a/applications/services/power/power_cli.c b/applications/services/power/power_cli.c index f4a10f0a9..021ce3553 100644 --- a/applications/services/power/power_cli.c +++ b/applications/services/power/power_cli.c @@ -26,24 +26,6 @@ void power_cli_reboot2dfu(Cli* cli, FuriString* args) { power_reboot(PowerBootModeDfu); } -static void power_cli_callback(const char* key, const char* value, bool last, void* context) { - UNUSED(last); - UNUSED(context); - printf("%-24s: %s\r\n", key, value); -} - -void power_cli_info(Cli* cli, FuriString* args) { - UNUSED(cli); - UNUSED(args); - furi_hal_power_info_get(power_cli_callback, '_', NULL); -} - -void power_cli_debug(Cli* cli, FuriString* args) { - UNUSED(cli); - UNUSED(args); - furi_hal_power_debug_get(power_cli_callback, NULL); -} - void power_cli_5v(Cli* cli, FuriString* args) { UNUSED(cli); if(!furi_string_cmp(args, "0")) { @@ -74,8 +56,6 @@ static void power_cli_command_print_usage() { printf("\toff\t - shutdown power\r\n"); printf("\treboot\t - reboot\r\n"); printf("\treboot2dfu\t - reboot to dfu bootloader\r\n"); - printf("\tinfo\t - show power info\r\n"); - printf("\tdebug\t - show debug information\r\n"); printf("\t5v <0 or 1>\t - enable or disable 5v ext\r\n"); if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { printf("\t3v3 <0 or 1>\t - enable or disable 3v3 ext\r\n"); @@ -108,16 +88,6 @@ void power_cli(Cli* cli, FuriString* args, void* context) { break; } - if(furi_string_cmp_str(cmd, "info") == 0) { - power_cli_info(cli, args); - break; - } - - if(furi_string_cmp_str(cmd, "debug") == 0) { - power_cli_debug(cli, args); - break; - } - if(furi_string_cmp_str(cmd, "5v") == 0) { power_cli_5v(cli, args); break; diff --git a/applications/services/power/power_service/power.c b/applications/services/power/power_service/power.c index aff3d946d..7c1b9be58 100644 --- a/applications/services/power/power_service/power.c +++ b/applications/services/power/power_service/power.c @@ -5,6 +5,7 @@ #include "xtreme/settings.h" #define POWER_OFF_TIMEOUT 90 +#define TAG "Power" void power_draw_battery_callback(Canvas* canvas, void* context) { furi_assert(context); @@ -73,11 +74,11 @@ void power_draw_battery_callback(Canvas* canvas, void* context) { } else if( (battery_icon == BatteryIconBarPercent) && (power->state != PowerStateCharging) && // Default bar display with percentage - (power->info.voltage_battery_charging >= + (power->info.voltage_battery_charge_limit >= 4.2)) { // not looking nice with low voltage indicator canvas_set_font(canvas, FontBatteryPercent); - // align charge dispaly value with digits to draw + // align charge display value with digits to draw uint8_t bar_charge = power->info.charge; if(bar_charge > 23 && bar_charge < 38) { bar_charge = 23; @@ -86,10 +87,10 @@ void power_draw_battery_callback(Canvas* canvas, void* context) { } else if(bar_charge >= 62 && bar_charge < 74) { bar_charge = 74; } - canvas_set_color(canvas, ColorBlack); - canvas_draw_box(canvas, 1, 1, (bar_charge * 22) / 100, 6); // drawing digits + canvas_set_color(canvas, ColorBlack); + canvas_draw_box(canvas, 1, 1, (bar_charge * 22) / 100, 6); if(bar_charge < 38) { // both digits are black canvas_set_color(canvas, ColorBlack); canvas_draw_str_aligned( @@ -117,17 +118,18 @@ void power_draw_battery_callback(Canvas* canvas, void* context) { canvas_set_color(canvas, ColorBlack); canvas_draw_str_aligned( canvas, 15, 4, AlignCenter, AlignCenter, batteryPercentileSecondDigit); - } else { // charge >= 62, both digits are white + } else { // charge >= 74, both digits are white canvas_set_color(canvas, ColorWhite); canvas_draw_str_aligned( canvas, 11, 4, AlignCenter, AlignCenter, batteryPercentile); } + } else { //default bar display, added here to serve as fallback/default behaviour. canvas_draw_box(canvas, 2, 2, (power->info.charge + 4) / 5, 4); } // TODO: Verify if it displays correctly with custom battery skins !!! - if(power->info.voltage_battery_charging < 4.2) { + if(power->info.voltage_battery_charge_limit < 4.2) { // Battery charging voltage is modified, indicate with cross pattern canvas_invert_color(canvas); uint8_t battery_bar_width = (power->info.charge + 4) / 5; @@ -145,7 +147,7 @@ void power_draw_battery_callback(Canvas* canvas, void* context) { if(power->state == PowerStateCharging) { canvas_set_bitmap_mode(canvas, 1); // TODO: replace -1 magic for uint8_t with re-framing - if(battery_icon == BatteryIconPercent || battery_icon == BatteryIconBarPercent) { + if(battery_icon == BatteryIconPercent) { canvas_set_color(canvas, ColorBlack); canvas_draw_box(canvas, 1, 1, 22, 6); canvas_draw_icon(canvas, 2, -1, &I_Charging_lightning_9x10); @@ -163,6 +165,63 @@ void power_draw_battery_callback(Canvas* canvas, void* context) { canvas_set_font(canvas, FontBatteryPercent); canvas_draw_str_aligned( canvas, 16, 4, AlignCenter, AlignCenter, batteryPercentile); + } else if(battery_icon == BatteryIconBarPercent) { + // clean-up default charging bar display + canvas_set_color(canvas, ColorWhite); + canvas_draw_box(canvas, 1, 1, 22, 6); + + // align charge display value with digits to draw + uint8_t bar_charge = power->info.charge; + + if(bar_charge > 48 && bar_charge < 63) { + bar_charge = 48; + } else if(bar_charge >= 63 && bar_charge < 84) { + bar_charge = 75; + } else if(bar_charge >= 84 && bar_charge < 96) { + bar_charge = 96; + } + canvas_set_color(canvas, ColorBlack); + canvas_draw_box(canvas, 1, 1, (bar_charge * 22) / 100, 6); + + // drawing charge icon + canvas_draw_icon(canvas, 2, -1, &I_Charging_lightning_9x10); + canvas_set_color(canvas, ColorWhite); + canvas_draw_icon(canvas, 2, -1, &I_Charging_lightning_mask_9x10); + + // drawing digits + canvas_set_font(canvas, FontBatteryPercent); + if(bar_charge < 64) { // both digits are black + canvas_set_color(canvas, ColorBlack); + canvas_draw_str_aligned( + canvas, 16, 4, AlignCenter, AlignCenter, batteryPercentile); + } else if(bar_charge >= 64 && bar_charge < 84) { // first digit is white + canvas_set_color(canvas, ColorWhite); + + // first + char batteryPercentileFirstDigit[2]; + snprintf( + batteryPercentileFirstDigit, + sizeof(batteryPercentileFirstDigit), + "%c", + batteryPercentile[0]); + canvas_draw_str_aligned( + canvas, 14, 4, AlignCenter, AlignCenter, batteryPercentileFirstDigit); + + // second + char batteryPercentileSecondDigit[2]; + snprintf( + batteryPercentileSecondDigit, + sizeof(batteryPercentileSecondDigit), + "%c", + batteryPercentile[1]); + canvas_set_color(canvas, ColorBlack); + canvas_draw_str_aligned( + canvas, 20, 4, AlignCenter, AlignCenter, batteryPercentileSecondDigit); + } else { // charge >= 84, both digits are white + canvas_set_color(canvas, ColorWhite); + canvas_draw_str_aligned( + canvas, 16, 4, AlignCenter, AlignCenter, batteryPercentile); + } } else { canvas_set_color(canvas, ColorWhite); canvas_draw_icon(canvas, 8, -1, &I_Charging_lightning_mask_9x10); @@ -261,6 +320,7 @@ Power* power_alloc() { // Records power->notification = furi_record_open(RECORD_NOTIFICATION); power->gui = furi_record_open(RECORD_GUI); + // Pubsub power->event_pubsub = furi_pubsub_alloc(); power->settings_events = furi_pubsub_alloc(); @@ -383,7 +443,7 @@ static bool power_update_info(Power* power) { info.capacity_full = furi_hal_power_get_battery_full_capacity(); info.current_charger = furi_hal_power_get_battery_current(FuriHalPowerICCharger); info.current_gauge = furi_hal_power_get_battery_current(FuriHalPowerICFuelGauge); - info.voltage_battery_charging = furi_hal_power_get_battery_charging_voltage(); + info.voltage_battery_charge_limit = furi_hal_power_get_battery_charge_voltage_limit(); info.voltage_charger = furi_hal_power_get_battery_voltage(FuriHalPowerICCharger); info.voltage_gauge = furi_hal_power_get_battery_voltage(FuriHalPowerICFuelGauge); info.voltage_vbus = furi_hal_power_get_usb_voltage(); @@ -454,6 +514,12 @@ static void power_check_battery_level_change(Power* power) { int32_t power_srv(void* p) { UNUSED(p); + + if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) { + FURI_LOG_W(TAG, "Skipping start in special boot mode"); + return 0; + } + Power* power = power_alloc(); if(!LOAD_POWER_SETTINGS(&power->shutdown_idle_delay_ms)) { power->shutdown_idle_delay_ms = 0; diff --git a/applications/services/power/power_service/power.h b/applications/services/power/power_service/power.h index 6e41d5c6e..cb2fc8fad 100644 --- a/applications/services/power/power_service/power.h +++ b/applications/services/power/power_service/power.h @@ -26,14 +26,14 @@ typedef enum { } PowerEventType; typedef enum { - BatteryIconOff = 1, - BatteryIconBar = 2, - BatteryIconPercent = 3, - BatteryIconInvertedPercent = 4, - BatteryIconRetro3 = 5, - BatteryIconRetro5 = 6, - BatteryIconBarPercent = 0, - BatteryIconCount = 7, + BatteryIconOff, + BatteryIconBar, + BatteryIconPercent, + BatteryIconInvertedPercent, + BatteryIconRetro3, + BatteryIconRetro5, + BatteryIconBarPercent, + BatteryIconCount, } BatteryIcon; typedef union { @@ -52,7 +52,7 @@ typedef struct { float current_charger; float current_gauge; - float voltage_battery_charging; + float voltage_battery_charge_limit; float voltage_charger; float voltage_gauge; float voltage_vbus; diff --git a/applications/services/rpc/rpc.c b/applications/services/rpc/rpc.c index 63a71ad06..ecb4b7639 100644 --- a/applications/services/rpc/rpc.c +++ b/applications/services/rpc/rpc.c @@ -145,8 +145,8 @@ void rpc_session_set_terminated_callback( /* Doesn't forbid using rpc_feed_bytes() after session close - it's safe. * Because any bytes received in buffer will be flushed before next session. - * If bytes get into stream buffer before it's get epmtified and this - * command is gets processed - it's safe either. But case of it is quite + * If bytes get into stream buffer before it's get emptied and this + * command is gets processed - it's safe either way. But case of it is quite * odd: client sends close request and sends command after. */ size_t diff --git a/applications/services/rpc/rpc_storage.c b/applications/services/rpc/rpc_storage.c index c4493cc74..c3a4a0470 100644 --- a/applications/services/rpc/rpc_storage.c +++ b/applications/services/rpc/rpc_storage.c @@ -201,7 +201,7 @@ static void rpc_system_storage_stat_process(const PB_Main* request, void* contex if(error == FSE_OK) { response->which_content = PB_Main_storage_stat_response_tag; response->content.storage_stat_response.has_file = true; - response->content.storage_stat_response.file.type = (fileinfo.flags & FSF_DIRECTORY) ? + response->content.storage_stat_response.file.type = file_info_is_dir(&fileinfo) ? PB_Storage_File_FileType_DIR : PB_Storage_File_FileType_FILE; response->content.storage_stat_response.file.size = fileinfo.size; @@ -291,9 +291,8 @@ static void rpc_system_storage_list_process(const PB_Main* request, void* contex rpc_send_and_release(session, &response); i = 0; } - list->file[i].type = (fileinfo.flags & FSF_DIRECTORY) ? - PB_Storage_File_FileType_DIR : - PB_Storage_File_FileType_FILE; + list->file[i].type = file_info_is_dir(&fileinfo) ? PB_Storage_File_FileType_DIR : + PB_Storage_File_FileType_FILE; list->file[i].size = fileinfo.size; list->file[i].data = NULL; list->file[i].name = name; @@ -458,7 +457,7 @@ static bool rpc_system_storage_is_dir_is_empty(Storage* fs_api, const char* path FileInfo fileinfo; bool is_dir_is_empty = true; FS_Error error = storage_common_stat(fs_api, path, &fileinfo); - if((error == FSE_OK) && (fileinfo.flags & FSF_DIRECTORY)) { + if((error == FSE_OK) && file_info_is_dir(&fileinfo)) { File* dir = storage_file_alloc(fs_api); if(storage_dir_open(dir, path)) { char* name = malloc(MAX_NAME_LENGTH); diff --git a/applications/services/storage/application.fam b/applications/services/storage/application.fam index 7aa721cc3..7b106e00f 100644 --- a/applications/services/storage/application.fam +++ b/applications/services/storage/application.fam @@ -7,7 +7,7 @@ App( requires=["storage_settings"], provides=["storage_start"], stack_size=3 * 1024, - order=120, + order=44, sdk_headers=["storage.h"], ) diff --git a/applications/services/storage/filesystem_api.c b/applications/services/storage/filesystem_api.c index b979967ac..30b20ede4 100644 --- a/applications/services/storage/filesystem_api.c +++ b/applications/services/storage/filesystem_api.c @@ -36,3 +36,7 @@ const char* filesystem_api_error_get_desc(FS_Error error_id) { } return result; } + +bool file_info_is_dir(const FileInfo* file_info) { + return (file_info->flags & FSF_DIRECTORY); +} \ No newline at end of file diff --git a/applications/services/storage/filesystem_api_defines.h b/applications/services/storage/filesystem_api_defines.h index b73e6eb33..cd24b8825 100644 --- a/applications/services/storage/filesystem_api_defines.h +++ b/applications/services/storage/filesystem_api_defines.h @@ -1,5 +1,6 @@ #pragma once #include +#include #ifdef __cplusplus extern "C" { @@ -40,10 +41,10 @@ typedef enum { FSF_DIRECTORY = (1 << 0), /**< Directory */ } FS_Flags; -/** Structure that hold file index and returned api errors */ +/** Structure that hold file index and returned api errors */ typedef struct File File; -/** Structure that hold file info */ +/** Structure that hold file info */ typedef struct { uint8_t flags; /**< flags from FS_Flags enum */ uint64_t size; /**< file size */ @@ -55,6 +56,12 @@ typedef struct { */ const char* filesystem_api_error_get_desc(FS_Error error_id); +/** Checks if file info is directory + * @param file_info file info pointer + * @return bool is directory + */ +bool file_info_is_dir(const FileInfo* file_info); + #ifdef __cplusplus } #endif diff --git a/applications/services/storage/storage.h b/applications/services/storage/storage.h index e093cbe0f..da583ce50 100644 --- a/applications/services/storage/storage.h +++ b/applications/services/storage/storage.h @@ -10,10 +10,12 @@ extern "C" { #define STORAGE_INT_PATH_PREFIX "/int" #define STORAGE_EXT_PATH_PREFIX "/ext" #define STORAGE_ANY_PATH_PREFIX "/any" +#define STORAGE_APP_DATA_PATH_PREFIX "/app" #define INT_PATH(path) STORAGE_INT_PATH_PREFIX "/" path #define EXT_PATH(path) STORAGE_EXT_PATH_PREFIX "/" path #define ANY_PATH(path) STORAGE_ANY_PATH_PREFIX "/" path +#define APP_DATA_PATH(path) STORAGE_APP_DATA_PATH_PREFIX "/" path #define RECORD_STORAGE "storage" @@ -175,6 +177,15 @@ bool storage_dir_read(File* file, FileInfo* fileinfo, char* name, uint16_t name_ */ bool storage_dir_rewind(File* file); +/** + * @brief Check that dir exists + * + * @param storage + * @param path + * @return bool + */ +bool storage_dir_exists(Storage* storage, const char* path); + /******************* Common Functions *******************/ /** Retrieves unix timestamp of last access @@ -246,6 +257,36 @@ FS_Error storage_common_fs_info( uint64_t* total_space, uint64_t* free_space); +/** + * @brief Parse aliases in path and replace them with real path + * Also will create special folders if they are not exist + * + * @param storage + * @param path + * @return bool + */ +void storage_common_resolve_path_and_ensure_app_directory(Storage* storage, FuriString* path); + +/** + * @brief Move content of one folder to another, with rename of all conflicting files. + * Source folder will be deleted if the migration is successful. + * + * @param storage + * @param source + * @param dest + * @return FS_Error + */ +FS_Error storage_common_migrate(Storage* storage, const char* source, const char* dest); + +/** + * @brief Check that file or dir exists + * + * @param storage + * @param path + * @return bool + */ +bool storage_common_exists(Storage* storage, const char* path); + /******************* Error Functions *******************/ /** Retrieves the error text from the error id @@ -305,14 +346,14 @@ typedef void (*Storage_name_converter)(FuriString*); /** Backs up internal storage to a tar archive * @param api pointer to the api - * @param dstmane destination archive path + * @param dstname destination archive path * @return FS_Error operation result */ FS_Error storage_int_backup(Storage* api, const char* dstname); /** Restores internal storage from a tar archive * @param api pointer to the api - * @param dstmane archive path + * @param dstname archive path * @param converter pointer to filename conversion function, may be NULL * @return FS_Error operation result */ diff --git a/applications/services/storage/storage_cli.c b/applications/services/storage/storage_cli.c index eeaa7fce8..8e2dcdbbb 100644 --- a/applications/services/storage/storage_cli.c +++ b/applications/services/storage/storage_cli.c @@ -1,6 +1,5 @@ #include #include -#include #include #include @@ -61,28 +60,26 @@ static void storage_cli_info(Cli* cli, FuriString* path) { } } else if(furi_string_cmp_str(path, STORAGE_EXT_PATH_PREFIX) == 0) { SDInfo sd_info; - SD_CID sd_cid; FS_Error error = storage_sd_info(api, &sd_info); - BSP_SD_GetCIDRegister(&sd_cid); if(error != FSE_OK) { storage_cli_print_error(error); } else { printf( "Label: %s\r\nType: %s\r\n%luKiB total\r\n%luKiB free\r\n" - "%02x%2.2s %5.5s %i.%i\r\nSN:%04lx %02i/%i\r\n", + "%02x%s %s v%i.%i\r\nSN:%04lx %02i/%i\r\n", sd_info.label, sd_api_get_fs_type_text(sd_info.fs_type), sd_info.kb_total, sd_info.kb_free, - sd_cid.ManufacturerID, - sd_cid.OEM_AppliID, - sd_cid.ProdName, - sd_cid.ProdRev >> 4, - sd_cid.ProdRev & 0xf, - sd_cid.ProdSN, - sd_cid.ManufactMonth, - sd_cid.ManufactYear + 2000); + sd_info.manufacturer_id, + sd_info.oem_id, + sd_info.product_name, + sd_info.product_revision_major, + sd_info.product_revision_minor, + sd_info.product_serial_number, + sd_info.manufacturing_month, + sd_info.manufacturing_year); } } else { storage_cli_print_usage(); @@ -134,7 +131,7 @@ static void storage_cli_list(Cli* cli, FuriString* path) { while(storage_dir_read(file, &fileinfo, name, MAX_NAME_LENGTH)) { read_done = true; - if(fileinfo.flags & FSF_DIRECTORY) { + if(file_info_is_dir(&fileinfo)) { printf("\t[D] %s\r\n", name); } else { printf("\t[F] %s %lub\r\n", name, (uint32_t)(fileinfo.size)); @@ -172,7 +169,7 @@ static void storage_cli_tree(Cli* cli, FuriString* path) { while(dir_walk_read(dir_walk, name, &fileinfo) == DirWalkOK) { read_done = true; - if(fileinfo.flags & FSF_DIRECTORY) { + if(file_info_is_dir(&fileinfo)) { printf("\t[D] %s\r\n", furi_string_get_cstr(name)); } else { printf( @@ -386,7 +383,7 @@ static void storage_cli_stat(Cli* cli, FuriString* path) { FS_Error error = storage_common_stat(api, furi_string_get_cstr(path), &fileinfo); if(error == FSE_OK) { - if(fileinfo.flags & FSF_DIRECTORY) { + if(file_info_is_dir(&fileinfo)) { printf("Directory\r\n"); } else { printf("File, size: %lub\r\n", (uint32_t)(fileinfo.size)); diff --git a/applications/services/storage/storage_external_api.c b/applications/services/storage/storage_external_api.c index 6929a9cbd..8d8220f81 100644 --- a/applications/services/storage/storage_external_api.c +++ b/applications/services/storage/storage_external_api.c @@ -12,9 +12,7 @@ #define TAG "StorageAPI" -#define S_API_PROLOGUE \ - FuriSemaphore* semaphore = furi_semaphore_alloc(1, 0); \ - furi_check(semaphore != NULL); +#define S_API_PROLOGUE FuriApiLock lock = api_lock_alloc_locked(); #define S_FILE_API_PROLOGUE \ Storage* storage = file->storage; \ @@ -24,13 +22,12 @@ furi_check( \ furi_message_queue_put(storage->message_queue, &message, FuriWaitForever) == \ FuriStatusOk); \ - furi_semaphore_acquire(semaphore, FuriWaitForever); \ - furi_semaphore_free(semaphore); + api_lock_wait_unlock_and_free(lock) #define S_API_MESSAGE(_command) \ SAReturn return_data; \ StorageMessage message = { \ - .semaphore = semaphore, \ + .lock = lock, \ .command = _command, \ .data = &data, \ .return_data = &return_data, \ @@ -42,12 +39,6 @@ .file = file, \ }}; -#define S_API_DATA_PATH \ - SAData data = { \ - .path = { \ - .path = path, \ - }}; - #define S_RETURN_BOOL (return_data.bool_value); #define S_RETURN_UINT16 (return_data.uint16_value); #define S_RETURN_UINT64 (return_data.uint64_value); @@ -73,6 +64,7 @@ static bool storage_file_open_internal( .path = path, .access_mode = access_mode, .open_mode = open_mode, + .thread_id = furi_thread_get_current_id(), }}; file->type = FileTypeOpenFile; @@ -252,7 +244,7 @@ bool storage_file_exists(Storage* storage, const char* path) { FileInfo fileinfo; FS_Error error = storage_common_stat(storage, path, &fileinfo); - if(error == FSE_OK && !(fileinfo.flags & FSF_DIRECTORY)) { + if(error == FSE_OK && !file_info_is_dir(&fileinfo)) { exist = true; } @@ -269,6 +261,7 @@ static bool storage_dir_open_internal(File* file, const char* path) { .dopen = { .file = file, .path = path, + .thread_id = furi_thread_get_current_id(), }}; file->type = FileTypeOpenDir; @@ -352,12 +345,28 @@ bool storage_dir_rewind(File* file) { return S_RETURN_BOOL; } +bool storage_dir_exists(Storage* storage, const char* path) { + bool exist = false; + FileInfo fileinfo; + FS_Error error = storage_common_stat(storage, path, &fileinfo); + + if(error == FSE_OK && file_info_is_dir(&fileinfo)) { + exist = true; + } + + return exist; +} /****************** COMMON ******************/ FS_Error storage_common_timestamp(Storage* storage, const char* path, uint32_t* timestamp) { S_API_PROLOGUE; - SAData data = {.ctimestamp = {.path = path, .timestamp = timestamp}}; + SAData data = { + .ctimestamp = { + .path = path, + .timestamp = timestamp, + .thread_id = furi_thread_get_current_id(), + }}; S_API_MESSAGE(StorageCommandCommonTimestamp); S_API_EPILOGUE; @@ -366,8 +375,12 @@ FS_Error storage_common_timestamp(Storage* storage, const char* path, uint32_t* FS_Error storage_common_stat(Storage* storage, const char* path, FileInfo* fileinfo) { S_API_PROLOGUE; - - SAData data = {.cstat = {.path = path, .fileinfo = fileinfo}}; + SAData data = { + .cstat = { + .path = path, + .fileinfo = fileinfo, + .thread_id = furi_thread_get_current_id(), + }}; S_API_MESSAGE(StorageCommandCommonStat); S_API_EPILOGUE; @@ -376,7 +389,12 @@ FS_Error storage_common_stat(Storage* storage, const char* path, FileInfo* filei FS_Error storage_common_remove(Storage* storage, const char* path) { S_API_PROLOGUE; - S_API_DATA_PATH; + SAData data = { + .path = { + .path = path, + .thread_id = furi_thread_get_current_id(), + }}; + S_API_MESSAGE(StorageCommandCommonRemove); S_API_EPILOGUE; return S_RETURN_ERROR; @@ -426,7 +444,7 @@ static FS_Error furi_string_right(path, strlen(old_path)); furi_string_printf(tmp_new_path, "%s%s", new_path, furi_string_get_cstr(path)); - if(fileinfo.flags & FSF_DIRECTORY) { + if(file_info_is_dir(&fileinfo)) { error = storage_common_mkdir(storage, furi_string_get_cstr(tmp_new_path)); } else { error = storage_common_copy( @@ -455,7 +473,7 @@ FS_Error storage_common_copy(Storage* storage, const char* old_path, const char* error = storage_common_stat(storage, old_path, &fileinfo); if(error == FSE_OK) { - if(fileinfo.flags & FSF_DIRECTORY) { + if(file_info_is_dir(&fileinfo)) { error = storage_copy_recursive(storage, old_path, new_path); } else { Stream* stream_from = file_stream_alloc(storage); @@ -482,7 +500,7 @@ FS_Error storage_common_copy(Storage* storage, const char* old_path, const char* static FS_Error storage_merge_recursive(Storage* storage, const char* old_path, const char* new_path) { - FS_Error error = storage_common_mkdir(storage, new_path); + FS_Error error = FSE_OK; DirWalk* dir_walk = dir_walk_alloc(storage); FuriString *path, *file_basename, *tmp_new_path; FileInfo fileinfo; @@ -491,7 +509,7 @@ static FS_Error tmp_new_path = furi_string_alloc(); do { - if((error != FSE_OK) && (error != FSE_EXIST)) break; + if(!storage_simply_mkdir(storage, new_path)) break; dir_walk_set_recursive(dir_walk, false); if(!dir_walk_open(dir_walk, old_path)) { @@ -511,13 +529,13 @@ static FS_Error path_extract_basename(furi_string_get_cstr(path), file_basename); path_concat(new_path, furi_string_get_cstr(file_basename), tmp_new_path); - if(fileinfo.flags & FSF_DIRECTORY) { + if(file_info_is_dir(&fileinfo)) { if(storage_common_stat( storage, furi_string_get_cstr(tmp_new_path), &fileinfo) == FSE_OK) { - if(fileinfo.flags & FSF_DIRECTORY) { + if(file_info_is_dir(&fileinfo)) { error = storage_common_mkdir(storage, furi_string_get_cstr(tmp_new_path)); - if(error != FSE_OK) { + if(error != FSE_OK && error != FSE_EXIST) { break; } } @@ -551,7 +569,7 @@ FS_Error storage_common_merge(Storage* storage, const char* old_path, const char error = storage_common_stat(storage, old_path, &fileinfo); if(error == FSE_OK) { - if(fileinfo.flags & FSF_DIRECTORY) { + if(file_info_is_dir(&fileinfo)) { error = storage_merge_recursive(storage, old_path, new_path); } else { error = storage_common_stat(storage, new_path, &fileinfo); @@ -559,7 +577,7 @@ FS_Error storage_common_merge(Storage* storage, const char* old_path, const char furi_string_set(new_path_next, new_path); FuriString* dir_path; FuriString* filename; - char extension[MAX_EXT_LEN]; + char extension[MAX_EXT_LEN] = {0}; dir_path = furi_string_alloc(); filename = furi_string_alloc(); @@ -611,7 +629,12 @@ FS_Error storage_common_merge(Storage* storage, const char* old_path, const char FS_Error storage_common_mkdir(Storage* storage, const char* path) { S_API_PROLOGUE; - S_API_DATA_PATH; + SAData data = { + .path = { + .path = path, + .thread_id = furi_thread_get_current_id(), + }}; + S_API_MESSAGE(StorageCommandCommonMkDir); S_API_EPILOGUE; return S_RETURN_ERROR; @@ -629,6 +652,7 @@ FS_Error storage_common_fs_info( .fs_path = fs_path, .total_space = total_space, .free_space = free_space, + .thread_id = furi_thread_get_current_id(), }}; S_API_MESSAGE(StorageCommandCommonFSInfo); @@ -636,6 +660,38 @@ FS_Error storage_common_fs_info( return S_RETURN_ERROR; } +void storage_common_resolve_path_and_ensure_app_directory(Storage* storage, FuriString* path) { + S_API_PROLOGUE; + + SAData data = { + .cresolvepath = { + .path = path, + .thread_id = furi_thread_get_current_id(), + }}; + + S_API_MESSAGE(StorageCommandCommonResolvePath); + S_API_EPILOGUE; +} + +FS_Error storage_common_migrate(Storage* storage, const char* source, const char* dest) { + if(!storage_common_exists(storage, source)) { + return FSE_OK; + } + + FS_Error error = storage_common_merge(storage, source, dest); + + if(error == FSE_OK) { + storage_simply_remove_recursive(storage, source); + } + + return error; +} + +bool storage_common_exists(Storage* storage, const char* path) { + FileInfo file_info; + return storage_common_stat(storage, path, &file_info) == FSE_OK; +} + /****************** ERROR ******************/ const char* storage_error_get_desc(FS_Error error_id) { @@ -753,7 +809,7 @@ bool storage_simply_remove_recursive(Storage* storage, const char* path) { } while(storage_dir_read(dir, &fileinfo, name, MAX_NAME_LENGTH)) { - if(fileinfo.flags & FSF_DIRECTORY) { + if(file_info_is_dir(&fileinfo)) { furi_string_cat_printf(cur_dir, "/%s", name); go_deeper = true; break; diff --git a/applications/services/storage/storage_glue.c b/applications/services/storage/storage_glue.c index 22f2e3dfa..5dabfa386 100644 --- a/applications/services/storage/storage_glue.c +++ b/applications/services/storage/storage_glue.c @@ -5,21 +5,18 @@ void storage_file_init(StorageFile* obj) { obj->file = NULL; - obj->type = ST_ERROR; obj->file_data = NULL; obj->path = furi_string_alloc(); } void storage_file_init_set(StorageFile* obj, const StorageFile* src) { obj->file = src->file; - obj->type = src->type; obj->file_data = src->file_data; obj->path = furi_string_alloc_set(src->path); } void storage_file_set(StorageFile* obj, const StorageFile* src) { //-V524 obj->file = src->file; - obj->type = src->type; obj->file_data = src->file_data; furi_string_set(obj->path, src->path); } @@ -31,29 +28,13 @@ void storage_file_clear(StorageFile* obj) { /****************** storage data ******************/ void storage_data_init(StorageData* storage) { - storage->mutex = furi_mutex_alloc(FuriMutexTypeNormal); - furi_check(storage->mutex != NULL); storage->data = NULL; storage->status = StorageStatusNotReady; StorageFileList_init(storage->files); } -bool storage_data_lock(StorageData* storage) { - return (furi_mutex_acquire(storage->mutex, FuriWaitForever) == FuriStatusOk); -} - -bool storage_data_unlock(StorageData* storage) { - return (furi_mutex_release(storage->mutex) == FuriStatusOk); -} - StorageStatus storage_data_status(StorageData* storage) { - StorageStatus status; - - storage_data_lock(storage); - status = storage->status; - storage_data_unlock(storage); - - return status; + return storage->status; } const char* storage_data_status_text(StorageData* storage) { @@ -166,16 +147,10 @@ void* storage_get_storage_file_data(const File* file, StorageData* storage) { return founded_file->file_data; } -void storage_push_storage_file( - File* file, - FuriString* path, - StorageType type, - StorageData* storage) { +void storage_push_storage_file(File* file, FuriString* path, StorageData* storage) { StorageFile* storage_file = StorageFileList_push_new(storage->files); - file->file_id = (uint32_t)storage_file; storage_file->file = file; - storage_file->type = type; furi_string_set(storage_file->path, path); } diff --git a/applications/services/storage/storage_glue.h b/applications/services/storage/storage_glue.h index 6fdc70099..bf0a1c69e 100644 --- a/applications/services/storage/storage_glue.h +++ b/applications/services/storage/storage_glue.h @@ -18,7 +18,6 @@ typedef struct { typedef struct { File* file; - StorageType type; void* file_data; FuriString* path; } StorageFile; @@ -38,8 +37,6 @@ void storage_file_set(StorageFile* obj, const StorageFile* src); void storage_file_clear(StorageFile* obj); void storage_data_init(StorageData* storage); -bool storage_data_lock(StorageData* storage); -bool storage_data_unlock(StorageData* storage); StorageStatus storage_data_status(StorageData* storage); const char* storage_data_status_text(StorageData* storage); void storage_data_timestamp(StorageData* storage); @@ -57,7 +54,6 @@ struct StorageData { const FS_Api* fs_api; StorageApi api; void* data; - FuriMutex* mutex; StorageStatus status; StorageFileList_t files; uint32_t timestamp; @@ -69,11 +65,7 @@ bool storage_path_already_open(FuriString* path, StorageFileList_t files); void storage_set_storage_file_data(const File* file, void* file_data, StorageData* storage); void* storage_get_storage_file_data(const File* file, StorageData* storage); -void storage_push_storage_file( - File* file, - FuriString* path, - StorageType type, - StorageData* storage); +void storage_push_storage_file(File* file, FuriString* path, StorageData* storage); bool storage_pop_storage_file(File* file, StorageData* storage); #ifdef __cplusplus diff --git a/applications/services/storage/storage_i.h b/applications/services/storage/storage_i.h index 406fc921e..85df5d926 100644 --- a/applications/services/storage/storage_i.h +++ b/applications/services/storage/storage_i.h @@ -12,6 +12,8 @@ extern "C" { #define STORAGE_COUNT (ST_INT + 1) +#define APPS_DATA_PATH EXT_PATH("apps_data") + typedef struct { ViewPort* view_port; bool enabled; diff --git a/applications/services/storage/storage_message.h b/applications/services/storage/storage_message.h index 987268017..9e13bf83d 100644 --- a/applications/services/storage/storage_message.h +++ b/applications/services/storage/storage_message.h @@ -1,5 +1,6 @@ #pragma once #include +#include #ifdef __cplusplus extern "C" { @@ -10,6 +11,7 @@ typedef struct { const char* path; FS_AccessMode access_mode; FS_OpenMode open_mode; + FuriThreadId thread_id; } SADataFOpen; typedef struct { @@ -33,6 +35,7 @@ typedef struct { typedef struct { File* file; const char* path; + FuriThreadId thread_id; } SADataDOpen; typedef struct { @@ -45,25 +48,34 @@ typedef struct { typedef struct { const char* path; uint32_t* timestamp; + FuriThreadId thread_id; } SADataCTimestamp; typedef struct { const char* path; FileInfo* fileinfo; + FuriThreadId thread_id; } SADataCStat; typedef struct { const char* fs_path; uint64_t* total_space; uint64_t* free_space; + FuriThreadId thread_id; } SADataCFSInfo; +typedef struct { + FuriString* path; + FuriThreadId thread_id; +} SADataCResolvePath; + typedef struct { uint32_t id; } SADataError; typedef struct { const char* path; + FuriThreadId thread_id; } SADataPath; typedef struct { @@ -86,6 +98,7 @@ typedef union { SADataCTimestamp ctimestamp; SADataCStat cstat; SADataCFSInfo cfsinfo; + SADataCResolvePath cresolvepath; SADataError error; @@ -127,10 +140,11 @@ typedef enum { StorageCommandSDUnmount, StorageCommandSDInfo, StorageCommandSDStatus, + StorageCommandCommonResolvePath, } StorageCommand; typedef struct { - FuriSemaphore* semaphore; + FuriApiLock lock; StorageCommand command; SAData* data; SAReturn* return_data; diff --git a/applications/services/storage/storage_processing.c b/applications/services/storage/storage_processing.c index 795a5d11c..cab1edff5 100644 --- a/applications/services/storage/storage_processing.c +++ b/applications/services/storage/storage_processing.c @@ -2,27 +2,13 @@ #include #include -#define FS_CALL(_storage, _fn) \ - storage_data_lock(_storage); \ - ret = _storage->fs_api->_fn; \ - storage_data_unlock(_storage); +#define FS_CALL(_storage, _fn) ret = _storage->fs_api->_fn; -#define ST_CALL(_storage, _fn) \ - storage_data_lock(_storage); \ - ret = _storage->api._fn; \ - storage_data_unlock(_storage); - -static StorageData* storage_get_storage_by_type(Storage* app, StorageType type) { - furi_check(type == ST_EXT || type == ST_INT); - StorageData* storage = &app->storage[type]; - return storage; -} - -static bool storage_type_is_not_valid(StorageType type) { +static bool storage_type_is_valid(StorageType type) { #ifdef FURI_RAM_EXEC - return type != ST_EXT; + return type == ST_EXT; #else - return type >= ST_ERROR; + return type < ST_ERROR; #endif } @@ -38,51 +24,36 @@ static StorageData* get_storage_by_file(File* file, StorageData* storages) { return storage_data; } -static const char* remove_vfs(const char* path) { - return path + MIN(4u, strlen(path)); +static const char* cstr_path_without_vfs_prefix(FuriString* path) { + const char* path_cstr = furi_string_get_cstr(path); + return path_cstr + MIN(4u, strlen(path_cstr)); } -static StorageType storage_get_type_by_path(Storage* app, const char* path) { +static StorageType storage_get_type_by_path(FuriString* path) { StorageType type = ST_ERROR; - if(strlen(path) >= strlen(STORAGE_EXT_PATH_PREFIX) && - memcmp(path, STORAGE_EXT_PATH_PREFIX, strlen(STORAGE_EXT_PATH_PREFIX)) == 0) { - type = ST_EXT; - } else if( - strlen(path) >= strlen(STORAGE_INT_PATH_PREFIX) && - memcmp(path, STORAGE_INT_PATH_PREFIX, strlen(STORAGE_INT_PATH_PREFIX)) == 0) { - type = ST_INT; - } else if( - strlen(path) >= strlen(STORAGE_ANY_PATH_PREFIX) && - memcmp(path, STORAGE_ANY_PATH_PREFIX, strlen(STORAGE_ANY_PATH_PREFIX)) == 0) { - type = ST_ANY; - } + const char* path_cstr = furi_string_get_cstr(path); - if(type == ST_ANY) { + if(memcmp(path_cstr, STORAGE_EXT_PATH_PREFIX, strlen(STORAGE_EXT_PATH_PREFIX)) == 0) { + type = ST_EXT; + } else if(memcmp(path_cstr, STORAGE_INT_PATH_PREFIX, strlen(STORAGE_INT_PATH_PREFIX)) == 0) { type = ST_INT; - if(storage_data_status(&app->storage[ST_EXT]) == StorageStatusOK) { - type = ST_EXT; - } + } else if(memcmp(path_cstr, STORAGE_ANY_PATH_PREFIX, strlen(STORAGE_ANY_PATH_PREFIX)) == 0) { + type = ST_ANY; } return type; } static void storage_path_change_to_real_storage(FuriString* path, StorageType real_storage) { - if(memcmp( - furi_string_get_cstr(path), STORAGE_ANY_PATH_PREFIX, strlen(STORAGE_ANY_PATH_PREFIX)) == - 0) { + if(furi_string_search(path, STORAGE_ANY_PATH_PREFIX) == 0) { switch(real_storage) { case ST_EXT: - furi_string_set_char(path, 0, STORAGE_EXT_PATH_PREFIX[0]); - furi_string_set_char(path, 1, STORAGE_EXT_PATH_PREFIX[1]); - furi_string_set_char(path, 2, STORAGE_EXT_PATH_PREFIX[2]); - furi_string_set_char(path, 3, STORAGE_EXT_PATH_PREFIX[3]); + furi_string_replace_at( + path, 0, strlen(STORAGE_EXT_PATH_PREFIX), STORAGE_EXT_PATH_PREFIX); break; case ST_INT: - furi_string_set_char(path, 0, STORAGE_INT_PATH_PREFIX[0]); - furi_string_set_char(path, 1, STORAGE_INT_PATH_PREFIX[1]); - furi_string_set_char(path, 2, STORAGE_INT_PATH_PREFIX[2]); - furi_string_set_char(path, 3, STORAGE_INT_PATH_PREFIX[3]); + furi_string_replace_at( + path, 0, strlen(STORAGE_INT_PATH_PREFIX), STORAGE_INT_PATH_PREFIX); break; default: break; @@ -90,38 +61,51 @@ static void storage_path_change_to_real_storage(FuriString* path, StorageType re } } +FS_Error storage_get_data(Storage* app, FuriString* path, StorageData** storage) { + StorageType type = storage_get_type_by_path(path); + + if(storage_type_is_valid(type)) { + if(type == ST_ANY) { + type = ST_INT; + if(storage_data_status(&app->storage[ST_EXT]) == StorageStatusOK) { + type = ST_EXT; + } + storage_path_change_to_real_storage(path, type); + } + + furi_assert(type == ST_EXT || type == ST_INT); + *storage = &app->storage[type]; + + return FSE_OK; + } else { + return FSE_INVALID_NAME; + } +} + /******************* File Functions *******************/ bool storage_process_file_open( Storage* app, File* file, - const char* path, + FuriString* path, FS_AccessMode access_mode, FS_OpenMode open_mode) { bool ret = false; - StorageType type = storage_get_type_by_path(app, path); StorageData* storage; - file->error_id = FSE_OK; + file->error_id = storage_get_data(app, path, &storage); - if(storage_type_is_not_valid(type)) { - file->error_id = FSE_INVALID_NAME; - } else { - storage = storage_get_storage_by_type(app, type); - FuriString* real_path; - real_path = furi_string_alloc_set(path); - storage_path_change_to_real_storage(real_path, type); - - if(storage_path_already_open(real_path, storage->files)) { + if(file->error_id == FSE_OK) { + if(storage_path_already_open(path, storage->files)) { file->error_id = FSE_ALREADY_OPEN; } else { if(access_mode & FSAM_WRITE) { storage_data_timestamp(storage); } - storage_push_storage_file(file, real_path, type, storage); - FS_CALL(storage, file.open(storage, file, remove_vfs(path), access_mode, open_mode)); - } + storage_push_storage_file(file, path, storage); - furi_string_free(real_path); + const char* path_cstr_no_vfs = cstr_path_without_vfs_prefix(path); + FS_CALL(storage, file.open(storage, file, path_cstr_no_vfs, access_mode, open_mode)); + } } return ret; @@ -262,27 +246,18 @@ static bool storage_process_file_eof(Storage* app, File* file) { /******************* Dir Functions *******************/ -bool storage_process_dir_open(Storage* app, File* file, const char* path) { +bool storage_process_dir_open(Storage* app, File* file, FuriString* path) { bool ret = false; - StorageType type = storage_get_type_by_path(app, path); StorageData* storage; - file->error_id = FSE_OK; + file->error_id = storage_get_data(app, path, &storage); - if(storage_type_is_not_valid(type)) { - file->error_id = FSE_INVALID_NAME; - } else { - storage = storage_get_storage_by_type(app, type); - FuriString* real_path; - real_path = furi_string_alloc_set(path); - storage_path_change_to_real_storage(real_path, type); - - if(storage_path_already_open(real_path, storage->files)) { + if(file->error_id == FSE_OK) { + if(storage_path_already_open(path, storage->files)) { file->error_id = FSE_ALREADY_OPEN; } else { - storage_push_storage_file(file, real_path, type, storage); - FS_CALL(storage, dir.open(storage, file, remove_vfs(path))); + storage_push_storage_file(file, path, storage); + FS_CALL(storage, dir.open(storage, file, cstr_path_without_vfs_prefix(path))); } - furi_string_free(real_path); } return ret; @@ -339,73 +314,52 @@ bool storage_process_dir_rewind(Storage* app, File* file) { /******************* Common FS Functions *******************/ static FS_Error - storage_process_common_timestamp(Storage* app, const char* path, uint32_t* timestamp) { - FS_Error ret = FSE_OK; - StorageType type = storage_get_type_by_path(app, path); + storage_process_common_timestamp(Storage* app, FuriString* path, uint32_t* timestamp) { + StorageData* storage; + FS_Error ret = storage_get_data(app, path, &storage); - if(storage_type_is_not_valid(type)) { - ret = FSE_INVALID_NAME; - } else { - StorageData* storage = storage_get_storage_by_type(app, type); + if(ret == FSE_OK) { *timestamp = storage_data_get_timestamp(storage); } return ret; } -static FS_Error storage_process_common_stat(Storage* app, const char* path, FileInfo* fileinfo) { - FS_Error ret = FSE_OK; - StorageType type = storage_get_type_by_path(app, path); +static FS_Error storage_process_common_stat(Storage* app, FuriString* path, FileInfo* fileinfo) { + StorageData* storage; + FS_Error ret = storage_get_data(app, path, &storage); - if(storage_type_is_not_valid(type)) { - ret = FSE_INVALID_NAME; - } else { - StorageData* storage = storage_get_storage_by_type(app, type); - FS_CALL(storage, common.stat(storage, remove_vfs(path), fileinfo)); + if(ret == FSE_OK) { + FS_CALL(storage, common.stat(storage, cstr_path_without_vfs_prefix(path), fileinfo)); } return ret; } -static FS_Error storage_process_common_remove(Storage* app, const char* path) { - FS_Error ret = FSE_OK; - StorageType type = storage_get_type_by_path(app, path); - - FuriString* real_path; - real_path = furi_string_alloc_set(path); - storage_path_change_to_real_storage(real_path, type); +static FS_Error storage_process_common_remove(Storage* app, FuriString* path) { + StorageData* storage; + FS_Error ret = storage_get_data(app, path, &storage); do { - if(storage_type_is_not_valid(type)) { - ret = FSE_INVALID_NAME; - break; - } - - StorageData* storage = storage_get_storage_by_type(app, type); - if(storage_path_already_open(real_path, storage->files)) { + if(storage_path_already_open(path, storage->files)) { ret = FSE_ALREADY_OPEN; break; } storage_data_timestamp(storage); - FS_CALL(storage, common.remove(storage, remove_vfs(path))); + FS_CALL(storage, common.remove(storage, cstr_path_without_vfs_prefix(path))); } while(false); - furi_string_free(real_path); - return ret; } -static FS_Error storage_process_common_mkdir(Storage* app, const char* path) { - FS_Error ret = FSE_OK; - StorageType type = storage_get_type_by_path(app, path); +static FS_Error storage_process_common_mkdir(Storage* app, FuriString* path) { + StorageData* storage; + FS_Error ret = storage_get_data(app, path, &storage); - if(storage_type_is_not_valid(type)) { - ret = FSE_INVALID_NAME; - } else { - StorageData* storage = storage_get_storage_by_type(app, type); + if(ret == FSE_OK) { storage_data_timestamp(storage); - FS_CALL(storage, common.mkdir(storage, remove_vfs(path))); + FS_CALL(storage, common.mkdir(storage, cstr_path_without_vfs_prefix(path))); } return ret; @@ -413,17 +367,16 @@ static FS_Error storage_process_common_mkdir(Storage* app, const char* path) { static FS_Error storage_process_common_fs_info( Storage* app, - const char* fs_path, + FuriString* path, uint64_t* total_space, uint64_t* free_space) { - FS_Error ret = FSE_OK; - StorageType type = storage_get_type_by_path(app, fs_path); + StorageData* storage; + FS_Error ret = storage_get_data(app, path, &storage); - if(storage_type_is_not_valid(type)) { - ret = FSE_INVALID_NAME; - } else { - StorageData* storage = storage_get_storage_by_type(app, type); - FS_CALL(storage, common.fs_info(storage, remove_vfs(fs_path), total_space, free_space)); + if(ret == FSE_OK) { + FS_CALL( + storage, + common.fs_info(storage, cstr_path_without_vfs_prefix(path), total_space, free_space)); } return ret; @@ -490,14 +443,52 @@ static FS_Error storage_process_sd_status(Storage* app) { return ret; } +/******************** Aliases processing *******************/ + +void storage_process_alias( + Storage* app, + FuriString* path, + FuriThreadId thread_id, + bool create_folders) { + if(furi_string_start_with(path, STORAGE_APP_DATA_PATH_PREFIX)) { + FuriString* apps_data_path_with_appsid = furi_string_alloc_set(APPS_DATA_PATH "/"); + furi_string_cat(apps_data_path_with_appsid, furi_thread_get_appid(thread_id)); + + // "/app" -> "/ext/apps_data/appsid" + furi_string_replace_at( + path, + 0, + strlen(STORAGE_APP_DATA_PATH_PREFIX), + furi_string_get_cstr(apps_data_path_with_appsid)); + + // Create app data folder if not exists + if(create_folders && + storage_process_common_stat(app, apps_data_path_with_appsid, NULL) != FSE_OK) { + furi_string_set(apps_data_path_with_appsid, APPS_DATA_PATH); + storage_process_common_mkdir(app, apps_data_path_with_appsid); + furi_string_cat(apps_data_path_with_appsid, "/"); + furi_string_cat(apps_data_path_with_appsid, furi_thread_get_appid(thread_id)); + storage_process_common_mkdir(app, apps_data_path_with_appsid); + } + + furi_string_free(apps_data_path_with_appsid); + } +} + /****************** API calls processing ******************/ + void storage_process_message_internal(Storage* app, StorageMessage* message) { + FuriString* path = NULL; + switch(message->command) { + // File operations case StorageCommandFileOpen: + path = furi_string_alloc_set(message->data->fopen.path); + storage_process_alias(app, path, message->data->fopen.thread_id, true); message->return_data->bool_value = storage_process_file_open( app, message->data->fopen.file, - message->data->fopen.path, + path, message->data->fopen.access_mode, message->data->fopen.open_mode); break; @@ -546,9 +537,12 @@ void storage_process_message_internal(Storage* app, StorageMessage* message) { message->return_data->bool_value = storage_process_file_eof(app, message->data->file.file); break; + // Dir operations case StorageCommandDirOpen: + path = furi_string_alloc_set(message->data->dopen.path); + storage_process_alias(app, path, message->data->dopen.thread_id, true); message->return_data->bool_value = - storage_process_dir_open(app, message->data->dopen.file, message->data->dopen.path); + storage_process_dir_open(app, message->data->dopen.file, path); break; case StorageCommandDirClose: message->return_data->bool_value = @@ -566,29 +560,42 @@ void storage_process_message_internal(Storage* app, StorageMessage* message) { message->return_data->bool_value = storage_process_dir_rewind(app, message->data->file.file); break; + + // Common operations case StorageCommandCommonTimestamp: - message->return_data->error_value = storage_process_common_timestamp( - app, message->data->ctimestamp.path, message->data->ctimestamp.timestamp); + path = furi_string_alloc_set(message->data->ctimestamp.path); + storage_process_alias(app, path, message->data->ctimestamp.thread_id, false); + message->return_data->error_value = + storage_process_common_timestamp(app, path, message->data->ctimestamp.timestamp); break; case StorageCommandCommonStat: - message->return_data->error_value = storage_process_common_stat( - app, message->data->cstat.path, message->data->cstat.fileinfo); + path = furi_string_alloc_set(message->data->cstat.path); + storage_process_alias(app, path, message->data->cstat.thread_id, false); + message->return_data->error_value = + storage_process_common_stat(app, path, message->data->cstat.fileinfo); break; case StorageCommandCommonRemove: - message->return_data->error_value = - storage_process_common_remove(app, message->data->path.path); + path = furi_string_alloc_set(message->data->path.path); + storage_process_alias(app, path, message->data->path.thread_id, false); + message->return_data->error_value = storage_process_common_remove(app, path); break; case StorageCommandCommonMkDir: - message->return_data->error_value = - storage_process_common_mkdir(app, message->data->path.path); + path = furi_string_alloc_set(message->data->path.path); + storage_process_alias(app, path, message->data->path.thread_id, true); + message->return_data->error_value = storage_process_common_mkdir(app, path); break; case StorageCommandCommonFSInfo: + path = furi_string_alloc_set(message->data->cfsinfo.fs_path); + storage_process_alias(app, path, message->data->cfsinfo.thread_id, false); message->return_data->error_value = storage_process_common_fs_info( - app, - message->data->cfsinfo.fs_path, - message->data->cfsinfo.total_space, - message->data->cfsinfo.free_space); + app, path, message->data->cfsinfo.total_space, message->data->cfsinfo.free_space); break; + case StorageCommandCommonResolvePath: + storage_process_alias( + app, message->data->cresolvepath.path, message->data->cresolvepath.thread_id, true); + break; + + // SD operations case StorageCommandSDFormat: message->return_data->error_value = storage_process_sd_format(app); break; @@ -604,7 +611,11 @@ void storage_process_message_internal(Storage* app, StorageMessage* message) { break; } - furi_semaphore_release(message->semaphore); + if(path != NULL) { //-V547 + furi_string_free(path); + } + + api_lock_unlock(message->lock); } void storage_process_message(Storage* app, StorageMessage* message) { diff --git a/applications/services/storage/storage_sd_api.h b/applications/services/storage/storage_sd_api.h index f83360955..880640394 100644 --- a/applications/services/storage/storage_sd_api.h +++ b/applications/services/storage/storage_sd_api.h @@ -23,6 +23,16 @@ typedef struct { uint16_t cluster_size; uint16_t sector_size; char label[SD_LABEL_LENGTH]; + + uint8_t manufacturer_id; + char oem_id[3]; + char product_name[6]; + uint8_t product_revision_major; + uint8_t product_revision_minor; + uint32_t product_serial_number; + uint8_t manufacturing_month; + uint16_t manufacturing_year; + FS_Error error; } SDInfo; diff --git a/applications/services/storage/storage_test_app.c b/applications/services/storage/storage_test_app.c deleted file mode 100644 index 852953e99..000000000 --- a/applications/services/storage/storage_test_app.c +++ /dev/null @@ -1,341 +0,0 @@ -#include -#include -#include - -#define TAG "StorageTest" -#define BYTES_COUNT 16 -#define TEST_STRING "TestDataStringProvidedByDiceRoll" -#define SEEK_OFFSET_FROM_START 10 -#define SEEK_OFFSET_INCREASE 12 -#define SEEK_OFFSET_SUM (SEEK_OFFSET_FROM_START + SEEK_OFFSET_INCREASE) - -static void do_file_test(Storage* api, const char* path) { - File* file = storage_file_alloc(api); - bool result; - uint8_t bytes[BYTES_COUNT + 1]; - uint8_t bytes_count; - uint64_t position; - uint64_t size; - - FURI_LOG_I(TAG, "--------- FILE \"%s\" ---------", path); - - // open - result = storage_file_open(file, path, FSAM_WRITE, FSOM_CREATE_ALWAYS); - if(result) { - FURI_LOG_I(TAG, "open"); - } else { - FURI_LOG_E(TAG, "open, %s", storage_file_get_error_desc(file)); - } - - // write - bytes_count = storage_file_write(file, TEST_STRING, strlen(TEST_STRING)); - if(bytes_count == 0) { - FURI_LOG_E(TAG, "write, %s", storage_file_get_error_desc(file)); - } else { - FURI_LOG_I(TAG, "write"); - } - - // sync - result = storage_file_sync(file); - if(result) { - FURI_LOG_I(TAG, "sync"); - } else { - FURI_LOG_E(TAG, "sync, %s", storage_file_get_error_desc(file)); - } - - // eof #1 - result = storage_file_eof(file); - if(result) { - FURI_LOG_I(TAG, "eof #1"); - } else { - FURI_LOG_E(TAG, "eof #1, %s", storage_file_get_error_desc(file)); - } - - // seek from start and tell - result = storage_file_seek(file, SEEK_OFFSET_FROM_START, true); - if(result) { - FURI_LOG_I(TAG, "seek #1"); - } else { - FURI_LOG_E(TAG, "seek #1, %s", storage_file_get_error_desc(file)); - } - position = storage_file_tell(file); - if(position != SEEK_OFFSET_FROM_START) { - FURI_LOG_E(TAG, "tell #1, %s", storage_file_get_error_desc(file)); - } else { - FURI_LOG_I(TAG, "tell #1"); - } - - // size - size = storage_file_size(file); - if(size != strlen(TEST_STRING)) { - FURI_LOG_E(TAG, "size #1, %s", storage_file_get_error_desc(file)); - } else { - FURI_LOG_I(TAG, "size #1"); - } - - // seek and tell - result = storage_file_seek(file, SEEK_OFFSET_INCREASE, false); - if(result) { - FURI_LOG_I(TAG, "seek #2"); - } else { - FURI_LOG_E(TAG, "seek #2, %s", storage_file_get_error_desc(file)); - } - position = storage_file_tell(file); - if(position != SEEK_OFFSET_SUM) { - FURI_LOG_E(TAG, "tell #2, %s", storage_file_get_error_desc(file)); - } else { - FURI_LOG_I(TAG, "tell #2"); - } - - // eof #2 - result = storage_file_eof(file); - if(!result) { - FURI_LOG_I(TAG, "eof #2"); - } else { - FURI_LOG_E(TAG, "eof #2, %s", storage_file_get_error_desc(file)); - } - - // truncate - result = storage_file_truncate(file); - if(result) { - FURI_LOG_I(TAG, "truncate"); - } else { - FURI_LOG_E(TAG, "truncate, %s", storage_file_get_error_desc(file)); - } - size = storage_file_size(file); - if(size != SEEK_OFFSET_SUM) { - FURI_LOG_E(TAG, "size #2, %s", storage_file_get_error_desc(file)); - } else { - FURI_LOG_I(TAG, "size #2"); - } - - // close - result = storage_file_close(file); - if(result) { - FURI_LOG_I(TAG, "close"); - } else { - FURI_LOG_E(TAG, "close, error"); - } - - // open - result = storage_file_open(file, path, FSAM_READ, FSOM_OPEN_EXISTING); - if(result) { - FURI_LOG_I(TAG, "open"); - } else { - FURI_LOG_E(TAG, "open, %s", storage_file_get_error_desc(file)); - } - - // read - memset(bytes, 0, BYTES_COUNT + 1); - bytes_count = storage_file_read(file, bytes, BYTES_COUNT); - if(bytes_count == 0) { - FURI_LOG_E(TAG, "read, %s", storage_file_get_error_desc(file)); - } else { - if(memcmp(TEST_STRING, bytes, bytes_count) == 0) { - FURI_LOG_I(TAG, "read"); - } else { - FURI_LOG_E(TAG, "read, garbage"); - } - } - - // close - result = storage_file_close(file); - if(result) { - FURI_LOG_I(TAG, "close"); - } else { - FURI_LOG_E(TAG, "close, error"); - } - - storage_file_free(file); -} - -static void do_dir_test(Storage* api, const char* path) { - File* file = storage_file_alloc(api); - bool result; - - FURI_LOG_I(TAG, "--------- DIR \"%s\" ---------", path); - - // open - result = storage_dir_open(file, path); - if(result) { - FURI_LOG_I(TAG, "open"); - } else { - FURI_LOG_E(TAG, "open, %s", storage_file_get_error_desc(file)); - } - - // read - const uint8_t filename_size = 100; - char* filename = malloc(filename_size); - FileInfo fileinfo; - - do { - result = storage_dir_read(file, &fileinfo, filename, filename_size); - if(result) { - if(strlen(filename)) { - FURI_LOG_I( - TAG, - "read #1, [%s]%s", - ((fileinfo.flags & FSF_DIRECTORY) ? "D" : "F"), - filename); - } - } else if(storage_file_get_error(file) != FSE_NOT_EXIST) { - FURI_LOG_E(TAG, "read #1, %s", storage_file_get_error_desc(file)); - break; - } - - } while(result); - - // rewind - result = storage_dir_rewind(file); - if(result) { - FURI_LOG_I(TAG, "rewind"); - } else { - FURI_LOG_E(TAG, "rewind, %s", storage_file_get_error_desc(file)); - } - - // read - do { - result = storage_dir_read(file, &fileinfo, filename, filename_size); - if(result) { - if(strlen(filename)) { - FURI_LOG_I( - TAG, - "read #2, [%s]%s", - ((fileinfo.flags & FSF_DIRECTORY) ? "D" : "F"), - filename); - } - } else if(storage_file_get_error(file) != FSE_NOT_EXIST) { - FURI_LOG_E(TAG, "read #2, %s", storage_file_get_error_desc(file)); - break; - } - - } while((strlen(filename))); - - // close - result = storage_dir_close(file); - if(result) { - FURI_LOG_I(TAG, "close"); - } else { - FURI_LOG_E(TAG, "close, error"); - } - - storage_file_free(file); - free(filename); -} - -static void do_test_start(Storage* api, const char* path) { - FuriString* str_path = furi_string_alloc_printf("%s/test-folder", path); - - FURI_LOG_I(TAG, "--------- START \"%s\" ---------", path); - - // mkdir - FS_Error result = storage_common_mkdir(api, furi_string_get_cstr(str_path)); - - if(result == FSE_OK) { - FURI_LOG_I(TAG, "mkdir ok"); - } else { - FURI_LOG_E(TAG, "mkdir, %s", storage_error_get_desc(result)); - } - - // stat - FileInfo fileinfo; - result = storage_common_stat(api, furi_string_get_cstr(str_path), &fileinfo); - - if(result == FSE_OK) { - if(fileinfo.flags & FSF_DIRECTORY) { - FURI_LOG_I(TAG, "stat #1 ok"); - } else { - FURI_LOG_E(TAG, "stat #1, %s", storage_error_get_desc(result)); - } - } else { - FURI_LOG_E(TAG, "stat #1, %s", storage_error_get_desc(result)); - } - - furi_string_free(str_path); -} - -static void do_test_end(Storage* api, const char* path) { - uint64_t total_space; - uint64_t free_space; - FuriString* str_path_1 = furi_string_alloc_printf("%s/test-folder", path); - FuriString* str_path_2 = furi_string_alloc_printf("%s/test-folder2", path); - - FURI_LOG_I(TAG, "--------- END \"%s\" ---------", path); - - // fs stat - FS_Error result = storage_common_fs_info(api, path, &total_space, &free_space); - - if(result == FSE_OK) { - uint32_t total_kb = total_space / 1024; - uint32_t free_kb = free_space / 1024; - FURI_LOG_I(TAG, "fs_info: total %luk, free %luk", total_kb, free_kb); - } else { - FURI_LOG_E(TAG, "fs_info, %s", storage_error_get_desc(result)); - } - - // rename #1 - result = storage_common_rename( - api, furi_string_get_cstr(str_path_1), furi_string_get_cstr(str_path_2)); - if(result == FSE_OK) { - FURI_LOG_I(TAG, "rename #1 ok"); - } else { - FURI_LOG_E(TAG, "rename #1, %s", storage_error_get_desc(result)); - } - - // remove #1 - result = storage_common_remove(api, furi_string_get_cstr(str_path_2)); - if(result == FSE_OK) { - FURI_LOG_I(TAG, "remove #1 ok"); - } else { - FURI_LOG_E(TAG, "remove #1, %s", storage_error_get_desc(result)); - } - - // rename #2 - furi_string_printf(str_path_1, "%s/test.txt", path); - furi_string_printf(str_path_2, "%s/test2.txt", path); - - result = storage_common_rename( - api, furi_string_get_cstr(str_path_1), furi_string_get_cstr(str_path_2)); - if(result == FSE_OK) { - FURI_LOG_I(TAG, "rename #2 ok"); - } else { - FURI_LOG_E(TAG, "rename #2, %s", storage_error_get_desc(result)); - } - - // remove #2 - result = storage_common_remove(api, furi_string_get_cstr(str_path_2)); - if(result == FSE_OK) { - FURI_LOG_I(TAG, "remove #2 ok"); - } else { - FURI_LOG_E(TAG, "remove #2, %s", storage_error_get_desc(result)); - } - - furi_string_free(str_path_1); - furi_string_free(str_path_2); -} - -int32_t storage_test_app(void* p) { - UNUSED(p); - Storage* api = furi_record_open(RECORD_STORAGE); - do_test_start(api, STORAGE_INT_PATH_PREFIX); - do_test_start(api, STORAGE_ANY_PATH_PREFIX); - do_test_start(api, STORAGE_EXT_PATH_PREFIX); - - do_file_test(api, INT_PATH("test.txt")); - do_file_test(api, ANY_PATH("test.txt")); - do_file_test(api, EXT_PATH("test.txt")); - - do_dir_test(api, STORAGE_INT_PATH_PREFIX); - do_dir_test(api, STORAGE_ANY_PATH_PREFIX); - do_dir_test(api, STORAGE_EXT_PATH_PREFIX); - - do_test_end(api, STORAGE_INT_PATH_PREFIX); - do_test_end(api, STORAGE_ANY_PATH_PREFIX); - do_test_end(api, STORAGE_EXT_PATH_PREFIX); - - while(true) { - furi_delay_ms(1000); - } - - return 0; -} diff --git a/applications/services/storage/storages/storage_ext.c b/applications/services/storage/storages/storage_ext.c index 0c81a0006..530c88f85 100644 --- a/applications/services/storage/storages/storage_ext.c +++ b/applications/services/storage/storages/storage_ext.c @@ -26,12 +26,10 @@ static FS_Error storage_ext_parse_error(SDError error); static bool sd_mount_card(StorageData* storage, bool notify) { bool result = false; - uint8_t counter = BSP_SD_MaxMountRetryCount(); + uint8_t counter = sd_max_mount_retry_count(); uint8_t bsp_result; SDData* sd_data = storage->data; - storage_data_lock(storage); - while(result == false && counter > 0 && hal_sd_detect()) { if(notify) { NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); @@ -41,9 +39,9 @@ static bool sd_mount_card(StorageData* storage, bool notify) { if((counter % 2) == 0) { // power reset sd card - bsp_result = BSP_SD_Init(true); + bsp_result = sd_init(true); } else { - bsp_result = BSP_SD_Init(false); + bsp_result = sd_init(false); } if(bsp_result) { @@ -91,7 +89,6 @@ static bool sd_mount_card(StorageData* storage, bool notify) { } storage_data_timestamp(storage); - storage_data_unlock(storage); return result; } @@ -100,14 +97,12 @@ FS_Error sd_unmount_card(StorageData* storage) { SDData* sd_data = storage->data; SDError error; - storage_data_lock(storage); storage->status = StorageStatusNotReady; error = FR_DISK_ERR; // TODO do i need to close the files? - f_mount(0, sd_data->path, 0); - storage_data_unlock(storage); + return storage_ext_parse_error(error); } @@ -120,8 +115,6 @@ FS_Error sd_format_card(StorageData* storage) { SDData* sd_data = storage->data; SDError error; - storage_data_lock(storage); - work_area = malloc(_MAX_SS); error = f_mkfs(sd_data->path, FM_ANY, 0, work_area, _MAX_SS); free(work_area); @@ -138,8 +131,6 @@ FS_Error sd_format_card(StorageData* storage) { storage->status = StorageStatusOK; } while(false); - storage_data_unlock(storage); - return storage_ext_parse_error(error); #endif } @@ -156,14 +147,12 @@ FS_Error sd_card_info(StorageData* storage, SDInfo* sd_info) { memset(sd_info, 0, sizeof(SDInfo)); // get fs info - storage_data_lock(storage); error = f_getlabel(sd_data->path, sd_info->label, NULL); if(error == FR_OK) { #ifndef FURI_RAM_EXEC error = f_getfree(sd_data->path, &free_clusters, &fs); #endif } - storage_data_unlock(storage); if(error == FR_OK) { // calculate size @@ -210,6 +199,20 @@ FS_Error sd_card_info(StorageData* storage, SDInfo* sd_info) { #endif } + SD_CID cid; + SdSpiStatus status = sd_get_cid(&cid); + + if(status == SdSpiStatusOK) { + sd_info->manufacturer_id = cid.ManufacturerID; + memcpy(sd_info->oem_id, cid.OEM_AppliID, sizeof(cid.OEM_AppliID)); + memcpy(sd_info->product_name, cid.ProdName, sizeof(cid.ProdName)); + sd_info->product_revision_major = cid.ProdRev >> 4; + sd_info->product_revision_minor = cid.ProdRev & 0x0F; + sd_info->product_serial_number = cid.ProdSN; + sd_info->manufacturing_year = 2000 + cid.ManufactYear; + sd_info->manufacturing_month = cid.ManufactMonth; + } + return storage_ext_parse_error(error); } diff --git a/applications/services/xtreme/application.fam b/applications/services/xtreme/application.fam index 2dfcab051..de90d455f 100644 --- a/applications/services/xtreme/application.fam +++ b/applications/services/xtreme/application.fam @@ -1,10 +1,14 @@ App( appid="xtreme", - apptype=FlipperAppType.STARTUP, - entry_point="xtreme_on_system_start", + name="Xtreme", + apptype=FlipperAppType.SERVICE, + entry_point="xtreme_srv", + cdefines=["SRV_XTREME"], requires=["storage"], - order=1000, - provides=[ - "xtreme", + stack_size=1 * 1024, + order=46, + sdk_headers=[ + "settings.h", + "assets.h", ], ) diff --git a/applications/services/xtreme/assets.c b/applications/services/xtreme/assets.c index 1862f1ec7..ce73e4f87 100644 --- a/applications/services/xtreme/assets.c +++ b/applications/services/xtreme/assets.c @@ -2,6 +2,8 @@ #include #include +#define TAG "XtremeAssets" + #define ICONS_FMT PACKS_DIR "/%s/Icons/%s" XtremeAssets* xtreme_assets = NULL; @@ -100,6 +102,7 @@ void swap(XtremeAssets* x, FuriString* p, File* f) { icon(&x->I_RFIDDolphinSend_97x61, "RFID/RFIDDolphinSend_97x61", p, f); icon(&x->I_RFIDDolphinSuccess_108x57, "RFID/RFIDDolphinSuccess_108x57", p, f); icon(&x->I_Cry_dolph_55x52, "Settings/Cry_dolph_55x52", p, f); + icon(&x->I_Fishing_123x52, "SubGhz/Fishing_123x52", p, f); icon(&x->I_Scanning_123x52, "SubGhz/Scanning_123x52", p, f); icon(&x->I_Auth_62x31, "U2F/Auth_62x31", p, f); icon(&x->I_Connect_me_62x31, "U2F/Connect_me_62x31", p, f); @@ -111,7 +114,6 @@ void XTREME_ASSETS_LOAD() { if(xtreme_assets != NULL) return; xtreme_assets = malloc(sizeof(XtremeAssets)); - XtremeSettings* xtreme_settings = XTREME_SETTINGS(); xtreme_assets->A_Levelup_128x64 = &A_Levelup_128x64; xtreme_assets->I_BLE_Pairing_128x64 = &I_BLE_Pairing_128x64; @@ -130,26 +132,41 @@ void XTREME_ASSETS_LOAD() { xtreme_assets->I_RFIDDolphinSend_97x61 = &I_RFIDDolphinSend_97x61; xtreme_assets->I_RFIDDolphinSuccess_108x57 = &I_RFIDDolphinSuccess_108x57; xtreme_assets->I_Cry_dolph_55x52 = &I_Cry_dolph_55x52; + xtreme_assets->I_Fishing_123x52 = &I_Fishing_123x52; xtreme_assets->I_Scanning_123x52 = &I_Scanning_123x52; xtreme_assets->I_Auth_62x31 = &I_Auth_62x31; xtreme_assets->I_Connect_me_62x31 = &I_Connect_me_62x31; xtreme_assets->I_Connected_62x31 = &I_Connected_62x31; xtreme_assets->I_Error_62x31 = &I_Error_62x31; + if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) { + FURI_LOG_W(TAG, "Load skipped. Device is in special startup mode."); + return; + } + + XtremeSettings* xtreme_settings = XTREME_SETTINGS(); if(xtreme_settings->asset_pack[0] == '\0') return; xtreme_assets->is_nsfw = strncmp(xtreme_settings->asset_pack, "NSFW", strlen("NSFW")) == 0; + + Storage* storage = furi_record_open(RECORD_STORAGE); + int32_t timeout = 5000; + while(timeout > 0) { + if(storage_sd_status(storage) == FSE_OK) break; + furi_delay_ms(250); + timeout -= 250; + } + FileInfo info; FuriString* path = furi_string_alloc(); furi_string_printf(path, PACKS_DIR "/%s", xtreme_settings->asset_pack); - Storage* storage = furi_record_open(RECORD_STORAGE); if(storage_common_stat(storage, furi_string_get_cstr(path), &info) == FSE_OK && info.flags & FSF_DIRECTORY) { File* file = storage_file_alloc(storage); swap(xtreme_assets, path, file); storage_file_free(file); } - furi_record_close(RECORD_STORAGE); furi_string_free(path); + furi_record_close(RECORD_STORAGE); } XtremeAssets* XTREME_ASSETS() { diff --git a/applications/services/xtreme/assets.h b/applications/services/xtreme/assets.h index b88a6cf1e..f987fd475 100644 --- a/applications/services/xtreme/assets.h +++ b/applications/services/xtreme/assets.h @@ -4,6 +4,10 @@ #include #include +#ifdef __cplusplus +extern "C" { +#endif + #define PACKS_DIR EXT_PATH("dolphin_custom") typedef struct { @@ -25,6 +29,7 @@ typedef struct { const Icon* I_RFIDDolphinSend_97x61; const Icon* I_RFIDDolphinSuccess_108x57; const Icon* I_Cry_dolph_55x52; + const Icon* I_Fishing_123x52; const Icon* I_Scanning_123x52; const Icon* I_Auth_62x31; const Icon* I_Connect_me_62x31; @@ -35,3 +40,7 @@ typedef struct { void XTREME_ASSETS_LOAD(); XtremeAssets* XTREME_ASSETS(); + +#ifdef __cplusplus +} +#endif diff --git a/applications/services/xtreme/settings.c b/applications/services/xtreme/settings.c index eea68bd2b..bfc3629f4 100644 --- a/applications/services/xtreme/settings.c +++ b/applications/services/xtreme/settings.c @@ -1,5 +1,7 @@ #include "settings.h" +#define TAG "XtremeSettings" + XtremeSettings* xtreme_settings = NULL; XtremeSettings* XTREME_SETTINGS() { @@ -9,28 +11,57 @@ XtremeSettings* XTREME_SETTINGS() { return xtreme_settings; } -bool XTREME_SETTINGS_LOAD() { +void XTREME_SETTINGS_LOAD() { if(xtreme_settings == NULL) { xtreme_settings = malloc(sizeof(XtremeSettings)); - bool loaded = saved_struct_load( - XTREME_SETTINGS_PATH, - xtreme_settings, - sizeof(XtremeSettings), - XTREME_SETTINGS_MAGIC, - XTREME_SETTINGS_VERSION); - if(!loaded) { - memset(xtreme_settings, 0, sizeof(XtremeSettings)); - loaded = XTREME_SETTINGS_SAVE(); + bool loaded = false; + bool skip = furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal; + + if(skip) { + FURI_LOG_W(TAG, "Load skipped. Device is in special startup mode."); + loaded = false; + } else { + loaded = saved_struct_load( + XTREME_SETTINGS_PATH, + xtreme_settings, + sizeof(XtremeSettings), + XTREME_SETTINGS_MAGIC, + XTREME_SETTINGS_VERSION); + } + + if(!loaded) { + if(!skip) { + storage_simply_remove(furi_record_open(RECORD_STORAGE), XTREME_SETTINGS_PATH_OLD); + furi_record_close(RECORD_STORAGE); + } + memset(xtreme_settings, 0, sizeof(XtremeSettings)); + strlcpy(xtreme_settings->asset_pack, "", MAX_PACK_NAME_LEN); // SFW + xtreme_settings->anim_speed = 100; // 100% + xtreme_settings->cycle_anims = 0; // Meta.txt + xtreme_settings->unlock_anims = false; // OFF + xtreme_settings->battery_icon = BatteryIconBarPercent; // Bar % + xtreme_settings->status_icons = true; // ON + xtreme_settings->bar_borders = true; // ON + xtreme_settings->bar_background = false; // OFF + xtreme_settings->bad_bt = false; // USB + xtreme_settings->bad_bt_remember = false; // OFF + xtreme_settings->butthurt_timer = 43200; // 12 H + xtreme_settings->sort_dirs_first = true; // ON + xtreme_settings->dark_mode = false; // OFF + xtreme_settings->left_handed = false; // OFF } - return loaded; } - return true; } bool XTREME_SETTINGS_SAVE() { if(xtreme_settings == NULL) { XTREME_SETTINGS_LOAD(); } + + if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) { + return true; + } + return saved_struct_save( XTREME_SETTINGS_PATH, xtreme_settings, diff --git a/applications/services/xtreme/settings.h b/applications/services/xtreme/settings.h index b4f12b1b8..562a37c73 100644 --- a/applications/services/xtreme/settings.h +++ b/applications/services/xtreme/settings.h @@ -9,10 +9,15 @@ #include #include +#ifdef __cplusplus +extern "C" { +#endif + #define MAX_PACK_NAME_LEN 32 -#define XTREME_SETTINGS_VERSION (2) -#define XTREME_SETTINGS_PATH INT_PATH(XTREME_SETTINGS_FILE_NAME) +#define XTREME_SETTINGS_VERSION (5) +#define XTREME_SETTINGS_PATH_OLD INT_PATH(XTREME_SETTINGS_FILE_NAME) +#define XTREME_SETTINGS_PATH EXT_PATH(XTREME_SETTINGS_FILE_NAME) #define XTREME_SETTINGS_MAGIC (0x69) // Some settings function backwards (logically) in @@ -28,11 +33,19 @@ typedef struct { bool bar_borders; bool bar_background; bool bad_bt; - bool sort_ignore_dirs; + bool bad_bt_remember; + int32_t butthurt_timer; + bool sort_dirs_first; + bool dark_mode; + bool left_handed; } XtremeSettings; XtremeSettings* XTREME_SETTINGS(); -bool XTREME_SETTINGS_LOAD(); +void XTREME_SETTINGS_LOAD(); bool XTREME_SETTINGS_SAVE(); + +#ifdef __cplusplus +} +#endif diff --git a/applications/services/xtreme/on_system_start.c b/applications/services/xtreme/xtreme_srv.c similarity index 61% rename from applications/services/xtreme/on_system_start.c rename to applications/services/xtreme/xtreme_srv.c index 531888f1b..fa6fb97f2 100644 --- a/applications/services/xtreme/on_system_start.c +++ b/applications/services/xtreme/xtreme_srv.c @@ -1,7 +1,11 @@ #include "settings.h" #include "assets.h" -void xtreme_on_system_start() { +int32_t xtreme_srv(void* p) { + UNUSED(p); + XTREME_SETTINGS_LOAD(); XTREME_ASSETS_LOAD(); + + return 0; } diff --git a/applications/settings/about/about.c b/applications/settings/about/about.c index 4172214a9..e0fa8204e 100644 --- a/applications/settings/about/about.c +++ b/applications/settings/about/about.c @@ -3,18 +3,13 @@ #include #include #include -#include #include #include #include #include -#include int screen_index; -#define LOW_CHARGE_THRESHOLD 10 -#define HIGH_DRAIN_CURRENT_THRESHOLD 100 - typedef DialogMessageButton (*AboutDialogScreen)(DialogsApp* dialogs, DialogMessage* message); static DialogMessageButton product_screen(DialogsApp* dialogs, DialogMessage* message) { @@ -108,7 +103,7 @@ static DialogMessageButton hw_version_screen(DialogsApp* dialogs, DialogMessage* furi_hal_version_get_hw_target(), furi_hal_version_get_hw_body(), furi_hal_version_get_hw_connect(), - furi_hal_version_get_hw_region_name(), + furi_hal_version_get_hw_region_name_otp(), furi_hal_region_get_name(), my_name ? my_name : "Unknown"); @@ -170,6 +165,13 @@ static DialogMessageButton fw_version_screen(DialogsApp* dialogs, DialogMessage* return result; } +#include +#include +#include + +#define LOW_CHARGE_THRESHOLD 10 +#define HIGH_DRAIN_CURRENT_THRESHOLD 100 + static void draw_stat(Canvas* canvas, int x, int y, const Icon* icon, char* val) { canvas_draw_frame(canvas, x - 7, y + 7, 30, 13); canvas_draw_icon(canvas, x, y, icon); @@ -221,15 +223,15 @@ static void draw_battery(Canvas* canvas, PowerInfo* info, int x, int y) { drain_current > HIGH_DRAIN_CURRENT_THRESHOLD ? "mA!" : "mA"); } else if(drain_current != 0) { snprintf(header, 20, "..."); - } else if(info->voltage_battery_charging < 4.2) { + } else if(info->voltage_battery_charge_limit < 4.2) { // Non-default battery charging limit, mention it snprintf(header, sizeof(header), "Limited to"); snprintf( value, sizeof(value), "%lu.%luV", - (uint32_t)(info->voltage_battery_charging), - (uint32_t)(info->voltage_battery_charging * 10) % 10); + (uint32_t)(info->voltage_battery_charge_limit), + (uint32_t)(info->voltage_battery_charge_limit * 10) % 10); } else { snprintf(header, sizeof(header), "Charged!"); } @@ -258,7 +260,15 @@ static void battery_info_draw_callback(Canvas* canvas, void* context) { char health[10]; snprintf(batt_level, sizeof(batt_level), "%lu%%", (uint32_t)info->charge); - snprintf(temperature, sizeof(temperature), "%lu C", (uint32_t)info->temperature_gauge); + if(locale_get_measurement_unit() == LocaleMeasurementUnitsMetric) { + snprintf(temperature, sizeof(temperature), "%lu C", (uint32_t)info->temperature_gauge); + } else { + snprintf( + temperature, + sizeof(temperature), + "%lu F", + (uint32_t)locale_celsius_to_fahrenheit(info->temperature_gauge)); + } snprintf( voltage, sizeof(voltage), diff --git a/applications/settings/application.fam b/applications/settings/application.fam index 49695b4b3..cc4b9703d 100644 --- a/applications/settings/application.fam +++ b/applications/settings/application.fam @@ -5,7 +5,6 @@ App( provides=[ "passport", "system_settings", - "xtreme_app", "about", ], ) diff --git a/applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_confirm.c b/applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_confirm.c index 31921b9f3..c912f2e5d 100644 --- a/applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_confirm.c +++ b/applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_confirm.c @@ -1,5 +1,6 @@ #include "../bt_settings_app.h" #include +#include void bt_settings_scene_forget_dev_confirm_dialog_callback(DialogExResult result, void* context) { furi_assert(context); @@ -30,6 +31,11 @@ bool bt_settings_scene_forget_dev_confirm_on_event(void* context, SceneManagerEv consumed = scene_manager_previous_scene(app->scene_manager); } else if(event.event == DialogExResultRight) { bt_forget_bonded_devices(app->bt); + // also removes keys of badkb bonded devices + bt_keys_storage_set_storage_path(app->bt, BAD_KB_APP_PATH_BOUND_KEYS_FILE); + bt_forget_bonded_devices(app->bt); + bt_keys_storage_set_default_path(app->bt); + scene_manager_next_scene(app->scene_manager, BtSettingsAppSceneForgetDevSuccess); consumed = true; } diff --git a/applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_success.c b/applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_success.c index 59071959c..c9e4b17d4 100644 --- a/applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_success.c +++ b/applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_success.c @@ -1,5 +1,4 @@ #include "../bt_settings_app.h" -#include "furi_hal_bt.h" #include "xtreme/assets.h" #include 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 519841f7d..94c5ee9f0 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_favorite.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_favorite.c @@ -69,7 +69,7 @@ void desktop_settings_scene_favorite_on_enter(void* context) { } submenu_set_header( - submenu, primary_favorite ? "Secondary favorite app:" : "Primary favorite app:"); + submenu, primary_favorite ? "Primary favorite app:" : "Secondary favorite app:"); submenu_set_selected_item(submenu, pre_select_item); // If set during loop, visual glitch. view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewMenu); diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_start.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_start.c index 33b7fd045..a3ebe5ffc 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_start.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_start.c @@ -39,6 +39,14 @@ static void desktop_settings_scene_start_auto_lock_delay_changed(VariableItem* i app->settings.auto_lock_delay_ms = auto_lock_delay_value[index]; } +static void desktop_settings_scene_start_auto_lock_pin_changed(VariableItem* item) { + DesktopSettingsApp* app = variable_item_get_context(item); + uint8_t value = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, value ? "ON" : "OFF"); + app->settings.auto_lock_with_pin = value; +} + void desktop_settings_scene_start_on_enter(void* context) { DesktopSettingsApp* app = context; VariableItemList* variable_item_list = app->variable_item_list; @@ -46,9 +54,9 @@ void desktop_settings_scene_start_on_enter(void* context) { VariableItem* item; uint8_t value_index; - variable_item_list_add(variable_item_list, "Primary Favorite App", 1, NULL, NULL); + variable_item_list_add(variable_item_list, "Primary Fav App (Up)", 1, NULL, NULL); - variable_item_list_add(variable_item_list, "Secondary Favorite App", 1, NULL, NULL); + variable_item_list_add(variable_item_list, "Secondary Fav App (Down)", 1, NULL, NULL); // variable_item_list_add(variable_item_list, "Favorite Game", 1, NULL, NULL); @@ -66,6 +74,16 @@ void desktop_settings_scene_start_on_enter(void* context) { variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, auto_lock_delay_text[value_index]); + item = variable_item_list_add( + variable_item_list, + "Auto Lock Pin", + 2, + desktop_settings_scene_start_auto_lock_pin_changed, + app); + + variable_item_set_current_value_index(item, app->settings.auto_lock_with_pin); + variable_item_set_current_value_text(item, app->settings.auto_lock_with_pin ? "ON" : "OFF"); + variable_item_list_set_enter_callback( variable_item_list, desktop_settings_scene_start_var_list_enter_callback, app); view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewVarItemList); @@ -78,12 +96,14 @@ bool desktop_settings_scene_start_on_event(void* context, SceneManagerEvent sme) if(sme.type == SceneManagerEventTypeCustom) { switch(sme.event) { case SCENE_EVENT_SELECT_FAVORITE_PRIMARY: - scene_manager_set_scene_state(app->scene_manager, DesktopSettingsAppSceneFavorite, 0); + scene_manager_set_scene_state( + app->scene_manager, DesktopSettingsAppSceneFavorite, true); scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite); consumed = true; break; case SCENE_EVENT_SELECT_FAVORITE_SECONDARY: - scene_manager_set_scene_state(app->scene_manager, DesktopSettingsAppSceneFavorite, 1); + scene_manager_set_scene_state( + app->scene_manager, DesktopSettingsAppSceneFavorite, false); scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite); consumed = true; break; diff --git a/applications/settings/notification_settings/notification_settings_app.c b/applications/settings/notification_settings/notification_settings_app.c index db9a1a01f..23ed4ac67 100644 --- a/applications/settings/notification_settings/notification_settings_app.c +++ b/applications/settings/notification_settings/notification_settings_app.c @@ -20,20 +20,14 @@ static const NotificationSequence sequence_note_c = { NULL, }; -#define BACKLIGHT_COUNT 5 +#define BACKLIGHT_COUNT 21 const char* const backlight_text[BACKLIGHT_COUNT] = { - "0%", - "25%", - "50%", - "75%", - "100%", + "0%", "5%", "10%", "15%", "20%", "25%", "30%", "35%", "40%", "45%", "50%", + "55%", "60%", "65%", "70%", "75%", "80%", "85%", "90%", "95%", "100%", }; const float backlight_value[BACKLIGHT_COUNT] = { - 0.0f, - 0.25f, - 0.5f, - 0.75f, - 1.0f, + 0.00f, 0.05f, 0.10f, 0.15f, 0.20f, 0.25f, 0.30f, 0.35f, 0.40f, 0.45f, 0.50f, + 0.55f, 0.60f, 0.65f, 0.70f, 0.75f, 0.80f, 0.85f, 0.90f, 0.95f, 1.00f, }; #define VOLUME_COUNT 5 diff --git a/applications/settings/power_settings_app/power_settings_app.c b/applications/settings/power_settings_app/power_settings_app.c index 016439c0d..8843a31ed 100644 --- a/applications/settings/power_settings_app/power_settings_app.c +++ b/applications/settings/power_settings_app/power_settings_app.c @@ -42,11 +42,11 @@ PowerSettingsApp* power_settings_app_alloc(uint32_t first_scene) { view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); // Views - app->batery_info = battery_info_alloc(); + app->battery_info = battery_info_alloc(); view_dispatcher_add_view( app->view_dispatcher, PowerSettingsAppViewBatteryInfo, - battery_info_get_view(app->batery_info)); + battery_info_get_view(app->battery_info)); app->submenu = submenu_alloc(); app->variable_item_list = variable_item_list_alloc(); view_dispatcher_add_view( @@ -68,7 +68,7 @@ void power_settings_app_free(PowerSettingsApp* app) { furi_assert(app); // Views view_dispatcher_remove_view(app->view_dispatcher, PowerSettingsAppViewBatteryInfo); - battery_info_free(app->batery_info); + battery_info_free(app->battery_info); view_dispatcher_remove_view(app->view_dispatcher, PowerSettingsAppViewSubmenu); submenu_free(app->submenu); diff --git a/applications/settings/power_settings_app/power_settings_app.h b/applications/settings/power_settings_app/power_settings_app.h index d30cbaf43..65f7f7ab1 100644 --- a/applications/settings/power_settings_app/power_settings_app.h +++ b/applications/settings/power_settings_app/power_settings_app.h @@ -21,7 +21,7 @@ typedef struct { Gui* gui; SceneManager* scene_manager; ViewDispatcher* view_dispatcher; - BatteryInfo* batery_info; + BatteryInfo* battery_info; Submenu* submenu; DialogEx* dialog; PowerInfo info; diff --git a/applications/settings/power_settings_app/scenes/power_settings_scene_battery_info.c b/applications/settings/power_settings_app/scenes/power_settings_scene_battery_info.c index 5fa38df72..e0186a5a6 100644 --- a/applications/settings/power_settings_app/scenes/power_settings_scene_battery_info.c +++ b/applications/settings/power_settings_app/scenes/power_settings_scene_battery_info.c @@ -7,11 +7,11 @@ static void power_settings_scene_battery_info_update_model(PowerSettingsApp* app .gauge_voltage = app->info.voltage_gauge, .gauge_current = app->info.current_gauge, .gauge_temperature = app->info.temperature_gauge, - .charging_voltage = app->info.voltage_battery_charging, + .charge_voltage_limit = app->info.voltage_battery_charge_limit, .charge = app->info.charge, .health = app->info.health, }; - battery_info_set_data(app->batery_info, &battery_info_data); + battery_info_set_data(app->battery_info, &battery_info_data); } void power_settings_scene_battery_info_on_enter(void* context) { diff --git a/applications/settings/power_settings_app/views/battery_info.c b/applications/settings/power_settings_app/views/battery_info.c index d29769d21..7394fd3c5 100644 --- a/applications/settings/power_settings_app/views/battery_info.c +++ b/applications/settings/power_settings_app/views/battery_info.c @@ -69,7 +69,7 @@ static void draw_battery(Canvas* canvas, BatteryInfoModel* data, int x, int y) { drain_current > HIGH_DRAIN_CURRENT_THRESHOLD ? "mA!" : "mA"); } else if(drain_current != 0) { snprintf(header, 20, "..."); - } else if(data->charging_voltage < 4.2) { + } else if(data->charge_voltage_limit < 4.2) { // Non-default battery charging limit, mention it snprintf(emote, sizeof(emote), "Charged!"); snprintf(header, sizeof(header), "Limited to"); @@ -77,8 +77,8 @@ static void draw_battery(Canvas* canvas, BatteryInfoModel* data, int x, int y) { value, sizeof(value), "%lu.%luV", - (uint32_t)(data->charging_voltage), - (uint32_t)(data->charging_voltage * 10) % 10); + (uint32_t)(data->charge_voltage_limit), + (uint32_t)(data->charge_voltage_limit * 10) % 10); } else { snprintf(header, sizeof(header), "Charged!"); } diff --git a/applications/settings/power_settings_app/views/battery_info.h b/applications/settings/power_settings_app/views/battery_info.h index 7bfacf69e..e52d1844c 100644 --- a/applications/settings/power_settings_app/views/battery_info.h +++ b/applications/settings/power_settings_app/views/battery_info.h @@ -9,7 +9,7 @@ typedef struct { float gauge_voltage; float gauge_current; float gauge_temperature; - float charging_voltage; + float charge_voltage_limit; uint8_t charge; uint8_t health; } BatteryInfoModel; diff --git a/applications/settings/storage_settings/scenes/storage_settings_scene_benchmark.c b/applications/settings/storage_settings/scenes/storage_settings_scene_benchmark.c index 71a3df78b..8359c00be 100644 --- a/applications/settings/storage_settings/scenes/storage_settings_scene_benchmark.c +++ b/applications/settings/storage_settings/scenes/storage_settings_scene_benchmark.c @@ -103,6 +103,9 @@ static void storage_settings_scene_benchmark(StorageSettings* app) { break; furi_string_cat_printf(app->text_string, "R %luK", bench_r_speed[i]); + + storage_common_remove(app->fs_api, BENCH_FILE); + dialog_ex_set_text( dialog_ex, furi_string_get_cstr(app->text_string), 0, 32, AlignLeft, AlignCenter); } 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 4901e960e..a3327aada 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 @@ -1,5 +1,4 @@ #include "../storage_settings.h" -#include static void storage_settings_scene_sd_info_dialog_callback(DialogExResult result, void* context) { StorageSettings* app = context; @@ -12,9 +11,7 @@ void storage_settings_scene_sd_info_on_enter(void* context) { DialogEx* dialog_ex = app->dialog_ex; SDInfo sd_info; - SD_CID sd_cid; FS_Error sd_status = storage_sd_info(app->fs_api, &sd_info); - BSP_SD_GetCIDRegister(&sd_cid); scene_manager_set_scene_state(app->scene_manager, StorageSettingsSDInfo, sd_status); @@ -28,52 +25,38 @@ void storage_settings_scene_sd_info_on_enter(void* context) { dialog_ex, "Try to reinsert\nor format SD\ncard.", 3, 19, AlignLeft, AlignTop); dialog_ex_set_center_button_text(dialog_ex, "Ok"); } else { - char unit_kb[] = "KB"; - char unit_mb[] = "MB"; - char unit_gb[] = "GB"; - - double sd_total_val = (double)sd_info.kb_total; - char* sd_total_unit = unit_kb; - double sd_free_val = (double)sd_info.kb_free; - char* sd_free_unit = unit_kb; - - if(sd_total_val > 1024) { - sd_total_val /= 1024; - sd_total_unit = unit_mb; + double total_v = (double)sd_info.kb_total; + double free_v = (double)sd_info.kb_free; + char* units[] = {"KiB", "MiB", "GiB", "TiB"}; + uint total_i, free_i; + for(total_i = 0; total_i < COUNT_OF(units); total_i++) { + if(total_v < 1024) break; + total_v /= 1024; } - if(sd_total_val > 1024) { - sd_total_val /= 1024; - sd_total_unit = unit_gb; - } - - if(sd_free_val > 1024) { - sd_free_val /= 1024; - sd_free_unit = unit_mb; - } - if(sd_free_val > 1024) { - sd_free_val /= 1024; - sd_free_unit = unit_gb; + for(free_i = 0; free_i < COUNT_OF(units); free_i++) { + if(free_v < 1024) break; + free_v /= 1024; } furi_string_printf( app->text_string, "Label: %s\nType: %s\n%.2f %s total\n%.2f %s free %.2f%% free\n" - "%02X%2.2s %5.5s %i.%i\nSN:%04lX %02i/%i", + "%02X%s %s v%i.%i\nSN:%04lX %02i/%i", sd_info.label, sd_api_get_fs_type_text(sd_info.fs_type), - sd_total_val, - sd_total_unit, - sd_free_val, - sd_free_unit, + total_v, + units[total_i], + free_v, + units[free_i], (double)(((int)sd_info.kb_free * 100.0) / (int)sd_info.kb_total), - sd_cid.ManufacturerID, - sd_cid.OEM_AppliID, - sd_cid.ProdName, - sd_cid.ProdRev >> 4, - sd_cid.ProdRev & 0xf, - sd_cid.ProdSN, - sd_cid.ManufactMonth, - sd_cid.ManufactYear + 2000); + sd_info.manufacturer_id, + sd_info.oem_id, + sd_info.product_name, + sd_info.product_revision_major, + sd_info.product_revision_minor, + sd_info.product_serial_number, + sd_info.manufacturing_month, + sd_info.manufacturing_year); dialog_ex_set_text( dialog_ex, furi_string_get_cstr(app->text_string), 4, 1, AlignLeft, AlignTop); } @@ -112,4 +95,4 @@ void storage_settings_scene_sd_info_on_exit(void* context) { dialog_ex_reset(dialog_ex); furi_string_reset(app->text_string); -} \ No newline at end of file +} diff --git a/applications/settings/system/system_settings.c b/applications/settings/system/system_settings.c index 5eade2115..f9abdb693 100644 --- a/applications/settings/system/system_settings.c +++ b/applications/settings/system/system_settings.c @@ -71,21 +71,21 @@ static void heap_trace_mode_changed(VariableItem* item) { furi_hal_rtc_set_heap_track_mode(heap_trace_mode_value[index]); } -const char* const mesurement_units_text[] = { +const char* const measurement_units_text[] = { "Metric", "Imperial", }; -const uint32_t mesurement_units_value[] = { +const uint32_t measurement_units_value[] = { LocaleMeasurementUnitsMetric, LocaleMeasurementUnitsImperial, }; -static void mesurement_units_changed(VariableItem* item) { +static void measurement_units_changed(VariableItem* item) { // SystemSettings* app = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); - variable_item_set_current_value_text(item, mesurement_units_text[index]); - locale_set_measurement_unit(mesurement_units_value[index]); + variable_item_set_current_value_text(item, measurement_units_text[index]); + locale_set_measurement_unit(measurement_units_value[index]); } const char* const time_format_text[] = { @@ -148,13 +148,13 @@ SystemSettings* system_settings_alloc() { item = variable_item_list_add( app->var_item_list, "Units", - COUNT_OF(mesurement_units_text), - mesurement_units_changed, + COUNT_OF(measurement_units_text), + measurement_units_changed, app); value_index = value_index_uint32( - locale_get_measurement_unit(), mesurement_units_value, COUNT_OF(mesurement_units_value)); + locale_get_measurement_unit(), measurement_units_value, COUNT_OF(measurement_units_value)); variable_item_set_current_value_index(item, value_index); - variable_item_set_current_value_text(item, mesurement_units_text[value_index]); + variable_item_set_current_value_text(item, measurement_units_text[value_index]); item = variable_item_list_add( app->var_item_list, "Time Format", COUNT_OF(time_format_text), time_format_changed, app); diff --git a/applications/settings/xtreme_app/application.fam b/applications/settings/xtreme_app/application.fam deleted file mode 100644 index e1b7fc964..000000000 --- a/applications/settings/xtreme_app/application.fam +++ /dev/null @@ -1,12 +0,0 @@ -App( - appid="xtreme_app", - name="Xtreme FW", - apptype=FlipperAppType.SETTINGS, - entry_point="xtreme_app", - stack_size=2 * 1024, - requires=[ - "gui", - "xtreme", - ], - order=90, -) diff --git a/applications/settings/xtreme_app/scenes/xtreme_app_scene_config.h b/applications/settings/xtreme_app/scenes/xtreme_app_scene_config.h deleted file mode 100644 index 9eed63575..000000000 --- a/applications/settings/xtreme_app/scenes/xtreme_app_scene_config.h +++ /dev/null @@ -1 +0,0 @@ -ADD_SCENE(xtreme_app, main, Main) diff --git a/applications/settings/xtreme_app/scenes/xtreme_app_scene_main.c b/applications/settings/xtreme_app/scenes/xtreme_app_scene_main.c deleted file mode 100644 index 03602bb58..000000000 --- a/applications/settings/xtreme_app/scenes/xtreme_app_scene_main.c +++ /dev/null @@ -1,319 +0,0 @@ -#include "../xtreme_app.h" -#include -#include -#include - -static void xtreme_app_scene_main_asset_pack_changed(VariableItem* item) { - XtremeApp* app = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - variable_item_set_current_value_text( - item, index == 0 ? "SFW" : *asset_packs_get(app->asset_packs, index - 1)); - strlcpy( - XTREME_SETTINGS()->asset_pack, - index == 0 ? "" : *asset_packs_get(app->asset_packs, index - 1), - MAX_PACK_NAME_LEN); - app->settings_changed = true; - app->assets_changed = true; -} - -const char* const anim_speed_names[] = - {"25%", "50%", "75%", "100%", "125%", "150%", "175%", "200%", "225%", "250%", "275%", "300%"}; -const int32_t anim_speed_values[COUNT_OF(anim_speed_names)] = - {25, 50, 75, 0, 125, 150, 175, 200, 225, 250, 275, 300}; -static void xtreme_app_scene_main_anim_speed_changed(VariableItem* item) { - XtremeApp* app = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - variable_item_set_current_value_text(item, anim_speed_names[index]); - XTREME_SETTINGS()->anim_speed = anim_speed_values[index]; - app->settings_changed = true; -} - -const char* const cycle_anims_names[] = { - "OFF", - "Meta.txt", - "30 S", - "1 M", - "5 M", - "10 M", - "15 M", - "30 M", - "1 H", - "2 H", - "6 H", - "12 H", - "24 H"}; -const int32_t cycle_anims_values[COUNT_OF(cycle_anims_names)] = - {-1, 0, 30, 60, 300, 600, 900, 1800, 3600, 7200, 21600, 43200, 86400}; -static void xtreme_app_scene_main_cycle_anims_changed(VariableItem* item) { - XtremeApp* app = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - variable_item_set_current_value_text(item, cycle_anims_names[index]); - XTREME_SETTINGS()->cycle_anims = cycle_anims_values[index]; - app->settings_changed = true; -} - -static void xtreme_app_scene_main_unlock_anims_changed(VariableItem* item) { - XtremeApp* app = variable_item_get_context(item); - bool value = variable_item_get_current_value_index(item); - variable_item_set_current_value_text(item, value ? "ON" : "OFF"); - XTREME_SETTINGS()->unlock_anims = value; - app->settings_changed = true; -} - -const char* const battery_icon_names[] = - {"OFF", "Bar", "%", "Inv. %", "Retro 3", "Retro 5", "Bar %"}; -static void xtreme_app_scene_main_battery_icon_changed(VariableItem* item) { - XtremeApp* app = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - variable_item_set_current_value_text(item, battery_icon_names[index]); - XTREME_SETTINGS()->battery_icon = (index + 1) % BatteryIconCount; - app->settings_changed = true; -} - -static void xtreme_app_scene_main_status_icons_changed(VariableItem* item) { - XtremeApp* app = variable_item_get_context(item); - bool value = variable_item_get_current_value_index(item); - variable_item_set_current_value_text(item, value ? "ON" : "OFF"); - XTREME_SETTINGS()->status_icons = value; - app->settings_changed = true; -} - -static void xtreme_app_scene_main_bar_borders_changed(VariableItem* item) { - XtremeApp* app = variable_item_get_context(item); - bool value = variable_item_get_current_value_index(item); - variable_item_set_current_value_text(item, value ? "ON" : "OFF"); - XTREME_SETTINGS()->bar_borders = value; - app->settings_changed = true; -} - -static void xtreme_app_scene_main_bar_background_changed(VariableItem* item) { - XtremeApp* app = variable_item_get_context(item); - bool value = variable_item_get_current_value_index(item); - variable_item_set_current_value_text(item, value ? "ON" : "OFF"); - XTREME_SETTINGS()->bar_background = value; - app->settings_changed = true; -} - -static void xtreme_app_scene_main_bad_bk_mode_changed(VariableItem* item) { - XtremeApp* app = variable_item_get_context(item); - bool value = variable_item_get_current_value_index(item); - variable_item_set_current_value_text(item, value ? "BT" : "USB"); - XTREME_SETTINGS()->bad_bt = value; - app->settings_changed = true; -} - -static void xtreme_app_scene_main_subghz_extend_changed(VariableItem* item) { - XtremeApp* app = variable_item_get_context(item); - app->subghz_extend = variable_item_get_current_value_index(item); - variable_item_set_current_value_text(item, app->subghz_extend ? "ON" : "OFF"); - app->subghz_changed = true; -} - -static void xtreme_app_scene_main_subghz_bypass_changed(VariableItem* item) { - XtremeApp* app = variable_item_get_context(item); - app->subghz_bypass = variable_item_get_current_value_index(item); - variable_item_set_current_value_text(item, app->subghz_bypass ? "ON" : "OFF"); - app->subghz_changed = true; -} - -static void xtreme_app_scene_main_sort_folders_before_changed(VariableItem* item) { - XtremeApp* app = variable_item_get_context(item); - bool value = variable_item_get_current_value_index(item); - variable_item_set_current_value_text(item, value ? "ON" : "OFF"); - XTREME_SETTINGS()->sort_ignore_dirs = !value; - app->settings_changed = true; -} - -static void xtreme_app_scene_main_xp_level_changed(VariableItem* item) { - XtremeApp* app = variable_item_get_context(item); - app->dolphin_level = variable_item_get_current_value_index(item) + 1; - char level_str[4]; - snprintf(level_str, 4, "%i", app->dolphin_level); - variable_item_set_current_value_text(item, level_str); - app->level_changed = true; -} - -void xtreme_app_scene_main_on_enter(void* context) { - XtremeApp* app = context; - XtremeSettings* xtreme_settings = XTREME_SETTINGS(); - VariableItemList* var_item_list = app->var_item_list; - VariableItem* item; - uint8_t value_index; - - Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN); - DolphinStats stats = dolphin_stats(dolphin); - furi_record_close(RECORD_DOLPHIN); - app->dolphin_level = stats.level; - - Storage* storage = furi_record_open(RECORD_STORAGE); - FlipperFormat* subghz_range = flipper_format_file_alloc(storage); - app->subghz_extend = false; - app->subghz_bypass = false; - if(flipper_format_file_open_existing(subghz_range, "/ext/subghz/assets/extend_range.txt")) { - flipper_format_read_bool( - subghz_range, "use_ext_range_at_own_risk", &app->subghz_extend, 1); - flipper_format_read_bool(subghz_range, "ignore_default_tx_region", &app->subghz_bypass, 1); - } - flipper_format_free(subghz_range); - - uint current_pack = 0; - asset_packs_init(app->asset_packs); - File* folder = storage_file_alloc(storage); - FileInfo info; - char* name = malloc(MAX_PACK_NAME_LEN); - if(storage_dir_open(folder, PACKS_DIR)) { - while(storage_dir_read(folder, &info, name, MAX_PACK_NAME_LEN)) { - if(info.flags & FSF_DIRECTORY) { - char* copy = malloc(MAX_PACK_NAME_LEN); - strlcpy(copy, name, MAX_PACK_NAME_LEN); - uint idx = 0; - if(strcmp(copy, "NSFW") != 0) { - for(; idx < asset_packs_size(app->asset_packs); idx++) { - char* comp = *asset_packs_get(app->asset_packs, idx); - if(strcasecmp(copy, comp) < 0 && strcmp(comp, "NSFW") != 0) { - break; - } - } - } - asset_packs_push_at(app->asset_packs, idx, copy); - if(current_pack != 0) { - if(idx < current_pack) current_pack++; - } else { - if(strcmp(copy, xtreme_settings->asset_pack) == 0) current_pack = idx + 1; - } - } - } - } - free(name); - storage_file_free(folder); - furi_record_close(RECORD_STORAGE); - - item = variable_item_list_add( - var_item_list, - "Asset Pack", - asset_packs_size(app->asset_packs) + 1, - xtreme_app_scene_main_asset_pack_changed, - app); - variable_item_set_current_value_index(item, current_pack); - variable_item_set_current_value_text( - item, current_pack == 0 ? "SFW" : *asset_packs_get(app->asset_packs, current_pack - 1)); - - item = variable_item_list_add( - var_item_list, - "Anim Speed", - COUNT_OF(anim_speed_names), - xtreme_app_scene_main_anim_speed_changed, - app); - value_index = value_index_int32( - xtreme_settings->anim_speed, anim_speed_values, COUNT_OF(anim_speed_names)); - variable_item_set_current_value_index(item, value_index); - variable_item_set_current_value_text(item, anim_speed_names[value_index]); - - item = variable_item_list_add( - var_item_list, - "Cycle Anims", - COUNT_OF(cycle_anims_names), - xtreme_app_scene_main_cycle_anims_changed, - app); - value_index = value_index_int32( - xtreme_settings->cycle_anims, cycle_anims_values, COUNT_OF(cycle_anims_names)); - variable_item_set_current_value_index(item, value_index); - variable_item_set_current_value_text(item, cycle_anims_names[value_index]); - - item = variable_item_list_add( - var_item_list, "Unlock Anims", 2, xtreme_app_scene_main_unlock_anims_changed, app); - variable_item_set_current_value_index(item, xtreme_settings->unlock_anims); - variable_item_set_current_value_text(item, xtreme_settings->unlock_anims ? "ON" : "OFF"); - - - variable_item_list_add(var_item_list, " = Status Bar =", 0, NULL, app); - - item = variable_item_list_add( - var_item_list, - "Battery Icon", - BatteryIconCount, - xtreme_app_scene_main_battery_icon_changed, - app); - value_index = (xtreme_settings->battery_icon + BatteryIconCount - 1) % BatteryIconCount; - variable_item_set_current_value_index(item, value_index); - variable_item_set_current_value_text(item, battery_icon_names[value_index]); - - item = variable_item_list_add( - var_item_list, "Status Icons", 2, xtreme_app_scene_main_status_icons_changed, app); - variable_item_set_current_value_index(item, xtreme_settings->status_icons); - variable_item_set_current_value_text(item, xtreme_settings->status_icons ? "ON" : "OFF"); - - item = variable_item_list_add( - var_item_list, "Bar Borders", 2, xtreme_app_scene_main_bar_borders_changed, app); - variable_item_set_current_value_index(item, xtreme_settings->bar_borders); - variable_item_set_current_value_text(item, xtreme_settings->bar_borders ? "ON" : "OFF"); - - item = variable_item_list_add( - var_item_list, "Bar Background", 2, xtreme_app_scene_main_bar_background_changed, app); - variable_item_set_current_value_index(item, xtreme_settings->bar_background); - variable_item_set_current_value_text(item, xtreme_settings->bar_background ? "ON" : "OFF"); - - - variable_item_list_add(var_item_list, " = Protocols =", 0, NULL, app); - - item = variable_item_list_add( - var_item_list, "Bad KB Mode", 2, xtreme_app_scene_main_bad_bk_mode_changed, app); - variable_item_set_current_value_index(item, xtreme_settings->bad_bt); - variable_item_set_current_value_text(item, xtreme_settings->bad_bt ? "BT" : "USB"); - - item = variable_item_list_add( - var_item_list, "SubGHz Extend", 2, xtreme_app_scene_main_subghz_extend_changed, app); - variable_item_set_current_value_index(item, app->subghz_extend); - variable_item_set_current_value_text(item, app->subghz_extend ? "ON" : "OFF"); - - item = variable_item_list_add( - var_item_list, "SubGHz Bypass", 2, xtreme_app_scene_main_subghz_bypass_changed, app); - variable_item_set_current_value_index(item, app->subghz_bypass); - variable_item_set_current_value_text(item, app->subghz_bypass ? "ON" : "OFF"); - - - variable_item_list_add(var_item_list, " = Misc =", 0, NULL, app); - - item = variable_item_list_add( - var_item_list, - "Sort Dirs First", - 2, - xtreme_app_scene_main_sort_folders_before_changed, - app); - variable_item_set_current_value_index(item, !xtreme_settings->sort_ignore_dirs); - variable_item_set_current_value_text(item, !xtreme_settings->sort_ignore_dirs ? "ON" : "OFF"); - - char level_str[4]; - snprintf(level_str, 4, "%i", app->dolphin_level); - item = variable_item_list_add( - var_item_list, - "XP Level", - DOLPHIN_LEVEL_COUNT + 1, - xtreme_app_scene_main_xp_level_changed, - app); - variable_item_set_current_value_index(item, app->dolphin_level - 1); - variable_item_set_current_value_text(item, level_str); - - FuriString* version_tag = furi_string_alloc_printf( - "%s %s", version_get_gitbranchnum(NULL), version_get_builddate(NULL)); - variable_item_list_add(var_item_list, furi_string_get_cstr(version_tag), 0, NULL, app); - - view_dispatcher_switch_to_view(app->view_dispatcher, XtremeAppViewVarItemList); -} - -bool xtreme_app_scene_main_on_event(void* context, SceneManagerEvent event) { - UNUSED(context); - UNUSED(event); - bool consumed = false; - return consumed; -} - -void xtreme_app_scene_main_on_exit(void* context) { - XtremeApp* app = context; - asset_packs_it_t it; - for(asset_packs_it(it, app->asset_packs); !asset_packs_end_p(it); asset_packs_next(it)) { - free(*asset_packs_cref(it)); - } - asset_packs_clear(app->asset_packs); - variable_item_list_reset(app->var_item_list); -} diff --git a/applications/settings/xtreme_app/xtreme_app.c b/applications/settings/xtreme_app/xtreme_app.c deleted file mode 100644 index c6d67b915..000000000 --- a/applications/settings/xtreme_app/xtreme_app.c +++ /dev/null @@ -1,115 +0,0 @@ -#include "xtreme_app.h" - -static bool xtreme_app_custom_event_callback(void* context, uint32_t event) { - furi_assert(context); - XtremeApp* app = context; - return scene_manager_handle_custom_event(app->scene_manager, event); -} - -void xtreme_app_reboot(void* context) { - UNUSED(context); - power_reboot(PowerBootModeNormal); -} - -static bool xtreme_app_back_event_callback(void* context) { - furi_assert(context); - XtremeApp* app = context; - - if(app->level_changed) { - Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN); - DolphinStats stats = dolphin_stats(dolphin); - if(app->dolphin_level != stats.level) { - int xp = app->dolphin_level > 1 ? dolphin_get_levels()[app->dolphin_level - 2] : 0; - dolphin->state->data.icounter = xp + 1; - dolphin->state->dirty = true; - dolphin_state_save(dolphin->state); - } - furi_record_close(RECORD_DOLPHIN); - } - - if(app->subghz_changed) { - Storage* storage = furi_record_open(RECORD_STORAGE); - FlipperFormat* subghz_range = flipper_format_file_alloc(storage); - if(flipper_format_file_open_existing(subghz_range, "/ext/subghz/assets/extend_range.txt")) { - flipper_format_insert_or_update_bool( - subghz_range, "use_ext_range_at_own_risk", &app->subghz_extend, 1); - flipper_format_insert_or_update_bool( - subghz_range, "ignore_default_tx_region", &app->subghz_bypass, 1); - } - flipper_format_free(subghz_range); - furi_record_close(RECORD_STORAGE); - } - - if(app->settings_changed) { - XTREME_SETTINGS_SAVE(); - if(app->assets_changed) { - popup_set_header(app->popup, "Rebooting...", 64, 26, AlignCenter, AlignCenter); - popup_set_text(app->popup, "Swapping assets...", 64, 40, AlignCenter, AlignCenter); - popup_set_callback(app->popup, xtreme_app_reboot); - popup_set_context(app->popup, app); - popup_set_timeout(app->popup, 1000); - popup_enable_timeout(app->popup); - view_dispatcher_switch_to_view(app->view_dispatcher, XtremeAppViewPopup); - return true; - } - } - - return scene_manager_handle_back_event(app->scene_manager); -} - -XtremeApp* xtreme_app_alloc() { - XtremeApp* app = malloc(sizeof(XtremeApp)); - app->gui = furi_record_open(RECORD_GUI); - - // View Dispatcher and Scene Manager - app->view_dispatcher = view_dispatcher_alloc(); - app->scene_manager = scene_manager_alloc(&xtreme_app_scene_handlers, app); - view_dispatcher_enable_queue(app->view_dispatcher); - view_dispatcher_set_event_callback_context(app->view_dispatcher, app); - - view_dispatcher_set_custom_event_callback( - app->view_dispatcher, xtreme_app_custom_event_callback); - view_dispatcher_set_navigation_event_callback( - app->view_dispatcher, xtreme_app_back_event_callback); - - view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); - - // Gui Modules - app->var_item_list = variable_item_list_alloc(); - view_dispatcher_add_view( - app->view_dispatcher, - XtremeAppViewVarItemList, - variable_item_list_get_view(app->var_item_list)); - - app->popup = popup_alloc(); - view_dispatcher_add_view(app->view_dispatcher, XtremeAppViewPopup, popup_get_view(app->popup)); - - return app; -} - -void xtreme_app_free(XtremeApp* app) { - furi_assert(app); - - // Gui modules - view_dispatcher_remove_view(app->view_dispatcher, XtremeAppViewVarItemList); - variable_item_list_free(app->var_item_list); - view_dispatcher_remove_view(app->view_dispatcher, XtremeAppViewPopup); - popup_free(app->popup); - - // View Dispatcher and Scene Manager - view_dispatcher_free(app->view_dispatcher); - scene_manager_free(app->scene_manager); - - // Records - furi_record_close(RECORD_GUI); - free(app); -} - -extern int32_t xtreme_app(void* p) { - UNUSED(p); - XtremeApp* app = xtreme_app_alloc(); - scene_manager_next_scene(app->scene_manager, XtremeAppSceneMain); - view_dispatcher_run(app->view_dispatcher); - xtreme_app_free(app); - return 0; -} diff --git a/applications/system/storage_move_to_sd/storage_move_to_sd.c b/applications/system/storage_move_to_sd/storage_move_to_sd.c index 2027bd237..9c91b9266 100644 --- a/applications/system/storage_move_to_sd/storage_move_to_sd.c +++ b/applications/system/storage_move_to_sd/storage_move_to_sd.c @@ -13,7 +13,7 @@ static bool storage_move_to_sd_check_entry(const char* name, FileInfo* fileinfo, void* ctx) { UNUSED(ctx); - if((fileinfo->flags & FSF_DIRECTORY) != 0) { + if(file_info_is_dir(fileinfo)) { return true; } diff --git a/applications/system/updater/util/update_task_worker_backup.c b/applications/system/updater/util/update_task_worker_backup.c index ed53c353b..f2c33c2ed 100644 --- a/applications/system/updater/util/update_task_worker_backup.c +++ b/applications/system/updater/util/update_task_worker_backup.c @@ -97,7 +97,16 @@ static void update_task_cleanup_resources(UpdateTask* update_task, const uint32_ path_concat( STORAGE_EXT_PATH_PREFIX, furi_string_get_cstr(entry_ptr->name), file_path); FURI_LOG_D(TAG, "Removing %s", furi_string_get_cstr(file_path)); - storage_simply_remove(update_task->storage, furi_string_get_cstr(file_path)); + + FS_Error result = + storage_common_remove(update_task->storage, furi_string_get_cstr(file_path)); + if(result != FSE_OK && result != FSE_EXIST) { + FURI_LOG_E( + TAG, + "%s remove failed, cause %s", + furi_string_get_cstr(file_path), + storage_error_get_desc(result)); + } furi_string_free(file_path); } else if(entry_ptr->type == ResourceManifestEntryTypeDirectory) { n_dir_entries++; @@ -116,7 +125,6 @@ static void update_task_cleanup_resources(UpdateTask* update_task, const uint32_ n_dir_entries); FuriString* folder_path = furi_string_alloc(); - File* folder_file = storage_file_alloc(update_task->storage); do { path_concat( @@ -125,24 +133,17 @@ static void update_task_cleanup_resources(UpdateTask* update_task, const uint32_ 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( + FS_Error result = storage_common_remove( + update_task->storage, furi_string_get_cstr(folder_path)); + if(result != FSE_OK && result != FSE_EXIST) { + FURI_LOG_E( TAG, - "%s can't be opened, skipping", - furi_string_get_cstr(folder_path)); - break; + "%s remove failed, cause %s", + furi_string_get_cstr(folder_path), + storage_error_get_desc(result)); } - - 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); } } diff --git a/assets/SConscript b/assets/SConscript index ef5d83c79..21437aa30 100644 --- a/assets/SConscript +++ b/assets/SConscript @@ -78,7 +78,9 @@ if assetsenv["IS_BASE_FIRMWARE"]: resources = assetsenv.Command( "#/assets/resources/Manifest", assetsenv.GlobRecursive( - "*", assetsenv.Dir("resources").srcnode(), exclude="Manifest" + "*", + assetsenv.Dir("resources").srcnode(), + exclude=["Manifest"], ), action=Action( '${PYTHON3} "${ASSETS_COMPILER}" manifest "${TARGET.dir.posix}" --timestamp=${GIT_UNIX_TIMESTAMP}', diff --git a/assets/dolphin/ReadMe.md b/assets/dolphin/ReadMe.md index 5f4932c53..cb9f8afd5 100644 --- a/assets/dolphin/ReadMe.md +++ b/assets/dolphin/ReadMe.md @@ -2,13 +2,11 @@ Dolphin assets are split into 3 parts: -- blocking - Essential animations that are used for blocking system notifications. They are packed to `assets_dolphin_blocking.[h,c]`. -- internal - Last fallback animations that are used for idle dolphin animation. Converted to `assets_dolphin_internal.[h,c]`. -- external - Essential animations that are used for idle dolphin animation. Compiled to `.bm` and found at `SD/dolphin/`. - +- blocking - Essential animations that are used for blocking system notifications. They are packed to `assets_dolphin_blocking.[h,c]`. +- internal - Internal animations that are used for idle dolphin animation. Converted to `assets_dolphin_internal.[h,c]`. +- external - External animations that are used for idle dolphin animation. Packed to resource folder and placed on SD card. - custom - Custom User-made animations that are used for a variety of scenes, such as `dolphin idle, passport, scanning`. Compiled to `.bmx` and found at `SD/dolphin_custom` - # Files - `manifest.txt` - contains animations enumeration that is used for random animation selection. Starting point for Dolphin. diff --git a/assets/dolphin/custom/NSFW/Icons/SubGhz/Fishing_123x52.png b/assets/dolphin/custom/NSFW/Icons/SubGhz/Fishing_123x52.png new file mode 100644 index 000000000..a48c5330e Binary files /dev/null and b/assets/dolphin/custom/NSFW/Icons/SubGhz/Fishing_123x52.png differ diff --git a/assets/dolphin/custom/WatchDogs/Icons/SubGhz/Fishing_123x52.png b/assets/dolphin/custom/WatchDogs/Icons/SubGhz/Fishing_123x52.png new file mode 100644 index 000000000..fd6762e65 Binary files /dev/null and b/assets/dolphin/custom/WatchDogs/Icons/SubGhz/Fishing_123x52.png differ diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_0.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_0.png deleted file mode 100644 index 909cc3303..000000000 Binary files a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_0.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_1.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_1.png deleted file mode 100644 index fdc1805d1..000000000 Binary files a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_1.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_10.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_10.png deleted file mode 100644 index 3a43c6b84..000000000 Binary files a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_10.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_11.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_11.png deleted file mode 100644 index 20ea12eb0..000000000 Binary files a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_11.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_12.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_12.png deleted file mode 100644 index a05abd80f..000000000 Binary files a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_12.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_2.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_2.png deleted file mode 100644 index fc7e0364a..000000000 Binary files a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_2.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_3.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_3.png deleted file mode 100644 index 06f08ae67..000000000 Binary files a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_3.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_4.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_4.png deleted file mode 100644 index bcc7f28a9..000000000 Binary files a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_4.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_5.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_5.png deleted file mode 100644 index 7d945dd06..000000000 Binary files a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_5.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_6.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_6.png deleted file mode 100644 index c0b1a4fe2..000000000 Binary files a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_6.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_7.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_7.png deleted file mode 100644 index 19c8181d0..000000000 Binary files a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_7.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_8.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_8.png deleted file mode 100644 index c0b316950..000000000 Binary files a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_8.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_9.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_9.png deleted file mode 100644 index 929a207ef..000000000 Binary files a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_9.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/meta.txt b/assets/dolphin/external/L1_Happy_holidays_128x64/meta.txt deleted file mode 100644 index ad883525e..000000000 --- a/assets/dolphin/external/L1_Happy_holidays_128x64/meta.txt +++ /dev/null @@ -1,23 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 10 -Active frames: 18 -Frames order: 0 1 2 1 0 1 2 1 0 1 2 3 4 5 6 5 4 7 2 8 9 10 11 10 9 10 11 12 -Active cycles: 1 -Frame rate: 2 -Duration: 360 -Active cooldown: 7 - -Bubble slots: 1 - -Slot: 0 -X: 11 -Y: 19 -Text: HAPPY\nHOLIDAYS! -AlignH: Right -AlignV: Center -StartFrame: 22 -EndFrame: 27 diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_0.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_0.png deleted file mode 100644 index 340ceb47f..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_0.png and /dev/null 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 deleted file mode 100644 index f401d60a8..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_1.png and /dev/null 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 deleted file mode 100644 index 890ea5d70..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_10.png and /dev/null 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 deleted file mode 100644 index 0b331e4b7..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_11.png and /dev/null 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 deleted file mode 100644 index 145eaa314..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_12.png and /dev/null 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 deleted file mode 100644 index 6eda42b5d..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_13.png and /dev/null 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 deleted file mode 100644 index b1eb38da5..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_14.png and /dev/null 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 deleted file mode 100644 index 554a177a9..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_15.png and /dev/null 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 deleted file mode 100644 index ad032f411..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_16.png and /dev/null 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 deleted file mode 100644 index 971e8f55b..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_17.png and /dev/null 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 deleted file mode 100644 index 4026dc373..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_18.png and /dev/null 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 deleted file mode 100644 index b8cfa50ce..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_19.png and /dev/null 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 deleted file mode 100644 index 0c380ca41..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_2.png and /dev/null 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 deleted file mode 100644 index 59ec9627c..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_20.png and /dev/null 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 deleted file mode 100644 index d0e44937c..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_21.png and /dev/null 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 deleted file mode 100644 index fd800b8b9..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_22.png and /dev/null 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 deleted file mode 100644 index 41615b613..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_23.png and /dev/null 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 deleted file mode 100644 index 05c5e969d..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_24.png and /dev/null 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 deleted file mode 100644 index 79fa45753..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_25.png and /dev/null 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 deleted file mode 100644 index edb038666..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_26.png and /dev/null 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 deleted file mode 100644 index 7696f9ce8..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_27.png and /dev/null 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 deleted file mode 100644 index 052ff2848..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_28.png and /dev/null 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 deleted file mode 100644 index c6caf88d9..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_29.png and /dev/null 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 deleted file mode 100644 index c69ac8012..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_3.png and /dev/null 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 deleted file mode 100644 index 8710e1a0a..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_30.png and /dev/null 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 deleted file mode 100644 index 7abad6b33..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_31.png and /dev/null 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 deleted file mode 100644 index 0a0882a9d..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_32.png and /dev/null 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 deleted file mode 100644 index 94b79d2ed..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_33.png and /dev/null 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 deleted file mode 100644 index ce0e210f4..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_34.png and /dev/null 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 deleted file mode 100644 index aba999f6d..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_35.png and /dev/null 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 deleted file mode 100644 index 1aac85583..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_36.png and /dev/null 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 deleted file mode 100644 index ef5e2fc53..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_4.png and /dev/null 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 deleted file mode 100644 index 44d566b15..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_5.png and /dev/null 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 deleted file mode 100644 index 789eebdfa..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_6.png and /dev/null 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 deleted file mode 100644 index cc4f5cbff..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_7.png and /dev/null 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 deleted file mode 100644 index 1a1fe5d2d..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_8.png and /dev/null 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 deleted file mode 100644 index a19da7382..000000000 Binary files a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_9.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/meta.txt b/assets/dolphin/external/L1_Sleigh_ride_128x64/meta.txt deleted file mode 100644 index eed81079d..000000000 --- a/assets/dolphin/external/L1_Sleigh_ride_128x64/meta.txt +++ /dev/null @@ -1,23 +0,0 @@ -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: 360 -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 fbc438c00..bd344074d 100644 --- a/assets/dolphin/external/manifest.txt +++ b/assets/dolphin/external/manifest.txt @@ -15,13 +15,6 @@ Min level: 2 Max level: 30 Weight: 3 -Name: L1_Sleigh_ride_128x64 -Min butthurt: 0 -Max butthurt: 14 -Min level: 4 -Max level: 30 -Weight: 4 - Name: L1_Sleep_128x64 Min butthurt: 0 Max butthurt: 10 @@ -36,13 +29,6 @@ Min level: 7 Max level: 30 Weight: 3 -Name: L1_Happy_holidays_128x64 -Min butthurt: 0 -Max butthurt: 14 -Min level: 8 -Max level: 30 -Weight: 4 - Name: L1_Furippa1_128x64 Min butthurt: 0 Max butthurt: 6 @@ -53,7 +39,7 @@ Weight: 3 Name: L1_Read_books_128x64 Min butthurt: 0 Max butthurt: 8 -Min level: 11 +Min level: 8 Max level: 30 Weight: 3 @@ -67,21 +53,21 @@ Weight: 3 Name: L1_Boxing_128x64 Min butthurt: 10 Max butthurt: 13 -Min level: 14 +Min level: 3 Max level: 30 Weight: 3 Name: L1_Mad_fist_128x64 Min butthurt: 9 Max butthurt: 13 -Min level: 15 +Min level: 4 Max level: 30 Weight: 3 Name: L1_Mods_128x64 Min butthurt: 0 Max butthurt: 9 -Min level: 17 +Min level: 15 Max level: 30 Weight: 4 @@ -92,13 +78,6 @@ Min level: 18 Max level: 30 Weight: 3 -Name: L2_Furippa2_128x64 -Min butthurt: 0 -Max butthurt: 6 -Min level: 20 -Max level: 30 -Weight: 3 - Name: L1_Leaving_sad_128x64 Min butthurt: 14 Max butthurt: 14 @@ -113,6 +92,13 @@ Min level: 22 Max level: 30 Weight: 4 +Name: L2_Furippa2_128x64 +Min butthurt: 0 +Max butthurt: 6 +Min level: 20 +Max level: 30 +Weight: 3 + Name: L2_Hacking_pc_128x64 Min butthurt: 0 Max butthurt: 8 @@ -127,10 +113,10 @@ Min level: 25 Max level: 30 Weight: 3 -Name: L3_Lab_research_128x54 +Name: L3_Furippa3_128x64 Min butthurt: 0 -Max butthurt: 10 -Min level: 27 +Max butthurt: 6 +Min level: 30 Max level: 30 Weight: 3 @@ -141,9 +127,9 @@ Min level: 28 Max level: 30 Weight: 3 -Name: L3_Furippa3_128x64 +Name: L3_Lab_research_128x54 Min butthurt: 0 -Max butthurt: 6 -Min level: 30 +Max butthurt: 10 +Min level: 27 Max level: 30 Weight: 3 diff --git a/assets/icons/ErasePin/Erase_pin_128x64.png b/assets/icons/ErasePin/Erase_pin_128x64.png new file mode 100644 index 000000000..92ca5f91c Binary files /dev/null and b/assets/icons/ErasePin/Erase_pin_128x64.png differ diff --git a/assets/icons/Keyboard/KeyKeyboardSelected_10x11.png b/assets/icons/Keyboard/KeyKeyboardSelected_10x11.png new file mode 100644 index 000000000..231880386 Binary files /dev/null and b/assets/icons/Keyboard/KeyKeyboardSelected_10x11.png differ diff --git a/assets/icons/Keyboard/KeyKeyboard_10x11.png b/assets/icons/Keyboard/KeyKeyboard_10x11.png new file mode 100644 index 000000000..1f4c03478 Binary files /dev/null and b/assets/icons/Keyboard/KeyKeyboard_10x11.png differ diff --git a/assets/icons/Keyboard/KeySaveSelected_24x11.png b/assets/icons/Keyboard/KeySaveSelected_24x11.png index eeb3569d3..6e5c9ac07 100644 Binary files a/assets/icons/Keyboard/KeySaveSelected_24x11.png and b/assets/icons/Keyboard/KeySaveSelected_24x11.png differ diff --git a/assets/icons/Keyboard/KeySave_24x11.png b/assets/icons/Keyboard/KeySave_24x11.png index e7dba987a..6945c7e8f 100644 Binary files a/assets/icons/Keyboard/KeySave_24x11.png and b/assets/icons/Keyboard/KeySave_24x11.png differ diff --git a/assets/icons/MainMenu/UniRFRemix_14/frame_0.png b/assets/icons/MainMenu/SubGHzRemote_14/frame_0.png similarity index 100% rename from assets/icons/MainMenu/UniRFRemix_14/frame_0.png rename to assets/icons/MainMenu/SubGHzRemote_14/frame_0.png diff --git a/assets/icons/MainMenu/UniRFRemix_14/frame_1.png b/assets/icons/MainMenu/SubGHzRemote_14/frame_1.png similarity index 100% rename from assets/icons/MainMenu/UniRFRemix_14/frame_1.png rename to assets/icons/MainMenu/SubGHzRemote_14/frame_1.png diff --git a/assets/icons/MainMenu/UniRFRemix_14/frame_2.png b/assets/icons/MainMenu/SubGHzRemote_14/frame_2.png similarity index 100% rename from assets/icons/MainMenu/UniRFRemix_14/frame_2.png rename to assets/icons/MainMenu/SubGHzRemote_14/frame_2.png diff --git a/assets/icons/MainMenu/UniRFRemix_14/frame_3.png b/assets/icons/MainMenu/SubGHzRemote_14/frame_3.png similarity index 100% rename from assets/icons/MainMenu/UniRFRemix_14/frame_3.png rename to assets/icons/MainMenu/SubGHzRemote_14/frame_3.png diff --git a/assets/icons/MainMenu/UniRFRemix_14/frame_4.png b/assets/icons/MainMenu/SubGHzRemote_14/frame_4.png similarity index 100% rename from assets/icons/MainMenu/UniRFRemix_14/frame_4.png rename to assets/icons/MainMenu/SubGHzRemote_14/frame_4.png diff --git a/assets/icons/MainMenu/UniRFRemix_14/frame_5.png b/assets/icons/MainMenu/SubGHzRemote_14/frame_5.png similarity index 100% rename from assets/icons/MainMenu/UniRFRemix_14/frame_5.png rename to assets/icons/MainMenu/SubGHzRemote_14/frame_5.png diff --git a/assets/icons/MainMenu/UniRFRemix_14/frame_6.png b/assets/icons/MainMenu/SubGHzRemote_14/frame_6.png similarity index 100% rename from assets/icons/MainMenu/UniRFRemix_14/frame_6.png rename to assets/icons/MainMenu/SubGHzRemote_14/frame_6.png diff --git a/assets/icons/MainMenu/UniRFRemix_14/frame_7.png b/assets/icons/MainMenu/SubGHzRemote_14/frame_7.png similarity index 100% rename from assets/icons/MainMenu/UniRFRemix_14/frame_7.png rename to assets/icons/MainMenu/SubGHzRemote_14/frame_7.png diff --git a/assets/icons/MainMenu/UniRFRemix_14/frame_rate b/assets/icons/MainMenu/SubGHzRemote_14/frame_rate similarity index 100% rename from assets/icons/MainMenu/UniRFRemix_14/frame_rate rename to assets/icons/MainMenu/SubGHzRemote_14/frame_rate diff --git a/assets/icons/MainMenu/Sub_Playlist_14/frame_01.png b/assets/icons/MainMenu/Sub_Playlist_14/frame_01.png deleted file mode 100644 index 09bf26f72..000000000 Binary files a/assets/icons/MainMenu/Sub_Playlist_14/frame_01.png and /dev/null differ diff --git a/assets/icons/MainMenu/Sub_Playlist_14/frame_02.png b/assets/icons/MainMenu/Sub_Playlist_14/frame_02.png deleted file mode 100644 index 7032e362c..000000000 Binary files a/assets/icons/MainMenu/Sub_Playlist_14/frame_02.png and /dev/null differ diff --git a/assets/icons/MainMenu/Sub_Playlist_14/frame_03.png b/assets/icons/MainMenu/Sub_Playlist_14/frame_03.png deleted file mode 100644 index 14f6daaff..000000000 Binary files a/assets/icons/MainMenu/Sub_Playlist_14/frame_03.png and /dev/null differ diff --git a/assets/icons/MainMenu/Sub_Playlist_14/frame_04.png b/assets/icons/MainMenu/Sub_Playlist_14/frame_04.png deleted file mode 100644 index fb525d309..000000000 Binary files a/assets/icons/MainMenu/Sub_Playlist_14/frame_04.png and /dev/null differ diff --git a/assets/icons/MainMenu/Sub_Playlist_14/frame_05.png b/assets/icons/MainMenu/Sub_Playlist_14/frame_05.png deleted file mode 100644 index 09bf26f72..000000000 Binary files a/assets/icons/MainMenu/Sub_Playlist_14/frame_05.png and /dev/null differ diff --git a/assets/icons/MainMenu/Sub_Playlist_14/frame_rate b/assets/icons/MainMenu/Sub_Playlist_14/frame_rate deleted file mode 100644 index 56a6051ca..000000000 --- a/assets/icons/MainMenu/Sub_Playlist_14/frame_rate +++ /dev/null @@ -1 +0,0 @@ -1 \ No newline at end of file diff --git a/assets/icons/MainMenu/Xtreme_14/frame_01.png b/assets/icons/MainMenu/Xtreme_14/frame_01.png new file mode 100644 index 000000000..bd77d092d Binary files /dev/null and b/assets/icons/MainMenu/Xtreme_14/frame_01.png differ diff --git a/assets/icons/MainMenu/Xtreme_14/frame_02.png b/assets/icons/MainMenu/Xtreme_14/frame_02.png new file mode 100644 index 000000000..5ca6f6528 Binary files /dev/null and b/assets/icons/MainMenu/Xtreme_14/frame_02.png differ diff --git a/assets/icons/MainMenu/Xtreme_14/frame_03.png b/assets/icons/MainMenu/Xtreme_14/frame_03.png new file mode 100644 index 000000000..99ac840a2 Binary files /dev/null and b/assets/icons/MainMenu/Xtreme_14/frame_03.png differ diff --git a/assets/icons/MainMenu/Xtreme_14/frame_04.png b/assets/icons/MainMenu/Xtreme_14/frame_04.png new file mode 100644 index 000000000..ec9d4d5b4 Binary files /dev/null and b/assets/icons/MainMenu/Xtreme_14/frame_04.png differ diff --git a/assets/icons/MainMenu/Xtreme_14/frame_05.png b/assets/icons/MainMenu/Xtreme_14/frame_05.png new file mode 100644 index 000000000..39f2d2979 Binary files /dev/null and b/assets/icons/MainMenu/Xtreme_14/frame_05.png differ diff --git a/assets/icons/MainMenu/Xtreme_14/frame_06.png b/assets/icons/MainMenu/Xtreme_14/frame_06.png new file mode 100644 index 000000000..782fc2854 Binary files /dev/null and b/assets/icons/MainMenu/Xtreme_14/frame_06.png differ diff --git a/assets/icons/MainMenu/Xtreme_14/frame_07.png b/assets/icons/MainMenu/Xtreme_14/frame_07.png new file mode 100644 index 000000000..5d56ce1fd Binary files /dev/null and b/assets/icons/MainMenu/Xtreme_14/frame_07.png differ diff --git a/assets/icons/MainMenu/Xtreme_14/frame_08.png b/assets/icons/MainMenu/Xtreme_14/frame_08.png new file mode 100644 index 000000000..a21cbb27c Binary files /dev/null and b/assets/icons/MainMenu/Xtreme_14/frame_08.png differ diff --git a/assets/icons/MainMenu/Xtreme_14/frame_09.png b/assets/icons/MainMenu/Xtreme_14/frame_09.png new file mode 100644 index 000000000..e2f07516a Binary files /dev/null and b/assets/icons/MainMenu/Xtreme_14/frame_09.png differ diff --git a/assets/icons/MainMenu/Xtreme_14/frame_10.png b/assets/icons/MainMenu/Xtreme_14/frame_10.png new file mode 100644 index 000000000..a888dd8a7 Binary files /dev/null and b/assets/icons/MainMenu/Xtreme_14/frame_10.png differ diff --git a/applications/plugins/subbrute/images/Sub1ghz_14/frame_rate b/assets/icons/MainMenu/Xtreme_14/frame_rate similarity index 100% rename from applications/plugins/subbrute/images/Sub1ghz_14/frame_rate rename to assets/icons/MainMenu/Xtreme_14/frame_rate diff --git a/assets/icons/SDCard/SDQuestion_35x43.png b/assets/icons/SDCard/SDQuestion_35x43.png index 9b9c9a58e..257ab1d85 100644 Binary files a/assets/icons/SDCard/SDQuestion_35x43.png and b/assets/icons/SDCard/SDQuestion_35x43.png differ diff --git a/assets/icons/SubGhz/Fishing_123x52.png b/assets/icons/SubGhz/Fishing_123x52.png new file mode 100644 index 000000000..1e365de8f Binary files /dev/null and b/assets/icons/SubGhz/Fishing_123x52.png differ diff --git a/assets/icons/SubGhz/Scanning_123x52.png b/assets/icons/SubGhz/Scanning_123x52.png index ec785948d..485052c14 100644 Binary files a/assets/icons/SubGhz/Scanning_123x52.png and b/assets/icons/SubGhz/Scanning_123x52.png differ diff --git a/assets/resources/picopass/assets/iclass_elite_dict.txt b/assets/resources/apps_data/picopass/assets/iclass_elite_dict.txt similarity index 98% rename from assets/resources/picopass/assets/iclass_elite_dict.txt rename to assets/resources/apps_data/picopass/assets/iclass_elite_dict.txt index 46808ef60..5da2a2fa8 100644 --- a/assets/resources/picopass/assets/iclass_elite_dict.txt +++ b/assets/resources/apps_data/picopass/assets/iclass_elite_dict.txt @@ -45,3 +45,5 @@ C1B74D7478053AE2 # default iCLASS RFIDeas 6B65797374726B72 + +5C100DF7042EAE64 diff --git a/assets/resources/badkb/.badkb.settings b/assets/resources/badkb/.badkb.settings deleted file mode 100644 index 4841c2ded..000000000 --- a/assets/resources/badkb/.badkb.settings +++ /dev/null @@ -1 +0,0 @@ -/any/badkb/layouts/en-US.kl diff --git a/assets/resources/badkb/layouts/ba-BA.kl b/assets/resources/badkb/assets/layouts/ba-BA.kl similarity index 100% rename from assets/resources/badkb/layouts/ba-BA.kl rename to assets/resources/badkb/assets/layouts/ba-BA.kl diff --git a/assets/resources/badkb/layouts/cz_CS.kl b/assets/resources/badkb/assets/layouts/cz_CS.kl similarity index 100% rename from assets/resources/badkb/layouts/cz_CS.kl rename to assets/resources/badkb/assets/layouts/cz_CS.kl diff --git a/assets/resources/badkb/layouts/da-DA.kl b/assets/resources/badkb/assets/layouts/da-DA.kl similarity index 100% rename from assets/resources/badkb/layouts/da-DA.kl rename to assets/resources/badkb/assets/layouts/da-DA.kl diff --git a/assets/resources/badkb/layouts/de-CH.kl b/assets/resources/badkb/assets/layouts/de-CH.kl similarity index 100% rename from assets/resources/badkb/layouts/de-CH.kl rename to assets/resources/badkb/assets/layouts/de-CH.kl diff --git a/assets/resources/badkb/layouts/de-DE.kl b/assets/resources/badkb/assets/layouts/de-DE.kl similarity index 100% rename from assets/resources/badkb/layouts/de-DE.kl rename to assets/resources/badkb/assets/layouts/de-DE.kl diff --git a/assets/resources/badkb/layouts/dvorak.kl b/assets/resources/badkb/assets/layouts/dvorak.kl similarity index 100% rename from assets/resources/badkb/layouts/dvorak.kl rename to assets/resources/badkb/assets/layouts/dvorak.kl diff --git a/assets/resources/badkb/layouts/en-UK.kl b/assets/resources/badkb/assets/layouts/en-UK.kl similarity index 100% rename from assets/resources/badkb/layouts/en-UK.kl rename to assets/resources/badkb/assets/layouts/en-UK.kl diff --git a/assets/resources/badkb/layouts/en-US.kl b/assets/resources/badkb/assets/layouts/en-US.kl similarity index 100% rename from assets/resources/badkb/layouts/en-US.kl rename to assets/resources/badkb/assets/layouts/en-US.kl diff --git a/assets/resources/badkb/layouts/es-ES.kl b/assets/resources/badkb/assets/layouts/es-ES.kl similarity index 100% rename from assets/resources/badkb/layouts/es-ES.kl rename to assets/resources/badkb/assets/layouts/es-ES.kl diff --git a/assets/resources/badkb/layouts/fr-BE.kl b/assets/resources/badkb/assets/layouts/fr-BE.kl similarity index 100% rename from assets/resources/badkb/layouts/fr-BE.kl rename to assets/resources/badkb/assets/layouts/fr-BE.kl diff --git a/assets/resources/badkb/layouts/fr-CH.kl b/assets/resources/badkb/assets/layouts/fr-CH.kl similarity index 100% rename from assets/resources/badkb/layouts/fr-CH.kl rename to assets/resources/badkb/assets/layouts/fr-CH.kl diff --git a/assets/resources/badkb/layouts/fr-FR.kl b/assets/resources/badkb/assets/layouts/fr-FR.kl similarity index 100% rename from assets/resources/badkb/layouts/fr-FR.kl rename to assets/resources/badkb/assets/layouts/fr-FR.kl diff --git a/assets/resources/badkb/layouts/hr-HR.kl b/assets/resources/badkb/assets/layouts/hr-HR.kl similarity index 100% rename from assets/resources/badkb/layouts/hr-HR.kl rename to assets/resources/badkb/assets/layouts/hr-HR.kl diff --git a/assets/resources/badkb/layouts/hu-HU.kl b/assets/resources/badkb/assets/layouts/hu-HU.kl similarity index 100% rename from assets/resources/badkb/layouts/hu-HU.kl rename to assets/resources/badkb/assets/layouts/hu-HU.kl diff --git a/assets/resources/badkb/layouts/it-IT.kl b/assets/resources/badkb/assets/layouts/it-IT.kl similarity index 100% rename from assets/resources/badkb/layouts/it-IT.kl rename to assets/resources/badkb/assets/layouts/it-IT.kl diff --git a/assets/resources/badkb/layouts/nb-NO.kl b/assets/resources/badkb/assets/layouts/nb-NO.kl similarity index 100% rename from assets/resources/badkb/layouts/nb-NO.kl rename to assets/resources/badkb/assets/layouts/nb-NO.kl diff --git a/assets/resources/badkb/layouts/nl-NL.kl b/assets/resources/badkb/assets/layouts/nl-NL.kl similarity index 100% rename from assets/resources/badkb/layouts/nl-NL.kl rename to assets/resources/badkb/assets/layouts/nl-NL.kl diff --git a/assets/resources/badkb/layouts/pt-BR.kl b/assets/resources/badkb/assets/layouts/pt-BR.kl similarity index 100% rename from assets/resources/badkb/layouts/pt-BR.kl rename to assets/resources/badkb/assets/layouts/pt-BR.kl diff --git a/assets/resources/badkb/layouts/pt-PT.kl b/assets/resources/badkb/assets/layouts/pt-PT.kl similarity index 100% rename from assets/resources/badkb/layouts/pt-PT.kl rename to assets/resources/badkb/assets/layouts/pt-PT.kl diff --git a/assets/resources/badkb/layouts/si-SI.kl b/assets/resources/badkb/assets/layouts/si-SI.kl similarity index 100% rename from assets/resources/badkb/layouts/si-SI.kl rename to assets/resources/badkb/assets/layouts/si-SI.kl diff --git a/assets/resources/badkb/layouts/sk-SK.kl b/assets/resources/badkb/assets/layouts/sk-SK.kl similarity index 100% rename from assets/resources/badkb/layouts/sk-SK.kl rename to assets/resources/badkb/assets/layouts/sk-SK.kl diff --git a/assets/resources/badkb/layouts/sv-SE.kl b/assets/resources/badkb/assets/layouts/sv-SE.kl similarity index 100% rename from assets/resources/badkb/layouts/sv-SE.kl rename to assets/resources/badkb/assets/layouts/sv-SE.kl diff --git a/assets/resources/badkb/layouts/tr-TR.kl b/assets/resources/badkb/assets/layouts/tr-TR.kl similarity index 100% rename from assets/resources/badkb/layouts/tr-TR.kl rename to assets/resources/badkb/assets/layouts/tr-TR.kl diff --git a/assets/resources/badkb/demo_ios.txt b/assets/resources/badkb/demo_ios.txt index 17d0722d3..ed7b4d8cc 100644 --- a/assets/resources/badkb/demo_ios.txt +++ b/assets/resources/badkb/demo_ios.txt @@ -11,14 +11,6 @@ GUI SPACE DELAY 150 BACKSPACE DELAY 250 -STRING Safari -DELAY 100 -ENTER -DELAY 500 -GUI t -DELAY 250 -GUI l -DELAY 100 STRING https://github.com/ClaraCrazy/Flipper-Xtreme DELAY 250 -ENTER \ No newline at end of file +ENTER diff --git a/assets/resources/dolphin/manifest.txt b/assets/resources/dolphin/manifest.txt index fbc438c00..bd344074d 100644 --- a/assets/resources/dolphin/manifest.txt +++ b/assets/resources/dolphin/manifest.txt @@ -15,13 +15,6 @@ Min level: 2 Max level: 30 Weight: 3 -Name: L1_Sleigh_ride_128x64 -Min butthurt: 0 -Max butthurt: 14 -Min level: 4 -Max level: 30 -Weight: 4 - Name: L1_Sleep_128x64 Min butthurt: 0 Max butthurt: 10 @@ -36,13 +29,6 @@ Min level: 7 Max level: 30 Weight: 3 -Name: L1_Happy_holidays_128x64 -Min butthurt: 0 -Max butthurt: 14 -Min level: 8 -Max level: 30 -Weight: 4 - Name: L1_Furippa1_128x64 Min butthurt: 0 Max butthurt: 6 @@ -53,7 +39,7 @@ Weight: 3 Name: L1_Read_books_128x64 Min butthurt: 0 Max butthurt: 8 -Min level: 11 +Min level: 8 Max level: 30 Weight: 3 @@ -67,21 +53,21 @@ Weight: 3 Name: L1_Boxing_128x64 Min butthurt: 10 Max butthurt: 13 -Min level: 14 +Min level: 3 Max level: 30 Weight: 3 Name: L1_Mad_fist_128x64 Min butthurt: 9 Max butthurt: 13 -Min level: 15 +Min level: 4 Max level: 30 Weight: 3 Name: L1_Mods_128x64 Min butthurt: 0 Max butthurt: 9 -Min level: 17 +Min level: 15 Max level: 30 Weight: 4 @@ -92,13 +78,6 @@ Min level: 18 Max level: 30 Weight: 3 -Name: L2_Furippa2_128x64 -Min butthurt: 0 -Max butthurt: 6 -Min level: 20 -Max level: 30 -Weight: 3 - Name: L1_Leaving_sad_128x64 Min butthurt: 14 Max butthurt: 14 @@ -113,6 +92,13 @@ Min level: 22 Max level: 30 Weight: 4 +Name: L2_Furippa2_128x64 +Min butthurt: 0 +Max butthurt: 6 +Min level: 20 +Max level: 30 +Weight: 3 + Name: L2_Hacking_pc_128x64 Min butthurt: 0 Max butthurt: 8 @@ -127,10 +113,10 @@ Min level: 25 Max level: 30 Weight: 3 -Name: L3_Lab_research_128x54 +Name: L3_Furippa3_128x64 Min butthurt: 0 -Max butthurt: 10 -Min level: 27 +Max butthurt: 6 +Min level: 30 Max level: 30 Weight: 3 @@ -141,9 +127,9 @@ Min level: 28 Max level: 30 Weight: 3 -Name: L3_Furippa3_128x64 +Name: L3_Lab_research_128x54 Min butthurt: 0 -Max butthurt: 6 -Min level: 30 +Max butthurt: 10 +Min level: 27 Max level: 30 Weight: 3 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/SubGhz/Fishing_123x52.bmx b/assets/resources/dolphin_custom/NSFW/Icons/SubGhz/Fishing_123x52.bmx new file mode 100644 index 000000000..75f57367c Binary files /dev/null and b/assets/resources/dolphin_custom/NSFW/Icons/SubGhz/Fishing_123x52.bmx differ diff --git a/assets/resources/dolphin_custom/WatchDogs/Icons/SubGhz/Fishing_123x52.bmx b/assets/resources/dolphin_custom/WatchDogs/Icons/SubGhz/Fishing_123x52.bmx new file mode 100644 index 000000000..7c576cb7b Binary files /dev/null and b/assets/resources/dolphin_custom/WatchDogs/Icons/SubGhz/Fishing_123x52.bmx differ diff --git a/assets/resources/infrared/assets/ac.ir b/assets/resources/infrared/assets/ac.ir index 4d9a85041..2b69e6f68 100644 --- a/assets/resources/infrared/assets/ac.ir +++ b/assets/resources/infrared/assets/ac.ir @@ -1,7 +1,7 @@ Filetype: IR library file Version: 1 -# Last Updated 13th Jan, 2023 -# Last Checked 31th Jan, 2023 +# Last Updated 07th Mar, 2023 +# Last Checked 07th Mar, 2023 # name: POWER type: raw @@ -1091,6 +1091,18 @@ type: raw frequency: 38000 duty_cycle: 0.330000 data: 270 18152 3021 8955 523 499 495 1497 492 504 500 468 526 496 498 498 496 499 495 501 493 502 492 1499 500 496 498 524 470 1521 498 497 497 499 495 1496 492 1499 500 1491 497 1494 494 1497 502 494 500 495 499 497 497 498 496 500 494 528 466 530 464 531 473 522 493 503 491 504 500 495 499 497 497 498 496 500 494 501 493 503 491 504 500 495 499 496 498 498 496 499 495 501 493 529 465 531 473 522 472 523 492 504 490 505 499 496 498 498 496 499 495 1496 492 1499 500 1492 496 1494 525 2947 2999 8953 525 1519 469 499 516 507 497 498 496 500 494 501 493 503 491 504 500 495 499 1492 496 499 495 501 493 1498 501 495 499 1492 496 1522 466 1524 496 1496 492 1499 500 1492 496 499 495 500 494 502 492 504 500 495 499 496 498 498 496 499 495 500 494 502 492 530 474 521 473 523 523 472 522 474 489 506 498 497 497 499 495 500 494 502 492 503 501 494 500 496 498 497 497 499 495 500 494 502 492 503 501 521 473 523 471 524 522 474 520 475 498 497 497 499 495 500 494 2978 2999 8952 525 1492 496 499 495 501 493 503 491 504 500 495 499 497 497 525 469 526 468 1524 516 479 494 501 493 503 491 504 500 495 499 497 497 1494 494 1497 492 1500 499 1492 496 499 495 1497 491 531 473 1518 522 1469 499 496 498 498 496 500 494 1497 491 1500 499 1492 496 499 495 501 493 502 492 504 500 495 499 523 471 525 469 526 520 1472 516 1475 493 502 492 504 500 495 499 1492 496 499 495 501 493 502 492 504 500 495 499 496 498 498 496 1522 466 1525 525 1466 491 1500 499 +# POWER_ON +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4455 4310 587 1553 587 481 588 1552 587 1553 587 482 587 483 586 1553 585 483 586 483 587 1553 586 483 586 483 585 1553 586 1553 586 483 586 1553 586 1553 586 483 586 483 585 1553 586 1553 587 1552 586 1553 585 1554 585 483 586 1552 588 1552 585 483 586 483 586 483 586 483 585 484 585 484 585 483 586 483 586 483 585 484 586 483 586 483 586 483 586 1553 585 1553 585 1554 585 1553 585 1554 585 1553 586 1553 585 1554 584 5128 4426 4312 584 1554 585 484 585 1553 585 1554 585 483 586 483 585 1554 584 484 585 484 584 1553 584 485 584 484 585 1554 584 1555 583 485 584 1552 586 1553 585 484 585 483 585 1553 583 1554 585 1553 584 1554 583 1554 584 485 584 1554 584 1554 583 484 585 484 585 484 584 484 584 485 584 484 585 484 584 485 583 485 583 484 585 484 585 484 584 485 583 1555 584 1554 584 1553 584 1553 585 1553 584 1553 585 1553 584 1555 583 +# POWER_ON +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 530 374 30128 50095 3452 1625 471 1229 471 459 471 459 471 459 444 459 471 459 470 459 471 459 471 459 471 459 444 460 470 459 471 1230 470 459 470 460 443 460 470 460 469 460 470 460 470 460 443 1231 470 1230 470 460 470 460 470 460 443 487 443 460 469 460 470 461 468 461 469 1232 442 461 469 1232 468 1232 442 1232 468 1232 468 1232 468 1232 441 461 469 1232 468 1232 442 488 442 461 469 462 468 463 466 463 467 485 418 488 442 485 444 1234 467 1256 418 1256 444 1256 444 1256 444 1256 418 1256 444 1256 444 485 444 486 417 486 444 1256 444 485 444 485 444 486 417 486 444 1257 443 486 444 485 444 1257 417 486 444 486 443 486 444 1257 417 1257 443 486 444 486 444 486 443 486 417 513 416 487 443 487 442 486 444 1257 416 1257 443 487 442 486 444 486 443 487 416 513 416 487 443 486 443 487 443 487 442 487 416 514 415 1258 442 487 442 487 443 487 416 514 416 488 442 487 443 488 442 488 442 1258 415 1258 442 488 442 488 441 488 442 488 415 488 441 488 442 488 442 1259 441 1259 415 489 440 489 441 488 442 489 440 488 415 1259 441 1260 440 1260 414 516 414 489 439 491 440 489 440 490 439 490 414 516 413 490 438 491 438 492 438 491 415 539 389 517 413 514 391 516 414 539 414 516 390 539 388 542 363 540 390 1311 389 540 389 540 363 567 363 540 390 540 389 540 390 540 390 540 363 567 363 541 389 540 390 540 389 540 390 541 362 567 363 541 389 541 389 541 389 541 389 541 362 568 361 541 389 541 389 542 388 541 389 541 362 542 388 542 387 542 388 542 388 543 387 567 335 567 363 567 363 567 363 567 363 567 336 594 335 568 362 567 362 568 361 568 362 567 336 595 335 568 362 568 361 568 362 1338 362 1339 334 569 361 569 360 569 361 569 360 569 334 570 360 570 360 595 334 595 334 1366 307 1366 334 595 335 595 335 595 335 596 306 623 307 596 334 596 333 597 333 622 307 597 306 597 333 623 306 623 306 624 306 624 306 624 278 1395 305 1395 305 650 279 651 360 # name: TEMP+ type: raw @@ -1541,78 +1553,130 @@ name: TEMP- 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 -# Model: Daikin FTXM20M. +data: 3110 1567 551 1031 550 356 471 1032 550 356 471 356 445 1084 497 355 472 357 497 356 472 1058 523 1058 523 356 471 356 470 357 469 357 470 357 474 357 470 1063 519 357 470 1063 518 1063 519 1063 518 357 470 357 471 # -name: Off +name: POWER type: raw frequency: 38000 duty_cycle: 0.330000 -data: 500 393 473 392 473 368 498 368 497 367 499 25050 3533 1662 502 1230 501 392 472 393 471 395 469 1263 467 400 465 401 465 401 465 401 465 1267 465 401 465 1268 464 1268 464 402 464 1268 464 1269 463 1269 463 1292 440 1269 463 404 462 426 439 1293 439 426 440 426 440 426 440 426 440 426 440 427 439 426 440 427 439 427 438 427 439 1294 438 427 439 1294 438 427 439 427 439 428 438 1294 438 1294 438 428 438 428 437 428 438 428 438 1294 438 428 438 428 438 429 437 429 437 429 436 429 437 429 437 429 437 429 436 429 437 430 436 1296 436 1296 436 1296 436 430 436 430 435 1297 435 1297 435 1298 434 35482 3500 1699 464 1268 464 402 463 402 463 403 463 1269 463 426 440 426 439 426 440 426 439 1293 439 426 440 1293 439 1293 439 427 439 1293 439 1293 439 1293 439 1293 439 1294 438 427 438 427 439 1294 438 427 439 428 438 428 438 428 438 427 438 428 438 428 437 428 438 428 437 428 438 428 438 1295 437 429 437 429 437 429 436 429 437 1296 436 429 437 429 436 429 437 430 436 430 436 430 435 430 436 430 435 431 435 431 435 431 435 455 410 456 409 1322 410 456 409 456 410 456 410 456 410 456 410 1323 409 457 409 457 409 1323 409 1323 409 457 409 35483 3500 1699 464 1268 464 402 464 402 463 403 463 1269 463 426 440 426 440 426 440 426 439 1293 439 426 439 1293 439 1293 439 427 439 1293 439 1293 439 1293 439 1294 438 1293 438 427 439 427 438 1294 438 428 438 427 438 428 438 428 438 428 438 428 437 428 438 428 438 428 438 428 438 428 438 428 438 429 437 429 437 429 437 429 437 429 437 429 437 429 437 429 437 430 436 1296 436 430 436 1297 435 430 436 430 436 431 435 431 435 456 410 456 410 456 410 456 409 1323 409 1322 410 456 410 456 409 457 409 457 409 457 409 457 409 457 408 457 409 1324 408 1324 408 1324 408 1324 408 458 408 1325 406 459 407 1351 356 1376 356 1376 356 1376 356 1377 355 510 355 510 356 511 355 511 354 511 355 537 328 537 329 538 328 538 327 538 328 539 327 565 300 566 300 1432 300 1433 298 593 272 594 271 621 245 621 244 622 243 +data: 3087 1607 488 1064 517 335 492 1087 484 315 512 340 487 1091 490 336 491 338 489 1089 492 333 494 332 485 341 486 340 487 338 489 336 491 339 488 1090 491 334 493 1085 486 340 487 1091 490 1088 493 333 484 343 484 44227 178 # -name: Dh +name: TIMER type: raw frequency: 38000 duty_cycle: 0.330000 -data: 500 368 498 369 497 367 499 366 499 367 499 25050 3533 1662 502 1230 501 392 472 393 471 395 469 1264 467 400 465 401 465 401 465 401 465 1267 465 401 465 1268 464 1268 464 402 464 1269 463 1269 463 1269 463 1292 440 1292 440 426 440 426 440 1293 439 426 440 426 439 426 440 426 440 427 439 427 438 427 439 427 439 427 439 427 439 1293 439 427 439 1294 438 427 439 427 439 428 437 1294 438 1294 438 428 437 428 438 428 437 429 437 1295 437 428 437 429 437 429 437 429 436 429 437 429 437 429 437 429 437 429 437 429 437 430 436 1296 436 1297 435 1297 435 431 435 455 410 1298 434 1322 410 1322 410 35482 3500 1700 464 1269 463 403 463 403 463 403 462 1292 440 426 440 426 440 426 440 426 439 1293 439 426 440 1293 439 1293 439 427 439 1293 439 1293 439 1293 439 1293 439 1293 439 427 439 427 439 1294 438 427 439 427 439 427 439 428 438 427 438 428 438 428 438 428 437 428 438 428 438 428 438 1295 437 429 437 429 436 429 437 429 437 1296 436 429 437 429 437 429 437 430 436 430 436 430 436 431 435 431 435 431 435 455 410 456 410 456 410 456 410 1322 410 456 410 456 410 456 409 457 409 456 409 1323 409 457 409 457 409 1323 409 1324 408 458 408 35483 3500 1700 464 1268 464 402 464 403 463 402 464 1292 440 426 440 426 440 426 439 426 440 1293 439 426 440 1293 439 1293 439 427 439 1293 439 1293 439 1293 438 1293 439 1293 439 427 439 427 438 1294 438 428 438 428 438 427 438 428 438 428 438 428 438 428 438 428 438 428 438 428 437 428 438 429 436 429 437 429 437 429 437 429 437 429 437 429 437 1296 436 430 435 430 436 1297 435 431 435 1297 435 431 435 456 409 456 410 456 409 456 410 456 410 456 410 456 410 1323 409 1323 409 457 409 457 409 457 408 457 409 457 409 458 408 458 407 458 408 1325 407 1326 405 1327 406 1351 381 485 356 1376 356 510 355 1377 355 1377 355 1377 355 1378 354 1378 354 512 354 512 354 538 327 538 328 538 328 539 326 565 300 566 300 566 299 567 299 593 272 621 244 596 270 1488 244 +data: 3092 1602 492 1059 512 341 486 1092 489 337 490 336 491 1087 494 332 485 344 493 1085 486 1093 488 1064 517 1061 520 333 494 332 484 341 486 343 484 1094 487 1091 490 336 491 335 492 334 493 333 484 1094 487 340 487 # -name: Cool_hi +name: POWER type: raw frequency: 38000 duty_cycle: 0.330000 -data: 504 389 477 364 501 364 502 364 501 364 502 25048 3511 1684 505 1228 504 389 475 390 474 392 472 1260 470 396 469 396 470 396 469 397 469 1263 469 397 469 1263 469 1263 469 397 468 1263 469 1263 469 1264 468 1263 469 1264 468 397 468 398 468 1264 468 398 468 398 468 398 468 398 468 398 468 399 467 398 468 399 467 423 443 400 466 1289 443 423 443 1289 443 423 443 423 443 423 443 1289 443 1290 442 424 442 423 442 423 443 424 442 1290 442 424 442 423 443 424 442 424 441 424 442 424 441 424 442 424 442 424 441 424 442 424 442 1291 441 1291 441 1291 441 425 441 425 441 1291 441 1291 441 1291 441 35478 3505 1695 468 1264 468 398 467 398 468 398 468 1265 467 398 467 398 468 398 468 399 467 1265 467 399 467 1266 466 1266 466 423 443 1289 443 1290 442 1290 442 1290 442 1290 442 424 442 423 442 1290 442 423 443 424 442 424 442 424 442 424 441 424 442 424 442 424 441 424 442 424 442 424 442 1291 441 425 441 425 441 424 442 424 441 1291 441 425 441 425 440 425 441 425 441 425 441 425 441 425 441 425 440 425 441 425 441 425 440 426 440 426 440 1292 440 426 440 426 440 426 440 426 439 427 439 1293 439 427 439 427 438 1294 438 1294 438 428 437 35479 3504 1695 468 1264 468 398 468 398 468 398 467 1265 467 399 467 399 467 399 466 399 467 1265 467 399 467 1289 443 1290 442 423 443 1290 442 1290 442 1290 442 1290 442 1290 442 423 443 424 442 1290 442 424 442 424 442 424 441 424 442 424 442 424 442 424 442 424 442 424 442 424 442 424 442 424 442 425 441 425 440 425 441 425 441 425 441 425 441 1291 441 425 441 425 441 1292 440 1292 440 1292 440 426 440 425 441 426 440 426 440 1292 440 426 440 426 440 1292 440 426 440 426 440 426 440 427 439 426 440 426 440 427 438 427 439 427 439 427 439 1294 438 1295 437 1294 438 1319 413 453 413 1319 413 453 412 1320 412 1319 413 1319 413 1319 413 1320 412 453 412 453 413 453 412 454 412 454 411 454 412 454 412 454 412 454 412 454 412 455 411 454 412 455 411 1321 411 1321 411 456 409 456 410 481 384 482 384 482 383 482 359 506 360 507 359 507 359 507 358 1374 358 1374 358 508 357 508 358 509 357 534 332 534 332 534 332 534 332 535 331 535 331 535 331 535 330 536 330 536 330 562 303 563 303 563 302 564 302 1430 302 565 301 564 302 591 274 619 247 619 247 1512 219 1539 190 +data: 8838 3941 624 1427 514 500 515 497 541 473 540 1487 540 474 539 474 538 476 512 1538 514 1516 544 466 514 497 514 501 514 499 514 500 513 499 513 501 487 525 514 499 512 502 487 526 512 1517 542 470 487 1539 513 499 512 503 513 499 512 1513 512 # -name: Cool_lo +name: POWER type: raw frequency: 38000 duty_cycle: 0.330000 -data: 502 390 476 365 501 365 500 365 500 365 501 25049 3535 1660 504 1229 502 390 474 391 473 393 471 1261 469 397 468 398 468 398 468 398 468 1264 468 398 468 1265 467 1265 467 399 467 1265 467 1265 467 1265 467 1266 466 1265 467 400 466 401 465 1266 466 401 465 401 465 401 465 424 442 424 441 424 442 424 441 424 442 424 442 424 442 1291 441 424 442 1291 441 424 442 425 440 425 441 1291 441 1291 441 425 441 425 441 425 440 425 441 1292 440 425 441 425 441 425 441 425 441 425 441 425 441 425 441 425 440 426 440 426 440 426 440 1292 440 1293 439 1293 439 426 439 427 439 1293 439 1293 439 1293 439 35480 3503 1696 467 1265 467 399 467 398 468 399 466 1266 466 399 467 399 467 399 467 399 467 1266 466 400 466 1267 465 1290 442 424 442 1290 442 1290 442 1290 442 1290 441 1291 441 424 442 424 442 1291 441 424 442 424 442 425 441 424 442 424 442 425 441 425 440 425 441 425 441 425 441 425 441 1292 440 425 441 425 441 425 440 426 440 1292 440 426 440 426 439 426 440 426 439 426 440 426 440 426 440 426 440 426 440 427 438 427 439 427 439 427 439 1293 439 427 438 427 439 428 438 428 438 428 438 1294 438 428 438 428 438 1295 437 1320 412 453 413 35480 3503 1696 467 1265 467 399 467 399 467 399 467 1266 466 399 467 399 467 400 466 400 466 1290 442 424 442 1289 443 1290 442 424 442 1291 441 1290 442 1290 442 1291 441 1291 441 424 442 424 442 1291 441 425 441 425 441 425 441 425 441 425 441 425 441 425 441 425 441 425 440 425 441 425 441 425 441 425 441 425 441 425 440 426 440 426 440 426 440 1292 440 426 440 426 440 1292 440 1293 439 1293 439 427 439 426 439 427 439 1293 439 1293 439 1293 439 427 438 1294 438 427 439 427 438 428 438 427 438 453 413 429 437 429 437 429 437 453 412 454 412 1320 412 1320 412 1320 412 1320 412 454 412 1321 411 454 412 1320 412 1321 411 1321 411 1321 411 1321 411 455 410 455 411 455 411 455 411 455 411 456 410 456 410 457 409 481 384 457 409 482 383 483 383 483 358 1374 358 1374 358 508 357 508 358 509 357 509 357 509 357 509 357 510 356 535 330 536 330 536 330 1402 330 1403 329 537 329 536 329 563 302 564 302 564 302 565 300 565 300 592 273 619 246 620 246 619 246 620 245 674 188 +data: 8364 4223 621 1958 1115 1478 593 1986 1082 1566 1072 1497 576 2003 1069 1513 1069 1541 1070 1505 1069 1505 1069 1497 1070 1550 1069 1530 1045 1522 551 2028 1045 1582 1044 1530 1044 1522 1045 1530 1044 1566 1045 1530 1045 1530 550 2021 550 2064 1044 1530 1044 1523 1043 1531 1044 1566 1045 1531 1043 1531 1044 1523 1044 1575 1043 1531 1044 1523 1044 1531 1044 1567 1044 1531 1043 1531 1044 1524 1043 1575 1043 1532 1043 1524 1043 1532 1043 1568 1042 1532 1043 1532 1043 1524 1043 1576 1042 1532 1043 1524 1043 1532 1042 1569 1042 1533 1016 1558 1042 1525 1042 1577 1016 1558 523 2048 1042 1525 547 2071 1041 1533 523 2049 522 2049 522 # -name: Heat_hi +name: MODE type: raw frequency: 38000 duty_cycle: 0.330000 -data: 504 365 500 365 500 365 500 365 501 365 501 25049 3535 1660 504 1229 502 390 474 391 473 393 471 1261 470 397 468 397 469 398 468 398 468 1264 468 398 467 1265 467 1265 467 398 468 1265 467 1265 467 1265 467 1266 466 1266 466 399 466 399 467 1266 466 400 466 400 466 400 465 400 466 424 442 424 442 424 442 424 441 424 442 424 442 1291 441 424 442 1291 441 424 442 424 442 425 441 1291 441 1291 441 425 440 425 441 425 441 425 440 1292 440 425 441 425 440 425 441 425 441 425 441 425 441 425 441 426 440 426 440 426 440 426 439 1293 439 1293 439 1293 439 426 440 427 439 1293 439 1293 439 1293 439 35480 3503 1696 468 1264 468 398 468 398 468 398 467 1265 467 399 467 399 467 399 467 399 467 1267 465 400 466 1266 466 1267 465 424 441 1290 442 1290 442 1290 442 1290 442 1291 441 424 442 424 442 1291 441 424 442 424 442 424 442 424 442 424 441 425 441 425 441 425 441 425 441 425 441 425 441 1292 440 425 441 425 441 425 441 425 441 1292 440 426 440 426 439 426 440 426 440 426 439 426 440 426 440 426 439 427 439 426 440 427 439 427 439 427 438 1293 439 427 439 427 439 427 439 428 438 428 438 1295 437 428 438 429 436 1296 436 1296 436 453 412 35481 3502 1696 467 1265 467 399 466 399 467 399 467 1265 467 399 467 399 467 399 466 400 466 1266 466 400 466 1290 442 1267 465 424 442 1290 442 1290 442 1290 442 1290 442 1291 441 424 442 424 441 1291 441 424 442 424 442 424 442 424 441 425 441 425 441 425 441 425 441 425 441 425 441 425 441 425 441 425 440 426 440 425 441 425 441 425 441 426 440 1292 440 426 440 426 440 1292 440 426 440 426 439 1293 439 427 439 427 439 427 439 1293 439 1294 438 1294 438 1293 439 427 439 428 438 428 438 428 438 429 436 429 437 429 437 453 412 453 413 453 412 1320 412 1320 412 1320 412 1320 412 454 412 1320 412 454 412 1321 411 1321 411 1321 411 1321 411 1322 410 455 411 455 411 456 410 456 410 455 410 456 410 456 409 481 384 458 408 482 383 482 384 483 358 508 358 1374 358 1374 358 508 357 509 357 509 357 510 355 535 330 536 330 536 330 536 329 536 330 536 330 1403 329 1430 301 564 302 564 302 564 301 591 274 566 301 592 273 619 247 619 247 620 245 647 219 -# -name: Heat_lo +data: 8362 4223 595 1983 1116 1480 592 1987 1081 1567 1070 1524 550 2029 1043 1540 1042 1568 1043 1532 549 2030 1042 1540 1042 1568 1042 1532 1042 1532 549 2022 549 2065 1043 1528 1043 1532 1043 1524 1042 1576 1042 1532 1042 1525 548 2030 549 2082 1042 1524 1042 1533 1042 1525 1042 1576 1042 1533 1042 1525 1041 1533 1042 1569 1042 1533 1042 1533 1042 1525 1041 1577 1041 1533 1042 1525 1041 1534 1041 1569 1042 1533 1041 1534 1040 1526 1041 1578 1041 1534 1041 1526 1040 1534 1041 1570 1041 1534 1015 1560 1015 1552 1015 1604 1040 1535 1040 1527 1039 1535 1014 1597 1013 1561 1014 1561 1013 1554 519 2129 987 1554 544 2036 518 2093 987 +# POWER_OFF +name: POWER type: raw frequency: 38000 duty_cycle: 0.330000 -data: 503 365 500 364 501 366 499 365 500 364 502 25049 3535 1660 504 1228 503 390 474 391 473 393 471 1261 469 397 468 397 469 397 469 397 469 1264 468 398 468 1264 468 1264 468 398 468 1265 467 1265 467 1265 467 1265 467 1265 467 399 467 399 467 1266 466 399 467 400 466 400 466 400 466 423 443 423 443 401 465 423 442 424 442 424 442 1290 442 424 442 1290 442 424 442 424 441 424 442 1290 442 1291 441 425 441 424 442 425 441 425 441 1291 441 425 440 425 441 425 441 425 441 425 441 425 440 425 441 425 441 425 441 425 441 426 440 1292 440 1292 440 1292 440 426 440 426 440 1292 440 1293 439 1293 439 35480 3503 1696 467 1264 468 398 468 398 467 398 468 1265 467 398 467 399 467 399 466 399 467 1265 467 399 467 1266 466 1267 465 400 466 1290 442 1290 442 1290 442 1290 442 1290 442 424 442 424 442 1290 442 424 441 424 442 424 442 424 442 424 442 424 441 424 442 425 441 424 442 425 441 425 441 1291 441 425 441 425 441 425 441 425 440 1292 440 425 441 425 440 426 440 426 440 426 440 426 440 426 440 426 440 426 440 426 439 427 439 426 440 426 440 1293 439 427 439 427 439 427 438 427 439 428 438 1294 438 428 437 428 438 1295 437 1319 413 453 413 35480 3503 1696 468 1265 467 398 468 398 468 398 468 1265 467 398 468 399 466 399 467 399 467 1266 466 399 466 1267 465 1290 442 401 465 1290 442 1290 442 1290 442 1290 442 1290 442 424 442 424 441 1291 441 424 442 424 442 424 442 424 442 424 441 424 442 425 441 424 442 424 441 425 441 425 441 425 440 425 441 425 441 425 441 425 441 425 441 425 441 1292 440 426 440 426 440 1292 440 426 440 426 440 1293 439 426 440 426 440 1293 439 1293 439 1293 439 427 439 1294 438 427 438 427 439 427 438 428 438 428 438 428 438 428 438 453 413 429 437 453 413 1319 413 1320 412 1320 412 1320 412 454 412 1320 412 454 412 1321 411 1321 411 1321 411 1321 411 1321 411 455 410 455 411 455 411 455 410 456 410 456 410 456 410 481 384 481 385 482 383 482 383 483 358 507 359 1374 358 1374 358 508 358 508 358 508 358 509 357 509 357 535 331 535 331 535 330 535 330 536 330 1403 329 1403 329 563 302 564 301 564 302 564 301 565 301 591 274 619 246 593 273 620 245 620 245 621 245 673 189 -# Model: Mitsubishi SRK63HE. -# -name: Off +data: 4453 4313 586 1554 585 483 586 1554 587 1552 587 483 587 484 586 1552 587 482 588 483 586 1553 586 483 587 483 586 1554 586 1553 585 484 587 1553 586 484 587 1553 586 1554 586 1553 586 1554 586 483 586 1553 586 1553 586 1555 585 483 586 482 588 483 585 484 585 1554 586 483 587 484 585 1553 587 1554 584 1554 585 484 586 484 586 483 586 483 586 484 586 482 588 483 584 484 586 1553 585 1555 584 1555 586 1553 586 1554 585 5129 4428 4312 585 1553 586 483 586 1555 584 1553 586 484 583 486 584 1555 585 484 585 483 586 1554 585 483 586 484 585 1554 585 1554 585 484 585 1554 585 484 584 1554 585 1554 584 1554 585 1554 586 483 585 1554 585 1555 584 1553 584 484 586 483 586 483 585 484 586 1554 583 484 586 483 585 1553 585 1553 585 1553 585 484 584 485 585 485 584 484 585 484 585 485 584 484 585 483 586 1554 585 1553 585 1554 584 1553 585 1553 585 +# TEMP_17 +name: TEMP- type: raw frequency: 38000 duty_cycle: 0.330000 -data: 3232 1526 463 334 462 1127 464 334 460 334 461 1128 463 336 459 1128 464 359 436 359 436 1133 458 1156 434 1157 434 362 433 1159 432 363 432 1159 432 1159 432 1159 432 363 433 363 432 363 432 363 432 1160 431 1160 432 363 432 1160 431 1160 431 363 432 364 432 1160 431 363 432 363 432 1160 432 363 432 364 431 1160 431 1160 431 364 431 1160 431 1160 431 1160 431 1160 431 1160 431 1160 431 364 431 1160 431 1160 431 1160 431 364 432 364 432 364 431 364 431 1160 432 364 432 364 431 364 431 1160 431 1160 431 1160 431 364 431 1161 430 1161 430 1160 431 1161 430 364 432 364 431 365 431 1161 430 364 431 365 431 364 431 365 430 365 431 1161 430 1161 430 1161 430 365 430 1161 430 365 430 365 430 1161 430 365 430 365 431 365 430 1161 430 365 430 1161 430 1161 430 -# -name: Dh +data: 4459 4311 588 1552 588 482 588 1552 589 1552 588 481 589 481 589 1552 588 482 587 483 587 1553 587 482 588 481 589 1553 588 1552 588 482 587 1554 586 483 587 1553 587 482 588 1552 588 1553 587 1553 588 1552 588 1552 588 1553 587 482 587 1553 588 481 589 482 587 482 587 483 587 482 587 483 588 482 587 483 587 482 587 483 587 482 587 483 586 483 587 1553 587 1553 587 1553 587 1554 586 1553 588 1553 587 1554 587 1553 587 5130 4431 4314 586 1553 588 482 588 1553 586 1554 587 483 587 483 587 1553 587 483 587 483 586 1554 587 483 586 483 587 1553 587 1553 587 482 588 1553 587 482 587 1553 587 483 587 1553 587 1554 586 1554 586 1554 586 1553 587 1554 587 482 588 1553 587 483 586 484 586 483 586 483 587 483 587 483 587 483 586 483 586 483 587 483 586 484 586 483 587 483 586 1554 586 1554 586 1554 586 1554 586 1554 586 1554 586 1555 586 1554 586 +# TEMP_30 +name: TEMP+ type: raw frequency: 38000 duty_cycle: 0.330000 -data: 3229 1528 461 335 461 1128 463 334 461 335 460 1129 463 337 458 1129 463 359 436 360 434 1156 434 1157 433 1159 432 364 431 1161 430 364 431 1161 430 1161 430 1160 431 364 431 364 431 365 430 365 430 1161 430 1161 430 365 430 1161 430 1161 431 365 430 365 431 1161 430 365 430 365 430 1161 430 365 430 365 431 1161 430 1161 430 365 431 1161 430 1161 430 1161 430 1161 430 1161 431 1161 430 365 430 1161 430 1161 430 1161 431 365 430 365 430 365 430 365 430 1162 430 365 430 365 430 365 431 1162 429 1162 429 1162 430 365 430 1162 429 1162 429 1162 429 1162 429 366 430 366 429 366 430 1162 429 366 429 366 430 366 429 366 429 1162 429 366 429 1163 428 366 429 1162 429 366 429 366 429 1162 429 366 430 1162 429 366 429 1163 429 366 430 1163 428 1163 428 367 428 -# -name: Cool_hi +data: 4489 4254 647 1493 645 425 619 1521 619 1521 619 452 617 453 616 1526 613 478 591 479 590 1551 589 481 588 481 589 1552 588 1552 588 482 588 1552 588 482 588 1552 588 482 588 1552 588 1553 587 1553 587 1552 588 1553 588 1552 588 482 588 1553 587 482 588 482 588 482 588 482 588 482 588 1552 588 482 588 1552 588 1552 588 482 588 482 588 482 588 482 588 482 588 1553 587 482 588 482 588 1553 587 1553 587 1553 587 1553 587 5130 4432 4313 587 1553 587 483 587 1553 587 1553 587 483 587 483 587 1553 587 483 587 483 587 1553 587 483 587 483 587 1553 587 1554 586 483 586 1553 587 483 587 1553 587 483 587 1553 587 1553 587 1554 586 1554 586 1554 586 1554 587 483 587 1553 587 483 587 483 587 483 587 483 587 483 587 1554 586 483 587 1554 586 1554 586 483 587 483 587 483 587 483 586 484 586 1554 586 484 586 483 587 1554 586 1554 586 1554 586 1554 586 +# POWER_OFF +name: POWER type: raw frequency: 38000 duty_cycle: 0.330000 -data: 3230 1526 463 335 460 1128 464 333 461 335 460 1128 463 337 458 1128 463 359 436 359 436 1155 435 1157 433 1158 432 363 432 1160 431 364 431 1161 430 1160 431 1160 431 364 431 364 431 364 431 364 431 1161 430 1161 430 364 431 1161 430 1160 431 365 430 364 431 1161 431 364 431 364 431 1161 430 365 430 365 431 1161 430 1161 431 364 431 1161 430 1161 430 1161 430 1161 431 1161 430 1161 431 365 430 1161 431 1161 430 1162 430 365 430 365 430 365 431 365 430 1161 430 365 430 365 430 365 431 1161 430 1162 430 1162 429 365 430 1162 429 1162 429 1162 429 1162 429 366 429 366 430 366 430 1162 429 366 430 366 429 366 429 366 430 366 429 1162 429 1163 429 366 429 366 429 1163 428 1163 428 1163 429 1163 428 366 429 366 430 1163 428 1163 428 367 428 367 429 366 429 +data: 479 397 30131 50087 3427 1674 447 1253 447 483 446 483 446 483 420 483 447 483 447 483 447 483 447 483 420 510 419 483 447 483 446 1254 446 483 420 510 419 484 446 483 447 484 445 483 446 484 419 1255 445 1254 446 484 445 484 445 484 419 484 446 484 446 484 445 484 446 484 445 1255 418 484 446 1255 445 1255 418 1255 445 1255 445 1254 446 1255 419 484 446 1255 445 1255 418 511 418 485 445 484 445 484 446 484 445 485 418 511 418 484 446 1255 445 1255 418 1255 445 1255 444 1256 444 1256 417 1256 445 1255 443 487 418 511 419 485 444 1255 445 485 445 485 420 509 418 485 445 1255 445 485 444 485 445 1256 417 512 418 485 444 485 445 1256 417 1256 444 485 444 485 420 509 444 486 418 511 418 485 445 485 444 1256 444 486 418 512 417 485 420 509 445 485 445 485 445 485 417 1256 444 486 444 485 445 1256 417 1256 443 486 444 485 444 486 444 485 444 485 418 485 444 485 444 485 444 486 443 486 417 1257 420 509 420 510 444 485 444 486 444 485 418 485 445 485 443 487 444 1256 444 1256 417 485 442 487 420 510 444 485 444 486 417 1256 420 1280 443 1257 417 512 393 510 444 486 442 487 443 486 419 510 417 513 419 484 419 510 444 486 419 510 419 510 393 537 415 488 420 510 418 510 419 510 419 510 392 537 392 511 445 484 419 511 418 511 418 511 392 538 391 511 419 511 419 511 418 511 418 511 391 511 419 512 418 511 418 511 419 511 418 512 391 512 418 512 417 512 417 512 418 512 417 512 391 512 418 512 417 512 417 512 417 512 391 539 390 513 416 513 417 512 417 513 417 537 366 564 365 514 416 537 392 537 392 538 392 538 365 564 365 538 392 538 392 538 391 538 391 538 365 564 365 538 392 538 391 1309 391 1308 365 538 391 538 392 538 391 538 392 538 365 565 364 538 392 538 391 539 391 1309 364 1309 391 538 391 539 391 538 391 538 391 539 364 538 392 539 390 539 391 539 390 539 364 565 364 539 391 539 390 1309 391 539 390 1309 364 539 391 539 390 539 391 539 498 # -name: Cool_lo +name: MODE type: raw frequency: 38000 duty_cycle: 0.330000 -data: 3231 1526 463 334 461 1127 465 333 461 334 460 1128 463 336 459 1129 463 359 436 359 436 1133 458 1156 434 1157 433 362 433 1159 432 363 432 1159 432 1160 431 1159 433 363 432 363 432 363 432 363 432 1159 433 1159 432 363 432 1159 432 1160 432 363 432 363 432 1159 432 363 432 363 433 1159 432 364 432 363 432 1160 431 1160 432 363 432 1160 431 1160 431 1160 431 1160 432 1160 431 1160 431 364 432 1160 431 1160 431 1160 431 364 431 364 431 364 432 364 431 1160 432 364 431 364 431 364 432 1160 431 1161 430 1161 430 364 431 1161 431 1161 430 1161 430 1161 430 364 432 364 431 364 431 1161 430 365 430 365 430 365 430 365 431 365 430 1161 430 1161 431 365 430 1161 430 365 430 365 431 1161 430 1161 430 365 431 365 430 1162 430 365 431 1161 430 1162 429 365 430 +data: 479 396 30132 50085 3427 1674 446 1254 446 483 446 483 446 483 420 483 446 483 447 483 446 483 446 483 420 510 419 483 446 483 446 1254 446 483 420 510 419 483 446 484 446 483 446 484 446 483 420 1254 446 1254 446 484 445 484 446 483 420 484 446 484 445 484 446 484 445 484 419 1255 445 484 445 1254 446 1255 418 1255 446 1255 445 1255 445 1254 419 484 446 1255 445 1255 418 511 419 484 446 484 445 484 445 484 445 485 418 511 418 484 446 1255 445 1255 418 1255 445 1255 445 1255 446 1255 418 1256 445 1255 445 484 419 511 418 485 444 1256 444 485 445 484 445 485 417 485 445 1256 444 485 444 485 445 1256 417 485 445 485 444 486 443 1256 417 1283 417 1256 444 485 420 509 445 485 417 485 445 485 443 486 443 1257 443 487 417 485 420 510 444 485 443 487 419 510 416 513 417 1256 442 487 420 510 419 1281 392 1281 419 510 443 486 420 510 443 487 416 513 417 486 443 486 443 486 444 486 419 510 417 1257 419 511 443 487 418 511 418 511 392 511 419 511 419 511 418 511 443 1258 391 1282 443 487 418 511 418 511 418 512 391 538 418 1256 417 1282 418 1282 391 512 417 512 417 512 417 512 417 513 417 512 391 513 416 536 393 536 393 536 393 537 366 563 367 537 393 536 393 536 393 537 393 537 366 564 366 537 393 537 393 1308 392 537 366 563 366 537 393 537 393 537 392 537 393 537 366 564 366 537 393 537 392 537 393 537 392 537 366 564 365 537 393 537 392 537 392 537 392 537 366 564 366 537 392 537 392 537 393 537 392 537 366 564 365 538 392 538 392 538 391 538 392 538 365 538 392 537 392 538 391 538 391 538 391 538 365 565 364 538 392 538 391 538 392 538 391 538 365 538 391 538 392 538 391 538 391 1309 364 1310 390 539 391 539 390 539 390 539 364 566 363 539 391 539 390 539 390 539 390 1310 363 1310 391 539 390 539 390 540 363 567 362 540 390 539 390 540 390 541 388 564 338 568 362 565 364 565 364 565 365 1335 338 1336 364 565 364 565 364 565 364 566 364 1309 391 # -name: Heat_hi +name: TEMP+ type: raw frequency: 38000 duty_cycle: 0.330000 -data: 3230 1526 463 334 461 1128 464 333 461 334 461 1128 463 336 459 1129 463 359 436 359 436 1155 435 1156 434 1158 433 363 432 1159 432 363 432 1159 432 1160 431 1160 431 363 433 363 432 363 432 363 432 1160 432 1160 431 364 431 1160 431 1160 432 364 431 364 431 1160 431 364 431 364 432 1160 431 364 431 364 431 1160 431 1160 431 364 432 1160 431 1160 431 1160 431 1160 431 1160 431 1160 431 364 431 1160 432 1160 431 1160 431 364 432 364 431 364 431 364 431 1161 430 364 431 364 432 364 431 1161 430 1161 430 1161 430 365 430 1161 431 1161 430 1161 430 1161 430 365 430 365 430 365 430 1161 430 365 431 365 431 365 430 365 431 1161 430 1161 430 365 430 365 430 365 430 1162 430 365 430 365 430 365 431 365 430 1162 429 1162 430 1162 429 366 429 1162 429 1162 429 +data: 531 372 30125 50084 3425 1651 471 1229 471 458 471 458 445 485 445 458 472 458 472 458 471 458 471 458 445 485 444 459 471 458 472 1229 471 458 445 485 444 459 470 459 471 459 471 459 470 459 444 1230 470 1230 470 459 471 459 470 459 444 459 470 459 471 459 470 459 470 460 470 1204 469 459 470 1230 470 1231 442 1231 469 1231 469 1231 469 1231 443 460 469 1231 469 1231 442 487 443 461 468 461 468 461 468 461 469 462 441 489 441 461 468 1255 445 1255 418 1282 418 1282 418 1256 444 1256 418 1256 444 1256 444 485 445 485 418 485 445 1256 444 485 444 485 444 485 418 485 444 1256 444 485 444 485 444 1256 417 485 445 485 444 485 444 485 444 485 418 512 417 1256 444 485 444 485 445 485 418 512 417 485 444 1256 444 486 443 486 416 513 417 486 444 486 443 486 443 486 444 486 417 1257 443 486 444 1257 443 1257 416 486 444 486 443 486 443 487 442 487 416 513 416 487 418 511 443 487 442 487 418 1282 440 462 442 487 442 488 442 487 443 487 416 514 415 487 418 512 442 1258 442 1258 414 489 418 512 417 512 417 512 440 490 440 1260 413 1260 417 1283 441 488 391 539 390 512 441 488 417 512 417 513 416 512 391 539 390 513 438 491 417 513 416 513 416 513 389 540 390 514 415 513 417 513 416 513 416 514 388 541 388 1285 415 538 391 514 415 538 365 541 389 538 391 538 391 538 391 538 391 539 364 565 364 538 392 538 391 538 391 539 390 539 364 539 390 538 391 539 390 539 390 539 391 539 363 539 390 539 390 539 390 539 390 539 390 539 363 540 390 539 390 539 390 539 391 539 364 566 363 540 390 540 390 540 390 539 390 540 362 566 363 540 390 540 389 540 389 540 390 540 362 567 362 540 390 540 389 1311 389 1311 362 566 363 541 389 566 363 566 363 566 363 566 337 566 363 566 363 566 363 1337 336 1363 337 566 363 566 363 566 363 566 363 566 363 540 363 567 362 567 362 567 362 567 336 594 335 567 363 1337 363 1338 362 568 334 594 335 568 362 568 362 568 361 1364 496 # -name: Heat_lo +name: TEMP- type: raw frequency: 38000 duty_cycle: 0.330000 -data: 3234 1525 463 333 462 1127 465 332 462 333 436 1153 518 307 488 1073 518 307 488 308 434 1131 459 1155 435 1156 434 362 433 1159 432 363 432 1159 432 1159 432 1159 433 363 432 363 432 363 433 363 432 1159 433 1159 432 363 432 1159 433 1159 432 363 432 363 432 1159 432 363 433 363 432 1159 432 363 432 363 432 1159 432 1159 432 363 432 1160 432 1160 431 1160 432 1160 431 1160 431 1160 431 364 431 1160 431 1160 431 1160 431 364 431 364 431 364 431 364 431 1160 431 364 431 364 431 364 431 1160 432 1160 431 1160 432 364 431 1160 431 1160 431 1161 431 1161 430 364 431 364 431 364 431 1160 432 364 431 364 431 364 432 364 431 1161 431 1161 431 364 431 364 431 1161 430 364 432 364 431 1161 430 365 431 365 431 1161 430 1161 430 365 431 1161 430 1161 430 365 430 +data: 479 398 30129 50086 3427 1675 446 1254 446 483 446 483 420 510 420 483 446 483 447 483 446 483 446 483 420 510 419 483 447 483 446 1254 446 484 419 510 420 484 446 483 446 484 446 484 445 484 419 1254 446 1254 446 484 445 484 445 484 419 484 446 484 446 484 445 484 446 484 419 1281 419 484 446 1255 445 1254 419 1254 446 1255 445 1255 445 1255 418 484 446 1255 445 1255 445 484 419 485 445 484 445 484 445 484 445 484 418 511 419 484 445 1255 445 1255 418 1282 418 1255 445 1255 445 1255 418 1282 418 1256 444 485 444 485 418 485 444 1256 443 486 444 485 445 485 418 485 444 1256 444 485 444 485 420 1281 418 485 443 486 444 485 420 1280 444 485 418 485 420 1280 444 486 443 487 417 512 416 487 420 510 444 1256 444 485 417 513 416 487 443 486 420 510 419 510 420 510 417 1257 443 486 419 510 419 1281 417 1257 419 510 420 510 420 509 419 511 418 511 392 511 419 510 443 486 419 511 418 510 393 1308 392 511 419 510 419 510 419 511 418 511 391 511 419 511 418 511 418 1281 419 1281 416 487 418 511 418 511 418 511 419 511 392 1281 419 1282 418 1282 392 537 392 511 418 511 419 511 419 511 419 511 392 538 392 512 418 511 419 511 418 512 418 512 391 538 391 512 418 512 417 512 418 512 417 512 391 539 390 512 417 1283 417 512 417 512 391 539 390 513 416 513 417 513 416 513 416 537 366 540 390 537 393 537 392 537 392 537 392 537 366 564 365 537 393 537 392 537 392 537 392 537 366 564 365 538 392 537 392 537 392 538 391 537 366 538 392 538 391 538 392 538 392 538 391 538 365 538 392 538 392 538 391 538 391 538 391 538 365 538 392 538 391 538 392 538 391 538 364 565 364 538 391 538 392 538 391 1309 364 1336 364 538 392 538 391 538 391 538 391 538 364 539 391 538 391 539 390 539 391 1309 364 1309 391 538 391 539 391 539 390 539 363 566 363 539 391 539 390 539 390 539 390 539 363 566 364 539 391 539 391 539 390 1310 363 566 364 539 391 539 390 540 389 1310 497 +# +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 454 414 452 413 453 413 453 413 453 412 454 25103 3488 1708 454 1278 454 413 453 413 453 413 453 1278 454 413 453 413 453 412 453 413 453 1278 479 388 478 1254 478 1255 477 389 476 1257 475 1283 449 1283 449 1284 448 1284 448 417 449 417 449 1284 448 417 449 417 449 417 449 417 449 418 448 417 449 418 448 417 449 418 448 418 448 1284 448 418 448 1284 448 418 448 418 448 418 448 1284 448 1284 448 418 448 418 448 418 448 418 448 1284 448 418 448 418 447 418 448 418 448 418 448 418 448 418 448 418 448 418 448 418 448 418 448 1284 448 1284 448 1285 447 418 448 418 448 1285 447 1285 447 1285 447 35478 3510 1713 449 1284 448 418 448 418 448 417 449 1284 448 418 448 418 448 418 448 418 448 1284 448 418 448 1284 448 1284 448 417 449 1284 448 1284 448 1284 448 1284 448 1284 448 418 448 418 448 1284 448 418 448 418 448 418 448 418 448 418 448 418 448 418 448 418 447 418 448 418 448 418 448 1284 448 418 448 418 448 418 448 418 448 1285 447 418 448 418 447 418 448 418 448 418 448 418 448 418 448 418 448 418 447 418 448 418 448 419 447 419 447 1285 447 418 448 418 448 418 448 418 448 418 448 1285 447 419 447 418 448 1285 447 1285 447 418 448 35479 3509 1713 449 1284 448 417 449 417 449 417 449 1284 448 418 448 418 448 418 448 418 448 1284 448 418 448 1284 448 1284 448 418 448 1284 448 1284 448 1284 448 1284 448 1284 448 418 448 418 448 1284 448 418 448 418 448 418 448 418 448 418 448 418 448 418 448 418 448 418 448 418 448 418 448 418 448 418 448 418 448 418 448 418 447 418 448 418 448 1285 447 418 448 418 448 1285 447 418 448 418 448 418 448 418 448 418 448 418 448 1285 447 418 448 418 448 1285 447 418 448 418 448 418 448 418 448 418 448 418 448 418 448 418 448 418 448 418 448 418 448 418 448 419 447 418 448 418 448 1285 447 419 447 1285 447 418 448 418 447 419 447 419 447 419 447 419 447 419 447 419 447 419 447 419 447 419 447 419 447 419 447 419 447 419 447 419 447 419 447 1285 447 1286 446 419 447 419 447 419 447 419 447 419 447 419 447 419 447 420 446 419 446 420 446 1286 446 1286 446 420 446 420 446 419 446 420 446 420 446 420 446 420 446 420 446 420 446 420 446 420 446 420 446 420 446 420 446 420 446 420 446 420 446 1287 445 420 446 420 446 420 446 420 446 421 445 1287 445 1287 445 421 445 421 445 421 445 421 445 421 445 421 445 421 445 1288 444 421 445 421 445 421 445 422 444 421 445 422 444 421 445 422 444 422 444 1288 444 1288 444 422 444 422 444 422 444 422 444 1289 443 +# +name: TEMP+ +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 453 414 452 413 453 412 454 414 452 413 453 25102 3490 1707 455 1278 483 384 482 383 482 383 482 1249 483 384 481 384 481 385 480 385 480 1251 481 387 478 1252 480 1253 478 414 451 1281 450 1282 449 1283 448 1283 449 1284 448 417 449 417 449 1284 448 417 449 417 449 417 449 417 449 418 448 417 449 417 449 417 449 417 449 417 449 1284 448 417 449 1284 448 417 449 417 449 417 449 1284 448 1284 448 417 449 417 449 417 449 418 448 1284 448 418 448 417 449 418 448 418 448 417 449 418 448 418 448 418 448 418 448 418 448 418 448 1284 448 1284 448 1284 448 418 448 418 448 1284 448 1284 448 1284 448 35479 3509 1713 449 1284 448 418 448 417 449 418 448 1284 448 417 449 418 448 417 449 418 448 1284 448 418 448 1284 448 1284 448 418 448 1284 448 1284 448 1284 448 1284 448 1284 448 418 448 418 448 1284 448 418 448 418 448 418 448 418 448 418 448 418 448 418 448 418 448 418 448 418 448 418 448 1284 448 418 448 418 448 418 447 418 448 1285 447 418 448 418 448 418 448 418 448 418 448 418 448 418 448 418 448 418 448 418 448 418 448 418 448 418 448 1284 448 418 448 418 448 418 448 418 447 418 448 1285 447 418 448 418 448 1285 447 1284 448 418 448 35478 3510 1713 449 1284 448 417 449 417 449 417 449 1284 448 417 449 417 449 417 449 417 449 1284 448 418 448 1284 448 1284 448 417 449 1284 448 1284 448 1284 448 1284 448 1284 448 418 448 417 449 1284 448 418 448 418 448 418 448 418 448 418 448 417 448 418 448 418 448 418 448 418 448 418 448 418 448 418 448 418 447 418 448 418 448 418 448 418 448 1284 448 418 448 418 448 1284 448 418 448 418 448 418 448 418 447 418 448 1284 448 1285 447 418 448 418 448 1284 448 418 448 418 448 418 448 418 448 418 448 418 448 418 448 419 447 418 447 419 447 418 448 418 448 418 448 418 448 1285 447 1285 447 419 447 1285 447 419 447 418 448 419 447 419 447 419 447 418 448 419 447 419 447 419 447 419 447 419 447 419 446 419 447 419 447 419 447 419 447 419 447 1285 447 1286 446 419 447 419 447 419 447 419 447 419 447 420 446 419 447 419 447 419 447 419 447 1286 446 1286 446 419 447 1286 446 419 447 420 446 419 446 420 446 420 446 420 446 420 446 420 446 420 446 420 446 420 446 420 446 420 446 420 446 420 445 1286 446 420 446 420 446 420 446 420 446 420 446 1287 445 1287 445 420 446 420 446 420 446 421 445 420 446 421 445 421 445 1287 445 421 445 420 446 421 445 421 445 421 445 421 445 421 445 421 445 1287 445 421 445 421 445 1287 445 1287 445 421 445 421 445 1287 445 +# +name: TEMP- +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 454 414 451 413 453 413 453 413 453 413 453 25102 3489 1708 454 1277 455 413 452 413 453 413 453 1278 454 412 454 412 454 412 454 412 453 1278 454 413 478 1253 479 1254 477 389 476 1257 475 1283 448 1284 448 1284 448 1284 448 417 449 417 449 1284 448 417 449 417 449 417 449 417 449 418 448 418 448 418 448 418 448 418 448 417 448 1284 448 417 449 1284 448 418 448 418 448 418 448 1284 448 1284 448 418 448 418 448 418 448 418 448 1284 448 418 448 418 448 418 448 418 448 418 448 418 448 418 448 418 448 418 448 418 448 419 447 1285 447 1285 447 1285 447 419 447 419 447 1285 447 1285 447 1285 447 35505 3483 1713 449 1284 448 417 449 417 449 418 448 1284 448 417 449 417 449 417 449 418 448 1284 448 418 448 1284 448 1284 448 418 448 1284 448 1284 448 1284 448 1284 448 1284 448 417 449 418 448 1284 448 417 449 418 448 418 448 418 448 418 448 418 448 418 448 418 447 418 448 418 448 418 448 1285 447 418 448 418 448 418 448 418 448 1284 448 418 448 418 448 418 448 418 448 418 448 418 448 418 448 418 448 418 448 418 448 418 448 418 448 418 448 1285 447 418 448 418 448 418 448 418 448 418 448 1285 447 418 447 418 448 1284 448 1285 447 418 448 35481 3508 1713 448 1284 448 417 449 418 448 418 448 1284 448 418 448 418 448 417 449 418 448 1284 448 418 448 1284 448 1284 448 418 448 1284 448 1284 448 1284 448 1284 448 1284 448 418 448 418 448 1285 447 418 448 418 448 418 448 418 448 418 448 418 448 419 447 418 448 418 448 419 447 419 447 419 447 419 447 419 447 419 447 419 447 419 447 419 446 419 447 419 447 419 447 1286 446 419 447 419 447 419 446 419 447 419 447 419 447 1286 446 419 447 419 447 1286 446 419 447 419 447 419 447 419 447 419 447 419 447 419 447 419 447 419 446 419 447 419 447 419 447 419 447 419 447 1286 446 1286 446 420 446 1286 446 420 446 420 446 419 446 420 446 420 446 420 446 420 446 420 446 420 446 420 445 420 446 421 445 420 446 421 445 421 445 420 446 421 445 1287 445 1287 445 421 445 421 445 422 444 446 420 446 420 423 443 446 420 446 420 446 420 446 420 1312 420 1312 420 446 420 446 420 446 420 446 420 446 420 446 420 446 420 446 420 446 420 446 420 446 420 446 420 446 420 446 420 446 419 446 420 446 420 1313 419 446 420 446 420 447 419 447 419 446 420 1313 419 1313 419 447 419 447 419 446 420 447 419 446 420 447 419 447 419 1313 419 447 419 446 420 447 419 447 419 447 419 447 419 447 419 447 419 1313 419 447 419 1313 419 447 419 1313 419 447 419 447 419 1313 419 +# +name: MODE +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 484 410 456 385 481 385 481 384 482 385 481 25074 3521 1676 483 1249 483 410 484 382 485 381 485 1247 485 381 484 381 485 381 484 382 483 1248 484 383 482 1249 482 1250 481 385 480 1253 478 1254 478 1255 477 1256 476 1256 476 389 477 389 477 1256 476 390 476 390 476 390 476 389 477 390 476 390 476 390 476 390 476 390 476 389 476 1256 476 390 476 1256 476 390 476 390 476 390 476 1256 476 1256 476 390 476 390 476 390 476 390 476 1257 475 390 476 390 476 390 476 390 476 390 476 390 476 390 476 390 475 391 475 391 475 390 476 1257 475 1257 475 1257 475 391 475 391 475 1257 475 1258 474 1258 474 35476 3516 1682 476 1256 476 390 476 389 477 389 477 1256 476 389 477 389 477 389 477 389 477 1256 476 389 477 1256 476 1256 476 390 476 1256 476 1256 476 1256 476 1256 476 1256 476 390 476 390 476 1256 476 390 476 390 476 390 476 390 476 390 476 390 476 390 476 390 476 390 476 390 476 390 476 1256 476 390 476 390 476 390 476 390 476 1257 475 390 476 390 476 390 475 391 475 390 476 391 475 391 475 391 475 391 475 391 475 391 475 391 475 391 475 1257 475 391 475 391 475 391 475 391 475 391 475 1258 474 392 474 392 474 1258 474 1259 473 416 450 35476 3516 1682 477 1257 475 390 476 390 476 390 476 1256 476 390 476 390 476 390 476 390 476 1257 475 390 476 1257 475 1257 475 390 476 1257 475 1257 475 1257 475 1257 475 1258 474 391 475 391 475 1258 474 391 475 391 475 391 475 391 475 391 475 391 475 391 475 391 475 391 475 391 475 391 475 391 475 391 475 392 474 391 475 391 475 391 475 392 474 392 474 391 475 392 474 1282 450 416 450 1283 449 416 450 416 450 416 450 416 450 393 473 416 449 416 450 416 450 1283 449 1283 449 417 449 417 449 416 449 417 449 416 450 416 450 417 449 416 450 416 450 417 449 416 450 417 449 417 449 1283 449 417 449 1283 449 417 449 417 449 417 449 417 449 417 449 417 449 417 448 417 449 417 449 417 449 417 449 417 449 417 449 417 449 417 449 417 449 417 449 1284 448 1283 449 417 449 417 449 417 449 417 449 417 449 417 449 417 449 417 449 417 449 417 449 1284 448 1284 448 417 449 418 448 417 449 418 448 418 448 418 448 418 448 418 448 418 448 418 448 417 449 418 447 418 448 418 448 418 448 418 448 418 448 1284 448 418 448 418 448 418 448 418 448 418 448 1284 448 1285 447 418 448 418 448 418 448 418 448 418 448 418 448 418 448 1285 447 418 448 418 448 418 448 418 448 418 448 419 447 418 448 418 448 1285 447 418 447 419 447 419 447 418 448 419 447 1285 447 419 447 +# +name: MODE +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 536 335 510 363 509 363 509 335 565 311 505 25378 3540 1697 455 1317 427 418 455 417 455 417 455 1289 456 390 455 417 455 417 455 417 428 1317 454 392 480 1265 480 1265 480 393 479 1268 476 1269 476 1269 476 1269 476 1269 476 397 475 369 476 1269 476 396 476 397 476 396 449 397 476 396 476 397 448 396 476 397 475 397 476 370 475 1270 475 397 475 1270 475 397 475 397 448 397 475 1271 474 1271 474 398 474 398 474 371 474 398 475 398 474 398 447 398 475 398 474 398 447 399 474 400 472 423 449 371 474 399 473 399 473 423 422 1324 421 1324 421 1324 421 423 450 1296 449 423 449 1297 448 1297 448 34986 3534 1704 476 1269 476 396 476 369 476 396 476 1270 475 396 476 369 476 397 476 396 476 1270 475 397 448 1297 448 1297 449 397 476 1270 475 1270 475 1270 475 1270 475 1270 476 397 475 397 475 1270 475 370 475 397 476 397 475 397 448 398 474 397 475 397 476 370 475 398 474 397 475 398 447 1298 447 398 475 398 474 398 447 398 474 1272 473 399 473 398 474 372 473 399 473 399 474 423 422 399 474 423 449 423 449 396 449 423 450 423 449 1296 449 1296 449 423 422 424 449 423 449 423 422 423 449 1297 448 1297 448 424 448 1297 448 1297 448 423 450 34986 3535 1703 476 1269 476 369 476 396 476 396 477 1269 476 396 449 396 477 396 476 396 476 1270 475 369 476 1270 475 1270 475 397 476 1270 475 1270 475 1270 475 1270 475 1270 475 397 476 370 475 1270 475 397 476 397 475 397 448 397 475 397 476 397 475 370 475 398 474 398 475 398 447 398 474 398 474 398 474 371 474 398 474 399 473 371 474 422 451 1272 473 423 449 422 423 1323 422 423 450 1296 449 423 449 423 449 395 450 423 449 423 449 423 422 423 449 423 449 1296 450 1296 449 423 449 396 449 423 449 423 449 423 422 423 449 424 448 424 421 1324 421 1324 421 1324 421 1324 421 424 449 1297 448 424 448 1297 448 1297 448 1297 448 1297 448 1297 448 424 448 397 448 424 448 424 448 424 421 424 449 424 448 424 448 397 448 424 448 424 448 425 420 425 448 1298 447 1298 447 425 447 425 420 425 447 425 447 425 448 397 448 425 447 425 448 425 420 425 448 1298 447 1298 447 425 448 425 447 398 447 425 448 425 447 425 420 425 447 425 448 425 420 425 447 425 447 425 447 398 447 426 446 426 446 426 419 426 446 1299 446 426 446 426 447 398 447 426 446 426 446 1299 446 1299 446 426 419 426 446 426 446 426 447 399 446 427 446 426 446 1300 445 400 445 427 445 427 446 427 418 427 446 427 445 428 444 400 445 453 419 453 419 453 392 453 419 453 420 1326 419 1326 419 453 419 +# SWING_WIDE +name: SWING +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 484 362 510 363 482 390 454 391 457 416 456 25430 3517 1720 457 1287 458 416 484 387 486 387 458 1286 458 387 485 387 457 414 431 414 458 1286 457 415 459 1286 457 1288 457 415 458 1288 456 1290 455 1290 480 1267 478 1292 453 392 453 420 453 1293 451 421 452 420 425 420 452 420 452 420 452 392 453 420 453 420 452 420 425 420 453 1293 452 421 452 1293 452 421 452 393 452 420 452 1293 452 1294 451 421 452 393 452 420 452 420 452 420 425 420 453 420 452 420 452 393 452 420 452 420 452 420 425 420 452 420 453 420 452 393 452 1293 451 1294 452 1294 451 421 451 1294 451 421 452 1294 451 1294 451 34985 3537 1726 452 1294 451 393 452 420 453 420 452 1293 452 421 424 420 453 420 452 420 452 1293 452 393 453 1293 452 1294 451 421 452 1293 452 1294 451 1294 451 1294 451 1294 451 421 452 421 424 1321 424 421 452 421 452 420 425 421 452 420 452 421 451 393 452 421 452 420 452 421 424 421 452 1294 451 421 452 421 451 394 451 421 451 1294 451 421 452 421 424 421 452 421 451 421 424 421 451 421 451 421 451 394 451 421 451 421 451 421 424 1321 424 1322 423 422 451 421 452 421 451 394 451 421 451 1294 451 1295 450 422 451 1294 451 1295 450 422 423 34985 3536 1726 452 1293 452 420 452 420 453 420 425 1320 425 421 452 420 452 420 452 393 452 1293 451 421 452 1293 451 1294 451 421 452 1293 452 1294 451 1294 451 1294 451 1294 451 421 424 421 452 1293 452 421 452 420 452 393 452 420 452 420 452 420 425 420 452 420 453 420 425 421 452 420 452 420 452 393 452 421 452 420 452 420 425 421 452 420 452 1294 451 421 452 394 451 1294 450 421 452 421 452 421 424 421 451 421 452 421 424 1322 423 421 452 421 451 1294 451 421 452 394 451 421 452 421 451 421 424 421 452 421 451 421 451 394 451 421 451 421 451 421 424 421 452 421 451 1294 451 1295 450 422 423 422 452 1294 450 1295 451 1295 450 1295 450 422 450 422 451 394 451 421 452 421 452 421 424 422 451 421 451 421 451 394 451 421 451 421 452 421 424 1322 423 1323 422 422 451 422 451 421 451 394 451 421 451 422 450 394 451 421 451 422 450 422 423 1322 422 1323 422 423 450 1295 450 423 450 422 450 395 450 422 450 422 450 422 423 422 451 422 450 422 450 395 450 422 451 422 450 395 450 422 450 422 451 1295 449 423 422 422 451 422 450 422 450 395 450 1295 449 1295 450 423 450 422 451 422 423 422 451 422 450 422 450 395 450 1295 449 423 450 422 451 395 450 422 450 422 450 422 423 422 451 422 450 422 451 1295 449 1296 449 396 449 423 450 1296 448 423 450 423 422 +# +name: TEMP+ +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 511 362 483 363 509 362 483 391 454 391 457 25457 3518 1719 457 1287 458 389 456 415 457 415 457 1287 485 389 456 388 457 415 457 415 457 1286 459 387 458 1286 459 1287 458 415 483 1262 483 1263 482 1264 481 1265 480 1291 454 419 453 420 425 1320 425 420 453 420 452 420 453 392 453 420 452 420 452 393 452 420 453 420 452 420 425 1320 425 420 453 1293 452 420 453 420 452 393 452 1293 452 1293 452 420 453 420 452 420 425 420 453 420 453 420 452 393 452 420 452 420 452 420 425 420 453 420 452 420 425 420 452 420 452 420 452 1293 452 1293 452 1293 452 393 452 1293 452 420 453 1293 452 1293 452 34983 3540 1724 452 1293 452 420 452 420 425 420 452 1293 452 420 453 420 453 392 453 420 452 1293 452 420 452 1293 452 1293 452 393 452 1293 452 1293 452 1293 452 1293 452 1294 451 420 452 421 452 1293 452 421 424 420 452 420 452 420 452 393 452 420 452 420 452 420 425 421 452 420 453 420 452 1293 452 394 451 421 451 421 451 393 452 1294 451 421 451 421 451 421 424 421 451 421 451 421 452 393 452 421 452 421 451 421 424 421 452 421 451 421 451 1294 451 394 451 421 452 421 451 394 451 421 451 1294 451 421 452 421 424 1322 423 1322 423 422 451 34985 3539 1724 452 1293 452 420 452 420 453 393 452 1293 452 421 452 420 452 420 425 420 452 1293 452 421 452 1293 452 1294 451 421 424 1321 424 1322 424 1321 424 1321 424 1321 424 421 451 421 452 1294 451 421 452 393 452 421 451 421 452 420 425 421 452 420 452 421 451 393 452 420 452 421 452 421 424 421 451 421 452 421 451 393 452 421 451 421 451 1294 451 394 452 421 451 1294 451 421 452 421 424 421 452 421 451 421 451 1294 451 1295 451 394 451 422 451 1294 450 422 451 421 424 421 451 421 451 421 452 394 451 422 451 422 450 394 451 421 451 421 451 421 424 422 451 422 450 1295 450 1295 450 422 450 395 450 422 450 422 450 422 423 422 450 422 450 422 451 394 451 422 450 422 450 395 450 422 450 422 450 422 423 422 451 422 450 422 450 395 450 1295 449 1296 449 423 449 423 450 423 422 423 450 422 450 423 449 396 449 423 450 423 449 423 422 1324 420 1324 422 424 449 1297 448 424 449 423 422 423 450 424 448 424 448 396 449 423 449 423 449 424 421 424 449 424 449 423 449 396 449 423 449 424 449 1297 447 425 420 425 448 424 449 424 421 425 448 1297 447 1298 447 425 447 425 448 397 448 425 447 424 448 425 420 426 447 1322 423 450 423 449 423 422 423 450 422 450 423 450 395 450 422 450 422 1323 421 451 395 450 423 1323 421 1324 421 451 421 451 422 423 422 +# +name: TEMP- +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 538 335 510 362 510 362 510 338 507 338 534 25377 3593 1642 510 1235 510 363 537 335 509 363 509 1209 537 335 510 361 511 362 510 362 483 1262 483 363 508 1236 508 1237 507 366 478 1268 477 1269 476 1269 476 1270 475 1270 475 397 475 370 475 1270 475 397 475 397 475 397 448 397 476 397 475 397 476 369 476 397 475 397 475 370 475 1270 475 397 475 1270 475 397 475 397 448 397 475 1271 474 1270 475 397 475 397 475 370 475 398 474 398 474 397 448 398 475 398 474 397 475 370 475 398 474 398 475 370 475 398 475 398 474 398 447 1299 447 1299 446 1299 446 398 474 1271 474 399 474 1271 474 1272 473 34986 3534 1703 476 1269 476 396 476 369 476 396 476 1269 476 396 476 397 448 397 476 396 477 1269 476 397 448 1297 448 1297 448 396 476 1270 475 1270 475 1270 475 1270 475 1270 475 397 475 397 475 1270 475 370 475 397 475 397 475 397 448 397 475 397 475 397 475 370 475 397 475 397 475 397 448 1298 447 398 475 398 475 398 447 398 474 1271 474 398 474 398 474 395 450 422 450 422 450 422 423 422 450 422 450 423 450 395 450 422 451 422 450 423 422 1323 422 423 449 423 449 422 450 395 450 423 449 1296 450 422 450 395 450 1296 449 1296 449 423 449 34986 3535 1703 476 1269 476 396 476 396 449 397 475 1270 475 396 477 396 476 369 476 397 476 1269 476 397 475 1270 476 1270 475 397 448 1298 447 1298 447 1297 448 1298 447 1298 448 398 474 398 474 1295 450 398 447 398 474 422 450 422 451 395 450 422 450 422 450 422 423 422 451 422 450 422 450 395 450 422 450 422 451 422 423 422 450 422 451 422 423 1323 423 422 450 422 451 1295 450 422 450 395 450 422 450 422 451 422 423 423 450 1295 450 422 450 422 451 1295 450 395 450 422 450 423 449 422 423 423 449 423 450 422 423 423 449 423 450 422 450 396 449 423 449 423 449 423 422 1324 421 1323 422 423 450 423 449 423 449 396 449 423 449 423 449 423 422 423 450 423 450 423 449 396 449 423 449 423 450 396 449 424 448 424 449 423 422 424 449 424 448 1297 448 1297 448 424 448 397 448 424 449 424 448 424 421 424 448 424 449 424 449 396 449 424 448 1297 448 1297 448 424 448 1297 448 397 448 424 448 424 449 424 421 425 448 424 448 424 448 398 447 424 448 424 448 425 420 425 447 425 447 425 448 397 448 1298 447 425 447 425 447 398 448 425 447 425 447 1298 447 1298 447 425 420 425 447 425 448 425 447 398 447 425 447 426 446 1299 446 426 419 426 447 425 447 426 446 399 446 426 446 426 447 426 419 1326 419 1327 418 1327 419 426 446 1300 445 426 446 426 419 427 446 +# SWING_WIDE +name: SWING +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 454 413 453 412 454 412 454 412 454 413 453 25102 3490 1707 483 1249 484 383 483 383 482 383 509 1222 483 383 482 384 481 385 480 385 480 1250 482 386 479 1252 479 1254 477 414 451 1281 450 1282 449 1283 449 1283 449 1284 448 417 448 417 449 1284 448 417 449 417 449 418 448 417 449 417 449 417 449 417 449 417 449 417 449 417 449 1284 448 418 448 1284 448 418 448 418 448 418 448 1284 448 1284 448 418 448 418 448 417 448 418 448 1284 448 417 449 417 448 418 448 417 449 417 449 417 449 418 448 417 449 418 448 418 448 418 448 1284 448 1284 448 1284 449 417 449 418 448 1284 448 1284 448 1284 448 35505 3485 1712 449 1283 449 417 449 417 449 417 449 1284 448 417 449 417 449 417 449 417 449 1284 448 417 449 1284 448 1284 448 417 449 1284 448 1284 448 1284 448 1283 449 1284 448 417 449 417 449 1284 448 417 449 417 449 417 449 417 449 417 449 417 449 417 449 417 449 418 448 418 448 417 449 1284 448 417 449 417 449 417 449 417 449 1284 448 417 448 418 448 418 448 417 448 418 448 418 448 418 448 418 448 418 448 418 448 418 448 418 448 418 448 1284 448 418 448 418 448 418 448 418 448 418 448 1284 448 418 448 418 448 1285 447 1285 447 418 448 35504 3486 1712 449 1283 449 417 449 417 448 417 449 1284 448 417 449 417 449 417 449 417 448 1284 448 417 449 1284 448 1284 448 417 449 1283 449 1284 448 1284 448 1284 448 1284 449 417 449 417 449 1284 448 417 449 417 449 418 448 417 449 417 449 417 449 418 448 417 449 417 449 418 448 418 448 418 448 417 448 418 448 418 448 418 448 418 448 418 448 418 448 418 448 418 448 1284 448 1284 448 1284 448 418 448 418 448 418 448 1284 448 418 448 418 448 1284 448 1284 448 418 448 418 448 418 448 418 448 418 448 418 448 418 448 418 448 418 448 418 448 418 448 418 448 418 448 418 448 1285 447 1285 447 418 448 1285 447 1285 447 1285 447 1285 447 1285 447 419 447 418 448 419 447 418 448 418 448 418 448 419 447 419 447 418 448 418 448 419 447 419 447 419 447 1285 447 1285 447 419 447 419 447 419 447 419 447 419 447 419 447 419 447 419 447 419 447 419 447 1286 446 1285 447 419 447 419 447 419 447 419 447 419 447 419 447 419 447 419 447 419 447 419 447 419 447 419 447 419 447 419 447 419 447 419 447 419 447 1286 446 420 446 420 446 420 446 420 446 419 447 1286 446 1286 446 419 447 420 446 420 446 420 446 420 446 420 446 420 446 1286 446 420 446 420 446 420 446 420 446 420 446 420 446 420 446 420 446 420 446 1286 446 420 446 420 446 420 446 1287 445 1287 445 1287 445 diff --git a/assets/resources/infrared/assets/audio.ir b/assets/resources/infrared/assets/audio.ir index ae29a43f0..4a5fe8f85 100644 --- a/assets/resources/infrared/assets/audio.ir +++ b/assets/resources/infrared/assets/audio.ir @@ -1,7 +1,7 @@ Filetype: IR library file Version: 1 -# Last Updated 30th Jan, 2023 -# Last Checked 31th Jan, 2023 +# Last Updated 07th Mar, 2023 +# Last Checked 07th Mar, 2023 # name: POWER type: parsed @@ -1913,7 +1913,7 @@ protocol: SIRC address: 0F 00 00 00 command: 15 00 00 00 # -name: Power +name: POWER type: parsed protocol: Samsung32 address: 10 00 00 00 @@ -2032,3 +2032,69 @@ type: parsed protocol: NEC address: 80 00 00 00 command: 04 00 00 00 +# +name: MUTE +type: parsed +protocol: NEC42 +address: 6E 00 00 00 +command: 4C 00 00 00 +# +name: VOL+ +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 11 00 00 00 +# +name: VOL- +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 10 00 00 00 +# +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4555 4463 532 473 532 473 531 474 530 474 530 1478 531 1478 531 475 529 476 528 1482 551 1480 529 1480 528 1481 528 477 527 478 527 478 526 478 527 4498 526 477 527 477 527 478 526 478 526 478 526 478 526 478 527 478 526 1483 527 1483 526 1483 526 478 526 1483 527 1483 527 1483 526 1483 526 478 526 478 527 478 527 1484 526 55527 4527 4492 526 478 526 478 526 478 526 479 526 1483 527 1483 526 478 526 478 526 1483 527 1483 526 1483 526 1483 526 478 527 478 526 479 526 478 526 4497 526 478 526 478 526 478 526 478 526 478 526 478 526 478 526 478 526 1484 525 1483 526 1484 525 478 526 1484 525 1484 525 1484 525 1484 525 478 526 479 526 479 525 1484 525 +# +name: VOL+ +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4552 4463 531 474 530 474 530 474 530 474 530 1478 531 1478 531 475 529 476 553 1455 554 1478 530 1479 529 1481 527 477 527 478 526 478 526 478 526 4497 526 478 526 478 526 478 526 477 527 1483 526 1482 527 1482 527 478 526 1483 526 1483 526 1483 526 478 526 478 526 478 526 478 526 1483 526 478 526 478 526 478 526 1483 526 +# +name: VOL- +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 325 50440 173 137541 4551 4465 530 475 529 475 529 474 530 474 530 1479 530 1479 529 476 528 477 527 1480 554 1457 551 1480 528 1481 527 477 527 478 526 478 526 478 526 4497 525 478 526 478 526 478 526 478 526 479 525 479 525 479 525 1484 525 1483 526 1484 525 1483 526 479 525 1483 526 1483 526 1483 526 479 525 479 525 479 525 479 525 1484 525 +# +name: POWER +type: parsed +protocol: NECext +address: 30 FC 00 00 +command: 10 EF 00 00 +# +name: MUTE +type: parsed +protocol: NECext +address: 30 FC 00 00 +command: 0C F3 00 00 +# +name: VOL+ +type: parsed +protocol: NECext +address: 30 FC 00 00 +command: 0D F2 00 00 +# +name: VOL- +type: parsed +protocol: NECext +address: 30 FC 00 00 +command: 17 E8 00 00 +# +name: POWER +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 04 00 00 00 diff --git a/assets/resources/infrared/assets/fans.ir b/assets/resources/infrared/assets/fans.ir index 942c2df13..d242bfdd6 100644 --- a/assets/resources/infrared/assets/fans.ir +++ b/assets/resources/infrared/assets/fans.ir @@ -1,7 +1,7 @@ Filetype: IR library file Version: 1 -# Last Updated 31th Jan, 2023 -# Last Checked 31th Jan, 2023 +# Last Updated 15th Feb, 2023 +# Last Checked 07th Mar, 2023 # name: POWER type: raw @@ -1364,4 +1364,58 @@ name: ROTATE type: raw frequency: 38000 duty_cycle: 0.330000 -data: 2256 698 786 695 786 689 787 1348 787 1342 787 673 788 668 787 663 787 722 786 696 784 1353 786 1375 759 706 759 702 783 672 783 1332 782 102265 2310 645 838 668 812 664 811 1323 810 1319 810 651 809 647 808 642 808 701 807 673 807 1332 807 1327 807 658 808 653 807 648 807 1307 807 \ No newline at end of file +data: 2256 698 786 695 786 689 787 1348 787 1342 787 673 788 668 787 663 787 722 786 696 784 1353 786 1375 759 706 759 702 783 672 783 1332 782 102265 2310 645 838 668 812 664 811 1323 810 1319 810 651 809 647 808 642 808 701 807 673 807 1332 807 1327 807 658 808 653 807 648 807 1307 807 +# +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 1356 337 1423 337 516 1157 1298 409 1296 416 448 1274 451 1276 452 1274 450 1274 449 1277 449 1275 1301 7091 1301 407 1300 405 450 1267 1302 407 1301 414 450 1274 451 1275 451 1275 451 1276 450 1275 451 1274 1302 7074 1303 406 1302 406 450 1268 1301 409 1299 416 449 1277 472 1254 473 1253 473 1254 472 1254 473 1252 1323 7069 1325 384 1300 408 470 1247 1324 387 1299 417 471 1253 472 1254 472 1254 472 1254 472 1254 472 1252 1324 7052 1324 384 1300 408 472 1246 1324 387 1299 417 472 1253 472 1254 472 1254 472 1254 473 1254 472 1252 1324 7064 1325 385 1323 385 471 1246 1324 387 1323 394 471 1254 471 1254 472 1254 472 1254 472 1254 472 1252 1324 7053 1324 385 1323 385 471 1247 1323 387 1322 394 471 1254 472 1254 472 1255 471 1255 471 1255 472 1253 1323 7070 1323 385 1323 385 471 1247 1323 388 1322 395 470 1254 472 1255 471 1255 471 1255 471 1255 471 1253 1323 7054 1323 386 1322 386 470 1248 1322 413 1297 396 470 1255 470 1256 470 1256 470 1256 470 1256 471 1254 1322 7071 1322 411 1297 387 469 1249 1321 414 1296 396 470 1280 445 1281 446 1281 445 1282 445 1282 445 1280 1296 7076 1296 412 1296 412 444 1274 1297 413 1297 421 445 1281 445 1282 444 1282 444 1282 445 1282 444 1280 1296 7098 1295 412 1296 412 445 1275 1296 414 1296 421 445 1282 444 1282 445 1282 444 1283 444 1282 445 1280 1296 7082 1296 412 1296 412 445 1275 1295 414 1296 421 444 1282 444 1282 444 1283 444 1283 444 1282 445 1280 1296 7098 1296 413 1295 413 444 1275 1295 415 1295 422 444 1283 443 1282 444 1283 443 1282 445 1282 444 1281 1295 7082 1295 413 1295 413 444 1275 1295 415 1294 423 443 1283 443 1283 443 1283 443 1283 443 1283 443 1281 1294 7093 1295 413 1295 414 443 1276 1294 415 1295 423 443 1283 443 1283 443 1283 443 1283 444 1283 444 1280 1295 7082 1295 414 1294 414 443 1276 1294 416 1294 423 443 1283 443 1283 443 1283 443 1283 444 1283 444 1281 1294 7098 1295 414 1294 414 443 1276 1294 416 1294 424 442 1284 443 1283 443 1283 443 1284 443 1283 443 1281 1294 7083 1294 414 1294 415 442 1277 1293 417 1293 425 441 1284 442 1284 442 1284 442 1284 442 1284 443 1282 1293 7099 1294 415 1293 416 441 1278 1292 418 1292 425 441 1285 441 1285 441 1285 442 1285 441 1285 441 1283 1292 7083 1293 417 1291 441 415 1279 1292 443 1267 451 415 1286 440 1286 441 1286 440 1285 441 1286 440 1284 1291 7096 1292 441 1267 442 415 1304 1266 444 1266 451 415 1311 415 1287 439 1287 440 1311 415 1286 440 1285 1290 7085 1291 442 1241 467 414 1304 1266 445 1241 476 414 1313 414 1312 415 1311 415 1312 415 1312 414 1310 1241 7151 1242 467 1241 467 415 1305 1241 469 1241 477 412 1314 414 1312 414 1312 414 1312 414 1312 414 1310 1241 7135 1241 467 1241 468 389 1330 1241 470 1240 477 389 1337 389 1337 414 1312 414 1313 413 1313 389 1335 1241 7151 1241 469 1239 470 386 1331 1240 496 1214 503 362 1338 388 1338 388 1338 388 1338 388 1338 388 1336 1240 7130 1241 494 1214 495 361 1357 1214 497 1213 504 362 1364 362 1364 362 1364 363 1364 362 1364 362 1362 1214 7153 1239 495 1213 496 360 1358 1213 498 1212 531 334 1365 362 1365 361 1365 361 1365 362 1364 362 1363 1213 7162 1213 496 1212 522 334 1359 1212 524 1186 532 333 1367 359 1366 360 1366 361 1365 361 1365 361 1364 1211 7180 1212 523 1185 550 306 1387 1185 552 1157 586 278 1395 332 1394 332 1394 332 1393 333 1393 334 1391 1184 7190 1185 576 1131 604 250 1415 1158 660 1049 2341 251 1448 278 1422 305 1448 278 1448 278 1447 1130 7229 1157 +# +name: SPEED+ +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 1357 353 1327 382 448 1239 1333 409 1301 416 477 1246 452 1274 452 1274 453 1273 479 1246 1330 416 448 7967 1300 409 1299 410 446 1273 1299 412 1298 419 446 1280 446 1281 446 1281 446 1281 446 1279 1298 419 446 7958 1298 411 1297 410 446 1273 1298 412 1298 419 446 1280 446 1281 445 1281 446 1281 445 1279 1298 419 446 7967 1298 410 1298 411 445 1274 1297 413 1297 420 445 1281 445 1281 445 1281 445 1282 445 1279 1298 420 445 7957 1297 411 1297 411 445 1274 1297 413 1297 420 445 1281 445 1281 445 1282 444 1282 444 1280 1296 420 445 7962 1297 411 1297 411 445 1274 1297 413 1297 420 445 1282 444 1282 444 1282 444 1282 444 1280 1297 421 444 7957 1296 412 1297 411 444 1275 1296 414 1296 421 444 1282 444 1283 443 1283 443 1282 444 1281 1296 421 444 7968 1296 412 1296 412 444 1275 1296 414 1296 421 444 1283 443 1283 443 1283 443 1283 443 1281 1296 422 443 7958 1295 413 1295 413 443 1276 1295 415 1295 422 443 1283 443 1284 442 1284 442 1284 442 1282 1294 422 443 7970 1293 414 1294 414 442 1277 1294 416 1294 423 442 1284 442 1309 417 1309 417 1309 417 1307 1269 424 441 7978 1270 416 1292 414 442 1302 1269 440 1270 424 441 1309 417 1309 417 1310 417 1309 417 1307 1270 448 417 7994 1269 439 1269 439 416 1302 1269 441 1269 448 416 1310 416 1310 416 1310 416 1310 416 1308 1269 448 416 7984 1269 439 1268 440 416 1303 1268 442 1267 449 415 1310 416 1310 416 1310 416 1310 416 1309 1268 449 416 7994 1268 440 1268 440 415 1303 1268 442 1268 449 415 1311 415 1311 415 1311 415 1311 415 1309 1267 450 415 7985 1267 441 1267 441 414 1304 1266 444 1267 450 414 1311 415 1312 413 1312 414 1312 414 1310 1265 452 414 7991 1266 442 1242 466 413 1306 1241 468 1242 475 414 1337 388 1338 388 1338 388 1338 388 1336 1217 501 386 8013 1215 492 1216 492 387 1331 1216 494 1216 501 363 1363 362 1363 363 1363 363 1363 387 1338 1215 502 362 8047 1215 493 1215 492 363 1356 1215 495 1215 502 362 1364 361 1364 362 1364 362 1364 362 1363 1214 503 361 8063 1189 494 1214 494 361 1382 1189 496 1214 503 361 1390 335 1391 335 1391 335 1391 335 1389 1188 529 335 8074 1188 520 1188 520 334 1385 1187 522 1187 529 334 1392 333 1417 308 1418 308 1419 307 1417 1161 556 307 8092 1160 547 1161 547 307 1412 1160 550 1160 557 306 1419 306 1446 279 1421 305 1446 279 1445 1133 584 279 8125 1133 575 1133 576 277 1466 1106 603 1107 611 251 1474 251 1501 224 1502 224 1528 186 1511 1079 638 224 8228 1025 735 972 2640 786 +# +name: SPEED+ +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 1A 00 00 00 +# +name: POWER +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 1D 00 00 00 +# +name: ROTATE +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 18 00 00 00 +# +name: TIMER +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 0D 00 00 00 +# +name: POWER +type: parsed +protocol: NECext +address: 80 DE 00 00 +command: 00 FF 00 00 +# +name: SPEED+ +type: parsed +protocol: NECext +address: 80 DE 00 00 +command: 08 F7 00 00 +# +name: SPEED- +type: parsed +protocol: NECext +address: 80 DE 00 00 +command: 10 EF 00 00 diff --git a/assets/resources/infrared/assets/projector.ir b/assets/resources/infrared/assets/projector.ir deleted file mode 100644 index e9861de21..000000000 --- a/assets/resources/infrared/assets/projector.ir +++ /dev/null @@ -1,829 +0,0 @@ -Filetype: IR library file -Version: 1 -# -# Model: Smart -name: Power -type: parsed -protocol: NEC -address: 00 00 00 00 -command: 8A 00 00 00 -# -# Model: Epson -name: Power -type: parsed -protocol: NECext -address: 83 55 00 00 -command: 90 6F 00 00 -# -# Model: Epson -name: Power -type: parsed -protocol: NECext -address: 81 03 00 00 -command: F0 0F 00 00 -# -# Model: Hitatchi -name: Power -type: parsed -protocol: NECext -address: 87 45 00 00 -command: 17 E8 00 00 -# -name: Power -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 310 27591 171 27662 241 27731 307 27575 107 27749 306 27551 130 55520 243 27614 217 55584 129 27743 119 27756 115 27747 163 27712 308 27502 243 27650 217 27732 175 27693 167 27698 166 27689 171 27622 215 27712 133 27658 216 27716 129 27732 162 27698 305 27571 131 27753 310 27570 170 27707 162 27707 175 10960 9194 4518 618 542 618 543 725 434 672 1623 671 1647 646 514 592 568 592 568 592 1702 592 568 592 567 593 1702 592 568 618 1676 618 1676 618 1676 618 543 617 543 617 543 617 1677 617 544 616 544 616 544 616 544 616 1678 616 1678 616 1678 616 544 616 1678 616 1679 615 1678 616 1678 616 40239 9196 2250 617 -# -name: Vol_up -type: parsed -protocol: NEC -address: 08 00 00 00 -command: 48 00 00 00 -# -name: Vol_dn -type: parsed -protocol: NEC -address: 08 00 00 00 -command: 49 00 00 00 -# -name: Mute -type: parsed -protocol: NEC -address: 08 00 00 00 -command: 14 00 00 00 -# -name: Power -type: parsed -protocol: NEC -address: 08 00 00 00 -command: 0B 00 00 00 -# -name: Vol_dn -type: parsed -protocol: NEC -address: 01 00 00 00 -command: 40 00 00 00 -# -name: Vol_up -type: parsed -protocol: NEC -address: 01 00 00 00 -command: 48 00 00 00 -# -name: Mute -type: parsed -protocol: NEC -address: 01 00 00 00 -command: 44 00 00 00 -# -name: Vol_dn -type: parsed -protocol: NECext -address: 00 30 00 00 -command: 83 7C 00 00 -# -name: Vol_up -type: parsed -protocol: NECext -address: 00 30 00 00 -command: 82 7D 00 00 -# -name: Power -type: parsed -protocol: NECext -address: 08 13 00 00 -command: 87 78 00 00 -# -name: Power -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 9055 4338 672 1551 669 1553 618 1603 619 481 617 482 616 481 617 507 591 1605 645 479 619 1577 645 1578 644 1578 644 479 619 480 618 1581 641 480 617 1605 617 1606 616 1606 615 483 615 1608 614 484 614 484 614 484 614 484 614 484 614 484 614 1609 614 484 614 1609 614 1609 613 1609 613 40058 9000 2068 614 95467 9022 2068 614 -# -name: Mute -type: parsed -protocol: NECext -address: 87 4E 00 00 -command: 29 D6 00 00 -# -name: Vol_up -type: parsed -protocol: NECext -address: 87 4E 00 00 -command: 08 F7 00 00 -# -name: Vol_dn -type: parsed -protocol: NECext -address: 87 4E 00 00 -command: 04 FB 00 00 -# -name: Mute -type: parsed -protocol: NECext -address: 83 55 00 00 -command: 93 6C 00 00 -# -name: Vol_dn -type: parsed -protocol: NEC -address: 02 00 00 00 -command: 15 00 00 00 -# -name: Vol_up -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 9032 4462 598 501 627 1604 627 530 598 531 677 423 706 422 706 421 707 451 677 1554 677 451 598 1633 598 1634 597 1634 598 1634 598 1634 625 1606 681 1550 626 502 598 530 599 529 600 1632 600 528 600 528 601 528 601 528 601 1631 600 1631 625 1607 625 504 625 1607 624 1608 624 1608 623 -# -name: Mute -type: parsed -protocol: NEC -address: 02 00 00 00 -command: 02 00 00 00 -# -name: Power -type: parsed -protocol: NEC -address: 02 00 00 00 -command: 1D 00 00 00 -# -# ON -name: Power -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 9096 4436 620 505 647 478 648 501 623 1599 647 1624 623 502 623 503 621 504 619 1628 618 507 617 507 617 1630 617 508 616 1630 617 1630 617 1631 616 508 616 508 617 508 616 1631 616 508 617 508 617 508 616 508 616 1630 616 1630 616 1631 616 508 616 1630 617 1630 617 1630 617 1631 617 509 616 508 616 509 616 509 616 509 616 509 615 509 616 508 617 1631 616 1631 615 1631 616 1631 616 1631 616 1631 616 1631 615 1631 616 14435 9093 2186 615 96359 9095 2184 617 -# -name: Vol_up -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 9091 4465 594 530 595 530 594 530 594 1651 595 1652 595 529 621 504 620 504 619 1628 618 507 617 508 616 1631 616 509 615 1631 616 1631 616 1632 615 509 616 509 616 509 615 1631 616 509 616 508 616 1631 616 509 616 1631 615 1631 616 1631 617 508 616 1631 616 1631 616 508 616 1631 617 508 617 509 616 509 616 509 616 509 616 509 616 509 616 509 616 1631 616 1631 616 1631 616 1631 616 1631 615 1631 615 1631 615 1631 616 14435 9090 2190 615 -# -name: Vol_dn -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 9092 4439 620 506 619 506 618 530 593 1627 620 1630 643 504 620 505 618 506 617 1630 617 508 616 508 616 1632 616 508 617 1631 616 1631 616 1631 616 1631 616 509 616 508 616 1631 616 509 616 509 615 1632 616 509 616 508 616 1631 616 1631 616 508 616 1631 615 1631 616 509 615 1632 615 509 616 509 616 509 616 509 616 509 616 510 615 509 616 509 616 1631 616 1631 615 1631 616 1631 615 1631 615 1631 615 1631 615 1631 615 14434 9088 2191 615 96339 9115 2189 616 96343 9117 2189 616 96343 9114 2189 616 -# AV-Mute -name: Mute -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 9092 4439 620 506 618 506 618 530 594 1627 619 1629 643 505 619 505 619 506 617 1629 617 508 616 508 616 1631 616 508 616 1630 616 1630 616 1630 617 1630 616 1630 616 1631 616 508 616 508 616 508 616 1631 616 508 617 508 616 508 616 508 616 1630 616 1631 615 1631 616 508 616 1631 616 508 617 508 616 509 615 509 616 508 616 509 615 509 616 508 616 1631 615 1631 615 1631 616 1631 615 1631 615 1631 615 1631 615 1631 616 14433 9088 2191 615 -# -name: Power -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 9014 4332 661 1570 661 471 660 473 658 474 657 476 655 498 633 498 634 502 633 499 633 1599 632 1599 632 1599 632 1599 632 1599 632 1600 631 1603 632 500 632 501 631 501 631 501 631 501 631 501 631 1601 631 504 631 1601 631 1601 631 1601 631 1601 631 1601 630 1601 630 501 631 1601 631 38177 8983 2149 630 -# -name: Vol_up -type: parsed -protocol: NEC -address: 01 00 00 00 -command: 11 00 00 00 -# -name: Vol_dn -type: parsed -protocol: NEC -address: 01 00 00 00 -command: 4C 00 00 00 -# -name: Mute -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 9042 4306 690 1541 665 468 664 468 664 469 663 470 662 471 660 495 636 499 636 497 634 1597 634 1598 633 1598 633 1599 633 1599 632 1599 633 1603 632 1599 633 499 633 499 633 500 632 499 633 500 632 1600 632 503 633 500 632 1600 632 1600 632 1600 633 1600 632 1600 632 500 632 1600 632 37912 8986 2145 633 -# ON -name: Power -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 3522 1701 472 426 444 1269 472 426 444 426 443 427 443 427 443 426 444 427 443 426 444 427 442 428 441 429 440 431 438 1304 437 433 437 433 438 433 437 433 437 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 434 436 1305 436 434 436 434 436 1305 436 435 435 435 435 435 435 435 435 435 435 435 435 435 435 459 411 459 411 459 411 1330 411 1330 411 1330 411 1330 411 1330 411 460 410 459 411 459 411 1330 411 1330 411 460 410 1330 411 1330 411 1331 410 1330 411 74392 3516 1736 436 433 437 1304 437 433 437 433 437 433 437 433 437 433 437 434 436 433 437 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 434 436 434 436 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 435 435 1305 436 435 435 435 435 1306 435 435 435 435 435 435 435 436 434 436 434 436 434 435 435 436 434 436 434 436 434 1330 411 1331 410 1330 411 1330 411 1330 411 459 411 460 410 460 410 1331 410 1331 410 460 410 1331 410 1331 410 1331 410 1331 410 74392 3515 1736 437 433 437 1304 437 433 437 433 437 434 436 433 437 434 436 433 437 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 434 436 434 436 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 435 436 434 436 1306 435 435 435 435 435 1306 435 435 435 435 435 435 435 435 435 435 435 436 434 436 434 435 435 436 434 435 435 1306 435 1330 411 1307 434 1331 410 1308 433 436 434 436 434 460 410 1331 410 1331 410 460 410 1331 410 1331 410 1331 410 1331 410 74392 3515 1736 437 433 437 1304 437 434 436 433 437 434 436 433 437 434 436 434 436 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 434 436 435 435 434 436 434 436 434 436 434 436 434 436 1306 435 435 435 435 435 435 435 1306 435 435 435 436 434 1306 435 435 435 436 434 436 434 435 435 436 434 436 434 460 410 460 410 460 410 460 410 1331 410 1331 410 1331 410 1331 410 1331 410 460 410 460 410 460 410 1331 410 1331 410 460 410 1331 410 1331 410 1331 410 1331 410 74392 3515 1736 437 433 437 1304 437 433 437 434 436 434 436 433 437 434 436 434 436 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 434 436 434 436 434 436 434 436 435 435 435 435 434 436 1306 435 434 436 435 435 435 435 1306 435 436 434 435 435 1306 435 435 435 436 434 436 434 436 434 436 434 460 410 437 433 459 411 460 410 460 410 1331 410 1331 410 1331 410 1331 410 1331 410 460 410 460 410 460 410 1331 410 1331 410 460 410 1331 410 1331 410 1331 410 1331 410 74393 3514 1736 437 434 436 1304 437 433 437 434 436 433 437 434 436 433 437 434 436 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 434 436 434 436 435 435 434 436 434 436 435 435 434 436 1305 436 435 435 435 435 435 435 1306 435 435 435 435 435 1306 435 435 435 436 434 435 435 459 411 436 434 435 435 459 411 459 411 459 411 459 411 1330 411 1306 435 1330 411 1330 411 1331 410 460 410 460 410 460 410 1331 410 1331 410 460 410 1331 410 1331 410 1331 410 1331 410 -# ON -name: Power -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 529 7218 126 6585 219 703 180 5362 427 18618 177 -# -name: Vol_up -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 9069 4362 622 486 621 487 621 491 622 1608 623 1603 622 487 621 487 621 491 622 1604 621 487 622 491 622 1604 621 491 622 1608 622 1609 621 1604 622 486 622 487 621 491 621 1605 621 487 621 491 622 1604 622 491 621 1609 621 1609 621 1604 622 491 621 1609 622 1604 621 491 621 1604 622 487 621 487 622 486 622 487 621 488 621 487 621 488 620 491 621 1609 622 1609 620 1609 621 1609 621 1609 621 1609 621 1609 621 1618 621 14330 9047 2137 620 -# -name: Vol_dn -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 9047 4362 621 486 622 463 645 490 622 1609 622 1604 622 487 620 487 621 491 622 1604 622 484 625 490 621 1605 649 463 621 1609 620 1611 621 1608 622 1605 621 486 622 491 622 1604 621 487 621 492 620 1604 621 488 621 492 620 1609 622 1604 621 492 622 1609 620 1605 621 491 622 1603 622 488 621 488 620 488 620 488 621 488 620 487 622 485 621 492 596 1635 621 1609 622 1585 643 1611 620 1608 621 1610 619 1611 620 1619 619 14332 9074 2109 647 -# -name: Mute -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 9073 4336 648 461 647 484 624 489 623 1607 623 1603 622 486 622 486 622 491 622 1604 621 487 621 491 622 1604 622 491 621 1609 621 1609 621 1609 621 1608 622 1609 621 1604 621 486 622 486 622 491 622 1604 622 486 622 487 621 487 621 491 622 1608 622 1609 621 1604 622 491 621 1604 621 487 621 486 622 487 621 487 621 487 621 487 621 487 621 491 622 1608 622 1608 622 1609 621 1608 622 1608 622 1608 622 1609 621 1617 622 14330 9047 2137 620 -# ON -name: Power -type: parsed -protocol: NECext -address: 83 F4 00 00 -command: 4F B0 00 00 -# -name: Power -type: parsed -protocol: NECext -address: 80 19 00 00 -command: 10 EF 00 00 -# -name: Vol_up -type: parsed -protocol: NECext -address: 80 19 00 00 -command: 1C E3 00 00 -# -name: Vol_dn -type: parsed -protocol: NECext -address: 80 19 00 00 -command: 46 B9 00 00 -# -name: Power -type: parsed -protocol: NEC -address: 80 00 00 00 -command: 51 00 00 00 -# -name: Power -type: parsed -protocol: NECext -address: 40 40 00 00 -command: 0A F5 00 00 -# -name: Power -type: parsed -protocol: NECext -address: 00 30 00 00 -command: 4E B1 00 00 -# -name: Vol_up -type: parsed -protocol: NECext -address: 00 30 00 00 -command: 0E F1 00 00 -# -name: Vol_dn -type: parsed -protocol: NECext -address: 00 30 00 00 -command: 0D F2 00 00 -# -name: Power -type: parsed -protocol: NECext -address: 00 30 00 00 -command: 4F B0 00 00 -# -name: Mute -type: parsed -protocol: NECext -address: 00 30 00 00 -command: 14 EB 00 00 -# -name: Power -type: parsed -protocol: NECext -address: 08 16 00 00 -command: 87 78 00 00 -# -name: Mute -type: parsed -protocol: NECext -address: 08 16 00 00 -command: C8 37 00 00 -# -name: Power -type: parsed -protocol: NEC -address: 01 00 00 00 -command: 01 00 00 00 -# -name: Mute -type: parsed -protocol: NEC -address: 01 00 00 00 -command: 02 00 00 00 -# -name: Vol_up -type: parsed -protocol: NEC -address: 01 00 00 00 -command: 28 00 00 00 -# -name: Vol_dn -type: parsed -protocol: NEC -address: 01 00 00 00 -command: 29 00 00 00 -# -name: Power -type: parsed -protocol: NECext -address: 84 F4 00 00 -command: 0B F4 00 00 -# -name: Power -type: parsed -protocol: NECext -address: 33 00 00 00 -command: 00 FF 00 00 -# -name: Vol_dn -type: parsed -protocol: NECext -address: 33 00 00 00 -command: 1E E1 00 00 -# -name: Vol_up -type: parsed -protocol: NECext -address: 33 00 00 00 -command: 1D E2 00 00 -# -name: Mute -type: parsed -protocol: NECext -address: 33 00 00 00 -command: 0B F4 00 00 -# -name: Power -type: parsed -protocol: NECext -address: 83 55 00 00 -command: 90 6F 00 00 -# -name: Vol_dn -type: parsed -protocol: NECext -address: 83 55 00 00 -command: 99 66 00 00 -# -name: Vol_up -type: parsed -protocol: NECext -address: 83 55 00 00 -command: 98 67 00 00 -# -name: Power -type: parsed -protocol: NECext -address: 00 DF 00 00 -command: 1C E3 00 00 -# -name: Vol_dn -type: parsed -protocol: NECext -address: 00 DF 00 00 -command: 4F B0 00 00 -# -name: Vol_up -type: parsed -protocol: NECext -address: 00 DF 00 00 -command: 4B B4 00 00 -# -name: Power -type: parsed -protocol: NEC -address: 32 00 00 00 -command: 02 00 00 00 -# -name: Power -type: parsed -protocol: NEC -address: 32 00 00 00 -command: 2E 00 00 00 -# -name: Mute -type: parsed -protocol: NEC -address: 32 00 00 00 -command: 52 00 00 00 -# -name: Power -type: parsed -protocol: NEC -address: 20 00 00 00 -command: 41 00 00 00 -# -name: Vol_up -type: parsed -protocol: NEC -address: 20 00 00 00 -command: 51 00 00 00 -# -name: Vol_dn -type: parsed -protocol: NEC -address: 20 00 00 00 -command: 56 00 00 00 -# -name: Mute -type: parsed -protocol: NEC -address: 20 00 00 00 -command: 5A 00 00 00 -# -name: Power -type: parsed -protocol: SIRC15 -address: 54 00 00 00 -command: 15 00 00 00 -# -name: Vol_up -type: parsed -protocol: NECext -address: 83 F4 00 00 -command: 82 7D 00 00 -# -name: Vol_dn -type: parsed -protocol: NECext -address: 83 F4 00 00 -command: 83 7C 00 00 -# -name: Mute -type: parsed -protocol: NECext -address: 83 F4 00 00 -command: 14 EB 00 00 -# -name: Power -type: parsed -protocol: NEC -address: 31 00 00 00 -command: 91 00 00 00 -# -name: Power -type: parsed -protocol: NEC -address: 31 00 00 00 -command: 90 00 00 00 -# -name: Vol_up -type: parsed -protocol: NEC -address: 31 00 00 00 -command: D0 00 00 00 -# -name: Mute -type: parsed -protocol: NEC -address: 31 00 00 00 -command: 89 00 00 00 -# -name: Power -type: parsed -protocol: NECext -address: 86 00 00 00 -command: 00 00 00 00 -# -name: Vol_up -type: parsed -protocol: NECext -address: 86 00 00 00 -command: 30 00 00 00 -# -name: Vol_dn -type: parsed -protocol: NECext -address: 86 00 00 00 -command: 31 00 00 00 -# -name: Mute -type: parsed -protocol: NECext -address: 86 00 00 00 -command: 32 00 00 00 -# -name: Power -type: parsed -protocol: NECext -address: 30 00 00 00 -command: 00 00 00 00 -# -name: Power -type: parsed -protocol: NECext -address: 87 4E 00 00 -command: 0D 00 00 00 -# -name: Power -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 9032 4479 597 560 572 558 564 566 566 1666 589 1671 594 562 570 560 562 568 564 1669 596 560 562 568 564 1669 596 560 562 1671 594 1666 588 1671 594 562 570 560 562 568 564 1669 596 560 562 568 564 566 566 563 569 1664 591 1669 596 1664 590 565 567 1667 598 1661 593 1666 588 1671 594 562 570 560 562 568 564 565 567 563 569 560 562 568 564 565 567 1666 588 1671 594 1665 589 1670 595 1665 590 1669 596 1664 590 1668 597 13983 9029 2222 599 96237 9030 2221 589 96244 9034 2217 594 96244 9033 2218 592 96249 9038 2213 597 96239 9037 2214 596 96238 9028 2223 598 96221 9032 2215 595 -# -name: Vol_up -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 9034 4482 593 563 569 561 571 559 563 1698 566 1694 570 559 563 568 564 566 566 1695 569 560 572 559 563 1671 593 563 569 1692 562 1671 593 1693 571 558 564 567 565 565 567 1693 571 532 590 567 565 1695 569 560 562 1698 566 1694 570 1663 591 539 593 1693 571 1688 566 564 568 1691 563 567 565 565 567 563 569 561 571 559 563 567 565 565 567 563 569 1690 564 1695 569 1691 563 1696 568 1691 563 1697 567 1692 562 1697 567 13988 9030 2223 597 96250 9035 2219 591 96245 9032 2221 589 96240 9038 2215 595 96235 9033 2220 590 -# -name: Vol_dn -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 9028 4482 593 563 569 561 571 558 564 1696 568 1690 564 566 566 563 569 561 571 1688 566 563 569 561 571 1688 566 563 569 1690 564 1695 569 1689 565 1668 596 560 562 568 564 1695 569 560 562 568 564 1695 569 560 562 568 564 1695 569 1690 564 566 566 1692 572 1687 567 563 569 1690 564 566 566 564 568 562 570 559 563 567 565 565 567 562 570 560 562 1696 568 1665 589 1670 594 1665 589 1670 594 1664 590 1669 647 1612 590 13987 9031 2220 590 96223 9033 2217 593 96223 9034 2218 592 96225 9032 2219 591 96221 9087 2164 595 -# -name: Mute -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 9031 4479 596 560 572 558 564 566 566 1693 571 1688 566 563 569 561 571 559 563 1696 568 561 571 559 563 1697 567 562 570 1689 565 1694 570 1688 566 1693 571 1661 593 1693 571 558 564 566 566 564 568 1691 563 541 591 564 568 562 570 560 562 1697 567 1692 562 1696 568 562 570 1689 565 564 568 561 571 559 563 567 565 564 568 562 570 560 562 567 565 1694 570 1689 565 1694 570 1688 566 1693 571 1688 566 1693 571 1662 592 13987 9031 2220 590 96231 9034 2217 593 96234 9030 2222 588 96247 9037 2215 595 -# -name: Vol_up -type: parsed -protocol: NEC -address: 32 00 00 00 -command: 11 00 00 00 -# -name: Vol_dn -type: parsed -protocol: NEC -address: 32 00 00 00 -command: 14 00 00 00 -# OFF -name: Power -type: parsed -protocol: NECext -address: 83 F4 00 00 -command: 4E B1 00 00 -# -name: Power -type: parsed -protocol: NEC -address: 03 00 00 00 -command: 1D 00 00 00 -# -name: Vol_up -type: parsed -protocol: NEC -address: 03 00 00 00 -command: 11 00 00 00 -# -name: Vol_dn -type: parsed -protocol: NEC -address: 03 00 00 00 -command: 15 00 00 00 -# OFF -name: Power -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 9075 4307 677 433 675 456 651 461 651 1579 650 1576 649 459 649 460 648 465 648 1578 647 461 622 491 622 1604 647 465 647 1583 622 1608 647 1579 647 461 647 466 622 1604 647 465 647 1579 647 461 645 463 648 465 648 1583 646 1580 646 466 647 1579 622 491 647 1583 622 1608 647 1579 647 461 647 461 622 486 622 486 647 461 647 462 646 462 622 491 646 1584 622 1608 647 1584 621 1608 647 1583 646 1584 647 1584 646 1592 622 14330 9047 2137 621 -# -name: Power -type: parsed -protocol: Samsung32 -address: 07 00 00 00 -command: E6 00 00 00 -# -name: Vol_up -type: parsed -protocol: Samsung32 -address: 07 00 00 00 -command: 07 00 00 00 -# -name: Vol_dn -type: parsed -protocol: Samsung32 -address: 07 00 00 00 -command: 0B 00 00 00 -# -name: Mute -type: parsed -protocol: Samsung32 -address: 07 00 00 00 -command: 0F 00 00 00 -# OFF -name: Power -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 3523 1701 472 426 444 1269 472 426 444 426 442 429 443 427 443 426 444 426 444 426 443 427 442 429 440 430 439 432 438 1304 437 433 437 432 438 432 438 433 437 433 437 433 437 433 437 433 437 433 437 1304 437 433 437 433 437 433 437 1304 437 433 437 433 437 1304 437 433 437 434 436 433 437 434 436 434 436 434 436 433 437 433 437 434 436 1304 437 1305 436 1305 436 1305 436 1305 436 1305 436 434 436 434 436 1305 436 1305 436 1305 436 434 436 1305 436 1305 436 1306 435 1306 435 74393 3515 1736 437 433 437 1304 437 433 437 433 437 433 437 433 437 433 437 433 437 433 437 434 436 433 437 434 436 434 436 1304 437 434 436 434 436 434 436 434 436 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 434 436 1305 436 434 436 434 436 1306 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 436 434 435 435 1307 434 1331 410 1307 434 1307 434 1330 411 1307 434 460 410 460 410 1331 410 1331 410 1331 410 460 410 1331 410 1331 410 1331 410 1331 410 74393 3515 1736 437 433 437 1304 437 433 437 433 437 433 437 433 437 433 437 433 437 433 437 434 436 434 436 433 437 433 437 1304 437 434 436 434 436 434 437 434 436 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 434 436 1305 436 435 435 434 436 1305 436 434 436 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 1307 434 1306 435 1307 434 1307 434 1307 434 1331 410 460 410 460 410 1331 410 1331 410 1331 410 460 410 1331 410 1331 410 1331 410 1331 410 74393 3515 1736 437 433 437 1304 437 433 437 433 437 433 437 433 437 433 437 433 437 433 437 433 437 433 437 434 436 433 437 1304 437 433 437 434 436 434 436 434 436 434 436 434 436 434 436 434 436 434 437 1305 436 434 436 434 436 434 436 1305 436 434 436 434 436 1306 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 1307 434 1330 411 1330 411 1330 411 1330 411 1330 411 460 410 460 410 1331 410 1331 410 1331 410 460 410 1331 410 1331 410 1331 410 1331 410 -# OFF -name: Power -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 9093 4441 620 507 618 530 594 531 593 1652 595 1653 620 505 620 505 619 506 617 1630 616 508 616 508 616 1632 615 509 615 1631 616 1632 615 1632 615 510 615 509 615 1632 615 509 615 1632 615 510 615 510 614 509 615 1632 614 1633 614 509 615 1633 614 509 615 1632 615 1632 614 1633 614 510 614 510 615 510 615 510 614 510 614 510 615 510 615 510 614 1632 615 1632 614 1632 615 1632 615 1632 615 1632 615 1632 615 1633 614 14439 9088 2192 614 96349 9112 2190 616 -# OFF -name: Power -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 243 27700 170 27632 246 27694 282 27595 307 27497 241 27696 177 27710 164 27644 245 27629 246 27712 174 27638 211 27736 131 27741 306 27504 214 27727 135 27749 132 27761 126 27744 131 27753 127 27764 121 27767 132 27773 307 27577 131 27706 213 27761 129 27759 128 27770 125 27694 213 27751 307 27578 131 27737 131 27745 304 27575 335 27540 124 27752 132 27749 132 27747 134 27757 134 27758 127 27762 131 27748 131 27750 122 27749 130 27748 125 27772 131 27774 136 27762 135 27686 215 27742 131 27749 132 27756 133 27764 126 24073 9255 4460 672 488 618 541 619 541 619 1675 619 1676 618 542 618 542 618 542 618 1676 618 542 618 543 617 1678 616 568 592 1702 592 1702 592 1703 617 543 617 543 617 1677 617 543 617 1678 615 544 616 544 616 544 616 1678 616 1679 615 544 616 1679 615 545 615 1679 615 1679 615 1679 615 40240 9173 2273 591 -# -name: Vol_up -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 219 27658 217 27663 216 27658 216 27634 216 27642 215 27646 217 27662 217 27637 216 27649 216 27649 218 27656 217 27658 215 27640 214 27636 217 27649 216 27644 218 27635 217 27630 215 27645 216 27631 215 27632 216 27650 216 27628 217 27630 214 27627 217 27623 215 27632 215 27641 216 27634 214 27633 215 27648 215 27648 217 27651 215 27635 216 27629 216 27630 216 2021 9254 4461 618 542 618 542 618 542 618 1675 619 1676 618 541 619 541 619 542 618 1677 617 543 617 543 617 1678 616 568 592 1702 592 1702 618 1676 618 542 618 542 618 543 617 1677 617 543 617 544 616 1678 616 544 616 1678 616 1678 616 1678 616 544 616 1678 616 1678 616 544 616 1678 616 40239 9200 2247 617 99930 110 27739 119 27738 123 27750 126 27738 175 27617 214 27716 203 27604 213 27639 217 27631 214 27722 136 27753 119 27736 175 27618 246 27683 177 27619 245 27685 171 55486 244 27693 158 27635 241 27695 170 27693 129 27717 340 27530 113 27757 106 27751 124 27728 172 27707 126 27666 215 27708 123 27733 123 -# -name: Vol_dn -type: parsed -protocol: NECext -address: 18 E9 00 00 -command: 49 B6 00 00 -# -name: Power -type: parsed -protocol: NEC -address: 02 00 00 00 -command: 14 00 00 00 -# -name: Vol_up -type: parsed -protocol: NEC -address: 02 00 00 00 -command: 48 00 00 00 -# -name: Vol_dn -type: parsed -protocol: NEC -address: 02 00 00 00 -command: 40 00 00 00 -# -name: Mute -type: parsed -protocol: NEC -address: 02 00 00 00 -command: 18 00 00 00 -# -name: Power -type: parsed -protocol: NECext -address: B8 57 00 00 -command: 0C F3 00 00 -# -name: Mute -type: parsed -protocol: NECext -address: B8 57 00 00 -command: 0D F2 00 00 -# -name: Vol_dn -type: parsed -protocol: NECext -address: B8 57 00 00 -command: 1E E1 00 00 -# -name: Vol_up -type: parsed -protocol: NECext -address: B8 57 00 00 -command: 1F E0 00 00 -# -name: Power -type: parsed -protocol: NEC -address: 32 00 00 00 -command: 81 00 00 00 -# -name: Vol_dn -type: parsed -protocol: NEC -address: 32 00 00 00 -command: 8F 00 00 00 -# -name: Vol_up -type: parsed -protocol: NEC -address: 32 00 00 00 -command: 8C 00 00 00 -# -name: Mute -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 9066 4428 608 507 609 1622 609 507 609 507 609 1623 608 1623 609 507 609 506 610 1623 609 507 609 1622 610 1623 608 507 609 506 610 1622 609 1623 609 506 610 1622 610 506 610 1623 637 478 690 425 638 478 637 1594 637 1594 664 451 636 1594 610 506 610 1621 611 1621 610 1621 610 505 611 40183 9065 2156 637 95953 9037 2185 608 -# -name: Power -type: parsed -protocol: NEC -address: 00 00 00 00 -command: A8 00 00 00 -# -name: Mute -type: parsed -protocol: NEC -address: 00 00 00 00 -command: 88 00 00 00 -# -name: Vol_dn -type: parsed -protocol: NEC -address: 00 00 00 00 -command: 9C 00 00 00 -# -name: Vol_up -type: parsed -protocol: NEC -address: 00 00 00 00 -command: 8C 00 00 00 -# -name: Power -type: parsed -protocol: NECext -address: 87 45 00 00 -command: 17 E8 00 00 -# -name: Vol_up -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 9064 4354 666 1559 666 1562 662 1586 638 475 636 477 635 477 635 478 635 1590 635 1591 634 478 635 1591 634 478 634 478 635 478 634 1591 635 478 634 1591 634 478 635 478 634 478 635 1591 634 478 634 1591 635 478 634 478 634 1591 634 1591 635 1591 634 478 635 1591 634 478 634 1591 635 40957 9035 2144 634 95483 9047 2155 632 95484 9048 2153 633 -# -name: Vol_dn -type: parsed -protocol: NECext -address: 87 45 00 00 -command: 50 AF 00 00 -# -name: Mute -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 9034 4385 638 1587 664 1562 663 1587 637 476 635 478 634 478 635 478 635 1591 634 1591 634 478 635 1591 635 478 634 478 635 478 635 1591 635 478 634 478 634 1591 634 478 635 479 634 1591 635 478 634 1591 635 478 634 1592 634 478 634 1591 635 1591 635 478 634 1592 634 478 634 1591 634 40958 9033 2144 635 -# -name: Power -type: parsed -protocol: NECext -address: FF FF 00 00 -command: E8 17 00 00 -# -name: Vol_up -type: parsed -protocol: NECext -address: FF FF 00 00 -command: BD 42 00 00 -# -name: Vol_dn -type: parsed -protocol: NECext -address: FF FF 00 00 -command: F2 0D 00 00 -# -name: Power -type: parsed -protocol: Kaseikyo -address: 41 54 32 00 -command: 05 00 00 00 -# -name: Vol_up -type: parsed -protocol: Kaseikyo -address: 41 54 32 00 -command: 70 01 00 00 -# -name: Vol_dn -type: parsed -protocol: Kaseikyo -address: 41 54 32 00 -command: 71 01 00 00 -# -name: Power -type: parsed -protocol: NEC -address: 31 00 00 00 -command: 81 00 00 00 -# -name: Power -type: parsed -protocol: NECext -address: 83 F4 00 00 -command: 17 E8 00 00 -# -name: Vol_up -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 9010 4413 532 1617 532 1617 533 489 533 489 533 489 558 464 558 465 557 1593 557 465 557 466 556 1594 555 467 555 1595 529 1621 554 1595 581 1569 581 441 581 1569 581 441 581 441 581 441 581 441 581 441 581 1569 581 1569 581 441 581 1569 580 1569 580 1570 580 1595 554 1595 555 468 554 42156 8983 2135 556 -# -name: Vol_dn -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 9032 4390 556 1592 559 1591 559 463 559 463 558 464 557 465 556 465 557 1593 583 440 581 441 580 1569 581 441 581 1569 580 1569 581 1569 581 1570 580 1596 554 1596 554 468 554 468 554 468 554 442 580 442 580 1596 554 469 553 469 553 1596 554 1596 553 1597 550 1598 553 1598 552 469 551 42155 9008 2107 531 95218 9006 2108 582 -# -name: Mute -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 9011 4388 557 1617 532 1617 532 489 533 489 558 464 558 440 582 440 582 1593 556 466 556 466 556 1594 556 467 555 1595 555 1595 529 1620 554 1596 554 467 554 468 555 1595 579 443 581 1569 581 441 581 441 580 442 581 1569 581 1569 581 441 581 1569 580 441 581 1569 581 1569 581 1570 579 42152 8957 2159 556 \ No newline at end of file diff --git a/assets/resources/infrared/assets/projectors.ir b/assets/resources/infrared/assets/projectors.ir index 9f1d8a0a0..2be118e9c 100644 --- a/assets/resources/infrared/assets/projectors.ir +++ b/assets/resources/infrared/assets/projectors.ir @@ -1,7 +1,7 @@ Filetype: IR library file Version: 1 -# Last Updated 31th Jan, 2023 -# Last Checked 31th Jan, 2023 +# Last Updated 07th Mar, 2023 +# Last Checked 07th Mar, 2023 # # ON name: POWER @@ -802,3 +802,75 @@ type: raw frequency: 38000 duty_cycle: 0.33 data: 9011 4388 557 1617 532 1617 532 489 533 489 558 464 558 440 582 440 582 1593 556 466 556 466 556 1594 556 467 555 1595 555 1595 529 1620 554 1596 554 467 554 468 555 1595 579 443 581 1569 581 441 581 441 580 442 581 1569 581 1569 581 441 581 1569 580 441 581 1569 581 1569 581 1570 579 42152 8957 2159 556 +# +name: POWER +type: parsed +protocol: NECext +address: 4F 50 00 00 +command: 02 FD 00 00 +# +name: VOL+ +type: parsed +protocol: NECext +address: 4F 50 00 00 +command: 08 F7 00 00 +# +name: VOL- +type: parsed +protocol: NECext +address: 4F 50 00 00 +command: 0B F4 00 00 +# +name: VOL+ +type: parsed +protocol: NECext +address: 81 03 00 00 +command: F0 0F 00 00 +# +name: VOL+ +type: parsed +protocol: NECext +address: 87 45 00 00 +command: 51 AE 00 00 +# +name: MUTE +type: parsed +protocol: NECext +address: 87 45 00 00 +command: 52 AD 00 00 +# +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8811 4222 530 1580 531 1579 531 507 531 507 531 507 531 508 531 508 530 1582 528 1583 527 535 503 1608 502 536 501 1609 501 537 501 1610 500 538 500 1611 499 538 500 539 500 538 500 1611 500 539 499 538 500 1611 499 539 499 1611 499 1611 500 1611 499 539 499 1611 500 1611 500 539 499 35437 8784 4252 500 1611 500 1612 500 539 500 539 500 539 500 539 500 539 500 1611 500 1612 499 539 500 1612 500 539 500 1612 499 539 500 1612 500 539 500 1612 499 539 500 539 500 539 499 1612 499 540 499 539 500 1612 499 539 500 1612 499 1613 499 1612 499 539 500 1612 500 1612 500 539 500 +# +name: VOL+ +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 06 00 00 00 +# +name: VOL- +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 09 00 00 00 +# +name: MUTE +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 1A 00 00 00 +# +name: POWER +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 00 00 00 00 +# +name: MUTE +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9035 4437 563 548 563 548 563 522 594 1645 591 1639 592 518 593 548 563 552 563 1640 592 548 563 553 562 1668 564 524 592 1642 594 1674 562 1673 563 1639 593 548 563 552 564 1669 562 548 563 520 615 529 586 1645 587 529 587 1650 586 1646 586 529 586 1650 586 1649 587 1646 586 524 587 524 587 524 587 524 587 525 643 467 644 440 671 467 644 472 643 1592 644 1593 643 1593 642 1594 641 1594 587 1649 585 1651 563 1682 562 14430 9008 2205 562 diff --git a/assets/resources/infrared/assets/tv.ir b/assets/resources/infrared/assets/tv.ir index a7a8c306a..fdc62cb02 100644 --- a/assets/resources/infrared/assets/tv.ir +++ b/assets/resources/infrared/assets/tv.ir @@ -1,7 +1,7 @@ Filetype: IR library file Version: 1 -# Last Updated 19th Jan, 2023 -# Last Checked 31th Jan, 2023 +# Last Updated 07th Mar, 2023 +# Last Checked 07th Mar, 2023 # name: POWER type: parsed @@ -1839,3 +1839,63 @@ type: parsed protocol: SIRC20 address: 5A 0E 00 00 command: 11 00 00 00 +# +name: VOL+ +type: parsed +protocol: RC5 +address: 00 00 00 00 +command: 15 00 00 00 +# +name: VOL- +type: parsed +protocol: RC5 +address: 00 00 00 00 +command: 14 00 00 00 +# +name: CH+ +type: parsed +protocol: RC5 +address: 00 00 00 00 +command: 18 00 00 00 +# +name: CH- +type: parsed +protocol: RC5 +address: 00 00 00 00 +command: 17 00 00 00 +# +name: POWER +type: parsed +protocol: NEC +address: 80 00 00 00 +command: 12 00 00 00 +# +name: VOL+ +type: parsed +protocol: NEC +address: 80 00 00 00 +command: 1A 00 00 00 +# +name: VOL- +type: parsed +protocol: NEC +address: 80 00 00 00 +command: 1E 00 00 00 +# +name: MUTE +type: parsed +protocol: NEC +address: 80 00 00 00 +command: 10 00 00 00 +# +name: CH+ +type: parsed +protocol: SIRC20 +address: 10 01 00 00 +command: 34 00 00 00 +# +name: CH- +type: parsed +protocol: SIRC20 +address: 10 01 00 00 +command: 33 00 00 00 diff --git a/assets/resources/nfc/assets/mf_classic_dict.nfc b/assets/resources/nfc/assets/mf_classic_dict.nfc index d62d0655b..b6dbcc06c 100644 --- a/assets/resources/nfc/assets/mf_classic_dict.nfc +++ b/assets/resources/nfc/assets/mf_classic_dict.nfc @@ -1,41 +1,31 @@ ########################### # Do not edit, this file will be overwritten after firmware update # Use the user_dict file for user keys -# Last updated 25 January 2023 +# Last updated 8 March 2023 # ------------------------- - # MIFARE DEFAULT KEYS # -- ICEMAN FORK VERSION -- # -- CONTRIBUTE TO THIS LIST, SHARING IS CARING -- # https://github.com/RfidResearchGroup/proxmark3/blob/master/client/dictionaries/mfc_default_keys.dic - # DEFAULTKEY(FIRSTKEYUSEDBYPROGRAMIFNOUSERDEFINEDKEY) FFFFFFFFFFFF - # BLANKKEY 000000000000 - # NFC FORUM MADKEY # MAD ACCESS KEY A (REVERSED) A5A4A3A2A1A0 - # MAD ACCESS KEY B 89ECA97F8C2A - # KEY A WIEN # KEY B WIEN - # ICOPY-X E00000000000 E7D6064C5860 B27CCAB30DBD - # LIB / NAT BIEB D2ECE8B9395E - # NSCP DEFAULT KEY 1494E81663D7 - # KIEV KEYS 569369C5A0E5 632193BE1C3C @@ -44,52 +34,40 @@ D2ECE8B9395E 9DE89E070277 EFF603E1EFE9 F14EE7CAE863 - # KIEV / OV-CHIPKAART B5FF67CBA951 - # RKF # VÄSTTRAFIKEN KEYA, RKF ÖSTGÖTATRAFIKEN KEYA FC00018778F7 0297927C0F77 54726176656C - # VÄSTTRAFIKEN KEYB 00000FFE2488 776974687573 EE0042F88840 - # RKF SLKEYA 26940B21FF5D A64598A77478 - # RKF SLKEYB 5C598C9C58B5 E4D2770A89BE - # RKF REJSKORTDANMARK KEYA 722BFCC5375F F1D83F964314 - # RKF JOJOPRIVAKEYA 505249564141 - # RKF JOJOPRIVAKEYB 505249564142 - # RKF JOJOGROUPKEYA 47524F555041 - # RKF JOJOGROUPKEYB 47524F555042 434F4D4D4F41 434F4D4D4F42 4B0B20107CCB - # TNP3XXX # ACCESS CONTROL SYSTEM 605F5E5D5C5B - # MORE KEYS FROM MFC_DEFAULT_KEYS.LUA 000000000001 000000000002 @@ -104,10 +82,8 @@ F1D83F964314 200000000000 222222222222 27DD91F1FCF1 - # DIRECTORYANDEVENTLOGKEYB 2BA9621E0A36 - # DIRECTORYANDEVENTLOGKEYA 4AF9D7ADEBE4 333333333333 @@ -128,7 +104,6 @@ A00000000000 A053A292A4AF A94133013401 AAAAAAAAAAAA - # KEYFROMLADYADA.NET B00000000000 B127C6F41436 @@ -138,41 +113,31 @@ C934FE34D934 CCCCCCCCCCCC DDDDDDDDDDDD EEEEEEEEEEEE - # ELEVATOR # DATA FROM FORUM FFFFFF545846 F1A97341A9FC - # HOTEL SYSTEM 44AB09010845 85FED980EA5A - # ARD (FR) KEY A 43454952534E - # ARD (FR) KEY B 4A2B29111213 4143414F5250 - # TEHRAN RAILWAY A9B43414F585 1FB235AC1388 - # DATA FROM HTTP://IRQ5.IO/2013/04/13/DECODING-BCARD-CONFERENCE-BADGES/ # BCARD KEYB F4A9EF2AFC6D - # DATA FROM ... # S0 B 89EAC97F8C2A - # S4 A 43C7600DEE6B - # S6 A 0120BF672A64 - # S6 B FB0B20DF1F34 A9F953DEF0A3 @@ -181,7 +146,6 @@ A9F953DEF0A3 21EDF95E7433 C121FF19F681 3D5D9996359A - # HERE BE BIP KEYS... 3A42F33AF429 1FC235AC1309 @@ -215,41 +179,32 @@ D49E2826664F 51284C3686A6 3DF14C8000A1 6A470D54127C - # DATA FROM HTTP://PASTEBIN.COM/AK9BFTPW # LÄNSTRAFIKEN I VÄSTERBOTTEN 48FFE71294A0 E3429281EFC1 16F21A82EC84 460722122510 - # 3DPRINTER # EPI ENVISIONTE 3DPRINTER AAFB06045877 - # GYM # FYSIKEN A 3E65E4FB65B3 - # FYSIKEN B 25094DF6F148 - # CLEVERFIT A05DBD98E0FC - # HOTEL KEYCARD D3B595E9DD63 AFBECD121004 - # SIMONSVOSS 6471A5EF2D1A - # ID06 4E3552426B32 22BDACF5A33F 6E7747394E63 763958704B78 - # 24-7 D21762B2DE3B 0E83A374B513 @@ -263,10 +218,8 @@ F101622750B7 710732200D34 7C335FB121B5 B39AE17435DC - # KEY A 454841585443 - # DATA FROM HTTP://PASTEBIN.COM/GQ6NK38G D39BB83F5297 85675B200017 @@ -292,11 +245,9 @@ FEE470A4CB58 75EDE6A84460 DF27A8F1CB8E B0C9DD55DD4D - # DATA FROM HTTP://BIT.LY/1BDSBJL A0B0C0D0E0F0 A1B1C1D1E1F1 - # DATA FROM MSK SOCIAL A229E68AD9E5 49C2B5296EF4 @@ -323,7 +274,6 @@ C7C0ADB3284F D8A274B2E026 B20B83CB145C 9AFA6CB4FC3D - # DATA FROM HTTP://PASTEBIN.COM/RRJUEDCM 0D258FE90296 E55A3CA71826 @@ -337,19 +287,16 @@ EEB420209D0C 1ACC3189578C C2B7EC7D4EB1 369A4663ACD2 - # DATA FROM HTTPS://GITHUB.COM/ZHANGJINGYE03/ZXCARDUMPER # ZXCARD KEY A/B 668770666644 003003003003 - # DATA FROM HTTP://PHREAKERCLUB.COM/FORUM/SHOWTHREAD.PHP?P=41266 26973EA74321 71F3A315AD26 51044EFB5AAB AC70CA327A04 EB0A8FF88ADE - # TRANSPORT SYSTEM METROMONEY 2803BCB0C7E1 9C616585E26D @@ -358,7 +305,6 @@ EB0A8FF88ADE A160FCD5EC4C 112233445566 361A62F35BC9 - # TRANSPORT SYSTEM SPAIN 83F3CB98C258 070D486BC555 @@ -392,18 +338,14 @@ C52876869800 5145C34DBA19 25352912CD8D 81B20C274C3F - # DATA FROM MALL # PLAYLAND BALIKESIR ABBA1234FCB0 - # A TRIO BOWLING BAHCELIEVLER 314F495254FF 4152414B4E41 - # KARINCA PARK NIGDE 4E474434FFFF - # DATA FROM HTTPS://GITHUB.COM/RADIOWAR/NFCGUI 44DD5A385AAF 21A600056CB0 @@ -441,12 +383,10 @@ CBA6AE869AD5 A7ABBC77CC9E F792C4C76A5C BFB6796A11DB - # DATA FROM SALTO A/B 6A1987C40A21 7F33625BC129 2338B4913111 - # DATA FROM STOYE CB779C50E1BD A27D3804C259 @@ -474,22 +414,16 @@ D9A37831DCE5 C5CFE06D9EA3 C0DECE673829 A56C2DF9A26D - # DATA FROM HTTPS://PASTEBIN.COM/VBWAST74 68D3F7307C89 - # SMART RIDER. WESTERN AUSTRALIAN PUBLIC TRANSPORT CARDS 568C9083F71C - # BANGKOK METRO KEY 97F5DA640B18 - # METRO VALENCIA KEY A8844B0BCA06 - # HTC EINDHOVEN KEY 857464D3AAD1 - # VIGIK KEYS # VARIOUS SOURCES : # * HTTPS://GITHUB.COM/DUMPDOS/VIGIK @@ -498,24 +432,18 @@ A8844B0BCA06 # FRENCH VIGIK # VIGIK1 A 314B49474956 - # VIGIK1 B 564C505F4D41 BA5B895DA162 - # VIGIK MYSTERY KEYS MIFARE 1K EV1 (S50) # 16 A 5C8FF9990DA2 - # 17 A 75CCB59C9BED - # 16 B D01AFEEB890A - # 17 B 4B791BEA7BCC - # BTCINO UNDETERMINED SPREAKD 0X01->0X13 KEY 021209197591 2EF720F2AF76 @@ -524,7 +452,6 @@ D01AFEEB890A 4A6352684677 BF1F4424AF76 536653644C65 - # INTRATONE COGELEC # DATA FROM HTTP://BOUZDECK.COM/RFID/32-CLONING-A-MIFARE-CLASSIC-1K-TAG.HTML 484558414354 @@ -543,7 +470,6 @@ E64A986A5D94 66D2B7DC39EF 6BC1E1AE547D 22729A9BD40F - # DATA FROM HTTPS://DFIR.LU/BLOG/CLONING-A-MIFARE-CLASSIC-1K-TAG.HTML 925B158F796F FAD63ECB5891 @@ -559,10 +485,8 @@ CC6B3B3CD263 703140FD6D86 157C9A513FA5 E2A5DC8E066F - # DATA FROM FORUM, SCHLAGE 9691T FOB EF1232AB18A0 - # DATA FROM A OYSTER CARD 374BF468607F BFC8E353AF63 @@ -593,10 +517,8 @@ A2ABB693CE34 91F93A5564C9 E10623E7A016 B725F9CBF183 - # DATA FROM FDI TAG 8829DA9DAF76 - # DATA FROM GITHUB ISSUE 0A7932DC7E65 11428B5BCE06 @@ -621,18 +543,14 @@ D4FE03CE5B09 D4FE03CE5B0A D4FE03CE5B0F E241E8AFCBAF - # DATA FROM FORUM POST 123F8888F322 050908080008 - # DATA FROM HOIST 4F9F59C9C875 - # DATA FROM PASTEBIN 66F3ED00FED7 F7A39753D018 - # DATA FROM HTTPS://PASTEBIN.COM/Z7PEEZIF 386B4D634A65 666E564F4A44 @@ -664,23 +582,19 @@ F7A39753D018 6F506F493353 31646241686C 77646B633657 - # DATA FROM TRANSPERT 2031D1E57A3B 53C11F90822A 9189449EA24E - # DATA FROM GITHUB 410B9B40B872 2CB1A90071C8 - # DATA FROM 8697389ACA26 1AB23CD45EF6 013889343891 0000000018DE 16DDCB6B3F24 - # DATA FROM HTTPS://PASTEBIN.COM/VWDRZW7D # VINGCARD MIFARE 4K STAFF CARD EC0A9B1A9E06 @@ -694,7 +608,6 @@ B66AC040203A 2E641D99AD5B AD4FB33388BF 69FB7B7CD8EE - # HOTEL 2A6D9205E7CA 2A2C13CC242A @@ -702,34 +615,25 @@ AD4FB33388BF 01FA3FC68349 6D44B5AAF464 1717E34A7A8A - # RFIDEAS 6B6579737472 - # HID MIFARE CLASSIC 1K KEY 484944204953 204752454154 - # HID MIFARE SO 3B7E4FD575AD 11496F97752A - # LUXEO/AZTEK CASHLESS VENDING 415A54454B4D - # BQT 321958042333 - # APERIO KEY_A SECTOR 1, 12, 13, 14, 15 DATA START 0 LENGTH 48 160A91D29A9C - # GALLAGHER B7BF0C13066E - # PIK COMFORT MOSCOW KEYS (ISBC MIFARE PLUS SE 1K) 009FB42D98ED 002E626E2820 - # BOSTON, MA, USA TRANSIT - MBTA CHARLIE CARD # CHARLIE 3060206F5B0A @@ -766,10 +670,8 @@ D80511FC2AB4 BB467463ACD6 E67C8010502D FF58BA1B4478 - # DATA FROM HTTPS://PASTEBIN.COM/KZ8XP4EV FBF225DC5D58 - # DATA HTTPS://PASTEBIN.COM/BEM6BDAE # VINGCARD.TXT 4708111C8604 @@ -786,19 +688,16 @@ FBF225DC5D58 D9BCDE7FC489 0C03A720F208 6018522FAC02 - # DATA FROM HTTPS://PASTEBIN.COM/4T2YFMGT # MIFARE TECHNISCHE UNIVERSITÄT GRAZ TUG D58660D1ACDE 50A11381502C C01FC822C6E5 0854BF31111E - # MORE KEYS: 8A19D40CF2B5 AE8587108640 135B88A94B8B - # RUSSIAN TROIKA CARD 08B386463229 0E8F64340BA4 @@ -854,7 +753,6 @@ EAAC88E5DC99 F8493407799D 6B8BD9860763 D3A297DC2698 - # KEYS FROM MIFARECLASSICTOOL PROJECT 044CE1872BC3 045CECA15535 @@ -910,28 +808,22 @@ FD8705E721B0 00ADA2CD516D 237A4D0D9119 0ED7846C2BC9 - # HOTEL ADINA 9EBC3EB37130 - # MOST LIKELY DIVERSED INDIVIDUAL KEYS. # DATA FROM HTTPS://GITHUB.COM/KORSEHINDI/PROXMARK3/COMMIT/24FDBFA9A1D5C996AAA5C192BC07E4AB28DB4C5C 491CDC863104 A2F63A485632 98631ED2B229 19F1FFE02563 - # ARGENTINA 563A22C01FC8 43CA22C13091 25094DF2C1BD - # OMNITEC.ES HOTEL TIMECARD / MAINTENANCECARD AFBECD120454 - # OMNITEC.ES HOTEL EMERGENCYCARD 842146108088 - # TAPCARD PUBLIC TRANSPORT LA EA1B88DF0A76 D1991E71E2C5 @@ -965,7 +857,6 @@ B81846F06EDF C6A76CB2F3B5 E3AD9E9BA5D4 6C9EC046C1A4 - # ROC HIGHSCHOOL ACCESSCARD B021669B44BB B18CDCDE52B7 @@ -997,7 +888,6 @@ AE43F36C1A9A BE7C4F6C7A9A 5EC7938F140A 82D58AA49CCB - # MELONCARD 323334353637 CEE3632EEFF5 @@ -1014,7 +904,6 @@ A7FB4824ACBF 00F0BD116D70 4CFF128FA3EF 10F3BEBC01DF - # TRANSPORTES INSULAR LA PALMA 0172066B2F03 0000085F0000 @@ -1048,7 +937,6 @@ B1A862985913 3B0172066B2F 3F1A87298691 F3F0172066B2 - # TEHRAN EZPAY 38A88AEC1C43 CBD2568BC7C6 @@ -1065,12 +953,10 @@ D3B1C7EA5C53 604AC8D87C7E 8E7B29460F12 BB3D7B11D224 - # CHACO B210CFA436D2 B8B1CFA646A8 A9F95891F0A4 - # KEYS FROM APK APPLICATION "SCAN BADGE" 4A4C474F524D 444156494442 @@ -1090,7 +976,6 @@ A0004A000036 DFE73BE48AC6 B069D0D03D17 000131B93F28 - # FROM THE DFW AREA, TX, USA A506370E7C0F 26396F2042E7 @@ -1107,7 +992,6 @@ EF4C5A7AC6FC B47058139187 8268046CD154 67CC03B7D577 - # FROM THE HTL MÖDLING, NÖ, AT A5524645CD91 D964406E67B4 @@ -1116,40 +1000,32 @@ D964406E67B4 C27D999912EA 66A163BA82B4 4C60F4B15BA8 - # CAFE + CO, AT 35D850D10A24 4B511F4D28DD E45230E7A9E8 535F47D35E39 FB6C88B7E279 - # METRO CARD, AT 223C3427108A - # UNKNOWN, AT 23D4CDFF8DA3 E6849FCC324B 12FD3A94DF0E 0B83797A9C64 39AD2963D3D1 - # HOTEL BERLIN CLASSIC ROOM A KEY 34B16CD59FF8 - # HOTEL BERLIN CLASSIC ROOM B KEY BB2C0007D022 - # COINMATIC LAUNDRY SMART CARD # DATA FROM: HTTPS://PASTEBIN.COM/XZQILTUF 0734BFB93DAB 85A438F72A8A - # DATA FROM FORUM, CHINESE HOTEL 58AC17BF3629 B62307B62307 A2A3CCA2A3CC - # GRANADA, ES TRANSPORT CARD 000000270000 0F385FFB6529 @@ -1167,7 +1043,6 @@ B385EFA64290 C9739233861F F3864FCCA693 FC9839273862 - # VARIOUS HOTEL KEYS 34D3C568B348 91FF18E63887 @@ -1175,7 +1050,6 @@ FC9839273862 354A787087F1 4A306E62E9B6 B9C874AE63D0 - # DATA FROM OFFICIAL REPO F00DFEEDD0D0 0BB31DC123E5 @@ -1193,23 +1067,18 @@ B8937130B6BA D7744A1A0C44 82908B57EF4F FE04ECFE5577 - # COMFORT INN HOTEL 4D57414C5648 4D48414C5648 - # UNKNOWN HOTEL KEY 6D9B485A4845 - # BOSCH SOLUTION 6000 # FOUND IN TAGINFO APP # RATB KEY C1E51C63B8F5 1DB710648A65 - # E-GO CARD KEY 18F34C92A56E - # LIBRARY CARD MFP - SL1 4A832584637D CA679D6291B0 @@ -1225,7 +1094,6 @@ AADE86B1F9C1 C67BEB41FFBF B84D52971107 52B0D3F6116E - # DATA FROM HTTPS://PASTEBIN.COM/CLSQQ9XN CA3A24669D45 4087C6A75A96 @@ -1234,12 +1102,10 @@ D73438698EEA 5F31F6FCD3A0 A0974382C4C5 A82045A10949 - # DATA FROM HTTPS://PASTEBIN.COM/2IV8H93H # FUNNIVARIUM # FORUM ANKARA 2602FFFFFFFF - # MACERA ADASI # ANKARA KENTPARK # INACTIVE @@ -1247,20 +1113,16 @@ A82045A10949 DFF293979FA7 4D6F62692E45 4118D7EF0902 - # PETROL OFISI # POSITIVE CARD # ODE-GEC 0406080A0C0E - # KONYA ELKART 988ACDECDFB0 120D00FFFFFF - # BOWLINGO # SERDIVAN AVYM 4AE23A562A80 - # KART54 2AFFD6F88B97 A9F3F289B70C @@ -1269,23 +1131,18 @@ DB6819558A25 B16B2E573235 42EF7BF572AB 274E6101FC5E - # CRAZY PARK # KIZILAY AVM 00DD300F4F10 - # KARTSISTEM B FEE2A3FBC5B6 - # TORU ENT # TAURUS AVM 005078565703 - # VING? 0602721E8F06 FC0B50AF8700 F7BA51A9434E - # ESKART # ESKISEHIR TRANSPORT CARD E902395C1744 @@ -1294,7 +1151,6 @@ E902395C1744 D8BA1AA9ABA0 76939DDD9E97 3BF391815A8D - # MUZEKART # MUSEUM CARD FOR TURKEY 7C87013A648A @@ -1326,7 +1182,6 @@ D0DDDF2933EC 240F0BB84681 9E7168064993 2F8A867B06B4 - # BURSAKART # BURSA TRANSPORT CARD 755D49191A78 @@ -1336,22 +1191,18 @@ DAC7E0CBA8FD 0860318A3A89 1927A45A83D3 B2FE3B2875A6 - # PLAYLAND # MALTEPE PARK ABCC1276FCB0 AABAFFCC7612 - # LUNASAN # KOCAELI FAIR 26107E7006A0 - # GAMEFACTORY # OZDILEK 17D071403C20 534F4C415249 534F4C303232 - # NESPRESSO, SMART CARD # KEY-GEN ALGO, THESE KEYS ARE FOR ONE CARD FF9A84635BD2 @@ -1430,20 +1281,15 @@ AE76242931F1 124578ABFEDC ABFEDC124578 4578ABFEDC12 - # PREMIER INN HOTEL CHAIN 5E594208EF02 AF9E38D36582 - # NORWEGIAN BUILDING SITE IDENTICATION CARD. (HMS KORT) 10DF4D1859C8 - # KEY B B5244E79B0C8 - # UKRAINE HOTEL F5C1C4C5DE34 - # DATA FROM MIFARE CLASSIC TOOL REPO # ROTTERDAM UNIVERSITY OF APPLIED SCIENCES CAMPUS CARD BB7923232725 @@ -1468,7 +1314,6 @@ B5ADEFCA46C4 BF3FE47637EC B290401B0CAD AD11006B0601 - # ARMENIAN METRO E4410EF8ED2D 6A68A7D83E11 @@ -1478,7 +1323,6 @@ D3F3B958B8A3 2196FAD8115B 7C469FE86855 CE99FBC8BD26 - # KEYS FROM EUROTHERMES GROUP (SWITZERLAND) D66D91829013 75B691829013 @@ -1494,11 +1338,9 @@ FED791829013 29A791829013 668091829013 00008627C10A - # KEYS FROM NSP MANCHESTER UNIVERSITY UK ACCOMODATION STAFF AND STUDENTS 199404281970 199404281998 - # EASYCARD 310D51E539CA 2CCDA1358323 @@ -1506,7 +1348,6 @@ FED791829013 562E6EF73DB6 F53E9F4114A9 AD38C17DE7D2 - # SOME KEYS OF HTTPS://W3BSIT3-DNS.COM AND HTTPS://IKEY.RU # STRELKA EXTENSION 5C83859F2224 @@ -1517,7 +1358,6 @@ C4D3911AD1B3 CAD7D4A6A996 DA898ACBB854 FEA1295774F9 - # MOSCOW PUBLIC TOILETS CARD 807119F81418 22C8BCD10AAA @@ -1527,7 +1367,6 @@ DBF9F79AB7A2 34EDE51B4C22 C8BCD10AAABA BCD10AAABA42 - # MOSCOW SOCIAL CARD 2F87F74090D1 E53EAEFE478F @@ -1570,12 +1409,10 @@ F750C0095199 82DA4B93DB1C 9CF46DB5FD46 93EB64ACF43D - # KEYS FROM RFIDRESEARCHGROUP PROXMARK3 PROJECT # HTTPS://GITHUB.COM/RFIDRESEARCHGROUP/PROXMARK3/BLOB/MASTER/CLIENT/DICTIONARIES/MFC_DEFAULT_KEYS.DIC 13B91C226E56 5A7A52D5E20D - # IRON LOGIC A3A26EF4C6B0 2C3FEAAE99FC @@ -1594,11 +1431,9 @@ DEC0CEB0CE24 413BED2AE45B D6261A9A4B3F CB9D507CE56D - # MORE KEYS FROM THE PM3 REPO # KEYS OF ARMENIAN UNDERGROUND TICKET A0A1A2A8A4A5 - # https://github.com/RfidResearchGroup/proxmark3/blob/master/client/dictionaries/mfc_keys_bmp_sorted.dic 002DE0301481 004173272D18 @@ -2600,7 +2435,6 @@ EE17C426D25E EE487A4C806E EE5931913A8D EED56840AEBA - # https://github.com/RfidResearchGroup/proxmark3/blob/master/client/dictionaries/mfc_keys_icbmp_sorted.dic 00383D96411D 005307DB7853 @@ -3602,7 +3436,6 @@ EE3029556CEB EE49610E6121 EEB704D69BCA EED69A391464 - # https://github.com/RfidResearchGroup/proxmark3/blob/master/client/dictionaries/mfc_keys_mrzd_sorted.dic 010203040506 013940233313 @@ -3661,7 +3494,6 @@ F83466888612 F89C86B2A961 FFFFAE82366C FFFFD06F83E3 - # Unknown origin 2DEB57A3EA8F 32C1BB023F87 @@ -3745,26 +3577,19 @@ D27058C6E2C7 E19504C39461 FA1FBB3F0F1F FF16014FEFC7 - # Cracked by UberGuidoZ # https://github.com/UberGuidoZ # BadgeMaker Leaked 1A1B1C1D1E1F 1665FE2AE945 158B51947A8E -EL67EC67C7FF -D53732OFF9OE 5E56BFA9E2C9 F81CED821B63 C81584EF5EDF 9551F8F9259D -36EL765CE3E8 509052C8E42E 776C9B03BE71 -C608EL3ADD50 BEE8B345B949 -EDOEC56EEFDD -9716D524LE28 05D1FC14DC31 3321FB75A356 F22A78E29880 @@ -3778,9 +3603,7 @@ DB32A6811327 8AA8544A2207 8C5819E780A4 7549E90353A2 -2E52ABEOCE95 E46210ED98AB -61DO30COD7A8 18E20102821E DA59354DFB88 040047C12B75 @@ -3789,7 +3612,6 @@ D10008074A6F 446176696453 6F6674776172 6520446F7665 - # Apartment keyfobs in USA from Corvette830 E60F8387F0B9 FFD46FF6C5EE @@ -3798,7 +3620,6 @@ FFD46FF6C5EE 1C5179C4A8A1 16CA203B811B 11AC8C8F3AF2 - # The Westin Jakarta Indonesia from D4DB0D # Peppers Hotel Unknown location from D4DB0D 6E0DD4136B0A @@ -3813,19 +3634,15 @@ FC5AC7678BE3 F09BB8DD142D B4B3FFEDBE0A 540E0D2D1D08 - # Schlage 9691T Keyfob 7579B671051A 4F4553746B41 - # FOOD REPUBLIC 30C1DC9DD040 A9B9C1D0E3F1 - # iGuard Simple (and reverse) keys AAAAAAFFFFFF FFFFFFAAAAAA - # Vigik verified by quantum-x # https://github.com/RfidResearchGroup/proxmark3/pull/1742#issuecomment-1206113976 A00027000099 @@ -3843,10 +3660,8 @@ A00000043D79 A00000000064 A00025000030 A00003000057 - # BH USA 2013 conference 012279BAD3E5 - # Vigik ScanBadge App (fr.badgevigik.scanbadge) # Website https://badge-vigik.fr/ - By Alex` 0000A2B3C86F @@ -3875,12 +3690,10 @@ A00003000057 9EB7C8A6D4E3 A22AE12C9013 AFC984A3576E - # Spackular A/B # data from http://www.proxmark.org/forum/viewtopic.php?pid=45100#p45100 7CB033257498 1153AABAFF6C - # iGuard Simple and Reverse Keys D537320FF90E 36E1765CE3E8 @@ -3889,13 +3702,10 @@ ED0EC56EEFDD 9716D5241E28 2E52ABE0CE95 61D030C0D7A8 - # BadgeMaker Leaked from https://github.com/UberGuidoZ E167EC67C7FF - # Schlage 9691T Keyfob from seasnaill Added by VideoMan. 3111A3A303EB - # Transport cards E954024EE754 0CD464CDC100 @@ -3905,18 +3715,13 @@ F7A545095C49 6862FD600F78 72A0C485D3F7 6A530C91F85B - # RENFE MADRID (TRAIN) Extracted with detect reader 701AA491A4A5 12BA20088ED3 - # MISC KEYS FROM MY OLD ACCESS CARDS F18D91EE3033 0E726E11CFCC 1D14130D1A0B -4ADLE273EAFL -3DFL4C8000A1 -E64Q986Q5D94 201106141030 D144BD193063 01E2C14F1B18 @@ -3964,75 +3769,68 @@ FE98F38F3EE2 # added by colonelborkmundus # "the more, the marriott" mifare project # - -# 1k - graduate hotel -C49DAE1C6049 -209A2B910545 - -# 1k - westin -8C29F8320617 -5697519A8F02 +# 20230125-01, Elite Member Marriott Rewards +43012BD9EB87 +# 20230125-02, Elite Member Marriott Rewards +3119A70628EB +# 20230125-03, Elite Member Marriott Rewards +23C9FDD9A366 +# 20230125-04, Elite Member Marriott Rewards +7B4DFC6D6525 +# 20230125-05, Elite Member Marriott Rewards +1330824CD356 +# 20230125-06, Elite Member Marriott Rewards +30AAD6A711EF +# 20230125-07, Fairfield Inn & Suites Marriott +7B3B589A5525 +# 20230125-08, Moxy Hotels +20C166C00ADB +# 20230125-09, Westin Hotels & Resorts 7D0A1C277C05 2058580A941F +8C29F8320617 +# 20230125-10, Westin Hotels & Resorts C40964215509 D44CFC178460 - -# 1k - marriott -7B4DFC6D6525 -23C9FDD9A366 -3119A70628EB -30AAD6A711EF -1330824CD356 -43012BD9EB87 -035C70558D7B -9966588CB9A0 -12AB4C37BB8B - -# 1k - AC hotels marriott -8EA8EC3F2320 +5697519A8F02 +# 20230125-12, AC Hotels Marriott 7B56B2B38725 - -# 1k - the ritz-carlton -30FB20D0EFEF -D20289CD9E6E -66A3B064CC4B -D18296CD9E6E - -# 1k - unknown -722538817225 - -# 1k - aria resort & casino -316B8FAA12EF +# 20230125-13, AC Hotels Marriott +8EA8EC3F2320 +# 20230125-14, Waldorf Astoria Chicago +011C6CF459E8 +# 20230125-24, Aria Resort & Casino A18D9F4E75AF - -# 1k - fairfield inn & suites marriott -7AEB989A5525 -7B3B589A5525 -215E9DED9DDF -334E91BE3377 -310308EC52EF - -# 1k - residence inn marriott -F72CD208FDF9 - -# 1k - sheraton -42FC522DE987 - -# 1k - millenium hotels -132F641C948B - -# 1k - moxy hotels -20C166C00ADB -9EE3896C4530 - -# 1k - residence inn marriott +# 20230125-25, Aria Resort & Casino +316B8FAA12EF +# 20230125-26, Residence Inn Mariott 3122AE5341EB - -# 1k - americinn -8AC04C1A4A25 - -# 1k - the industrialist +# 20230125-27, Residence Inn Mariott +F72CD208FDF9 +# 20230125-28, Marriott +035C70558D7B +# 20230125-29, Marriott +12AB4C37BB8B +# 20230125-30, Marriott +9966588CB9A0 +# 20230125-31, Sheraton +42FC522DE987 +# 20230125-32, The Industrialist 2158E314C3DF - -# 1k - waldorf astoria -011C6CF459E8 \ No newline at end of file +# 20230125-39, The Ritz-Carlton Balharbour +30FB20D0EFEF +# 20230125-40, The Ritz-Carlton Balharbour +66A3B064CC4B +# 20230125-41, The Ritz-Carlton Balharbour +D18296CD9E6E +# 20230125-42, The Ritz-Carlton Balharbour +D20289CD9E6E +# 20230125-44, Graduate Hotels +209A2B910545 +C49DAE1C6049 +# 20230125-46, AmericInn +8AC04C1A4A25 +# 20230129-53, Marriott Bonvoy +6E029927600D +3E173F64C01C +C670A9AD6066 \ No newline at end of file diff --git a/assets/resources/subghz/assets/README.md b/assets/resources/subghz/assets/README.md index b129906e6..e98ccba9c 100644 --- a/assets/resources/subghz/assets/README.md +++ b/assets/resources/subghz/assets/README.md @@ -1,10 +1,10 @@ -#Sub-Ghz Frequencies in Xtreme +#Sub-Ghz Frequencies in Xtreme -Officially supported frequencies: 300-348 MHz, 387-464 MHz, and 779-928 MHz (from [CC1101 chip docs](https://www.ti.com/product/CC1101))
+Officially supported frequencies: 300-350 MHz, 387-467.75 MHz, and 779-928 MHz (from [CC1101 chip docs](https://www.ti.com/product/CC1101))
Unofficially supported frequencies: 281-361 MHz, 378-481 MHz, and 749-962 MHz (from [YARD Stick One/CC1111 Docs](https://greatscottgadgets.com/yardstickone/)) Currently no other Flipper firmware allows anything outside of the officially supported CC1101 specs without editing files that get overwritten with every update. -Xtreme has these frequencies enabled by default and regional restrictions disabled. +Xtreme has these options easily togglable in the Xtreme Settings app. **NOTE: Operating outside supported frequencies can damage your amp and may not provide the same results as supported frequencies.
-Please understand what you're doing if you transmit on unsupported frequencies as medical devices are licensed to operate there.** +Please understand what you're doing if you transmit on unsupported frequencies as medical devices are licensed to operate there.** diff --git a/assets/resources/subghz/assets/extend_range.txt b/assets/resources/subghz/assets/extend_range.txt deleted file mode 100644 index 1b404a0e5..000000000 --- a/assets/resources/subghz/assets/extend_range.txt +++ /dev/null @@ -1,8 +0,0 @@ -Filetype: Flipper SubGhz Setting File -Version: 1 - -# Whether to allow extended ranges that can break your flipper -use_ext_range_at_own_risk: false - -# Whether to ignore the default TX region settings -ignore_default_tx_region: false diff --git a/assets/resources/subghz/assets/keeloq_mfcodes b/assets/resources/subghz/assets/keeloq_mfcodes index 31619d6e7..cb80c2e2e 100644 --- a/assets/resources/subghz/assets/keeloq_mfcodes +++ b/assets/resources/subghz/assets/keeloq_mfcodes @@ -1,57 +1,58 @@ Filetype: Flipper SubGhz Keystore File Version: 0 Encryption: 1 -IV: 5C AD F1 B1 BA B0 BA 11 AB 0B A3 6C F7 E3 6A 21 -AA7C98C6E94AA4062F875AB40BA3CE977D18B89830CCEB30179C5F2788E8941C -5B2D494F1AC8971B5F758D56D72F709E674B92F4B8D5BF9E4BBD48DBEFD11678 -AF3FFC3AB39E07694127DC64DD9BB371461200F4A6ED4622C5BA21F854394BE4 -2D889E3B426032518D20A7B35718FCCDA6769E310021BD41FF87D2A4DC4C1ED8 -7FB119118304533D748BAF06AAF69E838D6B6B20A84B51AEEBE6001FD5307DC8C98B46CF249E3E24FF64A033C470135B -B38B5D82576D4BFB7E5B600711695E409C1E33B539F167BE54572F9721D75566 -CAB78E2DE09DA67C95E0302A9A0010204A715CE61AD0884DEF018A15971D0DA8 -9794862BCFD7F31676245AB8BF369C754CBD6E818E8866CB2C743C3DC7F455BA -77AAE6912434A95B46A6C07B5A59EF287B83CE2620FEECF92CDAE7858F6A8050 -1A55BDD4F0E5F555EA3DF74182CE871343C955FD2349FB37B1B21FAC170511EB -892FAE550B5BF6CE2C6C85B74C8AD45D37E174CFD9EE0767927BCBCC904AA7CB464451E43EF9435E61635DC6EEFAD5E5 -26A7511E2213053BA49868CEE9FB56474CFB5AA422AFBF4C0FB5D5285E6C474DB7EF74A731D259A18AD1EF6EE015A9B2 -F3B14CFE82809DD20A8CE0EF9806CA40E3BA6EF8D62495AF9DEF0F92625F05E7 -BAC0B60E24FE913885FE3FF0A7E9EAB2B224471ABF738507E14060747792783E -070E67988EFF2B31EEE0AD2E8663EE8CEB4DACBB9D8D1159E7C9E84E18F1F7D4 -1755162EEA343C8D9D09852FA5B90E05DDE129145442822F937CE64EA168DE78 -70CF01EB50613461B447C6F54A2E28CAFDD39776B90BD88FDE3A2F5BE8ED5950387819C5F8BCAEEE0DE1A16692F5F07C -8152ECF2A96968C7803803851C70489B19846AB4450B5B5398A86E05D76A5E85 -D9A44B2BC3920622F8ED3EC2A18756612D05C3BF3555F752520AA206128576B3 -AD156C422650D482BFD399926330C849F61C145C6874EAE2826AEDA6BFEFB9892FF615E9CE4005274BF2217733629510 -1735246565057FF7A91DF91CE87A82F1823AC53282377CF188245C4E0A0A12420DFDDA0B70D2498D1C6C5456527FBF66 -E25E66ADCCF628CD21C14D61D47952816EE2265AD7664645A321D45197E1703B -07ACA960A68DAD72B3F7C51AB8573AB0BD08531390226F5404B0098A9FCAC239 -577209D5117506CEF9B6D4EA9F9783CE0EA57415EBBE5318B057D7E0AEFBDFC9 -708B950FCF78EFC2FEE1969A2590EBD8BDF95ACD2B052E96017B39EA667C2AAB -05D7DD808F82F97848B695D5C302FDF20F55D2FD56E386227726691ED581EC9A -08F1AEF4B9F694DF723813613FE0208A4ACF7079773B37CEFCAF7A132EBD4DBA -F607CEBFA3E307F27AC484B9E08FB96A2B678FBA4963AAC479F53D9CAC3530DF -578BBAD7BD2818DC9CC6F711035C267AF005B23A24BA79C66D408474CECEA799 -CA9A8ACCB5BEA6B53C4CE302CC052FC560317410A977425D0E7924AE10D4ED85 -523FE24DD7A7F4E2DF0E09771C863A934662A564152966B67DFECE7D37A53A50 -A606653C13720134F4564DD96FCD1AD44E66D9405BA95CFD0D91FE8828D0B615 -3ED9295F9C5D3C4E949B8E83CF63EBFD0482D224F4015451AD31646FD91F95D2 -7077291A87C15DE4605989467D38283F5416D6AD6D9B2458CE5A75D40A61B3D9 -08A7FECA1D73D605D4E71E69AD2112915A28DBE5A6817C52C2C451A9E1435CF1 -76FBB6AA098D630B5297261090F50011FFEEE6611347A2DF65E0C1C88C4DFFDD879A7C9134D2C51A61C3C7796833ADA6 -2315673B961F8D1C46020AAAB3BC73EE480496F6F5C6B9BF7175DF32A92D1E29 -851620CB407C3CA9A26CF6F6E3350A13FC241A99D7FCAF5EC41A90C40F6518A2 -F97AB6A733F1C288585629CC89A8AFA50B0D7B4CC2E0D41CA0B7118B37EB5170 -E725E6B06E9C8A575609E28DB887508D6CD6623BFD2151BDE0984E1E014A3FAB -138082F590404A5A335601C9872D08F0950EEEB7B28C5D6238F6A4C7FD73CFAC -22687BB4B5044A02C4F86FFA6B8113A3D315325CBAC503498284FA44C3566EE3 -1A5D93DE887B497D3904BD5C29F456EB6D8EA7D6B79962769F1862F21C6CC257 -F3ADA4CA246DC47577A40F7F24213A1E773CE7C9D3F5B0A054EE0807C81CB0B6 -CBA2632796ADB82F57A0B701BF1EF9787C27F4DD14C79D0FD09CD88447A8368B -5CF50ED192649B1F769451EEA7FC9D1F6585990D4D7618FB56A7C41DC3A93904 -62062BFC4F4CA5C7568F042832B27777BDB79522BB7BA3BB4F9644D35416ADC9 -AE7EED026C970F8A8AA2B33C01B7660BFD83B550367CACA4D9B266E2756C3A7E6C3BD0D2EEA3F6ED7C9A4023C575750A -A04D0A134EF9B67BAC02ABDD2E355422C96665D99484DF4E4EFD2CE4AA66F4BB -D7209CB12AFC47261B223C8EC48BDA3131C006FA997396F050FCBEEEFAC7B65A -2E58BF2F918C44F01DD3DFBB49C5FABB33FFFF25B6AC975CA1819A7554093C25 -96C5A04C551604AA8069E213A7FFCE7EA8D8BCF9B61F93BA4E912630AF1BA886 -9219823986AE58A5FD919E809478AF894A73A413F7C12D8B29DF8BD3ADE730652FB7EB3115CF2914F748AE64F996A4B3 +IV: AB 0B A1 23 45 FE E7 06 66 73 21 67 97 12 3D 61 +CA9DC3E30069ED9C257FCA6747136F617F4E390F2B8BDDFDEBEC8A398A6A0C1E +78F18401572E33117850EA83D00C2F92376E88D7CAC0BF7CBA7037BF6755F43C +909055FF43224057BCE5F965174AF46586EB7CA4CAE1B3EB8B66EA569047948A +AB9B7D338457774713147BF666A5996926B90146CB698AC2F4DE63ADE89D84BB +ED796AED9BB3185ACD94779F7CC42665D4A3B04419E4272B77DA8D94B5CF84921889CEB110AB55D7267720A7C5B290EF +88E0CECA92549C73981F95999FA8F03B1B2EB98774134752556D7D7EFA802757 +C42CABAD74010E35726659C8E4AF4888282FBEA9703616B3403DA7C3DCA8A8ED +6F44BC56AC2E9883A2469C1909D171A8C58A0CFE4B506CC562EB2F08A484AE1B +65DEBEBC629FA3CE72B5028E1E385DFFEE0A9FE227FC5F6DD4368C0CB1886A7D +EA9BDC762FCBAAA11A4BE677AE344993990153C9E7A4A89F8271F49765FC72EF +8FAE9AC3033E637703626956F91791DAE4B3BEA9C82C065C91A314DDB647F8FE661750526E58C613000260675C2B520C +3D853DEC62375B3201B1C2269E31794A3C29958B191953A331D39675CCB53C002EF1491B63C49E629AF5D747CC52BD11 +61A02BB85B08AA8047EAD9FB80D489AB15CBE0302C660891C4B29D2621C80DBB +5230A9651D1A0910695593E1A5F6EA6EB21990D6465E52B325CF141C9E0C9172 +C9348D18DC019C3E364F7AD9CD5B6D77EE2D6486CFBDFAFE7042AB917E8FFE7C +DED385BBAC8FAB5918DBDBDC8622850048A540963AF35C3DD772926927B148C2 +E1EC13990BDA8E22F2848F97069462FB46840FEA688C52EDE930CD22C4E6F445BF317A96C4A6C2DC4295B2E3E86053B9 +D5453884C337587A13117F35219C7B4356E8E63EBC4C197CC1633D444D0A6AD0 +72C3E291DA11AD3D195C6A1B65849B0C91B7D18762B515A5728389356C42B62C +0E9EA0D97053752977D83A019A2F0393D326407AE507F5EE6E650082DBC683F81BDD71B79BE81EEB3139815377577346 +A32FF38450B3121CB01CE06AA369DC7B883CD9B1695CBADBC9609F009D6BFF7B7518D9DD690D214A1DB0D1A0C6F9FC3F +98848EFBB09D2A3EA59EE91B1B510BA3775E36B14500DE1238317AFC9872358F +E8B2785366399F84EADF07B0E299603BB885780E6ECA883508FFB7664C6473FE +1F6CEA6696B2E07FEB256506609D7E11D9F09F18B9EE43DE9BC42014ABF5213F +F2FF5045A5E90AAF92C2ECCB9FEFFBDE400A7E3E6B09CF43608896F7BC91736F +73CC30A78808BB2B3F7F398D88C79470AF86B825DE0C2FF31442D351C2826D9C +B68FD5017BA4809AD22DF64805DCE329A81C2CE3F7BE87FADFBD02211AB02321 +57BC2E14A724D6E2F4B0FD9401C3E6E5117D338077958648A558E40C553C787F +882A41BC36393F06C57ED71E66D003E24B5DAE86F90D8AEDD89A2DFED6719BF1 +95EDC3C3EB639AC66656B58D8F71A5B1B329002C4CCF7666C41C717A939C0979 +494A32528A68F5B4DF45385CC7FB470224F25D8AC9C81AB0DBD291AA4764BB17 +9A6D21675317433CE6EE860C9A2713265E1DA5E8F4024690252971EA5C2A566A +2B8379BCDDD0E6F73B1AD2D5A4D69D34D0013E98C87AD2BCE7AEED80F3BF4F69 +6E5D67B8B825943F9B9979D5E1EA9348B1DD40A5DA39B20FA96B78CAD3E03747 +27559A18DD6D52FFED8427376113C1A35840D64A53466071E1B769A28F161A99 +A2F38E38C253947816B5E629AAC02BC77EF7B56CC95FBF291C05466C56E01E47FE92053C900C0F6F98B11D7873BB9AFC +8A7E57E1228F75F78D51C13FE79C269E43F007E55F5B87741BCADDAAA6402DF7 +E088817700DBC7D778427464368D7771E3C20194CE60D08668578CAA527258B8 +3E5AD04DE23578A3BBC5FB91608435EBA1FF1465EDCE3E064F60A2EED35C9015 +647C9BFB61C0509D152A7B6B5C548DC558052F862314B42F4D1D8B98F6BF2412 +3D659FF6999401CAB590681036C3FDABAF157C774928E0D7D76FAC08AA6CFE93 +342362E28923E64DC5047E25E5A2F3FC8A6EB63554793CB8A1C99FFE632A370508CA208CA912470DB343A1636C751B9E +3B71D04AB09DFB44015F5553B4B76C9419C4D615F60184BA0B6A5687E47D66BA +14CF7621A4943DE2156AB8FDE8A9E74D26776D8362D9364387626488CB3DA5DC +2F9205BF8B310C33E38F571FFBDF6FC4BA5135457A2CF6CA9CD319F3EDF4BF6E +785EDF05A2111B8E4A126BE274C9BD8D6C0482F4A2B716FFAE93EDB8D1634F41 +4B26880D1AE8EC1D285296F473EB5A805CF1C1EA47B899A6A3F8E9EFDB2CBCA3 +A002B3ED0D1FCBC02298BFE7F18207CD58AB21D358F20855D067939EF50DCC08 +BE82806DE526A6453C6FA309DAE0B52D67A98A194753DD4CC2C8C196A47B253F60149FAF49D0396E1F24CB1EDF1430DA +031686817FB37936FD0313B9358FD35BAF5DEB924F7A939C4B843DD095F11806 +3A7B7A7AE8723C2A060FF368AB048A48737D4EEAD3C97BF98BC9E8CAE552431B +357C4A1A41F43100208863F2E607AE14CC55235D757CDE5C491BE405BB72BDB4 +0C46E442B9AC3C479C18D4D94AB3E5124D4033AFD05AE00AC6881DD62F11E07F +8705CF1D9B202056CEBD98FF25CB0B6BF40175DBDC2FE86FE2A7D2AC796F818EA71A8C1312E9C7FCE6CC3D11FBBA98E4 diff --git a/assets/resources/subghz/assets/setting_user.pocsag b/assets/resources/subghz/assets/setting_user.example similarity index 79% rename from assets/resources/subghz/assets/setting_user.pocsag rename to assets/resources/subghz/assets/setting_user.example index 3c6b3b3a0..e0c474a99 100644 --- a/assets/resources/subghz/assets/setting_user.pocsag +++ b/assets/resources/subghz/assets/setting_user.example @@ -1,7 +1,6 @@ # to use manual settings and prevent them from being deleted on upgrade, rename *_user.example files to *_user Filetype: Flipper SubGhz Setting File Version: 1 - # Add Standard frequencies for your region #Add_standard_frequencies: true @@ -9,8 +8,6 @@ Version: 1 #Default_frequency: 433920000 # Frequencies used for "Read", "Read Raw" and "Frequency Analyzer" -# DAPNET frequency in most countries -Frequency: 439987500 #Frequency: 300000000 #Frequency: 310000000 #Frequency: 320000000 @@ -23,11 +20,6 @@ Frequency: 439987500 # Custom preset # format for CC1101 "Custom_preset_data:" XX YY XX YY .. 00 00 ZZ ZZ ZZ ZZ ZZ ZZ ZZ ZZ, where: XX-register, YY - register data, 00 00 - end load register, ZZ - 8 byte Pa table register -# FM with 9.5KHz frequency deviation - POCSAG settings -Custom_preset_name: FM95 -Custom_preset_module: CC1101 -Custom_preset_data: 02 0D 0B 06 08 32 07 04 14 00 13 02 12 04 11 83 10 67 15 24 18 18 19 16 1D 91 1C 00 1B 07 20 FB 22 10 21 56 00 00 C0 00 00 00 00 00 00 00 - #Custom_preset_name: AM_1 #Custom_preset_module: CC1101 #Custom_preset_data: 02 0D 03 07 08 32 0B 06 14 00 13 00 12 30 11 32 10 17 18 18 19 18 1D 91 1C 00 1B 07 20 FB 22 11 21 B6 00 00 00 C0 00 00 00 00 00 00 diff --git a/assets/resources/subghz/assets/setting_user.txt b/assets/resources/subghz/assets/setting_user.txt deleted file mode 100644 index 67e89dceb..000000000 --- a/assets/resources/subghz/assets/setting_user.txt +++ /dev/null @@ -1,81 +0,0 @@ -# to use manual settings and prevent them from being deleted on upgrade, rename *_user.example files to *_user -Filetype: Flipper SubGhz Setting File -Version: 1 - -# Add Standard frequencies for your region -Add_standard_frequencies: true - -# Default Frequency: used as default for "Read" and "Read Raw" -Default_frequency: 433920000 - -# Frequencies used for "Read", "Read Raw" and "Frequency Analyzer" -Frequency: 300000000 -Frequency: 302757000 -Frequency: 303875000 -Frequency: 304250000 -Frequency: 307000000 -Frequency: 307500000 -Frequency: 307800000 -Frequency: 309000000 -Frequency: 310000000 -Frequency: 312000000 -Frequency: 312100000 -Frequency: 312200000 -Frequency: 313000000 -Frequency: 313850000 -Frequency: 314000000 -Frequency: 314350000 -Frequency: 314980000 -Frequency: 315000000 -Frequency: 318000000 -Frequency: 330000000 -Frequency: 345000000 -Frequency: 348000000 -Frequency: 387000000 -Frequency: 390000000 -Frequency: 418000000 -Frequency: 433075000 -Frequency: 433220000 -Frequency: 433420000 -Frequency: 433657070 -Frequency: 433889000 -Frequency: 433920000 -Frequency: 434075000 -Frequency: 434176948 -Frequency: 434190000 -Frequency: 434390000 -Frequency: 434420000 -Frequency: 434620000 -Frequency: 434775000 -Frequency: 438900000 -Frequency: 440175000 -Frequency: 446000000 -Frequency: 464000000 -Frequency: 467750000 -Frequency: 779000000 -Frequency: 868350000 -Frequency: 868400000 -Frequency: 868800000 -Frequency: 868950000 -Frequency: 906400000 -Frequency: 915000000 -Frequency: 925000000 -Frequency: 928000000 - -# Frequencies used for hopping mode (keep this list small or flipper will miss signal) -Hopper_frequency: 310000000 -Hopper_frequency: 313000000 -Hopper_frequency: 315000000 -Hopper_frequency: 390000000 -Hopper_frequency: 433920000 -Hopper_frequency: 434420000 -Hopper_frequency: 868350000 - -# Custom preset examples -# format for CC1101 "Custom_preset_data:" XX YY XX YY .. 00 00 ZZ ZZ ZZ ZZ ZZ ZZ ZZ ZZ, where: XX-register, YY - register data, 00 00 - end load register, ZZ - 8 byte Pa table register -#Custom_preset_name: AM_1 -#Custom_preset_module: CC1101 -#Custom_preset_data: 02 0D 03 07 08 32 0B 06 14 00 13 00 12 30 11 32 10 17 18 18 19 18 1D 91 1C 00 1B 07 20 FB 22 11 21 B6 00 00 00 C0 00 00 00 00 00 00 -#Custom_preset_name: AM_2 -#Custom_preset_module: CC1101 -#Custom_preset_data: 02 0D 03 07 08 32 0B 06 14 00 13 00 12 30 11 32 10 17 18 18 19 18 1D 91 1C 00 1B 07 20 FB 22 11 21 B6 00 00 00 C0 00 00 00 00 00 00 diff --git a/assets/resources/subghz/playlist/example_playlist.txt b/assets/resources/subghz/playlist/example_playlist.txt new file mode 100644 index 000000000..efa883cb0 --- /dev/null +++ b/assets/resources/subghz/playlist/example_playlist.txt @@ -0,0 +1,5 @@ +# Example file, it will not work, you need to add paths to your files! +sub: /ext/subghz/Vehicles/Tesla/Tesla_charge_AM270.sub +sub: /ext/subghz/Vehicles/Tesla/Tesla_charge_AM650.sub +sub: /ext/subghz/Test1.sub +sub: /ext/subghz/Test2.sub \ No newline at end of file diff --git a/assets/resources/subghz/unirf/CVS_Walgreens.txt b/assets/resources/subghz/remote/CVS_Walgreens.txt similarity index 88% rename from assets/resources/subghz/unirf/CVS_Walgreens.txt rename to assets/resources/subghz/remote/CVS_Walgreens.txt index dc14502d2..a84a95df1 100644 --- a/assets/resources/subghz/unirf/CVS_Walgreens.txt +++ b/assets/resources/subghz/remote/CVS_Walgreens.txt @@ -1,5 +1,3 @@ -Filetype: Flipper SubGhz RAW File -Version: 1 UP: /any/subghz/Stores/Walgreens/Walgreens_Cough.sub DOWN: /any/subghz/Stores/CVS/Dental_Care.sub LEFT: /any/subghz/Stores/Walgreens/Walgreens_Skincare.sub diff --git a/assets/resources/subghz/unirf/Gas_Sign_Edit.txt b/assets/resources/subghz/remote/Gas_Sign_Edit.txt similarity index 94% rename from assets/resources/subghz/unirf/Gas_Sign_Edit.txt rename to assets/resources/subghz/remote/Gas_Sign_Edit.txt index 3988d7bbd..f1308d3b7 100644 --- a/assets/resources/subghz/unirf/Gas_Sign_Edit.txt +++ b/assets/resources/subghz/remote/Gas_Sign_Edit.txt @@ -7,4 +7,4 @@ ULABEL: Up DLABEL: Down LLABEL: Left RLABEL: Right -OKLABEL: Set \ No newline at end of file +OKLABEL: Set diff --git a/assets/resources/subghz/unirf/Ridin_Dirty.txt b/assets/resources/subghz/remote/Ridin_Dirty.txt similarity index 100% rename from assets/resources/subghz/unirf/Ridin_Dirty.txt rename to assets/resources/subghz/remote/Ridin_Dirty.txt diff --git a/assets/resources/subghz/unirf/Tesla_Handicap.txt b/assets/resources/subghz/remote/Tesla_Handicap.txt similarity index 83% rename from assets/resources/subghz/unirf/Tesla_Handicap.txt rename to assets/resources/subghz/remote/Tesla_Handicap.txt index 7f8f30cbe..8c7f3ee17 100644 --- a/assets/resources/subghz/unirf/Tesla_Handicap.txt +++ b/assets/resources/subghz/remote/Tesla_Handicap.txt @@ -1,5 +1,3 @@ -Filetype: Flipper SubGhz RAW File -Version: 1 UP: /any/subghz/Vehicles/Tesla/Tesla_charge_AM270.sub DOWN: /any/subghz/Vehicles/Tesla/Tesla_charge_AM650.sub LEFT: /any/subghz/Handicap/handicap_push_door.sub @@ -9,4 +7,4 @@ ULABEL: Tesla AM270 DLABEL: Tesla AM650 LLABEL: Handicap Door RLABEL: Handicap 1270 -OKLABEL: Handicap 1650 \ No newline at end of file +OKLABEL: Handicap 1650 diff --git a/assets/resources/subghz/remote/example_remote.txt b/assets/resources/subghz/remote/example_remote.txt new file mode 100644 index 000000000..0ccfa414a --- /dev/null +++ b/assets/resources/subghz/remote/example_remote.txt @@ -0,0 +1,10 @@ +UP: /ext/subghz/Up.sub +DOWN: /ext/subghz/Down.sub +LEFT: /ext/subghz/Left.sub +RIGHT: /ext/subghz/Right.sub +OK: /ext/subghz/Ok.sub +ULABEL: Up Label +DLABEL: Down Label +LLABEL: Left Label +RLABEL: Right Label +OKLABEL: Ok Label diff --git a/assets/resources/swd_scripts/100us.swd b/assets/resources/swd_scripts/100us.swd new file mode 100644 index 000000000..3ad89a0ab --- /dev/null +++ b/assets/resources/swd_scripts/100us.swd @@ -0,0 +1 @@ +swd_clock_delay 100 diff --git a/assets/resources/swd_scripts/call_test_1.swd b/assets/resources/swd_scripts/call_test_1.swd new file mode 100644 index 000000000..03f5575f4 --- /dev/null +++ b/assets/resources/swd_scripts/call_test_1.swd @@ -0,0 +1,6 @@ + +message 0 "gonna call call_test_2" dialog + +call call_test_2 + +message 0 "back now" dialog diff --git a/assets/resources/swd_scripts/call_test_2.swd b/assets/resources/swd_scripts/call_test_2.swd new file mode 100644 index 000000000..f358b6ece --- /dev/null +++ b/assets/resources/swd_scripts/call_test_2.swd @@ -0,0 +1,7 @@ + +# first do a beeeeeep +beep 1 + +message 0 "Seems to work" dialog + +beep 0 diff --git a/assets/resources/swd_scripts/dump_0x00000000_1k.swd b/assets/resources/swd_scripts/dump_0x00000000_1k.swd new file mode 100644 index 000000000..a8870fe30 --- /dev/null +++ b/assets/resources/swd_scripts/dump_0x00000000_1k.swd @@ -0,0 +1,6 @@ +ap_select 0 +max_tries 50 +block_size 4 +mem_dump /ext/swd_scripts/flash.bin 0x00000000 0x100000 2 +beep 1 +message 5 "Reading sucessful" diff --git a/assets/resources/swd_scripts/dump_0x00000000_4b.swd b/assets/resources/swd_scripts/dump_0x00000000_4b.swd new file mode 100644 index 000000000..a8870fe30 --- /dev/null +++ b/assets/resources/swd_scripts/dump_0x00000000_4b.swd @@ -0,0 +1,6 @@ +ap_select 0 +max_tries 50 +block_size 4 +mem_dump /ext/swd_scripts/flash.bin 0x00000000 0x100000 2 +beep 1 +message 5 "Reading sucessful" diff --git a/assets/resources/swd_scripts/dump_STM32.swd b/assets/resources/swd_scripts/dump_STM32.swd new file mode 100644 index 000000000..e675537c9 --- /dev/null +++ b/assets/resources/swd_scripts/dump_STM32.swd @@ -0,0 +1,6 @@ +ap_select 0 +max_tries 50 +block_size 1024 +mem_dump /ext/swd_scripts/flash.bin 0x08000000 0x100000 2 +beep 1 +message 0 "Reading finished" dialog diff --git a/assets/resources/swd_scripts/goto_test.swd b/assets/resources/swd_scripts/goto_test.swd new file mode 100644 index 000000000..680285653 --- /dev/null +++ b/assets/resources/swd_scripts/goto_test.swd @@ -0,0 +1,7 @@ +beep 1 +goto l2 +.label l1 +beep 0 +.label l2 +beep 1 +goto l1 diff --git a/assets/resources/swd_scripts/halt.swd b/assets/resources/swd_scripts/halt.swd new file mode 100644 index 000000000..6aad4c194 --- /dev/null +++ b/assets/resources/swd_scripts/halt.swd @@ -0,0 +1,11 @@ + +# make sure errors do not cause a script abort +errors ignore + +message 0 "HAMMER TIME! Trying to halt CPU" +ap_select 0 + +# loop writing the halt bits +.label l1 +mem_write 0xE000EDF0 0xA05F0003 +goto l1 diff --git a/assets/resources/swd_scripts/reset.swd b/assets/resources/swd_scripts/reset.swd new file mode 100644 index 000000000..1872757fb --- /dev/null +++ b/assets/resources/swd_scripts/reset.swd @@ -0,0 +1,8 @@ +errors ignore +status 0 +message 0 "HAMMER TIME! Try to halt the CPU" +.label l1 +ap_select 0 +mem_write 0xE000EDF0 0xA05F0001 +mem_write 0xE000ED0C 0x05FA0004 +goto l1 diff --git a/assets/resources/swd_scripts/test_write.swd b/assets/resources/swd_scripts/test_write.swd new file mode 100644 index 000000000..df69461fd --- /dev/null +++ b/assets/resources/swd_scripts/test_write.swd @@ -0,0 +1,3 @@ +mem_write 0x20002000 0xdeadbeef +mem_write 0xE000EDF0 0xA05F0001 +mem_write 0xE000EDF0 0xA05F0007 diff --git a/assets/unit_tests/subghz/pocsag.sub b/assets/unit_tests/subghz/pocsag.sub deleted file mode 100644 index 52f2ee0c3..000000000 --- a/assets/unit_tests/subghz/pocsag.sub +++ /dev/null @@ -1,35 +0,0 @@ -Filetype: Flipper SubGhz RAW File -Version: 1 -Frequency: 439988500 -Preset: FuriHalSubGhzPresetCustom -Custom_preset_module: CC1101 -Custom_preset_data: 02 0D 0B 06 08 32 07 04 14 00 13 02 12 04 11 83 10 67 15 31 18 18 19 16 1D 91 1C 00 1B 07 20 FB 22 10 21 56 00 00 C0 00 00 00 00 00 00 00 -Protocol: RAW -RAW_Data: -3490 143 -230 115 -1140 55 -2348 79 -3316 287 -228 95 -344 561 -286 105 -1026 137 -1108 81 -1090 71 -2211 57 -1010 55 -1276 113 -346 85 -706 169 -628 57 -2928 129 -2574 109 -1400 57 -1444 87 -824 95 -240 53 -3650 55 -202 143 -528 229 -216 57 -144 113 -2682 55 -1422 57 -1619 57 -1332 111 -402 57 -968 57 -500 55 -3277 71 -856 79 -1009 77 -2136 53 -550 71 -986 85 -1403 55 -244 53 -763 85 -2413 173 -1410 51 -114 85 -594 77 -584 191 -1102 95 -7897 125 -1584 77 -442 73 -384 95 -1774 55 -2588 69 -264 55 -3624 95 -6776 51 -102 99 -344 143 -716 55 -174 57 -1440 143 -312 97 -312 143 -1891 85 -194 99 -1696 55 -1188 95 -192 69 -102 75 -2378 95 -646 119 -828 85 -1766 163 -1440 55 -82 111 -56 55 -567 83 -3209 81 -1140 115 -5439 105 -82 113 -1308 55 -6938 71 -122 73 -520 79 -490 73 -922 81 -1250 229 -374 83 -614 57 -458 81 -1300 131 -668 139 -116 55 -414 55 -2304 51 -2456 85 -922 109 -490 141 -58 55 -376 55 -998 191 -1294 57 -1693 159 -678 55 -416 57 -344 141 -4490 75 -122 167 -1774 143 -1152 101 -3062 57 -1351 85 -402 111 -814 83 -1321 97 -406 71 -384 121 -670 55 -1892 221 -2230 51 -1713 143 -1525 55 -340 107 -1332 57 -2671 55 -576 85 -202 57 -338 115 -768 79 -1112 71 -1070 51 -538 113 -488 109 -162 77 -980 57 -1915 87 -2141 95 -2272 171 -5042 53 -3974 55 -902 85 -274 81 -160 55 -58 57 -448 77 -1394 51 -316 119 -2322 115 -1996 51 -3265 85 -634 57 -2308 51 -250 137 -8089 57 -3623 77 -268 282 -782 225 -506 77 -280 57 -468 107 -2886 55 -1378 135 -760 75 -3226 53 -154 51 -86 143 -1810 141 -943 111 -503 113 -1226 83 -144 57 -876 253 -292 77 -832 113 -316 143 -580 57 -1121 81 -2010 201 -326 186 -508 167 -1156 167 -644 85 -895 53 -1525 71 -2115 55 -844 95 -168 167 -406 97 -168 119 -266 165 -2264 57 -4167 115 -2244 53 -2009 115 -3916 143 -598 143 -1500 243 -1302 197 -1161 75 -359 153 -1117 71 -1622 159 -222 71 -70 119 -2112 163 -2066 107 -1291 113 -5859 57 -3327 71 -688 197 -3065 171 -344 171 -200 143 -1398 55 -1143 77 -214 81 -2840 143 -2109 199 -1229 99 -1354 190 -1710 73 -254 81 -874 85 -54 135 -220 115 -2313 81 -964 159 -1802 131 -284 53 -786 71 -482 135 -114 115 -814 53 -3491 51 -378 143 -2783 113 -572 85 -288 105 -1975 53 -1412 223 -144 71 -616 83 -2279 57 -456 83 -1340 51 -1635 127 -472 57 -1694 57 -1264 55 -966 53 -1601 71 -2584 57 -RAW_Data: -1052 225 -162 83 -2002 57 -2246 75 -2092 119 -2672 101 -897 113 -116 85 -1560 75 -2231 51 -896 73 -146 95 -1028 107 -1309 101 -1154 103 -1168 201 -114 57 -1050 55 -742 81 -1238 113 -1196 53 -700 75 -5446 79 -1340 51 -106 53 -160 71 -144 97 -3165 69 -192 85 -1526 69 -1010 71 -516 143 -2207 57 -1529 85 -1710 57 -288 57 -514 57 -1056 147 -156 143 -114 77 -828 57 -676 95 -1076 71 -1620 53 -532 101 -2202 129 -2282 143 -432 85 -836 85 -3336 51 -220 71 -250 79 -936 81 -270 111 -1981 71 -1910 251 -392 139 -58 57 -687 151 -764 113 -1012 79 -258 85 -144 85 -995 83 -2472 55 -508 57 -2619 171 -334 51 -604 53 -3071 71 -200 51 -128 199 -460 113 -556 83 -2625 51 -1290 51 -438 95 -144 119 -262 73 -122 71 -1368 75 -634 253 -1314 95 -2198 53 -844 85 -288 114 -112 109 -86 143 -878 141 -1058 57 -86 57 -826 85 -172 171 -490 57 -2034 155 -430 103 -334 87 -344 53 -1494 169 -276 57 -340 51 -3721 77 -172 51 -1163 85 -644 75 -2714 99 -290 147 -150 51 -1722 55 -644 85 -2486 167 -558 85 -586 57 -2266 171 -852 53 -672 79 -1480 95 -2830 237 -268 111 -576 95 -240 73 -256 101 -200 93 -612 53 -2921 57 -608 51 -82 57 -894 71 -270 53 -1584 75 -868 85 -607 53 -874 151 -1701 55 -1616 85 -2660 57 -2182 71 -390 51 -1392 169 -1503 85 -144 55 -656 51 -156 97 -194 117 -781 71 -292 51 -1036 75 -973 55 -2634 81 -106 103 -1802 129 -406 71 -964 53 -1933 79 -1157 55 -174 57 -1697 75 -204 119 -5124 75 -556 57 -174 113 -1098 229 -116 57 -102 97 -378 57 -144 57 -562 107 -682 55 -1079 121 -1050 51 -560 201 -138 55 -84 113 -2505 71 -144 69 -1764 121 -312 97 -1140 111 -1344 107 -1406 71 -405 51 -1736 55 -1672 103 -100 38491 -672 989 -704 937 -730 935 -756 911 -754 885 -780 911 -754 883 -780 885 -808 857 -808 859 -806 861 -808 859 -806 859 -784 885 -782 883 -808 857 -806 859 -806 859 -806 861 -806 857 -808 857 -808 859 -806 857 -808 857 -808 857 -808 861 -808 859 -806 861 -806 859 -808 859 -806 857 -808 857 -806 859 -808 859 -806 833 -832 859 -808 833 -832 835 -832 833 -832 859 -808 831 -836 831 -834 833 -832 831 -832 833 -832 835 -836 831 -834 833 -832 857 -808 833 -832 833 -832 835 -832 835 -832 831 -832 857 -810 831 -832 859 -808 857 -808 835 -836 833 -832 831 -832 831 -834 833 -832 859 -808 835 -836 831 -832 833 -832 831 -834 833 -832 831 -834 857 -808 859 -RAW_Data: -808 831 -832 833 -832 859 -808 857 -810 831 -834 859 -806 833 -834 857 -806 833 -832 859 -806 859 -808 859 -810 833 -832 831 -834 859 -806 833 -834 857 -808 831 -834 833 -834 833 -832 833 -836 833 -834 829 -832 859 -806 859 -808 833 -832 859 -808 831 -834 857 -806 835 -832 833 -834 859 -806 857 -810 833 -832 857 -808 833 -834 831 -834 833 -834 831 -832 833 -834 857 -808 857 -808 833 -834 835 -834 833 -832 831 -834 857 -806 833 -832 835 -832 833 -834 833 -832 835 -834 831 -834 831 -834 831 -832 833 -832 833 -834 835 -834 831 -832 833 -832 833 -834 833 -832 833 -832 831 -834 857 -808 859 -808 831 -834 831 -834 859 -808 831 -834 857 -808 859 -806 859 -808 857 -810 857 -808 857 -808 835 -834 835 -832 831 -832 835 -832 835 -834 833 -832 831 -830 833 -832 857 -808 859 -808 833 -832 833 -832 833 -834 831 -834 859 -808 831 -834 833 -832 859 -810 833 -832 831 -832 859 -806 859 -808 833 -832 833 -834 833 -832 833 -832 833 -832 857 -808 859 -808 833 -832 857 -808 859 -810 857 -808 831 -832 835 -834 831 -834 833 -832 833 -832 833 -834 857 -808 859 -806 859 -808 835 -834 831 -836 833 -834 831 -834 831 -832 831 -832 831 -834 835 -832 857 -806 859 -806 835 -834 857 -808 857 -808 857 -808 859 -806 859 -808 835 -832 831 -832 859 -808 833 -832 833 -834 833 -832 835 -834 833 -832 833 -834 831 -832 833 -834 831 -832 835 -832 835 -832 831 -832 857 -808 859 -808 859 -806 859 -808 835 -832 833 -832 831 -834 857 -808 833 -832 833 -834 833 -832 833 -832 859 -808 831 -834 859 -806 835 -834 835 -832 831 -836 831 -832 831 -832 859 -806 835 -832 859 -808 831 -834 831 -834 857 -808 833 -832 835 -834 831 -832 859 -808 831 -834 859 -808 833 -832 857 -808 857 -810 833 -834 833 -832 831 -832 859 -808 831 -834 833 -832 859 -808 857 -808 833 -836 831 -834 831 -832 833 -832 859 -808 831 -832 837 -834 833 -832 833 -834 831 -832 831 -834 857 -808 833 -832 859 -808 831 -834 859 -806 833 -834 833 -836 831 -832 833 -832 833 -834 859 -808 831 -832 831 -834 859 -806 859 -808 857 -808 859 -806 833 -834 857 -808 859 -806 835 -832 859 -808 857 -808 857 -808 857 -808 857 -808 859 -808 859 -812 831 -832 831 -832 833 -834 833 -832 857 -808 833 -836 831 -832 857 -808 835 -836 833 -832 831 -832 831 -836 835 -832 831 -834 831 -836 831 -832 857 -808 833 -832 835 -834 831 -832 833 -832 835 -832 1665 -4168 1665 -1668 831 -RAW_Data: -834 1669 -832 3333 -832 833 -832 831 -2500 833 -1666 3347 -3332 829 -830 831 -830 2501 -834 1665 -2502 4171 -1664 1667 -830 831 -2498 831 -3332 831 -836 835 -832 2499 -830 1671 -2510 4155 -1664 1687 -802 853 -2498 829 -3318 857 -828 829 -830 2503 -834 1667 -2502 4163 -1664 1665 -832 829 -2500 857 -3308 831 -836 835 -832 2497 -834 1667 -2504 4171 -1664 1663 -830 833 -2502 13329 -826 851 -3328 825 -826 853 -828 833 -2500 1669 -830 2503 -832 829 -2500 829 -830 1665 -1668 1665 -1668 829 -832 833 -4160 4175 -1668 1665 -1666 853 -4970 853 -1664 5847 -1634 2521 -828 1663 -832 831 -3334 829 -832 831 -836 2499 -832 1671 -2484 4193 -1638 1689 -832 829 -2500 829 -3338 829 -832 833 -832 2499 -832 1669 -2502 4173 -1664 1663 -832 829 -2508 833 -3332 831 -832 831 -832 2505 -832 1665 -2502 4163 -1664 1669 -830 831 -2502 833 -3340 827 -832 829 -834 2503 -832 1665 -2500 4177 -1662 1665 -830 831 -2482 857 -3312 857 -830 829 -830 2501 -834 1663 -2504 4173 -1638 1687 -832 831 -2502 829 -3332 833 -830 831 -832 2505 -832 1663 -2508 4167 -1664 1669 -832 833 -2502 831 -3334 829 -830 829 -832 2501 -832 1667 -2504 4169 -1670 1663 -832 831 -2500 831 -3332 833 -832 831 -832 2497 -834 1663 -2500 4175 -1664 1665 -832 831 -2508 829 -3338 831 -832 831 -830 2501 -832 1665 -2502 4165 -1666 1667 -832 831 -780 53 -1368 57 -86 143 -200 57 -374 57 -130 165 -374 85 -808 85 -196 127 -1010 51 -138 85 -58 77 -174 85 -228 113 -716 95 -520 71 -2831 53 -618 53 -188 83 -82 111 -114 55 -5464 71 -966 95 -1168 71 -824 71 -480 97 -72 101 -612 55 -3330 73 -1995 85 -832 53 -1016 57 -1462 71 -256 75 -776 143 -2066 77 -1748 71 -4832 81 -602 55 -3866 131 -1824 57 -1836 113 -1234 85 -836 73 -560 253 -1606 143 -286 351 -1008 87 -528 107 -1357 256 -1538 233 -1018 71 -264 77 -304 71 -1832 57 -144 85 -858 83 -511 131 -1220 107 -1318 85 -1782 81 -72 73 -7838 51 -696 55 -3805 125 -930 103 -292 95 -382 115 -3256 85 -859 57 -787 220 -1207 143 -2068 143 -2322 97 -548 53 -386 57 -130 51 -1673 57 -3004 71 -783 71 -1560 99 -102 51 -1068 81 -348 83 -2003 51 -2860 77 -2680 57 -4432 81 -936 95 -400 73 -583 71 -2842 97 -1656 81 -146 73 -286 105 -714 141 -2236 53 -1503 81 -200 85 -836 71 -356 109 -1310 75 -616 57 -690 87 -566 79 -727 73 -1604 97 -1438 57 -2641 77 -1650 217 -873 71 -890 109 -2042 163 -3562 107 -3098 111 -904 79 -238 51 -2699 71 -736 53 -488 233 -400 103 -340 81 -2933 53 -1508 51 -1886 53 -388 125 -3336 143 -RAW_Data: -390 99 -1386 71 -1092 97 -1150 51 -506 111 -2575 87 -114 143 -662 57 -644 53 -550 119 -274 85 -1267 55 -1278 71 -1622 117 -2082 77 -1316 73 -1882 55 -1575 71 -424 85 -1031 133 -2367 85 -542 115 -166 113 -2671 149 -256 113 -260 83 -1966 141 -430 71 -1844 127 -4269 57 -840 111 -1598 85 -3530 55 -2263 57 -1302 57 -346 85 -516 83 -2989 51 -334 83 -1811 85 -230 57 -836 55 -678 55 -7081 55 -288 115 -2062 55 -300 189 -738 57 -814 119 -1438 53 -2077 115 -631 145 -206 57 -645 79 -4121 115 -490 53 -186 71 -144 69 -2537 105 -932 53 -1136 55 -3124 57 -1873 125 -4551 77 -3337 57 -1122 57 -622 103 -2437 51 -120 71 -3629 129 -2139 77 -104 99 -1057 95 -416 71 -72 95 -312 95 -1052 115 -342 111 -2452 71 -746 221 -1418 57 -3125 119 -724 55 -950 57 -618 57 -259 107 -210 95 -1046 71 -454 71 -1651 71 -442 71 -312 79 -1169 55 -202 85 -632 57 -144 115 -412 69 -304 85 -2125 53 -998 125 -200 105 -1126 115 -1116 71 -604 85 -174 171 -260 171 -308 71 -120 95 -354 53 -1461 69 -863 121 -1696 55 -3336 79 -2306 83 -754 97 -160 171 -1612 139 -417 79 -2792 73 -498 85 -2112 57 -1370 57 -460 85 -482 55 -1946 113 -682 101 -136 85 -574 77 -838 53 -657 71 -3562 71 -588 97 -252 71 -366 57 -658 85 -1378 53 -168 85 -2896 167 -1412 87 -1684 53 -2008 97 -1861 83 -1251 71 -678 95 -192 71 -930 81 -1380 51 -572 81 -2795 105 -122 95 -1038 85 -1116 117 -1799 129 -356 83 -712 133 -1584 119 -1892 81 -672 143 -116 85 -996 57 -501 57 -450 85 -216 57 -2182 57 -1100 73 -456 85 -260 85 -144 167 -300 57 -202 55 -448 81 -764 143 -1707 79 -3204 53 -108 53 -54 190 -682 87 -1070 53 -1230 55 -112 87 -1014 141 -1659 57 -230 115 -230 57 -750 55 -686 57 -216 87 -4642 51 -4416 95 -2575 111 -1607 53 -368 107 -4757 51 -521 75 -1682 255 -2346 115 -1762 83 -3457 71 -1470 77 -246 157 -1678 55 -418 99 -922 113 -218 123 -260 55 -1384 105 -240 53 -848 53 -1810 79 -648 77 -664 85 -298 109 -86 55 -552 55 -260 57 -913 115 -504 77 -537 53 -3159 105 -3371 51 -138 53 -2668 71 -368 51 -674 55 -280 51 -1048 51 -963 85 -1000 57 -954 113 -1190 53 -1456 57 -200 115 -172 57 -864 57 -288 85 -442 127 -72 145 -584 71 -2799 143 -324 95 -120 121 -1873 51 -658 57 -1285 57 -1516 330 -172 143 -316 87 -200 115 -700 197 -4960 57 -1358 53 -2324 55 -2864 179 -1799 73 -3669 57 -316 85 -RAW_Data: -935 57 -864 115 -740 79 -220 87 -400 81 -1238 242 -1720 169 -176 125 -2132 57 -1227 119 -2810 107 -420 95 -192 75 -74 107 -3191 171 -914 81 -2426 57 -860 157 -618 115 -1462 107 -146 51 -1007 115 -1080 71 -803 75 -1619 55 -1492 57 -1171 53 -1050 57 -538 113 -2006 85 -2537 83 -1490 55 -598 109 -670 139 -166 55 -154 71 -1215 85 -1630 167 -390 127 -840 115 -288 57 -614 97 -397 71 -494 55 -74 73 -74 71 -806 71 -78 247 -656 105 -1176 73 -144 95 -1100 191 -190 135 -114 87 -1290 119 -506 191 -860 115 -3636 141 -6062 83 -2120 55 -392 77 -2020 55 -1316 51 -424 183 -448 225 -474 97 -1068 75 -294 81 -1090 85 -196 57 -2221 113 -458 115 -3219 141 -2086 85 -374 87 -834 105 -470 51 -1338 53 -1401 85 -604 142 -170 109 -848 55 -200 53 -1480 77 -326 51 -9597 57 -341 107 -959 57 -272 51 -4922 75 -78 195 -249 53 -1368 53 -236 127 -3235 105 -174 113 -2854 55 -1140 85 -374 55 -2335 57 -1164 55 -314 55 -368 51 -2454 57 -5231 55 -1868 103 -868 93 -498 71 -2701 119 -851 85 -810 131 -1466 111 -714 57 -1588 113 -846 53 -2596 105 -200 87 -772 75 -128 75 -1739 191 -4925 79 -144 139 -208 71 -348 79 -538 131 -880 143 -1048 155 -1850 109 -170 57 -1014 133 -906 85 -774 57 -1400 103 -1881 53 -2970 147 -554 95 -178 107 -622 85 -674 113 -1442 55 -3090 127 -488 51 -164 85 -4087 113 -1427 77 -6221 57 -3815 81 -488 109 -992 57 -1423 71 -434 51 -2449 95 -542 57 -594 53 -1282 133 -5219 71 -1574 87 -1658 53 -198 85 -628 105 -120 129 -804 137 -5041 171 -1264 125 -1245 129 -168 97 -146 71 -1196 55 -1194 57 -58 137 -86 85 -1885 101 -328 51 -5524 143 -2082 85 -2325 85 -1225 109 -784 55 -200 55 -562 55 -1644 77 -598 85 -1495 51 -882 161 -2568 105 -RAW_Data: -1288 57 -2688 205 -698 57 -434 51 -632 225 -188 109 -1032 129 -636 285 -1055 77 -204 95 -240 79 -462 169 -2442 57 -202 85 -478 57 -1511 55 -114 113 -444 169 -216 79 -124 73 -1030 141 -418 143 -3631 55 -836 129 -506 79 -954 139 -1470 85 -536 139 -416 53 -944 111 -1544 73 -562 55 -3102 71 -2248 85 -836 57 -3044 77 -562 53 -52 75 -480 53 -1200 71 -216 95 -974 71 -2312 83 -1609 51 -848 55 -174 143 -826 107 -4127 57 -1667 57 -498 77 -1660 55 -751 149 -152 193 -1474 99 -608 115 -2312 57 -1586 85 -144 57 -3152 105 -1225 71 -1321 75 -1732 57 -506 189 -1177 71 -1548 87 -1396 109 -3225 55 -1102 223 -114 85 -2537 143 -58 55 -1511 53 -780 57 -2098 71 -96 71 -1180 95 -144 97 -2283 55 -3182 143 -238 55 -2374 57 -792 55 -2931 75 -2015 119 -860 51 -358 173 -1256 165 -3319 141 -316 53 -1390 161 -4297 141 -3288 81 -977 57 -4351 57 -2804 71 -170 141 -2466 167 -1854 57 -260 113 -3224 83 -342 85 -58 79 -486 85 -446 87 -446 55 -1745 55 -366 51 -82 167 -1927 157 -2892 85 -86 85 -1712 83 -4854 83 -1790 109 -104 163 -784 101 -604 119 -888 159 -3756 143 -936 53 -2312 53 -1625 99 -3039 71 -1648 103 -520 57 -1024 57 -1404 57 -690 135 -410 57 -1228 201 -1932 77 -130 51 -688 101 -346 51 -198 87 -568 113 -828 87 -674 53 -684 87 -230 113 -670 143 -250 283 -488 97 -470 103 -122 79 -224 103 -386 71 -1160 55 -276 109 -1046 145 -2978 73 -1230 75 -8593 55 -298 87 -1980 55 -1970 139 -974 143 -446 57 -338 77 -1094 95 -840 77 -1066 55 -485 171 -292 95 -1434 152 -116 113 -658 55 -144 57 -836 85 -144 57 -86 53 -448 75 -2054 95 -240 71 -1638 53 -1000 53 -196 195 -2026 71 -939 51 -930 71 -799 57 -820 51 -968 57 -704 53 -356 173 -3870 57 -368 217 -574 69 -192 103 -764 95 -2853 55 -2042 103 -218 53 -260 69 -1754 97 -356 301 -116 55 -2265 87 -1452 95 -118 71 -1226 143 -638 51 -1195 115 -468 75 -1784 101 -152 51 -1222 71 -1619 113 -508 133 -1168 53 -394 71 -806 85 -600 85 -974 161 -386 73 -370 77 -3390 171 -276 77 -1595 95 -220 171 -3647 53 -772 55 -462 57 -832 71 -2348 119 -1478 51 -80 85 -909 79 -338 71 -2833 99 -274 77 -554 125 -1822 53 -108 83 -371 85 -172 57 -478 75 -1424 53 -680 55 -288 73 -1290 109 -3129 131 -958 131 -898 53 -1836 87 -418 53 -2138 119 -1003 101 -270 115 -1194 79 -2138 115 -2351 55 -1511 115 -1697 105 -1278 115 -198 183 -RAW_Data: -1450 57 -330 51 -894 57 -1926 55 -1552 83 -624 81 -3174 75 -270 107 -1111 85 -686 103 -4595 119 -96 286 -1021 71 -850 143 -606 85 -2639 81 -192 77 -1574 81 -468 55 -2484 137 -476 55 -314 85 -1139 53 -1418 79 -2247 111 -874 77 -120 71 -2082 99 -644 107 -288 115 -344 163 -5007 53 -102 121 -876 87 -244 115 -390 57 -144 85 -856 109 -510 103 -506 73 -3754 79 -2838 165 -1569 139 -2268 75 -2086 121 -2102 170 -634 71 -1294 191 -3428 75 -1719 99 -3194 75 -1630 71 -866 51 -644 171 -1416 97 -1687 57 -634 85 -96 71 -366 85 -745 53 -1418 57 -494 85 -1072 73 -122 73 -178 81 -2848 103 -1186 95 -286 71 -144 51 -1366 256 -1440 85 -1184 211 -2798 83 -140 57 -1516 55 -980 85 -86 141 -1316 55 -758 57 -3040 79 -928 99 -4315 181 -492 103 -400 73 -2040 81 -416 75 -388 171 -4212 53 -2250 85 -144 57 -144 85 -821 167 -192 117 -312 71 -3722 51 -248 83 -588 115 -2378 79 -594 79 -364 97 -408 167 -1382 53 -392 109 -1418 83 -2868 127 -120 71 -2244 71 -287 121 -672 85 -764 53 -260 57 -428 53 -1385 57 -3200 57 -3074 83 -114 55 -2185 77 -252 83 -1130 57 -1965 75 -2128 85 -3293 51 -1258 73 -934 97 -832 73 -432 95 -968 75 -692 93 -888 53 -560 77 -484 77 -954 81 -3098 85 -116 85 -506 85 -1618 141 -442 55 -938 73 -478 123 -944 215 -5486 57 -1530 77 -526 57 -1064 77 -342 83 -1814 75 -3042 85 -2245 85 -1223 79 -2229 83 -1538 71 -4608 103 -316 85 -58 185 -1588 71 -3626 85 -2112 253 -1060 57 -562 183 -3805 206 -674 71 -705 71 -2820 105 -468 95 -3669 213 -877 73 -118 121 -204 135 -2468 113 -232 85 -4493 57 -945 55 -672 113 -652 113 -734 53 -424 83 -510 83 -3710 83 -683 97 -2373 85 -2361 259 -995 51 -452 115 -486 81 -5736 57 -1278 51 -4423 111 -1306 163 -248 95 -844 57 -1317 115 -2350 75 -366 99 -1458 57 -444 57 -3824 87 -54 83 -388 249 -2503 101 -192 95 -910 135 -386 105 -2702 85 -1208 85 -250 85 -200 141 -1450 75 -485 57 -662 85 -3323 107 -622 36813 -362 51 -259 963 -732 937 -728 935 -756 909 -756 883 -780 885 -780 883 -784 883 -806 859 -782 883 -808 863 -806 859 -806 857 -808 857 -806 859 -808 857 -808 861 -808 857 -806 861 -806 859 -808 857 -806 861 -808 859 -808 833 -832 833 -834 831 -832 859 -806 857 -806 859 -808 859 -810 857 -806 859 -806 861 -808 833 -832 833 -832 831 -832 833 -834 857 -808 833 -834 831 -834 835 -834 833 -832 833 -832 831 -RAW_Data: -832 833 -834 857 -806 859 -808 857 -812 833 -834 857 -806 833 -832 833 -832 857 -808 835 -834 831 -834 831 -834 835 -834 831 -832 835 -832 831 -836 831 -832 857 -810 833 -832 831 -834 831 -834 857 -808 831 -836 833 -832 831 -834 831 -834 833 -834 831 -834 857 -808 859 -806 859 -810 831 -834 857 -808 857 -808 859 -810 831 -832 833 -832 833 -832 833 -832 859 -808 833 -832 857 -808 833 -832 859 -806 859 -808 833 -834 859 -808 831 -834 835 -832 833 -832 831 -834 857 -806 859 -808 859 -808 857 -810 831 -836 833 -832 831 -832 833 -832 857 -808 859 -806 835 -834 831 -834 835 -832 857 -808 833 -834 831 -832 833 -834 833 -834 833 -832 831 -834 833 -834 835 -830 833 -832 831 -834 833 -832 833 -832 859 -808 857 -808 833 -834 833 -832 833 -834 831 -834 857 -808 835 -832 831 -834 857 -808 857 -808 835 -834 831 -834 831 -834 833 -836 831 -832 833 -834 831 -834 831 -834 833 -834 835 -832 831 -832 833 -832 857 -810 833 -834 833 -836 829 -832 831 -832 835 -834 831 -832 835 -834 831 -834 833 -832 833 -832 831 -832 835 -832 833 -832 833 -832 833 -834 857 -808 859 -808 833 -834 833 -832 833 -834 831 -834 831 -832 835 -834 831 -832 833 -832 859 -810 831 -832 833 -834 857 -808 833 -832 833 -832 833 -832 833 -834 859 -806 859 -810 831 -834 831 -832 859 -806 835 -836 831 -832 831 -832 859 -808 833 -832 859 -808 857 -808 835 -832 831 -834 833 -834 831 -834 833 -832 857 -810 833 -832 833 -832 859 -808 831 -832 859 -808 831 -832 859 -808 859 -806 859 -806 859 -810 833 -834 835 -832 833 -832 831 -832 857 -808 857 -808 859 -806 835 -834 831 -836 835 -834 831 -832 831 -832 859 -808 831 -836 833 -834 833 -832 833 -832 831 -832 859 -806 835 -836 833 -832 831 -834 831 -836 831 -834 833 -832 835 -834 833 -834 831 -832 831 -832 835 -834 833 -832 831 -832 833 -834 831 -834 831 -832 857 -808 857 -808 833 -834 835 -832 831 -834 831 -834 857 -808 859 -808 833 -832 831 -834 833 -832 859 -808 857 -808 835 -832 835 -832 831 -832 835 -834 831 -834 831 -834 833 -834 831 -832 833 -832 833 -832 835 -834 831 -834 831 -832 835 -832 831 -834 859 -806 859 -808 859 -806 837 -832 831 -832 833 -834 857 -808 833 -832 859 -808 857 -808 831 -834 835 -832 833 -832 857 -808 833 -832 861 -810 831 -832 831 -834 857 -810 831 -834 831 -832 859 -808 833 -832 857 -808 859 -806 835 -834 831 -834 833 -832 859 -806 859 -808 857 -RAW_Data: -808 835 -832 831 -834 857 -808 857 -808 859 -808 859 -808 831 -832 833 -834 831 -836 833 -832 831 -832 859 -808 833 -834 833 -832 859 -806 859 -808 857 -808 833 -834 833 -832 831 -834 833 -834 857 -808 831 -832 859 -808 859 -806 835 -832 1665 -4178 1667 -1662 833 -832 1663 -832 3335 -832 835 -832 831 -2500 831 -1666 3339 -3338 829 -830 831 -832 2499 -832 1665 -2500 4173 -1664 1665 -832 831 -2500 833 -3344 831 -830 833 -830 2499 -832 1669 -2506 4177 -1640 1693 -802 853 -2476 853 -3332 831 -830 831 -830 2501 -836 1665 -2506 4169 -1664 1663 -832 829 -2506 831 -3338 827 -832 831 -830 2505 -830 1665 -2504 4167 -1664 1665 -832 831 -2498 13331 -828 853 -830 4165 -4166 1663 -2506 3333 -830 1665 -1664 1663 -1668 831 -832 1665 -3336 831 -1668 831 -832 831 -832 2499 -3342 829 -832 829 -834 2501 -832 1667 -2502 4169 -1672 1663 -830 831 -2502 829 -3338 829 -834 831 -832 2497 -832 1667 -2500 4169 -1666 1663 -836 831 -2500 831 -3342 833 -832 829 -832 2497 -832 1667 -2510 4179 -1638 1689 -804 855 -2476 855 -3330 831 -830 831 -832 2499 -832 1669 -2506 4173 -1662 1663 -832 829 -2504 831 -3336 829 -832 831 -834 2497 -832 1665 -2500 4177 -1668 1663 -832 831 -2504 829 -3336 829 -832 829 -832 2499 -832 1667 -2500 4169 -1668 1667 -834 831 -2500 833 -3338 827 -832 831 -832 2499 -832 1671 -2508 4169 -1662 1663 -830 829 -2500 831 -3332 833 -832 833 -834 2499 -834 1669 -2500 4167 -1664 1671 -832 829 -2500 831 -3334 829 -836 831 -832 2499 -834 1665 -2500 4185 -1642 1689 -828 831 -2500 829 -3334 831 -834 831 -830 2507 -830 1665 -2500 4167 -1668 1665 -830 831 -778 77 -502 75 -427 224 -242 167 -1264 57 -334 105 -5932 115 -2566 79 -514 55 -459 57 -1564 75 -296 95 -4925 111 -202 85 -3492 99 -4327 55 -582 57 -1520 197 -708 53 -1666 53 -1005 85 -3861 139 -858 73 -276 147 -1406 113 -5864 95 -1253 93 -2699 85 -575 93 -3012 57 -774 71 -1662 55 -926 77 -138 55 -744 97 -6232 79 -1435 109 -799 73 -1018 95 -982 79 -664 85 -192 171 -202 57 -198 83 -832 117 -96 53 -734 119 -780 79 -1394 197 -488 55 -371 83 -144 85 -1816 145 -2451 57 -114 115 -1158 69 -168 71 -868 87 -3907 51 -2788 123 -1454 55 -492 53 -510 85 -144 57 -857 53 -1214 85 -140 51 -899 103 -1318 55 -2793 73 -142 71 -1067 109 -5247 55 -172 113 -341 85 -2553 57 -682 217 -170 77 -1216 161 -1844 53 -2747 87 -538 111 -843 53 -1197 109 -2973 53 -1314 111 -514 95 -864 81 -1671 53 -1812 57 -1419 71 -636 83 -3998 75 -1285 135 -578 51 -2754 179 -1194 71 -7260 51 -RAW_Data: -1003 75 -1988 57 -764 57 -5820 85 -474 69 -655 81 -136 103 -1594 53 -1120 51 -520 101 -487 159 -5829 77 -784 103 -440 139 -432 57 -786 79 -506 95 -1982 53 -3728 71 -2506 77 -983 57 -922 135 -3584 57 -1088 57 -142 115 -424 55 -1435 103 -954 113 -286 99 -948 79 -417 133 -398 85 -260 143 -3084 55 -940 195 -758 71 -288 71 -216 95 -1490 77 -1902 85 -3592 53 -796 55 -1351 53 -1538 55 -778 75 -5106 85 -144 57 -1104 95 -452 51 -3006 83 -316 85 -3131 69 -1243 53 -2591 73 -192 71 -462 71 -2566 81 -706 85 -446 51 -1226 81 -656 57 -622 55 -1302 115 -428 55 -1165 171 -1767 111 -1888 85 -1362 55 -562 79 -698 71 -100 191 -192 57 -150 73 -342 97 -342 71 -534 125 -1558 81 -348 77 -232 55 -434 81 -2934 55 -2374 392 -774 53 -3880 71 -4408 105 -230 55 -888 57 -1014 133 -1074 51 -440 173 -314 111 -2931 105 -2165 53 -1654 51 -5547 109 -1688 163 -398 103 -1727 51 -958 139 -1370 87 -144 85 -316 143 -3860 51 -682 163 -2472 107 -1116 125 -896 51 -366 73 -1198 75 -647 143 -2628 53 -1663 85 -984 103 -882 115 -960 71 -2405 81 -1010 53 -510 79 -3410 135 -2502 139 -1552 53 -2004 55 -628 115 -404 55 -570 113 -688 113 -1817 131 -1240 51 -414 135 -538 109 -1233 201 -214 57 -274 73 -1428 85 -434 57 -2570 53 -340 57 -2775 51 -2914 71 -828 79 -740 57 -3780 137 -386 75 -1304 57 -641 53 -668 85 -480 53 -284 85 -764 85 -358 53 -6176 127 -442 95 -192 95 -2628 109 -722 83 -1068 95 -144 145 -404 57 -1762 51 -2612 171 -230 87 -632 171 -146 55 -196 79 -324 51 -224 57 -1318 81 -1345 85 -136 53 -410 83 -80 81 -930 143 -172 57 -2253 53 -216 71 -282 79 -788 212 -706 87 -3134 71 -498 87 -2968 77 -1186 83 -1189 57 -656 83 -424 53 -1410 71 -1554 55 -1004 55 -2736 83 -3182 83 -318 69 -456 95 -926 85 -1232 51 -138 55 -2078 111 -708 142 -268 79 -4582 53 -1152 55 -160 95 -144 161 -330 71 -2236 133 -324 51 -1134 119 -144 139 -718 85 -272 75 -694 55 -318 57 -1710 75 -330 55 -202 115 -1642 163 -56 113 -256 85 -398 73 -1378 95 -94 71 -168 71 -1748 111 -744 85 -2261 73 -336 85 -4614 57 -2696 79 -54 277 -2278 85 -716 71 -2765 85 -1852 77 -622 95 -913 157 -1542 105 -284 57 -3042 77 -1418 85 -900 51 -658 83 -1122 115 -452 81 -100 75 -1400 57 -700 105 -380 99 -332 55 -573 75 -2416 75 -294 107 -316 161 -957 113 -730 51 -998 71 -384 53 -488 209 -RAW_Data: -222 57 -364 53 -1099 57 -570 301 -4112 71 -2730 53 -917 85 -519 85 -1030 55 -2335 85 -374 143 -791 169 -786 57 -998 85 -404 85 -323 81 -1443 55 -1861 199 -88 85 -1991 57 -6675 95 -811 131 -1474 85 -202 229 -1528 85 -912 87 -1233 53 -484 123 -1245 53 -2714 83 -1715 51 -2626 77 -1429 57 -2401 85 -420 51 -669 55 -986 143 -1509 95 -1362 71 -530 79 -762 51 -1226 95 -5703 143 -138 71 -738 83 -912 55 -1582 55 -1200 55 -1106 55 -1289 95 -558 95 -206 143 -2372 109 -748 51 -1584 57 -172 115 -662 135 -156 207 -1464 55 -1732 125 -218 135 -2488 57 -312 55 -2344 79 -412 85 -610 53 -170 55 -1890 57 -3146 57 -924 167 -1127 145 -1284 51 -436 55 -458 115 -340 179 -640 121 -1297 184 -1106 55 -716 113 -2115 71 -240 95 -120 53 -190 57 -266 125 -158 57 -1042 203 -3645 101 -196 101 -138 85 -912 159 -2064 85 -536 83 -3063 55 -170 85 -510 53 -1224 81 -128 51 -1143 101 -4358 95 -947 53 -1402 85 -1894 51 -160 81 -3344 250 -718 103 -184 185 -280 51 -372 113 -820 57 -1154 105 -100 75 -462 71 -1681 53 -1395 79 -670 121 -2820 57 -1014 79 -1628 87 -498 57 -288 143 -740 57 -584 77 -676 85 -1106 57 -2601 103 -1062 83 -3676 75 -1387 171 -138 53 -164 83 -746 57 -316 172 -144 57 -1987 99 -1416 143 -2058 85 -1872 57 -1538 171 -1630 171 -2542 137 -1102 117 -1824 135 -292 163 -462 125 -1226 57 -622 57 -345 85 -1499 53 -910 53 -3758 77 -960 143 -216 167 -912 53 -160 75 -354 57 -831 71 -168 73 -1026 81 -1778 85 -250 51 -1472 57 -466 51 -916 119 -992 73 -980 55 -1039 139 -424 57 -1118 141 -534 85 -486 85 -906 119 -144 71 -1481 109 -1822 79 -2745 57 -3291 137 -2190 95 -1865 133 -1580 55 -628 57 -1683 87 -350 55 -1363 115 -220 87 -56 113 -1068 85 -1642 71 -1727 57 -1398 95 -2382 79 -416 53 -2231 57 -86 143 -984 77 -809 95 -2508 73 -5249 57 -1408 115 -1401 115 -974 143 -1392 57 -818 51 -472 83 -340 83 -533 57 -144 55 -1447 146 -2054 167 -116 55 -3623 51 -1221 75 -314 129 -1122 79 -1146 53 -1695 55 -772 165 -899 57 -454 53 -110 139 -1158 85 -1138 95 -318 55 -1394 75 -1302 85 -1774 81 -851 109 -1620 79 -1598 101 -1417 81 -1038 143 -376 201 -86 85 -2134 83 -2553 131 -431 71 -714 51 -1226 85 -565 254 -307 143 -72 75 -2096 51 -426 57 -1246 185 -972 51 -104 121 -422 51 -746 55 -174 85 -1839 75 -468 193 -952 55 -5596 53 -902 85 -932 77 -244 93 -797 71 -RAW_Data: -120 95 -1172 127 -1658 127 -336 115 -3786 81 -1645 175 -664 71 -1213 119 -538 81 -486 83 -1056 139 -502 81 -136 57 -1016 55 -4015 79 -983 83 -3019 162 -872 55 -1206 51 -668 69 -1794 71 -72 71 -1110 85 -260 109 -1256 77 -1689 77 -220 85 -1356 85 -1444 57 -1574 113 -748 85 -2763 111 -1122 97 -200 71 -180 163 -652 99 -406 221 -1464 57 -2674 152 -1056 111 -1950 95 -246 51 -564 57 -1968 143 -2935 125 -72 51 -3471 147 -1538 75 -2358 143 -998 55 -56 81 -1742 71 -614 71 -510 115 -1230 51 -592 85 -5279 71 -1456 143 -546 97 -2283 115 -768 135 -2157 53 -1126 125 -1852 85 -172 115 -288 135 -3593 55 -82 81 -418 79 -2092 75 -2337 75 -862 123 -1512 51 -196 57 -2481 81 -282 85 -272 71 -96 71 -468 75 -200 99 -1080 55 -1080 79 -80 81 -146 241 -1940 57 -172 85 -232 221 -166 111 -720 111 -1020 201 -202 57 -438 113 -3193 105 -432 95 -382 53 -254 85 -RAW_Data: -576 57 -428 71 -1124 85 -756 77 -160 81 -747 57 -720 53 -1277 75 -1634 143 -120 119 -72 85 -682 287 -156 71 -72 71 -828 155 -144 95 -238 145 -893 133 -784 55 -530 305 -346 85 -86 357 -690 95 -144 97 -606 95 -590 99 -748 51 -954 55 -786 77 -878 131 -834 141 -770 105 -718 53 -86 143 -958 75 -2848 101 -1184 57 -392 81 -202 55 -592 53 -854 57 -2498 71 -3492 217 -2608 55 -1050 215 -94 53 -230 115 -1046 85 -890 81 -464 57 -350 149 -178 57 -1463 71 -2903 113 -283 53 -1820 83 -2193 55 -3771 51 -2286 53 -132 109 -1115 97 -1400 57 -892 105 -670 81 -634 107 -400 53 -662 107 -172 55 -8569 95 -1160 113 -172 57 -376 119 -360 57 -4359 55 -5612 85 -903 107 -2561 217 -1696 53 -328 99 -222 69 -628 53 -190 75 -296 51 -100 95 -238 190 -463 255 -664 79 -2058 57 -840 55 -1408 71 -774 53 -84 93 -582 51 -8306 55 -1909 129 -4008 135 -3409 77 -106 77 -664 95 -1304 123 -308 79 -1343 77 -680 79 -3844 119 -1222 75 -1388 125 -3826 57 -870 73 -744 53 -2282 140 -500 81 -192 87 -2144 143 -892 55 -950 71 -614 187 -174 85 -402 51 -288 131 -494 111 -124 165 -1326 57 -202 55 -346 57 -918 181 -896 57 -380 105 -484 191 -670 85 -2323 55 -3243 133 -390 97 -3123 105 -1292 71 -722 105 -426 75 -172 93 -880 71 -96 81 -544 79 -122 79 -2570 53 -782 57 -2544 53 -438 143 -988 113 -490 57 -144 113 -1980 115 -1540 51 -1974 452 -1540 57 -1507 115 -1440 85 -1485 55 -1309 109 -4194 107 -826 53 -84 109 -4176 109 -1699 111 -1789 199 -1891 119 -1162 55 -1282 105 -1320 189 -200 115 -1004 83 -500 95 -3674 215 -1672 57 -396 55 -1992 71 -191 71 -4032 83 -238 57 -1076 139 -1018 55 -2031 73 -3503 77 -2106 51 -268 173 -1106 101 -160 57 -2695 53 -412 71 -971 143 -512 143 -1142 219 -314 57 -2280 141 -1352 53 -142 115 -1261 53 -3901 85 -1568 53 -1030 165 -1322 51 -1320 137 -859 95 -419 95 -1546 85 -431 195 -4704 55 -174 115 -86 55 -3617 81 -1847 57 -1075 55 -258 57 -512 55 -842 55 -144 95 -648 79 -1022 55 -2452 71 -400 95 -1854 57 -316 85 -3789 57 -1923 57 -56 51 -586 51 -558 81 -280 81 -144 71 -406 51 -5014 71 -386 127 -138 229 -270 75 -144 141 -511 123 -268 57 -1619 101 -2900 113 -426 85 -114 57 -1092 173 -690 57 -6610 85 -6804 57 -314 119 -382 71 -484 115 -2236 143 -1202 79 -584 105 -724 71 -948 115 -214 75 -5453 51 -952 145 -572 107 -2334 103 -338 55 -RAW_Data: -838 79 -4725 69 -298 95 -1760 81 -795 55 -480 179 -1534 133 -457 51 -970 51 -2324 81 -844 57 -1780 119 -550 145 -1218 57 -268 55 -573 115 -2077 71 -1476 57 -86 97 -4518 71 -526 51 -272 139 -3348 85 -1338 53 -780 53 -1548 77 -582 81 -881 55 -1422 105 -1636 77 -1617 71 -924 201 -260 85 -1092 83 -914 51 -2326 111 -382 109 -2328 77 -750 95 -1342 71 -120 51 -280 57 -918 73 -784 53 -4704 79 -1094 85 -624 57 -2733 85 -710 77 -346 57 -716 143 -1489 165 -792 173 -688 73 -102 97 -1139 75 -76 51 -164 81 -762 109 -296 77 -5079 113 -500 83 -304 139 -1080 51 -4167 75 -998 85 -367 85 -3404 71 -1460 95 -1588 111 -1510 95 -939 111 -2271 57 -694 135 -1592 85 -256 163 -708 95 -770 81 -164 113 -1608 95 -672 83 -470 79 -1810 55 -82 79 -144 51 -188 113 -58 115 -1338 55 -4046 119 -970 38481 -128 51 -496 935 -728 937 -754 911 -756 911 -754 911 -754 911 -756 883 -784 885 -780 883 -782 885 -808 857 -806 859 -806 859 -808 859 -806 859 -806 859 -808 857 -808 859 -806 861 -808 857 -808 859 -810 859 -806 857 -808 857 -806 859 -806 859 -808 859 -806 859 -806 859 -808 857 -810 857 -806 859 -810 835 -832 831 -834 831 -832 833 -832 859 -810 831 -832 833 -834 857 -808 833 -834 833 -832 831 -834 833 -834 831 -834 833 -834 831 -834 831 -832 859 -808 857 -806 859 -808 833 -832 833 -832 859 -810 831 -832 859 -808 857 -808 859 -808 831 -836 831 -834 833 -832 833 -834 857 -808 857 -810 831 -832 833 -834 857 -808 857 -808 833 -832 859 -810 831 -832 859 -808 831 -832 859 -808 835 -832 831 -834 857 -808 833 -832 859 -810 833 -836 831 -834 831 -832 831 -834 857 -808 859 -808 835 -834 831 -832 831 -834 831 -834 833 -832 857 -808 833 -832 859 -810 831 -834 831 -832 859 -808 857 -808 833 -834 831 -834 833 -832 859 -808 831 -834 833 -834 833 -834 831 -834 831 -834 857 -808 857 -808 833 -834 833 -834 833 -832 833 -832 835 -834 831 -834 833 -832 831 -832 831 -834 859 -808 857 -808 831 -834 857 -808 859 -806 835 -834 831 -834 831 -834 835 -832 833 -832 857 -810 831 -832 833 -832 833 -832 859 -808 861 -808 833 -834 831 -832 833 -832 857 -810 831 -834 831 -838 833 -830 833 -832 831 -832 859 -808 833 -832 833 -832 859 -808 857 -808 835 -832 833 -832 831 -836 831 -832 859 -806 859 -808 859 -808 857 -808 859 -808 833 -832 833 -834 833 -832 831 -834 857 -808 859 -806 835 -834 831 -832 833 -RAW_Data: -834 833 -834 831 -832 857 -808 835 -832 833 -834 857 -808 859 -806 859 -808 857 -808 831 -832 859 -808 859 -808 831 -834 857 -808 859 -810 833 -832 831 -838 831 -834 835 -830 833 -832 831 -832 835 -832 833 -834 835 -832 831 -832 831 -834 831 -834 857 -808 859 -806 859 -808 857 -808 833 -834 833 -832 831 -836 831 -832 859 -810 833 -834 833 -832 831 -832 833 -834 831 -832 859 -808 857 -808 857 -808 859 -808 859 -806 859 -808 857 -808 859 -808 831 -834 857 -810 857 -806 835 -834 831 -834 831 -834 833 -832 833 -834 831 -834 833 -832 857 -808 859 -808 857 -810 833 -834 833 -832 831 -832 833 -832 857 -812 831 -836 835 -830 831 -832 831 -836 831 -834 833 -836 831 -832 831 -834 831 -832 859 -808 859 -808 831 -836 835 -834 831 -832 831 -834 831 -832 859 -808 831 -836 835 -832 831 -832 831 -834 857 -808 857 -810 831 -834 833 -834 835 -832 831 -832 831 -836 831 -834 859 -808 833 -832 831 -834 857 -808 859 -806 833 -834 857 -810 835 -832 831 -834 831 -832 833 -832 833 -834 833 -834 831 -834 831 -832 859 -808 857 -808 833 -832 859 -808 857 -808 859 -808 835 -832 833 -832 831 -832 859 -808 857 -808 859 -808 833 -832 833 -834 835 -832 831 -834 831 -834 831 -834 857 -810 831 -832 857 -808 859 -808 859 -808 857 -808 831 -834 859 -806 859 -808 857 -808 857 -810 857 -808 859 -810 831 -834 831 -832 833 -834 857 -808 857 -808 835 -834 831 -832 833 -832 859 -808 857 -808 833 -834 831 -832 833 -834 857 -810 835 -834 831 -832 831 -832 859 -806 1691 -4134 1691 -1640 855 -830 1663 -832 3333 -832 831 -832 831 -2502 831 -1668 3337 -3336 829 -832 831 -832 2505 -830 1665 -2506 4169 -1642 1689 -830 829 -2500 831 -3342 829 -832 831 -832 2501 -832 1665 -2506 4157 -1664 1691 -804 853 -2478 857 -3306 853 -832 833 -834 2505 -830 1663 -2502 4153 -1660 1687 -828 829 -2500 833 -3340 829 -830 829 -832 2499 -832 1667 -2500 4171 -1668 1667 -832 829 -2500 829 -3318 859 -830 831 -832 2499 -832 1667 -2498 4171 -1664 1663 -832 831 -2502 829 -3338 831 -832 831 -832 2501 -832 1667 -2504 4167 -1670 1669 -828 831 -2500 12491 -3334 853 -4972 853 -1660 827 -1660 853 -3308 855 -830 829 -830 2499 -834 1665 -2508 4169 -1664 1663 -830 831 -2506 829 -3336 831 -830 833 -832 2499 -832 1667 -2508 4165 -1664 1663 -832 833 -2500 833 -3338 829 -832 831 -832 2501 -830 1665 -2502 4171 -1666 1663 -832 831 -2500 831 -3342 829 -832 831 -832 2501 -836 1663 -2500 4173 -1670 1663 -830 831 -2504 829 -3332 831 -832 831 -832 2507 -RAW_Data: -830 1667 -2502 4173 -1642 1695 -804 855 -2498 829 -3332 831 -830 831 -832 2499 -834 1669 -2506 4175 -1636 1689 -832 831 -2480 853 -3332 833 -830 831 -832 2509 -830 1663 -2502 4165 -1666 1667 -832 831 -2500 831 -3340 831 -830 831 -832 2503 -832 1665 -2504 4169 -1662 1663 -832 831 -2506 831 -3340 829 -830 831 -830 2503 -832 1669 -2502 4169 -1664 1665 -830 831 -1350 81 -266 177 -269 57 -490 143 -2812 85 -2206 57 -72 73 -689 200 -726 149 -598 143 -1554 85 -792 143 -336 123 -548 95 -996 109 -424 55 -140 143 -520 123 -744 81 -730 51 -2840 73 -2716 103 -490 129 -1354 51 -2207 85 -2683 55 -288 173 -895 85 -4697 55 -530 97 -1837 131 -946 73 -660 103 -2022 119 -1039 95 -454 143 -1098 109 -1004 219 -78 312 -778 53 -364 51 -666 113 -376 85 -1059 73 -802 55 -700 75 -1554 171 -1446 107 -168 85 -1278 111 -2564 97 -1578 71 -94 75 -1124 57 -202 171 -2954 79 -3597 191 -326 57 -922 51 -2730 53 -6135 55 -2020 225 -230 87 -1212 53 -1216 149 -1208 53 -502 57 -985 57 -2636 51 -2358 85 -926 143 -1490 57 -114 85 -634 87 -258 57 -1351 75 -1086 95 -192 71 -2150 73 -2060 137 -2159 95 -1362 73 -336 71 -953 77 -220 53 -1989 83 -172 57 -1969 107 -2257 140 -886 71 -1026 123 -372 73 -934 99 -522 139 -651 257 -1566 77 -5069 201 -4802 55 -142 57 -1396 79 -2716 55 -2050 79 -604 111 -1040 85 -1534 109 -544 83 -342 107 -1459 85 -86 57 -3009 99 -322 51 -2178 119 -1485 73 -694 95 -2832 85 -628 81 -1824 222 -1313 85 -918 71 -864 139 -872 79 -218 51 -468 81 -400 85 -1794 83 -742 227 -504 79 -1632 95 -1193 173 -316 85 -2780 51 -1454 103 -246 95 -1885 53 -2051 125 -1186 71 -1708 73 -719 83 -896 73 -524 81 -1082 141 -1076 77 -982 95 -1493 55 -1760 57 -1092 125 -2082 51 -120 83 -2372 77 -1458 75 -1212 75 -412 125 -504 109 -1937 57 -1176 161 -532 71 -224 75 -170 53 -2455 83 -1448 85 -520 55 -1582 143 -1199 115 -172 57 -3324 113 -968 115 -936 109 -3588 71 -1038 57 -1284 245 -674 125 -3396 105 -11139 57 -112 107 -1070 81 -424 115 -2333 131 -1048 115 -874 81 -1754 57 -1300 115 -776 109 -1778 83 -58 85 -2295 55 -5975 115 -6613 55 -2096 51 -394 113 -976 51 -856 53 -950 85 -896 57 -1021 83 -2526 71 -914 53 -1867 121 -376 85 -144 115 -1340 51 -544 143 -1367 71 -252 105 -3156 85 -1170 53 -82 55 -3404 107 -4580 81 -284 83 -2419 57 -1649 123 -714 51 -315 85 -452 105 -562 51 -2883 339 -284 105 -4443 79 -772 145 -700 71 -RAW_Data: -2030 57 -506 57 -2361 135 -4288 71 -240 95 -5117 81 -1952 85 -78 51 -268 131 -336 187 -256 103 -480 115 -200 57 -1072 51 -1488 169 -601 85 -344 51 -527 83 -316 105 -2755 81 -1151 55 -515 119 -1884 115 -86 57 -198 107 -572 75 -1890 87 -344 53 -3983 53 -1831 143 -1036 115 -776 73 -1976 83 -526 57 -244 71 -2232 53 -658 55 -2022 111 -1187 55 -1060 143 -790 113 -448 85 -1146 171 -360 77 -326 55 -1430 55 -1196 57 -2278 57 -58 85 -472 99 -144 153 -76 73 -3314 71 -336 127 -1364 85 -2548 161 -2241 83 -1835 173 -1282 105 -598 109 -1635 85 -1584 131 -936 57 -202 57 -316 87 -2655 57 -2056 83 -1442 51 -2730 51 -1225 73 -1618 85 -1316 73 -1938 75 -144 57 -466 75 -542 81 -666 135 -76 51 -2683 57 -2176 171 -1803 73 -1938 55 -116 85 -674 71 -144 73 -1015 71 -348 139 -114 87 -804 57 -876 81 -3570 103 -2494 115 -4706 97 -836 167 -3400 109 -159 109 -422 143 -1118 81 -1652 53 -1064 85 -2080 125 -942 51 -536 109 -596 57 -2403 101 -1859 51 -456 51 -4086 53 -944 57 -510 135 -1405 87 -1710 85 -2595 81 -654 71 -366 179 -2085 79 -1852 55 -1312 77 -1568 73 -230 201 -612 77 -2530 107 -834 53 -2593 71 -720 69 -956 51 -1000 71 -2016 73 -1062 53 -288 85 -1816 51 -1080 105 -4427 57 -502 57 -246 57 -172 85 -522 111 -1109 55 -1322 85 -3728 53 -726 77 -342 169 -548 85 -144 85 -4389 95 -168 111 -1119 107 -170 55 -482 55 -1877 115 -224 55 -1441 115 -1658 55 -260 115 -2828 57 -292 141 -588 77 -366 113 -1268 113 -2047 57 -2529 95 -96 95 -2418 51 -606 55 -7091 199 -232 55 -542 185 -503 115 -4766 71 -1646 81 -364 57 -842 57 -1202 77 -1835 173 -606 75 -1815 95 -2219 115 -1155 105 -1413 55 -624 55 -1132 73 -3094 107 -228 115 -2893 85 -410 77 -390 53 -876 71 -1922 95 -238 213 -102 71 -3646 57 -144 57 -790 83 -258 57 -534 55 -1262 55 -442 57 -922 55 -394 81 -2235 85 -432 115 -1230 55 -2571 167 -2298 57 -364 173 -114 115 -2858 55 -380 55 -1758 113 -140 137 -679 53 -486 57 -492 107 -470 79 -344 107 -708 55 -438 201 -647 85 -318 125 -1030 119 -216 115 -1186 190 -456 111 -830 85 -324 75 -204 73 -1540 55 -1106 51 -824 87 -1648 51 -236 51 -894 113 -758 57 -246 73 -2861 163 -368 107 -2106 85 -172 85 -230 57 -732 81 -1398 55 -1004 187 -1516 55 -1230 105 -1356 51 -134 169 -188 51 -3247 85 -346 115 -1154 71 -288 83 -1114 129 -294 75 -52 51 -1298 55 -2941 55 -RAW_Data: -214 75 -196 111 -2485 75 -126 95 -312 143 -72 71 -190 81 -804 57 -174 55 -1083 55 -368 51 -108 85 -288 171 -314 85 -508 57 -1214 53 -642 73 -1274 75 -2906 85 -994 53 -196 55 -516 57 -1062 71 -168 95 -230 73 -220 111 -1678 175 -885 79 -392 135 -780 75 -1414 101 -226 115 -1356 75 -1103 107 -166 55 -374 201 -1658 79 -2138 83 -2953 51 -3438 57 -1496 139 -706 55 -86 113 -550 79 -290 69 -886 75 -1810 85 -2014 57 -7729 85 -346 143 -320 71 -370 109 -630 171 -832 57 -230 85 -2047 57 -2083 55 -1038 53 -307 143 -3795 85 -224 51 -76 75 -734 51 -110 139 -1386 51 -1956 143 -804 87 -2378 51 -266 95 -479 71 -74 153 -3470 57 -1820 195 -2516 191 -2735 51 -298 95 -382 55 -142 85 -484 143 -422 139 -240 145 -224 71 -614 51 -5422 53 -284 57 -2086 79 -457 143 -792 69 -1877 57 -370 53 -1174 57 -202 201 -444 143 -2668 77 -499 95 -336 71 -168 99 -601 55 -1254 113 -342 107 -572 57 -2087 55 -140 55 -1613 103 -226 187 -516 87 -984 55 -1292 139 -2796 53 -104 83 -548 83 -58 115 -566 103 -2006 57 -399 167 -716 143 -924 105 -226 57 -756 127 -366 81 -7232 53 -3058 71 -3351 57 -316 77 -689 99 -1099 75 -1238 142 -RAW_Data: -338 85 -404 115 -1320 165 -86 113 -280 133 -1238 133 -838 137 -2438 233 -1339 77 -326 285 -684 85 -542 405 -144 117 -74 133 -488 161 -256 154 -204 219 -292 79 -234 131 -58 143 -386 381 -108 211 -370 143 -240 71 -510 177 -511 191 -238 163 -428 115 -784 105 -698 57 -774 141 -1686 85 -88 53 -504 53 -138 83 -3074 229 -2636 105 -344 57 -1200 85 -431 85 -368 131 -592 79 -108 57 -144 85 -2860 83 -1708 77 -5452 145 -130 111 -514 75 -214 97 -194 53 -1610 139 -968 51 -456 71 -1228 79 -831 115 -4064 103 -2760 57 -1613 57 -1198 55 -1513 85 -825 69 -72 87 -6472 143 -202 57 -2092 113 -764 111 -1016 85 -1328 321 -2368 57 -288 83 -948 107 -1250 111 -606 79 -392 87 -1048 81 -1190 57 -3359 131 -612 133 -4205 81 -244 71 -72 143 -1263 85 -230 115 -414 107 -209 125 -172 57 -718 133 -282 75 -1116 81 -1250 57 -200 231 -684 57 -568 75 -2242 81 -1015 85 -2736 141 -96 239 -574 119 -1188 95 -72 119 -1968 77 -364 57 -1090 75 -2203 125 -4261 171 -72 71 -432 71 -3192 87 -418 55 -646 79 -519 57 -3108 181 -1844 53 -576 111 -980 53 -498 95 -1398 57 -3554 81 -2111 157 -222 137 -156 85 -58 315 -180 55 -1576 115 -2007 75 -178 113 -1660 57 -2160 71 -942 51 -264 55 -470 77 -174 105 -1352 71 -528 71 -200 101 -1054 81 -672 57 -116 141 -168 57 -502 51 -3472 71 -96 71 -698 95 -557 139 -392 55 -572 57 -2121 75 -849 57 -757 191 -58 55 -835 51 -516 53 -1395 79 -230 51 -392 95 -1216 85 -540 81 -441 235 -2225 351 -84 111 -1198 57 -260 55 -86 113 -600 55 -1224 75 -104 51 -735 71 -2128 217 -2296 53 -174 125 -154 71 -96 71 -432 109 -1114 129 -2309 185 -760 93 -1597 119 -1341 53 -558 199 -1754 107 -80 53 -262 143 -2375 105 -530 127 -1875 77 -1161 55 -220 95 -1106 71 -427 213 -2730 71 -835 83 -1084 55 -958 113 -1938 55 -3530 99 -100 71 -1898 143 -1378 57 -1091 57 -872 85 -144 57 -1924 73 -1016 93 -72 95 -1056 97 -1922 79 -408 85 -1863 77 -908 57 -292 53 -816 79 -108 55 -918 95 -262 87 -1573 71 -384 105 -522 105 -5164 57 -2268 113 -1595 143 -1210 115 -1250 115 -1072 85 -392 55 -88 113 -392 83 -716 53 -2979 77 -945 173 -554 71 -3442 55 -1746 113 -144 287 -82 57 -604 157 -1221 115 -374 57 -1718 77 -432 57 -1504 57 -1140 83 -228 57 -202 85 -712 77 -934 57 -1234 129 -168 71 -800 77 -790 79 -2738 137 -1610 51 -610 55 -590 53 -2878 57 -1028 53 -RAW_Data: -2146 51 -722 113 -1007 85 -2775 71 -96 143 -168 71 -560 53 -80 81 -2334 81 -658 137 -220 51 -124 71 -6008 81 -776 71 -350 123 -168 71 -448 71 -454 109 -318 85 -3378 71 -490 109 -5580 127 -3651 73 -912 71 -1372 87 -1122 83 -144 57 -2868 53 -370 51 -1582 55 -266 157 -300 75 -1232 53 -662 199 -202 139 -1772 190 -294 81 -3744 53 -1156 79 -2455 55 -242 71 -408 57 -2073 99 -140 57 -1305 83 -455 57 -260 85 -720 75 -298 81 -975 259 -534 127 -352 57 -522 71 -1633 85 -843 57 -316 87 -1169 81 -566 75 -226 83 -196 115 -2258 173 -489 83 -136 55 -1445 81 -1108 71 -2362 97 -728 95 -1581 57 -432 57 -1344 133 -112 57 -910 55 -84 83 -954 83 -1375 155 -1141 195 -144 95 -1495 71 -829 111 -142 85 -2702 111 -1576 159 -1990 97 -640 75 -344 171 -86 85 -3409 71 -386 101 -912 143 -168 71 -1313 95 -1223 57 -288 85 -86 115 -230 57 -100 71 -910 71 -792 57 -254 85 -2755 137 -1395 69 -3515 87 -2440 55 -1038 137 -72 73 -448 105 -142 83 -1408 105 -2444 105 -492 97 -918 169 -86 81 -1721 57 -482 55 -5453 113 -3386 71 -128 173 -956 51 -1106 53 -1028 143 -168 81 -300 73 -1383 79 -3180 51 -3667 51 -480 57 -2206 53 -532 135 -2097 75 -440 53 -1294 85 -116 113 -484 105 -1614 55 -1106 53 -2197 105 -2796 113 -1504 71 -1038 75 -3274 165 -1598 97 -338 73 -1071 57 -230 115 -2267 331 -1306 85 -648 85 -492 81 -1232 55 -3281 55 -376 97 -3073 83 -3046 143 -978 85 -803 75 -720 107 -988 95 -758 51 -1076 109 -1359 85 -2750 95 -1355 79 -230 71 -312 71 -144 143 -1520 55 -1126 75 -641 77 -632 57 -76 73 -122 119 -1056 95 -1748 51 -918 113 -1070 85 -642 75 -1402 55 -58 159 -482 109 -2875 115 -388 101 -698 55 -554 53 -898 71 -622 95 -472 75 -272 109 -336 189 -1702 123 -184 57 -1217 79 -682 83 -372 137 -430 113 -674 57 -661 75 -1102 81 -1438 115 -976 83 -898 137 -808 53 -2065 151 -3742 55 -3758 55 -414 55 -5521 141 -988 53 -446 131 -142 87 -614 77 -1107 113 -1110 51 -602 85 -1184 75 -654 53 -1724 97 -528 77 -630 85 -58 113 -538 115 -1078 53 -284 55 -576 165 -224 71 -1224 55 -342 55 -462 113 -426 85 -364 171 -1258 107 -837 143 -2620 115 -2844 53 -2348 75 -544 115 -288 85 -898 75 -414 81 -1014 101 -1062 77 -1408 81 -158 113 -908 131 -496 187 -979 51 -484 71 -530 191 -2927 109 -3701 57 -86 85 -1132 57 -892 85 -1671 75 -1040 79 -106 51 -946 71 -RAW_Data: -1552 129 -4484 225 -2645 133 -482 71 -1211 163 -136 143 -144 55 -404 115 -1574 141 -2378 53 -726 75 -102 125 -246 97 -797 53 -624 71 -421 71 -1338 77 -1094 73 -1504 103 -340 51 -256 51 -418 191 -804 55 -368 53 -1197 51 -1778 77 -1496 57 -278 105 -340 111 -2992 141 -1000 221 -1470 79 -2306 57 -1330 81 -3216 77 -282 71 -120 69 -1884 85 -578 55 -775 57 -576 57 -1634 109 -1098 133 -926 113 -1887 85 -58 169 -3933 51 -4166 57 -2099 53 -1490 143 -2902 53 -622 85 -776 75 -412 57 -483 55 -2698 51 -1354 95 -664 53 -1022 57 -1886 53 -3041 85 -1478 55 -2702 299 -312 77 -817 99 -1344 53 -1290 167 -1844 133 -746 103 -313 93 -470 167 -182 79 -892 167 -162 57 -174 55 -836 97 -1160 73 -1863 55 -404 115 -6023 95 -646 53 -2812 53 -864 55 -1194 133 -486 57 -1476 87 -342 101 -1442 119 -178 163 -698 51 -2886 71 -484 71 -378 35173 -52 51 -78 51 -156 77 -106 1092 -702 937 -730 937 -754 913 -754 901 -756 911 -756 911 -758 889 -784 883 -782 887 -780 883 -780 885 -780 885 -780 881 -806 859 -784 887 -782 881 -802 857 -808 859 -806 787 -760 983 -806 855 -802 853 -830 831 -834 829 -830 777 -880 839 -828 857 -806 859 -728 679 -80 161 -834 1073 -590 851 -378 53 -258 71 -818 847 -796 869 -808 861 -790 863 -812 867 -808 857 -812 835 -830 857 -806 859 -806 857 -812 837 -182 51 -606 841 -814 837 -830 833 -834 859 -808 857 -806 857 -806 859 -806 839 -832 859 -806 831 -832 857 -808 857 -808 855 -806 861 -810 863 -806 835 -832 831 -830 857 -808 857 -812 835 -836 831 -834 831 -832 829 -838 829 -832 831 -836 835 -832 831 -832 833 -832 859 -808 831 -834 833 -834 835 -832 833 -832 831 -834 831 -836 831 -832 835 -832 831 -834 831 -836 831 -832 857 -808 859 -808 833 -834 833 -832 831 -834 833 -832 859 -808 833 -836 831 -836 831 -832 831 -836 831 -834 831 -832 833 -834 833 -836 833 -832 833 -830 833 -832 833 -832 831 -832 835 -832 833 -834 857 -812 831 -830 833 -832 833 -834 833 -836 835 -832 829 -834 833 -832 831 -834 857 -808 857 -808 859 -808 833 -832 831 -834 859 -808 833 -834 831 -834 831 -832 859 -808 859 -806 833 -832 859 -808 859 -806 835 -834 831 -832 833 -832 833 -834 857 -808 857 -808 859 -808 857 -808 833 -832 859 -808 833 -836 831 -834 831 -832 833 -836 835 -832 831 -834 831 -834 833 -832 831 -834 857 -806 833 -832 859 -808 833 -832 857 -808 859 -808 831 -834 857 -808 861 -RAW_Data: -808 857 -808 831 -832 859 -806 859 -810 833 -834 857 -806 833 -834 859 -808 831 -834 833 -834 831 -834 831 -836 831 -832 859 -808 833 -832 831 -832 833 -834 857 -810 831 -832 859 -808 857 -808 833 -832 857 -808 859 -806 859 -808 859 -810 833 -832 833 -832 833 -834 831 -834 833 -832 833 -834 833 -832 831 -834 859 -808 857 -808 857 -808 857 -808 833 -834 833 -832 831 -834 857 -808 833 -834 833 -832 833 -832 833 -832 837 -832 833 -836 831 -832 831 -832 857 -810 831 -832 835 -834 857 -808 835 -836 831 -832 831 -836 833 -830 835 -830 835 -834 831 -832 831 -834 831 -834 831 -834 833 -834 857 -808 833 -832 833 -832 835 -834 831 -832 833 -832 859 -806 859 -808 831 -834 859 -806 859 -808 831 -834 859 -806 835 -834 831 -834 831 -832 833 -836 831 -834 833 -832 833 -836 831 -834 831 -832 859 -808 831 -834 857 -812 831 -832 833 -832 857 -808 831 -834 833 -834 857 -810 831 -832 859 -808 857 -808 833 -834 833 -832 833 -832 859 -808 831 -832 833 -834 857 -808 859 -806 859 -806 859 -808 833 -832 861 -808 857 -808 831 -834 833 -832 833 -834 831 -834 857 -808 859 -808 833 -832 831 -832 859 -806 859 -808 859 -808 833 -832 859 -808 857 -808 859 -808 833 -832 857 -808 833 -834 833 -832 833 -832 835 -832 833 -832 833 -832 859 -808 857 -808 859 -806 833 -832 859 -806 859 -808 859 -808 833 -832 857 -808 859 -808 857 -808 857 -808 861 -808 831 -832 833 -832 859 -808 857 -808 859 -806 833 -834 833 -832 859 -806 859 -808 857 -810 831 -832 859 -810 831 -834 857 -808 833 -832 831 -834 859 -806 859 -808 831 -834 859 -806 833 -834 835 -832 1665 -4178 1667 -1644 853 -830 1665 -830 3343 -830 829 -832 833 -2502 831 -1666 3333 -3340 831 -830 829 -834 2499 -832 1665 -2508 4175 -1638 1695 -802 853 -2502 829 -3332 831 -832 829 -832 2499 -832 1669 -2508 4177 -1642 1687 -830 827 -2500 829 -3336 829 -830 831 -832 2503 -834 1671 -2480 4195 -1642 1687 -830 829 -2504 829 -3332 831 -832 829 -832 2501 -832 1669 -2500 4173 -1668 1667 -830 833 -2500 829 -3314 855 -830 829 -832 2499 -832 1665 -2500 4173 -1666 1663 -832 831 -2498 831 -3346 829 -830 829 -832 2505 -832 1665 -2504 4167 -1664 1665 -830 831 -2508 9999 -826 2519 -802 853 -1664 829 -2500 1667 -832 831 -834 1665 -2502 3333 -3334 2503 -1644 2531 -806 853 -832 831 -830 829 -832 831 -832 831 -2504 831 -1666 1673 -830 831 -3340 831 -832 831 -2498 831 -2500 1669 -832 831 -832 835 -830 1671 -7504 1665 -1666 10007 -2472 855 -830 829 -1668 2497 -RAW_Data: -3342 829 -830 831 -832 2503 -830 1667 -2506 4169 -1666 1661 -832 831 -2502 831 -3336 829 -832 829 -832 2505 -830 1671 -2500 4167 -1666 1665 -832 831 -2502 831 -3332 831 -832 831 -834 2507 -834 1663 -2498 4171 -1668 1667 -830 829 -2506 831 -3316 853 -830 829 -832 2503 -830 1665 -2498 4173 -1668 1667 -832 829 -2502 829 -3316 855 -828 829 -836 2501 -832 1665 -2504 4155 -1666 1667 -828 853 -2480 855 -3306 853 -828 831 -832 2503 -830 1669 -2500 4165 -1666 1665 -832 831 -884 239 -84 57 -86 139 -595 115 -58 171 -488 171 -196 109 -560 233 -354 85 -628 83 -170 87 -172 57 -114 201 -936 83 -440 51 -140 257 -1331 73 -1130 143 -850 107 -84 55 -2862 301 -392 57 -3352 109 -518 113 -1454 71 -309 113 -400 55 -1551 81 -3980 51 -2334 77 -2336 81 -818 79 -1622 57 -545 111 -368 57 -2790 141 -230 113 -1418 109 -3084 57 -1836 191 -1407 71 -1556 85 -3456 119 -1138 53 -1830 51 -6980 95 -1186 73 -246 95 -1590 53 -112 55 -1262 85 -1164 53 -1229 190 -1226 71 -314 73 -1102 55 -154 51 -202 111 -2555 55 -1915 85 -1214 85 -174 85 -58 57 -2466 71 -70 55 -1752 95 -1326 237 -1038 165 -74 57 -678 51 -2062 85 -1532 143 -1898 55 -232 55 -1478 93 -1740 85 -196 143 -765 71 -96 71 -780 53 -1918 357 -1807 51 -1054 173 -1584 57 -2294 79 -790 53 -424 71 -1492 69 -3693 85 -710 143 -202 57 -1562 71 -168 71 -1020 147 -1276 85 -2227 97 -192 95 -2028 73 -216 71 -72 95 -230 79 -2310 57 -1124 85 -2510 71 -1798 105 -1173 111 -560 73 -1120 73 -342 71 -144 166 -1704 73 -72 95 -304 57 -3130 85 -9625 119 -2383 113 -486 83 -190 83 -2192 95 -1553 51 -1986 119 -202 135 -1096 81 -2311 201 -2079 97 -894 95 -1782 83 -820 83 -1465 55 -1138 85 -2786 143 -3226 204 -1358 71 -2544 53 -56 105 -934 115 -284 53 -80 71 -896 163 -736 73 -1538 105 -184 101 -686 53 -642 103 -1330 75 -3897 85 -556 51 -602 225 -1666 53 -1428 85 -1193 83 -298 97 -1507 109 -2383 79 -1805 77 -2877 119 -1233 137 -968 55 -1210 85 -196 153 -264 103 -1470 85 -3017 113 -218 73 -2133 57 -1948 109 -594 83 -2816 157 -539 55 -558 55 -112 81 -952 51 -272 57 -444 85 -570 83 -1175 53 -1760 57 -4421 53 -168 111 -232 55 -2752 53 -366 79 -1238 73 -1228 85 -2416 57 -3523 85 -1566 103 -820 113 -765 173 -1354 53 -2122 85 -346 57 -4443 79 -408 53 -290 95 -1484 75 -1052 73 -278 145 -454 77 -164 167 -430 81 -5196 115 -2725 53 -2715 83 -136 119 -457 79 -364 105 -728 175 -1528 57 -RAW_Data: -728 53 -176 163 -2120 71 -1394 71 -488 107 -1404 221 -422 95 -96 201 -356 71 -7701 85 -3729 77 -3237 95 -846 71 -888 51 -322 123 -784 79 -276 105 -1064 77 -652 85 -776 83 -664 77 -3171 107 -112 57 -1733 77 -898 85 -2118 57 -1423 81 -616 73 -2090 55 -2730 85 -6676 85 -317 85 -632 79 -526 101 -1637 192 -700 107 -314 55 -608 109 -388 217 -2209 85 -116 143 -1837 143 -4598 79 -1406 87 -972 71 -1750 85 -882 167 -1105 75 -5685 57 -672 55 -2931 113 -730 109 -196 87 -86 85 -1168 51 -1041 57 -404 71 -2115 155 -880 85 -719 51 -216 53 -660 113 -1412 73 -2408 97 -2971 57 -2495 170 -2786 87 -2076 55 -2939 53 -1032 111 -258 115 -500 81 -1947 113 -580 85 -1656 75 -1528 79 -324 143 -1110 79 -274 143 -796 53 -460 83 -1480 53 -1874 119 -192 97 -676 71 -1558 55 -3100 79 -1702 53 -618 57 -1091 71 -854 119 -1086 53 -2373 57 -1252 77 -136 87 -4334 133 -1292 85 -304 53 -556 71 -668 157 -1981 109 -792 77 -446 57 -226 77 -296 51 -202 101 -1104 81 -1782 99 -414 95 -1043 121 -2439 109 -1296 103 -3848 55 -116 85 -690 85 -1092 105 -1873 83 -966 75 -140 87 -944 53 -2992 81 -755 101 -376 79 -2732 113 -422 53 -964 51 -396 57 -7268 75 -314 95 -826 105 -1332 71 -460 195 -740 53 -434 51 -104 85 -2272 135 -1858 57 -172 201 -200 113 -4127 129 -176 69 -712 79 -6216 57 -2069 73 -1370 51 -3561 143 -1895 95 -566 51 -1440 95 -1062 57 -791 57 -116 85 -1240 107 -1216 57 -3492 105 -3270 57 -548 57 -250 97 -2722 79 -1640 115 -1928 57 -425 85 -1278 85 -698 53 -534 85 -685 85 -3646 141 -1378 57 -562 192 -400 111 -622 133 -208 51 -1396 111 -924 143 -1442 95 -1145 95 -72 81 -803 107 -112 55 -692 57 -2961 107 -1823 83 -3286 51 -372 97 -1180 71 -192 167 -96 95 -862 79 -1030 77 -394 176 -1455 55 -1594 53 -400 157 -334 57 -678 77 -522 217 -254 77 -315 137 -116 85 -172 57 -198 171 -378 71 -743 55 -860 79 -483 57 -230 201 -1650 71 -326 53 -2747 95 -576 165 -1854 57 -2763 57 -440 53 -348 57 -772 135 -198 55 -2280 51 -1903 85 -1666 143 -1120 55 -1507 75 -166 115 -248 87 -142 53 -2238 147 -332 53 -1842 55 -670 201 -1078 53 -112 57 -1012 167 -336 51 -522 51 -1330 115 -1824 55 -776 85 -806 139 -216 95 -1318 85 -548 85 -1647 83 -398 51 -1452 95 -1982 143 -1146 267 -2308 107 -656 57 -757 83 -742 77 -414 107 -312 77 -864 53 -112 85 -564 85 -2234 113 -5404 95 -RAW_Data: -1490 57 -374 83 -1644 115 -110 71 -478 71 -72 71 -1140 135 -2792 95 -2703 129 -168 85 -1668 133 -1521 53 -914 51 -4717 79 -1724 57 -172 115 -746 127 -394 83 -674 169 -564 71 -2250 119 -1603 51 -464 77 -2220 85 -566 55 -144 143 -116 115 -487 115 -282 129 -1690 111 -706 129 -1429 55 -223 113 -1228 53 -544 57 -114 57 -698 77 -2874 55 -232 113 -254 57 -1523 159 -72 101 -2285 51 -166 85 -315 57 -2125 53 -758 55 -478 107 -1725 53 -5851 83 -1616 101 -339 55 -230 109 -188 77 -652 51 -478 113 -3742 55 -230 57 -144 173 -754 143 -480 71 -2526 179 -4037 57 -1720 75 -432 133 -1464 165 -1268 215 -154 107 -398 87 -3083 85 -144 87 -512 71 -222 113 -487 85 -2428 81 -1648 119 -1226 95 -1213 103 -1275 57 -702 81 -1908 55 -542 55 -1838 109 -148 71 -1286 95 -3248 55 -639 71 -144 71 -598 119 -3930 171 -2344 55 -2623 87 -168 79 -2264 141 -1018 83 -344 115 -426 109 -518 85 -756 55 -2340 55 -1679 85 -200 85 -6170 51 -590 224 -1233 85 -260 107 -6039 57 -2192 113 -1496 109 -1146 55 -174 85 -848 75 -1242 111 -1068 85 -2111 81 -542 95 -1350 75 -144 95 -96 105 -362 105 -340 57 -864 115 -1568 73 -722 71 -2762 249 -382 113 -1979 75 -1233 143 -342 81 -3036 51 -226 87 -572 55 -282 57 -288 103 -3244 83 -242 99 -1748 55 -1206 172 -276 79 -2060 167 -904 123 -770 79 -1816 115 -202 113 -1526 137 -604 85 -2234 73 -300 113 -1504 95 -1114 55 -590 75 -410 103 -1314 83 -590 77 -3440 167 -3132 55 -224 75 -2107 57 -144 123 -842 155 -813 75 -602 71 -3652 141 -2380 57 -224 249 -164 189 -530 75 -493 51 -130 51 -126 95 -670 51 -340 81 -1084 97 -216 115 -1284 57 -1200 85 -310 55 -1002 71 -1455 79 -720 85 -1140 119 -288 95 -944 119 -554 53 -2450 73 -324 79 -836 105 -1087 73 -418 105 -528 139 -468 121 -582 167 -4205 71 -1858 183 -2318 143 -780 101 -98 187 -136 55 -2239 57 -434 103 -1946 113 -86 57 -428 53 -222 57 -86 143 -1036 107 -1232 77 -606 113 -1394 115 -86 57 -2204 75 -456 57 -1623 173 -2832 53 -2464 228 -609 141 -58 95 -847 77 -488 95 -734 51 -882 95 -4004 143 -1242 97 -322 77 -218 109 -2283 85 -116 85 -1281 55 -228 173 -820 77 -2298 71 -2057 115 -116 85 -760 143 -489 57 -2702 139 -1380 143 -583 143 -658 99 -918 141 -1033 55 -262 85 -1408 143 -1979 75 -872 57 -268 219 -594 107 -172 105 -222 71 -448 51 -806 101 -540 51 -108 115 -1558 147 -418 79 -206 71 -RAW_Data: -514 107 -1090 125 -928 77 -172 153 -382 71 -742 73 -1060 133 -3547 71 -992 85 -890 119 -72 81 -1154 145 -122 51 -198 57 -1288 173 -2424 83 -260 55 -278 105 -926 55 -274 75 -1555 57 -538 137 -2713 71 -1082 57 -1110 57 -670 83 -494 105 -1504 77 -750 81 -1226 57 -2030 79 -258 57 -140 101 -858 57 -400 55 -2286 55 -3390 55 -542 103 -404 181 -316 57 -174 85 -3657 51 -162 81 -735 105 -518 87 -2452 51 -252 165 -1942 73 -74 73 -1386 113 -3530 181 -1312 57 -1262 115 -314 133 -2418 57 -4028 71 -182 121 -747 83 -2793 167 -686 85 -1814 171 -1984 109 -1592 101 -1250 57 -346 113 -1370 99 -2761 53 -1930 51 -280 57 -1590 71 -1105 79 -702 143 -1216 235 -3208 57 -1998 77 -859 119 -608 51 -1084 143 -709 81 -1286 85 -144 265 -1274 53 -2068 75 -3092 77 -232 55 -144 57 -1836 57 -258 115 -1507 85 -2022 109 -1121 71 -893 93 -696 119 -951 57 -710 75 -844 95 -396 109 -1323 83 -1878 85 -272 51 -1108 179 -584 147 -196 71 -4320 57 -904 81 -776 55 -1857 55 -712 111 -54 81 -1338 121 -510 57 -698 85 -396 55 -1239 57 -892 75 -146 71 -358 71 -278 113 -2876 105 -671 85 -316 269 -1362 81 -342 57 -2932 55 -1391 111 -202 57 -576 55 -910 113 -318 57 -586 55 -719 143 -256 113 -2136 81 -802 71 -1190 95 -144 71 -2156 71 -730 55 -2380 87 -977 83 -172 95 -1322 57 -1160 71 -144 147 -1028 83 -898 53 -878 87 -114 115 -1296 75 -476 171 -1442 53 -1705 103 -2318 57 -3989 73 -414 73 -1012 51 -1492 55 -1992 87 -2711 83 -484 77 -760 105 -1338 53 -56 81 -590 57 -316 169 -4225 105 -1172 109 -744 115 -394 51 -4057 107 -531 87 -734 83 -1522 55 -1895 55 -2121 83 -1574 265 -1181 83 -2434 71 -1888 105 -734 167 -2886 57 -114 135 -2099 79 -854 71 -3982 51 -440 229 -654 57 -2316 57 -266 97 -536 75 -2106 53 -937 149 -2691 85 -838 57 -506 51 -108 57 -1044 85 -760 57 -2398 73 -2428 75 -1016 81 -138 85 -88 57 -1134 235 -1612 113 -2849 71 -1517 77 -434 127 -192 57 -1992 95 -232 107 -3254 53 -1442 57 -86 107 -1024 95 -854 53 -534 85 -2618 81 -2344 115 -2866 111 -998 71 -3113 53 -1341 55 -1216 103 -246 85 -3100 260 -450 85 -3023 55 -372 167 -86 57 -1094 51 -1499 85 -1252 57 -546 51 -2099 191 -172 85 -1829 71 -1259 97 -1506 85 -1928 143 -900 143 -858 115 -942 81 -2027 53 -1292 115 -1010 119 -852 55 -342 51 -80 81 -470 101 -636 55 -1046 85 -576 81 -976 79 -188 95 -947 81 -RAW_Data: -1480 55 -1430 55 -596 171 -1583 95 -884 51 -1116 121 -276 143 -2098 51 -1172 73 -1918 111 -112 51 -220 57 -230 163 -740 51 -1074 57 -1608 55 -3299 57 -2876 77 -1268 85 -771 79 -386 77 -1618 71 -1308 55 -863 121 -938 75 -110 83 -1176 187 -1807 57 -2894 55 -344 285 -994 55 -836 51 -963 85 -202 263 -686 55 -853 57 -144 115 -658 127 -955 57 -116 85 -944 111 -1891 57 -460 311 -1531 129 -146 121 -1301 71 -312 75 -600 77 -1760 71 -1264 85 -2345 51 -294 111 -772 51 -78 75 -292 71 -542 113 -690 85 -1164 79 -1368 57 -1284 53 -130 182 -240 53 -114 57 -287 143 -304 75 -754 169 -130 83 -54 85 -527 71 -1657 71 -462 383 -560 115 -638 131 -3943 285 -264 75 -168 85 -3999 83 -996 73 -644 71 -1205 71 -1108 85 -172 87 -1139 139 -518 167 -1278 123 -1125 75 diff --git a/assets/unit_tests/subghz/test_random_raw.sub b/assets/unit_tests/subghz/test_random_raw.sub index c9440dbbb..949a44458 100644 --- a/assets/unit_tests/subghz/test_random_raw.sub +++ b/assets/unit_tests/subghz/test_random_raw.sub @@ -166,34 +166,6 @@ RAW_Data: 317 -144 57 -486 53 -282 115 -585 97 -72 229 -174 257 -440 225 -86 173 RAW_Data: 191 -240 85 -72 637 -408 213 -510 261 -168 143 -126 79 -106 167 -72 117 -218 251 -168 119 -96 215 -182 191 -238 517 -116 201 -144 255 -154 97 -94 215 -72 95 -120 71 -288 261 -106 434 -96 606 -232 229 -432 85 -174 343 -58 329 -156 55 -116 259 -144 488 -56 307 -339 115 -202 334 -88 113 -86 57 -174 143 -144 401 -376 85 -240 267 -82 95 -216 137 -158 85 -144 143 -58 221 -308 295 -114 87 -114 301 -120 358 -517 71 -262 191 -144 57 -140 165 -407 53 -262 217 -120 238 -358 119 -357 71 -72 119 -96 428 -72 95 -72 167 -72 93 -240 335 -96 357 -240 173 -230 143 -114 87 -200 143 -232 287 -150 97 -288 71 -72 93 -288 115 -58 143 -230 109 -264 71 -72 119 -72 238 -242 97 -78 163 -86 115 -518 79 -560 205 -449 969 -144 507 -86 231 -114 345 -58 979 -110 85 -288 287 -404 229 -202 57 -274 233 -86 115 -202 632 -230 85 -312 369 -392 460 -450 75 -280 85 -202 201 -86 229 -174 143 -144 233 -528 115 -212 127 -202 287 -172 403 -172 139 -128 165 -138 261 -392 143 -480 142 -189 291 -80 53 -283 167 -140 113 -1008 191 -144 119 -120 71 -193 241 -462 201 -58 143 -344 539 -316 113 -174 85 -116 113 -250 239 -168 405 -168 239 -158 85 -144 115 -86 57 -86 341 -144 171 -202 85 -202 115 -114 719 -88 55 -318 257 -56 254 -86 171 -116 459 -174 171 -329 95 -134 85 -314 431 -306 77 -316 401 -86 173 -404 281 -1073 488 -94 217 -78 101 -98 214 -120 215 -340 403 -535 143 -564 115 -116 199 -58 85 -174 315 -58 335 -136 55 -260 143 -144 229 -460 143 -58 143 -144 171 -202 115 -374 291 -130 339 -82 143 -58 171 -58 201 -86 85 -174 1022 -56 85 -82 255 -240 103 -202 431 -278 95 -216 119 -72 71 -96 71 -559 57 -144 171 -88 113 -86 231 -414 131 -192 237 -360 95 -168 145 -168 213 -120 167 -96 143 -110 57 -86 259 -56 87 -777 295 -96 57 -86 173 -86 171 -404 143 -172 231 -200 57 -441 55 -58 173 -56 87 -86 171 -72 287 -72 119 -262 119 -144 71 -72 121 -310 71 -302 113 -54 193 -80 307 -58 257 -232 143 -56 143 -116 219 -72 695 -70 71 -460 85 -232 719 -363 57 -402 604 -230 287 -138 83 -172 259 -58 171 -174 55 -88 489 -114 143 -116 171 -116 143 -58 199 -144 145 -343 374 -186 235 -140 77 -86 143 -202 143 -144 113 -144 143 -58 732 -96 263 -264 71 -206 95 -168 215 -144 271 -80 139 -88 85 -414 75 -100 RAW_Data: 285 -96 627 -362 53 -84 201 -374 113 -202 115 -202 421 -316 85 -58 139 -224 87 -86 229 -58 243 -178 267 -288 95 -336 171 -96 213 -288 71 -405 95 -96 95 -384 95 -72 213 -72 95 -96 95 -272 87 -1083 85 -58 113 -88 257 -116 143 -292 175 -318 95 -120 95 -144 95 -72 71 -216 368 -116 373 -172 115 -58 85 -116 143 -86 85 -144 201 -86 201 -202 257 -144 201 -174 113 -144 115 -144 257 -202 585 -364 173 -138 287 -422 431 -86 85 -96 869 -186 95 -52 115 -86 115 -58 55 -276 365 -86 85 -489 171 -140 577 -106 718 -144 391 -232 195 -82 143 -172 109 -120 167 -96 280 -216 145 -240 215 -186 163 -96 141 -172 159 -603 257 -108 629 -192 119 -80 87 -172 57 -144 286 -86 57 -230 344 -58 113 -537 75 -96 537 -86 403 -196 167 -264 119 -238 119 -120 167 -96 95 -478 95 -120 167 -216 1085 -96 358 -72 263 -72 69 -120 143 -96 71 -96 191 -362 55 -144 57 -260 113 -58 85 -174 55 -88 257 -86 231 -194 55 -58 115 -56 55 -339 55 -58 374 -172 139 -82 419 -98 119 -261 71 -72 71 -240 713 -86 143 -218 295 -72 53 -56 431 -58 317 -144 161 -144 373 -144 173 -144 57 -114 85 -116 195 -72 708 -172 115 -86 191 -96 506 -120 71 -174 85 -58 363 -114 317 -230 316 -200 87 -114 57 -230 115 -315 173 -280 694 -212 453 -256 143 -202 113 -540 352 -116 257 -116 457 -56 109 -58 143 -230 259 -144 259 -525 119 -408 247 -112 389 -72 431 -96 137 -236 97 -474 201 -298 71 -82 55 -116 55 -112 199 -174 191 -86 143 -144 115 -114 317 -86 85 -230 87 -114 259 -84 107 -130 143 -94 153 -86 135 -94 215 -72 239 -94 435 -96 263 -142 166 -334 87 -194 179 -96 115 -284 135 -56 57 -144 463 -204 143 -316 201 -58 403 -86 141 -288 85 -202 139 -397 171 -174 305 -202 85 -144 373 -253 161 -492 181 -191 95 -216 315 -191 71 -166 97 -126 337 -96 71 -96 189 -168 295 -84 197 -86 259 -345 137 -144 167 -796 115 -344 455 -72 119 -96 119 -550 209 -88 85 -86 143 -340 167 -260 143 -537 85 -226 51 -537 57 -260 315 -461 51 -84 199 -358 383 -96 143 -257 115 -86 173 -86 201 -144 143 -316 85 -86 479 -88 85 -72 71 -104 115 -116 267 -72 137 -144 143 -116 85 -86 373 -288 115 -200 87 -114 259 -114 259 -462 143 -144 171 -86 57 -58 137 -144 57 -634 343 -72 205 -86 143 -258 57 -232 113 -230 461 -58 185 -74 537 -86 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: -3490 143 -230 115 -1140 55 -2348 79 -3316 287 -228 95 -344 561 -286 105 -1026 137 -1108 81 -1090 71 -2211 57 -1010 55 -1276 113 -346 85 -706 169 -628 57 -2928 129 -2574 109 -1400 57 -1444 87 -824 95 -240 53 -3650 55 -202 143 -528 229 -216 57 -144 113 -2682 55 -1422 57 -1619 57 -1332 111 -402 57 -968 57 -500 55 -3277 71 -856 79 -1009 77 -2136 53 -550 71 -986 85 -1403 55 -244 53 -763 85 -2413 173 -1410 51 -114 85 -594 77 -584 191 -1102 95 -7897 125 -1584 77 -442 73 -384 95 -1774 55 -2588 69 -264 55 -3624 95 -6776 51 -102 99 -344 143 -716 55 -174 57 -1440 143 -312 97 -312 143 -1891 85 -194 99 -1696 55 -1188 95 -192 69 -102 75 -2378 95 -646 119 -828 85 -1766 163 -1440 55 -82 111 -56 55 -567 83 -3209 81 -1140 115 -5439 105 -82 113 -1308 55 -6938 71 -122 73 -520 79 -490 73 -922 81 -1250 229 -374 83 -614 57 -458 81 -1300 131 -668 139 -116 55 -414 55 -2304 51 -2456 85 -922 109 -490 141 -58 55 -376 55 -998 191 -1294 57 -1693 159 -678 55 -416 57 -344 141 -4490 75 -122 167 -1774 143 -1152 101 -3062 57 -1351 85 -402 111 -814 83 -1321 97 -406 71 -384 121 -670 55 -1892 221 -2230 51 -1713 143 -1525 55 -340 107 -1332 57 -2671 55 -576 85 -202 57 -338 115 -768 79 -1112 71 -1070 51 -538 113 -488 109 -162 77 -980 57 -1915 87 -2141 95 -2272 171 -5042 53 -3974 55 -902 85 -274 81 -160 55 -58 57 -448 77 -1394 51 -316 119 -2322 115 -1996 51 -3265 85 -634 57 -2308 51 -250 137 -8089 57 -3623 77 -268 282 -782 225 -506 77 -280 57 -468 107 -2886 55 -1378 135 -760 75 -3226 53 -154 51 -86 143 -1810 141 -943 111 -503 113 -1226 83 -144 57 -876 253 -292 77 -832 113 -316 143 -580 57 -1121 81 -2010 201 -326 186 -508 167 -1156 167 -644 85 -895 53 -1525 71 -2115 55 -844 95 -168 167 -406 97 -168 119 -266 165 -2264 57 -4167 115 -2244 53 -2009 115 -3916 143 -598 143 -1500 243 -1302 197 -1161 75 -359 153 -1117 71 -1622 159 -222 71 -70 119 -2112 163 -2066 107 -1291 113 -5859 57 -3327 71 -688 197 -3065 171 -344 171 -200 143 -1398 55 -1143 77 -214 81 -2840 143 -2109 199 -1229 99 -1354 190 -1710 73 -254 81 -874 85 -54 135 -220 115 -2313 81 -964 159 -1802 131 -284 53 -786 71 -482 135 -114 115 -814 53 -3491 51 -378 143 -2783 113 -572 85 -288 105 -1975 53 -1412 223 -144 71 -616 83 -2279 57 -456 83 -1340 51 -1635 127 -472 57 -1694 57 -1264 55 -966 53 -1601 71 -2584 57 -RAW_Data: -1052 225 -162 83 -2002 57 -2246 75 -2092 119 -2672 101 -897 113 -116 85 -1560 75 -2231 51 -896 73 -146 95 -1028 107 -1309 101 -1154 103 -1168 201 -114 57 -1050 55 -742 81 -1238 113 -1196 53 -700 75 -5446 79 -1340 51 -106 53 -160 71 -144 97 -3165 69 -192 85 -1526 69 -1010 71 -516 143 -2207 57 -1529 85 -1710 57 -288 57 -514 57 -1056 147 -156 143 -114 77 -828 57 -676 95 -1076 71 -1620 53 -532 101 -2202 129 -2282 143 -432 85 -836 85 -3336 51 -220 71 -250 79 -936 81 -270 111 -1981 71 -1910 251 -392 139 -58 57 -687 151 -764 113 -1012 79 -258 85 -144 85 -995 83 -2472 55 -508 57 -2619 171 -334 51 -604 53 -3071 71 -200 51 -128 199 -460 113 -556 83 -2625 51 -1290 51 -438 95 -144 119 -262 73 -122 71 -1368 75 -634 253 -1314 95 -2198 53 -844 85 -288 114 -112 109 -86 143 -878 141 -1058 57 -86 57 -826 85 -172 171 -490 57 -2034 155 -430 103 -334 87 -344 53 -1494 169 -276 57 -340 51 -3721 77 -172 51 -1163 85 -644 75 -2714 99 -290 147 -150 51 -1722 55 -644 85 -2486 167 -558 85 -586 57 -2266 171 -852 53 -672 79 -1480 95 -2830 237 -268 111 -576 95 -240 73 -256 101 -200 93 -612 53 -2921 57 -608 51 -82 57 -894 71 -270 53 -1584 75 -868 85 -607 53 -874 151 -1701 55 -1616 85 -2660 57 -2182 71 -390 51 -1392 169 -1503 85 -144 55 -656 51 -156 97 -194 117 -781 71 -292 51 -1036 75 -973 55 -2634 81 -106 103 -1802 129 -406 71 -964 53 -1933 79 -1157 55 -174 57 -1697 75 -204 119 -5124 75 -556 57 -174 113 -1098 229 -116 57 -102 97 -378 57 -144 57 -562 107 -682 55 -1079 121 -1050 51 -560 201 -138 55 -84 113 -2505 71 -144 69 -1764 121 -312 97 -1140 111 -1344 107 -1406 71 -405 51 -1736 55 -1672 103 -100 38491 -672 989 -704 937 -730 935 -756 911 -754 885 -780 911 -754 883 -780 885 -808 857 -808 859 -806 861 -808 859 -806 859 -784 885 -782 883 -808 857 -806 859 -806 859 -806 861 -806 857 -808 857 -808 859 -806 857 -808 857 -808 857 -808 861 -808 859 -806 861 -806 859 -808 859 -806 857 -808 857 -806 859 -808 859 -806 833 -832 859 -808 833 -832 835 -832 833 -832 859 -808 831 -836 831 -834 833 -832 831 -832 833 -832 835 -836 831 -834 833 -832 857 -808 833 -832 833 -832 835 -832 835 -832 831 -832 857 -810 831 -832 859 -808 857 -808 835 -836 833 -832 831 -832 831 -834 833 -832 859 -808 835 -836 831 -832 833 -832 831 -834 833 -832 831 -834 857 -808 859 -RAW_Data: -808 831 -832 833 -832 859 -808 857 -810 831 -834 859 -806 833 -834 857 -806 833 -832 859 -806 859 -808 859 -810 833 -832 831 -834 859 -806 833 -834 857 -808 831 -834 833 -834 833 -832 833 -836 833 -834 829 -832 859 -806 859 -808 833 -832 859 -808 831 -834 857 -806 835 -832 833 -834 859 -806 857 -810 833 -832 857 -808 833 -834 831 -834 833 -834 831 -832 833 -834 857 -808 857 -808 833 -834 835 -834 833 -832 831 -834 857 -806 833 -832 835 -832 833 -834 833 -832 835 -834 831 -834 831 -834 831 -832 833 -832 833 -834 835 -834 831 -832 833 -832 833 -834 833 -832 833 -832 831 -834 857 -808 859 -808 831 -834 831 -834 859 -808 831 -834 857 -808 859 -806 859 -808 857 -810 857 -808 857 -808 835 -834 835 -832 831 -832 835 -832 835 -834 833 -832 831 -830 833 -832 857 -808 859 -808 833 -832 833 -832 833 -834 831 -834 859 -808 831 -834 833 -832 859 -810 833 -832 831 -832 859 -806 859 -808 833 -832 833 -834 833 -832 833 -832 833 -832 857 -808 859 -808 833 -832 857 -808 859 -810 857 -808 831 -832 835 -834 831 -834 833 -832 833 -832 833 -834 857 -808 859 -806 859 -808 835 -834 831 -836 833 -834 831 -834 831 -832 831 -832 831 -834 835 -832 857 -806 859 -806 835 -834 857 -808 857 -808 857 -808 859 -806 859 -808 835 -832 831 -832 859 -808 833 -832 833 -834 833 -832 835 -834 833 -832 833 -834 831 -832 833 -834 831 -832 835 -832 835 -832 831 -832 857 -808 859 -808 859 -806 859 -808 835 -832 833 -832 831 -834 857 -808 833 -832 833 -834 833 -832 833 -832 859 -808 831 -834 859 -806 835 -834 835 -832 831 -836 831 -832 831 -832 859 -806 835 -832 859 -808 831 -834 831 -834 857 -808 833 -832 835 -834 831 -832 859 -808 831 -834 859 -808 833 -832 857 -808 857 -810 833 -834 833 -832 831 -832 859 -808 831 -834 833 -832 859 -808 857 -808 833 -836 831 -834 831 -832 833 -832 859 -808 831 -832 837 -834 833 -832 833 -834 831 -832 831 -834 857 -808 833 -832 859 -808 831 -834 859 -806 833 -834 833 -836 831 -832 833 -832 833 -834 859 -808 831 -832 831 -834 859 -806 859 -808 857 -808 859 -806 833 -834 857 -808 859 -806 835 -832 859 -808 857 -808 857 -808 857 -808 857 -808 859 -808 859 -812 831 -832 831 -832 833 -834 833 -832 857 -808 833 -836 831 -832 857 -808 835 -836 833 -832 831 -832 831 -836 835 -832 831 -834 831 -836 831 -832 857 -808 833 -832 835 -834 831 -832 833 -832 835 -832 1665 -4168 1665 -1668 831 -RAW_Data: -834 1669 -832 3333 -832 833 -832 831 -2500 833 -1666 3347 -3332 829 -830 831 -830 2501 -834 1665 -2502 4171 -1664 1667 -830 831 -2498 831 -3332 831 -836 835 -832 2499 -830 1671 -2510 4155 -1664 1687 -802 853 -2498 829 -3318 857 -828 829 -830 2503 -834 1667 -2502 4163 -1664 1665 -832 829 -2500 857 -3308 831 -836 835 -832 2497 -834 1667 -2504 4171 -1664 1663 -830 833 -2502 13329 -826 851 -3328 825 -826 853 -828 833 -2500 1669 -830 2503 -832 829 -2500 829 -830 1665 -1668 1665 -1668 829 -832 833 -4160 4175 -1668 1665 -1666 853 -4970 853 -1664 5847 -1634 2521 -828 1663 -832 831 -3334 829 -832 831 -836 2499 -832 1671 -2484 4193 -1638 1689 -832 829 -2500 829 -3338 829 -832 833 -832 2499 -832 1669 -2502 4173 -1664 1663 -832 829 -2508 833 -3332 831 -832 831 -832 2505 -832 1665 -2502 4163 -1664 1669 -830 831 -2502 833 -3340 827 -832 829 -834 2503 -832 1665 -2500 4177 -1662 1665 -830 831 -2482 857 -3312 857 -830 829 -830 2501 -834 1663 -2504 4173 -1638 1687 -832 831 -2502 829 -3332 833 -830 831 -832 2505 -832 1663 -2508 4167 -1664 1669 -832 833 -2502 831 -3334 829 -830 829 -832 2501 -832 1667 -2504 4169 -1670 1663 -832 831 -2500 831 -3332 833 -832 831 -832 2497 -834 1663 -2500 4175 -1664 1665 -832 831 -2508 829 -3338 831 -832 831 -830 2501 -832 1665 -2502 4165 -1666 1667 -832 831 -780 53 -1368 57 -86 143 -200 57 -374 57 -130 165 -374 85 -808 85 -196 127 -1010 51 -138 85 -58 77 -174 85 -228 113 -716 95 -520 71 -2831 53 -618 53 -188 83 -82 111 -114 55 -5464 71 -966 95 -1168 71 -824 71 -480 97 -72 101 -612 55 -3330 73 -1995 85 -832 53 -1016 57 -1462 71 -256 75 -776 143 -2066 77 -1748 71 -4832 81 -602 55 -3866 131 -1824 57 -1836 113 -1234 85 -836 73 -560 253 -1606 143 -286 351 -1008 87 -528 107 -1357 256 -1538 233 -1018 71 -264 77 -304 71 -1832 57 -144 85 -858 83 -511 131 -1220 107 -1318 85 -1782 81 -72 73 -7838 51 -696 55 -3805 125 -930 103 -292 95 -382 115 -3256 85 -859 57 -787 220 -1207 143 -2068 143 -2322 97 -548 53 -386 57 -130 51 -1673 57 -3004 71 -783 71 -1560 99 -102 51 -1068 81 -348 83 -2003 51 -2860 77 -2680 57 -4432 81 -936 95 -400 73 -583 71 -2842 97 -1656 81 -146 73 -286 105 -714 141 -2236 53 -1503 81 -200 85 -836 71 -356 109 -1310 75 -616 57 -690 87 -566 79 -727 73 -1604 97 -1438 57 -2641 77 -1650 217 -873 71 -890 109 -2042 163 -3562 107 -3098 111 -904 79 -238 51 -2699 71 -736 53 -488 233 -400 103 -340 81 -2933 53 -1508 51 -1886 53 -388 125 -3336 143 -RAW_Data: -390 99 -1386 71 -1092 97 -1150 51 -506 111 -2575 87 -114 143 -662 57 -644 53 -550 119 -274 85 -1267 55 -1278 71 -1622 117 -2082 77 -1316 73 -1882 55 -1575 71 -424 85 -1031 133 -2367 85 -542 115 -166 113 -2671 149 -256 113 -260 83 -1966 141 -430 71 -1844 127 -4269 57 -840 111 -1598 85 -3530 55 -2263 57 -1302 57 -346 85 -516 83 -2989 51 -334 83 -1811 85 -230 57 -836 55 -678 55 -7081 55 -288 115 -2062 55 -300 189 -738 57 -814 119 -1438 53 -2077 115 -631 145 -206 57 -645 79 -4121 115 -490 53 -186 71 -144 69 -2537 105 -932 53 -1136 55 -3124 57 -1873 125 -4551 77 -3337 57 -1122 57 -622 103 -2437 51 -120 71 -3629 129 -2139 77 -104 99 -1057 95 -416 71 -72 95 -312 95 -1052 115 -342 111 -2452 71 -746 221 -1418 57 -3125 119 -724 55 -950 57 -618 57 -259 107 -210 95 -1046 71 -454 71 -1651 71 -442 71 -312 79 -1169 55 -202 85 -632 57 -144 115 -412 69 -304 85 -2125 53 -998 125 -200 105 -1126 115 -1116 71 -604 85 -174 171 -260 171 -308 71 -120 95 -354 53 -1461 69 -863 121 -1696 55 -3336 79 -2306 83 -754 97 -160 171 -1612 139 -417 79 -2792 73 -498 85 -2112 57 -1370 57 -460 85 -482 55 -1946 113 -682 101 -136 85 -574 77 -838 53 -657 71 -3562 71 -588 97 -252 71 -366 57 -658 85 -1378 53 -168 85 -2896 167 -1412 87 -1684 53 -2008 97 -1861 83 -1251 71 -678 95 -192 71 -930 81 -1380 51 -572 81 -2795 105 -122 95 -1038 85 -1116 117 -1799 129 -356 83 -712 133 -1584 119 -1892 81 -672 143 -116 85 -996 57 -501 57 -450 85 -216 57 -2182 57 -1100 73 -456 85 -260 85 -144 167 -300 57 -202 55 -448 81 -764 143 -1707 79 -3204 53 -108 53 -54 190 -682 87 -1070 53 -1230 55 -112 87 -1014 141 -1659 57 -230 115 -230 57 -750 55 -686 57 -216 87 -4642 51 -4416 95 -2575 111 -1607 53 -368 107 -4757 51 -521 75 -1682 255 -2346 115 -1762 83 -3457 71 -1470 77 -246 157 -1678 55 -418 99 -922 113 -218 123 -260 55 -1384 105 -240 53 -848 53 -1810 79 -648 77 -664 85 -298 109 -86 55 -552 55 -260 57 -913 115 -504 77 -537 53 -3159 105 -3371 51 -138 53 -2668 71 -368 51 -674 55 -280 51 -1048 51 -963 85 -1000 57 -954 113 -1190 53 -1456 57 -200 115 -172 57 -864 57 -288 85 -442 127 -72 145 -584 71 -2799 143 -324 95 -120 121 -1873 51 -658 57 -1285 57 -1516 330 -172 143 -316 87 -200 115 -700 197 -4960 57 -1358 53 -2324 55 -2864 179 -1799 73 -3669 57 -316 85 -RAW_Data: -935 57 -864 115 -740 79 -220 87 -400 81 -1238 242 -1720 169 -176 125 -2132 57 -1227 119 -2810 107 -420 95 -192 75 -74 107 -3191 171 -914 81 -2426 57 -860 157 -618 115 -1462 107 -146 51 -1007 115 -1080 71 -803 75 -1619 55 -1492 57 -1171 53 -1050 57 -538 113 -2006 85 -2537 83 -1490 55 -598 109 -670 139 -166 55 -154 71 -1215 85 -1630 167 -390 127 -840 115 -288 57 -614 97 -397 71 -494 55 -74 73 -74 71 -806 71 -78 247 -656 105 -1176 73 -144 95 -1100 191 -190 135 -114 87 -1290 119 -506 191 -860 115 -3636 141 -6062 83 -2120 55 -392 77 -2020 55 -1316 51 -424 183 -448 225 -474 97 -1068 75 -294 81 -1090 85 -196 57 -2221 113 -458 115 -3219 141 -2086 85 -374 87 -834 105 -470 51 -1338 53 -1401 85 -604 142 -170 109 -848 55 -200 53 -1480 77 -326 51 -9597 57 -341 107 -959 57 -272 51 -4922 75 -78 195 -249 53 -1368 53 -236 127 -3235 105 -174 113 -2854 55 -1140 85 -374 55 -2335 57 -1164 55 -314 55 -368 51 -2454 57 -5231 55 -1868 103 -868 93 -498 71 -2701 119 -851 85 -810 131 -1466 111 -714 57 -1588 113 -846 53 -2596 105 -200 87 -772 75 -128 75 -1739 191 -4925 79 -144 139 -208 71 -348 79 -538 131 -880 143 -1048 155 -1850 109 -170 57 -1014 133 -906 85 -774 57 -1400 103 -1881 53 -2970 147 -554 95 -178 107 -622 85 -674 113 -1442 55 -3090 127 -488 51 -164 85 -4087 113 -1427 77 -6221 57 -3815 81 -488 109 -992 57 -1423 71 -434 51 -2449 95 -542 57 -594 53 -1282 133 -5219 71 -1574 87 -1658 53 -198 85 -628 105 -120 129 -804 137 -5041 171 -1264 125 -1245 129 -168 97 -146 71 -1196 55 -1194 57 -58 137 -86 85 -1885 101 -328 51 -5524 143 -2082 85 -2325 85 -1225 109 -784 55 -200 55 -562 55 -1644 77 -598 85 -1495 51 -882 161 -2568 105 -RAW_Data: -1288 57 -2688 205 -698 57 -434 51 -632 225 -188 109 -1032 129 -636 285 -1055 77 -204 95 -240 79 -462 169 -2442 57 -202 85 -478 57 -1511 55 -114 113 -444 169 -216 79 -124 73 -1030 141 -418 143 -3631 55 -836 129 -506 79 -954 139 -1470 85 -536 139 -416 53 -944 111 -1544 73 -562 55 -3102 71 -2248 85 -836 57 -3044 77 -562 53 -52 75 -480 53 -1200 71 -216 95 -974 71 -2312 83 -1609 51 -848 55 -174 143 -826 107 -4127 57 -1667 57 -498 77 -1660 55 -751 149 -152 193 -1474 99 -608 115 -2312 57 -1586 85 -144 57 -3152 105 -1225 71 -1321 75 -1732 57 -506 189 -1177 71 -1548 87 -1396 109 -3225 55 -1102 223 -114 85 -2537 143 -58 55 -1511 53 -780 57 -2098 71 -96 71 -1180 95 -144 97 -2283 55 -3182 143 -238 55 -2374 57 -792 55 -2931 75 -2015 119 -860 51 -358 173 -1256 165 -3319 141 -316 53 -1390 161 -4297 141 -3288 81 -977 57 -4351 57 -2804 71 -170 141 -2466 167 -1854 57 -260 113 -3224 83 -342 85 -58 79 -486 85 -446 87 -446 55 -1745 55 -366 51 -82 167 -1927 157 -2892 85 -86 85 -1712 83 -4854 83 -1790 109 -104 163 -784 101 -604 119 -888 159 -3756 143 -936 53 -2312 53 -1625 99 -3039 71 -1648 103 -520 57 -1024 57 -1404 57 -690 135 -410 57 -1228 201 -1932 77 -130 51 -688 101 -346 51 -198 87 -568 113 -828 87 -674 53 -684 87 -230 113 -670 143 -250 283 -488 97 -470 103 -122 79 -224 103 -386 71 -1160 55 -276 109 -1046 145 -2978 73 -1230 75 -8593 55 -298 87 -1980 55 -1970 139 -974 143 -446 57 -338 77 -1094 95 -840 77 -1066 55 -485 171 -292 95 -1434 152 -116 113 -658 55 -144 57 -836 85 -144 57 -86 53 -448 75 -2054 95 -240 71 -1638 53 -1000 53 -196 195 -2026 71 -939 51 -930 71 -799 57 -820 51 -968 57 -704 53 -356 173 -3870 57 -368 217 -574 69 -192 103 -764 95 -2853 55 -2042 103 -218 53 -260 69 -1754 97 -356 301 -116 55 -2265 87 -1452 95 -118 71 -1226 143 -638 51 -1195 115 -468 75 -1784 101 -152 51 -1222 71 -1619 113 -508 133 -1168 53 -394 71 -806 85 -600 85 -974 161 -386 73 -370 77 -3390 171 -276 77 -1595 95 -220 171 -3647 53 -772 55 -462 57 -832 71 -2348 119 -1478 51 -80 85 -909 79 -338 71 -2833 99 -274 77 -554 125 -1822 53 -108 83 -371 85 -172 57 -478 75 -1424 53 -680 55 -288 73 -1290 109 -3129 131 -958 131 -898 53 -1836 87 -418 53 -2138 119 -1003 101 -270 115 -1194 79 -2138 115 -2351 55 -1511 115 -1697 105 -1278 115 -198 183 -RAW_Data: -1450 57 -330 51 -894 57 -1926 55 -1552 83 -624 81 -3174 75 -270 107 -1111 85 -686 103 -4595 119 -96 286 -1021 71 -850 143 -606 85 -2639 81 -192 77 -1574 81 -468 55 -2484 137 -476 55 -314 85 -1139 53 -1418 79 -2247 111 -874 77 -120 71 -2082 99 -644 107 -288 115 -344 163 -5007 53 -102 121 -876 87 -244 115 -390 57 -144 85 -856 109 -510 103 -506 73 -3754 79 -2838 165 -1569 139 -2268 75 -2086 121 -2102 170 -634 71 -1294 191 -3428 75 -1719 99 -3194 75 -1630 71 -866 51 -644 171 -1416 97 -1687 57 -634 85 -96 71 -366 85 -745 53 -1418 57 -494 85 -1072 73 -122 73 -178 81 -2848 103 -1186 95 -286 71 -144 51 -1366 256 -1440 85 -1184 211 -2798 83 -140 57 -1516 55 -980 85 -86 141 -1316 55 -758 57 -3040 79 -928 99 -4315 181 -492 103 -400 73 -2040 81 -416 75 -388 171 -4212 53 -2250 85 -144 57 -144 85 -821 167 -192 117 -312 71 -3722 51 -248 83 -588 115 -2378 79 -594 79 -364 97 -408 167 -1382 53 -392 109 -1418 83 -2868 127 -120 71 -2244 71 -287 121 -672 85 -764 53 -260 57 -428 53 -1385 57 -3200 57 -3074 83 -114 55 -2185 77 -252 83 -1130 57 -1965 75 -2128 85 -3293 51 -1258 73 -934 97 -832 73 -432 95 -968 75 -692 93 -888 53 -560 77 -484 77 -954 81 -3098 85 -116 85 -506 85 -1618 141 -442 55 -938 73 -478 123 -944 215 -5486 57 -1530 77 -526 57 -1064 77 -342 83 -1814 75 -3042 85 -2245 85 -1223 79 -2229 83 -1538 71 -4608 103 -316 85 -58 185 -1588 71 -3626 85 -2112 253 -1060 57 -562 183 -3805 206 -674 71 -705 71 -2820 105 -468 95 -3669 213 -877 73 -118 121 -204 135 -2468 113 -232 85 -4493 57 -945 55 -672 113 -652 113 -734 53 -424 83 -510 83 -3710 83 -683 97 -2373 85 -2361 259 -995 51 -452 115 -486 81 -5736 57 -1278 51 -4423 111 -1306 163 -248 95 -844 57 -1317 115 -2350 75 -366 99 -1458 57 -444 57 -3824 87 -54 83 -388 249 -2503 101 -192 95 -910 135 -386 105 -2702 85 -1208 85 -250 85 -200 141 -1450 75 -485 57 -662 85 -3323 107 -622 36813 -362 51 -259 963 -732 937 -728 935 -756 909 -756 883 -780 885 -780 883 -784 883 -806 859 -782 883 -808 863 -806 859 -806 857 -808 857 -806 859 -808 857 -808 861 -808 857 -806 861 -806 859 -808 857 -806 861 -808 859 -808 833 -832 833 -834 831 -832 859 -806 857 -806 859 -808 859 -810 857 -806 859 -806 861 -808 833 -832 833 -832 831 -832 833 -834 857 -808 833 -834 831 -834 835 -834 833 -832 833 -832 831 -RAW_Data: -832 833 -834 857 -806 859 -808 857 -812 833 -834 857 -806 833 -832 833 -832 857 -808 835 -834 831 -834 831 -834 835 -834 831 -832 835 -832 831 -836 831 -832 857 -810 833 -832 831 -834 831 -834 857 -808 831 -836 833 -832 831 -834 831 -834 833 -834 831 -834 857 -808 859 -806 859 -810 831 -834 857 -808 857 -808 859 -810 831 -832 833 -832 833 -832 833 -832 859 -808 833 -832 857 -808 833 -832 859 -806 859 -808 833 -834 859 -808 831 -834 835 -832 833 -832 831 -834 857 -806 859 -808 859 -808 857 -810 831 -836 833 -832 831 -832 833 -832 857 -808 859 -806 835 -834 831 -834 835 -832 857 -808 833 -834 831 -832 833 -834 833 -834 833 -832 831 -834 833 -834 835 -830 833 -832 831 -834 833 -832 833 -832 859 -808 857 -808 833 -834 833 -832 833 -834 831 -834 857 -808 835 -832 831 -834 857 -808 857 -808 835 -834 831 -834 831 -834 833 -836 831 -832 833 -834 831 -834 831 -834 833 -834 835 -832 831 -832 833 -832 857 -810 833 -834 833 -836 829 -832 831 -832 835 -834 831 -832 835 -834 831 -834 833 -832 833 -832 831 -832 835 -832 833 -832 833 -832 833 -834 857 -808 859 -808 833 -834 833 -832 833 -834 831 -834 831 -832 835 -834 831 -832 833 -832 859 -810 831 -832 833 -834 857 -808 833 -832 833 -832 833 -832 833 -834 859 -806 859 -810 831 -834 831 -832 859 -806 835 -836 831 -832 831 -832 859 -808 833 -832 859 -808 857 -808 835 -832 831 -834 833 -834 831 -834 833 -832 857 -810 833 -832 833 -832 859 -808 831 -832 859 -808 831 -832 859 -808 859 -806 859 -806 859 -810 833 -834 835 -832 833 -832 831 -832 857 -808 857 -808 859 -806 835 -834 831 -836 835 -834 831 -832 831 -832 859 -808 831 -836 833 -834 833 -832 833 -832 831 -832 859 -806 835 -836 833 -832 831 -834 831 -836 831 -834 833 -832 835 -834 833 -834 831 -832 831 -832 835 -834 833 -832 831 -832 833 -834 831 -834 831 -832 857 -808 857 -808 833 -834 835 -832 831 -834 831 -834 857 -808 859 -808 833 -832 831 -834 833 -832 859 -808 857 -808 835 -832 835 -832 831 -832 835 -834 831 -834 831 -834 833 -834 831 -832 833 -832 833 -832 835 -834 831 -834 831 -832 835 -832 831 -834 859 -806 859 -808 859 -806 837 -832 831 -832 833 -834 857 -808 833 -832 859 -808 857 -808 831 -834 835 -832 833 -832 857 -808 833 -832 861 -810 831 -832 831 -834 857 -810 831 -834 831 -832 859 -808 833 -832 857 -808 859 -806 835 -834 831 -834 833 -832 859 -806 859 -808 857 -RAW_Data: -808 835 -832 831 -834 857 -808 857 -808 859 -808 859 -808 831 -832 833 -834 831 -836 833 -832 831 -832 859 -808 833 -834 833 -832 859 -806 859 -808 857 -808 833 -834 833 -832 831 -834 833 -834 857 -808 831 -832 859 -808 859 -806 835 -832 1665 -4178 1667 -1662 833 -832 1663 -832 3335 -832 835 -832 831 -2500 831 -1666 3339 -3338 829 -830 831 -832 2499 -832 1665 -2500 4173 -1664 1665 -832 831 -2500 833 -3344 831 -830 833 -830 2499 -832 1669 -2506 4177 -1640 1693 -802 853 -2476 853 -3332 831 -830 831 -830 2501 -836 1665 -2506 4169 -1664 1663 -832 829 -2506 831 -3338 827 -832 831 -830 2505 -830 1665 -2504 4167 -1664 1665 -832 831 -2498 13331 -828 853 -830 4165 -4166 1663 -2506 3333 -830 1665 -1664 1663 -1668 831 -832 1665 -3336 831 -1668 831 -832 831 -832 2499 -3342 829 -832 829 -834 2501 -832 1667 -2502 4169 -1672 1663 -830 831 -2502 829 -3338 829 -834 831 -832 2497 -832 1667 -2500 4169 -1666 1663 -836 831 -2500 831 -3342 833 -832 829 -832 2497 -832 1667 -2510 4179 -1638 1689 -804 855 -2476 855 -3330 831 -830 831 -832 2499 -832 1669 -2506 4173 -1662 1663 -832 829 -2504 831 -3336 829 -832 831 -834 2497 -832 1665 -2500 4177 -1668 1663 -832 831 -2504 829 -3336 829 -832 829 -832 2499 -832 1667 -2500 4169 -1668 1667 -834 831 -2500 833 -3338 827 -832 831 -832 2499 -832 1671 -2508 4169 -1662 1663 -830 829 -2500 831 -3332 833 -832 833 -834 2499 -834 1669 -2500 4167 -1664 1671 -832 829 -2500 831 -3334 829 -836 831 -832 2499 -834 1665 -2500 4185 -1642 1689 -828 831 -2500 829 -3334 831 -834 831 -830 2507 -830 1665 -2500 4167 -1668 1665 -830 831 -778 77 -502 75 -427 224 -242 167 -1264 57 -334 105 -5932 115 -2566 79 -514 55 -459 57 -1564 75 -296 95 -4925 111 -202 85 -3492 99 -4327 55 -582 57 -1520 197 -708 53 -1666 53 -1005 85 -3861 139 -858 73 -276 147 -1406 113 -5864 95 -1253 93 -2699 85 -575 93 -3012 57 -774 71 -1662 55 -926 77 -138 55 -744 97 -6232 79 -1435 109 -799 73 -1018 95 -982 79 -664 85 -192 171 -202 57 -198 83 -832 117 -96 53 -734 119 -780 79 -1394 197 -488 55 -371 83 -144 85 -1816 145 -2451 57 -114 115 -1158 69 -168 71 -868 87 -3907 51 -2788 123 -1454 55 -492 53 -510 85 -144 57 -857 53 -1214 85 -140 51 -899 103 -1318 55 -2793 73 -142 71 -1067 109 -5247 55 -172 113 -341 85 -2553 57 -682 217 -170 77 -1216 161 -1844 53 -2747 87 -538 111 -843 53 -1197 109 -2973 53 -1314 111 -514 95 -864 81 -1671 53 -1812 57 -1419 71 -636 83 -3998 75 -1285 135 -578 51 -2754 179 -1194 71 -7260 51 -RAW_Data: -1003 75 -1988 57 -764 57 -5820 85 -474 69 -655 81 -136 103 -1594 53 -1120 51 -520 101 -487 159 -5829 77 -784 103 -440 139 -432 57 -786 79 -506 95 -1982 53 -3728 71 -2506 77 -983 57 -922 135 -3584 57 -1088 57 -142 115 -424 55 -1435 103 -954 113 -286 99 -948 79 -417 133 -398 85 -260 143 -3084 55 -940 195 -758 71 -288 71 -216 95 -1490 77 -1902 85 -3592 53 -796 55 -1351 53 -1538 55 -778 75 -5106 85 -144 57 -1104 95 -452 51 -3006 83 -316 85 -3131 69 -1243 53 -2591 73 -192 71 -462 71 -2566 81 -706 85 -446 51 -1226 81 -656 57 -622 55 -1302 115 -428 55 -1165 171 -1767 111 -1888 85 -1362 55 -562 79 -698 71 -100 191 -192 57 -150 73 -342 97 -342 71 -534 125 -1558 81 -348 77 -232 55 -434 81 -2934 55 -2374 392 -774 53 -3880 71 -4408 105 -230 55 -888 57 -1014 133 -1074 51 -440 173 -314 111 -2931 105 -2165 53 -1654 51 -5547 109 -1688 163 -398 103 -1727 51 -958 139 -1370 87 -144 85 -316 143 -3860 51 -682 163 -2472 107 -1116 125 -896 51 -366 73 -1198 75 -647 143 -2628 53 -1663 85 -984 103 -882 115 -960 71 -2405 81 -1010 53 -510 79 -3410 135 -2502 139 -1552 53 -2004 55 -628 115 -404 55 -570 113 -688 113 -1817 131 -1240 51 -414 135 -538 109 -1233 201 -214 57 -274 73 -1428 85 -434 57 -2570 53 -340 57 -2775 51 -2914 71 -828 79 -740 57 -3780 137 -386 75 -1304 57 -641 53 -668 85 -480 53 -284 85 -764 85 -358 53 -6176 127 -442 95 -192 95 -2628 109 -722 83 -1068 95 -144 145 -404 57 -1762 51 -2612 171 -230 87 -632 171 -146 55 -196 79 -324 51 -224 57 -1318 81 -1345 85 -136 53 -410 83 -80 81 -930 143 -172 57 -2253 53 -216 71 -282 79 -788 212 -706 87 -3134 71 -498 87 -2968 77 -1186 83 -1189 57 -656 83 -424 53 -1410 71 -1554 55 -1004 55 -2736 83 -3182 83 -318 69 -456 95 -926 85 -1232 51 -138 55 -2078 111 -708 142 -268 79 -4582 53 -1152 55 -160 95 -144 161 -330 71 -2236 133 -324 51 -1134 119 -144 139 -718 85 -272 75 -694 55 -318 57 -1710 75 -330 55 -202 115 -1642 163 -56 113 -256 85 -398 73 -1378 95 -94 71 -168 71 -1748 111 -744 85 -2261 73 -336 85 -4614 57 -2696 79 -54 277 -2278 85 -716 71 -2765 85 -1852 77 -622 95 -913 157 -1542 105 -284 57 -3042 77 -1418 85 -900 51 -658 83 -1122 115 -452 81 -100 75 -1400 57 -700 105 -380 99 -332 55 -573 75 -2416 75 -294 107 -316 161 -957 113 -730 51 -998 71 -384 53 -488 209 -RAW_Data: -222 57 -364 53 -1099 57 -570 301 -4112 71 -2730 53 -917 85 -519 85 -1030 55 -2335 85 -374 143 -791 169 -786 57 -998 85 -404 85 -323 81 -1443 55 -1861 199 -88 85 -1991 57 -6675 95 -811 131 -1474 85 -202 229 -1528 85 -912 87 -1233 53 -484 123 -1245 53 -2714 83 -1715 51 -2626 77 -1429 57 -2401 85 -420 51 -669 55 -986 143 -1509 95 -1362 71 -530 79 -762 51 -1226 95 -5703 143 -138 71 -738 83 -912 55 -1582 55 -1200 55 -1106 55 -1289 95 -558 95 -206 143 -2372 109 -748 51 -1584 57 -172 115 -662 135 -156 207 -1464 55 -1732 125 -218 135 -2488 57 -312 55 -2344 79 -412 85 -610 53 -170 55 -1890 57 -3146 57 -924 167 -1127 145 -1284 51 -436 55 -458 115 -340 179 -640 121 -1297 184 -1106 55 -716 113 -2115 71 -240 95 -120 53 -190 57 -266 125 -158 57 -1042 203 -3645 101 -196 101 -138 85 -912 159 -2064 85 -536 83 -3063 55 -170 85 -510 53 -1224 81 -128 51 -1143 101 -4358 95 -947 53 -1402 85 -1894 51 -160 81 -3344 250 -718 103 -184 185 -280 51 -372 113 -820 57 -1154 105 -100 75 -462 71 -1681 53 -1395 79 -670 121 -2820 57 -1014 79 -1628 87 -498 57 -288 143 -740 57 -584 77 -676 85 -1106 57 -2601 103 -1062 83 -3676 75 -1387 171 -138 53 -164 83 -746 57 -316 172 -144 57 -1987 99 -1416 143 -2058 85 -1872 57 -1538 171 -1630 171 -2542 137 -1102 117 -1824 135 -292 163 -462 125 -1226 57 -622 57 -345 85 -1499 53 -910 53 -3758 77 -960 143 -216 167 -912 53 -160 75 -354 57 -831 71 -168 73 -1026 81 -1778 85 -250 51 -1472 57 -466 51 -916 119 -992 73 -980 55 -1039 139 -424 57 -1118 141 -534 85 -486 85 -906 119 -144 71 -1481 109 -1822 79 -2745 57 -3291 137 -2190 95 -1865 133 -1580 55 -628 57 -1683 87 -350 55 -1363 115 -220 87 -56 113 -1068 85 -1642 71 -1727 57 -1398 95 -2382 79 -416 53 -2231 57 -86 143 -984 77 -809 95 -2508 73 -5249 57 -1408 115 -1401 115 -974 143 -1392 57 -818 51 -472 83 -340 83 -533 57 -144 55 -1447 146 -2054 167 -116 55 -3623 51 -1221 75 -314 129 -1122 79 -1146 53 -1695 55 -772 165 -899 57 -454 53 -110 139 -1158 85 -1138 95 -318 55 -1394 75 -1302 85 -1774 81 -851 109 -1620 79 -1598 101 -1417 81 -1038 143 -376 201 -86 85 -2134 83 -2553 131 -431 71 -714 51 -1226 85 -565 254 -307 143 -72 75 -2096 51 -426 57 -1246 185 -972 51 -104 121 -422 51 -746 55 -174 85 -1839 75 -468 193 -952 55 -5596 53 -902 85 -932 77 -244 93 -797 71 -RAW_Data: -120 95 -1172 127 -1658 127 -336 115 -3786 81 -1645 175 -664 71 -1213 119 -538 81 -486 83 -1056 139 -502 81 -136 57 -1016 55 -4015 79 -983 83 -3019 162 -872 55 -1206 51 -668 69 -1794 71 -72 71 -1110 85 -260 109 -1256 77 -1689 77 -220 85 -1356 85 -1444 57 -1574 113 -748 85 -2763 111 -1122 97 -200 71 -180 163 -652 99 -406 221 -1464 57 -2674 152 -1056 111 -1950 95 -246 51 -564 57 -1968 143 -2935 125 -72 51 -3471 147 -1538 75 -2358 143 -998 55 -56 81 -1742 71 -614 71 -510 115 -1230 51 -592 85 -5279 71 -1456 143 -546 97 -2283 115 -768 135 -2157 53 -1126 125 -1852 85 -172 115 -288 135 -3593 55 -82 81 -418 79 -2092 75 -2337 75 -862 123 -1512 51 -196 57 -2481 81 -282 85 -272 71 -96 71 -468 75 -200 99 -1080 55 -1080 79 -80 81 -146 241 -1940 57 -172 85 -232 221 -166 111 -720 111 -1020 201 -202 57 -438 113 -3193 105 -432 95 -382 53 -254 85 -RAW_Data: -576 57 -428 71 -1124 85 -756 77 -160 81 -747 57 -720 53 -1277 75 -1634 143 -120 119 -72 85 -682 287 -156 71 -72 71 -828 155 -144 95 -238 145 -893 133 -784 55 -530 305 -346 85 -86 357 -690 95 -144 97 -606 95 -590 99 -748 51 -954 55 -786 77 -878 131 -834 141 -770 105 -718 53 -86 143 -958 75 -2848 101 -1184 57 -392 81 -202 55 -592 53 -854 57 -2498 71 -3492 217 -2608 55 -1050 215 -94 53 -230 115 -1046 85 -890 81 -464 57 -350 149 -178 57 -1463 71 -2903 113 -283 53 -1820 83 -2193 55 -3771 51 -2286 53 -132 109 -1115 97 -1400 57 -892 105 -670 81 -634 107 -400 53 -662 107 -172 55 -8569 95 -1160 113 -172 57 -376 119 -360 57 -4359 55 -5612 85 -903 107 -2561 217 -1696 53 -328 99 -222 69 -628 53 -190 75 -296 51 -100 95 -238 190 -463 255 -664 79 -2058 57 -840 55 -1408 71 -774 53 -84 93 -582 51 -8306 55 -1909 129 -4008 135 -3409 77 -106 77 -664 95 -1304 123 -308 79 -1343 77 -680 79 -3844 119 -1222 75 -1388 125 -3826 57 -870 73 -744 53 -2282 140 -500 81 -192 87 -2144 143 -892 55 -950 71 -614 187 -174 85 -402 51 -288 131 -494 111 -124 165 -1326 57 -202 55 -346 57 -918 181 -896 57 -380 105 -484 191 -670 85 -2323 55 -3243 133 -390 97 -3123 105 -1292 71 -722 105 -426 75 -172 93 -880 71 -96 81 -544 79 -122 79 -2570 53 -782 57 -2544 53 -438 143 -988 113 -490 57 -144 113 -1980 115 -1540 51 -1974 452 -1540 57 -1507 115 -1440 85 -1485 55 -1309 109 -4194 107 -826 53 -84 109 -4176 109 -1699 111 -1789 199 -1891 119 -1162 55 -1282 105 -1320 189 -200 115 -1004 83 -500 95 -3674 215 -1672 57 -396 55 -1992 71 -191 71 -4032 83 -238 57 -1076 139 -1018 55 -2031 73 -3503 77 -2106 51 -268 173 -1106 101 -160 57 -2695 53 -412 71 -971 143 -512 143 -1142 219 -314 57 -2280 141 -1352 53 -142 115 -1261 53 -3901 85 -1568 53 -1030 165 -1322 51 -1320 137 -859 95 -419 95 -1546 85 -431 195 -4704 55 -174 115 -86 55 -3617 81 -1847 57 -1075 55 -258 57 -512 55 -842 55 -144 95 -648 79 -1022 55 -2452 71 -400 95 -1854 57 -316 85 -3789 57 -1923 57 -56 51 -586 51 -558 81 -280 81 -144 71 -406 51 -5014 71 -386 127 -138 229 -270 75 -144 141 -511 123 -268 57 -1619 101 -2900 113 -426 85 -114 57 -1092 173 -690 57 -6610 85 -6804 57 -314 119 -382 71 -484 115 -2236 143 -1202 79 -584 105 -724 71 -948 115 -214 75 -5453 51 -952 145 -572 107 -2334 103 -338 55 -RAW_Data: -838 79 -4725 69 -298 95 -1760 81 -795 55 -480 179 -1534 133 -457 51 -970 51 -2324 81 -844 57 -1780 119 -550 145 -1218 57 -268 55 -573 115 -2077 71 -1476 57 -86 97 -4518 71 -526 51 -272 139 -3348 85 -1338 53 -780 53 -1548 77 -582 81 -881 55 -1422 105 -1636 77 -1617 71 -924 201 -260 85 -1092 83 -914 51 -2326 111 -382 109 -2328 77 -750 95 -1342 71 -120 51 -280 57 -918 73 -784 53 -4704 79 -1094 85 -624 57 -2733 85 -710 77 -346 57 -716 143 -1489 165 -792 173 -688 73 -102 97 -1139 75 -76 51 -164 81 -762 109 -296 77 -5079 113 -500 83 -304 139 -1080 51 -4167 75 -998 85 -367 85 -3404 71 -1460 95 -1588 111 -1510 95 -939 111 -2271 57 -694 135 -1592 85 -256 163 -708 95 -770 81 -164 113 -1608 95 -672 83 -470 79 -1810 55 -82 79 -144 51 -188 113 -58 115 -1338 55 -4046 119 -970 38481 -128 51 -496 935 -728 937 -754 911 -756 911 -754 911 -754 911 -756 883 -784 885 -780 883 -782 885 -808 857 -806 859 -806 859 -808 859 -806 859 -806 859 -808 857 -808 859 -806 861 -808 857 -808 859 -810 859 -806 857 -808 857 -806 859 -806 859 -808 859 -806 859 -806 859 -808 857 -810 857 -806 859 -810 835 -832 831 -834 831 -832 833 -832 859 -810 831 -832 833 -834 857 -808 833 -834 833 -832 831 -834 833 -834 831 -834 833 -834 831 -834 831 -832 859 -808 857 -806 859 -808 833 -832 833 -832 859 -810 831 -832 859 -808 857 -808 859 -808 831 -836 831 -834 833 -832 833 -834 857 -808 857 -810 831 -832 833 -834 857 -808 857 -808 833 -832 859 -810 831 -832 859 -808 831 -832 859 -808 835 -832 831 -834 857 -808 833 -832 859 -810 833 -836 831 -834 831 -832 831 -834 857 -808 859 -808 835 -834 831 -832 831 -834 831 -834 833 -832 857 -808 833 -832 859 -810 831 -834 831 -832 859 -808 857 -808 833 -834 831 -834 833 -832 859 -808 831 -834 833 -834 833 -834 831 -834 831 -834 857 -808 857 -808 833 -834 833 -834 833 -832 833 -832 835 -834 831 -834 833 -832 831 -832 831 -834 859 -808 857 -808 831 -834 857 -808 859 -806 835 -834 831 -834 831 -834 835 -832 833 -832 857 -810 831 -832 833 -832 833 -832 859 -808 861 -808 833 -834 831 -832 833 -832 857 -810 831 -834 831 -838 833 -830 833 -832 831 -832 859 -808 833 -832 833 -832 859 -808 857 -808 835 -832 833 -832 831 -836 831 -832 859 -806 859 -808 859 -808 857 -808 859 -808 833 -832 833 -834 833 -832 831 -834 857 -808 859 -806 835 -834 831 -832 833 -RAW_Data: -834 833 -834 831 -832 857 -808 835 -832 833 -834 857 -808 859 -806 859 -808 857 -808 831 -832 859 -808 859 -808 831 -834 857 -808 859 -810 833 -832 831 -838 831 -834 835 -830 833 -832 831 -832 835 -832 833 -834 835 -832 831 -832 831 -834 831 -834 857 -808 859 -806 859 -808 857 -808 833 -834 833 -832 831 -836 831 -832 859 -810 833 -834 833 -832 831 -832 833 -834 831 -832 859 -808 857 -808 857 -808 859 -808 859 -806 859 -808 857 -808 859 -808 831 -834 857 -810 857 -806 835 -834 831 -834 831 -834 833 -832 833 -834 831 -834 833 -832 857 -808 859 -808 857 -810 833 -834 833 -832 831 -832 833 -832 857 -812 831 -836 835 -830 831 -832 831 -836 831 -834 833 -836 831 -832 831 -834 831 -832 859 -808 859 -808 831 -836 835 -834 831 -832 831 -834 831 -832 859 -808 831 -836 835 -832 831 -832 831 -834 857 -808 857 -810 831 -834 833 -834 835 -832 831 -832 831 -836 831 -834 859 -808 833 -832 831 -834 857 -808 859 -806 833 -834 857 -810 835 -832 831 -834 831 -832 833 -832 833 -834 833 -834 831 -834 831 -832 859 -808 857 -808 833 -832 859 -808 857 -808 859 -808 835 -832 833 -832 831 -832 859 -808 857 -808 859 -808 833 -832 833 -834 835 -832 831 -834 831 -834 831 -834 857 -810 831 -832 857 -808 859 -808 859 -808 857 -808 831 -834 859 -806 859 -808 857 -808 857 -810 857 -808 859 -810 831 -834 831 -832 833 -834 857 -808 857 -808 835 -834 831 -832 833 -832 859 -808 857 -808 833 -834 831 -832 833 -834 857 -810 835 -834 831 -832 831 -832 859 -806 1691 -4134 1691 -1640 855 -830 1663 -832 3333 -832 831 -832 831 -2502 831 -1668 3337 -3336 829 -832 831 -832 2505 -830 1665 -2506 4169 -1642 1689 -830 829 -2500 831 -3342 829 -832 831 -832 2501 -832 1665 -2506 4157 -1664 1691 -804 853 -2478 857 -3306 853 -832 833 -834 2505 -830 1663 -2502 4153 -1660 1687 -828 829 -2500 833 -3340 829 -830 829 -832 2499 -832 1667 -2500 4171 -1668 1667 -832 829 -2500 829 -3318 859 -830 831 -832 2499 -832 1667 -2498 4171 -1664 1663 -832 831 -2502 829 -3338 831 -832 831 -832 2501 -832 1667 -2504 4167 -1670 1669 -828 831 -2500 12491 -3334 853 -4972 853 -1660 827 -1660 853 -3308 855 -830 829 -830 2499 -834 1665 -2508 4169 -1664 1663 -830 831 -2506 829 -3336 831 -830 833 -832 2499 -832 1667 -2508 4165 -1664 1663 -832 833 -2500 833 -3338 829 -832 831 -832 2501 -830 1665 -2502 4171 -1666 1663 -832 831 -2500 831 -3342 829 -832 831 -832 2501 -836 1663 -2500 4173 -1670 1663 -830 831 -2504 829 -3332 831 -832 831 -832 2507 -RAW_Data: -830 1667 -2502 4173 -1642 1695 -804 855 -2498 829 -3332 831 -830 831 -832 2499 -834 1669 -2506 4175 -1636 1689 -832 831 -2480 853 -3332 833 -830 831 -832 2509 -830 1663 -2502 4165 -1666 1667 -832 831 -2500 831 -3340 831 -830 831 -832 2503 -832 1665 -2504 4169 -1662 1663 -832 831 -2506 831 -3340 829 -830 831 -830 2503 -832 1669 -2502 4169 -1664 1665 -830 831 -1350 81 -266 177 -269 57 -490 143 -2812 85 -2206 57 -72 73 -689 200 -726 149 -598 143 -1554 85 -792 143 -336 123 -548 95 -996 109 -424 55 -140 143 -520 123 -744 81 -730 51 -2840 73 -2716 103 -490 129 -1354 51 -2207 85 -2683 55 -288 173 -895 85 -4697 55 -530 97 -1837 131 -946 73 -660 103 -2022 119 -1039 95 -454 143 -1098 109 -1004 219 -78 312 -778 53 -364 51 -666 113 -376 85 -1059 73 -802 55 -700 75 -1554 171 -1446 107 -168 85 -1278 111 -2564 97 -1578 71 -94 75 -1124 57 -202 171 -2954 79 -3597 191 -326 57 -922 51 -2730 53 -6135 55 -2020 225 -230 87 -1212 53 -1216 149 -1208 53 -502 57 -985 57 -2636 51 -2358 85 -926 143 -1490 57 -114 85 -634 87 -258 57 -1351 75 -1086 95 -192 71 -2150 73 -2060 137 -2159 95 -1362 73 -336 71 -953 77 -220 53 -1989 83 -172 57 -1969 107 -2257 140 -886 71 -1026 123 -372 73 -934 99 -522 139 -651 257 -1566 77 -5069 201 -4802 55 -142 57 -1396 79 -2716 55 -2050 79 -604 111 -1040 85 -1534 109 -544 83 -342 107 -1459 85 -86 57 -3009 99 -322 51 -2178 119 -1485 73 -694 95 -2832 85 -628 81 -1824 222 -1313 85 -918 71 -864 139 -872 79 -218 51 -468 81 -400 85 -1794 83 -742 227 -504 79 -1632 95 -1193 173 -316 85 -2780 51 -1454 103 -246 95 -1885 53 -2051 125 -1186 71 -1708 73 -719 83 -896 73 -524 81 -1082 141 -1076 77 -982 95 -1493 55 -1760 57 -1092 125 -2082 51 -120 83 -2372 77 -1458 75 -1212 75 -412 125 -504 109 -1937 57 -1176 161 -532 71 -224 75 -170 53 -2455 83 -1448 85 -520 55 -1582 143 -1199 115 -172 57 -3324 113 -968 115 -936 109 -3588 71 -1038 57 -1284 245 -674 125 -3396 105 -11139 57 -112 107 -1070 81 -424 115 -2333 131 -1048 115 -874 81 -1754 57 -1300 115 -776 109 -1778 83 -58 85 -2295 55 -5975 115 -6613 55 -2096 51 -394 113 -976 51 -856 53 -950 85 -896 57 -1021 83 -2526 71 -914 53 -1867 121 -376 85 -144 115 -1340 51 -544 143 -1367 71 -252 105 -3156 85 -1170 53 -82 55 -3404 107 -4580 81 -284 83 -2419 57 -1649 123 -714 51 -315 85 -452 105 -562 51 -2883 339 -284 105 -4443 79 -772 145 -700 71 -RAW_Data: -2030 57 -506 57 -2361 135 -4288 71 -240 95 -5117 81 -1952 85 -78 51 -268 131 -336 187 -256 103 -480 115 -200 57 -1072 51 -1488 169 -601 85 -344 51 -527 83 -316 105 -2755 81 -1151 55 -515 119 -1884 115 -86 57 -198 107 -572 75 -1890 87 -344 53 -3983 53 -1831 143 -1036 115 -776 73 -1976 83 -526 57 -244 71 -2232 53 -658 55 -2022 111 -1187 55 -1060 143 -790 113 -448 85 -1146 171 -360 77 -326 55 -1430 55 -1196 57 -2278 57 -58 85 -472 99 -144 153 -76 73 -3314 71 -336 127 -1364 85 -2548 161 -2241 83 -1835 173 -1282 105 -598 109 -1635 85 -1584 131 -936 57 -202 57 -316 87 -2655 57 -2056 83 -1442 51 -2730 51 -1225 73 -1618 85 -1316 73 -1938 75 -144 57 -466 75 -542 81 -666 135 -76 51 -2683 57 -2176 171 -1803 73 -1938 55 -116 85 -674 71 -144 73 -1015 71 -348 139 -114 87 -804 57 -876 81 -3570 103 -2494 115 -4706 97 -836 167 -3400 109 -159 109 -422 143 -1118 81 -1652 53 -1064 85 -2080 125 -942 51 -536 109 -596 57 -2403 101 -1859 51 -456 51 -4086 53 -944 57 -510 135 -1405 87 -1710 85 -2595 81 -654 71 -366 179 -2085 79 -1852 55 -1312 77 -1568 73 -230 201 -612 77 -2530 107 -834 53 -2593 71 -720 69 -956 51 -1000 71 -2016 73 -1062 53 -288 85 -1816 51 -1080 105 -4427 57 -502 57 -246 57 -172 85 -522 111 -1109 55 -1322 85 -3728 53 -726 77 -342 169 -548 85 -144 85 -4389 95 -168 111 -1119 107 -170 55 -482 55 -1877 115 -224 55 -1441 115 -1658 55 -260 115 -2828 57 -292 141 -588 77 -366 113 -1268 113 -2047 57 -2529 95 -96 95 -2418 51 -606 55 -7091 199 -232 55 -542 185 -503 115 -4766 71 -1646 81 -364 57 -842 57 -1202 77 -1835 173 -606 75 -1815 95 -2219 115 -1155 105 -1413 55 -624 55 -1132 73 -3094 107 -228 115 -2893 85 -410 77 -390 53 -876 71 -1922 95 -238 213 -102 71 -3646 57 -144 57 -790 83 -258 57 -534 55 -1262 55 -442 57 -922 55 -394 81 -2235 85 -432 115 -1230 55 -2571 167 -2298 57 -364 173 -114 115 -2858 55 -380 55 -1758 113 -140 137 -679 53 -486 57 -492 107 -470 79 -344 107 -708 55 -438 201 -647 85 -318 125 -1030 119 -216 115 -1186 190 -456 111 -830 85 -324 75 -204 73 -1540 55 -1106 51 -824 87 -1648 51 -236 51 -894 113 -758 57 -246 73 -2861 163 -368 107 -2106 85 -172 85 -230 57 -732 81 -1398 55 -1004 187 -1516 55 -1230 105 -1356 51 -134 169 -188 51 -3247 85 -346 115 -1154 71 -288 83 -1114 129 -294 75 -52 51 -1298 55 -2941 55 -RAW_Data: -214 75 -196 111 -2485 75 -126 95 -312 143 -72 71 -190 81 -804 57 -174 55 -1083 55 -368 51 -108 85 -288 171 -314 85 -508 57 -1214 53 -642 73 -1274 75 -2906 85 -994 53 -196 55 -516 57 -1062 71 -168 95 -230 73 -220 111 -1678 175 -885 79 -392 135 -780 75 -1414 101 -226 115 -1356 75 -1103 107 -166 55 -374 201 -1658 79 -2138 83 -2953 51 -3438 57 -1496 139 -706 55 -86 113 -550 79 -290 69 -886 75 -1810 85 -2014 57 -7729 85 -346 143 -320 71 -370 109 -630 171 -832 57 -230 85 -2047 57 -2083 55 -1038 53 -307 143 -3795 85 -224 51 -76 75 -734 51 -110 139 -1386 51 -1956 143 -804 87 -2378 51 -266 95 -479 71 -74 153 -3470 57 -1820 195 -2516 191 -2735 51 -298 95 -382 55 -142 85 -484 143 -422 139 -240 145 -224 71 -614 51 -5422 53 -284 57 -2086 79 -457 143 -792 69 -1877 57 -370 53 -1174 57 -202 201 -444 143 -2668 77 -499 95 -336 71 -168 99 -601 55 -1254 113 -342 107 -572 57 -2087 55 -140 55 -1613 103 -226 187 -516 87 -984 55 -1292 139 -2796 53 -104 83 -548 83 -58 115 -566 103 -2006 57 -399 167 -716 143 -924 105 -226 57 -756 127 -366 81 -7232 53 -3058 71 -3351 57 -316 77 -689 99 -1099 75 -1238 142 -RAW_Data: -338 85 -404 115 -1320 165 -86 113 -280 133 -1238 133 -838 137 -2438 233 -1339 77 -326 285 -684 85 -542 405 -144 117 -74 133 -488 161 -256 154 -204 219 -292 79 -234 131 -58 143 -386 381 -108 211 -370 143 -240 71 -510 177 -511 191 -238 163 -428 115 -784 105 -698 57 -774 141 -1686 85 -88 53 -504 53 -138 83 -3074 229 -2636 105 -344 57 -1200 85 -431 85 -368 131 -592 79 -108 57 -144 85 -2860 83 -1708 77 -5452 145 -130 111 -514 75 -214 97 -194 53 -1610 139 -968 51 -456 71 -1228 79 -831 115 -4064 103 -2760 57 -1613 57 -1198 55 -1513 85 -825 69 -72 87 -6472 143 -202 57 -2092 113 -764 111 -1016 85 -1328 321 -2368 57 -288 83 -948 107 -1250 111 -606 79 -392 87 -1048 81 -1190 57 -3359 131 -612 133 -4205 81 -244 71 -72 143 -1263 85 -230 115 -414 107 -209 125 -172 57 -718 133 -282 75 -1116 81 -1250 57 -200 231 -684 57 -568 75 -2242 81 -1015 85 -2736 141 -96 239 -574 119 -1188 95 -72 119 -1968 77 -364 57 -1090 75 -2203 125 -4261 171 -72 71 -432 71 -3192 87 -418 55 -646 79 -519 57 -3108 181 -1844 53 -576 111 -980 53 -498 95 -1398 57 -3554 81 -2111 157 -222 137 -156 85 -58 315 -180 55 -1576 115 -2007 75 -178 113 -1660 57 -2160 71 -942 51 -264 55 -470 77 -174 105 -1352 71 -528 71 -200 101 -1054 81 -672 57 -116 141 -168 57 -502 51 -3472 71 -96 71 -698 95 -557 139 -392 55 -572 57 -2121 75 -849 57 -757 191 -58 55 -835 51 -516 53 -1395 79 -230 51 -392 95 -1216 85 -540 81 -441 235 -2225 351 -84 111 -1198 57 -260 55 -86 113 -600 55 -1224 75 -104 51 -735 71 -2128 217 -2296 53 -174 125 -154 71 -96 71 -432 109 -1114 129 -2309 185 -760 93 -1597 119 -1341 53 -558 199 -1754 107 -80 53 -262 143 -2375 105 -530 127 -1875 77 -1161 55 -220 95 -1106 71 -427 213 -2730 71 -835 83 -1084 55 -958 113 -1938 55 -3530 99 -100 71 -1898 143 -1378 57 -1091 57 -872 85 -144 57 -1924 73 -1016 93 -72 95 -1056 97 -1922 79 -408 85 -1863 77 -908 57 -292 53 -816 79 -108 55 -918 95 -262 87 -1573 71 -384 105 -522 105 -5164 57 -2268 113 -1595 143 -1210 115 -1250 115 -1072 85 -392 55 -88 113 -392 83 -716 53 -2979 77 -945 173 -554 71 -3442 55 -1746 113 -144 287 -82 57 -604 157 -1221 115 -374 57 -1718 77 -432 57 -1504 57 -1140 83 -228 57 -202 85 -712 77 -934 57 -1234 129 -168 71 -800 77 -790 79 -2738 137 -1610 51 -610 55 -590 53 -2878 57 -1028 53 -RAW_Data: -2146 51 -722 113 -1007 85 -2775 71 -96 143 -168 71 -560 53 -80 81 -2334 81 -658 137 -220 51 -124 71 -6008 81 -776 71 -350 123 -168 71 -448 71 -454 109 -318 85 -3378 71 -490 109 -5580 127 -3651 73 -912 71 -1372 87 -1122 83 -144 57 -2868 53 -370 51 -1582 55 -266 157 -300 75 -1232 53 -662 199 -202 139 -1772 190 -294 81 -3744 53 -1156 79 -2455 55 -242 71 -408 57 -2073 99 -140 57 -1305 83 -455 57 -260 85 -720 75 -298 81 -975 259 -534 127 -352 57 -522 71 -1633 85 -843 57 -316 87 -1169 81 -566 75 -226 83 -196 115 -2258 173 -489 83 -136 55 -1445 81 -1108 71 -2362 97 -728 95 -1581 57 -432 57 -1344 133 -112 57 -910 55 -84 83 -954 83 -1375 155 -1141 195 -144 95 -1495 71 -829 111 -142 85 -2702 111 -1576 159 -1990 97 -640 75 -344 171 -86 85 -3409 71 -386 101 -912 143 -168 71 -1313 95 -1223 57 -288 85 -86 115 -230 57 -100 71 -910 71 -792 57 -254 85 -2755 137 -1395 69 -3515 87 -2440 55 -1038 137 -72 73 -448 105 -142 83 -1408 105 -2444 105 -492 97 -918 169 -86 81 -1721 57 -482 55 -5453 113 -3386 71 -128 173 -956 51 -1106 53 -1028 143 -168 81 -300 73 -1383 79 -3180 51 -3667 51 -480 57 -2206 53 -532 135 -2097 75 -440 53 -1294 85 -116 113 -484 105 -1614 55 -1106 53 -2197 105 -2796 113 -1504 71 -1038 75 -3274 165 -1598 97 -338 73 -1071 57 -230 115 -2267 331 -1306 85 -648 85 -492 81 -1232 55 -3281 55 -376 97 -3073 83 -3046 143 -978 85 -803 75 -720 107 -988 95 -758 51 -1076 109 -1359 85 -2750 95 -1355 79 -230 71 -312 71 -144 143 -1520 55 -1126 75 -641 77 -632 57 -76 73 -122 119 -1056 95 -1748 51 -918 113 -1070 85 -642 75 -1402 55 -58 159 -482 109 -2875 115 -388 101 -698 55 -554 53 -898 71 -622 95 -472 75 -272 109 -336 189 -1702 123 -184 57 -1217 79 -682 83 -372 137 -430 113 -674 57 -661 75 -1102 81 -1438 115 -976 83 -898 137 -808 53 -2065 151 -3742 55 -3758 55 -414 55 -5521 141 -988 53 -446 131 -142 87 -614 77 -1107 113 -1110 51 -602 85 -1184 75 -654 53 -1724 97 -528 77 -630 85 -58 113 -538 115 -1078 53 -284 55 -576 165 -224 71 -1224 55 -342 55 -462 113 -426 85 -364 171 -1258 107 -837 143 -2620 115 -2844 53 -2348 75 -544 115 -288 85 -898 75 -414 81 -1014 101 -1062 77 -1408 81 -158 113 -908 131 -496 187 -979 51 -484 71 -530 191 -2927 109 -3701 57 -86 85 -1132 57 -892 85 -1671 75 -1040 79 -106 51 -946 71 -RAW_Data: -1552 129 -4484 225 -2645 133 -482 71 -1211 163 -136 143 -144 55 -404 115 -1574 141 -2378 53 -726 75 -102 125 -246 97 -797 53 -624 71 -421 71 -1338 77 -1094 73 -1504 103 -340 51 -256 51 -418 191 -804 55 -368 53 -1197 51 -1778 77 -1496 57 -278 105 -340 111 -2992 141 -1000 221 -1470 79 -2306 57 -1330 81 -3216 77 -282 71 -120 69 -1884 85 -578 55 -775 57 -576 57 -1634 109 -1098 133 -926 113 -1887 85 -58 169 -3933 51 -4166 57 -2099 53 -1490 143 -2902 53 -622 85 -776 75 -412 57 -483 55 -2698 51 -1354 95 -664 53 -1022 57 -1886 53 -3041 85 -1478 55 -2702 299 -312 77 -817 99 -1344 53 -1290 167 -1844 133 -746 103 -313 93 -470 167 -182 79 -892 167 -162 57 -174 55 -836 97 -1160 73 -1863 55 -404 115 -6023 95 -646 53 -2812 53 -864 55 -1194 133 -486 57 -1476 87 -342 101 -1442 119 -178 163 -698 51 -2886 71 -484 71 -378 35173 -52 51 -78 51 -156 77 -106 1092 -702 937 -730 937 -754 913 -754 901 -756 911 -756 911 -758 889 -784 883 -782 887 -780 883 -780 885 -780 885 -780 881 -806 859 -784 887 -782 881 -802 857 -808 859 -806 787 -760 983 -806 855 -802 853 -830 831 -834 829 -830 777 -880 839 -828 857 -806 859 -728 679 -80 161 -834 1073 -590 851 -378 53 -258 71 -818 847 -796 869 -808 861 -790 863 -812 867 -808 857 -812 835 -830 857 -806 859 -806 857 -812 837 -182 51 -606 841 -814 837 -830 833 -834 859 -808 857 -806 857 -806 859 -806 839 -832 859 -806 831 -832 857 -808 857 -808 855 -806 861 -810 863 -806 835 -832 831 -830 857 -808 857 -812 835 -836 831 -834 831 -832 829 -838 829 -832 831 -836 835 -832 831 -832 833 -832 859 -808 831 -834 833 -834 835 -832 833 -832 831 -834 831 -836 831 -832 835 -832 831 -834 831 -836 831 -832 857 -808 859 -808 833 -834 833 -832 831 -834 833 -832 859 -808 833 -836 831 -836 831 -832 831 -836 831 -834 831 -832 833 -834 833 -836 833 -832 833 -830 833 -832 833 -832 831 -832 835 -832 833 -834 857 -812 831 -830 833 -832 833 -834 833 -836 835 -832 829 -834 833 -832 831 -834 857 -808 857 -808 859 -808 833 -832 831 -834 859 -808 833 -834 831 -834 831 -832 859 -808 859 -806 833 -832 859 -808 859 -806 835 -834 831 -832 833 -832 833 -834 857 -808 857 -808 859 -808 857 -808 833 -832 859 -808 833 -836 831 -834 831 -832 833 -836 835 -832 831 -834 831 -834 833 -832 831 -834 857 -806 833 -832 859 -808 833 -832 857 -808 859 -808 831 -834 857 -808 861 -RAW_Data: -808 857 -808 831 -832 859 -806 859 -810 833 -834 857 -806 833 -834 859 -808 831 -834 833 -834 831 -834 831 -836 831 -832 859 -808 833 -832 831 -832 833 -834 857 -810 831 -832 859 -808 857 -808 833 -832 857 -808 859 -806 859 -808 859 -810 833 -832 833 -832 833 -834 831 -834 833 -832 833 -834 833 -832 831 -834 859 -808 857 -808 857 -808 857 -808 833 -834 833 -832 831 -834 857 -808 833 -834 833 -832 833 -832 833 -832 837 -832 833 -836 831 -832 831 -832 857 -810 831 -832 835 -834 857 -808 835 -836 831 -832 831 -836 833 -830 835 -830 835 -834 831 -832 831 -834 831 -834 831 -834 833 -834 857 -808 833 -832 833 -832 835 -834 831 -832 833 -832 859 -806 859 -808 831 -834 859 -806 859 -808 831 -834 859 -806 835 -834 831 -834 831 -832 833 -836 831 -834 833 -832 833 -836 831 -834 831 -832 859 -808 831 -834 857 -812 831 -832 833 -832 857 -808 831 -834 833 -834 857 -810 831 -832 859 -808 857 -808 833 -834 833 -832 833 -832 859 -808 831 -832 833 -834 857 -808 859 -806 859 -806 859 -808 833 -832 861 -808 857 -808 831 -834 833 -832 833 -834 831 -834 857 -808 859 -808 833 -832 831 -832 859 -806 859 -808 859 -808 833 -832 859 -808 857 -808 859 -808 833 -832 857 -808 833 -834 833 -832 833 -832 835 -832 833 -832 833 -832 859 -808 857 -808 859 -806 833 -832 859 -806 859 -808 859 -808 833 -832 857 -808 859 -808 857 -808 857 -808 861 -808 831 -832 833 -832 859 -808 857 -808 859 -806 833 -834 833 -832 859 -806 859 -808 857 -810 831 -832 859 -810 831 -834 857 -808 833 -832 831 -834 859 -806 859 -808 831 -834 859 -806 833 -834 835 -832 1665 -4178 1667 -1644 853 -830 1665 -830 3343 -830 829 -832 833 -2502 831 -1666 3333 -3340 831 -830 829 -834 2499 -832 1665 -2508 4175 -1638 1695 -802 853 -2502 829 -3332 831 -832 829 -832 2499 -832 1669 -2508 4177 -1642 1687 -830 827 -2500 829 -3336 829 -830 831 -832 2503 -834 1671 -2480 4195 -1642 1687 -830 829 -2504 829 -3332 831 -832 829 -832 2501 -832 1669 -2500 4173 -1668 1667 -830 833 -2500 829 -3314 855 -830 829 -832 2499 -832 1665 -2500 4173 -1666 1663 -832 831 -2498 831 -3346 829 -830 829 -832 2505 -832 1665 -2504 4167 -1664 1665 -830 831 -2508 9999 -826 2519 -802 853 -1664 829 -2500 1667 -832 831 -834 1665 -2502 3333 -3334 2503 -1644 2531 -806 853 -832 831 -830 829 -832 831 -832 831 -2504 831 -1666 1673 -830 831 -3340 831 -832 831 -2498 831 -2500 1669 -832 831 -832 835 -830 1671 -7504 1665 -1666 10007 -2472 855 -830 829 -1668 2497 -RAW_Data: -3342 829 -830 831 -832 2503 -830 1667 -2506 4169 -1666 1661 -832 831 -2502 831 -3336 829 -832 829 -832 2505 -830 1671 -2500 4167 -1666 1665 -832 831 -2502 831 -3332 831 -832 831 -834 2507 -834 1663 -2498 4171 -1668 1667 -830 829 -2506 831 -3316 853 -830 829 -832 2503 -830 1665 -2498 4173 -1668 1667 -832 829 -2502 829 -3316 855 -828 829 -836 2501 -832 1665 -2504 4155 -1666 1667 -828 853 -2480 855 -3306 853 -828 831 -832 2503 -830 1669 -2500 4165 -1666 1665 -832 831 -884 239 -84 57 -86 139 -595 115 -58 171 -488 171 -196 109 -560 233 -354 85 -628 83 -170 87 -172 57 -114 201 -936 83 -440 51 -140 257 -1331 73 -1130 143 -850 107 -84 55 -2862 301 -392 57 -3352 109 -518 113 -1454 71 -309 113 -400 55 -1551 81 -3980 51 -2334 77 -2336 81 -818 79 -1622 57 -545 111 -368 57 -2790 141 -230 113 -1418 109 -3084 57 -1836 191 -1407 71 -1556 85 -3456 119 -1138 53 -1830 51 -6980 95 -1186 73 -246 95 -1590 53 -112 55 -1262 85 -1164 53 -1229 190 -1226 71 -314 73 -1102 55 -154 51 -202 111 -2555 55 -1915 85 -1214 85 -174 85 -58 57 -2466 71 -70 55 -1752 95 -1326 237 -1038 165 -74 57 -678 51 -2062 85 -1532 143 -1898 55 -232 55 -1478 93 -1740 85 -196 143 -765 71 -96 71 -780 53 -1918 357 -1807 51 -1054 173 -1584 57 -2294 79 -790 53 -424 71 -1492 69 -3693 85 -710 143 -202 57 -1562 71 -168 71 -1020 147 -1276 85 -2227 97 -192 95 -2028 73 -216 71 -72 95 -230 79 -2310 57 -1124 85 -2510 71 -1798 105 -1173 111 -560 73 -1120 73 -342 71 -144 166 -1704 73 -72 95 -304 57 -3130 85 -9625 119 -2383 113 -486 83 -190 83 -2192 95 -1553 51 -1986 119 -202 135 -1096 81 -2311 201 -2079 97 -894 95 -1782 83 -820 83 -1465 55 -1138 85 -2786 143 -3226 204 -1358 71 -2544 53 -56 105 -934 115 -284 53 -80 71 -896 163 -736 73 -1538 105 -184 101 -686 53 -642 103 -1330 75 -3897 85 -556 51 -602 225 -1666 53 -1428 85 -1193 83 -298 97 -1507 109 -2383 79 -1805 77 -2877 119 -1233 137 -968 55 -1210 85 -196 153 -264 103 -1470 85 -3017 113 -218 73 -2133 57 -1948 109 -594 83 -2816 157 -539 55 -558 55 -112 81 -952 51 -272 57 -444 85 -570 83 -1175 53 -1760 57 -4421 53 -168 111 -232 55 -2752 53 -366 79 -1238 73 -1228 85 -2416 57 -3523 85 -1566 103 -820 113 -765 173 -1354 53 -2122 85 -346 57 -4443 79 -408 53 -290 95 -1484 75 -1052 73 -278 145 -454 77 -164 167 -430 81 -5196 115 -2725 53 -2715 83 -136 119 -457 79 -364 105 -728 175 -1528 57 -RAW_Data: -728 53 -176 163 -2120 71 -1394 71 -488 107 -1404 221 -422 95 -96 201 -356 71 -7701 85 -3729 77 -3237 95 -846 71 -888 51 -322 123 -784 79 -276 105 -1064 77 -652 85 -776 83 -664 77 -3171 107 -112 57 -1733 77 -898 85 -2118 57 -1423 81 -616 73 -2090 55 -2730 85 -6676 85 -317 85 -632 79 -526 101 -1637 192 -700 107 -314 55 -608 109 -388 217 -2209 85 -116 143 -1837 143 -4598 79 -1406 87 -972 71 -1750 85 -882 167 -1105 75 -5685 57 -672 55 -2931 113 -730 109 -196 87 -86 85 -1168 51 -1041 57 -404 71 -2115 155 -880 85 -719 51 -216 53 -660 113 -1412 73 -2408 97 -2971 57 -2495 170 -2786 87 -2076 55 -2939 53 -1032 111 -258 115 -500 81 -1947 113 -580 85 -1656 75 -1528 79 -324 143 -1110 79 -274 143 -796 53 -460 83 -1480 53 -1874 119 -192 97 -676 71 -1558 55 -3100 79 -1702 53 -618 57 -1091 71 -854 119 -1086 53 -2373 57 -1252 77 -136 87 -4334 133 -1292 85 -304 53 -556 71 -668 157 -1981 109 -792 77 -446 57 -226 77 -296 51 -202 101 -1104 81 -1782 99 -414 95 -1043 121 -2439 109 -1296 103 -3848 55 -116 85 -690 85 -1092 105 -1873 83 -966 75 -140 87 -944 53 -2992 81 -755 101 -376 79 -2732 113 -422 53 -964 51 -396 57 -7268 75 -314 95 -826 105 -1332 71 -460 195 -740 53 -434 51 -104 85 -2272 135 -1858 57 -172 201 -200 113 -4127 129 -176 69 -712 79 -6216 57 -2069 73 -1370 51 -3561 143 -1895 95 -566 51 -1440 95 -1062 57 -791 57 -116 85 -1240 107 -1216 57 -3492 105 -3270 57 -548 57 -250 97 -2722 79 -1640 115 -1928 57 -425 85 -1278 85 -698 53 -534 85 -685 85 -3646 141 -1378 57 -562 192 -400 111 -622 133 -208 51 -1396 111 -924 143 -1442 95 -1145 95 -72 81 -803 107 -112 55 -692 57 -2961 107 -1823 83 -3286 51 -372 97 -1180 71 -192 167 -96 95 -862 79 -1030 77 -394 176 -1455 55 -1594 53 -400 157 -334 57 -678 77 -522 217 -254 77 -315 137 -116 85 -172 57 -198 171 -378 71 -743 55 -860 79 -483 57 -230 201 -1650 71 -326 53 -2747 95 -576 165 -1854 57 -2763 57 -440 53 -348 57 -772 135 -198 55 -2280 51 -1903 85 -1666 143 -1120 55 -1507 75 -166 115 -248 87 -142 53 -2238 147 -332 53 -1842 55 -670 201 -1078 53 -112 57 -1012 167 -336 51 -522 51 -1330 115 -1824 55 -776 85 -806 139 -216 95 -1318 85 -548 85 -1647 83 -398 51 -1452 95 -1982 143 -1146 267 -2308 107 -656 57 -757 83 -742 77 -414 107 -312 77 -864 53 -112 85 -564 85 -2234 113 -5404 95 -RAW_Data: -1490 57 -374 83 -1644 115 -110 71 -478 71 -72 71 -1140 135 -2792 95 -2703 129 -168 85 -1668 133 -1521 53 -914 51 -4717 79 -1724 57 -172 115 -746 127 -394 83 -674 169 -564 71 -2250 119 -1603 51 -464 77 -2220 85 -566 55 -144 143 -116 115 -487 115 -282 129 -1690 111 -706 129 -1429 55 -223 113 -1228 53 -544 57 -114 57 -698 77 -2874 55 -232 113 -254 57 -1523 159 -72 101 -2285 51 -166 85 -315 57 -2125 53 -758 55 -478 107 -1725 53 -5851 83 -1616 101 -339 55 -230 109 -188 77 -652 51 -478 113 -3742 55 -230 57 -144 173 -754 143 -480 71 -2526 179 -4037 57 -1720 75 -432 133 -1464 165 -1268 215 -154 107 -398 87 -3083 85 -144 87 -512 71 -222 113 -487 85 -2428 81 -1648 119 -1226 95 -1213 103 -1275 57 -702 81 -1908 55 -542 55 -1838 109 -148 71 -1286 95 -3248 55 -639 71 -144 71 -598 119 -3930 171 -2344 55 -2623 87 -168 79 -2264 141 -1018 83 -344 115 -426 109 -518 85 -756 55 -2340 55 -1679 85 -200 85 -6170 51 -590 224 -1233 85 -260 107 -6039 57 -2192 113 -1496 109 -1146 55 -174 85 -848 75 -1242 111 -1068 85 -2111 81 -542 95 -1350 75 -144 95 -96 105 -362 105 -340 57 -864 115 -1568 73 -722 71 -2762 249 -382 113 -1979 75 -1233 143 -342 81 -3036 51 -226 87 -572 55 -282 57 -288 103 -3244 83 -242 99 -1748 55 -1206 172 -276 79 -2060 167 -904 123 -770 79 -1816 115 -202 113 -1526 137 -604 85 -2234 73 -300 113 -1504 95 -1114 55 -590 75 -410 103 -1314 83 -590 77 -3440 167 -3132 55 -224 75 -2107 57 -144 123 -842 155 -813 75 -602 71 -3652 141 -2380 57 -224 249 -164 189 -530 75 -493 51 -130 51 -126 95 -670 51 -340 81 -1084 97 -216 115 -1284 57 -1200 85 -310 55 -1002 71 -1455 79 -720 85 -1140 119 -288 95 -944 119 -554 53 -2450 73 -324 79 -836 105 -1087 73 -418 105 -528 139 -468 121 -582 167 -4205 71 -1858 183 -2318 143 -780 101 -98 187 -136 55 -2239 57 -434 103 -1946 113 -86 57 -428 53 -222 57 -86 143 -1036 107 -1232 77 -606 113 -1394 115 -86 57 -2204 75 -456 57 -1623 173 -2832 53 -2464 228 -609 141 -58 95 -847 77 -488 95 -734 51 -882 95 -4004 143 -1242 97 -322 77 -218 109 -2283 85 -116 85 -1281 55 -228 173 -820 77 -2298 71 -2057 115 -116 85 -760 143 -489 57 -2702 139 -1380 143 -583 143 -658 99 -918 141 -1033 55 -262 85 -1408 143 -1979 75 -872 57 -268 219 -594 107 -172 105 -222 71 -448 51 -806 101 -540 51 -108 115 -1558 147 -418 79 -206 71 -RAW_Data: -514 107 -1090 125 -928 77 -172 153 -382 71 -742 73 -1060 133 -3547 71 -992 85 -890 119 -72 81 -1154 145 -122 51 -198 57 -1288 173 -2424 83 -260 55 -278 105 -926 55 -274 75 -1555 57 -538 137 -2713 71 -1082 57 -1110 57 -670 83 -494 105 -1504 77 -750 81 -1226 57 -2030 79 -258 57 -140 101 -858 57 -400 55 -2286 55 -3390 55 -542 103 -404 181 -316 57 -174 85 -3657 51 -162 81 -735 105 -518 87 -2452 51 -252 165 -1942 73 -74 73 -1386 113 -3530 181 -1312 57 -1262 115 -314 133 -2418 57 -4028 71 -182 121 -747 83 -2793 167 -686 85 -1814 171 -1984 109 -1592 101 -1250 57 -346 113 -1370 99 -2761 53 -1930 51 -280 57 -1590 71 -1105 79 -702 143 -1216 235 -3208 57 -1998 77 -859 119 -608 51 -1084 143 -709 81 -1286 85 -144 265 -1274 53 -2068 75 -3092 77 -232 55 -144 57 -1836 57 -258 115 -1507 85 -2022 109 -1121 71 -893 93 -696 119 -951 57 -710 75 -844 95 -396 109 -1323 83 -1878 85 -272 51 -1108 179 -584 147 -196 71 -4320 57 -904 81 -776 55 -1857 55 -712 111 -54 81 -1338 121 -510 57 -698 85 -396 55 -1239 57 -892 75 -146 71 -358 71 -278 113 -2876 105 -671 85 -316 269 -1362 81 -342 57 -2932 55 -1391 111 -202 57 -576 55 -910 113 -318 57 -586 55 -719 143 -256 113 -2136 81 -802 71 -1190 95 -144 71 -2156 71 -730 55 -2380 87 -977 83 -172 95 -1322 57 -1160 71 -144 147 -1028 83 -898 53 -878 87 -114 115 -1296 75 -476 171 -1442 53 -1705 103 -2318 57 -3989 73 -414 73 -1012 51 -1492 55 -1992 87 -2711 83 -484 77 -760 105 -1338 53 -56 81 -590 57 -316 169 -4225 105 -1172 109 -744 115 -394 51 -4057 107 -531 87 -734 83 -1522 55 -1895 55 -2121 83 -1574 265 -1181 83 -2434 71 -1888 105 -734 167 -2886 57 -114 135 -2099 79 -854 71 -3982 51 -440 229 -654 57 -2316 57 -266 97 -536 75 -2106 53 -937 149 -2691 85 -838 57 -506 51 -108 57 -1044 85 -760 57 -2398 73 -2428 75 -1016 81 -138 85 -88 57 -1134 235 -1612 113 -2849 71 -1517 77 -434 127 -192 57 -1992 95 -232 107 -3254 53 -1442 57 -86 107 -1024 95 -854 53 -534 85 -2618 81 -2344 115 -2866 111 -998 71 -3113 53 -1341 55 -1216 103 -246 85 -3100 260 -450 85 -3023 55 -372 167 -86 57 -1094 51 -1499 85 -1252 57 -546 51 -2099 191 -172 85 -1829 71 -1259 97 -1506 85 -1928 143 -900 143 -858 115 -942 81 -2027 53 -1292 115 -1010 119 -852 55 -342 51 -80 81 -470 101 -636 55 -1046 85 -576 81 -976 79 -188 95 -947 81 -RAW_Data: -1480 55 -1430 55 -596 171 -1583 95 -884 51 -1116 121 -276 143 -2098 51 -1172 73 -1918 111 -112 51 -220 57 -230 163 -740 51 -1074 57 -1608 55 -3299 57 -2876 77 -1268 85 -771 79 -386 77 -1618 71 -1308 55 -863 121 -938 75 -110 83 -1176 187 -1807 57 -2894 55 -344 285 -994 55 -836 51 -963 85 -202 263 -686 55 -853 57 -144 115 -658 127 -955 57 -116 85 -944 111 -1891 57 -460 311 -1531 129 -146 121 -1301 71 -312 75 -600 77 -1760 71 -1264 85 -2345 51 -294 111 -772 51 -78 75 -292 71 -542 113 -690 85 -1164 79 -1368 57 -1284 53 -130 182 -240 53 -114 57 -287 143 -304 75 -754 169 -130 83 -54 85 -527 71 -1657 71 -462 383 -560 115 -638 131 -3943 285 -264 75 -168 85 -3999 83 -996 73 -644 71 -1205 71 -1108 85 -172 87 -1139 139 -518 167 -1278 123 -1125 75 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 @@ -201,3 +173,26 @@ RAW_Data: 107 -1501 77 -1518 53 -704 113 -390 107 -650 73 -932 51 -3641 169 -704 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 +RAW_Data: -66 11813 -100 14655 -98 40111 -66 1625 -2116 1933 -34732 501 -11730 235 -3728 1887 -2106 1933 -2092 1971 -2072 1959 -34712 511 -3554 445 -3556 1997 -2036 455 -3594 1963 -2046 1979 -2076 1961 -2070 1989 -34690 483 -7724 1739 -2226 355 -3684 1857 -2138 1929 -2078 1965 -2074 1947 -34750 487 -3538 473 -3544 1993 -2042 485 -3548 1961 -2070 1965 -2070 1969 -2042 1997 -34716 443 -7734 1753 -2236 323 -3676 1903 -2098 1945 -2102 1927 -2070 1989 -34710 521 -3532 473 -3544 1991 -2032 481 -3556 1969 -2076 1967 -2036 1991 -2066 1969 -34718 467 -7756 1739 -2192 363 -3654 1889 -2132 1929 -2096 1935 -2070 1987 -34716 511 -3522 471 -3554 2009 -2036 459 -3550 2003 -2038 1979 -2042 1999 -2042 1999 -34704 471 -11774 225 -3710 1879 -2162 1885 -2112 1925 -2110 1939 -34738 459 -3636 403 -3612 1939 -2062 451 -3566 1985 -2044 1995 -2040 2009 -2032 2003 -34684 495 -3680 295 -3648 1935 -2098 423 -3562 2001 -2038 1989 -2044 2003 -2036 1977 -34718 461 -3678 295 -3684 1901 -2098 429 -3596 1967 -2036 1981 -2048 1993 -2042 2013 -34686 521 -3530 457 -3568 1999 -2036 455 -3552 1999 -2032 2019 -2024 1995 -2022 1997 -34716 441 -15774 1809 -2192 1905 -2100 1919 -2112 1961 -34720 417 -3830 167 -3710 1863 -2144 357 -3674 1909 -2100 1955 -2062 1977 -2072 1965 -34710 487 -3562 453 -3554 1985 -2052 481 -3536 2019 -2010 2001 -2042 1997 -2038 2005 -34716 451 -3602 433 -3584 1959 -2070 451 -3560 2001 -2038 1993 -2042 1967 -2072 1973 -34712 459 -3622 393 -3624 1933 -2068 457 -3584 1965 -2064 1979 -2052 1967 -2044 1981 -34722 477 -3608 397 -3588 1961 -2096 413 -3596 1971 -2040 1979 -2072 1963 -2070 1959 -34714 495 -3558 483 -3538 1985 -2042 479 -3562 1985 -2046 1967 -2070 1973 -2054 1995 -34688 493 -3578 413 -3614 1939 -2074 465 -3560 1971 -2038 2017 -2018 1995 -2042 2013 -34726 479 -3528 475 -3556 1999 -2036 455 -3570 1999 -2040 1973 -2054 2001 -2032 1987 -34720 477 -3562 445 -3602 1949 -2054 481 -3562 1975 -2060 1963 -2064 1977 -2038 2005 -34702 485 -3570 447 -3550 2015 -2020 479 -3564 1983 -2048 1999 -2034 1971 -2064 1993 -34688 517 -3516 497 -3532 1999 -2038 481 -3558 1997 -2004 2027 -2042 1963 -2038 1997 -34716 491 -3562 461 -3548 1995 -2032 491 -3524 2005 -2036 1989 -2038 1995 -2046 1979 -34714 465 -3682 293 -3680 1905 -2096 431 -3592 1969 -2070 1977 -2052 1965 -2044 1981 -34734 479 -3564 463 -3556 1999 -2032 457 -3550 1995 -2044 2011 -2042 1997 -2006 2027 -34680 531 -3524 483 -3538 1987 -2044 479 -3534 2013 -2048 1965 -2062 1987 -2030 1997 -34712 473 -3592 445 -3562 1975 -2072 451 -3566 1965 -2042 2013 -2046 1963 -2064 1993 -34700 459 -3632 371 -3638 1915 -2084 449 -3568 1987 -2046 1971 -2070 1983 -2022 1997 -34726 487 -3524 477 -3562 1985 -2044 481 -3542 2005 -2040 1995 -2038 1967 -2046 1993 -34710 511 -3528 471 -3560 1967 -2070 459 -3558 1971 +RAW_Data: -2072 1971 -2056 1971 -2074 1973 -34714 455 -3634 373 -3634 1901 -2110 419 -3620 1941 -2070 1991 -2040 1999 -2038 1965 -34740 467 -3562 481 -3534 1983 -2070 449 -3546 1999 -2044 1993 -2042 2003 -2036 1975 -34702 521 -3560 443 -3586 1969 -2044 449 -3562 1997 -2046 1987 -2042 2007 -2034 1973 -34732 487 -3562 443 -3582 1979 -2058 445 -3560 1995 -2044 1997 -2028 1987 -2034 2003 -34710 515 -3518 485 -3566 1977 -2036 483 -3536 1999 -2044 2009 -2024 1995 -2068 1973 -34710 487 -3564 471 -3558 1977 -2054 447 -3564 1991 -2042 1997 -2036 2007 -2034 2001 -34684 529 -3526 469 -3548 1989 -2038 483 -3562 1997 -2038 1973 -2034 1999 -2036 1997 -34728 487 -3536 479 -3534 2013 -2044 449 -3570 1985 -2042 1993 -2044 2005 -2014 1995 -34710 473 -3594 439 -3562 1995 -2040 457 -3564 2001 -2040 1975 -2046 1995 -2046 1999 -34704 491 -3548 451 -3570 1991 -2042 447 -3578 1967 -2046 1995 -2042 1999 -2034 2001 -34712 491 -3562 443 -3584 1981 -2018 479 -3562 1985 -2044 1997 -2030 1989 -2040 1997 -34722 489 -3554 459 -3560 1969 -2068 453 -3554 1999 -2034 1987 -2058 1997 -2046 1983 -34702 487 -3534 479 -3564 1983 -2040 483 -3538 1981 -2048 1993 -2048 2007 -2044 1995 -34696 489 -3550 453 -3570 1995 -2050 447 -3564 1983 -2040 1999 -2034 2003 -2034 1995 -34690 495 -3580 433 -3586 1969 -2064 453 -3552 1995 -2036 1991 -2056 1997 -2046 1987 -34706 441 -3636 373 -3626 1959 -2074 419 -3592 1963 -2074 1989 -2044 1971 -2070 1981 -34698 509 -3526 503 -3528 2005 -2034 481 -3528 1993 -2042 1999 -2066 1989 -2034 2003 -34678 495 -3540 481 -3546 1997 -2046 473 -3554 1999 -2034 2001 -2036 1995 -2046 1983 -34720 475 -3560 469 -3548 1997 -2030 485 -3566 1963 -2066 1983 -2046 1999 -2034 1973 -34734 487 -3560 443 -3584 1981 -2052 445 -3568 1987 -2044 1999 -2032 1993 -2034 2007 -34702 491 -3560 459 -3558 1967 -2070 455 -3556 2003 -2036 1977 -2042 2005 -2028 1997 -34730 461 -3564 473 -3536 2011 -2046 449 -3566 1989 -2044 1997 -2042 1971 -2054 2001 -34708 475 -3560 479 -3528 1999 -2040 485 -3566 1963 -2040 2013 -2042 1995 -2034 1987 -34694 519 -3554 441 -3582 1981 -2052 449 -3564 1985 -2040 1993 -2034 1991 -2062 1975 -34714 529 -3534 463 -3558 1969 -2068 451 -3560 2003 -2038 1993 -2042 1969 -2070 1975 -34720 493 -3582 383 -3616 1937 -2072 469 -3558 1995 -2036 1975 -2066 1995 -2042 1989 -34678 531 -3560 391 -3622 1937 -2094 429 -3588 1967 -2070 1981 -2054 1965 -2038 2021 -34682 525 -3524 481 -3564 1989 -2040 445 -3554 1997 -2040 2005 -2034 2001 -2024 1991 -34706 517 -3586 409 -3610 1927 -2076 451 -3558 1967 -2074 1993 -2038 2001 -2040 1975 -34714 495 -3588 409 -3602 1933 -2088 447 -3584 1965 -2044 1999 -2036 2007 -2030 1995 -34692 525 -3538 447 -3580 1981 -2042 487 -3542 1995 -2040 1969 -2072 1969 -2044 1991 -34714 443 -3636 399 -3630 1899 -2106 413 -3584 1997 +RAW_Data: 4046 -17306 65 -298 97 -100 133 -268 265 -330 133 -132 1723 -16806 165 -132 99 -920 65 -622 789 -130 99 -66 361 -98 295 -166 73573 -17510 97 -492 129 -728 529 -100 1063 -164 295 -66 1119 -14962 627 -166 363 -264 427 -132 593 -100 633 -132 39555 -16938 99 -2024 65 -100 97 -164 99 -66 399 -100 123891 -16736 163 -200 97 -200 165 -264 65 -828 427 -132 871 -5132 591 -490 595 -486 605 -454 275 -822 241 -824 273 -784 321 -782 649 -444 653 -408 657 -428 321 -744 693 -388 699 -388 707 -392 313 -752 345 -750 317 -744 351 -730 355 -738 323 -774 327 -748 329 -750 695 -386 701 -354 381 -722 351 -720 385 -718 351 -718 345 -738 705 -382 329 -736 713 -360 387 -718 369 -718 367 -706 735 -352 375 -726 351 -722 351 -720 719 -7808 4845 -1474 743 -332 741 -370 705 -370 349 -718 383 -716 345 -712 381 -704 747 -326 747 -350 737 -352 351 -718 719 -360 741 -366 687 -362 375 -704 381 -724 351 -740 353 -712 357 -718 359 -744 363 -688 365 -722 727 -354 727 -354 379 -724 351 -722 353 -720 387 -718 353 -718 703 -374 351 -716 735 -354 365 -708 353 -734 351 -746 717 -356 359 -720 371 -704 371 -720 731 -7786 4847 -1482 711 -386 711 -358 743 -330 373 -708 359 -748 349 -740 351 -716 719 -356 727 -354 739 -354 351 -718 719 -362 743 -364 721 -330 373 -706 381 -722 351 -740 353 -712 359 -720 361 -722 361 -720 361 -720 725 -354 731 -354 381 -720 353 -722 385 -720 351 -720 349 -716 735 -354 361 -748 711 -364 347 -740 365 -722 365 -720 695 -384 371 -704 381 -702 377 -710 709 -7804 4853 -1468 743 -336 735 -358 719 -352 379 -724 353 -722 353 -720 387 -686 721 -360 721 -362 743 -332 387 -718 721 -366 701 -382 701 -350 377 -720 351 -740 353 -714 357 -710 397 -710 365 -702 385 -688 377 -724 731 -352 703 -354 379 -736 343 -740 357 -720 349 -706 385 -718 719 -354 365 -724 735 -352 377 -724 355 -720 353 -720 721 -358 387 -686 387 -718 353 -718 733 -7796 4821 -1492 739 -350 719 -334 737 -350 365 -722 373 -722 367 -708 371 -702 747 -352 711 -358 743 -364 343 -706 749 -352 717 -350 717 -384 327 -736 351 -746 355 -716 357 -720 359 -710 365 -742 365 -708 367 -704 711 -354 743 -356 387 -684 373 -706 381 -722 351 -740 353 -714 721 -356 361 -720 733 -352 375 -694 385 -724 353 -722 719 -356 385 -686 385 -718 351 -716 731 -7792 4843 -1480 717 -354 719 -386 717 -354 359 -720 351 -708 387 -712 355 -718 721 -356 727 -354 739 -356 351 -718 741 -364 +RAW_Data: 705 -370 703 -372 351 -718 383 -720 347 -720 347 -714 381 -704 353 -744 357 -718 355 -720 723 -356 725 -354 379 -722 351 -722 353 -722 385 -718 351 -718 721 -372 351 -716 719 -372 351 -718 383 -716 345 -714 743 -346 361 -740 353 -712 357 -710 725 -7818 4837 -1498 713 -356 709 -360 741 -332 375 -706 359 -750 351 -706 353 -748 719 -356 723 -352 739 -354 351 -718 709 -364 719 -362 721 -364 385 -718 353 -718 383 -682 377 -712 349 -734 353 -742 355 -712 359 -722 723 -354 729 -352 381 -722 353 -722 351 -720 387 -718 353 -716 701 -388 345 -722 737 -354 357 -722 351 -708 387 -712 717 -350 731 -354 741 -356 743 -330 375 -8180 4829 -1468 739 -364 707 -354 729 -352 379 -722 353 -720 387 -686 387 -718 707 -368 721 -366 707 -368 351 -718 735 -354 719 -354 719 -388 329 -746 349 -738 351 -712 359 -718 361 -742 365 -708 371 -706 373 -720 733 -320 733 -354 383 -720 353 -720 387 -718 351 -716 385 -714 703 -388 327 -746 705 -348 387 -702 385 -690 385 -724 713 -358 709 -362 743 -364 709 -370 351 -8162 4837 -1482 715 -388 715 -352 715 -384 325 -730 353 -744 353 -712 359 -720 723 -354 733 -354 745 -356 351 -720 719 -362 741 -330 737 -382 349 -722 345 -724 361 -744 349 -704 383 -716 357 -718 357 -720 361 -720 723 -354 733 -354 383 -720 387 -686 387 -718 353 -718 349 -716 731 -384 347 -724 721 -352 365 -706 353 -732 353 -746 717 -356 723 -352 739 -354 711 -360 385 -8146 4841 -1470 737 -344 739 -326 751 -352 377 -690 387 -724 353 -724 353 -722 711 -360 743 -364 721 -330 387 -716 703 -386 721 -356 721 -354 363 -706 349 -734 351 -746 355 -718 355 -712 363 -744 365 -708 369 -722 695 -352 731 -354 381 -722 353 -722 351 -734 351 -716 383 -720 723 -354 333 -736 739 -348 361 -708 351 -748 355 -712 725 -354 727 -352 741 -352 713 -358 385 -8134 4855 -1474 719 -358 709 -362 721 -364 387 -716 351 -718 385 -712 347 -712 739 -334 739 -354 729 -352 379 -722 717 -354 711 -360 743 -332 387 -718 351 -716 377 -708 349 -730 353 -742 355 -710 359 -720 359 -720 723 -354 729 -352 381 -720 353 -722 351 -722 387 -684 387 -716 703 -384 349 -722 737 -354 329 -750 349 -738 353 -712 719 -356 725 -354 741 -354 717 -358 385 -8126 4861 -1470 735 -344 731 -346 729 -348 383 -718 347 -712 353 -734 353 -746 715 -356 725 -350 741 -352 351 -718 741 -366 721 -366 705 -370 353 -718 385 -682 377 -710 349 -734 353 -744 355 -710 359 -710 397 -688 +RAW_Data: 727 -354 729 -352 379 -724 353 -722 353 -718 387 -716 353 -716 735 -348 383 -682 727 -386 347 -722 347 -712 381 -706 747 -326 747 -350 737 -352 711 -358 +RAW_Data: -854 811 -454 811 -444 409 -838 811 -454 823 -432 385 -842 811 -454 389 -854 821 -418 837 -444 401 -850 417 -854 395 -830 417 -846 819 -432 811 -448 789 -444 839 -454 401 -856 381 -850 825 -410 841 -418 417 -834 411 -840 827 -442 417 -844 799 -472 809 -420 411 -842 419 -848 397 -822 413 -850 799 -486 381 -848 415 -854 423 -16394 449 -358 437 -386 411 -384 449 -382 417 -384 419 -386 417 -386 419 -388 419 -388 419 -388 419 -390 437 -4036 421 -810 425 -820 393 -866 813 -422 415 -856 397 -858 811 -456 427 -820 815 -416 419 -850 401 -854 805 -420 835 -444 409 -842 809 -454 433 -820 421 -838 813 -420 417 -822 435 -820 419 -834 431 -852 411 -866 805 -420 815 -444 805 -454 403 -824 809 -448 819 -448 413 -844 811 -446 811 -456 409 -816 809 -456 389 -866 387 -842 809 -454 827 -432 413 -850 829 -428 809 -452 381 -852 799 -452 413 -852 807 -450 801 -444 409 -872 411 -840 413 -812 413 -832 807 -450 815 -442 801 -454 809 -454 429 -820 419 -838 811 -456 785 -428 409 -842 439 -824 813 -448 415 -858 819 -418 831 -426 449 -808 427 -820 393 -866 421 -808 825 -436 413 -852 403 -884 421 -16394 407 -478 257 -574 229 -576 229 -564 231 -592 267 -490 305 -520 307 -486 309 -522 307 -458 341 -486 337 -4096 343 -882 389 -880 341 -874 807 -476 351 -882 397 -860 807 -450 431 -824 811 -450 399 -824 417 -844 817 -432 807 -448 411 -872 801 -460 417 -810 425 -836 809 -420 411 -838 409 -852 417 -844 425 -852 385 -872 801 -426 841 -420 811 -422 409 -844 809 -454 823 -432 415 -842 835 -450 805 -454 403 -822 809 -450 399 -826 417 -844 821 -434 807 -448 411 -876 801 -440 807 -450 383 -850 833 -416 415 -852 807 -456 811 -444 411 -838 419 -848 401 -852 377 -850 819 -454 795 -436 809 -448 821 -448 411 -846 417 -842 817 -432 811 -412 411 -864 417 -844 791 -438 415 -876 793 -458 809 -450 383 -832 413 -840 407 -866 387 -844 821 -434 413 -874 377 -868 419 -16408 411 -392 421 -390 421 -388 421 -376 427 -394 433 -388 409 -386 411 -384 449 -382 419 -384 419 -384 419 -4018 343 -89684 97 -430 65 -166 163 -66 231 -100 161 -392 161 -64 229 -1056 97 -198 97 -198 259 -166 691 -66 395 -98 131 -100 99 -66 199 -198 1657 -406 365 -462 361 -436 357 -438 387 -444 353 -444 385 -414 387 -448 355 -448 355 -444 395 -394 399 -4050 819 -434 811 -414 819 -450 409 -852 809 -450 805 -448 805 -446 831 -428 409 -846 389 -854 395 -862 811 +RAW_Data: -452 783 -462 811 -446 411 -874 383 -852 403 -852 777 -450 411 -838 805 -452 397 -858 805 -484 387 -872 803 -442 805 -448 383 -846 797 -484 777 -474 381 -846 831 -430 807 -482 387 -852 817 -418 805 -452 823 -430 385 -878 377 -876 411 -846 391 -884 811 -444 805 -420 415 -846 399 -852 807 -452 805 -444 803 -450 803 -454 807 -452 397 -850 395 -862 385 -844 427 -840 809 -456 379 -876 407 -880 383 -846 819 -432 387 -872 375 -854 413 -846 387 -886 779 -476 803 -450 801 -444 415 -846 793 -438 415 -846 417 -822 407 -852 417 -852 817 -444 409 -16426 443 -358 431 -388 409 -386 447 -350 449 -382 419 -386 419 -386 417 -386 419 -388 453 -354 453 -356 445 -4018 813 -416 839 -420 817 -418 451 -816 835 -444 809 -450 811 -440 803 -444 407 -830 419 -844 419 -836 805 -420 835 -444 803 -446 409 -868 421 -814 431 -822 807 -452 397 -828 819 -452 415 -856 787 -454 419 -848 837 -410 839 -416 395 -866 787 -450 811 -454 407 -850 805 -454 805 -470 381 -850 833 -418 805 -438 809 -448 397 -860 421 -810 431 -856 381 -888 791 -424 841 -422 415 -820 437 -818 813 -450 813 -454 803 -446 817 -448 829 -410 451 -816 403 -818 413 -850 409 -846 811 -448 415 -834 413 -884 399 -822 815 -452 381 -852 407 -846 415 -846 385 -866 807 -456 809 -446 835 -412 407 -834 815 -450 415 -822 405 -848 419 -844 427 -852 809 -442 407 -16420 425 -422 335 -486 309 -486 309 -522 307 -492 337 -454 351 -466 329 -498 327 -468 323 -472 355 -442 355 -4120 757 -456 773 -478 781 -458 381 -874 771 -482 801 -470 777 -482 805 -450 399 -826 415 -846 387 -866 805 -420 813 -446 831 -458 417 -846 401 -852 381 -840 835 -420 415 -846 795 -440 413 -842 835 -450 407 -860 811 -418 815 -424 413 -842 807 -454 823 -428 411 -842 801 -454 807 -488 401 -822 805 -448 803 -446 803 -426 447 -844 397 -856 381 -870 411 -870 777 -452 829 -432 385 -840 419 -848 797 -438 809 -450 815 -448 833 -440 803 -452 411 -820 415 -848 409 -844 411 -846 779 -462 409 -848 409 -864 421 -844 793 -438 385 -846 419 -850 399 -838 415 -872 777 -478 803 -448 799 -442 417 -848 799 -438 415 -842 409 -826 417 -844 427 -854 807 -452 409 -16402 461 -352 421 -386 421 -386 419 -388 453 -354 453 -354 447 -378 409 -386 439 -376 421 -410 385 -414 415 -4024 831 -418 809 -438 807 -444 409 -838 809 -456 821 -432 841 -414 255 -87638 131 -66 97 -296 97 -264 131 -196 65 -132 231 -632 197 -664 131 +RAW_Data: -500 395 -132 461 -132 689 -98 2685 -100 997 -1508 99 -2186 231 -166 231 -134 133 -932 65 -268 99 -132 65 -200 97 -68 163 -234 65 -68 99 -930 331 -98 763 -100 2025 -418 353 -446 385 -414 385 -416 387 -448 355 -442 383 -412 397 -424 405 -388 409 -418 379 -418 415 -4014 443 -814 413 -822 817 -454 801 -436 409 -842 409 -866 415 -838 441 -836 811 -432 387 -842 419 -846 793 -440 807 -448 837 -446 803 -448 835 -420 807 -448 383 -868 379 -850 409 -866 387 -844 825 -468 381 -884 793 -426 415 -842 427 -818 817 -440 407 -830 419 -844 429 -852 387 -872 409 -826 811 -450 813 -418 837 -412 409 -864 417 -844 397 -852 809 -454 805 -448 409 -840 809 -420 813 -458 409 -844 407 -860 385 -878 793 -470 809 -420 817 -416 417 -850 403 -852 381 -852 827 -428 447 -844 401 -854 813 -424 421 -840 419 -812 823 -438 415 -846 409 -844 415 -846 389 -868 809 -458 803 -416 409 -866 813 -418 417 -854 397 -862 419 -842 401 -854 415 -16404 435 -376 409 -410 407 -384 439 -384 409 -410 417 -368 421 -410 407 -378 447 -376 415 -378 447 -380 407 -4022 421 -844 423 -822 821 -418 807 -454 429 -820 421 -836 439 -854 421 -810 821 -436 385 -840 441 -822 813 -448 811 -452 803 -444 835 -444 801 -446 801 -426 447 -808 423 -834 413 -852 407 -840 819 -452 389 -856 813 -444 409 -848 415 -812 809 -458 409 -848 411 -842 415 -844 421 -834 415 -834 835 -418 819 -418 807 -456 393 -856 393 -866 421 -846 799 -474 809 -420 421 -836 811 -420 813 -458 407 -850 413 -842 415 -846 819 -428 835 -416 835 -412 407 -832 421 -842 423 -822 813 -446 407 -864 419 -846 799 -440 413 -850 419 -816 797 -442 413 -850 409 -844 417 -846 423 -834 841 -428 805 -414 435 -822 813 -450 413 -822 437 -818 421 -844 429 -854 411 -16406 427 -418 309 -522 307 -488 303 -520 289 -530 295 -500 323 -470 325 -504 321 -476 321 -476 355 -444 357 -4080 355 -906 339 -882 771 -476 777 -486 381 -874 383 -884 375 -884 387 -852 819 -418 417 -846 399 -854 809 -418 815 -446 837 -420 839 -454 801 -436 807 -452 399 -826 417 -844 391 -852 423 -838 809 -452 431 -852 811 -414 409 -836 417 -844 821 -432 385 -876 385 -850 409 -848 415 -854 421 -840 817 -420 815 -424 817 -448 409 -848 413 -844 389 -854 815 -446 829 -426 413 -842 819 -434 809 -446 409 -838 419 -846 401 -852 811 -456 811 -444 803 -418 417 -848 403 -850 381 -864 805 -450 395 -866 419 -848 801 -474 381 -848 411 +RAW_Data: -842 807 -446 381 -872 377 -866 421 -846 401 -854 813 -458 779 -446 407 -832 811 -450 415 -856 399 -856 385 -876 399 -854 411 -16398 435 -392 395 -400 421 -412 385 -412 417 -384 415 -386 415 -418 385 -420 385 -420 417 -390 417 -388 419 -4020 421 -838 421 -812 819 -434 809 -448 397 -864 421 -844 401 -850 413 -858 789 -426 413 -844 419 -836 807 -424 843 -410 829 -442 835 -446 801 -454 809 -420 417 -832 411 -848 249 -88020 133 -896 231 -466 67 -1062 131 -728 163 -98 621 -98 1051 -100 680933 -452 269 -522 273 -554 273 -558 239 -558 271 -490 337 -488 321 -498 295 -500 325 -470 323 -474 353 -4082 757 -492 375 -880 357 -872 777 -486 773 -480 807 -450 805 -444 805 -476 407 -812 413 -834 411 -848 407 -828 813 -450 811 -458 803 -448 835 -446 791 -424 447 -808 427 -818 423 -840 419 -848 401 -854 811 -458 809 -446 801 -416 439 -826 415 -848 813 -430 809 -450 395 -866 419 -846 403 -850 413 -820 407 -848 415 -846 781 -460 805 -446 803 -474 803 -448 835 -420 805 -454 389 -836 409 -842 407 -866 419 -842 399 -854 809 -456 809 -446 409 -840 385 -844 819 -434 809 -450 395 -860 811 -452 393 -886 779 -446 409 -830 419 -842 423 -818 423 -838 419 -844 799 -472 809 -454 385 -844 807 -454 391 -854 395 -860 385 -844 429 -852 809 -454 385 -874 409 -16402 427 -368 455 -358 433 -380 443 -378 415 -378 447 -380 411 -384 409 -406 421 -408 387 -412 415 -386 415 -4026 831 -398 441 -810 417 -832 837 -418 833 -444 803 -446 833 -448 801 -424 449 -810 427 -820 423 -838 419 -812 825 -438 841 -416 845 -446 825 -418 809 -422 419 -822 433 -822 419 -844 425 -820 421 -840 841 -458 797 -436 809 -414 435 -822 419 -844 819 -432 809 -448 395 -864 421 -846 407 -850 411 -808 433 -824 419 -844 819 -432 809 -446 823 -416 837 -454 807 -440 809 -414 435 -828 417 -844 425 -828 415 -848 419 -818 839 -446 807 -422 411 -844 419 -846 795 -438 807 -450 395 -866 811 -454 391 -854 845 -412 407 -832 421 -842 419 -832 411 -824 435 -820 815 -450 811 -460 409 -850 799 -454 407 -824 413 -848 411 -842 415 -844 815 -432 415 -848 405 -16400 441 -432 327 -492 301 -516 307 -484 309 -520 307 -492 339 -454 337 -490 331 -464 327 -500 325 -472 325 -4110 763 -480 373 -852 385 -878 759 -482 775 -474 813 -458 781 -482 789 -454 415 -846 397 -820 411 -840 405 -852 809 -450 811 -458 809 -450 817 -448 803 -426 411 -844 391 -854 393 -866 419 -848 399 -854 811 +RAW_Data: -454 811 -444 803 -418 417 -846 403 -850 809 -452 805 -444 411 -840 419 -846 407 -850 415 -836 385 -842 419 -850 797 -438 807 -452 817 -446 801 -486 813 -444 775 -450 409 -838 419 -810 431 -854 379 -848 405 -884 809 -450 817 -430 385 -874 375 -856 811 -446 809 -422 421 -836 835 -452 419 -848 783 -460 409 -814 407 -856 415 -846 383 -870 381 -848 819 -450 811 -472 383 -850 803 -454 415 -838 399 -854 379 -850 407 -848 811 -448 415 -872 387 -16400 451 -374 445 -374 415 -378 415 -412 411 -384 405 -422 409 -410 387 -410 417 -382 417 -384 415 -420 383 -4030 827 -428 411 -842 425 -820 817 -418 833 -426 845 -452 815 -428 837 -416 409 -842 421 -810 431 -820 421 -89106 265 -662 99 -532 131 -598 97 -668 65 -300 761 -198 231 -132 265 -100 233 -100 197 +RAW_Data: 7855 -12784 1413 -1544 469 -1040 465 -1010 479 -1020 967 -548 445 -1046 973 -524 967 -520 981 -516 483 -1042 449 -1034 949 -528 495 -1008 479 -1016 985 -518 453 -1042 449 -1052 949 -514 483 -1012 985 -512 477 -1042 445 -1050 951 -548 971 -512 975 -520 967 -554 949 -548 451 -1040 967 -520 987 -518 455 -1038 475 -1016 977 -518 983 -514 473 -1018 975 -518 487 -1002 475 -1020 965 -516 477 -1012 1007 -522 445 -1034 491 -1008 973 -524 +RAW_Data: 481 -992 481 -1010 483 -1030 977 -520 487 -1008 973 -522 987 -518 983 -514 965 -522 987 -520 489 -1004 473 -1018 471 -1016 1005 -476 511 -1012 457 -1018 1001 -510 975 -520 471 -1022 483 -1016 969 -536 1003 -454 981 -480 479 -986 981 -486 479 -946 989 -492 973 -484 473 -976 1503 -23606 1433 -1542 493 -1006 473 -1032 441 -1048 971 -514 483 -1012 985 -518 479 -1014 481 -1012 457 -1050 443 -1044 977 -520 473 -1004 495 -1004 969 -556 453 -1036 451 -1038 973 -520 485 -994 981 -520 457 -1050 477 -1014 977 -494 985 -538 961 -512 1005 -518 951 -526 491 -1006 969 -520 985 -524 455 -1044 447 -1048 983 -518 983 -514 441 -1050 981 -518 453 -1042 447 -1050 981 -518 451 -1046 975 -520 451 -1022 483 -1008 1001 -522 447 -1020 485 -1008 473 -1016 981 -550 449 -1044 977 -520 949 -550 979 -516 967 -520 983 -522 455 -1042 447 -1050 451 -1024 981 -520 483 -1018 963 -546 479 -1010 967 -520 483 -1022 975 -522 967 -552 487 -960 481 -990 451 -994 481 -980 479 -986 449 -984 969 -480 983 -510 1465 -23612 1473 -1520 479 -1026 453 -1044 451 -1036 943 -552 453 -1044 949 -518 481 -1018 977 -524 459 -1046 439 -1046 973 -528 463 -1012 471 -1046 943 -552 443 -1034 457 -1042 977 -518 479 -1028 949 -554 451 -1014 481 -1018 981 -524 985 -518 971 -514 979 -522 987 -512 477 -1016 977 -522 969 -552 449 -1016 483 -1014 985 -518 973 -516 481 -1012 967 -552 449 -1020 483 -1010 969 -554 447 -1022 977 -520 475 -1018 479 -1018 975 -522 457 -1036 479 -1016 479 -1002 969 -552 447 -1054 943 -548 969 -520 983 -520 983 -516 969 -518 479 -1030 453 -1044 449 -1048 943 -548 451 -1044 945 -552 975 -518 947 -552 449 -1034 975 -524 455 -1040 969 -520 449 -982 969 -518 945 -484 481 -984 481 -994 447 -986 477 -998 1435 -23658 1441 -1530 483 -1008 483 -1034 449 -1022 977 -520 485 -1018 479 -1018 975 -506 473 -1036 469 -1042 463 -1010 977 -520 487 -1030 451 -1010 981 -520 481 -1018 481 -1014 983 -518 479 -1016 975 -492 497 -1014 467 -1014 977 -520 975 -526 985 -516 979 -506 1005 -496 493 -1008 975 -522 983 -518 453 -1040 475 -1016 975 -524 987 -514 471 -1038 955 -514 473 -1046 445 -1044 967 -514 477 -1016 975 -520 457 -1050 477 -1010 973 -522 473 -1000 479 -1030 453 -1038 969 -506 473 -1050 971 -512 979 -524 955 -548 973 -512 975 -518 475 -1036 473 -1006 493 -1008 975 -520 973 -526 487 -1004 475 -1018 965 -516 1005 -512 481 -1014 985 -518 483 -986 975 -488 977 -480 977 -486 975 -482 481 -982 975 -480 977 -488 1477 -23618 1389 -1634 369 -1114 383 -1078 431 -1072 +RAW_Data: 931 -550 451 -1046 447 -1042 967 -552 945 -522 459 -1042 445 -1050 943 -552 439 -1036 459 -1046 977 -508 477 -1030 455 -1044 945 -552 451 -1020 979 -524 459 -1046 443 -1048 979 -518 967 -534 957 -516 977 -518 973 -528 455 -1042 973 -520 975 -526 459 -1040 481 -1020 969 -510 967 -546 447 -1050 955 -544 441 -1044 449 -1048 953 -550 443 -1046 975 -518 485 -1010 455 -1044 943 -554 447 -1054 449 -1010 475 -1048 943 -550 453 -1040 969 -520 973 -522 985 -514 969 -554 949 -524 459 -1040 477 -1014 483 -1034 947 -520 981 -554 447 -1016 977 -524 983 -516 973 -516 483 -1016 455 -1046 973 -484 977 -518 449 -986 447 -1016 971 -482 449 -1018 443 -1014 449 -984 1461 -129764 65 -3200 133 -464 133 -298 429 -132 265 -98 231 -134 265 -164 3439 -132 727 -132 199 -2058 133 -1644 361 -166 65 -492 165 -264 591 -428 197 -198 201 -98 831 -68 2313 -100 5839 -10922 65 -1320 425 -262 297 -428 97 -362 2463 -98 1025 -66 5263 -5030 99 -6924 461 -1092 133 -98 333 -166 2739 -132 3131 -66 10535 -2008 131 -434 297 -1058 65 -132 99 -198 529 -198 97 -526 97 -66 493 -664 99 -232 2613 -132 5371 -11166 229 -198 163 -394 199 -398 365 -132 99 -166 2121 -100 1195 -68 1821 -100 10635 -468 67 -1256 65 -2144 229 -100 163 -394 593 -98 67 -166 1677 -66 791 -66 335 -98 11033 -566 65 -1460 165 -1520 497 -1254 491 -564 99 -330 99 -232 1227 -132 2973 -66 3661 -11964 131 -132 99 -398 131 -328 97 -232 363 -396 1379 -98 99 -166 1591 -66 12171 -4136 65 -298 265 -298 199 -462 99 -330 65 -166 163 -66 1591 -66 165 -166 12079 -1002 65 -366 465 -530 97 -134 561 -66 497 -494 99 -64 131 -134 1095 -66 6537 -5066 65 -5458 397 -724 165 -466 131 -166 14293 -436 65 -1590 65 -1462 459 -332 65 -396 563 -794 197 -300 1255 -12100 99 -130 495 -166 97 -296 97 -658 757 -98 959 -66 1029 -1346 165 -2620 395 -494 197 -166 163 -198 65 -98 195 -394 821 -98 3063 -100 4469 -12120 497 -166 65 -462 195 -164 295 -66 4361 -100 1755 -100 131 -66 9415 -3840 99 -530 197 -364 463 -330 365 -332 133 -100 165 -166 2113 -100 1461 -132 4175 -3772 97 -7124 231 -1258 165 -100 429 -1326 995 -200 1755 -66 1519 -100 6437 -7198 133 -300 527 -398 165 -232 131 -166 67 -164 16443 -3270 131 -658 131 -726 97 -858 97 -300 331 -100 629 -10288 67 -164 133 -1458 297 -364 65 -98 163 -758 1189 -66 199 -68 1791 -66 897 -132 165 -3410 163 -364 99 -98 99 -66 365 -232 789 -494 65 -328 629 -66 1259 -66 365 -11422 7923 -12864 1405 -1562 +RAW_Data: 451 -1040 441 -1052 449 -1050 945 -554 449 -1052 451 -1020 481 -1010 473 -1050 449 -1052 451 -1040 969 -520 977 -520 455 -1042 977 -522 447 -1056 947 -518 979 -546 447 -1052 451 -1040 441 -1048 983 -518 455 -1044 449 -1018 979 -548 947 -554 449 -1032 481 -992 483 -1012 985 -514 999 -512 479 -1012 485 -1014 961 -544 477 -1010 965 -522 981 -512 483 -1012 487 -1020 477 -1014 479 -1016 459 -1014 471 -1012 1003 -492 997 -522 483 -1016 979 -522 985 -520 975 -512 975 -520 999 -488 985 -514 481 -1006 1001 -522 483 -990 483 -1008 483 -1020 977 -516 975 -518 999 -524 451 -1018 1009 -482 999 -506 983 -524 487 -1004 473 -980 501 -952 517 -940 497 -982 489 -974 987 -452 495 -974 487 -954 1485 -23662 1457 -1556 445 -1026 483 -1010 475 -1016 975 -518 483 -1014 487 -1034 447 -1022 977 -522 457 -1046 475 -1018 975 -524 985 -518 477 -1016 977 -524 459 -1048 969 -514 977 -522 457 -1038 479 -1018 481 -1002 1001 -520 447 -1054 449 -1008 1001 -520 977 -520 451 -1040 475 -1014 479 -1028 949 -518 983 -542 447 -1058 449 -1044 947 -552 447 -1024 977 -520 967 -542 479 -1024 451 -1040 441 -1050 451 -1028 481 -1014 483 -1010 965 -548 973 -518 485 -1010 981 -516 967 -520 983 -524 981 -514 969 -538 967 -518 481 -1016 973 -524 485 -1016 465 -1012 479 -1020 983 -532 959 -514 975 -554 949 -526 985 -512 969 -554 967 -534 461 -1042 443 -1014 967 -478 455 -1006 969 -486 967 -480 983 -486 969 -514 451 -982 1461 -23692 563 -4014 291 -1220 263 -1228 829 -620 883 -626 851 -608 903 -622 387 -1082 391 -1102 409 -1084 913 -588 941 -548 443 -1056 945 -522 445 -1046 971 -552 977 -516 441 -1048 481 -992 483 -1010 979 -554 451 -1018 481 -1014 983 -518 977 -514 479 -1040 447 -1034 485 -996 975 -520 979 -520 483 -1016 481 -1008 999 -506 471 -1050 971 -514 975 -520 473 -1000 483 -1020 481 -1008 473 -1018 481 -1020 481 -1008 967 -554 945 -518 481 -1038 967 -520 985 -520 981 -514 967 -520 985 -520 981 -508 479 -1016 1003 -518 479 -1010 479 -1010 473 -1018 975 -516 979 -520 983 -520 975 -514 977 -518 999 -520 979 -518 451 -1040 479 -986 479 -962 1007 -486 451 -986 975 -486 977 -482 483 -980 477 -982 1473 -23656 1453 -1548 447 -1016 485 -1012 491 -1012 973 -520 981 -526 983 -514 971 -554 947 -526 491 -1008 475 -1020 983 -498 989 -516 483 -1014 977 -524 453 -1044 979 -518 979 -520 453 -1042 449 -1048 447 -1022 975 -518 475 -1050 447 -1020 977 -522 983 -518 481 -1016 481 -1012 473 -1002 973 -550 945 -552 449 -1050 447 -1020 975 -522 487 -1034 973 -520 +RAW_Data: 979 -514 443 -1046 479 -1028 451 -1042 451 -1048 447 -1022 485 -1014 983 -520 973 -516 483 -1012 983 -518 973 -516 977 -520 1003 -520 975 -520 981 -514 475 -1034 969 -516 479 -1016 447 -1046 475 -1018 975 -516 975 -522 983 -510 469 -1010 1007 -518 951 -530 989 -516 973 -556 951 -494 481 -978 487 -978 975 -460 1005 -466 979 -486 969 -508 981 -450 1489 -23666 571 -4036 269 -1224 257 -1250 787 -642 867 -622 883 -622 359 -1136 373 -1086 421 -1080 417 -1074 935 -550 947 -552 445 -1048 939 -552 451 -1046 947 -552 947 -550 451 -1040 443 -1048 453 -1024 977 -522 471 -1034 449 -1020 973 -540 975 -508 479 -1032 453 -1042 449 -1050 977 -518 979 -518 449 -1018 481 -1018 975 -518 473 -1034 963 -542 961 -544 447 -1044 473 -1020 479 -1014 481 -1010 473 -1032 471 -1010 959 -546 973 -492 499 -1006 997 -510 977 -524 953 -552 971 -512 973 -508 979 -554 451 -1016 977 -518 471 -1038 485 -1010 457 -1036 969 -506 999 -520 481 -1014 975 -522 967 -520 975 -548 451 -1038 475 -1022 965 -518 463 -978 985 -486 465 -978 457 -1016 463 -978 985 -486 963 -480 1477 -129906 495 -726 197 -328 295 -132 2547 -66 233 -98 11033 -1856 233 -1458 65 -198 165 -134 199 -168 101 -694 463 -530 165 -300 99 -232 2479 -98 1745 -98 3029 -132 163 -1460 65 -500 65 -400 99 -664 895 -398 65 -564 331 -166 97 -66 197 -98 3813 -98 10097 -3848 165 -232 67 -266 397 -596 165 -66 199 -166 99 -66 199 -398 165 -166 1721 -232 429 -166 133 -330 133 -698 493 -200 197 -428 11029 -12118 65 -198 199 -68 231 -230 101 -166 99 -664 131 -132 3163 -4238 331 -298 531 -398 299 -98 199 -166 563 -100 131 -98 893 -66 3141 -1556 133 -1722 131 -830 197 -262 195 -66 163 -462 195 -396 195 -134 499 -132 265 -66 1717 -166 3175 -11366 199 -164 131 -66 163 -98 525 -98 363 -264 4495 -100 229 -66 131 -66 593 -3002 97 -394 131 -426 99 -462 597 -692 295 -298 431 -230 4231 -66 9711 -3246 131 -100 99 -400 263 -498 65 -100 297 -98 99 -132 65 -862 131 -66 365 -396 99 -166 1991 -98 1611 -132 10333 -790 65 -1984 99 -896 165 -332 365 -232 131 -830 65 -66 397 -166 197 -66 65 -496 199 -100 9975 -1728 67 -5008 727 -98 131 -100 2873 -66 12011 -3150 67 -960 99 -234 99 -298 231 -232 195 -266 165 -296 261 -166 757 -66 629 -196 657 -100 197 -134 297 -364 11237 -1684 65 -2076 165 -462 491 -100 663 -630 329 -264 263 -100 1357 -66 461 -1676 99 -1782 295 -296 65 -296 163 -230 99 -132 295 -66 163 -362 197 -724 757 -66 +RAW_Data: 3785 -66 13551 -1808 97 -730 65 -100 231 -132 131 -1230 593 -232 1579 -66 2667 -200 101 -3480 165 -692 133 -396 427 -1524 363 -66 431 -132 10305 -8288 461 -628 67 -430 725 -66 1053 -66 4501 -230 165 -66 331 -66 355 -266 263 -132 63 -562 459 -462 197 -66 129 -132 65 -100 2643 -132 2107 -66 9651 -3692 99 -100 195 -294 97 -660 759 -328 165 -560 891 -66 1953 -66 11305 -362 263 -662 131 -432 65 -134 563 -430 131 -132 1819 -100 165 -166 1061 -98 10089 -2476 65 -854 395 -198 99 -492 131 -164 229 -466 199 -428 299 -100 927 -200 1557 -134 4269 -10464 133 -1624 65 -198 265 -398 131 -430 729 -134 6189 -66 5421 -2082 165 -3342 19967 -12808 1439 -1536 453 -1046 449 -1032 449 -1056 947 -552 977 -522 977 -518 453 -1038 977 -522 977 -520 457 -1038 977 -506 1005 -496 495 -1008 975 -538 973 -530 465 -1008 975 -554 453 -1036 947 -518 487 -1008 475 -1042 443 -1050 461 -1008 1005 -510 447 -1048 985 -510 469 -1006 1005 -494 997 -514 975 -514 975 -504 999 -506 479 -1034 491 -1010 975 -508 973 -524 491 -1004 473 -1018 997 -520 975 -512 975 -518 473 -1030 983 -516 981 -514 471 -998 997 -522 481 -1012 481 -1012 457 -1050 973 -512 977 -524 459 -1016 1003 -512 479 -1014 459 -1016 475 -1012 1007 -522 969 -502 495 -1008 477 -1030 965 -522 975 -514 479 -1000 471 -1062 471 -964 483 -982 471 -1000 471 -980 979 -448 503 -988 465 -976 487 -974 1459 -23696 1407 -1616 401 -1068 429 -1080 419 -1058 935 -566 923 -584 417 -1078 939 -524 457 -1042 973 -550 443 -1028 949 -554 945 -552 447 -1022 979 -518 971 -542 479 -1024 947 -550 441 -1048 979 -518 453 -1044 449 -1050 449 -1020 485 -1014 981 -518 479 -1014 975 -524 459 -1036 973 -516 979 -518 971 -552 945 -550 945 -552 449 -1030 479 -1026 947 -554 949 -552 449 -1018 479 -1008 981 -518 975 -548 945 -554 451 -1034 967 -514 997 -514 445 -1036 967 -554 447 -1022 485 -1010 475 -1016 975 -518 977 -520 487 -1014 973 -552 451 -1040 441 -1050 447 -1022 485 -1014 987 -516 479 -1014 483 -1014 459 -1046 969 -514 449 -1044 967 -546 973 -488 447 -1016 443 -1000 973 -490 475 -980 983 -482 441 -1016 465 -976 1475 -23652 1451 -1548 479 -1014 461 -1014 471 -1044 975 -520 971 -502 495 -1012 977 -506 1005 -498 989 -516 481 -1016 975 -520 981 -514 475 -1014 979 -522 983 -512 475 -1022 965 -514 471 -1046 973 -494 473 -1016 475 -1046 447 -1050 463 -1012 999 -512 481 -1012 983 -520 477 -1014 977 -524 955 -548 973 -512 975 -520 967 -556 449 -1020 483 -1012 983 -520 973 -516 481 -1008 473 -1034 +RAW_Data: 967 -538 963 -544 973 -522 471 -1006 989 -512 1007 -520 443 -1036 985 -516 449 -1048 451 -1022 483 -1012 983 -520 977 -514 481 -1012 979 -514 483 -1022 481 -1010 471 -1020 479 -1020 979 -524 457 -1048 973 -514 483 -1012 981 -520 483 -1018 481 -1014 485 -986 467 -980 981 -486 469 -978 457 -1004 963 -480 983 -486 971 -514 1441 -23704 1383 -1628 389 -1112 385 -1092 407 -1092 915 -552 941 -570 441 -1064 423 -1046 451 -1044 939 -556 455 -1048 945 -552 973 -522 453 -1046 945 -552 947 -550 451 -1040 969 -518 479 -1028 951 -552 451 -1018 479 -1018 483 -1014 459 -1044 971 -514 483 -1010 971 -544 447 -1020 977 -524 987 -518 973 -516 979 -524 985 -518 479 -1016 447 -1050 953 -548 971 -514 483 -1014 459 -1048 967 -514 977 -526 953 -548 443 -1046 975 -492 995 -512 471 -1050 943 -552 445 -1032 455 -1044 449 -1048 941 -550 945 -552 449 -1050 945 -552 451 -1044 449 -1018 479 -1016 479 -1002 969 -542 973 -522 455 -1040 477 -1022 967 -534 959 -514 975 -554 469 -1008 449 -980 469 -1008 943 -484 1001 -484 467 -980 983 -482 961 -514 1439 -23700 1469 -1510 495 -1008 473 -1036 463 -1012 969 -546 973 -522 473 -1018 479 -1014 975 -526 955 -516 475 -1046 975 -490 999 -518 481 -1014 975 -520 967 -514 481 -1022 979 -524 457 -1048 971 -514 481 -1010 485 -1020 477 -1014 479 -1000 1001 -522 451 -1020 977 -520 473 -1032 967 -538 959 -514 1005 -522 965 -504 989 -514 475 -1046 441 -1050 971 -514 975 -520 473 -1018 481 -1014 979 -520 983 -520 977 -516 485 -1010 979 -544 975 -518 453 -1042 981 -520 453 -1024 483 -1010 457 -1050 975 -512 975 -524 459 -1048 973 -514 481 -1010 473 -1016 479 -1016 477 -1036 967 -506 995 -512 965 -546 445 -1048 957 -516 1005 -512 445 -1046 979 -486 473 -980 979 -486 473 -980 981 -486 473 -980 485 -986 467 -976 1477 -142204 197 -1486 165 -198 165 -664 295 -232 99 -266 231 -166 3045 -100 13411 -3670 197 -498 131 -166 231 -198 165 -66 265 -134 129 -1062 431 -130 465 -134 13447 -3848 329 -100 163 -298 99 -164 463 -98 197 -98 131 -198 65 -296 493 -264 789 -66 7225 -12438 99 -164 463 -132 197 -630 65 -198 2487 -66 165 -100 10097 -6554 459 -664 297 -460 4925 -132 6063 -12078 497 -98 99 -200 97 -234 165 -298 1721 -134 265 -100 3035 -100 12081 -3674 231 -100 97 -200 97 -264 461 -100 99 -132 231 -100 97 -430 527 -200 231 -64 2081 -132 327 -100 529 -66 831 -66 3067 -4704 99 -5520 97 -496 67 -198 167 -498 693 -462 2341 -15926 65 -1392 659 -134 131 -298 165 -66 99 -298 4777 -4208 429 -66 +RAW_Data: 377 -386 1117 -410 1121 -352 1141 -384 1151 -378 1119 -350 1139 -386 1115 -1134 389 -1114 395 -1122 363 -1136 389 -358 1167 -356 1145 -1120 389 -1110 391 -356 1139 -1126 389 -1114 391 -1122 363 -1146 389 -1110 395 -1122 363 -1138 389 -1110 393 -1122 363 -1140 389 -1112 393 -1120 389 -1118 389 -1112 397 -1124 363 -1142 389 -1112 359 -1154 367 -1134 389 -1144 365 -1138 355 -394 1119 -380 1107 -1152 353 -398 1113 -384 1139 -1118 385 -376 1141 -386 1129 -350 1143 -388 1109 -1132 389 -1112 393 -390 1107 -1128 389 -1112 397 -388 1111 -1132 389 -358 1127 -1118 417 -1116 383 -1120 353 -1158 389 -1108 375 -384 1121 -408 1123 -350 1139 -386 1111 -1130 389 -1114 395 -1122 395 -1114 389 -1116 395 -1122 363 -1138 387 -9444 373 -374 379 -374 381 -346 403 -346 389 -376 389 -390 353 -376 359 -382 383 -360 419 -360 359 -386 359 -2264 777 -356 1127 -390 1143 -362 1131 -1138 365 -1122 359 -386 1153 -1106 377 -1152 385 -372 1113 -1140 385 -1118 381 -1114 383 -1150 383 -1120 355 -1122 389 -358 1165 -386 1113 -1128 389 -360 1125 -384 1131 -368 1157 -350 1139 -386 1115 -406 1099 -384 1141 -1122 383 -1110 373 -1130 385 -1128 393 -380 1131 -380 1129 -1112 383 -1132 391 -356 1143 -1124 383 -1130 367 -1136 385 -1136 387 -1112 371 -1120 389 -1118 383 -1130 371 -1130 383 -1110 383 -1120 413 -1118 383 -1144 347 -1144 389 -1110 393 -1122 363 -1140 389 -1112 359 -1154 363 -1146 389 -1110 393 -374 1115 -384 1115 -1144 385 -368 1141 -388 1111 -1110 421 -360 1125 -388 1109 -392 1137 -358 1125 -1144 365 -1138 389 -358 1123 -1118 401 -1138 389 -360 1121 -1120 417 -358 1109 -1154 355 -1120 375 -1138 385 -1130 391 -1136 355 -398 1115 -380 1141 -384 1121 -382 1119 -1104 413 -1118 355 -1156 387 -1112 377 -1122 389 -1118 387 -1112 397 -9422 417 -352 363 -416 355 -388 345 -382 377 -380 375 -380 375 -380 375 -380 385 -356 379 -366 385 -374 387 -2246 745 -380 1141 -386 1113 -370 1125 -1140 373 -1152 355 -394 1117 -1140 381 -1120 385 -374 1145 -1112 385 -1122 381 -1116 383 -1120 375 -1120 389 -1120 373 -380 1171 -358 1121 -1142 377 -356 1127 -384 1137 -378 1155 -390 1105 -366 1125 -386 1135 -386 1111 -1132 389 -1112 393 -1120 365 -1138 387 -360 1163 -356 1143 -1126 387 -1114 357 -386 1141 -1126 383 -1130 365 -1132 381 -1140 377 -1116 383 -1130 371 -1128 381 -1140 347 -1148 385 -1128 369 -1128 381 -1142 377 -1114 389 -1112 395 -1124 361 -1142 389 -1114 393 -1122 365 -1138 389 -1114 397 -1108 389 -392 1119 -350 1139 -1152 355 -396 1115 -382 1109 -1156 385 -374 1111 -384 1139 -368 1147 -388 1109 -1112 389 -1120 383 -388 1107 -1150 389 -1112 +RAW_Data: 393 -390 1109 -1128 389 -360 1125 -1120 381 -1152 383 -1118 353 -1158 387 -1112 375 -386 1117 -408 1121 -350 1143 -388 1109 -1132 389 -1114 391 -1122 395 -1120 389 -1112 393 -1122 365 -1136 389 -9442 373 -376 389 -354 377 -366 387 -384 357 -378 361 -418 347 -394 385 -358 363 -382 361 -414 357 -392 333 -2290 751 -384 1113 -406 1121 -350 1137 -1136 353 -1160 385 -356 1135 -1120 361 -1146 385 -388 1137 -1108 361 -1150 387 -1112 395 -1136 349 -1154 353 -1142 371 -384 1145 -378 1117 -1138 381 -382 1087 -410 1121 -382 1143 -380 1121 -380 1115 -384 1107 -418 1115 -1106 385 -1148 365 -1118 359 -1146 387 -388 1135 -388 1113 -1126 383 -1130 367 -376 1113 -1142 383 -1114 375 -1154 355 -1160 385 -1110 371 -1152 357 -1118 385 -1146 365 -1122 361 -1146 387 -1114 395 -1134 355 -1160 351 -1146 369 -1154 355 -1120 387 -1114 397 -1136 357 -1118 407 -1144 351 -1134 359 -420 1111 -366 1131 -1142 379 -384 1089 -410 1119 -1142 379 -366 1141 -386 1109 -388 1131 -350 1141 -1118 391 -1114 375 -378 1153 -1116 385 -1136 383 -358 1139 -1120 359 -420 1099 -1142 383 -1118 383 -1138 347 -1144 385 -1144 369 -386 1113 -404 1089 -386 1141 -382 1099 -1136 381 -1128 375 -1130 383 -1140 359 -1146 387 -1114 395 -1138 357 -9430 383 -378 359 -418 347 -392 387 -360 363 -384 361 -414 357 -386 347 -384 375 -382 385 -358 383 -364 387 -2252 773 -354 1131 -384 1137 -386 1111 -1132 387 -1112 397 -388 1113 -1124 389 -1116 387 -388 1115 -1132 389 -1114 393 -1120 365 -1140 389 -1114 357 -1154 365 -378 1151 -358 1127 -1156 367 -376 1135 -358 1125 -388 1141 -368 1125 -386 1133 -388 1109 -370 1155 -1106 375 -1122 389 -1118 389 -1114 395 -386 1117 -410 1123 -1106 377 -1130 383 -388 1107 -1152 353 -1148 353 -1150 367 -1142 389 -1110 397 -1120 365 -1138 389 -1110 391 -1122 363 -1142 387 -1116 389 -1120 391 -1116 389 -1116 395 -1122 365 -1140 389 -1114 357 -1154 363 -1138 389 -1142 365 -1138 355 -396 1115 -382 1141 -1118 353 -400 1111 -384 1139 -1120 381 -386 1139 -366 1119 -392 1121 -388 1107 -1152 389 -1114 355 -388 1139 -1126 387 -1114 397 -376 1111 -1144 375 -380 1129 -1138 375 -1098 385 -1140 377 -1118 387 -1144 371 -386 1115 -404 1121 -348 1137 -386 1113 -1134 389 -1112 395 -1124 395 -1118 389 -1110 395 -1122 363 -1138 389 -9418 391 -360 407 -384 361 -388 355 -390 367 -376 373 -380 387 -356 377 -366 385 -388 355 -378 359 -384 377 -2266 777 -346 1149 -388 1107 -390 1129 -1104 415 -1118 353 -398 1113 -1138 383 -1122 381 -388 1141 -1118 389 -1112 357 -1154 365 -1138 389 -1114 357 -1154 363 -378 1155 -358 1123 -1156 381 -360 1107 -384 +RAW_Data: 1153 -378 1119 -382 1143 -382 1121 -382 1117 -382 1113 -1120 389 -1120 387 -1112 399 -1120 393 -392 1119 -350 1141 -1154 357 -1116 389 -360 1163 -1120 365 -1138 387 -1114 389 -1122 389 -1116 387 -1114 397 -1104 379 -1156 353 -1148 367 -1118 377 -1122 423 -1110 373 -1122 389 -1118 383 -1130 373 -1128 383 -1140 345 -1146 383 -1130 399 -1130 353 -1142 377 -358 1127 -384 1143 -1118 385 -372 1111 -386 1137 -1120 381 -388 1141 -364 1127 -384 1133 -374 1111 -1148 383 -1114 373 -384 1115 -1136 387 -1144 371 -386 1115 -1132 387 -360 1123 -1150 345 -1148 383 -1128 371 -1132 381 -1140 379 -390 1123 -350 1139 -388 1113 -406 1089 -1142 373 -1120 389 -1118 423 -1110 371 -1120 379 -1122 407 -1104 417 -9440 389 -356 379 -366 385 -388 355 -378 359 -384 381 -394 387 -358 361 -386 359 -416 355 -388 345 -384 377 -2262 747 -384 1149 -380 1115 -382 1113 -1120 389 -1120 389 -360 1129 -1152 367 -1136 389 -358 1131 -1152 367 -1138 389 -1116 355 -1152 367 -1134 389 -1116 353 -388 1141 -368 1123 -1138 375 -386 1113 -408 1121 -350 1175 -372 1105 -386 1145 -352 1141 -366 1145 -1114 385 -1116 377 -1122 389 -1120 421 -354 1139 -388 1109 -1132 383 -1130 369 -374 1113 -1144 385 -1114 377 -1120 391 -1128 373 -1138 385 -1130 359 -1138 377 -1120 373 -1138 383 -1130 359 -1138 379 -1160 375 -1106 385 -1130 393 -1120 377 -1118 389 -1112 393 -1140 355 -1120 421 -1114 371 -1122 391 -390 1123 -350 1139 -1134 353 -402 1113 -384 1141 -1118 385 -376 1143 -352 1161 -352 1135 -386 1113 -1132 387 -1114 395 -388 1111 -1128 387 -1114 399 -374 1115 -1142 375 -380 1117 -1118 387 -1144 363 -1136 385 -1130 367 -1130 383 -388 1107 -392 1129 -380 1115 -384 1113 -1136 389 -1114 393 -1124 393 -1120 389 -1114 393 -1124 363 -1140 389 -9416 391 -360 405 -386 329 -416 357 -392 365 -374 377 -380 343 -412 341 -412 353 -390 375 -366 385 -386 355 -2264 743 -394 1123 -388 1111 -392 1133 -1110 395 -1120 363 -382 1133 -1142 381 -1118 383 -376 1111 -1146 383 -1122 383 -1146 347 -1150 381 -1116 353 -1158 343 -410 1133 -382 1111 -1152 355 -394 1119 -382 1109 -382 1153 -378 1131 -354 1137 -396 1119 -388 1111 -1150 351 -1152 351 -1150 365 -1136 387 -356 1131 -386 1143 -1122 387 -1112 357 -420 1107 -1128 387 -1114 359 -1152 363 -1148 387 -1114 395 -1122 361 -1140 387 -1110 395 -1120 361 -1140 387 -1114 393 -1154 355 -1120 387 -1146 365 -1118 361 -1146 387 -1112 395 -1120 363 -1140 385 -1144 367 -1120 359 -420 1099 -384 1139 -1118 383 -384 1109 -392 1129 -1138 379 -366 1147 -388 1109 -386 1099 -384 1139 -1132 349 -1158 375 -380 1131 -1104 411 -1122 351 -416 1111 -1148 +RAW_Data: 353 -396 1121 -1142 347 -1150 381 -1116 355 -1156 375 -1144 387 -360 1119 -388 1107 -394 1131 -386 1101 -1152 363 -1138 387 -1112 391 -1152 357 -1116 375 -1136 383 -1122 383 -9448 357 -392 357 -398 363 -378 385 -358 383 -364 389 -386 357 -380 389 -386 347 -382 375 -384 375 -380 373 -2262 747 -376 1145 -390 1107 -386 1129 -1104 413 -1120 353 -396 1115 -1140 381 -1122 383 -376 1143 -1110 385 -1118 383 -1114 417 -1114 383 -1120 353 -1156 389 -356 1135 -386 1113 -1134 389 -358 1129 -390 1107 -392 1153 -358 1127 -388 1143 -362 1131 -356 1131 -1154 365 -1136 389 -1114 357 -1150 363 -386 1153 -358 1125 -1118 417 -1120 381 -350 1123 -1134 391 -1112 395 -1124 395 -1116 389 -1112 393 -1124 363 -1140 389 -1114 359 -1154 363 -1138 389 -1114 391 -1124 361 -1144 389 -1112 393 -1122 363 -1138 389 -1112 391 -1122 363 -1138 389 -1144 365 -1124 361 -382 1155 -350 1137 -1120 391 -386 1131 -350 1151 -1120 383 -378 1141 -352 1137 -394 1117 -390 1107 -1150 389 -1114 355 -388 1143 -1120 387 -1112 397 -388 1113 -1130 385 -344 1163 -1104 379 -1122 373 -1140 383 -1130 389 -1124 359 -386 1127 -386 1139 -368 1141 -390 1107 -1112 387 -1116 385 -1150 367 -1140 389 -1112 393 -1124 363 -1136 389 -9444 379 -340 417 -360 359 -386 359 -416 355 -386 347 -384 375 -382 375 -380 375 -378 375 -380 385 -356 379 -2278 745 -354 1151 -368 1141 -390 1105 -1114 387 -1116 385 -386 1141 -1120 389 -1114 389 -388 1113 -1130 389 -1112 393 -1124 363 -1138 389 -1112 389 -1122 363 -380 1159 -350 1137 -1122 391 -388 1097 -384 1139 -382 1125 -386 1145 -352 1141 -366 1145 -390 1107 -1110 387 -1150 353 -1150 367 -1138 387 -360 1125 -390 1109 -1152 389 -1112 357 -388 1141 -1122 389 -1110 391 -1122 395 -1112 389 -1110 397 -1120 363 -1144 389 -1114 391 -1122 365 -1138 389 -1116 389 -1142 355 -1120 389 -1112 397 -1122 363 -1140 389 -1110 393 -1130 349 -1140 405 -1134 389 -1112 357 -388 1141 -364 1129 -1142 367 -388 1111 -370 1131 -1140 383 -364 1149 -388 1109 -388 1137 -356 1127 -1118 383 -1120 413 -350 1121 -1132 407 -1140 355 -364 1149 -1112 371 -406 1129 -1104 409 -1098 383 -1116 417 -1118 381 -1118 385 -388 1111 -390 1129 -350 1137 -386 1113 -1138 389 -1114 395 -1122 393 -1120 389 -1112 393 -1124 363 -1138 389 -9444 379 -340 417 -360 359 -386 361 -414 357 -386 347 -384 375 -382 375 -380 375 -380 375 -378 373 -380 373 -2262 749 -386 1111 -408 1117 -348 1143 -1120 391 -1116 389 -358 1127 -1154 365 -1136 387 -360 1129 -1154 365 -1136 389 -1114 357 -1154 365 -1134 389 -1112 357 -390 1137 -406 1119 -1106 377 -386 1117 -406 1123 -350 1143 -384 +RAW_Data: 1149 -378 1121 -350 1145 -380 1133 -1104 375 -1136 385 -1130 359 -1138 379 -400 1129 -354 1139 -1148 355 -1150 365 -378 1119 -1146 355 -1152 365 -1134 389 -1110 397 -1122 363 -1140 389 -1110 395 -1122 363 -1142 389 -1116 357 -1152 365 -1146 389 -1112 393 -1130 349 -1138 383 -1116 413 -1120 353 -1122 387 -1114 397 -1154 355 -1124 387 -360 1133 -384 1131 -1134 383 -376 1133 -352 1133 -1132 383 -376 1139 -378 1135 -380 1115 -382 1141 -1118 353 -1160 387 -356 1133 -1118 379 -1158 353 -392 1133 -1104 379 -398 1117 -1138 383 -1118 353 -1164 353 -1146 403 -1120 353 -398 1115 -382 1143 -384 1089 -412 1121 -1106 377 -1154 355 -1118 407 -1146 351 -1130 395 -1138 355 -1118 407 -9434 353 -396 363 -380 383 -360 383 -364 389 -386 357 -414 355 -386 345 -386 375 -382 375 -380 375 -378 375 -2256 769 -384 1119 -382 1117 -380 1113 -1122 389 -1118 389 -360 1131 -1140 377 -1118 421 -354 1139 -1140 355 -1118 405 -1104 387 -1128 391 -1122 363 -1138 387 -360 1157 -354 1143 -1128 387 -360 1121 -388 1141 -362 1129 -384 1139 -388 1111 -370 1127 -384 1135 -1122 363 -1140 389 -1116 393 -1122 395 -356 1129 -384 1141 -1120 383 -1134 387 -356 1133 -1130 349 -1140 383 -1116 413 -1118 381 -1110 377 -1146 389 -1114 391 -1124 365 -1138 391 -1112 357 -1150 365 -1144 387 -1112 395 -1122 361 -1140 387 -1112 393 -1104 379 -1158 353 -1144 403 -1118 353 -1158 353 -390 1133 -390 1107 -1130 389 -358 1125 -388 1111 -1154 389 -358 1129 -386 1131 -368 1129 -382 1139 -1118 353 -1164 387 -356 1135 -1120 393 -1118 389 -358 1131 -1154 365 -376 1135 -1108 395 -1136 347 -1126 387 -1144 403 -1120 353 -398 1115 -384 1141 -372 1103 -386 1145 -1108 387 -1120 383 -1116 403 -1140 389 -1116 353 -1146 361 -1144 389 -9420 393 -362 401 -338 377 -388 385 -374 361 -382 375 -382 375 -380 373 -380 373 -380 373 -380 389 -354 379 -2272 743 -388 1137 -360 1121 -386 1111 -1152 351 -1148 359 -384 1143 -1126 387 -1114 391 -384 1117 -1130 383 -1132 369 -1134 351 -1138 377 -1142 385 -1112 359 -420 1107 -406 1121 -1104 377 -384 1119 -406 1119 -352 1171 -382 1123 -378 1119 -384 1107 -384 1119 -1136 385 -1116 393 -1120 361 -1142 387 -388 1137 -386 1113 -1128 385 -1116 357 -420 1113 -1124 387 -1114 359 -1148 395 -1116 385 -1146 363 -1116 361 -1144 387 -1144 363 -1120 361 -1144 385 -1112 393 -1152 355 -1154 353 -1144 367 -1136 355 -1158 351 -1146 369 -1120 361 -1144 385 -1148 369 -1152 357 -392 1117 -380 1107 -1152 355 -398 1119 -382 1109 -1152 385 -374 1113 -386 1139 -366 1145 -388 1111 -1110 385 -1148 353 -386 1139 -1124 387 -1142 365 -386 1113 -1132 385 -360 1125 -1144 +RAW_Data: 363 -1140 387 -1114 357 -1152 363 -1146 387 -358 1129 -386 1139 -366 1125 -384 1139 -1120 363 -1140 387 -1112 393 -1130 381 -1136 383 -1114 371 -1132 383 -116626 65 -934 133 -1954 131 -102 133 -136 97 -332 65 -430 299 -296 129 -100 265 -168 367 -100 65 -66 231 -336 9643 -7766 529 -68 467 -166 65 -134 99 -500 331 -132 65 -130 329 -98 497 -100 1195 -100 1959 -66 4163 -7346 97 -392 165 -194 97 -2978 433 -298 531 -298 65 -200 131 -132 261 -98 229 -68 12837 -340 99 -268 165 -134 65 -898 67 -100 265 -66 165 -100 597 -166 199 -298 199 -200 99 -132 233 -132 299 -132 233 -166 65 -66 4021 -168 133 -68 231 -168 4647 -130 1399 -7750 133 -1714 197 -2480 131 -200 65 -100 265 -890 63 -1152 197 -98 293 -134 65 -300 361 -100 1035 -100 231 -132 299 -100 3399 -66 6287 -4506 99 -100 65 -130 99 -196 461 -98 331 -164 97 -162 227 -64 197 -98 229 -130 195 -100 425 -526 165 -130 95 -522 457 -560 233 -98 261 -66 1155 -100 259 -130 1407 -98 553 -66 7793 -494 65 -232 65 -3652 229 -2716 361 -266 333 -200 133 -166 99 -132 267 -66 133 -132 199 -166 331 -132 331 -166 197 -950 229 -198 303 -298 365 -100 4839 -3816 165 -130 229 -696 131 -130 261 -262 97 -166 263 -894 165 -230 365 -566 129 -560 197 -324 99 -98 261 -134 131 -100 67 -334 67 -232 199 -132 165 -302 67 -100 1467 -98 459 -100 1081 -130 131 -66 8927 -232 165 -3104 99 -2812 65 -982 131 -98 195 -98 263 -264 231 -66 195 -132 193 -164 65 -100 365 -132 1629 -66 1009 -132 8383 -632 131 -3060 131 -492 425 -100 763 -166 371 -132 1197 -134 229 -694 461 -366 365 -98 329 -198 267 -168 399 -68 131 -332 493 -132 231 -132 569 -66 7765 -7568 99 -532 65 -634 133 -3540 65 -100 263 -592 261 -1484 299 -302 265 -234 1129 -304 99 -436 163 -360 97 -556 231 -166 265 -1164 165 -134 235 -100 163 -332 297 -100 197 -132 99 -566 133 -234 133 -328 295 -98 985 -98 163 -396 399 -134 1557 -134 297 -266 6875 -68 1759 -7194 133 -166 99 -266 65 -432 67 -432 393 -5086 99 -66 199 -68 263 -866 429 -100 359 -130 261 -132 267 -134 533 -134 9251 -4184 65 -1156 165 -198 65 -426 297 -492 67 -164 131 -198 259 -164 199 -100 733 -134 865 -100 397 -132 65 -100 197 -66 327 -164 227 -98 231 -132 97 -262 99 -130 229 -66 589 -96 1119 -98 1905 -7486 599 -66 561 -66 359 -98 757 -162 261 -66 323 -130 5573 -8538 99 -894 131 -594 229 -364 63 -1378 197 -1682 331 -100 199 -166 diff --git a/documentation/HardwareTargets.md b/documentation/HardwareTargets.md deleted file mode 100644 index 0c3474839..000000000 --- a/documentation/HardwareTargets.md +++ /dev/null @@ -1,44 +0,0 @@ -## What a Firmware Target is - -Flipper's firmware is modular and supports different hardware configurations in a common code base. It encapsulates hardware-specific differences in `furi_hal`, board initialization code, linker files, SDK data and other information in a _target definition_. - -Target-specific files are placed in a single sub-folder in `firmware/targets`. It must contain a target definition file, `target.json`, and may contain other files if they are referenced by current target's definition. By default, `fbt` gathers all source files in target folder, unless they are explicitly excluded. - -Targets can inherit most code parts from other targets, to reduce common code duplication. - - -## Target Definition File - -A target definition file, `target.json`, is a JSON file that can contain the following fields: - -* `include_paths`: list of strings, folder paths relative to current target folder to add to global C/C++ header path lookup list. -* `sdk_header_paths`: list of strings, folder paths relative to current target folder to gather headers from for including in SDK. -* `startup_script`: filename of a startup script, performing initial hardware initialization. -* `linker_script_flash`: filename of a linker script for creating the main firmware image. -* `linker_script_ram`: filename of a linker script to use in "updater" build configuration. -* `linker_script_app`: filename of a linker script to use for linking .fap files. -* `sdk_symbols`: filename of a .csv file containing current SDK configuration for this target. -* `linker_dependencies`: list of libraries to link the firmware with. Note that those not in the list won't be built by `fbt`. Also several link passes might be needed, in such case you may need to specify same library name twice. -* `inherit`: string, specifies hardware target to borrow main configuration from. Current configuration may specify additional values for parameters that are lists of strings, or override values that are not lists. -* `excluded_sources`: list of filenames from the inherited configuration(s) NOT to be built. -* `excluded_headers`: list of headers from the inherited configuration(s) NOT to be included in generated SDK. -* `excluded_modules`: list of strings specifying fbt library (module) names to exclude from being used to configure build environment. - - -## Applications & Hardware - -Not all applications are available on different hardware targets. - -* For applications built into the firmware, you have to specify a compatible application set using `FIRMWARE_APP_SET=...` fbt option. See [fbt docs](./fbt.md#firmware-application-set) for details on build configurations. - -* For applications built as external .faps, you have to explicitly specify compatible targets in application's manifest, `application.fam`. For example, to limit application to a single target, add `targets=["f7"],` to the manifest. It won't be built for other targets. - -For details on application manifests, check out [their docs page](./AppManifests.md). - - -## Building Firmware for a Specific Target - -You have to specify TARGET_HW (and, optionally, FIRMWARE_APP_SET) for `fbt` to build firmware for non-default target. For example, building and flashing debug firmware for f18 can be done with - - ./fbt TARGET_HW=18 flash_usb_full - diff --git a/documentation/UniversalRemotes.md b/documentation/UniversalRemotes.md deleted file mode 100644 index 325f640d7..000000000 --- a/documentation/UniversalRemotes.md +++ /dev/null @@ -1,76 +0,0 @@ -# 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`, 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. 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`, 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 player universal remote file](/assets/resources/infrared/assets/audio.ir). - -## Projectors - -Adding your projector to the universal remote is really simple. Up to 4 signals can be recorded: `Power`, `Mute`, `Vol_up`, `Vol_dn`. Any of them can be omitted if not supported by your projector. -To save time, please make sure every recording has been named accordingly. -In case of omitting, on most projectors with the 4 following buttons, you should not have a problem. - - -## 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 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`, 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), 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 steps 2-6 for each signal from the table below. - -| 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 | | - -Finally, record the `Off` signal: - -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. 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 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/fbt b/fbt index e576f37ac..f80e802b6 100755 --- a/fbt +++ b/fbt @@ -6,16 +6,21 @@ set -eu; # private variables SCRIPT_PATH="$(cd "$(dirname "$0")" && pwd -P)"; -SCONS_DEFAULT_FLAGS="-Q --warn=target-not-built"; -SCONS_EP="python3 -m SCons" +SCONS_DEFAULT_FLAGS="--warn=target-not-built"; +SCONS_EP="python3 -m SCons"; # public variables FBT_NOENV="${FBT_NOENV:-""}"; FBT_NO_SYNC="${FBT_NO_SYNC:-""}"; FBT_TOOLCHAIN_PATH="${FBT_TOOLCHAIN_PATH:-$SCRIPT_PATH}"; +FBT_VERBOSE="${FBT_VERBOSE:-""}"; if [ -z "$FBT_NOENV" ]; then - . "$SCRIPT_PATH/scripts/toolchain/fbtenv.sh"; + FBT_VERBOSE="$FBT_VERBOSE" . "$SCRIPT_PATH/scripts/toolchain/fbtenv.sh"; +fi + +if [ -z "$FBT_VERBOSE" ]; then + SCONS_DEFAULT_FLAGS="$SCONS_DEFAULT_FLAGS -Q"; fi if [ -z "$FBT_NO_SYNC" ]; then diff --git a/fbt.cmd b/fbt.cmd index 197f2359c..92c734860 100644 --- a/fbt.cmd +++ b/fbt.cmd @@ -12,5 +12,10 @@ if [%FBT_NO_SYNC%] == [] ( ) ) -set "SCONS_DEFAULT_FLAGS=-Q --warn=target-not-built" +set "SCONS_DEFAULT_FLAGS=--warn=target-not-built" + +if not defined FBT_VERBOSE ( + set "SCONS_DEFAULT_FLAGS=%SCONS_DEFAULT_FLAGS% -Q" +) + %SCONS_EP% %SCONS_DEFAULT_FLAGS% %* diff --git a/fbt_options.py b/fbt_options.py index d3670b01a..6ffb6c4c2 100644 --- a/fbt_options.py +++ b/fbt_options.py @@ -32,7 +32,7 @@ COPRO_STACK_TYPE = "ble_light" # Leave 0 to let scripts automatically calculate it COPRO_STACK_ADDR = "0x0" -# If you override COPRO_CUBE_DIR on commandline, override this as well +# If you override COPRO_CUBE_DIR on command-line, override this as well COPRO_STACK_BIN_DIR = posixpath.join( COPRO_CUBE_DIR, "Projects", @@ -86,7 +86,7 @@ FIRMWARE_APPS = { # Svc "basic_services", # Apps - "main_apps_default", + "main_apps", "system_apps", # Settings "settings_apps", diff --git a/firmware.scons b/firmware.scons index 92f2d1a91..a094765af 100644 --- a/firmware.scons +++ b/firmware.scons @@ -2,6 +2,7 @@ Import("ENV", "fw_build_meta") from SCons.Errors import UserError from SCons.Node import FS + import itertools from fbt_extra.util import ( @@ -171,7 +172,7 @@ sources = [apps_c] # Gather sources only from app folders in current configuration sources.extend( itertools.chain.from_iterable( - fwenv.GlobRecursive(source_type, appdir.relpath, exclude="lib") + fwenv.GlobRecursive(source_type, appdir.relpath, exclude=["lib"]) for appdir, source_type in fwenv["APPBUILD"].get_builtin_app_folders() ) ) diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv index e4a7dfdd5..8060d38a2 100644 --- a/firmware/targets/f18/api_symbols.csv +++ b/firmware/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,12.1,, +Version,+,18.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -156,6 +156,7 @@ Header,+,lib/toolbox/manchester_decoder.h,, Header,+,lib/toolbox/manchester_encoder.h,, Header,+,lib/toolbox/md5.h,, Header,+,lib/toolbox/path.h,, +Header,+,lib/toolbox/pretty_format.h,, Header,+,lib/toolbox/protocols/protocol_dict.h,, Header,+,lib/toolbox/random_name.h,, Header,+,lib/toolbox/saved_struct.h,, @@ -439,7 +440,6 @@ Function,-,_wctomb_r,int,"_reent*, char*, wchar_t, _mbstate_t*" Function,-,a64l,long,const char* Function,+,abort,void, Function,-,abs,int,int -Function,+,acquire_mutex,void*,"ValueMutex*, uint32_t" Function,-,aligned_alloc,void*,"size_t, size_t" Function,+,aligned_free,void,void* Function,+,aligned_malloc,void*,"size_t, size_t" @@ -522,7 +522,7 @@ 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_current_font_height,uint8_t,const 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" Function,+,canvas_draw_circle,void,"Canvas*, uint8_t, uint8_t, uint8_t" @@ -539,17 +539,18 @@ Function,+,canvas_draw_str,void,"Canvas*, uint8_t, uint8_t, const char*" Function,+,canvas_draw_str_aligned,void,"Canvas*, uint8_t, uint8_t, Align, Align, const char*" Function,+,canvas_draw_triangle,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, CanvasDirection" Function,+,canvas_draw_xbm,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, const uint8_t*" -Function,+,canvas_get_font_params,CanvasFontParameters*,"Canvas*, Font" +Function,+,canvas_get_font_params,const CanvasFontParameters*,"const Canvas*, Font" Function,+,canvas_glyph_width,uint8_t,"Canvas*, char" -Function,+,canvas_height,uint8_t,Canvas* +Function,+,canvas_height,uint8_t,const 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_custom_u8g2_font,void,"Canvas*, const uint8_t*" Function,+,canvas_set_font,void,"Canvas*, Font" Function,+,canvas_set_font_direction,void,"Canvas*, CanvasDirection" Function,+,canvas_string_width,uint16_t,"Canvas*, const char*" -Function,+,canvas_width,uint8_t,Canvas* +Function,+,canvas_width,uint8_t,const Canvas* Function,-,cfree,void,void* Function,-,clearerr,void,FILE* Function,-,clearerr_unlocked,void,FILE* @@ -572,7 +573,6 @@ Function,-,ctermid,char*,char* Function,-,ctime,char*,const time_t* Function,-,ctime_r,char*,"const time_t*, char*" Function,-,cuserid,char*,char* -Function,+,delete_mutex,_Bool,ValueMutex* Function,+,dialog_ex_alloc,DialogEx*, Function,+,dialog_ex_disable_extended_events,void,DialogEx* Function,+,dialog_ex_enable_extended_events,void,DialogEx* @@ -683,6 +683,7 @@ Function,+,file_browser_worker_set_folder_callback,void,"BrowserWorker*, Browser Function,+,file_browser_worker_set_item_callback,void,"BrowserWorker*, BrowserWorkerListItemCallback" Function,+,file_browser_worker_set_list_callback,void,"BrowserWorker*, BrowserWorkerListLoadCallback" Function,+,file_browser_worker_set_long_load_callback,void,"BrowserWorker*, BrowserWorkerLongLoadCallback" +Function,+,file_info_is_dir,_Bool,const FileInfo* Function,+,file_stream_alloc,Stream*,Storage* Function,+,file_stream_close,_Bool,Stream* Function,+,file_stream_get_error,FS_Error,Stream* @@ -706,6 +707,7 @@ Function,-,flipper_application_preload_status_to_string,const char*,FlipperAppli Function,+,flipper_application_spawn,FuriThread*,"FlipperApplication*, void*" Function,+,flipper_format_buffered_file_alloc,FlipperFormat*,Storage* Function,+,flipper_format_buffered_file_close,_Bool,FlipperFormat* +Function,+,flipper_format_buffered_file_open_always,_Bool,"FlipperFormat*, const char*" Function,+,flipper_format_buffered_file_open_existing,_Bool,"FlipperFormat*, const char*" Function,+,flipper_format_delete_key,_Bool,"FlipperFormat*, const char*" Function,+,flipper_format_file_alloc,FlipperFormat*,Storage* @@ -978,7 +980,7 @@ Function,+,furi_hal_power_enable_external_3_3v,void, Function,+,furi_hal_power_enable_otg,void, Function,+,furi_hal_power_gauge_is_ok,_Bool, Function,+,furi_hal_power_get_bat_health_pct,uint8_t, -Function,+,furi_hal_power_get_battery_charging_voltage,float, +Function,+,furi_hal_power_get_battery_charge_voltage_limit,float, Function,+,furi_hal_power_get_battery_current,float,FuriHalPowerIC Function,+,furi_hal_power_get_battery_design_capacity,uint32_t, Function,+,furi_hal_power_get_battery_full_capacity,uint32_t, @@ -997,7 +999,7 @@ Function,+,furi_hal_power_is_charging_done,_Bool, Function,+,furi_hal_power_is_otg_enabled,_Bool, Function,+,furi_hal_power_off,void, Function,+,furi_hal_power_reset,void, -Function,+,furi_hal_power_set_battery_charging_voltage,void,float +Function,+,furi_hal_power_set_battery_charge_voltage_limit,void,float Function,+,furi_hal_power_shutdown,void, Function,+,furi_hal_power_sleep,void, Function,+,furi_hal_power_sleep_available,_Bool, @@ -1016,6 +1018,7 @@ Function,+,furi_hal_region_is_frequency_allowed,_Bool,uint32_t Function,+,furi_hal_region_is_provisioned,_Bool, Function,+,furi_hal_region_set,void,FuriHalRegion* Function,-,furi_hal_resources_deinit_early,void, +Function,+,furi_hal_resources_get_ext_pin_number,int32_t,const GpioPin* Function,-,furi_hal_resources_init,void, Function,-,furi_hal_resources_init_early,void, Function,+,furi_hal_rtc_datetime_to_timestamp,uint32_t,FuriHalRtcDateTime* @@ -1061,11 +1064,13 @@ Function,+,furi_hal_spi_bus_handle_deinit,void,FuriHalSpiBusHandle* Function,+,furi_hal_spi_bus_handle_init,void,FuriHalSpiBusHandle* Function,+,furi_hal_spi_bus_init,void,FuriHalSpiBus* Function,+,furi_hal_spi_bus_rx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, size_t, uint32_t" -Function,+,furi_hal_spi_bus_trx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, uint8_t*, size_t, uint32_t" -Function,+,furi_hal_spi_bus_tx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, size_t, uint32_t" +Function,+,furi_hal_spi_bus_trx,_Bool,"FuriHalSpiBusHandle*, const uint8_t*, uint8_t*, size_t, uint32_t" +Function,+,furi_hal_spi_bus_trx_dma,_Bool,"FuriHalSpiBusHandle*, uint8_t*, uint8_t*, size_t, uint32_t" +Function,+,furi_hal_spi_bus_tx,_Bool,"FuriHalSpiBusHandle*, const uint8_t*, size_t, uint32_t" Function,-,furi_hal_spi_config_deinit_early,void, Function,-,furi_hal_spi_config_init,void, Function,-,furi_hal_spi_config_init_early,void, +Function,-,furi_hal_spi_dma_init,void, Function,+,furi_hal_spi_release,void,FuriHalSpiBusHandle* Function,+,furi_hal_switch,void,void* Function,+,furi_hal_uart_deinit,void,FuriHalUartId @@ -1119,6 +1124,7 @@ Function,+,furi_kernel_unlock,int32_t, Function,+,furi_log_get_level,FuriLogLevel, Function,-,furi_log_init,void, Function,+,furi_log_print_format,void,"FuriLogLevel, const char*, const char*, ..." +Function,+,furi_log_print_raw_format,void,"FuriLogLevel, const char*, ..." Function,+,furi_log_set_level,void,FuriLogLevel Function,-,furi_log_set_puts,void,FuriLogPuts Function,-,furi_log_set_timestamp,void,FuriLogTimestamp @@ -1229,8 +1235,10 @@ Function,+,furi_thread_flags_get,uint32_t, Function,+,furi_thread_flags_set,uint32_t,"FuriThreadId, uint32_t" Function,+,furi_thread_flags_wait,uint32_t,"uint32_t, uint32_t, uint32_t" Function,+,furi_thread_free,void,FuriThread* +Function,+,furi_thread_get_appid,const char*,FuriThreadId Function,+,furi_thread_get_current,FuriThread*, Function,+,furi_thread_get_current_id,FuriThreadId, +Function,+,furi_thread_get_current_priority,FuriThreadPriority, Function,+,furi_thread_get_heap_size,size_t,FuriThread* Function,+,furi_thread_get_id,FuriThreadId,FuriThread* Function,+,furi_thread_get_name,const char*,FuriThreadId @@ -1242,8 +1250,10 @@ Function,+,furi_thread_is_suspended,_Bool,FuriThreadId Function,+,furi_thread_join,_Bool,FuriThread* Function,+,furi_thread_mark_as_service,void,FuriThread* Function,+,furi_thread_resume,void,FuriThreadId +Function,+,furi_thread_set_appid,void,"FuriThread*, const char*" Function,+,furi_thread_set_callback,void,"FuriThread*, FuriThreadCallback" Function,+,furi_thread_set_context,void,"FuriThread*, void*" +Function,+,furi_thread_set_current_priority,void,FuriThreadPriority Function,+,furi_thread_set_name,void,"FuriThread*, const char*" Function,+,furi_thread_set_priority,void,"FuriThread*, FuriThreadPriority" Function,+,furi_thread_set_stack_size,void,"FuriThread*, size_t" @@ -1281,7 +1291,7 @@ Function,+,gui_add_framebuffer_callback,void,"Gui*, GuiCanvasCommitCallback, voi 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_get_framebuffer_size,size_t,const Gui* Function,+,gui_remove_framebuffer_callback,void,"Gui*, GuiCanvasCommitCallback, void*" Function,+,gui_remove_view_port,void,"Gui*, ViewPort*" Function,+,gui_set_lockdown,void,"Gui*, _Bool" @@ -1295,9 +1305,9 @@ Function,+,hmac_sha256_init,void,"hmac_sha256_context*, const uint8_t*" Function,+,hmac_sha256_update,void,"const hmac_sha256_context*, const uint8_t*, unsigned" Function,+,icon_animation_alloc,IconAnimation*,const Icon* Function,+,icon_animation_free,void,IconAnimation* -Function,+,icon_animation_get_height,uint8_t,IconAnimation* -Function,+,icon_animation_get_width,uint8_t,IconAnimation* -Function,+,icon_animation_is_last_frame,_Bool,IconAnimation* +Function,+,icon_animation_get_height,uint8_t,const IconAnimation* +Function,+,icon_animation_get_width,uint8_t,const IconAnimation* +Function,+,icon_animation_is_last_frame,_Bool,const IconAnimation* Function,+,icon_animation_set_update_callback,void,"IconAnimation*, IconAnimationCallback, void*" Function,+,icon_animation_start,void,IconAnimation* Function,+,icon_animation_stop,void,IconAnimation* @@ -1305,7 +1315,6 @@ Function,+,icon_get_data,const uint8_t*,const Icon* Function,+,icon_get_height,uint8_t,const Icon* Function,+,icon_get_width,uint8_t,const Icon* Function,-,index,char*,"const char*, int" -Function,+,init_mutex,_Bool,"ValueMutex*, void*, size_t" Function,-,initstate,char*,"unsigned, char*, size_t" Function,+,input_get_key_name,const char*,InputKey Function,+,input_get_type_name,const char*,InputType @@ -1346,7 +1355,7 @@ Function,-,ldiv,ldiv_t,"long, long" Function,-,llabs,long long,long long Function,-,lldiv,lldiv_t,"long long, long long" Function,+,loader_get_pubsub,FuriPubSub*,Loader* -Function,+,loader_is_locked,_Bool,Loader* +Function,+,loader_is_locked,_Bool,const Loader* Function,+,loader_lock,_Bool,Loader* Function,+,loader_show_menu,void, Function,+,loader_start,LoaderStatus,"Loader*, const char*, const char*" @@ -1484,6 +1493,7 @@ Function,+,power_get_pubsub,FuriPubSub*,Power* Function,+,power_is_battery_healthy,_Bool,Power* Function,+,power_off,void,Power* Function,+,power_reboot,void,PowerBootMode +Function,+,pretty_format_bytes_hex_canonical,void,"FuriString*, size_t, const char*, const uint8_t*, size_t" Function,-,printf,int,"const char*, ..." Function,+,property_value_out,void,"PropertyValueContext*, const char*, unsigned int, ..." Function,+,protocol_dict_alloc,ProtocolDict*,"const ProtocolBase**, size_t" @@ -1527,12 +1537,10 @@ Function,+,rand,int, Function,-,rand_r,int,unsigned* Function,+,random,long, Function,-,rawmemchr,void*,"const void*, int" -Function,-,read_mutex,_Bool,"ValueMutex*, void*, size_t, uint32_t" Function,+,realloc,void*,"void*, size_t" Function,-,reallocarray,void*,"void*, size_t, size_t" Function,-,reallocf,void*,"void*, size_t" Function,-,realpath,char*,"const char*, char*" -Function,+,release_mutex,_Bool,"ValueMutex*, const void*" Function,-,remove,int,const char* Function,-,rename,int,"const char*, const char*" Function,-,renameat,int,"int, const char*, int, const char*" @@ -1564,11 +1572,11 @@ Function,+,saved_struct_save,_Bool,"const char*, void*, size_t, uint8_t, uint8_t Function,-,scanf,int,"const char*, ..." Function,+,scene_manager_alloc,SceneManager*,"const SceneManagerHandlers*, void*" Function,+,scene_manager_free,void,SceneManager* -Function,+,scene_manager_get_scene_state,uint32_t,"SceneManager*, uint32_t" +Function,+,scene_manager_get_scene_state,uint32_t,"const SceneManager*, uint32_t" Function,+,scene_manager_handle_back_event,_Bool,SceneManager* Function,+,scene_manager_handle_custom_event,_Bool,"SceneManager*, uint32_t" Function,+,scene_manager_handle_tick_event,void,SceneManager* -Function,+,scene_manager_has_previous_scene,_Bool,"SceneManager*, uint32_t" +Function,+,scene_manager_has_previous_scene,_Bool,"const SceneManager*, uint32_t" Function,+,scene_manager_next_scene,void,"SceneManager*, uint32_t" Function,+,scene_manager_previous_scene,_Bool,SceneManager* Function,+,scene_manager_search_and_switch_to_another_scene,_Bool,"SceneManager*, uint32_t" @@ -1610,14 +1618,18 @@ Function,-,srand48,void,long Function,-,srandom,void,unsigned Function,+,sscanf,int,"const char*, const char*, ..." Function,+,storage_common_copy,FS_Error,"Storage*, const char*, const char*" +Function,+,storage_common_exists,_Bool,"Storage*, const char*" Function,+,storage_common_fs_info,FS_Error,"Storage*, const char*, uint64_t*, uint64_t*" Function,+,storage_common_merge,FS_Error,"Storage*, const char*, const char*" +Function,+,storage_common_migrate,FS_Error,"Storage*, const char*, const char*" Function,+,storage_common_mkdir,FS_Error,"Storage*, const char*" Function,+,storage_common_remove,FS_Error,"Storage*, const char*" Function,+,storage_common_rename,FS_Error,"Storage*, const char*, const char*" +Function,+,storage_common_resolve_path_and_ensure_app_directory,void,"Storage*, FuriString*" Function,+,storage_common_stat,FS_Error,"Storage*, const char*, FileInfo*" Function,+,storage_common_timestamp,FS_Error,"Storage*, const char*, uint32_t*" Function,+,storage_dir_close,_Bool,File* +Function,+,storage_dir_exists,_Bool,"Storage*, const char*" Function,+,storage_dir_open,_Bool,"File*, const char*" Function,+,storage_dir_read,_Bool,"File*, FileInfo*, char*, uint16_t" Function,-,storage_dir_rewind,_Bool,File* @@ -1940,11 +1952,11 @@ Function,+,view_port_alloc,ViewPort*, Function,+,view_port_draw_callback_set,void,"ViewPort*, ViewPortDrawCallback, void*" Function,+,view_port_enabled_set,void,"ViewPort*, _Bool" Function,+,view_port_free,void,ViewPort* -Function,+,view_port_get_height,uint8_t,ViewPort* +Function,+,view_port_get_height,uint8_t,const ViewPort* Function,+,view_port_get_orientation,ViewPortOrientation,const ViewPort* -Function,+,view_port_get_width,uint8_t,ViewPort* +Function,+,view_port_get_width,uint8_t,const ViewPort* Function,+,view_port_input_callback_set,void,"ViewPort*, ViewPortInputCallback, void*" -Function,+,view_port_is_enabled,_Bool,ViewPort* +Function,+,view_port_is_enabled,_Bool,const ViewPort* Function,+,view_port_set_height,void,"ViewPort*, uint8_t" Function,+,view_port_set_orientation,void,"ViewPort*, ViewPortOrientation" Function,+,view_port_set_width,void,"ViewPort*, uint8_t" @@ -1988,7 +2000,6 @@ Function,+,widget_alloc,Widget*, Function,+,widget_free,void,Widget* Function,+,widget_get_view,View*,Widget* Function,+,widget_reset,void,Widget* -Function,-,write_mutex,_Bool,"ValueMutex*, void*, size_t, uint32_t" Function,-,xPortGetFreeHeapSize,size_t, Function,-,xPortGetMinimumEverFreeHeapSize,size_t, Function,-,xPortStartScheduler,BaseType_t, diff --git a/firmware/targets/f18/furi_hal/furi_hal.c b/firmware/targets/f18/furi_hal/furi_hal.c index ad35a0074..0a68fdb69 100644 --- a/firmware/targets/f18/furi_hal/furi_hal.c +++ b/firmware/targets/f18/furi_hal/furi_hal.c @@ -51,6 +51,7 @@ void furi_hal_init() { furi_hal_version_init(); furi_hal_spi_config_init(); + furi_hal_spi_dma_init(); furi_hal_speaker_init(); FURI_LOG_I(TAG, "Speaker OK"); diff --git a/firmware/targets/f18/furi_hal/furi_hal_resources.c b/firmware/targets/f18/furi_hal/furi_hal_resources.c index dafeefdd0..41cc80bfb 100644 --- a/firmware/targets/f18/furi_hal/furi_hal_resources.c +++ b/firmware/targets/f18/furi_hal/furi_hal_resources.c @@ -199,3 +199,27 @@ void furi_hal_resources_init() { NVIC_SetPriority(EXTI15_10_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); NVIC_EnableIRQ(EXTI15_10_IRQn); } + +int32_t furi_hal_resources_get_ext_pin_number(const GpioPin* gpio) { + // TODO: describe second ROW + if(gpio == &gpio_ext_pa7) + return 2; + else if(gpio == &gpio_ext_pa6) + return 3; + else if(gpio == &gpio_ext_pa4) + return 4; + else if(gpio == &gpio_ext_pb3) + return 5; + else if(gpio == &gpio_ext_pb2) + return 6; + else if(gpio == &gpio_ext_pc3) + return 7; + else if(gpio == &gpio_ext_pc1) + return 15; + else if(gpio == &gpio_ext_pc0) + return 16; + else if(gpio == &ibutton_gpio) + return 17; + else + return -1; +} diff --git a/firmware/targets/f18/furi_hal/furi_hal_resources.h b/firmware/targets/f18/furi_hal/furi_hal_resources.h index ef2cdae7f..a24afbf94 100644 --- a/firmware/targets/f18/furi_hal/furi_hal_resources.h +++ b/firmware/targets/f18/furi_hal/furi_hal_resources.h @@ -111,6 +111,13 @@ void furi_hal_resources_deinit_early(); void furi_hal_resources_init(); +/** + * Get a corresponding external connector pin number for a gpio + * @param gpio GpioPin + * @return pin number or -1 if gpio is not on the external connector + */ +int32_t furi_hal_resources_get_ext_pin_number(const GpioPin* gpio); + #ifdef __cplusplus } #endif diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index adca3b714..3de7bd978 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,10 +1,11 @@ entry,status,name,type,params -Version,+,14.0,, +Version,+,18.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, Header,+,applications/services/dialogs/dialogs.h,, Header,+,applications/services/dolphin/dolphin.h,, +Header,+,applications/services/dolphin/helpers/dolphin_state.h,, Header,+,applications/services/gui/canvas_i.h,, Header,+,applications/services/gui/elements.h,, Header,+,applications/services/gui/gui.h,, @@ -36,6 +37,8 @@ Header,+,applications/services/notification/notification_messages.h,, Header,+,applications/services/power/power_service/power.h,, Header,+,applications/services/rpc/rpc_app.h,, Header,+,applications/services/storage/storage.h,, +Header,+,applications/services/xtreme/assets.h,, +Header,+,applications/services/xtreme/settings.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_clock.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_console.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_flash.h,, @@ -165,9 +168,10 @@ Header,+,lib/mlib/m-rbtree.h,, Header,+,lib/mlib/m-tuple.h,, Header,+,lib/mlib/m-variant.h,, Header,+,lib/nfc/nfc_device.h,, +Header,+,lib/one_wire/ibutton/ibutton_key.h,, +Header,+,lib/one_wire/ibutton/ibutton_protocols.h,, Header,+,lib/one_wire/ibutton/ibutton_worker.h,, Header,+,lib/one_wire/maxim_crc.h,, -Header,+,lib/one_wire/one_wire_device.h,, Header,+,lib/one_wire/one_wire_host.h,, Header,+,lib/one_wire/one_wire_host_timing.h,, Header,+,lib/one_wire/one_wire_slave.h,, @@ -195,6 +199,7 @@ Header,+,lib/toolbox/manchester_decoder.h,, Header,+,lib/toolbox/manchester_encoder.h,, Header,+,lib/toolbox/md5.h,, Header,+,lib/toolbox/path.h,, +Header,+,lib/toolbox/pretty_format.h,, Header,+,lib/toolbox/protocols/protocol_dict.h,, Header,+,lib/toolbox/random_name.h,, Header,+,lib/toolbox/saved_struct.h,, @@ -205,6 +210,7 @@ Header,+,lib/toolbox/stream/string_stream.h,, Header,+,lib/toolbox/tar/tar_archive.h,, Header,+,lib/toolbox/value_index.h,, Header,+,lib/toolbox/version.h,, +Header,+,lib/u8g2/u8g2.h,, Function,-,LL_ADC_CommonDeInit,ErrorStatus,ADC_Common_TypeDef* Function,-,LL_ADC_CommonInit,ErrorStatus,"ADC_Common_TypeDef*, LL_ADC_CommonInitTypeDef*" Function,-,LL_ADC_CommonStructInit,void,LL_ADC_CommonInitTypeDef* @@ -304,6 +310,11 @@ Function,-,LL_USART_StructInit,void,LL_USART_InitTypeDef* Function,-,LL_mDelay,void,uint32_t Function,-,SystemCoreClockUpdate,void, Function,-,SystemInit,void, +Function,+,XTREME_ASSETS,XtremeAssets*, +Function,-,XTREME_ASSETS_LOAD,void, +Function,+,XTREME_SETTINGS,XtremeSettings*, +Function,-,XTREME_SETTINGS_LOAD,void, +Function,+,XTREME_SETTINGS_SAVE,_Bool, Function,-,_Exit,void,int Function,-,__assert,void,"const char*, int, const char*" Function,+,__assert_func,void,"const char*, int, const char*, const char*" @@ -493,10 +504,13 @@ Function,-,acosh,double,double Function,-,acoshf,float,float Function,-,acoshl,long double,long double Function,-,acosl,long double,long double -Function,+,acquire_mutex,void*,"ValueMutex*, uint32_t" Function,-,aligned_alloc,void*,"size_t, size_t" Function,+,aligned_free,void,void* Function,+,aligned_malloc,void*,"size_t, size_t" +Function,-,alutech_get_custom_btn,uint8_t, +Function,-,alutech_get_original_btn,uint8_t, +Function,-,alutech_reset_original_btn,void, +Function,-,alutech_set_btn,void,uint8_t Function,-,arc4random,__uint32_t, Function,-,arc4random_buf,void,"void*, size_t" Function,-,arc4random_uniform,__uint32_t,__uint32_t @@ -588,7 +602,7 @@ Function,+,bt_get_profile_mac_address,const uint8_t*,Bt* Function,+,bt_get_profile_pairing_method,GapPairing,Bt* Function,+,bt_keys_storage_set_default_path,void,Bt* Function,+,bt_keys_storage_set_storage_path,void,"Bt*, const char*" -Function,+,bt_remote_rssi,_Bool,"Bt*, BtRssi*" +Function,+,bt_remote_rssi,_Bool,"Bt*, uint8_t*" Function,+,bt_set_profile,_Bool,"Bt*, BtProfile" Function,+,bt_set_profile_adv_name,void,"Bt*, const char*, ..." Function,+,bt_set_profile_mac_address,void,"Bt*, const uint8_t[6]" @@ -623,7 +637,7 @@ 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_current_font_height,uint8_t,const 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" Function,+,canvas_draw_circle,void,"Canvas*, uint8_t, uint8_t, uint8_t" @@ -644,11 +658,11 @@ Function,+,canvas_draw_xbm,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, co Function,-,canvas_frame_set,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t" Function,-,canvas_free,void,Canvas* Function,+,canvas_get_buffer,uint8_t*,Canvas* -Function,+,canvas_get_buffer_size,size_t,Canvas* -Function,+,canvas_get_font_params,CanvasFontParameters*,"Canvas*, Font" +Function,+,canvas_get_buffer_size,size_t,const Canvas* +Function,+,canvas_get_font_params,const CanvasFontParameters*,"const Canvas*, Font" Function,-,canvas_get_orientation,CanvasOrientation,const Canvas* Function,+,canvas_glyph_width,uint8_t,"Canvas*, char" -Function,+,canvas_height,uint8_t,Canvas* +Function,+,canvas_height,uint8_t,const Canvas* Function,-,canvas_init,Canvas*, Function,+,canvas_invert_color,void,Canvas* Function,+,canvas_reset,void,Canvas* @@ -659,7 +673,7 @@ Function,+,canvas_set_font,void,"Canvas*, Font" Function,+,canvas_set_font_direction,void,"Canvas*, CanvasDirection" Function,-,canvas_set_orientation,void,"Canvas*, CanvasOrientation" Function,+,canvas_string_width,uint16_t,"Canvas*, const char*" -Function,+,canvas_width,uint8_t,Canvas* +Function,+,canvas_width,uint8_t,const Canvas* Function,-,cbrt,double,double Function,-,cbrtf,float,float Function,-,cbrtl,long double,long double @@ -725,7 +739,6 @@ Function,-,ctermid,char*,char* Function,-,ctime,char*,const time_t* Function,-,ctime_r,char*,"const time_t*, char*" Function,-,cuserid,char*,char* -Function,+,delete_mutex,_Bool,ValueMutex* Function,+,dialog_ex_alloc,DialogEx*, Function,+,dialog_ex_disable_extended_events,void,DialogEx* Function,+,dialog_ex_enable_extended_events,void,DialogEx* @@ -783,7 +796,21 @@ Function,+,dolphin_deed_get_app,DolphinApp,DolphinDeed Function,+,dolphin_deed_get_app_limit,uint8_t,DolphinApp Function,+,dolphin_deed_get_weight,uint8_t,DolphinDeed Function,+,dolphin_flush,void,Dolphin* +Function,+,dolphin_get_level,uint8_t,int +Function,+,dolphin_get_levels,const int*, Function,+,dolphin_get_pubsub,FuriPubSub*,Dolphin* +Function,+,dolphin_state_alloc,DolphinState*, +Function,+,dolphin_state_butthurted,void,DolphinState* +Function,+,dolphin_state_clear_limits,void,DolphinState* +Function,+,dolphin_state_free,void,DolphinState* +Function,+,dolphin_state_increase_level,void,DolphinState* +Function,+,dolphin_state_is_levelup,_Bool,int +Function,+,dolphin_state_load,_Bool,DolphinState* +Function,+,dolphin_state_on_deed,void,"DolphinState*, DolphinDeed" +Function,+,dolphin_state_save,_Bool,DolphinState* +Function,+,dolphin_state_timestamp,uint64_t, +Function,+,dolphin_state_xp_above_last_levelup,uint32_t,int +Function,+,dolphin_state_xp_to_levelup,uint32_t,int Function,+,dolphin_stats,DolphinStats,Dolphin* Function,+,dolphin_upgrade_level,void,Dolphin* Function,-,dprintf,int,"int, const char*, ..." @@ -880,6 +907,7 @@ Function,+,file_browser_worker_set_folder_callback,void,"BrowserWorker*, Browser Function,+,file_browser_worker_set_item_callback,void,"BrowserWorker*, BrowserWorkerListItemCallback" Function,+,file_browser_worker_set_list_callback,void,"BrowserWorker*, BrowserWorkerListLoadCallback" Function,+,file_browser_worker_set_long_load_callback,void,"BrowserWorker*, BrowserWorkerLongLoadCallback" +Function,+,file_info_is_dir,_Bool,const FileInfo* Function,+,file_stream_alloc,Stream*,Storage* Function,+,file_stream_close,_Bool,Stream* Function,+,file_stream_get_error,FS_Error,Stream* @@ -906,6 +934,7 @@ Function,-,flipper_application_preload_status_to_string,const char*,FlipperAppli Function,+,flipper_application_spawn,FuriThread*,"FlipperApplication*, void*" Function,+,flipper_format_buffered_file_alloc,FlipperFormat*,Storage* Function,+,flipper_format_buffered_file_close,_Bool,FlipperFormat* +Function,+,flipper_format_buffered_file_open_always,_Bool,"FlipperFormat*, const char*" Function,+,flipper_format_buffered_file_open_existing,_Bool,"FlipperFormat*, const char*" Function,+,flipper_format_delete_key,_Bool,"FlipperFormat*, const char*" Function,+,flipper_format_file_alloc,FlipperFormat*,Storage* @@ -1172,20 +1201,13 @@ Function,+,furi_hal_i2c_tx,_Bool,"FuriHalI2cBusHandle*, const uint8_t, const uin Function,+,furi_hal_i2c_write_mem,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t, uint8_t*, uint8_t, uint32_t" Function,+,furi_hal_i2c_write_reg_16,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t, uint16_t, uint32_t" Function,+,furi_hal_i2c_write_reg_8,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t, uint8_t, uint32_t" -Function,+,furi_hal_ibutton_add_interrupt,void,"GpioExtiCallback, void*" Function,+,furi_hal_ibutton_emulate_set_next,void,uint32_t Function,+,furi_hal_ibutton_emulate_start,void,"uint32_t, FuriHalIbuttonEmulateCallback, void*" Function,+,furi_hal_ibutton_emulate_stop,void, Function,-,furi_hal_ibutton_init,void, -Function,+,furi_hal_ibutton_pin_get_level,_Bool, -Function,+,furi_hal_ibutton_pin_high,void, -Function,+,furi_hal_ibutton_pin_low,void, -Function,+,furi_hal_ibutton_remove_interrupt,void, -Function,+,furi_hal_ibutton_start_drive,void, -Function,+,furi_hal_ibutton_start_drive_in_isr,void, -Function,+,furi_hal_ibutton_start_interrupt,void, -Function,+,furi_hal_ibutton_start_interrupt_in_isr,void, -Function,+,furi_hal_ibutton_stop,void, +Function,+,furi_hal_ibutton_pin_configure,void, +Function,+,furi_hal_ibutton_pin_reset,void, +Function,+,furi_hal_ibutton_pin_write,void,const _Bool Function,+,furi_hal_info_get,void,"PropertyValueCallback, char, void*" Function,+,furi_hal_infrared_async_rx_set_capture_isr_callback,void,"FuriHalInfraredRxCaptureCallback, void*" Function,+,furi_hal_infrared_async_rx_set_timeout,void,uint32_t @@ -1197,7 +1219,9 @@ Function,+,furi_hal_infrared_async_tx_set_signal_sent_isr_callback,void,"FuriHal Function,+,furi_hal_infrared_async_tx_start,void,"uint32_t, float" Function,+,furi_hal_infrared_async_tx_stop,void, Function,+,furi_hal_infrared_async_tx_wait_termination,void, +Function,+,furi_hal_infrared_get_debug_out_status,_Bool, Function,+,furi_hal_infrared_is_busy,_Bool, +Function,+,furi_hal_infrared_set_debug_out,void,_Bool Function,-,furi_hal_init,void, Function,-,furi_hal_init_early,void, Function,-,furi_hal_interrupt_init,void, @@ -1220,12 +1244,13 @@ Function,+,furi_hal_mpu_protect_disable,void,FuriHalMpuRegion Function,+,furi_hal_mpu_protect_no_access,void,"FuriHalMpuRegion, uint32_t, FuriHalMPURegionSize" Function,+,furi_hal_mpu_protect_read_only,void,"FuriHalMpuRegion, uint32_t, FuriHalMPURegionSize" Function,+,furi_hal_nfc_activate_nfca,_Bool,"uint32_t, uint32_t*" +Function,+,furi_hal_nfc_deinit,void, Function,+,furi_hal_nfc_detect,_Bool,"FuriHalNfcDevData*, uint32_t" Function,+,furi_hal_nfc_emulate_nfca,_Bool,"uint8_t*, uint8_t, uint8_t*, uint8_t, FuriHalNfcEmulateCallback, void*, uint32_t" Function,+,furi_hal_nfc_exit_sleep,void, Function,+,furi_hal_nfc_field_off,void, Function,+,furi_hal_nfc_field_on,void, -Function,-,furi_hal_nfc_init,void, +Function,+,furi_hal_nfc_init,void, Function,+,furi_hal_nfc_is_busy,_Bool, Function,+,furi_hal_nfc_is_init,_Bool, Function,+,furi_hal_nfc_listen,_Bool,"uint8_t*, uint8_t, uint8_t*, uint8_t, _Bool, uint32_t" @@ -1259,7 +1284,7 @@ Function,+,furi_hal_power_enable_external_3_3v,void, Function,+,furi_hal_power_enable_otg,void, Function,+,furi_hal_power_gauge_is_ok,_Bool, Function,+,furi_hal_power_get_bat_health_pct,uint8_t, -Function,+,furi_hal_power_get_battery_charging_voltage,float, +Function,+,furi_hal_power_get_battery_charge_voltage_limit,float, Function,+,furi_hal_power_get_battery_current,float,FuriHalPowerIC Function,+,furi_hal_power_get_battery_design_capacity,uint32_t, Function,+,furi_hal_power_get_battery_full_capacity,uint32_t, @@ -1278,7 +1303,7 @@ Function,+,furi_hal_power_is_charging_done,_Bool, Function,+,furi_hal_power_is_otg_enabled,_Bool, Function,+,furi_hal_power_off,void, Function,+,furi_hal_power_reset,void, -Function,+,furi_hal_power_set_battery_charging_voltage,void,float +Function,+,furi_hal_power_set_battery_charge_voltage_limit,void,float Function,+,furi_hal_power_shutdown,void, Function,+,furi_hal_power_sleep,void, Function,+,furi_hal_power_sleep_available,_Bool, @@ -1374,16 +1399,19 @@ Function,+,furi_hal_spi_bus_tx,_Bool,"FuriHalSpiBusHandle*, const uint8_t*, size Function,-,furi_hal_spi_config_deinit_early,void, Function,-,furi_hal_spi_config_init,void, Function,-,furi_hal_spi_config_init_early,void, -Function,+,furi_hal_spi_dma_init,void, +Function,-,furi_hal_spi_dma_init,void, Function,+,furi_hal_spi_release,void,FuriHalSpiBusHandle* Function,+,furi_hal_subghz_check_radio,_Bool, Function,+,furi_hal_subghz_disable_ext_power,void, Function,-,furi_hal_subghz_dump_state,void, -Function,+,furi_hal_subghz_enable_ext_power,void, +Function,+,furi_hal_subghz_enable_ext_power,_Bool, Function,+,furi_hal_subghz_flush_rx,void, Function,+,furi_hal_subghz_flush_tx,void, +Function,+,furi_hal_subghz_get_extend_settings,void,"_Bool*, _Bool*" +Function,+,furi_hal_subghz_get_external_power_disable,_Bool, Function,+,furi_hal_subghz_get_lqi,uint8_t, Function,+,furi_hal_subghz_get_radio_type,SubGhzRadioType, +Function,+,furi_hal_subghz_get_rolling_counter_mult,uint8_t, Function,+,furi_hal_subghz_get_rssi,float, Function,+,furi_hal_subghz_idle,void, Function,-,furi_hal_subghz_init,void, @@ -1401,10 +1429,13 @@ Function,+,furi_hal_subghz_reset,void, Function,+,furi_hal_subghz_rx,void, Function,+,furi_hal_subghz_rx_pipe_not_empty,_Bool, Function,+,furi_hal_subghz_set_async_mirror_pin,void,const GpioPin* +Function,+,furi_hal_subghz_set_extend_settings,void,"_Bool, _Bool" +Function,+,furi_hal_subghz_set_external_power_disable,void,_Bool Function,+,furi_hal_subghz_set_frequency,uint32_t,uint32_t Function,+,furi_hal_subghz_set_frequency_and_path,uint32_t,uint32_t Function,+,furi_hal_subghz_set_path,void,FuriHalSubGhzPath Function,+,furi_hal_subghz_set_radio_type,_Bool,SubGhzRadioType +Function,+,furi_hal_subghz_set_rolling_counter_mult,void,uint8_t Function,-,furi_hal_subghz_shutdown,void, Function,+,furi_hal_subghz_sleep,void, Function,+,furi_hal_subghz_start_async_rx,void,"FuriHalSubGhzCaptureCallback, void*" @@ -1443,6 +1474,8 @@ Function,+,furi_hal_version_get_hw_connect,uint8_t, Function,+,furi_hal_version_get_hw_display,FuriHalVersionDisplay, Function,+,furi_hal_version_get_hw_region,FuriHalVersionRegion, Function,+,furi_hal_version_get_hw_region_name,const char*, +Function,+,furi_hal_version_get_hw_region_name_otp,const char*, +Function,+,furi_hal_version_get_hw_region_otp,FuriHalVersionRegion, Function,+,furi_hal_version_get_hw_target,uint8_t, Function,+,furi_hal_version_get_hw_timestamp,uint32_t, Function,+,furi_hal_version_get_hw_version,uint8_t, @@ -1466,6 +1499,7 @@ Function,+,furi_kernel_unlock,int32_t, Function,+,furi_log_get_level,FuriLogLevel, Function,-,furi_log_init,void, Function,+,furi_log_print_format,void,"FuriLogLevel, const char*, const char*, ..." +Function,+,furi_log_print_raw_format,void,"FuriLogLevel, const char*, ..." Function,+,furi_log_set_level,void,FuriLogLevel Function,-,furi_log_set_puts,void,FuriLogPuts Function,-,furi_log_set_timestamp,void,FuriLogTimestamp @@ -1576,8 +1610,10 @@ Function,+,furi_thread_flags_get,uint32_t, Function,+,furi_thread_flags_set,uint32_t,"FuriThreadId, uint32_t" Function,+,furi_thread_flags_wait,uint32_t,"uint32_t, uint32_t, uint32_t" Function,+,furi_thread_free,void,FuriThread* +Function,+,furi_thread_get_appid,const char*,FuriThreadId Function,+,furi_thread_get_current,FuriThread*, Function,+,furi_thread_get_current_id,FuriThreadId, +Function,+,furi_thread_get_current_priority,FuriThreadPriority, Function,+,furi_thread_get_heap_size,size_t,FuriThread* Function,+,furi_thread_get_id,FuriThreadId,FuriThread* Function,+,furi_thread_get_name,const char*,FuriThreadId @@ -1589,8 +1625,10 @@ Function,+,furi_thread_is_suspended,_Bool,FuriThreadId Function,+,furi_thread_join,_Bool,FuriThread* Function,+,furi_thread_mark_as_service,void,FuriThread* Function,+,furi_thread_resume,void,FuriThreadId +Function,+,furi_thread_set_appid,void,"FuriThread*, const char*" Function,+,furi_thread_set_callback,void,"FuriThread*, FuriThreadCallback" Function,+,furi_thread_set_context,void,"FuriThread*, void*" +Function,+,furi_thread_set_current_priority,void,FuriThreadPriority Function,+,furi_thread_set_name,void,"FuriThread*, const char*" Function,+,furi_thread_set_priority,void,"FuriThread*, FuriThreadPriority" Function,+,furi_thread_set_stack_size,void,"FuriThread*, size_t" @@ -1619,7 +1657,6 @@ Function,-,gap_init,_Bool,"GapConfig*, GapEventCallback, void*" Function,-,gap_start_advertising,void, Function,-,gap_stop_advertising,void, Function,-,gap_thread_stop,void, -Function,+,getRandomDeed,DolphinDeed, Function,-,getc,int,FILE* Function,-,getc_unlocked,int,FILE* Function,-,getchar,int, @@ -1634,7 +1671,7 @@ Function,+,gui_add_framebuffer_callback,void,"Gui*, GuiCanvasCommitCallback, voi 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_get_framebuffer_size,size_t,const Gui* Function,+,gui_remove_framebuffer_callback,void,"Gui*, GuiCanvasCommitCallback, void*" Function,+,gui_remove_view_port,void,"Gui*, ViewPort*" Function,+,gui_set_lockdown,void,"Gui*, _Bool" @@ -1649,22 +1686,33 @@ Function,+,hmac_sha256_update,void,"const hmac_sha256_context*, const uint8_t*, Function,-,hypot,double,"double, double" Function,-,hypotf,float,"float, float" Function,-,hypotl,long double,"long double, long double" -Function,+,ibutton_key_alloc,iButtonKey*, -Function,+,ibutton_key_clear_data,void,iButtonKey* -Function,+,ibutton_key_dallas_crc_is_valid,_Bool,iButtonKey* -Function,+,ibutton_key_dallas_is_1990_key,_Bool,iButtonKey* +Function,+,ibutton_key_alloc,iButtonKey*,size_t Function,+,ibutton_key_free,void,iButtonKey* -Function,+,ibutton_key_get_data_p,const uint8_t*,iButtonKey* -Function,+,ibutton_key_get_data_size,uint8_t,iButtonKey* -Function,+,ibutton_key_get_max_size,uint8_t, -Function,+,ibutton_key_get_size_by_type,uint8_t,iButtonKeyType -Function,+,ibutton_key_get_string_by_type,const char*,iButtonKeyType -Function,+,ibutton_key_get_type,iButtonKeyType,iButtonKey* -Function,+,ibutton_key_get_type_by_string,_Bool,"const char*, iButtonKeyType*" -Function,+,ibutton_key_set,void,"iButtonKey*, const iButtonKey*" -Function,+,ibutton_key_set_data,void,"iButtonKey*, uint8_t*, uint8_t" -Function,+,ibutton_key_set_type,void,"iButtonKey*, iButtonKeyType" -Function,+,ibutton_worker_alloc,iButtonWorker*, +Function,+,ibutton_key_get_protocol_id,iButtonProtocolId,const iButtonKey* +Function,+,ibutton_key_reset,void,iButtonKey* +Function,+,ibutton_key_set_protocol_id,void,"iButtonKey*, iButtonProtocolId" +Function,+,ibutton_protocols_alloc,iButtonProtocols*, +Function,+,ibutton_protocols_apply_edits,void,"iButtonProtocols*, const iButtonKey*" +Function,+,ibutton_protocols_emulate_start,void,"iButtonProtocols*, iButtonKey*" +Function,+,ibutton_protocols_emulate_stop,void,"iButtonProtocols*, iButtonKey*" +Function,+,ibutton_protocols_free,void,iButtonProtocols* +Function,+,ibutton_protocols_get_editable_data,void,"iButtonProtocols*, const iButtonKey*, iButtonEditableData*" +Function,+,ibutton_protocols_get_features,uint32_t,"iButtonProtocols*, iButtonProtocolId" +Function,+,ibutton_protocols_get_id_by_name,iButtonProtocolId,"iButtonProtocols*, const char*" +Function,+,ibutton_protocols_get_manufacturer,const char*,"iButtonProtocols*, iButtonProtocolId" +Function,+,ibutton_protocols_get_max_data_size,size_t,iButtonProtocols* +Function,+,ibutton_protocols_get_name,const char*,"iButtonProtocols*, iButtonProtocolId" +Function,+,ibutton_protocols_get_protocol_count,uint32_t, +Function,+,ibutton_protocols_is_valid,_Bool,"iButtonProtocols*, const iButtonKey*" +Function,+,ibutton_protocols_load,_Bool,"iButtonProtocols*, iButtonKey*, const char*" +Function,+,ibutton_protocols_read,_Bool,"iButtonProtocols*, iButtonKey*" +Function,+,ibutton_protocols_render_brief_data,void,"iButtonProtocols*, const iButtonKey*, FuriString*" +Function,+,ibutton_protocols_render_data,void,"iButtonProtocols*, const iButtonKey*, FuriString*" +Function,+,ibutton_protocols_render_error,void,"iButtonProtocols*, const iButtonKey*, FuriString*" +Function,+,ibutton_protocols_save,_Bool,"iButtonProtocols*, const iButtonKey*, const char*" +Function,+,ibutton_protocols_write_blank,_Bool,"iButtonProtocols*, iButtonKey*" +Function,+,ibutton_protocols_write_copy,_Bool,"iButtonProtocols*, iButtonKey*" +Function,+,ibutton_worker_alloc,iButtonWorker*,iButtonProtocols* Function,+,ibutton_worker_emulate_set_callback,void,"iButtonWorker*, iButtonWorkerEmulateCallback, void*" Function,+,ibutton_worker_emulate_start,void,"iButtonWorker*, iButtonKey*" Function,+,ibutton_worker_free,void,iButtonWorker* @@ -1673,13 +1721,14 @@ Function,+,ibutton_worker_read_start,void,"iButtonWorker*, iButtonKey*" Function,+,ibutton_worker_start_thread,void,iButtonWorker* Function,+,ibutton_worker_stop,void,iButtonWorker* Function,+,ibutton_worker_stop_thread,void,iButtonWorker* +Function,+,ibutton_worker_write_blank_start,void,"iButtonWorker*, iButtonKey*" +Function,+,ibutton_worker_write_copy_start,void,"iButtonWorker*, iButtonKey*" Function,+,ibutton_worker_write_set_callback,void,"iButtonWorker*, iButtonWorkerWriteCallback, void*" -Function,+,ibutton_worker_write_start,void,"iButtonWorker*, iButtonKey*" Function,+,icon_animation_alloc,IconAnimation*,const Icon* Function,+,icon_animation_free,void,IconAnimation* -Function,+,icon_animation_get_height,uint8_t,IconAnimation* -Function,+,icon_animation_get_width,uint8_t,IconAnimation* -Function,+,icon_animation_is_last_frame,_Bool,IconAnimation* +Function,+,icon_animation_get_height,uint8_t,const IconAnimation* +Function,+,icon_animation_get_width,uint8_t,const IconAnimation* +Function,+,icon_animation_is_last_frame,_Bool,const IconAnimation* Function,+,icon_animation_set_update_callback,void,"IconAnimation*, IconAnimationCallback, void*" Function,+,icon_animation_start,void,IconAnimation* Function,+,icon_animation_stop,void,IconAnimation* @@ -1729,7 +1778,6 @@ Function,+,infrared_worker_tx_set_get_signal_callback,void,"InfraredWorker*, Inf Function,+,infrared_worker_tx_set_signal_sent_callback,void,"InfraredWorker*, InfraredWorkerMessageSentCallback, void*" Function,+,infrared_worker_tx_start,void,InfraredWorker* Function,+,infrared_worker_tx_stop,void,InfraredWorker* -Function,+,init_mutex,_Bool,"ValueMutex*, void*, size_t" Function,-,initstate,char*,"unsigned, char*, size_t" Function,+,input_get_key_name,const char*,InputKey Function,+,input_get_type_name,const char*,InputType @@ -1775,8 +1823,12 @@ Function,-,j1f,float,float Function,-,jn,double,"int, double" Function,-,jnf,float,"int, float" Function,-,jrand48,long,unsigned short[3] +Function,-,keeloq_get_custom_btn,uint8_t, +Function,-,keeloq_get_original_btn,uint8_t, Function,-,keeloq_reset_kl_type,void, Function,-,keeloq_reset_mfname,void, +Function,-,keeloq_reset_original_btn,void, +Function,-,keeloq_set_btn,void,uint8_t Function,-,l64a,char*,long Function,-,labs,long,long Function,-,lcong48,void,unsigned short[7] @@ -1823,7 +1875,7 @@ Function,-,llround,long long int,double Function,-,llroundf,long long int,float Function,-,llroundl,long long int,long double Function,+,loader_get_pubsub,FuriPubSub*,Loader* -Function,+,loader_is_locked,_Bool,Loader* +Function,+,loader_is_locked,_Bool,const Loader* Function,+,loader_lock,_Bool,Loader* Function,+,loader_show_menu,void, Function,+,loader_start,LoaderStatus,"Loader*, const char*, const char*" @@ -1936,7 +1988,7 @@ Function,+,menu_free,void,Menu* Function,+,menu_get_view,View*,Menu* Function,+,menu_reset,void,Menu* Function,+,menu_set_selected_item,void,"Menu*, uint32_t" -Function,-,mf_classic_auth_attempt,_Bool,"FuriHalNfcTxRxContext*, MfClassicAuthContext*, uint64_t" +Function,-,mf_classic_auth_attempt,_Bool,"FuriHalNfcTxRxContext*, Crypto1*, MfClassicAuthContext*, uint64_t" Function,-,mf_classic_auth_init_context,void,"MfClassicAuthContext*, uint8_t" Function,-,mf_classic_auth_write_block,_Bool,"FuriHalNfcTxRxContext*, MfClassicBlock*, uint8_t, MfClassicKey, uint64_t" Function,-,mf_classic_authenticate,_Bool,"FuriHalNfcTxRxContext*, uint8_t, uint64_t, MfClassicKey" @@ -2124,19 +2176,17 @@ Function,-,nfcv_inventory,ReturnCode,uint8_t* Function,-,nfcv_read_blocks,ReturnCode,"NfcVReader*, NfcVData*" Function,-,nfcv_read_card,_Bool,"NfcVReader*, FuriHalNfcDevData*, NfcVData*" Function,-,nfcv_read_sysinfo,ReturnCode,"FuriHalNfcDevData*, NfcVData*" +Function,-,nice_flors_get_custom_btn,uint8_t, +Function,-,nice_flors_get_original_btn,uint8_t, +Function,-,nice_flors_reset_original_btn,void, +Function,-,nice_flors_set_btn,void,uint8_t Function,+,notification_internal_message,void,"NotificationApp*, const NotificationSequence*" Function,+,notification_internal_message_block,void,"NotificationApp*, const NotificationSequence*" Function,+,notification_message,void,"NotificationApp*, const NotificationSequence*" Function,+,notification_message_block,void,"NotificationApp*, const NotificationSequence*" Function,-,nrand48,long,unsigned short[3] Function,-,on_exit,int,"void (*)(int, void*), void*" -Function,+,onewire_device_alloc,OneWireDevice*,"uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t" -Function,+,onewire_device_attach,void,"OneWireDevice*, OneWireSlave*" -Function,+,onewire_device_detach,void,OneWireDevice* -Function,+,onewire_device_free,void,OneWireDevice* -Function,+,onewire_device_get_id_p,uint8_t*,OneWireDevice* -Function,+,onewire_device_send_id,void,OneWireDevice* -Function,+,onewire_host_alloc,OneWireHost*, +Function,+,onewire_host_alloc,OneWireHost*,const GpioPin* Function,+,onewire_host_free,void,OneWireHost* Function,+,onewire_host_read,uint8_t,OneWireHost* Function,+,onewire_host_read_bit,_Bool,OneWireHost* @@ -2150,10 +2200,15 @@ Function,+,onewire_host_stop,void,OneWireHost* Function,+,onewire_host_target_search,void,"OneWireHost*, uint8_t" Function,+,onewire_host_write,void,"OneWireHost*, uint8_t" Function,+,onewire_host_write_bit,void,"OneWireHost*, _Bool" -Function,+,onewire_slave_alloc,OneWireSlave*, -Function,+,onewire_slave_attach,void,"OneWireSlave*, OneWireDevice*" -Function,+,onewire_slave_detach,void,OneWireSlave* +Function,+,onewire_host_write_bytes,void,"OneWireHost*, const uint8_t*, uint16_t" +Function,+,onewire_slave_alloc,OneWireSlave*,const GpioPin* Function,+,onewire_slave_free,void,OneWireSlave* +Function,+,onewire_slave_receive,_Bool,"OneWireSlave*, uint8_t*, size_t" +Function,+,onewire_slave_receive_bit,_Bool,OneWireSlave* +Function,+,onewire_slave_send,_Bool,"OneWireSlave*, const uint8_t*, size_t" +Function,+,onewire_slave_send_bit,_Bool,"OneWireSlave*, _Bool" +Function,+,onewire_slave_set_command_callback,void,"OneWireSlave*, OneWireSlaveCommandCallback, void*" +Function,+,onewire_slave_set_reset_callback,void,"OneWireSlave*, OneWireSlaveResetCallback, void*" Function,+,onewire_slave_set_result_callback,void,"OneWireSlave*, OneWireSlaveResultCallback, void*" Function,+,onewire_slave_start,void,OneWireSlave* Function,+,onewire_slave_stop,void,OneWireSlave* @@ -2202,6 +2257,7 @@ Function,+,power_off,void,Power* Function,+,power_reboot,void,PowerBootMode Function,+,powf,float,"float, float" Function,-,powl,long double,"long double, long double" +Function,+,pretty_format_bytes_hex_canonical,void,"FuriString*, size_t, const char*, const uint8_t*, size_t" Function,-,printf,int,"const char*, ..." Function,-,prng_successor,uint32_t,"uint32_t, uint32_t" Function,+,property_value_out,void,"PropertyValueContext*, const char*, unsigned int, ..." @@ -2253,12 +2309,10 @@ Function,+,rand,int, Function,-,rand_r,int,unsigned* Function,+,random,long, Function,-,rawmemchr,void*,"const void*, int" -Function,-,read_mutex,_Bool,"ValueMutex*, void*, size_t, uint32_t" Function,+,realloc,void*,"void*, size_t" Function,-,reallocarray,void*,"void*, size_t, size_t" Function,-,reallocf,void*,"void*, size_t" Function,-,realpath,char*,"const char*, char*" -Function,+,release_mutex,_Bool,"ValueMutex*, const void*" Function,-,remainder,double,"double, double" Function,-,remainderf,float,"float, float" Function,-,remainderl,long double,"long double, long double" @@ -2480,11 +2534,11 @@ Function,-,scalbnl,long double,"long double, int" Function,-,scanf,int,"const char*, ..." Function,+,scene_manager_alloc,SceneManager*,"const SceneManagerHandlers*, void*" Function,+,scene_manager_free,void,SceneManager* -Function,+,scene_manager_get_scene_state,uint32_t,"SceneManager*, uint32_t" +Function,+,scene_manager_get_scene_state,uint32_t,"const SceneManager*, uint32_t" Function,+,scene_manager_handle_back_event,_Bool,SceneManager* Function,+,scene_manager_handle_custom_event,_Bool,"SceneManager*, uint32_t" Function,+,scene_manager_handle_tick_event,void,SceneManager* -Function,+,scene_manager_has_previous_scene,_Bool,"SceneManager*, uint32_t" +Function,+,scene_manager_has_previous_scene,_Bool,"const SceneManager*, uint32_t" Function,+,scene_manager_next_scene,void,"SceneManager*, uint32_t" Function,+,scene_manager_previous_scene,_Bool,SceneManager* Function,+,scene_manager_search_and_switch_to_another_scene,_Bool,"SceneManager*, uint32_t" @@ -2528,6 +2582,10 @@ Function,-,siprintf,int,"char*, const char*, ..." Function,-,siscanf,int,"const char*, const char*, ..." Function,-,sniprintf,int,"char*, size_t, const char*, ..." Function,+,snprintf,int,"char*, size_t, const char*, ..." +Function,-,somfy_telis_get_custom_btn,uint8_t, +Function,-,somfy_telis_get_original_btn,uint8_t, +Function,-,somfy_telis_reset_original_btn,void, +Function,-,somfy_telis_set_btn,void,uint8_t Function,-,sprintf,int,"char*, const char*, ..." Function,-,sqrt,double,double Function,-,sqrtf,float,float @@ -2539,14 +2597,18 @@ Function,+,sscanf,int,"const char*, const char*, ..." Function,-,star_line_reset_kl_type,void, Function,-,star_line_reset_mfname,void, Function,+,storage_common_copy,FS_Error,"Storage*, const char*, const char*" +Function,+,storage_common_exists,_Bool,"Storage*, const char*" Function,+,storage_common_fs_info,FS_Error,"Storage*, const char*, uint64_t*, uint64_t*" Function,+,storage_common_merge,FS_Error,"Storage*, const char*, const char*" +Function,+,storage_common_migrate,FS_Error,"Storage*, const char*, const char*" Function,+,storage_common_mkdir,FS_Error,"Storage*, const char*" Function,+,storage_common_remove,FS_Error,"Storage*, const char*" Function,+,storage_common_rename,FS_Error,"Storage*, const char*, const char*" +Function,+,storage_common_resolve_path_and_ensure_app_directory,void,"Storage*, FuriString*" Function,+,storage_common_stat,FS_Error,"Storage*, const char*, FileInfo*" Function,+,storage_common_timestamp,FS_Error,"Storage*, const char*, uint32_t*" Function,+,storage_dir_close,_Bool,File* +Function,+,storage_dir_exists,_Bool,"Storage*, const char*" Function,+,storage_dir_open,_Bool,"File*, const char*" Function,+,storage_dir_read,_Bool,"File*, FileInfo*, char*, uint16_t" Function,-,storage_dir_rewind,_Bool,File* @@ -2582,7 +2644,7 @@ Function,+,storage_simply_remove,_Bool,"Storage*, const char*" Function,+,storage_simply_remove_recursive,_Bool,"Storage*, const char*" Function,-,stpcpy,char*,"char*, const char*" Function,-,stpncpy,char*,"char*, const char*, size_t" -Function,-,strcasecmp,int,"const char*, const char*" +Function,+,strcasecmp,int,"const char*, const char*" Function,-,strcasecmp_l,int,"const char*, const char*, locale_t" Function,+,strcasestr,char*,"const char*, const char*" Function,+,strcat,char*,"char*, const char*" @@ -2675,9 +2737,10 @@ Function,-,strupr,char*,char* Function,-,strverscmp,int,"const char*, const char*" Function,-,strxfrm,size_t,"char*, const char*, size_t" Function,-,strxfrm_l,size_t,"char*, const char*, size_t, locale_t" -Function,+,subghz_block_generic_deserialize,_Bool,"SubGhzBlockGeneric*, FlipperFormat*" +Function,+,subghz_block_generic_deserialize,SubGhzProtocolStatus,"SubGhzBlockGeneric*, FlipperFormat*" +Function,+,subghz_block_generic_deserialize_check_count_bit,SubGhzProtocolStatus,"SubGhzBlockGeneric*, FlipperFormat*, uint16_t" Function,+,subghz_block_generic_get_preset_name,void,"const char*, FuriString*" -Function,+,subghz_block_generic_serialize,_Bool,"SubGhzBlockGeneric*, FlipperFormat*, SubGhzRadioPreset*" +Function,+,subghz_block_generic_serialize,SubGhzProtocolStatus,"SubGhzBlockGeneric*, FlipperFormat*, SubGhzRadioPreset*" Function,+,subghz_environment_alloc,SubGhzEnvironment*, Function,+,subghz_environment_free,void,SubGhzEnvironment* Function,+,subghz_environment_get_alutech_at_4n_rainbow_table_file_name,const char*,SubGhzEnvironment* @@ -2721,544 +2784,544 @@ Function,+,subghz_protocol_blocks_reverse_key,uint64_t,"uint64_t, uint8_t" Function,+,subghz_protocol_blocks_set_bit_array,void,"_Bool, uint8_t[], size_t, size_t" Function,+,subghz_protocol_blocks_xor_bytes,uint8_t,"const uint8_t[], size_t" Function,-,subghz_protocol_decoder_alutech_at_4n_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_alutech_at_4n_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_alutech_at_4n_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_alutech_at_4n_feed,void,"void*, _Bool, uint32_t" Function,-,subghz_protocol_decoder_alutech_at_4n_free,void,void* Function,-,subghz_protocol_decoder_alutech_at_4n_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_alutech_at_4n_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_alutech_at_4n_reset,void,void* -Function,-,subghz_protocol_decoder_alutech_at_4n_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_alutech_at_4n_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_ansonic_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_ansonic_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_ansonic_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_ansonic_feed,void,"void*, _Bool, uint32_t" Function,-,subghz_protocol_decoder_ansonic_free,void,void* Function,-,subghz_protocol_decoder_ansonic_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_ansonic_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_ansonic_reset,void,void* -Function,-,subghz_protocol_decoder_ansonic_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_base_deserialize,_Bool,"SubGhzProtocolDecoderBase*, FlipperFormat*" +Function,-,subghz_protocol_decoder_ansonic_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,+,subghz_protocol_decoder_base_deserialize,SubGhzProtocolStatus,"SubGhzProtocolDecoderBase*, FlipperFormat*" Function,+,subghz_protocol_decoder_base_get_hash_data,uint8_t,SubGhzProtocolDecoderBase* Function,+,subghz_protocol_decoder_base_get_string,_Bool,"SubGhzProtocolDecoderBase*, FuriString*" -Function,+,subghz_protocol_decoder_base_serialize,_Bool,"SubGhzProtocolDecoderBase*, FlipperFormat*, SubGhzRadioPreset*" +Function,+,subghz_protocol_decoder_base_serialize,SubGhzProtocolStatus,"SubGhzProtocolDecoderBase*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_base_set_decoder_callback,void,"SubGhzProtocolDecoderBase*, SubGhzProtocolDecoderBaseRxCallback, void*" Function,-,subghz_protocol_decoder_bett_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_bett_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_bett_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_bett_feed,void,"void*, _Bool, uint32_t" Function,-,subghz_protocol_decoder_bett_free,void,void* Function,-,subghz_protocol_decoder_bett_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_bett_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_bett_reset,void,void* -Function,-,subghz_protocol_decoder_bett_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_bett_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_bin_raw_alloc,void*,SubGhzEnvironment* Function,-,subghz_protocol_decoder_bin_raw_data_input_rssi,void,"SubGhzProtocolDecoderBinRAW*, float" -Function,-,subghz_protocol_decoder_bin_raw_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_bin_raw_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_bin_raw_feed,void,"void*, _Bool, uint32_t" Function,-,subghz_protocol_decoder_bin_raw_free,void,void* Function,-,subghz_protocol_decoder_bin_raw_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_bin_raw_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_bin_raw_reset,void,void* -Function,-,subghz_protocol_decoder_bin_raw_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_bin_raw_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_came_alloc,void*,SubGhzEnvironment* Function,-,subghz_protocol_decoder_came_atomo_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_came_atomo_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_came_atomo_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_came_atomo_feed,void,"void*, _Bool, uint32_t" Function,-,subghz_protocol_decoder_came_atomo_free,void,void* Function,-,subghz_protocol_decoder_came_atomo_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_came_atomo_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_came_atomo_reset,void,void* -Function,-,subghz_protocol_decoder_came_atomo_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_came_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_came_atomo_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_came_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_came_feed,void,"void*, _Bool, uint32_t" Function,-,subghz_protocol_decoder_came_free,void,void* Function,-,subghz_protocol_decoder_came_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_came_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_came_reset,void,void* -Function,-,subghz_protocol_decoder_came_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_came_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_came_twee_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_came_twee_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_came_twee_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_came_twee_feed,void,"void*, _Bool, uint32_t" Function,-,subghz_protocol_decoder_came_twee_free,void,void* Function,-,subghz_protocol_decoder_came_twee_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_came_twee_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_came_twee_reset,void,void* -Function,-,subghz_protocol_decoder_came_twee_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_came_twee_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_chamb_code_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_chamb_code_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_chamb_code_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_chamb_code_feed,void,"void*, _Bool, uint32_t" Function,-,subghz_protocol_decoder_chamb_code_free,void,void* Function,-,subghz_protocol_decoder_chamb_code_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_chamb_code_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_chamb_code_reset,void,void* -Function,-,subghz_protocol_decoder_chamb_code_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_chamb_code_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_clemsa_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_clemsa_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_clemsa_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_clemsa_feed,void,"void*, _Bool, uint32_t" Function,-,subghz_protocol_decoder_clemsa_free,void,void* Function,-,subghz_protocol_decoder_clemsa_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_clemsa_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_clemsa_reset,void,void* -Function,-,subghz_protocol_decoder_clemsa_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_clemsa_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_doitrand_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_doitrand_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_doitrand_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_doitrand_feed,void,"void*, _Bool, uint32_t" Function,-,subghz_protocol_decoder_doitrand_free,void,void* Function,-,subghz_protocol_decoder_doitrand_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_doitrand_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_doitrand_reset,void,void* -Function,-,subghz_protocol_decoder_doitrand_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_doitrand_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_dooya_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_dooya_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_dooya_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_dooya_feed,void,"void*, _Bool, uint32_t" Function,-,subghz_protocol_decoder_dooya_free,void,void* Function,-,subghz_protocol_decoder_dooya_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_dooya_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_dooya_reset,void,void* -Function,-,subghz_protocol_decoder_dooya_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_dooya_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_faac_slh_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_faac_slh_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_faac_slh_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_faac_slh_feed,void,"void*, _Bool, uint32_t" Function,-,subghz_protocol_decoder_faac_slh_free,void,void* Function,-,subghz_protocol_decoder_faac_slh_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_faac_slh_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_faac_slh_reset,void,void* -Function,-,subghz_protocol_decoder_faac_slh_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_faac_slh_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_gate_tx_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_gate_tx_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_gate_tx_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_gate_tx_feed,void,"void*, _Bool, uint32_t" Function,-,subghz_protocol_decoder_gate_tx_free,void,void* Function,-,subghz_protocol_decoder_gate_tx_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_gate_tx_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_gate_tx_reset,void,void* -Function,-,subghz_protocol_decoder_gate_tx_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_gate_tx_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_holtek_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_holtek_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_holtek_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_holtek_feed,void,"void*, _Bool, uint32_t" Function,-,subghz_protocol_decoder_holtek_free,void,void* Function,-,subghz_protocol_decoder_holtek_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_holtek_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_holtek_reset,void,void* -Function,-,subghz_protocol_decoder_holtek_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_holtek_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_holtek_th12x_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_holtek_th12x_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_holtek_th12x_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_holtek_th12x_feed,void,"void*, _Bool, uint32_t" Function,-,subghz_protocol_decoder_holtek_th12x_free,void,void* Function,-,subghz_protocol_decoder_holtek_th12x_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_holtek_th12x_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_holtek_th12x_reset,void,void* -Function,-,subghz_protocol_decoder_holtek_th12x_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_holtek_th12x_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_honeywell_wdb_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_honeywell_wdb_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_honeywell_wdb_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_honeywell_wdb_feed,void,"void*, _Bool, uint32_t" Function,-,subghz_protocol_decoder_honeywell_wdb_free,void,void* Function,-,subghz_protocol_decoder_honeywell_wdb_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_honeywell_wdb_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_honeywell_wdb_reset,void,void* -Function,-,subghz_protocol_decoder_honeywell_wdb_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_honeywell_wdb_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_hormann_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_hormann_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_hormann_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_hormann_feed,void,"void*, _Bool, uint32_t" Function,-,subghz_protocol_decoder_hormann_free,void,void* Function,-,subghz_protocol_decoder_hormann_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_hormann_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_hormann_reset,void,void* -Function,-,subghz_protocol_decoder_hormann_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_hormann_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_ido_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_ido_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_ido_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_ido_feed,void,"void*, _Bool, uint32_t" Function,-,subghz_protocol_decoder_ido_free,void,void* Function,-,subghz_protocol_decoder_ido_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_ido_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_ido_reset,void,void* -Function,-,subghz_protocol_decoder_ido_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_ido_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_intertechno_v3_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_intertechno_v3_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_intertechno_v3_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_intertechno_v3_feed,void,"void*, _Bool, uint32_t" Function,-,subghz_protocol_decoder_intertechno_v3_free,void,void* Function,-,subghz_protocol_decoder_intertechno_v3_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_intertechno_v3_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_intertechno_v3_reset,void,void* -Function,-,subghz_protocol_decoder_intertechno_v3_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_intertechno_v3_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_keeloq_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_keeloq_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_keeloq_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_keeloq_feed,void,"void*, _Bool, uint32_t" Function,-,subghz_protocol_decoder_keeloq_free,void,void* Function,-,subghz_protocol_decoder_keeloq_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_keeloq_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_keeloq_reset,void,void* -Function,-,subghz_protocol_decoder_keeloq_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_keeloq_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_kia_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_kia_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_kia_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_kia_feed,void,"void*, _Bool, uint32_t" Function,-,subghz_protocol_decoder_kia_free,void,void* Function,-,subghz_protocol_decoder_kia_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_kia_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_kia_reset,void,void* -Function,-,subghz_protocol_decoder_kia_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_kia_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_kinggates_stylo_4k_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_kinggates_stylo_4k_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_kinggates_stylo_4k_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_kinggates_stylo_4k_feed,void,"void*, _Bool, uint32_t" Function,-,subghz_protocol_decoder_kinggates_stylo_4k_free,void,void* Function,-,subghz_protocol_decoder_kinggates_stylo_4k_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_kinggates_stylo_4k_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_kinggates_stylo_4k_reset,void,void* -Function,-,subghz_protocol_decoder_kinggates_stylo_4k_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_kinggates_stylo_4k_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_linear_alloc,void*,SubGhzEnvironment* Function,-,subghz_protocol_decoder_linear_delta3_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_linear_delta3_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_linear_delta3_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_linear_delta3_feed,void,"void*, _Bool, uint32_t" Function,-,subghz_protocol_decoder_linear_delta3_free,void,void* Function,-,subghz_protocol_decoder_linear_delta3_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_linear_delta3_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_linear_delta3_reset,void,void* -Function,-,subghz_protocol_decoder_linear_delta3_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_linear_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_linear_delta3_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_linear_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_linear_feed,void,"void*, _Bool, uint32_t" Function,-,subghz_protocol_decoder_linear_free,void,void* Function,-,subghz_protocol_decoder_linear_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_linear_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_linear_reset,void,void* -Function,-,subghz_protocol_decoder_linear_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_linear_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_magellan_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_magellan_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_magellan_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_magellan_feed,void,"void*, _Bool, uint32_t" Function,-,subghz_protocol_decoder_magellan_free,void,void* Function,-,subghz_protocol_decoder_magellan_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_magellan_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_magellan_reset,void,void* -Function,-,subghz_protocol_decoder_magellan_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_magellan_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_marantec_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_marantec_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_marantec_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_marantec_feed,void,"void*, _Bool, uint32_t" Function,-,subghz_protocol_decoder_marantec_free,void,void* Function,-,subghz_protocol_decoder_marantec_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_marantec_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_marantec_reset,void,void* -Function,-,subghz_protocol_decoder_marantec_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_marantec_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_megacode_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_megacode_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_megacode_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_megacode_feed,void,"void*, _Bool, uint32_t" Function,-,subghz_protocol_decoder_megacode_free,void,void* Function,-,subghz_protocol_decoder_megacode_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_megacode_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_megacode_reset,void,void* -Function,-,subghz_protocol_decoder_megacode_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_megacode_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_nero_radio_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_nero_radio_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_nero_radio_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_nero_radio_feed,void,"void*, _Bool, uint32_t" Function,-,subghz_protocol_decoder_nero_radio_free,void,void* Function,-,subghz_protocol_decoder_nero_radio_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_nero_radio_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_nero_radio_reset,void,void* -Function,-,subghz_protocol_decoder_nero_radio_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_nero_radio_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_nero_sketch_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_nero_sketch_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_nero_sketch_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_nero_sketch_feed,void,"void*, _Bool, uint32_t" Function,-,subghz_protocol_decoder_nero_sketch_free,void,void* Function,-,subghz_protocol_decoder_nero_sketch_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_nero_sketch_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_nero_sketch_reset,void,void* -Function,-,subghz_protocol_decoder_nero_sketch_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_nero_sketch_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_nice_flo_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_nice_flo_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_nice_flo_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_nice_flo_feed,void,"void*, _Bool, uint32_t" Function,-,subghz_protocol_decoder_nice_flo_free,void,void* Function,-,subghz_protocol_decoder_nice_flo_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_nice_flo_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_nice_flo_reset,void,void* -Function,-,subghz_protocol_decoder_nice_flo_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_nice_flo_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_nice_flor_s_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_nice_flor_s_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_nice_flor_s_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_nice_flor_s_feed,void,"void*, _Bool, uint32_t" Function,-,subghz_protocol_decoder_nice_flor_s_free,void,void* Function,-,subghz_protocol_decoder_nice_flor_s_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_nice_flor_s_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_nice_flor_s_reset,void,void* -Function,-,subghz_protocol_decoder_nice_flor_s_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_nice_flor_s_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_phoenix_v2_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_phoenix_v2_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_phoenix_v2_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_phoenix_v2_feed,void,"void*, _Bool, uint32_t" Function,-,subghz_protocol_decoder_phoenix_v2_free,void,void* Function,-,subghz_protocol_decoder_phoenix_v2_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_phoenix_v2_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_phoenix_v2_reset,void,void* -Function,-,subghz_protocol_decoder_phoenix_v2_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_phoenix_v2_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_power_smart_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_power_smart_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_power_smart_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_power_smart_feed,void,"void*, _Bool, uint32_t" Function,-,subghz_protocol_decoder_power_smart_free,void,void* Function,-,subghz_protocol_decoder_power_smart_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_power_smart_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_power_smart_reset,void,void* -Function,-,subghz_protocol_decoder_power_smart_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_power_smart_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,+,subghz_protocol_decoder_princeton_alloc,void*,SubGhzEnvironment* -Function,+,subghz_protocol_decoder_princeton_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_princeton_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,+,subghz_protocol_decoder_princeton_feed,void,"void*, _Bool, uint32_t" Function,+,subghz_protocol_decoder_princeton_free,void,void* Function,+,subghz_protocol_decoder_princeton_get_hash_data,uint8_t,void* Function,+,subghz_protocol_decoder_princeton_get_string,void,"void*, FuriString*" Function,+,subghz_protocol_decoder_princeton_reset,void,void* -Function,+,subghz_protocol_decoder_princeton_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_princeton_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,+,subghz_protocol_decoder_raw_alloc,void*,SubGhzEnvironment* -Function,+,subghz_protocol_decoder_raw_deserialize,_Bool,"void*, FlipperFormat*" +Function,+,subghz_protocol_decoder_raw_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,+,subghz_protocol_decoder_raw_feed,void,"void*, _Bool, uint32_t" Function,+,subghz_protocol_decoder_raw_free,void,void* Function,+,subghz_protocol_decoder_raw_get_string,void,"void*, FuriString*" Function,+,subghz_protocol_decoder_raw_reset,void,void* Function,-,subghz_protocol_decoder_scher_khan_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_scher_khan_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_scher_khan_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_scher_khan_feed,void,"void*, _Bool, uint32_t" Function,-,subghz_protocol_decoder_scher_khan_free,void,void* Function,-,subghz_protocol_decoder_scher_khan_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_scher_khan_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_scher_khan_reset,void,void* -Function,-,subghz_protocol_decoder_scher_khan_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_scher_khan_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_secplus_v1_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_secplus_v1_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_secplus_v1_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_secplus_v1_feed,void,"void*, _Bool, uint32_t" Function,-,subghz_protocol_decoder_secplus_v1_free,void,void* Function,-,subghz_protocol_decoder_secplus_v1_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_secplus_v1_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_secplus_v1_reset,void,void* -Function,-,subghz_protocol_decoder_secplus_v1_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_secplus_v1_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_secplus_v2_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_secplus_v2_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_secplus_v2_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_secplus_v2_feed,void,"void*, _Bool, uint32_t" Function,-,subghz_protocol_decoder_secplus_v2_free,void,void* Function,-,subghz_protocol_decoder_secplus_v2_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_secplus_v2_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_secplus_v2_reset,void,void* -Function,-,subghz_protocol_decoder_secplus_v2_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_secplus_v2_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_smc5326_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_smc5326_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_smc5326_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_smc5326_feed,void,"void*, _Bool, uint32_t" Function,-,subghz_protocol_decoder_smc5326_free,void,void* Function,-,subghz_protocol_decoder_smc5326_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_smc5326_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_smc5326_reset,void,void* -Function,-,subghz_protocol_decoder_smc5326_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_smc5326_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_somfy_keytis_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_somfy_keytis_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_somfy_keytis_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_somfy_keytis_feed,void,"void*, _Bool, uint32_t" Function,-,subghz_protocol_decoder_somfy_keytis_free,void,void* Function,-,subghz_protocol_decoder_somfy_keytis_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_somfy_keytis_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_somfy_keytis_reset,void,void* -Function,-,subghz_protocol_decoder_somfy_keytis_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_somfy_keytis_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_somfy_telis_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_somfy_telis_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_somfy_telis_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_somfy_telis_feed,void,"void*, _Bool, uint32_t" Function,-,subghz_protocol_decoder_somfy_telis_free,void,void* Function,-,subghz_protocol_decoder_somfy_telis_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_somfy_telis_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_somfy_telis_reset,void,void* -Function,-,subghz_protocol_decoder_somfy_telis_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_somfy_telis_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_star_line_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_star_line_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_star_line_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_star_line_feed,void,"void*, _Bool, uint32_t" Function,-,subghz_protocol_decoder_star_line_free,void,void* Function,-,subghz_protocol_decoder_star_line_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_star_line_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_star_line_reset,void,void* -Function,-,subghz_protocol_decoder_star_line_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_star_line_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_encoder_alutech_at_4n_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_alutech_at_4n_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_alutech_at_4n_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_encoder_alutech_at_4n_free,void,void* Function,-,subghz_protocol_encoder_alutech_at_4n_stop,void,void* Function,-,subghz_protocol_encoder_alutech_at_4n_yield,LevelDuration,void* Function,-,subghz_protocol_encoder_ansonic_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_ansonic_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_ansonic_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_encoder_ansonic_free,void,void* Function,-,subghz_protocol_encoder_ansonic_stop,void,void* Function,-,subghz_protocol_encoder_ansonic_yield,LevelDuration,void* Function,-,subghz_protocol_encoder_bett_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_bett_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_bett_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_encoder_bett_free,void,void* Function,-,subghz_protocol_encoder_bett_stop,void,void* Function,-,subghz_protocol_encoder_bett_yield,LevelDuration,void* Function,-,subghz_protocol_encoder_bin_raw_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_bin_raw_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_bin_raw_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_encoder_bin_raw_free,void,void* Function,-,subghz_protocol_encoder_bin_raw_stop,void,void* Function,-,subghz_protocol_encoder_bin_raw_yield,LevelDuration,void* Function,-,subghz_protocol_encoder_came_alloc,void*,SubGhzEnvironment* Function,-,subghz_protocol_encoder_came_atomo_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_came_atomo_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_came_atomo_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_encoder_came_atomo_free,void,void* Function,-,subghz_protocol_encoder_came_atomo_stop,void,void* Function,-,subghz_protocol_encoder_came_atomo_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_came_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_came_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_encoder_came_free,void,void* Function,-,subghz_protocol_encoder_came_stop,void,void* Function,-,subghz_protocol_encoder_came_twee_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_came_twee_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_came_twee_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_encoder_came_twee_free,void,void* Function,-,subghz_protocol_encoder_came_twee_stop,void,void* Function,-,subghz_protocol_encoder_came_twee_yield,LevelDuration,void* Function,-,subghz_protocol_encoder_came_yield,LevelDuration,void* Function,-,subghz_protocol_encoder_chamb_code_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_chamb_code_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_chamb_code_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_encoder_chamb_code_free,void,void* Function,-,subghz_protocol_encoder_chamb_code_stop,void,void* Function,-,subghz_protocol_encoder_chamb_code_yield,LevelDuration,void* Function,-,subghz_protocol_encoder_clemsa_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_clemsa_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_clemsa_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_encoder_clemsa_free,void,void* Function,-,subghz_protocol_encoder_clemsa_stop,void,void* Function,-,subghz_protocol_encoder_clemsa_yield,LevelDuration,void* Function,-,subghz_protocol_encoder_doitrand_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_doitrand_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_doitrand_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_encoder_doitrand_free,void,void* Function,-,subghz_protocol_encoder_doitrand_stop,void,void* Function,-,subghz_protocol_encoder_doitrand_yield,LevelDuration,void* Function,-,subghz_protocol_encoder_dooya_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_dooya_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_dooya_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_encoder_dooya_free,void,void* Function,-,subghz_protocol_encoder_dooya_stop,void,void* Function,-,subghz_protocol_encoder_dooya_yield,LevelDuration,void* Function,-,subghz_protocol_encoder_faac_slh_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_faac_slh_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_faac_slh_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_encoder_faac_slh_free,void,void* Function,-,subghz_protocol_encoder_faac_slh_stop,void,void* Function,-,subghz_protocol_encoder_faac_slh_yield,LevelDuration,void* Function,-,subghz_protocol_encoder_gate_tx_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_gate_tx_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_gate_tx_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_encoder_gate_tx_free,void,void* Function,-,subghz_protocol_encoder_gate_tx_stop,void,void* Function,-,subghz_protocol_encoder_gate_tx_yield,LevelDuration,void* Function,-,subghz_protocol_encoder_holtek_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_holtek_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_holtek_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_encoder_holtek_free,void,void* Function,-,subghz_protocol_encoder_holtek_stop,void,void* Function,-,subghz_protocol_encoder_holtek_th12x_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_holtek_th12x_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_holtek_th12x_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_encoder_holtek_th12x_free,void,void* Function,-,subghz_protocol_encoder_holtek_th12x_stop,void,void* Function,-,subghz_protocol_encoder_holtek_th12x_yield,LevelDuration,void* Function,-,subghz_protocol_encoder_holtek_yield,LevelDuration,void* Function,-,subghz_protocol_encoder_honeywell_wdb_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_honeywell_wdb_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_honeywell_wdb_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_encoder_honeywell_wdb_free,void,void* Function,-,subghz_protocol_encoder_honeywell_wdb_stop,void,void* Function,-,subghz_protocol_encoder_honeywell_wdb_yield,LevelDuration,void* Function,-,subghz_protocol_encoder_hormann_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_hormann_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_hormann_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_encoder_hormann_free,void,void* Function,-,subghz_protocol_encoder_hormann_stop,void,void* Function,-,subghz_protocol_encoder_hormann_yield,LevelDuration,void* Function,-,subghz_protocol_encoder_intertechno_v3_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_intertechno_v3_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_intertechno_v3_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_encoder_intertechno_v3_free,void,void* Function,-,subghz_protocol_encoder_intertechno_v3_stop,void,void* Function,-,subghz_protocol_encoder_intertechno_v3_yield,LevelDuration,void* Function,-,subghz_protocol_encoder_keeloq_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_keeloq_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_keeloq_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_encoder_keeloq_free,void,void* Function,-,subghz_protocol_encoder_keeloq_stop,void,void* Function,-,subghz_protocol_encoder_keeloq_yield,LevelDuration,void* Function,-,subghz_protocol_encoder_kinggates_stylo_4k_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_kinggates_stylo_4k_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_kinggates_stylo_4k_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_encoder_kinggates_stylo_4k_free,void,void* Function,-,subghz_protocol_encoder_kinggates_stylo_4k_stop,void,void* Function,-,subghz_protocol_encoder_kinggates_stylo_4k_yield,LevelDuration,void* Function,-,subghz_protocol_encoder_linear_alloc,void*,SubGhzEnvironment* Function,-,subghz_protocol_encoder_linear_delta3_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_linear_delta3_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_linear_delta3_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_encoder_linear_delta3_free,void,void* Function,-,subghz_protocol_encoder_linear_delta3_stop,void,void* Function,-,subghz_protocol_encoder_linear_delta3_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_linear_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_linear_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_encoder_linear_free,void,void* Function,-,subghz_protocol_encoder_linear_stop,void,void* Function,-,subghz_protocol_encoder_linear_yield,LevelDuration,void* Function,-,subghz_protocol_encoder_magellan_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_magellan_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_magellan_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_encoder_magellan_free,void,void* Function,-,subghz_protocol_encoder_magellan_stop,void,void* Function,-,subghz_protocol_encoder_magellan_yield,LevelDuration,void* Function,-,subghz_protocol_encoder_marantec_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_marantec_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_marantec_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_encoder_marantec_free,void,void* Function,-,subghz_protocol_encoder_marantec_stop,void,void* Function,-,subghz_protocol_encoder_marantec_yield,LevelDuration,void* Function,-,subghz_protocol_encoder_megacode_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_megacode_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_megacode_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_encoder_megacode_free,void,void* Function,-,subghz_protocol_encoder_megacode_stop,void,void* Function,-,subghz_protocol_encoder_megacode_yield,LevelDuration,void* Function,-,subghz_protocol_encoder_nero_radio_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_nero_radio_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_nero_radio_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_encoder_nero_radio_free,void,void* Function,-,subghz_protocol_encoder_nero_radio_stop,void,void* Function,-,subghz_protocol_encoder_nero_radio_yield,LevelDuration,void* Function,-,subghz_protocol_encoder_nero_sketch_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_nero_sketch_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_nero_sketch_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_encoder_nero_sketch_free,void,void* Function,-,subghz_protocol_encoder_nero_sketch_stop,void,void* Function,-,subghz_protocol_encoder_nero_sketch_yield,LevelDuration,void* Function,-,subghz_protocol_encoder_nice_flo_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_nice_flo_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_nice_flo_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_encoder_nice_flo_free,void,void* Function,-,subghz_protocol_encoder_nice_flo_stop,void,void* Function,-,subghz_protocol_encoder_nice_flo_yield,LevelDuration,void* Function,-,subghz_protocol_encoder_nice_flor_s_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_nice_flor_s_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_nice_flor_s_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_encoder_nice_flor_s_free,void,void* Function,-,subghz_protocol_encoder_nice_flor_s_stop,void,void* Function,-,subghz_protocol_encoder_nice_flor_s_yield,LevelDuration,void* Function,-,subghz_protocol_encoder_phoenix_v2_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_phoenix_v2_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_phoenix_v2_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_encoder_phoenix_v2_free,void,void* Function,-,subghz_protocol_encoder_phoenix_v2_stop,void,void* Function,-,subghz_protocol_encoder_phoenix_v2_yield,LevelDuration,void* Function,-,subghz_protocol_encoder_power_smart_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_power_smart_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_power_smart_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_encoder_power_smart_free,void,void* Function,-,subghz_protocol_encoder_power_smart_stop,void,void* Function,-,subghz_protocol_encoder_power_smart_yield,LevelDuration,void* Function,+,subghz_protocol_encoder_princeton_alloc,void*,SubGhzEnvironment* -Function,+,subghz_protocol_encoder_princeton_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_princeton_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,+,subghz_protocol_encoder_princeton_free,void,void* Function,+,subghz_protocol_encoder_princeton_stop,void,void* Function,+,subghz_protocol_encoder_princeton_yield,LevelDuration,void* Function,+,subghz_protocol_encoder_raw_alloc,void*,SubGhzEnvironment* -Function,+,subghz_protocol_encoder_raw_deserialize,_Bool,"void*, FlipperFormat*" +Function,+,subghz_protocol_encoder_raw_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,+,subghz_protocol_encoder_raw_free,void,void* Function,+,subghz_protocol_encoder_raw_stop,void,void* Function,+,subghz_protocol_encoder_raw_yield,LevelDuration,void* Function,-,subghz_protocol_encoder_secplus_v1_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_secplus_v1_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_secplus_v1_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_encoder_secplus_v1_free,void,void* Function,-,subghz_protocol_encoder_secplus_v1_stop,void,void* Function,-,subghz_protocol_encoder_secplus_v1_yield,LevelDuration,void* Function,-,subghz_protocol_encoder_secplus_v2_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_secplus_v2_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_secplus_v2_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_encoder_secplus_v2_free,void,void* Function,-,subghz_protocol_encoder_secplus_v2_stop,void,void* Function,-,subghz_protocol_encoder_secplus_v2_yield,LevelDuration,void* Function,-,subghz_protocol_encoder_smc5326_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_smc5326_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_smc5326_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_encoder_smc5326_free,void,void* Function,-,subghz_protocol_encoder_smc5326_stop,void,void* Function,-,subghz_protocol_encoder_smc5326_yield,LevelDuration,void* Function,-,subghz_protocol_encoder_somfy_keytis_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_somfy_keytis_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_somfy_keytis_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_encoder_somfy_keytis_free,void,void* Function,-,subghz_protocol_encoder_somfy_keytis_stop,void,void* Function,-,subghz_protocol_encoder_somfy_keytis_yield,LevelDuration,void* Function,-,subghz_protocol_encoder_somfy_telis_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_somfy_telis_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_somfy_telis_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_encoder_somfy_telis_free,void,void* Function,-,subghz_protocol_encoder_somfy_telis_stop,void,void* Function,-,subghz_protocol_encoder_somfy_telis_yield,LevelDuration,void* Function,-,subghz_protocol_encoder_star_line_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_star_line_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_star_line_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_encoder_star_line_free,void,void* Function,-,subghz_protocol_encoder_star_line_stop,void,void* Function,-,subghz_protocol_encoder_star_line_yield,LevelDuration,void* Function,-,subghz_protocol_faac_slh_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint32_t, uint32_t, const char*, SubGhzRadioPreset*" Function,-,subghz_protocol_keeloq_bft_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, uint32_t, const char*, SubGhzRadioPreset*" Function,-,subghz_protocol_keeloq_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, const char*, SubGhzRadioPreset*" -Function,-,subghz_protocol_nice_flor_s_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, SubGhzRadioPreset*" +Function,-,subghz_protocol_nice_flor_s_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, SubGhzRadioPreset*, _Bool" Function,-,subghz_protocol_nice_flor_s_encrypt,uint64_t,"uint64_t, const char*" Function,+,subghz_protocol_raw_file_encoder_worker_set_callback_end,void,"SubGhzProtocolEncoderRAW*, SubGhzProtocolEncoderRAWCallbackEnd, void*" Function,+,subghz_protocol_raw_gen_fff_data,void,"FlipperFormat*, const char*" @@ -3300,7 +3363,7 @@ Function,+,subghz_setting_load,void,"SubGhzSetting*, const char*" Function,+,subghz_setting_load_custom_preset,_Bool,"SubGhzSetting*, const char*, FlipperFormat*" Function,+,subghz_setting_set_default_frequency,void,"SubGhzSetting*, uint32_t" Function,+,subghz_transmitter_alloc_init,SubGhzTransmitter*,"SubGhzEnvironment*, const char*" -Function,+,subghz_transmitter_deserialize,_Bool,"SubGhzTransmitter*, FlipperFormat*" +Function,+,subghz_transmitter_deserialize,SubGhzProtocolStatus,"SubGhzTransmitter*, FlipperFormat*" Function,+,subghz_transmitter_free,void,SubGhzTransmitter* Function,+,subghz_transmitter_get_protocol_instance,SubGhzProtocolEncoderBase*,SubGhzTransmitter* Function,+,subghz_transmitter_stop,_Bool,SubGhzTransmitter* @@ -3427,6 +3490,7 @@ Function,-,u8g2_DrawUTF8Lines,u8g2_uint_t,"u8g2_t*, u8g2_uint_t, u8g2_uint_t, u8 Function,-,u8g2_DrawVLine,void,"u8g2_t*, u8g2_uint_t, u8g2_uint_t, u8g2_uint_t" Function,-,u8g2_DrawXBM,void,"u8g2_t*, u8g2_uint_t, u8g2_uint_t, u8g2_uint_t, u8g2_uint_t, const uint8_t*" Function,-,u8g2_DrawXBMP,void,"u8g2_t*, u8g2_uint_t, u8g2_uint_t, u8g2_uint_t, u8g2_uint_t, const uint8_t*" +Function,-,u8g2_FillBuffer,void,u8g2_t* Function,-,u8g2_FirstPage,void,u8g2_t* Function,-,u8g2_GetFontSize,size_t,const uint8_t* Function,-,u8g2_GetGlyphWidth,int8_t,"u8g2_t*, uint16_t" @@ -4483,7 +4547,7 @@ Function,-,uxStreamBufferGetStreamBufferNumber,UBaseType_t,StreamBufferHandle_t Function,-,uxTaskGetNumberOfTasks,UBaseType_t, Function,-,uxTaskGetStackHighWaterMark,UBaseType_t,TaskHandle_t Function,-,uxTaskGetStackHighWaterMark2,uint16_t,TaskHandle_t -Function,+,uxTaskGetSystemState,UBaseType_t,"TaskStatus_t*, const UBaseType_t, uint32_t*" +Function,-,uxTaskGetSystemState,UBaseType_t,"TaskStatus_t*, const UBaseType_t, uint32_t*" Function,-,uxTaskGetTaskNumber,UBaseType_t,TaskHandle_t Function,+,uxTaskPriorityGet,UBaseType_t,const TaskHandle_t Function,-,uxTaskPriorityGetFromISR,UBaseType_t,const TaskHandle_t @@ -4599,11 +4663,11 @@ Function,+,view_port_alloc,ViewPort*, Function,+,view_port_draw_callback_set,void,"ViewPort*, ViewPortDrawCallback, void*" Function,+,view_port_enabled_set,void,"ViewPort*, _Bool" Function,+,view_port_free,void,ViewPort* -Function,+,view_port_get_height,uint8_t,ViewPort* +Function,+,view_port_get_height,uint8_t,const ViewPort* Function,+,view_port_get_orientation,ViewPortOrientation,const ViewPort* -Function,+,view_port_get_width,uint8_t,ViewPort* +Function,+,view_port_get_width,uint8_t,const ViewPort* Function,+,view_port_input_callback_set,void,"ViewPort*, ViewPortInputCallback, void*" -Function,+,view_port_is_enabled,_Bool,ViewPort* +Function,+,view_port_is_enabled,_Bool,const ViewPort* Function,+,view_port_set_height,void,"ViewPort*, uint8_t" Function,+,view_port_set_orientation,void,"ViewPort*, ViewPortOrientation" Function,+,view_port_set_width,void,"ViewPort*, uint8_t" @@ -4647,7 +4711,6 @@ Function,+,widget_alloc,Widget*, Function,+,widget_free,void,Widget* Function,+,widget_get_view,View*,Widget* Function,+,widget_reset,void,Widget* -Function,-,write_mutex,_Bool,"ValueMutex*, void*, size_t, uint32_t" Function,-,xPortGetFreeHeapSize,size_t, Function,-,xPortGetMinimumEverFreeHeapSize,size_t, Function,-,xPortStartScheduler,BaseType_t, diff --git a/firmware/targets/f7/ble_glue/gap.c b/firmware/targets/f7/ble_glue/gap.c index 83944b4b5..ebf27b369 100644 --- a/firmware/targets/f7/ble_glue/gap.c +++ b/firmware/targets/f7/ble_glue/gap.c @@ -373,6 +373,7 @@ static void gap_init_svc(Gap* gap) { bool keypress_supported = false; uint8_t conf_mitm = CFG_MITM_PROTECTION; uint8_t conf_used_fixed_pin = CFG_USED_FIXED_PIN; + bool conf_bonding = gap->config->bonding_mode; if(gap->config->pairing_method == GapPairingPinCodeShow) { aci_gap_set_io_capability(IO_CAP_DISPLAY_ONLY); } else if(gap->config->pairing_method == GapPairingPinCodeVerifyYesNo) { @@ -382,6 +383,7 @@ static void gap_init_svc(Gap* gap) { // Just works pairing method (IOS accept it, it seems android and linux doesn't) conf_mitm = 0; conf_used_fixed_pin = 0; + conf_bonding = false; // if just works isn't supported, we want the numeric comparaison method aci_gap_set_io_capability(IO_CAP_DISPLAY_YES_NO); keypress_supported = true; @@ -389,7 +391,7 @@ static void gap_init_svc(Gap* gap) { // Setup authentication aci_gap_set_authentication_requirement( - gap->config->bonding_mode, + conf_bonding, conf_mitm, CFG_SC_SUPPORT, keypress_supported, @@ -515,16 +517,16 @@ bool gap_init(GapConfig* config, GapEventCallback on_event_cb, void* context) { gap->advertise_timer = furi_timer_alloc(gap_advetise_timer_callback, FuriTimerTypeOnce, NULL); // Initialization of GATT & GAP layer gap->service.adv_name = config->adv_name; - FURI_LOG_I(TAG, "Advertising name: %s", &(gap->service.adv_name[1])); - FURI_LOG_I( + FURI_LOG_D(TAG, "Advertising name: %s", &(gap->service.adv_name[1])); + FURI_LOG_D( TAG, "MAC @ : %02X:%02X:%02X:%02X:%02X:%02X", - config->mac_address[0], - config->mac_address[1], - config->mac_address[2], - config->mac_address[3], + config->mac_address[5], config->mac_address[4], - config->mac_address[5]); + config->mac_address[3], + config->mac_address[2], + config->mac_address[1], + config->mac_address[0]); gap_init_svc(gap); // Initialization of the BLE Services SVCCTL_Init(); diff --git a/firmware/targets/f7/fatfs/sd_spi_io.c b/firmware/targets/f7/fatfs/sd_spi_io.c index 93b837e85..e8e542b32 100644 --- a/firmware/targets/f7/fatfs/sd_spi_io.c +++ b/firmware/targets/f7/fatfs/sd_spi_io.c @@ -17,7 +17,6 @@ #define SD_DUMMY_BYTE 0xFF #define SD_ANSWER_RETRY_COUNT 8 #define SD_IDLE_RETRY_COUNT 100 -#define SD_BLOCK_SIZE 512 #define FLAG_SET(x, y) (((x) & (y)) == (y)) @@ -586,6 +585,8 @@ static SdSpiStatus sd_spi_get_cid(SD_CID* Cid) { Cid->ProdSN |= cid_data[12]; Cid->Reserved1 = (cid_data[13] & 0xF0) >> 4; Cid->ManufactYear = (cid_data[13] & 0x0F) << 4; + Cid->ManufactYear |= (cid_data[14] & 0xF0) >> 4; + Cid->ManufactMonth = (cid_data[14] & 0x0F); Cid->CID_CRC = (cid_data[15] & 0xFE) >> 1; Cid->Reserved2 = 1; @@ -598,23 +599,6 @@ static SdSpiStatus sd_spi_get_cid(SD_CID* Cid) { return ret; } -static inline bool sd_cache_get(uint32_t address, uint32_t* data) { - uint8_t* cached_data = sector_cache_get(address); - if(cached_data) { - memcpy(data, cached_data, SD_BLOCK_SIZE); - return true; - } - return false; -} - -static inline void sd_cache_put(uint32_t address, uint32_t* data) { - sector_cache_put(address, (uint8_t*)data); -} - -static inline void sd_cache_invalidate_range(uint32_t start_sector, uint32_t end_sector) { - sector_cache_invalidate_range(start_sector, end_sector); -} - static SdSpiStatus sd_spi_cmd_read_blocks(uint32_t* data, uint32_t address, uint32_t blocks, uint32_t timeout_ms) { uint32_t block_address = address; @@ -833,30 +817,12 @@ SdSpiStatus sd_get_card_info(SD_CardInfo* card_info) { SdSpiStatus sd_read_blocks(uint32_t* data, uint32_t address, uint32_t blocks, uint32_t timeout_ms) { - SdSpiStatus status = SdSpiStatusError; - - bool single_sector_read = (blocks == 1); - - if(single_sector_read) { - if(sd_cache_get(address, data)) { - return SdSpiStatusOK; - } - - status = sd_spi_cmd_read_blocks(data, address, blocks, timeout_ms); - - if(status == SdSpiStatusOK) { - sd_cache_put(address, data); - } - } else { - status = sd_spi_cmd_read_blocks(data, address, blocks, timeout_ms); - } - + SdSpiStatus status = sd_spi_cmd_read_blocks(data, address, blocks, timeout_ms); return status; } SdSpiStatus sd_write_blocks(uint32_t* data, uint32_t address, uint32_t blocks, uint32_t timeout_ms) { - sd_cache_invalidate_range(address, address + blocks); SdSpiStatus status = sd_spi_cmd_write_blocks(data, address, blocks, timeout_ms); return status; } diff --git a/firmware/targets/f7/fatfs/sd_spi_io.h b/firmware/targets/f7/fatfs/sd_spi_io.h index 8850eceb7..954c78c40 100644 --- a/firmware/targets/f7/fatfs/sd_spi_io.h +++ b/firmware/targets/f7/fatfs/sd_spi_io.h @@ -5,6 +5,7 @@ #define __IO volatile #define SD_TIMEOUT_MS (1000) +#define SD_BLOCK_SIZE 512 typedef enum { SdSpiStatusOK, diff --git a/firmware/targets/f7/fatfs/sector_cache.c b/firmware/targets/f7/fatfs/sector_cache.c index d23c1d5ad..efb520ec8 100644 --- a/firmware/targets/f7/fatfs/sector_cache.c +++ b/firmware/targets/f7/fatfs/sector_cache.c @@ -8,7 +8,6 @@ #define SECTOR_SIZE 512 #define N_SECTORS 8 -#define TAG "SDCache" typedef struct { uint32_t itr; @@ -20,15 +19,11 @@ static SectorCache* cache = NULL; void sector_cache_init() { if(cache == NULL) { - // TODO: tuneup allocation order, to place cache in mem pool (MEM2) cache = memmgr_alloc_from_pool(sizeof(SectorCache)); } if(cache != NULL) { - FURI_LOG_I(TAG, "Init"); memset(cache, 0, sizeof(SectorCache)); - } else { - FURI_LOG_E(TAG, "Init failed"); } } diff --git a/firmware/targets/f7/fatfs/spi_sd_hal.c b/firmware/targets/f7/fatfs/spi_sd_hal.c deleted file mode 100644 index bfe046b58..000000000 --- a/firmware/targets/f7/fatfs/spi_sd_hal.c +++ /dev/null @@ -1,98 +0,0 @@ -#include -#include - -#define SD_DUMMY_BYTE 0xFF - -const uint32_t SpiTimeout = 1000; -uint8_t SD_IO_WriteByte(uint8_t Data); - -/****************************************************************************** - BUS OPERATIONS - *******************************************************************************/ - -/** - * @brief SPI Write byte(s) to device - * @param DataIn: Pointer to data buffer to write - * @param DataOut: Pointer to data buffer for read data - * @param DataLength: number of bytes to write - * @retval None - */ -static void SPIx_WriteReadData(const uint8_t* DataIn, uint8_t* DataOut, uint16_t DataLength) { - furi_check(furi_hal_spi_bus_trx( - furi_hal_sd_spi_handle, (uint8_t*)DataIn, DataOut, DataLength, SpiTimeout)); -} - -/** - * @brief SPI Write a byte to device - * @param Value: value to be written - * @retval None - */ -__attribute__((unused)) static void SPIx_Write(uint8_t Value) { - furi_check(furi_hal_spi_bus_tx(furi_hal_sd_spi_handle, (uint8_t*)&Value, 1, SpiTimeout)); -} - -/****************************************************************************** - LINK OPERATIONS - *******************************************************************************/ - -/********************************* LINK SD ************************************/ -/** - * @brief Initialize the SD Card and put it into StandBy State (Ready for - * data transfer). - * @retval None - */ -void SD_IO_Init(void) { - uint8_t counter = 0; - - /* SD chip select high */ - furi_hal_gpio_write(furi_hal_sd_spi_handle->cs, true); - furi_delay_us(10); - - /* Send dummy byte 0xFF, 10 times with CS high */ - /* Rise CS and MOSI for 80 clocks cycles */ - for(counter = 0; counter <= 200; counter++) { - /* Send dummy byte 0xFF */ - SD_IO_WriteByte(SD_DUMMY_BYTE); - } -} - -/** - * @brief Set SD interface Chip Select state - * @param val: 0 (low) or 1 (high) state - * @retval None - */ -void SD_IO_CSState(uint8_t val) { - /* Some SD Cards are prone to fail if CLK-ed too soon after CS transition. Worst case found: 8us */ - if(val == 1) { - furi_delay_us(10); // Exit guard time for some SD cards - furi_hal_gpio_write(furi_hal_sd_spi_handle->cs, true); - } else { - furi_hal_gpio_write(furi_hal_sd_spi_handle->cs, false); - furi_delay_us(10); // Entry guard time for some SD cards - } -} - -/** - * @brief Write byte(s) on the SD - * @param DataIn: Pointer to data buffer to write - * @param DataOut: Pointer to data buffer for read data - * @param DataLength: number of bytes to write - * @retval None - */ -void SD_IO_WriteReadData(const uint8_t* DataIn, uint8_t* DataOut, uint16_t DataLength) { - /* Send the byte */ - SPIx_WriteReadData(DataIn, DataOut, DataLength); -} - -/** - * @brief Write a byte on the SD. - * @param Data: byte to send. - * @retval Data written - */ -uint8_t SD_IO_WriteByte(uint8_t Data) { - uint8_t tmp; - - /* Send the byte */ - SPIx_WriteReadData(&Data, &tmp, 1); - return tmp; -} diff --git a/firmware/targets/f7/fatfs/stm32_adafruit_sd.c b/firmware/targets/f7/fatfs/stm32_adafruit_sd.c deleted file mode 100644 index 998adee29..000000000 --- a/firmware/targets/f7/fatfs/stm32_adafruit_sd.c +++ /dev/null @@ -1,1113 +0,0 @@ -/** - ****************************************************************************** - * @file stm32_adafruit_sd.c - * @author MCD Application Team - * @version V3.0.0 - * @date 23-December-2016 - * @brief This file provides a set of functions needed to manage the SD card - * mounted on the Adafruit 1.8" TFT LCD shield (reference ID 802), - * that is used with the STM32 Nucleo board through SPI interface. - * It implements a high level communication layer for read and write - * from/to this memory. The needed STM32XXxx hardware resources (SPI and - * GPIO) are defined in stm32XXxx_nucleo.h file, and the initialization is - * performed in SD_IO_Init() function declared in stm32XXxx_nucleo.c - * file. - * You can easily tailor this driver to any other development board, - * by just adapting the defines for hardware resources and - * SD_IO_Init() function. - * - * +-------------------------------------------------------+ - * | Pin assignment | - * +-------------------------+---------------+-------------+ - * | STM32XXxx SPI Pins | SD | Pin | - * +-------------------------+---------------+-------------+ - * | SD_SPI_CS_PIN | ChipSelect | 1 | - * | SD_SPI_MOSI_PIN / MOSI | DataIn | 2 | - * | | GND | 3 (0 V) | - * | | VDD | 4 (3.3 V)| - * | SD_SPI_SCK_PIN / SCLK | Clock | 5 | - * | | GND | 6 (0 V) | - * | SD_SPI_MISO_PIN / MISO | DataOut | 7 | - * +-------------------------+---------------+-------------+ - ****************************************************************************** - * @attention - * - *

© COPYRIGHT(c) 2016 STMicroelectronics

- * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of STMicroelectronics nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - ****************************************************************************** - */ - -/* File Info : ----------------------------------------------------------------- - User NOTES -1. How to use this driver: --------------------------- - - This driver does not need a specific component driver for the micro SD device - to be included with. - -2. Driver description: ---------------------- - + Initialization steps: - o Initialize the micro SD card using the BSP_SD_Init() function. - o Checking the SD card presence is not managed because SD detection pin is - not physically mapped on the Adafruit shield. - o The function BSP_SD_GetCardInfo() is used to get the micro SD card information - which is stored in the structure "SD_CardInfo". - - + Micro SD card operations - o The micro SD card can be accessed with read/write block(s) operations once - it is ready for access. The access can be performed in polling - mode by calling the functions BSP_SD_ReadBlocks()/BSP_SD_WriteBlocks() - - o The SD erase block(s) is performed using the function BSP_SD_Erase() with - specifying the number of blocks to erase. - o The SD runtime status is returned when calling the function BSP_SD_GetStatus(). - -------------------------------------------------------------------------------*/ - -/* Includes ------------------------------------------------------------------*/ -#include "stm32_adafruit_sd.h" -#include "stdlib.h" -#include "string.h" -#include "stdio.h" -#include -#include "sector_cache.h" - -/** @addtogroup BSP - * @{ - */ - -/** @addtogroup STM32_ADAFRUIT - * @{ - */ - -/** @defgroup STM32_ADAFRUIT_SD - * @{ - */ - -/* Private typedef -----------------------------------------------------------*/ - -/** @defgroup STM32_ADAFRUIT_SD_Private_Types_Definitions - * @{ - */ -typedef struct { - uint8_t r1; - uint8_t r2; - uint8_t r3; - uint8_t r4; - uint8_t r5; -} SD_CmdAnswer_typedef; - -/** - * @} - */ - -/* Private define ------------------------------------------------------------*/ - -/** @defgroup STM32_ADAFRUIT_SD_Private_Defines - * @{ - */ -#define SD_DUMMY_BYTE 0xFF - -#define SD_MAX_FRAME_LENGTH 17 /* Lenght = 16 + 1 */ -#define SD_CMD_LENGTH 6 - -#define SD_MAX_TRY 100 /* Number of try */ - -#define SD_CSD_STRUCT_V1 0x2 /* CSD struct version V1 */ -#define SD_CSD_STRUCT_V2 0x1 /* CSD struct version V2 */ - -/** - * @brief SD ansewer format - */ -typedef enum { - SD_ANSWER_R1_EXPECTED, - SD_ANSWER_R1B_EXPECTED, - SD_ANSWER_R2_EXPECTED, - SD_ANSWER_R3_EXPECTED, - SD_ANSWER_R4R5_EXPECTED, - SD_ANSWER_R7_EXPECTED, -} SD_Answer_type; - -/** - * @brief Start Data tokens: - * Tokens (necessary because at nop/idle (and CS active) only 0xff is - * on the data/command line) - */ -#define SD_TOKEN_START_DATA_SINGLE_BLOCK_READ \ - 0xFE /* Data token start byte, Start Single Block Read */ -#define SD_TOKEN_START_DATA_MULTIPLE_BLOCK_READ \ - 0xFE /* Data token start byte, Start Multiple Block Read */ -#define SD_TOKEN_START_DATA_SINGLE_BLOCK_WRITE \ - 0xFE /* Data token start byte, Start Single Block Write */ -#define SD_TOKEN_START_DATA_MULTIPLE_BLOCK_WRITE \ - 0xFD /* Data token start byte, Start Multiple Block Write */ -#define SD_TOKEN_STOP_DATA_MULTIPLE_BLOCK_WRITE \ - 0xFD /* Data toke stop byte, Stop Multiple Block Write */ - -/** - * @brief Commands: CMDxx = CMD-number | 0x40 - */ -#define SD_CMD_GO_IDLE_STATE 0 /* CMD0 = 0x40 */ -#define SD_CMD_SEND_OP_COND 1 /* CMD1 = 0x41 */ -#define SD_CMD_SEND_IF_COND 8 /* CMD8 = 0x48 */ -#define SD_CMD_SEND_CSD 9 /* CMD9 = 0x49 */ -#define SD_CMD_SEND_CID 10 /* CMD10 = 0x4A */ -#define SD_CMD_STOP_TRANSMISSION 12 /* CMD12 = 0x4C */ -#define SD_CMD_SEND_STATUS 13 /* CMD13 = 0x4D */ -#define SD_CMD_SET_BLOCKLEN 16 /* CMD16 = 0x50 */ -#define SD_CMD_READ_SINGLE_BLOCK 17 /* CMD17 = 0x51 */ -#define SD_CMD_READ_MULT_BLOCK 18 /* CMD18 = 0x52 */ -#define SD_CMD_SET_BLOCK_COUNT 23 /* CMD23 = 0x57 */ -#define SD_CMD_WRITE_SINGLE_BLOCK 24 /* CMD24 = 0x58 */ -#define SD_CMD_WRITE_MULT_BLOCK 25 /* CMD25 = 0x59 */ -#define SD_CMD_PROG_CSD 27 /* CMD27 = 0x5B */ -#define SD_CMD_SET_WRITE_PROT 28 /* CMD28 = 0x5C */ -#define SD_CMD_CLR_WRITE_PROT 29 /* CMD29 = 0x5D */ -#define SD_CMD_SEND_WRITE_PROT 30 /* CMD30 = 0x5E */ -#define SD_CMD_SD_ERASE_GRP_START 32 /* CMD32 = 0x60 */ -#define SD_CMD_SD_ERASE_GRP_END 33 /* CMD33 = 0x61 */ -#define SD_CMD_UNTAG_SECTOR 34 /* CMD34 = 0x62 */ -#define SD_CMD_ERASE_GRP_START 35 /* CMD35 = 0x63 */ -#define SD_CMD_ERASE_GRP_END 36 /* CMD36 = 0x64 */ -#define SD_CMD_UNTAG_ERASE_GROUP 37 /* CMD37 = 0x65 */ -#define SD_CMD_ERASE 38 /* CMD38 = 0x66 */ -#define SD_CMD_SD_APP_OP_COND 41 /* CMD41 = 0x69 */ -#define SD_CMD_APP_CMD 55 /* CMD55 = 0x77 */ -#define SD_CMD_READ_OCR 58 /* CMD55 = 0x79 */ - -/** - * @brief SD reponses and error flags - */ -typedef enum { - /* R1 answer value */ - SD_R1_NO_ERROR = (0x00), - SD_R1_IN_IDLE_STATE = (0x01), - SD_R1_ERASE_RESET = (0x02), - SD_R1_ILLEGAL_COMMAND = (0x04), - SD_R1_COM_CRC_ERROR = (0x08), - SD_R1_ERASE_SEQUENCE_ERROR = (0x10), - SD_R1_ADDRESS_ERROR = (0x20), - SD_R1_PARAMETER_ERROR = (0x40), - - /* R2 answer value */ - SD_R2_NO_ERROR = 0x00, - SD_R2_CARD_LOCKED = 0x01, - SD_R2_LOCKUNLOCK_ERROR = 0x02, - SD_R2_ERROR = 0x04, - SD_R2_CC_ERROR = 0x08, - SD_R2_CARD_ECC_FAILED = 0x10, - SD_R2_WP_VIOLATION = 0x20, - SD_R2_ERASE_PARAM = 0x40, - SD_R2_OUTOFRANGE = 0x80, - - /** - * @brief Data response error - */ - SD_DATA_OK = (0x05), - SD_DATA_CRC_ERROR = (0x0B), - SD_DATA_WRITE_ERROR = (0x0D), - SD_DATA_OTHER_ERROR = (0xFF) -} SD_Error; - -/** - * @} - */ - -/* Private macro -------------------------------------------------------------*/ - -/** @defgroup STM32_ADAFRUIT_SD_Private_Macros - * @{ - */ - -/** - * @} - */ - -/* Private variables ---------------------------------------------------------*/ - -/** @defgroup STM32_ADAFRUIT_SD_Private_Variables - * @{ - */ -__IO uint8_t SdStatus = SD_NOT_PRESENT; - -/* flag_SDHC : - 0 : Standard capacity - 1 : High capacity -*/ -uint16_t flag_SDHC = 0; - -/** - * @} - */ - -/* Private function prototypes -----------------------------------------------*/ -static uint8_t SD_GetCIDRegister(SD_CID* Cid); -static uint8_t SD_GetCSDRegister(SD_CSD* Csd); -static uint8_t SD_GetDataResponse(void); -static uint8_t SD_GoIdleState(void); -static SD_CmdAnswer_typedef SD_SendCmd(uint8_t Cmd, uint32_t Arg, uint8_t Crc, uint8_t Answer); -static uint8_t SD_WaitData(uint8_t data); -static uint8_t SD_ReadData(void); -/** @defgroup STM32_ADAFRUIT_SD_Private_Function_Prototypes - * @{ - */ -/** - * @} - */ - -/* Private functions ---------------------------------------------------------*/ - -void SD_SPI_Bus_To_Down_State() { - furi_hal_gpio_init_ex( - furi_hal_sd_spi_handle->miso, - GpioModeOutputPushPull, - GpioPullNo, - GpioSpeedVeryHigh, - GpioAltFnUnused); - furi_hal_gpio_init_ex( - furi_hal_sd_spi_handle->mosi, - GpioModeOutputPushPull, - GpioPullNo, - GpioSpeedVeryHigh, - GpioAltFnUnused); - furi_hal_gpio_init_ex( - furi_hal_sd_spi_handle->sck, - GpioModeOutputPushPull, - GpioPullNo, - GpioSpeedVeryHigh, - GpioAltFnUnused); - - furi_hal_gpio_write(furi_hal_sd_spi_handle->cs, false); - furi_hal_gpio_write(furi_hal_sd_spi_handle->miso, false); - furi_hal_gpio_write(furi_hal_sd_spi_handle->mosi, false); - furi_hal_gpio_write(furi_hal_sd_spi_handle->sck, false); -} - -void SD_SPI_Bus_To_Normal_State() { - furi_hal_gpio_write(furi_hal_sd_spi_handle->cs, true); - - furi_hal_gpio_init_ex( - furi_hal_sd_spi_handle->miso, - GpioModeAltFunctionPushPull, - GpioPullUp, - GpioSpeedVeryHigh, - GpioAltFn5SPI2); - furi_hal_gpio_init_ex( - furi_hal_sd_spi_handle->mosi, - GpioModeAltFunctionPushPull, - GpioPullUp, - GpioSpeedVeryHigh, - GpioAltFn5SPI2); - furi_hal_gpio_init_ex( - furi_hal_sd_spi_handle->sck, - GpioModeAltFunctionPushPull, - GpioPullUp, - GpioSpeedVeryHigh, - GpioAltFn5SPI2); -} - -/** @defgroup STM32_ADAFRUIT_SD_Private_Functions - * @{ - */ - -uint8_t BSP_SD_MaxMountRetryCount() { - return 10; -} - -/** - * @brief Initializes the SD/SD communication. - * @param None - * @retval The SD Response: - * - MSD_ERROR: Sequence failed - * - MSD_OK: Sequence succeed - */ -uint8_t BSP_SD_Init(bool reset_card) { - /* Slow speed init */ - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_slow); - furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_slow; - - /* We must reset card in spi_lock context */ - if(reset_card) { - /* disable power and set low on all bus pins */ - furi_hal_power_disable_external_3_3v(); - SD_SPI_Bus_To_Down_State(); - hal_sd_detect_set_low(); - furi_delay_ms(250); - - /* reinit bus and enable power */ - SD_SPI_Bus_To_Normal_State(); - hal_sd_detect_init(); - furi_hal_power_enable_external_3_3v(); - furi_delay_ms(100); - } - - /* Configure IO functionalities for SD pin */ - SD_IO_Init(); - - /* SD detection pin is not physically mapped on the Adafruit shield */ - SdStatus = SD_PRESENT; - uint8_t res = BSP_SD_ERROR; - - for(uint8_t i = 0; i < 128; i++) { - res = SD_GoIdleState(); - if(res == BSP_SD_OK) break; - } - - furi_hal_sd_spi_handle = NULL; - furi_hal_spi_release(&furi_hal_spi_bus_handle_sd_slow); - - sector_cache_init(); - - /* SD initialized and set to SPI mode properly */ - return res; -} - -/** - * @brief Returns information about specific card. - * @param pCardInfo: Pointer to a SD_CardInfo structure that contains all SD - * card information. - * @retval The SD Response: - * - MSD_ERROR: Sequence failed - * - MSD_OK: Sequence succeed - */ -uint8_t BSP_SD_GetCardInfo(SD_CardInfo* pCardInfo) { - uint8_t status; - - status = SD_GetCSDRegister(&(pCardInfo->Csd)); - status |= SD_GetCIDRegister(&(pCardInfo->Cid)); - if(flag_SDHC == 1) { - pCardInfo->LogBlockSize = 512; - pCardInfo->CardBlockSize = 512; - pCardInfo->CardCapacity = ((uint64_t)pCardInfo->Csd.version.v2.DeviceSize + 1UL) * 1024UL * - (uint64_t)pCardInfo->LogBlockSize; - pCardInfo->LogBlockNbr = (pCardInfo->CardCapacity) / (pCardInfo->LogBlockSize); - } else { - pCardInfo->CardCapacity = (pCardInfo->Csd.version.v1.DeviceSize + 1); - pCardInfo->CardCapacity *= (1UL << (pCardInfo->Csd.version.v1.DeviceSizeMul + 2)); - pCardInfo->LogBlockSize = 512; - pCardInfo->CardBlockSize = 1UL << (pCardInfo->Csd.RdBlockLen); - pCardInfo->CardCapacity *= pCardInfo->CardBlockSize; - pCardInfo->LogBlockNbr = (pCardInfo->CardCapacity) / (pCardInfo->LogBlockSize); - } - - return status; -} - -/** - * @brief Reads block(s) from a specified address in the SD card, in polling mode. - * @param pData: Pointer to the buffer that will contain the data to transmit - * @param ReadAddr: Address from where data is to be read. The address is counted - * in blocks of 512bytes - * @param NumOfBlocks: Number of SD blocks to read - * @param Timeout: This parameter is used for compatibility with BSP implementation - * @retval SD status - */ -uint8_t - BSP_SD_ReadBlocks(uint32_t* pData, uint32_t ReadAddr, uint32_t NumOfBlocks, uint32_t Timeout) { - UNUSED(Timeout); // FIXME! - uint32_t offset = 0; - uint32_t addr; - uint8_t retr = BSP_SD_ERROR; - SD_CmdAnswer_typedef response; - uint16_t BlockSize = 512; - uint8_t* cached_data; - - bool single_sector_read = (NumOfBlocks == 1); - if(single_sector_read && (cached_data = sector_cache_get(ReadAddr))) { - memcpy(pData, cached_data, BlockSize); - return BSP_SD_OK; - } - - /* Send CMD16 (SD_CMD_SET_BLOCKLEN) to set the size of the block and - Check if the SD acknowledged the set block length command: R1 response (0x00: no errors) */ - response = SD_SendCmd(SD_CMD_SET_BLOCKLEN, BlockSize, 0xFF, SD_ANSWER_R1_EXPECTED); - SD_IO_CSState(1); - SD_IO_WriteByte(SD_DUMMY_BYTE); - if(response.r1 != SD_R1_NO_ERROR) { - goto error; - } - - /* Initialize the address */ - addr = (ReadAddr * ((flag_SDHC == 1) ? 1 : BlockSize)); - - /* Data transfer */ - while(NumOfBlocks--) { - /* Send CMD17 (SD_CMD_READ_SINGLE_BLOCK) to read one block */ - /* Check if the SD acknowledged the read block command: R1 response (0x00: no errors) */ - response = SD_SendCmd(SD_CMD_READ_SINGLE_BLOCK, addr, 0xFF, SD_ANSWER_R1_EXPECTED); - if(response.r1 != SD_R1_NO_ERROR) { - goto error; - } - - /* Now look for the data token to signify the start of the data */ - if(SD_WaitData(SD_TOKEN_START_DATA_SINGLE_BLOCK_READ) == BSP_SD_OK) { - /* Read the SD block data : read NumByteToRead data */ - SD_IO_WriteReadData(NULL, (uint8_t*)pData + offset, BlockSize); - - /* Set next read address*/ - offset += BlockSize; - addr = ((flag_SDHC == 1) ? (addr + 1) : (addr + BlockSize)); - - /* get CRC bytes (not really needed by us, but required by SD) */ - SD_IO_WriteByte(SD_DUMMY_BYTE); - SD_IO_WriteByte(SD_DUMMY_BYTE); - } else { - goto error; - } - - /* End the command data read cycle */ - SD_IO_CSState(1); - SD_IO_WriteByte(SD_DUMMY_BYTE); - } - - if(single_sector_read) { - sector_cache_put(ReadAddr, (uint8_t*)pData); - } - - retr = BSP_SD_OK; - -error: - /* Send dummy byte: 8 Clock pulses of delay */ - SD_IO_CSState(1); - SD_IO_WriteByte(SD_DUMMY_BYTE); - - /* Return the reponse */ - return retr; -} - -/** - * @brief Writes block(s) to a specified address in the SD card, in polling mode. - * @param pData: Pointer to the buffer that will contain the data to transmit - * @param WriteAddr: Address from where data is to be written. The address is counted - * in blocks of 512bytes - * @param NumOfBlocks: Number of SD blocks to write - * @param Timeout: This parameter is used for compatibility with BSP implementation - * @retval SD status - */ -uint8_t BSP_SD_WriteBlocks( - uint32_t* pData, - uint32_t WriteAddr, - uint32_t NumOfBlocks, - uint32_t Timeout) { - UNUSED(Timeout); // FIXME! - uint32_t offset = 0; - uint32_t addr; - uint8_t retr = BSP_SD_ERROR; - SD_CmdAnswer_typedef response; - uint16_t BlockSize = 512; - sector_cache_invalidate_range(WriteAddr, WriteAddr + NumOfBlocks); - - /* Send CMD16 (SD_CMD_SET_BLOCKLEN) to set the size of the block and - Check if the SD acknowledged the set block length command: R1 response (0x00: no errors) */ - response = SD_SendCmd(SD_CMD_SET_BLOCKLEN, BlockSize, 0xFF, SD_ANSWER_R1_EXPECTED); - SD_IO_CSState(1); - SD_IO_WriteByte(SD_DUMMY_BYTE); - if(response.r1 != SD_R1_NO_ERROR) { - goto error; - } - - /* Initialize the address */ - addr = (WriteAddr * ((flag_SDHC == 1) ? 1 : BlockSize)); - - /* Data transfer */ - while(NumOfBlocks--) { - /* Send CMD24 (SD_CMD_WRITE_SINGLE_BLOCK) to write blocks and - Check if the SD acknowledged the write block command: R1 response (0x00: no errors) */ - response = SD_SendCmd(SD_CMD_WRITE_SINGLE_BLOCK, addr, 0xFF, SD_ANSWER_R1_EXPECTED); - if(response.r1 != SD_R1_NO_ERROR) { - goto error; - } - - /* Send dummy byte for NWR timing : one byte between CMDWRITE and TOKEN */ - SD_IO_WriteByte(SD_DUMMY_BYTE); - SD_IO_WriteByte(SD_DUMMY_BYTE); - - /* Send the data token to signify the start of the data */ - SD_IO_WriteByte(SD_TOKEN_START_DATA_SINGLE_BLOCK_WRITE); - - /* Write the block data to SD */ - SD_IO_WriteReadData((uint8_t*)pData + offset, NULL, BlockSize); - - /* Set next write address */ - offset += BlockSize; - addr = ((flag_SDHC == 1) ? (addr + 1) : (addr + BlockSize)); - - /* Put CRC bytes (not really needed by us, but required by SD) */ - SD_IO_WriteByte(SD_DUMMY_BYTE); - SD_IO_WriteByte(SD_DUMMY_BYTE); - - /* Read data response */ - if(SD_GetDataResponse() != SD_DATA_OK) { - /* Set response value to failure */ - goto error; - } - - SD_IO_CSState(1); - SD_IO_WriteByte(SD_DUMMY_BYTE); - } - retr = BSP_SD_OK; - -error: - - /* Send dummy byte: 8 Clock pulses of delay */ - SD_IO_CSState(1); - SD_IO_WriteByte(SD_DUMMY_BYTE); - - /* Return the reponse */ - return retr; -} - -/** - * @brief Erases the specified memory area of the given SD card. - * @param StartAddr: Start address in Blocks (Size of a block is 512bytes) - * @param EndAddr: End address in Blocks (Size of a block is 512bytes) - * @retval SD status - */ -uint8_t BSP_SD_Erase(uint32_t StartAddr, uint32_t EndAddr) { - uint8_t retr = BSP_SD_ERROR; - SD_CmdAnswer_typedef response; - uint16_t BlockSize = 512; - - /* Send CMD32 (Erase group start) and check if the SD acknowledged the erase command: R1 response (0x00: no errors) */ - response = SD_SendCmd( - SD_CMD_SD_ERASE_GRP_START, - (StartAddr) * (flag_SDHC == 1 ? 1 : BlockSize), - 0xFF, - SD_ANSWER_R1_EXPECTED); - SD_IO_CSState(1); - SD_IO_WriteByte(SD_DUMMY_BYTE); - if(response.r1 == SD_R1_NO_ERROR) { - /* Send CMD33 (Erase group end) and Check if the SD acknowledged the erase command: R1 response (0x00: no errors) */ - response = SD_SendCmd( - SD_CMD_SD_ERASE_GRP_END, - (EndAddr * 512) * (flag_SDHC == 1 ? 1 : BlockSize), - 0xFF, - SD_ANSWER_R1_EXPECTED); - SD_IO_CSState(1); - SD_IO_WriteByte(SD_DUMMY_BYTE); - if(response.r1 == SD_R1_NO_ERROR) { - /* Send CMD38 (Erase) and Check if the SD acknowledged the erase command: R1 response (0x00: no errors) */ - response = SD_SendCmd(SD_CMD_ERASE, 0, 0xFF, SD_ANSWER_R1B_EXPECTED); - if(response.r1 == SD_R1_NO_ERROR) { - retr = BSP_SD_OK; - } - SD_IO_CSState(1); - SD_IO_WriteByte(SD_DUMMY_BYTE); - } - } - - /* Return the reponse */ - return retr; -} - -/** - * @brief Returns the SD status. - * @param None - * @retval The SD status. - */ -uint8_t BSP_SD_GetCardState(void) { - SD_CmdAnswer_typedef retr; - - /* Send CMD13 (SD_SEND_STATUS) to get SD status */ - retr = SD_SendCmd(SD_CMD_SEND_STATUS, 0, 0xFF, SD_ANSWER_R2_EXPECTED); - SD_IO_CSState(1); - SD_IO_WriteByte(SD_DUMMY_BYTE); - - /* Find SD status according to card state */ - if((retr.r1 == SD_R1_NO_ERROR) && (retr.r2 == SD_R2_NO_ERROR)) { - return BSP_SD_OK; - } - - return BSP_SD_ERROR; -} - -/** - * @brief Reads the SD card SCD register. - * Reading the contents of the CSD register in SPI mode is a simple - * read-block transaction. - * @param Csd: pointer on an SCD register structure - * @retval SD status - */ -uint8_t SD_GetCSDRegister(SD_CSD* Csd) { - uint16_t counter = 0; - uint8_t CSD_Tab[16]; - uint8_t retr = BSP_SD_ERROR; - SD_CmdAnswer_typedef response; - - /* Send CMD9 (CSD register) or CMD10(CSD register) and Wait for response in the R1 format (0x00 is no errors) */ - response = SD_SendCmd(SD_CMD_SEND_CSD, 0, 0xFF, SD_ANSWER_R1_EXPECTED); - if(response.r1 == SD_R1_NO_ERROR) { - if(SD_WaitData(SD_TOKEN_START_DATA_SINGLE_BLOCK_READ) == BSP_SD_OK) { - for(counter = 0; counter < 16; counter++) { - /* Store CSD register value on CSD_Tab */ - CSD_Tab[counter] = SD_IO_WriteByte(SD_DUMMY_BYTE); - } - - /* Get CRC bytes (not really needed by us, but required by SD) */ - SD_IO_WriteByte(SD_DUMMY_BYTE); - SD_IO_WriteByte(SD_DUMMY_BYTE); - - /************************************************************************* - CSD header decoding - *************************************************************************/ - - /* Byte 0 */ - Csd->CSDStruct = (CSD_Tab[0] & 0xC0) >> 6; - Csd->Reserved1 = CSD_Tab[0] & 0x3F; - - /* Byte 1 */ - Csd->TAAC = CSD_Tab[1]; - - /* Byte 2 */ - Csd->NSAC = CSD_Tab[2]; - - /* Byte 3 */ - Csd->MaxBusClkFrec = CSD_Tab[3]; - - /* Byte 4/5 */ - Csd->CardComdClasses = (CSD_Tab[4] << 4) | ((CSD_Tab[5] & 0xF0) >> 4); - Csd->RdBlockLen = CSD_Tab[5] & 0x0F; - - /* Byte 6 */ - Csd->PartBlockRead = (CSD_Tab[6] & 0x80) >> 7; - Csd->WrBlockMisalign = (CSD_Tab[6] & 0x40) >> 6; - Csd->RdBlockMisalign = (CSD_Tab[6] & 0x20) >> 5; - Csd->DSRImpl = (CSD_Tab[6] & 0x10) >> 4; - - /************************************************************************* - CSD v1/v2 decoding - *************************************************************************/ - - if(flag_SDHC == 0) { - Csd->version.v1.Reserved1 = ((CSD_Tab[6] & 0x0C) >> 2); - - Csd->version.v1.DeviceSize = ((CSD_Tab[6] & 0x03) << 10) | (CSD_Tab[7] << 2) | - ((CSD_Tab[8] & 0xC0) >> 6); - Csd->version.v1.MaxRdCurrentVDDMin = (CSD_Tab[8] & 0x38) >> 3; - Csd->version.v1.MaxRdCurrentVDDMax = (CSD_Tab[8] & 0x07); - Csd->version.v1.MaxWrCurrentVDDMin = (CSD_Tab[9] & 0xE0) >> 5; - Csd->version.v1.MaxWrCurrentVDDMax = (CSD_Tab[9] & 0x1C) >> 2; - Csd->version.v1.DeviceSizeMul = ((CSD_Tab[9] & 0x03) << 1) | - ((CSD_Tab[10] & 0x80) >> 7); - } else { - Csd->version.v2.Reserved1 = ((CSD_Tab[6] & 0x0F) << 2) | - ((CSD_Tab[7] & 0xC0) >> 6); - Csd->version.v2.DeviceSize = ((CSD_Tab[7] & 0x3F) << 16) | (CSD_Tab[8] << 8) | - CSD_Tab[9]; - Csd->version.v2.Reserved2 = ((CSD_Tab[10] & 0x80) >> 8); - } - - Csd->EraseSingleBlockEnable = (CSD_Tab[10] & 0x40) >> 6; - Csd->EraseSectorSize = ((CSD_Tab[10] & 0x3F) << 1) | ((CSD_Tab[11] & 0x80) >> 7); - Csd->WrProtectGrSize = (CSD_Tab[11] & 0x7F); - Csd->WrProtectGrEnable = (CSD_Tab[12] & 0x80) >> 7; - Csd->Reserved2 = (CSD_Tab[12] & 0x60) >> 5; - Csd->WrSpeedFact = (CSD_Tab[12] & 0x1C) >> 2; - Csd->MaxWrBlockLen = ((CSD_Tab[12] & 0x03) << 2) | ((CSD_Tab[13] & 0xC0) >> 6); - Csd->WriteBlockPartial = (CSD_Tab[13] & 0x20) >> 5; - Csd->Reserved3 = (CSD_Tab[13] & 0x1F); - Csd->FileFormatGrouop = (CSD_Tab[14] & 0x80) >> 7; - Csd->CopyFlag = (CSD_Tab[14] & 0x40) >> 6; - Csd->PermWrProtect = (CSD_Tab[14] & 0x20) >> 5; - Csd->TempWrProtect = (CSD_Tab[14] & 0x10) >> 4; - Csd->FileFormat = (CSD_Tab[14] & 0x0C) >> 2; - Csd->Reserved4 = (CSD_Tab[14] & 0x03); - Csd->crc = (CSD_Tab[15] & 0xFE) >> 1; - Csd->Reserved5 = (CSD_Tab[15] & 0x01); - - retr = BSP_SD_OK; - } - } - - /* Send dummy byte: 8 Clock pulses of delay */ - SD_IO_CSState(1); - SD_IO_WriteByte(SD_DUMMY_BYTE); - - /* Return the reponse */ - return retr; -} - -/** - * @brief Reads the SD card CID register. - * Reading the contents of the CID register in SPI mode is a simple - * read-block transaction. - * @param Cid: pointer on an CID register structure - * @retval SD status - */ -uint8_t SD_GetCIDRegister(SD_CID* Cid) { - uint32_t counter = 0; - uint8_t retr = BSP_SD_ERROR; - uint8_t CID_Tab[16]; - SD_CmdAnswer_typedef response; - - /* Send CMD10 (CID register) and Wait for response in the R1 format (0x00 is no errors) */ - response = SD_SendCmd(SD_CMD_SEND_CID, 0, 0xFF, SD_ANSWER_R1_EXPECTED); - if(response.r1 == SD_R1_NO_ERROR) { - if(SD_WaitData(SD_TOKEN_START_DATA_SINGLE_BLOCK_READ) == BSP_SD_OK) { - /* Store CID register value on CID_Tab */ - for(counter = 0; counter < 16; counter++) { - CID_Tab[counter] = SD_IO_WriteByte(SD_DUMMY_BYTE); - } - - /* Get CRC bytes (not really needed by us, but required by SD) */ - SD_IO_WriteByte(SD_DUMMY_BYTE); - SD_IO_WriteByte(SD_DUMMY_BYTE); - - /* Byte 0 */ - Cid->ManufacturerID = CID_Tab[0]; - - /* Byte 1 */ - memcpy(Cid->OEM_AppliID, CID_Tab + 1, 2); - - /* Byte 3 */ - memcpy(Cid->ProdName, CID_Tab + 3, 5); - - /* Byte 8 */ - Cid->ProdRev = CID_Tab[8]; - - /* Byte 9 */ - Cid->ProdSN = CID_Tab[9] << 24; - - /* Byte 10 */ - Cid->ProdSN |= CID_Tab[10] << 16; - - /* Byte 11 */ - Cid->ProdSN |= CID_Tab[11] << 8; - - /* Byte 12 */ - Cid->ProdSN |= CID_Tab[12]; - - /* Byte 13 */ - Cid->Reserved1 = (CID_Tab[13] & 0xF0) >> 4; - Cid->ManufactYear = (CID_Tab[13] & 0x0F) << 4; - - /* Byte 14 */ - Cid->ManufactYear |= (CID_Tab[14] & 0xF0) >> 4; - Cid->ManufactMonth = (CID_Tab[14] & 0x0F); - - /* Byte 15 */ - Cid->CID_CRC = (CID_Tab[15] & 0xFE) >> 1; - Cid->Reserved2 = 1; - - retr = BSP_SD_OK; - } - } - - /* Send dummy byte: 8 Clock pulses of delay */ - SD_IO_CSState(1); - SD_IO_WriteByte(SD_DUMMY_BYTE); - - /* Return the reponse */ - return retr; -} - -uint8_t BSP_SD_GetCIDRegister(SD_CID* Cid) { - uint8_t retr = BSP_SD_ERROR; - - /* Slow speed init */ - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_slow); - furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_slow; - - memset(Cid, 0, sizeof(SD_CID)); - retr = SD_GetCIDRegister(Cid); - - furi_hal_sd_spi_handle = NULL; - furi_hal_spi_release(&furi_hal_spi_bus_handle_sd_slow); - return retr; -} - -/** - * @brief Sends 5 bytes command to the SD card and get response - * @param Cmd: The user expected command to send to SD card. - * @param Arg: The command argument. - * @param Crc: The CRC. - * @param Answer: SD_ANSWER_NOT_EXPECTED or SD_ANSWER_EXPECTED - * @retval SD status - */ -SD_CmdAnswer_typedef SD_SendCmd(uint8_t Cmd, uint32_t Arg, uint8_t Crc, uint8_t Answer) { - uint8_t frame[SD_CMD_LENGTH], frameout[SD_CMD_LENGTH]; - SD_CmdAnswer_typedef retr = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; - - /* R1 Lenght = NCS(0)+ 6 Bytes command + NCR(min1 max8) + 1 Bytes answer + NEC(0) = 15bytes */ - /* R1b identical to R1 + Busy information */ - /* R2 Lenght = NCS(0)+ 6 Bytes command + NCR(min1 max8) + 2 Bytes answer + NEC(0) = 16bytes */ - - /* Prepare Frame to send */ - frame[0] = (Cmd | 0x40); /* Construct byte 1 */ - frame[1] = (uint8_t)(Arg >> 24); /* Construct byte 2 */ - frame[2] = (uint8_t)(Arg >> 16); /* Construct byte 3 */ - frame[3] = (uint8_t)(Arg >> 8); /* Construct byte 4 */ - frame[4] = (uint8_t)(Arg); /* Construct byte 5 */ - frame[5] = (Crc | 0x01); /* Construct byte 6 */ - - /* Send the command */ - SD_IO_CSState(0); - SD_IO_WriteReadData(frame, frameout, SD_CMD_LENGTH); /* Send the Cmd bytes */ - - switch(Answer) { - case SD_ANSWER_R1_EXPECTED: - retr.r1 = SD_ReadData(); - break; - case SD_ANSWER_R1B_EXPECTED: - retr.r1 = SD_ReadData(); - retr.r2 = SD_IO_WriteByte(SD_DUMMY_BYTE); - /* Set CS High */ - SD_IO_CSState(1); - furi_delay_us(1000); - /* Set CS Low */ - SD_IO_CSState(0); - - /* Wait IO line return 0xFF */ - while(SD_IO_WriteByte(SD_DUMMY_BYTE) != 0xFF) - ; - break; - case SD_ANSWER_R2_EXPECTED: - retr.r1 = SD_ReadData(); - retr.r2 = SD_IO_WriteByte(SD_DUMMY_BYTE); - break; - case SD_ANSWER_R3_EXPECTED: - case SD_ANSWER_R7_EXPECTED: - retr.r1 = SD_ReadData(); - retr.r2 = SD_IO_WriteByte(SD_DUMMY_BYTE); - retr.r3 = SD_IO_WriteByte(SD_DUMMY_BYTE); - retr.r4 = SD_IO_WriteByte(SD_DUMMY_BYTE); - retr.r5 = SD_IO_WriteByte(SD_DUMMY_BYTE); - break; - default: - break; - } - return retr; -} - -/** - * @brief Gets the SD card data response and check the busy flag. - * @param None - * @retval The SD status: Read data response xxx01 - * - status 010: Data accecpted - * - status 101: Data rejected due to a crc error - * - status 110: Data rejected due to a Write error. - * - status 111: Data rejected due to other error. - */ -uint8_t SD_GetDataResponse(void) { - uint8_t dataresponse; - uint8_t rvalue = SD_DATA_OTHER_ERROR; - - dataresponse = SD_IO_WriteByte(SD_DUMMY_BYTE); - SD_IO_WriteByte(SD_DUMMY_BYTE); /* read the busy response byte*/ - - /* Mask unused bits */ - switch(dataresponse & 0x1F) { - case SD_DATA_OK: - rvalue = SD_DATA_OK; - - /* Set CS High */ - SD_IO_CSState(1); - /* Set CS Low */ - SD_IO_CSState(0); - - /* Wait IO line return 0xFF */ - while(SD_IO_WriteByte(SD_DUMMY_BYTE) != 0xFF) - ; - break; - case SD_DATA_CRC_ERROR: - rvalue = SD_DATA_CRC_ERROR; - break; - case SD_DATA_WRITE_ERROR: - rvalue = SD_DATA_WRITE_ERROR; - break; - default: - break; - } - - /* Return response */ - return rvalue; -} - -/** - * @brief Put the SD in Idle state. - * @param None - * @retval SD status - */ -uint8_t SD_GoIdleState(void) { - SD_CmdAnswer_typedef response; - __IO uint8_t counter; - /* Send CMD0 (SD_CMD_GO_IDLE_STATE) to put SD in SPI mode and - wait for In Idle State Response (R1 Format) equal to 0x01 */ - counter = 0; - do { - counter++; - response = SD_SendCmd(SD_CMD_GO_IDLE_STATE, 0, 0x95, SD_ANSWER_R1_EXPECTED); - SD_IO_CSState(1); - SD_IO_WriteByte(SD_DUMMY_BYTE); - if(counter >= SD_MAX_TRY) { - return BSP_SD_ERROR; - } - } while(response.r1 != SD_R1_IN_IDLE_STATE); - - /* Send CMD8 (SD_CMD_SEND_IF_COND) to check the power supply status - and wait until response (R7 Format) equal to 0xAA and */ - response = SD_SendCmd(SD_CMD_SEND_IF_COND, 0x1AA, 0x87, SD_ANSWER_R7_EXPECTED); - SD_IO_CSState(1); - SD_IO_WriteByte(SD_DUMMY_BYTE); - if((response.r1 & SD_R1_ILLEGAL_COMMAND) == SD_R1_ILLEGAL_COMMAND) { - /* initialise card V1 */ - counter = 0; - do { - counter++; - /* initialise card V1 */ - /* Send CMD55 (SD_CMD_APP_CMD) before any ACMD command: R1 response (0x00: no errors) */ - response = SD_SendCmd(SD_CMD_APP_CMD, 0x00000000, 0xFF, SD_ANSWER_R1_EXPECTED); - SD_IO_CSState(1); - 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 = //-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) { - return BSP_SD_ERROR; - } - } while(response.r1 == SD_R1_IN_IDLE_STATE); - flag_SDHC = 0; - } else if(response.r1 == SD_R1_IN_IDLE_STATE) { - /* initialise card V2 */ - counter = 0; - do { - counter++; - /* Send CMD55 (SD_CMD_APP_CMD) before any ACMD command: R1 response (0x00: no errors) */ - response = SD_SendCmd(SD_CMD_APP_CMD, 0, 0xFF, SD_ANSWER_R1_EXPECTED); - SD_IO_CSState(1); - 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 = //-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) { - return BSP_SD_ERROR; - } - } while(response.r1 == SD_R1_IN_IDLE_STATE); - - if((response.r1 & SD_R1_ILLEGAL_COMMAND) == SD_R1_ILLEGAL_COMMAND) { - counter = 0; - do { - counter++; - /* Send CMD55 (SD_CMD_APP_CMD) before any ACMD command: R1 response (0x00: no errors) */ - response = SD_SendCmd(SD_CMD_APP_CMD, 0, 0xFF, SD_ANSWER_R1_EXPECTED); - SD_IO_CSState(1); - SD_IO_WriteByte(SD_DUMMY_BYTE); - if(response.r1 != SD_R1_IN_IDLE_STATE) { - return BSP_SD_ERROR; - } - /* 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); - SD_IO_CSState(1); - SD_IO_WriteByte(SD_DUMMY_BYTE); - if(counter >= SD_MAX_TRY) { - return BSP_SD_ERROR; - } - } while(response.r1 == SD_R1_IN_IDLE_STATE); - } - - /* Send CMD58 (SD_CMD_READ_OCR) to initialize SDHC or SDXC cards: R3 response (0x00: no errors) */ - response = SD_SendCmd(SD_CMD_READ_OCR, 0x00000000, 0xFF, SD_ANSWER_R3_EXPECTED); - SD_IO_CSState(1); - SD_IO_WriteByte(SD_DUMMY_BYTE); - if(response.r1 != SD_R1_NO_ERROR) { - return BSP_SD_ERROR; - } - flag_SDHC = (response.r2 & 0x40) >> 6; - } else { - return BSP_SD_ERROR; - } - - return BSP_SD_OK; -} - -/** - * @brief Waits a data until a value different from SD_DUMMY_BITE - * @param None - * @retval the value read - */ -uint8_t SD_ReadData(void) { - uint8_t timeout = 0x08; - uint8_t readvalue; - - /* Check if response is got or a timeout is happen */ - do { - readvalue = SD_IO_WriteByte(SD_DUMMY_BYTE); - timeout--; - - } while((readvalue == SD_DUMMY_BYTE) && timeout); - - /* Right response got */ - return readvalue; -} - -/** - * @brief Waits a data from the SD card - * @param data : Expected data from the SD card - * @retval BSP_SD_OK or BSP_SD_TIMEOUT - */ -uint8_t SD_WaitData(uint8_t data) { - uint16_t timeout = 0xFFFF; - uint8_t readvalue; - - /* Check if response is got or a timeout is happen */ - - do { - readvalue = SD_IO_WriteByte(SD_DUMMY_BYTE); - timeout--; - } while((readvalue != data) && timeout); - - if(timeout == 0) { - /* After time out */ - return BSP_SD_TIMEOUT; - } - - /* Right response got */ - return BSP_SD_OK; -} - -/** - * @} - */ - -/** - * @} - */ - -/** - * @} - */ - -/** - * @} - */ - -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/firmware/targets/f7/fatfs/stm32_adafruit_sd.h b/firmware/targets/f7/fatfs/stm32_adafruit_sd.h deleted file mode 100644 index a133c5922..000000000 --- a/firmware/targets/f7/fatfs/stm32_adafruit_sd.h +++ /dev/null @@ -1,245 +0,0 @@ -/** - ****************************************************************************** - * @file stm32_adafruit_sd.h - * @author MCD Application Team - * @version V3.0.0 - * @date 23-December-2016 - * @brief This file contains the common defines and functions prototypes for - * the stm32_adafruit_sd.c driver. - ****************************************************************************** - * @attention - * - *

© COPYRIGHT(c) 2016 STMicroelectronics

- * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of STMicroelectronics nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - ****************************************************************************** - */ - -/* Define to prevent recursive inclusion -------------------------------------*/ -#ifndef __STM32_ADAFRUIT_SD_H -#define __STM32_ADAFRUIT_SD_H - -#ifdef __cplusplus -extern "C" { -#endif - -/* Includes ------------------------------------------------------------------*/ -#include -#include - -/** @addtogroup BSP - * @{ - */ -#define __IO volatile - -/** @addtogroup STM32_ADAFRUIT - * @{ - */ - -/** @defgroup STM32_ADAFRUIT_SD - * @{ - */ - -/** @defgroup STM32_ADAFRUIT_SD_Exported_Types - * @{ - */ - -/** - * @brief SD status structure definition - */ -enum { BSP_SD_OK = 0x00, MSD_OK = 0x00, BSP_SD_ERROR = 0x01, BSP_SD_TIMEOUT }; - -typedef struct { - uint8_t Reserved1 : 2; /* Reserved */ - uint16_t DeviceSize : 12; /* Device Size */ - uint8_t MaxRdCurrentVDDMin : 3; /* Max. read current @ VDD min */ - uint8_t MaxRdCurrentVDDMax : 3; /* Max. read current @ VDD max */ - uint8_t MaxWrCurrentVDDMin : 3; /* Max. write current @ VDD min */ - uint8_t MaxWrCurrentVDDMax : 3; /* Max. write current @ VDD max */ - uint8_t DeviceSizeMul : 3; /* Device size multiplier */ -} struct_v1; - -typedef struct { - uint8_t Reserved1 : 6; /* Reserved */ - uint32_t DeviceSize : 22; /* Device Size */ - uint8_t Reserved2 : 1; /* Reserved */ -} struct_v2; - -/** - * @brief Card Specific Data: CSD Register - */ -typedef struct { - /* Header part */ - uint8_t CSDStruct : 2; /* CSD structure */ - uint8_t Reserved1 : 6; /* Reserved */ - uint8_t TAAC : 8; /* Data read access-time 1 */ - uint8_t NSAC : 8; /* Data read access-time 2 in CLK cycles */ - uint8_t MaxBusClkFrec : 8; /* Max. bus clock frequency */ - uint16_t CardComdClasses : 12; /* Card command classes */ - uint8_t RdBlockLen : 4; /* Max. read data block length */ - uint8_t PartBlockRead : 1; /* Partial blocks for read allowed */ - uint8_t WrBlockMisalign : 1; /* Write block misalignment */ - uint8_t RdBlockMisalign : 1; /* Read block misalignment */ - uint8_t DSRImpl : 1; /* DSR implemented */ - - /* v1 or v2 struct */ - union csd_version { - struct_v1 v1; - struct_v2 v2; - } version; - - uint8_t EraseSingleBlockEnable : 1; /* Erase single block enable */ - uint8_t EraseSectorSize : 7; /* Erase group size multiplier */ - uint8_t WrProtectGrSize : 7; /* Write protect group size */ - uint8_t WrProtectGrEnable : 1; /* Write protect group enable */ - uint8_t Reserved2 : 2; /* Reserved */ - uint8_t WrSpeedFact : 3; /* Write speed factor */ - uint8_t MaxWrBlockLen : 4; /* Max. write data block length */ - uint8_t WriteBlockPartial : 1; /* Partial blocks for write allowed */ - uint8_t Reserved3 : 5; /* Reserved */ - uint8_t FileFormatGrouop : 1; /* File format group */ - uint8_t CopyFlag : 1; /* Copy flag (OTP) */ - uint8_t PermWrProtect : 1; /* Permanent write protection */ - uint8_t TempWrProtect : 1; /* Temporary write protection */ - uint8_t FileFormat : 2; /* File Format */ - uint8_t Reserved4 : 2; /* Reserved */ - uint8_t crc : 7; /* Reserved */ - uint8_t Reserved5 : 1; /* always 1*/ - -} SD_CSD; - -/** - * @brief Card Identification Data: CID Register - */ -typedef struct { - uint8_t ManufacturerID; /* ManufacturerID */ - char OEM_AppliID[2]; /* OEM/Application ID */ - char ProdName[5]; /* Product Name */ - uint8_t ProdRev; /* Product Revision */ - uint32_t ProdSN; /* Product Serial Number */ - uint8_t Reserved1; /* Reserved1 */ - uint8_t ManufactYear; /* Manufacturing Year */ - uint8_t ManufactMonth; /* Manufacturing Month */ - uint8_t CID_CRC; /* CID CRC */ - uint8_t Reserved2; /* always 1 */ -} SD_CID; - -/** - * @brief SD Card information - */ -typedef struct { - SD_CSD Csd; - SD_CID Cid; - uint64_t CardCapacity; /*!< Card Capacity */ - uint32_t CardBlockSize; /*!< Card Block Size */ - uint32_t LogBlockNbr; /*!< Specifies the Card logical Capacity in blocks */ - uint32_t LogBlockSize; /*!< Specifies logical block size in bytes */ -} SD_CardInfo; - -/** - * @} - */ - -/** @defgroup STM32_ADAFRUIT_SPI_SD_Exported_Constants - * @{ - */ - -/** - * @brief Block Size - */ -#define SD_BLOCK_SIZE 0x200 - -/** - * @brief SD detection on its memory slot - */ -#define SD_PRESENT ((uint8_t)0x01) -#define SD_NOT_PRESENT ((uint8_t)0x00) - -#define SD_DATATIMEOUT ((uint32_t)100000000) - -/** - * @brief SD Card information structure - */ -#define BSP_SD_CardInfo SD_CardInfo - -/** - * @} - */ - -/** @defgroup STM32_ADAFRUIT_SD_Exported_Macro - * @{ - */ - -/** - * @} - */ - -/** @defgroup STM32_ADAFRUIT_SD_Exported_Functions - * @{ - */ -uint8_t BSP_SD_MaxMountRetryCount(); -uint8_t BSP_SD_Init(bool reset_card); -uint8_t - BSP_SD_ReadBlocks(uint32_t* pData, uint32_t ReadAddr, uint32_t NumOfBlocks, uint32_t Timeout); -uint8_t - BSP_SD_WriteBlocks(uint32_t* pData, uint32_t WriteAddr, uint32_t NumOfBlocks, uint32_t Timeout); -uint8_t BSP_SD_Erase(uint32_t StartAddr, uint32_t EndAddr); -uint8_t BSP_SD_GetCardState(void); -uint8_t BSP_SD_GetCardInfo(SD_CardInfo* pCardInfo); -uint8_t BSP_SD_GetCIDRegister(SD_CID* Cid); - -/* Link functions for SD Card peripheral*/ -void SD_SPI_Slow_Init(void); -void SD_SPI_Fast_Init(void); -void SD_IO_Init(void); -void SD_IO_CSState(uint8_t state); -void SD_IO_WriteReadData(const uint8_t* DataIn, uint8_t* DataOut, uint16_t DataLength); -uint8_t SD_IO_WriteByte(uint8_t Data); - -/* Link function for HAL delay */ -void HAL_Delay(__IO uint32_t Delay); - -#ifdef __cplusplus -} -#endif - -#endif /* __STM32_ADAFRUIT_SD_H */ - -/** - * @} - */ - -/** - * @} - */ - -/** - * @} - */ - -/** - * @} - */ - -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/firmware/targets/f7/fatfs/user_diskio.c b/firmware/targets/f7/fatfs/user_diskio.c index b504fcd51..d7be09c53 100644 --- a/firmware/targets/f7/fatfs/user_diskio.c +++ b/firmware/targets/f7/fatfs/user_diskio.c @@ -36,6 +36,7 @@ /* Includes ------------------------------------------------------------------*/ #include "user_diskio.h" #include +#include "sector_cache.h" /* Private typedef -----------------------------------------------------------*/ /* Private define ------------------------------------------------------------*/ @@ -46,7 +47,7 @@ static volatile DSTATUS Stat = STA_NOINIT; static DSTATUS User_CheckStatus(BYTE lun) { UNUSED(lun); Stat = STA_NOINIT; - if(BSP_SD_GetCardState() == MSD_OK) { + if(sd_get_card_state() == SdSpiStatusOK) { Stat &= ~STA_NOINIT; } @@ -79,6 +80,26 @@ Diskio_drvTypeDef USER_Driver = { }; /* Private functions ---------------------------------------------------------*/ +static inline bool sd_cache_get(uint32_t address, uint32_t* data) { + uint8_t* cached_data = sector_cache_get(address); + if(cached_data) { + memcpy(data, cached_data, SD_BLOCK_SIZE); + return true; + } + return false; +} + +static inline void sd_cache_put(uint32_t address, uint32_t* data) { + sector_cache_put(address, (uint8_t*)data); +} + +static inline void sd_cache_invalidate_range(uint32_t start_sector, uint32_t end_sector) { + sector_cache_invalidate_range(start_sector, end_sector); +} + +static inline void sd_cache_invalidate_all() { + sector_cache_init(); +} /** * @brief Initializes a Drive @@ -125,19 +146,38 @@ DRESULT USER_read(BYTE pdrv, BYTE* buff, DWORD sector, UINT count) { UNUSED(pdrv); DRESULT res = RES_ERROR; + bool single_sector = count == 1; + + if(single_sector) { + if(sd_cache_get(sector, (uint32_t*)buff)) { + return RES_OK; + } + } + furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_fast); furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_fast; - if(BSP_SD_ReadBlocks((uint32_t*)buff, (uint32_t)(sector), count, SD_DATATIMEOUT) == MSD_OK) { + if(sd_read_blocks((uint32_t*)buff, (uint32_t)(sector), count, SD_TIMEOUT_MS) == + SdSpiStatusOK) { + FuriHalCortexTimer timer = furi_hal_cortex_timer_get(SD_TIMEOUT_MS * 1000); + /* wait until the read operation is finished */ - while(BSP_SD_GetCardState() != MSD_OK) { - } res = RES_OK; + while(sd_get_card_state() != SdSpiStatusOK) { + if(furi_hal_cortex_timer_is_expired(timer)) { + res = RES_ERROR; + break; + } + } } furi_hal_sd_spi_handle = NULL; furi_hal_spi_release(&furi_hal_spi_bus_handle_sd_fast); + if(single_sector && res == RES_OK) { + sd_cache_put(sector, (uint32_t*)buff); + } + return res; /* USER CODE END READ */ } @@ -157,14 +197,25 @@ DRESULT USER_write(BYTE pdrv, const BYTE* buff, DWORD sector, UINT count) { UNUSED(pdrv); DRESULT res = RES_ERROR; + sd_cache_invalidate_range(sector, sector + count); + furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_fast); furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_fast; - if(BSP_SD_WriteBlocks((uint32_t*)buff, (uint32_t)(sector), count, SD_DATATIMEOUT) == MSD_OK) { + if(sd_write_blocks((uint32_t*)buff, (uint32_t)(sector), count, SD_TIMEOUT_MS) == + SdSpiStatusOK) { + FuriHalCortexTimer timer = furi_hal_cortex_timer_get(SD_TIMEOUT_MS * 1000); + /* wait until the Write operation is finished */ - while(BSP_SD_GetCardState() != MSD_OK) { - } res = RES_OK; + while(sd_get_card_state() != SdSpiStatusOK) { + if(furi_hal_cortex_timer_is_expired(timer)) { + sd_cache_invalidate_all(); + + res = RES_ERROR; + break; + } + } } furi_hal_sd_spi_handle = NULL; @@ -187,7 +238,7 @@ DRESULT USER_ioctl(BYTE pdrv, BYTE cmd, void* buff) { /* USER CODE BEGIN IOCTL */ UNUSED(pdrv); DRESULT res = RES_ERROR; - BSP_SD_CardInfo CardInfo; + SD_CardInfo CardInfo; if(Stat & STA_NOINIT) return RES_NOTRDY; @@ -202,21 +253,21 @@ DRESULT USER_ioctl(BYTE pdrv, BYTE cmd, void* buff) { /* Get number of sectors on the disk (DWORD) */ case GET_SECTOR_COUNT: - BSP_SD_GetCardInfo(&CardInfo); + sd_get_card_info(&CardInfo); *(DWORD*)buff = CardInfo.LogBlockNbr; res = RES_OK; break; /* Get R/W sector size (WORD) */ case GET_SECTOR_SIZE: - BSP_SD_GetCardInfo(&CardInfo); + sd_get_card_info(&CardInfo); *(WORD*)buff = CardInfo.LogBlockSize; res = RES_OK; break; /* Get erase block size in unit of sector (DWORD) */ case GET_BLOCK_SIZE: - BSP_SD_GetCardInfo(&CardInfo); + sd_get_card_info(&CardInfo); *(DWORD*)buff = CardInfo.LogBlockSize; res = RES_OK; break; diff --git a/firmware/targets/f7/fatfs/user_diskio.h b/firmware/targets/f7/fatfs/user_diskio.h index 177723be1..12e0f27dc 100644 --- a/firmware/targets/f7/fatfs/user_diskio.h +++ b/firmware/targets/f7/fatfs/user_diskio.h @@ -30,7 +30,7 @@ extern "C" { /* USER CODE BEGIN 0 */ /* Includes ------------------------------------------------------------------*/ -#include "stm32_adafruit_sd.h" +#include "sd_spi_io.h" #include "fatfs/ff_gen_drv.h" /* Exported types ------------------------------------------------------------*/ /* Exported constants --------------------------------------------------------*/ diff --git a/firmware/targets/f7/furi_hal/furi_hal.c b/firmware/targets/f7/furi_hal/furi_hal.c index ad79ba33a..a887de5cd 100644 --- a/firmware/targets/f7/furi_hal/furi_hal.c +++ b/firmware/targets/f7/furi_hal/furi_hal.c @@ -52,6 +52,7 @@ void furi_hal_init() { furi_hal_version_init(); furi_hal_spi_config_init(); + furi_hal_spi_dma_init(); furi_hal_ibutton_init(); FURI_LOG_I(TAG, "iButton OK"); diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt.c b/firmware/targets/f7/furi_hal/furi_hal_bt.c index 79fbc693f..198bb4454 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_bt.c +++ b/firmware/targets/f7/furi_hal/furi_hal_bt.c @@ -54,25 +54,27 @@ FuriHalBtProfileConfig profile_config[FuriHalBtProfileNumber] = { }, }, }, - [FuriHalBtProfileHidKeyboard] = { - .start = furi_hal_bt_hid_start, - .stop = furi_hal_bt_hid_stop, - .config = - { - .adv_service_uuid = HUMAN_INTERFACE_DEVICE_SERVICE_UUID, - .appearance_char = GAP_APPEARANCE_KEYBOARD, - .bonding_mode = true, - .pairing_method = GapPairingPinCodeVerifyYesNo, - .mac_address = FURI_HAL_BT_DEFAULT_MAC_ADDR, - .conn_param = - { - .conn_int_min = 0x18, // 30 ms - .conn_int_max = 0x24, // 45 ms - .slave_latency = 0, - .supervisor_timeout = 0, - }, - }, - }}; + [FuriHalBtProfileHidKeyboard] = + { + .start = furi_hal_bt_hid_start, + .stop = furi_hal_bt_hid_stop, + .config = + { + .adv_service_uuid = HUMAN_INTERFACE_DEVICE_SERVICE_UUID, + .appearance_char = GAP_APPEARANCE_KEYBOARD, + .bonding_mode = true, + .pairing_method = GapPairingPinCodeVerifyYesNo, + .mac_address = FURI_HAL_BT_DEFAULT_MAC_ADDR, + .conn_param = + { + .conn_int_min = 0x18, // 30 ms + .conn_int_max = 0x24, // 45 ms + .slave_latency = 0, + .supervisor_timeout = 0, + }, + }, + }, +}; FuriHalBtProfileConfig* current_profile = NULL; void furi_hal_bt_init() { @@ -207,6 +209,12 @@ bool furi_hal_bt_start_app(FuriHalBtProfile profile, GapEventCallback event_cb, profile_config[profile].config.mac_address, furi_hal_version_get_ble_mac(), sizeof(profile_config[profile].config.mac_address)); + // Set advertise name + strlcpy( + profile_config[profile].config.adv_name, + furi_hal_version_get_ble_local_device_name_ptr(), + FURI_HAL_VERSION_DEVICE_NAME_LENGTH); + config->adv_service_uuid |= furi_hal_version_get_hw_color(); } else if(profile == FuriHalBtProfileHidKeyboard) { // Change MAC address for HID profile @@ -216,8 +224,11 @@ bool furi_hal_bt_start_app(FuriHalBtProfile profile, GapEventCallback event_cb, } // Change name Flipper -> Control if(strlen(&config->adv_name[1]) == 0) { - const char* clicker_str = "Control"; - memcpy(&config->adv_name[1], clicker_str, strlen(clicker_str)); + snprintf( + &config->adv_name[1], + strlen("Control ") + FURI_HAL_VERSION_DEVICE_NAME_LENGTH, + "Control %s", + furi_hal_version_get_ble_local_device_name_ptr()); } } if(!gap_init(config, event_cb, context)) { @@ -468,11 +479,18 @@ void furi_hal_bt_set_profile_adv_name( furi_assert(profile < FuriHalBtProfileNumber); furi_assert(name); - profile_config[profile].config.adv_name[0] = 0x09; - memcpy( - &(profile_config[profile].config.adv_name[1]), - name, - FURI_HAL_VERSION_DEVICE_NAME_LENGTH - 1); + if(strlen(name) == 0) { + memset( + &(profile_config[profile].config.adv_name[1]), + 0, + strlen(&(profile_config[profile].config.adv_name[1]))); + } else { + profile_config[profile].config.adv_name[0] = AD_TYPE_COMPLETE_LOCAL_NAME; + memcpy( + &(profile_config[profile].config.adv_name[1]), + name, + FURI_HAL_VERSION_DEVICE_NAME_LENGTH - 1); + } } const char* furi_hal_bt_get_profile_adv_name(FuriHalBtProfile profile) { 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 be0bd2af3..4e6dc16c8 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c +++ b/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c @@ -64,6 +64,12 @@ static const uint8_t furi_hal_bt_hid_report_map_data[] = { HID_REPORT_COUNT(1), HID_REPORT_SIZE(8), HID_INPUT(HID_IOF_CONSTANT | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE), + HID_USAGE_PAGE(HID_PAGE_LED), + HID_REPORT_COUNT(8), + HID_REPORT_SIZE(1), + HID_USAGE_MINIMUM(1), + HID_USAGE_MAXIMUM(8), + HID_OUTPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE), HID_REPORT_COUNT(FURI_HAL_BT_HID_KB_MAX_KEYS), HID_REPORT_SIZE(8), HID_LOGICAL_MINIMUM(0), @@ -196,6 +202,7 @@ void furi_hal_bt_hid_start() { if(!hid_svc_is_started()) { hid_svc_start(); } + // Configure HID Keyboard hid_svc_register_led_state_callback(furi_hal_bt_hid_led_state_cb, &hid_host_led_state); diff --git a/firmware/targets/f7/furi_hal/furi_hal_gpio.c b/firmware/targets/f7/furi_hal/furi_hal_gpio.c index a148a3af2..e8318afcf 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_gpio.c +++ b/firmware/targets/f7/furi_hal/furi_hal_gpio.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #define GET_SYSCFG_EXTI_PORT(gpio) \ @@ -224,85 +225,85 @@ static void furi_hal_gpio_int_call(uint16_t pin_num) { /* Interrupt handlers */ void EXTI0_IRQHandler(void) { if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_0)) { - LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_0); furi_hal_gpio_int_call(0); + LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_0); } } void EXTI1_IRQHandler(void) { if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_1)) { - LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_1); furi_hal_gpio_int_call(1); + LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_1); } } void EXTI2_IRQHandler(void) { if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_2)) { - LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_2); furi_hal_gpio_int_call(2); + LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_2); } } void EXTI3_IRQHandler(void) { if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_3)) { - LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_3); furi_hal_gpio_int_call(3); + LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_3); } } void EXTI4_IRQHandler(void) { if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_4)) { - LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_4); furi_hal_gpio_int_call(4); + LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_4); } } void EXTI9_5_IRQHandler(void) { if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_5)) { - LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_5); furi_hal_gpio_int_call(5); + LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_5); } if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_6)) { - LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_6); furi_hal_gpio_int_call(6); + LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_6); } if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_7)) { - LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_7); furi_hal_gpio_int_call(7); + LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_7); } if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_8)) { - LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_8); furi_hal_gpio_int_call(8); + LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_8); } if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_9)) { - LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_9); furi_hal_gpio_int_call(9); + LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_9); } } void EXTI15_10_IRQHandler(void) { if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_10)) { - LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_10); furi_hal_gpio_int_call(10); + LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_10); } if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_11)) { - LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_11); furi_hal_gpio_int_call(11); + LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_11); } if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_12)) { - LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_12); furi_hal_gpio_int_call(12); + LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_12); } if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_13)) { - LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_13); furi_hal_gpio_int_call(13); + LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_13); } if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_14)) { - LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_14); furi_hal_gpio_int_call(14); + LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_14); } if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_15)) { - LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_15); furi_hal_gpio_int_call(15); + LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_15); } } diff --git a/firmware/targets/f7/furi_hal/furi_hal_ibutton.c b/firmware/targets/f7/furi_hal/furi_hal_ibutton.c index 0375893e7..c05cd69a8 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_ibutton.c +++ b/firmware/targets/f7/furi_hal/furi_hal_ibutton.c @@ -89,47 +89,16 @@ void furi_hal_ibutton_emulate_stop() { } } -void furi_hal_ibutton_start_drive() { - furi_hal_ibutton_pin_high(); +void furi_hal_ibutton_pin_configure() { + furi_hal_gpio_write(&ibutton_gpio, true); furi_hal_gpio_init(&ibutton_gpio, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow); } -void furi_hal_ibutton_start_drive_in_isr() { - furi_hal_gpio_init(&ibutton_gpio, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow); - LL_EXTI_ClearFlag_0_31(ibutton_gpio.pin); -} - -void furi_hal_ibutton_start_interrupt() { - furi_hal_ibutton_pin_high(); - furi_hal_gpio_init(&ibutton_gpio, GpioModeInterruptRiseFall, GpioPullNo, GpioSpeedLow); -} - -void furi_hal_ibutton_start_interrupt_in_isr() { - furi_hal_gpio_init(&ibutton_gpio, GpioModeInterruptRiseFall, GpioPullNo, GpioSpeedLow); - LL_EXTI_ClearFlag_0_31(ibutton_gpio.pin); -} - -void furi_hal_ibutton_stop() { - furi_hal_ibutton_pin_high(); +void furi_hal_ibutton_pin_reset() { + furi_hal_gpio_write(&ibutton_gpio, true); furi_hal_gpio_init(&ibutton_gpio, GpioModeAnalog, GpioPullNo, GpioSpeedLow); } -void furi_hal_ibutton_add_interrupt(GpioExtiCallback cb, void* context) { - furi_hal_gpio_add_int_callback(&ibutton_gpio, cb, context); -} - -void furi_hal_ibutton_remove_interrupt() { - furi_hal_gpio_remove_int_callback(&ibutton_gpio); -} - -void furi_hal_ibutton_pin_low() { - furi_hal_gpio_write(&ibutton_gpio, false); -} - -void furi_hal_ibutton_pin_high() { - furi_hal_gpio_write(&ibutton_gpio, true); -} - -bool furi_hal_ibutton_pin_get_level() { - return furi_hal_gpio_read(&ibutton_gpio); +void furi_hal_ibutton_pin_write(const bool state) { + furi_hal_gpio_write(&ibutton_gpio, state); } diff --git a/firmware/targets/f7/furi_hal/furi_hal_ibutton.h b/firmware/targets/f7/furi_hal/furi_hal_ibutton.h index fb57d628b..b723fe176 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_ibutton.h +++ b/firmware/targets/f7/furi_hal/furi_hal_ibutton.h @@ -7,7 +7,6 @@ #include #include -#include #ifdef __cplusplus extern "C" { @@ -18,70 +17,43 @@ typedef void (*FuriHalIbuttonEmulateCallback)(void* context); /** Initialize */ void furi_hal_ibutton_init(); +/** + * Start emulation timer + * @param period timer period + * @param callback timer callback + * @param context callback context + */ void furi_hal_ibutton_emulate_start( uint32_t period, FuriHalIbuttonEmulateCallback callback, void* context); +/** + * Update emulation timer period + * @param period new timer period + */ void furi_hal_ibutton_emulate_set_next(uint32_t period); +/** + * Stop emulation timer + */ void furi_hal_ibutton_emulate_stop(); /** - * Sets the pin to normal mode (open collector), and sets it to float + * Set the pin to normal mode (open collector), and sets it to float */ -void furi_hal_ibutton_start_drive(); - -/** - * Sets the pin to normal mode (open collector), and clears pin EXTI interrupt. - * Used in EXTI interrupt context. - */ -void furi_hal_ibutton_start_drive_in_isr(); - -/** - * Sets the pin to interrupt mode (EXTI interrupt on rise or fall), and sets it to float - */ -void furi_hal_ibutton_start_interrupt(); - -/** - * Sets the pin to interrupt mode (EXTI interrupt on rise or fall), and clears pin EXTI interrupt. - * Used in EXTI interrupt context. - */ -void furi_hal_ibutton_start_interrupt_in_isr(); +void furi_hal_ibutton_pin_configure(); /** * Sets the pin to analog mode, and sets it to float */ -void furi_hal_ibutton_stop(); +void furi_hal_ibutton_pin_reset(); /** - * Attach interrupt callback to iButton pin - * @param cb callback - * @param context context + * iButton write pin + * @param state true / false */ -void furi_hal_ibutton_add_interrupt(GpioExtiCallback cb, void* context); - -/** - * Remove interrupt callback from iButton pin - */ -void furi_hal_ibutton_remove_interrupt(); - -/** - * Sets the pin to low - */ -void furi_hal_ibutton_pin_low(); - -/** - * Sets the pin to high (float in iButton pin modes) - */ -void furi_hal_ibutton_pin_high(); - -/** - * Get pin level - * @return true if level is high - * @return false if level is low - */ -bool furi_hal_ibutton_pin_get_level(); +void furi_hal_ibutton_pin_write(const bool state); #ifdef __cplusplus } diff --git a/firmware/targets/f7/furi_hal/furi_hal_infrared.c b/firmware/targets/f7/furi_hal/furi_hal_infrared.c index 5af43f4e5..b65ea42e1 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_infrared.c +++ b/firmware/targets/f7/furi_hal/furi_hal_infrared.c @@ -13,13 +13,6 @@ #include #include -#define INFRARED_TX_DEBUG 0 - -#if INFRARED_TX_DEBUG == 1 -#define gpio_infrared_tx gpio_infrared_tx_debug -const GpioPin gpio_infrared_tx_debug = {.port = GPIOA, .pin = LL_GPIO_PIN_6}; -#endif - #define INFRARED_TIM_TX_DMA_BUFFER_SIZE 200 #define INFRARED_POLARITY_SHIFT 1 @@ -28,6 +21,15 @@ const GpioPin gpio_infrared_tx_debug = {.port = GPIOA, .pin = LL_GPIO_PIN_6}; #define INFRARED_TX_CCMR_LOW \ (TIM_CCMR2_OC3PE | LL_TIM_OCMODE_FORCED_INACTIVE) /* Space time - force low */ +/* DMA Channels definition */ +#define IR_DMA DMA2 +#define IR_DMA_CH1_CHANNEL LL_DMA_CHANNEL_1 +#define IR_DMA_CH2_CHANNEL LL_DMA_CHANNEL_2 +#define IR_DMA_CH1_IRQ FuriHalInterruptIdDma2Ch1 +#define IR_DMA_CH2_IRQ FuriHalInterruptIdDma2Ch2 +#define IR_DMA_CH1_DEF IR_DMA, IR_DMA_CH1_CHANNEL +#define IR_DMA_CH2_DEF IR_DMA, IR_DMA_CH2_CHANNEL + typedef struct { FuriHalInfraredRxCaptureCallback capture_callback; void* capture_context; @@ -70,6 +72,7 @@ typedef enum { static volatile InfraredState furi_hal_infrared_state = InfraredStateIdle; static InfraredTimTx infrared_tim_tx; static InfraredTimRx infrared_tim_rx; +static bool infrared_external_output; static void furi_hal_infrared_tx_fill_buffer(uint8_t buf_num, uint8_t polarity_shift); static void furi_hal_infrared_async_tx_free_resources(void); @@ -80,6 +83,14 @@ static uint8_t furi_hal_infrared_get_current_dma_tx_buffer(void); static void furi_hal_infrared_tx_dma_polarity_isr(); static void furi_hal_infrared_tx_dma_isr(); +void furi_hal_infrared_set_debug_out(bool enable) { + infrared_external_output = enable; +} + +bool furi_hal_infrared_get_debug_out_status(void) { + return infrared_external_output; +} + static void furi_hal_infrared_tim_rx_isr() { static uint32_t previous_captured_ch2 = 0; @@ -213,15 +224,15 @@ void furi_hal_infrared_async_rx_set_timeout_isr_callback( } static void furi_hal_infrared_tx_dma_terminate(void) { - LL_DMA_DisableIT_TC(DMA1, LL_DMA_CHANNEL_1); - LL_DMA_DisableIT_HT(DMA1, LL_DMA_CHANNEL_2); - LL_DMA_DisableIT_TC(DMA1, LL_DMA_CHANNEL_2); + LL_DMA_DisableIT_TC(IR_DMA_CH1_DEF); + LL_DMA_DisableIT_HT(IR_DMA_CH2_DEF); + LL_DMA_DisableIT_TC(IR_DMA_CH2_DEF); furi_assert(furi_hal_infrared_state == InfraredStateAsyncTxStopInProgress); - LL_DMA_DisableIT_TC(DMA1, LL_DMA_CHANNEL_1); - LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2); - LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_DisableIT_TC(IR_DMA_CH1_DEF); + LL_DMA_DisableChannel(IR_DMA_CH2_DEF); + LL_DMA_DisableChannel(IR_DMA_CH1_DEF); LL_TIM_DisableCounter(TIM1); FuriStatus status = furi_semaphore_release(infrared_tim_tx.stop_semaphore); furi_check(status == FuriStatusOk); @@ -230,7 +241,7 @@ static void furi_hal_infrared_tx_dma_terminate(void) { static uint8_t furi_hal_infrared_get_current_dma_tx_buffer(void) { uint8_t buf_num = 0; - uint32_t buffer_adr = LL_DMA_GetMemoryAddress(DMA1, LL_DMA_CHANNEL_2); + uint32_t buffer_adr = LL_DMA_GetMemoryAddress(IR_DMA_CH2_DEF); if(buffer_adr == (uint32_t)infrared_tim_tx.buffer[0].data) { buf_num = 0; } else if(buffer_adr == (uint32_t)infrared_tim_tx.buffer[1].data) { @@ -242,12 +253,13 @@ static uint8_t furi_hal_infrared_get_current_dma_tx_buffer(void) { } static void furi_hal_infrared_tx_dma_polarity_isr() { - if(LL_DMA_IsActiveFlag_TE1(DMA1)) { - LL_DMA_ClearFlag_TE1(DMA1); +#if IR_DMA_CH1_CHANNEL == LL_DMA_CHANNEL_1 + if(LL_DMA_IsActiveFlag_TE1(IR_DMA)) { + LL_DMA_ClearFlag_TE1(IR_DMA); furi_crash(NULL); } - if(LL_DMA_IsActiveFlag_TC1(DMA1) && LL_DMA_IsEnabledIT_TC(DMA1, LL_DMA_CHANNEL_1)) { - LL_DMA_ClearFlag_TC1(DMA1); + if(LL_DMA_IsActiveFlag_TC1(IR_DMA) && LL_DMA_IsEnabledIT_TC(IR_DMA_CH1_DEF)) { + LL_DMA_ClearFlag_TC1(IR_DMA); furi_check( (furi_hal_infrared_state == InfraredStateAsyncTx) || @@ -257,25 +269,29 @@ static void furi_hal_infrared_tx_dma_polarity_isr() { uint8_t next_buf_num = furi_hal_infrared_get_current_dma_tx_buffer(); furi_hal_infrared_tx_dma_set_polarity(next_buf_num, 0); } +#else +#error Update this code. Would you kindly? +#endif } static void furi_hal_infrared_tx_dma_isr() { - if(LL_DMA_IsActiveFlag_TE2(DMA1)) { - LL_DMA_ClearFlag_TE2(DMA1); +#if IR_DMA_CH2_CHANNEL == LL_DMA_CHANNEL_2 + if(LL_DMA_IsActiveFlag_TE2(IR_DMA)) { + LL_DMA_ClearFlag_TE2(IR_DMA); furi_crash(NULL); } - if(LL_DMA_IsActiveFlag_HT2(DMA1) && LL_DMA_IsEnabledIT_HT(DMA1, LL_DMA_CHANNEL_2)) { - LL_DMA_ClearFlag_HT2(DMA1); + if(LL_DMA_IsActiveFlag_HT2(IR_DMA) && LL_DMA_IsEnabledIT_HT(IR_DMA_CH2_DEF)) { + LL_DMA_ClearFlag_HT2(IR_DMA); uint8_t buf_num = furi_hal_infrared_get_current_dma_tx_buffer(); uint8_t next_buf_num = !buf_num; if(infrared_tim_tx.buffer[buf_num].last_packet_end) { - LL_DMA_DisableIT_HT(DMA1, LL_DMA_CHANNEL_2); + LL_DMA_DisableIT_HT(IR_DMA_CH2_DEF); } else if( !infrared_tim_tx.buffer[buf_num].packet_end || (furi_hal_infrared_state == InfraredStateAsyncTx)) { furi_hal_infrared_tx_fill_buffer(next_buf_num, 0); if(infrared_tim_tx.buffer[next_buf_num].last_packet_end) { - LL_DMA_DisableIT_HT(DMA1, LL_DMA_CHANNEL_2); + LL_DMA_DisableIT_HT(IR_DMA_CH2_DEF); } } else if(furi_hal_infrared_state == InfraredStateAsyncTxStopReq) { /* fallthrough */ @@ -283,8 +299,8 @@ static void furi_hal_infrared_tx_dma_isr() { furi_crash(NULL); } } - if(LL_DMA_IsActiveFlag_TC2(DMA1) && LL_DMA_IsEnabledIT_TC(DMA1, LL_DMA_CHANNEL_2)) { - LL_DMA_ClearFlag_TC2(DMA1); + if(LL_DMA_IsActiveFlag_TC2(IR_DMA) && LL_DMA_IsEnabledIT_TC(IR_DMA_CH2_DEF)) { + LL_DMA_ClearFlag_TC2(IR_DMA); furi_check( (furi_hal_infrared_state == InfraredStateAsyncTxStopInProgress) || (furi_hal_infrared_state == InfraredStateAsyncTxStopReq) || @@ -310,6 +326,9 @@ static void furi_hal_infrared_tx_dma_isr() { infrared_tim_tx.signal_sent_callback(infrared_tim_tx.signal_sent_context); } } +#else +#error Update this code. Would you kindly? +#endif } static void furi_hal_infrared_configure_tim_pwm_tx(uint32_t freq, float duty_cycle) { @@ -323,25 +342,25 @@ static void furi_hal_infrared_configure_tim_pwm_tx(uint32_t freq, float duty_cyc LL_TIM_EnableARRPreload(TIM1); LL_TIM_SetAutoReload( TIM1, __LL_TIM_CALC_ARR(SystemCoreClock, LL_TIM_GetPrescaler(TIM1), freq)); -#if INFRARED_TX_DEBUG == 1 - LL_TIM_OC_SetCompareCH1(TIM1, ((LL_TIM_GetAutoReload(TIM1) + 1) * (1 - duty_cycle))); - LL_TIM_OC_EnablePreload(TIM1, LL_TIM_CHANNEL_CH1); - /* LL_TIM_OCMODE_PWM2 set by DMA */ - LL_TIM_OC_SetMode(TIM1, LL_TIM_CHANNEL_CH1, LL_TIM_OCMODE_FORCED_INACTIVE); - LL_TIM_OC_SetPolarity(TIM1, LL_TIM_CHANNEL_CH1N, LL_TIM_OCPOLARITY_HIGH); - LL_TIM_OC_DisableFast(TIM1, LL_TIM_CHANNEL_CH1); - LL_TIM_CC_EnableChannel(TIM1, LL_TIM_CHANNEL_CH1N); - LL_TIM_DisableIT_CC1(TIM1); -#else - LL_TIM_OC_SetCompareCH3(TIM1, ((LL_TIM_GetAutoReload(TIM1) + 1) * (1 - duty_cycle))); - LL_TIM_OC_EnablePreload(TIM1, LL_TIM_CHANNEL_CH3); - /* LL_TIM_OCMODE_PWM2 set by DMA */ - LL_TIM_OC_SetMode(TIM1, LL_TIM_CHANNEL_CH3, LL_TIM_OCMODE_FORCED_INACTIVE); - LL_TIM_OC_SetPolarity(TIM1, LL_TIM_CHANNEL_CH3N, LL_TIM_OCPOLARITY_HIGH); - LL_TIM_OC_DisableFast(TIM1, LL_TIM_CHANNEL_CH3); - LL_TIM_CC_EnableChannel(TIM1, LL_TIM_CHANNEL_CH3N); - LL_TIM_DisableIT_CC3(TIM1); -#endif + if(infrared_external_output) { + LL_TIM_OC_SetCompareCH1(TIM1, ((LL_TIM_GetAutoReload(TIM1) + 1) * (1 - duty_cycle))); + LL_TIM_OC_EnablePreload(TIM1, LL_TIM_CHANNEL_CH1); + /* LL_TIM_OCMODE_PWM2 set by DMA */ + LL_TIM_OC_SetMode(TIM1, LL_TIM_CHANNEL_CH1, LL_TIM_OCMODE_FORCED_INACTIVE); + LL_TIM_OC_SetPolarity(TIM1, LL_TIM_CHANNEL_CH1N, LL_TIM_OCPOLARITY_HIGH); + LL_TIM_OC_DisableFast(TIM1, LL_TIM_CHANNEL_CH1); + LL_TIM_CC_EnableChannel(TIM1, LL_TIM_CHANNEL_CH1N); + LL_TIM_DisableIT_CC1(TIM1); + } else { + LL_TIM_OC_SetCompareCH3(TIM1, ((LL_TIM_GetAutoReload(TIM1) + 1) * (1 - duty_cycle))); + LL_TIM_OC_EnablePreload(TIM1, LL_TIM_CHANNEL_CH3); + /* LL_TIM_OCMODE_PWM2 set by DMA */ + LL_TIM_OC_SetMode(TIM1, LL_TIM_CHANNEL_CH3, LL_TIM_OCMODE_FORCED_INACTIVE); + LL_TIM_OC_SetPolarity(TIM1, LL_TIM_CHANNEL_CH3N, LL_TIM_OCPOLARITY_HIGH); + LL_TIM_OC_DisableFast(TIM1, LL_TIM_CHANNEL_CH3); + LL_TIM_CC_EnableChannel(TIM1, LL_TIM_CHANNEL_CH3N); + LL_TIM_DisableIT_CC3(TIM1); + } LL_TIM_DisableMasterSlaveMode(TIM1); LL_TIM_EnableAllOutputs(TIM1); LL_TIM_DisableIT_UPDATE(TIM1); @@ -353,11 +372,11 @@ static void furi_hal_infrared_configure_tim_pwm_tx(uint32_t freq, float duty_cyc static void furi_hal_infrared_configure_tim_cmgr2_dma_tx(void) { LL_DMA_InitTypeDef dma_config = {0}; -#if INFRARED_TX_DEBUG == 1 - dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (TIM1->CCMR1); -#else - dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (TIM1->CCMR2); -#endif + if(infrared_external_output) { + dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (TIM1->CCMR1); + } else { + dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (TIM1->CCMR2); + } dma_config.MemoryOrM2MDstAddress = (uint32_t)NULL; dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; dma_config.Mode = LL_DMA_MODE_NORMAL; @@ -369,16 +388,19 @@ static void furi_hal_infrared_configure_tim_cmgr2_dma_tx(void) { dma_config.NbData = 0; dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM1_UP; dma_config.Priority = LL_DMA_PRIORITY_VERYHIGH; - LL_DMA_Init(DMA1, LL_DMA_CHANNEL_1, &dma_config); + LL_DMA_Init(IR_DMA_CH1_DEF, &dma_config); - LL_DMA_ClearFlag_TE1(DMA1); - LL_DMA_ClearFlag_TC1(DMA1); +#if IR_DMA_CH1_CHANNEL == LL_DMA_CHANNEL_1 + LL_DMA_ClearFlag_TE1(IR_DMA); + LL_DMA_ClearFlag_TC1(IR_DMA); +#else +#error Update this code. Would you kindly? +#endif - LL_DMA_EnableIT_TE(DMA1, LL_DMA_CHANNEL_1); - LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_EnableIT_TE(IR_DMA_CH1_DEF); + LL_DMA_EnableIT_TC(IR_DMA_CH1_DEF); - furi_hal_interrupt_set_isr_ex( - FuriHalInterruptIdDma1Ch1, 4, furi_hal_infrared_tx_dma_polarity_isr, NULL); + furi_hal_interrupt_set_isr_ex(IR_DMA_CH1_IRQ, 4, furi_hal_infrared_tx_dma_polarity_isr, NULL); } static void furi_hal_infrared_configure_tim_rcr_dma_tx(void) { @@ -394,18 +416,21 @@ static void furi_hal_infrared_configure_tim_rcr_dma_tx(void) { dma_config.NbData = 0; dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM1_UP; dma_config.Priority = LL_DMA_PRIORITY_MEDIUM; - LL_DMA_Init(DMA1, LL_DMA_CHANNEL_2, &dma_config); + LL_DMA_Init(IR_DMA_CH2_DEF, &dma_config); - LL_DMA_ClearFlag_TC2(DMA1); - LL_DMA_ClearFlag_HT2(DMA1); - LL_DMA_ClearFlag_TE2(DMA1); +#if IR_DMA_CH2_CHANNEL == LL_DMA_CHANNEL_2 + LL_DMA_ClearFlag_TC2(IR_DMA); + LL_DMA_ClearFlag_HT2(IR_DMA); + LL_DMA_ClearFlag_TE2(IR_DMA); +#else +#error Update this code. Would you kindly? +#endif - LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_2); - LL_DMA_EnableIT_HT(DMA1, LL_DMA_CHANNEL_2); - LL_DMA_EnableIT_TE(DMA1, LL_DMA_CHANNEL_2); + LL_DMA_EnableIT_TC(IR_DMA_CH2_DEF); + LL_DMA_EnableIT_HT(IR_DMA_CH2_DEF); + LL_DMA_EnableIT_TE(IR_DMA_CH2_DEF); - furi_hal_interrupt_set_isr_ex( - FuriHalInterruptIdDma1Ch2, 5, furi_hal_infrared_tx_dma_isr, NULL); + furi_hal_interrupt_set_isr_ex(IR_DMA_CH2_IRQ, 5, furi_hal_infrared_tx_dma_isr, NULL); } static void furi_hal_infrared_tx_fill_buffer_last(uint8_t buf_num) { @@ -507,14 +532,14 @@ static void furi_hal_infrared_tx_dma_set_polarity(uint8_t buf_num, uint8_t polar furi_assert(buffer->polarity != NULL); FURI_CRITICAL_ENTER(); - bool channel_enabled = LL_DMA_IsEnabledChannel(DMA1, LL_DMA_CHANNEL_1); + bool channel_enabled = LL_DMA_IsEnabledChannel(IR_DMA_CH1_DEF); if(channel_enabled) { - LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_DisableChannel(IR_DMA_CH1_DEF); } - LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_1, (uint32_t)buffer->polarity); - LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_1, buffer->size + polarity_shift); + LL_DMA_SetMemoryAddress(IR_DMA_CH1_DEF, (uint32_t)buffer->polarity); + LL_DMA_SetDataLength(IR_DMA_CH1_DEF, buffer->size + polarity_shift); if(channel_enabled) { - LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_EnableChannel(IR_DMA_CH1_DEF); } FURI_CRITICAL_EXIT(); } @@ -527,14 +552,14 @@ static void furi_hal_infrared_tx_dma_set_buffer(uint8_t buf_num) { /* non-circular mode requires disabled channel before setup */ FURI_CRITICAL_ENTER(); - bool channel_enabled = LL_DMA_IsEnabledChannel(DMA1, LL_DMA_CHANNEL_2); + bool channel_enabled = LL_DMA_IsEnabledChannel(IR_DMA_CH2_DEF); if(channel_enabled) { - LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2); + LL_DMA_DisableChannel(IR_DMA_CH2_DEF); } - LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_2, (uint32_t)buffer->data); - LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_2, buffer->size); + LL_DMA_SetMemoryAddress(IR_DMA_CH2_DEF, (uint32_t)buffer->data); + LL_DMA_SetDataLength(IR_DMA_CH2_DEF, buffer->size); if(channel_enabled) { - LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); + LL_DMA_EnableChannel(IR_DMA_CH2_DEF); } FURI_CRITICAL_EXIT(); } @@ -544,9 +569,13 @@ static void furi_hal_infrared_async_tx_free_resources(void) { (furi_hal_infrared_state == InfraredStateIdle) || (furi_hal_infrared_state == InfraredStateAsyncTxStopped)); - furi_hal_gpio_init(&gpio_infrared_tx, GpioModeOutputOpenDrain, GpioPullDown, GpioSpeedLow); - furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch1, NULL, NULL); - furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch2, NULL, NULL); + if(infrared_external_output) { + furi_hal_gpio_init(&gpio_ext_pa7, GpioModeOutputOpenDrain, GpioPullDown, GpioSpeedLow); + } else { + furi_hal_gpio_init(&gpio_infrared_tx, GpioModeOutputOpenDrain, GpioPullDown, GpioSpeedLow); + } + furi_hal_interrupt_set_isr(IR_DMA_CH1_IRQ, NULL, NULL); + furi_hal_interrupt_set_isr(IR_DMA_CH2_IRQ, NULL, NULL); LL_TIM_DeInit(TIM1); furi_semaphore_free(infrared_tim_tx.stop_semaphore); @@ -597,15 +626,27 @@ void furi_hal_infrared_async_tx_start(uint32_t freq, float duty_cycle) { furi_hal_infrared_state = InfraredStateAsyncTx; LL_TIM_ClearFlag_UPDATE(TIM1); - LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); - LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); + LL_DMA_EnableChannel(IR_DMA_CH1_DEF); + LL_DMA_EnableChannel(IR_DMA_CH2_DEF); furi_delay_us(5); LL_TIM_GenerateEvent_UPDATE(TIM1); /* DMA -> TIMx_RCR */ furi_delay_us(5); - LL_GPIO_ResetOutputPin( - gpio_infrared_tx.port, gpio_infrared_tx.pin); /* when disable it prevents false pulse */ - furi_hal_gpio_init_ex( - &gpio_infrared_tx, GpioModeAltFunctionPushPull, GpioPullUp, GpioSpeedHigh, GpioAltFn1TIM1); + if(infrared_external_output) { + LL_GPIO_ResetOutputPin( + gpio_ext_pa7.port, gpio_ext_pa7.pin); /* when disable it prevents false pulse */ + furi_hal_gpio_init_ex( + &gpio_ext_pa7, GpioModeAltFunctionPushPull, GpioPullUp, GpioSpeedHigh, GpioAltFn1TIM1); + } else { + LL_GPIO_ResetOutputPin( + gpio_infrared_tx.port, + gpio_infrared_tx.pin); /* when disable it prevents false pulse */ + furi_hal_gpio_init_ex( + &gpio_infrared_tx, + GpioModeAltFunctionPushPull, + GpioPullUp, + GpioSpeedHigh, + GpioAltFn1TIM1); + } FURI_CRITICAL_ENTER(); LL_TIM_GenerateEvent_UPDATE(TIM1); /* TIMx_RCR -> Repetition counter */ diff --git a/firmware/targets/f7/furi_hal/furi_hal_interrupt.c b/firmware/targets/f7/furi_hal/furi_hal_interrupt.c index 1b1132d0c..b5639d230 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_interrupt.c +++ b/firmware/targets/f7/furi_hal/furi_hal_interrupt.c @@ -74,6 +74,21 @@ __attribute__((always_inline)) static inline void NVIC_EnableIRQ(furi_hal_interrupt_irqn[index]); } +__attribute__((always_inline)) static inline void + furi_hal_interrupt_clear_pending(FuriHalInterruptId index) { + NVIC_ClearPendingIRQ(furi_hal_interrupt_irqn[index]); +} + +__attribute__((always_inline)) static inline void + furi_hal_interrupt_get_pending(FuriHalInterruptId index) { + NVIC_GetPendingIRQ(furi_hal_interrupt_irqn[index]); +} + +__attribute__((always_inline)) static inline void + furi_hal_interrupt_set_pending(FuriHalInterruptId index) { + NVIC_SetPendingIRQ(furi_hal_interrupt_irqn[index]); +} + __attribute__((always_inline)) static inline void furi_hal_interrupt_disable(FuriHalInterruptId index) { NVIC_DisableIRQ(furi_hal_interrupt_irqn[index]); @@ -123,6 +138,7 @@ void furi_hal_interrupt_set_isr_ex( // Pre ISR clear furi_assert(furi_hal_interrupt_isr[index].isr != NULL); furi_hal_interrupt_disable(index); + furi_hal_interrupt_clear_pending(index); } furi_hal_interrupt_isr[index].isr = isr; @@ -131,6 +147,7 @@ void furi_hal_interrupt_set_isr_ex( if(isr) { // Post ISR set + furi_hal_interrupt_clear_pending(index); furi_hal_interrupt_enable(index, priority); } else { // Post ISR clear diff --git a/firmware/targets/f7/furi_hal/furi_hal_nfc.c b/firmware/targets/f7/furi_hal/furi_hal_nfc.c index ce81fd058..8910d887b 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_nfc.c +++ b/firmware/targets/f7/furi_hal/furi_hal_nfc.c @@ -24,13 +24,29 @@ FuriEventFlag* event = NULL; #define FURI_HAL_NFC_UID_INCOMPLETE (0x04) void furi_hal_nfc_init() { + furi_assert(!event); + event = furi_event_flag_alloc(); + ReturnCode ret = rfalNfcInitialize(); if(ret == ERR_NONE) { furi_hal_nfc_start_sleep(); - event = furi_event_flag_alloc(); FURI_LOG_I(TAG, "Init OK"); } else { - FURI_LOG_W(TAG, "Initialization failed, RFAL returned: %d", ret); + FURI_LOG_W(TAG, "Init Failed, RFAL returned: %d", ret); + } +} + +void furi_hal_nfc_deinit() { + ReturnCode ret = rfalDeinitialize(); + if(ret == ERR_NONE) { + FURI_LOG_I(TAG, "Deinit OK"); + } else { + FURI_LOG_W(TAG, "Deinit Failed, RFAL returned: %d", ret); + } + + if(event) { + furi_event_flag_free(event); + event = NULL; } } diff --git a/firmware/targets/f7/furi_hal/furi_hal_nfc.h b/firmware/targets/f7/furi_hal/furi_hal_nfc.h index d3f6de602..dc3f873f3 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_nfc.h +++ b/firmware/targets/f7/furi_hal/furi_hal_nfc.h @@ -101,6 +101,10 @@ typedef struct { */ void furi_hal_nfc_init(); +/** Deinit nfc + */ +void furi_hal_nfc_deinit(); + /** Check if nfc worker is busy * * @return true if busy diff --git a/firmware/targets/f7/furi_hal/furi_hal_power.c b/firmware/targets/f7/furi_hal/furi_hal_power.c index 2d709620d..dd7c34ae7 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_power.c +++ b/firmware/targets/f7/furi_hal/furi_hal_power.c @@ -341,14 +341,14 @@ bool furi_hal_power_is_otg_enabled() { return ret; } -float furi_hal_power_get_battery_charging_voltage() { +float furi_hal_power_get_battery_charge_voltage_limit() { furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); float ret = (float)bq25896_get_vreg_voltage(&furi_hal_i2c_handle_power) / 1000.0f; furi_hal_i2c_release(&furi_hal_i2c_handle_power); return ret; } -void furi_hal_power_set_battery_charging_voltage(float voltage) { +void furi_hal_power_set_battery_charge_voltage_limit(float voltage) { furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); // Adding 0.0005 is necessary because 4.016f is 4.015999794000, which gets truncated bq25896_set_vreg_voltage(&furi_hal_i2c_handle_power, (uint16_t)(voltage * 1000.0f + 0.0005f)); @@ -486,7 +486,7 @@ void furi_hal_power_info_get(PropertyValueCallback out, char sep, void* context) property_value_out(&property_context, NULL, 2, "format", "major", "2"); property_value_out(&property_context, NULL, 2, "format", "minor", "1"); } else { - property_value_out(&property_context, NULL, 3, "power", "info", "major", "1"); + property_value_out(&property_context, NULL, 3, "power", "info", "major", "2"); property_value_out(&property_context, NULL, 3, "power", "info", "minor", "1"); } @@ -505,8 +505,10 @@ void furi_hal_power_info_get(PropertyValueCallback out, char sep, void* context) } property_value_out(&property_context, NULL, 2, "charge", "state", charge_state); - uint16_t charge_voltage = (uint16_t)(furi_hal_power_get_battery_charging_voltage() * 1000.f); - property_value_out(&property_context, "%u", 2, "charge", "voltage", charge_voltage); + uint16_t charge_voltage_limit = + (uint16_t)(furi_hal_power_get_battery_charge_voltage_limit() * 1000.f); + property_value_out( + &property_context, "%u", 3, "charge", "voltage", "limit", charge_voltage_limit); uint16_t voltage = (uint16_t)(furi_hal_power_get_battery_voltage(FuriHalPowerICFuelGauge) * 1000.f); property_value_out(&property_context, "%u", 2, "battery", "voltage", voltage); diff --git a/firmware/targets/f7/furi_hal/furi_hal_random.c b/firmware/targets/f7/furi_hal/furi_hal_random.c index 780618052..f36407cc1 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_random.c +++ b/firmware/targets/f7/furi_hal/furi_hal_random.c @@ -1,6 +1,7 @@ #include #include #include + #include #include diff --git a/firmware/targets/f7/furi_hal/furi_hal_rfid.c b/firmware/targets/f7/furi_hal/furi_hal_rfid.c index 0ade85e0a..145e49df2 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_rfid.c +++ b/firmware/targets/f7/furi_hal/furi_hal_rfid.c @@ -21,6 +21,14 @@ #define RFID_CAPTURE_IND_CH LL_TIM_CHANNEL_CH3 #define RFID_CAPTURE_DIR_CH LL_TIM_CHANNEL_CH4 +/* DMA Channels definition */ +#define RFID_DMA DMA2 +#define RFID_DMA_CH1_CHANNEL LL_DMA_CHANNEL_1 +#define RFID_DMA_CH2_CHANNEL LL_DMA_CHANNEL_2 +#define RFID_DMA_CH1_IRQ FuriHalInterruptIdDma2Ch1 +#define RFID_DMA_CH1_DEF RFID_DMA, RFID_DMA_CH1_CHANNEL +#define RFID_DMA_CH2_DEF RFID_DMA, RFID_DMA_CH2_CHANNEL + typedef struct { FuriHalRfidEmulateCallback callback; FuriHalRfidDMACallback dma_callback; @@ -69,7 +77,7 @@ void furi_hal_rfid_init() { void furi_hal_rfid_pins_reset() { // ibutton bus disable - furi_hal_ibutton_stop(); + furi_hal_ibutton_pin_reset(); // pulldown rfid antenna furi_hal_gpio_init(&gpio_rfid_carrier_out, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); @@ -86,8 +94,8 @@ void furi_hal_rfid_pins_reset() { void furi_hal_rfid_pins_emulate() { // ibutton low - furi_hal_ibutton_start_drive(); - furi_hal_ibutton_pin_low(); + furi_hal_ibutton_pin_configure(); + furi_hal_ibutton_pin_write(false); // pull pin to timer out furi_hal_gpio_init_ex( @@ -107,8 +115,8 @@ void furi_hal_rfid_pins_emulate() { void furi_hal_rfid_pins_read() { // ibutton low - furi_hal_ibutton_start_drive(); - furi_hal_ibutton_pin_low(); + furi_hal_ibutton_pin_configure(); + furi_hal_ibutton_pin_write(false); // dont pull rfid antenna furi_hal_gpio_init(&gpio_nfc_irq_rfid_pull, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); @@ -302,15 +310,19 @@ void furi_hal_rfid_tim_read_capture_stop() { } static void furi_hal_rfid_dma_isr() { - if(LL_DMA_IsActiveFlag_HT1(DMA1)) { - LL_DMA_ClearFlag_HT1(DMA1); +#if RFID_DMA_CH1_CHANNEL == LL_DMA_CHANNEL_1 + if(LL_DMA_IsActiveFlag_HT1(RFID_DMA)) { + LL_DMA_ClearFlag_HT1(RFID_DMA); furi_hal_rfid->dma_callback(true, furi_hal_rfid->context); } - if(LL_DMA_IsActiveFlag_TC1(DMA1)) { - LL_DMA_ClearFlag_TC1(DMA1); + if(LL_DMA_IsActiveFlag_TC1(RFID_DMA)) { + LL_DMA_ClearFlag_TC1(RFID_DMA); furi_hal_rfid->dma_callback(false, furi_hal_rfid->context); } +#else +#error Update this code. Would you kindly? +#endif } void furi_hal_rfid_tim_emulate_dma_start( @@ -347,8 +359,8 @@ void furi_hal_rfid_tim_emulate_dma_start( dma_config.NbData = length; dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; dma_config.Priority = LL_DMA_MODE_NORMAL; - LL_DMA_Init(DMA1, LL_DMA_CHANNEL_1, &dma_config); - LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_Init(RFID_DMA_CH1_DEF, &dma_config); + LL_DMA_EnableChannel(RFID_DMA_CH1_DEF); // configure DMA "mem -> CCR3" channel #if FURI_HAL_RFID_EMULATE_TIMER_CHANNEL == LL_TIM_CHANNEL_CH3 @@ -366,13 +378,13 @@ void furi_hal_rfid_tim_emulate_dma_start( dma_config.NbData = length; dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; dma_config.Priority = LL_DMA_MODE_NORMAL; - LL_DMA_Init(DMA1, LL_DMA_CHANNEL_2, &dma_config); - LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); + LL_DMA_Init(RFID_DMA_CH2_DEF, &dma_config); + LL_DMA_EnableChannel(RFID_DMA_CH2_DEF); // attach interrupt to one of DMA channels - furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch1, furi_hal_rfid_dma_isr, NULL); - LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_1); - LL_DMA_EnableIT_HT(DMA1, LL_DMA_CHANNEL_1); + furi_hal_interrupt_set_isr(RFID_DMA_CH1_IRQ, furi_hal_rfid_dma_isr, NULL); + LL_DMA_EnableIT_TC(RFID_DMA_CH1_DEF); + LL_DMA_EnableIT_HT(RFID_DMA_CH1_DEF); // start LL_TIM_EnableAllOutputs(FURI_HAL_RFID_EMULATE_TIMER); @@ -385,14 +397,14 @@ void furi_hal_rfid_tim_emulate_dma_stop() { LL_TIM_DisableCounter(FURI_HAL_RFID_EMULATE_TIMER); LL_TIM_DisableAllOutputs(FURI_HAL_RFID_EMULATE_TIMER); - furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch1, NULL, NULL); - LL_DMA_DisableIT_TC(DMA1, LL_DMA_CHANNEL_1); - LL_DMA_DisableIT_HT(DMA1, LL_DMA_CHANNEL_1); + furi_hal_interrupt_set_isr(RFID_DMA_CH1_IRQ, NULL, NULL); + LL_DMA_DisableIT_TC(RFID_DMA_CH1_DEF); + LL_DMA_DisableIT_HT(RFID_DMA_CH1_DEF); FURI_CRITICAL_ENTER(); - LL_DMA_DeInit(DMA1, LL_DMA_CHANNEL_1); - LL_DMA_DeInit(DMA1, LL_DMA_CHANNEL_2); + LL_DMA_DeInit(RFID_DMA_CH1_DEF); + LL_DMA_DeInit(RFID_DMA_CH2_DEF); LL_TIM_DeInit(FURI_HAL_RFID_EMULATE_TIMER); FURI_CRITICAL_EXIT(); @@ -471,4 +483,4 @@ void COMP_IRQHandler() { (LL_COMP_ReadOutputLevel(COMP1) == LL_COMP_OUTPUT_LEVEL_LOW), furi_hal_rfid_comp_callback_context); } -} +} \ No newline at end of file diff --git a/firmware/targets/f7/furi_hal/furi_hal_rtc.c b/firmware/targets/f7/furi_hal/furi_hal_rtc.c index 72a2921c1..e011406ad 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_rtc.c +++ b/firmware/targets/f7/furi_hal/furi_hal_rtc.c @@ -409,4 +409,4 @@ uint32_t furi_hal_rtc_datetime_to_timestamp(FuriHalRtcDateTime* datetime) { timestamp += datetime->second; return timestamp; -} \ No newline at end of file +} diff --git a/firmware/targets/f7/furi_hal/furi_hal_subghz.c b/firmware/targets/f7/furi_hal/furi_hal_subghz.c index 0fe5d1878..77cedf109 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_subghz.c +++ b/firmware/targets/f7/furi_hal/furi_hal_subghz.c @@ -21,7 +21,6 @@ #define INIT_TIMEOUT 10 static uint32_t furi_hal_subghz_debug_gpio_buff[2]; -static bool last_OTG_state = false; /* DMA Channels definition */ #define SUBGHZ_DMA DMA2 @@ -39,18 +38,22 @@ volatile FuriHalSubGhz furi_hal_subghz = { .radio_type = SubGhzRadioInternal, .spi_bus_handle = &furi_hal_spi_bus_handle_subghz, .cc1101_g0_pin = &gpio_cc1101_g0, + .rolling_counter_mult = 1, + .ext_module_power_disabled = false, }; bool furi_hal_subghz_set_radio_type(SubGhzRadioType state) { furi_hal_subghz.radio_type = state; furi_hal_spi_bus_handle_deinit(furi_hal_subghz.spi_bus_handle); - if(state) { - furi_hal_subghz.spi_bus_handle = &furi_hal_spi_bus_handle_subghz_ext; - furi_hal_subghz.cc1101_g0_pin = &gpio_cc1101_g0_ext; - } else { + + if(furi_hal_subghz.radio_type == SubGhzRadioInternal) { furi_hal_subghz.spi_bus_handle = &furi_hal_spi_bus_handle_subghz; furi_hal_subghz.cc1101_g0_pin = &gpio_cc1101_g0; + } else { + furi_hal_subghz.spi_bus_handle = &furi_hal_spi_bus_handle_subghz_ext; + furi_hal_subghz.cc1101_g0_pin = &gpio_cc1101_g0_ext; } + furi_hal_spi_bus_handle_init(furi_hal_subghz.spi_bus_handle); furi_hal_subghz_init_check(); return true; @@ -60,6 +63,22 @@ SubGhzRadioType furi_hal_subghz_get_radio_type(void) { return furi_hal_subghz.radio_type; } +uint8_t furi_hal_subghz_get_rolling_counter_mult(void) { + return furi_hal_subghz.rolling_counter_mult; +} + +void furi_hal_subghz_set_rolling_counter_mult(uint8_t mult) { + furi_hal_subghz.rolling_counter_mult = mult; +} + +void furi_hal_subghz_set_external_power_disable(bool state) { + furi_hal_subghz.ext_module_power_disabled = state; +} + +bool furi_hal_subghz_get_external_power_disable(void) { + return furi_hal_subghz.ext_module_power_disabled; +} + void furi_hal_subghz_set_async_mirror_pin(const GpioPin* pin) { furi_hal_subghz.async_mirror_pin = pin; } @@ -68,14 +87,23 @@ void furi_hal_subghz_init(void) { furi_hal_subghz_init_check(); } -void furi_hal_subghz_enable_ext_power(void) { - if(furi_hal_subghz.radio_type != SubGhzRadioInternal && !furi_hal_power_is_otg_enabled()) { - furi_hal_power_enable_otg(); +bool furi_hal_subghz_enable_ext_power(void) { + if(furi_hal_subghz.ext_module_power_disabled) { + return false; } + if(furi_hal_subghz.radio_type != SubGhzRadioInternal) { + uint8_t attempts = 0; + while(!furi_hal_power_is_otg_enabled() && attempts++ < 2) { + furi_hal_power_enable_otg(); + //CC1101 power-up time + furi_delay_ms(5); + } + } + return furi_hal_power_is_otg_enabled(); } void furi_hal_subghz_disable_ext_power(void) { - if(furi_hal_subghz.radio_type != SubGhzRadioInternal && !last_OTG_state) { + if(furi_hal_power_is_otg_enabled()) { furi_hal_power_disable_otg(); } } @@ -83,9 +111,8 @@ void furi_hal_subghz_disable_ext_power(void) { bool furi_hal_subghz_check_radio(void) { bool result = true; - furi_hal_subghz_enable_ext_power(); - furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle); + uint8_t ver = cc1101_get_version(furi_hal_subghz.spi_bus_handle); furi_hal_spi_release(furi_hal_subghz.spi_bus_handle); @@ -93,7 +120,7 @@ bool furi_hal_subghz_check_radio(void) { FURI_LOG_D(TAG, "Radio check ok"); } else { FURI_LOG_D(TAG, "Radio check failed"); - furi_hal_subghz_disable_ext_power(); + result = false; } return result; @@ -106,8 +133,6 @@ bool furi_hal_subghz_init_check(void) { furi_hal_subghz.state = SubGhzStateIdle; furi_hal_subghz.preset = FuriHalSubGhzPresetIDLE; - last_OTG_state = furi_hal_power_is_otg_enabled(); - furi_hal_subghz_enable_ext_power(); furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle); #ifdef FURI_HAL_SUBGHZ_TX_GPIO @@ -121,37 +146,26 @@ bool furi_hal_subghz_init_check(void) { // Prepare GD0 for power on self test furi_hal_gpio_init(furi_hal_subghz.cc1101_g0_pin, GpioModeInput, GpioPullNo, GpioSpeedLow); - if(furi_hal_subghz.radio_type == SubGhzRadioExternal) { - // GD0 low - cc1101_write_reg(furi_hal_subghz.spi_bus_handle, CC1101_IOCFG0, CC1101IocfgHW); - uint32_t test_start_time = furi_get_tick(); - while(furi_hal_gpio_read(furi_hal_subghz.cc1101_g0_pin) != false && result) { - if(furi_get_tick() - test_start_time > INIT_TIMEOUT) { - result = false; - } - } - // GD0 high - cc1101_write_reg( - furi_hal_subghz.spi_bus_handle, CC1101_IOCFG0, CC1101IocfgHW | CC1101_IOCFG_INV); - test_start_time = furi_get_tick(); - while(furi_hal_gpio_read(furi_hal_subghz.cc1101_g0_pin) != true && result) { - if(furi_get_tick() - test_start_time > INIT_TIMEOUT) { - result = false; - } + // GD0 low + cc1101_write_reg(furi_hal_subghz.spi_bus_handle, CC1101_IOCFG0, CC1101IocfgHW); + uint32_t test_start_time = furi_get_tick(); + while(furi_hal_gpio_read(furi_hal_subghz.cc1101_g0_pin) != false && result) { + if(furi_get_tick() - test_start_time > INIT_TIMEOUT) { + result = false; } - } else { - // GD0 low - cc1101_write_reg(furi_hal_subghz.spi_bus_handle, CC1101_IOCFG0, CC1101IocfgHW); - while(furi_hal_gpio_read(&gpio_cc1101_g0) != false) - ; - - // GD0 high - cc1101_write_reg( - furi_hal_subghz.spi_bus_handle, CC1101_IOCFG0, CC1101IocfgHW | CC1101_IOCFG_INV); - while(furi_hal_gpio_read(&gpio_cc1101_g0) != true) - ; } + + // GD0 high + cc1101_write_reg( + furi_hal_subghz.spi_bus_handle, CC1101_IOCFG0, CC1101IocfgHW | CC1101_IOCFG_INV); + test_start_time = furi_get_tick(); + while(furi_hal_gpio_read(furi_hal_subghz.cc1101_g0_pin) != true && result) { + if(furi_get_tick() - test_start_time > INIT_TIMEOUT) { + result = false; + } + } + // Reset GD0 to floating state cc1101_write_reg(furi_hal_subghz.spi_bus_handle, CC1101_IOCFG0, CC1101IocfgHighImpedance); furi_hal_gpio_init(furi_hal_subghz.cc1101_g0_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); @@ -169,7 +183,6 @@ bool furi_hal_subghz_init_check(void) { FURI_LOG_I(TAG, "Init OK"); } else { FURI_LOG_E(TAG, "Failed to initialization"); - furi_hal_subghz_disable_ext_power(); } return result; } @@ -187,8 +200,6 @@ void furi_hal_subghz_sleep() { furi_hal_spi_release(furi_hal_subghz.spi_bus_handle); - furi_hal_subghz_disable_ext_power(); - furi_hal_subghz.preset = FuriHalSubGhzPresetIDLE; } @@ -333,7 +344,6 @@ void furi_hal_subghz_shutdown() { // Reset and shutdown cc1101_shutdown(furi_hal_subghz.spi_bus_handle); furi_hal_spi_release(furi_hal_subghz.spi_bus_handle); - furi_hal_subghz_disable_ext_power(); } void furi_hal_subghz_reset() { @@ -419,23 +429,48 @@ uint32_t furi_hal_subghz_set_frequency_and_path(uint32_t value) { return value; } -bool furi_hal_subghz_is_tx_allowed(uint32_t value) { - bool is_extended = false; - bool is_allowed = false; - - // TODO: !!! Move file check to another place +void furi_hal_subghz_get_extend_settings(bool* extend, bool* bypass) { + *extend = false; + *bypass = false; Storage* storage = furi_record_open(RECORD_STORAGE); - FlipperFormat* fff_data_file = flipper_format_file_alloc(storage); + FlipperFormat* file = flipper_format_file_alloc(storage); - if(flipper_format_file_open_existing(fff_data_file, "/ext/subghz/assets/extend_range.txt")) { - flipper_format_read_bool(fff_data_file, "use_ext_range_at_own_risk", &is_extended, 1); - flipper_format_read_bool(fff_data_file, "ignore_default_tx_region", &is_allowed, 1); + if(flipper_format_file_open_existing(file, "/ext/subghz/assets/extend_range.txt")) { + flipper_format_read_bool(file, "use_ext_range_at_own_risk", extend, 1); + flipper_format_read_bool(file, "ignore_default_tx_region", bypass, 1); } - flipper_format_free(fff_data_file); + flipper_format_free(file); furi_record_close(RECORD_STORAGE); +} - switch(furi_hal_version_get_hw_region()) { +void furi_hal_subghz_set_extend_settings(bool extend, bool bypass) { + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* file = flipper_format_file_alloc(storage); + + do { + if(!flipper_format_file_open_always(file, "/ext/subghz/assets/extend_range.txt")) break; + if(!flipper_format_write_header_cstr(file, "Flipper SubGhz Setting File", 1)) break; + if(!flipper_format_write_comment_cstr( + file, "Whether to allow extended ranges that can break your flipper")) + break; + if(!flipper_format_write_bool(file, "use_ext_range_at_own_risk", &extend, 1)) break; + if(!flipper_format_write_comment_cstr( + file, "Whether to ignore the default TX region settings")) + break; + if(!flipper_format_write_bool(file, "ignore_default_tx_region", &bypass, 1)) break; + } while(0); + + flipper_format_free(file); + furi_record_close(RECORD_STORAGE); +} + +bool furi_hal_subghz_is_tx_allowed(uint32_t value) { + bool is_extended; + bool is_allowed; + furi_hal_subghz_get_extend_settings(&is_extended, &is_allowed); + + switch(furi_hal_version_get_hw_region_otp()) { case FuriHalVersionRegionEuRu: //433,05..434,79; 868,15..868,55 if(!(value >= 433050000 && value <= 434790000) && @@ -474,8 +509,8 @@ bool furi_hal_subghz_is_tx_allowed(uint32_t value) { break; } // No flag - test original range, flag set, test extended range - if(!(value >= 299999755 && value <= 348000335) && - !(value >= 386999938 && value <= 464000000) && + if(!(value >= 299999755 && value <= 350000335) && // was increased from 348 to 350 + !(value >= 386999938 && value <= 467750000) && // was increased from 464 to 467.75 !(value >= 778999847 && value <= 928000000) && !(is_extended)) { FURI_LOG_I(TAG, "Frequency blocked - outside regional range"); return false; @@ -491,11 +526,8 @@ bool furi_hal_subghz_is_tx_allowed(uint32_t value) { } uint32_t furi_hal_subghz_set_frequency(uint32_t value) { - if(furi_hal_region_is_frequency_allowed(value)) { - furi_hal_subghz.regulation = SubGhzRegulationTxRx; - } else { - furi_hal_subghz.regulation = SubGhzRegulationTxRx; - } + furi_hal_subghz.regulation = SubGhzRegulationTxRx; + furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle); uint32_t real_frequency = cc1101_set_frequency(furi_hal_subghz.spi_bus_handle, value); cc1101_calibrate(furi_hal_subghz.spi_bus_handle); @@ -558,56 +590,55 @@ volatile uint32_t furi_hal_subghz_capture_delta_duration = 0; volatile FuriHalSubGhzCaptureCallback furi_hal_subghz_capture_callback = NULL; volatile void* furi_hal_subghz_capture_callback_context = NULL; -static void furi_hal_subghz_capture_ISR() { - if(furi_hal_subghz.radio_type == SubGhzRadioExternal) { - if(!furi_hal_gpio_read(furi_hal_subghz.cc1101_g0_pin)) { - if(furi_hal_subghz_capture_callback) { - if(furi_hal_subghz.async_mirror_pin != NULL) - furi_hal_gpio_write(furi_hal_subghz.async_mirror_pin, false); +static void furi_hal_subghz_capture_int_ISR() { + // Channel 1 + if(LL_TIM_IsActiveFlag_CC1(TIM2)) { + LL_TIM_ClearFlag_CC1(TIM2); + furi_hal_subghz_capture_delta_duration = LL_TIM_IC_GetCaptureCH1(TIM2); + if(furi_hal_subghz_capture_callback) { + if(furi_hal_subghz.async_mirror_pin != NULL) + furi_hal_gpio_write(furi_hal_subghz.async_mirror_pin, false); - furi_hal_subghz_capture_callback( - true, TIM2->CNT, (void*)furi_hal_subghz_capture_callback_context); - } - } else { - if(furi_hal_subghz_capture_callback) { - if(furi_hal_subghz.async_mirror_pin != NULL) - furi_hal_gpio_write(furi_hal_subghz.async_mirror_pin, true); - - furi_hal_subghz_capture_callback( - false, TIM2->CNT, (void*)furi_hal_subghz_capture_callback_context); - } - } - //Forced correction for improved accuracy - TIM2->CNT = 9; - } else { - // Channel 1 - if(LL_TIM_IsActiveFlag_CC1(TIM2)) { - LL_TIM_ClearFlag_CC1(TIM2); - furi_hal_subghz_capture_delta_duration = LL_TIM_IC_GetCaptureCH1(TIM2); - if(furi_hal_subghz_capture_callback) { - if(furi_hal_subghz.async_mirror_pin != NULL) - furi_hal_gpio_write(furi_hal_subghz.async_mirror_pin, false); - - furi_hal_subghz_capture_callback( - true, - furi_hal_subghz_capture_delta_duration, - (void*)furi_hal_subghz_capture_callback_context); - } - } - // Channel 2 - if(LL_TIM_IsActiveFlag_CC2(TIM2)) { - LL_TIM_ClearFlag_CC2(TIM2); - if(furi_hal_subghz_capture_callback) { - if(furi_hal_subghz.async_mirror_pin != NULL) - furi_hal_gpio_write(furi_hal_subghz.async_mirror_pin, true); - - furi_hal_subghz_capture_callback( - false, - LL_TIM_IC_GetCaptureCH2(TIM2) - furi_hal_subghz_capture_delta_duration, - (void*)furi_hal_subghz_capture_callback_context); - } + furi_hal_subghz_capture_callback( + true, + furi_hal_subghz_capture_delta_duration, + (void*)furi_hal_subghz_capture_callback_context); } } + // Channel 2 + if(LL_TIM_IsActiveFlag_CC2(TIM2)) { + LL_TIM_ClearFlag_CC2(TIM2); + if(furi_hal_subghz_capture_callback) { + if(furi_hal_subghz.async_mirror_pin != NULL) + furi_hal_gpio_write(furi_hal_subghz.async_mirror_pin, true); + + furi_hal_subghz_capture_callback( + false, + LL_TIM_IC_GetCaptureCH2(TIM2) - furi_hal_subghz_capture_delta_duration, + (void*)furi_hal_subghz_capture_callback_context); + } + } +} + +static void furi_hal_subghz_capture_ext_ISR() { + if(!furi_hal_gpio_read(furi_hal_subghz.cc1101_g0_pin)) { + if(furi_hal_subghz_capture_callback) { + if(furi_hal_subghz.async_mirror_pin != NULL) + furi_hal_gpio_write(furi_hal_subghz.async_mirror_pin, false); + + furi_hal_subghz_capture_callback( + true, TIM2->CNT, (void*)furi_hal_subghz_capture_callback_context); + } + } else { + if(furi_hal_subghz_capture_callback) { + if(furi_hal_subghz.async_mirror_pin != NULL) + furi_hal_gpio_write(furi_hal_subghz.async_mirror_pin, true); + + furi_hal_subghz_capture_callback( + false, TIM2->CNT, (void*)furi_hal_subghz_capture_callback_context); + } + } + TIM2->CNT = 6; } void furi_hal_subghz_start_async_rx(FuriHalSubGhzCaptureCallback callback, void* context) { @@ -617,43 +648,27 @@ void furi_hal_subghz_start_async_rx(FuriHalSubGhzCaptureCallback callback, void* furi_hal_subghz_capture_callback = callback; furi_hal_subghz_capture_callback_context = context; - if(furi_hal_subghz.radio_type == SubGhzRadioExternal) { - furi_hal_gpio_init( - furi_hal_subghz.cc1101_g0_pin, - GpioModeInterruptRiseFall, - GpioPullUp, - GpioSpeedVeryHigh); - furi_hal_gpio_add_int_callback( - furi_hal_subghz.cc1101_g0_pin, - furi_hal_subghz_capture_ISR, - furi_hal_subghz_capture_callback); - furi_hal_gpio_enable_int_callback(furi_hal_subghz.cc1101_g0_pin); - } else { - furi_hal_gpio_init_ex( - &gpio_cc1101_g0, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn1TIM2); - } - // Timer: base LL_TIM_InitTypeDef TIM_InitStruct = {0}; TIM_InitStruct.Prescaler = 64 - 1; TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP; TIM_InitStruct.Autoreload = 0x7FFFFFFE; - TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV4; // Clock division for capture filter + // Clock division for capture filter (for internal radio) + TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV4; LL_TIM_Init(TIM2, &TIM_InitStruct); // Timer: advanced LL_TIM_SetClockSource(TIM2, LL_TIM_CLOCKSOURCE_INTERNAL); LL_TIM_DisableARRPreload(TIM2); + LL_TIM_DisableDMAReq_TRIG(TIM2); + LL_TIM_DisableIT_TRIG(TIM2); + if(furi_hal_subghz.radio_type == SubGhzRadioInternal) { LL_TIM_SetTriggerInput(TIM2, LL_TIM_TS_TI2FP2); LL_TIM_SetSlaveMode(TIM2, LL_TIM_SLAVEMODE_RESET); LL_TIM_SetTriggerOutput(TIM2, LL_TIM_TRGO_RESET); LL_TIM_EnableMasterSlaveMode(TIM2); - } - LL_TIM_DisableDMAReq_TRIG(TIM2); - LL_TIM_DisableIT_TRIG(TIM2); - if(furi_hal_subghz.radio_type == SubGhzRadioInternal) { // Timer: channel 1 indirect LL_TIM_IC_SetActiveInput(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_ACTIVEINPUT_INDIRECTTI); LL_TIM_IC_SetPrescaler(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_ICPSC_DIV1); @@ -667,13 +682,31 @@ void furi_hal_subghz_start_async_rx(FuriHalSubGhzCaptureCallback callback, void* LL_TIM_IC_SetFilter(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_IC_FILTER_FDIV32_N8); // ISR setup - furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, furi_hal_subghz_capture_ISR, NULL); + furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, furi_hal_subghz_capture_int_ISR, NULL); // Interrupts and channels LL_TIM_EnableIT_CC1(TIM2); LL_TIM_EnableIT_CC2(TIM2); LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH1); LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH2); + + furi_hal_gpio_init_ex( + furi_hal_subghz.cc1101_g0_pin, + GpioModeAltFunctionPushPull, + GpioPullNo, + GpioSpeedLow, + GpioAltFn1TIM2); + } else { + furi_hal_gpio_init( + furi_hal_subghz.cc1101_g0_pin, + GpioModeInterruptRiseFall, + GpioPullUp, + GpioSpeedVeryHigh); + furi_hal_gpio_add_int_callback( + furi_hal_subghz.cc1101_g0_pin, + furi_hal_subghz_capture_ext_ISR, + furi_hal_subghz_capture_callback); + furi_hal_gpio_enable_int_callback(furi_hal_subghz.cc1101_g0_pin); } // Start timer @@ -706,6 +739,9 @@ void furi_hal_subghz_stop_async_rx() { FURI_CRITICAL_EXIT(); if(furi_hal_subghz.radio_type == SubGhzRadioInternal) { furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, NULL, NULL); + } else { + furi_hal_gpio_disable_int_callback(furi_hal_subghz.cc1101_g0_pin); + furi_hal_gpio_remove_int_callback(furi_hal_subghz.cc1101_g0_pin); } furi_hal_gpio_init(furi_hal_subghz.cc1101_g0_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); @@ -840,18 +876,19 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* furi_hal_subghz_async_tx.buffer = malloc(API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * sizeof(uint32_t)); - if(furi_hal_subghz.radio_type == SubGhzRadioExternal) { - furi_hal_gpio_write(furi_hal_subghz.cc1101_g0_pin, true); - furi_hal_gpio_init( - furi_hal_subghz.cc1101_g0_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); - } else { + if(furi_hal_subghz.radio_type == SubGhzRadioInternal) { // Connect CC1101_GD0 to TIM2 as output furi_hal_gpio_init_ex( - &gpio_cc1101_g0, + furi_hal_subghz.cc1101_g0_pin, GpioModeAltFunctionPushPull, GpioPullDown, GpioSpeedLow, GpioAltFn1TIM2); + } else { + //Signal generation with mem-to-mem DMA + furi_hal_gpio_write(furi_hal_subghz.cc1101_g0_pin, true); + furi_hal_gpio_init( + furi_hal_subghz.cc1101_g0_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); } // Configure DMA @@ -912,22 +949,28 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* LL_TIM_SetCounter(TIM2, 0); LL_TIM_EnableCounter(TIM2); - //Signal generation for external module + // Start debug + if(furi_hal_subghz_start_debug() || furi_hal_subghz.radio_type == SubGhzRadioExternal) { + const GpioPin* gpio = furi_hal_subghz.cc1101_g0_pin; + //Preparing bit mask + //Debug pin is may be only PORTB! (PB0, PB1, .., PB15) + furi_hal_subghz_debug_gpio_buff[0] = 0; + furi_hal_subghz_debug_gpio_buff[1] = 0; - // Start debug (and speaker) - furi_hal_subghz_start_debug(); + //Mirror pin (for example, speaker) + if(furi_hal_subghz.async_mirror_pin != NULL) { + furi_hal_subghz_debug_gpio_buff[0] |= (uint32_t)furi_hal_subghz.async_mirror_pin->pin + << GPIO_NUMBER; + furi_hal_subghz_debug_gpio_buff[1] |= furi_hal_subghz.async_mirror_pin->pin; + gpio = furi_hal_subghz.async_mirror_pin; + } - const GpioPin* gpio = furi_hal_subghz.cc1101_g0_pin; - - if((furi_hal_subghz.async_mirror_pin != NULL) && - (furi_hal_subghz.radio_type == SubGhzRadioInternal)) { - gpio = furi_hal_subghz.async_mirror_pin; - } - if(((furi_hal_subghz.async_mirror_pin != NULL) && - (furi_hal_subghz.radio_type == SubGhzRadioInternal)) || - (furi_hal_subghz.radio_type == SubGhzRadioExternal)) { - furi_hal_subghz_debug_gpio_buff[0] = (uint32_t)gpio->pin << GPIO_NUMBER; - furi_hal_subghz_debug_gpio_buff[1] = gpio->pin; + //G0 singnal generation for external radio + if(furi_hal_subghz.radio_type == SubGhzRadioExternal) { + furi_hal_subghz_debug_gpio_buff[0] |= (uint32_t)furi_hal_subghz.cc1101_g0_pin->pin + << GPIO_NUMBER; + furi_hal_subghz_debug_gpio_buff[1] |= furi_hal_subghz.cc1101_g0_pin->pin; + } dma_config.MemoryOrM2MDstAddress = (uint32_t)furi_hal_subghz_debug_gpio_buff; dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (gpio->port->BSRR); diff --git a/firmware/targets/f7/furi_hal/furi_hal_subghz.h b/firmware/targets/f7/furi_hal/furi_hal_subghz.h index b19a71f9a..1a42d84ff 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_subghz.h +++ b/firmware/targets/f7/furi_hal/furi_hal_subghz.h @@ -77,6 +77,8 @@ typedef struct { SubGhzRadioType radio_type; FuriHalSpiBusHandle* spi_bus_handle; const GpioPin* cc1101_g0_pin; + uint8_t rolling_counter_mult; + bool ext_module_power_disabled; } FuriHalSubGhz; extern volatile FuriHalSubGhz furi_hal_subghz; @@ -222,6 +224,20 @@ bool furi_hal_subghz_is_frequency_valid(uint32_t value); */ uint32_t furi_hal_subghz_set_frequency_and_path(uint32_t value); +/** Read extend and bypass settings values into out params + * + * @param extend pointer to bool for extend + * @param bypass pointer to bool for bypass + */ +void furi_hal_subghz_get_extend_settings(bool* extend, bool* bypass); + +/** Set extend and bypass settings values to file + * + * @param extend bool for extend + * @param bypass bool for bypass + */ +void furi_hal_subghz_set_extend_settings(bool extend, bool bypass); + /** Сheck if transmission is allowed on this frequency with your current config * * @param value frequency in Hz @@ -302,13 +318,32 @@ SubGhzRadioType furi_hal_subghz_get_radio_type(void); bool furi_hal_subghz_check_radio(void); /** Turn on the power of the external radio module + * @return true if power-up is successful */ -void furi_hal_subghz_enable_ext_power(void); +bool furi_hal_subghz_enable_ext_power(void); /** Turn off the power of the external radio module */ void furi_hal_subghz_disable_ext_power(void); +/** Get the current rolling protocols counter ++ value + * @return uint8_t current value + */ +uint8_t furi_hal_subghz_get_rolling_counter_mult(void); + +/** Set the current rolling protocols counter ++ value + * @param mult uint8_t = 1, 2, 4, 8 + */ +void furi_hal_subghz_set_rolling_counter_mult(uint8_t mult); + +/** If true - disable 5v power of the external radio module + */ +void furi_hal_subghz_set_external_power_disable(bool state); + +/** Get the current state of the external power disable flag + */ +bool furi_hal_subghz_get_external_power_disable(void); + #ifdef __cplusplus } #endif diff --git a/firmware/targets/f7/furi_hal/furi_hal_uart.h b/firmware/targets/f7/furi_hal/furi_hal_uart.h index 07211db8b..3ba4dc483 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_uart.h +++ b/firmware/targets/f7/furi_hal/furi_hal_uart.h @@ -31,7 +31,7 @@ typedef enum { /** * Init UART - * Configures GPIO to UART function, сonfigures UART hardware, enables UART hardware + * Configures GPIO to UART function, configures UART hardware, enables UART hardware * @param channel UART channel * @param baud baudrate */ diff --git a/firmware/targets/f7/furi_hal/furi_hal_version.c b/firmware/targets/f7/furi_hal/furi_hal_version.c index 643a30e6e..607560460 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_version.c +++ b/firmware/targets/f7/furi_hal/furi_hal_version.c @@ -271,8 +271,16 @@ FuriHalVersionRegion furi_hal_version_get_hw_region() { return FuriHalVersionRegionUnknown; } +FuriHalVersionRegion furi_hal_version_get_hw_region_otp() { + return furi_hal_version.board_region; +} + const char* furi_hal_version_get_hw_region_name() { - switch(furi_hal_version_get_hw_region()) { + return "R00"; +} + +const char* furi_hal_version_get_hw_region_name_otp() { + switch(furi_hal_version_get_hw_region_otp()) { case FuriHalVersionRegionUnknown: return "R00"; case FuriHalVersionRegionEuRu: diff --git a/firmware/targets/f7/src/recovery.c b/firmware/targets/f7/src/recovery.c index d56be4fe0..db538b0d5 100644 --- a/firmware/targets/f7/src/recovery.c +++ b/firmware/targets/f7/src/recovery.c @@ -5,24 +5,20 @@ #include #include -#define COUNTER_VALUE (100U) +#define COUNTER_VALUE (136U) static void flipper_boot_recovery_draw_splash(u8g2_t* fb, size_t progress) { - u8g2_ClearBuffer(fb); - u8g2_SetDrawColor(fb, 0x01); - - u8g2_SetFont(fb, u8g2_font_helvB08_tr); - u8g2_DrawStr(fb, 2, 8, "PIN and Factory Reset"); - u8g2_SetFont(fb, u8g2_font_haxrcorp4089_tr); - u8g2_DrawStr(fb, 2, 21, "Hold Right to confirm"); - u8g2_DrawStr(fb, 2, 31, "Press Down to cancel"); - if(progress < COUNTER_VALUE) { - size_t width = progress / (COUNTER_VALUE / 100); - u8g2_DrawBox(fb, 14 + (50 - width / 2), 54, width, 3); + // Fill the progress bar while the progress is going down + u8g2_SetDrawColor(fb, 0x01); + u8g2_DrawRFrame(fb, 59, 41, 69, 8, 2); + size_t width = (COUNTER_VALUE - progress) * 68 / COUNTER_VALUE; + u8g2_DrawBox(fb, 60, 42, width, 6); + } else { + u8g2_SetDrawColor(fb, 0x00); + u8g2_DrawRBox(fb, 59, 41, 69, 8, 2); } - u8g2_SetPowerSave(fb, 0); u8g2_SendBuffer(fb); } @@ -31,6 +27,18 @@ void flipper_boot_recovery_exec() { u8g2_Setup_st756x_flipper(fb, U8G2_R0, u8x8_hw_spi_stm32, u8g2_gpio_and_delay_stm32); u8g2_InitDisplay(fb); + furi_hal_compress_icon_init(); + uint8_t* splash_data = NULL; + furi_hal_compress_icon_decode(icon_get_data(&I_Erase_pin_128x64), &splash_data); + + u8g2_ClearBuffer(fb); + u8g2_SetDrawColor(fb, 0x01); + + // Draw the recovery picture + u8g2_DrawXBM(fb, 0, 0, 128, 64, splash_data); + u8g2_SendBuffer(fb); + u8g2_SetPowerSave(fb, 0); + size_t counter = COUNTER_VALUE; while(counter) { if(!furi_hal_gpio_read(&gpio_button_down)) { diff --git a/firmware/targets/f7/src/update.c b/firmware/targets/f7/src/update.c index b223a5dc3..d8d26eb7c 100644 --- a/firmware/targets/f7/src/update.c +++ b/firmware/targets/f7/src/update.c @@ -23,8 +23,8 @@ static FATFS* pfs = NULL; } static bool flipper_update_mount_sd() { - for(int i = 0; i < BSP_SD_MaxMountRetryCount(); ++i) { - if(BSP_SD_Init((i % 2) == 0) != MSD_OK) { + for(int i = 0; i < sd_max_mount_retry_count(); ++i) { + if(sd_init((i % 2) == 0) != SdSpiStatusOK) { /* Next attempt will be without card reset, let it settle */ furi_delay_ms(1000); continue; diff --git a/firmware/targets/furi_hal_include/furi_hal_infrared.h b/firmware/targets/furi_hal_include/furi_hal_infrared.h index 5fcea0661..bac3aba1e 100644 --- a/firmware/targets/furi_hal_include/furi_hal_infrared.h +++ b/firmware/targets/furi_hal_include/furi_hal_infrared.h @@ -48,6 +48,12 @@ typedef void (*FuriHalInfraredRxCaptureCallback)(void* ctx, bool level, uint32_t */ typedef void (*FuriHalInfraredRxTimeoutCallback)(void* ctx); +// Debug TX pin set +void furi_hal_infrared_set_debug_out(bool enable); + +// Debug TX pin get status +bool furi_hal_infrared_get_debug_out_status(void); + /** Initialize INFRARED RX timer to receive interrupts. * * It provides interrupts for every RX-signal edge changing with its duration. diff --git a/firmware/targets/furi_hal_include/furi_hal_power.h b/firmware/targets/furi_hal_include/furi_hal_power.h index 39a11e99f..462e20e41 100644 --- a/firmware/targets/furi_hal_include/furi_hal_power.h +++ b/firmware/targets/furi_hal_include/furi_hal_power.h @@ -121,21 +121,21 @@ void furi_hal_power_check_otg_status(); */ bool furi_hal_power_is_otg_enabled(); -/** Get battery charging voltage in V +/** Get battery charge voltage limit in V * * @return voltage in V */ -float furi_hal_power_get_battery_charging_voltage(); +float furi_hal_power_get_battery_charge_voltage_limit(); -/** Set battery charging voltage in V +/** Set battery charge voltage limit in V * - * Invalid values will be clamped to the nearest valid value. + * Invalid values will be clamped downward to the nearest valid value. * * @param voltage[in] voltage in V * * @return voltage in V */ -void furi_hal_power_set_battery_charging_voltage(float voltage); +void furi_hal_power_set_battery_charge_voltage_limit(float voltage); /** Get remaining battery battery capacity in mAh * diff --git a/firmware/targets/furi_hal_include/furi_hal_rtc.h b/firmware/targets/furi_hal_include/furi_hal_rtc.h index 8d40353a2..fe095e749 100644 --- a/firmware/targets/furi_hal_include/furi_hal_rtc.h +++ b/firmware/targets/furi_hal_include/furi_hal_rtc.h @@ -78,32 +78,81 @@ typedef enum { /** Early initialization */ void furi_hal_rtc_init_early(); -/** Early deinitialization */ +/** Early de-initialization */ void furi_hal_rtc_deinit_early(); /** Initialize RTC subsystem */ void furi_hal_rtc_init(); +/** Get RTC register content + * + * @param[in] reg The register identifier + * + * @return content of the register + */ uint32_t furi_hal_rtc_get_register(FuriHalRtcRegister reg); +/** Set register content + * + * @param[in] reg The register identifier + * @param[in] value The value to store into register + */ void furi_hal_rtc_set_register(FuriHalRtcRegister reg, uint32_t value); +/** Set Log Level value + * + * @param[in] level The level to store + */ void furi_hal_rtc_set_log_level(uint8_t level); +/** Get Log Level value + * + * @return The Log Level value + */ uint8_t furi_hal_rtc_get_log_level(); +/** Set RTC Flag + * + * @param[in] flag The flag to set + */ void furi_hal_rtc_set_flag(FuriHalRtcFlag flag); +/** Reset RTC Flag + * + * @param[in] flag The flag to reset + */ void furi_hal_rtc_reset_flag(FuriHalRtcFlag flag); +/** Check if RTC Flag is set + * + * @param[in] flag The flag to check + * + * @return true if set + */ bool furi_hal_rtc_is_flag_set(FuriHalRtcFlag flag); +/** Set RTC boot mode + * + * @param[in] mode The mode to set + */ void furi_hal_rtc_set_boot_mode(FuriHalRtcBootMode mode); +/** Get RTC boot mode + * + * @return The RTC boot mode. + */ FuriHalRtcBootMode furi_hal_rtc_get_boot_mode(); +/** Set Heap Track mode + * + * @param[in] mode The mode to set + */ void furi_hal_rtc_set_heap_track_mode(FuriHalRtcHeapTrackMode mode); +/** Get RTC Heap Track mode + * + * @return The RTC heap track mode. + */ FuriHalRtcHeapTrackMode furi_hal_rtc_get_heap_track_mode(); /** Set locale units @@ -146,23 +195,58 @@ FuriHalRtcLocaleDateFormat furi_hal_rtc_get_locale_dateformat(); * * @param datetime The date time to set */ - void furi_hal_rtc_set_datetime(FuriHalRtcDateTime* datetime); +/** Get RTC Date Time + * + * @param datetime The datetime + */ void furi_hal_rtc_get_datetime(FuriHalRtcDateTime* datetime); +/** Validate Date Time + * + * @param datetime The datetime to validate + * + * @return { description_of_the_return_value } + */ bool furi_hal_rtc_validate_datetime(FuriHalRtcDateTime* datetime); +/** Set RTC Fault Data + * + * @param[in] value The value + */ void furi_hal_rtc_set_fault_data(uint32_t value); +/** Get RTC Fault Data + * + * @return RTC Fault Data value + */ uint32_t furi_hal_rtc_get_fault_data(); +/** Set Pin Fails count + * + * @param[in] value The Pin Fails count + */ void furi_hal_rtc_set_pin_fails(uint32_t value); +/** Get Pin Fails count + * + * @return Pin Fails Count + */ uint32_t furi_hal_rtc_get_pin_fails(); +/** Get UNIX Timestamp + * + * @return Unix Timestamp in seconds from UNIX epoch start + */ uint32_t furi_hal_rtc_get_timestamp(); +/** Convert DateTime to UNIX timestamp + * + * @param datetime The datetime + * + * @return UNIX Timestamp in seconds from UNIX epoch start + */ uint32_t furi_hal_rtc_datetime_to_timestamp(FuriHalRtcDateTime* datetime); #ifdef __cplusplus diff --git a/firmware/targets/furi_hal_include/furi_hal_version.h b/firmware/targets/furi_hal_include/furi_hal_version.h index 889b29777..b81602029 100644 --- a/firmware/targets/furi_hal_include/furi_hal_version.h +++ b/firmware/targets/furi_hal_include/furi_hal_version.h @@ -122,17 +122,29 @@ FuriHalVersionColor furi_hal_version_get_hw_color(); */ uint8_t furi_hal_version_get_hw_connect(); -/** Get hardware region +/** Get hardware region (fake) = 0 * - * @return Hardware Region + * @return Hardware Region (fake) */ FuriHalVersionRegion furi_hal_version_get_hw_region(); -/** Get hardware region name +/** Get hardware region name (fake) = R00 + * + * @return Hardware Region name (fake) + */ +const char* furi_hal_version_get_hw_region_name(); + +/** Get hardware region (OTP) + * + * @return Hardware Region + */ +FuriHalVersionRegion furi_hal_version_get_hw_region_otp(); + +/** Get hardware region name (OTP) * * @return Hardware Region name */ -const char* furi_hal_version_get_hw_region_name(); +const char* furi_hal_version_get_hw_region_name_otp(); /** Get hardware display id * diff --git a/furi/core/event_flag.c b/furi/core/event_flag.c index 87de65f2d..9406a581f 100644 --- a/furi/core/event_flag.c +++ b/furi/core/event_flag.c @@ -32,7 +32,7 @@ uint32_t furi_event_flag_set(FuriEventFlag* instance, uint32_t flags) { if(FURI_IS_IRQ_MODE()) { yield = pdFALSE; if(xEventGroupSetBitsFromISR(hEventGroup, (EventBits_t)flags, &yield) == pdFAIL) { - rflags = (uint32_t)FuriStatusErrorResource; + rflags = (uint32_t)FuriFlagErrorResource; } else { rflags = flags; portYIELD_FROM_ISR(yield); diff --git a/furi/core/log.c b/furi/core/log.c index a3967ed92..d910ecf21 100644 --- a/furi/core/log.c +++ b/furi/core/log.c @@ -28,27 +28,27 @@ void furi_log_print_format(FuriLogLevel level, const char* tag, const char* form FuriString* string; string = furi_string_alloc(); - const char* color = FURI_LOG_CLR_RESET; + const char* color = _FURI_LOG_CLR_RESET; const char* log_letter = " "; switch(level) { case FuriLogLevelError: - color = FURI_LOG_CLR_E; + color = _FURI_LOG_CLR_E; log_letter = "E"; break; case FuriLogLevelWarn: - color = FURI_LOG_CLR_W; + color = _FURI_LOG_CLR_W; log_letter = "W"; break; case FuriLogLevelInfo: - color = FURI_LOG_CLR_I; + color = _FURI_LOG_CLR_I; log_letter = "I"; break; case FuriLogLevelDebug: - color = FURI_LOG_CLR_D; + color = _FURI_LOG_CLR_D; log_letter = "D"; break; case FuriLogLevelTrace: - color = FURI_LOG_CLR_T; + color = _FURI_LOG_CLR_T; log_letter = "T"; break; default: @@ -58,7 +58,7 @@ void furi_log_print_format(FuriLogLevel level, const char* tag, const char* form // Timestamp furi_string_printf( string, - "%lu %s[%s][%s] " FURI_LOG_CLR_RESET, + "%lu %s[%s][%s] " _FURI_LOG_CLR_RESET, furi_log.timestamp(), color, log_letter, @@ -80,6 +80,23 @@ void furi_log_print_format(FuriLogLevel level, const char* tag, const char* form } } +void furi_log_print_raw_format(FuriLogLevel level, const char* format, ...) { + if(level <= furi_log.log_level && + furi_mutex_acquire(furi_log.mutex, FuriWaitForever) == FuriStatusOk) { + FuriString* string; + string = furi_string_alloc(); + va_list args; + va_start(args, format); + furi_string_vprintf(string, format, args); + va_end(args); + + furi_log.puts(furi_string_get_cstr(string)); + furi_string_free(string); + + furi_mutex_release(furi_log.mutex); + } +} + void furi_log_set_level(FuriLogLevel level) { if(level == FuriLogLevelDefault) { level = FURI_LOG_LEVEL_DEFAULT; diff --git a/furi/core/log.h b/furi/core/log.h index cb8b3d9cc..46ae7f007 100644 --- a/furi/core/log.h +++ b/furi/core/log.h @@ -22,21 +22,21 @@ typedef enum { FuriLogLevelTrace = 6, } FuriLogLevel; -#define FURI_LOG_CLR(clr) "\033[0;" clr "m" -#define FURI_LOG_CLR_RESET "\033[0m" +#define _FURI_LOG_CLR(clr) "\033[0;" clr "m" +#define _FURI_LOG_CLR_RESET "\033[0m" -#define FURI_LOG_CLR_BLACK "30" -#define FURI_LOG_CLR_RED "31" -#define FURI_LOG_CLR_GREEN "32" -#define FURI_LOG_CLR_BROWN "33" -#define FURI_LOG_CLR_BLUE "34" -#define FURI_LOG_CLR_PURPLE "35" +#define _FURI_LOG_CLR_BLACK "30" +#define _FURI_LOG_CLR_RED "31" +#define _FURI_LOG_CLR_GREEN "32" +#define _FURI_LOG_CLR_BROWN "33" +#define _FURI_LOG_CLR_BLUE "34" +#define _FURI_LOG_CLR_PURPLE "35" -#define FURI_LOG_CLR_E FURI_LOG_CLR(FURI_LOG_CLR_RED) -#define FURI_LOG_CLR_W FURI_LOG_CLR(FURI_LOG_CLR_BROWN) -#define FURI_LOG_CLR_I FURI_LOG_CLR(FURI_LOG_CLR_GREEN) -#define FURI_LOG_CLR_D FURI_LOG_CLR(FURI_LOG_CLR_BLUE) -#define FURI_LOG_CLR_T FURI_LOG_CLR(FURI_LOG_CLR_PURPLE) +#define _FURI_LOG_CLR_E _FURI_LOG_CLR(_FURI_LOG_CLR_RED) +#define _FURI_LOG_CLR_W _FURI_LOG_CLR(_FURI_LOG_CLR_BROWN) +#define _FURI_LOG_CLR_I _FURI_LOG_CLR(_FURI_LOG_CLR_GREEN) +#define _FURI_LOG_CLR_D _FURI_LOG_CLR(_FURI_LOG_CLR_BLUE) +#define _FURI_LOG_CLR_T _FURI_LOG_CLR(_FURI_LOG_CLR_PURPLE) typedef void (*FuriLogPuts)(const char* data); typedef uint32_t (*FuriLogTimestamp)(void); @@ -54,6 +54,15 @@ void furi_log_init(); void furi_log_print_format(FuriLogLevel level, const char* tag, const char* format, ...) _ATTRIBUTE((__format__(__printf__, 3, 4))); +/** Print log record + * + * @param level + * @param format + * @param ... + */ +void furi_log_print_raw_format(FuriLogLevel level, const char* format, ...) + _ATTRIBUTE((__format__(__printf__, 2, 3))); + /** Set log level * * @param[in] level The level @@ -95,6 +104,22 @@ void furi_log_set_timestamp(FuriLogTimestamp timestamp); #define FURI_LOG_T(tag, format, ...) \ furi_log_print_format(FuriLogLevelTrace, tag, format, ##__VA_ARGS__) +/** Log methods + * + * @param format The raw format + * @param ... VA Args + */ +#define FURI_LOG_RAW_E(format, ...) \ + furi_log_print_raw_format(FuriLogLevelError, format, ##__VA_ARGS__) +#define FURI_LOG_RAW_W(format, ...) \ + furi_log_print_raw_format(FuriLogLevelWarn, format, ##__VA_ARGS__) +#define FURI_LOG_RAW_I(format, ...) \ + furi_log_print_raw_format(FuriLogLevelInfo, format, ##__VA_ARGS__) +#define FURI_LOG_RAW_D(format, ...) \ + furi_log_print_raw_format(FuriLogLevelDebug, format, ##__VA_ARGS__) +#define FURI_LOG_RAW_T(format, ...) \ + furi_log_print_raw_format(FuriLogLevelTrace, format, ##__VA_ARGS__) + #ifdef __cplusplus } #endif diff --git a/furi/core/thread.c b/furi/core/thread.c index ef9560b4a..b45651c29 100644 --- a/furi/core/thread.c +++ b/furi/core/thread.c @@ -35,6 +35,8 @@ struct FuriThread { void* state_context; char* name; + char* appid; + configSTACK_DEPTH_TYPE stack_size; FuriThreadPriority priority; @@ -96,9 +98,9 @@ static void furi_thread_body(void* context) { furi_assert(thread->state == FuriThreadStateRunning); if(thread->is_service) { - FURI_LOG_E( + FURI_LOG_W( TAG, - "%s service thread exited. Thread memory cannot be reclaimed.", + "%s service thread TCB memory will not be reclaimed", thread->name ? thread->name : ""); } @@ -122,11 +124,25 @@ FuriThread* furi_thread_alloc() { thread->output.buffer = furi_string_alloc(); thread->is_service = false; + FuriThread* parent = NULL; + if(xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) { + // TLS is not available, if we called not from thread context + parent = pvTaskGetThreadLocalStoragePointer(NULL, 0); + + if(parent && parent->appid) { + furi_thread_set_appid(thread, parent->appid); + } else { + furi_thread_set_appid(thread, "unknown"); + } + } else { + // if scheduler is not started, we are starting driver thread + furi_thread_set_appid(thread, "driver"); + } + FuriHalRtcHeapTrackMode mode = furi_hal_rtc_get_heap_track_mode(); if(mode == FuriHalRtcHeapTrackModeAll) { thread->heap_trace_enabled = true; } else if(mode == FuriHalRtcHeapTrackModeTree && furi_thread_get_current_id()) { - FuriThread* parent = pvTaskGetThreadLocalStoragePointer(NULL, 0); if(parent) thread->heap_trace_enabled = parent->heap_trace_enabled; } else { thread->heap_trace_enabled = false; @@ -153,6 +169,7 @@ void furi_thread_free(FuriThread* thread) { furi_assert(thread->state == FuriThreadStateStopped); if(thread->name) free((void*)thread->name); + if(thread->appid) free((void*)thread->appid); furi_string_free(thread->output.buffer); free(thread); @@ -165,6 +182,13 @@ void furi_thread_set_name(FuriThread* thread, const char* name) { thread->name = name ? strdup(name) : NULL; } +void furi_thread_set_appid(FuriThread* thread, const char* appid) { + furi_assert(thread); + furi_assert(thread->state == FuriThreadStateStopped); + if(thread->appid) free((void*)thread->appid); + thread->appid = appid ? strdup(appid) : NULL; +} + void furi_thread_mark_as_service(FuriThread* thread) { thread->is_service = true; } @@ -195,6 +219,15 @@ void furi_thread_set_priority(FuriThread* thread, FuriThreadPriority priority) { thread->priority = priority; } +void furi_thread_set_current_priority(FuriThreadPriority priority) { + UBaseType_t new_priority = priority ? priority : FuriThreadPriorityNormal; + vTaskPrioritySet(NULL, new_priority); +} + +FuriThreadPriority furi_thread_get_current_priority() { + return (FuriThreadPriority)uxTaskPriorityGet(NULL); +} + void furi_thread_set_state_callback(FuriThread* thread, FuriThreadStateCallback callback) { furi_assert(thread); furi_assert(thread->state == FuriThreadStateStopped); @@ -489,6 +522,20 @@ const char* furi_thread_get_name(FuriThreadId thread_id) { return (name); } +const char* furi_thread_get_appid(FuriThreadId thread_id) { + TaskHandle_t hTask = (TaskHandle_t)thread_id; + const char* appid = "system"; + + if(!FURI_IS_IRQ_MODE() && (hTask != NULL)) { + FuriThread* thread = (FuriThread*)pvTaskGetThreadLocalStoragePointer(hTask, 0); + if(thread) { + appid = thread->appid; + } + } + + return (appid); +} + uint32_t furi_thread_get_stack_space(FuriThreadId thread_id) { TaskHandle_t hTask = (TaskHandle_t)thread_id; uint32_t sz; diff --git a/furi/core/thread.h b/furi/core/thread.h index 1542d5bf0..8f4398419 100644 --- a/furi/core/thread.h +++ b/furi/core/thread.h @@ -87,6 +87,16 @@ void furi_thread_free(FuriThread* thread); */ void furi_thread_set_name(FuriThread* thread, const char* name); +/** + * @brief Set FuriThread appid + * Technically, it is like a "process id", but it is not a system-wide unique identifier. + * All threads spawned by the same app will have the same appid. + * + * @param thread + * @param appid + */ +void furi_thread_set_appid(FuriThread* thread, const char* appid); + /** Mark thread as service * The service cannot be stopped or removed, and cannot exit from the thread body * @@ -122,6 +132,18 @@ void furi_thread_set_context(FuriThread* thread, void* context); */ void furi_thread_set_priority(FuriThread* thread, FuriThreadPriority priority); +/** Set current thread priority + * + * @param priority FuriThreadPriority value + */ +void furi_thread_set_current_priority(FuriThreadPriority priority); + +/** Get current thread priority + * + * @return FuriThreadPriority value + */ +FuriThreadPriority furi_thread_get_current_priority(); + /** Set FuriThread state change callback * * @param thread FuriThread instance @@ -221,10 +243,37 @@ uint32_t furi_thread_flags_get(void); uint32_t furi_thread_flags_wait(uint32_t flags, uint32_t options, uint32_t timeout); +/** + * @brief Enumerate threads + * + * @param thread_array array of FuriThreadId, where thread ids will be stored + * @param array_items array size + * @return uint32_t threads count + */ uint32_t furi_thread_enumerate(FuriThreadId* thread_array, uint32_t array_items); +/** + * @brief Get thread name + * + * @param thread_id + * @return const char* name or NULL + */ const char* furi_thread_get_name(FuriThreadId thread_id); +/** + * @brief Get thread appid + * + * @param thread_id + * @return const char* appid + */ +const char* furi_thread_get_appid(FuriThreadId thread_id); + +/** + * @brief Get thread stack watermark + * + * @param thread_id + * @return uint32_t + */ uint32_t furi_thread_get_stack_space(FuriThreadId thread_id); /** Get STDOUT callback for thead diff --git a/furi/core/valuemutex.c b/furi/core/valuemutex.c deleted file mode 100644 index bf4e6130b..000000000 --- a/furi/core/valuemutex.c +++ /dev/null @@ -1,59 +0,0 @@ -#include "valuemutex.h" - -#include - -bool init_mutex(ValueMutex* valuemutex, void* value, size_t size) { - // mutex without name, - // no attributes (unfortunately robust mutex is not supported by FreeRTOS), - // with dynamic memory allocation - valuemutex->mutex = furi_mutex_alloc(FuriMutexTypeNormal); - if(valuemutex->mutex == NULL) return false; - - valuemutex->value = value; - valuemutex->size = size; - - return true; -} - -bool delete_mutex(ValueMutex* valuemutex) { - if(furi_mutex_acquire(valuemutex->mutex, FuriWaitForever) == FuriStatusOk) { - furi_mutex_free(valuemutex->mutex); - return true; - } else { - return false; - } -} - -void* acquire_mutex(ValueMutex* valuemutex, uint32_t timeout) { - if(furi_mutex_acquire(valuemutex->mutex, timeout) == FuriStatusOk) { - return valuemutex->value; - } else { - return NULL; - } -} - -bool release_mutex(ValueMutex* valuemutex, const void* value) { - if(value != valuemutex->value) return false; - - if(furi_mutex_release(valuemutex->mutex) != FuriStatusOk) return false; - - return true; -} - -bool read_mutex(ValueMutex* valuemutex, void* data, size_t len, uint32_t timeout) { - void* value = acquire_mutex(valuemutex, timeout); - if(value == NULL || len > valuemutex->size) return false; - memcpy(data, value, len > 0 ? len : valuemutex->size); - if(!release_mutex(valuemutex, value)) return false; - - return true; -} - -bool write_mutex(ValueMutex* valuemutex, void* data, size_t len, uint32_t timeout) { - void* value = acquire_mutex(valuemutex, timeout); - if(value == NULL || len > valuemutex->size) return false; - memcpy(value, data, len > 0 ? len : valuemutex->size); - if(!release_mutex(valuemutex, value)) return false; - - return true; -} diff --git a/furi/core/valuemutex.h b/furi/core/valuemutex.h deleted file mode 100644 index 0d867a1df..000000000 --- a/furi/core/valuemutex.h +++ /dev/null @@ -1,149 +0,0 @@ -#pragma once - -#include -#include "mutex.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * == ValueMutex == - - * The most simple concept is ValueMutex. - * It is wrapper around mutex and value pointer. - * You can take and give mutex to work with value and read and write value. - */ - -typedef struct { - void* value; - size_t size; - FuriMutex* mutex; -} ValueMutex; - -/** - * Creates ValueMutex. - */ -bool init_mutex(ValueMutex* valuemutex, void* value, size_t size); - -/** - * Free resources allocated by `init_mutex`. - * This function doesn't free the memory occupied by `ValueMutex` itself. - */ -bool delete_mutex(ValueMutex* valuemutex); - -/** - * Call for work with data stored in mutex. - * @return pointer to data if success, NULL otherwise. - */ -void* acquire_mutex(ValueMutex* valuemutex, uint32_t timeout); - -/** - * Helper: infinitely wait for mutex - */ -static inline void* acquire_mutex_block(ValueMutex* valuemutex) { - return acquire_mutex(valuemutex, FuriWaitForever); -} - -/** - * With statement for value mutex, acts as lambda - * @param name a resource name, const char* - * @param function_body a (){} lambda declaration, - * executed within you parent function context. - */ -#define with_value_mutex(value_mutex, function_body) \ - { \ - void* p = acquire_mutex_block(value_mutex); \ - furi_check(p); \ - ({ void __fn__ function_body __fn__; })(p); \ - release_mutex(value_mutex, p); \ - } - -/** - * Release mutex after end of work with data. - * Call `release_mutex` and pass ValueData instance and pointer to data. - */ -bool release_mutex(ValueMutex* valuemutex, const void* value); - -/** - * Instead of take-access-give sequence you can use `read_mutex` and `write_mutex` functions. - * Both functions return true in case of success, false otherwise. - */ -bool read_mutex(ValueMutex* valuemutex, void* data, size_t len, uint32_t timeout); - -bool write_mutex(ValueMutex* valuemutex, void* data, size_t len, uint32_t timeout); - -inline static bool write_mutex_block(ValueMutex* valuemutex, void* data, size_t len) { - return write_mutex(valuemutex, data, len, FuriWaitForever); -} - -inline static bool read_mutex_block(ValueMutex* valuemutex, void* data, size_t len) { - return read_mutex(valuemutex, data, len, FuriWaitForever); -} - -#ifdef __cplusplus -} -#endif - -/* - -Usage example - -```C -// MANIFEST -// name="example-provider-app" -// stack=128 - -void provider_app(void* _p) { - // create record with mutex - uint32_t example_value = 0; - ValueMutex example_mutex; - // call `init_mutex`. - if(!init_mutex(&example_mutex, (void*)&example_value, sizeof(uint32_t))) { - printf("critical error\n"); - flapp_exit(NULL); - } - - furi_record_create("provider/example", (void*)&example_mutex); - - // we are ready to provide record to other apps - flapp_ready(); - - // get value and increment it - while(1) { - uint32_t* value = acquire_mutex(&example_mutex, OsWaitForever); - if(value != NULL) { - value++; - } - release_mutex(&example_mutex, value); - - furi_delay_ms(100); - } -} - -// MANIFEST -// name="example-consumer-app" -// stack=128 -// require="example-provider-app" -void consumer_app(void* _p) { - // this app run after flapp_ready call in all requirements app - - // open mutex value - ValueMutex* counter_mutex = furi_record_open("provider/example"); - if(counter_mutex == NULL) { - printf("critical error\n"); - flapp_exit(NULL); - } - - // continuously read value every 1s - uint32_t counter; - while(1) { - if(read_mutex(counter_mutex, &counter, sizeof(counter), OsWaitForever)) { - printf("counter value: %d\n", counter); - } - - furi_delay_ms(1000); - } -} -``` -*/ diff --git a/furi/flipper.c b/furi/flipper.c index 73899e58b..f0147c060 100644 --- a/furi/flipper.c +++ b/furi/flipper.c @@ -3,6 +3,7 @@ #include #include #include +#include #define TAG "Flipper" @@ -29,10 +30,10 @@ static void flipper_print_version(const char* target, const Version* version) { void flipper_init() { flipper_print_version("Firmware", furi_hal_version_get_firmware_version()); - FURI_LOG_I(TAG, "starting services"); + FURI_LOG_I(TAG, "Boot mode %d, starting services", furi_hal_rtc_get_boot_mode()); for(size_t i = 0; i < FLIPPER_SERVICES_COUNT; i++) { - FURI_LOG_I(TAG, "starting service %s", FLIPPER_SERVICES[i].name); + FURI_LOG_I(TAG, "Starting service %s", FLIPPER_SERVICES[i].name); FuriThread* thread = furi_thread_alloc_ex( FLIPPER_SERVICES[i].name, @@ -40,11 +41,12 @@ void flipper_init() { FLIPPER_SERVICES[i].app, NULL); furi_thread_mark_as_service(thread); + furi_thread_set_appid(thread, FLIPPER_SERVICES[i].appid); furi_thread_start(thread); } - FURI_LOG_I(TAG, "services startup complete"); + FURI_LOG_I(TAG, "Startup complete"); } void vApplicationGetIdleTaskMemory( diff --git a/furi/furi.h b/furi/furi.h index 3ce834227..cfdeb2c0f 100644 --- a/furi/furi.h +++ b/furi/furi.h @@ -16,7 +16,6 @@ #include "core/semaphore.h" #include "core/thread.h" #include "core/timer.h" -#include "core/valuemutex.h" #include "core/string.h" #include "core/stream_buffer.h" diff --git a/lib/SConscript b/lib/SConscript index d72ef7700..2de801597 100644 --- a/lib/SConscript +++ b/lib/SConscript @@ -43,6 +43,7 @@ env.Append( "variant", ) ), + File("u8g2/u8g2.h"), ], CPPDEFINES=[ '"M_MEMORY_FULL(x)=abort()"', diff --git a/lib/drivers/bq25896.c b/lib/drivers/bq25896.c index 7e3008d62..4c1d687cb 100644 --- a/lib/drivers/bq25896.c +++ b/lib/drivers/bq25896.c @@ -140,19 +140,16 @@ uint16_t bq25896_get_vreg_voltage(FuriHalI2cBusHandle* handle) { void bq25896_set_vreg_voltage(FuriHalI2cBusHandle* handle, uint16_t vreg_voltage) { if(vreg_voltage < 3840) { - // Minimum value is 3840 mV - bq25896_regs.r06.VREG = 0; - } else { - // Find the nearest voltage value (subtract offset, divide into sections) - // Values are truncated downward as needed (e.g. 4200mV -> 4192 mV) - bq25896_regs.r06.VREG = (uint8_t)((vreg_voltage - 3840) / 16); + // Minimum valid value is 3840 mV + vreg_voltage = 3840; + } else if(vreg_voltage > 4208) { + // Maximum safe value is 4208 mV + vreg_voltage = 4208; } - // Do not allow values above 23 (0x17, 4208mV) - // Exceeding 4.2v will overcharge the battery! - if(bq25896_regs.r06.VREG > 23) { - bq25896_regs.r06.VREG = 23; - } + // Find the nearest voltage value (subtract offset, divide into sections) + // Values are truncated downward as needed (e.g. 4200mV -> 4192 mV) + bq25896_regs.r06.VREG = (uint8_t)((vreg_voltage - 3840) / 16); // Apply changes furi_hal_i2c_write_reg_8( diff --git a/lib/drivers/bq25896.h b/lib/drivers/bq25896.h index c8a8526a1..f3d1d0e05 100644 --- a/lib/drivers/bq25896.h +++ b/lib/drivers/bq25896.h @@ -36,10 +36,10 @@ void bq25896_disable_otg(FuriHalI2cBusHandle* handle); /** Is otg enabled */ bool bq25896_is_otg_enabled(FuriHalI2cBusHandle* handle); -/** Get VREG (charging) voltage in mV */ +/** Get VREG (charging limit) voltage in mV */ uint16_t bq25896_get_vreg_voltage(FuriHalI2cBusHandle* handle); -/** Set VREG (charging) voltage in mV +/** Set VREG (charging limit) voltage in mV * * Valid range: 3840mV - 4208mV, in steps of 16mV */ diff --git a/lib/drivers/cc1101.c b/lib/drivers/cc1101.c index d563c30c3..d0feb0218 100644 --- a/lib/drivers/cc1101.c +++ b/lib/drivers/cc1101.c @@ -150,9 +150,8 @@ uint8_t cc1101_write_fifo(FuriHalSpiBusHandle* handle, const uint8_t* data, uint } uint8_t cc1101_read_fifo(FuriHalSpiBusHandle* handle, uint8_t* data, uint8_t* size) { - uint8_t buff_tx[64]; - buff_tx[0] = CC1101_FIFO | CC1101_READ | CC1101_BURST; - uint8_t buff_rx[2]; + uint8_t buff_trx[2]; + buff_trx[0] = CC1101_FIFO | CC1101_READ | CC1101_BURST; // Start transaction // Wait IC to become ready @@ -160,15 +159,15 @@ uint8_t cc1101_read_fifo(FuriHalSpiBusHandle* handle, uint8_t* data, uint8_t* si ; // First byte - packet length - furi_hal_spi_bus_trx(handle, buff_tx, buff_rx, 2, CC1101_TIMEOUT); + furi_hal_spi_bus_trx(handle, buff_trx, buff_trx, 2, CC1101_TIMEOUT); // Check that the packet is placed in the receive buffer - if(buff_rx[1] > 64) { + if(buff_trx[1] > 64) { *size = 64; } else { - *size = buff_rx[1]; + *size = buff_trx[1]; } - furi_hal_spi_bus_trx(handle, &buff_tx[1], data, *size, CC1101_TIMEOUT); + furi_hal_spi_bus_trx(handle, NULL, data, *size, CC1101_TIMEOUT); return *size; -} +} \ No newline at end of file diff --git a/lib/flipper_format/flipper_format.c b/lib/flipper_format/flipper_format.c index 292dab5f1..bb1aa59f5 100644 --- a/lib/flipper_format/flipper_format.c +++ b/lib/flipper_format/flipper_format.c @@ -91,6 +91,12 @@ bool flipper_format_file_open_always(FlipperFormat* flipper_format, const char* return file_stream_open(flipper_format->stream, path, FSAM_READ_WRITE, FSOM_CREATE_ALWAYS); } +bool flipper_format_buffered_file_open_always(FlipperFormat* flipper_format, const char* path) { + furi_assert(flipper_format); + return buffered_file_stream_open( + flipper_format->stream, path, FSAM_READ_WRITE, FSOM_CREATE_ALWAYS); +} + bool flipper_format_file_open_new(FlipperFormat* flipper_format, const char* path) { furi_assert(flipper_format); return file_stream_open(flipper_format->stream, path, FSAM_READ_WRITE, FSOM_CREATE_NEW); diff --git a/lib/flipper_format/flipper_format.h b/lib/flipper_format/flipper_format.h index 743918e3b..671ff6fa0 100644 --- a/lib/flipper_format/flipper_format.h +++ b/lib/flipper_format/flipper_format.h @@ -131,7 +131,7 @@ bool flipper_format_file_open_existing(FlipperFormat* flipper_format, const char /** * Open existing file, buffered mode. - * Use only if FlipperFormat allocated as a file. + * Use only if FlipperFormat allocated as a buffered file. * @param flipper_format Pointer to a FlipperFormat instance * @param path File path * @return True on success @@ -156,6 +156,15 @@ bool flipper_format_file_open_append(FlipperFormat* flipper_format, const char* */ bool flipper_format_file_open_always(FlipperFormat* flipper_format, const char* path); +/** + * Open file. Creates a new file, or deletes the contents of the file if it already exists, buffered mode. + * Use only if FlipperFormat allocated as a buffered file. + * @param flipper_format Pointer to a FlipperFormat instance + * @param path File path + * @return True on success + */ +bool flipper_format_buffered_file_open_always(FlipperFormat* flipper_format, const char* path); + /** * Open file. Creates a new file, fails if file already exists. * Use only if FlipperFormat allocated as a file. diff --git a/lib/misc.scons b/lib/misc.scons index b0afa932a..7a70670d1 100644 --- a/lib/misc.scons +++ b/lib/misc.scons @@ -1,5 +1,7 @@ Import("env") +from fbt.util import GLOB_FILE_EXCLUSION + env.Append( CPPPATH=[ "#/lib/digital_signal", @@ -41,7 +43,11 @@ libs_plain = [ ] for lib in libs_plain: - sources += Glob(lib + "/*.c*", source=True) + sources += Glob( + lib + "/*.c*", + exclude=GLOB_FILE_EXCLUSION, + source=True, + ) lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources) libenv.Install("${LIB_DIST_DIR}", lib) diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index 4138bf033..d77029b61 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -61,6 +61,8 @@ void nfc_worker_start( while(furi_hal_nfc_is_busy()) { furi_delay_ms(10); } + furi_hal_nfc_deinit(); + furi_hal_nfc_init(); nfc_worker->callback = callback; nfc_worker->context = context; @@ -900,6 +902,32 @@ void nfc_worker_emulate_mf_ultralight(NfcWorker* nfc_worker) { } } +static bool nfc_worker_mf_get_b_key_from_sector_trailer( + FuriHalNfcTxRxContext* tx_rx, + uint16_t sector, + uint64_t key, + uint64_t* found_key) { + // Some access conditions allow reading B key via A key + + uint8_t block = mf_classic_get_sector_trailer_block_num_by_sector(sector); + + Crypto1 crypto = {}; + MfClassicBlock block_tmp = {}; + MfClassicAuthContext auth_context = {.sector = sector, .key_a = MF_CLASSIC_NO_KEY, .key_b = 0}; + + furi_hal_nfc_sleep(); + + if(mf_classic_auth_attempt(tx_rx, &crypto, &auth_context, key)) { + if(mf_classic_read_block(tx_rx, &crypto, block, &block_tmp)) { + *found_key = nfc_util_bytes2num(&block_tmp.value[10], sizeof(uint8_t) * 6); + + return *found_key; + } + } + + return false; +} + static void nfc_worker_mf_classic_key_attack( NfcWorker* nfc_worker, uint64_t key, @@ -945,6 +973,16 @@ static void nfc_worker_mf_classic_key_attack( mf_classic_set_key_found(data, i, MfClassicKeyA, key); FURI_LOG_D(TAG, "Key found"); nfc_worker->callback(NfcWorkerEventFoundKeyA, nfc_worker->context); + + uint64_t found_key; + if(nfc_worker_mf_get_b_key_from_sector_trailer(tx_rx, i, key, &found_key)) { + FURI_LOG_D(TAG, "Found B key via reading sector %d", i); + mf_classic_set_key_found(data, i, MfClassicKeyB, found_key); + + if(nfc_worker->state == NfcWorkerStateMfClassicDictAttack) { + nfc_worker->callback(NfcWorkerEventFoundKeyB, nfc_worker->context); + } + } } } if(!mf_classic_is_key_found(data, i, MfClassicKeyB)) { @@ -1036,6 +1074,19 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) { mf_classic_set_key_found(data, i, MfClassicKeyA, key); FURI_LOG_D(TAG, "Key found"); nfc_worker->callback(NfcWorkerEventFoundKeyA, nfc_worker->context); + + uint64_t found_key; + if(nfc_worker_mf_get_b_key_from_sector_trailer( + &tx_rx, i, key, &found_key)) { + FURI_LOG_D(TAG, "Found B key via reading sector %d", i); + mf_classic_set_key_found(data, i, MfClassicKeyB, found_key); + + if(nfc_worker->state == NfcWorkerStateMfClassicDictAttack) { + nfc_worker->callback(NfcWorkerEventFoundKeyB, nfc_worker->context); + } + + nfc_worker_mf_classic_key_attack(nfc_worker, found_key, &tx_rx, i + 1); + } nfc_worker_mf_classic_key_attack(nfc_worker, key, &tx_rx, i + 1); deactivated = true; } diff --git a/lib/nfc/protocols/mifare_classic.c b/lib/nfc/protocols/mifare_classic.c index a9b286611..0adcb1923 100644 --- a/lib/nfc/protocols/mifare_classic.c +++ b/lib/nfc/protocols/mifare_classic.c @@ -541,6 +541,7 @@ bool mf_classic_authenticate_skip_activate( bool mf_classic_auth_attempt( FuriHalNfcTxRxContext* tx_rx, + Crypto1* crypto, MfClassicAuthContext* auth_ctx, uint64_t key) { furi_assert(tx_rx); @@ -549,15 +550,14 @@ bool mf_classic_auth_attempt( bool need_halt = (auth_ctx->key_a == MF_CLASSIC_NO_KEY) && (auth_ctx->key_b == MF_CLASSIC_NO_KEY); - Crypto1 crypto; if(auth_ctx->key_a == MF_CLASSIC_NO_KEY) { // Try AUTH with key A if(mf_classic_auth( tx_rx, - mf_classic_get_first_block_num_of_sector(auth_ctx->sector), + mf_classic_get_sector_trailer_block_num_by_sector(auth_ctx->sector), key, MfClassicKeyA, - &crypto, + crypto, false, 0)) { auth_ctx->key_a = key; @@ -573,10 +573,10 @@ bool mf_classic_auth_attempt( // Try AUTH with key B if(mf_classic_auth( tx_rx, - mf_classic_get_first_block_num_of_sector(auth_ctx->sector), + mf_classic_get_sector_trailer_block_num_by_sector(auth_ctx->sector), key, MfClassicKeyB, - &crypto, + crypto, false, 0)) { auth_ctx->key_b = key; @@ -672,6 +672,9 @@ void mf_classic_read_sector(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data, u do { if(blocks_read == total_blocks) break; if(!key_b_found) break; + if(key_a_found) { + furi_hal_nfc_sleep(); + } FURI_LOG_D(TAG, "Try to read blocks with key B"); key = nfc_util_bytes2num(sec_tr->key_b, sizeof(sec_tr->key_b)); if(!mf_classic_auth(tx_rx, start_block, key, MfClassicKeyB, &crypto, false, 0)) break; diff --git a/lib/nfc/protocols/mifare_classic.h b/lib/nfc/protocols/mifare_classic.h index e09322ae8..f2623d16e 100644 --- a/lib/nfc/protocols/mifare_classic.h +++ b/lib/nfc/protocols/mifare_classic.h @@ -175,6 +175,7 @@ bool mf_classic_authenticate_skip_activate( bool mf_classic_auth_attempt( FuriHalNfcTxRxContext* tx_rx, + Crypto1* crypto, MfClassicAuthContext* auth_ctx, uint64_t key); diff --git a/lib/one_wire/SConscript b/lib/one_wire/SConscript index 5e06d21e3..56d4759eb 100644 --- a/lib/one_wire/SConscript +++ b/lib/one_wire/SConscript @@ -11,8 +11,9 @@ env.Append( File("one_wire_host_timing.h"), File("one_wire_host.h"), File("one_wire_slave.h"), - File("one_wire_device.h"), + File("ibutton/ibutton_key.h"), File("ibutton/ibutton_worker.h"), + File("ibutton/ibutton_protocols.h"), File("maxim_crc.h"), ], ) diff --git a/lib/one_wire/ibutton/ibutton_key.c b/lib/one_wire/ibutton/ibutton_key.c index 7b7571a29..926a826a2 100644 --- a/lib/one_wire/ibutton/ibutton_key.c +++ b/lib/one_wire/ibutton/ibutton_key.c @@ -1,110 +1,43 @@ -#include -#include -#include "ibutton_key.h" +#include "ibutton_key_i.h" struct iButtonKey { - uint8_t data[IBUTTON_KEY_DATA_SIZE]; - iButtonKeyType type; + iButtonProtocolId protocol_id; + iButtonProtocolData* protocol_data; + size_t protocol_data_size; }; -iButtonKey* ibutton_key_alloc() { +iButtonKey* ibutton_key_alloc(size_t data_size) { iButtonKey* key = malloc(sizeof(iButtonKey)); - memset(key, 0, sizeof(iButtonKey)); + + key->protocol_id = iButtonProtocolIdInvalid; + key->protocol_data = malloc(data_size); + key->protocol_data_size = data_size; + return key; } void ibutton_key_free(iButtonKey* key) { + free(key->protocol_data); free(key); } -void ibutton_key_set(iButtonKey* to, const iButtonKey* from) { - memcpy(to, from, sizeof(iButtonKey)); +void ibutton_key_reset(iButtonKey* key) { + key->protocol_id = iButtonProtocolIdInvalid; + memset(key->protocol_data, 0, key->protocol_data_size); } -void ibutton_key_set_data(iButtonKey* key, uint8_t* data, uint8_t data_count) { - furi_check(data_count > 0); - furi_check(data_count <= IBUTTON_KEY_DATA_SIZE); - - memset(key->data, 0, IBUTTON_KEY_DATA_SIZE); - memcpy(key->data, data, data_count); +iButtonProtocolId ibutton_key_get_protocol_id(const iButtonKey* key) { + return key->protocol_id; } -void ibutton_key_clear_data(iButtonKey* key) { - memset(key->data, 0, IBUTTON_KEY_DATA_SIZE); +void ibutton_key_set_protocol_id(iButtonKey* key, iButtonProtocolId protocol_id) { + key->protocol_id = protocol_id; } -const uint8_t* ibutton_key_get_data_p(iButtonKey* key) { - return key->data; +iButtonProtocolData* ibutton_key_get_protocol_data(const iButtonKey* key) { + return key->protocol_data; } -uint8_t ibutton_key_get_data_size(iButtonKey* key) { - return ibutton_key_get_size_by_type(key->type); -} - -void ibutton_key_set_type(iButtonKey* key, iButtonKeyType key_type) { - key->type = key_type; -} - -iButtonKeyType ibutton_key_get_type(iButtonKey* key) { - return key->type; -} - -const char* ibutton_key_get_string_by_type(iButtonKeyType key_type) { - switch(key_type) { - case iButtonKeyCyfral: - return "Cyfral"; - break; - case iButtonKeyMetakom: - return "Metakom"; - break; - case iButtonKeyDS1990: - return "Dallas"; - break; - default: - furi_crash("Invalid iButton type"); - } -} - -bool ibutton_key_get_type_by_string(const char* type_string, iButtonKeyType* key_type) { - if(strcmp(type_string, ibutton_key_get_string_by_type(iButtonKeyCyfral)) == 0) { - *key_type = iButtonKeyCyfral; - } else if(strcmp(type_string, ibutton_key_get_string_by_type(iButtonKeyMetakom)) == 0) { - *key_type = iButtonKeyMetakom; - } else if(strcmp(type_string, ibutton_key_get_string_by_type(iButtonKeyDS1990)) == 0) { - *key_type = iButtonKeyDS1990; - } else { - return false; - } - - return true; -} - -uint8_t ibutton_key_get_size_by_type(iButtonKeyType key_type) { - uint8_t size = 0; - - switch(key_type) { - case iButtonKeyCyfral: - size = 2; - break; - case iButtonKeyMetakom: - size = 4; - break; - case iButtonKeyDS1990: - size = 8; - break; - } - - return size; -} - -uint8_t ibutton_key_get_max_size() { - return IBUTTON_KEY_DATA_SIZE; -} - -bool ibutton_key_dallas_crc_is_valid(iButtonKey* key) { - return (maxim_crc8(key->data, 8, MAXIM_CRC8_INIT) == 0); -} - -bool ibutton_key_dallas_is_1990_key(iButtonKey* key) { - return (key->data[0] == 0x01); +size_t ibutton_key_get_protocol_data_size(const iButtonKey* key) { + return key->protocol_data_size; } diff --git a/lib/one_wire/ibutton/ibutton_key.h b/lib/one_wire/ibutton/ibutton_key.h index d682555a8..1848cd672 100644 --- a/lib/one_wire/ibutton/ibutton_key.h +++ b/lib/one_wire/ibutton/ibutton_key.h @@ -5,127 +5,49 @@ */ #pragma once -#include + +#include + +#include "protocols/protocol_common.h" #ifdef __cplusplus extern "C" { #endif -#define IBUTTON_KEY_DATA_SIZE 8 -#define IBUTTON_KEY_NAME_SIZE 22 - -typedef enum { - iButtonKeyDS1990, - iButtonKeyCyfral, - iButtonKeyMetakom, -} iButtonKeyType; - typedef struct iButtonKey iButtonKey; /** - * Allocate key - * @return iButtonKey* + * Allocate a key object + * @param [in] data_size maximum data size held by the key + * @return pointer to the key object */ -iButtonKey* ibutton_key_alloc(); +iButtonKey* ibutton_key_alloc(size_t data_size); /** - * Free key - * @param key + * Destroy the key object, free resources + * @param [in] key pointer to the key object */ void ibutton_key_free(iButtonKey* key); /** - * Copy key - * @param to - * @param from + * Get the protocol id held by the key + * @param [in] key pointer to the key object + * @return protocol id held by the key */ -void ibutton_key_set(iButtonKey* to, const iButtonKey* from); +iButtonProtocolId ibutton_key_get_protocol_id(const iButtonKey* key); /** - * Set key data - * @param key - * @param data - * @param data_count + * Set the protocol id held by the key + * @param [in] key pointer to the key object + * @param [in] protocol_id new protocol id */ -void ibutton_key_set_data(iButtonKey* key, uint8_t* data, uint8_t data_count); +void ibutton_key_set_protocol_id(iButtonKey* key, iButtonProtocolId protocol_id); /** - * Clear key data - * @param key + * Reset the protocol id and data held by the key + * @param [in] key pointer to the key object */ -void ibutton_key_clear_data(iButtonKey* key); - -/** - * Get pointer to key data - * @param key - * @return const uint8_t* - */ -const uint8_t* ibutton_key_get_data_p(iButtonKey* key); - -/** - * Get key data size - * @param key - * @return uint8_t - */ -uint8_t ibutton_key_get_data_size(iButtonKey* key); - -/** - * Set key type - * @param key - * @param key_type - */ -void ibutton_key_set_type(iButtonKey* key, iButtonKeyType key_type); - -/** - * Get key type - * @param key - * @return iButtonKeyType - */ -iButtonKeyType ibutton_key_get_type(iButtonKey* key); - -/** - * Get type string from key type - * @param key_type - * @return const char* - */ -const char* ibutton_key_get_string_by_type(iButtonKeyType key_type); - -/** - * Get key type from string - * @param type_string - * @param key_type - * @return bool - */ -bool ibutton_key_get_type_by_string(const char* type_string, iButtonKeyType* key_type); - -/** - * Get key data size from type - * @param key_type - * @return uint8_t - */ -uint8_t ibutton_key_get_size_by_type(iButtonKeyType key_type); - -/** - * Get max key size - * @return uint8_t - */ -uint8_t ibutton_key_get_max_size(); - -/** - * Check if CRC for onewire key is valid - * @param key - * @return true - * @return false - */ -bool ibutton_key_dallas_crc_is_valid(iButtonKey* key); - -/** - * Check if onewire key is a DS1990 key - * @param key - * @return true - * @return false - */ -bool ibutton_key_dallas_is_1990_key(iButtonKey* key); +void ibutton_key_reset(iButtonKey* key); #ifdef __cplusplus } diff --git a/lib/one_wire/ibutton/ibutton_key_command.h b/lib/one_wire/ibutton/ibutton_key_command.h deleted file mode 100644 index 88049d064..000000000 --- a/lib/one_wire/ibutton/ibutton_key_command.h +++ /dev/null @@ -1,28 +0,0 @@ -/** - * @file ibutton_key_command.h - * - * List of misc commands for Dallas and blanks - */ - -#pragma once - -#define RW1990_1_CMD_WRITE_RECORD_FLAG 0xD1 -#define RW1990_1_CMD_READ_RECORD_FLAG 0xB5 -#define RW1990_1_CMD_WRITE_ROM 0xD5 - -#define RW1990_2_CMD_WRITE_RECORD_FLAG 0x1D -#define RW1990_2_CMD_READ_RECORD_FLAG 0x1E -#define RW1990_2_CMD_WRITE_ROM 0xD5 - -#define TM2004_CMD_READ_STATUS 0xAA -#define TM2004_CMD_READ_MEMORY 0xF0 -#define TM2004_CMD_WRITE_ROM 0x3C -#define TM2004_CMD_FINALIZATION 0x35 -#define TM2004_ANSWER_READ_MEMORY 0xF5 - -#define TM01_CMD_WRITE_RECORD_FLAG 0xC1 -#define TM01_CMD_WRITE_ROM 0xC5 -#define TM01_CMD_SWITCH_TO_CYFRAL 0xCA -#define TM01_CMD_SWITCH_TO_METAKOM 0xCB - -#define DS1990_CMD_READ_ROM 0x33 diff --git a/lib/one_wire/ibutton/ibutton_key_i.h b/lib/one_wire/ibutton/ibutton_key_i.h new file mode 100644 index 000000000..b527c65b4 --- /dev/null +++ b/lib/one_wire/ibutton/ibutton_key_i.h @@ -0,0 +1,9 @@ +#pragma once + +#include "ibutton_key.h" + +#include "protocols/protocol_common_i.h" + +iButtonProtocolData* ibutton_key_get_protocol_data(const iButtonKey* key); + +size_t ibutton_key_get_protocol_data_size(const iButtonKey* key); diff --git a/lib/one_wire/ibutton/ibutton_protocols.c b/lib/one_wire/ibutton/ibutton_protocols.c new file mode 100644 index 000000000..75aa225ef --- /dev/null +++ b/lib/one_wire/ibutton/ibutton_protocols.c @@ -0,0 +1,309 @@ +#include "ibutton_protocols.h" + +#include + +#include "ibutton_key_i.h" + +#include "protocols/protocol_group_defs.h" + +#define IBUTTON_FILE_TYPE "Flipper iButton key" + +#define IBUTTON_PROTOCOL_KEY_V1 "Key type" +#define IBUTTON_PROTOCOL_KEY_V2 "Protocol" + +#define IBUTTON_CURRENT_FORMAT_VERSION 2U + +#define GET_PROTOCOL_GROUP(id) \ + iButtonProtocolGroupInfo info; \ + ibutton_protocols_get_group_by_id(protocols, (id), &info); + +#define GROUP_BASE (info.base) +#define GROUP_DATA (info.group) +#define PROTOCOL_ID (info.id) + +struct iButtonProtocols { + iButtonProtocolGroupData** group_datas; +}; + +typedef struct { + const iButtonProtocolGroupBase* base; + iButtonProtocolGroupData* group; + iButtonProtocolLocalId id; +} iButtonProtocolGroupInfo; + +static void ibutton_protocols_get_group_by_id( + iButtonProtocols* protocols, + iButtonProtocolId id, + iButtonProtocolGroupInfo* info) { + iButtonProtocolLocalId local_id = id; + + for(iButtonProtocolGroupId i = 0; i < iButtonProtocolGroupMax; ++i) { + if(local_id < (signed)ibutton_protocol_groups[i]->protocol_count) { + info->base = ibutton_protocol_groups[i]; + info->group = protocols->group_datas[i]; + info->id = local_id; + return; + + } else { + local_id -= ibutton_protocol_groups[i]->protocol_count; + } + } + furi_crash(NULL); +} + +iButtonProtocols* ibutton_protocols_alloc() { + iButtonProtocols* protocols = malloc(sizeof(iButtonProtocols*)); + + protocols->group_datas = malloc(sizeof(iButtonProtocolGroupData*) * iButtonProtocolGroupMax); + + for(iButtonProtocolGroupId i = 0; i < iButtonProtocolGroupMax; ++i) { + protocols->group_datas[i] = ibutton_protocol_groups[i]->alloc(); + } + + return protocols; +} + +void ibutton_protocols_free(iButtonProtocols* protocols) { + for(iButtonProtocolGroupId i = 0; i < iButtonProtocolGroupMax; ++i) { + ibutton_protocol_groups[i]->free(protocols->group_datas[i]); + } + + free(protocols->group_datas); + free(protocols); +} + +uint32_t ibutton_protocols_get_protocol_count() { + uint32_t count = 0; + + for(iButtonProtocolGroupId i = 0; i < iButtonProtocolGroupMax; ++i) { + count += ibutton_protocol_groups[i]->protocol_count; + } + + return count; +} + +iButtonProtocolId ibutton_protocols_get_id_by_name(iButtonProtocols* protocols, const char* name) { + iButtonProtocolLocalId offset = 0; + + for(iButtonProtocolGroupId i = 0; i < iButtonProtocolGroupMax; ++i) { + iButtonProtocolLocalId local_id; + if(ibutton_protocol_groups[i]->get_id_by_name(protocols->group_datas[i], &local_id, name)) { + return local_id + offset; + } + offset += ibutton_protocol_groups[i]->protocol_count; + } + return iButtonProtocolIdInvalid; +} + +uint32_t ibutton_protocols_get_features(iButtonProtocols* protocols, iButtonProtocolId id) { + GET_PROTOCOL_GROUP(id); + return GROUP_BASE->get_features(GROUP_DATA, PROTOCOL_ID); +} + +size_t ibutton_protocols_get_max_data_size(iButtonProtocols* protocols) { + size_t max_size = 0; + + for(iButtonProtocolGroupId i = 0; i < iButtonProtocolGroupMax; ++i) { + const size_t current_max_size = + ibutton_protocol_groups[i]->get_max_data_size(protocols->group_datas[i]); + if(current_max_size > max_size) { + max_size = current_max_size; + } + } + + return max_size; +} + +const char* ibutton_protocols_get_manufacturer(iButtonProtocols* protocols, iButtonProtocolId id) { + GET_PROTOCOL_GROUP(id); + return GROUP_BASE->get_manufacturer(GROUP_DATA, PROTOCOL_ID); +} + +const char* ibutton_protocols_get_name(iButtonProtocols* protocols, iButtonProtocolId id) { + GET_PROTOCOL_GROUP(id); + return GROUP_BASE->get_name(GROUP_DATA, PROTOCOL_ID); +} + +bool ibutton_protocols_read(iButtonProtocols* protocols, iButtonKey* key) { + iButtonProtocolLocalId id = iButtonProtocolIdInvalid; + iButtonProtocolData* data = ibutton_key_get_protocol_data(key); + + iButtonProtocolLocalId offset = 0; + for(iButtonProtocolGroupId i = 0; i < iButtonProtocolGroupMax; ++i) { + if(ibutton_protocol_groups[i]->read(protocols->group_datas[i], data, &id)) { + id += offset; + break; + } + offset += ibutton_protocol_groups[i]->protocol_count; + } + + ibutton_key_set_protocol_id(key, id); + return id != iButtonProtocolIdInvalid; +} + +bool ibutton_protocols_write_blank(iButtonProtocols* protocols, iButtonKey* key) { + const iButtonProtocolId id = ibutton_key_get_protocol_id(key); + iButtonProtocolData* data = ibutton_key_get_protocol_data(key); + + GET_PROTOCOL_GROUP(id); + return GROUP_BASE->write_blank(GROUP_DATA, data, PROTOCOL_ID); +} + +bool ibutton_protocols_write_copy(iButtonProtocols* protocols, iButtonKey* key) { + const iButtonProtocolId id = ibutton_key_get_protocol_id(key); + iButtonProtocolData* data = ibutton_key_get_protocol_data(key); + + GET_PROTOCOL_GROUP(id); + return GROUP_BASE->write_copy(GROUP_DATA, data, PROTOCOL_ID); +} + +void ibutton_protocols_emulate_start(iButtonProtocols* protocols, iButtonKey* key) { + const iButtonProtocolId id = ibutton_key_get_protocol_id(key); + iButtonProtocolData* data = ibutton_key_get_protocol_data(key); + + GET_PROTOCOL_GROUP(id); + GROUP_BASE->emulate_start(GROUP_DATA, data, PROTOCOL_ID); +} + +void ibutton_protocols_emulate_stop(iButtonProtocols* protocols, iButtonKey* key) { + const iButtonProtocolId id = ibutton_key_get_protocol_id(key); + iButtonProtocolData* data = ibutton_key_get_protocol_data(key); + + GET_PROTOCOL_GROUP(id); + GROUP_BASE->emulate_stop(GROUP_DATA, data, PROTOCOL_ID); +} + +bool ibutton_protocols_save( + iButtonProtocols* protocols, + const iButtonKey* key, + const char* file_name) { + const iButtonProtocolId id = ibutton_key_get_protocol_id(key); + const iButtonProtocolData* data = ibutton_key_get_protocol_data(key); + + bool success = false; + Storage* storage = furi_record_open(RECORD_STORAGE); + + FlipperFormat* ff = flipper_format_buffered_file_alloc(storage); + + do { + const char* protocol_name = ibutton_protocols_get_name(protocols, id); + + if(!flipper_format_buffered_file_open_always(ff, file_name)) break; + + if(!flipper_format_write_header_cstr(ff, IBUTTON_FILE_TYPE, IBUTTON_CURRENT_FORMAT_VERSION)) + break; + if(!flipper_format_write_string_cstr(ff, IBUTTON_PROTOCOL_KEY_V2, protocol_name)) break; + + GET_PROTOCOL_GROUP(id); + if(!GROUP_BASE->save(GROUP_DATA, data, PROTOCOL_ID, ff)) break; + + success = true; + } while(false); + + flipper_format_free(ff); + furi_record_close(RECORD_STORAGE); + + return success; +} + +bool ibutton_protocols_load(iButtonProtocols* protocols, iButtonKey* key, const char* file_name) { + iButtonProtocolData* data = ibutton_key_get_protocol_data(key); + + bool success = false; + Storage* storage = furi_record_open(RECORD_STORAGE); + + FlipperFormat* ff = flipper_format_buffered_file_alloc(storage); + FuriString* tmp = furi_string_alloc(); + + do { + if(!flipper_format_buffered_file_open_existing(ff, file_name)) break; + + uint32_t version; + + if(!flipper_format_read_header(ff, tmp, &version)) break; + if(!furi_string_equal(tmp, IBUTTON_FILE_TYPE)) break; + + if(version == 1) { + if(!flipper_format_read_string(ff, IBUTTON_PROTOCOL_KEY_V1, tmp)) break; + } else if(version == 2) { + if(!flipper_format_read_string(ff, IBUTTON_PROTOCOL_KEY_V2, tmp)) break; + } else { + break; + } + + const iButtonProtocolId id = + ibutton_protocols_get_id_by_name(protocols, furi_string_get_cstr(tmp)); + ibutton_key_set_protocol_id(key, id); + + GET_PROTOCOL_GROUP(id); + if(!GROUP_BASE->load(GROUP_DATA, data, PROTOCOL_ID, version, ff)) break; + + success = true; + } while(false); + + flipper_format_free(ff); + furi_string_free(tmp); + furi_record_close(RECORD_STORAGE); + + return success; +} + +void ibutton_protocols_render_data( + iButtonProtocols* protocols, + const iButtonKey* key, + FuriString* result) { + const iButtonProtocolId id = ibutton_key_get_protocol_id(key); + const iButtonProtocolData* data = ibutton_key_get_protocol_data(key); + + GET_PROTOCOL_GROUP(id); + GROUP_BASE->render_data(GROUP_DATA, data, PROTOCOL_ID, result); +} + +void ibutton_protocols_render_brief_data( + iButtonProtocols* protocols, + const iButtonKey* key, + FuriString* result) { + const iButtonProtocolId id = ibutton_key_get_protocol_id(key); + const iButtonProtocolData* data = ibutton_key_get_protocol_data(key); + + GET_PROTOCOL_GROUP(id); + GROUP_BASE->render_brief_data(GROUP_DATA, data, PROTOCOL_ID, result); +} + +void ibutton_protocols_render_error( + iButtonProtocols* protocols, + const iButtonKey* key, + FuriString* result) { + const iButtonProtocolId id = ibutton_key_get_protocol_id(key); + const iButtonProtocolData* data = ibutton_key_get_protocol_data(key); + + GET_PROTOCOL_GROUP(id); + GROUP_BASE->render_error(GROUP_DATA, data, PROTOCOL_ID, result); +} + +bool ibutton_protocols_is_valid(iButtonProtocols* protocols, const iButtonKey* key) { + const iButtonProtocolId id = ibutton_key_get_protocol_id(key); + const iButtonProtocolData* data = ibutton_key_get_protocol_data(key); + + GET_PROTOCOL_GROUP(id); + return GROUP_BASE->is_valid(GROUP_DATA, data, PROTOCOL_ID); +} + +void ibutton_protocols_get_editable_data( + iButtonProtocols* protocols, + const iButtonKey* key, + iButtonEditableData* editable) { + const iButtonProtocolId id = ibutton_key_get_protocol_id(key); + iButtonProtocolData* data = ibutton_key_get_protocol_data(key); + + GET_PROTOCOL_GROUP(id); + GROUP_BASE->get_editable_data(GROUP_DATA, data, PROTOCOL_ID, editable); +} + +void ibutton_protocols_apply_edits(iButtonProtocols* protocols, const iButtonKey* key) { + const iButtonProtocolId id = ibutton_key_get_protocol_id(key); + iButtonProtocolData* data = ibutton_key_get_protocol_data(key); + + GET_PROTOCOL_GROUP(id); + GROUP_BASE->apply_edits(GROUP_DATA, data, PROTOCOL_ID); +} diff --git a/lib/one_wire/ibutton/ibutton_protocols.h b/lib/one_wire/ibutton/ibutton_protocols.h new file mode 100644 index 000000000..0e7ed0a80 --- /dev/null +++ b/lib/one_wire/ibutton/ibutton_protocols.h @@ -0,0 +1,197 @@ +/** + * @file ibutton_protocols.h + * + * Common interface for accessing various iButton protocols + */ + +#pragma once + +#include +#include + +#include "protocols/protocol_common.h" + +#include "ibutton_key.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct iButtonProtocols iButtonProtocols; + +/** + * Allocate an iButtonProtocols object + * @return pointer to an iButtonProtocols object + */ +iButtonProtocols* ibutton_protocols_alloc(); + +/** + * Destroy an iButtonProtocols object, free resources + * @param [in] protocols pointer to an iButtonProtocols object + */ +void ibutton_protocols_free(iButtonProtocols* protocols); + +/** + * Get the total number of available protocols + */ +uint32_t ibutton_protocols_get_protocol_count(); + +/** + * Get maximum data size out of all protocols available + * @param [in] protocols pointer to an iButtonProtocols object + * @return maximum data size in bytes + */ +size_t ibutton_protocols_get_max_data_size(iButtonProtocols* protocols); + +/** + * Get the protocol id based on its name + * @param [in] protocols pointer to an iButtonProtocols object + * @param [in] name pointer to a string containing the name + * @return protocol id on success on iButtonProtocolIdInvalid on failure + */ +iButtonProtocolId ibutton_protocols_get_id_by_name(iButtonProtocols* protocols, const char* name); + +/** + * Get the manufacturer name based on the protocol id + * @param [in] protocols pointer to an iButtonProtocols object + * @param [in] id id of the protocol in question + * @return pointer to a statically allocated string with manufacturer name + */ +const char* ibutton_protocols_get_manufacturer(iButtonProtocols* protocols, iButtonProtocolId id); + +/** + * Get the protocol name based on the protocol id + * @param [in] protocols pointer to an iButtonProtocols object + * @param [in] id id of the protocol in question + * @return pointer to a statically allocated string with protocol name + */ +const char* ibutton_protocols_get_name(iButtonProtocols* protocols, iButtonProtocolId id); + +/** + * Get protocol features bitmask by protocol id + * @param [in] protocols pointer to an iButtonProtocols object + * @param [in] id id of the protocol in question + */ +uint32_t ibutton_protocols_get_features(iButtonProtocols* protocols, iButtonProtocolId id); + +/** + * Read a physical device (a key or an emulator) + * @param [in] protocols pointer to an iButtonProtocols object + * @param [out] key pointer to the key to read into (must be allocated before) + * @return true on success, false on failure + */ +bool ibutton_protocols_read(iButtonProtocols* protocols, iButtonKey* key); + +/** + * Write the key to a blank + * @param [in] protocols pointer to an iButtonProtocols object + * @param [in] key pointer to the key to be written + * @return true on success, false on failure + */ +bool ibutton_protocols_write_blank(iButtonProtocols* protocols, iButtonKey* key); + +/** + * Write the key to another one of the same type + * @param [in] protocols pointer to an iButtonProtocols object + * @param [in] key pointer to the key to be written + * @return true on success, false on failure + */ +bool ibutton_protocols_write_copy(iButtonProtocols* protocols, iButtonKey* key); + +/** + * Start emulating the key + * @param [in] protocols pointer to an iButtonProtocols object + * @param [in] key pointer to the key to be emulated + */ +void ibutton_protocols_emulate_start(iButtonProtocols* protocols, iButtonKey* key); + +/** + * Stop emulating the key + * @param [in] protocols pointer to an iButtonProtocols object + * @param [in] key pointer to the key to be emulated + */ +void ibutton_protocols_emulate_stop(iButtonProtocols* protocols, iButtonKey* key); + +/** + * Save the key data to a file. + * @param [in] protocols pointer to an iButtonProtocols object + * @param [in] key pointer to the key to be saved + * @param [in] file_name full absolute path to the file name + * @return true on success, false on failure + */ +bool ibutton_protocols_save( + iButtonProtocols* protocols, + const iButtonKey* key, + const char* file_name); + +/** + * Load the key from a file. + * @param [in] protocols pointer to an iButtonProtocols object + * @param [out] key pointer to the key to load into (must be allocated before) + * @param [in] file_name full absolute path to the file name + * @return true on success, false on failure + */ +bool ibutton_protocols_load(iButtonProtocols* protocols, iButtonKey* key, const char* file_name); + +/** + * Format a string containing device full data + * @param [in] protocols pointer to an iButtonProtocols object + * @param [in] key pointer to the key to be rendered + * @param [out] result pointer to the FuriString instance (must be initialized) + */ +void ibutton_protocols_render_data( + iButtonProtocols* protocols, + const iButtonKey* key, + FuriString* result); + +/** + * Format a string containing device brief data + * @param [in] protocols pointer to an iButtonProtocols object + * @param [in] key pointer to the key to be rendered + * @param [out] result pointer to the FuriString instance (must be initialized) + */ +void ibutton_protocols_render_brief_data( + iButtonProtocols* protocols, + const iButtonKey* key, + FuriString* result); + +/** + * Format a string containing error message (for invalid keys) + * @param [in] protocols pointer to an iButtonProtocols object + * @param [in] key pointer to the key to be rendered + * @param [out] result pointer to the FuriString instance (must be initialized) + */ +void ibutton_protocols_render_error( + iButtonProtocols* protocols, + const iButtonKey* key, + FuriString* result); + +/** + * Check whether the key data is valid + * @param [in] protocols pointer to an iButtonProtocols object + * @param [in] key pointer to the key to be checked + * @return true if data is valid, false otherwise + */ +bool ibutton_protocols_is_valid(iButtonProtocols* protocols, const iButtonKey* key); + +/** + * Get a pointer to the key's editable data (for in-place editing) + * @param [in] protocols pointer to an iButtonProtocols object + * @param [in] key pointer to the key to be checked + * @param [out] editable pointer to a structure to contain the editable data + */ +void ibutton_protocols_get_editable_data( + iButtonProtocols* protocols, + const iButtonKey* key, + iButtonEditableData* editable); + +/** + * Make all necessary internal adjustments after editing the key + * @param [in] protocols pointer to an iButtonProtocols object + * @param [in,out] key pointer to the key to be adjusted + */ +void ibutton_protocols_apply_edits(iButtonProtocols* protocols, const iButtonKey* key); + +#ifdef __cplusplus +} +#endif diff --git a/lib/one_wire/ibutton/ibutton_worker.c b/lib/one_wire/ibutton/ibutton_worker.c index 9d5ea3897..d40dba71a 100644 --- a/lib/one_wire/ibutton/ibutton_worker.c +++ b/lib/one_wire/ibutton/ibutton_worker.c @@ -1,13 +1,14 @@ -#include -#include -#include #include "ibutton_worker_i.h" +#include "ibutton_protocols.h" + +#include typedef enum { iButtonMessageEnd, iButtonMessageStop, iButtonMessageRead, - iButtonMessageWrite, + iButtonMessageWriteBlank, + iButtonMessageWriteCopy, iButtonMessageEmulate, iButtonMessageNotifyEmulate, } iButtonMessageType; @@ -21,26 +22,15 @@ typedef struct { static int32_t ibutton_worker_thread(void* thread_context); -iButtonWorker* ibutton_worker_alloc() { +iButtonWorker* ibutton_worker_alloc(iButtonProtocols* protocols) { iButtonWorker* worker = malloc(sizeof(iButtonWorker)); - worker->key_p = NULL; - worker->key_data = malloc(ibutton_key_get_max_size()); - worker->host = onewire_host_alloc(); - worker->slave = onewire_slave_alloc(); - worker->writer = ibutton_writer_alloc(worker->host); - worker->device = onewire_device_alloc(0, 0, 0, 0, 0, 0, 0, 0); + + worker->protocols = protocols; worker->messages = furi_message_queue_alloc(1, sizeof(iButtonMessage)); - worker->mode_index = iButtonWorkerIdle; - worker->read_cb = NULL; - worker->write_cb = NULL; - worker->emulate_cb = NULL; - worker->cb_ctx = NULL; - + worker->mode_index = iButtonWorkerModeIdle; worker->thread = furi_thread_alloc_ex("iButtonWorker", 2048, ibutton_worker_thread, worker); - worker->protocols = protocol_dict_alloc(ibutton_protocols, iButtonProtocolMax); - return worker; } @@ -48,7 +38,7 @@ void ibutton_worker_read_set_callback( iButtonWorker* worker, iButtonWorkerReadCallback callback, void* context) { - furi_check(worker->mode_index == iButtonWorkerIdle); + furi_check(worker->mode_index == iButtonWorkerModeIdle); worker->read_cb = callback; worker->cb_ctx = context; } @@ -57,7 +47,7 @@ void ibutton_worker_write_set_callback( iButtonWorker* worker, iButtonWorkerWriteCallback callback, void* context) { - furi_check(worker->mode_index == iButtonWorkerIdle); + furi_check(worker->mode_index == iButtonWorkerModeIdle); worker->write_cb = callback; worker->cb_ctx = context; } @@ -66,7 +56,7 @@ void ibutton_worker_emulate_set_callback( iButtonWorker* worker, iButtonWorkerEmulateCallback callback, void* context) { - furi_check(worker->mode_index == iButtonWorkerIdle); + furi_check(worker->mode_index == iButtonWorkerModeIdle); worker->emulate_cb = callback; worker->cb_ctx = context; } @@ -77,8 +67,14 @@ void ibutton_worker_read_start(iButtonWorker* worker, iButtonKey* key) { furi_message_queue_put(worker->messages, &message, FuriWaitForever) == FuriStatusOk); } -void ibutton_worker_write_start(iButtonWorker* worker, iButtonKey* key) { - iButtonMessage message = {.type = iButtonMessageWrite, .data.key = key}; +void ibutton_worker_write_blank_start(iButtonWorker* worker, iButtonKey* key) { + iButtonMessage message = {.type = iButtonMessageWriteBlank, .data.key = key}; + furi_check( + furi_message_queue_put(worker->messages, &message, FuriWaitForever) == FuriStatusOk); +} + +void ibutton_worker_write_copy_start(iButtonWorker* worker, iButtonKey* key) { + iButtonMessage message = {.type = iButtonMessageWriteCopy, .data.key = key}; furi_check( furi_message_queue_put(worker->messages, &message, FuriWaitForever) == FuriStatusOk); } @@ -96,19 +92,8 @@ void ibutton_worker_stop(iButtonWorker* worker) { } void ibutton_worker_free(iButtonWorker* worker) { - ibutton_writer_free(worker->writer); - - onewire_slave_free(worker->slave); - - onewire_host_free(worker->host); - onewire_device_free(worker->device); - - protocol_dict_free(worker->protocols); - furi_message_queue_free(worker->messages); - furi_thread_free(worker->thread); - free(worker->key_data); free(worker); } @@ -137,7 +122,7 @@ void ibutton_worker_notify_emulate(iButtonWorker* worker) { } void ibutton_worker_set_key_p(iButtonWorker* worker, iButtonKey* key) { - worker->key_p = key; + worker->key = key; } static int32_t ibutton_worker_thread(void* thread_context) { @@ -154,25 +139,29 @@ static int32_t ibutton_worker_thread(void* thread_context) { if(status == FuriStatusOk) { switch(message.type) { case iButtonMessageEnd: - ibutton_worker_switch_mode(worker, iButtonWorkerIdle); + ibutton_worker_switch_mode(worker, iButtonWorkerModeIdle); ibutton_worker_set_key_p(worker, NULL); running = false; break; case iButtonMessageStop: - ibutton_worker_switch_mode(worker, iButtonWorkerIdle); + ibutton_worker_switch_mode(worker, iButtonWorkerModeIdle); ibutton_worker_set_key_p(worker, NULL); break; case iButtonMessageRead: ibutton_worker_set_key_p(worker, message.data.key); - ibutton_worker_switch_mode(worker, iButtonWorkerRead); + ibutton_worker_switch_mode(worker, iButtonWorkerModeRead); break; - case iButtonMessageWrite: + case iButtonMessageWriteBlank: ibutton_worker_set_key_p(worker, message.data.key); - ibutton_worker_switch_mode(worker, iButtonWorkerWrite); + ibutton_worker_switch_mode(worker, iButtonWorkerModeWriteBlank); + break; + case iButtonMessageWriteCopy: + ibutton_worker_set_key_p(worker, message.data.key); + ibutton_worker_switch_mode(worker, iButtonWorkerModeWriteCopy); break; case iButtonMessageEmulate: ibutton_worker_set_key_p(worker, message.data.key); - ibutton_worker_switch_mode(worker, iButtonWorkerEmulate); + ibutton_worker_switch_mode(worker, iButtonWorkerModeEmulate); break; case iButtonMessageNotifyEmulate: if(worker->emulate_cb) { diff --git a/lib/one_wire/ibutton/ibutton_worker.h b/lib/one_wire/ibutton/ibutton_worker.h index 5c8b1fc39..2a12a3194 100644 --- a/lib/one_wire/ibutton/ibutton_worker.h +++ b/lib/one_wire/ibutton/ibutton_worker.h @@ -5,7 +5,9 @@ */ #pragma once + #include "ibutton_key.h" +#include "ibutton_protocols.h" #ifdef __cplusplus extern "C" { @@ -28,7 +30,7 @@ typedef struct iButtonWorker iButtonWorker; * Allocate ibutton worker * @return iButtonWorker* */ -iButtonWorker* ibutton_worker_alloc(); +iButtonWorker* ibutton_worker_alloc(iButtonProtocols* protocols); /** * Free ibutton worker @@ -78,11 +80,18 @@ void ibutton_worker_write_set_callback( void* context); /** - * Start write mode + * Start write blank mode * @param worker * @param key */ -void ibutton_worker_write_start(iButtonWorker* worker, iButtonKey* key); +void ibutton_worker_write_blank_start(iButtonWorker* worker, iButtonKey* key); + +/** + * Start write copy mode + * @param worker + * @param key + */ +void ibutton_worker_write_copy_start(iButtonWorker* worker, iButtonKey* key); /** * Set "emulate success" callback diff --git a/lib/one_wire/ibutton/ibutton_worker_i.h b/lib/one_wire/ibutton/ibutton_worker_i.h index 2396fbd61..5f259a38a 100644 --- a/lib/one_wire/ibutton/ibutton_worker_i.h +++ b/lib/one_wire/ibutton/ibutton_worker_i.h @@ -5,13 +5,11 @@ */ #pragma once + +#include +#include + #include "ibutton_worker.h" -#include "ibutton_writer.h" -#include "../one_wire_host.h" -#include "../one_wire_slave.h" -#include "../one_wire_device.h" -#include -#include "protocols/ibutton_protocols.h" #ifdef __cplusplus extern "C" { @@ -25,19 +23,16 @@ typedef struct { } iButtonWorkerModeType; typedef enum { - iButtonWorkerIdle = 0, - iButtonWorkerRead = 1, - iButtonWorkerWrite = 2, - iButtonWorkerEmulate = 3, + iButtonWorkerModeIdle, + iButtonWorkerModeRead, + iButtonWorkerModeWriteBlank, + iButtonWorkerModeWriteCopy, + iButtonWorkerModeEmulate, } iButtonWorkerMode; struct iButtonWorker { - iButtonKey* key_p; - uint8_t* key_data; - OneWireHost* host; - OneWireSlave* slave; - OneWireDevice* device; - iButtonWriter* writer; + iButtonKey* key; + iButtonProtocols* protocols; iButtonWorkerMode mode_index; FuriMessageQueue* messages; FuriThread* thread; @@ -45,10 +40,8 @@ struct iButtonWorker { iButtonWorkerReadCallback read_cb; iButtonWorkerWriteCallback write_cb; iButtonWorkerEmulateCallback emulate_cb; - void* cb_ctx; - ProtocolDict* protocols; - iButtonProtocol protocol_to_encode; + void* cb_ctx; }; extern const iButtonWorkerModeType ibutton_worker_modes[]; diff --git a/lib/one_wire/ibutton/ibutton_worker_modes.c b/lib/one_wire/ibutton/ibutton_worker_modes.c index b284940e7..1b8e0a3b8 100644 --- a/lib/one_wire/ibutton/ibutton_worker_modes.c +++ b/lib/one_wire/ibutton/ibutton_worker_modes.c @@ -1,23 +1,28 @@ -#include -#include #include "ibutton_worker_i.h" -#include "ibutton_key_command.h" -void ibutton_worker_mode_idle_start(iButtonWorker* worker); -void ibutton_worker_mode_idle_tick(iButtonWorker* worker); -void ibutton_worker_mode_idle_stop(iButtonWorker* worker); +#include -void ibutton_worker_mode_emulate_start(iButtonWorker* worker); -void ibutton_worker_mode_emulate_tick(iButtonWorker* worker); -void ibutton_worker_mode_emulate_stop(iButtonWorker* worker); +#include +#include -void ibutton_worker_mode_read_start(iButtonWorker* worker); -void ibutton_worker_mode_read_tick(iButtonWorker* worker); -void ibutton_worker_mode_read_stop(iButtonWorker* worker); +#include "ibutton_protocols.h" -void ibutton_worker_mode_write_start(iButtonWorker* worker); -void ibutton_worker_mode_write_tick(iButtonWorker* worker); -void ibutton_worker_mode_write_stop(iButtonWorker* worker); +static void ibutton_worker_mode_idle_start(iButtonWorker* worker); +static void ibutton_worker_mode_idle_tick(iButtonWorker* worker); +static void ibutton_worker_mode_idle_stop(iButtonWorker* worker); + +static void ibutton_worker_mode_emulate_start(iButtonWorker* worker); +static void ibutton_worker_mode_emulate_tick(iButtonWorker* worker); +static void ibutton_worker_mode_emulate_stop(iButtonWorker* worker); + +static void ibutton_worker_mode_read_start(iButtonWorker* worker); +static void ibutton_worker_mode_read_tick(iButtonWorker* worker); +static void ibutton_worker_mode_read_stop(iButtonWorker* worker); + +static void ibutton_worker_mode_write_common_start(iButtonWorker* worker); +static void ibutton_worker_mode_write_blank_tick(iButtonWorker* worker); +static void ibutton_worker_mode_write_copy_tick(iButtonWorker* worker); +static void ibutton_worker_mode_write_common_stop(iButtonWorker* worker); const iButtonWorkerModeType ibutton_worker_modes[] = { { @@ -34,9 +39,15 @@ const iButtonWorkerModeType ibutton_worker_modes[] = { }, { .quant = 1000, - .start = ibutton_worker_mode_write_start, - .tick = ibutton_worker_mode_write_tick, - .stop = ibutton_worker_mode_write_stop, + .start = ibutton_worker_mode_write_common_start, + .tick = ibutton_worker_mode_write_blank_tick, + .stop = ibutton_worker_mode_write_common_stop, + }, + { + .quant = 1000, + .start = ibutton_worker_mode_write_common_start, + .tick = ibutton_worker_mode_write_copy_tick, + .stop = ibutton_worker_mode_write_common_stop, }, { .quant = 1000, @@ -62,143 +73,18 @@ void ibutton_worker_mode_idle_stop(iButtonWorker* worker) { /*********************** READ ***********************/ -typedef struct { - uint32_t last_dwt_value; - FuriStreamBuffer* stream; -} iButtonReadContext; - -void ibutton_worker_comparator_callback(bool level, void* context) { - iButtonReadContext* read_context = context; - - uint32_t current_dwt_value = DWT->CYCCNT; - - LevelDuration data = - level_duration_make(level, current_dwt_value - read_context->last_dwt_value); - furi_stream_buffer_send(read_context->stream, &data, sizeof(LevelDuration), 0); - - read_context->last_dwt_value = current_dwt_value; -} - -bool ibutton_worker_read_comparator(iButtonWorker* worker) { - bool result = false; - - protocol_dict_decoders_start(worker->protocols); - - furi_hal_rfid_pins_reset(); - // pulldown pull pin, we sense the signal through the analog part of the RFID schematic - furi_hal_rfid_pin_pull_pulldown(); - - iButtonReadContext read_context = { - .last_dwt_value = DWT->CYCCNT, - .stream = furi_stream_buffer_alloc(sizeof(LevelDuration) * 512, 1), - }; - - furi_hal_rfid_comp_set_callback(ibutton_worker_comparator_callback, &read_context); - furi_hal_rfid_comp_start(); - - uint32_t tick_start = furi_get_tick(); - while(true) { - LevelDuration level; - size_t ret = - furi_stream_buffer_receive(read_context.stream, &level, sizeof(LevelDuration), 100); - - if((furi_get_tick() - tick_start) > 100) { - break; - } - - if(ret > 0) { - ProtocolId decoded_index = protocol_dict_decoders_feed( - worker->protocols, - level_duration_get_level(level), - level_duration_get_duration(level)); - - if(decoded_index == PROTOCOL_NO) continue; - - protocol_dict_get_data( - worker->protocols, decoded_index, worker->key_data, ibutton_key_get_max_size()); - - switch(decoded_index) { - case iButtonProtocolCyfral: - furi_check(worker->key_p != NULL); - ibutton_key_set_type(worker->key_p, iButtonKeyCyfral); - ibutton_key_set_data(worker->key_p, worker->key_data, ibutton_key_get_max_size()); - result = true; - break; - case iButtonProtocolMetakom: - furi_check(worker->key_p != NULL); - ibutton_key_set_type(worker->key_p, iButtonKeyMetakom); - ibutton_key_set_data(worker->key_p, worker->key_data, ibutton_key_get_max_size()); - result = true; - break; - default: - break; - } - } - } - - furi_hal_rfid_comp_stop(); - furi_hal_rfid_comp_set_callback(NULL, NULL); - furi_hal_rfid_pins_reset(); - - furi_stream_buffer_free(read_context.stream); - - return result; -} - -bool ibutton_worker_read_dallas(iButtonWorker* worker) { - bool result = false; - onewire_host_start(worker->host); - furi_delay_ms(100); - FURI_CRITICAL_ENTER(); - if(onewire_host_search(worker->host, worker->key_data, NORMAL_SEARCH)) { - onewire_host_reset_search(worker->host); - - // key found, verify - if(onewire_host_reset(worker->host)) { - onewire_host_write(worker->host, DS1990_CMD_READ_ROM); - bool key_valid = true; - for(uint8_t i = 0; i < ibutton_key_get_max_size(); i++) { - if(onewire_host_read(worker->host) != worker->key_data[i]) { - key_valid = false; - break; - } - } - - if(key_valid) { - result = true; - - furi_check(worker->key_p != NULL); - ibutton_key_set_type(worker->key_p, iButtonKeyDS1990); - ibutton_key_set_data(worker->key_p, worker->key_data, ibutton_key_get_max_size()); - } - } - } else { - onewire_host_reset_search(worker->host); - } - onewire_host_stop(worker->host); - FURI_CRITICAL_EXIT(); - return result; -} - void ibutton_worker_mode_read_start(iButtonWorker* worker) { UNUSED(worker); furi_hal_power_enable_otg(); } void ibutton_worker_mode_read_tick(iButtonWorker* worker) { - bool valid = false; - if(ibutton_worker_read_dallas(worker)) { - valid = true; - } else if(ibutton_worker_read_comparator(worker)) { - valid = true; - } - - if(valid) { + if(ibutton_protocols_read(worker->protocols, worker->key)) { if(worker->read_cb != NULL) { worker->read_cb(worker->cb_ctx); } - ibutton_worker_switch_mode(worker, iButtonWorkerIdle); + ibutton_worker_switch_mode(worker, iButtonWorkerModeIdle); } } @@ -208,88 +94,14 @@ void ibutton_worker_mode_read_stop(iButtonWorker* worker) { } /*********************** EMULATE ***********************/ -static void onewire_slave_callback(void* context) { - furi_assert(context); - iButtonWorker* worker = context; - ibutton_worker_notify_emulate(worker); -} - -void ibutton_worker_emulate_dallas_start(iButtonWorker* worker) { - uint8_t* device_id = onewire_device_get_id_p(worker->device); - const uint8_t* key_id = ibutton_key_get_data_p(worker->key_p); - const uint8_t key_size = ibutton_key_get_max_size(); - memcpy(device_id, key_id, key_size); - - onewire_slave_attach(worker->slave, worker->device); - onewire_slave_set_result_callback(worker->slave, onewire_slave_callback, worker); - onewire_slave_start(worker->slave); -} - -void ibutton_worker_emulate_dallas_stop(iButtonWorker* worker) { - onewire_slave_stop(worker->slave); - onewire_slave_detach(worker->slave); -} - -void ibutton_worker_emulate_timer_cb(void* context) { - furi_assert(context); - iButtonWorker* worker = context; - - LevelDuration level = - protocol_dict_encoder_yield(worker->protocols, worker->protocol_to_encode); - - furi_hal_ibutton_emulate_set_next(level_duration_get_duration(level)); - - if(level_duration_get_level(level)) { - furi_hal_ibutton_pin_high(); - } else { - furi_hal_ibutton_pin_low(); - } -} - -void ibutton_worker_emulate_timer_start(iButtonWorker* worker) { - furi_assert(worker->key_p); - const uint8_t* key_id = ibutton_key_get_data_p(worker->key_p); - const uint8_t key_size = ibutton_key_get_max_size(); - - switch(ibutton_key_get_type(worker->key_p)) { - case iButtonKeyDS1990: - return; - break; - case iButtonKeyCyfral: - worker->protocol_to_encode = iButtonProtocolCyfral; - break; - case iButtonKeyMetakom: - worker->protocol_to_encode = iButtonProtocolMetakom; - break; - } - - protocol_dict_set_data(worker->protocols, worker->protocol_to_encode, key_id, key_size); - protocol_dict_encoder_start(worker->protocols, worker->protocol_to_encode); - - furi_hal_ibutton_start_drive(); - furi_hal_ibutton_emulate_start(0, ibutton_worker_emulate_timer_cb, worker); -} - -void ibutton_worker_emulate_timer_stop(iButtonWorker* worker) { - UNUSED(worker); - furi_hal_ibutton_emulate_stop(); -} void ibutton_worker_mode_emulate_start(iButtonWorker* worker) { - furi_assert(worker->key_p); + furi_assert(worker->key); furi_hal_rfid_pins_reset(); furi_hal_rfid_pin_pull_pulldown(); - switch(ibutton_key_get_type(worker->key_p)) { - case iButtonKeyDS1990: - ibutton_worker_emulate_dallas_start(worker); - break; - case iButtonKeyCyfral: - case iButtonKeyMetakom: - ibutton_worker_emulate_timer_start(worker); - break; - } + ibutton_protocols_emulate_start(worker->protocols, worker->key); } void ibutton_worker_mode_emulate_tick(iButtonWorker* worker) { @@ -297,56 +109,45 @@ void ibutton_worker_mode_emulate_tick(iButtonWorker* worker) { } void ibutton_worker_mode_emulate_stop(iButtonWorker* worker) { - furi_assert(worker->key_p); + furi_assert(worker->key); + + ibutton_protocols_emulate_stop(worker->protocols, worker->key); furi_hal_rfid_pins_reset(); - - switch(ibutton_key_get_type(worker->key_p)) { - case iButtonKeyDS1990: - ibutton_worker_emulate_dallas_stop(worker); - break; - case iButtonKeyCyfral: - case iButtonKeyMetakom: - ibutton_worker_emulate_timer_stop(worker); - break; - } } /*********************** WRITE ***********************/ -void ibutton_worker_mode_write_start(iButtonWorker* worker) { +void ibutton_worker_mode_write_common_start(iButtonWorker* worker) { //-V524 + UNUSED(worker); furi_hal_power_enable_otg(); - onewire_host_start(worker->host); } -void ibutton_worker_mode_write_tick(iButtonWorker* worker) { - furi_check(worker->key_p != NULL); - iButtonWriterResult writer_result = ibutton_writer_write(worker->writer, worker->key_p); - iButtonWorkerWriteResult result; - switch(writer_result) { - case iButtonWriterOK: - result = iButtonWorkerWriteOK; - break; - case iButtonWriterSameKey: - result = iButtonWorkerWriteSameKey; - break; - case iButtonWriterNoDetect: - result = iButtonWorkerWriteNoDetect; - break; - case iButtonWriterCannotWrite: - result = iButtonWorkerWriteCannotWrite; - break; - default: - result = iButtonWorkerWriteNoDetect; - break; - } +void ibutton_worker_mode_write_blank_tick(iButtonWorker* worker) { + furi_assert(worker->key); + const bool success = ibutton_protocols_write_blank(worker->protocols, worker->key); + // TODO: pass a proper result to the callback + const iButtonWorkerWriteResult result = success ? iButtonWorkerWriteOK : + iButtonWorkerWriteNoDetect; if(worker->write_cb != NULL) { worker->write_cb(worker->cb_ctx, result); } } -void ibutton_worker_mode_write_stop(iButtonWorker* worker) { - furi_hal_power_disable_otg(); - onewire_host_stop(worker->host); +void ibutton_worker_mode_write_copy_tick(iButtonWorker* worker) { + furi_assert(worker->key); + + const bool success = ibutton_protocols_write_copy(worker->protocols, worker->key); + // TODO: pass a proper result to the callback + const iButtonWorkerWriteResult result = success ? iButtonWorkerWriteOK : + iButtonWorkerWriteNoDetect; + if(worker->write_cb != NULL) { + worker->write_cb(worker->cb_ctx, result); + } +} + +void ibutton_worker_mode_write_common_stop(iButtonWorker* worker) { //-V524 + UNUSED(worker); + furi_hal_power_disable_otg(); } diff --git a/lib/one_wire/ibutton/ibutton_writer.c b/lib/one_wire/ibutton/ibutton_writer.c deleted file mode 100644 index 84d122491..000000000 --- a/lib/one_wire/ibutton/ibutton_writer.c +++ /dev/null @@ -1,298 +0,0 @@ -#include -#include -#include "ibutton_writer.h" -#include "ibutton_key_command.h" - -/*********************** PRIVATE ***********************/ - -struct iButtonWriter { - OneWireHost* host; -}; - -static void writer_write_one_bit(iButtonWriter* writer, bool value, uint32_t delay) { - onewire_host_write_bit(writer->host, value); - furi_delay_us(delay); -} - -static void writer_write_byte_ds1990(iButtonWriter* writer, uint8_t data) { - for(uint8_t n_bit = 0; n_bit < 8; n_bit++) { - onewire_host_write_bit(writer->host, data & 1); - furi_delay_us(5000); - data = data >> 1; - } -} - -static bool writer_compare_key_ds1990(iButtonWriter* writer, iButtonKey* key) { - bool result = false; - - if(ibutton_key_get_type(key) == iButtonKeyDS1990) { - FURI_CRITICAL_ENTER(); - bool presence = onewire_host_reset(writer->host); - - if(presence) { - onewire_host_write(writer->host, DS1990_CMD_READ_ROM); - - result = true; - for(uint8_t i = 0; i < ibutton_key_get_data_size(key); i++) { - if(ibutton_key_get_data_p(key)[i] != onewire_host_read(writer->host)) { - result = false; - break; - } - } - } - - FURI_CRITICAL_EXIT(); - } - - return result; -} - -static bool writer_write_TM2004(iButtonWriter* writer, iButtonKey* key) { - uint8_t answer; - bool result = true; - - if(ibutton_key_get_type(key) == iButtonKeyDS1990) { - FURI_CRITICAL_ENTER(); - - // write rom, addr is 0x0000 - onewire_host_reset(writer->host); - onewire_host_write(writer->host, TM2004_CMD_WRITE_ROM); - onewire_host_write(writer->host, 0x00); - onewire_host_write(writer->host, 0x00); - - // write key - for(uint8_t i = 0; i < ibutton_key_get_data_size(key); i++) { - // write key byte - onewire_host_write(writer->host, ibutton_key_get_data_p(key)[i]); - answer = onewire_host_read(writer->host); - // TODO: check answer CRC - - // pulse indicating that data is correct - furi_delay_us(600); - writer_write_one_bit(writer, 1, 50000); - - // read written key byte - answer = onewire_host_read(writer->host); //-V519 - - // check that written and read are same - if(ibutton_key_get_data_p(key)[i] != answer) { - result = false; - break; - } - } - - if(!writer_compare_key_ds1990(writer, key)) { - result = false; - } - - onewire_host_reset(writer->host); - - FURI_CRITICAL_EXIT(); - } else { - result = false; - } - - return result; -} - -static bool writer_write_1990_1(iButtonWriter* writer, iButtonKey* key) { - bool result = false; - - if(ibutton_key_get_type(key) == iButtonKeyDS1990) { - FURI_CRITICAL_ENTER(); - - // unlock - onewire_host_reset(writer->host); - onewire_host_write(writer->host, RW1990_1_CMD_WRITE_RECORD_FLAG); - furi_delay_us(10); - writer_write_one_bit(writer, 0, 5000); - - // write key - onewire_host_reset(writer->host); - onewire_host_write(writer->host, RW1990_1_CMD_WRITE_ROM); - for(uint8_t i = 0; i < ibutton_key_get_data_size(key); i++) { - // inverted key for RW1990.1 - writer_write_byte_ds1990(writer, ~ibutton_key_get_data_p(key)[i]); - furi_delay_us(30000); - } - - // lock - onewire_host_write(writer->host, RW1990_1_CMD_WRITE_RECORD_FLAG); - writer_write_one_bit(writer, 1, 10000); - - FURI_CRITICAL_EXIT(); - - if(writer_compare_key_ds1990(writer, key)) { - result = true; - } - } - - return result; -} - -static bool writer_write_1990_2(iButtonWriter* writer, iButtonKey* key) { - bool result = false; - - if(ibutton_key_get_type(key) == iButtonKeyDS1990) { - FURI_CRITICAL_ENTER(); - - // unlock - onewire_host_reset(writer->host); - onewire_host_write(writer->host, RW1990_2_CMD_WRITE_RECORD_FLAG); - furi_delay_us(10); - writer_write_one_bit(writer, 1, 5000); - - // write key - onewire_host_reset(writer->host); - onewire_host_write(writer->host, RW1990_2_CMD_WRITE_ROM); - for(uint8_t i = 0; i < ibutton_key_get_data_size(key); i++) { - writer_write_byte_ds1990(writer, ibutton_key_get_data_p(key)[i]); - furi_delay_us(30000); - } - - // lock - onewire_host_write(writer->host, RW1990_2_CMD_WRITE_RECORD_FLAG); - writer_write_one_bit(writer, 0, 10000); - - FURI_CRITICAL_EXIT(); - - if(writer_compare_key_ds1990(writer, key)) { - result = true; - } - } - - return result; -} - -/* -// TODO: adapt and test -static bool writer_write_TM01( - iButtonWriter* writer, - iButtonKey type, - const uint8_t* key, - uint8_t key_length) { - bool result = true; - - { - // TODO test and encoding - FURI_CRITICAL_ENTER(); - - // unlock - onewire_host_reset(writer->host); - onewire_host_write(writer->host, TM01::CMD_WRITE_RECORD_FLAG); - onewire_write_one_bit(1, 10000); - - // write key - onewire_host_reset(writer->host); - onewire_host_write(writer->host, TM01::CMD_WRITE_ROM); - - // TODO: key types - //if(type == KEY_METAKOM || type == KEY_CYFRAL) { - //} else { - for(uint8_t i = 0; i < key->get_type_data_size(); i++) { - write_byte_ds1990(key->get_data()[i]); - furi_delay_us(10000); - } - //} - - // lock - onewire_host_write(writer->host, TM01::CMD_WRITE_RECORD_FLAG); - onewire_write_one_bit(0, 10000); - - FURI_CRITICAL_EXIT(); - } - - if(!compare_key_ds1990(key)) { - result = false; - } - - { - FURI_CRITICAL_ENTER(); - - if(key->get_key_type() == iButtonKeyType::KeyMetakom || - key->get_key_type() == iButtonKeyType::KeyCyfral) { - onewire_host_reset(writer->host); - if(key->get_key_type() == iButtonKeyType::KeyCyfral) - onewire_host_write(writer->host, TM01::CMD_SWITCH_TO_CYFRAL); - else - onewire_host_write(writer->host, TM01::CMD_SWITCH_TO_METAKOM); - onewire_write_one_bit(1); - } - - FURI_CRITICAL_EXIT(); - } - - return result; -} -*/ - -static iButtonWriterResult writer_write_DS1990(iButtonWriter* writer, iButtonKey* key) { - iButtonWriterResult result = iButtonWriterNoDetect; - bool same_key = writer_compare_key_ds1990(writer, key); - - if(!same_key) { - // currently we can write: - // RW1990_1, TM08v2, TM08vi-2 by write_1990_1() - // RW1990_2 by write_1990_2() - // RW2004, RW2004, TM2004 with EEPROM by write_TM2004(); - - bool write_result = true; - do { - if(writer_write_1990_1(writer, key)) break; - if(writer_write_1990_2(writer, key)) break; - if(writer_write_TM2004(writer, key)) break; - write_result = false; - } while(false); - - if(write_result) { - result = iButtonWriterOK; - } else { - result = iButtonWriterCannotWrite; - } - } else { - result = iButtonWriterSameKey; - } - - return result; -} - -/*********************** PUBLIC ***********************/ - -iButtonWriter* ibutton_writer_alloc(OneWireHost* host) { - iButtonWriter* writer = malloc(sizeof(iButtonWriter)); - writer->host = host; - return writer; -} - -void ibutton_writer_free(iButtonWriter* writer) { - free(writer); -} - -iButtonWriterResult ibutton_writer_write(iButtonWriter* writer, iButtonKey* key) { - iButtonWriterResult result = iButtonWriterNoDetect; - - furi_kernel_lock(); - bool blank_present = onewire_host_reset(writer->host); - furi_kernel_unlock(); - - if(blank_present) { - switch(ibutton_key_get_type(key)) { - case iButtonKeyDS1990: - result = writer_write_DS1990(writer, key); - default: - break; - } - } - - return result; -} - -void ibutton_writer_start(iButtonWriter* writer) { - furi_hal_power_enable_otg(); - onewire_host_start(writer->host); -} - -void ibutton_writer_stop(iButtonWriter* writer) { - onewire_host_stop(writer->host); - furi_hal_power_disable_otg(); -} diff --git a/lib/one_wire/ibutton/ibutton_writer.h b/lib/one_wire/ibutton/ibutton_writer.h deleted file mode 100644 index 6dbdbb27a..000000000 --- a/lib/one_wire/ibutton/ibutton_writer.h +++ /dev/null @@ -1,60 +0,0 @@ -/** - * @file ibutton_writer.h - * - * iButton blanks writer - */ - -#pragma once -#include -#include "ibutton_key.h" -#include "../one_wire_host.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef enum { - iButtonWriterOK, - iButtonWriterSameKey, - iButtonWriterNoDetect, - iButtonWriterCannotWrite, -} iButtonWriterResult; - -typedef struct iButtonWriter iButtonWriter; - -/** - * Allocate writer - * @param host - * @return iButtonWriter* - */ -iButtonWriter* ibutton_writer_alloc(OneWireHost* host); - -/** - * Deallocate writer - * @param writer - */ -void ibutton_writer_free(iButtonWriter* writer); - -/** - * Write key to blank - * @param writer - * @param key - * @return iButtonWriterResult - */ -iButtonWriterResult ibutton_writer_write(iButtonWriter* writer, iButtonKey* key); - -/** - * Start writing. Must be called before write attempt - * @param writer - */ -void ibutton_writer_start(iButtonWriter* writer); - -/** - * Stop writing - * @param writer - */ -void ibutton_writer_stop(iButtonWriter* writer); - -#ifdef __cplusplus -} -#endif diff --git a/lib/one_wire/ibutton/protocols/blanks/rw1990.c b/lib/one_wire/ibutton/protocols/blanks/rw1990.c new file mode 100644 index 000000000..d3350fcf0 --- /dev/null +++ b/lib/one_wire/ibutton/protocols/blanks/rw1990.c @@ -0,0 +1,95 @@ +#include "rw1990.h" + +#include + +#define RW1990_1_CMD_WRITE_RECORD_FLAG 0xD1 +#define RW1990_1_CMD_READ_RECORD_FLAG 0xB5 +#define RW1990_1_CMD_WRITE_ROM 0xD5 + +#define RW1990_2_CMD_WRITE_RECORD_FLAG 0x1D +#define RW1990_2_CMD_READ_RECORD_FLAG 0x1E +#define RW1990_2_CMD_WRITE_ROM 0xD5 + +#define DS1990_CMD_READ_ROM 0x33 + +static void rw1990_write_byte(OneWireHost* host, uint8_t value) { + for(uint8_t bitMask = 0x01; bitMask; bitMask <<= 1) { + onewire_host_write_bit(host, (bool)(bitMask & value)); + furi_delay_us(5000); + } +} + +static bool rw1990_read_and_compare(OneWireHost* host, const uint8_t* data, size_t data_size) { + bool success = false; + + if(onewire_host_reset(host)) { + success = true; + onewire_host_write(host, DS1990_CMD_READ_ROM); + + for(size_t i = 0; i < data_size; ++i) { + if(data[i] != onewire_host_read(host)) { + success = false; + break; + } + } + } + + return success; +} + +bool rw1990_write_v1(OneWireHost* host, const uint8_t* data, size_t data_size) { + // Unlock sequence + onewire_host_reset(host); + onewire_host_write(host, RW1990_1_CMD_WRITE_RECORD_FLAG); + furi_delay_us(10); + + onewire_host_write_bit(host, false); + furi_delay_us(5000); + + // Write data + onewire_host_reset(host); + onewire_host_write(host, RW1990_1_CMD_WRITE_ROM); + + for(size_t i = 0; i < data_size; ++i) { + // inverted key for RW1990.1 + rw1990_write_byte(host, ~(data[i])); + furi_delay_us(30000); + } + + // Lock sequence + onewire_host_write(host, RW1990_1_CMD_WRITE_RECORD_FLAG); + + onewire_host_write_bit(host, true); + furi_delay_us(10000); + + // TODO: Better error handling + return rw1990_read_and_compare(host, data, data_size); +} + +bool rw1990_write_v2(OneWireHost* host, const uint8_t* data, size_t data_size) { + // Unlock sequence + onewire_host_reset(host); + onewire_host_write(host, RW1990_2_CMD_WRITE_RECORD_FLAG); + furi_delay_us(10); + + onewire_host_write_bit(host, true); + furi_delay_us(5000); + + // Write data + onewire_host_reset(host); + onewire_host_write(host, RW1990_2_CMD_WRITE_ROM); + + for(size_t i = 0; i < data_size; ++i) { + rw1990_write_byte(host, data[i]); + furi_delay_us(30000); + } + + // Lock sequence + onewire_host_write(host, RW1990_2_CMD_WRITE_RECORD_FLAG); + + onewire_host_write_bit(host, false); + furi_delay_us(10000); + + // TODO: Better error handling + return rw1990_read_and_compare(host, data, data_size); +} diff --git a/lib/one_wire/ibutton/protocols/blanks/rw1990.h b/lib/one_wire/ibutton/protocols/blanks/rw1990.h new file mode 100644 index 000000000..bdd27f6bf --- /dev/null +++ b/lib/one_wire/ibutton/protocols/blanks/rw1990.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +#include + +bool rw1990_write_v1(OneWireHost* host, const uint8_t* data, size_t data_size); + +bool rw1990_write_v2(OneWireHost* host, const uint8_t* data, size_t data_size); diff --git a/lib/one_wire/ibutton/protocols/blanks/tm2004.c b/lib/one_wire/ibutton/protocols/blanks/tm2004.c new file mode 100644 index 000000000..ef6f0619e --- /dev/null +++ b/lib/one_wire/ibutton/protocols/blanks/tm2004.c @@ -0,0 +1,42 @@ +#include "tm2004.h" + +#include + +#define TM2004_CMD_READ_STATUS 0xAA +#define TM2004_CMD_READ_MEMORY 0xF0 +#define TM2004_CMD_WRITE_ROM 0x3C +#define TM2004_CMD_FINALIZATION 0x35 +#define TM2004_ANSWER_READ_MEMORY 0xF5 + +bool tm2004_write(OneWireHost* host, const uint8_t* data, size_t data_size) { + onewire_host_reset(host); + onewire_host_write(host, TM2004_CMD_WRITE_ROM); + // Starting writing from address 0x0000 + onewire_host_write(host, 0x00); + onewire_host_write(host, 0x00); + + size_t i; + for(i = 0; i < data_size; ++i) { + uint8_t answer; + + onewire_host_write(host, data[i]); + answer = onewire_host_read(host); + // TODO: check answer CRC + + // pulse indicating that data is correct + furi_delay_us(600); + onewire_host_write_bit(host, true); + furi_delay_us(50000); + + // read written key byte + answer = onewire_host_read(host); //-V519 + + // check that written and read are same + if(data[i] != answer) { + break; + } + } + + // TODO: Better error handling + return i == data_size; +} diff --git a/lib/one_wire/ibutton/protocols/blanks/tm2004.h b/lib/one_wire/ibutton/protocols/blanks/tm2004.h new file mode 100644 index 000000000..15713af56 --- /dev/null +++ b/lib/one_wire/ibutton/protocols/blanks/tm2004.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +#include + +bool tm2004_write(OneWireHost* host, const uint8_t* data, size_t data_size); diff --git a/lib/one_wire/ibutton/protocols/dallas/dallas_common.c b/lib/one_wire/ibutton/protocols/dallas/dallas_common.c new file mode 100644 index 000000000..57a873b1d --- /dev/null +++ b/lib/one_wire/ibutton/protocols/dallas/dallas_common.c @@ -0,0 +1,250 @@ +#include "dallas_common.h" + +#include +#include + +#define BITS_IN_BYTE 8U + +#define DALLAS_COMMON_ROM_DATA_KEY_V1 "Data" +#define DALLAS_COMMON_ROM_DATA_KEY_V2 "Rom Data" + +#define DALLAS_COMMON_COPY_SCRATCH_MIN_TIMEOUT_US 5U +#define DALLAS_COMMON_COPY_SCRATCH_POLL_COUNT 20U + +#define DALLAS_COMMON_END_ADDRESS_MASK 0x01F +#define DALLAS_COMMON_STATUS_FLAG_PF (1U << 5) +#define DALLAS_COMMON_STATUS_FLAG_OF (1U << 6) +#define DALLAS_COMMON_STATUS_FLAG_AA (1U << 7) + +#define DALLAS_COMMON_BRIEF_HEAD_COUNT 4U +#define DALLAS_COMMON_BRIEF_TAIL_COUNT 3U + +#define BITS_IN_BYTE 8U +#define BITS_IN_KBIT 1024U + +bool dallas_common_skip_rom(OneWireHost* host) { + onewire_host_write(host, DALLAS_COMMON_CMD_SKIP_ROM); + return true; +} + +bool dallas_common_read_rom(OneWireHost* host, DallasCommonRomData* rom_data) { + onewire_host_write(host, DALLAS_COMMON_CMD_READ_ROM); + onewire_host_read_bytes(host, rom_data->bytes, sizeof(DallasCommonRomData)); + + return dallas_common_is_valid_crc(rom_data); +} + +bool dallas_common_write_scratchpad( + OneWireHost* host, + uint16_t address, + const uint8_t* data, + size_t data_size) { + onewire_host_write(host, DALLAS_COMMON_CMD_WRITE_SCRATCH); + onewire_host_write(host, (uint8_t)address); + onewire_host_write(host, (uint8_t)(address >> BITS_IN_BYTE)); + + onewire_host_write_bytes(host, data, data_size); + + return true; +} + +bool dallas_common_read_scratchpad( + OneWireHost* host, + DallasCommonAddressRegs* regs, + uint8_t* data, + size_t data_size) { + onewire_host_write(host, DALLAS_COMMON_CMD_READ_SCRATCH); + onewire_host_read_bytes(host, regs->bytes, sizeof(DallasCommonAddressRegs)); + onewire_host_read_bytes(host, data, data_size); + + return true; +} + +bool dallas_common_copy_scratchpad( + OneWireHost* host, + const DallasCommonAddressRegs* regs, + uint32_t timeout_us) { + onewire_host_write(host, DALLAS_COMMON_CMD_COPY_SCRATCH); + onewire_host_write_bytes(host, regs->bytes, sizeof(DallasCommonAddressRegs)); + + const uint32_t poll_delay = + MAX(timeout_us / DALLAS_COMMON_COPY_SCRATCH_POLL_COUNT, + DALLAS_COMMON_COPY_SCRATCH_MIN_TIMEOUT_US); + + uint32_t time_elapsed; + for(time_elapsed = 0; time_elapsed < timeout_us; time_elapsed += poll_delay) { + if(!onewire_host_read_bit(host)) break; + furi_delay_us(poll_delay); + } + + return time_elapsed < timeout_us; +} + +bool dallas_common_read_mem(OneWireHost* host, uint16_t address, uint8_t* data, size_t data_size) { + onewire_host_write(host, DALLAS_COMMON_CMD_READ_MEM); + + onewire_host_write(host, (uint8_t)address); + onewire_host_write(host, (uint8_t)(address > BITS_IN_BYTE)); + + onewire_host_read_bytes(host, data, (uint16_t)data_size); + + return true; +} + +bool dallas_common_write_mem( + OneWireHost* host, + uint32_t timeout_us, + size_t page_size, + const uint8_t* data, + size_t data_size) { + // Data size must be a multiple of page size + furi_check(data_size % page_size == 0); + + DallasCommonAddressRegs regs; + uint8_t* scratch = malloc(page_size); + + size_t i; + for(i = 0; i < data_size; i += page_size) { + const uint8_t* data_ptr = data + i; + + // Write scratchpad with the next page value + if(!onewire_host_reset(host)) break; + if(!dallas_common_skip_rom(host)) break; + if(!dallas_common_write_scratchpad(host, i, data_ptr, page_size)) break; + + // Read back the scratchpad contents and address registers + if(!onewire_host_reset(host)) break; + if(!dallas_common_skip_rom(host)) break; + if(!dallas_common_read_scratchpad(host, ®s, scratch, page_size)) break; + + // Verify scratchpad contents + if(memcmp(data_ptr, scratch, page_size) != 0) break; + + // Write scratchpad to internal memory + if(!onewire_host_reset(host)) break; + if(!dallas_common_skip_rom(host)) break; + if(!dallas_common_copy_scratchpad(host, ®s, timeout_us)) break; + + // Read back the address registers again + if(!onewire_host_reset(host)) break; + if(!dallas_common_skip_rom(host)) break; + if(!dallas_common_read_scratchpad(host, ®s, scratch, 0)) break; + + // Check if AA flag is set + if(!(regs.fields.status & DALLAS_COMMON_STATUS_FLAG_AA)) break; + } + + free(scratch); + + return i == data_size; +} + +bool dallas_common_emulate_search_rom(OneWireSlave* bus, const DallasCommonRomData* rom_data) { + for(size_t i = 0; i < sizeof(DallasCommonRomData); i++) { + for(size_t j = 0; j < BITS_IN_BYTE; j++) { + bool bit = (rom_data->bytes[i] >> j) & 0x01; + + if(!onewire_slave_send_bit(bus, bit)) return false; + if(!onewire_slave_send_bit(bus, !bit)) return false; + + onewire_slave_receive_bit(bus); + // TODO: check for errors and return if any + } + } + + return true; +} + +bool dallas_common_emulate_read_rom(OneWireSlave* bus, const DallasCommonRomData* rom_data) { + return onewire_slave_send(bus, rom_data->bytes, sizeof(DallasCommonRomData)); +} + +bool dallas_common_emulate_read_mem(OneWireSlave* bus, const uint8_t* data, size_t data_size) { + bool success = false; + + union { + uint8_t bytes[sizeof(uint16_t)]; + uint16_t word; + } address; + + do { + if(!onewire_slave_receive(bus, address.bytes, sizeof(address))) break; + if(address.word >= data_size) break; + if(!onewire_slave_send(bus, data + address.word, data_size - address.word)) break; + + success = true; + } while(false); + + return success; +} + +bool dallas_common_save_rom_data(FlipperFormat* ff, const DallasCommonRomData* rom_data) { + return flipper_format_write_hex( + ff, DALLAS_COMMON_ROM_DATA_KEY_V2, rom_data->bytes, sizeof(DallasCommonRomData)); +} + +bool dallas_common_load_rom_data( + FlipperFormat* ff, + uint32_t format_version, + DallasCommonRomData* rom_data) { + switch(format_version) { + case 1: + return flipper_format_read_hex( + ff, DALLAS_COMMON_ROM_DATA_KEY_V1, rom_data->bytes, sizeof(DallasCommonRomData)); + case 2: + return flipper_format_read_hex( + ff, DALLAS_COMMON_ROM_DATA_KEY_V2, rom_data->bytes, sizeof(DallasCommonRomData)); + default: + return false; + } +} + +bool dallas_common_is_valid_crc(const DallasCommonRomData* rom_data) { + const uint8_t crc_calculated = + maxim_crc8(rom_data->bytes, sizeof(DallasCommonRomData) - 1, MAXIM_CRC8_INIT); + const uint8_t crc_received = rom_data->fields.checksum; + + return crc_calculated == crc_received; +} + +void dallas_common_render_brief_data( + FuriString* result, + const DallasCommonRomData* rom_data, + const uint8_t* sram_data, + size_t sram_data_size) { + for(size_t i = 0; i < sizeof(rom_data->bytes); ++i) { + furi_string_cat_printf(result, "%02X ", rom_data->bytes[i]); + } + + furi_string_cat_printf( + result, + "\nInternal SRAM: %zu Kbit\n", + (size_t)(sram_data_size * BITS_IN_BYTE / BITS_IN_KBIT)); + + for(size_t i = 0; i < DALLAS_COMMON_BRIEF_HEAD_COUNT; ++i) { + furi_string_cat_printf(result, "%02X ", sram_data[i]); + } + + furi_string_cat_printf(result, "[ . . . ]"); + + for(size_t i = sram_data_size - DALLAS_COMMON_BRIEF_TAIL_COUNT; i < sram_data_size; ++i) { + furi_string_cat_printf(result, " %02X", sram_data[i]); + } +} + +void dallas_common_render_crc_error(FuriString* result, const DallasCommonRomData* rom_data) { + furi_string_set(result, "CRC Error\n"); + + const size_t data_size = sizeof(DallasCommonRomData); + + for(size_t i = 0; i < data_size; ++i) { + furi_string_cat_printf(result, (i < data_size - 1) ? "%02X " : "%02X", rom_data->bytes[i]); + } +} + +void dallas_common_apply_edits(DallasCommonRomData* rom_data, uint8_t family_code) { + rom_data->fields.family_code = family_code; + const uint8_t crc = + maxim_crc8(rom_data->bytes, sizeof(DallasCommonRomData) - 1, MAXIM_CRC8_INIT); + rom_data->fields.checksum = crc; +} diff --git a/lib/one_wire/ibutton/protocols/dallas/dallas_common.h b/lib/one_wire/ibutton/protocols/dallas/dallas_common.h new file mode 100644 index 000000000..7ad13eb2c --- /dev/null +++ b/lib/one_wire/ibutton/protocols/dallas/dallas_common.h @@ -0,0 +1,107 @@ +#pragma once + +#include + +#include +#include + +#define DALLAS_COMMON_MANUFACTURER_NAME "Dallas" + +#define DALLAS_COMMON_CMD_READ_ROM 0x33U +#define DALLAS_COMMON_CMD_MATCH_ROM 0x55U +#define DALLAS_COMMON_CMD_SKIP_ROM 0xCCU +#define DALLAS_COMMON_CMD_COND_SEARCH 0xECU +#define DALLAS_COMMON_CMD_SEARCH_ROM 0xF0U + +#define DALLAS_COMMON_CMD_READ_SCRATCH 0xAAU +#define DALLAS_COMMON_CMD_WRITE_SCRATCH 0x0FU +#define DALLAS_COMMON_CMD_COPY_SCRATCH 0x55U + +#define DALLAS_COMMON_CMD_READ_MEM 0xF0U + +#define DALLAS_COMMON_CMD_OVERDRIVE_SKIP_ROM 0x3CU +#define DALLAS_COMMON_CMD_OVERDRIVE_MATCH_ROM 0x69U + +typedef enum { + DallasCommonCommandStateIdle, + DallasCommonCommandStateRomCmd, + DallasCommonCommandStateMemCmd, +} DallasCommonCommandState; + +typedef union { + struct { + uint8_t family_code; + uint8_t serial_number[6]; + uint8_t checksum; + } fields; + uint8_t bytes[8]; +} DallasCommonRomData; + +typedef union { + struct { + uint8_t address_lo; + uint8_t address_hi; + uint8_t status; + } fields; + uint8_t bytes[3]; +} DallasCommonAddressRegs; + +/* Standard(ish) iButton commands */ +bool dallas_common_skip_rom(OneWireHost* host); + +bool dallas_common_read_rom(OneWireHost* host, DallasCommonRomData* rom_data); + +bool dallas_common_write_scratchpad( + OneWireHost* host, + uint16_t address, + const uint8_t* data, + size_t data_size); + +bool dallas_common_read_scratchpad( + OneWireHost* host, + DallasCommonAddressRegs* regs, + uint8_t* data, + size_t data_size); + +bool dallas_common_copy_scratchpad( + OneWireHost* host, + const DallasCommonAddressRegs* regs, + uint32_t timeout_us); + +bool dallas_common_read_mem(OneWireHost* host, uint16_t address, uint8_t* data, size_t data_size); + +/* Combined operations */ +bool dallas_common_write_mem( + OneWireHost* host, + uint32_t timeout_us, + size_t page_size, + const uint8_t* data, + size_t data_size); + +/* Emulation */ +bool dallas_common_emulate_search_rom(OneWireSlave* bus, const DallasCommonRomData* rom_data); + +bool dallas_common_emulate_read_rom(OneWireSlave* bus, const DallasCommonRomData* rom_data); + +bool dallas_common_emulate_read_mem(OneWireSlave* bus, const uint8_t* data, size_t data_size); + +/* Save & Load */ +bool dallas_common_save_rom_data(FlipperFormat* ff, const DallasCommonRomData* rom_data); + +bool dallas_common_load_rom_data( + FlipperFormat* ff, + uint32_t format_version, + DallasCommonRomData* rom_data); + +/* Miscellaneous */ +bool dallas_common_is_valid_crc(const DallasCommonRomData* rom_data); + +void dallas_common_render_brief_data( + FuriString* result, + const DallasCommonRomData* rom_data, + const uint8_t* sram_data, + size_t sram_data_size); + +void dallas_common_render_crc_error(FuriString* result, const DallasCommonRomData* rom_data); + +void dallas_common_apply_edits(DallasCommonRomData* rom_data, uint8_t family_code); diff --git a/lib/one_wire/ibutton/protocols/dallas/protocol_dallas_base.h b/lib/one_wire/ibutton/protocols/dallas/protocol_dallas_base.h new file mode 100644 index 000000000..b4edb2b20 --- /dev/null +++ b/lib/one_wire/ibutton/protocols/dallas/protocol_dallas_base.h @@ -0,0 +1,39 @@ +#pragma once + +#include "../protocol_common_i.h" + +#include + +#include +#include + +typedef bool (*iButtonProtocolDallasReadWriteFunc)(OneWireHost*, iButtonProtocolData*); +typedef void (*iButtonProtocolDallasEmulateFunc)(OneWireSlave*, iButtonProtocolData*); +typedef bool (*iButtonProtocolDallasSaveFunc)(FlipperFormat*, const iButtonProtocolData*); +typedef bool (*iButtonProtocolDallasLoadFunc)(FlipperFormat*, uint32_t, iButtonProtocolData*); +typedef void (*iButtonProtocolDallasRenderDataFunc)(FuriString*, const iButtonProtocolData*); +typedef bool (*iButtonProtocolDallasIsValidFunc)(const iButtonProtocolData*); +typedef void ( + *iButtonProtocolDallasGetEditableDataFunc)(iButtonEditableData*, iButtonProtocolData*); +typedef void (*iButtonProtocolDallasApplyEditsFunc)(iButtonProtocolData*); + +typedef struct { + const uint8_t family_code; + const uint32_t features; + const size_t data_size; + const char* manufacturer; + const char* name; + + iButtonProtocolDallasReadWriteFunc read; + iButtonProtocolDallasReadWriteFunc write_blank; + iButtonProtocolDallasReadWriteFunc write_copy; + iButtonProtocolDallasEmulateFunc emulate; + iButtonProtocolDallasSaveFunc save; + iButtonProtocolDallasLoadFunc load; + iButtonProtocolDallasRenderDataFunc render_data; + iButtonProtocolDallasRenderDataFunc render_brief_data; + iButtonProtocolDallasRenderDataFunc render_error; + iButtonProtocolDallasIsValidFunc is_valid; + iButtonProtocolDallasGetEditableDataFunc get_editable_data; + iButtonProtocolDallasApplyEditsFunc apply_edits; +} iButtonProtocolDallasBase; diff --git a/lib/one_wire/ibutton/protocols/dallas/protocol_ds1990.c b/lib/one_wire/ibutton/protocols/dallas/protocol_ds1990.c new file mode 100644 index 000000000..0d9c937ee --- /dev/null +++ b/lib/one_wire/ibutton/protocols/dallas/protocol_ds1990.c @@ -0,0 +1,144 @@ +#include "protocol_ds1990.h" + +#include +#include + +#include "dallas_common.h" + +#include "../blanks/rw1990.h" +#include "../blanks/tm2004.h" + +#define DS1990_FAMILY_CODE 0x01U +#define DS1990_FAMILY_NAME "DS1990" + +#define DS1990_CMD_READ_ROM 0x0FU + +typedef struct { + OneWireSlave* bus; +} DS1990ProtocolState; + +typedef struct { + DallasCommonRomData rom_data; + DS1990ProtocolState state; +} DS1990ProtocolData; + +static bool dallas_ds1990_read(OneWireHost*, iButtonProtocolData*); +static bool dallas_ds1990_write_blank(OneWireHost*, iButtonProtocolData*); +static void dallas_ds1990_emulate(OneWireSlave*, iButtonProtocolData*); +static bool dallas_ds1990_load(FlipperFormat*, uint32_t, iButtonProtocolData*); +static bool dallas_ds1990_save(FlipperFormat*, const iButtonProtocolData*); +static void dallas_ds1990_render_brief_data(FuriString*, const iButtonProtocolData*); +static void dallas_ds1990_render_error(FuriString*, const iButtonProtocolData*); +static bool dallas_ds1990_is_data_valid(const iButtonProtocolData*); +static void dallas_ds1990_get_editable_data(iButtonEditableData*, iButtonProtocolData*); +static void dallas_ds1990_apply_edits(iButtonProtocolData*); + +const iButtonProtocolDallasBase ibutton_protocol_ds1990 = { + .family_code = DS1990_FAMILY_CODE, + .features = iButtonProtocolFeatureWriteBlank, + .data_size = sizeof(DS1990ProtocolData), + .manufacturer = DALLAS_COMMON_MANUFACTURER_NAME, + .name = DS1990_FAMILY_NAME, + + .read = dallas_ds1990_read, + .write_blank = dallas_ds1990_write_blank, + .write_copy = NULL, /* No data to write a copy */ + .emulate = dallas_ds1990_emulate, + .save = dallas_ds1990_save, + .load = dallas_ds1990_load, + .render_data = NULL, /* No data to render */ + .render_brief_data = dallas_ds1990_render_brief_data, + .render_error = dallas_ds1990_render_error, + .is_valid = dallas_ds1990_is_data_valid, + .get_editable_data = dallas_ds1990_get_editable_data, + .apply_edits = dallas_ds1990_apply_edits, +}; + +bool dallas_ds1990_read(OneWireHost* host, iButtonProtocolData* protocol_data) { + DS1990ProtocolData* data = protocol_data; + return onewire_host_reset(host) && dallas_common_read_rom(host, &data->rom_data); +} + +bool dallas_ds1990_write_blank(OneWireHost* host, iButtonProtocolData* protocol_data) { + DS1990ProtocolData* data = protocol_data; + + return rw1990_write_v1(host, data->rom_data.bytes, sizeof(DallasCommonRomData)) || + rw1990_write_v2(host, data->rom_data.bytes, sizeof(DallasCommonRomData)) || + tm2004_write(host, data->rom_data.bytes, sizeof(DallasCommonRomData)); +} + +static bool dallas_ds1990_command_callback(uint8_t command, void* context) { + furi_assert(context); + DS1990ProtocolData* data = context; + OneWireSlave* bus = data->state.bus; + + switch(command) { + case DALLAS_COMMON_CMD_SEARCH_ROM: + dallas_common_emulate_search_rom(bus, &data->rom_data); + break; + case DALLAS_COMMON_CMD_READ_ROM: + case DS1990_CMD_READ_ROM: + dallas_common_emulate_read_rom(bus, &data->rom_data); + break; + default: + break; + } + + // No support for multiple consecutive commands + return false; +} + +void dallas_ds1990_emulate(OneWireSlave* bus, iButtonProtocolData* protocol_data) { + DS1990ProtocolData* data = protocol_data; + data->state.bus = bus; + + onewire_slave_set_reset_callback(bus, NULL, NULL); + onewire_slave_set_command_callback(bus, dallas_ds1990_command_callback, protocol_data); +} + +bool dallas_ds1990_save(FlipperFormat* ff, const iButtonProtocolData* protocol_data) { + const DS1990ProtocolData* data = protocol_data; + return dallas_common_save_rom_data(ff, &data->rom_data); +} + +bool dallas_ds1990_load( + FlipperFormat* ff, + uint32_t format_version, + iButtonProtocolData* protocol_data) { + DS1990ProtocolData* data = protocol_data; + return dallas_common_load_rom_data(ff, format_version, &data->rom_data); +} + +void dallas_ds1990_render_brief_data(FuriString* result, const iButtonProtocolData* protocol_data) { + const DS1990ProtocolData* data = protocol_data; + + for(size_t i = 0; i < sizeof(DallasCommonRomData); ++i) { + furi_string_cat_printf(result, "%02X ", data->rom_data.bytes[i]); + } +} + +void dallas_ds1990_render_error(FuriString* result, const iButtonProtocolData* protocol_data) { + const DS1990ProtocolData* data = protocol_data; + + if(!dallas_common_is_valid_crc(&data->rom_data)) { + dallas_common_render_crc_error(result, &data->rom_data); + } +} + +bool dallas_ds1990_is_data_valid(const iButtonProtocolData* protocol_data) { + const DS1990ProtocolData* data = protocol_data; + return dallas_common_is_valid_crc(&data->rom_data); +} + +void dallas_ds1990_get_editable_data( + iButtonEditableData* editable_data, + iButtonProtocolData* protocol_data) { + DS1990ProtocolData* data = protocol_data; + editable_data->ptr = data->rom_data.bytes; + editable_data->size = sizeof(DallasCommonRomData); +} + +void dallas_ds1990_apply_edits(iButtonProtocolData* protocol_data) { + DS1990ProtocolData* data = protocol_data; + dallas_common_apply_edits(&data->rom_data, DS1990_FAMILY_CODE); +} diff --git a/lib/one_wire/ibutton/protocols/dallas/protocol_ds1990.h b/lib/one_wire/ibutton/protocols/dallas/protocol_ds1990.h new file mode 100644 index 000000000..de3da0e4c --- /dev/null +++ b/lib/one_wire/ibutton/protocols/dallas/protocol_ds1990.h @@ -0,0 +1,5 @@ +#pragma once + +#include "protocol_dallas_base.h" + +extern const iButtonProtocolDallasBase ibutton_protocol_ds1990; diff --git a/lib/one_wire/ibutton/protocols/dallas/protocol_ds1992.c b/lib/one_wire/ibutton/protocols/dallas/protocol_ds1992.c new file mode 100644 index 000000000..131bc634a --- /dev/null +++ b/lib/one_wire/ibutton/protocols/dallas/protocol_ds1992.c @@ -0,0 +1,218 @@ +#include "protocol_ds1992.h" + +#include +#include + +#include "dallas_common.h" + +#include "../blanks/tm2004.h" + +#define DS1992_FAMILY_CODE 0x08U +#define DS1992_FAMILY_NAME "DS1992" + +#define DS1992_SRAM_DATA_SIZE 128U +#define DS1992_SRAM_PAGE_SIZE 4U +#define DS1992_COPY_SCRATCH_TIMEOUT_US 100U + +#define DS1992_DATA_BYTE_COUNT 4U + +#define DS1992_SRAM_DATA_KEY "Sram Data" + +typedef struct { + OneWireSlave* bus; + DallasCommonCommandState command_state; +} DS1992ProtocolState; + +typedef struct { + DallasCommonRomData rom_data; + uint8_t sram_data[DS1992_SRAM_DATA_SIZE]; + DS1992ProtocolState state; +} DS1992ProtocolData; + +static bool dallas_ds1992_read(OneWireHost*, void*); +static bool dallas_ds1992_write_blank(OneWireHost*, iButtonProtocolData*); +static bool dallas_ds1992_write_copy(OneWireHost*, iButtonProtocolData*); +static void dallas_ds1992_emulate(OneWireSlave*, iButtonProtocolData*); +static bool dallas_ds1992_load(FlipperFormat*, uint32_t, iButtonProtocolData*); +static bool dallas_ds1992_save(FlipperFormat*, const iButtonProtocolData*); +static void dallas_ds1992_render_data(FuriString*, const iButtonProtocolData*); +static void dallas_ds1992_render_brief_data(FuriString*, const iButtonProtocolData*); +static void dallas_ds1992_render_error(FuriString*, const iButtonProtocolData*); +static bool dallas_ds1992_is_data_valid(const iButtonProtocolData*); +static void dallas_ds1992_get_editable_data(iButtonEditableData*, iButtonProtocolData*); +static void dallas_ds1992_apply_edits(iButtonProtocolData*); + +const iButtonProtocolDallasBase ibutton_protocol_ds1992 = { + .family_code = DS1992_FAMILY_CODE, + .features = iButtonProtocolFeatureExtData | iButtonProtocolFeatureWriteBlank | + iButtonProtocolFeatureWriteCopy, + .data_size = sizeof(DS1992ProtocolData), + .manufacturer = DALLAS_COMMON_MANUFACTURER_NAME, + .name = DS1992_FAMILY_NAME, + + .read = dallas_ds1992_read, + .write_blank = dallas_ds1992_write_blank, + .write_copy = dallas_ds1992_write_copy, + .emulate = dallas_ds1992_emulate, + .save = dallas_ds1992_save, + .load = dallas_ds1992_load, + .render_data = dallas_ds1992_render_data, + .render_brief_data = dallas_ds1992_render_brief_data, + .render_error = dallas_ds1992_render_error, + .is_valid = dallas_ds1992_is_data_valid, + .get_editable_data = dallas_ds1992_get_editable_data, + .apply_edits = dallas_ds1992_apply_edits, +}; + +bool dallas_ds1992_read(OneWireHost* host, iButtonProtocolData* protocol_data) { + DS1992ProtocolData* data = protocol_data; + return onewire_host_reset(host) && dallas_common_read_rom(host, &data->rom_data) && + dallas_common_read_mem(host, 0, data->sram_data, DS1992_SRAM_DATA_SIZE); +} + +bool dallas_ds1992_write_blank(OneWireHost* host, iButtonProtocolData* protocol_data) { + DS1992ProtocolData* data = protocol_data; + // TODO: Make this work, currently broken + return tm2004_write(host, (uint8_t*)data, sizeof(DallasCommonRomData) + DS1992_SRAM_DATA_SIZE); +} + +bool dallas_ds1992_write_copy(OneWireHost* host, iButtonProtocolData* protocol_data) { + DS1992ProtocolData* data = protocol_data; + return dallas_common_write_mem( + host, + DS1992_COPY_SCRATCH_TIMEOUT_US, + DS1992_SRAM_PAGE_SIZE, + data->sram_data, + DS1992_SRAM_DATA_SIZE); +} + +static void dallas_ds1992_reset_callback(void* context) { + furi_assert(context); + DS1992ProtocolData* data = context; + data->state.command_state = DallasCommonCommandStateIdle; +} + +static bool dallas_ds1992_command_callback(uint8_t command, void* context) { + furi_assert(context); + DS1992ProtocolData* data = context; + OneWireSlave* bus = data->state.bus; + + switch(command) { + case DALLAS_COMMON_CMD_SEARCH_ROM: + if(data->state.command_state == DallasCommonCommandStateIdle) { + data->state.command_state = DallasCommonCommandStateRomCmd; + return dallas_common_emulate_search_rom(bus, &data->rom_data); + + } else if(data->state.command_state == DallasCommonCommandStateRomCmd) { + data->state.command_state = DallasCommonCommandStateMemCmd; + dallas_common_emulate_read_mem(bus, data->sram_data, DS1992_SRAM_DATA_SIZE); + return false; + + } else { + return false; + } + + case DALLAS_COMMON_CMD_READ_ROM: + if(data->state.command_state == DallasCommonCommandStateIdle) { + data->state.command_state = DallasCommonCommandStateRomCmd; + return dallas_common_emulate_read_rom(bus, &data->rom_data); + } else { + return false; + } + + case DALLAS_COMMON_CMD_SKIP_ROM: + if(data->state.command_state == DallasCommonCommandStateIdle) { + data->state.command_state = DallasCommonCommandStateRomCmd; + return true; + } else { + return false; + } + + default: + return false; + } +} + +void dallas_ds1992_emulate(OneWireSlave* bus, iButtonProtocolData* protocol_data) { + DS1992ProtocolData* data = protocol_data; + data->state.bus = bus; + + onewire_slave_set_reset_callback(bus, dallas_ds1992_reset_callback, protocol_data); + onewire_slave_set_command_callback(bus, dallas_ds1992_command_callback, protocol_data); +} + +bool dallas_ds1992_load( + FlipperFormat* ff, + uint32_t format_version, + iButtonProtocolData* protocol_data) { + DS1992ProtocolData* data = protocol_data; + bool success = false; + + do { + if(format_version < 2) break; + if(!dallas_common_load_rom_data(ff, format_version, &data->rom_data)) break; + if(!flipper_format_read_hex( + ff, DS1992_SRAM_DATA_KEY, data->sram_data, DS1992_SRAM_DATA_SIZE)) + break; + success = true; + } while(false); + + return success; +} + +bool dallas_ds1992_save(FlipperFormat* ff, const iButtonProtocolData* protocol_data) { + const DS1992ProtocolData* data = protocol_data; + bool success = false; + + do { + if(!dallas_common_save_rom_data(ff, &data->rom_data)) break; + if(!flipper_format_write_hex( + ff, DS1992_SRAM_DATA_KEY, data->sram_data, DS1992_SRAM_DATA_SIZE)) + break; + success = true; + } while(false); + + return success; +} + +void dallas_ds1992_render_data(FuriString* result, const iButtonProtocolData* protocol_data) { + const DS1992ProtocolData* data = protocol_data; + pretty_format_bytes_hex_canonical( + result, + DS1992_DATA_BYTE_COUNT, + PRETTY_FORMAT_FONT_MONOSPACE, + data->sram_data, + DS1992_SRAM_DATA_SIZE); +} + +void dallas_ds1992_render_brief_data(FuriString* result, const iButtonProtocolData* protocol_data) { + const DS1992ProtocolData* data = protocol_data; + dallas_common_render_brief_data( + result, &data->rom_data, data->sram_data, DS1992_SRAM_DATA_SIZE); +} + +void dallas_ds1992_render_error(FuriString* result, const iButtonProtocolData* protocol_data) { + const DS1992ProtocolData* data = protocol_data; + + if(!dallas_common_is_valid_crc(&data->rom_data)) { + dallas_common_render_crc_error(result, &data->rom_data); + } +} + +bool dallas_ds1992_is_data_valid(const iButtonProtocolData* protocol_data) { + const DS1992ProtocolData* data = protocol_data; + return dallas_common_is_valid_crc(&data->rom_data); +} + +void dallas_ds1992_get_editable_data( + iButtonEditableData* editable_data, + iButtonProtocolData* protocol_data) { + DS1992ProtocolData* data = protocol_data; + editable_data->ptr = data->rom_data.bytes; + editable_data->size = sizeof(DallasCommonRomData); +} + +void dallas_ds1992_apply_edits(iButtonProtocolData* protocol_data) { + DS1992ProtocolData* data = protocol_data; + dallas_common_apply_edits(&data->rom_data, DS1992_FAMILY_CODE); +} diff --git a/lib/one_wire/ibutton/protocols/dallas/protocol_ds1992.h b/lib/one_wire/ibutton/protocols/dallas/protocol_ds1992.h new file mode 100644 index 000000000..5a3c45f3b --- /dev/null +++ b/lib/one_wire/ibutton/protocols/dallas/protocol_ds1992.h @@ -0,0 +1,5 @@ +#pragma once + +#include "protocol_dallas_base.h" + +extern const iButtonProtocolDallasBase ibutton_protocol_ds1992; diff --git a/lib/one_wire/ibutton/protocols/dallas/protocol_ds1996.c b/lib/one_wire/ibutton/protocols/dallas/protocol_ds1996.c new file mode 100644 index 000000000..e69145c58 --- /dev/null +++ b/lib/one_wire/ibutton/protocols/dallas/protocol_ds1996.c @@ -0,0 +1,212 @@ +#include "protocol_ds1996.h" + +#include +#include + +#include "dallas_common.h" + +#define DS1996_FAMILY_CODE 0x0CU +#define DS1996_FAMILY_NAME "DS1996" + +#define DS1996_SRAM_DATA_SIZE 8192U +#define DS1996_SRAM_PAGE_SIZE 32U +#define DS1996_COPY_SCRATCH_TIMEOUT_US 100U + +#define DS1996_DATA_BYTE_COUNT 4U + +#define DS1996_SRAM_DATA_KEY "Sram Data" + +typedef struct { + OneWireSlave* bus; + DallasCommonCommandState command_state; +} DS1996ProtocolState; + +typedef struct { + DallasCommonRomData rom_data; + uint8_t sram_data[DS1996_SRAM_DATA_SIZE]; + DS1996ProtocolState state; +} DS1996ProtocolData; + +static bool dallas_ds1996_read(OneWireHost*, void*); +static bool dallas_ds1996_write_copy(OneWireHost*, iButtonProtocolData*); +static void dallas_ds1996_emulate(OneWireSlave*, iButtonProtocolData*); +static bool dallas_ds1996_load(FlipperFormat*, uint32_t, iButtonProtocolData*); +static bool dallas_ds1996_save(FlipperFormat*, const iButtonProtocolData*); +static void dallas_ds1996_render_data(FuriString*, const iButtonProtocolData*); +static void dallas_ds1996_render_brief_data(FuriString*, const iButtonProtocolData*); +static void dallas_ds1996_render_error(FuriString*, const iButtonProtocolData*); +static bool dallas_ds1996_is_data_valid(const iButtonProtocolData*); +static void dallas_ds1996_get_editable_data(iButtonEditableData*, iButtonProtocolData*); +static void dallas_ds1996_apply_edits(iButtonProtocolData*); + +const iButtonProtocolDallasBase ibutton_protocol_ds1996 = { + .family_code = DS1996_FAMILY_CODE, + .features = iButtonProtocolFeatureExtData | iButtonProtocolFeatureWriteCopy, + .data_size = sizeof(DS1996ProtocolData), + .manufacturer = DALLAS_COMMON_MANUFACTURER_NAME, + .name = DS1996_FAMILY_NAME, + + .read = dallas_ds1996_read, + .write_blank = NULL, /* Data too big for known blanks */ + .write_copy = dallas_ds1996_write_copy, + .emulate = dallas_ds1996_emulate, + .save = dallas_ds1996_save, + .load = dallas_ds1996_load, + .render_data = dallas_ds1996_render_data, + .render_brief_data = dallas_ds1996_render_brief_data, + .render_error = dallas_ds1996_render_error, + .is_valid = dallas_ds1996_is_data_valid, + .get_editable_data = dallas_ds1996_get_editable_data, + .apply_edits = dallas_ds1996_apply_edits, +}; + +bool dallas_ds1996_read(OneWireHost* host, iButtonProtocolData* protocol_data) { + DS1996ProtocolData* data = protocol_data; + return onewire_host_reset(host) && dallas_common_read_rom(host, &data->rom_data) && + dallas_common_read_mem(host, 0, data->sram_data, DS1996_SRAM_DATA_SIZE); +} + +bool dallas_ds1996_write_copy(OneWireHost* host, iButtonProtocolData* protocol_data) { + DS1996ProtocolData* data = protocol_data; + return dallas_common_write_mem( + host, + DS1996_COPY_SCRATCH_TIMEOUT_US, + DS1996_SRAM_PAGE_SIZE, + data->sram_data, + DS1996_SRAM_DATA_SIZE); +} + +static void dallas_ds1996_reset_callback(void* context) { + furi_assert(context); + DS1996ProtocolData* data = context; + data->state.command_state = DallasCommonCommandStateIdle; +} + +static bool dallas_ds1996_command_callback(uint8_t command, void* context) { + furi_assert(context); + DS1996ProtocolData* data = context; + OneWireSlave* bus = data->state.bus; + + switch(command) { + case DALLAS_COMMON_CMD_SEARCH_ROM: + if(data->state.command_state == DallasCommonCommandStateIdle) { + data->state.command_state = DallasCommonCommandStateRomCmd; + return dallas_common_emulate_search_rom(bus, &data->rom_data); + + } else if(data->state.command_state == DallasCommonCommandStateRomCmd) { + data->state.command_state = DallasCommonCommandStateMemCmd; + dallas_common_emulate_read_mem(bus, data->sram_data, DS1996_SRAM_DATA_SIZE); + return false; + + } else { + return false; + } + + case DALLAS_COMMON_CMD_READ_ROM: + if(data->state.command_state == DallasCommonCommandStateIdle) { + data->state.command_state = DallasCommonCommandStateRomCmd; + return dallas_common_emulate_read_rom(bus, &data->rom_data); + } else { + return false; + } + + case DALLAS_COMMON_CMD_SKIP_ROM: + if(data->state.command_state == DallasCommonCommandStateIdle) { + data->state.command_state = DallasCommonCommandStateRomCmd; + return true; + } else { + return false; + } + + case DALLAS_COMMON_CMD_OVERDRIVE_SKIP_ROM: + case DALLAS_COMMON_CMD_OVERDRIVE_MATCH_ROM: + /* TODO: Overdrive mode support */ + default: + return false; + } +} + +void dallas_ds1996_emulate(OneWireSlave* bus, iButtonProtocolData* protocol_data) { + DS1996ProtocolData* data = protocol_data; + data->state.bus = bus; + + onewire_slave_set_reset_callback(bus, dallas_ds1996_reset_callback, protocol_data); + onewire_slave_set_command_callback(bus, dallas_ds1996_command_callback, protocol_data); +} + +bool dallas_ds1996_load( + FlipperFormat* ff, + uint32_t format_version, + iButtonProtocolData* protocol_data) { + DS1996ProtocolData* data = protocol_data; + bool success = false; + + do { + if(format_version < 2) break; + if(!dallas_common_load_rom_data(ff, format_version, &data->rom_data)) break; + if(!flipper_format_read_hex( + ff, DS1996_SRAM_DATA_KEY, data->sram_data, DS1996_SRAM_DATA_SIZE)) + break; + success = true; + } while(false); + + return success; +} + +bool dallas_ds1996_save(FlipperFormat* ff, const iButtonProtocolData* protocol_data) { + const DS1996ProtocolData* data = protocol_data; + bool success = false; + + do { + if(!dallas_common_save_rom_data(ff, &data->rom_data)) break; + if(!flipper_format_write_hex( + ff, DS1996_SRAM_DATA_KEY, data->sram_data, DS1996_SRAM_DATA_SIZE)) + break; + success = true; + } while(false); + + return success; +} + +void dallas_ds1996_render_data(FuriString* result, const iButtonProtocolData* protocol_data) { + const DS1996ProtocolData* data = protocol_data; + + pretty_format_bytes_hex_canonical( + result, + DS1996_DATA_BYTE_COUNT, + PRETTY_FORMAT_FONT_MONOSPACE, + data->sram_data, + DS1996_SRAM_DATA_SIZE); +} + +void dallas_ds1996_render_brief_data(FuriString* result, const iButtonProtocolData* protocol_data) { + const DS1996ProtocolData* data = protocol_data; + dallas_common_render_brief_data( + result, &data->rom_data, data->sram_data, DS1996_SRAM_DATA_SIZE); +} + +void dallas_ds1996_render_error(FuriString* result, const iButtonProtocolData* protocol_data) { + const DS1996ProtocolData* data = protocol_data; + + if(!dallas_common_is_valid_crc(&data->rom_data)) { + dallas_common_render_crc_error(result, &data->rom_data); + } +} + +bool dallas_ds1996_is_data_valid(const iButtonProtocolData* protocol_data) { + const DS1996ProtocolData* data = protocol_data; + return dallas_common_is_valid_crc(&data->rom_data); +} + +void dallas_ds1996_get_editable_data( + iButtonEditableData* editable_data, + iButtonProtocolData* protocol_data) { + DS1996ProtocolData* data = protocol_data; + editable_data->ptr = data->rom_data.bytes; + editable_data->size = sizeof(DallasCommonRomData); +} + +void dallas_ds1996_apply_edits(iButtonProtocolData* protocol_data) { + DS1996ProtocolData* data = protocol_data; + dallas_common_apply_edits(&data->rom_data, DS1996_FAMILY_CODE); +} diff --git a/lib/one_wire/ibutton/protocols/dallas/protocol_ds1996.h b/lib/one_wire/ibutton/protocols/dallas/protocol_ds1996.h new file mode 100644 index 000000000..34546985b --- /dev/null +++ b/lib/one_wire/ibutton/protocols/dallas/protocol_ds1996.h @@ -0,0 +1,5 @@ +#pragma once + +#include "protocol_dallas_base.h" + +extern const iButtonProtocolDallasBase ibutton_protocol_ds1996; diff --git a/lib/one_wire/ibutton/protocols/dallas/protocol_ds_generic.c b/lib/one_wire/ibutton/protocols/dallas/protocol_ds_generic.c new file mode 100644 index 000000000..50fd04511 --- /dev/null +++ b/lib/one_wire/ibutton/protocols/dallas/protocol_ds_generic.c @@ -0,0 +1,133 @@ +#include "protocol_ds_generic.h" + +#include +#include + +#include "dallas_common.h" + +#include "../blanks/tm2004.h" + +#define DALLAS_GENERIC_FAMILY_CODE 0x00U +#define DALLAS_GENERIC_FAMILY_NAME "DSGeneric" + +typedef struct { + OneWireSlave* bus; +} DallasGenericProtocolState; + +typedef struct { + DallasCommonRomData rom_data; + DallasGenericProtocolState state; +} DallasGenericProtocolData; + +static bool ds_generic_read(OneWireHost*, iButtonProtocolData*); +static bool ds_generic_write_blank(OneWireHost*, iButtonProtocolData*); +static void ds_generic_emulate(OneWireSlave*, iButtonProtocolData*); +static bool ds_generic_load(FlipperFormat*, uint32_t, iButtonProtocolData*); +static bool ds_generic_save(FlipperFormat*, const iButtonProtocolData*); +static void ds_generic_render_brief_data(FuriString*, const iButtonProtocolData*); +static void ds_generic_render_error(FuriString*, const iButtonProtocolData*); +static bool ds_generic_is_data_valid(const iButtonProtocolData*); +static void ds_generic_get_editable_data(iButtonEditableData*, iButtonProtocolData*); +static void ds_generic_apply_edits(iButtonProtocolData*); + +const iButtonProtocolDallasBase ibutton_protocol_ds_generic = { + .family_code = DALLAS_GENERIC_FAMILY_CODE, + .features = iButtonProtocolFeatureWriteBlank, + .data_size = sizeof(DallasGenericProtocolData), + .manufacturer = DALLAS_COMMON_MANUFACTURER_NAME, + .name = DALLAS_GENERIC_FAMILY_NAME, + + .read = ds_generic_read, + .write_blank = ds_generic_write_blank, + .write_copy = NULL, /* No data to write a copy */ + .emulate = ds_generic_emulate, + .save = ds_generic_save, + .load = ds_generic_load, + .render_data = NULL, /* No data to render */ + .render_brief_data = ds_generic_render_brief_data, + .render_error = ds_generic_render_error, + .is_valid = ds_generic_is_data_valid, + .get_editable_data = ds_generic_get_editable_data, + .apply_edits = ds_generic_apply_edits, +}; + +bool ds_generic_read(OneWireHost* host, iButtonProtocolData* protocol_data) { + DallasGenericProtocolData* data = protocol_data; + return onewire_host_reset(host) && dallas_common_read_rom(host, &data->rom_data); +} + +bool ds_generic_write_blank(OneWireHost* host, iButtonProtocolData* protocol_data) { + DallasGenericProtocolData* data = protocol_data; + return tm2004_write(host, data->rom_data.bytes, sizeof(DallasCommonRomData)); +} + +static bool ds_generic_command_callback(uint8_t command, void* context) { + furi_assert(context); + DallasGenericProtocolData* data = context; + OneWireSlave* bus = data->state.bus; + + switch(command) { + case DALLAS_COMMON_CMD_SEARCH_ROM: + dallas_common_emulate_search_rom(bus, &data->rom_data); + break; + case DALLAS_COMMON_CMD_READ_ROM: + dallas_common_emulate_read_rom(bus, &data->rom_data); + break; + default: + break; + } + + // No support for multiple consecutive commands + return false; +} + +void ds_generic_emulate(OneWireSlave* bus, iButtonProtocolData* protocol_data) { + DallasGenericProtocolData* data = protocol_data; + data->state.bus = bus; + + onewire_slave_set_reset_callback(bus, NULL, NULL); + onewire_slave_set_command_callback(bus, ds_generic_command_callback, protocol_data); +} + +bool ds_generic_save(FlipperFormat* ff, const iButtonProtocolData* protocol_data) { + const DallasGenericProtocolData* data = protocol_data; + return dallas_common_save_rom_data(ff, &data->rom_data); +} + +bool ds_generic_load( + FlipperFormat* ff, + uint32_t format_version, + iButtonProtocolData* protocol_data) { + DallasGenericProtocolData* data = protocol_data; + return dallas_common_load_rom_data(ff, format_version, &data->rom_data); +} + +void ds_generic_render_brief_data(FuriString* result, const iButtonProtocolData* protocol_data) { + const DallasGenericProtocolData* data = protocol_data; + + for(size_t i = 0; i < sizeof(DallasCommonRomData); ++i) { + furi_string_cat_printf(result, "%02X ", data->rom_data.bytes[i]); + } +} + +void ds_generic_render_error(FuriString* result, const iButtonProtocolData* protocol_data) { + UNUSED(result); + UNUSED(protocol_data); +} + +bool ds_generic_is_data_valid(const iButtonProtocolData* protocol_data) { + UNUSED(protocol_data); + return true; +} + +void ds_generic_get_editable_data( + iButtonEditableData* editable_data, + iButtonProtocolData* protocol_data) { + DallasGenericProtocolData* data = protocol_data; + editable_data->ptr = data->rom_data.bytes; + editable_data->size = sizeof(DallasCommonRomData); +} + +void ds_generic_apply_edits(iButtonProtocolData* protocol_data) { + UNUSED(protocol_data); +} diff --git a/lib/one_wire/ibutton/protocols/dallas/protocol_ds_generic.h b/lib/one_wire/ibutton/protocols/dallas/protocol_ds_generic.h new file mode 100644 index 000000000..008f8a67b --- /dev/null +++ b/lib/one_wire/ibutton/protocols/dallas/protocol_ds_generic.h @@ -0,0 +1,5 @@ +#pragma once + +#include "protocol_dallas_base.h" + +extern const iButtonProtocolDallasBase ibutton_protocol_ds_generic; diff --git a/lib/one_wire/ibutton/protocols/dallas/protocol_group_dallas.c b/lib/one_wire/ibutton/protocols/dallas/protocol_group_dallas.c new file mode 100644 index 000000000..d0ffa511b --- /dev/null +++ b/lib/one_wire/ibutton/protocols/dallas/protocol_group_dallas.c @@ -0,0 +1,310 @@ +#include "protocol_group_dallas.h" + +#include + +#include "protocol_group_dallas_defs.h" + +#define IBUTTON_ONEWIRE_ROM_SIZE 8U + +typedef struct { + OneWireHost* host; + OneWireSlave* bus; +} iButtonProtocolGroupDallas; + +static iButtonProtocolGroupDallas* ibutton_protocol_group_dallas_alloc() { + iButtonProtocolGroupDallas* group = malloc(sizeof(iButtonProtocolGroupDallas)); + + group->host = onewire_host_alloc(&ibutton_gpio); + group->bus = onewire_slave_alloc(&ibutton_gpio); + + return group; +} + +static void ibutton_protocol_group_dallas_free(iButtonProtocolGroupDallas* group) { + onewire_slave_free(group->bus); + onewire_host_free(group->host); + free(group); +} + +static size_t ibutton_protocol_group_dallas_get_max_data_size(iButtonProtocolGroupDallas* group) { + UNUSED(group); + size_t max_data_size = 0; + + for(iButtonProtocolLocalId i = 0; i < iButtonProtocolDSMax; ++i) { + const size_t current_rom_size = ibutton_protocols_dallas[i]->data_size; + if(current_rom_size > max_data_size) { + max_data_size = current_rom_size; + } + } + + return max_data_size; +} + +static bool ibutton_protocol_group_dallas_get_id_by_name( + iButtonProtocolGroupDallas* group, + iButtonProtocolLocalId* id, + const char* name) { + UNUSED(group); + // Handle older key files which refer to DS1990 as just "Dallas" + if(strcmp(name, "Dallas") == 0) { + *id = iButtonProtocolDS1990; + return true; + } + + for(iButtonProtocolLocalId i = 0; i < iButtonProtocolDSMax; ++i) { + if(strcmp(ibutton_protocols_dallas[i]->name, name) == 0) { + *id = i; + return true; + } + } + return false; +} + +static uint32_t ibutton_protocol_group_dallas_get_features( + iButtonProtocolGroupDallas* group, + iButtonProtocolLocalId id) { + UNUSED(group); + furi_assert(id < iButtonProtocolDSMax); + return ibutton_protocols_dallas[id]->features; +} + +static const char* ibutton_protocol_group_dallas_get_manufacturer( + iButtonProtocolGroupDallas* group, + iButtonProtocolLocalId id) { + UNUSED(group); + furi_assert(id < iButtonProtocolDSMax); + return ibutton_protocols_dallas[id]->manufacturer; +} + +static const char* ibutton_protocol_group_dallas_get_name( + iButtonProtocolGroupDallas* group, + iButtonProtocolLocalId id) { + UNUSED(group); + furi_assert(id < iButtonProtocolDSMax); + return ibutton_protocols_dallas[id]->name; +} + +static iButtonProtocolLocalId + ibutton_protocol_group_dallas_get_id_by_family_code(uint8_t family_code) { + iButtonProtocolLocalId id; + + for(id = 0; id < iButtonProtocolDSGeneric; ++id) { + if(ibutton_protocols_dallas[id]->family_code == family_code) break; + } + + return id; +} + +static bool ibutton_protocol_group_dallas_read( + iButtonProtocolGroupDallas* group, + iButtonProtocolData* data, + iButtonProtocolLocalId* id) { + bool success = false; + uint8_t rom_data[IBUTTON_ONEWIRE_ROM_SIZE]; + OneWireHost* host = group->host; + + onewire_host_start(host); + furi_delay_ms(100); + + FURI_CRITICAL_ENTER(); + + if(onewire_host_search(host, rom_data, OneWireHostSearchModeNormal)) { + /* Considering any found 1-Wire device a success. + * It can be checked later with ibutton_key_is_valid(). */ + success = true; + + /* If a 1-Wire device was found, id is guaranteed to be + * one of the known keys or DSGeneric. */ + *id = ibutton_protocol_group_dallas_get_id_by_family_code(rom_data[0]); + ibutton_protocols_dallas[*id]->read(host, data); + } + + onewire_host_reset_search(host); + onewire_host_stop(host); + + FURI_CRITICAL_EXIT(); + + return success; +} + +static bool ibutton_protocol_group_dallas_write_blank( + iButtonProtocolGroupDallas* group, + iButtonProtocolData* data, + iButtonProtocolLocalId id) { + furi_assert(id < iButtonProtocolDSMax); + const iButtonProtocolDallasBase* protocol = ibutton_protocols_dallas[id]; + furi_assert(protocol->features & iButtonProtocolFeatureWriteBlank); + + OneWireHost* host = group->host; + + onewire_host_start(host); + furi_delay_ms(100); + + FURI_CRITICAL_ENTER(); + + const bool success = protocol->write_blank(host, data); + onewire_host_stop(host); + + FURI_CRITICAL_EXIT(); + return success; +} + +static bool ibutton_protocol_group_dallas_write_copy( + iButtonProtocolGroupDallas* group, + iButtonProtocolData* data, + iButtonProtocolLocalId id) { + furi_assert(id < iButtonProtocolDSMax); + + const iButtonProtocolDallasBase* protocol = ibutton_protocols_dallas[id]; + furi_assert(protocol->features & iButtonProtocolFeatureWriteCopy); + + OneWireHost* host = group->host; + + onewire_host_start(host); + furi_delay_ms(100); + + FURI_CRITICAL_ENTER(); + + const bool success = protocol->write_copy(host, data); + onewire_host_stop(host); + + FURI_CRITICAL_EXIT(); + return success; +} + +static void ibutton_protocol_group_dallas_emulate_start( + iButtonProtocolGroupDallas* group, + iButtonProtocolData* data, + iButtonProtocolLocalId id) { + furi_assert(id < iButtonProtocolDSMax); + OneWireSlave* bus = group->bus; + ibutton_protocols_dallas[id]->emulate(bus, data); + onewire_slave_start(bus); +} + +static void ibutton_protocol_group_dallas_emulate_stop( + iButtonProtocolGroupDallas* group, + iButtonProtocolData* data, + iButtonProtocolLocalId id) { + furi_assert(id < iButtonProtocolDSMax); + UNUSED(data); + onewire_slave_stop(group->bus); +} + +static bool ibutton_protocol_group_dallas_save( + iButtonProtocolGroupDallas* group, + const iButtonProtocolData* data, + iButtonProtocolLocalId id, + FlipperFormat* ff) { + UNUSED(group); + furi_assert(id < iButtonProtocolDSMax); + return ibutton_protocols_dallas[id]->save(ff, data); +} + +static bool ibutton_protocol_group_dallas_load( + iButtonProtocolGroupDallas* group, + iButtonProtocolData* data, + iButtonProtocolLocalId id, + uint32_t version, + FlipperFormat* ff) { + UNUSED(group); + furi_assert(id < iButtonProtocolDSMax); + return ibutton_protocols_dallas[id]->load(ff, version, data); +} + +static void ibutton_protocol_group_dallas_render_data( + iButtonProtocolGroupDallas* group, + const iButtonProtocolData* data, + iButtonProtocolLocalId id, + FuriString* result) { + UNUSED(group); + furi_assert(id < iButtonProtocolDSMax); + const iButtonProtocolDallasBase* protocol = ibutton_protocols_dallas[id]; + furi_assert(protocol->render_data); + protocol->render_data(result, data); +} + +static void ibutton_protocol_group_dallas_render_brief_data( + iButtonProtocolGroupDallas* group, + const iButtonProtocolData* data, + iButtonProtocolLocalId id, + FuriString* result) { + UNUSED(group); + furi_assert(id < iButtonProtocolDSMax); + ibutton_protocols_dallas[id]->render_brief_data(result, data); +} + +static void ibutton_protocol_group_dallas_render_error( + iButtonProtocolGroupDallas* group, + const iButtonProtocolData* data, + iButtonProtocolLocalId id, + FuriString* result) { + UNUSED(group); + furi_assert(id < iButtonProtocolDSMax); + ibutton_protocols_dallas[id]->render_error(result, data); +} + +static bool ibutton_protocol_group_dallas_is_valid( + iButtonProtocolGroupDallas* group, + const iButtonProtocolData* data, + iButtonProtocolLocalId id) { + UNUSED(group); + furi_assert(id < iButtonProtocolDSMax); + return ibutton_protocols_dallas[id]->is_valid(data); +} + +static void ibutton_protocol_group_dallas_get_editable_data( + iButtonProtocolGroupDallas* group, + iButtonProtocolData* data, + iButtonProtocolLocalId id, + iButtonEditableData* editable) { + UNUSED(group); + furi_assert(id < iButtonProtocolDSMax); + ibutton_protocols_dallas[id]->get_editable_data(editable, data); +} + +static void ibutton_protocol_group_dallas_apply_edits( + iButtonProtocolGroupDallas* group, + iButtonProtocolData* data, + iButtonProtocolLocalId id) { + UNUSED(group); + furi_assert(id < iButtonProtocolDSMax); + ibutton_protocols_dallas[id]->apply_edits(data); +} + +const iButtonProtocolGroupBase ibutton_protocol_group_dallas = { + .protocol_count = iButtonProtocolDSMax, + + .alloc = (iButtonProtocolGroupAllocFunc)ibutton_protocol_group_dallas_alloc, + .free = (iButtonProtocolGroupFreeFunc)ibutton_protocol_group_dallas_free, + + .get_max_data_size = + (iButtonProtocolGropuGetSizeFunc)ibutton_protocol_group_dallas_get_max_data_size, + .get_id_by_name = (iButtonProtocolGroupGetIdFunc)ibutton_protocol_group_dallas_get_id_by_name, + .get_features = + (iButtonProtocolGroupGetFeaturesFunc)ibutton_protocol_group_dallas_get_features, + + .get_manufacturer = + (iButtonProtocolGroupGetStringFunc)ibutton_protocol_group_dallas_get_manufacturer, + .get_name = (iButtonProtocolGroupGetStringFunc)ibutton_protocol_group_dallas_get_name, + + .read = (iButtonProtocolGroupReadFunc)ibutton_protocol_group_dallas_read, + .write_blank = (iButtonProtocolGroupWriteFunc)ibutton_protocol_group_dallas_write_blank, + .write_copy = (iButtonProtocolGroupWriteFunc)ibutton_protocol_group_dallas_write_copy, + + .emulate_start = (iButtonProtocolGroupApplyFunc)ibutton_protocol_group_dallas_emulate_start, + .emulate_stop = (iButtonProtocolGroupApplyFunc)ibutton_protocol_group_dallas_emulate_stop, + + .save = (iButtonProtocolGroupSaveFunc)ibutton_protocol_group_dallas_save, + .load = (iButtonProtocolGroupLoadFunc)ibutton_protocol_group_dallas_load, + + .render_data = (iButtonProtocolGroupRenderFunc)ibutton_protocol_group_dallas_render_data, + .render_brief_data = + (iButtonProtocolGroupRenderFunc)ibutton_protocol_group_dallas_render_brief_data, + .render_error = (iButtonProtocolGroupRenderFunc)ibutton_protocol_group_dallas_render_error, + + .is_valid = (iButtonProtocolGroupIsValidFunc)ibutton_protocol_group_dallas_is_valid, + .get_editable_data = + (iButtonProtocolGroupGetDataFunc)ibutton_protocol_group_dallas_get_editable_data, + .apply_edits = (iButtonProtocolGroupApplyFunc)ibutton_protocol_group_dallas_apply_edits, +}; diff --git a/lib/one_wire/ibutton/protocols/dallas/protocol_group_dallas.h b/lib/one_wire/ibutton/protocols/dallas/protocol_group_dallas.h new file mode 100644 index 000000000..d1293c71b --- /dev/null +++ b/lib/one_wire/ibutton/protocols/dallas/protocol_group_dallas.h @@ -0,0 +1,5 @@ +#pragma once + +#include "../protocol_group_base.h" + +extern const iButtonProtocolGroupBase ibutton_protocol_group_dallas; diff --git a/lib/one_wire/ibutton/protocols/dallas/protocol_group_dallas_defs.c b/lib/one_wire/ibutton/protocols/dallas/protocol_group_dallas_defs.c new file mode 100644 index 000000000..e54c3125d --- /dev/null +++ b/lib/one_wire/ibutton/protocols/dallas/protocol_group_dallas_defs.c @@ -0,0 +1,16 @@ +#include "protocol_group_dallas_defs.h" + +#include "protocol_ds1990.h" +#include "protocol_ds1992.h" +#include "protocol_ds1996.h" +#include "protocol_ds_generic.h" + +const iButtonProtocolDallasBase* ibutton_protocols_dallas[] = { + [iButtonProtocolDS1990] = &ibutton_protocol_ds1990, + [iButtonProtocolDS1992] = &ibutton_protocol_ds1992, + [iButtonProtocolDS1996] = &ibutton_protocol_ds1996, + /* Add new 1-Wire protocols here */ + + /* Default catch-all 1-Wire protocol */ + [iButtonProtocolDSGeneric] = &ibutton_protocol_ds_generic, +}; diff --git a/lib/one_wire/ibutton/protocols/dallas/protocol_group_dallas_defs.h b/lib/one_wire/ibutton/protocols/dallas/protocol_group_dallas_defs.h new file mode 100644 index 000000000..ba74c0c23 --- /dev/null +++ b/lib/one_wire/ibutton/protocols/dallas/protocol_group_dallas_defs.h @@ -0,0 +1,16 @@ +#pragma once + +#include "protocol_dallas_base.h" + +typedef enum { + iButtonProtocolDS1990, + iButtonProtocolDS1992, + iButtonProtocolDS1996, + /* Add new 1-Wire protocols here */ + + /* Default catch-all 1-Wire protocol */ + iButtonProtocolDSGeneric, + iButtonProtocolDSMax, +} iButtonProtocolDallas; + +extern const iButtonProtocolDallasBase* ibutton_protocols_dallas[]; diff --git a/lib/one_wire/ibutton/protocols/ibutton_protocols.c b/lib/one_wire/ibutton/protocols/ibutton_protocols.c deleted file mode 100644 index b07d68b33..000000000 --- a/lib/one_wire/ibutton/protocols/ibutton_protocols.c +++ /dev/null @@ -1,8 +0,0 @@ -#include "ibutton_protocols.h" -#include "protocol_cyfral.h" -#include "protocol_metakom.h" - -const ProtocolBase* ibutton_protocols[] = { - [iButtonProtocolCyfral] = &protocol_cyfral, - [iButtonProtocolMetakom] = &protocol_metakom, -}; \ No newline at end of file diff --git a/lib/one_wire/ibutton/protocols/ibutton_protocols.h b/lib/one_wire/ibutton/protocols/ibutton_protocols.h deleted file mode 100644 index cdaa3ab6f..000000000 --- a/lib/one_wire/ibutton/protocols/ibutton_protocols.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once -#include "toolbox/protocols/protocol.h" - -typedef enum { - iButtonProtocolCyfral, - iButtonProtocolMetakom, - - iButtonProtocolMax, -} iButtonProtocol; - -extern const ProtocolBase* ibutton_protocols[]; \ No newline at end of file diff --git a/lib/one_wire/ibutton/protocols/protocol_cyfral.c b/lib/one_wire/ibutton/protocols/misc/protocol_cyfral.c similarity index 96% rename from lib/one_wire/ibutton/protocols/protocol_cyfral.c rename to lib/one_wire/ibutton/protocols/misc/protocol_cyfral.c index a5f459bc0..0cdb8766b 100644 --- a/lib/one_wire/ibutton/protocols/protocol_cyfral.c +++ b/lib/one_wire/ibutton/protocols/misc/protocol_cyfral.c @@ -1,5 +1,6 @@ #include #include + #include "protocol_cyfral.h" #define CYFRAL_DATA_SIZE sizeof(uint16_t) @@ -324,7 +325,13 @@ static LevelDuration protocol_cyfral_encoder_yield(ProtocolCyfral* proto) { return result; } -const ProtocolBase protocol_cyfral = { +static void protocol_cyfral_render_brief_data(ProtocolCyfral* proto, FuriString* result) { + for(size_t i = 0; i < CYFRAL_DATA_SIZE; ++i) { + furi_string_cat_printf(result, "%02X ", ((uint8_t*)&proto->data)[i]); + } +} + +const ProtocolBase ibutton_protocol_misc_cyfral = { .name = "Cyfral", .manufacturer = "Cyfral", .data_size = CYFRAL_DATA_SIZE, @@ -341,4 +348,5 @@ const ProtocolBase protocol_cyfral = { .start = (ProtocolEncoderStart)protocol_cyfral_encoder_start, .yield = (ProtocolEncoderYield)protocol_cyfral_encoder_yield, }, + .render_brief_data = (ProtocolRenderData)protocol_cyfral_render_brief_data, }; diff --git a/lib/one_wire/ibutton/protocols/misc/protocol_cyfral.h b/lib/one_wire/ibutton/protocols/misc/protocol_cyfral.h new file mode 100644 index 000000000..08b8eef81 --- /dev/null +++ b/lib/one_wire/ibutton/protocols/misc/protocol_cyfral.h @@ -0,0 +1,4 @@ +#pragma once +#include "toolbox/protocols/protocol.h" + +extern const ProtocolBase ibutton_protocol_misc_cyfral; diff --git a/lib/one_wire/ibutton/protocols/misc/protocol_group_misc.c b/lib/one_wire/ibutton/protocols/misc/protocol_group_misc.c new file mode 100644 index 000000000..dad1ef3cf --- /dev/null +++ b/lib/one_wire/ibutton/protocols/misc/protocol_group_misc.c @@ -0,0 +1,295 @@ +#include "protocol_group_misc.h" + +#include +#include + +#include + +#include "protocol_group_misc_defs.h" + +#define IBUTTON_MISC_READ_TIMEOUT 100 + +#define IBUTTON_MISC_DATA_KEY_KEY_COMMON "Data" + +typedef struct { + ProtocolDict* dict; + ProtocolId emulate_id; +} iButtonProtocolGroupMisc; + +static iButtonProtocolGroupMisc* ibutton_protocol_group_misc_alloc() { + iButtonProtocolGroupMisc* group = malloc(sizeof(iButtonProtocolGroupMisc)); + + group->dict = protocol_dict_alloc(ibutton_protocols_misc, iButtonProtocolMiscMax); + group->emulate_id = PROTOCOL_NO; + + return group; +} + +static void ibutton_protocol_group_misc_free(iButtonProtocolGroupMisc* group) { + protocol_dict_free(group->dict); + free(group); +} + +static size_t ibutton_protocol_group_misc_get_max_data_size(iButtonProtocolGroupMisc* group) { + return protocol_dict_get_max_data_size(group->dict); +} + +static bool ibutton_protocol_group_misc_get_id_by_name( + iButtonProtocolGroupMisc* group, + iButtonProtocolLocalId* id, + const char* name) { + const ProtocolId found_id = protocol_dict_get_protocol_by_name(group->dict, name); + + if(found_id != PROTOCOL_NO) { + *id = found_id; + return true; + } + return false; +} + +static uint32_t ibutton_protocol_group_misc_get_features( + iButtonProtocolGroupMisc* group, + iButtonProtocolLocalId id) { + UNUSED(group); + UNUSED(id); + return 0; +} + +static const char* ibutton_protocol_group_misc_get_manufacturer( + iButtonProtocolGroupMisc* group, + iButtonProtocolLocalId id) { + return protocol_dict_get_manufacturer(group->dict, id); +} + +static const char* ibutton_protocol_group_misc_get_name( + iButtonProtocolGroupMisc* group, + iButtonProtocolLocalId id) { + return protocol_dict_get_name(group->dict, id); +} + +typedef struct { + uint32_t last_dwt_value; + FuriStreamBuffer* stream; +} iButtonReadContext; + +static void ibutton_protocols_comparator_callback(bool level, void* context) { + iButtonReadContext* read_context = context; + + uint32_t current_dwt_value = DWT->CYCCNT; + + LevelDuration data = + level_duration_make(level, current_dwt_value - read_context->last_dwt_value); + furi_stream_buffer_send(read_context->stream, &data, sizeof(LevelDuration), 0); + + read_context->last_dwt_value = current_dwt_value; +} + +static bool ibutton_protocol_group_misc_read( + iButtonProtocolGroupMisc* group, + iButtonProtocolData* data, + iButtonProtocolLocalId* id) { + bool result = false; + + protocol_dict_decoders_start(group->dict); + + furi_hal_rfid_pins_reset(); + // pulldown pull pin, we sense the signal through the analog part of the RFID schematic + furi_hal_rfid_pin_pull_pulldown(); + + iButtonReadContext read_context = { + .last_dwt_value = DWT->CYCCNT, + .stream = furi_stream_buffer_alloc(sizeof(LevelDuration) * 512, 1), + }; + + furi_hal_rfid_comp_set_callback(ibutton_protocols_comparator_callback, &read_context); + furi_hal_rfid_comp_start(); + + const uint32_t tick_start = furi_get_tick(); + + for(;;) { + LevelDuration level; + size_t ret = furi_stream_buffer_receive( + read_context.stream, &level, sizeof(LevelDuration), IBUTTON_MISC_READ_TIMEOUT); + + if((furi_get_tick() - tick_start) > IBUTTON_MISC_READ_TIMEOUT) { + break; + } + + if(ret > 0) { + ProtocolId decoded_index = protocol_dict_decoders_feed( + group->dict, level_duration_get_level(level), level_duration_get_duration(level)); + + if(decoded_index == PROTOCOL_NO) continue; + + *id = decoded_index; + + protocol_dict_get_data( + group->dict, + decoded_index, + data, + protocol_dict_get_data_size(group->dict, decoded_index)); + + result = true; + } + } + + furi_hal_rfid_comp_stop(); + furi_hal_rfid_comp_set_callback(NULL, NULL); + furi_hal_rfid_pins_reset(); + + furi_stream_buffer_free(read_context.stream); + + return result; +} + +static void ibutton_protocol_group_misc_emulate_callback(void* context) { + iButtonProtocolGroupMisc* group = context; + + const LevelDuration level_duration = + protocol_dict_encoder_yield(group->dict, group->emulate_id); + + furi_hal_ibutton_emulate_set_next(level_duration_get_duration(level_duration)); + furi_hal_ibutton_pin_write(level_duration_get_level(level_duration)); +} + +static void ibutton_protocol_group_misc_emulate_start( + iButtonProtocolGroupMisc* group, + iButtonProtocolData* data, + iButtonProtocolLocalId id) { + group->emulate_id = id; + protocol_dict_set_data(group->dict, id, data, protocol_dict_get_data_size(group->dict, id)); + protocol_dict_encoder_start(group->dict, group->emulate_id); + + furi_hal_ibutton_pin_configure(); + furi_hal_ibutton_emulate_start(0, ibutton_protocol_group_misc_emulate_callback, group); +} + +static void ibutton_protocol_group_misc_emulate_stop( + iButtonProtocolGroupMisc* group, + iButtonProtocolData* data, + iButtonProtocolLocalId id) { + UNUSED(group); + UNUSED(data); + UNUSED(id); + furi_hal_ibutton_emulate_stop(); + furi_hal_ibutton_pin_reset(); +} + +static bool ibutton_protocol_group_misc_save( + iButtonProtocolGroupMisc* group, + const iButtonProtocolData* data, + iButtonProtocolLocalId id, + FlipperFormat* ff) { + const size_t data_size = protocol_dict_get_data_size(group->dict, id); + return flipper_format_write_hex(ff, IBUTTON_MISC_DATA_KEY_KEY_COMMON, data, data_size); +} + +static bool ibutton_protocol_group_misc_load( + iButtonProtocolGroupMisc* group, + iButtonProtocolData* data, + iButtonProtocolLocalId id, + uint32_t version, + FlipperFormat* ff) { + const size_t data_size = protocol_dict_get_data_size(group->dict, id); + switch(version) { + case 1: + case 2: + return flipper_format_read_hex(ff, IBUTTON_MISC_DATA_KEY_KEY_COMMON, data, data_size); + default: + return false; + } +} + +static void ibutton_protocol_group_misc_render_data( + iButtonProtocolGroupMisc* group, + const iButtonProtocolData* data, + iButtonProtocolLocalId id, + FuriString* result) { + const size_t data_size = protocol_dict_get_data_size(group->dict, id); + protocol_dict_set_data(group->dict, id, data, data_size); + protocol_dict_render_data(group->dict, result, id); +} + +static void ibutton_protocol_group_misc_render_brief_data( + iButtonProtocolGroupMisc* group, + const iButtonProtocolData* data, + iButtonProtocolLocalId id, + FuriString* result) { + const size_t data_size = protocol_dict_get_data_size(group->dict, id); + protocol_dict_set_data(group->dict, id, data, data_size); + protocol_dict_render_brief_data(group->dict, result, id); +} + +static void ibutton_protocol_group_misc_render_error( + iButtonProtocolGroupMisc* group, + const iButtonProtocolData* data, + iButtonProtocolLocalId id, + FuriString* result) { + UNUSED(group); + UNUSED(data); + UNUSED(id); + UNUSED(result); +} + +static bool ibutton_protocol_group_misc_is_valid( + iButtonProtocolGroupMisc* group, + const iButtonProtocolData* data, + iButtonProtocolLocalId id) { + UNUSED(group); + UNUSED(data); + UNUSED(id); + return true; +} + +static void ibutton_protocol_group_misc_get_editable_data( + iButtonProtocolGroupMisc* group, + iButtonProtocolData* data, + iButtonProtocolLocalId id, + iButtonEditableData* editable) { + editable->ptr = data; + editable->size = protocol_dict_get_data_size(group->dict, id); +} + +static void ibutton_protocol_group_misc_apply_edits( + iButtonProtocolGroupMisc* group, + iButtonProtocolData* data, + iButtonProtocolLocalId id) { + const size_t data_size = protocol_dict_get_data_size(group->dict, id); + protocol_dict_set_data(group->dict, id, data, data_size); +} + +const iButtonProtocolGroupBase ibutton_protocol_group_misc = { + .protocol_count = iButtonProtocolMiscMax, + + .alloc = (iButtonProtocolGroupAllocFunc)ibutton_protocol_group_misc_alloc, + .free = (iButtonProtocolGroupFreeFunc)ibutton_protocol_group_misc_free, + + .get_max_data_size = + (iButtonProtocolGropuGetSizeFunc)ibutton_protocol_group_misc_get_max_data_size, + .get_id_by_name = (iButtonProtocolGroupGetIdFunc)ibutton_protocol_group_misc_get_id_by_name, + .get_features = (iButtonProtocolGroupGetFeaturesFunc)ibutton_protocol_group_misc_get_features, + + .get_manufacturer = + (iButtonProtocolGroupGetStringFunc)ibutton_protocol_group_misc_get_manufacturer, + .get_name = (iButtonProtocolGroupGetStringFunc)ibutton_protocol_group_misc_get_name, + + .read = (iButtonProtocolGroupReadFunc)ibutton_protocol_group_misc_read, + .write_blank = NULL, + .write_copy = NULL, + + .emulate_start = (iButtonProtocolGroupApplyFunc)ibutton_protocol_group_misc_emulate_start, + .emulate_stop = (iButtonProtocolGroupApplyFunc)ibutton_protocol_group_misc_emulate_stop, + + .save = (iButtonProtocolGroupSaveFunc)ibutton_protocol_group_misc_save, + .load = (iButtonProtocolGroupLoadFunc)ibutton_protocol_group_misc_load, + + .render_data = (iButtonProtocolGroupRenderFunc)ibutton_protocol_group_misc_render_data, + .render_brief_data = + (iButtonProtocolGroupRenderFunc)ibutton_protocol_group_misc_render_brief_data, + .render_error = (iButtonProtocolGroupRenderFunc)ibutton_protocol_group_misc_render_error, + + .is_valid = (iButtonProtocolGroupIsValidFunc)ibutton_protocol_group_misc_is_valid, + .get_editable_data = + (iButtonProtocolGroupGetDataFunc)ibutton_protocol_group_misc_get_editable_data, + .apply_edits = (iButtonProtocolGroupApplyFunc)ibutton_protocol_group_misc_apply_edits, +}; diff --git a/lib/one_wire/ibutton/protocols/misc/protocol_group_misc.h b/lib/one_wire/ibutton/protocols/misc/protocol_group_misc.h new file mode 100644 index 000000000..7fde6e737 --- /dev/null +++ b/lib/one_wire/ibutton/protocols/misc/protocol_group_misc.h @@ -0,0 +1,5 @@ +#pragma once + +#include "../protocol_group_base.h" + +extern const iButtonProtocolGroupBase ibutton_protocol_group_misc; diff --git a/lib/one_wire/ibutton/protocols/misc/protocol_group_misc_defs.c b/lib/one_wire/ibutton/protocols/misc/protocol_group_misc_defs.c new file mode 100644 index 000000000..09ae0bdc7 --- /dev/null +++ b/lib/one_wire/ibutton/protocols/misc/protocol_group_misc_defs.c @@ -0,0 +1,10 @@ +#include "protocol_group_misc_defs.h" + +#include "protocol_cyfral.h" +#include "protocol_metakom.h" + +const ProtocolBase* ibutton_protocols_misc[] = { + [iButtonProtocolMiscCyfral] = &ibutton_protocol_misc_cyfral, + [iButtonProtocolMiscMetakom] = &ibutton_protocol_misc_metakom, + /* Add new misc protocols here */ +}; diff --git a/lib/one_wire/ibutton/protocols/misc/protocol_group_misc_defs.h b/lib/one_wire/ibutton/protocols/misc/protocol_group_misc_defs.h new file mode 100644 index 000000000..0a7f92847 --- /dev/null +++ b/lib/one_wire/ibutton/protocols/misc/protocol_group_misc_defs.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +typedef enum { + iButtonProtocolMiscCyfral, + iButtonProtocolMiscMetakom, + iButtonProtocolMiscMax, +} iButtonProtocolMisc; + +extern const ProtocolBase* ibutton_protocols_misc[]; diff --git a/lib/one_wire/ibutton/protocols/protocol_metakom.c b/lib/one_wire/ibutton/protocols/misc/protocol_metakom.c similarity index 96% rename from lib/one_wire/ibutton/protocols/protocol_metakom.c rename to lib/one_wire/ibutton/protocols/misc/protocol_metakom.c index ff65c6678..a2bd2cf7c 100644 --- a/lib/one_wire/ibutton/protocols/protocol_metakom.c +++ b/lib/one_wire/ibutton/protocols/misc/protocol_metakom.c @@ -1,5 +1,6 @@ #include #include + #include "protocol_metakom.h" #define METAKOM_DATA_SIZE sizeof(uint32_t) @@ -300,7 +301,13 @@ static LevelDuration protocol_metakom_encoder_yield(ProtocolMetakom* proto) { return result; } -const ProtocolBase protocol_metakom = { +static void protocol_metakom_render_brief_data(ProtocolMetakom* proto, FuriString* result) { + for(size_t i = 0; i < METAKOM_DATA_SIZE; ++i) { + furi_string_cat_printf(result, "%02X ", ((uint8_t*)&proto->data)[i]); + } +} + +const ProtocolBase ibutton_protocol_misc_metakom = { .name = "Metakom", .manufacturer = "Metakom", .data_size = METAKOM_DATA_SIZE, @@ -317,4 +324,5 @@ const ProtocolBase protocol_metakom = { .start = (ProtocolEncoderStart)protocol_metakom_encoder_start, .yield = (ProtocolEncoderYield)protocol_metakom_encoder_yield, }, + .render_brief_data = (ProtocolRenderData)protocol_metakom_render_brief_data, }; diff --git a/lib/one_wire/ibutton/protocols/misc/protocol_metakom.h b/lib/one_wire/ibutton/protocols/misc/protocol_metakom.h new file mode 100644 index 000000000..317619e3f --- /dev/null +++ b/lib/one_wire/ibutton/protocols/misc/protocol_metakom.h @@ -0,0 +1,4 @@ +#pragma once +#include "toolbox/protocols/protocol.h" + +extern const ProtocolBase ibutton_protocol_misc_metakom; diff --git a/lib/one_wire/ibutton/protocols/protocol_common.h b/lib/one_wire/ibutton/protocols/protocol_common.h new file mode 100644 index 000000000..5383158e4 --- /dev/null +++ b/lib/one_wire/ibutton/protocols/protocol_common.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include + +typedef int32_t iButtonProtocolId; + +enum { + iButtonProtocolIdInvalid = -1, +}; + +typedef enum { + iButtonProtocolFeatureExtData = (1U << 0), + iButtonProtocolFeatureWriteBlank = (1U << 1), + iButtonProtocolFeatureWriteCopy = (1U << 2), +} iButtonProtocolFeature; + +typedef struct { + uint8_t* ptr; + size_t size; +} iButtonEditableData; diff --git a/lib/one_wire/ibutton/protocols/protocol_common_i.h b/lib/one_wire/ibutton/protocols/protocol_common_i.h new file mode 100644 index 000000000..b80c92887 --- /dev/null +++ b/lib/one_wire/ibutton/protocols/protocol_common_i.h @@ -0,0 +1,6 @@ +#pragma once + +#include "protocol_common.h" + +typedef void iButtonProtocolData; +typedef int32_t iButtonProtocolLocalId; diff --git a/lib/one_wire/ibutton/protocols/protocol_cyfral.h b/lib/one_wire/ibutton/protocols/protocol_cyfral.h deleted file mode 100644 index 97a98e485..000000000 --- a/lib/one_wire/ibutton/protocols/protocol_cyfral.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -#include "toolbox/protocols/protocol.h" - -extern const ProtocolBase protocol_cyfral; \ No newline at end of file diff --git a/lib/one_wire/ibutton/protocols/protocol_group_base.h b/lib/one_wire/ibutton/protocols/protocol_group_base.h new file mode 100644 index 000000000..c8fec70fe --- /dev/null +++ b/lib/one_wire/ibutton/protocols/protocol_group_base.h @@ -0,0 +1,104 @@ +#pragma once + +#include +#include + +#include "protocol_common_i.h" + +typedef void iButtonProtocolGroupData; +typedef int32_t iButtonProtocolGroupId; + +typedef iButtonProtocolGroupData* (*iButtonProtocolGroupAllocFunc)(void); + +typedef void (*iButtonProtocolGroupFreeFunc)(iButtonProtocolGroupData*); + +typedef void (*iButtonProtocolGroupRenderFunc)( + iButtonProtocolGroupData*, + const iButtonProtocolData*, + iButtonProtocolLocalId, + FuriString*); + +typedef bool (*iButtonProtocolGroupIsValidFunc)( + iButtonProtocolGroupData*, + const iButtonProtocolData*, + iButtonProtocolLocalId); + +typedef void (*iButtonProtocolGroupGetDataFunc)( + iButtonProtocolGroupData*, + iButtonProtocolData*, + iButtonProtocolLocalId, + iButtonEditableData*); + +typedef void (*iButtonProtocolGroupApplyFunc)( + iButtonProtocolGroupData*, + iButtonProtocolData*, + iButtonProtocolLocalId); + +typedef size_t (*iButtonProtocolGropuGetSizeFunc)(iButtonProtocolGroupData*); + +typedef uint32_t ( + *iButtonProtocolGroupGetFeaturesFunc)(iButtonProtocolGroupData*, iButtonProtocolLocalId); + +typedef const char* ( + *iButtonProtocolGroupGetStringFunc)(iButtonProtocolGroupData*, iButtonProtocolLocalId); + +typedef bool (*iButtonProtocolGroupGetIdFunc)( + iButtonProtocolGroupData*, + iButtonProtocolLocalId*, + const char*); + +typedef bool (*iButtonProtocolGroupReadFunc)( + iButtonProtocolGroupData*, + iButtonProtocolData*, + iButtonProtocolLocalId*); + +typedef bool (*iButtonProtocolGroupWriteFunc)( + iButtonProtocolGroupData*, + iButtonProtocolData*, + iButtonProtocolLocalId); + +typedef bool (*iButtonProtocolGroupSaveFunc)( + iButtonProtocolGroupData*, + const iButtonProtocolData*, + iButtonProtocolLocalId, + FlipperFormat*); + +typedef bool (*iButtonProtocolGroupLoadFunc)( + iButtonProtocolGroupData*, + iButtonProtocolData*, + iButtonProtocolLocalId, + uint32_t, + FlipperFormat*); + +typedef struct { + const uint32_t protocol_count; + + iButtonProtocolGroupAllocFunc alloc; + iButtonProtocolGroupFreeFunc free; + + iButtonProtocolGropuGetSizeFunc get_max_data_size; + iButtonProtocolGroupGetIdFunc get_id_by_name; + iButtonProtocolGroupGetFeaturesFunc get_features; + + iButtonProtocolGroupGetStringFunc get_manufacturer; + iButtonProtocolGroupGetStringFunc get_name; + + iButtonProtocolGroupReadFunc read; + iButtonProtocolGroupWriteFunc write_blank; + iButtonProtocolGroupWriteFunc write_copy; + + iButtonProtocolGroupApplyFunc emulate_start; + iButtonProtocolGroupApplyFunc emulate_stop; + + iButtonProtocolGroupSaveFunc save; + iButtonProtocolGroupLoadFunc load; + + iButtonProtocolGroupRenderFunc render_data; + iButtonProtocolGroupRenderFunc render_brief_data; + iButtonProtocolGroupRenderFunc render_error; + + iButtonProtocolGroupIsValidFunc is_valid; + iButtonProtocolGroupGetDataFunc get_editable_data; + + iButtonProtocolGroupApplyFunc apply_edits; +} iButtonProtocolGroupBase; diff --git a/lib/one_wire/ibutton/protocols/protocol_group_defs.c b/lib/one_wire/ibutton/protocols/protocol_group_defs.c new file mode 100644 index 000000000..40a360f0e --- /dev/null +++ b/lib/one_wire/ibutton/protocols/protocol_group_defs.c @@ -0,0 +1,9 @@ +#include "protocol_group_defs.h" + +#include "dallas/protocol_group_dallas.h" +#include "misc/protocol_group_misc.h" + +const iButtonProtocolGroupBase* ibutton_protocol_groups[] = { + [iButtonProtocolGroupDallas] = &ibutton_protocol_group_dallas, + [iButtonProtocolGroupMisc] = &ibutton_protocol_group_misc, +}; diff --git a/lib/one_wire/ibutton/protocols/protocol_group_defs.h b/lib/one_wire/ibutton/protocols/protocol_group_defs.h new file mode 100644 index 000000000..2d41e3cb8 --- /dev/null +++ b/lib/one_wire/ibutton/protocols/protocol_group_defs.h @@ -0,0 +1,11 @@ +#pragma once + +#include "protocol_group_base.h" + +typedef enum { + iButtonProtocolGroupDallas, + iButtonProtocolGroupMisc, + iButtonProtocolGroupMax +} iButtonProtocolGroup; + +extern const iButtonProtocolGroupBase* ibutton_protocol_groups[]; diff --git a/lib/one_wire/ibutton/protocols/protocol_metakom.h b/lib/one_wire/ibutton/protocols/protocol_metakom.h deleted file mode 100644 index 5e44a2a8c..000000000 --- a/lib/one_wire/ibutton/protocols/protocol_metakom.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -#include "toolbox/protocols/protocol.h" - -extern const ProtocolBase protocol_metakom; \ No newline at end of file diff --git a/lib/one_wire/one_wire_device.c b/lib/one_wire/one_wire_device.c deleted file mode 100644 index d9b4955db..000000000 --- a/lib/one_wire/one_wire_device.c +++ /dev/null @@ -1,59 +0,0 @@ -#include -#include "maxim_crc.h" -#include "one_wire_device.h" -#include "one_wire_slave.h" -#include "one_wire_slave_i.h" - -struct OneWireDevice { - uint8_t id_storage[8]; - OneWireSlave* bus; -}; - -OneWireDevice* onewire_device_alloc( - uint8_t id_1, - uint8_t id_2, - uint8_t id_3, - uint8_t id_4, - uint8_t id_5, - uint8_t id_6, - uint8_t id_7, - uint8_t id_8) { - OneWireDevice* device = malloc(sizeof(OneWireDevice)); - device->id_storage[0] = id_1; - device->id_storage[1] = id_2; - device->id_storage[2] = id_3; - device->id_storage[3] = id_4; - device->id_storage[4] = id_5; - device->id_storage[5] = id_6; - device->id_storage[6] = id_7; - device->id_storage[7] = id_8; - device->bus = NULL; - - return device; -} - -void onewire_device_free(OneWireDevice* device) { - if(device->bus != NULL) { - onewire_slave_detach(device->bus); - } - - free(device); -} - -void onewire_device_send_id(OneWireDevice* device) { - if(device->bus != NULL) { - onewire_slave_send(device->bus, device->id_storage, 8); - } -} - -void onewire_device_attach(OneWireDevice* device, OneWireSlave* bus) { - device->bus = bus; -} - -void onewire_device_detach(OneWireDevice* device) { - device->bus = NULL; -} - -uint8_t* onewire_device_get_id_p(OneWireDevice* device) { - return device->id_storage; -} diff --git a/lib/one_wire/one_wire_device.h b/lib/one_wire/one_wire_device.h deleted file mode 100644 index fc687c7ba..000000000 --- a/lib/one_wire/one_wire_device.h +++ /dev/null @@ -1,74 +0,0 @@ -/** - * @file one_wire_device.h - * - * 1-Wire slave library, device interface. Currently it can only emulate ID. - */ - -#pragma once -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct OneWireSlave OneWireSlave; -typedef struct OneWireDevice OneWireDevice; - -/** - * Allocate onewire device with ID - * @param id_1 - * @param id_2 - * @param id_3 - * @param id_4 - * @param id_5 - * @param id_6 - * @param id_7 - * @param id_8 - * @return OneWireDevice* - */ -OneWireDevice* onewire_device_alloc( - uint8_t id_1, - uint8_t id_2, - uint8_t id_3, - uint8_t id_4, - uint8_t id_5, - uint8_t id_6, - uint8_t id_7, - uint8_t id_8); - -/** - * Deallocate onewire device - * @param device - */ -void onewire_device_free(OneWireDevice* device); - -/** - * Send ID report, called from onewire slave - * @param device - */ -void onewire_device_send_id(OneWireDevice* device); - -/** - * Attach device to onewire slave bus - * @param device - * @param bus - */ -void onewire_device_attach(OneWireDevice* device, OneWireSlave* bus); - -/** - * Attach device from onewire slave bus - * @param device - */ -void onewire_device_detach(OneWireDevice* device); - -/** - * Get pointer to device id array - * @param device - * @return uint8_t* - */ -uint8_t* onewire_device_get_id_p(OneWireDevice* device); - -#ifdef __cplusplus -} -#endif diff --git a/lib/one_wire/one_wire_host.c b/lib/one_wire/one_wire_host.c index 654b9e45d..0a4a79f5c 100644 --- a/lib/one_wire/one_wire_host.c +++ b/lib/one_wire/one_wire_host.c @@ -1,18 +1,19 @@ #include -#include + #include "one_wire_host.h" #include "one_wire_host_timing.h" struct OneWireHost { - // global search state - unsigned char saved_rom[8]; + const GpioPin* gpio_pin; + unsigned char saved_rom[8]; /** < global search state */ uint8_t last_discrepancy; uint8_t last_family_discrepancy; bool last_device_flag; }; -OneWireHost* onewire_host_alloc() { +OneWireHost* onewire_host_alloc(const GpioPin* gpio_pin) { OneWireHost* host = malloc(sizeof(OneWireHost)); + host->gpio_pin = gpio_pin; onewire_host_reset_search(host); return host; } @@ -23,49 +24,47 @@ void onewire_host_free(OneWireHost* host) { } bool onewire_host_reset(OneWireHost* host) { - UNUSED(host); uint8_t r; uint8_t retries = 125; // wait until the gpio is high - furi_hal_ibutton_pin_high(); + furi_hal_gpio_write(host->gpio_pin, true); do { if(--retries == 0) return 0; furi_delay_us(2); - } while(!furi_hal_ibutton_pin_get_level()); + } while(!furi_hal_gpio_read(host->gpio_pin)); // pre delay furi_delay_us(OWH_RESET_DELAY_PRE); // drive low - furi_hal_ibutton_pin_low(); + furi_hal_gpio_write(host->gpio_pin, false); furi_delay_us(OWH_RESET_DRIVE); // release - furi_hal_ibutton_pin_high(); + furi_hal_gpio_write(host->gpio_pin, true); furi_delay_us(OWH_RESET_RELEASE); // read and post delay - r = !furi_hal_ibutton_pin_get_level(); + r = !furi_hal_gpio_read(host->gpio_pin); furi_delay_us(OWH_RESET_DELAY_POST); return r; } bool onewire_host_read_bit(OneWireHost* host) { - UNUSED(host); bool result; // drive low - furi_hal_ibutton_pin_low(); + furi_hal_gpio_write(host->gpio_pin, false); furi_delay_us(OWH_READ_DRIVE); // release - furi_hal_ibutton_pin_high(); + furi_hal_gpio_write(host->gpio_pin, true); furi_delay_us(OWH_READ_RELEASE); // read and post delay - result = furi_hal_ibutton_pin_get_level(); + result = furi_hal_gpio_read(host->gpio_pin); furi_delay_us(OWH_READ_DELAY_POST); return result; @@ -90,22 +89,21 @@ void onewire_host_read_bytes(OneWireHost* host, uint8_t* buffer, uint16_t count) } void onewire_host_write_bit(OneWireHost* host, bool value) { - UNUSED(host); if(value) { // drive low - furi_hal_ibutton_pin_low(); + furi_hal_gpio_write(host->gpio_pin, false); furi_delay_us(OWH_WRITE_1_DRIVE); // release - furi_hal_ibutton_pin_high(); + furi_hal_gpio_write(host->gpio_pin, true); furi_delay_us(OWH_WRITE_1_RELEASE); } else { // drive low - furi_hal_ibutton_pin_low(); + furi_hal_gpio_write(host->gpio_pin, false); furi_delay_us(OWH_WRITE_0_DRIVE); // release - furi_hal_ibutton_pin_high(); + furi_hal_gpio_write(host->gpio_pin, true); furi_delay_us(OWH_WRITE_0_RELEASE); } } @@ -118,18 +116,24 @@ void onewire_host_write(OneWireHost* host, uint8_t value) { } } +void onewire_host_write_bytes(OneWireHost* host, const uint8_t* buffer, uint16_t count) { + for(uint16_t i = 0; i < count; ++i) { + onewire_host_write(host, buffer[i]); + } +} + void onewire_host_skip(OneWireHost* host) { onewire_host_write(host, 0xCC); } void onewire_host_start(OneWireHost* host) { - UNUSED(host); - furi_hal_ibutton_start_drive(); + furi_hal_gpio_write(host->gpio_pin, true); + furi_hal_gpio_init(host->gpio_pin, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow); } void onewire_host_stop(OneWireHost* host) { - UNUSED(host); - furi_hal_ibutton_stop(); + furi_hal_gpio_write(host->gpio_pin, true); + furi_hal_gpio_init(host->gpio_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); } void onewire_host_reset_search(OneWireHost* host) { @@ -150,7 +154,7 @@ void onewire_host_target_search(OneWireHost* host, uint8_t family_code) { host->last_device_flag = false; } -uint8_t onewire_host_search(OneWireHost* host, uint8_t* newAddr, OneWireHostSearchMode mode) { +uint8_t onewire_host_search(OneWireHost* host, uint8_t* new_addr, OneWireHostSearchMode mode) { uint8_t id_bit_number; uint8_t last_zero, rom_byte_number, search_result; uint8_t id_bit, cmp_id_bit; @@ -177,10 +181,10 @@ uint8_t onewire_host_search(OneWireHost* host, uint8_t* newAddr, OneWireHostSear // issue the search command switch(mode) { - case CONDITIONAL_SEARCH: + case OneWireHostSearchModeConditional: onewire_host_write(host, 0xEC); break; - case NORMAL_SEARCH: + case OneWireHostSearchModeNormal: onewire_host_write(host, 0xF0); break; } @@ -259,7 +263,7 @@ uint8_t onewire_host_search(OneWireHost* host, uint8_t* newAddr, OneWireHostSear host->last_family_discrepancy = 0; search_result = false; } else { - for(int i = 0; i < 8; i++) newAddr[i] = host->saved_rom[i]; + for(int i = 0; i < 8; i++) new_addr[i] = host->saved_rom[i]; } return search_result; diff --git a/lib/one_wire/one_wire_host.h b/lib/one_wire/one_wire_host.h index 5b55ee8dd..dc469904d 100644 --- a/lib/one_wire/one_wire_host.h +++ b/lib/one_wire/one_wire_host.h @@ -14,18 +14,18 @@ extern "C" { #endif typedef enum { - CONDITIONAL_SEARCH = 0, /**< Search for alarmed device */ - NORMAL_SEARCH = 1, /**< Search all devices */ + OneWireHostSearchModeConditional = 0, /**< Search for alarmed device */ + OneWireHostSearchModeNormal = 1, /**< Search all devices */ } OneWireHostSearchMode; typedef struct OneWireHost OneWireHost; /** * Allocate onewire host bus - * @param gpio + * @param pin * @return OneWireHost* */ -OneWireHost* onewire_host_alloc(); +OneWireHost* onewire_host_alloc(const GpioPin* gpio_pin); /** * Deallocate onewire host bus @@ -76,6 +76,14 @@ void onewire_host_write_bit(OneWireHost* host, bool value); */ void onewire_host_write(OneWireHost* host, uint8_t value); +/** + * Write many bytes + * @param host + * @param buffer + * @param count + */ +void onewire_host_write_bytes(OneWireHost* host, const uint8_t* buffer, uint16_t count); + /** * Skip ROM command * @param host @@ -114,7 +122,7 @@ void onewire_host_target_search(OneWireHost* host, uint8_t family_code); * @param mode * @return uint8_t */ -uint8_t onewire_host_search(OneWireHost* host, uint8_t* newAddr, OneWireHostSearchMode mode); +uint8_t onewire_host_search(OneWireHost* host, uint8_t* new_addr, OneWireHostSearchMode mode); #ifdef __cplusplus } diff --git a/lib/one_wire/one_wire_slave.c b/lib/one_wire/one_wire_slave.c index ad9c34b19..d1676cf3b 100644 --- a/lib/one_wire/one_wire_slave.c +++ b/lib/one_wire/one_wire_slave.c @@ -1,49 +1,55 @@ #include "one_wire_slave.h" -#include "one_wire_slave_i.h" -#include "one_wire_device.h" + #include #include -#define OWS_RESET_MIN 270 -#define OWS_RESET_MAX 960 -#define OWS_PRESENCE_TIMEOUT 20 -#define OWS_PRESENCE_MIN 100 -#define OWS_PRESENCE_MAX 480 -#define OWS_MSG_HIGH_TIMEOUT 15000 -#define OWS_SLOT_MAX 135 -#define OWS_READ_MIN 20 -#define OWS_READ_MAX 60 -#define OWS_WRITE_ZERO 30 +#define ONEWIRE_TRSTL_MIN 270 /* Minimum Reset Low time */ +#define ONEWIRE_TRSTL_MAX 1200 /* Maximum Reset Low time */ + +#define ONEWIRE_TPDH_TYP 20 /* Typical Presence Detect High time */ +#define ONEWIRE_TPDL_MIN 100 /* Minimum Presence Detect Low time */ +#define ONEWIRE_TPDL_MAX 480 /* Maximum Presence Detect Low time */ + +#define ONEWIRE_TSLOT_MIN 60 /* Minimum Read/Write Slot time */ +#define ONEWIRE_TSLOT_MAX 135 /* Maximum Read/Write Slot time */ + +#define ONEWIRE_TW1L_MAX 20 /* Maximum Master Write 1 time */ +#define ONEWIRE_TRL_TMSR_MAX 30 /* Maximum Master Read Low + Read Sample time */ + +#define ONEWIRE_TH_TIMEOUT 15000 /* Maximum time before general timeout */ typedef enum { - NO_ERROR = 0, - VERY_LONG_RESET, - VERY_SHORT_RESET, - PRESENCE_LOW_ON_LINE, - AWAIT_TIMESLOT_TIMEOUT_HIGH, - INCORRECT_ONEWIRE_CMD, - FIRST_BIT_OF_BYTE_TIMEOUT, - RESET_IN_PROGRESS + OneWireSlaveErrorNone = 0, + OneWireSlaveErrorResetInProgress, + OneWireSlaveErrorPresenceConflict, + OneWireSlaveErrorInvalidCommand, + OneWireSlaveErrorTimeout, } OneWireSlaveError; struct OneWireSlave { + const GpioPin* gpio_pin; OneWireSlaveError error; - OneWireDevice* device; - OneWireSlaveResultCallback result_cb; - void* result_cb_ctx; + + OneWireSlaveResetCallback reset_callback; + OneWireSlaveCommandCallback command_callback; + OneWireSlaveResultCallback result_callback; + + void* reset_callback_context; + void* result_callback_context; + void* command_callback_context; }; /*********************** PRIVATE ***********************/ -uint32_t onewire_slave_wait_while_gpio_is(OneWireSlave* bus, uint32_t time, const bool pin_value) { - UNUSED(bus); +static uint32_t + onewire_slave_wait_while_gpio_is(OneWireSlave* bus, uint32_t time, const bool pin_value) { uint32_t start = DWT->CYCCNT; uint32_t time_ticks = time * furi_hal_cortex_instructions_per_microsecond(); uint32_t time_captured; do { //-V1044 time_captured = DWT->CYCCNT; - if(furi_hal_ibutton_pin_get_level() != pin_value) { + if(furi_hal_gpio_read(bus->gpio_pin) != pin_value) { uint32_t remaining_time = time_ticks - (time_captured - start); remaining_time /= furi_hal_cortex_instructions_per_microsecond(); return remaining_time; @@ -53,193 +59,107 @@ uint32_t onewire_slave_wait_while_gpio_is(OneWireSlave* bus, uint32_t time, cons return 0; } -bool onewire_slave_show_presence(OneWireSlave* bus) { +static bool onewire_slave_show_presence(OneWireSlave* bus) { + // wait until the bus is high (might return immediately) + onewire_slave_wait_while_gpio_is(bus, ONEWIRE_TRSTL_MAX, false); // wait while master delay presence check - onewire_slave_wait_while_gpio_is(bus, OWS_PRESENCE_TIMEOUT, true); + furi_delay_us(ONEWIRE_TPDH_TYP); // show presence - furi_hal_ibutton_pin_low(); - furi_delay_us(OWS_PRESENCE_MIN); - furi_hal_ibutton_pin_high(); + furi_hal_gpio_write(bus->gpio_pin, false); + furi_delay_us(ONEWIRE_TPDL_MIN); + furi_hal_gpio_write(bus->gpio_pin, true); // somebody also can show presence - const uint32_t wait_low_time = OWS_PRESENCE_MAX - OWS_PRESENCE_MIN; + const uint32_t wait_low_time = ONEWIRE_TPDL_MAX - ONEWIRE_TPDL_MIN; // so we will wait if(onewire_slave_wait_while_gpio_is(bus, wait_low_time, false) == 0) { - bus->error = PRESENCE_LOW_ON_LINE; + bus->error = OneWireSlaveErrorPresenceConflict; return false; } return true; } -bool onewire_slave_receive_bit(OneWireSlave* bus) { - // wait while bus is low - uint32_t time = OWS_SLOT_MAX; - time = onewire_slave_wait_while_gpio_is(bus, time, false); - if(time == 0) { - bus->error = RESET_IN_PROGRESS; - return false; - } - - // wait while bus is high - time = OWS_MSG_HIGH_TIMEOUT; - time = onewire_slave_wait_while_gpio_is(bus, time, true); - if(time == 0) { - bus->error = AWAIT_TIMESLOT_TIMEOUT_HIGH; - return false; - } - - // wait a time of zero - time = OWS_READ_MIN; - time = onewire_slave_wait_while_gpio_is(bus, time, false); - - return (time > 0); -} - -bool onewire_slave_send_bit(OneWireSlave* bus, bool value) { - const bool write_zero = !value; - - // wait while bus is low - uint32_t time = OWS_SLOT_MAX; - time = onewire_slave_wait_while_gpio_is(bus, time, false); - if(time == 0) { - bus->error = RESET_IN_PROGRESS; - return false; - } - - // wait while bus is high - time = OWS_MSG_HIGH_TIMEOUT; - time = onewire_slave_wait_while_gpio_is(bus, time, true); - if(time == 0) { - bus->error = AWAIT_TIMESLOT_TIMEOUT_HIGH; - return false; - } - - // choose write time - if(write_zero) { - furi_hal_ibutton_pin_low(); - time = OWS_WRITE_ZERO; - } else { - time = OWS_READ_MAX; - } - - // hold line for ZERO or ONE time - furi_delay_us(time); - furi_hal_ibutton_pin_high(); - - return true; -} - -void onewire_slave_cmd_search_rom(OneWireSlave* bus) { - const uint8_t key_bytes = 8; - uint8_t* key = onewire_device_get_id_p(bus->device); - - for(uint8_t i = 0; i < key_bytes; i++) { - uint8_t key_byte = key[i]; - - for(uint8_t j = 0; j < 8; j++) { - bool bit = (key_byte >> j) & 0x01; - - if(!onewire_slave_send_bit(bus, bit)) return; - if(!onewire_slave_send_bit(bus, !bit)) return; - - onewire_slave_receive_bit(bus); - if(bus->error != NO_ERROR) return; - } - } -} - -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; - else if(bus->error != NO_ERROR) - return false; - - switch(cmd) { - case 0xF0: - // SEARCH ROM - onewire_slave_cmd_search_rom(bus); - return true; - - case 0x0F: - case 0x33: - // READ ROM - onewire_device_send_id(bus->device); - return true; - - default: // Unknown command - bus->error = INCORRECT_ONEWIRE_CMD; - return false; - } -} - -bool onewire_slave_bus_start(OneWireSlave* bus) { - bool result = true; - - if(bus->device == NULL) { - result = false; - } else { - FURI_CRITICAL_ENTER(); - furi_hal_ibutton_start_drive_in_isr(); - bus->error = NO_ERROR; - +static inline bool onewire_slave_receive_and_process_command(OneWireSlave* bus) { + /* Reset condition detected, send a presence pulse and reset protocol state */ + if(bus->error == OneWireSlaveErrorResetInProgress) { if(onewire_slave_show_presence(bus)) { - // TODO think about multiple command cycles - onewire_slave_receive_and_process_cmd(bus); - result = (bus->error == NO_ERROR || bus->error == INCORRECT_ONEWIRE_CMD); + bus->error = OneWireSlaveErrorNone; - } else { - result = false; + if(bus->reset_callback != NULL) { + bus->reset_callback(bus->reset_callback_context); + } + + return true; } - furi_hal_ibutton_start_interrupt_in_isr(); - FURI_CRITICAL_EXIT(); + } else if(bus->error == OneWireSlaveErrorNone) { + uint8_t command; + if(!onewire_slave_receive(bus, &command, 1)) { + /* Upon failure, request an additional iteration to + choose the appropriate action by checking bus->error */ + return true; + } else if(bus->command_callback) { + return bus->command_callback(command, bus->command_callback_context); + } else { + bus->error = OneWireSlaveErrorInvalidCommand; + } } + return false; +} + +static inline bool onewire_slave_bus_start(OneWireSlave* bus) { + FURI_CRITICAL_ENTER(); + furi_hal_gpio_init(bus->gpio_pin, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow); + + /* Start in Reset state in order to send a presence pulse immediately */ + bus->error = OneWireSlaveErrorResetInProgress; + + while(onewire_slave_receive_and_process_command(bus)) + ; + + const bool result = (bus->error == OneWireSlaveErrorNone); + + furi_hal_gpio_init(bus->gpio_pin, GpioModeInterruptRiseFall, GpioPullNo, GpioSpeedLow); + FURI_CRITICAL_EXIT(); + return result; } -static void exti_cb(void* context) { +static void onewire_slave_exti_callback(void* context) { OneWireSlave* bus = context; - volatile bool input_state = furi_hal_ibutton_pin_get_level(); + const volatile bool input_state = furi_hal_gpio_read(bus->gpio_pin); static uint32_t pulse_start = 0; if(input_state) { - uint32_t pulse_length = + const uint32_t pulse_length = (DWT->CYCCNT - pulse_start) / furi_hal_cortex_instructions_per_microsecond(); - if(pulse_length >= OWS_RESET_MIN) { - if(pulse_length <= OWS_RESET_MAX) { - // reset cycle ok - bool result = onewire_slave_bus_start(bus); - if(result && bus->result_cb != NULL) { - bus->result_cb(bus->result_cb_ctx); - } - } else { - bus->error = VERY_LONG_RESET; + + if((pulse_length >= ONEWIRE_TRSTL_MIN) && pulse_length <= (ONEWIRE_TRSTL_MAX)) { + const bool result = onewire_slave_bus_start(bus); + + if(result && bus->result_callback != NULL) { + bus->result_callback(bus->result_callback_context); } - } else { - bus->error = VERY_SHORT_RESET; } + } else { - //FALL event pulse_start = DWT->CYCCNT; } }; /*********************** PUBLIC ***********************/ -OneWireSlave* onewire_slave_alloc() { +OneWireSlave* onewire_slave_alloc(const GpioPin* gpio_pin) { OneWireSlave* bus = malloc(sizeof(OneWireSlave)); - bus->error = NO_ERROR; - bus->device = NULL; - bus->result_cb = NULL; - bus->result_cb_ctx = NULL; + + bus->gpio_pin = gpio_pin; + bus->error = OneWireSlaveErrorNone; + return bus; } @@ -249,51 +169,109 @@ void onewire_slave_free(OneWireSlave* bus) { } void onewire_slave_start(OneWireSlave* bus) { - furi_hal_ibutton_add_interrupt(exti_cb, bus); - furi_hal_ibutton_start_interrupt(); + furi_hal_gpio_add_int_callback(bus->gpio_pin, onewire_slave_exti_callback, bus); + furi_hal_gpio_write(bus->gpio_pin, true); + furi_hal_gpio_init(bus->gpio_pin, GpioModeInterruptRiseFall, GpioPullNo, GpioSpeedLow); } void onewire_slave_stop(OneWireSlave* bus) { - UNUSED(bus); - furi_hal_ibutton_stop(); - furi_hal_ibutton_remove_interrupt(); + furi_hal_gpio_write(bus->gpio_pin, true); + furi_hal_gpio_init(bus->gpio_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_remove_int_callback(bus->gpio_pin); } -void onewire_slave_attach(OneWireSlave* bus, OneWireDevice* device) { - bus->device = device; - onewire_device_attach(device, bus); +void onewire_slave_set_reset_callback( + OneWireSlave* bus, + OneWireSlaveResetCallback callback, + void* context) { + bus->reset_callback = callback; + bus->reset_callback_context = context; } -void onewire_slave_detach(OneWireSlave* bus) { - if(bus->device != NULL) { - onewire_device_detach(bus->device); - } - bus->device = NULL; +void onewire_slave_set_command_callback( + OneWireSlave* bus, + OneWireSlaveCommandCallback callback, + void* context) { + bus->command_callback = callback; + bus->command_callback_context = context; } void onewire_slave_set_result_callback( OneWireSlave* bus, OneWireSlaveResultCallback result_cb, void* context) { - bus->result_cb = result_cb; - bus->result_cb_ctx = context; + bus->result_callback = result_cb; + bus->result_callback_context = context; } -bool onewire_slave_send(OneWireSlave* bus, const uint8_t* address, const uint8_t data_length) { - uint8_t bytes_sent = 0; +bool onewire_slave_receive_bit(OneWireSlave* bus) { + // wait while bus is low + uint32_t time = ONEWIRE_TSLOT_MAX; + time = onewire_slave_wait_while_gpio_is(bus, time, false); + if(time == 0) { + bus->error = OneWireSlaveErrorResetInProgress; + return false; + } - furi_hal_ibutton_pin_high(); + // wait while bus is high + time = ONEWIRE_TH_TIMEOUT; + time = onewire_slave_wait_while_gpio_is(bus, time, true); + if(time == 0) { + bus->error = OneWireSlaveErrorTimeout; + return false; + } + + // wait a time of zero + time = ONEWIRE_TW1L_MAX; + time = onewire_slave_wait_while_gpio_is(bus, time, false); + + return (time > 0); +} + +bool onewire_slave_send_bit(OneWireSlave* bus, bool value) { + // wait while bus is low + uint32_t time = ONEWIRE_TSLOT_MAX; + time = onewire_slave_wait_while_gpio_is(bus, time, false); + if(time == 0) { + bus->error = OneWireSlaveErrorResetInProgress; + return false; + } + + // wait while bus is high + time = ONEWIRE_TH_TIMEOUT; + time = onewire_slave_wait_while_gpio_is(bus, time, true); + if(time == 0) { + bus->error = OneWireSlaveErrorTimeout; + return false; + } + + // choose write time + if(!value) { + furi_hal_gpio_write(bus->gpio_pin, false); + time = ONEWIRE_TRL_TMSR_MAX; + } else { + time = ONEWIRE_TSLOT_MIN; + } + + // hold line for ZERO or ONE time + furi_delay_us(time); + furi_hal_gpio_write(bus->gpio_pin, true); + + return true; +} + +bool onewire_slave_send(OneWireSlave* bus, const uint8_t* data, size_t data_size) { + furi_hal_gpio_write(bus->gpio_pin, true); + + size_t bytes_sent = 0; // bytes loop - for(; bytes_sent < data_length; ++bytes_sent) { - const uint8_t data_byte = address[bytes_sent]; + for(; bytes_sent < data_size; ++bytes_sent) { + const uint8_t data_byte = data[bytes_sent]; // bit loop for(uint8_t bit_mask = 0x01; bit_mask != 0; bit_mask <<= 1) { if(!onewire_slave_send_bit(bus, bit_mask & data_byte)) { - // if we cannot send first bit - if((bit_mask == 0x01) && (bus->error == AWAIT_TIMESLOT_TIMEOUT_HIGH)) - bus->error = FIRST_BIT_OF_BYTE_TIMEOUT; return false; } } @@ -301,19 +279,25 @@ bool onewire_slave_send(OneWireSlave* bus, const uint8_t* address, const uint8_t return true; } -bool onewire_slave_receive(OneWireSlave* bus, uint8_t* data, const uint8_t data_length) { - uint8_t bytes_received = 0; +bool onewire_slave_receive(OneWireSlave* bus, uint8_t* data, size_t data_size) { + furi_hal_gpio_write(bus->gpio_pin, true); - furi_hal_ibutton_pin_high(); + size_t bytes_received = 0; - for(; bytes_received < data_length; ++bytes_received) { + for(; bytes_received < data_size; ++bytes_received) { uint8_t value = 0; for(uint8_t bit_mask = 0x01; bit_mask != 0; bit_mask <<= 1) { - if(onewire_slave_receive_bit(bus)) value |= bit_mask; + if(onewire_slave_receive_bit(bus)) { + value |= bit_mask; + } + + if(bus->error != OneWireSlaveErrorNone) { + return false; + } } data[bytes_received] = value; } - return (bytes_received != data_length); + return true; } diff --git a/lib/one_wire/one_wire_slave.h b/lib/one_wire/one_wire_slave.h index 82e9f5523..914cd9335 100644 --- a/lib/one_wire/one_wire_slave.h +++ b/lib/one_wire/one_wire_slave.h @@ -1,12 +1,14 @@ /** * @file one_wire_slave.h * - * 1-Wire slave library. Currently it can only emulate ID. + * 1-Wire slave library. */ #pragma once +#include #include #include + #include #ifdef __cplusplus @@ -15,14 +17,17 @@ extern "C" { typedef struct OneWireDevice OneWireDevice; typedef struct OneWireSlave OneWireSlave; + +typedef void (*OneWireSlaveResetCallback)(void* context); typedef void (*OneWireSlaveResultCallback)(void* context); +typedef bool (*OneWireSlaveCommandCallback)(uint8_t command, void* context); /** * Allocate onewire slave - * @param pin + * @param gpio_pin * @return OneWireSlave* */ -OneWireSlave* onewire_slave_alloc(); +OneWireSlave* onewire_slave_alloc(const GpioPin* gpio_pin); /** * Free onewire slave @@ -43,17 +48,54 @@ void onewire_slave_start(OneWireSlave* bus); void onewire_slave_stop(OneWireSlave* bus); /** - * Attach device for emulation - * @param bus - * @param device + * TODO: description comment */ -void onewire_slave_attach(OneWireSlave* bus, OneWireDevice* device); +bool onewire_slave_receive_bit(OneWireSlave* bus); /** - * Detach device from bus - * @param bus + * TODO: description comment */ -void onewire_slave_detach(OneWireSlave* bus); +bool onewire_slave_send_bit(OneWireSlave* bus, bool value); + +/** + * Send data + * @param bus + * @param data + * @param data_size + * @return bool + */ +bool onewire_slave_send(OneWireSlave* bus, const uint8_t* data, size_t data_size); + +/** + * Receive data + * @param bus + * @param data + * @param data_size + * @return bool + */ +bool onewire_slave_receive(OneWireSlave* bus, uint8_t* data, size_t data_size); + +/** + * Set a callback to be called on each reset + * @param bus + * @param callback + * @param context + */ +void onewire_slave_set_reset_callback( + OneWireSlave* bus, + OneWireSlaveResetCallback callback, + void* context); + +/** + * Set a callback to be called on each command + * @param bus + * @param callback + * @param context + */ +void onewire_slave_set_command_callback( + OneWireSlave* bus, + OneWireSlaveCommandCallback callback, + void* context); /** * Set a callback to report emulation success diff --git a/lib/one_wire/one_wire_slave_i.h b/lib/one_wire/one_wire_slave_i.h deleted file mode 100644 index 55e0762e4..000000000 --- a/lib/one_wire/one_wire_slave_i.h +++ /dev/null @@ -1,38 +0,0 @@ -/** - * @file one_wire_slave_i.h - * - * 1-Wire slave library, internal functions - */ - -#pragma once -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct OneWireDevice OneWireDevice; -typedef struct OneWireSlave OneWireSlave; - -/** - * Send data, called from emulated device - * @param bus - * @param address - * @param data_length - * @return bool - */ -bool onewire_slave_send(OneWireSlave* bus, const uint8_t* address, const uint8_t data_length); - -/** - * Receive data, called from emulated device - * @param bus - * @param data - * @param data_length - * @return bool - */ -bool onewire_slave_receive(OneWireSlave* bus, uint8_t* data, const uint8_t data_length); - -#ifdef __cplusplus -} -#endif diff --git a/lib/subghz/blocks/generic.c b/lib/subghz/blocks/generic.c index 3d59adc82..331606fe5 100644 --- a/lib/subghz/blocks/generic.c +++ b/lib/subghz/blocks/generic.c @@ -20,12 +20,12 @@ void subghz_block_generic_get_preset_name(const char* preset_name, FuriString* p furi_string_set(preset_str, preset_name_temp); } -bool subghz_block_generic_serialize( +SubGhzProtocolStatus subghz_block_generic_serialize( SubGhzBlockGeneric* instance, FlipperFormat* flipper_format, SubGhzRadioPreset* preset) { furi_assert(instance); - bool res = false; + SubGhzProtocolStatus res = SubGhzProtocolStatusError; FuriString* temp_str; temp_str = furi_string_alloc(); do { @@ -33,11 +33,13 @@ bool subghz_block_generic_serialize( if(!flipper_format_write_header_cstr( flipper_format, SUBGHZ_KEY_FILE_TYPE, SUBGHZ_KEY_FILE_VERSION)) { FURI_LOG_E(TAG, "Unable to add header"); + res = SubGhzProtocolStatusErrorParserHeader; break; } if(!flipper_format_write_uint32(flipper_format, "Frequency", &preset->frequency, 1)) { FURI_LOG_E(TAG, "Unable to add Frequency"); + res = SubGhzProtocolStatusErrorParserFrequency; break; } @@ -45,27 +47,32 @@ bool subghz_block_generic_serialize( if(!flipper_format_write_string_cstr( flipper_format, "Preset", furi_string_get_cstr(temp_str))) { FURI_LOG_E(TAG, "Unable to add Preset"); + res = SubGhzProtocolStatusErrorParserPreset; break; } if(!strcmp(furi_string_get_cstr(temp_str), "FuriHalSubGhzPresetCustom")) { if(!flipper_format_write_string_cstr( flipper_format, "Custom_preset_module", "CC1101")) { FURI_LOG_E(TAG, "Unable to add Custom_preset_module"); + res = SubGhzProtocolStatusErrorParserCustomPreset; break; } if(!flipper_format_write_hex( flipper_format, "Custom_preset_data", preset->data, preset->data_size)) { FURI_LOG_E(TAG, "Unable to add Custom_preset_data"); + res = SubGhzProtocolStatusErrorParserCustomPreset; break; } } if(!flipper_format_write_string_cstr(flipper_format, "Protocol", instance->protocol_name)) { FURI_LOG_E(TAG, "Unable to add Protocol"); + res = SubGhzProtocolStatusErrorParserProtocolName; break; } uint32_t temp = instance->data_count_bit; if(!flipper_format_write_uint32(flipper_format, "Bit", &temp, 1)) { FURI_LOG_E(TAG, "Unable to add Bit"); + res = SubGhzProtocolStatusErrorParserBitCount; break; } @@ -76,17 +83,29 @@ bool subghz_block_generic_serialize( if(!flipper_format_write_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) { FURI_LOG_E(TAG, "Unable to add Key"); + res = SubGhzProtocolStatusErrorParserKey; break; } - res = true; + + // Nice One - Manual adding support + if(instance->data_count_bit == 72 && + (strcmp(instance->protocol_name, "Nice FloR-S") == 0)) { + uint32_t temp = (instance->data_2 >> 4) & 0xFFFFF; + if(!flipper_format_write_uint32(flipper_format, "Data", &temp, 1)) { + FURI_LOG_E(TAG, "Unable to add Data"); + break; + } + } + res = SubGhzProtocolStatusOk; } while(false); furi_string_free(temp_str); return res; } -bool subghz_block_generic_deserialize(SubGhzBlockGeneric* instance, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_block_generic_deserialize(SubGhzBlockGeneric* instance, FlipperFormat* flipper_format) { furi_assert(instance); - bool res = false; + SubGhzProtocolStatus res = SubGhzProtocolStatusError; FuriString* temp_str; temp_str = furi_string_alloc(); uint32_t temp_data = 0; @@ -94,10 +113,12 @@ bool subghz_block_generic_deserialize(SubGhzBlockGeneric* instance, FlipperForma do { if(!flipper_format_rewind(flipper_format)) { FURI_LOG_E(TAG, "Rewind error"); + res = SubGhzProtocolStatusErrorParserOthers; break; } if(!flipper_format_read_uint32(flipper_format, "Bit", (uint32_t*)&temp_data, 1)) { FURI_LOG_E(TAG, "Missing Bit"); + res = SubGhzProtocolStatusErrorParserBitCount; break; } instance->data_count_bit = (uint16_t)temp_data; @@ -105,16 +126,36 @@ bool subghz_block_generic_deserialize(SubGhzBlockGeneric* instance, FlipperForma uint8_t key_data[sizeof(uint64_t)] = {0}; if(!flipper_format_read_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) { FURI_LOG_E(TAG, "Missing Key"); + res = SubGhzProtocolStatusErrorParserKey; break; } for(uint8_t i = 0; i < sizeof(uint64_t); i++) { instance->data = instance->data << 8 | key_data[i]; } - res = true; + res = SubGhzProtocolStatusOk; } while(0); furi_string_free(temp_str); return res; } + +SubGhzProtocolStatus subghz_block_generic_deserialize_check_count_bit( + SubGhzBlockGeneric* instance, + FlipperFormat* flipper_format, + uint16_t count_bit) { + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; + do { + ret = subghz_block_generic_deserialize(instance, flipper_format); + if(ret != SubGhzProtocolStatusOk) { + break; + } + if(instance->data_count_bit != count_bit) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + ret = SubGhzProtocolStatusErrorValueBitCount; + break; + } + } while(false); + return ret; +} \ No newline at end of file diff --git a/lib/subghz/blocks/generic.h b/lib/subghz/blocks/generic.h index e69de8b4f..10e7b63fa 100644 --- a/lib/subghz/blocks/generic.h +++ b/lib/subghz/blocks/generic.h @@ -39,9 +39,9 @@ void subghz_block_generic_get_preset_name(const char* preset_name, FuriString* p * @param instance Pointer to a SubGhzBlockGeneric instance * @param flipper_format Pointer to a FlipperFormat instance * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success + * @return Status Error */ -bool subghz_block_generic_serialize( +SubGhzProtocolStatus subghz_block_generic_serialize( SubGhzBlockGeneric* instance, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); @@ -50,9 +50,22 @@ bool subghz_block_generic_serialize( * Deserialize data SubGhzBlockGeneric. * @param instance Pointer to a SubGhzBlockGeneric instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return Status Error */ -bool subghz_block_generic_deserialize(SubGhzBlockGeneric* instance, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_block_generic_deserialize(SubGhzBlockGeneric* instance, FlipperFormat* flipper_format); + +/** + * Deserialize data SubGhzBlockGeneric. + * @param instance Pointer to a SubGhzBlockGeneric instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param count_bit Count bit protocol + * @return Status Error + */ +SubGhzProtocolStatus subghz_block_generic_deserialize_check_count_bit( + SubGhzBlockGeneric* instance, + FlipperFormat* flipper_format, + uint16_t count_bit); #ifdef __cplusplus } diff --git a/lib/subghz/helpers/subghz_config_preset_custom.c b/lib/subghz/helpers/subghz_config_preset_custom.c deleted file mode 100644 index 73b732d48..000000000 --- a/lib/subghz/helpers/subghz_config_preset_custom.c +++ /dev/null @@ -1,326 +0,0 @@ -#include "subghz_config_preset_custom.h" - -#include -#include -#include // UNUSED() -#include // furi_assert() -#include // log2(), floor() - -#include - -// https://www.ti.com/lit/ds/symlink/cc1101.pdf?ts=1671943815135 -// page 35. -// 12 Data Rate Programming -// -#define DATARATE_FUNC_CHIP_FOSC 26000000.0 /* 26MHz */ -#define DATARATE_FUNC_DIVIDER (1 << 28) /* 2 pow 28 */ -#define DATARATE_FUNC_MULTIPLIER \ - (DATARATE_FUNC_CHIP_FOSC / DATARATE_FUNC_DIVIDER) /* should be 0.09685754 */ - -#define DATARATE_EXP_FORMULA_DIVISIBLE (1 << 20) /* 2 pow 20 */ -#define DATARATE_EXP_FORMULA_MULTIPLIER \ - (DATARATE_EXP_FORMULA_DIVISIBLE / DATARATE_FUNC_CHIP_FOSC) /* should be 0.04032984 */ - -#define DATARATE_MNT_FORMULA_DIVISIBLE (1 << 28) /* 2 pow 28 */ -#define DATARATE_MNT_FORMULA_MULTIPLIER \ - (DATARATE_MNT_FORMULA_DIVISIBLE / DATARATE_FUNC_CHIP_FOSC) /* should be 10.3244406 */ -// - -#define SUGHZ_CONFIG_TAG "SubGHz_Config" - -uint8_t furi_hal_subghz_preset_ook_custom_async_regs[PRESET_OOK_CUSTOM_ADVANCED_AM_SIZE] = {0}; - -/** Check if cursom preset is AM (OOK) modulation - * - * This will check MOD_FORMAT bits in CC1101_MDMCFG2 register - * If preset data doesn have this register - will return false. - * This function will not fail in any case - * - * @param preset_data Custom preset data (registers and patable) - * @param data_len Data length - */ -bool subghz_preset_custom_is_ook_modulation(const uint8_t* preset_data, uint8_t data_len) { - if(preset_data != NULL) { - for(uint8_t i = 2; i <= data_len; i += 2) { - if(preset_data[i - 2] == CC1101_MDMCFG2) { - return (preset_data[i - 1] & 0b01110000) == 0x30; - } - } - } - return false; -} - -/** Get bandwidth value from preset data. - * - * This will get HIGHER bits in CC1101_MDMCFG4 register - * If CC1101_MDMCFG4 is not found in preset data - will return - * CH_BANDWIDTH_INVALID (0xFF) - * If there is ANY low 4 bits in returned value - the value is invalid - * - * @param preset_data Custom preset data (registers and patable) - * @param data_len Data length - */ -uint8_t subghz_preset_custom_get_bandwidth(const uint8_t* preset_data, uint8_t data_len) { - if(preset_data != NULL) { - for(uint8_t i = 2; i <= data_len; i += 2) { - if(preset_data[i - 2] == CC1101_MDMCFG4) { - return (preset_data[i - 1] & 0b11110000); - } - } - } - return CH_BANDWIDTH_INVALID; -} - -/** Set bandwidth value to preset data. - * - * This will set HIGHER bits in CC1101_MDMCFG4 register - * If CC1101_MDMCFG4 is not found in preset data - will do nothing and return false - * If there are ANY low 4 bits in provided value - they will be ignored - * - * @param preset_data Custom preset data (registers and patable) - * @param data_len Data length - * @param value New bandwidth value. See macros definition for possible values - */ -bool subghz_preset_custom_set_bandwidth(uint8_t* preset_data, uint8_t data_len, uint8_t value) { - if(preset_data != NULL) { - for(uint8_t i = 2; i <= data_len; i += 2) { - if(preset_data[i - 2] == CC1101_MDMCFG4) { - preset_data[i - 1] = (preset_data[i - 1] & 0b00001111) | (0b11110000 & value); - return true; - } - } - } - return false; -} - -/** Get data rate value from preset data. - * - * This will get DRATE_M and DRATE_E bits from CC1101_MDMCFG3 and CC1101_MDMCFG4 registers - * and calculate the value for 26MHz chip oscillator by formula from datasheet. - * - * If CC1101_MDMCFG[3:4] are not found in preset data - will return `-1` - * - * @param preset_data Custom preset data (registers and patable) - * @param data_len Data length - */ -float subghz_preset_custom_get_datarate(const uint8_t* preset_data, uint8_t data_len) { - if(preset_data != NULL) { - uint8_t mantissa = 0xFF; - uint8_t exponent = 0xFF; // Invalid, only 4 lower bits are singificant - - uint8_t step = 0; - - for(uint8_t i = 2; i <= data_len && step < 2; i += 2) { - if(preset_data[i - 2] == CC1101_MDMCFG4) { - exponent = preset_data[i - 1] & 0b00001111; - step++; - } else if(preset_data[i - 2] == CC1101_MDMCFG3) { - mantissa = preset_data[i - 1]; - step++; - } - } - - if(step == 2) { - return (float)((256 + mantissa) * (1 << exponent) * DATARATE_FUNC_MULTIPLIER); - } - } - return -1; -} - -/** Set data rate value to preset data. - * - * This will update DRATE_M and DRATE_E bits from CC1101_MDMCFG3 and CC1101_MDMCFG4 registers - * with calculated values for 26MHz chip oscillator by formula from datasheet. - * - * If CC1101_MDMCFG[3:4] are not found in preset data - will return false - * - * @param preset_data Custom preset data (registers and patable) - * @param data_len Data length - * @param value value in kBaud - */ -bool subghz_preset_custom_set_datarate(uint8_t* preset_data, uint8_t data_len, float value) { - if(preset_data != NULL) { - uint8_t* pMantissa = NULL; - uint8_t* pExponent = NULL; - - uint8_t step = 0; - for(uint8_t i = 2; i <= data_len && step < 2; i += 2) { - if(preset_data[i - 2] == CC1101_MDMCFG4) { - pExponent = &preset_data[i - 1]; - step++; - } else if(preset_data[i - 2] == CC1101_MDMCFG3) { - pMantissa = &preset_data[i - 1]; - step++; - } - } - - // Has both registers in data - calculate values - if(step == 2) { - // │ value * 2^20 │ - // DRATE_E = │log2(──────────────)│ - // └ Fosc ┘ - - double exponent = floor(log2(value * DATARATE_EXP_FORMULA_MULTIPLIER)); - uint8_t datarate_e = (uint8_t)exponent; - - // value * 2^28 - // DRATE_M = (────────────────────) - 256 - // Fosc * 2^DRATE_E - double mantissa = - floor((value * DATARATE_MNT_FORMULA_MULTIPLIER) / (1 << datarate_e) + 0.5) - 256; - - // If DRATE_M is rounded to the nearest integer and becomes 256, increment DRATE_E and use DRATE_M = 0. - if(mantissa >= 256) { - mantissa = 0; - datarate_e += 1; - } - uint8_t datarate_m = (uint8_t)mantissa; - - *pExponent = (*pExponent & 0b11110000) | (datarate_e & 0b00001111); - *pMantissa = datarate_m; - - return true; - } - } - - return false; -} - -/** Print datarate value to string - * - * This is just convenience function - * - * @param datarate datarate obtained from `subghz_preset_custom_get_datarate` function - * @param string Target print buffer - * @param size Target print buffer size - */ -void subghz_preset_custom_printf_datarate(float datarate, char* string, uint8_t size) { - float kBaudRate = datarate / 1000.0f; - snprintf( - string, - size, - "%lu.%02lu kBd", - (uint32_t)(kBaudRate), // decimal part - (uint32_t)((kBaudRate - (uint32_t)kBaudRate) * 100) // fractional part multiplied by 100 - ); -} - -/** Get Manchester encoding/decoding flag value from preset data. - * - * This will get MANCHESTER_EN (3-rd) bit in CC1101_MDMCFG2 register - * If CC1101_MDMCFG2 is not found in preset data - will return false - * - * @param preset_data Custom preset data (registers and patable) - * @param data_len Data length - */ -bool subghz_preset_custom_get_machester_enable(const uint8_t* preset_data, uint8_t data_len) { - if(preset_data != NULL) { - for(uint8_t i = 2; i <= data_len; i += 2) { - if(preset_data[i - 2] == CC1101_MDMCFG2) { - return (preset_data[i - 1] & 0b00001000); - } - } - } - return false; -} - -/** Set Manchester encoding/decoding flag value to preset data. - * - * This will set MANCHESTER_EN (3-rd) bit in CC1101_MDMCFG2 register - * If CC1101_MDMCFG2 is not found in preset data - will return false - * - * @param preset_data Custom preset data (registers and patable) - * @param data_len Data length - */ -bool subghz_preset_custom_set_machester_enable(uint8_t* preset_data, uint8_t data_len, bool value) { - if(preset_data != NULL) { - for(uint8_t i = 2; i <= data_len; i += 2) { - if(preset_data[i - 2] == CC1101_MDMCFG2) { - preset_data[i - 1] = (preset_data[i - 1] & 0b11110111) | (0b00001000 * value); - return true; - } - } - } - return false; -} - -/** - * Initialize custom preset data - */ -void subghz_preset_custom_init_advanced_am_preset() { - FURI_LOG_D(SUGHZ_CONFIG_TAG, "Initializing AM preset with custom Modem configuration"); - - if(furi_hal_subghz_preset_ook_custom_async_regs[0]) { - // already initialized - FURI_LOG_D(SUGHZ_CONFIG_TAG, "Already initialized"); - return; - } - - // Copy default AM270 preset - memcpy( - &furi_hal_subghz_preset_ook_custom_async_regs, - &furi_hal_subghz_preset_ook_270khz_async_regs, - sizeof(furi_hal_subghz_preset_ook_270khz_async_regs)); - - const uint8_t ModemConfigStart = 4; - -#if FURI_DEBUG - const uint8_t ModemConfigEnd = ModemConfigStart + MODEM_CONFIG_REGISTERS_COUNT; - for(uint8_t i = ModemConfigStart; i < ModemConfigEnd; ++i) { - // Check we'll overwrite correct settings - furi_assert( - furi_hal_subghz_preset_ook_custom_async_regs[i * 2 + 0] == - furi_hal_subghz_custom_modulation_regs[i - ModemConfigStart][0]); - } -#endif - - // Copy CUSTOM Modem preset - memcpy( - &furi_hal_subghz_preset_ook_custom_async_regs[ModemConfigStart * 2], - &furi_hal_subghz_custom_modulation_regs, - sizeof(furi_hal_subghz_custom_modulation_regs)); - - // Copy default AM270 patable - memcpy( - &furi_hal_subghz_preset_ook_custom_async_regs[sizeof( - furi_hal_subghz_preset_ook_270khz_async_regs)], - &furi_hal_subghz_preset_ook_async_patable, - sizeof(furi_hal_subghz_preset_ook_async_patable)); - - // Here at the end we should have - // 00 00 - -#if FURI_DEBUG - FURI_LOG_D(SUGHZ_CONFIG_TAG, "Custom OOK preset created"); - - for(uint8_t i = 0; i < PRESET_OOK_CUSTOM_ADVANCED_AM_SIZE; i += 2) { - FURI_LOG_D( - SUGHZ_CONFIG_TAG, - "Register: 0x%hhX, Value: 0x%hhX", - furi_hal_subghz_preset_ook_custom_async_regs[i * 2 + 0], - furi_hal_subghz_preset_ook_custom_async_regs[i * 2 + 1]); - } -#endif - - FURI_LOG_D(SUGHZ_CONFIG_TAG, "Done"); -} - -/** - * Create subghz preset file with custom am preset - * this is used for preset initialization if subghz app - */ -FlipperFormat* subghz_preset_custom_advanced_am_preset_alloc() { - FlipperFormat* advanced_am_preset = flipper_format_string_alloc(); - - subghz_preset_custom_init_advanced_am_preset(); - - flipper_format_write_hex( - advanced_am_preset, - (const char*)"Custom_preset_data", - (const uint8_t*)&furi_hal_subghz_preset_ook_custom_async_regs[0], - sizeof(furi_hal_subghz_preset_ook_custom_async_regs)); - - flipper_format_rewind(advanced_am_preset); - - return advanced_am_preset; -} \ No newline at end of file diff --git a/lib/subghz/helpers/subghz_config_preset_custom.h b/lib/subghz/helpers/subghz_config_preset_custom.h deleted file mode 100644 index 318197f5d..000000000 --- a/lib/subghz/helpers/subghz_config_preset_custom.h +++ /dev/null @@ -1,193 +0,0 @@ -#pragma once - -#include -#include /* memcpy() */ - -#define ADVANCED_AM_PRESET_NAME "AM*" - -// Awailable bandwidth values -// Setup in MDMCFG4 register -#define CH_BANDWIDTH_058 0b11110000 -#define CH_BANDWIDTH_068 0b11100000 -#define CH_BANDWIDTH_081 0b11010000 -#define CH_BANDWIDTH_102 0b11000000 - -#define CH_BANDWIDTH_116 0b10110000 -#define CH_BANDWIDTH_135 0b10100000 -#define CH_BANDWIDTH_162 0b10010000 -#define CH_BANDWIDTH_203 0b10000000 - -#define CH_BANDWIDTH_232 0b01110000 -#define CH_BANDWIDTH_270 0b01100000 -#define CH_BANDWIDTH_325 0b01010000 -#define CH_BANDWIDTH_406 0b01000000 - -#define CH_BANDWIDTH_464 0b00110000 -#define CH_BANDWIDTH_541 0b00100000 -#define CH_BANDWIDTH_650 0b00010000 -#define CH_BANDWIDTH_812 0b00000000 - -#define CH_BANDWIDTH_INVALID 0xFF - -static const uint8_t subghz_preset_custom_bandwidth_values[] = { - CH_BANDWIDTH_058, - CH_BANDWIDTH_068, - CH_BANDWIDTH_081, - CH_BANDWIDTH_102, - - CH_BANDWIDTH_116, - CH_BANDWIDTH_135, - CH_BANDWIDTH_162, - CH_BANDWIDTH_203, - - CH_BANDWIDTH_232, - CH_BANDWIDTH_270, - CH_BANDWIDTH_325, - CH_BANDWIDTH_406, - - CH_BANDWIDTH_464, - CH_BANDWIDTH_541, - CH_BANDWIDTH_650, - CH_BANDWIDTH_812, -}; -#define CH_BANDWIDTH_NUM (sizeof(subghz_preset_custom_bandwidth_values) / sizeof(uint8_t)) - -#define DATARATE_EXPONENT_3_79_kBaud 0b00000111 // 7 -#define DATARATE_MANTISSA_3_79_kBaud 0x32 - -#define CHANNEL_SPACING_25_EXPONENT 0b00000000 /* last bit */ -#define CHANNEL_SPACING_25_MANTISSA 0x00 - -#define MODEM_CONFIG_REGISTERS_COUNT 5 -#define PRESET_OOK_CUSTOM_ADVANCED_AM_SIZE \ - sizeof(furi_hal_subghz_preset_ook_270khz_async_regs) + \ - sizeof(furi_hal_subghz_preset_ook_async_patable) - -extern uint8_t furi_hal_subghz_preset_ook_custom_async_regs[PRESET_OOK_CUSTOM_ADVANCED_AM_SIZE]; - -static const uint8_t furi_hal_subghz_custom_modulation_regs[MODEM_CONFIG_REGISTERS_COUNT][2] = { - // Channel spacing is 25kHz, no Forward Error Correction, 2 preamble bytes (will be ignored) - {CC1101_MDMCFG0, CHANNEL_SPACING_25_MANTISSA}, - {CC1101_MDMCFG1, 0x00 | CHANNEL_SPACING_25_EXPONENT}, - - // [0:2] SYNC_MODE = 00 // No preamble/sync - // [3:3] MANCHESTER_EN = 0 // Disable - // [4:6] MOD_FORMAT = 03 // Format ASK/OOK - // [7:7] DEM_DCFILT_OFF = 0 // Enable - {CC1101_MDMCFG2, 0x30}, - - // 3.79 kBaud data rate (mantissa in 3rd register) - {CC1101_MDMCFG3, DATARATE_MANTISSA_3_79_kBaud}, - - // 270.8333 kHz Rx BW filer (hi) and 3.79 kBaud data rate (exponent in 4th register) - {CC1101_MDMCFG4, DATARATE_EXPONENT_3_79_kBaud | CH_BANDWIDTH_270}, -}; - -#ifdef __cplusplus -extern "C" { -#endif - -/** Check if cursom preset is AM (OOK) modulation - * - * This will check MOD_FORMAT bits in CC1101_MDMCFG2 register - * If preset data doesn have this register - will return false. - * This function will not fail in any case - * - * @param preset_data Custom preset data (registers and patable) - * @param data_len Data length - */ -bool subghz_preset_custom_is_ook_modulation(const uint8_t* preset_data, uint8_t data_len); - -/** Get bandwidth value from preset data. - * - * This will get HIGHER bits in CC1101_MDMCFG4 register - * If CC1101_MDMCFG4 is not found in preset data - will return - * CH_BANDWIDTH_INVALID (0xFF) - * If there is ANY low 4 bits in returned value - the value is invalid - * - * @param preset_data Custom preset data (registers and patable) - * @param data_len Data length - */ -uint8_t subghz_preset_custom_get_bandwidth(const uint8_t* preset_data, uint8_t data_len); - -/** Set bandwidth value to preset data. - * - * This will set HIGHER bits in CC1101_MDMCFG4 register - * If CC1101_MDMCFG4 is not found in preset data - will do nothing and return false - * If there are ANY low 4 bits in provided value - they will be ignored - * - * @param preset_data Custom preset data (registers and patable) - * @param data_len Data length - * @param value New bandwidth value. See macros definition for possible values - */ -bool subghz_preset_custom_set_bandwidth(uint8_t* preset_data, uint8_t data_len, uint8_t value); - -/** Get data rate value from preset data. - * - * This will get DRATE_M and DRATE_E bits from CC1101_MDMCFG3 and CC1101_MDMCFG4 registers - * and calculate the value for 26MHz chip oscillator by formula from datasheet. - * - * If CC1101_MDMCFG[3:4] are not found in preset data - will return `-1` - * - * @param preset_data Custom preset data (registers and patable) - * @param data_len Data length - */ -float subghz_preset_custom_get_datarate(const uint8_t* preset_data, uint8_t data_len); - -/** Set data rate value to preset data. - * - * This will update DRATE_M and DRATE_E bits from CC1101_MDMCFG3 and CC1101_MDMCFG4 registers - * with calculated values for 26MHz chip oscillator by formula from datasheet. - * - * If CC1101_MDMCFG[3:4] are not found in preset data - will return false - * - * @param preset_data Custom preset data (registers and patable) - * @param data_len Data length - * @param value value in kBaud - */ -bool subghz_preset_custom_set_datarate(uint8_t* preset_data, uint8_t data_len, float value); - -/** Print datarate value to string - * - * This is just conviniece function - * - * @param datarate datarate obtained from `subghz_preset_custom_get_datarate` function - * @param string Target print buffer - * @param size Target print buffer size - */ -void subghz_preset_custom_printf_datarate(float datarate, char* string, uint8_t size); - -/** Get Manchester encoding/decoding flag value from preset data. - * - * This will get MANCHESTER_EN (3-rd) bit in CC1101_MDMCFG2 register - * If CC1101_MDMCFG2 is not found in preset data - will return false - * - * @param preset_data Custom preset data (registers and patable) - * @param data_len Data length - */ -bool subghz_preset_custom_get_machester_enable(const uint8_t* preset_data, uint8_t data_len); - -/** Set Manchester encoding/decoding flag value to preset data. - * - * This will set MANCHESTER_EN (3-rd) bit in CC1101_MDMCFG2 register - * If CC1101_MDMCFG2 is not found in preset data - will return false - * - * @param preset_data Custom preset data (registers and patable) - * @param data_len Data length - */ -bool subghz_preset_custom_set_machester_enable(uint8_t* preset_data, uint8_t data_len, bool value); - -/** - * Initialize advanced am custom preset - */ -void subghz_preset_custom_init_advanced_am_preset(); - -/** - * Create subghz preset file with custom am preset - * this is used for preset initialization if subghz app - */ -struct FlipperFormat* subghz_preset_custom_advanced_am_preset_alloc(); - -#ifdef __cplusplus -} -#endif \ No newline at end of file diff --git a/lib/subghz/protocols/alutech_at_4n.c b/lib/subghz/protocols/alutech_at_4n.c index 7849f7b21..7b1bd5e76 100644 --- a/lib/subghz/protocols/alutech_at_4n.c +++ b/lib/subghz/protocols/alutech_at_4n.c @@ -77,6 +77,25 @@ const SubGhzProtocol subghz_protocol_alutech_at_4n = { .encoder = &subghz_protocol_alutech_at_4n_encoder, }; +static uint8_t al_btn_temp_id; +static uint8_t al_btn_temp_id_original; + +void alutech_set_btn(uint8_t b) { + al_btn_temp_id = b; +} + +uint8_t alutech_get_original_btn() { + return al_btn_temp_id_original; +} + +uint8_t alutech_get_custom_btn() { + return al_btn_temp_id; +} + +void alutech_reset_original_btn() { + al_btn_temp_id_original = 0; +} + void* subghz_protocol_encoder_alutech_at_4n_alloc(SubGhzEnvironment* environment) { UNUSED(environment); SubGhzProtocolEncoderAlutech_at_4n* instance = @@ -255,8 +274,6 @@ static uint64_t subghz_protocol_alutech_at_4n_encrypt(uint64_t data, const char* static bool subghz_protocol_alutech_at_4n_gen_data( SubGhzProtocolEncoderAlutech_at_4n* instance, uint8_t btn) { - UNUSED(btn); - uint64_t data = subghz_protocol_blocks_reverse_key(instance->generic.data, 64); data = subghz_protocol_alutech_at_4n_decrypt( @@ -269,13 +286,17 @@ static bool subghz_protocol_alutech_at_4n_gen_data( } if(instance->generic.cnt < 0xFFFF) { - instance->generic.cnt++; + if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) >= 0xFFFF) { + instance->generic.cnt = 0; + } else { + instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult(); + } } else if(instance->generic.cnt >= 0xFFFF) { instance->generic.cnt = 0; } crc = subghz_protocol_alutech_at_4n_decrypt_data_crc((uint8_t)(instance->generic.cnt & 0xFF)); data = (uint64_t)crc << 56 | (uint64_t)instance->generic.serial << 24 | - (uint32_t)instance->generic.cnt << 8 | instance->generic.btn; + (uint32_t)instance->generic.cnt << 8 | btn; data = subghz_protocol_alutech_at_4n_encrypt( data, instance->alutech_at_4n_rainbow_table_file_name); @@ -299,14 +320,15 @@ bool subghz_protocol_alutech_at_4n_create_data( instance->generic.data_count_bit = 72; bool res = subghz_protocol_alutech_at_4n_gen_data(instance, btn); if(res) { - res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + return SubGhzProtocolStatusOk == + subghz_block_generic_serialize(&instance->generic, flipper_format, preset); } return res; } /** * Generating an upload from data. - * @param instance Pointer to a SubGhzProtocolEncoderKeeloq instance + * @param instance Pointer to a SubGhzProtocolEncoderAlutech instance * @return true On success */ static bool subghz_protocol_encoder_alutech_at_4n_get_upload( @@ -314,6 +336,104 @@ static bool subghz_protocol_encoder_alutech_at_4n_get_upload( uint8_t btn) { furi_assert(instance); + // Save original button for later use + if(al_btn_temp_id_original == 0) { + al_btn_temp_id_original = btn; + } + + // Set custom button + if(al_btn_temp_id == 1) { + switch(al_btn_temp_id_original) { + case 0x11: + btn = 0x22; + break; + case 0x22: + btn = 0x11; + break; + case 0xFF: + btn = 0x11; + break; + case 0x44: + btn = 0x11; + break; + case 0x33: + btn = 0x11; + break; + + default: + break; + } + } + if(al_btn_temp_id == 2) { + switch(al_btn_temp_id_original) { + case 0x11: + btn = 0x44; + break; + case 0x22: + btn = 0x44; + break; + case 0xFF: + btn = 0x44; + break; + case 0x44: + btn = 0xFF; + break; + case 0x33: + btn = 0x44; + break; + + default: + break; + } + } + if(al_btn_temp_id == 3) { + switch(al_btn_temp_id_original) { + case 0x11: + btn = 0x33; + break; + case 0x22: + btn = 0x33; + break; + case 0xFF: + btn = 0x33; + break; + case 0x44: + btn = 0x33; + break; + case 0x33: + btn = 0x22; + break; + + default: + break; + } + } + if(al_btn_temp_id == 4) { + switch(al_btn_temp_id_original) { + case 0x11: + btn = 0xFF; + break; + case 0x22: + btn = 0xFF; + break; + case 0xFF: + btn = 0x22; + break; + case 0x44: + btn = 0x22; + break; + case 0x33: + btn = 0xFF; + break; + + default: + break; + } + } + + if((al_btn_temp_id == 0) && (al_btn_temp_id_original != 0)) { + btn = al_btn_temp_id_original; + } //gen new key if(subghz_protocol_alutech_at_4n_gen_data(instance, btn)) { //ToDo if you need to add a callback to automatically update the data on the display @@ -380,14 +500,15 @@ static bool subghz_protocol_encoder_alutech_at_4n_get_upload( return true; } -bool subghz_protocol_encoder_alutech_at_4n_deserialize( +SubGhzProtocolStatus subghz_protocol_encoder_alutech_at_4n_deserialize( void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolEncoderAlutech_at_4n* instance = context; - bool res = false; + SubGhzProtocolStatus res = SubGhzProtocolStatusError; do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + if(SubGhzProtocolStatusOk != + subghz_block_generic_deserialize(&instance->generic, flipper_format)) { FURI_LOG_E(TAG, "Deserialize error"); break; } @@ -421,7 +542,7 @@ bool subghz_protocol_encoder_alutech_at_4n_deserialize( instance->encoder.is_running = true; - res = true; + res = SubGhzProtocolStatusOk; } while(false); return res; @@ -608,6 +729,11 @@ static void subghz_protocol_alutech_at_4n_remote_controller( instance->cnt = 0; instance->serial = 0; } + + // Save original button for later use + if(al_btn_temp_id_original == 0) { + al_btn_temp_id_original = instance->btn; + } } uint8_t subghz_protocol_decoder_alutech_at_4n_get_hash_data(void* context) { @@ -616,46 +742,46 @@ uint8_t subghz_protocol_decoder_alutech_at_4n_get_hash_data(void* context) { return (uint8_t)instance->crc; } -bool subghz_protocol_decoder_alutech_at_4n_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_alutech_at_4n_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderAlutech_at_4n* instance = context; - bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); - if(res && !flipper_format_write_uint32(flipper_format, "CRC", &instance->crc, 1)) { + SubGhzProtocolStatus res = + subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + if((res == SubGhzProtocolStatusOk) && + !flipper_format_write_uint32(flipper_format, "CRC", &instance->crc, 1)) { FURI_LOG_E(TAG, "Unable to add CRC"); - res = false; + res = SubGhzProtocolStatusErrorParserOthers; } return res; - - return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); } -bool subghz_protocol_decoder_alutech_at_4n_deserialize( +SubGhzProtocolStatus subghz_protocol_decoder_alutech_at_4n_deserialize( void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolDecoderAlutech_at_4n* instance = context; - bool ret = false; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_alutech_at_4n_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); + ret = subghz_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + subghz_protocol_alutech_at_4n_const.min_count_bit_for_found); + if(ret != SubGhzProtocolStatusOk) { break; } if(!flipper_format_rewind(flipper_format)) { FURI_LOG_E(TAG, "Rewind error"); + ret = SubGhzProtocolStatusErrorParserOthers; break; } if(!flipper_format_read_uint32(flipper_format, "CRC", (uint32_t*)&instance->crc, 1)) { FURI_LOG_E(TAG, "Missing CRC"); + ret = SubGhzProtocolStatusErrorParserOthers; break; } - ret = true; } while(false); return ret; } @@ -670,17 +796,16 @@ void subghz_protocol_decoder_alutech_at_4n_get_string(void* context, FuriString* furi_string_cat_printf( output, - "%s %d\r\n" - "Key:0x%08lX%08lX%02X\r\n" + "%s\r\n" + "Key:0x%08lX%08lX\nCRC:%02X %dbit\r\n" "Sn:0x%08lX Btn:0x%01X\r\n" - "Cnt:0x%03lX\r\n", - + "Cnt:0x%04lX\r\n", instance->generic.protocol_name, - instance->generic.data_count_bit, code_found_hi, code_found_lo, (uint8_t)instance->crc, + instance->generic.data_count_bit, instance->generic.serial, instance->generic.btn, instance->generic.cnt); -} \ No newline at end of file +} diff --git a/lib/subghz/protocols/alutech_at_4n.h b/lib/subghz/protocols/alutech_at_4n.h index b6d51439b..89adbb5c6 100644 --- a/lib/subghz/protocols/alutech_at_4n.h +++ b/lib/subghz/protocols/alutech_at_4n.h @@ -1,7 +1,7 @@ #pragma once #include "base.h" -#define SUBGHZ_PROTOCOL_ALUTECH_AT_4N_NAME "Alutech at-4n" +#define SUBGHZ_PROTOCOL_ALUTECH_AT_4N_NAME "Alutech AT-4N" typedef struct SubGhzProtocolDecoderAlutech_at_4n SubGhzProtocolDecoderAlutech_at_4n; typedef struct SubGhzProtocolEncoderAlutech_at_4n SubGhzProtocolEncoderAlutech_at_4n; @@ -10,6 +10,14 @@ extern const SubGhzProtocolDecoder subghz_protocol_alutech_at_4n_decoder; extern const SubGhzProtocolEncoder subghz_protocol_alutech_at_4n_encoder; extern const SubGhzProtocol subghz_protocol_alutech_at_4n; +// Custom buttons +void alutech_set_btn(uint8_t b); + +uint8_t alutech_get_original_btn(); +uint8_t alutech_get_custom_btn(); + +void alutech_reset_original_btn(); + /** * Allocate SubGhzProtocolEncoderAlutech_at_4n. * @param environment Pointer to a SubGhzEnvironment instance @@ -47,9 +55,8 @@ bool subghz_protocol_alutech_at_4n_create_data( * @param flipper_format Pointer to a FlipperFormat instance * @return true On success */ -bool subghz_protocol_encoder_alutech_at_4n_deserialize( - void* context, - FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_encoder_alutech_at_4n_deserialize(void* context, FlipperFormat* flipper_format); /** * Forced transmission stop. @@ -103,9 +110,9 @@ uint8_t subghz_protocol_decoder_alutech_at_4n_get_hash_data(void* context); * @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance * @param flipper_format Pointer to a FlipperFormat instance * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success + * @return status */ -bool subghz_protocol_decoder_alutech_at_4n_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_alutech_at_4n_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); @@ -114,15 +121,14 @@ bool subghz_protocol_decoder_alutech_at_4n_serialize( * Deserialize data SubGhzProtocolDecoderAlutech_at_4n. * @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_protocol_decoder_alutech_at_4n_deserialize( - void* context, - FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_decoder_alutech_at_4n_deserialize(void* context, FlipperFormat* flipper_format); /** * Getting a textual representation of the received data. * @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance * @param output Resulting text */ -void subghz_protocol_decoder_alutech_at_4n_get_string(void* context, FuriString* output); \ No newline at end of file +void subghz_protocol_decoder_alutech_at_4n_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/ansonic.c b/lib/subghz/protocols/ansonic.c index 81b196e36..9a122629b 100644 --- a/lib/subghz/protocols/ansonic.c +++ b/lib/subghz/protocols/ansonic.c @@ -136,28 +136,29 @@ static bool subghz_protocol_encoder_ansonic_get_upload(SubGhzProtocolEncoderAnso return true; } -bool subghz_protocol_encoder_ansonic_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_encoder_ansonic_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolEncoderAnsonic* instance = context; - bool res = false; + SubGhzProtocolStatus res = SubGhzProtocolStatusError; do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + res = subghz_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + subghz_protocol_ansonic_const.min_count_bit_for_found); + if(res != SubGhzProtocolStatusOk) { FURI_LOG_E(TAG, "Deserialize error"); break; } - if(instance->generic.data_count_bit != - subghz_protocol_ansonic_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_ansonic_get_upload(instance)) break; + if(!subghz_protocol_encoder_ansonic_get_upload(instance)) { + res = SubGhzProtocolStatusErrorEncoderGetUpload; + break; + } instance->encoder.is_running = true; - - res = true; } while(false); return res; @@ -301,7 +302,7 @@ uint8_t subghz_protocol_decoder_ansonic_get_hash_data(void* context) { &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } -bool subghz_protocol_decoder_ansonic_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_ansonic_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset) { @@ -310,22 +311,12 @@ bool subghz_protocol_decoder_ansonic_serialize( return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); } -bool subghz_protocol_decoder_ansonic_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_decoder_ansonic_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolDecoderAnsonic* instance = context; - bool ret = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_ansonic_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); - break; - } - ret = true; - } while(false); - return ret; + return subghz_block_generic_deserialize_check_count_bit( + &instance->generic, flipper_format, subghz_protocol_ansonic_const.min_count_bit_for_found); } void subghz_protocol_decoder_ansonic_get_string(void* context, FuriString* output) { diff --git a/lib/subghz/protocols/ansonic.h b/lib/subghz/protocols/ansonic.h index 0170a6048..955853187 100644 --- a/lib/subghz/protocols/ansonic.h +++ b/lib/subghz/protocols/ansonic.h @@ -30,7 +30,8 @@ void subghz_protocol_encoder_ansonic_free(void* context); * @param flipper_format Pointer to a FlipperFormat instance * @return true On success */ -bool subghz_protocol_encoder_ansonic_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_encoder_ansonic_deserialize(void* context, FlipperFormat* flipper_format); /** * Forced transmission stop. @@ -86,7 +87,7 @@ uint8_t subghz_protocol_decoder_ansonic_get_hash_data(void* context); * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ -bool subghz_protocol_decoder_ansonic_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_ansonic_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); @@ -97,7 +98,8 @@ bool subghz_protocol_decoder_ansonic_serialize( * @param flipper_format Pointer to a FlipperFormat instance * @return true On success */ -bool subghz_protocol_decoder_ansonic_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_decoder_ansonic_deserialize(void* context, FlipperFormat* flipper_format); /** * Getting a textual representation of the received data. diff --git a/lib/subghz/protocols/base.c b/lib/subghz/protocols/base.c index 36f33b9a5..37d1a308f 100644 --- a/lib/subghz/protocols/base.c +++ b/lib/subghz/protocols/base.c @@ -23,11 +23,11 @@ bool subghz_protocol_decoder_base_get_string( return status; } -bool subghz_protocol_decoder_base_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_base_serialize( SubGhzProtocolDecoderBase* decoder_base, FlipperFormat* flipper_format, SubGhzRadioPreset* preset) { - bool status = false; + SubGhzProtocolStatus status = SubGhzProtocolStatusError; if(decoder_base->protocol && decoder_base->protocol->decoder && decoder_base->protocol->decoder->serialize) { @@ -37,10 +37,10 @@ bool subghz_protocol_decoder_base_serialize( return status; } -bool subghz_protocol_decoder_base_deserialize( +SubGhzProtocolStatus subghz_protocol_decoder_base_deserialize( SubGhzProtocolDecoderBase* decoder_base, FlipperFormat* flipper_format) { - bool status = false; + SubGhzProtocolStatus status = SubGhzProtocolStatusError; if(decoder_base->protocol && decoder_base->protocol->decoder && decoder_base->protocol->decoder->deserialize) { diff --git a/lib/subghz/protocols/base.h b/lib/subghz/protocols/base.h index 1f3d3e1be..1d819ab5e 100644 --- a/lib/subghz/protocols/base.h +++ b/lib/subghz/protocols/base.h @@ -49,9 +49,9 @@ bool subghz_protocol_decoder_base_get_string( * @param decoder_base Pointer to a SubGhzProtocolDecoderBase instance * @param flipper_format Pointer to a FlipperFormat instance * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success + * @return Status Error */ -bool subghz_protocol_decoder_base_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_base_serialize( SubGhzProtocolDecoderBase* decoder_base, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); @@ -60,9 +60,9 @@ bool subghz_protocol_decoder_base_serialize( * Deserialize data SubGhzProtocolDecoderBase. * @param decoder_base Pointer to a SubGhzProtocolDecoderBase instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return Status Error */ -bool subghz_protocol_decoder_base_deserialize( +SubGhzProtocolStatus subghz_protocol_decoder_base_deserialize( SubGhzProtocolDecoderBase* decoder_base, FlipperFormat* flipper_format); diff --git a/lib/subghz/protocols/bett.c b/lib/subghz/protocols/bett.c index 644d80fd8..de13472ac 100644 --- a/lib/subghz/protocols/bett.c +++ b/lib/subghz/protocols/bett.c @@ -155,31 +155,32 @@ static bool subghz_protocol_encoder_bett_get_upload(SubGhzProtocolEncoderBETT* i return true; } -bool subghz_protocol_encoder_bett_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_encoder_bett_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolEncoderBETT* instance = context; - bool res = false; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + ret = subghz_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + subghz_protocol_bett_const.min_count_bit_for_found); + if(ret != SubGhzProtocolStatusOk) { FURI_LOG_E(TAG, "Deserialize error"); break; } - if(instance->generic.data_count_bit != - subghz_protocol_bett_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_bett_get_upload(instance)) break; + if(!subghz_protocol_encoder_bett_get_upload(instance)) { + ret = SubGhzProtocolStatusErrorEncoderGetUpload; + break; + } instance->encoder.is_running = true; - - res = true; } while(false); - return res; + return ret; } void subghz_protocol_encoder_bett_stop(void* context) { @@ -295,7 +296,7 @@ uint8_t subghz_protocol_decoder_bett_get_hash_data(void* context) { &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } -bool subghz_protocol_decoder_bett_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_bett_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset) { @@ -304,22 +305,12 @@ bool subghz_protocol_decoder_bett_serialize( return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); } -bool subghz_protocol_decoder_bett_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_decoder_bett_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolDecoderBETT* instance = context; - bool ret = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_bett_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); - break; - } - ret = true; - } while(false); - return ret; + return subghz_block_generic_deserialize_check_count_bit( + &instance->generic, flipper_format, subghz_protocol_bett_const.min_count_bit_for_found); } void subghz_protocol_decoder_bett_get_string(void* context, FuriString* output) { diff --git a/lib/subghz/protocols/bett.h b/lib/subghz/protocols/bett.h index c0ce0b7f4..0a11cbe69 100644 --- a/lib/subghz/protocols/bett.h +++ b/lib/subghz/protocols/bett.h @@ -28,9 +28,10 @@ void subghz_protocol_encoder_bett_free(void* context); * Deserialize and generating an upload to send. * @param context Pointer to a SubGhzProtocolEncoderBETT instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_protocol_encoder_bett_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_encoder_bett_deserialize(void* context, FlipperFormat* flipper_format); /** * Forced transmission stop. @@ -84,9 +85,9 @@ uint8_t subghz_protocol_decoder_bett_get_hash_data(void* context); * @param context Pointer to a SubGhzProtocolDecoderBETT instance * @param flipper_format Pointer to a FlipperFormat instance * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success + * @return status */ -bool subghz_protocol_decoder_bett_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_bett_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); @@ -95,9 +96,10 @@ bool subghz_protocol_decoder_bett_serialize( * Deserialize data SubGhzProtocolDecoderBETT. * @param context Pointer to a SubGhzProtocolDecoderBETT instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_protocol_decoder_bett_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_decoder_bett_deserialize(void* context, FlipperFormat* flipper_format); /** * Getting a textual representation of the received data. diff --git a/lib/subghz/protocols/bin_raw.c b/lib/subghz/protocols/bin_raw.c index 67e0467ee..003cc5edd 100644 --- a/lib/subghz/protocols/bin_raw.c +++ b/lib/subghz/protocols/bin_raw.c @@ -219,20 +219,23 @@ static bool subghz_protocol_encoder_bin_raw_get_upload(SubGhzProtocolEncoderBinR return true; } -bool subghz_protocol_encoder_bin_raw_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_encoder_bin_raw_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolEncoderBinRAW* instance = context; - bool res = false; + SubGhzProtocolStatus res = SubGhzProtocolStatusError; uint32_t temp_data = 0; do { if(!flipper_format_rewind(flipper_format)) { FURI_LOG_E(TAG, "Rewind error"); + res = SubGhzProtocolStatusErrorParserOthers; break; } if(!flipper_format_read_uint32(flipper_format, "Bit", (uint32_t*)&temp_data, 1)) { FURI_LOG_E(TAG, "Missing Bit"); + res = SubGhzProtocolStatusErrorParserBitCount; break; } @@ -240,6 +243,7 @@ bool subghz_protocol_encoder_bin_raw_deserialize(void* context, FlipperFormat* f if(!flipper_format_read_uint32(flipper_format, "TE", (uint32_t*)&instance->te, 1)) { FURI_LOG_E(TAG, "Missing TE"); + res = SubGhzProtocolStatusErrorParserTe; break; } @@ -251,11 +255,13 @@ bool subghz_protocol_encoder_bin_raw_deserialize(void* context, FlipperFormat* f while(flipper_format_read_uint32(flipper_format, "Bit_RAW", (uint32_t*)&temp_data, 1)) { if(ind >= BIN_RAW_MAX_MARKUP_COUNT) { FURI_LOG_E(TAG, "Markup overflow"); + res = SubGhzProtocolStatusErrorParserOthers; break; } byte_count += subghz_protocol_bin_raw_get_full_byte(temp_data); if(byte_count > BIN_RAW_BUF_DATA_SIZE) { FURI_LOG_E(TAG, "Receive buffer overflow"); + res = SubGhzProtocolStatusErrorParserOthers; break; } @@ -270,6 +276,7 @@ bool subghz_protocol_encoder_bin_raw_deserialize(void* context, FlipperFormat* f subghz_protocol_bin_raw_get_full_byte(temp_data))) { instance->data_markup[ind].bit_count = 0; FURI_LOG_E(TAG, "Missing Data_RAW"); + res = SubGhzProtocolStatusErrorParserOthers; break; } ind++; @@ -297,16 +304,20 @@ bool subghz_protocol_encoder_bin_raw_deserialize(void* context, FlipperFormat* f #endif if(!flipper_format_rewind(flipper_format)) { FURI_LOG_E(TAG, "Rewind error"); + res = SubGhzProtocolStatusErrorParserOthers; break; } //optional parameter parameter flipper_format_read_uint32( flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); - if(!subghz_protocol_encoder_bin_raw_get_upload(instance)) break; + if(!subghz_protocol_encoder_bin_raw_get_upload(instance)) { + break; + res = SubGhzProtocolStatusErrorEncoderGetUpload; + } instance->encoder.is_running = true; - res = true; + res = SubGhzProtocolStatusOk; } while(0); return res; @@ -957,14 +968,14 @@ uint8_t subghz_protocol_decoder_bin_raw_get_hash_data(void* context) { subghz_protocol_bin_raw_get_full_byte(instance->data_markup[0].bit_count)); } -bool subghz_protocol_decoder_bin_raw_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_bin_raw_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderBinRAW* instance = context; - bool res = false; + SubGhzProtocolStatus res = SubGhzProtocolStatusError; FuriString* temp_str; temp_str = furi_string_alloc(); do { @@ -972,11 +983,13 @@ bool subghz_protocol_decoder_bin_raw_serialize( if(!flipper_format_write_header_cstr( flipper_format, SUBGHZ_KEY_FILE_TYPE, SUBGHZ_KEY_FILE_VERSION)) { FURI_LOG_E(TAG, "Unable to add header"); + res = SubGhzProtocolStatusErrorParserHeader; break; } if(!flipper_format_write_uint32(flipper_format, "Frequency", &preset->frequency, 1)) { FURI_LOG_E(TAG, "Unable to add Frequency"); + res = SubGhzProtocolStatusErrorParserFrequency; break; } @@ -984,34 +997,40 @@ bool subghz_protocol_decoder_bin_raw_serialize( if(!flipper_format_write_string_cstr( flipper_format, "Preset", furi_string_get_cstr(temp_str))) { FURI_LOG_E(TAG, "Unable to add Preset"); + res = SubGhzProtocolStatusErrorParserPreset; break; } if(!strcmp(furi_string_get_cstr(temp_str), "FuriHalSubGhzPresetCustom")) { if(!flipper_format_write_string_cstr( flipper_format, "Custom_preset_module", "CC1101")) { FURI_LOG_E(TAG, "Unable to add Custom_preset_module"); + res = SubGhzProtocolStatusErrorParserCustomPreset; break; } if(!flipper_format_write_hex( flipper_format, "Custom_preset_data", preset->data, preset->data_size)) { FURI_LOG_E(TAG, "Unable to add Custom_preset_data"); + res = SubGhzProtocolStatusErrorParserCustomPreset; break; } } if(!flipper_format_write_string_cstr( flipper_format, "Protocol", instance->generic.protocol_name)) { FURI_LOG_E(TAG, "Unable to add Protocol"); + res = SubGhzProtocolStatusErrorParserProtocolName; break; } uint32_t temp = instance->generic.data_count_bit; if(!flipper_format_write_uint32(flipper_format, "Bit", &temp, 1)) { FURI_LOG_E(TAG, "Unable to add Bit"); + res = SubGhzProtocolStatusErrorParserBitCount; break; } if(!flipper_format_write_uint32(flipper_format, "TE", &instance->te, 1)) { FURI_LOG_E(TAG, "Unable to add TE"); + res = SubGhzProtocolStatusErrorParserTe; break; } @@ -1020,6 +1039,7 @@ bool subghz_protocol_decoder_bin_raw_serialize( temp = instance->data_markup[i].bit_count; if(!flipper_format_write_uint32(flipper_format, "Bit_RAW", &temp, 1)) { FURI_LOG_E(TAG, "Bit_RAW"); + res = SubGhzProtocolStatusErrorParserOthers; break; } if(!flipper_format_write_hex( @@ -1028,31 +1048,35 @@ bool subghz_protocol_decoder_bin_raw_serialize( instance->data + instance->data_markup[i].byte_bias, subghz_protocol_bin_raw_get_full_byte(instance->data_markup[i].bit_count))) { FURI_LOG_E(TAG, "Unable to add Data_RAW"); + res = SubGhzProtocolStatusErrorParserOthers; break; } i++; } - res = true; + res = SubGhzProtocolStatusOk; } while(false); furi_string_free(temp_str); return res; } -bool subghz_protocol_decoder_bin_raw_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_decoder_bin_raw_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolDecoderBinRAW* instance = context; - bool res = false; + SubGhzProtocolStatus res = SubGhzProtocolStatusError; uint32_t temp_data = 0; do { if(!flipper_format_rewind(flipper_format)) { FURI_LOG_E(TAG, "Rewind error"); + res = SubGhzProtocolStatusErrorParserOthers; break; } if(!flipper_format_read_uint32(flipper_format, "Bit", (uint32_t*)&temp_data, 1)) { FURI_LOG_E(TAG, "Missing Bit"); + res = SubGhzProtocolStatusErrorParserBitCount; break; } @@ -1060,6 +1084,7 @@ bool subghz_protocol_decoder_bin_raw_deserialize(void* context, FlipperFormat* f if(!flipper_format_read_uint32(flipper_format, "TE", (uint32_t*)&instance->te, 1)) { FURI_LOG_E(TAG, "Missing TE"); + res = SubGhzProtocolStatusErrorParserTe; break; } @@ -1071,11 +1096,13 @@ bool subghz_protocol_decoder_bin_raw_deserialize(void* context, FlipperFormat* f while(flipper_format_read_uint32(flipper_format, "Bit_RAW", (uint32_t*)&temp_data, 1)) { if(ind >= BIN_RAW_MAX_MARKUP_COUNT) { FURI_LOG_E(TAG, "Markup overflow"); + res = SubGhzProtocolStatusErrorParserOthers; break; } byte_count += subghz_protocol_bin_raw_get_full_byte(temp_data); if(byte_count > BIN_RAW_BUF_DATA_SIZE) { FURI_LOG_E(TAG, "Receive buffer overflow"); + res = SubGhzProtocolStatusErrorParserOthers; break; } @@ -1090,12 +1117,13 @@ bool subghz_protocol_decoder_bin_raw_deserialize(void* context, FlipperFormat* f subghz_protocol_bin_raw_get_full_byte(temp_data))) { instance->data_markup[ind].bit_count = 0; FURI_LOG_E(TAG, "Missing Data_RAW"); + res = SubGhzProtocolStatusErrorParserOthers; break; } ind++; } - res = true; + res = SubGhzProtocolStatusOk; } while(0); return res; diff --git a/lib/subghz/protocols/bin_raw.h b/lib/subghz/protocols/bin_raw.h index c63f86ce6..82775e575 100644 --- a/lib/subghz/protocols/bin_raw.h +++ b/lib/subghz/protocols/bin_raw.h @@ -28,9 +28,10 @@ void subghz_protocol_encoder_bin_raw_free(void* context); * Deserialize and generating an upload to send. * @param context Pointer to a SubGhzProtocolEncoderBinRAW instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_protocol_encoder_bin_raw_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_encoder_bin_raw_deserialize(void* context, FlipperFormat* flipper_format); /** * Forced transmission stop. @@ -88,9 +89,9 @@ void subghz_protocol_decoder_bin_raw_data_input_rssi( * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance * @param flipper_format Pointer to a FlipperFormat instance * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success + * @return status */ -bool subghz_protocol_decoder_bin_raw_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_bin_raw_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); @@ -99,9 +100,10 @@ bool subghz_protocol_decoder_bin_raw_serialize( * Deserialize data SubGhzProtocolDecoderBinRAW. * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_protocol_decoder_bin_raw_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_decoder_bin_raw_deserialize(void* context, FlipperFormat* flipper_format); /** * Getting a textual representation of the received data. diff --git a/lib/subghz/protocols/came.c b/lib/subghz/protocols/came.c index bed26d7d2..14b2e0101 100644 --- a/lib/subghz/protocols/came.c +++ b/lib/subghz/protocols/came.c @@ -13,6 +13,7 @@ */ #define TAG "SubGhzProtocolCAME" +#define CAME_12_COUNT_BIT 12 #define CAME_24_COUNT_BIT 24 #define PRASTEL_COUNT_BIT 25 #define PRASTEL_NAME "Prastel" @@ -108,6 +109,7 @@ void subghz_protocol_encoder_came_free(void* context) { */ static bool subghz_protocol_encoder_came_get_upload(SubGhzProtocolEncoderCame* instance) { furi_assert(instance); + uint32_t header_te = 0; size_t index = 0; size_t size_upload = (instance->generic.data_count_bit * 2) + 2; if(size_upload > instance->encoder.size_upload) { @@ -117,13 +119,28 @@ static bool subghz_protocol_encoder_came_get_upload(SubGhzProtocolEncoderCame* i instance->encoder.size_upload = size_upload; } //Send header - instance->encoder.upload[index++] = level_duration_make( - false, - (((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)); + + switch(instance->generic.data_count_bit) { + case CAME_24_COUNT_BIT: + // CAME 24 Bit = 24320 us + header_te = 76; + break; + case CAME_12_COUNT_BIT: + case AIRFORCE_COUNT_BIT: + // CAME 12 Bit Original only! and Airforce protocol = 15040 us + header_te = 47; + break; + case PRASTEL_COUNT_BIT: + // PRASTEL = 11520 us + header_te = 36; + break; + default: + // Some wrong detected protocols, 5120 us + header_te = 16; + break; + } + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_came_const.te_short * header_te); //Send start bit instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)subghz_protocol_came_const.te_short); @@ -146,30 +163,33 @@ static bool subghz_protocol_encoder_came_get_upload(SubGhzProtocolEncoderCame* i return true; } -bool subghz_protocol_encoder_came_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_encoder_came_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolEncoderCame* instance = context; - bool res = false; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - FURI_LOG_E(TAG, "Deserialize error"); + ret = subghz_block_generic_deserialize(&instance->generic, flipper_format); + if(ret != SubGhzProtocolStatusOk) { break; } if((instance->generic.data_count_bit > PRASTEL_COUNT_BIT)) { FURI_LOG_E(TAG, "Wrong number of bits in key"); + ret = SubGhzProtocolStatusErrorValueBitCount; break; } //optional parameter parameter flipper_format_read_uint32( flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); - if(!subghz_protocol_encoder_came_get_upload(instance)) break; + if(!subghz_protocol_encoder_came_get_upload(instance)) { + ret = SubGhzProtocolStatusErrorEncoderGetUpload; + break; + } instance->encoder.is_running = true; - - res = true; } while(false); - return res; + return ret; } void subghz_protocol_encoder_came_stop(void* context) { @@ -244,8 +264,11 @@ void subghz_protocol_decoder_came_feed(void* context, bool level, uint32_t durat if(!level) { //save interval if(duration >= (subghz_protocol_came_const.te_short * 4)) { instance->decoder.parser_step = CameDecoderStepFoundStartBit; - if(instance->decoder.decode_count_bit >= - subghz_protocol_came_const.min_count_bit_for_found) { + if((instance->decoder.decode_count_bit == + subghz_protocol_came_const.min_count_bit_for_found) || + (instance->decoder.decode_count_bit == AIRFORCE_COUNT_BIT) || + (instance->decoder.decode_count_bit == PRASTEL_COUNT_BIT) || + (instance->decoder.decode_count_bit == CAME_24_COUNT_BIT)) { instance->generic.serial = 0x0; instance->generic.btn = 0x0; @@ -294,7 +317,7 @@ uint8_t subghz_protocol_decoder_came_get_hash_data(void* context) { &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } -bool subghz_protocol_decoder_came_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_came_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset) { @@ -303,19 +326,21 @@ bool subghz_protocol_decoder_came_serialize( return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); } -bool subghz_protocol_decoder_came_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_decoder_came_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolDecoderCame* instance = context; - bool ret = false; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + ret = subghz_block_generic_deserialize(&instance->generic, flipper_format); + if(ret != SubGhzProtocolStatusOk) { break; } if((instance->generic.data_count_bit > PRASTEL_COUNT_BIT)) { FURI_LOG_E(TAG, "Wrong number of bits in key"); + ret = SubGhzProtocolStatusErrorValueBitCount; break; } - ret = true; } while(false); return ret; } diff --git a/lib/subghz/protocols/came.h b/lib/subghz/protocols/came.h index 253c93aae..fffa017ff 100644 --- a/lib/subghz/protocols/came.h +++ b/lib/subghz/protocols/came.h @@ -28,9 +28,10 @@ void subghz_protocol_encoder_came_free(void* context); * Deserialize and generating an upload to send. * @param context Pointer to a SubGhzProtocolEncoderCame instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_protocol_encoder_came_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_encoder_came_deserialize(void* context, FlipperFormat* flipper_format); /** * Forced transmission stop. @@ -84,9 +85,9 @@ uint8_t subghz_protocol_decoder_came_get_hash_data(void* context); * @param context Pointer to a SubGhzProtocolDecoderCame instance * @param flipper_format Pointer to a FlipperFormat instance * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success + * @return status */ -bool subghz_protocol_decoder_came_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_came_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); @@ -95,9 +96,10 @@ bool subghz_protocol_decoder_came_serialize( * Deserialize data SubGhzProtocolDecoderCame. * @param context Pointer to a SubGhzProtocolDecoderCame instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_protocol_decoder_came_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_decoder_came_deserialize(void* context, FlipperFormat* flipper_format); /** * Getting a textual representation of the received data. diff --git a/lib/subghz/protocols/came_atomo.c b/lib/subghz/protocols/came_atomo.c index d12e5976c..870afede3 100644 --- a/lib/subghz/protocols/came_atomo.c +++ b/lib/subghz/protocols/came_atomo.c @@ -136,7 +136,11 @@ static void uint8_t pack[8] = {}; if(instance->generic.cnt < 0xFFFF) { - instance->generic.cnt++; + if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) >= 0xFFFF) { + instance->generic.cnt = 0; + } else { + instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult(); + } } else if(instance->generic.cnt >= 0xFFFF) { instance->generic.cnt = 0; } @@ -219,12 +223,14 @@ static void instance->generic.data &= 0xFFFFFFFFFFFFFFF; } -bool subghz_protocol_encoder_came_atomo_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_encoder_came_atomo_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolEncoderCameAtomo* instance = context; - bool res = false; + SubGhzProtocolStatus res = SubGhzProtocolStatusError; do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + if(SubGhzProtocolStatusOk != + subghz_block_generic_deserialize(&instance->generic, flipper_format)) { FURI_LOG_E(TAG, "Deserialize error"); break; } @@ -251,7 +257,7 @@ bool subghz_protocol_encoder_came_atomo_deserialize(void* context, FlipperFormat instance->encoder.is_running = true; - res = true; + res = SubGhzProtocolStatusOk; } while(false); return res; @@ -545,7 +551,7 @@ uint8_t subghz_protocol_decoder_came_atomo_get_hash_data(void* context) { &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } -bool subghz_protocol_decoder_came_atomo_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_came_atomo_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset) { @@ -554,22 +560,14 @@ bool subghz_protocol_decoder_came_atomo_serialize( return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); } -bool subghz_protocol_decoder_came_atomo_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_decoder_came_atomo_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolDecoderCameAtomo* instance = context; - bool ret = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_came_atomo_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); - break; - } - ret = true; - } while(false); - return ret; + return subghz_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + subghz_protocol_came_atomo_const.min_count_bit_for_found); } void subghz_protocol_decoder_came_atomo_get_string(void* context, FuriString* output) { diff --git a/lib/subghz/protocols/came_atomo.h b/lib/subghz/protocols/came_atomo.h index 736aee850..c5e45a68d 100644 --- a/lib/subghz/protocols/came_atomo.h +++ b/lib/subghz/protocols/came_atomo.h @@ -33,7 +33,8 @@ void subghz_protocol_encoder_came_atomo_free(void* context); * @param flipper_format Pointer to a FlipperFormat instance * @return true On success */ -bool subghz_protocol_encoder_came_atomo_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_encoder_came_atomo_deserialize(void* context, FlipperFormat* flipper_format); /** * Forced transmission stop. @@ -87,9 +88,9 @@ uint8_t subghz_protocol_decoder_came_atomo_get_hash_data(void* context); * @param context Pointer to a SubGhzProtocolDecoderCameAtomo instance * @param flipper_format Pointer to a FlipperFormat instance * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success + * @return status */ -bool subghz_protocol_decoder_came_atomo_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_came_atomo_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); @@ -98,9 +99,10 @@ bool subghz_protocol_decoder_came_atomo_serialize( * Deserialize data SubGhzProtocolDecoderCameAtomo. * @param context Pointer to a SubGhzProtocolDecoderCameAtomo instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_protocol_decoder_came_atomo_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_decoder_came_atomo_deserialize(void* context, FlipperFormat* flipper_format); /** * Getting a textual representation of the received data. diff --git a/lib/subghz/protocols/came_twee.c b/lib/subghz/protocols/came_twee.c index e7eb33c42..6fe615813 100644 --- a/lib/subghz/protocols/came_twee.c +++ b/lib/subghz/protocols/came_twee.c @@ -241,18 +241,17 @@ static void subghz_protocol_came_twee_remote_controller(SubGhzBlockGeneric* inst instance->cnt = data >> 6; } -bool subghz_protocol_encoder_came_twee_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_encoder_came_twee_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolEncoderCameTwee* instance = context; - bool res = false; + SubGhzProtocolStatus res = SubGhzProtocolStatusError; do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - FURI_LOG_E(TAG, "Deserialize error"); - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_came_twee_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); + res = subghz_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + subghz_protocol_came_twee_const.min_count_bit_for_found); + if(res != SubGhzProtocolStatusOk) { break; } //optional parameter parameter @@ -262,8 +261,6 @@ bool subghz_protocol_encoder_came_twee_deserialize(void* context, FlipperFormat* subghz_protocol_came_twee_remote_controller(&instance->generic); subghz_protocol_encoder_came_twee_get_upload(instance); instance->encoder.is_running = true; - - res = true; } while(false); return res; @@ -419,7 +416,7 @@ uint8_t subghz_protocol_decoder_came_twee_get_hash_data(void* context) { &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } -bool subghz_protocol_decoder_came_twee_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_came_twee_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset) { @@ -428,22 +425,14 @@ bool subghz_protocol_decoder_came_twee_serialize( return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); } -bool subghz_protocol_decoder_came_twee_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_decoder_came_twee_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolDecoderCameTwee* instance = context; - bool ret = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_came_twee_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); - break; - } - ret = true; - } while(false); - return ret; + return subghz_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + subghz_protocol_came_twee_const.min_count_bit_for_found); } void subghz_protocol_decoder_came_twee_get_string(void* context, FuriString* output) { diff --git a/lib/subghz/protocols/came_twee.h b/lib/subghz/protocols/came_twee.h index 359b964da..f26f1e806 100644 --- a/lib/subghz/protocols/came_twee.h +++ b/lib/subghz/protocols/came_twee.h @@ -28,9 +28,10 @@ void subghz_protocol_encoder_came_twee_free(void* context); * Deserialize and generating an upload to send. * @param context Pointer to a SubGhzProtocolEncoderCameTwee instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_protocol_encoder_came_twee_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_encoder_came_twee_deserialize(void* context, FlipperFormat* flipper_format); /** * Forced transmission stop. @@ -84,9 +85,9 @@ uint8_t subghz_protocol_decoder_came_twee_get_hash_data(void* context); * @param context Pointer to a SubGhzProtocolDecoderCameTwee instance * @param flipper_format Pointer to a FlipperFormat instance * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success + * @return status */ -bool subghz_protocol_decoder_came_twee_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_came_twee_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); @@ -95,9 +96,10 @@ bool subghz_protocol_decoder_came_twee_serialize( * Deserialize data SubGhzProtocolDecoderCameTwee. * @param context Pointer to a SubGhzProtocolDecoderCameTwee instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_protocol_decoder_came_twee_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_decoder_came_twee_deserialize(void* context, FlipperFormat* flipper_format); /** * Getting a textual representation of the received data. diff --git a/lib/subghz/protocols/chamberlain_code.c b/lib/subghz/protocols/chamberlain_code.c index 9c8e5ee4a..be0877fb5 100644 --- a/lib/subghz/protocols/chamberlain_code.c +++ b/lib/subghz/protocols/chamberlain_code.c @@ -207,31 +207,35 @@ static bool return true; } -bool subghz_protocol_encoder_chamb_code_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_encoder_chamb_code_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolEncoderChamb_Code* instance = context; - bool res = false; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - FURI_LOG_E(TAG, "Deserialize error"); + ret = subghz_block_generic_deserialize(&instance->generic, flipper_format); + if(ret != SubGhzProtocolStatusOk) { break; } if(instance->generic.data_count_bit > subghz_protocol_chamb_code_const.min_count_bit_for_found) { FURI_LOG_E(TAG, "Wrong number of bits in key"); + ret = SubGhzProtocolStatusErrorValueBitCount; break; } //optional parameter parameter flipper_format_read_uint32( flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); - if(!subghz_protocol_encoder_chamb_code_get_upload(instance)) break; + if(!subghz_protocol_encoder_chamb_code_get_upload(instance)) { + ret = SubGhzProtocolStatusErrorEncoderGetUpload; + break; + } instance->encoder.is_running = true; - res = true; } while(false); - return res; + return ret; } void subghz_protocol_encoder_chamb_code_stop(void* context) { @@ -425,7 +429,7 @@ uint8_t subghz_protocol_decoder_chamb_code_get_hash_data(void* context) { &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } -bool subghz_protocol_decoder_chamb_code_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_chamb_code_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset) { @@ -434,20 +438,22 @@ bool subghz_protocol_decoder_chamb_code_serialize( return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); } -bool subghz_protocol_decoder_chamb_code_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_decoder_chamb_code_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolDecoderChamb_Code* instance = context; - bool ret = false; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + ret = subghz_block_generic_deserialize(&instance->generic, flipper_format); + if(ret != SubGhzProtocolStatusOk) { break; } if(instance->generic.data_count_bit > subghz_protocol_chamb_code_const.min_count_bit_for_found) { FURI_LOG_E(TAG, "Wrong number of bits in key"); + ret = SubGhzProtocolStatusErrorValueBitCount; break; } - ret = true; } while(false); return ret; } diff --git a/lib/subghz/protocols/chamberlain_code.h b/lib/subghz/protocols/chamberlain_code.h index f87b64d90..c8ffed5c5 100644 --- a/lib/subghz/protocols/chamberlain_code.h +++ b/lib/subghz/protocols/chamberlain_code.h @@ -28,9 +28,10 @@ void subghz_protocol_encoder_chamb_code_free(void* context); * Deserialize and generating an upload to send. * @param context Pointer to a SubGhzProtocolEncoderChamb_Code instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_protocol_encoder_chamb_code_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_encoder_chamb_code_deserialize(void* context, FlipperFormat* flipper_format); /** * Forced transmission stop. @@ -84,9 +85,9 @@ uint8_t subghz_protocol_decoder_chamb_code_get_hash_data(void* context); * @param context Pointer to a SubGhzProtocolDecoderChamb_Code instance * @param flipper_format Pointer to a FlipperFormat instance * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success + * @return status */ -bool subghz_protocol_decoder_chamb_code_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_chamb_code_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); @@ -95,9 +96,10 @@ bool subghz_protocol_decoder_chamb_code_serialize( * Deserialize data SubGhzProtocolDecoderChamb_Code. * @param context Pointer to a SubGhzProtocolDecoderChamb_Code instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_protocol_decoder_chamb_code_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_decoder_chamb_code_deserialize(void* context, FlipperFormat* flipper_format); /** * Getting a textual representation of the received data. diff --git a/lib/subghz/protocols/clemsa.c b/lib/subghz/protocols/clemsa.c index a2cb7a18b..a0547a113 100644 --- a/lib/subghz/protocols/clemsa.c +++ b/lib/subghz/protocols/clemsa.c @@ -155,31 +155,32 @@ static bool subghz_protocol_encoder_clemsa_get_upload(SubGhzProtocolEncoderClems return true; } -bool subghz_protocol_encoder_clemsa_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_encoder_clemsa_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolEncoderClemsa* instance = context; - bool res = false; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - FURI_LOG_E(TAG, "Deserialize error"); - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_clemsa_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); + ret = subghz_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + subghz_protocol_clemsa_const.min_count_bit_for_found); + if(ret != SubGhzProtocolStatusOk) { break; } //optional parameter parameter flipper_format_read_uint32( flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); - if(!subghz_protocol_encoder_clemsa_get_upload(instance)) break; + if(!subghz_protocol_encoder_clemsa_get_upload(instance)) { + ret = SubGhzProtocolStatusErrorEncoderGetUpload; + break; + } instance->encoder.is_running = true; - res = true; } while(false); - return res; + return ret; } void subghz_protocol_encoder_clemsa_stop(void* context) { @@ -316,7 +317,7 @@ uint8_t subghz_protocol_decoder_clemsa_get_hash_data(void* context) { &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } -bool subghz_protocol_decoder_clemsa_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_clemsa_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset) { @@ -325,22 +326,12 @@ bool subghz_protocol_decoder_clemsa_serialize( return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); } -bool subghz_protocol_decoder_clemsa_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_decoder_clemsa_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolDecoderClemsa* instance = context; - bool ret = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_clemsa_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); - break; - } - ret = true; - } while(false); - return ret; + return subghz_block_generic_deserialize_check_count_bit( + &instance->generic, flipper_format, subghz_protocol_clemsa_const.min_count_bit_for_found); } void subghz_protocol_decoder_clemsa_get_string(void* context, FuriString* output) { diff --git a/lib/subghz/protocols/clemsa.h b/lib/subghz/protocols/clemsa.h index 8858c1a3b..f14cd3dac 100644 --- a/lib/subghz/protocols/clemsa.h +++ b/lib/subghz/protocols/clemsa.h @@ -28,9 +28,10 @@ void subghz_protocol_encoder_clemsa_free(void* context); * Deserialize and generating an upload to send. * @param context Pointer to a SubGhzProtocolEncoderClemsa instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_protocol_encoder_clemsa_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_encoder_clemsa_deserialize(void* context, FlipperFormat* flipper_format); /** * Forced transmission stop. @@ -84,9 +85,9 @@ uint8_t subghz_protocol_decoder_clemsa_get_hash_data(void* context); * @param context Pointer to a SubGhzProtocolDecoderClemsa instance * @param flipper_format Pointer to a FlipperFormat instance * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success + * @return status */ -bool subghz_protocol_decoder_clemsa_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_clemsa_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); @@ -95,9 +96,10 @@ bool subghz_protocol_decoder_clemsa_serialize( * Deserialize data SubGhzProtocolDecoderClemsa. * @param context Pointer to a SubGhzProtocolDecoderClemsa instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_protocol_decoder_clemsa_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_decoder_clemsa_deserialize(void* context, FlipperFormat* flipper_format); /** * Getting a textual representation of the received data. diff --git a/lib/subghz/protocols/doitrand.c b/lib/subghz/protocols/doitrand.c index 6b31d4f27..69b8bba4a 100644 --- a/lib/subghz/protocols/doitrand.c +++ b/lib/subghz/protocols/doitrand.c @@ -136,31 +136,31 @@ static bool subghz_protocol_encoder_doitrand_get_upload(SubGhzProtocolEncoderDoi return true; } -bool subghz_protocol_encoder_doitrand_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_encoder_doitrand_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolEncoderDoitrand* instance = context; - bool res = false; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - FURI_LOG_E(TAG, "Deserialize error"); - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_doitrand_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); + ret = subghz_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + subghz_protocol_doitrand_const.min_count_bit_for_found); + if(ret != SubGhzProtocolStatusOk) { break; } //optional parameter parameter flipper_format_read_uint32( flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); - if(!subghz_protocol_encoder_doitrand_get_upload(instance)) break; + if(!subghz_protocol_encoder_doitrand_get_upload(instance)) { + ret = SubGhzProtocolStatusErrorEncoderGetUpload; + break; + } instance->encoder.is_running = true; - - res = true; } while(false); - return res; + return ret; } void subghz_protocol_encoder_doitrand_stop(void* context) { @@ -310,7 +310,7 @@ uint8_t subghz_protocol_decoder_doitrand_get_hash_data(void* context) { &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } -bool subghz_protocol_decoder_doitrand_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_doitrand_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset) { @@ -319,22 +319,14 @@ bool subghz_protocol_decoder_doitrand_serialize( return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); } -bool subghz_protocol_decoder_doitrand_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_decoder_doitrand_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolDecoderDoitrand* instance = context; - bool ret = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_doitrand_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); - break; - } - ret = true; - } while(false); - return ret; + return subghz_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + subghz_protocol_doitrand_const.min_count_bit_for_found); } void subghz_protocol_decoder_doitrand_get_string(void* context, FuriString* output) { diff --git a/lib/subghz/protocols/doitrand.h b/lib/subghz/protocols/doitrand.h index 30f1fffd0..5dbc6678f 100644 --- a/lib/subghz/protocols/doitrand.h +++ b/lib/subghz/protocols/doitrand.h @@ -28,9 +28,10 @@ void subghz_protocol_encoder_doitrand_free(void* context); * Deserialize and generating an upload to send. * @param context Pointer to a SubGhzProtocolEncoderDoitrand instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_protocol_encoder_doitrand_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_encoder_doitrand_deserialize(void* context, FlipperFormat* flipper_format); /** * Forced transmission stop. @@ -84,9 +85,9 @@ uint8_t subghz_protocol_decoder_doitrand_get_hash_data(void* context); * @param context Pointer to a SubGhzProtocolDecoderDoitrand instance * @param flipper_format Pointer to a FlipperFormat instance * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success + * @return status */ -bool subghz_protocol_decoder_doitrand_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_doitrand_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); @@ -95,9 +96,10 @@ bool subghz_protocol_decoder_doitrand_serialize( * Deserialize data SubGhzProtocolDecoderDoitrand. * @param context Pointer to a SubGhzProtocolDecoderDoitrand instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_protocol_decoder_doitrand_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_decoder_doitrand_deserialize(void* context, FlipperFormat* flipper_format); /** * Getting a textual representation of the received data. diff --git a/lib/subghz/protocols/dooya.c b/lib/subghz/protocols/dooya.c index c70b6d54e..47e95209e 100644 --- a/lib/subghz/protocols/dooya.c +++ b/lib/subghz/protocols/dooya.c @@ -146,31 +146,31 @@ static bool subghz_protocol_encoder_dooya_get_upload(SubGhzProtocolEncoderDooya* return true; } -bool subghz_protocol_encoder_dooya_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_encoder_dooya_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolEncoderDooya* instance = context; - bool res = false; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - FURI_LOG_E(TAG, "Deserialize error"); - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_dooya_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); + ret = subghz_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + subghz_protocol_dooya_const.min_count_bit_for_found); + if(ret != SubGhzProtocolStatusOk) { break; } //optional parameter parameter flipper_format_read_uint32( flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); - if(!subghz_protocol_encoder_dooya_get_upload(instance)) break; + if(!subghz_protocol_encoder_dooya_get_upload(instance)) { + ret = SubGhzProtocolStatusErrorEncoderGetUpload; + break; + } instance->encoder.is_running = true; - - res = true; } while(false); - return res; + return ret; } void subghz_protocol_encoder_dooya_stop(void* context) { @@ -354,7 +354,7 @@ uint8_t subghz_protocol_decoder_dooya_get_hash_data(void* context) { &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } -bool subghz_protocol_decoder_dooya_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_dooya_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset) { @@ -363,22 +363,12 @@ bool subghz_protocol_decoder_dooya_serialize( return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); } -bool subghz_protocol_decoder_dooya_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_decoder_dooya_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolDecoderDooya* instance = context; - bool ret = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_dooya_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); - break; - } - ret = true; - } while(false); - return ret; + return subghz_block_generic_deserialize_check_count_bit( + &instance->generic, flipper_format, subghz_protocol_dooya_const.min_count_bit_for_found); } /** diff --git a/lib/subghz/protocols/dooya.h b/lib/subghz/protocols/dooya.h index f0cf843c0..ffe9d41ef 100644 --- a/lib/subghz/protocols/dooya.h +++ b/lib/subghz/protocols/dooya.h @@ -28,9 +28,10 @@ void subghz_protocol_encoder_dooya_free(void* context); * Deserialize and generating an upload to send. * @param context Pointer to a SubGhzProtocolEncoderDooya instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_protocol_encoder_dooya_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_encoder_dooya_deserialize(void* context, FlipperFormat* flipper_format); /** * Forced transmission stop. @@ -84,9 +85,9 @@ uint8_t subghz_protocol_decoder_dooya_get_hash_data(void* context); * @param context Pointer to a SubGhzProtocolDecoderDooya instance * @param flipper_format Pointer to a FlipperFormat instance * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success + * @return status */ -bool subghz_protocol_decoder_dooya_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_dooya_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); @@ -95,9 +96,10 @@ bool subghz_protocol_decoder_dooya_serialize( * Deserialize data SubGhzProtocolDecoderDooya. * @param context Pointer to a SubGhzProtocolDecoderDooya instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_protocol_decoder_dooya_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_decoder_dooya_deserialize(void* context, FlipperFormat* flipper_format); /** * Getting a textual representation of the received data. diff --git a/lib/subghz/protocols/faac_slh.c b/lib/subghz/protocols/faac_slh.c index 7572bd8ab..c1c96b7a7 100644 --- a/lib/subghz/protocols/faac_slh.c +++ b/lib/subghz/protocols/faac_slh.c @@ -110,7 +110,7 @@ void subghz_protocol_encoder_faac_slh_free(void* context) { } static bool subghz_protocol_faac_slh_gen_data(SubGhzProtocolEncoderFaacSLH* instance) { - instance->generic.cnt++; + instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult(); uint32_t fix = instance->generic.serial << 4 | instance->generic.btn; uint32_t hop = 0; uint32_t decrypt = 0; @@ -169,7 +169,8 @@ bool subghz_protocol_faac_slh_create_data( instance->generic.data_count_bit = 64; bool res = subghz_protocol_faac_slh_gen_data(instance); if(res) { - res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + return SubGhzProtocolStatusOk == + subghz_block_generic_serialize(&instance->generic, flipper_format, preset); } return res; } @@ -217,12 +218,14 @@ static bool subghz_protocol_encoder_faac_slh_get_upload(SubGhzProtocolEncoderFaa return true; } -bool subghz_protocol_encoder_faac_slh_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_encoder_faac_slh_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolEncoderFaacSLH* instance = context; - bool res = false; + SubGhzProtocolStatus res = SubGhzProtocolStatusError; do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + if(SubGhzProtocolStatusOk != + subghz_block_generic_deserialize(&instance->generic, flipper_format)) { FURI_LOG_E(TAG, "Deserialize error"); break; } @@ -261,7 +264,7 @@ bool subghz_protocol_encoder_faac_slh_deserialize(void* context, FlipperFormat* instance->encoder.is_running = true; - res = true; + res = SubGhzProtocolStatusOk; } while(false); return res; @@ -422,22 +425,24 @@ uint8_t subghz_protocol_decoder_faac_slh_get_hash_data(void* context) { &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } -bool subghz_protocol_decoder_faac_slh_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_faac_slh_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderFaacSLH* instance = context; - bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + SubGhzProtocolStatus res = + subghz_block_generic_serialize(&instance->generic, flipper_format, preset); uint8_t seed_data[sizeof(uint32_t)] = {0}; for(size_t i = 0; i < sizeof(uint32_t); i++) { seed_data[sizeof(uint32_t) - i - 1] = (instance->generic.seed >> i * 8) & 0xFF; } - if(res && !flipper_format_write_hex(flipper_format, "Seed", seed_data, sizeof(uint32_t))) { + if((res == SubGhzProtocolStatusOk) && + !flipper_format_write_hex(flipper_format, "Seed", seed_data, sizeof(uint32_t))) { FURI_LOG_E(TAG, "Unable to add Seed"); - res = false; + res = SubGhzProtocolStatusError; } instance->generic.seed = seed_data[0] << 24 | seed_data[1] << 16 | seed_data[2] << 8 | seed_data[3]; @@ -448,12 +453,14 @@ bool subghz_protocol_decoder_faac_slh_serialize( return res; } -bool subghz_protocol_decoder_faac_slh_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_decoder_faac_slh_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolDecoderFaacSLH* instance = context; - bool res = false; + SubGhzProtocolStatus res = SubGhzProtocolStatusError; do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + if(SubGhzProtocolStatusOk != + subghz_block_generic_deserialize(&instance->generic, flipper_format)) { FURI_LOG_E(TAG, "Deserialize error"); break; } @@ -478,7 +485,7 @@ bool subghz_protocol_decoder_faac_slh_deserialize(void* context, FlipperFormat* FURI_LOG_E(TAG, "Rewind error"); break; } - res = true; + res = SubGhzProtocolStatusOk; } while(false); return res; diff --git a/lib/subghz/protocols/faac_slh.h b/lib/subghz/protocols/faac_slh.h index 9390da43a..bab042ca6 100644 --- a/lib/subghz/protocols/faac_slh.h +++ b/lib/subghz/protocols/faac_slh.h @@ -52,7 +52,8 @@ bool subghz_protocol_faac_slh_create_data( * @param flipper_format Pointer to a FlipperFormat instance * @return true On success */ -bool subghz_protocol_encoder_faac_slh_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_encoder_faac_slh_deserialize(void* context, FlipperFormat* flipper_format); /** * Forced transmission stop. @@ -106,9 +107,9 @@ uint8_t subghz_protocol_decoder_faac_slh_get_hash_data(void* context); * @param context Pointer to a SubGhzProtocolDecoderFaacSLH instance * @param flipper_format Pointer to a FlipperFormat instance * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success + * @return status */ -bool subghz_protocol_decoder_faac_slh_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_faac_slh_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); @@ -117,9 +118,10 @@ bool subghz_protocol_decoder_faac_slh_serialize( * Deserialize data SubGhzProtocolDecoderFaacSLH. * @param context Pointer to a SubGhzProtocolDecoderFaacSLH instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_protocol_decoder_faac_slh_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_decoder_faac_slh_deserialize(void* context, FlipperFormat* flipper_format); /** * Getting a textual representation of the received data. diff --git a/lib/subghz/protocols/gate_tx.c b/lib/subghz/protocols/gate_tx.c index 4c7c2d484..51a424fed 100644 --- a/lib/subghz/protocols/gate_tx.c +++ b/lib/subghz/protocols/gate_tx.c @@ -129,31 +129,31 @@ static bool subghz_protocol_encoder_gate_tx_get_upload(SubGhzProtocolEncoderGate return true; } -bool subghz_protocol_encoder_gate_tx_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_encoder_gate_tx_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolEncoderGateTx* instance = context; - bool res = false; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - FURI_LOG_E(TAG, "Deserialize error"); - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_gate_tx_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); + ret = subghz_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + subghz_protocol_gate_tx_const.min_count_bit_for_found); + if(ret != SubGhzProtocolStatusOk) { break; } //optional parameter parameter flipper_format_read_uint32( flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); - if(!subghz_protocol_encoder_gate_tx_get_upload(instance)) break; + if(!subghz_protocol_encoder_gate_tx_get_upload(instance)) { + ret = SubGhzProtocolStatusErrorEncoderGetUpload; + break; + } instance->encoder.is_running = true; - - res = true; } while(false); - return res; + return ret; } void subghz_protocol_encoder_gate_tx_stop(void* context) { @@ -290,7 +290,7 @@ uint8_t subghz_protocol_decoder_gate_tx_get_hash_data(void* context) { &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } -bool subghz_protocol_decoder_gate_tx_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_gate_tx_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset) { @@ -299,22 +299,12 @@ bool subghz_protocol_decoder_gate_tx_serialize( return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); } -bool subghz_protocol_decoder_gate_tx_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_decoder_gate_tx_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolDecoderGateTx* instance = context; - bool ret = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_gate_tx_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); - break; - } - ret = true; - } while(false); - return ret; + return subghz_block_generic_deserialize_check_count_bit( + &instance->generic, flipper_format, subghz_protocol_gate_tx_const.min_count_bit_for_found); } void subghz_protocol_decoder_gate_tx_get_string(void* context, FuriString* output) { diff --git a/lib/subghz/protocols/gate_tx.h b/lib/subghz/protocols/gate_tx.h index 4bfba3597..a6abede0d 100644 --- a/lib/subghz/protocols/gate_tx.h +++ b/lib/subghz/protocols/gate_tx.h @@ -28,9 +28,10 @@ void subghz_protocol_encoder_gate_tx_free(void* context); * Deserialize and generating an upload to send. * @param context Pointer to a SubGhzProtocolEncoderGateTx instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_protocol_encoder_gate_tx_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_encoder_gate_tx_deserialize(void* context, FlipperFormat* flipper_format); /** * Forced transmission stop. @@ -84,9 +85,9 @@ uint8_t subghz_protocol_decoder_gate_tx_get_hash_data(void* context); * @param context Pointer to a SubGhzProtocolDecoderGateTx instance * @param flipper_format Pointer to a FlipperFormat instance * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success + * @return status */ -bool subghz_protocol_decoder_gate_tx_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_gate_tx_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); @@ -95,9 +96,10 @@ bool subghz_protocol_decoder_gate_tx_serialize( * Deserialize data SubGhzProtocolDecoderGateTx. * @param context Pointer to a SubGhzProtocolDecoderGateTx instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_protocol_decoder_gate_tx_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_decoder_gate_tx_deserialize(void* context, FlipperFormat* flipper_format); /** * Getting a textual representation of the received data. diff --git a/lib/subghz/protocols/holtek.c b/lib/subghz/protocols/holtek.c index 8aaad3b71..294bd124d 100644 --- a/lib/subghz/protocols/holtek.c +++ b/lib/subghz/protocols/holtek.c @@ -142,31 +142,31 @@ static bool subghz_protocol_encoder_holtek_get_upload(SubGhzProtocolEncoderHolte return true; } -bool subghz_protocol_encoder_holtek_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_encoder_holtek_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolEncoderHoltek* instance = context; - bool res = false; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - FURI_LOG_E(TAG, "Deserialize error"); - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_holtek_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); + ret = subghz_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + subghz_protocol_holtek_const.min_count_bit_for_found); + if(ret != SubGhzProtocolStatusOk) { break; } //optional parameter parameter flipper_format_read_uint32( flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); - if(!subghz_protocol_encoder_holtek_get_upload(instance)) break; + if(!subghz_protocol_encoder_holtek_get_upload(instance)) { + ret = SubGhzProtocolStatusErrorEncoderGetUpload; + break; + } instance->encoder.is_running = true; - - res = true; } while(false); - return res; + return ret; } void subghz_protocol_encoder_holtek_stop(void* context) { @@ -322,7 +322,7 @@ uint8_t subghz_protocol_decoder_holtek_get_hash_data(void* context) { &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } -bool subghz_protocol_decoder_holtek_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_holtek_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset) { @@ -331,22 +331,12 @@ bool subghz_protocol_decoder_holtek_serialize( return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); } -bool subghz_protocol_decoder_holtek_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_decoder_holtek_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolDecoderHoltek* 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_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); - break; - } - ret = true; - } while(false); - return ret; + return subghz_block_generic_deserialize_check_count_bit( + &instance->generic, flipper_format, subghz_protocol_holtek_const.min_count_bit_for_found); } void subghz_protocol_decoder_holtek_get_string(void* context, FuriString* output) { diff --git a/lib/subghz/protocols/holtek.h b/lib/subghz/protocols/holtek.h index 252a26dc7..19081308d 100644 --- a/lib/subghz/protocols/holtek.h +++ b/lib/subghz/protocols/holtek.h @@ -28,9 +28,10 @@ void subghz_protocol_encoder_holtek_free(void* context); * Deserialize and generating an upload to send. * @param context Pointer to a SubGhzProtocolEncoderHoltek instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_protocol_encoder_holtek_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_encoder_holtek_deserialize(void* context, FlipperFormat* flipper_format); /** * Forced transmission stop. @@ -84,9 +85,9 @@ uint8_t subghz_protocol_decoder_holtek_get_hash_data(void* context); * @param context Pointer to a SubGhzProtocolDecoderHoltek instance * @param flipper_format Pointer to a FlipperFormat instance * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success + * @return status */ -bool subghz_protocol_decoder_holtek_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_holtek_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); @@ -95,9 +96,10 @@ bool subghz_protocol_decoder_holtek_serialize( * Deserialize data SubGhzProtocolDecoderHoltek. * @param context Pointer to a SubGhzProtocolDecoderHoltek instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_protocol_decoder_holtek_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_decoder_holtek_deserialize(void* context, FlipperFormat* flipper_format); /** * Getting a textual representation of the received data. diff --git a/lib/subghz/protocols/holtek_ht12x.c b/lib/subghz/protocols/holtek_ht12x.c index 169387ded..831f824dd 100644 --- a/lib/subghz/protocols/holtek_ht12x.c +++ b/lib/subghz/protocols/holtek_ht12x.c @@ -147,39 +147,41 @@ static bool return true; } -bool subghz_protocol_encoder_holtek_th12x_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_encoder_holtek_th12x_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolEncoderHoltek_HT12X* instance = context; - bool res = false; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - FURI_LOG_E(TAG, "Deserialize error"); + ret = subghz_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + subghz_protocol_holtek_th12x_const.min_count_bit_for_found); + if(ret != SubGhzProtocolStatusOk) { break; } if(!flipper_format_rewind(flipper_format)) { FURI_LOG_E(TAG, "Rewind error"); + ret = SubGhzProtocolStatusErrorParserOthers; 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"); + ret = SubGhzProtocolStatusErrorParserTe; 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; + if(!subghz_protocol_encoder_holtek_th12x_get_upload(instance)) { + ret = SubGhzProtocolStatusErrorEncoderGetUpload; + break; + } instance->encoder.is_running = true; - - res = true; } while(false); - return res; + return ret; } void subghz_protocol_encoder_holtek_th12x_stop(void* context) { @@ -327,42 +329,45 @@ uint8_t subghz_protocol_decoder_holtek_th12x_get_hash_data(void* context) { &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } -bool subghz_protocol_decoder_holtek_th12x_serialize( +SubGhzProtocolStatus 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)) { + SubGhzProtocolStatus ret = + subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + if((ret == SubGhzProtocolStatusOk) && + !flipper_format_write_uint32(flipper_format, "TE", &instance->te, 1)) { FURI_LOG_E(TAG, "Unable to add TE"); - res = false; + ret = SubGhzProtocolStatusErrorParserTe; } - return res; + return ret; } -bool subghz_protocol_decoder_holtek_th12x_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_decoder_holtek_th12x_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolDecoderHoltek_HT12X* instance = context; - bool ret = false; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; 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"); + ret = subghz_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + subghz_protocol_holtek_th12x_const.min_count_bit_for_found); + if(ret != SubGhzProtocolStatusOk) { break; } if(!flipper_format_rewind(flipper_format)) { FURI_LOG_E(TAG, "Rewind error"); + ret = SubGhzProtocolStatusErrorParserOthers; break; } if(!flipper_format_read_uint32(flipper_format, "TE", (uint32_t*)&instance->te, 1)) { FURI_LOG_E(TAG, "Missing TE"); + ret = SubGhzProtocolStatusErrorParserTe; break; } - ret = true; } while(false); return ret; } diff --git a/lib/subghz/protocols/holtek_ht12x.h b/lib/subghz/protocols/holtek_ht12x.h index 7b5c31dd7..500c061aa 100644 --- a/lib/subghz/protocols/holtek_ht12x.h +++ b/lib/subghz/protocols/holtek_ht12x.h @@ -28,9 +28,10 @@ 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 + * @return status */ -bool subghz_protocol_encoder_holtek_th12x_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_encoder_holtek_th12x_deserialize(void* context, FlipperFormat* flipper_format); /** * Forced transmission stop. @@ -84,9 +85,9 @@ uint8_t subghz_protocol_decoder_holtek_th12x_get_hash_data(void* context); * @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 + * @return status */ -bool subghz_protocol_decoder_holtek_th12x_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_holtek_th12x_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); @@ -95,9 +96,10 @@ bool subghz_protocol_decoder_holtek_th12x_serialize( * Deserialize data SubGhzProtocolDecoderHoltek_HT12X. * @param context Pointer to a SubGhzProtocolDecoderHoltek_HT12X instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_protocol_decoder_holtek_th12x_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_decoder_holtek_th12x_deserialize(void* context, FlipperFormat* flipper_format); /** * Getting a textual representation of the received data. diff --git a/lib/subghz/protocols/honeywell_wdb.c b/lib/subghz/protocols/honeywell_wdb.c index 3b940fc67..7fd8d66d6 100644 --- a/lib/subghz/protocols/honeywell_wdb.c +++ b/lib/subghz/protocols/honeywell_wdb.c @@ -142,33 +142,32 @@ static bool subghz_protocol_encoder_honeywell_wdb_get_upload( return true; } -bool subghz_protocol_encoder_honeywell_wdb_deserialize( +SubGhzProtocolStatus subghz_protocol_encoder_honeywell_wdb_deserialize( void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolEncoderHoneywell_WDB* instance = context; - bool res = false; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - FURI_LOG_E(TAG, "Deserialize error"); - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_honeywell_wdb_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); + ret = subghz_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + subghz_protocol_honeywell_wdb_const.min_count_bit_for_found); + if(ret != SubGhzProtocolStatusOk) { break; } //optional parameter parameter flipper_format_read_uint32( flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); - if(!subghz_protocol_encoder_honeywell_wdb_get_upload(instance)) break; + if(!subghz_protocol_encoder_honeywell_wdb_get_upload(instance)) { + ret = SubGhzProtocolStatusErrorEncoderGetUpload; + break; + } instance->encoder.is_running = true; - - res = true; } while(false); - return res; + return ret; } void subghz_protocol_encoder_honeywell_wdb_stop(void* context) { @@ -345,7 +344,7 @@ uint8_t subghz_protocol_decoder_honeywell_wdb_get_hash_data(void* context) { &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } -bool subghz_protocol_decoder_honeywell_wdb_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_honeywell_wdb_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset) { @@ -354,24 +353,15 @@ bool subghz_protocol_decoder_honeywell_wdb_serialize( return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); } -bool subghz_protocol_decoder_honeywell_wdb_deserialize( +SubGhzProtocolStatus subghz_protocol_decoder_honeywell_wdb_deserialize( void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolDecoderHoneywell_WDB* instance = context; - bool ret = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_honeywell_wdb_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); - break; - } - ret = true; - } while(false); - return ret; + return subghz_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + subghz_protocol_honeywell_wdb_const.min_count_bit_for_found); } void subghz_protocol_decoder_honeywell_wdb_get_string(void* context, FuriString* output) { diff --git a/lib/subghz/protocols/honeywell_wdb.h b/lib/subghz/protocols/honeywell_wdb.h index 828631837..91728691b 100644 --- a/lib/subghz/protocols/honeywell_wdb.h +++ b/lib/subghz/protocols/honeywell_wdb.h @@ -28,11 +28,10 @@ void subghz_protocol_encoder_honeywell_wdb_free(void* context); * Deserialize and generating an upload to send. * @param context Pointer to a SubGhzProtocolEncoderHoneywell_WDB instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_protocol_encoder_honeywell_wdb_deserialize( - void* context, - FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_encoder_honeywell_wdb_deserialize(void* context, FlipperFormat* flipper_format); /** * Forced transmission stop. @@ -86,9 +85,9 @@ uint8_t subghz_protocol_decoder_honeywell_wdb_get_hash_data(void* context); * @param context Pointer to a SubGhzProtocolDecoderHoneywell_WDB instance * @param flipper_format Pointer to a FlipperFormat instance * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success + * @return status */ -bool subghz_protocol_decoder_honeywell_wdb_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_honeywell_wdb_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); @@ -97,11 +96,10 @@ bool subghz_protocol_decoder_honeywell_wdb_serialize( * Deserialize data SubGhzProtocolDecoderHoneywell_WDB. * @param context Pointer to a SubGhzProtocolDecoderHoneywell_WDB instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_protocol_decoder_honeywell_wdb_deserialize( - void* context, - FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_decoder_honeywell_wdb_deserialize(void* context, FlipperFormat* flipper_format); /** * Getting a textual representation of the received data. diff --git a/lib/subghz/protocols/hormann.c b/lib/subghz/protocols/hormann.c index 67b8cdfca..4c5c68cc4 100644 --- a/lib/subghz/protocols/hormann.c +++ b/lib/subghz/protocols/hormann.c @@ -140,31 +140,31 @@ static bool subghz_protocol_encoder_hormann_get_upload(SubGhzProtocolEncoderHorm return true; } -bool subghz_protocol_encoder_hormann_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_encoder_hormann_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolEncoderHormann* instance = context; - bool res = false; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - FURI_LOG_E(TAG, "Deserialize error"); - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_hormann_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); + ret = subghz_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + subghz_protocol_hormann_const.min_count_bit_for_found); + if(ret != SubGhzProtocolStatusOk) { break; } //optional parameter parameter flipper_format_read_uint32( flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); - if(!subghz_protocol_encoder_hormann_get_upload(instance)) break; + if(!subghz_protocol_encoder_hormann_get_upload(instance)) { + ret = SubGhzProtocolStatusErrorEncoderGetUpload; + break; + } instance->encoder.is_running = true; - - res = true; } while(false); - return res; + return ret; } void subghz_protocol_encoder_hormann_stop(void* context) { @@ -295,7 +295,7 @@ uint8_t subghz_protocol_decoder_hormann_get_hash_data(void* context) { &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } -bool subghz_protocol_decoder_hormann_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_hormann_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset) { @@ -304,22 +304,12 @@ bool subghz_protocol_decoder_hormann_serialize( return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); } -bool subghz_protocol_decoder_hormann_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_decoder_hormann_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolDecoderHormann* instance = context; - bool ret = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_hormann_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); - break; - } - ret = true; - } while(false); - return ret; + return subghz_block_generic_deserialize_check_count_bit( + &instance->generic, flipper_format, subghz_protocol_hormann_const.min_count_bit_for_found); } void subghz_protocol_decoder_hormann_get_string(void* context, FuriString* output) { diff --git a/lib/subghz/protocols/hormann.h b/lib/subghz/protocols/hormann.h index 857a50041..8cb45aec3 100644 --- a/lib/subghz/protocols/hormann.h +++ b/lib/subghz/protocols/hormann.h @@ -28,9 +28,10 @@ void subghz_protocol_encoder_hormann_free(void* context); * Deserialize and generating an upload to send. * @param context Pointer to a SubGhzProtocolEncoderHormann instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_protocol_encoder_hormann_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_encoder_hormann_deserialize(void* context, FlipperFormat* flipper_format); /** * Forced transmission stop. @@ -84,9 +85,9 @@ uint8_t subghz_protocol_decoder_hormann_get_hash_data(void* context); * @param context Pointer to a SubGhzProtocolDecoderHormann instance * @param flipper_format Pointer to a FlipperFormat instance * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success + * @return status */ -bool subghz_protocol_decoder_hormann_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_hormann_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); @@ -95,9 +96,10 @@ bool subghz_protocol_decoder_hormann_serialize( * Deserialize data SubGhzProtocolDecoderHormann. * @param context Pointer to a SubGhzProtocolDecoderHormann instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_protocol_decoder_hormann_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_decoder_hormann_deserialize(void* context, FlipperFormat* flipper_format); /** * Getting a textual representation of the received data. diff --git a/lib/subghz/protocols/ido.c b/lib/subghz/protocols/ido.c index dff9defc0..70870dd93 100644 --- a/lib/subghz/protocols/ido.c +++ b/lib/subghz/protocols/ido.c @@ -180,7 +180,7 @@ uint8_t subghz_protocol_decoder_ido_get_hash_data(void* context) { &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } -bool subghz_protocol_decoder_ido_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_ido_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset) { @@ -189,21 +189,12 @@ bool subghz_protocol_decoder_ido_serialize( return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); } -bool subghz_protocol_decoder_ido_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_decoder_ido_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolDecoderIDo* instance = context; - bool ret = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit != subghz_protocol_ido_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); - break; - } - ret = true; - } while(false); - return ret; + return subghz_block_generic_deserialize_check_count_bit( + &instance->generic, flipper_format, subghz_protocol_ido_const.min_count_bit_for_found); } void subghz_protocol_decoder_ido_get_string(void* context, FuriString* output) { diff --git a/lib/subghz/protocols/ido.h b/lib/subghz/protocols/ido.h index 634f6ff89..949320246 100644 --- a/lib/subghz/protocols/ido.h +++ b/lib/subghz/protocols/ido.h @@ -50,9 +50,9 @@ uint8_t subghz_protocol_decoder_ido_get_hash_data(void* context); * @param context Pointer to a SubGhzProtocolDecoderIDo instance * @param flipper_format Pointer to a FlipperFormat instance * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success + * @return status */ -bool subghz_protocol_decoder_ido_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_ido_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); @@ -61,9 +61,10 @@ bool subghz_protocol_decoder_ido_serialize( * Deserialize data SubGhzProtocolDecoderIDo. * @param context Pointer to a SubGhzProtocolDecoderIDo instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_protocol_decoder_ido_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_decoder_ido_deserialize(void* context, FlipperFormat* flipper_format); /** * Getting a textual representation of the received data. diff --git a/lib/subghz/protocols/intertechno_v3.c b/lib/subghz/protocols/intertechno_v3.c index 2c4e514cc..7fe952995 100644 --- a/lib/subghz/protocols/intertechno_v3.c +++ b/lib/subghz/protocols/intertechno_v3.c @@ -158,34 +158,36 @@ static bool subghz_protocol_encoder_intertechno_v3_get_upload( return true; } -bool subghz_protocol_encoder_intertechno_v3_deserialize( +SubGhzProtocolStatus subghz_protocol_encoder_intertechno_v3_deserialize( void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolEncoderIntertechno_V3* instance = context; - bool res = false; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - FURI_LOG_E(TAG, "Deserialize error"); + ret = subghz_block_generic_deserialize(&instance->generic, flipper_format); + if(ret != SubGhzProtocolStatusOk) { break; } if((instance->generic.data_count_bit != subghz_protocol_intertechno_v3_const.min_count_bit_for_found) && (instance->generic.data_count_bit != INTERTECHNO_V3_DIMMING_COUNT_BIT)) { FURI_LOG_E(TAG, "Wrong number of bits in key"); + ret = SubGhzProtocolStatusErrorValueBitCount; break; } //optional parameter parameter flipper_format_read_uint32( flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); - if(!subghz_protocol_encoder_intertechno_v3_get_upload(instance)) break; + if(!subghz_protocol_encoder_intertechno_v3_get_upload(instance)) { + ret = SubGhzProtocolStatusErrorEncoderGetUpload; + break; + } instance->encoder.is_running = true; - - res = true; } while(false); - return res; + return ret; } void subghz_protocol_encoder_intertechno_v3_stop(void* context) { @@ -404,7 +406,7 @@ uint8_t subghz_protocol_decoder_intertechno_v3_get_hash_data(void* context) { &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } -bool subghz_protocol_decoder_intertechno_v3_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_intertechno_v3_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset) { @@ -413,23 +415,24 @@ bool subghz_protocol_decoder_intertechno_v3_serialize( return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); } -bool subghz_protocol_decoder_intertechno_v3_deserialize( +SubGhzProtocolStatus subghz_protocol_decoder_intertechno_v3_deserialize( void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolDecoderIntertechno_V3* instance = context; - bool ret = false; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + ret = subghz_block_generic_deserialize(&instance->generic, flipper_format); + if(ret != SubGhzProtocolStatusOk) { break; } if((instance->generic.data_count_bit != subghz_protocol_intertechno_v3_const.min_count_bit_for_found) && (instance->generic.data_count_bit != INTERTECHNO_V3_DIMMING_COUNT_BIT)) { FURI_LOG_E(TAG, "Wrong number of bits in key"); + ret = SubGhzProtocolStatusErrorValueBitCount; break; } - ret = true; } while(false); return ret; } diff --git a/lib/subghz/protocols/intertechno_v3.h b/lib/subghz/protocols/intertechno_v3.h index ffee14b04..4d1c24cb6 100644 --- a/lib/subghz/protocols/intertechno_v3.h +++ b/lib/subghz/protocols/intertechno_v3.h @@ -28,9 +28,9 @@ void subghz_protocol_encoder_intertechno_v3_free(void* context); * Deserialize and generating an upload to send. * @param context Pointer to a SubGhzProtocolEncoderIntertechno_V3 instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return Starus error */ -bool subghz_protocol_encoder_intertechno_v3_deserialize( +SubGhzProtocolStatus subghz_protocol_encoder_intertechno_v3_deserialize( void* context, FlipperFormat* flipper_format); @@ -86,9 +86,9 @@ uint8_t subghz_protocol_decoder_intertechno_v3_get_hash_data(void* context); * @param context Pointer to a SubGhzProtocolDecoderIntertechno_V3 instance * @param flipper_format Pointer to a FlipperFormat instance * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success + * @return Starus error */ -bool subghz_protocol_decoder_intertechno_v3_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_intertechno_v3_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); @@ -97,9 +97,9 @@ bool subghz_protocol_decoder_intertechno_v3_serialize( * Deserialize data SubGhzProtocolDecoderIntertechno_V3. * @param context Pointer to a SubGhzProtocolDecoderIntertechno_V3 instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return Starus error */ -bool subghz_protocol_decoder_intertechno_v3_deserialize( +SubGhzProtocolStatus subghz_protocol_decoder_intertechno_v3_deserialize( void* context, FlipperFormat* flipper_format); diff --git a/lib/subghz/protocols/keeloq.c b/lib/subghz/protocols/keeloq.c index a0970de4d..4bd978c8f 100644 --- a/lib/subghz/protocols/keeloq.c +++ b/lib/subghz/protocols/keeloq.c @@ -85,7 +85,29 @@ const SubGhzProtocol subghz_protocol_keeloq = { }; static const char* mfname; -static int kl_type; +static uint8_t kl_type; +static uint8_t btn_temp_id; +static uint8_t btn_temp_id_original; +static bool bft_prog_mode; +static uint16_t temp_counter; + +void keeloq_set_btn(uint8_t b) { + btn_temp_id = b; +} + +uint8_t keeloq_get_original_btn() { + return btn_temp_id_original; +} + +uint8_t keeloq_get_custom_btn() { + return btn_temp_id; +} + +void keeloq_reset_original_btn() { + btn_temp_id_original = 0; + temp_counter = 0; + bft_prog_mode = false; +} void keeloq_reset_mfname() { mfname = ""; @@ -136,17 +158,11 @@ void subghz_protocol_encoder_keeloq_free(void* context) { * @param instance Pointer to a SubGhzProtocolEncoderKeeloq* instance * @param btn Button number, 4 bit */ -static bool subghz_protocol_keeloq_gen_data(SubGhzProtocolEncoderKeeloq* instance, uint8_t btn) { - if(instance->generic.cnt < 0xFFFF) { - instance->generic.cnt++; - } else if(instance->generic.cnt >= 0xFFFF) { - instance->generic.cnt = 0; - } +static bool subghz_protocol_keeloq_gen_data( + SubGhzProtocolEncoderKeeloq* instance, + uint8_t btn, + bool counter_up) { 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; uint32_t hop = 0; uint64_t man = 0; uint64_t code_found_reverse; @@ -155,31 +171,63 @@ static bool subghz_protocol_keeloq_gen_data(SubGhzProtocolEncoderKeeloq* instanc instance->manufacture_name = ""; } - // DTM Neo uses 12bit -> simple learning -- FAAC_RC,XT , Mutanco_Mutancode -> 12bit normal learning - if((strcmp(instance->manufacture_name, "DTM_Neo") == 0) || - (strcmp(instance->manufacture_name, "FAAC_RC,XT") == 0) || - (strcmp(instance->manufacture_name, "Mutanco_Mutancode") == 0)) { - decrypt = btn << 28 | (instance->generic.serial & 0xFFF) << 16 | instance->generic.cnt; + // BFT programming mode on / off conditions + if((strcmp(instance->manufacture_name, "BFT") == 0) && (btn == 0xF)) { + bft_prog_mode = true; } - - // Nice Smilo, MHouse, JCM, Normstahl -> 8bit serial - simple learning - if((strcmp(instance->manufacture_name, "NICE_Smilo") == 0) || - (strcmp(instance->manufacture_name, "NICE_MHOUSE") == 0) || - (strcmp(instance->manufacture_name, "JCM_Tech") == 0) || - (strcmp(instance->manufacture_name, "Normstahl") == 0)) { - decrypt = btn << 28 | (instance->generic.serial & 0xFF) << 16 | instance->generic.cnt; + if((strcmp(instance->manufacture_name, "BFT") == 0) && (btn != 0xF) && bft_prog_mode) { + bft_prog_mode = false; } + // If we using BFT programming mode we will trasmit its seed in hop part like original remote + if(bft_prog_mode) { + hop = instance->generic.seed; + } + if(counter_up && !bft_prog_mode) { + if(instance->generic.cnt < 0xFFFF) { + if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) >= 0xFFFF) { + instance->generic.cnt = 0; + } else { + instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult(); + } + } else if(instance->generic.cnt >= 0xFFFF) { + instance->generic.cnt = 0; + } + } + if(!bft_prog_mode) { + uint32_t decrypt = (uint32_t)btn << 28 | + (instance->generic.serial & 0x3FF) + << 16 | //ToDo in some protocols the discriminator is 0 + instance->generic.cnt; + // DTM Neo uses 12bit -> simple learning -- FAAC_RC,XT , Mutanco_Mutancode -> 12bit normal learning + if((strcmp(instance->manufacture_name, "DTM_Neo") == 0) || + (strcmp(instance->manufacture_name, "FAAC_RC,XT") == 0) || + (strcmp(instance->manufacture_name, "Mutanco_Mutancode") == 0)) { + decrypt = btn << 28 | (instance->generic.serial & 0xFFF) << 16 | instance->generic.cnt; + } - if(strcmp(instance->manufacture_name, "Unknown") == 0) { - code_found_reverse = subghz_protocol_blocks_reverse_key( - instance->generic.data, instance->generic.data_count_bit); - hop = code_found_reverse & 0x00000000ffffffff; - } else if(strcmp(instance->manufacture_name, "AN-Motors") == 0) { - hop = (instance->generic.cnt & 0xFF) << 24 | (instance->generic.cnt & 0xFF) << 16 | - (instance->generic.btn & 0xF) << 12 | 0x404; - } else if(strcmp(instance->manufacture_name, "HCS101") == 0) { - hop = instance->generic.cnt << 16 | (instance->generic.btn & 0xF) << 12 | 0x000; - } else { + // Nice Smilo, MHouse, JCM, Normstahl -> 8bit serial - simple learning + if((strcmp(instance->manufacture_name, "NICE_Smilo") == 0) || + (strcmp(instance->manufacture_name, "NICE_MHOUSE") == 0) || + (strcmp(instance->manufacture_name, "JCM_Tech") == 0) || + (strcmp(instance->manufacture_name, "Normstahl") == 0)) { + decrypt = btn << 28 | (instance->generic.serial & 0xFF) << 16 | instance->generic.cnt; + } + + // Beninca -> 4bit serial - simple XOR + if(strcmp(instance->manufacture_name, "Beninca") == 0) { + decrypt = btn << 28 | (instance->generic.serial & 0xF) << 16 | instance->generic.cnt; + } + + if(strcmp(instance->manufacture_name, "Unknown") == 0) { + code_found_reverse = subghz_protocol_blocks_reverse_key( + instance->generic.data, instance->generic.data_count_bit); + hop = code_found_reverse & 0x00000000ffffffff; + } else if(strcmp(instance->manufacture_name, "AN-Motors") == 0) { + hop = (instance->generic.cnt & 0xFF) << 24 | (instance->generic.cnt & 0xFF) << 16 | + (btn & 0xF) << 12 | 0x404; + } else if(strcmp(instance->manufacture_name, "HCS101") == 0) { + hop = instance->generic.cnt << 16 | (btn & 0xF) << 12 | 0x000; + } else { for M_EACH(manufacture_code, *subghz_keystore_get_data(instance->keystore), SubGhzKeyArray_t) { res = strcmp(furi_string_get_cstr(manufacture_code->name), instance->manufacture_name); @@ -238,6 +286,7 @@ static bool subghz_protocol_keeloq_gen_data(SubGhzProtocolEncoderKeeloq* instanc break; } } + } } if(hop) { uint64_t yek = (uint64_t)fix << 32 | hop; @@ -261,9 +310,10 @@ bool subghz_protocol_keeloq_create_data( instance->generic.cnt = cnt; instance->manufacture_name = manufacture_name; instance->generic.data_count_bit = 64; - bool res = subghz_protocol_keeloq_gen_data(instance, btn); + bool res = subghz_protocol_keeloq_gen_data(instance, btn, false); if(res) { - res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + return SubGhzProtocolStatusOk == + subghz_block_generic_serialize(&instance->generic, flipper_format, preset); } return res; } @@ -286,9 +336,10 @@ bool subghz_protocol_keeloq_bft_create_data( instance->manufacture_name = manufacture_name; instance->generic.data_count_bit = 64; // roguuemaster don't steal.!!!! - bool res = subghz_protocol_keeloq_gen_data(instance, btn); + bool res = subghz_protocol_keeloq_gen_data(instance, btn, false); if(res) { - res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + return SubGhzProtocolStatusOk == + subghz_block_generic_serialize(&instance->generic, flipper_format, preset); } return res; } @@ -302,9 +353,136 @@ static bool subghz_protocol_encoder_keeloq_get_upload(SubGhzProtocolEncoderKeeloq* instance, uint8_t btn) { furi_assert(instance); - //gen new key - if(subghz_protocol_keeloq_gen_data(instance, btn)) { - //ToDo if you need to add a callback to automatically update the data on the display + // Save original button + if(btn_temp_id_original == 0) { + btn_temp_id_original = btn; + } + + if(instance->manufacture_name == 0x0) { + instance->manufacture_name = ""; + } + if(bft_prog_mode) { + instance->manufacture_name = "BFT"; + } + uint8_t klq_last_custom_btn = 0xA; + if(strcmp(instance->manufacture_name, "BFT") == 0) { + klq_last_custom_btn = 0xF; + } + + // Set custom button + if(btn_temp_id == 1) { + switch(btn_temp_id_original) { + case 0x1: + btn = 0x2; + break; + case 0x2: + btn = 0x1; + break; + case 0xA: + btn = 0x1; + break; + case 0x4: + btn = 0x1; + break; + case 0x8: + btn = 0x1; + break; + case 0xF: + btn = 0x1; + break; + + default: + btn = 0x1; + break; + } + } + if(btn_temp_id == 2) { + switch(btn_temp_id_original) { + case 0x1: + btn = 0x4; + break; + case 0x2: + btn = 0x4; + break; + case 0xA: + btn = 0x4; + break; + case 0x4: + btn = klq_last_custom_btn; + break; + case 0x8: + btn = 0x4; + break; + case 0xF: + btn = 0x4; + break; + + default: + btn = 0x4; + break; + } + } + if(btn_temp_id == 3) { + switch(btn_temp_id_original) { + case 0x1: + btn = 0x8; + break; + case 0x2: + btn = 0x8; + break; + case 0xA: + btn = 0x8; + break; + case 0x4: + btn = 0x8; + break; + case 0x8: + btn = 0x2; + break; + case 0xF: + btn = 0x8; + break; + + default: + btn = 0x8; + break; + } + } + if(btn_temp_id == 4) { + switch(btn_temp_id_original) { + case 0x1: + btn = klq_last_custom_btn; + break; + case 0x2: + btn = klq_last_custom_btn; + break; + case 0xA: + btn = 0x2; + break; + case 0x4: + btn = 0x2; + break; + case 0x8: + btn = klq_last_custom_btn; + break; + case 0xF: + btn = 0x2; + break; + + default: + btn = 0x2; + break; + } + } + + if((btn_temp_id == 0) && (btn_temp_id_original != 0)) { + btn = btn_temp_id_original; + } + + // Generate new key + + if(subghz_protocol_keeloq_gen_data(instance, btn, true)) { + // OK } else { return false; } @@ -360,13 +538,17 @@ static bool return true; } -bool subghz_protocol_encoder_keeloq_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_encoder_keeloq_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolEncoderKeeloq* instance = context; - bool res = false; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - FURI_LOG_E(TAG, "Deserialize error"); + ret = subghz_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + subghz_protocol_keeloq_const.min_count_bit_for_found); + if(ret != SubGhzProtocolStatusOk) { break; } if(instance->generic.data_count_bit != @@ -406,10 +588,13 @@ bool subghz_protocol_encoder_keeloq_deserialize(void* context, FlipperFormat* fl flipper_format_read_uint32( flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); - if(!subghz_protocol_encoder_keeloq_get_upload(instance, instance->generic.btn)) break; - + if(!subghz_protocol_encoder_keeloq_get_upload(instance, instance->generic.btn)) { + ret = SubGhzProtocolStatusErrorEncoderGetUpload; + break; + } if(!flipper_format_rewind(flipper_format)) { FURI_LOG_E(TAG, "Rewind error"); + ret = SubGhzProtocolStatusErrorParserOthers; break; } uint8_t key_data[sizeof(uint64_t)] = {0}; @@ -418,15 +603,14 @@ bool subghz_protocol_encoder_keeloq_deserialize(void* context, FlipperFormat* fl } if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) { FURI_LOG_E(TAG, "Unable to add Key"); + ret = SubGhzProtocolStatusErrorParserKey; break; } instance->encoder.is_running = true; - - res = true; } while(false); - return res; + return ret; } void subghz_protocol_encoder_keeloq_stop(void* context) { @@ -459,6 +643,8 @@ void* subghz_protocol_decoder_keeloq_alloc(SubGhzEnvironment* environment) { instance->keystore = subghz_environment_get_keystore(environment); instance->manufacture_from_file = furi_string_alloc(); + bft_prog_mode = false; + return instance; } @@ -575,7 +761,7 @@ void subghz_protocol_decoder_keeloq_feed(void* context, bool level, uint32_t dur /** * Validation of decrypt data. * @param instance Pointer to a SubGhzBlockGeneric instance - * @param decrypt Decrypd data + * @param decrypt Decrypted data * @param btn Button number, 4 bit * @param end_serial decrement the last 10 bits of the serial number * @return true On success @@ -957,23 +1143,38 @@ static void subghz_protocol_keeloq_check_remote_controller( uint64_t key = subghz_protocol_blocks_reverse_key(instance->data, instance->data_count_bit); uint32_t key_fix = key >> 32; uint32_t key_hop = key & 0x00000000ffffffff; - // Check key AN-Motors - if((key_hop >> 24) == ((key_hop >> 16) & 0x00ff) && - (key_fix >> 28) == ((key_hop >> 12) & 0x0f) && (key_hop & 0xFFF) == 0x404) { - *manufacture_name = "AN-Motors"; - mfname = *manufacture_name; - instance->cnt = key_hop >> 16; - } else if((key_hop & 0xFFF) == (0x000) && (key_fix >> 28) == ((key_hop >> 12) & 0x0f)) { - *manufacture_name = "HCS101"; - mfname = *manufacture_name; - instance->cnt = key_hop >> 16; + + // If we are in BFT programming mode we will set previous remembered counter and skip mf keys check + if(!bft_prog_mode) { + // Check key AN-Motors + if((key_hop >> 24) == ((key_hop >> 16) & 0x00ff) && + (key_fix >> 28) == ((key_hop >> 12) & 0x0f) && (key_hop & 0xFFF) == 0x404) { + *manufacture_name = "AN-Motors"; + mfname = *manufacture_name; + instance->cnt = key_hop >> 16; + } else if((key_hop & 0xFFF) == (0x000) && (key_fix >> 28) == ((key_hop >> 12) & 0x0f)) { + *manufacture_name = "HCS101"; + mfname = *manufacture_name; + instance->cnt = key_hop >> 16; + } else { + subghz_protocol_keeloq_check_remote_controller_selector( + instance, key_fix, key_hop, keystore, manufacture_name); + } + temp_counter = instance->cnt; + } else { - subghz_protocol_keeloq_check_remote_controller_selector( - instance, key_fix, key_hop, keystore, manufacture_name); + *manufacture_name = "BFT"; + mfname = *manufacture_name; + instance->cnt = temp_counter; } instance->serial = key_fix & 0x0FFFFFFF; instance->btn = key_fix >> 28; + + // Save original button for later use + if(btn_temp_id_original == 0) { + btn_temp_id_original = instance->btn; + } } uint8_t subghz_protocol_decoder_keeloq_get_hash_data(void* context) { @@ -983,14 +1184,15 @@ uint8_t subghz_protocol_decoder_keeloq_get_hash_data(void* context) { &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } -bool subghz_protocol_decoder_keeloq_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_keeloq_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderKeeloq* instance = context; - bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + SubGhzProtocolStatus res = + subghz_block_generic_serialize(&instance->generic, flipper_format, preset); subghz_protocol_keeloq_check_remote_controller( &instance->generic, instance->keystore, &instance->manufacture_name); @@ -1000,28 +1202,32 @@ bool subghz_protocol_decoder_keeloq_serialize( for(size_t i = 0; i < sizeof(uint32_t); i++) { seed_data[sizeof(uint32_t) - i - 1] = (instance->generic.seed >> i * 8) & 0xFF; } - if(res && !flipper_format_write_hex(flipper_format, "Seed", seed_data, sizeof(uint32_t))) { + if((res == SubGhzProtocolStatusOk) && + !flipper_format_write_hex(flipper_format, "Seed", seed_data, sizeof(uint32_t))) { FURI_LOG_E(TAG, "DECODER Serialize: Unable to add Seed"); - res = false; + res = SubGhzProtocolStatusError; } instance->generic.seed = seed_data[0] << 24 | seed_data[1] << 16 | seed_data[2] << 8 | seed_data[3]; } - if(res && !flipper_format_write_string_cstr( - flipper_format, "Manufacture", instance->manufacture_name)) { + if((res == SubGhzProtocolStatusOk) && + !flipper_format_write_string_cstr( + flipper_format, "Manufacture", instance->manufacture_name)) { FURI_LOG_E(TAG, "DECODER Serialize: Unable to add manufacture name"); - res = false; + res = SubGhzProtocolStatusError; } return res; } -bool subghz_protocol_decoder_keeloq_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_decoder_keeloq_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolDecoderKeeloq* instance = context; - bool res = false; + SubGhzProtocolStatus res = SubGhzProtocolStatusError; do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + if(SubGhzProtocolStatusOk != + subghz_block_generic_deserialize(&instance->generic, flipper_format)) { FURI_LOG_E(TAG, "Deserialize error"); break; } @@ -1055,7 +1261,7 @@ bool subghz_protocol_decoder_keeloq_deserialize(void* context, FlipperFormat* fl break; } - res = true; + res = SubGhzProtocolStatusOk; } while(false); return res; diff --git a/lib/subghz/protocols/keeloq.h b/lib/subghz/protocols/keeloq.h index 7b0cfc3bd..f0715648c 100644 --- a/lib/subghz/protocols/keeloq.h +++ b/lib/subghz/protocols/keeloq.h @@ -15,6 +15,13 @@ void keeloq_reset_mfname(); void keeloq_reset_kl_type(); +void keeloq_set_btn(uint8_t b); + +uint8_t keeloq_get_original_btn(); +uint8_t keeloq_get_custom_btn(); + +void keeloq_reset_original_btn(); + /** * Allocate SubGhzProtocolEncoderKeeloq. * @param environment Pointer to a SubGhzEnvironment instance @@ -74,9 +81,10 @@ bool subghz_protocol_keeloq_bft_create_data( * Deserialize and generating an upload to send. * @param context Pointer to a SubGhzProtocolEncoderKeeloq instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_protocol_encoder_keeloq_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_encoder_keeloq_deserialize(void* context, FlipperFormat* flipper_format); /** * Forced transmission stop. @@ -130,9 +138,9 @@ uint8_t subghz_protocol_decoder_keeloq_get_hash_data(void* context); * @param context Pointer to a SubGhzProtocolDecoderKeeloq instance * @param flipper_format Pointer to a FlipperFormat instance * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success + * @return SubGhzProtocolStatus */ -bool subghz_protocol_decoder_keeloq_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_keeloq_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); @@ -141,9 +149,10 @@ bool subghz_protocol_decoder_keeloq_serialize( * Deserialize data SubGhzProtocolDecoderKeeloq. * @param context Pointer to a SubGhzProtocolDecoderKeeloq instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return SubGhzProtocolStatus */ -bool subghz_protocol_decoder_keeloq_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_decoder_keeloq_deserialize(void* context, FlipperFormat* flipper_format); /** * Getting a textual representation of the received data. diff --git a/lib/subghz/protocols/kia.c b/lib/subghz/protocols/kia.c index a5d9e37ef..1d134f7ba 100644 --- a/lib/subghz/protocols/kia.c +++ b/lib/subghz/protocols/kia.c @@ -230,7 +230,7 @@ uint8_t subghz_protocol_decoder_kia_get_hash_data(void* context) { &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } -bool subghz_protocol_decoder_kia_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_kia_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset) { @@ -239,21 +239,12 @@ bool subghz_protocol_decoder_kia_serialize( return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); } -bool subghz_protocol_decoder_kia_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_decoder_kia_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolDecoderKIA* instance = context; - bool ret = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit != subghz_protocol_kia_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); - break; - } - ret = true; - } while(false); - return ret; + return subghz_block_generic_deserialize_check_count_bit( + &instance->generic, flipper_format, subghz_protocol_kia_const.min_count_bit_for_found); } void subghz_protocol_decoder_kia_get_string(void* context, FuriString* output) { diff --git a/lib/subghz/protocols/kia.h b/lib/subghz/protocols/kia.h index a9afcf149..749ff8afd 100644 --- a/lib/subghz/protocols/kia.h +++ b/lib/subghz/protocols/kia.h @@ -50,9 +50,9 @@ uint8_t subghz_protocol_decoder_kia_get_hash_data(void* context); * @param context Pointer to a SubGhzProtocolDecoderKIA instance * @param flipper_format Pointer to a FlipperFormat instance * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success + * @return status */ -bool subghz_protocol_decoder_kia_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_kia_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); @@ -61,9 +61,10 @@ bool subghz_protocol_decoder_kia_serialize( * Deserialize data SubGhzProtocolDecoderKIA. * @param context Pointer to a SubGhzProtocolDecoderKIA instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_protocol_decoder_kia_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_decoder_kia_deserialize(void* context, FlipperFormat* flipper_format); /** * Getting a textual representation of the received data. diff --git a/lib/subghz/protocols/kinggates_stylo_4k.c b/lib/subghz/protocols/kinggates_stylo_4k.c index 5f2a83d77..a9127d2b1 100644 --- a/lib/subghz/protocols/kinggates_stylo_4k.c +++ b/lib/subghz/protocols/kinggates_stylo_4k.c @@ -156,7 +156,11 @@ static bool subghz_protocol_kinggates_stylo_4k_gen_data( instance->generic.cnt = decrypt & 0xFFFF; if(instance->generic.cnt < 0xFFFF) { - instance->generic.cnt++; + if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) >= 0xFFFF) { + instance->generic.cnt = 0; + } else { + instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult(); + } } else if(instance->generic.cnt >= 0xFFFF) { instance->generic.cnt = 0; } @@ -259,14 +263,15 @@ static bool subghz_protocol_encoder_kinggates_stylo_4k_get_upload( return true; } -bool subghz_protocol_encoder_kinggates_stylo_4k_deserialize( +SubGhzProtocolStatus subghz_protocol_encoder_kinggates_stylo_4k_deserialize( void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolEncoderKingGates_stylo_4k* instance = context; - bool res = false; + SubGhzProtocolStatus res = SubGhzProtocolStatusError; do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + if(SubGhzProtocolStatusOk != + subghz_block_generic_deserialize(&instance->generic, flipper_format)) { FURI_LOG_E(TAG, "Deserialize error"); break; } @@ -310,7 +315,7 @@ bool subghz_protocol_encoder_kinggates_stylo_4k_deserialize( instance->encoder.is_running = true; - res = true; + res = SubGhzProtocolStatusOk; } while(false); return res; @@ -505,57 +510,57 @@ uint8_t subghz_protocol_decoder_kinggates_stylo_4k_get_hash_data(void* context) &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } -bool subghz_protocol_decoder_kinggates_stylo_4k_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_kinggates_stylo_4k_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderKingGates_stylo_4k* instance = context; - bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + SubGhzProtocolStatus ret = + subghz_block_generic_serialize(&instance->generic, flipper_format, preset); 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_2 >> (i * 8)) & 0xFF; } - if(res && !flipper_format_write_hex(flipper_format, "Data", key_data, sizeof(uint64_t))) { + if((ret == SubGhzProtocolStatusOk) && + !flipper_format_write_hex(flipper_format, "Data", key_data, sizeof(uint64_t))) { FURI_LOG_E(TAG, "Unable to add Data"); - res = false; + ret = SubGhzProtocolStatusErrorParserOthers; } - return res; - - return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + return ret; } -bool subghz_protocol_decoder_kinggates_stylo_4k_deserialize( +SubGhzProtocolStatus subghz_protocol_decoder_kinggates_stylo_4k_deserialize( void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolDecoderKingGates_stylo_4k* instance = context; - bool ret = false; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_kinggates_stylo_4k_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); + ret = subghz_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + subghz_protocol_kinggates_stylo_4k_const.min_count_bit_for_found); + if(ret != SubGhzProtocolStatusOk) { break; } if(!flipper_format_rewind(flipper_format)) { FURI_LOG_E(TAG, "Rewind error"); + ret = SubGhzProtocolStatusErrorParserOthers; break; } uint8_t key_data[sizeof(uint64_t)] = {0}; if(!flipper_format_read_hex(flipper_format, "Data", key_data, sizeof(uint64_t))) { FURI_LOG_E(TAG, "Missing Data"); + ret = SubGhzProtocolStatusErrorParserOthers; break; } for(uint8_t i = 0; i < sizeof(uint64_t); i++) { instance->generic.data_2 = instance->generic.data_2 << 8 | key_data[i]; } - ret = true; } while(false); return ret; } diff --git a/lib/subghz/protocols/kinggates_stylo_4k.h b/lib/subghz/protocols/kinggates_stylo_4k.h index 9717f6715..cdefebc84 100644 --- a/lib/subghz/protocols/kinggates_stylo_4k.h +++ b/lib/subghz/protocols/kinggates_stylo_4k.h @@ -29,7 +29,7 @@ void subghz_protocol_encoder_kinggates_stylo_4k_free(void* context); * @param flipper_format Pointer to a FlipperFormat instance * @return true On success */ -bool subghz_protocol_encoder_kinggates_stylo_4k_deserialize( +SubGhzProtocolStatus subghz_protocol_encoder_kinggates_stylo_4k_deserialize( void* context, FlipperFormat* flipper_format); @@ -85,9 +85,9 @@ uint8_t subghz_protocol_decoder_kinggates_stylo_4k_get_hash_data(void* context); * @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance * @param flipper_format Pointer to a FlipperFormat instance * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success + * @return status */ -bool subghz_protocol_decoder_kinggates_stylo_4k_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_kinggates_stylo_4k_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); @@ -96,9 +96,9 @@ bool subghz_protocol_decoder_kinggates_stylo_4k_serialize( * Deserialize data SubGhzProtocolDecoderKingGates_stylo_4k. * @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_protocol_decoder_kinggates_stylo_4k_deserialize( +SubGhzProtocolStatus subghz_protocol_decoder_kinggates_stylo_4k_deserialize( void* context, FlipperFormat* flipper_format); diff --git a/lib/subghz/protocols/linear.c b/lib/subghz/protocols/linear.c index 2fc8b20c8..8d3735796 100644 --- a/lib/subghz/protocols/linear.c +++ b/lib/subghz/protocols/linear.c @@ -147,31 +147,31 @@ static bool subghz_protocol_encoder_linear_get_upload(SubGhzProtocolEncoderLinea return true; } -bool subghz_protocol_encoder_linear_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_encoder_linear_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolEncoderLinear* instance = context; - bool res = false; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - FURI_LOG_E(TAG, "Deserialize error"); - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_linear_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); + ret = subghz_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + subghz_protocol_linear_const.min_count_bit_for_found); + if(ret != SubGhzProtocolStatusOk) { break; } //optional parameter parameter flipper_format_read_uint32( flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); - if(!subghz_protocol_encoder_linear_get_upload(instance)) break; + if(!subghz_protocol_encoder_linear_get_upload(instance)) { + ret = SubGhzProtocolStatusErrorEncoderGetUpload; + break; + } instance->encoder.is_running = true; - - res = true; } while(false); - return res; + return ret; } void subghz_protocol_encoder_linear_stop(void* context) { @@ -300,7 +300,7 @@ uint8_t subghz_protocol_decoder_linear_get_hash_data(void* context) { &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } -bool subghz_protocol_decoder_linear_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_linear_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset) { @@ -309,22 +309,12 @@ bool subghz_protocol_decoder_linear_serialize( return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); } -bool subghz_protocol_decoder_linear_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_decoder_linear_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolDecoderLinear* instance = context; - bool ret = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_linear_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); - break; - } - ret = true; - } while(false); - return ret; + return subghz_block_generic_deserialize_check_count_bit( + &instance->generic, flipper_format, subghz_protocol_linear_const.min_count_bit_for_found); } void subghz_protocol_decoder_linear_get_string(void* context, FuriString* output) { diff --git a/lib/subghz/protocols/linear.h b/lib/subghz/protocols/linear.h index 923337ac2..b692b911c 100644 --- a/lib/subghz/protocols/linear.h +++ b/lib/subghz/protocols/linear.h @@ -28,9 +28,10 @@ void subghz_protocol_encoder_linear_free(void* context); * Deserialize and generating an upload to send. * @param context Pointer to a SubGhzProtocolEncoderLinear instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_protocol_encoder_linear_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_encoder_linear_deserialize(void* context, FlipperFormat* flipper_format); /** * Forced transmission stop. @@ -84,9 +85,9 @@ uint8_t subghz_protocol_decoder_linear_get_hash_data(void* context); * @param context Pointer to a SubGhzProtocolDecoderLinear instance * @param flipper_format Pointer to a FlipperFormat instance * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success + * @return status */ -bool subghz_protocol_decoder_linear_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_linear_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); @@ -95,9 +96,10 @@ bool subghz_protocol_decoder_linear_serialize( * Deserialize data SubGhzProtocolDecoderLinear. * @param context Pointer to a SubGhzProtocolDecoderLinear instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_protocol_decoder_linear_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_decoder_linear_deserialize(void* context, FlipperFormat* flipper_format); /** * Getting a textual representation of the received data. diff --git a/lib/subghz/protocols/linear_delta3.c b/lib/subghz/protocols/linear_delta3.c index 869edac84..97ac5cc5a 100644 --- a/lib/subghz/protocols/linear_delta3.c +++ b/lib/subghz/protocols/linear_delta3.c @@ -150,33 +150,32 @@ static bool return true; } -bool subghz_protocol_encoder_linear_delta3_deserialize( +SubGhzProtocolStatus subghz_protocol_encoder_linear_delta3_deserialize( void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolEncoderLinearDelta3* instance = context; - bool res = false; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - FURI_LOG_E(TAG, "Deserialize error"); - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_linear_delta3_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); + ret = subghz_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + subghz_protocol_linear_delta3_const.min_count_bit_for_found); + if(ret != SubGhzProtocolStatusOk) { break; } //optional parameter parameter flipper_format_read_uint32( flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); - if(!subghz_protocol_encoder_linear_delta3_get_upload(instance)) break; + if(!subghz_protocol_encoder_linear_delta3_get_upload(instance)) { + ret = SubGhzProtocolStatusErrorEncoderGetUpload; + break; + } instance->encoder.is_running = true; - - res = true; } while(false); - return res; + return ret; } void subghz_protocol_encoder_linear_delta3_stop(void* context) { @@ -312,7 +311,7 @@ uint8_t subghz_protocol_decoder_linear_delta3_get_hash_data(void* context) { &instance->decoder, (instance->decoder.decode_count_bit / 8)); } -bool subghz_protocol_decoder_linear_delta3_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_linear_delta3_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset) { @@ -321,24 +320,15 @@ bool subghz_protocol_decoder_linear_delta3_serialize( return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); } -bool subghz_protocol_decoder_linear_delta3_deserialize( +SubGhzProtocolStatus subghz_protocol_decoder_linear_delta3_deserialize( void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolDecoderLinearDelta3* instance = context; - bool ret = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_linear_delta3_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); - break; - } - ret = true; - } while(false); - return ret; + return subghz_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + subghz_protocol_linear_delta3_const.min_count_bit_for_found); } void subghz_protocol_decoder_linear_delta3_get_string(void* context, FuriString* output) { diff --git a/lib/subghz/protocols/linear_delta3.h b/lib/subghz/protocols/linear_delta3.h index 2f0a32e68..22f6730f4 100644 --- a/lib/subghz/protocols/linear_delta3.h +++ b/lib/subghz/protocols/linear_delta3.h @@ -28,11 +28,10 @@ void subghz_protocol_encoder_linear_delta3_free(void* context); * Deserialize and generating an upload to send. * @param context Pointer to a SubGhzProtocolEncoderLinearDelta3 instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_protocol_encoder_linear_delta3_deserialize( - void* context, - FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_encoder_linear_delta3_deserialize(void* context, FlipperFormat* flipper_format); /** * Forced transmission stop. @@ -86,9 +85,9 @@ uint8_t subghz_protocol_decoder_linear_delta3_get_hash_data(void* context); * @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance * @param flipper_format Pointer to a FlipperFormat instance * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success + * @return status */ -bool subghz_protocol_decoder_linear_delta3_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_linear_delta3_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); @@ -97,11 +96,10 @@ bool subghz_protocol_decoder_linear_delta3_serialize( * Deserialize data SubGhzProtocolDecoderLinearDelta3. * @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_protocol_decoder_linear_delta3_deserialize( - void* context, - FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_decoder_linear_delta3_deserialize(void* context, FlipperFormat* flipper_format); /** * Getting a textual representation of the received data. diff --git a/lib/subghz/protocols/magellan.c b/lib/subghz/protocols/magellan.c index 67d3fe3d3..2d02a866c 100644 --- a/lib/subghz/protocols/magellan.c +++ b/lib/subghz/protocols/magellan.c @@ -150,31 +150,31 @@ static bool subghz_protocol_encoder_magellan_get_upload(SubGhzProtocolEncoderMag return true; } -bool subghz_protocol_encoder_magellan_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_encoder_magellan_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolEncoderMagellan* instance = context; - bool res = false; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - FURI_LOG_E(TAG, "Deserialize error"); - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_magellan_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); + ret = subghz_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + subghz_protocol_magellan_const.min_count_bit_for_found); + if(ret != SubGhzProtocolStatusOk) { break; } //optional parameter parameter flipper_format_read_uint32( flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); - if(!subghz_protocol_encoder_magellan_get_upload(instance)) break; + if(!subghz_protocol_encoder_magellan_get_upload(instance)) { + ret = SubGhzProtocolStatusErrorEncoderGetUpload; + break; + } instance->encoder.is_running = true; - - res = true; } while(false); - return res; + return ret; } void subghz_protocol_encoder_magellan_stop(void* context) { @@ -397,7 +397,7 @@ uint8_t subghz_protocol_decoder_magellan_get_hash_data(void* context) { &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } -bool subghz_protocol_decoder_magellan_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_magellan_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset) { @@ -406,22 +406,14 @@ bool subghz_protocol_decoder_magellan_serialize( return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); } -bool subghz_protocol_decoder_magellan_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_decoder_magellan_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolDecoderMagellan* instance = context; - bool ret = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_magellan_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); - break; - } - ret = true; - } while(false); - return ret; + return subghz_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + subghz_protocol_magellan_const.min_count_bit_for_found); } void subghz_protocol_decoder_magellan_get_string(void* context, FuriString* output) { diff --git a/lib/subghz/protocols/magellan.h b/lib/subghz/protocols/magellan.h index a179c9cb4..e0fb7ca52 100644 --- a/lib/subghz/protocols/magellan.h +++ b/lib/subghz/protocols/magellan.h @@ -28,9 +28,10 @@ void subghz_protocol_encoder_magellan_free(void* context); * Deserialize and generating an upload to send. * @param context Pointer to a SubGhzProtocolEncoderMagellan instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_protocol_encoder_magellan_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_encoder_magellan_deserialize(void* context, FlipperFormat* flipper_format); /** * Forced transmission stop. @@ -84,9 +85,9 @@ uint8_t subghz_protocol_decoder_magellan_get_hash_data(void* context); * @param context Pointer to a SubGhzProtocolDecoderMagellan instance * @param flipper_format Pointer to a FlipperFormat instance * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success + * @return status */ -bool subghz_protocol_decoder_magellan_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_magellan_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); @@ -95,9 +96,10 @@ bool subghz_protocol_decoder_magellan_serialize( * Deserialize data SubGhzProtocolDecoderMagellan. * @param context Pointer to a SubGhzProtocolDecoderMagellan instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_protocol_decoder_magellan_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_decoder_magellan_deserialize(void* context, FlipperFormat* flipper_format); /** * Getting a textual representation of the received data. diff --git a/lib/subghz/protocols/marantec.c b/lib/subghz/protocols/marantec.c index d557c29b0..fc4aa0dca 100644 --- a/lib/subghz/protocols/marantec.c +++ b/lib/subghz/protocols/marantec.c @@ -188,18 +188,17 @@ static void subghz_protocol_marantec_remote_controller(SubGhzBlockGeneric* insta instance->serial = ((instance->data >> 12) & 0xFFFFFF00) | ((instance->data >> 8) & 0xFF); } -bool subghz_protocol_encoder_marantec_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_encoder_marantec_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolEncoderMarantec* instance = context; - bool res = false; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - FURI_LOG_E(TAG, "Deserialize error"); - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_marantec_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); + ret = subghz_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + subghz_protocol_marantec_const.min_count_bit_for_found); + if(ret != SubGhzProtocolStatusOk) { break; } //optional parameter parameter @@ -209,11 +208,9 @@ bool subghz_protocol_encoder_marantec_deserialize(void* context, FlipperFormat* subghz_protocol_marantec_remote_controller(&instance->generic); subghz_protocol_encoder_marantec_get_upload(instance); instance->encoder.is_running = true; - - res = true; } while(false); - return res; + return ret; } void subghz_protocol_encoder_marantec_stop(void* context) { @@ -346,7 +343,7 @@ uint8_t subghz_protocol_decoder_marantec_get_hash_data(void* context) { &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } -bool subghz_protocol_decoder_marantec_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_marantec_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset) { @@ -355,22 +352,14 @@ bool subghz_protocol_decoder_marantec_serialize( return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); } -bool subghz_protocol_decoder_marantec_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_decoder_marantec_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolDecoderMarantec* instance = context; - bool ret = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_marantec_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); - break; - } - ret = true; - } while(false); - return ret; + return subghz_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + subghz_protocol_marantec_const.min_count_bit_for_found); } void subghz_protocol_decoder_marantec_get_string(void* context, FuriString* output) { diff --git a/lib/subghz/protocols/marantec.h b/lib/subghz/protocols/marantec.h index e330ccf16..485c563b2 100644 --- a/lib/subghz/protocols/marantec.h +++ b/lib/subghz/protocols/marantec.h @@ -28,9 +28,10 @@ void subghz_protocol_encoder_marantec_free(void* context); * Deserialize and generating an upload to send. * @param context Pointer to a SubGhzProtocolEncoderMarantec instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_protocol_encoder_marantec_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_encoder_marantec_deserialize(void* context, FlipperFormat* flipper_format); /** * Forced transmission stop. @@ -84,9 +85,9 @@ uint8_t subghz_protocol_decoder_marantec_get_hash_data(void* context); * @param context Pointer to a SubGhzProtocolDecoderMarantec instance * @param flipper_format Pointer to a FlipperFormat instance * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success + * @return status */ -bool subghz_protocol_decoder_marantec_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_marantec_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); @@ -95,9 +96,10 @@ bool subghz_protocol_decoder_marantec_serialize( * Deserialize data SubGhzProtocolDecoderMarantec. * @param context Pointer to a SubGhzProtocolDecoderMarantec instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_protocol_decoder_marantec_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_decoder_marantec_deserialize(void* context, FlipperFormat* flipper_format); /** * Getting a textual representation of the received data. diff --git a/lib/subghz/protocols/megacode.c b/lib/subghz/protocols/megacode.c index 05b5b6894..ba58bc445 100644 --- a/lib/subghz/protocols/megacode.c +++ b/lib/subghz/protocols/megacode.c @@ -175,31 +175,31 @@ static bool subghz_protocol_encoder_megacode_get_upload(SubGhzProtocolEncoderMeg return true; } -bool subghz_protocol_encoder_megacode_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_encoder_megacode_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolEncoderMegaCode* instance = context; - bool res = false; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - FURI_LOG_E(TAG, "Deserialize error"); - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_megacode_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); + ret = subghz_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + subghz_protocol_megacode_const.min_count_bit_for_found); + if(ret != SubGhzProtocolStatusOk) { break; } //optional parameter parameter flipper_format_read_uint32( flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); - if(!subghz_protocol_encoder_megacode_get_upload(instance)) break; + if(!subghz_protocol_encoder_megacode_get_upload(instance)) { + ret = SubGhzProtocolStatusErrorEncoderGetUpload; + break; + } instance->encoder.is_running = true; - - res = true; } while(false); - return res; + return ret; } void subghz_protocol_encoder_megacode_stop(void* context) { @@ -381,7 +381,7 @@ uint8_t subghz_protocol_decoder_megacode_get_hash_data(void* context) { &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } -bool subghz_protocol_decoder_megacode_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_megacode_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset) { @@ -390,22 +390,14 @@ bool subghz_protocol_decoder_megacode_serialize( return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); } -bool subghz_protocol_decoder_megacode_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_decoder_megacode_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolDecoderMegaCode* instance = context; - bool ret = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_megacode_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); - break; - } - ret = true; - } while(false); - return ret; + return subghz_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + subghz_protocol_megacode_const.min_count_bit_for_found); } void subghz_protocol_decoder_megacode_get_string(void* context, FuriString* output) { diff --git a/lib/subghz/protocols/megacode.h b/lib/subghz/protocols/megacode.h index e31434fa3..616ecdf64 100644 --- a/lib/subghz/protocols/megacode.h +++ b/lib/subghz/protocols/megacode.h @@ -28,9 +28,10 @@ void subghz_protocol_encoder_megacode_free(void* context); * Deserialize and generating an upload to send. * @param context Pointer to a SubGhzProtocolEncoderMegaCode instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_protocol_encoder_megacode_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_encoder_megacode_deserialize(void* context, FlipperFormat* flipper_format); /** * Forced transmission stop. @@ -84,9 +85,9 @@ uint8_t subghz_protocol_decoder_megacode_get_hash_data(void* context); * @param context Pointer to a SubGhzProtocolDecoderMegaCode instance * @param flipper_format Pointer to a FlipperFormat instance * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success + * @return status */ -bool subghz_protocol_decoder_megacode_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_megacode_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); @@ -95,9 +96,10 @@ bool subghz_protocol_decoder_megacode_serialize( * Deserialize data SubGhzProtocolDecoderMegaCode. * @param context Pointer to a SubGhzProtocolDecoderMegaCode instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_protocol_decoder_megacode_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_decoder_megacode_deserialize(void* context, FlipperFormat* flipper_format); /** * Getting a textual representation of the received data. diff --git a/lib/subghz/protocols/nero_radio.c b/lib/subghz/protocols/nero_radio.c index c8126b1e1..d7731dca6 100644 --- a/lib/subghz/protocols/nero_radio.c +++ b/lib/subghz/protocols/nero_radio.c @@ -154,31 +154,31 @@ static bool return true; } -bool subghz_protocol_encoder_nero_radio_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_encoder_nero_radio_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolEncoderNeroRadio* instance = context; - bool res = false; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - FURI_LOG_E(TAG, "Deserialize error"); - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_nero_radio_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); + ret = subghz_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + subghz_protocol_nero_radio_const.min_count_bit_for_found); + if(ret != SubGhzProtocolStatusOk) { break; } //optional parameter parameter flipper_format_read_uint32( flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); - if(!subghz_protocol_encoder_nero_radio_get_upload(instance)) break; + if(!subghz_protocol_encoder_nero_radio_get_upload(instance)) { + ret = SubGhzProtocolStatusErrorEncoderGetUpload; + break; + } instance->encoder.is_running = true; - - res = true; } while(false); - return res; + return ret; } void subghz_protocol_encoder_nero_radio_stop(void* context) { @@ -343,7 +343,7 @@ uint8_t subghz_protocol_decoder_nero_radio_get_hash_data(void* context) { &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } -bool subghz_protocol_decoder_nero_radio_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_nero_radio_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset) { @@ -352,22 +352,14 @@ bool subghz_protocol_decoder_nero_radio_serialize( return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); } -bool subghz_protocol_decoder_nero_radio_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_decoder_nero_radio_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolDecoderNeroRadio* instance = context; - bool ret = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_nero_radio_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); - break; - } - ret = true; - } while(false); - return ret; + return subghz_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + subghz_protocol_nero_radio_const.min_count_bit_for_found); } void subghz_protocol_decoder_nero_radio_get_string(void* context, FuriString* output) { diff --git a/lib/subghz/protocols/nero_radio.h b/lib/subghz/protocols/nero_radio.h index 361da6173..0598aee6c 100644 --- a/lib/subghz/protocols/nero_radio.h +++ b/lib/subghz/protocols/nero_radio.h @@ -28,9 +28,10 @@ void subghz_protocol_encoder_nero_radio_free(void* context); * Deserialize and generating an upload to send. * @param context Pointer to a SubGhzProtocolEncoderNeroRadio instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_protocol_encoder_nero_radio_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_encoder_nero_radio_deserialize(void* context, FlipperFormat* flipper_format); /** * Forced transmission stop. @@ -84,9 +85,9 @@ uint8_t subghz_protocol_decoder_nero_radio_get_hash_data(void* context); * @param context Pointer to a SubGhzProtocolDecoderNeroRadio instance * @param flipper_format Pointer to a FlipperFormat instance * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success + * @return status */ -bool subghz_protocol_decoder_nero_radio_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_nero_radio_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); @@ -95,9 +96,10 @@ bool subghz_protocol_decoder_nero_radio_serialize( * Deserialize data SubGhzProtocolDecoderNeroRadio. * @param context Pointer to a SubGhzProtocolDecoderNeroRadio instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_protocol_decoder_nero_radio_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_decoder_nero_radio_deserialize(void* context, FlipperFormat* flipper_format); /** * Getting a textual representation of the received data. diff --git a/lib/subghz/protocols/nero_sketch.c b/lib/subghz/protocols/nero_sketch.c index b124b717b..09cd0255a 100644 --- a/lib/subghz/protocols/nero_sketch.c +++ b/lib/subghz/protocols/nero_sketch.c @@ -148,31 +148,31 @@ static bool return true; } -bool subghz_protocol_encoder_nero_sketch_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_encoder_nero_sketch_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolEncoderNeroSketch* instance = context; - bool res = false; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - FURI_LOG_E(TAG, "Deserialize error"); - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_nero_sketch_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); + ret = subghz_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + subghz_protocol_nero_sketch_const.min_count_bit_for_found); + if(ret != SubGhzProtocolStatusOk) { break; } //optional parameter parameter flipper_format_read_uint32( flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); - if(!subghz_protocol_encoder_nero_sketch_get_upload(instance)) break; + if(!subghz_protocol_encoder_nero_sketch_get_upload(instance)) { + ret = SubGhzProtocolStatusErrorEncoderGetUpload; + break; + } instance->encoder.is_running = true; - - res = true; } while(false); - return res; + return ret; } void subghz_protocol_encoder_nero_sketch_stop(void* context) { @@ -328,7 +328,7 @@ uint8_t subghz_protocol_decoder_nero_sketch_get_hash_data(void* context) { &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } -bool subghz_protocol_decoder_nero_sketch_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_nero_sketch_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset) { @@ -337,22 +337,14 @@ bool subghz_protocol_decoder_nero_sketch_serialize( return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); } -bool subghz_protocol_decoder_nero_sketch_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_decoder_nero_sketch_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolDecoderNeroSketch* instance = context; - bool ret = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_nero_sketch_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); - break; - } - ret = true; - } while(false); - return ret; + return subghz_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + subghz_protocol_nero_sketch_const.min_count_bit_for_found); } void subghz_protocol_decoder_nero_sketch_get_string(void* context, FuriString* output) { diff --git a/lib/subghz/protocols/nero_sketch.h b/lib/subghz/protocols/nero_sketch.h index ac87fb00a..b557772d2 100644 --- a/lib/subghz/protocols/nero_sketch.h +++ b/lib/subghz/protocols/nero_sketch.h @@ -28,9 +28,10 @@ void subghz_protocol_encoder_nero_sketch_free(void* context); * Deserialize and generating an upload to send. * @param context Pointer to a SubGhzProtocolEncoderNeroSketch instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_protocol_encoder_nero_sketch_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_encoder_nero_sketch_deserialize(void* context, FlipperFormat* flipper_format); /** * Forced transmission stop. @@ -84,9 +85,9 @@ uint8_t subghz_protocol_decoder_nero_sketch_get_hash_data(void* context); * @param context Pointer to a SubGhzProtocolDecoderNeroSketch instance * @param flipper_format Pointer to a FlipperFormat instance * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success + * @return status */ -bool subghz_protocol_decoder_nero_sketch_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_nero_sketch_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); @@ -95,9 +96,10 @@ bool subghz_protocol_decoder_nero_sketch_serialize( * Deserialize data SubGhzProtocolDecoderNeroSketch. * @param context Pointer to a SubGhzProtocolDecoderNeroSketch instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_protocol_decoder_nero_sketch_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_decoder_nero_sketch_deserialize(void* context, FlipperFormat* flipper_format); /** * Getting a textual representation of the received data. diff --git a/lib/subghz/protocols/nice_flo.c b/lib/subghz/protocols/nice_flo.c index a57d5f4da..af81d9f90 100644 --- a/lib/subghz/protocols/nice_flo.c +++ b/lib/subghz/protocols/nice_flo.c @@ -129,13 +129,14 @@ static bool subghz_protocol_encoder_nice_flo_get_upload(SubGhzProtocolEncoderNic return true; } -bool subghz_protocol_encoder_nice_flo_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_encoder_nice_flo_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolEncoderNiceFlo* instance = context; - bool res = false; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - FURI_LOG_E(TAG, "Deserialize error"); + ret = subghz_block_generic_deserialize(&instance->generic, flipper_format); + if(ret != SubGhzProtocolStatusOk) { break; } if((instance->generic.data_count_bit < @@ -143,19 +144,21 @@ bool subghz_protocol_encoder_nice_flo_deserialize(void* context, FlipperFormat* (instance->generic.data_count_bit > 2 * subghz_protocol_nice_flo_const.min_count_bit_for_found)) { FURI_LOG_E(TAG, "Wrong number of bits in key"); + ret = SubGhzProtocolStatusErrorValueBitCount; break; } //optional parameter parameter flipper_format_read_uint32( flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); - if(!subghz_protocol_encoder_nice_flo_get_upload(instance)) break; + if(!subghz_protocol_encoder_nice_flo_get_upload(instance)) { + ret = SubGhzProtocolStatusErrorEncoderGetUpload; + break; + } instance->encoder.is_running = true; - - res = true; } while(false); - return res; + return ret; } void subghz_protocol_encoder_nice_flo_stop(void* context) { @@ -280,7 +283,7 @@ uint8_t subghz_protocol_decoder_nice_flo_get_hash_data(void* context) { &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } -bool subghz_protocol_decoder_nice_flo_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_nice_flo_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset) { @@ -289,12 +292,14 @@ bool subghz_protocol_decoder_nice_flo_serialize( return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); } -bool subghz_protocol_decoder_nice_flo_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_decoder_nice_flo_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolDecoderNiceFlo* instance = context; - bool ret = false; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + ret = subghz_block_generic_deserialize(&instance->generic, flipper_format); + if(ret != SubGhzProtocolStatusOk) { break; } if((instance->generic.data_count_bit < @@ -302,9 +307,9 @@ bool subghz_protocol_decoder_nice_flo_deserialize(void* context, FlipperFormat* (instance->generic.data_count_bit > 2 * subghz_protocol_nice_flo_const.min_count_bit_for_found)) { FURI_LOG_E(TAG, "Wrong number of bits in key"); + ret = SubGhzProtocolStatusErrorValueBitCount; break; } - ret = true; } while(false); return ret; } diff --git a/lib/subghz/protocols/nice_flo.h b/lib/subghz/protocols/nice_flo.h index e382e6146..9a4b53d12 100644 --- a/lib/subghz/protocols/nice_flo.h +++ b/lib/subghz/protocols/nice_flo.h @@ -28,9 +28,10 @@ void subghz_protocol_encoder_nice_flo_free(void* context); * Deserialize and generating an upload to send. * @param context Pointer to a SubGhzProtocolEncoderNiceFlo instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_protocol_encoder_nice_flo_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_encoder_nice_flo_deserialize(void* context, FlipperFormat* flipper_format); /** * Forced transmission stop. @@ -84,9 +85,9 @@ uint8_t subghz_protocol_decoder_nice_flo_get_hash_data(void* context); * @param context Pointer to a SubGhzProtocolDecoderNiceFlo instance * @param flipper_format Pointer to a FlipperFormat instance * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success + * @return status */ -bool subghz_protocol_decoder_nice_flo_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_nice_flo_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); @@ -95,9 +96,10 @@ bool subghz_protocol_decoder_nice_flo_serialize( * Deserialize data SubGhzProtocolDecoderNiceFlo. * @param context Pointer to a SubGhzProtocolDecoderNiceFlo instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_protocol_decoder_nice_flo_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_decoder_nice_flo_deserialize(void* context, FlipperFormat* flipper_format); /** * Getting a textual representation of the received data. diff --git a/lib/subghz/protocols/nice_flor_s.c b/lib/subghz/protocols/nice_flor_s.c index d3a2d6acd..dbdb4c8b5 100644 --- a/lib/subghz/protocols/nice_flor_s.c +++ b/lib/subghz/protocols/nice_flor_s.c @@ -84,6 +84,25 @@ const SubGhzProtocol subghz_protocol_nice_flor_s = { .encoder = &subghz_protocol_nice_flor_s_encoder, }; +static uint8_t n_btn_temp_id; +static uint8_t n_btn_temp_id_original; + +void nice_flors_set_btn(uint8_t b) { + n_btn_temp_id = b; +} + +uint8_t nice_flors_get_original_btn() { + return n_btn_temp_id_original; +} + +uint8_t nice_flors_get_custom_btn() { + return n_btn_temp_id; +} + +void nice_flors_reset_original_btn() { + n_btn_temp_id_original = 0; +} + static void subghz_protocol_nice_flor_s_remote_controller( SubGhzBlockGeneric* instance, const char* file_name); @@ -128,6 +147,74 @@ static void subghz_protocol_encoder_nice_flor_s_get_upload( size_t index = 0; btn = instance->generic.btn; + // Save original button for later use + if(n_btn_temp_id_original == 0) { + n_btn_temp_id_original = btn; + } + + // Set custom button + if(n_btn_temp_id == 1) { + switch(n_btn_temp_id_original) { + case 0x1: + btn = 0x2; + break; + case 0x2: + btn = 0x1; + break; + case 0x4: + btn = 0x1; + break; + case 0x8: + btn = 0x1; + break; + + default: + break; + } + } + if(n_btn_temp_id == 2) { + switch(n_btn_temp_id_original) { + case 0x1: + btn = 0x4; + break; + case 0x2: + btn = 0x4; + break; + case 0x4: + btn = 0x2; + break; + case 0x8: + btn = 0x4; + break; + + default: + break; + } + } + if(n_btn_temp_id == 3) { + switch(n_btn_temp_id_original) { + case 0x1: + btn = 0x8; + break; + case 0x2: + btn = 0x8; + break; + case 0x4: + btn = 0x8; + break; + case 0x8: + btn = 0x2; + break; + + default: + break; + } + } + + if((n_btn_temp_id == 0) && (n_btn_temp_id_original != 0)) { + btn = n_btn_temp_id_original; + } + size_t size_upload = ((instance->generic.data_count_bit * 2) + ((37 + 2 + 2) * 2) * 16); if(size_upload > instance->encoder.size_upload) { FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer."); @@ -136,7 +223,11 @@ static void subghz_protocol_encoder_nice_flor_s_get_upload( } if(instance->generic.cnt < 0xFFFF) { - instance->generic.cnt++; + if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) >= 0xFFFF) { + instance->generic.cnt = 0; + } else { + instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult(); + } } else if(instance->generic.cnt >= 0xFFFF) { instance->generic.cnt = 0; } @@ -215,12 +306,14 @@ static void subghz_protocol_encoder_nice_flor_s_get_upload( instance->encoder.size_upload = index; } -bool subghz_protocol_encoder_nice_flor_s_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_encoder_nice_flor_s_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolEncoderNiceFlorS* instance = context; - bool res = false; + SubGhzProtocolStatus res = SubGhzProtocolStatusError; do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + if(SubGhzProtocolStatusOk != + subghz_block_generic_deserialize(&instance->generic, flipper_format)) { FURI_LOG_E(TAG, "Deserialize error"); break; } @@ -262,7 +355,7 @@ bool subghz_protocol_encoder_nice_flor_s_deserialize(void* context, FlipperForma instance->encoder.is_running = true; - res = true; + res = SubGhzProtocolStatusOk; } while(false); return res; @@ -452,21 +545,40 @@ bool subghz_protocol_nice_flor_s_create_data( uint32_t serial, uint8_t btn, uint16_t cnt, - SubGhzRadioPreset* preset) { + SubGhzRadioPreset* preset, + bool nice_one) { furi_assert(context); SubGhzProtocolEncoderNiceFlorS* instance = context; instance->generic.serial = serial; instance->generic.cnt = cnt; - instance->generic.data_count_bit = 52; + if(nice_one) { + instance->generic.data_count_bit = NICE_ONE_COUNT_BIT; + } else { + instance->generic.data_count_bit = 52; + } uint64_t decrypt = ((uint64_t)instance->generic.serial << 16) | instance->generic.cnt; uint64_t enc_part = subghz_protocol_nice_flor_s_encrypt( decrypt, instance->nice_flor_s_rainbow_table_file_name); uint8_t byte = btn << 4 | (0xF ^ btn ^ 0x3); instance->generic.data = (uint64_t)byte << 44 | enc_part; - bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + if(instance->generic.data_count_bit == NICE_ONE_COUNT_BIT) { + uint8_t add_data[10] = {0}; + for(size_t i = 0; i < 7; i++) { + add_data[i] = (instance->generic.data >> (48 - i * 8)) & 0xFF; + } + subghz_protocol_nice_one_get_data(add_data, 0, 0); + instance->generic.data_2 = 0; + for(size_t j = 7; j < 10; j++) { + instance->generic.data_2 <<= 8; + instance->generic.data_2 += add_data[j]; + } + } - return res; + SubGhzProtocolStatus res = + subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + + return res == SubGhzProtocolStatusOk; } void* subghz_protocol_decoder_nice_flor_s_alloc(SubGhzEnvironment* environment) { @@ -642,6 +754,11 @@ static void subghz_protocol_nice_flor_s_remote_controller( instance->serial = (decrypt >> 16) & 0xFFFFFFF; instance->btn = (decrypt >> 48) & 0xF; } + + // Save original button for later use + if(n_btn_temp_id_original == 0) { + n_btn_temp_id_original = instance->btn; + } } uint8_t subghz_protocol_decoder_nice_flor_s_get_hash_data(void* context) { @@ -651,51 +768,55 @@ uint8_t subghz_protocol_decoder_nice_flor_s_get_hash_data(void* context) { &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } -bool subghz_protocol_decoder_nice_flor_s_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_nice_flor_s_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderNiceFlorS* instance = context; - bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + SubGhzProtocolStatus ret = + subghz_block_generic_serialize(&instance->generic, flipper_format, preset); if(instance->generic.data_count_bit == NICE_ONE_COUNT_BIT) { - if(res && + if((ret == SubGhzProtocolStatusOk) && !flipper_format_write_uint32(flipper_format, "Data", (uint32_t*)&instance->data, 1)) { FURI_LOG_E(TAG, "Unable to add Data"); - res = false; + ret = SubGhzProtocolStatusErrorParserOthers; } } - return res; + return ret; } -bool subghz_protocol_decoder_nice_flor_s_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_decoder_nice_flor_s_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolDecoderNiceFlorS* instance = context; - bool ret = false; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + ret = subghz_block_generic_deserialize(&instance->generic, flipper_format); + if(ret != SubGhzProtocolStatusOk) { break; } if((instance->generic.data_count_bit != subghz_protocol_nice_flor_s_const.min_count_bit_for_found) && (instance->generic.data_count_bit != NICE_ONE_COUNT_BIT)) { FURI_LOG_E(TAG, "Wrong number of bits in key"); + ret = SubGhzProtocolStatusErrorValueBitCount; break; } if(instance->generic.data_count_bit == NICE_ONE_COUNT_BIT) { if(!flipper_format_rewind(flipper_format)) { FURI_LOG_E(TAG, "Rewind error"); + ret = SubGhzProtocolStatusErrorParserOthers; break; } uint32_t temp = 0; if(!flipper_format_read_uint32(flipper_format, "Data", (uint32_t*)&temp, 1)) { FURI_LOG_E(TAG, "Missing Data"); + ret = SubGhzProtocolStatusErrorParserOthers; break; } instance->data = (uint64_t)temp; } - - ret = true; } while(false); return ret; } @@ -735,4 +856,4 @@ void subghz_protocol_decoder_nice_flor_s_get_string(void* context, FuriString* o instance->generic.cnt, instance->generic.btn); } -} \ No newline at end of file +} diff --git a/lib/subghz/protocols/nice_flor_s.h b/lib/subghz/protocols/nice_flor_s.h index e333fc979..679b31812 100644 --- a/lib/subghz/protocols/nice_flor_s.h +++ b/lib/subghz/protocols/nice_flor_s.h @@ -11,6 +11,14 @@ extern const SubGhzProtocolDecoder subghz_protocol_nice_flor_s_decoder; extern const SubGhzProtocolEncoder subghz_protocol_nice_flor_s_encoder; extern const SubGhzProtocol subghz_protocol_nice_flor_s; +// Custom buttons +void nice_flors_set_btn(uint8_t b); + +uint8_t nice_flors_get_original_btn(); +uint8_t nice_flors_get_custom_btn(); + +void nice_flors_reset_original_btn(); + /** * Allocate SubGhzProtocolEncoderNiceFlorS. * @param environment Pointer to a SubGhzEnvironment instance @@ -30,7 +38,8 @@ void subghz_protocol_encoder_nice_flor_s_free(void* context); * @param flipper_format Pointer to a FlipperFormat instance * @return true On success */ -bool subghz_protocol_encoder_nice_flor_s_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_encoder_nice_flor_s_deserialize(void* context, FlipperFormat* flipper_format); /** * Forced transmission stop. @@ -55,6 +64,7 @@ uint64_t subghz_protocol_nice_flor_s_encrypt(uint64_t data, const char* file_nam * @param btn Button number, 4 bit * @param cnt Counter value, 16 bit * @param preset Modulation, SubGhzRadioPreset + * @param nice_one Nice One if true, Nice Flor S if false * @return true On success */ bool subghz_protocol_nice_flor_s_create_data( @@ -63,7 +73,8 @@ bool subghz_protocol_nice_flor_s_create_data( uint32_t serial, uint8_t btn, uint16_t cnt, - SubGhzRadioPreset* preset); + SubGhzRadioPreset* preset, + bool nice_one); /** * Allocate SubGhzProtocolDecoderNiceFlorS. @@ -104,9 +115,9 @@ uint8_t subghz_protocol_decoder_nice_flor_s_get_hash_data(void* context); * @param context Pointer to a SubGhzProtocolDecoderNiceFlorS instance * @param flipper_format Pointer to a FlipperFormat instance * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success + * @return status */ -bool subghz_protocol_decoder_nice_flor_s_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_nice_flor_s_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); @@ -115,9 +126,10 @@ bool subghz_protocol_decoder_nice_flor_s_serialize( * Deserialize data SubGhzProtocolDecoderNiceFlorS. * @param context Pointer to a SubGhzProtocolDecoderNiceFlorS instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_protocol_decoder_nice_flor_s_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_decoder_nice_flor_s_deserialize(void* context, FlipperFormat* flipper_format); /** * Getting a textual representation of the received data. diff --git a/lib/subghz/protocols/phoenix_v2.c b/lib/subghz/protocols/phoenix_v2.c index b3d6f1e98..4ed9766ef 100644 --- a/lib/subghz/protocols/phoenix_v2.c +++ b/lib/subghz/protocols/phoenix_v2.c @@ -132,31 +132,31 @@ static bool return true; } -bool subghz_protocol_encoder_phoenix_v2_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_encoder_phoenix_v2_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolEncoderPhoenix_V2* instance = context; - bool res = false; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - FURI_LOG_E(TAG, "Deserialize error"); - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_phoenix_v2_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); + ret = subghz_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + subghz_protocol_phoenix_v2_const.min_count_bit_for_found); + if(ret != SubGhzProtocolStatusOk) { break; } //optional parameter parameter flipper_format_read_uint32( flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); - if(!subghz_protocol_encoder_phoenix_v2_get_upload(instance)) break; + if(!subghz_protocol_encoder_phoenix_v2_get_upload(instance)) { + ret = SubGhzProtocolStatusErrorEncoderGetUpload; + break; + } instance->encoder.is_running = true; - - res = true; } while(false); - return res; + return ret; } void subghz_protocol_encoder_phoenix_v2_stop(void* context) { @@ -293,7 +293,7 @@ uint8_t subghz_protocol_decoder_phoenix_v2_get_hash_data(void* context) { &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } -bool subghz_protocol_decoder_phoenix_v2_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_phoenix_v2_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset) { @@ -302,22 +302,14 @@ bool subghz_protocol_decoder_phoenix_v2_serialize( return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); } -bool subghz_protocol_decoder_phoenix_v2_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_decoder_phoenix_v2_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolDecoderPhoenix_V2* instance = context; - bool ret = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_phoenix_v2_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); - break; - } - ret = true; - } while(false); - return ret; + return subghz_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + subghz_protocol_phoenix_v2_const.min_count_bit_for_found); } void subghz_protocol_decoder_phoenix_v2_get_string(void* context, FuriString* output) { diff --git a/lib/subghz/protocols/phoenix_v2.h b/lib/subghz/protocols/phoenix_v2.h index 48487535e..0724de1f0 100644 --- a/lib/subghz/protocols/phoenix_v2.h +++ b/lib/subghz/protocols/phoenix_v2.h @@ -28,9 +28,10 @@ void subghz_protocol_encoder_phoenix_v2_free(void* context); * Deserialize and generating an upload to send. * @param context Pointer to a SubGhzProtocolEncoderPhoenix_V2 instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_protocol_encoder_phoenix_v2_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_encoder_phoenix_v2_deserialize(void* context, FlipperFormat* flipper_format); /** * Forced transmission stop. @@ -84,9 +85,9 @@ uint8_t subghz_protocol_decoder_phoenix_v2_get_hash_data(void* context); * @param context Pointer to a SubGhzProtocolDecoderPhoenix_V2 instance * @param flipper_format Pointer to a FlipperFormat instance * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success + * @return status */ -bool subghz_protocol_decoder_phoenix_v2_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_phoenix_v2_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); @@ -95,9 +96,10 @@ bool subghz_protocol_decoder_phoenix_v2_serialize( * Deserialize data SubGhzProtocolDecoderPhoenix_V2. * @param context Pointer to a SubGhzProtocolDecoderPhoenix_V2 instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_protocol_decoder_phoenix_v2_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_decoder_phoenix_v2_deserialize(void* context, FlipperFormat* flipper_format); /** * Getting a textual representation of the received data. diff --git a/lib/subghz/protocols/pocsag.c b/lib/subghz/protocols/pocsag.c deleted file mode 100644 index cd531660b..000000000 --- a/lib/subghz/protocols/pocsag.c +++ /dev/null @@ -1,363 +0,0 @@ -#include "pocsag.h" - -#include -#include "../blocks/const.h" -#include "../blocks/decoder.h" -#include "../blocks/generic.h" -#include "../blocks/math.h" -#include -#include - -#define TAG "POCSAG" - -static const SubGhzBlockConst pocsag_const = { - .te_short = 833, - .te_delta = 100, -}; - -// Minimal amount of sync bits (interleaving zeros and ones) -#define POCSAG_MIN_SYNC_BITS 32 -#define POCSAG_CW_BITS 32 -#define POCSAG_CW_MASK 0xFFFFFFFF -#define POCSAG_FRAME_SYNC_CODE 0x7CD215D8 -#define POCSAG_IDLE_CODE_WORD 0x7A89C197 - -#define POCSAG_FUNC_NUM 0 -#define POCSAG_FUNC_ALERT1 1 -#define POCSAG_FUNC_ALERT2 2 -#define POCSAG_FUNC_ALPHANUM 3 - -static const char* func_msg[] = {"num: ", "alert", "alert: ", "msg: "}; -static const char* bcd_chars = "*U -)("; - -struct SubGhzProtocolDecoderPocsag { - SubGhzProtocolDecoderBase base; - - SubGhzBlockDecoder decoder; - SubGhzBlockGeneric generic; - - uint8_t codeword_idx; - uint32_t ric; - uint8_t func; - - // partially decoded character - uint8_t char_bits; - uint8_t char_data; - - // message being decoded - FuriString* msg; - - // Done messages, ready to be serialized/deserialized - FuriString* done_msg; -}; - -typedef struct SubGhzProtocolDecoderPocsag SubGhzProtocolDecoderPocsag; - -typedef enum { - PocsagDecoderStepReset = 0, - PocsagDecoderStepFoundSync, - PocsagDecoderStepFoundPreamble, - PocsagDecoderStepMessage, -} PocsagDecoderStep; - -void* subghz_protocol_decoder_pocsag_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - - SubGhzProtocolDecoderPocsag* instance = malloc(sizeof(SubGhzProtocolDecoderPocsag)); - instance->base.protocol = &subghz_protocol_pocsag; - instance->generic.protocol_name = instance->base.protocol->name; - instance->msg = furi_string_alloc(); - instance->done_msg = furi_string_alloc(); - return instance; -} - -void subghz_protocol_decoder_pocsag_free(void* context) { - furi_assert(context); - SubGhzProtocolDecoderPocsag* instance = context; - furi_string_free(instance->msg); - furi_string_free(instance->done_msg); - free(instance); -} - -void subghz_protocol_decoder_pocsag_reset(void* context) { - furi_assert(context); - SubGhzProtocolDecoderPocsag* instance = context; - - instance->decoder.parser_step = PocsagDecoderStepReset; - instance->decoder.decode_data = 0UL; - instance->decoder.decode_count_bit = 0; - instance->codeword_idx = 0; - instance->char_bits = 0; - instance->char_data = 0; - furi_string_reset(instance->msg); - furi_string_reset(instance->done_msg); -} - -static void pocsag_decode_address_word(SubGhzProtocolDecoderPocsag* instance, uint32_t data) { - instance->ric = (data >> 13); - instance->ric = (instance->ric << 3) | (instance->codeword_idx >> 1); - instance->func = (data >> 11) & 0b11; -} - -static bool decode_message_alphanumeric(SubGhzProtocolDecoderPocsag* instance, uint32_t data) { - for(uint8_t i = 0; i < 20; i++) { - instance->char_data >>= 1; - if(data & (1 << 30)) { - instance->char_data |= 1 << 6; - } - instance->char_bits++; - if(instance->char_bits == 7) { - if(instance->char_data == 0) return false; - furi_string_push_back(instance->msg, instance->char_data); - instance->char_data = 0; - instance->char_bits = 0; - } - data <<= 1; - } - return true; -} - -static void decode_message_numeric(SubGhzProtocolDecoderPocsag* instance, uint32_t data) { - // 5 groups with 4 bits each - uint8_t val; - for(uint8_t i = 0; i < 5; i++) { - val = (data >> (27 - i * 4)) & 0b1111; - // reverse the order of 4 bits - val = (val & 0x5) << 1 | (val & 0xA) >> 1; - val = (val & 0x3) << 2 | (val & 0xC) >> 2; - - if(val <= 9) - val += '0'; - else - val = bcd_chars[val - 10]; - furi_string_push_back(instance->msg, val); - } -} - -// decode message word, maintaining instance state for partial decoding. Return true if more data -// might follow or false if end of message reached. -static bool pocsag_decode_message_word(SubGhzProtocolDecoderPocsag* instance, uint32_t data) { - switch(instance->func) { - case POCSAG_FUNC_ALERT2: - case POCSAG_FUNC_ALPHANUM: - return decode_message_alphanumeric(instance, data); - - case POCSAG_FUNC_NUM: - decode_message_numeric(instance, data); - return true; - } - return false; -} - -// Function called when current message got decoded, but other messages might follow -static void pocsag_message_done(SubGhzProtocolDecoderPocsag* instance) { - // append the message to the long-term storage string - furi_string_cat_printf(instance->done_msg, "RIC: %" PRIu32 ", ", instance->ric); - furi_string_cat_str(instance->done_msg, func_msg[instance->func]); - if(instance->func != POCSAG_FUNC_ALERT1) { - furi_string_cat(instance->done_msg, instance->msg); - } - furi_string_cat_str(instance->done_msg, "\r\n"); - - // reset the state - instance->char_bits = 0; - instance->char_data = 0; - furi_string_reset(instance->msg); -} - -void subghz_protocol_decoder_pocsag_feed(void* context, bool level, uint32_t duration) { - furi_assert(context); - SubGhzProtocolDecoderPocsag* instance = context; - - // reset state - waiting for 32 bits of interleaving 1s and 0s - if(instance->decoder.parser_step == PocsagDecoderStepReset) { - if(DURATION_DIFF(duration, pocsag_const.te_short) < pocsag_const.te_delta) { - // POCSAG signals are inverted - subghz_protocol_blocks_add_bit(&instance->decoder, !level); - - if(instance->decoder.decode_count_bit == POCSAG_MIN_SYNC_BITS) { - instance->decoder.parser_step = PocsagDecoderStepFoundSync; - } - } else if(instance->decoder.decode_count_bit > 0) { - subghz_protocol_decoder_pocsag_reset(context); - } - return; - } - - int bits_count = duration / pocsag_const.te_short; - uint32_t extra = duration - pocsag_const.te_short * bits_count; - - if(DURATION_DIFF(extra, pocsag_const.te_short) < pocsag_const.te_delta) - bits_count++; - else if(extra > pocsag_const.te_delta) { - // in non-reset state we faced the error signal - we reached the end of the packet, flush data - if(furi_string_size(instance->done_msg) > 0) { - if(instance->base.callback) - instance->base.callback(&instance->base, instance->base.context); - } - subghz_protocol_decoder_pocsag_reset(context); - return; - } - - uint32_t codeword; - - // handle state machine for every incoming bit - while(bits_count-- > 0) { - subghz_protocol_blocks_add_bit(&instance->decoder, !level); - - switch(instance->decoder.parser_step) { - case PocsagDecoderStepFoundSync: - if((instance->decoder.decode_data & POCSAG_CW_MASK) == POCSAG_FRAME_SYNC_CODE) { - instance->decoder.parser_step = PocsagDecoderStepFoundPreamble; - instance->decoder.decode_count_bit = 0; - instance->decoder.decode_data = 0UL; - } - break; - case PocsagDecoderStepFoundPreamble: - // handle codewords - if(instance->decoder.decode_count_bit == POCSAG_CW_BITS) { - codeword = (uint32_t)(instance->decoder.decode_data & POCSAG_CW_MASK); - switch(codeword) { - case POCSAG_IDLE_CODE_WORD: - instance->codeword_idx++; - break; - case POCSAG_FRAME_SYNC_CODE: - instance->codeword_idx = 0; - break; - default: - // Here we expect only address messages - if(codeword >> 31 == 0) { - pocsag_decode_address_word(instance, codeword); - instance->decoder.parser_step = PocsagDecoderStepMessage; - } - instance->codeword_idx++; - } - instance->decoder.decode_count_bit = 0; - instance->decoder.decode_data = 0UL; - } - break; - - case PocsagDecoderStepMessage: - if(instance->decoder.decode_count_bit == POCSAG_CW_BITS) { - codeword = (uint32_t)(instance->decoder.decode_data & POCSAG_CW_MASK); - switch(codeword) { - case POCSAG_IDLE_CODE_WORD: - // Idle during the message stops the message - instance->codeword_idx++; - instance->decoder.parser_step = PocsagDecoderStepFoundPreamble; - pocsag_message_done(instance); - break; - case POCSAG_FRAME_SYNC_CODE: - instance->codeword_idx = 0; - break; - default: - // In this state, both address and message words can arrive - if(codeword >> 31 == 0) { - pocsag_message_done(instance); - pocsag_decode_address_word(instance, codeword); - } else { - if(!pocsag_decode_message_word(instance, codeword)) { - instance->decoder.parser_step = PocsagDecoderStepFoundPreamble; - pocsag_message_done(instance); - } - } - instance->codeword_idx++; - } - instance->decoder.decode_count_bit = 0; - instance->decoder.decode_data = 0UL; - } - break; - } - } -} - -uint8_t subghz_protocol_decoder_pocsag_get_hash_data(void* context) { - furi_assert(context); - SubGhzProtocolDecoderPocsag* instance = context; - uint8_t hash = 0; - for(size_t i = 0; i < furi_string_size(instance->done_msg); i++) - hash ^= furi_string_get_char(instance->done_msg, i); - return hash; -} - -bool subghz_protocol_decoder_pocsag_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) { - furi_assert(context); - SubGhzProtocolDecoderPocsag* instance = context; - uint32_t msg_len; - - if(!subghz_block_generic_serialize(&instance->generic, flipper_format, preset)) return false; - - msg_len = furi_string_size(instance->done_msg); - if(!flipper_format_write_uint32(flipper_format, "MsgLen", &msg_len, 1)) { - FURI_LOG_E(TAG, "Error adding MsgLen"); - return false; - } - - uint8_t* s = (uint8_t*)furi_string_get_cstr(instance->done_msg); - if(!flipper_format_write_hex(flipper_format, "Msg", s, msg_len)) { - FURI_LOG_E(TAG, "Error adding Msg"); - return false; - } - return true; -} - -bool subghz_protocol_decoder_pocsag_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolDecoderPocsag* instance = context; - bool ret = false; - uint32_t msg_len; - uint8_t* buf; - - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - - if(!flipper_format_read_uint32(flipper_format, "MsgLen", &msg_len, 1)) { - FURI_LOG_E(TAG, "Missing MsgLen"); - break; - } - - buf = malloc(msg_len); - if(!flipper_format_read_hex(flipper_format, "Msg", buf, msg_len)) { - FURI_LOG_E(TAG, "Missing Msg"); - free(buf); - break; - } - furi_string_set_strn(instance->done_msg, (const char*)buf, msg_len); - free(buf); - - ret = true; - } while(false); - return ret; -} - -void subhz_protocol_decoder_pocsag_get_string(void* context, FuriString* output) { - furi_assert(context); - SubGhzProtocolDecoderPocsag* instance = context; - furi_string_cat_printf(output, "%s\r\n", instance->generic.protocol_name); - furi_string_cat(output, instance->done_msg); -} - -const SubGhzProtocolDecoder subghz_protocol_pocsag_decoder = { - .alloc = subghz_protocol_decoder_pocsag_alloc, - .free = subghz_protocol_decoder_pocsag_free, - .reset = subghz_protocol_decoder_pocsag_reset, - .feed = subghz_protocol_decoder_pocsag_feed, - .get_hash_data = subghz_protocol_decoder_pocsag_get_hash_data, - .serialize = subghz_protocol_decoder_pocsag_serialize, - .deserialize = subghz_protocol_decoder_pocsag_deserialize, - .get_string = subhz_protocol_decoder_pocsag_get_string, -}; - -const SubGhzProtocol subghz_protocol_pocsag = { - .name = SUBGHZ_PROTOCOL_POCSAG_NAME, - .type = SubGhzProtocolTypeStatic, - .flag = SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Save | - SubGhzProtocolFlag_Load, - - .decoder = &subghz_protocol_pocsag_decoder, -}; diff --git a/lib/subghz/protocols/pocsag.h b/lib/subghz/protocols/pocsag.h deleted file mode 100644 index 5700e2401..000000000 --- a/lib/subghz/protocols/pocsag.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -#include "base.h" - -#define SUBGHZ_PROTOCOL_POCSAG_NAME "POCSAG" - -extern const SubGhzProtocol subghz_protocol_pocsag; diff --git a/lib/subghz/protocols/power_smart.c b/lib/subghz/protocols/power_smart.c index 1e8d10e95..d03282f73 100644 --- a/lib/subghz/protocols/power_smart.c +++ b/lib/subghz/protocols/power_smart.c @@ -192,18 +192,17 @@ static void subghz_protocol_power_smart_remote_controller(SubGhzBlockGeneric* in instance->cnt = ((instance->data >> 49) & 0x3F); } -bool subghz_protocol_encoder_power_smart_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_encoder_power_smart_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolEncoderPowerSmart* instance = context; - bool res = false; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - FURI_LOG_E(TAG, "Deserialize error"); - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_power_smart_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); + ret = subghz_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + subghz_protocol_power_smart_const.min_count_bit_for_found); + if(ret != SubGhzProtocolStatusOk) { break; } //optional parameter parameter @@ -213,11 +212,9 @@ bool subghz_protocol_encoder_power_smart_deserialize(void* context, FlipperForma subghz_protocol_power_smart_remote_controller(&instance->generic); subghz_protocol_encoder_power_smart_get_upload(instance); instance->encoder.is_running = true; - - res = true; } while(false); - return res; + return ret; } void subghz_protocol_encoder_power_smart_stop(void* context) { @@ -345,7 +342,7 @@ uint8_t subghz_protocol_decoder_power_smart_get_hash_data(void* context) { &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } -bool subghz_protocol_decoder_power_smart_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_power_smart_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset) { @@ -354,22 +351,14 @@ bool subghz_protocol_decoder_power_smart_serialize( return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); } -bool subghz_protocol_decoder_power_smart_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_decoder_power_smart_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolDecoderPowerSmart* instance = context; - bool ret = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_power_smart_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); - break; - } - ret = true; - } while(false); - return ret; + return subghz_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + subghz_protocol_power_smart_const.min_count_bit_for_found); } void subghz_protocol_decoder_power_smart_get_string(void* context, FuriString* output) { diff --git a/lib/subghz/protocols/power_smart.h b/lib/subghz/protocols/power_smart.h index 806729f8e..5687cf8b1 100644 --- a/lib/subghz/protocols/power_smart.h +++ b/lib/subghz/protocols/power_smart.h @@ -28,9 +28,10 @@ void subghz_protocol_encoder_power_smart_free(void* context); * Deserialize and generating an upload to send. * @param context Pointer to a SubGhzProtocolEncoderPowerSmart instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_protocol_encoder_power_smart_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_encoder_power_smart_deserialize(void* context, FlipperFormat* flipper_format); /** * Forced transmission stop. @@ -84,9 +85,9 @@ uint8_t subghz_protocol_decoder_power_smart_get_hash_data(void* context); * @param context Pointer to a SubGhzProtocolDecoderPowerSmart instance * @param flipper_format Pointer to a FlipperFormat instance * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success + * @return status */ -bool subghz_protocol_decoder_power_smart_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_power_smart_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); @@ -95,9 +96,10 @@ bool subghz_protocol_decoder_power_smart_serialize( * Deserialize data SubGhzProtocolDecoderPowerSmart. * @param context Pointer to a SubGhzProtocolDecoderPowerSmart instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_protocol_decoder_power_smart_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_decoder_power_smart_deserialize(void* context, FlipperFormat* flipper_format); /** * Getting a textual representation of the received data. diff --git a/lib/subghz/protocols/princeton.c b/lib/subghz/protocols/princeton.c index 7fc8f6524..aa15b8b41 100644 --- a/lib/subghz/protocols/princeton.c +++ b/lib/subghz/protocols/princeton.c @@ -141,39 +141,41 @@ static bool return true; } -bool subghz_protocol_encoder_princeton_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_encoder_princeton_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolEncoderPrinceton* instance = context; - bool res = false; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - FURI_LOG_E(TAG, "Deserialize error"); + ret = subghz_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + subghz_protocol_princeton_const.min_count_bit_for_found); + if(ret != SubGhzProtocolStatusOk) { break; } if(!flipper_format_rewind(flipper_format)) { FURI_LOG_E(TAG, "Rewind error"); + ret = SubGhzProtocolStatusErrorParserOthers; 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_princeton_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); + ret = SubGhzProtocolStatusErrorParserTe; break; } //optional parameter parameter flipper_format_read_uint32( flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); - if(!subghz_protocol_encoder_princeton_get_upload(instance)) break; + if(!subghz_protocol_encoder_princeton_get_upload(instance)) { + ret = SubGhzProtocolStatusErrorEncoderGetUpload; + break; + } instance->encoder.is_running = true; - - res = true; } while(false); - return res; + return ret; } void subghz_protocol_encoder_princeton_stop(void* context) { @@ -308,46 +310,48 @@ uint8_t subghz_protocol_decoder_princeton_get_hash_data(void* context) { &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } -bool subghz_protocol_decoder_princeton_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_princeton_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderPrinceton* 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)) { + SubGhzProtocolStatus ret = + subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + if((ret == SubGhzProtocolStatusOk) && + !flipper_format_write_uint32(flipper_format, "TE", &instance->te, 1)) { FURI_LOG_E(TAG, "Unable to add TE"); - res = false; + ret = SubGhzProtocolStatusErrorParserTe; } - return res; + return ret; } -bool subghz_protocol_decoder_princeton_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_decoder_princeton_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolDecoderPrinceton* instance = context; - bool res = false; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - FURI_LOG_E(TAG, "Deserialize error"); - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_princeton_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); + ret = subghz_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + subghz_protocol_princeton_const.min_count_bit_for_found); + if(ret != SubGhzProtocolStatusOk) { break; } if(!flipper_format_rewind(flipper_format)) { FURI_LOG_E(TAG, "Rewind error"); + ret = SubGhzProtocolStatusErrorParserOthers; break; } if(!flipper_format_read_uint32(flipper_format, "TE", (uint32_t*)&instance->te, 1)) { FURI_LOG_E(TAG, "Missing TE"); + ret = SubGhzProtocolStatusErrorParserTe; break; } - res = true; } while(false); - return res; + return ret; } void subghz_protocol_decoder_princeton_get_string(void* context, FuriString* output) { diff --git a/lib/subghz/protocols/princeton.h b/lib/subghz/protocols/princeton.h index a2a11292e..7c775292d 100644 --- a/lib/subghz/protocols/princeton.h +++ b/lib/subghz/protocols/princeton.h @@ -32,9 +32,10 @@ void subghz_protocol_encoder_princeton_free(void* context); * Deserialize and generating an upload to send. * @param context Pointer to a SubGhzProtocolEncoderPrinceton instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_protocol_encoder_princeton_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_encoder_princeton_deserialize(void* context, FlipperFormat* flipper_format); /** * Forced transmission stop. @@ -88,9 +89,9 @@ uint8_t subghz_protocol_decoder_princeton_get_hash_data(void* context); * @param context Pointer to a SubGhzProtocolDecoderPrinceton instance * @param flipper_format Pointer to a FlipperFormat instance * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success + * @return status */ -bool subghz_protocol_decoder_princeton_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_princeton_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); @@ -99,9 +100,10 @@ bool subghz_protocol_decoder_princeton_serialize( * Deserialize data SubGhzProtocolDecoderPrinceton. * @param context Pointer to a SubGhzProtocolDecoderPrinceton instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_protocol_decoder_princeton_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_decoder_princeton_deserialize(void* context, FlipperFormat* flipper_format); /** * Getting a textual representation of the received data. diff --git a/lib/subghz/protocols/raw.c b/lib/subghz/protocols/raw.c index 01a229047..eaad78b91 100644 --- a/lib/subghz/protocols/raw.c +++ b/lib/subghz/protocols/raw.c @@ -259,12 +259,13 @@ void subghz_protocol_decoder_raw_feed(void* context, bool level, uint32_t durati } } -bool subghz_protocol_decoder_raw_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_decoder_raw_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); UNUSED(context); UNUSED(flipper_format); //ToDo stub, for backwards compatibility - return true; + return SubGhzProtocolStatusOk; } void subghz_protocol_decoder_raw_get_string(void* context, FuriString* output) { @@ -342,25 +343,32 @@ void subghz_protocol_raw_gen_fff_data(FlipperFormat* flipper_format, const char* } while(false); } -bool subghz_protocol_encoder_raw_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_encoder_raw_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolEncoderRAW* instance = context; - bool res = false; + SubGhzProtocolStatus res = SubGhzProtocolStatusError; FuriString* temp_str; temp_str = furi_string_alloc(); do { if(!flipper_format_rewind(flipper_format)) { FURI_LOG_E(TAG, "Rewind error"); + res = SubGhzProtocolStatusErrorParserOthers; break; } if(!flipper_format_read_string(flipper_format, "File_name", temp_str)) { FURI_LOG_E(TAG, "Missing File_name"); + res = SubGhzProtocolStatusErrorParserOthers; break; } furi_string_set(instance->file_name, temp_str); - res = subghz_protocol_encoder_raw_worker_init(instance); + if(!subghz_protocol_encoder_raw_worker_init(instance)) { + res = SubGhzProtocolStatusErrorEncoderGetUpload; + break; + } + res = SubGhzProtocolStatusOk; } while(false); furi_string_free(temp_str); return res; diff --git a/lib/subghz/protocols/raw.h b/lib/subghz/protocols/raw.h index 44c7faec5..4f67a4e2f 100644 --- a/lib/subghz/protocols/raw.h +++ b/lib/subghz/protocols/raw.h @@ -73,9 +73,10 @@ void subghz_protocol_decoder_raw_feed(void* context, bool level, uint32_t durati * Deserialize data SubGhzProtocolDecoderRAW. * @param context Pointer to a SubGhzProtocolDecoderRAW instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_protocol_decoder_raw_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_decoder_raw_deserialize(void* context, FlipperFormat* flipper_format); /** * Getting a textual representation of the received data. @@ -132,9 +133,10 @@ void subghz_protocol_raw_gen_fff_data(FlipperFormat* flipper_format, const char* * Deserialize and generating an upload to send. * @param context Pointer to a SubGhzProtocolEncoderRAW instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_protocol_encoder_raw_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_encoder_raw_deserialize(void* context, FlipperFormat* flipper_format); /** * Getting the level and duration of the upload to be loaded into DMA. diff --git a/lib/subghz/protocols/scher_khan.c b/lib/subghz/protocols/scher_khan.c index a5de7d04b..b68e3c869 100644 --- a/lib/subghz/protocols/scher_khan.c +++ b/lib/subghz/protocols/scher_khan.c @@ -249,7 +249,7 @@ uint8_t subghz_protocol_decoder_scher_khan_get_hash_data(void* context) { &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } -bool subghz_protocol_decoder_scher_khan_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_scher_khan_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset) { @@ -258,7 +258,8 @@ bool subghz_protocol_decoder_scher_khan_serialize( return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); } -bool subghz_protocol_decoder_scher_khan_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_decoder_scher_khan_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolDecoderScherKhan* instance = context; return subghz_block_generic_deserialize(&instance->generic, flipper_format); diff --git a/lib/subghz/protocols/scher_khan.h b/lib/subghz/protocols/scher_khan.h index b7e84ea1f..58545069c 100644 --- a/lib/subghz/protocols/scher_khan.h +++ b/lib/subghz/protocols/scher_khan.h @@ -50,9 +50,9 @@ uint8_t subghz_protocol_decoder_scher_khan_get_hash_data(void* context); * @param context Pointer to a SubGhzProtocolDecoderScherKhan instance * @param flipper_format Pointer to a FlipperFormat instance * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success + * @return status */ -bool subghz_protocol_decoder_scher_khan_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_scher_khan_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); @@ -61,9 +61,10 @@ bool subghz_protocol_decoder_scher_khan_serialize( * Deserialize data SubGhzProtocolDecoderScherKhan. * @param context Pointer to a SubGhzProtocolDecoderScherKhan instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_protocol_decoder_scher_khan_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_decoder_scher_khan_deserialize(void* context, FlipperFormat* flipper_format); /** * Getting a textual representation of the received data. diff --git a/lib/subghz/protocols/secplus_v1.c b/lib/subghz/protocols/secplus_v1.c index 3ef95db36..55373cc21 100644 --- a/lib/subghz/protocols/secplus_v1.c +++ b/lib/subghz/protocols/secplus_v1.c @@ -264,18 +264,17 @@ static bool subghz_protocol_secplus_v1_encode(SubGhzProtocolEncoderSecPlus_v1* i return true; } -bool subghz_protocol_encoder_secplus_v1_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_encoder_secplus_v1_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolEncoderSecPlus_v1* instance = context; - bool res = false; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - FURI_LOG_E(TAG, "Deserialize error"); - break; - } - if(instance->generic.data_count_bit != - 2 * subghz_protocol_secplus_v1_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); + ret = subghz_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + 2 * subghz_protocol_secplus_v1_const.min_count_bit_for_found); + if(ret != SubGhzProtocolStatusOk) { break; } //optional parameter parameter @@ -283,9 +282,12 @@ bool subghz_protocol_encoder_secplus_v1_deserialize(void* context, FlipperFormat flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); if(!subghz_protocol_secplus_v1_encode(instance)) { + ret = SubGhzProtocolStatusErrorParserOthers; break; } if(!subghz_protocol_encoder_secplus_v1_get_upload(instance)) { + ret = SubGhzProtocolStatusErrorEncoderGetUpload; + ; break; } @@ -295,15 +297,14 @@ bool subghz_protocol_encoder_secplus_v1_deserialize(void* context, FlipperFormat } if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) { FURI_LOG_E(TAG, "Unable to add Key"); + ret = SubGhzProtocolStatusErrorParserKey; break; } instance->encoder.is_running = true; - - res = true; } while(false); - return res; + return ret; } void subghz_protocol_encoder_secplus_v1_stop(void* context) { @@ -516,7 +517,7 @@ uint8_t subghz_protocol_decoder_secplus_v1_get_hash_data(void* context) { &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } -bool subghz_protocol_decoder_secplus_v1_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_secplus_v1_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset) { @@ -525,22 +526,14 @@ bool subghz_protocol_decoder_secplus_v1_serialize( return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); } -bool subghz_protocol_decoder_secplus_v1_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_decoder_secplus_v1_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolDecoderSecPlus_v1* instance = context; - bool ret = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit != - 2 * subghz_protocol_secplus_v1_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); - break; - } - ret = true; - } while(false); - return ret; + return subghz_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + 2 * subghz_protocol_secplus_v1_const.min_count_bit_for_found); } bool subghz_protocol_secplus_v1_check_fixed(uint32_t fixed) { diff --git a/lib/subghz/protocols/secplus_v1.h b/lib/subghz/protocols/secplus_v1.h index 99480b62b..3490f2ca5 100644 --- a/lib/subghz/protocols/secplus_v1.h +++ b/lib/subghz/protocols/secplus_v1.h @@ -27,9 +27,10 @@ void subghz_protocol_encoder_secplus_v1_free(void* context); * Deserialize and generating an upload to send. * @param context Pointer to a SubGhzProtocolEncoderSecPlus_v1 instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_protocol_encoder_secplus_v1_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_encoder_secplus_v1_deserialize(void* context, FlipperFormat* flipper_format); /** * Forced transmission stop. @@ -83,9 +84,9 @@ uint8_t subghz_protocol_decoder_secplus_v1_get_hash_data(void* context); * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v1 instance * @param flipper_format Pointer to a FlipperFormat instance * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success + * @return status */ -bool subghz_protocol_decoder_secplus_v1_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_secplus_v1_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); @@ -94,9 +95,10 @@ bool subghz_protocol_decoder_secplus_v1_serialize( * Deserialize data SubGhzProtocolDecoderSecPlus_v1. * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v1 instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_protocol_decoder_secplus_v1_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_decoder_secplus_v1_deserialize(void* context, FlipperFormat* flipper_format); /** * Validation of fixed parts SubGhzProtocolDecoderSecPlus_v1. diff --git a/lib/subghz/protocols/secplus_v2.c b/lib/subghz/protocols/secplus_v2.c index bcef90dad..a55930552 100644 --- a/lib/subghz/protocols/secplus_v2.c +++ b/lib/subghz/protocols/secplus_v2.c @@ -379,7 +379,8 @@ static void subghz_protocol_secplus_v2_encode(SubGhzProtocolEncoderSecPlus_v2* i uint8_t roll_1[9] = {0}; uint8_t roll_2[9] = {0}; - instance->generic.cnt++; + instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult(); + //ToDo it is not known what value the counter starts if(instance->generic.cnt > 0xFFFFFFF) instance->generic.cnt = 0xE500000; uint32_t rolling = subghz_protocol_blocks_reverse_key(instance->generic.cnt, 28); @@ -503,24 +504,24 @@ static void instance->encoder.size_upload = index; } -bool subghz_protocol_encoder_secplus_v2_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_encoder_secplus_v2_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolEncoderSecPlus_v2* instance = context; - bool res = false; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - FURI_LOG_E(TAG, "Deserialize error"); - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_secplus_v2_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); + ret = subghz_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + subghz_protocol_secplus_v2_const.min_count_bit_for_found); + if(ret != SubGhzProtocolStatusOk) { break; } uint8_t key_data[sizeof(uint64_t)] = {0}; if(!flipper_format_read_hex( flipper_format, "Secplus_packet_1", key_data, sizeof(uint64_t))) { FURI_LOG_E(TAG, "Secplus_packet_1"); + ret = SubGhzProtocolStatusErrorParserOthers; break; } for(uint8_t i = 0; i < sizeof(uint64_t); i++) { @@ -541,6 +542,7 @@ bool subghz_protocol_encoder_secplus_v2_deserialize(void* context, FlipperFormat } if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) { FURI_LOG_E(TAG, "Unable to add Key"); + ret = SubGhzProtocolStatusErrorParserKey; break; } @@ -550,15 +552,14 @@ bool subghz_protocol_encoder_secplus_v2_deserialize(void* context, FlipperFormat if(!flipper_format_update_hex( flipper_format, "Secplus_packet_1", key_data, sizeof(uint64_t))) { FURI_LOG_E(TAG, "Unable to add Secplus_packet_1"); + ret = SubGhzProtocolStatusErrorParserOthers; break; } instance->encoder.is_running = true; - - res = true; } while(false); - return res; + return ret; } void subghz_protocol_encoder_secplus_v2_stop(void* context) { @@ -599,19 +600,20 @@ bool subghz_protocol_secplus_v2_create_data( instance->generic.data_count_bit = (uint8_t)subghz_protocol_secplus_v2_const.min_count_bit_for_found; subghz_protocol_secplus_v2_encode(instance); - bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + SubGhzProtocolStatus res = + subghz_block_generic_serialize(&instance->generic, flipper_format, preset); 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; } - if(res && + if((res == SubGhzProtocolStatusOk) && !flipper_format_write_hex(flipper_format, "Secplus_packet_1", key_data, sizeof(uint64_t))) { FURI_LOG_E(TAG, "Unable to add Secplus_packet_1"); - res = false; + res = SubGhzProtocolStatusError; } - return res; + return res == SubGhzProtocolStatusOk; } void* subghz_protocol_decoder_secplus_v2_alloc(SubGhzEnvironment* environment) { @@ -754,58 +756,59 @@ uint8_t subghz_protocol_decoder_secplus_v2_get_hash_data(void* context) { &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } -bool subghz_protocol_decoder_secplus_v2_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_secplus_v2_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderSecPlus_v2* instance = context; - bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + SubGhzProtocolStatus ret = + subghz_block_generic_serialize(&instance->generic, flipper_format, preset); 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; } - if(res && + if((ret == SubGhzProtocolStatusOk) && !flipper_format_write_hex(flipper_format, "Secplus_packet_1", key_data, sizeof(uint64_t))) { FURI_LOG_E(TAG, "Unable to add Secplus_packet_1"); - res = false; + ret = SubGhzProtocolStatusErrorParserOthers; } - return res; + return ret; } -bool subghz_protocol_decoder_secplus_v2_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_decoder_secplus_v2_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolDecoderSecPlus_v2* instance = context; - bool res = false; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - FURI_LOG_E(TAG, "Deserialize error"); - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_secplus_v2_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); + ret = subghz_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + subghz_protocol_secplus_v2_const.min_count_bit_for_found); + if(ret != SubGhzProtocolStatusOk) { break; } if(!flipper_format_rewind(flipper_format)) { FURI_LOG_E(TAG, "Rewind error"); + ret = SubGhzProtocolStatusErrorParserOthers; break; } uint8_t key_data[sizeof(uint64_t)] = {0}; if(!flipper_format_read_hex( flipper_format, "Secplus_packet_1", key_data, sizeof(uint64_t))) { FURI_LOG_E(TAG, "Missing Secplus_packet_1"); + ret = SubGhzProtocolStatusErrorParserOthers; break; } for(uint8_t i = 0; i < sizeof(uint64_t); i++) { instance->secplus_packet_1 = instance->secplus_packet_1 << 8 | key_data[i]; } - res = true; } while(false); - return res; + return ret; } void subghz_protocol_decoder_secplus_v2_get_string(void* context, FuriString* output) { diff --git a/lib/subghz/protocols/secplus_v2.h b/lib/subghz/protocols/secplus_v2.h index bea8cdb5d..0eea732af 100644 --- a/lib/subghz/protocols/secplus_v2.h +++ b/lib/subghz/protocols/secplus_v2.h @@ -27,9 +27,10 @@ void subghz_protocol_encoder_secplus_v2_free(void* context); * Deserialize and generating an upload to send. * @param context Pointer to a SubGhzProtocolEncoderSecPlus_v2 instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_protocol_encoder_secplus_v2_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_encoder_secplus_v2_deserialize(void* context, FlipperFormat* flipper_format); /** * Forced transmission stop. @@ -102,9 +103,9 @@ uint8_t subghz_protocol_decoder_secplus_v2_get_hash_data(void* context); * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v2 instance * @param flipper_format Pointer to a FlipperFormat instance * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success + * @return status */ -bool subghz_protocol_decoder_secplus_v2_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_secplus_v2_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); @@ -113,9 +114,10 @@ bool subghz_protocol_decoder_secplus_v2_serialize( * Deserialize data SubGhzProtocolDecoderSecPlus_v2. * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v2 instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_protocol_decoder_secplus_v2_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_decoder_secplus_v2_deserialize(void* context, FlipperFormat* flipper_format); /** * Getting a textual representation of the received data. diff --git a/lib/subghz/protocols/smc5326.c b/lib/subghz/protocols/smc5326.c index 9c9b5d4fd..bfb36b76a 100644 --- a/lib/subghz/protocols/smc5326.c +++ b/lib/subghz/protocols/smc5326.c @@ -155,39 +155,41 @@ static bool subghz_protocol_encoder_smc5326_get_upload(SubGhzProtocolEncoderSMC5 return true; } -bool subghz_protocol_encoder_smc5326_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_encoder_smc5326_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolEncoderSMC5326* instance = context; - bool res = false; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - FURI_LOG_E(TAG, "Deserialize error"); + ret = subghz_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + subghz_protocol_smc5326_const.min_count_bit_for_found); + if(ret != SubGhzProtocolStatusOk) { break; } if(!flipper_format_rewind(flipper_format)) { FURI_LOG_E(TAG, "Rewind error"); + ret = SubGhzProtocolStatusErrorParserOthers; 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_smc5326_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); + ret = SubGhzProtocolStatusErrorParserTe; break; } //optional parameter parameter flipper_format_read_uint32( flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); - if(!subghz_protocol_encoder_smc5326_get_upload(instance)) break; + if(!subghz_protocol_encoder_smc5326_get_upload(instance)) { + ret = SubGhzProtocolStatusErrorEncoderGetUpload; + break; + } instance->encoder.is_running = true; - - res = true; } while(false); - return res; + return ret; } void subghz_protocol_encoder_smc5326_stop(void* context) { @@ -313,46 +315,48 @@ uint8_t subghz_protocol_decoder_smc5326_get_hash_data(void* context) { &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } -bool subghz_protocol_decoder_smc5326_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_smc5326_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderSMC5326* 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)) { + SubGhzProtocolStatus ret = + subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + if((ret == SubGhzProtocolStatusOk) && + !flipper_format_write_uint32(flipper_format, "TE", &instance->te, 1)) { FURI_LOG_E(TAG, "Unable to add TE"); - res = false; + ret = SubGhzProtocolStatusErrorParserTe; } - return res; + return ret; } -bool subghz_protocol_decoder_smc5326_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_decoder_smc5326_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolDecoderSMC5326* instance = context; - bool res = false; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - FURI_LOG_E(TAG, "Deserialize error"); - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_smc5326_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); + ret = subghz_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + subghz_protocol_smc5326_const.min_count_bit_for_found); + if(ret != SubGhzProtocolStatusOk) { break; } if(!flipper_format_rewind(flipper_format)) { FURI_LOG_E(TAG, "Rewind error"); + ret = SubGhzProtocolStatusErrorParserOthers; break; } if(!flipper_format_read_uint32(flipper_format, "TE", (uint32_t*)&instance->te, 1)) { FURI_LOG_E(TAG, "Missing TE"); + ret = SubGhzProtocolStatusErrorParserTe; break; } - res = true; } while(false); - return res; + return ret; } static void subghz_protocol_smc5326_get_event_serialize(uint8_t event, FuriString* output) { diff --git a/lib/subghz/protocols/smc5326.h b/lib/subghz/protocols/smc5326.h index ddc954bd5..911226cf9 100644 --- a/lib/subghz/protocols/smc5326.h +++ b/lib/subghz/protocols/smc5326.h @@ -28,9 +28,10 @@ void subghz_protocol_encoder_smc5326_free(void* context); * Deserialize and generating an upload to send. * @param context Pointer to a SubGhzProtocolEncoderSMC5326 instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_protocol_encoder_smc5326_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_encoder_smc5326_deserialize(void* context, FlipperFormat* flipper_format); /** * Forced transmission stop. @@ -84,9 +85,9 @@ uint8_t subghz_protocol_decoder_smc5326_get_hash_data(void* context); * @param context Pointer to a SubGhzProtocolDecoderSMC5326 instance * @param flipper_format Pointer to a FlipperFormat instance * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success + * @return status */ -bool subghz_protocol_decoder_smc5326_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_smc5326_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); @@ -95,9 +96,10 @@ bool subghz_protocol_decoder_smc5326_serialize( * Deserialize data SubGhzProtocolDecoderSMC5326. * @param context Pointer to a SubGhzProtocolDecoderSMC5326 instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_protocol_decoder_smc5326_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_decoder_smc5326_deserialize(void* context, FlipperFormat* flipper_format); /** * Getting a textual representation of the received data. diff --git a/lib/subghz/protocols/somfy_keytis.c b/lib/subghz/protocols/somfy_keytis.c index ab9202cc3..dd860a61d 100644 --- a/lib/subghz/protocols/somfy_keytis.c +++ b/lib/subghz/protocols/somfy_keytis.c @@ -131,7 +131,11 @@ static bool instance->generic.serial = data & 0xFFFFFF; if(instance->generic.cnt < 0xFFFF) { - instance->generic.cnt++; + if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) >= 0xFFFF) { + instance->generic.cnt = 0; + } else { + instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult(); + } } else if(instance->generic.cnt >= 0xFFFF) { instance->generic.cnt = 0; } @@ -188,7 +192,8 @@ bool subghz_protocol_somfy_keytis_create_data( instance->generic.data_count_bit = 80; bool res = subghz_protocol_somfy_keytis_gen_data(instance, btn); if(res) { - res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + return SubGhzProtocolStatusOk == + subghz_block_generic_serialize(&instance->generic, flipper_format, preset); } return res; } @@ -386,12 +391,14 @@ static bool subghz_protocol_encoder_somfy_keytis_get_upload( return true; } -bool subghz_protocol_encoder_somfy_keytis_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_encoder_somfy_keytis_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolEncoderSomfyKeytis* instance = context; - bool res = false; + SubGhzProtocolStatus res = SubGhzProtocolStatusError; do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + if(SubGhzProtocolStatusOk != + subghz_block_generic_deserialize(&instance->generic, flipper_format)) { FURI_LOG_E(TAG, "Deserialize error"); break; } @@ -417,7 +424,7 @@ bool subghz_protocol_encoder_somfy_keytis_deserialize(void* context, FlipperForm instance->encoder.is_running = true; - res = true; + res = SubGhzProtocolStatusOk; } while(false); return res; @@ -725,37 +732,39 @@ uint8_t subghz_protocol_decoder_somfy_keytis_get_hash_data(void* context) { &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } -bool subghz_protocol_decoder_somfy_keytis_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_somfy_keytis_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderSomfyKeytis* instance = context; - bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); - if(res && !flipper_format_write_uint32( - flipper_format, "Duration_Counter", &instance->press_duration_counter, 1)) { + SubGhzProtocolStatus ret = + subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + if((ret == SubGhzProtocolStatusOk) && + !flipper_format_write_uint32( + flipper_format, "Duration_Counter", &instance->press_duration_counter, 1)) { FURI_LOG_E(TAG, "Unable to add Duration_Counter"); - res = false; + ret = SubGhzProtocolStatusErrorParserOthers; } - return res; + return ret; } -bool subghz_protocol_decoder_somfy_keytis_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_decoder_somfy_keytis_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolDecoderSomfyKeytis* instance = context; - bool res = false; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - FURI_LOG_E(TAG, "Deserialize error"); - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_somfy_keytis_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); + ret = subghz_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + subghz_protocol_somfy_keytis_const.min_count_bit_for_found); + if(ret != SubGhzProtocolStatusOk) { break; } if(!flipper_format_rewind(flipper_format)) { FURI_LOG_E(TAG, "Rewind error"); + ret = SubGhzProtocolStatusErrorParserOthers; break; } if(!flipper_format_read_uint32( @@ -764,12 +773,12 @@ bool subghz_protocol_decoder_somfy_keytis_deserialize(void* context, FlipperForm (uint32_t*)&instance->press_duration_counter, 1)) { FURI_LOG_E(TAG, "Missing Duration_Counter"); + ret = SubGhzProtocolStatusErrorParserOthers; break; } - res = true; } while(false); - return res; + return ret; } void subghz_protocol_decoder_somfy_keytis_get_string(void* context, FuriString* output) { diff --git a/lib/subghz/protocols/somfy_keytis.h b/lib/subghz/protocols/somfy_keytis.h index b08da3641..0e339c889 100644 --- a/lib/subghz/protocols/somfy_keytis.h +++ b/lib/subghz/protocols/somfy_keytis.h @@ -48,7 +48,8 @@ bool subghz_protocol_somfy_keytis_create_data( * @param flipper_format Pointer to a FlipperFormat instance * @return true On success */ -bool subghz_protocol_encoder_somfy_keytis_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_encoder_somfy_keytis_deserialize(void* context, FlipperFormat* flipper_format); /** * Forced transmission stop. @@ -102,9 +103,9 @@ uint8_t subghz_protocol_decoder_somfy_keytis_get_hash_data(void* context); * @param context Pointer to a SubGhzProtocolDecoderSomfyKeytis instance * @param flipper_format Pointer to a FlipperFormat instance * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success + * @return status */ -bool subghz_protocol_decoder_somfy_keytis_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_somfy_keytis_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); @@ -113,9 +114,10 @@ bool subghz_protocol_decoder_somfy_keytis_serialize( * Deserialize data SubGhzProtocolDecoderSomfyKeytis. * @param context Pointer to a SubGhzProtocolDecoderSomfyKeytis instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_protocol_decoder_somfy_keytis_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_decoder_somfy_keytis_deserialize(void* context, FlipperFormat* flipper_format); /** * Getting a textual representation of the received data. diff --git a/lib/subghz/protocols/somfy_telis.c b/lib/subghz/protocols/somfy_telis.c index 96997c581..0294a1922 100644 --- a/lib/subghz/protocols/somfy_telis.c +++ b/lib/subghz/protocols/somfy_telis.c @@ -73,6 +73,25 @@ const SubGhzProtocol subghz_protocol_somfy_telis = { .encoder = &subghz_protocol_somfy_telis_encoder, }; +static uint8_t st_btn_temp_id; +static uint8_t st_btn_temp_id_original; + +void somfy_telis_set_btn(uint8_t b) { + st_btn_temp_id = b; +} + +uint8_t somfy_telis_get_original_btn() { + return st_btn_temp_id_original; +} + +uint8_t somfy_telis_get_custom_btn() { + return st_btn_temp_id; +} + +void somfy_telis_reset_original_btn() { + st_btn_temp_id_original = 0; +} + void* subghz_protocol_encoder_somfy_telis_alloc(SubGhzEnvironment* environment) { UNUSED(environment); SubGhzProtocolEncoderSomfyTelis* instance = malloc(sizeof(SubGhzProtocolEncoderSomfyTelis)); @@ -95,23 +114,104 @@ void subghz_protocol_encoder_somfy_telis_free(void* context) { free(instance); } -static bool - subghz_protocol_somfy_telis_gen_data(SubGhzProtocolEncoderSomfyTelis* instance, uint8_t btn) { - UNUSED(btn); +static bool subghz_protocol_somfy_telis_gen_data( + SubGhzProtocolEncoderSomfyTelis* instance, + uint8_t btn, + bool new_remote) { + // If we doing a clone we will use its data uint64_t data = instance->generic.data ^ (instance->generic.data >> 8); - instance->generic.btn = (data >> 44) & 0xF; // ctrl - instance->generic.cnt = (data >> 24) & 0xFFFF; // rolling code - instance->generic.serial = data & 0xFFFFFF; // address + if(!new_remote) { + instance->generic.btn = (data >> 44) & 0xF; // ctrl + btn = instance->generic.btn; + instance->generic.cnt = (data >> 24) & 0xFFFF; // rolling code + instance->generic.serial = data & 0xFFFFFF; // address + } + + // Save original button for later use + if(st_btn_temp_id_original == 0) { + st_btn_temp_id_original = btn; + } + + // Set custom button + if(st_btn_temp_id == 1) { + switch(st_btn_temp_id_original) { + case 0x1: + btn = 0x2; + break; + case 0x2: + btn = 0x1; + break; + case 0x4: + btn = 0x1; + break; + case 0x8: + btn = 0x1; + break; + + default: + break; + } + } + if(st_btn_temp_id == 2) { + switch(st_btn_temp_id_original) { + case 0x1: + btn = 0x4; + break; + case 0x2: + btn = 0x4; + break; + case 0x4: + btn = 0x2; + break; + case 0x8: + btn = 0x4; + break; + + default: + break; + } + } + if(st_btn_temp_id == 3) { + switch(st_btn_temp_id_original) { + case 0x1: + btn = 0x8; + break; + case 0x2: + btn = 0x8; + break; + case 0x4: + btn = 0x8; + break; + case 0x8: + btn = 0x2; + break; + + default: + break; + } + } + + if((st_btn_temp_id == 0) && (st_btn_temp_id_original != 0)) { + btn = st_btn_temp_id_original; + } if(instance->generic.cnt < 0xFFFF) { - instance->generic.cnt++; + if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) >= 0xFFFF) { + instance->generic.cnt = 0; + } else { + instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult(); + } } else if(instance->generic.cnt >= 0xFFFF) { instance->generic.cnt = 0; } uint8_t frame[7]; - frame[0] = data >> 48; - frame[1] = instance->generic.btn << 4; + if(!new_remote) { + frame[0] = data >> 48; + } else { + frame[0] = 0xA7; + } + frame[1] = btn << 4; frame[2] = instance->generic.cnt >> 8; frame[3] = instance->generic.cnt; frame[4] = instance->generic.serial >> 16; @@ -150,9 +250,10 @@ bool subghz_protocol_somfy_telis_create_data( instance->generic.serial = serial; instance->generic.cnt = cnt; instance->generic.data_count_bit = 56; - bool res = subghz_protocol_somfy_telis_gen_data(instance, btn); + bool res = subghz_protocol_somfy_telis_gen_data(instance, btn, true); if(res) { - res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + return SubGhzProtocolStatusOk == + subghz_block_generic_serialize(&instance->generic, flipper_format, preset); } return res; } @@ -168,7 +269,7 @@ static bool subghz_protocol_encoder_somfy_telis_get_upload( furi_assert(instance); //gen new key - if(subghz_protocol_somfy_telis_gen_data(instance, btn)) { + if(subghz_protocol_somfy_telis_gen_data(instance, btn, false)) { //ToDo if you need to add a callback to automatically update the data on the display } else { return false; @@ -290,12 +391,14 @@ static bool subghz_protocol_encoder_somfy_telis_get_upload( return true; } -bool subghz_protocol_encoder_somfy_telis_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_encoder_somfy_telis_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolEncoderSomfyTelis* instance = context; - bool res = false; + SubGhzProtocolStatus res = SubGhzProtocolStatusError; do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + if(SubGhzProtocolStatusOk != + subghz_block_generic_deserialize(&instance->generic, flipper_format)) { FURI_LOG_E(TAG, "Deserialize error"); break; } @@ -321,7 +424,7 @@ bool subghz_protocol_encoder_somfy_telis_deserialize(void* context, FlipperForma instance->encoder.is_running = true; - res = true; + res = SubGhzProtocolStatusOk; } while(false); return res; @@ -579,6 +682,11 @@ static void subghz_protocol_somfy_telis_check_remote_controller(SubGhzBlockGener instance->btn = (data >> 44) & 0xF; // ctrl instance->cnt = (data >> 24) & 0xFFFF; // rolling code instance->serial = data & 0xFFFFFF; // address + + // Save original button for later use + if(st_btn_temp_id_original == 0) { + st_btn_temp_id_original = instance->btn; + } } /** @@ -613,7 +721,7 @@ uint8_t subghz_protocol_decoder_somfy_telis_get_hash_data(void* context) { &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } -bool subghz_protocol_decoder_somfy_telis_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_somfy_telis_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset) { @@ -622,22 +730,14 @@ bool subghz_protocol_decoder_somfy_telis_serialize( return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); } -bool subghz_protocol_decoder_somfy_telis_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_decoder_somfy_telis_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolDecoderSomfyTelis* instance = context; - bool ret = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_somfy_telis_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); - break; - } - ret = true; - } while(false); - return ret; + return subghz_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + subghz_protocol_somfy_telis_const.min_count_bit_for_found); } void subghz_protocol_decoder_somfy_telis_get_string(void* context, FuriString* output) { diff --git a/lib/subghz/protocols/somfy_telis.h b/lib/subghz/protocols/somfy_telis.h index b5e989866..36f6c70b5 100644 --- a/lib/subghz/protocols/somfy_telis.h +++ b/lib/subghz/protocols/somfy_telis.h @@ -11,6 +11,14 @@ extern const SubGhzProtocolDecoder subghz_protocol_somfy_telis_decoder; extern const SubGhzProtocolEncoder subghz_protocol_somfy_telis_encoder; extern const SubGhzProtocol subghz_protocol_somfy_telis; +// Custom buttons +void somfy_telis_set_btn(uint8_t b); + +uint8_t somfy_telis_get_original_btn(); +uint8_t somfy_telis_get_custom_btn(); + +void somfy_telis_reset_original_btn(); + /** * Allocate SubGhzProtocolEncoderSomfyTelis. * @param environment Pointer to a SubGhzEnvironment instance @@ -48,7 +56,8 @@ bool subghz_protocol_somfy_telis_create_data( * @param flipper_format Pointer to a FlipperFormat instance * @return true On success */ -bool subghz_protocol_encoder_somfy_telis_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_encoder_somfy_telis_deserialize(void* context, FlipperFormat* flipper_format); /** * Forced transmission stop. @@ -102,9 +111,9 @@ uint8_t subghz_protocol_decoder_somfy_telis_get_hash_data(void* context); * @param context Pointer to a SubGhzProtocolDecoderSomfyTelis instance * @param flipper_format Pointer to a FlipperFormat instance * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success + * @return status */ -bool subghz_protocol_decoder_somfy_telis_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_somfy_telis_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); @@ -113,9 +122,10 @@ bool subghz_protocol_decoder_somfy_telis_serialize( * Deserialize data SubGhzProtocolDecoderSomfyTelis. * @param context Pointer to a SubGhzProtocolDecoderSomfyTelis instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_protocol_decoder_somfy_telis_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_decoder_somfy_telis_deserialize(void* context, FlipperFormat* flipper_format); /** * Getting a textual representation of the received data. diff --git a/lib/subghz/protocols/star_line.c b/lib/subghz/protocols/star_line.c index 3066c6e2b..5f96d3fcb 100644 --- a/lib/subghz/protocols/star_line.c +++ b/lib/subghz/protocols/star_line.c @@ -139,7 +139,11 @@ void subghz_protocol_encoder_star_line_free(void* context) { static bool subghz_protocol_star_line_gen_data(SubGhzProtocolEncoderStarLine* instance, uint8_t btn) { if(instance->generic.cnt < 0xFFFF) { - instance->generic.cnt++; + if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) >= 0xFFFF) { + instance->generic.cnt = 0; + } else { + instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult(); + } } else if(instance->generic.cnt >= 0xFFFF) { instance->generic.cnt = 0; } @@ -217,7 +221,8 @@ bool subghz_protocol_star_line_create_data( instance->generic.data_count_bit = 64; bool res = subghz_protocol_star_line_gen_data(instance, btn); if(res) { - res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + return SubGhzProtocolStatusOk == + subghz_block_generic_serialize(&instance->generic, flipper_format, preset); } return res; } @@ -276,12 +281,14 @@ static bool subghz_protocol_encoder_star_line_get_upload( return true; } -bool subghz_protocol_encoder_star_line_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_encoder_star_line_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolEncoderStarLine* instance = context; - bool res = false; + SubGhzProtocolStatus res = SubGhzProtocolStatusError; do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + if(SubGhzProtocolStatusOk != + subghz_block_generic_deserialize(&instance->generic, flipper_format)) { FURI_LOG_E(TAG, "Deserialize error"); break; } @@ -319,7 +326,7 @@ bool subghz_protocol_encoder_star_line_deserialize(void* context, FlipperFormat* instance->encoder.is_running = true; - res = true; + res = SubGhzProtocolStatusOk; } while(false); return res; @@ -698,7 +705,7 @@ uint8_t subghz_protocol_decoder_star_line_get_hash_data(void* context) { &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); } -bool subghz_protocol_decoder_star_line_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_star_line_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset) { @@ -706,27 +713,32 @@ bool subghz_protocol_decoder_star_line_serialize( SubGhzProtocolDecoderStarLine* instance = context; subghz_protocol_star_line_check_remote_controller( &instance->generic, instance->keystore, &instance->manufacture_name); - bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + SubGhzProtocolStatus ret = + subghz_block_generic_serialize(&instance->generic, flipper_format, preset); - if(res && !flipper_format_write_string_cstr( - flipper_format, "Manufacture", instance->manufacture_name)) { + if((ret == SubGhzProtocolStatusOk) && + !flipper_format_write_string_cstr( + flipper_format, "Manufacture", instance->manufacture_name)) { FURI_LOG_E(TAG, "Unable to add manufacture name"); - res = false; + ret = SubGhzProtocolStatusErrorParserOthers; } - if(res && instance->generic.data_count_bit != - subghz_protocol_star_line_const.min_count_bit_for_found) { + if((ret == SubGhzProtocolStatusOk) && + instance->generic.data_count_bit != + subghz_protocol_star_line_const.min_count_bit_for_found) { FURI_LOG_E(TAG, "Wrong number of bits in key"); - res = false; + ret = SubGhzProtocolStatusErrorParserOthers; } - return res; + return ret; } -bool subghz_protocol_decoder_star_line_deserialize(void* context, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_protocol_decoder_star_line_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolDecoderStarLine* instance = context; - bool res = false; + SubGhzProtocolStatus res = SubGhzProtocolStatusError; do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + if(SubGhzProtocolStatusOk != + subghz_block_generic_deserialize(&instance->generic, flipper_format)) { FURI_LOG_E(TAG, "Deserialize error"); break; } @@ -740,7 +752,7 @@ bool subghz_protocol_decoder_star_line_deserialize(void* context, FlipperFormat* FURI_LOG_D(TAG, "DECODER: Missing Manufacture"); } - res = true; + res = SubGhzProtocolStatusOk; } while(false); return res; diff --git a/lib/subghz/protocols/star_line.h b/lib/subghz/protocols/star_line.h index e8873d41a..901b82f7c 100644 --- a/lib/subghz/protocols/star_line.h +++ b/lib/subghz/protocols/star_line.h @@ -54,7 +54,8 @@ bool subghz_protocol_star_line_create_data( * @param flipper_format Pointer to a FlipperFormat instance * @return true On success */ -bool subghz_protocol_encoder_star_line_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_encoder_star_line_deserialize(void* context, FlipperFormat* flipper_format); /** * Forced transmission stop. @@ -108,9 +109,9 @@ uint8_t subghz_protocol_decoder_star_line_get_hash_data(void* context); * @param context Pointer to a SubGhzProtocolDecoderStarLine instance * @param flipper_format Pointer to a FlipperFormat instance * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success + * @return status */ -bool subghz_protocol_decoder_star_line_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_star_line_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); @@ -119,9 +120,10 @@ bool subghz_protocol_decoder_star_line_serialize( * Deserialize data SubGhzProtocolDecoderStarLine. * @param context Pointer to a SubGhzProtocolDecoderStarLine instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_protocol_decoder_star_line_deserialize(void* context, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_protocol_decoder_star_line_deserialize(void* context, FlipperFormat* flipper_format); /** * Getting a textual representation of the received data. diff --git a/lib/subghz/subghz_file_encoder_worker.c b/lib/subghz/subghz_file_encoder_worker.c index a8c6519ef..8bc6e8446 100644 --- a/lib/subghz/subghz_file_encoder_worker.c +++ b/lib/subghz/subghz_file_encoder_worker.c @@ -16,7 +16,7 @@ struct SubGhzFileEncoderWorker { FlipperFormat* flipper_format; volatile bool worker_running; - volatile bool worker_stoping; + volatile bool worker_stopping; bool level; bool is_storage_slow; FuriString* str_data; @@ -105,7 +105,7 @@ LevelDuration subghz_file_encoder_worker_get_level_duration(void* context) { } else if(duration == 0) { //-V547 level_duration = level_duration_reset(); FURI_LOG_I(TAG, "Stop transmission"); - instance->worker_stoping = true; + instance->worker_stopping = true; } return level_duration; } else { @@ -142,7 +142,7 @@ static int32_t subghz_file_encoder_worker_thread(void* context) { //skip the end of the previous line "\n" stream_seek(stream, 1, StreamOffsetFromCurrent); res = true; - instance->worker_stoping = false; + instance->worker_stopping = false; FURI_LOG_I(TAG, "Start transmission"); } while(0); @@ -174,7 +174,7 @@ static int32_t subghz_file_encoder_worker_thread(void* context) { } FURI_LOG_I(TAG, "End transmission"); while(instance->worker_running) { - if(instance->worker_stoping) { + if(instance->worker_stopping) { if(instance->callback_end) instance->callback_end(instance->context_end); } furi_delay_ms(50); @@ -198,7 +198,7 @@ SubGhzFileEncoderWorker* subghz_file_encoder_worker_alloc() { instance->str_data = furi_string_alloc(); instance->file_path = furi_string_alloc(); instance->level = false; - instance->worker_stoping = true; + instance->worker_stopping = true; return instance; } diff --git a/lib/subghz/subghz_keystore.h b/lib/subghz/subghz_keystore.h index 43f8356d0..06ae8adae 100644 --- a/lib/subghz/subghz_keystore.h +++ b/lib/subghz/subghz_keystore.h @@ -77,4 +77,4 @@ bool subghz_keystore_raw_get_data(const char* file_name, size_t offset, uint8_t* #ifdef __cplusplus } -#endif \ No newline at end of file +#endif diff --git a/lib/subghz/subghz_setting.c b/lib/subghz/subghz_setting.c index 05b6a74ad..0b3c8939e 100644 --- a/lib/subghz/subghz_setting.c +++ b/lib/subghz/subghz_setting.c @@ -3,14 +3,10 @@ //#include "subghz_i.h" #include -#include #include #define TAG "SubGhzSetting" -#define SUBGHZ_SETTING_FILE_TYPE "Flipper SubGhz Setting File" -#define SUBGHZ_SETTING_FILE_VERSION 1 - #define FREQUENCY_FLAG_DEFAULT (1 << 31) #define FREQUENCY_MASK (0xFFFFFFFF ^ FREQUENCY_FLAG_DEFAULT) @@ -97,10 +93,6 @@ ARRAY_DEF(SubGhzSettingCustomPresetItemArray, SubGhzSettingCustomPresetItem, M_P #define M_OPL_SubGhzSettingCustomPresetItemArray_t() \ ARRAY_OPLIST(SubGhzSettingCustomPresetItemArray, M_POD_OPLIST) -LIST_DEF(FrequencyList, uint32_t) - -#define M_OPL_FrequencyList_t() LIST_OPLIST(FrequencyList) - typedef struct { SubGhzSettingCustomPresetItemArray_t data; } SubGhzSettingCustomPresetStruct; diff --git a/lib/subghz/subghz_setting.h b/lib/subghz/subghz_setting.h index 3cb07ff6d..60b751e35 100644 --- a/lib/subghz/subghz_setting.h +++ b/lib/subghz/subghz_setting.h @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -10,8 +11,15 @@ extern "C" { #endif +#define SUBGHZ_SETTING_FILE_TYPE "Flipper SubGhz Setting File" +#define SUBGHZ_SETTING_FILE_VERSION 1 + #define SUBGHZ_SETTING_DEFAULT_PRESET_COUNT 4 +LIST_DEF(FrequencyList, uint32_t) + +#define M_OPL_FrequencyList_t() LIST_OPLIST(FrequencyList) + typedef struct SubGhzSetting SubGhzSetting; SubGhzSetting* subghz_setting_alloc(void); diff --git a/lib/subghz/subghz_tx_rx_worker.c b/lib/subghz/subghz_tx_rx_worker.c index e380fc967..21568dab1 100644 --- a/lib/subghz/subghz_tx_rx_worker.c +++ b/lib/subghz/subghz_tx_rx_worker.c @@ -16,7 +16,7 @@ struct SubGhzTxRxWorker { FuriStreamBuffer* stream_rx; volatile bool worker_running; - volatile bool worker_stoping; + volatile bool worker_stopping; SubGhzTxRxWorkerStatus status; @@ -166,7 +166,7 @@ static int32_t subghz_tx_rx_worker_thread(void* context) { subghz_tx_rx_worker_tx(instance, data, size_tx); } } else { - //recive + //receive if(subghz_tx_rx_worker_rx(instance, data, size_rx)) { if(furi_stream_buffer_spaces_available(instance->stream_rx) >= size_rx[0]) { if(instance->callback_have_read && @@ -211,7 +211,7 @@ SubGhzTxRxWorker* subghz_tx_rx_worker_alloc() { furi_stream_buffer_alloc(sizeof(uint8_t) * SUBGHZ_TXRX_WORKER_BUF_SIZE, sizeof(uint8_t)); instance->status = SubGhzTxRxWorkerStatusIDLE; - instance->worker_stoping = true; + instance->worker_stopping = true; return instance; } diff --git a/lib/subghz/transmitter.c b/lib/subghz/transmitter.c index 8507ee054..81be143b5 100644 --- a/lib/subghz/transmitter.c +++ b/lib/subghz/transmitter.c @@ -47,9 +47,10 @@ bool subghz_transmitter_stop(SubGhzTransmitter* instance) { return ret; } -bool subghz_transmitter_deserialize(SubGhzTransmitter* instance, FlipperFormat* flipper_format) { +SubGhzProtocolStatus + subghz_transmitter_deserialize(SubGhzTransmitter* instance, FlipperFormat* flipper_format) { furi_assert(instance); - bool ret = false; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; if(instance->protocol && instance->protocol->encoder && instance->protocol->encoder->deserialize) { ret = diff --git a/lib/subghz/transmitter.h b/lib/subghz/transmitter.h index cce98a463..a1c4cda08 100644 --- a/lib/subghz/transmitter.h +++ b/lib/subghz/transmitter.h @@ -39,9 +39,10 @@ bool subghz_transmitter_stop(SubGhzTransmitter* instance); * Deserialize and generating an upload to send. * @param instance Pointer to a SubGhzTransmitter instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success + * @return status */ -bool subghz_transmitter_deserialize(SubGhzTransmitter* instance, FlipperFormat* flipper_format); +SubGhzProtocolStatus + subghz_transmitter_deserialize(SubGhzTransmitter* instance, FlipperFormat* flipper_format); /** * Getting the level and duration of the upload to be loaded into DMA. diff --git a/lib/subghz/types.h b/lib/subghz/types.h index 9d121dc3c..e0ed95654 100644 --- a/lib/subghz/types.h +++ b/lib/subghz/types.h @@ -29,14 +29,36 @@ typedef struct { size_t data_size; } SubGhzRadioPreset; +typedef enum { + SubGhzProtocolStatusOk = 0, + // Errors + SubGhzProtocolStatusError = (-1), ///< General unclassified error + // Serialize/De-serialize + SubGhzProtocolStatusErrorParserHeader = (-2), ///< Missing or invalid file header + SubGhzProtocolStatusErrorParserFrequency = (-3), ///< Missing `Frequency` + SubGhzProtocolStatusErrorParserPreset = (-4), ///< Missing `Preset` + SubGhzProtocolStatusErrorParserCustomPreset = (-5), ///< Missing `Custom_preset_module` + SubGhzProtocolStatusErrorParserProtocolName = (-6), ///< Missing `Protocol` name + SubGhzProtocolStatusErrorParserBitCount = (-7), ///< Missing `Bit` + SubGhzProtocolStatusErrorParserKey = (-8), ///< Missing `Key` + SubGhzProtocolStatusErrorParserTe = (-9), ///< Missing `Te` + SubGhzProtocolStatusErrorParserOthers = (-10), ///< Missing some other mandatory keys + // Invalid data + SubGhzProtocolStatusErrorValueBitCount = (-11), ///< Invalid bit count value + // Encoder issue + SubGhzProtocolStatusErrorEncoderGetUpload = (-12), ///< Payload encoder failure + // Special Values + SubGhzProtocolStatusReserved = 0x7FFFFFFF, ///< Prevents enum down-size compiler optimization. +} SubGhzProtocolStatus; + // Allocator and Deallocator typedef void* (*SubGhzAlloc)(SubGhzEnvironment* environment); typedef void (*SubGhzFree)(void* context); // Serialize and Deserialize -typedef bool ( +typedef SubGhzProtocolStatus ( *SubGhzSerialize)(void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); -typedef bool (*SubGhzDeserialize)(void* context, FlipperFormat* flipper_format); +typedef SubGhzProtocolStatus (*SubGhzDeserialize)(void* context, FlipperFormat* flipper_format); // Decoder specific typedef void (*SubGhzDecoderFeed)(void* decoder, bool level, uint32_t duration); diff --git a/lib/toolbox/SConscript b/lib/toolbox/SConscript index 9234f1e80..c01e32a6b 100644 --- a/lib/toolbox/SConscript +++ b/lib/toolbox/SConscript @@ -21,13 +21,13 @@ env.Append( File("saved_struct.h"), File("version.h"), File("float_tools.h"), - File("value_index.h"), File("tar/tar_archive.h"), File("stream/stream.h"), File("stream/file_stream.h"), File("stream/string_stream.h"), File("stream/buffered_file_stream.h"), File("protocols/protocol_dict.h"), + File("pretty_format.h"), ], ) diff --git a/lib/toolbox/dir_walk.c b/lib/toolbox/dir_walk.c index e5a3cf32b..509ceb5b4 100644 --- a/lib/toolbox/dir_walk.c +++ b/lib/toolbox/dir_walk.c @@ -83,7 +83,7 @@ static DirWalkResult end = true; } - if((info.flags & FSF_DIRECTORY) && dir_walk->recursive) { + if(file_info_is_dir(&info) && dir_walk->recursive) { // step into DirIndexList_push_back(dir_walk->index_list, dir_walk->current_index); dir_walk->current_index = 0; diff --git a/lib/toolbox/level_duration.h b/lib/toolbox/level_duration.h index 9406cef31..7618344ac 100644 --- a/lib/toolbox/level_duration.h +++ b/lib/toolbox/level_duration.h @@ -13,8 +13,8 @@ #define LEVEL_DURATION_RESERVED 0x800000U typedef struct { - uint32_t level; - uint32_t duration; + uint32_t duration : 30; + uint8_t level : 2; } LevelDuration; static inline LevelDuration level_duration_make(bool level, uint32_t duration) { diff --git a/lib/toolbox/pretty_format.c b/lib/toolbox/pretty_format.c new file mode 100644 index 000000000..d5ba10381 --- /dev/null +++ b/lib/toolbox/pretty_format.c @@ -0,0 +1,60 @@ +#include "pretty_format.h" + +#include +#include + +#define PRETTY_FORMAT_MAX_CANONICAL_DATA_SIZE 256U + +void pretty_format_bytes_hex_canonical( + FuriString* result, + size_t num_places, + const char* line_prefix, + const uint8_t* data, + size_t data_size) { + furi_assert(data); + + bool is_truncated = false; + + if(data_size > PRETTY_FORMAT_MAX_CANONICAL_DATA_SIZE) { + data_size = PRETTY_FORMAT_MAX_CANONICAL_DATA_SIZE; + is_truncated = true; + } + + /* Only num_places byte(s) can be on a single line, therefore: */ + const size_t line_count = + data_size / num_places + (data_size % num_places != 0 ? 1 : 0) + (is_truncated ? 2 : 0); + /* Line length = Prefix length + 3 * num_places (2 hex digits + space) + 1 * num_places + + + 1 pipe character + 1 newline character */ + const size_t line_length = (line_prefix ? strlen(line_prefix) : 0) + 4 * num_places + 2; + + /* Reserve memory in adance in order to avoid unnecessary reallocs */ + furi_string_reset(result); + furi_string_reserve(result, line_count * line_length); + + for(size_t i = 0; i < data_size; i += num_places) { + if(line_prefix) { + furi_string_cat(result, line_prefix); + } + + const size_t begin_idx = i; + const size_t end_idx = MIN(i + num_places, data_size); + + for(size_t j = begin_idx; j < end_idx; j++) { + furi_string_cat_printf(result, "%02X ", data[j]); + } + + furi_string_push_back(result, '|'); + + for(size_t j = begin_idx; j < end_idx; j++) { + const char c = data[j]; + const char sep = ((j < end_idx - 1) ? ' ' : '\n'); + const char* fmt = ((j < data_size - 1) ? "%c%c" : "%c"); + furi_string_cat_printf(result, fmt, (c > 0x1f && c < 0x7f) ? c : '.', sep); + } + } + + if(is_truncated) { + furi_string_cat_printf( + result, "\n(Data is too big. Showing only the first %zu bytes.)", data_size); + } +} diff --git a/lib/toolbox/pretty_format.h b/lib/toolbox/pretty_format.h new file mode 100644 index 000000000..860528e4c --- /dev/null +++ b/lib/toolbox/pretty_format.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define PRETTY_FORMAT_FONT_BOLD "\e#" +#define PRETTY_FORMAT_FONT_MONOSPACE "\e*" + +/** + * Format a data buffer as a canonical HEX dump + * @param [out] result pointer to the output string (must be initialised) + * @param [in] num_places the number of bytes on one line (both as HEX and ASCII) + * @param [in] line_prefix if not NULL, prepend this string to each line + * @param [in] data pointer to the input data buffer + * @param [in] data_size input data size + */ +void pretty_format_bytes_hex_canonical( + FuriString* result, + size_t num_places, + const char* line_prefix, + const uint8_t* data, + size_t data_size); + +#ifdef __cplusplus +} +#endif diff --git a/lib/toolbox/stream/buffered_file_stream.c b/lib/toolbox/stream/buffered_file_stream.c index 3b20a391c..3b485e80d 100644 --- a/lib/toolbox/stream/buffered_file_stream.c +++ b/lib/toolbox/stream/buffered_file_stream.c @@ -95,6 +95,7 @@ FS_Error buffered_file_stream_get_error(Stream* _stream) { static void buffered_file_stream_free(BufferedFileStream* stream) { furi_assert(stream); + buffered_file_stream_sync((Stream*)stream); stream_free(stream->file_stream); stream_cache_free(stream->cache); free(stream); diff --git a/lib/toolbox/tar/tar_archive.c b/lib/toolbox/tar/tar_archive.c index 01969b471..fcfc22a70 100644 --- a/lib/toolbox/tar/tar_archive.c +++ b/lib/toolbox/tar/tar_archive.c @@ -344,7 +344,7 @@ bool tar_archive_add_dir(TarArchive* archive, const char* fs_full_path, const ch furi_string_set(element_name, name); } - if(file_info.flags & FSF_DIRECTORY) { + if(file_info_is_dir(&file_info)) { success = tar_archive_dir_add_element(archive, furi_string_get_cstr(element_name)) && tar_archive_add_dir( diff --git a/lib/u8g2/u8g2.h b/lib/u8g2/u8g2.h index 68611d482..0068ea61d 100644 --- a/lib/u8g2/u8g2.h +++ b/lib/u8g2/u8g2.h @@ -3548,6 +3548,7 @@ void u8g2_Setup_a2printer_384x240_f( void u8g2_SendBuffer(u8g2_t* u8g2); void u8g2_ClearBuffer(u8g2_t* u8g2); +void u8g2_FillBuffer(u8g2_t* u8g2); void u8g2_SetBufferCurrTileRow(u8g2_t* u8g2, uint8_t row) U8G2_NOINLINE; diff --git a/lib/u8g2/u8g2_buffer.c b/lib/u8g2/u8g2_buffer.c index 45855bd5d..06686b79b 100644 --- a/lib/u8g2/u8g2_buffer.c +++ b/lib/u8g2/u8g2_buffer.c @@ -45,6 +45,14 @@ void u8g2_ClearBuffer(u8g2_t* u8g2) { memset(u8g2->tile_buf_ptr, 0, cnt); } +void u8g2_FillBuffer(u8g2_t* u8g2) { + size_t cnt; + cnt = u8g2_GetU8x8(u8g2)->display_info->tile_width; + cnt *= u8g2->tile_buf_height; + cnt *= 8; + memset(u8g2->tile_buf_ptr, 255, cnt); +} + /*============================================*/ static void u8g2_send_tile_row(u8g2_t* u8g2, uint8_t src_tile_row, uint8_t dest_tile_row) { diff --git a/scripts/asset_packer.py b/scripts/asset_packer.py index 02ed0b94e..ebf52c4a1 100755 --- a/scripts/asset_packer.py +++ b/scripts/asset_packer.py @@ -6,7 +6,9 @@ import shutil import struct import typing import time +import re import io +import os def convert_bm(img: "Image.Image | pathlib.Path") -> bytes: @@ -28,7 +30,7 @@ def convert_bm(img: "Image.Image | pathlib.Path") -> bytes: data_enc = bytearray(data_encoded_str) data_enc = bytearray([len(data_enc) & 0xFF, len(data_enc) >> 8]) + data_enc - if len(data_enc) < len(data_bin) + 1: + if len(data_enc) + 2 < len(data_bin) + 1: return b"\x01\x00" + data_enc else: return b"\x00" + data_bin @@ -94,7 +96,7 @@ def pack( if not source.is_dir(): continue - logger(f"Packing {source.name}... ") + logger(f"Pack: custom user pack '{source.name}'") packed = output / source.name if packed.exists(): try: @@ -110,10 +112,11 @@ def pack( shutil.copyfile( source / "Anims/manifest.txt", packed / "Anims/manifest.txt" ) - for anim in (source / "Anims").iterdir(): - if not anim.is_dir(): - continue - pack_anim(anim, packed / "Anims" / anim.name) + manifest = (source / "Anims/manifest.txt").read_bytes() + for anim in re.finditer(rb"Name: (.*)", manifest): + anim = anim.group(1).decode().replace("\\", "/").replace("/", os.sep) + logger(f"Compile: anim for pack '{source.name}': {anim}") + pack_anim(source / "Anims" / anim, packed / "Anims" / anim) if (source / "Icons").is_dir(): for icons in (source / "Icons").iterdir(): @@ -121,10 +124,16 @@ def pack( continue for icon in icons.iterdir(): if icon.is_dir(): + logger( + f"Compile: icon for pack '{source.name}': {icons.name}/{icon.name}" + ) pack_icon_animated( icon, packed / "Icons" / icons.name / icon.name ) elif icon.is_file(): + logger( + f"Compile: icon for pack '{source.name}': {icons.name}/{icon.name}" + ) pack_icon_static( icon, packed / "Icons" / icons.name / icon.name ) diff --git a/scripts/assets.py b/scripts/assets.py index c1a9f8097..0520c9fb6 100755 --- a/scripts/assets.py +++ b/scripts/assets.py @@ -5,7 +5,6 @@ from flipper.app import App from flipper.assets.icon import file2image import os -import shutil import pathlib ICONS_SUPPORTED_FORMATS = ["png"] @@ -289,7 +288,6 @@ class Main(App): self.logger.info(f"Packing") dolphin.pack(self.args.output_directory, self.args.symbol_name) self.logger.info(f"Complete") - __import__("time").sleep(2) return 0 diff --git a/scripts/fbt/appmanifest.py b/scripts/fbt/appmanifest.py index 4f960de83..624113101 100644 --- a/scripts/fbt/appmanifest.py +++ b/scripts/fbt/appmanifest.py @@ -51,7 +51,6 @@ class FlipperApplication: stack_size: int = 2048 icon: Optional[str] = None order: int = 0 - link: Optional[str] = "" sdk_headers: List[str] = field(default_factory=list) targets: List[str] = field(default_factory=lambda: ["all"]) @@ -323,9 +322,9 @@ class ApplicationsCGenerator: return f""" {{.app = {app.entry_point}, .name = "{app.name}", + .appid = "{app.appid}", .stack_size = {app.stack_size}, .icon = {f"&{app.icon}" if app.icon else "NULL"}, - .link = "{f"{app.link}" if app.link else "NULL"}", .flags = {'|'.join(f"FlipperApplicationFlag{flag}" for flag in app.flags)} }}""" def generate(self): diff --git a/scripts/fbt/util.py b/scripts/fbt/util.py index ee7562058..7bdaea031 100644 --- a/scripts/fbt/util.py +++ b/scripts/fbt/util.py @@ -8,6 +8,10 @@ import os WINPATHSEP_RE = re.compile(r"\\([^\"'\\]|$)") +# Used by default when globbing for files with GlobRecursive +# Excludes all files ending with ~, usually created by editors as backup files +GLOB_FILE_EXCLUSION = ["*~"] + def tempfile_arg_esc_func(arg): arg = quote_spaces(arg) diff --git a/scripts/fbt_tools/fbt_assets.py b/scripts/fbt_tools/fbt_assets.py index e17487358..d2a58f3fb 100644 --- a/scripts/fbt_tools/fbt_assets.py +++ b/scripts/fbt_tools/fbt_assets.py @@ -27,9 +27,7 @@ def proto_emitter(target, source, env): def dolphin_emitter(target, source, env): res_root_dir = source[0].Dir(env["DOLPHIN_RES_TYPE"]) source = [res_root_dir] - source.extend( - env.GlobRecursive("*.*", res_root_dir.srcnode()), - ) + source.extend(env.GlobRecursive("*.*", res_root_dir.srcnode())) target_base_dir = target[0] env.Replace(_DOLPHIN_OUT_DIR=target[0]) diff --git a/scripts/fbt_tools/fbt_debugopts.py b/scripts/fbt_tools/fbt_debugopts.py index f4b021c20..9abe59893 100644 --- a/scripts/fbt_tools/fbt_debugopts.py +++ b/scripts/fbt_tools/fbt_debugopts.py @@ -47,6 +47,7 @@ def generate(env, **kw): "source ${FBT_DEBUG_DIR}/gdbinit", ], GDBOPTS_BLACKMAGIC=[ + "-q", "-ex", "monitor swdp_scan", "-ex", diff --git a/scripts/fbt_tools/fbt_dist.py b/scripts/fbt_tools/fbt_dist.py index f0b443486..c0eca0d2a 100644 --- a/scripts/fbt_tools/fbt_dist.py +++ b/scripts/fbt_tools/fbt_dist.py @@ -4,7 +4,7 @@ from SCons.Script import Mkdir from SCons.Defaults import Touch -def GetProjetDirName(env, project=None): +def GetProjectDirName(env, project=None): parts = [f"f{env['TARGET_HW']}"] if project: parts.append(project) @@ -21,7 +21,7 @@ def GetProjetDirName(env, project=None): def create_fw_build_targets(env, configuration_name): - flavor = GetProjetDirName(env, configuration_name) + flavor = GetProjectDirName(env, configuration_name) build_dir = env.Dir("build").Dir(flavor) return env.SConscript( "firmware.scons", @@ -49,7 +49,7 @@ def AddFwProject(env, base_env, fw_type, fw_env_key): ], ) - env.Replace(DIST_DIR=env.GetProjetDirName()) + env.Replace(DIST_DIR=env.GetProjectDirName()) return project_env @@ -115,7 +115,7 @@ def generate(env): env.AddMethod(AddFwProject) env.AddMethod(DistCommand) env.AddMethod(AddOpenOCDFlashTarget) - env.AddMethod(GetProjetDirName) + env.AddMethod(GetProjectDirName) env.AddMethod(AddJFlashTarget) env.AddMethod(AddUsbFlashTarget) diff --git a/scripts/fbt_tools/fbt_extapps.py b/scripts/fbt_tools/fbt_extapps.py index e1c7641db..214afd8af 100644 --- a/scripts/fbt_tools/fbt_extapps.py +++ b/scripts/fbt_tools/fbt_extapps.py @@ -169,7 +169,6 @@ def prepare_app_metadata(target, source, env): sdk_cache = SdkCache(env["SDK_DEFINITION"].path, load_version_only=True) if not sdk_cache.is_buildable(): - print(target, source, env) raise UserError( "SDK version is not finalized, please review changes and re-run operation" ) diff --git a/scripts/fbt_tools/fbt_help.py b/scripts/fbt_tools/fbt_help.py index afdb36665..1b2903058 100644 --- a/scripts/fbt_tools/fbt_help.py +++ b/scripts/fbt_tools/fbt_help.py @@ -26,7 +26,7 @@ Other: cli: Open a Flipper CLI session over USB firmware_cdb, updater_cdb: - Generate сompilation_database.json + Generate compilation_database.json lint, lint_py: run linters format, format_py: diff --git a/scripts/fbt_tools/fbt_hwtarget.py b/scripts/fbt_tools/fbt_hwtarget.py index b4e1e58ac..7deb3e87f 100644 --- a/scripts/fbt_tools/fbt_hwtarget.py +++ b/scripts/fbt_tools/fbt_hwtarget.py @@ -93,7 +93,7 @@ class HardwareTargetLoader: sdk_headers = [] seen_sdk_headers = set(self.excluded_headers) for sdk_path in self.sdk_header_paths: - # dirty, but fast - exclude headers from overlayed targets by name + # dirty, but fast - exclude headers from overlaid targets by name # proper way would be to use relative paths, but names will do for now for header in self.env.GlobRecursive("*.h", sdk_path, "*_i.h"): if header.name not in seen_sdk_headers: diff --git a/scripts/fbt_tools/fbt_sdk.py b/scripts/fbt_tools/fbt_sdk.py index 48faf2125..3a37eacc9 100644 --- a/scripts/fbt_tools/fbt_sdk.py +++ b/scripts/fbt_tools/fbt_sdk.py @@ -219,7 +219,9 @@ def gen_sdk_data(sdk_cache: SdkCache): def _check_sdk_is_up2date(sdk_cache: SdkCache): if not sdk_cache.is_buildable(): - print("Working with WIP SDK. Expect failures") + raise UserError( + "SDK version is not finalized, please review changes and re-run operation" + ) def validate_sdk_cache(source, target, env): diff --git a/scripts/fbt_tools/sconsrecursiveglob.py b/scripts/fbt_tools/sconsrecursiveglob.py index 32ff55ea1..fbcee965b 100644 --- a/scripts/fbt_tools/sconsrecursiveglob.py +++ b/scripts/fbt_tools/sconsrecursiveglob.py @@ -1,7 +1,11 @@ import SCons +from SCons.Script import Flatten +from fbt.util import GLOB_FILE_EXCLUSION -def GlobRecursive(env, pattern, node=".", exclude=None): +def GlobRecursive(env, pattern, node=".", exclude=[]): + exclude = list(set(Flatten(exclude) + GLOB_FILE_EXCLUSION)) + # print(f"Starting glob for {pattern} from {node} (exclude: {exclude})") results = [] if isinstance(node, str): node = env.Dir(node) @@ -13,7 +17,7 @@ def GlobRecursive(env, pattern, node=".", exclude=None): source=True, exclude=exclude, ) - # print(f"Glob for {pattern} from {node}: {results}") + # print(f"Glob result for {pattern} from {node}: {results}") return results diff --git a/scripts/flipper/assets/dolphin.py b/scripts/flipper/assets/dolphin.py index 45ed3e19f..cbd1320b6 100644 --- a/scripts/flipper/assets/dolphin.py +++ b/scripts/flipper/assets/dolphin.py @@ -3,7 +3,6 @@ import logging import os import sys import shutil -import pathlib from collections import Counter from flipper.utils.fff import * @@ -23,7 +22,6 @@ def _convert_image(source_filename: str): class DolphinBubbleAnimation: - FILE_TYPE = "Flipper Animation" FILE_VERSION = 1 @@ -35,7 +33,6 @@ class DolphinBubbleAnimation: min_level: int, max_level: int, weight: int, - subpath: str = None, ): # Manifest self.name = name @@ -44,7 +41,6 @@ class DolphinBubbleAnimation: self.min_level = min_level self.max_level = max_level self.weight = weight - self.subpath = subpath # Meta and data self.meta = {} self.frames = [] @@ -187,12 +183,7 @@ class DolphinBubbleAnimation: bubble["_NextBubbleIndex"] = bubble_index + 1 def save(self, output_directory: str): - if self.subpath: - animation_directory = os.path.join( - output_directory, self.subpath, self.name - ) - else: - animation_directory = os.path.join(output_directory, self.name) + animation_directory = os.path.join(output_directory, self.name) os.makedirs(animation_directory, exist_ok=True) meta_filename = os.path.join(animation_directory, "meta.txt") @@ -251,7 +242,6 @@ class DolphinBubbleAnimation: class DolphinManifest: - FILE_TYPE = "Flipper Animation Manifest" FILE_VERSION = 1 @@ -265,8 +255,8 @@ class DolphinManifest: self.animations = [] self.logger = logging.getLogger("DolphinManifest") - def load(self, loc: str, subpath: str = None): - manifest_filename = os.path.join(loc, "manifest.txt") + def load(self, source_directory: str): + manifest_filename = os.path.join(source_directory, "manifest.txt") file = FlipperFormatFile() file.load(manifest_filename) @@ -296,17 +286,11 @@ class DolphinManifest: # Initialize animation animation = DolphinBubbleAnimation( - name, - min_butthurt, - max_butthurt, - min_level, - max_level, - weight, - subpath, + name, min_butthurt, max_butthurt, min_level, max_level, weight ) # Load Animation meta and frames - animation.load(os.path.join(loc, name)) + animation.load(os.path.join(source_directory, name)) # Add to array self.animations.append(animation) @@ -375,20 +359,11 @@ class Dolphin: self.manifest = DolphinManifest() self.logger = logging.getLogger("Dolphin") - def load(self, input_directory, dirs: list = None): - if dirs: - input = str(pathlib.Path(input_directory).absolute()) - for path in dirs: - path = str(pathlib.Path(path).absolute()) - assert os.path.isdir(path) - # Load Manifest - self.logger.info(f"Loading directory {path}") - self.manifest.load(path, subpath=path.removeprefix(input + os.sep)) - else: - assert os.path.isdir(input_directory) - # Load Manifest - self.logger.info(f"Loading directory {input_directory}") - self.manifest.load(input_directory) + def load(self, source_directory: str): + assert os.path.isdir(source_directory) + # Load Manifest + self.logger.info(f"Loading directory {source_directory}") + self.manifest.load(source_directory) def pack(self, output_directory: str, symbol_name: str = None): self.manifest.save(output_directory, symbol_name) diff --git a/scripts/flipper/assets/icon.py b/scripts/flipper/assets/icon.py index ed85b024e..f0dae25be 100644 --- a/scripts/flipper/assets/icon.py +++ b/scripts/flipper/assets/icon.py @@ -105,7 +105,7 @@ def file2image(file): data_enc = bytearray([len(data_enc) & 0xFF, len(data_enc) >> 8]) + data_enc # Use encoded data only if its length less than original, including header - if len(data_enc) < len(data_bin) + 1: + if len(data_enc) + 2 < len(data_bin) + 1: data = b"\x01\x00" + data_enc else: data = b"\x00" + data_bin diff --git a/scripts/flipper/utils/cdc.py b/scripts/flipper/utils/cdc.py index 7047db2a6..7c7351670 100644 --- a/scripts/flipper/utils/cdc.py +++ b/scripts/flipper/utils/cdc.py @@ -1,5 +1,6 @@ import serial.tools.list_ports as list_ports + # Returns a valid port or None, if it cannot be found def resolve_port(logger, portname: str = "auto"): if portname != "auto": diff --git a/scripts/flipper/utils/templite.py b/scripts/flipper/utils/templite.py index 8af57d7f8..2d958bd77 100644 --- a/scripts/flipper/utils/templite.py +++ b/scripts/flipper/utils/templite.py @@ -173,12 +173,14 @@ class Templite: """Renders the template according to the given namespace.""" stack = [] namespace["__file__"] = self.file + # add write method def write(*args): for value in args: stack.append(str(value)) namespace["write"] = write + # add include method def include(file): if not os.path.isabs(file): diff --git a/scripts/merge_report_qa.py b/scripts/merge_report_qa.py index c0707848e..caa742408 100755 --- a/scripts/merge_report_qa.py +++ b/scripts/merge_report_qa.py @@ -17,7 +17,7 @@ def parse_args(): def checkCommitMessage(msg): - regex = re.compile(r"^'?\[FL-\d+\]") + regex = re.compile(r"^'?\[(FL-\d+,?\s?)+\]") if regex.match(msg): return True return False diff --git a/scripts/runfap.py b/scripts/runfap.py index 9f544fd90..410b3e7d2 100644 --- a/scripts/runfap.py +++ b/scripts/runfap.py @@ -16,11 +16,11 @@ class Main(App): def init(self): self.parser.add_argument("-p", "--port", help="CDC Port", default="auto") self.parser.add_argument( - "-l", - "--launch", + "-n", + "--no-launch", dest="launch_app", - action="store_true", - help="Launch app", + action="store_false", + help="Don't launch app", ) self.parser.add_argument("fap_src_path", help="App file to upload") @@ -91,11 +91,14 @@ class Main(App): self.logger.error(f"Error: upload failed: {storage.last_error}") return -3 - storage.send_and_wait_eol(f'loader open "Applications" {fap_dst_path}\r') - result = storage.read.until(storage.CLI_EOL) - if len(result): - self.logger.error(f"Unexpected response: {result.decode('ascii')}") - return -4 + if self.args.launch_app: + storage.send_and_wait_eol( + f'loader open "Applications" {fap_dst_path}\r' + ) + result = storage.read.until(storage.CLI_EOL) + if len(result): + self.logger.error(f"Unexpected response: {result.decode('ascii')}") + return -4 return 0 finally: diff --git a/scripts/toolchain/fbtenv.cmd b/scripts/toolchain/fbtenv.cmd index 9fbd8fd9b..8587f6d0e 100644 --- a/scripts/toolchain/fbtenv.cmd +++ b/scripts/toolchain/fbtenv.cmd @@ -13,7 +13,7 @@ if not ["%FBT_NOENV%"] == [""] ( exit /b 0 ) -set "FLIPPER_TOOLCHAIN_VERSION=19" +set "FLIPPER_TOOLCHAIN_VERSION=21" if ["%FBT_TOOLCHAIN_ROOT%"] == [""] ( set "FBT_TOOLCHAIN_ROOT=%FBT_ROOT%\toolchain\x86_64-windows" @@ -31,7 +31,13 @@ if not exist "%FBT_TOOLCHAIN_VERSION_FILE%" ( set /p REAL_TOOLCHAIN_VERSION=<"%FBT_TOOLCHAIN_VERSION_FILE%" if not "%REAL_TOOLCHAIN_VERSION%" == "%FLIPPER_TOOLCHAIN_VERSION%" ( + echo FBT: starting toolchain upgrade process.. powershell -ExecutionPolicy Bypass -File "%FBT_ROOT%\scripts\toolchain\windows-toolchain-download.ps1" %flipper_toolchain_version% "%FBT_TOOLCHAIN_ROOT%" + set /p REAL_TOOLCHAIN_VERSION=<"%FBT_TOOLCHAIN_VERSION_FILE%" +) + +if defined FBT_VERBOSE ( + echo FBT: using toolchain version %REAL_TOOLCHAIN_VERSION% ) set "HOME=%USERPROFILE%" diff --git a/scripts/toolchain/fbtenv.sh b/scripts/toolchain/fbtenv.sh index 5eef5bfa8..cefa55d7e 100755 --- a/scripts/toolchain/fbtenv.sh +++ b/scripts/toolchain/fbtenv.sh @@ -5,15 +5,16 @@ # public variables DEFAULT_SCRIPT_PATH="$(pwd -P)"; SCRIPT_PATH="${SCRIPT_PATH:-$DEFAULT_SCRIPT_PATH}"; -FBT_TOOLCHAIN_VERSION="${FBT_TOOLCHAIN_VERSION:-"19"}"; +FBT_TOOLCHAIN_VERSION="${FBT_TOOLCHAIN_VERSION:-"21"}"; FBT_TOOLCHAIN_PATH="${FBT_TOOLCHAIN_PATH:-$SCRIPT_PATH}"; +FBT_VERBOSE="${FBT_VERBOSE:-""}"; fbtenv_show_usage() { echo "Running this script manually is wrong, please source it"; echo "Example:"; printf "\tsource scripts/toolchain/fbtenv.sh\n"; - echo "To restore your environment source fbtenv.sh with '--restore'." + echo "To restore your environment, source fbtenv.sh with '--restore'." echo "Example:"; printf "\tsource scripts/toolchain/fbtenv.sh --restore\n"; } @@ -35,16 +36,26 @@ fbtenv_restore_env() PATH="$(echo "$PATH" | /usr/bin/sed "s/$TOOLCHAIN_ARCH_DIR_SED\/bin://g")"; PATH="$(echo "$PATH" | /usr/bin/sed "s/$TOOLCHAIN_ARCH_DIR_SED\/protobuf\/bin://g")"; PATH="$(echo "$PATH" | /usr/bin/sed "s/$TOOLCHAIN_ARCH_DIR_SED\/openocd\/bin://g")"; + PATH="$(echo "$PATH" | /usr/bin/sed "s/$TOOLCHAIN_ARCH_DIR_SED\/openssl\/bin://g")"; if [ -n "${PS1:-""}" ]; then PS1="$(echo "$PS1" | sed 's/\[fbt\]//g')"; elif [ -n "${PROMPT:-""}" ]; then PROMPT="$(echo "$PROMPT" | sed 's/\[fbt\]//g')"; fi - PYTHONNOUSERSITE="$SAVED_PYTHONNOUSERSITE"; - PYTHONPATH="$SAVED_PYTHONPATH"; - PYTHONHOME="$SAVED_PYTHONHOME"; + if [ -n "$SAVED_SSL_CERT_FILE" ]; then + export SSL_CERT_FILE="$SAVED_SSL_CERT_FILE"; + export REQUESTS_CA_BUNDLE="$SAVED_REQUESTS_CA_BUNDLE"; + else + unset SSL_CERT_FILE; + unset REQUESTS_CA_BUNDLE; + fi + export PYTHONNOUSERSITE="$SAVED_PYTHONNOUSERSITE"; + export PYTHONPATH="$SAVED_PYTHONPATH"; + export PYTHONHOME="$SAVED_PYTHONHOME"; + unset SAVED_SSL_CERT_FILE; + unset SAVED_REQUESTS_CA_BUNDLE; unset SAVED_PYTHONNOUSERSITE; unset SAVED_PYTHONPATH; unset SAVED_PYTHONHOME; @@ -131,7 +142,7 @@ fbtenv_get_kernel_type() TOOLCHAIN_URL=$FBT_TOOLS_CUSTOM_LINK; fi elif echo "$SYS_TYPE" | grep -q "MINGW"; then - echo "In MinGW shell use \"[u]fbt.cmd\" instead of \"[u]fbt\""; + echo "In MinGW shell, use \"[u]fbt.cmd\" instead of \"[u]fbt\""; return 1; else echo "Your system configuration is not supported. Sorry.. Please report us your configuration."; @@ -258,6 +269,7 @@ fbtenv_check_download_toolchain() elif [ ! -f "$TOOLCHAIN_ARCH_DIR/VERSION" ]; then fbtenv_download_toolchain || return 1; elif [ "$(cat "$TOOLCHAIN_ARCH_DIR/VERSION")" -ne "$FBT_TOOLCHAIN_VERSION" ]; then + echo "FBT: starting toolchain upgrade process.." fbtenv_download_toolchain || return 1; fi return 0; @@ -279,6 +291,13 @@ fbtenv_download_toolchain() return 0; } +fbtenv_print_version() +{ + if [ -n "$FBT_VERBOSE" ]; then + echo "FBT: using toolchain version $(cat "$TOOLCHAIN_ARCH_DIR/VERSION")"; + fi +} + fbtenv_main() { fbtenv_check_sourced || return 1; @@ -291,18 +310,25 @@ fbtenv_main() fbtenv_check_script_path || return 1; fbtenv_check_download_toolchain || return 1; fbtenv_set_shell_prompt; + fbtenv_print_version; PATH="$TOOLCHAIN_ARCH_DIR/python/bin:$PATH"; PATH="$TOOLCHAIN_ARCH_DIR/bin:$PATH"; PATH="$TOOLCHAIN_ARCH_DIR/protobuf/bin:$PATH"; PATH="$TOOLCHAIN_ARCH_DIR/openocd/bin:$PATH"; + PATH="$TOOLCHAIN_ARCH_DIR/openssl/bin:$PATH"; + export PATH; - SAVED_PYTHONNOUSERSITE="${PYTHONNOUSERSITE:-""}"; - SAVED_PYTHONPATH="${PYTHONPATH:-""}"; - SAVED_PYTHONHOME="${PYTHONHOME:-""}"; + export SAVED_SSL_CERT_FILE="${SSL_CERT_FILE:-""}"; + export SAVED_REQUESTS_CA_BUNDLE="${REQUESTS_CA_BUNDLE:-""}"; + export SAVED_PYTHONNOUSERSITE="${PYTHONNOUSERSITE:-""}"; + export SAVED_PYTHONPATH="${PYTHONPATH:-""}"; + export SAVED_PYTHONHOME="${PYTHONHOME:-""}"; - PYTHONNOUSERSITE=1; - PYTHONPATH=; - PYTHONHOME=; + export SSL_CERT_FILE="$TOOLCHAIN_ARCH_DIR/python/lib/python3.11/site-packages/certifi/cacert.pem"; + export REQUESTS_CA_BUNDLE="$SSL_CERT_FILE"; + export PYTHONNOUSERSITE=1; + export PYTHONPATH=; + export PYTHONHOME=; } fbtenv_main "${1:-""}"; diff --git a/scripts/update.py b/scripts/update.py index 3259c5b09..2b0157260 100755 --- a/scripts/update.py +++ b/scripts/update.py @@ -199,7 +199,7 @@ class Main(App): def disclaimer(self): self.logger.error( - "You might brick you device into a state in which you'd need an SWD programmer to fix it." + "You might brick your device into a state in which you'd need an SWD programmer to fix it." ) self.logger.error( "Please confirm that you REALLY want to do that with --I-understand-what-I-am-doing=yes" diff --git a/scripts/version.py b/scripts/version.py index ed2e83162..5cdd16138 100644 --- a/scripts/version.py +++ b/scripts/version.py @@ -1,4 +1,5 @@ #!/usb/bin/env python3 +VERSION = "XFW-0041" from flipper.app import App @@ -11,24 +12,9 @@ from datetime import date, datetime class GitVersion: def __init__(self, source_dir): self.source_dir = source_dir - self.gitlist = [ - ("commit", "rev-parse --short HEAD"), - ("branch", "rev-parse --abbrev-ref"), - ("branch_num", "rev-list -count HEAD"), - ] def get_version_info(self): - commit = branch = branch_num = "XFW-0041" - - # We dont use an `or` in commands that we expect to fail. It will serve no function. - # We also dont try;exept an entire block of code. This is bad practise. We only try the single part that we expect to fail! - # Furthermore, traceback.format_exc() is a thing. Fucking use it. JFC - - for git_tuple in self.gitlist: - try: - exec(f"{git_tuple[0]} = {self._exec_git(git_tuple[1])}") - except: - exec(f'{git_tuple[0]} = "Unknown"') + commit = self._exec_git("rev-parse --short HEAD") or "unknown" dirty = False try: @@ -39,11 +25,20 @@ class GitVersion: # If WORKFLOW_BRANCH_OR_TAG is set in environment, is has precedence # (set by CI) + branch = ( + os.environ.get("WORKFLOW_BRANCH_OR_TAG", None) + or VERSION + or self._exec_git("rev-parse --abbrev-ref HEAD") + or "unknown" + ) + + branch_num = self._exec_git("rev-list --count HEAD") or "n/a" + + version = os.environ.get("DIST_SUFFIX", None) or VERSION or "unknown" custom_fz_name = os.environ.get("CUSTOM_FLIPPER_NAME", None) or "" force_no_dirty = os.environ.get("FORCE_NO_DIRTY", None) or "" - if force_no_dirty != "": dirty = False @@ -55,19 +50,19 @@ class GitVersion: ): return { "GIT_COMMIT": commit, - "GIT_BRANCH": "dev", + "GIT_BRANCH": branch, "GIT_BRANCH_NUM": branch_num, "FURI_CUSTOM_FLIPPER_NAME": custom_fz_name, - "VERSION": "0.74.3", - "BUILD_DIRTY": 0, + "VERSION": version, + "BUILD_DIRTY": dirty and 1 or 0, } else: return { "GIT_COMMIT": commit, - "GIT_BRANCH": "dev", + "GIT_BRANCH": branch, "GIT_BRANCH_NUM": branch_num, - "VERSION": "0.74.3", - "BUILD_DIRTY": 0, + "VERSION": version, + "BUILD_DIRTY": dirty and 1 or 0, } def _exec_git(self, args): diff --git a/sonar-build b/sonar-build index 8dc9bf0dd..3cf736dcd 100755 --- a/sonar-build +++ b/sonar-build @@ -1,4 +1,4 @@ #!/bin/bash pushd . -./fbt updater_package -popd \ No newline at end of file +$1 +popd diff --git a/test_iso7816_helpers.c b/test_iso7816_helpers.c deleted file mode 100644 index 91d420dd1..000000000 --- a/test_iso7816_helpers.c +++ /dev/null @@ -1,243 +0,0 @@ -#include -#include - -#include "lib/nfc/helpers/iso7816.h" - -#define COLOR_RED "\033[0;31m" -#define COLOR_GREEN "\033[0;32m" -#define COLOR_RESET "\033[0;0m" - -#define num_elements(A) (sizeof(A)/sizeof(A[0])) - -//TODO: do something with ISO7816-4 Table 9 — Interindustry data objects for tag allocation authority -//0x06 Object identifier (encoding specified in ISO/IEC 8825-1, see examples in annex A) -//0x41 Country code (encoding specified in ISO 3166-1 [1] ) and optional national data -//0x42 Issuer identification number (encoding and registration specified in ISO/IEC 7812-1 [3] ) and optional issuer data -//0x4F Application identifier (AID, encoding specified in 8.2.1.2) - -void print_hex(const uint8_t* data, size_t length) { - for(size_t i=0; i -#include -#include -#include - -#include "applications/main/nfc/test_bac_creds.h" //TODO: remove - -#include "lib/nfc/protocols/mrtd_helpers.h" - -// gcc -o test_mrtd_helpers -Wall -Ilib/mbedtls/include -Itoolchain/x86_64-linux/arm-none-eabi/include/ lib/nfc/protocols/mrtd_helpers.c lib/mbedtls/library/sha1.c lib/mbedtls/library/des.c lib/mbedtls/library/platform_util.c test_mrtd_helpers.c - -#define COLOR_RED "\033[0;31m" -#define COLOR_GREEN "\033[0;32m" -#define COLOR_RESET "\033[0;0m" - -void print_hex(const uint8_t* data, size_t length) { - for(uint8_t i=0; i